Skip to content

Make the fleet passive and put the player at the helm - #140

Merged
dmccoystephenson merged 5 commits into
mainfrom
feature/captained-voyages
Aug 1, 2026
Merged

Make the fleet passive and put the player at the helm#140
dmccoystephenson merged 5 commits into
mainfrom
feature/captained-voyages

Conversation

@dmccoystephenson

Copy link
Copy Markdown
Member

Summary

The fleet becomes the background economy, and captaining a boat yourself becomes the game. Two halves, landed together because either alone leaves the game worse: passive-only removes the interactive content, and voyages-only would pay twice for the same day.

The fleet runs itself

Every role earns each morning now, not just fishing — hauling and transport bring in money, piracy brings in the most plus seized fish. A bad day of piracy damages the hull and, rarely, costs a villager.

That last part needed a way to tell the player, so increaseDay now returns an overnight report surfaced everywhere a day passes (sleeping, a night at the tavern, an export run, a voyage). Losing someone you hired by name should never be something you only notice because the roster got shorter.

Taking the helm — src/business/adventures.py

Take the Helm at the docks. Pick a boat, pick how far out (short run / middle grounds / far water — longer, richer, less forgiving), provision her, then sail her a leg at a time. Each leg is a day at sea and puts a situation in front of you:

LEG 3 of 7   Hull 74%   Supplies 12   Crew 4

A squall builds to the north and the light goes the colour of a bruise.

 [1] Run before it
 [2] Hug the coast and lose a day
 [3] Iris Dunmore reads the sky for a gap
 [4] Ride it out at anchor

That third option exists only because Iris Dunmore is aboard. Choices are gated on the specialties of the villagers you actually brought — Cormac Ide finds the seam in a leaking hull, Junia Marsh goes over the side after what sank, Bastian Roe nurses a sick crew through the night, Sena Vale works out where a merchantman will anchor so you take her asleep. Crewing a boat stops being a headcount and becomes casting, which is what makes the villager roster pay off.

Hull, supplies and crew only ever get scarcer unless a decision is spent on them, and the hold is only yours if you get home. If the hull gives out or the crew starve the voyage is cut short — everything aboard is lost and she comes back a wreck, but the boat is still yours, so a bad run is a setback rather than a save-wrecker. A boat at sea earns nothing at home; that's what taking the helm costs.

Nine events across four role-filtered pools, with the shared ones (squall, wreckage, a leak, sickness, becalmed) reachable by any role and the rest specific — a fishing voyage never meets a patrol cutter, and a raid never frets about its passengers.

Removals

src/business/voyages.py is deleted. The one-shot job runs it added this morning are superseded by captained voyages, and keeping them alongside passive income would have paid twice for the same day. totalRaids / totalPlunder now count days of passive piracy, so the counts already in live saves stay meaningful instead of being stranded.

Test plan

  • python3 -m compileall -q src tests
  • SDL_VIDEODRIVER=dummy SDL_AUDIODRIVER=dummy python3 -m pytest --verbose -vv --cov=src --cov-report=term-missing --cov-report=xml:cov.xml606 passed, 96% total; adventures.py and boats.py both 97%.
  • Ran the full suite five times over to confirm the flakiness below is gone.
  • black + autoflake over the changed files only.
  • Front-end parity. Everything goes through showOptions / showDialogue / currentPrompt — no new primitive, so console, pygame and web all get voyages.
  • Played a full voyage through the real console front-end, which is how both problems below were found.

Two things playing it caught

  • "Full stores" was a lie. One event (heaving to for the sick) ate crew × 2 supplies, which on a short voyage is half a full load — so a fully-provisioned 4-leg run starved on leg 3. Halved that cost, and added a deep stores option so insuring against a bad week is a decision the player gets to make rather than a trap.
  • Two tests were flaky, about 1 run in 6. They asserted a voyage always advances the day by its full leg count. It doesn't — a foundering voyage correctly ends early. Fixed to assert what's actually invariant, and added a deliberate test that a voyage which founders ends early and still comes home.

Balance note

A captained leg is worth several times what the same boat earns passively in a day, which is the point — but the numbers are first-pass. ROLE_LEG_VALUE, VOYAGE_PLANS' multipliers and ROLE_DAILY_PER_CREW are each a single table if it plays too hot or too cold.

dmccoystephenson and others added 5 commits August 1, 2026 15:41
The fleet becomes the background economy; captaining a boat yourself
becomes the game.

Passive fleet:
- Every role now earns each morning, not just fishing. Hauling and
  transport bring in money, piracy brings in the most plus seized fish -
  and a bad day of piracy damages the hull and can cost a villager
- Add an overnight report surfaced wherever a day passes (sleeping, a
  night at the tavern, an export run, a voyage), so losing a named crew
  member is news the player is told rather than a roster they notice
  changed later

Captained voyages (src/business/adventures.py):
- Take the Helm at the docks: pick a boat, pick how far out, provision
  her, then sail her a leg at a time. Each leg is a day at sea and puts a
  situation in front of you
- Hull, supplies and crew only get scarcer unless a decision is spent on
  them, and the hold is only yours if you get home
- The crew you brought unlock choices nobody else can offer - Iris
  Dunmore reads the weather, Cormac Ide patches a hull at sea, Sena Vale
  works out where a merchantman will anchor. Crewing becomes casting
- A voyage that founders is cut short: the hold goes over the side and
  she comes back a wreck, but the boat is still yours
- A boat at sea earns nothing at home; that's the cost of taking the helm

Removes src/business/voyages.py: the one-shot job runs are superseded,
and leaving them alongside passive income would have paid twice for the
same day. totalRaids/totalPlunder now count days of passive piracy so
existing saves keep their numbers.

Found by playing it: one event ate half a full stores load on a short
voyage, which made "full stores" a lie. Halved that cost and added a
deep-stores option so insuring against a bad week is the player's call.
Also fixed two tests that assumed every voyage runs its full length -
they were flaky about 1 run in 6, because a foundering voyage correctly
ends early.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
CI caught what ten local runs did not: pinning random.randint alone isn't
enough to guarantee a voyage completes. Events that consume stores don't
use randint at all, so a full load can still empty out, and the
starvation roll is random.random - which on a two-hand crew can take both
of them and founder her on the last leg.

Pin both dice in the two tests that need a completed voyage. Verified
with 25 consecutive runs of the docks suite.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
CI caught what ten local runs did not: pinning random.randint alone isn't
enough to guarantee a voyage completes. Events that consume stores don't
use randint at all, so a full load can still empty out, and the
starvation roll is random.random - which on a two-hand crew can take both
of them and founder her on the last leg.

Pin both dice in the two tests that need a completed voyage. Verified
with 25 consecutive runs of the docks suite and 10 of the whole one.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Reviewed and played each round, against Nielsen's heuristics and Krug's
"don't make me think".

1. Visibility of system status. Every boat's line now says what she earns
   a day, and the fleet header leads with income against payroll and the
   net. There was no way to tell a boat that paid her wages from one that
   didn't. Also fixed a label that read as a stutter - "Fishing Fleet,
   Fishing" - by leading with the role and labelling the hull.
2. Don't make me think. The plan menu quoted "x1.6 the pickings" against
   a per-leg value the player couldn't see; it now quotes what the voyage
   is worth in money. The boat picker says what taking her out gives up.
   The standing village prompt no longer follows you out to sea.
3. Error prevention. Half rations on a long voyage was certain starvation
   presented as a neutral option; every provisioning option now says which
   day it runs out on. Sailing a half-wrecked hull on an eleven-day voyage
   now warns first, with the repair cost. The repair refusal says how to
   raise the money.
4. User control and freedom. You can break off and run for home from any
   leg, keeping the hold - being committed to every leg made a thinning
   hull a slow walk to the bottom rather than a decision. The hold is now
   on the status line; the only numbers moving on screen were the money
   and fish at home, which climb from the rest of the fleet and read as
   progress when they weren't.
5. Recognition over recall. The docks screen names anything wrong with
   the fleet - a boat idle, a hull that can't sail, hands ashore on full
   wages - instead of leaving it to be found by opening the fleet screen.
   The overnight report puts the wages next to the takings so a day reads
   as a profit or a loss, and says where to repair a damaged boat.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
startVoyage marks a boat atSea and the flag gets serialized, so a save
written with it set would otherwise strand her out of the fleet economy
with no way back. The loader already rebuilds boats without it; this
holds that behaviour down.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@dmccoystephenson
dmccoystephenson merged commit 21d2048 into main Aug 1, 2026
1 check passed
@dmccoystephenson
dmccoystephenson deleted the feature/captained-voyages branch August 1, 2026 17:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant