diff --git a/docs/REVIEW_LOG.md b/docs/REVIEW_LOG.md index a2894d8..413007d 100644 --- a/docs/REVIEW_LOG.md +++ b/docs/REVIEW_LOG.md @@ -4617,3 +4617,39 @@ two defects found while building it. Worth noting for anyone issuing shields: a riot shield is `torsoArms`, and in melee **legs and head are 10 of 20**. A shield is not a suit of armour and this fight is what that sentence costs. + +## Riot shields for Containment and the Night Warden + +Both postings now draw a **riot shield** from stores, and both are trained to use it. + +Issuing a shield without the skill would have been R-212 again with a different item — +the Antiquities Officer who drew a flintlock and rolled 5% with it — so +`melee_weapon:shield` goes into each posting's support list. It is **swapped in, not +added**: keeping five core and five support means the shield arrives without a free +power bump, which is not what was asked for. + +* **Containment** drops `stealth`. A posting whose blurb is "goes through the door, + holds the corridor" was always the least stealthy thing in the department. +* **Night Warden** drops `navigate`. They sit with one site all night; they are not + going anywhere. + +**check-kits now covers shields.** The guard has always required a posting to be +trained in the weapons it carries, and a shield is armour you defend *with*, so it +needs its skill for exactly the same reason. Verified by removing the skill from the +Warden and watching the build refuse: + + check-kits: FAILED + warden (Night Warden): issued Riot shield, trains no melee_weapon:shield + +**What it does to the two postings.** Four generated agents, shield skill landing at +25–30% (the support tier), and none of them encumbered — though a Containment officer +came out at 32.9 of 33 kg, which is as close to the line as this kit should get. + +| | slung | raised | +|---|---|---| +| Containment (breaching suit, `all` 7) | 7 everywhere | torso and arms **10**, legs and head 7 | +| Night Warden (stab vest, `torso` 2) | torso 2, all else 0 | torso **5**, arms **3**, legs and head 0 | + +The shield ships **slung**. Raising it is a choice made at the table, which is the whole +design — and for the Warden it is the difference between two points of armour and a +real one. diff --git a/packs/armour/LOG b/packs/armour/LOG index ff9cf85..2e84cf9 100644 --- a/packs/armour/LOG +++ b/packs/armour/LOG @@ -1 +1 @@ -2026/08/11-18:22:31.919810 17373b000 Delete type=3 #1 +2026/08/11-21:36:45.974918 171407000 Delete type=3 #1 diff --git a/packs/expedition/LOG b/packs/expedition/LOG index 0861364..26d962d 100644 --- a/packs/expedition/LOG +++ b/packs/expedition/LOG @@ -1 +1 @@ -2026/08/11-18:22:31.954955 173f47000 Delete type=3 #1 +2026/08/11-21:36:46.010315 1703ef000 Delete type=3 #1 diff --git a/packs/gear/LOG b/packs/gear/LOG index 90a702f..d9f5564 100644 --- a/packs/gear/LOG +++ b/packs/gear/LOG @@ -1 +1 @@ -2026/08/11-18:22:31.921916 172723000 Delete type=3 #1 +2026/08/11-21:36:45.977183 171407000 Delete type=3 #1 diff --git a/packs/npcs/LOG b/packs/npcs/LOG index b32283b..7615028 100644 --- a/packs/npcs/LOG +++ b/packs/npcs/LOG @@ -1 +1 @@ -2026/08/11-18:22:31.938436 172723000 Delete type=3 #1 +2026/08/11-21:36:45.995509 171c13000 Delete type=3 #1 diff --git a/packs/powers/LOG b/packs/powers/LOG index 5b533e1..d86c829 100644 --- a/packs/powers/LOG +++ b/packs/powers/LOG @@ -1 +1 @@ -2026/08/11-18:22:31.946701 172723000 Delete type=3 #1 +2026/08/11-21:36:46.003934 1703ef000 Delete type=3 #1 diff --git a/packs/pregens/LOG b/packs/pregens/LOG index 8380368..096f735 100644 --- a/packs/pregens/LOG +++ b/packs/pregens/LOG @@ -1 +1 @@ -2026/08/11-18:22:31.928648 172723000 Delete type=3 #1 +2026/08/11-21:36:45.985591 171c13000 Delete type=3 #1 diff --git a/packs/rules/LOG b/packs/rules/LOG index 2e749bc..f335a61 100644 --- a/packs/rules/LOG +++ b/packs/rules/LOG @@ -1 +1 @@ -2026/08/11-18:22:31.951456 172f2f000 Delete type=3 #1 +2026/08/11-21:36:46.008640 171c13000 Delete type=3 #1 diff --git a/packs/skills/LOG b/packs/skills/LOG index 221a782..1bb1c63 100644 --- a/packs/skills/LOG +++ b/packs/skills/LOG @@ -1 +1 @@ -2026/08/11-18:22:31.912459 172f2f000 Delete type=3 #1 +2026/08/11-21:36:45.965730 171c13000 Delete type=3 #1 diff --git a/packs/slice/LOG b/packs/slice/LOG index 8638976..373fe2b 100644 --- a/packs/slice/LOG +++ b/packs/slice/LOG @@ -1 +1 @@ -2026/08/11-18:22:31.945043 17373b000 Delete type=3 #1 +2026/08/11-21:36:46.002345 1703ef000 Delete type=3 #1 diff --git a/packs/tables/LOG b/packs/tables/LOG index 6590e5d..b9229d0 100644 --- a/packs/tables/LOG +++ b/packs/tables/LOG @@ -1 +1 @@ -2026/08/11-18:22:31.943087 17373b000 Delete type=3 #1 +2026/08/11-21:36:46.000570 170bfb000 Delete type=3 #1 diff --git a/packs/talents/LOG b/packs/talents/LOG index 23ce093..5167af1 100644 --- a/packs/talents/LOG +++ b/packs/talents/LOG @@ -1 +1 @@ -2026/08/11-18:22:31.915613 173f47000 Delete type=3 #1 +2026/08/11-21:36:45.970370 1703ef000 Delete type=3 #1 diff --git a/packs/vehicles/LOG b/packs/vehicles/LOG index 924b9a0..088f352 100644 --- a/packs/vehicles/LOG +++ b/packs/vehicles/LOG @@ -1 +1 @@ -2026/08/11-18:22:31.924112 172723000 Delete type=3 #1 +2026/08/11-21:36:45.980477 171c13000 Delete type=3 #1 diff --git a/packs/weapons/LOG b/packs/weapons/LOG index 6ff2401..153a60d 100644 --- a/packs/weapons/LOG +++ b/packs/weapons/LOG @@ -1 +1 @@ -2026/08/11-18:22:31.917625 172f2f000 Delete type=3 #1 +2026/08/11-21:36:45.972739 171c13000 Delete type=3 #1 diff --git a/ringbrp.mjs b/ringbrp.mjs index 75ebeec..fd758e9 100644 --- a/ringbrp.mjs +++ b/ringbrp.mjs @@ -3774,9 +3774,9 @@ export const ROLES = { heavy: { label: "Containment", blurb: "Containment. Goes through the door, holds the corridor, and puts things in boxes that do not want to be in boxes.", stat: "str", core: ["firearm:rifle","brawl","melee_weapon:prybar","athletics","dodge"], - support: ["demolition","first_aid","repair:mechanical","spot","stealth"], + support: ["demolition","first_aid","repair:mechanical","spot","melee_weapon:shield"], talents: ["hard_landing","chain_custody","first_through","clean_scene"], - kit: ["rifle_med","breach_suit","prybar","restraints","climbing_kit", "bolt_croppers", "cordon_kit", "ear_defenders"] }, + kit: ["rifle_med","breach_suit","prybar","riot_shield","restraints","climbing_kit", "bolt_croppers", "cordon_kit", "ear_defenders"] }, /* --------------------------------------------------------------- The rest of the establishment. @@ -3820,9 +3820,9 @@ export const ROLES = { warden: { label: "Night Warden", blurb: "Sits with a site overnight so that nobody else has to. Listens, watches, and writes down the times.", stat: "con", core: ["listen","spot","stealth","dodge","brawl"], - support: ["first_aid","navigate","athletics","melee_weapon:club","anomaly_lore"], + support: ["first_aid","melee_weapon:shield","athletics","melee_weapon:club","anomaly_lore"], talents: ["unblinking","out_of_step","first_through","clean_scene"], - kit: ["oil_lantern","ward","cudgel","stab_vest","torch", "vacuum_flask", "stopwatch", "blanket"] }, + kit: ["oil_lantern","ward","cudgel","riot_shield","stab_vest","torch", "vacuum_flask", "stopwatch", "blanket"] }, stores: { label: "Stores Officer", blurb: "Requisition, custody and the paperwork that moves materiel. Can get you a thing the department says it does not have.", stat: "edu", diff --git a/tools/check-kits.mjs b/tools/check-kits.mjs index 8c58120..ebc0c0f 100644 --- a/tools/check-kits.mjs +++ b/tools/check-kits.mjs @@ -10,8 +10,10 @@ */ import { readFile } from "node:fs/promises"; import { SKILL_CATALOGUE, TALENTS, GEAR, WEAPONS, ARMOURS, DEVICES } from "./content.mjs"; +import { SHIELD } from "../rules.mjs"; const WEAPON_BY_KEY = new Map(WEAPONS.map(w => [w.key, w])); +const ARMOUR_BY_KEY = new Map(ARMOURS.map(a => [a.key, a])); const src = await readFile(new URL("../ringbrp.mjs", import.meta.url), "utf8"); const ROLES = eval("(" + src.match(/export const ROLES = (\{[\s\S]*?\n\});/)[1] + ")"); @@ -44,6 +46,17 @@ for (const [id, r] of Object.entries(ROLES)) { const need = w.spec ? `${w.fam}:${w.spec}` : w.fam; if (!trained.has(need)) problems.push(`${where}: issued ${w.name}, trains no ${need}`); } + // A shield is armour you defend WITH, so it needs its skill for exactly the same + // reason a weapon does — a posting issued one and untrained in it parries at the + // 5% base and would be better off with both hands free. + const shieldSkill = `${SHIELD.parryFamily}:${SHIELD.parrySpec}`; + for (const k of r.kit) { + const a = ARMOUR_BY_KEY.get(k); + if (!a?.shield) continue; + if (!trained.has(shieldSkill)) { + problems.push(`${where}: issued ${a.name}, trains no ${shieldSkill}`); + } + } const sig = JSON.stringify([...r.kit].sort()); if (kitSets.has(sig)) problems.push(`${where}: identical loadout to ${kitSets.get(sig)}`); kitSets.set(sig, id);