/** Recoil per shot, in radians of aim. `pitch > 0` climbs the muzzle, `yaw > 0` pulls right. */ export interface WeaponRecoilTuning { /** * Fixed kick per shot index as `[pitch, yaw]` rad — the learnable part of a spray pattern. Past its end the * last entry repeats. Omit for a flat `pitch` kick every shot. */ pattern?: readonly (readonly [number, number])[]; /** Kick per shot when there is no `pattern`, rad (default `0`). */ pitch?: number; /** Random extra kick, rad, drawn per shot from the injected `random` within ±this cone (default `0`). */ randomCone?: number; /** Share of each kick that moves the camera rather than the aim, `0..1` (default `0`: all aim). */ cameraShare?: number; /** Rate the accumulated kick recovers toward centre, rad/s (default `0`: none). */ recoverRate?: number; /** Seconds after a shot before recovery starts (default `0.1`). */ recoverDelay?: number; /** Aim kick multiplier while aiming down sights (default `1`). */ adsScale?: number; } /** Cone of fire, as a half-angle in radians. */ export interface WeaponSpreadTuning { /** Resting spread, rad. */ base: number; /** Spread added per shot (bloom), rad (default `0`). */ perShot?: number; /** Largest bloom, rad (default `base + 10 · perShot`). */ max?: number; /** Rate bloom recovers toward `base`, rad/s (default `0`). */ recoverRate?: number; /** Seconds after a shot before bloom recovers (default `0.1`). */ recoverDelay?: number; /** Multipliers on the final spread for stance and aim; each defaults to `1`. */ ads?: number; crouching?: number; moving?: number; airborne?: number; } /** * Tuning for {@link createWeaponHandling}. Every number is an angle in radians or a time in seconds, so two * weapons differ by numbers a person can reason about: a controllable rifle climbs a little and recovers * fast; a shotgun kicks hard and blooms wide. */ export interface WeaponHandlingTuning { recoil?: WeaponRecoilTuning; spread: WeaponSpreadTuning; /** Seconds to go fully into or out of aim-down-sights (default `0.2`). */ adsTime?: number; } /** Stance and aim for one tick. */ export interface WeaponStance { ads?: boolean; crouching?: boolean; moving?: boolean; airborne?: boolean; } /** Serializable handling state. */ export interface WeaponHandlingState { aimPitch: number; aimYaw: number; cameraPitch: number; cameraYaw: number; bloom: number; shotIndex: number; sinceShot: number; adsProgress: number; } /** What one shot did: the cone to sample and the kick it applied. */ export interface WeaponShot { /** Half-angle of the cone this shot fires into, rad, with stance multipliers applied. */ spread: number; /** Aim kick applied by this shot, rad. */ kickPitch: number; kickYaw: number; } /** Live handling readout: the offsets to add to aim and camera, current spread and ADS blend. */ export interface WeaponHandlingFrame { /** Recoil offset to add to the aim direction, rad. */ aimPitch: number; aimYaw: number; /** Recoil offset to add to the camera only (view punch), rad. */ cameraPitch: number; cameraYaw: number; /** Current half-angle spread with stance multipliers, rad. */ spread: number; /** `0` hip … `1` fully aimed. */ adsProgress: number; } /** * The player-to-weapon relationship as state: recoil that climbs and recovers, spread that blooms and * settles, and an aim-down-sights blend. Cadence, ammo and hits stay in `weaponFire`/`magazine`; this owns * how the weapon feels between them. First-person, third-person and over-the-shoulder presentations read * the same frame. */ export interface WeaponHandling { /** Register one shot (call when the cadence fires) and return its spread and kick. */ fire(stance?: WeaponStance): WeaponShot; /** Advance recovery, bloom decay and ADS by `dt`. */ tick(dt: number, stance?: WeaponStance): WeaponHandlingFrame; frame(): WeaponHandlingFrame; retune(next: WeaponHandlingTuning): void; snapshot(): WeaponHandlingState; restore(state: WeaponHandlingState): void; reset(): void; } /** * Creates a {@link WeaponHandling}. Pass `random` (e.g. `ctx.rng` or `seededRng(seed)`) for the random * recoil cone; without it the cone is skipped, so the result stays deterministic either way. * @capability weapon-handling recoil patterns with camera kick and recovery, spread bloom, stance multipliers and ADS as serializable state */ export declare function createWeaponHandling(initial: WeaponHandlingTuning, options?: { random?: () => number; }): WeaponHandling; /** Deterministic weapon-feel metrics from {@link measureWeapon}. */ export interface WeaponReport { /** Spread of the first shot from rest, rad. */ firstShotSpread: number; /** Spread of the tenth shot of a held burst, rad. */ tenthShotSpread: number; /** Spread of every shot in the burst, rad; `spreadByShot[n - 1]` is the spread after `n - 1` shots of bloom. */ spreadByShot: number[]; /** Aim climb after a full burst, rad; set `burst` to the magazine size for climb over a magazine. */ burstClimb: number; /** Seconds after the burst until aim recoil is back within 5% of its peak and bloom within 5% of base. */ resetSeconds: number; /** Seconds to go fully into aim-down-sights. */ adsSeconds: number; /** Shots until the expected damage reaches `targetHealth` (`0` without `damage`, `Infinity` if it never does). */ shotsToKill: number; /** Seconds from the first to the killing shot at `interval` (`0` without `damage` or when one shot kills). */ timeToKill: number; } /** Options for {@link measureWeapon}. */ export interface WeaponProbeOptions { /** Seconds between shots (`60 / rpm`). */ interval: number; /** Shots in the held burst (default `30`). */ burst?: number; /** Damage per projectile at the muzzle. */ damage?: number; /** Projectiles per shot, e.g. shotgun pellets (default `1`). */ pellets?: number; targetHealth?: number; /** Distance to the target, m. Without it every projectile hits. */ range?: number; /** Radius of the target's hittable area, m (default `0.4`). */ targetRadius?: number; /** Damage multiplier at a range, the game's falloff policy (default: none). */ damageAt?: (range: number) => number; /** Stance the burst and kill run are fired in (e.g. `{ ads: true }` after the ADS time). */ stance?: WeaponStance; dt?: number; } /** * Fires a held burst through a fresh handling instance at a fixed interval and reports spread growth, * climb, reset time, ADS time and time-to-kill. Time-to-kill counts expected damage: each projectile hits * with the share of its spread cone the target covers at `range`, scaled by `damageAt(range)`, so a * blooming rifle and a wide shotgun compare at the distance the game cares about. * @capability weapon-metrics measure a weapon's feel as numbers — spread per shot, climb over a magazine, reset time, ADS time, time-to-kill at a range */ export declare function measureWeapon(create: () => WeaponHandling, options: WeaponProbeOptions): WeaponReport;