export type AbilityCastType = "instant" | "projectile" | "channel" | "targeted"; export type AbilitySlotState = "ready" | "cooldown" | "no-resource" | "just-cast"; export interface AbilitySlotConfig { id: string; cooldownMs: number; chargesMax?: number; resourceCost?: number; castType?: AbilityCastType; flashMs?: number; /** Shared cooldown groups this slot participates in — ids declared in `AbilityKitOptions.groups`. */ groups?: readonly string[]; } /** A cooldown shared across every member slot: casting any member starts it, and no member can cast while it runs. One group that every slot joins is an MMO global cooldown. */ export interface AbilityCooldownGroup { id: string; cooldownMs: number; } /** * A spendable resource pool (mana, energy, rage) bound to an ability kit. When bound, `cast` deducts a * slot's `resourceCost` on success and `state`/`snapshot`/`canCast` reflect affordability without a * per-call `resourceAvailable` argument — the cast path actually spends instead of only gating (#357). */ export interface AbilityResource { /** Current spendable amount, read fresh on each cast/snapshot. */ available(): number; /** Deduct `cost` from the pool after a successful cast. */ spend(cost: number): void; } export interface AbilityKitOptions { groups?: readonly AbilityCooldownGroup[]; /** Bind a resource pool so casting charges `resourceCost` and snapshots reflect affordability automatically. Passing an explicit `resourceAvailable` to a method overrides the bound pool for that call and never auto-spends. */ resource?: AbilityResource; } export interface AbilitySlotSnapshot { id: string; state: AbilitySlotState; castType: AbilityCastType; resourceCost: number; charges: number; chargesMax: number; /** Max of the slot's own recharge and any blocking group cooldown. */ cooldownRemainingMs: number; cooldownFraction: number; /** Remaining ms on the slowest of the slot's shared cooldown groups; 0 when unblocked. */ groupRemainingMs: number; justCast: boolean; ready: boolean; } export type AbilityCastReason = "unknown-slot" | "cooldown" | "group-cooldown" | "no-resource"; export interface AbilitySlotRetune { cooldownMs?: number; resourceCost?: number; } /** Mutable kit state; slot/group definitions and any bound resource are saved by the caller. */ export interface AbilityKitState { slots: { id: string; cooldownMs: number; resourceCost: number; charges: number; rechargeRemainingMs: number; flashRemainingMs: number; }[]; groups: { id: string; remainingMs: number; }[]; } export type AbilityCastResult = { ok: true; slot: AbilitySlotSnapshot; } | { ok: false; reason: AbilityCastReason; slot: AbilitySlotSnapshot | null; }; export interface AbilityKit { slots(): readonly string[]; config(slotId: string): AbilitySlotConfig | null; /** Detached persistence state, including in-flight timers and slot retuning. */ state(): AbilityKitState; state(slotId: string, resourceAvailable?: number): AbilitySlotSnapshot | null; snapshot(resourceAvailable?: number): AbilitySlotSnapshot[]; /** Restore without spending resources; returns false without mutation for mismatched slot/group ids. */ restore(next: AbilityKitState): boolean; canCast(slotId: string, resourceAvailable?: number): AbilityCastResult; cast(slotId: string, resourceAvailable?: number): AbilityCastResult; tick(dtSeconds: number): void; /** Remaining ms on a shared cooldown group; 0 when ready, null for an unknown group. */ groupRemaining(groupId: string): number | null; reset(slotId?: string): void; retuneSlot(slotId: string, patch: AbilitySlotRetune): boolean; } /** * A bar of cooldown-gated abilities the player fires by slot, tracking readiness and cooldown per ability. * * @capability ability-bar a bar of cooldown-gated abilities the player triggers by slot * @capability global-cooldown share one cooldown across every ability slot (MMO global cooldown) with a cooldown group */ export declare function createAbilityKit(configs: readonly AbilitySlotConfig[], options?: AbilityKitOptions): AbilityKit;