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.
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.