Skip to content

A week at the table

A “run” in Pale Jack is one night. A campaign is seven nights. Charms persist across nights within a campaign; recovered Pieces persist permanently across campaigns (the dealer hasn’t been keeping duplicates); the dealer learns your name on Night 2.

NightDayBeat
1SundayFirst sit-down. The dealer is friendly but does not name you.
2Monday”Ah, {name}. You came back.”
3Tuesday”Three nights, {name}. We’re practically familiars.”
4WednesdayThe midweek thinning.
4½—The Act Break beat. One per campaign.
5ThursdayThe lamp dims one notch. Pie reserved.
6Friday”On the first night you flinched at the lamp. You don’t now. I kept track.”
7Saturday”I sit on this side tonight.”
OutcomeWhat happens
ClearedAll 6 rooms cleared. Dawn. Charms intact, capital + winnings, and the dealer hands you back a Piece.
Cleared with strainFinal-room target missed but no last call. Partial reward — and the Piece still comes back.
Last callCapital hit zero, or terminal bust. The morning is heavier; you shed 1-2 charms. No Piece moves either way. The campaign continues.
Bergman foldVoluntary fold of the final hand. Campaign ends with accept_it. No Piece recovered.

Every campaign reaches Night 7 regardless of mid-campaign last calls. A failed night doesn’t end the campaign — it just doesn’t yield a Piece, and you shed a charm or two. You walk into Night 5 with what you’ve already collected: zero, three, six Pieces back. The campaign is a slow recovery, paced by clearance.

StateAcross nightsAcross campaigns
Player nameyesno (entered fresh)
Arcanum trayyesno (cleared)
Capitalyesno
Sanityyes (refreshed to 6 between)no
Recovered Piecesyesyes (permanent)
seenActBreakyesno
Hours of the Nightyes (banked)yes (permanent)
Mantle tieryesyes
Pages unlockedyesyes

CampaignState is persisted via SharedPreferences (key palejack:campaign:v1). One active campaign at a time. Auto-saves on every state transition (cleared / strain / last call / fold).

A player can quit after Night 3 of 7 and pick up at Night 4 weeks later. The controller’s resume() lands them on the right phase.

  • lib/data/campaign_state.dart — the immutable state model.
  • lib/data/campaign_store.dart — persistence.
  • lib/sim/campaign_runtime.dart — pure transitions (cleared / strain / last call / fold).
  • lib/sim/campaign_controller.dart — state machine + phase enum.
  • lib/sim/campaign_outcome_adapter.dart — TableScreen-end → controller transition dispatch.
  • lib/main.dart — Root wiring (kCampaignMode gates the path).

The web build at palejack.com is a single- night demo (kCampaignMode = false). Desktop builds flip the flag and unlock the full campaign. Same engine; the demo is effectively Night 1 of an unsaved campaign.