using SVSim.Database.Enums; using SVSim.Database.Models; namespace SVSim.Database.Services.RankProgress; /// /// Wire-shape result of a single rank-battle finish grant. The wire-mapped fields /// (, , , /// , , , /// ) map 1:1 to RankBattleFinishResponseDto /// keys the client reads via GetValueOrDefault in Wizard/RankBattleFinishTask.cs:57-63. /// is a controller-side signal for the rank_achieved mission /// emit and is not serialized to the wire. /// /// rank_id post-grant (1..29). /// Point post-grant (accumulated). /// MasterPoint post-grant (accumulated). /// Signed Point delta (+100/-50/0). /// Signed MasterPoint delta. /// True iff Rank == 25. /// True iff Rank >= 26. /// /// True iff this grant crossed the viewer into a higher bucket /// than they held pre-grant. Callsites gate rank_achieved mission emits on this flag. /// public sealed record RankProgressResult( int Rank, int AfterBattlePoint, int AfterMasterPoint, int BattlePoint, int MasterPoint, bool IsMasterRank, bool IsGrandMasterRank, bool TierAdvanced = false); public interface IRankProgressService { /// /// Applies a +100 (win) or -50 (loss) delta to the viewer's rank progression in the /// given format, respecting tier floors from ranks.csv's LowerLimitPoint column. /// Creates a ViewerRankProgress row for (viewer, format) if none exists. Caller must /// have loaded the viewer with .Include(v => v.RankProgress) and must call /// SaveChangesAsync after this returns. /// /// Must be Format.Rotation or Format.Unlimited. Task GrantAsync( Viewer viewer, Format format, bool isWin, CancellationToken ct = default); /// /// Read-only current progression snapshot for (viewer, format). Returns a zero-value /// result if no row exists. Does not mutate the viewer or hit the DB. /// Task GetAsync( Viewer viewer, Format format, CancellationToken ct = default); }