Skip to content

Gold

Gold buys speed, never a higher ceiling. It fills the fuel tank, swaps for cash and buys a few brand tokens of the player’s pick, so a player who spends reaches the same cars and brand levels sooner, never ones a free player cannot reach. Three free sources pay it (see below): at most 25 gold a day and 50 from the weekly event, 225 a week. The design is in docs/2026-11-08-progression-design.md (“Currencies”); every price is an estimate to tune after playtesting.

What Price Limit
A full tank (10 fuel), from any amount 30 gold refused when the tank is full
Cash 1 gold = 5% of the furthest open district’s average event prize, rounded to $10 any whole amount; the dialog offers 10, 50 and 250
1 brand token, any brand 20 gold 3 a week (ISO week, Monday 00:00 UTC)

The cash rate per district: Street $60, Harbor $170, Chinatown $260, the Strip $280. A replayed win pays 20% of the prize, so 1 gold is a quarter of a replayed win wherever the player is, and 550 gold (the EUR 4.99 pack in docs/2026-11-06-monetization-design.md) buys about three days of racing in any district (44 races a day, “Economy as built”).

A tank of 10 races is worth about 50 gold in replayed wins at that rate, so racing on a bought tank pays more than swapping the same 30 gold for cash. The free 50 gold a week buy one or two tanks. A free player earns about 15 brand tokens a week by racing, so the 3 bought tokens add a fifth at most.

Source Gold Limit
Weekly event wins 10, 15, 25 the first win of each rung, once a week (see Weekly event)
The day’s first won career event, boss fight or tournament 10 once a UTC day
Rewarded gold ad 5 3 a UTC day

Sized against the 30 gold tank: a day’s free gold (25) stays under a tank, so it adds at most about 8 races of fuel to the 36 a day in the play model. The gold ad’s 5 gold are about 1.7 fuel, less than the fuel ad’s 2, so the fuel ad stays the better ad for fuel. A won career event, a won boss fight and a won tournament (the final won) count for the first-win gold, each series once as a whole, not per round. Quick races and weekly races do not.

player/gold/ holds them, with no Phaser, so the race server and the game run the same code.

  • goldPrices.ts: TANK_PRICE, GOLD_CASH_SHARE, CASH_BUNDLES, TOKEN_PRICE, TOKENS_PER_WEEK and goldCashRate(progress), built on averageEventPrize (player/career/progress.ts), the same base as the daily bonus and the quick race prizes.
  • freeGold.ts: GOLD_PER_AD, with the first-win gold and the ads a day from remote config (dailyWinGold 10, goldAdsPerDay 3, remote config), payDailyWinGold (adds the gold to the garage and to the race’s award) and applyGoldAd, both counted in GarageState.goldDay ({ date, winPaid, ads }, the UTC day from ads/dailyBonus.ts bonusDay). A new day starts a new goldDay.
  • goldActions.ts: the three spending actions and goldAd. goldFuel fills the tank with fillFuel (player/fuel/fuel.ts); goldCash takes a whole number of gold and pays it at the current rate; goldToken adds one token of the named brand and counts it in GarageState.goldTokens ({ week, count }, the week key from player/weeklyEvent/weekCalendar.ts). Each refuses without changing anything: Not enough gold., a full tank, a bad amount, an unknown brand or No gold tokens left this week.

tests/freeGold.test.ts covers the first-win gold once a day, the gold ad’s cap and their reset at midnight UTC, and that the game cannot set goldDay. tests/gold.test.ts covers the tank price, the rate per district, the exchange, the weekly token cap and its reset on Monday, and that the game’s own copy of gold and the token count is ignored.

The actions are garage actions (player/garageActions.ts), applied by POST /api/garage/action (party/src/api/garageApi.ts) with the server’s clock: the server decides the week and the fuel, and works out every price itself. goldAd is an ad reward like fuelAd and dailyBonus: it pays only against an ad credit AdMob confirmed (party/src/api/adCredit.ts). The first-win gold is paid where the server settles a judged career event (party/src/race/judgedRace.ts) or a series (settleSeries in race/judge/seriesRound.ts), and comes back in the race’s award (RaceAward.gold), which the results line shows as “+10 gold for today’s first win”. gold, goldTokens and goldDay are not among the fields the game’s sync sends (player/garageSync.ts), so the game never sets its own gold.

  • Top bar (ui/topBar/goldGauge.ts): a coin and the amount left of the fuel. Tapping the gold or the fuel opens the gold dialog; a spend redraws the menu.
  • Gold dialog (ui/gold/goldDialog.ts): fill the tank, swap for cash (a second dialog with the amounts and what they pay) or brand tokens (a dialog with the 4 brands and the count the player has of each). “Watch an ad” (ui/gold/goldAd.ts) shows while a rewarded ad is loaded and one of the day’s 3 is left; paid, the menu redraws.
  • Results (race/raceAward.ts): the prize line ends with “+10 gold for today’s first win” when the race paid it. The pink slip, a boss re-race win and a tournament’s end add “+10 gold” to their prize line (player/gold/winGoldText.ts).
  • Out of fuel (ui/fuelGuard.ts): with enough gold, “Fill the tank” sits next to the fuel ad and starts the race once paid.
  • Brand dialog in the parts shop (shop/parts/brands/BrandDialog.ts): “Buy tokens” opens the token pick and comes back to the brand dialog after.
  • DEV menu: “+100 gold” adds gold to a dev account’s garage.

Four consumable store products (player/gold/goldPacks.ts): gold_100, gold_550, gold_1200 and gold_2500, priced by the store in the player’s currency (EUR 0.99 to 19.99 planned, docs/2026-11-06-monetization-design.md). Only the native apps sell them, with a RevenueCat key for the platform (canBuy in player/account/purchases.ts).

  • Buying (ui/gold/buyGold.ts): the gold dialog’s “Get gold” row opens the packs (and the gold ad, when one is loaded). The RevenueCat plugin makes the sale, signed in with the account id as RevenueCat’s app user id; then the game calls POST /api/purchases/sync and shows “+550 gold”. A cancelled purchase sheet shows nothing.
  • Crediting (party/src/api/purchaseApi.ts, party/src/db/creditedPurchases.ts): the race server reads the account’s one-time purchases from RevenueCat’s API v2, looks up each product’s store id, and credits every gold pack it has not credited before. credited_purchase (migration 0014) records each one by the store’s purchase id (a Google Play order id), in the same D1 batch as the garage change and only if the garage is still the version read, so a purchase pays once even when two syncs run at the same time. The game never sets its own gold.
  • At start (settleStorePurchases, from startAccount): the app configures RevenueCat, which hands over a purchase the app never got to finish, and syncs, so a pack paid just before a crash still pays.
  • Refunds: a credited purchase RevenueCat now reports refunded takes its gold back at the next sync, once. Gold may go below zero; nothing can be spent until it is back up.
  • Guests: after a purchase, a guest is asked to link the account in Settings, since a guest’s gold is lost with the phone. When a guest links an account that has its own save (party/src/db/linkGuest.ts), the dropped save hands its gold to the kept one, up to what that account bought and still owns (its free gold stays behind, or guests could farm it), and its purchase records move to the account. The account also remembers the guest (merged_guest), because RevenueCat keeps listing the guest’s purchases under the guest’s id: each sync reads those too, so a pack bought but not yet credited when the guest linked (offline, or the app killed before its sync) still pays out.
  • Key: the worker’s REVENUECAT_API_KEY needs customer_information:purchases:read and project_configuration:products:read. Product lookups are cached per worker (that API allows 60 calls a minute); the purchase list allows 480 a minute, which every app start uses once.

npm --prefix party run test:purchases checks crediting against a stand-in RevenueCat (once, two pages, two syncs at once, a refund, the store down); test:link checks the gold that moves on a link.

  • The play-length model (npm run career:check) does not count the free gold’s fuel.