feat(rank-progress): RankProgressResult carries TierAdvanced signal

RankProgressResult gains a TierAdvanced bool (defaulted false so the 7-arg
positional-constructor sites don't break). Set true iff the grant is a win
AND RankTier.Name resolves both pre-grant and post-grant to distinct strings —
i.e. a promotion crossed a tier boundary.

Explicitly guards on isWin so a demotion (in principle possible after a loss
below a tier floor, though the current floor rules prevent it) never fires
the achievement backward.

Three new tests: no-cross win, beginner→d cross, loss stays false.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
gamer147
2026-07-04 13:32:45 -04:00
parent 8e41739bde
commit e9c13ae5f4
3 changed files with 65 additions and 5 deletions

View File

@@ -39,6 +39,9 @@ public sealed class RankProgressService : IRankProgressService
viewer.RankProgress.Add(row);
}
// Snapshot pre-tier for the TierAdvanced signal — compared to post-tier below.
string? preTier = RankTier.Name(CurrentRankId(row, byId));
int deltaPoint = 0;
int deltaMp = 0;
int masterFloor = byId[MasterRankId].LowerLimitPoint; // 50000
@@ -81,6 +84,12 @@ public sealed class RankProgressService : IRankProgressService
}
int finalRank = CurrentRankId(row, byId);
string? postTier = RankTier.Name(finalRank);
// Tier "advanced" only on a promotion (pre != post AND both resolve). Demotion by
// point loss would technically change the tier string too, but rank_achieved is
// an achievement — it doesn't fire on going backward.
bool tierAdvanced = isWin && preTier != postTier && postTier is not null && preTier is not null;
return new RankProgressResult(
Rank: finalRank,
AfterBattlePoint: row.Point,
@@ -88,7 +97,8 @@ public sealed class RankProgressService : IRankProgressService
BattlePoint: deltaPoint,
MasterPoint: deltaMp,
IsMasterRank: finalRank == MasterRankId,
IsGrandMasterRank: finalRank >= FirstGrandMasterRankId);
IsGrandMasterRank: finalRank >= FirstGrandMasterRankId,
TierAdvanced: tierAdvanced);
}
public async Task<RankProgressResult> GetAsync(