export interface CastConfig { abilityId: string; castTimeMs: number; /** Total movement distance tolerated before the cast auto-interrupts; default `0.04`. `Infinity` allows casting on the move. */ moveTolerance?: number; /** * Per-tick movement at or below this is ignored rather than added to the running total — the * caster's noise floor. A 3D game's pose jitters every frame from terrain-following ground snap * and float drift; since `moveTolerance` accumulates, feeding those raw deltas straight in will * silently cancel casts the player never interrupted, and the failure is intermittent rather than * obvious. Only the game knows its own noise floor, so declare it once here instead of filtering * at every `tick` call site. Default `0` (count every reported movement). */ moveDeadzone?: number; } export interface CastBarSnapshot { abilityId: string; castTimeMs: number; elapsedMs: number; remainingMs: number; /** Fill fraction in `[0, 1]` for the cast bar. */ fraction: number; } export type CastInterruptReason = "moved" | "cancelled" | "replaced"; export type CastEvent = { kind: "completed"; abilityId: string; } | { kind: "interrupted"; abilityId: string; reason: CastInterruptReason; }; /** * Per-entity cast-time state machine — begin, tick with game-time `dt` plus how far the caster * moved, and act on the returned event. The runner owns timing and move-interruption only; the * caller spends resources, starts cooldowns, and executes the ability when `completed` fires * (compose with `abilityKit` — check readiness before `begin`, `cast` on completion). */ export interface CastRunner { /** Start a cast; `false` while one is already in flight (interrupt or let it finish first). */ begin(config: CastConfig): boolean; casting(): boolean; state(): CastBarSnapshot | null; /** Interrupt the in-flight cast now; returns the interruption event, or `null` when idle. */ interrupt(reason?: CastInterruptReason): CastEvent | null; /** * Advance by `dtSeconds`; `movedDistance` accumulates toward the config's `moveTolerance`, except * for per-tick movement at or below `moveDeadzone`, which is treated as jitter and ignored. */ tick(dtSeconds: number, movedDistance?: number): CastEvent | null; } /** * Run a channeled cast/charge timer that movement or damage can interrupt — the spell cast bar. * * @capability cast-bar run a channeled cast timer that movement or damage can interrupt */ export declare function createCastRunner(): CastRunner;