Add the Corporate Ladder pack, and two engine features it needed
Seventeen events across the mailroom: ordinary days, Marlene Hobb and Trevor Pike, a misrouted envelope that can be delivered or kept, and a hard-times branch that fires on negative money and always leaves an ungated way out. Writing real content turned up two things the engine could not express. Per-turn upkeep (`pack.upkeep`): the MVP spec wants recurring income and costs, but effects only run when a player chooses something, and baking wages into every option would be tedious and easy to get wrong. Upkeep entries carry their own conditions, which turned out to be enough for the loan service charge to be a three-step function that escalates with the size of the debt — no multiply operation, no engine change. Upkeep changes are recorded separately from choice changes so the UI can distinguish them. Per-character starting relationships: Trevor starts below the pack default on `likes_you` because he arrived two weeks earlier and has priced that in. Characterisation as data rather than as an opening cutscene. Nobody starts feared, either — `fears_you` defaults to 10, since a midpoint default would mean every new hire arrives already intimidating. The content suite plays 60 seeded runs and asserts every event fires and every option is taken at least once. That caught real dead content: Trevor's favour chain sat behind a gate no plausible player could reach, taking two events and a hard-times escape route with it, and debt could pass 5,500 while the service charge stayed flat, so the failure spiral flattened out instead of tightening. Both fixed in the data — the gate softened and given another route up, the loan capped and its charge tiered. The validator also gained a real fix: it counted an event with a cooldown as a stage's fallback, but a cooldown can exclude it, so a stage could still run dry. 86 tests. Reasoning in docs/DECISIONS.md §11-13, including the seeding trap that makes a correlated chooser look exactly like a content bug. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VMSFHyVPitUoosW5wyEADj
This commit is contained in:
co-authored by
Claude Opus 5
parent
cc59b81155
commit
1d4ac0fdbd
@@ -107,3 +107,45 @@ The product is **The Ladder**; the default content pack is **Corporate
|
||||
Ladder**. The planning docs use "Corporate Ladder" as the working title for
|
||||
both, which would have baked one setting into the product name, against the
|
||||
explicit goal that corporate is one pack among many.
|
||||
|
||||
## 11. Per-turn upkeep is declared by the pack
|
||||
|
||||
The MVP spec wants "basic income and basic recurring costs", but effects only
|
||||
run when a player chooses something. Baking wages and rent into every option
|
||||
would be tedious and easy to get wrong.
|
||||
|
||||
`pack.upkeep` is a list of `{ label, requires?, effects }` applied at the end of
|
||||
each turn, after the choice lands. It is still data — the engine reads it and
|
||||
knows nothing about wages — and the conditions make it expressive enough to be
|
||||
useful: Corporate Ladder's loan service charge is three conditional entries
|
||||
forming a step function that escalates with the size of the debt, which needed
|
||||
no multiply operation and no engine change.
|
||||
|
||||
Upkeep changes are recorded separately from choice changes in the turn record,
|
||||
so the UI can show "you chose this" and "and then the month happened" as
|
||||
different things.
|
||||
|
||||
## 12. Characters may start somewhere other than the pack default
|
||||
|
||||
`relationshipStats` sets the defaults; a character's `startingRelationship`
|
||||
overrides individual stats. Trevor starts below the default on `likes_you`
|
||||
because he arrived two weeks before you and has priced that in — which is
|
||||
characterisation expressed as data rather than as an opening cutscene.
|
||||
|
||||
Nobody starts feared: `fears_you` defaults to 10 rather than 50, because a
|
||||
midpoint default would mean every new hire arrives already intimidating.
|
||||
|
||||
## 13. Content is tested for reachability, not just validity
|
||||
|
||||
`validatePack` proves a pack *can* run. It does not prove any of it is ever
|
||||
seen. The content suite plays 60 seeded runs and asserts that every event fires
|
||||
and every option is taken at least once — dead content is otherwise invisible,
|
||||
and two whole events plus a hard-times escape route were in fact unreachable on
|
||||
the first pass because Trevor's gate sat above anything a player could
|
||||
plausibly reach.
|
||||
|
||||
**A trap that cost a cycle:** the chooser's RNG must be seeded differently from
|
||||
the game's. Both draw exactly one number per turn from the same generator, so a
|
||||
shared seed correlates the choice index with the event draw and makes whole
|
||||
option combinations structurally unreachable. It presents as a content bug and
|
||||
is not one. Any headless playtest harness written later has to know this.
|
||||
|
||||
Reference in New Issue
Block a user