Files
TheLadder/content/countinghouse/index.js
T
JesseMarkowitzandClaude Opus 5.5 3108319a2b Add chance and a cast that changes between runs, and a pack to try them
An option used to do exactly what it said. Now it may carry a check: one of
four attributes (Nerve, Wits, Charm, Resolve) against a set difficulty or
against a named person's own attribute, leaning on how they regard you, with a
success and a failure. The player sees only the odds in words ("a long shot",
"likely"), never a number. The roll is one draw from the run's generator, so a
run still replays exactly from its seed and choices.

A character may be a role with several authored candidates, one drawn per run,
or a group of two to four drawn into numbered slots. Everyone carries a name, a
description and the same four attributes as the player, so a replayed pack
meets a different office. Text names people as {partner} or {clerk}, and an
event about "a clerk" binds one present clerk for the turn. The draw and the
binding are saved with the run, and a save from before a pack drew a cast gets
the same one every time it is loaded.

All of it is generic and optional: Corporate Ladder and the Frontier use none
of it and play as before. The Counting House, a one-stage office about 1910
built from the story-to-pack catalogue, exercises it, and was tuned against the
conformance suite by measurement. DECISIONS #41. Version 0.4.0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018MhABSrqkwTdcybXmheDPr
2026-09-23 22:12:26 -04:00

137 lines
4.9 KiB
JavaScript

// The Counting House — a prototype pack for checks and a drawn cast.
//
// Harrow & Pike, a trading firm's counting house, somewhere about 1910. You
// are the newest clerk. The situations are the ones public-domain office
// fiction kept returning to (tools/story-to-pack, catalogue v3); the people are
// drawn fresh each run from several candidates per role, two to four clerks
// among them; and most choices are a check, shown to the player only as odds
// in words. See DECISIONS #41.
//
// One stage, deliberately: this pack exists to find out whether chance and a
// varying cast make the same situations worth replaying, before a whole career
// is built on them.
import { characters } from './characters.js';
import { events } from './events.js';
export const countingHouse = {
id: 'countinghouse',
name: 'The Counting House',
blurb:
'Harrow & Pike, about 1910: a stool, a pen, a chief clerk with red ink, and a ' +
'different office every time you start.',
version: '0.1.0',
turnUnit: 'day',
startingStage: 'desk',
peopleHeading: 'Harrow & Pike',
stages: {
desk: { label: 'The Desk', title: 'Junior Clerk' },
},
// A six-day week: the office shuts at one on Saturday, and that is pay day.
calendar: {
cycleLength: 6,
cycleName: 'Week',
unitNames: ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
},
// The four attributes, the same for the player and for everyone in the cast.
// A pack may call them what it likes; these are the prototype's names.
attributes: {
nerve: { label: 'Nerve' },
wits: { label: 'Wits' },
charm: { label: 'Charm' },
resolve: { label: 'Resolve' },
},
// How attributes turn into chances. The player sees only the words.
// chance = 50 + 8 x (yours - theirs) + 0.4 x (their regard - 50), from 5 to 95
// A new clerk (3) against a sharp partner (8) is 10%, a long shot; against an
// ordinary task (4) it is 42%. Each point of training is worth eight.
checks: {
base: 50,
perPoint: 8,
unopposed: 4,
relationshipPerPoint: 0.4,
relationshipCentre: 50,
floor: 5,
ceiling: 95,
odds: [
{ upTo: 20, words: 'a long shot' },
{ upTo: 40, words: 'unlikely' },
{ upTo: 60, words: 'an even chance' },
{ upTo: 80, words: 'likely' },
{ upTo: 95, words: 'a safe bet' },
],
said: { success: 'and it came off.', failure: 'and it did not come off.' },
},
state: {
'money': { initial: 20, min: -200, max: 100000 },
'standing': { initial: 40, min: 0, max: 100 },
'attributes.nerve': { initial: 3, min: 0, max: 10 },
'attributes.wits': { initial: 3, min: 0, max: 10 },
'attributes.charm': { initial: 3, min: 0, max: 10 },
'attributes.resolve': { initial: 3, min: 0, max: 10 },
// Borrowed from the other clerks when times are hard, repaid on a Saturday.
'owed': { initial: 0, min: 0, max: 100000 },
// Rises won from the partner: each is a permanent addition to the wage.
'rises': { initial: 0, min: 0, max: 3 },
'flags.pawned_watch': { initial: false },
'notable': { initial: [] },
},
// Two axes, not one: whether they believe you, and what they think of you.
relationshipStats: {
trust: { label: 'Trusts you', initial: 50, min: 0, max: 100 },
regard: { label: 'Thinks well of you', initial: 50, min: 0, max: 100 },
},
characters,
events,
// Per six-day week: -12 board and lodging +16 wages = +4.
// Each rise adds four; a rise is earned, so it is outside the baseline.
upkeep: [
{ label: 'Board and lodging', effects: [{ path: 'money', op: 'add', value: -2 }] },
{
label: 'Wages from Harrow & Pike',
every: 6,
offset: 5,
effects: [{ path: 'money', op: 'add', value: 16 }],
},
...[1, 2, 3].map((n) => ({
label: n === 1 ? 'Your rise' : `Your rise (${n})`,
every: 6,
offset: 5,
requires: [{ path: 'rises', op: '>=', value: n }],
effects: [{ path: 'money', op: 'add', value: 4 }],
})),
],
tuning: {
baselinePerCycle: { desk: [0, 10] },
baselineExcludes: ['rises'],
},
display: {
'money': { label: 'Money', format: 'money', order: 1 },
'owed': { label: 'Owed to the floor', format: 'money', order: 2, hideWhenZero: true },
'standing': { label: 'Standing', format: 'meter', order: 2 },
'attributes.nerve': { label: 'Nerve', format: 'meter', order: 3 },
'attributes.wits': { label: 'Wits', format: 'meter', order: 4 },
'attributes.charm': { label: 'Charm', format: 'meter', order: 5 },
'attributes.resolve': { label: 'Resolve', format: 'meter', order: 6 },
'rises': { label: 'Rises', order: 7, hideWhenZero: true },
'notable': { hidden: true, inLedger: false },
'flags.pawned_watch': {
label: 'Your father\'s watch', hidden: true,
whenTrue: 'Your father\'s watch is at the pawnbroker\'s.',
whenFalse: 'You have your father\'s watch back.',
},
},
};
export default countingHouse;