export type RoundPhase = string; /** Default phase-duration shape for the built-in buy/live/end cycle; pass a wider `Record` when using a custom `phaseOrder`. */ export interface RoundPhaseDurations { buy: number; live: number; end: number; } export interface LossBonusRule { base: number; step: number; max: number; } /** A team entry with an optional role tag (e.g. "attacker", "defender") retrievable via `RoundState.roleOf`. */ export interface RoundTeam { id: string; role?: string; } export interface RoundConfig { /** Duration in seconds for each phase name in `phaseOrder` (or the default `RoundPhaseDurations` shape). */ phases: Record; teams: readonly string[] | readonly RoundTeam[]; /** * Overrides the default `["buy", "live", "end"]` cycle. Phases other than the first and last are * "conclude-eligible": `concludeRound`/`evaluate` may settle the round only while the current phase * is neither the first nor the last entry, matching the original buy (no conclude) / live (conclude) / * end (no conclude, settles the round) roles. The cycle wraps from the last entry back to the first. */ phaseOrder?: readonly TPhase[]; /** Evaluated by `evaluate()`; returning a team id concludes the round with that team as winner. */ winCondition?: (state: RoundSnapshot) => string | null; maxRounds?: number; winReward?: number; lossBonus?: LossBonusRule; } export type RoundEventKind = "phase.start" | "phase.end" | "round.win" | "round.economy" | "match.end"; export interface RoundEconomy { team: string; reward: number; won: boolean; lossStreak: number; } export interface RoundEvent { kind: RoundEventKind; round: number; phase?: TPhase; nextPhase?: TPhase; winner?: string; economy?: RoundEconomy[]; } export type PhaseEndHook = (endingPhase: TPhase, nextPhase: TPhase, round: number) => void; export interface RoundSnapshot { round: number; phase: TPhase; timeLeft: number; scores: Record; lossStreaks: Record; roles: Record; matchOver: boolean; /** Winner of the round being settled, or null; drives `economyFor` until the next round starts. */ pendingWinner: string | null; } export interface RoundState { tick(dt: number): RoundEvent[]; concludeRound(winner: string): RoundEvent[]; /** Calls `RoundConfig.winCondition` (a no-op when absent) and concludes the round if it returns a team id. */ evaluate(): RoundEvent[]; onPhaseEnd(hook: PhaseEndHook): () => void; phase(): TPhase; round(): number; timeLeft(): number; score(team: string): number; economyFor(team: string): RoundEconomy; /** The role tag configured for `team`, if any. */ roleOf(team: string): string | undefined; snapshot(): RoundSnapshot; /** Puts a {@link RoundState.snapshot} back; phase hooks stay registered. */ restore(next: RoundSnapshot): void; } /** @internal */ export declare function lossBonusFor(rule: LossBonusRule | undefined, streak: number): number; /** * Run a match as repeating buy/action/end round phases with per-round win rewards and loss-streak economy. * * @capability match-rounds run buy/action/end round phases with per-round economy * @internal */ export declare function createRoundState(config: RoundConfig): RoundState; /** @internal */ export declare function createRoundState(config: RoundConfig & { phaseOrder: readonly TPhase[]; }): RoundState;