← Back to the gamebug or idea?Terrapin Trail//Code BibleIIhow the game works, archive to van
Reel 08 · 6 min read

Economy, buffs & caps

Stats and their ceilings, van parts feeding a buff object, and the shared mutable CAP.

The economy is eight stats, four of them capped, plus a buff layer that van parts feed into. The one subtlety worth real attention is that a cap is shared mutable state.

Stats and caps

Stats live on S: cash, gas, grub, van (0–100), vibes, tapes, blanks, cred (0–100), plus counters (legendaryTapes, showsAttended, miles, day). Gas, Grub, Van, and Vibes have ceilings. The base ceilings are a module constant; the live ceilings are a separate, mutable object:

var BASE_CAP = { gas: 20, grub: 40, van: 100, vibes: 100 };  // game.js:923
var CAP      = { gas: 20, grub: 40, van: 100, vibes: 100 };  // game.js:924

Every clamp(v, 0, CAP.x) in the codebase reads CAP, and CAP can change mid-run.

Buffs: van parts fold into an accumulator

Van parts don't mutate stats directly — each contributes to a buff object, and one function folds them all together and re-derives the caps:

●assets/game/game.js : 1123–1137trace ↗
1123  function recomputeBuffs(s) {
1124    var b = { gasCap: 0, grubCap: 0, wearReduce: 0, showVibes: 0, bustGuard: 0, tapeQuality: 0, roadLuck: 0 };
1125    s.vanmods = s.vanmods || {};
1126    Object.keys(s.vanmods).forEach(function (slot) {
1127      var pid = s.vanmods[slot];
1128      var part = pid && VAN_PARTS.filter(function (p) { return p.id === pid; })[0];
1129      if (part) part.buff(b);
1130    });
1131    s.buff = b;
1132    CAP.gas = BASE_CAP.gas + b.gasCap;
1133    CAP.grub = BASE_CAP.grub + b.grubCap;
1134    // Clamp current gas/grub down if a part was removed and lowered the cap.
1135    if (s.gas > CAP.gas) s.gas = CAP.gas;
1136    if (s.grub > CAP.grub) s.grub = CAP.grub;
1137  }
recomputeBuffs: rebuild s.buff from installed parts, then set CAP.gas/grub from BASE_CAP + the buff, and clamp current values down if a cap dropped.

This is the codebase's sharpest gotcha, so hold it clearly: CAP is shared, module-global, and mutable. Two rules follow, and breaking either produces bugs that only appear after a save/load or a part swap:

  • Re-run recomputeBuffs after any change to installed parts.
  • Re-run it on save-resume too — a loaded save restores S.vanmods but not the derived CAP, so applySave calls recomputeBuffs explicitly to rebuild it.

statCap(stat) (game.js:1007) returns the ceiling for a given stat and is what the Peak clamps against.

Mods vs. parts vs. consumables

Three different money-shaped systems, deliberately kept distinct:

  • Mods (builder) set stats/flags and are re-derived from baseStats every toggle — so they must never spend cash.
  • Van parts are bought as gear, installed into slots, and feed recomputeBuffs.
  • Consumables live in the stash; some are active (use(s)), some passive/auto (the spare tire auto-saves a blowout).

VAN_PARTS and CONSUMABLES are indexed together in GEAR_INDEX for name/lookup, which is why a "buy gear" flow can treat them uniformly and only the install step cares which is which.

Key takeaways
  • Four stats are capped; the live cap (CAP) is mutable and separate from BASE_CAP.
  • Van parts feed a buff accumulator; recomputeBuffs re-derives CAP and must run after any part change AND on load.
  • Mods, parts, and consumables are three distinct systems — mods never touch cash because they're re-derived.