Skip to content

Achievements

Achievements are badges only: they pay no cash, XP or items. An achievement is done while its condition holds on the save, so nothing records the moment it unlocks. Old saves get theirs as soon as the game loads, and the race server and the game can never disagree about them. The design is in docs/2026-11-08-achievements-inbox-design.md.

player/achievements/ holds the list, with no Phaser, so the server could run it too. Each entry is { id, name, description, icon, done(state) }:

  • careerAchievements.ts: first win, each district’s boss, every star in each district (the “sweep”), and all four bosses. Bosses are named by district, so a locked badge gives nothing of the story away.
  • garageAchievements.ts: owning 3 and 5 cars, a car with every part in sim/upgrades.ts fitted, a repainted car, a car with decals, and 1, 10 and 50 online wins.

Adding one is a new entry in either file. tests/achievements.test.ts checks the ids are unique and that a new game has none.

The save does not otherwise know how many online races a player won, so settleOnlineHeat (player/level/onlineXp.ts) adds one to onlineWins when the race server pays the XP for a won heat. It counts only heats inside the daily XP cap (20 a day), so two accounts racing each other cannot farm the badge. The game’s sync never sends the field (player/garageSync.ts): only the server writes it.

  • Top bar (menu/topBar/achievementsButton.ts): the gold shield with a star next to the rank badge opens the screen.
  • Achievements screen (achievements/scenes/Achievements.ts, its own feature module): every badge in a grid, done ones lit gold, with the count done at the top. The glyphs are drawn in code (achievements/view/achievementIcons.ts) until there is art.