import type { AxisInput } from "../input/axisInput.js"; import type { KinematicVehicleModifiers, KinematicVehicleTuning } from "./kinematicVehicle.js"; /** * Handling change one damage zone applies at its *last* stage. Multiplier fields read `1` = undamaged, * and a zone part-way through its stages gets the fraction between; `steerPull` and `drag` are additive * and reach their full value only at the last stage. */ export interface VehicleDamageEffect { /** Tire grip multiplier at full damage (`< 1` loses grip). */ gripScale?: number; /** Drive-force multiplier at full damage. */ engineScale?: number; brakeScale?: number; topSpeedScale?: number; /** Steering bias in radians at full damage — a bent frame that pulls one way. */ steerPull?: number; /** Extra coast drag per second at full damage. */ drag?: number; } /** * One damageable zone: a fixed local body point, the impulse thresholds that step it through its * stages, the bounded dent it shows, and the handling it costs. There is no mesh editing — a zone's * deformation is one displacement it can never exceed, so the visual can never grow without limit. */ export interface VehicleDamageZone { id: string; /** Local body point in metres (x right, y up, z forward); picks the zone nearest an impact and anchors the dent. */ at: readonly [number, number, number]; /** Impulse thresholds; the count is the zone's stage count, and the last stage is fully damaged. */ thresholds: readonly number[]; /** Dent displacement in metres at the last stage, scaled by stage. Omit for a zone that only changes handling. */ deform?: readonly [number, number, number]; /** Stage at which the part comes off (the renderer hides or drops it); omit to never detach. */ detachStage?: number; /** Handling change at the last stage, reached proportionally across the stages. */ effect?: VehicleDamageEffect; } /** Live state of one zone, as a HUD or a save reads it. */ export interface VehicleDamageZoneState { id: string; accumulated: number; stage: number; detached: boolean; } /** Serializable damage state: {@link VehicleDamage.restore} needs only this. */ export interface VehicleDamageState { zones: VehicleDamageZoneState[]; total: number; disabled: boolean; } /** A zone crossing a threshold this tick — the event a HUD feed, a smoke puff or a camera shake keys off. */ export interface VehicleDamageTransition { zone: string; stage: number; previousStage: number; detached: boolean; disabled: boolean; total: number; } /** A zone's current dent: a bounded local displacement and whether the part has come off. */ export interface VehicleDeformation { zoneId: string; stage: number; /** Local displacement in metres, never larger than the zone's `deform`. */ offset: readonly [number, number, number]; /** The part has come off — the renderer hides or drops it. */ detached: boolean; } /** Every zone's handling cost summed into one set of numbers (all fields present, defaults filled). */ export interface VehicleDamageEffectResolved { gripScale: number; engineScale: number; brakeScale: number; topSpeedScale: number; steerPull: number; drag: number; } /** Everything {@link createVehicleDamage} needs. */ export interface VehicleDamageConfig { zones: readonly VehicleDamageZone[]; /** Total accumulated impulse across every zone that disables the vehicle; omit to never disable. */ disableAt?: number; /** Called on each threshold crossing, for HUD/feed/audio. */ onTransition?: (transition: VehicleDamageTransition) => void; /** Register a devtools probe under this name so the perf overlay shows the damage summary. */ devtoolsName?: string; } /** * Fixed-zone crash damage for a vehicle: impacts accumulate into discrete stages, stages change handling * and show a bounded dent, and a part can come off at a set stage. Data-driven and bounded by * construction — the zone set is authored, each zone's deformation is one capped displacement, and no * mesh is edited. Serializes through {@link snapshot}/{@link restore}, so a save, a replay or a host * correction resumes mid-crash. */ export interface VehicleDamage { /** * Route an impulse to the zone nearest a local body hit point (x right, y up, z forward) and return * the transitions it caused — empty when nothing crossed a threshold. The caller rotates a world * impact into body space; this module never reads the world. */ hit(local: readonly [number, number, number], impulse: number): readonly VehicleDamageTransition[]; /** Add impulse straight to a named zone; null for an unknown zone or a non-positive impulse. */ impact(zoneId: string, impulse: number): VehicleDamageTransition | null; stageOf(zoneId: string): number; zones(): readonly VehicleDamageZoneState[]; /** Summed handling change across every zone at its current stage. */ effect(): VehicleDamageEffectResolved; /** Bounded visual deformation, one entry per zone that has taken a stage — the render hook. */ deformation(): readonly VehicleDeformation[]; total(): number; disabled(): boolean; /** * Per-tick modifiers the sim applies for one frame. Use this for a scratch or a reset that should not * rebuild the tuning; for lasting damage call {@link damagedTuning} and `retune` once per change. */ modifiers(): KinematicVehicleModifiers; snapshot(): VehicleDamageState; restore(next: VehicleDamageState): void; reset(): void; /** Drop the devtools probe registered by `devtoolsName`. Safe to call twice. */ dispose(): void; } /** * Fixed-zone crash damage (issue #86's stage model) wired to handling and visuals: see {@link VehicleDamage}. * * @capability vehicle-damage fixed-zone crash damage that changes handling and shows a bounded dent, serializable */ export declare function createVehicleDamage(config: VehicleDamageConfig): VehicleDamage; /** * Bake a damage effect into a tuning — the lasting-damage path. Hand the result to the sim's `retune`, * which keeps the vehicle's momentum; the base tuning is not mutated, so a repair is the same call with * the undamaged base. */ export declare function damagedTuning(base: KinematicVehicleTuning, effect: VehicleDamageEffectResolved): KinematicVehicleTuning; /** Apply a damage `steerPull` to a steer axis, clamped to the input range — a bent frame pulling one way. */ export declare function damagedAxis(axis: AxisInput, effect: VehicleDamageEffectResolved): AxisInput;