@adatechnology/game-crowd 0.3.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/physics/offload/frame.ts","../src/grid.ts","../src/identity.ts","../src/types.ts","../src/physics/contact.ts","../src/physics/pressure.ts","../src/physics/offload/kernel.ts","../src/physics/offload/worker-host.ts","../src/physics/offload/worker-entry.ts"],"sourcesContent":["/**\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","/** 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 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 { 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 { attachPhysicsWorker } from './worker-host'\n\ntype WorkerScope = { postMessage(message: unknown, transfer?: ArrayBuffer[]): void; addEventListener(type: 'message', listener: (event: { data: unknown }) => void): void }\n\n// the file a browser `Worker` loads (`dist/physics-worker.js`); outside a worker scope it does nothing\nconst scope = globalThis as unknown as Partial<WorkerScope> & { WorkerGlobalScope?: unknown }\nif (typeof scope.WorkerGlobalScope !== 'undefined' && scope.postMessage && scope.addEventListener) {\n const self = scope as WorkerScope\n attachPhysicsWorker({ postMessage: (message, transfer) => self.postMessage(message, transfer), onMessage: (listener) => self.addEventListener('message', (event) => listener(event.data)) })\n}\n"],"mappings":";AAKA,IAAM,aAAa,CAAC,KAAK,KAAK,KAAK,MAAM,MAAM,SAAS,SAAS,QAAQ,SAAS,QAAQ,QAAQ,eAAe,WAAW;AAC5H,IAAM,YAAY,CAAC,QAAQ,WAAW,SAAS,MAAM;AACrD,IAAM,cAAc,CAAC,UAAU,UAAU,cAAc,cAAc,WAAW,WAAW,SAAS,SAAS,UAAU;AAGhH,IAAM,SAAS,EAAE,GAAG,GAAG,GAAG,GAAG,UAAU,GAAG,SAAS,GAAG,SAAS,GAAG,UAAU,GAAG,YAAY,GAAG,YAAY,GAAG,YAAY,GAAG,SAAS,GAAG,UAAU,IAAI,MAAM,GAAG;AAatK,IAAM,SAAS,CAAC,UAA0B,KAAK,KAAK,QAAQ,CAAC,IAAI;AAK1D,SAAS,YAAY,QAAyB,OAAiC;AACpF,QAAM,EAAE,UAAU,MAAM,IAAI;AAC5B,MAAI,SAAS;AACb,QAAM,OAAO,CAAI,MAAsE,iBAAyB,WAAsB;AACpI,UAAM,OAAO,KAAK,QAAQ,QAAQ,MAAM;AACxC,cAAU,OAAO,SAAS,eAAe;AACzC,WAAO;AAAA,EACT;AACA,QAAM,MAAM,CAAC,GAAoB,GAAW,MAA4B,IAAI,aAAa,GAAG,GAAG,CAAC;AAChG,QAAM,MAAM,CAAC,GAAoB,GAAW,MAA4B,IAAI,aAAa,GAAG,GAAG,CAAC;AAChG,QAAM,KAAK,CAAC,GAAoB,GAAW,MAA0B,IAAI,WAAW,GAAG,GAAG,CAAC;AAC3F,QAAM,SAAS,KAAK,KAAK,GAAG,OAAO,IAAI;AACvC,QAAM,SAAS,CAAC;AAChB,aAAW,OAAO,CAAC,GAAG,YAAY,GAAG,WAAW,EAAG,QAAO,GAAG,IAAI,KAAK,KAAK,GAAG,QAAQ;AACtF,QAAM,WAAW,KAAK,CAAC,GAAG,GAAG,MAAM,IAAI,YAAY,GAAG,GAAG,CAAC,GAAG,GAAG,QAAQ;AACxE,QAAM,WAAW,KAAK,CAAC,GAAG,GAAG,MAAM,IAAI,WAAW,GAAG,GAAG,CAAC,GAAG,GAAG,QAAQ;AACvE,QAAM,QAAQ,CAAC;AACf,aAAW,OAAO,UAAW,OAAM,GAAG,IAAI,KAAK,IAAI,GAAG,QAAQ;AAC9D,QAAM,MAAM,KAAK,IAAI,GAAG,KAAK;AAC7B,SAAO,EAAE,QAAQ,KAAK,QAA+B,IAAI,OAA6B,UAAU,UAAU,IAAI;AAChH;;;ACXO,IAAM,eACX,CAAC,MAAc,MAAc,MAC7B,CAAC,GAAW,MAAsB;AAChC,QAAM,KAAK,KAAK,OAAO,IAAI,QAAQ,IAAI;AACvC,QAAM,KAAK,KAAK,OAAO,IAAI,QAAQ,IAAI;AACvC,SAAO,KAAK,KAAK,KAAK,KAAK,MAAM,KAAK,MAAM,IAAI,KAAK,KAAK,IAAI;AAChE;;;AC1CK,IAAM,QAAQ,CAAC,UAA0B;AAC9C,MAAI,OAAO,UAAU;AACrB,UAAQ,SAAS;AACjB,SAAO,KAAK,KAAK,MAAM,UAAU;AACjC,UAAQ,SAAS;AACjB,SAAO,KAAK,KAAK,MAAM,UAAU;AACjC,UAAQ,SAAS;AACjB,SAAO,SAAS;AAClB;AAMO,IAAM,OAAO,CAAC,UAAkB,MAAc,UAA0B,MAAO,MAAM,WAAW,IAAI,IAAI,KAAK,KAAK,OAAO,GAAG,UAAU,MAAO,CAAC,MAAM,KAAK;AAMzJ,IAAM,OAAO;AAAA,EAClB,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,KAAK;AAAA,EACL,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,OAAO;AAAA,EACP,UAAU;AAAA,EACV,WAAW;AAAA,EACX,aAAa;AAAA,EACb,OAAO;AAAA,EACP,SAAS;AAAA,EACT,YAAY;AAAA,EACZ,MAAM;AAAA,EACN,MAAM;AACR;;;ACrCO,IAAM,QAAQ;AAAA,EACnB,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,MAAM;AAAA,EACN,SAAS;AAAA,EACT,SAAS;AAAA,EACT,UAAU;AAAA,EACV,SAAS;AAAA,EACT,UAAU;AAAA,EACV,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,WAAW;AAAA,EACX,eAAe;AACjB;AAGO,IAAM,UAAU,EAAE,UAAU,GAAG,YAAY,GAAG,QAAQ,GAAG,YAAY,GAAG,QAAQ,EAAE;;;ACVzF,IAAM,qBAAqB;AAC3B,IAAM,cAAc;AACpB,IAAM,gBAAgB;AACtB,IAAM,kBAAkB;AACxB,IAAM,eAAe;AAsCd,IAAM,yBAAyB,CAAC,UAAkB,UAAyC;AAAA,EAChG,MAAM,IAAI,WAAW,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,EAAE,KAAK,EAAE;AAAA,EACxD,MAAM,IAAI,WAAW,QAAQ;AAAA,EAC7B,SAAS,IAAI,WAAW,QAAQ;AAAA,EAChC,cAAc;AAAA,EACd,OAAO,IAAI,WAAW,WAAW,eAAe;AAAA,EAChD,OAAO,IAAI,WAAW,WAAW,eAAe;AAAA,EAChD,WAAW;AAAA,EACX,QAAQ,IAAI,aAAa,QAAQ;AAAA,EACjC,QAAQ,IAAI,aAAa,QAAQ;AAAA,EACjC,OAAO,IAAI,WAAW,QAAQ;AAAA,EAC9B,aAAa,IAAI,aAAa,QAAQ;AAAA,EACtC,aAAa,IAAI,aAAa,QAAQ;AAAA,EACtC,OAAO,IAAI,aAAa,QAAQ;AAAA,EAChC,WAAW,IAAI,WAAW,QAAQ;AAAA,EAClC,UAAU,IAAI,aAAa,QAAQ;AAAA,EACnC,QAAQ,IAAI,aAAa,QAAQ;AAAA,EACjC,WAAW,IAAI,aAAa,WAAW,eAAe;AAAA,EACtD,cAAc,IAAI,aAAa,WAAW,eAAe;AAAA,EACzD,aAAa,IAAI,aAAa,WAAW,eAAe;AAAA,EACxD,aAAa,IAAI,aAAa,WAAW,eAAe;AAC1D;AAKO,IAAM,qBAAqB,CAAC,OAAqB,MAAuB,MAAM,KAAK,CAAC,MAAM,KAAK,MAAM,QAAQ,CAAC,MAAM,QAAQ,UAAU,MAAM,MAAM,CAAC,MAAM,MAAM,aAAa,MAAM,MAAM,CAAC,MAAM,MAAM;AAE5M,IAAM,aAAa,CAAC,OAAqB,MAAuB,MAAM,QAAQ,CAAC,MAAM,QAAQ,UAAU,MAAM,QAAQ,CAAC,MAAM,QAAQ;AAEpI,IAAM,gBAAgB,CAAC,OAAqB,MAAsB,MAAM,KAAK,CAAC,KAAM,WAAW,OAAO,CAAC,IAAI,qBAAqB;AAEhI,IAAM,aAAa,CAAC,OAAqB,GAAW,WAA2B,SAAS,MAAM,MAAM,CAAC;AAGrG,SAAS,WAAW,MAAmB,QAAsB;AAC3D,QAAM,EAAE,OAAO,UAAU,IAAI;AAC7B,QAAM,EAAE,WAAW,UAAU,OAAO,IAAI;AACxC,WAAS,IAAI,GAAG,IAAI,KAAK,GAAG,KAAK;AAC/B,UAAM,QAAQ,mBAAmB,OAAO,CAAC;AACzC,cAAU,CAAC,IAAI,QAAQ,IAAI;AAC3B,aAAS,CAAC,IAAI,WAAW,OAAO,GAAG,MAAM;AACzC,WAAO,CAAC,IAAI,cAAc,OAAO,CAAC;AAAA,EACpC;AACF;AAGO,SAAS,YAAY,MAAyB;AACnD,QAAM,EAAE,OAAO,MAAM,UAAU,IAAI;AACnC,QAAM,OAAO,KAAK,MAAM,KAAK,IAAI;AACjC,WAAS,IAAI,GAAG,IAAI,UAAU,cAAc,IAAK,WAAU,KAAK,UAAU,QAAQ,CAAC,CAAE,IAAI;AACzF,YAAU,eAAe;AACzB,WAAS,IAAI,GAAG,IAAI,KAAK,GAAG,KAAK;AAC/B,QAAI,CAAC,UAAU,UAAU,CAAC,EAAG;AAC7B,UAAM,KAAK,KAAK,MAAM,MAAM,EAAE,CAAC,IAAK,KAAK,IAAI;AAC7C,UAAM,KAAK,KAAK,MAAM,MAAM,EAAE,CAAC,IAAK,KAAK,IAAI;AAC7C,QAAI,KAAK,KAAK,KAAK,KAAK,MAAM,QAAQ,MAAM,KAAM;AAClD,UAAM,OAAO,KAAK,OAAO;AACzB,QAAI,UAAU,KAAK,IAAI,MAAM,GAAI,WAAU,QAAQ,UAAU,cAAc,IAAI;AAC/E,cAAU,KAAK,CAAC,IAAI,UAAU,KAAK,IAAI;AACvC,cAAU,KAAK,IAAI,IAAI;AAAA,EACzB;AACF;AAGA,IAAM,gBAAgB,CAAC,GAAG,GAAG,IAAI,GAAG,GAAG,GAAG,GAAG,CAAC;AAE9C,SAAS,eAAe,MAAmB,GAAW,MAAc,UAAyB;AAC3F,QAAM,EAAE,OAAO,UAAU,IAAI;AAC7B,QAAM,EAAE,MAAM,MAAM,UAAU,OAAO,MAAM,IAAI;AAC/C,QAAM,EAAE,GAAG,GAAG,EAAE,IAAI;AACpB,QAAM,KAAK,EAAE,CAAC;AACd,QAAM,KAAK,EAAE,CAAC;AACd,QAAM,KAAK,EAAE,CAAC;AACd,QAAM,YAAY,SAAS,CAAC,IAAK;AACjC,WAAS,IAAI,KAAK,IAAI,GAAI,MAAM,IAAI,IAAI,KAAK,CAAC,GAAI;AAChD,QAAK,YAAY,KAAK,KAAM,KAAK,IAAI,KAAK,EAAE,CAAC,CAAE,IAAI,cAAe;AAClE,UAAM,QAAQ,YAAY,SAAS,CAAC;AACpC,UAAM,KAAK,EAAE,CAAC,IAAK;AACnB,UAAM,KAAK,EAAE,CAAC,IAAK;AACnB,QAAI,KAAK,KAAK,KAAK,MAAM,QAAQ,SAAS,UAAU,aAAa,MAAM,OAAQ;AAC/E,UAAM,UAAU,SAAS,IAAI;AAC7B,UAAM,UAAU,WAAW,IAAI;AAAA,EACjC;AACF;AAGA,SAAS,aAAa,MAAyB;AAC7C,QAAM,EAAE,OAAO,MAAM,UAAU,IAAI;AACnC,QAAM,OAAO,KAAK,MAAM,KAAK,IAAI;AACjC,YAAU,YAAY;AACtB,WAAS,IAAI,GAAG,IAAI,KAAK,GAAG,KAAK;AAC/B,QAAI,CAAC,UAAU,UAAU,CAAC,EAAG;AAC7B,UAAM,KAAK,KAAK,MAAM,MAAM,EAAE,CAAC,IAAK,KAAK,IAAI;AAC7C,UAAM,KAAK,KAAK,MAAM,MAAM,EAAE,CAAC,IAAK,KAAK,IAAI;AAC7C,QAAI,KAAK,KAAK,KAAK,KAAK,MAAM,QAAQ,MAAM,KAAM;AAClD,mBAAe,MAAM,GAAG,KAAK,OAAO,IAAI,IAAI;AAC5C,aAAS,IAAI,GAAG,IAAI,cAAc,QAAQ,KAAK,GAAG;AAChD,YAAM,KAAK,KAAK,cAAc,CAAC;AAC/B,YAAM,KAAK,KAAK,cAAc,IAAI,CAAC;AACnC,UAAI,MAAM,KAAK,MAAM,KAAK,KAAK,QAAQ,KAAK,KAAM,gBAAe,MAAM,GAAG,KAAK,OAAO,IAAI,KAAK;AAAA,IACjG;AAAA,EACF;AACF;AAGA,SAAS,aAAa,MAAyB;AAC7C,QAAM,EAAE,OAAO,UAAU,IAAI;AAC7B,QAAM,EAAE,OAAO,OAAO,UAAU,WAAW,cAAc,aAAa,YAAY,IAAI;AACtF,QAAM,EAAE,GAAG,GAAG,SAAS,IAAI;AAC3B,WAAS,IAAI,GAAG,IAAI,UAAU,WAAW,KAAK;AAC5C,UAAM,IAAI,MAAM,CAAC;AACjB,UAAM,IAAI,MAAM,CAAC;AACjB,UAAM,QAAQ,SAAS,CAAC,IAAK,SAAS,CAAC;AACvC,UAAM,KAAK,EAAE,CAAC,IAAK,EAAE,CAAC;AACtB,UAAM,KAAK,EAAE,CAAC,IAAK,EAAE,CAAC;AACtB,UAAM,WAAW,KAAK,KAAK,KAAK,KAAK,KAAK,EAAE;AAC5C,cAAU,CAAC,IAAI;AACf,iBAAa,CAAC,IAAI;AAClB,QAAI,YAAY,MAAO;AACvB,QAAI,WAAW,cAAc;AAC3B,kBAAY,CAAC,IAAI,KAAK;AACtB,kBAAY,CAAC,IAAI,KAAK;AACtB;AAAA,IACF;AACA,UAAM,QAAQ,IAAI,KAAK,KAAK,KAAK,SAAS,CAAC,IAAK,SAAS,CAAC,GAAI,GAAG,KAAK,IAAI;AAC1E,gBAAY,CAAC,IAAI,KAAK,IAAI,KAAK;AAC/B,gBAAY,CAAC,IAAI,KAAK,IAAI,KAAK;AAAA,EACjC;AACF;AAGA,SAAS,aAAa,OAAqB,WAA6B,GAAiB;AACvF,MAAI,UAAU,aAAa,CAAC,KAAM,UAAU,UAAU,CAAC,EAAI;AAC3D,QAAM,IAAI,UAAU,MAAM,CAAC;AAC3B,QAAM,IAAI,UAAU,MAAM,CAAC;AAC3B,QAAM,KAAK,UAAU,YAAY,CAAC;AAClC,QAAM,KAAK,UAAU,YAAY,CAAC;AAClC,QAAM,EAAE,IAAI,IAAI,OAAO,MAAM,IAAI;AACjC,QAAM,WAAW,GAAG,CAAC,IAAK,MAAM,CAAC,IAAK,GAAG,CAAC,IAAK,MAAM,CAAC,KAAM,MAAM,GAAG,CAAC,IAAK,MAAM,CAAC,IAAK,GAAG,CAAC,IAAK,MAAM,CAAC,KAAM;AAC7G,MAAI,WAAW,EAAG;AAClB,QAAM,QAAQ,UAAU,OAAO,CAAC;AAChC,QAAM,QAAQ,UAAU,OAAO,CAAC;AAChC,QAAM,UAAW,CAAC,UAAU,QAAQ,SAAU,QAAQ;AACtD,QAAM,CAAC,KAAO,UAAU,KAAM;AAC9B,QAAM,CAAC,KAAO,UAAU,KAAM;AAC9B,QAAM,CAAC,KAAO,UAAU,KAAM;AAC9B,QAAM,CAAC,KAAO,UAAU,KAAM;AAChC;AAEA,SAAS,qBAAqB,WAA6B,GAAW,YAA0B;AAC9F,QAAM,WAAW,UAAU,aAAa,CAAC;AACzC,QAAM,QAAQ,UAAU,UAAU,CAAC;AACnC,MAAI,YAAY,MAAO;AACvB,QAAM,IAAI,UAAU,MAAM,CAAC;AAC3B,QAAM,IAAI,UAAU,MAAM,CAAC;AAC3B,QAAM,QAAQ,UAAU,OAAO,CAAC;AAChC,QAAM,QAAQ,UAAU,OAAO,CAAC;AAChC,QAAM,OAAO,cAAc,QAAQ;AACnC,QAAM,KAAK,UAAU,YAAY,CAAC;AAClC,QAAM,KAAK,UAAU,YAAY,CAAC;AAClC,YAAU,OAAO,CAAC,KAAM,OAAO,MAAM,SAAS,QAAQ;AACtD,YAAU,OAAO,CAAC,KAAM,OAAO,MAAM,SAAS,QAAQ;AACtD,YAAU,OAAO,CAAC,KAAM,OAAO,MAAM,SAAS,QAAQ;AACtD,YAAU,OAAO,CAAC,KAAM,OAAO,MAAM,SAAS,QAAQ;AACtD,YAAU,MAAM,CAAC;AACjB,YAAU,MAAM,CAAC;AACnB;AAEA,IAAM,SAAS,CAAC,MAAmB,GAAW,MAAuB;AACnE,QAAM,OAAO,KAAK,OAAO,GAAG,CAAC;AAC7B,SAAO,QAAQ,MAAM,KAAK,IAAI,IAAI,MAAM,KAAK,KAAK,IAAI,IAAI,MAAM;AAClE;AAGA,SAAS,gBAAgB,MAAmB,OAAe,QAA6B;AACtF,QAAM,EAAE,OAAO,MAAM,UAAU,IAAI;AACnC,QAAM,QAAQ,UAAU,MAAM,KAAK;AACnC,MAAI,CAAC,MAAO;AACZ,YAAU,MAAM,KAAK,IAAI;AACzB,QAAM,MAAM,OAAO;AACnB,QAAM,KAAK,KAAK,IAAI,CAAC,KAAK,KAAK,IAAI,KAAK,UAAU,OAAO,KAAK,IAAK,KAAK,CAAC;AACzE,QAAM,KAAK,KAAK,IAAI,CAAC,KAAK,KAAK,IAAI,KAAK,UAAU,OAAO,KAAK,IAAK,KAAK,CAAC;AACzE,MAAI,OAAO,MAAM,MAAM,EAAE,KAAK,IAAK,IAAI,MAAM,EAAE,KAAK,CAAE,EAAG,OAAM,EAAE,KAAK,KAAM;AAC5E,MAAI,OAAO,MAAM,MAAM,EAAE,KAAK,GAAI,MAAM,EAAE,KAAK,IAAK,EAAE,EAAG,OAAM,EAAE,KAAK,KAAM;AAC9E;AAEA,SAAS,gBAAgB,WAA6B,OAAqB;AACzE,YAAU,OAAO,KAAK,IAAI;AAC1B,YAAU,OAAO,KAAK,IAAI;AAC1B,YAAU,MAAM,KAAK,IAAI;AAC3B;AAEA,SAAS,iBAAiB,MAAmB,QAA6B;AACxE,QAAM,EAAE,UAAU,IAAI;AACtB,WAAS,IAAI,GAAG,IAAI,UAAU,WAAW,KAAK;AAC5C,oBAAgB,WAAW,UAAU,MAAM,CAAC,CAAE;AAC9C,oBAAgB,WAAW,UAAU,MAAM,CAAC,CAAE;AAAA,EAChD;AACA,WAAS,IAAI,GAAG,IAAI,UAAU,WAAW,IAAK,sBAAqB,WAAW,GAAG,OAAO,UAAU;AAClG,WAAS,IAAI,GAAG,IAAI,UAAU,WAAW,KAAK;AAC5C,oBAAgB,MAAM,UAAU,MAAM,CAAC,GAAI,MAAM;AACjD,oBAAgB,MAAM,UAAU,MAAM,CAAC,GAAI,MAAM;AAAA,EACnD;AACF;AAEA,SAAS,gBAAgB,MAAmB,KAA0B;AACpE,QAAM,EAAE,OAAO,UAAU,IAAI;AAC7B,WAAS,IAAI,GAAG,IAAI,UAAU,WAAW,KAAK;AAC5C,UAAM,IAAI,UAAU,MAAM,CAAC;AAC3B,UAAM,IAAI,UAAU,MAAM,CAAC;AAE3B,QAAI,WAAW,OAAO,CAAC,KAAK,WAAW,OAAO,CAAC,EAAG;AAClD,UAAM,QAAQ,UAAU,SAAS,CAAC,IAAK,UAAU,SAAS,CAAC;AAC3D,UAAM,UAAU,QAAQ,KAAK,MAAM,MAAM,EAAE,CAAC,IAAK,MAAM,EAAE,CAAC,GAAI,MAAM,EAAE,CAAC,IAAK,MAAM,EAAE,CAAC,CAAE;AACvF,QAAI,WAAW,EAAG;AAClB,QAAI;AACJ,QAAI,cAAc;AAClB,QAAI,UAAU,IAAI,WAAY,KAAI,aAAa;AAAA,EACjD;AACF;AAGA,SAAS,SAAS,MAAmB,QAA6B;AAChE,QAAM,EAAE,OAAO,UAAU,IAAI;AAC7B,QAAM,SAAS,KAAK,IAAI,CAAC,OAAO,WAAW,OAAO,CAAC;AACnD,WAAS,IAAI,GAAG,IAAI,KAAK,GAAG,KAAK;AAC/B,cAAU,MAAM,CAAC,IAAI,KAAK,MAAM,MAAM,MAAM,CAAC,IAAK,UAAU,YAAY,CAAC,GAAI,MAAM,MAAM,CAAC,IAAK,UAAU,YAAY,CAAC,CAAE;AACxH,UAAM,MAAM,CAAC,KAAM;AACnB,UAAM,MAAM,CAAC,KAAM;AACnB,UAAM,QAAQ,KAAK,MAAM,MAAM,MAAM,CAAC,GAAI,MAAM,MAAM,CAAC,CAAE;AACzD,QAAI,SAAS,OAAO,QAAS;AAC7B,UAAM,MAAM,CAAC,KAAM,OAAO,UAAU;AACpC,UAAM,MAAM,CAAC,KAAM,OAAO,UAAU;AAAA,EACtC;AACF;AAaO,SAAS,YAAY,MAAmB,QAAuB,KAA0B;AAC9F,QAAM,EAAE,OAAO,UAAU,IAAI;AAC7B,MAAI,WAAW;AACf,MAAI,aAAa;AACjB,MAAI,aAAa;AACjB,WAAS,IAAI,GAAG,IAAI,KAAK,GAAG,KAAK;AAC/B,cAAU,YAAY,CAAC,IAAI,MAAM,MAAM,CAAC;AACxC,cAAU,YAAY,CAAC,IAAI,MAAM,MAAM,CAAC;AAAA,EAC1C;AACA,aAAW,MAAM,OAAO,MAAM;AAC9B,cAAY,IAAI;AAChB,eAAa,IAAI;AACjB,WAAS,YAAY,GAAG,YAAY,OAAO,YAAY,aAAa;AAClE,iBAAa,IAAI;AACjB,aAAS,IAAI,GAAG,IAAI,UAAU,WAAW,IAAK,cAAa,OAAO,WAAW,CAAC;AAC9E,aAAS,IAAI,UAAU,YAAY,GAAG,KAAK,GAAG,IAAK,cAAa,OAAO,WAAW,CAAC;AACnF,qBAAiB,MAAM,MAAM;AAAA,EAC/B;AACA,kBAAgB,MAAM,GAAG;AACzB,WAAS,MAAM,MAAM;AACvB;;;ACvTA,IAAM,cAAc;AAEpB,IAAM,aAAa;AAEnB,IAAM,eAAe;AACrB,IAAM,wBAAwB,eAAe;AAmCtC,IAAM,0BAA0B,CAAC,UAAkB,kBAA6C;AAAA,EACrG,WAAW,IAAI,aAAa,QAAQ;AAAA,EACpC,UAAU,IAAI,WAAW,QAAQ;AAAA,EACjC,UAAU,IAAI,WAAW,QAAQ;AAAA,EACjC,UAAU,IAAI,WAAW,QAAQ;AAAA,EACjC,cAAc,IAAI,aAAa,QAAQ;AAAA,EACvC,WAAW,IAAI,aAAa,QAAQ;AAAA,EACpC,MAAM,IAAI,aAAa,QAAQ;AAAA,EAC/B,MAAM,IAAI,aAAa,QAAQ;AAAA,EAC/B,mBAAmB,IAAI,aAAa,YAAY;AAAA,EAChD,oBAAoB,IAAI,aAAa,YAAY;AAAA,EACjD,UAAU,IAAI,WAAW,QAAQ;AACnC;AAEA,IAAM,aAAa,CAAC,OAAqB,MAAuB,mBAAmB,OAAO,CAAC,MAAM,MAAM,QAAQ,CAAC,MAAM,QAAQ,YAAY,MAAM,QAAQ,CAAC,MAAM,QAAQ;AAGvK,SAAS,aAAa,MAAmB,WAAoC;AAC3E,WAAS,IAAI,GAAG,IAAI,KAAK,GAAG,IAAK,WAAU,SAAS,CAAC,IAAI,WAAW,KAAK,OAAO,CAAC,IAAI,IAAI;AAC3F;AAEA,IAAM,cAAc,CAAC,WAAmB,SAA0B,YAAY,QAAQ,YAAY,SAAS,IAAI,QAAQ;AAEvH,IAAM,SAAS,CAAC,OAAqB,MAAuB,MAAM,KAAK,CAAC,MAAM,KAAK,MAAM,KAAK,CAAC,MAAM;AAGrG,SAAS,QAAQ,OAAqB,WAA8B,GAAW,GAAW,IAAY,IAAkB;AACtH,QAAM,UAAU,MAAM,GAAG,CAAC,IAAK,MAAM,GAAG,CAAC,KAAM,MAAM,MAAM,GAAG,CAAC,IAAK,MAAM,GAAG,CAAC,KAAM;AACpF,MAAI,UAAU,UAAU,UAAU,CAAC,EAAI;AACvC,YAAU,UAAU,CAAC,IAAI;AACzB,YAAU,KAAK,CAAC,IAAI;AACpB,YAAU,KAAK,CAAC,IAAI;AACtB;AAGA,SAAS,WAAW,MAAmB,WAA8B,QAA8B;AACjG,QAAM,EAAE,MAAM,IAAI;AAClB,WAAS,IAAI,GAAG,IAAI,KAAK,UAAU,WAAW,KAAK;AACjD,cAAU,kBAAkB,CAAC,IAAI;AACjC,cAAU,mBAAmB,CAAC,IAAI;AAClC,UAAM,IAAI,KAAK,UAAU,MAAM,CAAC;AAChC,UAAM,IAAI,KAAK,UAAU,MAAM,CAAC;AAChC,QAAI,CAAC,UAAU,SAAS,CAAC,KAAK,CAAC,UAAU,SAAS,CAAC,EAAG;AACtD,UAAM,KAAK,MAAM,EAAE,CAAC,IAAK,MAAM,EAAE,CAAC;AAClC,UAAM,KAAK,MAAM,EAAE,CAAC,IAAK,MAAM,EAAE,CAAC;AAClC,UAAM,WAAW,KAAK,KAAK,KAAK,KAAK,KAAK,EAAE;AAC5C,QAAI,WAAW,QAAQ,YAAY,MAAM,MAAM,CAAC,IAAK,MAAM,MAAM,CAAC,KAAM,OAAO,SAAS,YAAa;AACrG,UAAM,KAAK,KAAK;AAChB,UAAM,KAAK,KAAK;AAChB,UAAM,UAAU,YAAY,MAAM,KAAK,CAAC,IAAK,KAAK,MAAM,KAAK,CAAC,IAAK,IAAI,OAAO,SAAS;AACvF,UAAM,WAAW,YAAY,EAAE,MAAM,KAAK,CAAC,IAAK,KAAK,MAAM,KAAK,CAAC,IAAK,KAAK,OAAO,SAAS;AAC3F,QAAI,UAAU,GAAG;AACf,gBAAU,SAAS,CAAC,IAAI;AACxB,cAAQ,OAAO,WAAW,GAAG,GAAG,IAAI,EAAE;AAAA,IACxC;AACA,QAAI,WAAW,GAAG;AAChB,gBAAU,SAAS,CAAC,IAAI;AACxB,cAAQ,OAAO,WAAW,GAAG,GAAG,CAAC,IAAI,CAAC,EAAE;AAAA,IAC1C;AAEA,QAAI,UAAU,KAAK,WAAW,EAAG;AACjC,cAAU,kBAAkB,CAAC,IAAI;AACjC,cAAU,mBAAmB,CAAC,IAAI;AAClC,cAAU,UAAU,CAAC,KAAM;AAC3B,cAAU,UAAU,CAAC,KAAM;AAAA,EAC7B;AACF;AAGA,SAAS,WAAW,MAAmB,WAA8B,QAAwB,QAAyC;AACpI,QAAM,EAAE,OAAO,KAAK,IAAI;AACxB,WAAS,IAAI,GAAG,IAAI,KAAK,GAAG,KAAK;AAC/B,QAAI,CAAC,UAAU,SAAS,CAAC,KAAK,CAAC,OAAO,OAAO,CAAC,EAAG;AACjD,UAAM,QAAQ,MAAM,MAAM,CAAC,IAAK,OAAO,SAAS;AAChD,UAAM,OAAO,KAAK,OAAO,MAAM,EAAE,CAAC,IAAK,MAAM,KAAK,CAAC,IAAK,OAAO,MAAM,EAAE,CAAC,IAAK,MAAM,KAAK,CAAC,IAAK,KAAK;AACnG,QAAI,QAAQ,MAAM,KAAK,IAAI,IAAI,MAAM,KAAK,KAAK,IAAI,IAAI,MAAM,GAAI;AACjE,cAAU,SAAS,CAAC,IAAI;AACxB,cAAU,SAAS,CAAC,IAAI,QAAQ,KAAK,KAAK,IAAI,IAAI,MAAM,IAAI,OAAO,IAAI,IAAI;AAC3E,cAAU,SAAS,CAAC,IAAI;AACxB,cAAU,UAAU,CAAC,KAAM;AAAA,EAC7B;AACF;AAGA,SAAS,YAAY,MAAmB,WAA8B,QAAwB,QAA0D;AACtJ,QAAM,EAAE,MAAM,IAAI;AAClB,WAAS,IAAI,GAAG,IAAI,KAAK,GAAG,KAAK;AAC/B,QAAI,CAAC,UAAU,SAAS,CAAC,EAAG;AAC5B,UAAM,KAAK,OAAO,IAAI,MAAM,EAAE,CAAC;AAC/B,UAAM,KAAK,OAAO,IAAI,MAAM,EAAE,CAAC;AAC/B,UAAM,UAAU,KAAK,KAAK,KAAK;AAC/B,QAAI,UAAU,yBAAyB,UAAU,KAAM;AACvD,UAAM,WAAW,KAAK,KAAK,OAAO;AAClC,UAAM,SAAS,OAAO,OAAO,CAAC,IAAI,aAAa,MAAM,KAAK,CAAC,IAAK,KAAK,MAAM,KAAK,CAAC,IAAK,MAAM,UAAU,OAAO,SAAS,IAAI;AAC1H,cAAU,aAAa,CAAC,IAAI;AAC5B,QAAI,SAAS,EAAG,WAAU,SAAS,CAAC,IAAI;AACxC,cAAU,UAAU,CAAC,KAAM;AAAA,EAC7B;AACF;AAEA,SAAS,eAAe,MAAmB,WAAoC;AAC7E,QAAM,EAAE,MAAM,IAAI;AAClB,WAAS,IAAI,GAAG,IAAI,KAAK,GAAG,KAAK;AAC/B,cAAU,UAAU,CAAC,IAAI;AACzB,cAAU,SAAS,CAAC,IAAI;AACxB,cAAU,SAAS,CAAC,IAAI;AACxB,cAAU,aAAa,CAAC,IAAI;AAC5B,cAAU,UAAU,CAAC,IAAI;AACzB,UAAM,MAAM,CAAC,IAAI;AAAA,EACnB;AACF;AAGA,SAAS,SAAS,OAAqB,SAA0B,WAA8B,GAAmB;AAChH,MAAI,CAAC,OAAO,OAAO,CAAC,KAAK,CAAC,UAAU,SAAS,CAAC,EAAG,QAAO;AACxD,SAAO,QAAQ,iBAAiB,MAAM,KAAK,CAAC,CAAE,IAAK,MAAM,YAAY,CAAC;AACxE;AAGA,SAAS,YAAY,MAAmB,WAA8B,QAAwB,SAA0B,KAA2B;AACjJ,QAAM,EAAE,MAAM,IAAI;AAClB,WAAS,IAAI,GAAG,IAAI,KAAK,GAAG,KAAK;AAC/B,QAAI,CAAC,UAAU,SAAS,CAAC,EAAG;AAC5B,UAAM,MAAM,CAAC,IAAI,SAAS,OAAO,SAAS,WAAW,CAAC;AACtD,QAAI,MAAM,MAAM,CAAC,IAAK,EAAG,KAAI;AAAA,EAC/B;AACA,WAAS,IAAI,GAAG,IAAI,KAAK,UAAU,WAAW,KAAK;AACjD,UAAM,IAAI,KAAK,UAAU,MAAM,CAAC;AAChC,UAAM,IAAI,KAAK,UAAU,MAAM,CAAC;AAChC,UAAM,UAAU,UAAU,kBAAkB,CAAC;AAC7C,UAAM,WAAW,UAAU,mBAAmB,CAAC;AAC/C,QAAI,UAAU,EAAG,OAAM,MAAM,CAAC,KAAO,OAAO,eAAe,MAAM,UAAU,CAAC,IAAK,UAAU,UAAW,UAAU,UAAU,CAAC;AAC3H,QAAI,WAAW,EAAG,OAAM,MAAM,CAAC,KAAO,OAAO,eAAe,MAAM,UAAU,CAAC,IAAK,WAAW,WAAY,UAAU,UAAU,CAAC;AAAA,EAChI;AACA,WAAS,IAAI,GAAG,IAAI,KAAK,GAAG,KAAK;AAC/B,QAAI,CAAC,UAAU,SAAS,CAAC,EAAG;AAC5B,UAAM,SAAU,OAAO,eAAe,MAAM,UAAU,CAAC,KAAO,UAAU,UAAU,CAAC,KAAM;AACzF,QAAI,UAAU,SAAS,CAAC,KAAK,UAAU,SAAS,CAAC,KAAM,GAAG;AACxD,UAAI,QAAQ,SAAS;AACnB,gBAAQ,QAAQ,KAAK,CAAC,IAAI,UAAU,SAAS,CAAC;AAC9C,gBAAQ,QAAQ,KAAK,CAAC,IAAI,SAAS,OAAO;AAAA,MAC5C,MAAO,SAAQ,SAAS,UAAU,SAAS,CAAC,CAAE,KAAM,SAAS,OAAO;AAAA,IACtE;AACA,QAAI,UAAU,aAAa,CAAC,IAAK,EAAG,KAAI,cAAc,SAAS,UAAU,aAAa,CAAC,IAAK,UAAU,aAAa,CAAC;AACpH,QAAI,MAAM,MAAM,CAAC,IAAK,OAAO,IAAK,OAAM,MAAM,CAAC,IAAI,OAAO;AAAA,EAC5D;AACF;AAEA,SAAS,UAAU,MAAmB,WAAoC;AACxE,QAAM,EAAE,MAAM,IAAI;AAClB,WAAS,IAAI,GAAG,IAAI,KAAK,GAAG,KAAK;AAC/B,UAAM,SAAS,UAAU,UAAU,CAAC;AACpC,QAAI,UAAU,EAAG;AACjB,UAAM,GAAG,CAAC,KAAM,SAAS,UAAU,KAAK,CAAC;AACzC,UAAM,GAAG,CAAC,KAAM,SAAS,UAAU,KAAK,CAAC;AAAA,EAC3C;AACF;AAMO,SAAS,aAAa,MAAmB,WAA8B,QAAwB,SAA0B,KAA2B;AACzJ,MAAI,aAAa;AACjB,MAAI,UAAU;AACd,iBAAe,MAAM,SAAS;AAC9B,eAAa,MAAM,SAAS;AAC5B,aAAW,MAAM,WAAW,MAAM;AAClC,aAAW,MAAM,WAAW,QAAQ,QAAQ,MAAM;AAClD,MAAI,QAAQ,OAAQ,aAAY,MAAM,WAAW,QAAQ,QAAQ,MAAM;AACvE,cAAY,MAAM,WAAW,QAAQ,SAAS,GAAG;AACjD,YAAU,MAAM,SAAS;AACzB,WAAS,IAAI,GAAG,IAAI,KAAK,GAAG,IAAK,MAAK,MAAM,UAAU,CAAC,IAAI,KAAK,MAAM,MAAM,CAAC;AAC/E;;;AClNA,IAAMA,mBAAkB;AAiBxB,IAAM,aAAa,CAAC,KAAK,KAAK,KAAK,MAAM,MAAM,SAAS,SAAS,QAAQ,SAAS,QAAQ,QAAQ,eAAe,WAAW;AAGrH,SAAS,aAAa,OAAmC;AAC9D,QAAM,EAAE,SAAS,IAAI;AACrB,QAAM,OAAO,KAAK,MAAM,MAAM,WAAW,MAAM,QAAQ;AACvD,QAAM,OAAO,MAAM,WAAW;AAC9B,QAAM,SAAS,EAAE,GAAG,IAAI,aAAa,QAAQ,GAAG,GAAG,IAAI,aAAa,QAAQ,GAAG,OAAO,IAAI,aAAa,QAAQ,GAAG,OAAO,IAAI,aAAa,QAAQ,GAAG,IAAI,IAAI,aAAa,QAAQ,GAAG,IAAI,IAAI,aAAa,QAAQ,EAAE;AACpN,QAAM,mBAAmB,aAAa,KAAK,MAAM,gBAAgB;AACjE,QAAM,gBAA+B,EAAE,UAAU,GAAG,YAAY,GAAG,YAAY,EAAE;AACjF,QAAM,iBAAiC,EAAE,YAAY,GAAG,SAAS,EAAE;AACnE,QAAM,YAAsC,MAAM,WAAW,wBAAwB,UAAU,WAAWA,gBAAe,IAAI;AAC7H,QAAM,SAAS,aAAa,MAAM,MAAM,UAAU,IAAI;AACtD,QAAM,iBAAiB,IAAI,aAAa,CAAC;AAEzC,QAAM,QAAiC,CAAC;AACxC,QAAM,OAAgG,EAAE,MAAM,MAAM,UAAU,MAAM,KAAK,IAAI,WAAW,CAAC,GAAG,OAAO;AACnK,QAAM,OAAoB,EAAE,GAAG,GAAG,OAA8B,MAAsC,WAAW,uBAAuB,UAAU,IAA8B,EAAE;AAElL,QAAM,OAAO,CAAC,UAAqC;AACjD,WAAO,OAAO,OAAO,MAAM,IAAI,EAAE,UAAU,MAAM,UAAU,OAAO,MAAM,IAAI,MAAM,CAAC;AACnF,eAAW,OAAO,WAAY,OAAM,GAAG,IAAI,MAAM,IAAI,GAAG;AACxD,SAAK,MAAM,MAAM;AACjB,WAAO;AAAA,EACT;AAEA,QAAM,MAAM,CAAC,UAA8B;AACzC,UAAM,EAAE,OAAO,IAAI;AACnB,UAAM,IAAI,OAAO,OAAO,CAAC;AACzB,UAAM,UAAU,KAAK,KAAK;AAC1B,YAAQ,IAAI;AACZ,aAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,aAAO,EAAE,CAAC,IAAI,MAAM,IAAI,EAAE,CAAC;AAC3B,aAAO,EAAE,CAAC,IAAI,MAAM,IAAI,EAAE,CAAC;AAC3B,aAAO,MAAM,CAAC,IAAI,MAAM,IAAI,MAAM,CAAC;AACnC,aAAO,MAAM,CAAC,IAAI,MAAM,IAAI,MAAM,CAAC;AACnC,aAAO,GAAG,CAAC,IAAI,MAAM,IAAI,GAAG,CAAC;AAC7B,aAAO,GAAG,CAAC,IAAI,MAAM,IAAI,GAAG,CAAC;AAAA,IAC/B;AACA,UAAM,IAAI,OAAO,OAAO,CAAC;AACzB,UAAM,SAAwB,EAAE,GAAG,MAAM,SAAS,EAAE;AACpD,gBAAY,SAAS,QAAQ,aAAa;AAC1C,WAAO,OAAO,QAAQ,IAAI,cAAc;AACxC,WAAO,OAAO,UAAU,IAAI,cAAc;AAC1C,WAAO,OAAO,UAAU,IAAI,cAAc;AAC1C,UAAM,SAAS,KAAK,IAAI,GAAG,CAAC;AAC5B,UAAM,IAAI,SAAS,KAAK,GAAG,GAAG,CAAC;AAC/B,QAAI,aAAa,MAAM,UAAU;AAC/B,YAAM,SAAS,OAAO,OAAO,QAAQ,IAAI,EAAE,GAAG,OAAO,OAAO,OAAO,GAAI,GAAG,OAAO,OAAO,OAAO,EAAG,IAAI;AACtG,YAAM,UAAU,EAAE,MAAM,MAAM,UAAU,MAAM,MAAM,IAAI,SAAS;AACjE,mBAAa,SAAS,WAAW,EAAE,GAAG,MAAM,UAAU,GAAG,QAAQ,MAAM,QAAQ,OAAO,GAAG,EAAE,kBAAkB,QAAQ,CAAC,SAAS,MAAM,UAAU,gBAAgB,QAAQ,QAAQ,GAAG,cAAc;AAChM,UAAI,MAAM;AACV,eAAS,IAAI,GAAG,IAAI,GAAG,IAAK,OAAM,KAAK,IAAI,KAAK,MAAM,IAAI,MAAM,CAAC,CAAE;AACnE,aAAO,OAAO,QAAQ,IAAI;AAAA,IAC5B,MAAO,QAAO,OAAO,QAAQ,IAAI;AACjC,WAAO,OAAO,UAAU,IAAI,eAAe;AAC3C,WAAO,OAAO,OAAO,IAAI,eAAe;AACxC,UAAM,EAAE,IAAI,IAAI;AAChB,aAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,UAAI,OAAO,CAAC,IAAI,IAAI,EAAE,CAAC,IAAK,OAAO,EAAE,CAAC;AACtC,UAAI,OAAO,CAAC,IAAI,IAAI,EAAE,CAAC,IAAK,OAAO,EAAE,CAAC;AACtC,UAAI,WAAW,CAAC,IAAI,IAAI,MAAM,CAAC,IAAK,OAAO,MAAM,CAAC;AAClD,UAAI,WAAW,CAAC,IAAI,IAAI,MAAM,CAAC,IAAK,OAAO,MAAM,CAAC;AAClD,UAAI,QAAQ,CAAC,IAAI,IAAI,GAAG,CAAC,IAAK,OAAO,GAAG,CAAC;AACzC,UAAI,QAAQ,CAAC,IAAI,IAAI,GAAG,CAAC,IAAK,OAAO,GAAG,CAAC;AACzC,UAAI,MAAM,CAAC,IAAI,QAAQ,UAAU,MAAM,CAAC;AAAA,IAC1C;AAAA,EACF;AAEA,SAAO,EAAE,IAAI;AACf;;;AC1FO,IAAM,UAAU,EAAE,MAAM,GAAG,WAAW,GAAG,MAAM,GAAG,MAAM,EAAE;AAgB1D,SAAS,oBAAoB,MAAwB;AAC1D,MAAI,SAAiD;AACrD,MAAI,QAA2B;AAC/B,OAAK,UAAU,CAAC,SAAS;AACvB,UAAM,UAAU;AAChB,QAAI,QAAQ,SAAS,QAAQ;AAC3B,eAAS,aAAa,QAAQ,KAAK;AACnC,cAAQ,QAAQ;AAChB,UAAI,QAAQ,UAAU,QAAQ,QAAS,WAAU,QAAQ,QAAQ,OAAO,IAAI,CAAC,WAAW,YAAY,QAAQ,QAAQ,KAAK,CAAC,GAAG,IAAI,WAAW,QAAQ,OAAO,CAAC;AAC5J;AAAA,IACF;AACA,QAAI,CAAC,UAAU,CAAC,MAAO;AACvB,UAAM,QAAQ,YAAY,QAAQ,QAAQ,KAAK;AAC/C,WAAO,IAAI,KAAK;AAChB,SAAK,YAAY,EAAE,MAAM,QAAQ,QAAQ,QAAQ,OAAO,GAAyB,CAAC,QAAQ,MAAM,CAAC;AAAA,EACnG,CAAC;AACH;AAGA,SAAS,UAAU,QAAyC,QAAmD,SAA2B;AACxI,WAAS,OAAO,KAAK,QAAQ;AAC3B,UAAM,OAAO,OAAO,OAAO;AAC3B,eAAS;AACP,YAAM,QAAQ,QAAQ,KAAK,SAAS,IAAI;AACxC,UAAI,UAAU,QAAQ,KAAM;AAC5B,UAAI,UAAU,QAAQ,UAAW;AACjC,cAAQ,KAAK,SAAS,MAAM,KAAK;AAAA,IACnC;AACA,WAAO,IAAI,OAAO,IAAI,CAAE;AACxB,YAAQ,MAAM,SAAS,MAAM,QAAQ,IAAI;AACzC,YAAQ,OAAO,SAAS,IAAI;AAAA,EAC9B;AACF;;;ACjDA,IAAM,QAAQ;AACd,IAAI,OAAO,MAAM,sBAAsB,eAAe,MAAM,eAAe,MAAM,kBAAkB;AACjG,QAAM,OAAO;AACb,sBAAoB,EAAE,aAAa,CAAC,SAAS,aAAa,KAAK,YAAY,SAAS,QAAQ,GAAG,WAAW,CAAC,aAAa,KAAK,iBAAiB,WAAW,CAAC,UAAU,SAAS,MAAM,IAAI,CAAC,EAAE,CAAC;AAC7L;","names":["PAIRS_PER_AGENT"]}
1
+ {"version":3,"sources":["../src/physics/offload/frame.ts","../src/grid.ts","../src/identity.ts","../src/types.ts","../src/physics/contact.ts","../src/physics/pressure.ts","../src/physics/offload/kernel.ts","../src/physics/offload/worker-host.ts","../src/physics/offload/worker-entry.ts"],"sourcesContent":["/**\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","/** 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 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 { 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 { attachPhysicsWorker } from './worker-host'\n\ntype WorkerScope = { postMessage(message: unknown, transfer?: ArrayBuffer[]): void; addEventListener(type: 'message', listener: (event: { data: unknown }) => void): void }\n\n// the file a browser `Worker` loads (`dist/physics-worker.js`); outside a worker scope it does nothing\nconst scope = globalThis as unknown as Partial<WorkerScope> & { WorkerGlobalScope?: unknown }\nif (typeof scope.WorkerGlobalScope !== 'undefined' && scope.postMessage && scope.addEventListener) {\n const self = scope as WorkerScope\n attachPhysicsWorker({ postMessage: (message, transfer) => self.postMessage(message, transfer), onMessage: (listener) => self.addEventListener('message', (event) => listener(event.data)) })\n}\n"],"mappings":"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","names":["PAIRS_PER_AGENT"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adatechnology/game-crowd",
3
- "version": "0.3.0",
3
+ "version": "0.4.1",
4
4
  "license": "MIT",
5
5
  "description": "Any crowd as a struct of arrays, pure and seeded: relevance bands, perception by sight, hearing and smell, a sliced flow field, separation, attack slots, chain alerts, doors, piles against walls, abilities as type traits, corpses, population and migration; civilians, herds and flocks; a bridge to game-infection. A literal port of the team game horde. Knows nothing about any game.",
6
6
  "keywords": [
@@ -39,8 +39,8 @@
39
39
  "dist"
40
40
  ],
41
41
  "dependencies": {
42
- "@adatechnology/game-characters": "^0.2.8",
43
- "@adatechnology/game-contracts": "^0.22.0"
42
+ "@adatechnology/game-characters": "^0.2.10",
43
+ "@adatechnology/game-contracts": "^0.24.0"
44
44
  },
45
45
  "devDependencies": {
46
46
  "@types/node": "^22.10.7",