@adatechnology/game-crowd 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +5 -0
- package/dist/index.global.js +13 -8
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +13 -8
- package/dist/index.js.map +1 -1
- package/dist/physics-worker.js.map +1 -1
- package/package.json +3 -3
package/dist/index.global.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../../game-contracts/src/blast-shove.ts","../../game-contracts/src/impact-cost.ts","../../game-contracts/src/impact.ts","../../game-contracts/src/body.ts","../../game-contracts/src/spec.ts","../../game-contracts/src/weapons.ts","../../game-contracts/src/ignition.ts","../../game-contracts/src/seal.ts","../../game-contracts/src/twist.ts","../../game-contracts/src/animation.ts","../../game-contracts/src/tool-animation.ts","../../game-contracts/src/frame.ts","../../game-contracts/src/warn.ts","../../game-contracts/src/registry.ts","../../game-contracts/src/bench.ts","../../game-contracts/src/environment.ts","../../game-contracts/src/categories.ts","../../game-contracts/src/origin.ts","../../game-contracts/src/footprint.ts","../../game-contracts/src/foley.ts","../../game-contracts/src/index.ts","../../game-characters/src/data/body.json","../../game-characters/src/body/body-spec.ts","../../game-characters/src/corpses.ts","../../game-characters/src/body/body-state.ts","../../game-characters/src/presets/team-game.json","../../game-characters/src/body/regions.ts","../../game-characters/src/data/segments.json","../../game-characters/src/body/segment-tree.ts","../../game-characters/src/body/describe.ts","../../game-characters/src/body/object-state.ts","../../game-characters/src/body/rig.ts","../../game-characters/src/character.ts","../../game-characters/src/body/transform.ts","../../game-characters/src/damage/bleeding.ts","../../game-characters/src/damage/body-ops.ts","../../game-characters/src/presets/physical.json","../../game-characters/src/damage/defaults.ts","../../game-characters/src/damage/penetration.ts","../../game-characters/src/damage/rules/blow.ts","../../game-characters/src/damage/rules/regions.ts","../../game-characters/src/damage/points.ts","../../game-characters/src/damage/blast.ts","../../game-characters/src/damage/protection.ts","../../game-characters/src/damage/resolve-part.ts","../../game-characters/src/damage/rules/arsenal.ts","../../game-characters/src/damage/rules/elenco.ts","../../game-characters/src/damage/rules/kit.ts","../../game-characters/src/damage/pipeline.ts","../../game-characters/src/damage/damage-character.ts","../../game-characters/src/damage/line.ts","../../game-characters/src/registry.ts","../../game-characters/src/kit.ts","../../game-characters/src/data/gait.json","../../game-characters/src/gait/stride.ts","../../game-characters/src/gait/profile.ts","../../game-characters/src/gait/animate.ts","../../game-characters/src/gait/cascade.ts","../../game-characters/src/gait/moves.ts","../../game-characters/src/gait/lying.ts","../../game-characters/src/condition/timeline.ts","../../game-characters/src/data/appearance.json","../../game-characters/src/appearance/variance.ts","../../game-characters/src/appearance/palette.ts","../../game-characters/src/appearance/traits.ts","../../game-characters/src/pose-clips/pose-clip.ts","../../game-characters/src/presets/pose-clips.json","../../game-characters/src/pose-clips/registry.ts","../../game-characters/src/pose-clips/first-person-clip.ts","../src/data/defaults.json","../src/doors.ts","../src/grid.ts","../src/identity.ts","../src/types.ts","../src/physics/balance.ts","../src/physics/blast.ts","../src/physics/contact.ts","../src/physics/context.ts","../src/physics/density.ts","../src/physics/mound.ts","../src/physics/offload/default-worker.ts","../src/physics/offload/frame.ts","../src/physics/offload/offload.ts","../src/physics/pressure.ts","../src/physics/offload/kernel.ts","../src/physics/offload/worker-host.ts","../src/physics/offload/runner.ts","../src/physics/player-pressure.ts","../src/physics/thrown.ts","../src/physics/engine.ts","../src/physics/pile-column.ts","../src/personality.ts","../src/physics/seat.ts","../src/physics/options.ts","../src/rng.ts","../src/store.ts","../src/crowd.ts","../src/behaviours/civilians.ts","../src/behaviours/herd.ts","../src/behaviours/flock.ts","../src/behaviours/infection.ts"],"sourcesContent":["/**\n * @adatechnology/game-crowd: any crowd as a struct of arrays, pure and seeded. A literal port of the team game's horde (`jogo.js` G 66714-67430):\n * relevance bands with hysteresis, perception by sight, hearing and smell, the sliced flow field, the spatial hash and separation, six attack slots,\n * chain alerts, doors, piles against walls, the special moves as type traits, corpses, the blood trail, the population and migration.\n * The pack (types, ages, outfits) is data: `game-characters-undead` gives the undead one.\n */\nexport { CROWD_DEFAULTS, createCrowd } from './crowd'\nexport type { Corpse, Crowd, CrowdBand, CrowdNumbers, CrowdOptions, CrowdPlugin, CrowdUpdateContext, CrowdView, MigrationOptions, PlayerCrowdLike } from './crowd'\nexport { DOOR_DEFAULTS, createDoors } from './doors'\nexport type { Door, DoorEventMap, DoorEventName, DoorNumbers, Doors } from './doors'\nexport { CELL, createNavGrid } from './grid'\nexport type { NavGrid, NavGridOptions } from './grid'\nexport { createRng } from './rng'\nexport { AGENT_FIELDS, createStore } from './store'\nexport type { CrowdStore } from './store'\nexport { MISSING, STATE } from './types'\nexport type * from './types'\nexport { CIVILIAN_DEFAULTS, createCivilians } from './behaviours/civilians'\nexport type { Civilian, CivilianBuilding, CivilianEventMap, CivilianEventName, CivilianNumbers, CivilianOptions, CivilianSpec, CivilianState, Civilians } from './behaviours/civilians'\nexport { HERD_DEFAULTS, HERD_ROLES, createHerd } from './behaviours/herd'\nexport type { Herd, HerdAnimal, HerdAnimalSpec, HerdEventMap, HerdEventName, HerdNumbers, HerdOptions, HerdRole } from './behaviours/herd'\nexport { carrionCenter, createFlock } from './behaviours/flock'\nexport type { Flock, FlockBird, FlockMode, FlockOptions } from './behaviours/flock'\nexport { bindInfection } from './behaviours/infection'\nexport type { InfectionBinding, InfectionBindingOptions, InfectionModelLike } from './behaviours/infection'\nexport { BALANCE, FALL_CAUSE } from './types'\nexport { mix32, unit, identityOf } from './identity'\nexport { CROWD_PHYSICS_DEFAULTS, resolvePhysics } from './physics/options'\nexport type {\n CrowdBalanceOptions,\n CrowdBlastOptions,\n CrowdContactOptions,\n CrowdDensityOptions,\n CrowdMoundOptions,\n CrowdPersonalityOptions,\n CrowdPhysicsOptions,\n CrowdPlayerPressureOptions,\n CrowdPressureOptions,\n CrowdThrownOptions,\n ResolvedPhysics,\n} from './physics/options'\nexport { contactStep, createContactView } from './physics/contact'\nexport type { ContactParams, ContactResult, ContactView } from './physics/contact'\nexport type { PhysicsStats } from './physics/context'\nexport type { ThrownPool } from './physics/thrown'\nexport { attachPhysicsWorker } from './physics/offload/worker-host'\nexport type { WorkerPort } from './physics/offload/worker-host'\nexport type { OffloadTransport, PhysicsWorkerFactory, PhysicsWorkerHandle } from './physics/offload/types'\n","import type { Vec3 } from './destruction'\n\n/**\n * The numbers of the shove a blast gives to a body that moves (a loose item, a piece of furniture, a person): all of them a game value, with a default that reads well in a room,\n * not a physical table. A host reads them once and lends them to the modules that move their own bodies (`ModuleHost.blastShove`).\n */\nexport interface BlastShoveOptions {\n /**\n * The share of the blast energy that reaches a body as motion: the speed at the centre is `sqrt(2 * energy * coupling / mass)`, before the falloff (default 0.02).\n */\n readonly coupling?: number\n /** Fastest a body is thrown, in m/s, at the centre of the blast (default 20). */\n readonly maxSpeed?: number\n /** How much of the push goes up: the direction is `(away, lift)` normalised from the horizontal outward direction (default 0.3). */\n readonly lift?: number\n /** Below this speed (m/s) nothing moves: a heavy thing far from a small blast stays where it is (default 0.3). */\n readonly minSpeed?: number\n /** Exponent of the falloff `(1 - distance / radius) ^ power`, the same as the damage of the destruction world (default 2). */\n readonly falloffPower?: number\n}\n\n/** The defaults of `BlastShoveOptions`. */\nexport const DEFAULT_BLAST_SHOVE: Readonly<Required<BlastShoveOptions>> = { coupling: 0.02, maxSpeed: 20, lift: 0.3, minSpeed: 0.3, falloffPower: 2 }\n\n/** A blast as the shove reads it: where, how much energy (J) and how far it reaches (m). `reach` (0..1) is the share that got through what stood in the way. */\nexport interface BlastSource {\n readonly point: Vec3\n readonly energy: number\n readonly radius: number\n readonly reach?: number\n}\n\n/** What a blast does to one body: how fast it leaves (m/s), the unit direction it leaves in, and how far from the centre it was. */\nexport interface BlastShove {\n readonly speed: number\n readonly direction: Vec3\n readonly distance: number\n /** The falloff applied, 0..1. */\n readonly falloff: number\n}\n\n/**\n * The shove a blast gives to a body of `mass` kg whose centre is at `position`: away from the centre, lifted a little, with the speed\n * `min(maxSpeed, sqrt(2 * energy * reach * coupling / mass)) * (1 - distance / radius) ^ 2`. Undefined when the body is outside the radius or the speed is under `minSpeed`.\n * The same rule moves the loose items and furniture of the test room and the people of the characters module, so a game that tunes the options tunes all of them.\n */\nexport function blastShove(blast: BlastSource, target: { readonly position: Vec3; readonly mass: number }, options: BlastShoveOptions = {}): BlastShove | undefined {\n const o = { ...DEFAULT_BLAST_SHOVE, ...options }\n const dx = target.position[0] - blast.point[0]\n const dy = target.position[1] - blast.point[1]\n const dz = target.position[2] - blast.point[2]\n const distance = Math.hypot(dx, dy, dz)\n if (!(blast.radius > 0) || distance > blast.radius) return undefined\n const falloff = (1 - distance / blast.radius) ** o.falloffPower\n const mass = Math.max(target.mass, 0.05)\n const speed = Math.min(o.maxSpeed, Math.sqrt((2 * blast.energy * (blast.reach ?? 1) * o.coupling) / mass)) * falloff\n if (!(speed >= o.minSpeed)) return undefined\n // straight above or below the centre there is no \"away\": the push goes up\n const direction: Vec3 = distance > 1e-4 ? [dx / distance, dy / distance + o.lift, dz / distance] : [0, 1, 0]\n const length = Math.hypot(direction[0], direction[1], direction[2])\n return { speed, direction: [direction[0] / length, direction[1] / length, direction[2] / length], distance, falloff }\n}\n","export interface CostOptions {\n /** Reads of `state()` and of `describe()` to time. Default 10 000. */\n reads?: number\n /** Ceiling, in milliseconds, for all the reads of one method. Default 50 (5 microseconds per read). */\n ceilingMs?: number\n /** Ceiling of heap growth, in bytes, over all the reads of one method (only where `process.memoryUsage` exists). Default 1 MiB. */\n allocationBytes?: number\n}\n\nconst DEFAULT_COST_READS = 10_000\nconst DEFAULT_COST_CEILING_MS = 50\nconst DEFAULT_COST_ALLOCATION_BYTES = 1024 * 1024\n\nconst heapUsedBytes = (): number | undefined => {\n const nodeProcess = (globalThis as { process?: { memoryUsage?: () => { heapUsed: number } } }).process\n return nodeProcess?.memoryUsage?.().heapUsed\n}\n\ntype CheckReadCostParams = { readonly name: string; readonly read: () => unknown; readonly options: CostOptions; readonly problems: string[] }\n\nexport const checkReadCost = ({ name, read, options, problems }: CheckReadCostParams): void => {\n const reads = options.reads ?? DEFAULT_COST_READS\n const ceilingMs = options.ceilingMs ?? DEFAULT_COST_CEILING_MS\n const allocationBytes = options.allocationBytes ?? DEFAULT_COST_ALLOCATION_BYTES\n if (read() !== read()) problems.push(`${name}() returns a new object on each read: cache it between changes (cost contract)`)\n // a first pass lets the engine compile and optimise the loop: that code and its feedback are heap too, and would read as the method allocating\n for (let i = 0; i < reads; i++) read()\n const heapBefore = heapUsedBytes()\n const started = performance.now()\n for (let i = 0; i < reads; i++) read()\n const elapsedMs = performance.now() - started\n const heapAfter = heapUsedBytes()\n if (elapsedMs > ceilingMs) problems.push(`${name}() took ${elapsedMs.toFixed(1)} ms for ${reads} reads, over the ${ceilingMs} ms ceiling (cost contract)`)\n if (heapBefore !== undefined && heapAfter !== undefined && heapAfter - heapBefore > allocationBytes)\n problems.push(`${name}() grew the heap by ${heapAfter - heapBefore} bytes over ${reads} reads, over the ${allocationBytes} byte ceiling (cost contract)`)\n}\n","import { checkReadCost } from './impact-cost'\nimport type { CostOptions } from './impact-cost'\nimport type { DamageType, DestructibleSpec, DestructionEvent, Hit, JointSpec, ObjectState, ToolKind, Vec3 } from './destruction'\n\n/**\n * What a game module implements for an object whose behaviour it owns (a vehicle with its own panel deformation, a creature, a machine). The\n * world (`DestructionWorld.register`) hosts it next to its own objects: raycast, shoot, explode, hit, disassemble, extinguish, update, state,\n * reset and `on` all route to it.\n *\n * Space: the impl receives hits in WORLD space and returns events in WORLD space (it owns its transform; the transform given to `register` or\n * `setTransform` is only used by the world for raycasts and blast range). Every event must carry `object === id` and name parts listed by\n * `describe()`.\n */\n/** World-space ray for `DestructibleImpl.surfaceRay`. */\nexport interface SurfaceRay {\n readonly origin: Vec3\n readonly direction: Vec3\n readonly maxDistance: number\n ignored?(part: string): boolean\n}\n\nexport interface SurfaceRayHit {\n readonly part: string\n readonly point: Vec3\n readonly normal: Vec3\n readonly distance: number\n /** Thickness the ray crosses in this part, metres. */\n readonly pathLen: number\n}\n\nexport interface DestructibleImpl {\n readonly id: string\n readonly name?: string\n /**\n * Parts, joints and structure in the impl's local space, for raycast, inspector and log.\n * COST CONTRACT (D8): called per frame by the renderer, so it is O(1), allocation-free and cached between changes: the same object comes\n * back until a hit, tool, fire or pose change alters it. `checkDestructible(impl, { cost: true })` measures it.\n */\n describe(): DestructibleSpec\n /** Apply one hit (see `IMPACT_CONTRACT` for the fields per type) and return the events it caused. */\n hit(hit: Hit): DestructionEvent[]\n /**\n * The real surface a ray meets, for an impl whose drawn shape is not its boxes (a car body): the boxes of `describe()` only say where to look, and a shot that crosses\n * them without touching the drawn surface goes on. `ignored` answers for the parts the round already went through. Null when the ray meets nothing.\n */\n surfaceRay?(ray: SurfaceRay): SurfaceRayHit | null\n /** Advance fire, melting, collapse and other timed behaviour. */\n update?(dt: number): DestructionEvent[]\n /**\n * COST CONTRACT (D8): the host reads it per part per frame, so it is O(1), allocation-free and cached between changes (see `describe()`).\n * The cached object is shared with every reader: treat it as read-only, copy before changing it.\n */\n state(): Readonly<ObjectState>\n reset?(): void\n /** The world took the object out (`DestructionWorld.remove`): the impl lets go of what it joined (shared queues, listeners). */\n dispose?(): void\n /** Same contract as `DestructionWorld.disassemble`: progress 0..1, or -1 when the tool cannot undo the joint. */\n disassemble?(jointId: string, tool: ToolKind, dt: number): number\n extinguish?(partId?: string): void\n /** Optional: an animation moved a part; `position` / `rotation` (Euler XYZ, radians) are object-local. Keep raycast and hit shapes in sync. */\n setPartPose?(partId: string, position: Vec3, rotation?: Vec3): void\n /** Plain JSON of the impl's own state, stored in `DestructionSnapshot.impls`. */\n serialize?(): unknown\n load?(data: unknown): void\n}\n\n/** The fields a weapon or hazard must put in a `Hit`, per damage type. THE impact contract. */\nexport interface ImpactContract {\n required: string[]\n optional: string[]\n note: string\n}\n\nexport const IMPACT_CONTRACT: Record<DamageType, ImpactContract> = {\n ballistic: { required: ['type', 'point', 'direction', 'energy', 'caliber'], optional: ['space', 'source'], note: 'bullets and pellets: joules and diameter in millimetres' },\n blunt: { required: ['type', 'point', 'energy'], optional: ['direction', 'radius', 'space', 'source'], note: 'contact radius in metres (default 3 cm)' },\n slash: { required: ['type', 'point', 'energy'], optional: ['direction', 'radius', 'space', 'source'], note: 'axes, knives, machetes' },\n explosive: { required: ['type', 'point', 'energy', 'radius'], optional: ['lethalRadius', 'space', 'source'], note: 'point is the centre of the blast, radius the range in metres' },\n fire: { required: ['type', 'point', 'temperature', 'radius'], optional: ['duration', 'space', 'source'], note: 'flame temperature in °C; duration in seconds (call every frame with `dt`)' },\n heat: { required: ['type', 'point', 'temperature', 'radius'], optional: ['duration', 'space', 'source'], note: 'heat without flame (cutting torch, hot plate)' },\n}\n\n/** Problems with a hit according to the impact contract (empty when it is valid). */\nexport const checkHit = (hit: Hit): string[] => {\n const c = IMPACT_CONTRACT[hit.type]\n if (!c) return [`unknown damage type \"${String(hit.type)}\"`]\n const out: string[] = []\n for (const f of c.required) {\n const v = (hit as unknown as Record<string, unknown>)[f]\n if (v === undefined || (Array.isArray(v) ? v.some((n) => !Number.isFinite(n)) : typeof v === 'number' && !Number.isFinite(v))) out.push(`${hit.type} hit needs \"${f}\"`)\n }\n return out\n}\n\nexport interface CheckOptions {\n /**\n * Ids of every material the impl may use (the contracts package does not know the materials of your world: pass the ids of\n * `world.materials`, or the built-in plus your own). When omitted, the material check is skipped.\n */\n materials?: Iterable<string>\n /** Opt-in cost measurement (D8): `true` for the defaults, or the limits to use. */\n cost?: boolean | CostOptions\n}\n\nconst finiteJson = (v: unknown, path: string, out: string[]) => {\n if (typeof v === 'number') {\n if (!Number.isFinite(v)) out.push(`${path} is not a finite number`)\n } else if (Array.isArray(v)) v.forEach((x, i) => finiteJson(x, `${path}[${i}]`, out))\n else if (v && typeof v === 'object') for (const [k, x] of Object.entries(v)) finiteJson(x, `${path}.${k}`, out)\n else if (typeof v === 'function' || typeof v === 'symbol' || typeof v === 'bigint') out.push(`${path} is not JSON`)\n}\n\n/**\n * Contract checker for the test room (it needs no world): `describe()` is valid, a 500 J ballistic hit at a part centre returns well-formed events of this impl, `state()`\n * matches `describe()`, hit points never go negative, `update(0.016)` returns an array and every event is JSON-serializable. It hits the impl\n * (and calls `reset()` afterwards when there is one). With `options.cost` it also times `state()` and `describe()` (the cost contract of D8).\n */\nexport const checkDestructible = (impl: DestructibleImpl, options: CheckOptions = {}): { ok: boolean; problems: string[] } => {\n const problems: string[] = []\n const known = options.materials ? new Set(options.materials) : null\n let spec: DestructibleSpec | undefined\n try {\n spec = impl.describe()\n } catch (e) {\n return { ok: false, problems: [`describe() threw: ${String(e)}`] }\n }\n if (!spec || !Array.isArray(spec.parts) || spec.parts.length === 0) return { ok: false, problems: ['describe() must return at least one part'] }\n const ids = new Set<string>()\n for (const p of spec.parts) {\n if (ids.has(p.id)) problems.push(`duplicate part id \"${p.id}\"`)\n ids.add(p.id)\n if (known && !known.has(p.material)) problems.push(`part \"${p.id}\" has unknown material \"${p.material}\"`)\n if (!p.shape || !['box', 'cylinder', 'sphere'].includes(p.shape.kind)) problems.push(`part \"${p.id}\" has no valid shape`)\n if (!Array.isArray(p.position) || p.position.length !== 3) problems.push(`part \"${p.id}\" has no position`)\n }\n const jointIds = new Set<string>()\n for (const j of (spec.joints ?? []) as JointSpec[]) {\n if (!ids.has(j.a) || !ids.has(j.b)) problems.push(`joint \"${j.id ?? `${j.a}~${j.b}`}\" references an unknown part`)\n const jid = j.id ?? `${j.a}~${j.b}`\n if (jointIds.has(jid)) problems.push(`duplicate joint id \"${jid}\"`)\n jointIds.add(jid)\n }\n const checkState = (label: string) => {\n let st: ObjectState\n try {\n st = impl.state()\n } catch (e) {\n problems.push(`state() threw ${label}: ${String(e)}`)\n return\n }\n const sids = st.parts.map((p) => p.id).sort().join(',')\n if (sids !== [...ids].sort().join(',')) problems.push(`state() part ids do not match describe() ${label}`)\n for (const p of st.parts) if (!(p.hp >= 0)) problems.push(`part \"${p.id}\" has negative or invalid hp ${label}`)\n }\n const checkEvents = (events: unknown, label: string) => {\n if (!Array.isArray(events)) {\n problems.push(`${label} must return an array`)\n return\n }\n const evs = events as DestructionEvent[]\n evs.forEach((e, i) => {\n if (e.object !== impl.id) problems.push(`${label} event ${i} (${e.type}) has object \"${String(e.object)}\", expected \"${impl.id}\"`)\n const part = (e as { part?: string }).part\n if (part !== undefined && !ids.has(part)) problems.push(`${label} event ${i} (${e.type}) names unknown part \"${part}\"`)\n })\n const bad: string[] = []\n finiteJson(evs, 'events', bad)\n problems.push(...bad.map((b) => `${label}: ${b}`))\n try {\n const s = JSON.stringify(evs)\n if (JSON.stringify(JSON.parse(s)) !== s) problems.push(`${label} events do not survive a JSON round trip`)\n } catch (e) {\n problems.push(`${label} events are not JSON-serializable: ${String(e)}`)\n }\n }\n checkState('before the hit')\n const target = spec.parts[0]\n try {\n const hit: Hit = { type: 'ballistic', point: [target.position[0], target.position[1], target.position[2]], direction: [0, 0, -1], energy: 500, caliber: 9 }\n checkEvents(impl.hit(hit), 'hit()')\n } catch (e) {\n problems.push(`hit() threw: ${String(e)}`)\n }\n checkState('after the hit')\n if (impl.update) {\n try {\n checkEvents(impl.update(0.016), 'update()')\n } catch (e) {\n problems.push(`update() threw: ${String(e)}`)\n }\n checkState('after update')\n }\n try {\n impl.reset?.()\n } catch (e) {\n problems.push(`reset() threw: ${String(e)}`)\n }\n if (options.cost) {\n const cost = options.cost === true ? {} : options.cost\n try {\n checkReadCost({ name: 'state', read: () => impl.state(), options: cost, problems })\n checkReadCost({ name: 'describe', read: () => impl.describe(), options: cost, problems })\n } catch (e) {\n problems.push(`cost measurement threw: ${String(e)}`)\n }\n }\n return { ok: problems.length === 0, problems }\n}\n","/** The slots of a body an item can cover (the item slot vocabulary: game-items imports it from here). */\nexport type BodySlot = 'head' | 'eyes' | 'torso' | 'arms' | 'hands' | 'legs' | 'feet' | 'over'\n\n/** The 11 segments of the detailed body model (the Arsenal ids). */\nexport type SegmentKind = 'head' | 'jaw' | 'neck' | 'torso' | 'pelvis' | 'upperArm' | 'forearm' | 'hand' | 'thigh' | 'shin' | 'foot'\n\n/** The 6 regions of the crowd model (the game regions 0..5). */\nexport type BodyRegion = 'head' | 'torso' | 'armL' | 'armR' | 'legL' | 'legR'\n\n/** Bits of the `missing` mask of a body. The instanced body shader reads them, so the values are part of the contract. */\nexport const MISSING_BITS = {\n head: 1,\n armL: 2,\n armR: 4,\n legL: 8,\n legR: 16,\n armLBroken: 32,\n armRBroken: 64,\n legLBroken: 128,\n legRBroken: 256,\n jaw: 512,\n} as const\n","import type { DestructibleSpec, PartSpec } from './destruction'\n\nexport interface CheckSpecOptions {\n /** Known material ids. When given, every part's material must be one of them. */\n materials?: Iterable<string>\n}\n\nexport interface SpecReport {\n /** False when there is at least one problem (warnings do not count). */\n ok: boolean\n problems: string[]\n /** Things that are legal but probably wrong, e.g. structural parts that cannot reach an anchored part. */\n warnings: string[]\n}\n\nconst STRUCTURE_ROLES = ['foundation', 'column', 'beam', 'slab', 'wall', 'partition', 'roof', 'stair', 'none']\nconst BEARS = [true, false, 'a_on_b', 'b_on_a']\n\nconst finite = (n: unknown): n is number => typeof n === 'number' && Number.isFinite(n)\nconst positive = (n: unknown): boolean => finite(n) && n > 0\nconst vec3 = (v: unknown): boolean => Array.isArray(v) && v.length === 3 && v.every(finite)\nconst nonNegative = (n: unknown): boolean => n === undefined || (finite(n) && n >= 0)\n\nconst FACES = ['both', '+', '-']\nconst BONDS = ['running', 'stack']\n\n/** Layers, reinforcement, units and breach: known materials, finite positive sizes. */\nconst checkStagedFields = (p: PartSpec | undefined, label: string, materials: Set<string> | null, problems: string[]): void => {\n const known = (m: unknown, what: string): void => {\n if (materials && !materials.has(m as string)) problems.push(`${label} ${what} has unknown material \"${String(m)}\"`)\n }\n ;(p?.layers ?? []).forEach((l, i) => {\n known(l?.material, `layer ${i}`)\n if (!positive(l?.thickness)) problems.push(`${label} layer ${i} thickness must be finite and positive`)\n if (!FACES.includes(l?.faces)) problems.push(`${label} layer ${i} has an invalid faces \"${String(l?.faces)}\"`)\n })\n const r = p?.reinforcement\n if (r) {\n known(r.material, 'reinforcement')\n if (!positive(r.spacing) || !positive(r.diameter)) problems.push(`${label} reinforcement spacing and diameter must be finite and positive`)\n if (r.residualCapacity !== undefined && !(finite(r.residualCapacity) && r.residualCapacity >= 0 && r.residualCapacity <= 1)) problems.push(`${label} reinforcement residualCapacity must be between 0 and 1`)\n }\n const u = p?.units\n if (u) {\n known(u.mortar, 'units mortar')\n if (!vec3(u.size) || !u.size.every(positive)) problems.push(`${label} units size must be three finite positive numbers`)\n if (!BONDS.includes(u.bond)) problems.push(`${label} units has an invalid bond \"${String(u.bond)}\"`)\n }\n if (p?.breach && p.breach.area !== undefined && !positive(p.breach.area)) problems.push(`${label} breach area must be finite and positive`)\n}\n\n/**\n * Static checks on a `DestructibleSpec` (no world needed), for items that give a `spec` instead of an `impl`: unique part ids, known materials\n * (when `options.materials` is given), joints that reference existing, different parts, finite positive sizes, non-negative mass / hp / capacity,\n * valid structure roles and `bears` directions. A structural part with no path of joints to an anchored part is a warning, not a problem: it\n * is allowed, it just falls.\n */\nexport const checkSpec = (spec: DestructibleSpec, options: CheckSpecOptions = {}): SpecReport => {\n const problems: string[] = []\n const warnings: string[] = []\n const materials = options.materials ? new Set(options.materials) : null\n const parts = Array.isArray(spec?.parts) ? spec.parts : []\n if (!Array.isArray(spec?.parts) || parts.length === 0) problems.push('the spec needs at least one part')\n const ids = new Set<string>()\n for (const p of parts) {\n const id = String(p?.id ?? '')\n const label = `part \"${id}\"`\n if (id === '') problems.push('a part has no id')\n else if (ids.has(id)) problems.push(`duplicate part id \"${id}\"`)\n ids.add(id)\n if (materials && !materials.has(p?.material)) problems.push(`${label} has unknown material \"${String(p?.material)}\"`)\n else if (typeof p?.material !== 'string' || p.material === '') problems.push(`${label} needs a material`)\n const s = p?.shape\n if (s?.kind === 'box') {\n if (!Array.isArray(s.size) || s.size.length !== 3 || !s.size.every(positive)) problems.push(`${label} box size must be three finite positive numbers`)\n } else if (s?.kind === 'cylinder') {\n if (!positive(s.radius) || !positive(s.height)) problems.push(`${label} cylinder radius and height must be finite and positive`)\n } else if (s?.kind === 'sphere') {\n if (!positive(s.radius)) problems.push(`${label} sphere radius must be finite and positive`)\n } else problems.push(`${label} has an unknown shape`)\n if (!vec3(p?.position)) problems.push(`${label} needs a finite position [x, y, z]`)\n if (p?.rotation !== undefined && !vec3(p.rotation)) problems.push(`${label} has an invalid rotation`)\n for (const f of ['mass', 'hp', 'thickness'] as const) if (!nonNegative(p?.[f])) problems.push(`${label} ${f} must be a finite number >= 0`)\n if (p?.fill !== undefined && !(finite(p.fill) && p.fill >= 0 && p.fill <= 1)) problems.push(`${label} fill must be between 0 and 1`)\n if (p?.structure) {\n if (!STRUCTURE_ROLES.includes(p.structure.role)) problems.push(`${label} has an invalid structure role \"${String(p.structure.role)}\"`)\n if (!nonNegative(p.structure.capacity)) problems.push(`${label} structure capacity must be a finite number >= 0`)\n }\n if (p?.contains && !nonNegative(p.contains.liters)) problems.push(`${label} contains.liters must be a finite number >= 0`)\n checkStagedFields(p, label, materials, problems)\n }\n\n const jointIds = new Set<string>()\n const edges: Array<[string, string]> = []\n for (const j of spec?.joints ?? []) {\n const id = j.id ?? `${j.a}~${j.b}`\n const label = `joint \"${id}\"`\n if (jointIds.has(id)) problems.push(`duplicate joint id \"${id}\"`)\n jointIds.add(id)\n if (!ids.has(j.a)) problems.push(`${label} references unknown part \"${String(j.a)}\"`)\n if (!ids.has(j.b)) problems.push(`${label} references unknown part \"${String(j.b)}\"`)\n if (j.a === j.b) problems.push(`${label} joins part \"${String(j.a)}\" to itself`)\n if (j.bears !== undefined && !BEARS.includes(j.bears)) problems.push(`${label} has an invalid bears \"${String(j.bears)}\"`)\n if (!nonNegative(j.strength)) problems.push(`${label} strength must be a finite number >= 0`)\n if (!nonNegative(j.seconds)) problems.push(`${label} seconds must be a finite number >= 0`)\n if (j.bears !== false && ids.has(j.a) && ids.has(j.b) && j.a !== j.b) edges.push([j.a, j.b])\n }\n\n // structural parts must reach an anchored part over the joints (only when the spec lists its joints: otherwise they are inferred later)\n const structural = parts.filter((p) => p?.structure && p.structure.role !== 'none')\n if (structural.length && spec.joints) {\n const anchored = parts.filter((p) => p.anchored || p.structure?.role === 'foundation').map((p) => p.id)\n const adj = new Map<string, string[]>()\n for (const [a, b] of edges) {\n adj.set(a, [...(adj.get(a) ?? []), b])\n adj.set(b, [...(adj.get(b) ?? []), a])\n }\n const seen = new Set(anchored)\n const queue = [...anchored]\n while (queue.length) {\n for (const n of adj.get(queue.pop()!) ?? []) {\n if (seen.has(n)) continue\n seen.add(n)\n queue.push(n)\n }\n }\n const lost = structural.filter((p) => !seen.has(p.id)).map((p) => p.id)\n if (lost.length) warnings.push(`structural part(s) ${lost.map((i) => `\"${i}\"`).join(', ')} cannot reach an anchored part, so they will fall`)\n }\n return { ok: problems.length === 0, problems, warnings }\n}\n","import type { BallisticExtras, Hit, HitIgnition, Vec3 } from './destruction'\n\n/**\n * A ballistic shot (one per pellet) as a weapon module produces it. Plain data: any damage model can consume it (`shotToHit` makes the `Hit`).\n */\nexport interface Shot {\n origin: Vec3\n /** Unit vector. */\n direction: Vec3\n /** Muzzle energy in joules. */\n energy: number\n /** Bullet diameter in millimetres. */\n caliber: number\n /** snake_case calibre id, e.g. `9x19`. */\n caliberId: string\n /** Muzzle speed in m/s. */\n speed: number\n /** Projectile mass in grams (one pellet for a shotgun load). */\n mass?: number\n /** Energy at a distance in metres, with the calibre's drag. */\n energyAt(distance: number): number\n pellet: number\n /** snake_case weapon id. */\n weapon: string\n source?: string\n /** Kind of the round that was loaded (`fmj`, `hp`, `ap`, `buck`, `slug`, `incendiary`, `tracer`...). */\n ammo?: string\n /** Millimetres of steel, concrete, brick and wood the round goes through at the muzzle (the ammo table of the weapons module). `shotToHit` scales it by the energy left. */\n penetration?: Readonly<Record<string, number>>\n incendiary?: boolean\n tracer?: boolean\n /** Chances that the round lights flammable vapour or fuel. */\n ignition?: HitIgnition\n}\n\n/**\n * The fields of the round that a damage model takes besides the geometry: the penetration by material scaled by the energy that is left at `distance`\n * (linear in the energy: half the energy goes through half as much), the incendiary and tracer flags and the ignition chances. Pass it as the sixth\n * argument of `DestructionWorld.shoot` or spread it in a `Hit`.\n */\nexport const shotExtras = (shot: Shot, distance = 0): BallisticExtras => {\n const out: { -readonly [K in keyof BallisticExtras]: BallisticExtras[K] } = {}\n if (shot.penetration) {\n const k = shot.energy > 0 ? Math.min(1, Math.max(0, shot.energyAt(distance) / shot.energy)) : 1\n const scaled: Record<string, number> = {}\n for (const key of Object.keys(shot.penetration)) scaled[key] = shot.penetration[key]! * k\n out.penetration = scaled\n }\n if (shot.incendiary) out.incendiary = true\n if (shot.tracer) out.tracer = true\n if (shot.ignition) out.ignition = { spark: shot.ignition.spark, flame: shot.ignition.flame }\n return out\n}\n\n/** One use of a non-firearm (melee swing, flame tick, heat tick, throw, tool tick, extinguisher tick). */\nexport type Use =\n | { kind: 'melee'; damage: 'blunt' | 'slash'; energy: number; radius: number; reach: number; origin: Vec3; direction: Vec3 }\n | { kind: 'flame'; temperature: number; radius: number; range: number; cone: boolean; duration: number; origin: Vec3; direction: Vec3 }\n | { kind: 'heat'; temperature: number; radius: number; range: number; duration: number; origin: Vec3; direction: Vec3 }\n | {\n kind: 'throw'\n energy: number\n radius: number\n fuse: number\n velocity: Vec3\n origin: Vec3\n fire?: { temperature: number; radius: number; seconds: number }\n /** Explosive id (`m67`, `molotov`, `pg7v`...): the host looks the numbers up in the arsenal. Absent on a plain thrown object. */\n explosive?: string\n /** What sets it off: `timer` after `fuse` seconds, `impact` on the first hit once armed. */\n trigger?: 'timer' | 'impact'\n /** Metres the projectile must fly before an impact sets it off; closer, it hits without exploding (rocket launchers). */\n armDistance?: number\n /** Radius in metres of the wounding area (`radius` is the lethal one). */\n injuryRadius?: number\n /** Seconds the blast stuns whoever is inside the injury radius (flashbang). */\n stunSeconds?: number\n /** Seconds the thrower held it after pulling the pin: `fuse` is what was left of the full fuse when it left the hand. */\n cooked?: number\n /** Went off in the thrower's hand (held past the fuse): no velocity, `fuse` is 0 and the blast is at `origin`. */\n inHand?: boolean\n }\n /** An explosive set down where the trigger is pulled (mine, C4, charge): it waits for its trigger instead of flying. */\n | {\n kind: 'place'\n explosive: string\n /** `wire`: something trips it; `remote`: the player fires it; `timer`: after `fuse` seconds. */\n triggers: readonly ('timer' | 'wire' | 'remote')[]\n energy: number\n /** Lethal radius in metres. */\n radius: number\n injuryRadius: number\n /** Cone the blast goes through, in front of `direction` (mine); absent for a blast in every direction. */\n cone?: { halfAngleDeg: number; length: number }\n /** The wire a `wire` charge stretches from where it is set down: metres out, degrees from its facing (counter-clockwise from above) and metres above it. */\n tripwire?: { length: number; angleDeg: number; height: number }\n fuse?: number\n origin: Vec3\n direction: Vec3\n }\n /** The blast behind a rocket launcher: dangerous inside `radius` metres, in the cone around `direction` (pointing away from the target). */\n | { kind: 'backblast'; radius: number; halfAngleDeg: number; origin: Vec3; direction: Vec3 }\n | { kind: 'tool'; tool: string; reach: number; duration: number; origin: Vec3; direction: Vec3 }\n | { kind: 'extinguish'; range: number; coolC: number; duration: number; origin: Vec3; direction: Vec3 }\n | { kind: 'hand'; reach: number; origin: Vec3; direction: Vec3 }\n\n/**\n * The `Hit` a ballistic shot makes at `point` (world space): the energy is what is left of the shot at that distance from its origin\n * (`shot.energyAt`), the direction and calibre come from the shot. `normal` (the surface normal at the point, when the caller knows it) does not\n * change the hit; it is accepted so that callers can pass what their raycast returned and so that a stricter implementation could use it.\n */\nexport const shotToHit = (shot: Shot, point: Vec3, normal?: Vec3): Hit => {\n void normal\n const dx = point[0] - shot.origin[0]\n const dy = point[1] - shot.origin[1]\n const dz = point[2] - shot.origin[2]\n const distance = Math.hypot(dx, dy, dz)\n return {\n type: 'ballistic',\n point: [point[0], point[1], point[2]],\n direction: [shot.direction[0], shot.direction[1], shot.direction[2]],\n energy: shot.energyAt(distance),\n caliber: shot.caliber,\n ...(shot.mass === undefined ? {} : { mass: shot.mass }),\n ...shotExtras(shot, distance),\n source: shot.source ?? shot.weapon,\n }\n}\n\n/**\n * The `Hit` a non-firearm `Use` makes at `point` (world space), or `null` for uses that do not damage by themselves.\n *\n * - `melee` -> `blunt` / `slash` with the swing's `energy`, contact `radius` and `direction`;\n * - `flame` -> `fire` and `heat` -> `heat`, with `temperature`, `duration` and a contact radius of at most 0.3 m (one ray of the cone);\n * - `throw` -> the blast (`explosive`, `energy`, `radius`, centred on `point`, the place it detonates). A thrown item with no blast energy but\n * a `fire` patch (a molotov) gives that patch as a `fire` hit (`temperature`, `radius`, `seconds` as `duration`). No energy and no fire -> `null`;\n * - `tool`, `extinguish` and `hand` -> `null` (tools work on joints, the extinguisher cools, the hand only grabs).\n *\n * `normal` does not change the hit (same as `shotToHit`).\n */\nexport const useToHit = (use: Use, point: Vec3, normal?: Vec3): Hit | null => {\n void normal\n const p: Vec3 = [point[0], point[1], point[2]]\n switch (use.kind) {\n case 'melee':\n return { type: use.damage, point: p, direction: [use.direction[0], use.direction[1], use.direction[2]], energy: use.energy, radius: use.radius }\n case 'flame':\n case 'heat':\n return {\n type: use.kind === 'flame' ? 'fire' : 'heat',\n point: p,\n direction: [use.direction[0], use.direction[1], use.direction[2]],\n temperature: use.temperature,\n radius: Math.min(use.radius, 0.3),\n duration: use.duration,\n }\n case 'throw':\n if (use.energy > 0) return { type: 'explosive', point: p, energy: use.energy, radius: use.radius }\n if (use.fire) return { type: 'fire', point: p, temperature: use.fire.temperature, radius: use.fire.radius, duration: use.fire.seconds }\n return null\n default:\n return null\n }\n}\n","import type { Hit } from './destruction'\n\n/**\n * How a container of flammable fluid (a fuel tank, a jerrycan, a gas bottle) is set alight by what hits it. A normal bullet that goes through only\n * makes a leak; the leak lights when something supplies a flame: a spark of a hard round on metal, an incendiary or tracer round, a fire nearby\n * or a blast. These are the numbers of the target side; the numbers of the round (core, jacket, incendiary, tracer) are in the weapons module\n * (`rules.ignition` of `@adatechnology/game-weapons`) and reach the target in `Hit.ignition`. Every number is a default to be changed per game.\n */\nexport interface IgnitionRules {\n /**\n * How much of the spark and of the flame of a round reaches the vapour, by how full the container is: points `[filled, factor]`, `filled` 0..1\n * (litres over capacity), `factor` 0..1, linear between points. A full container holds a mixture too rich to burn (the vapour has no air) and\n * mostly leaks; a nearly empty one holds the vapour/air mixture of a flammable range and lights the easiest; a dry one has little vapour left.\n * Default: 0.6 empty, 1 at 10 percent, 0.6 at 35, 0.3 at 65, 0.15 at 90 and 0.12 full.\n */\n fillCurve: ReadonlyArray<readonly [number, number]>\n /** 0..1: how much of the fill factor also weighs on the flame of an incendiary or tracer (1: as much as on a spark; 0: not at all). Default 0.5. */\n flameFillInfluence: number\n /** Chance of the flame of a round that is flagged `incendiary` or `tracer` but carries no `ignition` numbers (a host that passes only the flags). */\n flameDefaults: { incendiary: number; tracer: number }\n /**\n * What a spark needs to strike: a factor 0..1 by material id of the struck part. A material that is not listed but is metal takes\n * `sparkMetalDefault`; a part that is not metal never sparks. Default: steel and iron 1, aluminium 0.25.\n */\n sparkTarget: Readonly<Record<string, number>>\n sparkMetalDefault: number\n /**\n * Factor 0..1 on the spark by fluid (default 1): diesel has a flash point of 52 degrees C and more, so a spark that lights petrol vapour does\n * not light it (a flame does). Default: diesel 0.15.\n */\n sparkFluid: Readonly<Record<string, number>>\n /** A leaking container of flammable fluid within `radius` metres of a burning part or a burning fuel stain lights at `chancePerSecond`. */\n fireNear: { radius: number; chancePerSecond: number }\n}\n\n/** Defaults of `IgnitionRules`. */\nexport const DEFAULT_IGNITION_RULES: IgnitionRules = {\n fillCurve: [\n [0, 0.6],\n [0.1, 1],\n [0.35, 0.6],\n [0.65, 0.3],\n [0.9, 0.15],\n [1, 0.12],\n ],\n flameFillInfluence: 0.5,\n flameDefaults: { incendiary: 0.7, tracer: 0.2 },\n sparkTarget: { steel: 1, iron: 1, aluminium: 0.25 },\n sparkMetalDefault: 0.5,\n sparkFluid: { diesel: 0.15 },\n fireNear: { radius: 1.5, chancePerSecond: 0.5 },\n}\n\n/** Merges a partial `IgnitionRules` over the defaults (an object is merged one level deep). */\nexport const resolveIgnitionRules = (rules?: Partial<IgnitionRules>): IgnitionRules => ({\n ...DEFAULT_IGNITION_RULES,\n ...rules,\n flameDefaults: { ...DEFAULT_IGNITION_RULES.flameDefaults, ...rules?.flameDefaults },\n sparkTarget: { ...DEFAULT_IGNITION_RULES.sparkTarget, ...rules?.sparkTarget },\n sparkFluid: { ...DEFAULT_IGNITION_RULES.sparkFluid, ...rules?.sparkFluid },\n fireNear: { ...DEFAULT_IGNITION_RULES.fireNear, ...rules?.fireNear },\n})\n\n/** Linear interpolation of the fill curve at `fill` (0..1); outside the points it keeps the first or the last. */\nexport const fillFactor = (fill: number, curve: IgnitionRules['fillCurve'] = DEFAULT_IGNITION_RULES.fillCurve): number => {\n if (curve.length === 0) return 1\n const f = Math.min(1, Math.max(0, fill))\n if (f <= curve[0]![0]) return curve[0]![1]\n for (let i = 1; i < curve.length; i++) {\n const [x1, y1] = curve[i]!\n const [x0, y0] = curve[i - 1]!\n if (f <= x1) return x1 === x0 ? y1 : y0 + ((y1 - y0) * (f - x0)) / (x1 - x0)\n }\n return curve[curve.length - 1]![1]\n}\n\n/** The factor of what a spark strikes: 0 when the struck part is not metal. */\nexport const sparkTargetFactor = (materialId: string, metal: boolean, rules: IgnitionRules = DEFAULT_IGNITION_RULES): number =>\n !metal ? 0 : rules.sparkTarget[materialId] ?? rules.sparkMetalDefault\n\n/**\n * Chance that one round that went through the wall of a container lights the fluid that leaks: the spark (only on metal) and the flame (any\n * wall), each weighed by the fill of the container; the two are independent. 0 when the round carries nothing (`Hit.ignition` absent and not\n * flagged `incendiary` or `tracer`): a plain bullet on a plain wall only leaks.\n */\nexport const ignitionChance = (hit: Pick<Hit, 'incendiary' | 'tracer' | 'ignition'>, target: { sparkTarget: number; fill: number; fluid?: string }, rules: IgnitionRules = DEFAULT_IGNITION_RULES): number => {\n const k = fillFactor(target.fill, rules.fillCurve)\n const spark = (hit.ignition?.spark ?? 0) * target.sparkTarget * (target.fluid === undefined ? 1 : rules.sparkFluid[target.fluid] ?? 1) * k\n const flameBase = hit.ignition?.flame ?? Math.max(hit.incendiary ? rules.flameDefaults.incendiary : 0, hit.tracer ? rules.flameDefaults.tracer : 0)\n const flame = flameBase * (1 - rules.flameFillInfluence * (1 - k))\n return 1 - (1 - Math.min(1, Math.max(0, spark))) * (1 - Math.min(1, Math.max(0, flame)))\n}\n","/**\n * The seal of a closed container (a food tin, a drink can, a jar, a bottle with a cap): what opens it and whether a breach spoils what is inside.\n * Plain data and one pure check, shared by the item rules (`resolveUse` of game-items) and any host that lets the player open and eat (the test room).\n */\nexport interface SealSpec {\n /** Ids of the tools that open it (items or weapons: `can_opener`, `knife`); any one of them is enough. */\n readonly openWith?: readonly string[]\n /** It opens by hand (a pull tab, a ring pull, a screw cap): no tool needed. */\n readonly byHand?: boolean\n /** A hole through the container or a leak spoils what is inside: it can no longer be eaten or drunk. */\n readonly spoilsWhenBreached?: boolean\n}\n\n/** The state of one container: opened, holed through (a shot, a blade) or leaking. */\nexport interface SealState {\n readonly opened?: boolean\n readonly breached?: boolean\n readonly leaked?: boolean\n}\n\n/** Why a sealed container cannot be eaten or drunk now. */\nexport type SealBlock = 'sealed' | 'breached' | 'leaked'\n\n/** Whether it can be opened with the tools at hand, and with which one (undefined when it opens by hand). */\nexport const sealOpener = (seal: SealSpec, tools: Iterable<string>): { readonly ok: boolean; readonly tool?: string } => {\n if (seal.byHand || !seal.openWith || seal.openWith.length === 0) return { ok: true }\n const have = new Set(tools)\n const tool = seal.openWith.find((id) => have.has(id))\n return tool === undefined ? { ok: false } : { ok: true, tool }\n}\n\n/** What stops eating or drinking it now, or undefined when nothing does. A spoiled container (breached, leaked) is blocked even once open. */\nexport const sealBlock = (seal: SealSpec, state: SealState): SealBlock | undefined => {\n if (seal.spoilsWhenBreached && state.breached) return 'breached'\n if (seal.spoilsWhenBreached && state.leaked) return 'leaked'\n if (!state.opened) return 'sealed'\n return undefined\n}\n","/** How a hand turns something round in strokes (a screw cap, a tap handle, a knob): what `twistStroke` answers for one moment of the turning. */\nexport interface TwistStroke {\n /** Radians the wrist is turned from the middle of its stroke, positive the way the part turns. */\n readonly angle: number\n /** Radians the part has turned in all, from 0 to `strokes * reach`. */\n readonly turned: number\n /** How closed the fingers are, 0 open to 1 closed: closed on the part while it turns, open while the wrist turns back. */\n readonly grip: number\n /** 0..1, how far the hand rises off the part while the wrist turns back (1 is `TWIST_LIFT` metres). */\n readonly lift: number\n}\n\nexport interface TwistOptions {\n /** Strokes of the wrist. Default 3. */\n readonly strokes?: number\n /** Radians the part turns in one stroke (a wrist turns about a quarter round with its hold). Default 1.6. */\n readonly reach?: number\n}\n\n/** Metres a hand rises off the part between two strokes. */\nexport const TWIST_LIFT = 0.012\n/** Share of each stroke spent turning the part; the rest turns the wrist back open. */\nconst TURNING = 0.7\n\nconst smooth = (k: number): number => k * k * (3 - 2 * k)\n\n/**\n * The hand turning a part round at `share` (0..1) of the whole turning: in each stroke the wrist turns with the part, then lets go, rises a little and turns back.\n * Pure and the same for every host, so first and third person turn the same hand the same way.\n */\nexport const twistStroke = (share: number, options: TwistOptions = {}): TwistStroke => {\n const strokes = Math.max(1, Math.round(options.strokes ?? 3))\n const reach = options.reach ?? 1.6\n const along = Math.min(1, Math.max(0, share)) * strokes\n const index = Math.min(strokes - 1, Math.floor(along))\n const phase = along - index\n if (phase <= TURNING) {\n const k = smooth(phase / TURNING)\n return { angle: reach * (k - 0.5), turned: reach * (index + k), grip: 1, lift: 0 }\n }\n const k = (phase - TURNING) / (1 - TURNING)\n return { angle: reach * (0.5 - smooth(k)), turned: reach * (index + 1), grip: 0.35, lift: Math.sin(k * Math.PI) }\n}\n","import type { Vec3 } from './destruction'\n\n/** Pose of one part (or of the whole item) at a key: position offset in metres and Euler XYZ rotation in radians, both relative to the rest pose. */\nexport interface AnimationPose {\n position?: Vec3\n rotation?: Vec3\n}\n\nexport interface AnimationKey {\n /** Seconds from the start of the clip. */\n t: number\n /** Pose of the item's root. */\n position?: Vec3\n rotation?: Vec3\n /** Poses of named parts: the key is a destruction part id (a pistol `reload` moves `mag`; a car door `open` rotates `door_l`). */\n parts?: Record<string, AnimationPose>\n}\n\nexport interface AnimationEventSpec {\n /** Seconds from the start of the clip. */\n t: number\n /** snake_case type the host maps (`sound`, `eject_casing`, `door_latch`...). */\n type: string\n data?: Record<string, unknown>\n}\n\n/** A use animation a module brings with its item. Poses between keys are interpolated by the player (the test room does it linearly). */\nexport interface AnimationClipSpec {\n /** snake_case: `reload`, `open`, `drink`... Unique within the item. */\n id: string\n /** Seconds. */\n duration: number\n keys: AnimationKey[]\n events?: AnimationEventSpec[]\n}\n\n/** What an item exposes when it brings its own animations. */\nexport interface ItemAnimationContract {\n animations: AnimationClipSpec[]\n}\n\nconst finite3 = (v: unknown): boolean => Array.isArray(v) && v.length === 3 && v.every((n) => typeof n === 'number' && Number.isFinite(n))\n\n/**\n * Problems with a clip (empty when it is valid): a snake_case id, a positive finite duration, at least one key, key and event times inside\n * `[0, duration]` and not going backwards, finite vectors, every named part one of `partIds`, event types that are non-empty strings and\n * JSON-serializable event data.\n */\nexport const checkAnimation = (clip: AnimationClipSpec, partIds: Iterable<string>): { ok: boolean; problems: string[] } => {\n const problems: string[] = []\n const parts = new Set(partIds)\n const id = String(clip?.id ?? '')\n if (!/^[a-z][a-z0-9_]*$/.test(id)) problems.push(`clip id \"${id}\" must be snake_case`)\n const label = `clip \"${id}\"`\n if (!(typeof clip?.duration === 'number' && Number.isFinite(clip.duration) && clip.duration > 0)) problems.push(`${label} needs a positive finite duration`)\n const dur = typeof clip?.duration === 'number' && clip.duration > 0 ? clip.duration : Infinity\n if (!Array.isArray(clip?.keys) || clip.keys.length === 0) {\n problems.push(`${label} needs at least one key`)\n return { ok: false, problems }\n }\n let prev = -Infinity\n clip.keys.forEach((k, i) => {\n if (!(typeof k.t === 'number' && Number.isFinite(k.t)) || k.t < 0 || k.t > dur + 1e-9) problems.push(`${label} key ${i} has t=${String(k.t)} outside 0..${dur}`)\n else if (k.t < prev) problems.push(`${label} key ${i} goes back in time (${k.t} < ${prev})`)\n else prev = k.t\n for (const f of ['position', 'rotation'] as const) if (k[f] !== undefined && !finite3(k[f])) problems.push(`${label} key ${i} has an invalid ${f}`)\n for (const [pid, pose] of Object.entries(k.parts ?? {})) {\n if (!parts.has(pid)) problems.push(`${label} key ${i} moves unknown part \"${pid}\"`)\n for (const f of ['position', 'rotation'] as const) if (pose?.[f] !== undefined && !finite3(pose[f])) problems.push(`${label} key ${i} part \"${pid}\" has an invalid ${f}`)\n }\n })\n let prevEv = -Infinity\n ;(clip.events ?? []).forEach((e, i) => {\n if (!(typeof e.t === 'number' && Number.isFinite(e.t)) || e.t < 0 || e.t > dur + 1e-9) problems.push(`${label} event ${i} has t=${String(e.t)} outside 0..${dur}`)\n else if (e.t < prevEv) problems.push(`${label} event ${i} goes back in time`)\n else prevEv = e.t\n if (typeof e.type !== 'string' || e.type === '') problems.push(`${label} event ${i} needs a type`)\n try {\n JSON.stringify(e.data ?? {})\n } catch {\n problems.push(`${label} event ${i} data is not JSON`)\n }\n })\n return { ok: problems.length === 0, problems }\n}\n","import type { AnimationClipSpec, AnimationEventSpec, AnimationPose } from './animation'\nimport { checkAnimation } from './animation'\nimport type { HostEvent } from './module'\n\n/** Clips a tool can bring, one per move of its cycle. A tool brings the ones it has; a missing clip is simply not played. */\nexport const TOOL_CLIP = {\n DRAW: 'draw',\n SHOT: 'shot',\n MISFIRE: 'misfire',\n EMPTY: 'empty',\n CYCLE: 'cycle',\n RELOAD: 'reload',\n FEED: 'feed',\n FAULT: 'fault',\n CLEAR: 'clear',\n SAFETY_ON: 'safety_on',\n SAFETY_OFF: 'safety_off',\n MODE: 'mode',\n /** A throwable with the pin out, held until it is thrown. */\n COOK: 'cook',\n /** The stock folds, unfolds, slides in or out (variants `fold`, `unfold`, `collapse`, `extend`). */\n STOCK: 'stock',\n /** A light or a laser is switched (variants `on`, `off`). */\n TOGGLE: 'toggle',\n} as const\nexport type ToolClipId = (typeof TOOL_CLIP)[keyof typeof TOOL_CLIP]\n\n/** What a renderer does when a rule event arrives. `variant` picks `<clip>_<variant>` when the tool has it, else `<clip>`. */\nexport type ToolAnimationStep =\n | { readonly type: 'play'; readonly clip: ToolClipId; readonly variant?: string; readonly duration?: number; readonly repeat?: number }\n /** Move the clip that is playing to the marker (a clip event `stage` with `data.stage`) the rule just reached. */\n | { readonly type: 'sync'; readonly stage: string }\n /** The rule says the phase is over: the clip jumps to its end if it is still running. */\n | { readonly type: 'finish'; readonly clip: ToolClipId }\n\nconst stockVariant = (event: Extract<HostEvent, { type: 'stock' }>): string => {\n const isFolding = event.kind === undefined ? false : event.kind.includes('fold')\n if (event.state === 'collapsed') return isFolding ? 'fold' : 'collapse'\n return isFolding ? 'unfold' : 'extend'\n}\n\n/** The move a host event asks of the tool in hand, or null when it has no motion. */\nexport const toolAnimationStep = (event: HostEvent): ToolAnimationStep | null => {\n switch (event.type) {\n case 'tool':\n return { type: 'play', clip: TOOL_CLIP.DRAW, ...(event.duration === undefined ? {} : { duration: event.duration }) }\n case 'shot':\n return { type: 'play', clip: TOOL_CLIP.SHOT, repeat: Math.max(1, event.rounds ?? 1) }\n case 'misfire':\n return { type: 'play', clip: TOOL_CLIP.MISFIRE }\n case 'empty':\n return { type: 'play', clip: TOOL_CLIP.EMPTY }\n case 'mode':\n return { type: 'play', clip: TOOL_CLIP.MODE, variant: event.mode }\n case 'fault':\n return { type: 'play', clip: TOOL_CLIP.FAULT, variant: event.fault }\n case 'clear':\n if (event.phase === 'end') return { type: 'finish', clip: TOOL_CLIP.CLEAR }\n return { type: 'play', clip: TOOL_CLIP.CLEAR, variant: event.fault, ...(event.duration === undefined ? {} : { duration: event.duration }) }\n case 'safety':\n return { type: 'play', clip: event.on ? TOOL_CLIP.SAFETY_ON : TOOL_CLIP.SAFETY_OFF }\n case 'cycle':\n if (event.phase === 'end') return { type: 'finish', clip: TOOL_CLIP.CYCLE }\n return { type: 'play', clip: TOOL_CLIP.CYCLE, ...(event.duration === undefined ? {} : { duration: event.duration }) }\n case 'reload':\n if (event.phase === 'end') return { type: 'finish', clip: TOOL_CLIP.RELOAD }\n return { type: 'play', clip: TOOL_CLIP.RELOAD, ...(event.kind === undefined ? {} : { variant: event.kind }), ...(event.duration === undefined ? {} : { duration: event.duration }) }\n case 'stage':\n return { type: 'sync', stage: event.stage }\n case 'feed':\n return { type: 'play', clip: TOOL_CLIP.FEED }\n case 'cook':\n if (event.phase === 'start') return { type: 'play', clip: TOOL_CLIP.COOK, duration: event.fuse }\n return { type: 'finish', clip: TOOL_CLIP.COOK }\n case 'stock':\n return { type: 'play', clip: TOOL_CLIP.STOCK, variant: stockVariant(event), ...(event.duration === undefined ? {} : { duration: event.duration }) }\n case 'toggle':\n return { type: 'play', clip: TOOL_CLIP.TOGGLE, variant: event.on ? 'on' : 'off' }\n default:\n return null\n }\n}\n\n/** Where a renderer puts the poses: every part of the tool by id (the ids the clips move), and the whole tool. Poses are offsets from rest. */\nexport interface ToolRig {\n setPartPose(partId: string, pose: AnimationPose): void\n setRootPose?(pose: AnimationPose): void\n}\n\n/** Clips by id, with the variants as `<clip>_<variant>`. */\nexport type ToolClipSet = Readonly<Record<string, AnimationClipSpec>>\n\n/** A renderer's tool animator: feed it the host events and the frame time. */\nexport interface ToolAnimator {\n handle(event: HostEvent): void\n update(dt: number): void\n /** Ids of the clips playing now. */\n readonly playing: readonly string[]\n dispose(): void\n}\n\nexport const STAGE_EVENT = 'stage'\n\n/** The clip with its times (keys and events) scaled so that it lasts `duration` seconds. */\nexport const scaleClip = (clip: AnimationClipSpec, duration: number): AnimationClipSpec => {\n const factor = clip.duration > 0 ? duration / clip.duration : 1\n return {\n ...clip,\n duration,\n keys: clip.keys.map((key) => ({ ...key, t: key.t * factor })),\n ...(clip.events ? { events: clip.events.map((event) => ({ ...event, t: event.t * factor })) } : {}),\n }\n}\n\nconst ZERO: [number, number, number] = [0, 0, 0]\nconst mix = (from: readonly number[], to: readonly number[], fraction: number): [number, number, number] => [\n (from[0] ?? 0) + ((to[0] ?? 0) - (from[0] ?? 0)) * fraction,\n (from[1] ?? 0) + ((to[1] ?? 0) - (from[1] ?? 0)) * fraction,\n (from[2] ?? 0) + ((to[2] ?? 0) - (from[2] ?? 0)) * fraction,\n]\n\n/** Pose of the whole tool (`partId` undefined) or of one part at `time`, linear between keys; a key that does not name it means rest. */\nexport const sampleClip = (clip: AnimationClipSpec, partId: string | undefined, time: number): { position: [number, number, number]; rotation: [number, number, number] } => {\n const points = clip.keys.map((key) => {\n const pose = partId === undefined ? key : key.parts?.[partId]\n return { t: key.t, position: pose?.position ?? ZERO, rotation: pose?.rotation ?? ZERO }\n })\n const first = points[0]\n if (!first || first.t > 0) points.unshift({ t: 0, position: ZERO, rotation: ZERO })\n const last = points[points.length - 1]\n if (!last) return { position: ZERO, rotation: ZERO }\n if (time >= last.t) return { position: mix(last.position, last.position, 0), rotation: mix(last.rotation, last.rotation, 0) }\n const index = Math.max(0, points.findIndex((point, i) => time >= point.t && time < (points[i + 1]?.t ?? Infinity)))\n const from = points[index]\n const to = points[index + 1]\n if (!from || !to) return { position: ZERO, rotation: ZERO }\n const fraction = to.t > from.t ? (time - from.t) / (to.t - from.t) : 0\n return { position: mix(from.position, to.position, fraction), rotation: mix(from.rotation, to.rotation, fraction) }\n}\n\n/** Problems with a clip set (empty when valid): each key is the clip id (or `<clip>_<variant>`), every clip passes `checkAnimation`, and a `stage` event names its stage. */\nexport const checkToolClips = (clips: ToolClipSet, partIds: Iterable<string>): { ok: boolean; problems: string[] } => {\n const parts = [...partIds]\n const problems: string[] = []\n for (const [key, clip] of Object.entries(clips)) {\n if (clip.id !== key) problems.push(`clip \"${key}\" has the id \"${clip.id}\"`)\n if (!(clip.duration > 0)) problems.push(`clip \"${key}\" has no duration`)\n problems.push(...checkAnimation(clip, parts).problems)\n for (const event of clip.events ?? []) if (event.type === STAGE_EVENT && typeof event.data?.stage !== 'string') problems.push(`clip \"${key}\" has a stage event without data.stage`)\n }\n return { ok: problems.length === 0, problems }\n}\n\ninterface Instance {\n readonly name: string\n readonly clip: AnimationClipSpec\n time: number\n delay: number\n fired: number\n}\n\nexport interface ToolClipPlayerOptions {\n clips: ToolClipSet\n rig: ToolRig\n /** Called for each clip event as its time passes (`eject_casing`, `drop_magazine`, `stage`...). */\n onEvent?(event: AnimationEventSpec, clip: string): void\n /** Seconds between the rounds of a `repeat` (a burst): default 0.1. */\n repeatInterval?(): number\n}\n\n/** The default player of a tool's clips: runs the steps of `toolAnimationStep` against a rig, scaled to the duration the rule gives. */\nexport interface ToolClipPlayer {\n apply(step: ToolAnimationStep): void\n update(dt: number): void\n readonly playing: readonly string[]\n /** The clips being played with the time each has reached and the clip itself (scaled to the duration the rule gave), for a renderer that has to follow what the clip does now (the hands working a part). */\n readonly running: ReadonlyArray<{ readonly name: string; readonly time: number; readonly clip: AnimationClipSpec }>\n}\n\nexport const createToolClipPlayer = (options: ToolClipPlayerOptions): ToolClipPlayer => {\n const { clips, rig, onEvent } = options\n let instances: Instance[] = []\n\n const find = (clip: string, variant?: string): { name: string; clip: AnimationClipSpec } | undefined => {\n const name = variant !== undefined && clips[`${clip}_${variant}`] ? `${clip}_${variant}` : clip\n const found = clips[name]\n return found ? { name, clip: found } : undefined\n }\n\n const render = (instance: Instance): void => {\n const time = Math.min(instance.time, instance.clip.duration)\n const root = sampleClip(instance.clip, undefined, time)\n if (instance.clip.keys.some((key) => key.position || key.rotation)) rig.setRootPose?.(root)\n const touched = new Set(instance.clip.keys.flatMap((key) => Object.keys(key.parts ?? {})))\n for (const id of touched) rig.setPartPose(id, sampleClip(instance.clip, id, time))\n }\n\n const fireEvents = (instance: Instance, upTo: number): void => {\n const events = instance.clip.events ?? []\n while (instance.fired < events.length && (events[instance.fired]?.t ?? Infinity) <= upTo + 1e-9) {\n const event = events[instance.fired]\n instance.fired += 1\n if (event) onEvent?.(event, instance.name)\n }\n }\n\n return {\n apply(step) {\n if (step.type === 'sync') {\n for (const instance of instances) {\n const marker = (instance.clip.events ?? []).find((event) => event.type === STAGE_EVENT && event.data?.stage === step.stage)\n if (marker && instance.time < marker.t) instance.time = marker.t\n }\n return\n }\n if (step.type === 'finish') {\n for (const instance of instances) {\n if (instance.name !== step.clip && !instance.name.startsWith(`${step.clip}_`)) continue\n fireEvents(instance, instance.clip.duration)\n instance.time = instance.clip.duration\n }\n return\n }\n const found = find(step.clip, step.variant)\n if (!found) return\n const clip = step.duration && step.duration > 0 ? scaleClip(found.clip, step.duration) : found.clip\n const interval = options.repeatInterval?.() ?? 0.1\n instances = instances.filter((existing) => existing.name !== found.name || existing.delay > 0)\n for (let round = 0; round < (step.repeat ?? 1); round++) {\n instances.push({ name: found.name, clip, time: 0, delay: round * interval, fired: 0 })\n }\n },\n update(dt) {\n for (const instance of instances) {\n const carried = instance.delay > 0 ? dt - instance.delay : dt\n if (instance.delay > 0) {\n instance.delay = Math.max(0, instance.delay - dt)\n if (instance.delay > 0) continue\n }\n instance.time += carried\n fireEvents(instance, instance.time)\n render(instance)\n }\n instances = instances.filter((instance) => instance.delay > 0 || instance.time < instance.clip.duration)\n },\n get playing() {\n return instances.map((instance) => instance.name)\n },\n get running() {\n return instances.filter((instance) => instance.delay <= 0).map((instance) => ({ name: instance.name, time: instance.time, clip: instance.clip }))\n },\n }\n}\n","export const FRAME_PHASES = ['input', 'fixedSimulate', 'lateSimulate', 'present', 'render', 'idle'] as const\nexport type FramePhase = (typeof FRAME_PHASES)[number]\n\n/**\n * Presentation work a system leaves for later frames, run in small units within a shared time budget.\n * Never simulation: nothing that consumes a seeded generator or decides an outcome may wait in a queue.\n */\nexport interface DeferredQueue {\n /** Stable name for stats and logs (see `DEFERRED_QUEUE_ID`). */\n readonly id: string\n /**\n * Units waiting. 0 means idle; the budget skips the queue without calling `run`. `frame` is the budget's frame counter: a queue may\n * report 0 for the rest of a frame (the crease queue does in the frame a panel was torn). Same value for repeated calls in one frame.\n */\n pending(frame?: number): number\n /**\n * Runs units in order until `clock() >= deadline`. Contract: when `pending() > 0` it runs **at least one** unit, even with a deadline\n * already past; it never runs a unit after it saw the deadline pass. Returns how many units it ran.\n */\n run(deadline: number, clock: () => number): number\n /** Runs everything now (synchronous step, clear, dispose). */\n flush(): void\n /**\n * Called by `budget.register`: from now on the owner stops draining on its own (no MessageChannel, no drain inside `update`).\n * Never called by a getter; a queue nobody registered keeps today's internal draining.\n */\n attach?(): void\n /** Called by the unregister function and by `budget.dispose`: the owner goes back to its internal draining. */\n detach?(): void\n}\n\n/** Where a module registers a deferred queue with the host's budget. */\nexport interface DeferredQueueOptions {\n /** Higher runs first. See DEFERRED_PRIORITY. */\n readonly priority: number\n /** `present` runs before the render (the work must show this frame); `idle` runs after it. Default `idle`. */\n readonly phase?: 'present' | 'idle'\n /** Starvation guard: the queue runs at least one unit every this many frames, even with the budget spent. Default 4. */\n readonly minEveryFrames?: number\n /** Milliseconds of every frame kept for this queue before the others share the rest (when it has work). Default 0. */\n readonly reserveMs?: number\n}\n\n/** The ids of the kit's queues: what `DeferredQueue.id` says and what `DEFERRED_PRIORITY` is keyed by. */\nexport const DEFERRED_QUEUE_ID = {\n DESTRUCTION_EVENTS: 'destruction.events',\n BREACH_PAINT: 'floorplan.breach-paint',\n AUDIO_VOICES: 'audio.voices',\n VEHICLE_CREASE: 'vehicles.crease',\n} as const\n\n/** Default priorities and reserves of the kit's queues; a host may override any of them. */\nexport const DEFERRED_PRIORITY = {\n [DEFERRED_QUEUE_ID.DESTRUCTION_EVENTS]: { priority: 300, phase: 'present' },\n [DEFERRED_QUEUE_ID.BREACH_PAINT]: { priority: 250, phase: 'present' },\n [DEFERRED_QUEUE_ID.AUDIO_VOICES]: { priority: 200, phase: 'idle', reserveMs: 1 },\n [DEFERRED_QUEUE_ID.VEHICLE_CREASE]: { priority: 100, phase: 'idle' },\n} as const satisfies Readonly<Record<string, DeferredQueueOptions>>\n\nexport interface DeferredQueueReport {\n readonly ok: boolean\n readonly problems: readonly string[]\n}\n\n/**\n * Checks the `DeferredQueue` contract. `fill` leaves the queue with at least two units waiting (the same call is made on a fresh queue).\n * It cannot prove the queue only carries presentation work: that is a rule of review.\n */\nexport function checkDeferredQueue(queue: DeferredQueue, fill: () => void): DeferredQueueReport {\n const problems: string[] = []\n fill()\n const waiting = queue.pending(1)\n if (waiting < 2) problems.push(`fill() must leave at least 2 units waiting (pending is ${waiting})`)\n if (queue.pending(1) !== waiting) problems.push('pending(frame) changed between two calls in the same frame')\n const ran = queue.run(0, () => 1)\n if (ran !== 1) problems.push(`run with a deadline already past must run exactly one unit (ran ${ran})`)\n if (waiting >= 2 && queue.pending(2) !== waiting - 1) problems.push(`one unit ran but pending went from ${waiting} to ${queue.pending(2)}`)\n try {\n queue.attach?.()\n queue.attach?.()\n queue.detach?.()\n queue.detach?.()\n } catch (error) {\n problems.push(`attach/detach must be idempotent: ${error instanceof Error ? error.message : String(error)}`)\n }\n queue.flush()\n if (queue.pending(3) !== 0) problems.push(`flush must leave nothing pending (pending is ${queue.pending(3)})`)\n if (queue.run(0, () => 1) !== 0) problems.push('run on an empty queue must run no unit')\n return { ok: problems.length === 0, problems }\n}\n","const WARNED_KEY = Symbol.for('@adatechnology/game-contracts.warned')\n\ntype WarnedHolder = { [WARNED_KEY]?: Set<string> }\n\nconst warnedKeys = (): Set<string> => {\n const holder = globalThis as WarnedHolder\n return (holder[WARNED_KEY] ??= new Set<string>())\n}\n\ndeclare const process: { readonly env: { readonly NODE_ENV?: string } }\n\n// The literal `process.env.NODE_ENV` is what bundlers replace; where there is no `process` the reference throws and counts as development.\nconst isProduction = (): boolean => {\n try {\n return process.env.NODE_ENV === 'production'\n } catch {\n return false\n }\n}\n\nconst isQuiet = (): boolean => (globalThis as { __GAME_KIT_QUIET__?: unknown }).__GAME_KIT_QUIET__ === true\n\n/**\n * Warns on the console the first time `key` is seen, and only outside production and when `globalThis.__GAME_KIT_QUIET__` is not `true`: for a silent fallback that keeps working (an adapter that made its own effects\n * world or audio engine because the host lent none). The seen keys live on `globalThis`, so several bundles of the kit on one page warn once together.\n */\nexport const warnOnce = (key: string, message: string): void => {\n if (isQuiet() || isProduction()) return\n const seen = warnedKeys()\n if (seen.has(key)) return\n seen.add(key)\n console.warn(`[game-kit] ${message}`)\n}\n\n/** Forgets what `warnOnce` has seen (tests that count warnings). */\nexport const resetWarnOnce = (): void => warnedKeys().clear()\n","import { warnOnce } from './warn'\n\ntype PlainObject = Record<string, unknown>\ntype Entry = { readonly id: string }\n\n/** Keys that would reach the prototype of the result when a patch comes from parsed JSON. */\nconst UNSAFE_KEYS: ReadonlySet<string> = new Set(['__proto__', 'constructor', 'prototype'])\n\nconst isPlainObject = (value: unknown): value is PlainObject =>\n typeof value === 'object' && value !== null && !Array.isArray(value) && Object.getPrototypeOf(value) === Object.prototype\n\n/** Recursively optional; arrays and functions are replaced as a whole, objects are merged. */\nexport type DeepPartial<TValue> = TValue extends (...args: never[]) => unknown\n ? TValue\n : TValue extends readonly unknown[]\n ? TValue\n : TValue extends object\n ? { [TKey in keyof TValue]?: DeepPartial<TValue[TKey]> }\n : TValue\n\n/** Merges `patch` over `base`: plain objects merge field by field, arrays, functions and primitives replace. `undefined` in the patch is ignored. */\nexport const deepMerge = <TBase extends object>(base: TBase, patch: object | undefined): TBase => {\n if (!patch) return base\n const result: PlainObject = { ...(base as PlainObject) }\n for (const [key, value] of Object.entries(patch)) {\n if (value === undefined || UNSAFE_KEYS.has(key)) continue\n const current = result[key]\n result[key] = isPlainObject(current) && isPlainObject(value) ? deepMerge(current, value) : value\n }\n return result as TBase\n}\n\n/** The shipped entries with the ones from `extra` added or, when the id exists, replaced. */\nexport const mergeById = <TEntry extends Entry>(shipped: readonly TEntry[], extra: readonly TEntry[] | undefined): Map<string, TEntry> => {\n const entries = new Map<string, TEntry>()\n for (const entry of [...shipped, ...(extra ?? [])]) entries.set(entry.id, entry)\n return entries\n}\n\n/** Applies `patch` by id; a patch for an id that does not exist is ignored (and warned about once, in development). */\nexport const applyPatches = <TEntry extends Entry>(entries: Map<string, TEntry>, patches: Readonly<Record<string, DeepPartial<TEntry> | Partial<TEntry>>> | undefined): void => {\n for (const [id, patch] of Object.entries(patches ?? {})) {\n const entry = entries.get(id)\n if (entry) entries.set(id, deepMerge(entry, patch))\n else warnOnce(`registry.patch.${id}`, `patch for \"${id}\" ignored: no entry has that id`)\n }\n}\n\n/** Takes the ids out; an id that does not exist is ignored (and warned about once, in development). */\nexport const removeIds = (entries: Map<string, unknown>, ids: readonly string[] | undefined): void => {\n for (const id of ids ?? []) {\n if (!entries.delete(id)) warnOnce(`registry.remove.${id}`, `remove of \"${id}\" ignored: no entry has that id`)\n }\n}\n","import type { Vec3 } from './destruction'\n\n/**\n * The bench of a module: controls, layers and counters it declares as data. The host keeps the values, draws them in a generic way and hands\n * every change back through `ModuleHost.bench`. Nothing here is specific to a kind of module.\n */\nexport type ControlValue = number | boolean | string\n\nexport interface ChoiceOption {\n readonly id: string\n readonly name: string\n /** Heading the host lists the option under (`optgroup`, a grouped row of buttons). Options of a group must sit next to each other. */\n readonly group?: string\n}\n\ninterface ControlBase {\n /** snake_case, unique across the module's controls, layers and metrics. */\n readonly id: string\n /** Display name, already translated for the locale the host asked for. */\n readonly name: string\n readonly hint?: string\n readonly hidden?: boolean\n /**\n * The change (or the press, for an action) takes a noticeable time, a third of a second or more: the host shows its loading indicator while it works, with this text, or the control's name for `true`.\n * Only the host's own controls panel does it; a change made through `TestRoom.bench().set / act` runs at once.\n */\n readonly slow?: boolean | string\n}\n\nexport interface NumberControl extends ControlBase {\n readonly kind: 'number'\n readonly min: number\n readonly max: number\n readonly step?: number\n readonly unit?: string\n readonly default: number\n}\n\nexport interface BooleanControl extends ControlBase {\n readonly kind: 'boolean'\n readonly default: boolean\n}\n\nexport interface ChoiceControl extends ControlBase {\n readonly kind: 'choice'\n readonly options: readonly ChoiceOption[]\n readonly default: string\n}\n\n/** A button: no value, only the press. */\nexport interface ActionControl extends ControlBase {\n readonly kind: 'action'\n}\n\nexport type ModuleControl = NumberControl | BooleanControl | ChoiceControl | ActionControl\n\n/** Something of the module the viewer turns on or off (smoke, flash, casings). */\nexport interface ModuleLayer {\n readonly id: string\n readonly name: string\n /** On unless `false`. */\n readonly default?: boolean\n}\n\n/** Pulled: the room calls read only when it samples (~4 Hz, or on TestRoom.metrics()). */\nexport interface ModuleMetric {\n readonly id: string\n readonly name: string\n readonly unit?: string\n /** Budget: the host shows a bar and warns past it. */\n readonly max?: number\n readonly digits?: number\n read(): number | string\n}\n\nexport type BenchChange =\n | { readonly kind: 'control'; readonly id: string; readonly value: ControlValue }\n | { readonly kind: 'layer'; readonly id: string; readonly on: boolean }\n | { readonly kind: 'action'; readonly id: string }\n\nexport interface ControlPatch {\n readonly hidden?: boolean\n readonly disabled?: boolean\n readonly options?: readonly ChoiceOption[]\n readonly value?: ControlValue\n}\n\n/** What the host lends a module that declares a bench: the current values and the changes. */\nexport interface ModuleBench {\n value(id: string): ControlValue | undefined\n layer(id: string): boolean\n /** Right after attach returns, the host sends one change per control (not actions) and per layer, in declaration order. */\n onChange(listener: (change: BenchChange) => void): () => void\n /** `value` sets without emitting (no echo loops); `options` falls back to the first option if the value is no longer offered. */\n patch(id: string, patch: ControlPatch): void\n /** Move the host camera (a near / middle / far choice). */\n look(eye: Vec3, target: Vec3): void\n /** Shows a sheet on the host's panel, or takes it down with `null`. A host without a sheet area ignores it. */\n sheet?(sheet: BenchSheet | null): void\n}\n\n/**\n * A card of text the module puts on the host's panel (the sheet of an effect, of a weapon): what it is, its numbers, its steps, its colours and,\n * optionally, a strip of segments over time. Names and texts are already translated for the host's locale.\n */\nexport interface BenchSheet {\n readonly kicker?: string\n readonly title: string\n readonly body?: string\n readonly facts?: readonly { readonly label: string; readonly value: string }[]\n readonly steps?: readonly { readonly at: string; readonly text: string }[]\n readonly swatches?: readonly { readonly color: string; readonly name: string }[]\n readonly note?: string\n /** A strip of named segments (seconds), with tick marks and a playhead that follows the module metric `metric`. */\n readonly timeline?: {\n readonly segments: readonly { readonly name: string; readonly start: number; readonly end: number; readonly color: string }[]\n readonly ticks: readonly number[]\n readonly metric: string\n }\n}\n\nexport interface BenchReport {\n readonly ok: boolean\n readonly problems: readonly string[]\n}\n\nexport interface BenchDeclaration {\n readonly controls?: readonly ModuleControl[]\n readonly layers?: readonly ModuleLayer[]\n readonly metrics?: readonly ModuleMetric[]\n}\n\nconst SNAKE_CASE = /^[a-z][a-z0-9_]*$/\nconst finite = (n: unknown): n is number => typeof n === 'number' && Number.isFinite(n)\n\nfunction checkControl(control: ModuleControl, problems: string[]): void {\n const label = `control \"${control.id}\"`\n if (control.kind === 'number') {\n if (!finite(control.min) || !finite(control.max) || control.min >= control.max) problems.push(`${label} needs min < max`)\n if (control.step !== undefined && !(finite(control.step) && control.step > 0)) problems.push(`${label} needs a step > 0`)\n if (!finite(control.default) || control.default < control.min || control.default > control.max) problems.push(`${label} default ${String(control.default)} is outside ${control.min}..${control.max}`)\n return\n }\n if (control.kind !== 'choice') return\n if (control.options.length === 0) {\n problems.push(`${label} needs at least one option`)\n return\n }\n const seen = new Set<string>()\n const groups = new Set<string>()\n let previousGroup: string | undefined\n for (const option of control.options) {\n if (seen.has(option.id)) problems.push(`${label} repeats option \"${option.id}\"`)\n seen.add(option.id)\n if (option.group !== undefined && (typeof option.group !== 'string' || option.group === '')) problems.push(`${label} option \"${option.id}\" has an empty group`)\n else if (option.group !== undefined && option.group !== previousGroup && groups.has(option.group)) problems.push(`${label} group \"${option.group}\" is split: its options must be next to each other`)\n if (typeof option.group === 'string' && option.group !== '') groups.add(option.group)\n previousGroup = option.group\n }\n if (!seen.has(control.default)) problems.push(`${label} default \"${control.default}\" is not among its options`)\n}\n\n/** snake_case ids unique across controls, layers and metrics; min < max; step > 0; default in range; options non-empty, unique, default among them, each group in one run; metric max > 0. */\nexport function checkBench(declared: BenchDeclaration): BenchReport {\n const problems: string[] = []\n const ids = new Set<string>()\n const claim = (kind: string, id: string): void => {\n if (!SNAKE_CASE.test(id)) problems.push(`${kind} id \"${id}\" must be snake_case`)\n if (ids.has(id)) problems.push(`duplicate id \"${id}\" across controls, layers and metrics`)\n ids.add(id)\n }\n for (const control of declared.controls ?? []) {\n claim('control', control.id)\n checkControl(control, problems)\n }\n for (const layer of declared.layers ?? []) claim('layer', layer.id)\n for (const metric of declared.metrics ?? []) {\n claim('metric', metric.id)\n if (metric.max !== undefined && !(finite(metric.max) && metric.max > 0)) problems.push(`metric \"${metric.id}\" needs a max > 0`)\n }\n return { ok: problems.length === 0, problems }\n}\n","/** The weather, light and ground as the world is right now. Written by whoever drives the weather, read by whoever reacts to it. */\nexport interface EnvironmentState {\n /** Rain in mm/h; 0 is dry. */\n readonly rain: number\n /** Wind in m/s along x and z, and its strength (the length of that vector). */\n readonly windX: number\n readonly windZ: number\n readonly windSpeed: number\n /** Strongest gust in m/s. */\n readonly gust: number\n /** Render fog far distance in metres. */\n readonly fogFar: number\n /** How far one can see for gameplay, in metres (not the render fog). */\n readonly visibility: number\n /** 0 is full daylight, 1 is the darkest night. */\n readonly nightFactor: number\n readonly temperatureC: number\n /** Wet ground, 0..1: rises with rain and dries after it stops. */\n readonly wetness: number\n}\n\nexport type EnvironmentKey = keyof EnvironmentState\n\n/** A calm dry day (the normal world, before anything went wrong). */\nexport const DEFAULT_ENVIRONMENT: EnvironmentState = {\n rain: 0,\n windX: 0,\n windZ: 0,\n windSpeed: 0,\n gust: 0,\n fogFar: 220,\n visibility: 220,\n nightFactor: 0,\n temperatureC: 20,\n wetness: 0,\n}\n\n/** The host's environment (`ModuleHost.environment`): a store the weather module writes to and the others read. */\nexport interface EnvironmentBus {\n get(): EnvironmentState\n /** Merges `patch` into the state; listeners hear it only when a value really changed. */\n set(patch: Partial<EnvironmentState>): void\n /** Hears every change with the keys that changed. Returns the unsubscribe function. */\n on(listener: (state: EnvironmentState, changed: readonly EnvironmentKey[]) => void): () => void\n}\n\n/** A plain store that implements the bus; the test room and tests use it, a game may bring its own. */\nexport function createEnvironmentBus(initial: Partial<EnvironmentState> = {}): EnvironmentBus {\n let state: EnvironmentState = { ...DEFAULT_ENVIRONMENT, ...initial }\n const listeners = new Set<(state: EnvironmentState, changed: readonly EnvironmentKey[]) => void>()\n return {\n get: () => state,\n set(patch) {\n const changed = (Object.keys(patch) as EnvironmentKey[]).filter((key) => patch[key] !== undefined && patch[key] !== state[key])\n if (changed.length === 0) return\n const next = { ...state } as { -readonly [Key in EnvironmentKey]: number }\n for (const key of changed) next[key] = patch[key] as number\n state = next\n for (const listener of [...listeners]) listener(state, changed)\n },\n on(listener) {\n listeners.add(listener)\n return () => void listeners.delete(listener)\n },\n }\n}\n","import { applyPatches, deepMerge, mergeById, removeIds } from './registry'\nimport type { DeepPartial } from './registry'\n\n/** One category (kind) of items. Defaults and required contracts are inherited from the `extends` chain. */\nexport interface CategoryDef<TDefaults extends object = Record<string, unknown>> {\n readonly id: string\n /** Parent category id; the child inherits its defaults and its required contracts. */\n readonly extends?: string\n /** Field values every entry of this kind starts with; objects merge down the chain, arrays replace. */\n readonly defaults?: DeepPartial<TDefaults>\n /** Contract ids (`ContractChecker.id`) every entry of this kind, or of a descendant, must satisfy. */\n readonly requires?: readonly string[]\n /** An abstract category only groups: entries cannot declare it directly, only its descendants. */\n readonly abstract?: boolean\n}\n\n/** Where a checker is being run: the entry's kind and the chain of categories root first. */\nexport interface ContractContext {\n readonly kind: string\n readonly chain: readonly string[]\n}\n\n/** A named contract: returns the problems of an entry, each a short phrase like `missing leftover.despawnSeconds`. Empty means it holds. */\nexport interface ContractChecker<TEntry> {\n readonly id: string\n check(entry: TEntry, ctx: ContractContext): string[]\n}\n\nexport interface CategoryOptions {\n /** Categories added to the shipped ones; the same id replaces. */\n readonly extra?: readonly CategoryDef[]\n /** Partial changes by id, applied after `extra`. */\n readonly patches?: Readonly<Record<string, DeepPartial<CategoryDef> | Partial<CategoryDef>>>\n /** Ids taken out of the tree (their children are then reported by `check()`). */\n readonly remove?: readonly string[]\n}\n\nexport interface CategoryRegistry {\n get(id: string): CategoryDef | undefined\n has(id: string): boolean\n ids(): string[]\n /** Root first, ending at `id`. Throws on an unknown id, an unknown parent or a cycle. */\n chain(id: string): string[]\n /** `defaults` merged along the chain, root first, so the closest category wins. */\n defaults(id: string): Record<string, unknown>\n /** Union of `requires` along the chain, order kept, without duplicates. */\n requires(id: string): string[]\n /** True when `id` is `ancestorId` or descends from it. */\n isA(id: string, ancestorId: string): boolean\n /** Problems in the tree itself: duplicate ids, unknown `extends`, cycles. */\n check(): string[]\n}\n\nexport interface CategorizedOptions {\n /** Field of the entry that names its category. Default `kind`. */\n readonly kindField?: string\n /** Known gaps by entry id: problems starting with one of these texts go to `allowed`, so the list can shrink over time. */\n readonly allow?: Readonly<Record<string, readonly string[]>>\n}\n\nexport interface CategorizedReport {\n readonly ok: boolean\n readonly problems: string[]\n readonly allowed: string[]\n}\n\nexport const defineCategories = (defs: readonly CategoryDef[], options: CategoryOptions = {}): CategoryRegistry => {\n const problems: string[] = []\n const duplicates = (list: readonly CategoryDef[]): void => {\n const ids = new Set<string>()\n for (const def of list) {\n if (ids.has(def.id)) problems.push(`duplicate category \"${def.id}\"`)\n ids.add(def.id)\n }\n }\n duplicates(defs)\n duplicates(options.extra ?? [])\n\n const entries = mergeById(defs, options.extra)\n applyPatches(entries, options.patches)\n removeIds(entries, options.remove)\n\n const chain = (id: string): string[] => {\n const path: string[] = []\n let current: string | undefined = id\n while (current !== undefined) {\n const def = entries.get(current)\n if (!def) throw new Error(path.length === 0 ? `unknown category \"${current}\"` : `category \"${path[0]}\" has unknown parent \"${current}\"`)\n if (path.includes(current)) throw new Error(`category cycle: ${[...path, current].reverse().join(' -> ')}`)\n path.push(current)\n current = def.extends\n }\n return path.reverse()\n }\n\n const registry: CategoryRegistry = {\n get: (id) => entries.get(id),\n has: (id) => entries.has(id),\n ids: () => [...entries.keys()],\n chain,\n defaults: (id) => chain(id).reduce<Record<string, unknown>>((acc, ancestor) => deepMerge(acc, entries.get(ancestor)?.defaults), {}),\n requires: (id) => {\n const out: string[] = []\n for (const ancestor of chain(id)) for (const contract of entries.get(ancestor)?.requires ?? []) if (!out.includes(contract)) out.push(contract)\n return out\n },\n isA: (id, ancestorId) => {\n try {\n return chain(id).includes(ancestorId)\n } catch {\n return false\n }\n },\n check: () => {\n const found = [...problems]\n for (const [id, def] of entries) {\n if (def.extends !== undefined && !entries.has(def.extends)) found.push(`category \"${id}\" extends unknown \"${def.extends}\"`)\n else {\n try {\n chain(id)\n } catch (error) {\n const message = (error as Error).message\n // The same cycle is found from each of its members; the dedupe keeps one line per distinct message.\n if (message.startsWith('category cycle') && !found.includes(message)) found.push(message)\n }\n }\n }\n return found\n },\n }\n return registry\n}\n\n/** The entry with the defaults of its category underneath: the entry wins, objects merge, arrays replace. */\nexport const resolveEntry = <TEntry extends object>(registry: CategoryRegistry, entry: TEntry, kindField = 'kind'): TEntry =>\n deepMerge(registry.defaults(String((entry as Record<string, unknown>)[kindField])) as TEntry, entry)\n\n/** Checks that each entry has a known, concrete kind and satisfies every contract its chain requires. */\nexport const checkCategorized = <TEntry extends { readonly id: string }>(\n entries: readonly TEntry[],\n registry: CategoryRegistry,\n checkers: readonly ContractChecker<TEntry>[],\n options: CategorizedOptions = {},\n): CategorizedReport => {\n const kindField = options.kindField ?? 'kind'\n const byId = new Map(checkers.map((checker) => [checker.id, checker]))\n const problems: string[] = [...registry.check()]\n const allowed: string[] = []\n\n for (const entry of entries) {\n const kind = String((entry as Record<string, unknown>)[kindField])\n if (!registry.has(kind)) {\n problems.push(`\"${entry.id}\" has unknown kind \"${kind}\"`)\n continue\n }\n if (registry.get(kind)?.abstract) {\n problems.push(`\"${entry.id}\" has abstract kind \"${kind}\"`)\n continue\n }\n let path: string[]\n try {\n path = registry.chain(kind)\n } catch (error) {\n problems.push(`\"${entry.id}\" (${kind}): ${(error as Error).message}`)\n continue\n }\n const resolved = resolveEntry(registry, entry, kindField)\n for (const contract of registry.requires(kind)) {\n const owner = path.find((ancestor) => registry.get(ancestor)?.requires?.includes(contract)) ?? kind\n const checker = byId.get(contract)\n if (!checker) {\n problems.push(`kind \"${kind}\" requires contract \"${contract}\" but no checker is registered`)\n continue\n }\n const prefix = `\"${entry.id}\" (${kind}): `\n for (const problem of checker.check(resolved, { kind, chain: path })) {\n const text = `${prefix}${problem}, required by ${owner}`\n if ((options.allow?.[entry.id] ?? []).some((start) => problem.startsWith(start))) allowed.push(text)\n else problems.push(text)\n }\n }\n }\n return { ok: problems.length === 0, problems: [...new Set(problems)], allowed }\n}\n","/**\n * Where a drawn object comes from. Every builder of the kit that draws a piece (a car of the fleet, a house or a wall, a piece of furniture, a person, an\n * animal, a plant, an item, a weapon) marks the root it returns, and every renderer that draws for the room (effects, debris, liquids, the room itself)\n * marks its own roots, so a host can tell a piece of the kit from something drawn on the spot. Duck-typed on three.js objects (`userData`, `parent`,\n * `children`), so the contracts stay free of three.js.\n */\nexport const KIT_ORIGIN_KEY = 'kitOrigin'\n\n/** What drew an object: the package and the kind of piece (`vehicle`, `structure`, `furniture`, `person`... or `effect`, `room` for what is not a piece). */\nexport interface KitOrigin {\n readonly owner: string\n readonly kind: string\n /** The catalogue id of the piece, when it has one. */\n readonly id?: string\n /** Drawn as a simple stand-in shape because the kit has no model of the piece yet (an item with no team model): a model still to make. */\n readonly standIn?: boolean\n}\n\ninterface Node {\n userData?: Record<string, unknown>\n parent?: Node | null\n children?: readonly Node[]\n name?: string\n visible?: boolean\n isMesh?: boolean\n isPoints?: boolean\n isLine?: boolean\n isSprite?: boolean\n}\n\n/** Marks `object` as drawn by `origin.owner` and returns it. */\nexport function markKitOrigin<T>(object: T, origin: KitOrigin): T {\n const node = object as unknown as Node\n node.userData = node.userData ?? {}\n node.userData[KIT_ORIGIN_KEY] = { ...origin }\n return object\n}\n\n/** The origin of the object or of its nearest marked ancestor; null when nothing up the tree is marked. */\nexport function kitOriginOf(object: unknown): KitOrigin | null {\n for (let node = object as Node | null | undefined; node; node = node.parent) {\n const origin = node.userData?.[KIT_ORIGIN_KEY] as KitOrigin | undefined\n if (origin && typeof origin.owner === 'string') return origin\n }\n return null\n}\n\n/** A drawn thing that no builder of the kit marked: what was drawn on the spot. */\nexport interface UnmarkedDrawing {\n /** The names of the object and its named ancestors, outermost first. */\n readonly path: string\n readonly object: unknown\n}\n\n/** Every visible mesh, line, point cloud or sprite under `root` with no marked ancestor (`root` itself not counted as one). */\nexport function unmarkedDrawings(root: unknown): UnmarkedDrawing[] {\n const out: UnmarkedDrawing[] = []\n const pathOf = (node: Node): string => {\n const names: string[] = []\n for (let n: Node | null | undefined = node; n && n !== root; n = n.parent) if (n.name) names.push(n.name)\n return names.reverse().join(' > ') || '(no name)'\n }\n const walk = (node: Node, marked: boolean): void => {\n if (node.visible === false) return\n const here = marked || (node !== root && Boolean((node.userData?.[KIT_ORIGIN_KEY] as KitOrigin | undefined)?.owner))\n if (!here && (node.isMesh || node.isPoints || node.isLine || node.isSprite)) out.push({ path: pathOf(node), object: node })\n for (const child of node.children ?? []) walk(child, here)\n }\n walk(root as Node, false)\n return out\n}\n","/** A floor rectangle in an object's own space, metres (x across, z along). */\nexport interface Footprint {\n readonly minX: number\n readonly maxX: number\n readonly minZ: number\n readonly maxZ: number\n}\n\n/** What `footprintBlocker` reads of an object: its world matrix (three.js `Object3D`, duck-typed so the contracts stay free of three.js). */\nexport interface FootprintObject {\n readonly matrixWorld: { readonly elements: ArrayLike<number> }\n readonly parent?: unknown\n updateWorldMatrix?(updateParents: boolean, updateChildren: boolean): void\n}\n\n/**\n * The `ModuleHost.addBlocker` function of a solid object standing on the floor: the walking player stays out of `footprint`, a rectangle in the object's own space,\n * so the object blocks turned with it and follows it when it moves. An object out of the scene (no parent) blocks nothing.\n */\nexport function footprintBlocker(object: FootprintObject | (() => FootprintObject | null | undefined), footprint: Footprint | (() => Footprint | null | undefined)): (x: number, z: number, radius: number) => boolean {\n return (x, z, radius) => {\n const o = typeof object === 'function' ? object() : object\n const fp = typeof footprint === 'function' ? footprint() : footprint\n if (!o || !fp || !o.parent) return false\n o.updateWorldMatrix?.(true, false)\n const e = o.matrixWorld.elements\n // the world point in the object's own floor axes: its x and z columns are at right angles, so a projection on each (over its squared length, for a scale) inverts the matrix\n const dx = x - (e[12] as number)\n const dz = z - (e[14] as number)\n const ax = (e[0] as number) ** 2 + (e[1] as number) ** 2 + (e[2] as number) ** 2\n const az = (e[8] as number) ** 2 + (e[9] as number) ** 2 + (e[10] as number) ** 2\n const lx = (dx * (e[0] as number) + dz * (e[2] as number)) / (ax || 1)\n const lz = (dx * (e[8] as number) + dz * (e[10] as number)) / (az || 1)\n return lx > fp.minX - radius && lx < fp.maxX + radius && lz > fp.minZ - radius && lz < fp.maxZ + radius\n }\n}\n","/**\n * The sounds of a body (footsteps, jumps, landings, blows, hurt), as ids every package agrees on: the rule that knows what the body did\n * (`game-player`, a host's own player) names the sound, and the bank that knows how it sounds (`game-audio`'s `BODY_FOLEY_BANK`) answers it.\n * A game swaps the sound of an id (a sample instead of the synthesis) without touching the rule.\n */\n\n/** What the feet touch, by how it sounds (not by what it is made of: a wooden floor and a plywood board both sound `wood`). */\nexport const SURFACE_IDS = ['concrete', 'asphalt', 'wood', 'tile', 'dirt', 'grass', 'gravel', 'sand', 'metal', 'water', 'glass', 'carpet'] as const\nexport type SurfaceId = (typeof SURFACE_IDS)[number]\n\n/** The surface a body stands on when nobody says (the test room's floor is a concrete slab). */\nexport const DEFAULT_SURFACE: SurfaceId = 'concrete'\n\n/** How the feet move: a run hits harder and shorter; a crouched step is the walk, quieter. */\nexport const GAIT_IDS = ['walk', 'run'] as const\nexport type GaitId = (typeof GAIT_IDS)[number]\n\n/** How hard a landing is: `light` for a jump on the spot or a step down (up to about 5 m/s), `heavy` for a drop of more than a metre and a half. */\nexport const LANDING_IDS = ['light', 'heavy'] as const\nexport type LandingId = (typeof LANDING_IDS)[number]\n\n/** The fall speed (m/s) from which a landing is `heavy`. */\nexport const HEAVY_LANDING_SPEED = 5.5\n\nexport type FootstepSoundId = `footstep_${SurfaceId}_${GaitId}`\nexport type LandSoundId = `land_${SurfaceId}_${LandingId}`\n\n/**\n * How a blow is struck, by the body part or the tool that does it. `blade` stabs or slashes, `blunt` is a club, a bat, a pipe or a hammer, `chop` an\n * axe or a machete driven down.\n */\nexport const BLOW_IDS = ['punch', 'kick', 'shove', 'blade', 'blunt', 'chop'] as const\nexport type BlowId = (typeof BLOW_IDS)[number]\n\n/** What the blow meets: a body (flesh and bone, the head apart), something else (the material answers with its own impact), or nothing. */\nexport type BlowTarget = 'body' | 'head' | 'object' | 'air'\n\n/** The sounds of the body that are not a footstep or a landing. */\nexport const BODY_SOUND_IDS = [\n 'jump',\n 'cloth_rustle',\n 'body_fall',\n 'fall_damage',\n 'player_hurt',\n 'player_breath_run',\n 'player_death',\n 'bite',\n 'grab',\n 'punch_swing',\n 'punch_hit_body',\n 'punch_hit_face',\n 'punch_hit_object',\n 'kick_swing',\n 'kick_hit',\n 'kick_hit_object',\n 'shove_two_hands',\n 'knife_swing',\n 'knife_stab',\n 'knife_slash',\n 'knife_hit_object',\n 'blunt_swing',\n 'blunt_hit',\n 'blunt_hit_object',\n 'axe_swing',\n 'axe_chop',\n 'axe_hit_object',\n] as const\nexport type BodySoundId = (typeof BODY_SOUND_IDS)[number]\n\n/** Every sound of a body: what a host plays for the player, a survivor or a zombie. */\nexport type BodyFoleyId = FootstepSoundId | LandSoundId | BodySoundId\n\nexport const footstepSoundId = (surface: SurfaceId, gait: GaitId): FootstepSoundId => `footstep_${surface}_${gait}`\nexport const landSoundId = (surface: SurfaceId, fallSpeed: number): LandSoundId => `land_${surface}_${fallSpeed >= HEAVY_LANDING_SPEED ? 'heavy' : 'light'}`\n\n/** Every footstep and landing id, in the order of the surfaces. */\nexport const FOOTSTEP_SOUND_IDS: readonly FootstepSoundId[] = SURFACE_IDS.flatMap((surface) => GAIT_IDS.map((gait) => footstepSoundId(surface, gait)))\nexport const LAND_SOUND_IDS: readonly LandSoundId[] = SURFACE_IDS.flatMap((surface) => LANDING_IDS.map((landing): LandSoundId => `land_${surface}_${landing}`))\nexport const BODY_FOLEY_IDS: readonly BodyFoleyId[] = [...FOOTSTEP_SOUND_IDS, ...LAND_SOUND_IDS, ...BODY_SOUND_IDS]\n\nconst SWING: Readonly<Record<BlowId, BodySoundId | null>> = {\n punch: 'punch_swing',\n kick: 'kick_swing',\n // two hands pushed out do not whistle: the shove is one sound, at the contact\n shove: null,\n blade: 'knife_swing',\n blunt: 'blunt_swing',\n chop: 'axe_swing',\n}\n\n/**\n * Seconds from the start of a swing's sound to its loudest point (the limb or the tool at full speed, just before the contact). A host that knows when the\n * blow lands starts the swing this long before; one that does not plays the swing now and the hit this long after.\n */\nexport const SWING_LEAD_SECONDS: Readonly<Record<BlowId, number>> = { punch: 0.07, kick: 0.09, shove: 0, blade: 0.05, blunt: 0.1, chop: 0.11 }\n\n/** The swing through the air, played as the blow starts (`null`: the blow has none). */\nexport const swingSoundId = (blow: BlowId): BodySoundId | null => SWING[blow]\n\nconst HIT: Readonly<Record<BlowId, Readonly<Record<Exclude<BlowTarget, 'air'>, BodySoundId>>>> = {\n punch: { body: 'punch_hit_body', head: 'punch_hit_face', object: 'punch_hit_object' },\n kick: { body: 'kick_hit', head: 'kick_hit', object: 'kick_hit_object' },\n shove: { body: 'shove_two_hands', head: 'shove_two_hands', object: 'shove_two_hands' },\n blade: { body: 'knife_stab', head: 'knife_stab', object: 'knife_hit_object' },\n blunt: { body: 'blunt_hit', head: 'blunt_hit', object: 'blunt_hit_object' },\n chop: { body: 'axe_chop', head: 'axe_chop', object: 'axe_hit_object' },\n}\n\n/**\n * The body's side of a blow that lands: the knuckles, the shoe, the blade or the club meeting what it hit. On an object the material's own impact\n * plays too (the wood, the metal); this is the part that says it was a punch and not a shot.\n */\nexport const hitSoundId = (blow: BlowId, target: Exclude<BlowTarget, 'air'>): BodySoundId => HIT[blow][target]\n\n/**\n * The surface a material sounds like underfoot, by the material ids of `game-destruction` and `game-terrain` (unknown ids: `fallback`). Data, so\n * a game with its own materials passes its own table to `surfaceOfMaterial`.\n */\nexport const MATERIAL_SURFACES: Readonly<Record<string, SurfaceId>> = {\n concrete: 'concrete',\n mortar: 'concrete',\n stone: 'concrete',\n brick: 'concrete',\n plaster: 'concrete',\n asphalt: 'asphalt',\n wood_soft: 'wood',\n wood_hard: 'wood',\n green_wood: 'wood',\n plywood: 'wood',\n mdf: 'wood',\n ceramic: 'tile',\n plastic: 'tile',\n soil: 'dirt',\n wax: 'dirt',\n flesh_fruit: 'dirt',\n turf: 'grass',\n foliage: 'grass',\n cactus: 'grass',\n gravel: 'gravel',\n sand: 'sand',\n steel: 'metal',\n cast_iron: 'metal',\n aluminium: 'metal',\n copper: 'metal',\n water: 'water',\n glass: 'glass',\n tempered_glass: 'glass',\n fabric: 'carpet',\n foam: 'carpet',\n leather: 'carpet',\n rubber: 'carpet',\n paper: 'carpet',\n}\n\nexport const isSurfaceId = (value: unknown): value is SurfaceId => typeof value === 'string' && (SURFACE_IDS as readonly string[]).includes(value)\n\n/** The surface of a material id; a surface id passes through. */\nexport function surfaceOfMaterial(material: string | undefined, table: Readonly<Record<string, SurfaceId>> = MATERIAL_SURFACES, fallback: SurfaceId = DEFAULT_SURFACE): SurfaceId {\n if (material === undefined) return fallback\n if (isSurfaceId(material)) return material\n return table[material] ?? fallback\n}\n","/**\n * @adatechnology/game-contracts: the contracts every game module (vehicles, furniture, plants, houses, weapons) depends on, and nothing else.\n * Types plus tiny pure checkers, no runtime dependencies. The default destruction world, the renderers and the test room are optional.\n */\nexport type {\n ChainDelay,\n Crack,\n DamageStage,\n DamageType,\n DebrisKind,\n Dent,\n DestructibleSpec,\n BlastOptions,\n BlastReport,\n ConfinementOptions,\n ExplosionChain,\n FragmentHitOptions,\n ShelterOptions,\n DestructionEvent,\n DestructionEventType,\n FluidKind,\n Fragment,\n FragmentMark,\n FractureStyle,\n BallisticExtras,\n Hit,\n HitIgnition,\n HitResult,\n Hole,\n JointKind,\n JointSpec,\n JointState,\n MaterialId,\n ObjectState,\n PartShape,\n PartSpec,\n PartBreach,\n PartState,\n PartStatus,\n PartUnits,\n Quat,\n RayHit,\n Reinforcement,\n TensionRebar,\n TunedMassDamper,\n WindExposure,\n WindState,\n PileSpec,\n SoilLayer,\n SoilProfile,\n SoilState,\n StructureRole,\n SurfaceLayer,\n ToolKind,\n Vec3,\n} from './destruction'\nexport type { BusyOptions, BusySink, BusyTask } from './busy'\nexport { blastShove, DEFAULT_BLAST_SHOVE } from './blast-shove'\nexport type { BlastShove, BlastShoveOptions, BlastSource } from './blast-shove'\nexport { IMPACT_CONTRACT, checkDestructible, checkHit } from './impact'\nexport type { CheckOptions, DestructibleImpl, ImpactContract, SurfaceRay, SurfaceRayHit } from './impact'\nexport type { CostOptions } from './impact-cost'\nexport { MISSING_BITS } from './body'\nexport type { BodyRegion, BodySlot, SegmentKind } from './body'\nexport { checkSpec } from './spec'\nexport type { CheckSpecOptions, SpecReport } from './spec'\nexport { shotToHit, shotExtras, useToHit } from './weapons'\nexport { DEFAULT_IGNITION_RULES, fillFactor, ignitionChance, resolveIgnitionRules, sparkTargetFactor } from './ignition'\nexport type { IgnitionRules } from './ignition'\nexport type { Shot, Use } from './weapons'\nexport type { ContainerSpec, ItemUseDef, LiquidId } from './fluids'\nexport { sealBlock, sealOpener } from './seal'\nexport type { SealBlock, SealSpec, SealState } from './seal'\nexport type { HandPose, HandTargetSource, HandTargets } from './hands'\nexport { TWIST_LIFT, twistStroke } from './twist'\nexport type { TwistOptions, TwistStroke } from './twist'\nexport type { EffectHandle, EffectRequest, EffectSink, Enclosure, EnclosureBox } from './effects'\nexport type { WarmUpProbes } from './warm-up'\nexport { checkAnimation } from './animation'\nexport type { AnimationClipSpec, AnimationEventSpec, AnimationKey, AnimationPose, ItemAnimationContract } from './animation'\nexport { STAGE_EVENT, TOOL_CLIP, checkToolClips, createToolClipPlayer, sampleClip, scaleClip, toolAnimationStep } from './tool-animation'\nexport type { ToolAnimationStep, ToolAnimator, ToolClipId, ToolClipPlayer, ToolClipPlayerOptions, ToolClipSet, ToolRig } from './tool-animation'\nexport { DEFERRED_PRIORITY, DEFERRED_QUEUE_ID, FRAME_PHASES, checkDeferredQueue } from './frame'\nexport type { DeferredQueue, DeferredQueueOptions, DeferredQueueReport, FramePhase } from './frame'\nexport type { FrameOptions, AreaPlacement, GroundLayout, GroundZone, HostEvent, HostGraphics, HostTool, HostToolAttachment, ModuleArea, ModuleHost, ModuleItemFluid, ModuleItemSeal, ModuleScenario, PlayerState, ItemSeat, TestableItem, TestableModule, Thumbnail, ToolCommand, ToolCommandResult, HostReach } from './module'\nexport { applyPatches, deepMerge, mergeById, removeIds } from './registry'\nexport type { DeepPartial } from './registry'\nimport { resetWarnOnce as resetWarnOnceForTests } from './warn'\n\nexport { warnOnce } from './warn'\n/** @deprecated Import it from `@adatechnology/game-contracts/testing`. */\nexport const resetWarnOnce = resetWarnOnceForTests\nexport { checkBench } from './bench'\nexport type {\n ActionControl,\n BenchChange,\n BenchDeclaration,\n BenchReport,\n BenchSheet,\n BooleanControl,\n ChoiceControl,\n ChoiceOption,\n ControlPatch,\n ControlValue,\n ModuleBench,\n ModuleControl,\n ModuleLayer,\n ModuleMetric,\n NumberControl,\n} from './bench'\nexport type { AudioListener, AudioSink, LoopHandle, SoundHandle, SoundRequest } from './audio'\nexport { DEFAULT_ENVIRONMENT, createEnvironmentBus } from './environment'\nexport type { EnvironmentBus, EnvironmentKey, EnvironmentState } from './environment'\nexport { checkCategorized, defineCategories, resolveEntry } from './categories'\nexport type { CategorizedOptions, CategorizedReport, CategoryDef, CategoryOptions, CategoryRegistry, ContractChecker, ContractContext } from './categories'\nexport { KIT_ORIGIN_KEY, kitOriginOf, markKitOrigin, unmarkedDrawings } from './origin'\nexport type { KitOrigin, UnmarkedDrawing } from './origin'\nexport { footprintBlocker } from './footprint'\nexport type { Footprint, FootprintObject } from './footprint'\nexport {\n BLOW_IDS,\n BODY_FOLEY_IDS,\n BODY_SOUND_IDS,\n DEFAULT_SURFACE,\n FOOTSTEP_SOUND_IDS,\n GAIT_IDS,\n HEAVY_LANDING_SPEED,\n LANDING_IDS,\n LAND_SOUND_IDS,\n MATERIAL_SURFACES,\n SURFACE_IDS,\n SWING_LEAD_SECONDS,\n footstepSoundId,\n hitSoundId,\n isSurfaceId,\n landSoundId,\n surfaceOfMaterial,\n swingSoundId,\n} from './foley'\nexport type { BlowId, BlowTarget, BodyFoleyId, BodySoundId, FootstepSoundId, GaitId, LandSoundId, LandingId, SurfaceId } from './foley'\n","{\n \"guide\": {\n \"heightMetres\": 1.8,\n \"shoulderWidthMetres\": 0.61,\n \"shoulderWidthWomanMetres\": 0.57,\n \"shoulderHeightMetres\": 1.475,\n \"headToHeight\": 0.1667\n },\n \"teamGame\": {\n \"source\": \"G 65336 J (shoulders +-0.24 m)\",\n \"joints\": {\n \"quadril\": [\n 0,\n 0.95,\n 0\n ],\n \"pescoco\": [\n 0,\n 1.52,\n 0\n ],\n \"ombroE\": [\n -0.24,\n 1.44,\n 0\n ],\n \"ombroD\": [\n 0.24,\n 1.44,\n 0\n ],\n \"cotoveloE\": [\n -0.27,\n 1.13,\n 0\n ],\n \"cotoveloD\": [\n 0.27,\n 1.13,\n 0\n ],\n \"coxaE\": [\n -0.1,\n 0.92,\n 0\n ],\n \"coxaD\": [\n 0.1,\n 0.92,\n 0\n ],\n \"joelhoE\": [\n -0.1,\n 0.5,\n 0\n ],\n \"joelhoD\": [\n 0.1,\n 0.5,\n 0\n ]\n },\n \"heightMetres\": 1.8\n },\n \"lodDistancesMetres\": [\n 18,\n 55\n ],\n \"triangleBudget\": {\n \"lod0\": 2500,\n \"lod1\": 800,\n \"lod2\": 250\n }\n}\n","import type { SegmentKind, Vec3 } from '@adatechnology/game-contracts'\n\nimport body from '../data/body.json'\n\n/** The 17 joints of a body: one per segment, the point the segment hangs from. */\nexport type JointId =\n | 'hip'\n | 'spine'\n | 'neck'\n | 'head'\n | 'jaw'\n | 'shoulderL'\n | 'shoulderR'\n | 'elbowL'\n | 'elbowR'\n | 'wristL'\n | 'wristR'\n | 'hipL'\n | 'hipR'\n | 'kneeL'\n | 'kneeR'\n | 'ankleL'\n | 'ankleR'\n\nexport const JOINT_IDS: readonly JointId[] = ['hip', 'spine', 'neck', 'head', 'jaw', 'shoulderL', 'shoulderR', 'elbowL', 'elbowR', 'wristL', 'wristR', 'hipL', 'hipR', 'kneeL', 'kneeR', 'ankleL', 'ankleR']\n\n/**\n * Proportions of a body: joint positions in metres for a body of `heightMetres`, standing, feet on y = 0, facing +z, left side on +x.\n * `segmentRadius` is the radius of the capsule a segment is hit through.\n */\nexport type BodySpec = {\n readonly id: string\n readonly heightMetres: number\n readonly joints: Readonly<Record<JointId, Vec3>>\n readonly segmentRadius: Readonly<Record<SegmentKind, number>>\n}\n\nconst SEGMENT_RADIUS: Readonly<Record<SegmentKind, number>> = {\n head: 0.1,\n jaw: 0.05,\n neck: 0.06,\n torso: 0.17,\n pelvis: 0.16,\n upperArm: 0.085,\n forearm: 0.065,\n hand: 0.05,\n thigh: 0.1,\n shin: 0.08,\n foot: 0.05,\n}\n\nconst { guide } = body\nconst shoulderX = guide.shoulderWidthMetres / 2\nconst shoulderY = guide.shoulderHeightMetres\n\n/** The Elenco style guide body (1.80 m, shoulders 0.61 m at 1.475 m, head about 1/6 of the height): the default. */\nexport const GUIDE: BodySpec = {\n id: 'guide',\n heightMetres: guide.heightMetres,\n joints: {\n hip: [0, 0.92, 0],\n spine: [0, 1.05, 0],\n neck: [0, 1.56, 0],\n head: [0, 1.64, 0],\n jaw: [0, 1.66, 0.06],\n shoulderL: [shoulderX, shoulderY, 0],\n shoulderR: [-shoulderX, shoulderY, 0],\n elbowL: [shoulderX, shoulderY - 0.31, 0],\n elbowR: [-shoulderX, shoulderY - 0.31, 0],\n wristL: [shoulderX, shoulderY - 0.61, 0],\n wristR: [-shoulderX, shoulderY - 0.61, 0],\n hipL: [0.105, 0.9, 0],\n hipR: [-0.105, 0.9, 0],\n kneeL: [0.105, 0.48, 0],\n kneeR: [-0.105, 0.48, 0],\n ankleL: [0.105, 0.1, 0],\n ankleR: [-0.105, 0.1, 0],\n },\n segmentRadius: SEGMENT_RADIUS,\n}\n\nconst J = body.teamGame.joints\nconst below = (point: readonly number[], drop: number): Vec3 => [point[0], point[1] - drop, point[2]]\n\n/**\n * The game's crowd body (G 65336 `J`, shoulders ±0.24 m), kept for the goldens of the instanced path. `J` has no spine, head, jaw, wrist or\n * ankle: spine and head sit on the hip and neck bones (bones 0 and 1 of the game), the wrist and ankle one forearm and one shin below.\n */\nexport const TEAM_GAME: BodySpec = {\n id: 'team-game',\n heightMetres: body.teamGame.heightMetres,\n joints: {\n hip: J.quadril as Vec3,\n spine: J.quadril as Vec3,\n neck: J.pescoco as Vec3,\n head: J.pescoco as Vec3,\n jaw: [0, J.pescoco[1] + 0.08, 0.06],\n shoulderL: J.ombroE as Vec3,\n shoulderR: J.ombroD as Vec3,\n elbowL: J.cotoveloE as Vec3,\n elbowR: J.cotoveloD as Vec3,\n wristL: below(J.cotoveloE, 0.3),\n wristR: below(J.cotoveloD, 0.3),\n hipL: J.coxaE as Vec3,\n hipR: J.coxaD as Vec3,\n kneeL: J.joelhoE as Vec3,\n kneeR: J.joelhoD as Vec3,\n ankleL: below(J.joelhoE, 0.42),\n ankleR: below(J.joelhoD, 0.42),\n },\n segmentRadius: SEGMENT_RADIUS,\n}\n\n/** A body spec from `base` (default `GUIDE`) with the given fields replaced; `joints` and `segmentRadius` merge key by key. */\nexport const createBodySpec = (patch: Partial<BodySpec> = {}, base: BodySpec = GUIDE): BodySpec => ({\n id: patch.id ?? base.id,\n heightMetres: patch.heightMetres ?? base.heightMetres,\n joints: { ...base.joints, ...patch.joints },\n segmentRadius: { ...base.segmentRadius, ...patch.segmentRadius },\n})\n","import type { CorpseOptions } from './types'\n\nexport type CorpseEntry<TData> = {\n /** Seconds since it fell. */\n age: number\n readonly data: TData\n}\n\nexport type CorpseLedger<TData> = {\n readonly entries: readonly CorpseEntry<TData>[]\n readonly count: number\n /** Adds a corpse; when the ledger is full the oldest one is replaced. Returns the replaced data, if any. */\n add(data: TData): TData | undefined\n /** Ages every corpse by `dt` seconds and removes the ones past their life (swap with the last, like the game). Returns the removed data. */\n update(dt: number): TData[]\n /** Takes one corpse away (eaten, searched and dragged off): the last one moves into its place, like the game's `CP`. Returns its data. */\n remove(index: number): TData | undefined\n clear(): void\n}\n\n/** The bodies left on the ground: capped (400) and gone after a lifetime (900 s); generic over what each one carries. */\nexport const createCorpseLedger = <TData>(options: CorpseOptions): CorpseLedger<TData> => {\n const entries: CorpseEntry<TData>[] = []\n return {\n entries,\n get count() {\n return entries.length\n },\n add(data) {\n if (entries.length < options.max) {\n entries.push({ age: 0, data })\n return undefined\n }\n let oldest = 0\n for (let index = 1; index < entries.length; index++) if ((entries[index] as CorpseEntry<TData>).age > (entries[oldest] as CorpseEntry<TData>).age) oldest = index\n const replaced = (entries[oldest] as CorpseEntry<TData>).data\n entries[oldest] = { age: 0, data }\n return replaced\n },\n update(dt) {\n const removed: TData[] = []\n for (let index = 0; index < entries.length; index++) {\n const entry = entries[index] as CorpseEntry<TData>\n entry.age += dt\n if (entry.age <= options.seconds) continue\n removed.push(entry.data)\n entries[index] = entries[entries.length - 1] as CorpseEntry<TData>\n entries.pop()\n index--\n }\n return removed\n },\n remove(index) {\n const entry = entries[index]\n if (!entry) return undefined\n entries[index] = entries[entries.length - 1] as CorpseEntry<TData>\n entries.pop()\n return entry.data\n },\n clear() {\n entries.length = 0\n },\n }\n}\n","import { MISSING_BITS } from '@adatechnology/game-contracts'\nimport type { SegmentKind } from '@adatechnology/game-contracts'\n\nimport type { Detail, SegmentState } from '../types'\nimport { LIMB_REGIONS, REGION_HIT_POINTS, REGION_SEGMENTS } from './regions'\nimport type { LimbRegion } from './regions'\nimport { SEGMENT_TREE } from './segment-tree'\nimport type { SegmentId, SegmentSide } from './segment-tree'\n\n/** The mutable state of one body. Both detail levels live here: the 17 segments and the hit points of the 5 crowd regions. */\nexport type BodyState = {\n readonly segments: SegmentState[]\n readonly regions: Record<LimbRegion, number>\n vitality: number\n readonly maxVitality: number\n alive: boolean\n /** The game's `MISSING_BITS` mask. */\n missing: number\n /** Sides that limp, in the order they started. */\n readonly limping: SegmentSide[]\n crawl: boolean\n stunSeconds: number\n /** Loose pieces: each severed segment with what fell with it. */\n readonly pieces: SegmentId[][]\n /** Protection layers used up (a helmet that took its one shot). */\n readonly consumed: Set<string>\n detail: Detail\n}\n\nexport type CreateBodyStateParams = { readonly vitality: number; readonly detail: Detail; readonly crawling?: boolean; readonly hitPointsOf: (kind: SegmentKind) => number }\n\nexport const createBodyState = ({ vitality, detail, crawling, hitPointsOf }: CreateBodyStateParams): BodyState => ({\n segments: SEGMENT_TREE.map((node) => {\n const max = hitPointsOf(node.kind)\n return { id: node.id, kind: node.kind, region: node.region, hp: max, max, status: 'ok', attached: true, ...(node.side ? { side: node.side } : {}) }\n }),\n regions: { ...REGION_HIT_POINTS },\n vitality,\n maxVitality: vitality,\n alive: true,\n missing: 0,\n limping: [],\n crawl: crawling ?? false,\n stunSeconds: 0,\n pieces: [],\n consumed: new Set(),\n detail,\n})\n\nexport const segmentOf = (body: BodyState, id: SegmentId): SegmentState => {\n const segment = body.segments.find((candidate) => candidate.id === id)\n if (!segment) throw new Error(`game-characters: unknown segment \"${id}\"`)\n return segment\n}\n\nconst LIMB_MASK: readonly { readonly ids: readonly SegmentId[]; readonly lost: number; readonly broken: number }[] = [\n { ids: ['upperArmL', 'forearmL'], lost: MISSING_BITS.armL, broken: MISSING_BITS.armLBroken },\n { ids: ['upperArmR', 'forearmR'], lost: MISSING_BITS.armR, broken: MISSING_BITS.armRBroken },\n { ids: ['thighL', 'shinL'], lost: MISSING_BITS.legL, broken: MISSING_BITS.legLBroken },\n { ids: ['thighR', 'shinR'], lost: MISSING_BITS.legR, broken: MISSING_BITS.legRBroken },\n]\n\n/** The crowd mask the segments amount to: a lost head, jaw, arm or leg (upper or lower part), and a broken one still attached. */\nexport const missingFromSegments = (body: BodyState): number => {\n let mask = 0\n if (!segmentOf(body, 'head').attached) mask |= MISSING_BITS.head\n else if (!segmentOf(body, 'jaw').attached) mask |= MISSING_BITS.jaw\n for (const limb of LIMB_MASK) {\n const parts = limb.ids.map((id) => segmentOf(body, id))\n if (parts.some((part) => !part.attached)) mask |= limb.lost\n else if (parts.some((part) => part.status === 'broken')) mask |= limb.broken\n }\n return mask\n}\n\n/** Region hit points from the segments: the share of the region's segment hit points left, scaled to the region's own hit points. */\nexport const regionsFromSegments = (body: BodyState): void => {\n for (const region of LIMB_REGIONS) {\n const parts = REGION_SEGMENTS[region].map((id) => segmentOf(body, id))\n const left = parts.reduce((sum, part) => sum + (part.attached ? Math.max(0, part.hp) : 0), 0)\n const total = parts.reduce((sum, part) => sum + part.max, 0)\n body.regions[region] = (REGION_HIT_POINTS[region] * left) / total\n }\n body.missing |= missingFromSegments(body)\n}\n\n/** Segment hit points from the regions (the reverse is lossy: every segment of a region keeps the region's share). */\nexport const segmentsFromRegions = (body: BodyState): void => {\n for (const region of LIMB_REGIONS) {\n const share = Math.max(0, body.regions[region]) / REGION_HIT_POINTS[region]\n for (const id of REGION_SEGMENTS[region]) {\n const part = segmentOf(body, id)\n if (part.attached) part.hp = Math.min(part.hp, part.max * share)\n }\n }\n}\n","{\n \"id\": \"team-game\",\n \"segments\": {\n \"head\": 40,\n \"arm\": 60,\n \"leg\": 80\n },\n \"calibers\": {\n \"7_62x51\": {\n \"gameKey\": \"308\",\n \"name\": \"7,62×51 (.308)\",\n \"points\": 75,\n \"penetration\": 4,\n \"effectiveRangeMetres\": 600,\n \"noiseMetres\": 130,\n \"skullRange\": 1,\n \"class\": \"rifle\"\n },\n \"9x19\": {\n \"gameKey\": \"9x19\",\n \"name\": \"9×19 mm\",\n \"points\": 35,\n \"penetration\": 1.2,\n \"effectiveRangeMetres\": 35,\n \"noiseMetres\": 60,\n \"skullRange\": 1,\n \"class\": \"pistol\"\n },\n \"12ga_00buck\": {\n \"gameKey\": \"12ga\",\n \"name\": \"12 ga 00\",\n \"points\": 12,\n \"pellets\": 9,\n \"penetration\": 0.5,\n \"effectiveRangeMetres\": 15,\n \"noiseMetres\": 90,\n \"skullRange\": 8,\n \"class\": \"pistol\"\n },\n \"7_62x39\": {\n \"gameKey\": \"762x39\",\n \"name\": \"7,62×39 mm\",\n \"points\": 55,\n \"penetration\": 3,\n \"effectiveRangeMetres\": 250,\n \"noiseMetres\": 110,\n \"skullRange\": 1,\n \"class\": \"rifle\"\n }\n },\n \"regionCost\": {\n \"head\": 0.7,\n \"torso\": 1,\n \"bruteTorso\": 2,\n \"limb\": 0.5\n },\n \"damageKeptPerBody\": 0.75,\n \"falloff\": {\n \"fullAtRange\": 1,\n \"atTwiceRange\": 0.5\n },\n \"helmetAbsorbs\": \"pistol\",\n \"regions\": [\n \"head\",\n \"torso\",\n \"armL\",\n \"armR\",\n \"legL\",\n \"legR\"\n ]\n}\n","import type { BodyRegion } from '@adatechnology/game-contracts'\n\nimport teamGame from '../presets/team-game.json'\nimport type { SegmentId } from './segment-tree'\n\n/** The regions that keep their own hit points in the crowd model (the torso has none: torso hits go to vitality). */\nexport type LimbRegion = Exclude<BodyRegion, 'torso'>\n\nexport const LIMB_REGIONS: readonly LimbRegion[] = ['head', 'armL', 'armR', 'legL', 'legR']\n\n/** The game's region numbers (`SEG`): hit points of the head, of an arm and of a leg. */\nexport const REGION_HIT_POINTS: Readonly<Record<LimbRegion, number>> = {\n head: teamGame.segments.head,\n armL: teamGame.segments.arm,\n armR: teamGame.segments.arm,\n legL: teamGame.segments.leg,\n legR: teamGame.segments.leg,\n}\n\n/** The segments that make up each region, and the one that names it in events (the first). */\nexport const REGION_SEGMENTS: Readonly<Record<BodyRegion, readonly SegmentId[]>> = {\n head: ['head', 'jaw', 'neck'],\n torso: ['torso', 'pelvis'],\n armL: ['upperArmL', 'forearmL', 'handL'],\n armR: ['upperArmR', 'forearmR', 'handR'],\n legL: ['thighL', 'shinL', 'footL'],\n legR: ['thighR', 'shinR', 'footR'],\n}\n\nexport const BODY_REGIONS: readonly BodyRegion[] = ['head', 'torso', 'armL', 'armR', 'legL', 'legR']\n\nexport const isBodyRegion = (value: string): value is BodyRegion => (BODY_REGIONS as readonly string[]).includes(value)\n","{\n \"segments\": [\n {\n \"id\": \"head\",\n \"region\": \"head\",\n \"hitPoints\": 40,\n \"multiplier\": 3,\n \"bleed\": 40\n },\n {\n \"id\": \"jaw\",\n \"region\": \"head\",\n \"hitPoints\": 12,\n \"multiplier\": 2,\n \"bleed\": 8\n },\n {\n \"id\": \"neck\",\n \"region\": \"head\",\n \"hitPoints\": 25,\n \"multiplier\": 2,\n \"bleed\": 46\n },\n {\n \"id\": \"torso\",\n \"region\": \"torso\",\n \"hitPoints\": 150,\n \"multiplier\": 1,\n \"bleed\": 10\n },\n {\n \"id\": \"pelvis\",\n \"region\": \"torso\",\n \"hitPoints\": 90,\n \"multiplier\": 1,\n \"bleed\": 12\n },\n {\n \"id\": \"upperArm\",\n \"region\": \"arm\",\n \"hitPoints\": 45,\n \"multiplier\": 0.6,\n \"bleed\": 15\n },\n {\n \"id\": \"forearm\",\n \"region\": \"arm\",\n \"hitPoints\": 30,\n \"multiplier\": 0.6,\n \"bleed\": 8\n },\n {\n \"id\": \"hand\",\n \"region\": \"arm\",\n \"hitPoints\": 12,\n \"multiplier\": 0.6,\n \"bleed\": 3\n },\n {\n \"id\": \"thigh\",\n \"region\": \"leg\",\n \"hitPoints\": 55,\n \"multiplier\": 0.7,\n \"bleed\": 19\n },\n {\n \"id\": \"shin\",\n \"region\": \"leg\",\n \"hitPoints\": 40,\n \"multiplier\": 0.7,\n \"bleed\": 9\n },\n {\n \"id\": \"foot\",\n \"region\": \"leg\",\n \"hitPoints\": 15,\n \"multiplier\": 0.7,\n \"bleed\": 3\n }\n ],\n \"regions\": {\n \"head\": {\n \"hitPoints\": 40,\n \"segmentSum\": 77,\n \"scale\": 0.5195\n },\n \"arm\": {\n \"hitPoints\": 60,\n \"segmentSum\": 87,\n \"scale\": 0.6897\n },\n \"leg\": {\n \"hitPoints\": 80,\n \"segmentSum\": 110,\n \"scale\": 0.7273\n }\n },\n \"arsenalWeapons\": {\n \"pistola\": {\n \"n\": \"Pistola 9×19\",\n \"dmg\": 35,\n \"pel\": 1\n },\n \"fuzil\": {\n \"n\": \"Fuzil 7,62×39\",\n \"dmg\": 55,\n \"pel\": 1\n },\n \"escopeta\": {\n \"n\": \"Escopeta 12 ga\",\n \"dmg\": 12,\n \"pel\": 9\n },\n \"cinquenta\": {\n \"n\": \".50 BMG\",\n \"dmg\": 180,\n \"pel\": 1\n },\n \"granada\": {\n \"n\": \"Granada\",\n \"dmg\": 300,\n \"pel\": 0\n }\n },\n \"elenco\": {\n \"segments\": {\n \"head\": {\n \"hp\": 40,\n \"radius\": 0.07\n },\n \"torso\": {\n \"hp\": 120\n },\n \"pelvis\": {\n \"hp\": 90\n },\n \"upperArm\": {\n \"hp\": 60,\n \"radius\": 0.085\n },\n \"forearm\": {\n \"hp\": 45,\n \"radius\": 0.065\n },\n \"hand\": {\n \"hp\": 25,\n \"radius\": 0.05\n },\n \"thigh\": {\n \"hp\": 80,\n \"radius\": 0.1\n },\n \"shin\": {\n \"hp\": 55,\n \"radius\": 0.08\n }\n },\n \"below\": {\n \"upperArm\": [\n \"forearm\",\n \"hand\"\n ],\n \"forearm\": [\n \"hand\"\n ],\n \"thigh\": [\n \"shin\"\n ]\n },\n \"falls\": {\n \"upperArm\": \"o braço inteiro\",\n \"forearm\": \"o antebraço com a mão\",\n \"hand\": \"a mão\",\n \"thigh\": \"a perna inteira\",\n \"shin\": \"a canela com o pé\",\n \"head\": \"a cabeça\"\n },\n \"cutWeapons\": [\n {\n \"id\": \"faca\",\n \"n\": \"Faca\",\n \"dmg\": 22,\n \"cut\": 0.7\n },\n {\n \"id\": \"facao\",\n \"n\": \"Facão\",\n \"dmg\": 55,\n \"cut\": 1\n },\n {\n \"id\": \"machado\",\n \"n\": \"Machado\",\n \"dmg\": 85,\n \"cut\": 1\n },\n {\n \"id\": \"p9\",\n \"n\": \"Pistola 9 mm\",\n \"dmg\": 30,\n \"cut\": 0.15\n },\n {\n \"id\": \"e12\",\n \"n\": \"Espingarda 12\",\n \"dmg\": 95,\n \"cut\": 0.75\n },\n {\n \"id\": \"f762\",\n \"n\": \"Fuzil 7,62\",\n \"dmg\": 65,\n \"cut\": 0.45\n }\n ]\n },\n \"severity\": {\n \"severAtCut\": 0.5,\n \"severAtDamageFraction\": 0.9,\n \"destroyAtDamageMultiple\": 2,\n \"limpAtHpFraction\": 0.5,\n \"splitAtDamage\": 150,\n \"headKillDamage\": 20,\n \"jawChance\": 0.3,\n \"jawDamage\": 25,\n \"headBitDamage\": 60,\n \"headBitShotgunMetres\": 5\n },\n \"defaultCut\": {\n \"slash\": 0.85,\n \"blunt\": 0,\n \"explosive\": 1,\n \"ballistic\": {\n \"pistol\": 0.15,\n \"buckshot\": 0.75,\n \"rifle\": 0.45\n }\n },\n \"deathRule\": {\n \"default\": \"vitality-or-head\",\n \"options\": [\n \"vitality-or-head\",\n \"head-only\"\n ]\n }\n}\n","import type { BodyRegion, BodySlot, SegmentKind } from '@adatechnology/game-contracts'\n\nimport segments from '../data/segments.json'\nimport type { JointId } from './body-spec'\n\nexport type SegmentSide = 'L' | 'R'\ntype LimbKind = 'upperArm' | 'forearm' | 'hand' | 'thigh' | 'shin' | 'foot'\nexport type SegmentId = 'pelvis' | 'torso' | 'neck' | 'head' | 'jaw' | `${LimbKind}${SegmentSide}`\n\n/** One segment of the tree: the joint it hangs from, its parent, the crowd region it belongs to and the item slots that cover it. */\nexport type SegmentNode = {\n readonly id: SegmentId\n readonly kind: SegmentKind\n readonly side?: SegmentSide\n readonly joint: JointId\n readonly parent?: SegmentId\n readonly region: BodyRegion\n readonly slots: readonly BodySlot[]\n}\n\n/** Numbers of a segment kind (the Arsenal table, A4): hit points, damage multiplier and bleed rate. */\nexport type SegmentNumbers = { readonly hitPoints: number; readonly multiplier: number; readonly bleed: number }\n\nconst limb = (kind: LimbKind, side: SegmentSide, joint: JointId, parent: SegmentId | undefined, slots: readonly BodySlot[]): SegmentNode => {\n const isArm = kind === 'upperArm' || kind === 'forearm' || kind === 'hand'\n const region: BodyRegion = isArm ? (side === 'L' ? 'armL' : 'armR') : side === 'L' ? 'legL' : 'legR'\n return { id: `${kind}${side}`, kind, side, joint, region, slots, ...(parent ? { parent } : {}) }\n}\n\nconst sideOf = (side: SegmentSide): SegmentNode[] => [\n limb('upperArm', side, `shoulder${side}`, 'torso', ['arms']),\n limb('forearm', side, `elbow${side}`, `upperArm${side}`, ['arms']),\n limb('hand', side, `wrist${side}`, `forearm${side}`, ['hands']),\n limb('thigh', side, `hip${side}`, 'pelvis', ['legs']),\n limb('shin', side, `knee${side}`, `thigh${side}`, ['legs']),\n limb('foot', side, `ankle${side}`, `shin${side}`, ['feet']),\n]\n\n/** The 17 segments (11 kinds, limbs on both sides), parents before children. Neck and jaw have no item slot: a helmet does not cover them. */\nexport const SEGMENT_TREE: readonly SegmentNode[] = [\n { id: 'pelvis', kind: 'pelvis', joint: 'hip', region: 'torso', slots: ['torso'] },\n { id: 'torso', kind: 'torso', joint: 'spine', parent: 'pelvis', region: 'torso', slots: ['torso'] },\n { id: 'neck', kind: 'neck', joint: 'neck', parent: 'torso', region: 'head', slots: [] },\n { id: 'head', kind: 'head', joint: 'head', parent: 'neck', region: 'head', slots: ['head', 'eyes'] },\n { id: 'jaw', kind: 'jaw', joint: 'jaw', parent: 'head', region: 'head', slots: [] },\n ...sideOf('L'),\n ...sideOf('R'),\n]\n\nexport const SEGMENT_IDS: readonly SegmentId[] = SEGMENT_TREE.map((node) => node.id)\n\nconst NODES = new Map(SEGMENT_TREE.map((node) => [node.id, node]))\n\nexport const segmentNode = (id: SegmentId): SegmentNode => {\n const node = NODES.get(id)\n if (!node) throw new Error(`game-characters: unknown segment \"${id}\"`)\n return node\n}\n\nexport const isSegmentId = (value: string): value is SegmentId => NODES.has(value as SegmentId)\n\n/** Every segment that hangs below `id` (children, grandchildren...), in tree order: what falls with it when it is severed. */\nexport const segmentsBelow = (id: SegmentId): SegmentId[] => {\n const out: SegmentId[] = []\n const visit = (parent: SegmentId): void => {\n for (const node of SEGMENT_TREE) {\n if (node.parent !== parent) continue\n out.push(node.id)\n visit(node.id)\n }\n }\n visit(id)\n return out\n}\n\n/** The Arsenal numbers per segment kind (hit points, `MULT`, `BLEED`). */\nexport const SEGMENT_NUMBERS: Readonly<Record<SegmentKind, SegmentNumbers>> = Object.fromEntries(\n segments.segments.map((entry) => [entry.id, { hitPoints: entry.hitPoints, multiplier: entry.multiplier, bleed: entry.bleed }]),\n) as Record<SegmentKind, SegmentNumbers>\n","import type { DestructibleSpec, JointSpec, PartShape, PartSpec, SegmentKind, Vec3 } from '@adatechnology/game-contracts'\n\nimport type { BodySpec } from './body-spec'\nimport type { Rig } from './rig'\nimport { SEGMENT_TREE } from './segment-tree'\nimport type { SegmentNode } from './segment-tree'\n\n/** The material id the parts of a body carry (a destruction world without it skips its own material rules: the body is a `DestructibleImpl`). */\nexport const BODY_MATERIAL = 'flesh'\n\n/** Share of the body mass per segment kind (sums to 1 over the 17 segments). */\nconst MASS_SHARE: Readonly<Record<SegmentKind, number>> = {\n head: 0.07,\n jaw: 0.01,\n neck: 0.02,\n torso: 0.34,\n pelvis: 0.14,\n upperArm: 0.028,\n forearm: 0.016,\n hand: 0.006,\n thigh: 0.1,\n shin: 0.045,\n foot: 0.015,\n}\nconst ADULT_MASS_KG = 70\nconst LEAF_KINDS: ReadonlySet<SegmentKind> = new Set(['head', 'jaw', 'hand', 'foot'])\n\nconst firstChild = (node: SegmentNode): SegmentNode | undefined => SEGMENT_TREE.find((candidate) => candidate.parent === node.id)\n\n/** A cylinder from `from` to `to`: centred between them, its local +Y turned onto the segment (Euler XYZ). */\nconst cylinderBetween = (from: Vec3, to: Vec3, radius: number): { shape: PartShape; position: Vec3; rotation: Vec3 } => {\n const axis: Vec3 = [to[0] - from[0], to[1] - from[1], to[2] - from[2]]\n const length = Math.max(1e-3, Math.hypot(axis[0], axis[1], axis[2]))\n const [x, y, z] = [axis[0] / length, axis[1] / length, axis[2] / length]\n return {\n shape: { kind: 'cylinder', radius, height: length },\n position: [(from[0] + to[0]) / 2, (from[1] + to[1]) / 2, (from[2] + to[2]) / 2],\n rotation: [Math.atan2(z, y), 0, -Math.asin(Math.max(-1, Math.min(1, x)))],\n }\n}\n\nconst partOf = (node: SegmentNode, rig: Rig, body: BodySpec, massKg: number): PartSpec => {\n const joint = rig.joints[node.joint]\n const radius = body.segmentRadius[node.kind] * (rig.height / body.heightMetres)\n const mass = massKg * MASS_SHARE[node.kind]\n const child = firstChild(node)\n if (LEAF_KINDS.has(node.kind) || !child) {\n const lift = node.kind === 'head' ? radius : node.kind === 'jaw' ? 0 : -radius\n return { id: node.id, material: BODY_MATERIAL, shape: { kind: 'sphere', radius }, position: [joint[0], joint[1] + lift, joint[2]], mass, tags: ['body', node.kind] }\n }\n const placed = cylinderBetween(joint, rig.joints[child.joint], radius)\n return { id: node.id, material: BODY_MATERIAL, ...placed, mass, tags: ['body', node.kind] }\n}\n\nexport type DescribeBodyParams = { readonly rig: Rig; readonly body: BodySpec; readonly build: number; readonly name?: string }\n\n/** The body as destruction parts (one per segment, in the body's local space) and the joints between them. */\nexport const describeBody = ({ rig, body, build, name }: DescribeBodyParams): DestructibleSpec => {\n const massKg = ADULT_MASS_KG * (rig.height / body.heightMetres) ** 3 * build\n const parts = SEGMENT_TREE.map((node) => partOf(node, rig, body, massKg))\n const joints: JointSpec[] = SEGMENT_TREE.filter((node) => node.parent).map((node) => ({ id: `${node.parent}~${node.id}`, a: node.parent as string, b: node.id, kind: 'fused', tools: [] }))\n return { ...(name ? { name } : {}), parts, joints, tags: ['character'] }\n}\n","import type { DestructibleSpec, JointState, ObjectState, PartState, PartStatus, Quat, Vec3 } from '@adatechnology/game-contracts'\n\nimport type { SegmentStatus } from '../types'\nimport { missingFromSegments } from './body-state'\nimport type { BodyState } from './body-state'\n\nconst PART_STATUS: Readonly<Record<SegmentStatus, PartStatus>> = { ok: 'intact', wounded: 'damaged', broken: 'damaged', severed: 'collapsed', destroyed: 'broken' }\nconst BODY_TEMPERATURE_C = 37\n\nexport type ToObjectStateParams = {\n readonly id: string\n readonly name?: string\n readonly position: Vec3\n readonly rotation: Quat\n readonly body: BodyState\n readonly spec: DestructibleSpec\n}\n\n/** The body in the destruction contract: one part per segment, the joints between them, the pieces and the `body` summary. */\nexport const toObjectState = ({ id, name, position, rotation, body, spec }: ToObjectStateParams): ObjectState => {\n const groups: string[][] = [body.segments.filter((segment) => segment.attached).map((segment) => segment.id)]\n for (const piece of body.pieces) {\n const left = piece.filter((part) => body.segments.find((segment) => segment.id === part)?.status !== 'destroyed')\n if (left.length > 0) groups.push(left)\n }\n const groupOf = (part: string): number => groups.findIndex((group) => group.includes(part))\n const parts: PartState[] = body.segments.map((segment) => {\n const mass = spec.parts.find((part) => part.id === segment.id)?.mass ?? 0\n return {\n id: segment.id,\n material: 'flesh',\n status: PART_STATUS[segment.status],\n hp: Math.max(0, segment.hp),\n maxHp: segment.max,\n mass,\n temperature: BODY_TEMPERATURE_C,\n burning: false,\n fuel: 0,\n char: 0,\n melt: 0,\n dents: [],\n holes: [],\n cracks: [],\n group: groupOf(segment.id),\n }\n })\n const joints: JointState[] = (spec.joints ?? []).map((joint) => {\n const child = body.segments.find((segment) => segment.id === joint.b)\n const parent = body.segments.find((segment) => segment.id === joint.a)\n const open = child !== undefined && parent !== undefined && parent.attached !== child.attached\n return { id: joint.id ?? `${joint.a}~${joint.b}`, a: joint.a, b: joint.b, kind: joint.kind, hp: open ? 0 : 1, maxHp: 1, progress: 0, open, ...(open ? { openedBy: 'break' as const } : {}) }\n })\n return {\n id,\n ...(name ? { name } : {}),\n position: [...position],\n rotation: [...rotation],\n parts,\n joints,\n groups,\n destroyed: false,\n body: { alive: body.alive, vitality: body.vitality, missing: body.detail === 'segments' ? body.missing | missingFromSegments(body) : body.missing, limp: body.limping.length > 0, crawl: body.crawl },\n }\n}\n","import type { Vec3 } from '@adatechnology/game-contracts'\n\nimport { JOINT_IDS } from './body-spec'\nimport type { BodySpec, JointId } from './body-spec'\n\n/** The bones of the game's instanced body (G 65336): 0 torso and hip, 1 head, 2/3 arms, 4/5 thighs, 6 eyes, 7/8 forearms, 9/10 shins, 11 jaw. */\nexport const GAME_BONES = {\n torso: 0,\n head: 1,\n upperArmL: 2,\n upperArmR: 3,\n thighL: 4,\n thighR: 5,\n eyes: 6,\n forearmL: 7,\n forearmR: 8,\n shinL: 9,\n shinR: 10,\n jaw: 11,\n} as const\n\nexport const JOINT_PARENTS: Readonly<Record<JointId, JointId | undefined>> = {\n hip: undefined,\n spine: 'hip',\n neck: 'spine',\n head: 'neck',\n jaw: 'head',\n shoulderL: 'spine',\n shoulderR: 'spine',\n elbowL: 'shoulderL',\n elbowR: 'shoulderR',\n wristL: 'elbowL',\n wristR: 'elbowR',\n hipL: 'hip',\n hipR: 'hip',\n kneeL: 'hipL',\n kneeR: 'hipR',\n ankleL: 'kneeL',\n ankleR: 'kneeR',\n}\n\n/** The joints of one body, scaled to its height and build. */\nexport type Rig = {\n readonly joints: Readonly<Record<JointId, Vec3>>\n readonly parents: Readonly<Record<JointId, JointId | undefined>>\n readonly gameBones: typeof GAME_BONES\n /** Height in metres of this body. */\n readonly height: number\n}\n\nexport type CreateRigParams = { readonly body: BodySpec; readonly height: number; readonly build: number }\n\n/** Joints of `body` scaled to `height` metres (vertical and depth) and to `build` (width). */\nexport const createRig = ({ body, height, build }: CreateRigParams): Rig => {\n const scale = height / body.heightMetres\n const joints = Object.fromEntries(\n JOINT_IDS.map((id) => {\n const [x, y, z] = body.joints[id]\n return [id, [x * scale * build, y * scale, z * scale] as Vec3]\n }),\n ) as Record<JointId, Vec3>\n return { joints, parents: JOINT_PARENTS, gameBones: GAME_BONES, height }\n}\n","import type { DestructibleSpec, ObjectState, Quat, Vec3 } from '@adatechnology/game-contracts'\n\nimport type { BodySpec } from './body/body-spec'\nimport { createBodyState, regionsFromSegments, segmentsFromRegions } from './body/body-state'\nimport type { BodyState } from './body/body-state'\nimport { describeBody } from './body/describe'\nimport { toObjectState } from './body/object-state'\nimport type { LimbRegion } from './body/regions'\nimport { createRig } from './body/rig'\nimport type { Rig } from './body/rig'\nimport { SEGMENT_NUMBERS } from './body/segment-tree'\nimport type { BodyMultipliers, CharacterSpec, Detail, SegmentState } from './types'\n\n/** A spec with every default filled in. */\nexport type ResolvedCharacterSpec = CharacterSpec & {\n readonly id: string\n readonly height: number\n readonly build: number\n readonly vitality: number\n readonly multipliers: BodyMultipliers\n readonly limbFactor: number\n readonly bloodFactor: number\n readonly bodyCount: number\n readonly stunSeconds: number\n readonly protection: readonly string[]\n readonly detail: Detail\n}\n\nexport type Character = {\n readonly id: string\n readonly spec: ResolvedCharacterSpec\n readonly rig: Rig\n /** Live view of the 17 segments. */\n readonly segments: readonly SegmentState[]\n readonly detail: Detail\n /** Hit points of the 5 crowd regions (head, arms, legs). */\n regionHitPoints(): Readonly<Record<LimbRegion, number>>\n /** Swaps the detail level: segments to regions sums each region (lossless); regions to segments spreads the region's share (lossy). */\n setDetail(detail: Detail): void\n setTransform(position: Vec3, rotation?: Quat): void\n describe(): DestructibleSpec\n state(): ObjectState\n reset(): void\n}\n\nexport const DEFAULT_VITALITY = 100\nexport const DEFAULT_STUN_SECONDS = 0.45\nconst NO_MULTIPLIERS: BodyMultipliers = { head: 1, torso: 1, arms: 1, legs: 1 }\n\nexport const resolveSpec = (spec: CharacterSpec, body: BodySpec, counter: number): ResolvedCharacterSpec => ({\n ...spec,\n id: spec.id ?? `${spec.kind}_${counter}`,\n height: spec.height ?? body.heightMetres,\n build: spec.build ?? 1,\n vitality: spec.vitality ?? DEFAULT_VITALITY,\n multipliers: { ...NO_MULTIPLIERS, ...spec.multipliers },\n limbFactor: spec.limbFactor ?? 1,\n bloodFactor: spec.bloodFactor ?? 1,\n bodyCount: spec.bodyCount ?? 1,\n stunSeconds: spec.stunSeconds ?? DEFAULT_STUN_SECONDS,\n protection: spec.protection ?? [],\n detail: spec.detail ?? 'segments',\n})\n\nexport type CharacterCore = {\n readonly spec: ResolvedCharacterSpec\n readonly rig: Rig\n readonly body: BodyState\n position: Vec3\n rotation: Quat\n readonly describe: () => DestructibleSpec\n /** Back to the state at creation (full health, every segment attached). */\n readonly resetBody: () => void\n /** The `ObjectState` of the body, built again only after `invalidateState` (cost contract: O(1) between changes). */\n readonly readState: () => ObjectState\n /** Call after anything that changes the body or the transform. */\n readonly invalidateState: () => void\n}\n\nexport type CreateCharacterCoreParams = { readonly spec: ResolvedCharacterSpec; readonly bodySpec: BodySpec; readonly hitPointsOf?: (kind: SegmentState['kind']) => number }\n\n/** The state every character shares: rig, body state, transform and the cached part list. */\nexport const createCharacterCore = ({ spec, bodySpec, hitPointsOf }: CreateCharacterCoreParams): CharacterCore => {\n const rig = createRig({ body: bodySpec, height: spec.height, build: spec.build })\n const parts = describeBody({ rig, body: bodySpec, build: spec.build, name: spec.kind })\n const fresh = (): BodyState => createBodyState({ vitality: spec.vitality, detail: spec.detail, crawling: spec.crawling ?? false, hitPointsOf: hitPointsOf ?? ((kind) => SEGMENT_NUMBERS[kind].hitPoints) })\n const body = fresh()\n let cachedState: ObjectState | undefined\n const core: CharacterCore = {\n spec,\n rig,\n body,\n position: [0, 0, 0],\n rotation: [0, 0, 0, 1],\n describe: () => parts,\n resetBody: () => {\n Object.assign(body, fresh())\n cachedState = undefined\n },\n readState: () => (cachedState ??= toObjectState({ id: spec.id, name: spec.kind, position: core.position, rotation: core.rotation, body, spec: parts })),\n invalidateState: () => {\n cachedState = undefined\n },\n }\n return core\n}\n\n/** The plain-data part of a character (state, detail and transform); the damage model is added on top of it. */\nexport const characterView = (core: CharacterCore): Character => ({\n id: core.spec.id,\n spec: core.spec,\n rig: core.rig,\n get segments() {\n return core.body.segments\n },\n get detail() {\n return core.body.detail\n },\n regionHitPoints: () => ({ ...core.body.regions }),\n setDetail(detail) {\n if (detail === core.body.detail) return\n if (detail === 'regions') regionsFromSegments(core.body)\n else segmentsFromRegions(core.body)\n core.body.detail = detail\n core.invalidateState()\n },\n setTransform(position, rotation) {\n core.position = [...position]\n if (rotation) core.rotation = [...rotation]\n core.invalidateState()\n },\n describe: core.describe,\n state: core.readState,\n reset: core.resetBody,\n})\n","import type { Quat, Vec3 } from '@adatechnology/game-contracts'\n\n/** `v` rotated by the unit quaternion `q`. */\nexport const rotate = (q: Quat, v: Vec3): Vec3 => {\n const [x, y, z, w] = q\n const tx = 2 * (y * v[2] - z * v[1])\n const ty = 2 * (z * v[0] - x * v[2])\n const tz = 2 * (x * v[1] - y * v[0])\n return [v[0] + w * tx + (y * tz - z * ty), v[1] + w * ty + (z * tx - x * tz), v[2] + w * tz + (x * ty - y * tx)]\n}\n\nconst conjugate = (q: Quat): Quat => [-q[0], -q[1], -q[2], q[3]]\n\nexport type Transform = { readonly position: Vec3; readonly rotation: Quat }\n\nexport const toWorld = ({ position, rotation }: Transform, local: Vec3): Vec3 => {\n const turned = rotate(rotation, local)\n return [turned[0] + position[0], turned[1] + position[1], turned[2] + position[2]]\n}\n\nexport const toLocal = ({ position, rotation }: Transform, world: Vec3): Vec3 => rotate(conjugate(rotation), [world[0] - position[0], world[1] - position[1], world[2] - position[2]])\n\nexport const distance = (a: Vec3, b: Vec3): number => Math.hypot(a[0] - b[0], a[1] - b[1], a[2] - b[2])\n","import type { DestructionEvent, Vec3 } from '@adatechnology/game-contracts'\n\nimport type { SegmentId } from '../body/segment-tree'\n\n/** A bleeding wound: `rate` in drops per second (the Arsenal's unit) fading to 0 over `duration` seconds; `spurt` pulses like an artery. */\nexport type Bleeder = {\n readonly part: SegmentId\n readonly rate: number\n readonly duration: number\n readonly spurt: boolean\n age: number\n /** Whole seconds of this bleeder already reported as `leak` events. */\n reported: number\n}\n\n/** Volume of one drop (a 4 mm drop of the Arsenal's blood): `leak` events carry litres per second. */\nexport const BLOOD_LITERS_PER_DROP = 0.00027\n\nexport const bleedRateAt = (bleeder: Bleeder): number => (bleeder.age >= bleeder.duration ? 0 : bleeder.rate * (1 - bleeder.age / bleeder.duration))\n\nexport type StepBleedingParams = {\n readonly bleeders: Bleeder[]\n readonly dt: number\n readonly emit: (event: Omit<Extract<DestructionEvent, { type: 'leak' }>, 'object' | 'time'>) => void\n readonly pointOf: (id: SegmentId) => Vec3\n}\n\n/** Ages the bleeders; each one still running reports a `leak` with its current rate once per second of its life. */\nexport const stepBleeding = ({ bleeders, dt, emit, pointOf }: StepBleedingParams): void => {\n for (const bleeder of bleeders) {\n if (bleeder.age >= bleeder.duration) continue\n bleeder.age += dt\n const second = Math.floor(bleeder.age)\n if (second <= bleeder.reported) continue\n bleeder.reported = second\n const rate = bleedRateAt(bleeder)\n if (rate > 0) emit({ type: 'leak', part: bleeder.part, fluid: 'blood', point: pointOf(bleeder.part), direction: [0, -1, 0], rate: rate * BLOOD_LITERS_PER_DROP, pressurized: bleeder.spurt })\n }\n}\n","import type { DamageType, DestructionEvent, Vec3 } from '@adatechnology/game-contracts'\n\nimport type { BodyState } from '../body/body-state'\nimport { segmentOf } from '../body/body-state'\nimport { segmentNode, segmentsBelow } from '../body/segment-tree'\nimport type { SegmentId, SegmentSide } from '../body/segment-tree'\nimport type { ResolvedCharacterSpec } from '../character'\nimport type { SegmentState } from '../types'\nimport { BLOOD_LITERS_PER_DROP } from './bleeding'\nimport type { Bleeder } from './bleeding'\nimport type { DamageRules, DeathRule } from './types'\n\ntype BodyChange = Extract<DestructionEvent, { type: 'body' }>['change']\ntype KillCause = 'head' | 'vitality' | 'bleed' | 'explosion'\ntype EventDraft = DestructionEvent extends infer E ? (E extends DestructionEvent ? Omit<E, 'object' | 'time'> : never) : never\n\nexport type BleedParams = { readonly segment: SegmentState; readonly rate: number; readonly duration: number; readonly spurt: boolean; readonly point?: Vec3; readonly direction?: Vec3 }\n\n/** How severing treats what hangs below: the Arsenal only detaches it, the Elenco also zeroes it and marks it cut. */\nexport type SeverBelow = 'detach' | 'zero'\n\n/** Every change a damage rule can make to a body, each emitting its events. The rules (kit, arsenal, elenco, regions) only decide. */\nexport type BodyOps = {\n readonly body: BodyState\n readonly spec: ResolvedCharacterSpec\n readonly rules: DamageRules\n readonly random: () => number\n readonly events: DestructionEvent[]\n emit(event: EventDraft): void\n pointOf(id: SegmentId): Vec3\n change(change: BodyChange, part?: SegmentId, extra?: { cause?: KillCause; below?: string[] }): void\n damage(segment: SegmentState, amount: number, damageType: DamageType): void\n wound(segment: SegmentState): void\n breakBone(segment: SegmentState): void\n sever(segment: SegmentState, below?: SeverBelow): void\n destroy(segment: SegmentState): void\n kill(cause: KillCause): void\n limp(side: SegmentSide): void\n crawl(): void\n /** A heavy hit staggers the body for the spec's `stunSeconds`. */\n stun(): void\n bleed(params: BleedParams): void\n}\n\nexport type CreateBodyOpsParams = {\n readonly body: BodyState\n readonly spec: ResolvedCharacterSpec\n readonly rules: DamageRules\n readonly random: () => number\n readonly time: number\n readonly source?: string\n readonly pointOf: (id: SegmentId) => Vec3\n readonly bleeders: Bleeder[]\n}\n\nconst killAllowed = (rule: DeathRule, cause: KillCause): boolean => rule === 'vitality-or-head' || cause === 'head' || cause === 'explosion'\n\nexport const createBodyOps = ({ body, spec, rules, random, time, source, pointOf, bleeders }: CreateBodyOpsParams): BodyOps => {\n const events: DestructionEvent[] = []\n const emit = (event: EventDraft): void => void events.push({ ...event, object: spec.id, time, ...(source !== undefined ? { source } : {}) } as DestructionEvent)\n const change: BodyOps['change'] = (kind, part, extra) => emit({ type: 'body', change: kind, ...(part ? { part } : {}), ...extra })\n const detachBelow = (segment: SegmentState, below: SeverBelow): SegmentId[] => {\n const fallen = segmentsBelow(segment.id).filter((id) => segmentOf(body, id).attached)\n segment.attached = false\n for (const id of fallen) {\n const part = segmentOf(body, id)\n part.attached = false\n if (below === 'zero') Object.assign(part, { hp: 0, status: 'severed' })\n }\n return fallen\n }\n const ops: BodyOps = {\n body,\n spec,\n rules,\n random,\n events,\n emit,\n pointOf,\n change,\n damage: (segment, amount, damageType) => emit({ type: 'damage', part: segment.id, amount, hp: Math.max(0, segment.hp), damageType }),\n wound(segment) {\n if (segment.status === 'ok') segment.status = 'wounded'\n change('wound', segment.id)\n },\n breakBone(segment) {\n segment.status = 'broken'\n change('broken', segment.id)\n },\n sever(segment, below = 'detach') {\n if (!segment.attached) return\n const fallen = detachBelow(segment, below)\n segment.status = 'severed'\n body.pieces.push([segment.id, ...fallen])\n const parent = segmentNode(segment.id).parent\n change('severed', segment.id, { below: fallen })\n if (parent) emit({ type: 'detach', joint: `${parent}~${segment.id}`, parts: [parent, segment.id], by: 'break', groups: [[segment.id, ...fallen]] })\n },\n destroy(segment) {\n if (!segment.attached) return\n const fallen = detachBelow(segment, 'detach')\n segment.status = 'destroyed'\n if (fallen.length > 0) body.pieces.push(fallen)\n change('destroyed', segment.id, { below: fallen })\n emit({ type: 'fracture', part: segment.id, style: 'tear', fragments: [], point: pointOf(segment.id) })\n },\n kill(cause) {\n if (!body.alive || !killAllowed(rules.deathRule, cause)) return\n body.alive = false\n change('killed', undefined, { cause })\n },\n limp(side) {\n if (body.limping.includes(side)) return\n body.limping.push(side)\n change('limp')\n },\n crawl() {\n if (body.crawl) return\n body.crawl = true\n change('crawl')\n },\n stun() {\n body.stunSeconds = spec.stunSeconds\n change('stunned')\n },\n bleed({ segment, rate, duration, spurt, point, direction }) {\n const scaled = rate * spec.bloodFactor\n bleeders.push({ part: segment.id, rate: scaled, duration, spurt, age: 0, reported: 0 })\n emit({ type: 'leak', part: segment.id, fluid: 'blood', point: point ?? pointOf(segment.id), direction: direction ?? [0, -1, 0], rate: scaled * BLOOD_LITERS_PER_DROP, pressurized: spurt })\n },\n }\n return ops\n}\n","{\n \"id\": \"physical\",\n \"tissuePerCentimetre\": {\n \"flesh\": 0.25,\n \"bone\": 1\n },\n \"torsoPath\": {\n \"fleshCentimetres\": 22,\n \"boneCentimetres\": 2\n },\n \"regionFactor\": {\n \"head\": 0.7,\n \"limb\": 0.5,\n \"torso\": 1\n },\n \"bruteTorsoFactor\": 2,\n \"energyKept\": \"P / (P + R)\",\n \"points\": {\n \"coefficient\": 3.5,\n \"exponent\": 0.37\n },\n \"tolerance\": {\n \"bodies\": 0,\n \"energyKept\": 0.05\n },\n \"calibration\": [\n {\n \"caliber\": \"9x19\",\n \"force\": 7.67,\n \"bodies\": 2,\n \"energyKeptFirstBody\": 0.51,\n \"expectedBodies\": 2,\n \"bestiaryEnergyKept\": 0.5\n },\n {\n \"caliber\": \"5_56x45\",\n \"force\": 16.56,\n \"bodies\": 3,\n \"energyKeptFirstBody\": 0.69,\n \"expectedBodies\": 3,\n \"bestiaryEnergyKept\": 0.65\n },\n {\n \"caliber\": \"7_62x51\",\n \"force\": 26.67,\n \"bodies\": 4,\n \"energyKeptFirstBody\": 0.78,\n \"expectedBodies\": 4,\n \"bestiaryEnergyKept\": 0.75\n },\n {\n \"caliber\": \"7_62x39\",\n \"force\": 18.52,\n \"bodies\": 3,\n \"energyKeptFirstBody\": 0.71,\n \"expectedBodies\": 3\n },\n {\n \"caliber\": \"12ga_00buck\",\n \"force\": 4.35,\n \"bodies\": 1,\n \"energyKeptFirstBody\": 0.37,\n \"expectedBodies\": 1\n }\n ],\n \"torsoResistance\": 7.5\n}\n","import type { SegmentKind } from '@adatechnology/game-contracts'\n\nimport { SEGMENT_NUMBERS } from '../body/segment-tree'\nimport segments from '../data/segments.json'\nimport physical from '../presets/physical.json'\nimport teamGame from '../presets/team-game.json'\nimport type { BallisticClass, CaliberEntry, DamageRules, ProtectionLayer } from './types'\n\nconst { severity } = segments\n\n/** The kit's rules: Arsenal numbers per segment (A4), the design's severity, the game's head rule, death by vitality or head. */\nexport const DEFAULT_DAMAGE_RULES: DamageRules = {\n deathRule: 'vitality-or-head',\n segmentRule: 'kit',\n severity: { ...severity, stunAtPoints: 35, limbVitalityShare: 0.3, regionLimbVitalityShare: 0.5 },\n blast: { minimumPoints: 8, headDestroyPoints: 40, bigPoints: 120 },\n defaultCut: segments.defaultCut,\n points: physical.points,\n spread: 0,\n segments: SEGMENT_NUMBERS,\n burnPointsPerSecond: 3,\n}\n\n/** The Galeria 3D do Arsenal, replayed: its `damage`, `sever`, `explodeSeg`, `splitTorso`, `legLost` and `grenadeAt`. */\nexport const ARSENAL_RULES: DamageRules = { ...DEFAULT_DAMAGE_RULES, segmentRule: 'arsenal' }\n\nconst ELENCO_HIT_POINTS = segments.elenco.segments as Partial<Record<SegmentKind, { readonly hp: number }>>\n\n/** The Elenco, replayed: its own hit points, no multiplier, ±15 % per blow and its cut rule (`hit`, `sever`, `breakBone`); a torso at 0 kills (a human). */\nexport const ELENCO_RULES: DamageRules = {\n ...DEFAULT_DAMAGE_RULES,\n segmentRule: 'elenco',\n spread: 0.15,\n segments: Object.fromEntries(\n Object.entries(SEGMENT_NUMBERS).map(([kind, numbers]) => [kind, { ...numbers, multiplier: 1, hitPoints: ELENCO_HIT_POINTS[kind as SegmentKind]?.hp ?? numbers.hitPoints }]),\n ) as DamageRules['segments'],\n}\n\nexport const DAMAGE_PRESETS = { kit: DEFAULT_DAMAGE_RULES, arsenal: ARSENAL_RULES, elenco: ELENCO_RULES } as const\n\n/** The protection presets that reproduce the game's `ROUPAS` (tools/characters-catalog/decisions.json `protectionPresets`). */\nexport const PROTECTION_PRESETS: readonly ProtectionLayer[] = [\n { id: 'vest_police', slots: ['torso'], ballistic: { resistance: 14, blunt: 0.3, pass: 0.8 } },\n { id: 'vest_military', slots: ['torso'], ballistic: { resistance: 28, blunt: 0.5 } },\n { id: 'helmet_combat', slots: ['head'], ballistic: { resistance: 9, blunt: 0, pass: 1, consumable: true } },\n { id: 'turnout_gear', slots: ['torso', 'arms', 'legs'], fire: 0.25, limbs: 0.8 },\n]\n\nconst CLASS_OF: Readonly<Record<string, BallisticClass>> = { pistol: 'pistol', rifle: 'rifle' }\n\n/** The game's calibre table (`CALIBRES`) under the kit's calibre ids, for the `team-game` model. */\nexport const TEAM_GAME_CALIBERS: readonly CaliberEntry[] = Object.entries(teamGame.calibers).map(([id, entry]) => ({\n id,\n points: entry.points,\n penetration: entry.penetration,\n class: CLASS_OF[entry.class] ?? 'pistol',\n skullRange: entry.skullRange,\n pellets: 'pellets' in entry ? entry.pellets : 1,\n effectiveRangeMetres: entry.effectiveRangeMetres,\n}))\n\n/** The game's calibre keys (`9x19`, `12ga`, `762x39`, `308`) to the kit's calibre ids. */\nexport const TEAM_GAME_CALIBER_KEYS: Readonly<Record<string, string>> = Object.fromEntries(Object.entries(teamGame.calibers).map(([id, entry]) => [entry.gameKey, id]))\n\nexport const TEAM_GAME_RULES = { regionCost: teamGame.regionCost, damageKeptPerBody: teamGame.damageKeptPerBody, falloff: teamGame.falloff } as const\n\nexport const PHYSICAL_RULES = physical\n","import type { BodyRegion, SegmentKind } from '@adatechnology/game-contracts'\n\nimport { PHYSICAL_RULES, TEAM_GAME_RULES } from './defaults'\nimport type { PenetrationModel } from './types'\n\n/** P = √(E·m/A) against the resistance of the path; a protection layer stops a projectile whose force is not above its resistance. */\nexport const PHYSICAL_PENETRATION: PenetrationModel = {\n id: 'physical',\n stops: (layer, projectile) => projectile.force <= (layer.ballistic?.resistance ?? 0),\n piercesSkull: (projectile, headResistance) => projectile.force > headResistance,\n}\n\n/** The game's rule: pistol-class rounds are stopped by a vest or a helmet; the skull is pierced within the calibre's `cranio` range. */\nexport const TEAM_GAME_PENETRATION: PenetrationModel = {\n id: 'team-game',\n stops: (_layer, projectile) => projectile.class === 'pistol',\n piercesSkull: (projectile) => projectile.skullRange === 1 || projectile.distance <= projectile.skullRange,\n}\n\nexport const PENETRATION_MODELS = { physical: PHYSICAL_PENETRATION, 'team-game': TEAM_GAME_PENETRATION } as const\n\nconst { tissuePerCentimetre, torsoPath, regionFactor } = PHYSICAL_RULES\n\n/** Resistance of a torso path: flesh 22 cm × 0.25 + bone 2 cm × 1.0 = 7.5. */\nexport const TORSO_RESISTANCE = torsoPath.fleshCentimetres * tissuePerCentimetre.flesh + torsoPath.boneCentimetres * tissuePerCentimetre.bone\n\nconst HEAD_KINDS: ReadonlySet<SegmentKind> = new Set(['head', 'jaw', 'neck'])\nconst TORSO_KINDS: ReadonlySet<SegmentKind> = new Set(['torso', 'pelvis'])\n\nexport type ResistanceParams = { readonly kind: SegmentKind; readonly bodyCount: number; readonly protection: number }\n\n/** R of the path through a segment: torso 7.5 (× the body count of a brute), head 0.7×, limbs 0.5×, plus the protection worn over it. */\nexport const segmentResistance = ({ kind, bodyCount, protection }: ResistanceParams): number => {\n if (TORSO_KINDS.has(kind)) return TORSO_RESISTANCE * regionFactor.torso * bodyCount + protection\n if (HEAD_KINDS.has(kind)) return TORSO_RESISTANCE * regionFactor.head + protection\n return TORSO_RESISTANCE * regionFactor.limb + protection\n}\n\n/** Share of the energy a projectile of force `force` keeps after a path of resistance `resistance`: P/(P+R). */\nexport const energyKept = (force: number, resistance: number): number => (force > 0 ? force / (force + resistance) : 0)\n\n/** Penetration points a region costs in the game (`custo`): head 0.7, torso 1 (a brute 2), limb 0.5. */\nexport const teamGameCost = (region: BodyRegion, bodyCount: number): number => {\n const cost = TEAM_GAME_RULES.regionCost\n if (region === 'head') return cost.head\n if (region === 'torso') return bodyCount > 1 ? cost.bruteTorso : cost.torso\n return cost.limb\n}\n\n/** The game's falloff: full damage up to the effective range, then linear down to half at twice the range, never below half. */\nexport const teamGameFalloff = (distance: number, range: number): number => {\n const floor = TEAM_GAME_RULES.falloff.atTwiceRange\n return distance <= range * TEAM_GAME_RULES.falloff.fullAtRange ? 1 : Math.max(floor, 1 - ((distance - range) / range) * (1 - floor))\n}\n","import type { DamageType, SegmentKind, Vec3 } from '@adatechnology/game-contracts'\n\nimport { segmentNode } from '../../body/segment-tree'\nimport type { ResolvedCharacterSpec } from '../../character'\nimport type { SegmentState } from '../../types'\nimport type { BodyOps } from '../body-ops'\nimport type { PenetrationModel, ProjectileInfo } from '../types'\n\n/** One blow on one segment, after protection: what every segment rule receives. */\nexport type SegmentBlow = {\n readonly segment: SegmentState\n /** Points after protection, before the rule's multipliers and spread. */\n readonly points: number\n readonly cut: number\n readonly damageType: DamageType\n readonly projectile?: ProjectileInfo\n readonly explosive: boolean\n /** A blast strong enough to destroy what it takes to 0 (Arsenal `big`). */\n readonly big: boolean\n /** Where it landed (world): a bullet wound bleeds there; a blast share has none. */\n readonly point?: Vec3\n /** Extra factor on arms and legs (age × protection). */\n readonly limbFactor: number\n readonly model: PenetrationModel\n /** Resistance of the head path (the skull rule of the `physical` model). */\n readonly headResistance: number\n}\n\nexport type RuleResult = { readonly headshot: boolean }\n\nexport type SegmentRule = (ops: BodyOps, blow: SegmentBlow) => RuleResult\n\nexport const NO_HEADSHOT: RuleResult = { headshot: false }\nexport const HEADSHOT: RuleResult = { headshot: true }\n\nconst ARM_KINDS: ReadonlySet<SegmentKind> = new Set(['upperArm', 'forearm', 'hand'])\nconst LEG_KINDS: ReadonlySet<SegmentKind> = new Set(['thigh', 'shin', 'foot'])\n\nexport const isArm = (kind: SegmentKind): boolean => ARM_KINDS.has(kind)\nexport const isLeg = (kind: SegmentKind): boolean => LEG_KINDS.has(kind)\nexport const isLimb = (kind: SegmentKind): boolean => isArm(kind) || isLeg(kind)\n\n/** The body type's multiplier for the group the segment is in (head, torso, arms, legs). */\nexport const typeMultiplier = (spec: ResolvedCharacterSpec, kind: SegmentKind): number => {\n if (isArm(kind)) return spec.multipliers.arms\n if (isLeg(kind)) return spec.multipliers.legs\n if (kind === 'torso' || kind === 'pelvis') return spec.multipliers.torso\n return spec.multipliers.head\n}\n\n/** Points the segment takes: the blow × segment multiplier × body type × (on a limb) age and protection. */\nexport const segmentDamage = (ops: BodyOps, blow: SegmentBlow, points: number): number => {\n const { kind } = blow.segment\n return points * ops.rules.segments[kind].multiplier * typeMultiplier(ops.spec, kind) * (isLimb(kind) ? blow.limbFactor : 1)\n}\n\n/** The stump a lost segment leaves bleeds hard (×1.5 at the neck and head) for 45 s, spurting (Arsenal `stumpAt`). */\nexport const bleedStump = (ops: BodyOps, segment: SegmentState): void => {\n const bleed = ops.rules.segments[segment.kind].bleed\n const parent = segmentNode(segment.id).parent\n const stump = parent ? (ops.body.segments.find((candidate) => candidate.id === parent) ?? segment) : segment\n ops.bleed({ segment: stump, rate: bleed * (segment.kind === 'neck' || segment.kind === 'head' ? 1.5 : 1), duration: 45, spurt: true })\n}\n\n/** The piece that flies off bleeds 35 % of the rate for 15 s (Arsenal `sever`). */\nexport const bleedPiece = (ops: BodyOps, segment: SegmentState): void => ops.bleed({ segment, rate: ops.rules.segments[segment.kind].bleed * 0.35, duration: 15, spurt: true })\n\n/** A bullet wound bleeds 30 % of the segment's rate for 10 s, without spurting (Arsenal `damage` with a point). */\nexport const bleedWound = (ops: BodyOps, segment: SegmentState, point: Vec3): void =>\n ops.bleed({ segment, rate: ops.rules.segments[segment.kind].bleed * 0.3, duration: 10, spurt: false, point })\n\n/** Cut off at the joint, with its two bleeders. */\nexport const severWithBleeding = (ops: BodyOps, segment: SegmentState): void => {\n if (!segment.attached) return\n bleedStump(ops, segment)\n ops.sever(segment)\n bleedPiece(ops, segment)\n}\n\n/** Blown to pieces: only the stump bleeds. */\nexport const destroyWithBleeding = (ops: BodyOps, segment: SegmentState): void => {\n if (!segment.attached) return\n bleedStump(ops, segment)\n ops.destroy(segment)\n}\n","import { MISSING_BITS } from '@adatechnology/game-contracts'\nimport type { BodyRegion, DamageType } from '@adatechnology/game-contracts'\n\nimport { segmentOf } from '../../body/body-state'\nimport { REGION_HIT_POINTS, REGION_SEGMENTS } from '../../body/regions'\nimport type { LimbRegion } from '../../body/regions'\nimport type { BodyOps } from '../body-ops'\nimport { spreadPoints } from '../points'\nimport type { ProtectionOutcome } from '../protection'\nimport type { PenetrationModel, ProjectileInfo } from '../types'\nimport { HEADSHOT, NO_HEADSHOT } from './blow'\nimport type { RuleResult } from './blow'\n\n/** One blow on one crowd region. `points` are the blow's own (the game stuns on them); the torso applies the vest share itself. */\nexport type RegionBlow = {\n readonly region: BodyRegion\n readonly points: number\n readonly cut: number\n readonly damageType: DamageType\n readonly projectile?: ProjectileInfo\n readonly protection: ProtectionOutcome\n readonly model: PenetrationModel\n readonly headResistance: number\n}\n\nconst LIMB_BITS: Readonly<Record<Exclude<LimbRegion, 'head'>, { readonly severed: number; readonly broken: number }>> = {\n armL: { severed: MISSING_BITS.armL, broken: MISSING_BITS.armLBroken },\n armR: { severed: MISSING_BITS.armR, broken: MISSING_BITS.armRBroken },\n legL: { severed: MISSING_BITS.legL, broken: MISSING_BITS.legLBroken },\n legR: { severed: MISSING_BITS.legR, broken: MISSING_BITS.legRBroken },\n}\n\nconst rootOf = (ops: BodyOps, region: BodyRegion) => segmentOf(ops.body, REGION_SEGMENTS[region][0] as Parameters<typeof segmentOf>[1])\n\nconst hitHead = (ops: BodyOps, blow: RegionBlow, points: number): RuleResult => {\n const { body, rules } = ops\n const rule = rules.severity\n const multiplier = rules.segments.head.multiplier\n const { projectile } = blow\n const lethal = projectile ? blow.model.piercesSkull(projectile, blow.headResistance) && points >= rule.headKillDamage : points * multiplier >= body.regions.head\n if (lethal) {\n const shotgunClose = projectile !== undefined && projectile.pellets > 1 && projectile.distance < rule.headBitShotgunMetres\n if (points > rule.headBitDamage || shotgunClose) {\n body.missing |= MISSING_BITS.head\n ops.destroy(segmentOf(body, 'head'))\n }\n return HEADSHOT\n }\n body.vitality -= points * multiplier\n body.regions.head -= points\n ops.damage(segmentOf(body, 'head'), points * multiplier, blow.damageType)\n if (points > rule.jawDamage && ops.random() < rule.jawChance && (body.missing & MISSING_BITS.jaw) === 0) {\n body.missing |= MISSING_BITS.jaw\n ops.sever(segmentOf(body, 'jaw'))\n }\n return NO_HEADSHOT\n}\n\nconst hitLimb = (ops: BodyOps, blow: RegionBlow, region: Exclude<LimbRegion, 'head'>, points: number): void => {\n const { body, spec, rules } = ops\n const leg = region === 'legL' || region === 'legR'\n const factor = (leg ? spec.multipliers.legs : spec.multipliers.arms) * spec.limbFactor * blow.protection.limbs\n const amount = points * factor\n body.regions[region] -= amount\n body.vitality -= amount * rules.severity.regionLimbVitalityShare\n const root = rootOf(ops, region)\n ops.damage(root, amount, blow.damageType)\n const max = REGION_HIT_POINTS[region]\n if (leg && body.regions[region] <= max * rules.severity.limpAtHpFraction) ops.limp(region === 'legL' ? 'L' : 'R')\n if (body.regions[region] > 0) return\n const severs = blow.cut >= rules.severity.severAtCut || amount > max * rules.severity.severAtDamageFraction\n const bit = severs ? LIMB_BITS[region].severed : LIMB_BITS[region].broken\n const isNew = (body.missing & bit) === 0\n body.missing |= bit\n if (leg) ops.crawl()\n if (!isNew) return\n if (severs) ops.sever(root)\n else ops.breakBone(root)\n}\n\n/** The game's `zDamage` (G 67011–67045): the crowd model of 6 regions, the bitmask, the helmet, the vest, limp, crawl and the jaw. */\nexport const regionRule = (ops: BodyOps, blow: RegionBlow): RuleResult => {\n const { body, rules } = ops\n const points = spreadPoints(blow.points, rules.spread, ops.random)\n let result = NO_HEADSHOT\n if (blow.region === 'head') result = hitHead(ops, blow, points)\n else if (blow.region === 'torso') {\n const amount = points * ops.spec.multipliers.torso * blow.protection.ballistic\n body.vitality -= amount\n ops.damage(segmentOf(body, 'torso'), amount, blow.damageType)\n } else hitLimb(ops, blow, blow.region, points)\n if (result.headshot) ops.kill('head')\n else if (body.vitality <= 0) ops.kill('vitality')\n if (!body.alive || result.headshot || body.vitality <= 0) return result\n if (points >= rules.severity.stunAtPoints) ops.stun()\n return result\n}\n","import type { DamageType } from '@adatechnology/game-contracts'\n\nimport type { BallisticClass, BodyHit, CaliberEntry, DamageRules, ProjectileInfo } from './types'\n\n/** Deterministic mulberry32 stream in [0, 1). */\nexport const createRng = (seed: number): (() => number) => {\n let state = seed >>> 0\n return () => {\n state = (state + 0x6d2b79f5) >>> 0\n let mixed = Math.imul(state ^ (state >>> 15), state | 1)\n mixed ^= mixed + Math.imul(mixed ^ (mixed >>> 7), mixed | 61)\n return ((mixed ^ (mixed >>> 14)) >>> 0) / 4294967296\n }\n}\n\n/** Damage points of a blow from its energy in joules: `coefficient · E^exponent` (9×19 → 35, 7.62×39 → 59, .308 → 71.7). */\nexport const pointsFromEnergy = (energy: number, rules: Pick<DamageRules, 'points'>): number => (energy > 0 ? rules.points.coefficient * energy ** rules.points.exponent : 0)\n\n/** The caller's points, else the calibre table's, else the energy formula. */\nexport const resolvePoints = (hit: BodyHit, caliber: CaliberEntry | undefined, rules: Pick<DamageRules, 'points'>): number => hit.points ?? caliber?.points ?? pointsFromEnergy(hit.energy ?? 0, rules)\n\n/** One draw of ±`spread` on the points (none when the spread is 0, so the stream is not touched). */\nexport const spreadPoints = (points: number, spread: number, random: () => number): number => (spread > 0 ? points * (1 - spread + random() * 2 * spread) : points)\n\nconst DEFAULT_PROJECTILE_GRAMS = 8\nconst RIFLE_JOULES = 1000\n\n/** P = √(E·m/A): E in joules, m in grams, A the cross-section in mm². */\nexport const penetrationForce = (energy: number, grams: number, diameterMm: number): number => {\n const area = (Math.PI * diameterMm ** 2) / 4\n return area > 0 && energy > 0 ? Math.sqrt((energy * grams) / area) : 0\n}\n\n/** What a ballistic hit is (force, class, skull range, pellets, distance), from the hit and the calibre table. */\nexport const projectileOf = (hit: BodyHit, caliber: CaliberEntry | undefined): ProjectileInfo => {\n const pellets = hit.pellets ?? caliber?.pellets ?? 1\n const energy = hit.energy ?? 0\n const fallback: BallisticClass = energy > RIFLE_JOULES ? 'rifle' : 'pistol'\n return {\n force: penetrationForce(energy, hit.mass ?? DEFAULT_PROJECTILE_GRAMS, hit.caliber ?? 9),\n class: caliber?.class ?? fallback,\n skullRange: caliber?.skullRange ?? 1,\n pellets,\n distance: hit.distance ?? 0,\n }\n}\n\n/** `hit.cut`, else the default of its damage type (a bullet by class: pistol 0.15, buckshot 0.75, rifle 0.45). */\nexport const cutOf = (hit: BodyHit, projectile: ProjectileInfo | undefined, rules: Pick<DamageRules, 'defaultCut'>): number => {\n if (hit.cut !== undefined) return hit.cut\n const type: DamageType = hit.type\n if (type === 'ballistic' && projectile) return rules.defaultCut.ballistic[projectile.pellets > 1 ? 'buckshot' : projectile.class]\n if (type === 'slash' || type === 'blunt' || type === 'explosive') return rules.defaultCut[type]\n return 0\n}\n","import type { Vec3 } from '@adatechnology/game-contracts'\n\nimport { REGION_SEGMENTS, BODY_REGIONS } from '../body/regions'\nimport { segmentNode } from '../body/segment-tree'\nimport type { SegmentId } from '../body/segment-tree'\nimport { distance } from '../body/transform'\nimport type { BodyOps } from './body-ops'\nimport { HEADSHOT, NO_HEADSHOT, destroyWithBleeding } from './rules/blow'\nimport type { RuleResult, SegmentBlow, SegmentRule } from './rules/blow'\nimport { regionRule } from './rules/regions'\nimport type { RegionBlow } from './rules/regions'\n\nexport type BlastParams = {\n readonly ops: BodyOps\n /** Centre of the blast in the body's local space. */\n readonly center: Vec3\n readonly points: number\n readonly radius: number\n /** Inside this distance a segment takes the whole blast (lethal); from it to `radius` it takes at most `blast.injuryPoints`, falling off to nothing at `radius`. Without it: `points × (1 − distance/radius)`. */\n readonly lethalRadius?: number\n /** A joint position in local space. */\n readonly jointOf: (id: SegmentId) => Vec3\n}\n\n/** Points per segment at the edge of the lethal radius when the rules name none: a person there is left badly hurt, not dead. */\nconst INJURY_POINTS = 15\n\n/** The points of a blast on a segment `metres` from its centre: `points × (1 − distance/radius)`; with a `lethalRadius`, all of them inside it and, beyond it, the injury share that falls off to the edge. */\nconst pointsAt = (params: BlastParams, metres: number): number => {\n const { points, radius, lethalRadius, ops } = params\n if (lethalRadius === undefined || lethalRadius >= radius) return points * Math.max(0, 1 - metres / radius)\n if (metres <= lethalRadius) return points\n const injury = Math.min(points, ops.rules.blast.injuryPoints ?? INJURY_POINTS)\n return injury * Math.max(0, 1 - (metres - lethalRadius) / (radius - lethalRadius))\n}\n\n/** The share of a blast each target takes (`pointsAt`), kept above the minimum, strongest first (Arsenal `grenadeAt`). */\nconst shares = <TTarget>(params: BlastParams, targets: readonly TTarget[], pivot: (target: TTarget) => SegmentId): { target: TTarget; points: number }[] =>\n targets\n .map((target) => ({ target, points: pointsAt(params, distance(params.jointOf(pivot(target)), params.center)) }))\n .filter((share) => share.points > params.ops.rules.blast.minimumPoints)\n .sort((a, b) => b.points - a.points)\n\nexport type SegmentBlastParams = BlastParams & { readonly rule: SegmentRule; readonly blow: Omit<SegmentBlow, 'segment' | 'points' | 'big' | 'explosive' | 'point'> }\n\n/** A blast on the 17 segments: a strong share on the head blows it off; every other share goes through the segment rule as an explosive blow. */\nexport const segmentBlast = (params: SegmentBlastParams): RuleResult => {\n const { ops, rule, blow } = params\n const { blast, segments } = ops.rules\n let result = NO_HEADSHOT\n for (const { target, points } of shares(params, ops.body.segments.filter((segment) => segment.attached), (segment) => segment.id)) {\n if (!target.attached) continue\n if (target.kind === 'head' && points > blast.headDestroyPoints) {\n destroyWithBleeding(ops, target)\n ops.kill('explosion')\n result = HEADSHOT\n continue\n }\n rule(ops, { ...blow, segment: target, points: points / segments[target.kind].multiplier, big: points > blast.bigPoints, explosive: true })\n }\n return result\n}\n\nexport type RegionBlastParams = BlastParams & { readonly blow: Omit<RegionBlow, 'region' | 'points'> }\n\n/** A blast on the 6 crowd regions, each measured at the joint of the segment that names it. */\nexport const regionBlast = (params: RegionBlastParams): RuleResult => {\n let result = NO_HEADSHOT\n const pivot = (region: (typeof BODY_REGIONS)[number]): SegmentId => segmentNode(REGION_SEGMENTS[region][0] as SegmentId).id\n for (const { target, points } of shares(params, BODY_REGIONS, pivot)) {\n if (regionRule(params.ops, { ...params.blow, region: target, points }).headshot) result = HEADSHOT\n }\n return result\n}\n","import type { BodySlot } from '@adatechnology/game-contracts'\n\nimport type { PenetrationModel, ProjectileInfo, ProtectionLayer } from './types'\n\n/** The layered protection of T3: `1 − Π(1 − value·condition)`, at most `cap`. */\nexport const LAYERED_PROTECTION_CAP = 0.9\n\nexport type LayeredProtectionParams = {\n readonly layers: readonly ProtectionLayer[]\n readonly attack: 'bite' | 'scratch'\n /** 0..1 condition per layer id (worn, torn); 1 by default. */\n readonly conditions?: Readonly<Record<string, number>>\n}\n\nexport const layeredProtection = ({ layers, attack, conditions }: LayeredProtectionParams): number => {\n const through = layers.reduce((left, layer) => left * (1 - (layer[attack] ?? 0) * (conditions?.[layer.id] ?? 1)), 1)\n return Math.min(LAYERED_PROTECTION_CAP, 1 - through)\n}\n\n/** The layers worn that cover any of `slots` and are not used up. */\nexport const layersOn = (worn: readonly ProtectionLayer[], slots: readonly BodySlot[], consumed: ReadonlySet<string>): ProtectionLayer[] =>\n worn.filter((layer) => !consumed.has(layer.id) && layer.slots.some((slot) => slots.includes(slot)))\n\n/** What the layers over a segment do to one blow. */\nexport type ProtectionOutcome = {\n /** A consumable layer took the whole projectile (a helmet): no damage, and the layer is used up. */\n readonly absorbedBy?: string\n /** Share of the points that gets through the ballistic layers. */\n readonly ballistic: number\n /** Penetration resistance the layers add (the `physical` model). */\n readonly resistance: number\n /** Factor on limb damage (a turnout coat). */\n readonly limbs: number\n /** Factor on fire damage. */\n readonly fire: number\n}\n\nexport type ProtectParams = {\n readonly layers: readonly ProtectionLayer[]\n readonly projectile?: ProjectileInfo\n readonly model: PenetrationModel\n}\n\n/** A stopped projectile does the layer's `blunt` share; one that goes through, `pass` (default `blunt`). Non-projectiles ignore ballistic layers. */\nexport const protect = ({ layers, projectile, model }: ProtectParams): ProtectionOutcome => {\n let ballistic = 1\n let resistance = 0\n for (const layer of layers) {\n const armour = layer.ballistic\n if (!armour || !projectile) continue\n const stops = model.stops(layer, projectile)\n if (stops && armour.consumable) return { absorbedBy: layer.id, ballistic: 0, resistance: armour.resistance, limbs: 1, fire: 1 }\n ballistic *= stops ? armour.blunt : (armour.pass ?? armour.blunt)\n resistance += armour.resistance\n }\n const limbs = layers.reduce((factor, layer) => factor * (layer.limbs ?? 1), 1)\n const fire = layers.reduce((factor, layer) => factor * (layer.fire ?? 1), 1)\n return { ballistic, resistance, limbs, fire }\n}\n","import type { BodyRegion, DestructibleSpec, Vec3 } from '@adatechnology/game-contracts'\n\nimport type { BodyState } from '../body/body-state'\nimport { isBodyRegion, REGION_SEGMENTS } from '../body/regions'\nimport { isSegmentId } from '../body/segment-tree'\nimport type { SegmentId } from '../body/segment-tree'\nimport { distance } from '../body/transform'\n\nconst radiusOf = (part: DestructibleSpec['parts'][number]): number => (part.shape.kind === 'box' ? Math.min(...part.shape.size) / 2 : part.shape.radius)\n\n/**\n * The segment a hit lands on: `hit.part` when the caller resolved it (a segment id, or a region id for its first segment), else the attached\n * part whose surface is nearest to the local point.\n */\nexport const resolveSegment = (body: BodyState, spec: DestructibleSpec, part: string | undefined, localPoint: Vec3): SegmentId => {\n if (part !== undefined && isSegmentId(part)) return part\n if (part !== undefined && isBodyRegion(part)) return REGION_SEGMENTS[part][0] as SegmentId\n let best: SegmentId = 'torso'\n let bestGap = Infinity\n for (const candidate of spec.parts) {\n const segment = body.segments.find((entry) => entry.id === candidate.id)\n if (!segment?.attached) continue\n const gap = distance(candidate.position, localPoint) - radiusOf(candidate)\n if (gap < bestGap) [best, bestGap] = [segment.id, gap]\n }\n return best\n}\n\nexport const regionOfSegment = (body: BodyState, id: SegmentId): BodyRegion => body.segments.find((segment) => segment.id === id)?.region ?? 'torso'\n","import { segmentOf } from '../../body/body-state'\nimport type { SegmentState } from '../../types'\nimport type { BodyOps } from '../body-ops'\nimport { HEADSHOT, NO_HEADSHOT, bleedWound, destroyWithBleeding, segmentDamage, severWithBleeding } from './blow'\nimport type { SegmentBlow, SegmentRule } from './blow'\n\n/** A blow of at least this many points blows the head off (Arsenal `damage`: `dmg >= 30`). */\nconst HEAD_BLOW_POINTS = 30\nconst LIMB_VITALITY_SHARE = 0.3\n\nconst split = (ops: BodyOps): void => {\n const torso = segmentOf(ops.body, 'torso')\n if (!torso.attached) return\n severWithBleeding(ops, torso)\n ops.change('split', 'torso')\n ops.kill('vitality')\n}\n\nconst loseLimb = (ops: BodyOps, segment: SegmentState, blow: SegmentBlow): void => {\n if (blow.big || -segment.hp > segment.max) destroyWithBleeding(ops, segment)\n else severWithBleeding(ops, segment)\n const { body } = ops\n if (segment.kind === 'thigh' || segment.kind === 'shin') {\n if (body.alive) ops.crawl()\n } else if (segment.kind === 'foot' && body.alive && !body.crawl && body.limping.length === 0 && segment.side) ops.limp(segment.side)\n}\n\n/** The Galeria 3D do Arsenal's `damage`: every number and branch of the page, in its order. */\nexport const arsenalRule: SegmentRule = (ops, blow) => {\n const { segment } = blow\n if (!segment.attached) return NO_HEADSHOT\n const amount = segmentDamage(ops, blow, blow.points)\n if (blow.point) bleedWound(ops, segment, blow.point)\n segment.hp -= amount\n ops.damage(segment, amount, blow.damageType)\n let result = NO_HEADSHOT\n if (segment.kind === 'head') {\n if (blow.points >= HEAD_BLOW_POINTS || segment.hp <= 0) {\n destroyWithBleeding(ops, segment)\n ops.kill('head')\n result = HEADSHOT\n } else ops.wound(segment)\n } else if (segment.kind === 'jaw') {\n if (segment.hp <= 0) severWithBleeding(ops, segment)\n } else if (segment.kind === 'neck') {\n if (segment.hp <= 0) {\n severWithBleeding(ops, segment)\n ops.kill('head')\n }\n } else if (segment.kind === 'torso' || segment.kind === 'pelvis') {\n ops.body.vitality -= amount\n if (blow.points >= ops.rules.severity.splitAtDamage || segment.hp <= 0) split(ops)\n } else {\n ops.body.vitality -= amount * LIMB_VITALITY_SHARE\n if (segment.hp <= 0) loseLimb(ops, segment, blow)\n }\n if (ops.body.alive && ops.body.vitality <= 0) ops.kill('vitality')\n return result\n}\n","import type { SegmentKind } from '@adatechnology/game-contracts'\n\nimport { spreadPoints } from '../points'\nimport { HEADSHOT, NO_HEADSHOT, isLeg, segmentDamage } from './blow'\nimport type { SegmentRule } from './blow'\n\n/** A head at 0 is cut off by an edge of at least this `cut` or a blow of at least this many points (Elenco `hit`). */\nconst HEAD_SEVER_CUT = 0.7\nconst HEAD_SEVER_POINTS = 90\n/** The segments that hang from a joint of their own in the Elenco (`s.node`): they are cut or break at it. */\nconst JOINTED: ReadonlySet<SegmentKind> = new Set(['head', 'upperArm', 'forearm', 'hand', 'thigh', 'shin', 'foot'])\n\n/** The Elenco's `hit`, `sever` and `breakBone`: one ±15 % draw per blow, cut at the joint above, broken bones dangle or make the leg limp. */\nexport const elencoRule: SegmentRule = (ops, blow) => {\n const { segment } = blow\n const { body, rules } = ops\n if (!segment.attached || (!body.alive && segment.kind !== 'head')) return NO_HEADSHOT\n const amount = segmentDamage(ops, blow, spreadPoints(blow.points, rules.spread, ops.random))\n segment.hp = Math.max(0, segment.hp - amount)\n ops.damage(segment, amount, blow.damageType)\n if (segment.hp > 0) {\n ops.wound(segment)\n return NO_HEADSHOT\n }\n if (segment.kind === 'head') {\n if (blow.cut >= HEAD_SEVER_CUT || blow.points >= HEAD_SEVER_POINTS) ops.sever(segment, 'zero')\n else ops.breakBone(segment)\n ops.kill('head')\n return HEADSHOT\n }\n if (JOINTED.has(segment.kind)) {\n if (blow.cut >= rules.severity.severAtCut || amount >= segment.max * rules.severity.severAtDamageFraction) {\n ops.sever(segment, 'zero')\n if (isLeg(segment.kind)) ops.crawl()\n return NO_HEADSHOT\n }\n ops.breakBone(segment)\n if (isLeg(segment.kind) && segment.side) {\n ops.limp(segment.side)\n if (body.limping.length === 2) ops.crawl()\n }\n return NO_HEADSHOT\n }\n ops.breakBone(segment)\n if (segment.kind === 'torso') ops.kill('vitality')\n else if (segment.kind === 'pelvis') ops.crawl()\n else if (segment.kind === 'neck') ops.kill('head')\n return NO_HEADSHOT\n}\n","import { segmentOf } from '../../body/body-state'\nimport type { SegmentState } from '../../types'\nimport type { BodyOps } from '../body-ops'\nimport { spreadPoints } from '../points'\nimport { HEADSHOT, NO_HEADSHOT, bleedWound, destroyWithBleeding, isLeg, segmentDamage, severWithBleeding } from './blow'\nimport type { RuleResult, SegmentBlow, SegmentRule } from './blow'\n\ntype Outcome = 'destroyed' | 'severed' | 'broken'\n\n/** At 0: destroyed by a blast or a blow of twice the segment; severed by an edge (`cut` ≥ 0.5) or a blow above 90 % of it; else broken. */\nexport const severityOf = (ops: BodyOps, blow: SegmentBlow, amount: number, max: number): Outcome => {\n const rule = ops.rules.severity\n if (blow.explosive || amount >= max * rule.destroyAtDamageMultiple) return 'destroyed'\n if (blow.cut >= rule.severAtCut || amount > max * rule.severAtDamageFraction) return 'severed'\n return 'broken'\n}\n\nconst lose = (ops: BodyOps, segment: SegmentState, outcome: Outcome): void => {\n if (outcome === 'destroyed') destroyWithBleeding(ops, segment)\n else if (outcome === 'severed') severWithBleeding(ops, segment)\n else ops.breakBone(segment)\n}\n\n/** The game's head rule: a projectile kills when it pierces the skull with enough points, a blow when it takes the head's hit points. */\nconst hitHead = (ops: BodyOps, blow: SegmentBlow, points: number, amount: number, hpBefore: number): RuleResult => {\n const { segment, projectile } = blow\n const rule = ops.rules.severity\n const lethal = projectile ? blow.model.piercesSkull(projectile, blow.headResistance) && points >= rule.headKillDamage : amount >= hpBefore\n if (lethal) {\n const shotgunClose = projectile !== undefined && projectile.pellets > 1 && projectile.distance < rule.headBitShotgunMetres\n if (blow.explosive || points > rule.headBitDamage || shotgunClose) destroyWithBleeding(ops, segment)\n else ops.breakBone(segment)\n ops.kill('head')\n return HEADSHOT\n }\n ops.body.vitality -= amount\n ops.wound(segment)\n if (points > rule.jawDamage && ops.random() < rule.jawChance) severWithBleeding(ops, segmentOf(ops.body, 'jaw'))\n return NO_HEADSHOT\n}\n\nconst hitTorso = (ops: BodyOps, points: number, amount: number, segment: SegmentState): void => {\n ops.body.vitality -= amount\n if (points < ops.rules.severity.splitAtDamage && segment.hp > 0) return ops.wound(segment)\n const torso = segmentOf(ops.body, 'torso')\n if (!torso.attached) return\n severWithBleeding(ops, torso)\n ops.change('split', 'torso')\n if (ops.rules.deathRule === 'head-only') ops.crawl()\n else ops.kill('vitality')\n}\n\nconst hitLimb = (ops: BodyOps, blow: SegmentBlow, amount: number, hpBefore: number): void => {\n const { segment } = blow\n const rule = ops.rules.severity\n ops.body.vitality -= amount * rule.limbVitalityShare\n if (isLeg(segment.kind) && segment.side && segment.hp <= segment.max * rule.limpAtHpFraction) ops.limp(segment.side)\n if (segment.hp > 0 || hpBefore <= 0) return ops.wound(segment)\n const outcome = severityOf(ops, blow, amount, segment.max)\n lose(ops, segment, outcome)\n if (segment.kind === 'thigh' || segment.kind === 'shin') ops.crawl()\n}\n\n/** The kit's rule: Arsenal numbers, the design's severity, the game's head rule, the death rule of the kit. */\nexport const kitRule: SegmentRule = (ops, blow) => {\n const { segment } = blow\n if (!segment.attached) return NO_HEADSHOT\n const points = spreadPoints(blow.points, ops.rules.spread, ops.random)\n const amount = segmentDamage(ops, blow, points)\n if (blow.point && !blow.explosive) bleedWound(ops, segment, blow.point)\n const hpBefore = segment.hp\n segment.hp -= amount\n ops.damage(segment, amount, blow.damageType)\n let result = NO_HEADSHOT\n if (segment.kind === 'head') result = hitHead(ops, blow, points, amount, hpBefore)\n else if (segment.kind === 'torso' || segment.kind === 'pelvis') hitTorso(ops, points, amount, segment)\n else if (segment.kind === 'jaw' || segment.kind === 'neck') {\n if (segment.hp <= 0 && hpBefore > 0) {\n lose(ops, segment, severityOf(ops, blow, amount, segment.max))\n if (segment.kind === 'neck' && !segment.attached) ops.kill('head')\n } else ops.wound(segment)\n } else hitLimb(ops, blow, amount, hpBefore)\n if (ops.body.vitality <= 0) ops.kill('vitality')\n if (ops.body.alive && points >= ops.rules.severity.stunAtPoints) ops.stun()\n return result\n}\n","import type { DestructibleSpec, Vec3 } from '@adatechnology/game-contracts'\n\nimport { segmentOf } from '../body/body-state'\nimport { REGION_SEGMENTS } from '../body/regions'\nimport { segmentNode } from '../body/segment-tree'\nimport type { SegmentId } from '../body/segment-tree'\nimport { regionBlast, segmentBlast } from './blast'\nimport type { BodyOps } from './body-ops'\nimport type { DamageSetup } from './damage-character'\nimport { energyKept, segmentResistance } from './penetration'\nimport { cutOf, pointsFromEnergy, projectileOf, resolvePoints } from './points'\nimport { layersOn, protect } from './protection'\nimport type { ProtectionOutcome } from './protection'\nimport { resolveSegment, regionOfSegment } from './resolve-part'\nimport { arsenalRule } from './rules/arsenal'\nimport type { RuleResult, SegmentRule } from './rules/blow'\nimport { elencoRule } from './rules/elenco'\nimport { kitRule } from './rules/kit'\nimport { regionRule } from './rules/regions'\nimport type { BodyHit, HitReport, ProtectionLayer } from './types'\n\nconst SEGMENT_RULES: Readonly<Record<string, SegmentRule>> = { kit: kitRule, arsenal: arsenalRule, elenco: elencoRule }\nconst HEAD_RESISTANCE = segmentResistance({ kind: 'head', bodyCount: 1, protection: 0 })\nconst NO_PROTECTION: ProtectionOutcome = { ballistic: 1, resistance: 0, limbs: 1, fire: 1 }\nconst ABSORBED_SPARKS = 8\nconst METRES_PER_MM = 0.001\n\nexport type ApplyBlowParams = {\n readonly ops: BodyOps\n readonly hit: BodyHit\n readonly setup: DamageSetup\n readonly worn: readonly ProtectionLayer[]\n readonly localPoint: Vec3\n readonly jointOf: (id: SegmentId) => Vec3\n readonly describe: () => DestructibleSpec\n}\n\nconst report = (ops: BodyOps, fields: Partial<HitReport> & { points: number }, result: RuleResult = { headshot: false }): HitReport => ({\n through: false,\n exitEnergy: 0,\n ...fields,\n killed: !ops.body.alive,\n headshot: result.headshot,\n})\n\n/** Fire and heat: the exposure burns the torso (`burnPointsPerSecond` × seconds), less what the clothing keeps off. */\nconst burn = ({ ops, hit, setup, worn }: ApplyBlowParams): HitReport => {\n const torso = segmentOf(ops.body, 'torso')\n const fire = protect({ layers: layersOn(worn, segmentNode('torso').slots, ops.body.consumed), model: setup.model }).fire\n const points = setup.rules.burnPointsPerSecond * (hit.duration ?? 0) * fire\n ops.body.vitality -= points\n ops.damage(torso, points, hit.type)\n if (ops.body.vitality <= 0) ops.kill('vitality')\n return report(ops, { part: 'torso', points })\n}\n\nconst blast = ({ ops, hit, setup, localPoint, jointOf }: ApplyBlowParams): HitReport => {\n const points = hit.points ?? pointsFromEnergy(hit.energy ?? 0, setup.rules)\n const base = { ops, center: localPoint, points, radius: hit.radius ?? 1, ...(hit.lethalRadius !== undefined ? { lethalRadius: hit.lethalRadius } : {}), jointOf }\n const common = { cut: hit.cut ?? setup.rules.defaultCut.explosive, damageType: hit.type, model: setup.model, headResistance: HEAD_RESISTANCE }\n const rule = SEGMENT_RULES[setup.rules.segmentRule] ?? kitRule\n const result =\n ops.body.detail === 'regions'\n ? regionBlast({ ...base, blow: { ...common, protection: NO_PROTECTION } })\n : segmentBlast({ ...base, rule, blow: { ...common, limbFactor: ops.spec.limbFactor } })\n return report(ops, { points }, result)\n}\n\nconst DEFAULT_PATH_METRES = 0.2\n\n/** Length of the path through a part (its diameter), for the exit point of a projectile that goes through. */\nconst pathThrough = (spec: DestructibleSpec, id: SegmentId): number => {\n const shape = spec.parts.find((part) => part.id === id)?.shape\n return shape && shape.kind !== 'box' ? 2 * shape.radius : DEFAULT_PATH_METRES\n}\n\nconst exitOf = (hit: BodyHit, depth: number): Vec3 => {\n const [x, y, z] = hit.direction ?? [0, 0, -1]\n return [hit.point[0] + x * depth, hit.point[1] + y * depth, hit.point[2] + z * depth]\n}\n\n/** Bullets, blades and blunt blows: resolve the segment, the protection, the penetration, the points; then the detail's rule decides. */\nconst direct = ({ ops, hit, setup, worn, localPoint, describe }: ApplyBlowParams): HitReport => {\n const { body, spec, rules } = ops\n const caliber = hit.caliberId === undefined ? undefined : setup.calibers.get(hit.caliberId)\n const projectile = hit.type === 'ballistic' ? projectileOf(hit, caliber) : undefined\n const id = resolveSegment(body, describe(), hit.part, localPoint)\n const region = regionOfSegment(body, id)\n const slots = segmentNode(body.detail === 'regions' ? (REGION_SEGMENTS[region][0] as SegmentId) : id).slots\n const protection = protect({ layers: layersOn(worn, slots, body.consumed), model: setup.model, ...(projectile ? { projectile } : {}) })\n if (protection.absorbedBy) {\n body.consumed.add(protection.absorbedBy)\n ops.emit({ type: 'debris', part: id, kind: 'sparks', point: hit.point, normal: [0, 1, 0], count: ABSORBED_SPARKS, speed: 3 })\n return report(ops, { part: id, points: 0, absorbedBy: protection.absorbedBy })\n }\n const rawPoints = resolvePoints(hit, caliber, rules)\n const points = rawPoints * protection.ballistic\n const cut = cutOf(hit, projectile, rules)\n const segment = segmentOf(body, id)\n const resistance = segmentResistance({ kind: segment.kind, bodyCount: spec.bodyCount, protection: protection.resistance })\n const through = setup.model.id === 'physical' && projectile !== undefined && projectile.force > resistance\n const exitEnergy = through && projectile ? (hit.energy ?? 0) * energyKept(projectile.force, resistance) : 0\n if (projectile) {\n const depth = pathThrough(describe(), id)\n ops.emit({ type: 'hole', part: id, hole: { point: localPoint, normal: [0, 0, 1], diameter: (hit.caliber ?? 9) * METRES_PER_MM, through }, worldPoint: hit.point, ...(through ? { exitPoint: exitOf(hit, depth) } : {}) })\n }\n const common = { points, cut, damageType: hit.type, model: setup.model, headResistance: HEAD_RESISTANCE, ...(projectile ? { projectile } : {}) }\n const result =\n body.detail === 'regions'\n ? regionRule(ops, { ...common, points: rawPoints, region, protection })\n : (SEGMENT_RULES[rules.segmentRule] ?? kitRule)(ops, {\n ...common,\n segment,\n explosive: false,\n big: points > rules.blast.bigPoints,\n limbFactor: spec.limbFactor * protection.limbs,\n ...(hit.woundAtPoint === false ? {} : { point: hit.point }),\n })\n return report(ops, { part: id, points, through, exitEnergy }, result)\n}\n\nexport const applyBlow = (params: ApplyBlowParams): HitReport => {\n if (params.hit.type === 'fire' || params.hit.type === 'heat') return burn(params)\n if (params.hit.type === 'explosive') return blast(params)\n return direct(params)\n}\n","import type { DestructibleImpl, DestructionEvent, Hit, Vec3 } from '@adatechnology/game-contracts'\n\nimport type { BodyState } from '../body/body-state'\nimport { segmentNode } from '../body/segment-tree'\nimport type { SegmentId, SegmentSide } from '../body/segment-tree'\nimport { toLocal, toWorld } from '../body/transform'\nimport { characterView } from '../character'\nimport type { Character, CharacterCore } from '../character'\nimport { stepBleeding } from './bleeding'\nimport type { Bleeder } from './bleeding'\nimport { createBodyOps } from './body-ops'\nimport type { BodyOps } from './body-ops'\nimport { segmentResistance } from './penetration'\nimport { applyBlow } from './pipeline'\nimport { createRng } from './points'\nimport { layeredProtection, layersOn } from './protection'\nimport { resolveSegment } from './resolve-part'\nimport type { BodyHit, CaliberEntry, DamageRules, HitReport, PenetrationModel, ProtectionLayer } from './types'\n\n/** A character with the damage model: a `DestructibleImpl` a destruction world can host next to its own objects. */\nexport type DamageCharacter = Character &\n DestructibleImpl & {\n readonly alive: boolean\n /** The sides that limp, in the order they started. */\n readonly limping: readonly SegmentSide[]\n /** Seconds of stagger left from a heavy hit. */\n readonly stunSeconds: number\n /** What the last `hit` did (points, kill, headshot, through). */\n readonly lastHit?: HitReport\n /** The bleeding wounds, oldest first. */\n bleeders(): readonly Readonly<Bleeder>[]\n /** 0..1 protection of the layers over a segment against a bite or a scratch (the layered T3 formula, capped at 90 %). */\n protectionAgainst(attack: 'bite' | 'scratch', part: SegmentId): number\n /** Ids of the protection layers worn and not used up (a helmet that took its shot is gone). */\n intactProtection(): readonly string[]\n /** Penetration resistance R of the path through a part (a segment or a region id), with the protection worn over it. */\n resistanceOf(part: string): number\n hit(hit: Hit | BodyHit): DestructionEvent[]\n /** Ages bleeders (a `leak` per second each) and the stun. */\n update(dt: number): DestructionEvent[]\n serialize(): unknown\n load(data: unknown): void\n }\n\nexport type DamageSetup = {\n readonly rules: DamageRules\n readonly model: PenetrationModel\n readonly protection: ReadonlyMap<string, ProtectionLayer>\n readonly calibers: ReadonlyMap<string, CaliberEntry>\n}\n\ntype Saved = { body: Omit<BodyState, 'consumed'> & { consumed: string[] }; bleeders: Bleeder[]; hitCounter: number; clock: number }\n\nexport const createDamageCharacter = (core: CharacterCore, setup: DamageSetup): DamageCharacter => {\n const view = characterView(core)\n const worn = core.spec.protection.map((id) => setup.protection.get(id)).filter((layer): layer is ProtectionLayer => layer !== undefined)\n let bleeders: Bleeder[] = []\n let hitCounter = 0\n let clock = 0\n let lastHit: HitReport | undefined\n const transform = () => ({ position: core.position, rotation: core.rotation })\n const jointOf = (id: SegmentId): Vec3 => core.rig.joints[segmentNode(id).joint]\n const pointOf = (id: SegmentId): Vec3 => toWorld(transform(), jointOf(id))\n const opsFor = (random: () => number, source?: string): BodyOps =>\n createBodyOps({ body: core.body, spec: core.spec, rules: setup.rules, random, time: clock, pointOf, bleeders, ...(source !== undefined ? { source } : {}) })\n\n const character: DamageCharacter = {\n ...view,\n get segments() {\n return core.body.segments\n },\n get detail() {\n return core.body.detail\n },\n get alive() {\n return core.body.alive\n },\n get limping() {\n return [...core.body.limping]\n },\n get stunSeconds() {\n return core.body.stunSeconds\n },\n get lastHit() {\n return lastHit\n },\n name: core.spec.kind,\n hit(raw) {\n const hit = raw as BodyHit\n hitCounter += 1\n const random = core.spec.random ?? createRng((core.spec.seed ^ hitCounter) >>> 0)\n const ops = opsFor(random, hit.source)\n core.invalidateState()\n lastHit = applyBlow({ ops, hit, setup, worn, localPoint: toLocal(transform(), hit.point), jointOf, describe: core.describe })\n core.invalidateState()\n return ops.events\n },\n update(dt) {\n clock += dt\n core.body.stunSeconds = Math.max(0, core.body.stunSeconds - dt)\n const ops = opsFor(() => 0)\n stepBleeding({ bleeders, dt, emit: ops.emit, pointOf })\n return ops.events\n },\n bleeders: () => bleeders.map((bleeder) => ({ ...bleeder })),\n intactProtection: () => core.spec.protection.filter((id) => !core.body.consumed.has(id)),\n resistanceOf(part) {\n const id = resolveSegment(core.body, core.describe(), part, [0, 0, 0])\n const layers = layersOn(worn, segmentNode(id).slots, core.body.consumed)\n const protection = layers.reduce((sum, layer) => sum + (layer.ballistic?.resistance ?? 0), 0)\n return segmentResistance({ kind: segmentNode(id).kind, bodyCount: core.spec.bodyCount, protection })\n },\n protectionAgainst: (attack, part) => layeredProtection({ layers: layersOn(worn, segmentNode(part).slots, core.body.consumed), attack }),\n reset() {\n core.resetBody()\n bleeders = []\n hitCounter = 0\n lastHit = undefined\n },\n serialize(): Saved {\n const { consumed, ...rest } = core.body\n return { body: { ...structuredClone(rest), consumed: [...consumed] }, bleeders: structuredClone(bleeders), hitCounter, clock }\n },\n load(data) {\n const saved = data as Saved\n Object.assign(core.body, structuredClone(saved.body), { consumed: new Set(saved.body.consumed) })\n bleeders = structuredClone(saved.bleeders)\n hitCounter = saved.hitCounter\n clock = saved.clock\n core.invalidateState()\n },\n }\n return character\n}\n","import type { DestructionEvent, Vec3 } from '@adatechnology/game-contracts'\n\nimport { isBodyRegion } from '../body/regions'\nimport { isSegmentId, segmentNode } from '../body/segment-tree'\nimport type { DamageCharacter } from './damage-character'\nimport { TEAM_GAME_RULES } from './defaults'\nimport { energyKept, teamGameCost, teamGameFalloff } from './penetration'\nimport { penetrationForce } from './points'\nimport type { CaliberEntry } from './types'\n\n/** One body in the line of fire, `distance` metres from the muzzle. */\nexport type LineTarget = { readonly character: DamageCharacter; readonly part: string; readonly distance: number; readonly point?: Vec3 }\n\n/** What the line needs from a shot (a `Shot` of game-weapons fits). */\nexport type LineShot = {\n readonly energyAt: (distance: number) => number\n /** Projectile grams. */\n readonly mass: number\n /** Diameter in mm. */\n readonly caliber: number\n readonly caliberId?: string\n readonly source?: string\n}\n\nexport type LineHit = { readonly index: number; readonly points: number; readonly energy: number; readonly killed: boolean; readonly headshot: boolean; readonly events: DestructionEvent[] }\nexport type LineResult = { readonly hits: LineHit[]; /** 1-based index of the last body hit (0: none). */ readonly stoppedAt: number }\n\nexport type PenetrateLineParams = {\n readonly model: 'physical' | 'team-game'\n readonly shot: LineShot\n readonly targets: readonly LineTarget[]\n readonly calibers: ReadonlyMap<string, CaliberEntry>\n}\n\n/** After a kill the game restarts the ray 0.3 m past the body and measures the next distances from there (`shootRay`). */\nconst RESTART_METRES = 0.3\n\nconst regionOfPart = (part: string) => (isSegmentId(part) ? segmentNode(part).region : isBodyRegion(part) ? part : 'torso')\n\nconst lineHit = (index: number, target: LineTarget, fields: { points: number; energy: number; events: DestructionEvent[] }): LineHit => ({\n index,\n ...fields,\n killed: !target.character.alive,\n headshot: target.character.lastHit?.headshot ?? false,\n})\n\n/** The game's `shootRay`: penetration points spent per body (head 0.7, torso 1, a brute 2, limb 0.5), 75 % of the damage carried on. */\nconst teamGameLine = ({ shot, targets, calibers }: PenetrateLineParams): LineResult => {\n const caliber = shot.caliberId === undefined ? undefined : calibers.get(shot.caliberId)\n if (caliber?.penetration === undefined || caliber.points === undefined) throw new Error(`game-characters: the team-game line needs a calibre with points and penetration (\"${shot.caliberId}\")`)\n let penetration = caliber.penetration\n let points = caliber.points\n let origin = 0\n const hits: LineHit[] = []\n for (const [index, target] of targets.entries()) {\n if (!target.character.alive || target.distance - origin <= 0) continue\n const distance = target.distance - origin\n const dealt = points * teamGameFalloff(distance, caliber.effectiveRangeMetres ?? Infinity)\n const events = target.character.hit({ type: 'ballistic', point: target.point ?? [0, 0, 0], part: target.part, points: dealt, caliberId: caliber.id, distance, ...(shot.source ? { source: shot.source } : {}) })\n hits.push(lineHit(index, target, { points: dealt, energy: shot.energyAt(target.distance), events }))\n penetration -= teamGameCost(regionOfPart(target.part), target.character.spec.bodyCount)\n points *= TEAM_GAME_RULES.damageKeptPerBody\n if (penetration <= 0) break\n if (!target.character.alive) origin = target.distance + RESTART_METRES\n }\n return { hits, stoppedAt: hits.length === 0 ? 0 : (hits[hits.length - 1]?.index ?? -1) + 1 }\n}\n\n/** P7: the force P = √(E·m/A) hits a body while it is above 0, loses the body's resistance R and keeps P/(P+R) of the energy. */\nconst physicalLine = ({ shot, targets }: PenetrateLineParams): LineResult => {\n const first = targets[0]\n let force = first ? penetrationForce(shot.energyAt(first.distance), shot.mass, shot.caliber) : 0\n let kept = 1\n const hits: LineHit[] = []\n for (const [index, target] of targets.entries()) {\n if (force <= 0) break\n if (!target.character.alive) continue\n const energy = shot.energyAt(target.distance) * kept\n const events = target.character.hit({ type: 'ballistic', point: target.point ?? [0, 0, 0], part: target.part, energy, mass: shot.mass, caliber: shot.caliber, distance: target.distance, ...(shot.caliberId ? { caliberId: shot.caliberId } : {}) })\n hits.push(lineHit(index, target, { points: target.character.lastHit?.points ?? 0, energy, events }))\n const resistance = target.character.resistanceOf(target.part)\n kept *= energyKept(force, resistance)\n force -= resistance\n }\n return { hits, stoppedAt: hits.length === 0 ? 0 : (hits[hits.length - 1]?.index ?? -1) + 1 }\n}\n\n/** A shot through a line of bodies, nearest first. Each body takes the hit through its own damage model. */\nexport const penetrateLine = (params: PenetrateLineParams): LineResult => (params.model === 'team-game' ? teamGameLine(params) : physicalLine(params))\n","type Entry = { readonly id: string }\n\n/** The shipped entries with the ones from `extra` added or, when the id exists, replaced. */\nexport const mergeById = <TEntry extends Entry>(shipped: readonly TEntry[], extra: readonly TEntry[] | undefined): Map<string, TEntry> => {\n const entries = new Map<string, TEntry>()\n for (const entry of [...shipped, ...(extra ?? [])]) entries.set(entry.id, entry)\n return entries\n}\n\n/** Applies `patch` by id (a shallow merge); a patch for an id that does not exist is ignored. */\nexport const applyPatches = <TEntry extends Entry>(entries: Map<string, TEntry>, patches: Readonly<Record<string, Partial<TEntry>>> | undefined): void => {\n for (const [id, patch] of Object.entries(patches ?? {})) {\n const entry = entries.get(id)\n if (entry) entries.set(id, { ...entry, ...patch, id })\n }\n}\n\nexport const removeIds = (entries: Map<string, unknown>, ids: readonly string[] | undefined): void => {\n for (const id of ids ?? []) entries.delete(id)\n}\n","import { createBodySpec } from './body/body-spec'\nimport { createCorpseLedger } from './corpses'\nimport type { CorpseLedger } from './corpses'\nimport type { BodySpec } from './body/body-spec'\nimport { createCharacterCore, resolveSpec } from './character'\nimport { createDamageCharacter } from './damage/damage-character'\nimport type { DamageCharacter } from './damage/damage-character'\nimport { DAMAGE_PRESETS, PROTECTION_PRESETS, TEAM_GAME_CALIBERS } from './damage/defaults'\nimport { penetrateLine } from './damage/line'\nimport type { LineResult, LineShot, LineTarget } from './damage/line'\nimport { PENETRATION_MODELS } from './damage/penetration'\nimport type { CaliberEntry, DamageRules, PenetrationModel, ProtectionLayer } from './damage/types'\nimport { applyPatches, mergeById, removeIds } from './registry'\nimport type { CharacterPreset, CharacterSpec, CharactersOptions, CorpseOptions, DamageOptions } from './types'\n\nexport const DEFAULT_CORPSES: CorpseOptions = { max: 400, seconds: 900 }\n\n/** The base preset every spec can start from: an unnamed adult person. */\nexport const PERSON_PRESET: CharacterPreset = { id: 'person', kind: 'person', age: 'adult' }\n\nexport type CharacterKit = {\n readonly body: BodySpec\n readonly presets: ReadonlyMap<string, CharacterPreset>\n readonly corpses: CorpseOptions\n readonly damage: DamageRules\n readonly penetration: PenetrationModel\n readonly protection: ReadonlyMap<string, ProtectionLayer>\n readonly calibers: ReadonlyMap<string, CaliberEntry>\n /** A new character: the preset (`spec.preset`, else the one named `spec.kind`) under the spec. */\n create(spec: CharacterSpec): DamageCharacter\n /** The bodies left on the ground, capped and timed by the kit's `corpses` option. */\n createCorpses<TData>(): CorpseLedger<TData>\n /** A shot through a line of bodies with the kit's penetration model and calibre table. */\n penetrateLine(shot: LineShot, targets: readonly LineTarget[]): LineResult\n}\n\n/** Fills a spec from its preset: what the spec says wins; `multipliers` merge field by field. */\nexport const applyPreset = (spec: CharacterSpec, presets: ReadonlyMap<string, CharacterPreset>): CharacterSpec => {\n const preset = presets.get(spec.preset ?? spec.kind)\n if (!preset) return spec\n const { id: _presetId, ...defaults } = preset\n const multipliers = { ...defaults.multipliers, ...spec.multipliers }\n return { ...defaults, ...spec, multipliers }\n}\n\n/** The rules of a preset (`kit` by default) with the given fields replaced; `severity`, `blast` and `segments` merge key by key. */\nexport const resolveDamageRules = (options: DamageOptions = {}): DamageRules => {\n const { preset, severity, blast, segments, ...rest } = options\n const base = DAMAGE_PRESETS[preset ?? 'kit']\n return { ...base, ...rest, severity: { ...base.severity, ...severity }, blast: { ...base.blast, ...blast }, segments: { ...base.segments, ...segments } }\n}\n\nconst modelOf = (option: CharactersOptions['penetration']): PenetrationModel => (typeof option === 'object' ? option : PENETRATION_MODELS[option ?? 'physical'])\n\nexport const createCharacters = (options: CharactersOptions = {}): CharacterKit => {\n const body = createBodySpec(options.body)\n const packs = options.packs ?? []\n const presets = mergeById([PERSON_PRESET], packs.flatMap((pack) => pack.presets ?? []))\n applyPatches(presets, options.patch)\n removeIds(presets, options.remove)\n const damage = resolveDamageRules(options.damage)\n const penetration = modelOf(options.penetration)\n const protection = mergeById(PROTECTION_PRESETS, [...packs.flatMap((pack) => pack.protection ?? []), ...(options.protection ?? [])])\n const shippedCalibers = penetration.id === 'team-game' ? TEAM_GAME_CALIBERS : []\n const calibers = mergeById(shippedCalibers, [...packs.flatMap((pack) => pack.calibers ?? []), ...(options.calibers ?? [])])\n const setup = { rules: damage, model: penetration, protection, calibers }\n let counter = 0\n return {\n body,\n presets,\n corpses: { ...DEFAULT_CORPSES, ...options.corpses },\n damage,\n penetration,\n protection,\n calibers,\n create(spec) {\n counter += 1\n const resolved = resolveSpec(applyPreset(spec, presets), body, counter)\n const core = createCharacterCore({ spec: resolved, bodySpec: body, hitPointsOf: (kind) => damage.segments[kind].hitPoints })\n return createDamageCharacter(core, setup)\n },\n createCorpses: () => createCorpseLedger({ ...DEFAULT_CORPSES, ...options.corpses }),\n penetrateLine: (shot, targets) => penetrateLine({ model: penetration.id === 'team-game' ? 'team-game' : 'physical', shot, targets, calibers }),\n }\n}\n","{\n \"cascade\": {\n \"hip\": 16,\n \"spine\": 11,\n \"shoulder\": 9,\n \"elbow\": 7,\n \"leg\": 14,\n \"knee\": 11,\n \"head\": 6\n },\n \"moves\": {\n \"idle\": {\n \"gait\": 0,\n \"freq\": 0,\n \"amp\": 0,\n \"knee\": 0,\n \"bob\": 0,\n \"lean\": 0,\n \"arm\": 0,\n \"elb\": 0.12,\n \"crouch\": 0,\n \"aim\": 0,\n \"turn\": 0,\n \"grip\": 0.35\n },\n \"walk\": {\n \"gait\": 1,\n \"freq\": 5.6,\n \"amp\": 0.42,\n \"knee\": 0.85,\n \"bob\": 0.035,\n \"lean\": 0.05,\n \"arm\": 0.42,\n \"elb\": 0.3,\n \"crouch\": 0,\n \"aim\": 0,\n \"turn\": 0,\n \"grip\": 0.4\n },\n \"run\": {\n \"gait\": 1,\n \"freq\": 9.2,\n \"amp\": 0.72,\n \"knee\": 1.5,\n \"bob\": 0.06,\n \"lean\": 0.3,\n \"arm\": 0.85,\n \"elb\": 1.35,\n \"crouch\": 0,\n \"aim\": 0,\n \"turn\": 0,\n \"grip\": 1.1\n },\n \"crouch\": {\n \"gait\": 1,\n \"freq\": 3.8,\n \"amp\": 0.3,\n \"knee\": 0.6,\n \"bob\": 0.02,\n \"lean\": 0.42,\n \"arm\": 0.15,\n \"elb\": 0.7,\n \"crouch\": 1,\n \"aim\": 0,\n \"turn\": 0,\n \"grip\": 0.8\n },\n \"aim\": {\n \"gait\": 0,\n \"freq\": 0,\n \"amp\": 0,\n \"knee\": 0,\n \"bob\": 0,\n \"lean\": 0.06,\n \"arm\": 0,\n \"elb\": 0.2,\n \"crouch\": 0.25,\n \"aim\": 1,\n \"turn\": 0,\n \"grip\": 1.2\n },\n \"turn\": {\n \"gait\": 0.5,\n \"freq\": 4.2,\n \"amp\": 0.14,\n \"knee\": 0.7,\n \"bob\": 0.012,\n \"lean\": 0.03,\n \"arm\": 0.12,\n \"elb\": 0.25,\n \"crouch\": 0,\n \"aim\": 0,\n \"turn\": 1,\n \"grip\": 0.4\n }\n },\n \"poses\": [\n {\n \"index\": 0,\n \"id\": \"walk\",\n \"name\": {\n \"pt-BR\": \"anda\"\n }\n },\n {\n \"index\": 1,\n \"id\": \"crawl\",\n \"name\": {\n \"pt-BR\": \"rasteja\"\n }\n },\n {\n \"index\": 2,\n \"id\": \"fallen\",\n \"name\": {\n \"pt-BR\": \"caído\"\n }\n },\n {\n \"index\": 3,\n \"id\": \"attack\",\n \"name\": {\n \"pt-BR\": \"ataca\"\n }\n },\n {\n \"index\": 4,\n \"id\": \"scream\",\n \"name\": {\n \"pt-BR\": \"grito\"\n }\n },\n {\n \"index\": 5,\n \"id\": \"crouch\",\n \"name\": {\n \"pt-BR\": \"agachado\"\n }\n },\n {\n \"index\": 6,\n \"id\": \"leap\",\n \"name\": {\n \"pt-BR\": \"salto\"\n }\n },\n {\n \"index\": 7,\n \"id\": \"eat\",\n \"name\": {\n \"pt-BR\": \"comendo\"\n }\n }\n ],\n \"walk\": {\n \"cadenceBase\": 0.5,\n \"cadencePerSpeed\": 0.3,\n \"stopBelowSpeed\": 0.05,\n \"maxSine\": 0.9,\n \"strideFactor\": 4,\n \"legLengthFactor\": 0.9\n }\n}\n","import gait from '../data/gait.json'\n\nconst WALK = gait.walk\n\n/** Cycles per second at `speed` m/s (the game's `walkCad`). */\nexport const walkCadence = (speed: number): number => WALK.cadenceBase + WALK.cadencePerSpeed * speed\n\n/** Hip swing in radians at `speed` m/s for a body `height` times the base height: the stride that makes the foot meet the ground (the game's `walkGait`). */\nexport const walkGait = (speed: number, height = 1): number =>\n speed < WALK.stopBelowSpeed ? 0 : 2 * Math.asin(Math.min(WALK.maxSine, speed / (WALK.strideFactor * WALK.legLengthFactor * height * walkCadence(speed))))\n\nexport type StrideParams = {\n readonly speed: number\n readonly height?: number\n /** Default true: cadence and swing come from the speed, so the feet do not slide. False keeps the profile's own numbers (the Bestiário preset). */\n readonly strideFromSpeed?: boolean\n}\n\nexport type Stride = {\n /** Cycles per second. */\n readonly frequency: number\n /** Thigh swing, radians. */\n readonly swing: number\n /** Knee bend, radians. */\n readonly kneeBend: number\n}\n\nconst SWING_SHARE = 0.5\nconst KNEE_PER_GAIT = 0.85\n\n/** The gait numbers for a speed: from the ground-matching stride, or `fallback` (the profile) when `strideFromSpeed` is false. */\nexport const strideFor = ({ speed, height = 1, strideFromSpeed = true }: StrideParams, fallback: Stride): Stride => {\n if (!strideFromSpeed) return fallback\n const amplitude = walkGait(speed, height)\n return { frequency: walkCadence(speed), swing: amplitude * SWING_SHARE, kneeBend: amplitude * KNEE_PER_GAIT }\n}\n","import gait from '../data/gait.json'\n\n/** How a body type walks: offsets over its rest pose (the Bestiário's `GAIT`). `f` cycles per second, `a` thigh swing, `k` knee bend, `arm` arm swing. */\nexport type GaitProfile = {\n readonly f: number\n readonly a?: number\n readonly k?: number\n readonly arm?: number\n readonly bob?: number\n readonly sway?: number\n readonly drag?: boolean\n readonly crawl?: boolean\n readonly idle?: boolean\n readonly eat?: boolean\n readonly leap?: boolean\n readonly listen?: boolean\n readonly stalk?: boolean\n readonly sniff?: boolean\n}\n\nexport type JointAngles = { readonly shoulder: number; readonly elbow: number }\nexport type LegAngles = { readonly hip: number; readonly knee: number; readonly lost?: boolean }\n\n/**\n * The pose the gait swings around. `arms[0]` is the right arm and `arms[1]` the left; `legs[0]` is the left leg and `legs[1]` the right\n * (the order the Bestiário builds them in). `hipY` is the rest height of the hips.\n */\nexport type RestPose = {\n readonly hipY: number\n readonly lean: number\n readonly headX: number\n readonly arms: readonly [JointAngles, JointAngles]\n readonly legs: readonly [LegAngles, LegAngles]\n}\n\n/** What a gait sets this frame; a field the gait does not write stays `undefined` and the joint keeps what it had. */\nexport type GaitSample = {\n hipY?: number\n upper: { x?: number; y?: number; z?: number }\n head: { x?: number; y?: number }\n arms: { shoulder?: number; elbow?: number }[]\n legs: { hip?: number; knee?: number }[]\n}\n\nexport const emptySample = (): GaitSample => ({ upper: {}, head: {}, arms: [{}, {}], legs: [{}, {}] })\n\nexport type CascadeJoint = keyof typeof gait.cascade\n\n/** Cascade rates per joint (1/s): each joint reaches its target with a delay, from the hips to the head (Bestiário 12.3). */\nexport const CASCADE: Readonly<Record<CascadeJoint, number>> = gait.cascade\n\n/** The 8 poses of the instanced body, in the order of the game's `iB.w`. */\nexport const INSTANCE_POSES = ['walk', 'crawl', 'fallen', 'attack', 'scream', 'crouch', 'leap', 'eat'] as const\nexport type InstancePose = (typeof INSTANCE_POSES)[number]\n\nconst MIN_HEIGHT = -2\nconst MAX_HEIGHT = 8\nconst HEIGHT_STEPS = 1000\nconst POSE_SLOTS = 16\n\n/** `iB.w` of the instanced body: the pose plus 16 times the ground height in millimetres (a pile of bodies, a jump). */\nexport const encodePoseHeight = (pose: InstancePose, height = 0): number =>\n INSTANCE_POSES.indexOf(pose) + POSE_SLOTS * Math.round((Math.min(MAX_HEIGHT, Math.max(MIN_HEIGHT, height)) - MIN_HEIGHT) * HEIGHT_STEPS)\n\nexport const decodePoseHeight = (encoded: number): { readonly pose: InstancePose; readonly height: number } => {\n const height = Math.floor(encoded / POSE_SLOTS) / HEIGHT_STEPS + MIN_HEIGHT\n return { pose: INSTANCE_POSES[encoded - Math.floor(encoded / POSE_SLOTS) * POSE_SLOTS] as InstancePose, height }\n}\n","import { emptySample } from './profile'\nimport type { GaitProfile, GaitSample, RestPose } from './profile'\nimport { strideFor } from './stride'\nimport type { StrideParams } from './stride'\n\nconst TWO_PI = Math.PI * 2\nconst LEAP_AIR_SHARE = 0.38\nconst LEAP_PHASE_DIVISOR = 6.3\nconst DEFAULT_PROFILE: GaitProfile = { f: 1.1, a: 0.28, k: 0.35, drag: true, arm: 0.08 }\n\nexport type AnimateProfileParams = {\n readonly profile?: GaitProfile\n readonly rest: RestPose\n /** Seconds. */\n readonly time: number\n /** Per-body phase offset, radians (the page's `rig.ph`). */\n readonly phase: number\n /** With `strideFromSpeed`, the speed and height that set cadence and swing. Without it the profile's own numbers are used. */\n readonly stride?: StrideParams\n}\n\n/** The leap, as a cycle: crouched, then airborne for 38% of it. */\nconst leap = ({ rest, time, phase, profile }: Required<Pick<AnimateProfileParams, 'rest' | 'time' | 'phase'>> & { profile: GaitProfile }): GaitSample => {\n const sample = emptySample()\n const cycle = (time * profile.f + phase / LEAP_PHASE_DIVISOR) % 1\n const air = cycle < LEAP_AIR_SHARE ? Math.sin((cycle / LEAP_AIR_SHARE) * Math.PI) : 0\n sample.hipY = rest.hipY + air * 0.95\n sample.upper.x = rest.lean - air * 0.45\n rest.legs.forEach((leg, index) => (sample.legs[index] = { hip: leg.hip * (1 - air * 0.7), knee: leg.knee * (1 - air * 0.75) }))\n rest.arms.forEach((arm, index) => (sample.arms[index] = { shoulder: arm.shoulder - air * 2.3, elbow: arm.elbow * (1 - air) }))\n return sample\n}\n\n/** Joint targets of one gait at `time` (the Bestiário's `animate0`), as numbers; the rig applies the ones that are set. */\nexport const animateProfile = ({ profile = DEFAULT_PROFILE, rest, time, phase, stride }: AnimateProfileParams): GaitSample => {\n const own = { frequency: profile.f, swing: profile.a ?? 0, kneeBend: profile.k ?? 0 }\n const { frequency, swing, kneeBend } = stride ? strideFor(stride, own) : own\n const angle = time * frequency * TWO_PI + phase\n const sine = Math.sin(angle)\n const cosine = Math.cos(angle)\n const sample = emptySample()\n if (profile.leap) return leap({ rest, time, phase, profile })\n if (profile.listen) {\n sample.head.y = Math.sin(time * 0.7 + phase) * 0.6\n rest.arms.forEach((arm, index) => (sample.arms[index] = { shoulder: arm.shoulder + Math.sin(time * 0.9 + index) * 0.08 }))\n return sample\n }\n if (profile.eat) {\n const wave = Math.sin(time * profile.f * TWO_PI + phase)\n sample.head.x = rest.headX + Math.max(0, wave) * 0.35\n sample.upper.x = rest.lean + Math.max(0, wave) * 0.08\n rest.arms.forEach((arm, index) => (sample.arms[index] = { elbow: arm.elbow + wave * 0.2 }))\n return sample\n }\n if (profile.crawl) {\n rest.arms.forEach((arm, index) => {\n const wave = index ? sine : -sine\n sample.arms[index] = { shoulder: arm.shoulder + wave * 0.35, elbow: arm.elbow - Math.max(0, wave) * 0.6 }\n })\n sample.upper.z = sine * 0.06\n return sample\n }\n if (profile.idle) {\n const scare = Math.max(0, Math.sin(time * 0.9 + phase))\n sample.upper.x = rest.lean - scare * 0.18\n sample.head.x = rest.headX - scare * 0.25\n rest.arms.forEach((arm, index) => (sample.arms[index] = { shoulder: arm.shoulder + scare * 0.25 }))\n return sample\n }\n rest.legs.forEach((leg, index) => {\n if (leg.lost) return\n const wave = index ? -sine : sine\n if (profile.drag && index === 1) return void (sample.legs[index] = { hip: leg.hip + wave * swing * 0.35, knee: leg.knee })\n sample.legs[index] = { hip: leg.hip + wave * swing, knee: leg.knee + Math.max(0, index ? cosine : -cosine) * kneeBend }\n })\n rest.arms.forEach((arm, index) => (sample.arms[index] = { shoulder: arm.shoulder + (index ? sine : -sine) * (profile.arm || 0.1) }))\n sample.hipY = rest.hipY - Math.abs(cosine) * (profile.bob || 0.02)\n sample.upper.y = sine * 0.06\n sample.upper.z = (profile.sway || 0) * sine + (profile.drag ? 0.04 : 0)\n if (profile.stalk) sample.upper.x = rest.lean + sine * 0.04\n if (profile.sniff) sample.head.y = Math.sin(time * 3.1 + phase) * 0.35\n return sample\n}\n","import { CASCADE } from './profile'\nimport type { CascadeJoint } from './profile'\n\n/** One joint moving toward its target: it covers `dt · rate` of the distance each step (never more than all of it). */\nexport const stepCascade = (current: number, target: number, dt: number, joint: CascadeJoint, rates: Readonly<Record<CascadeJoint, number>> = CASCADE): number =>\n current + (target - current) * Math.min(1, dt * rates[joint])\n\n/** Between two poses by joint name: a joint `from` does not name starts at the target (the Bestiário's `lerpPose`). */\nexport const lerpPose = <TPose extends Readonly<Record<string, number>>>(from: Partial<TPose>, to: TPose, amount: number): TPose => {\n const result: Record<string, number> = {}\n for (const name of Object.keys(to)) {\n const start = from[name]\n const end = to[name] as number\n result[name] = start === undefined ? end : (start as number) + (end - (start as number)) * amount\n }\n return result as TPose\n}\n","import gait from '../data/gait.json'\n\nexport type MoveId = keyof typeof gait.moves\nexport type MoveBlend = { gait: number; freq: number; amp: number; knee: number; bob: number; lean: number; arm: number; elb: number; crouch: number; aim: number; turn: number; grip: number }\n\n/** The human movement states the Elenco blends every frame. */\nexport const MOVES: Readonly<Record<MoveId, Readonly<MoveBlend>>> = gait.moves\n\nexport const MOVE_BLEND_RATE = 4\n\nexport const moveBlendOf = (move: MoveId): MoveBlend => ({ ...MOVES[move] })\n\n/** Moves `current` toward the target move: each number covers `dt · 4` of the distance (never past it). Mutates and returns `current`. */\nexport const blendMoves = (current: MoveBlend, target: MoveId | Readonly<MoveBlend>, dt: number): MoveBlend => {\n const goal = typeof target === 'string' ? MOVES[target] : target\n const amount = Math.min(1, dt * MOVE_BLEND_RATE)\n for (const key of Object.keys(goal) as (keyof MoveBlend)[]) current[key] += (goal[key] - current[key]) * amount\n return current\n}\n\nexport type HumanArm = { readonly side: 1 | -1; readonly hold?: boolean }\n\nexport type HumanPoseParams = {\n readonly moves: Readonly<MoveBlend>\n /** Seconds since the start. */\n readonly time: number\n /** The character's own phase offset. */\n readonly phase: number\n /** `phase + offset`: the walking cycle's angle, advanced by `dt · freq`. */\n readonly cycle: number\n /** The character's resting hunch plus the stage's. */\n readonly hunch: number\n /** The infection stage's arm raise (0 healthy .. 1 reanimated). */\n readonly armRaise: number\n /** Stage index: 3 and up adds a spine tremor, 4 is the reanimated body. */\n readonly stage: number\n /** The `prefers-reduced-motion` mode: no idle motion, no gait. */\n readonly reduced?: boolean\n readonly skirt?: boolean\n /** [right, left] as built: side and whether the hand holds an item. */\n readonly arms: readonly [HumanArm, HumanArm]\n /** [left, right] leg sides. */\n readonly legSides: readonly [1 | -1, 1 | -1]\n}\n\nexport type HumanPose = {\n readonly chestScaleY: number\n readonly hip: { readonly x: number; readonly y: number; readonly zRotation: number }\n readonly upper: { readonly x: number; readonly y: number; readonly z: number }\n readonly head: { readonly x: number; readonly y: number; readonly z: number }\n readonly legs: readonly { readonly hip: number; readonly knee: number; readonly hipZ: number }[]\n readonly arms: readonly { readonly shoulder: number; readonly shoulderZ: number; readonly elbow: number; readonly wrist: number; readonly grip: number }[]\n}\n\nconst REANIMATED_STAGE = 4\nconst TREMOR_STAGE = 3\n\n/** The blended walk of the Elenco for one frame (hips, spine, head, legs, arms), as joint targets. Pure: the caller keeps the phase and the blend. */\nexport const humanPose = (params: HumanPoseParams): HumanPose => {\n const { moves, time, phase, cycle, hunch, armRaise, stage, reduced = false, skirt = false } = params\n const zombie = stage === REANIMATED_STAGE\n const walking = reduced ? 0 : moves.gait\n const phased = phase + time\n const shift = reduced ? 0 : Math.sin(time * 0.45 + phase) * (1 - walking) * (1 - moves.aim)\n const upperX = hunch + moves.crouch * 0.15 + (stage >= TREMOR_STAGE && !reduced ? Math.sin(phased * 2.3) * 0.03 : 0)\n const upperY = walking * Math.sin(cycle - 0.35) * (0.08 + moves.arm * 0.12) - moves.aim * 0.35 + moves.turn * 0.25\n const upperZ = -shift * 0.025 + walking * Math.sin(cycle - 0.35) * 0.02 + (zombie ? 0.05 + Math.sin(cycle * 0.5) * 0.06 * walking : 0)\n const legs = params.legSides.map((side, index) => {\n const legCycle = cycle + (index ? Math.PI : 0)\n const amplitude = moves.amp * (zombie && index === 0 ? 0.45 : 1) * (skirt ? 0.75 : 1)\n return {\n hip: -walking * Math.sin(legCycle) * amplitude - moves.crouch * 0.95 + (zombie && !walking ? (index ? -0.1 : 0.08) : 0),\n knee:\n walking * (Math.max(0, Math.cos(legCycle)) * moves.knee * (zombie && index === 0 ? 0.3 : 1) + 0.06) +\n moves.crouch * 1.55 +\n (1 - walking) * (0.03 + Math.max(0, shift * (index ? 1 : -1)) * 0.18),\n hipZ: side * (0.02 + moves.aim * 0.1 + moves.crouch * 0.06),\n }\n })\n const arms = params.arms.map(({ side, hold = false }, index) => {\n const raise = armRaise * (hold && !zombie ? 0.2 : 1)\n const swing = cycle - 0.25\n const shoulderX =\n (hold ? -0.2 : 0) -\n raise * (zombie ? 1.12 : 1.3) -\n side * Math.sin(swing) * moves.arm * walking * (zombie ? 0.3 : 1) +\n (reduced || walking ? 0 : Math.sin(phased * 1.6 + side) * 0.04) +\n (zombie && !reduced ? Math.sin(phased * 3 + side * 2) * 0.08 : 0)\n const aimX = index === 0 ? -1.45 : -1.32\n return {\n shoulder: shoulderX * (1 - moves.aim) + aimX * moves.aim,\n shoulderZ: side * (0.07 + (zombie ? 0.1 : 0)) * (1 - moves.aim) - side * 0.32 * moves.aim,\n elbow: zombie ? -0.18 : -((moves.elb + (hold ? 0.25 : 0) + walking * Math.max(0, side * Math.sin(swing - 0.3)) * 0.3) * (1 - moves.aim) + (index === 0 ? 0.15 : 0.55) * moves.aim),\n wrist: reduced ? 0 : -walking * Math.sin(swing - 0.6) * 0.15 * side * (1 - moves.aim),\n grip: (hold ? 1.25 : moves.grip) * (zombie ? 0.5 : 1),\n }\n })\n const headY = (reduced ? 0 : Math.sin(phased * 0.5) * 0.25 * (1 - walking) * (1 - moves.aim) * (zombie ? 0.3 : 1)) - upperY * 0.75 * (1 - moves.turn) + moves.turn * 0.45 + moves.aim * 0.3\n return {\n chestScaleY: 1 + (reduced ? 0 : Math.sin(phased * (1.6 + moves.freq * 0.12)) * (0.012 + moves.freq * 0.0012)),\n hip: { x: shift * 0.025 + walking * Math.sin(cycle) * 0.018, y: -walking * moves.bob * Math.abs(Math.sin(cycle)) - moves.crouch * 0.26, zRotation: shift * 0.02 - walking * Math.sin(cycle) * 0.022 },\n upper: { x: upperX, y: upperY, z: upperZ },\n head: { x: -hunch * 0.6 - moves.crouch * 0.12 + (zombie ? 0.15 : 0) + walking * Math.sin(cycle * 2 - 0.6) * 0.02, y: headY, z: (zombie ? 0.22 : 0) - upperZ * 0.6 },\n legs,\n arms,\n }\n}\n\n/** A leg that cannot carry weight (limp): the swing shrinks to 30%, the knee straightens, the spine leans toward the good leg. */\nexport const LIMP = { swing: 0.3, knee: 0.05, lean: 0.07 } as const\n","/** Prone crawl on the arms (the Elenco's leg-less body): joint targets at `time`; `figTilt` is the body's tilt target, which the caller approaches at `dt · 4`. */\nexport type CrawlPose = {\n readonly figTilt: number\n readonly hipY: number\n readonly hipZRotation: number\n readonly upper: { readonly x: number; readonly y: number }\n readonly headX: number\n readonly arms: readonly { readonly shoulder: number; readonly shoulderZ: number; readonly elbow: number }[]\n readonly legs: readonly { readonly hip: number; readonly knee: number }[]\n}\n\nexport const CRAWL_TILT_RATE = 4\nexport const DEAD_TILT_RATE = 3\n\nexport const crawlPose = (time: number, phase: number, sides: readonly [1 | -1, 1 | -1]): CrawlPose => {\n const cycle = time * 2.2 + phase\n const wave = Math.sin(cycle)\n return {\n figTilt: 1.38,\n hipY: 0.2 + Math.abs(wave) * 0.02,\n hipZRotation: wave * 0.05,\n upper: { x: 0, y: wave * 0.12 },\n headX: -0.95,\n arms: sides.map((side) => ({ shoulder: -2.55 + side * wave * 0.45, shoulderZ: side * 0.25, elbow: -0.35 - Math.max(0, -side * wave) * 0.6 })),\n legs: [\n { hip: 0.1, knee: 0 },\n { hip: 0.12 + wave * 0.08, knee: 0.25 },\n ],\n }\n}\n\nexport type DeadPose = {\n readonly figTilt: number\n readonly hipY: number\n readonly headY: number\n readonly arms: readonly { readonly shoulder: number; readonly shoulderZ: number; readonly elbow: number }[]\n readonly legs: readonly { readonly hip: number; readonly hipZ: number; readonly knee: number }[]\n}\n\n/** A body on the ground (the Elenco's death pose); the caller approaches `figTilt` at `dt · 3`. */\nexport const deadPose = (armSides: readonly [1 | -1, 1 | -1]): DeadPose => ({\n figTilt: -1.45,\n hipY: 0.22,\n headY: 0.5,\n arms: armSides.map((side) => ({ shoulder: -0.2, shoulderZ: side * 1.1, elbow: -0.3 })),\n legs: [-0.15, 0.15].map((hipZ) => ({ hip: 0, hipZ, knee: 0.1 })),\n})\n","export type ConditionPhase = {\n readonly id: string\n /** When the phase starts, in the timeline's units (hours, or seconds on an accelerated clock). The first phase starts at 0. */\n readonly startsAt: number\n /** Numbers the phase drives (skin tint, hunch, arm raise, ...), by name. */\n readonly effects?: Readonly<Record<string, number>>\n}\n\nexport type ConditionOptions = {\n readonly phases: readonly ConditionPhase[]\n /** Timeline units per real second: 1 on a clock already in seconds (the accelerated preset), 1/3600 for hours. Default 1. */\n readonly unitsPerSecond?: number\n /** Interpolate effects toward the next phase instead of stepping. Default false. */\n readonly blend?: boolean\n}\n\nexport type ConditionSample = {\n readonly index: number\n readonly phase: ConditionPhase\n /** 0..1 inside the current phase (1 on the last one). */\n readonly progress: number\n readonly effects: Readonly<Record<string, number>>\n readonly isFinal: boolean\n}\n\nexport type ConditionTimeline = {\n readonly elapsed: number\n /** Advances the clock by `seconds` of real time. */\n advance(seconds: number): ConditionSample\n /** Jumps the clock to `units` of the timeline. */\n seek(units: number): ConditionSample\n sample(): ConditionSample\n reset(): void\n}\n\nconst phaseIndexAt = (phases: readonly ConditionPhase[], units: number): number => {\n let found = 0\n phases.forEach((phase, index) => {\n if (units >= phase.startsAt) found = index\n })\n return found\n}\n\nconst mixEffects = (from: ConditionPhase['effects'], to: ConditionPhase['effects'], amount: number): Record<string, number> => {\n const result: Record<string, number> = {}\n for (const name of new Set([...Object.keys(from ?? {}), ...Object.keys(to ?? {})])) {\n const start = from?.[name] ?? 0\n result[name] = start + ((to?.[name] ?? 0) - start) * amount\n }\n return result\n}\n\n/** A condition that moves through ordered phases over time (an infection: well, fever, delirium, reanimated). Generic: phases and effects are data. */\nexport const createConditionTimeline = (options: ConditionOptions): ConditionTimeline => {\n const { phases, unitsPerSecond = 1, blend = false } = options\n if (phases.length === 0) throw new Error('createConditionTimeline: at least one phase is required')\n let units = 0\n const sample = (): ConditionSample => {\n const index = phaseIndexAt(phases, units)\n const phase = phases[index] as ConditionPhase\n const next = phases[index + 1]\n const progress = next ? Math.min(1, (units - phase.startsAt) / (next.startsAt - phase.startsAt)) : 1\n const effects = blend && next ? mixEffects(phase.effects, next.effects, progress) : { ...phase.effects }\n return { index, phase, progress, effects, isFinal: !next }\n }\n return {\n get elapsed() {\n return units\n },\n advance(seconds) {\n units += seconds * unitsPerSecond\n return sample()\n },\n seek(target) {\n units = Math.max(0, target)\n return sample()\n },\n sample,\n reset() {\n units = 0\n },\n }\n}\n","{\n \"variance\": {\n \"height\": {\n \"gaussianSpread\": 0.09,\n \"min\": 0.9,\n \"max\": 1.1\n },\n \"buildBands\": [\n {\n \"below\": 0.3,\n \"base\": 0.85,\n \"range\": 0.1\n },\n {\n \"below\": 0.8,\n \"base\": 0.95,\n \"range\": 0.13\n },\n {\n \"base\": 1.08,\n \"range\": 0.17\n }\n ],\n \"buildSpeed\": {\n \"lean\": 1.05,\n \"heavy\": 0.9\n },\n \"buildHealth\": {\n \"lean\": 0.9,\n \"heavy\": 1.15\n },\n \"speedJitter\": {\n \"base\": 0.92,\n \"range\": 0.16\n },\n \"wanderJitter\": {\n \"base\": 0.8,\n \"range\": 0.4\n },\n \"runnerAgeClamp\": 1,\n \"variantCount\": {\n \"walker\": 4,\n \"runner\": 3,\n \"crawler\": 2,\n \"brute\": 2\n },\n \"variantSeedBase\": 7000\n },\n \"civilianColors\": [\n \"6D5A4A\",\n \"4D5D6B\",\n \"7A3B33\",\n \"5B6B4A\",\n \"8A7A5A\",\n \"3E4A5C\",\n \"6A4A6A\",\n \"9A8A70\",\n \"40403A\"\n ],\n \"corpses\": {\n \"max\": 400,\n \"lifeSeconds\": 900\n }\n}\n","import appearance from '../data/appearance.json'\n\nconst { variance } = appearance\n\nexport type BuildClass = 'lean' | 'average' | 'heavy'\n\n/** What `zSpawn` draws for a body: height, width, how it builds up and the speed jitters. Every field comes from the stream, in the game's order. */\nexport type BodyVariance = {\n readonly height: number\n readonly build: number\n readonly buildClass: BuildClass\n /** Multiplies the chase speed: the build factor times a 0.92 .. 1.08 jitter. */\n readonly speedFactor: number\n /** Multiplies the wander speed: 0.8 .. 1.2. */\n readonly wanderFactor: number\n /** Multiplies the health: 0.9 lean, 1.15 heavy. */\n readonly healthFactor: number\n}\n\nexport type VarianceScale = { readonly height?: number; readonly build?: number }\n\nconst clamp = (value: number, low: number, high: number): number => Math.min(high, Math.max(low, value))\n\n/**\n * One body's variance from `random`, drawing exactly what the game's `zSpawn` draws after the type, age and outfit picks:\n * three draws for the height (a gaussian), one for the build band plus one inside it, one for the speed and one for the wander speed.\n * `scale` is the type's own scale (a brute's 1.42) and `build` its width factor.\n */\nexport const drawBodyVariance = (random: () => number, scale: VarianceScale = {}): BodyVariance => {\n const spread = (random() + random() + random() - 1.5) * variance.height.gaussianSpread\n const height = clamp(1 + spread, variance.height.min, variance.height.max) * (scale.height ?? 1)\n const band = random()\n const bands = variance.buildBands as readonly { below?: number; base: number; range: number }[]\n const found = bands.find((candidate) => candidate.below !== undefined && band < candidate.below) ?? (bands[bands.length - 1] as { base: number; range: number })\n const { base, range } = found\n const build = (base + random() * range) * (scale.build ?? 1)\n const buildClass: BuildClass = band < 0.3 ? 'lean' : band >= 0.8 ? 'heavy' : 'average'\n const speedBuild = buildClass === 'lean' ? variance.buildSpeed.lean : buildClass === 'heavy' ? variance.buildSpeed.heavy : 1\n const healthBuild = buildClass === 'lean' ? variance.buildHealth.lean : buildClass === 'heavy' ? variance.buildHealth.heavy : 1\n return {\n height,\n build,\n buildClass,\n speedFactor: speedBuild * (variance.speedJitter.base + random() * variance.speedJitter.range),\n wanderFactor: variance.wanderJitter.base + random() * variance.wanderJitter.range,\n healthFactor: healthBuild,\n }\n}\n\n/** Variants baked per type (the game's `nv`): 4 walkers, 3 runners, 2 crawlers and brutes, 1 for the rest. */\nexport const variantCountFor = (typeId: string): number => (variance.variantCount as Readonly<Record<string, number>>)[typeId] ?? 1\n\n/** The seed of variant `variant` of the type at `typeIndex` (the game's ZVAR rule: `7000 + c·101 + v·13`). */\nexport const variantSeed = (typeIndex: number, variant: number): number => variance.variantSeedBase + typeIndex * 101 + variant * 13\n\n/** The decay each variant is baked at: 1, 2, 1, 2. */\nexport const variantDecay = (variant: number): number => (variant % 2 === 0 ? 1 : 2)\n","/** The colour and style pools of the Bestiário v17 builder, in the order its random stream picks from them. */\nexport const SKIN_TONES = [0xf1c9a5, 0xe0ac80, 0xc68c62, 0xa8714a, 0x8a5a3a, 0x5e3b26] as const\nexport const TOP_COLORS = [0x4a6f96, 0x8e3b3b, 0x5d4a3a, 0x4f7a43, 0x6b6f78, 0x3f5f3a, 0x5a4a6e, 0x8a6a3a, 0xb8b2a2, 0x2c2c30, 0x9a4a2a, 0x2f5d63, 0xc9a94a, 0x7a2e4a, 0xd8d4c8, 0x3a4a6a] as const\nexport const PANTS_COLORS = [0x2c3340, 0x1f2228, 0x4a4030, 0x5a5f66, 0x3a4a6a, 0x6a5a40, 0x2a3a2a] as const\nexport const SLEEVES = ['short', 'short', 'long', 'tank'] as const\nexport const HAIR_STYLES_FEMALE = ['long', 'long', 'bun', 'short', 'afro', 'bald'] as const\nexport const HAIR_STYLES_MALE = ['short', 'short', 'buzz', 'bald', 'afro', 'long'] as const\nexport const CAP_COLORS = [0x8e3b3b, 0x2c2c30, 0x3a4a6a, 0xc9a94a] as const\nexport const BAG_COLORS = [0x3a4a3a, 0x5a2a24, 0x2c3340] as const\nexport const HAIR_COLORS = [0x1a1410, 0x2a1e16, 0x3b2c20, 0x6a4a2a, 0xb08a50, 0x7a2a1a] as const\nexport const SHOE_COLORS = [0x1d1f22, 0x3a2a1a, 0xb8b4a8, 0x4a1a1a] as const\n/** Uniform bodies (the crowd's baked variants): skin by decay, shirt by type index. */\nexport const DECAY_SKIN = [0xcdbba8, 0x94a084, 0x7b744a, 0x8c7c5e] as const\nexport const UNIFORM_SHIRTS = [0x4a6f96, 0x8e3b3b, 0x5d4a3a, 0x4f7a43, 0x6b6f78, 0x3f5f3a, 0x5a4a6e, 0x8a6a3a] as const\nexport const UNIFORM_PANTS = 0x2c3340\nexport const UNIFORM_SHOES = 0x1d1f22\nexport const SKIN_DECAY_BLEND = [0.45, 0.65, 0.8, 0.88] as const\nexport const GRAY = 0x4a4a46\nexport const RED = 0x7a120c\nexport const BLOOD_COLORS = [0x9e1b12, 0x6e1410, 0x4a2a18, 0x1c1612] as const\nexport const OLD_HAIR = 0x8a8780\n","import { createRng } from '../damage/points'\n\nimport {\n BAG_COLORS,\n CAP_COLORS,\n DECAY_SKIN,\n HAIR_COLORS,\n HAIR_STYLES_FEMALE,\n HAIR_STYLES_MALE,\n OLD_HAIR,\n PANTS_COLORS,\n SHOE_COLORS,\n SKIN_TONES,\n SLEEVES,\n TOP_COLORS,\n UNIFORM_PANTS,\n UNIFORM_SHIRTS,\n UNIFORM_SHOES,\n} from './palette'\n\nexport type HairStyle = (typeof HAIR_STYLES_FEMALE)[number] | (typeof HAIR_STYLES_MALE)[number]\n\nexport type OutfitLook = { readonly cloth?: number; readonly pants?: number; readonly hat?: string }\n\nexport type AppearanceParams = {\n readonly seed: number\n /** The look of an outfit (police, military ...): its colours replace the draws, so the stream does not spend them. */\n readonly outfit?: OutfitLook\n /** A baked crowd variant: no random look, fixed skin, shirt by `typeIndex`, short hair. */\n readonly uniform?: boolean\n readonly typeIndex?: number\n readonly clothIndex?: number\n /** Forces the sex; unset draws it (42% female). */\n readonly female?: boolean\n /** False for bodies that are never female (a brute): the draw is skipped. */\n readonly allowFemale?: boolean\n readonly tone?: number\n readonly hairStyle?: HairStyle\n readonly hairColor?: number\n /** The type's own look: wet bodies have long hair, charred ones are bald. */\n readonly wet?: boolean\n readonly charred?: boolean\n /** A living person: no bag, no tear. */\n readonly human?: boolean\n /** 0 fresh-dead, 1 normal, 2 old (grey hair). */\n readonly life?: number\n}\n\nexport type AppearanceTraits = {\n readonly female: boolean\n readonly tone: number\n readonly topColor: number\n readonly pantsColor: number\n readonly sleeve: string\n readonly hair: string\n readonly headWidth: number\n readonly headHeight: number\n readonly jaw: number\n readonly nose: number\n readonly beard: boolean\n readonly glasses: boolean\n readonly cap: boolean\n readonly capColor: number\n readonly bag: boolean\n readonly bagColor: number\n readonly torn: boolean\n readonly apron: boolean\n readonly hairColor: number\n readonly shoeColor: number\n}\n\nexport type DrawnAppearance = {\n readonly traits: AppearanceTraits\n /** The stream, positioned after the look: the body builder keeps drawing from it (the hair of a decrepit body). */\n readonly random: () => number\n}\n\n/** The seed of the blood stream (the same drips on every level of detail). */\nexport const BLOOD_SEED_OFFSET = 99\n\nexport const createBloodStream = (seed: number): (() => number) => createRng(seed + BLOOD_SEED_OFFSET)\n\nconst FEMALE_SHARE = 0.42\nconst BEARD_SHARE = 0.25\nconst GLASSES_SHARE = 0.12\nconst CAP_SHARE = 0.12\nconst BAG_SHARE = 0.15\nconst TORN_SHARE = 0.35\nconst APRON_SHARE = 0.05\n\n/**\n * A person's look from a seed, drawing in exactly the order the Bestiário v17 builder draws from its `VR` stream, including the draws a\n * short-circuit skips (no beard roll for a woman, no cap roll under a hat). Returns the stream so the builder can continue from it.\n */\nexport const drawAppearance = (params: AppearanceParams): DrawnAppearance => {\n const { seed, outfit = {}, uniform = false, typeIndex = 0, human = false, life = 1 } = params\n const random = createRng(seed)\n const pick = <T>(options: readonly T[]): T => options[Math.floor(random() * options.length)] as T\n const female = !uniform && (params.allowFemale ?? true) && (params.female ?? random() < FEMALE_SHARE)\n const tone = params.tone ?? (uniform ? (DECAY_SKIN[0] as number) : pick(SKIN_TONES))\n const topColor = outfit.cloth ?? (uniform ? (UNIFORM_SHIRTS[(params.clothIndex ?? typeIndex) % UNIFORM_SHIRTS.length] as number) : pick(TOP_COLORS))\n const pantsColor = outfit.pants ?? (uniform ? UNIFORM_PANTS : pick(PANTS_COLORS))\n const look = uniform ? uniformLook() : drawLook({ random, pick, female, outfit, human, params })\n const hairColor = params.hairColor ?? (life === 2 ? OLD_HAIR : pick(HAIR_COLORS))\n const shoeColor = uniform ? UNIFORM_SHOES : pick(SHOE_COLORS)\n return { traits: { female, tone, topColor, pantsColor, ...look, hairColor, shoeColor }, random }\n}\n\ntype Look = Omit<AppearanceTraits, 'female' | 'tone' | 'topColor' | 'pantsColor' | 'hairColor' | 'shoeColor'>\n\nconst uniformLook = (): Look => ({\n sleeve: 'short',\n hair: 'short',\n headWidth: 1,\n headHeight: 1,\n jaw: 1,\n nose: 1,\n beard: false,\n glasses: false,\n cap: false,\n capColor: 0,\n bag: false,\n bagColor: 0,\n torn: false,\n apron: false,\n})\n\ntype DrawLookParams = {\n readonly random: () => number\n readonly pick: <T>(options: readonly T[]) => T\n readonly female: boolean\n readonly outfit: OutfitLook\n readonly human: boolean\n readonly params: AppearanceParams\n}\n\nconst drawLook = ({ random, pick, female, outfit, human, params }: DrawLookParams): Look => {\n const sleeve = outfit.cloth ? 'long' : pick(SLEEVES)\n const hair = params.hairStyle ?? (params.wet ? 'long' : params.charred ? 'bald' : pick(female ? HAIR_STYLES_FEMALE : HAIR_STYLES_MALE))\n const headWidth = 1 + (random() - 0.5) * 0.16\n const headHeight = 1 + (random() - 0.5) * 0.14\n const jaw = 1 + (random() - 0.5) * 0.3\n const nose = 1 + (random() - 0.5) * 0.5\n const beard = !female && random() < BEARD_SHARE\n const glasses = random() < GLASSES_SHARE\n const cap = !outfit.hat && random() < CAP_SHARE\n const capColor = pick(CAP_COLORS)\n const bag = random() < BAG_SHARE && !human\n const bagColor = pick(BAG_COLORS)\n const torn = random() < TORN_SHARE && !human\n const apron = random() < APRON_SHARE\n return { sleeve, hair, headWidth, headHeight, jaw, nose, beard, glasses, cap, capColor, bag, bagColor, torn, apron }\n}\n","import { lerpPose } from '../gait/cascade'\n\n/** One key of a clip: the time in seconds and the joint targets by name (the names of the rich body's `setPose`; a clip may add channels of its own, such as `x` or `run`). */\nexport type PoseKey = { t: number; pose: Record<string, number> }\n\n/** What a clip says happens at a time: `impact` (\"the blow lands now\"), `phase` (a label for the screen), or any type a game defines. */\nexport type PoseClipEvent = { t: number; type: string; data?: Record<string, unknown> }\n\n/** Which joints a clip drives: `upper` leaves the legs to the gait, `lower` leaves the trunk, head and arms, `full` sets everything. */\nexport type PoseClipChannels = 'full' | 'upper' | 'lower'\n\nexport type PoseClip = {\n id: string\n duration: number\n loop?: boolean\n keys: PoseKey[]\n events?: PoseClipEvent[]\n channels?: PoseClipChannels\n /** Where the clip was ported from (file and variable), for the diff against the team page. */\n source?: Record<string, unknown>\n}\n\n/**\n * The Bestiário's `sample(K, t)`: finds the segment of two keys, clamps the fraction to [0, 1], eases it with smoothstep `k·k·(3 − 2k)` and\n * blends the two poses by joint name (`lerpPose`). Before the first key it holds the first segment's start, after the last it holds the end.\n */\nexport const samplePoseClip = (clip: Pick<PoseClip, 'keys'>, time: number): Record<string, number> => {\n const keys = clip.keys\n if (keys.length === 0) return {}\n if (keys.length === 1) return { ...keys[0]!.pose }\n let index = 0\n while (index < keys.length - 2 && time > keys[index + 1]!.t) index++\n const a = keys[index]!\n const b = keys[index + 1]!\n const k = Math.min(1, Math.max(0, (time - a.t) / (b.t - a.t)))\n const eased = k * k * (3 - 2 * k)\n return lerpPose(a.pose, b.pose, eased)\n}\n\n/** The events a clip fires while the clock goes from `from` (excluded) to `to` (included), in time order. */\nexport const clipEventsBetween = (clip: Pick<PoseClip, 'events'>, from: number, to: number): PoseClipEvent[] =>\n (clip.events ?? []).filter((event) => event.t > from && event.t <= to).sort((x, y) => x.t - y.t)\n\n/** The names of the joints a channel set drives, from the names `PoseTargets` of the rich animator knows. */\nexport const POSE_CHANNEL_JOINTS: Readonly<Record<PoseClipChannels, readonly string[] | undefined>> = {\n full: undefined,\n upper: ['lean', 'twist', 'side', 'headX', 'headY', 'headZ', 'armR', 'armL', 'elbR', 'elbL', 'armRz', 'armLz'],\n lower: ['drop', 'legL', 'legR', 'kneeL', 'kneeR'],\n}\n\n/** Keeps from a sampled pose only the joints of the channel set (`full` keeps everything, including extra channels such as `x`). */\nexport const filterPoseChannels = (pose: Record<string, number>, channels: PoseClipChannels = 'full'): Record<string, number> => {\n const allowed = POSE_CHANNEL_JOINTS[channels]\n if (!allowed) return pose\n const out: Record<string, number> = {}\n for (const name of allowed) if (pose[name] !== undefined) out[name] = pose[name] as number\n return out\n}\n\n/** Joints that swap with their opposite when a clip is mirrored, and the channels that change sign (side, twist and the sideways turns). */\nconst MIRROR_PAIRS: ReadonlyArray<readonly [string, string]> = [['armR', 'armL'], ['elbR', 'elbL'], ['armRz', 'armLz'], ['legR', 'legL'], ['kneeR', 'kneeL']]\nconst MIRROR_NEGATED: ReadonlySet<string> = new Set(['side', 'twist', 'headY', 'headZ'])\n\n/** The clip as seen in a mirror: left and right swap and the sideways channels change sign; the times, the events and the duration stay, so the impact falls at the same second. */\nexport const mirrorPoseClip = (clip: PoseClip, id: string): PoseClip => ({\n ...clip,\n id,\n keys: clip.keys.map((key) => {\n const pose: Record<string, number> = {}\n for (const [name, value] of Object.entries(key.pose)) pose[name] = MIRROR_NEGATED.has(name) ? -value : value\n for (const [a, b] of MIRROR_PAIRS) {\n const va = key.pose[a]\n const vb = key.pose[b]\n const sign = a === 'armRz' ? -1 : 1 // the sideways swing of an arm opens to the other side\n if (vb !== undefined) pose[a] = sign * vb\n else delete pose[a]\n if (va !== undefined) pose[b] = sign * va\n else delete pose[b]\n }\n return { t: key.t, pose }\n }),\n ...(clip.events ? { events: clip.events.map((event) => ({ ...event })) } : {}),\n})\n","{\n \"clips\": [\n {\n \"id\": \"grabbed_bitten_human\",\n \"source\": {\n \"file\": \"site/pages/zumbis-personagens/bestiario.html\",\n \"scene\": \"ataque (Agarrão e mordida)\",\n \"variable\": \"HK\"\n },\n \"duration\": 8,\n \"loop\": false,\n \"channels\": \"full\",\n \"keys\": [\n {\n \"t\": 0,\n \"pose\": {\n \"lean\": 0,\n \"headY\": 0,\n \"armL\": -0.1,\n \"armR\": 0.05,\n \"elbL\": -0.2,\n \"elbR\": -0.2,\n \"drop\": 0,\n \"kneeL\": 0.05,\n \"kneeR\": 0.05,\n \"legL\": -0.05,\n \"legR\": 0.05,\n \"headX\": 0\n }\n },\n {\n \"t\": 1.5,\n \"pose\": {\n \"lean\": -0.1,\n \"headY\": -0.6,\n \"armL\": -0.1,\n \"armR\": 0.05,\n \"elbL\": -0.2,\n \"elbR\": -0.2,\n \"drop\": 0,\n \"kneeL\": 0.05,\n \"kneeR\": 0.05,\n \"legL\": -0.05,\n \"legR\": 0.05,\n \"headX\": -0.1\n }\n },\n {\n \"t\": 2.1,\n \"pose\": {\n \"lean\": -0.3,\n \"headY\": -0.2,\n \"armL\": -1.3,\n \"armR\": -1.2,\n \"elbL\": -1.4,\n \"elbR\": -1.5,\n \"drop\": -0.04,\n \"kneeL\": 0.25,\n \"kneeR\": 0.2,\n \"legL\": -0.3,\n \"legR\": 0.25,\n \"headX\": -0.25\n }\n },\n {\n \"t\": 3.6,\n \"pose\": {\n \"lean\": -0.35,\n \"headY\": 0.3,\n \"armL\": -1.5,\n \"armR\": -1.0,\n \"elbL\": -0.6,\n \"elbR\": -1.8,\n \"drop\": -0.05,\n \"kneeL\": 0.3,\n \"kneeR\": 0.2,\n \"legL\": -0.4,\n \"legR\": 0.35,\n \"headX\": -0.2\n }\n },\n {\n \"t\": 4.3,\n \"pose\": {\n \"lean\": -0.2,\n \"headY\": 0.7,\n \"headZ\": -0.5,\n \"armL\": -0.6,\n \"armR\": -1.9,\n \"elbL\": -0.4,\n \"elbR\": -1.2,\n \"drop\": -0.06,\n \"kneeL\": 0.3,\n \"kneeR\": 0.3,\n \"legL\": -0.3,\n \"legR\": 0.3,\n \"headX\": -0.3\n }\n },\n {\n \"t\": 5.6,\n \"pose\": {\n \"lean\": 0.55,\n \"headY\": 0.4,\n \"headZ\": -0.3,\n \"armL\": -0.3,\n \"armR\": -0.5,\n \"elbL\": -0.6,\n \"elbR\": -0.8,\n \"drop\": -0.5,\n \"kneeL\": 1.9,\n \"kneeR\": 1.7,\n \"legL\": -1.1,\n \"legR\": -0.9,\n \"headX\": 0.3\n }\n },\n {\n \"t\": 8,\n \"pose\": {\n \"lean\": 0.6,\n \"headY\": 0.4,\n \"headZ\": -0.3,\n \"armL\": -0.25,\n \"armR\": -0.45,\n \"elbL\": -0.6,\n \"elbR\": -0.8,\n \"drop\": -0.52,\n \"kneeL\": 1.95,\n \"kneeR\": 1.75,\n \"legL\": -1.1,\n \"legR\": -0.95,\n \"headX\": 0.4\n }\n }\n ],\n \"events\": [\n {\n \"t\": 0,\n \"type\": \"phase\",\n \"data\": {\n \"label\": \"pose_clip.grab_bite.phase.approach\"\n }\n },\n {\n \"t\": 1.7,\n \"type\": \"phase\",\n \"data\": {\n \"label\": \"pose_clip.grab_bite.phase.pounce\"\n }\n },\n {\n \"t\": 2.1,\n \"type\": \"phase\",\n \"data\": {\n \"label\": \"pose_clip.grab_bite.phase.grab\"\n }\n },\n {\n \"t\": 4.3,\n \"type\": \"phase\",\n \"data\": {\n \"label\": \"pose_clip.grab_bite.phase.bite\"\n }\n },\n {\n \"t\": 5.6,\n \"type\": \"phase\",\n \"data\": {\n \"label\": \"pose_clip.grab_bite.phase.fall\"\n }\n },\n {\n \"t\": 4.4,\n \"type\": \"impact\",\n \"data\": {\n \"kind\": \"bite\",\n \"source\": \"bite.visible=T>4.4 in the scene's step()\"\n }\n }\n ]\n },\n {\n \"id\": \"grab_bite_zombie\",\n \"source\": {\n \"file\": \"site/pages/zumbis-personagens/bestiario.html\",\n \"scene\": \"ataque (Agarrão e mordida)\",\n \"variable\": \"ZK\"\n },\n \"duration\": 8,\n \"loop\": false,\n \"channels\": \"full\",\n \"keys\": [\n {\n \"t\": 0,\n \"pose\": {\n \"x\": -6,\n \"lean\": 0.75,\n \"armL\": -1.35,\n \"armR\": -1.05,\n \"elbL\": -0.08,\n \"elbR\": -0.12,\n \"headX\": -0.55,\n \"drop\": -0.1,\n \"run\": 1\n }\n },\n {\n \"t\": 1.7,\n \"pose\": {\n \"x\": -0.9,\n \"lean\": 0.75,\n \"armL\": -1.35,\n \"armR\": -1.05,\n \"elbL\": -0.08,\n \"elbR\": -0.12,\n \"headX\": -0.55,\n \"drop\": -0.1,\n \"run\": 1\n }\n },\n {\n \"t\": 2.1,\n \"pose\": {\n \"x\": -0.32,\n \"lean\": 0.45,\n \"armL\": -1.65,\n \"armR\": -1.6,\n \"elbL\": -0.25,\n \"elbR\": -0.3,\n \"headX\": -0.3,\n \"drop\": -0.08,\n \"legL\": -0.6,\n \"legR\": 0.4,\n \"kneeL\": 0.5,\n \"kneeR\": 0.6,\n \"run\": 0\n }\n },\n {\n \"t\": 3.6,\n \"pose\": {\n \"x\": -0.2,\n \"lean\": 0.35,\n \"armL\": -1.5,\n \"armR\": -1.45,\n \"elbL\": -1.1,\n \"elbR\": -1.2,\n \"headX\": -0.2,\n \"drop\": -0.06,\n \"legL\": -0.4,\n \"legR\": 0.3,\n \"kneeL\": 0.4,\n \"kneeR\": 0.4,\n \"run\": 0\n }\n },\n {\n \"t\": 4.3,\n \"pose\": {\n \"x\": -0.1,\n \"lean\": 0.5,\n \"armL\": -1.4,\n \"armR\": -1.3,\n \"elbL\": -1.5,\n \"elbR\": -1.5,\n \"headX\": 0.35,\n \"headY\": 0.35,\n \"drop\": -0.08,\n \"legL\": -0.35,\n \"legR\": 0.3,\n \"kneeL\": 0.4,\n \"kneeR\": 0.4,\n \"run\": 0\n }\n },\n {\n \"t\": 5.6,\n \"pose\": {\n \"x\": 0,\n \"lean\": 0.95,\n \"armL\": -1.2,\n \"armR\": -1.0,\n \"elbL\": -1.4,\n \"elbR\": -1.3,\n \"headX\": 0.5,\n \"headY\": 0.3,\n \"drop\": -0.45,\n \"legL\": -1.2,\n \"legR\": -0.8,\n \"kneeL\": 1.8,\n \"kneeR\": 1.5,\n \"run\": 0\n }\n },\n {\n \"t\": 8,\n \"pose\": {\n \"x\": 0,\n \"lean\": 1.0,\n \"armL\": -1.15,\n \"armR\": -0.95,\n \"elbL\": -1.4,\n \"elbR\": -1.3,\n \"headX\": 0.55,\n \"headY\": 0.3,\n \"drop\": -0.47,\n \"legL\": -1.2,\n \"legR\": -0.8,\n \"kneeL\": 1.85,\n \"kneeR\": 1.5,\n \"run\": 0\n }\n }\n ],\n \"events\": [\n {\n \"t\": 0,\n \"type\": \"phase\",\n \"data\": {\n \"label\": \"pose_clip.grab_bite.phase.approach\"\n }\n },\n {\n \"t\": 1.7,\n \"type\": \"phase\",\n \"data\": {\n \"label\": \"pose_clip.grab_bite.phase.pounce\"\n }\n },\n {\n \"t\": 2.1,\n \"type\": \"phase\",\n \"data\": {\n \"label\": \"pose_clip.grab_bite.phase.grab\"\n }\n },\n {\n \"t\": 4.3,\n \"type\": \"phase\",\n \"data\": {\n \"label\": \"pose_clip.grab_bite.phase.bite\"\n }\n },\n {\n \"t\": 5.6,\n \"type\": \"phase\",\n \"data\": {\n \"label\": \"pose_clip.grab_bite.phase.fall\"\n }\n }\n ]\n }\n ],\n \"locales\": {\n \"pt-BR\": {\n \"pose_clip.grab_bite.phase.approach\": \"1. Aproximação\",\n \"pose_clip.grab_bite.phase.pounce\": \"2. O bote\",\n \"pose_clip.grab_bite.phase.grab\": \"3. Agarrão: 3 s para se soltar\",\n \"pose_clip.grab_bite.phase.bite\": \"4. Mordida: infecção 100%\",\n \"pose_clip.grab_bite.phase.fall\": \"5. Queda\"\n },\n \"en\": {\n \"pose_clip.grab_bite.phase.approach\": \"1. Approach\",\n \"pose_clip.grab_bite.phase.pounce\": \"2. The pounce\",\n \"pose_clip.grab_bite.phase.grab\": \"3. Grab: 3 s to break free\",\n \"pose_clip.grab_bite.phase.bite\": \"4. Bite: 100% infection\",\n \"pose_clip.grab_bite.phase.fall\": \"5. The fall\"\n }\n }\n}","import data from '../presets/pose-clips.json'\n\nimport type { PoseClip } from './pose-clip'\n\nexport type PoseClipRegistry = {\n /** Adds a clip, or replaces the one with the same id. Returns the registry. */\n register(clip: PoseClip): PoseClipRegistry\n get(id: string): PoseClip | undefined\n list(): PoseClip[]\n remove(id: string): boolean\n}\n\n/** A registry of clips by id: games add or replace clips without touching the core. */\nexport const createPoseClipRegistry = (initial: readonly PoseClip[] = []): PoseClipRegistry => {\n const clips = new Map<string, PoseClip>()\n const registry: PoseClipRegistry = {\n register(clip) {\n clips.set(clip.id, clip)\n return registry\n },\n get: (id) => clips.get(id),\n list: () => [...clips.values()],\n remove: (id) => clips.delete(id),\n }\n for (const clip of initial) registry.register(clip)\n return registry\n}\n\n/** The clips ported from the Bestiário \"grab and bite\" scene (human and zombie). Phase labels are locale keys, see `POSE_CLIP_LOCALES`. */\nexport const POSE_CLIPS: readonly PoseClip[] = data.clips as PoseClip[]\n\n/** Texts of the phase labels by locale (`pt-BR` is the team's text). */\nexport const POSE_CLIP_LOCALES: Readonly<Record<string, Readonly<Record<string, string>>>> = data.locales\n","import type { PoseClipEvent } from './pose-clip'\n\ntype Triple = [number, number, number]\n\n/**\n * A hand or a foot in the camera's space (x to the right, y up, the view looks down -z), in metres and radians. `p` is the middle of the palm (the ankle for a foot) and `r` the Euler angles\n * (order XYZ) of its frame: x from the wrist to the knuckles (from the heel to the toes), y the back of the hand (the top of the foot), z the rest of the right-handed frame; a left hand is the\n * mirror image of a right one, so the same numbers describe the same grip.\n */\nexport type FirstPersonPoseKey = {\n p: Triple\n r: Triple\n /** How closed the fingers are, 0 open to 1 closed; left out, the fingers keep what the stance has. */\n grip?: number\n /** How much of the clip replaces what the hand is doing otherwise (holding a weapon, the stance), 0 none to 1 all. Default 1. */\n w?: number\n}\n\n/** One key: at `t` seconds the right hand (`R`), the left hand (`L`, or `'mirror'` for the mirror image of the right) and the right foot (`F`) are where the entries say. A side without an entry here is not driven by this key. */\nexport type FirstPersonKey = { t: number; R?: FirstPersonPoseKey; L?: FirstPersonPoseKey | 'mirror'; F?: FirstPersonPoseKey }\n\n/** A first-person animation of the hands and the foot: the counterpart of a third-person `PoseClip`, with the same events. */\nexport type FirstPersonClip = {\n id: string\n duration: number\n keys: FirstPersonKey[]\n events?: PoseClipEvent[]\n /** Where the clip was ported from, or who authored it. */\n source?: Record<string, unknown>\n}\n\n/** What a channel does at a time. `mirror` says the numbers are the right hand's and the left hand is their mirror image. */\nexport type FirstPersonSampleChannel = { p: Triple; r: Triple; grip: number | undefined; w: number; mirror: boolean }\nexport type FirstPersonSample = { R?: FirstPersonSampleChannel; L?: FirstPersonSampleChannel; F?: FirstPersonSampleChannel }\n\nconst ease = (k: number): number => k * k * (3 - 2 * k)\nconst mix = (a: number, b: number, k: number): number => a + (b - a) * k\nconst mixTriple = (a: Triple, b: Triple, k: number): Triple => [mix(a[0], b[0], k), mix(a[1], b[1], k), mix(a[2], b[2], k)]\n\ntype Track = { t: number; key: FirstPersonPoseKey; mirror: boolean }\n\nconst trackOf = (clip: Pick<FirstPersonClip, 'keys'>, side: 'R' | 'L' | 'F'): Track[] => {\n const out: Track[] = []\n for (const key of clip.keys) {\n if (side === 'L') {\n if (key.L === 'mirror') {\n if (key.R) out.push({ t: key.t, key: key.R, mirror: true })\n } else if (key.L) out.push({ t: key.t, key: key.L, mirror: false })\n } else {\n const entry = key[side]\n if (entry) out.push({ t: key.t, key: entry, mirror: false })\n }\n }\n return out.sort((a, b) => a.t - b.t)\n}\n\nconst sampleTrack = (track: Track[], time: number): FirstPersonSampleChannel | undefined => {\n if (track.length === 0) return undefined\n const first = track[0]!\n const last = track[track.length - 1]!\n const of = (entry: Track): FirstPersonSampleChannel => ({ p: [...entry.key.p], r: [...entry.key.r], grip: entry.key.grip, w: entry.key.w ?? 1, mirror: entry.mirror })\n if (time <= first.t) return of(first)\n if (time >= last.t) return of(last)\n let index = 0\n while (index < track.length - 2 && time > track[index + 1]!.t) index++\n const a = track[index]!\n const b = track[index + 1]!\n const k = ease(Math.min(1, Math.max(0, (time - a.t) / (b.t - a.t))))\n const ga = a.key.grip\n const gb = b.key.grip\n return {\n p: mixTriple(a.key.p, b.key.p, k),\n r: mixTriple(a.key.r, b.key.r, k),\n grip: ga === undefined ? gb : gb === undefined ? ga : mix(ga, gb, k),\n w: mix(a.key.w ?? 1, b.key.w ?? 1, k),\n mirror: a.mirror,\n }\n}\n\n/** Where each driven channel is at `time`: between two keys of a channel the numbers are blended with the smoothstep of `samplePoseClip`; before its first key and after its last it holds them. */\nexport const sampleFirstPersonClip = (clip: Pick<FirstPersonClip, 'keys'>, time: number): FirstPersonSample => {\n const out: FirstPersonSample = {}\n for (const side of ['R', 'L', 'F'] as const) {\n const sample = sampleTrack(trackOf(clip, side), time)\n if (sample) out[side] = sample\n }\n return out\n}\n","{\n \"sources\": {\n \"store\": \"jogo.js G 66714-66734 (Z, ZMAX 6000, FIELDS)\",\n \"bands\": \"jogo.js G 62019-62020 (BANDS, HYST)\",\n \"flow\": \"jogo.js G 66735-66737, 66773-66825 (FLOW_R 192, flowStart, flowStep; budget 1 ms a frame, the first field in one go)\",\n \"update\": \"jogo.js G 66835-66977 hordeUpdate\",\n \"alerts\": \"jogo.js G 66978-66993 groan, screamAt\",\n \"attack\": \"jogo.js G 66996-67006 zAttack\",\n \"damage\": \"jogo.js G 67010-67045 zDamage\",\n \"kill\": \"jogo.js G 67046-67059 zKill, inchadoBurst\",\n \"corpses\": \"jogo.js G 62021 (CORPOS_MAX 400, CORPO_VIDA 900), 67062-67075 corpseAdd, corpsesUpdate\",\n \"population\": \"jogo.js G 67139-67177 spawnAway, populationUpdate (popTarget 1200, migration 120 every 90 s, the first after 60 s)\",\n \"pile\": \"jogo.js G 67184-67227 (PILE_STEP 0.48, PILE_TOP 3.7, PILE_MAX 160, groundAt, zBlockedAt, tryStep, stepRelease, pileBlast)\",\n \"trail\": \"jogo.js G 67238-67246 trailUpdate\",\n \"abilities\": \"jogo.js G 67266-67356 zAbility, zChaseSpeed, zHurtOther, killQ\",\n \"frame\": \"jogo.js G 67377-67402 zFrame\",\n \"cadence\": \"jogo.js G 65507 walkCad\"\n },\n \"bands\": [\n { \"max\": 35, \"dt\": 0.05 },\n { \"max\": 120, \"dt\": 0.2 },\n { \"max\": 400, \"dt\": 1 },\n { \"max\": null, \"dt\": 5 }\n ],\n \"numbers\": {\n \"capacity\": 6000,\n \"hysteresis\": 10,\n \"flowRadius\": 192,\n \"flowBudgetMs\": 1,\n \"hashCell\": 1,\n \"nightSight\": 0.4,\n \"crouchSight\": 0.6,\n \"sightCone\": 0.5,\n \"sightNear\": 2.5,\n \"loseAfter\": 15,\n \"hearingBase\": 40,\n \"heardLost\": 5,\n \"alertRadius\": 8,\n \"alertDelay\": 0.2,\n \"alertDelayRandom\": 0.4,\n \"voiceDistance\": 40,\n \"voiceEvery\": 0.25,\n \"directRange\": 4,\n \"pyramidRange\": 14,\n \"screamSpeed\": 1.2,\n \"attackRange\": 1.15,\n \"attackHeight\": 1.3,\n \"attackSlots\": 6,\n \"attackEvery\": 1.5,\n \"crouchBite\": 1.3,\n \"crawlDamage\": 0.6,\n \"wanderSpan\": 24,\n \"crawlSpeed\": 0.4,\n \"limpSpeed\": 0.5,\n \"separation\": 0.31,\n \"separationPush\": 2.2,\n \"doorDamage\": 10,\n \"doorMessageDistance\": 25,\n \"skullMinDamage\": 20,\n \"skullTakeDamage\": 60,\n \"skullTakeWithin\": { \"12ga\": 5 },\n \"jawDamage\": 25,\n \"jawChance\": 0.3,\n \"hitStunDamage\": 35,\n \"hitStun\": 0.45,\n \"dormantWake\": 3,\n \"dormantWakeBehind\": 10,\n \"pileStep\": 0.48,\n \"pileTop\": 3.7,\n \"pileMax\": 160,\n \"pileReleaseDistance\": 30,\n \"gravity\": 9.8,\n \"fallStun\": 3.2,\n \"climbSpeed\": 1.9,\n \"cadenceBase\": 0.5,\n \"cadencePerSpeed\": 0.3,\n \"corpsesMax\": 400,\n \"corpseSeconds\": 900,\n \"trailSeconds\": 120,\n \"trailStep\": 1.5,\n \"trailMax\": 200,\n \"population\": 1200,\n \"refillMin\": 70,\n \"refillMax\": 160,\n \"migrationFirst\": 60\n }\n}\n","import type { NavGrid } from './grid'\n\nexport type Door = {\n readonly x: number\n readonly z: number\n /** The leaf runs along `x` (front or back wall) or `z`. */\n readonly axis: 'x' | 'z'\n readonly width: number\n hp: number\n hpMax: number\n open: boolean\n broken: boolean\n planks: number\n readonly rect: readonly [number, number, number, number]\n /** Whatever the game hangs on it (the building it belongs to). */\n readonly data?: unknown\n}\n\nexport type DoorEventMap = {\n sound: { sound: 'wood'; volume: number; position: [number, number, number] }\n noise: { x: number; z: number; radius: number }\n effect: { effect: 'dust'; point: [number, number, number]; color: number }\n broken: { door: number }\n}\nexport type DoorEventName = keyof DoorEventMap\n\nexport type DoorNumbers = {\n /** Hit points of a door (G 63803 `PORTA_VIDA`). */\n readonly doorHp: number\n /** Hit points each plank adds (G 63803 `TABUA_VIDA`). */\n readonly plankHp: number\n readonly height: number\n readonly thickness: number\n readonly soundChance: number\n readonly soundVolume: number\n readonly breakVolume: number\n readonly breakNoise: number\n}\n\nexport const DOOR_DEFAULTS: DoorNumbers = { doorHp: 300, plankHp: 150, height: 2.2, thickness: 0.1, soundChance: 0.3, soundVolume: 0.8, breakVolume: 1.5, breakNoise: 30 }\n\nexport type Doors = {\n readonly list: readonly Door[]\n /** The door index at a grid cell, -1 for none. */\n at(cell: number): number\n add(x: number, z: number, axis: 'x' | 'z', width: number, data?: unknown): number\n /** Opens or closes: the cells become passable (3) or a closed door (5). A broken door stays open. */\n set(index: number, open: boolean): void\n /** An agent hits a closed door (G 64044 `doorHit`): the 30% knock, planks giving way, and the door breaking open. */\n hit(index: number, damage: number): void\n /** Nails a plank on (each adds `plankHp`). */\n plank(index: number): void\n on<K extends DoorEventName>(event: K, listener: (event: DoorEventMap[K]) => void): () => void\n}\n\n/** The doors of the game's buildings on a grid (G 63796-63816 `DOORS`, `doorAt`, `addDoor`, `setDoor`; G 64044 `doorHit`). */\nexport function createDoors(grid: NavGrid, options: { random: () => number; numbers?: Partial<DoorNumbers> }): Doors {\n const N: DoorNumbers = { ...DOOR_DEFAULTS, ...options.numbers }\n const random = options.random\n const list: Door[] = []\n const doorAt = new Int16Array(grid.n * grid.n).fill(-1)\n const listeners = new Map<DoorEventName, Set<(event: never) => void>>()\n const emit = <K extends DoorEventName>(event: K, payload: DoorEventMap[K]): void => {\n for (const listener of listeners.get(event) ?? []) (listener as (event: DoorEventMap[K]) => void)(payload)\n }\n const set = (index: number, open: boolean): void => {\n const d = list[index]\n if (!d) return\n d.open = open\n grid.fillRect(...d.rect, open || d.broken ? 3 : 5, N.height)\n }\n return {\n list,\n at: (cell) => (cell < 0 ? -1 : doorAt[cell]!),\n add(x, z, axis, width, data) {\n const i = list.length\n const t = N.thickness\n const x0 = axis === 'x' ? x - width / 2 : x - t\n const x1 = axis === 'x' ? x + width / 2 : x + t\n const z0 = axis === 'x' ? z - t : z - width / 2\n const z1 = axis === 'x' ? z + t : z + width / 2\n list.push({ x, z, axis, width, hp: N.doorHp, hpMax: N.doorHp, open: false, broken: false, planks: 0, rect: [x0, z0, x1, z1], data })\n grid.fillRect(x0, z0, x1, z1, 5, N.height)\n const a = Math.floor((x0 + grid.half) / grid.cell)\n const b = Math.ceil((x1 + grid.half) / grid.cell)\n const c = Math.floor((z0 + grid.half) / grid.cell)\n const e = Math.ceil((z1 + grid.half) / grid.cell)\n for (let gz = c; gz < e; gz++) for (let gx = a; gx < b; gx++) doorAt[gz * grid.n + gx] = i\n return i\n },\n set,\n hit(index, damage) {\n const d = list[index]\n if (!d || d.open || d.broken) return\n d.hp -= damage\n if (random() < N.soundChance) emit('sound', { sound: 'wood', volume: N.soundVolume, position: [d.x, 1, d.z] })\n if (d.planks && d.hp < d.hpMax - d.planks * N.plankHp + N.plankHp * (d.planks - 1)) d.planks = Math.max(0, d.planks - 1)\n if (d.hp <= 0) {\n d.broken = true\n d.planks = 0\n set(index, true)\n emit('noise', { x: d.x, z: d.z, radius: N.breakNoise })\n emit('sound', { sound: 'wood', volume: N.breakVolume, position: [d.x, 1, d.z] })\n emit('effect', { effect: 'dust', point: [d.x, 1, d.z], color: 0x8a6a4a })\n emit('broken', { door: index })\n }\n },\n plank(index) {\n const d = list[index]\n if (!d) return\n d.planks += 1\n d.hp += N.plankHp\n d.hpMax += N.plankHp\n },\n on(event, listener) {\n let set_ = listeners.get(event)\n if (!set_) listeners.set(event, (set_ = new Set()))\n set_.add(listener as (event: never) => void)\n return () => void set_.delete(listener as (event: never) => void)\n },\n }\n}\n","/** Occupancy codes of a grid cell (G 62162: 0 free, 1 solid, 2 low wall that can be climbed, 3 open door, 4 glass; 5 closed door, G 63816). */\nexport const CELL = { FREE: 0, SOLID: 1, LOW_WALL: 2, OPEN_DOOR: 3, GLASS: 4, CLOSED_DOOR: 5 } as const\n\nexport type NavGridOptions = {\n /** Side of the square world in metres, centred on the origin (default 768, the game's world). */\n readonly size?: number\n /** Cell side in metres (default 0.5). */\n readonly cell?: number\n /** Fill a solid border of `borderWidth` m and `borderHeight` m around the world, as the game does (default true). */\n readonly border?: boolean\n}\n\n/**\n * The navigation and collision grid of the game (G 62158-62170): a square world of cells with an occupancy code and a height. Agents, the player,\n * the flow field and line of sight all read it. Buildings write it with `fillRect`.\n */\nexport type NavGrid = {\n readonly size: number\n readonly half: number\n readonly cell: number\n /** Cells per side. */\n readonly n: number\n readonly occ: Uint8Array\n readonly occH: Float32Array\n cellOf(x: number, z: number): number\n /** Solid for a body at height `y` (default 0.5): outside the world, or an occupied cell that is not an open door and is taller than `y`. */\n solidAt(x: number, z: number, y?: number): boolean\n /** The occupancy code at (x, z), -1 outside. */\n cellKind(x: number, z: number): number\n fillRect(x0: number, z0: number, x1: number, z1: number, kind: number, height: number): void\n /** Blocked for a walking agent: outside, or anything but free and open door (G 66826 `zBlocked`). */\n blocked(x: number, z: number): boolean\n /** Nothing solid and taller than 1.5 m between the two points, sampled every 0.5 m (G 66828 `losClear`). */\n losClear(x0: number, z0: number, x1: number, z1: number): boolean\n}\n\n/** The cell index of a world position for a square grid of `n` cells of `cell` metres centred on the origin, -1 outside. */\nexport const createCellOf =\n (half: number, cell: number, n: number) =>\n (x: number, z: number): number => {\n const gx = Math.floor((x + half) / cell)\n const gz = Math.floor((z + half) / cell)\n return gx < 0 || gz < 0 || gx >= n || gz >= n ? -1 : gz * n + gx\n }\n\nconst BORDER_WIDTH = 1\nconst BORDER_HEIGHT = 50\nconst LOS_STEP = 0.5\nconst LOS_HEIGHT = 1.5\n\nexport function createNavGrid(options: NavGridOptions = {}): NavGrid {\n const size = options.size ?? 768\n const half = size / 2\n const cell = options.cell ?? 0.5\n const n = size / cell\n const occ = new Uint8Array(n * n)\n const occH = new Float32Array(n * n)\n const cellOf = createCellOf(half, cell, n)\n const solidAt = (x: number, z: number, y = 0.5): boolean => {\n const c = cellOf(x, z)\n return c < 0 || (occ[c] !== 0 && occ[c] !== 3 && occH[c]! > y)\n }\n const fillRect = (x0: number, z0: number, x1: number, z1: number, kind: number, h: number): void => {\n const a = Math.floor((x0 + half) / cell)\n const b = Math.ceil((x1 + half) / cell)\n const c = Math.floor((z0 + half) / cell)\n const d = Math.ceil((z1 + half) / cell)\n for (let gz = Math.max(0, c); gz < Math.min(n, d); gz++)\n for (let gx = Math.max(0, a); gx < Math.min(n, b); gx++) {\n const i = gz * n + gx\n occ[i] = kind\n occH[i] = h\n }\n }\n if (options.border ?? true) {\n fillRect(-half, -half, half, -half + BORDER_WIDTH, 1, BORDER_HEIGHT)\n fillRect(-half, half - BORDER_WIDTH, half, half, 1, BORDER_HEIGHT)\n fillRect(-half, -half, -half + BORDER_WIDTH, half, 1, BORDER_HEIGHT)\n fillRect(half - BORDER_WIDTH, -half, half, half, 1, BORDER_HEIGHT)\n }\n return {\n size,\n half,\n cell,\n n,\n occ,\n occH,\n cellOf,\n solidAt,\n cellKind: (x, z) => {\n const c = cellOf(x, z)\n return c < 0 ? -1 : occ[c]!\n },\n fillRect,\n blocked(x, z) {\n const c = cellOf(x, z)\n return c < 0 || (occ[c] !== 0 && occ[c] !== 3)\n },\n losClear(x0, z0, x1, z1) {\n const d = Math.hypot(x1 - x0, z1 - z0)\n const steps = Math.ceil(d / LOS_STEP)\n for (let k = 1; k < steps; k++) {\n const t = k / steps\n const c = cellOf(x0 + (x1 - x0) * t, z0 + (z1 - z0) * t)\n if (c < 0 || (occ[c] === 1 && occH[c]! > LOS_HEIGHT)) return false\n }\n return true\n },\n }\n}\n","/** murmur3 fmix32: a full avalanche of the 32 bits. */\nexport const mix32 = (value: number): number => {\n let hash = value >>> 0\n hash ^= hash >>> 16\n hash = Math.imul(hash, 0x85ebca6b)\n hash ^= hash >>> 13\n hash = Math.imul(hash, 0xc2b2ae35)\n hash ^= hash >>> 16\n return hash >>> 0\n}\n\n/**\n * 0..1 from an identity, a step and a salt: the counter-based draw every physics feature uses (never the shared `random` stream, whose order is the game's).\n * The salt goes in before the step so two different `(step, salt)` pairs never share an input.\n */\nexport const unit = (identity: number, step: number, salt: number): number => (mix32((mix32(identity ^ salt) + Math.imul(step + 1, 0x9e3779b9)) >>> 0) >>> 0) / 4294967296\n\n/** The identity of the `serial`-th agent born into a crowd seeded with `seed`. */\nexport const identityOf = (seed: number, serial: number): number => mix32(seed ^ mix32(serial))\n\n/** One salt per use, so draws of the same agent at the same step stay independent. */\nexport const SALT = {\n FALL: 0x0f411,\n TRIP: 0x07219,\n LIMB: 0x1b100,\n REGION: 0x2e610,\n JAW: 0x3a7b0,\n CORPSE: 0x4c0de,\n SPIN: 0x5917a,\n SPEED: 0x6511d,\n REACTION: 0x7201c,\n STABILITY: 0x8b21e,\n TRIP_FACTOR: 0x93b5d,\n DRIVE: 0xa00d3,\n CADENCE: 0xb1c4e,\n AGGRESSION: 0xc2a97,\n IDLE: 0xd3e08,\n GAIT: 0xe4f19,\n} as const\n","/** What an agent is doing (G 66724 and 67180-67183): the store keeps the number. */\nexport const STATE = {\n WANDER: 0,\n CHASE: 1,\n GRAB: 2,\n STUNNED: 3,\n CALLING: 4,\n AIRBORNE: 5,\n DORMANT: 6,\n SWELLING: 7,\n SWING: 8,\n EATING: 9,\n PILE_STEP: 10,\n SEEKING_SOUND: 12,\n} as const\n\n/** Balance of an agent (physics): a state machine of its own, apart from `STATE`, so the game's behaviour machine stays as it is. */\nexport const BALANCE = { STANDING: 0, STAGGERING: 1, FALLEN: 2, GETTING_UP: 3, THROWN: 4 } as const\n\n/** Why an agent went down (index `cause - 1` in `stats.physics.falls`). */\nexport const FALL_CAUSE = { NONE: 0, PRESSURE: 1, SHOVE: 2, TRIP: 3, LANDING: 4, COLLAPSE: 5, BLAST: 6 } as const\n\n/** Body regions of a hit (G 67012): 0 head, 1 torso, 2 left arm, 3 right arm, 4 left leg, 5 right leg. */\nexport type Region = 0 | 1 | 2 | 3 | 4 | 5\n\n/**\n * Missing parts as bits (G 67010-67045): 1 skull, 2 left arm off, 4 right arm off, 8 left leg off, 16 right leg off, 32 left arm broken, 64 right arm broken,\n * 128 left leg broken, 256 right leg broken, 512 jaw.\n */\nexport const MISSING = { SKULL: 1, ARM_L: 2, ARM_R: 4, LEG_L: 8, LEG_R: 16, ARM_L_BROKEN: 32, ARM_R_BROKEN: 64, LEG_L_BROKEN: 128, LEG_R_BROKEN: 256, JAW: 512 } as const\n\n/**\n * The type traits: the special moves of a type, read in the game's order. A type without a trait does not have the move. Every number is the game's\n * (`jogo.js` G 67180-67430, the Bestiário table 3.1 and 12.7).\n */\nexport type CrowdTraits = {\n /** Jumps a low obstacle in the way while chasing (car, yard wall) up to `maxHeight` m (runner, leaper). */\n readonly vaultsLow?: { readonly maxHeight: number; readonly minRise: number; readonly clearance: number; readonly speedFactor: number }\n /** Pounce from `min` to `max` m in line of sight: cooldown, flight time, speed cap, the vertical speeds and the knock-down on landing (leaper). */\n readonly pounce?: { readonly min: number; readonly max: number; readonly cooldown: number; readonly time: number; readonly speedCap: number; readonly lift: number; readonly liftHigh: number; readonly highAbove: number; readonly landRange: number; readonly knock: number }\n /** A heavy swing: chance per attack, wind-up, reach and knock-down (brute). */\n readonly heavySwing?: { readonly chance: number; readonly windUp: number; readonly range: number; readonly knock: number }\n /** Multiplies the damage on doors (brute 3). */\n readonly doorDamage?: number\n /** Seconds stunned by a heavy hit (default `numbers.hitStun`; brute 0.25). */\n readonly hitStun?: number\n /** Only hears: goes to the source of a sound and hits whatever it meets on the way, other agents too (blind). */\n readonly soundSeeker?: { readonly arrive: number; readonly loseChase: number; readonly hitRadius2: number }\n /** Calls the crowd: wind-up, radius, cooldown, the boost it gives the target, and running from the fight afterwards (screamer). */\n readonly caller?: { readonly range: number; readonly windUp: number; readonly radius: number; readonly cooldown: number; readonly boost: number; readonly fleeAbove: number; readonly fleeRange: number }\n /** Sets the target on fire on a hit (charred). */\n readonly burns?: { readonly seconds: number }\n /** Loses a share of its health the first time it enters water (charred 0.7). */\n readonly waterWeak?: { readonly keep: number; readonly cloud: number }\n /** Waits under water and pulls the target down (drowned). */\n readonly submerged?: { readonly depth: number; readonly wake: number; readonly swim: number; readonly surface: number }\n /** Plays dead and wakes close or behind the target, then sprints for a while (stalker). */\n readonly ambush?: { readonly chance: number; readonly wake: number; readonly wakeBehind: number; readonly behindCos: number; readonly sprint: number; readonly sprintSeconds: number; readonly slow: number }\n /** Swells and bursts close to the target: blinds and marks it with a scent (bloater). */\n readonly burst?: { readonly range: number; readonly swell: number; readonly radius: number; readonly blind: number; readonly scent: number; readonly noiseNear: number; readonly noise: number; readonly cloudColor: number; readonly bloodColor: number; readonly bloodDry: number }\n /** Eats corpses: search radius, reach, seconds, how many, what it gains (devourer). */\n readonly eatsCorpses?: { readonly max: number; readonly search: number; readonly reach: number; readonly seconds: number; readonly grabRange: number; readonly health: number; readonly grow: number; readonly rest: number; readonly retry: number; readonly defend: number; readonly approach: number }\n /** Follows the target's blood trail and leads agents of the type `leads` along (sniffer). */\n readonly tracker?: { readonly range: number; readonly leads: string; readonly max: number; readonly leadRadius: number }\n /** Never older than this age index (runner: recent at most). */\n readonly maxAge?: number\n /** The groan sound id (runner `runner_groan`; default `groan`). */\n readonly voice?: string\n /** How much it adds to the target's panic (runner 2.5). */\n readonly panicWeight?: number\n /** Pulls the target into water when it grabs there (drowned). */\n readonly drownGrab?: boolean\n /** Body colour and skin override at birth (charred, drowned). */\n readonly color?: number\n readonly skin?: number\n}\n\n/** One type of agent (a zombie type, a cow, a civilian). The undead pack (`undeadCrowdTypes`) gives the 13 types of the game. */\nexport type CrowdType = {\n readonly id: string\n /** Weight in the draw of an agent without a forced type (0: never drawn). */\n readonly weight: number\n readonly health: number\n readonly speed: { readonly wander: number; readonly chase: number }\n readonly damage: number\n /** Chance per attack to grab (and bite). */\n readonly bite: number\n readonly multipliers: { readonly head: number; readonly torso: number; readonly arms: number; readonly legs: number }\n /** Metres. */\n readonly perception: { readonly sight: number; readonly hearing: number; readonly smell: number }\n readonly crawls?: boolean\n /** Height multiplier (brute 1.42). */\n readonly scale?: number\n /** Build multiplier (bloater 1.35; a scaled type without one is 1.3). */\n readonly build?: number\n /** Body variants of the type for the renderer (walker 4, runner 3, crawler and brute 2, others 1). */\n readonly variants?: number\n readonly eye?: number\n readonly traits?: CrowdTraits\n /** How the body takes a push (physics only; a type without it uses the defaults). */\n readonly balance?: CrowdBalanceTraits\n}\n\nexport type CrowdBalanceTraits = {\n /** Multiplies the critical pressure (default 1). */\n readonly stability?: number\n /** Chance of tripping when entering a cell with a body (default 0.04). */\n readonly trip?: number\n /** Force of its will against what is in front, in N (default 250). */\n readonly driveForce?: number\n /** False: already on the ground (a crawler). */\n readonly canFall?: boolean\n}\n\n/** A blast as the crowd reads it. */\nexport type CrowdBlast = {\n readonly point: readonly [number, number, number]\n /** J. */\n readonly energy: number\n /** m, how far it hurts. */\n readonly radius: number\n /** m, everything inside dies. */\n readonly lethalRadius?: number\n readonly source?: string\n /** Id of the chain this blast belongs to (game-destruction `chain`), for the counters. */\n readonly chain?: string\n}\n\n/** The age of a body (G IDADES): its share, and what it does to speed, health, limbs and senses. */\nexport type CrowdAge = { readonly id: string; readonly p: number; readonly speed: number; readonly health: number; readonly limb: number; readonly sight: number; readonly hearing: number; readonly blood: number; readonly bloodColor: number; readonly dryColor: number }\n/** What a body wears (G ROUPAS): its share, the vest against pistol and rifle classes, a helmet, a limb factor and the colour. */\nexport type CrowdOutfit = { readonly id: string; readonly p: number; readonly vest?: { readonly pistol: number; readonly rifle: number }; readonly helmet?: boolean; readonly limb?: number; readonly color: number | null }\n\n/** Everything the crowd draws a body from. */\nexport type CrowdPack = {\n readonly types: readonly CrowdType[]\n readonly ages: readonly CrowdAge[]\n readonly outfits: readonly CrowdOutfit[]\n /** Hit points of the segments, only for dismembering (G `SEG`). */\n readonly segments: { readonly head: number; readonly arm: number; readonly leg: number }\n /** Clothes colours of the outfits without one (G `CIVIL_COR`). */\n readonly palette: readonly number[]\n}\n\n/** The calibre of a projectile hit: its class (pistol or rifle, for vests and helmets), the skull distance (1: any) and an id. */\nexport type CrowdCaliber = { readonly id: string; readonly class: 'pistol' | 'rifle'; readonly skull: number }\nexport type CrowdHit = { readonly region: Region; readonly amount: number; readonly distance: number; readonly cut?: number; readonly caliber?: CrowdCaliber | null }\nexport type CrowdDamageResult = { readonly died: boolean; readonly headshot?: boolean }\n\n/**\n * Who the crowd hunts. `game-player`'s `Player` is one as it is; a game with its own player writes a small object with the same names.\n * Reads: `state`. Writes: the verbs, and `state.burnTimer`, `state.blind`, `state.scentTimer`, `state.screamBoost`.\n */\nexport interface CrowdTarget {\n readonly state: {\n x: number\n z: number\n y: number\n floor: number\n alive: boolean\n turned: boolean\n crouch: boolean\n bleeding: number\n scentTimer: number\n screamBoost: number\n grabbedBy: number\n blind: number\n burnTimer: number\n /** The body's yaw; the view looks the other way (`yaw - π`). */\n yaw: number\n }\n grab(by: number, noJaw?: boolean, drown?: boolean): void\n hurt(amount: number, by?: number): void\n knock(seconds: number): void\n release(): void\n}\n\nexport type CrowdSoundId = 'groan' | 'runner_groan' | 'scream_hoarse' | 'step' | 'impact' | 'flesh' | 'wood' | (string & {})\nexport type CrowdEffectId = 'spark' | 'blood' | 'limb' | 'cloud' | 'dust' | (string & {})\nexport type CrowdMessageId = 'called' | 'burst_hit' | 'swing_knock' | 'pounce_knock' | 'migration' | 'door_broken' | (string & {})\n\nexport type CrowdEventMap = {\n sound: { sound: CrowdSoundId; volume: number; position: [number, number, number]; distance: number }\n effect: { effect: CrowdEffectId; point: [number, number, number]; agent?: number; region?: number; severed?: boolean; radius?: number; color?: number; amount?: number; died?: boolean; age?: number }\n message: { id: CrowdMessageId; seconds: number; type?: string }\n spawn: { agent: number; type: string }\n alert: { agent: number }\n kill: { agent: number; type: string; headshot: boolean; x: number; z: number }\n remove: { agent: number; moved: number }\n scream: { agent: number; x: number; z: number }\n fall: { agent: number; cause: number; x: number; z: number; identity: number }\n blasted: { point: [number, number, number]; radius: number; reached: number; killed: number; thrown: number; fallen: number; chain?: string }\n}\nexport type CrowdEventName = keyof CrowdEventMap\n\n/** A body on the ground (G 67062 `CP`). */\nexport type Corpse = {\n readonly x: number\n readonly z: number\n readonly yaw: number\n readonly height: number\n readonly build: number\n readonly missing: number\n readonly color: number\n /** Body variant of the renderer, -1 for a body that was not an agent (a civilian). */\n readonly variant: number\n readonly skin: number\n readonly seed: number\n readonly named: number\n}\n","import { SALT, unit } from '../identity'\nimport { BALANCE, FALL_CAUSE, STATE } from '../types'\nimport type { PhysicsContext } from './context'\nimport type { CrowdBalanceOptions } from './options'\n\nconst GRAVITY_FORCE = 9.8\nconst GROUND_MASS_FACTOR = 3\n/** Speed above which entering a cell with a body can trip (m/s), and the speed the trip chance is given for. */\nconst TRIP_MIN_SPEED = 0.5\nconst TRIP_REFERENCE_SPEED = 1.4\nconst TRIP_MOUND = 150\nconst TRIP_CLEARANCE = 0.1\n/** Drop height (m) that makes a body stagger, and that knocks it down (`design-chain-horde.md` §6, item 5). */\nconst COLLAPSE_STAGGER = 0.6\nconst COLLAPSE_FALL = 1\nconst CAUSE_ORDER = [FALL_CAUSE.PRESSURE, FALL_CAUSE.SHOVE, FALL_CAUSE.TRIP, FALL_CAUSE.LANDING, FALL_CAUSE.COLLAPSE, FALL_CAUSE.BLAST]\nconst CHAIN_WINDOW_STEPS = 240\n\n/** A fall that waited for its turn (the ceiling of falls per step) is dropped after this many steps. */\nconst CARRY_STEPS = 24\n/** Pressure and trip are drawn again every step; the rest (a shove, a landing, a collapse, a blast) is a fact and waits for its turn. */\nconst isCarried = (cause: number): boolean => cause !== FALL_CAUSE.PRESSURE && cause !== FALL_CAUSE.TRIP\n\ntype FallRequest = { index: number; identity: number; cause: number; score: number; born: number }\n\nexport type Balance = {\n /** Asks for agent `i` to go down; the ones with the highest `score` go first, up to the ceiling of the step. */\n requestFall(i: number, cause: number, score: number): void\n /** Agent `i` starts staggering if it stands. */\n stagger(i: number): void\n /** A push of `speed` m/s (an increment) on a standing agent: staggers it, or asks for the fall, by its stability. */\n shove(i: number, speed: number, cause: number): void\n step(options: Required<CrowdBalanceOptions>, contactShove: Float32Array): void\n /** Carries out the requests of the step, in the order of the score. */\n flush(): void\n /** A body landed after a drop of `height` m (a pile or a mound gave way). */\n landed(i: number, height: number): void\n /** Falls requested and not yet carried out. */\n pending(): number\n}\n\nexport function createBalance(ctx: PhysicsContext): Balance {\n const { store } = ctx\n const requests: FallRequest[] = []\n const balanceOptions = (): Required<CrowdBalanceOptions> => ctx.options.balance!\n\n const stabilityOf = (i: number): number => (ctx.typeOf(i).balance?.stability ?? 1) * store.stability[i]!\n const canFall = (i: number): boolean => ctx.typeOf(i).balance?.canFall !== false\n const criticalOf = (i: number): number => balanceOptions().criticalFactor * store.mass[i]! * GRAVITY_FORCE * stabilityOf(i)\n\n const requestFall = (i: number, cause: number, score: number): void => {\n if (!canFall(i)) return\n requests.push({ index: i, identity: store.identity[i]!, cause, score, born: ctx.step })\n }\n\n const stagger = (i: number): void => {\n if (store.balance[i] !== BALANCE.STANDING || !canFall(i)) return\n store.balance[i] = BALANCE.STAGGERING\n store.balanceTimer[i] = 0\n }\n\n const shove = (i: number, speed: number, cause: number): void => {\n const options = balanceOptions()\n const knockSpeed = options.shoveFall * stabilityOf(i)\n if (store.balance[i] !== BALANCE.STANDING && store.balance[i] !== BALANCE.STAGGERING) return\n if (speed > knockSpeed) requestFall(i, cause, speed / knockSpeed)\n else if (speed > options.shoveStagger) stagger(i)\n }\n\n const countsAsChainFall = (i: number, cause: number): boolean => {\n if (cause === FALL_CAUSE.PRESSURE || cause === FALL_CAUSE.BLAST) return false\n if (ctx.step - ctx.lastBlastStep > CHAIN_WINDOW_STEPS) return false\n const direct = store.blastStep[i] === ctx.lastBlastStep ? store.directShove[i]! : 0\n return direct < balanceOptions().shoveStagger\n }\n\n const goDown = (i: number, cause: number): void => {\n ctx.stats.falls[CAUSE_ORDER.indexOf(cause as (typeof CAUSE_ORDER)[number])]!++\n if (countsAsChainFall(i, cause)) ctx.stats.fallsBeyondBlast++\n if (store.state[i] === STATE.PILE_STEP) ctx.releasePileStep(i)\n store.balance[i] = BALANCE.FALLEN\n store.balanceTimer[i] = 0\n store.fallCause[i] = cause\n store.vx[i] = store.vz[i] = 0\n store.pushX[i]! /= GROUND_MASS_FACTOR\n store.pushZ[i]! /= GROUND_MASS_FACTOR\n store.aimX[i] = store.aimZ[i] = 0\n const target = ctx.target()\n if (target && target.state.grabbedBy === i) target.release()\n ctx.emit('fall', { agent: i, cause, x: store.x[i]!, z: store.z[i]!, identity: store.identity[i]! })\n }\n\n const flush = (): void => {\n if (!requests.length) return\n const options = balanceOptions()\n requests.sort((a, b) => b.score - a.score || a.identity - b.identity)\n let fallen = ctx.stats.fallen\n let fallsLeft = options.maxFallsPerStep\n const waiting: FallRequest[] = []\n for (const request of requests) {\n const i = request.index\n if (i >= store.n || store.identity[i] !== request.identity) continue\n const balance = store.balance[i]\n if (balance !== BALANCE.STANDING && balance !== BALANCE.STAGGERING) continue\n if (fallsLeft > 0 && fallen < options.maxFallen) {\n goDown(i, request.cause)\n fallsLeft--\n fallen++\n continue\n }\n stagger(i)\n if (isCarried(request.cause) && ctx.step - request.born < CARRY_STEPS) waiting.push(request)\n }\n requests.length = 0\n requests.push(...waiting)\n }\n\n const hasBodyOnTop = (i: number): boolean => {\n const cell = ctx.grid.cellOf(store.x[i]!, store.z[i]!)\n if (cell < 0) return false\n const options = ctx.options.mound\n if (!options) return false\n const top = store.y[i]! + options.bodyThickness * store.build[i]!\n return ctx.moundAt(cell) / 1000 > top + 0.05 || ctx.weightOn(i) > 0\n }\n\n const stepStanding = (i: number, ratio: number, contactShove: number, options: Required<CrowdBalanceOptions>): void => {\n if (contactShove > options.shoveFall * stabilityOf(i)) return requestFall(i, FALL_CAUSE.SHOVE, contactShove / (options.shoveFall * stabilityOf(i)))\n if (ratio > options.staggerEnter || contactShove > options.shoveStagger) stagger(i)\n else tryTrip(i)\n }\n\n const tryTrip = (i: number): void => {\n const cell = ctx.grid.cellOf(store.x[i]!, store.z[i]!)\n if (cell < 0 || cell === ctx.grid.cellOf(store.prevX[i]!, store.prevZ[i]!)) return\n const mound = ctx.moundAt(cell)\n if (mound < TRIP_MOUND || store.y[i]! >= mound / 1000 - TRIP_CLEARANCE) return\n const speed = Math.hypot(store.vx[i]! + store.pushX[i]!, store.vz[i]! + store.pushZ[i]!)\n if (speed <= TRIP_MIN_SPEED) return\n const chance = (ctx.typeOf(i).balance?.trip ?? 0.04) * store.tripFactor[i]! * (speed / TRIP_REFERENCE_SPEED)\n if (unit(store.identity[i]!, ctx.step, SALT.TRIP) < chance) requestFall(i, FALL_CAUSE.TRIP, 1)\n }\n\n const stepStaggering = (i: number, ratio: number, contactShove: number, options: Required<CrowdBalanceOptions>): void => {\n store.balanceTimer[i]! += ctx.h\n if (contactShove > options.shoveFall * stabilityOf(i)) return requestFall(i, FALL_CAUSE.SHOVE, contactShove / (options.shoveFall * stabilityOf(i)))\n if (ratio > 1 && unit(store.identity[i]!, ctx.step, SALT.FALL) < 1 - Math.exp(-options.fallRate * (ratio - 1) ** 2 * ctx.h)) return requestFall(i, FALL_CAUSE.PRESSURE, ratio)\n if (ratio < options.staggerLeave && store.balanceTimer[i]! >= options.staggerMin) {\n store.balance[i] = BALANCE.STANDING\n store.balanceTimer[i] = 0\n }\n }\n\n const stepFallen = (i: number, ratio: number, options: Required<CrowdBalanceOptions>): void => {\n store.balanceTimer[i]! += ctx.h\n if (store.balanceTimer[i]! < options.fallenMin + store.reaction[i]! || ratio >= options.releaseFactor || hasBodyOnTop(i)) return\n store.balance[i] = BALANCE.GETTING_UP\n store.balanceTimer[i] = 0\n }\n\n const step = (options: Required<CrowdBalanceOptions>, contactShove: Float32Array): void => {\n const smoothing = 1 - Math.exp(-ctx.h / options.pressureTau)\n let fallen = 0\n let staggering = 0\n for (let i = 0; i < store.n; i++) {\n const state = store.balance[i]!\n if (state === BALANCE.THROWN) continue\n store.pressure[i]! += (store.field[i]! - store.pressure[i]!) * smoothing\n const ratio = store.pressure[i]! / criticalOf(i)\n if (state === BALANCE.STANDING) stepStanding(i, ratio, contactShove[i]!, options)\n else if (state === BALANCE.STAGGERING) stepStaggering(i, ratio, contactShove[i]!, options)\n else if (state === BALANCE.FALLEN) stepFallen(i, ratio, options)\n else if ((store.balanceTimer[i]! += ctx.h) >= options.getUpSeconds) {\n store.balance[i] = BALANCE.STANDING\n store.balanceTimer[i] = 0\n }\n if (store.balance[i] === BALANCE.STAGGERING) staggering++\n else if (store.balance[i] === BALANCE.FALLEN || store.balance[i] === BALANCE.GETTING_UP) fallen++\n }\n ctx.stats.fallen = fallen\n ctx.stats.staggering = staggering\n }\n\n const landed = (i: number, height: number): void => {\n if (store.balance[i] !== BALANCE.STANDING && store.balance[i] !== BALANCE.STAGGERING) return\n if (height > COLLAPSE_FALL) requestFall(i, FALL_CAUSE.COLLAPSE, height / COLLAPSE_FALL)\n else if (height > COLLAPSE_STAGGER) stagger(i)\n }\n\n return { requestFall, stagger, shove, step, flush, landed, pending: () => requests.length }\n}\n","import { blastShove } from '@adatechnology/game-contracts'\n\nimport { SALT, unit } from '../identity'\nimport { BALANCE, FALL_CAUSE, MISSING, STATE } from '../types'\nimport type { CrowdBlast, Region } from '../types'\nimport type { Balance } from './balance'\nimport type { PhysicsContext } from './context'\nimport type { Thrown, ThrownCandidate } from './thrown'\n\nconst CENTRE_HEIGHT = 1\nconst DISMEMBER_SHARE = 0.5\nconst LIMB_CHANCE = 0.5\nconst STUN_BASE = 0.45\nconst BLOOD_PER_BODY = 60\nconst LIMBS = [\n { bit: MISSING.ARM_L, region: 2 },\n { bit: MISSING.ARM_R, region: 3 },\n { bit: MISSING.LEG_L, region: 4 },\n { bit: MISSING.LEG_R, region: 5 },\n] as const\n\nexport type Blasts = {\n enqueue(blast: CrowdBlast): void\n /** Resolves up to `maxBlastsPerStep` blasts in one linear pass over the agents; the rest waits for the next step. */\n resolve(): void\n pending(): number\n}\n\ntype BlastTally = { reached: number; killed: number; fallen: number; limbEffects: number; bloodEffects: number; bloodSum: number }\n\nexport type BlastHooks = {\n readonly balance: Balance | null\n readonly thrown: Thrown | null\n pileBlast(x: number, z: number, radius: number): void\n}\n\nexport function createBlasts(ctx: PhysicsContext, hooks: BlastHooks): Blasts {\n const { store } = ctx\n const options = ctx.options.blast\n const queue: CrowdBlast[] = []\n\n const emitLimb = (tally: BlastTally, i: number, region: number): void => {\n if (tally.limbEffects++ >= options.limbEffectsPerBlast) return\n ctx.emit('effect', { effect: 'limb', point: [store.x[i]!, store.height[i]! * 0.9, store.z[i]!], agent: i, region, severed: true })\n }\n\n const emitBlood = (tally: BlastTally, i: number): void => {\n if (tally.bloodEffects++ >= options.bloodEffectsPerBlast) {\n tally.bloodSum += BLOOD_PER_BODY\n return\n }\n ctx.emit('effect', { effect: 'blood', point: [store.x[i]!, store.height[i]! * 1.2, store.z[i]!], agent: i, amount: BLOOD_PER_BODY, died: true })\n }\n\n const kill = (i: number, blast: CrowdBlast, distance: number, tally: BlastTally): void => {\n tally.killed++\n store.hp[i] = 0\n if (blast.lethalRadius !== undefined && distance < DISMEMBER_SHARE * blast.lethalRadius) {\n for (let k = 0; k < LIMBS.length; k++) {\n if (unit(store.identity[i]!, ctx.step, SALT.LIMB + k) >= LIMB_CHANCE) continue\n store.missing[i]! |= LIMBS[k]!.bit\n emitLimb(tally, i, LIMBS[k]!.region)\n }\n }\n emitBlood(tally, i)\n ctx.deferKill(i)\n }\n\n /** Damage by the falloff, and a blow to a limb with half of it; returns true when the agent died. */\n const hurt = (i: number, blast: CrowdBlast, distance: number, index: number): boolean => {\n const falloff = (1 - distance / blast.radius) ** 2\n const amount = (blast.energy / 1000) * options.pointsPerKilojoule * falloff\n const draw = (salt: number) => () => unit(store.identity[i]!, ctx.step, salt)\n if (ctx.damage(i, { region: 1, amount, distance, cut: falloff }, draw(SALT.JAW + index)).died) return true\n const region = Math.floor(unit(store.identity[i]!, ctx.step, SALT.REGION + index) * 6) as Region\n return ctx.damage(i, { region, amount: amount / 2, distance, cut: falloff }, draw(SALT.JAW + 8 + index)).died\n }\n\n /** Sums the shove of every blast on the agent: `[vx, vy, vz]` of the body, or undefined when no blast moves it. */\n const shoveOf = (i: number, blasts: readonly CrowdBlast[]): { vx: number; vy: number; vz: number; falloff: number } | undefined => {\n const position: [number, number, number] = [store.x[i]!, store.y[i]! + CENTRE_HEIGHT * store.height[i]!, store.z[i]!]\n let vx = 0\n let vy = 0\n let vz = 0\n let falloff = 0\n for (const blast of blasts) {\n const shove = blastShove({ point: blast.point as [number, number, number], energy: blast.energy, radius: blast.radius }, { position, mass: store.mass[i]! }, options.shove)\n if (!shove) continue\n vx += shove.speed * shove.direction[0]\n vy += shove.speed * shove.direction[1]\n vz += shove.speed * shove.direction[2]\n falloff = Math.max(falloff, shove.falloff)\n }\n return vx || vy || vz ? { vx, vy, vz, falloff } : undefined\n }\n\n const distanceTo = (i: number, blast: CrowdBlast): number => Math.hypot(store.x[i]! - blast.point[0], store.y[i]! + CENTRE_HEIGHT * store.height[i]! - blast.point[1], store.z[i]! - blast.point[2])\n\n const applyShove = (i: number, shove: { vx: number; vy: number; vz: number; falloff: number }, candidates: ThrownCandidate[], tallies: BlastTally[], blasts: readonly CrowdBlast[]): void => {\n const horizontal = Math.hypot(shove.vx, shove.vz)\n const speed = Math.hypot(horizontal, shove.vy)\n store.directShove[i] = horizontal\n store.blastStep[i] = ctx.step\n const { balance, thrown } = hooks\n if (thrown && speed >= options.throwSpeed && (store.x[i]! - ctx.centre.x) ** 2 + (store.z[i]! - ctx.centre.z) ** 2 <= ctx.options.thrown!.radius ** 2) {\n candidates.push({ index: i, speed, vx: shove.vx, vy: shove.vy, vz: shove.vz, distance: Math.hypot(store.x[i]! - ctx.centre.x, store.z[i]! - ctx.centre.z) })\n return\n }\n if (store.hp[i]! <= 0) return\n if (!balance) {\n store.state[i] = STATE.STUNNED\n store.stateTimer[i] = STUN_BASE + shove.falloff\n store.vx[i] = shove.vx\n store.vz[i] = shove.vz\n return\n }\n store.pushX[i]! += shove.vx\n store.pushZ[i]! += shove.vz\n balance.shove(i, horizontal, FALL_CAUSE.BLAST)\n for (let b = 0; b < blasts.length; b++) if (distanceTo(i, blasts[b]!) <= blasts[b]!.radius && horizontal > ctx.options.balance!.shoveFall) tallies[b]!.fallen++\n }\n\n const affect = (i: number, blasts: readonly CrowdBlast[], tallies: BlastTally[], candidates: ThrownCandidate[]): void => {\n let dead = store.hp[i]! <= 0\n for (let b = 0; b < blasts.length && !dead; b++) {\n const blast = blasts[b]!\n const distance = distanceTo(i, blast)\n if (distance > blast.radius) continue\n tallies[b]!.reached++\n if (blast.lethalRadius !== undefined && distance < blast.lethalRadius) kill(i, blast, distance, tallies[b]!)\n else if (hurt(i, blast, distance, b)) tallies[b]!.killed++\n dead = store.hp[i]! <= 0 || dead\n }\n const shove = shoveOf(i, blasts)\n if (shove) applyShove(i, shove, candidates, tallies, blasts)\n }\n\n const resolve = (): void => {\n if (!queue.length) return\n const blasts = queue.splice(0, options.maxBlastsPerStep)\n const tallies: BlastTally[] = blasts.map(() => ({ reached: 0, killed: 0, fallen: 0, limbEffects: 0, bloodEffects: 0, bloodSum: 0 }))\n const candidates: ThrownCandidate[] = []\n ctx.lastBlastStep = ctx.step\n ctx.stats.blastChecks = store.n * blasts.length\n for (let i = 0; i < store.n; i++) if (store.balance[i] !== BALANCE.THROWN) affect(i, blasts, tallies, candidates)\n const thrown = hooks.thrown?.assign(candidates) ?? 0\n blasts.forEach((blast, b) => {\n const tally = tallies[b]!\n if (tally.bloodSum > 0) ctx.emit('effect', { effect: 'blood', point: [blast.point[0], blast.point[1], blast.point[2]], amount: tally.bloodSum, died: true })\n hooks.pileBlast(blast.point[0], blast.point[2], blast.radius)\n ctx.emit('blasted', {\n point: [blast.point[0], blast.point[1], blast.point[2]],\n radius: blast.radius,\n reached: tally.reached,\n killed: tally.killed,\n thrown: b === 0 ? thrown : 0,\n fallen: tally.fallen,\n ...(blast.chain === undefined ? {} : { chain: blast.chain }),\n })\n if (blast.chain !== undefined) ctx.stats.chainLinks++\n })\n }\n\n return { enqueue: (blast) => void queue.push(blast), resolve, pending: () => queue.length }\n}\n","import type { NavGrid } from '../grid'\nimport { SALT, unit } from '../identity'\nimport type { CrowdStore } from '../store'\nimport { BALANCE, STATE } from '../types'\nimport type { CrowdContactOptions } from './options'\n\n/** A fallen body weighs as three standing ones against a push (it is on the ground, spread out). */\nconst GROUND_MASS_FACTOR = 3\nconst PAIR_MARGIN = 0.15\nconst MAX_LEVEL_GAP = 0.15\nconst PAIRS_PER_AGENT = 12\nconst MIN_DISTANCE = 1e-6\n\nexport type ContactParams = Required<CrowdContactOptions> & { readonly h: number }\nexport type ContactResult = { contacts: number; maxOverlap: number; overlapSum: number }\n\nexport type ContactWorkspace = {\n readonly head: Int32Array\n readonly next: Int32Array\n readonly touched: Int32Array\n touchedCount: number\n readonly pairI: Int32Array\n readonly pairJ: Int32Array\n pairCount: number\n readonly deltaX: Float32Array\n readonly deltaZ: Float32Array\n readonly count: Uint8Array\n readonly pushBeforeX: Float32Array\n readonly pushBeforeZ: Float32Array\n /** |Δpush| the contact gave to each agent in the last step. */\n readonly shove: Float32Array\n /** 1 for the agents that take part in the contact this step (`takesPartInContact`), read once. */\n readonly takesPart: Uint8Array\n /** The radius and the effective mass of each agent this step, in doubles: the balance does not change inside the contact step. */\n readonly radiusOf: Float64Array\n readonly massOf: Float64Array\n /** Per pair, valid for the positions of the current iteration: the reach, the distance, and the normal (only meaningful when the distance is under the reach) from `j` to `i`. */\n readonly pairReach: Float64Array\n readonly pairDistance: Float64Array\n readonly pairNormalX: Float64Array\n readonly pairNormalZ: Float64Array\n}\n\n/** The arrays the contact and the pressure read and write: the step needs no more of the store, so it can run over a copy of them (a worker). */\nexport type PhysicsStore = Pick<CrowdStore, 'x' | 'z' | 'y' | 'vx' | 'vz' | 'pushX' | 'pushZ' | 'mass' | 'build' | 'balance' | 'state' | 'band' | 'identity' | 'aimX' | 'aimZ' | 'field' | 'fieldPrev' | 'type' | 'driveFactor'>\nexport type PhysicsGrid = Pick<NavGrid, 'size' | 'half' | 'occ' | 'cellOf'>\n\nexport type ContactView = { n: number; readonly store: PhysicsStore; readonly grid: PhysicsGrid; readonly workspace: ContactWorkspace }\n\nexport const createContactWorkspace = (capacity: number, grid: PhysicsGrid): ContactWorkspace => ({\n head: new Int32Array(Math.floor(grid.size) ** 2).fill(-1),\n next: new Int32Array(capacity),\n touched: new Int32Array(capacity),\n touchedCount: 0,\n pairI: new Int32Array(capacity * PAIRS_PER_AGENT),\n pairJ: new Int32Array(capacity * PAIRS_PER_AGENT),\n pairCount: 0,\n deltaX: new Float32Array(capacity),\n deltaZ: new Float32Array(capacity),\n count: new Uint8Array(capacity),\n pushBeforeX: new Float32Array(capacity),\n pushBeforeZ: new Float32Array(capacity),\n shove: new Float32Array(capacity),\n takesPart: new Uint8Array(capacity),\n radiusOf: new Float64Array(capacity),\n massOf: new Float64Array(capacity),\n pairReach: new Float64Array(capacity * PAIRS_PER_AGENT),\n pairDistance: new Float64Array(capacity * PAIRS_PER_AGENT),\n pairNormalX: new Float64Array(capacity * PAIRS_PER_AGENT),\n pairNormalZ: new Float64Array(capacity * PAIRS_PER_AGENT),\n})\n\nexport const createContactView = (store: PhysicsStore, grid: PhysicsGrid): ContactView => ({ n: 0, store, grid, workspace: createContactWorkspace(store.x.length, grid) })\n\n/** In the contact: near the viewer, not a pile step, not asleep, not in the air as a thrown body. */\nexport const takesPartInContact = (store: PhysicsStore, i: number): boolean => store.band[i] === 0 && store.balance[i] !== BALANCE.THROWN && store.state[i] !== STATE.PILE_STEP && store.state[i] !== STATE.DORMANT\n\nconst isOnGround = (store: PhysicsStore, i: number): boolean => store.balance[i] === BALANCE.FALLEN || store.balance[i] === BALANCE.GETTING_UP\n\nconst effectiveMass = (store: PhysicsStore, i: number): number => store.mass[i]! * (isOnGround(store, i) ? GROUND_MASS_FACTOR : 1)\n\nconst bodyRadius = (store: PhysicsStore, i: number, radius: number): number => radius * store.build[i]!\n\n/** What each agent is for this step: in the contact or not, its radius and its effective mass. */\nfunction readBodies(view: ContactView, radius: number): void {\n const { store, workspace } = view\n const { takesPart, radiusOf, massOf } = workspace\n for (let i = 0; i < view.n; i++) {\n const takes = takesPartInContact(store, i)\n takesPart[i] = takes ? 1 : 0\n radiusOf[i] = bodyRadius(store, i, radius)\n massOf[i] = effectiveMass(store, i)\n }\n}\n\n/** The agents of band R0 into the 1 m hash, clearing only the cells the last step used. */\nexport function rebuildHash(view: ContactView): void {\n const { store, grid, workspace } = view\n const side = Math.floor(grid.size)\n for (let k = 0; k < workspace.touchedCount; k++) workspace.head[workspace.touched[k]!] = -1\n workspace.touchedCount = 0\n for (let i = 0; i < view.n; i++) {\n if (!workspace.takesPart[i]) continue\n const hx = Math.floor(store.x[i]! + grid.half)\n const hz = Math.floor(store.z[i]! + grid.half)\n if (hx < 0 || hz < 0 || hx >= side || hz >= side) continue\n const cell = hz * side + hx\n if (workspace.head[cell] === -1) workspace.touched[workspace.touchedCount++] = cell\n workspace.next[i] = workspace.head[cell]!\n workspace.head[cell] = i\n }\n}\n\n/** The cells scanned from a cell for pairs: itself (the agents after the first) and four neighbours, so that every pair of cells is looked at once, from one side. */\nconst FORWARD_CELLS = [1, 0, -1, 1, 0, 1, 1, 1] as const\n\nfunction addPairsOfCell(view: ContactView, i: number, cell: number, sameCell: boolean): void {\n const { store, workspace } = view\n const { head, next, radiusOf, pairI, pairJ } = workspace\n const { x, z, y } = store\n const xi = x[i]!\n const zi = z[i]!\n const yi = y[i]!\n const reachBase = radiusOf[i]! + PAIR_MARGIN\n for (let j = head[cell]!; j !== -1; j = next[j]!) {\n if ((sameCell && j <= i) || Math.abs(yi - y[j]!) > MAX_LEVEL_GAP) continue\n const reach = reachBase + radiusOf[j]!\n const ex = x[j]! - xi\n const ez = z[j]! - zi\n if (ex * ex + ez * ez >= reach * reach || workspace.pairCount >= pairI.length) continue\n pairI[workspace.pairCount] = i\n pairJ[workspace.pairCount++] = j\n }\n}\n\n/** The pairs `(i, j)` near enough to touch before the step ends, in the order of the agent that found them. The solver is symmetric in the two of a pair, so which one comes first does not matter. */\nfunction collectPairs(view: ContactView): void {\n const { store, grid, workspace } = view\n const side = Math.floor(grid.size)\n workspace.pairCount = 0\n for (let i = 0; i < view.n; i++) {\n if (!workspace.takesPart[i]) continue\n const hx = Math.floor(store.x[i]! + grid.half)\n const hz = Math.floor(store.z[i]! + grid.half)\n if (hx < 0 || hz < 0 || hx >= side || hz >= side) continue\n addPairsOfCell(view, i, hz * side + hx, true)\n for (let k = 0; k < FORWARD_CELLS.length; k += 2) {\n const qx = hx + FORWARD_CELLS[k]!\n const qz = hz + FORWARD_CELLS[k + 1]!\n if (qx >= 0 && qz >= 0 && qx < side && qz < side) addPairsOfCell(view, i, qz * side + qx, false)\n }\n }\n}\n\n/** The distance, reach and normal (from `j` to `i`) of every pair at the current positions; a pair on the same spot gets a direction from the two identities. */\nfunction measurePairs(view: ContactView): void {\n const { store, workspace } = view\n const { pairI, pairJ, radiusOf, pairReach, pairDistance, pairNormalX, pairNormalZ } = workspace\n const { x, z, identity } = store\n for (let p = 0; p < workspace.pairCount; p++) {\n const i = pairI[p]!\n const j = pairJ[p]!\n const reach = radiusOf[i]! + radiusOf[j]!\n const ex = x[i]! - x[j]!\n const ez = z[i]! - z[j]!\n const distance = Math.sqrt(ex * ex + ez * ez)\n pairReach[p] = reach\n pairDistance[p] = distance\n if (distance >= reach) continue\n if (distance > MIN_DISTANCE) {\n pairNormalX[p] = ex / distance\n pairNormalZ[p] = ez / distance\n continue\n }\n const angle = 2 * Math.PI * unit(identity[i]! ^ identity[j]!, 0, SALT.SPIN)\n pairNormalX[p] = Math.cos(angle)\n pairNormalZ[p] = Math.sin(angle)\n }\n}\n\n/** One inelastic impulse along the normal: the closing speed of the two bodies goes to zero, never below (restitution 0). */\nfunction solveImpulse(store: PhysicsStore, workspace: ContactWorkspace, p: number): void {\n if (workspace.pairDistance[p]! >= workspace.pairReach[p]!) return\n const i = workspace.pairI[p]!\n const j = workspace.pairJ[p]!\n const nx = workspace.pairNormalX[p]!\n const nz = workspace.pairNormalZ[p]!\n const { vx, vz, pushX, pushZ } = store\n const closing = (vx[i]! + pushX[i]! - vx[j]! - pushX[j]!) * nx + (vz[i]! + pushZ[i]! - vz[j]! - pushZ[j]!) * nz\n if (closing >= 0) return\n const massI = workspace.massOf[i]!\n const massJ = workspace.massOf[j]!\n const impulse = (-closing * massI * massJ) / (massI + massJ)\n pushX[i]! += (impulse * nx) / massI\n pushZ[i]! += (impulse * nz) / massI\n pushX[j]! -= (impulse * nx) / massJ\n pushZ[j]! -= (impulse * nz) / massJ\n}\n\nfunction accumulateCorrection(workspace: ContactWorkspace, p: number, relaxation: number): void {\n const distance = workspace.pairDistance[p]!\n const reach = workspace.pairReach[p]!\n if (distance >= reach) return\n const i = workspace.pairI[p]!\n const j = workspace.pairJ[p]!\n const massI = workspace.massOf[i]!\n const massJ = workspace.massOf[j]!\n const move = relaxation * (reach - distance)\n const nx = workspace.pairNormalX[p]!\n const nz = workspace.pairNormalZ[p]!\n workspace.deltaX[i]! += move * nx * (massJ / (massI + massJ))\n workspace.deltaZ[i]! += move * nz * (massJ / (massI + massJ))\n workspace.deltaX[j]! -= move * nx * (massI / (massI + massJ))\n workspace.deltaZ[j]! -= move * nz * (massI / (massI + massJ))\n workspace.count[i]!++\n workspace.count[j]!++\n}\n\nconst isOpen = (grid: PhysicsGrid, x: number, z: number): boolean => {\n const cell = grid.cellOf(x, z)\n return cell >= 0 && (grid.occ[cell] === 0 || grid.occ[cell] === 3)\n}\n\n/** The mean of the corrections an agent got, capped, along each axis only if the grid lets it through. */\nfunction applyCorrection(view: ContactView, agent: number, params: ContactParams): void {\n const { store, grid, workspace } = view\n const count = workspace.count[agent]!\n if (!count) return\n workspace.count[agent] = 0\n const cap = params.maxCorrection\n const dx = Math.max(-cap, Math.min(cap, workspace.deltaX[agent]! / count))\n const dz = Math.max(-cap, Math.min(cap, workspace.deltaZ[agent]! / count))\n if (isOpen(grid, store.x[agent]! + dx, store.z[agent]!)) store.x[agent]! += dx\n if (isOpen(grid, store.x[agent]!, store.z[agent]! + dz)) store.z[agent]! += dz\n}\n\nfunction resetCorrection(workspace: ContactWorkspace, agent: number): void {\n workspace.deltaX[agent] = 0\n workspace.deltaZ[agent] = 0\n workspace.count[agent] = 0\n}\n\nfunction correctPositions(view: ContactView, params: ContactParams): void {\n const { workspace } = view\n for (let p = 0; p < workspace.pairCount; p++) {\n resetCorrection(workspace, workspace.pairI[p]!)\n resetCorrection(workspace, workspace.pairJ[p]!)\n }\n for (let p = 0; p < workspace.pairCount; p++) accumulateCorrection(workspace, p, params.relaxation)\n for (let p = 0; p < workspace.pairCount; p++) {\n applyCorrection(view, workspace.pairI[p]!, params)\n applyCorrection(view, workspace.pairJ[p]!, params)\n }\n}\n\nfunction measureOverlaps(view: ContactView, out: ContactResult): void {\n const { store, workspace } = view\n for (let p = 0; p < workspace.pairCount; p++) {\n const i = workspace.pairI[p]!\n const j = workspace.pairJ[p]!\n // two bodies lying on the ground are meant to lie on one another: that is the mound, not a failure of the contact\n if (isOnGround(store, i) && isOnGround(store, j)) continue\n const reach = workspace.radiusOf[i]! + workspace.radiusOf[j]!\n const overlap = reach - Math.hypot(store.x[i]! - store.x[j]!, store.z[i]! - store.z[j]!)\n if (overlap <= 0) continue\n out.contacts++\n out.overlapSum += overlap\n if (overlap > out.maxOverlap) out.maxOverlap = overlap\n }\n}\n\n/** The damping and the ceiling of the push, for every agent: a body out of band R0 keeps no push for long. */\nfunction dampPush(view: ContactView, params: ContactParams): void {\n const { store, workspace } = view\n const lambda = Math.exp(-params.friction * params.h)\n for (let i = 0; i < view.n; i++) {\n workspace.shove[i] = Math.hypot(store.pushX[i]! - workspace.pushBeforeX[i]!, store.pushZ[i]! - workspace.pushBeforeZ[i]!)\n store.pushX[i]! *= lambda\n store.pushZ[i]! *= lambda\n const speed = Math.hypot(store.pushX[i]!, store.pushZ[i]!)\n if (speed <= params.maxPush) continue\n store.pushX[i]! *= params.maxPush / speed\n store.pushZ[i]! *= params.maxPush / speed\n }\n}\n\n/** The 1 m hash of the agents in band R0 at their positions of now, for whoever looks at the neighbours (the landing of a thrown body) while the pairs are solved elsewhere. */\nexport function prepareHash(view: ContactView, radius: number): void {\n readBodies(view, radius)\n rebuildHash(view)\n}\n\n/**\n * One fixed step of contact in band R0 (`design-chain-horde.md` §4.3): `iterations` rounds of a symmetric Gauss-Seidel impulse (the pairs forward, then backward, so\n * a low index has no advantage) and a Jacobi position correction (the mean of what each agent was asked to move), then the damping of the push.\n * It resolves overlap and shock only: pressure is the field of `pressure.ts`. An inelastic impulse never adds energy, and the correction never touches the velocity.\n */\nexport function contactStep(view: ContactView, params: ContactParams, out: ContactResult): void {\n const { store, workspace } = view\n out.contacts = 0\n out.maxOverlap = 0\n out.overlapSum = 0\n for (let i = 0; i < view.n; i++) {\n workspace.pushBeforeX[i] = store.pushX[i]!\n workspace.pushBeforeZ[i] = store.pushZ[i]!\n }\n readBodies(view, params.radius)\n rebuildHash(view)\n collectPairs(view)\n for (let iteration = 0; iteration < params.iterations; iteration++) {\n measurePairs(view)\n for (let p = 0; p < workspace.pairCount; p++) solveImpulse(store, workspace, p)\n for (let p = workspace.pairCount - 1; p >= 0; p--) solveImpulse(store, workspace, p)\n correctPositions(view, params)\n }\n measureOverlaps(view, out)\n dampPush(view, params)\n}\n","import type { Doors } from '../doors'\nimport type { NavGrid } from '../grid'\nimport type { CrowdStore } from '../store'\nimport type { Corpse, CrowdDamageResult, CrowdEventMap, CrowdEventName, CrowdHit, CrowdTarget, CrowdType } from '../types'\nimport type { ResolvedPhysics } from './options'\n\n/** Counters of the physics, all zero with it off (`crowd.stats.physics`). */\nexport type PhysicsStats = {\n contacts: number\n maxOverlap: number\n meanOverlap: number\n /** Falls by cause: pressure, shove, trip, landing, collapse, blast. */\n falls: [pressure: number, shove: number, trip: number, landing: number, collapse: number, blast: number]\n /** Chain falls: shove, landing, collapse or trip within 240 steps of a blast, of an agent whose own direct shove from it was below `shoveStagger`. */\n fallsBeyondBlast: number\n fallen: number\n staggering: number\n climbers: number\n moundMax: number\n thrownActive: number\n thrownPeak: number\n blastChecks: number\n chainLinks: number\n /** The highest field of the step, N: the front of a column, or the funnel of a door. */\n fieldMax: number\n /** The pressure of the horde on the target, N (exponential mean). */\n playerPressure: number\n}\n\nexport const createPhysicsStats = (): PhysicsStats => ({\n contacts: 0,\n maxOverlap: 0,\n meanOverlap: 0,\n falls: [0, 0, 0, 0, 0, 0],\n fallsBeyondBlast: 0,\n fallen: 0,\n staggering: 0,\n climbers: 0,\n moundMax: 0,\n thrownActive: 0,\n thrownPeak: 0,\n blastChecks: 0,\n chainLinks: 0,\n fieldMax: 0,\n playerPressure: 0,\n})\n\n/** What the physics modules need from the crowd: the shared state and the few verbs they cannot do alone. */\nexport type PhysicsContext = {\n readonly store: CrowdStore\n readonly grid: NavGrid\n readonly doors: Doors\n readonly options: ResolvedPhysics\n readonly stats: PhysicsStats\n readonly gravity: number\n readonly attackEvery: number\n /** The fixed step being run, counted from 0: the counter of every `unit` draw. */\n step: number\n /** Seconds of the fixed step. */\n h: number\n /** Centre of the bands of this step. */\n centre: { x: number; z: number }\n /** Step of the last blast, -1000 before the first. */\n lastBlastStep: number\n typeOf(i: number): CrowdType\n target(): CrowdTarget | null\n emit<K extends CrowdEventName>(event: K, payload: CrowdEventMap[K]): void\n /** Damage with the draw handed in, so the shared stream stays where the game left it. A death is deferred to the end of the step. */\n damage(i: number, hit: CrowdHit, draw: () => number): CrowdDamageResult\n deferKill(i: number): void\n /** A pile step goes (falls, is thrown or dies): the steps behind it in the column come down. */\n releasePileStep(i: number): void\n corpseOf(i: number, seed: number): Corpse\n /** The bodies on the ground (the ledger), for the mound. */\n corpseList(): readonly { readonly data: Corpse }[]\n addCorpse(corpse: Corpse): void\n groundAt(cell: number, y: number): number\n /** Mound height in mm at a grid cell (0 with the mound off). */\n moundAt(cell: number): number\n /** Kilograms standing on the three cells of the lying body of agent `i` (0 with the mound off). */\n weightOn: (i: number) => number\n}\n","import { BALANCE, STATE } from '../types'\nimport type { PhysicsContext } from './context'\nimport type { CrowdDensityOptions } from './options'\n\n/** Fixed steps between two counts: the 5 Hz of the decision of band R1 at 60 Hz. */\nconst DECISION_STEPS = 12\nconst FAR_BAND = 1\n\nexport type Density = {\n /** Counts the walkers of band R1 per coarse cell (every 12th step) and writes the pressure that follows from it into their field. */\n step(): void\n /** What the speed of an agent of band `band` is multiplied by where the crowd is thick: `capacity / bodies` above `capacity` bodies in its cell, 1 elsewhere and in the other bands. */\n slowdown(i: number, band: number): number\n /** Bodies counted in the cell of an agent at the last count. */\n bodiesAround(i: number): number\n}\n\n/**\n * Band R1 without pairs (`design-chain-horde.md` §9): a coarse grid counts the standing walkers per cell; above `capacity` bodies the speed falls by `capacity / bodies` and the\n * field is `(bodies − capacity) · pressurePerBody` N, which the balance rule of band R0 reads as it reads any other field. Cost: one pass over the agents, no pairs.\n */\nexport function createDensity(ctx: PhysicsContext, options: Required<CrowdDensityOptions>): Density {\n const { store, grid } = ctx\n const side = Math.ceil(grid.size / options.cell)\n const bodies = new Uint16Array(side * side)\n const touched: number[] = []\n const half = grid.half\n\n const cellOf = (i: number): number => {\n const gx = Math.floor((store.x[i]! + half) / options.cell)\n const gz = Math.floor((store.z[i]! + half) / options.cell)\n return gx < 0 || gz < 0 || gx >= side || gz >= side ? -1 : gz * side + gx\n }\n\n const isWalking = (i: number): boolean => store.band[i] === FAR_BAND && (store.balance[i] === BALANCE.STANDING || store.balance[i] === BALANCE.STAGGERING) && store.state[i] !== STATE.PILE_STEP && store.state[i] !== STATE.DORMANT\n\n const count = (): void => {\n for (let k = 0; k < touched.length; k++) bodies[touched[k]!] = 0\n touched.length = 0\n for (let i = 0; i < store.n; i++) {\n if (!isWalking(i)) continue\n const cell = cellOf(i)\n if (cell < 0) continue\n if (bodies[cell] === 0) touched.push(cell)\n bodies[cell]!++\n }\n }\n\n const step = (): void => {\n if (ctx.step % DECISION_STEPS === 0) count()\n if (!touched.length) return\n for (let i = 0; i < store.n; i++) {\n if (!isWalking(i)) continue\n const cell = cellOf(i)\n const around = cell < 0 ? 0 : bodies[cell]!\n store.field[i] = around > options.capacity ? (around - options.capacity) * options.pressurePerBody : 0\n }\n }\n\n const bodiesAround = (i: number): number => {\n const cell = cellOf(i)\n return cell < 0 ? 0 : bodies[cell]!\n }\n\n const slowdown = (i: number, band: number): number => {\n if (band !== FAR_BAND) return 1\n const around = bodiesAround(i)\n return around > options.capacity ? options.capacity / around : 1\n }\n\n return { step, slowdown, bodiesAround }\n}\n","import { mix32 } from '../identity'\nimport { BALANCE, STATE } from '../types'\nimport type { PhysicsContext } from './context'\n\nconst CELLS_PER_BODY = 3\n/** Spacing of head, trunk and legs of a lying body along its yaw (m). */\nconst BODY_PART_SPACING = 0.5\nconst CLIMB_HEIGHT = 0.25\nconst ELEVATED = 0.05\nconst NEUTRAL_MASS = 70\nconst NEIGHBOURS = [-1, 1, -1, 0, -1, -1, 0, -1, 0, 1, 1, -1, 1, 0, 1, 1] as const\n\nexport type Mound = {\n /** Millimetres of lying bodies per navigation cell, recomputed every step. */\n readonly height: Uint16Array\n step(): void\n /** Kilograms of the agents that stand on the three cells of the lying body of agent `i`, as of the last step. */\n weightOn(i: number): number\n}\n\ntype Entries = { count: number; cell: Int32Array; thickness: Uint16Array; identity: Uint32Array; agent: Int32Array }\n\nconst BODY_IDENTITY_SCALE = 1e6\n\nexport function createMound(ctx: PhysicsContext, extraBodies: number, height: Uint16Array): Mound {\n const { store, grid } = ctx\n const options = ctx.options.mound!\n const balance = ctx.options.balance!\n const cellCount = grid.n * grid.n\n const touched = new Int32Array((store.capacity + extraBodies) * CELLS_PER_BODY * 2)\n let touchedCount = 0\n const entryCapacity = (store.capacity + extraBodies) * CELLS_PER_BODY\n const entries: Entries = { count: 0, cell: new Int32Array(entryCapacity), thickness: new Uint16Array(entryCapacity), identity: new Uint32Array(entryCapacity), agent: new Int32Array(entryCapacity) }\n const standMass = new Float32Array(cellCount)\n const standTouched = new Int32Array(store.capacity)\n let standTouchedCount = 0\n const maxMillimetres = Math.round(options.maxHeight * 1000)\n const cells: [number, number, number] = [0, 0, 0]\n\n const cellsOfBody = (x: number, z: number, yaw: number): void => {\n const sin = Math.sin(yaw)\n const cos = Math.cos(yaw)\n for (let part = 0; part < CELLS_PER_BODY; part++) {\n const offset = (part - 1) * BODY_PART_SPACING\n cells[part] = grid.cellOf(x + sin * offset, z + cos * offset)\n }\n }\n\n const addBody = (x: number, z: number, yaw: number, millimetres: number, identity: number, agent: number): void => {\n cellsOfBody(x, z, yaw)\n for (const cell of cells) {\n if (cell < 0) continue\n if (height[cell] === 0) touched[touchedCount++] = cell\n height[cell]! += millimetres\n const index = entries.count++\n entries.cell[index] = cell\n entries.thickness[index] = millimetres\n entries.identity[index] = identity\n entries.agent[index] = agent\n }\n }\n\n const sumBodies = (): void => {\n for (let k = 0; k < touchedCount; k++) height[touched[k]!] = 0\n touchedCount = 0\n entries.count = 0\n for (let i = 0; i < store.n; i++) {\n const state = store.balance[i]\n if (store.band[i] !== 0 || (state !== BALANCE.FALLEN && state !== BALANCE.GETTING_UP)) continue\n addBody(store.x[i]!, store.z[i]!, store.renderYaw[i]!, Math.round(options.bodyThickness * store.build[i]! * 1000), store.identity[i]!, i)\n }\n const reach = options.corpseRadius * options.corpseRadius\n for (const { data } of ctx.corpseList()) {\n if ((data.x - ctx.centre.x) ** 2 + (data.z - ctx.centre.z) ** 2 > reach) continue\n addBody(data.x, data.z, data.yaw, Math.round(options.corpseThickness * data.build * 1000), mix32(Math.floor(data.seed * BODY_IDENTITY_SCALE)), -1)\n }\n }\n\n const isOpenCell = (cell: number): boolean => grid.occ[cell] === 0 || grid.occ[cell] === 3\n\n /** The lowest of the 8 neighbours that is open and has room for `millimetres` more; -1 when none has. Ties go to the lowest cell number. */\n const lowestNeighbour = (cell: number, millimetres: number): number => {\n const cx = cell % grid.n\n const cz = Math.floor(cell / grid.n)\n let best = -1\n for (let k = 0; k < NEIGHBOURS.length; k += 2) {\n const nx = cx + NEIGHBOURS[k]!\n const nz = cz + NEIGHBOURS[k + 1]!\n if (nx < 0 || nz < 0 || nx >= grid.n || nz >= grid.n) continue\n const neighbour = nz * grid.n + nx\n if (!isOpenCell(neighbour) || height[neighbour]! + millimetres > maxMillimetres) continue\n if (best < 0 || height[neighbour]! < height[best]! || (height[neighbour] === height[best] && neighbour < best)) best = neighbour\n }\n return best\n }\n\n const slideEntry = (index: number): void => {\n const cell = entries.cell[index]!\n const target = lowestNeighbour(cell, entries.thickness[index]!)\n if (target < 0) return\n if (height[target] === 0) touched[touchedCount++] = target\n height[target]! += entries.thickness[index]!\n height[cell]! -= entries.thickness[index]!\n entries.cell[index] = target\n const agent = entries.agent[index]!\n if (agent < 0) return\n store.x[agent]! += ((target % grid.n) - (cell % grid.n)) * grid.cell\n store.z[agent]! += (Math.floor(target / grid.n) - Math.floor(cell / grid.n)) * grid.cell\n }\n\n /** The bodies of a cell above the ceiling, taken in the order of their identity, slide to a lower neighbour: the result depends on who is in the cell, not on who was added first. */\n const capHeights = (): void => {\n let over = false\n for (let k = 0; k < touchedCount && !over; k++) over = height[touched[k]!]! > maxMillimetres\n if (!over) return\n const order = Array.from({ length: entries.count }, (_, index) => index).sort((a, b) => entries.cell[a]! - entries.cell[b]! || entries.identity[a]! - entries.identity[b]! || a - b)\n let current = -1\n let running = 0\n for (const index of order) {\n const cell = entries.cell[index]!\n if (cell !== current) {\n current = cell\n running = 0\n }\n if (height[cell]! <= maxMillimetres) continue\n if (running + entries.thickness[index]! <= maxMillimetres) running += entries.thickness[index]!\n else slideEntry(index)\n }\n for (let k = 0; k < touchedCount; k++) if (height[touched[k]!]! > maxMillimetres) height[touched[k]!] = maxMillimetres\n }\n\n const seatFallen = (): void => {\n let top = 0\n for (let k = 0; k < touchedCount; k++) top = Math.max(top, height[touched[k]!]!)\n ctx.stats.moundMax = top / 1000\n for (let i = 0; i < store.n; i++) {\n const state = store.balance[i]\n if (store.band[i] !== 0 || (state !== BALANCE.FALLEN && state !== BALANCE.GETTING_UP)) continue\n const cell = grid.cellOf(store.x[i]!, store.z[i]!)\n if (cell >= 0) store.y[i] = Math.max(0, height[cell]! / 1000 - options.bodyThickness * store.build[i]!)\n }\n }\n\n const weighStanding = (): void => {\n for (let k = 0; k < standTouchedCount; k++) standMass[standTouched[k]!] = 0\n standTouchedCount = 0\n let climbers = 0\n for (let i = 0; i < store.n; i++) {\n const state = store.balance[i]\n if (store.band[i] !== 0 || store.state[i] === STATE.PILE_STEP || (state !== BALANCE.STANDING && state !== BALANCE.STAGGERING)) continue\n if (store.y[i]! >= CLIMB_HEIGHT) climbers++\n if (store.y[i]! <= ELEVATED) continue\n const cell = grid.cellOf(store.x[i]!, store.z[i]!)\n if (cell < 0) continue\n if (standMass[cell] === 0) standTouched[standTouchedCount++] = cell\n standMass[cell]! += store.mass[i]!\n }\n ctx.stats.climbers = climbers\n }\n\n /** Every lying body takes damage for the weight standing on its three cells (`trampleDps` per 70 kg); a death waits for the end of the step. */\n const trample = (): void => {\n if (!standTouchedCount) return\n for (let i = 0; i < store.n; i++) {\n const state = store.balance[i]\n if (store.band[i] !== 0 || (state !== BALANCE.FALLEN && state !== BALANCE.GETTING_UP)) continue\n const mass = weightOn(i)\n if (!mass) continue\n store.hp[i]! -= balance.trampleDps * (mass / NEUTRAL_MASS) * ctx.h\n if (store.hp[i]! <= 0) ctx.deferKill(i)\n }\n }\n\n const step = (): void => {\n sumBodies()\n capHeights()\n seatFallen()\n weighStanding()\n trample()\n }\n\n const weightOn = (i: number): number => {\n cellsOfBody(store.x[i]!, store.z[i]!, store.renderYaw[i]!)\n let mass = 0\n for (const cell of cells) if (cell >= 0) mass += standMass[cell]!\n return mass\n }\n\n return { height, step, weightOn }\n}\n","import type { PhysicsWorkerFactory } from './types'\n\n/** The browser worker that loads `physics-worker.js` from beside the bundle; `null` where there is no `Worker` or no `import.meta.url` (the iife build, node). */\nexport function createDefaultWorkerFactory(): PhysicsWorkerFactory | null {\n if (typeof Worker === 'undefined') return null\n let url: URL\n try {\n url = new URL('./physics-worker.js', import.meta.url)\n } catch {\n return null\n }\n return () => {\n const worker = new Worker(url, { type: 'module' })\n return {\n postMessage: (message, transfer) => worker.postMessage(message, transfer ?? []),\n onMessage: (listener) => worker.addEventListener('message', (event) => listener(event.data)),\n terminate: () => worker.terminate(),\n }\n }\n}\n","/**\n * The frame the physics step is handed to a worker in: one buffer holding every array the contact and the pressure read (the inputs), the deltas and fields they give back\n * (the outputs) and the grid occupancy. One layout for the shared buffer and for the transferred one, so the two paths run the same step over the same bytes.\n */\n\nconst F32_INPUTS = ['x', 'z', 'y', 'vx', 'vz', 'pushX', 'pushZ', 'mass', 'build', 'aimX', 'aimZ', 'driveFactor', 'fieldPrev'] as const\nconst U8_INPUTS = ['band', 'balance', 'state', 'type'] as const\nconst F32_OUTPUTS = ['deltaX', 'deltaZ', 'deltaPushX', 'deltaPushZ', 'deltaVx', 'deltaVz', 'field', 'shove', 'doorLoad'] as const\n\n/** Header slots (Float64): the step's inputs, then its results. */\nexport const HEADER = { n: 0, h: 1, playerOn: 2, playerX: 3, playerZ: 4, contacts: 5, maxOverlap: 6, overlapSum: 7, playerLoad: 8, drivers: 9, fieldMax: 10, size: 16 } as const\n\nexport type FrameShape = { readonly capacity: number; readonly cells: number }\n\nexport type PhysicsFrame = {\n readonly header: Float64Array\n readonly f32: { readonly [K in (typeof F32_INPUTS)[number] | (typeof F32_OUTPUTS)[number]]: Float32Array }\n readonly u8: { readonly [K in (typeof U8_INPUTS)[number]]: Uint8Array }\n readonly identity: Uint32Array\n readonly doorCell: Int32Array\n readonly occ: Uint8Array\n}\n\nconst align8 = (bytes: number): number => Math.ceil(bytes / 8) * 8\n\n/** Bytes of a frame; the arrays follow the header in the order of the lists above. */\nexport const frameBytes = ({ capacity, cells }: FrameShape): number => HEADER.size * 8 + (F32_INPUTS.length + F32_OUTPUTS.length + 2) * align8(capacity * 4) + U8_INPUTS.length * align8(capacity) + align8(cells)\n\nexport function createFrame(buffer: ArrayBufferLike, shape: FrameShape): PhysicsFrame {\n const { capacity, cells } = shape\n let offset = 0\n const take = <T>(make: (buffer: ArrayBufferLike, offset: number, length: number) => T, bytesPerElement: number, length: number): T => {\n const view = make(buffer, offset, length)\n offset += align8(length * bytesPerElement)\n return view\n }\n const f64 = (b: ArrayBufferLike, o: number, l: number): Float64Array => new Float64Array(b, o, l)\n const f32 = (b: ArrayBufferLike, o: number, l: number): Float32Array => new Float32Array(b, o, l)\n const u8 = (b: ArrayBufferLike, o: number, l: number): Uint8Array => new Uint8Array(b, o, l)\n const header = take(f64, 8, HEADER.size)\n const floats = {} as Record<string, Float32Array>\n for (const key of [...F32_INPUTS, ...F32_OUTPUTS]) floats[key] = take(f32, 4, capacity)\n const identity = take((b, o, l) => new Uint32Array(b, o, l), 4, capacity)\n const doorCell = take((b, o, l) => new Int32Array(b, o, l), 4, capacity)\n const bytes = {} as Record<string, Uint8Array>\n for (const key of U8_INPUTS) bytes[key] = take(u8, 1, capacity)\n const occ = take(u8, 1, cells)\n return { header, f32: floats as PhysicsFrame['f32'], u8: bytes as PhysicsFrame['u8'], identity, doorCell, occ }\n}\n\n/** The inputs of the step, by the name the store gives them. */\nexport const INPUT_KEYS = { f32: F32_INPUTS, u8: U8_INPUTS } as const\n","import type { NavGrid } from '../../grid'\nimport type { CrowdStore } from '../../store'\nimport type { ContactView } from '../contact'\nimport type { PhysicsStats } from '../context'\nimport { HEADER, INPUT_KEYS } from './frame'\nimport type { PhysicsFrame } from './frame'\nimport type { StepRunner } from './runner'\n\nexport type OffloadHost = {\n readonly store: CrowdStore\n readonly grid: NavGrid\n readonly stats: PhysicsStats\n readonly contact: ContactView\n /** The door at a grid cell took `load` N·s from the field (the door index is the host's to find). */\n addDoorLoad(cell: number, load: number): void\n}\n\nexport type OffloadStepResult = { playerLoad: number }\n\nexport type Offload = {\n /** Applies the result of the previous step, if it is back, then hands the state of this one to the runner. */\n step(h: number, player: { readonly x: number; readonly z: number } | undefined): OffloadStepResult\n /** The store moved agent `moved` into the place of `removed`: the result still in flight is read through it. */\n remap(removed: number, moved: number): void\n dispose(): void\n}\n\nconst isOpen = (grid: NavGrid, x: number, z: number): boolean => {\n const cell = grid.cellOf(x, z)\n return cell >= 0 && (grid.occ[cell] === 0 || grid.occ[cell] === 3)\n}\n\n/** The inputs of one step, copied from the store and the grid into the frame. */\nexport function writeSnapshot(frame: PhysicsFrame, source: { readonly store: CrowdStore; readonly grid: NavGrid }, h: number, player: { readonly x: number; readonly z: number } | undefined): void {\n const { store, grid } = source\n const n = store.n\n for (const key of INPUT_KEYS.f32) frame.f32[key].set(store[key].subarray(0, n))\n for (const key of INPUT_KEYS.u8) frame.u8[key].set(store[key].subarray(0, n))\n frame.identity.set(store.identity.subarray(0, n))\n frame.occ.set(grid.occ)\n frame.header[HEADER.n] = n\n frame.header[HEADER.h] = h\n frame.header[HEADER.playerOn] = player ? 1 : 0\n frame.header[HEADER.playerX] = player?.x ?? 0\n frame.header[HEADER.playerZ] = player?.z ?? 0\n}\n\n/**\n * The contact and the pressure one step behind: what the runner gives back was computed from the state at the end of the previous step, so it comes as deltas (position,\n * push, velocity) added to the state of now, and as fields that replace the old ones. With the same inputs per step it is the same result whichever thread computed it.\n */\nexport function createOffload(runner: StepRunner, host: OffloadHost, latency = 1): Offload {\n const { store, grid, stats, contact } = host\n type Pending = { count: number; origin: Int32Array | null; readonly originBuffer: Int32Array }\n const queue: Pending[] = []\n const spare: Pending[] = Array.from({ length: latency }, () => ({ count: 0, origin: null, originBuffer: new Int32Array(store.capacity) }))\n const result: OffloadStepResult = { playerLoad: 0 }\n\n const apply = (frame: PhysicsFrame, pending: Pending): void => {\n const { f32, header } = frame\n const { origin, count } = pending\n for (let agent = 0; agent < store.n; agent++) {\n const from = origin ? origin[agent]! : agent < count ? agent : -1\n if (from < 0) {\n contact.workspace.shove[agent] = 0\n continue\n }\n const dx = f32.deltaX[from]!\n const dz = f32.deltaZ[from]!\n if (dx !== 0 && isOpen(grid, store.x[agent]! + dx, store.z[agent]!)) store.x[agent]! += dx\n if (dz !== 0 && isOpen(grid, store.x[agent]!, store.z[agent]! + dz)) store.z[agent]! += dz\n store.pushX[agent]! += f32.deltaPushX[from]!\n store.pushZ[agent]! += f32.deltaPushZ[from]!\n store.vx[agent]! += f32.deltaVx[from]!\n store.vz[agent]! += f32.deltaVz[from]!\n store.field[agent] = f32.field[from]!\n store.fieldPrev[agent] = f32.field[from]!\n contact.workspace.shove[agent] = f32.shove[from]!\n const cell = frame.doorCell[from]!\n if (cell >= 0) host.addDoorLoad(cell, f32.doorLoad[from]!)\n }\n stats.contacts = header[HEADER.contacts]!\n stats.maxOverlap = header[HEADER.maxOverlap]!\n stats.meanOverlap = header[HEADER.contacts]! ? header[HEADER.overlapSum]! / header[HEADER.contacts]! : 0\n stats.fieldMax = header[HEADER.fieldMax]!\n result.playerLoad = header[HEADER.playerLoad]!\n }\n\n const step = (h: number, player: { readonly x: number; readonly z: number } | undefined): OffloadStepResult => {\n result.playerLoad = 0\n if (queue.length >= latency) {\n const frame = runner.settle()\n if (frame) {\n const pending = queue.shift()!\n apply(frame, pending)\n pending.origin = null\n spare.push(pending)\n }\n }\n const frame = runner.frame()\n if (frame && spare.length) {\n writeSnapshot(frame, { store, grid }, h, player)\n runner.submit()\n const pending = spare.shift()!\n pending.count = store.n\n queue.push(pending)\n }\n return result\n }\n\n const remap = (removed: number, moved: number): void => {\n queue.forEach((pending) => {\n if (!pending.origin) {\n pending.origin = pending.originBuffer.fill(-1)\n for (let agent = 0; agent < pending.count; agent++) pending.origin[agent] = agent\n }\n pending.origin[removed] = removed === moved ? -1 : pending.origin[moved]!\n if (removed !== moved) pending.origin[moved] = -1\n })\n }\n\n return { step, remap, dispose: () => runner.dispose() }\n}\n","import { BALANCE } from '../types'\nimport { takesPartInContact } from './contact'\nimport type { ContactView, PhysicsStore } from './contact'\nimport type { CrowdPressureOptions } from './options'\n\n/** Two bodies count as touching up to this far past the sum of their radii. */\nconst TOUCH_SLACK = 0.08\n/** How far past its own radius an agent feels a wall in front of it. */\nconst WALL_PROBE = 0.12\n/** The agents that stand in front of the target and pass the field on to it (a little past the attack range). */\nconst PLAYER_REACH = 1.3\nconst PLAYER_RADIUS_SQUARED = PLAYER_REACH * PLAYER_REACH\n\nexport type PressureParams = Required<CrowdPressureOptions> & { readonly h: number; readonly radius: number }\n\nexport type PressureWorkspace = {\n readonly weightSum: Float32Array\n /** 1 when something stands in front of the agent that it presses on (a wall, a body, even one pressing back). */\n readonly pressing: Uint8Array\n readonly wallKind: Uint8Array\n readonly wallDoor: Int32Array\n readonly playerWeight: Float32Array\n readonly cutExcess: Float32Array\n readonly cutX: Float32Array\n readonly cutZ: Float32Array\n readonly pairWeightForward: Float32Array\n readonly pairWeightBackward: Float32Array\n /** 1 for the agents that press on what is in front of them (`isPressing`), read once per step. */\n readonly canPress: Uint8Array\n}\n\nexport type PressureContext = {\n /** Newtons of will of each type, by type index. */\n readonly driveForceOfType: Float32Array\n /** The door index of a grid cell, -1 for none. */\n readonly doorAt: (cell: number) => number\n /** N·s each door got from the field this step, added to. */\n readonly doorLoad: Float32Array\n /** The agent the horde presses on (position only); undefined: nothing to press on. */\n readonly player?: { readonly x: number; readonly z: number } | undefined\n /** When given, the load a door gets from each agent is recorded here (by agent, with the door \"index\" `doorAt` answered) instead of added to `doorLoad`: the caller adds it, in agent order. */\n readonly doorOut?: { readonly cell: Int32Array; readonly load: Float32Array } | undefined\n}\n\nexport type PressureResult = { playerLoad: number; drivers: number }\n\nexport const createPressureWorkspace = (capacity: number, pairCapacity: number): PressureWorkspace => ({\n weightSum: new Float32Array(capacity),\n pressing: new Uint8Array(capacity),\n wallKind: new Uint8Array(capacity),\n wallDoor: new Int32Array(capacity),\n playerWeight: new Float32Array(capacity),\n cutExcess: new Float32Array(capacity),\n cutX: new Float32Array(capacity),\n cutZ: new Float32Array(capacity),\n pairWeightForward: new Float32Array(pairCapacity),\n pairWeightBackward: new Float32Array(pairCapacity),\n canPress: new Uint8Array(capacity),\n})\n\nconst isPressing = (store: PhysicsStore, i: number): boolean => takesPartInContact(store, i) && (store.balance[i] === BALANCE.STANDING || store.balance[i] === BALANCE.STAGGERING)\n\n/** `isPressing` of every agent into `canPress`: the state does not change inside the pressure step. */\nfunction readPressing(view: ContactView, workspace: PressureWorkspace): void {\n for (let i = 0; i < view.n; i++) workspace.canPress[i] = isPressing(view.store, i) ? 1 : 0\n}\n\nconst frontWeight = (alignment: number, cone: number): number => (alignment > cone ? (alignment - cone) / (1 - cone) : 0)\n\nconst hasAim = (store: PhysicsStore, i: number): boolean => store.aimX[i] !== 0 || store.aimZ[i] !== 0\n\n/** The will of `i` against its front neighbour `j`: the part of its speed along the contact that the neighbour does not have is cut. */\nfunction noteCut(store: PhysicsStore, workspace: PressureWorkspace, i: number, j: number, nx: number, nz: number): void {\n const excess = (store.vx[i]! - store.vx[j]!) * nx + (store.vz[i]! - store.vz[j]!) * nz\n if (excess <= workspace.cutExcess[i]!) return\n workspace.cutExcess[i] = excess\n workspace.cutX[i] = nx\n workspace.cutZ[i] = nz\n}\n\n/** Pass A: the weight of each front contact between agents, per direction, and the cut of the will against it. */\nfunction weighPairs(view: ContactView, workspace: PressureWorkspace, params: PressureParams): void {\n const { store } = view\n for (let p = 0; p < view.workspace.pairCount; p++) {\n workspace.pairWeightForward[p] = 0\n workspace.pairWeightBackward[p] = 0\n const i = view.workspace.pairI[p]!\n const j = view.workspace.pairJ[p]!\n if (!workspace.canPress[i] || !workspace.canPress[j]) continue\n const ex = store.x[j]! - store.x[i]!\n const ez = store.z[j]! - store.z[i]!\n const distance = Math.sqrt(ex * ex + ez * ez)\n if (distance < 1e-6 || distance > (store.build[i]! + store.build[j]!) * params.radius + TOUCH_SLACK) continue\n const nx = ex / distance\n const nz = ez / distance\n const forward = frontWeight(store.aimX[i]! * nx + store.aimZ[i]! * nz, params.frontCone)\n const backward = frontWeight(-(store.aimX[j]! * nx + store.aimZ[j]! * nz), params.frontCone)\n if (forward > 0) {\n workspace.pressing[i] = 1\n noteCut(store, workspace, i, j, nx, nz)\n }\n if (backward > 0) {\n workspace.pressing[j] = 1\n noteCut(store, workspace, j, i, -nx, -nz)\n }\n // two agents pressing on each other (head on) feel their own drive and no more: passing the field both ways would feed it back on itself without a limit\n if (forward > 0 && backward > 0) continue\n workspace.pairWeightForward[p] = forward\n workspace.pairWeightBackward[p] = backward\n workspace.weightSum[i]! += forward\n workspace.weightSum[j]! += backward\n }\n}\n\n/** Pass B: a blocked grid cell in the direction of the will is a wall of weight 1 (a closed door too, and the door takes the load). */\nfunction weighWalls(view: ContactView, workspace: PressureWorkspace, params: PressureParams, doorAt: PressureContext['doorAt']): void {\n const { store, grid } = view\n for (let i = 0; i < view.n; i++) {\n if (!workspace.canPress[i] || !hasAim(store, i)) continue\n const reach = store.build[i]! * params.radius + WALL_PROBE\n const cell = grid.cellOf(store.x[i]! + store.aimX[i]! * reach, store.z[i]! + store.aimZ[i]! * reach)\n if (cell >= 0 && (grid.occ[cell] === 0 || grid.occ[cell] === 3)) continue\n workspace.wallKind[i] = 1\n workspace.wallDoor[i] = cell >= 0 && grid.occ[cell] === 5 ? doorAt(cell) : -1\n workspace.pressing[i] = 1\n workspace.weightSum[i]! += 1\n }\n}\n\n/** The agents at the target pass the field on to it: one that wants to go there by its cone weight, one that stopped to attack with weight 1. */\nfunction weighPlayer(view: ContactView, workspace: PressureWorkspace, params: PressureParams, player: { readonly x: number; readonly z: number }): void {\n const { store } = view\n for (let i = 0; i < view.n; i++) {\n if (!workspace.canPress[i]) continue\n const ex = player.x - store.x[i]!\n const ez = player.z - store.z[i]!\n const squared = ex * ex + ez * ez\n if (squared > PLAYER_RADIUS_SQUARED || squared < 1e-6) continue\n const distance = Math.sqrt(squared)\n const weight = hasAim(store, i) ? frontWeight((store.aimX[i]! * ex + store.aimZ[i]! * ez) / distance, params.frontCone) : 1\n workspace.playerWeight[i] = weight\n if (weight > 0) workspace.pressing[i] = 1\n workspace.weightSum[i]! += weight\n }\n}\n\nfunction clearWorkspace(view: ContactView, workspace: PressureWorkspace): void {\n const { store } = view\n for (let i = 0; i < view.n; i++) {\n workspace.weightSum[i] = 0\n workspace.pressing[i] = 0\n workspace.wallKind[i] = 0\n workspace.playerWeight[i] = 0\n workspace.cutExcess[i] = 0\n store.field[i] = 0\n }\n}\n\n/** What agent `i` adds to the field by itself: its drive, when it wants to go and has something in front. */\nfunction ownDrive(store: PhysicsStore, context: PressureContext, workspace: PressureWorkspace, i: number): number {\n if (!hasAim(store, i) || !workspace.pressing[i]) return 0\n return context.driveForceOfType[store.type[i]!]! * store.driveFactor[i]!\n}\n\n/** Pass C: each agent's field is its own drive plus what the agents behind it passed on, read from the previous step. */\nfunction spreadField(view: ContactView, workspace: PressureWorkspace, params: PressureParams, context: PressureContext, out: PressureResult): void {\n const { store } = view\n for (let i = 0; i < view.n; i++) {\n if (!workspace.canPress[i]) continue\n store.field[i] = ownDrive(store, context, workspace, i)\n if (store.field[i]! > 0) out.drivers++\n }\n for (let p = 0; p < view.workspace.pairCount; p++) {\n const i = view.workspace.pairI[p]!\n const j = view.workspace.pairJ[p]!\n const forward = workspace.pairWeightForward[p]!\n const backward = workspace.pairWeightBackward[p]!\n if (forward > 0) store.field[j]! += (params.transmission * store.fieldPrev[i]! * forward * forward) / workspace.weightSum[i]!\n if (backward > 0) store.field[i]! += (params.transmission * store.fieldPrev[j]! * backward * backward) / workspace.weightSum[j]!\n }\n for (let i = 0; i < view.n; i++) {\n if (!workspace.canPress[i]) continue\n const passed = (params.transmission * store.fieldPrev[i]!) / (workspace.weightSum[i]! || 1)\n if (workspace.wallKind[i] && workspace.wallDoor[i]! >= 0) {\n if (context.doorOut) {\n context.doorOut.cell[i] = workspace.wallDoor[i]!\n context.doorOut.load[i] = passed * params.h\n } else context.doorLoad[workspace.wallDoor[i]!]! += passed * params.h\n }\n if (workspace.playerWeight[i]! > 0) out.playerLoad += passed * workspace.playerWeight[i]! * workspace.playerWeight[i]!\n if (store.field[i]! > params.cap) store.field[i] = params.cap\n }\n}\n\nfunction applyCuts(view: ContactView, workspace: PressureWorkspace): void {\n const { store } = view\n for (let i = 0; i < view.n; i++) {\n const excess = workspace.cutExcess[i]!\n if (excess <= 0) continue\n store.vx[i]! -= excess * workspace.cutX[i]!\n store.vz[i]! -= excess * workspace.cutZ[i]!\n }\n}\n\n/**\n * One fixed step of the pressure field (`design-chain-horde.md` §4.5): the field moves one hop per step, each agent reading only the previous step of the others, so the\n * result does not depend on the order of the indices. A column of N pushing agents gives its front `D·(1 − τᴺ)/(1 − τ)`; a hexagonal block against a flat wall loses 29% per hop.\n */\nexport function pressureStep(view: ContactView, workspace: PressureWorkspace, params: PressureParams, context: PressureContext, out: PressureResult): void {\n out.playerLoad = 0\n out.drivers = 0\n clearWorkspace(view, workspace)\n readPressing(view, workspace)\n weighPairs(view, workspace, params)\n weighWalls(view, workspace, params, context.doorAt)\n if (context.player) weighPlayer(view, workspace, params, context.player)\n spreadField(view, workspace, params, context, out)\n applyCuts(view, workspace)\n for (let i = 0; i < view.n; i++) view.store.fieldPrev[i] = view.store.field[i]!\n}\n\n","import { createCellOf } from '../../grid'\nimport { contactStep, createContactWorkspace } from '../contact'\nimport type { ContactParams, ContactResult, ContactView, PhysicsGrid, PhysicsStore } from '../contact'\nimport type { CrowdContactOptions, CrowdPressureOptions } from '../options'\nimport { createPressureWorkspace, pressureStep } from '../pressure'\nimport type { PressureResult, PressureWorkspace } from '../pressure'\nimport { HEADER } from './frame'\nimport type { PhysicsFrame } from './frame'\n\nconst PAIRS_PER_AGENT = 12\n\n/** What the kernel needs once, to be built (it travels to the worker as a message). */\nexport type KernelSetup = {\n readonly capacity: number\n readonly gridSize: number\n readonly gridCell: number\n readonly contact: Required<CrowdContactOptions>\n readonly pressure: Required<CrowdPressureOptions> | null\n readonly driveForceOfType: readonly number[]\n}\n\nexport type PhysicsKernel = {\n /** The contact and the pressure over the arrays of the frame, the deltas and the fields written back into it. The same call runs on the main thread and in a worker. */\n run(frame: PhysicsFrame): void\n}\n\nconst STORE_KEYS = ['x', 'z', 'y', 'vx', 'vz', 'pushX', 'pushZ', 'mass', 'build', 'aimX', 'aimZ', 'driveFactor', 'fieldPrev'] as const\n\n/** The step as a pure function of one frame: no read of anything outside it, so a worker that gets the same bytes writes the same bytes. */\nexport function createKernel(setup: KernelSetup): PhysicsKernel {\n const { capacity } = setup\n const side = Math.floor(setup.gridSize / setup.gridCell)\n const half = setup.gridSize / 2\n const before = { x: new Float32Array(capacity), z: new Float32Array(capacity), pushX: new Float32Array(capacity), pushZ: new Float32Array(capacity), vx: new Float32Array(capacity), vz: new Float32Array(capacity) }\n const driveForceOfType = Float32Array.from(setup.driveForceOfType)\n const contactResult: ContactResult = { contacts: 0, maxOverlap: 0, overlapSum: 0 }\n const pressureResult: PressureResult = { playerLoad: 0, drivers: 0 }\n const workspace: PressureWorkspace | null = setup.pressure ? createPressureWorkspace(capacity, capacity * PAIRS_PER_AGENT) : null\n const cellOf = createCellOf(half, setup.gridCell, side)\n const doorLoadUnused = new Float32Array(0)\n // one store and one grid for the life of the kernel (the contact workspace is big): a new frame only points their arrays at its own bytes\n const store: Record<string, unknown> = {}\n const grid: { size: number; half: number; occ: Uint8Array<ArrayBufferLike>; cellOf: typeof cellOf } = { size: setup.gridSize, half, occ: new Uint8Array(0), cellOf }\n const view: ContactView = { n: 0, store: store as PhysicsStore, grid: grid as unknown as PhysicsGrid, workspace: createContactWorkspace(capacity, grid as unknown as PhysicsGrid) }\n\n const bind = (frame: PhysicsFrame): ContactView => {\n Object.assign(store, frame.u8, { identity: frame.identity, field: frame.f32.field })\n for (const key of STORE_KEYS) store[key] = frame.f32[key]\n grid.occ = frame.occ\n return view\n }\n\n const run = (frame: PhysicsFrame): void => {\n const { header } = frame\n const n = header[HEADER.n]!\n const contact = bind(frame)\n contact.n = n\n for (let i = 0; i < n; i++) {\n before.x[i] = frame.f32.x[i]!\n before.z[i] = frame.f32.z[i]!\n before.pushX[i] = frame.f32.pushX[i]!\n before.pushZ[i] = frame.f32.pushZ[i]!\n before.vx[i] = frame.f32.vx[i]!\n before.vz[i] = frame.f32.vz[i]!\n }\n const h = header[HEADER.h]!\n const params: ContactParams = { ...setup.contact, h }\n contactStep(contact, params, contactResult)\n header[HEADER.contacts] = contactResult.contacts\n header[HEADER.maxOverlap] = contactResult.maxOverlap\n header[HEADER.overlapSum] = contactResult.overlapSum\n frame.doorCell.fill(-1, 0, n)\n frame.f32.doorLoad.fill(0, 0, n)\n if (workspace && setup.pressure) {\n const player = header[HEADER.playerOn] ? { x: header[HEADER.playerX]!, z: header[HEADER.playerZ]! } : undefined\n const doorOut = { cell: frame.doorCell, load: frame.f32.doorLoad }\n pressureStep(contact, workspace, { ...setup.pressure, h, radius: setup.contact.radius }, { driveForceOfType, doorAt: (cell) => cell, doorLoad: doorLoadUnused, player, doorOut }, pressureResult)\n let top = 0\n for (let i = 0; i < n; i++) top = Math.max(top, frame.f32.field[i]!)\n header[HEADER.fieldMax] = top\n } else header[HEADER.fieldMax] = 0\n header[HEADER.playerLoad] = pressureResult.playerLoad\n header[HEADER.drivers] = pressureResult.drivers\n const { f32 } = frame\n for (let i = 0; i < n; i++) {\n f32.deltaX[i] = f32.x[i]! - before.x[i]!\n f32.deltaZ[i] = f32.z[i]! - before.z[i]!\n f32.deltaPushX[i] = f32.pushX[i]! - before.pushX[i]!\n f32.deltaPushZ[i] = f32.pushZ[i]! - before.pushZ[i]!\n f32.deltaVx[i] = f32.vx[i]! - before.vx[i]!\n f32.deltaVz[i] = f32.vz[i]! - before.vz[i]!\n f32.shove[i] = contact.workspace.shove[i]!\n }\n }\n\n return { run }\n}\n","import { createFrame } from './frame'\nimport type { FrameShape } from './frame'\nimport { createKernel } from './kernel'\nimport type { KernelSetup } from './kernel'\n\n/** The slot of the control word the two threads share. */\nexport const CONTROL = { IDLE: 0, SUBMITTED: 1, DONE: 2, STOP: 3 } as const\n\nexport type InitMessage = { readonly type: 'init'; readonly setup: KernelSetup; readonly shape: FrameShape; readonly frames?: readonly SharedArrayBuffer[]; readonly control?: SharedArrayBuffer }\nexport type FrameMessage = { readonly type: 'frame'; readonly buffer: ArrayBuffer }\nexport type DoneMessage = { readonly type: 'done'; readonly buffer: ArrayBuffer }\n\n/** What a worker thread has to offer: browsers and node give it different shapes, so the host adapts them. */\nexport type WorkerPort = {\n postMessage(message: unknown, transfer?: ArrayBuffer[]): void\n onMessage(listener: (data: unknown) => void): void\n}\n\n/**\n * The worker side: builds the kernel on `init`, then either loops on the shared control word (shared transport) or answers each transferred frame.\n * A host that loads the worker its own way (a bundler, node `worker_threads`) calls this with its port.\n */\nexport function attachPhysicsWorker(port: WorkerPort): void {\n let kernel: ReturnType<typeof createKernel> | null = null\n let shape: FrameShape | null = null\n port.onMessage((data) => {\n const message = data as InitMessage | FrameMessage\n if (message.type === 'init') {\n kernel = createKernel(message.setup)\n shape = message.shape\n if (message.frames && message.control) runShared(kernel, message.frames.map((buffer) => createFrame(buffer, message.shape)), new Int32Array(message.control))\n return\n }\n if (!kernel || !shape) return\n const frame = createFrame(message.buffer, shape)\n kernel.run(frame)\n port.postMessage({ type: 'done', buffer: message.buffer } satisfies DoneMessage, [message.buffer])\n })\n}\n\n/** The frames in order, each one when the main thread says it is submitted: the main thread takes them back in the same order. */\nfunction runShared(kernel: ReturnType<typeof createKernel>, frames: readonly ReturnType<typeof createFrame>[], control: Int32Array): void {\n for (let turn = 0; ; turn++) {\n const slot = turn % frames.length\n for (;;) {\n const state = Atomics.load(control, slot)\n if (state === CONTROL.STOP) return\n if (state === CONTROL.SUBMITTED) break\n Atomics.wait(control, slot, state)\n }\n kernel.run(frames[slot]!)\n Atomics.store(control, slot, CONTROL.DONE)\n Atomics.notify(control, slot)\n }\n}\n","import { createFrame, frameBytes } from './frame'\nimport type { FrameShape, PhysicsFrame } from './frame'\nimport { createKernel } from './kernel'\nimport type { KernelSetup } from './kernel'\nimport type { OffloadTransport, PhysicsWorkerFactory } from './types'\nimport { CONTROL } from './worker-host'\nimport type { DoneMessage, FrameMessage, InitMessage } from './worker-host'\n\n/** A ring of `slots` frames: the main thread writes the next step into a free one, submits it, and takes the oldest submitted one back when it is done. */\nexport type StepRunner = {\n readonly transport: OffloadTransport\n /** The frame to write the inputs of the next step into; `null` when every frame is out (all in flight, or with the worker in the transfer transport). */\n frame(): PhysicsFrame | null\n /** Sends the frame `frame()` gave. */\n submit(): void\n /** Frames submitted and not yet taken back. */\n readonly inFlight: number\n /**\n * The oldest submitted frame with its outputs, and it is free again for `frame()` once they are read; `null` when it is not back yet. The shared and inline transports wait for\n * it (the worker computes in step with the main thread, so the answer never depends on timing); the transfer transport cannot wait for an event, so it answers when it can.\n */\n settle(): PhysicsFrame | null\n dispose(): void\n}\n\nconst WAIT_LIMIT_MS = 5000\n\nconst canBlock = (() => {\n try {\n Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 1, 0)\n return true\n } catch {\n return false\n }\n})()\n\nexport const isSharedAvailable = (): boolean => typeof SharedArrayBuffer !== 'undefined' && (globalThis as { crossOriginIsolated?: boolean }).crossOriginIsolated !== false\n\n/** The same protocol and the same kernel on the calling thread: the result is computed at `submit` and read at `settle`. */\nexport function createInlineRunner(setup: KernelSetup, shape: FrameShape, slots = 1): StepRunner {\n const kernel = createKernel(setup)\n const frames = Array.from({ length: slots }, () => createFrame(new ArrayBuffer(frameBytes(shape)), shape))\n let submitted = 0\n let settled = 0\n const runner = {\n transport: 'inline' as const,\n get inFlight() {\n return submitted - settled\n },\n frame: () => (submitted - settled < slots ? frames[submitted % slots]! : null),\n submit: () => kernel.run(frames[submitted++ % slots]!),\n settle: () => (submitted > settled ? frames[settled++ % slots]! : null),\n dispose: () => undefined,\n }\n return runner\n}\n\nexport function createSharedRunner(setup: KernelSetup, shape: FrameShape, factory: PhysicsWorkerFactory, slots = 1): StepRunner {\n const buffers = Array.from({ length: slots }, () => new SharedArrayBuffer(frameBytes(shape)))\n const control = new Int32Array(new SharedArrayBuffer(4 * slots))\n const frames = buffers.map((buffer) => createFrame(buffer, shape))\n const worker = factory()\n worker.postMessage({ type: 'init', setup, shape, frames: buffers, control: control.buffer as SharedArrayBuffer } satisfies InitMessage)\n let submitted = 0\n let settled = 0\n return {\n transport: 'shared',\n get inFlight() {\n return submitted - settled\n },\n frame: () => (submitted - settled < slots ? frames[submitted % slots]! : null),\n submit: () => {\n const slot = submitted++ % slots\n Atomics.store(control, slot, CONTROL.SUBMITTED)\n Atomics.notify(control, slot)\n },\n settle: () => {\n if (submitted === settled) return null\n const slot = settled++ % slots\n const started = Date.now()\n while (Atomics.load(control, slot) !== CONTROL.DONE) {\n if (Date.now() - started > WAIT_LIMIT_MS) throw new Error('crowd physics worker did not answer')\n if (canBlock) Atomics.wait(control, slot, CONTROL.SUBMITTED, 50)\n }\n Atomics.store(control, slot, CONTROL.IDLE)\n return frames[slot]!\n },\n dispose: () => {\n for (let slot = 0; slot < slots; slot++) {\n Atomics.store(control, slot, CONTROL.STOP)\n Atomics.notify(control, slot)\n }\n worker.terminate()\n },\n }\n}\n\nexport function createTransferRunner(setup: KernelSetup, shape: FrameShape, factory: PhysicsWorkerFactory, slots = 1): StepRunner {\n const worker = factory()\n const free: ArrayBuffer[] = Array.from({ length: slots }, () => new ArrayBuffer(frameBytes(shape)))\n const done: ArrayBuffer[] = []\n const views = new WeakMap<ArrayBuffer, PhysicsFrame>()\n const viewOf = (buffer: ArrayBuffer): PhysicsFrame => {\n let view = views.get(buffer)\n if (!view) views.set(buffer, (view = createFrame(buffer, shape)))\n return view\n }\n let inFlight = 0\n worker.postMessage({ type: 'init', setup, shape } satisfies InitMessage)\n worker.onMessage((data) => {\n const message = data as DoneMessage\n if (message.type === 'done') done.push(message.buffer)\n })\n return {\n transport: 'transfer',\n get inFlight() {\n return inFlight\n },\n frame: () => (free.length ? viewOf(free[0]!) : null),\n submit: () => {\n const buffer = free.shift()\n if (!buffer) return\n worker.postMessage({ type: 'frame', buffer } satisfies FrameMessage, [buffer])\n inFlight++\n },\n settle: () => {\n const buffer = done.shift()\n if (!buffer) return null\n inFlight--\n free.push(buffer)\n return viewOf(buffer)\n },\n dispose: () => worker.terminate(),\n }\n}\n","import type { PhysicsContext } from './context'\n\nconst KNOCK_SECONDS = 1.5\nconst KNOCK_COOLDOWN = 2\nconst CRUSH_BLOW_EVERY = 0.25\n\nexport type PlayerPressure = {\n /** `load` is the N the horde passed on to the target in this step. */\n step(load: number): void\n}\n\n/**\n * The pressure of the horde on the target (D13.2): the exponential mean of what the agents pass on to it; above `knockThreshold` it knocks the target down, and the excess\n * crushes at `crushDamagePerSecond` through the same `CrowdTarget` verbs the agents already use. Off, nothing is called.\n */\nexport function createPlayerPressure(ctx: PhysicsContext): PlayerPressure {\n const options = ctx.options.playerPressure\n const tau = ctx.options.balance?.pressureTau ?? 0.3\n let knockCooldown = 0\n let crushed = 0\n let crushClock = 0\n\n const crush = (excess: number): void => {\n crushed += options.crushDamagePerSecond * excess * ctx.h\n crushClock += ctx.h\n if (crushClock < CRUSH_BLOW_EVERY) return\n crushClock = 0\n if (crushed <= 0) return\n ctx.target()?.hurt(crushed)\n crushed = 0\n }\n\n const step = (load: number): void => {\n ctx.stats.playerPressure += (load - ctx.stats.playerPressure) * (1 - Math.exp(-ctx.h / tau))\n if (!options.enabled) return\n const target = ctx.target()\n knockCooldown = Math.max(0, knockCooldown - ctx.h)\n if (!target || !target.state.alive || ctx.stats.playerPressure <= options.knockThreshold) {\n crushed = 0\n return\n }\n if (knockCooldown === 0) {\n target.knock(KNOCK_SECONDS)\n knockCooldown = KNOCK_COOLDOWN\n }\n crush(ctx.stats.playerPressure / options.knockThreshold - 1)\n }\n\n return { step }\n}\n","import { SALT, unit } from '../identity'\nimport { BALANCE, FALL_CAUSE, STATE } from '../types'\nimport type { Corpse } from '../types'\nimport { takesPartInContact } from './contact'\nimport type { ContactView } from './contact'\nimport type { Balance } from './balance'\nimport type { PhysicsContext } from './context'\nimport type { CrowdThrownOptions } from './options'\n\nexport const FREE_SLOT = -2\nexport const CARRIED_BODY = -1\nconst MAX_SPIN = 8\n/** Past this distance per step the flight is swept cell by cell instead of tested at the new point. */\nconst SWEEP_DISTANCE = 0.25\nconst LANDING_DAMAGE_SPEED = 6\nconst CENTRE_HEIGHT = 1\nconst MAX_POOL = 127\n\n/** The thrown bodies, simulated here and only read by the renderer (`x`, `y`, `z` are the centre of the body, `prev*` the position of the step before). */\nexport type ThrownPool = {\n readonly size: number\n /** An agent index, `CARRIED_BODY` for a dead body carried until it lands, `FREE_SLOT` for an empty slot. */\n readonly agent: Int32Array\n readonly identity: Uint32Array\n readonly corpse: (Corpse | null)[]\n readonly x: Float32Array\n readonly y: Float32Array\n readonly z: Float32Array\n readonly prevX: Float32Array\n readonly prevY: Float32Array\n readonly prevZ: Float32Array\n readonly vx: Float32Array\n readonly vy: Float32Array\n readonly vz: Float32Array\n readonly pitch: Float32Array\n readonly roll: Float32Array\n readonly spinPitch: Float32Array\n readonly spinRoll: Float32Array\n active(): number\n}\n\nexport type ThrownCandidate = { readonly index: number; readonly speed: number; readonly vx: number; readonly vy: number; readonly vz: number; readonly distance: number }\n\nexport type Thrown = {\n readonly pool: ThrownPool\n /** Gives the slots to the candidates by the priority of the options; returns how many flew. */\n assign(candidates: ThrownCandidate[]): number\n step(contact: ContactView, balance: Balance): void\n /** The store removed agent `removed` and moved agent `moved` into its place (the same index when it was the last). */\n remap(removed: number, moved: number): void\n}\n\nexport const createThrownPool = (size: number): ThrownPool => {\n const f32 = (): Float32Array => new Float32Array(size)\n const pool: ThrownPool = {\n size,\n agent: new Int32Array(size).fill(FREE_SLOT),\n identity: new Uint32Array(size),\n corpse: Array.from({ length: size }, () => null),\n x: f32(),\n y: f32(),\n z: f32(),\n prevX: f32(),\n prevY: f32(),\n prevZ: f32(),\n vx: f32(),\n vy: f32(),\n vz: f32(),\n pitch: f32(),\n roll: f32(),\n spinPitch: f32(),\n spinRoll: f32(),\n active: () => pool.agent.reduce((count, agent) => count + (agent === FREE_SLOT ? 0 : 1), 0),\n }\n return pool\n}\n\nconst byFastest = (a: ThrownCandidate, b: ThrownCandidate, identityOf: (i: number) => number): number => b.speed - a.speed || identityOf(a.index) - identityOf(b.index)\nconst byNearest = (a: ThrownCandidate, b: ThrownCandidate, identityOf: (i: number) => number): number => a.distance - b.distance || identityOf(a.index) - identityOf(b.index)\n\n/** Who gets the free slots (`mixed`: `mixedRatio` of them to the fastest, the rest to the nearest of what is left). */\nexport function chooseCandidates(candidates: ThrownCandidate[], free: number, options: Required<CrowdThrownOptions>, identityOf: (i: number) => number): ThrownCandidate[] {\n if (candidates.length <= free) return candidates\n if (options.priority === 'fastest') return candidates.sort((a, b) => byFastest(a, b, identityOf)).slice(0, free)\n if (options.priority === 'nearest') return candidates.sort((a, b) => byNearest(a, b, identityOf)).slice(0, free)\n const fast = Math.round(free * options.mixedRatio)\n const sorted = candidates.sort((a, b) => byFastest(a, b, identityOf))\n const rest = sorted.slice(fast).sort((a, b) => byNearest(a, b, identityOf))\n return [...sorted.slice(0, fast), ...rest.slice(0, free - fast)]\n}\n\nexport function createThrown(ctx: PhysicsContext): Thrown {\n const { store, grid } = ctx\n const options = ctx.options.thrown!\n const size = Math.min(MAX_POOL, options.pool)\n const pool = createThrownPool(size)\n const identityOf = (i: number): number => store.identity[i]!\n\n const assign = (candidates: ThrownCandidate[]): number => {\n let free = 0\n for (let slot = 0; slot < size; slot++) if (pool.agent[slot] === FREE_SLOT) free++\n const chosen = chooseCandidates(candidates, free, options, identityOf)\n let slot = 0\n for (const candidate of chosen) {\n while (pool.agent[slot] !== FREE_SLOT) slot++\n const i = candidate.index\n pool.agent[slot] = i\n pool.identity[slot] = store.identity[i]!\n pool.corpse[slot] = null\n pool.x[slot] = pool.prevX[slot] = store.x[i]!\n pool.y[slot] = pool.prevY[slot] = store.y[i]! + CENTRE_HEIGHT * store.height[i]!\n pool.z[slot] = pool.prevZ[slot] = store.z[i]!\n pool.vx[slot] = candidate.vx\n pool.vy[slot] = candidate.vy\n pool.vz[slot] = candidate.vz\n pool.pitch[slot] = pool.roll[slot] = 0\n pool.spinPitch[slot] = (unit(store.identity[i]!, ctx.step, SALT.SPIN) * 2 - 1) * MAX_SPIN\n pool.spinRoll[slot] = (unit(store.identity[i]!, ctx.step, SALT.SPIN + 1) * 2 - 1) * MAX_SPIN\n if (store.state[i] === STATE.PILE_STEP) ctx.releasePileStep(i)\n store.balance[i] = BALANCE.THROWN\n store.thrown[i] = slot\n store.vx[i] = store.vz[i] = store.pushX[i] = store.pushZ[i] = 0\n store.aimX[i] = store.aimZ[i] = 0\n }\n return chosen.length\n }\n\n const remap = (removed: number, moved: number): void => {\n for (let slot = 0; slot < size; slot++) {\n const agent = pool.agent[slot]!\n if (agent === removed) {\n pool.corpse[slot] ??= ctx.corpseOf(removed, unit(pool.identity[slot]!, ctx.step, SALT.CORPSE) * 10)\n pool.agent[slot] = CARRIED_BODY\n } else if (agent === moved && moved !== removed) pool.agent[slot] = removed\n }\n }\n\n /** The first solid cell on the horizontal path of one step, by the grid traversal of Amanatides and Woo; undefined when the path is clear. */\n const sweepWall = (x0: number, z0: number, x1: number, z1: number, y: number): { x: number; z: number } | undefined => {\n const cell = grid.cell\n let gx = Math.floor((x0 + grid.half) / cell)\n let gz = Math.floor((z0 + grid.half) / cell)\n const dx = x1 - x0\n const dz = z1 - z0\n const stepX = dx > 0 ? 1 : -1\n const stepZ = dz > 0 ? 1 : -1\n const deltaX = dx === 0 ? Infinity : Math.abs(cell / dx)\n const deltaZ = dz === 0 ? Infinity : Math.abs(cell / dz)\n let nextX = dx === 0 ? Infinity : ((dx > 0 ? gx + 1 : gx) * cell - grid.half - x0) / dx\n let nextZ = dz === 0 ? Infinity : ((dz > 0 ? gz + 1 : gz) * cell - grid.half - z0) / dz\n let t = 0\n while (Math.min(nextX, nextZ) <= 1) {\n if (nextX < nextZ) {\n t = nextX\n nextX += deltaX\n gx += stepX\n } else {\n t = nextZ\n nextZ += deltaZ\n gz += stepZ\n }\n const hitX = x0 + dx * t\n const hitZ = z0 + dz * t\n if (grid.solidAt(hitX + Math.sign(dx) * 1e-4, hitZ + Math.sign(dz) * 1e-4, y)) return { x: x0 + dx * Math.max(0, t - 1e-3), z: z0 + dz * Math.max(0, t - 1e-3) }\n }\n return undefined\n }\n\n const fly = (slot: number): void => {\n pool.prevX[slot] = pool.x[slot]!\n pool.prevY[slot] = pool.y[slot]!\n pool.prevZ[slot] = pool.z[slot]!\n pool.vy[slot]! -= ctx.gravity * ctx.h\n pool.pitch[slot]! += pool.spinPitch[slot]! * ctx.h\n pool.roll[slot]! += pool.spinRoll[slot]! * ctx.h\n const nx = pool.x[slot]! + pool.vx[slot]! * ctx.h\n const nz = pool.z[slot]! + pool.vz[slot]! * ctx.h\n const ny = pool.y[slot]! + pool.vy[slot]! * ctx.h\n const speed = Math.hypot(pool.vx[slot]!, pool.vy[slot]!, pool.vz[slot]!)\n const wall = speed * ctx.h > SWEEP_DISTANCE ? sweepWall(pool.x[slot]!, pool.z[slot]!, nx, nz, ny) : grid.solidAt(nx, nz, ny) ? { x: pool.x[slot]!, z: pool.z[slot]! } : undefined\n pool.x[slot] = wall ? wall.x : nx\n pool.z[slot] = wall ? wall.z : nz\n pool.y[slot] = ny\n if (wall) pool.vx[slot] = pool.vz[slot] = 0\n }\n\n const massOf = (slot: number): number => {\n const agent = pool.agent[slot]!\n if (agent >= 0) return store.mass[agent]!\n const body = pool.corpse[slot]!\n return ctx.options.contact!.baseMass * body.height * body.build * body.build\n }\n\n const knockNeighbours = (contact: ContactView, balance: Balance, slot: number): void => {\n const side = Math.floor(grid.size)\n const x = pool.x[slot]!\n const z = pool.z[slot]!\n const hx = Math.floor(x + grid.half)\n const hz = Math.floor(z + grid.half)\n const mass = massOf(slot)\n const speed = Math.hypot(pool.vx[slot]!, pool.vz[slot]!)\n const reach = options.landingRadius * options.landingRadius\n for (let oz = -1; oz <= 1; oz++)\n for (let ox = -1; ox <= 1; ox++) {\n if (hx + ox < 0 || hz + oz < 0 || hx + ox >= side || hz + oz >= side) continue\n for (let j = contact.workspace.head[(hz + oz) * side + hx + ox]!; j !== -1; j = contact.workspace.next[j]!) {\n const ex = store.x[j]! - x\n const ez = store.z[j]! - z\n if (j === pool.agent[slot] || ex * ex + ez * ez > reach || !takesPartInContact(store, j)) continue\n const away = Math.hypot(ex, ez)\n const along = away > 1e-6 ? [ex / away, ez / away] : [pool.vx[slot]! / (speed || 1), pool.vz[slot]! / (speed || 1)]\n const delta = (mass / (mass + store.mass[j]!)) * speed\n store.pushX[j]! += delta * along[0]!\n store.pushZ[j]! += delta * along[1]!\n balance.shove(j, delta, FALL_CAUSE.LANDING)\n }\n }\n }\n\n const land = (contact: ContactView, balance: Balance, slot: number, ground: number): void => {\n knockNeighbours(contact, balance, slot)\n const x = pool.x[slot]!\n const z = pool.z[slot]!\n ctx.emit('sound', { sound: 'impact', volume: 1.2, position: [x, ground, z], distance: Math.hypot(x - ctx.centre.x, z - ctx.centre.z) })\n ctx.emit('effect', { effect: 'dust', point: [x, ground, z], color: 0x8a7a66 })\n const agent = pool.agent[slot]!\n if (agent >= 0) {\n store.balance[agent] = BALANCE.FALLEN\n store.balanceTimer[agent] = 0\n store.fallCause[agent] = FALL_CAUSE.BLAST\n store.x[agent] = x\n store.z[agent] = z\n store.y[agent] = ground\n store.vy[agent] = 0\n store.thrown[agent] = -1\n const amount = Math.max(0, -pool.vy[slot]! - LANDING_DAMAGE_SPEED) * options.fallDamage\n if (amount > 0) ctx.damage(agent, { region: 1, amount, distance: 0 }, () => unit(store.identity[agent]!, ctx.step, SALT.JAW))\n } else {\n ctx.emit('effect', { effect: 'blood', point: [x, ground + 0.1, z], amount: 30, died: true })\n ctx.addCorpse({ ...pool.corpse[slot]!, x, z })\n }\n pool.agent[slot] = FREE_SLOT\n pool.corpse[slot] = null\n }\n\n const step = (contact: ContactView, balance: Balance): void => {\n let active = 0\n for (let slot = 0; slot < size; slot++) {\n if (pool.agent[slot] === FREE_SLOT) continue\n fly(slot)\n const cell = grid.cellOf(pool.x[slot]!, pool.z[slot]!)\n const ground = cell < 0 ? 0 : ctx.groundAt(cell, pool.y[slot]!)\n const agent = pool.agent[slot]!\n if (pool.y[slot]! <= ground) {\n land(contact, balance, slot, ground)\n continue\n }\n active++\n if (agent < 0) continue\n store.x[agent] = pool.x[slot]!\n store.z[agent] = pool.z[slot]!\n store.y[agent] = Math.max(ground, pool.y[slot]! - CENTRE_HEIGHT * store.height[agent]!)\n }\n ctx.stats.thrownActive = active\n ctx.stats.thrownPeak = Math.max(ctx.stats.thrownPeak, active)\n }\n\n return { pool, assign, step, remap }\n}\n","import type { CrowdType } from '../types'\nimport { createBalance } from './balance'\nimport type { Balance } from './balance'\nimport { createBlasts } from './blast'\nimport type { Blasts } from './blast'\nimport { contactStep, createContactView, prepareHash } from './contact'\nimport type { ContactResult, ContactView } from './contact'\nimport { createPhysicsStats } from './context'\nimport type { PhysicsContext, PhysicsStats } from './context'\nimport { createDensity } from './density'\nimport type { Density } from './density'\nimport { createMound } from './mound'\nimport type { Mound } from './mound'\nimport type { ResolvedPhysics } from './options'\nimport { createDefaultWorkerFactory } from './offload/default-worker'\nimport { createOffload } from './offload/offload'\nimport type { Offload } from './offload/offload'\nimport { createInlineRunner, createSharedRunner, createTransferRunner, isSharedAvailable } from './offload/runner'\nimport type { StepRunner } from './offload/runner'\nimport { createPlayerPressure } from './player-pressure'\nimport type { PlayerPressure } from './player-pressure'\nimport { createPressureWorkspace, pressureStep } from './pressure'\nimport type { PressureResult, PressureWorkspace } from './pressure'\nimport { createThrown, createThrownPool } from './thrown'\nimport type { Thrown, ThrownPool } from './thrown'\n\n/** Newtons of will of a type that says nothing else (`design-chain-horde.md` §5.1). */\nexport const DEFAULT_DRIVE_FORCE = 250\nconst GRAVITY_FORCE = 9.8\nconst EXTRA_BODIES = 500\nconst PAIRS_PER_AGENT = 12\n\n/** What the crowd hands over: the shared state and the verbs only it can do. */\nexport type PhysicsBase = Omit<PhysicsContext, 'options' | 'stats' | 'step' | 'h' | 'centre' | 'lastBlastStep' | 'moundAt' | 'weightOn'> & {\n readonly types: readonly CrowdType[]\n readonly moundHeight: Uint16Array\n readonly corpsesMax: number\n pileBlast(x: number, z: number, radius: number): void\n}\n\nexport type PhysicsEngine = {\n readonly ctx: PhysicsContext\n /** True when the contact is on: the rest of the parts depend on it. */\n readonly active: boolean\n readonly stats: PhysicsStats\n readonly blasts: Blasts\n readonly thrown: ThrownPool\n readonly contact: ContactView | null\n readonly balance: Balance | null\n /** Starts the fixed step: keeps the positions, counts the step and resolves the blasts that arrived. */\n beginStep(h: number, centre: { readonly x: number; readonly z: number }): void\n /** Contact, pressure, balance, mound and thrown bodies, in the order of the design (§3, items 5 to 10). */\n endStep(): void\n /** The store removed agent `removed` and moved agent `moved` into its place. */\n remap(removed: number, moved: number): void\n /** Stops the worker, if there is one. */\n dispose(): void\n /** What the wanted speed of an agent of band `band` is multiplied by in a thick far crowd (1 with the density off or in any other band). */\n densitySlowdown(i: number, band: number): number\n /** `pressure / critical pressure` of an agent: how close it is to falling. */\n pressureRatio(i: number): number\n}\n\nexport function createPhysics(base: PhysicsBase, options: ResolvedPhysics, stats: PhysicsStats = createPhysicsStats()): PhysicsEngine {\n const moundOn = options.mound !== null\n const ctx: PhysicsContext = { ...base, options, stats, step: -1, h: 0, centre: { x: 0, z: 0 }, lastBlastStep: -1000, moundAt: (cell) => (moundOn ? base.moundHeight[cell]! : 0), weightOn: () => 0 }\n const { store, grid } = base\n const balance = options.balance ? createBalance(ctx) : null\n const thrown: Thrown | null = options.thrown ? createThrown(ctx) : null\n const contact = options.contact ? createContactView(store, grid) : null\n const pressure: PressureWorkspace | null = options.pressure && contact ? createPressureWorkspace(store.capacity, store.capacity * PAIRS_PER_AGENT) : null\n const mound: Mound | null = options.mound ? createMound(ctx, Math.max(EXTRA_BODIES, base.corpsesMax), base.moundHeight) : null\n if (mound) ctx.weightOn = mound.weightOn\n const density: Density | null = options.density && balance ? createDensity(ctx, options.density) : null\n const playerPressure: PlayerPressure | null = pressure ? createPlayerPressure(ctx) : null\n const blasts = createBlasts(ctx, { balance, thrown, pileBlast: base.pileBlast })\n const contactResult: ContactResult = { contacts: 0, maxOverlap: 0, overlapSum: 0 }\n const pressureResult: PressureResult = { playerLoad: 0, drivers: 0 }\n const driveForceOfType = Float32Array.from(base.types, (type) => type.balance?.driveForce ?? DEFAULT_DRIVE_FORCE)\n const offload: Offload | null = options.worker !== 'off' && contact ? createOffloadFor() : null\n let doorLoad = new Float32Array(0)\n let doorSince = new Float32Array(0)\n const keepPositions = options.contact !== null || options.deterministic\n\n function createRunner(): StepRunner | null {\n const gridCells = grid.occ.length\n const shape = { capacity: store.capacity, cells: gridCells }\n const setup = { capacity: store.capacity, gridSize: grid.size, gridCell: grid.cell, contact: options.contact!, pressure: options.pressure, driveForceOfType: Array.from(driveForceOfType) }\n const transport = options.workerTransport ?? (isSharedAvailable() ? 'shared' : 'transfer')\n const slots = options.workerLatency\n if (transport === 'inline') return createInlineRunner(setup, shape, slots)\n const factory = options.workerFactory ?? createDefaultWorkerFactory()\n if (!factory) {\n if (options.worker === 'on') throw new Error('crowd physics: worker \"on\" but no Worker can be made here (give physics.workerFactory)')\n return null\n }\n return transport === 'shared' ? createSharedRunner(setup, shape, factory, slots) : createTransferRunner(setup, shape, factory, slots)\n }\n\n function createOffloadFor(): Offload | null {\n const runner = createRunner()\n if (!runner) return null\n const host = {\n store,\n grid,\n stats,\n contact: contact!,\n addDoorLoad: (cell: number, load: number): void => {\n const door = ctx.doors.at(cell)\n if (door < 0) return\n growDoorArrays()\n doorLoad[door]! += load\n },\n }\n return createOffload(runner, host, options.workerLatency)\n }\n\n const beginStep = (h: number, centre: { readonly x: number; readonly z: number }): void => {\n ctx.step++\n ctx.h = h\n ctx.centre = { x: centre.x, z: centre.z }\n if (keepPositions) {\n for (let i = 0; i < store.n; i++) {\n store.prevX[i] = store.x[i]!\n store.prevZ[i] = store.z[i]!\n store.prevY[i] = store.y[i]!\n }\n }\n blasts.resolve()\n }\n\n /** The load the field passed to each door in the step; every `attackEvery` seconds it becomes one blow. */\n const hitDoors = (): void => {\n for (let door = 0; door < ctx.doors.list.length; door++) {\n if (doorLoad[door]! <= 0) continue\n doorSince[door]! += ctx.h\n if (doorSince[door]! < ctx.attackEvery) continue\n ctx.doors.hit(door, doorLoad[door]! * options.doorPressureDamage)\n doorLoad[door] = 0\n doorSince[door] = 0\n }\n }\n\n const growDoorArrays = (): void => {\n if (doorLoad.length >= ctx.doors.list.length) return\n const length = Math.max(64, ctx.doors.list.length * 2)\n doorLoad = Float32Array.from({ length }, (_, door) => doorLoad[door] ?? 0)\n doorSince = Float32Array.from({ length }, (_, door) => doorSince[door] ?? 0)\n }\n\n const runPressure = (view: ContactView, workspace: PressureWorkspace): void => {\n growDoorArrays()\n const target = ctx.target()\n const player = options.playerPressure.enabled && target && target.state.alive ? { x: target.state.x, z: target.state.z } : undefined\n pressureStep(view, workspace, { ...options.pressure!, h: ctx.h, radius: options.contact!.radius }, { driveForceOfType, doorAt: ctx.doors.at, doorLoad, player }, pressureResult)\n let top = 0\n for (let i = 0; i < store.n; i++) top = Math.max(top, store.field[i]!)\n stats.fieldMax = top\n hitDoors()\n }\n\n /** The contact and the pressure ran in the worker on the state of the previous step: apply them, hand over this step, and keep the hash for the thrown bodies. */\n const stepOffloaded = (running: Offload, view: ContactView): void => {\n growDoorArrays()\n const target = ctx.target()\n const player = options.playerPressure.enabled && target && target.state.alive ? { x: target.state.x, z: target.state.z } : undefined\n const result = running.step(ctx.h, player)\n pressureResult.playerLoad = result.playerLoad\n prepareHash(view, options.contact!.radius)\n if (pressure) hitDoors()\n }\n\n const measureLean = (): void => {\n const maxPush = options.contact!.maxPush\n for (let i = 0; i < store.n; i++) {\n store.leanX[i] = store.pushX[i]! / maxPush\n store.leanZ[i] = store.pushZ[i]! / maxPush\n }\n }\n\n const endStep = (): void => {\n if (!contact) return\n contact.n = store.n\n if (offload) stepOffloaded(offload, contact)\n else {\n contactStep(contact, { ...options.contact!, h: ctx.h }, contactResult)\n stats.contacts = contactResult.contacts\n stats.maxOverlap = contactResult.maxOverlap\n stats.meanOverlap = contactResult.contacts ? contactResult.overlapSum / contactResult.contacts : 0\n if (pressure) runPressure(contact, pressure)\n }\n density?.step()\n measureLean()\n balance?.step(options.balance!, contact.workspace.shove)\n mound?.step()\n thrown?.step(contact, balance!)\n balance?.flush()\n playerPressure?.step(pressureResult.playerLoad)\n }\n\n const criticalOf = (i: number): number => options.balance!.criticalFactor * store.mass[i]! * GRAVITY_FORCE * (base.typeOf(i).balance?.stability ?? 1) * store.stability[i]!\n\n return {\n ctx,\n active: contact !== null,\n stats,\n blasts,\n thrown: thrown?.pool ?? createThrownPool(0),\n contact,\n balance,\n beginStep,\n endStep,\n remap: (removed, moved) => {\n thrown?.remap(removed, moved)\n offload?.remap(removed, moved)\n },\n dispose: () => offload?.dispose(),\n densitySlowdown: (i, band) => density?.slowdown(i, band) ?? 1,\n pressureRatio: (i) => (options.balance ? store.pressure[i]! / criticalOf(i) : 0),\n }\n}\n","import type { CrowdStore } from '../store'\nimport { STATE } from '../types'\n\n/** What a falling pile step marks on the steps that come down with it, so the landing knows it was a collapse and not a jump. */\nexport const COLLAPSE_MARK = -7\n\nexport type PileColumn = { readonly cell: number; readonly dir: number }\n\n/**\n * The steps of the pile behind a step that went, in its column (`cell - dir`, `cell - 2 dir`, ...), come down with it: they let go of the pile and fall from where they are. The column stops\n * at the first cell with no step in it.\n */\nexport function collapsePileColumns(store: CrowdStore, columns: PileColumn[], cellCount: number, release: (i: number) => void): void {\n const owners = new Map<number, number>()\n for (let i = 0; i < store.n; i++) if (store.state[i] === STATE.PILE_STEP && store.stepCell[i]! >= 0) owners.set(store.stepCell[i]!, i)\n for (const column of columns.splice(0)) {\n if (column.dir === 0) continue\n for (let k = 1, cell = column.cell - column.dir; cell >= 0 && cell < cellCount; k++, cell = column.cell - column.dir * k) {\n const j = owners.get(cell)\n if (j === undefined) break\n owners.delete(cell)\n release(j)\n store.state[j] = STATE.AIRBORNE\n store.vy[j] = 0\n store.stateTimer[j] = COLLAPSE_MARK\n }\n }\n}\n","import { SALT, unit } from './identity'\nimport type { CrowdPersonalityOptions } from './physics/options'\nimport type { CrowdStore } from './store'\n\nconst GAIT_WEIGHTS = [6, 1, 1, 1, 1, 2] as const\n\n/** A triangular draw between the two ends of the range, from two independent counter-based draws of the agent. */\nconst triangular = (identity: number, salt: number, range: readonly [number, number]): number => range[0] + (range[1] - range[0]) * ((unit(identity, 0, salt) + unit(identity, 1, salt)) / 2)\n\nconst weighted = (identity: number, salt: number, weights: readonly number[]): number => {\n const total = weights.reduce((sum, weight) => sum + weight, 0)\n let pick = unit(identity, 0, salt) * total\n for (let index = 0; index < weights.length; index++) {\n pick -= weights[index]!\n if (pick < 0) return index\n }\n return weights.length - 1\n}\n\n/** No personality: every factor is neutral, so the maths of the game does not change. */\nexport function applyNeutralPersonality(store: CrowdStore, i: number): void {\n store.stability[i] = store.tripFactor[i] = store.driveFactor[i] = store.cadenceFactor[i] = store.aggression[i] = 1\n store.reaction[i] = 0\n store.idle[i] = 0\n store.gaitStyle[i] = 0\n}\n\n/**\n * The personality of an agent (T4b.8), all of it from its identity and never from the shared random stream: a speed factor that multiplies the chase and wander speeds,\n * a reaction time, the stability, trip and drive factors that move its thresholds of falling and climbing, the cadence of its walk, its aggression, a gait and an idle.\n */\nexport function applyPersonality(store: CrowdStore, i: number, options: Required<CrowdPersonalityOptions>): void {\n const identity = store.identity[i]!\n const speed = triangular(identity, SALT.SPEED, options.speed)\n store.chaseSpeed[i]! *= speed\n store.wanderSpeed[i]! *= speed\n store.reaction[i] = triangular(identity, SALT.REACTION, options.reaction)\n store.stability[i] = triangular(identity, SALT.STABILITY, options.stability)\n store.tripFactor[i] = triangular(identity, SALT.TRIP_FACTOR, options.trip)\n store.driveFactor[i] = triangular(identity, SALT.DRIVE, options.drive)\n store.cadenceFactor[i] = triangular(identity, SALT.CADENCE, options.cadence)\n store.aggression[i] = triangular(identity, SALT.AGGRESSION, options.aggression)\n store.idle[i] = weighted(identity, SALT.IDLE, options.idle)\n store.gaitStyle[i] = weighted(identity, SALT.GAIT, GAIT_WEIGHTS)\n}\n","import { identityOf } from '../identity'\nimport { applyNeutralPersonality, applyPersonality } from '../personality'\nimport type { CrowdStore } from '../store'\nimport { BALANCE } from '../types'\nimport type { CrowdPersonalityOptions } from './options'\n\n/** The fields of the physics of a newborn: identity, mass and personality, all without a draw from the shared stream. */\nexport function seatPhysics(store: CrowdStore, i: number, born: { readonly seed: number; readonly serial: number; readonly baseMass: number; readonly personality: Required<CrowdPersonalityOptions> | null }): void {\n store.identity[i] = identityOf(born.seed, born.serial)\n store.mass[i] = born.baseMass * store.height[i]! * store.build[i]! ** 2\n store.balance[i] = BALANCE.STANDING\n store.balanceTimer[i] = store.pushX[i] = store.pushZ[i] = store.pressure[i] = store.field[i] = store.fieldPrev[i] = store.aimX[i] = store.aimZ[i] = store.directShove[i] = store.leanX[i] = store.leanZ[i] = 0\n store.fallCause[i] = 0\n store.blastStep[i] = -1\n store.thrown[i] = -1\n store.wounds[i] = 0\n store.stepDir[i] = 0\n store.prevX[i] = store.x[i]!\n store.prevZ[i] = store.z[i]!\n store.prevY[i] = 0\n if (born.personality) applyPersonality(store, i, born.personality)\n else applyNeutralPersonality(store, i)\n}\n","import type { BlastShoveOptions } from '@adatechnology/game-contracts'\n\nimport type { OffloadTransport, PhysicsWorkerFactory } from './offload/types'\n\nexport type CrowdContactOptions = {\n /** Body radius over build (default 0.25 → 0.5 m between two average bodies; the separation keeps them at 0.78 m when it can). */\n readonly radius?: number\n /** Iterations per fixed step in band R0 (default 2; 3 is the ceiling). */\n readonly iterations?: 1 | 2 | 3\n /** Push velocity damping per second: push ← push·exp(−friction·h) (default 6 → λ = 0.905 at 60 Hz). */\n readonly friction?: number\n /** Ceiling of the push velocity, m/s (default 3). */\n readonly maxPush?: number\n /** Ceiling of the position correction of one agent in one iteration, m (default 0.1; the design started at 0.06). */\n readonly maxCorrection?: number\n /** Jacobi relaxation of the position correction (default 1; the design started at 0.5, which left 0.25 m overlaps in a crush of 800). */\n readonly relaxation?: number\n /** kg of a body of height 1 and build 1 (default 70). */\n readonly baseMass?: number\n /** Ceiling of the separation term `|Σ sep · separationPush|`, m/s (default 1.5). */\n readonly separationMax?: number\n}\n\nexport type CrowdPressureOptions = {\n /** Share kept per hop, τ (default 0.97). */\n readonly transmission?: number\n /** Ceiling of the field per agent, N (default 6000). */\n readonly cap?: number\n /** Cosine of the half angle that counts as \"in front\" (default 0.5 → 60°). */\n readonly frontCone?: number\n /** Share of the will speed of a staggering agent (default 0.35); the field keeps the full drive. */\n readonly staggerSpeed?: number\n}\n\nexport type CrowdBalanceOptions = {\n /** `P_crit = criticalFactor · m · g · stability` (default 2.0 → 1372 N for a walker). */\n readonly criticalFactor?: number\n /** P/P_crit to start staggering (default 0.7). */\n readonly staggerEnter?: number\n /** P/P_crit to stop, after `staggerMin` (default 0.4). */\n readonly staggerLeave?: number\n /** Seconds (default 0.5). */\n readonly staggerMin?: number\n /** 1/s, rate = fallRate·(P/P_crit − 1)² above 1 (default 1.5). */\n readonly fallRate?: number\n /** |Δpush| in one step that staggers, m/s (default 1.2). */\n readonly shoveStagger?: number\n /** |Δpush| that knocks down, m/s, times stability (default 2.5). */\n readonly shoveFall?: number\n /** Seconds on the ground before trying to get up (default 1.5, plus reaction). */\n readonly fallenMin?: number\n /** P/P_crit under which a fallen one may get up (default 0.3). */\n readonly releaseFactor?: number\n /** Default 1.2. */\n readonly getUpSeconds?: number\n /** Seconds of the exponential mean (default 0.3). */\n readonly pressureTau?: number\n /** Ceiling of new falls per fixed step (default 8). */\n readonly maxFallsPerStep?: number\n /** Ceiling of agents on the ground (default 300); above it a fall becomes a stagger. */\n readonly maxFallen?: number\n /** Damage per second to a fallen body per 70 kg standing on it (default 6). */\n readonly trampleDps?: number\n}\n\nexport type CrowdMoundOptions = {\n /** m per cell of a lying body, times build (default 0.28). */\n readonly bodyThickness?: number\n /** Default 0.25. */\n readonly corpseThickness?: number\n /** Ceiling of the mound per cell, m (default 1.6, about 5 or 6 bodies). */\n readonly maxHeight?: number\n /** Corpses within this distance of the band centre count (default 35, band R0). */\n readonly corpseRadius?: number\n}\n\nexport type CrowdThrownOptions = {\n /** Slots of the pool (default 32, at most 127). */\n readonly pool?: number\n /** Only within this distance of the band centre (default 30 m). */\n readonly radius?: number\n /** m around the landing point that is knocked (default 1.2: a body at 10 m/s knocks the ones within about two body widths; 0.7 left the chain beyond a grenade at 0 in 4 of 12 scenes). */\n readonly landingRadius?: number\n /** Damage per m/s of landing speed above 6 m/s (default 8). */\n readonly fallDamage?: number\n /** Who gets a slot when there are more candidates than slots: half the slots to the fastest and half to the nearest to the band centre (`'mixed'`, default), or only one criterion. */\n readonly priority?: 'mixed' | 'fastest' | 'nearest'\n /** Share of the slots that goes to the fastest in `'mixed'` (default 0.5). */\n readonly mixedRatio?: number\n}\n\nexport type CrowdBlastOptions = {\n /** Damage at the centre per kJ before falloff (default 0.6 → m67 120 points). */\n readonly pointsPerKilojoule?: number\n /** Shove speed from which a body flies, m/s (default 4). */\n readonly throwSpeed?: number\n /** Blasts resolved in one fixed step; more wait for the next one, in order (default 8). */\n readonly maxBlastsPerStep?: number\n /** `limb` effects per blast; the rest is summed into one blood effect (default 24). */\n readonly limbEffectsPerBlast?: number\n /** `blood` effects per blast (default 32). */\n readonly bloodEffectsPerBlast?: number\n /** The shove rule of `game-contracts`, the same numbers the host lends to the items and the furniture. */\n readonly shove?: BlastShoveOptions\n}\n\nexport type CrowdPersonalityOptions = {\n /** Chase and wander factor (default [0.9, 1.1]). */\n readonly speed?: readonly [number, number]\n /** Seconds added to getting up and to the alert hop (default [0.05, 0.35]). */\n readonly reaction?: readonly [number, number]\n readonly stability?: readonly [number, number]\n readonly trip?: readonly [number, number]\n /** Pushing will (default [0.7, 1.3]). */\n readonly drive?: readonly [number, number]\n /** Walk cadence factor (default [0.85, 1.15]). */\n readonly cadence?: readonly [number, number]\n /** Attack will (default [0.8, 1.2]): divides `attackEvery`, multiplies the bite chance and decides who gets the last attack slots. */\n readonly aggression?: readonly [number, number]\n /** Weights of sway, shuffle, head twitch, still (default [3, 2, 2, 1]). */\n readonly idle?: readonly number[]\n}\n\n/** Band R1, summarised (D10, `design-chain-horde.md` §9): no pairs, a coarse grid of bodies per cell. */\nexport type CrowdDensityOptions = {\n /** Side of a coarse cell, m (default 2). */\n readonly cell?: number\n /** Bodies per cell that walk freely (default 4, one per square metre); above it the speed is `capacity / bodies`. */\n readonly capacity?: number\n /** N of pressure per body above `capacity` (default 150). */\n readonly pressurePerBody?: number\n}\n\n/** Pressure of the horde on the target (D13.2): above `knockThreshold` N it knocks the target down; the excess crushes at `crushDamagePerSecond`. */\nexport type CrowdPlayerPressureOptions = {\n readonly enabled?: boolean\n readonly knockThreshold?: number\n readonly crushDamagePerSecond?: number\n}\n\nexport type CrowdPhysicsOptions = {\n readonly contact?: CrowdContactOptions | boolean\n /** Needs `contact`. */\n readonly pressure?: CrowdPressureOptions | boolean\n /** Needs `contact`. */\n readonly balance?: CrowdBalanceOptions | boolean\n /** Needs `balance`. */\n readonly mound?: CrowdMoundOptions | boolean\n /** Needs `balance`. */\n readonly thrown?: CrowdThrownOptions | boolean\n /** Needs `balance`. The far band without pairs: speed and pressure from the bodies per coarse cell. */\n readonly density?: CrowdDensityOptions | boolean\n readonly blast?: CrowdBlastOptions\n readonly personality?: CrowdPersonalityOptions | boolean\n readonly door?: { readonly pressureDamage?: number }\n /** Needs `pressure`. Off in the package; a game turns it on. */\n readonly playerPressure?: CrowdPlayerPressureOptions\n /** Flow budget in cells per step, `attackPose` in the simulation, positions kept for interpolation. */\n readonly deterministic?: boolean\n /**\n * Where the contact and the pressure run. `'off'` (default): in the step, exactly as before. `'on'`: in a worker, one step behind (the main thread applies what the worker\n * computed from the previous step), and an error when no worker can be made. `'auto'`: the same when a worker can be made, `'off'` when it cannot. Needs `contact`.\n */\n readonly worker?: 'off' | 'auto' | 'on'\n /** Makes the worker (default: a module worker that loads `physics-worker.js` next to the bundle). A host that bundles its own worker gives the factory and calls `attachPhysicsWorker` in it. */\n readonly workerFactory?: PhysicsWorkerFactory\n /** `'shared'` (a `SharedArrayBuffer`, deterministic, needs `crossOriginIsolated`), `'transfer'` (`postMessage`, any host, the answer is as late as the event loop makes it), `'inline'` (no worker: the same protocol on the main thread, the reference). Default: `'shared'` when it is available, else `'transfer'`. */\n readonly workerTransport?: OffloadTransport\n /** Steps between the state the worker is handed and the step that applies its answer (default 1). 2 gives the worker a whole step to finish in, so the main thread never waits for it, and costs one more step of lag in the contact. */\n readonly workerLatency?: 1 | 2\n}\n\nexport const CONTACT_DEFAULTS: Required<CrowdContactOptions> = { radius: 0.25, iterations: 2, friction: 6, maxPush: 3, maxCorrection: 0.1, relaxation: 1, baseMass: 70, separationMax: 1.5 }\nexport const PRESSURE_DEFAULTS: Required<CrowdPressureOptions> = { transmission: 0.97, cap: 6000, frontCone: 0.5, staggerSpeed: 0.35 }\nexport const BALANCE_DEFAULTS: Required<CrowdBalanceOptions> = {\n criticalFactor: 2,\n staggerEnter: 0.7,\n staggerLeave: 0.4,\n staggerMin: 0.5,\n fallRate: 1.5,\n shoveStagger: 1.2,\n shoveFall: 2.5,\n fallenMin: 1.5,\n releaseFactor: 0.3,\n getUpSeconds: 1.2,\n pressureTau: 0.3,\n maxFallsPerStep: 8,\n maxFallen: 300,\n trampleDps: 6,\n}\nexport const MOUND_DEFAULTS: Required<CrowdMoundOptions> = { bodyThickness: 0.28, corpseThickness: 0.25, maxHeight: 1.6, corpseRadius: 35 }\nexport const THROWN_DEFAULTS: Required<CrowdThrownOptions> = { pool: 32, radius: 30, landingRadius: 1.2, fallDamage: 8, priority: 'mixed', mixedRatio: 0.5 }\nexport const DENSITY_DEFAULTS: Required<CrowdDensityOptions> = { cell: 2, capacity: 4, pressurePerBody: 150 }\nexport const BLAST_DEFAULTS: Required<CrowdBlastOptions> = { pointsPerKilojoule: 0.6, throwSpeed: 4, maxBlastsPerStep: 8, limbEffectsPerBlast: 24, bloodEffectsPerBlast: 32, shove: {} }\nexport const PERSONALITY_DEFAULTS: Required<CrowdPersonalityOptions> = {\n speed: [0.9, 1.1],\n reaction: [0.05, 0.35],\n stability: [0.75, 1.25],\n trip: [0.5, 1.5],\n drive: [0.7, 1.3],\n cadence: [0.85, 1.15],\n aggression: [0.8, 1.2],\n idle: [3, 2, 2, 1],\n}\nexport const PLAYER_PRESSURE_DEFAULTS: Required<CrowdPlayerPressureOptions> = { enabled: false, knockThreshold: 1500, crushDamagePerSecond: 6 }\n/** Pressure damage per N·s on a door pushed by the horde (§6.5). */\nexport const DOOR_PRESSURE_DAMAGE = 0.004\n/** Flow cells per step in deterministic mode. */\nexport const DETERMINISTIC_FLOW_CELLS = 20000\n\n/** Everything on, deterministic, the numbers of the design (`design-chain-horde.md`); the player pressure stays off, a game turns it on. */\nexport const CROWD_PHYSICS_DEFAULTS: CrowdPhysicsOptions = {\n contact: CONTACT_DEFAULTS,\n pressure: PRESSURE_DEFAULTS,\n balance: BALANCE_DEFAULTS,\n mound: MOUND_DEFAULTS,\n thrown: THROWN_DEFAULTS,\n density: DENSITY_DEFAULTS,\n blast: BLAST_DEFAULTS,\n personality: PERSONALITY_DEFAULTS,\n door: { pressureDamage: DOOR_PRESSURE_DAMAGE },\n playerPressure: PLAYER_PRESSURE_DEFAULTS,\n deterministic: true,\n}\n\nexport type ResolvedPhysics = {\n readonly contact: Required<CrowdContactOptions> | null\n readonly pressure: Required<CrowdPressureOptions> | null\n readonly balance: Required<CrowdBalanceOptions> | null\n readonly mound: Required<CrowdMoundOptions> | null\n readonly thrown: Required<CrowdThrownOptions> | null\n readonly density: Required<CrowdDensityOptions> | null\n readonly blast: Required<CrowdBlastOptions>\n readonly personality: Required<CrowdPersonalityOptions> | null\n readonly doorPressureDamage: number\n readonly playerPressure: Required<CrowdPlayerPressureOptions>\n readonly deterministic: boolean\n readonly worker: 'off' | 'auto' | 'on'\n readonly workerFactory: PhysicsWorkerFactory | undefined\n readonly workerTransport: OffloadTransport | undefined\n readonly workerLatency: 1 | 2\n}\n\nconst part = <T extends object>(option: T | boolean | undefined, defaults: Required<T>): Required<T> | null => {\n if (option === undefined || option === false) return null\n return option === true ? defaults : { ...defaults, ...option }\n}\n\n/** Reads `CrowdOptions.physics`: the defaults filled in, and a thrown error that lists every part whose dependency is missing. */\nexport function resolvePhysics(physics: CrowdPhysicsOptions = {}): ResolvedPhysics {\n const contact = part(physics.contact, CONTACT_DEFAULTS)\n const pressure = part(physics.pressure, PRESSURE_DEFAULTS)\n const balance = part(physics.balance, BALANCE_DEFAULTS)\n const mound = part(physics.mound, MOUND_DEFAULTS)\n const thrown = part(physics.thrown, THROWN_DEFAULTS)\n const density = part(physics.density, DENSITY_DEFAULTS)\n const playerPressure = { ...PLAYER_PRESSURE_DEFAULTS, ...physics.playerPressure }\n const missing: string[] = []\n if (!contact && pressure) missing.push('pressure needs contact')\n if (!contact && balance) missing.push('balance needs contact')\n if (!balance && mound) missing.push('mound needs balance')\n if (!balance && thrown) missing.push('thrown needs balance')\n if (!balance && density) missing.push('density needs balance')\n if (!pressure && playerPressure.enabled) missing.push('playerPressure needs pressure')\n if (!contact && physics.worker === 'on') missing.push('worker needs contact')\n if (missing.length) throw new Error(`crowd physics: ${missing.join('; ')}`)\n return {\n contact,\n pressure,\n balance,\n mound,\n thrown,\n density,\n blast: { ...BLAST_DEFAULTS, ...physics.blast },\n personality: part(physics.personality, PERSONALITY_DEFAULTS),\n doorPressureDamage: physics.door?.pressureDamage ?? DOOR_PRESSURE_DAMAGE,\n playerPressure,\n deterministic: physics.deterministic ?? false,\n worker: physics.worker ?? 'off',\n workerFactory: physics.workerFactory,\n workerTransport: physics.workerTransport,\n workerLatency: physics.workerLatency ?? 1,\n }\n}\n","const FNV_OFFSET = 0x811c9dc5\nconst FNV_PRIME = 0x01000193\nconst MIX = 0x9e3779b1\n\n/** Deterministic mulberry32 generator; returns floats in [0, 1). */\nexport function createRng(seed: number): () => number {\n let state = seed >>> 0\n return () => {\n state = (state + 0x6d2b79f5) >>> 0\n let mixed = Math.imul(state ^ (state >>> 15), state | 1)\n mixed ^= mixed + Math.imul(mixed ^ (mixed >>> 7), mixed | 61)\n return ((mixed ^ (mixed >>> 14)) >>> 0) / 4294967296\n }\n}\n\n/** A seed for the stream called `name`: the same seed gives each name its own sequence, so lightning never shifts the schedule. */\nexport function streamSeed(seed: number, name: string): number {\n let hash = FNV_OFFSET\n for (let index = 0; index < name.length; index++) hash = Math.imul(hash ^ name.charCodeAt(index), FNV_PRIME) >>> 0\n return Math.imul((seed >>> 0) ^ hash, MIX) >>> 0\n}\n\nexport function createStream(seed: number, name: string): () => number {\n return createRng(streamSeed(seed, name))\n}\n\nexport function drawBetween(rng: () => number, range: readonly [min: number, max: number]): number {\n return range[0] + rng() * (range[1] - range[0])\n}\n","/**\n * The agents as a struct of arrays (G 66717 `Z`, ZMAX 6000): one typed array per field, the same types as the game so the floats round the same way.\n * The names are the game's in English; `armHp` and `legHp` hold two values per agent (left, right).\n */\nexport const createStore = (capacity: number) => {\n const f32 = () => new Float32Array(capacity)\n const u8 = () => new Uint8Array(capacity)\n return {\n n: 0,\n capacity,\n x: f32(),\n z: f32(),\n vx: f32(),\n vz: f32(),\n yaw: f32(),\n phase: f32(),\n type: u8(),\n age: u8(),\n outfit: u8(),\n height: f32(),\n build: f32(),\n chaseSpeed: f32(),\n wanderSpeed: f32(),\n hp: f32(),\n hpMax: f32(),\n headHp: f32(),\n armHp: new Float32Array(capacity * 2),\n legHp: new Float32Array(capacity * 2),\n missing: new Uint16Array(capacity),\n helmet: u8(),\n state: u8(),\n stateTimer: f32(),\n lost: f32(),\n attackTimer: f32(),\n band: u8(),\n next: f32(),\n wx: f32(),\n wz: f32(),\n crawl: u8(),\n limp: u8(),\n color: new Uint32Array(capacity),\n skin: f32(),\n alertIn: f32(),\n cooldown: f32(),\n y: f32(),\n floor: u8(),\n floorLocked: u8(),\n vy: f32(),\n abilityTimer: f32(),\n eaten: u8(),\n stuck: f32(),\n stepCell: new Int32Array(capacity),\n burn: f32(),\n hunts: new Int8Array(capacity),\n named: new Int8Array(capacity),\n dvx: f32(),\n dvz: f32(),\n steering: u8(),\n renderYaw: f32(),\n renderSpeed: f32(),\n pause: f32(),\n lod: u8(),\n variant: u8(),\n attackPose: u8(),\n arms: f32(),\n stepDir: new Int32Array(capacity),\n // physics (CrowdOptions.physics): they exist with it off, so the store keeps its shape; the game replays never read them\n pushX: f32(),\n pushZ: f32(),\n mass: f32(),\n pressure: f32(),\n field: f32(),\n fieldPrev: f32(),\n aimX: f32(),\n aimZ: f32(),\n directShove: f32(),\n blastStep: new Int32Array(capacity),\n balance: u8(),\n balanceTimer: f32(),\n fallCause: u8(),\n identity: new Uint32Array(capacity),\n stability: f32(),\n tripFactor: f32(),\n driveFactor: f32(),\n reaction: f32(),\n cadenceFactor: f32(),\n aggression: f32(),\n idle: u8(),\n gaitStyle: u8(),\n wounds: new Uint16Array(capacity),\n leanX: f32(),\n leanZ: f32(),\n thrown: new Int8Array(capacity),\n prevX: f32(),\n prevZ: f32(),\n prevY: f32(),\n }\n}\nexport type CrowdStore = ReturnType<typeof createStore>\ntype Arrays = Omit<CrowdStore, 'n' | 'capacity' | 'armHp' | 'legHp'>\n/** The one-per-agent fields (G 66733 `FIELDS`): what a removal copies from the last agent. */\nexport const AGENT_FIELDS = [\n 'x', 'z', 'vx', 'vz', 'yaw', 'phase', 'type', 'age', 'outfit', 'height', 'build', 'chaseSpeed', 'wanderSpeed', 'hp', 'hpMax', 'headHp', 'missing', 'helmet', 'state', 'stateTimer', 'lost', 'attackTimer',\n 'band', 'next', 'wx', 'wz', 'crawl', 'limp', 'color', 'skin', 'alertIn', 'cooldown', 'y', 'floor', 'floorLocked', 'vy', 'abilityTimer', 'eaten', 'stuck', 'stepCell', 'burn', 'hunts', 'named', 'dvx', 'dvz',\n 'steering', 'renderYaw', 'renderSpeed', 'pause', 'lod', 'variant', 'attackPose', 'arms', 'stepDir',\n 'pushX', 'pushZ', 'mass', 'pressure', 'field', 'fieldPrev', 'aimX', 'aimZ', 'directShove', 'blastStep', 'balance', 'balanceTimer', 'fallCause', 'identity', 'stability', 'tripFactor', 'driveFactor', 'reaction',\n 'cadenceFactor', 'aggression', 'idle', 'gaitStyle', 'wounds', 'leanX', 'leanZ', 'thrown', 'prevX', 'prevZ', 'prevY',\n] as const satisfies readonly (keyof Arrays)[]\n","import { createCorpseLedger } from '@adatechnology/game-characters'\nimport type { CorpseLedger } from '@adatechnology/game-characters'\n\nimport defaults from './data/defaults.json'\nimport { createDoors } from './doors'\nimport type { Doors } from './doors'\nimport { createNavGrid } from './grid'\nimport type { NavGrid } from './grid'\nimport { unit, SALT } from './identity'\nimport { createPhysics } from './physics/engine'\nimport { COLLAPSE_MARK, collapsePileColumns } from './physics/pile-column'\nimport type { PileColumn } from './physics/pile-column'\nimport { seatPhysics } from './physics/seat'\nimport { DETERMINISTIC_FLOW_CELLS, resolvePhysics } from './physics/options'\nimport type { CrowdPhysicsOptions } from './physics/options'\nimport { createPhysicsStats } from './physics/context'\nimport type { PhysicsStats } from './physics/context'\nimport type { ThrownPool } from './physics/thrown'\nimport { createRng } from './rng'\nimport { AGENT_FIELDS, createStore } from './store'\nimport type { CrowdStore } from './store'\nimport { BALANCE, STATE } from './types'\nimport type { Corpse, CrowdBlast, CrowdDamageResult, CrowdEventMap, CrowdEventName, CrowdHit, CrowdPack, CrowdTraits, CrowdTarget, CrowdType } from './types'\n\nexport type CrowdNumbers = typeof defaults.numbers\nexport type CrowdBand = { readonly max: number; readonly dt: number }\n/** The game's numbers; `bands[3].max` is `Infinity` (JSON keeps it as null). */\nexport const CROWD_DEFAULTS: { readonly sources: Readonly<Record<string, string>>; readonly bands: readonly CrowdBand[]; readonly numbers: CrowdNumbers } = {\n sources: defaults.sources,\n bands: defaults.bands.map((band) => ({ max: band.max ?? Infinity, dt: band.dt })),\n numbers: defaults.numbers,\n}\n\nexport type { Corpse } from './types'\n/** A custom move: runs on every decision tick after the timed states and before the moves on the target; `true` means it took care of the movement. */\nexport type CrowdPlugin = {\n readonly id: string\n ability?(crowd: Crowd, i: number, context: { d: number; dx: number; dz: number; tdt: number; band: number; alive: boolean }): boolean\n}\n\n/** A migration: `count` agents every `every` s (the first after `numbers.migrationFirst`), born in the box at (x, z) and walking to `goal`. */\nexport type MigrationOptions = { readonly every: number; readonly count: number; readonly x: number; readonly z: number; readonly width: number; readonly depth: number; readonly goal: readonly [number, number] }\nexport type CrowdOptions = {\n readonly pack: CrowdPack\n readonly random?: () => number\n readonly seed?: number\n readonly capacity?: number\n readonly grid?: NavGrid\n readonly doors?: Doors\n /** Water at (x, z): the drowned swim, the charred lose health (the game's `POOL`). */\n readonly water?: (x: number, z: number) => boolean\n readonly numbers?: Partial<CrowdNumbers>\n readonly bands?: readonly CrowdBand[]\n /** Flow field: half window in cells (192), and the clock its time budget reads (default `performance.now`; pass `() => 0` to finish every field in one frame). */\n readonly flow?: { readonly radius?: number; readonly clock?: () => number; /** Flow budget in cells per step instead of the clock: the same field at any frame rate (`physics.deterministic` sets 20000). */ readonly cellsPerStep?: number }\n readonly plugins?: readonly CrowdPlugin[]\n readonly target?: CrowdTarget | null\n /** Contact, pressure, balance, mounds, thrown bodies and personality: all off by default, and then the horde is the game's, bit for bit. */\n readonly physics?: CrowdPhysicsOptions\n}\nexport type CrowdView = { readonly x: number; readonly z: number; readonly yaw: number; readonly far: number }\n\nexport type CrowdUpdateContext = {\n readonly target?: CrowdTarget | null\n /** 0 day, 1 night: sight drops by 40% at night. */\n readonly nightFactor?: number\n /** Where the flow field converges (the target by default; the foot of a ladder when the target is on a roof). */\n readonly flowTarget?: readonly [number, number]\n /** Where the viewer looks (yaw: forward is `(-sin, -cos)`); the stalker wakes when the target turns its back. Default: the target's view. */\n readonly viewYaw?: number\n /** Centre of the relevance bands, for scenes without a player near the agents (tests, benches, cutscenes). Default: the target, or `flowTarget` without one. */\n readonly bandCentre?: readonly [number, number]\n}\n\nexport type Crowd = {\n readonly store: CrowdStore\n readonly pack: CrowdPack\n readonly grid: NavGrid\n readonly doors: Doors\n readonly corpses: CorpseLedger<Corpse>\n readonly numbers: CrowdNumbers\n readonly stats: { bands: [number, number, number, number]; slots: number; kills: number; heads: number; physics: PhysicsStats }\n /** Height of the bodies piled per grid cell. */\n readonly pile: Float32Array\n /** Millimetres of lying bodies per navigation cell, recomputed every fixed step (empty with `physics.mound` off). */\n readonly mound: Uint16Array\n /** The thrown bodies of a blast, simulated here and only drawn by the renderer (empty with `physics.thrown` off). */\n readonly thrown: ThrownPool\n /** A blast: queued, and resolved at the start of the next step in one pass over the agents. Without `physics` it only hurts, kills and stuns. */\n blast(blast: CrowdBlast): void\n /** How many agents are steps of a pile now. */\n pileCount(): number\n readonly trail: { x: number; z: number; t: number }[]\n /** Global variant index of each type's variants (G 67079 `ZVAR_OF`). */\n readonly variantsOf: readonly (readonly number[])[]\n target: CrowdTarget | null\n /** The stream every draw takes (the behaviours share it, in the game's order). */\n readonly random: () => number\n /** Emits a `sound` event, as the crowd's own (for the behaviours). */\n sound(id: string, volume: number, x: number, y: number, z: number, distance: number): void\n /** Emits an `effect` event, as the crowd's own (for the behaviours). */\n effect(payload: CrowdEventMap['effect']): void\n typeIndex(id: string): number\n spawn(x: number, z: number, type?: number): number\n remove(i: number): void\n update(dt: number, context?: CrowdUpdateContext): void\n dispose(): void\n emitNoise(x: number, z: number, radius: number): void\n damage(i: number, hit: CrowdHit): CrowdDamageResult\n kill(i: number, headshot?: boolean): void\n /** The blood trail of the target, for the trackers (G 67238 `trailUpdate`). */\n updateTrail(dt: number, target?: CrowdTarget | null): void\n updateCorpses(dt: number): void\n /** Keeps the count near `target` from the edge, removes the excess out of sight and sends a migration now and then (G 67158). */\n updatePopulation(dt: number, view: CrowdView, options?: { readonly target?: number; readonly migration?: MigrationOptions | null }): void\n /** Spawns one agent `minD` to `maxD` m from the target, out of the view (G 67141 `spawnAway`). */\n spawnAway(minD: number, maxD: number, view: CrowdView): number\n /** A blast at the base of a pile undoes it (G 67227 `pileBlast`). */\n pileBlast(x: number, z: number, radius: number): void\n /** The agent hunts something else than the target (a civilian): `game-crowd` civilians set it. */\n setHunt(hunt: ((i: number, tdt: number) => boolean) | null): void\n /** The chase speed of agent `i` now (the stalker's sprint, the drowned in water; G 67343 `zChaseSpeed`). */\n chaseSpeed(i: number): number\n /** The interface `game-player` uses to push, cut and be held by the agents. */\n asPlayerCrowd(): PlayerCrowdLike\n on<K extends CrowdEventName>(event: K, listener: (event: CrowdEventMap[K]) => void): () => void\n}\n\n/** The same shape as `game-player`'s `PlayerCrowd` (structural, no dependency). */\nexport type PlayerCrowdLike = {\n count(): number\n active(i: number): boolean\n x(i: number): number\n z(i: number): number\n y(i: number): number\n floor(i: number): number\n chasing(i: number): boolean\n down(i: number): boolean\n panicWeight(i: number): number\n hold(i: number, x: number, z: number, k: number, lookX: number, lookZ: number): void\n stun(i: number, seconds: number, vx: number, vz: number): void\n chase(i: number, cooldown: number): void\n damage(i: number, hit: { region: number; amount: number; distance: number; cut: number }): void\n}\n\nconst clamp = (v: number, a: number, b: number): number => (v < a ? a : v > b ? b : v)\n// one shared empty set: the update loop reads the traits of every agent every frame\nconst NO_TRAITS: CrowdTraits = {}\nconst TAU_APPROX = 6.2832\nconst TURN = 6.28\n\n/**\n * The crowd: a literal port of the team game's horde (`jogo.js` G 66714-67430). One struct of arrays, relevance bands R0-R3 with hysteresis, perception\n * by sight, hearing and smell, the sliced flow field, the spatial hash, separation, six attack slots, chain alerts, doors, piles against walls, the\n * abilities as type traits, corpses, the blood trail and the population. Every draw on `random` is in the game's order.\n */\nexport function createCrowd(options: CrowdOptions): Crowd {\n const random = options.random ?? createRng(options.seed ?? 1)\n const R = random\n const pack = options.pack\n const types: readonly CrowdType[] = pack.types\n const N: CrowdNumbers = { ...CROWD_DEFAULTS.numbers, ...options.numbers }\n const BANDS = options.bands ?? CROWD_DEFAULTS.bands\n const HYST = N.hysteresis\n const grid = options.grid ?? createNavGrid()\n const doors = options.doors ?? createDoors(grid, { random })\n const water = options.water ?? (() => false)\n const plugins = options.plugins ?? []\n const clock = options.flow?.clock ?? (() => (typeof performance === 'undefined' ? Date.now() : performance.now()))\n const FLOW_R = options.flow?.radius ?? N.flowRadius\n const Z = createStore(options.capacity ?? N.capacity)\n const { occ, occH, cellOf, solidAt } = grid\n const HALF = grid.half\n const GC = grid.cell\n const FW = grid.n\n const physics = resolvePhysics(options.physics)\n const flowCells = options.flow?.cellsPerStep ?? (physics.deterministic ? DETERMINISTIC_FLOW_CELLS : undefined)\n const seed32 = (options.seed ?? 1) >>> 0\n let serial = 0\n const moundHeight = new Uint16Array(physics.mound ? FW * FW : 0)\n const moundOn = physics.mound !== null\n const balanceOn = physics.balance !== null\n const staggerSpeed = physics.pressure?.staggerSpeed ?? 1\n const separationMax = physics.contact?.separationMax ?? Infinity\n const RESERVED_SLOTS = 2\n const WALK_DOWN = 0.6\n // the attack pose by distance with hysteresis (in at 1.6 m, out at 2.2 m), squared: the renderer drew it, the simulation reads it\n const ATTACK_ENTER = 2.56\n const ATTACK_LEAVE = 4.84\n const HC = N.hashCell\n const HN = Math.floor(grid.size / HC)\n const hHead = new Int32Array(HN * HN).fill(-1)\n const hNext = new Int32Array(Z.capacity)\n const hashTouched = new Int32Array(Z.capacity)\n let hashTouchedN = 0\n const pile = new Float32Array(FW * FW)\n let pileN = 0\n const flow = {\n dir: new Int8Array(FW * FW * 2),\n back: new Int8Array(FW * FW * 2),\n dist: new Float32Array(FW * FW),\n q: new Int32Array((2 * FLOW_R + 1) ** 2 * 2),\n phase: 0,\n qh: 0,\n qt: 0,\n c: 0,\n t: 0,\n cell: -1,\n ms: 0,\n win: [0, 0, -1, -1],\n winB: [0, 0, -1, -1],\n }\n const catTable: number[] = []\n types.forEach((c, i) => {\n for (let k = 0; k < c.weight; k++) catTable.push(i)\n })\n const variantsOf: number[][] = []\n {\n let next = 0\n types.forEach((type) => {\n const list: number[] = []\n for (let v = 0; v < (type.variants ?? 1); v++) list.push(next++)\n variantsOf.push(list)\n })\n }\n const typeIndexById = new Map(types.map((type, index) => [type.id, index]))\n const noises: { x: number; z: number; r: number }[] = []\n const stats: Crowd['stats'] = { bands: [0, 0, 0, 0], slots: 0, kills: 0, heads: 0, physics: createPhysicsStats() }\n const corpses = createCorpseLedger<Corpse>({ max: N.corpsesMax, seconds: N.corpseSeconds })\n const trail: { x: number; z: number; t: number }[] = []\n let trailLast = { x: 1e9, z: 1e9 }\n const killQ: number[] = []\n let groanBudget = 0\n let migrateT = N.migrationFirst\n let hunt: ((i: number, tdt: number) => boolean) | null = null\n let target: CrowdTarget | null = options.target ?? null\n let nightF = 0\n let viewYaw = 0\n const listeners = new Map<CrowdEventName, Set<(event: never) => void>>()\n const emit = <K extends CrowdEventName>(event: K, payload: CrowdEventMap[K]): void => {\n const set = listeners.get(event)\n if (set) for (const listener of set) (listener as (event: CrowdEventMap[K]) => void)(payload)\n }\n const emitNoise = (x: number, z: number, r: number): void => void noises.push({ x, z, r })\n doors.on('noise', (e) => emitNoise(e.x, e.z, e.radius))\n doors.on('sound', (e) => emit('sound', { ...e, distance: target ? Math.hypot(e.position[0] - target.state.x, e.position[2] - target.state.z) : 0 }))\n doors.on('effect', (e) => emit('effect', e))\n doors.on('broken', (e) => {\n const d = doors.list[e.door]!\n if (target && Math.hypot(d.x - target.state.x, d.z - target.state.z) < N.doorMessageDistance) emit('message', { id: 'door_broken', seconds: 2 })\n })\n const sound = (id: string, volume: number, x: number, y: number, z: number, d: number): void => emit('sound', { sound: id, volume, position: [x, y, z], distance: d })\n const pick = <T extends { p: number }>(list: readonly T[]): T => {\n const r = R()\n let acc = 0\n for (const it of list) {\n acc += it.p\n if (r < acc) return it\n }\n return list[0]!\n }\n const T = (i: number): CrowdType => types[Z.type[i]!]!\n const traits = (i: number): CrowdTraits => T(i).traits ?? NO_TRAITS\n\n // ----- spawn and removal -----\n const special = (i: number): void => {\n const tr = traits(i)\n Z.y[i] = 0\n Z.floor[i] = 0\n Z.floorLocked[i] = 0\n Z.vy[i] = 0\n Z.abilityTimer[i] = 0\n Z.eaten[i] = 0\n Z.stuck[i] = 0\n Z.stepCell[i] = -1\n Z.burn[i] = 0\n Z.hunts[i] = -1\n Z.named[i] = -1\n if (tr.color !== undefined && !tr.submerged) {\n Z.color[i] = tr.color\n if (tr.skin !== undefined) Z.skin[i] = tr.skin\n }\n if (tr.ambush && R() < tr.ambush.chance) {\n Z.state[i] = STATE.DORMANT\n Z.yaw[i] = R() * TURN\n }\n if (tr.submerged) {\n Z.state[i] = STATE.DORMANT\n Z.y[i] = tr.submerged.depth\n if (tr.color !== undefined) Z.color[i] = tr.color\n }\n }\n\n const spawn = (x: number, z: number, catForce = -1): number => {\n if (Z.n >= Z.capacity) return -1\n const i = Z.n++\n const c = catForce >= 0 ? catForce : catTable[Math.floor(R() * catTable.length)]!\n const C = types[c]!\n const ai = pack.ages.indexOf(pick(pack.ages))\n const maxAge = C.traits?.maxAge\n const A = pack.ages[maxAge !== undefined ? Math.min(ai, maxAge) : ai]!\n const ri = pack.outfits.indexOf(pick(pack.outfits))\n const RO = pack.outfits[ri]!\n Z.x[i] = x\n Z.z[i] = z\n Z.vx[i] = Z.vz[i] = 0\n Z.type[i] = c\n Z.age[i] = pack.ages.indexOf(A)\n Z.outfit[i] = ri\n const g = (R() + R() + R() - 1.5) * 0.09\n Z.height[i] = clamp(1 + g, 0.9, 1.1) * (C.scale || 1)\n const bb = R()\n Z.build[i] = (bb < 0.3 ? 0.85 + R() * 0.1 : bb < 0.8 ? 0.95 + R() * 0.13 : 1.08 + R() * 0.17) * (C.build || (C.scale ? 1.3 : 1))\n const heavy = bb >= 0.8\n const thin = bb < 0.3\n Z.chaseSpeed[i] = C.speed.chase * A.speed * (thin ? 1.05 : heavy ? 0.9 : 1) * (0.92 + R() * 0.16)\n Z.wanderSpeed[i] = C.speed.wander * A.speed * (0.8 + R() * 0.4)\n Z.hpMax[i] = Z.hp[i] = C.health * A.health * (thin ? 0.9 : heavy ? 1.15 : 1)\n Z.headHp[i] = pack.segments.head\n Z.armHp[i * 2] = Z.armHp[i * 2 + 1] = pack.segments.arm\n Z.legHp[i * 2] = Z.legHp[i * 2 + 1] = pack.segments.leg\n Z.missing[i] = 0\n Z.helmet[i] = RO.helmet ? 1 : 0\n Z.crawl[i] = C.crawls ? 1 : 0\n Z.limp[i] = 0\n Z.state[i] = 0\n Z.stateTimer[i] = 0\n Z.lost[i] = 0\n Z.attackTimer[i] = R()\n Z.alertIn[i] = 0\n Z.cooldown[i] = 0\n {\n const vs = variantsOf[c]!\n Z.variant[i] = vs[Math.floor(R() * vs.length)]!\n }\n Z.yaw[i] = Z.renderYaw[i] = R() * TURN\n Z.renderSpeed[i] = 0\n Z.steering[i] = 0\n Z.pause[i] = 0\n Z.attackPose[i] = 0\n Z.arms[i] = 0\n Z.phase[i] = R() * 10\n Z.band[i] = 3\n Z.next[i] = R() * 0.05\n Z.wx[i] = x\n Z.wz[i] = z\n Z.color[i] = RO.color || pack.palette[Math.floor(R() * pack.palette.length)]!\n Z.skin[i] = R()\n seatPhysics(Z, i, { seed: seed32, serial: serial++, baseMass: physics.contact?.baseMass ?? 70, personality: physics.personality })\n special(i)\n emit('spawn', { agent: i, type: C.id })\n return i\n }\n\n const remove = (i: number): void => {\n if (Z.state[i] === STATE.PILE_STEP) {\n if (engine.active) releasePileStep(i)\n else stepRelease(i)\n }\n const j = --Z.n\n engine.remap(i, j)\n if (i === j) {\n emit('remove', { agent: i, moved: -1 })\n return\n }\n for (const k of AGENT_FIELDS) (Z[k] as { [index: number]: number })[i] = (Z[k] as { [index: number]: number })[j]!\n Z.armHp[i * 2] = Z.armHp[j * 2]!\n Z.armHp[i * 2 + 1] = Z.armHp[j * 2 + 1]!\n Z.legHp[i * 2] = Z.legHp[j * 2]!\n Z.legHp[i * 2 + 1] = Z.legHp[j * 2 + 1]!\n if (target) {\n if (target.state.grabbedBy === j) target.state.grabbedBy = i\n else if (target.state.grabbedBy === i) target.state.grabbedBy = -1\n }\n emit('remove', { agent: i, moved: j })\n }\n\n // ----- the sliced flow field (no spike) -----\n const flowStart = (x: number, z: number): void => {\n const gx = clamp(Math.floor((x + HALF) / GC), 0, FW - 1)\n const gz = clamp(Math.floor((z + HALF) / GC), 0, FW - 1)\n const W = flow.winB\n W[0] = Math.max(0, gx - FLOW_R)\n W[1] = Math.max(0, gz - FLOW_R)\n W[2] = Math.min(FW - 1, gx + FLOW_R)\n W[3] = Math.min(FW - 1, gz + FLOW_R)\n for (let r = W[1]; r <= W[3]; r++) flow.dist.fill(1e9, r * FW + W[0], r * FW + W[2] + 1)\n const s = gz * FW + gx\n flow.dist[s] = 0\n flow.q[0] = s\n flow.qh = 0\n flow.qt = 1\n flow.phase = 1\n flow.c = 0\n flow.t = 0\n }\n const flowStep = (budget: number, cellsPerStep?: number): void => {\n if (!flow.phase) return\n const t0 = clock()\n let cellsLeft = cellsPerStep ?? Infinity\n const D = flow.dist\n const Q = flow.q\n const F = flow.back\n const W = flow.winB\n let n = 0\n while (flow.phase === 1) {\n if (flow.qh >= flow.qt) {\n flow.phase = 2\n break\n }\n if (--cellsLeft < 0) return\n const c = Q[flow.qh++]!\n const cx = c % FW\n const cz = (c / FW) | 0\n const dc = D[c]!\n for (let dz = -1; dz <= 1; dz++)\n for (let dx = -1; dx <= 1; dx++) {\n if (!dx && !dz) continue\n const nx = cx + dx\n const nz = cz + dz\n if (nx < W[0]! || nz < W[1]! || nx > W[2]! || nz > W[3]!) continue\n const k = nz * FW + nx\n if (occ[k] && occ[k]! < 3) continue\n if (dx && dz && ((occ[cz * FW + nx] && occ[cz * FW + nx]! < 3) || (occ[nz * FW + cx] && occ[nz * FW + cx]! < 3))) continue\n const nd = dc + (dx && dz ? 1.414 : 1)\n if (nd < D[k]! - 1e-3) {\n D[k] = nd\n if (flow.qt < Q.length) Q[flow.qt++] = k\n }\n }\n if (cellsPerStep === undefined && (++n & 2047) === 0 && clock() - t0 > budget) {\n flow.t += clock() - t0\n return\n }\n }\n const ww = W[2]! - W[0]! + 1\n while (flow.phase === 2) {\n if (--cellsLeft < 0) return\n const j = flow.c++\n if (j >= ww * (W[3]! - W[1]! + 1)) {\n const tmp = flow.dir\n flow.dir = F\n flow.back = tmp\n const tw = flow.win\n flow.win = W\n flow.winB = tw\n flow.phase = 0\n flow.ms = flow.t + clock() - t0\n return\n }\n const cx = W[0]! + (j % ww)\n const cz = W[1]! + ((j / ww) | 0)\n const c = cz * FW + cx\n if (occ[c] && occ[c]! < 3) {\n F[c * 2] = F[c * 2 + 1] = 0\n continue\n }\n let best = D[c]!\n let bx = 0\n let bz = 0\n for (let dz = -1; dz <= 1; dz++)\n for (let dx = -1; dx <= 1; dx++) {\n const nx = cx + dx\n const nz = cz + dz\n if (nx < W[0]! || nz < W[1]! || nx > W[2]! || nz > W[3]!) continue\n const d = D[nz * FW + nx]!\n if (d < best) {\n best = d\n bx = dx\n bz = dz\n }\n }\n F[c * 2] = bx\n F[c * 2 + 1] = bz\n if (cellsPerStep === undefined && (j & 8191) === 0 && clock() - t0 > budget) {\n flow.t += clock() - t0\n return\n }\n }\n }\n const losClear = grid.losClear\n\n // ----- piles against walls -----\n const groundAt = (c: number, _y: number): number => {\n if (c < 0) return 0\n const k = occ[c]\n if (k === 2 || (k === 1 && occH[c]! <= _y + 0.05)) return occH[c]!\n return moundOn ? pile[c]! + moundHeight[c]! / 1000 : pile[c]!\n }\n const blockedAt = (i: number, x: number, z: number): boolean => {\n const c = cellOf(x, z)\n if (c < 0) return true\n const k = occ[c]\n const y = Z.y[i]!\n if (k === 0 || k === 3) return (moundOn ? pile[c]! + moundHeight[c]! / 1000 : pile[c]!) > y + 0.5 && Z.state[i] !== STATE.AIRBORNE\n if (k === 2 && y + 0.5 >= occH[c]!) return false\n if (Z.state[i] === STATE.AIRBORNE && occH[c]! <= y) return false\n return true\n }\n const tryStep = (i: number, hitCell: number): boolean => {\n if (pileN >= N.pileMax) return false\n const c = cellOf(Z.x[i]!, Z.z[i]!)\n if (c < 0 || occ[c] !== 0) return false\n const dir = hitCell - c\n const w = c + dir\n const back = c - dir\n const h = pile[c]! + N.pileStep\n if (h > N.pileTop) return false\n const cap = occ[w] === 2 ? (back >= 0 ? pile[back]! : 0) + 1.0 : pile[w]!\n if (h > cap + 1e-3) return false\n pile[c] = h\n pileN++\n Z.state[i] = STATE.PILE_STEP\n Z.stepCell[i] = c\n Z.stepDir[i] = dir\n Z.vx[i] = Z.vz[i] = 0\n Z.y[i] = h - N.pileStep\n Z.stateTimer[i] = 6 + R() * 6\n return true\n }\n function stepRelease(i: number): void {\n const c = Z.stepCell[i]!\n if (c >= 0) {\n pile[c] = Math.max(0, pile[c]! - N.pileStep)\n pileN = Math.max(0, pileN - 1)\n }\n Z.stepCell[i] = -1\n Z.state[i] = STATE.CHASE\n Z.stuck[i] = 0\n }\n const pileBlast = (x: number, z: number, r: number): void => {\n for (let i = 0; i < Z.n; i++)\n if (Z.state[i] === STATE.PILE_STEP && (Z.x[i]! - x) ** 2 + (Z.z[i]! - z) ** 2 < r * r) {\n stepRelease(i)\n Z.state[i] = STATE.STUNNED\n Z.stateTimer[i] = 1.2\n }\n }\n\n // a pile step that falls, flies or dies takes the steps behind it in its column down with it (the pile is not a wall that stays up without them)\n const pileColumns: PileColumn[] = []\n const releasePileStep = (i: number): void => {\n if (Z.stepCell[i]! >= 0) pileColumns.push({ cell: Z.stepCell[i]!, dir: Z.stepDir[i]! })\n stepRelease(i)\n }\n // ----- alerts -----\n const voice = (i: number): string => traits(i).voice ?? 'groan'\n const groan = (i: number, d: number): void => {\n // chain alert: 8 m, 0.2-0.6 s per hop\n const r2 = N.alertRadius * N.alertRadius\n for (let j = 0; j < Z.n; j++) {\n if (Z.state[j] !== 0 || Z.alertIn[j]! > 0) continue\n const ex = Z.x[j]! - Z.x[i]!\n const ez = Z.z[j]! - Z.z[i]!\n if (ex * ex + ez * ez < r2) Z.alertIn[j] = N.alertDelay + R() * N.alertDelayRandom\n }\n emit('alert', { agent: i })\n if (d < N.voiceDistance && groanBudget <= 0) {\n sound(voice(i), 1, Z.x[i]!, Z.height[i]! * 1.6, Z.z[i]!, d)\n groanBudget = N.voiceEvery\n }\n }\n const screamAt = (i: number, x: number, z: number): void => {\n const tr = traits(i).caller!\n sound('scream_hoarse', 1, x, 1.6, z, target ? Math.hypot(x - target.state.x, z - target.state.z) : 0)\n for (let j = 0; j < Z.n; j++) {\n const ex = Z.x[j]! - x\n const ez = Z.z[j]! - z\n if (ex * ex + ez * ez < tr.radius * tr.radius) {\n Z.state[j] = STATE.CHASE\n Z.lost[j] = 0\n }\n }\n if (target) target.state.screamBoost = tr.boost\n emit('message', { id: 'called', seconds: 3, type: T(i).id })\n emit('scream', { agent: i, x, z })\n }\n\n // ----- attack on the target -----\n const attack = (i: number, d: number, tdt: number): void => {\n Z.attackTimer[i]! -= tdt\n if (Z.attackTimer[i]! > 0) return\n Z.attackTimer[i] = N.attackEvery / Z.aggression[i]!\n const C = T(i)\n const tr = C.traits ?? NO_TRAITS\n if (tr.heavySwing && R() < tr.heavySwing.chance) {\n Z.state[i] = STATE.SWING\n Z.stateTimer[i] = tr.heavySwing.windUp\n return\n }\n const noJaw = Z.missing[i]! & 512\n const noArms = (Z.missing[i]! & 6) === 6\n const P = target!\n if (P.state.grabbedBy < 0 && !noArms && !Z.crawl[i] && R() < C.bite * (P.state.crouch ? N.crouchBite : 1) * Z.aggression[i]!) {\n P.grab(i, !!noJaw, !!tr.drownGrab && water(P.state.x, P.state.z))\n Z.state[i] = STATE.GRAB\n } else {\n P.hurt(C.damage * (Z.crawl[i] ? N.crawlDamage : 1), i)\n if (tr.burns && Z.abilityTimer[i]! >= 0) P.state.burnTimer = tr.burns.seconds\n }\n void d\n }\n\n // ----- damage (G 67010 zDamage) -----\n const effect = (payload: CrowdEventMap['effect']): void => emit('effect', payload)\n const blood = (x: number, y: number, z: number, age: number, amount: number, died: boolean, agent?: number): void => effect({ effect: 'blood', point: [x, y, z], age, amount, died, ...(agent === undefined ? {} : { agent }) })\n /** `draw` is the source of the jaw draw (and of the corpse seed of a death); a death waits for the end of the step when `deferDeath` is on. */\n const damageWith = (i: number, hit: CrowdHit, draw: () => number, deferDeath = false): CrowdDamageResult => {\n const C = T(i)\n const A = pack.ages[Z.age[i]!]!\n const RO = pack.outfits[Z.outfit[i]!]!\n const cal = hit.caliber ?? null\n const proj = !!cal\n const dano = hit.amount\n const dist = hit.distance\n const corte = hit.cut ?? 0\n const regiao = hit.region\n let morreu = false\n let headshot = false\n if (regiao === 0) {\n if (proj && Z.helmet[i] && cal!.class === 'pistol') {\n Z.helmet[i] = 0\n effect({ effect: 'spark', point: [Z.x[i]!, Z.height[i]! * 1.75, Z.z[i]!], agent: i })\n sound('impact', 1, Z.x[i]!, Z.height[i]! * 1.75, Z.z[i]!, dist)\n return { died: false }\n }\n const mata = proj ? (cal!.skull === 1 || dist <= cal!.skull) && dano >= N.skullMinDamage : dano * 3 >= Z.headHp[i]!\n if (mata) {\n morreu = true\n headshot = true\n if (dano > N.skullTakeDamage || (cal && N.skullTakeWithin[cal.id as keyof typeof N.skullTakeWithin] !== undefined && dist < N.skullTakeWithin[cal.id as keyof typeof N.skullTakeWithin]!)) Z.missing[i]! |= 1\n } else {\n Z.hp[i]! -= dano * 3\n Z.headHp[i]! -= dano\n if (dano > N.jawDamage && draw() < N.jawChance) Z.missing[i]! |= 512\n }\n } else if (regiao === 1) {\n let m = C.multipliers.torso\n if (RO.vest && proj) m *= cal!.class === 'pistol' ? RO.vest.pistol : RO.vest.rifle\n Z.hp[i]! -= dano * m\n } else {\n const perna = regiao >= 4\n const lado = regiao & 1\n const mm = (perna ? C.multipliers.legs : C.multipliers.arms) * A.limb * (RO.limb || 1)\n const segArr = perna ? Z.legHp : Z.armHp\n const k = i * 2 + (perna ? lado : regiao === 3 ? 1 : 0)\n segArr[k]! -= dano * mm\n Z.hp[i]! -= dano * mm * 0.5\n const max = perna ? pack.segments.leg : pack.segments.arm\n if (perna && segArr[k]! <= max * 0.5) Z.limp[i] = 1\n if (segArr[k]! <= 0) {\n const arranca = corte >= 0.5 || dano * mm > max * 0.9\n if (perna) {\n Z.crawl[i] = 1\n if (arranca) Z.missing[i]! |= lado ? 16 : 8\n else Z.missing[i]! |= lado ? 256 : 128\n } else if (arranca) Z.missing[i]! |= regiao === 3 ? 4 : 2\n else Z.missing[i]! |= regiao === 3 ? 64 : 32\n effect({ effect: 'limb', point: [Z.x[i]!, Z.height[i]! * (perna ? 0.5 : 1.2), Z.z[i]!], agent: i, region: regiao, severed: arranca })\n }\n }\n if (Z.hp[i]! <= 0) morreu = true\n blood(Z.x[i]!, regiao === 0 ? Z.height[i]! * 1.65 : regiao === 1 ? Z.height[i]! * 1.2 : Z.height[i]! * 0.7, Z.z[i]!, Z.age[i]!, regiao === 0 ? 120 : 30, morreu, i)\n if (morreu) {\n if (deferDeath) deferKill(i)\n else kill(i, headshot, draw)\n return { died: morreu, headshot }\n }\n const st = Z.state[i]\n if (dano >= N.hitStunDamage && st !== STATE.GRAB && st !== STATE.PILE_STEP && st !== STATE.AIRBORNE) {\n Z.state[i] = STATE.STUNNED\n Z.stateTimer[i] = C.traits?.hitStun ?? N.hitStun\n }\n const st2 = Z.state[i]\n if (st2 === 0 || st2 === STATE.DORMANT || st2 === STATE.EATING || st2 === STATE.SEEKING_SOUND) {\n Z.state[i] = STATE.CHASE\n Z.lost[i] = 0\n const sub = C.traits?.submerged\n if (sub) Z.y[i] = Math.max(Z.y[i]!, sub.surface)\n }\n return { died: morreu }\n }\n const damage = (i: number, hit: CrowdHit): CrowdDamageResult => damageWith(i, hit, R)\n function kill(i: number, headshot = false, draw: () => number = R): void {\n stats.kills++\n if (headshot) stats.heads++\n const tr = traits(i)\n if (tr.burst) burstAt(i, Z.x[i]!, Z.z[i]!)\n const C = T(i)\n emit('kill', { agent: i, type: C.id, headshot, x: Z.x[i]!, z: Z.z[i]! })\n if (Z.balance[i] !== BALANCE.THROWN) corpseAdd(i, draw)\n if (target && target.state.grabbedBy === i) target.release()\n remove(i)\n }\n const burstAt = (i: number, x: number, z: number): void => {\n const b = traits(i).burst!\n effect({ effect: 'cloud', point: [x, 0, z], radius: 4, color: b.cloudColor, amount: 8 })\n effect({ effect: 'blood', point: [x, 1.0, z], amount: 1500, died: true, color: b.bloodColor })\n if (target) {\n const d = Math.hypot(x - target.state.x, z - target.state.z)\n if (d < b.radius) {\n target.state.blind = b.blind\n target.state.scentTimer = b.scent\n emit('message', { id: 'burst_hit', seconds: 2.5, type: T(i).id })\n emitNoise(target.state.x, target.state.z, b.noiseNear)\n }\n }\n emitNoise(x, z, b.noise)\n }\n\n // ----- corpses -----\n const corpseOf = (i: number, seed: number): Corpse => ({\n x: Z.x[i]!,\n z: Z.z[i]!,\n yaw: Z.renderYaw[i]!,\n height: Z.height[i]!,\n build: Z.build[i]!,\n named: Z.named[i]!,\n missing: Z.missing[i]!,\n color: Z.color[i]!,\n variant: Z.variant[i]!,\n skin: Z.skin[i]!,\n seed,\n })\n const corpseAdd = (i: number, draw: () => number = R): void => void corpses.add(corpseOf(i, draw() * 10))\n\n // ----- abilities (G 67265 zAbility). true: it took care of the movement -----\n const ability = (i: number, d: number, dx: number, dz: number, tdt: number, b: number, alive: boolean): boolean => {\n const C = T(i)\n const tr = C.traits ?? NO_TRAITS\n const st = Z.state[i]\n const P = target\n // dormant: the stalker wakes under 3 m or with the target's back turned within 10 m; the drowned rises at 5 m\n if (st === STATE.DORMANT) {\n Z.vx[i] = Z.vz[i] = 0\n if (!alive || b > 1) return true\n if (tr.submerged) {\n if (d < tr.submerged.wake) {\n Z.state[i] = STATE.CHASE\n groan(i, d)\n sound('step', 2, Z.x[i]!, 0, Z.z[i]!, d)\n }\n return true\n }\n const am = tr.ambush\n const wake = am?.wake ?? N.dormantWake\n const behind = am?.wakeBehind ?? N.dormantWakeBehind\n const fx = -Math.sin(viewYaw)\n const fz = -Math.cos(viewYaw)\n const costas = (dx * fx + dz * fz) / (d || 1) < (am?.behindCos ?? -0.3)\n if (d < wake || (costas && d < behind)) {\n Z.state[i] = STATE.CHASE\n Z.abilityTimer[i] = am?.sprintSeconds ?? 3\n Z.lost[i] = 0\n sound('runner_groan', 1, Z.x[i]!, 1.6, Z.z[i]!, d)\n }\n return true\n }\n if (st === STATE.AIRBORNE || st === STATE.PILE_STEP) return true\n if (st === STATE.SWELLING) {\n Z.vx[i] = Z.vz[i] = 0\n return true\n }\n if (st === STATE.SWING) {\n Z.vx[i] = Z.vz[i] = 0\n Z.stateTimer[i]! -= tdt\n if (Z.stateTimer[i]! <= 0) {\n const hs = tr.heavySwing!\n Z.state[i] = STATE.CHASE\n Z.attackTimer[i] = 2\n sound('impact', 1.4, Z.x[i]!, 1, Z.z[i]!, d)\n if (d < hs.range && alive && P) {\n P.hurt(C.damage, i)\n P.knock(hs.knock)\n emit('message', { id: 'swing_knock', seconds: 1.5, type: C.id })\n }\n }\n return true\n }\n if (st === STATE.EATING) {\n const ec = tr.eatsCorpses!\n Z.vx[i] = Z.vz[i] = 0\n Z.stateTimer[i]! -= tdt\n if (alive && d < ec.defend) {\n Z.state[i] = STATE.CHASE\n Z.lost[i] = 0\n groan(i, d)\n return false\n } // defends the food\n if (Z.stateTimer[i]! <= 0) {\n let best = -1\n let bd = ec.grabRange\n const list = corpses.entries\n for (let k = 0; k < list.length; k++) {\n const e = Math.hypot(list[k]!.data.x - Z.x[i]!, list[k]!.data.z - Z.z[i]!)\n if (e < bd) {\n bd = e\n best = k\n }\n }\n if (best >= 0) {\n corpses.remove(best)\n Z.hp[i]! += ec.health\n Z.hpMax[i]! += ec.health\n Z.height[i]! *= ec.grow\n Z.build[i]! *= ec.grow\n Z.eaten[i]!++\n }\n Z.state[i] = 0\n Z.abilityTimer[i] = ec.rest\n }\n return true\n }\n if (st === STATE.SEEKING_SOUND) {\n const ss = tr.soundSeeker ?? { arrive: 1, loseChase: 3, hitRadius2: 1.1 }\n if (alive && d < 2.5) {\n Z.state[i] = STATE.CHASE\n return false\n }\n const wx = Z.wx[i]! - Z.x[i]!\n const wz = Z.wz[i]! - Z.z[i]!\n const wl = Math.hypot(wx, wz)\n if (wl < ss.arrive) {\n Z.state[i] = 0\n return false\n }\n Z.vx[i] = (wx / wl) * Z.chaseSpeed[i]!\n Z.vz[i] = (wz / wl) * Z.chaseSpeed[i]!\n Z.yaw[i] = Math.atan2(wx, wz)\n // hits whatever it meets on the way, other agents too\n if (b === 0) {\n Z.attackTimer[i]! -= tdt\n if (Z.attackTimer[i]! <= 0) {\n const hx = Math.floor(Z.x[i]! + HALF)\n const hz = Math.floor(Z.z[i]! + HALF)\n if (hx >= 0 && hz >= 0 && hx < HN && hz < HN)\n for (let j = hHead[hz * HN + hx]!; j !== -1; j = hNext[j]!) {\n if (j !== i && (Z.x[j]! - Z.x[i]!) ** 2 + (Z.z[j]! - Z.z[i]!) ** 2 < ss.hitRadius2) {\n Z.attackTimer[i] = N.attackEvery\n hurtOther(j, C.damage)\n break\n }\n }\n }\n }\n return true\n }\n // custom moves (none in the game): after the timed states, before the moves on the target, with or without a target\n for (const plugin of plugins) if (plugin.ability?.(crowd, i, { d, dx, dz, tdt, band: b, alive })) return true\n if (!alive) return false\n if (Z.hunts[i]! >= 0 && st <= 1 && hunt) return hunt(i, tdt)\n // the blind one does not follow the target across the map: it only goes where it heard\n if (tr.soundSeeker && st === STATE.CHASE && d > tr.soundSeeker.loseChase) {\n Z.state[i] = STATE.SEEKING_SOUND\n Z.wx[i] = P!.state.x\n Z.wz[i] = P!.state.z\n return false\n }\n // pounce up to 6 m, 5 s cooldown\n const po = tr.pounce\n if (po && st === STATE.CHASE && Z.cooldown[i]! <= 0 && d > po.min && d < po.max && b === 0 && losClear(Z.x[i]!, Z.z[i]!, P!.state.x, P!.state.z)) {\n const sp = Math.min(po.speedCap, d / po.time)\n Z.state[i] = STATE.AIRBORNE\n Z.vy[i] = P!.state.y > po.highAbove ? po.liftHigh : po.lift\n Z.vx[i] = (-dx / d) * sp\n Z.vz[i] = (-dz / d) * sp\n Z.yaw[i] = Math.atan2(-dx, -dz)\n Z.cooldown[i] = po.cooldown\n Z.abilityTimer[i] = 1\n sound('runner_groan', 1, Z.x[i]!, 1.6, Z.z[i]!, d)\n return true\n }\n // the bloater swells 0.5 s and bursts when close\n if (tr.burst && st === STATE.CHASE && d < tr.burst.range) {\n Z.state[i] = STATE.SWELLING\n Z.stateTimer[i] = tr.burst.swell\n sound('flesh', 1.2, Z.x[i]!, 1, Z.z[i]!, d)\n return true\n }\n // the devourer looks for corpses (up to 3)\n const ec = tr.eatsCorpses\n if (ec && st === 0 && Z.eaten[i]! < ec.max && Z.abilityTimer[i]! <= 0 && corpses.count) {\n let best = -1\n let bd = ec.search\n const list = corpses.entries\n for (let k = 0; k < list.length; k++) {\n const e = Math.hypot(list[k]!.data.x - Z.x[i]!, list[k]!.data.z - Z.z[i]!)\n if (e < bd) {\n bd = e\n best = k\n }\n }\n if (best >= 0) {\n if (bd < ec.reach) {\n Z.state[i] = STATE.EATING\n Z.stateTimer[i] = ec.seconds\n Z.vx[i] = Z.vz[i] = 0\n return true\n }\n const cp = list[best]!.data\n const ex = cp.x - Z.x[i]!\n const ez = cp.z - Z.z[i]!\n const sp = Z.wanderSpeed[i]! * ec.approach\n Z.vx[i] = (ex / bd) * sp\n Z.vz[i] = (ez / bd) * sp\n Z.yaw[i] = Math.atan2(ex, ez)\n Z.wx[i] = cp.x\n Z.wz[i] = cp.z\n return true\n } else Z.abilityTimer[i] = ec.retry\n }\n // the tracker follows the blood trail and leads up to 8 of its kind\n const tk = tr.tracker\n if (tk && st === 0 && trail.length) {\n let best: { x: number; z: number } | null = null\n for (let k = trail.length - 1; k >= 0; k--) {\n const p = trail[k]!\n if ((p.x - Z.x[i]!) ** 2 + (p.z - Z.z[i]!) ** 2 < tk.range * tk.range) {\n best = p\n break\n }\n }\n if (best) {\n Z.wx[i] = best.x\n Z.wz[i] = best.z\n let led = 0\n const leads = typeIndexById.get(tk.leads) ?? -1\n for (let j = 0; j < Z.n && led < tk.max; j++)\n if (Z.type[j] === leads && Z.state[j] === 0 && (Z.x[j]! - Z.x[i]!) ** 2 + (Z.z[j]! - Z.z[i]!) ** 2 < tk.leadRadius * tk.leadRadius) {\n Z.wx[j] = Z.x[i]!\n Z.wz[j] = Z.z[i]!\n led++\n }\n }\n }\n return false\n }\n const chaseSpeed = (i: number): number => {\n const tr = traits(i)\n if (tr.ambush) return Z.abilityTimer[i]! > 0 ? tr.ambush.sprint : tr.ambush.slow\n if (tr.submerged && water(Z.x[i]!, Z.z[i]!)) return tr.submerged.swim\n return Z.chaseSpeed[i]!\n }\n /** The shared stream with the physics off (the game's order); a counter-based draw of the agent with it on. */\n const physicsDraw = (i: number, salt: number): (() => number) => (engine.active ? () => unit(Z.identity[i]!, engine.ctx.step, salt) : R)\n const hurtOther = (j: number, amount: number): void => {\n Z.hp[j]! -= amount\n blood(Z.x[j]!, Z.height[j]! * 1.2, Z.z[j]!, Z.age[j]!, 30, false, j)\n if (Z.hp[j]! <= 0) deferKill(j)\n }\n const deferKill = (j: number): void => {\n if (!killQ.includes(j)) killQ.push(j)\n }\n const killQFlush = (): void => {\n if (!killQ.length) return\n killQ.sort((a, b) => b - a)\n for (const j of killQ) if (j < Z.n) kill(j, false, physicsDraw(j, SALT.CORPSE))\n killQ.length = 0\n }\n\n // ----- per frame: height (pile, wall, jump), timed states (G 67377 zFrame) -----\n const frame = (i: number, dt: number, d: number, alive: boolean): void => {\n const st = Z.state[i]\n const tr = traits(i)\n if (st === STATE.PILE_STEP) {\n // a step: lets go when the target is gone\n Z.stateTimer[i]! -= dt\n if (!alive || d > N.pileReleaseDistance || Z.stateTimer[i]! <= 0) stepRelease(i)\n return\n }\n if (st === STATE.SWELLING) {\n Z.stateTimer[i]! -= dt\n if (Z.stateTimer[i]! <= 0) deferKill(i)\n return\n }\n if (tr.ambush && Z.abilityTimer[i]! > 0) Z.abilityTimer[i]! -= dt\n if (tr.eatsCorpses && Z.abilityTimer[i]! > 0 && st === 0) Z.abilityTimer[i]! -= dt\n if (tr.waterWeak && Z.abilityTimer[i] === 0 && water(Z.x[i]!, Z.z[i]!)) {\n Z.abilityTimer[i] = -1\n Z.hp[i]! *= tr.waterWeak.keep\n effect({ effect: 'cloud', point: [Z.x[i]!, 0, Z.z[i]!], radius: 1.5, color: tr.waterWeak.cloud, amount: 3, agent: i })\n }\n if (engine.active && (Z.balance[i] === BALANCE.FALLEN || Z.balance[i] === BALANCE.GETTING_UP)) return\n const c = cellOf(Z.x[i]!, Z.z[i]!)\n const gy = groundAt(c, Z.y[i]!)\n if (st === STATE.DORMANT || Z.floorLocked[i]) return\n if (engine.active && st !== STATE.AIRBORNE && Z.vy[i] === 0 && Z.y[i]! > gy + 0.02 && Z.y[i]! - gy <= WALK_DOWN) {\n // down a step of a mound is a walk, not a fall: only a drop above this height (a base that goes) is a fall\n Z.y[i] = Math.max(gy, Z.y[i]! - N.climbSpeed * dt)\n return\n }\n if (st === STATE.AIRBORNE || Z.y[i]! > gy + 0.02 || Z.vy[i]! > 0) {\n Z.vy[i]! -= N.gravity * dt\n Z.y[i]! += Z.vy[i]! * dt\n if (Z.y[i]! <= gy) {\n const fall = -Z.vy[i]!\n Z.y[i] = gy\n Z.vy[i] = 0\n const collapsed = st === STATE.AIRBORNE && Z.stateTimer[i] === COLLAPSE_MARK\n if (collapsed) Z.stateTimer[i] = 0\n if (engine.active && balanceOn && (st !== STATE.AIRBORNE || collapsed)) engine.balance!.landed(i, (fall * fall) / (2 * N.gravity))\n if (st === STATE.AIRBORNE) {\n Z.state[i] = STATE.CHASE\n if (tr.pounce && alive && d < tr.pounce.landRange && target) {\n target.knock(tr.pounce.knock)\n target.hurt(T(i).damage, i)\n emit('message', { id: 'pounce_knock', seconds: 1.5, type: T(i).id })\n }\n } else if (fall > N.fallStun && Z.state[i]! <= 1) {\n Z.state[i] = STATE.STUNNED\n Z.stateTimer[i] = 0.9\n }\n }\n } else if (gy > Z.y[i]!) Z.y[i] = Math.min(gy, Z.y[i]! + N.climbSpeed * dt)\n }\n\n // ----- the horde update (G 66835 hordeUpdate) -----\n const update = (dt: number, context: CrowdUpdateContext = {}): void => {\n if (context.target !== undefined) target = context.target\n groanBudget -= dt\n nightF = context.nightFactor ?? 0\n const P = target\n const px = P ? P.state.x : (context.flowTarget?.[0] ?? 0)\n const pz = P ? P.state.z : (context.flowTarget?.[1] ?? 0)\n const alive = !!P && P.state.alive && !P.state.turned\n viewYaw = context.viewYaw ?? (P ? P.state.yaw - Math.PI : 0)\n const bandX = context.bandCentre?.[0] ?? px\n const bandZ = context.bandCentre?.[1] ?? pz\n engine.beginStep(dt, { x: bandX, z: bandZ })\n const fx0 = context.flowTarget?.[0] ?? px\n const fz0 = context.flowTarget?.[1] ?? pz\n const pc = cellOf(fx0, fz0)\n if (flow.cell < 0) {\n flowStart(fx0, fz0)\n flowStep(1e9)\n flow.cell = pc\n } else if (pc !== flow.cell && !flow.phase) {\n flowStart(fx0, fz0)\n flow.cell = pc\n }\n flowStep(N.flowBudgetMs, flowCells)\n // spatial hash for R0\n if (engine.active) {\n for (let k = 0; k < hashTouchedN; k++) hHead[hashTouched[k]!] = -1\n hashTouchedN = 0\n } else hHead.fill(-1)\n for (let i = 0; i < Z.n; i++) {\n if (Z.band[i] !== 0 || (engine.active && Z.balance[i] === BALANCE.THROWN)) continue\n const hx = Math.floor(Z.x[i]! + HALF)\n const hz = Math.floor(Z.z[i]! + HALF)\n if (hx < 0 || hz < 0 || hx >= HN || hz >= HN) continue\n const h = hz * HN + hx\n if (engine.active && hHead[h] === -1) hashTouched[hashTouchedN++] = h\n hNext[i] = hHead[h]!\n hHead[h] = i\n }\n const B = stats.bands\n B[0] = B[1] = B[2] = B[3] = 0\n let slots = 0\n const F = flow.dir\n for (let i = 0; i < Z.n; i++) {\n const dx = Z.x[i]! - px\n const dz = Z.z[i]! - pz\n const d = Math.sqrt(dx * dx + dz * dz)\n const bandD = context.bandCentre ? Math.hypot(Z.x[i]! - bandX, Z.z[i]! - bandZ) : d\n let b = Z.band[i]!\n while (b > 0 && bandD < BANDS[b - 1]!.max - HYST) b--\n while (b < 3 && bandD > BANDS[b]!.max + HYST) b++\n Z.band[i] = b\n B[b as 0 | 1 | 2 | 3]++\n if (engine.active && Z.balance[i] === BALANCE.THROWN) continue\n const grounded = engine.active && balanceOn && (Z.balance[i] === BALANCE.FALLEN || Z.balance[i] === BALANCE.GETTING_UP)\n const C = T(i)\n const tr = C.traits ?? NO_TRAITS\n if (Z.state[i] === STATE.STUNNED) {\n Z.stateTimer[i]! -= dt\n if (Z.stateTimer[i]! <= 0) Z.state[i] = STATE.CHASE\n Z.vx[i]! *= 0.9\n Z.vz[i]! *= 0.9\n }\n if (Z.state[i] === STATE.CALLING) {\n Z.stateTimer[i]! -= dt\n Z.vx[i] = Z.vz[i] = 0\n if (Z.stateTimer[i]! <= 0) {\n Z.state[i] = STATE.CHASE\n screamAt(i, Z.x[i]!, Z.z[i]!)\n Z.cooldown[i] = tr.caller!.cooldown\n }\n }\n Z.cooldown[i]! -= dt\n if (Z.alertIn[i]! > 0) {\n Z.alertIn[i]! -= dt\n if (Z.alertIn[i]! <= 0 && Z.state[i] === 0 && !tr.soundSeeker) {\n Z.state[i] = STATE.CHASE\n Z.lost[i] = 0\n }\n }\n if (!grounded) Z.next[i]! -= dt\n else {\n Z.vx[i] = Z.vz[i] = Z.dvx[i] = Z.dvz[i] = Z.aimX[i] = Z.aimZ[i] = 0\n Z.steering[i] = 0\n }\n if (Z.next[i]! <= 0 && !grounded) {\n const tdt = BANDS[b]!.dt\n Z.next[i]! += tdt\n // perception (R0 and R1 only)\n if (alive && b <= 1 && Z.state[i]! <= 1) {\n let sees = false\n const A = pack.ages[Z.age[i]!]!\n const vis = C.perception.sight * (1 - N.nightSight * nightF) * (P!.state.crouch ? N.crouchSight : 1) * A.sight\n if (d < vis) {\n const fx = Math.sin(Z.yaw[i]!)\n const fz = Math.cos(Z.yaw[i]!)\n const cone = (-dx * fx - dz * fz) / (d || 1)\n if ((cone > N.sightCone || d < N.sightNear) && (Z.floor[i] === P!.state.floor || d < 5) && (Z.floor[i] || P!.state.floor || losClear(Z.x[i]!, Z.z[i]!, px, pz))) sees = true\n }\n if (C.perception.smell && d < C.perception.smell * (P!.state.bleeding || P!.state.scentTimer > 0 ? 2 : 1)) sees = true // smell\n if (sees) {\n if (Z.state[i] === 0) groan(i, d)\n Z.state[i] = STATE.CHASE\n Z.lost[i] = 0\n } else if (Z.state[i] === STATE.CHASE) {\n Z.lost[i]! += tdt\n if (Z.lost[i]! > N.loseAfter) {\n Z.state[i] = 0\n Z.wx[i] = px\n Z.wz[i] = pz\n }\n }\n }\n for (let k = 0; k < noises.length && (Z.state[i] === 0 || Z.state[i] === STATE.SEEKING_SOUND); k++) {\n const Nz = noises[k]!\n const hear = Nz.r * (C.perception.hearing / N.hearingBase) * pack.ages[Z.age[i]!]!.hearing\n if ((Z.x[i]! - Nz.x) ** 2 + (Z.z[i]! - Nz.z) ** 2 < hear * hear) {\n if (tr.soundSeeker) {\n Z.state[i] = STATE.SEEKING_SOUND\n Z.wx[i] = Nz.x\n Z.wz[i] = Nz.z\n break\n } // straight to the source\n Z.state[i] = STATE.CHASE\n Z.lost[i] = N.heardLost\n groan(i, d)\n }\n }\n // the caller winds up 1.5 s and calls the crowd within 150 m\n if (tr.caller && Z.state[i] === STATE.CHASE && d < tr.caller.range && Z.cooldown[i]! <= 0) {\n Z.state[i] = STATE.CALLING\n Z.stateTimer[i] = tr.caller.windUp\n }\n Z.steering[i] = 0\n Z.aimX[i] = Z.aimZ[i] = 0\n if (!ability(i, d, dx, dz, tdt, b, alive)) {\n // where it wants to go\n let tx = 0\n let tz = 0\n let sp = 0\n if (Z.state[i] === STATE.CHASE || Z.state[i] === STATE.GRAB) {\n const c = cellOf(Z.x[i]!, Z.z[i]!)\n const sameL = !P || Z.floor[i] === P.state.floor\n if (d < N.directRange && alive && sameL) {\n tx = -dx / (d || 1)\n tz = -dz / (d || 1)\n } else if (c >= 0 && b === 0 && d < N.pyramidRange && sameL && flow.dist[c]! < 1e8 && flow.dist[c]! * GC > d * 2 + 6 && losClear(Z.x[i]!, Z.z[i]!, px, pz)) {\n tx = -dx / d\n tz = -dz / d\n } // the path goes round: straight against the wall (pyramid)\n else if (c >= 0) {\n const W = flow.win\n const cx = c % FW\n const cz = (c / FW) | 0\n if (cx >= W[0]! && cx <= W[2]! && cz >= W[1]! && cz <= W[3]!) {\n tx = F[c * 2]!\n tz = F[c * 2 + 1]!\n } else {\n tx = -dx\n tz = -dz\n } // outside the window: straight at the target\n const l = Math.hypot(tx, tz) || 1\n tx /= l\n tz /= l\n }\n if (tr.caller && Z.cooldown[i]! > tr.caller.fleeAbove && d < tr.caller.fleeRange && alive) {\n tx = dx / (d || 1)\n tz = dz / (d || 1)\n } // runs from the fight after calling\n sp = chaseSpeed(i) * (P && P.state.screamBoost > 0 ? N.screamSpeed : 1)\n if (!alive) sp = Z.wanderSpeed[i]!\n if (d < N.attackRange && alive && Math.abs(P!.state.y - Z.y[i]!) < N.attackHeight) {\n sp = 0\n if (slots < N.attackSlots - (Z.aggression[i]! < 1 ? RESERVED_SLOTS : 0)) {\n slots++\n attack(i, d, tdt)\n }\n }\n } else if (Z.state[i] === 0) {\n const wx = Z.wx[i]! - Z.x[i]!\n const wz = Z.wz[i]! - Z.z[i]!\n const wl = Math.hypot(wx, wz)\n if (wl < 1 || R() < 0.01 * tdt * 60) {\n Z.wx[i] = clamp(Z.x[i]! + (R() - 0.5) * N.wanderSpan, -HALF + 2, HALF - 2)\n Z.wz[i] = clamp(Z.z[i]! + (R() - 0.5) * N.wanderSpan, -HALF + 2, HALF - 2)\n } else {\n tx = wx / wl\n tz = wz / wl\n Z.pause[i]! -= tdt\n if (Z.pause[i]! < -4 - R() * 6) Z.pause[i] = 1 + R() * 2.5\n sp = Z.pause[i]! > 0 ? 0 : Z.wanderSpeed[i]!\n } // pauses of 1 to 3.5 s between walks\n }\n if (Z.crawl[i]) sp = Math.min(sp, N.crawlSpeed)\n else if (Z.limp[i]) sp *= N.limpSpeed\n if (Z.balance[i] === BALANCE.STAGGERING) sp *= staggerSpeed\n // separation from the neighbours\n let sx = 0\n let sz = 0\n if (b === 0) {\n const hx = Math.floor(Z.x[i]! + HALF)\n const hz = Math.floor(Z.z[i]! + HALF)\n for (let oz = -1; oz <= 1; oz++)\n for (let ox = -1; ox <= 1; ox++) {\n const qx = hx + ox\n const qz = hz + oz\n if (qx < 0 || qz < 0 || qx >= HN || qz >= HN) continue\n for (let j = hHead[qz * HN + qx]!; j !== -1; j = hNext[j]!) {\n if (j === i || Z.state[j] === STATE.PILE_STEP || Z.state[j] === STATE.DORMANT || (balanceOn && (Z.balance[j] === BALANCE.FALLEN || Z.balance[j] === BALANCE.GETTING_UP))) continue\n const ex = Z.x[i]! - Z.x[j]!\n const ez = Z.z[i]! - Z.z[j]!\n const e2 = ex * ex + ez * ez\n const rr = N.separation * (Z.build[i]! + Z.build[j]!)\n if (e2 < rr * rr * 1.6 && e2 > 1e-6) {\n const e = Math.sqrt(e2)\n const w = (rr * 1.26 - e) / e\n sx += ex * w\n sz += ez * w\n }\n }\n }\n }\n if (Z.state[i]! <= 2) {\n if (separationMax !== Infinity) {\n const push = Math.hypot(sx, sz) * N.separationPush\n if (push > separationMax) {\n sx *= separationMax / push\n sz *= separationMax / push\n }\n }\n if (engine.active && sp > 0.05) {\n Z.aimX[i] = tx\n Z.aimZ[i] = tz\n }\n const slow = engine.densitySlowdown(i, b)\n Z.dvx[i] = (tx * sp + sx * N.separationPush) * slow\n Z.dvz[i] = (tz * sp + sz * N.separationPush) * slow\n Z.steering[i] = 1\n if (sp > 0.05) Z.yaw[i] = Math.atan2(tx, tz)\n else if (d < 3 && alive) Z.yaw[i] = Math.atan2(-dx, -dz)\n }\n }\n }\n // integrates every frame (the wanted speed arrives in ~0.15 s, no jerk)\n if (Z.steering[i] && Z.state[i]! <= 2) {\n const k = 1 - Math.exp(-dt * (b === 0 ? 9 : 5))\n Z.vx[i]! += (Z.dvx[i]! - Z.vx[i]!) * k\n Z.vz[i]! += (Z.dvz[i]! - Z.vz[i]!) * k\n }\n const nx = Z.x[i]! + (Z.vx[i]! + Z.pushX[i]!) * dt\n const nz = Z.z[i]! + (Z.vz[i]! + Z.pushZ[i]!) * dt\n let hitCell = -1\n if (!blockedAt(i, nx, Z.z[i]!)) Z.x[i] = nx\n else {\n if (Z.state[i] !== STATE.AIRBORNE) Z.vx[i] = Z.pushX[i] = 0\n hitCell = cellOf(nx, Z.z[i]!)\n }\n if (!blockedAt(i, Z.x[i]!, nz)) Z.z[i] = nz\n else {\n if (Z.state[i] !== STATE.AIRBORNE) Z.vz[i] = Z.pushZ[i] = 0\n hitCell = cellOf(Z.x[i]!, nz)\n }\n if (hitCell >= 0 && Z.state[i] === STATE.CHASE && b === 0) {\n // runners and leapers jump a low obstacle (car, yard wall)\n const vl = tr.vaultsLow\n if (vl && occ[hitCell] !== 5 && occH[hitCell]! <= vl.maxHeight && occH[hitCell]! > Z.y[i]! + vl.minRise) {\n Z.state[i] = STATE.AIRBORNE\n Z.vy[i] = Math.sqrt(2 * N.gravity * (occH[hitCell]! - Z.y[i]! + vl.clearance))\n const s2 = Z.chaseSpeed[i]! * vl.speedFactor\n Z.vx[i] = Math.sin(Z.yaw[i]!) * s2\n Z.vz[i] = Math.cos(Z.yaw[i]!) * s2\n } else if (occ[hitCell] === 2 || (occ[hitCell] === 0 && pile[hitCell]! > 0)) {\n // stuck on the wall or the pile: becomes a step of the pyramid\n Z.stuck[i]! += engine.active && balanceOn ? dt * (1 + engine.pressureRatio(i)) : dt\n if (Z.stuck[i]! > 0.35 + 0.5 * Z.skin[i]!) {\n Z.stuck[i] = 0\n tryStep(i, hitCell)\n }\n }\n } else if (Z.stuck[i]! > 0) Z.stuck[i]! -= dt\n // a closed door in the way: hits it (10 per blow every 1.5 s; brute x3)\n if (hitCell >= 0 && Z.state[i] === STATE.CHASE && doors.at(hitCell) >= 0) {\n Z.attackTimer[i]! -= dt\n if (Z.attackTimer[i]! <= 0) {\n Z.attackTimer[i] = N.attackEvery\n doors.hit(doors.at(hitCell), N.doorDamage * (tr.doorDamage ?? 1))\n }\n }\n frame(i, dt, d, alive)\n const spd = Math.hypot(Z.vx[i]!, Z.vz[i]!)\n Z.renderSpeed[i]! += (spd - Z.renderSpeed[i]!) * Math.min(1, dt * 6)\n // arms: up when it perceives someone; down against a wall so the hand does not go through\n if (Z.band[i]! <= 1) {\n const s = Z.state[i]\n let goal = s === STATE.CHASE || s === STATE.GRAB || s === STATE.PILE_STEP ? 1 : 0\n if (goal && solidAt(Z.x[i]! + Math.sin(Z.renderYaw[i]!) * 0.62, Z.z[i]! + Math.cos(Z.renderYaw[i]!) * 0.62, 1.0)) goal = 0.1\n Z.arms[i]! += (goal - Z.arms[i]!) * Math.min(1, dt * (goal > Z.arms[i]! ? 3 : 5))\n }\n Z.phase[i]! += dt * (Z.renderSpeed[i]! < 0.05 ? 0.15 : N.cadenceBase + N.cadencePerSpeed * Z.renderSpeed[i]!) * Z.cadenceFactor[i]!\n if (physics.deterministic) {\n const attacking = Z.state[i] === STATE.CHASE && alive\n Z.attackPose[i] = attacking && (Z.x[i]! - P!.state.x) ** 2 + (Z.z[i]! - P!.state.z) ** 2 < (Z.attackPose[i] ? ATTACK_LEAVE : ATTACK_ENTER) ? 1 : 0\n }\n // walking without attacking: the body points where it goes\n if (spd > 0.35 && Z.state[i]! <= 1 && !Z.attackPose[i]) Z.yaw[i] = Math.atan2(Z.vx[i]!, Z.vz[i]!)\n let dy = Z.yaw[i]! - Z.renderYaw[i]!\n dy -= Math.round(dy / TAU_APPROX) * TAU_APPROX\n Z.renderYaw[i]! += dy * Math.min(1, dt * (Z.state[i] === STATE.AIRBORNE ? 20 : 9))\n }\n engine.endStep()\n if (pileColumns.length) collapsePileColumns(Z, pileColumns, FW * FW, stepRelease)\n killQFlush()\n noises.length = 0\n stats.slots = slots\n }\n\n // ----- trail, corpses, population -----\n const updateTrail = (dt: number, who: CrowdTarget | null = target): void => {\n for (const p of trail) p.t += dt\n while (trail.length && trail[0]!.t > N.trailSeconds) trail.shift()\n if (!who) return\n const s = who.state\n if ((s.bleeding || s.scentTimer > 0) && s.alive && Math.hypot(s.x - trailLast.x, s.z - trailLast.z) > N.trailStep) {\n trail.push({ x: s.x, z: s.z, t: 0 })\n trailLast = { x: s.x, z: s.z }\n if (trail.length > N.trailMax) trail.shift()\n }\n }\n const spawnAway = (minD: number, maxD: number, view: CrowdView): number => {\n const ox = target ? target.state.x : view.x\n const oz = target ? target.state.z : view.z\n for (let t = 0; t < 30; t++) {\n const a = R() * TURN\n const r = minD + Math.sqrt(R()) * (maxD - minD) // denser away from home\n const x = ox + Math.cos(a) * r\n const z = oz + Math.sin(a) * r\n // never born in front of the camera inside the fog\n const vx = x - view.x\n const vz = z - view.z\n const vd = Math.hypot(vx, vz)\n if (vd < view.far && vx * -Math.sin(view.yaw) + vz * -Math.cos(view.yaw) > vd * 0.5) continue\n if (Math.abs(x) < HALF - 3 && Math.abs(z) < HALF - 3 && !grid.blocked(x, z)) return spawn(x, z)\n }\n return -1\n }\n const updatePopulation = (dt: number, view: CrowdView, opts: { readonly target?: number; readonly migration?: MigrationOptions | null } = {}): void => {\n const popTarget = opts.target ?? N.population\n let k = 0\n while (Z.n < popTarget && k++ < 20) spawnAway(N.refillMin, N.refillMax, view)\n // the excess fades away slowly, only far and out of sight\n if (Z.n > popTarget + 120)\n for (let t = 0, rm = 0; t < 40 && rm < 4; t++) {\n const i = Math.floor(R() * Z.n)\n if (Z.band[i]! < 2 || Z.named[i]! >= 0 || (target && i === target.state.grabbedBy)) continue\n const vx = Z.x[i]! - view.x\n const vz = Z.z[i]! - view.z\n const vd = Math.hypot(vx, vz)\n if (vd < view.far && vx * -Math.sin(view.yaw) + vz * -Math.cos(view.yaw) > -vd * 0.35) continue\n remove(i)\n rm++\n }\n const mg = opts.migration === undefined ? null : opts.migration\n if (!mg) return\n migrateT -= dt\n if (migrateT <= 0) {\n migrateT = mg.every\n let m = 0\n for (let q = 0; q < mg.count && Z.n < Z.capacity; q++) {\n const i = spawn(mg.x + (R() - 0.5) * mg.width, mg.z + R() * mg.depth)\n if (i >= 0) {\n Z.wx[i] = mg.goal[0]\n Z.wz[i] = mg.goal[1]\n m++\n }\n }\n if (m) emit('message', { id: 'migration', seconds: 4 })\n }\n }\n\n const engine = createPhysics(\n {\n store: Z,\n grid,\n doors,\n gravity: N.gravity,\n attackEvery: N.attackEvery,\n types,\n moundHeight,\n corpsesMax: N.corpsesMax,\n typeOf: T,\n target: () => target,\n emit,\n damage: (i, hit, draw) => damageWith(i, hit, draw, true),\n deferKill,\n releasePileStep,\n corpseOf,\n corpseList: () => corpses.entries,\n addCorpse: (corpse) => void corpses.add(corpse),\n groundAt,\n pileBlast,\n },\n physics,\n stats.physics,\n )\n\n const crowd: Crowd = {\n store: Z,\n pack,\n grid,\n doors,\n corpses,\n numbers: N,\n stats,\n pile,\n mound: moundHeight,\n thrown: engine.thrown,\n blast: (blast) => engine.blasts.enqueue(blast),\n pileCount: () => pileN,\n trail,\n variantsOf,\n get target() {\n return target\n },\n set target(next) {\n target = next\n },\n random,\n sound,\n effect,\n typeIndex: (id) => typeIndexById.get(id) ?? -1,\n spawn: (x, z, type = -1) => spawn(x, z, type),\n remove,\n update,\n dispose: () => engine.dispose(),\n emitNoise,\n damage,\n kill: (i, headshot = false) => kill(i, headshot),\n updateTrail,\n updateCorpses: (dt) => void corpses.update(dt),\n updatePopulation,\n spawnAway,\n pileBlast,\n setHunt(next) {\n hunt = next\n },\n chaseSpeed,\n asPlayerCrowd: () => ({\n count: () => Z.n,\n active: (i) => Z.band[i] === 0,\n x: (i) => Z.x[i]!,\n z: (i) => Z.z[i]!,\n y: (i) => Z.y[i]!,\n floor: (i) => Z.floor[i]!,\n chasing: (i) => Z.state[i] === STATE.CHASE,\n down: (i) => Z.crawl[i] !== 0 || Z.state[i] === STATE.STUNNED,\n panicWeight: (i) => traits(i).panicWeight ?? 1,\n hold(i, x, z, k, lookX, lookZ) {\n Z.x[i]! += (x - Z.x[i]!) * k\n Z.z[i]! += (z - Z.z[i]!) * k\n Z.vx[i] = Z.vz[i] = Z.dvx[i] = Z.dvz[i] = 0\n Z.yaw[i] = Math.atan2(lookX - Z.x[i]!, lookZ - Z.z[i]!)\n },\n stun(i, seconds, vx, vz) {\n Z.state[i] = STATE.STUNNED\n Z.stateTimer[i] = seconds\n Z.vx[i] = vx\n Z.vz[i] = vz\n },\n chase(i, cooldown) {\n Z.state[i] = STATE.CHASE\n Z.attackTimer[i] = cooldown\n },\n damage: (i, hit) => void damage(i, { region: hit.region as CrowdHit['region'], amount: hit.amount, distance: hit.distance, cut: hit.cut, caliber: null }),\n }),\n on(event, listener) {\n let set = listeners.get(event)\n if (!set) listeners.set(event, (set = new Set()))\n set.add(listener as (event: never) => void)\n return () => void set.delete(listener as (event: never) => void)\n },\n }\n return crowd\n}\n","import type { Crowd } from '../crowd'\nimport { STATE } from '../types'\n\n/** A building the civilians walk between: its rectangle, whether it is a house (it has a gate), and the side its front faces (-1: toward -z). */\nexport type CivilianBuilding = { readonly x: number; readonly z: number; readonly w: number; readonly d: number; readonly house?: boolean; readonly front: -1 | 1 }\n\nexport type CivilianSpec = {\n readonly id: string\n /** The building name it lives in. */\n readonly home: string\n readonly child?: boolean\n readonly color?: number\n /** `[hour, building]`: from that hour, for one hour, it walks to that building. */\n readonly routine?: readonly (readonly [number, string])[]\n /** In a group it follows this civilian when that one is in the group too, else the target. */\n readonly leader?: string\n /** How close it keeps to whom it follows (2.2 m; the game's child keeps 1.2). */\n readonly followDistance?: number\n}\n\nexport type CivilianState = 'home' | 'walking' | 'group' | 'fleeing' | 'gone' | 'dead' | 'undead'\n\nexport type Civilian = {\n readonly id: string\n readonly spec: CivilianSpec\n readonly k: number\n x: number\n z: number\n yaw: number\n phase: number\n vx: number\n vz: number\n hp: number\n state: CivilianState\n path: [number, number][]\n pathIndex: number\n /** Seconds left to turn after a bite (0: not bitten). */\n biteTimer: number\n attackTimer: number\n fear: number\n /** The building it is going to (or is at). */\n goal: string\n stuckTimer: number\n /** The agent it turned into. */\n agent: number\n}\n\nexport type CivilianNumbers = {\n readonly hpAdult: number\n readonly hpChild: number\n readonly childOffset: number\n readonly sightSquared: number\n readonly leaveDistance: number\n readonly attackRange: number\n readonly attackEvery: number\n readonly biteFactor: number\n readonly biteSeconds: number\n readonly fearAfterHit: number\n readonly nearMessage: number\n readonly fearRange: number\n readonly fearSpeed: number\n readonly walkSpeed: number\n readonly childSpeed: number\n readonly bittenSpeed: number\n readonly groupRunBeyond: number\n readonly groupRunSpeed: number\n readonly fleeSpeed: number\n readonly childFleeFactor: number\n readonly stuckSeconds: number\n readonly arrive: number\n readonly probe: number\n readonly whiskers: readonly number[]\n readonly doorProbe: number\n readonly steer: number\n readonly bodyHeight: number\n readonly patrolChance: number\n readonly childHeight: number\n readonly childBuild: number\n readonly cadenceBase: number\n readonly cadencePerSpeed: number\n readonly inside: number\n readonly doorInside: number\n readonly doorOutside: number\n readonly gate: number\n readonly sidewalk: number\n readonly houseOffset: number\n}\n\n/** The game's numbers (`jogo.js` G 69960-70175). */\nexport const CIVILIAN_DEFAULTS: CivilianNumbers = {\n hpAdult: 100,\n hpChild: 40,\n childOffset: 0.6,\n sightSquared: 100,\n leaveDistance: 18,\n attackRange: 1.0,\n attackEvery: 1.5,\n biteFactor: 1.5,\n biteSeconds: 120,\n fearAfterHit: 10,\n nearMessage: 30,\n fearRange: 6,\n fearSpeed: 3.4,\n walkSpeed: 1.3,\n childSpeed: 0.85,\n bittenSpeed: 0.8,\n groupRunBeyond: 6,\n groupRunSpeed: 3.6,\n fleeSpeed: 2.6,\n childFleeFactor: 1.1,\n stuckSeconds: 8,\n arrive: 0.25,\n probe: 0.45,\n whiskers: [0, 0.7, -0.7, 1.4, -1.4],\n doorProbe: 0.6,\n steer: 8,\n bodyHeight: 0.4,\n patrolChance: 0.2,\n childHeight: 0.65,\n childBuild: 0.7,\n cadenceBase: 0.5,\n cadencePerSpeed: 0.3,\n inside: 0.8,\n doorInside: 0.8,\n doorOutside: 0.9,\n gate: 7.6,\n sidewalk: 6,\n houseOffset: 0.8,\n}\n\nexport type CivilianEventMap = {\n bitten: { id: string; near: boolean }\n died: { id: string; cause: 'attacked' }\n turned: { id: string; agent: number }\n gone: { id: string }\n}\nexport type CivilianEventName = keyof CivilianEventMap\n\nexport type CivilianOptions = {\n readonly people: readonly CivilianSpec[]\n readonly buildings: Readonly<Record<string, CivilianBuilding>>\n /** The hour of the day now (0-24), for the routines. */\n readonly hour?: () => number\n /** The crowd type a bitten civilian turns into (default the first type). */\n readonly turnInto?: number\n /** The waypoints of a building (G 69967 `pontos`): inside, the door from inside and outside, the gate of a house, the sidewalk. */\n readonly waypoints?: (building: CivilianBuilding) => [number, number][]\n readonly numbers?: Partial<CivilianNumbers>\n}\n\nexport type Civilians = {\n readonly list: readonly Civilian[]\n get(id: string): Civilian | undefined\n /** Moves the civilians, lets the agents hunt them, bites, deaths and turning (G 70144 `npcUpdate`, G 70042 `npcZombies`). */\n update(dt: number): void\n /** Joins the target's group: follows its leader or the target. */\n join(id: string): void\n /** Leaves along a path of points; at the end it is `gone`. */\n flee(id: string, path: readonly (readonly [number, number])[]): void\n /** The route between two buildings (G 69974 `rota`): the points of one, the street crossing, the points of the other reversed. */\n route(from: string, to: string): [number, number][]\n on<K extends CivilianEventName>(event: K, listener: (event: CivilianEventMap[K]) => void): () => void\n}\n\n/**\n * Civilians on a crowd: a literal port of the team game's survivors on the block (`jogo.js` G 69960-70175: `pontos`, `rota`, `npcZombies`,\n * `zChaseNpc`, `npcMorre`, `npcVira`, `npcMove`, `npcUpdate`). They keep a routine by the hour, run home when an agent is near, follow a leader in a\n * group, leave along a path, open doors on the way; agents within 10 m that are closer to them than to the target hunt them, bite, and a bitten one\n * turns after `biteSeconds`. No names, lines, radio or diary: those are the game's, through the events.\n */\nexport function createCivilians(crowd: Crowd, options: CivilianOptions): Civilians {\n const N: CivilianNumbers = { ...CIVILIAN_DEFAULTS, ...options.numbers }\n const Z = crowd.store\n const grid = crowd.grid\n const doors = crowd.doors\n const hourNow = options.hour ?? (() => 12)\n const turnInto = options.turnInto ?? 0\n const listeners = new Map<CivilianEventName, Set<(event: never) => void>>()\n const emit = <K extends CivilianEventName>(event: K, payload: CivilianEventMap[K]): void => {\n for (const listener of listeners.get(event) ?? []) (listener as (event: CivilianEventMap[K]) => void)(payload)\n }\n const random = crowd.random\n const R = random\n const lerp = (a: number, b: number, t: number): number => a + (b - a) * t\n const casaDe = (name: string): CivilianBuilding | undefined => options.buildings[name]\n const pontos =\n options.waypoints ??\n ((b: CivilianBuilding): [number, number][] => {\n const casa = !!b.house\n const side = b.front\n const front = side < 0 ? b.z + b.d : b.z\n const back = side < 0 ? b.z : b.z + b.d\n const cx = b.x + b.w / 2\n const inw = side < 0 ? -1 : 1\n const calc = side < 0 ? -N.sidewalk : N.sidewalk\n const pts: [number, number][] = [\n [cx + (casa ? N.houseOffset : 0), (front + (front + back) / 2) / 2],\n [cx, front + inw * N.doorInside],\n [cx, front - inw * N.doorOutside],\n ]\n if (casa) pts.push([cx, side < 0 ? -N.gate : N.gate])\n pts.push([cx, calc])\n return pts\n })\n const rota = (bA: CivilianBuilding, bB: CivilianBuilding): [number, number][] => {\n const a = pontos(bA)\n const b = pontos(bB).reverse()\n const out = a.slice()\n if (Math.sign(a[a.length - 1]![1]) !== Math.sign(b[0]![1])) out.push([a[a.length - 1]![0], b[0]![1]]) // crosses the street\n return out.concat(b)\n }\n const list: Civilian[] = options.people.map((spec, k) => {\n const b = casaDe(spec.home)!\n const p = pontos(b)[0]!\n return {\n id: spec.id,\n spec,\n k,\n x: p[0] + (spec.child ? N.childOffset : 0),\n z: p[1],\n yaw: 0,\n phase: 0,\n vx: 0,\n vz: 0,\n hp: spec.child ? N.hpChild : N.hpAdult,\n state: 'home',\n path: [],\n pathIndex: 0,\n biteTimer: 0,\n attackTimer: 0,\n fear: 0,\n goal: spec.home,\n stuckTimer: 0,\n agent: -1,\n }\n })\n const byId = new Map(list.map((c) => [c.id, c]))\n const exposed = (n: Civilian): boolean => n.state !== 'dead' && n.state !== 'undead' && n.state !== 'gone'\n const target = () => crowd.target?.state ?? null\n const distToTarget = (x: number, z: number): number => {\n const t = target()\n return t ? Math.hypot(x - t.x, z - t.z) : 0\n }\n\n const turn = (n: Civilian): void => {\n n.state = 'undead'\n const i = crowd.spawn(n.x, n.z, turnInto)\n if (i >= 0) {\n Z.color[i] = n.spec.color ?? Z.color[i]!\n Z.named[i] = n.k\n Z.height[i] = n.spec.child ? N.childHeight : Z.height[i]!\n Z.build[i] = n.spec.child ? N.childBuild : Z.build[i]!\n Z.age[i] = 0\n Z.state[i] = STATE.CHASE\n n.agent = i\n }\n emit('turned', { id: n.id, agent: i })\n }\n const die = (n: Civilian): void => {\n if (n.biteTimer > 0) return turn(n)\n n.state = 'dead'\n n.vx = n.vz = 0\n // the body with what it carried (the game's own item stays with the game)\n crowd.corpses.add({\n x: n.x,\n z: n.z,\n yaw: n.yaw,\n height: n.spec.child ? N.childHeight : 1,\n build: n.spec.child ? N.childBuild : 1,\n missing: 0,\n color: n.spec.color ?? 0,\n variant: -1,\n skin: 0.4,\n seed: R() * 10,\n named: n.k,\n })\n crowd.effect({ effect: 'blood', point: [n.x, 1, n.z], age: 0, amount: 300, died: true })\n emit('died', { id: n.id, cause: 'attacked' })\n }\n\n // ----- the agents against the civilians (G 70042 npcZombies) -----\n const agentsAgainst = (dt: number): void => {\n const t = target()\n for (let i = 0; i < Z.n; i++) {\n if (Z.band[i]! > 1) continue\n const k = Z.hunts[i]!\n if (k >= 0) {\n const n = list[k]!\n if (!exposed(n)) {\n Z.hunts[i] = -1\n continue\n }\n const dx = n.x - Z.x[i]!\n const dz = n.z - Z.z[i]!\n const d = Math.hypot(dx, dz)\n if (d > N.leaveDistance) {\n Z.hunts[i] = -1\n continue\n }\n if (d < N.attackRange) {\n Z.attackTimer[i]! -= dt\n if (Z.attackTimer[i]! <= 0) {\n Z.attackTimer[i] = N.attackEvery\n const C = crowd.pack.types[Z.type[i]!]!\n n.hp -= C.damage\n n.fear = N.fearAfterHit\n crowd.sound('flesh', 0.7, n.x, 1, n.z, distToTarget(n.x, n.z))\n crowd.effect({ effect: 'blood', point: [n.x, 1.2, n.z], age: 0, amount: 30, died: false })\n if (n.biteTimer <= 0 && R() < C.bite * N.biteFactor) {\n n.biteTimer = N.biteSeconds\n emit('bitten', { id: n.id, near: distToTarget(n.x, n.z) < N.nearMessage })\n }\n if (n.hp <= 0) die(n)\n }\n }\n } else if (Z.state[i]! <= 1) {\n for (const n of list) {\n if (!exposed(n) || n.state === 'home') continue\n const dx = n.x - Z.x[i]!\n const dz = n.z - Z.z[i]!\n const d2 = dx * dx + dz * dz\n const dp = t ? (t.x - Z.x[i]!) ** 2 + (t.z - Z.z[i]!) ** 2 : Infinity\n if (d2 < N.sightSquared && d2 < dp && grid.losClear(Z.x[i]!, Z.z[i]!, n.x, n.z)) {\n Z.hunts[i] = n.k\n Z.state[i] = STATE.CHASE\n break\n }\n }\n }\n }\n }\n // the crowd calls it: an agent hunting a civilian goes straight at it (G 70073 zChaseNpc)\n crowd.setHunt((i) => {\n const n = list[Z.hunts[i]!]\n if (!n) return false\n const dx = n.x - Z.x[i]!\n const dz = n.z - Z.z[i]!\n const d = Math.hypot(dx, dz) || 1\n const sp = d < 1 ? 0 : crowd.chaseSpeed(i)\n Z.vx[i] = (dx / d) * sp\n Z.vz[i] = (dz / d) * sp\n Z.yaw[i] = Math.atan2(dx, dz)\n return true\n })\n\n // ----- moving (G 70125 npcMove) -----\n const move = (n: Civilian, dt: number, tx: number, tz: number, sp: number): boolean => {\n const dx = tx - n.x\n const dz = tz - n.z\n const d = Math.hypot(dx, dz)\n if (d < N.arrive) {\n n.vx = n.vz = 0\n return true\n }\n let ax = dx / d\n let az = dz / d\n // round a wall: tries 0, ±40°, ±80°\n for (const a of N.whiskers) {\n const c = Math.cos(a)\n const s = Math.sin(a)\n const bx = ax * c - az * s\n const bz = ax * s + az * c\n if (!grid.solidAt(n.x + bx * N.probe, n.z + bz * N.probe, N.bodyHeight)) {\n ax = bx\n az = bz\n break\n }\n }\n // a closed door ahead: opens it\n const c = grid.cellOf(n.x + ax * N.doorProbe, n.z + az * N.doorProbe)\n const di = doors.at(c)\n if (c >= 0 && di >= 0) {\n const D = doors.list[di]!\n if (!D.open && !D.broken && !D.planks) {\n doors.set(di, true)\n crowd.sound('wood', 0.3, D.x, 1, D.z, distToTarget(D.x, D.z))\n }\n }\n n.vx = lerp(n.vx, ax * sp, Math.min(1, dt * N.steer))\n n.vz = lerp(n.vz, az * sp, Math.min(1, dt * N.steer))\n const nx = n.x + n.vx * dt\n const nz = n.z + n.vz * dt\n if (!grid.solidAt(nx, n.z, N.bodyHeight)) n.x = nx\n if (!grid.solidAt(n.x, nz, N.bodyHeight)) n.z = nz\n n.yaw = Math.atan2(n.vx, n.vz)\n return false\n }\n\n const update = (dt: number): void => {\n agentsAgainst(dt)\n const h = hourNow()\n const t = target()\n for (const n of list) {\n if (n.state === 'dead' || n.state === 'undead' || n.state === 'gone') continue\n if (n.biteTimer > 0) {\n n.biteTimer -= dt\n if (n.biteTimer <= 0) {\n turn(n)\n continue\n }\n }\n // fear: an agent near sends it home (or close to its leader in a group)\n let perto = 99\n for (let i = 0; i < Z.n; i++) {\n if (Z.band[i]) continue\n const d = Math.hypot(Z.x[i]! - n.x, Z.z[i]! - n.z)\n if (d < perto) perto = d\n }\n const sp = (perto < N.fearRange ? N.fearSpeed : N.walkSpeed) * (n.spec.child ? N.childSpeed : 1) * (n.biteTimer > 0 ? N.bittenSpeed : 1)\n if (n.state === 'group') {\n const leaderCivilian = n.spec.leader ? byId.get(n.spec.leader) : undefined\n const lider = leaderCivilian && leaderCivilian.state === 'group' ? leaderCivilian : t\n if (lider) {\n const dd = Math.hypot(lider.x - n.x, lider.z - n.z)\n if (dd > (n.spec.followDistance ?? 2.2)) move(n, dt, lider.x, lider.z, dd > N.groupRunBeyond ? N.groupRunSpeed : sp)\n else {\n n.vx *= 0.8\n n.vz *= 0.8\n }\n }\n } else if (n.state === 'fleeing') {\n const p = n.path[n.pathIndex]\n if (!p) {\n n.state = 'gone'\n emit('gone', { id: n.id })\n continue\n }\n n.stuckTimer += dt\n if (move(n, dt, p[0], p[1], N.fleeSpeed * (n.spec.child ? N.childFleeFactor : 1)) || n.stuckTimer > N.stuckSeconds) {\n n.pathIndex++\n n.stuckTimer = 0\n }\n } else if (n.spec.routine) {\n // the routine by hour: walks to the building of the hour\n let alvo = n.spec.home\n for (const [hh, onde] of n.spec.routine) if (h >= hh && h < hh + 1) alvo = onde\n if (perto < N.fearRange) alvo = n.spec.home\n if (alvo !== n.goal) {\n const home = casaDe(n.spec.home)!\n n.path = rota(casaDe(n.goal) ?? home, casaDe(alvo) ?? home)\n n.pathIndex = 0\n n.goal = alvo\n n.state = 'walking'\n }\n if (n.state === 'walking') {\n const p = n.path[n.pathIndex]\n n.stuckTimer += dt\n if (!p) n.state = 'home'\n else if (move(n, dt, p[0], p[1], sp) || n.stuckTimer > N.stuckSeconds) {\n n.pathIndex++\n n.stuckTimer = 0\n }\n } else {\n n.vx *= 0.8\n n.vz *= 0.8\n }\n } else {\n n.vx *= 0.8\n n.vz *= 0.8\n }\n {\n const v = Math.hypot(n.vx, n.vz)\n n.phase += dt * (v < 0.05 ? 0 : N.cadenceBase + N.cadencePerSpeed * v)\n }\n }\n // a named agent: walks the street of its old routine slowly while it sees nobody\n for (let i = 0; i < Z.n; i++)\n if (Z.named[i]! >= 0 && Z.state[i] === STATE.WANDER && R() < dt * N.patrolChance) {\n const n = list[Z.named[i]!]\n if (!n) continue\n const p = pontos(casaDe(n.spec.home)!)\n const q = p[Math.floor(R() * p.length)]!\n Z.wx[i] = q[0]\n Z.wz[i] = q[1]\n }\n }\n\n return {\n list,\n get: (id) => byId.get(id),\n update,\n join(id) {\n const n = byId.get(id)\n if (n && exposed(n)) n.state = 'group'\n },\n flee(id, path) {\n const n = byId.get(id)\n if (!n || !exposed(n)) return\n n.state = 'fleeing'\n n.path = path.map(([x, z]) => [x, z])\n n.pathIndex = 0\n },\n route(from, to) {\n return rota(casaDe(from)!, casaDe(to)!)\n },\n on(event, listener) {\n let set = listeners.get(event)\n if (!set) listeners.set(event, (set = new Set()))\n set.add(listener as (event: never) => void)\n return () => void set.delete(listener as (event: never) => void)\n },\n }\n}\n","import type { Crowd } from '../crowd'\nimport { STATE } from '../types'\n\n/** What an animal does besides wandering and fleeing; each is optional, an animal without one only wanders and flees. */\nexport type HerdRole = {\n /** Wanders within this many metres of home (8). */\n readonly range?: number\n /** Flees from the target closer than this and from agents closer than `fear·1.5 + 2` (5; 0 never flees). */\n readonly fear?: number\n /** Calls when it flees (the hen): sound, noise radius, cooldown. */\n readonly fleeCall?: { readonly sound: string; readonly noise: number; readonly cooldown: number }\n /** Alarm when the target comes within `range` (the lapwing): flies for `fly` s. */\n readonly alarm?: { readonly range: number; readonly sound: string; readonly noise: number; readonly cooldown: number; readonly fly: number }\n /** Now and then calls loud (the cow): chance per second, sound, noise, cooldown. */\n readonly call?: { readonly chance: number; readonly sound: string; readonly noise: number; readonly cooldown: number }\n /** Barks at agents within `bark` m and growls at agents within `growl` m when it is the target's companion (the dog). */\n readonly guard?: { readonly bark: number; readonly barkNoise: number; readonly barkCooldown: number; readonly growl: number; readonly growlCooldown: number; readonly sound: string }\n}\n\nexport type HerdAnimalSpec = HerdRole & {\n /** The species id (any registry: `game-animals-species` has the 13 of the deliveries). */\n readonly species: string\n readonly x: number\n readonly z: number\n /** The species' gait rates and its size (`game-animals` `quadrupedLayout(body).size`, `birdSize(body)`). */\n readonly walkHz: number\n readonly runHz: number\n readonly size: number\n /** Follows 2.2 m behind the target (the companion dog). */\n readonly companion?: boolean\n /** A carcass: it does not move (the vultures circle it). */\n readonly dead?: boolean\n /** A bird of the sky (the vulture): only the flock moves it. */\n readonly sky?: boolean\n}\n\nexport type HerdAnimal = {\n readonly index: number\n readonly spec: HerdAnimalSpec\n x: number\n z: number\n yaw: number\n homeX: number\n homeZ: number\n range: number\n fear: number\n clip: 'idle' | 'walk' | 'run'\n timer: number\n targetX: number\n targetZ: number\n fly: number\n cooldown: number\n walkSpeed: number\n runSpeed: number\n companion: boolean\n dead: boolean\n /** The nearest agent seen in the last look (index and distance), refreshed about a quarter of the time. */\n near: readonly [number, number] | null | undefined\n}\n\nexport type HerdEventMap = {\n sound: { sound: string; volume: number; position: [number, number, number]; distance: number; animal: number }\n noise: { x: number; z: number; radius: number; animal: number }\n message: { id: 'alarm' | 'bark' | 'growl'; seconds: number; animal: number; distance?: number }\n}\nexport type HerdEventName = keyof HerdEventMap\n\nexport type HerdNumbers = {\n readonly range: number\n readonly fear: number\n readonly walkFactor: number\n readonly walkBase: number\n readonly runFactor: number\n readonly runBase: number\n readonly activeDistance: number\n readonly companionBehind: number\n readonly companionRun: number\n readonly companionStop: number\n readonly retargetTries: number\n readonly restBase: number\n readonly restRandom: number\n readonly lookEvery: number\n readonly lookShare: number\n readonly lookRadius: number\n readonly fearAgentFactor: number\n readonly fearAgentBase: number\n readonly fleeStep: number\n readonly arrive: number\n readonly bodyHeight: number\n readonly turn: number\n readonly freeHeight: number\n readonly worldMargin: number\n}\n\n/** The game's numbers (`jogo.js` G 66592-66700 `bichoNovo`, `bichoLivre`, `bichosUpdate`). */\nexport const HERD_DEFAULTS: HerdNumbers = {\n range: 8,\n fear: 5,\n walkFactor: 1.05,\n walkBase: 0.25,\n runFactor: 2.2,\n runBase: 0.8,\n activeDistance: 70,\n companionBehind: 2.2,\n companionRun: 6,\n companionStop: 0.8,\n retargetTries: 6,\n restBase: 4,\n restRandom: 7,\n lookEvery: 7,\n lookShare: 0.25,\n lookRadius: 40,\n fearAgentFactor: 1.5,\n fearAgentBase: 2,\n fleeStep: 6,\n arrive: 0.3,\n bodyHeight: 0.3,\n turn: 6,\n freeHeight: 0.6,\n worldMargin: 4,\n}\n\n/**\n * The roles of the animals of the deliveries, by `game-animals-species` id: what `bichosPovoar` gives each one (`jogo.js` G 66607-66623) and the\n * calls of `bichosUpdate` (hen 30 m every 6 s; lapwing 12 m, 40 m, flies 4 s; cow 80 m, 2% a second, every 30 s; dog barks under 12 m, 60 m, every\n * 3 s, growls under 40 m every 10 s). A game passes its own or changes one.\n */\nexport const HERD_ROLES: Readonly<Record<string, HerdRole & { readonly sky?: boolean }>> = {\n dog: { range: 6, fear: 0, guard: { bark: 12, barkNoise: 60, barkCooldown: 3, growl: 40, growlCooldown: 10, sound: 'bark' } },\n cat: { range: 6, fear: 5 },\n chicken: { range: 5, fear: 4, fleeCall: { sound: 'cluck', noise: 30, cooldown: 6 } },\n southern_lapwing: { range: 5, fear: 0, alarm: { range: 12, sound: 'lapwing', noise: 40, cooldown: 8, fly: 4 } },\n cow: { range: 10, fear: 6, call: { chance: 0.02, sound: 'moo', noise: 80, cooldown: 30 } },\n horse: { range: 10, fear: 7 },\n pig: { range: 5, fear: 3 },\n capybara: { range: 8, fear: 15 },\n vulture: { sky: true },\n}\n\nexport type HerdOptions = {\n /** Where the camera is: animals farther than 70 m rest, and sounds carry their distance from here. Default: the target. */\n readonly view?: () => { readonly x: number; readonly z: number }\n /** A spot an animal must not wander into (inside a building). */\n readonly blocked?: (x: number, z: number) => boolean\n readonly numbers?: Partial<HerdNumbers>\n}\n\nexport type Herd = {\n readonly list: readonly HerdAnimal[]\n /** Adds an animal (draws its heading and its first rest, as the game does). */\n add(spec: HerdAnimalSpec): HerdAnimal\n /** A free spot `r0` to `r1` m from (x, z), or null (G 66601 `bichoLivre`). */\n free(x: number, z: number, r0: number, r1: number, tries?: number): [number, number] | null\n /** Wander, flee, follow, call (G 66648 `bichosUpdate`, without the drawing). */\n update(dt: number): void\n /** The time the herd has run (the flock reads it). */\n readonly time: number\n setCompanion(index: number, on: boolean): void\n on<K extends HerdEventName>(event: K, listener: (event: HerdEventMap[K]) => void): () => void\n}\n\n/**\n * Animals on a crowd: a literal port of the team game's animals (`jogo.js` G 66592-66700). Each wanders around home and rests 4 to 11 s, flees\n * from the target and from the crowd's agents, and may call, raise an alarm or guard the target as its companion. The calls are noises the crowd\n * hears. The drawing and the bones are the game's (`game-animals-three`).\n */\nexport function createHerd(crowd: Crowd, options: HerdOptions = {}): Herd {\n const N: HerdNumbers = { ...HERD_DEFAULTS, ...options.numbers }\n const R = crowd.random\n const Z = crowd.store\n const grid = crowd.grid\n const list: HerdAnimal[] = []\n let time = 0\n const listeners = new Map<HerdEventName, Set<(event: never) => void>>()\n const emit = <K extends HerdEventName>(event: K, payload: HerdEventMap[K]): void => {\n for (const listener of listeners.get(event) ?? []) (listener as (event: HerdEventMap[K]) => void)(payload)\n }\n const view = options.view ?? (() => crowd.target?.state ?? { x: 0, z: 0 })\n const free = (x0: number, z0: number, r0: number, r1: number, tries = 40): [number, number] | null => {\n for (let k = 0; k < tries; k++) {\n const a = R() * 6.28\n const r = r0 + R() * (r1 - r0)\n const x = x0 + Math.cos(a) * r\n const z = z0 + Math.sin(a) * r\n if (Math.abs(x) < grid.half - N.worldMargin && Math.abs(z) < grid.half - N.worldMargin && !grid.solidAt(x, z, N.freeHeight) && !options.blocked?.(x, z)) return [x, z]\n }\n return null\n }\n // the nearest live agent (the crowd's grid is costly for this; the fauna is small, so it sweeps all)\n const nearestAgent = (x: number, z: number, r: number): [number, number] | null => {\n let best = -1\n let bd = r * r\n for (let i = 0; i < Z.n; i++) {\n if (Z.state[i] === STATE.DORMANT) continue\n const dx = Z.x[i]! - x\n const dz = Z.z[i]! - z\n const d = dx * dx + dz * dz\n if (d < bd) {\n bd = d\n best = i\n }\n }\n return best < 0 ? null : [best, Math.sqrt(bd)]\n }\n const call = (E: HerdAnimal, sound: string, noise: number, dc: number): void => {\n crowd.emitNoise(E.x, E.z, noise)\n emit('noise', { x: E.x, z: E.z, radius: noise, animal: E.index })\n emit('sound', { sound, volume: 1, position: [E.x, 0.5, E.z], distance: dc, animal: E.index })\n }\n const update = (dt: number): void => {\n time += dt\n const t = crowd.target?.state\n const px = t?.x ?? 0\n const pz = t?.z ?? 0\n const v = view()\n for (const E of list) {\n if (E.spec.sky) continue\n const dc = Math.hypot(E.x - v.x, E.z - v.z)\n if (dc >= N.activeDistance || E.dead) continue\n E.timer -= dt\n E.cooldown -= dt\n let alvoX = E.targetX\n let alvoZ = E.targetZ\n let corre = false\n const dp = t ? Math.hypot(E.x - px, E.z - pz) : Infinity\n if (E.companion && t) {\n // follows 2.2 m behind the target\n const bx = px - Math.sin(t.yaw) * N.companionBehind\n const bz = pz - Math.cos(t.yaw) * N.companionBehind\n alvoX = bx\n alvoZ = bz\n corre = dp > N.companionRun\n if (Math.hypot(bx - E.x, bz - E.z) < N.companionStop) {\n alvoX = E.x\n alvoZ = E.z\n }\n } else if (E.timer <= 0) {\n const q = free(E.homeX, E.homeZ, 0, E.range, N.retargetTries)\n if (q) {\n E.targetX = q[0]\n E.targetZ = q[1]\n }\n E.timer = N.restBase + R() * N.restRandom\n alvoX = E.targetX\n alvoZ = E.targetZ\n }\n // fear: flees from the target (not the companion) and from agents\n const zp = (time * N.lookEvery + E.x) % 1 < N.lookShare || E.near === undefined ? (E.near = nearestAgent(E.x, E.z, N.lookRadius)) : E.near\n let fx = 0\n let fz = 0\n if (E.fear && !E.companion && dp < E.fear) {\n fx += E.x - px\n fz += E.z - pz\n }\n if (E.fear && zp && zp[1] < E.fear * N.fearAgentFactor + N.fearAgentBase) {\n fx += E.x - Z.x[zp[0]]!\n fz += E.z - Z.z[zp[0]]!\n }\n if (fx || fz) {\n const l = Math.hypot(fx, fz) || 1\n alvoX = E.x + (fx / l) * N.fleeStep\n alvoZ = E.z + (fz / l) * N.fleeStep\n corre = true\n const fc = E.spec.fleeCall\n if (fc && E.cooldown <= 0) {\n E.cooldown = fc.cooldown\n call(E, fc.sound, fc.noise, dc)\n }\n }\n // alarms\n const al = E.spec.alarm\n if (al && dp < al.range && E.cooldown <= 0) {\n E.cooldown = al.cooldown\n E.fly = al.fly\n call(E, al.sound, al.noise, dc)\n emit('message', { id: 'alarm', seconds: 2.5, animal: E.index })\n }\n const cl = E.spec.call\n if (cl && E.cooldown <= 0 && R() < dt * cl.chance) {\n E.cooldown = cl.cooldown\n call(E, cl.sound, cl.noise, dc)\n }\n const g = E.spec.guard\n if (g && zp && E.cooldown <= 0) {\n if (zp[1] < g.bark) {\n E.cooldown = g.barkCooldown\n call(E, g.sound, g.barkNoise, dc)\n if (E.companion) emit('message', { id: 'bark', seconds: 1.5, animal: E.index, distance: Math.round(zp[1]) })\n } else if (E.companion && zp[1] < g.growl) {\n E.cooldown = g.growlCooldown\n emit('message', { id: 'growl', seconds: 2.5, animal: E.index, distance: Math.round(zp[1]) })\n }\n }\n // walks\n const ax = alvoX - E.x\n const az = alvoZ - E.z\n const ad = Math.hypot(ax, az)\n let sp = 0\n if (E.fly > 0) {\n E.fly -= dt\n E.clip = 'run'\n } else if (ad > N.arrive) {\n sp = corre ? E.runSpeed : E.walkSpeed\n E.clip = corre ? 'run' : 'walk'\n } else E.clip = 'idle'\n if (sp) {\n const k = Math.min(1, (sp * dt) / ad)\n const nx = E.x + ax * k\n const nz = E.z + az * k\n if (!grid.solidAt(nx, nz, N.bodyHeight)) {\n E.x = nx\n E.z = nz\n } else E.timer = 0\n let dy = Math.atan2(ax, az) - E.yaw\n dy -= Math.round(dy / 6.2832) * 6.2832\n E.yaw += dy * Math.min(1, dt * N.turn)\n }\n }\n }\n return {\n list,\n add(spec) {\n const E: HerdAnimal = {\n index: list.length,\n spec,\n x: spec.x,\n z: spec.z,\n yaw: R() * 6.28,\n homeX: spec.x,\n homeZ: spec.z,\n range: spec.range ?? N.range,\n clip: 'idle',\n timer: R() * 4,\n targetX: spec.x,\n targetZ: spec.z,\n fear: spec.fear ?? N.fear,\n fly: 0,\n cooldown: 0,\n walkSpeed: spec.walkHz * spec.size * N.walkFactor + N.walkBase,\n runSpeed: spec.runHz * spec.size * N.runFactor + N.runBase,\n companion: !!spec.companion,\n dead: !!spec.dead,\n near: undefined,\n }\n list.push(E)\n return E\n },\n free,\n update,\n get time() {\n return time\n },\n setCompanion(index, on) {\n const E = list[index]\n if (!E) return\n E.companion = on\n E.homeX = E.x\n E.homeZ = E.z\n },\n on(event, listener) {\n let set = listeners.get(event)\n if (!set) listeners.set(event, (set = new Set()))\n set.add(listener as (event: never) => void)\n return () => void set.delete(listener as (event: never) => void)\n },\n }\n}\n","/** One bird of a flock this frame: where it is, its heading and its roll, its wing phase, and whether it is close enough to draw. */\nexport type FlockBird = { x: number; y: number; z: number; yaw: number; roll: number; wing: number; visible: boolean }\n\nexport type FlockMode =\n /** The vultures (G 66700-66708): circle the centre of the bodies on the ground, each at its own speed, radius and height. */\n | 'carrion'\n /** The birds of the live map (Bancada de Hordas G 53609-53615): a loose ring that rises and falls. */\n | 'orbit'\n\nexport type FlockOptions = {\n readonly count: number\n readonly mode: FlockMode\n /** Visible within this distance of the view (carrion 260 m; orbit always). */\n readonly visibleWithin?: number\n}\n\nexport type Flock = {\n readonly birds: readonly FlockBird[]\n /** Moves the flock to time `t` (seconds) around `center`; `view` decides what is visible. */\n update(t: number, dt: number, center: readonly [number, number], view?: readonly [number, number]): void\n}\n\n/** The centre the vultures circle: the mean of the bodies on the ground when there are three or more, else `fallback` (a carcass). */\nexport const carrionCenter = (corpses: readonly { readonly x: number; readonly z: number }[], fallback: readonly [number, number] | null): [number, number] => {\n let ux = 0\n let uz = 0\n let un = 0\n for (const c of corpses) {\n ux += c.x\n uz += c.z\n un++\n }\n if (un >= 3) return [ux / un, uz / un]\n // as in the game: with one or two bodies and no carcass the sums stay (G 66702)\n return fallback ? [fallback[0], fallback[1]] : [ux, uz]\n}\n\n/** A flock of birds by formula: the game's vultures or the live map's ring (literal formulas from the deliveries). */\nexport function createFlock(options: FlockOptions): Flock {\n const birds: FlockBird[] = Array.from({ length: options.count }, () => ({ x: 0, y: 0, z: 0, yaw: 0, roll: 0, wing: 0, visible: true }))\n const within = options.visibleWithin ?? (options.mode === 'carrion' ? 260 : Infinity)\n return {\n birds,\n update(t, dt, [cx, cz], view) {\n birds.forEach((U, i) => {\n if (options.mode === 'carrion') {\n const a = t * (0.18 + i * 0.04) + i * 2.1\n const r = 7 + i * 2\n U.x = cx + Math.cos(a) * r\n U.y = 16 + i * 2.5\n U.z = cz + Math.sin(a) * r\n U.yaw = -a\n U.roll = 0.35\n U.visible = !view || Math.hypot(cx - view[0], cz - view[1]) < within\n if (U.visible) U.wing += dt * 1.2 * 2 * Math.PI\n } else {\n const a = t * 0.3 + i * 0.4\n const r = 20 + (i % 7) * 4\n U.x = cx + Math.cos(a) * r\n U.y = 25 + Math.sin(t + i) * 2\n U.z = cz + Math.sin(a) * r\n U.yaw = -a\n U.roll = 0\n U.visible = !view || Math.hypot(cx - view[0], cz - view[1]) < within\n }\n })\n },\n }\n}\n","import type { Crowd } from '../crowd'\nimport type { Civilians } from './civilians'\n\n/** What the crowd needs of a region model: `game-infection`'s `Infection` is one as it is. */\nexport type InfectionModelLike = {\n materialize(region: string, n: number): { state: 'active' | 'contained' | 'removed'; seed: number }[]\n giveBack(region: string, batch: { state: 'active' | 'contained' | 'removed'; seed: number }[]): void\n infect(region: string, n?: number, hoursElapsed?: number): void\n}\n\nexport type InfectionBindingOptions = {\n /** Where the k-th agent of a batch is born in the region, from its seed; null skips it (it goes straight back). */\n readonly place: (region: string, seed: number, k: number) => readonly [number, number] | null\n /** The crowd type of an agent (default: drawn by the pack's weights). */\n readonly typeFor?: (agent: { readonly state: 'active' | 'contained' | 'removed'; readonly seed: number }, region: string) => number\n /** The relevance band from which an agent goes back to the model (default 3: farther than 400 m). */\n readonly giveBackBand?: number\n /** Civilians whose bites the model hears about (`infect`), in the region `regionOf` gives. */\n readonly civilians?: Civilians\n readonly regionOf?: (x: number, z: number) => string | null\n}\n\nexport type InfectionBinding = {\n /** Brings up to `n` infected of `region` out of the model into the crowd. Returns the agents born. */\n materialize(region: string, n: number): number[]\n /** Gives back to the model the agents of `region` (all regions when left out) that are in the far band, or all of them with `all`. */\n giveBack(region?: string, options?: { readonly all?: boolean }): number\n /** The region of a materialised agent, or null. */\n regionOf(agent: number): string | null\n unbind(): void\n}\n\n/**\n * Ties a crowd to a region model (`game-infection`): `materialize` turns the model's numbers into agents near the player, a kill of one of them\n * goes back to its region as removed, the far ones go back with `giveBack`, and a civilian's bite is an `infect`. No team code exists for this bridge\n * (decision P12 of the spec); the model's own API is used as it is.\n */\nexport function bindInfection(crowd: Crowd, model: InfectionModelLike, options: InfectionBindingOptions): InfectionBinding {\n const tracked = new Map<number, { region: string; state: 'active' | 'contained' | 'removed'; seed: number }>()\n const band = options.giveBackBand ?? 3\n const offKill = crowd.on('kill', ({ agent }) => {\n const entry = tracked.get(agent)\n if (!entry) return\n // it left the numbers when it was materialised: it goes back as removed (a `remove` would take one more active from the model)\n model.giveBack(entry.region, [{ state: 'removed', seed: entry.seed }])\n tracked.delete(agent)\n })\n const offRemove = crowd.on('remove', ({ agent, moved }) => {\n tracked.delete(agent)\n if (moved < 0) return\n const entry = tracked.get(moved)\n if (!entry) return\n tracked.delete(moved)\n tracked.set(agent, entry)\n })\n const offBite = options.civilians?.on('bitten', ({ id }) => {\n const n = options.civilians?.get(id)\n const region = n && options.regionOf?.(n.x, n.z)\n if (region) model.infect(region, 1, 0)\n })\n return {\n materialize(region, n) {\n const batch = model.materialize(region, n)\n const born: number[] = []\n const back: typeof batch = []\n batch.forEach((agent, k) => {\n const at = options.place(region, agent.seed, k)\n const i = at ? crowd.spawn(at[0], at[1], options.typeFor?.(agent, region) ?? -1) : -1\n if (i < 0) {\n back.push(agent)\n return\n }\n tracked.set(i, { region, state: agent.state, seed: agent.seed })\n born.push(i)\n })\n if (back.length) model.giveBack(region, back)\n return born\n },\n giveBack(region, opts = {}) {\n const byRegion = new Map<string, { state: 'active' | 'contained' | 'removed'; seed: number }[]>()\n const indices = [...tracked.keys()].sort((a, b) => b - a)\n for (const i of indices) {\n const entry = tracked.get(i)\n if (!entry || (region !== undefined && entry.region !== region)) continue\n if (!opts.all && crowd.store.band[i]! < band) continue\n let list = byRegion.get(entry.region)\n if (!list) byRegion.set(entry.region, (list = []))\n list.push({ state: entry.state, seed: entry.seed })\n tracked.delete(i)\n crowd.remove(i)\n }\n let count = 0\n for (const [r, list] of byRegion) {\n model.giveBack(r, list)\n count += list.length\n }\n return count\n },\n regionOf: (agent) => tracked.get(agent)?.region ?? null,\n unbind() {\n offKill()\n offRemove()\n offBite?.()\n tracked.clear()\n },\n }\n}\n"],"mappings":"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AAU;QACV,WAAa;QACb,YAAc;QACd,OAAS;MACX;MACA;QACE,IAAM;QACN,QAAU;QACV,WAAa;QACb,YAAc;QACd,OAAS;MACX;MACA;QACE,IAAM;QACN,QAAU;QACV,WAAa;QACb,YAAc;QACd,OAAS;MACX;MACA;QACE,IAAM;QACN,QAAU;QACV,WAAa;QACb,YAAc;QACd,OAAS;MACX;MACA;QACE,IAAM;QACN,QAAU;QACV,WAAa;QACb,YAAc;QACd,OAAS;MACX;MACA;QACE,IAAM;QACN,QAAU;QACV,WAAa;QACb,YAAc;QACd,OAAS;MACX;MACA;QACE,IAAM;QACN,QAAU;QACV,WAAa;QACb,YAAc;QACd,OAAS;MACX;IACF;IACA,SAAW;MACT,MAAQ;QACN,WAAa;QACb,YAAc;QACd,OAAS;MACX;MACA,KAAO;QACL,WAAa;QACb,YAAc;QACd,OAAS;MACX;MACA,KAAO;QACL,WAAa;QACb,YAAc;QACd,OAAS;MACX;IACF;IACA,gBAAkB;MAChB,SAAW;QACT,GAAK;QACL,KAAO;QACP,KAAO;MACT;MACA,OAAS;QACP,GAAK;QACL,KAAO;QACP,KAAO;MACT;MACA,UAAY;QACV,GAAK;QACL,KAAO;QACP,KAAO;MACT;MACA,WAAa;QACX,GAAK;QACL,KAAO;QACP,KAAO;MACT;MACA,SAAW;QACT,GAAK;QACL,KAAO;QACP,KAAO;MACT;IACF;IACA,QAAU;MACR,UAAY;QACV,MAAQ;UACN,IAAM;UACN,QAAU;QACZ;QACA,OAAS;UACP,IAAM;QACR;QACA,QAAU;UACR,IAAM;QACR;QACA,UAAY;UACV,IAAM;UACN,QAAU;QACZ;QACA,SAAW;UACT,IAAM;UACN,QAAU;QACZ;QACA,MAAQ;UACN,IAAM;UACN,QAAU;QACZ;QACA,OAAS;UACP,IAAM;UACN,QAAU;QACZ;QACA,MAAQ;UACN,IAAM;UACN,QAAU;QACZ;MACF;MACA,OAAS;QACP,UAAY;UACV;UACA;QACF;QACA,SAAW;UACT;QACF;QACA,OAAS;UACP;QACF;MACF;MACA,OAAS;QACP,UAAY;QACZ,SAAW;QACX,MAA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","names":["isArm","MISSING_BITS","__export","GROUND_MASS_FACTOR","part","isOpen","frame","PAIRS_PER_AGENT","CENTRE_HEIGHT","identityOf","slot","GRAVITY_FORCE","PAIRS_PER_AGENT","ec","c"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../../game-contracts/src/blast-shove.ts","../../game-contracts/src/blast-knock.ts","../../game-contracts/src/impact-cost.ts","../../game-contracts/src/impact.ts","../../game-contracts/src/body.ts","../../game-contracts/src/spec.ts","../../game-contracts/src/weapons.ts","../../game-contracts/src/ignition.ts","../../game-contracts/src/seal.ts","../../game-contracts/src/twist.ts","../../game-contracts/src/animation.ts","../../game-contracts/src/tool-animation.ts","../../game-contracts/src/frame.ts","../../game-contracts/src/warn.ts","../../game-contracts/src/registry.ts","../../game-contracts/src/bench.ts","../../game-contracts/src/environment.ts","../../game-contracts/src/categories.ts","../../game-contracts/src/origin.ts","../../game-contracts/src/footprint.ts","../../game-contracts/src/foley.ts","../../game-contracts/src/index.ts","../../game-characters/src/data/body.json","../../game-characters/src/body/body-spec.ts","../../game-characters/src/corpses.ts","../../game-characters/src/body/body-state.ts","../../game-characters/src/presets/team-game.json","../../game-characters/src/body/regions.ts","../../game-characters/src/data/segments.json","../../game-characters/src/body/segment-tree.ts","../../game-characters/src/body/describe.ts","../../game-characters/src/body/object-state.ts","../../game-characters/src/body/rig.ts","../../game-characters/src/character.ts","../../game-characters/src/body/transform.ts","../../game-characters/src/damage/bleeding.ts","../../game-characters/src/damage/body-ops.ts","../../game-characters/src/presets/physical.json","../../game-characters/src/damage/defaults.ts","../../game-characters/src/damage/penetration.ts","../../game-characters/src/damage/rules/blow.ts","../../game-characters/src/damage/rules/regions.ts","../../game-characters/src/damage/points.ts","../../game-characters/src/damage/blast.ts","../../game-characters/src/damage/protection.ts","../../game-characters/src/damage/resolve-part.ts","../../game-characters/src/damage/rules/arsenal.ts","../../game-characters/src/damage/rules/elenco.ts","../../game-characters/src/damage/rules/kit.ts","../../game-characters/src/damage/pipeline.ts","../../game-characters/src/damage/damage-character.ts","../../game-characters/src/damage/line.ts","../../game-characters/src/registry.ts","../../game-characters/src/kit.ts","../../game-characters/src/data/gait.json","../../game-characters/src/gait/stride.ts","../../game-characters/src/gait/profile.ts","../../game-characters/src/gait/animate.ts","../../game-characters/src/gait/cascade.ts","../../game-characters/src/gait/moves.ts","../../game-characters/src/gait/lying.ts","../../game-characters/src/condition/timeline.ts","../../game-characters/src/data/appearance.json","../../game-characters/src/appearance/variance.ts","../../game-characters/src/appearance/palette.ts","../../game-characters/src/appearance/traits.ts","../../game-characters/src/pose-clips/pose-clip.ts","../../game-characters/src/presets/pose-clips.json","../../game-characters/src/pose-clips/registry.ts","../../game-characters/src/pose-clips/first-person-clip.ts","../src/data/defaults.json","../src/doors.ts","../src/grid.ts","../src/identity.ts","../src/types.ts","../src/physics/balance.ts","../src/physics/blast.ts","../src/physics/contact.ts","../src/physics/context.ts","../src/physics/density.ts","../src/physics/mound.ts","../src/physics/offload/default-worker.ts","../src/physics/offload/frame.ts","../src/physics/offload/offload.ts","../src/physics/pressure.ts","../src/physics/offload/kernel.ts","../src/physics/offload/worker-host.ts","../src/physics/offload/runner.ts","../src/physics/player-pressure.ts","../src/physics/thrown.ts","../src/physics/engine.ts","../src/physics/pile-column.ts","../src/personality.ts","../src/physics/seat.ts","../src/physics/options.ts","../src/rng.ts","../src/store.ts","../src/crowd.ts","../src/behaviours/civilians.ts","../src/behaviours/herd.ts","../src/behaviours/flock.ts","../src/behaviours/infection.ts"],"sourcesContent":["/**\n * @adatechnology/game-crowd: any crowd as a struct of arrays, pure and seeded. A literal port of the team game's horde (`jogo.js` G 66714-67430):\n * relevance bands with hysteresis, perception by sight, hearing and smell, the sliced flow field, the spatial hash and separation, six attack slots,\n * chain alerts, doors, piles against walls, the special moves as type traits, corpses, the blood trail, the population and migration.\n * The pack (types, ages, outfits) is data: `game-characters-undead` gives the undead one.\n */\nexport { CROWD_DEFAULTS, createCrowd } from './crowd'\nexport type { Corpse, Crowd, CrowdBand, CrowdNumbers, CrowdOptions, CrowdPlugin, CrowdUpdateContext, CrowdView, MigrationOptions, PlayerCrowdLike } from './crowd'\nexport { DOOR_DEFAULTS, createDoors } from './doors'\nexport type { Door, DoorEventMap, DoorEventName, DoorNumbers, Doors } from './doors'\nexport { CELL, createNavGrid } from './grid'\nexport type { NavGrid, NavGridOptions } from './grid'\nexport { createRng } from './rng'\nexport { AGENT_FIELDS, createStore } from './store'\nexport type { CrowdStore } from './store'\nexport { MISSING, STATE } from './types'\nexport type * from './types'\nexport { CIVILIAN_DEFAULTS, createCivilians } from './behaviours/civilians'\nexport type { Civilian, CivilianBuilding, CivilianEventMap, CivilianEventName, CivilianNumbers, CivilianOptions, CivilianSpec, CivilianState, Civilians } from './behaviours/civilians'\nexport { HERD_DEFAULTS, HERD_ROLES, createHerd } from './behaviours/herd'\nexport type { Herd, HerdAnimal, HerdAnimalSpec, HerdEventMap, HerdEventName, HerdNumbers, HerdOptions, HerdRole } from './behaviours/herd'\nexport { carrionCenter, createFlock } from './behaviours/flock'\nexport type { Flock, FlockBird, FlockMode, FlockOptions } from './behaviours/flock'\nexport { bindInfection } from './behaviours/infection'\nexport type { InfectionBinding, InfectionBindingOptions, InfectionModelLike } from './behaviours/infection'\nexport { BALANCE, FALL_CAUSE } from './types'\nexport { mix32, unit, identityOf } from './identity'\nexport { CROWD_PHYSICS_DEFAULTS, resolvePhysics } from './physics/options'\nexport type {\n CrowdBalanceOptions,\n CrowdBlastOptions,\n CrowdContactOptions,\n CrowdDensityOptions,\n CrowdMoundOptions,\n CrowdPersonalityOptions,\n CrowdPhysicsOptions,\n CrowdPlayerPressureOptions,\n CrowdPressureOptions,\n CrowdThrownOptions,\n ResolvedPhysics,\n} from './physics/options'\nexport { contactStep, createContactView } from './physics/contact'\nexport type { ContactParams, ContactResult, ContactView } from './physics/contact'\nexport type { PhysicsStats } from './physics/context'\nexport type { ThrownPool } from './physics/thrown'\nexport { attachPhysicsWorker } from './physics/offload/worker-host'\nexport type { WorkerPort } from './physics/offload/worker-host'\nexport type { OffloadTransport, PhysicsWorkerFactory, PhysicsWorkerHandle } from './physics/offload/types'\n","import type { Vec3 } from './destruction'\n\n/**\n * The numbers of the shove a blast gives to a body that moves (a loose item, a piece of furniture, a person): all of them a game value, with a default that reads well in a room,\n * not a physical table. A host reads them once and lends them to the modules that move their own bodies (`ModuleHost.blastShove`).\n */\nexport interface BlastShoveOptions {\n /**\n * The share of the blast energy that reaches a body as motion: the speed at the centre is `sqrt(2 * energy * coupling / mass)`, before the falloff (default 0.02).\n */\n readonly coupling?: number\n /** Fastest a body is thrown, in m/s, at the centre of the blast (default 20). */\n readonly maxSpeed?: number\n /** How much of the push goes up: the direction is `(away, lift)` normalised from the horizontal outward direction (default 0.3). */\n readonly lift?: number\n /** Below this speed (m/s) nothing moves: a heavy thing far from a small blast stays where it is (default 0.3). */\n readonly minSpeed?: number\n /** Exponent of the falloff `(1 - distance / radius) ^ power`, the same as the damage of the destruction world (default 2). */\n readonly falloffPower?: number\n}\n\n/** The defaults of `BlastShoveOptions`. */\nexport const DEFAULT_BLAST_SHOVE: Readonly<Required<BlastShoveOptions>> = { coupling: 0.02, maxSpeed: 20, lift: 0.3, minSpeed: 0.3, falloffPower: 2 }\n\n/** A blast as the shove reads it: where, how much energy (J) and how far it reaches (m). `reach` (0..1) is the share that got through what stood in the way. */\nexport interface BlastSource {\n readonly point: Vec3\n readonly energy: number\n readonly radius: number\n readonly reach?: number\n}\n\n/** What a blast does to one body: how fast it leaves (m/s), the unit direction it leaves in, and how far from the centre it was. */\nexport interface BlastShove {\n readonly speed: number\n readonly direction: Vec3\n readonly distance: number\n /** The falloff applied, 0..1. */\n readonly falloff: number\n}\n\n/**\n * The shove a blast gives to a body of `mass` kg whose centre is at `position`: away from the centre, lifted a little, with the speed\n * `min(maxSpeed, sqrt(2 * energy * reach * coupling / mass)) * (1 - distance / radius) ^ 2`. Undefined when the body is outside the radius or the speed is under `minSpeed`.\n * The same rule moves the loose items and furniture of the test room and the people of the characters module, so a game that tunes the options tunes all of them.\n */\nexport function blastShove(blast: BlastSource, target: { readonly position: Vec3; readonly mass: number }, options: BlastShoveOptions = {}): BlastShove | undefined {\n const o = { ...DEFAULT_BLAST_SHOVE, ...options }\n const dx = target.position[0] - blast.point[0]\n const dy = target.position[1] - blast.point[1]\n const dz = target.position[2] - blast.point[2]\n const distance = Math.hypot(dx, dy, dz)\n if (!(blast.radius > 0) || distance > blast.radius) return undefined\n const falloff = (1 - distance / blast.radius) ** o.falloffPower\n const mass = Math.max(target.mass, 0.05)\n const speed = Math.min(o.maxSpeed, Math.sqrt((2 * blast.energy * (blast.reach ?? 1) * o.coupling) / mass)) * falloff\n if (!(speed >= o.minSpeed)) return undefined\n // straight above or below the centre there is no \"away\": the push goes up\n const direction: Vec3 = distance > 1e-4 ? [dx / distance, dy / distance + o.lift, dz / distance] : [0, 1, 0]\n const length = Math.hypot(direction[0], direction[1], direction[2])\n return { speed, direction: [direction[0] / length, direction[1] / length, direction[2] / length], distance, falloff }\n}\n","import { blastShove } from './blast-shove'\nimport type { BlastShoveOptions, BlastSource } from './blast-shove'\nimport type { Vec3 } from './destruction'\n\n/** From this speed (m/s) a blast takes a standing body off its feet. Below it the body is only pushed. */\nexport const KNOCK_DOWN_SPEED = 2.5\n/** The angle of a body lying where it fell (the same tilt a dead body takes). */\nexport const KNOCKED_DOWN_ANGLE = 1.35\n/** Seconds the body lies after it lands: this plus the landing speed times `STAND_UP_PER_SPEED`. */\nconst LIE_SECONDS = 1.6\nconst STAND_UP_PER_SPEED = 0.25\nconst FALL_SECONDS = 0.25\nconst RISE_SECONDS = 0.6\n/** m/s squared that slows a body sliding on the ground. */\nconst GROUND_FRICTION = 7\nconst GRAVITY = 9.81\n/** Height of the chest above the feet: where the blast takes the body. */\nexport const KNOCK_CHEST = 1.1\nconst CHEST = KNOCK_CHEST\n\n/** What a blast is doing to one body: its velocity (m/s), whether it is in the air, and how far down it is (0 on its feet, 1 lying). */\nexport interface Knock {\n velocity: [number, number, number]\n airborne: boolean\n /** 0..1 */\n lying: number\n /** Seconds left to lie on the ground after it stopped. */\n lieLeft: number\n /** Knocked off its feet by this blast. */\n down: boolean\n}\n\n/** The mass of a body in kg from its height (m) and its build (1 = average): 70 kg at 1.75 m. */\nexport const bodyMass = (height: number, build = 1): number => 70 * (height / 1.75) ** 2 * build\n\n/**\n * What a blast does to a body standing at `feet`: the shove of `game-contracts` taken at the chest, a hop when it goes up, and a fall when it is strong enough. Undefined when the blast does not\n * reach it. The numbers are the host's (`ModuleHost.blastShove`), the same ones that throw its loose items.\n */\nexport function knockOf(blast: BlastSource, feet: Vec3, mass: number, options?: BlastShoveOptions): Knock | undefined {\n const hit = blastShove(blast, { position: [feet[0], feet[1] + CHEST, feet[2]], mass }, options)\n if (!hit) return undefined\n const [dx, dy, dz] = hit.direction\n return { velocity: [dx * hit.speed, Math.max(0, dy * hit.speed), dz * hit.speed], airborne: dy > 0, lying: 0, lieLeft: 0, down: hit.speed >= KNOCK_DOWN_SPEED }\n}\n\n/**\n * Moves a body one step: it slides on the ground and slows, falls when it was knocked down, lies a while and gets up (a dead body stays down). Moves `position` in place; returns true when the\n * body is on its feet and still, and the knock can be dropped.\n */\nexport function stepKnock(knock: Knock, position: { x: number; y: number; z: number }, dt: number, groundAt: (x: number, z: number) => number, alive: boolean): boolean {\n const v = knock.velocity\n position.x += v[0] * dt\n position.z += v[2] * dt\n const ground = groundAt(position.x, position.z)\n if (knock.airborne || position.y > ground + 1e-4) {\n v[1] -= GRAVITY * dt\n position.y += v[1] * dt\n if (position.y <= ground) {\n position.y = ground\n v[1] = 0\n knock.airborne = false\n }\n } else position.y = ground\n const horizontal = Math.hypot(v[0], v[2])\n if (!knock.airborne && horizontal > 0) {\n const slowed = Math.max(0, horizontal - GROUND_FRICTION * dt)\n v[0] *= slowed / horizontal\n v[2] *= slowed / horizontal\n }\n const moving = horizontal > 0.05 || knock.airborne\n if (knock.down) {\n if (moving) knock.lying = Math.min(1, knock.lying + dt / FALL_SECONDS)\n else {\n knock.lying = 1\n if (alive) {\n knock.lieLeft = knock.lieLeft > 0 ? knock.lieLeft : LIE_SECONDS + Math.hypot(v[0], v[2]) * STAND_UP_PER_SPEED\n knock.lieLeft -= dt\n if (knock.lieLeft <= 0) knock.down = false\n }\n }\n } else if (knock.lying > 0) knock.lying = Math.max(0, knock.lying - dt / RISE_SECONDS)\n return !moving && !knock.down && knock.lying === 0\n}\n","export interface CostOptions {\n /** Reads of `state()` and of `describe()` to time. Default 10 000. */\n reads?: number\n /** Ceiling, in milliseconds, for all the reads of one method. Default 50 (5 microseconds per read). */\n ceilingMs?: number\n /** Ceiling of heap growth, in bytes, over all the reads of one method (only where `process.memoryUsage` exists). Default 1 MiB. */\n allocationBytes?: number\n}\n\nconst DEFAULT_COST_READS = 10_000\nconst DEFAULT_COST_CEILING_MS = 50\nconst DEFAULT_COST_ALLOCATION_BYTES = 1024 * 1024\n\nconst heapUsedBytes = (): number | undefined => {\n const nodeProcess = (globalThis as { process?: { memoryUsage?: () => { heapUsed: number } } }).process\n return nodeProcess?.memoryUsage?.().heapUsed\n}\n\ntype CheckReadCostParams = { readonly name: string; readonly read: () => unknown; readonly options: CostOptions; readonly problems: string[] }\n\nexport const checkReadCost = ({ name, read, options, problems }: CheckReadCostParams): void => {\n const reads = options.reads ?? DEFAULT_COST_READS\n const ceilingMs = options.ceilingMs ?? DEFAULT_COST_CEILING_MS\n const allocationBytes = options.allocationBytes ?? DEFAULT_COST_ALLOCATION_BYTES\n if (read() !== read()) problems.push(`${name}() returns a new object on each read: cache it between changes (cost contract)`)\n // a first pass lets the engine compile and optimise the loop: that code and its feedback are heap too, and would read as the method allocating\n for (let i = 0; i < reads; i++) read()\n const heapBefore = heapUsedBytes()\n const started = performance.now()\n for (let i = 0; i < reads; i++) read()\n const elapsedMs = performance.now() - started\n const heapAfter = heapUsedBytes()\n if (elapsedMs > ceilingMs) problems.push(`${name}() took ${elapsedMs.toFixed(1)} ms for ${reads} reads, over the ${ceilingMs} ms ceiling (cost contract)`)\n if (heapBefore !== undefined && heapAfter !== undefined && heapAfter - heapBefore > allocationBytes)\n problems.push(`${name}() grew the heap by ${heapAfter - heapBefore} bytes over ${reads} reads, over the ${allocationBytes} byte ceiling (cost contract)`)\n}\n","import { checkReadCost } from './impact-cost'\nimport type { CostOptions } from './impact-cost'\nimport type { DamageType, DestructibleSpec, DestructionEvent, Hit, JointSpec, ObjectState, ToolKind, Vec3 } from './destruction'\n\n/**\n * What a game module implements for an object whose behaviour it owns (a vehicle with its own panel deformation, a creature, a machine). The\n * world (`DestructionWorld.register`) hosts it next to its own objects: raycast, shoot, explode, hit, disassemble, extinguish, update, state,\n * reset and `on` all route to it.\n *\n * Space: the impl receives hits in WORLD space and returns events in WORLD space (it owns its transform; the transform given to `register` or\n * `setTransform` is only used by the world for raycasts and blast range). Every event must carry `object === id` and name parts listed by\n * `describe()`.\n */\n/** World-space ray for `DestructibleImpl.surfaceRay`. */\nexport interface SurfaceRay {\n readonly origin: Vec3\n readonly direction: Vec3\n readonly maxDistance: number\n ignored?(part: string): boolean\n}\n\nexport interface SurfaceRayHit {\n readonly part: string\n readonly point: Vec3\n readonly normal: Vec3\n readonly distance: number\n /** Thickness the ray crosses in this part, metres. */\n readonly pathLen: number\n}\n\nexport interface DestructibleImpl {\n readonly id: string\n readonly name?: string\n /**\n * Parts, joints and structure in the impl's local space, for raycast, inspector and log.\n * COST CONTRACT (D8): called per frame by the renderer, so it is O(1), allocation-free and cached between changes: the same object comes\n * back until a hit, tool, fire or pose change alters it. `checkDestructible(impl, { cost: true })` measures it.\n */\n describe(): DestructibleSpec\n /** Apply one hit (see `IMPACT_CONTRACT` for the fields per type) and return the events it caused. */\n hit(hit: Hit): DestructionEvent[]\n /**\n * The real surface a ray meets, for an impl whose drawn shape is not its boxes (a car body): the boxes of `describe()` only say where to look, and a shot that crosses\n * them without touching the drawn surface goes on. `ignored` answers for the parts the round already went through. Null when the ray meets nothing.\n */\n surfaceRay?(ray: SurfaceRay): SurfaceRayHit | null\n /** Advance fire, melting, collapse and other timed behaviour. */\n update?(dt: number): DestructionEvent[]\n /**\n * COST CONTRACT (D8): the host reads it per part per frame, so it is O(1), allocation-free and cached between changes (see `describe()`).\n * The cached object is shared with every reader: treat it as read-only, copy before changing it.\n */\n state(): Readonly<ObjectState>\n reset?(): void\n /** The world took the object out (`DestructionWorld.remove`): the impl lets go of what it joined (shared queues, listeners). */\n dispose?(): void\n /** Same contract as `DestructionWorld.disassemble`: progress 0..1, or -1 when the tool cannot undo the joint. */\n disassemble?(jointId: string, tool: ToolKind, dt: number): number\n extinguish?(partId?: string): void\n /** Optional: an animation moved a part; `position` / `rotation` (Euler XYZ, radians) are object-local. Keep raycast and hit shapes in sync. */\n setPartPose?(partId: string, position: Vec3, rotation?: Vec3): void\n /** Plain JSON of the impl's own state, stored in `DestructionSnapshot.impls`. */\n serialize?(): unknown\n load?(data: unknown): void\n}\n\n/** The fields a weapon or hazard must put in a `Hit`, per damage type. THE impact contract. */\nexport interface ImpactContract {\n required: string[]\n optional: string[]\n note: string\n}\n\nexport const IMPACT_CONTRACT: Record<DamageType, ImpactContract> = {\n ballistic: { required: ['type', 'point', 'direction', 'energy', 'caliber'], optional: ['space', 'source'], note: 'bullets and pellets: joules and diameter in millimetres' },\n blunt: { required: ['type', 'point', 'energy'], optional: ['direction', 'radius', 'space', 'source'], note: 'contact radius in metres (default 3 cm)' },\n slash: { required: ['type', 'point', 'energy'], optional: ['direction', 'radius', 'space', 'source'], note: 'axes, knives, machetes' },\n explosive: { required: ['type', 'point', 'energy', 'radius'], optional: ['lethalRadius', 'space', 'source'], note: 'point is the centre of the blast, radius the range in metres' },\n fire: { required: ['type', 'point', 'temperature', 'radius'], optional: ['duration', 'space', 'source'], note: 'flame temperature in °C; duration in seconds (call every frame with `dt`)' },\n heat: { required: ['type', 'point', 'temperature', 'radius'], optional: ['duration', 'space', 'source'], note: 'heat without flame (cutting torch, hot plate)' },\n}\n\n/** Problems with a hit according to the impact contract (empty when it is valid). */\nexport const checkHit = (hit: Hit): string[] => {\n const c = IMPACT_CONTRACT[hit.type]\n if (!c) return [`unknown damage type \"${String(hit.type)}\"`]\n const out: string[] = []\n for (const f of c.required) {\n const v = (hit as unknown as Record<string, unknown>)[f]\n if (v === undefined || (Array.isArray(v) ? v.some((n) => !Number.isFinite(n)) : typeof v === 'number' && !Number.isFinite(v))) out.push(`${hit.type} hit needs \"${f}\"`)\n }\n return out\n}\n\nexport interface CheckOptions {\n /**\n * Ids of every material the impl may use (the contracts package does not know the materials of your world: pass the ids of\n * `world.materials`, or the built-in plus your own). When omitted, the material check is skipped.\n */\n materials?: Iterable<string>\n /** Opt-in cost measurement (D8): `true` for the defaults, or the limits to use. */\n cost?: boolean | CostOptions\n}\n\nconst finiteJson = (v: unknown, path: string, out: string[]) => {\n if (typeof v === 'number') {\n if (!Number.isFinite(v)) out.push(`${path} is not a finite number`)\n } else if (Array.isArray(v)) v.forEach((x, i) => finiteJson(x, `${path}[${i}]`, out))\n else if (v && typeof v === 'object') for (const [k, x] of Object.entries(v)) finiteJson(x, `${path}.${k}`, out)\n else if (typeof v === 'function' || typeof v === 'symbol' || typeof v === 'bigint') out.push(`${path} is not JSON`)\n}\n\n/**\n * Contract checker for the test room (it needs no world): `describe()` is valid, a 500 J ballistic hit at a part centre returns well-formed events of this impl, `state()`\n * matches `describe()`, hit points never go negative, `update(0.016)` returns an array and every event is JSON-serializable. It hits the impl\n * (and calls `reset()` afterwards when there is one). With `options.cost` it also times `state()` and `describe()` (the cost contract of D8).\n */\nexport const checkDestructible = (impl: DestructibleImpl, options: CheckOptions = {}): { ok: boolean; problems: string[] } => {\n const problems: string[] = []\n const known = options.materials ? new Set(options.materials) : null\n let spec: DestructibleSpec | undefined\n try {\n spec = impl.describe()\n } catch (e) {\n return { ok: false, problems: [`describe() threw: ${String(e)}`] }\n }\n if (!spec || !Array.isArray(spec.parts) || spec.parts.length === 0) return { ok: false, problems: ['describe() must return at least one part'] }\n const ids = new Set<string>()\n for (const p of spec.parts) {\n if (ids.has(p.id)) problems.push(`duplicate part id \"${p.id}\"`)\n ids.add(p.id)\n if (known && !known.has(p.material)) problems.push(`part \"${p.id}\" has unknown material \"${p.material}\"`)\n if (!p.shape || !['box', 'cylinder', 'sphere'].includes(p.shape.kind)) problems.push(`part \"${p.id}\" has no valid shape`)\n if (!Array.isArray(p.position) || p.position.length !== 3) problems.push(`part \"${p.id}\" has no position`)\n }\n const jointIds = new Set<string>()\n for (const j of (spec.joints ?? []) as JointSpec[]) {\n if (!ids.has(j.a) || !ids.has(j.b)) problems.push(`joint \"${j.id ?? `${j.a}~${j.b}`}\" references an unknown part`)\n const jid = j.id ?? `${j.a}~${j.b}`\n if (jointIds.has(jid)) problems.push(`duplicate joint id \"${jid}\"`)\n jointIds.add(jid)\n }\n const checkState = (label: string) => {\n let st: ObjectState\n try {\n st = impl.state()\n } catch (e) {\n problems.push(`state() threw ${label}: ${String(e)}`)\n return\n }\n const sids = st.parts.map((p) => p.id).sort().join(',')\n if (sids !== [...ids].sort().join(',')) problems.push(`state() part ids do not match describe() ${label}`)\n for (const p of st.parts) if (!(p.hp >= 0)) problems.push(`part \"${p.id}\" has negative or invalid hp ${label}`)\n }\n const checkEvents = (events: unknown, label: string) => {\n if (!Array.isArray(events)) {\n problems.push(`${label} must return an array`)\n return\n }\n const evs = events as DestructionEvent[]\n evs.forEach((e, i) => {\n if (e.object !== impl.id) problems.push(`${label} event ${i} (${e.type}) has object \"${String(e.object)}\", expected \"${impl.id}\"`)\n const part = (e as { part?: string }).part\n if (part !== undefined && !ids.has(part)) problems.push(`${label} event ${i} (${e.type}) names unknown part \"${part}\"`)\n })\n const bad: string[] = []\n finiteJson(evs, 'events', bad)\n problems.push(...bad.map((b) => `${label}: ${b}`))\n try {\n const s = JSON.stringify(evs)\n if (JSON.stringify(JSON.parse(s)) !== s) problems.push(`${label} events do not survive a JSON round trip`)\n } catch (e) {\n problems.push(`${label} events are not JSON-serializable: ${String(e)}`)\n }\n }\n checkState('before the hit')\n const target = spec.parts[0]\n try {\n const hit: Hit = { type: 'ballistic', point: [target.position[0], target.position[1], target.position[2]], direction: [0, 0, -1], energy: 500, caliber: 9 }\n checkEvents(impl.hit(hit), 'hit()')\n } catch (e) {\n problems.push(`hit() threw: ${String(e)}`)\n }\n checkState('after the hit')\n if (impl.update) {\n try {\n checkEvents(impl.update(0.016), 'update()')\n } catch (e) {\n problems.push(`update() threw: ${String(e)}`)\n }\n checkState('after update')\n }\n try {\n impl.reset?.()\n } catch (e) {\n problems.push(`reset() threw: ${String(e)}`)\n }\n if (options.cost) {\n const cost = options.cost === true ? {} : options.cost\n try {\n checkReadCost({ name: 'state', read: () => impl.state(), options: cost, problems })\n checkReadCost({ name: 'describe', read: () => impl.describe(), options: cost, problems })\n } catch (e) {\n problems.push(`cost measurement threw: ${String(e)}`)\n }\n }\n return { ok: problems.length === 0, problems }\n}\n","/** The slots of a body an item can cover (the item slot vocabulary: game-items imports it from here). */\nexport type BodySlot = 'head' | 'eyes' | 'torso' | 'arms' | 'hands' | 'legs' | 'feet' | 'over'\n\n/** The 11 segments of the detailed body model (the Arsenal ids). */\nexport type SegmentKind = 'head' | 'jaw' | 'neck' | 'torso' | 'pelvis' | 'upperArm' | 'forearm' | 'hand' | 'thigh' | 'shin' | 'foot'\n\n/** The 6 regions of the crowd model (the game regions 0..5). */\nexport type BodyRegion = 'head' | 'torso' | 'armL' | 'armR' | 'legL' | 'legR'\n\n/** Bits of the `missing` mask of a body. The instanced body shader reads them, so the values are part of the contract. */\nexport const MISSING_BITS = {\n head: 1,\n armL: 2,\n armR: 4,\n legL: 8,\n legR: 16,\n armLBroken: 32,\n armRBroken: 64,\n legLBroken: 128,\n legRBroken: 256,\n jaw: 512,\n} as const\n","import type { DestructibleSpec, PartSpec } from './destruction'\n\nexport interface CheckSpecOptions {\n /** Known material ids. When given, every part's material must be one of them. */\n materials?: Iterable<string>\n}\n\nexport interface SpecReport {\n /** False when there is at least one problem (warnings do not count). */\n ok: boolean\n problems: string[]\n /** Things that are legal but probably wrong, e.g. structural parts that cannot reach an anchored part. */\n warnings: string[]\n}\n\nconst STRUCTURE_ROLES = ['foundation', 'column', 'beam', 'slab', 'wall', 'partition', 'roof', 'stair', 'none']\nconst BEARS = [true, false, 'a_on_b', 'b_on_a']\n\nconst finite = (n: unknown): n is number => typeof n === 'number' && Number.isFinite(n)\nconst positive = (n: unknown): boolean => finite(n) && n > 0\nconst vec3 = (v: unknown): boolean => Array.isArray(v) && v.length === 3 && v.every(finite)\nconst nonNegative = (n: unknown): boolean => n === undefined || (finite(n) && n >= 0)\n\nconst FACES = ['both', '+', '-']\nconst BONDS = ['running', 'stack']\n\n/** Layers, reinforcement, units and breach: known materials, finite positive sizes. */\nconst checkStagedFields = (p: PartSpec | undefined, label: string, materials: Set<string> | null, problems: string[]): void => {\n const known = (m: unknown, what: string): void => {\n if (materials && !materials.has(m as string)) problems.push(`${label} ${what} has unknown material \"${String(m)}\"`)\n }\n ;(p?.layers ?? []).forEach((l, i) => {\n known(l?.material, `layer ${i}`)\n if (!positive(l?.thickness)) problems.push(`${label} layer ${i} thickness must be finite and positive`)\n if (!FACES.includes(l?.faces)) problems.push(`${label} layer ${i} has an invalid faces \"${String(l?.faces)}\"`)\n })\n const r = p?.reinforcement\n if (r) {\n known(r.material, 'reinforcement')\n if (!positive(r.spacing) || !positive(r.diameter)) problems.push(`${label} reinforcement spacing and diameter must be finite and positive`)\n if (r.residualCapacity !== undefined && !(finite(r.residualCapacity) && r.residualCapacity >= 0 && r.residualCapacity <= 1)) problems.push(`${label} reinforcement residualCapacity must be between 0 and 1`)\n }\n const u = p?.units\n if (u) {\n known(u.mortar, 'units mortar')\n if (!vec3(u.size) || !u.size.every(positive)) problems.push(`${label} units size must be three finite positive numbers`)\n if (!BONDS.includes(u.bond)) problems.push(`${label} units has an invalid bond \"${String(u.bond)}\"`)\n }\n if (p?.breach && p.breach.area !== undefined && !positive(p.breach.area)) problems.push(`${label} breach area must be finite and positive`)\n}\n\n/**\n * Static checks on a `DestructibleSpec` (no world needed), for items that give a `spec` instead of an `impl`: unique part ids, known materials\n * (when `options.materials` is given), joints that reference existing, different parts, finite positive sizes, non-negative mass / hp / capacity,\n * valid structure roles and `bears` directions. A structural part with no path of joints to an anchored part is a warning, not a problem: it\n * is allowed, it just falls.\n */\nexport const checkSpec = (spec: DestructibleSpec, options: CheckSpecOptions = {}): SpecReport => {\n const problems: string[] = []\n const warnings: string[] = []\n const materials = options.materials ? new Set(options.materials) : null\n const parts = Array.isArray(spec?.parts) ? spec.parts : []\n if (!Array.isArray(spec?.parts) || parts.length === 0) problems.push('the spec needs at least one part')\n const ids = new Set<string>()\n for (const p of parts) {\n const id = String(p?.id ?? '')\n const label = `part \"${id}\"`\n if (id === '') problems.push('a part has no id')\n else if (ids.has(id)) problems.push(`duplicate part id \"${id}\"`)\n ids.add(id)\n if (materials && !materials.has(p?.material)) problems.push(`${label} has unknown material \"${String(p?.material)}\"`)\n else if (typeof p?.material !== 'string' || p.material === '') problems.push(`${label} needs a material`)\n const s = p?.shape\n if (s?.kind === 'box') {\n if (!Array.isArray(s.size) || s.size.length !== 3 || !s.size.every(positive)) problems.push(`${label} box size must be three finite positive numbers`)\n } else if (s?.kind === 'cylinder') {\n if (!positive(s.radius) || !positive(s.height)) problems.push(`${label} cylinder radius and height must be finite and positive`)\n } else if (s?.kind === 'sphere') {\n if (!positive(s.radius)) problems.push(`${label} sphere radius must be finite and positive`)\n } else problems.push(`${label} has an unknown shape`)\n if (!vec3(p?.position)) problems.push(`${label} needs a finite position [x, y, z]`)\n if (p?.rotation !== undefined && !vec3(p.rotation)) problems.push(`${label} has an invalid rotation`)\n for (const f of ['mass', 'hp', 'thickness'] as const) if (!nonNegative(p?.[f])) problems.push(`${label} ${f} must be a finite number >= 0`)\n if (p?.fill !== undefined && !(finite(p.fill) && p.fill >= 0 && p.fill <= 1)) problems.push(`${label} fill must be between 0 and 1`)\n if (p?.structure) {\n if (!STRUCTURE_ROLES.includes(p.structure.role)) problems.push(`${label} has an invalid structure role \"${String(p.structure.role)}\"`)\n if (!nonNegative(p.structure.capacity)) problems.push(`${label} structure capacity must be a finite number >= 0`)\n }\n if (p?.contains && !nonNegative(p.contains.liters)) problems.push(`${label} contains.liters must be a finite number >= 0`)\n checkStagedFields(p, label, materials, problems)\n }\n\n const jointIds = new Set<string>()\n const edges: Array<[string, string]> = []\n for (const j of spec?.joints ?? []) {\n const id = j.id ?? `${j.a}~${j.b}`\n const label = `joint \"${id}\"`\n if (jointIds.has(id)) problems.push(`duplicate joint id \"${id}\"`)\n jointIds.add(id)\n if (!ids.has(j.a)) problems.push(`${label} references unknown part \"${String(j.a)}\"`)\n if (!ids.has(j.b)) problems.push(`${label} references unknown part \"${String(j.b)}\"`)\n if (j.a === j.b) problems.push(`${label} joins part \"${String(j.a)}\" to itself`)\n if (j.bears !== undefined && !BEARS.includes(j.bears)) problems.push(`${label} has an invalid bears \"${String(j.bears)}\"`)\n if (!nonNegative(j.strength)) problems.push(`${label} strength must be a finite number >= 0`)\n if (!nonNegative(j.seconds)) problems.push(`${label} seconds must be a finite number >= 0`)\n if (j.bears !== false && ids.has(j.a) && ids.has(j.b) && j.a !== j.b) edges.push([j.a, j.b])\n }\n\n // structural parts must reach an anchored part over the joints (only when the spec lists its joints: otherwise they are inferred later)\n const structural = parts.filter((p) => p?.structure && p.structure.role !== 'none')\n if (structural.length && spec.joints) {\n const anchored = parts.filter((p) => p.anchored || p.structure?.role === 'foundation').map((p) => p.id)\n const adj = new Map<string, string[]>()\n for (const [a, b] of edges) {\n adj.set(a, [...(adj.get(a) ?? []), b])\n adj.set(b, [...(adj.get(b) ?? []), a])\n }\n const seen = new Set(anchored)\n const queue = [...anchored]\n while (queue.length) {\n for (const n of adj.get(queue.pop()!) ?? []) {\n if (seen.has(n)) continue\n seen.add(n)\n queue.push(n)\n }\n }\n const lost = structural.filter((p) => !seen.has(p.id)).map((p) => p.id)\n if (lost.length) warnings.push(`structural part(s) ${lost.map((i) => `\"${i}\"`).join(', ')} cannot reach an anchored part, so they will fall`)\n }\n return { ok: problems.length === 0, problems, warnings }\n}\n","import type { BallisticExtras, Hit, HitIgnition, Vec3 } from './destruction'\n\n/**\n * A ballistic shot (one per pellet) as a weapon module produces it. Plain data: any damage model can consume it (`shotToHit` makes the `Hit`).\n */\nexport interface Shot {\n origin: Vec3\n /** Unit vector. */\n direction: Vec3\n /** Muzzle energy in joules. */\n energy: number\n /** Bullet diameter in millimetres. */\n caliber: number\n /** snake_case calibre id, e.g. `9x19`. */\n caliberId: string\n /** Muzzle speed in m/s. */\n speed: number\n /** Projectile mass in grams (one pellet for a shotgun load). */\n mass?: number\n /** Energy at a distance in metres, with the calibre's drag. */\n energyAt(distance: number): number\n pellet: number\n /** snake_case weapon id. */\n weapon: string\n source?: string\n /** Kind of the round that was loaded (`fmj`, `hp`, `ap`, `buck`, `slug`, `incendiary`, `tracer`...). */\n ammo?: string\n /** Millimetres of steel, concrete, brick and wood the round goes through at the muzzle (the ammo table of the weapons module). `shotToHit` scales it by the energy left. */\n penetration?: Readonly<Record<string, number>>\n incendiary?: boolean\n tracer?: boolean\n /** Chances that the round lights flammable vapour or fuel. */\n ignition?: HitIgnition\n}\n\n/**\n * The fields of the round that a damage model takes besides the geometry: the penetration by material scaled by the energy that is left at `distance`\n * (linear in the energy: half the energy goes through half as much), the incendiary and tracer flags and the ignition chances. Pass it as the sixth\n * argument of `DestructionWorld.shoot` or spread it in a `Hit`.\n */\nexport const shotExtras = (shot: Shot, distance = 0): BallisticExtras => {\n const out: { -readonly [K in keyof BallisticExtras]: BallisticExtras[K] } = {}\n if (shot.penetration) {\n const k = shot.energy > 0 ? Math.min(1, Math.max(0, shot.energyAt(distance) / shot.energy)) : 1\n const scaled: Record<string, number> = {}\n for (const key of Object.keys(shot.penetration)) scaled[key] = shot.penetration[key]! * k\n out.penetration = scaled\n }\n if (shot.incendiary) out.incendiary = true\n if (shot.tracer) out.tracer = true\n if (shot.ignition) out.ignition = { spark: shot.ignition.spark, flame: shot.ignition.flame }\n return out\n}\n\n/** One use of a non-firearm (melee swing, flame tick, heat tick, throw, tool tick, extinguisher tick). */\nexport type Use =\n | { kind: 'melee'; damage: 'blunt' | 'slash'; energy: number; radius: number; reach: number; origin: Vec3; direction: Vec3 }\n | { kind: 'flame'; temperature: number; radius: number; range: number; cone: boolean; duration: number; origin: Vec3; direction: Vec3 }\n | { kind: 'heat'; temperature: number; radius: number; range: number; duration: number; origin: Vec3; direction: Vec3 }\n | {\n kind: 'throw'\n energy: number\n radius: number\n fuse: number\n velocity: Vec3\n origin: Vec3\n fire?: { temperature: number; radius: number; seconds: number }\n /** Explosive id (`m67`, `molotov`, `pg7v`...): the host looks the numbers up in the arsenal. Absent on a plain thrown object. */\n explosive?: string\n /** What sets it off: `timer` after `fuse` seconds, `impact` on the first hit once armed. */\n trigger?: 'timer' | 'impact'\n /** Metres the projectile must fly before an impact sets it off; closer, it hits without exploding (rocket launchers). */\n armDistance?: number\n /** Radius in metres of the wounding area (`radius` is the lethal one). */\n injuryRadius?: number\n /** Seconds the blast stuns whoever is inside the injury radius (flashbang). */\n stunSeconds?: number\n /** Seconds the thrower held it after pulling the pin: `fuse` is what was left of the full fuse when it left the hand. */\n cooked?: number\n /** Went off in the thrower's hand (held past the fuse): no velocity, `fuse` is 0 and the blast is at `origin`. */\n inHand?: boolean\n }\n /** An explosive set down where the trigger is pulled (mine, C4, charge): it waits for its trigger instead of flying. */\n | {\n kind: 'place'\n explosive: string\n /** `wire`: something trips it; `remote`: the player fires it; `timer`: after `fuse` seconds. */\n triggers: readonly ('timer' | 'wire' | 'remote')[]\n energy: number\n /** Lethal radius in metres. */\n radius: number\n injuryRadius: number\n /** Cone the blast goes through, in front of `direction` (mine); absent for a blast in every direction. */\n cone?: { halfAngleDeg: number; length: number }\n /** The wire a `wire` charge stretches from where it is set down: metres out, degrees from its facing (counter-clockwise from above) and metres above it. */\n tripwire?: { length: number; angleDeg: number; height: number }\n fuse?: number\n origin: Vec3\n direction: Vec3\n }\n /** The blast behind a rocket launcher: dangerous inside `radius` metres, in the cone around `direction` (pointing away from the target). */\n | { kind: 'backblast'; radius: number; halfAngleDeg: number; origin: Vec3; direction: Vec3 }\n | { kind: 'tool'; tool: string; reach: number; duration: number; origin: Vec3; direction: Vec3 }\n | { kind: 'extinguish'; range: number; coolC: number; duration: number; origin: Vec3; direction: Vec3 }\n | { kind: 'hand'; reach: number; origin: Vec3; direction: Vec3 }\n\n/**\n * The `Hit` a ballistic shot makes at `point` (world space): the energy is what is left of the shot at that distance from its origin\n * (`shot.energyAt`), the direction and calibre come from the shot. `normal` (the surface normal at the point, when the caller knows it) does not\n * change the hit; it is accepted so that callers can pass what their raycast returned and so that a stricter implementation could use it.\n */\nexport const shotToHit = (shot: Shot, point: Vec3, normal?: Vec3): Hit => {\n void normal\n const dx = point[0] - shot.origin[0]\n const dy = point[1] - shot.origin[1]\n const dz = point[2] - shot.origin[2]\n const distance = Math.hypot(dx, dy, dz)\n return {\n type: 'ballistic',\n point: [point[0], point[1], point[2]],\n direction: [shot.direction[0], shot.direction[1], shot.direction[2]],\n energy: shot.energyAt(distance),\n caliber: shot.caliber,\n ...(shot.mass === undefined ? {} : { mass: shot.mass }),\n ...shotExtras(shot, distance),\n source: shot.source ?? shot.weapon,\n }\n}\n\n/**\n * The `Hit` a non-firearm `Use` makes at `point` (world space), or `null` for uses that do not damage by themselves.\n *\n * - `melee` -> `blunt` / `slash` with the swing's `energy`, contact `radius` and `direction`;\n * - `flame` -> `fire` and `heat` -> `heat`, with `temperature`, `duration` and a contact radius of at most 0.3 m (one ray of the cone);\n * - `throw` -> the blast (`explosive`, `energy`, `radius`, centred on `point`, the place it detonates). A thrown item with no blast energy but\n * a `fire` patch (a molotov) gives that patch as a `fire` hit (`temperature`, `radius`, `seconds` as `duration`). No energy and no fire -> `null`;\n * - `tool`, `extinguish` and `hand` -> `null` (tools work on joints, the extinguisher cools, the hand only grabs).\n *\n * `normal` does not change the hit (same as `shotToHit`).\n */\nexport const useToHit = (use: Use, point: Vec3, normal?: Vec3): Hit | null => {\n void normal\n const p: Vec3 = [point[0], point[1], point[2]]\n switch (use.kind) {\n case 'melee':\n return { type: use.damage, point: p, direction: [use.direction[0], use.direction[1], use.direction[2]], energy: use.energy, radius: use.radius }\n case 'flame':\n case 'heat':\n return {\n type: use.kind === 'flame' ? 'fire' : 'heat',\n point: p,\n direction: [use.direction[0], use.direction[1], use.direction[2]],\n temperature: use.temperature,\n radius: Math.min(use.radius, 0.3),\n duration: use.duration,\n }\n case 'throw':\n if (use.energy > 0) return { type: 'explosive', point: p, energy: use.energy, radius: use.radius }\n if (use.fire) return { type: 'fire', point: p, temperature: use.fire.temperature, radius: use.fire.radius, duration: use.fire.seconds }\n return null\n default:\n return null\n }\n}\n","import type { Hit } from './destruction'\n\n/**\n * How a container of flammable fluid (a fuel tank, a jerrycan, a gas bottle) is set alight by what hits it. A normal bullet that goes through only\n * makes a leak; the leak lights when something supplies a flame: a spark of a hard round on metal, an incendiary or tracer round, a fire nearby\n * or a blast. These are the numbers of the target side; the numbers of the round (core, jacket, incendiary, tracer) are in the weapons module\n * (`rules.ignition` of `@adatechnology/game-weapons`) and reach the target in `Hit.ignition`. Every number is a default to be changed per game.\n */\nexport interface IgnitionRules {\n /**\n * How much of the spark and of the flame of a round reaches the vapour, by how full the container is: points `[filled, factor]`, `filled` 0..1\n * (litres over capacity), `factor` 0..1, linear between points. A full container holds a mixture too rich to burn (the vapour has no air) and\n * mostly leaks; a nearly empty one holds the vapour/air mixture of a flammable range and lights the easiest; a dry one has little vapour left.\n * Default: 0.6 empty, 1 at 10 percent, 0.6 at 35, 0.3 at 65, 0.15 at 90 and 0.12 full.\n */\n fillCurve: ReadonlyArray<readonly [number, number]>\n /** 0..1: how much of the fill factor also weighs on the flame of an incendiary or tracer (1: as much as on a spark; 0: not at all). Default 0.5. */\n flameFillInfluence: number\n /** Chance of the flame of a round that is flagged `incendiary` or `tracer` but carries no `ignition` numbers (a host that passes only the flags). */\n flameDefaults: { incendiary: number; tracer: number }\n /**\n * What a spark needs to strike: a factor 0..1 by material id of the struck part. A material that is not listed but is metal takes\n * `sparkMetalDefault`; a part that is not metal never sparks. Default: steel and iron 1, aluminium 0.25.\n */\n sparkTarget: Readonly<Record<string, number>>\n sparkMetalDefault: number\n /**\n * Factor 0..1 on the spark by fluid (default 1): diesel has a flash point of 52 degrees C and more, so a spark that lights petrol vapour does\n * not light it (a flame does). Default: diesel 0.15.\n */\n sparkFluid: Readonly<Record<string, number>>\n /** A leaking container of flammable fluid within `radius` metres of a burning part or a burning fuel stain lights at `chancePerSecond`. */\n fireNear: { radius: number; chancePerSecond: number }\n}\n\n/** Defaults of `IgnitionRules`. */\nexport const DEFAULT_IGNITION_RULES: IgnitionRules = {\n fillCurve: [\n [0, 0.6],\n [0.1, 1],\n [0.35, 0.6],\n [0.65, 0.3],\n [0.9, 0.15],\n [1, 0.12],\n ],\n flameFillInfluence: 0.5,\n flameDefaults: { incendiary: 0.7, tracer: 0.2 },\n sparkTarget: { steel: 1, iron: 1, aluminium: 0.25 },\n sparkMetalDefault: 0.5,\n sparkFluid: { diesel: 0.15 },\n fireNear: { radius: 1.5, chancePerSecond: 0.5 },\n}\n\n/** Merges a partial `IgnitionRules` over the defaults (an object is merged one level deep). */\nexport const resolveIgnitionRules = (rules?: Partial<IgnitionRules>): IgnitionRules => ({\n ...DEFAULT_IGNITION_RULES,\n ...rules,\n flameDefaults: { ...DEFAULT_IGNITION_RULES.flameDefaults, ...rules?.flameDefaults },\n sparkTarget: { ...DEFAULT_IGNITION_RULES.sparkTarget, ...rules?.sparkTarget },\n sparkFluid: { ...DEFAULT_IGNITION_RULES.sparkFluid, ...rules?.sparkFluid },\n fireNear: { ...DEFAULT_IGNITION_RULES.fireNear, ...rules?.fireNear },\n})\n\n/** Linear interpolation of the fill curve at `fill` (0..1); outside the points it keeps the first or the last. */\nexport const fillFactor = (fill: number, curve: IgnitionRules['fillCurve'] = DEFAULT_IGNITION_RULES.fillCurve): number => {\n if (curve.length === 0) return 1\n const f = Math.min(1, Math.max(0, fill))\n if (f <= curve[0]![0]) return curve[0]![1]\n for (let i = 1; i < curve.length; i++) {\n const [x1, y1] = curve[i]!\n const [x0, y0] = curve[i - 1]!\n if (f <= x1) return x1 === x0 ? y1 : y0 + ((y1 - y0) * (f - x0)) / (x1 - x0)\n }\n return curve[curve.length - 1]![1]\n}\n\n/** The factor of what a spark strikes: 0 when the struck part is not metal. */\nexport const sparkTargetFactor = (materialId: string, metal: boolean, rules: IgnitionRules = DEFAULT_IGNITION_RULES): number =>\n !metal ? 0 : rules.sparkTarget[materialId] ?? rules.sparkMetalDefault\n\n/**\n * Chance that one round that went through the wall of a container lights the fluid that leaks: the spark (only on metal) and the flame (any\n * wall), each weighed by the fill of the container; the two are independent. 0 when the round carries nothing (`Hit.ignition` absent and not\n * flagged `incendiary` or `tracer`): a plain bullet on a plain wall only leaks.\n */\nexport const ignitionChance = (hit: Pick<Hit, 'incendiary' | 'tracer' | 'ignition'>, target: { sparkTarget: number; fill: number; fluid?: string }, rules: IgnitionRules = DEFAULT_IGNITION_RULES): number => {\n const k = fillFactor(target.fill, rules.fillCurve)\n const spark = (hit.ignition?.spark ?? 0) * target.sparkTarget * (target.fluid === undefined ? 1 : rules.sparkFluid[target.fluid] ?? 1) * k\n const flameBase = hit.ignition?.flame ?? Math.max(hit.incendiary ? rules.flameDefaults.incendiary : 0, hit.tracer ? rules.flameDefaults.tracer : 0)\n const flame = flameBase * (1 - rules.flameFillInfluence * (1 - k))\n return 1 - (1 - Math.min(1, Math.max(0, spark))) * (1 - Math.min(1, Math.max(0, flame)))\n}\n","/**\n * The seal of a closed container (a food tin, a drink can, a jar, a bottle with a cap): what opens it and whether a breach spoils what is inside.\n * Plain data and one pure check, shared by the item rules (`resolveUse` of game-items) and any host that lets the player open and eat (the test room).\n */\nexport interface SealSpec {\n /** Ids of the tools that open it (items or weapons: `can_opener`, `knife`); any one of them is enough. */\n readonly openWith?: readonly string[]\n /** It opens by hand (a pull tab, a ring pull, a screw cap): no tool needed. */\n readonly byHand?: boolean\n /** A hole through the container or a leak spoils what is inside: it can no longer be eaten or drunk. */\n readonly spoilsWhenBreached?: boolean\n}\n\n/** The state of one container: opened, holed through (a shot, a blade) or leaking. */\nexport interface SealState {\n readonly opened?: boolean\n readonly breached?: boolean\n readonly leaked?: boolean\n}\n\n/** Why a sealed container cannot be eaten or drunk now. */\nexport type SealBlock = 'sealed' | 'breached' | 'leaked'\n\n/** Whether it can be opened with the tools at hand, and with which one (undefined when it opens by hand). */\nexport const sealOpener = (seal: SealSpec, tools: Iterable<string>): { readonly ok: boolean; readonly tool?: string } => {\n if (seal.byHand || !seal.openWith || seal.openWith.length === 0) return { ok: true }\n const have = new Set(tools)\n const tool = seal.openWith.find((id) => have.has(id))\n return tool === undefined ? { ok: false } : { ok: true, tool }\n}\n\n/** What stops eating or drinking it now, or undefined when nothing does. A spoiled container (breached, leaked) is blocked even once open. */\nexport const sealBlock = (seal: SealSpec, state: SealState): SealBlock | undefined => {\n if (seal.spoilsWhenBreached && state.breached) return 'breached'\n if (seal.spoilsWhenBreached && state.leaked) return 'leaked'\n if (!state.opened) return 'sealed'\n return undefined\n}\n","/** How a hand turns something round in strokes (a screw cap, a tap handle, a knob): what `twistStroke` answers for one moment of the turning. */\nexport interface TwistStroke {\n /** Radians the wrist is turned from the middle of its stroke, positive the way the part turns. */\n readonly angle: number\n /** Radians the part has turned in all, from 0 to `strokes * reach`. */\n readonly turned: number\n /** How closed the fingers are, 0 open to 1 closed: closed on the part while it turns, open while the wrist turns back. */\n readonly grip: number\n /** 0..1, how far the hand rises off the part while the wrist turns back (1 is `TWIST_LIFT` metres). */\n readonly lift: number\n}\n\nexport interface TwistOptions {\n /** Strokes of the wrist. Default 3. */\n readonly strokes?: number\n /** Radians the part turns in one stroke (a wrist turns about a quarter round with its hold). Default 1.6. */\n readonly reach?: number\n}\n\n/** Metres a hand rises off the part between two strokes. */\nexport const TWIST_LIFT = 0.012\n/** Share of each stroke spent turning the part; the rest turns the wrist back open. */\nconst TURNING = 0.7\n\nconst smooth = (k: number): number => k * k * (3 - 2 * k)\n\n/**\n * The hand turning a part round at `share` (0..1) of the whole turning: in each stroke the wrist turns with the part, then lets go, rises a little and turns back.\n * Pure and the same for every host, so first and third person turn the same hand the same way.\n */\nexport const twistStroke = (share: number, options: TwistOptions = {}): TwistStroke => {\n const strokes = Math.max(1, Math.round(options.strokes ?? 3))\n const reach = options.reach ?? 1.6\n const along = Math.min(1, Math.max(0, share)) * strokes\n const index = Math.min(strokes - 1, Math.floor(along))\n const phase = along - index\n if (phase <= TURNING) {\n const k = smooth(phase / TURNING)\n return { angle: reach * (k - 0.5), turned: reach * (index + k), grip: 1, lift: 0 }\n }\n const k = (phase - TURNING) / (1 - TURNING)\n return { angle: reach * (0.5 - smooth(k)), turned: reach * (index + 1), grip: 0.35, lift: Math.sin(k * Math.PI) }\n}\n","import type { Vec3 } from './destruction'\n\n/** Pose of one part (or of the whole item) at a key: position offset in metres and Euler XYZ rotation in radians, both relative to the rest pose. */\nexport interface AnimationPose {\n position?: Vec3\n rotation?: Vec3\n}\n\nexport interface AnimationKey {\n /** Seconds from the start of the clip. */\n t: number\n /** Pose of the item's root. */\n position?: Vec3\n rotation?: Vec3\n /** Poses of named parts: the key is a destruction part id (a pistol `reload` moves `mag`; a car door `open` rotates `door_l`). */\n parts?: Record<string, AnimationPose>\n}\n\nexport interface AnimationEventSpec {\n /** Seconds from the start of the clip. */\n t: number\n /** snake_case type the host maps (`sound`, `eject_casing`, `door_latch`...). */\n type: string\n data?: Record<string, unknown>\n /**\n * Seconds of real time the event fires before `t`, not stretched with the clip: a recorded sound whose hit comes that long after its start\n * (the click of a magazine seating) starts early so the hit lands on `t`. The player passes the lead it could apply as `data.lead`.\n */\n lead?: number\n}\n\n/** A use animation a module brings with its item. Poses between keys are interpolated by the player (the test room does it linearly). */\nexport interface AnimationClipSpec {\n /** snake_case: `reload`, `open`, `drink`... Unique within the item. */\n id: string\n /** Seconds. */\n duration: number\n keys: AnimationKey[]\n events?: AnimationEventSpec[]\n}\n\n/** What an item exposes when it brings its own animations. */\nexport interface ItemAnimationContract {\n animations: AnimationClipSpec[]\n}\n\nconst finite3 = (v: unknown): boolean => Array.isArray(v) && v.length === 3 && v.every((n) => typeof n === 'number' && Number.isFinite(n))\n\n/**\n * Problems with a clip (empty when it is valid): a snake_case id, a positive finite duration, at least one key, key and event times inside\n * `[0, duration]` and not going backwards, finite vectors, every named part one of `partIds`, event types that are non-empty strings and\n * JSON-serializable event data.\n */\nexport const checkAnimation = (clip: AnimationClipSpec, partIds: Iterable<string>): { ok: boolean; problems: string[] } => {\n const problems: string[] = []\n const parts = new Set(partIds)\n const id = String(clip?.id ?? '')\n if (!/^[a-z][a-z0-9_]*$/.test(id)) problems.push(`clip id \"${id}\" must be snake_case`)\n const label = `clip \"${id}\"`\n if (!(typeof clip?.duration === 'number' && Number.isFinite(clip.duration) && clip.duration > 0)) problems.push(`${label} needs a positive finite duration`)\n const dur = typeof clip?.duration === 'number' && clip.duration > 0 ? clip.duration : Infinity\n if (!Array.isArray(clip?.keys) || clip.keys.length === 0) {\n problems.push(`${label} needs at least one key`)\n return { ok: false, problems }\n }\n let prev = -Infinity\n clip.keys.forEach((k, i) => {\n if (!(typeof k.t === 'number' && Number.isFinite(k.t)) || k.t < 0 || k.t > dur + 1e-9) problems.push(`${label} key ${i} has t=${String(k.t)} outside 0..${dur}`)\n else if (k.t < prev) problems.push(`${label} key ${i} goes back in time (${k.t} < ${prev})`)\n else prev = k.t\n for (const f of ['position', 'rotation'] as const) if (k[f] !== undefined && !finite3(k[f])) problems.push(`${label} key ${i} has an invalid ${f}`)\n for (const [pid, pose] of Object.entries(k.parts ?? {})) {\n if (!parts.has(pid)) problems.push(`${label} key ${i} moves unknown part \"${pid}\"`)\n for (const f of ['position', 'rotation'] as const) if (pose?.[f] !== undefined && !finite3(pose[f])) problems.push(`${label} key ${i} part \"${pid}\" has an invalid ${f}`)\n }\n })\n let prevEv = -Infinity\n ;(clip.events ?? []).forEach((e, i) => {\n if (!(typeof e.t === 'number' && Number.isFinite(e.t)) || e.t < 0 || e.t > dur + 1e-9) problems.push(`${label} event ${i} has t=${String(e.t)} outside 0..${dur}`)\n else if (e.t < prevEv) problems.push(`${label} event ${i} goes back in time`)\n else prevEv = e.t\n if (typeof e.type !== 'string' || e.type === '') problems.push(`${label} event ${i} needs a type`)\n try {\n JSON.stringify(e.data ?? {})\n } catch {\n problems.push(`${label} event ${i} data is not JSON`)\n }\n })\n return { ok: problems.length === 0, problems }\n}\n","import type { AnimationClipSpec, AnimationEventSpec, AnimationPose } from './animation'\nimport { checkAnimation } from './animation'\nimport type { HostEvent } from './module'\n\n/** Clips a tool can bring, one per move of its cycle. A tool brings the ones it has; a missing clip is simply not played. */\nexport const TOOL_CLIP = {\n DRAW: 'draw',\n SHOT: 'shot',\n MISFIRE: 'misfire',\n EMPTY: 'empty',\n CYCLE: 'cycle',\n RELOAD: 'reload',\n FEED: 'feed',\n FAULT: 'fault',\n CLEAR: 'clear',\n SAFETY_ON: 'safety_on',\n SAFETY_OFF: 'safety_off',\n MODE: 'mode',\n /** A throwable with the pin out, held until it is thrown. */\n COOK: 'cook',\n /** The stock folds, unfolds, slides in or out (variants `fold`, `unfold`, `collapse`, `extend`). */\n STOCK: 'stock',\n /** A light or a laser is switched (variants `on`, `off`). */\n TOGGLE: 'toggle',\n} as const\nexport type ToolClipId = (typeof TOOL_CLIP)[keyof typeof TOOL_CLIP]\n\n/** What a renderer does when a rule event arrives. `variant` picks `<clip>_<variant>` when the tool has it, else `<clip>`. */\nexport type ToolAnimationStep =\n | { readonly type: 'play'; readonly clip: ToolClipId; readonly variant?: string; readonly duration?: number; readonly repeat?: number }\n /** Move the clip that is playing to the marker (a clip event `stage` with `data.stage`) the rule just reached. */\n | { readonly type: 'sync'; readonly stage: string }\n /** The rule says the phase is over: the clip jumps to its end if it is still running. */\n | { readonly type: 'finish'; readonly clip: ToolClipId }\n\nconst stockVariant = (event: Extract<HostEvent, { type: 'stock' }>): string => {\n const isFolding = event.kind === undefined ? false : event.kind.includes('fold')\n if (event.state === 'collapsed') return isFolding ? 'fold' : 'collapse'\n return isFolding ? 'unfold' : 'extend'\n}\n\n/** The move a host event asks of the tool in hand, or null when it has no motion. */\nexport const toolAnimationStep = (event: HostEvent): ToolAnimationStep | null => {\n switch (event.type) {\n case 'tool':\n return { type: 'play', clip: TOOL_CLIP.DRAW, ...(event.duration === undefined ? {} : { duration: event.duration }) }\n case 'shot':\n return { type: 'play', clip: TOOL_CLIP.SHOT, repeat: Math.max(1, event.rounds ?? 1) }\n case 'misfire':\n return { type: 'play', clip: TOOL_CLIP.MISFIRE }\n case 'empty':\n return { type: 'play', clip: TOOL_CLIP.EMPTY }\n case 'mode':\n return { type: 'play', clip: TOOL_CLIP.MODE, variant: event.mode }\n case 'fault':\n return { type: 'play', clip: TOOL_CLIP.FAULT, variant: event.fault }\n case 'clear':\n if (event.phase === 'end') return { type: 'finish', clip: TOOL_CLIP.CLEAR }\n return { type: 'play', clip: TOOL_CLIP.CLEAR, variant: event.fault, ...(event.duration === undefined ? {} : { duration: event.duration }) }\n case 'safety':\n return { type: 'play', clip: event.on ? TOOL_CLIP.SAFETY_ON : TOOL_CLIP.SAFETY_OFF }\n case 'cycle':\n if (event.phase === 'end') return { type: 'finish', clip: TOOL_CLIP.CYCLE }\n return { type: 'play', clip: TOOL_CLIP.CYCLE, ...(event.duration === undefined ? {} : { duration: event.duration }) }\n case 'reload':\n if (event.phase === 'end') return { type: 'finish', clip: TOOL_CLIP.RELOAD }\n return { type: 'play', clip: TOOL_CLIP.RELOAD, ...(event.kind === undefined ? {} : { variant: event.kind }), ...(event.duration === undefined ? {} : { duration: event.duration }) }\n case 'stage':\n return { type: 'sync', stage: event.stage }\n case 'feed':\n return { type: 'play', clip: TOOL_CLIP.FEED }\n case 'cook':\n if (event.phase === 'start') return { type: 'play', clip: TOOL_CLIP.COOK, duration: event.fuse }\n return { type: 'finish', clip: TOOL_CLIP.COOK }\n case 'stock':\n return { type: 'play', clip: TOOL_CLIP.STOCK, variant: stockVariant(event), ...(event.duration === undefined ? {} : { duration: event.duration }) }\n case 'toggle':\n return { type: 'play', clip: TOOL_CLIP.TOGGLE, variant: event.on ? 'on' : 'off' }\n default:\n return null\n }\n}\n\n/** Where a renderer puts the poses: every part of the tool by id (the ids the clips move), and the whole tool. Poses are offsets from rest. */\nexport interface ToolRig {\n setPartPose(partId: string, pose: AnimationPose): void\n setRootPose?(pose: AnimationPose): void\n}\n\n/** Clips by id, with the variants as `<clip>_<variant>`. */\nexport type ToolClipSet = Readonly<Record<string, AnimationClipSpec>>\n\n/** A renderer's tool animator: feed it the host events and the frame time. */\nexport interface ToolAnimator {\n handle(event: HostEvent): void\n update(dt: number): void\n /** Ids of the clips playing now. */\n readonly playing: readonly string[]\n dispose(): void\n}\n\nexport const STAGE_EVENT = 'stage'\n\n/** The clip with its times (keys and events) scaled so that it lasts `duration` seconds. */\nexport const scaleClip = (clip: AnimationClipSpec, duration: number): AnimationClipSpec => {\n const factor = clip.duration > 0 ? duration / clip.duration : 1\n return {\n ...clip,\n duration,\n keys: clip.keys.map((key) => ({ ...key, t: key.t * factor })),\n ...(clip.events ? { events: clip.events.map((event) => ({ ...event, t: event.t * factor })) } : {}),\n }\n}\n\nconst ZERO: [number, number, number] = [0, 0, 0]\nconst mix = (from: readonly number[], to: readonly number[], fraction: number): [number, number, number] => [\n (from[0] ?? 0) + ((to[0] ?? 0) - (from[0] ?? 0)) * fraction,\n (from[1] ?? 0) + ((to[1] ?? 0) - (from[1] ?? 0)) * fraction,\n (from[2] ?? 0) + ((to[2] ?? 0) - (from[2] ?? 0)) * fraction,\n]\n\n/** Pose of the whole tool (`partId` undefined) or of one part at `time`, linear between keys; a key that does not name it means rest. */\nexport const sampleClip = (clip: AnimationClipSpec, partId: string | undefined, time: number): { position: [number, number, number]; rotation: [number, number, number] } => {\n const points = clip.keys.map((key) => {\n const pose = partId === undefined ? key : key.parts?.[partId]\n return { t: key.t, position: pose?.position ?? ZERO, rotation: pose?.rotation ?? ZERO }\n })\n const first = points[0]\n if (!first || first.t > 0) points.unshift({ t: 0, position: ZERO, rotation: ZERO })\n const last = points[points.length - 1]\n if (!last) return { position: ZERO, rotation: ZERO }\n if (time >= last.t) return { position: mix(last.position, last.position, 0), rotation: mix(last.rotation, last.rotation, 0) }\n const index = Math.max(0, points.findIndex((point, i) => time >= point.t && time < (points[i + 1]?.t ?? Infinity)))\n const from = points[index]\n const to = points[index + 1]\n if (!from || !to) return { position: ZERO, rotation: ZERO }\n const fraction = to.t > from.t ? (time - from.t) / (to.t - from.t) : 0\n return { position: mix(from.position, to.position, fraction), rotation: mix(from.rotation, to.rotation, fraction) }\n}\n\n/** Problems with a clip set (empty when valid): each key is the clip id (or `<clip>_<variant>`), every clip passes `checkAnimation`, and a `stage` event names its stage. */\nexport const checkToolClips = (clips: ToolClipSet, partIds: Iterable<string>): { ok: boolean; problems: string[] } => {\n const parts = [...partIds]\n const problems: string[] = []\n for (const [key, clip] of Object.entries(clips)) {\n if (clip.id !== key) problems.push(`clip \"${key}\" has the id \"${clip.id}\"`)\n if (!(clip.duration > 0)) problems.push(`clip \"${key}\" has no duration`)\n problems.push(...checkAnimation(clip, parts).problems)\n for (const event of clip.events ?? []) if (event.type === STAGE_EVENT && typeof event.data?.stage !== 'string') problems.push(`clip \"${key}\" has a stage event without data.stage`)\n }\n return { ok: problems.length === 0, problems }\n}\n\ninterface Instance {\n readonly name: string\n readonly clip: AnimationClipSpec\n time: number\n delay: number\n fired: number\n}\n\nexport interface ToolClipPlayerOptions {\n clips: ToolClipSet\n rig: ToolRig\n /** Called for each clip event as its time passes (`eject_casing`, `drop_magazine`, `stage`...). */\n onEvent?(event: AnimationEventSpec, clip: string): void\n /** Seconds between the rounds of a `repeat` (a burst): default 0.1. */\n repeatInterval?(): number\n}\n\n/** The default player of a tool's clips: runs the steps of `toolAnimationStep` against a rig, scaled to the duration the rule gives. */\nexport interface ToolClipPlayer {\n apply(step: ToolAnimationStep): void\n update(dt: number): void\n readonly playing: readonly string[]\n /** The clips being played with the time each has reached and the clip itself (scaled to the duration the rule gave), for a renderer that has to follow what the clip does now (the hands working a part). */\n readonly running: ReadonlyArray<{ readonly name: string; readonly time: number; readonly clip: AnimationClipSpec }>\n}\n\n/**\n * The clip with every event that has a `lead` moved that many seconds earlier, the lead it got in `data.lead`. A led event never goes before 0\n * nor before the event written ahead of it (a fast clip keeps its order: the lead shrinks). Run after `scaleClip`: the lead is real time, the\n * clip's stretch does not touch it.\n */\nexport const leadClipEvents = (clip: AnimationClipSpec): AnimationClipSpec => {\n const events = clip.events ?? []\n if (!events.some((event) => (event.lead ?? 0) > 0)) return clip\n let floor = 0\n const led = events.map((event) => {\n const lead = Math.max(0, Math.min(event.t - floor, event.lead ?? 0))\n const t = event.t - lead\n floor = Math.max(floor, t)\n if (event.lead === undefined) return event\n const moved: AnimationEventSpec = { ...event, t, data: lead > 0 ? { ...event.data, lead } : { ...event.data } }\n delete moved.lead\n return moved\n })\n return { ...clip, events: led }\n}\n\nexport const createToolClipPlayer = (options: ToolClipPlayerOptions): ToolClipPlayer => {\n const { clips, rig, onEvent } = options\n let instances: Instance[] = []\n\n const find = (clip: string, variant?: string): { name: string; clip: AnimationClipSpec } | undefined => {\n const name = variant !== undefined && clips[`${clip}_${variant}`] ? `${clip}_${variant}` : clip\n const found = clips[name]\n return found ? { name, clip: found } : undefined\n }\n\n const render = (instance: Instance): void => {\n const time = Math.min(instance.time, instance.clip.duration)\n const root = sampleClip(instance.clip, undefined, time)\n if (instance.clip.keys.some((key) => key.position || key.rotation)) rig.setRootPose?.(root)\n const touched = new Set(instance.clip.keys.flatMap((key) => Object.keys(key.parts ?? {})))\n for (const id of touched) rig.setPartPose(id, sampleClip(instance.clip, id, time))\n }\n\n const fireEvents = (instance: Instance, upTo: number): void => {\n const events = instance.clip.events ?? []\n while (instance.fired < events.length && (events[instance.fired]?.t ?? Infinity) <= upTo + 1e-9) {\n const event = events[instance.fired]\n instance.fired += 1\n if (event) onEvent?.(event, instance.name)\n }\n }\n\n return {\n apply(step) {\n if (step.type === 'sync') {\n for (const instance of instances) {\n const marker = (instance.clip.events ?? []).find((event) => event.type === STAGE_EVENT && event.data?.stage === step.stage)\n if (marker && instance.time < marker.t) instance.time = marker.t\n }\n return\n }\n if (step.type === 'finish') {\n for (const instance of instances) {\n if (instance.name !== step.clip && !instance.name.startsWith(`${step.clip}_`)) continue\n fireEvents(instance, instance.clip.duration)\n instance.time = instance.clip.duration\n }\n return\n }\n const found = find(step.clip, step.variant)\n if (!found) return\n const clip = leadClipEvents(step.duration && step.duration > 0 ? scaleClip(found.clip, step.duration) : found.clip)\n const interval = options.repeatInterval?.() ?? 0.1\n instances = instances.filter((existing) => existing.name !== found.name || existing.delay > 0)\n for (let round = 0; round < (step.repeat ?? 1); round++) {\n instances.push({ name: found.name, clip, time: 0, delay: round * interval, fired: 0 })\n }\n },\n update(dt) {\n for (const instance of instances) {\n const carried = instance.delay > 0 ? dt - instance.delay : dt\n if (instance.delay > 0) {\n instance.delay = Math.max(0, instance.delay - dt)\n if (instance.delay > 0) continue\n }\n instance.time += carried\n fireEvents(instance, instance.time)\n render(instance)\n }\n instances = instances.filter((instance) => instance.delay > 0 || instance.time < instance.clip.duration)\n },\n get playing() {\n return instances.map((instance) => instance.name)\n },\n get running() {\n return instances.filter((instance) => instance.delay <= 0).map((instance) => ({ name: instance.name, time: instance.time, clip: instance.clip }))\n },\n }\n}\n","export const FRAME_PHASES = ['input', 'fixedSimulate', 'lateSimulate', 'present', 'render', 'idle'] as const\nexport type FramePhase = (typeof FRAME_PHASES)[number]\n\n/**\n * Presentation work a system leaves for later frames, run in small units within a shared time budget.\n * Never simulation: nothing that consumes a seeded generator or decides an outcome may wait in a queue.\n */\nexport interface DeferredQueue {\n /** Stable name for stats and logs (see `DEFERRED_QUEUE_ID`). */\n readonly id: string\n /**\n * Units waiting. 0 means idle; the budget skips the queue without calling `run`. `frame` is the budget's frame counter: a queue may\n * report 0 for the rest of a frame (the crease queue does in the frame a panel was torn). Same value for repeated calls in one frame.\n */\n pending(frame?: number): number\n /**\n * Runs units in order until `clock() >= deadline`. Contract: when `pending() > 0` it runs **at least one** unit, even with a deadline\n * already past; it never runs a unit after it saw the deadline pass. Returns how many units it ran.\n */\n run(deadline: number, clock: () => number): number\n /** Runs everything now (synchronous step, clear, dispose). */\n flush(): void\n /**\n * Called by `budget.register`: from now on the owner stops draining on its own (no MessageChannel, no drain inside `update`).\n * Never called by a getter; a queue nobody registered keeps today's internal draining.\n */\n attach?(): void\n /** Called by the unregister function and by `budget.dispose`: the owner goes back to its internal draining. */\n detach?(): void\n}\n\n/** Where a module registers a deferred queue with the host's budget. */\nexport interface DeferredQueueOptions {\n /** Higher runs first. See DEFERRED_PRIORITY. */\n readonly priority: number\n /** `present` runs before the render (the work must show this frame); `idle` runs after it. Default `idle`. */\n readonly phase?: 'present' | 'idle'\n /** Starvation guard: the queue runs at least one unit every this many frames, even with the budget spent. Default 4. */\n readonly minEveryFrames?: number\n /** Milliseconds of every frame kept for this queue before the others share the rest (when it has work). Default 0. */\n readonly reserveMs?: number\n}\n\n/** The ids of the kit's queues: what `DeferredQueue.id` says and what `DEFERRED_PRIORITY` is keyed by. */\nexport const DEFERRED_QUEUE_ID = {\n DESTRUCTION_EVENTS: 'destruction.events',\n BREACH_PAINT: 'floorplan.breach-paint',\n AUDIO_VOICES: 'audio.voices',\n VEHICLE_CREASE: 'vehicles.crease',\n} as const\n\n/** Default priorities and reserves of the kit's queues; a host may override any of them. */\nexport const DEFERRED_PRIORITY = {\n [DEFERRED_QUEUE_ID.DESTRUCTION_EVENTS]: { priority: 300, phase: 'present' },\n [DEFERRED_QUEUE_ID.BREACH_PAINT]: { priority: 250, phase: 'present' },\n [DEFERRED_QUEUE_ID.AUDIO_VOICES]: { priority: 200, phase: 'idle', reserveMs: 1 },\n [DEFERRED_QUEUE_ID.VEHICLE_CREASE]: { priority: 100, phase: 'idle' },\n} as const satisfies Readonly<Record<string, DeferredQueueOptions>>\n\nexport interface DeferredQueueReport {\n readonly ok: boolean\n readonly problems: readonly string[]\n}\n\n/**\n * Checks the `DeferredQueue` contract. `fill` leaves the queue with at least two units waiting (the same call is made on a fresh queue).\n * It cannot prove the queue only carries presentation work: that is a rule of review.\n */\nexport function checkDeferredQueue(queue: DeferredQueue, fill: () => void): DeferredQueueReport {\n const problems: string[] = []\n fill()\n const waiting = queue.pending(1)\n if (waiting < 2) problems.push(`fill() must leave at least 2 units waiting (pending is ${waiting})`)\n if (queue.pending(1) !== waiting) problems.push('pending(frame) changed between two calls in the same frame')\n const ran = queue.run(0, () => 1)\n if (ran !== 1) problems.push(`run with a deadline already past must run exactly one unit (ran ${ran})`)\n if (waiting >= 2 && queue.pending(2) !== waiting - 1) problems.push(`one unit ran but pending went from ${waiting} to ${queue.pending(2)}`)\n try {\n queue.attach?.()\n queue.attach?.()\n queue.detach?.()\n queue.detach?.()\n } catch (error) {\n problems.push(`attach/detach must be idempotent: ${error instanceof Error ? error.message : String(error)}`)\n }\n queue.flush()\n if (queue.pending(3) !== 0) problems.push(`flush must leave nothing pending (pending is ${queue.pending(3)})`)\n if (queue.run(0, () => 1) !== 0) problems.push('run on an empty queue must run no unit')\n return { ok: problems.length === 0, problems }\n}\n","const WARNED_KEY = Symbol.for('@adatechnology/game-contracts.warned')\n\ntype WarnedHolder = { [WARNED_KEY]?: Set<string> }\n\nconst warnedKeys = (): Set<string> => {\n const holder = globalThis as WarnedHolder\n return (holder[WARNED_KEY] ??= new Set<string>())\n}\n\ndeclare const process: { readonly env: { readonly NODE_ENV?: string } }\n\n// The literal `process.env.NODE_ENV` is what bundlers replace; where there is no `process` the reference throws and counts as development.\nconst isProduction = (): boolean => {\n try {\n return process.env.NODE_ENV === 'production'\n } catch {\n return false\n }\n}\n\nconst isQuiet = (): boolean => (globalThis as { __GAME_KIT_QUIET__?: unknown }).__GAME_KIT_QUIET__ === true\n\n/**\n * Warns on the console the first time `key` is seen, and only outside production and when `globalThis.__GAME_KIT_QUIET__` is not `true`: for a silent fallback that keeps working (an adapter that made its own effects\n * world or audio engine because the host lent none). The seen keys live on `globalThis`, so several bundles of the kit on one page warn once together.\n */\nexport const warnOnce = (key: string, message: string): void => {\n if (isQuiet() || isProduction()) return\n const seen = warnedKeys()\n if (seen.has(key)) return\n seen.add(key)\n console.warn(`[game-kit] ${message}`)\n}\n\n/** Forgets what `warnOnce` has seen (tests that count warnings). */\nexport const resetWarnOnce = (): void => warnedKeys().clear()\n","import { warnOnce } from './warn'\n\ntype PlainObject = Record<string, unknown>\ntype Entry = { readonly id: string }\n\n/** Keys that would reach the prototype of the result when a patch comes from parsed JSON. */\nconst UNSAFE_KEYS: ReadonlySet<string> = new Set(['__proto__', 'constructor', 'prototype'])\n\nconst isPlainObject = (value: unknown): value is PlainObject =>\n typeof value === 'object' && value !== null && !Array.isArray(value) && Object.getPrototypeOf(value) === Object.prototype\n\n/** Recursively optional; arrays and functions are replaced as a whole, objects are merged. */\nexport type DeepPartial<TValue> = TValue extends (...args: never[]) => unknown\n ? TValue\n : TValue extends readonly unknown[]\n ? TValue\n : TValue extends object\n ? { [TKey in keyof TValue]?: DeepPartial<TValue[TKey]> }\n : TValue\n\n/** Merges `patch` over `base`: plain objects merge field by field, arrays, functions and primitives replace. `undefined` in the patch is ignored. */\nexport const deepMerge = <TBase extends object>(base: TBase, patch: object | undefined): TBase => {\n if (!patch) return base\n const result: PlainObject = { ...(base as PlainObject) }\n for (const [key, value] of Object.entries(patch)) {\n if (value === undefined || UNSAFE_KEYS.has(key)) continue\n const current = result[key]\n result[key] = isPlainObject(current) && isPlainObject(value) ? deepMerge(current, value) : value\n }\n return result as TBase\n}\n\n/** The shipped entries with the ones from `extra` added or, when the id exists, replaced. */\nexport const mergeById = <TEntry extends Entry>(shipped: readonly TEntry[], extra: readonly TEntry[] | undefined): Map<string, TEntry> => {\n const entries = new Map<string, TEntry>()\n for (const entry of [...shipped, ...(extra ?? [])]) entries.set(entry.id, entry)\n return entries\n}\n\n/** Applies `patch` by id; a patch for an id that does not exist is ignored (and warned about once, in development). */\nexport const applyPatches = <TEntry extends Entry>(entries: Map<string, TEntry>, patches: Readonly<Record<string, DeepPartial<TEntry> | Partial<TEntry>>> | undefined): void => {\n for (const [id, patch] of Object.entries(patches ?? {})) {\n const entry = entries.get(id)\n if (entry) entries.set(id, deepMerge(entry, patch))\n else warnOnce(`registry.patch.${id}`, `patch for \"${id}\" ignored: no entry has that id`)\n }\n}\n\n/** Takes the ids out; an id that does not exist is ignored (and warned about once, in development). */\nexport const removeIds = (entries: Map<string, unknown>, ids: readonly string[] | undefined): void => {\n for (const id of ids ?? []) {\n if (!entries.delete(id)) warnOnce(`registry.remove.${id}`, `remove of \"${id}\" ignored: no entry has that id`)\n }\n}\n","import type { Vec3 } from './destruction'\n\n/**\n * The bench of a module: controls, layers and counters it declares as data. The host keeps the values, draws them in a generic way and hands\n * every change back through `ModuleHost.bench`. Nothing here is specific to a kind of module.\n */\nexport type ControlValue = number | boolean | string\n\nexport interface ChoiceOption {\n readonly id: string\n readonly name: string\n /** Heading the host lists the option under (`optgroup`, a grouped row of buttons). Options of a group must sit next to each other. */\n readonly group?: string\n}\n\ninterface ControlBase {\n /** snake_case, unique across the module's controls, layers and metrics. */\n readonly id: string\n /** Display name, already translated for the locale the host asked for. */\n readonly name: string\n readonly hint?: string\n readonly hidden?: boolean\n /**\n * The change (or the press, for an action) takes a noticeable time, a third of a second or more: the host shows its loading indicator while it works, with this text, or the control's name for `true`.\n * Only the host's own controls panel does it; a change made through `TestRoom.bench().set / act` runs at once.\n */\n readonly slow?: boolean | string\n}\n\nexport interface NumberControl extends ControlBase {\n readonly kind: 'number'\n readonly min: number\n readonly max: number\n readonly step?: number\n readonly unit?: string\n readonly default: number\n}\n\nexport interface BooleanControl extends ControlBase {\n readonly kind: 'boolean'\n readonly default: boolean\n}\n\nexport interface ChoiceControl extends ControlBase {\n readonly kind: 'choice'\n readonly options: readonly ChoiceOption[]\n readonly default: string\n}\n\n/** A button: no value, only the press. */\nexport interface ActionControl extends ControlBase {\n readonly kind: 'action'\n}\n\nexport type ModuleControl = NumberControl | BooleanControl | ChoiceControl | ActionControl\n\n/** Something of the module the viewer turns on or off (smoke, flash, casings). */\nexport interface ModuleLayer {\n readonly id: string\n readonly name: string\n /** On unless `false`. */\n readonly default?: boolean\n}\n\n/** Pulled: the room calls read only when it samples (~4 Hz, or on TestRoom.metrics()). */\nexport interface ModuleMetric {\n readonly id: string\n readonly name: string\n readonly unit?: string\n /** Budget: the host shows a bar and warns past it. */\n readonly max?: number\n readonly digits?: number\n read(): number | string\n}\n\nexport type BenchChange =\n | { readonly kind: 'control'; readonly id: string; readonly value: ControlValue }\n | { readonly kind: 'layer'; readonly id: string; readonly on: boolean }\n | { readonly kind: 'action'; readonly id: string }\n\nexport interface ControlPatch {\n readonly hidden?: boolean\n readonly disabled?: boolean\n readonly options?: readonly ChoiceOption[]\n readonly value?: ControlValue\n}\n\n/** What the host lends a module that declares a bench: the current values and the changes. */\nexport interface ModuleBench {\n value(id: string): ControlValue | undefined\n layer(id: string): boolean\n /** Right after attach returns, the host sends one change per control (not actions) and per layer, in declaration order. */\n onChange(listener: (change: BenchChange) => void): () => void\n /** `value` sets without emitting (no echo loops); `options` falls back to the first option if the value is no longer offered. */\n patch(id: string, patch: ControlPatch): void\n /** Move the host camera (a near / middle / far choice). */\n look(eye: Vec3, target: Vec3): void\n /** Shows a sheet on the host's panel, or takes it down with `null`. A host without a sheet area ignores it. */\n sheet?(sheet: BenchSheet | null): void\n}\n\n/**\n * A card of text the module puts on the host's panel (the sheet of an effect, of a weapon): what it is, its numbers, its steps, its colours and,\n * optionally, a strip of segments over time. Names and texts are already translated for the host's locale.\n */\nexport interface BenchSheet {\n readonly kicker?: string\n readonly title: string\n readonly body?: string\n readonly facts?: readonly { readonly label: string; readonly value: string }[]\n readonly steps?: readonly { readonly at: string; readonly text: string }[]\n readonly swatches?: readonly { readonly color: string; readonly name: string }[]\n readonly note?: string\n /** A strip of named segments (seconds), with tick marks and a playhead that follows the module metric `metric`. */\n readonly timeline?: {\n readonly segments: readonly { readonly name: string; readonly start: number; readonly end: number; readonly color: string }[]\n readonly ticks: readonly number[]\n readonly metric: string\n }\n}\n\nexport interface BenchReport {\n readonly ok: boolean\n readonly problems: readonly string[]\n}\n\nexport interface BenchDeclaration {\n readonly controls?: readonly ModuleControl[]\n readonly layers?: readonly ModuleLayer[]\n readonly metrics?: readonly ModuleMetric[]\n}\n\nconst SNAKE_CASE = /^[a-z][a-z0-9_]*$/\nconst finite = (n: unknown): n is number => typeof n === 'number' && Number.isFinite(n)\n\nfunction checkControl(control: ModuleControl, problems: string[]): void {\n const label = `control \"${control.id}\"`\n if (control.kind === 'number') {\n if (!finite(control.min) || !finite(control.max) || control.min >= control.max) problems.push(`${label} needs min < max`)\n if (control.step !== undefined && !(finite(control.step) && control.step > 0)) problems.push(`${label} needs a step > 0`)\n if (!finite(control.default) || control.default < control.min || control.default > control.max) problems.push(`${label} default ${String(control.default)} is outside ${control.min}..${control.max}`)\n return\n }\n if (control.kind !== 'choice') return\n if (control.options.length === 0) {\n problems.push(`${label} needs at least one option`)\n return\n }\n const seen = new Set<string>()\n const groups = new Set<string>()\n let previousGroup: string | undefined\n for (const option of control.options) {\n if (seen.has(option.id)) problems.push(`${label} repeats option \"${option.id}\"`)\n seen.add(option.id)\n if (option.group !== undefined && (typeof option.group !== 'string' || option.group === '')) problems.push(`${label} option \"${option.id}\" has an empty group`)\n else if (option.group !== undefined && option.group !== previousGroup && groups.has(option.group)) problems.push(`${label} group \"${option.group}\" is split: its options must be next to each other`)\n if (typeof option.group === 'string' && option.group !== '') groups.add(option.group)\n previousGroup = option.group\n }\n if (!seen.has(control.default)) problems.push(`${label} default \"${control.default}\" is not among its options`)\n}\n\n/** snake_case ids unique across controls, layers and metrics; min < max; step > 0; default in range; options non-empty, unique, default among them, each group in one run; metric max > 0. */\nexport function checkBench(declared: BenchDeclaration): BenchReport {\n const problems: string[] = []\n const ids = new Set<string>()\n const claim = (kind: string, id: string): void => {\n if (!SNAKE_CASE.test(id)) problems.push(`${kind} id \"${id}\" must be snake_case`)\n if (ids.has(id)) problems.push(`duplicate id \"${id}\" across controls, layers and metrics`)\n ids.add(id)\n }\n for (const control of declared.controls ?? []) {\n claim('control', control.id)\n checkControl(control, problems)\n }\n for (const layer of declared.layers ?? []) claim('layer', layer.id)\n for (const metric of declared.metrics ?? []) {\n claim('metric', metric.id)\n if (metric.max !== undefined && !(finite(metric.max) && metric.max > 0)) problems.push(`metric \"${metric.id}\" needs a max > 0`)\n }\n return { ok: problems.length === 0, problems }\n}\n","/** The weather, light and ground as the world is right now. Written by whoever drives the weather, read by whoever reacts to it. */\nexport interface EnvironmentState {\n /** Rain in mm/h; 0 is dry. */\n readonly rain: number\n /** Wind in m/s along x and z, and its strength (the length of that vector). */\n readonly windX: number\n readonly windZ: number\n readonly windSpeed: number\n /** Strongest gust in m/s. */\n readonly gust: number\n /** Render fog far distance in metres. */\n readonly fogFar: number\n /** How far one can see for gameplay, in metres (not the render fog). */\n readonly visibility: number\n /** 0 is full daylight, 1 is the darkest night. */\n readonly nightFactor: number\n readonly temperatureC: number\n /** Wet ground, 0..1: rises with rain and dries after it stops. */\n readonly wetness: number\n}\n\nexport type EnvironmentKey = keyof EnvironmentState\n\n/** A calm dry day (the normal world, before anything went wrong). */\nexport const DEFAULT_ENVIRONMENT: EnvironmentState = {\n rain: 0,\n windX: 0,\n windZ: 0,\n windSpeed: 0,\n gust: 0,\n fogFar: 220,\n visibility: 220,\n nightFactor: 0,\n temperatureC: 20,\n wetness: 0,\n}\n\n/** The host's environment (`ModuleHost.environment`): a store the weather module writes to and the others read. */\nexport interface EnvironmentBus {\n get(): EnvironmentState\n /** Merges `patch` into the state; listeners hear it only when a value really changed. */\n set(patch: Partial<EnvironmentState>): void\n /** Hears every change with the keys that changed. Returns the unsubscribe function. */\n on(listener: (state: EnvironmentState, changed: readonly EnvironmentKey[]) => void): () => void\n}\n\n/** A plain store that implements the bus; the test room and tests use it, a game may bring its own. */\nexport function createEnvironmentBus(initial: Partial<EnvironmentState> = {}): EnvironmentBus {\n let state: EnvironmentState = { ...DEFAULT_ENVIRONMENT, ...initial }\n const listeners = new Set<(state: EnvironmentState, changed: readonly EnvironmentKey[]) => void>()\n return {\n get: () => state,\n set(patch) {\n const changed = (Object.keys(patch) as EnvironmentKey[]).filter((key) => patch[key] !== undefined && patch[key] !== state[key])\n if (changed.length === 0) return\n const next = { ...state } as { -readonly [Key in EnvironmentKey]: number }\n for (const key of changed) next[key] = patch[key] as number\n state = next\n for (const listener of [...listeners]) listener(state, changed)\n },\n on(listener) {\n listeners.add(listener)\n return () => void listeners.delete(listener)\n },\n }\n}\n","import { applyPatches, deepMerge, mergeById, removeIds } from './registry'\nimport type { DeepPartial } from './registry'\n\n/** One category (kind) of items. Defaults and required contracts are inherited from the `extends` chain. */\nexport interface CategoryDef<TDefaults extends object = Record<string, unknown>> {\n readonly id: string\n /** Parent category id; the child inherits its defaults and its required contracts. */\n readonly extends?: string\n /** Field values every entry of this kind starts with; objects merge down the chain, arrays replace. */\n readonly defaults?: DeepPartial<TDefaults>\n /** Contract ids (`ContractChecker.id`) every entry of this kind, or of a descendant, must satisfy. */\n readonly requires?: readonly string[]\n /** An abstract category only groups: entries cannot declare it directly, only its descendants. */\n readonly abstract?: boolean\n}\n\n/** Where a checker is being run: the entry's kind and the chain of categories root first. */\nexport interface ContractContext {\n readonly kind: string\n readonly chain: readonly string[]\n}\n\n/** A named contract: returns the problems of an entry, each a short phrase like `missing leftover.despawnSeconds`. Empty means it holds. */\nexport interface ContractChecker<TEntry> {\n readonly id: string\n check(entry: TEntry, ctx: ContractContext): string[]\n}\n\nexport interface CategoryOptions {\n /** Categories added to the shipped ones; the same id replaces. */\n readonly extra?: readonly CategoryDef[]\n /** Partial changes by id, applied after `extra`. */\n readonly patches?: Readonly<Record<string, DeepPartial<CategoryDef> | Partial<CategoryDef>>>\n /** Ids taken out of the tree (their children are then reported by `check()`). */\n readonly remove?: readonly string[]\n}\n\nexport interface CategoryRegistry {\n get(id: string): CategoryDef | undefined\n has(id: string): boolean\n ids(): string[]\n /** Root first, ending at `id`. Throws on an unknown id, an unknown parent or a cycle. */\n chain(id: string): string[]\n /** `defaults` merged along the chain, root first, so the closest category wins. */\n defaults(id: string): Record<string, unknown>\n /** Union of `requires` along the chain, order kept, without duplicates. */\n requires(id: string): string[]\n /** True when `id` is `ancestorId` or descends from it. */\n isA(id: string, ancestorId: string): boolean\n /** Problems in the tree itself: duplicate ids, unknown `extends`, cycles. */\n check(): string[]\n}\n\nexport interface CategorizedOptions {\n /** Field of the entry that names its category. Default `kind`. */\n readonly kindField?: string\n /** Known gaps by entry id: problems starting with one of these texts go to `allowed`, so the list can shrink over time. */\n readonly allow?: Readonly<Record<string, readonly string[]>>\n}\n\nexport interface CategorizedReport {\n readonly ok: boolean\n readonly problems: string[]\n readonly allowed: string[]\n}\n\nexport const defineCategories = (defs: readonly CategoryDef[], options: CategoryOptions = {}): CategoryRegistry => {\n const problems: string[] = []\n const duplicates = (list: readonly CategoryDef[]): void => {\n const ids = new Set<string>()\n for (const def of list) {\n if (ids.has(def.id)) problems.push(`duplicate category \"${def.id}\"`)\n ids.add(def.id)\n }\n }\n duplicates(defs)\n duplicates(options.extra ?? [])\n\n const entries = mergeById(defs, options.extra)\n applyPatches(entries, options.patches)\n removeIds(entries, options.remove)\n\n const chain = (id: string): string[] => {\n const path: string[] = []\n let current: string | undefined = id\n while (current !== undefined) {\n const def = entries.get(current)\n if (!def) throw new Error(path.length === 0 ? `unknown category \"${current}\"` : `category \"${path[0]}\" has unknown parent \"${current}\"`)\n if (path.includes(current)) throw new Error(`category cycle: ${[...path, current].reverse().join(' -> ')}`)\n path.push(current)\n current = def.extends\n }\n return path.reverse()\n }\n\n const registry: CategoryRegistry = {\n get: (id) => entries.get(id),\n has: (id) => entries.has(id),\n ids: () => [...entries.keys()],\n chain,\n defaults: (id) => chain(id).reduce<Record<string, unknown>>((acc, ancestor) => deepMerge(acc, entries.get(ancestor)?.defaults), {}),\n requires: (id) => {\n const out: string[] = []\n for (const ancestor of chain(id)) for (const contract of entries.get(ancestor)?.requires ?? []) if (!out.includes(contract)) out.push(contract)\n return out\n },\n isA: (id, ancestorId) => {\n try {\n return chain(id).includes(ancestorId)\n } catch {\n return false\n }\n },\n check: () => {\n const found = [...problems]\n for (const [id, def] of entries) {\n if (def.extends !== undefined && !entries.has(def.extends)) found.push(`category \"${id}\" extends unknown \"${def.extends}\"`)\n else {\n try {\n chain(id)\n } catch (error) {\n const message = (error as Error).message\n // The same cycle is found from each of its members; the dedupe keeps one line per distinct message.\n if (message.startsWith('category cycle') && !found.includes(message)) found.push(message)\n }\n }\n }\n return found\n },\n }\n return registry\n}\n\n/** The entry with the defaults of its category underneath: the entry wins, objects merge, arrays replace. */\nexport const resolveEntry = <TEntry extends object>(registry: CategoryRegistry, entry: TEntry, kindField = 'kind'): TEntry =>\n deepMerge(registry.defaults(String((entry as Record<string, unknown>)[kindField])) as TEntry, entry)\n\n/** Checks that each entry has a known, concrete kind and satisfies every contract its chain requires. */\nexport const checkCategorized = <TEntry extends { readonly id: string }>(\n entries: readonly TEntry[],\n registry: CategoryRegistry,\n checkers: readonly ContractChecker<TEntry>[],\n options: CategorizedOptions = {},\n): CategorizedReport => {\n const kindField = options.kindField ?? 'kind'\n const byId = new Map(checkers.map((checker) => [checker.id, checker]))\n const problems: string[] = [...registry.check()]\n const allowed: string[] = []\n\n for (const entry of entries) {\n const kind = String((entry as Record<string, unknown>)[kindField])\n if (!registry.has(kind)) {\n problems.push(`\"${entry.id}\" has unknown kind \"${kind}\"`)\n continue\n }\n if (registry.get(kind)?.abstract) {\n problems.push(`\"${entry.id}\" has abstract kind \"${kind}\"`)\n continue\n }\n let path: string[]\n try {\n path = registry.chain(kind)\n } catch (error) {\n problems.push(`\"${entry.id}\" (${kind}): ${(error as Error).message}`)\n continue\n }\n const resolved = resolveEntry(registry, entry, kindField)\n for (const contract of registry.requires(kind)) {\n const owner = path.find((ancestor) => registry.get(ancestor)?.requires?.includes(contract)) ?? kind\n const checker = byId.get(contract)\n if (!checker) {\n problems.push(`kind \"${kind}\" requires contract \"${contract}\" but no checker is registered`)\n continue\n }\n const prefix = `\"${entry.id}\" (${kind}): `\n for (const problem of checker.check(resolved, { kind, chain: path })) {\n const text = `${prefix}${problem}, required by ${owner}`\n if ((options.allow?.[entry.id] ?? []).some((start) => problem.startsWith(start))) allowed.push(text)\n else problems.push(text)\n }\n }\n }\n return { ok: problems.length === 0, problems: [...new Set(problems)], allowed }\n}\n","/**\n * Where a drawn object comes from. Every builder of the kit that draws a piece (a car of the fleet, a house or a wall, a piece of furniture, a person, an\n * animal, a plant, an item, a weapon) marks the root it returns, and every renderer that draws for the room (effects, debris, liquids, the room itself)\n * marks its own roots, so a host can tell a piece of the kit from something drawn on the spot. Duck-typed on three.js objects (`userData`, `parent`,\n * `children`), so the contracts stay free of three.js.\n */\nexport const KIT_ORIGIN_KEY = 'kitOrigin'\n\n/** What drew an object: the package and the kind of piece (`vehicle`, `structure`, `furniture`, `person`... or `effect`, `room` for what is not a piece). */\nexport interface KitOrigin {\n readonly owner: string\n readonly kind: string\n /** The catalogue id of the piece, when it has one. */\n readonly id?: string\n /** Drawn as a simple stand-in shape because the kit has no model of the piece yet (an item with no team model): a model still to make. */\n readonly standIn?: boolean\n}\n\ninterface Node {\n userData?: Record<string, unknown>\n parent?: Node | null\n children?: readonly Node[]\n name?: string\n visible?: boolean\n isMesh?: boolean\n isPoints?: boolean\n isLine?: boolean\n isSprite?: boolean\n}\n\n/** Marks `object` as drawn by `origin.owner` and returns it. */\nexport function markKitOrigin<T>(object: T, origin: KitOrigin): T {\n const node = object as unknown as Node\n node.userData = node.userData ?? {}\n node.userData[KIT_ORIGIN_KEY] = { ...origin }\n return object\n}\n\n/** The origin of the object or of its nearest marked ancestor; null when nothing up the tree is marked. */\nexport function kitOriginOf(object: unknown): KitOrigin | null {\n for (let node = object as Node | null | undefined; node; node = node.parent) {\n const origin = node.userData?.[KIT_ORIGIN_KEY] as KitOrigin | undefined\n if (origin && typeof origin.owner === 'string') return origin\n }\n return null\n}\n\n/** A drawn thing that no builder of the kit marked: what was drawn on the spot. */\nexport interface UnmarkedDrawing {\n /** The names of the object and its named ancestors, outermost first. */\n readonly path: string\n readonly object: unknown\n}\n\n/** Every visible mesh, line, point cloud or sprite under `root` with no marked ancestor (`root` itself not counted as one). */\nexport function unmarkedDrawings(root: unknown): UnmarkedDrawing[] {\n const out: UnmarkedDrawing[] = []\n const pathOf = (node: Node): string => {\n const names: string[] = []\n for (let n: Node | null | undefined = node; n && n !== root; n = n.parent) if (n.name) names.push(n.name)\n return names.reverse().join(' > ') || '(no name)'\n }\n const walk = (node: Node, marked: boolean): void => {\n if (node.visible === false) return\n const here = marked || (node !== root && Boolean((node.userData?.[KIT_ORIGIN_KEY] as KitOrigin | undefined)?.owner))\n if (!here && (node.isMesh || node.isPoints || node.isLine || node.isSprite)) out.push({ path: pathOf(node), object: node })\n for (const child of node.children ?? []) walk(child, here)\n }\n walk(root as Node, false)\n return out\n}\n","/** A floor rectangle in an object's own space, metres (x across, z along). */\nexport interface Footprint {\n readonly minX: number\n readonly maxX: number\n readonly minZ: number\n readonly maxZ: number\n}\n\n/** What `footprintBlocker` reads of an object: its world matrix (three.js `Object3D`, duck-typed so the contracts stay free of three.js). */\nexport interface FootprintObject {\n readonly matrixWorld: { readonly elements: ArrayLike<number> }\n readonly parent?: unknown\n updateWorldMatrix?(updateParents: boolean, updateChildren: boolean): void\n}\n\n/**\n * The `ModuleHost.addBlocker` function of a solid object standing on the floor: the walking player stays out of `footprint`, a rectangle in the object's own space,\n * so the object blocks turned with it and follows it when it moves. An object out of the scene (no parent) blocks nothing.\n */\nexport function footprintBlocker(object: FootprintObject | (() => FootprintObject | null | undefined), footprint: Footprint | (() => Footprint | null | undefined)): (x: number, z: number, radius: number) => boolean {\n return (x, z, radius) => {\n const o = typeof object === 'function' ? object() : object\n const fp = typeof footprint === 'function' ? footprint() : footprint\n if (!o || !fp || !o.parent) return false\n o.updateWorldMatrix?.(true, false)\n const e = o.matrixWorld.elements\n // the world point in the object's own floor axes: its x and z columns are at right angles, so a projection on each (over its squared length, for a scale) inverts the matrix\n const dx = x - (e[12] as number)\n const dz = z - (e[14] as number)\n const ax = (e[0] as number) ** 2 + (e[1] as number) ** 2 + (e[2] as number) ** 2\n const az = (e[8] as number) ** 2 + (e[9] as number) ** 2 + (e[10] as number) ** 2\n const lx = (dx * (e[0] as number) + dz * (e[2] as number)) / (ax || 1)\n const lz = (dx * (e[8] as number) + dz * (e[10] as number)) / (az || 1)\n return lx > fp.minX - radius && lx < fp.maxX + radius && lz > fp.minZ - radius && lz < fp.maxZ + radius\n }\n}\n","/**\n * The sounds of a body (footsteps, jumps, landings, blows, hurt), as ids every package agrees on: the rule that knows what the body did\n * (`game-player`, a host's own player) names the sound, and the bank that knows how it sounds (`game-audio`'s `BODY_FOLEY_BANK`) answers it.\n * A game swaps the sound of an id (a sample instead of the synthesis) without touching the rule.\n */\n\n/** What the feet touch, by how it sounds (not by what it is made of: a wooden floor and a plywood board both sound `wood`). */\nexport const SURFACE_IDS = ['concrete', 'asphalt', 'wood', 'tile', 'dirt', 'grass', 'gravel', 'sand', 'metal', 'water', 'glass', 'carpet'] as const\nexport type SurfaceId = (typeof SURFACE_IDS)[number]\n\n/** The surface a body stands on when nobody says (the test room's floor is a concrete slab). */\nexport const DEFAULT_SURFACE: SurfaceId = 'concrete'\n\n/** How the feet move: a run hits harder and shorter; a crouched step is the walk, quieter. */\nexport const GAIT_IDS = ['walk', 'run'] as const\nexport type GaitId = (typeof GAIT_IDS)[number]\n\n/** How hard a landing is: `light` for a jump on the spot or a step down (up to about 5 m/s), `heavy` for a drop of more than a metre and a half. */\nexport const LANDING_IDS = ['light', 'heavy'] as const\nexport type LandingId = (typeof LANDING_IDS)[number]\n\n/** The fall speed (m/s) from which a landing is `heavy`. */\nexport const HEAVY_LANDING_SPEED = 5.5\n\nexport type FootstepSoundId = `footstep_${SurfaceId}_${GaitId}`\nexport type LandSoundId = `land_${SurfaceId}_${LandingId}`\n\n/**\n * How a blow is struck, by the body part or the tool that does it. `blade` stabs or slashes, `blunt` is a club, a bat, a pipe or a hammer, `chop` an\n * axe or a machete driven down.\n */\nexport const BLOW_IDS = ['punch', 'kick', 'shove', 'blade', 'blunt', 'chop'] as const\nexport type BlowId = (typeof BLOW_IDS)[number]\n\n/** What the blow meets: a body (flesh and bone, the head apart), something else (the material answers with its own impact), or nothing. */\nexport type BlowTarget = 'body' | 'head' | 'object' | 'air'\n\n/** The sounds of the body that are not a footstep or a landing. */\nexport const BODY_SOUND_IDS = [\n 'jump',\n 'cloth_rustle',\n 'body_fall',\n 'fall_damage',\n 'player_hurt',\n 'player_breath_run',\n 'player_death',\n 'bite',\n 'grab',\n 'punch_swing',\n 'punch_hit_body',\n 'punch_hit_face',\n 'punch_hit_object',\n 'kick_swing',\n 'kick_hit',\n 'kick_hit_object',\n 'shove_two_hands',\n 'knife_swing',\n 'knife_stab',\n 'knife_slash',\n 'knife_hit_object',\n 'blunt_swing',\n 'blunt_hit',\n 'blunt_hit_object',\n 'axe_swing',\n 'axe_chop',\n 'axe_hit_object',\n] as const\nexport type BodySoundId = (typeof BODY_SOUND_IDS)[number]\n\n/** Every sound of a body: what a host plays for the player, a survivor or a zombie. */\nexport type BodyFoleyId = FootstepSoundId | LandSoundId | BodySoundId\n\nexport const footstepSoundId = (surface: SurfaceId, gait: GaitId): FootstepSoundId => `footstep_${surface}_${gait}`\nexport const landSoundId = (surface: SurfaceId, fallSpeed: number): LandSoundId => `land_${surface}_${fallSpeed >= HEAVY_LANDING_SPEED ? 'heavy' : 'light'}`\n\n/** Every footstep and landing id, in the order of the surfaces. */\nexport const FOOTSTEP_SOUND_IDS: readonly FootstepSoundId[] = SURFACE_IDS.flatMap((surface) => GAIT_IDS.map((gait) => footstepSoundId(surface, gait)))\nexport const LAND_SOUND_IDS: readonly LandSoundId[] = SURFACE_IDS.flatMap((surface) => LANDING_IDS.map((landing): LandSoundId => `land_${surface}_${landing}`))\nexport const BODY_FOLEY_IDS: readonly BodyFoleyId[] = [...FOOTSTEP_SOUND_IDS, ...LAND_SOUND_IDS, ...BODY_SOUND_IDS]\n\nconst SWING: Readonly<Record<BlowId, BodySoundId | null>> = {\n punch: 'punch_swing',\n kick: 'kick_swing',\n // two hands pushed out do not whistle: the shove is one sound, at the contact\n shove: null,\n blade: 'knife_swing',\n blunt: 'blunt_swing',\n chop: 'axe_swing',\n}\n\n/**\n * Seconds from the start of a swing's sound to its loudest point (the limb or the tool at full speed, just before the contact). A host that knows when the\n * blow lands starts the swing this long before; one that does not plays the swing now and the hit this long after.\n */\nexport const SWING_LEAD_SECONDS: Readonly<Record<BlowId, number>> = { punch: 0.07, kick: 0.09, shove: 0, blade: 0.05, blunt: 0.1, chop: 0.11 }\n\n/** The swing through the air, played as the blow starts (`null`: the blow has none). */\nexport const swingSoundId = (blow: BlowId): BodySoundId | null => SWING[blow]\n\nconst HIT: Readonly<Record<BlowId, Readonly<Record<Exclude<BlowTarget, 'air'>, BodySoundId>>>> = {\n punch: { body: 'punch_hit_body', head: 'punch_hit_face', object: 'punch_hit_object' },\n kick: { body: 'kick_hit', head: 'kick_hit', object: 'kick_hit_object' },\n shove: { body: 'shove_two_hands', head: 'shove_two_hands', object: 'shove_two_hands' },\n blade: { body: 'knife_stab', head: 'knife_stab', object: 'knife_hit_object' },\n blunt: { body: 'blunt_hit', head: 'blunt_hit', object: 'blunt_hit_object' },\n chop: { body: 'axe_chop', head: 'axe_chop', object: 'axe_hit_object' },\n}\n\n/**\n * The body's side of a blow that lands: the knuckles, the shoe, the blade or the club meeting what it hit. On an object the material's own impact\n * plays too (the wood, the metal); this is the part that says it was a punch and not a shot.\n */\nexport const hitSoundId = (blow: BlowId, target: Exclude<BlowTarget, 'air'>): BodySoundId => HIT[blow][target]\n\n/**\n * The surface a material sounds like underfoot, by the material ids of `game-destruction` and `game-terrain` (unknown ids: `fallback`). Data, so\n * a game with its own materials passes its own table to `surfaceOfMaterial`.\n */\nexport const MATERIAL_SURFACES: Readonly<Record<string, SurfaceId>> = {\n concrete: 'concrete',\n mortar: 'concrete',\n stone: 'concrete',\n brick: 'concrete',\n plaster: 'concrete',\n asphalt: 'asphalt',\n wood_soft: 'wood',\n wood_hard: 'wood',\n green_wood: 'wood',\n plywood: 'wood',\n mdf: 'wood',\n ceramic: 'tile',\n plastic: 'tile',\n soil: 'dirt',\n wax: 'dirt',\n flesh_fruit: 'dirt',\n turf: 'grass',\n foliage: 'grass',\n cactus: 'grass',\n gravel: 'gravel',\n sand: 'sand',\n steel: 'metal',\n cast_iron: 'metal',\n aluminium: 'metal',\n copper: 'metal',\n water: 'water',\n glass: 'glass',\n tempered_glass: 'glass',\n fabric: 'carpet',\n foam: 'carpet',\n leather: 'carpet',\n rubber: 'carpet',\n paper: 'carpet',\n}\n\nexport const isSurfaceId = (value: unknown): value is SurfaceId => typeof value === 'string' && (SURFACE_IDS as readonly string[]).includes(value)\n\n/** The surface of a material id; a surface id passes through. */\nexport function surfaceOfMaterial(material: string | undefined, table: Readonly<Record<string, SurfaceId>> = MATERIAL_SURFACES, fallback: SurfaceId = DEFAULT_SURFACE): SurfaceId {\n if (material === undefined) return fallback\n if (isSurfaceId(material)) return material\n return table[material] ?? fallback\n}\n","/**\n * @adatechnology/game-contracts: the contracts every game module (vehicles, furniture, plants, houses, weapons) depends on, and nothing else.\n * Types plus tiny pure checkers, no runtime dependencies. The default destruction world, the renderers and the test room are optional.\n */\nexport type {\n ChainDelay,\n Crack,\n DamageStage,\n DamageType,\n DebrisKind,\n Dent,\n DestructibleSpec,\n BlastOptions,\n BlastReport,\n ConfinementOptions,\n ExplosionChain,\n FragmentHitOptions,\n ShelterOptions,\n DestructionEvent,\n DestructionEventType,\n FluidKind,\n Fragment,\n FragmentMark,\n FractureStyle,\n BallisticExtras,\n Hit,\n HitIgnition,\n HitResult,\n Hole,\n JointKind,\n JointSpec,\n JointState,\n MaterialId,\n ObjectState,\n PartShape,\n PartSpec,\n PartBreach,\n PartState,\n PartStatus,\n PartUnits,\n Quat,\n RayHit,\n Reinforcement,\n TensionRebar,\n TunedMassDamper,\n WindExposure,\n WindState,\n PileSpec,\n SoilLayer,\n SoilProfile,\n SoilState,\n StructureRole,\n SurfaceLayer,\n ToolKind,\n Vec3,\n} from './destruction'\nexport type { BusyOptions, BusySink, BusyTask } from './busy'\nexport { blastShove, DEFAULT_BLAST_SHOVE } from './blast-shove'\nexport { bodyMass, KNOCK_CHEST, KNOCK_DOWN_SPEED, KNOCKED_DOWN_ANGLE, knockOf, stepKnock } from './blast-knock'\nexport type { Knock } from './blast-knock'\nexport type { BlastShove, BlastShoveOptions, BlastSource } from './blast-shove'\nexport { IMPACT_CONTRACT, checkDestructible, checkHit } from './impact'\nexport type { CheckOptions, DestructibleImpl, ImpactContract, SurfaceRay, SurfaceRayHit } from './impact'\nexport type { CostOptions } from './impact-cost'\nexport { MISSING_BITS } from './body'\nexport type { BodyRegion, BodySlot, SegmentKind } from './body'\nexport { checkSpec } from './spec'\nexport type { CheckSpecOptions, SpecReport } from './spec'\nexport { shotToHit, shotExtras, useToHit } from './weapons'\nexport { DEFAULT_IGNITION_RULES, fillFactor, ignitionChance, resolveIgnitionRules, sparkTargetFactor } from './ignition'\nexport type { IgnitionRules } from './ignition'\nexport type { Shot, Use } from './weapons'\nexport type { ContainerSpec, ItemUseDef, LiquidId } from './fluids'\nexport { sealBlock, sealOpener } from './seal'\nexport type { SealBlock, SealSpec, SealState } from './seal'\nexport type { HandPose, HandTargetSource, HandTargets } from './hands'\nexport { TWIST_LIFT, twistStroke } from './twist'\nexport type { TwistOptions, TwistStroke } from './twist'\nexport type { EffectHandle, EffectRequest, EffectSink, Enclosure, EnclosureBox } from './effects'\nexport type { WarmUpProbes } from './warm-up'\nexport { checkAnimation } from './animation'\nexport type { AnimationClipSpec, AnimationEventSpec, AnimationKey, AnimationPose, ItemAnimationContract } from './animation'\nexport { STAGE_EVENT, TOOL_CLIP, checkToolClips, createToolClipPlayer, leadClipEvents, sampleClip, scaleClip, toolAnimationStep } from './tool-animation'\nexport type { ToolAnimationStep, ToolAnimator, ToolClipId, ToolClipPlayer, ToolClipPlayerOptions, ToolClipSet, ToolRig } from './tool-animation'\nexport { DEFERRED_PRIORITY, DEFERRED_QUEUE_ID, FRAME_PHASES, checkDeferredQueue } from './frame'\nexport type { DeferredQueue, DeferredQueueOptions, DeferredQueueReport, FramePhase } from './frame'\nexport type { FrameOptions, AreaPlacement, GroundLayout, GroundZone, HostEvent, HostGraphics, HostTool, HostToolAttachment, ModuleArea, ModuleHost, ModuleView, ModuleItemFluid, ModuleItemSeal, ModuleScenario, PlayerState, ItemSeat, ItemSounds, TestableItem, TestableModule, Thumbnail, ToolCommand, ToolCommandResult, HostReach } from './module'\nexport { applyPatches, deepMerge, mergeById, removeIds } from './registry'\nexport type { DeepPartial } from './registry'\nimport { resetWarnOnce as resetWarnOnceForTests } from './warn'\n\nexport { warnOnce } from './warn'\n/** @deprecated Import it from `@adatechnology/game-contracts/testing`. */\nexport const resetWarnOnce = resetWarnOnceForTests\nexport { checkBench } from './bench'\nexport type {\n ActionControl,\n BenchChange,\n BenchDeclaration,\n BenchReport,\n BenchSheet,\n BooleanControl,\n ChoiceControl,\n ChoiceOption,\n ControlPatch,\n ControlValue,\n ModuleBench,\n ModuleControl,\n ModuleLayer,\n ModuleMetric,\n NumberControl,\n} from './bench'\nexport type { AudioListener, AudioSink, LoopHandle, SoundHandle, SoundRequest } from './audio'\nexport { DEFAULT_ENVIRONMENT, createEnvironmentBus } from './environment'\nexport type { EnvironmentBus, EnvironmentKey, EnvironmentState } from './environment'\nexport { checkCategorized, defineCategories, resolveEntry } from './categories'\nexport type { CategorizedOptions, CategorizedReport, CategoryDef, CategoryOptions, CategoryRegistry, ContractChecker, ContractContext } from './categories'\nexport { KIT_ORIGIN_KEY, kitOriginOf, markKitOrigin, unmarkedDrawings } from './origin'\nexport type { KitOrigin, UnmarkedDrawing } from './origin'\nexport { footprintBlocker } from './footprint'\nexport type { Footprint, FootprintObject } from './footprint'\nexport {\n BLOW_IDS,\n BODY_FOLEY_IDS,\n BODY_SOUND_IDS,\n DEFAULT_SURFACE,\n FOOTSTEP_SOUND_IDS,\n GAIT_IDS,\n HEAVY_LANDING_SPEED,\n LANDING_IDS,\n LAND_SOUND_IDS,\n MATERIAL_SURFACES,\n SURFACE_IDS,\n SWING_LEAD_SECONDS,\n footstepSoundId,\n hitSoundId,\n isSurfaceId,\n landSoundId,\n surfaceOfMaterial,\n swingSoundId,\n} from './foley'\nexport type { BlowId, BlowTarget, BodyFoleyId, BodySoundId, FootstepSoundId, GaitId, LandSoundId, LandingId, SurfaceId } from './foley'\n","{\n \"guide\": {\n \"heightMetres\": 1.8,\n \"shoulderWidthMetres\": 0.61,\n \"shoulderWidthWomanMetres\": 0.57,\n \"shoulderHeightMetres\": 1.475,\n \"headToHeight\": 0.1667\n },\n \"teamGame\": {\n \"source\": \"G 65336 J (shoulders +-0.24 m)\",\n \"joints\": {\n \"quadril\": [\n 0,\n 0.95,\n 0\n ],\n \"pescoco\": [\n 0,\n 1.52,\n 0\n ],\n \"ombroE\": [\n -0.24,\n 1.44,\n 0\n ],\n \"ombroD\": [\n 0.24,\n 1.44,\n 0\n ],\n \"cotoveloE\": [\n -0.27,\n 1.13,\n 0\n ],\n \"cotoveloD\": [\n 0.27,\n 1.13,\n 0\n ],\n \"coxaE\": [\n -0.1,\n 0.92,\n 0\n ],\n \"coxaD\": [\n 0.1,\n 0.92,\n 0\n ],\n \"joelhoE\": [\n -0.1,\n 0.5,\n 0\n ],\n \"joelhoD\": [\n 0.1,\n 0.5,\n 0\n ]\n },\n \"heightMetres\": 1.8\n },\n \"lodDistancesMetres\": [\n 18,\n 55\n ],\n \"triangleBudget\": {\n \"lod0\": 2500,\n \"lod1\": 800,\n \"lod2\": 250\n }\n}\n","import type { SegmentKind, Vec3 } from '@adatechnology/game-contracts'\n\nimport body from '../data/body.json'\n\n/** The 17 joints of a body: one per segment, the point the segment hangs from. */\nexport type JointId =\n | 'hip'\n | 'spine'\n | 'neck'\n | 'head'\n | 'jaw'\n | 'shoulderL'\n | 'shoulderR'\n | 'elbowL'\n | 'elbowR'\n | 'wristL'\n | 'wristR'\n | 'hipL'\n | 'hipR'\n | 'kneeL'\n | 'kneeR'\n | 'ankleL'\n | 'ankleR'\n\nexport const JOINT_IDS: readonly JointId[] = ['hip', 'spine', 'neck', 'head', 'jaw', 'shoulderL', 'shoulderR', 'elbowL', 'elbowR', 'wristL', 'wristR', 'hipL', 'hipR', 'kneeL', 'kneeR', 'ankleL', 'ankleR']\n\n/**\n * Proportions of a body: joint positions in metres for a body of `heightMetres`, standing, feet on y = 0, facing +z, left side on +x.\n * `segmentRadius` is the radius of the capsule a segment is hit through.\n */\nexport type BodySpec = {\n readonly id: string\n readonly heightMetres: number\n readonly joints: Readonly<Record<JointId, Vec3>>\n readonly segmentRadius: Readonly<Record<SegmentKind, number>>\n}\n\nconst SEGMENT_RADIUS: Readonly<Record<SegmentKind, number>> = {\n head: 0.1,\n jaw: 0.05,\n neck: 0.06,\n torso: 0.17,\n pelvis: 0.16,\n upperArm: 0.085,\n forearm: 0.065,\n hand: 0.05,\n thigh: 0.1,\n shin: 0.08,\n foot: 0.05,\n}\n\nconst { guide } = body\nconst shoulderX = guide.shoulderWidthMetres / 2\nconst shoulderY = guide.shoulderHeightMetres\n\n/** The Elenco style guide body (1.80 m, shoulders 0.61 m at 1.475 m, head about 1/6 of the height): the default. */\nexport const GUIDE: BodySpec = {\n id: 'guide',\n heightMetres: guide.heightMetres,\n joints: {\n hip: [0, 0.92, 0],\n spine: [0, 1.05, 0],\n neck: [0, 1.56, 0],\n head: [0, 1.64, 0],\n jaw: [0, 1.66, 0.06],\n shoulderL: [shoulderX, shoulderY, 0],\n shoulderR: [-shoulderX, shoulderY, 0],\n elbowL: [shoulderX, shoulderY - 0.31, 0],\n elbowR: [-shoulderX, shoulderY - 0.31, 0],\n wristL: [shoulderX, shoulderY - 0.61, 0],\n wristR: [-shoulderX, shoulderY - 0.61, 0],\n hipL: [0.105, 0.9, 0],\n hipR: [-0.105, 0.9, 0],\n kneeL: [0.105, 0.48, 0],\n kneeR: [-0.105, 0.48, 0],\n ankleL: [0.105, 0.1, 0],\n ankleR: [-0.105, 0.1, 0],\n },\n segmentRadius: SEGMENT_RADIUS,\n}\n\nconst J = body.teamGame.joints\nconst below = (point: readonly number[], drop: number): Vec3 => [point[0], point[1] - drop, point[2]]\n\n/**\n * The game's crowd body (G 65336 `J`, shoulders ±0.24 m), kept for the goldens of the instanced path. `J` has no spine, head, jaw, wrist or\n * ankle: spine and head sit on the hip and neck bones (bones 0 and 1 of the game), the wrist and ankle one forearm and one shin below.\n */\nexport const TEAM_GAME: BodySpec = {\n id: 'team-game',\n heightMetres: body.teamGame.heightMetres,\n joints: {\n hip: J.quadril as Vec3,\n spine: J.quadril as Vec3,\n neck: J.pescoco as Vec3,\n head: J.pescoco as Vec3,\n jaw: [0, J.pescoco[1] + 0.08, 0.06],\n shoulderL: J.ombroE as Vec3,\n shoulderR: J.ombroD as Vec3,\n elbowL: J.cotoveloE as Vec3,\n elbowR: J.cotoveloD as Vec3,\n wristL: below(J.cotoveloE, 0.3),\n wristR: below(J.cotoveloD, 0.3),\n hipL: J.coxaE as Vec3,\n hipR: J.coxaD as Vec3,\n kneeL: J.joelhoE as Vec3,\n kneeR: J.joelhoD as Vec3,\n ankleL: below(J.joelhoE, 0.42),\n ankleR: below(J.joelhoD, 0.42),\n },\n segmentRadius: SEGMENT_RADIUS,\n}\n\n/** A body spec from `base` (default `GUIDE`) with the given fields replaced; `joints` and `segmentRadius` merge key by key. */\nexport const createBodySpec = (patch: Partial<BodySpec> = {}, base: BodySpec = GUIDE): BodySpec => ({\n id: patch.id ?? base.id,\n heightMetres: patch.heightMetres ?? base.heightMetres,\n joints: { ...base.joints, ...patch.joints },\n segmentRadius: { ...base.segmentRadius, ...patch.segmentRadius },\n})\n","import type { CorpseOptions } from './types'\n\nexport type CorpseEntry<TData> = {\n /** Seconds since it fell. */\n age: number\n readonly data: TData\n}\n\nexport type CorpseLedger<TData> = {\n readonly entries: readonly CorpseEntry<TData>[]\n readonly count: number\n /** Adds a corpse; when the ledger is full the oldest one is replaced. Returns the replaced data, if any. */\n add(data: TData): TData | undefined\n /** Ages every corpse by `dt` seconds and removes the ones past their life (swap with the last, like the game). Returns the removed data. */\n update(dt: number): TData[]\n /** Takes one corpse away (eaten, searched and dragged off): the last one moves into its place, like the game's `CP`. Returns its data. */\n remove(index: number): TData | undefined\n clear(): void\n}\n\n/** The bodies left on the ground: capped (400) and gone after a lifetime (900 s); generic over what each one carries. */\nexport const createCorpseLedger = <TData>(options: CorpseOptions): CorpseLedger<TData> => {\n const entries: CorpseEntry<TData>[] = []\n return {\n entries,\n get count() {\n return entries.length\n },\n add(data) {\n if (entries.length < options.max) {\n entries.push({ age: 0, data })\n return undefined\n }\n let oldest = 0\n for (let index = 1; index < entries.length; index++) if ((entries[index] as CorpseEntry<TData>).age > (entries[oldest] as CorpseEntry<TData>).age) oldest = index\n const replaced = (entries[oldest] as CorpseEntry<TData>).data\n entries[oldest] = { age: 0, data }\n return replaced\n },\n update(dt) {\n const removed: TData[] = []\n for (let index = 0; index < entries.length; index++) {\n const entry = entries[index] as CorpseEntry<TData>\n entry.age += dt\n if (entry.age <= options.seconds) continue\n removed.push(entry.data)\n entries[index] = entries[entries.length - 1] as CorpseEntry<TData>\n entries.pop()\n index--\n }\n return removed\n },\n remove(index) {\n const entry = entries[index]\n if (!entry) return undefined\n entries[index] = entries[entries.length - 1] as CorpseEntry<TData>\n entries.pop()\n return entry.data\n },\n clear() {\n entries.length = 0\n },\n }\n}\n","import { MISSING_BITS } from '@adatechnology/game-contracts'\nimport type { SegmentKind } from '@adatechnology/game-contracts'\n\nimport type { Detail, SegmentState } from '../types'\nimport { LIMB_REGIONS, REGION_HIT_POINTS, REGION_SEGMENTS } from './regions'\nimport type { LimbRegion } from './regions'\nimport { SEGMENT_TREE } from './segment-tree'\nimport type { SegmentId, SegmentSide } from './segment-tree'\n\n/** The mutable state of one body. Both detail levels live here: the 17 segments and the hit points of the 5 crowd regions. */\nexport type BodyState = {\n readonly segments: SegmentState[]\n readonly regions: Record<LimbRegion, number>\n vitality: number\n readonly maxVitality: number\n alive: boolean\n /** The game's `MISSING_BITS` mask. */\n missing: number\n /** Sides that limp, in the order they started. */\n readonly limping: SegmentSide[]\n crawl: boolean\n stunSeconds: number\n /** Loose pieces: each severed segment with what fell with it. */\n readonly pieces: SegmentId[][]\n /** Protection layers used up (a helmet that took its one shot). */\n readonly consumed: Set<string>\n detail: Detail\n}\n\nexport type CreateBodyStateParams = { readonly vitality: number; readonly detail: Detail; readonly crawling?: boolean; readonly hitPointsOf: (kind: SegmentKind) => number }\n\nexport const createBodyState = ({ vitality, detail, crawling, hitPointsOf }: CreateBodyStateParams): BodyState => ({\n segments: SEGMENT_TREE.map((node) => {\n const max = hitPointsOf(node.kind)\n return { id: node.id, kind: node.kind, region: node.region, hp: max, max, status: 'ok', attached: true, ...(node.side ? { side: node.side } : {}) }\n }),\n regions: { ...REGION_HIT_POINTS },\n vitality,\n maxVitality: vitality,\n alive: true,\n missing: 0,\n limping: [],\n crawl: crawling ?? false,\n stunSeconds: 0,\n pieces: [],\n consumed: new Set(),\n detail,\n})\n\nexport const segmentOf = (body: BodyState, id: SegmentId): SegmentState => {\n const segment = body.segments.find((candidate) => candidate.id === id)\n if (!segment) throw new Error(`game-characters: unknown segment \"${id}\"`)\n return segment\n}\n\nconst LIMB_MASK: readonly { readonly ids: readonly SegmentId[]; readonly lost: number; readonly broken: number }[] = [\n { ids: ['upperArmL', 'forearmL'], lost: MISSING_BITS.armL, broken: MISSING_BITS.armLBroken },\n { ids: ['upperArmR', 'forearmR'], lost: MISSING_BITS.armR, broken: MISSING_BITS.armRBroken },\n { ids: ['thighL', 'shinL'], lost: MISSING_BITS.legL, broken: MISSING_BITS.legLBroken },\n { ids: ['thighR', 'shinR'], lost: MISSING_BITS.legR, broken: MISSING_BITS.legRBroken },\n]\n\n/** The crowd mask the segments amount to: a lost head, jaw, arm or leg (upper or lower part), and a broken one still attached. */\nexport const missingFromSegments = (body: BodyState): number => {\n let mask = 0\n if (!segmentOf(body, 'head').attached) mask |= MISSING_BITS.head\n else if (!segmentOf(body, 'jaw').attached) mask |= MISSING_BITS.jaw\n for (const limb of LIMB_MASK) {\n const parts = limb.ids.map((id) => segmentOf(body, id))\n if (parts.some((part) => !part.attached)) mask |= limb.lost\n else if (parts.some((part) => part.status === 'broken')) mask |= limb.broken\n }\n return mask\n}\n\n/** Region hit points from the segments: the share of the region's segment hit points left, scaled to the region's own hit points. */\nexport const regionsFromSegments = (body: BodyState): void => {\n for (const region of LIMB_REGIONS) {\n const parts = REGION_SEGMENTS[region].map((id) => segmentOf(body, id))\n const left = parts.reduce((sum, part) => sum + (part.attached ? Math.max(0, part.hp) : 0), 0)\n const total = parts.reduce((sum, part) => sum + part.max, 0)\n body.regions[region] = (REGION_HIT_POINTS[region] * left) / total\n }\n body.missing |= missingFromSegments(body)\n}\n\n/** Segment hit points from the regions (the reverse is lossy: every segment of a region keeps the region's share). */\nexport const segmentsFromRegions = (body: BodyState): void => {\n for (const region of LIMB_REGIONS) {\n const share = Math.max(0, body.regions[region]) / REGION_HIT_POINTS[region]\n for (const id of REGION_SEGMENTS[region]) {\n const part = segmentOf(body, id)\n if (part.attached) part.hp = Math.min(part.hp, part.max * share)\n }\n }\n}\n","{\n \"id\": \"team-game\",\n \"segments\": {\n \"head\": 40,\n \"arm\": 60,\n \"leg\": 80\n },\n \"calibers\": {\n \"7_62x51\": {\n \"gameKey\": \"308\",\n \"name\": \"7,62×51 (.308)\",\n \"points\": 75,\n \"penetration\": 4,\n \"effectiveRangeMetres\": 600,\n \"noiseMetres\": 130,\n \"skullRange\": 1,\n \"class\": \"rifle\"\n },\n \"9x19\": {\n \"gameKey\": \"9x19\",\n \"name\": \"9×19 mm\",\n \"points\": 35,\n \"penetration\": 1.2,\n \"effectiveRangeMetres\": 35,\n \"noiseMetres\": 60,\n \"skullRange\": 1,\n \"class\": \"pistol\"\n },\n \"12ga_00buck\": {\n \"gameKey\": \"12ga\",\n \"name\": \"12 ga 00\",\n \"points\": 12,\n \"pellets\": 9,\n \"penetration\": 0.5,\n \"effectiveRangeMetres\": 15,\n \"noiseMetres\": 90,\n \"skullRange\": 8,\n \"class\": \"pistol\"\n },\n \"7_62x39\": {\n \"gameKey\": \"762x39\",\n \"name\": \"7,62×39 mm\",\n \"points\": 55,\n \"penetration\": 3,\n \"effectiveRangeMetres\": 250,\n \"noiseMetres\": 110,\n \"skullRange\": 1,\n \"class\": \"rifle\"\n }\n },\n \"regionCost\": {\n \"head\": 0.7,\n \"torso\": 1,\n \"bruteTorso\": 2,\n \"limb\": 0.5\n },\n \"damageKeptPerBody\": 0.75,\n \"falloff\": {\n \"fullAtRange\": 1,\n \"atTwiceRange\": 0.5\n },\n \"helmetAbsorbs\": \"pistol\",\n \"regions\": [\n \"head\",\n \"torso\",\n \"armL\",\n \"armR\",\n \"legL\",\n \"legR\"\n ]\n}\n","import type { BodyRegion } from '@adatechnology/game-contracts'\n\nimport teamGame from '../presets/team-game.json'\nimport type { SegmentId } from './segment-tree'\n\n/** The regions that keep their own hit points in the crowd model (the torso has none: torso hits go to vitality). */\nexport type LimbRegion = Exclude<BodyRegion, 'torso'>\n\nexport const LIMB_REGIONS: readonly LimbRegion[] = ['head', 'armL', 'armR', 'legL', 'legR']\n\n/** The game's region numbers (`SEG`): hit points of the head, of an arm and of a leg. */\nexport const REGION_HIT_POINTS: Readonly<Record<LimbRegion, number>> = {\n head: teamGame.segments.head,\n armL: teamGame.segments.arm,\n armR: teamGame.segments.arm,\n legL: teamGame.segments.leg,\n legR: teamGame.segments.leg,\n}\n\n/** The segments that make up each region, and the one that names it in events (the first). */\nexport const REGION_SEGMENTS: Readonly<Record<BodyRegion, readonly SegmentId[]>> = {\n head: ['head', 'jaw', 'neck'],\n torso: ['torso', 'pelvis'],\n armL: ['upperArmL', 'forearmL', 'handL'],\n armR: ['upperArmR', 'forearmR', 'handR'],\n legL: ['thighL', 'shinL', 'footL'],\n legR: ['thighR', 'shinR', 'footR'],\n}\n\nexport const BODY_REGIONS: readonly BodyRegion[] = ['head', 'torso', 'armL', 'armR', 'legL', 'legR']\n\nexport const isBodyRegion = (value: string): value is BodyRegion => (BODY_REGIONS as readonly string[]).includes(value)\n","{\n \"segments\": [\n {\n \"id\": \"head\",\n \"region\": \"head\",\n \"hitPoints\": 40,\n \"multiplier\": 3,\n \"bleed\": 40\n },\n {\n \"id\": \"jaw\",\n \"region\": \"head\",\n \"hitPoints\": 12,\n \"multiplier\": 2,\n \"bleed\": 8\n },\n {\n \"id\": \"neck\",\n \"region\": \"head\",\n \"hitPoints\": 25,\n \"multiplier\": 2,\n \"bleed\": 46\n },\n {\n \"id\": \"torso\",\n \"region\": \"torso\",\n \"hitPoints\": 150,\n \"multiplier\": 1,\n \"bleed\": 10\n },\n {\n \"id\": \"pelvis\",\n \"region\": \"torso\",\n \"hitPoints\": 90,\n \"multiplier\": 1,\n \"bleed\": 12\n },\n {\n \"id\": \"upperArm\",\n \"region\": \"arm\",\n \"hitPoints\": 45,\n \"multiplier\": 0.6,\n \"bleed\": 15\n },\n {\n \"id\": \"forearm\",\n \"region\": \"arm\",\n \"hitPoints\": 30,\n \"multiplier\": 0.6,\n \"bleed\": 8\n },\n {\n \"id\": \"hand\",\n \"region\": \"arm\",\n \"hitPoints\": 12,\n \"multiplier\": 0.6,\n \"bleed\": 3\n },\n {\n \"id\": \"thigh\",\n \"region\": \"leg\",\n \"hitPoints\": 55,\n \"multiplier\": 0.7,\n \"bleed\": 19\n },\n {\n \"id\": \"shin\",\n \"region\": \"leg\",\n \"hitPoints\": 40,\n \"multiplier\": 0.7,\n \"bleed\": 9\n },\n {\n \"id\": \"foot\",\n \"region\": \"leg\",\n \"hitPoints\": 15,\n \"multiplier\": 0.7,\n \"bleed\": 3\n }\n ],\n \"regions\": {\n \"head\": {\n \"hitPoints\": 40,\n \"segmentSum\": 77,\n \"scale\": 0.5195\n },\n \"arm\": {\n \"hitPoints\": 60,\n \"segmentSum\": 87,\n \"scale\": 0.6897\n },\n \"leg\": {\n \"hitPoints\": 80,\n \"segmentSum\": 110,\n \"scale\": 0.7273\n }\n },\n \"arsenalWeapons\": {\n \"pistola\": {\n \"n\": \"Pistola 9×19\",\n \"dmg\": 35,\n \"pel\": 1\n },\n \"fuzil\": {\n \"n\": \"Fuzil 7,62×39\",\n \"dmg\": 55,\n \"pel\": 1\n },\n \"escopeta\": {\n \"n\": \"Escopeta 12 ga\",\n \"dmg\": 12,\n \"pel\": 9\n },\n \"cinquenta\": {\n \"n\": \".50 BMG\",\n \"dmg\": 180,\n \"pel\": 1\n },\n \"granada\": {\n \"n\": \"Granada\",\n \"dmg\": 300,\n \"pel\": 0\n }\n },\n \"elenco\": {\n \"segments\": {\n \"head\": {\n \"hp\": 40,\n \"radius\": 0.07\n },\n \"torso\": {\n \"hp\": 120\n },\n \"pelvis\": {\n \"hp\": 90\n },\n \"upperArm\": {\n \"hp\": 60,\n \"radius\": 0.085\n },\n \"forearm\": {\n \"hp\": 45,\n \"radius\": 0.065\n },\n \"hand\": {\n \"hp\": 25,\n \"radius\": 0.05\n },\n \"thigh\": {\n \"hp\": 80,\n \"radius\": 0.1\n },\n \"shin\": {\n \"hp\": 55,\n \"radius\": 0.08\n }\n },\n \"below\": {\n \"upperArm\": [\n \"forearm\",\n \"hand\"\n ],\n \"forearm\": [\n \"hand\"\n ],\n \"thigh\": [\n \"shin\"\n ]\n },\n \"falls\": {\n \"upperArm\": \"o braço inteiro\",\n \"forearm\": \"o antebraço com a mão\",\n \"hand\": \"a mão\",\n \"thigh\": \"a perna inteira\",\n \"shin\": \"a canela com o pé\",\n \"head\": \"a cabeça\"\n },\n \"cutWeapons\": [\n {\n \"id\": \"faca\",\n \"n\": \"Faca\",\n \"dmg\": 22,\n \"cut\": 0.7\n },\n {\n \"id\": \"facao\",\n \"n\": \"Facão\",\n \"dmg\": 55,\n \"cut\": 1\n },\n {\n \"id\": \"machado\",\n \"n\": \"Machado\",\n \"dmg\": 85,\n \"cut\": 1\n },\n {\n \"id\": \"p9\",\n \"n\": \"Pistola 9 mm\",\n \"dmg\": 30,\n \"cut\": 0.15\n },\n {\n \"id\": \"e12\",\n \"n\": \"Espingarda 12\",\n \"dmg\": 95,\n \"cut\": 0.75\n },\n {\n \"id\": \"f762\",\n \"n\": \"Fuzil 7,62\",\n \"dmg\": 65,\n \"cut\": 0.45\n }\n ]\n },\n \"severity\": {\n \"severAtCut\": 0.5,\n \"severAtDamageFraction\": 0.9,\n \"destroyAtDamageMultiple\": 2,\n \"limpAtHpFraction\": 0.5,\n \"splitAtDamage\": 150,\n \"headKillDamage\": 20,\n \"jawChance\": 0.3,\n \"jawDamage\": 25,\n \"headBitDamage\": 60,\n \"headBitShotgunMetres\": 5\n },\n \"defaultCut\": {\n \"slash\": 0.85,\n \"blunt\": 0,\n \"explosive\": 1,\n \"ballistic\": {\n \"pistol\": 0.15,\n \"buckshot\": 0.75,\n \"rifle\": 0.45\n }\n },\n \"deathRule\": {\n \"default\": \"vitality-or-head\",\n \"options\": [\n \"vitality-or-head\",\n \"head-only\"\n ]\n }\n}\n","import type { BodyRegion, BodySlot, SegmentKind } from '@adatechnology/game-contracts'\n\nimport segments from '../data/segments.json'\nimport type { JointId } from './body-spec'\n\nexport type SegmentSide = 'L' | 'R'\ntype LimbKind = 'upperArm' | 'forearm' | 'hand' | 'thigh' | 'shin' | 'foot'\nexport type SegmentId = 'pelvis' | 'torso' | 'neck' | 'head' | 'jaw' | `${LimbKind}${SegmentSide}`\n\n/** One segment of the tree: the joint it hangs from, its parent, the crowd region it belongs to and the item slots that cover it. */\nexport type SegmentNode = {\n readonly id: SegmentId\n readonly kind: SegmentKind\n readonly side?: SegmentSide\n readonly joint: JointId\n readonly parent?: SegmentId\n readonly region: BodyRegion\n readonly slots: readonly BodySlot[]\n}\n\n/** Numbers of a segment kind (the Arsenal table, A4): hit points, damage multiplier and bleed rate. */\nexport type SegmentNumbers = { readonly hitPoints: number; readonly multiplier: number; readonly bleed: number }\n\nconst limb = (kind: LimbKind, side: SegmentSide, joint: JointId, parent: SegmentId | undefined, slots: readonly BodySlot[]): SegmentNode => {\n const isArm = kind === 'upperArm' || kind === 'forearm' || kind === 'hand'\n const region: BodyRegion = isArm ? (side === 'L' ? 'armL' : 'armR') : side === 'L' ? 'legL' : 'legR'\n return { id: `${kind}${side}`, kind, side, joint, region, slots, ...(parent ? { parent } : {}) }\n}\n\nconst sideOf = (side: SegmentSide): SegmentNode[] => [\n limb('upperArm', side, `shoulder${side}`, 'torso', ['arms']),\n limb('forearm', side, `elbow${side}`, `upperArm${side}`, ['arms']),\n limb('hand', side, `wrist${side}`, `forearm${side}`, ['hands']),\n limb('thigh', side, `hip${side}`, 'pelvis', ['legs']),\n limb('shin', side, `knee${side}`, `thigh${side}`, ['legs']),\n limb('foot', side, `ankle${side}`, `shin${side}`, ['feet']),\n]\n\n/** The 17 segments (11 kinds, limbs on both sides), parents before children. Neck and jaw have no item slot: a helmet does not cover them. */\nexport const SEGMENT_TREE: readonly SegmentNode[] = [\n { id: 'pelvis', kind: 'pelvis', joint: 'hip', region: 'torso', slots: ['torso'] },\n { id: 'torso', kind: 'torso', joint: 'spine', parent: 'pelvis', region: 'torso', slots: ['torso'] },\n { id: 'neck', kind: 'neck', joint: 'neck', parent: 'torso', region: 'head', slots: [] },\n { id: 'head', kind: 'head', joint: 'head', parent: 'neck', region: 'head', slots: ['head', 'eyes'] },\n { id: 'jaw', kind: 'jaw', joint: 'jaw', parent: 'head', region: 'head', slots: [] },\n ...sideOf('L'),\n ...sideOf('R'),\n]\n\nexport const SEGMENT_IDS: readonly SegmentId[] = SEGMENT_TREE.map((node) => node.id)\n\nconst NODES = new Map(SEGMENT_TREE.map((node) => [node.id, node]))\n\nexport const segmentNode = (id: SegmentId): SegmentNode => {\n const node = NODES.get(id)\n if (!node) throw new Error(`game-characters: unknown segment \"${id}\"`)\n return node\n}\n\nexport const isSegmentId = (value: string): value is SegmentId => NODES.has(value as SegmentId)\n\n/** Every segment that hangs below `id` (children, grandchildren...), in tree order: what falls with it when it is severed. */\nexport const segmentsBelow = (id: SegmentId): SegmentId[] => {\n const out: SegmentId[] = []\n const visit = (parent: SegmentId): void => {\n for (const node of SEGMENT_TREE) {\n if (node.parent !== parent) continue\n out.push(node.id)\n visit(node.id)\n }\n }\n visit(id)\n return out\n}\n\n/** The Arsenal numbers per segment kind (hit points, `MULT`, `BLEED`). */\nexport const SEGMENT_NUMBERS: Readonly<Record<SegmentKind, SegmentNumbers>> = Object.fromEntries(\n segments.segments.map((entry) => [entry.id, { hitPoints: entry.hitPoints, multiplier: entry.multiplier, bleed: entry.bleed }]),\n) as Record<SegmentKind, SegmentNumbers>\n","import type { DestructibleSpec, JointSpec, PartShape, PartSpec, SegmentKind, Vec3 } from '@adatechnology/game-contracts'\n\nimport type { BodySpec } from './body-spec'\nimport type { Rig } from './rig'\nimport { SEGMENT_TREE } from './segment-tree'\nimport type { SegmentNode } from './segment-tree'\n\n/** The material id the parts of a body carry (a destruction world without it skips its own material rules: the body is a `DestructibleImpl`). */\nexport const BODY_MATERIAL = 'flesh'\n\n/** Share of the body mass per segment kind (sums to 1 over the 17 segments). */\nconst MASS_SHARE: Readonly<Record<SegmentKind, number>> = {\n head: 0.07,\n jaw: 0.01,\n neck: 0.02,\n torso: 0.34,\n pelvis: 0.14,\n upperArm: 0.028,\n forearm: 0.016,\n hand: 0.006,\n thigh: 0.1,\n shin: 0.045,\n foot: 0.015,\n}\nconst ADULT_MASS_KG = 70\nconst LEAF_KINDS: ReadonlySet<SegmentKind> = new Set(['head', 'jaw', 'hand', 'foot'])\n\nconst firstChild = (node: SegmentNode): SegmentNode | undefined => SEGMENT_TREE.find((candidate) => candidate.parent === node.id)\n\n/** A cylinder from `from` to `to`: centred between them, its local +Y turned onto the segment (Euler XYZ). */\nconst cylinderBetween = (from: Vec3, to: Vec3, radius: number): { shape: PartShape; position: Vec3; rotation: Vec3 } => {\n const axis: Vec3 = [to[0] - from[0], to[1] - from[1], to[2] - from[2]]\n const length = Math.max(1e-3, Math.hypot(axis[0], axis[1], axis[2]))\n const [x, y, z] = [axis[0] / length, axis[1] / length, axis[2] / length]\n return {\n shape: { kind: 'cylinder', radius, height: length },\n position: [(from[0] + to[0]) / 2, (from[1] + to[1]) / 2, (from[2] + to[2]) / 2],\n rotation: [Math.atan2(z, y), 0, -Math.asin(Math.max(-1, Math.min(1, x)))],\n }\n}\n\nconst partOf = (node: SegmentNode, rig: Rig, body: BodySpec, massKg: number): PartSpec => {\n const joint = rig.joints[node.joint]\n const radius = body.segmentRadius[node.kind] * (rig.height / body.heightMetres)\n const mass = massKg * MASS_SHARE[node.kind]\n const child = firstChild(node)\n if (LEAF_KINDS.has(node.kind) || !child) {\n const lift = node.kind === 'head' ? radius : node.kind === 'jaw' ? 0 : -radius\n return { id: node.id, material: BODY_MATERIAL, shape: { kind: 'sphere', radius }, position: [joint[0], joint[1] + lift, joint[2]], mass, tags: ['body', node.kind] }\n }\n const placed = cylinderBetween(joint, rig.joints[child.joint], radius)\n return { id: node.id, material: BODY_MATERIAL, ...placed, mass, tags: ['body', node.kind] }\n}\n\nexport type DescribeBodyParams = { readonly rig: Rig; readonly body: BodySpec; readonly build: number; readonly name?: string }\n\n/** The body as destruction parts (one per segment, in the body's local space) and the joints between them. */\nexport const describeBody = ({ rig, body, build, name }: DescribeBodyParams): DestructibleSpec => {\n const massKg = ADULT_MASS_KG * (rig.height / body.heightMetres) ** 3 * build\n const parts = SEGMENT_TREE.map((node) => partOf(node, rig, body, massKg))\n const joints: JointSpec[] = SEGMENT_TREE.filter((node) => node.parent).map((node) => ({ id: `${node.parent}~${node.id}`, a: node.parent as string, b: node.id, kind: 'fused', tools: [] }))\n return { ...(name ? { name } : {}), parts, joints, tags: ['character'] }\n}\n","import type { DestructibleSpec, JointState, ObjectState, PartState, PartStatus, Quat, Vec3 } from '@adatechnology/game-contracts'\n\nimport type { SegmentStatus } from '../types'\nimport { missingFromSegments } from './body-state'\nimport type { BodyState } from './body-state'\n\nconst PART_STATUS: Readonly<Record<SegmentStatus, PartStatus>> = { ok: 'intact', wounded: 'damaged', broken: 'damaged', severed: 'collapsed', destroyed: 'broken' }\nconst BODY_TEMPERATURE_C = 37\n\nexport type ToObjectStateParams = {\n readonly id: string\n readonly name?: string\n readonly position: Vec3\n readonly rotation: Quat\n readonly body: BodyState\n readonly spec: DestructibleSpec\n}\n\n/** The body in the destruction contract: one part per segment, the joints between them, the pieces and the `body` summary. */\nexport const toObjectState = ({ id, name, position, rotation, body, spec }: ToObjectStateParams): ObjectState => {\n const groups: string[][] = [body.segments.filter((segment) => segment.attached).map((segment) => segment.id)]\n for (const piece of body.pieces) {\n const left = piece.filter((part) => body.segments.find((segment) => segment.id === part)?.status !== 'destroyed')\n if (left.length > 0) groups.push(left)\n }\n const groupOf = (part: string): number => groups.findIndex((group) => group.includes(part))\n const parts: PartState[] = body.segments.map((segment) => {\n const mass = spec.parts.find((part) => part.id === segment.id)?.mass ?? 0\n return {\n id: segment.id,\n material: 'flesh',\n status: PART_STATUS[segment.status],\n hp: Math.max(0, segment.hp),\n maxHp: segment.max,\n mass,\n temperature: BODY_TEMPERATURE_C,\n burning: false,\n fuel: 0,\n char: 0,\n melt: 0,\n dents: [],\n holes: [],\n cracks: [],\n group: groupOf(segment.id),\n }\n })\n const joints: JointState[] = (spec.joints ?? []).map((joint) => {\n const child = body.segments.find((segment) => segment.id === joint.b)\n const parent = body.segments.find((segment) => segment.id === joint.a)\n const open = child !== undefined && parent !== undefined && parent.attached !== child.attached\n return { id: joint.id ?? `${joint.a}~${joint.b}`, a: joint.a, b: joint.b, kind: joint.kind, hp: open ? 0 : 1, maxHp: 1, progress: 0, open, ...(open ? { openedBy: 'break' as const } : {}) }\n })\n return {\n id,\n ...(name ? { name } : {}),\n position: [...position],\n rotation: [...rotation],\n parts,\n joints,\n groups,\n destroyed: false,\n body: { alive: body.alive, vitality: body.vitality, missing: body.detail === 'segments' ? body.missing | missingFromSegments(body) : body.missing, limp: body.limping.length > 0, crawl: body.crawl },\n }\n}\n","import type { Vec3 } from '@adatechnology/game-contracts'\n\nimport { JOINT_IDS } from './body-spec'\nimport type { BodySpec, JointId } from './body-spec'\n\n/** The bones of the game's instanced body (G 65336): 0 torso and hip, 1 head, 2/3 arms, 4/5 thighs, 6 eyes, 7/8 forearms, 9/10 shins, 11 jaw. */\nexport const GAME_BONES = {\n torso: 0,\n head: 1,\n upperArmL: 2,\n upperArmR: 3,\n thighL: 4,\n thighR: 5,\n eyes: 6,\n forearmL: 7,\n forearmR: 8,\n shinL: 9,\n shinR: 10,\n jaw: 11,\n} as const\n\nexport const JOINT_PARENTS: Readonly<Record<JointId, JointId | undefined>> = {\n hip: undefined,\n spine: 'hip',\n neck: 'spine',\n head: 'neck',\n jaw: 'head',\n shoulderL: 'spine',\n shoulderR: 'spine',\n elbowL: 'shoulderL',\n elbowR: 'shoulderR',\n wristL: 'elbowL',\n wristR: 'elbowR',\n hipL: 'hip',\n hipR: 'hip',\n kneeL: 'hipL',\n kneeR: 'hipR',\n ankleL: 'kneeL',\n ankleR: 'kneeR',\n}\n\n/** The joints of one body, scaled to its height and build. */\nexport type Rig = {\n readonly joints: Readonly<Record<JointId, Vec3>>\n readonly parents: Readonly<Record<JointId, JointId | undefined>>\n readonly gameBones: typeof GAME_BONES\n /** Height in metres of this body. */\n readonly height: number\n}\n\nexport type CreateRigParams = { readonly body: BodySpec; readonly height: number; readonly build: number }\n\n/** Joints of `body` scaled to `height` metres (vertical and depth) and to `build` (width). */\nexport const createRig = ({ body, height, build }: CreateRigParams): Rig => {\n const scale = height / body.heightMetres\n const joints = Object.fromEntries(\n JOINT_IDS.map((id) => {\n const [x, y, z] = body.joints[id]\n return [id, [x * scale * build, y * scale, z * scale] as Vec3]\n }),\n ) as Record<JointId, Vec3>\n return { joints, parents: JOINT_PARENTS, gameBones: GAME_BONES, height }\n}\n","import type { DestructibleSpec, ObjectState, Quat, Vec3 } from '@adatechnology/game-contracts'\n\nimport type { BodySpec } from './body/body-spec'\nimport { createBodyState, regionsFromSegments, segmentsFromRegions } from './body/body-state'\nimport type { BodyState } from './body/body-state'\nimport { describeBody } from './body/describe'\nimport { toObjectState } from './body/object-state'\nimport type { LimbRegion } from './body/regions'\nimport { createRig } from './body/rig'\nimport type { Rig } from './body/rig'\nimport { SEGMENT_NUMBERS } from './body/segment-tree'\nimport type { BodyMultipliers, CharacterSpec, Detail, SegmentState } from './types'\n\n/** A spec with every default filled in. */\nexport type ResolvedCharacterSpec = CharacterSpec & {\n readonly id: string\n readonly height: number\n readonly build: number\n readonly vitality: number\n readonly multipliers: BodyMultipliers\n readonly limbFactor: number\n readonly bloodFactor: number\n readonly bodyCount: number\n readonly stunSeconds: number\n readonly protection: readonly string[]\n readonly detail: Detail\n}\n\nexport type Character = {\n readonly id: string\n readonly spec: ResolvedCharacterSpec\n readonly rig: Rig\n /** Live view of the 17 segments. */\n readonly segments: readonly SegmentState[]\n readonly detail: Detail\n /** Hit points of the 5 crowd regions (head, arms, legs). */\n regionHitPoints(): Readonly<Record<LimbRegion, number>>\n /** Swaps the detail level: segments to regions sums each region (lossless); regions to segments spreads the region's share (lossy). */\n setDetail(detail: Detail): void\n setTransform(position: Vec3, rotation?: Quat): void\n describe(): DestructibleSpec\n state(): ObjectState\n reset(): void\n}\n\nexport const DEFAULT_VITALITY = 100\nexport const DEFAULT_STUN_SECONDS = 0.45\nconst NO_MULTIPLIERS: BodyMultipliers = { head: 1, torso: 1, arms: 1, legs: 1 }\n\nexport const resolveSpec = (spec: CharacterSpec, body: BodySpec, counter: number): ResolvedCharacterSpec => ({\n ...spec,\n id: spec.id ?? `${spec.kind}_${counter}`,\n height: spec.height ?? body.heightMetres,\n build: spec.build ?? 1,\n vitality: spec.vitality ?? DEFAULT_VITALITY,\n multipliers: { ...NO_MULTIPLIERS, ...spec.multipliers },\n limbFactor: spec.limbFactor ?? 1,\n bloodFactor: spec.bloodFactor ?? 1,\n bodyCount: spec.bodyCount ?? 1,\n stunSeconds: spec.stunSeconds ?? DEFAULT_STUN_SECONDS,\n protection: spec.protection ?? [],\n detail: spec.detail ?? 'segments',\n})\n\nexport type CharacterCore = {\n readonly spec: ResolvedCharacterSpec\n readonly rig: Rig\n readonly body: BodyState\n position: Vec3\n rotation: Quat\n readonly describe: () => DestructibleSpec\n /** Back to the state at creation (full health, every segment attached). */\n readonly resetBody: () => void\n /** The `ObjectState` of the body, built again only after `invalidateState` (cost contract: O(1) between changes). */\n readonly readState: () => ObjectState\n /** Call after anything that changes the body or the transform. */\n readonly invalidateState: () => void\n}\n\nexport type CreateCharacterCoreParams = { readonly spec: ResolvedCharacterSpec; readonly bodySpec: BodySpec; readonly hitPointsOf?: (kind: SegmentState['kind']) => number }\n\n/** The state every character shares: rig, body state, transform and the cached part list. */\nexport const createCharacterCore = ({ spec, bodySpec, hitPointsOf }: CreateCharacterCoreParams): CharacterCore => {\n const rig = createRig({ body: bodySpec, height: spec.height, build: spec.build })\n const parts = describeBody({ rig, body: bodySpec, build: spec.build, name: spec.kind })\n const fresh = (): BodyState => createBodyState({ vitality: spec.vitality, detail: spec.detail, crawling: spec.crawling ?? false, hitPointsOf: hitPointsOf ?? ((kind) => SEGMENT_NUMBERS[kind].hitPoints) })\n const body = fresh()\n let cachedState: ObjectState | undefined\n const core: CharacterCore = {\n spec,\n rig,\n body,\n position: [0, 0, 0],\n rotation: [0, 0, 0, 1],\n describe: () => parts,\n resetBody: () => {\n Object.assign(body, fresh())\n cachedState = undefined\n },\n readState: () => (cachedState ??= toObjectState({ id: spec.id, name: spec.kind, position: core.position, rotation: core.rotation, body, spec: parts })),\n invalidateState: () => {\n cachedState = undefined\n },\n }\n return core\n}\n\n/** The plain-data part of a character (state, detail and transform); the damage model is added on top of it. */\nexport const characterView = (core: CharacterCore): Character => ({\n id: core.spec.id,\n spec: core.spec,\n rig: core.rig,\n get segments() {\n return core.body.segments\n },\n get detail() {\n return core.body.detail\n },\n regionHitPoints: () => ({ ...core.body.regions }),\n setDetail(detail) {\n if (detail === core.body.detail) return\n if (detail === 'regions') regionsFromSegments(core.body)\n else segmentsFromRegions(core.body)\n core.body.detail = detail\n core.invalidateState()\n },\n setTransform(position, rotation) {\n core.position = [...position]\n if (rotation) core.rotation = [...rotation]\n core.invalidateState()\n },\n describe: core.describe,\n state: core.readState,\n reset: core.resetBody,\n})\n","import type { Quat, Vec3 } from '@adatechnology/game-contracts'\n\n/** `v` rotated by the unit quaternion `q`. */\nexport const rotate = (q: Quat, v: Vec3): Vec3 => {\n const [x, y, z, w] = q\n const tx = 2 * (y * v[2] - z * v[1])\n const ty = 2 * (z * v[0] - x * v[2])\n const tz = 2 * (x * v[1] - y * v[0])\n return [v[0] + w * tx + (y * tz - z * ty), v[1] + w * ty + (z * tx - x * tz), v[2] + w * tz + (x * ty - y * tx)]\n}\n\nconst conjugate = (q: Quat): Quat => [-q[0], -q[1], -q[2], q[3]]\n\nexport type Transform = { readonly position: Vec3; readonly rotation: Quat }\n\nexport const toWorld = ({ position, rotation }: Transform, local: Vec3): Vec3 => {\n const turned = rotate(rotation, local)\n return [turned[0] + position[0], turned[1] + position[1], turned[2] + position[2]]\n}\n\nexport const toLocal = ({ position, rotation }: Transform, world: Vec3): Vec3 => rotate(conjugate(rotation), [world[0] - position[0], world[1] - position[1], world[2] - position[2]])\n\nexport const distance = (a: Vec3, b: Vec3): number => Math.hypot(a[0] - b[0], a[1] - b[1], a[2] - b[2])\n","import type { DestructionEvent, Vec3 } from '@adatechnology/game-contracts'\n\nimport type { SegmentId } from '../body/segment-tree'\n\n/** A bleeding wound: `rate` in drops per second (the Arsenal's unit) fading to 0 over `duration` seconds; `spurt` pulses like an artery. */\nexport type Bleeder = {\n readonly part: SegmentId\n readonly rate: number\n readonly duration: number\n readonly spurt: boolean\n age: number\n /** Whole seconds of this bleeder already reported as `leak` events. */\n reported: number\n}\n\n/** Volume of one drop (a 4 mm drop of the Arsenal's blood): `leak` events carry litres per second. */\nexport const BLOOD_LITERS_PER_DROP = 0.00027\n\nexport const bleedRateAt = (bleeder: Bleeder): number => (bleeder.age >= bleeder.duration ? 0 : bleeder.rate * (1 - bleeder.age / bleeder.duration))\n\nexport type StepBleedingParams = {\n readonly bleeders: Bleeder[]\n readonly dt: number\n readonly emit: (event: Omit<Extract<DestructionEvent, { type: 'leak' }>, 'object' | 'time'>) => void\n readonly pointOf: (id: SegmentId) => Vec3\n}\n\n/** Ages the bleeders; each one still running reports a `leak` with its current rate once per second of its life. */\nexport const stepBleeding = ({ bleeders, dt, emit, pointOf }: StepBleedingParams): void => {\n for (const bleeder of bleeders) {\n if (bleeder.age >= bleeder.duration) continue\n bleeder.age += dt\n const second = Math.floor(bleeder.age)\n if (second <= bleeder.reported) continue\n bleeder.reported = second\n const rate = bleedRateAt(bleeder)\n if (rate > 0) emit({ type: 'leak', part: bleeder.part, fluid: 'blood', point: pointOf(bleeder.part), direction: [0, -1, 0], rate: rate * BLOOD_LITERS_PER_DROP, pressurized: bleeder.spurt })\n }\n}\n","import type { DamageType, DestructionEvent, Vec3 } from '@adatechnology/game-contracts'\n\nimport type { BodyState } from '../body/body-state'\nimport { segmentOf } from '../body/body-state'\nimport { segmentNode, segmentsBelow } from '../body/segment-tree'\nimport type { SegmentId, SegmentSide } from '../body/segment-tree'\nimport type { ResolvedCharacterSpec } from '../character'\nimport type { SegmentState } from '../types'\nimport { BLOOD_LITERS_PER_DROP } from './bleeding'\nimport type { Bleeder } from './bleeding'\nimport type { DamageRules, DeathRule } from './types'\n\ntype BodyChange = Extract<DestructionEvent, { type: 'body' }>['change']\ntype KillCause = 'head' | 'vitality' | 'bleed' | 'explosion'\ntype EventDraft = DestructionEvent extends infer E ? (E extends DestructionEvent ? Omit<E, 'object' | 'time'> : never) : never\n\nexport type BleedParams = { readonly segment: SegmentState; readonly rate: number; readonly duration: number; readonly spurt: boolean; readonly point?: Vec3; readonly direction?: Vec3 }\n\n/** How severing treats what hangs below: the Arsenal only detaches it, the Elenco also zeroes it and marks it cut. */\nexport type SeverBelow = 'detach' | 'zero'\n\n/** Every change a damage rule can make to a body, each emitting its events. The rules (kit, arsenal, elenco, regions) only decide. */\nexport type BodyOps = {\n readonly body: BodyState\n readonly spec: ResolvedCharacterSpec\n readonly rules: DamageRules\n readonly random: () => number\n readonly events: DestructionEvent[]\n emit(event: EventDraft): void\n pointOf(id: SegmentId): Vec3\n change(change: BodyChange, part?: SegmentId, extra?: { cause?: KillCause; below?: string[] }): void\n damage(segment: SegmentState, amount: number, damageType: DamageType): void\n wound(segment: SegmentState): void\n breakBone(segment: SegmentState): void\n sever(segment: SegmentState, below?: SeverBelow): void\n destroy(segment: SegmentState): void\n kill(cause: KillCause): void\n limp(side: SegmentSide): void\n crawl(): void\n /** A heavy hit staggers the body for the spec's `stunSeconds`. */\n stun(): void\n bleed(params: BleedParams): void\n}\n\nexport type CreateBodyOpsParams = {\n readonly body: BodyState\n readonly spec: ResolvedCharacterSpec\n readonly rules: DamageRules\n readonly random: () => number\n readonly time: number\n readonly source?: string\n readonly pointOf: (id: SegmentId) => Vec3\n readonly bleeders: Bleeder[]\n}\n\nconst killAllowed = (rule: DeathRule, cause: KillCause): boolean => rule === 'vitality-or-head' || cause === 'head' || cause === 'explosion'\n\nexport const createBodyOps = ({ body, spec, rules, random, time, source, pointOf, bleeders }: CreateBodyOpsParams): BodyOps => {\n const events: DestructionEvent[] = []\n const emit = (event: EventDraft): void => void events.push({ ...event, object: spec.id, time, ...(source !== undefined ? { source } : {}) } as DestructionEvent)\n const change: BodyOps['change'] = (kind, part, extra) => emit({ type: 'body', change: kind, ...(part ? { part } : {}), ...extra })\n const detachBelow = (segment: SegmentState, below: SeverBelow): SegmentId[] => {\n const fallen = segmentsBelow(segment.id).filter((id) => segmentOf(body, id).attached)\n segment.attached = false\n for (const id of fallen) {\n const part = segmentOf(body, id)\n part.attached = false\n if (below === 'zero') Object.assign(part, { hp: 0, status: 'severed' })\n }\n return fallen\n }\n const ops: BodyOps = {\n body,\n spec,\n rules,\n random,\n events,\n emit,\n pointOf,\n change,\n damage: (segment, amount, damageType) => emit({ type: 'damage', part: segment.id, amount, hp: Math.max(0, segment.hp), damageType }),\n wound(segment) {\n if (segment.status === 'ok') segment.status = 'wounded'\n change('wound', segment.id)\n },\n breakBone(segment) {\n segment.status = 'broken'\n change('broken', segment.id)\n },\n sever(segment, below = 'detach') {\n if (!segment.attached) return\n const fallen = detachBelow(segment, below)\n segment.status = 'severed'\n body.pieces.push([segment.id, ...fallen])\n const parent = segmentNode(segment.id).parent\n change('severed', segment.id, { below: fallen })\n if (parent) emit({ type: 'detach', joint: `${parent}~${segment.id}`, parts: [parent, segment.id], by: 'break', groups: [[segment.id, ...fallen]] })\n },\n destroy(segment) {\n if (!segment.attached) return\n const fallen = detachBelow(segment, 'detach')\n segment.status = 'destroyed'\n if (fallen.length > 0) body.pieces.push(fallen)\n change('destroyed', segment.id, { below: fallen })\n emit({ type: 'fracture', part: segment.id, style: 'tear', fragments: [], point: pointOf(segment.id) })\n },\n kill(cause) {\n if (!body.alive || !killAllowed(rules.deathRule, cause)) return\n body.alive = false\n change('killed', undefined, { cause })\n },\n limp(side) {\n if (body.limping.includes(side)) return\n body.limping.push(side)\n change('limp')\n },\n crawl() {\n if (body.crawl) return\n body.crawl = true\n change('crawl')\n },\n stun() {\n body.stunSeconds = spec.stunSeconds\n change('stunned')\n },\n bleed({ segment, rate, duration, spurt, point, direction }) {\n const scaled = rate * spec.bloodFactor\n bleeders.push({ part: segment.id, rate: scaled, duration, spurt, age: 0, reported: 0 })\n emit({ type: 'leak', part: segment.id, fluid: 'blood', point: point ?? pointOf(segment.id), direction: direction ?? [0, -1, 0], rate: scaled * BLOOD_LITERS_PER_DROP, pressurized: spurt })\n },\n }\n return ops\n}\n","{\n \"id\": \"physical\",\n \"tissuePerCentimetre\": {\n \"flesh\": 0.25,\n \"bone\": 1\n },\n \"torsoPath\": {\n \"fleshCentimetres\": 22,\n \"boneCentimetres\": 2\n },\n \"regionFactor\": {\n \"head\": 0.7,\n \"limb\": 0.5,\n \"torso\": 1\n },\n \"bruteTorsoFactor\": 2,\n \"energyKept\": \"P / (P + R)\",\n \"points\": {\n \"coefficient\": 3.5,\n \"exponent\": 0.37\n },\n \"tolerance\": {\n \"bodies\": 0,\n \"energyKept\": 0.05\n },\n \"calibration\": [\n {\n \"caliber\": \"9x19\",\n \"force\": 7.67,\n \"bodies\": 2,\n \"energyKeptFirstBody\": 0.51,\n \"expectedBodies\": 2,\n \"bestiaryEnergyKept\": 0.5\n },\n {\n \"caliber\": \"5_56x45\",\n \"force\": 16.56,\n \"bodies\": 3,\n \"energyKeptFirstBody\": 0.69,\n \"expectedBodies\": 3,\n \"bestiaryEnergyKept\": 0.65\n },\n {\n \"caliber\": \"7_62x51\",\n \"force\": 26.67,\n \"bodies\": 4,\n \"energyKeptFirstBody\": 0.78,\n \"expectedBodies\": 4,\n \"bestiaryEnergyKept\": 0.75\n },\n {\n \"caliber\": \"7_62x39\",\n \"force\": 18.52,\n \"bodies\": 3,\n \"energyKeptFirstBody\": 0.71,\n \"expectedBodies\": 3\n },\n {\n \"caliber\": \"12ga_00buck\",\n \"force\": 4.35,\n \"bodies\": 1,\n \"energyKeptFirstBody\": 0.37,\n \"expectedBodies\": 1\n }\n ],\n \"torsoResistance\": 7.5\n}\n","import type { SegmentKind } from '@adatechnology/game-contracts'\n\nimport { SEGMENT_NUMBERS } from '../body/segment-tree'\nimport segments from '../data/segments.json'\nimport physical from '../presets/physical.json'\nimport teamGame from '../presets/team-game.json'\nimport type { BallisticClass, CaliberEntry, DamageRules, ProtectionLayer } from './types'\n\nconst { severity } = segments\n\n/** The kit's rules: Arsenal numbers per segment (A4), the design's severity, the game's head rule, death by vitality or head. */\nexport const DEFAULT_DAMAGE_RULES: DamageRules = {\n deathRule: 'vitality-or-head',\n segmentRule: 'kit',\n severity: { ...severity, stunAtPoints: 35, limbVitalityShare: 0.3, regionLimbVitalityShare: 0.5 },\n blast: { minimumPoints: 8, headDestroyPoints: 40, bigPoints: 120 },\n defaultCut: segments.defaultCut,\n points: physical.points,\n spread: 0,\n segments: SEGMENT_NUMBERS,\n burnPointsPerSecond: 3,\n}\n\n/** The Galeria 3D do Arsenal, replayed: its `damage`, `sever`, `explodeSeg`, `splitTorso`, `legLost` and `grenadeAt`. */\nexport const ARSENAL_RULES: DamageRules = { ...DEFAULT_DAMAGE_RULES, segmentRule: 'arsenal' }\n\nconst ELENCO_HIT_POINTS = segments.elenco.segments as Partial<Record<SegmentKind, { readonly hp: number }>>\n\n/** The Elenco, replayed: its own hit points, no multiplier, ±15 % per blow and its cut rule (`hit`, `sever`, `breakBone`); a torso at 0 kills (a human). */\nexport const ELENCO_RULES: DamageRules = {\n ...DEFAULT_DAMAGE_RULES,\n segmentRule: 'elenco',\n spread: 0.15,\n segments: Object.fromEntries(\n Object.entries(SEGMENT_NUMBERS).map(([kind, numbers]) => [kind, { ...numbers, multiplier: 1, hitPoints: ELENCO_HIT_POINTS[kind as SegmentKind]?.hp ?? numbers.hitPoints }]),\n ) as DamageRules['segments'],\n}\n\nexport const DAMAGE_PRESETS = { kit: DEFAULT_DAMAGE_RULES, arsenal: ARSENAL_RULES, elenco: ELENCO_RULES } as const\n\n/** The protection presets that reproduce the game's `ROUPAS` (tools/characters-catalog/decisions.json `protectionPresets`). */\nexport const PROTECTION_PRESETS: readonly ProtectionLayer[] = [\n { id: 'vest_police', slots: ['torso'], ballistic: { resistance: 14, blunt: 0.3, pass: 0.8 } },\n { id: 'vest_military', slots: ['torso'], ballistic: { resistance: 28, blunt: 0.5 } },\n { id: 'helmet_combat', slots: ['head'], ballistic: { resistance: 9, blunt: 0, pass: 1, consumable: true } },\n { id: 'turnout_gear', slots: ['torso', 'arms', 'legs'], fire: 0.25, limbs: 0.8 },\n]\n\nconst CLASS_OF: Readonly<Record<string, BallisticClass>> = { pistol: 'pistol', rifle: 'rifle' }\n\n/** The game's calibre table (`CALIBRES`) under the kit's calibre ids, for the `team-game` model. */\nexport const TEAM_GAME_CALIBERS: readonly CaliberEntry[] = Object.entries(teamGame.calibers).map(([id, entry]) => ({\n id,\n points: entry.points,\n penetration: entry.penetration,\n class: CLASS_OF[entry.class] ?? 'pistol',\n skullRange: entry.skullRange,\n pellets: 'pellets' in entry ? entry.pellets : 1,\n effectiveRangeMetres: entry.effectiveRangeMetres,\n}))\n\n/** The game's calibre keys (`9x19`, `12ga`, `762x39`, `308`) to the kit's calibre ids. */\nexport const TEAM_GAME_CALIBER_KEYS: Readonly<Record<string, string>> = Object.fromEntries(Object.entries(teamGame.calibers).map(([id, entry]) => [entry.gameKey, id]))\n\nexport const TEAM_GAME_RULES = { regionCost: teamGame.regionCost, damageKeptPerBody: teamGame.damageKeptPerBody, falloff: teamGame.falloff } as const\n\nexport const PHYSICAL_RULES = physical\n","import type { BodyRegion, SegmentKind } from '@adatechnology/game-contracts'\n\nimport { PHYSICAL_RULES, TEAM_GAME_RULES } from './defaults'\nimport type { PenetrationModel } from './types'\n\n/** P = √(E·m/A) against the resistance of the path; a protection layer stops a projectile whose force is not above its resistance. */\nexport const PHYSICAL_PENETRATION: PenetrationModel = {\n id: 'physical',\n stops: (layer, projectile) => projectile.force <= (layer.ballistic?.resistance ?? 0),\n piercesSkull: (projectile, headResistance) => projectile.force > headResistance,\n}\n\n/** The game's rule: pistol-class rounds are stopped by a vest or a helmet; the skull is pierced within the calibre's `cranio` range. */\nexport const TEAM_GAME_PENETRATION: PenetrationModel = {\n id: 'team-game',\n stops: (_layer, projectile) => projectile.class === 'pistol',\n piercesSkull: (projectile) => projectile.skullRange === 1 || projectile.distance <= projectile.skullRange,\n}\n\nexport const PENETRATION_MODELS = { physical: PHYSICAL_PENETRATION, 'team-game': TEAM_GAME_PENETRATION } as const\n\nconst { tissuePerCentimetre, torsoPath, regionFactor } = PHYSICAL_RULES\n\n/** Resistance of a torso path: flesh 22 cm × 0.25 + bone 2 cm × 1.0 = 7.5. */\nexport const TORSO_RESISTANCE = torsoPath.fleshCentimetres * tissuePerCentimetre.flesh + torsoPath.boneCentimetres * tissuePerCentimetre.bone\n\nconst HEAD_KINDS: ReadonlySet<SegmentKind> = new Set(['head', 'jaw', 'neck'])\nconst TORSO_KINDS: ReadonlySet<SegmentKind> = new Set(['torso', 'pelvis'])\n\nexport type ResistanceParams = { readonly kind: SegmentKind; readonly bodyCount: number; readonly protection: number }\n\n/** R of the path through a segment: torso 7.5 (× the body count of a brute), head 0.7×, limbs 0.5×, plus the protection worn over it. */\nexport const segmentResistance = ({ kind, bodyCount, protection }: ResistanceParams): number => {\n if (TORSO_KINDS.has(kind)) return TORSO_RESISTANCE * regionFactor.torso * bodyCount + protection\n if (HEAD_KINDS.has(kind)) return TORSO_RESISTANCE * regionFactor.head + protection\n return TORSO_RESISTANCE * regionFactor.limb + protection\n}\n\n/** Share of the energy a projectile of force `force` keeps after a path of resistance `resistance`: P/(P+R). */\nexport const energyKept = (force: number, resistance: number): number => (force > 0 ? force / (force + resistance) : 0)\n\n/** Penetration points a region costs in the game (`custo`): head 0.7, torso 1 (a brute 2), limb 0.5. */\nexport const teamGameCost = (region: BodyRegion, bodyCount: number): number => {\n const cost = TEAM_GAME_RULES.regionCost\n if (region === 'head') return cost.head\n if (region === 'torso') return bodyCount > 1 ? cost.bruteTorso : cost.torso\n return cost.limb\n}\n\n/** The game's falloff: full damage up to the effective range, then linear down to half at twice the range, never below half. */\nexport const teamGameFalloff = (distance: number, range: number): number => {\n const floor = TEAM_GAME_RULES.falloff.atTwiceRange\n return distance <= range * TEAM_GAME_RULES.falloff.fullAtRange ? 1 : Math.max(floor, 1 - ((distance - range) / range) * (1 - floor))\n}\n","import type { DamageType, SegmentKind, Vec3 } from '@adatechnology/game-contracts'\n\nimport { segmentNode } from '../../body/segment-tree'\nimport type { ResolvedCharacterSpec } from '../../character'\nimport type { SegmentState } from '../../types'\nimport type { BodyOps } from '../body-ops'\nimport type { PenetrationModel, ProjectileInfo } from '../types'\n\n/** One blow on one segment, after protection: what every segment rule receives. */\nexport type SegmentBlow = {\n readonly segment: SegmentState\n /** Points after protection, before the rule's multipliers and spread. */\n readonly points: number\n readonly cut: number\n readonly damageType: DamageType\n readonly projectile?: ProjectileInfo\n readonly explosive: boolean\n /** A blast strong enough to destroy what it takes to 0 (Arsenal `big`). */\n readonly big: boolean\n /** Where it landed (world): a bullet wound bleeds there; a blast share has none. */\n readonly point?: Vec3\n /** Extra factor on arms and legs (age × protection). */\n readonly limbFactor: number\n readonly model: PenetrationModel\n /** Resistance of the head path (the skull rule of the `physical` model). */\n readonly headResistance: number\n}\n\nexport type RuleResult = { readonly headshot: boolean }\n\nexport type SegmentRule = (ops: BodyOps, blow: SegmentBlow) => RuleResult\n\nexport const NO_HEADSHOT: RuleResult = { headshot: false }\nexport const HEADSHOT: RuleResult = { headshot: true }\n\nconst ARM_KINDS: ReadonlySet<SegmentKind> = new Set(['upperArm', 'forearm', 'hand'])\nconst LEG_KINDS: ReadonlySet<SegmentKind> = new Set(['thigh', 'shin', 'foot'])\n\nexport const isArm = (kind: SegmentKind): boolean => ARM_KINDS.has(kind)\nexport const isLeg = (kind: SegmentKind): boolean => LEG_KINDS.has(kind)\nexport const isLimb = (kind: SegmentKind): boolean => isArm(kind) || isLeg(kind)\n\n/** The body type's multiplier for the group the segment is in (head, torso, arms, legs). */\nexport const typeMultiplier = (spec: ResolvedCharacterSpec, kind: SegmentKind): number => {\n if (isArm(kind)) return spec.multipliers.arms\n if (isLeg(kind)) return spec.multipliers.legs\n if (kind === 'torso' || kind === 'pelvis') return spec.multipliers.torso\n return spec.multipliers.head\n}\n\n/** Points the segment takes: the blow × segment multiplier × body type × (on a limb) age and protection. */\nexport const segmentDamage = (ops: BodyOps, blow: SegmentBlow, points: number): number => {\n const { kind } = blow.segment\n return points * ops.rules.segments[kind].multiplier * typeMultiplier(ops.spec, kind) * (isLimb(kind) ? blow.limbFactor : 1)\n}\n\n/** The stump a lost segment leaves bleeds hard (×1.5 at the neck and head) for 45 s, spurting (Arsenal `stumpAt`). */\nexport const bleedStump = (ops: BodyOps, segment: SegmentState): void => {\n const bleed = ops.rules.segments[segment.kind].bleed\n const parent = segmentNode(segment.id).parent\n const stump = parent ? (ops.body.segments.find((candidate) => candidate.id === parent) ?? segment) : segment\n ops.bleed({ segment: stump, rate: bleed * (segment.kind === 'neck' || segment.kind === 'head' ? 1.5 : 1), duration: 45, spurt: true })\n}\n\n/** The piece that flies off bleeds 35 % of the rate for 15 s (Arsenal `sever`). */\nexport const bleedPiece = (ops: BodyOps, segment: SegmentState): void => ops.bleed({ segment, rate: ops.rules.segments[segment.kind].bleed * 0.35, duration: 15, spurt: true })\n\n/** A bullet wound bleeds 30 % of the segment's rate for 10 s, without spurting (Arsenal `damage` with a point). */\nexport const bleedWound = (ops: BodyOps, segment: SegmentState, point: Vec3): void =>\n ops.bleed({ segment, rate: ops.rules.segments[segment.kind].bleed * 0.3, duration: 10, spurt: false, point })\n\n/** Cut off at the joint, with its two bleeders. */\nexport const severWithBleeding = (ops: BodyOps, segment: SegmentState): void => {\n if (!segment.attached) return\n bleedStump(ops, segment)\n ops.sever(segment)\n bleedPiece(ops, segment)\n}\n\n/** Blown to pieces: only the stump bleeds. */\nexport const destroyWithBleeding = (ops: BodyOps, segment: SegmentState): void => {\n if (!segment.attached) return\n bleedStump(ops, segment)\n ops.destroy(segment)\n}\n","import { MISSING_BITS } from '@adatechnology/game-contracts'\nimport type { BodyRegion, DamageType } from '@adatechnology/game-contracts'\n\nimport { segmentOf } from '../../body/body-state'\nimport { REGION_HIT_POINTS, REGION_SEGMENTS } from '../../body/regions'\nimport type { LimbRegion } from '../../body/regions'\nimport type { BodyOps } from '../body-ops'\nimport { spreadPoints } from '../points'\nimport type { ProtectionOutcome } from '../protection'\nimport type { PenetrationModel, ProjectileInfo } from '../types'\nimport { HEADSHOT, NO_HEADSHOT } from './blow'\nimport type { RuleResult } from './blow'\n\n/** One blow on one crowd region. `points` are the blow's own (the game stuns on them); the torso applies the vest share itself. */\nexport type RegionBlow = {\n readonly region: BodyRegion\n readonly points: number\n readonly cut: number\n readonly damageType: DamageType\n readonly projectile?: ProjectileInfo\n readonly protection: ProtectionOutcome\n readonly model: PenetrationModel\n readonly headResistance: number\n}\n\nconst LIMB_BITS: Readonly<Record<Exclude<LimbRegion, 'head'>, { readonly severed: number; readonly broken: number }>> = {\n armL: { severed: MISSING_BITS.armL, broken: MISSING_BITS.armLBroken },\n armR: { severed: MISSING_BITS.armR, broken: MISSING_BITS.armRBroken },\n legL: { severed: MISSING_BITS.legL, broken: MISSING_BITS.legLBroken },\n legR: { severed: MISSING_BITS.legR, broken: MISSING_BITS.legRBroken },\n}\n\nconst rootOf = (ops: BodyOps, region: BodyRegion) => segmentOf(ops.body, REGION_SEGMENTS[region][0] as Parameters<typeof segmentOf>[1])\n\nconst hitHead = (ops: BodyOps, blow: RegionBlow, points: number): RuleResult => {\n const { body, rules } = ops\n const rule = rules.severity\n const multiplier = rules.segments.head.multiplier\n const { projectile } = blow\n const lethal = projectile ? blow.model.piercesSkull(projectile, blow.headResistance) && points >= rule.headKillDamage : points * multiplier >= body.regions.head\n if (lethal) {\n const shotgunClose = projectile !== undefined && projectile.pellets > 1 && projectile.distance < rule.headBitShotgunMetres\n if (points > rule.headBitDamage || shotgunClose) {\n body.missing |= MISSING_BITS.head\n ops.destroy(segmentOf(body, 'head'))\n }\n return HEADSHOT\n }\n body.vitality -= points * multiplier\n body.regions.head -= points\n ops.damage(segmentOf(body, 'head'), points * multiplier, blow.damageType)\n if (points > rule.jawDamage && ops.random() < rule.jawChance && (body.missing & MISSING_BITS.jaw) === 0) {\n body.missing |= MISSING_BITS.jaw\n ops.sever(segmentOf(body, 'jaw'))\n }\n return NO_HEADSHOT\n}\n\nconst hitLimb = (ops: BodyOps, blow: RegionBlow, region: Exclude<LimbRegion, 'head'>, points: number): void => {\n const { body, spec, rules } = ops\n const leg = region === 'legL' || region === 'legR'\n const factor = (leg ? spec.multipliers.legs : spec.multipliers.arms) * spec.limbFactor * blow.protection.limbs\n const amount = points * factor\n body.regions[region] -= amount\n body.vitality -= amount * rules.severity.regionLimbVitalityShare\n const root = rootOf(ops, region)\n ops.damage(root, amount, blow.damageType)\n const max = REGION_HIT_POINTS[region]\n if (leg && body.regions[region] <= max * rules.severity.limpAtHpFraction) ops.limp(region === 'legL' ? 'L' : 'R')\n if (body.regions[region] > 0) return\n const severs = blow.cut >= rules.severity.severAtCut || amount > max * rules.severity.severAtDamageFraction\n const bit = severs ? LIMB_BITS[region].severed : LIMB_BITS[region].broken\n const isNew = (body.missing & bit) === 0\n body.missing |= bit\n if (leg) ops.crawl()\n if (!isNew) return\n if (severs) ops.sever(root)\n else ops.breakBone(root)\n}\n\n/** The game's `zDamage` (G 67011–67045): the crowd model of 6 regions, the bitmask, the helmet, the vest, limp, crawl and the jaw. */\nexport const regionRule = (ops: BodyOps, blow: RegionBlow): RuleResult => {\n const { body, rules } = ops\n const points = spreadPoints(blow.points, rules.spread, ops.random)\n let result = NO_HEADSHOT\n if (blow.region === 'head') result = hitHead(ops, blow, points)\n else if (blow.region === 'torso') {\n const amount = points * ops.spec.multipliers.torso * blow.protection.ballistic\n body.vitality -= amount\n ops.damage(segmentOf(body, 'torso'), amount, blow.damageType)\n } else hitLimb(ops, blow, blow.region, points)\n if (result.headshot) ops.kill('head')\n else if (body.vitality <= 0) ops.kill('vitality')\n if (!body.alive || result.headshot || body.vitality <= 0) return result\n if (points >= rules.severity.stunAtPoints) ops.stun()\n return result\n}\n","import type { DamageType } from '@adatechnology/game-contracts'\n\nimport type { BallisticClass, BodyHit, CaliberEntry, DamageRules, ProjectileInfo } from './types'\n\n/** Deterministic mulberry32 stream in [0, 1). */\nexport const createRng = (seed: number): (() => number) => {\n let state = seed >>> 0\n return () => {\n state = (state + 0x6d2b79f5) >>> 0\n let mixed = Math.imul(state ^ (state >>> 15), state | 1)\n mixed ^= mixed + Math.imul(mixed ^ (mixed >>> 7), mixed | 61)\n return ((mixed ^ (mixed >>> 14)) >>> 0) / 4294967296\n }\n}\n\n/** Damage points of a blow from its energy in joules: `coefficient · E^exponent` (9×19 → 35, 7.62×39 → 59, .308 → 71.7). */\nexport const pointsFromEnergy = (energy: number, rules: Pick<DamageRules, 'points'>): number => (energy > 0 ? rules.points.coefficient * energy ** rules.points.exponent : 0)\n\n/** The caller's points, else the calibre table's, else the energy formula. */\nexport const resolvePoints = (hit: BodyHit, caliber: CaliberEntry | undefined, rules: Pick<DamageRules, 'points'>): number => hit.points ?? caliber?.points ?? pointsFromEnergy(hit.energy ?? 0, rules)\n\n/** One draw of ±`spread` on the points (none when the spread is 0, so the stream is not touched). */\nexport const spreadPoints = (points: number, spread: number, random: () => number): number => (spread > 0 ? points * (1 - spread + random() * 2 * spread) : points)\n\nconst DEFAULT_PROJECTILE_GRAMS = 8\nconst RIFLE_JOULES = 1000\n\n/** P = √(E·m/A): E in joules, m in grams, A the cross-section in mm². */\nexport const penetrationForce = (energy: number, grams: number, diameterMm: number): number => {\n const area = (Math.PI * diameterMm ** 2) / 4\n return area > 0 && energy > 0 ? Math.sqrt((energy * grams) / area) : 0\n}\n\n/** What a ballistic hit is (force, class, skull range, pellets, distance), from the hit and the calibre table. */\nexport const projectileOf = (hit: BodyHit, caliber: CaliberEntry | undefined): ProjectileInfo => {\n const pellets = hit.pellets ?? caliber?.pellets ?? 1\n const energy = hit.energy ?? 0\n const fallback: BallisticClass = energy > RIFLE_JOULES ? 'rifle' : 'pistol'\n return {\n force: penetrationForce(energy, hit.mass ?? DEFAULT_PROJECTILE_GRAMS, hit.caliber ?? 9),\n class: caliber?.class ?? fallback,\n skullRange: caliber?.skullRange ?? 1,\n pellets,\n distance: hit.distance ?? 0,\n }\n}\n\n/** `hit.cut`, else the default of its damage type (a bullet by class: pistol 0.15, buckshot 0.75, rifle 0.45). */\nexport const cutOf = (hit: BodyHit, projectile: ProjectileInfo | undefined, rules: Pick<DamageRules, 'defaultCut'>): number => {\n if (hit.cut !== undefined) return hit.cut\n const type: DamageType = hit.type\n if (type === 'ballistic' && projectile) return rules.defaultCut.ballistic[projectile.pellets > 1 ? 'buckshot' : projectile.class]\n if (type === 'slash' || type === 'blunt' || type === 'explosive') return rules.defaultCut[type]\n return 0\n}\n","import type { Vec3 } from '@adatechnology/game-contracts'\n\nimport { REGION_SEGMENTS, BODY_REGIONS } from '../body/regions'\nimport { segmentNode } from '../body/segment-tree'\nimport type { SegmentId } from '../body/segment-tree'\nimport { distance } from '../body/transform'\nimport type { BodyOps } from './body-ops'\nimport { HEADSHOT, NO_HEADSHOT, destroyWithBleeding } from './rules/blow'\nimport type { RuleResult, SegmentBlow, SegmentRule } from './rules/blow'\nimport { regionRule } from './rules/regions'\nimport type { RegionBlow } from './rules/regions'\n\nexport type BlastParams = {\n readonly ops: BodyOps\n /** Centre of the blast in the body's local space. */\n readonly center: Vec3\n readonly points: number\n readonly radius: number\n /** Inside this distance a segment takes the whole blast (lethal); from it to `radius` it takes at most `blast.injuryPoints`, falling off to nothing at `radius`. Without it: `points × (1 − distance/radius)`. */\n readonly lethalRadius?: number\n /** A joint position in local space. */\n readonly jointOf: (id: SegmentId) => Vec3\n}\n\n/** Points per segment at the edge of the lethal radius when the rules name none: a person there is left badly hurt, not dead. */\nconst INJURY_POINTS = 15\n\n/** The points of a blast on a segment `metres` from its centre: `points × (1 − distance/radius)`; with a `lethalRadius`, all of them inside it and, beyond it, the injury share that falls off to the edge. */\nconst pointsAt = (params: BlastParams, metres: number): number => {\n const { points, radius, lethalRadius, ops } = params\n if (lethalRadius === undefined || lethalRadius >= radius) return points * Math.max(0, 1 - metres / radius)\n if (metres <= lethalRadius) return points\n const injury = Math.min(points, ops.rules.blast.injuryPoints ?? INJURY_POINTS)\n return injury * Math.max(0, 1 - (metres - lethalRadius) / (radius - lethalRadius))\n}\n\n/** The share of a blast each target takes (`pointsAt`), kept above the minimum, strongest first (Arsenal `grenadeAt`). */\nconst shares = <TTarget>(params: BlastParams, targets: readonly TTarget[], pivot: (target: TTarget) => SegmentId): { target: TTarget; points: number }[] =>\n targets\n .map((target) => ({ target, points: pointsAt(params, distance(params.jointOf(pivot(target)), params.center)) }))\n .filter((share) => share.points > params.ops.rules.blast.minimumPoints)\n .sort((a, b) => b.points - a.points)\n\nexport type SegmentBlastParams = BlastParams & { readonly rule: SegmentRule; readonly blow: Omit<SegmentBlow, 'segment' | 'points' | 'big' | 'explosive' | 'point'> }\n\n/** A blast on the 17 segments: a strong share on the head blows it off; every other share goes through the segment rule as an explosive blow. */\nexport const segmentBlast = (params: SegmentBlastParams): RuleResult => {\n const { ops, rule, blow } = params\n const { blast, segments } = ops.rules\n let result = NO_HEADSHOT\n for (const { target, points } of shares(params, ops.body.segments.filter((segment) => segment.attached), (segment) => segment.id)) {\n if (!target.attached) continue\n if (target.kind === 'head' && points > blast.headDestroyPoints) {\n destroyWithBleeding(ops, target)\n ops.kill('explosion')\n result = HEADSHOT\n continue\n }\n rule(ops, { ...blow, segment: target, points: points / segments[target.kind].multiplier, big: points > blast.bigPoints, explosive: true })\n }\n return result\n}\n\nexport type RegionBlastParams = BlastParams & { readonly blow: Omit<RegionBlow, 'region' | 'points'> }\n\n/** A blast on the 6 crowd regions, each measured at the joint of the segment that names it. */\nexport const regionBlast = (params: RegionBlastParams): RuleResult => {\n let result = NO_HEADSHOT\n const pivot = (region: (typeof BODY_REGIONS)[number]): SegmentId => segmentNode(REGION_SEGMENTS[region][0] as SegmentId).id\n for (const { target, points } of shares(params, BODY_REGIONS, pivot)) {\n if (regionRule(params.ops, { ...params.blow, region: target, points }).headshot) result = HEADSHOT\n }\n return result\n}\n","import type { BodySlot } from '@adatechnology/game-contracts'\n\nimport type { PenetrationModel, ProjectileInfo, ProtectionLayer } from './types'\n\n/** The layered protection of T3: `1 − Π(1 − value·condition)`, at most `cap`. */\nexport const LAYERED_PROTECTION_CAP = 0.9\n\nexport type LayeredProtectionParams = {\n readonly layers: readonly ProtectionLayer[]\n readonly attack: 'bite' | 'scratch'\n /** 0..1 condition per layer id (worn, torn); 1 by default. */\n readonly conditions?: Readonly<Record<string, number>>\n}\n\nexport const layeredProtection = ({ layers, attack, conditions }: LayeredProtectionParams): number => {\n const through = layers.reduce((left, layer) => left * (1 - (layer[attack] ?? 0) * (conditions?.[layer.id] ?? 1)), 1)\n return Math.min(LAYERED_PROTECTION_CAP, 1 - through)\n}\n\n/** The layers worn that cover any of `slots` and are not used up. */\nexport const layersOn = (worn: readonly ProtectionLayer[], slots: readonly BodySlot[], consumed: ReadonlySet<string>): ProtectionLayer[] =>\n worn.filter((layer) => !consumed.has(layer.id) && layer.slots.some((slot) => slots.includes(slot)))\n\n/** What the layers over a segment do to one blow. */\nexport type ProtectionOutcome = {\n /** A consumable layer took the whole projectile (a helmet): no damage, and the layer is used up. */\n readonly absorbedBy?: string\n /** Share of the points that gets through the ballistic layers. */\n readonly ballistic: number\n /** Penetration resistance the layers add (the `physical` model). */\n readonly resistance: number\n /** Factor on limb damage (a turnout coat). */\n readonly limbs: number\n /** Factor on fire damage. */\n readonly fire: number\n}\n\nexport type ProtectParams = {\n readonly layers: readonly ProtectionLayer[]\n readonly projectile?: ProjectileInfo\n readonly model: PenetrationModel\n}\n\n/** A stopped projectile does the layer's `blunt` share; one that goes through, `pass` (default `blunt`). Non-projectiles ignore ballistic layers. */\nexport const protect = ({ layers, projectile, model }: ProtectParams): ProtectionOutcome => {\n let ballistic = 1\n let resistance = 0\n for (const layer of layers) {\n const armour = layer.ballistic\n if (!armour || !projectile) continue\n const stops = model.stops(layer, projectile)\n if (stops && armour.consumable) return { absorbedBy: layer.id, ballistic: 0, resistance: armour.resistance, limbs: 1, fire: 1 }\n ballistic *= stops ? armour.blunt : (armour.pass ?? armour.blunt)\n resistance += armour.resistance\n }\n const limbs = layers.reduce((factor, layer) => factor * (layer.limbs ?? 1), 1)\n const fire = layers.reduce((factor, layer) => factor * (layer.fire ?? 1), 1)\n return { ballistic, resistance, limbs, fire }\n}\n","import type { BodyRegion, DestructibleSpec, Vec3 } from '@adatechnology/game-contracts'\n\nimport type { BodyState } from '../body/body-state'\nimport { isBodyRegion, REGION_SEGMENTS } from '../body/regions'\nimport { isSegmentId } from '../body/segment-tree'\nimport type { SegmentId } from '../body/segment-tree'\nimport { distance } from '../body/transform'\n\nconst radiusOf = (part: DestructibleSpec['parts'][number]): number => (part.shape.kind === 'box' ? Math.min(...part.shape.size) / 2 : part.shape.radius)\n\n/**\n * The segment a hit lands on: `hit.part` when the caller resolved it (a segment id, or a region id for its first segment), else the attached\n * part whose surface is nearest to the local point.\n */\nexport const resolveSegment = (body: BodyState, spec: DestructibleSpec, part: string | undefined, localPoint: Vec3): SegmentId => {\n if (part !== undefined && isSegmentId(part)) return part\n if (part !== undefined && isBodyRegion(part)) return REGION_SEGMENTS[part][0] as SegmentId\n let best: SegmentId = 'torso'\n let bestGap = Infinity\n for (const candidate of spec.parts) {\n const segment = body.segments.find((entry) => entry.id === candidate.id)\n if (!segment?.attached) continue\n const gap = distance(candidate.position, localPoint) - radiusOf(candidate)\n if (gap < bestGap) [best, bestGap] = [segment.id, gap]\n }\n return best\n}\n\nexport const regionOfSegment = (body: BodyState, id: SegmentId): BodyRegion => body.segments.find((segment) => segment.id === id)?.region ?? 'torso'\n","import { segmentOf } from '../../body/body-state'\nimport type { SegmentState } from '../../types'\nimport type { BodyOps } from '../body-ops'\nimport { HEADSHOT, NO_HEADSHOT, bleedWound, destroyWithBleeding, segmentDamage, severWithBleeding } from './blow'\nimport type { SegmentBlow, SegmentRule } from './blow'\n\n/** A blow of at least this many points blows the head off (Arsenal `damage`: `dmg >= 30`). */\nconst HEAD_BLOW_POINTS = 30\nconst LIMB_VITALITY_SHARE = 0.3\n\nconst split = (ops: BodyOps): void => {\n const torso = segmentOf(ops.body, 'torso')\n if (!torso.attached) return\n severWithBleeding(ops, torso)\n ops.change('split', 'torso')\n ops.kill('vitality')\n}\n\nconst loseLimb = (ops: BodyOps, segment: SegmentState, blow: SegmentBlow): void => {\n if (blow.big || -segment.hp > segment.max) destroyWithBleeding(ops, segment)\n else severWithBleeding(ops, segment)\n const { body } = ops\n if (segment.kind === 'thigh' || segment.kind === 'shin') {\n if (body.alive) ops.crawl()\n } else if (segment.kind === 'foot' && body.alive && !body.crawl && body.limping.length === 0 && segment.side) ops.limp(segment.side)\n}\n\n/** The Galeria 3D do Arsenal's `damage`: every number and branch of the page, in its order. */\nexport const arsenalRule: SegmentRule = (ops, blow) => {\n const { segment } = blow\n if (!segment.attached) return NO_HEADSHOT\n const amount = segmentDamage(ops, blow, blow.points)\n if (blow.point) bleedWound(ops, segment, blow.point)\n segment.hp -= amount\n ops.damage(segment, amount, blow.damageType)\n let result = NO_HEADSHOT\n if (segment.kind === 'head') {\n if (blow.points >= HEAD_BLOW_POINTS || segment.hp <= 0) {\n destroyWithBleeding(ops, segment)\n ops.kill('head')\n result = HEADSHOT\n } else ops.wound(segment)\n } else if (segment.kind === 'jaw') {\n if (segment.hp <= 0) severWithBleeding(ops, segment)\n } else if (segment.kind === 'neck') {\n if (segment.hp <= 0) {\n severWithBleeding(ops, segment)\n ops.kill('head')\n }\n } else if (segment.kind === 'torso' || segment.kind === 'pelvis') {\n ops.body.vitality -= amount\n if (blow.points >= ops.rules.severity.splitAtDamage || segment.hp <= 0) split(ops)\n } else {\n ops.body.vitality -= amount * LIMB_VITALITY_SHARE\n if (segment.hp <= 0) loseLimb(ops, segment, blow)\n }\n if (ops.body.alive && ops.body.vitality <= 0) ops.kill('vitality')\n return result\n}\n","import type { SegmentKind } from '@adatechnology/game-contracts'\n\nimport { spreadPoints } from '../points'\nimport { HEADSHOT, NO_HEADSHOT, isLeg, segmentDamage } from './blow'\nimport type { SegmentRule } from './blow'\n\n/** A head at 0 is cut off by an edge of at least this `cut` or a blow of at least this many points (Elenco `hit`). */\nconst HEAD_SEVER_CUT = 0.7\nconst HEAD_SEVER_POINTS = 90\n/** The segments that hang from a joint of their own in the Elenco (`s.node`): they are cut or break at it. */\nconst JOINTED: ReadonlySet<SegmentKind> = new Set(['head', 'upperArm', 'forearm', 'hand', 'thigh', 'shin', 'foot'])\n\n/** The Elenco's `hit`, `sever` and `breakBone`: one ±15 % draw per blow, cut at the joint above, broken bones dangle or make the leg limp. */\nexport const elencoRule: SegmentRule = (ops, blow) => {\n const { segment } = blow\n const { body, rules } = ops\n if (!segment.attached || (!body.alive && segment.kind !== 'head')) return NO_HEADSHOT\n const amount = segmentDamage(ops, blow, spreadPoints(blow.points, rules.spread, ops.random))\n segment.hp = Math.max(0, segment.hp - amount)\n ops.damage(segment, amount, blow.damageType)\n if (segment.hp > 0) {\n ops.wound(segment)\n return NO_HEADSHOT\n }\n if (segment.kind === 'head') {\n if (blow.cut >= HEAD_SEVER_CUT || blow.points >= HEAD_SEVER_POINTS) ops.sever(segment, 'zero')\n else ops.breakBone(segment)\n ops.kill('head')\n return HEADSHOT\n }\n if (JOINTED.has(segment.kind)) {\n if (blow.cut >= rules.severity.severAtCut || amount >= segment.max * rules.severity.severAtDamageFraction) {\n ops.sever(segment, 'zero')\n if (isLeg(segment.kind)) ops.crawl()\n return NO_HEADSHOT\n }\n ops.breakBone(segment)\n if (isLeg(segment.kind) && segment.side) {\n ops.limp(segment.side)\n if (body.limping.length === 2) ops.crawl()\n }\n return NO_HEADSHOT\n }\n ops.breakBone(segment)\n if (segment.kind === 'torso') ops.kill('vitality')\n else if (segment.kind === 'pelvis') ops.crawl()\n else if (segment.kind === 'neck') ops.kill('head')\n return NO_HEADSHOT\n}\n","import { segmentOf } from '../../body/body-state'\nimport type { SegmentState } from '../../types'\nimport type { BodyOps } from '../body-ops'\nimport { spreadPoints } from '../points'\nimport { HEADSHOT, NO_HEADSHOT, bleedWound, destroyWithBleeding, isLeg, segmentDamage, severWithBleeding } from './blow'\nimport type { RuleResult, SegmentBlow, SegmentRule } from './blow'\n\ntype Outcome = 'destroyed' | 'severed' | 'broken'\n\n/** At 0: destroyed by a blast or a blow of twice the segment; severed by an edge (`cut` ≥ 0.5) or a blow above 90 % of it; else broken. */\nexport const severityOf = (ops: BodyOps, blow: SegmentBlow, amount: number, max: number): Outcome => {\n const rule = ops.rules.severity\n if (blow.explosive || amount >= max * rule.destroyAtDamageMultiple) return 'destroyed'\n if (blow.cut >= rule.severAtCut || amount > max * rule.severAtDamageFraction) return 'severed'\n return 'broken'\n}\n\nconst lose = (ops: BodyOps, segment: SegmentState, outcome: Outcome): void => {\n if (outcome === 'destroyed') destroyWithBleeding(ops, segment)\n else if (outcome === 'severed') severWithBleeding(ops, segment)\n else ops.breakBone(segment)\n}\n\n/** The game's head rule: a projectile kills when it pierces the skull with enough points, a blow when it takes the head's hit points. */\nconst hitHead = (ops: BodyOps, blow: SegmentBlow, points: number, amount: number, hpBefore: number): RuleResult => {\n const { segment, projectile } = blow\n const rule = ops.rules.severity\n const lethal = projectile ? blow.model.piercesSkull(projectile, blow.headResistance) && points >= rule.headKillDamage : amount >= hpBefore\n if (lethal) {\n const shotgunClose = projectile !== undefined && projectile.pellets > 1 && projectile.distance < rule.headBitShotgunMetres\n if (blow.explosive || points > rule.headBitDamage || shotgunClose) destroyWithBleeding(ops, segment)\n else ops.breakBone(segment)\n ops.kill('head')\n return HEADSHOT\n }\n ops.body.vitality -= amount\n ops.wound(segment)\n if (points > rule.jawDamage && ops.random() < rule.jawChance) severWithBleeding(ops, segmentOf(ops.body, 'jaw'))\n return NO_HEADSHOT\n}\n\nconst hitTorso = (ops: BodyOps, points: number, amount: number, segment: SegmentState): void => {\n ops.body.vitality -= amount\n if (points < ops.rules.severity.splitAtDamage && segment.hp > 0) return ops.wound(segment)\n const torso = segmentOf(ops.body, 'torso')\n if (!torso.attached) return\n severWithBleeding(ops, torso)\n ops.change('split', 'torso')\n if (ops.rules.deathRule === 'head-only') ops.crawl()\n else ops.kill('vitality')\n}\n\nconst hitLimb = (ops: BodyOps, blow: SegmentBlow, amount: number, hpBefore: number): void => {\n const { segment } = blow\n const rule = ops.rules.severity\n ops.body.vitality -= amount * rule.limbVitalityShare\n if (isLeg(segment.kind) && segment.side && segment.hp <= segment.max * rule.limpAtHpFraction) ops.limp(segment.side)\n if (segment.hp > 0 || hpBefore <= 0) return ops.wound(segment)\n const outcome = severityOf(ops, blow, amount, segment.max)\n lose(ops, segment, outcome)\n if (segment.kind === 'thigh' || segment.kind === 'shin') ops.crawl()\n}\n\n/** The kit's rule: Arsenal numbers, the design's severity, the game's head rule, the death rule of the kit. */\nexport const kitRule: SegmentRule = (ops, blow) => {\n const { segment } = blow\n if (!segment.attached) return NO_HEADSHOT\n const points = spreadPoints(blow.points, ops.rules.spread, ops.random)\n const amount = segmentDamage(ops, blow, points)\n if (blow.point && !blow.explosive) bleedWound(ops, segment, blow.point)\n const hpBefore = segment.hp\n segment.hp -= amount\n ops.damage(segment, amount, blow.damageType)\n let result = NO_HEADSHOT\n if (segment.kind === 'head') result = hitHead(ops, blow, points, amount, hpBefore)\n else if (segment.kind === 'torso' || segment.kind === 'pelvis') hitTorso(ops, points, amount, segment)\n else if (segment.kind === 'jaw' || segment.kind === 'neck') {\n if (segment.hp <= 0 && hpBefore > 0) {\n lose(ops, segment, severityOf(ops, blow, amount, segment.max))\n if (segment.kind === 'neck' && !segment.attached) ops.kill('head')\n } else ops.wound(segment)\n } else hitLimb(ops, blow, amount, hpBefore)\n if (ops.body.vitality <= 0) ops.kill('vitality')\n if (ops.body.alive && points >= ops.rules.severity.stunAtPoints) ops.stun()\n return result\n}\n","import type { DestructibleSpec, Vec3 } from '@adatechnology/game-contracts'\n\nimport { segmentOf } from '../body/body-state'\nimport { REGION_SEGMENTS } from '../body/regions'\nimport { segmentNode } from '../body/segment-tree'\nimport type { SegmentId } from '../body/segment-tree'\nimport { regionBlast, segmentBlast } from './blast'\nimport type { BodyOps } from './body-ops'\nimport type { DamageSetup } from './damage-character'\nimport { energyKept, segmentResistance } from './penetration'\nimport { cutOf, pointsFromEnergy, projectileOf, resolvePoints } from './points'\nimport { layersOn, protect } from './protection'\nimport type { ProtectionOutcome } from './protection'\nimport { resolveSegment, regionOfSegment } from './resolve-part'\nimport { arsenalRule } from './rules/arsenal'\nimport type { RuleResult, SegmentRule } from './rules/blow'\nimport { elencoRule } from './rules/elenco'\nimport { kitRule } from './rules/kit'\nimport { regionRule } from './rules/regions'\nimport type { BodyHit, HitReport, ProtectionLayer } from './types'\n\nconst SEGMENT_RULES: Readonly<Record<string, SegmentRule>> = { kit: kitRule, arsenal: arsenalRule, elenco: elencoRule }\nconst HEAD_RESISTANCE = segmentResistance({ kind: 'head', bodyCount: 1, protection: 0 })\nconst NO_PROTECTION: ProtectionOutcome = { ballistic: 1, resistance: 0, limbs: 1, fire: 1 }\nconst ABSORBED_SPARKS = 8\nconst METRES_PER_MM = 0.001\n\nexport type ApplyBlowParams = {\n readonly ops: BodyOps\n readonly hit: BodyHit\n readonly setup: DamageSetup\n readonly worn: readonly ProtectionLayer[]\n readonly localPoint: Vec3\n readonly jointOf: (id: SegmentId) => Vec3\n readonly describe: () => DestructibleSpec\n}\n\nconst report = (ops: BodyOps, fields: Partial<HitReport> & { points: number }, result: RuleResult = { headshot: false }): HitReport => ({\n through: false,\n exitEnergy: 0,\n ...fields,\n killed: !ops.body.alive,\n headshot: result.headshot,\n})\n\n/** Fire and heat: the exposure burns the torso (`burnPointsPerSecond` × seconds), less what the clothing keeps off. */\nconst burn = ({ ops, hit, setup, worn }: ApplyBlowParams): HitReport => {\n const torso = segmentOf(ops.body, 'torso')\n const fire = protect({ layers: layersOn(worn, segmentNode('torso').slots, ops.body.consumed), model: setup.model }).fire\n const points = setup.rules.burnPointsPerSecond * (hit.duration ?? 0) * fire\n ops.body.vitality -= points\n ops.damage(torso, points, hit.type)\n if (ops.body.vitality <= 0) ops.kill('vitality')\n return report(ops, { part: 'torso', points })\n}\n\nconst blast = ({ ops, hit, setup, localPoint, jointOf }: ApplyBlowParams): HitReport => {\n const points = hit.points ?? pointsFromEnergy(hit.energy ?? 0, setup.rules)\n const base = { ops, center: localPoint, points, radius: hit.radius ?? 1, ...(hit.lethalRadius !== undefined ? { lethalRadius: hit.lethalRadius } : {}), jointOf }\n const common = { cut: hit.cut ?? setup.rules.defaultCut.explosive, damageType: hit.type, model: setup.model, headResistance: HEAD_RESISTANCE }\n const rule = SEGMENT_RULES[setup.rules.segmentRule] ?? kitRule\n const result =\n ops.body.detail === 'regions'\n ? regionBlast({ ...base, blow: { ...common, protection: NO_PROTECTION } })\n : segmentBlast({ ...base, rule, blow: { ...common, limbFactor: ops.spec.limbFactor } })\n return report(ops, { points }, result)\n}\n\nconst DEFAULT_PATH_METRES = 0.2\n\n/** Length of the path through a part (its diameter), for the exit point of a projectile that goes through. */\nconst pathThrough = (spec: DestructibleSpec, id: SegmentId): number => {\n const shape = spec.parts.find((part) => part.id === id)?.shape\n return shape && shape.kind !== 'box' ? 2 * shape.radius : DEFAULT_PATH_METRES\n}\n\nconst exitOf = (hit: BodyHit, depth: number): Vec3 => {\n const [x, y, z] = hit.direction ?? [0, 0, -1]\n return [hit.point[0] + x * depth, hit.point[1] + y * depth, hit.point[2] + z * depth]\n}\n\n/** Bullets, blades and blunt blows: resolve the segment, the protection, the penetration, the points; then the detail's rule decides. */\nconst direct = ({ ops, hit, setup, worn, localPoint, describe }: ApplyBlowParams): HitReport => {\n const { body, spec, rules } = ops\n const caliber = hit.caliberId === undefined ? undefined : setup.calibers.get(hit.caliberId)\n const projectile = hit.type === 'ballistic' ? projectileOf(hit, caliber) : undefined\n const id = resolveSegment(body, describe(), hit.part, localPoint)\n const region = regionOfSegment(body, id)\n const slots = segmentNode(body.detail === 'regions' ? (REGION_SEGMENTS[region][0] as SegmentId) : id).slots\n const protection = protect({ layers: layersOn(worn, slots, body.consumed), model: setup.model, ...(projectile ? { projectile } : {}) })\n if (protection.absorbedBy) {\n body.consumed.add(protection.absorbedBy)\n ops.emit({ type: 'debris', part: id, kind: 'sparks', point: hit.point, normal: [0, 1, 0], count: ABSORBED_SPARKS, speed: 3 })\n return report(ops, { part: id, points: 0, absorbedBy: protection.absorbedBy })\n }\n const rawPoints = resolvePoints(hit, caliber, rules)\n const points = rawPoints * protection.ballistic\n const cut = cutOf(hit, projectile, rules)\n const segment = segmentOf(body, id)\n const resistance = segmentResistance({ kind: segment.kind, bodyCount: spec.bodyCount, protection: protection.resistance })\n const through = setup.model.id === 'physical' && projectile !== undefined && projectile.force > resistance\n const exitEnergy = through && projectile ? (hit.energy ?? 0) * energyKept(projectile.force, resistance) : 0\n if (projectile) {\n const depth = pathThrough(describe(), id)\n ops.emit({ type: 'hole', part: id, hole: { point: localPoint, normal: [0, 0, 1], diameter: (hit.caliber ?? 9) * METRES_PER_MM, through }, worldPoint: hit.point, ...(through ? { exitPoint: exitOf(hit, depth) } : {}) })\n }\n const common = { points, cut, damageType: hit.type, model: setup.model, headResistance: HEAD_RESISTANCE, ...(projectile ? { projectile } : {}) }\n const result =\n body.detail === 'regions'\n ? regionRule(ops, { ...common, points: rawPoints, region, protection })\n : (SEGMENT_RULES[rules.segmentRule] ?? kitRule)(ops, {\n ...common,\n segment,\n explosive: false,\n big: points > rules.blast.bigPoints,\n limbFactor: spec.limbFactor * protection.limbs,\n ...(hit.woundAtPoint === false ? {} : { point: hit.point }),\n })\n return report(ops, { part: id, points, through, exitEnergy }, result)\n}\n\nexport const applyBlow = (params: ApplyBlowParams): HitReport => {\n if (params.hit.type === 'fire' || params.hit.type === 'heat') return burn(params)\n if (params.hit.type === 'explosive') return blast(params)\n return direct(params)\n}\n","import type { DestructibleImpl, DestructionEvent, Hit, Vec3 } from '@adatechnology/game-contracts'\n\nimport type { BodyState } from '../body/body-state'\nimport { segmentNode } from '../body/segment-tree'\nimport type { SegmentId, SegmentSide } from '../body/segment-tree'\nimport { toLocal, toWorld } from '../body/transform'\nimport { characterView } from '../character'\nimport type { Character, CharacterCore } from '../character'\nimport { stepBleeding } from './bleeding'\nimport type { Bleeder } from './bleeding'\nimport { createBodyOps } from './body-ops'\nimport type { BodyOps } from './body-ops'\nimport { segmentResistance } from './penetration'\nimport { applyBlow } from './pipeline'\nimport { createRng } from './points'\nimport { layeredProtection, layersOn } from './protection'\nimport { resolveSegment } from './resolve-part'\nimport type { BodyHit, CaliberEntry, DamageRules, HitReport, PenetrationModel, ProtectionLayer } from './types'\n\n/** A character with the damage model: a `DestructibleImpl` a destruction world can host next to its own objects. */\nexport type DamageCharacter = Character &\n DestructibleImpl & {\n readonly alive: boolean\n /** The sides that limp, in the order they started. */\n readonly limping: readonly SegmentSide[]\n /** Seconds of stagger left from a heavy hit. */\n readonly stunSeconds: number\n /** What the last `hit` did (points, kill, headshot, through). */\n readonly lastHit?: HitReport\n /** The bleeding wounds, oldest first. */\n bleeders(): readonly Readonly<Bleeder>[]\n /** 0..1 protection of the layers over a segment against a bite or a scratch (the layered T3 formula, capped at 90 %). */\n protectionAgainst(attack: 'bite' | 'scratch', part: SegmentId): number\n /** Ids of the protection layers worn and not used up (a helmet that took its shot is gone). */\n intactProtection(): readonly string[]\n /** Penetration resistance R of the path through a part (a segment or a region id), with the protection worn over it. */\n resistanceOf(part: string): number\n hit(hit: Hit | BodyHit): DestructionEvent[]\n /** Ages bleeders (a `leak` per second each) and the stun. */\n update(dt: number): DestructionEvent[]\n serialize(): unknown\n load(data: unknown): void\n }\n\nexport type DamageSetup = {\n readonly rules: DamageRules\n readonly model: PenetrationModel\n readonly protection: ReadonlyMap<string, ProtectionLayer>\n readonly calibers: ReadonlyMap<string, CaliberEntry>\n}\n\ntype Saved = { body: Omit<BodyState, 'consumed'> & { consumed: string[] }; bleeders: Bleeder[]; hitCounter: number; clock: number }\n\nexport const createDamageCharacter = (core: CharacterCore, setup: DamageSetup): DamageCharacter => {\n const view = characterView(core)\n const worn = core.spec.protection.map((id) => setup.protection.get(id)).filter((layer): layer is ProtectionLayer => layer !== undefined)\n let bleeders: Bleeder[] = []\n let hitCounter = 0\n let clock = 0\n let lastHit: HitReport | undefined\n const transform = () => ({ position: core.position, rotation: core.rotation })\n const jointOf = (id: SegmentId): Vec3 => core.rig.joints[segmentNode(id).joint]\n const pointOf = (id: SegmentId): Vec3 => toWorld(transform(), jointOf(id))\n const opsFor = (random: () => number, source?: string): BodyOps =>\n createBodyOps({ body: core.body, spec: core.spec, rules: setup.rules, random, time: clock, pointOf, bleeders, ...(source !== undefined ? { source } : {}) })\n\n const character: DamageCharacter = {\n ...view,\n get segments() {\n return core.body.segments\n },\n get detail() {\n return core.body.detail\n },\n get alive() {\n return core.body.alive\n },\n get limping() {\n return [...core.body.limping]\n },\n get stunSeconds() {\n return core.body.stunSeconds\n },\n get lastHit() {\n return lastHit\n },\n name: core.spec.kind,\n hit(raw) {\n const hit = raw as BodyHit\n hitCounter += 1\n const random = core.spec.random ?? createRng((core.spec.seed ^ hitCounter) >>> 0)\n const ops = opsFor(random, hit.source)\n core.invalidateState()\n lastHit = applyBlow({ ops, hit, setup, worn, localPoint: toLocal(transform(), hit.point), jointOf, describe: core.describe })\n core.invalidateState()\n return ops.events\n },\n update(dt) {\n clock += dt\n core.body.stunSeconds = Math.max(0, core.body.stunSeconds - dt)\n const ops = opsFor(() => 0)\n stepBleeding({ bleeders, dt, emit: ops.emit, pointOf })\n return ops.events\n },\n bleeders: () => bleeders.map((bleeder) => ({ ...bleeder })),\n intactProtection: () => core.spec.protection.filter((id) => !core.body.consumed.has(id)),\n resistanceOf(part) {\n const id = resolveSegment(core.body, core.describe(), part, [0, 0, 0])\n const layers = layersOn(worn, segmentNode(id).slots, core.body.consumed)\n const protection = layers.reduce((sum, layer) => sum + (layer.ballistic?.resistance ?? 0), 0)\n return segmentResistance({ kind: segmentNode(id).kind, bodyCount: core.spec.bodyCount, protection })\n },\n protectionAgainst: (attack, part) => layeredProtection({ layers: layersOn(worn, segmentNode(part).slots, core.body.consumed), attack }),\n reset() {\n core.resetBody()\n bleeders = []\n hitCounter = 0\n lastHit = undefined\n },\n serialize(): Saved {\n const { consumed, ...rest } = core.body\n return { body: { ...structuredClone(rest), consumed: [...consumed] }, bleeders: structuredClone(bleeders), hitCounter, clock }\n },\n load(data) {\n const saved = data as Saved\n Object.assign(core.body, structuredClone(saved.body), { consumed: new Set(saved.body.consumed) })\n bleeders = structuredClone(saved.bleeders)\n hitCounter = saved.hitCounter\n clock = saved.clock\n core.invalidateState()\n },\n }\n return character\n}\n","import type { DestructionEvent, Vec3 } from '@adatechnology/game-contracts'\n\nimport { isBodyRegion } from '../body/regions'\nimport { isSegmentId, segmentNode } from '../body/segment-tree'\nimport type { DamageCharacter } from './damage-character'\nimport { TEAM_GAME_RULES } from './defaults'\nimport { energyKept, teamGameCost, teamGameFalloff } from './penetration'\nimport { penetrationForce } from './points'\nimport type { CaliberEntry } from './types'\n\n/** One body in the line of fire, `distance` metres from the muzzle. */\nexport type LineTarget = { readonly character: DamageCharacter; readonly part: string; readonly distance: number; readonly point?: Vec3 }\n\n/** What the line needs from a shot (a `Shot` of game-weapons fits). */\nexport type LineShot = {\n readonly energyAt: (distance: number) => number\n /** Projectile grams. */\n readonly mass: number\n /** Diameter in mm. */\n readonly caliber: number\n readonly caliberId?: string\n readonly source?: string\n}\n\nexport type LineHit = { readonly index: number; readonly points: number; readonly energy: number; readonly killed: boolean; readonly headshot: boolean; readonly events: DestructionEvent[] }\nexport type LineResult = { readonly hits: LineHit[]; /** 1-based index of the last body hit (0: none). */ readonly stoppedAt: number }\n\nexport type PenetrateLineParams = {\n readonly model: 'physical' | 'team-game'\n readonly shot: LineShot\n readonly targets: readonly LineTarget[]\n readonly calibers: ReadonlyMap<string, CaliberEntry>\n}\n\n/** After a kill the game restarts the ray 0.3 m past the body and measures the next distances from there (`shootRay`). */\nconst RESTART_METRES = 0.3\n\nconst regionOfPart = (part: string) => (isSegmentId(part) ? segmentNode(part).region : isBodyRegion(part) ? part : 'torso')\n\nconst lineHit = (index: number, target: LineTarget, fields: { points: number; energy: number; events: DestructionEvent[] }): LineHit => ({\n index,\n ...fields,\n killed: !target.character.alive,\n headshot: target.character.lastHit?.headshot ?? false,\n})\n\n/** The game's `shootRay`: penetration points spent per body (head 0.7, torso 1, a brute 2, limb 0.5), 75 % of the damage carried on. */\nconst teamGameLine = ({ shot, targets, calibers }: PenetrateLineParams): LineResult => {\n const caliber = shot.caliberId === undefined ? undefined : calibers.get(shot.caliberId)\n if (caliber?.penetration === undefined || caliber.points === undefined) throw new Error(`game-characters: the team-game line needs a calibre with points and penetration (\"${shot.caliberId}\")`)\n let penetration = caliber.penetration\n let points = caliber.points\n let origin = 0\n const hits: LineHit[] = []\n for (const [index, target] of targets.entries()) {\n if (!target.character.alive || target.distance - origin <= 0) continue\n const distance = target.distance - origin\n const dealt = points * teamGameFalloff(distance, caliber.effectiveRangeMetres ?? Infinity)\n const events = target.character.hit({ type: 'ballistic', point: target.point ?? [0, 0, 0], part: target.part, points: dealt, caliberId: caliber.id, distance, ...(shot.source ? { source: shot.source } : {}) })\n hits.push(lineHit(index, target, { points: dealt, energy: shot.energyAt(target.distance), events }))\n penetration -= teamGameCost(regionOfPart(target.part), target.character.spec.bodyCount)\n points *= TEAM_GAME_RULES.damageKeptPerBody\n if (penetration <= 0) break\n if (!target.character.alive) origin = target.distance + RESTART_METRES\n }\n return { hits, stoppedAt: hits.length === 0 ? 0 : (hits[hits.length - 1]?.index ?? -1) + 1 }\n}\n\n/** P7: the force P = √(E·m/A) hits a body while it is above 0, loses the body's resistance R and keeps P/(P+R) of the energy. */\nconst physicalLine = ({ shot, targets }: PenetrateLineParams): LineResult => {\n const first = targets[0]\n let force = first ? penetrationForce(shot.energyAt(first.distance), shot.mass, shot.caliber) : 0\n let kept = 1\n const hits: LineHit[] = []\n for (const [index, target] of targets.entries()) {\n if (force <= 0) break\n if (!target.character.alive) continue\n const energy = shot.energyAt(target.distance) * kept\n const events = target.character.hit({ type: 'ballistic', point: target.point ?? [0, 0, 0], part: target.part, energy, mass: shot.mass, caliber: shot.caliber, distance: target.distance, ...(shot.caliberId ? { caliberId: shot.caliberId } : {}) })\n hits.push(lineHit(index, target, { points: target.character.lastHit?.points ?? 0, energy, events }))\n const resistance = target.character.resistanceOf(target.part)\n kept *= energyKept(force, resistance)\n force -= resistance\n }\n return { hits, stoppedAt: hits.length === 0 ? 0 : (hits[hits.length - 1]?.index ?? -1) + 1 }\n}\n\n/** A shot through a line of bodies, nearest first. Each body takes the hit through its own damage model. */\nexport const penetrateLine = (params: PenetrateLineParams): LineResult => (params.model === 'team-game' ? teamGameLine(params) : physicalLine(params))\n","type Entry = { readonly id: string }\n\n/** The shipped entries with the ones from `extra` added or, when the id exists, replaced. */\nexport const mergeById = <TEntry extends Entry>(shipped: readonly TEntry[], extra: readonly TEntry[] | undefined): Map<string, TEntry> => {\n const entries = new Map<string, TEntry>()\n for (const entry of [...shipped, ...(extra ?? [])]) entries.set(entry.id, entry)\n return entries\n}\n\n/** Applies `patch` by id (a shallow merge); a patch for an id that does not exist is ignored. */\nexport const applyPatches = <TEntry extends Entry>(entries: Map<string, TEntry>, patches: Readonly<Record<string, Partial<TEntry>>> | undefined): void => {\n for (const [id, patch] of Object.entries(patches ?? {})) {\n const entry = entries.get(id)\n if (entry) entries.set(id, { ...entry, ...patch, id })\n }\n}\n\nexport const removeIds = (entries: Map<string, unknown>, ids: readonly string[] | undefined): void => {\n for (const id of ids ?? []) entries.delete(id)\n}\n","import { createBodySpec } from './body/body-spec'\nimport { createCorpseLedger } from './corpses'\nimport type { CorpseLedger } from './corpses'\nimport type { BodySpec } from './body/body-spec'\nimport { createCharacterCore, resolveSpec } from './character'\nimport { createDamageCharacter } from './damage/damage-character'\nimport type { DamageCharacter } from './damage/damage-character'\nimport { DAMAGE_PRESETS, PROTECTION_PRESETS, TEAM_GAME_CALIBERS } from './damage/defaults'\nimport { penetrateLine } from './damage/line'\nimport type { LineResult, LineShot, LineTarget } from './damage/line'\nimport { PENETRATION_MODELS } from './damage/penetration'\nimport type { CaliberEntry, DamageRules, PenetrationModel, ProtectionLayer } from './damage/types'\nimport { applyPatches, mergeById, removeIds } from './registry'\nimport type { CharacterPreset, CharacterSpec, CharactersOptions, CorpseOptions, DamageOptions } from './types'\n\nexport const DEFAULT_CORPSES: CorpseOptions = { max: 400, seconds: 900 }\n\n/** The base preset every spec can start from: an unnamed adult person. */\nexport const PERSON_PRESET: CharacterPreset = { id: 'person', kind: 'person', age: 'adult' }\n\nexport type CharacterKit = {\n readonly body: BodySpec\n readonly presets: ReadonlyMap<string, CharacterPreset>\n readonly corpses: CorpseOptions\n readonly damage: DamageRules\n readonly penetration: PenetrationModel\n readonly protection: ReadonlyMap<string, ProtectionLayer>\n readonly calibers: ReadonlyMap<string, CaliberEntry>\n /** A new character: the preset (`spec.preset`, else the one named `spec.kind`) under the spec. */\n create(spec: CharacterSpec): DamageCharacter\n /** The bodies left on the ground, capped and timed by the kit's `corpses` option. */\n createCorpses<TData>(): CorpseLedger<TData>\n /** A shot through a line of bodies with the kit's penetration model and calibre table. */\n penetrateLine(shot: LineShot, targets: readonly LineTarget[]): LineResult\n}\n\n/** Fills a spec from its preset: what the spec says wins; `multipliers` merge field by field. */\nexport const applyPreset = (spec: CharacterSpec, presets: ReadonlyMap<string, CharacterPreset>): CharacterSpec => {\n const preset = presets.get(spec.preset ?? spec.kind)\n if (!preset) return spec\n const { id: _presetId, ...defaults } = preset\n const multipliers = { ...defaults.multipliers, ...spec.multipliers }\n return { ...defaults, ...spec, multipliers }\n}\n\n/** The rules of a preset (`kit` by default) with the given fields replaced; `severity`, `blast` and `segments` merge key by key. */\nexport const resolveDamageRules = (options: DamageOptions = {}): DamageRules => {\n const { preset, severity, blast, segments, ...rest } = options\n const base = DAMAGE_PRESETS[preset ?? 'kit']\n return { ...base, ...rest, severity: { ...base.severity, ...severity }, blast: { ...base.blast, ...blast }, segments: { ...base.segments, ...segments } }\n}\n\nconst modelOf = (option: CharactersOptions['penetration']): PenetrationModel => (typeof option === 'object' ? option : PENETRATION_MODELS[option ?? 'physical'])\n\nexport const createCharacters = (options: CharactersOptions = {}): CharacterKit => {\n const body = createBodySpec(options.body)\n const packs = options.packs ?? []\n const presets = mergeById([PERSON_PRESET], packs.flatMap((pack) => pack.presets ?? []))\n applyPatches(presets, options.patch)\n removeIds(presets, options.remove)\n const damage = resolveDamageRules(options.damage)\n const penetration = modelOf(options.penetration)\n const protection = mergeById(PROTECTION_PRESETS, [...packs.flatMap((pack) => pack.protection ?? []), ...(options.protection ?? [])])\n const shippedCalibers = penetration.id === 'team-game' ? TEAM_GAME_CALIBERS : []\n const calibers = mergeById(shippedCalibers, [...packs.flatMap((pack) => pack.calibers ?? []), ...(options.calibers ?? [])])\n const setup = { rules: damage, model: penetration, protection, calibers }\n let counter = 0\n return {\n body,\n presets,\n corpses: { ...DEFAULT_CORPSES, ...options.corpses },\n damage,\n penetration,\n protection,\n calibers,\n create(spec) {\n counter += 1\n const resolved = resolveSpec(applyPreset(spec, presets), body, counter)\n const core = createCharacterCore({ spec: resolved, bodySpec: body, hitPointsOf: (kind) => damage.segments[kind].hitPoints })\n return createDamageCharacter(core, setup)\n },\n createCorpses: () => createCorpseLedger({ ...DEFAULT_CORPSES, ...options.corpses }),\n penetrateLine: (shot, targets) => penetrateLine({ model: penetration.id === 'team-game' ? 'team-game' : 'physical', shot, targets, calibers }),\n }\n}\n","{\n \"cascade\": {\n \"hip\": 16,\n \"spine\": 11,\n \"shoulder\": 9,\n \"elbow\": 7,\n \"leg\": 14,\n \"knee\": 11,\n \"head\": 6\n },\n \"moves\": {\n \"idle\": {\n \"gait\": 0,\n \"freq\": 0,\n \"amp\": 0,\n \"knee\": 0,\n \"bob\": 0,\n \"lean\": 0,\n \"arm\": 0,\n \"elb\": 0.12,\n \"crouch\": 0,\n \"aim\": 0,\n \"turn\": 0,\n \"grip\": 0.35\n },\n \"walk\": {\n \"gait\": 1,\n \"freq\": 5.6,\n \"amp\": 0.42,\n \"knee\": 0.85,\n \"bob\": 0.035,\n \"lean\": 0.05,\n \"arm\": 0.42,\n \"elb\": 0.3,\n \"crouch\": 0,\n \"aim\": 0,\n \"turn\": 0,\n \"grip\": 0.4\n },\n \"run\": {\n \"gait\": 1,\n \"freq\": 9.2,\n \"amp\": 0.72,\n \"knee\": 1.5,\n \"bob\": 0.06,\n \"lean\": 0.3,\n \"arm\": 0.85,\n \"elb\": 1.35,\n \"crouch\": 0,\n \"aim\": 0,\n \"turn\": 0,\n \"grip\": 1.1\n },\n \"crouch\": {\n \"gait\": 1,\n \"freq\": 3.8,\n \"amp\": 0.3,\n \"knee\": 0.6,\n \"bob\": 0.02,\n \"lean\": 0.42,\n \"arm\": 0.15,\n \"elb\": 0.7,\n \"crouch\": 1,\n \"aim\": 0,\n \"turn\": 0,\n \"grip\": 0.8\n },\n \"aim\": {\n \"gait\": 0,\n \"freq\": 0,\n \"amp\": 0,\n \"knee\": 0,\n \"bob\": 0,\n \"lean\": 0.06,\n \"arm\": 0,\n \"elb\": 0.2,\n \"crouch\": 0.25,\n \"aim\": 1,\n \"turn\": 0,\n \"grip\": 1.2\n },\n \"turn\": {\n \"gait\": 0.5,\n \"freq\": 4.2,\n \"amp\": 0.14,\n \"knee\": 0.7,\n \"bob\": 0.012,\n \"lean\": 0.03,\n \"arm\": 0.12,\n \"elb\": 0.25,\n \"crouch\": 0,\n \"aim\": 0,\n \"turn\": 1,\n \"grip\": 0.4\n }\n },\n \"poses\": [\n {\n \"index\": 0,\n \"id\": \"walk\",\n \"name\": {\n \"pt-BR\": \"anda\"\n }\n },\n {\n \"index\": 1,\n \"id\": \"crawl\",\n \"name\": {\n \"pt-BR\": \"rasteja\"\n }\n },\n {\n \"index\": 2,\n \"id\": \"fallen\",\n \"name\": {\n \"pt-BR\": \"caído\"\n }\n },\n {\n \"index\": 3,\n \"id\": \"attack\",\n \"name\": {\n \"pt-BR\": \"ataca\"\n }\n },\n {\n \"index\": 4,\n \"id\": \"scream\",\n \"name\": {\n \"pt-BR\": \"grito\"\n }\n },\n {\n \"index\": 5,\n \"id\": \"crouch\",\n \"name\": {\n \"pt-BR\": \"agachado\"\n }\n },\n {\n \"index\": 6,\n \"id\": \"leap\",\n \"name\": {\n \"pt-BR\": \"salto\"\n }\n },\n {\n \"index\": 7,\n \"id\": \"eat\",\n \"name\": {\n \"pt-BR\": \"comendo\"\n }\n }\n ],\n \"walk\": {\n \"cadenceBase\": 0.5,\n \"cadencePerSpeed\": 0.3,\n \"stopBelowSpeed\": 0.05,\n \"maxSine\": 0.9,\n \"strideFactor\": 4,\n \"legLengthFactor\": 0.9\n }\n}\n","import gait from '../data/gait.json'\n\nconst WALK = gait.walk\n\n/** Cycles per second at `speed` m/s (the game's `walkCad`). */\nexport const walkCadence = (speed: number): number => WALK.cadenceBase + WALK.cadencePerSpeed * speed\n\n/** Hip swing in radians at `speed` m/s for a body `height` times the base height: the stride that makes the foot meet the ground (the game's `walkGait`). */\nexport const walkGait = (speed: number, height = 1): number =>\n speed < WALK.stopBelowSpeed ? 0 : 2 * Math.asin(Math.min(WALK.maxSine, speed / (WALK.strideFactor * WALK.legLengthFactor * height * walkCadence(speed))))\n\nexport type StrideParams = {\n readonly speed: number\n readonly height?: number\n /** Default true: cadence and swing come from the speed, so the feet do not slide. False keeps the profile's own numbers (the Bestiário preset). */\n readonly strideFromSpeed?: boolean\n}\n\nexport type Stride = {\n /** Cycles per second. */\n readonly frequency: number\n /** Thigh swing, radians. */\n readonly swing: number\n /** Knee bend, radians. */\n readonly kneeBend: number\n}\n\nconst SWING_SHARE = 0.5\nconst KNEE_PER_GAIT = 0.85\n\n/** The gait numbers for a speed: from the ground-matching stride, or `fallback` (the profile) when `strideFromSpeed` is false. */\nexport const strideFor = ({ speed, height = 1, strideFromSpeed = true }: StrideParams, fallback: Stride): Stride => {\n if (!strideFromSpeed) return fallback\n const amplitude = walkGait(speed, height)\n return { frequency: walkCadence(speed), swing: amplitude * SWING_SHARE, kneeBend: amplitude * KNEE_PER_GAIT }\n}\n","import gait from '../data/gait.json'\n\n/** How a body type walks: offsets over its rest pose (the Bestiário's `GAIT`). `f` cycles per second, `a` thigh swing, `k` knee bend, `arm` arm swing. */\nexport type GaitProfile = {\n readonly f: number\n readonly a?: number\n readonly k?: number\n readonly arm?: number\n readonly bob?: number\n readonly sway?: number\n readonly drag?: boolean\n readonly crawl?: boolean\n readonly idle?: boolean\n readonly eat?: boolean\n readonly leap?: boolean\n readonly listen?: boolean\n readonly stalk?: boolean\n readonly sniff?: boolean\n}\n\nexport type JointAngles = { readonly shoulder: number; readonly elbow: number }\nexport type LegAngles = { readonly hip: number; readonly knee: number; readonly lost?: boolean }\n\n/**\n * The pose the gait swings around. `arms[0]` is the right arm and `arms[1]` the left; `legs[0]` is the left leg and `legs[1]` the right\n * (the order the Bestiário builds them in). `hipY` is the rest height of the hips.\n */\nexport type RestPose = {\n readonly hipY: number\n readonly lean: number\n readonly headX: number\n readonly arms: readonly [JointAngles, JointAngles]\n readonly legs: readonly [LegAngles, LegAngles]\n}\n\n/** What a gait sets this frame; a field the gait does not write stays `undefined` and the joint keeps what it had. */\nexport type GaitSample = {\n hipY?: number\n upper: { x?: number; y?: number; z?: number }\n head: { x?: number; y?: number }\n arms: { shoulder?: number; elbow?: number }[]\n legs: { hip?: number; knee?: number }[]\n}\n\nexport const emptySample = (): GaitSample => ({ upper: {}, head: {}, arms: [{}, {}], legs: [{}, {}] })\n\nexport type CascadeJoint = keyof typeof gait.cascade\n\n/** Cascade rates per joint (1/s): each joint reaches its target with a delay, from the hips to the head (Bestiário 12.3). */\nexport const CASCADE: Readonly<Record<CascadeJoint, number>> = gait.cascade\n\n/** The 8 poses of the instanced body, in the order of the game's `iB.w`. */\nexport const INSTANCE_POSES = ['walk', 'crawl', 'fallen', 'attack', 'scream', 'crouch', 'leap', 'eat'] as const\nexport type InstancePose = (typeof INSTANCE_POSES)[number]\n\nconst MIN_HEIGHT = -2\nconst MAX_HEIGHT = 8\nconst HEIGHT_STEPS = 1000\nconst POSE_SLOTS = 16\n\n/** `iB.w` of the instanced body: the pose plus 16 times the ground height in millimetres (a pile of bodies, a jump). */\nexport const encodePoseHeight = (pose: InstancePose, height = 0): number =>\n INSTANCE_POSES.indexOf(pose) + POSE_SLOTS * Math.round((Math.min(MAX_HEIGHT, Math.max(MIN_HEIGHT, height)) - MIN_HEIGHT) * HEIGHT_STEPS)\n\nexport const decodePoseHeight = (encoded: number): { readonly pose: InstancePose; readonly height: number } => {\n const height = Math.floor(encoded / POSE_SLOTS) / HEIGHT_STEPS + MIN_HEIGHT\n return { pose: INSTANCE_POSES[encoded - Math.floor(encoded / POSE_SLOTS) * POSE_SLOTS] as InstancePose, height }\n}\n","import { emptySample } from './profile'\nimport type { GaitProfile, GaitSample, RestPose } from './profile'\nimport { strideFor } from './stride'\nimport type { StrideParams } from './stride'\n\nconst TWO_PI = Math.PI * 2\nconst LEAP_AIR_SHARE = 0.38\nconst LEAP_PHASE_DIVISOR = 6.3\nconst DEFAULT_PROFILE: GaitProfile = { f: 1.1, a: 0.28, k: 0.35, drag: true, arm: 0.08 }\n\nexport type AnimateProfileParams = {\n readonly profile?: GaitProfile\n readonly rest: RestPose\n /** Seconds. */\n readonly time: number\n /** Per-body phase offset, radians (the page's `rig.ph`). */\n readonly phase: number\n /** With `strideFromSpeed`, the speed and height that set cadence and swing. Without it the profile's own numbers are used. */\n readonly stride?: StrideParams\n}\n\n/** The leap, as a cycle: crouched, then airborne for 38% of it. */\nconst leap = ({ rest, time, phase, profile }: Required<Pick<AnimateProfileParams, 'rest' | 'time' | 'phase'>> & { profile: GaitProfile }): GaitSample => {\n const sample = emptySample()\n const cycle = (time * profile.f + phase / LEAP_PHASE_DIVISOR) % 1\n const air = cycle < LEAP_AIR_SHARE ? Math.sin((cycle / LEAP_AIR_SHARE) * Math.PI) : 0\n sample.hipY = rest.hipY + air * 0.95\n sample.upper.x = rest.lean - air * 0.45\n rest.legs.forEach((leg, index) => (sample.legs[index] = { hip: leg.hip * (1 - air * 0.7), knee: leg.knee * (1 - air * 0.75) }))\n rest.arms.forEach((arm, index) => (sample.arms[index] = { shoulder: arm.shoulder - air * 2.3, elbow: arm.elbow * (1 - air) }))\n return sample\n}\n\n/** Joint targets of one gait at `time` (the Bestiário's `animate0`), as numbers; the rig applies the ones that are set. */\nexport const animateProfile = ({ profile = DEFAULT_PROFILE, rest, time, phase, stride }: AnimateProfileParams): GaitSample => {\n const own = { frequency: profile.f, swing: profile.a ?? 0, kneeBend: profile.k ?? 0 }\n const { frequency, swing, kneeBend } = stride ? strideFor(stride, own) : own\n const angle = time * frequency * TWO_PI + phase\n const sine = Math.sin(angle)\n const cosine = Math.cos(angle)\n const sample = emptySample()\n if (profile.leap) return leap({ rest, time, phase, profile })\n if (profile.listen) {\n sample.head.y = Math.sin(time * 0.7 + phase) * 0.6\n rest.arms.forEach((arm, index) => (sample.arms[index] = { shoulder: arm.shoulder + Math.sin(time * 0.9 + index) * 0.08 }))\n return sample\n }\n if (profile.eat) {\n const wave = Math.sin(time * profile.f * TWO_PI + phase)\n sample.head.x = rest.headX + Math.max(0, wave) * 0.35\n sample.upper.x = rest.lean + Math.max(0, wave) * 0.08\n rest.arms.forEach((arm, index) => (sample.arms[index] = { elbow: arm.elbow + wave * 0.2 }))\n return sample\n }\n if (profile.crawl) {\n rest.arms.forEach((arm, index) => {\n const wave = index ? sine : -sine\n sample.arms[index] = { shoulder: arm.shoulder + wave * 0.35, elbow: arm.elbow - Math.max(0, wave) * 0.6 }\n })\n sample.upper.z = sine * 0.06\n return sample\n }\n if (profile.idle) {\n const scare = Math.max(0, Math.sin(time * 0.9 + phase))\n sample.upper.x = rest.lean - scare * 0.18\n sample.head.x = rest.headX - scare * 0.25\n rest.arms.forEach((arm, index) => (sample.arms[index] = { shoulder: arm.shoulder + scare * 0.25 }))\n return sample\n }\n rest.legs.forEach((leg, index) => {\n if (leg.lost) return\n const wave = index ? -sine : sine\n if (profile.drag && index === 1) return void (sample.legs[index] = { hip: leg.hip + wave * swing * 0.35, knee: leg.knee })\n sample.legs[index] = { hip: leg.hip + wave * swing, knee: leg.knee + Math.max(0, index ? cosine : -cosine) * kneeBend }\n })\n rest.arms.forEach((arm, index) => (sample.arms[index] = { shoulder: arm.shoulder + (index ? sine : -sine) * (profile.arm || 0.1) }))\n sample.hipY = rest.hipY - Math.abs(cosine) * (profile.bob || 0.02)\n sample.upper.y = sine * 0.06\n sample.upper.z = (profile.sway || 0) * sine + (profile.drag ? 0.04 : 0)\n if (profile.stalk) sample.upper.x = rest.lean + sine * 0.04\n if (profile.sniff) sample.head.y = Math.sin(time * 3.1 + phase) * 0.35\n return sample\n}\n","import { CASCADE } from './profile'\nimport type { CascadeJoint } from './profile'\n\n/** One joint moving toward its target: it covers `dt · rate` of the distance each step (never more than all of it). */\nexport const stepCascade = (current: number, target: number, dt: number, joint: CascadeJoint, rates: Readonly<Record<CascadeJoint, number>> = CASCADE): number =>\n current + (target - current) * Math.min(1, dt * rates[joint])\n\n/** Between two poses by joint name: a joint `from` does not name starts at the target (the Bestiário's `lerpPose`). */\nexport const lerpPose = <TPose extends Readonly<Record<string, number>>>(from: Partial<TPose>, to: TPose, amount: number): TPose => {\n const result: Record<string, number> = {}\n for (const name of Object.keys(to)) {\n const start = from[name]\n const end = to[name] as number\n result[name] = start === undefined ? end : (start as number) + (end - (start as number)) * amount\n }\n return result as TPose\n}\n","import gait from '../data/gait.json'\n\nexport type MoveId = keyof typeof gait.moves\nexport type MoveBlend = { gait: number; freq: number; amp: number; knee: number; bob: number; lean: number; arm: number; elb: number; crouch: number; aim: number; turn: number; grip: number }\n\n/** The human movement states the Elenco blends every frame. */\nexport const MOVES: Readonly<Record<MoveId, Readonly<MoveBlend>>> = gait.moves\n\nexport const MOVE_BLEND_RATE = 4\n\nexport const moveBlendOf = (move: MoveId): MoveBlend => ({ ...MOVES[move] })\n\n/** Moves `current` toward the target move: each number covers `dt · 4` of the distance (never past it). Mutates and returns `current`. */\nexport const blendMoves = (current: MoveBlend, target: MoveId | Readonly<MoveBlend>, dt: number): MoveBlend => {\n const goal = typeof target === 'string' ? MOVES[target] : target\n const amount = Math.min(1, dt * MOVE_BLEND_RATE)\n for (const key of Object.keys(goal) as (keyof MoveBlend)[]) current[key] += (goal[key] - current[key]) * amount\n return current\n}\n\nexport type HumanArm = { readonly side: 1 | -1; readonly hold?: boolean }\n\nexport type HumanPoseParams = {\n readonly moves: Readonly<MoveBlend>\n /** Seconds since the start. */\n readonly time: number\n /** The character's own phase offset. */\n readonly phase: number\n /** `phase + offset`: the walking cycle's angle, advanced by `dt · freq`. */\n readonly cycle: number\n /** The character's resting hunch plus the stage's. */\n readonly hunch: number\n /** The infection stage's arm raise (0 healthy .. 1 reanimated). */\n readonly armRaise: number\n /** Stage index: 3 and up adds a spine tremor, 4 is the reanimated body. */\n readonly stage: number\n /** The `prefers-reduced-motion` mode: no idle motion, no gait. */\n readonly reduced?: boolean\n readonly skirt?: boolean\n /** [right, left] as built: side and whether the hand holds an item. */\n readonly arms: readonly [HumanArm, HumanArm]\n /** [left, right] leg sides. */\n readonly legSides: readonly [1 | -1, 1 | -1]\n}\n\nexport type HumanPose = {\n readonly chestScaleY: number\n readonly hip: { readonly x: number; readonly y: number; readonly zRotation: number }\n readonly upper: { readonly x: number; readonly y: number; readonly z: number }\n readonly head: { readonly x: number; readonly y: number; readonly z: number }\n readonly legs: readonly { readonly hip: number; readonly knee: number; readonly hipZ: number }[]\n readonly arms: readonly { readonly shoulder: number; readonly shoulderZ: number; readonly elbow: number; readonly wrist: number; readonly grip: number }[]\n}\n\nconst REANIMATED_STAGE = 4\nconst TREMOR_STAGE = 3\n\n/** The blended walk of the Elenco for one frame (hips, spine, head, legs, arms), as joint targets. Pure: the caller keeps the phase and the blend. */\nexport const humanPose = (params: HumanPoseParams): HumanPose => {\n const { moves, time, phase, cycle, hunch, armRaise, stage, reduced = false, skirt = false } = params\n const zombie = stage === REANIMATED_STAGE\n const walking = reduced ? 0 : moves.gait\n const phased = phase + time\n const shift = reduced ? 0 : Math.sin(time * 0.45 + phase) * (1 - walking) * (1 - moves.aim)\n const upperX = hunch + moves.crouch * 0.15 + (stage >= TREMOR_STAGE && !reduced ? Math.sin(phased * 2.3) * 0.03 : 0)\n const upperY = walking * Math.sin(cycle - 0.35) * (0.08 + moves.arm * 0.12) - moves.aim * 0.35 + moves.turn * 0.25\n const upperZ = -shift * 0.025 + walking * Math.sin(cycle - 0.35) * 0.02 + (zombie ? 0.05 + Math.sin(cycle * 0.5) * 0.06 * walking : 0)\n const legs = params.legSides.map((side, index) => {\n const legCycle = cycle + (index ? Math.PI : 0)\n const amplitude = moves.amp * (zombie && index === 0 ? 0.45 : 1) * (skirt ? 0.75 : 1)\n return {\n hip: -walking * Math.sin(legCycle) * amplitude - moves.crouch * 0.95 + (zombie && !walking ? (index ? -0.1 : 0.08) : 0),\n knee:\n walking * (Math.max(0, Math.cos(legCycle)) * moves.knee * (zombie && index === 0 ? 0.3 : 1) + 0.06) +\n moves.crouch * 1.55 +\n (1 - walking) * (0.03 + Math.max(0, shift * (index ? 1 : -1)) * 0.18),\n hipZ: side * (0.02 + moves.aim * 0.1 + moves.crouch * 0.06),\n }\n })\n const arms = params.arms.map(({ side, hold = false }, index) => {\n const raise = armRaise * (hold && !zombie ? 0.2 : 1)\n const swing = cycle - 0.25\n const shoulderX =\n (hold ? -0.2 : 0) -\n raise * (zombie ? 1.12 : 1.3) -\n side * Math.sin(swing) * moves.arm * walking * (zombie ? 0.3 : 1) +\n (reduced || walking ? 0 : Math.sin(phased * 1.6 + side) * 0.04) +\n (zombie && !reduced ? Math.sin(phased * 3 + side * 2) * 0.08 : 0)\n const aimX = index === 0 ? -1.45 : -1.32\n return {\n shoulder: shoulderX * (1 - moves.aim) + aimX * moves.aim,\n shoulderZ: side * (0.07 + (zombie ? 0.1 : 0)) * (1 - moves.aim) - side * 0.32 * moves.aim,\n elbow: zombie ? -0.18 : -((moves.elb + (hold ? 0.25 : 0) + walking * Math.max(0, side * Math.sin(swing - 0.3)) * 0.3) * (1 - moves.aim) + (index === 0 ? 0.15 : 0.55) * moves.aim),\n wrist: reduced ? 0 : -walking * Math.sin(swing - 0.6) * 0.15 * side * (1 - moves.aim),\n grip: (hold ? 1.25 : moves.grip) * (zombie ? 0.5 : 1),\n }\n })\n const headY = (reduced ? 0 : Math.sin(phased * 0.5) * 0.25 * (1 - walking) * (1 - moves.aim) * (zombie ? 0.3 : 1)) - upperY * 0.75 * (1 - moves.turn) + moves.turn * 0.45 + moves.aim * 0.3\n return {\n chestScaleY: 1 + (reduced ? 0 : Math.sin(phased * (1.6 + moves.freq * 0.12)) * (0.012 + moves.freq * 0.0012)),\n hip: { x: shift * 0.025 + walking * Math.sin(cycle) * 0.018, y: -walking * moves.bob * Math.abs(Math.sin(cycle)) - moves.crouch * 0.26, zRotation: shift * 0.02 - walking * Math.sin(cycle) * 0.022 },\n upper: { x: upperX, y: upperY, z: upperZ },\n head: { x: -hunch * 0.6 - moves.crouch * 0.12 + (zombie ? 0.15 : 0) + walking * Math.sin(cycle * 2 - 0.6) * 0.02, y: headY, z: (zombie ? 0.22 : 0) - upperZ * 0.6 },\n legs,\n arms,\n }\n}\n\n/** A leg that cannot carry weight (limp): the swing shrinks to 30%, the knee straightens, the spine leans toward the good leg. */\nexport const LIMP = { swing: 0.3, knee: 0.05, lean: 0.07 } as const\n","/** Prone crawl on the arms (the Elenco's leg-less body): joint targets at `time`; `figTilt` is the body's tilt target, which the caller approaches at `dt · 4`. */\nexport type CrawlPose = {\n readonly figTilt: number\n readonly hipY: number\n readonly hipZRotation: number\n readonly upper: { readonly x: number; readonly y: number }\n readonly headX: number\n readonly arms: readonly { readonly shoulder: number; readonly shoulderZ: number; readonly elbow: number }[]\n readonly legs: readonly { readonly hip: number; readonly knee: number }[]\n}\n\nexport const CRAWL_TILT_RATE = 4\nexport const DEAD_TILT_RATE = 3\n\nexport const crawlPose = (time: number, phase: number, sides: readonly [1 | -1, 1 | -1]): CrawlPose => {\n const cycle = time * 2.2 + phase\n const wave = Math.sin(cycle)\n return {\n figTilt: 1.38,\n hipY: 0.2 + Math.abs(wave) * 0.02,\n hipZRotation: wave * 0.05,\n upper: { x: 0, y: wave * 0.12 },\n headX: -0.95,\n arms: sides.map((side) => ({ shoulder: -2.55 + side * wave * 0.45, shoulderZ: side * 0.25, elbow: -0.35 - Math.max(0, -side * wave) * 0.6 })),\n legs: [\n { hip: 0.1, knee: 0 },\n { hip: 0.12 + wave * 0.08, knee: 0.25 },\n ],\n }\n}\n\nexport type DeadPose = {\n readonly figTilt: number\n readonly hipY: number\n readonly headY: number\n readonly arms: readonly { readonly shoulder: number; readonly shoulderZ: number; readonly elbow: number }[]\n readonly legs: readonly { readonly hip: number; readonly hipZ: number; readonly knee: number }[]\n}\n\n/** A body on the ground (the Elenco's death pose); the caller approaches `figTilt` at `dt · 3`. */\nexport const deadPose = (armSides: readonly [1 | -1, 1 | -1]): DeadPose => ({\n figTilt: -1.45,\n hipY: 0.22,\n headY: 0.5,\n arms: armSides.map((side) => ({ shoulder: -0.2, shoulderZ: side * 1.1, elbow: -0.3 })),\n legs: [-0.15, 0.15].map((hipZ) => ({ hip: 0, hipZ, knee: 0.1 })),\n})\n","export type ConditionPhase = {\n readonly id: string\n /** When the phase starts, in the timeline's units (hours, or seconds on an accelerated clock). The first phase starts at 0. */\n readonly startsAt: number\n /** Numbers the phase drives (skin tint, hunch, arm raise, ...), by name. */\n readonly effects?: Readonly<Record<string, number>>\n}\n\nexport type ConditionOptions = {\n readonly phases: readonly ConditionPhase[]\n /** Timeline units per real second: 1 on a clock already in seconds (the accelerated preset), 1/3600 for hours. Default 1. */\n readonly unitsPerSecond?: number\n /** Interpolate effects toward the next phase instead of stepping. Default false. */\n readonly blend?: boolean\n}\n\nexport type ConditionSample = {\n readonly index: number\n readonly phase: ConditionPhase\n /** 0..1 inside the current phase (1 on the last one). */\n readonly progress: number\n readonly effects: Readonly<Record<string, number>>\n readonly isFinal: boolean\n}\n\nexport type ConditionTimeline = {\n readonly elapsed: number\n /** Advances the clock by `seconds` of real time. */\n advance(seconds: number): ConditionSample\n /** Jumps the clock to `units` of the timeline. */\n seek(units: number): ConditionSample\n sample(): ConditionSample\n reset(): void\n}\n\nconst phaseIndexAt = (phases: readonly ConditionPhase[], units: number): number => {\n let found = 0\n phases.forEach((phase, index) => {\n if (units >= phase.startsAt) found = index\n })\n return found\n}\n\nconst mixEffects = (from: ConditionPhase['effects'], to: ConditionPhase['effects'], amount: number): Record<string, number> => {\n const result: Record<string, number> = {}\n for (const name of new Set([...Object.keys(from ?? {}), ...Object.keys(to ?? {})])) {\n const start = from?.[name] ?? 0\n result[name] = start + ((to?.[name] ?? 0) - start) * amount\n }\n return result\n}\n\n/** A condition that moves through ordered phases over time (an infection: well, fever, delirium, reanimated). Generic: phases and effects are data. */\nexport const createConditionTimeline = (options: ConditionOptions): ConditionTimeline => {\n const { phases, unitsPerSecond = 1, blend = false } = options\n if (phases.length === 0) throw new Error('createConditionTimeline: at least one phase is required')\n let units = 0\n const sample = (): ConditionSample => {\n const index = phaseIndexAt(phases, units)\n const phase = phases[index] as ConditionPhase\n const next = phases[index + 1]\n const progress = next ? Math.min(1, (units - phase.startsAt) / (next.startsAt - phase.startsAt)) : 1\n const effects = blend && next ? mixEffects(phase.effects, next.effects, progress) : { ...phase.effects }\n return { index, phase, progress, effects, isFinal: !next }\n }\n return {\n get elapsed() {\n return units\n },\n advance(seconds) {\n units += seconds * unitsPerSecond\n return sample()\n },\n seek(target) {\n units = Math.max(0, target)\n return sample()\n },\n sample,\n reset() {\n units = 0\n },\n }\n}\n","{\n \"variance\": {\n \"height\": {\n \"gaussianSpread\": 0.09,\n \"min\": 0.9,\n \"max\": 1.1\n },\n \"buildBands\": [\n {\n \"below\": 0.3,\n \"base\": 0.85,\n \"range\": 0.1\n },\n {\n \"below\": 0.8,\n \"base\": 0.95,\n \"range\": 0.13\n },\n {\n \"base\": 1.08,\n \"range\": 0.17\n }\n ],\n \"buildSpeed\": {\n \"lean\": 1.05,\n \"heavy\": 0.9\n },\n \"buildHealth\": {\n \"lean\": 0.9,\n \"heavy\": 1.15\n },\n \"speedJitter\": {\n \"base\": 0.92,\n \"range\": 0.16\n },\n \"wanderJitter\": {\n \"base\": 0.8,\n \"range\": 0.4\n },\n \"runnerAgeClamp\": 1,\n \"variantCount\": {\n \"walker\": 4,\n \"runner\": 3,\n \"crawler\": 2,\n \"brute\": 2\n },\n \"variantSeedBase\": 7000\n },\n \"civilianColors\": [\n \"6D5A4A\",\n \"4D5D6B\",\n \"7A3B33\",\n \"5B6B4A\",\n \"8A7A5A\",\n \"3E4A5C\",\n \"6A4A6A\",\n \"9A8A70\",\n \"40403A\"\n ],\n \"corpses\": {\n \"max\": 400,\n \"lifeSeconds\": 900\n }\n}\n","import appearance from '../data/appearance.json'\n\nconst { variance } = appearance\n\nexport type BuildClass = 'lean' | 'average' | 'heavy'\n\n/** What `zSpawn` draws for a body: height, width, how it builds up and the speed jitters. Every field comes from the stream, in the game's order. */\nexport type BodyVariance = {\n readonly height: number\n readonly build: number\n readonly buildClass: BuildClass\n /** Multiplies the chase speed: the build factor times a 0.92 .. 1.08 jitter. */\n readonly speedFactor: number\n /** Multiplies the wander speed: 0.8 .. 1.2. */\n readonly wanderFactor: number\n /** Multiplies the health: 0.9 lean, 1.15 heavy. */\n readonly healthFactor: number\n}\n\nexport type VarianceScale = { readonly height?: number; readonly build?: number }\n\nconst clamp = (value: number, low: number, high: number): number => Math.min(high, Math.max(low, value))\n\n/**\n * One body's variance from `random`, drawing exactly what the game's `zSpawn` draws after the type, age and outfit picks:\n * three draws for the height (a gaussian), one for the build band plus one inside it, one for the speed and one for the wander speed.\n * `scale` is the type's own scale (a brute's 1.42) and `build` its width factor.\n */\nexport const drawBodyVariance = (random: () => number, scale: VarianceScale = {}): BodyVariance => {\n const spread = (random() + random() + random() - 1.5) * variance.height.gaussianSpread\n const height = clamp(1 + spread, variance.height.min, variance.height.max) * (scale.height ?? 1)\n const band = random()\n const bands = variance.buildBands as readonly { below?: number; base: number; range: number }[]\n const found = bands.find((candidate) => candidate.below !== undefined && band < candidate.below) ?? (bands[bands.length - 1] as { base: number; range: number })\n const { base, range } = found\n const build = (base + random() * range) * (scale.build ?? 1)\n const buildClass: BuildClass = band < 0.3 ? 'lean' : band >= 0.8 ? 'heavy' : 'average'\n const speedBuild = buildClass === 'lean' ? variance.buildSpeed.lean : buildClass === 'heavy' ? variance.buildSpeed.heavy : 1\n const healthBuild = buildClass === 'lean' ? variance.buildHealth.lean : buildClass === 'heavy' ? variance.buildHealth.heavy : 1\n return {\n height,\n build,\n buildClass,\n speedFactor: speedBuild * (variance.speedJitter.base + random() * variance.speedJitter.range),\n wanderFactor: variance.wanderJitter.base + random() * variance.wanderJitter.range,\n healthFactor: healthBuild,\n }\n}\n\n/** Variants baked per type (the game's `nv`): 4 walkers, 3 runners, 2 crawlers and brutes, 1 for the rest. */\nexport const variantCountFor = (typeId: string): number => (variance.variantCount as Readonly<Record<string, number>>)[typeId] ?? 1\n\n/** The seed of variant `variant` of the type at `typeIndex` (the game's ZVAR rule: `7000 + c·101 + v·13`). */\nexport const variantSeed = (typeIndex: number, variant: number): number => variance.variantSeedBase + typeIndex * 101 + variant * 13\n\n/** The decay each variant is baked at: 1, 2, 1, 2. */\nexport const variantDecay = (variant: number): number => (variant % 2 === 0 ? 1 : 2)\n","/** The colour and style pools of the Bestiário v17 builder, in the order its random stream picks from them. */\nexport const SKIN_TONES = [0xf1c9a5, 0xe0ac80, 0xc68c62, 0xa8714a, 0x8a5a3a, 0x5e3b26] as const\nexport const TOP_COLORS = [0x4a6f96, 0x8e3b3b, 0x5d4a3a, 0x4f7a43, 0x6b6f78, 0x3f5f3a, 0x5a4a6e, 0x8a6a3a, 0xb8b2a2, 0x2c2c30, 0x9a4a2a, 0x2f5d63, 0xc9a94a, 0x7a2e4a, 0xd8d4c8, 0x3a4a6a] as const\nexport const PANTS_COLORS = [0x2c3340, 0x1f2228, 0x4a4030, 0x5a5f66, 0x3a4a6a, 0x6a5a40, 0x2a3a2a] as const\nexport const SLEEVES = ['short', 'short', 'long', 'tank'] as const\nexport const HAIR_STYLES_FEMALE = ['long', 'long', 'bun', 'short', 'afro', 'bald'] as const\nexport const HAIR_STYLES_MALE = ['short', 'short', 'buzz', 'bald', 'afro', 'long'] as const\nexport const CAP_COLORS = [0x8e3b3b, 0x2c2c30, 0x3a4a6a, 0xc9a94a] as const\nexport const BAG_COLORS = [0x3a4a3a, 0x5a2a24, 0x2c3340] as const\nexport const HAIR_COLORS = [0x1a1410, 0x2a1e16, 0x3b2c20, 0x6a4a2a, 0xb08a50, 0x7a2a1a] as const\nexport const SHOE_COLORS = [0x1d1f22, 0x3a2a1a, 0xb8b4a8, 0x4a1a1a] as const\n/** Uniform bodies (the crowd's baked variants): skin by decay, shirt by type index. */\nexport const DECAY_SKIN = [0xcdbba8, 0x94a084, 0x7b744a, 0x8c7c5e] as const\nexport const UNIFORM_SHIRTS = [0x4a6f96, 0x8e3b3b, 0x5d4a3a, 0x4f7a43, 0x6b6f78, 0x3f5f3a, 0x5a4a6e, 0x8a6a3a] as const\nexport const UNIFORM_PANTS = 0x2c3340\nexport const UNIFORM_SHOES = 0x1d1f22\nexport const SKIN_DECAY_BLEND = [0.45, 0.65, 0.8, 0.88] as const\nexport const GRAY = 0x4a4a46\nexport const RED = 0x7a120c\nexport const BLOOD_COLORS = [0x9e1b12, 0x6e1410, 0x4a2a18, 0x1c1612] as const\nexport const OLD_HAIR = 0x8a8780\n","import { createRng } from '../damage/points'\n\nimport {\n BAG_COLORS,\n CAP_COLORS,\n DECAY_SKIN,\n HAIR_COLORS,\n HAIR_STYLES_FEMALE,\n HAIR_STYLES_MALE,\n OLD_HAIR,\n PANTS_COLORS,\n SHOE_COLORS,\n SKIN_TONES,\n SLEEVES,\n TOP_COLORS,\n UNIFORM_PANTS,\n UNIFORM_SHIRTS,\n UNIFORM_SHOES,\n} from './palette'\n\nexport type HairStyle = (typeof HAIR_STYLES_FEMALE)[number] | (typeof HAIR_STYLES_MALE)[number]\n\nexport type OutfitLook = { readonly cloth?: number; readonly pants?: number; readonly hat?: string }\n\nexport type AppearanceParams = {\n readonly seed: number\n /** The look of an outfit (police, military ...): its colours replace the draws, so the stream does not spend them. */\n readonly outfit?: OutfitLook\n /** A baked crowd variant: no random look, fixed skin, shirt by `typeIndex`, short hair. */\n readonly uniform?: boolean\n readonly typeIndex?: number\n readonly clothIndex?: number\n /** Forces the sex; unset draws it (42% female). */\n readonly female?: boolean\n /** False for bodies that are never female (a brute): the draw is skipped. */\n readonly allowFemale?: boolean\n readonly tone?: number\n readonly hairStyle?: HairStyle\n readonly hairColor?: number\n /** The type's own look: wet bodies have long hair, charred ones are bald. */\n readonly wet?: boolean\n readonly charred?: boolean\n /** A living person: no bag, no tear. */\n readonly human?: boolean\n /** 0 fresh-dead, 1 normal, 2 old (grey hair). */\n readonly life?: number\n}\n\nexport type AppearanceTraits = {\n readonly female: boolean\n readonly tone: number\n readonly topColor: number\n readonly pantsColor: number\n readonly sleeve: string\n readonly hair: string\n readonly headWidth: number\n readonly headHeight: number\n readonly jaw: number\n readonly nose: number\n readonly beard: boolean\n readonly glasses: boolean\n readonly cap: boolean\n readonly capColor: number\n readonly bag: boolean\n readonly bagColor: number\n readonly torn: boolean\n readonly apron: boolean\n readonly hairColor: number\n readonly shoeColor: number\n}\n\nexport type DrawnAppearance = {\n readonly traits: AppearanceTraits\n /** The stream, positioned after the look: the body builder keeps drawing from it (the hair of a decrepit body). */\n readonly random: () => number\n}\n\n/** The seed of the blood stream (the same drips on every level of detail). */\nexport const BLOOD_SEED_OFFSET = 99\n\nexport const createBloodStream = (seed: number): (() => number) => createRng(seed + BLOOD_SEED_OFFSET)\n\nconst FEMALE_SHARE = 0.42\nconst BEARD_SHARE = 0.25\nconst GLASSES_SHARE = 0.12\nconst CAP_SHARE = 0.12\nconst BAG_SHARE = 0.15\nconst TORN_SHARE = 0.35\nconst APRON_SHARE = 0.05\n\n/**\n * A person's look from a seed, drawing in exactly the order the Bestiário v17 builder draws from its `VR` stream, including the draws a\n * short-circuit skips (no beard roll for a woman, no cap roll under a hat). Returns the stream so the builder can continue from it.\n */\nexport const drawAppearance = (params: AppearanceParams): DrawnAppearance => {\n const { seed, outfit = {}, uniform = false, typeIndex = 0, human = false, life = 1 } = params\n const random = createRng(seed)\n const pick = <T>(options: readonly T[]): T => options[Math.floor(random() * options.length)] as T\n const female = !uniform && (params.allowFemale ?? true) && (params.female ?? random() < FEMALE_SHARE)\n const tone = params.tone ?? (uniform ? (DECAY_SKIN[0] as number) : pick(SKIN_TONES))\n const topColor = outfit.cloth ?? (uniform ? (UNIFORM_SHIRTS[(params.clothIndex ?? typeIndex) % UNIFORM_SHIRTS.length] as number) : pick(TOP_COLORS))\n const pantsColor = outfit.pants ?? (uniform ? UNIFORM_PANTS : pick(PANTS_COLORS))\n const look = uniform ? uniformLook() : drawLook({ random, pick, female, outfit, human, params })\n const hairColor = params.hairColor ?? (life === 2 ? OLD_HAIR : pick(HAIR_COLORS))\n const shoeColor = uniform ? UNIFORM_SHOES : pick(SHOE_COLORS)\n return { traits: { female, tone, topColor, pantsColor, ...look, hairColor, shoeColor }, random }\n}\n\ntype Look = Omit<AppearanceTraits, 'female' | 'tone' | 'topColor' | 'pantsColor' | 'hairColor' | 'shoeColor'>\n\nconst uniformLook = (): Look => ({\n sleeve: 'short',\n hair: 'short',\n headWidth: 1,\n headHeight: 1,\n jaw: 1,\n nose: 1,\n beard: false,\n glasses: false,\n cap: false,\n capColor: 0,\n bag: false,\n bagColor: 0,\n torn: false,\n apron: false,\n})\n\ntype DrawLookParams = {\n readonly random: () => number\n readonly pick: <T>(options: readonly T[]) => T\n readonly female: boolean\n readonly outfit: OutfitLook\n readonly human: boolean\n readonly params: AppearanceParams\n}\n\nconst drawLook = ({ random, pick, female, outfit, human, params }: DrawLookParams): Look => {\n const sleeve = outfit.cloth ? 'long' : pick(SLEEVES)\n const hair = params.hairStyle ?? (params.wet ? 'long' : params.charred ? 'bald' : pick(female ? HAIR_STYLES_FEMALE : HAIR_STYLES_MALE))\n const headWidth = 1 + (random() - 0.5) * 0.16\n const headHeight = 1 + (random() - 0.5) * 0.14\n const jaw = 1 + (random() - 0.5) * 0.3\n const nose = 1 + (random() - 0.5) * 0.5\n const beard = !female && random() < BEARD_SHARE\n const glasses = random() < GLASSES_SHARE\n const cap = !outfit.hat && random() < CAP_SHARE\n const capColor = pick(CAP_COLORS)\n const bag = random() < BAG_SHARE && !human\n const bagColor = pick(BAG_COLORS)\n const torn = random() < TORN_SHARE && !human\n const apron = random() < APRON_SHARE\n return { sleeve, hair, headWidth, headHeight, jaw, nose, beard, glasses, cap, capColor, bag, bagColor, torn, apron }\n}\n","import { lerpPose } from '../gait/cascade'\n\n/** One key of a clip: the time in seconds and the joint targets by name (the names of the rich body's `setPose`; a clip may add channels of its own, such as `x` or `run`). */\nexport type PoseKey = { t: number; pose: Record<string, number> }\n\n/** What a clip says happens at a time: `impact` (\"the blow lands now\"), `phase` (a label for the screen), or any type a game defines. */\nexport type PoseClipEvent = { t: number; type: string; data?: Record<string, unknown> }\n\n/** Which joints a clip drives: `upper` leaves the legs to the gait, `lower` leaves the trunk, head and arms, `full` sets everything. */\nexport type PoseClipChannels = 'full' | 'upper' | 'lower'\n\nexport type PoseClip = {\n id: string\n duration: number\n loop?: boolean\n keys: PoseKey[]\n events?: PoseClipEvent[]\n channels?: PoseClipChannels\n /** Where the clip was ported from (file and variable), for the diff against the team page. */\n source?: Record<string, unknown>\n}\n\n/**\n * The Bestiário's `sample(K, t)`: finds the segment of two keys, clamps the fraction to [0, 1], eases it with smoothstep `k·k·(3 − 2k)` and\n * blends the two poses by joint name (`lerpPose`). Before the first key it holds the first segment's start, after the last it holds the end.\n */\nexport const samplePoseClip = (clip: Pick<PoseClip, 'keys'>, time: number): Record<string, number> => {\n const keys = clip.keys\n if (keys.length === 0) return {}\n if (keys.length === 1) return { ...keys[0]!.pose }\n let index = 0\n while (index < keys.length - 2 && time > keys[index + 1]!.t) index++\n const a = keys[index]!\n const b = keys[index + 1]!\n const k = Math.min(1, Math.max(0, (time - a.t) / (b.t - a.t)))\n const eased = k * k * (3 - 2 * k)\n return lerpPose(a.pose, b.pose, eased)\n}\n\n/** The events a clip fires while the clock goes from `from` (excluded) to `to` (included), in time order. */\nexport const clipEventsBetween = (clip: Pick<PoseClip, 'events'>, from: number, to: number): PoseClipEvent[] =>\n (clip.events ?? []).filter((event) => event.t > from && event.t <= to).sort((x, y) => x.t - y.t)\n\n/** The names of the joints a channel set drives, from the names `PoseTargets` of the rich animator knows. */\nexport const POSE_CHANNEL_JOINTS: Readonly<Record<PoseClipChannels, readonly string[] | undefined>> = {\n full: undefined,\n upper: ['lean', 'twist', 'side', 'headX', 'headY', 'headZ', 'armR', 'armL', 'elbR', 'elbL', 'armRz', 'armLz'],\n lower: ['drop', 'legL', 'legR', 'kneeL', 'kneeR'],\n}\n\n/** Keeps from a sampled pose only the joints of the channel set (`full` keeps everything, including extra channels such as `x`). */\nexport const filterPoseChannels = (pose: Record<string, number>, channels: PoseClipChannels = 'full'): Record<string, number> => {\n const allowed = POSE_CHANNEL_JOINTS[channels]\n if (!allowed) return pose\n const out: Record<string, number> = {}\n for (const name of allowed) if (pose[name] !== undefined) out[name] = pose[name] as number\n return out\n}\n\n/** Joints that swap with their opposite when a clip is mirrored, and the channels that change sign (side, twist and the sideways turns). */\nconst MIRROR_PAIRS: ReadonlyArray<readonly [string, string]> = [['armR', 'armL'], ['elbR', 'elbL'], ['armRz', 'armLz'], ['legR', 'legL'], ['kneeR', 'kneeL']]\nconst MIRROR_NEGATED: ReadonlySet<string> = new Set(['side', 'twist', 'headY', 'headZ'])\n\n/** The clip as seen in a mirror: left and right swap and the sideways channels change sign; the times, the events and the duration stay, so the impact falls at the same second. */\nexport const mirrorPoseClip = (clip: PoseClip, id: string): PoseClip => ({\n ...clip,\n id,\n keys: clip.keys.map((key) => {\n const pose: Record<string, number> = {}\n for (const [name, value] of Object.entries(key.pose)) pose[name] = MIRROR_NEGATED.has(name) ? -value : value\n for (const [a, b] of MIRROR_PAIRS) {\n const va = key.pose[a]\n const vb = key.pose[b]\n const sign = a === 'armRz' ? -1 : 1 // the sideways swing of an arm opens to the other side\n if (vb !== undefined) pose[a] = sign * vb\n else delete pose[a]\n if (va !== undefined) pose[b] = sign * va\n else delete pose[b]\n }\n return { t: key.t, pose }\n }),\n ...(clip.events ? { events: clip.events.map((event) => ({ ...event })) } : {}),\n})\n","{\n \"clips\": [\n {\n \"id\": \"grabbed_bitten_human\",\n \"source\": {\n \"file\": \"site/pages/zumbis-personagens/bestiario.html\",\n \"scene\": \"ataque (Agarrão e mordida)\",\n \"variable\": \"HK\"\n },\n \"duration\": 8,\n \"loop\": false,\n \"channels\": \"full\",\n \"keys\": [\n {\n \"t\": 0,\n \"pose\": {\n \"lean\": 0,\n \"headY\": 0,\n \"armL\": -0.1,\n \"armR\": 0.05,\n \"elbL\": -0.2,\n \"elbR\": -0.2,\n \"drop\": 0,\n \"kneeL\": 0.05,\n \"kneeR\": 0.05,\n \"legL\": -0.05,\n \"legR\": 0.05,\n \"headX\": 0\n }\n },\n {\n \"t\": 1.5,\n \"pose\": {\n \"lean\": -0.1,\n \"headY\": -0.6,\n \"armL\": -0.1,\n \"armR\": 0.05,\n \"elbL\": -0.2,\n \"elbR\": -0.2,\n \"drop\": 0,\n \"kneeL\": 0.05,\n \"kneeR\": 0.05,\n \"legL\": -0.05,\n \"legR\": 0.05,\n \"headX\": -0.1\n }\n },\n {\n \"t\": 2.1,\n \"pose\": {\n \"lean\": -0.3,\n \"headY\": -0.2,\n \"armL\": -1.3,\n \"armR\": -1.2,\n \"elbL\": -1.4,\n \"elbR\": -1.5,\n \"drop\": -0.04,\n \"kneeL\": 0.25,\n \"kneeR\": 0.2,\n \"legL\": -0.3,\n \"legR\": 0.25,\n \"headX\": -0.25\n }\n },\n {\n \"t\": 3.6,\n \"pose\": {\n \"lean\": -0.35,\n \"headY\": 0.3,\n \"armL\": -1.5,\n \"armR\": -1.0,\n \"elbL\": -0.6,\n \"elbR\": -1.8,\n \"drop\": -0.05,\n \"kneeL\": 0.3,\n \"kneeR\": 0.2,\n \"legL\": -0.4,\n \"legR\": 0.35,\n \"headX\": -0.2\n }\n },\n {\n \"t\": 4.3,\n \"pose\": {\n \"lean\": -0.2,\n \"headY\": 0.7,\n \"headZ\": -0.5,\n \"armL\": -0.6,\n \"armR\": -1.9,\n \"elbL\": -0.4,\n \"elbR\": -1.2,\n \"drop\": -0.06,\n \"kneeL\": 0.3,\n \"kneeR\": 0.3,\n \"legL\": -0.3,\n \"legR\": 0.3,\n \"headX\": -0.3\n }\n },\n {\n \"t\": 5.6,\n \"pose\": {\n \"lean\": 0.55,\n \"headY\": 0.4,\n \"headZ\": -0.3,\n \"armL\": -0.3,\n \"armR\": -0.5,\n \"elbL\": -0.6,\n \"elbR\": -0.8,\n \"drop\": -0.5,\n \"kneeL\": 1.9,\n \"kneeR\": 1.7,\n \"legL\": -1.1,\n \"legR\": -0.9,\n \"headX\": 0.3\n }\n },\n {\n \"t\": 8,\n \"pose\": {\n \"lean\": 0.6,\n \"headY\": 0.4,\n \"headZ\": -0.3,\n \"armL\": -0.25,\n \"armR\": -0.45,\n \"elbL\": -0.6,\n \"elbR\": -0.8,\n \"drop\": -0.52,\n \"kneeL\": 1.95,\n \"kneeR\": 1.75,\n \"legL\": -1.1,\n \"legR\": -0.95,\n \"headX\": 0.4\n }\n }\n ],\n \"events\": [\n {\n \"t\": 0,\n \"type\": \"phase\",\n \"data\": {\n \"label\": \"pose_clip.grab_bite.phase.approach\"\n }\n },\n {\n \"t\": 1.7,\n \"type\": \"phase\",\n \"data\": {\n \"label\": \"pose_clip.grab_bite.phase.pounce\"\n }\n },\n {\n \"t\": 2.1,\n \"type\": \"phase\",\n \"data\": {\n \"label\": \"pose_clip.grab_bite.phase.grab\"\n }\n },\n {\n \"t\": 4.3,\n \"type\": \"phase\",\n \"data\": {\n \"label\": \"pose_clip.grab_bite.phase.bite\"\n }\n },\n {\n \"t\": 5.6,\n \"type\": \"phase\",\n \"data\": {\n \"label\": \"pose_clip.grab_bite.phase.fall\"\n }\n },\n {\n \"t\": 4.4,\n \"type\": \"impact\",\n \"data\": {\n \"kind\": \"bite\",\n \"source\": \"bite.visible=T>4.4 in the scene's step()\"\n }\n }\n ]\n },\n {\n \"id\": \"grab_bite_zombie\",\n \"source\": {\n \"file\": \"site/pages/zumbis-personagens/bestiario.html\",\n \"scene\": \"ataque (Agarrão e mordida)\",\n \"variable\": \"ZK\"\n },\n \"duration\": 8,\n \"loop\": false,\n \"channels\": \"full\",\n \"keys\": [\n {\n \"t\": 0,\n \"pose\": {\n \"x\": -6,\n \"lean\": 0.75,\n \"armL\": -1.35,\n \"armR\": -1.05,\n \"elbL\": -0.08,\n \"elbR\": -0.12,\n \"headX\": -0.55,\n \"drop\": -0.1,\n \"run\": 1\n }\n },\n {\n \"t\": 1.7,\n \"pose\": {\n \"x\": -0.9,\n \"lean\": 0.75,\n \"armL\": -1.35,\n \"armR\": -1.05,\n \"elbL\": -0.08,\n \"elbR\": -0.12,\n \"headX\": -0.55,\n \"drop\": -0.1,\n \"run\": 1\n }\n },\n {\n \"t\": 2.1,\n \"pose\": {\n \"x\": -0.32,\n \"lean\": 0.45,\n \"armL\": -1.65,\n \"armR\": -1.6,\n \"elbL\": -0.25,\n \"elbR\": -0.3,\n \"headX\": -0.3,\n \"drop\": -0.08,\n \"legL\": -0.6,\n \"legR\": 0.4,\n \"kneeL\": 0.5,\n \"kneeR\": 0.6,\n \"run\": 0\n }\n },\n {\n \"t\": 3.6,\n \"pose\": {\n \"x\": -0.2,\n \"lean\": 0.35,\n \"armL\": -1.5,\n \"armR\": -1.45,\n \"elbL\": -1.1,\n \"elbR\": -1.2,\n \"headX\": -0.2,\n \"drop\": -0.06,\n \"legL\": -0.4,\n \"legR\": 0.3,\n \"kneeL\": 0.4,\n \"kneeR\": 0.4,\n \"run\": 0\n }\n },\n {\n \"t\": 4.3,\n \"pose\": {\n \"x\": -0.1,\n \"lean\": 0.5,\n \"armL\": -1.4,\n \"armR\": -1.3,\n \"elbL\": -1.5,\n \"elbR\": -1.5,\n \"headX\": 0.35,\n \"headY\": 0.35,\n \"drop\": -0.08,\n \"legL\": -0.35,\n \"legR\": 0.3,\n \"kneeL\": 0.4,\n \"kneeR\": 0.4,\n \"run\": 0\n }\n },\n {\n \"t\": 5.6,\n \"pose\": {\n \"x\": 0,\n \"lean\": 0.95,\n \"armL\": -1.2,\n \"armR\": -1.0,\n \"elbL\": -1.4,\n \"elbR\": -1.3,\n \"headX\": 0.5,\n \"headY\": 0.3,\n \"drop\": -0.45,\n \"legL\": -1.2,\n \"legR\": -0.8,\n \"kneeL\": 1.8,\n \"kneeR\": 1.5,\n \"run\": 0\n }\n },\n {\n \"t\": 8,\n \"pose\": {\n \"x\": 0,\n \"lean\": 1.0,\n \"armL\": -1.15,\n \"armR\": -0.95,\n \"elbL\": -1.4,\n \"elbR\": -1.3,\n \"headX\": 0.55,\n \"headY\": 0.3,\n \"drop\": -0.47,\n \"legL\": -1.2,\n \"legR\": -0.8,\n \"kneeL\": 1.85,\n \"kneeR\": 1.5,\n \"run\": 0\n }\n }\n ],\n \"events\": [\n {\n \"t\": 0,\n \"type\": \"phase\",\n \"data\": {\n \"label\": \"pose_clip.grab_bite.phase.approach\"\n }\n },\n {\n \"t\": 1.7,\n \"type\": \"phase\",\n \"data\": {\n \"label\": \"pose_clip.grab_bite.phase.pounce\"\n }\n },\n {\n \"t\": 2.1,\n \"type\": \"phase\",\n \"data\": {\n \"label\": \"pose_clip.grab_bite.phase.grab\"\n }\n },\n {\n \"t\": 4.3,\n \"type\": \"phase\",\n \"data\": {\n \"label\": \"pose_clip.grab_bite.phase.bite\"\n }\n },\n {\n \"t\": 5.6,\n \"type\": \"phase\",\n \"data\": {\n \"label\": \"pose_clip.grab_bite.phase.fall\"\n }\n }\n ]\n }\n ],\n \"locales\": {\n \"pt-BR\": {\n \"pose_clip.grab_bite.phase.approach\": \"1. Aproximação\",\n \"pose_clip.grab_bite.phase.pounce\": \"2. O bote\",\n \"pose_clip.grab_bite.phase.grab\": \"3. Agarrão: 3 s para se soltar\",\n \"pose_clip.grab_bite.phase.bite\": \"4. Mordida: infecção 100%\",\n \"pose_clip.grab_bite.phase.fall\": \"5. Queda\"\n },\n \"en\": {\n \"pose_clip.grab_bite.phase.approach\": \"1. Approach\",\n \"pose_clip.grab_bite.phase.pounce\": \"2. The pounce\",\n \"pose_clip.grab_bite.phase.grab\": \"3. Grab: 3 s to break free\",\n \"pose_clip.grab_bite.phase.bite\": \"4. Bite: 100% infection\",\n \"pose_clip.grab_bite.phase.fall\": \"5. The fall\"\n }\n }\n}","import data from '../presets/pose-clips.json'\n\nimport type { PoseClip } from './pose-clip'\n\nexport type PoseClipRegistry = {\n /** Adds a clip, or replaces the one with the same id. Returns the registry. */\n register(clip: PoseClip): PoseClipRegistry\n get(id: string): PoseClip | undefined\n list(): PoseClip[]\n remove(id: string): boolean\n}\n\n/** A registry of clips by id: games add or replace clips without touching the core. */\nexport const createPoseClipRegistry = (initial: readonly PoseClip[] = []): PoseClipRegistry => {\n const clips = new Map<string, PoseClip>()\n const registry: PoseClipRegistry = {\n register(clip) {\n clips.set(clip.id, clip)\n return registry\n },\n get: (id) => clips.get(id),\n list: () => [...clips.values()],\n remove: (id) => clips.delete(id),\n }\n for (const clip of initial) registry.register(clip)\n return registry\n}\n\n/** The clips ported from the Bestiário \"grab and bite\" scene (human and zombie). Phase labels are locale keys, see `POSE_CLIP_LOCALES`. */\nexport const POSE_CLIPS: readonly PoseClip[] = data.clips as PoseClip[]\n\n/** Texts of the phase labels by locale (`pt-BR` is the team's text). */\nexport const POSE_CLIP_LOCALES: Readonly<Record<string, Readonly<Record<string, string>>>> = data.locales\n","import type { PoseClipEvent } from './pose-clip'\n\ntype Triple = [number, number, number]\n\n/**\n * A hand or a foot in the camera's space (x to the right, y up, the view looks down -z), in metres and radians. `p` is the middle of the palm (the ankle for a foot) and `r` the Euler angles\n * (order XYZ) of its frame: x from the wrist to the knuckles (from the heel to the toes), y the back of the hand (the top of the foot), z the rest of the right-handed frame; a left hand is the\n * mirror image of a right one, so the same numbers describe the same grip.\n */\nexport type FirstPersonPoseKey = {\n p: Triple\n r: Triple\n /** How closed the fingers are, 0 open to 1 closed; left out, the fingers keep what the stance has. */\n grip?: number\n /** How much of the clip replaces what the hand is doing otherwise (holding a weapon, the stance), 0 none to 1 all. Default 1. */\n w?: number\n}\n\n/** One key: at `t` seconds the right hand (`R`), the left hand (`L`, or `'mirror'` for the mirror image of the right) and the right foot (`F`) are where the entries say. A side without an entry here is not driven by this key. */\nexport type FirstPersonKey = { t: number; R?: FirstPersonPoseKey; L?: FirstPersonPoseKey | 'mirror'; F?: FirstPersonPoseKey }\n\n/** A first-person animation of the hands and the foot: the counterpart of a third-person `PoseClip`, with the same events. */\nexport type FirstPersonClip = {\n id: string\n duration: number\n keys: FirstPersonKey[]\n events?: PoseClipEvent[]\n /** Where the clip was ported from, or who authored it. */\n source?: Record<string, unknown>\n}\n\n/** What a channel does at a time. `mirror` says the numbers are the right hand's and the left hand is their mirror image. */\nexport type FirstPersonSampleChannel = { p: Triple; r: Triple; grip: number | undefined; w: number; mirror: boolean }\nexport type FirstPersonSample = { R?: FirstPersonSampleChannel; L?: FirstPersonSampleChannel; F?: FirstPersonSampleChannel }\n\nconst ease = (k: number): number => k * k * (3 - 2 * k)\nconst mix = (a: number, b: number, k: number): number => a + (b - a) * k\nconst mixTriple = (a: Triple, b: Triple, k: number): Triple => [mix(a[0], b[0], k), mix(a[1], b[1], k), mix(a[2], b[2], k)]\n\ntype Track = { t: number; key: FirstPersonPoseKey; mirror: boolean }\n\nconst trackOf = (clip: Pick<FirstPersonClip, 'keys'>, side: 'R' | 'L' | 'F'): Track[] => {\n const out: Track[] = []\n for (const key of clip.keys) {\n if (side === 'L') {\n if (key.L === 'mirror') {\n if (key.R) out.push({ t: key.t, key: key.R, mirror: true })\n } else if (key.L) out.push({ t: key.t, key: key.L, mirror: false })\n } else {\n const entry = key[side]\n if (entry) out.push({ t: key.t, key: entry, mirror: false })\n }\n }\n return out.sort((a, b) => a.t - b.t)\n}\n\nconst sampleTrack = (track: Track[], time: number): FirstPersonSampleChannel | undefined => {\n if (track.length === 0) return undefined\n const first = track[0]!\n const last = track[track.length - 1]!\n const of = (entry: Track): FirstPersonSampleChannel => ({ p: [...entry.key.p], r: [...entry.key.r], grip: entry.key.grip, w: entry.key.w ?? 1, mirror: entry.mirror })\n if (time <= first.t) return of(first)\n if (time >= last.t) return of(last)\n let index = 0\n while (index < track.length - 2 && time > track[index + 1]!.t) index++\n const a = track[index]!\n const b = track[index + 1]!\n const k = ease(Math.min(1, Math.max(0, (time - a.t) / (b.t - a.t))))\n const ga = a.key.grip\n const gb = b.key.grip\n return {\n p: mixTriple(a.key.p, b.key.p, k),\n r: mixTriple(a.key.r, b.key.r, k),\n grip: ga === undefined ? gb : gb === undefined ? ga : mix(ga, gb, k),\n w: mix(a.key.w ?? 1, b.key.w ?? 1, k),\n mirror: a.mirror,\n }\n}\n\n/** Where each driven channel is at `time`: between two keys of a channel the numbers are blended with the smoothstep of `samplePoseClip`; before its first key and after its last it holds them. */\nexport const sampleFirstPersonClip = (clip: Pick<FirstPersonClip, 'keys'>, time: number): FirstPersonSample => {\n const out: FirstPersonSample = {}\n for (const side of ['R', 'L', 'F'] as const) {\n const sample = sampleTrack(trackOf(clip, side), time)\n if (sample) out[side] = sample\n }\n return out\n}\n","{\n \"sources\": {\n \"store\": \"jogo.js G 66714-66734 (Z, ZMAX 6000, FIELDS)\",\n \"bands\": \"jogo.js G 62019-62020 (BANDS, HYST)\",\n \"flow\": \"jogo.js G 66735-66737, 66773-66825 (FLOW_R 192, flowStart, flowStep; budget 1 ms a frame, the first field in one go)\",\n \"update\": \"jogo.js G 66835-66977 hordeUpdate\",\n \"alerts\": \"jogo.js G 66978-66993 groan, screamAt\",\n \"attack\": \"jogo.js G 66996-67006 zAttack\",\n \"damage\": \"jogo.js G 67010-67045 zDamage\",\n \"kill\": \"jogo.js G 67046-67059 zKill, inchadoBurst\",\n \"corpses\": \"jogo.js G 62021 (CORPOS_MAX 400, CORPO_VIDA 900), 67062-67075 corpseAdd, corpsesUpdate\",\n \"population\": \"jogo.js G 67139-67177 spawnAway, populationUpdate (popTarget 1200, migration 120 every 90 s, the first after 60 s)\",\n \"pile\": \"jogo.js G 67184-67227 (PILE_STEP 0.48, PILE_TOP 3.7, PILE_MAX 160, groundAt, zBlockedAt, tryStep, stepRelease, pileBlast)\",\n \"trail\": \"jogo.js G 67238-67246 trailUpdate\",\n \"abilities\": \"jogo.js G 67266-67356 zAbility, zChaseSpeed, zHurtOther, killQ\",\n \"frame\": \"jogo.js G 67377-67402 zFrame\",\n \"cadence\": \"jogo.js G 65507 walkCad\"\n },\n \"bands\": [\n { \"max\": 35, \"dt\": 0.05 },\n { \"max\": 120, \"dt\": 0.2 },\n { \"max\": 400, \"dt\": 1 },\n { \"max\": null, \"dt\": 5 }\n ],\n \"numbers\": {\n \"capacity\": 6000,\n \"hysteresis\": 10,\n \"flowRadius\": 192,\n \"flowBudgetMs\": 1,\n \"hashCell\": 1,\n \"nightSight\": 0.4,\n \"crouchSight\": 0.6,\n \"sightCone\": 0.5,\n \"sightNear\": 2.5,\n \"loseAfter\": 15,\n \"hearingBase\": 40,\n \"heardLost\": 5,\n \"alertRadius\": 8,\n \"alertDelay\": 0.2,\n \"alertDelayRandom\": 0.4,\n \"voiceDistance\": 40,\n \"voiceEvery\": 0.25,\n \"directRange\": 4,\n \"pyramidRange\": 14,\n \"screamSpeed\": 1.2,\n \"attackRange\": 1.15,\n \"attackHeight\": 1.3,\n \"attackSlots\": 6,\n \"attackEvery\": 1.5,\n \"crouchBite\": 1.3,\n \"crawlDamage\": 0.6,\n \"wanderSpan\": 24,\n \"crawlSpeed\": 0.4,\n \"limpSpeed\": 0.5,\n \"separation\": 0.31,\n \"separationPush\": 2.2,\n \"doorDamage\": 10,\n \"doorMessageDistance\": 25,\n \"skullMinDamage\": 20,\n \"skullTakeDamage\": 60,\n \"skullTakeWithin\": { \"12ga\": 5 },\n \"jawDamage\": 25,\n \"jawChance\": 0.3,\n \"hitStunDamage\": 35,\n \"hitStun\": 0.45,\n \"dormantWake\": 3,\n \"dormantWakeBehind\": 10,\n \"pileStep\": 0.48,\n \"pileTop\": 3.7,\n \"pileMax\": 160,\n \"pileReleaseDistance\": 30,\n \"gravity\": 9.8,\n \"fallStun\": 3.2,\n \"climbSpeed\": 1.9,\n \"cadenceBase\": 0.5,\n \"cadencePerSpeed\": 0.3,\n \"corpsesMax\": 400,\n \"corpseSeconds\": 900,\n \"trailSeconds\": 120,\n \"trailStep\": 1.5,\n \"trailMax\": 200,\n \"population\": 1200,\n \"refillMin\": 70,\n \"refillMax\": 160,\n \"migrationFirst\": 60\n }\n}\n","import type { NavGrid } from './grid'\n\nexport type Door = {\n readonly x: number\n readonly z: number\n /** The leaf runs along `x` (front or back wall) or `z`. */\n readonly axis: 'x' | 'z'\n readonly width: number\n hp: number\n hpMax: number\n open: boolean\n broken: boolean\n planks: number\n readonly rect: readonly [number, number, number, number]\n /** Whatever the game hangs on it (the building it belongs to). */\n readonly data?: unknown\n}\n\nexport type DoorEventMap = {\n sound: { sound: 'wood'; volume: number; position: [number, number, number] }\n noise: { x: number; z: number; radius: number }\n effect: { effect: 'dust'; point: [number, number, number]; color: number }\n broken: { door: number }\n}\nexport type DoorEventName = keyof DoorEventMap\n\nexport type DoorNumbers = {\n /** Hit points of a door (G 63803 `PORTA_VIDA`). */\n readonly doorHp: number\n /** Hit points each plank adds (G 63803 `TABUA_VIDA`). */\n readonly plankHp: number\n readonly height: number\n readonly thickness: number\n readonly soundChance: number\n readonly soundVolume: number\n readonly breakVolume: number\n readonly breakNoise: number\n}\n\nexport const DOOR_DEFAULTS: DoorNumbers = { doorHp: 300, plankHp: 150, height: 2.2, thickness: 0.1, soundChance: 0.3, soundVolume: 0.8, breakVolume: 1.5, breakNoise: 30 }\n\nexport type Doors = {\n readonly list: readonly Door[]\n /** The door index at a grid cell, -1 for none. */\n at(cell: number): number\n add(x: number, z: number, axis: 'x' | 'z', width: number, data?: unknown): number\n /** Opens or closes: the cells become passable (3) or a closed door (5). A broken door stays open. */\n set(index: number, open: boolean): void\n /** An agent hits a closed door (G 64044 `doorHit`): the 30% knock, planks giving way, and the door breaking open. */\n hit(index: number, damage: number): void\n /** Nails a plank on (each adds `plankHp`). */\n plank(index: number): void\n on<K extends DoorEventName>(event: K, listener: (event: DoorEventMap[K]) => void): () => void\n}\n\n/** The doors of the game's buildings on a grid (G 63796-63816 `DOORS`, `doorAt`, `addDoor`, `setDoor`; G 64044 `doorHit`). */\nexport function createDoors(grid: NavGrid, options: { random: () => number; numbers?: Partial<DoorNumbers> }): Doors {\n const N: DoorNumbers = { ...DOOR_DEFAULTS, ...options.numbers }\n const random = options.random\n const list: Door[] = []\n const doorAt = new Int16Array(grid.n * grid.n).fill(-1)\n const listeners = new Map<DoorEventName, Set<(event: never) => void>>()\n const emit = <K extends DoorEventName>(event: K, payload: DoorEventMap[K]): void => {\n for (const listener of listeners.get(event) ?? []) (listener as (event: DoorEventMap[K]) => void)(payload)\n }\n const set = (index: number, open: boolean): void => {\n const d = list[index]\n if (!d) return\n d.open = open\n grid.fillRect(...d.rect, open || d.broken ? 3 : 5, N.height)\n }\n return {\n list,\n at: (cell) => (cell < 0 ? -1 : doorAt[cell]!),\n add(x, z, axis, width, data) {\n const i = list.length\n const t = N.thickness\n const x0 = axis === 'x' ? x - width / 2 : x - t\n const x1 = axis === 'x' ? x + width / 2 : x + t\n const z0 = axis === 'x' ? z - t : z - width / 2\n const z1 = axis === 'x' ? z + t : z + width / 2\n list.push({ x, z, axis, width, hp: N.doorHp, hpMax: N.doorHp, open: false, broken: false, planks: 0, rect: [x0, z0, x1, z1], data })\n grid.fillRect(x0, z0, x1, z1, 5, N.height)\n const a = Math.floor((x0 + grid.half) / grid.cell)\n const b = Math.ceil((x1 + grid.half) / grid.cell)\n const c = Math.floor((z0 + grid.half) / grid.cell)\n const e = Math.ceil((z1 + grid.half) / grid.cell)\n for (let gz = c; gz < e; gz++) for (let gx = a; gx < b; gx++) doorAt[gz * grid.n + gx] = i\n return i\n },\n set,\n hit(index, damage) {\n const d = list[index]\n if (!d || d.open || d.broken) return\n d.hp -= damage\n if (random() < N.soundChance) emit('sound', { sound: 'wood', volume: N.soundVolume, position: [d.x, 1, d.z] })\n if (d.planks && d.hp < d.hpMax - d.planks * N.plankHp + N.plankHp * (d.planks - 1)) d.planks = Math.max(0, d.planks - 1)\n if (d.hp <= 0) {\n d.broken = true\n d.planks = 0\n set(index, true)\n emit('noise', { x: d.x, z: d.z, radius: N.breakNoise })\n emit('sound', { sound: 'wood', volume: N.breakVolume, position: [d.x, 1, d.z] })\n emit('effect', { effect: 'dust', point: [d.x, 1, d.z], color: 0x8a6a4a })\n emit('broken', { door: index })\n }\n },\n plank(index) {\n const d = list[index]\n if (!d) return\n d.planks += 1\n d.hp += N.plankHp\n d.hpMax += N.plankHp\n },\n on(event, listener) {\n let set_ = listeners.get(event)\n if (!set_) listeners.set(event, (set_ = new Set()))\n set_.add(listener as (event: never) => void)\n return () => void set_.delete(listener as (event: never) => void)\n },\n }\n}\n","/** Occupancy codes of a grid cell (G 62162: 0 free, 1 solid, 2 low wall that can be climbed, 3 open door, 4 glass; 5 closed door, G 63816). */\nexport const CELL = { FREE: 0, SOLID: 1, LOW_WALL: 2, OPEN_DOOR: 3, GLASS: 4, CLOSED_DOOR: 5 } as const\n\nexport type NavGridOptions = {\n /** Side of the square world in metres, centred on the origin (default 768, the game's world). */\n readonly size?: number\n /** Cell side in metres (default 0.5). */\n readonly cell?: number\n /** Fill a solid border of `borderWidth` m and `borderHeight` m around the world, as the game does (default true). */\n readonly border?: boolean\n}\n\n/**\n * The navigation and collision grid of the game (G 62158-62170): a square world of cells with an occupancy code and a height. Agents, the player,\n * the flow field and line of sight all read it. Buildings write it with `fillRect`.\n */\nexport type NavGrid = {\n readonly size: number\n readonly half: number\n readonly cell: number\n /** Cells per side. */\n readonly n: number\n readonly occ: Uint8Array\n readonly occH: Float32Array\n cellOf(x: number, z: number): number\n /** Solid for a body at height `y` (default 0.5): outside the world, or an occupied cell that is not an open door and is taller than `y`. */\n solidAt(x: number, z: number, y?: number): boolean\n /** The occupancy code at (x, z), -1 outside. */\n cellKind(x: number, z: number): number\n fillRect(x0: number, z0: number, x1: number, z1: number, kind: number, height: number): void\n /** Blocked for a walking agent: outside, or anything but free and open door (G 66826 `zBlocked`). */\n blocked(x: number, z: number): boolean\n /** Nothing solid and taller than 1.5 m between the two points, sampled every 0.5 m (G 66828 `losClear`). */\n losClear(x0: number, z0: number, x1: number, z1: number): boolean\n}\n\n/** The cell index of a world position for a square grid of `n` cells of `cell` metres centred on the origin, -1 outside. */\nexport const createCellOf =\n (half: number, cell: number, n: number) =>\n (x: number, z: number): number => {\n const gx = Math.floor((x + half) / cell)\n const gz = Math.floor((z + half) / cell)\n return gx < 0 || gz < 0 || gx >= n || gz >= n ? -1 : gz * n + gx\n }\n\nconst BORDER_WIDTH = 1\nconst BORDER_HEIGHT = 50\nconst LOS_STEP = 0.5\nconst LOS_HEIGHT = 1.5\n\nexport function createNavGrid(options: NavGridOptions = {}): NavGrid {\n const size = options.size ?? 768\n const half = size / 2\n const cell = options.cell ?? 0.5\n const n = size / cell\n const occ = new Uint8Array(n * n)\n const occH = new Float32Array(n * n)\n const cellOf = createCellOf(half, cell, n)\n const solidAt = (x: number, z: number, y = 0.5): boolean => {\n const c = cellOf(x, z)\n return c < 0 || (occ[c] !== 0 && occ[c] !== 3 && occH[c]! > y)\n }\n const fillRect = (x0: number, z0: number, x1: number, z1: number, kind: number, h: number): void => {\n const a = Math.floor((x0 + half) / cell)\n const b = Math.ceil((x1 + half) / cell)\n const c = Math.floor((z0 + half) / cell)\n const d = Math.ceil((z1 + half) / cell)\n for (let gz = Math.max(0, c); gz < Math.min(n, d); gz++)\n for (let gx = Math.max(0, a); gx < Math.min(n, b); gx++) {\n const i = gz * n + gx\n occ[i] = kind\n occH[i] = h\n }\n }\n if (options.border ?? true) {\n fillRect(-half, -half, half, -half + BORDER_WIDTH, 1, BORDER_HEIGHT)\n fillRect(-half, half - BORDER_WIDTH, half, half, 1, BORDER_HEIGHT)\n fillRect(-half, -half, -half + BORDER_WIDTH, half, 1, BORDER_HEIGHT)\n fillRect(half - BORDER_WIDTH, -half, half, half, 1, BORDER_HEIGHT)\n }\n return {\n size,\n half,\n cell,\n n,\n occ,\n occH,\n cellOf,\n solidAt,\n cellKind: (x, z) => {\n const c = cellOf(x, z)\n return c < 0 ? -1 : occ[c]!\n },\n fillRect,\n blocked(x, z) {\n const c = cellOf(x, z)\n return c < 0 || (occ[c] !== 0 && occ[c] !== 3)\n },\n losClear(x0, z0, x1, z1) {\n const d = Math.hypot(x1 - x0, z1 - z0)\n const steps = Math.ceil(d / LOS_STEP)\n for (let k = 1; k < steps; k++) {\n const t = k / steps\n const c = cellOf(x0 + (x1 - x0) * t, z0 + (z1 - z0) * t)\n if (c < 0 || (occ[c] === 1 && occH[c]! > LOS_HEIGHT)) return false\n }\n return true\n },\n }\n}\n","/** murmur3 fmix32: a full avalanche of the 32 bits. */\nexport const mix32 = (value: number): number => {\n let hash = value >>> 0\n hash ^= hash >>> 16\n hash = Math.imul(hash, 0x85ebca6b)\n hash ^= hash >>> 13\n hash = Math.imul(hash, 0xc2b2ae35)\n hash ^= hash >>> 16\n return hash >>> 0\n}\n\n/**\n * 0..1 from an identity, a step and a salt: the counter-based draw every physics feature uses (never the shared `random` stream, whose order is the game's).\n * The salt goes in before the step so two different `(step, salt)` pairs never share an input.\n */\nexport const unit = (identity: number, step: number, salt: number): number => (mix32((mix32(identity ^ salt) + Math.imul(step + 1, 0x9e3779b9)) >>> 0) >>> 0) / 4294967296\n\n/** The identity of the `serial`-th agent born into a crowd seeded with `seed`. */\nexport const identityOf = (seed: number, serial: number): number => mix32(seed ^ mix32(serial))\n\n/** One salt per use, so draws of the same agent at the same step stay independent. */\nexport const SALT = {\n FALL: 0x0f411,\n TRIP: 0x07219,\n LIMB: 0x1b100,\n REGION: 0x2e610,\n JAW: 0x3a7b0,\n CORPSE: 0x4c0de,\n SPIN: 0x5917a,\n SPEED: 0x6511d,\n REACTION: 0x7201c,\n STABILITY: 0x8b21e,\n TRIP_FACTOR: 0x93b5d,\n DRIVE: 0xa00d3,\n CADENCE: 0xb1c4e,\n AGGRESSION: 0xc2a97,\n IDLE: 0xd3e08,\n GAIT: 0xe4f19,\n} as const\n","/** What an agent is doing (G 66724 and 67180-67183): the store keeps the number. */\nexport const STATE = {\n WANDER: 0,\n CHASE: 1,\n GRAB: 2,\n STUNNED: 3,\n CALLING: 4,\n AIRBORNE: 5,\n DORMANT: 6,\n SWELLING: 7,\n SWING: 8,\n EATING: 9,\n PILE_STEP: 10,\n SEEKING_SOUND: 12,\n} as const\n\n/** Balance of an agent (physics): a state machine of its own, apart from `STATE`, so the game's behaviour machine stays as it is. */\nexport const BALANCE = { STANDING: 0, STAGGERING: 1, FALLEN: 2, GETTING_UP: 3, THROWN: 4 } as const\n\n/** Why an agent went down (index `cause - 1` in `stats.physics.falls`). */\nexport const FALL_CAUSE = { NONE: 0, PRESSURE: 1, SHOVE: 2, TRIP: 3, LANDING: 4, COLLAPSE: 5, BLAST: 6 } as const\n\n/** Body regions of a hit (G 67012): 0 head, 1 torso, 2 left arm, 3 right arm, 4 left leg, 5 right leg. */\nexport type Region = 0 | 1 | 2 | 3 | 4 | 5\n\n/**\n * Missing parts as bits (G 67010-67045): 1 skull, 2 left arm off, 4 right arm off, 8 left leg off, 16 right leg off, 32 left arm broken, 64 right arm broken,\n * 128 left leg broken, 256 right leg broken, 512 jaw.\n */\nexport const MISSING = { SKULL: 1, ARM_L: 2, ARM_R: 4, LEG_L: 8, LEG_R: 16, ARM_L_BROKEN: 32, ARM_R_BROKEN: 64, LEG_L_BROKEN: 128, LEG_R_BROKEN: 256, JAW: 512 } as const\n\n/**\n * The type traits: the special moves of a type, read in the game's order. A type without a trait does not have the move. Every number is the game's\n * (`jogo.js` G 67180-67430, the Bestiário table 3.1 and 12.7).\n */\nexport type CrowdTraits = {\n /** Jumps a low obstacle in the way while chasing (car, yard wall) up to `maxHeight` m (runner, leaper). */\n readonly vaultsLow?: { readonly maxHeight: number; readonly minRise: number; readonly clearance: number; readonly speedFactor: number }\n /** Pounce from `min` to `max` m in line of sight: cooldown, flight time, speed cap, the vertical speeds and the knock-down on landing (leaper). */\n readonly pounce?: { readonly min: number; readonly max: number; readonly cooldown: number; readonly time: number; readonly speedCap: number; readonly lift: number; readonly liftHigh: number; readonly highAbove: number; readonly landRange: number; readonly knock: number }\n /** A heavy swing: chance per attack, wind-up, reach and knock-down (brute). */\n readonly heavySwing?: { readonly chance: number; readonly windUp: number; readonly range: number; readonly knock: number }\n /** Multiplies the damage on doors (brute 3). */\n readonly doorDamage?: number\n /** Seconds stunned by a heavy hit (default `numbers.hitStun`; brute 0.25). */\n readonly hitStun?: number\n /** Only hears: goes to the source of a sound and hits whatever it meets on the way, other agents too (blind). */\n readonly soundSeeker?: { readonly arrive: number; readonly loseChase: number; readonly hitRadius2: number }\n /** Calls the crowd: wind-up, radius, cooldown, the boost it gives the target, and running from the fight afterwards (screamer). */\n readonly caller?: { readonly range: number; readonly windUp: number; readonly radius: number; readonly cooldown: number; readonly boost: number; readonly fleeAbove: number; readonly fleeRange: number }\n /** Sets the target on fire on a hit (charred). */\n readonly burns?: { readonly seconds: number }\n /** Loses a share of its health the first time it enters water (charred 0.7). */\n readonly waterWeak?: { readonly keep: number; readonly cloud: number }\n /** Waits under water and pulls the target down (drowned). */\n readonly submerged?: { readonly depth: number; readonly wake: number; readonly swim: number; readonly surface: number }\n /** Plays dead and wakes close or behind the target, then sprints for a while (stalker). */\n readonly ambush?: { readonly chance: number; readonly wake: number; readonly wakeBehind: number; readonly behindCos: number; readonly sprint: number; readonly sprintSeconds: number; readonly slow: number }\n /** Swells and bursts close to the target: blinds and marks it with a scent (bloater). */\n readonly burst?: { readonly range: number; readonly swell: number; readonly radius: number; readonly blind: number; readonly scent: number; readonly noiseNear: number; readonly noise: number; readonly cloudColor: number; readonly bloodColor: number; readonly bloodDry: number }\n /** Eats corpses: search radius, reach, seconds, how many, what it gains (devourer). */\n readonly eatsCorpses?: { readonly max: number; readonly search: number; readonly reach: number; readonly seconds: number; readonly grabRange: number; readonly health: number; readonly grow: number; readonly rest: number; readonly retry: number; readonly defend: number; readonly approach: number }\n /** Follows the target's blood trail and leads agents of the type `leads` along (sniffer). */\n readonly tracker?: { readonly range: number; readonly leads: string; readonly max: number; readonly leadRadius: number }\n /** Never older than this age index (runner: recent at most). */\n readonly maxAge?: number\n /** The groan sound id (runner `runner_groan`; default `groan`). */\n readonly voice?: string\n /** How much it adds to the target's panic (runner 2.5). */\n readonly panicWeight?: number\n /** Pulls the target into water when it grabs there (drowned). */\n readonly drownGrab?: boolean\n /** Body colour and skin override at birth (charred, drowned). */\n readonly color?: number\n readonly skin?: number\n}\n\n/** One type of agent (a zombie type, a cow, a civilian). The undead pack (`undeadCrowdTypes`) gives the 13 types of the game. */\nexport type CrowdType = {\n readonly id: string\n /** Weight in the draw of an agent without a forced type (0: never drawn). */\n readonly weight: number\n readonly health: number\n readonly speed: { readonly wander: number; readonly chase: number }\n readonly damage: number\n /** Chance per attack to grab (and bite). */\n readonly bite: number\n readonly multipliers: { readonly head: number; readonly torso: number; readonly arms: number; readonly legs: number }\n /** Metres. */\n readonly perception: { readonly sight: number; readonly hearing: number; readonly smell: number }\n readonly crawls?: boolean\n /** Height multiplier (brute 1.42). */\n readonly scale?: number\n /** Build multiplier (bloater 1.35; a scaled type without one is 1.3). */\n readonly build?: number\n /** Body variants of the type for the renderer (walker 4, runner 3, crawler and brute 2, others 1). */\n readonly variants?: number\n readonly eye?: number\n readonly traits?: CrowdTraits\n /** How the body takes a push (physics only; a type without it uses the defaults). */\n readonly balance?: CrowdBalanceTraits\n}\n\nexport type CrowdBalanceTraits = {\n /** Multiplies the critical pressure (default 1). */\n readonly stability?: number\n /** Chance of tripping when entering a cell with a body (default 0.04). */\n readonly trip?: number\n /** Force of its will against what is in front, in N (default 250). */\n readonly driveForce?: number\n /** False: already on the ground (a crawler). */\n readonly canFall?: boolean\n}\n\n/** A blast as the crowd reads it. */\nexport type CrowdBlast = {\n readonly point: readonly [number, number, number]\n /** J. */\n readonly energy: number\n /** m, how far it hurts. */\n readonly radius: number\n /** m, everything inside dies. */\n readonly lethalRadius?: number\n readonly source?: string\n /** Id of the chain this blast belongs to (game-destruction `chain`), for the counters. */\n readonly chain?: string\n /**\n * The share (0..1) of the blast that gets to a point through the cover in the way (the host's `blastReach`): the shove and the damage of an agent behind a wall\n * are cut by it. Without it the whole blast reaches every agent.\n */\n readonly reach?: (target: readonly [number, number, number]) => number\n}\n\n/** The age of a body (G IDADES): its share, and what it does to speed, health, limbs and senses. */\nexport type CrowdAge = { readonly id: string; readonly p: number; readonly speed: number; readonly health: number; readonly limb: number; readonly sight: number; readonly hearing: number; readonly blood: number; readonly bloodColor: number; readonly dryColor: number }\n/** What a body wears (G ROUPAS): its share, the vest against pistol and rifle classes, a helmet, a limb factor and the colour. */\nexport type CrowdOutfit = { readonly id: string; readonly p: number; readonly vest?: { readonly pistol: number; readonly rifle: number }; readonly helmet?: boolean; readonly limb?: number; readonly color: number | null }\n\n/** Everything the crowd draws a body from. */\nexport type CrowdPack = {\n readonly types: readonly CrowdType[]\n readonly ages: readonly CrowdAge[]\n readonly outfits: readonly CrowdOutfit[]\n /** Hit points of the segments, only for dismembering (G `SEG`). */\n readonly segments: { readonly head: number; readonly arm: number; readonly leg: number }\n /** Clothes colours of the outfits without one (G `CIVIL_COR`). */\n readonly palette: readonly number[]\n}\n\n/** The calibre of a projectile hit: its class (pistol or rifle, for vests and helmets), the skull distance (1: any) and an id. */\nexport type CrowdCaliber = { readonly id: string; readonly class: 'pistol' | 'rifle'; readonly skull: number }\nexport type CrowdHit = { readonly region: Region; readonly amount: number; readonly distance: number; readonly cut?: number; readonly caliber?: CrowdCaliber | null }\nexport type CrowdDamageResult = { readonly died: boolean; readonly headshot?: boolean }\n\n/**\n * Who the crowd hunts. `game-player`'s `Player` is one as it is; a game with its own player writes a small object with the same names.\n * Reads: `state`. Writes: the verbs, and `state.burnTimer`, `state.blind`, `state.scentTimer`, `state.screamBoost`.\n */\nexport interface CrowdTarget {\n readonly state: {\n x: number\n z: number\n y: number\n floor: number\n alive: boolean\n turned: boolean\n crouch: boolean\n bleeding: number\n scentTimer: number\n screamBoost: number\n grabbedBy: number\n blind: number\n burnTimer: number\n /** The body's yaw; the view looks the other way (`yaw - π`). */\n yaw: number\n }\n grab(by: number, noJaw?: boolean, drown?: boolean): void\n hurt(amount: number, by?: number): void\n knock(seconds: number): void\n release(): void\n}\n\nexport type CrowdSoundId = 'groan' | 'runner_groan' | 'scream_hoarse' | 'step' | 'impact' | 'flesh' | 'wood' | (string & {})\nexport type CrowdEffectId = 'spark' | 'blood' | 'limb' | 'cloud' | 'dust' | (string & {})\nexport type CrowdMessageId = 'called' | 'burst_hit' | 'swing_knock' | 'pounce_knock' | 'migration' | 'door_broken' | (string & {})\n\nexport type CrowdEventMap = {\n sound: { sound: CrowdSoundId; volume: number; position: [number, number, number]; distance: number }\n effect: { effect: CrowdEffectId; point: [number, number, number]; agent?: number; region?: number; severed?: boolean; radius?: number; color?: number; amount?: number; died?: boolean; age?: number }\n message: { id: CrowdMessageId; seconds: number; type?: string }\n spawn: { agent: number; type: string }\n alert: { agent: number }\n kill: { agent: number; type: string; headshot: boolean; x: number; z: number }\n remove: { agent: number; moved: number }\n scream: { agent: number; x: number; z: number }\n fall: { agent: number; cause: number; x: number; z: number; identity: number }\n blasted: { point: [number, number, number]; radius: number; reached: number; killed: number; thrown: number; fallen: number; chain?: string }\n}\nexport type CrowdEventName = keyof CrowdEventMap\n\n/** A body on the ground (G 67062 `CP`). */\nexport type Corpse = {\n readonly x: number\n readonly z: number\n readonly yaw: number\n readonly height: number\n readonly build: number\n readonly missing: number\n readonly color: number\n /** Body variant of the renderer, -1 for a body that was not an agent (a civilian). */\n readonly variant: number\n readonly skin: number\n readonly seed: number\n readonly named: number\n}\n","import { SALT, unit } from '../identity'\nimport { BALANCE, FALL_CAUSE, STATE } from '../types'\nimport type { PhysicsContext } from './context'\nimport type { CrowdBalanceOptions } from './options'\n\nconst GRAVITY_FORCE = 9.8\nconst GROUND_MASS_FACTOR = 3\n/** Speed above which entering a cell with a body can trip (m/s), and the speed the trip chance is given for. */\nconst TRIP_MIN_SPEED = 0.5\nconst TRIP_REFERENCE_SPEED = 1.4\nconst TRIP_MOUND = 150\nconst TRIP_CLEARANCE = 0.1\n/** Drop height (m) that makes a body stagger, and that knocks it down (`design-chain-horde.md` §6, item 5). */\nconst COLLAPSE_STAGGER = 0.6\nconst COLLAPSE_FALL = 1\nconst CAUSE_ORDER = [FALL_CAUSE.PRESSURE, FALL_CAUSE.SHOVE, FALL_CAUSE.TRIP, FALL_CAUSE.LANDING, FALL_CAUSE.COLLAPSE, FALL_CAUSE.BLAST]\nconst CHAIN_WINDOW_STEPS = 240\n\n/** A fall that waited for its turn (the ceiling of falls per step) is dropped after this many steps. */\nconst CARRY_STEPS = 24\n/** Pressure and trip are drawn again every step; the rest (a shove, a landing, a collapse, a blast) is a fact and waits for its turn. */\nconst isCarried = (cause: number): boolean => cause !== FALL_CAUSE.PRESSURE && cause !== FALL_CAUSE.TRIP\n\ntype FallRequest = { index: number; identity: number; cause: number; score: number; born: number }\n\nexport type Balance = {\n /** Asks for agent `i` to go down; the ones with the highest `score` go first, up to the ceiling of the step. */\n requestFall(i: number, cause: number, score: number): void\n /** Agent `i` starts staggering if it stands. */\n stagger(i: number): void\n /** A push of `speed` m/s (an increment) on a standing agent: staggers it, or asks for the fall, by its stability. */\n shove(i: number, speed: number, cause: number): void\n step(options: Required<CrowdBalanceOptions>, contactShove: Float32Array): void\n /** Carries out the requests of the step, in the order of the score. */\n flush(): void\n /** A body landed after a drop of `height` m (a pile or a mound gave way). */\n landed(i: number, height: number): void\n /** Falls requested and not yet carried out. */\n pending(): number\n}\n\nexport function createBalance(ctx: PhysicsContext): Balance {\n const { store } = ctx\n const requests: FallRequest[] = []\n const balanceOptions = (): Required<CrowdBalanceOptions> => ctx.options.balance!\n\n const stabilityOf = (i: number): number => (ctx.typeOf(i).balance?.stability ?? 1) * store.stability[i]!\n const canFall = (i: number): boolean => ctx.typeOf(i).balance?.canFall !== false\n const criticalOf = (i: number): number => balanceOptions().criticalFactor * store.mass[i]! * GRAVITY_FORCE * stabilityOf(i)\n\n const requestFall = (i: number, cause: number, score: number): void => {\n if (!canFall(i)) return\n requests.push({ index: i, identity: store.identity[i]!, cause, score, born: ctx.step })\n }\n\n const stagger = (i: number): void => {\n if (store.balance[i] !== BALANCE.STANDING || !canFall(i)) return\n store.balance[i] = BALANCE.STAGGERING\n store.balanceTimer[i] = 0\n }\n\n const shove = (i: number, speed: number, cause: number): void => {\n const options = balanceOptions()\n const knockSpeed = options.shoveFall * stabilityOf(i)\n if (store.balance[i] !== BALANCE.STANDING && store.balance[i] !== BALANCE.STAGGERING) return\n if (speed > knockSpeed) requestFall(i, cause, speed / knockSpeed)\n else if (speed > options.shoveStagger) stagger(i)\n }\n\n const countsAsChainFall = (i: number, cause: number): boolean => {\n if (cause === FALL_CAUSE.PRESSURE || cause === FALL_CAUSE.BLAST) return false\n if (ctx.step - ctx.lastBlastStep > CHAIN_WINDOW_STEPS) return false\n const direct = store.blastStep[i] === ctx.lastBlastStep ? store.directShove[i]! : 0\n return direct < balanceOptions().shoveStagger\n }\n\n const goDown = (i: number, cause: number): void => {\n ctx.stats.falls[CAUSE_ORDER.indexOf(cause as (typeof CAUSE_ORDER)[number])]!++\n if (countsAsChainFall(i, cause)) ctx.stats.fallsBeyondBlast++\n if (store.state[i] === STATE.PILE_STEP) ctx.releasePileStep(i)\n store.balance[i] = BALANCE.FALLEN\n store.balanceTimer[i] = 0\n store.fallCause[i] = cause\n store.vx[i] = store.vz[i] = 0\n store.pushX[i]! /= GROUND_MASS_FACTOR\n store.pushZ[i]! /= GROUND_MASS_FACTOR\n store.aimX[i] = store.aimZ[i] = 0\n const target = ctx.target()\n if (target && target.state.grabbedBy === i) target.release()\n ctx.emit('fall', { agent: i, cause, x: store.x[i]!, z: store.z[i]!, identity: store.identity[i]! })\n }\n\n const flush = (): void => {\n if (!requests.length) return\n const options = balanceOptions()\n requests.sort((a, b) => b.score - a.score || a.identity - b.identity)\n let fallen = ctx.stats.fallen\n let fallsLeft = options.maxFallsPerStep\n const waiting: FallRequest[] = []\n for (const request of requests) {\n const i = request.index\n if (i >= store.n || store.identity[i] !== request.identity) continue\n const balance = store.balance[i]\n if (balance !== BALANCE.STANDING && balance !== BALANCE.STAGGERING) continue\n if (fallsLeft > 0 && fallen < options.maxFallen) {\n goDown(i, request.cause)\n fallsLeft--\n fallen++\n continue\n }\n stagger(i)\n if (isCarried(request.cause) && ctx.step - request.born < CARRY_STEPS) waiting.push(request)\n }\n requests.length = 0\n requests.push(...waiting)\n }\n\n const hasBodyOnTop = (i: number): boolean => {\n const cell = ctx.grid.cellOf(store.x[i]!, store.z[i]!)\n if (cell < 0) return false\n const options = ctx.options.mound\n if (!options) return false\n const top = store.y[i]! + options.bodyThickness * store.build[i]!\n return ctx.moundAt(cell) / 1000 > top + 0.05 || ctx.weightOn(i) > 0\n }\n\n const stepStanding = (i: number, ratio: number, contactShove: number, options: Required<CrowdBalanceOptions>): void => {\n if (contactShove > options.shoveFall * stabilityOf(i)) return requestFall(i, FALL_CAUSE.SHOVE, contactShove / (options.shoveFall * stabilityOf(i)))\n if (ratio > options.staggerEnter || contactShove > options.shoveStagger) stagger(i)\n else tryTrip(i)\n }\n\n const tryTrip = (i: number): void => {\n const cell = ctx.grid.cellOf(store.x[i]!, store.z[i]!)\n if (cell < 0 || cell === ctx.grid.cellOf(store.prevX[i]!, store.prevZ[i]!)) return\n const mound = ctx.moundAt(cell)\n if (mound < TRIP_MOUND || store.y[i]! >= mound / 1000 - TRIP_CLEARANCE) return\n const speed = Math.hypot(store.vx[i]! + store.pushX[i]!, store.vz[i]! + store.pushZ[i]!)\n if (speed <= TRIP_MIN_SPEED) return\n const chance = (ctx.typeOf(i).balance?.trip ?? 0.04) * store.tripFactor[i]! * (speed / TRIP_REFERENCE_SPEED)\n if (unit(store.identity[i]!, ctx.step, SALT.TRIP) < chance) requestFall(i, FALL_CAUSE.TRIP, 1)\n }\n\n const stepStaggering = (i: number, ratio: number, contactShove: number, options: Required<CrowdBalanceOptions>): void => {\n store.balanceTimer[i]! += ctx.h\n if (contactShove > options.shoveFall * stabilityOf(i)) return requestFall(i, FALL_CAUSE.SHOVE, contactShove / (options.shoveFall * stabilityOf(i)))\n if (ratio > 1 && unit(store.identity[i]!, ctx.step, SALT.FALL) < 1 - Math.exp(-options.fallRate * (ratio - 1) ** 2 * ctx.h)) return requestFall(i, FALL_CAUSE.PRESSURE, ratio)\n if (ratio < options.staggerLeave && store.balanceTimer[i]! >= options.staggerMin) {\n store.balance[i] = BALANCE.STANDING\n store.balanceTimer[i] = 0\n }\n }\n\n const stepFallen = (i: number, ratio: number, options: Required<CrowdBalanceOptions>): void => {\n store.balanceTimer[i]! += ctx.h\n if (store.balanceTimer[i]! < options.fallenMin + store.reaction[i]! || ratio >= options.releaseFactor || hasBodyOnTop(i)) return\n store.balance[i] = BALANCE.GETTING_UP\n store.balanceTimer[i] = 0\n }\n\n const step = (options: Required<CrowdBalanceOptions>, contactShove: Float32Array): void => {\n const smoothing = 1 - Math.exp(-ctx.h / options.pressureTau)\n let fallen = 0\n let staggering = 0\n for (let i = 0; i < store.n; i++) {\n const state = store.balance[i]!\n if (state === BALANCE.THROWN) continue\n store.pressure[i]! += (store.field[i]! - store.pressure[i]!) * smoothing\n const ratio = store.pressure[i]! / criticalOf(i)\n if (state === BALANCE.STANDING) stepStanding(i, ratio, contactShove[i]!, options)\n else if (state === BALANCE.STAGGERING) stepStaggering(i, ratio, contactShove[i]!, options)\n else if (state === BALANCE.FALLEN) stepFallen(i, ratio, options)\n else if ((store.balanceTimer[i]! += ctx.h) >= options.getUpSeconds) {\n store.balance[i] = BALANCE.STANDING\n store.balanceTimer[i] = 0\n }\n if (store.balance[i] === BALANCE.STAGGERING) staggering++\n else if (store.balance[i] === BALANCE.FALLEN || store.balance[i] === BALANCE.GETTING_UP) fallen++\n }\n ctx.stats.fallen = fallen\n ctx.stats.staggering = staggering\n }\n\n const landed = (i: number, height: number): void => {\n if (store.balance[i] !== BALANCE.STANDING && store.balance[i] !== BALANCE.STAGGERING) return\n if (height > COLLAPSE_FALL) requestFall(i, FALL_CAUSE.COLLAPSE, height / COLLAPSE_FALL)\n else if (height > COLLAPSE_STAGGER) stagger(i)\n }\n\n return { requestFall, stagger, shove, step, flush, landed, pending: () => requests.length }\n}\n","import { blastShove } from '@adatechnology/game-contracts'\n\nimport { SALT, unit } from '../identity'\nimport { BALANCE, FALL_CAUSE, MISSING, STATE } from '../types'\nimport type { CrowdBlast, Region } from '../types'\nimport type { Balance } from './balance'\nimport type { PhysicsContext } from './context'\nimport type { Thrown, ThrownCandidate } from './thrown'\n\nconst CENTRE_HEIGHT = 1\n/** Below this share of a blast getting through the cover, an agent inside the lethal radius is hurt by what reached it instead of killed outright. */\nconst SHELTERED = 0.5\nconst DISMEMBER_SHARE = 0.5\nconst LIMB_CHANCE = 0.5\nconst STUN_BASE = 0.45\nconst BLOOD_PER_BODY = 60\nconst LIMBS = [\n { bit: MISSING.ARM_L, region: 2 },\n { bit: MISSING.ARM_R, region: 3 },\n { bit: MISSING.LEG_L, region: 4 },\n { bit: MISSING.LEG_R, region: 5 },\n] as const\n\nexport type Blasts = {\n enqueue(blast: CrowdBlast): void\n /** Resolves up to `maxBlastsPerStep` blasts in one linear pass over the agents; the rest waits for the next step. */\n resolve(): void\n pending(): number\n}\n\ntype BlastTally = { reached: number; killed: number; fallen: number; limbEffects: number; bloodEffects: number; bloodSum: number }\n\nexport type BlastHooks = {\n readonly balance: Balance | null\n readonly thrown: Thrown | null\n pileBlast(x: number, z: number, radius: number): void\n}\n\nexport function createBlasts(ctx: PhysicsContext, hooks: BlastHooks): Blasts {\n const { store } = ctx\n const options = ctx.options.blast\n const queue: CrowdBlast[] = []\n\n const emitLimb = (tally: BlastTally, i: number, region: number): void => {\n if (tally.limbEffects++ >= options.limbEffectsPerBlast) return\n ctx.emit('effect', { effect: 'limb', point: [store.x[i]!, store.height[i]! * 0.9, store.z[i]!], agent: i, region, severed: true })\n }\n\n const emitBlood = (tally: BlastTally, i: number): void => {\n if (tally.bloodEffects++ >= options.bloodEffectsPerBlast) {\n tally.bloodSum += BLOOD_PER_BODY\n return\n }\n ctx.emit('effect', { effect: 'blood', point: [store.x[i]!, store.height[i]! * 1.2, store.z[i]!], agent: i, amount: BLOOD_PER_BODY, died: true })\n }\n\n const kill = (i: number, blast: CrowdBlast, distance: number, tally: BlastTally): void => {\n tally.killed++\n store.hp[i] = 0\n if (blast.lethalRadius !== undefined && distance < DISMEMBER_SHARE * blast.lethalRadius) {\n for (let k = 0; k < LIMBS.length; k++) {\n if (unit(store.identity[i]!, ctx.step, SALT.LIMB + k) >= LIMB_CHANCE) continue\n store.missing[i]! |= LIMBS[k]!.bit\n emitLimb(tally, i, LIMBS[k]!.region)\n }\n }\n emitBlood(tally, i)\n ctx.deferKill(i)\n }\n\n /** Damage by the falloff, and a blow to a limb with half of it; returns true when the agent died. */\n const hurt = (i: number, blast: CrowdBlast, distance: number, index: number, reach = 1): boolean => {\n const falloff = (1 - distance / blast.radius) ** 2 * reach\n const amount = (blast.energy / 1000) * options.pointsPerKilojoule * falloff\n const draw = (salt: number) => () => unit(store.identity[i]!, ctx.step, salt)\n if (ctx.damage(i, { region: 1, amount, distance, cut: falloff }, draw(SALT.JAW + index)).died) return true\n const region = Math.floor(unit(store.identity[i]!, ctx.step, SALT.REGION + index) * 6) as Region\n return ctx.damage(i, { region, amount: amount / 2, distance, cut: falloff }, draw(SALT.JAW + 8 + index)).died\n }\n\n /** Sums the shove of every blast on the agent: `[vx, vy, vz]` of the body, or undefined when no blast moves it. */\n const shoveOf = (i: number, blasts: readonly CrowdBlast[], reaches?: readonly number[]): { vx: number; vy: number; vz: number; falloff: number } | undefined => {\n const position: [number, number, number] = [store.x[i]!, store.y[i]! + CENTRE_HEIGHT * store.height[i]!, store.z[i]!]\n let vx = 0\n let vy = 0\n let vz = 0\n let falloff = 0\n for (let b = 0; b < blasts.length; b++) {\n const blast = blasts[b]!\n const shove = blastShove({ point: blast.point as [number, number, number], energy: blast.energy, radius: blast.radius, reach: reaches?.[b] ?? 1 }, { position, mass: store.mass[i]! }, options.shove)\n if (!shove) continue\n vx += shove.speed * shove.direction[0]\n vy += shove.speed * shove.direction[1]\n vz += shove.speed * shove.direction[2]\n falloff = Math.max(falloff, shove.falloff)\n }\n return vx || vy || vz ? { vx, vy, vz, falloff } : undefined\n }\n\n const distanceTo = (i: number, blast: CrowdBlast): number => Math.hypot(store.x[i]! - blast.point[0], store.y[i]! + CENTRE_HEIGHT * store.height[i]! - blast.point[1], store.z[i]! - blast.point[2])\n\n const applyShove = (i: number, shove: { vx: number; vy: number; vz: number; falloff: number }, candidates: ThrownCandidate[], tallies: BlastTally[], blasts: readonly CrowdBlast[]): void => {\n const horizontal = Math.hypot(shove.vx, shove.vz)\n const speed = Math.hypot(horizontal, shove.vy)\n store.directShove[i] = horizontal\n store.blastStep[i] = ctx.step\n const { balance, thrown } = hooks\n if (thrown && speed >= options.throwSpeed && (store.x[i]! - ctx.centre.x) ** 2 + (store.z[i]! - ctx.centre.z) ** 2 <= ctx.options.thrown!.radius ** 2) {\n candidates.push({ index: i, speed, vx: shove.vx, vy: shove.vy, vz: shove.vz, distance: Math.hypot(store.x[i]! - ctx.centre.x, store.z[i]! - ctx.centre.z) })\n return\n }\n if (store.hp[i]! <= 0) return\n if (!balance) {\n store.state[i] = STATE.STUNNED\n store.stateTimer[i] = STUN_BASE + shove.falloff\n store.vx[i] = shove.vx\n store.vz[i] = shove.vz\n return\n }\n store.pushX[i]! += shove.vx\n store.pushZ[i]! += shove.vz\n balance.shove(i, horizontal, FALL_CAUSE.BLAST)\n for (let b = 0; b < blasts.length; b++) if (distanceTo(i, blasts[b]!) <= blasts[b]!.radius && horizontal > ctx.options.balance!.shoveFall) tallies[b]!.fallen++\n }\n\n const affect = (i: number, blasts: readonly CrowdBlast[], tallies: BlastTally[], candidates: ThrownCandidate[]): void => {\n let dead = store.hp[i]! <= 0\n // the share of each blast that gets to the agent's chest through the cover in the way; 1 out of reach or when the blast says nothing\n const reaches = blasts.map((blast) =>\n blast.reach && distanceTo(i, blast) <= blast.radius ? blast.reach([store.x[i]!, store.y[i]! + CENTRE_HEIGHT * store.height[i]!, store.z[i]!]) : 1,\n )\n for (let b = 0; b < blasts.length && !dead; b++) {\n const blast = blasts[b]!\n const distance = distanceTo(i, blast)\n if (distance > blast.radius) continue\n tallies[b]!.reached++\n // behind solid cover the lethal core is not lethal: the agent takes the share that got through\n if (blast.lethalRadius !== undefined && distance < blast.lethalRadius && reaches[b]! >= SHELTERED) kill(i, blast, distance, tallies[b]!)\n else if (hurt(i, blast, distance, b, reaches[b])) tallies[b]!.killed++\n dead = store.hp[i]! <= 0 || dead\n }\n const shove = shoveOf(i, blasts, reaches)\n if (shove) applyShove(i, shove, candidates, tallies, blasts)\n }\n\n const resolve = (): void => {\n if (!queue.length) return\n const blasts = queue.splice(0, options.maxBlastsPerStep)\n const tallies: BlastTally[] = blasts.map(() => ({ reached: 0, killed: 0, fallen: 0, limbEffects: 0, bloodEffects: 0, bloodSum: 0 }))\n const candidates: ThrownCandidate[] = []\n ctx.lastBlastStep = ctx.step\n ctx.stats.blastChecks = store.n * blasts.length\n for (let i = 0; i < store.n; i++) if (store.balance[i] !== BALANCE.THROWN) affect(i, blasts, tallies, candidates)\n const thrown = hooks.thrown?.assign(candidates) ?? 0\n blasts.forEach((blast, b) => {\n const tally = tallies[b]!\n if (tally.bloodSum > 0) ctx.emit('effect', { effect: 'blood', point: [blast.point[0], blast.point[1], blast.point[2]], amount: tally.bloodSum, died: true })\n hooks.pileBlast(blast.point[0], blast.point[2], blast.radius)\n ctx.emit('blasted', {\n point: [blast.point[0], blast.point[1], blast.point[2]],\n radius: blast.radius,\n reached: tally.reached,\n killed: tally.killed,\n thrown: b === 0 ? thrown : 0,\n fallen: tally.fallen,\n ...(blast.chain === undefined ? {} : { chain: blast.chain }),\n })\n if (blast.chain !== undefined) ctx.stats.chainLinks++\n })\n }\n\n return { enqueue: (blast) => void queue.push(blast), resolve, pending: () => queue.length }\n}\n","import type { NavGrid } from '../grid'\nimport { SALT, unit } from '../identity'\nimport type { CrowdStore } from '../store'\nimport { BALANCE, STATE } from '../types'\nimport type { CrowdContactOptions } from './options'\n\n/** A fallen body weighs as three standing ones against a push (it is on the ground, spread out). */\nconst GROUND_MASS_FACTOR = 3\nconst PAIR_MARGIN = 0.15\nconst MAX_LEVEL_GAP = 0.15\nconst PAIRS_PER_AGENT = 12\nconst MIN_DISTANCE = 1e-6\n\nexport type ContactParams = Required<CrowdContactOptions> & { readonly h: number }\nexport type ContactResult = { contacts: number; maxOverlap: number; overlapSum: number }\n\nexport type ContactWorkspace = {\n readonly head: Int32Array\n readonly next: Int32Array\n readonly touched: Int32Array\n touchedCount: number\n readonly pairI: Int32Array\n readonly pairJ: Int32Array\n pairCount: number\n readonly deltaX: Float32Array\n readonly deltaZ: Float32Array\n readonly count: Uint8Array\n readonly pushBeforeX: Float32Array\n readonly pushBeforeZ: Float32Array\n /** |Δpush| the contact gave to each agent in the last step. */\n readonly shove: Float32Array\n /** 1 for the agents that take part in the contact this step (`takesPartInContact`), read once. */\n readonly takesPart: Uint8Array\n /** The radius and the effective mass of each agent this step, in doubles: the balance does not change inside the contact step. */\n readonly radiusOf: Float64Array\n readonly massOf: Float64Array\n /** Per pair, valid for the positions of the current iteration: the reach, the distance, and the normal (only meaningful when the distance is under the reach) from `j` to `i`. */\n readonly pairReach: Float64Array\n readonly pairDistance: Float64Array\n readonly pairNormalX: Float64Array\n readonly pairNormalZ: Float64Array\n}\n\n/** The arrays the contact and the pressure read and write: the step needs no more of the store, so it can run over a copy of them (a worker). */\nexport type PhysicsStore = Pick<CrowdStore, 'x' | 'z' | 'y' | 'vx' | 'vz' | 'pushX' | 'pushZ' | 'mass' | 'build' | 'balance' | 'state' | 'band' | 'identity' | 'aimX' | 'aimZ' | 'field' | 'fieldPrev' | 'type' | 'driveFactor'>\nexport type PhysicsGrid = Pick<NavGrid, 'size' | 'half' | 'occ' | 'cellOf'>\n\nexport type ContactView = { n: number; readonly store: PhysicsStore; readonly grid: PhysicsGrid; readonly workspace: ContactWorkspace }\n\nexport const createContactWorkspace = (capacity: number, grid: PhysicsGrid): ContactWorkspace => ({\n head: new Int32Array(Math.floor(grid.size) ** 2).fill(-1),\n next: new Int32Array(capacity),\n touched: new Int32Array(capacity),\n touchedCount: 0,\n pairI: new Int32Array(capacity * PAIRS_PER_AGENT),\n pairJ: new Int32Array(capacity * PAIRS_PER_AGENT),\n pairCount: 0,\n deltaX: new Float32Array(capacity),\n deltaZ: new Float32Array(capacity),\n count: new Uint8Array(capacity),\n pushBeforeX: new Float32Array(capacity),\n pushBeforeZ: new Float32Array(capacity),\n shove: new Float32Array(capacity),\n takesPart: new Uint8Array(capacity),\n radiusOf: new Float64Array(capacity),\n massOf: new Float64Array(capacity),\n pairReach: new Float64Array(capacity * PAIRS_PER_AGENT),\n pairDistance: new Float64Array(capacity * PAIRS_PER_AGENT),\n pairNormalX: new Float64Array(capacity * PAIRS_PER_AGENT),\n pairNormalZ: new Float64Array(capacity * PAIRS_PER_AGENT),\n})\n\nexport const createContactView = (store: PhysicsStore, grid: PhysicsGrid): ContactView => ({ n: 0, store, grid, workspace: createContactWorkspace(store.x.length, grid) })\n\n/** In the contact: near the viewer, not a pile step, not asleep, not in the air as a thrown body. */\nexport const takesPartInContact = (store: PhysicsStore, i: number): boolean => store.band[i] === 0 && store.balance[i] !== BALANCE.THROWN && store.state[i] !== STATE.PILE_STEP && store.state[i] !== STATE.DORMANT\n\nconst isOnGround = (store: PhysicsStore, i: number): boolean => store.balance[i] === BALANCE.FALLEN || store.balance[i] === BALANCE.GETTING_UP\n\nconst effectiveMass = (store: PhysicsStore, i: number): number => store.mass[i]! * (isOnGround(store, i) ? GROUND_MASS_FACTOR : 1)\n\nconst bodyRadius = (store: PhysicsStore, i: number, radius: number): number => radius * store.build[i]!\n\n/** What each agent is for this step: in the contact or not, its radius and its effective mass. */\nfunction readBodies(view: ContactView, radius: number): void {\n const { store, workspace } = view\n const { takesPart, radiusOf, massOf } = workspace\n for (let i = 0; i < view.n; i++) {\n const takes = takesPartInContact(store, i)\n takesPart[i] = takes ? 1 : 0\n radiusOf[i] = bodyRadius(store, i, radius)\n massOf[i] = effectiveMass(store, i)\n }\n}\n\n/** The agents of band R0 into the 1 m hash, clearing only the cells the last step used. */\nexport function rebuildHash(view: ContactView): void {\n const { store, grid, workspace } = view\n const side = Math.floor(grid.size)\n for (let k = 0; k < workspace.touchedCount; k++) workspace.head[workspace.touched[k]!] = -1\n workspace.touchedCount = 0\n for (let i = 0; i < view.n; i++) {\n if (!workspace.takesPart[i]) continue\n const hx = Math.floor(store.x[i]! + grid.half)\n const hz = Math.floor(store.z[i]! + grid.half)\n if (hx < 0 || hz < 0 || hx >= side || hz >= side) continue\n const cell = hz * side + hx\n if (workspace.head[cell] === -1) workspace.touched[workspace.touchedCount++] = cell\n workspace.next[i] = workspace.head[cell]!\n workspace.head[cell] = i\n }\n}\n\n/** The cells scanned from a cell for pairs: itself (the agents after the first) and four neighbours, so that every pair of cells is looked at once, from one side. */\nconst FORWARD_CELLS = [1, 0, -1, 1, 0, 1, 1, 1] as const\n\nfunction addPairsOfCell(view: ContactView, i: number, cell: number, sameCell: boolean): void {\n const { store, workspace } = view\n const { head, next, radiusOf, pairI, pairJ } = workspace\n const { x, z, y } = store\n const xi = x[i]!\n const zi = z[i]!\n const yi = y[i]!\n const reachBase = radiusOf[i]! + PAIR_MARGIN\n for (let j = head[cell]!; j !== -1; j = next[j]!) {\n if ((sameCell && j <= i) || Math.abs(yi - y[j]!) > MAX_LEVEL_GAP) continue\n const reach = reachBase + radiusOf[j]!\n const ex = x[j]! - xi\n const ez = z[j]! - zi\n if (ex * ex + ez * ez >= reach * reach || workspace.pairCount >= pairI.length) continue\n pairI[workspace.pairCount] = i\n pairJ[workspace.pairCount++] = j\n }\n}\n\n/** The pairs `(i, j)` near enough to touch before the step ends, in the order of the agent that found them. The solver is symmetric in the two of a pair, so which one comes first does not matter. */\nfunction collectPairs(view: ContactView): void {\n const { store, grid, workspace } = view\n const side = Math.floor(grid.size)\n workspace.pairCount = 0\n for (let i = 0; i < view.n; i++) {\n if (!workspace.takesPart[i]) continue\n const hx = Math.floor(store.x[i]! + grid.half)\n const hz = Math.floor(store.z[i]! + grid.half)\n if (hx < 0 || hz < 0 || hx >= side || hz >= side) continue\n addPairsOfCell(view, i, hz * side + hx, true)\n for (let k = 0; k < FORWARD_CELLS.length; k += 2) {\n const qx = hx + FORWARD_CELLS[k]!\n const qz = hz + FORWARD_CELLS[k + 1]!\n if (qx >= 0 && qz >= 0 && qx < side && qz < side) addPairsOfCell(view, i, qz * side + qx, false)\n }\n }\n}\n\n/** The distance, reach and normal (from `j` to `i`) of every pair at the current positions; a pair on the same spot gets a direction from the two identities. */\nfunction measurePairs(view: ContactView): void {\n const { store, workspace } = view\n const { pairI, pairJ, radiusOf, pairReach, pairDistance, pairNormalX, pairNormalZ } = workspace\n const { x, z, identity } = store\n for (let p = 0; p < workspace.pairCount; p++) {\n const i = pairI[p]!\n const j = pairJ[p]!\n const reach = radiusOf[i]! + radiusOf[j]!\n const ex = x[i]! - x[j]!\n const ez = z[i]! - z[j]!\n const distance = Math.sqrt(ex * ex + ez * ez)\n pairReach[p] = reach\n pairDistance[p] = distance\n if (distance >= reach) continue\n if (distance > MIN_DISTANCE) {\n pairNormalX[p] = ex / distance\n pairNormalZ[p] = ez / distance\n continue\n }\n const angle = 2 * Math.PI * unit(identity[i]! ^ identity[j]!, 0, SALT.SPIN)\n pairNormalX[p] = Math.cos(angle)\n pairNormalZ[p] = Math.sin(angle)\n }\n}\n\n/** One inelastic impulse along the normal: the closing speed of the two bodies goes to zero, never below (restitution 0). */\nfunction solveImpulse(store: PhysicsStore, workspace: ContactWorkspace, p: number): void {\n if (workspace.pairDistance[p]! >= workspace.pairReach[p]!) return\n const i = workspace.pairI[p]!\n const j = workspace.pairJ[p]!\n const nx = workspace.pairNormalX[p]!\n const nz = workspace.pairNormalZ[p]!\n const { vx, vz, pushX, pushZ } = store\n const closing = (vx[i]! + pushX[i]! - vx[j]! - pushX[j]!) * nx + (vz[i]! + pushZ[i]! - vz[j]! - pushZ[j]!) * nz\n if (closing >= 0) return\n const massI = workspace.massOf[i]!\n const massJ = workspace.massOf[j]!\n const impulse = (-closing * massI * massJ) / (massI + massJ)\n pushX[i]! += (impulse * nx) / massI\n pushZ[i]! += (impulse * nz) / massI\n pushX[j]! -= (impulse * nx) / massJ\n pushZ[j]! -= (impulse * nz) / massJ\n}\n\nfunction accumulateCorrection(workspace: ContactWorkspace, p: number, relaxation: number): void {\n const distance = workspace.pairDistance[p]!\n const reach = workspace.pairReach[p]!\n if (distance >= reach) return\n const i = workspace.pairI[p]!\n const j = workspace.pairJ[p]!\n const massI = workspace.massOf[i]!\n const massJ = workspace.massOf[j]!\n const move = relaxation * (reach - distance)\n const nx = workspace.pairNormalX[p]!\n const nz = workspace.pairNormalZ[p]!\n workspace.deltaX[i]! += move * nx * (massJ / (massI + massJ))\n workspace.deltaZ[i]! += move * nz * (massJ / (massI + massJ))\n workspace.deltaX[j]! -= move * nx * (massI / (massI + massJ))\n workspace.deltaZ[j]! -= move * nz * (massI / (massI + massJ))\n workspace.count[i]!++\n workspace.count[j]!++\n}\n\nconst isOpen = (grid: PhysicsGrid, x: number, z: number): boolean => {\n const cell = grid.cellOf(x, z)\n return cell >= 0 && (grid.occ[cell] === 0 || grid.occ[cell] === 3)\n}\n\n/** The mean of the corrections an agent got, capped, along each axis only if the grid lets it through. */\nfunction applyCorrection(view: ContactView, agent: number, params: ContactParams): void {\n const { store, grid, workspace } = view\n const count = workspace.count[agent]!\n if (!count) return\n workspace.count[agent] = 0\n const cap = params.maxCorrection\n const dx = Math.max(-cap, Math.min(cap, workspace.deltaX[agent]! / count))\n const dz = Math.max(-cap, Math.min(cap, workspace.deltaZ[agent]! / count))\n if (isOpen(grid, store.x[agent]! + dx, store.z[agent]!)) store.x[agent]! += dx\n if (isOpen(grid, store.x[agent]!, store.z[agent]! + dz)) store.z[agent]! += dz\n}\n\nfunction resetCorrection(workspace: ContactWorkspace, agent: number): void {\n workspace.deltaX[agent] = 0\n workspace.deltaZ[agent] = 0\n workspace.count[agent] = 0\n}\n\nfunction correctPositions(view: ContactView, params: ContactParams): void {\n const { workspace } = view\n for (let p = 0; p < workspace.pairCount; p++) {\n resetCorrection(workspace, workspace.pairI[p]!)\n resetCorrection(workspace, workspace.pairJ[p]!)\n }\n for (let p = 0; p < workspace.pairCount; p++) accumulateCorrection(workspace, p, params.relaxation)\n for (let p = 0; p < workspace.pairCount; p++) {\n applyCorrection(view, workspace.pairI[p]!, params)\n applyCorrection(view, workspace.pairJ[p]!, params)\n }\n}\n\nfunction measureOverlaps(view: ContactView, out: ContactResult): void {\n const { store, workspace } = view\n for (let p = 0; p < workspace.pairCount; p++) {\n const i = workspace.pairI[p]!\n const j = workspace.pairJ[p]!\n // two bodies lying on the ground are meant to lie on one another: that is the mound, not a failure of the contact\n if (isOnGround(store, i) && isOnGround(store, j)) continue\n const reach = workspace.radiusOf[i]! + workspace.radiusOf[j]!\n const overlap = reach - Math.hypot(store.x[i]! - store.x[j]!, store.z[i]! - store.z[j]!)\n if (overlap <= 0) continue\n out.contacts++\n out.overlapSum += overlap\n if (overlap > out.maxOverlap) out.maxOverlap = overlap\n }\n}\n\n/** The damping and the ceiling of the push, for every agent: a body out of band R0 keeps no push for long. */\nfunction dampPush(view: ContactView, params: ContactParams): void {\n const { store, workspace } = view\n const lambda = Math.exp(-params.friction * params.h)\n for (let i = 0; i < view.n; i++) {\n workspace.shove[i] = Math.hypot(store.pushX[i]! - workspace.pushBeforeX[i]!, store.pushZ[i]! - workspace.pushBeforeZ[i]!)\n store.pushX[i]! *= lambda\n store.pushZ[i]! *= lambda\n const speed = Math.hypot(store.pushX[i]!, store.pushZ[i]!)\n if (speed <= params.maxPush) continue\n store.pushX[i]! *= params.maxPush / speed\n store.pushZ[i]! *= params.maxPush / speed\n }\n}\n\n/** The 1 m hash of the agents in band R0 at their positions of now, for whoever looks at the neighbours (the landing of a thrown body) while the pairs are solved elsewhere. */\nexport function prepareHash(view: ContactView, radius: number): void {\n readBodies(view, radius)\n rebuildHash(view)\n}\n\n/**\n * One fixed step of contact in band R0 (`design-chain-horde.md` §4.3): `iterations` rounds of a symmetric Gauss-Seidel impulse (the pairs forward, then backward, so\n * a low index has no advantage) and a Jacobi position correction (the mean of what each agent was asked to move), then the damping of the push.\n * It resolves overlap and shock only: pressure is the field of `pressure.ts`. An inelastic impulse never adds energy, and the correction never touches the velocity.\n */\nexport function contactStep(view: ContactView, params: ContactParams, out: ContactResult): void {\n const { store, workspace } = view\n out.contacts = 0\n out.maxOverlap = 0\n out.overlapSum = 0\n for (let i = 0; i < view.n; i++) {\n workspace.pushBeforeX[i] = store.pushX[i]!\n workspace.pushBeforeZ[i] = store.pushZ[i]!\n }\n readBodies(view, params.radius)\n rebuildHash(view)\n collectPairs(view)\n for (let iteration = 0; iteration < params.iterations; iteration++) {\n measurePairs(view)\n for (let p = 0; p < workspace.pairCount; p++) solveImpulse(store, workspace, p)\n for (let p = workspace.pairCount - 1; p >= 0; p--) solveImpulse(store, workspace, p)\n correctPositions(view, params)\n }\n measureOverlaps(view, out)\n dampPush(view, params)\n}\n","import type { Doors } from '../doors'\nimport type { NavGrid } from '../grid'\nimport type { CrowdStore } from '../store'\nimport type { Corpse, CrowdDamageResult, CrowdEventMap, CrowdEventName, CrowdHit, CrowdTarget, CrowdType } from '../types'\nimport type { ResolvedPhysics } from './options'\n\n/** Counters of the physics, all zero with it off (`crowd.stats.physics`). */\nexport type PhysicsStats = {\n contacts: number\n maxOverlap: number\n meanOverlap: number\n /** Falls by cause: pressure, shove, trip, landing, collapse, blast. */\n falls: [pressure: number, shove: number, trip: number, landing: number, collapse: number, blast: number]\n /** Chain falls: shove, landing, collapse or trip within 240 steps of a blast, of an agent whose own direct shove from it was below `shoveStagger`. */\n fallsBeyondBlast: number\n fallen: number\n staggering: number\n climbers: number\n moundMax: number\n thrownActive: number\n thrownPeak: number\n blastChecks: number\n chainLinks: number\n /** The highest field of the step, N: the front of a column, or the funnel of a door. */\n fieldMax: number\n /** The pressure of the horde on the target, N (exponential mean). */\n playerPressure: number\n}\n\nexport const createPhysicsStats = (): PhysicsStats => ({\n contacts: 0,\n maxOverlap: 0,\n meanOverlap: 0,\n falls: [0, 0, 0, 0, 0, 0],\n fallsBeyondBlast: 0,\n fallen: 0,\n staggering: 0,\n climbers: 0,\n moundMax: 0,\n thrownActive: 0,\n thrownPeak: 0,\n blastChecks: 0,\n chainLinks: 0,\n fieldMax: 0,\n playerPressure: 0,\n})\n\n/** What the physics modules need from the crowd: the shared state and the few verbs they cannot do alone. */\nexport type PhysicsContext = {\n readonly store: CrowdStore\n readonly grid: NavGrid\n readonly doors: Doors\n readonly options: ResolvedPhysics\n readonly stats: PhysicsStats\n readonly gravity: number\n readonly attackEvery: number\n /** The fixed step being run, counted from 0: the counter of every `unit` draw. */\n step: number\n /** Seconds of the fixed step. */\n h: number\n /** Centre of the bands of this step. */\n centre: { x: number; z: number }\n /** Step of the last blast, -1000 before the first. */\n lastBlastStep: number\n typeOf(i: number): CrowdType\n target(): CrowdTarget | null\n emit<K extends CrowdEventName>(event: K, payload: CrowdEventMap[K]): void\n /** Damage with the draw handed in, so the shared stream stays where the game left it. A death is deferred to the end of the step. */\n damage(i: number, hit: CrowdHit, draw: () => number): CrowdDamageResult\n deferKill(i: number): void\n /** A pile step goes (falls, is thrown or dies): the steps behind it in the column come down. */\n releasePileStep(i: number): void\n corpseOf(i: number, seed: number): Corpse\n /** The bodies on the ground (the ledger), for the mound. */\n corpseList(): readonly { readonly data: Corpse }[]\n addCorpse(corpse: Corpse): void\n groundAt(cell: number, y: number): number\n /** Mound height in mm at a grid cell (0 with the mound off). */\n moundAt(cell: number): number\n /** Kilograms standing on the three cells of the lying body of agent `i` (0 with the mound off). */\n weightOn: (i: number) => number\n}\n","import { BALANCE, STATE } from '../types'\nimport type { PhysicsContext } from './context'\nimport type { CrowdDensityOptions } from './options'\n\n/** Fixed steps between two counts: the 5 Hz of the decision of band R1 at 60 Hz. */\nconst DECISION_STEPS = 12\nconst FAR_BAND = 1\n\nexport type Density = {\n /** Counts the walkers of band R1 per coarse cell (every 12th step) and writes the pressure that follows from it into their field. */\n step(): void\n /** What the speed of an agent of band `band` is multiplied by where the crowd is thick: `capacity / bodies` above `capacity` bodies in its cell, 1 elsewhere and in the other bands. */\n slowdown(i: number, band: number): number\n /** Bodies counted in the cell of an agent at the last count. */\n bodiesAround(i: number): number\n}\n\n/**\n * Band R1 without pairs (`design-chain-horde.md` §9): a coarse grid counts the standing walkers per cell; above `capacity` bodies the speed falls by `capacity / bodies` and the\n * field is `(bodies − capacity) · pressurePerBody` N, which the balance rule of band R0 reads as it reads any other field. Cost: one pass over the agents, no pairs.\n */\nexport function createDensity(ctx: PhysicsContext, options: Required<CrowdDensityOptions>): Density {\n const { store, grid } = ctx\n const side = Math.ceil(grid.size / options.cell)\n const bodies = new Uint16Array(side * side)\n const touched: number[] = []\n const half = grid.half\n\n const cellOf = (i: number): number => {\n const gx = Math.floor((store.x[i]! + half) / options.cell)\n const gz = Math.floor((store.z[i]! + half) / options.cell)\n return gx < 0 || gz < 0 || gx >= side || gz >= side ? -1 : gz * side + gx\n }\n\n const isWalking = (i: number): boolean => store.band[i] === FAR_BAND && (store.balance[i] === BALANCE.STANDING || store.balance[i] === BALANCE.STAGGERING) && store.state[i] !== STATE.PILE_STEP && store.state[i] !== STATE.DORMANT\n\n const count = (): void => {\n for (let k = 0; k < touched.length; k++) bodies[touched[k]!] = 0\n touched.length = 0\n for (let i = 0; i < store.n; i++) {\n if (!isWalking(i)) continue\n const cell = cellOf(i)\n if (cell < 0) continue\n if (bodies[cell] === 0) touched.push(cell)\n bodies[cell]!++\n }\n }\n\n const step = (): void => {\n if (ctx.step % DECISION_STEPS === 0) count()\n if (!touched.length) return\n for (let i = 0; i < store.n; i++) {\n if (!isWalking(i)) continue\n const cell = cellOf(i)\n const around = cell < 0 ? 0 : bodies[cell]!\n store.field[i] = around > options.capacity ? (around - options.capacity) * options.pressurePerBody : 0\n }\n }\n\n const bodiesAround = (i: number): number => {\n const cell = cellOf(i)\n return cell < 0 ? 0 : bodies[cell]!\n }\n\n const slowdown = (i: number, band: number): number => {\n if (band !== FAR_BAND) return 1\n const around = bodiesAround(i)\n return around > options.capacity ? options.capacity / around : 1\n }\n\n return { step, slowdown, bodiesAround }\n}\n","import { mix32 } from '../identity'\nimport { BALANCE, STATE } from '../types'\nimport type { PhysicsContext } from './context'\n\nconst CELLS_PER_BODY = 3\n/** Spacing of head, trunk and legs of a lying body along its yaw (m). */\nconst BODY_PART_SPACING = 0.5\nconst CLIMB_HEIGHT = 0.25\nconst ELEVATED = 0.05\nconst NEUTRAL_MASS = 70\nconst NEIGHBOURS = [-1, 1, -1, 0, -1, -1, 0, -1, 0, 1, 1, -1, 1, 0, 1, 1] as const\n\nexport type Mound = {\n /** Millimetres of lying bodies per navigation cell, recomputed every step. */\n readonly height: Uint16Array\n step(): void\n /** Kilograms of the agents that stand on the three cells of the lying body of agent `i`, as of the last step. */\n weightOn(i: number): number\n}\n\ntype Entries = { count: number; cell: Int32Array; thickness: Uint16Array; identity: Uint32Array; agent: Int32Array }\n\nconst BODY_IDENTITY_SCALE = 1e6\n\nexport function createMound(ctx: PhysicsContext, extraBodies: number, height: Uint16Array): Mound {\n const { store, grid } = ctx\n const options = ctx.options.mound!\n const balance = ctx.options.balance!\n const cellCount = grid.n * grid.n\n const touched = new Int32Array((store.capacity + extraBodies) * CELLS_PER_BODY * 2)\n let touchedCount = 0\n const entryCapacity = (store.capacity + extraBodies) * CELLS_PER_BODY\n const entries: Entries = { count: 0, cell: new Int32Array(entryCapacity), thickness: new Uint16Array(entryCapacity), identity: new Uint32Array(entryCapacity), agent: new Int32Array(entryCapacity) }\n const standMass = new Float32Array(cellCount)\n const standTouched = new Int32Array(store.capacity)\n let standTouchedCount = 0\n const maxMillimetres = Math.round(options.maxHeight * 1000)\n const cells: [number, number, number] = [0, 0, 0]\n\n const cellsOfBody = (x: number, z: number, yaw: number): void => {\n const sin = Math.sin(yaw)\n const cos = Math.cos(yaw)\n for (let part = 0; part < CELLS_PER_BODY; part++) {\n const offset = (part - 1) * BODY_PART_SPACING\n cells[part] = grid.cellOf(x + sin * offset, z + cos * offset)\n }\n }\n\n const addBody = (x: number, z: number, yaw: number, millimetres: number, identity: number, agent: number): void => {\n cellsOfBody(x, z, yaw)\n for (const cell of cells) {\n if (cell < 0) continue\n if (height[cell] === 0) touched[touchedCount++] = cell\n height[cell]! += millimetres\n const index = entries.count++\n entries.cell[index] = cell\n entries.thickness[index] = millimetres\n entries.identity[index] = identity\n entries.agent[index] = agent\n }\n }\n\n const sumBodies = (): void => {\n for (let k = 0; k < touchedCount; k++) height[touched[k]!] = 0\n touchedCount = 0\n entries.count = 0\n for (let i = 0; i < store.n; i++) {\n const state = store.balance[i]\n if (store.band[i] !== 0 || (state !== BALANCE.FALLEN && state !== BALANCE.GETTING_UP)) continue\n addBody(store.x[i]!, store.z[i]!, store.renderYaw[i]!, Math.round(options.bodyThickness * store.build[i]! * 1000), store.identity[i]!, i)\n }\n const reach = options.corpseRadius * options.corpseRadius\n for (const { data } of ctx.corpseList()) {\n if ((data.x - ctx.centre.x) ** 2 + (data.z - ctx.centre.z) ** 2 > reach) continue\n addBody(data.x, data.z, data.yaw, Math.round(options.corpseThickness * data.build * 1000), mix32(Math.floor(data.seed * BODY_IDENTITY_SCALE)), -1)\n }\n }\n\n const isOpenCell = (cell: number): boolean => grid.occ[cell] === 0 || grid.occ[cell] === 3\n\n /** The lowest of the 8 neighbours that is open and has room for `millimetres` more; -1 when none has. Ties go to the lowest cell number. */\n const lowestNeighbour = (cell: number, millimetres: number): number => {\n const cx = cell % grid.n\n const cz = Math.floor(cell / grid.n)\n let best = -1\n for (let k = 0; k < NEIGHBOURS.length; k += 2) {\n const nx = cx + NEIGHBOURS[k]!\n const nz = cz + NEIGHBOURS[k + 1]!\n if (nx < 0 || nz < 0 || nx >= grid.n || nz >= grid.n) continue\n const neighbour = nz * grid.n + nx\n if (!isOpenCell(neighbour) || height[neighbour]! + millimetres > maxMillimetres) continue\n if (best < 0 || height[neighbour]! < height[best]! || (height[neighbour] === height[best] && neighbour < best)) best = neighbour\n }\n return best\n }\n\n const slideEntry = (index: number): void => {\n const cell = entries.cell[index]!\n const target = lowestNeighbour(cell, entries.thickness[index]!)\n if (target < 0) return\n if (height[target] === 0) touched[touchedCount++] = target\n height[target]! += entries.thickness[index]!\n height[cell]! -= entries.thickness[index]!\n entries.cell[index] = target\n const agent = entries.agent[index]!\n if (agent < 0) return\n store.x[agent]! += ((target % grid.n) - (cell % grid.n)) * grid.cell\n store.z[agent]! += (Math.floor(target / grid.n) - Math.floor(cell / grid.n)) * grid.cell\n }\n\n /** The bodies of a cell above the ceiling, taken in the order of their identity, slide to a lower neighbour: the result depends on who is in the cell, not on who was added first. */\n const capHeights = (): void => {\n let over = false\n for (let k = 0; k < touchedCount && !over; k++) over = height[touched[k]!]! > maxMillimetres\n if (!over) return\n const order = Array.from({ length: entries.count }, (_, index) => index).sort((a, b) => entries.cell[a]! - entries.cell[b]! || entries.identity[a]! - entries.identity[b]! || a - b)\n let current = -1\n let running = 0\n for (const index of order) {\n const cell = entries.cell[index]!\n if (cell !== current) {\n current = cell\n running = 0\n }\n if (height[cell]! <= maxMillimetres) continue\n if (running + entries.thickness[index]! <= maxMillimetres) running += entries.thickness[index]!\n else slideEntry(index)\n }\n for (let k = 0; k < touchedCount; k++) if (height[touched[k]!]! > maxMillimetres) height[touched[k]!] = maxMillimetres\n }\n\n const seatFallen = (): void => {\n let top = 0\n for (let k = 0; k < touchedCount; k++) top = Math.max(top, height[touched[k]!]!)\n ctx.stats.moundMax = top / 1000\n for (let i = 0; i < store.n; i++) {\n const state = store.balance[i]\n if (store.band[i] !== 0 || (state !== BALANCE.FALLEN && state !== BALANCE.GETTING_UP)) continue\n const cell = grid.cellOf(store.x[i]!, store.z[i]!)\n if (cell >= 0) store.y[i] = Math.max(0, height[cell]! / 1000 - options.bodyThickness * store.build[i]!)\n }\n }\n\n const weighStanding = (): void => {\n for (let k = 0; k < standTouchedCount; k++) standMass[standTouched[k]!] = 0\n standTouchedCount = 0\n let climbers = 0\n for (let i = 0; i < store.n; i++) {\n const state = store.balance[i]\n if (store.band[i] !== 0 || store.state[i] === STATE.PILE_STEP || (state !== BALANCE.STANDING && state !== BALANCE.STAGGERING)) continue\n if (store.y[i]! >= CLIMB_HEIGHT) climbers++\n if (store.y[i]! <= ELEVATED) continue\n const cell = grid.cellOf(store.x[i]!, store.z[i]!)\n if (cell < 0) continue\n if (standMass[cell] === 0) standTouched[standTouchedCount++] = cell\n standMass[cell]! += store.mass[i]!\n }\n ctx.stats.climbers = climbers\n }\n\n /** Every lying body takes damage for the weight standing on its three cells (`trampleDps` per 70 kg); a death waits for the end of the step. */\n const trample = (): void => {\n if (!standTouchedCount) return\n for (let i = 0; i < store.n; i++) {\n const state = store.balance[i]\n if (store.band[i] !== 0 || (state !== BALANCE.FALLEN && state !== BALANCE.GETTING_UP)) continue\n const mass = weightOn(i)\n if (!mass) continue\n store.hp[i]! -= balance.trampleDps * (mass / NEUTRAL_MASS) * ctx.h\n if (store.hp[i]! <= 0) ctx.deferKill(i)\n }\n }\n\n const step = (): void => {\n sumBodies()\n capHeights()\n seatFallen()\n weighStanding()\n trample()\n }\n\n const weightOn = (i: number): number => {\n cellsOfBody(store.x[i]!, store.z[i]!, store.renderYaw[i]!)\n let mass = 0\n for (const cell of cells) if (cell >= 0) mass += standMass[cell]!\n return mass\n }\n\n return { height, step, weightOn }\n}\n","import type { PhysicsWorkerFactory } from './types'\n\n/** The browser worker that loads `physics-worker.js` from beside the bundle; `null` where there is no `Worker` or no `import.meta.url` (the iife build, node). */\nexport function createDefaultWorkerFactory(): PhysicsWorkerFactory | null {\n if (typeof Worker === 'undefined') return null\n let url: URL\n try {\n url = new URL('./physics-worker.js', import.meta.url)\n } catch {\n return null\n }\n return () => {\n const worker = new Worker(url, { type: 'module' })\n return {\n postMessage: (message, transfer) => worker.postMessage(message, transfer ?? []),\n onMessage: (listener) => worker.addEventListener('message', (event) => listener(event.data)),\n terminate: () => worker.terminate(),\n }\n }\n}\n","/**\n * The frame the physics step is handed to a worker in: one buffer holding every array the contact and the pressure read (the inputs), the deltas and fields they give back\n * (the outputs) and the grid occupancy. One layout for the shared buffer and for the transferred one, so the two paths run the same step over the same bytes.\n */\n\nconst F32_INPUTS = ['x', 'z', 'y', 'vx', 'vz', 'pushX', 'pushZ', 'mass', 'build', 'aimX', 'aimZ', 'driveFactor', 'fieldPrev'] as const\nconst U8_INPUTS = ['band', 'balance', 'state', 'type'] as const\nconst F32_OUTPUTS = ['deltaX', 'deltaZ', 'deltaPushX', 'deltaPushZ', 'deltaVx', 'deltaVz', 'field', 'shove', 'doorLoad'] as const\n\n/** Header slots (Float64): the step's inputs, then its results. */\nexport const HEADER = { n: 0, h: 1, playerOn: 2, playerX: 3, playerZ: 4, contacts: 5, maxOverlap: 6, overlapSum: 7, playerLoad: 8, drivers: 9, fieldMax: 10, size: 16 } as const\n\nexport type FrameShape = { readonly capacity: number; readonly cells: number }\n\nexport type PhysicsFrame = {\n readonly header: Float64Array\n readonly f32: { readonly [K in (typeof F32_INPUTS)[number] | (typeof F32_OUTPUTS)[number]]: Float32Array }\n readonly u8: { readonly [K in (typeof U8_INPUTS)[number]]: Uint8Array }\n readonly identity: Uint32Array\n readonly doorCell: Int32Array\n readonly occ: Uint8Array\n}\n\nconst align8 = (bytes: number): number => Math.ceil(bytes / 8) * 8\n\n/** Bytes of a frame; the arrays follow the header in the order of the lists above. */\nexport const frameBytes = ({ capacity, cells }: FrameShape): number => HEADER.size * 8 + (F32_INPUTS.length + F32_OUTPUTS.length + 2) * align8(capacity * 4) + U8_INPUTS.length * align8(capacity) + align8(cells)\n\nexport function createFrame(buffer: ArrayBufferLike, shape: FrameShape): PhysicsFrame {\n const { capacity, cells } = shape\n let offset = 0\n const take = <T>(make: (buffer: ArrayBufferLike, offset: number, length: number) => T, bytesPerElement: number, length: number): T => {\n const view = make(buffer, offset, length)\n offset += align8(length * bytesPerElement)\n return view\n }\n const f64 = (b: ArrayBufferLike, o: number, l: number): Float64Array => new Float64Array(b, o, l)\n const f32 = (b: ArrayBufferLike, o: number, l: number): Float32Array => new Float32Array(b, o, l)\n const u8 = (b: ArrayBufferLike, o: number, l: number): Uint8Array => new Uint8Array(b, o, l)\n const header = take(f64, 8, HEADER.size)\n const floats = {} as Record<string, Float32Array>\n for (const key of [...F32_INPUTS, ...F32_OUTPUTS]) floats[key] = take(f32, 4, capacity)\n const identity = take((b, o, l) => new Uint32Array(b, o, l), 4, capacity)\n const doorCell = take((b, o, l) => new Int32Array(b, o, l), 4, capacity)\n const bytes = {} as Record<string, Uint8Array>\n for (const key of U8_INPUTS) bytes[key] = take(u8, 1, capacity)\n const occ = take(u8, 1, cells)\n return { header, f32: floats as PhysicsFrame['f32'], u8: bytes as PhysicsFrame['u8'], identity, doorCell, occ }\n}\n\n/** The inputs of the step, by the name the store gives them. */\nexport const INPUT_KEYS = { f32: F32_INPUTS, u8: U8_INPUTS } as const\n","import type { NavGrid } from '../../grid'\nimport type { CrowdStore } from '../../store'\nimport type { ContactView } from '../contact'\nimport type { PhysicsStats } from '../context'\nimport { HEADER, INPUT_KEYS } from './frame'\nimport type { PhysicsFrame } from './frame'\nimport type { StepRunner } from './runner'\n\nexport type OffloadHost = {\n readonly store: CrowdStore\n readonly grid: NavGrid\n readonly stats: PhysicsStats\n readonly contact: ContactView\n /** The door at a grid cell took `load` N·s from the field (the door index is the host's to find). */\n addDoorLoad(cell: number, load: number): void\n}\n\nexport type OffloadStepResult = { playerLoad: number }\n\nexport type Offload = {\n /** Applies the result of the previous step, if it is back, then hands the state of this one to the runner. */\n step(h: number, player: { readonly x: number; readonly z: number } | undefined): OffloadStepResult\n /** The store moved agent `moved` into the place of `removed`: the result still in flight is read through it. */\n remap(removed: number, moved: number): void\n dispose(): void\n}\n\nconst isOpen = (grid: NavGrid, x: number, z: number): boolean => {\n const cell = grid.cellOf(x, z)\n return cell >= 0 && (grid.occ[cell] === 0 || grid.occ[cell] === 3)\n}\n\n/** The inputs of one step, copied from the store and the grid into the frame. */\nexport function writeSnapshot(frame: PhysicsFrame, source: { readonly store: CrowdStore; readonly grid: NavGrid }, h: number, player: { readonly x: number; readonly z: number } | undefined): void {\n const { store, grid } = source\n const n = store.n\n for (const key of INPUT_KEYS.f32) frame.f32[key].set(store[key].subarray(0, n))\n for (const key of INPUT_KEYS.u8) frame.u8[key].set(store[key].subarray(0, n))\n frame.identity.set(store.identity.subarray(0, n))\n frame.occ.set(grid.occ)\n frame.header[HEADER.n] = n\n frame.header[HEADER.h] = h\n frame.header[HEADER.playerOn] = player ? 1 : 0\n frame.header[HEADER.playerX] = player?.x ?? 0\n frame.header[HEADER.playerZ] = player?.z ?? 0\n}\n\n/**\n * The contact and the pressure one step behind: what the runner gives back was computed from the state at the end of the previous step, so it comes as deltas (position,\n * push, velocity) added to the state of now, and as fields that replace the old ones. With the same inputs per step it is the same result whichever thread computed it.\n */\nexport function createOffload(runner: StepRunner, host: OffloadHost, latency = 1): Offload {\n const { store, grid, stats, contact } = host\n type Pending = { count: number; origin: Int32Array | null; readonly originBuffer: Int32Array }\n const queue: Pending[] = []\n const spare: Pending[] = Array.from({ length: latency }, () => ({ count: 0, origin: null, originBuffer: new Int32Array(store.capacity) }))\n const result: OffloadStepResult = { playerLoad: 0 }\n\n const apply = (frame: PhysicsFrame, pending: Pending): void => {\n const { f32, header } = frame\n const { origin, count } = pending\n for (let agent = 0; agent < store.n; agent++) {\n const from = origin ? origin[agent]! : agent < count ? agent : -1\n if (from < 0) {\n contact.workspace.shove[agent] = 0\n continue\n }\n const dx = f32.deltaX[from]!\n const dz = f32.deltaZ[from]!\n if (dx !== 0 && isOpen(grid, store.x[agent]! + dx, store.z[agent]!)) store.x[agent]! += dx\n if (dz !== 0 && isOpen(grid, store.x[agent]!, store.z[agent]! + dz)) store.z[agent]! += dz\n store.pushX[agent]! += f32.deltaPushX[from]!\n store.pushZ[agent]! += f32.deltaPushZ[from]!\n store.vx[agent]! += f32.deltaVx[from]!\n store.vz[agent]! += f32.deltaVz[from]!\n store.field[agent] = f32.field[from]!\n store.fieldPrev[agent] = f32.field[from]!\n contact.workspace.shove[agent] = f32.shove[from]!\n const cell = frame.doorCell[from]!\n if (cell >= 0) host.addDoorLoad(cell, f32.doorLoad[from]!)\n }\n stats.contacts = header[HEADER.contacts]!\n stats.maxOverlap = header[HEADER.maxOverlap]!\n stats.meanOverlap = header[HEADER.contacts]! ? header[HEADER.overlapSum]! / header[HEADER.contacts]! : 0\n stats.fieldMax = header[HEADER.fieldMax]!\n result.playerLoad = header[HEADER.playerLoad]!\n }\n\n const step = (h: number, player: { readonly x: number; readonly z: number } | undefined): OffloadStepResult => {\n result.playerLoad = 0\n if (queue.length >= latency) {\n const frame = runner.settle()\n if (frame) {\n const pending = queue.shift()!\n apply(frame, pending)\n pending.origin = null\n spare.push(pending)\n }\n }\n const frame = runner.frame()\n if (frame && spare.length) {\n writeSnapshot(frame, { store, grid }, h, player)\n runner.submit()\n const pending = spare.shift()!\n pending.count = store.n\n queue.push(pending)\n }\n return result\n }\n\n const remap = (removed: number, moved: number): void => {\n queue.forEach((pending) => {\n if (!pending.origin) {\n pending.origin = pending.originBuffer.fill(-1)\n for (let agent = 0; agent < pending.count; agent++) pending.origin[agent] = agent\n }\n pending.origin[removed] = removed === moved ? -1 : pending.origin[moved]!\n if (removed !== moved) pending.origin[moved] = -1\n })\n }\n\n return { step, remap, dispose: () => runner.dispose() }\n}\n","import { BALANCE } from '../types'\nimport { takesPartInContact } from './contact'\nimport type { ContactView, PhysicsStore } from './contact'\nimport type { CrowdPressureOptions } from './options'\n\n/** Two bodies count as touching up to this far past the sum of their radii. */\nconst TOUCH_SLACK = 0.08\n/** How far past its own radius an agent feels a wall in front of it. */\nconst WALL_PROBE = 0.12\n/** The agents that stand in front of the target and pass the field on to it (a little past the attack range). */\nconst PLAYER_REACH = 1.3\nconst PLAYER_RADIUS_SQUARED = PLAYER_REACH * PLAYER_REACH\n\nexport type PressureParams = Required<CrowdPressureOptions> & { readonly h: number; readonly radius: number }\n\nexport type PressureWorkspace = {\n readonly weightSum: Float32Array\n /** 1 when something stands in front of the agent that it presses on (a wall, a body, even one pressing back). */\n readonly pressing: Uint8Array\n readonly wallKind: Uint8Array\n readonly wallDoor: Int32Array\n readonly playerWeight: Float32Array\n readonly cutExcess: Float32Array\n readonly cutX: Float32Array\n readonly cutZ: Float32Array\n readonly pairWeightForward: Float32Array\n readonly pairWeightBackward: Float32Array\n /** 1 for the agents that press on what is in front of them (`isPressing`), read once per step. */\n readonly canPress: Uint8Array\n}\n\nexport type PressureContext = {\n /** Newtons of will of each type, by type index. */\n readonly driveForceOfType: Float32Array\n /** The door index of a grid cell, -1 for none. */\n readonly doorAt: (cell: number) => number\n /** N·s each door got from the field this step, added to. */\n readonly doorLoad: Float32Array\n /** The agent the horde presses on (position only); undefined: nothing to press on. */\n readonly player?: { readonly x: number; readonly z: number } | undefined\n /** When given, the load a door gets from each agent is recorded here (by agent, with the door \"index\" `doorAt` answered) instead of added to `doorLoad`: the caller adds it, in agent order. */\n readonly doorOut?: { readonly cell: Int32Array; readonly load: Float32Array } | undefined\n}\n\nexport type PressureResult = { playerLoad: number; drivers: number }\n\nexport const createPressureWorkspace = (capacity: number, pairCapacity: number): PressureWorkspace => ({\n weightSum: new Float32Array(capacity),\n pressing: new Uint8Array(capacity),\n wallKind: new Uint8Array(capacity),\n wallDoor: new Int32Array(capacity),\n playerWeight: new Float32Array(capacity),\n cutExcess: new Float32Array(capacity),\n cutX: new Float32Array(capacity),\n cutZ: new Float32Array(capacity),\n pairWeightForward: new Float32Array(pairCapacity),\n pairWeightBackward: new Float32Array(pairCapacity),\n canPress: new Uint8Array(capacity),\n})\n\nconst isPressing = (store: PhysicsStore, i: number): boolean => takesPartInContact(store, i) && (store.balance[i] === BALANCE.STANDING || store.balance[i] === BALANCE.STAGGERING)\n\n/** `isPressing` of every agent into `canPress`: the state does not change inside the pressure step. */\nfunction readPressing(view: ContactView, workspace: PressureWorkspace): void {\n for (let i = 0; i < view.n; i++) workspace.canPress[i] = isPressing(view.store, i) ? 1 : 0\n}\n\nconst frontWeight = (alignment: number, cone: number): number => (alignment > cone ? (alignment - cone) / (1 - cone) : 0)\n\nconst hasAim = (store: PhysicsStore, i: number): boolean => store.aimX[i] !== 0 || store.aimZ[i] !== 0\n\n/** The will of `i` against its front neighbour `j`: the part of its speed along the contact that the neighbour does not have is cut. */\nfunction noteCut(store: PhysicsStore, workspace: PressureWorkspace, i: number, j: number, nx: number, nz: number): void {\n const excess = (store.vx[i]! - store.vx[j]!) * nx + (store.vz[i]! - store.vz[j]!) * nz\n if (excess <= workspace.cutExcess[i]!) return\n workspace.cutExcess[i] = excess\n workspace.cutX[i] = nx\n workspace.cutZ[i] = nz\n}\n\n/** Pass A: the weight of each front contact between agents, per direction, and the cut of the will against it. */\nfunction weighPairs(view: ContactView, workspace: PressureWorkspace, params: PressureParams): void {\n const { store } = view\n for (let p = 0; p < view.workspace.pairCount; p++) {\n workspace.pairWeightForward[p] = 0\n workspace.pairWeightBackward[p] = 0\n const i = view.workspace.pairI[p]!\n const j = view.workspace.pairJ[p]!\n if (!workspace.canPress[i] || !workspace.canPress[j]) continue\n const ex = store.x[j]! - store.x[i]!\n const ez = store.z[j]! - store.z[i]!\n const distance = Math.sqrt(ex * ex + ez * ez)\n if (distance < 1e-6 || distance > (store.build[i]! + store.build[j]!) * params.radius + TOUCH_SLACK) continue\n const nx = ex / distance\n const nz = ez / distance\n const forward = frontWeight(store.aimX[i]! * nx + store.aimZ[i]! * nz, params.frontCone)\n const backward = frontWeight(-(store.aimX[j]! * nx + store.aimZ[j]! * nz), params.frontCone)\n if (forward > 0) {\n workspace.pressing[i] = 1\n noteCut(store, workspace, i, j, nx, nz)\n }\n if (backward > 0) {\n workspace.pressing[j] = 1\n noteCut(store, workspace, j, i, -nx, -nz)\n }\n // two agents pressing on each other (head on) feel their own drive and no more: passing the field both ways would feed it back on itself without a limit\n if (forward > 0 && backward > 0) continue\n workspace.pairWeightForward[p] = forward\n workspace.pairWeightBackward[p] = backward\n workspace.weightSum[i]! += forward\n workspace.weightSum[j]! += backward\n }\n}\n\n/** Pass B: a blocked grid cell in the direction of the will is a wall of weight 1 (a closed door too, and the door takes the load). */\nfunction weighWalls(view: ContactView, workspace: PressureWorkspace, params: PressureParams, doorAt: PressureContext['doorAt']): void {\n const { store, grid } = view\n for (let i = 0; i < view.n; i++) {\n if (!workspace.canPress[i] || !hasAim(store, i)) continue\n const reach = store.build[i]! * params.radius + WALL_PROBE\n const cell = grid.cellOf(store.x[i]! + store.aimX[i]! * reach, store.z[i]! + store.aimZ[i]! * reach)\n if (cell >= 0 && (grid.occ[cell] === 0 || grid.occ[cell] === 3)) continue\n workspace.wallKind[i] = 1\n workspace.wallDoor[i] = cell >= 0 && grid.occ[cell] === 5 ? doorAt(cell) : -1\n workspace.pressing[i] = 1\n workspace.weightSum[i]! += 1\n }\n}\n\n/** The agents at the target pass the field on to it: one that wants to go there by its cone weight, one that stopped to attack with weight 1. */\nfunction weighPlayer(view: ContactView, workspace: PressureWorkspace, params: PressureParams, player: { readonly x: number; readonly z: number }): void {\n const { store } = view\n for (let i = 0; i < view.n; i++) {\n if (!workspace.canPress[i]) continue\n const ex = player.x - store.x[i]!\n const ez = player.z - store.z[i]!\n const squared = ex * ex + ez * ez\n if (squared > PLAYER_RADIUS_SQUARED || squared < 1e-6) continue\n const distance = Math.sqrt(squared)\n const weight = hasAim(store, i) ? frontWeight((store.aimX[i]! * ex + store.aimZ[i]! * ez) / distance, params.frontCone) : 1\n workspace.playerWeight[i] = weight\n if (weight > 0) workspace.pressing[i] = 1\n workspace.weightSum[i]! += weight\n }\n}\n\nfunction clearWorkspace(view: ContactView, workspace: PressureWorkspace): void {\n const { store } = view\n for (let i = 0; i < view.n; i++) {\n workspace.weightSum[i] = 0\n workspace.pressing[i] = 0\n workspace.wallKind[i] = 0\n workspace.playerWeight[i] = 0\n workspace.cutExcess[i] = 0\n store.field[i] = 0\n }\n}\n\n/** What agent `i` adds to the field by itself: its drive, when it wants to go and has something in front. */\nfunction ownDrive(store: PhysicsStore, context: PressureContext, workspace: PressureWorkspace, i: number): number {\n if (!hasAim(store, i) || !workspace.pressing[i]) return 0\n return context.driveForceOfType[store.type[i]!]! * store.driveFactor[i]!\n}\n\n/** Pass C: each agent's field is its own drive plus what the agents behind it passed on, read from the previous step. */\nfunction spreadField(view: ContactView, workspace: PressureWorkspace, params: PressureParams, context: PressureContext, out: PressureResult): void {\n const { store } = view\n for (let i = 0; i < view.n; i++) {\n if (!workspace.canPress[i]) continue\n store.field[i] = ownDrive(store, context, workspace, i)\n if (store.field[i]! > 0) out.drivers++\n }\n for (let p = 0; p < view.workspace.pairCount; p++) {\n const i = view.workspace.pairI[p]!\n const j = view.workspace.pairJ[p]!\n const forward = workspace.pairWeightForward[p]!\n const backward = workspace.pairWeightBackward[p]!\n if (forward > 0) store.field[j]! += (params.transmission * store.fieldPrev[i]! * forward * forward) / workspace.weightSum[i]!\n if (backward > 0) store.field[i]! += (params.transmission * store.fieldPrev[j]! * backward * backward) / workspace.weightSum[j]!\n }\n for (let i = 0; i < view.n; i++) {\n if (!workspace.canPress[i]) continue\n const passed = (params.transmission * store.fieldPrev[i]!) / (workspace.weightSum[i]! || 1)\n if (workspace.wallKind[i] && workspace.wallDoor[i]! >= 0) {\n if (context.doorOut) {\n context.doorOut.cell[i] = workspace.wallDoor[i]!\n context.doorOut.load[i] = passed * params.h\n } else context.doorLoad[workspace.wallDoor[i]!]! += passed * params.h\n }\n if (workspace.playerWeight[i]! > 0) out.playerLoad += passed * workspace.playerWeight[i]! * workspace.playerWeight[i]!\n if (store.field[i]! > params.cap) store.field[i] = params.cap\n }\n}\n\nfunction applyCuts(view: ContactView, workspace: PressureWorkspace): void {\n const { store } = view\n for (let i = 0; i < view.n; i++) {\n const excess = workspace.cutExcess[i]!\n if (excess <= 0) continue\n store.vx[i]! -= excess * workspace.cutX[i]!\n store.vz[i]! -= excess * workspace.cutZ[i]!\n }\n}\n\n/**\n * One fixed step of the pressure field (`design-chain-horde.md` §4.5): the field moves one hop per step, each agent reading only the previous step of the others, so the\n * result does not depend on the order of the indices. A column of N pushing agents gives its front `D·(1 − τᴺ)/(1 − τ)`; a hexagonal block against a flat wall loses 29% per hop.\n */\nexport function pressureStep(view: ContactView, workspace: PressureWorkspace, params: PressureParams, context: PressureContext, out: PressureResult): void {\n out.playerLoad = 0\n out.drivers = 0\n clearWorkspace(view, workspace)\n readPressing(view, workspace)\n weighPairs(view, workspace, params)\n weighWalls(view, workspace, params, context.doorAt)\n if (context.player) weighPlayer(view, workspace, params, context.player)\n spreadField(view, workspace, params, context, out)\n applyCuts(view, workspace)\n for (let i = 0; i < view.n; i++) view.store.fieldPrev[i] = view.store.field[i]!\n}\n\n","import { createCellOf } from '../../grid'\nimport { contactStep, createContactWorkspace } from '../contact'\nimport type { ContactParams, ContactResult, ContactView, PhysicsGrid, PhysicsStore } from '../contact'\nimport type { CrowdContactOptions, CrowdPressureOptions } from '../options'\nimport { createPressureWorkspace, pressureStep } from '../pressure'\nimport type { PressureResult, PressureWorkspace } from '../pressure'\nimport { HEADER } from './frame'\nimport type { PhysicsFrame } from './frame'\n\nconst PAIRS_PER_AGENT = 12\n\n/** What the kernel needs once, to be built (it travels to the worker as a message). */\nexport type KernelSetup = {\n readonly capacity: number\n readonly gridSize: number\n readonly gridCell: number\n readonly contact: Required<CrowdContactOptions>\n readonly pressure: Required<CrowdPressureOptions> | null\n readonly driveForceOfType: readonly number[]\n}\n\nexport type PhysicsKernel = {\n /** The contact and the pressure over the arrays of the frame, the deltas and the fields written back into it. The same call runs on the main thread and in a worker. */\n run(frame: PhysicsFrame): void\n}\n\nconst STORE_KEYS = ['x', 'z', 'y', 'vx', 'vz', 'pushX', 'pushZ', 'mass', 'build', 'aimX', 'aimZ', 'driveFactor', 'fieldPrev'] as const\n\n/** The step as a pure function of one frame: no read of anything outside it, so a worker that gets the same bytes writes the same bytes. */\nexport function createKernel(setup: KernelSetup): PhysicsKernel {\n const { capacity } = setup\n const side = Math.floor(setup.gridSize / setup.gridCell)\n const half = setup.gridSize / 2\n const before = { x: new Float32Array(capacity), z: new Float32Array(capacity), pushX: new Float32Array(capacity), pushZ: new Float32Array(capacity), vx: new Float32Array(capacity), vz: new Float32Array(capacity) }\n const driveForceOfType = Float32Array.from(setup.driveForceOfType)\n const contactResult: ContactResult = { contacts: 0, maxOverlap: 0, overlapSum: 0 }\n const pressureResult: PressureResult = { playerLoad: 0, drivers: 0 }\n const workspace: PressureWorkspace | null = setup.pressure ? createPressureWorkspace(capacity, capacity * PAIRS_PER_AGENT) : null\n const cellOf = createCellOf(half, setup.gridCell, side)\n const doorLoadUnused = new Float32Array(0)\n // one store and one grid for the life of the kernel (the contact workspace is big): a new frame only points their arrays at its own bytes\n const store: Record<string, unknown> = {}\n const grid: { size: number; half: number; occ: Uint8Array<ArrayBufferLike>; cellOf: typeof cellOf } = { size: setup.gridSize, half, occ: new Uint8Array(0), cellOf }\n const view: ContactView = { n: 0, store: store as PhysicsStore, grid: grid as unknown as PhysicsGrid, workspace: createContactWorkspace(capacity, grid as unknown as PhysicsGrid) }\n\n const bind = (frame: PhysicsFrame): ContactView => {\n Object.assign(store, frame.u8, { identity: frame.identity, field: frame.f32.field })\n for (const key of STORE_KEYS) store[key] = frame.f32[key]\n grid.occ = frame.occ\n return view\n }\n\n const run = (frame: PhysicsFrame): void => {\n const { header } = frame\n const n = header[HEADER.n]!\n const contact = bind(frame)\n contact.n = n\n for (let i = 0; i < n; i++) {\n before.x[i] = frame.f32.x[i]!\n before.z[i] = frame.f32.z[i]!\n before.pushX[i] = frame.f32.pushX[i]!\n before.pushZ[i] = frame.f32.pushZ[i]!\n before.vx[i] = frame.f32.vx[i]!\n before.vz[i] = frame.f32.vz[i]!\n }\n const h = header[HEADER.h]!\n const params: ContactParams = { ...setup.contact, h }\n contactStep(contact, params, contactResult)\n header[HEADER.contacts] = contactResult.contacts\n header[HEADER.maxOverlap] = contactResult.maxOverlap\n header[HEADER.overlapSum] = contactResult.overlapSum\n frame.doorCell.fill(-1, 0, n)\n frame.f32.doorLoad.fill(0, 0, n)\n if (workspace && setup.pressure) {\n const player = header[HEADER.playerOn] ? { x: header[HEADER.playerX]!, z: header[HEADER.playerZ]! } : undefined\n const doorOut = { cell: frame.doorCell, load: frame.f32.doorLoad }\n pressureStep(contact, workspace, { ...setup.pressure, h, radius: setup.contact.radius }, { driveForceOfType, doorAt: (cell) => cell, doorLoad: doorLoadUnused, player, doorOut }, pressureResult)\n let top = 0\n for (let i = 0; i < n; i++) top = Math.max(top, frame.f32.field[i]!)\n header[HEADER.fieldMax] = top\n } else header[HEADER.fieldMax] = 0\n header[HEADER.playerLoad] = pressureResult.playerLoad\n header[HEADER.drivers] = pressureResult.drivers\n const { f32 } = frame\n for (let i = 0; i < n; i++) {\n f32.deltaX[i] = f32.x[i]! - before.x[i]!\n f32.deltaZ[i] = f32.z[i]! - before.z[i]!\n f32.deltaPushX[i] = f32.pushX[i]! - before.pushX[i]!\n f32.deltaPushZ[i] = f32.pushZ[i]! - before.pushZ[i]!\n f32.deltaVx[i] = f32.vx[i]! - before.vx[i]!\n f32.deltaVz[i] = f32.vz[i]! - before.vz[i]!\n f32.shove[i] = contact.workspace.shove[i]!\n }\n }\n\n return { run }\n}\n","import { createFrame } from './frame'\nimport type { FrameShape } from './frame'\nimport { createKernel } from './kernel'\nimport type { KernelSetup } from './kernel'\n\n/** The slot of the control word the two threads share. */\nexport const CONTROL = { IDLE: 0, SUBMITTED: 1, DONE: 2, STOP: 3 } as const\n\nexport type InitMessage = { readonly type: 'init'; readonly setup: KernelSetup; readonly shape: FrameShape; readonly frames?: readonly SharedArrayBuffer[]; readonly control?: SharedArrayBuffer }\nexport type FrameMessage = { readonly type: 'frame'; readonly buffer: ArrayBuffer }\nexport type DoneMessage = { readonly type: 'done'; readonly buffer: ArrayBuffer }\n\n/** What a worker thread has to offer: browsers and node give it different shapes, so the host adapts them. */\nexport type WorkerPort = {\n postMessage(message: unknown, transfer?: ArrayBuffer[]): void\n onMessage(listener: (data: unknown) => void): void\n}\n\n/**\n * The worker side: builds the kernel on `init`, then either loops on the shared control word (shared transport) or answers each transferred frame.\n * A host that loads the worker its own way (a bundler, node `worker_threads`) calls this with its port.\n */\nexport function attachPhysicsWorker(port: WorkerPort): void {\n let kernel: ReturnType<typeof createKernel> | null = null\n let shape: FrameShape | null = null\n port.onMessage((data) => {\n const message = data as InitMessage | FrameMessage\n if (message.type === 'init') {\n kernel = createKernel(message.setup)\n shape = message.shape\n if (message.frames && message.control) runShared(kernel, message.frames.map((buffer) => createFrame(buffer, message.shape)), new Int32Array(message.control))\n return\n }\n if (!kernel || !shape) return\n const frame = createFrame(message.buffer, shape)\n kernel.run(frame)\n port.postMessage({ type: 'done', buffer: message.buffer } satisfies DoneMessage, [message.buffer])\n })\n}\n\n/** The frames in order, each one when the main thread says it is submitted: the main thread takes them back in the same order. */\nfunction runShared(kernel: ReturnType<typeof createKernel>, frames: readonly ReturnType<typeof createFrame>[], control: Int32Array): void {\n for (let turn = 0; ; turn++) {\n const slot = turn % frames.length\n for (;;) {\n const state = Atomics.load(control, slot)\n if (state === CONTROL.STOP) return\n if (state === CONTROL.SUBMITTED) break\n Atomics.wait(control, slot, state)\n }\n kernel.run(frames[slot]!)\n Atomics.store(control, slot, CONTROL.DONE)\n Atomics.notify(control, slot)\n }\n}\n","import { createFrame, frameBytes } from './frame'\nimport type { FrameShape, PhysicsFrame } from './frame'\nimport { createKernel } from './kernel'\nimport type { KernelSetup } from './kernel'\nimport type { OffloadTransport, PhysicsWorkerFactory } from './types'\nimport { CONTROL } from './worker-host'\nimport type { DoneMessage, FrameMessage, InitMessage } from './worker-host'\n\n/** A ring of `slots` frames: the main thread writes the next step into a free one, submits it, and takes the oldest submitted one back when it is done. */\nexport type StepRunner = {\n readonly transport: OffloadTransport\n /** The frame to write the inputs of the next step into; `null` when every frame is out (all in flight, or with the worker in the transfer transport). */\n frame(): PhysicsFrame | null\n /** Sends the frame `frame()` gave. */\n submit(): void\n /** Frames submitted and not yet taken back. */\n readonly inFlight: number\n /**\n * The oldest submitted frame with its outputs, and it is free again for `frame()` once they are read; `null` when it is not back yet. The shared and inline transports wait for\n * it (the worker computes in step with the main thread, so the answer never depends on timing); the transfer transport cannot wait for an event, so it answers when it can.\n */\n settle(): PhysicsFrame | null\n dispose(): void\n}\n\nconst WAIT_LIMIT_MS = 5000\n\nconst canBlock = (() => {\n try {\n Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 1, 0)\n return true\n } catch {\n return false\n }\n})()\n\nexport const isSharedAvailable = (): boolean => typeof SharedArrayBuffer !== 'undefined' && (globalThis as { crossOriginIsolated?: boolean }).crossOriginIsolated !== false\n\n/** The same protocol and the same kernel on the calling thread: the result is computed at `submit` and read at `settle`. */\nexport function createInlineRunner(setup: KernelSetup, shape: FrameShape, slots = 1): StepRunner {\n const kernel = createKernel(setup)\n const frames = Array.from({ length: slots }, () => createFrame(new ArrayBuffer(frameBytes(shape)), shape))\n let submitted = 0\n let settled = 0\n const runner = {\n transport: 'inline' as const,\n get inFlight() {\n return submitted - settled\n },\n frame: () => (submitted - settled < slots ? frames[submitted % slots]! : null),\n submit: () => kernel.run(frames[submitted++ % slots]!),\n settle: () => (submitted > settled ? frames[settled++ % slots]! : null),\n dispose: () => undefined,\n }\n return runner\n}\n\nexport function createSharedRunner(setup: KernelSetup, shape: FrameShape, factory: PhysicsWorkerFactory, slots = 1): StepRunner {\n const buffers = Array.from({ length: slots }, () => new SharedArrayBuffer(frameBytes(shape)))\n const control = new Int32Array(new SharedArrayBuffer(4 * slots))\n const frames = buffers.map((buffer) => createFrame(buffer, shape))\n const worker = factory()\n worker.postMessage({ type: 'init', setup, shape, frames: buffers, control: control.buffer as SharedArrayBuffer } satisfies InitMessage)\n let submitted = 0\n let settled = 0\n return {\n transport: 'shared',\n get inFlight() {\n return submitted - settled\n },\n frame: () => (submitted - settled < slots ? frames[submitted % slots]! : null),\n submit: () => {\n const slot = submitted++ % slots\n Atomics.store(control, slot, CONTROL.SUBMITTED)\n Atomics.notify(control, slot)\n },\n settle: () => {\n if (submitted === settled) return null\n const slot = settled++ % slots\n const started = Date.now()\n while (Atomics.load(control, slot) !== CONTROL.DONE) {\n if (Date.now() - started > WAIT_LIMIT_MS) throw new Error('crowd physics worker did not answer')\n if (canBlock) Atomics.wait(control, slot, CONTROL.SUBMITTED, 50)\n }\n Atomics.store(control, slot, CONTROL.IDLE)\n return frames[slot]!\n },\n dispose: () => {\n for (let slot = 0; slot < slots; slot++) {\n Atomics.store(control, slot, CONTROL.STOP)\n Atomics.notify(control, slot)\n }\n worker.terminate()\n },\n }\n}\n\nexport function createTransferRunner(setup: KernelSetup, shape: FrameShape, factory: PhysicsWorkerFactory, slots = 1): StepRunner {\n const worker = factory()\n const free: ArrayBuffer[] = Array.from({ length: slots }, () => new ArrayBuffer(frameBytes(shape)))\n const done: ArrayBuffer[] = []\n const views = new WeakMap<ArrayBuffer, PhysicsFrame>()\n const viewOf = (buffer: ArrayBuffer): PhysicsFrame => {\n let view = views.get(buffer)\n if (!view) views.set(buffer, (view = createFrame(buffer, shape)))\n return view\n }\n let inFlight = 0\n worker.postMessage({ type: 'init', setup, shape } satisfies InitMessage)\n worker.onMessage((data) => {\n const message = data as DoneMessage\n if (message.type === 'done') done.push(message.buffer)\n })\n return {\n transport: 'transfer',\n get inFlight() {\n return inFlight\n },\n frame: () => (free.length ? viewOf(free[0]!) : null),\n submit: () => {\n const buffer = free.shift()\n if (!buffer) return\n worker.postMessage({ type: 'frame', buffer } satisfies FrameMessage, [buffer])\n inFlight++\n },\n settle: () => {\n const buffer = done.shift()\n if (!buffer) return null\n inFlight--\n free.push(buffer)\n return viewOf(buffer)\n },\n dispose: () => worker.terminate(),\n }\n}\n","import type { PhysicsContext } from './context'\n\nconst KNOCK_SECONDS = 1.5\nconst KNOCK_COOLDOWN = 2\nconst CRUSH_BLOW_EVERY = 0.25\n\nexport type PlayerPressure = {\n /** `load` is the N the horde passed on to the target in this step. */\n step(load: number): void\n}\n\n/**\n * The pressure of the horde on the target (D13.2): the exponential mean of what the agents pass on to it; above `knockThreshold` it knocks the target down, and the excess\n * crushes at `crushDamagePerSecond` through the same `CrowdTarget` verbs the agents already use. Off, nothing is called.\n */\nexport function createPlayerPressure(ctx: PhysicsContext): PlayerPressure {\n const options = ctx.options.playerPressure\n const tau = ctx.options.balance?.pressureTau ?? 0.3\n let knockCooldown = 0\n let crushed = 0\n let crushClock = 0\n\n const crush = (excess: number): void => {\n crushed += options.crushDamagePerSecond * excess * ctx.h\n crushClock += ctx.h\n if (crushClock < CRUSH_BLOW_EVERY) return\n crushClock = 0\n if (crushed <= 0) return\n ctx.target()?.hurt(crushed)\n crushed = 0\n }\n\n const step = (load: number): void => {\n ctx.stats.playerPressure += (load - ctx.stats.playerPressure) * (1 - Math.exp(-ctx.h / tau))\n if (!options.enabled) return\n const target = ctx.target()\n knockCooldown = Math.max(0, knockCooldown - ctx.h)\n if (!target || !target.state.alive || ctx.stats.playerPressure <= options.knockThreshold) {\n crushed = 0\n return\n }\n if (knockCooldown === 0) {\n target.knock(KNOCK_SECONDS)\n knockCooldown = KNOCK_COOLDOWN\n }\n crush(ctx.stats.playerPressure / options.knockThreshold - 1)\n }\n\n return { step }\n}\n","import { SALT, unit } from '../identity'\nimport { BALANCE, FALL_CAUSE, STATE } from '../types'\nimport type { Corpse } from '../types'\nimport { takesPartInContact } from './contact'\nimport type { ContactView } from './contact'\nimport type { Balance } from './balance'\nimport type { PhysicsContext } from './context'\nimport type { CrowdThrownOptions } from './options'\n\nexport const FREE_SLOT = -2\nexport const CARRIED_BODY = -1\nconst MAX_SPIN = 8\n/** Past this distance per step the flight is swept cell by cell instead of tested at the new point. */\nconst SWEEP_DISTANCE = 0.25\nconst LANDING_DAMAGE_SPEED = 6\nconst CENTRE_HEIGHT = 1\nconst MAX_POOL = 127\n\n/** The thrown bodies, simulated here and only read by the renderer (`x`, `y`, `z` are the centre of the body, `prev*` the position of the step before). */\nexport type ThrownPool = {\n readonly size: number\n /** An agent index, `CARRIED_BODY` for a dead body carried until it lands, `FREE_SLOT` for an empty slot. */\n readonly agent: Int32Array\n readonly identity: Uint32Array\n readonly corpse: (Corpse | null)[]\n readonly x: Float32Array\n readonly y: Float32Array\n readonly z: Float32Array\n readonly prevX: Float32Array\n readonly prevY: Float32Array\n readonly prevZ: Float32Array\n readonly vx: Float32Array\n readonly vy: Float32Array\n readonly vz: Float32Array\n readonly pitch: Float32Array\n readonly roll: Float32Array\n readonly spinPitch: Float32Array\n readonly spinRoll: Float32Array\n active(): number\n}\n\nexport type ThrownCandidate = { readonly index: number; readonly speed: number; readonly vx: number; readonly vy: number; readonly vz: number; readonly distance: number }\n\nexport type Thrown = {\n readonly pool: ThrownPool\n /** Gives the slots to the candidates by the priority of the options; returns how many flew. */\n assign(candidates: ThrownCandidate[]): number\n step(contact: ContactView, balance: Balance): void\n /** The store removed agent `removed` and moved agent `moved` into its place (the same index when it was the last). */\n remap(removed: number, moved: number): void\n}\n\nexport const createThrownPool = (size: number): ThrownPool => {\n const f32 = (): Float32Array => new Float32Array(size)\n const pool: ThrownPool = {\n size,\n agent: new Int32Array(size).fill(FREE_SLOT),\n identity: new Uint32Array(size),\n corpse: Array.from({ length: size }, () => null),\n x: f32(),\n y: f32(),\n z: f32(),\n prevX: f32(),\n prevY: f32(),\n prevZ: f32(),\n vx: f32(),\n vy: f32(),\n vz: f32(),\n pitch: f32(),\n roll: f32(),\n spinPitch: f32(),\n spinRoll: f32(),\n active: () => pool.agent.reduce((count, agent) => count + (agent === FREE_SLOT ? 0 : 1), 0),\n }\n return pool\n}\n\nconst byFastest = (a: ThrownCandidate, b: ThrownCandidate, identityOf: (i: number) => number): number => b.speed - a.speed || identityOf(a.index) - identityOf(b.index)\nconst byNearest = (a: ThrownCandidate, b: ThrownCandidate, identityOf: (i: number) => number): number => a.distance - b.distance || identityOf(a.index) - identityOf(b.index)\n\n/** Who gets the free slots (`mixed`: `mixedRatio` of them to the fastest, the rest to the nearest of what is left). */\nexport function chooseCandidates(candidates: ThrownCandidate[], free: number, options: Required<CrowdThrownOptions>, identityOf: (i: number) => number): ThrownCandidate[] {\n if (candidates.length <= free) return candidates\n if (options.priority === 'fastest') return candidates.sort((a, b) => byFastest(a, b, identityOf)).slice(0, free)\n if (options.priority === 'nearest') return candidates.sort((a, b) => byNearest(a, b, identityOf)).slice(0, free)\n const fast = Math.round(free * options.mixedRatio)\n const sorted = candidates.sort((a, b) => byFastest(a, b, identityOf))\n const rest = sorted.slice(fast).sort((a, b) => byNearest(a, b, identityOf))\n return [...sorted.slice(0, fast), ...rest.slice(0, free - fast)]\n}\n\nexport function createThrown(ctx: PhysicsContext): Thrown {\n const { store, grid } = ctx\n const options = ctx.options.thrown!\n const size = Math.min(MAX_POOL, options.pool)\n const pool = createThrownPool(size)\n const identityOf = (i: number): number => store.identity[i]!\n\n const assign = (candidates: ThrownCandidate[]): number => {\n let free = 0\n for (let slot = 0; slot < size; slot++) if (pool.agent[slot] === FREE_SLOT) free++\n const chosen = chooseCandidates(candidates, free, options, identityOf)\n let slot = 0\n for (const candidate of chosen) {\n while (pool.agent[slot] !== FREE_SLOT) slot++\n const i = candidate.index\n pool.agent[slot] = i\n pool.identity[slot] = store.identity[i]!\n pool.corpse[slot] = null\n pool.x[slot] = pool.prevX[slot] = store.x[i]!\n pool.y[slot] = pool.prevY[slot] = store.y[i]! + CENTRE_HEIGHT * store.height[i]!\n pool.z[slot] = pool.prevZ[slot] = store.z[i]!\n pool.vx[slot] = candidate.vx\n pool.vy[slot] = candidate.vy\n pool.vz[slot] = candidate.vz\n pool.pitch[slot] = pool.roll[slot] = 0\n pool.spinPitch[slot] = (unit(store.identity[i]!, ctx.step, SALT.SPIN) * 2 - 1) * MAX_SPIN\n pool.spinRoll[slot] = (unit(store.identity[i]!, ctx.step, SALT.SPIN + 1) * 2 - 1) * MAX_SPIN\n if (store.state[i] === STATE.PILE_STEP) ctx.releasePileStep(i)\n store.balance[i] = BALANCE.THROWN\n store.thrown[i] = slot\n store.vx[i] = store.vz[i] = store.pushX[i] = store.pushZ[i] = 0\n store.aimX[i] = store.aimZ[i] = 0\n }\n return chosen.length\n }\n\n const remap = (removed: number, moved: number): void => {\n for (let slot = 0; slot < size; slot++) {\n const agent = pool.agent[slot]!\n if (agent === removed) {\n pool.corpse[slot] ??= ctx.corpseOf(removed, unit(pool.identity[slot]!, ctx.step, SALT.CORPSE) * 10)\n pool.agent[slot] = CARRIED_BODY\n } else if (agent === moved && moved !== removed) pool.agent[slot] = removed\n }\n }\n\n /** The first solid cell on the horizontal path of one step, by the grid traversal of Amanatides and Woo; undefined when the path is clear. */\n const sweepWall = (x0: number, z0: number, x1: number, z1: number, y: number): { x: number; z: number } | undefined => {\n const cell = grid.cell\n let gx = Math.floor((x0 + grid.half) / cell)\n let gz = Math.floor((z0 + grid.half) / cell)\n const dx = x1 - x0\n const dz = z1 - z0\n const stepX = dx > 0 ? 1 : -1\n const stepZ = dz > 0 ? 1 : -1\n const deltaX = dx === 0 ? Infinity : Math.abs(cell / dx)\n const deltaZ = dz === 0 ? Infinity : Math.abs(cell / dz)\n let nextX = dx === 0 ? Infinity : ((dx > 0 ? gx + 1 : gx) * cell - grid.half - x0) / dx\n let nextZ = dz === 0 ? Infinity : ((dz > 0 ? gz + 1 : gz) * cell - grid.half - z0) / dz\n let t = 0\n while (Math.min(nextX, nextZ) <= 1) {\n if (nextX < nextZ) {\n t = nextX\n nextX += deltaX\n gx += stepX\n } else {\n t = nextZ\n nextZ += deltaZ\n gz += stepZ\n }\n const hitX = x0 + dx * t\n const hitZ = z0 + dz * t\n if (grid.solidAt(hitX + Math.sign(dx) * 1e-4, hitZ + Math.sign(dz) * 1e-4, y)) return { x: x0 + dx * Math.max(0, t - 1e-3), z: z0 + dz * Math.max(0, t - 1e-3) }\n }\n return undefined\n }\n\n const fly = (slot: number): void => {\n pool.prevX[slot] = pool.x[slot]!\n pool.prevY[slot] = pool.y[slot]!\n pool.prevZ[slot] = pool.z[slot]!\n pool.vy[slot]! -= ctx.gravity * ctx.h\n pool.pitch[slot]! += pool.spinPitch[slot]! * ctx.h\n pool.roll[slot]! += pool.spinRoll[slot]! * ctx.h\n const nx = pool.x[slot]! + pool.vx[slot]! * ctx.h\n const nz = pool.z[slot]! + pool.vz[slot]! * ctx.h\n const ny = pool.y[slot]! + pool.vy[slot]! * ctx.h\n const speed = Math.hypot(pool.vx[slot]!, pool.vy[slot]!, pool.vz[slot]!)\n const wall = speed * ctx.h > SWEEP_DISTANCE ? sweepWall(pool.x[slot]!, pool.z[slot]!, nx, nz, ny) : grid.solidAt(nx, nz, ny) ? { x: pool.x[slot]!, z: pool.z[slot]! } : undefined\n pool.x[slot] = wall ? wall.x : nx\n pool.z[slot] = wall ? wall.z : nz\n pool.y[slot] = ny\n if (wall) pool.vx[slot] = pool.vz[slot] = 0\n }\n\n const massOf = (slot: number): number => {\n const agent = pool.agent[slot]!\n if (agent >= 0) return store.mass[agent]!\n const body = pool.corpse[slot]!\n return ctx.options.contact!.baseMass * body.height * body.build * body.build\n }\n\n const knockNeighbours = (contact: ContactView, balance: Balance, slot: number): void => {\n const side = Math.floor(grid.size)\n const x = pool.x[slot]!\n const z = pool.z[slot]!\n const hx = Math.floor(x + grid.half)\n const hz = Math.floor(z + grid.half)\n const mass = massOf(slot)\n const speed = Math.hypot(pool.vx[slot]!, pool.vz[slot]!)\n const reach = options.landingRadius * options.landingRadius\n for (let oz = -1; oz <= 1; oz++)\n for (let ox = -1; ox <= 1; ox++) {\n if (hx + ox < 0 || hz + oz < 0 || hx + ox >= side || hz + oz >= side) continue\n for (let j = contact.workspace.head[(hz + oz) * side + hx + ox]!; j !== -1; j = contact.workspace.next[j]!) {\n const ex = store.x[j]! - x\n const ez = store.z[j]! - z\n if (j === pool.agent[slot] || ex * ex + ez * ez > reach || !takesPartInContact(store, j)) continue\n const away = Math.hypot(ex, ez)\n const along = away > 1e-6 ? [ex / away, ez / away] : [pool.vx[slot]! / (speed || 1), pool.vz[slot]! / (speed || 1)]\n const delta = (mass / (mass + store.mass[j]!)) * speed\n store.pushX[j]! += delta * along[0]!\n store.pushZ[j]! += delta * along[1]!\n balance.shove(j, delta, FALL_CAUSE.LANDING)\n }\n }\n }\n\n const land = (contact: ContactView, balance: Balance, slot: number, ground: number): void => {\n knockNeighbours(contact, balance, slot)\n const x = pool.x[slot]!\n const z = pool.z[slot]!\n ctx.emit('sound', { sound: 'impact', volume: 1.2, position: [x, ground, z], distance: Math.hypot(x - ctx.centre.x, z - ctx.centre.z) })\n ctx.emit('effect', { effect: 'dust', point: [x, ground, z], color: 0x8a7a66 })\n const agent = pool.agent[slot]!\n if (agent >= 0) {\n store.balance[agent] = BALANCE.FALLEN\n store.balanceTimer[agent] = 0\n store.fallCause[agent] = FALL_CAUSE.BLAST\n store.x[agent] = x\n store.z[agent] = z\n store.y[agent] = ground\n store.vy[agent] = 0\n store.thrown[agent] = -1\n const amount = Math.max(0, -pool.vy[slot]! - LANDING_DAMAGE_SPEED) * options.fallDamage\n if (amount > 0) ctx.damage(agent, { region: 1, amount, distance: 0 }, () => unit(store.identity[agent]!, ctx.step, SALT.JAW))\n } else {\n ctx.emit('effect', { effect: 'blood', point: [x, ground + 0.1, z], amount: 30, died: true })\n ctx.addCorpse({ ...pool.corpse[slot]!, x, z })\n }\n pool.agent[slot] = FREE_SLOT\n pool.corpse[slot] = null\n }\n\n const step = (contact: ContactView, balance: Balance): void => {\n let active = 0\n for (let slot = 0; slot < size; slot++) {\n if (pool.agent[slot] === FREE_SLOT) continue\n fly(slot)\n const cell = grid.cellOf(pool.x[slot]!, pool.z[slot]!)\n const ground = cell < 0 ? 0 : ctx.groundAt(cell, pool.y[slot]!)\n const agent = pool.agent[slot]!\n if (pool.y[slot]! <= ground) {\n land(contact, balance, slot, ground)\n continue\n }\n active++\n if (agent < 0) continue\n store.x[agent] = pool.x[slot]!\n store.z[agent] = pool.z[slot]!\n store.y[agent] = Math.max(ground, pool.y[slot]! - CENTRE_HEIGHT * store.height[agent]!)\n }\n ctx.stats.thrownActive = active\n ctx.stats.thrownPeak = Math.max(ctx.stats.thrownPeak, active)\n }\n\n return { pool, assign, step, remap }\n}\n","import type { CrowdType } from '../types'\nimport { createBalance } from './balance'\nimport type { Balance } from './balance'\nimport { createBlasts } from './blast'\nimport type { Blasts } from './blast'\nimport { contactStep, createContactView, prepareHash } from './contact'\nimport type { ContactResult, ContactView } from './contact'\nimport { createPhysicsStats } from './context'\nimport type { PhysicsContext, PhysicsStats } from './context'\nimport { createDensity } from './density'\nimport type { Density } from './density'\nimport { createMound } from './mound'\nimport type { Mound } from './mound'\nimport type { ResolvedPhysics } from './options'\nimport { createDefaultWorkerFactory } from './offload/default-worker'\nimport { createOffload } from './offload/offload'\nimport type { Offload } from './offload/offload'\nimport { createInlineRunner, createSharedRunner, createTransferRunner, isSharedAvailable } from './offload/runner'\nimport type { StepRunner } from './offload/runner'\nimport { createPlayerPressure } from './player-pressure'\nimport type { PlayerPressure } from './player-pressure'\nimport { createPressureWorkspace, pressureStep } from './pressure'\nimport type { PressureResult, PressureWorkspace } from './pressure'\nimport { createThrown, createThrownPool } from './thrown'\nimport type { Thrown, ThrownPool } from './thrown'\n\n/** Newtons of will of a type that says nothing else (`design-chain-horde.md` §5.1). */\nexport const DEFAULT_DRIVE_FORCE = 250\nconst GRAVITY_FORCE = 9.8\nconst EXTRA_BODIES = 500\nconst PAIRS_PER_AGENT = 12\n\n/** What the crowd hands over: the shared state and the verbs only it can do. */\nexport type PhysicsBase = Omit<PhysicsContext, 'options' | 'stats' | 'step' | 'h' | 'centre' | 'lastBlastStep' | 'moundAt' | 'weightOn'> & {\n readonly types: readonly CrowdType[]\n readonly moundHeight: Uint16Array\n readonly corpsesMax: number\n pileBlast(x: number, z: number, radius: number): void\n}\n\nexport type PhysicsEngine = {\n readonly ctx: PhysicsContext\n /** True when the contact is on: the rest of the parts depend on it. */\n readonly active: boolean\n readonly stats: PhysicsStats\n readonly blasts: Blasts\n readonly thrown: ThrownPool\n readonly contact: ContactView | null\n readonly balance: Balance | null\n /** Starts the fixed step: keeps the positions, counts the step and resolves the blasts that arrived. */\n beginStep(h: number, centre: { readonly x: number; readonly z: number }): void\n /** Contact, pressure, balance, mound and thrown bodies, in the order of the design (§3, items 5 to 10). */\n endStep(): void\n /** The store removed agent `removed` and moved agent `moved` into its place. */\n remap(removed: number, moved: number): void\n /** Stops the worker, if there is one. */\n dispose(): void\n /** What the wanted speed of an agent of band `band` is multiplied by in a thick far crowd (1 with the density off or in any other band). */\n densitySlowdown(i: number, band: number): number\n /** `pressure / critical pressure` of an agent: how close it is to falling. */\n pressureRatio(i: number): number\n}\n\nexport function createPhysics(base: PhysicsBase, options: ResolvedPhysics, stats: PhysicsStats = createPhysicsStats()): PhysicsEngine {\n const moundOn = options.mound !== null\n const ctx: PhysicsContext = { ...base, options, stats, step: -1, h: 0, centre: { x: 0, z: 0 }, lastBlastStep: -1000, moundAt: (cell) => (moundOn ? base.moundHeight[cell]! : 0), weightOn: () => 0 }\n const { store, grid } = base\n const balance = options.balance ? createBalance(ctx) : null\n const thrown: Thrown | null = options.thrown ? createThrown(ctx) : null\n const contact = options.contact ? createContactView(store, grid) : null\n const pressure: PressureWorkspace | null = options.pressure && contact ? createPressureWorkspace(store.capacity, store.capacity * PAIRS_PER_AGENT) : null\n const mound: Mound | null = options.mound ? createMound(ctx, Math.max(EXTRA_BODIES, base.corpsesMax), base.moundHeight) : null\n if (mound) ctx.weightOn = mound.weightOn\n const density: Density | null = options.density && balance ? createDensity(ctx, options.density) : null\n const playerPressure: PlayerPressure | null = pressure ? createPlayerPressure(ctx) : null\n const blasts = createBlasts(ctx, { balance, thrown, pileBlast: base.pileBlast })\n const contactResult: ContactResult = { contacts: 0, maxOverlap: 0, overlapSum: 0 }\n const pressureResult: PressureResult = { playerLoad: 0, drivers: 0 }\n const driveForceOfType = Float32Array.from(base.types, (type) => type.balance?.driveForce ?? DEFAULT_DRIVE_FORCE)\n const offload: Offload | null = options.worker !== 'off' && contact ? createOffloadFor() : null\n let doorLoad = new Float32Array(0)\n let doorSince = new Float32Array(0)\n const keepPositions = options.contact !== null || options.deterministic\n\n function createRunner(): StepRunner | null {\n const gridCells = grid.occ.length\n const shape = { capacity: store.capacity, cells: gridCells }\n const setup = { capacity: store.capacity, gridSize: grid.size, gridCell: grid.cell, contact: options.contact!, pressure: options.pressure, driveForceOfType: Array.from(driveForceOfType) }\n const transport = options.workerTransport ?? (isSharedAvailable() ? 'shared' : 'transfer')\n const slots = options.workerLatency\n if (transport === 'inline') return createInlineRunner(setup, shape, slots)\n const factory = options.workerFactory ?? createDefaultWorkerFactory()\n if (!factory) {\n if (options.worker === 'on') throw new Error('crowd physics: worker \"on\" but no Worker can be made here (give physics.workerFactory)')\n return null\n }\n return transport === 'shared' ? createSharedRunner(setup, shape, factory, slots) : createTransferRunner(setup, shape, factory, slots)\n }\n\n function createOffloadFor(): Offload | null {\n const runner = createRunner()\n if (!runner) return null\n const host = {\n store,\n grid,\n stats,\n contact: contact!,\n addDoorLoad: (cell: number, load: number): void => {\n const door = ctx.doors.at(cell)\n if (door < 0) return\n growDoorArrays()\n doorLoad[door]! += load\n },\n }\n return createOffload(runner, host, options.workerLatency)\n }\n\n const beginStep = (h: number, centre: { readonly x: number; readonly z: number }): void => {\n ctx.step++\n ctx.h = h\n ctx.centre = { x: centre.x, z: centre.z }\n if (keepPositions) {\n for (let i = 0; i < store.n; i++) {\n store.prevX[i] = store.x[i]!\n store.prevZ[i] = store.z[i]!\n store.prevY[i] = store.y[i]!\n }\n }\n blasts.resolve()\n }\n\n /** The load the field passed to each door in the step; every `attackEvery` seconds it becomes one blow. */\n const hitDoors = (): void => {\n for (let door = 0; door < ctx.doors.list.length; door++) {\n if (doorLoad[door]! <= 0) continue\n doorSince[door]! += ctx.h\n if (doorSince[door]! < ctx.attackEvery) continue\n ctx.doors.hit(door, doorLoad[door]! * options.doorPressureDamage)\n doorLoad[door] = 0\n doorSince[door] = 0\n }\n }\n\n const growDoorArrays = (): void => {\n if (doorLoad.length >= ctx.doors.list.length) return\n const length = Math.max(64, ctx.doors.list.length * 2)\n doorLoad = Float32Array.from({ length }, (_, door) => doorLoad[door] ?? 0)\n doorSince = Float32Array.from({ length }, (_, door) => doorSince[door] ?? 0)\n }\n\n const runPressure = (view: ContactView, workspace: PressureWorkspace): void => {\n growDoorArrays()\n const target = ctx.target()\n const player = options.playerPressure.enabled && target && target.state.alive ? { x: target.state.x, z: target.state.z } : undefined\n pressureStep(view, workspace, { ...options.pressure!, h: ctx.h, radius: options.contact!.radius }, { driveForceOfType, doorAt: ctx.doors.at, doorLoad, player }, pressureResult)\n let top = 0\n for (let i = 0; i < store.n; i++) top = Math.max(top, store.field[i]!)\n stats.fieldMax = top\n hitDoors()\n }\n\n /** The contact and the pressure ran in the worker on the state of the previous step: apply them, hand over this step, and keep the hash for the thrown bodies. */\n const stepOffloaded = (running: Offload, view: ContactView): void => {\n growDoorArrays()\n const target = ctx.target()\n const player = options.playerPressure.enabled && target && target.state.alive ? { x: target.state.x, z: target.state.z } : undefined\n const result = running.step(ctx.h, player)\n pressureResult.playerLoad = result.playerLoad\n prepareHash(view, options.contact!.radius)\n if (pressure) hitDoors()\n }\n\n const measureLean = (): void => {\n const maxPush = options.contact!.maxPush\n for (let i = 0; i < store.n; i++) {\n store.leanX[i] = store.pushX[i]! / maxPush\n store.leanZ[i] = store.pushZ[i]! / maxPush\n }\n }\n\n const endStep = (): void => {\n if (!contact) return\n contact.n = store.n\n if (offload) stepOffloaded(offload, contact)\n else {\n contactStep(contact, { ...options.contact!, h: ctx.h }, contactResult)\n stats.contacts = contactResult.contacts\n stats.maxOverlap = contactResult.maxOverlap\n stats.meanOverlap = contactResult.contacts ? contactResult.overlapSum / contactResult.contacts : 0\n if (pressure) runPressure(contact, pressure)\n }\n density?.step()\n measureLean()\n balance?.step(options.balance!, contact.workspace.shove)\n mound?.step()\n thrown?.step(contact, balance!)\n balance?.flush()\n playerPressure?.step(pressureResult.playerLoad)\n }\n\n const criticalOf = (i: number): number => options.balance!.criticalFactor * store.mass[i]! * GRAVITY_FORCE * (base.typeOf(i).balance?.stability ?? 1) * store.stability[i]!\n\n return {\n ctx,\n active: contact !== null,\n stats,\n blasts,\n thrown: thrown?.pool ?? createThrownPool(0),\n contact,\n balance,\n beginStep,\n endStep,\n remap: (removed, moved) => {\n thrown?.remap(removed, moved)\n offload?.remap(removed, moved)\n },\n dispose: () => offload?.dispose(),\n densitySlowdown: (i, band) => density?.slowdown(i, band) ?? 1,\n pressureRatio: (i) => (options.balance ? store.pressure[i]! / criticalOf(i) : 0),\n }\n}\n","import type { CrowdStore } from '../store'\nimport { STATE } from '../types'\n\n/** What a falling pile step marks on the steps that come down with it, so the landing knows it was a collapse and not a jump. */\nexport const COLLAPSE_MARK = -7\n\nexport type PileColumn = { readonly cell: number; readonly dir: number }\n\n/**\n * The steps of the pile behind a step that went, in its column (`cell - dir`, `cell - 2 dir`, ...), come down with it: they let go of the pile and fall from where they are. The column stops\n * at the first cell with no step in it.\n */\nexport function collapsePileColumns(store: CrowdStore, columns: PileColumn[], cellCount: number, release: (i: number) => void): void {\n const owners = new Map<number, number>()\n for (let i = 0; i < store.n; i++) if (store.state[i] === STATE.PILE_STEP && store.stepCell[i]! >= 0) owners.set(store.stepCell[i]!, i)\n for (const column of columns.splice(0)) {\n if (column.dir === 0) continue\n for (let k = 1, cell = column.cell - column.dir; cell >= 0 && cell < cellCount; k++, cell = column.cell - column.dir * k) {\n const j = owners.get(cell)\n if (j === undefined) break\n owners.delete(cell)\n release(j)\n store.state[j] = STATE.AIRBORNE\n store.vy[j] = 0\n store.stateTimer[j] = COLLAPSE_MARK\n }\n }\n}\n","import { SALT, unit } from './identity'\nimport type { CrowdPersonalityOptions } from './physics/options'\nimport type { CrowdStore } from './store'\n\nconst GAIT_WEIGHTS = [6, 1, 1, 1, 1, 2] as const\n\n/** A triangular draw between the two ends of the range, from two independent counter-based draws of the agent. */\nconst triangular = (identity: number, salt: number, range: readonly [number, number]): number => range[0] + (range[1] - range[0]) * ((unit(identity, 0, salt) + unit(identity, 1, salt)) / 2)\n\nconst weighted = (identity: number, salt: number, weights: readonly number[]): number => {\n const total = weights.reduce((sum, weight) => sum + weight, 0)\n let pick = unit(identity, 0, salt) * total\n for (let index = 0; index < weights.length; index++) {\n pick -= weights[index]!\n if (pick < 0) return index\n }\n return weights.length - 1\n}\n\n/** No personality: every factor is neutral, so the maths of the game does not change. */\nexport function applyNeutralPersonality(store: CrowdStore, i: number): void {\n store.stability[i] = store.tripFactor[i] = store.driveFactor[i] = store.cadenceFactor[i] = store.aggression[i] = 1\n store.reaction[i] = 0\n store.idle[i] = 0\n store.gaitStyle[i] = 0\n}\n\n/**\n * The personality of an agent (T4b.8), all of it from its identity and never from the shared random stream: a speed factor that multiplies the chase and wander speeds,\n * a reaction time, the stability, trip and drive factors that move its thresholds of falling and climbing, the cadence of its walk, its aggression, a gait and an idle.\n */\nexport function applyPersonality(store: CrowdStore, i: number, options: Required<CrowdPersonalityOptions>): void {\n const identity = store.identity[i]!\n const speed = triangular(identity, SALT.SPEED, options.speed)\n store.chaseSpeed[i]! *= speed\n store.wanderSpeed[i]! *= speed\n store.reaction[i] = triangular(identity, SALT.REACTION, options.reaction)\n store.stability[i] = triangular(identity, SALT.STABILITY, options.stability)\n store.tripFactor[i] = triangular(identity, SALT.TRIP_FACTOR, options.trip)\n store.driveFactor[i] = triangular(identity, SALT.DRIVE, options.drive)\n store.cadenceFactor[i] = triangular(identity, SALT.CADENCE, options.cadence)\n store.aggression[i] = triangular(identity, SALT.AGGRESSION, options.aggression)\n store.idle[i] = weighted(identity, SALT.IDLE, options.idle)\n store.gaitStyle[i] = weighted(identity, SALT.GAIT, GAIT_WEIGHTS)\n}\n","import { identityOf } from '../identity'\nimport { applyNeutralPersonality, applyPersonality } from '../personality'\nimport type { CrowdStore } from '../store'\nimport { BALANCE } from '../types'\nimport type { CrowdPersonalityOptions } from './options'\n\n/** The fields of the physics of a newborn: identity, mass and personality, all without a draw from the shared stream. */\nexport function seatPhysics(store: CrowdStore, i: number, born: { readonly seed: number; readonly serial: number; readonly baseMass: number; readonly personality: Required<CrowdPersonalityOptions> | null }): void {\n store.identity[i] = identityOf(born.seed, born.serial)\n store.mass[i] = born.baseMass * store.height[i]! * store.build[i]! ** 2\n store.balance[i] = BALANCE.STANDING\n store.balanceTimer[i] = store.pushX[i] = store.pushZ[i] = store.pressure[i] = store.field[i] = store.fieldPrev[i] = store.aimX[i] = store.aimZ[i] = store.directShove[i] = store.leanX[i] = store.leanZ[i] = 0\n store.fallCause[i] = 0\n store.blastStep[i] = -1\n store.thrown[i] = -1\n store.wounds[i] = 0\n store.stepDir[i] = 0\n store.prevX[i] = store.x[i]!\n store.prevZ[i] = store.z[i]!\n store.prevY[i] = 0\n if (born.personality) applyPersonality(store, i, born.personality)\n else applyNeutralPersonality(store, i)\n}\n","import type { BlastShoveOptions } from '@adatechnology/game-contracts'\n\nimport type { OffloadTransport, PhysicsWorkerFactory } from './offload/types'\n\nexport type CrowdContactOptions = {\n /** Body radius over build (default 0.25 → 0.5 m between two average bodies; the separation keeps them at 0.78 m when it can). */\n readonly radius?: number\n /** Iterations per fixed step in band R0 (default 2; 3 is the ceiling). */\n readonly iterations?: 1 | 2 | 3\n /** Push velocity damping per second: push ← push·exp(−friction·h) (default 6 → λ = 0.905 at 60 Hz). */\n readonly friction?: number\n /** Ceiling of the push velocity, m/s (default 3). */\n readonly maxPush?: number\n /** Ceiling of the position correction of one agent in one iteration, m (default 0.1; the design started at 0.06). */\n readonly maxCorrection?: number\n /** Jacobi relaxation of the position correction (default 1; the design started at 0.5, which left 0.25 m overlaps in a crush of 800). */\n readonly relaxation?: number\n /** kg of a body of height 1 and build 1 (default 70). */\n readonly baseMass?: number\n /** Ceiling of the separation term `|Σ sep · separationPush|`, m/s (default 1.5). */\n readonly separationMax?: number\n}\n\nexport type CrowdPressureOptions = {\n /** Share kept per hop, τ (default 0.97). */\n readonly transmission?: number\n /** Ceiling of the field per agent, N (default 6000). */\n readonly cap?: number\n /** Cosine of the half angle that counts as \"in front\" (default 0.5 → 60°). */\n readonly frontCone?: number\n /** Share of the will speed of a staggering agent (default 0.35); the field keeps the full drive. */\n readonly staggerSpeed?: number\n}\n\nexport type CrowdBalanceOptions = {\n /** `P_crit = criticalFactor · m · g · stability` (default 2.0 → 1372 N for a walker). */\n readonly criticalFactor?: number\n /** P/P_crit to start staggering (default 0.7). */\n readonly staggerEnter?: number\n /** P/P_crit to stop, after `staggerMin` (default 0.4). */\n readonly staggerLeave?: number\n /** Seconds (default 0.5). */\n readonly staggerMin?: number\n /** 1/s, rate = fallRate·(P/P_crit − 1)² above 1 (default 1.5). */\n readonly fallRate?: number\n /** |Δpush| in one step that staggers, m/s (default 1.2). */\n readonly shoveStagger?: number\n /** |Δpush| that knocks down, m/s, times stability (default 2.5). */\n readonly shoveFall?: number\n /** Seconds on the ground before trying to get up (default 1.5, plus reaction). */\n readonly fallenMin?: number\n /** P/P_crit under which a fallen one may get up (default 0.3). */\n readonly releaseFactor?: number\n /** Default 1.2. */\n readonly getUpSeconds?: number\n /** Seconds of the exponential mean (default 0.3). */\n readonly pressureTau?: number\n /** Ceiling of new falls per fixed step (default 8). */\n readonly maxFallsPerStep?: number\n /** Ceiling of agents on the ground (default 300); above it a fall becomes a stagger. */\n readonly maxFallen?: number\n /** Damage per second to a fallen body per 70 kg standing on it (default 6). */\n readonly trampleDps?: number\n}\n\nexport type CrowdMoundOptions = {\n /** m per cell of a lying body, times build (default 0.28). */\n readonly bodyThickness?: number\n /** Default 0.25. */\n readonly corpseThickness?: number\n /** Ceiling of the mound per cell, m (default 1.6, about 5 or 6 bodies). */\n readonly maxHeight?: number\n /** Corpses within this distance of the band centre count (default 35, band R0). */\n readonly corpseRadius?: number\n}\n\nexport type CrowdThrownOptions = {\n /** Slots of the pool (default 32, at most 127). */\n readonly pool?: number\n /** Only within this distance of the band centre (default 30 m). */\n readonly radius?: number\n /** m around the landing point that is knocked (default 1.2: a body at 10 m/s knocks the ones within about two body widths; 0.7 left the chain beyond a grenade at 0 in 4 of 12 scenes). */\n readonly landingRadius?: number\n /** Damage per m/s of landing speed above 6 m/s (default 8). */\n readonly fallDamage?: number\n /** Who gets a slot when there are more candidates than slots: half the slots to the fastest and half to the nearest to the band centre (`'mixed'`, default), or only one criterion. */\n readonly priority?: 'mixed' | 'fastest' | 'nearest'\n /** Share of the slots that goes to the fastest in `'mixed'` (default 0.5). */\n readonly mixedRatio?: number\n}\n\nexport type CrowdBlastOptions = {\n /** Damage at the centre per kJ before falloff (default 0.6 → m67 120 points). */\n readonly pointsPerKilojoule?: number\n /** Shove speed from which a body flies, m/s (default 4). */\n readonly throwSpeed?: number\n /** Blasts resolved in one fixed step; more wait for the next one, in order (default 8). */\n readonly maxBlastsPerStep?: number\n /** `limb` effects per blast; the rest is summed into one blood effect (default 24). */\n readonly limbEffectsPerBlast?: number\n /** `blood` effects per blast (default 32). */\n readonly bloodEffectsPerBlast?: number\n /** The shove rule of `game-contracts`, the same numbers the host lends to the items and the furniture. */\n readonly shove?: BlastShoveOptions\n}\n\nexport type CrowdPersonalityOptions = {\n /** Chase and wander factor (default [0.9, 1.1]). */\n readonly speed?: readonly [number, number]\n /** Seconds added to getting up and to the alert hop (default [0.05, 0.35]). */\n readonly reaction?: readonly [number, number]\n readonly stability?: readonly [number, number]\n readonly trip?: readonly [number, number]\n /** Pushing will (default [0.7, 1.3]). */\n readonly drive?: readonly [number, number]\n /** Walk cadence factor (default [0.85, 1.15]). */\n readonly cadence?: readonly [number, number]\n /** Attack will (default [0.8, 1.2]): divides `attackEvery`, multiplies the bite chance and decides who gets the last attack slots. */\n readonly aggression?: readonly [number, number]\n /** Weights of sway, shuffle, head twitch, still (default [3, 2, 2, 1]). */\n readonly idle?: readonly number[]\n}\n\n/** Band R1, summarised (D10, `design-chain-horde.md` §9): no pairs, a coarse grid of bodies per cell. */\nexport type CrowdDensityOptions = {\n /** Side of a coarse cell, m (default 2). */\n readonly cell?: number\n /** Bodies per cell that walk freely (default 4, one per square metre); above it the speed is `capacity / bodies`. */\n readonly capacity?: number\n /** N of pressure per body above `capacity` (default 150). */\n readonly pressurePerBody?: number\n}\n\n/** Pressure of the horde on the target (D13.2): above `knockThreshold` N it knocks the target down; the excess crushes at `crushDamagePerSecond`. */\nexport type CrowdPlayerPressureOptions = {\n readonly enabled?: boolean\n readonly knockThreshold?: number\n readonly crushDamagePerSecond?: number\n}\n\nexport type CrowdPhysicsOptions = {\n readonly contact?: CrowdContactOptions | boolean\n /** Needs `contact`. */\n readonly pressure?: CrowdPressureOptions | boolean\n /** Needs `contact`. */\n readonly balance?: CrowdBalanceOptions | boolean\n /** Needs `balance`. */\n readonly mound?: CrowdMoundOptions | boolean\n /** Needs `balance`. */\n readonly thrown?: CrowdThrownOptions | boolean\n /** Needs `balance`. The far band without pairs: speed and pressure from the bodies per coarse cell. */\n readonly density?: CrowdDensityOptions | boolean\n readonly blast?: CrowdBlastOptions\n readonly personality?: CrowdPersonalityOptions | boolean\n readonly door?: { readonly pressureDamage?: number }\n /** Needs `pressure`. Off in the package; a game turns it on. */\n readonly playerPressure?: CrowdPlayerPressureOptions\n /** Flow budget in cells per step, `attackPose` in the simulation, positions kept for interpolation. */\n readonly deterministic?: boolean\n /**\n * Where the contact and the pressure run. `'off'` (default): in the step, exactly as before. `'on'`: in a worker, one step behind (the main thread applies what the worker\n * computed from the previous step), and an error when no worker can be made. `'auto'`: the same when a worker can be made, `'off'` when it cannot. Needs `contact`.\n */\n readonly worker?: 'off' | 'auto' | 'on'\n /** Makes the worker (default: a module worker that loads `physics-worker.js` next to the bundle). A host that bundles its own worker gives the factory and calls `attachPhysicsWorker` in it. */\n readonly workerFactory?: PhysicsWorkerFactory\n /** `'shared'` (a `SharedArrayBuffer`, deterministic, needs `crossOriginIsolated`), `'transfer'` (`postMessage`, any host, the answer is as late as the event loop makes it), `'inline'` (no worker: the same protocol on the main thread, the reference). Default: `'shared'` when it is available, else `'transfer'`. */\n readonly workerTransport?: OffloadTransport\n /** Steps between the state the worker is handed and the step that applies its answer (default 1). 2 gives the worker a whole step to finish in, so the main thread never waits for it, and costs one more step of lag in the contact. */\n readonly workerLatency?: 1 | 2\n}\n\nexport const CONTACT_DEFAULTS: Required<CrowdContactOptions> = { radius: 0.25, iterations: 2, friction: 6, maxPush: 3, maxCorrection: 0.1, relaxation: 1, baseMass: 70, separationMax: 1.5 }\nexport const PRESSURE_DEFAULTS: Required<CrowdPressureOptions> = { transmission: 0.97, cap: 6000, frontCone: 0.5, staggerSpeed: 0.35 }\nexport const BALANCE_DEFAULTS: Required<CrowdBalanceOptions> = {\n criticalFactor: 2,\n staggerEnter: 0.7,\n staggerLeave: 0.4,\n staggerMin: 0.5,\n fallRate: 1.5,\n shoveStagger: 1.2,\n shoveFall: 2.5,\n fallenMin: 1.5,\n releaseFactor: 0.3,\n getUpSeconds: 1.2,\n pressureTau: 0.3,\n maxFallsPerStep: 8,\n maxFallen: 300,\n trampleDps: 6,\n}\nexport const MOUND_DEFAULTS: Required<CrowdMoundOptions> = { bodyThickness: 0.28, corpseThickness: 0.25, maxHeight: 1.6, corpseRadius: 35 }\nexport const THROWN_DEFAULTS: Required<CrowdThrownOptions> = { pool: 32, radius: 30, landingRadius: 1.2, fallDamage: 8, priority: 'mixed', mixedRatio: 0.5 }\nexport const DENSITY_DEFAULTS: Required<CrowdDensityOptions> = { cell: 2, capacity: 4, pressurePerBody: 150 }\nexport const BLAST_DEFAULTS: Required<CrowdBlastOptions> = { pointsPerKilojoule: 0.6, throwSpeed: 4, maxBlastsPerStep: 8, limbEffectsPerBlast: 24, bloodEffectsPerBlast: 32, shove: {} }\nexport const PERSONALITY_DEFAULTS: Required<CrowdPersonalityOptions> = {\n speed: [0.9, 1.1],\n reaction: [0.05, 0.35],\n stability: [0.75, 1.25],\n trip: [0.5, 1.5],\n drive: [0.7, 1.3],\n cadence: [0.85, 1.15],\n aggression: [0.8, 1.2],\n idle: [3, 2, 2, 1],\n}\nexport const PLAYER_PRESSURE_DEFAULTS: Required<CrowdPlayerPressureOptions> = { enabled: false, knockThreshold: 1500, crushDamagePerSecond: 6 }\n/** Pressure damage per N·s on a door pushed by the horde (§6.5). */\nexport const DOOR_PRESSURE_DAMAGE = 0.004\n/** Flow cells per step in deterministic mode. */\nexport const DETERMINISTIC_FLOW_CELLS = 20000\n\n/** Everything on, deterministic, the numbers of the design (`design-chain-horde.md`); the player pressure stays off, a game turns it on. */\nexport const CROWD_PHYSICS_DEFAULTS: CrowdPhysicsOptions = {\n contact: CONTACT_DEFAULTS,\n pressure: PRESSURE_DEFAULTS,\n balance: BALANCE_DEFAULTS,\n mound: MOUND_DEFAULTS,\n thrown: THROWN_DEFAULTS,\n density: DENSITY_DEFAULTS,\n blast: BLAST_DEFAULTS,\n personality: PERSONALITY_DEFAULTS,\n door: { pressureDamage: DOOR_PRESSURE_DAMAGE },\n playerPressure: PLAYER_PRESSURE_DEFAULTS,\n deterministic: true,\n}\n\nexport type ResolvedPhysics = {\n readonly contact: Required<CrowdContactOptions> | null\n readonly pressure: Required<CrowdPressureOptions> | null\n readonly balance: Required<CrowdBalanceOptions> | null\n readonly mound: Required<CrowdMoundOptions> | null\n readonly thrown: Required<CrowdThrownOptions> | null\n readonly density: Required<CrowdDensityOptions> | null\n readonly blast: Required<CrowdBlastOptions>\n readonly personality: Required<CrowdPersonalityOptions> | null\n readonly doorPressureDamage: number\n readonly playerPressure: Required<CrowdPlayerPressureOptions>\n readonly deterministic: boolean\n readonly worker: 'off' | 'auto' | 'on'\n readonly workerFactory: PhysicsWorkerFactory | undefined\n readonly workerTransport: OffloadTransport | undefined\n readonly workerLatency: 1 | 2\n}\n\nconst part = <T extends object>(option: T | boolean | undefined, defaults: Required<T>): Required<T> | null => {\n if (option === undefined || option === false) return null\n return option === true ? defaults : { ...defaults, ...option }\n}\n\n/** Reads `CrowdOptions.physics`: the defaults filled in, and a thrown error that lists every part whose dependency is missing. */\nexport function resolvePhysics(physics: CrowdPhysicsOptions = {}): ResolvedPhysics {\n const contact = part(physics.contact, CONTACT_DEFAULTS)\n const pressure = part(physics.pressure, PRESSURE_DEFAULTS)\n const balance = part(physics.balance, BALANCE_DEFAULTS)\n const mound = part(physics.mound, MOUND_DEFAULTS)\n const thrown = part(physics.thrown, THROWN_DEFAULTS)\n const density = part(physics.density, DENSITY_DEFAULTS)\n const playerPressure = { ...PLAYER_PRESSURE_DEFAULTS, ...physics.playerPressure }\n const missing: string[] = []\n if (!contact && pressure) missing.push('pressure needs contact')\n if (!contact && balance) missing.push('balance needs contact')\n if (!balance && mound) missing.push('mound needs balance')\n if (!balance && thrown) missing.push('thrown needs balance')\n if (!balance && density) missing.push('density needs balance')\n if (!pressure && playerPressure.enabled) missing.push('playerPressure needs pressure')\n if (!contact && physics.worker === 'on') missing.push('worker needs contact')\n if (missing.length) throw new Error(`crowd physics: ${missing.join('; ')}`)\n return {\n contact,\n pressure,\n balance,\n mound,\n thrown,\n density,\n blast: { ...BLAST_DEFAULTS, ...physics.blast },\n personality: part(physics.personality, PERSONALITY_DEFAULTS),\n doorPressureDamage: physics.door?.pressureDamage ?? DOOR_PRESSURE_DAMAGE,\n playerPressure,\n deterministic: physics.deterministic ?? false,\n worker: physics.worker ?? 'off',\n workerFactory: physics.workerFactory,\n workerTransport: physics.workerTransport,\n workerLatency: physics.workerLatency ?? 1,\n }\n}\n","const FNV_OFFSET = 0x811c9dc5\nconst FNV_PRIME = 0x01000193\nconst MIX = 0x9e3779b1\n\n/** Deterministic mulberry32 generator; returns floats in [0, 1). */\nexport function createRng(seed: number): () => number {\n let state = seed >>> 0\n return () => {\n state = (state + 0x6d2b79f5) >>> 0\n let mixed = Math.imul(state ^ (state >>> 15), state | 1)\n mixed ^= mixed + Math.imul(mixed ^ (mixed >>> 7), mixed | 61)\n return ((mixed ^ (mixed >>> 14)) >>> 0) / 4294967296\n }\n}\n\n/** A seed for the stream called `name`: the same seed gives each name its own sequence, so lightning never shifts the schedule. */\nexport function streamSeed(seed: number, name: string): number {\n let hash = FNV_OFFSET\n for (let index = 0; index < name.length; index++) hash = Math.imul(hash ^ name.charCodeAt(index), FNV_PRIME) >>> 0\n return Math.imul((seed >>> 0) ^ hash, MIX) >>> 0\n}\n\nexport function createStream(seed: number, name: string): () => number {\n return createRng(streamSeed(seed, name))\n}\n\nexport function drawBetween(rng: () => number, range: readonly [min: number, max: number]): number {\n return range[0] + rng() * (range[1] - range[0])\n}\n","/**\n * The agents as a struct of arrays (G 66717 `Z`, ZMAX 6000): one typed array per field, the same types as the game so the floats round the same way.\n * The names are the game's in English; `armHp` and `legHp` hold two values per agent (left, right).\n */\nexport const createStore = (capacity: number) => {\n const f32 = () => new Float32Array(capacity)\n const u8 = () => new Uint8Array(capacity)\n return {\n n: 0,\n capacity,\n x: f32(),\n z: f32(),\n vx: f32(),\n vz: f32(),\n yaw: f32(),\n phase: f32(),\n type: u8(),\n age: u8(),\n outfit: u8(),\n height: f32(),\n build: f32(),\n chaseSpeed: f32(),\n wanderSpeed: f32(),\n hp: f32(),\n hpMax: f32(),\n headHp: f32(),\n armHp: new Float32Array(capacity * 2),\n legHp: new Float32Array(capacity * 2),\n missing: new Uint16Array(capacity),\n helmet: u8(),\n state: u8(),\n stateTimer: f32(),\n lost: f32(),\n attackTimer: f32(),\n band: u8(),\n next: f32(),\n wx: f32(),\n wz: f32(),\n crawl: u8(),\n limp: u8(),\n color: new Uint32Array(capacity),\n skin: f32(),\n alertIn: f32(),\n cooldown: f32(),\n y: f32(),\n floor: u8(),\n floorLocked: u8(),\n vy: f32(),\n abilityTimer: f32(),\n eaten: u8(),\n stuck: f32(),\n stepCell: new Int32Array(capacity),\n burn: f32(),\n hunts: new Int8Array(capacity),\n named: new Int8Array(capacity),\n dvx: f32(),\n dvz: f32(),\n steering: u8(),\n renderYaw: f32(),\n renderSpeed: f32(),\n pause: f32(),\n lod: u8(),\n variant: u8(),\n attackPose: u8(),\n arms: f32(),\n stepDir: new Int32Array(capacity),\n // physics (CrowdOptions.physics): they exist with it off, so the store keeps its shape; the game replays never read them\n pushX: f32(),\n pushZ: f32(),\n mass: f32(),\n pressure: f32(),\n field: f32(),\n fieldPrev: f32(),\n aimX: f32(),\n aimZ: f32(),\n directShove: f32(),\n blastStep: new Int32Array(capacity),\n balance: u8(),\n balanceTimer: f32(),\n fallCause: u8(),\n identity: new Uint32Array(capacity),\n stability: f32(),\n tripFactor: f32(),\n driveFactor: f32(),\n reaction: f32(),\n cadenceFactor: f32(),\n aggression: f32(),\n idle: u8(),\n gaitStyle: u8(),\n wounds: new Uint16Array(capacity),\n leanX: f32(),\n leanZ: f32(),\n thrown: new Int8Array(capacity),\n prevX: f32(),\n prevZ: f32(),\n prevY: f32(),\n }\n}\nexport type CrowdStore = ReturnType<typeof createStore>\ntype Arrays = Omit<CrowdStore, 'n' | 'capacity' | 'armHp' | 'legHp'>\n/** The one-per-agent fields (G 66733 `FIELDS`): what a removal copies from the last agent. */\nexport const AGENT_FIELDS = [\n 'x', 'z', 'vx', 'vz', 'yaw', 'phase', 'type', 'age', 'outfit', 'height', 'build', 'chaseSpeed', 'wanderSpeed', 'hp', 'hpMax', 'headHp', 'missing', 'helmet', 'state', 'stateTimer', 'lost', 'attackTimer',\n 'band', 'next', 'wx', 'wz', 'crawl', 'limp', 'color', 'skin', 'alertIn', 'cooldown', 'y', 'floor', 'floorLocked', 'vy', 'abilityTimer', 'eaten', 'stuck', 'stepCell', 'burn', 'hunts', 'named', 'dvx', 'dvz',\n 'steering', 'renderYaw', 'renderSpeed', 'pause', 'lod', 'variant', 'attackPose', 'arms', 'stepDir',\n 'pushX', 'pushZ', 'mass', 'pressure', 'field', 'fieldPrev', 'aimX', 'aimZ', 'directShove', 'blastStep', 'balance', 'balanceTimer', 'fallCause', 'identity', 'stability', 'tripFactor', 'driveFactor', 'reaction',\n 'cadenceFactor', 'aggression', 'idle', 'gaitStyle', 'wounds', 'leanX', 'leanZ', 'thrown', 'prevX', 'prevZ', 'prevY',\n] as const satisfies readonly (keyof Arrays)[]\n","import { createCorpseLedger } from '@adatechnology/game-characters'\nimport type { CorpseLedger } from '@adatechnology/game-characters'\n\nimport defaults from './data/defaults.json'\nimport { createDoors } from './doors'\nimport type { Doors } from './doors'\nimport { createNavGrid } from './grid'\nimport type { NavGrid } from './grid'\nimport { unit, SALT } from './identity'\nimport { createPhysics } from './physics/engine'\nimport { COLLAPSE_MARK, collapsePileColumns } from './physics/pile-column'\nimport type { PileColumn } from './physics/pile-column'\nimport { seatPhysics } from './physics/seat'\nimport { DETERMINISTIC_FLOW_CELLS, resolvePhysics } from './physics/options'\nimport type { CrowdPhysicsOptions } from './physics/options'\nimport { createPhysicsStats } from './physics/context'\nimport type { PhysicsStats } from './physics/context'\nimport type { ThrownPool } from './physics/thrown'\nimport { createRng } from './rng'\nimport { AGENT_FIELDS, createStore } from './store'\nimport type { CrowdStore } from './store'\nimport { BALANCE, STATE } from './types'\nimport type { Corpse, CrowdBlast, CrowdDamageResult, CrowdEventMap, CrowdEventName, CrowdHit, CrowdPack, CrowdTraits, CrowdTarget, CrowdType } from './types'\n\nexport type CrowdNumbers = typeof defaults.numbers\nexport type CrowdBand = { readonly max: number; readonly dt: number }\n/** The game's numbers; `bands[3].max` is `Infinity` (JSON keeps it as null). */\nexport const CROWD_DEFAULTS: { readonly sources: Readonly<Record<string, string>>; readonly bands: readonly CrowdBand[]; readonly numbers: CrowdNumbers } = {\n sources: defaults.sources,\n bands: defaults.bands.map((band) => ({ max: band.max ?? Infinity, dt: band.dt })),\n numbers: defaults.numbers,\n}\n\nexport type { Corpse } from './types'\n/** A custom move: runs on every decision tick after the timed states and before the moves on the target; `true` means it took care of the movement. */\nexport type CrowdPlugin = {\n readonly id: string\n ability?(crowd: Crowd, i: number, context: { d: number; dx: number; dz: number; tdt: number; band: number; alive: boolean }): boolean\n}\n\n/** A migration: `count` agents every `every` s (the first after `numbers.migrationFirst`), born in the box at (x, z) and walking to `goal`. */\nexport type MigrationOptions = { readonly every: number; readonly count: number; readonly x: number; readonly z: number; readonly width: number; readonly depth: number; readonly goal: readonly [number, number] }\nexport type CrowdOptions = {\n readonly pack: CrowdPack\n readonly random?: () => number\n readonly seed?: number\n readonly capacity?: number\n readonly grid?: NavGrid\n readonly doors?: Doors\n /** Water at (x, z): the drowned swim, the charred lose health (the game's `POOL`). */\n readonly water?: (x: number, z: number) => boolean\n readonly numbers?: Partial<CrowdNumbers>\n readonly bands?: readonly CrowdBand[]\n /** Flow field: half window in cells (192), and the clock its time budget reads (default `performance.now`; pass `() => 0` to finish every field in one frame). */\n readonly flow?: { readonly radius?: number; readonly clock?: () => number; /** Flow budget in cells per step instead of the clock: the same field at any frame rate (`physics.deterministic` sets 20000). */ readonly cellsPerStep?: number }\n readonly plugins?: readonly CrowdPlugin[]\n readonly target?: CrowdTarget | null\n /** Contact, pressure, balance, mounds, thrown bodies and personality: all off by default, and then the horde is the game's, bit for bit. */\n readonly physics?: CrowdPhysicsOptions\n}\nexport type CrowdView = { readonly x: number; readonly z: number; readonly yaw: number; readonly far: number }\n\nexport type CrowdUpdateContext = {\n readonly target?: CrowdTarget | null\n /** 0 day, 1 night: sight drops by 40% at night. */\n readonly nightFactor?: number\n /** Where the flow field converges (the target by default; the foot of a ladder when the target is on a roof). */\n readonly flowTarget?: readonly [number, number]\n /** Where the viewer looks (yaw: forward is `(-sin, -cos)`); the stalker wakes when the target turns its back. Default: the target's view. */\n readonly viewYaw?: number\n /** Centre of the relevance bands, for scenes without a player near the agents (tests, benches, cutscenes). Default: the target, or `flowTarget` without one. */\n readonly bandCentre?: readonly [number, number]\n}\n\nexport type Crowd = {\n readonly store: CrowdStore\n readonly pack: CrowdPack\n readonly grid: NavGrid\n readonly doors: Doors\n readonly corpses: CorpseLedger<Corpse>\n readonly numbers: CrowdNumbers\n readonly stats: { bands: [number, number, number, number]; slots: number; kills: number; heads: number; physics: PhysicsStats }\n /** Height of the bodies piled per grid cell. */\n readonly pile: Float32Array\n /** Millimetres of lying bodies per navigation cell, recomputed every fixed step (empty with `physics.mound` off). */\n readonly mound: Uint16Array\n /** The thrown bodies of a blast, simulated here and only drawn by the renderer (empty with `physics.thrown` off). */\n readonly thrown: ThrownPool\n /** A blast: queued, and resolved at the start of the next step in one pass over the agents. Without `physics` it only hurts, kills and stuns. */\n blast(blast: CrowdBlast): void\n /** How many agents are steps of a pile now. */\n pileCount(): number\n readonly trail: { x: number; z: number; t: number }[]\n /** Global variant index of each type's variants (G 67079 `ZVAR_OF`). */\n readonly variantsOf: readonly (readonly number[])[]\n target: CrowdTarget | null\n /** The stream every draw takes (the behaviours share it, in the game's order). */\n readonly random: () => number\n /** Emits a `sound` event, as the crowd's own (for the behaviours). */\n sound(id: string, volume: number, x: number, y: number, z: number, distance: number): void\n /** Emits an `effect` event, as the crowd's own (for the behaviours). */\n effect(payload: CrowdEventMap['effect']): void\n typeIndex(id: string): number\n spawn(x: number, z: number, type?: number): number\n remove(i: number): void\n update(dt: number, context?: CrowdUpdateContext): void\n dispose(): void\n emitNoise(x: number, z: number, radius: number): void\n damage(i: number, hit: CrowdHit): CrowdDamageResult\n kill(i: number, headshot?: boolean): void\n /** The blood trail of the target, for the trackers (G 67238 `trailUpdate`). */\n updateTrail(dt: number, target?: CrowdTarget | null): void\n updateCorpses(dt: number): void\n /** Keeps the count near `target` from the edge, removes the excess out of sight and sends a migration now and then (G 67158). */\n updatePopulation(dt: number, view: CrowdView, options?: { readonly target?: number; readonly migration?: MigrationOptions | null }): void\n /** Spawns one agent `minD` to `maxD` m from the target, out of the view (G 67141 `spawnAway`). */\n spawnAway(minD: number, maxD: number, view: CrowdView): number\n /** A blast at the base of a pile undoes it (G 67227 `pileBlast`). */\n pileBlast(x: number, z: number, radius: number): void\n /** The agent hunts something else than the target (a civilian): `game-crowd` civilians set it. */\n setHunt(hunt: ((i: number, tdt: number) => boolean) | null): void\n /** The chase speed of agent `i` now (the stalker's sprint, the drowned in water; G 67343 `zChaseSpeed`). */\n chaseSpeed(i: number): number\n /** The interface `game-player` uses to push, cut and be held by the agents. */\n asPlayerCrowd(): PlayerCrowdLike\n on<K extends CrowdEventName>(event: K, listener: (event: CrowdEventMap[K]) => void): () => void\n}\n\n/** The same shape as `game-player`'s `PlayerCrowd` (structural, no dependency). */\nexport type PlayerCrowdLike = {\n count(): number\n active(i: number): boolean\n x(i: number): number\n z(i: number): number\n y(i: number): number\n floor(i: number): number\n chasing(i: number): boolean\n down(i: number): boolean\n panicWeight(i: number): number\n hold(i: number, x: number, z: number, k: number, lookX: number, lookZ: number): void\n stun(i: number, seconds: number, vx: number, vz: number): void\n chase(i: number, cooldown: number): void\n damage(i: number, hit: { region: number; amount: number; distance: number; cut: number }): void\n}\n\nconst clamp = (v: number, a: number, b: number): number => (v < a ? a : v > b ? b : v)\n// one shared empty set: the update loop reads the traits of every agent every frame\nconst NO_TRAITS: CrowdTraits = {}\nconst TAU_APPROX = 6.2832\nconst TURN = 6.28\n\n/**\n * The crowd: a literal port of the team game's horde (`jogo.js` G 66714-67430). One struct of arrays, relevance bands R0-R3 with hysteresis, perception\n * by sight, hearing and smell, the sliced flow field, the spatial hash, separation, six attack slots, chain alerts, doors, piles against walls, the\n * abilities as type traits, corpses, the blood trail and the population. Every draw on `random` is in the game's order.\n */\nexport function createCrowd(options: CrowdOptions): Crowd {\n const random = options.random ?? createRng(options.seed ?? 1)\n const R = random\n const pack = options.pack\n const types: readonly CrowdType[] = pack.types\n const N: CrowdNumbers = { ...CROWD_DEFAULTS.numbers, ...options.numbers }\n const BANDS = options.bands ?? CROWD_DEFAULTS.bands\n const HYST = N.hysteresis\n const grid = options.grid ?? createNavGrid()\n const doors = options.doors ?? createDoors(grid, { random })\n const water = options.water ?? (() => false)\n const plugins = options.plugins ?? []\n const clock = options.flow?.clock ?? (() => (typeof performance === 'undefined' ? Date.now() : performance.now()))\n const FLOW_R = options.flow?.radius ?? N.flowRadius\n const Z = createStore(options.capacity ?? N.capacity)\n const { occ, occH, cellOf, solidAt } = grid\n const HALF = grid.half\n const GC = grid.cell\n const FW = grid.n\n const physics = resolvePhysics(options.physics)\n const flowCells = options.flow?.cellsPerStep ?? (physics.deterministic ? DETERMINISTIC_FLOW_CELLS : undefined)\n const seed32 = (options.seed ?? 1) >>> 0\n let serial = 0\n const moundHeight = new Uint16Array(physics.mound ? FW * FW : 0)\n const moundOn = physics.mound !== null\n const balanceOn = physics.balance !== null\n const staggerSpeed = physics.pressure?.staggerSpeed ?? 1\n const separationMax = physics.contact?.separationMax ?? Infinity\n const RESERVED_SLOTS = 2\n const WALK_DOWN = 0.6\n // the attack pose by distance with hysteresis (in at 1.6 m, out at 2.2 m), squared: the renderer drew it, the simulation reads it\n const ATTACK_ENTER = 2.56\n const ATTACK_LEAVE = 4.84\n const HC = N.hashCell\n const HN = Math.floor(grid.size / HC)\n const hHead = new Int32Array(HN * HN).fill(-1)\n const hNext = new Int32Array(Z.capacity)\n const hashTouched = new Int32Array(Z.capacity)\n let hashTouchedN = 0\n const pile = new Float32Array(FW * FW)\n let pileN = 0\n const flow = {\n dir: new Int8Array(FW * FW * 2),\n back: new Int8Array(FW * FW * 2),\n dist: new Float32Array(FW * FW),\n q: new Int32Array((2 * FLOW_R + 1) ** 2 * 2),\n phase: 0,\n qh: 0,\n qt: 0,\n c: 0,\n t: 0,\n cell: -1,\n ms: 0,\n win: [0, 0, -1, -1],\n winB: [0, 0, -1, -1],\n }\n const catTable: number[] = []\n types.forEach((c, i) => {\n for (let k = 0; k < c.weight; k++) catTable.push(i)\n })\n const variantsOf: number[][] = []\n {\n let next = 0\n types.forEach((type) => {\n const list: number[] = []\n for (let v = 0; v < (type.variants ?? 1); v++) list.push(next++)\n variantsOf.push(list)\n })\n }\n const typeIndexById = new Map(types.map((type, index) => [type.id, index]))\n const noises: { x: number; z: number; r: number }[] = []\n const stats: Crowd['stats'] = { bands: [0, 0, 0, 0], slots: 0, kills: 0, heads: 0, physics: createPhysicsStats() }\n const corpses = createCorpseLedger<Corpse>({ max: N.corpsesMax, seconds: N.corpseSeconds })\n const trail: { x: number; z: number; t: number }[] = []\n let trailLast = { x: 1e9, z: 1e9 }\n const killQ: number[] = []\n let groanBudget = 0\n let migrateT = N.migrationFirst\n let hunt: ((i: number, tdt: number) => boolean) | null = null\n let target: CrowdTarget | null = options.target ?? null\n let nightF = 0\n let viewYaw = 0\n const listeners = new Map<CrowdEventName, Set<(event: never) => void>>()\n const emit = <K extends CrowdEventName>(event: K, payload: CrowdEventMap[K]): void => {\n const set = listeners.get(event)\n if (set) for (const listener of set) (listener as (event: CrowdEventMap[K]) => void)(payload)\n }\n const emitNoise = (x: number, z: number, r: number): void => void noises.push({ x, z, r })\n doors.on('noise', (e) => emitNoise(e.x, e.z, e.radius))\n doors.on('sound', (e) => emit('sound', { ...e, distance: target ? Math.hypot(e.position[0] - target.state.x, e.position[2] - target.state.z) : 0 }))\n doors.on('effect', (e) => emit('effect', e))\n doors.on('broken', (e) => {\n const d = doors.list[e.door]!\n if (target && Math.hypot(d.x - target.state.x, d.z - target.state.z) < N.doorMessageDistance) emit('message', { id: 'door_broken', seconds: 2 })\n })\n const sound = (id: string, volume: number, x: number, y: number, z: number, d: number): void => emit('sound', { sound: id, volume, position: [x, y, z], distance: d })\n const pick = <T extends { p: number }>(list: readonly T[]): T => {\n const r = R()\n let acc = 0\n for (const it of list) {\n acc += it.p\n if (r < acc) return it\n }\n return list[0]!\n }\n const T = (i: number): CrowdType => types[Z.type[i]!]!\n const traits = (i: number): CrowdTraits => T(i).traits ?? NO_TRAITS\n\n // ----- spawn and removal -----\n const special = (i: number): void => {\n const tr = traits(i)\n Z.y[i] = 0\n Z.floor[i] = 0\n Z.floorLocked[i] = 0\n Z.vy[i] = 0\n Z.abilityTimer[i] = 0\n Z.eaten[i] = 0\n Z.stuck[i] = 0\n Z.stepCell[i] = -1\n Z.burn[i] = 0\n Z.hunts[i] = -1\n Z.named[i] = -1\n if (tr.color !== undefined && !tr.submerged) {\n Z.color[i] = tr.color\n if (tr.skin !== undefined) Z.skin[i] = tr.skin\n }\n if (tr.ambush && R() < tr.ambush.chance) {\n Z.state[i] = STATE.DORMANT\n Z.yaw[i] = R() * TURN\n }\n if (tr.submerged) {\n Z.state[i] = STATE.DORMANT\n Z.y[i] = tr.submerged.depth\n if (tr.color !== undefined) Z.color[i] = tr.color\n }\n }\n\n const spawn = (x: number, z: number, catForce = -1): number => {\n if (Z.n >= Z.capacity) return -1\n const i = Z.n++\n const c = catForce >= 0 ? catForce : catTable[Math.floor(R() * catTable.length)]!\n const C = types[c]!\n const ai = pack.ages.indexOf(pick(pack.ages))\n const maxAge = C.traits?.maxAge\n const A = pack.ages[maxAge !== undefined ? Math.min(ai, maxAge) : ai]!\n const ri = pack.outfits.indexOf(pick(pack.outfits))\n const RO = pack.outfits[ri]!\n Z.x[i] = x\n Z.z[i] = z\n Z.vx[i] = Z.vz[i] = 0\n Z.type[i] = c\n Z.age[i] = pack.ages.indexOf(A)\n Z.outfit[i] = ri\n const g = (R() + R() + R() - 1.5) * 0.09\n Z.height[i] = clamp(1 + g, 0.9, 1.1) * (C.scale || 1)\n const bb = R()\n Z.build[i] = (bb < 0.3 ? 0.85 + R() * 0.1 : bb < 0.8 ? 0.95 + R() * 0.13 : 1.08 + R() * 0.17) * (C.build || (C.scale ? 1.3 : 1))\n const heavy = bb >= 0.8\n const thin = bb < 0.3\n Z.chaseSpeed[i] = C.speed.chase * A.speed * (thin ? 1.05 : heavy ? 0.9 : 1) * (0.92 + R() * 0.16)\n Z.wanderSpeed[i] = C.speed.wander * A.speed * (0.8 + R() * 0.4)\n Z.hpMax[i] = Z.hp[i] = C.health * A.health * (thin ? 0.9 : heavy ? 1.15 : 1)\n Z.headHp[i] = pack.segments.head\n Z.armHp[i * 2] = Z.armHp[i * 2 + 1] = pack.segments.arm\n Z.legHp[i * 2] = Z.legHp[i * 2 + 1] = pack.segments.leg\n Z.missing[i] = 0\n Z.helmet[i] = RO.helmet ? 1 : 0\n Z.crawl[i] = C.crawls ? 1 : 0\n Z.limp[i] = 0\n Z.state[i] = 0\n Z.stateTimer[i] = 0\n Z.lost[i] = 0\n Z.attackTimer[i] = R()\n Z.alertIn[i] = 0\n Z.cooldown[i] = 0\n {\n const vs = variantsOf[c]!\n Z.variant[i] = vs[Math.floor(R() * vs.length)]!\n }\n Z.yaw[i] = Z.renderYaw[i] = R() * TURN\n Z.renderSpeed[i] = 0\n Z.steering[i] = 0\n Z.pause[i] = 0\n Z.attackPose[i] = 0\n Z.arms[i] = 0\n Z.phase[i] = R() * 10\n Z.band[i] = 3\n Z.next[i] = R() * 0.05\n Z.wx[i] = x\n Z.wz[i] = z\n Z.color[i] = RO.color || pack.palette[Math.floor(R() * pack.palette.length)]!\n Z.skin[i] = R()\n seatPhysics(Z, i, { seed: seed32, serial: serial++, baseMass: physics.contact?.baseMass ?? 70, personality: physics.personality })\n special(i)\n emit('spawn', { agent: i, type: C.id })\n return i\n }\n\n const remove = (i: number): void => {\n if (Z.state[i] === STATE.PILE_STEP) {\n if (engine.active) releasePileStep(i)\n else stepRelease(i)\n }\n const j = --Z.n\n engine.remap(i, j)\n if (i === j) {\n emit('remove', { agent: i, moved: -1 })\n return\n }\n for (const k of AGENT_FIELDS) (Z[k] as { [index: number]: number })[i] = (Z[k] as { [index: number]: number })[j]!\n Z.armHp[i * 2] = Z.armHp[j * 2]!\n Z.armHp[i * 2 + 1] = Z.armHp[j * 2 + 1]!\n Z.legHp[i * 2] = Z.legHp[j * 2]!\n Z.legHp[i * 2 + 1] = Z.legHp[j * 2 + 1]!\n if (target) {\n if (target.state.grabbedBy === j) target.state.grabbedBy = i\n else if (target.state.grabbedBy === i) target.state.grabbedBy = -1\n }\n emit('remove', { agent: i, moved: j })\n }\n\n // ----- the sliced flow field (no spike) -----\n const flowStart = (x: number, z: number): void => {\n const gx = clamp(Math.floor((x + HALF) / GC), 0, FW - 1)\n const gz = clamp(Math.floor((z + HALF) / GC), 0, FW - 1)\n const W = flow.winB\n W[0] = Math.max(0, gx - FLOW_R)\n W[1] = Math.max(0, gz - FLOW_R)\n W[2] = Math.min(FW - 1, gx + FLOW_R)\n W[3] = Math.min(FW - 1, gz + FLOW_R)\n for (let r = W[1]; r <= W[3]; r++) flow.dist.fill(1e9, r * FW + W[0], r * FW + W[2] + 1)\n const s = gz * FW + gx\n flow.dist[s] = 0\n flow.q[0] = s\n flow.qh = 0\n flow.qt = 1\n flow.phase = 1\n flow.c = 0\n flow.t = 0\n }\n const flowStep = (budget: number, cellsPerStep?: number): void => {\n if (!flow.phase) return\n const t0 = clock()\n let cellsLeft = cellsPerStep ?? Infinity\n const D = flow.dist\n const Q = flow.q\n const F = flow.back\n const W = flow.winB\n let n = 0\n while (flow.phase === 1) {\n if (flow.qh >= flow.qt) {\n flow.phase = 2\n break\n }\n if (--cellsLeft < 0) return\n const c = Q[flow.qh++]!\n const cx = c % FW\n const cz = (c / FW) | 0\n const dc = D[c]!\n for (let dz = -1; dz <= 1; dz++)\n for (let dx = -1; dx <= 1; dx++) {\n if (!dx && !dz) continue\n const nx = cx + dx\n const nz = cz + dz\n if (nx < W[0]! || nz < W[1]! || nx > W[2]! || nz > W[3]!) continue\n const k = nz * FW + nx\n if (occ[k] && occ[k]! < 3) continue\n if (dx && dz && ((occ[cz * FW + nx] && occ[cz * FW + nx]! < 3) || (occ[nz * FW + cx] && occ[nz * FW + cx]! < 3))) continue\n const nd = dc + (dx && dz ? 1.414 : 1)\n if (nd < D[k]! - 1e-3) {\n D[k] = nd\n if (flow.qt < Q.length) Q[flow.qt++] = k\n }\n }\n if (cellsPerStep === undefined && (++n & 2047) === 0 && clock() - t0 > budget) {\n flow.t += clock() - t0\n return\n }\n }\n const ww = W[2]! - W[0]! + 1\n while (flow.phase === 2) {\n if (--cellsLeft < 0) return\n const j = flow.c++\n if (j >= ww * (W[3]! - W[1]! + 1)) {\n const tmp = flow.dir\n flow.dir = F\n flow.back = tmp\n const tw = flow.win\n flow.win = W\n flow.winB = tw\n flow.phase = 0\n flow.ms = flow.t + clock() - t0\n return\n }\n const cx = W[0]! + (j % ww)\n const cz = W[1]! + ((j / ww) | 0)\n const c = cz * FW + cx\n if (occ[c] && occ[c]! < 3) {\n F[c * 2] = F[c * 2 + 1] = 0\n continue\n }\n let best = D[c]!\n let bx = 0\n let bz = 0\n for (let dz = -1; dz <= 1; dz++)\n for (let dx = -1; dx <= 1; dx++) {\n const nx = cx + dx\n const nz = cz + dz\n if (nx < W[0]! || nz < W[1]! || nx > W[2]! || nz > W[3]!) continue\n const d = D[nz * FW + nx]!\n if (d < best) {\n best = d\n bx = dx\n bz = dz\n }\n }\n F[c * 2] = bx\n F[c * 2 + 1] = bz\n if (cellsPerStep === undefined && (j & 8191) === 0 && clock() - t0 > budget) {\n flow.t += clock() - t0\n return\n }\n }\n }\n const losClear = grid.losClear\n\n // ----- piles against walls -----\n const groundAt = (c: number, _y: number): number => {\n if (c < 0) return 0\n const k = occ[c]\n if (k === 2 || (k === 1 && occH[c]! <= _y + 0.05)) return occH[c]!\n return moundOn ? pile[c]! + moundHeight[c]! / 1000 : pile[c]!\n }\n const blockedAt = (i: number, x: number, z: number): boolean => {\n const c = cellOf(x, z)\n if (c < 0) return true\n const k = occ[c]\n const y = Z.y[i]!\n if (k === 0 || k === 3) return (moundOn ? pile[c]! + moundHeight[c]! / 1000 : pile[c]!) > y + 0.5 && Z.state[i] !== STATE.AIRBORNE\n if (k === 2 && y + 0.5 >= occH[c]!) return false\n if (Z.state[i] === STATE.AIRBORNE && occH[c]! <= y) return false\n return true\n }\n const tryStep = (i: number, hitCell: number): boolean => {\n if (pileN >= N.pileMax) return false\n const c = cellOf(Z.x[i]!, Z.z[i]!)\n if (c < 0 || occ[c] !== 0) return false\n const dir = hitCell - c\n const w = c + dir\n const back = c - dir\n const h = pile[c]! + N.pileStep\n if (h > N.pileTop) return false\n const cap = occ[w] === 2 ? (back >= 0 ? pile[back]! : 0) + 1.0 : pile[w]!\n if (h > cap + 1e-3) return false\n pile[c] = h\n pileN++\n Z.state[i] = STATE.PILE_STEP\n Z.stepCell[i] = c\n Z.stepDir[i] = dir\n Z.vx[i] = Z.vz[i] = 0\n Z.y[i] = h - N.pileStep\n Z.stateTimer[i] = 6 + R() * 6\n return true\n }\n function stepRelease(i: number): void {\n const c = Z.stepCell[i]!\n if (c >= 0) {\n pile[c] = Math.max(0, pile[c]! - N.pileStep)\n pileN = Math.max(0, pileN - 1)\n }\n Z.stepCell[i] = -1\n Z.state[i] = STATE.CHASE\n Z.stuck[i] = 0\n }\n const pileBlast = (x: number, z: number, r: number): void => {\n for (let i = 0; i < Z.n; i++)\n if (Z.state[i] === STATE.PILE_STEP && (Z.x[i]! - x) ** 2 + (Z.z[i]! - z) ** 2 < r * r) {\n stepRelease(i)\n Z.state[i] = STATE.STUNNED\n Z.stateTimer[i] = 1.2\n }\n }\n\n // a pile step that falls, flies or dies takes the steps behind it in its column down with it (the pile is not a wall that stays up without them)\n const pileColumns: PileColumn[] = []\n const releasePileStep = (i: number): void => {\n if (Z.stepCell[i]! >= 0) pileColumns.push({ cell: Z.stepCell[i]!, dir: Z.stepDir[i]! })\n stepRelease(i)\n }\n // ----- alerts -----\n const voice = (i: number): string => traits(i).voice ?? 'groan'\n const groan = (i: number, d: number): void => {\n // chain alert: 8 m, 0.2-0.6 s per hop\n const r2 = N.alertRadius * N.alertRadius\n for (let j = 0; j < Z.n; j++) {\n if (Z.state[j] !== 0 || Z.alertIn[j]! > 0) continue\n const ex = Z.x[j]! - Z.x[i]!\n const ez = Z.z[j]! - Z.z[i]!\n if (ex * ex + ez * ez < r2) Z.alertIn[j] = N.alertDelay + R() * N.alertDelayRandom\n }\n emit('alert', { agent: i })\n if (d < N.voiceDistance && groanBudget <= 0) {\n sound(voice(i), 1, Z.x[i]!, Z.height[i]! * 1.6, Z.z[i]!, d)\n groanBudget = N.voiceEvery\n }\n }\n const screamAt = (i: number, x: number, z: number): void => {\n const tr = traits(i).caller!\n sound('scream_hoarse', 1, x, 1.6, z, target ? Math.hypot(x - target.state.x, z - target.state.z) : 0)\n for (let j = 0; j < Z.n; j++) {\n const ex = Z.x[j]! - x\n const ez = Z.z[j]! - z\n if (ex * ex + ez * ez < tr.radius * tr.radius) {\n Z.state[j] = STATE.CHASE\n Z.lost[j] = 0\n }\n }\n if (target) target.state.screamBoost = tr.boost\n emit('message', { id: 'called', seconds: 3, type: T(i).id })\n emit('scream', { agent: i, x, z })\n }\n\n // ----- attack on the target -----\n const attack = (i: number, d: number, tdt: number): void => {\n Z.attackTimer[i]! -= tdt\n if (Z.attackTimer[i]! > 0) return\n Z.attackTimer[i] = N.attackEvery / Z.aggression[i]!\n const C = T(i)\n const tr = C.traits ?? NO_TRAITS\n if (tr.heavySwing && R() < tr.heavySwing.chance) {\n Z.state[i] = STATE.SWING\n Z.stateTimer[i] = tr.heavySwing.windUp\n return\n }\n const noJaw = Z.missing[i]! & 512\n const noArms = (Z.missing[i]! & 6) === 6\n const P = target!\n if (P.state.grabbedBy < 0 && !noArms && !Z.crawl[i] && R() < C.bite * (P.state.crouch ? N.crouchBite : 1) * Z.aggression[i]!) {\n P.grab(i, !!noJaw, !!tr.drownGrab && water(P.state.x, P.state.z))\n Z.state[i] = STATE.GRAB\n } else {\n P.hurt(C.damage * (Z.crawl[i] ? N.crawlDamage : 1), i)\n if (tr.burns && Z.abilityTimer[i]! >= 0) P.state.burnTimer = tr.burns.seconds\n }\n void d\n }\n\n // ----- damage (G 67010 zDamage) -----\n const effect = (payload: CrowdEventMap['effect']): void => emit('effect', payload)\n const blood = (x: number, y: number, z: number, age: number, amount: number, died: boolean, agent?: number): void => effect({ effect: 'blood', point: [x, y, z], age, amount, died, ...(agent === undefined ? {} : { agent }) })\n /** `draw` is the source of the jaw draw (and of the corpse seed of a death); a death waits for the end of the step when `deferDeath` is on. */\n const damageWith = (i: number, hit: CrowdHit, draw: () => number, deferDeath = false): CrowdDamageResult => {\n const C = T(i)\n const A = pack.ages[Z.age[i]!]!\n const RO = pack.outfits[Z.outfit[i]!]!\n const cal = hit.caliber ?? null\n const proj = !!cal\n const dano = hit.amount\n const dist = hit.distance\n const corte = hit.cut ?? 0\n const regiao = hit.region\n let morreu = false\n let headshot = false\n if (regiao === 0) {\n if (proj && Z.helmet[i] && cal!.class === 'pistol') {\n Z.helmet[i] = 0\n effect({ effect: 'spark', point: [Z.x[i]!, Z.height[i]! * 1.75, Z.z[i]!], agent: i })\n sound('impact', 1, Z.x[i]!, Z.height[i]! * 1.75, Z.z[i]!, dist)\n return { died: false }\n }\n const mata = proj ? (cal!.skull === 1 || dist <= cal!.skull) && dano >= N.skullMinDamage : dano * 3 >= Z.headHp[i]!\n if (mata) {\n morreu = true\n headshot = true\n if (dano > N.skullTakeDamage || (cal && N.skullTakeWithin[cal.id as keyof typeof N.skullTakeWithin] !== undefined && dist < N.skullTakeWithin[cal.id as keyof typeof N.skullTakeWithin]!)) Z.missing[i]! |= 1\n } else {\n Z.hp[i]! -= dano * 3\n Z.headHp[i]! -= dano\n if (dano > N.jawDamage && draw() < N.jawChance) Z.missing[i]! |= 512\n }\n } else if (regiao === 1) {\n let m = C.multipliers.torso\n if (RO.vest && proj) m *= cal!.class === 'pistol' ? RO.vest.pistol : RO.vest.rifle\n Z.hp[i]! -= dano * m\n } else {\n const perna = regiao >= 4\n const lado = regiao & 1\n const mm = (perna ? C.multipliers.legs : C.multipliers.arms) * A.limb * (RO.limb || 1)\n const segArr = perna ? Z.legHp : Z.armHp\n const k = i * 2 + (perna ? lado : regiao === 3 ? 1 : 0)\n segArr[k]! -= dano * mm\n Z.hp[i]! -= dano * mm * 0.5\n const max = perna ? pack.segments.leg : pack.segments.arm\n if (perna && segArr[k]! <= max * 0.5) Z.limp[i] = 1\n if (segArr[k]! <= 0) {\n const arranca = corte >= 0.5 || dano * mm > max * 0.9\n if (perna) {\n Z.crawl[i] = 1\n if (arranca) Z.missing[i]! |= lado ? 16 : 8\n else Z.missing[i]! |= lado ? 256 : 128\n } else if (arranca) Z.missing[i]! |= regiao === 3 ? 4 : 2\n else Z.missing[i]! |= regiao === 3 ? 64 : 32\n effect({ effect: 'limb', point: [Z.x[i]!, Z.height[i]! * (perna ? 0.5 : 1.2), Z.z[i]!], agent: i, region: regiao, severed: arranca })\n }\n }\n if (Z.hp[i]! <= 0) morreu = true\n blood(Z.x[i]!, regiao === 0 ? Z.height[i]! * 1.65 : regiao === 1 ? Z.height[i]! * 1.2 : Z.height[i]! * 0.7, Z.z[i]!, Z.age[i]!, regiao === 0 ? 120 : 30, morreu, i)\n if (morreu) {\n if (deferDeath) deferKill(i)\n else kill(i, headshot, draw)\n return { died: morreu, headshot }\n }\n const st = Z.state[i]\n if (dano >= N.hitStunDamage && st !== STATE.GRAB && st !== STATE.PILE_STEP && st !== STATE.AIRBORNE) {\n Z.state[i] = STATE.STUNNED\n Z.stateTimer[i] = C.traits?.hitStun ?? N.hitStun\n }\n const st2 = Z.state[i]\n if (st2 === 0 || st2 === STATE.DORMANT || st2 === STATE.EATING || st2 === STATE.SEEKING_SOUND) {\n Z.state[i] = STATE.CHASE\n Z.lost[i] = 0\n const sub = C.traits?.submerged\n if (sub) Z.y[i] = Math.max(Z.y[i]!, sub.surface)\n }\n return { died: morreu }\n }\n const damage = (i: number, hit: CrowdHit): CrowdDamageResult => damageWith(i, hit, R)\n function kill(i: number, headshot = false, draw: () => number = R): void {\n stats.kills++\n if (headshot) stats.heads++\n const tr = traits(i)\n if (tr.burst) burstAt(i, Z.x[i]!, Z.z[i]!)\n const C = T(i)\n emit('kill', { agent: i, type: C.id, headshot, x: Z.x[i]!, z: Z.z[i]! })\n if (Z.balance[i] !== BALANCE.THROWN) corpseAdd(i, draw)\n if (target && target.state.grabbedBy === i) target.release()\n remove(i)\n }\n const burstAt = (i: number, x: number, z: number): void => {\n const b = traits(i).burst!\n effect({ effect: 'cloud', point: [x, 0, z], radius: 4, color: b.cloudColor, amount: 8 })\n effect({ effect: 'blood', point: [x, 1.0, z], amount: 1500, died: true, color: b.bloodColor })\n if (target) {\n const d = Math.hypot(x - target.state.x, z - target.state.z)\n if (d < b.radius) {\n target.state.blind = b.blind\n target.state.scentTimer = b.scent\n emit('message', { id: 'burst_hit', seconds: 2.5, type: T(i).id })\n emitNoise(target.state.x, target.state.z, b.noiseNear)\n }\n }\n emitNoise(x, z, b.noise)\n }\n\n // ----- corpses -----\n const corpseOf = (i: number, seed: number): Corpse => ({\n x: Z.x[i]!,\n z: Z.z[i]!,\n yaw: Z.renderYaw[i]!,\n height: Z.height[i]!,\n build: Z.build[i]!,\n named: Z.named[i]!,\n missing: Z.missing[i]!,\n color: Z.color[i]!,\n variant: Z.variant[i]!,\n skin: Z.skin[i]!,\n seed,\n })\n const corpseAdd = (i: number, draw: () => number = R): void => void corpses.add(corpseOf(i, draw() * 10))\n\n // ----- abilities (G 67265 zAbility). true: it took care of the movement -----\n const ability = (i: number, d: number, dx: number, dz: number, tdt: number, b: number, alive: boolean): boolean => {\n const C = T(i)\n const tr = C.traits ?? NO_TRAITS\n const st = Z.state[i]\n const P = target\n // dormant: the stalker wakes under 3 m or with the target's back turned within 10 m; the drowned rises at 5 m\n if (st === STATE.DORMANT) {\n Z.vx[i] = Z.vz[i] = 0\n if (!alive || b > 1) return true\n if (tr.submerged) {\n if (d < tr.submerged.wake) {\n Z.state[i] = STATE.CHASE\n groan(i, d)\n sound('step', 2, Z.x[i]!, 0, Z.z[i]!, d)\n }\n return true\n }\n const am = tr.ambush\n const wake = am?.wake ?? N.dormantWake\n const behind = am?.wakeBehind ?? N.dormantWakeBehind\n const fx = -Math.sin(viewYaw)\n const fz = -Math.cos(viewYaw)\n const costas = (dx * fx + dz * fz) / (d || 1) < (am?.behindCos ?? -0.3)\n if (d < wake || (costas && d < behind)) {\n Z.state[i] = STATE.CHASE\n Z.abilityTimer[i] = am?.sprintSeconds ?? 3\n Z.lost[i] = 0\n sound('runner_groan', 1, Z.x[i]!, 1.6, Z.z[i]!, d)\n }\n return true\n }\n if (st === STATE.AIRBORNE || st === STATE.PILE_STEP) return true\n if (st === STATE.SWELLING) {\n Z.vx[i] = Z.vz[i] = 0\n return true\n }\n if (st === STATE.SWING) {\n Z.vx[i] = Z.vz[i] = 0\n Z.stateTimer[i]! -= tdt\n if (Z.stateTimer[i]! <= 0) {\n const hs = tr.heavySwing!\n Z.state[i] = STATE.CHASE\n Z.attackTimer[i] = 2\n sound('impact', 1.4, Z.x[i]!, 1, Z.z[i]!, d)\n if (d < hs.range && alive && P) {\n P.hurt(C.damage, i)\n P.knock(hs.knock)\n emit('message', { id: 'swing_knock', seconds: 1.5, type: C.id })\n }\n }\n return true\n }\n if (st === STATE.EATING) {\n const ec = tr.eatsCorpses!\n Z.vx[i] = Z.vz[i] = 0\n Z.stateTimer[i]! -= tdt\n if (alive && d < ec.defend) {\n Z.state[i] = STATE.CHASE\n Z.lost[i] = 0\n groan(i, d)\n return false\n } // defends the food\n if (Z.stateTimer[i]! <= 0) {\n let best = -1\n let bd = ec.grabRange\n const list = corpses.entries\n for (let k = 0; k < list.length; k++) {\n const e = Math.hypot(list[k]!.data.x - Z.x[i]!, list[k]!.data.z - Z.z[i]!)\n if (e < bd) {\n bd = e\n best = k\n }\n }\n if (best >= 0) {\n corpses.remove(best)\n Z.hp[i]! += ec.health\n Z.hpMax[i]! += ec.health\n Z.height[i]! *= ec.grow\n Z.build[i]! *= ec.grow\n Z.eaten[i]!++\n }\n Z.state[i] = 0\n Z.abilityTimer[i] = ec.rest\n }\n return true\n }\n if (st === STATE.SEEKING_SOUND) {\n const ss = tr.soundSeeker ?? { arrive: 1, loseChase: 3, hitRadius2: 1.1 }\n if (alive && d < 2.5) {\n Z.state[i] = STATE.CHASE\n return false\n }\n const wx = Z.wx[i]! - Z.x[i]!\n const wz = Z.wz[i]! - Z.z[i]!\n const wl = Math.hypot(wx, wz)\n if (wl < ss.arrive) {\n Z.state[i] = 0\n return false\n }\n Z.vx[i] = (wx / wl) * Z.chaseSpeed[i]!\n Z.vz[i] = (wz / wl) * Z.chaseSpeed[i]!\n Z.yaw[i] = Math.atan2(wx, wz)\n // hits whatever it meets on the way, other agents too\n if (b === 0) {\n Z.attackTimer[i]! -= tdt\n if (Z.attackTimer[i]! <= 0) {\n const hx = Math.floor(Z.x[i]! + HALF)\n const hz = Math.floor(Z.z[i]! + HALF)\n if (hx >= 0 && hz >= 0 && hx < HN && hz < HN)\n for (let j = hHead[hz * HN + hx]!; j !== -1; j = hNext[j]!) {\n if (j !== i && (Z.x[j]! - Z.x[i]!) ** 2 + (Z.z[j]! - Z.z[i]!) ** 2 < ss.hitRadius2) {\n Z.attackTimer[i] = N.attackEvery\n hurtOther(j, C.damage)\n break\n }\n }\n }\n }\n return true\n }\n // custom moves (none in the game): after the timed states, before the moves on the target, with or without a target\n for (const plugin of plugins) if (plugin.ability?.(crowd, i, { d, dx, dz, tdt, band: b, alive })) return true\n if (!alive) return false\n if (Z.hunts[i]! >= 0 && st <= 1 && hunt) return hunt(i, tdt)\n // the blind one does not follow the target across the map: it only goes where it heard\n if (tr.soundSeeker && st === STATE.CHASE && d > tr.soundSeeker.loseChase) {\n Z.state[i] = STATE.SEEKING_SOUND\n Z.wx[i] = P!.state.x\n Z.wz[i] = P!.state.z\n return false\n }\n // pounce up to 6 m, 5 s cooldown\n const po = tr.pounce\n if (po && st === STATE.CHASE && Z.cooldown[i]! <= 0 && d > po.min && d < po.max && b === 0 && losClear(Z.x[i]!, Z.z[i]!, P!.state.x, P!.state.z)) {\n const sp = Math.min(po.speedCap, d / po.time)\n Z.state[i] = STATE.AIRBORNE\n Z.vy[i] = P!.state.y > po.highAbove ? po.liftHigh : po.lift\n Z.vx[i] = (-dx / d) * sp\n Z.vz[i] = (-dz / d) * sp\n Z.yaw[i] = Math.atan2(-dx, -dz)\n Z.cooldown[i] = po.cooldown\n Z.abilityTimer[i] = 1\n sound('runner_groan', 1, Z.x[i]!, 1.6, Z.z[i]!, d)\n return true\n }\n // the bloater swells 0.5 s and bursts when close\n if (tr.burst && st === STATE.CHASE && d < tr.burst.range) {\n Z.state[i] = STATE.SWELLING\n Z.stateTimer[i] = tr.burst.swell\n sound('flesh', 1.2, Z.x[i]!, 1, Z.z[i]!, d)\n return true\n }\n // the devourer looks for corpses (up to 3)\n const ec = tr.eatsCorpses\n if (ec && st === 0 && Z.eaten[i]! < ec.max && Z.abilityTimer[i]! <= 0 && corpses.count) {\n let best = -1\n let bd = ec.search\n const list = corpses.entries\n for (let k = 0; k < list.length; k++) {\n const e = Math.hypot(list[k]!.data.x - Z.x[i]!, list[k]!.data.z - Z.z[i]!)\n if (e < bd) {\n bd = e\n best = k\n }\n }\n if (best >= 0) {\n if (bd < ec.reach) {\n Z.state[i] = STATE.EATING\n Z.stateTimer[i] = ec.seconds\n Z.vx[i] = Z.vz[i] = 0\n return true\n }\n const cp = list[best]!.data\n const ex = cp.x - Z.x[i]!\n const ez = cp.z - Z.z[i]!\n const sp = Z.wanderSpeed[i]! * ec.approach\n Z.vx[i] = (ex / bd) * sp\n Z.vz[i] = (ez / bd) * sp\n Z.yaw[i] = Math.atan2(ex, ez)\n Z.wx[i] = cp.x\n Z.wz[i] = cp.z\n return true\n } else Z.abilityTimer[i] = ec.retry\n }\n // the tracker follows the blood trail and leads up to 8 of its kind\n const tk = tr.tracker\n if (tk && st === 0 && trail.length) {\n let best: { x: number; z: number } | null = null\n for (let k = trail.length - 1; k >= 0; k--) {\n const p = trail[k]!\n if ((p.x - Z.x[i]!) ** 2 + (p.z - Z.z[i]!) ** 2 < tk.range * tk.range) {\n best = p\n break\n }\n }\n if (best) {\n Z.wx[i] = best.x\n Z.wz[i] = best.z\n let led = 0\n const leads = typeIndexById.get(tk.leads) ?? -1\n for (let j = 0; j < Z.n && led < tk.max; j++)\n if (Z.type[j] === leads && Z.state[j] === 0 && (Z.x[j]! - Z.x[i]!) ** 2 + (Z.z[j]! - Z.z[i]!) ** 2 < tk.leadRadius * tk.leadRadius) {\n Z.wx[j] = Z.x[i]!\n Z.wz[j] = Z.z[i]!\n led++\n }\n }\n }\n return false\n }\n const chaseSpeed = (i: number): number => {\n const tr = traits(i)\n if (tr.ambush) return Z.abilityTimer[i]! > 0 ? tr.ambush.sprint : tr.ambush.slow\n if (tr.submerged && water(Z.x[i]!, Z.z[i]!)) return tr.submerged.swim\n return Z.chaseSpeed[i]!\n }\n /** The shared stream with the physics off (the game's order); a counter-based draw of the agent with it on. */\n const physicsDraw = (i: number, salt: number): (() => number) => (engine.active ? () => unit(Z.identity[i]!, engine.ctx.step, salt) : R)\n const hurtOther = (j: number, amount: number): void => {\n Z.hp[j]! -= amount\n blood(Z.x[j]!, Z.height[j]! * 1.2, Z.z[j]!, Z.age[j]!, 30, false, j)\n if (Z.hp[j]! <= 0) deferKill(j)\n }\n const deferKill = (j: number): void => {\n if (!killQ.includes(j)) killQ.push(j)\n }\n const killQFlush = (): void => {\n if (!killQ.length) return\n killQ.sort((a, b) => b - a)\n for (const j of killQ) if (j < Z.n) kill(j, false, physicsDraw(j, SALT.CORPSE))\n killQ.length = 0\n }\n\n // ----- per frame: height (pile, wall, jump), timed states (G 67377 zFrame) -----\n const frame = (i: number, dt: number, d: number, alive: boolean): void => {\n const st = Z.state[i]\n const tr = traits(i)\n if (st === STATE.PILE_STEP) {\n // a step: lets go when the target is gone\n Z.stateTimer[i]! -= dt\n if (!alive || d > N.pileReleaseDistance || Z.stateTimer[i]! <= 0) stepRelease(i)\n return\n }\n if (st === STATE.SWELLING) {\n Z.stateTimer[i]! -= dt\n if (Z.stateTimer[i]! <= 0) deferKill(i)\n return\n }\n if (tr.ambush && Z.abilityTimer[i]! > 0) Z.abilityTimer[i]! -= dt\n if (tr.eatsCorpses && Z.abilityTimer[i]! > 0 && st === 0) Z.abilityTimer[i]! -= dt\n if (tr.waterWeak && Z.abilityTimer[i] === 0 && water(Z.x[i]!, Z.z[i]!)) {\n Z.abilityTimer[i] = -1\n Z.hp[i]! *= tr.waterWeak.keep\n effect({ effect: 'cloud', point: [Z.x[i]!, 0, Z.z[i]!], radius: 1.5, color: tr.waterWeak.cloud, amount: 3, agent: i })\n }\n if (engine.active && (Z.balance[i] === BALANCE.FALLEN || Z.balance[i] === BALANCE.GETTING_UP)) return\n const c = cellOf(Z.x[i]!, Z.z[i]!)\n const gy = groundAt(c, Z.y[i]!)\n if (st === STATE.DORMANT || Z.floorLocked[i]) return\n if (engine.active && st !== STATE.AIRBORNE && Z.vy[i] === 0 && Z.y[i]! > gy + 0.02 && Z.y[i]! - gy <= WALK_DOWN) {\n // down a step of a mound is a walk, not a fall: only a drop above this height (a base that goes) is a fall\n Z.y[i] = Math.max(gy, Z.y[i]! - N.climbSpeed * dt)\n return\n }\n if (st === STATE.AIRBORNE || Z.y[i]! > gy + 0.02 || Z.vy[i]! > 0) {\n Z.vy[i]! -= N.gravity * dt\n Z.y[i]! += Z.vy[i]! * dt\n if (Z.y[i]! <= gy) {\n const fall = -Z.vy[i]!\n Z.y[i] = gy\n Z.vy[i] = 0\n const collapsed = st === STATE.AIRBORNE && Z.stateTimer[i] === COLLAPSE_MARK\n if (collapsed) Z.stateTimer[i] = 0\n if (engine.active && balanceOn && (st !== STATE.AIRBORNE || collapsed)) engine.balance!.landed(i, (fall * fall) / (2 * N.gravity))\n if (st === STATE.AIRBORNE) {\n Z.state[i] = STATE.CHASE\n if (tr.pounce && alive && d < tr.pounce.landRange && target) {\n target.knock(tr.pounce.knock)\n target.hurt(T(i).damage, i)\n emit('message', { id: 'pounce_knock', seconds: 1.5, type: T(i).id })\n }\n } else if (fall > N.fallStun && Z.state[i]! <= 1) {\n Z.state[i] = STATE.STUNNED\n Z.stateTimer[i] = 0.9\n }\n }\n } else if (gy > Z.y[i]!) Z.y[i] = Math.min(gy, Z.y[i]! + N.climbSpeed * dt)\n }\n\n // ----- the horde update (G 66835 hordeUpdate) -----\n const update = (dt: number, context: CrowdUpdateContext = {}): void => {\n if (context.target !== undefined) target = context.target\n groanBudget -= dt\n nightF = context.nightFactor ?? 0\n const P = target\n const px = P ? P.state.x : (context.flowTarget?.[0] ?? 0)\n const pz = P ? P.state.z : (context.flowTarget?.[1] ?? 0)\n const alive = !!P && P.state.alive && !P.state.turned\n viewYaw = context.viewYaw ?? (P ? P.state.yaw - Math.PI : 0)\n const bandX = context.bandCentre?.[0] ?? px\n const bandZ = context.bandCentre?.[1] ?? pz\n engine.beginStep(dt, { x: bandX, z: bandZ })\n const fx0 = context.flowTarget?.[0] ?? px\n const fz0 = context.flowTarget?.[1] ?? pz\n const pc = cellOf(fx0, fz0)\n if (flow.cell < 0) {\n flowStart(fx0, fz0)\n flowStep(1e9)\n flow.cell = pc\n } else if (pc !== flow.cell && !flow.phase) {\n flowStart(fx0, fz0)\n flow.cell = pc\n }\n flowStep(N.flowBudgetMs, flowCells)\n // spatial hash for R0\n if (engine.active) {\n for (let k = 0; k < hashTouchedN; k++) hHead[hashTouched[k]!] = -1\n hashTouchedN = 0\n } else hHead.fill(-1)\n for (let i = 0; i < Z.n; i++) {\n if (Z.band[i] !== 0 || (engine.active && Z.balance[i] === BALANCE.THROWN)) continue\n const hx = Math.floor(Z.x[i]! + HALF)\n const hz = Math.floor(Z.z[i]! + HALF)\n if (hx < 0 || hz < 0 || hx >= HN || hz >= HN) continue\n const h = hz * HN + hx\n if (engine.active && hHead[h] === -1) hashTouched[hashTouchedN++] = h\n hNext[i] = hHead[h]!\n hHead[h] = i\n }\n const B = stats.bands\n B[0] = B[1] = B[2] = B[3] = 0\n let slots = 0\n const F = flow.dir\n for (let i = 0; i < Z.n; i++) {\n const dx = Z.x[i]! - px\n const dz = Z.z[i]! - pz\n const d = Math.sqrt(dx * dx + dz * dz)\n const bandD = context.bandCentre ? Math.hypot(Z.x[i]! - bandX, Z.z[i]! - bandZ) : d\n let b = Z.band[i]!\n while (b > 0 && bandD < BANDS[b - 1]!.max - HYST) b--\n while (b < 3 && bandD > BANDS[b]!.max + HYST) b++\n Z.band[i] = b\n B[b as 0 | 1 | 2 | 3]++\n if (engine.active && Z.balance[i] === BALANCE.THROWN) continue\n const grounded = engine.active && balanceOn && (Z.balance[i] === BALANCE.FALLEN || Z.balance[i] === BALANCE.GETTING_UP)\n const C = T(i)\n const tr = C.traits ?? NO_TRAITS\n if (Z.state[i] === STATE.STUNNED) {\n Z.stateTimer[i]! -= dt\n if (Z.stateTimer[i]! <= 0) Z.state[i] = STATE.CHASE\n Z.vx[i]! *= 0.9\n Z.vz[i]! *= 0.9\n }\n if (Z.state[i] === STATE.CALLING) {\n Z.stateTimer[i]! -= dt\n Z.vx[i] = Z.vz[i] = 0\n if (Z.stateTimer[i]! <= 0) {\n Z.state[i] = STATE.CHASE\n screamAt(i, Z.x[i]!, Z.z[i]!)\n Z.cooldown[i] = tr.caller!.cooldown\n }\n }\n Z.cooldown[i]! -= dt\n if (Z.alertIn[i]! > 0) {\n Z.alertIn[i]! -= dt\n if (Z.alertIn[i]! <= 0 && Z.state[i] === 0 && !tr.soundSeeker) {\n Z.state[i] = STATE.CHASE\n Z.lost[i] = 0\n }\n }\n if (!grounded) Z.next[i]! -= dt\n else {\n Z.vx[i] = Z.vz[i] = Z.dvx[i] = Z.dvz[i] = Z.aimX[i] = Z.aimZ[i] = 0\n Z.steering[i] = 0\n }\n if (Z.next[i]! <= 0 && !grounded) {\n const tdt = BANDS[b]!.dt\n Z.next[i]! += tdt\n // perception (R0 and R1 only)\n if (alive && b <= 1 && Z.state[i]! <= 1) {\n let sees = false\n const A = pack.ages[Z.age[i]!]!\n const vis = C.perception.sight * (1 - N.nightSight * nightF) * (P!.state.crouch ? N.crouchSight : 1) * A.sight\n if (d < vis) {\n const fx = Math.sin(Z.yaw[i]!)\n const fz = Math.cos(Z.yaw[i]!)\n const cone = (-dx * fx - dz * fz) / (d || 1)\n if ((cone > N.sightCone || d < N.sightNear) && (Z.floor[i] === P!.state.floor || d < 5) && (Z.floor[i] || P!.state.floor || losClear(Z.x[i]!, Z.z[i]!, px, pz))) sees = true\n }\n if (C.perception.smell && d < C.perception.smell * (P!.state.bleeding || P!.state.scentTimer > 0 ? 2 : 1)) sees = true // smell\n if (sees) {\n if (Z.state[i] === 0) groan(i, d)\n Z.state[i] = STATE.CHASE\n Z.lost[i] = 0\n } else if (Z.state[i] === STATE.CHASE) {\n Z.lost[i]! += tdt\n if (Z.lost[i]! > N.loseAfter) {\n Z.state[i] = 0\n Z.wx[i] = px\n Z.wz[i] = pz\n }\n }\n }\n for (let k = 0; k < noises.length && (Z.state[i] === 0 || Z.state[i] === STATE.SEEKING_SOUND); k++) {\n const Nz = noises[k]!\n const hear = Nz.r * (C.perception.hearing / N.hearingBase) * pack.ages[Z.age[i]!]!.hearing\n if ((Z.x[i]! - Nz.x) ** 2 + (Z.z[i]! - Nz.z) ** 2 < hear * hear) {\n if (tr.soundSeeker) {\n Z.state[i] = STATE.SEEKING_SOUND\n Z.wx[i] = Nz.x\n Z.wz[i] = Nz.z\n break\n } // straight to the source\n Z.state[i] = STATE.CHASE\n Z.lost[i] = N.heardLost\n groan(i, d)\n }\n }\n // the caller winds up 1.5 s and calls the crowd within 150 m\n if (tr.caller && Z.state[i] === STATE.CHASE && d < tr.caller.range && Z.cooldown[i]! <= 0) {\n Z.state[i] = STATE.CALLING\n Z.stateTimer[i] = tr.caller.windUp\n }\n Z.steering[i] = 0\n Z.aimX[i] = Z.aimZ[i] = 0\n if (!ability(i, d, dx, dz, tdt, b, alive)) {\n // where it wants to go\n let tx = 0\n let tz = 0\n let sp = 0\n if (Z.state[i] === STATE.CHASE || Z.state[i] === STATE.GRAB) {\n const c = cellOf(Z.x[i]!, Z.z[i]!)\n const sameL = !P || Z.floor[i] === P.state.floor\n if (d < N.directRange && alive && sameL) {\n tx = -dx / (d || 1)\n tz = -dz / (d || 1)\n } else if (c >= 0 && b === 0 && d < N.pyramidRange && sameL && flow.dist[c]! < 1e8 && flow.dist[c]! * GC > d * 2 + 6 && losClear(Z.x[i]!, Z.z[i]!, px, pz)) {\n tx = -dx / d\n tz = -dz / d\n } // the path goes round: straight against the wall (pyramid)\n else if (c >= 0) {\n const W = flow.win\n const cx = c % FW\n const cz = (c / FW) | 0\n if (cx >= W[0]! && cx <= W[2]! && cz >= W[1]! && cz <= W[3]!) {\n tx = F[c * 2]!\n tz = F[c * 2 + 1]!\n } else {\n tx = -dx\n tz = -dz\n } // outside the window: straight at the target\n const l = Math.hypot(tx, tz) || 1\n tx /= l\n tz /= l\n }\n if (tr.caller && Z.cooldown[i]! > tr.caller.fleeAbove && d < tr.caller.fleeRange && alive) {\n tx = dx / (d || 1)\n tz = dz / (d || 1)\n } // runs from the fight after calling\n sp = chaseSpeed(i) * (P && P.state.screamBoost > 0 ? N.screamSpeed : 1)\n if (!alive) sp = Z.wanderSpeed[i]!\n if (d < N.attackRange && alive && Math.abs(P!.state.y - Z.y[i]!) < N.attackHeight) {\n sp = 0\n if (slots < N.attackSlots - (Z.aggression[i]! < 1 ? RESERVED_SLOTS : 0)) {\n slots++\n attack(i, d, tdt)\n }\n }\n } else if (Z.state[i] === 0) {\n const wx = Z.wx[i]! - Z.x[i]!\n const wz = Z.wz[i]! - Z.z[i]!\n const wl = Math.hypot(wx, wz)\n if (wl < 1 || R() < 0.01 * tdt * 60) {\n Z.wx[i] = clamp(Z.x[i]! + (R() - 0.5) * N.wanderSpan, -HALF + 2, HALF - 2)\n Z.wz[i] = clamp(Z.z[i]! + (R() - 0.5) * N.wanderSpan, -HALF + 2, HALF - 2)\n } else {\n tx = wx / wl\n tz = wz / wl\n Z.pause[i]! -= tdt\n if (Z.pause[i]! < -4 - R() * 6) Z.pause[i] = 1 + R() * 2.5\n sp = Z.pause[i]! > 0 ? 0 : Z.wanderSpeed[i]!\n } // pauses of 1 to 3.5 s between walks\n }\n if (Z.crawl[i]) sp = Math.min(sp, N.crawlSpeed)\n else if (Z.limp[i]) sp *= N.limpSpeed\n if (Z.balance[i] === BALANCE.STAGGERING) sp *= staggerSpeed\n // separation from the neighbours\n let sx = 0\n let sz = 0\n if (b === 0) {\n const hx = Math.floor(Z.x[i]! + HALF)\n const hz = Math.floor(Z.z[i]! + HALF)\n for (let oz = -1; oz <= 1; oz++)\n for (let ox = -1; ox <= 1; ox++) {\n const qx = hx + ox\n const qz = hz + oz\n if (qx < 0 || qz < 0 || qx >= HN || qz >= HN) continue\n for (let j = hHead[qz * HN + qx]!; j !== -1; j = hNext[j]!) {\n if (j === i || Z.state[j] === STATE.PILE_STEP || Z.state[j] === STATE.DORMANT || (balanceOn && (Z.balance[j] === BALANCE.FALLEN || Z.balance[j] === BALANCE.GETTING_UP))) continue\n const ex = Z.x[i]! - Z.x[j]!\n const ez = Z.z[i]! - Z.z[j]!\n const e2 = ex * ex + ez * ez\n const rr = N.separation * (Z.build[i]! + Z.build[j]!)\n if (e2 < rr * rr * 1.6 && e2 > 1e-6) {\n const e = Math.sqrt(e2)\n const w = (rr * 1.26 - e) / e\n sx += ex * w\n sz += ez * w\n }\n }\n }\n }\n if (Z.state[i]! <= 2) {\n if (separationMax !== Infinity) {\n const push = Math.hypot(sx, sz) * N.separationPush\n if (push > separationMax) {\n sx *= separationMax / push\n sz *= separationMax / push\n }\n }\n if (engine.active && sp > 0.05) {\n Z.aimX[i] = tx\n Z.aimZ[i] = tz\n }\n const slow = engine.densitySlowdown(i, b)\n Z.dvx[i] = (tx * sp + sx * N.separationPush) * slow\n Z.dvz[i] = (tz * sp + sz * N.separationPush) * slow\n Z.steering[i] = 1\n if (sp > 0.05) Z.yaw[i] = Math.atan2(tx, tz)\n else if (d < 3 && alive) Z.yaw[i] = Math.atan2(-dx, -dz)\n }\n }\n }\n // integrates every frame (the wanted speed arrives in ~0.15 s, no jerk)\n if (Z.steering[i] && Z.state[i]! <= 2) {\n const k = 1 - Math.exp(-dt * (b === 0 ? 9 : 5))\n Z.vx[i]! += (Z.dvx[i]! - Z.vx[i]!) * k\n Z.vz[i]! += (Z.dvz[i]! - Z.vz[i]!) * k\n }\n const nx = Z.x[i]! + (Z.vx[i]! + Z.pushX[i]!) * dt\n const nz = Z.z[i]! + (Z.vz[i]! + Z.pushZ[i]!) * dt\n let hitCell = -1\n if (!blockedAt(i, nx, Z.z[i]!)) Z.x[i] = nx\n else {\n if (Z.state[i] !== STATE.AIRBORNE) Z.vx[i] = Z.pushX[i] = 0\n hitCell = cellOf(nx, Z.z[i]!)\n }\n if (!blockedAt(i, Z.x[i]!, nz)) Z.z[i] = nz\n else {\n if (Z.state[i] !== STATE.AIRBORNE) Z.vz[i] = Z.pushZ[i] = 0\n hitCell = cellOf(Z.x[i]!, nz)\n }\n if (hitCell >= 0 && Z.state[i] === STATE.CHASE && b === 0) {\n // runners and leapers jump a low obstacle (car, yard wall)\n const vl = tr.vaultsLow\n if (vl && occ[hitCell] !== 5 && occH[hitCell]! <= vl.maxHeight && occH[hitCell]! > Z.y[i]! + vl.minRise) {\n Z.state[i] = STATE.AIRBORNE\n Z.vy[i] = Math.sqrt(2 * N.gravity * (occH[hitCell]! - Z.y[i]! + vl.clearance))\n const s2 = Z.chaseSpeed[i]! * vl.speedFactor\n Z.vx[i] = Math.sin(Z.yaw[i]!) * s2\n Z.vz[i] = Math.cos(Z.yaw[i]!) * s2\n } else if (occ[hitCell] === 2 || (occ[hitCell] === 0 && pile[hitCell]! > 0)) {\n // stuck on the wall or the pile: becomes a step of the pyramid\n Z.stuck[i]! += engine.active && balanceOn ? dt * (1 + engine.pressureRatio(i)) : dt\n if (Z.stuck[i]! > 0.35 + 0.5 * Z.skin[i]!) {\n Z.stuck[i] = 0\n tryStep(i, hitCell)\n }\n }\n } else if (Z.stuck[i]! > 0) Z.stuck[i]! -= dt\n // a closed door in the way: hits it (10 per blow every 1.5 s; brute x3)\n if (hitCell >= 0 && Z.state[i] === STATE.CHASE && doors.at(hitCell) >= 0) {\n Z.attackTimer[i]! -= dt\n if (Z.attackTimer[i]! <= 0) {\n Z.attackTimer[i] = N.attackEvery\n doors.hit(doors.at(hitCell), N.doorDamage * (tr.doorDamage ?? 1))\n }\n }\n frame(i, dt, d, alive)\n const spd = Math.hypot(Z.vx[i]!, Z.vz[i]!)\n Z.renderSpeed[i]! += (spd - Z.renderSpeed[i]!) * Math.min(1, dt * 6)\n // arms: up when it perceives someone; down against a wall so the hand does not go through\n if (Z.band[i]! <= 1) {\n const s = Z.state[i]\n let goal = s === STATE.CHASE || s === STATE.GRAB || s === STATE.PILE_STEP ? 1 : 0\n if (goal && solidAt(Z.x[i]! + Math.sin(Z.renderYaw[i]!) * 0.62, Z.z[i]! + Math.cos(Z.renderYaw[i]!) * 0.62, 1.0)) goal = 0.1\n Z.arms[i]! += (goal - Z.arms[i]!) * Math.min(1, dt * (goal > Z.arms[i]! ? 3 : 5))\n }\n Z.phase[i]! += dt * (Z.renderSpeed[i]! < 0.05 ? 0.15 : N.cadenceBase + N.cadencePerSpeed * Z.renderSpeed[i]!) * Z.cadenceFactor[i]!\n if (physics.deterministic) {\n const attacking = Z.state[i] === STATE.CHASE && alive\n Z.attackPose[i] = attacking && (Z.x[i]! - P!.state.x) ** 2 + (Z.z[i]! - P!.state.z) ** 2 < (Z.attackPose[i] ? ATTACK_LEAVE : ATTACK_ENTER) ? 1 : 0\n }\n // walking without attacking: the body points where it goes\n if (spd > 0.35 && Z.state[i]! <= 1 && !Z.attackPose[i]) Z.yaw[i] = Math.atan2(Z.vx[i]!, Z.vz[i]!)\n let dy = Z.yaw[i]! - Z.renderYaw[i]!\n dy -= Math.round(dy / TAU_APPROX) * TAU_APPROX\n Z.renderYaw[i]! += dy * Math.min(1, dt * (Z.state[i] === STATE.AIRBORNE ? 20 : 9))\n }\n engine.endStep()\n if (pileColumns.length) collapsePileColumns(Z, pileColumns, FW * FW, stepRelease)\n killQFlush()\n noises.length = 0\n stats.slots = slots\n }\n\n // ----- trail, corpses, population -----\n const updateTrail = (dt: number, who: CrowdTarget | null = target): void => {\n for (const p of trail) p.t += dt\n while (trail.length && trail[0]!.t > N.trailSeconds) trail.shift()\n if (!who) return\n const s = who.state\n if ((s.bleeding || s.scentTimer > 0) && s.alive && Math.hypot(s.x - trailLast.x, s.z - trailLast.z) > N.trailStep) {\n trail.push({ x: s.x, z: s.z, t: 0 })\n trailLast = { x: s.x, z: s.z }\n if (trail.length > N.trailMax) trail.shift()\n }\n }\n const spawnAway = (minD: number, maxD: number, view: CrowdView): number => {\n const ox = target ? target.state.x : view.x\n const oz = target ? target.state.z : view.z\n for (let t = 0; t < 30; t++) {\n const a = R() * TURN\n const r = minD + Math.sqrt(R()) * (maxD - minD) // denser away from home\n const x = ox + Math.cos(a) * r\n const z = oz + Math.sin(a) * r\n // never born in front of the camera inside the fog\n const vx = x - view.x\n const vz = z - view.z\n const vd = Math.hypot(vx, vz)\n if (vd < view.far && vx * -Math.sin(view.yaw) + vz * -Math.cos(view.yaw) > vd * 0.5) continue\n if (Math.abs(x) < HALF - 3 && Math.abs(z) < HALF - 3 && !grid.blocked(x, z)) return spawn(x, z)\n }\n return -1\n }\n const updatePopulation = (dt: number, view: CrowdView, opts: { readonly target?: number; readonly migration?: MigrationOptions | null } = {}): void => {\n const popTarget = opts.target ?? N.population\n let k = 0\n while (Z.n < popTarget && k++ < 20) spawnAway(N.refillMin, N.refillMax, view)\n // the excess fades away slowly, only far and out of sight\n if (Z.n > popTarget + 120)\n for (let t = 0, rm = 0; t < 40 && rm < 4; t++) {\n const i = Math.floor(R() * Z.n)\n if (Z.band[i]! < 2 || Z.named[i]! >= 0 || (target && i === target.state.grabbedBy)) continue\n const vx = Z.x[i]! - view.x\n const vz = Z.z[i]! - view.z\n const vd = Math.hypot(vx, vz)\n if (vd < view.far && vx * -Math.sin(view.yaw) + vz * -Math.cos(view.yaw) > -vd * 0.35) continue\n remove(i)\n rm++\n }\n const mg = opts.migration === undefined ? null : opts.migration\n if (!mg) return\n migrateT -= dt\n if (migrateT <= 0) {\n migrateT = mg.every\n let m = 0\n for (let q = 0; q < mg.count && Z.n < Z.capacity; q++) {\n const i = spawn(mg.x + (R() - 0.5) * mg.width, mg.z + R() * mg.depth)\n if (i >= 0) {\n Z.wx[i] = mg.goal[0]\n Z.wz[i] = mg.goal[1]\n m++\n }\n }\n if (m) emit('message', { id: 'migration', seconds: 4 })\n }\n }\n\n const engine = createPhysics(\n {\n store: Z,\n grid,\n doors,\n gravity: N.gravity,\n attackEvery: N.attackEvery,\n types,\n moundHeight,\n corpsesMax: N.corpsesMax,\n typeOf: T,\n target: () => target,\n emit,\n damage: (i, hit, draw) => damageWith(i, hit, draw, true),\n deferKill,\n releasePileStep,\n corpseOf,\n corpseList: () => corpses.entries,\n addCorpse: (corpse) => void corpses.add(corpse),\n groundAt,\n pileBlast,\n },\n physics,\n stats.physics,\n )\n\n const crowd: Crowd = {\n store: Z,\n pack,\n grid,\n doors,\n corpses,\n numbers: N,\n stats,\n pile,\n mound: moundHeight,\n thrown: engine.thrown,\n blast: (blast) => engine.blasts.enqueue(blast),\n pileCount: () => pileN,\n trail,\n variantsOf,\n get target() {\n return target\n },\n set target(next) {\n target = next\n },\n random,\n sound,\n effect,\n typeIndex: (id) => typeIndexById.get(id) ?? -1,\n spawn: (x, z, type = -1) => spawn(x, z, type),\n remove,\n update,\n dispose: () => engine.dispose(),\n emitNoise,\n damage,\n kill: (i, headshot = false) => kill(i, headshot),\n updateTrail,\n updateCorpses: (dt) => void corpses.update(dt),\n updatePopulation,\n spawnAway,\n pileBlast,\n setHunt(next) {\n hunt = next\n },\n chaseSpeed,\n asPlayerCrowd: () => ({\n count: () => Z.n,\n active: (i) => Z.band[i] === 0,\n x: (i) => Z.x[i]!,\n z: (i) => Z.z[i]!,\n y: (i) => Z.y[i]!,\n floor: (i) => Z.floor[i]!,\n chasing: (i) => Z.state[i] === STATE.CHASE,\n down: (i) => Z.crawl[i] !== 0 || Z.state[i] === STATE.STUNNED,\n panicWeight: (i) => traits(i).panicWeight ?? 1,\n hold(i, x, z, k, lookX, lookZ) {\n Z.x[i]! += (x - Z.x[i]!) * k\n Z.z[i]! += (z - Z.z[i]!) * k\n Z.vx[i] = Z.vz[i] = Z.dvx[i] = Z.dvz[i] = 0\n Z.yaw[i] = Math.atan2(lookX - Z.x[i]!, lookZ - Z.z[i]!)\n },\n stun(i, seconds, vx, vz) {\n Z.state[i] = STATE.STUNNED\n Z.stateTimer[i] = seconds\n Z.vx[i] = vx\n Z.vz[i] = vz\n },\n chase(i, cooldown) {\n Z.state[i] = STATE.CHASE\n Z.attackTimer[i] = cooldown\n },\n damage: (i, hit) => void damage(i, { region: hit.region as CrowdHit['region'], amount: hit.amount, distance: hit.distance, cut: hit.cut, caliber: null }),\n }),\n on(event, listener) {\n let set = listeners.get(event)\n if (!set) listeners.set(event, (set = new Set()))\n set.add(listener as (event: never) => void)\n return () => void set.delete(listener as (event: never) => void)\n },\n }\n return crowd\n}\n","import type { Crowd } from '../crowd'\nimport { STATE } from '../types'\n\n/** A building the civilians walk between: its rectangle, whether it is a house (it has a gate), and the side its front faces (-1: toward -z). */\nexport type CivilianBuilding = { readonly x: number; readonly z: number; readonly w: number; readonly d: number; readonly house?: boolean; readonly front: -1 | 1 }\n\nexport type CivilianSpec = {\n readonly id: string\n /** The building name it lives in. */\n readonly home: string\n readonly child?: boolean\n readonly color?: number\n /** `[hour, building]`: from that hour, for one hour, it walks to that building. */\n readonly routine?: readonly (readonly [number, string])[]\n /** In a group it follows this civilian when that one is in the group too, else the target. */\n readonly leader?: string\n /** How close it keeps to whom it follows (2.2 m; the game's child keeps 1.2). */\n readonly followDistance?: number\n}\n\nexport type CivilianState = 'home' | 'walking' | 'group' | 'fleeing' | 'gone' | 'dead' | 'undead'\n\nexport type Civilian = {\n readonly id: string\n readonly spec: CivilianSpec\n readonly k: number\n x: number\n z: number\n yaw: number\n phase: number\n vx: number\n vz: number\n hp: number\n state: CivilianState\n path: [number, number][]\n pathIndex: number\n /** Seconds left to turn after a bite (0: not bitten). */\n biteTimer: number\n attackTimer: number\n fear: number\n /** The building it is going to (or is at). */\n goal: string\n stuckTimer: number\n /** The agent it turned into. */\n agent: number\n}\n\nexport type CivilianNumbers = {\n readonly hpAdult: number\n readonly hpChild: number\n readonly childOffset: number\n readonly sightSquared: number\n readonly leaveDistance: number\n readonly attackRange: number\n readonly attackEvery: number\n readonly biteFactor: number\n readonly biteSeconds: number\n readonly fearAfterHit: number\n readonly nearMessage: number\n readonly fearRange: number\n readonly fearSpeed: number\n readonly walkSpeed: number\n readonly childSpeed: number\n readonly bittenSpeed: number\n readonly groupRunBeyond: number\n readonly groupRunSpeed: number\n readonly fleeSpeed: number\n readonly childFleeFactor: number\n readonly stuckSeconds: number\n readonly arrive: number\n readonly probe: number\n readonly whiskers: readonly number[]\n readonly doorProbe: number\n readonly steer: number\n readonly bodyHeight: number\n readonly patrolChance: number\n readonly childHeight: number\n readonly childBuild: number\n readonly cadenceBase: number\n readonly cadencePerSpeed: number\n readonly inside: number\n readonly doorInside: number\n readonly doorOutside: number\n readonly gate: number\n readonly sidewalk: number\n readonly houseOffset: number\n}\n\n/** The game's numbers (`jogo.js` G 69960-70175). */\nexport const CIVILIAN_DEFAULTS: CivilianNumbers = {\n hpAdult: 100,\n hpChild: 40,\n childOffset: 0.6,\n sightSquared: 100,\n leaveDistance: 18,\n attackRange: 1.0,\n attackEvery: 1.5,\n biteFactor: 1.5,\n biteSeconds: 120,\n fearAfterHit: 10,\n nearMessage: 30,\n fearRange: 6,\n fearSpeed: 3.4,\n walkSpeed: 1.3,\n childSpeed: 0.85,\n bittenSpeed: 0.8,\n groupRunBeyond: 6,\n groupRunSpeed: 3.6,\n fleeSpeed: 2.6,\n childFleeFactor: 1.1,\n stuckSeconds: 8,\n arrive: 0.25,\n probe: 0.45,\n whiskers: [0, 0.7, -0.7, 1.4, -1.4],\n doorProbe: 0.6,\n steer: 8,\n bodyHeight: 0.4,\n patrolChance: 0.2,\n childHeight: 0.65,\n childBuild: 0.7,\n cadenceBase: 0.5,\n cadencePerSpeed: 0.3,\n inside: 0.8,\n doorInside: 0.8,\n doorOutside: 0.9,\n gate: 7.6,\n sidewalk: 6,\n houseOffset: 0.8,\n}\n\nexport type CivilianEventMap = {\n bitten: { id: string; near: boolean }\n died: { id: string; cause: 'attacked' }\n turned: { id: string; agent: number }\n gone: { id: string }\n}\nexport type CivilianEventName = keyof CivilianEventMap\n\nexport type CivilianOptions = {\n readonly people: readonly CivilianSpec[]\n readonly buildings: Readonly<Record<string, CivilianBuilding>>\n /** The hour of the day now (0-24), for the routines. */\n readonly hour?: () => number\n /** The crowd type a bitten civilian turns into (default the first type). */\n readonly turnInto?: number\n /** The waypoints of a building (G 69967 `pontos`): inside, the door from inside and outside, the gate of a house, the sidewalk. */\n readonly waypoints?: (building: CivilianBuilding) => [number, number][]\n readonly numbers?: Partial<CivilianNumbers>\n}\n\nexport type Civilians = {\n readonly list: readonly Civilian[]\n get(id: string): Civilian | undefined\n /** Moves the civilians, lets the agents hunt them, bites, deaths and turning (G 70144 `npcUpdate`, G 70042 `npcZombies`). */\n update(dt: number): void\n /** Joins the target's group: follows its leader or the target. */\n join(id: string): void\n /** Leaves along a path of points; at the end it is `gone`. */\n flee(id: string, path: readonly (readonly [number, number])[]): void\n /** The route between two buildings (G 69974 `rota`): the points of one, the street crossing, the points of the other reversed. */\n route(from: string, to: string): [number, number][]\n on<K extends CivilianEventName>(event: K, listener: (event: CivilianEventMap[K]) => void): () => void\n}\n\n/**\n * Civilians on a crowd: a literal port of the team game's survivors on the block (`jogo.js` G 69960-70175: `pontos`, `rota`, `npcZombies`,\n * `zChaseNpc`, `npcMorre`, `npcVira`, `npcMove`, `npcUpdate`). They keep a routine by the hour, run home when an agent is near, follow a leader in a\n * group, leave along a path, open doors on the way; agents within 10 m that are closer to them than to the target hunt them, bite, and a bitten one\n * turns after `biteSeconds`. No names, lines, radio or diary: those are the game's, through the events.\n */\nexport function createCivilians(crowd: Crowd, options: CivilianOptions): Civilians {\n const N: CivilianNumbers = { ...CIVILIAN_DEFAULTS, ...options.numbers }\n const Z = crowd.store\n const grid = crowd.grid\n const doors = crowd.doors\n const hourNow = options.hour ?? (() => 12)\n const turnInto = options.turnInto ?? 0\n const listeners = new Map<CivilianEventName, Set<(event: never) => void>>()\n const emit = <K extends CivilianEventName>(event: K, payload: CivilianEventMap[K]): void => {\n for (const listener of listeners.get(event) ?? []) (listener as (event: CivilianEventMap[K]) => void)(payload)\n }\n const random = crowd.random\n const R = random\n const lerp = (a: number, b: number, t: number): number => a + (b - a) * t\n const casaDe = (name: string): CivilianBuilding | undefined => options.buildings[name]\n const pontos =\n options.waypoints ??\n ((b: CivilianBuilding): [number, number][] => {\n const casa = !!b.house\n const side = b.front\n const front = side < 0 ? b.z + b.d : b.z\n const back = side < 0 ? b.z : b.z + b.d\n const cx = b.x + b.w / 2\n const inw = side < 0 ? -1 : 1\n const calc = side < 0 ? -N.sidewalk : N.sidewalk\n const pts: [number, number][] = [\n [cx + (casa ? N.houseOffset : 0), (front + (front + back) / 2) / 2],\n [cx, front + inw * N.doorInside],\n [cx, front - inw * N.doorOutside],\n ]\n if (casa) pts.push([cx, side < 0 ? -N.gate : N.gate])\n pts.push([cx, calc])\n return pts\n })\n const rota = (bA: CivilianBuilding, bB: CivilianBuilding): [number, number][] => {\n const a = pontos(bA)\n const b = pontos(bB).reverse()\n const out = a.slice()\n if (Math.sign(a[a.length - 1]![1]) !== Math.sign(b[0]![1])) out.push([a[a.length - 1]![0], b[0]![1]]) // crosses the street\n return out.concat(b)\n }\n const list: Civilian[] = options.people.map((spec, k) => {\n const b = casaDe(spec.home)!\n const p = pontos(b)[0]!\n return {\n id: spec.id,\n spec,\n k,\n x: p[0] + (spec.child ? N.childOffset : 0),\n z: p[1],\n yaw: 0,\n phase: 0,\n vx: 0,\n vz: 0,\n hp: spec.child ? N.hpChild : N.hpAdult,\n state: 'home',\n path: [],\n pathIndex: 0,\n biteTimer: 0,\n attackTimer: 0,\n fear: 0,\n goal: spec.home,\n stuckTimer: 0,\n agent: -1,\n }\n })\n const byId = new Map(list.map((c) => [c.id, c]))\n const exposed = (n: Civilian): boolean => n.state !== 'dead' && n.state !== 'undead' && n.state !== 'gone'\n const target = () => crowd.target?.state ?? null\n const distToTarget = (x: number, z: number): number => {\n const t = target()\n return t ? Math.hypot(x - t.x, z - t.z) : 0\n }\n\n const turn = (n: Civilian): void => {\n n.state = 'undead'\n const i = crowd.spawn(n.x, n.z, turnInto)\n if (i >= 0) {\n Z.color[i] = n.spec.color ?? Z.color[i]!\n Z.named[i] = n.k\n Z.height[i] = n.spec.child ? N.childHeight : Z.height[i]!\n Z.build[i] = n.spec.child ? N.childBuild : Z.build[i]!\n Z.age[i] = 0\n Z.state[i] = STATE.CHASE\n n.agent = i\n }\n emit('turned', { id: n.id, agent: i })\n }\n const die = (n: Civilian): void => {\n if (n.biteTimer > 0) return turn(n)\n n.state = 'dead'\n n.vx = n.vz = 0\n // the body with what it carried (the game's own item stays with the game)\n crowd.corpses.add({\n x: n.x,\n z: n.z,\n yaw: n.yaw,\n height: n.spec.child ? N.childHeight : 1,\n build: n.spec.child ? N.childBuild : 1,\n missing: 0,\n color: n.spec.color ?? 0,\n variant: -1,\n skin: 0.4,\n seed: R() * 10,\n named: n.k,\n })\n crowd.effect({ effect: 'blood', point: [n.x, 1, n.z], age: 0, amount: 300, died: true })\n emit('died', { id: n.id, cause: 'attacked' })\n }\n\n // ----- the agents against the civilians (G 70042 npcZombies) -----\n const agentsAgainst = (dt: number): void => {\n const t = target()\n for (let i = 0; i < Z.n; i++) {\n if (Z.band[i]! > 1) continue\n const k = Z.hunts[i]!\n if (k >= 0) {\n const n = list[k]!\n if (!exposed(n)) {\n Z.hunts[i] = -1\n continue\n }\n const dx = n.x - Z.x[i]!\n const dz = n.z - Z.z[i]!\n const d = Math.hypot(dx, dz)\n if (d > N.leaveDistance) {\n Z.hunts[i] = -1\n continue\n }\n if (d < N.attackRange) {\n Z.attackTimer[i]! -= dt\n if (Z.attackTimer[i]! <= 0) {\n Z.attackTimer[i] = N.attackEvery\n const C = crowd.pack.types[Z.type[i]!]!\n n.hp -= C.damage\n n.fear = N.fearAfterHit\n crowd.sound('flesh', 0.7, n.x, 1, n.z, distToTarget(n.x, n.z))\n crowd.effect({ effect: 'blood', point: [n.x, 1.2, n.z], age: 0, amount: 30, died: false })\n if (n.biteTimer <= 0 && R() < C.bite * N.biteFactor) {\n n.biteTimer = N.biteSeconds\n emit('bitten', { id: n.id, near: distToTarget(n.x, n.z) < N.nearMessage })\n }\n if (n.hp <= 0) die(n)\n }\n }\n } else if (Z.state[i]! <= 1) {\n for (const n of list) {\n if (!exposed(n) || n.state === 'home') continue\n const dx = n.x - Z.x[i]!\n const dz = n.z - Z.z[i]!\n const d2 = dx * dx + dz * dz\n const dp = t ? (t.x - Z.x[i]!) ** 2 + (t.z - Z.z[i]!) ** 2 : Infinity\n if (d2 < N.sightSquared && d2 < dp && grid.losClear(Z.x[i]!, Z.z[i]!, n.x, n.z)) {\n Z.hunts[i] = n.k\n Z.state[i] = STATE.CHASE\n break\n }\n }\n }\n }\n }\n // the crowd calls it: an agent hunting a civilian goes straight at it (G 70073 zChaseNpc)\n crowd.setHunt((i) => {\n const n = list[Z.hunts[i]!]\n if (!n) return false\n const dx = n.x - Z.x[i]!\n const dz = n.z - Z.z[i]!\n const d = Math.hypot(dx, dz) || 1\n const sp = d < 1 ? 0 : crowd.chaseSpeed(i)\n Z.vx[i] = (dx / d) * sp\n Z.vz[i] = (dz / d) * sp\n Z.yaw[i] = Math.atan2(dx, dz)\n return true\n })\n\n // ----- moving (G 70125 npcMove) -----\n const move = (n: Civilian, dt: number, tx: number, tz: number, sp: number): boolean => {\n const dx = tx - n.x\n const dz = tz - n.z\n const d = Math.hypot(dx, dz)\n if (d < N.arrive) {\n n.vx = n.vz = 0\n return true\n }\n let ax = dx / d\n let az = dz / d\n // round a wall: tries 0, ±40°, ±80°\n for (const a of N.whiskers) {\n const c = Math.cos(a)\n const s = Math.sin(a)\n const bx = ax * c - az * s\n const bz = ax * s + az * c\n if (!grid.solidAt(n.x + bx * N.probe, n.z + bz * N.probe, N.bodyHeight)) {\n ax = bx\n az = bz\n break\n }\n }\n // a closed door ahead: opens it\n const c = grid.cellOf(n.x + ax * N.doorProbe, n.z + az * N.doorProbe)\n const di = doors.at(c)\n if (c >= 0 && di >= 0) {\n const D = doors.list[di]!\n if (!D.open && !D.broken && !D.planks) {\n doors.set(di, true)\n crowd.sound('wood', 0.3, D.x, 1, D.z, distToTarget(D.x, D.z))\n }\n }\n n.vx = lerp(n.vx, ax * sp, Math.min(1, dt * N.steer))\n n.vz = lerp(n.vz, az * sp, Math.min(1, dt * N.steer))\n const nx = n.x + n.vx * dt\n const nz = n.z + n.vz * dt\n if (!grid.solidAt(nx, n.z, N.bodyHeight)) n.x = nx\n if (!grid.solidAt(n.x, nz, N.bodyHeight)) n.z = nz\n n.yaw = Math.atan2(n.vx, n.vz)\n return false\n }\n\n const update = (dt: number): void => {\n agentsAgainst(dt)\n const h = hourNow()\n const t = target()\n for (const n of list) {\n if (n.state === 'dead' || n.state === 'undead' || n.state === 'gone') continue\n if (n.biteTimer > 0) {\n n.biteTimer -= dt\n if (n.biteTimer <= 0) {\n turn(n)\n continue\n }\n }\n // fear: an agent near sends it home (or close to its leader in a group)\n let perto = 99\n for (let i = 0; i < Z.n; i++) {\n if (Z.band[i]) continue\n const d = Math.hypot(Z.x[i]! - n.x, Z.z[i]! - n.z)\n if (d < perto) perto = d\n }\n const sp = (perto < N.fearRange ? N.fearSpeed : N.walkSpeed) * (n.spec.child ? N.childSpeed : 1) * (n.biteTimer > 0 ? N.bittenSpeed : 1)\n if (n.state === 'group') {\n const leaderCivilian = n.spec.leader ? byId.get(n.spec.leader) : undefined\n const lider = leaderCivilian && leaderCivilian.state === 'group' ? leaderCivilian : t\n if (lider) {\n const dd = Math.hypot(lider.x - n.x, lider.z - n.z)\n if (dd > (n.spec.followDistance ?? 2.2)) move(n, dt, lider.x, lider.z, dd > N.groupRunBeyond ? N.groupRunSpeed : sp)\n else {\n n.vx *= 0.8\n n.vz *= 0.8\n }\n }\n } else if (n.state === 'fleeing') {\n const p = n.path[n.pathIndex]\n if (!p) {\n n.state = 'gone'\n emit('gone', { id: n.id })\n continue\n }\n n.stuckTimer += dt\n if (move(n, dt, p[0], p[1], N.fleeSpeed * (n.spec.child ? N.childFleeFactor : 1)) || n.stuckTimer > N.stuckSeconds) {\n n.pathIndex++\n n.stuckTimer = 0\n }\n } else if (n.spec.routine) {\n // the routine by hour: walks to the building of the hour\n let alvo = n.spec.home\n for (const [hh, onde] of n.spec.routine) if (h >= hh && h < hh + 1) alvo = onde\n if (perto < N.fearRange) alvo = n.spec.home\n if (alvo !== n.goal) {\n const home = casaDe(n.spec.home)!\n n.path = rota(casaDe(n.goal) ?? home, casaDe(alvo) ?? home)\n n.pathIndex = 0\n n.goal = alvo\n n.state = 'walking'\n }\n if (n.state === 'walking') {\n const p = n.path[n.pathIndex]\n n.stuckTimer += dt\n if (!p) n.state = 'home'\n else if (move(n, dt, p[0], p[1], sp) || n.stuckTimer > N.stuckSeconds) {\n n.pathIndex++\n n.stuckTimer = 0\n }\n } else {\n n.vx *= 0.8\n n.vz *= 0.8\n }\n } else {\n n.vx *= 0.8\n n.vz *= 0.8\n }\n {\n const v = Math.hypot(n.vx, n.vz)\n n.phase += dt * (v < 0.05 ? 0 : N.cadenceBase + N.cadencePerSpeed * v)\n }\n }\n // a named agent: walks the street of its old routine slowly while it sees nobody\n for (let i = 0; i < Z.n; i++)\n if (Z.named[i]! >= 0 && Z.state[i] === STATE.WANDER && R() < dt * N.patrolChance) {\n const n = list[Z.named[i]!]\n if (!n) continue\n const p = pontos(casaDe(n.spec.home)!)\n const q = p[Math.floor(R() * p.length)]!\n Z.wx[i] = q[0]\n Z.wz[i] = q[1]\n }\n }\n\n return {\n list,\n get: (id) => byId.get(id),\n update,\n join(id) {\n const n = byId.get(id)\n if (n && exposed(n)) n.state = 'group'\n },\n flee(id, path) {\n const n = byId.get(id)\n if (!n || !exposed(n)) return\n n.state = 'fleeing'\n n.path = path.map(([x, z]) => [x, z])\n n.pathIndex = 0\n },\n route(from, to) {\n return rota(casaDe(from)!, casaDe(to)!)\n },\n on(event, listener) {\n let set = listeners.get(event)\n if (!set) listeners.set(event, (set = new Set()))\n set.add(listener as (event: never) => void)\n return () => void set.delete(listener as (event: never) => void)\n },\n }\n}\n","import type { Crowd } from '../crowd'\nimport { STATE } from '../types'\n\n/** What an animal does besides wandering and fleeing; each is optional, an animal without one only wanders and flees. */\nexport type HerdRole = {\n /** Wanders within this many metres of home (8). */\n readonly range?: number\n /** Flees from the target closer than this and from agents closer than `fear·1.5 + 2` (5; 0 never flees). */\n readonly fear?: number\n /** Calls when it flees (the hen): sound, noise radius, cooldown. */\n readonly fleeCall?: { readonly sound: string; readonly noise: number; readonly cooldown: number }\n /** Alarm when the target comes within `range` (the lapwing): flies for `fly` s. */\n readonly alarm?: { readonly range: number; readonly sound: string; readonly noise: number; readonly cooldown: number; readonly fly: number }\n /** Now and then calls loud (the cow): chance per second, sound, noise, cooldown. */\n readonly call?: { readonly chance: number; readonly sound: string; readonly noise: number; readonly cooldown: number }\n /** Barks at agents within `bark` m and growls at agents within `growl` m when it is the target's companion (the dog). */\n readonly guard?: { readonly bark: number; readonly barkNoise: number; readonly barkCooldown: number; readonly growl: number; readonly growlCooldown: number; readonly sound: string }\n}\n\nexport type HerdAnimalSpec = HerdRole & {\n /** The species id (any registry: `game-animals-species` has the 13 of the deliveries). */\n readonly species: string\n readonly x: number\n readonly z: number\n /** The species' gait rates and its size (`game-animals` `quadrupedLayout(body).size`, `birdSize(body)`). */\n readonly walkHz: number\n readonly runHz: number\n readonly size: number\n /** Follows 2.2 m behind the target (the companion dog). */\n readonly companion?: boolean\n /** A carcass: it does not move (the vultures circle it). */\n readonly dead?: boolean\n /** A bird of the sky (the vulture): only the flock moves it. */\n readonly sky?: boolean\n}\n\nexport type HerdAnimal = {\n readonly index: number\n readonly spec: HerdAnimalSpec\n x: number\n z: number\n yaw: number\n homeX: number\n homeZ: number\n range: number\n fear: number\n clip: 'idle' | 'walk' | 'run'\n timer: number\n targetX: number\n targetZ: number\n fly: number\n cooldown: number\n walkSpeed: number\n runSpeed: number\n companion: boolean\n dead: boolean\n /** The nearest agent seen in the last look (index and distance), refreshed about a quarter of the time. */\n near: readonly [number, number] | null | undefined\n}\n\nexport type HerdEventMap = {\n sound: { sound: string; volume: number; position: [number, number, number]; distance: number; animal: number }\n noise: { x: number; z: number; radius: number; animal: number }\n message: { id: 'alarm' | 'bark' | 'growl'; seconds: number; animal: number; distance?: number }\n}\nexport type HerdEventName = keyof HerdEventMap\n\nexport type HerdNumbers = {\n readonly range: number\n readonly fear: number\n readonly walkFactor: number\n readonly walkBase: number\n readonly runFactor: number\n readonly runBase: number\n readonly activeDistance: number\n readonly companionBehind: number\n readonly companionRun: number\n readonly companionStop: number\n readonly retargetTries: number\n readonly restBase: number\n readonly restRandom: number\n readonly lookEvery: number\n readonly lookShare: number\n readonly lookRadius: number\n readonly fearAgentFactor: number\n readonly fearAgentBase: number\n readonly fleeStep: number\n readonly arrive: number\n readonly bodyHeight: number\n readonly turn: number\n readonly freeHeight: number\n readonly worldMargin: number\n}\n\n/** The game's numbers (`jogo.js` G 66592-66700 `bichoNovo`, `bichoLivre`, `bichosUpdate`). */\nexport const HERD_DEFAULTS: HerdNumbers = {\n range: 8,\n fear: 5,\n walkFactor: 1.05,\n walkBase: 0.25,\n runFactor: 2.2,\n runBase: 0.8,\n activeDistance: 70,\n companionBehind: 2.2,\n companionRun: 6,\n companionStop: 0.8,\n retargetTries: 6,\n restBase: 4,\n restRandom: 7,\n lookEvery: 7,\n lookShare: 0.25,\n lookRadius: 40,\n fearAgentFactor: 1.5,\n fearAgentBase: 2,\n fleeStep: 6,\n arrive: 0.3,\n bodyHeight: 0.3,\n turn: 6,\n freeHeight: 0.6,\n worldMargin: 4,\n}\n\n/**\n * The roles of the animals of the deliveries, by `game-animals-species` id: what `bichosPovoar` gives each one (`jogo.js` G 66607-66623) and the\n * calls of `bichosUpdate` (hen 30 m every 6 s; lapwing 12 m, 40 m, flies 4 s; cow 80 m, 2% a second, every 30 s; dog barks under 12 m, 60 m, every\n * 3 s, growls under 40 m every 10 s). A game passes its own or changes one.\n */\nexport const HERD_ROLES: Readonly<Record<string, HerdRole & { readonly sky?: boolean }>> = {\n dog: { range: 6, fear: 0, guard: { bark: 12, barkNoise: 60, barkCooldown: 3, growl: 40, growlCooldown: 10, sound: 'bark' } },\n cat: { range: 6, fear: 5 },\n chicken: { range: 5, fear: 4, fleeCall: { sound: 'cluck', noise: 30, cooldown: 6 } },\n southern_lapwing: { range: 5, fear: 0, alarm: { range: 12, sound: 'lapwing', noise: 40, cooldown: 8, fly: 4 } },\n cow: { range: 10, fear: 6, call: { chance: 0.02, sound: 'moo', noise: 80, cooldown: 30 } },\n horse: { range: 10, fear: 7 },\n pig: { range: 5, fear: 3 },\n capybara: { range: 8, fear: 15 },\n vulture: { sky: true },\n}\n\nexport type HerdOptions = {\n /** Where the camera is: animals farther than 70 m rest, and sounds carry their distance from here. Default: the target. */\n readonly view?: () => { readonly x: number; readonly z: number }\n /** A spot an animal must not wander into (inside a building). */\n readonly blocked?: (x: number, z: number) => boolean\n readonly numbers?: Partial<HerdNumbers>\n}\n\nexport type Herd = {\n readonly list: readonly HerdAnimal[]\n /** Adds an animal (draws its heading and its first rest, as the game does). */\n add(spec: HerdAnimalSpec): HerdAnimal\n /** A free spot `r0` to `r1` m from (x, z), or null (G 66601 `bichoLivre`). */\n free(x: number, z: number, r0: number, r1: number, tries?: number): [number, number] | null\n /** Wander, flee, follow, call (G 66648 `bichosUpdate`, without the drawing). */\n update(dt: number): void\n /** The time the herd has run (the flock reads it). */\n readonly time: number\n setCompanion(index: number, on: boolean): void\n on<K extends HerdEventName>(event: K, listener: (event: HerdEventMap[K]) => void): () => void\n}\n\n/**\n * Animals on a crowd: a literal port of the team game's animals (`jogo.js` G 66592-66700). Each wanders around home and rests 4 to 11 s, flees\n * from the target and from the crowd's agents, and may call, raise an alarm or guard the target as its companion. The calls are noises the crowd\n * hears. The drawing and the bones are the game's (`game-animals-three`).\n */\nexport function createHerd(crowd: Crowd, options: HerdOptions = {}): Herd {\n const N: HerdNumbers = { ...HERD_DEFAULTS, ...options.numbers }\n const R = crowd.random\n const Z = crowd.store\n const grid = crowd.grid\n const list: HerdAnimal[] = []\n let time = 0\n const listeners = new Map<HerdEventName, Set<(event: never) => void>>()\n const emit = <K extends HerdEventName>(event: K, payload: HerdEventMap[K]): void => {\n for (const listener of listeners.get(event) ?? []) (listener as (event: HerdEventMap[K]) => void)(payload)\n }\n const view = options.view ?? (() => crowd.target?.state ?? { x: 0, z: 0 })\n const free = (x0: number, z0: number, r0: number, r1: number, tries = 40): [number, number] | null => {\n for (let k = 0; k < tries; k++) {\n const a = R() * 6.28\n const r = r0 + R() * (r1 - r0)\n const x = x0 + Math.cos(a) * r\n const z = z0 + Math.sin(a) * r\n if (Math.abs(x) < grid.half - N.worldMargin && Math.abs(z) < grid.half - N.worldMargin && !grid.solidAt(x, z, N.freeHeight) && !options.blocked?.(x, z)) return [x, z]\n }\n return null\n }\n // the nearest live agent (the crowd's grid is costly for this; the fauna is small, so it sweeps all)\n const nearestAgent = (x: number, z: number, r: number): [number, number] | null => {\n let best = -1\n let bd = r * r\n for (let i = 0; i < Z.n; i++) {\n if (Z.state[i] === STATE.DORMANT) continue\n const dx = Z.x[i]! - x\n const dz = Z.z[i]! - z\n const d = dx * dx + dz * dz\n if (d < bd) {\n bd = d\n best = i\n }\n }\n return best < 0 ? null : [best, Math.sqrt(bd)]\n }\n const call = (E: HerdAnimal, sound: string, noise: number, dc: number): void => {\n crowd.emitNoise(E.x, E.z, noise)\n emit('noise', { x: E.x, z: E.z, radius: noise, animal: E.index })\n emit('sound', { sound, volume: 1, position: [E.x, 0.5, E.z], distance: dc, animal: E.index })\n }\n const update = (dt: number): void => {\n time += dt\n const t = crowd.target?.state\n const px = t?.x ?? 0\n const pz = t?.z ?? 0\n const v = view()\n for (const E of list) {\n if (E.spec.sky) continue\n const dc = Math.hypot(E.x - v.x, E.z - v.z)\n if (dc >= N.activeDistance || E.dead) continue\n E.timer -= dt\n E.cooldown -= dt\n let alvoX = E.targetX\n let alvoZ = E.targetZ\n let corre = false\n const dp = t ? Math.hypot(E.x - px, E.z - pz) : Infinity\n if (E.companion && t) {\n // follows 2.2 m behind the target\n const bx = px - Math.sin(t.yaw) * N.companionBehind\n const bz = pz - Math.cos(t.yaw) * N.companionBehind\n alvoX = bx\n alvoZ = bz\n corre = dp > N.companionRun\n if (Math.hypot(bx - E.x, bz - E.z) < N.companionStop) {\n alvoX = E.x\n alvoZ = E.z\n }\n } else if (E.timer <= 0) {\n const q = free(E.homeX, E.homeZ, 0, E.range, N.retargetTries)\n if (q) {\n E.targetX = q[0]\n E.targetZ = q[1]\n }\n E.timer = N.restBase + R() * N.restRandom\n alvoX = E.targetX\n alvoZ = E.targetZ\n }\n // fear: flees from the target (not the companion) and from agents\n const zp = (time * N.lookEvery + E.x) % 1 < N.lookShare || E.near === undefined ? (E.near = nearestAgent(E.x, E.z, N.lookRadius)) : E.near\n let fx = 0\n let fz = 0\n if (E.fear && !E.companion && dp < E.fear) {\n fx += E.x - px\n fz += E.z - pz\n }\n if (E.fear && zp && zp[1] < E.fear * N.fearAgentFactor + N.fearAgentBase) {\n fx += E.x - Z.x[zp[0]]!\n fz += E.z - Z.z[zp[0]]!\n }\n if (fx || fz) {\n const l = Math.hypot(fx, fz) || 1\n alvoX = E.x + (fx / l) * N.fleeStep\n alvoZ = E.z + (fz / l) * N.fleeStep\n corre = true\n const fc = E.spec.fleeCall\n if (fc && E.cooldown <= 0) {\n E.cooldown = fc.cooldown\n call(E, fc.sound, fc.noise, dc)\n }\n }\n // alarms\n const al = E.spec.alarm\n if (al && dp < al.range && E.cooldown <= 0) {\n E.cooldown = al.cooldown\n E.fly = al.fly\n call(E, al.sound, al.noise, dc)\n emit('message', { id: 'alarm', seconds: 2.5, animal: E.index })\n }\n const cl = E.spec.call\n if (cl && E.cooldown <= 0 && R() < dt * cl.chance) {\n E.cooldown = cl.cooldown\n call(E, cl.sound, cl.noise, dc)\n }\n const g = E.spec.guard\n if (g && zp && E.cooldown <= 0) {\n if (zp[1] < g.bark) {\n E.cooldown = g.barkCooldown\n call(E, g.sound, g.barkNoise, dc)\n if (E.companion) emit('message', { id: 'bark', seconds: 1.5, animal: E.index, distance: Math.round(zp[1]) })\n } else if (E.companion && zp[1] < g.growl) {\n E.cooldown = g.growlCooldown\n emit('message', { id: 'growl', seconds: 2.5, animal: E.index, distance: Math.round(zp[1]) })\n }\n }\n // walks\n const ax = alvoX - E.x\n const az = alvoZ - E.z\n const ad = Math.hypot(ax, az)\n let sp = 0\n if (E.fly > 0) {\n E.fly -= dt\n E.clip = 'run'\n } else if (ad > N.arrive) {\n sp = corre ? E.runSpeed : E.walkSpeed\n E.clip = corre ? 'run' : 'walk'\n } else E.clip = 'idle'\n if (sp) {\n const k = Math.min(1, (sp * dt) / ad)\n const nx = E.x + ax * k\n const nz = E.z + az * k\n if (!grid.solidAt(nx, nz, N.bodyHeight)) {\n E.x = nx\n E.z = nz\n } else E.timer = 0\n let dy = Math.atan2(ax, az) - E.yaw\n dy -= Math.round(dy / 6.2832) * 6.2832\n E.yaw += dy * Math.min(1, dt * N.turn)\n }\n }\n }\n return {\n list,\n add(spec) {\n const E: HerdAnimal = {\n index: list.length,\n spec,\n x: spec.x,\n z: spec.z,\n yaw: R() * 6.28,\n homeX: spec.x,\n homeZ: spec.z,\n range: spec.range ?? N.range,\n clip: 'idle',\n timer: R() * 4,\n targetX: spec.x,\n targetZ: spec.z,\n fear: spec.fear ?? N.fear,\n fly: 0,\n cooldown: 0,\n walkSpeed: spec.walkHz * spec.size * N.walkFactor + N.walkBase,\n runSpeed: spec.runHz * spec.size * N.runFactor + N.runBase,\n companion: !!spec.companion,\n dead: !!spec.dead,\n near: undefined,\n }\n list.push(E)\n return E\n },\n free,\n update,\n get time() {\n return time\n },\n setCompanion(index, on) {\n const E = list[index]\n if (!E) return\n E.companion = on\n E.homeX = E.x\n E.homeZ = E.z\n },\n on(event, listener) {\n let set = listeners.get(event)\n if (!set) listeners.set(event, (set = new Set()))\n set.add(listener as (event: never) => void)\n return () => void set.delete(listener as (event: never) => void)\n },\n }\n}\n","/** One bird of a flock this frame: where it is, its heading and its roll, its wing phase, and whether it is close enough to draw. */\nexport type FlockBird = { x: number; y: number; z: number; yaw: number; roll: number; wing: number; visible: boolean }\n\nexport type FlockMode =\n /** The vultures (G 66700-66708): circle the centre of the bodies on the ground, each at its own speed, radius and height. */\n | 'carrion'\n /** The birds of the live map (Bancada de Hordas G 53609-53615): a loose ring that rises and falls. */\n | 'orbit'\n\nexport type FlockOptions = {\n readonly count: number\n readonly mode: FlockMode\n /** Visible within this distance of the view (carrion 260 m; orbit always). */\n readonly visibleWithin?: number\n}\n\nexport type Flock = {\n readonly birds: readonly FlockBird[]\n /** Moves the flock to time `t` (seconds) around `center`; `view` decides what is visible. */\n update(t: number, dt: number, center: readonly [number, number], view?: readonly [number, number]): void\n}\n\n/** The centre the vultures circle: the mean of the bodies on the ground when there are three or more, else `fallback` (a carcass). */\nexport const carrionCenter = (corpses: readonly { readonly x: number; readonly z: number }[], fallback: readonly [number, number] | null): [number, number] => {\n let ux = 0\n let uz = 0\n let un = 0\n for (const c of corpses) {\n ux += c.x\n uz += c.z\n un++\n }\n if (un >= 3) return [ux / un, uz / un]\n // as in the game: with one or two bodies and no carcass the sums stay (G 66702)\n return fallback ? [fallback[0], fallback[1]] : [ux, uz]\n}\n\n/** A flock of birds by formula: the game's vultures or the live map's ring (literal formulas from the deliveries). */\nexport function createFlock(options: FlockOptions): Flock {\n const birds: FlockBird[] = Array.from({ length: options.count }, () => ({ x: 0, y: 0, z: 0, yaw: 0, roll: 0, wing: 0, visible: true }))\n const within = options.visibleWithin ?? (options.mode === 'carrion' ? 260 : Infinity)\n return {\n birds,\n update(t, dt, [cx, cz], view) {\n birds.forEach((U, i) => {\n if (options.mode === 'carrion') {\n const a = t * (0.18 + i * 0.04) + i * 2.1\n const r = 7 + i * 2\n U.x = cx + Math.cos(a) * r\n U.y = 16 + i * 2.5\n U.z = cz + Math.sin(a) * r\n U.yaw = -a\n U.roll = 0.35\n U.visible = !view || Math.hypot(cx - view[0], cz - view[1]) < within\n if (U.visible) U.wing += dt * 1.2 * 2 * Math.PI\n } else {\n const a = t * 0.3 + i * 0.4\n const r = 20 + (i % 7) * 4\n U.x = cx + Math.cos(a) * r\n U.y = 25 + Math.sin(t + i) * 2\n U.z = cz + Math.sin(a) * r\n U.yaw = -a\n U.roll = 0\n U.visible = !view || Math.hypot(cx - view[0], cz - view[1]) < within\n }\n })\n },\n }\n}\n","import type { Crowd } from '../crowd'\nimport type { Civilians } from './civilians'\n\n/** What the crowd needs of a region model: `game-infection`'s `Infection` is one as it is. */\nexport type InfectionModelLike = {\n materialize(region: string, n: number): { state: 'active' | 'contained' | 'removed'; seed: number }[]\n giveBack(region: string, batch: { state: 'active' | 'contained' | 'removed'; seed: number }[]): void\n infect(region: string, n?: number, hoursElapsed?: number): void\n}\n\nexport type InfectionBindingOptions = {\n /** Where the k-th agent of a batch is born in the region, from its seed; null skips it (it goes straight back). */\n readonly place: (region: string, seed: number, k: number) => readonly [number, number] | null\n /** The crowd type of an agent (default: drawn by the pack's weights). */\n readonly typeFor?: (agent: { readonly state: 'active' | 'contained' | 'removed'; readonly seed: number }, region: string) => number\n /** The relevance band from which an agent goes back to the model (default 3: farther than 400 m). */\n readonly giveBackBand?: number\n /** Civilians whose bites the model hears about (`infect`), in the region `regionOf` gives. */\n readonly civilians?: Civilians\n readonly regionOf?: (x: number, z: number) => string | null\n}\n\nexport type InfectionBinding = {\n /** Brings up to `n` infected of `region` out of the model into the crowd. Returns the agents born. */\n materialize(region: string, n: number): number[]\n /** Gives back to the model the agents of `region` (all regions when left out) that are in the far band, or all of them with `all`. */\n giveBack(region?: string, options?: { readonly all?: boolean }): number\n /** The region of a materialised agent, or null. */\n regionOf(agent: number): string | null\n unbind(): void\n}\n\n/**\n * Ties a crowd to a region model (`game-infection`): `materialize` turns the model's numbers into agents near the player, a kill of one of them\n * goes back to its region as removed, the far ones go back with `giveBack`, and a civilian's bite is an `infect`. No team code exists for this bridge\n * (decision P12 of the spec); the model's own API is used as it is.\n */\nexport function bindInfection(crowd: Crowd, model: InfectionModelLike, options: InfectionBindingOptions): InfectionBinding {\n const tracked = new Map<number, { region: string; state: 'active' | 'contained' | 'removed'; seed: number }>()\n const band = options.giveBackBand ?? 3\n const offKill = crowd.on('kill', ({ agent }) => {\n const entry = tracked.get(agent)\n if (!entry) return\n // it left the numbers when it was materialised: it goes back as removed (a `remove` would take one more active from the model)\n model.giveBack(entry.region, [{ state: 'removed', seed: entry.seed }])\n tracked.delete(agent)\n })\n const offRemove = crowd.on('remove', ({ agent, moved }) => {\n tracked.delete(agent)\n if (moved < 0) return\n const entry = tracked.get(moved)\n if (!entry) return\n tracked.delete(moved)\n tracked.set(agent, entry)\n })\n const offBite = options.civilians?.on('bitten', ({ id }) => {\n const n = options.civilians?.get(id)\n const region = n && options.regionOf?.(n.x, n.z)\n if (region) model.infect(region, 1, 0)\n })\n return {\n materialize(region, n) {\n const batch = model.materialize(region, n)\n const born: number[] = []\n const back: typeof batch = []\n batch.forEach((agent, k) => {\n const at = options.place(region, agent.seed, k)\n const i = at ? crowd.spawn(at[0], at[1], options.typeFor?.(agent, region) ?? -1) : -1\n if (i < 0) {\n back.push(agent)\n return\n }\n tracked.set(i, { region, state: agent.state, seed: agent.seed })\n born.push(i)\n })\n if (back.length) model.giveBack(region, back)\n return born\n },\n giveBack(region, opts = {}) {\n const byRegion = new Map<string, { state: 'active' | 'contained' | 'removed'; seed: number }[]>()\n const indices = [...tracked.keys()].sort((a, b) => b - a)\n for (const i of indices) {\n const entry = tracked.get(i)\n if (!entry || (region !== undefined && entry.region !== region)) continue\n if (!opts.all && crowd.store.band[i]! < band) continue\n let list = byRegion.get(entry.region)\n if (!list) byRegion.set(entry.region, (list = []))\n list.push({ state: entry.state, seed: entry.seed })\n tracked.delete(i)\n crowd.remove(i)\n }\n let count = 0\n for (const [r, list] of byRegion) {\n model.giveBack(r, list)\n count += list.length\n }\n return count\n },\n regionOf: (agent) => tracked.get(agent)?.region ?? null,\n unbind() {\n offKill()\n offRemove()\n offBite?.()\n tracked.clear()\n },\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACsBO,MAAM,sBAA6D,EAAE,UAAU,MAAM,UAAU,IAAI,MAAM,KAAK,UAAU,KAAK,cAAc,EAAE;AAwB7I,WAAS,WAAW,OAAoB,QAA4D,UAA6B,CAAC,GAA2B;AAClK,UAAM,IAAI,EAAE,GAAG,qBAAqB,GAAG,QAAQ;AAC/C,UAAM,KAAK,OAAO,SAAS,CAAC,IAAI,MAAM,MAAM,CAAC;AAC7C,UAAM,KAAK,OAAO,SAAS,CAAC,IAAI,MAAM,MAAM,CAAC;AAC7C,UAAM,KAAK,OAAO,SAAS,CAAC,IAAI,MAAM,MAAM,CAAC;AAC7C,UAAM,WAAW,KAAK,MAAM,IAAI,IAAI,EAAE;AACtC,QAAI,EAAE,MAAM,SAAS,MAAM,WAAW,MAAM,OAAQ,QAAO;AAC3D,UAAM,WAAW,IAAI,WAAW,MAAM,WAAW,EAAE;AACnD,UAAM,OAAO,KAAK,IAAI,OAAO,MAAM,IAAI;AACvC,UAAM,QAAQ,KAAK,IAAI,EAAE,UAAU,KAAK,KAAM,IAAI,MAAM,UAAU,MAAM,SAAS,KAAK,EAAE,WAAY,IAAI,CAAC,IAAI;AAC7G,QAAI,EAAE,SAAS,EAAE,UAAW,QAAO;AAEnC,UAAM,YAAkB,WAAW,OAAO,CAAC,KAAK,UAAU,KAAK,WAAW,EAAE,MAAM,KAAK,QAAQ,IAAI,CAAC,GAAG,GAAG,CAAC;AAC3G,UAAM,SAAS,KAAK,MAAM,UAAU,CAAC,GAAG,UAAU,CAAC,GAAG,UAAU,CAAC,CAAC;AAClE,WAAO,EAAE,OAAO,WAAW,CAAC,UAAU,CAAC,IAAI,QAAQ,UAAU,CAAC,IAAI,QAAQ,UAAU,CAAC,IAAI,MAAM,GAAG,UAAU,QAAQ;EACtH;AElDA,MAAM,gCAAgC,OAAO;AEDtC,MAAM,eAAe;IAC1B,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,YAAY;IACZ,YAAY;IACZ,YAAY;IACZ,YAAY;IACZ,KAAK;EACP;AQuBO,MAAM,oBAAoB;IAC/B,oBAAoB;IACpB,cAAc;IACd,cAAc;IACd,gBAAgB;EAClB;AAGO,MAAM,oBAAoB;IAC/B,CAAC,kBAAkB,kBAAkB,GAAG,EAAE,UAAU,KAAK,OAAO,UAAU;IAC1E,CAAC,kBAAkB,YAAY,GAAG,EAAE,UAAU,KAAK,OAAO,UAAU;IACpE,CAAC,kBAAkB,YAAY,GAAG,EAAE,UAAU,KAAK,OAAO,QAAQ,WAAW,EAAE;IAC/E,CAAC,kBAAkB,cAAc,GAAG,EAAE,UAAU,KAAK,OAAO,OAAO;EACrE;AQlDO,MAAM,cAAc,CAAC,YAAY,WAAW,QAAQ,QAAQ,QAAQ,SAAS,UAAU,QAAQ,SAAS,SAAS,SAAS,QAAQ;AAOlI,MAAM,WAAW,CAAC,QAAQ,KAAK;AAI/B,MAAM,cAAc,CAAC,SAAS,OAAO;AAoBrC,MAAM,iBAAiB;IAC5B;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;EACF;AAMO,MAAM,kBAAkB,CAAC,SAAoB,SAAkC,YAAY,OAAO,IAAI,IAAI;AAI1G,MAAM,qBAAiD,YAAY,QAAQ,CAAC,YAAY,SAAS,IAAI,CAAC,SAAS,gBAAgB,SAAS,IAAI,CAAC,CAAC;AAC9I,MAAM,iBAAyC,YAAY,QAAQ,CAAC,YAAY,YAAY,IAAI,CAAC,YAAyB,QAAQ,OAAO,IAAI,OAAO,EAAE,CAAC;AACvJ,MAAM,iBAAyC,CAAC,GAAG,oBAAoB,GAAG,gBAAgB,GAAG,cAAc;;;;;;;;AE9ElH,MAAA,eAAA;IACE,OAAS;MACP,cAAgB;MAChB,qBAAuB;MACvB,0BAA4B;MAC5B,sBAAwB;MACxB,cAAgB;IAClB;IACA,UAAY;MACV,QAAU;MACV,QAAU;QACR,SAAW;UACT;UACA;UACA;QACF;QACA,SAAW;UACT;UACA;UACA;QACF;QACA,QAAU;UACR;UACA;UACA;QACF;QACA,QAAU;UACR;UACA;UACA;QACF;QACA,WAAa;UACX;UACA;UACA;QACF;QACA,WAAa;UACX;UACA;UACA;QACF;QACA,OAAS;UACP;UACA;UACA;QACF;QACA,OAAS;UACP;UACA;UACA;QACF;QACA,SAAW;UACT;UACA;UACA;QACF;QACA,SAAW;UACT;UACA;UACA;QACF;MACF;MACA,cAAgB;IAClB;IACA,oBAAsB;MACpB;MACA;IACF;IACA,gBAAkB;MAChB,MAAQ;MACR,MAAQ;MACR,MAAQ;IACV;EACF;ACpCA,MAAM,iBAAwD;IAC5D,MAAM;IACN,KAAK;IACL,MAAM;IACN,OAAO;IACP,QAAQ;IACR,UAAU;IACV,SAAS;IACT,MAAM;IACN,OAAO;IACP,MAAM;IACN,MAAM;EACR;AAEA,MAAM,EAAE,MAAM,IAAI;AAClB,MAAM,YAAY,MAAM,sBAAsB;AAC9C,MAAM,YAAY,MAAM;AAGjB,MAAM,QAAkB;IAC7B,IAAI;IACJ,cAAc,MAAM;IACpB,QAAQ;MACN,KAAK,CAAC,GAAG,MAAM,CAAC;MAChB,OAAO,CAAC,GAAG,MAAM,CAAC;MAClB,MAAM,CAAC,GAAG,MAAM,CAAC;MACjB,MAAM,CAAC,GAAG,MAAM,CAAC;MACjB,KAAK,CAAC,GAAG,MAAM,IAAI;MACnB,WAAW,CAAC,WAAW,WAAW,CAAC;MACnC,WAAW,CAAC,CAAC,WAAW,WAAW,CAAC;MACpC,QAAQ,CAAC,WAAW,YAAY,MAAM,CAAC;MACvC,QAAQ,CAAC,CAAC,WAAW,YAAY,MAAM,CAAC;MACxC,QAAQ,CAAC,WAAW,YAAY,MAAM,CAAC;MACvC,QAAQ,CAAC,CAAC,WAAW,YAAY,MAAM,CAAC;MACxC,MAAM,CAAC,OAAO,KAAK,CAAC;MACpB,MAAM,CAAC,QAAQ,KAAK,CAAC;MACrB,OAAO,CAAC,OAAO,MAAM,CAAC;MACtB,OAAO,CAAC,QAAQ,MAAM,CAAC;MACvB,QAAQ,CAAC,OAAO,KAAK,CAAC;MACtB,QAAQ,CAAC,QAAQ,KAAK,CAAC;IACzB;IACA,eAAe;EACjB;AAEA,MAAM,IAAI,aAAK,SAAS;AACxB,MAAM,QAAQ,CAAC,OAA0B,SAAuB,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,MAAM,MAAM,CAAC,CAAC;AAM7F,MAAM,YAAsB;IACjC,IAAI;IACJ,cAAc,aAAK,SAAS;IAC5B,QAAQ;MACN,KAAK,EAAE;MACP,OAAO,EAAE;MACT,MAAM,EAAE;MACR,MAAM,EAAE;MACR,KAAK,CAAC,GAAG,EAAE,QAAQ,CAAC,IAAI,MAAM,IAAI;MAClC,WAAW,EAAE;MACb,WAAW,EAAE;MACb,QAAQ,EAAE;MACV,QAAQ,EAAE;MACV,QAAQ,MAAM,EAAE,WAAW,GAAG;MAC9B,QAAQ,MAAM,EAAE,WAAW,GAAG;MAC9B,MAAM,EAAE;MACR,MAAM,EAAE;MACR,OAAO,EAAE;MACT,OAAO,EAAE;MACT,QAAQ,MAAM,EAAE,SAAS,IAAI;MAC7B,QAAQ,MAAM,EAAE,SAAS,IAAI;IAC/B;IACA,eAAe;EACjB;AC1FO,MAAM,qBAAqB,CAAQ,YAAgD;AACxF,UAAM,UAAgC,CAAC;AACvC,WAAO;MACL;MACA,IAAI,QAAQ;AACV,eAAO,QAAQ;MACjB;MACA,IAAI,MAAM;AACR,YAAI,QAAQ,SAAS,QAAQ,KAAK;AAChC,kBAAQ,KAAK,EAAE,KAAK,GAAG,KAAK,CAAC;AAC7B,iBAAO;QACT;AACA,YAAI,SAAS;AACb,iBAAS,QAAQ,GAAG,QAAQ,QAAQ,QAAQ,QAAS,KAAK,QAAQ,KAAK,EAAyB,MAAO,QAAQ,MAAM,EAAyB,IAAK,UAAS;AAC5J,cAAM,WAAY,QAAQ,MAAM,EAAyB;AACzD,gBAAQ,MAAM,IAAI,EAAE,KAAK,GAAG,KAAK;AACjC,eAAO;MACT;MACA,OAAO,IAAI;AACT,cAAM,UAAmB,CAAC;AAC1B,iBAAS,QAAQ,GAAG,QAAQ,QAAQ,QAAQ,SAAS;AACnD,gBAAM,QAAQ,QAAQ,KAAK;AAC3B,gBAAM,OAAO;AACb,cAAI,MAAM,OAAO,QAAQ,QAAS;AAClC,kBAAQ,KAAK,MAAM,IAAI;AACvB,kBAAQ,KAAK,IAAI,QAAQ,QAAQ,SAAS,CAAC;AAC3C,kBAAQ,IAAI;AACZ;QACF;AACA,eAAO;MACT;MACA,OAAO,OAAO;AACZ,cAAM,QAAQ,QAAQ,KAAK;AAC3B,YAAI,CAAC,MAAO,QAAO;AACnB,gBAAQ,KAAK,IAAI,QAAQ,QAAQ,SAAS,CAAC;AAC3C,gBAAQ,IAAI;AACZ,eAAO,MAAM;MACf;MACA,QAAQ;AACN,gBAAQ,SAAS;MACnB;IACF;EACF;AE/DA,MAAA,oBAAA;IACE,IAAM;IACN,UAAY;MACV,MAAQ;MACR,KAAO;MACP,KAAO;IACT;IACA,UAAY;MACV,WAAW;QACT,SAAW;QACX,MAAQ;QACR,QAAU;QACV,aAAe;QACf,sBAAwB;QACxB,aAAe;QACf,YAAc;QACd,OAAS;MACX;MACA,QAAQ;QACN,SAAW;QACX,MAAQ;QACR,QAAU;QACV,aAAe;QACf,sBAAwB;QACxB,aAAe;QACf,YAAc;QACd,OAAS;MACX;MACA,eAAe;QACb,SAAW;QACX,MAAQ;QACR,QAAU;QACV,SAAW;QACX,aAAe;QACf,sBAAwB;QACxB,aAAe;QACf,YAAc;QACd,OAAS;MACX;MACA,WAAW;QACT,SAAW;QACX,MAAQ;QACR,QAAU;QACV,aAAe;QACf,sBAAwB;QACxB,aAAe;QACf,YAAc;QACd,OAAS;MACX;IACF;IACA,YAAc;MACZ,MAAQ;MACR,OAAS;MACT,YAAc;MACd,MAAQ;IACV;IACA,mBAAqB;IACrB,SAAW;MACT,aAAe;MACf,cAAgB;IAClB;IACA,eAAiB;IACjB,SAAW;MACT;MACA;MACA;MACA;MACA;MACA;IACF;EACF;AC3DO,MAAM,oBAA0D;IACrE,MAAM,kBAAS,SAAS;IACxB,MAAM,kBAAS,SAAS;IACxB,MAAM,kBAAS,SAAS;IACxB,MAAM,kBAAS,SAAS;IACxB,MAAM,kBAAS,SAAS;EAC1B;ACjBA,MAAA,mBAAA;IACE,UAAY;MACV;QACE,IAAM;QACN,QAAU;QACV,WAAa;QACb,YAAc;QACd,OAAS;MACX;MACA;QACE,IAAM;QACN,QAAU;QACV,WAAa;QACb,YAAc;QACd,OAAS;MACX;MACA;QACE,IAAM;QACN,QAAU;QACV,WAAa;QACb,YAAc;QACd,OAAS;MACX;MACA;QACE,IAAM;QACN,QAAU;QACV,WAAa;QACb,YAAc;QACd,OAAS;MACX;MACA;QACE,IAAM;QACN,QAAU;QACV,WAAa;QACb,YAAc;QACd,OAAS;MACX;MACA;QACE,IAAM;QACN,QAAU;QACV,WAAa;QACb,YAAc;QACd,OAAS;MACX;MACA;QACE,IAAM;QACN,QAAU;QACV,WAAa;QACb,YAAc;QACd,OAAS;MACX;MACA;QACE,IAAM;QACN,QAAU;QACV,WAAa;QACb,YAAc;QACd,OAAS;MACX;MACA;QACE,IAAM;QACN,QAAU;QACV,WAAa;QACb,YAAc;QACd,OAAS;MACX;MACA;QACE,IAAM;QACN,QAAU;QACV,WAAa;QACb,YAAc;QACd,OAAS;MACX;MACA;QACE,IAAM;QACN,QAAU;QACV,WAAa;QACb,YAAc;QACd,OAAS;MACX;IACF;IACA,SAAW;MACT,MAAQ;QACN,WAAa;QACb,YAAc;QACd,OAAS;MACX;MACA,KAAO;QACL,WAAa;QACb,YAAc;QACd,OAAS;MACX;MACA,KAAO;QACL,WAAa;QACb,YAAc;QACd,OAAS;MACX;IACF;IACA,gBAAkB;MAChB,SAAW;QACT,GAAK;QACL,KAAO;QACP,KAAO;MACT;MACA,OAAS;QACP,GAAK;QACL,KAAO;QACP,KAAO;MACT;MACA,UAAY;QACV,GAAK;QACL,KAAO;QACP,KAAO;MACT;MACA,WAAa;QACX,GAAK;QACL,KAAO;QACP,KAAO;MACT;MACA,SAAW;QACT,GAAK;QACL,KAAO;QACP,KAAO;MACT;IACF;IACA,QAAU;MACR,UAAY;QACV,MAAQ;UACN,IAAM;UACN,QAAU;QACZ;QACA,OAAS;UACP,IAAM;QACR;QACA,QAAU;UACR,IAAM;QACR;QACA,UAAY;UACV,IAAM;UACN,QAAU;QACZ;QACA,SAAW;UACT,IAAM;UACN,QAAU;QACZ;QACA,MAAQ;UACN,IAAM;UACN,QAAU;QACZ;QACA,OAAS;UACP,IAAM;UACN,QAAU;QACZ;QACA,MAAQ;UACN,IAAM;UACN,QAAU;QACZ;MACF;MACA,OAAS;QACP,UAAY;UACV;UACA;QACF;QACA,SAAW;UACT;QACF;QACA,OAAS;UACP;QACF;MACF;MACA,OAAS;QACP,UAAY;QACZ,SAAW;QACX,MAA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","names":["isArm","MISSING_BITS","__export","GROUND_MASS_FACTOR","part","isOpen","frame","PAIRS_PER_AGENT","CENTRE_HEIGHT","identityOf","slot","GRAVITY_FORCE","PAIRS_PER_AGENT","ec","c"]}
|