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

The heartbeat: driving between shows

What a 'loop' is, and how the game turns one drive into fuel spent, morale lost, and events on the road.

The core of the game is one thing repeated over and over: you're at a parking lot, you get ready, you drive to the next show, you arrive at the next lot, repeat. That "do this again and again" is the game's heartbeat.

Concept: a "loop". A loop is code that repeats. Sometimes it repeats a fixed number of times; sometimes, like this game, the "loop" is the player doing the same cycle each show: prepare, drive, arrive, prepare, drive, arrive.

One trip between two shows is called a leg. A leg is where the game spends your resources, so it's worth walking through slowly.

First: what will this leg cost?

Before you can drive, the game works out what the trip needs — how much fuel and food:

●assets/game/game.js : 1478–1485trace ↗
1478  function legNeeds(s) {
1479    var next = STOPS[s.stop + 1];
1480    if (!next) return null;
1481    var gas = Math.ceil(next.miles / 70 * DNA.gas); // TT-DNA gas guzzling
1482    var rat = RATIONS[s.rations];
1483    var grub = Math.max(2, Math.ceil(crewAlive(s) * rat.per) + Math.floor(next.miles / 300) - (crewHas(s, "doses") ? 1 : 0));
1484    var gap = daysBetween(STOPS[s.stop].ymd, next.ymd); // days until the next show
1485    return { gas: gas, grub: grub, miles: next.miles, next: next, gap: gap };
Work out the leg's needs: fuel is based on the distance (and the Gas guzzling rule, chapter 15); food is based on how many people are in the van, the distance, and whether a certain crew member helps.

Concept: a "function that returns a value". This recipe doesn't change anything — it just calculates an answer and hands it back (fuel needed, food needed). Other parts of the game ask it "what will this cost?" and use the answer.

Then: spend it

When you actually drive, the game subtracts the fuel and food, and wears down the van a little:

●assets/game/game.js : 1518–1525trace ↗
1518    // consume
1519    S.gas = clamp(S.gas - need.gas, 0, CAP.gas);
1520    var pace = PACE[S.pace], rat = RATIONS[S.rations];
1521    var grubEat = need.grub;
1522    if (S.grub >= grubEat) { S.grub -= grubEat; }
1523    else { var deficit = grubEat - S.grub; S.grub = 0; S.vibes = clamp(S.vibes - deficit * 3, 0, CAP.vibes); addLog(S, "The larder ran dry on the road. The crew grumbles.", "bad"); }
1524    var leadFoot = S.flags && S.flags.leadFoot;
1525    var wear = 4 + pace.wear + (need.miles > 600 ? 4 : 0) + rnd(0, 2) + (leadFoot ? 3 : 0) - S.buff.wearReduce - (crewHas(S, "hitch") ? 1 : 0);
Driving subtracts the fuel and food. Running out of food hurts morale. Then the van takes wear, worked out from your speed, the distance, and a bit of luck.

Notice the wear line adds several things together: a base amount, extra for driving fast, extra for a long haul, a little randomness, minus any upgrades that reduce wear. That "add up all the things that matter" style is how the game turns a situation into a number. Your morale ("Vibes") is figured the same way, and long drives quietly drain it — which is why keeping spirits up is a real part of the strategy.

Finally: arrive, and handle whatever happened

Arriving isn't just teleporting to the next lot. The game checks, in a careful order: did anyone's spirits hit zero (game over)? Did a special "Peak" moment happen? Was there a road event to play out? Did the van break down? Did the crew share supplies because morale was high?

●assets/game/game.js : 1604–1620trace ↗
1604  function resumeLeg() {
1605    // check death first
1606    if (checkDeath()) return;
1607    // the Peak interjects once, before this leg's events resolve
1608    if (S.peakQueued) { S.peakQueued = false; showPeak(); return; }
1609    if (S.queue && S.queue.length) {
1610      var id = S.queue.shift();
1611      var ev = EVENTS.filter(function (e) { return e.id === id; })[0];
1612      S.currentEvent = ev; S.view = "event"; render(); return;
1613    }
1614    // van check on arrival
1615    if (S.van <= 0) {
1616      if (S.cash >= 50) { S.cash -= 50; S.van = 30; addLog(S, "The bus died on the shoulder. A tow and a prayer cost you $50.", "bad"); }
1617      else { endGame(false, "The bus finally gave out for good, and you couldn't cover the tow."); return; }
1618    }
1619    if (checkDeath()) return;
1620    // Vibes death-spiral: when morale craters, people start to walk. A van-mate may
Arriving runs a checklist in a strict order: check for game-over, then the Peak moment, then any road event, then whether the van broke down.

The order matters: the game checks for "game over" first, so a run that should have ended doesn't accidentally keep going for one more step. Getting that sequence right is exactly the kind of care that separates a working game from a buggy one.

The ideas to keep
  • A loop is repetition; here it's the prepare-drive-arrive cycle at each show.
  • A "leg" first calculates its cost, then spends fuel/food and wears the van — each number is a sum of everything that matters.
  • Arriving runs a careful, ordered checklist; the order is part of getting the game right.