import type { LootPipeline, LootResolution } from "../game/lootPipeline.js"; import type { Drop } from "../game/lootTable.js"; import { type RandomSeed } from "../random/rng.js"; /** Timestamped serializable event. `at` is epoch milliseconds, compatible with `appendFeed`. */ export interface ExpeditionEvent { at: number; /** Lifecycle kinds are loot/hazard/supply/death/recall/return; encounter notices may use caller kinds. */ kind: string; text: string; /** Accepted drops; `loot.drops` records the full roll before the item-unit capacity bound. */ drops?: readonly Drop[]; loot?: LootResolution; hpDelta?: number; exposureDelta?: number; supply?: string; } /** Plain saveable expedition data. Persist this whole value, including the RNG cursor and tick frontier. */ export interface ExpeditionState { readonly id: string; readonly status: "exploring" | "returning" | "home" | "dead"; readonly startedAtMs: number; /** Last completed event tick (arrival time after return), never an unprocessed catch-up target. */ readonly lastSettledMs: number; readonly elapsedMs: number; readonly returnUntilMs: number | null; readonly hp: number; /** Caller-interpreted hazard meter; its meaning and effective max-HP policy are injected. */ readonly exposure: number; readonly supplies: Readonly>; /** Counts are merged by item/currency id; currency does not consume item capacity. */ readonly carried: readonly Drop[]; readonly log: readonly ExpeditionEvent[]; readonly seed: string; readonly cursor: RandomSeed; } /** Dispatch data, detached from the caller's supply record. All timestamps are epoch milliseconds. */ export interface ExpeditionDispatch { id: string; startAtMs: number; hp: number; exposure?: number; supplies?: Readonly>; seed: string; } /** One encounter's mechanical effects and optional caller-authored notices. */ export interface ExpeditionHazard { damage?: number; exposure?: number; notices?: readonly { kind: string; text: string; }[]; } /** Ordered auto-consumption rule: spends one finite supply only when its declared effect changes a vital. */ export interface ExpeditionSupplyRule { key: string; when: (state: ExpeditionState) => boolean; restoreHp?: number; clearExposure?: number; } /** Runtime policy, supplied on each transition and never saved. Callbacks must be pure. */ export interface ExpeditionConfig { /** Positive whole milliseconds between events; cadence retuning anchors at the last completed tick. */ tickMs: number; loot: LootPipeline; lootCtx: (state: ExpeditionState) => TCtx; maxHp: (state: ExpeditionState) => number; hazard?: (state: ExpeditionState, rng: () => number) => ExpeditionHazard; supplies?: readonly ExpeditionSupplyRule[]; /** Nonnegative fraction of exploring elapsed time spent returning. */ returnFraction: number; /** Positive whole item units; currency is carried without consuming item slots. */ carryCap: number; /** Bounded work per call, default 10,000; excess ticks remain unsettled for the next call. */ maxTicksPerSettle?: number; /** Newest event count retained by appendFeed, default 200; rewards and RNG are retained separately. */ logLimit?: number; /** Optional alternative persisted RNG recurrence; returns a [0,1) value and its next cursor. */ randomStep?: (cursor: RandomSeed) => readonly [number, RandomSeed]; /** All lifecycle prose is caller-authored; omit for headless logs with empty text. */ describe?: (event: Omit, state: ExpeditionState) => string; } /** * Dispatch plain expedition state with a persisted seeded cursor; no clock, scheduler or storage is owned. * * @capability expedition-dispatch dispatch a saveable timed away mission with finite supplies and a seeded RNG cursor */ export declare function dispatchExpedition(input: ExpeditionDispatch): ExpeditionState; /** * Replay due event ticks using the existing stepped catch-up primitive. Order is loot → hazard → ordered * supplies → death → capacity recall. Unprocessed ticks retain their frontier; returning never rolls or * takes damage. Auto-recall uses the triggering tick's time, including offline arrival in the same call. * * @capability expedition-settle settle bounded offline expedition ticks without losing supplies rewards or RNG continuity */ export declare function settleExpedition(config: ExpeditionConfig, state: ExpeditionState, nowMs: number): ExpeditionState; /** * Settle first, then recall safely. If the catch-up budget leaves due exploring ticks, return that state * without recalling; repeat at the same target until the frontier catches up. Dead state never revives. * * @capability expedition-recall begin a safe fractional-time return after settling the pending expedition frontier */ export declare function recallExpedition(config: ExpeditionConfig, state: ExpeditionState, nowMs: number): ExpeditionState; /** * Settle first, then complete a live return instantly. Caller owns payment/authorization; pending ticks * remain unsettled when the budget is exhausted, and dead/home state is never changed. * * @capability expedition-return complete an authorized instant return without skipping unsettled ticks or reviving death */ export declare function returnExpeditionNow(config: ExpeditionConfig, state: ExpeditionState, nowMs: number): ExpeditionState;