f847cb21719085cbb549d4fc9304f8d581d7ad77
22
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
f847cb2171 |
The sheet header said 9 while the body said 4 (R-256)
Verifying R-255 on a live sheet found the rule working everywhere it was built and contradicted in the first place a GM looks. The header's ARMOUR field is the hide the creature HAS; on a grounded beast the hit-location table under it read 4 and the header still read 9. Nothing computes off that field, so nothing in the code was wrong. It was wrong at the table. The field stays editable and stays 9 — that is the creature's hide and what a GM types into. Under it, when system.grounded is set, the sheet now says what is in play: DOWN: 4 · CHEST 9, derived from exposedHideFor rather than from a second opinion, with the reason on the tooltip. Verified live: no badge in the air, and grounded the badge and the location table agree at 4 with the forequarters at 9. check-rules forbids redefining a rule by NAME, which misses how this goes wrong in practice — nobody redefines exposedHideFor, they write Math.floor(hide / 2) in the sheet because it is cheaper than an import. I wrote that version first and check-rules passed it. It now fails any file outside rules.mjs that halves a hide inline, naming file and line; check files are exempt because stating the expected value independently is what a test is for. That guard also turned up something I have not fixed, recorded in R-256: armourAgainst lives in ringbrp.mjs rather than rules.mjs, and tools/simulate.mjs has its own inline copy of it. They agree today so no published number is wrong, but every figure in the bestiary and the lethality baseline comes out of that simulator. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
ddc4f99ff4 |
Hit locations reach ordinary combat, and creatures get a body
THE DEFECT. locationFor had ONE caller in the engine, hitLocationRoll, and hitLocationRoll was called from fall, burn, detonate and burstAttack. Falling, fire, explosions and automatic fire. Nothing else. rollWeaponDamage rolled dice, printed a number and returned it, so the chat Damage button, the character sheet's damage button and the item sheet's all stopped there. A grenade knew which arm it took off. A sword did not. Every individual piece was already correct - the tables, per-location armour and coverage, disable and destroy thresholds, Major Wound, dying, death, the figure on the sheet. They simply were not joined up, which is why this reads as a wiring change rather than a new subsystem. - rollWeaponDamage now carries through to hitLocationRoll: it rolls the location, applies that location's armour, applies the wound, and handles the rest through the machinery that already owned it. Nothing here re-implements any of that - locationModeFor picks the melee or ranged column from the weapon's class. The two columns have always differed and nothing was choosing between them - with no target it does exactly what it did before and SAYS SO on a card, rather than failing quietly - burstAttack opts out with locate:false - it rolls its own location per round, and letting both fire would have applied every round of a burst twice BODY PLANS. Two new tables: quadruped (four legs, fore/hindquarters, neck, a head that is hard to reach) and winged (that, plus wings). Forequarters are the vital. DISABLE EITHER WING AND IT CANNOT FLY, which is the fight a party can win. Barghest, Kelpie and Church grim were all species "baseline" - a black dog, a horse and a grave-hound, each hit-located with two arms and a chest. Fixed. Added "The supporter", a winged heraldic beast, so the new table is exercised by real content instead of existing unused. Body plan is a NEW FIELD, not a species. A bear is not a playable people with characteristic dice and talents, so creature specs take bodyPlan and the playable species list is untouched. THE NPC SHEET had no hit locations at all - 102 lines against the character sheet's 627 - while every NPC in the game had them derived and wired to consequences. It now carries the body-plan selector, the figure, the per-location table, the condition and a GROUNDED flag. The sheet class already extended the character sheet, so it already HAD the context; the template never used it. FOUND IN PASSING, by the new guard: kind "body" - the abdomen, the trunk, the hindquarters - did nothing whatever. Its printed effect has always promised "-30% to all Physical actions and bleeding 1 hit point per round until First Aid" and the code delivered none of it, on every body plan including the humanoid default. It now bleeds 1, or 2 destroyed, exactly as bodyX says. NEW GUARD: check-anatomy. Every body plan must cover 1-20 exactly once in BOTH modes, every location must be drawn and every drawn shape must be a location, and every kind must be one locationEffectsFor acts on, has effect text for, and has a destruction outcome. Verified it fails on a d20 gap, a shapeless limb and an unhandled kind before wiring it in. Ninth guard. VERIFIED IN A RUNNING FOUNDRY, not just unit-tested: melee bar reads the Melee column and the rifle reads Ranged on the same creature; forequarters graded as a vital hit; a maxed wing set flightLost and the sheet said GROUNDED; the real chat Damage button rolled 1D20 15, found the right foreleg, applied 12 through armour, disabled the leg and took the dog from 16 to 4; and with nothing targeted nothing was touched. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
85c1062106 |
Pause overlay: tighten the caption, lift the field grid
Both found by looking at the thing render, which nothing had done before — the earlier work on this overlay was verified through the CSSOM because the browser pane would not composite frames, so the geometry and timing were confirmed and the LOOK was not. Michroma is already a wide face, and .4em on top of it set GAME PAUSED at 176px against a 190px field: 93%, running bracket to bracket, so the readout was competing with the lock for the same width. .22em measures 154px, 81%, leaving 18px clear at each bracket. text-indent moves with it so the word stays optically centred. The field grid was drawn at opacity .3, which composited over the void band is 1.44:1 against its own ground. On a dark table map that is not a faint grid, it is no grid at all. .45 measures 1.80:1 — present as texture, still far below the 3.1:1 --rb-line is chosen for, so the amber lock stays the only thing asking for attention. Both measured in a browser with Michroma confirmed loaded, not estimated. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
b31b1028a9 |
Rebuild the pause overlay as a containment hold
The uniselector rotated forever, which is the one thing a hunting stepping switch never does, and it was carrying two outright bugs: the motion trail pointed the way the arm was going rather than the way it came, because conic angles run clockwise from the origin and the tip was declared first; and the hub pip ran on a free 2.6s sine against a 7.7s arm, so the light drifted permanently out of phase with the mechanism and meant nothing. Replaced with an instrument that acquires, locks, reports and lets go: a static field, four corner brackets that close and snap, a scan line reading down the field at exactly cycle/3 so its third pass always lands on the lock, and a readout welded to the same cycle. Every layer reads --rb-pause-cycle, so nothing can drift. The field offset is derived from the column rather than measured and pasted in. Also ignores the impeccable hook cache and keeps its config, which records why broken-image is suppressed for a stylesheet. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
24b1aaa682 |
Development: skills improve from what beat you, not what worked
Reversed experience. A skill is marked when it FAILS - failure or fumble - and never when it works, and the improvement roll is the same reversal: roll OVER your rating and the skill goes up 1d6. Nobody learns anything from the lock that opened first time. The two halves pull against each other on purpose, so ratings converge instead of running away. Simulated over 2000 campaigns: a 10% and an 85% skill start 75 points apart and are 11 apart after 60 cases. - rules.mjs owns it: DEVELOPMENT, developmentMarksOn, developmentImproves, developmentCeiling, developmentGainFor - trainingCap has been on the skill schema and the item sheet since it was written and nothing has ever read it. Development reads it now: a skill stops at its own cap, and everything else stops at DEVELOPMENT.ceiling, because a d6 gain could otherwise push a 98% skill past 100 - GM opens the phase from a GM-only toggle in the token controls; it posts a card naming every agent and their marked count, and puts a Development button on each sheet. Players press their own - the toggle re-reads the setting on change, since getSceneControlButtons only fires at load and the button would otherwise lie if the phase were opened by the API or a second GM - rules journal and player primer rewritten from the tables; the old text described the pre-reversal rule on both halves - 7 spot-checks, 4 behaviour tests; verified end to end in a running Foundry Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
37b4043746 |
Play a full case, then fix what it found
Ran THE HOLLOW ROAD with five agents — approach, crossing, contact, the thing on
the road, extraction, debrief — with panic and Coherence both live. It ran clean
and it found one thing that mattered.
ANOMALY LORE 0. Marta Holloway, Field Lead, Senior Field Officer, could not roll
the department's signature skill. Nor could Okonkwo. Induction was gated to
TRADES on the reasoning that a career posting is itself the agency training —
but 17 of the 25 postings do not list anomaly lore, its printed base is 0 on
purpose, and nothing else supplies it. So the people who joined the department
straight out of school were the only ones who never received its basic training,
which is exactly backwards.
Induction is a FLOOR for everybody now, posting or trade, in the live generator
and the pack builder both. It never demotes: Bhattacharya keeps Registry's 63,
Holloway goes 0 → 43, Okonkwo 0 → 33. All twenty built agents verified non-zero.
Two GM tools the case showed were missing, both found by feeling the friction
rather than by reading anything:
- clearPanic(actors). Panic clears when the scene does — one line in the book
and, until now, five sheets edited by hand while the table waited. It leaves
Coherence alone, which is the whole point of having two tracks.
- partyState(). Everybody's hit points, panic and Coherence in one card. With
five agents the alternative is asking "what are you on?" five times a scene,
which is friction a table absorbs silently and then blames on the game.
And the player-facing gap: the primer never mentioned panic. Players got a
one-page aide-memoire for a track they now meet in the first scene. It has a
page of its own and a row on the cheat sheet, both generated from the same
tables as the rules.
One thing I reported as a bug and withdraw: Okonkwo's "Firearm (Pistol) 5%" was
me rolling the wrong weapon. He carries a marksman rifle at 53%. check-kits was
right.
Version 1.2.0.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
5aaae186e0 |
A fumbled relief attempt puts a point on
Trying to steady somebody and making it worse is the most familiar thing on that list, and until now it was the only outcome that cost nothing. Done as a NEGATIVE relief rather than a second code path: panicReliefFor returns -1 on a fumble, and easePanic already subtracts what it is given, so subtracting a negative raises the track. One number, one direction, no branch that could drift from the other. The clamp still holds — fumbling at 5 stays at 5. The chat card had to be told, because otherwise it prints the word "Psychology" over a number that has just gone UP and the table reads that as a bug rather than as the rule. It now leads with a red FUMBLED tag. Guarded three ways: a spot-check in check-rules for the -1 itself, and two behaviour tests — one that the sign survives easePanic's subtraction and actually raises the track, one that it cannot push past the top. A test on the constant alone would have passed even if easePanic had healed on a fumble. Verified live: 2 → fumble → 3 → success → 2 → special → 0, and a fumble at 5 staying at 5, with the cards reading correctly. Version 1.1.2. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
cbad87ba98 |
Add Occult, Religion and Psychology, and the Panic track
From the first game's list. The three skills are not decoration — they are the
levers on the new track, which is why they arrived together.
PANIC, 0 to 5, up. It is the other half of Coherence and the rules page says so
in the first line, because a table that confuses them will make the wrong call
at the worst moment:
Coherence what knowing has cost you weeks 10-0, down
Panic what is happening to you NOW minutes 0-5, up
A trigger gives a point with NO ROLL. Asking for a die to find out whether you
are frightened slows the scene and tells the player how to feel; the die is for
getting rid of it, which is the interesting direction.
0 steady · 1 rattled · 2 shaken -5% · 3 losing it -10%
4 overwhelmed -20% · 5 broken
The first point is free on purpose, so the opening shock of a scene costs the
table nothing. At 5 you break — freeze, flee or fixate, choose fast — and come
back at 3, not at 0, because breaking is not a rest.
Relief is where the skills come in. A success takes one point, a special takes
two. Psychology is the only thing that steadies somebody who is NOT you.
Religion steadies you alone and does not care whether you are right. Occult is
the odd one: naming a thing correctly, out loud, helps everybody who can hear —
the department's actual work, and the only relief that reaches a room. Plus
POW×5 to steady yourself, and your anchor object once per case, which is what
that briefing scene was for.
The three skills are placed in six registers rather than left floating: Occult
to the professor and to Registry, Psychology to the doctor and to Interview,
Religion to Antiquities and Pathology.
Guards, because a track nothing reads is this project's oldest failure: the five
new rules carry spot-checks in check-rules, and check-behaviour gains nine tests
— the free first point, monotonic penalties, only 5 breaking, clamping at both
ends, breaking not clearing the track, special taking two, every band/trigger/
relief having a label, and every relief naming a skill the catalogue actually
has.
Verified in a live game: penalty reaches the roll (58 → 58 → 53 → 48 → 38 for
panic 0-4, criticals and specials recomputed with it), the full arc runs 0 → 2 →
4 → eased to 2 → pushed to 5 → breaks → 3 with no errors, the sheet shows the
band, and the rules journal has a Panic page generated from the same tables.
Version 1.1.0.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
0852427586 |
Fix the pause: it would not hide, it was off centre, and it was too small
Three faults in 1.0.4, and the first was the serious one.
1. It never went away. Core hides the overlay with `#pause { display: none }`
and shows it with `#pause.paused { display: flex }`. 1.0.4 styled the BARE
#pause with display:flex, which wins on specificity, so unpausing removed
the class and changed nothing — GAME PAUSED sat over the table for the rest
of the session. Every rule here is now scoped to `.paused` and `display` is
not set anywhere.
2. It was off centre because I repositioned it. Core already centres it: a
full-width band at top calc(50vh - 100px), column flex, centred both ways,
pointer-events none. That layout was correct and 1.0.4 fought it with
left/bottom/transform. It is left alone now; only the height changes, and
the top moves up by exactly half the extra so the centring holds.
3. It was small: a 322x116 panel in the corner. It is now core's full-width
band at 300px with a 170px dial — against core's own 100px — and the
caption at 24px to match core's.
Guarded: check-behaviour now parses the stylesheet and fails any rule that sets
display on an unscoped #pause. Verified it bites. It strips CSS comments first,
because the first version of the guard reported the comment explaining the
guard — which is exactly what the template guard did last time, so that is twice
now and the lesson is written down properly.
Verified by driving the real toggle: running → display none, box 0x0, no class;
paused → display flex, 341x302, centre X exactly on the viewport centre;
unpaused again → display none, box 0x0.
Version 1.0.5.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
ad771ff2b5 |
A uniselector for the pause
Foundry's pause is a spinning d20 and a caption. This replaces both from CSS
alone, without touching the markup: figure#pause becomes a small navy panel with
a yellow rule along the top, and the <img> becomes the arm of a uniselector.
A uniselector is the stepping switch inside a telephone exchange — an arm that
clicks round one contact at a time, hunting for a free line. It is the right
object for this game. The department's whole business is a machine that says
where things belong, and OPEN DAY is set inside one. Eleven contacts, because
eleven minutes is the interval this setting keeps returning to.
It STEPS rather than sweeps: steps(11) over 7.7s, so a contact every 700ms. A
continuous spin reads as a loading throbber; a discrete click reads as a machine
working through a list, which is what the department is doing while the table is
out of the room. The arm carries a faint trailing sector, because without it a
700ms step is easy to miss and the stepping is the whole idea.
No new text. Foundry's caption is localised and this only restyles it — a string
hardcoded in a stylesheet would be one the lang file could never reach.
Two things the browser had to be asked rather than assumed:
- Core pins #pause with BOTH top and bottom, so an auto height stretched it to
180px for a 16px caption. It is pinned here instead and sized to content.
- The arm is rotated, so getBoundingClientRect returns the diagonal of its own
square (75px for a 52px element). The layout takes its size from the flex
basis and never from the measured box.
prefers-reduced-motion stops the movement and keeps the object — a stepping dial
in the corner of the eye is exactly what that preference is asking about.
Version 1.0.4.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
02ec47a351 |
Fix every NPC sheet, which has never opened
npc-sheet.hbs line 43 read
{{#if (lookup ../skillsByCategory key).length}}
and Handlebars cannot read a property off a subexpression. That is a PARSE
error, so the template never compiled, so every NPC sheet in the system threw
on click — Natives & Threats, the starter's enemy roster, and the whole LAST
ADMISSION cast. #if already treats an empty array as falsy, so the .length was
never doing anything in the first place.
It was invisible to all four existing guards, and the reason is worth writing
down: check-rules reads .mjs, check-behaviour says in its own header that it
cannot reach sheets, and every play-through in this project has driven the
system through its API rather than by clicking. Nobody had ever opened an NPC
sheet, including me, in any of the sessions that "played through" scenarios
full of NPCs.
So: check-templates.mjs, a fifth guard, compiling every .hbs in about a tenth
of a second, wired into the build ahead of check-behaviour. It also names the
specific shape — a property read off a subexpression — because the raw parse
error says only where. Verified it bites by reintroducing the bug. Handlebars
is a devDependency now.
Opening the sheet then showed a second thing nobody had seen: the NPC skill
rows carry four children and were using the character sheet's seven-column
grid, so the name landed in a 13px column and the whole list overlapped
itself. The NPC row now has its own four-column template.
Verified by opening all six LAST ADMISSION cast sheets with an error hook
attached: six rendered, no errors, and Old Marrow reads HP 20/20, major wound
10, armour 2, Brawl 60%, Melee Weapon (Polearm) 60%.
Version 1.0.1.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
f185c92088 |
Add OPEN DAY, the recruitment prologue
Ten civilians, one decommissioned telephone exchange, and eleven minutes during
which the building went back to being on the street it was built on. Nothing
attacks anybody. Nothing wants anything. That is the horror, and it is the whole
design.
The premise was already in the pregens. Wren Finch's biography said she was
inside a building "on the night it stopped being on the street it was on", and
Soraya Rahimi's said she talked a caller down on air "for eleven minutes, and
the recording does not play back the same way twice". Those are two views of one
afternoon, so the scenario is built at the intersection and the other eight
"How they found her/him" lines are the same afternoon from eight more jobs.
Nothing in the prologue contradicts a sheet, because the sheets came first.
The reconciliation pass, which is most of the work:
- Finch's line said "night". The open day is an afternoon. One word changed on
the pregen, and both are true. Her line also says she was the only witness
who could describe the inside, so she is the one who goes into number 11 —
written into the location note rather than left to chance.
- Rahimi's line says she was ON AIR. So the disconnected telephone that rings
at 15:45 is patched through to her outside broadcast, which makes the canon
line and the scene the same event instead of two coincidences.
- Okonkwo carries two men out; Nkemdirim's patient has a forearm that healed
crooked over a break he took six minutes ago. Both seams needed an injury in
Act Two and there was none, so 15:50 and 15:51 now have one.
- Sandoval's forty minutes of traffic sit inside an eleven-minute window. That
is the anomaly, so it is on the handout in those words.
- "Prine" collided with an NPC already in the world. The engineer is Kearsley.
Structure: 2h15 against the house 3h30, stated in the design intent, because a
prologue exists to make the first real case land. No combat, no antagonist, no
Coherence — the last one is thematic, not a simplification: Coherence is what it
costs to keep looking once you know what you are looking at, and these ten do
not know yet. The Briefing is the first time the word is said aloud.
Ships as docs/scenarios/OPEN_DAY.md (the full GM document) and a Foundry
Adventure in a new `prologue` pack: 8 GM pages, 7 handouts, 5 NPCs, and ten
generated plates. The PCs are the "(before)" pregens already in the Pregens
pack — a second copy here would give a GM two sets of the same ten people and
no way to know which one their notes were on.
Verified in Foundry: the adventure imports, both journals render branded, the
countdown table reads as a table, and all ten plates load.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
4101221eb1 |
Fill the empty space in the actor sheet header
The header was one flex row — identity, then vitals pinned to the top right. The identity column is tall (name, rank, marksmanship, the two style hints, two age fields) and the vitals row is short, so everything under the vitals was empty blue: roughly a third of the header. Split it into two columns and stack the status column: vitals, then the derived numbers, then the readied weapons. Both of those already existed as full-width bands directly below, so nothing new is on screen — it is the same content arranged to use the space, and it puts damage modifier and readied weapons next to hit points, which is where they are read during a fight. Two sizing traps, both found by measuring rather than looking: The status column is an auto track, so its width came from max-content — the readied-weapons line, whose length is whatever the weapon names happen to be. That crushed identity into a 190px gutter with the name truncated to "Marta Hollow". width:0 + min-width:100% keeps those panels out of the measurement so the column is sized by the vitals and they stretch to match. The vitals row is wide enough on its own to do the same thing, so the columns are proportional (1fr / 1.6fr) and the vitals now wrap. Measured at 1020px: header 367px tall before, 266px after, columns 241 and 235 so they end level. At 1400px: 214px tall, columns 189 and 158, vitals unwrapped. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
c15b13c3b9 |
Add the duty roster: ten ready-to-play agents with generated portraits
Five postings and five trades, so a group choosing from this sheet meets both halves of character creation without anyone having to explain them. Skills and kit are not written out per agent. They are expanded from the register at build time by the same helper the starter scenario uses, which is hoisted out of the BLACK PLATFORM block for the purpose — two copies of that expansion is two places for the induction rule to drift. Ratings sit at the middle of each band rather than being rolled, because a pregen that varies between installs is not a pregen. Portraits are generated art, resized to 512 and stored as webp (12MB of PNG became 210KB). buildActor now takes a portrait and gives the same face to the prototype token: a pregen you can recognise on the map is worth more at a convention table than a correct one you cannot tell apart. It throws on a portrait that is not on disk, because a broken image on a pregen sheet is the sort of thing that is first noticed on a projector. Guard: check-behaviour gains a test that every roster agent names a real posting or trade and has its portrait on disk, verified to bite. It caught "archive", which is not a posting — Bhattacharya is Registry. Verified in a running Foundry: 14 pregens in the pack, sheets render with the portraits, coherence and hit points fill from the template, no system errors. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
7f8355f0d0 |
Add the player primer and cheat sheet, and brand every journal
Ten pages a player reads before session one, in their own compendium at OBSERVER ownership: nine of setting, one cheat sheet. The draft called the organisation "the Bureau"; everything else in the game calls it the department or the agency, so that is what it is called. Thin areas filled rather than padded: the seven character principles as a list, the real Coherence band table instead of a paragraph describing one, and "what continuity is not" pulled out of the middle of a page because it is the most important sentence for a new player. The cheat sheet's numbers are computed from rules.mjs and lang/en.json at build time. A hand-typed one would have been the sixth time this project shipped a number that disagreed with the code, and the one the players were holding. R-250: the design tokens were never on :root. Journals rendered as default parchment, and four things were wrong in sequence — v14's hook is renderJournalEntrySheet; its `element` is the node, so `[0]` returns the first CHILD and put the class on a header button; there is no .journal-page-content in v14; and then the real one: with the selector matching, font-size, line-height and padding all applied while every colour did not, because the palette is declared on .ringbrp-sheet/.ringbrp-chat/.ringbrp-window and never on :root. A journal inherited no tokens, so every var() silently became nothing. A rule that HALF applies is not a specificity problem — I spent three attempts treating it as one, including a version bump on a cache theory that was wrong. Branded: primer, rulebook, starter GM text and handouts. Scoped by flag, so other modules' journals are untouched. Version 0.9.0. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
c61700566c |
Shields: armour you raise, and the only armour you can defend with
A shield is the one piece of armour you hold, and it protects you exactly as much as you are currently holding it up. Raised, its points apply to what it covers and it can parry on a new Shield skill. Lowered, it is weight. The price of keeping it up is that it occupies a hand and you are shooting around it: -20% to your own ranged attacks, printed on the card like everything else. Only one shield goes up at a time. Seven of them, 2-point buckler to 6-point ballistic shield; the pavise and barrier plate cover legs too because they are walls you carry. `degradation` finally does something — it has been on every armour item since the packs were first built and was read by nothing. A blow costs the shield a point of wear for every step it stood above an ordinary success, so an ordinary hit costs nothing, a special 1 and a critical 2. At its limit it comes apart, drops, and will not be raised again. Two defects found by driving the real functions rather than reading the diff, both introduced by this change: - Every shield defence quietly rolled DODGE. "shield" passed through defenceTypeAllowed unchanged, so neither parry branch matched and it fell through. Invisible in the card; it showed up as eight rolls at a supposed 85% returning six failures. Taking a blow on the shield IS a parry, so it is now a flag on a parry rather than a defence type of its own. - The wear rule was unreachable: it fired only on a clean stop, but under graded defences only a critical fully stops a critical, so a shield would never have taken a scratch. Wear is for what the shield ABSORBED — dragging a critical down to a special is exactly when shields splinter. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
6ed9ded362 |
Armour covers what it covers, and Coherence moves to the vitals
Asking for the type of armour per location turned out not to be a display request. Every equipped piece added its points to EVERY location, so a Brodie helmet protected your shins and a cuirass protected your skull — with a catalogue containing sallets, lobster-tail helmets and three cuirasses, that could not be printed without the sheet saying something visibly false. Armour now declares coverage as a named set rather than a list of locations, because a piece has to fit whatever body wears it: all, torso, torsoArms, torsoLimbs, head. The sets expand to location KINDS, so a cuirass covers a baseline torso and a vesh trunk and ridge without either knowing the other exists. Unstated coverage is `all`, so anything that does not declare behaves as everything did before. Eighteen of the twenty-five catalogue pieces are not whole-body and now say so. The trap was double counting: hitLocationRoll subtracted the global worn total PLUS the body's own armour at that location, which was right while armour was global and becomes a double count the moment a location knows its own pieces. It reads the location's total and nothing else, verified by firing fourteen hits of 10 damage across every location and asserting damage applied == 10 - that location's armour. A single ARMOUR number no longer exists, so the header shows the torso with "0-8 by location" beside it when the rest of you is not that. This is a real difficulty change: limbs are frequently bare and limbs are 12 of 20 on the melee table. Coherence moves out of the derived strip into the vitals at hit-point size, with its band under it and the box reddening as it falls. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
3421ed4a2e |
Reorder the combat tab, and make weapons readyable
There was no way to ready a weapon. The strip under the characteristics reads `equipped`, and the only toggle-equipped control on the sheet was on the ARMOUR list — a weapon could only be readied by opening its item sheet and finding the tickbox there. The weapons table now carries the same toggle, the row is tinted when readied, and a line above the table says what the control does. The tab runs in the order it is used: weapons, armour, defend, reaction, body last. Weapons lead with 40px art and a display-face name, having previously sat below two screens of anatomy and reaction arithmetic. The figure is a partial, drawn in three places — header thumbnail, combat tab, anatomy tab. Three inline copies would have been the template-layer version of the defect check-rules exists to prevent: a location present in one drawing and missing from another. The header carries a 34px figure and every location with its damage and the armour over it, so "how much armour is on the part that just got hit" is answered on the main page without changing tab. Also fixed: `.ringbrp-sheet a` outranks a bare `.rb-equip`, so an unequipped toggle was painted white and read as lit — true of the armour toggle too, since it was added. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
2e10ecced9 |
Fire modes, fitted sights and lamps, and a tab rail
Selectable fire, with the modifiers on the choice Every weapon has carried a fireMode since the packs were first built and nothing read it: a musket could rip a ten-round burst and "rapid" bought a weapon nothing. FIRE_MODE now gates the burst rules and carries its own attack modifier — slow +10% and then you work for the next one, fast a second shot at -20% to both, rapid bursts, beam +5% and it can be held on the target for damage instead of hits. The gate lives in burstAttack too, so no macro can route round it. `fast` was on five catalogue weapons and was not in the system's list of modes at all, so re-saving one of them silently blanked it; the list is derived from the rule now. burstAttack shipped complete and was reachable from nothing. It is the resolution path the selector chooses, and takes the dialog's whole stack rather than charging the player twice for the same burst. Sights and lamps that apply themselves A sight is graded by range band, so a telescopic sight is a -10% liability inside ten metres and worth +20% at extreme, and it only counts if it fits. The band is already known when the modifier is wanted, so nothing is selected and nothing is forgotten. The dark costs something now; a carried lamp buys it back to dim, never to daylight, and the card says it shows them exactly where you are. A migration fits gear that predates the fields, matched on the stamped key rather than by name. The damage card The last card still wearing Foundry's stock dice styling — it posted a bare Roll and let the core template dress it. It renders through the module's own card now, with the Roll still on the message so dice stay inspectable. A rail instead of a strip Eight tabs across an 820px sheet compressed to 11px type. They run down the side now, one line each, with counts as badges; the sheet is 152px wider to pay for it. Also fixed: any weapon with a Coherence cost could not be attacked with AT ALL — the handler re-read the weapon after an await, when currentTarget is already null, and the attack died on a TypeError with nothing shown. And the duplicate-rule guard failed an honest table because FIRE_MODE and speedBands share two key names; it wants a majority now, and prints partial overlaps rather than swallowing them. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
89ce557355 |
Death at zero, and a case brief that answers the useful questions
R-223: nothing happened at 0 hit points. An agent was marked down and lay there forever; the only mention of death in the system was flavour text on a destroyed head. Zero now starts a clock whose length is the agent's own Major Wound threshold, ticked from the same hook that charges blood loss. First Aid or Medicine stabilises and stops the count; healing above zero clears it; heal() refuses the dead. On death, Coherence decides what the record says — from a proper death in service down to not being recorded as dead at all. Case generator: four phases instead of five (the arc drew two interchangeable middles that read as padding). The brief now leads with Where / When / Classification / Phases, states the opposition in full, and has a "How you get back" section. place and duration were generated and never printed. The same facts are on the case file itself. 300 seeds checked for object leaks; one found and fixed (group taboo/desire are objects with .text). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
1734612c06 |
v0.7.0 — vehicles, Borrowed Authority, and a door for the case generator
Since 0.6.1: - VEHICLES as their own Item type: crew, seats, cargo, hull, armour, range, terrain and the skill that works them, with speed as a band. 14 of them, from the pool car and the Custodian van to a sealed crawler and the carriage that is already parked outside wherever you were going to go. - STORES roughly doubled to 118 gear items across four eras — standing issue and paperwork, transport stores, and the forward stores Registry will not hand a probationary officer. Catalogue now 175 items, 231 icons. - Agents leave creation properly equipped: standing issue, the posting's kit, what seniority adds, and one personal effect that is theirs and not the department's. - All 25 postings now describe themselves in the creation dialog. - BORROWED AUTHORITY, the access subsystem. No hacking skill and nothing to break into: access is a named credential with a scope, a Trust that erodes, and red flags describing how its real owner behaves. Inside scope there is no roll. Two tiers beyond is impossible at any skill. Coherence widens a record, and pushing one past what its Trust will carry merges your file with theirs. - Defences graded by level, and damage now rolls at the level a blow landed at. - Dodge's DEX x 2 base is finally evaluated; it read 0% for most postings. - Brawl is a fallback rather than a peer of the weapon in hand. - check-lang guard: one key that was both a value and a branch silently un-localised the entire system. - The case generator is reachable from the Actor Directory, and no longer puts every character in the world on a new case. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
d0144f744e |
The Custodians v0.6.1 — full system
A Foundry VTT game system: Basic Roleplaying d100 in a drab British agency that deals with crossings, where reality keeps a record of what belongs where and can be made to get it wrong. Contents: - rules.mjs as the single authority for every rule formula, with 40 rules and 132 spot-checked values. Nothing may restate a formula it defines. - 25 postings, each with a key characteristic, trained skills, talents and its own kit; a 175-item catalogue across four eras; 231 generated icons. - Reaction Lanes initiative, re-rolled every round including the first. - Coherence, and the Quiet Arts, which only ever arrive from a crossing gone wrong. - Graded defences: only a critical stops a critical. - Borrowed Authority — access as a record rather than a hacking roll. - A case generator, and a rules journal generated from rules.mjs so the book cannot drift from the code. Built compendia are committed so a remote Foundry needs only the files. node_modules is not; `npm install` restores it for rebuilding content. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |