Files
TheLadder/content/frontier/index.js
T
JesseMarkowitzandClaude Opus 5 5d645e6452 Make standing cost money to keep, in both settings
Each pack declares a lifestyle tier, bought on the second rung and charged for
every cycle after: Corporate Ladder's car and address, the Frontier's bay and
the room over the Ellis place. Falling behind what the job expects costs
standing, in steps that steepen as standing rises. A Dispatch coordinator
keeping up fully nets -21 a week and earns the difference out of the days. No
engine change; the only interface change is a tier display format.

Sized by measurement, not feel (DECISIONS #36): a -2/week penalty is invisible
against Dispatch's standing gains, and a player who chases standing every turn
beats any flat penalty — 99 standing while buying nothing, and $2,245 richer
than one who kept up. Hence the top step.

Two defects fixed on the way. The conformance suite excluded debt from the
economic baseline by matching /debt|owed|loan/i on path names, which would have
counted every lifestyle tier into it; packs now declare tuning.baselineExcludes.
And 'the promotion leaves a player better off' pooled end balances across
archetypes that do not spend alike, so it measured the mix — it is now derived
from the upkeep arithmetic.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RxdJFbLq1rBKsUeticGV1g
2026-09-12 21:53:03 -04:00

264 lines
11 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// The Frontier — the second content pack for The Ladder.
//
// Cold Fork, a freight town at the end of a stage line. You are a hired hand
// in Vane & Co.'s yard; the town has a marshal who is tired and a road that is
// not safe. Two rungs, the same as Corporate Ladder, and not one line of
// engine code between them.
//
// This pack exists to prove the split holds. Everything that makes it a
// different game from Corporate Ladder is in this directory: a four-day cycle
// instead of a week, money that arrives from work rather than from a wage,
// relationship stats named for a place where being owed a favour matters more
// than being liked, and a skill pair of nerve and trade.
import { characters } from './characters.js';
import { events } from './events/index.js';
export const frontier = {
id: 'frontier',
name: 'The Frontier',
blurb:
'Cold Fork, at the end of a stage line: a freight yard, a road that is ' +
'not safe, and a marshal who has been six years retiring.',
version: '0.1.0',
turnUnit: 'day',
startingStage: 'hand',
peopleHeading: 'Cold Fork',
stages: {
hand: { label: 'The Yard', title: 'Hired Hand' },
deputy: { label: 'The Office', title: 'Deputy Marshal' },
},
// Not a week. The stage comes every fourth day, and the whole town runs on
// that: the outfit settles up when the stage comes in, the boarding house
// collects on the rest day before it. A pack that wants a seven-day week
// says seven here; the engine has no opinion either way, and Corporate
// Ladder's calendar is the same field with different numbers in it.
calendar: {
cycleLength: 4,
cycleName: 'Run',
unitNames: ['Stage day', 'Haul', 'Yard', 'Rest'],
},
// Money is smaller here than in Corporate Ladder by a factor of about
// fifteen, and it behaves differently: the settlement covers board and room
// and very little else, so the difference between a good run and a bad one
// is what you took from the work. See `tuning` below for the arithmetic that
// is actually asserted.
state: {
'money': { initial: 40, min: -400, max: 100000 },
// The store ledger. Named for what it is rather than for a loan: nobody in
// Cold Fork lends money, they let you carry a balance.
'owed': { initial: 0, min: 0, max: 100000 },
'standing': { initial: 50, min: 0, max: 100 },
'skills.nerve': { initial: 5, min: 0, max: 100 },
'skills.trade': { initial: 5, min: 0, max: 100 },
// The same shape as Corporate Ladder's lifestyle, in this setting's own
// terms and at this setting's own scale: what the office expects a man to
// turn up on, and what keeping that up costs. See DECISIONS #36.
'lifestyle': { initial: 0, min: 0, max: 2 },
'flags.carries_iron': { initial: false },
'flags.owes_the_store': { initial: false },
'flags.covered_for_dutch': { initial: false },
'flags.sworn_in': { initial: false },
'flags.spoke_for_dutch': { initial: false },
'notable': { initial: [] },
},
// A different vocabulary from Corporate Ladder's likes/fears/helps, and the
// reason the vocabulary is content's to choose. Out here the useful question
// about a person is not whether they like you: it is whether they will take
// your word, and whether they are carrying a debt to you they mean to pay.
relationshipStats: {
trusts_you: { label: 'Takes your word', initial: 45, min: 0, max: 100 },
owes_you: { label: 'Owes you', initial: 10, min: 0, max: 100 },
wary_of_you: { label: 'Wary of you', initial: 15, min: 0, max: 100 },
},
// The economic shape, and the thing that makes this pack a real test of the
// engine rather than Corporate Ladder in a hat: there is no wage worth
// living on. Vane settles up when the stage comes in and it covers the room
// and the feed and four dollars besides. Everything else you earn, you earn
// on a turn, by choosing to — and what the choices cost is where the
// pressure comes from, not an unexplained drain.
//
// Keep this arithmetic honest when tuning. Per four-day run:
// hand: -12 board -8 room +24 settlement = +4
// deputy: -12 board -8 room +38 pay = +18
// Both are asserted directly by the conformance suite against `tuning`
// below, so a number changed here without re-deriving it fails the build
// rather than trapping a player forty turns in.
upkeep: [
{ label: 'Board and feed', effects: [{ path: 'money', op: 'add', value: -3 }] },
// Turn 1 is a Stage day, so offset 3 is the first Rest day and offset 4
// the next Stage day — the room is collected the night before the outfit
// settles up, which is the boarding house's whole business model.
{
label: 'Room at the Cordwood',
every: 4,
offset: 3,
effects: [{ path: 'money', op: 'add', value: -8 }],
},
{
label: 'Settlement',
every: 4,
offset: 4,
requires: [{ path: 'stage', op: '==', value: 'hand' }],
effects: [{ path: 'money', op: 'add', value: 24 }],
},
{
label: 'Deputy pay',
every: 4,
offset: 4,
// The town pays a real wage, and it is still not much of a living. What
// changes with the star is not the money; it is what the days contain.
requires: [{ path: 'stage', op: '==', value: 'deputy' }],
effects: [{ path: 'money', op: 'add', value: 38 }],
},
// What the star is expected to arrive on, and what that expectation costs
// to meet. The office nets +18 a run; a deputy on his own horse is +11,
// and one with the horse and the room is +4 and makes the rest on the
// days. Money is meant to keep mattering after the promotion — but the
// first pass charged 9 for the room and left a keeper's median balance at
// $8 after 200 turns, which is not pressure, it is the debt trap this pack
// was tuned twice to avoid.
{
label: 'Feed and shoeing',
every: 4,
offset: 4,
requires: [{ path: 'lifestyle', op: '>=', value: 1 }],
effects: [{ path: 'money', op: 'add', value: -7 }],
},
{
label: 'The room over the Ellis place',
every: 4,
offset: 4,
requires: [{ path: 'lifestyle', op: '>=', value: 2 }],
effects: [{ path: 'money', op: 'add', value: -7 }],
},
// Gated on the office for the same reason Corporate Ladder gates its
// equivalent on Dispatch: a hand cannot afford any of this, and being
// charged standing for that would be a trap rather than a choice.
{
label: 'What the star is expected to be',
every: 4,
offset: 4,
requires: [
{ path: 'stage', op: '==', value: 'deputy' },
{ path: 'lifestyle', op: '<', value: 1 },
],
effects: [{ path: 'standing', op: 'add', value: -2 }],
},
{
label: 'What the star is expected to be',
every: 4,
offset: 4,
requires: [
{ path: 'stage', op: '==', value: 'deputy' },
{ path: 'standing', op: '>=', value: 65 },
{ path: 'lifestyle', op: '<', value: 2 },
],
effects: [{ path: 'standing', op: 'add', value: -2 }],
},
{
// The top step, for the reason Corporate Ladder's has one: without it a
// man who plays for standing every single day holds a hundred of it on a
// livery horse, and the bay is money thrown away. With it he settles in
// the low eighties and the last stretch has to be bought.
label: 'What the star is expected to be',
every: 4,
offset: 4,
requires: [
{ path: 'stage', op: '==', value: 'deputy' },
{ path: 'standing', op: '>=', value: 80 },
{ path: 'lifestyle', op: '<', value: 2 },
],
effects: [{ path: 'standing', op: 'add', value: -14 }],
},
// Store credit costs more the deeper in you are, the same step function
// Corporate Ladder uses for its loan — a shape, not a setting.
{
label: 'Store credit',
every: 4,
offset: 4,
requires: [{ path: 'owed', op: '>', value: 0 }],
effects: [{ path: 'money', op: 'add', value: -3 }],
},
{
label: 'Store credit (carried)',
every: 4,
offset: 4,
requires: [{ path: 'owed', op: '>=', value: 60 }],
effects: [{ path: 'money', op: 'add', value: -3 }],
},
{
label: 'Store credit (posted)',
every: 4,
offset: 4,
requires: [{ path: 'owed', op: '>=', value: 120 }],
effects: [{ path: 'money', op: 'add', value: -4 }],
},
],
// What the conformance suite holds this pack to. Declaring it here rather
// than writing it into a test is the same principle as the rest of the pack:
// the numbers a setting must hit are the setting's business. See
// DECISIONS.md #31.
tuning: {
// Net movement of `money` from upkeep alone, per calendar cycle, at each
// stage. Upkeep is the floor a player plays against: barely above water in
// the yard, and a real if unremarkable living with the star.
baselinePerCycle: {
hand: [0, 12],
deputy: [10, 30],
},
// States a player opts into, excluded from the baseline above: the floor
// is what a man faces owing nothing and keeping nothing.
baselineExcludes: ['owed', 'lifestyle'],
},
display: {
'money': { label: 'Money', format: 'money', order: 1 },
'owed': { label: 'Owed at the store', format: 'money', order: 2, hideWhenZero: true },
'standing': { label: 'Standing', format: 'meter', order: 3 },
'skills.nerve': { label: 'Nerve', format: 'meter', order: 4 },
'skills.trade': { label: 'Trade', format: 'meter', order: 5 },
'lifestyle': {
label: 'What you keep', format: 'tier', order: 6, hideWhenZero: true,
labels: ['The livery horse', 'Your own horse', 'Horse and a room'],
},
'notable': { hidden: true, inLedger: false },
'flags.carries_iron': {
label: 'The scattergun', hidden: true,
whenTrue: 'You are carrying Vane’s scattergun on the box.',
whenFalse: 'The scattergun is back under the office counter.',
},
'flags.owes_the_store': {
label: 'The store ledger', hidden: true,
whenTrue: 'Your name is in the book at the store.',
whenFalse: 'You are square with the store.',
},
'flags.covered_for_dutch': {
label: 'Dutch’s run', hidden: true,
whenTrue: 'Dutch owes you a run.',
whenFalse: 'You and Dutch are square.',
},
'flags.sworn_in': {
label: 'The star', hidden: true,
whenTrue: 'You are sworn in.',
whenFalse: 'You are back in the yard.',
},
'flags.spoke_for_dutch': {
label: 'Dutch’s name', hidden: true,
whenTrue: 'Dutch’s name is in for the good runs.',
whenFalse: 'Dutch’s name is not in for the good runs.',
},
},
characters,
events,
};
export default frontier;