diff --git a/README.md b/README.md index 96e5ea1..2aef32c 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,8 @@ changing underneath a running server. | **Bestiary** | 21 statblocks across folklore, horror and the far side | | **Eras** | modern, antique, future, anomalous — the agency is old and never abolishes anything | | **Icons** | 292, all generated from `tools/icons.mjs`; no external art | -| **Compendia** | pregens, NPCs, skills, weapons, armour, gear, vehicles, powers, tables, rules, a starter case | +| **Starter scenario** | **PAPER WINDOW**, a six-player introductory case, importable as a single Adventure | +| **Compendia** | pregens, NPCs, skills, weapons, armour, gear, vehicles, powers, tables, rules, the starter | | **Rules journal** | 12 pages, generated from `rules.mjs` so the book cannot drift from the code | @@ -179,7 +180,7 @@ The build runs all three guards before it writes anything: ``` -check-rules: OK — 87 rules, 15 files scanned, 3 aliases + 41 constant sets checked, 275 formulas verified +check-rules: OK — 87 rules, 16 files scanned, 3 aliases + 41 constant sets checked, 275 formulas verified check-kits: OK — 25 roles, 10 trades, 218 catalogue items, every kit key resolves, every posting can use what it carries, every loadout distinct check-lang: OK — en.json, 926 keys, no leaf/branch collisions check-behaviour: OK — 28 behavioural tests diff --git a/docs/REVIEW_LOG.md b/docs/REVIEW_LOG.md index 951dec3..bfd2793 100644 --- a/docs/REVIEW_LOG.md +++ b/docs/REVIEW_LOG.md @@ -4901,3 +4901,43 @@ should sit *above* executable evidence rather than instead of it. Every numbered that can be reproduced without a browser now has a test. Those that cannot — anything inside the generators, the sheets or the contact engine — are named in the test file's header as out of its reach, so the suite does not look more complete than it is. + +## PAPER WINDOW — the starter scenario, as a Foundry Adventure + +Codex's six-player introductory case is now in the system as an importable scenario +module. **Adventure**, not a journal: a scenario that arrives as nine loose documents a +GM has to assemble is a scenario that gets read rather than run. + +`ringbrp.starter` contains one Adventure holding: + +* the **GM scenario** in twelve pages, in running order; +* the **seven player handouts** as their own journal at ownership OBSERVER, so they can + be revealed one at a time without the GM changing permissions on seven pages before + the session — the GM journal stays hidden; +* the **six pregens**, built through the same `buildActor` path the shipped pregens + use, so they arrive with full skill lists, kit, hit points and linked tokens; +* the **cast** — Evelyn Vale, both Lorna Vales, Gareth Moss and the Cancellation Man — + as statblocks a GM can drag onto a scene; +* the **Borrowed Authority** as a real credential item with scope, Trust 2 and its three + red flags, rather than a paragraph in the brief; +* the **case file** already at Containment 8, Resources 8, Clearance 6 with its four + phases loaded at the scenario's stated difficulties and modifiers. + +**The pregens are fixed, not rolled.** Ratings sit at the middle of each band rather +than being generated, because a pregen that varies between installs is not a pregen and +six tables running the starter should see the same six agents. Verified after a live +import: Nia carries the team's highest Tradecraft at 53 as the text requires, Callum's +riot shield is **slung** so raising it is still the player's decision, and the three +probationers show the trade/induction split the scenario is built to teach — Elin +Medicine 58 against an induction of 26, Owen Electronics 58, Sennen Sleight of Hand 58. + +The expansion from a posting or trade into a full sheet reuses the register and the +induction rule rather than restating either, so the pregens cannot drift from the +generator: change `TRADE_BANDS` and the starter's doctor changes with it. + +**Verified by importing it rather than by building it.** The pack builds, the Adventure +is well formed, the import completes with no errors, the case roster resolves through +stable keys after import (raw ids do not survive one), the handouts come in +player-visible and the GM text does not, and **phase one runs straight out of the box** — +Nia leading the cordon on Tradecraft 53. Then everything imported was deleted again, +which the pack does not notice. diff --git a/packs/armour/000003.log b/packs/armour/000003.log deleted file mode 100644 index 9b6d27d..0000000 Binary files a/packs/armour/000003.log and /dev/null differ diff --git a/packs/armour/000005.ldb b/packs/armour/000005.ldb new file mode 100644 index 0000000..c982555 Binary files /dev/null and b/packs/armour/000005.ldb differ diff --git a/packs/armour/000006.log b/packs/armour/000006.log new file mode 100644 index 0000000..1a1bb27 Binary files /dev/null and b/packs/armour/000006.log differ diff --git a/packs/armour/CURRENT b/packs/armour/CURRENT index 1a84852..cacca75 100644 --- a/packs/armour/CURRENT +++ b/packs/armour/CURRENT @@ -1 +1 @@ -MANIFEST-000002 +MANIFEST-000004 diff --git a/packs/armour/LOG b/packs/armour/LOG index da64381..ff37f12 100644 --- a/packs/armour/LOG +++ b/packs/armour/LOG @@ -1 +1,5 @@ -2026/08/11-22:56:53.172229 172e3b000 Delete type=3 #1 +2026/08/11-23:23:08.314417 1742cf000 Recovering log #3 +2026/08/11-23:23:08.314570 1742cf000 Level-0 table #5: started +2026/08/11-23:23:08.314789 1742cf000 Level-0 table #5: 4591 bytes OK +2026/08/11-23:23:08.315089 1742cf000 Delete type=0 #3 +2026/08/11-23:23:08.315117 1742cf000 Delete type=3 #2 diff --git a/packs/armour/LOG.old b/packs/armour/LOG.old new file mode 100644 index 0000000..507c1cb --- /dev/null +++ b/packs/armour/LOG.old @@ -0,0 +1 @@ +2026/08/11-23:22:30.827615 17117f000 Delete type=3 #1 diff --git a/packs/armour/MANIFEST-000002 b/packs/armour/MANIFEST-000002 deleted file mode 100644 index bbbc585..0000000 Binary files a/packs/armour/MANIFEST-000002 and /dev/null differ diff --git a/packs/armour/MANIFEST-000004 b/packs/armour/MANIFEST-000004 new file mode 100644 index 0000000..9c05997 Binary files /dev/null and b/packs/armour/MANIFEST-000004 differ diff --git a/packs/expedition/000003.log b/packs/expedition/000003.log deleted file mode 100644 index 263295b..0000000 Binary files a/packs/expedition/000003.log and /dev/null differ diff --git a/packs/expedition/000005.ldb b/packs/expedition/000005.ldb new file mode 100644 index 0000000..4ff460d Binary files /dev/null and b/packs/expedition/000005.ldb differ diff --git a/packs/expedition/000006.log b/packs/expedition/000006.log new file mode 100644 index 0000000..268b187 Binary files /dev/null and b/packs/expedition/000006.log differ diff --git a/packs/expedition/CURRENT b/packs/expedition/CURRENT index 1a84852..cacca75 100644 --- a/packs/expedition/CURRENT +++ b/packs/expedition/CURRENT @@ -1 +1 @@ -MANIFEST-000002 +MANIFEST-000004 diff --git a/packs/expedition/LOG b/packs/expedition/LOG index 32e7b2f..c83cac5 100644 --- a/packs/expedition/LOG +++ b/packs/expedition/LOG @@ -1 +1,5 @@ -2026/08/11-22:56:53.204356 173647000 Delete type=3 #1 +2026/08/11-23:23:08.332681 174adb000 Recovering log #3 +2026/08/11-23:23:08.332779 174adb000 Level-0 table #5: started +2026/08/11-23:23:08.332918 174adb000 Level-0 table #5: 4129 bytes OK +2026/08/11-23:23:08.333190 174adb000 Delete type=0 #3 +2026/08/11-23:23:08.333236 174adb000 Delete type=3 #2 diff --git a/packs/expedition/LOG.old b/packs/expedition/LOG.old new file mode 100644 index 0000000..a6bf6d4 --- /dev/null +++ b/packs/expedition/LOG.old @@ -0,0 +1 @@ +2026/08/11-23:22:30.870148 17198b000 Delete type=3 #1 diff --git a/packs/expedition/MANIFEST-000002 b/packs/expedition/MANIFEST-000002 deleted file mode 100644 index bbbc585..0000000 Binary files a/packs/expedition/MANIFEST-000002 and /dev/null differ diff --git a/packs/expedition/MANIFEST-000004 b/packs/expedition/MANIFEST-000004 new file mode 100644 index 0000000..47fbcd4 Binary files /dev/null and b/packs/expedition/MANIFEST-000004 differ diff --git a/packs/gear/000003.log b/packs/gear/000003.log deleted file mode 100644 index 629ba6d..0000000 Binary files a/packs/gear/000003.log and /dev/null differ diff --git a/packs/gear/000005.ldb b/packs/gear/000005.ldb new file mode 100644 index 0000000..d9bac03 Binary files /dev/null and b/packs/gear/000005.ldb differ diff --git a/packs/gear/000006.log b/packs/gear/000006.log new file mode 100644 index 0000000..d1deb9b Binary files /dev/null and b/packs/gear/000006.log differ diff --git a/packs/gear/CURRENT b/packs/gear/CURRENT index 1a84852..cacca75 100644 --- a/packs/gear/CURRENT +++ b/packs/gear/CURRENT @@ -1 +1 @@ -MANIFEST-000002 +MANIFEST-000004 diff --git a/packs/gear/LOG b/packs/gear/LOG index a4c7f1f..e3e88c3 100644 --- a/packs/gear/LOG +++ b/packs/gear/LOG @@ -1 +1,5 @@ -2026/08/11-22:56:53.174127 172e3b000 Delete type=3 #1 +2026/08/11-23:23:08.316151 174adb000 Recovering log #3 +2026/08/11-23:23:08.316320 174adb000 Level-0 table #5: started +2026/08/11-23:23:08.316628 174adb000 Level-0 table #5: 31614 bytes OK +2026/08/11-23:23:08.316965 174adb000 Delete type=0 #3 +2026/08/11-23:23:08.317018 174adb000 Delete type=3 #2 diff --git a/packs/gear/LOG.old b/packs/gear/LOG.old new file mode 100644 index 0000000..61717b2 --- /dev/null +++ b/packs/gear/LOG.old @@ -0,0 +1 @@ +2026/08/11-23:22:30.829491 170973000 Delete type=3 #1 diff --git a/packs/gear/MANIFEST-000002 b/packs/gear/MANIFEST-000002 deleted file mode 100644 index bbbc585..0000000 Binary files a/packs/gear/MANIFEST-000002 and /dev/null differ diff --git a/packs/gear/MANIFEST-000004 b/packs/gear/MANIFEST-000004 new file mode 100644 index 0000000..f7cd73c Binary files /dev/null and b/packs/gear/MANIFEST-000004 differ diff --git a/packs/npcs/000003.log b/packs/npcs/000003.log deleted file mode 100644 index 09b2bf6..0000000 Binary files a/packs/npcs/000003.log and /dev/null differ diff --git a/packs/npcs/000005.ldb b/packs/npcs/000005.ldb new file mode 100644 index 0000000..29f69de Binary files /dev/null and b/packs/npcs/000005.ldb differ diff --git a/packs/npcs/000006.log b/packs/npcs/000006.log new file mode 100644 index 0000000..ac2ef10 Binary files /dev/null and b/packs/npcs/000006.log differ diff --git a/packs/npcs/CURRENT b/packs/npcs/CURRENT index 1a84852..cacca75 100644 --- a/packs/npcs/CURRENT +++ b/packs/npcs/CURRENT @@ -1 +1 @@ -MANIFEST-000002 +MANIFEST-000004 diff --git a/packs/npcs/LOG b/packs/npcs/LOG index f2ab953..aefc8a1 100644 --- a/packs/npcs/LOG +++ b/packs/npcs/LOG @@ -1 +1,5 @@ -2026/08/11-22:56:53.190267 173e53000 Delete type=3 #1 +2026/08/11-23:23:08.298312 174adb000 Recovering log #3 +2026/08/11-23:23:08.300367 174adb000 Level-0 table #5: started +2026/08/11-23:23:08.301985 174adb000 Level-0 table #5: 225945 bytes OK +2026/08/11-23:23:08.302352 174adb000 Delete type=0 #3 +2026/08/11-23:23:08.302416 174adb000 Delete type=3 #2 diff --git a/packs/npcs/LOG.old b/packs/npcs/LOG.old new file mode 100644 index 0000000..78abf31 --- /dev/null +++ b/packs/npcs/LOG.old @@ -0,0 +1 @@ +2026/08/11-23:22:30.845061 17198b000 Delete type=3 #1 diff --git a/packs/npcs/MANIFEST-000002 b/packs/npcs/MANIFEST-000002 deleted file mode 100644 index bbbc585..0000000 Binary files a/packs/npcs/MANIFEST-000002 and /dev/null differ diff --git a/packs/npcs/MANIFEST-000004 b/packs/npcs/MANIFEST-000004 new file mode 100644 index 0000000..116e469 Binary files /dev/null and b/packs/npcs/MANIFEST-000004 differ diff --git a/packs/powers/000003.log b/packs/powers/000003.log deleted file mode 100644 index d723043..0000000 Binary files a/packs/powers/000003.log and /dev/null differ diff --git a/packs/powers/000005.ldb b/packs/powers/000005.ldb new file mode 100644 index 0000000..7f540c6 Binary files /dev/null and b/packs/powers/000005.ldb differ diff --git a/packs/powers/000006.log b/packs/powers/000006.log new file mode 100644 index 0000000..43e306f Binary files /dev/null and b/packs/powers/000006.log differ diff --git a/packs/powers/CURRENT b/packs/powers/CURRENT index 1a84852..cacca75 100644 --- a/packs/powers/CURRENT +++ b/packs/powers/CURRENT @@ -1 +1 @@ -MANIFEST-000002 +MANIFEST-000004 diff --git a/packs/powers/LOG b/packs/powers/LOG index ee5c32b..0a51aaa 100644 --- a/packs/powers/LOG +++ b/packs/powers/LOG @@ -1 +1,5 @@ -2026/08/11-22:56:53.198465 172e3b000 Delete type=3 #1 +2026/08/11-23:23:08.309911 1742cf000 Recovering log #3 +2026/08/11-23:23:08.310322 1742cf000 Level-0 table #5: started +2026/08/11-23:23:08.310473 1742cf000 Level-0 table #5: 3295 bytes OK +2026/08/11-23:23:08.310750 1742cf000 Delete type=0 #3 +2026/08/11-23:23:08.310794 1742cf000 Delete type=3 #2 diff --git a/packs/powers/LOG.old b/packs/powers/LOG.old new file mode 100644 index 0000000..0099045 --- /dev/null +++ b/packs/powers/LOG.old @@ -0,0 +1 @@ +2026/08/11-23:22:30.862697 17198b000 Delete type=3 #1 diff --git a/packs/powers/MANIFEST-000002 b/packs/powers/MANIFEST-000002 deleted file mode 100644 index bbbc585..0000000 Binary files a/packs/powers/MANIFEST-000002 and /dev/null differ diff --git a/packs/powers/MANIFEST-000004 b/packs/powers/MANIFEST-000004 new file mode 100644 index 0000000..20de082 Binary files /dev/null and b/packs/powers/MANIFEST-000004 differ diff --git a/packs/pregens/000003.log b/packs/pregens/000003.log deleted file mode 100644 index 712264f..0000000 Binary files a/packs/pregens/000003.log and /dev/null differ diff --git a/packs/pregens/000005.ldb b/packs/pregens/000005.ldb new file mode 100644 index 0000000..ef0a53e Binary files /dev/null and b/packs/pregens/000005.ldb differ diff --git a/packs/pregens/000006.log b/packs/pregens/000006.log new file mode 100644 index 0000000..ddbae86 Binary files /dev/null and b/packs/pregens/000006.log differ diff --git a/packs/pregens/CURRENT b/packs/pregens/CURRENT index 1a84852..cacca75 100644 --- a/packs/pregens/CURRENT +++ b/packs/pregens/CURRENT @@ -1 +1 @@ -MANIFEST-000002 +MANIFEST-000004 diff --git a/packs/pregens/LOG b/packs/pregens/LOG index 4910f8d..83ac41a 100644 --- a/packs/pregens/LOG +++ b/packs/pregens/LOG @@ -1 +1,5 @@ -2026/08/11-22:56:53.180591 173647000 Delete type=3 #1 +2026/08/11-23:23:08.294604 174adb000 Recovering log #3 +2026/08/11-23:23:08.295663 174adb000 Level-0 table #5: started +2026/08/11-23:23:08.296163 174adb000 Level-0 table #5: 58792 bytes OK +2026/08/11-23:23:08.296520 174adb000 Delete type=0 #3 +2026/08/11-23:23:08.296610 174adb000 Delete type=3 #2 diff --git a/packs/pregens/LOG.old b/packs/pregens/LOG.old new file mode 100644 index 0000000..46f8755 --- /dev/null +++ b/packs/pregens/LOG.old @@ -0,0 +1 @@ +2026/08/11-23:22:30.835678 17198b000 Delete type=3 #1 diff --git a/packs/pregens/MANIFEST-000002 b/packs/pregens/MANIFEST-000002 deleted file mode 100644 index bbbc585..0000000 Binary files a/packs/pregens/MANIFEST-000002 and /dev/null differ diff --git a/packs/pregens/MANIFEST-000004 b/packs/pregens/MANIFEST-000004 new file mode 100644 index 0000000..fb2adca Binary files /dev/null and b/packs/pregens/MANIFEST-000004 differ diff --git a/packs/rules/000003.log b/packs/rules/000003.log deleted file mode 100644 index 7bc4ddf..0000000 Binary files a/packs/rules/000003.log and /dev/null differ diff --git a/packs/rules/000005.ldb b/packs/rules/000005.ldb new file mode 100644 index 0000000..aff8ef7 Binary files /dev/null and b/packs/rules/000005.ldb differ diff --git a/packs/rules/000006.log b/packs/rules/000006.log new file mode 100644 index 0000000..f3c7033 Binary files /dev/null and b/packs/rules/000006.log differ diff --git a/packs/rules/CURRENT b/packs/rules/CURRENT index 1a84852..cacca75 100644 --- a/packs/rules/CURRENT +++ b/packs/rules/CURRENT @@ -1 +1 @@ -MANIFEST-000002 +MANIFEST-000004 diff --git a/packs/rules/LOG b/packs/rules/LOG index 516c822..f872108 100644 --- a/packs/rules/LOG +++ b/packs/rules/LOG @@ -1 +1,5 @@ -2026/08/11-22:56:53.202660 17465f000 Delete type=3 #1 +2026/08/11-23:23:08.320015 175af3000 Recovering log #3 +2026/08/11-23:23:08.320173 175af3000 Level-0 table #5: started +2026/08/11-23:23:08.320528 175af3000 Level-0 table #5: 33474 bytes OK +2026/08/11-23:23:08.320923 175af3000 Delete type=0 #3 +2026/08/11-23:23:08.320978 175af3000 Delete type=3 #2 diff --git a/packs/rules/LOG.old b/packs/rules/LOG.old new file mode 100644 index 0000000..06c8aac --- /dev/null +++ b/packs/rules/LOG.old @@ -0,0 +1 @@ +2026/08/11-23:22:30.868156 17117f000 Delete type=3 #1 diff --git a/packs/rules/MANIFEST-000002 b/packs/rules/MANIFEST-000002 deleted file mode 100644 index bbbc585..0000000 Binary files a/packs/rules/MANIFEST-000002 and /dev/null differ diff --git a/packs/rules/MANIFEST-000004 b/packs/rules/MANIFEST-000004 new file mode 100644 index 0000000..a877353 Binary files /dev/null and b/packs/rules/MANIFEST-000004 differ diff --git a/packs/skills/000003.log b/packs/skills/000003.log deleted file mode 100644 index 2a55440..0000000 Binary files a/packs/skills/000003.log and /dev/null differ diff --git a/packs/skills/000005.ldb b/packs/skills/000005.ldb new file mode 100644 index 0000000..eb948ea Binary files /dev/null and b/packs/skills/000005.ldb differ diff --git a/packs/skills/000006.log b/packs/skills/000006.log new file mode 100644 index 0000000..1989ee1 Binary files /dev/null and b/packs/skills/000006.log differ diff --git a/packs/skills/CURRENT b/packs/skills/CURRENT index 1a84852..cacca75 100644 --- a/packs/skills/CURRENT +++ b/packs/skills/CURRENT @@ -1 +1 @@ -MANIFEST-000002 +MANIFEST-000004 diff --git a/packs/skills/LOG b/packs/skills/LOG index e1f4f28..7f8b75b 100644 --- a/packs/skills/LOG +++ b/packs/skills/LOG @@ -1 +1,5 @@ -2026/08/11-22:56:53.165752 173647000 Delete type=3 #1 +2026/08/11-23:23:08.305494 1742cf000 Recovering log #3 +2026/08/11-23:23:08.305911 1742cf000 Level-0 table #5: started +2026/08/11-23:23:08.306128 1742cf000 Level-0 table #5: 7590 bytes OK +2026/08/11-23:23:08.306405 1742cf000 Delete type=0 #3 +2026/08/11-23:23:08.306467 1742cf000 Delete type=3 #2 diff --git a/packs/skills/LOG.old b/packs/skills/LOG.old new file mode 100644 index 0000000..1d811f3 --- /dev/null +++ b/packs/skills/LOG.old @@ -0,0 +1 @@ +2026/08/11-23:22:30.820649 172197000 Delete type=3 #1 diff --git a/packs/skills/MANIFEST-000002 b/packs/skills/MANIFEST-000002 deleted file mode 100644 index bbbc585..0000000 Binary files a/packs/skills/MANIFEST-000002 and /dev/null differ diff --git a/packs/skills/MANIFEST-000004 b/packs/skills/MANIFEST-000004 new file mode 100644 index 0000000..90efb97 Binary files /dev/null and b/packs/skills/MANIFEST-000004 differ diff --git a/packs/slice/000003.log b/packs/slice/000003.log deleted file mode 100644 index 4a86fe9..0000000 Binary files a/packs/slice/000003.log and /dev/null differ diff --git a/packs/slice/000005.ldb b/packs/slice/000005.ldb new file mode 100644 index 0000000..4851c77 Binary files /dev/null and b/packs/slice/000005.ldb differ diff --git a/packs/slice/000006.log b/packs/slice/000006.log new file mode 100644 index 0000000..c7d9b16 Binary files /dev/null and b/packs/slice/000006.log differ diff --git a/packs/slice/CURRENT b/packs/slice/CURRENT index 1a84852..cacca75 100644 --- a/packs/slice/CURRENT +++ b/packs/slice/CURRENT @@ -1 +1 @@ -MANIFEST-000002 +MANIFEST-000004 diff --git a/packs/slice/LOG b/packs/slice/LOG index b02a7ad..fc3115d 100644 --- a/packs/slice/LOG +++ b/packs/slice/LOG @@ -1 +1,5 @@ -2026/08/11-22:56:53.196772 17465f000 Delete type=3 #1 +2026/08/11-23:23:08.328983 1752e7000 Recovering log #3 +2026/08/11-23:23:08.329262 1752e7000 Level-0 table #5: started +2026/08/11-23:23:08.329521 1752e7000 Level-0 table #5: 16892 bytes OK +2026/08/11-23:23:08.329805 1752e7000 Delete type=0 #3 +2026/08/11-23:23:08.329838 1752e7000 Delete type=3 #2 diff --git a/packs/slice/LOG.old b/packs/slice/LOG.old new file mode 100644 index 0000000..82360c8 --- /dev/null +++ b/packs/slice/LOG.old @@ -0,0 +1 @@ +2026/08/11-23:22:30.860796 17117f000 Delete type=3 #1 diff --git a/packs/slice/MANIFEST-000002 b/packs/slice/MANIFEST-000002 deleted file mode 100644 index bbbc585..0000000 Binary files a/packs/slice/MANIFEST-000002 and /dev/null differ diff --git a/packs/slice/MANIFEST-000004 b/packs/slice/MANIFEST-000004 new file mode 100644 index 0000000..0552c3a Binary files /dev/null and b/packs/slice/MANIFEST-000004 differ diff --git a/packs/starter/000005.ldb b/packs/starter/000005.ldb new file mode 100644 index 0000000..f7db5b4 Binary files /dev/null and b/packs/starter/000005.ldb differ diff --git a/packs/starter/000006.log b/packs/starter/000006.log new file mode 100644 index 0000000..68669cb Binary files /dev/null and b/packs/starter/000006.log differ diff --git a/packs/starter/CURRENT b/packs/starter/CURRENT new file mode 100644 index 0000000..cacca75 --- /dev/null +++ b/packs/starter/CURRENT @@ -0,0 +1 @@ +MANIFEST-000004 diff --git a/packs/starter/LOG b/packs/starter/LOG new file mode 100644 index 0000000..d947226 --- /dev/null +++ b/packs/starter/LOG @@ -0,0 +1,5 @@ +2026/08/11-23:23:08.324024 1752e7000 Recovering log #3 +2026/08/11-23:23:08.325074 1752e7000 Level-0 table #5: started +2026/08/11-23:23:08.325775 1752e7000 Level-0 table #5: 89206 bytes OK +2026/08/11-23:23:08.326188 1752e7000 Delete type=0 #3 +2026/08/11-23:23:08.326248 1752e7000 Delete type=3 #2 diff --git a/packs/starter/LOG.old b/packs/starter/LOG.old new file mode 100644 index 0000000..48f3c56 --- /dev/null +++ b/packs/starter/LOG.old @@ -0,0 +1 @@ +2026/08/11-23:22:30.856844 17117f000 Delete type=3 #1 diff --git a/packs/starter/MANIFEST-000004 b/packs/starter/MANIFEST-000004 new file mode 100644 index 0000000..78b3fb2 Binary files /dev/null and b/packs/starter/MANIFEST-000004 differ diff --git a/packs/tables/000003.log b/packs/tables/000003.log deleted file mode 100644 index f125b7d..0000000 Binary files a/packs/tables/000003.log and /dev/null differ diff --git a/packs/tables/000005.ldb b/packs/tables/000005.ldb new file mode 100644 index 0000000..64baa1c Binary files /dev/null and b/packs/tables/000005.ldb differ diff --git a/packs/tables/000006.log b/packs/tables/000006.log new file mode 100644 index 0000000..d5e06f3 Binary files /dev/null and b/packs/tables/000006.log differ diff --git a/packs/tables/CURRENT b/packs/tables/CURRENT index 1a84852..cacca75 100644 --- a/packs/tables/CURRENT +++ b/packs/tables/CURRENT @@ -1 +1 @@ -MANIFEST-000002 +MANIFEST-000004 diff --git a/packs/tables/LOG b/packs/tables/LOG index 6f8f621..d53aad9 100644 --- a/packs/tables/LOG +++ b/packs/tables/LOG @@ -1 +1,5 @@ -2026/08/11-22:56:53.194787 17465f000 Delete type=3 #1 +2026/08/11-23:23:08.330955 1742cf000 Recovering log #3 +2026/08/11-23:23:08.331078 1742cf000 Level-0 table #5: started +2026/08/11-23:23:08.331226 1742cf000 Level-0 table #5: 3739 bytes OK +2026/08/11-23:23:08.331550 1742cf000 Delete type=0 #3 +2026/08/11-23:23:08.331583 1742cf000 Delete type=3 #2 diff --git a/packs/tables/LOG.old b/packs/tables/LOG.old new file mode 100644 index 0000000..2c994fb --- /dev/null +++ b/packs/tables/LOG.old @@ -0,0 +1 @@ +2026/08/11-23:22:30.849798 17117f000 Delete type=3 #1 diff --git a/packs/tables/MANIFEST-000002 b/packs/tables/MANIFEST-000002 deleted file mode 100644 index bbbc585..0000000 Binary files a/packs/tables/MANIFEST-000002 and /dev/null differ diff --git a/packs/tables/MANIFEST-000004 b/packs/tables/MANIFEST-000004 new file mode 100644 index 0000000..004ff30 Binary files /dev/null and b/packs/tables/MANIFEST-000004 differ diff --git a/packs/talents/000003.log b/packs/talents/000003.log deleted file mode 100644 index 443b7ea..0000000 Binary files a/packs/talents/000003.log and /dev/null differ diff --git a/packs/talents/000005.ldb b/packs/talents/000005.ldb new file mode 100644 index 0000000..fb653de Binary files /dev/null and b/packs/talents/000005.ldb differ diff --git a/packs/talents/000006.log b/packs/talents/000006.log new file mode 100644 index 0000000..29003f7 Binary files /dev/null and b/packs/talents/000006.log differ diff --git a/packs/talents/CURRENT b/packs/talents/CURRENT index 1a84852..cacca75 100644 --- a/packs/talents/CURRENT +++ b/packs/talents/CURRENT @@ -1 +1 @@ -MANIFEST-000002 +MANIFEST-000004 diff --git a/packs/talents/LOG b/packs/talents/LOG index cb1063d..80e25cf 100644 --- a/packs/talents/LOG +++ b/packs/talents/LOG @@ -1 +1,5 @@ -2026/08/11-22:56:53.168626 17465f000 Delete type=3 #1 +2026/08/11-23:23:08.307829 174adb000 Recovering log #3 +2026/08/11-23:23:08.308163 174adb000 Level-0 table #5: started +2026/08/11-23:23:08.308314 174adb000 Level-0 table #5: 5770 bytes OK +2026/08/11-23:23:08.308647 174adb000 Delete type=0 #3 +2026/08/11-23:23:08.308699 174adb000 Delete type=3 #2 diff --git a/packs/talents/LOG.old b/packs/talents/LOG.old new file mode 100644 index 0000000..54aea62 --- /dev/null +++ b/packs/talents/LOG.old @@ -0,0 +1 @@ +2026/08/11-23:22:30.823417 172197000 Delete type=3 #1 diff --git a/packs/talents/MANIFEST-000002 b/packs/talents/MANIFEST-000002 deleted file mode 100644 index bbbc585..0000000 Binary files a/packs/talents/MANIFEST-000002 and /dev/null differ diff --git a/packs/talents/MANIFEST-000004 b/packs/talents/MANIFEST-000004 new file mode 100644 index 0000000..f8c6b47 Binary files /dev/null and b/packs/talents/MANIFEST-000004 differ diff --git a/packs/vehicles/000003.log b/packs/vehicles/000003.log deleted file mode 100644 index 9950ce5..0000000 Binary files a/packs/vehicles/000003.log and /dev/null differ diff --git a/packs/vehicles/000005.ldb b/packs/vehicles/000005.ldb new file mode 100644 index 0000000..f6b2cd4 Binary files /dev/null and b/packs/vehicles/000005.ldb differ diff --git a/packs/vehicles/000006.log b/packs/vehicles/000006.log new file mode 100644 index 0000000..47800e2 Binary files /dev/null and b/packs/vehicles/000006.log differ diff --git a/packs/vehicles/CURRENT b/packs/vehicles/CURRENT index 1a84852..cacca75 100644 --- a/packs/vehicles/CURRENT +++ b/packs/vehicles/CURRENT @@ -1 +1 @@ -MANIFEST-000002 +MANIFEST-000004 diff --git a/packs/vehicles/LOG b/packs/vehicles/LOG index 1489aeb..58fcb45 100644 --- a/packs/vehicles/LOG +++ b/packs/vehicles/LOG @@ -1 +1,5 @@ -2026/08/11-22:56:53.176044 17465f000 Delete type=3 #1 +2026/08/11-23:23:08.318258 1752e7000 Recovering log #3 +2026/08/11-23:23:08.318423 1752e7000 Level-0 table #5: started +2026/08/11-23:23:08.318619 1752e7000 Level-0 table #5: 6939 bytes OK +2026/08/11-23:23:08.318944 1752e7000 Delete type=0 #3 +2026/08/11-23:23:08.319006 1752e7000 Delete type=3 #2 diff --git a/packs/vehicles/LOG.old b/packs/vehicles/LOG.old new file mode 100644 index 0000000..6ec0926 --- /dev/null +++ b/packs/vehicles/LOG.old @@ -0,0 +1 @@ +2026/08/11-23:22:30.831389 170973000 Delete type=3 #1 diff --git a/packs/vehicles/MANIFEST-000002 b/packs/vehicles/MANIFEST-000002 deleted file mode 100644 index bbbc585..0000000 Binary files a/packs/vehicles/MANIFEST-000002 and /dev/null differ diff --git a/packs/vehicles/MANIFEST-000004 b/packs/vehicles/MANIFEST-000004 new file mode 100644 index 0000000..d4d73b7 Binary files /dev/null and b/packs/vehicles/MANIFEST-000004 differ diff --git a/packs/weapons/000003.log b/packs/weapons/000003.log deleted file mode 100644 index e1488ed..0000000 Binary files a/packs/weapons/000003.log and /dev/null differ diff --git a/packs/weapons/000005.ldb b/packs/weapons/000005.ldb new file mode 100644 index 0000000..742beba Binary files /dev/null and b/packs/weapons/000005.ldb differ diff --git a/packs/weapons/000006.log b/packs/weapons/000006.log new file mode 100644 index 0000000..4d427a6 Binary files /dev/null and b/packs/weapons/000006.log differ diff --git a/packs/weapons/CURRENT b/packs/weapons/CURRENT index 1a84852..cacca75 100644 --- a/packs/weapons/CURRENT +++ b/packs/weapons/CURRENT @@ -1 +1 @@ -MANIFEST-000002 +MANIFEST-000004 diff --git a/packs/weapons/LOG b/packs/weapons/LOG index 1caa48d..f4adbf3 100644 --- a/packs/weapons/LOG +++ b/packs/weapons/LOG @@ -1 +1,5 @@ -2026/08/11-22:56:53.170534 173e53000 Delete type=3 #1 +2026/08/11-23:23:08.311931 174adb000 Recovering log #3 +2026/08/11-23:23:08.312527 174adb000 Level-0 table #5: started +2026/08/11-23:23:08.312793 174adb000 Level-0 table #5: 14767 bytes OK +2026/08/11-23:23:08.313217 174adb000 Delete type=0 #3 +2026/08/11-23:23:08.313275 174adb000 Delete type=3 #2 diff --git a/packs/weapons/LOG.old b/packs/weapons/LOG.old new file mode 100644 index 0000000..b9e9eaa --- /dev/null +++ b/packs/weapons/LOG.old @@ -0,0 +1 @@ +2026/08/11-23:22:30.825501 172197000 Delete type=3 #1 diff --git a/packs/weapons/MANIFEST-000002 b/packs/weapons/MANIFEST-000002 deleted file mode 100644 index bbbc585..0000000 Binary files a/packs/weapons/MANIFEST-000002 and /dev/null differ diff --git a/packs/weapons/MANIFEST-000004 b/packs/weapons/MANIFEST-000004 new file mode 100644 index 0000000..6a582af Binary files /dev/null and b/packs/weapons/MANIFEST-000004 differ diff --git a/system.json b/system.json index 67ca553..aec29a2 100644 --- a/system.json +++ b/system.json @@ -120,6 +120,13 @@ "type": "JournalEntry", "system": "ringbrp" }, + { + "name": "starter", + "label": "The Custodians · PAPER WINDOW (starter scenario)", + "path": "packs/starter", + "type": "Adventure", + "system": "ringbrp" + }, { "name": "slice", "label": "The Custodians · The Ilfracombe Crossing", diff --git a/tools/build-packs.mjs b/tools/build-packs.mjs index 5d83fa6..6565b1f 100644 --- a/tools/build-packs.mjs +++ b/tools/build-packs.mjs @@ -472,6 +472,161 @@ await writePack("armour", ARMOURS.map(a => { await writePack("tables", entries); } +// PAPER WINDOW — the starter scenario, shipped as an Adventure so the whole thing +// imports in one click rather than as nine loose documents a GM has to assemble. +{ + const S = await import("./scenario-starter.mjs"); + const { ROLES, TRADES, INDUCTION, TRADE_BANDS, STANDING_ISSUE, TIER_ISSUE, TIERS } + = await import("../postings.mjs"); + + // A posting or trade expanded into the shape buildActor wants. Ratings are FIXED at + // the middle of the band rather than rolled: every table that runs the starter should + // get the same six agents, and a pregen that varies between installs is not a pregen. + const mid = b => Math.round((b[0] + b[1]) / 2); + const expand = spec => { + const isTrade = !!spec.trade; + const R = isTrade ? TRADES[spec.trade] : ROLES[spec.role]; + if (!R) throw new Error(`starter: no ${isTrade ? "trade" : "role"} "${spec.trade ?? spec.role}"`); + const T = TIERS[spec.rank === "Probationary" ? "probationary" : "officer"]; + const coreV = isTrade ? mid(TRADE_BANDS.core) : mid(T.core); + const supportV = isTrade ? mid(TRADE_BANDS.support) : mid(T.support); + const inducV = mid(INDUCTION.bandByTier[spec.rank === "Probationary" ? "probationary" : "officer"]); + + const skills = []; + const seen = new Set(); + const addSkill = (id, val) => { + if (seen.has(id)) return; // induction never demotes a trade + seen.add(id); + const [fam, sp = ""] = id.split(":"); + skills.push({ fam, spec: sp, val }); + }; + for (const k of R.core) addSkill(k, coreV); + for (const k of R.support) addSkill(k, supportV); + if (isTrade) for (const k of INDUCTION.skills) addSkill(k, inducV); + addSkill("language:trade_cant", 55); + + // Kit: standing issue, the posting or trade's own, and induction for a trade. + const kitKeys = [...STANDING_ISSUE, ...R.kit, + ...(isTrade ? INDUCTION.kit : []), + ...(TIER_ISSUE[spec.rank === "Probationary" ? "probationary" : "officer"] ?? [])]; + const weapons = [], armour = [], gear = []; + for (const k of new Set(kitKeys)) { + if (WEAPON_LOOKUP.has(k)) weapons.push(k); + else if (ARMOUR_LOOKUP.has(k)) armour.push(k); + else if (GEAR_LOOKUP.has(k)) gear.push(k); + else throw new Error(`starter: ${spec.name} issued unknown item "${k}"`); + } + const bio = `
${R.label}, ${spec.rank}. ${R.blurb}
` + + `Your job in this case. ${spec.spotlight}
` + + `Your complication. ${spec.complication}
` + + `${spec.note}
`; + return { ...spec, skills, weapons, armour, gear, bio }; + }; + + const actorDocs = [], itemDocsFor = new Map(); + for (const spec of S.STARTER_TEAM) { + const { actorDoc, itemDocs } = buildActor(expand(spec), "character"); + actorDoc.items = itemDocs; // Adventures embed full documents + actorDocs.push(actorDoc); + } + for (const spec of S.STARTER_CAST) { + const { actorDoc, itemDocs } = buildActor(spec, "npc"); + actorDoc.items = itemDocs; + actorDocs.push(actorDoc); + } + + // The credential, as an item rather than a paragraph in the brief. + const A = S.STARTER_AUTHORITY; + const authId = idFor(`item:${A.key}`); + const authority = { + _id: authId, name: A.name, type: "authority", img: IC("cover_id") ?? ICON.gear, + system: { + description: A.description, + holderName: A.holderName, holderRole: A.holderRole, issuer: A.issuer, + scope: A.scope, scopeNote: A.scopeNote, + trust: { value: A.trust, max: 4 }, + expires: "", redFlags: A.redFlags, dossier: [], + establishesRemit: false, timesWidened: 0, mergeSteps: 0, + burned: false, suspect: false, aiPosture: "", notes: "" + }, + effects: [], ...BASE(), flags: { ringbrp: { key: A.key } } + }; + + // The case file, already at the scenario's opening state. + const C = S.STARTER_CASE; + const caseId = idFor(`actor:${C.key}`); + const caseDoc = { + _id: caseId, name: C.name, type: "expedition", img: ICON.caseFile ?? ICON.actor, + system: { + integrityMode: "abstract", + integrity: { value: C.containment, max: 10 }, + supply: { value: C.resources, max: 10 }, + power: { value: C.clearance, max: 10 }, + assets: [], members: S.STARTER_TEAM.map(t => t.key), + journey: { + destinationId: "rookery_heath", destinationName: C.destination, + destinationKnowledge: "briefed", currentLeg: 0, elapsedDays: 0, + siteTableId: "sites", hazardTableId: "hazards", returnClock: 0, + legs: C.legs.map((l, i) => ({ + index: i, terrain: l.label, mode: "foot", kindId: l.kindId, + difficulty: l.difficulty, modifier: l.mod, days: 0, + explored: true, resolved: false, forcedEncounterId: "", + costSupply: 1, costPower: 0 + })) + }, + standings: [{ groupId: "returned_clerks", value: 0, impression: 0, encounters: 0, + knownTaboos: [], knownDesires: [], pidginEstablished: false, obligations: [] }], + across: false, crossingModifier: 0, salvageEra: "modern", + notes: C.notes + }, + items: [], effects: [], + prototypeToken: { name: C.name, displayName: 20, actorLink: true, width: 1, height: 1, + texture: { src: ICON.actor }, sight: { enabled: false }, disposition: 1, + bar1: { attribute: "integrity" }, bar2: { attribute: "supply" }, flags: {} }, + ...BASE() + }; + actorDocs.push(caseDoc); + + const gmJournal = journalDoc("PAPER WINDOW — Run the case", "starter_gm", S.STARTER_PAGES); + const handouts = journalDoc("PAPER WINDOW — Player handouts", "starter_handouts", S.STARTER_HANDOUTS); + // Handouts are for handing out. A GM who has to change ownership on seven pages + // before the session starts will simply read them aloud instead. + handouts.doc.ownership = { default: 2 }; + for (const p of handouts.pages) p.ownership = { default: 2 }; + + const advId = idFor("adventure:paper_window"); + const adventure = { + _id: advId, + name: "PAPER WINDOW — The Dead Letter Office", + img: IC("actor_case") ?? ICON.caseFile ?? ICON.actor, + caption: "A six-player starter case. Four to five hours.", + description: + "Six brown envelopes with a 1987 postmark, from an office that closed in " + + "1998. Open one and the person it is addressed to begins disappearing from the " + + "record — not dying, just no longer filed.
" + + "The complete starter: GM scenario, seven player handouts, six pregenerated " + + "agents with their scenario jobs and complications, the cast, the Borrowed " + + "Authority as a usable credential, and the case file already set to Containment 8, " + + "Resources 8, Clearance 6 with its four phases loaded.
" + + "It teaches the game while it is being played: Borrowed Authority at the " + + "cordon, investigation in the cage, crossing at Window Six, READ / SIGNAL / OFFER " + + "with the night shift, one containable fight, and a closing scene that needs all " + + "six players.
", + actors: actorDocs, + items: [authority], + journal: [ + { ...gmJournal.doc, pages: gmJournal.pages }, + { ...handouts.doc, pages: handouts.pages } + ], + scenes: [], combats: [], tables: [], macros: [], cards: [], playlists: [], folders: [], + sort: 0, ...BASE() + }; + + await writePack("starter", [[`!adventures!${advId}`, adventure]]); + console.log(` starter PAPER WINDOW — ${actorDocs.length} actors, ` + + `${S.STARTER_PAGES.length} GM pages, ${S.STARTER_HANDOUTS.length} handouts`); +} + // the slice journal { const { SLICE_PAGES } = await import("./slice-text.mjs"); diff --git a/tools/scenario-starter.mjs b/tools/scenario-starter.mjs new file mode 100644 index 0000000..7511e91 --- /dev/null +++ b/tools/scenario-starter.mjs @@ -0,0 +1,696 @@ +/** + * PAPER WINDOW — the starter scenario, as data. + * + * Shipped as a Foundry ADVENTURE so the whole thing imports in one click: the GM + * text, the seven player handouts as separate pages a GM can reveal one at a time, + * the six pregens with their scenario functions and complications already written + * into their biographies, the cast, the case file with its tracks and phases already + * set, and the Borrowed Authority as a real item rather than a paragraph. + * + * The handouts are their own journal with default ownership OBSERVER, because a + * handout a GM has to copy out of the scenario text is a handout that does not get + * used. The GM journal stays hidden. + */ + +const P = s => `${s}
`; +const H = (n, s) => `| ${h} | `).join("")}
|---|
| ${c} | `).join("")}
${s}`; + +/* ---------------------------------------------------------------- */ +/* The GM scenario */ +/* ---------------------------------------------------------------- */ + +export const STARTER_PAGES = [ + +{ name: "1 · Run this first", content: + P("PAPER WINDOW is a six-player introductory case for The Custodians. " + + "Four to five hours. Classification Near. Bureaucratic horror, " + + "investigation, negotiation and one containable fight.") + + P("It teaches the game while it is being played. Do not explain the " + + "subsystems in the briefing. Introduce each one the first time the agents need it.") + + TABLE(["Scene", "Introduces"], [ + ["Briefing", "the agency, the case tracks, cover and objectives"], + ["The cordon", "ordinary skill rolls and Borrowed Authority"], + ["The dead-letter cage", "investigation, fail-forward clues and the case clock"], + ["Window Six", "crossing and Coherence"], + ["The night shift", "READ, SIGNAL, OFFER and Standing"], + ["The Cancellation Man", "reaction lanes, cover, graded defences, armour, First Aid"], + ["Closing the register", "specialist teamwork, Containment and a real choice"], + ["Coming home", "extraction, return rolls, debrief and experience"] + ]) + + P("There is no clue that must be found and no roll that must succeed. " + + "A failed roll changes the cost, the danger or the clarity of the information. It " + + "never stops the scenario.") + + H(2, "The six-player spotlight rule") + + P("Every major scene names a spotlight agent. That player frames the " + + "first approach. Others assist, supply dossier items, handle consequences or propose " + + "alternatives. Do not solve another player's specialist scene with the Field " + + "Lead's phase roll. The purpose of a six-player starter is six protagonists, " + + "not six people present.") +}, + +{ name: "2 · The briefing", content: + READ("At 08:12 yesterday, six brown envelopes were delivered in Coventry. They bear " + + "valid first-class postage, a 1987 postmark and the cancellation mark of Rookery Heath " + + "Delivery Office, which closed in 1998.
Inside its scope this works and costs 1 Trust: a surveyor walking a condemned " + + "building is not intrusion, it is Tuesday.
" + + "Entering the sealed records cage or altering the demolition schedule is one " + + "tier beyond — roll, at −20% for each red flag broken.
" + + "A fumble burns it: the real Imogen Price telephones the site manager.
" +}; + +/** The case file, already set to the scenario's opening state. */ +export const STARTER_CASE = { + key: "pw_case", + name: "PW-01 · Rookery Heath", + containment: 8, resources: 8, clearance: 6, + destination: "Rookery Heath Delivery Office, Coventry", + notes: + "Last Collection clock 0 / 4. Advance it on a fumbled phase, twenty " + + "minutes of argument without action, a loud weapon fired in the mundane building, the " + + "credential burned, or the clerks' taboo violated after it has been learned.
" + + "Not for careful investigation. Never for careful investigation.
", + legs: [ + { label: "Approach — the demolition cordon", kindId: "approach", difficulty: "average", mod: 0 }, + { label: "Investigation — the dead-letter cage", kindId: "cordon", difficulty: "average", mod: -5 }, + { label: "Containment — the night shift", kindId: "containment", difficulty: "difficult", mod: 0 }, + { label: "Extraction — closing the register", kindId: "extraction", difficulty: "average", mod: -15 } + ] +}; diff --git a/tools/update-readme.mjs b/tools/update-readme.mjs index e3e93bc..6c0d163 100644 --- a/tools/update-readme.mjs +++ b/tools/update-readme.mjs @@ -40,7 +40,8 @@ const box = `| | | | **Bestiary** | ${NPCS.length} statblocks across folklore, horror and the far side | | **Eras** | modern, antique, future, anomalous — the agency is old and never abolishes anything | | **Icons** | ${Object.keys(ICON_MAP).length}, all generated from \`tools/icons.mjs\`; no external art | -| **Compendia** | pregens, NPCs, skills, weapons, armour, gear, vehicles, powers, tables, rules, a starter case | +| **Starter scenario** | **PAPER WINDOW**, a six-player introductory case, importable as a single Adventure | +| **Compendia** | pregens, NPCs, skills, weapons, armour, gear, vehicles, powers, tables, rules, the starter | | **Rules journal** | ${RULES_PAGES.length} pages, generated from \`rules.mjs\` so the book cannot drift from the code |`; const replaceRegion = (src, marker, body) => {