import { ISaveSection } from './ISaveSection'; export type MatchOutcome = 'win' | 'loss' | 'draw'; interface StatsData { played: number; won: number; lost: number; drawn: number; streak: number; bestStreak: number; } function emptyStats(): StatsData { return { played: 0, won: 0, lost: 0, drawn: 0, streak: 0, bestStreak: 0 }; } /** * How the player has been doing. The only section iteration 1 ships; currency * and level progress will join it later without touching `SaveManager`. */ export class StatsSection implements ISaveSection { static readonly KEY = 'stats'; readonly key = StatsSection.KEY; private _data: StatsData = emptyStats(); get played(): number { return this._data.played; } get won(): number { return this._data.won; } get lost(): number { return this._data.lost; } get drawn(): number { return this._data.drawn; } /** Wins in a row right now. A loss resets it; a draw leaves it alone. */ get streak(): number { return this._data.streak; } get bestStreak(): number { return this._data.bestStreak; } recordResult(outcome: MatchOutcome): void { this._data.played += 1; switch (outcome) { case 'win': this._data.won += 1; this._data.streak += 1; this._data.bestStreak = Math.max(this._data.bestStreak, this._data.streak); break; case 'loss': this._data.lost += 1; this._data.streak = 0; break; case 'draw': this._data.drawn += 1; break; } } save(): unknown { return { ...this._data }; } load(data: unknown): void { const source = (typeof data === 'object' && data !== null ? data : {}) as Record; const defaults = emptyStats(); this._data = { played: count(source.played, defaults.played), won: count(source.won, defaults.won), lost: count(source.lost, defaults.lost), drawn: count(source.drawn, defaults.drawn), streak: count(source.streak, defaults.streak), bestStreak: count(source.bestStreak, defaults.bestStreak), }; // A save edited by hand should not be able to show a nonsense best. this._data.bestStreak = Math.max(this._data.bestStreak, this._data.streak); } reset(): void { this._data = emptyStats(); } } /** Reads a non-negative whole number, or falls back. */ function count(value: unknown, fallback: number): number { return typeof value === 'number' && Number.isFinite(value) && value >= 0 ? Math.floor(value) : fallback; }