diff --git a/README.md b/README.md index b09ae2c..cf3be88 100644 --- a/README.md +++ b/README.md @@ -65,13 +65,13 @@ changing underneath a running server. | **Postings** | 25, each with a key characteristic, trained skills, talents and its own kit | | **Trades** | 10 ordinary jobs the department recruits from, plus induction | | **Catalogue** | 236 items — 123 gear, 62 weapons, 32 armour, 19 vehicles | -| **Skills / talents** | 59 skills, 19 talents | +| **Skills / talents** | 62 skills, 19 talents | | **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 | | **Starter scenario** | **BLACK PLATFORM**, a six-player action starter, 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 | +| **Rules journal** | 13 pages, generated from `rules.mjs` so the book cannot drift from the code | --- @@ -180,9 +180,9 @@ The build runs all five guards before it writes anything: ``` -check-rules: OK — 87 rules, 20 files scanned, 3 aliases + 41 constant sets checked, 275 formulas verified +check-rules: OK — 92 rules, 20 files scanned, 3 aliases + 45 constant sets checked, 290 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, 945 keys, no leaf/branch collisions -check-behaviour: OK — 46 behavioural tests +check-lang: OK — en.json, 976 keys, no leaf/branch collisions +check-behaviour: OK — 55 behavioural tests ``` diff --git a/lang/en.json b/lang/en.json index 6e20b38..ca76973 100644 --- a/lang/en.json +++ b/lang/en.json @@ -943,5 +943,36 @@ "RINGBRP.Item.ArtRating": "Rating", "RINGBRP.Item.PushCost": "Push cost", "RINGBRP.Item.PushRule": "What pushing does", - "RINGBRP.Item.Origin": "Where it came from" + "RINGBRP.Item.Origin": "Where it came from", + "RINGBRP.Panic.Label": "Panic", + "RINGBRP.Panic.Steady": "Steady", + "RINGBRP.Panic.SteadyNote": "You are all right. You are doing the job.", + "RINGBRP.Panic.Rattled": "Rattled", + "RINGBRP.Panic.RattledNote": "Your hands are not quite steady. No penalty — the first one is free.", + "RINGBRP.Panic.Shaken": "Shaken", + "RINGBRP.Panic.ShakenNote": "−5% to everything. You keep checking the way you came in.", + "RINGBRP.Panic.Losing": "Losing it", + "RINGBRP.Panic.LosingNote": "−10% to everything. You are talking too fast and not hearing answers.", + "RINGBRP.Panic.Overwhelmed": "Overwhelmed", + "RINGBRP.Panic.OverwhelmedNote": "−20% to everything. One more and you go.", + "RINGBRP.Panic.Broken": "Broken", + "RINGBRP.Panic.BrokenNote": "You freeze, run, or go at it. Choose one, take it, and come back at 3.", + "RINGBRP.Panic.Trig.WrongThing": "Something that should not be there", + "RINGBRP.Panic.Trig.Touched": "It touched you", + "RINGBRP.Panic.Trig.Body": "A body", + "RINGBRP.Panic.Trig.Known": "Somebody you knew", + "RINGBRP.Panic.Trig.Dark": "The lights went", + "RINGBRP.Panic.Trig.Wounded": "A major wound", + "RINGBRP.Panic.Trig.Named": "It said your name", + "RINGBRP.Panic.Relief.Steady": "Steady yourself (POW×5)", + "RINGBRP.Panic.Relief.Religion": "Religion — something older than the department", + "RINGBRP.Panic.Relief.Psychology": "Psychology — talk somebody else down", + "RINGBRP.Panic.Relief.Occult": "Occult — name it, out loud, for everyone who can hear", + "RINGBRP.Panic.Relief.Anchor": "Your anchor, once per case", + "RINGBRP.Panic.Gained": "{name} — panic {from} → {to}", + "RINGBRP.Panic.Eased": "{name} — panic {from} → {to}", + "RINGBRP.Panic.Breaks": "BREAKS — freeze, flee, or fixate. Choose, take it, come back at 3.", + "RINGBRP.Panic.NoChange": "No change.", + "RINGBRP.Flavour.Panicking": "panicking ({mod}%)", + "RINGBRP.Chat.ToEverything": "to everything" } diff --git a/packs/armour/000003.log b/packs/armour/000003.log new file mode 100644 index 0000000..9b6d27d Binary files /dev/null and b/packs/armour/000003.log differ diff --git a/packs/armour/000005.ldb b/packs/armour/000005.ldb deleted file mode 100644 index c982555..0000000 Binary files a/packs/armour/000005.ldb and /dev/null differ diff --git a/packs/armour/000006.log b/packs/armour/000006.log deleted file mode 100644 index 1a1bb27..0000000 Binary files a/packs/armour/000006.log and /dev/null differ diff --git a/packs/armour/CURRENT b/packs/armour/CURRENT index cacca75..1a84852 100644 --- a/packs/armour/CURRENT +++ b/packs/armour/CURRENT @@ -1 +1 @@ -MANIFEST-000004 +MANIFEST-000002 diff --git a/packs/armour/LOG b/packs/armour/LOG index c7e3f23..9679cb5 100644 --- a/packs/armour/LOG +++ b/packs/armour/LOG @@ -1,5 +1 @@ -2026/08/12-22:44:48.920895 172817000 Recovering log #3 -2026/08/12-22:44:48.921047 172817000 Level-0 table #5: started -2026/08/12-22:44:48.921223 172817000 Level-0 table #5: 4591 bytes OK -2026/08/12-22:44:48.921517 172817000 Delete type=0 #3 -2026/08/12-22:44:48.921554 172817000 Delete type=3 #2 +2026/08/12-22:54:48.028748 16ea07000 Delete type=3 #1 diff --git a/packs/armour/LOG.old b/packs/armour/LOG.old deleted file mode 100644 index 7792940..0000000 --- a/packs/armour/LOG.old +++ /dev/null @@ -1 +0,0 @@ -2026/08/12-22:44:33.287733 16f5db000 Delete type=3 #1 diff --git a/packs/armour/MANIFEST-000002 b/packs/armour/MANIFEST-000002 new file mode 100644 index 0000000..bbbc585 Binary files /dev/null and b/packs/armour/MANIFEST-000002 differ diff --git a/packs/armour/MANIFEST-000004 b/packs/armour/MANIFEST-000004 deleted file mode 100644 index 9c05997..0000000 Binary files a/packs/armour/MANIFEST-000004 and /dev/null differ diff --git a/packs/expedition/000003.log b/packs/expedition/000003.log new file mode 100644 index 0000000..263295b Binary files /dev/null and b/packs/expedition/000003.log differ diff --git a/packs/expedition/000005.ldb b/packs/expedition/000005.ldb deleted file mode 100644 index 4ff460d..0000000 Binary files a/packs/expedition/000005.ldb and /dev/null differ diff --git a/packs/expedition/000006.log b/packs/expedition/000006.log deleted file mode 100644 index 268b187..0000000 Binary files a/packs/expedition/000006.log and /dev/null differ diff --git a/packs/expedition/CURRENT b/packs/expedition/CURRENT index cacca75..1a84852 100644 --- a/packs/expedition/CURRENT +++ b/packs/expedition/CURRENT @@ -1 +1 @@ -MANIFEST-000004 +MANIFEST-000002 diff --git a/packs/expedition/LOG b/packs/expedition/LOG index 96ac7df..971eb80 100644 --- a/packs/expedition/LOG +++ b/packs/expedition/LOG @@ -1,5 +1 @@ -2026/08/12-22:44:48.946376 17382f000 Recovering log #3 -2026/08/12-22:44:48.946637 17382f000 Level-0 table #5: started -2026/08/12-22:44:48.946788 17382f000 Level-0 table #5: 4129 bytes OK -2026/08/12-22:44:48.947029 17382f000 Delete type=0 #3 -2026/08/12-22:44:48.947065 17382f000 Delete type=3 #2 +2026/08/12-22:54:48.107122 16f213000 Delete type=3 #1 diff --git a/packs/expedition/LOG.old b/packs/expedition/LOG.old deleted file mode 100644 index 7110bdd..0000000 --- a/packs/expedition/LOG.old +++ /dev/null @@ -1 +0,0 @@ -2026/08/12-22:44:33.362147 16edcf000 Delete type=3 #1 diff --git a/packs/expedition/MANIFEST-000002 b/packs/expedition/MANIFEST-000002 new file mode 100644 index 0000000..bbbc585 Binary files /dev/null and b/packs/expedition/MANIFEST-000002 differ diff --git a/packs/expedition/MANIFEST-000004 b/packs/expedition/MANIFEST-000004 deleted file mode 100644 index 47fbcd4..0000000 Binary files a/packs/expedition/MANIFEST-000004 and /dev/null differ diff --git a/packs/gear/000003.log b/packs/gear/000003.log new file mode 100644 index 0000000..629ba6d Binary files /dev/null and b/packs/gear/000003.log differ diff --git a/packs/gear/000005.ldb b/packs/gear/000005.ldb deleted file mode 100644 index d9bac03..0000000 Binary files a/packs/gear/000005.ldb and /dev/null differ diff --git a/packs/gear/000006.log b/packs/gear/000006.log deleted file mode 100644 index d1deb9b..0000000 Binary files a/packs/gear/000006.log and /dev/null differ diff --git a/packs/gear/CURRENT b/packs/gear/CURRENT index cacca75..1a84852 100644 --- a/packs/gear/CURRENT +++ b/packs/gear/CURRENT @@ -1 +1 @@ -MANIFEST-000004 +MANIFEST-000002 diff --git a/packs/gear/LOG b/packs/gear/LOG index 7aade72..d9b5688 100644 --- a/packs/gear/LOG +++ b/packs/gear/LOG @@ -1,5 +1 @@ -2026/08/12-22:44:48.922471 173023000 Recovering log #3 -2026/08/12-22:44:48.923346 173023000 Level-0 table #5: started -2026/08/12-22:44:48.923621 173023000 Level-0 table #5: 31614 bytes OK -2026/08/12-22:44:48.923809 173023000 Delete type=0 #3 -2026/08/12-22:44:48.923836 173023000 Delete type=3 #2 +2026/08/12-22:54:48.030538 16e1fb000 Delete type=3 #1 diff --git a/packs/gear/LOG.old b/packs/gear/LOG.old deleted file mode 100644 index 44758ca..0000000 --- a/packs/gear/LOG.old +++ /dev/null @@ -1 +0,0 @@ -2026/08/12-22:44:33.290003 16f5db000 Delete type=3 #1 diff --git a/packs/gear/MANIFEST-000002 b/packs/gear/MANIFEST-000002 new file mode 100644 index 0000000..bbbc585 Binary files /dev/null and b/packs/gear/MANIFEST-000002 differ diff --git a/packs/gear/MANIFEST-000004 b/packs/gear/MANIFEST-000004 deleted file mode 100644 index f7cd73c..0000000 Binary files a/packs/gear/MANIFEST-000004 and /dev/null differ diff --git a/packs/lastadmission/000003.log b/packs/lastadmission/000003.log new file mode 100644 index 0000000..bfabd98 Binary files /dev/null and b/packs/lastadmission/000003.log differ diff --git a/packs/lastadmission/000005.ldb b/packs/lastadmission/000005.ldb deleted file mode 100644 index 8b1bbb9..0000000 Binary files a/packs/lastadmission/000005.ldb and /dev/null differ diff --git a/packs/lastadmission/000006.log b/packs/lastadmission/000006.log deleted file mode 100644 index 670254b..0000000 Binary files a/packs/lastadmission/000006.log and /dev/null differ diff --git a/packs/lastadmission/CURRENT b/packs/lastadmission/CURRENT index cacca75..1a84852 100644 --- a/packs/lastadmission/CURRENT +++ b/packs/lastadmission/CURRENT @@ -1 +1 @@ -MANIFEST-000004 +MANIFEST-000002 diff --git a/packs/lastadmission/LOG b/packs/lastadmission/LOG index bc3d885..f41b510 100644 --- a/packs/lastadmission/LOG +++ b/packs/lastadmission/LOG @@ -1,5 +1 @@ -2026/08/12-22:44:48.936635 17200b000 Recovering log #3 -2026/08/12-22:44:48.937301 17200b000 Level-0 table #5: started -2026/08/12-22:44:48.937794 17200b000 Level-0 table #5: 45532 bytes OK -2026/08/12-22:44:48.938219 17200b000 Delete type=0 #3 -2026/08/12-22:44:48.938275 17200b000 Delete type=3 #2 +2026/08/12-22:54:48.091041 16ea07000 Delete type=3 #1 diff --git a/packs/lastadmission/LOG.old b/packs/lastadmission/LOG.old deleted file mode 100644 index 4a3b718..0000000 --- a/packs/lastadmission/LOG.old +++ /dev/null @@ -1 +0,0 @@ -2026/08/12-22:44:33.345401 16e5c3000 Delete type=3 #1 diff --git a/packs/lastadmission/MANIFEST-000002 b/packs/lastadmission/MANIFEST-000002 new file mode 100644 index 0000000..bbbc585 Binary files /dev/null and b/packs/lastadmission/MANIFEST-000002 differ diff --git a/packs/lastadmission/MANIFEST-000004 b/packs/lastadmission/MANIFEST-000004 deleted file mode 100644 index bd10fee..0000000 Binary files a/packs/lastadmission/MANIFEST-000004 and /dev/null differ diff --git a/packs/npcs/000003.log b/packs/npcs/000003.log new file mode 100644 index 0000000..535c9fd Binary files /dev/null and b/packs/npcs/000003.log differ diff --git a/packs/npcs/000005.ldb b/packs/npcs/000005.ldb deleted file mode 100644 index 29f69de..0000000 Binary files a/packs/npcs/000005.ldb and /dev/null differ diff --git a/packs/npcs/000006.log b/packs/npcs/000006.log deleted file mode 100644 index ac2ef10..0000000 Binary files a/packs/npcs/000006.log and /dev/null differ diff --git a/packs/npcs/CURRENT b/packs/npcs/CURRENT index cacca75..1a84852 100644 --- a/packs/npcs/CURRENT +++ b/packs/npcs/CURRENT @@ -1 +1 @@ -MANIFEST-000004 +MANIFEST-000002 diff --git a/packs/npcs/LOG b/packs/npcs/LOG index b5e9426..bfe79db 100644 --- a/packs/npcs/LOG +++ b/packs/npcs/LOG @@ -1,5 +1 @@ -2026/08/12-22:44:48.907196 173023000 Recovering log #3 -2026/08/12-22:44:48.908927 173023000 Level-0 table #5: started -2026/08/12-22:44:48.910392 173023000 Level-0 table #5: 225945 bytes OK -2026/08/12-22:44:48.910874 173023000 Delete type=0 #3 -2026/08/12-22:44:48.910934 173023000 Delete type=3 #2 +2026/08/12-22:54:48.072716 16f213000 Delete type=3 #1 diff --git a/packs/npcs/LOG.old b/packs/npcs/LOG.old deleted file mode 100644 index 4af5cd8..0000000 --- a/packs/npcs/LOG.old +++ /dev/null @@ -1 +0,0 @@ -2026/08/12-22:44:33.327015 16f5db000 Delete type=3 #1 diff --git a/packs/npcs/MANIFEST-000002 b/packs/npcs/MANIFEST-000002 new file mode 100644 index 0000000..bbbc585 Binary files /dev/null and b/packs/npcs/MANIFEST-000002 differ diff --git a/packs/npcs/MANIFEST-000004 b/packs/npcs/MANIFEST-000004 deleted file mode 100644 index 116e469..0000000 Binary files a/packs/npcs/MANIFEST-000004 and /dev/null differ diff --git a/packs/powers/000003.log b/packs/powers/000003.log new file mode 100644 index 0000000..d723043 Binary files /dev/null and b/packs/powers/000003.log differ diff --git a/packs/powers/000005.ldb b/packs/powers/000005.ldb deleted file mode 100644 index 7f540c6..0000000 Binary files a/packs/powers/000005.ldb and /dev/null differ diff --git a/packs/powers/000006.log b/packs/powers/000006.log deleted file mode 100644 index 43e306f..0000000 Binary files a/packs/powers/000006.log and /dev/null differ diff --git a/packs/powers/CURRENT b/packs/powers/CURRENT index cacca75..1a84852 100644 --- a/packs/powers/CURRENT +++ b/packs/powers/CURRENT @@ -1 +1 @@ -MANIFEST-000004 +MANIFEST-000002 diff --git a/packs/powers/LOG b/packs/powers/LOG index c9b9114..0181288 100644 --- a/packs/powers/LOG +++ b/packs/powers/LOG @@ -1,5 +1 @@ -2026/08/12-22:44:48.917345 172817000 Recovering log #3 -2026/08/12-22:44:48.917660 172817000 Level-0 table #5: started -2026/08/12-22:44:48.917794 172817000 Level-0 table #5: 3295 bytes OK -2026/08/12-22:44:48.918046 172817000 Delete type=0 #3 -2026/08/12-22:44:48.918081 172817000 Delete type=3 #2 +2026/08/12-22:54:48.101253 16ea07000 Delete type=3 #1 diff --git a/packs/powers/LOG.old b/packs/powers/LOG.old deleted file mode 100644 index e06ec38..0000000 --- a/packs/powers/LOG.old +++ /dev/null @@ -1 +0,0 @@ -2026/08/12-22:44:33.356982 16edcf000 Delete type=3 #1 diff --git a/packs/powers/MANIFEST-000002 b/packs/powers/MANIFEST-000002 new file mode 100644 index 0000000..bbbc585 Binary files /dev/null and b/packs/powers/MANIFEST-000002 differ diff --git a/packs/powers/MANIFEST-000004 b/packs/powers/MANIFEST-000004 deleted file mode 100644 index 20de082..0000000 Binary files a/packs/powers/MANIFEST-000004 and /dev/null differ diff --git a/packs/pregens/000003.log b/packs/pregens/000003.log new file mode 100644 index 0000000..5245fc9 Binary files /dev/null and b/packs/pregens/000003.log differ diff --git a/packs/pregens/000005.ldb b/packs/pregens/000005.ldb deleted file mode 100644 index 88aa716..0000000 Binary files a/packs/pregens/000005.ldb and /dev/null differ diff --git a/packs/pregens/000006.log b/packs/pregens/000006.log deleted file mode 100644 index ccd9db2..0000000 Binary files a/packs/pregens/000006.log and /dev/null differ diff --git a/packs/pregens/CURRENT b/packs/pregens/CURRENT index cacca75..1a84852 100644 --- a/packs/pregens/CURRENT +++ b/packs/pregens/CURRENT @@ -1 +1 @@ -MANIFEST-000004 +MANIFEST-000002 diff --git a/packs/pregens/LOG b/packs/pregens/LOG index f879377..3f4b771 100644 --- a/packs/pregens/LOG +++ b/packs/pregens/LOG @@ -1,5 +1 @@ -2026/08/12-22:44:48.896053 17200b000 Recovering log #3 -2026/08/12-22:44:48.898901 17200b000 Level-0 table #5: started -2026/08/12-22:44:48.902279 17200b000 Level-0 table #5: 545293 bytes OK -2026/08/12-22:44:48.902701 17200b000 Delete type=0 #3 -2026/08/12-22:44:48.902787 17200b000 Delete type=3 #2 +2026/08/12-22:54:48.053348 16f213000 Delete type=3 #1 diff --git a/packs/pregens/LOG.old b/packs/pregens/LOG.old deleted file mode 100644 index a390901..0000000 --- a/packs/pregens/LOG.old +++ /dev/null @@ -1 +0,0 @@ -2026/08/12-22:44:33.311753 16e5c3000 Delete type=3 #1 diff --git a/packs/pregens/MANIFEST-000002 b/packs/pregens/MANIFEST-000002 new file mode 100644 index 0000000..bbbc585 Binary files /dev/null and b/packs/pregens/MANIFEST-000002 differ diff --git a/packs/pregens/MANIFEST-000004 b/packs/pregens/MANIFEST-000004 deleted file mode 100644 index 7b13493..0000000 Binary files a/packs/pregens/MANIFEST-000004 and /dev/null differ diff --git a/packs/primer/000003.log b/packs/primer/000003.log new file mode 100644 index 0000000..fff1285 Binary files /dev/null and b/packs/primer/000003.log differ diff --git a/packs/primer/000005.ldb b/packs/primer/000005.ldb deleted file mode 100644 index 669e7b1..0000000 Binary files a/packs/primer/000005.ldb and /dev/null differ diff --git a/packs/primer/000006.log b/packs/primer/000006.log deleted file mode 100644 index daa37bb..0000000 Binary files a/packs/primer/000006.log and /dev/null differ diff --git a/packs/primer/CURRENT b/packs/primer/CURRENT index cacca75..1a84852 100644 --- a/packs/primer/CURRENT +++ b/packs/primer/CURRENT @@ -1 +1 @@ -MANIFEST-000004 +MANIFEST-000002 diff --git a/packs/primer/LOG b/packs/primer/LOG index 186daa8..90e6c20 100644 --- a/packs/primer/LOG +++ b/packs/primer/LOG @@ -1,5 +1 @@ -2026/08/12-22:44:48.925841 17382f000 Recovering log #3 -2026/08/12-22:44:48.926073 17382f000 Level-0 table #5: started -2026/08/12-22:44:48.926304 17382f000 Level-0 table #5: 21613 bytes OK -2026/08/12-22:44:48.926557 17382f000 Delete type=0 #3 -2026/08/12-22:44:48.926598 17382f000 Delete type=3 #2 +2026/08/12-22:54:48.098549 16ea07000 Delete type=3 #1 diff --git a/packs/primer/LOG.old b/packs/primer/LOG.old deleted file mode 100644 index 72124e1..0000000 --- a/packs/primer/LOG.old +++ /dev/null @@ -1 +0,0 @@ -2026/08/12-22:44:33.353318 16edcf000 Delete type=3 #1 diff --git a/packs/primer/MANIFEST-000002 b/packs/primer/MANIFEST-000002 new file mode 100644 index 0000000..bbbc585 Binary files /dev/null and b/packs/primer/MANIFEST-000002 differ diff --git a/packs/primer/MANIFEST-000004 b/packs/primer/MANIFEST-000004 deleted file mode 100644 index 2ce4ec2..0000000 Binary files a/packs/primer/MANIFEST-000004 and /dev/null differ diff --git a/packs/prologue/000003.log b/packs/prologue/000003.log new file mode 100644 index 0000000..122ec89 Binary files /dev/null and b/packs/prologue/000003.log differ diff --git a/packs/prologue/000005.ldb b/packs/prologue/000005.ldb deleted file mode 100644 index 03d2f7f..0000000 Binary files a/packs/prologue/000005.ldb and /dev/null differ diff --git a/packs/prologue/000006.log b/packs/prologue/000006.log deleted file mode 100644 index 2d03e81..0000000 Binary files a/packs/prologue/000006.log and /dev/null differ diff --git a/packs/prologue/CURRENT b/packs/prologue/CURRENT index cacca75..1a84852 100644 --- a/packs/prologue/CURRENT +++ b/packs/prologue/CURRENT @@ -1 +1 @@ -MANIFEST-000004 +MANIFEST-000002 diff --git a/packs/prologue/LOG b/packs/prologue/LOG index 44b46d1..7a65a61 100644 --- a/packs/prologue/LOG +++ b/packs/prologue/LOG @@ -1,5 +1 @@ -2026/08/12-22:44:48.939398 17200b000 Recovering log #3 -2026/08/12-22:44:48.940505 17200b000 Level-0 table #5: started -2026/08/12-22:44:48.941454 17200b000 Level-0 table #5: 43953 bytes OK -2026/08/12-22:44:48.942198 17200b000 Delete type=0 #3 -2026/08/12-22:44:48.942249 17200b000 Delete type=3 #2 +2026/08/12-22:54:48.095111 16f213000 Delete type=3 #1 diff --git a/packs/prologue/LOG.old b/packs/prologue/LOG.old deleted file mode 100644 index 200e76c..0000000 --- a/packs/prologue/LOG.old +++ /dev/null @@ -1 +0,0 @@ -2026/08/12-22:44:33.349699 16f5db000 Delete type=3 #1 diff --git a/packs/prologue/MANIFEST-000002 b/packs/prologue/MANIFEST-000002 new file mode 100644 index 0000000..bbbc585 Binary files /dev/null and b/packs/prologue/MANIFEST-000002 differ diff --git a/packs/prologue/MANIFEST-000004 b/packs/prologue/MANIFEST-000004 deleted file mode 100644 index 8e0eaa0..0000000 Binary files a/packs/prologue/MANIFEST-000004 and /dev/null differ diff --git a/packs/rules/000003.log b/packs/rules/000003.log new file mode 100644 index 0000000..a3b0e4b Binary files /dev/null and b/packs/rules/000003.log differ diff --git a/packs/rules/000005.ldb b/packs/rules/000005.ldb deleted file mode 100644 index c8f9c7d..0000000 Binary files a/packs/rules/000005.ldb and /dev/null differ diff --git a/packs/rules/000006.log b/packs/rules/000006.log deleted file mode 100644 index 7b08998..0000000 Binary files a/packs/rules/000006.log and /dev/null differ diff --git a/packs/rules/CURRENT b/packs/rules/CURRENT index cacca75..1a84852 100644 --- a/packs/rules/CURRENT +++ b/packs/rules/CURRENT @@ -1 +1 @@ -MANIFEST-000004 +MANIFEST-000002 diff --git a/packs/rules/LOG b/packs/rules/LOG index 9f296bb..c02b27a 100644 --- a/packs/rules/LOG +++ b/packs/rules/LOG @@ -1,5 +1 @@ -2026/08/12-22:44:48.927292 172817000 Recovering log #3 -2026/08/12-22:44:48.927647 172817000 Level-0 table #5: started -2026/08/12-22:44:48.928175 172817000 Level-0 table #5: 33484 bytes OK -2026/08/12-22:44:48.928522 172817000 Delete type=0 #3 -2026/08/12-22:44:48.928579 172817000 Delete type=3 #2 +2026/08/12-22:54:48.104875 16fa1f000 Delete type=3 #1 diff --git a/packs/rules/LOG.old b/packs/rules/LOG.old deleted file mode 100644 index c8e350f..0000000 --- a/packs/rules/LOG.old +++ /dev/null @@ -1 +0,0 @@ -2026/08/12-22:44:33.360431 16e5c3000 Delete type=3 #1 diff --git a/packs/rules/MANIFEST-000002 b/packs/rules/MANIFEST-000002 new file mode 100644 index 0000000..bbbc585 Binary files /dev/null and b/packs/rules/MANIFEST-000002 differ diff --git a/packs/rules/MANIFEST-000004 b/packs/rules/MANIFEST-000004 deleted file mode 100644 index 15f0607..0000000 Binary files a/packs/rules/MANIFEST-000004 and /dev/null differ diff --git a/packs/skills/000003.log b/packs/skills/000003.log new file mode 100644 index 0000000..d985ef5 Binary files /dev/null and b/packs/skills/000003.log differ diff --git a/packs/skills/000005.ldb b/packs/skills/000005.ldb deleted file mode 100644 index eb948ea..0000000 Binary files a/packs/skills/000005.ldb and /dev/null differ diff --git a/packs/skills/000006.log b/packs/skills/000006.log deleted file mode 100644 index 1989ee1..0000000 Binary files a/packs/skills/000006.log and /dev/null differ diff --git a/packs/skills/CURRENT b/packs/skills/CURRENT index cacca75..1a84852 100644 --- a/packs/skills/CURRENT +++ b/packs/skills/CURRENT @@ -1 +1 @@ -MANIFEST-000004 +MANIFEST-000002 diff --git a/packs/skills/LOG b/packs/skills/LOG index c52d119..6d830e7 100644 --- a/packs/skills/LOG +++ b/packs/skills/LOG @@ -1,5 +1 @@ -2026/08/12-22:44:48.913614 172817000 Recovering log #3 -2026/08/12-22:44:48.914288 172817000 Level-0 table #5: started -2026/08/12-22:44:48.914458 172817000 Level-0 table #5: 7590 bytes OK -2026/08/12-22:44:48.914739 172817000 Delete type=0 #3 -2026/08/12-22:44:48.914773 172817000 Delete type=3 #2 +2026/08/12-22:54:48.022621 16ea07000 Delete type=3 #1 diff --git a/packs/skills/LOG.old b/packs/skills/LOG.old deleted file mode 100644 index 6d0b24f..0000000 --- a/packs/skills/LOG.old +++ /dev/null @@ -1 +0,0 @@ -2026/08/12-22:44:33.281949 16edcf000 Delete type=3 #1 diff --git a/packs/skills/MANIFEST-000002 b/packs/skills/MANIFEST-000002 new file mode 100644 index 0000000..bbbc585 Binary files /dev/null and b/packs/skills/MANIFEST-000002 differ diff --git a/packs/skills/MANIFEST-000004 b/packs/skills/MANIFEST-000004 deleted file mode 100644 index 90efb97..0000000 Binary files a/packs/skills/MANIFEST-000004 and /dev/null differ diff --git a/packs/slice/000003.log b/packs/slice/000003.log new file mode 100644 index 0000000..39926e8 Binary files /dev/null and b/packs/slice/000003.log differ diff --git a/packs/slice/000005.ldb b/packs/slice/000005.ldb deleted file mode 100644 index ac4df01..0000000 Binary files a/packs/slice/000005.ldb and /dev/null differ diff --git a/packs/slice/000006.log b/packs/slice/000006.log deleted file mode 100644 index c25ac75..0000000 Binary files a/packs/slice/000006.log and /dev/null differ diff --git a/packs/slice/CURRENT b/packs/slice/CURRENT index cacca75..1a84852 100644 --- a/packs/slice/CURRENT +++ b/packs/slice/CURRENT @@ -1 +1 @@ -MANIFEST-000004 +MANIFEST-000002 diff --git a/packs/slice/LOG b/packs/slice/LOG index 7fbc5a6..79e0be8 100644 --- a/packs/slice/LOG +++ b/packs/slice/LOG @@ -1,5 +1 @@ -2026/08/12-22:44:48.943128 17200b000 Recovering log #3 -2026/08/12-22:44:48.943292 17200b000 Level-0 table #5: started -2026/08/12-22:44:48.943546 17200b000 Level-0 table #5: 16902 bytes OK -2026/08/12-22:44:48.943901 17200b000 Delete type=0 #3 -2026/08/12-22:44:48.943932 17200b000 Delete type=3 #2 +2026/08/12-22:54:48.100047 16e1fb000 Delete type=3 #1 diff --git a/packs/slice/LOG.old b/packs/slice/LOG.old deleted file mode 100644 index 67e4d93..0000000 --- a/packs/slice/LOG.old +++ /dev/null @@ -1 +0,0 @@ -2026/08/12-22:44:33.355279 16e5c3000 Delete type=3 #1 diff --git a/packs/slice/MANIFEST-000002 b/packs/slice/MANIFEST-000002 new file mode 100644 index 0000000..bbbc585 Binary files /dev/null and b/packs/slice/MANIFEST-000002 differ diff --git a/packs/slice/MANIFEST-000004 b/packs/slice/MANIFEST-000004 deleted file mode 100644 index 0966a50..0000000 Binary files a/packs/slice/MANIFEST-000004 and /dev/null differ diff --git a/packs/starter/000003.log b/packs/starter/000003.log new file mode 100644 index 0000000..5fb9735 Binary files /dev/null and b/packs/starter/000003.log differ diff --git a/packs/starter/000005.ldb b/packs/starter/000005.ldb deleted file mode 100644 index c0f9d9b..0000000 Binary files a/packs/starter/000005.ldb and /dev/null differ diff --git a/packs/starter/000006.log b/packs/starter/000006.log deleted file mode 100644 index aaf167c..0000000 Binary files a/packs/starter/000006.log and /dev/null differ diff --git a/packs/starter/CURRENT b/packs/starter/CURRENT index cacca75..1a84852 100644 --- a/packs/starter/CURRENT +++ b/packs/starter/CURRENT @@ -1 +1 @@ -MANIFEST-000004 +MANIFEST-000002 diff --git a/packs/starter/LOG b/packs/starter/LOG index fd4c1cc..e0190c8 100644 --- a/packs/starter/LOG +++ b/packs/starter/LOG @@ -1,5 +1 @@ -2026/08/12-22:44:48.931234 17200b000 Recovering log #3 -2026/08/12-22:44:48.932452 17200b000 Level-0 table #5: started -2026/08/12-22:44:48.933139 17200b000 Level-0 table #5: 82299 bytes OK -2026/08/12-22:44:48.933493 17200b000 Delete type=0 #3 -2026/08/12-22:44:48.933538 17200b000 Delete type=3 #2 +2026/08/12-22:54:48.085121 16ea07000 Delete type=3 #1 diff --git a/packs/starter/LOG.old b/packs/starter/LOG.old deleted file mode 100644 index c79d586..0000000 --- a/packs/starter/LOG.old +++ /dev/null @@ -1 +0,0 @@ -2026/08/12-22:44:33.337758 16f5db000 Delete type=3 #1 diff --git a/packs/starter/MANIFEST-000002 b/packs/starter/MANIFEST-000002 new file mode 100644 index 0000000..bbbc585 Binary files /dev/null and b/packs/starter/MANIFEST-000002 differ diff --git a/packs/starter/MANIFEST-000004 b/packs/starter/MANIFEST-000004 deleted file mode 100644 index 87687fc..0000000 Binary files a/packs/starter/MANIFEST-000004 and /dev/null differ diff --git a/packs/tables/000003.log b/packs/tables/000003.log new file mode 100644 index 0000000..f125b7d Binary files /dev/null and b/packs/tables/000003.log differ diff --git a/packs/tables/000005.ldb b/packs/tables/000005.ldb deleted file mode 100644 index 64baa1c..0000000 Binary files a/packs/tables/000005.ldb and /dev/null differ diff --git a/packs/tables/000006.log b/packs/tables/000006.log deleted file mode 100644 index d5e06f3..0000000 Binary files a/packs/tables/000006.log and /dev/null differ diff --git a/packs/tables/CURRENT b/packs/tables/CURRENT index cacca75..1a84852 100644 --- a/packs/tables/CURRENT +++ b/packs/tables/CURRENT @@ -1 +1 @@ -MANIFEST-000004 +MANIFEST-000002 diff --git a/packs/tables/LOG b/packs/tables/LOG index 937a7a9..6f35d78 100644 --- a/packs/tables/LOG +++ b/packs/tables/LOG @@ -1,5 +1 @@ -2026/08/12-22:44:48.944808 172817000 Recovering log #3 -2026/08/12-22:44:48.944973 172817000 Level-0 table #5: started -2026/08/12-22:44:48.945086 172817000 Level-0 table #5: 3739 bytes OK -2026/08/12-22:44:48.945317 172817000 Delete type=0 #3 -2026/08/12-22:44:48.945349 172817000 Delete type=3 #2 +2026/08/12-22:54:48.077691 16e1fb000 Delete type=3 #1 diff --git a/packs/tables/LOG.old b/packs/tables/LOG.old deleted file mode 100644 index d0fb875..0000000 --- a/packs/tables/LOG.old +++ /dev/null @@ -1 +0,0 @@ -2026/08/12-22:44:33.331276 16e5c3000 Delete type=3 #1 diff --git a/packs/tables/MANIFEST-000002 b/packs/tables/MANIFEST-000002 new file mode 100644 index 0000000..bbbc585 Binary files /dev/null and b/packs/tables/MANIFEST-000002 differ diff --git a/packs/tables/MANIFEST-000004 b/packs/tables/MANIFEST-000004 deleted file mode 100644 index 004ff30..0000000 Binary files a/packs/tables/MANIFEST-000004 and /dev/null differ diff --git a/packs/talents/000003.log b/packs/talents/000003.log new file mode 100644 index 0000000..443b7ea Binary files /dev/null and b/packs/talents/000003.log differ diff --git a/packs/talents/000005.ldb b/packs/talents/000005.ldb deleted file mode 100644 index fb653de..0000000 Binary files a/packs/talents/000005.ldb and /dev/null differ diff --git a/packs/talents/000006.log b/packs/talents/000006.log deleted file mode 100644 index 29003f7..0000000 Binary files a/packs/talents/000006.log and /dev/null differ diff --git a/packs/talents/CURRENT b/packs/talents/CURRENT index cacca75..1a84852 100644 --- a/packs/talents/CURRENT +++ b/packs/talents/CURRENT @@ -1 +1 @@ -MANIFEST-000004 +MANIFEST-000002 diff --git a/packs/talents/LOG b/packs/talents/LOG index 6204424..4ec26e8 100644 --- a/packs/talents/LOG +++ b/packs/talents/LOG @@ -1,5 +1 @@ -2026/08/12-22:44:48.915783 173023000 Recovering log #3 -2026/08/12-22:44:48.915922 173023000 Level-0 table #5: started -2026/08/12-22:44:48.916069 173023000 Level-0 table #5: 5770 bytes OK -2026/08/12-22:44:48.916313 173023000 Delete type=0 #3 -2026/08/12-22:44:48.916348 173023000 Delete type=3 #2 +2026/08/12-22:54:48.025116 16fa1f000 Delete type=3 #1 diff --git a/packs/talents/LOG.old b/packs/talents/LOG.old deleted file mode 100644 index f7759d7..0000000 --- a/packs/talents/LOG.old +++ /dev/null @@ -1 +0,0 @@ -2026/08/12-22:44:33.284515 16e5c3000 Delete type=3 #1 diff --git a/packs/talents/MANIFEST-000002 b/packs/talents/MANIFEST-000002 new file mode 100644 index 0000000..bbbc585 Binary files /dev/null and b/packs/talents/MANIFEST-000002 differ diff --git a/packs/talents/MANIFEST-000004 b/packs/talents/MANIFEST-000004 deleted file mode 100644 index f8c6b47..0000000 Binary files a/packs/talents/MANIFEST-000004 and /dev/null differ diff --git a/packs/vehicles/000003.log b/packs/vehicles/000003.log new file mode 100644 index 0000000..9950ce5 Binary files /dev/null and b/packs/vehicles/000003.log differ diff --git a/packs/vehicles/000005.ldb b/packs/vehicles/000005.ldb deleted file mode 100644 index f6b2cd4..0000000 Binary files a/packs/vehicles/000005.ldb and /dev/null differ diff --git a/packs/vehicles/000006.log b/packs/vehicles/000006.log deleted file mode 100644 index 47800e2..0000000 Binary files a/packs/vehicles/000006.log and /dev/null differ diff --git a/packs/vehicles/CURRENT b/packs/vehicles/CURRENT index cacca75..1a84852 100644 --- a/packs/vehicles/CURRENT +++ b/packs/vehicles/CURRENT @@ -1 +1 @@ -MANIFEST-000004 +MANIFEST-000002 diff --git a/packs/vehicles/LOG b/packs/vehicles/LOG index 8450cde..0da96d1 100644 --- a/packs/vehicles/LOG +++ b/packs/vehicles/LOG @@ -1,5 +1 @@ -2026/08/12-22:44:48.924550 17200b000 Recovering log #3 -2026/08/12-22:44:48.924684 17200b000 Level-0 table #5: started -2026/08/12-22:44:48.924837 17200b000 Level-0 table #5: 6939 bytes OK -2026/08/12-22:44:48.925120 17200b000 Delete type=0 #3 -2026/08/12-22:44:48.925163 17200b000 Delete type=3 #2 +2026/08/12-22:54:48.032886 16f213000 Delete type=3 #1 diff --git a/packs/vehicles/LOG.old b/packs/vehicles/LOG.old deleted file mode 100644 index 0e04bb5..0000000 --- a/packs/vehicles/LOG.old +++ /dev/null @@ -1 +0,0 @@ -2026/08/12-22:44:33.292038 16fde7000 Delete type=3 #1 diff --git a/packs/vehicles/MANIFEST-000002 b/packs/vehicles/MANIFEST-000002 new file mode 100644 index 0000000..bbbc585 Binary files /dev/null and b/packs/vehicles/MANIFEST-000002 differ diff --git a/packs/vehicles/MANIFEST-000004 b/packs/vehicles/MANIFEST-000004 deleted file mode 100644 index d4d73b7..0000000 Binary files a/packs/vehicles/MANIFEST-000004 and /dev/null differ diff --git a/packs/weapons/000003.log b/packs/weapons/000003.log new file mode 100644 index 0000000..e1488ed Binary files /dev/null and b/packs/weapons/000003.log differ diff --git a/packs/weapons/000005.ldb b/packs/weapons/000005.ldb deleted file mode 100644 index 742beba..0000000 Binary files a/packs/weapons/000005.ldb and /dev/null differ diff --git a/packs/weapons/000006.log b/packs/weapons/000006.log deleted file mode 100644 index 4d427a6..0000000 Binary files a/packs/weapons/000006.log and /dev/null differ diff --git a/packs/weapons/CURRENT b/packs/weapons/CURRENT index cacca75..1a84852 100644 --- a/packs/weapons/CURRENT +++ b/packs/weapons/CURRENT @@ -1 +1 @@ -MANIFEST-000004 +MANIFEST-000002 diff --git a/packs/weapons/LOG b/packs/weapons/LOG index 95ecea5..18b1cef 100644 --- a/packs/weapons/LOG +++ b/packs/weapons/LOG @@ -1,5 +1 @@ -2026/08/12-22:44:48.918709 173023000 Recovering log #3 -2026/08/12-22:44:48.919008 173023000 Level-0 table #5: started -2026/08/12-22:44:48.919255 173023000 Level-0 table #5: 14767 bytes OK -2026/08/12-22:44:48.919627 173023000 Delete type=0 #3 -2026/08/12-22:44:48.919674 173023000 Delete type=3 #2 +2026/08/12-22:54:48.027160 16f213000 Delete type=3 #1 diff --git a/packs/weapons/LOG.old b/packs/weapons/LOG.old deleted file mode 100644 index 8cdd7d9..0000000 --- a/packs/weapons/LOG.old +++ /dev/null @@ -1 +0,0 @@ -2026/08/12-22:44:33.286188 16e5c3000 Delete type=3 #1 diff --git a/packs/weapons/MANIFEST-000002 b/packs/weapons/MANIFEST-000002 new file mode 100644 index 0000000..bbbc585 Binary files /dev/null and b/packs/weapons/MANIFEST-000002 differ diff --git a/packs/weapons/MANIFEST-000004 b/packs/weapons/MANIFEST-000004 deleted file mode 100644 index 6a582af..0000000 Binary files a/packs/weapons/MANIFEST-000004 and /dev/null differ diff --git a/postings.mjs b/postings.mjs index 38bd71f..f1d5d40 100644 --- a/postings.mjs +++ b/postings.mjs @@ -60,7 +60,7 @@ export const ROLES = { registry: { label: "Registry Officer", blurb: "Keeps the record that a crossing is made against. Knows what the file says and, more usefully, what it used to say.", stat: "edu", - core: ["research","knowledge","anomaly_lore","language:own","computer_use","insight"], + core: ["occult","knowledge","anomaly_lore","language:own","computer_use","insight"], support: ["spot","art","first_aid","tradecraft","firearm:pistol"], talents: ["chain_custody","wrong_memory","debriefer","quorum"], kit: ["spectacles","notebook","handset","jacket","pistol_light", "carbon_book", "date_stamp", "seals"] }, @@ -82,7 +82,7 @@ export const ROLES = { interview: { label: "Interview Officer", blurb: "Sits with the witness afterwards. Patient, unhurried, and very hard to lie to twice.", stat: "cha", core: ["insight","persuade","listen","language:own","anthropology"], - support: ["fast_talk","spot","tradecraft","first_aid","firearm:pistol"], + support: ["psychology","spot","tradecraft","first_aid","firearm:pistol"], talents: ["debriefer","read_room","wrong_memory","unblinking"], kit: ["dictaphone","cover_id","notebook","jacket","pistol_light", "carbon_book", "vacuum_flask", "clipboard"] }, @@ -110,7 +110,7 @@ export const ROLES = { pathology: { label: "Pathology Officer", blurb: "Examines what is left. Establishes what a thing was, which is usually the question the case turns on.", stat: "int", core: ["medicine","science:biology","xenology:baseline","insight","research"], - support: ["first_aid","spot","anthropology","dodge","melee_weapon:knife"], + support: ["first_aid","spot","religion","dodge","melee_weapon:knife"], talents: ["triage","chain_custody","unblinking","clean_scene"], kit: ["chirurgeon_kit","gas_mask","envsuit","sample_case","dagger", "specimen_jars", "swab_kit", "barrier_gloves"] }, @@ -172,7 +172,7 @@ export const ROLES = { antiquities: { label: "Antiquities Officer", blurb: "An archaic posting the department never abolished. Old objects, older records, and the manners to ask about both.", stat: "edu", - core: ["knowledge","art","anomaly_lore","research","status"], + core: ["religion","art","anomaly_lore","research","status"], support: ["insight","spot","first_aid","melee_weapon:sword","firearm:pistol"], talents: ["chain_custody","wrong_memory","borrowed_hour","quorum"], kit: ["pocket_watch","tallow_candle","duelling_sword","brigandine","flintlock", "quill_ink", "sealing_wax", "sand_glass"] }, @@ -231,13 +231,13 @@ export const TRADES = { doctor: { label: "Doctor", blurb: "A&E, mostly. Used to people at their worst and to being the calmest thing in the room.", stat: "edu", core: ["medicine","first_aid","science:biology","insight","knowledge"], - support: ["research","status","persuade","listen","spot"], + support: ["research","psychology","persuade","listen","spot"], talents: ["triage","debriefer","unblinking","clean_scene"], kit: ["medkit","trauma_unit","analgesics","specimen_jars","swab_kit"] }, professor: { label: "Professor", blurb: "A chair in something narrow, and a reading list nobody else has finished. Argues for a living.", stat: "edu", - core: ["knowledge","research","anthropology","language:own","status"], + core: ["knowledge","research","occult","language:own","status"], support: ["insight","persuade","science:botany","listen","art"], talents: ["debriefer","read_room","requisition","wrong_memory"], kit: ["carbon_book","notebook","spectacles","clipboard","quill_ink"] }, diff --git a/ringbrp.mjs b/ringbrp.mjs index 50655aa..db91b1a 100644 --- a/ringbrp.mjs +++ b/ringbrp.mjs @@ -50,7 +50,9 @@ import { ARTS, powerCostFor, powerStartingRating, CROSSING, crossingArtPoolFor, crossingGrantsArt, crossingCostFor, REACTION, CIRCUMSTANCE, RANGE_LADDER, rangeBandFrom, MAJOR_WOUND_TABLE, - COHERENCE_BANDS, COHERENCE_COSTS, COHERENCE_RECOVERY, coherenceBandFrom + COHERENCE_BANDS, COHERENCE_COSTS, COHERENCE_RECOVERY, coherenceBandFrom, + PANIC_MAX, PANIC_BANDS, PANIC_TRIGGERS, PANIC_RELIEF, PANIC_BREAK, PANIC_AFTER_BREAK, + panicBandFrom, panicPenaltyFor, panicBreaksAt, panicAfter, panicReliefFor } from "./rules.mjs"; export { hitPointsFor, majorWoundFor, damageModifierFor, locationMaxHp, reflexRating, awarenessRating }; @@ -1030,6 +1032,17 @@ export async function skillRoll({ flavourLines = [...flavourLines, game.i18n.format("RINGBRP.Flavour.Encumbered", { mod: enc })]; } } + // Panic is the short track and it bites on everything, so it sits in the same stack + // the wound and encumbrance penalties already use — anything that computes a modifier + // and is not added here is a modifier nothing reads, which is this project's oldest + // way of shipping a rule that does not exist. + if (actor) { + const pan = panicPenaltyFor(actor.system?.panic?.value); + if (pan) { + situational = (Number(situational) || 0) + pan; + flavourLines = [...flavourLines, game.i18n.format("RINGBRP.Flavour.Panicking", { mod: pan })]; + } + } if (actor && !skipCoherence) { const coh = coherencePenaltyFor(actor.system?.coherence?.value, categoryId); if (coh) { @@ -1534,6 +1547,68 @@ export async function loseCoherence(actor, reasonKey, { amount = null, note = "" return bAfter; } +RINGBRP.panicBands = PANIC_BANDS; +RINGBRP.panicTriggers = PANIC_TRIGGERS; +RINGBRP.panicRelief = PANIC_RELIEF; + +export const panicBand = panicBandFrom; + +/** + * Take a point of panic. No roll: 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. + */ +export async function gainPanic(actor, triggerKey, { amount = null, note = "" } = {}) { + if (!actor) return null; + const spec = PANIC_TRIGGERS[triggerKey]; + const cost = amount ?? spec?.cost ?? 1; + const before = Number(actor.system?.panic?.value ?? 0); + const after = panicAfter(before, cost); + const log = [...(actor.system?.panic?.log ?? []), { reason: triggerKey, cost, note }]; + await actor.update({ "system.panic.value": after, "system.panic.log": log }); + await panicCard(actor, before, after, game.i18n.localize(String(spec?.label ?? triggerKey ?? "")), note); + return panicBandFrom(after); +} + +/** + * Put a point back. `level` is the outcome of whatever was rolled — a special takes two + * — so every relief in PANIC_RELIEF runs through one function and cannot drift. + */ +export async function easePanic(actor, reliefKey, level = "success", { note = "" } = {}) { + if (!actor) return null; + const spec = PANIC_RELIEF[reliefKey]; + const back = panicReliefFor(level); + const before = Number(actor.system?.panic?.value ?? 0); + const after = panicAfter(before, -back); + await actor.update({ "system.panic.value": after }); + await panicCard(actor, before, after, game.i18n.localize(String(spec?.label ?? reliefKey ?? "")), note); + return panicBandFrom(after); +} + +/** Breaking spends the panic: you come back at 3, because it is not a rest. */ +export async function breakPanic(actor, choice = "freeze") { + if (!actor) return null; + const before = Number(actor.system?.panic?.value ?? 0); + const after = PANIC_AFTER_BREAK; + await actor.update({ "system.panic.value": after }); + await panicCard(actor, before, after, + game.i18n.localize("RINGBRP.Panic.Breaks"), + PANIC_BREAK.includes(choice) ? choice : "freeze"); + return panicBandFrom(after); +} + +async function panicCard(actor, before, after, reason, note) { + const band = panicBandFrom(after); + await ChatMessage.create({ + speaker: ChatMessage.getSpeaker({ actor }), + content: await rbRenderTemplate("systems/ringbrp/templates/chat-panic.hbs", { + actorName: actor.name, before, after, reason, note, + band, breaks: panicBreaksAt(after), penalty: panicPenaltyFor(after), + choices: PANIC_BREAK, + pips: Array.from({ length: PANIC_MAX }, (_, i) => i < after) + }) + }); +} + /** * Displacement. Reaching zero is not a slow slide to be nursed back; it is an * event. The character does not die — they go home to an Earth that is almost @@ -4754,6 +4829,9 @@ class RingBRPCharacterSheet extends ActorSheet { ctx.reaction = this._reactionRail(); ctx.readiness = this._readiness ?? { rating: 0 }; ctx.coherence = { band: coherenceBand(sys.coherence?.value) }; + ctx.panic = { band: panicBandFrom(sys.panic?.value), + penalty: panicPenaltyFor(sys.panic?.value), + pips: Array.from({ length: PANIC_MAX }, (_, i) => i < Number(sys.panic?.value ?? 0)) }; ctx.readinessSource = ctx.readiness.best >= ctx.readiness.intFloor ? game.i18n.localize("RINGBRP.React.ReadinessSkill") : game.i18n.localize("RINGBRP.React.ReadinessInt"); @@ -4847,6 +4925,18 @@ class RingBRPCharacterSheet extends ActorSheet { if (item) await useDeviceFunction(this.actor, item, btn.dataset.fn); this.render(false); })); + root.querySelectorAll("[data-action='panic-adjust']").forEach(el => + el.addEventListener("click", async ev => { + ev.preventDefault(); ev.stopPropagation(); + const delta = Number(ev.currentTarget.dataset.delta) || 0; + if (delta > 0) await gainPanic(this.actor, "wrongThing", { amount: 1 }); + else await easePanic(this.actor, "steadyYourself", "success"); + })); + root.querySelectorAll("[data-action='panic-break']").forEach(el => + el.addEventListener("click", async ev => { + ev.preventDefault(); ev.stopPropagation(); + await breakPanic(this.actor, ev.currentTarget.dataset.choice ?? "freeze"); + })); root.querySelectorAll("[data-action='coherence-adjust']").forEach(el => el.addEventListener("click", async ev => { ev.preventDefault(); ev.stopPropagation(); @@ -6085,6 +6175,7 @@ Hooks.once("init", () => { reactionLoadFrom, reactionPrintedFrom, readinessRating, percentileFaces, circumstanceTotal, coherenceBand, coherencePenaltyFor, woundPenaltyFor, loseCoherence, regainCoherence, displace, + panicBand, panicPenaltyFor, panicBreaksAt, gainPanic, easePanic, breakPanic, isAcross, setAcross, useTransposed, debrief, activeCaseFile, returnCrossing, caseMembers, earnedDiscoveries, deviceFunctionStatus, earnDiscovery, useDeviceFunction, pickContactActor, resolveContact, newEncounter, claimRewards, activeRewardEffects, diff --git a/rules.mjs b/rules.mjs index c581eb4..d4a97d4 100644 --- a/rules.mjs +++ b/rules.mjs @@ -263,6 +263,108 @@ export const COHERENCE_COSTS = { export const COHERENCE_RECOVERY = { debrief: 1, downtimeWeek: 1, anchorObject: 1 }; +/* ============================================================ + PANIC — the short track + ============================================================ + Coherence is the long one: 0-10, campaign-scale, what it costs to keep looking once + you know what you are looking at, recovered at a debrief. Panic is the other half and + they must not be confused at the table: + + Coherence what knowing has cost you weeks 0-10, down + Panic what is happening to you NOW minutes 0-5, up + + Panic goes UP. A trigger gives a point with no roll — asking for a die to find out + whether you are frightened is a die that slows the scene down and tells the player + how to feel. The roll is to get RID of it, which is the interesting direction and the + only one worth a check. + + One free point on purpose: at Panic 1 you are rattled and roll at no penalty. It costs + the table nothing and it means the first shock of a scene is colour rather than + arithmetic. + ============================================================ */ + +export const PANIC_MAX = 5; + +export const PANIC_BANDS = [ + { min: 5, id: "broken", label: "RINGBRP.Panic.Broken", all: -20, breaks: true, + note: "RINGBRP.Panic.BrokenNote" }, + { min: 4, id: "overwhelmed", label: "RINGBRP.Panic.Overwhelmed", all: -20, breaks: false, + note: "RINGBRP.Panic.OverwhelmedNote" }, + { min: 3, id: "losing", label: "RINGBRP.Panic.Losing", all: -10, breaks: false, + note: "RINGBRP.Panic.LosingNote" }, + { min: 2, id: "shaken", label: "RINGBRP.Panic.Shaken", all: -5, breaks: false, + note: "RINGBRP.Panic.ShakenNote" }, + { min: 1, id: "rattled", label: "RINGBRP.Panic.Rattled", all: 0, breaks: false, + note: "RINGBRP.Panic.RattledNote" }, + { min: 0, id: "steady", label: "RINGBRP.Panic.Steady", all: 0, breaks: false, + note: "RINGBRP.Panic.SteadyNote" } +]; + +/** What a point costs. One each, so a GM never has to weigh anything mid-scene. */ +export const PANIC_TRIGGERS = { + wrongThing: { cost: 1, label: "RINGBRP.Panic.Trig.WrongThing" }, + touchedByIt: { cost: 1, label: "RINGBRP.Panic.Trig.Touched" }, + aBody: { cost: 1, label: "RINGBRP.Panic.Trig.Body" }, + someoneYouKnew:{ cost: 2, label: "RINGBRP.Panic.Trig.Known" }, + theLightsWent: { cost: 1, label: "RINGBRP.Panic.Trig.Dark" }, + majorWound: { cost: 1, label: "RINGBRP.Panic.Trig.Wounded" }, + itSaidYourName:{ cost: 2, label: "RINGBRP.Panic.Trig.Named" } +}; + +/** + * What takes a point off, and what it is rolled against. + * + * `self` steadies you; `other` steadies somebody else, which is the whole reason + * Psychology is worth carrying. `heard` reaches everyone who can hear it: naming a thing + * correctly is the department's actual work, and it is worth a mechanical reward. + * A special takes two. + */ +export const PANIC_RELIEF = { + steadyYourself: { skill: null, reach: "self", once: "scene", + label: "RINGBRP.Panic.Relief.Steady" }, + religion: { skill: "religion", reach: "self", once: "scene", + label: "RINGBRP.Panic.Relief.Religion" }, + psychology: { skill: "psychology", reach: "other", once: "scene", + label: "RINGBRP.Panic.Relief.Psychology" }, + occult: { skill: "occult", reach: "heard", once: "encounter", + label: "RINGBRP.Panic.Relief.Occult" }, + anchorObject: { skill: null, reach: "self", once: "case", + label: "RINGBRP.Panic.Relief.Anchor" } +}; + +/** Breaking is a choice, not a table: three bad options, taken fast. */ +export const PANIC_BREAK = ["freeze", "flee", "fixate"]; + +/** Breaking spends the panic. You come back at 3, not at 0 — it is not a rest. */ +export const PANIC_AFTER_BREAK = 3; + +export function panicBandFrom(value) { + const v = Math.max(0, Math.min(PANIC_MAX, Number(value) || 0)); + return PANIC_BANDS.find(b => v >= b.min) ?? PANIC_BANDS.at(-1); +} + +/** The roll penalty a level of panic carries. Never positive. */ +export function panicPenaltyFor(value) { + return Math.min(0, panicBandFrom(value).all); +} + +/** Does this level break the character this round? */ +export function panicBreaksAt(value) { + return panicBandFrom(value).breaks === true; +} + +/** Clamped, so nothing can push the track past its own ends. */ +export function panicAfter(value, delta) { + return Math.max(0, Math.min(PANIC_MAX, (Number(value) || 0) + (Number(delta) || 0))); +} + +/** A success takes one point, a special or better takes two. */ +export function panicReliefFor(level) { + if (level === "critical" || level === "special") return 2; + if (level === "success") return 1; + return 0; +} + export function coherenceBandFrom(value) { const v = Math.max(0, Number(value) || 0); return COHERENCE_BANDS.find(b => v >= b.min) ?? COHERENCE_BANDS.at(-1); diff --git a/styles/ringbrp.css b/styles/ringbrp.css index 2b1e9a4..6e816da 100644 --- a/styles/ringbrp.css +++ b/styles/ringbrp.css @@ -1788,3 +1788,46 @@ #pause.paused::after { animation: none; } #pause.paused img { transform: rotate(-32.7deg); } } + +/* ============================================================ + PANIC — the short track + ============================================================ + Deliberately NOT the same colour as Coherence. The two tracks run in opposite + directions on different timescales, and a table that reads one as the other will make + the wrong decision at the worst moment: Coherence is cool and yellow like the rest of + the department, Panic warms as it climbs and ends red. + ============================================================ */ + +.rb-vital-panic .rb-big { color: var(--rb-white); } +.rb-panic-steady .rb-big, .rb-panic-steady .rb-cohband { color: var(--rb-muted); } +.rb-panic-rattled .rb-big, .rb-panic-rattled .rb-cohband { color: var(--rb-white); } +.rb-panic-shaken .rb-big, .rb-panic-shaken .rb-cohband { color: var(--rb-yellow); } +.rb-panic-losing .rb-big, .rb-panic-losing .rb-cohband { color: #ffb066; } +.rb-panic-overwhelmed .rb-big, .rb-panic-overwhelmed .rb-cohband { color: var(--rb-danger); } +.rb-panic-broken .rb-big, .rb-panic-broken .rb-cohband { color: var(--rb-danger); } +.rb-vital-panic.rb-panic-broken { border-color: var(--rb-danger); } + +.rb-panic-pips { display: flex; gap: 5px; list-style: none; margin: 8px 0; padding: 0; } +.rb-panic-pip { + width: 20px; height: 8px; + background: transparent; border: 1px solid var(--rb-hairline); +} +.rb-panic-pip.on { background: var(--rb-danger); border-color: var(--rb-danger); } + +.rb-panic-move { display: flex; align-items: baseline; gap: 8px; margin: 6px 0; } +.rb-panic-from, .rb-panic-to { + font-family: var(--rb-display); font-size: 20px; color: var(--rb-white); +} +.rb-panic-arrow { color: var(--rb-muted); } +.rb-panic-band { + font-family: var(--rb-body); font-size: 11px; font-weight: 700; + letter-spacing: .1em; text-transform: uppercase; color: var(--rb-yellow); +} +.rb-panic-reason { font-size: 13px; color: var(--rb-white); margin: 4px 0; } +.rb-panic-note { font-size: 12px; color: var(--rb-muted); margin: 2px 0 6px; } +.rb-panic-pen { font-size: 13px; font-weight: 700; color: var(--rb-danger); margin: 2px 0; } +.rb-panic-break { + font-family: var(--rb-body); font-size: 12px; font-weight: 700; letter-spacing: .08em; + color: var(--rb-void); background: var(--rb-danger); + padding: 5px 8px; margin: 6px 0 0; +} diff --git a/system.json b/system.json index eefd591..69f426c 100644 --- a/system.json +++ b/system.json @@ -2,7 +2,7 @@ "id": "ringbrp", "title": "The Custodians", "description": "A Basic Roleplaying game of agents who cross the crossing to contain what should not be there — and who come back a little less from here each time.", - "version": "1.0.7", + "version": "1.1.0", "compatibility": { "minimum": 13, "verified": "14.364" diff --git a/template.json b/template.json index bc9a91f..4a68026 100644 --- a/template.json +++ b/template.json @@ -69,6 +69,11 @@ "value": 10, "max": 10, "drift": [] + }, + "panic": { + "value": 0, + "max": 5, + "log": [] } } }, diff --git a/templates/character-sheet.hbs b/templates/character-sheet.hbs index da31033..58b4ebf 100644 --- a/templates/character-sheet.hbs +++ b/templates/character-sheet.hbs @@ -51,6 +51,16 @@ {{localize coherence.band.label}} +
{{reason}}{{#if note}} — {{note}}{{/if}}
+{{localize band.note}}
+ + {{#if penalty}}{{penalty}}% {{localize "RINGBRP.Chat.ToEverything"}}
{{/if}} + {{#if breaks}}{{localize "RINGBRP.Panic.Breaks"}}
{{/if}} +Coherence is what knowing has cost you, and it is measured in weeks. Panic is +what is happening to you now, and it is measured in minutes. Do not confuse them at +the table: one goes down over a career, the other goes up over a corridor.
+ +Panic runs from 0 up to ${PANIC_MAX}. You take a point when +something happens to you — no roll. Asking for a die to find out +whether you are frightened slows the scene down and tells you how to feel. The die is +for getting rid of it.
+ +${table(["Panic", "Band", "Penalty", "What it looks like"], panicRows)} + +The first point is free. At Panic 1 you are rattled and you roll at no penalty, +because the opening shock of a scene should be colour rather than arithmetic.
+ +Roll it. A success takes one point off; a special or better takes two. +A failure takes nothing and costs nothing but the moment you spent on it.
+${table(["How", "Reaches", "How often"], panicReliefRows)} + +Occult is the odd one. Naming a thing correctly, out loud, helps +everybody who can hear you — which is the department's actual work, and the only relief +that reaches a whole room. Psychology is the only one that steadies +somebody who is not you. Religion steadies you and nobody else, and it +does not care whether you are right.
+ +At ${PANIC_MAX} you break. Choose one — ${PANIC_BREAK.join(", ")} — +take it, and come back at ${PANIC_AFTER_BREAK}. You do not come back at +zero. Breaking is not a rest.
+ +Panic clears when the scene does. When you are somewhere safe with the door shut and +somebody has made tea, you are at nought again.
` }, + { name: "5 · The Quiet Arts", content: `Some agents come back with something. It is not a gift and it is not training — it is the crossing, left in a person.