namespace Outnumbered.Domain; // Per-stat run-scoped bonus (the survival cards). Implemented engine-side by the active driver; the domain only sees // this interface, so it stays pure. Returns 0 for any key without a bonus (and outside a survival run). public interface IStatBonusSource { double Bonus(string statKey); } // An immutable read of everything the pure domain math needs about one player, built at the single engine site // (SnapshotBuilder). The domain never touches a pawn/controller — only this. Upgrades is held by reference (not copied) // so building a snapshot per hit/tick stays allocation-free; the dictionary is treated as read-only here. public readonly struct PlayerSnapshot { public int Level { get; init; } public int Prestige { get; init; } public long Xp { get; init; } public int Kills { get; init; } public int Deaths { get; init; } public int HeadshotKills { get; init; } public int Streak { get; init; } public int Health { get; init; } // current HP (0 = no live pawn captured) — drives missing-HP (Berserk) public double HandicapProgress { get; init; } // mode progress axis, 0..1 (Gun Game ladder; 0 in TDM) public double HandicapFloor { get; init; } // monotonic escalation floor in t-space; -1 = no floor public double TeamDealMult { get; init; } // survival team card (squad-wide); 1.0 = none public double TeamTakeMult { get; init; } // survival team card (squad-wide); 1.0 = none public bool OverchargeActive { get; init; } // killstreak abilities that bend the damage chains public bool BerserkActive { get; init; } public bool AdrenalineActive { get; init; } public Dictionary Upgrades { get; init; } // permanent stat levels, by key (the PlayerData instance, by ref) public IStatBonusSource? Cards { get; init; } // run-card bonus per stat key; null outside survival }