using SVSim.Database.Models; namespace SVSim.Database.Services.BattleXp; /// /// Amounts returned to callers after a class-XP grant. and /// are POST-grant, POST-level-up (matching the wire shape's /// class_experience + class_level post-state semantics). /// is true iff at least one level threshold was /// crossed during this grant — callers gate class_level_up mission emits /// on this flag rather than caching pre-state themselves. /// public sealed record BattleXpGrantResult(int GetXp, int TotalXp, int Level, bool LeveledUp); public interface IBattleXpService { /// /// Grants class XP for a battle finish. Caller supplies a viewer loaded via /// /// (or equivalent, with .Include(v => v.Classes).ThenInclude(c => c.Class)). /// Caller SaveChangesAsync after this returns. /// /// Amount resolution: mode-specific config override if non-null, else global /// XpPerWin/XpPerLoss. Story ignores (always /// treated as a clear). /// /// /// Level-up: loops on ClassExpEntry curve; row.Exp stores level-relative /// XP and carries overflow after each level-up. Saturates at curve max level (excess /// piles in Exp). /// /// /// Guardrail: viewer has no row for /// → returns (0, 0, 1), no mutation, logs Warning. /// /// Task GrantAsync( Viewer viewer, int classId, bool isWin, BattleXpMode mode, CancellationToken ct = default); }