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.
Prices
Section titled “Prices”| 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.
Free gold
Section titled “Free gold”| 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.
The rules
Section titled “The rules”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_WEEKandgoldCashRate(progress), built onaverageEventPrize(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 (dailyWinGold10,goldAdsPerDay3, remote config),payDailyWinGold(adds the gold to the garage and to the race’s award) andapplyGoldAd, both counted inGarageState.goldDay({ date, winPaid, ads }, the UTC day fromads/dailyBonus.tsbonusDay). A new day starts a newgoldDay.goldActions.ts: the three spending actions andgoldAd.goldFuelfills the tank withfillFuel(player/fuel/fuel.ts);goldCashtakes a whole number of gold and pays it at the current rate;goldTokenadds one token of the named brand and counts it inGarageState.goldTokens({ week, count }, the week key fromplayer/weeklyEvent/weekCalendar.ts). Each refuses without changing anything:Not enough gold., a full tank, a bad amount, an unknown brand orNo 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.
On the race server
Section titled “On the race server”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.
In the game
Section titled “In the game”- 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.
Gold packs
Section titled “Gold packs”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 callsPOST /api/purchases/syncand 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, fromstartAccount): 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_KEYneedscustomer_information:purchases:readandproject_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.
Not built yet
Section titled “Not built yet”- The play-length model (
npm run career:check) does not count the free gold’s fuel.