Achievements overhaul: 37 achievements, page with tabs, leagues, gating
Achievements (client + server mirror, metric-driven so the list is one source): - 37 achievements across 6 categories (Victories, Kot, Streaks, Levels, Ranks, Veterancy) incl. 7–0 sweeps, kot milestones (1/5/10/25/50/100), win streaks (3/5/10/15), level milestones every 5 (5..50), rank floors, games/tricks. - New AchievementsScreen with category tabs, progress bars, coin + sticker-unlock badges, and unlocked/locked states; summary header (unlocked count + coins). - Some achievements unlock sticker packs: Seven–Zip→Hokm, 25 Kots→Taunts, 100 Wins→Persian (ownedStickerPackIds now also honors profile.unlocked). - Prestige titles added: Expert, Professional, Captain, Leader (+ existing). - Tracks new stat shutoutWins; MatchSummary.shutout (7–0). Profile shows a 6-item preview + "view all" link. Leagues: 3 ranked entry tiers — Starter (100, lvl1), Pro (500, lvl10), Expert (1000, lvl20). Higher league stakes more, so wins/losses swing bigger; kot bonus now scales to the stake (40%). OnlineLobby shows league cards with level gating. Profile photo upload gated to level 25 (client button + server Update guard). Win animation: PostMatchRewardsModal now shows an animated coins-won count-up hero on a win. Verified: dotnet build + tsc + next build clean; sim unlocks 26 achievements over 500 matches; live server grants first_win/first_kot/shutout_1 and pays 2050 coins on an expert-league shutout+kot win. Images rebuilt on :1500/:1505. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+3
-2
@@ -85,7 +85,7 @@ function baseProfile(): UserProfile {
|
||||
rating: 1000,
|
||||
stats: {
|
||||
games: 0, wins: 0, losses: 0, kotsFor: 0, kotsAgainst: 0,
|
||||
tricks: 0, bestWinStreak: 0, currentWinStreak: 0,
|
||||
tricks: 0, bestWinStreak: 0, currentWinStreak: 0, shutoutWins: 0,
|
||||
},
|
||||
plan: "free",
|
||||
ownedAvatars: ["a-fox"],
|
||||
@@ -122,6 +122,7 @@ for (let i = 0; i < M; i++) {
|
||||
tricksWon: won ? 7 + (i % 6) : i % 7,
|
||||
rounds: 7,
|
||||
trump: "spades",
|
||||
shutout: won && i % 8 === 0,
|
||||
};
|
||||
const before = profile;
|
||||
const { profile: after, reward } = applyMatchResult(before, summary, 1000);
|
||||
@@ -148,7 +149,7 @@ for (let i = 0; i < M; i++) {
|
||||
{
|
||||
const r = applyMatchResult(baseProfile(), {
|
||||
ranked: false, stake: 0, won: true, kotFor: false, kotAgainst: false,
|
||||
tricksWon: 7, rounds: 7, trump: null,
|
||||
tricksWon: 7, rounds: 7, trump: null, shutout: false,
|
||||
}, 1000);
|
||||
assert(r.reward.ratingDelta === 0, "casual match must not change rating");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user