Shields: armour you raise, and the only armour you can defend with

A shield is the one piece of armour you hold, and it protects you exactly as much as
you are currently holding it up. Raised, its points apply to what it covers and it can
parry on a new Shield skill. Lowered, it is weight. The price of keeping it up is that
it occupies a hand and you are shooting around it: -20% to your own ranged attacks,
printed on the card like everything else. Only one shield goes up at a time.

Seven of them, 2-point buckler to 6-point ballistic shield; the pavise and barrier
plate cover legs too because they are walls you carry.

`degradation` finally does something — it has been on every armour item since the packs
were first built and was read by nothing. A blow costs the shield a point of wear for
every step it stood above an ordinary success, so an ordinary hit costs nothing, a
special 1 and a critical 2. At its limit it comes apart, drops, and will not be raised
again.

Two defects found by driving the real functions rather than reading the diff, both
introduced by this change:

- Every shield defence quietly rolled DODGE. "shield" passed through
  defenceTypeAllowed unchanged, so neither parry branch matched and it fell through.
  Invisible in the card; it showed up as eight rolls at a supposed 85% returning six
  failures. Taking a blow on the shield IS a parry, so it is now a flag on a parry
  rather than a defence type of its own.
- The wear rule was unreachable: it fired only on a clean stop, but under graded
  defences only a critical fully stops a critical, so a shield would never have taken
  a scratch. Wear is for what the shield ABSORBED — dragging a critical down to a
  special is exactly when shields splinter.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
slaguru666
2026-08-11 18:25:30 +01:00
co-authored by Claude Opus 5
parent 016ea1c4ab
commit c61700566c
125 changed files with 419 additions and 102 deletions
+49
View File
@@ -4521,3 +4521,52 @@ on the first attempt and landed on the second, staunched the bleeding, healed 3,
`reviveIfHealed` took Plated out of the dying state without being asked. My harness
then called `tickDying` on a revived character and read `.state` off the null it
correctly returned — the rule was right and the probe was wrong.
## Shields — armour with a switch
A shield is the one piece of armour you *hold*, and it protects you exactly as much as
you are currently holding it up. Everything interesting follows from that switch
having a cost, so the design is: raised it is armour and a defence, lowered it is
weight, and the price of keeping it up is that it occupies a hand and you are shooting
around it.
* **Raised** — its points apply to what it covers, and it is the only armour you can
also defend with, on a new **Shield** skill.
* **Lowered** — nothing. Verified: heater shield slung reads 0 on every location,
raised reads 4 on torso and both arms and 0 on legs and head.
* **The cost** — −20% to your own ranged attacks while it is up, printed on
the card as plainly as anything else: *"Heater shield −20%"*. That is what stops
"always raised" being the only answer; it is obviously right for a containment
officer and obviously wrong for a marksman.
* **Only one goes up.** Raising a second lowers the first. Two shields is not a stance.
Seven of them, from a 2-point buckler to a 6-point ballistic shield, with the pavise
and the barrier plate covering legs as well because they are walls you carry.
**`degradation` finally does something.** It has been on every armour item since the
packs were first built and was read by nothing. Shields wear: a blow costs the shield a
point for every step the attack stood above an ordinary success, so an ordinary hit
costs it nothing, a special costs 1 and a critical costs 2. At its limit it comes apart
— no points, nothing to parry with, and it drops out of your hand and refuses to be
raised again.
### Two defects found by testing it rather than reading it
**R-244. Every shield defence quietly rolled Dodge.** I added `"shield"` as a third
defence type and passed it through `defenceTypeAllowed`, which returned it unchanged —
so `kind` was `"shield"`, neither of the `kind === "parry"` branches matched, and the
function fell through to the dodge branch. It was invisible in the chat card and only
showed up as a statistical smell: eight rolls at a supposed 85% produced six failures.
Taking a blow on the shield **is** a parry — it locks with parries and is not a dodge —
so the shield is a flag on a parry rather than a type of its own.
**R-245. The wear rule was unreachable.** I first wrote it to fire only when the shield
turned a blow aside *completely*. But under graded defences only a critical fully stops
a critical, so a shield would have gone its entire service life without a scratch. Wear
is for what the shield **absorbed**: a shield that drags a critical down to a special
has very much taken that blow, and that is exactly when shields splinter. Ten forced
criticals produced zero wear under the first rule; under the corrected one the shield
went 0 → 2 → 4 → broken in three absorbed blows.
Both were mine, both were introduced in this change, and both were found by driving the
real functions rather than by reading the diff.
+9
View File
@@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128" width="128" height="128">
<rect width="128" height="128" rx="20" fill="hsl(197 62% 34%)"/>
<rect x="5" y="5" width="118" height="118" rx="16" fill="none" stroke="hsl(197 80% 64%)" stroke-width="4"/>
<path d="M64 14l38 12v34c0 30-16 48-38 58-22-10-38-28-38-58V26z" fill="hsl(197 90% 88%)" stroke="none"/>
<path d="M64 14v104 M30 52h68" fill="none" stroke="hsl(197 70% 46%)" stroke-width="6" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 510 B

+9
View File
@@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128" width="128" height="128">
<rect width="128" height="128" rx="20" fill="hsl(249 62% 34%)"/>
<path d="M96 0H128v32z" fill="hsl(249 80% 64%)" opacity="0.55"/>
<rect x="5" y="5" width="118" height="118" rx="16" fill="none" stroke="hsl(249 80% 64%)" stroke-width="4"/>
<path d="M64 14l38 12v34c0 30-16 48-38 58-22-10-38-28-38-58V26z" fill="hsl(249 90% 88%)" stroke="none"/>
<path d="M64 14v104 M30 52h68" fill="none" stroke="hsl(249 70% 46%)" stroke-width="6" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12 102h104" stroke="hsl(249 80% 64%)" stroke-width="2" opacity="0.7"/>
</svg>

After

Width:  |  Height:  |  Size: 654 B

+9
View File
@@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128" width="128" height="128">
<rect width="128" height="128" rx="20" fill="hsl(227 38% 30%)"/>
<path d="M0 0h30L0 30z" fill="hsl(227 44% 22%)"/>
<rect x="5" y="5" width="118" height="118" rx="16" fill="none" stroke="hsl(227 80% 64%)" stroke-width="4"/>
<path d="M64 20a44 44 0 1 1 0 88 44 44 0 0 1 0-88z" fill="hsl(227 55% 82%)" stroke="none"/>
<path d="M64 50a14 14 0 1 1 0 28 14 14 0 0 1 0-28z" fill="hsl(227 40% 52%)" stroke="none"/>
</svg>

After

Width:  |  Height:  |  Size: 503 B

+9
View File
@@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128" width="128" height="128">
<rect width="128" height="128" rx="20" fill="hsl(211 38% 30%)"/>
<path d="M0 0h30L0 30z" fill="hsl(211 44% 22%)"/>
<rect x="5" y="5" width="118" height="118" rx="16" fill="none" stroke="hsl(211 80% 64%)" stroke-width="4"/>
<path d="M64 14l38 12v34c0 30-16 48-38 58-22-10-38-28-38-58V26z" fill="hsl(211 55% 82%)" stroke="none"/>
<path d="M64 14v104 M30 52h68" fill="none" stroke="hsl(211 40% 52%)" stroke-width="6" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 559 B

+9
View File
@@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128" width="128" height="128">
<rect width="128" height="128" rx="20" fill="hsl(179 38% 30%)"/>
<path d="M0 0h30L0 30z" fill="hsl(179 44% 22%)"/>
<rect x="5" y="5" width="118" height="118" rx="16" fill="none" stroke="hsl(179 80% 64%)" stroke-width="4"/>
<path d="M64 14l38 12v34c0 30-16 48-38 58-22-10-38-28-38-58V26z" fill="hsl(179 55% 82%)" stroke="none"/>
<path d="M64 14v104 M30 52h68" fill="none" stroke="hsl(179 40% 52%)" stroke-width="6" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 559 B

+9
View File
@@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128" width="128" height="128">
<rect width="128" height="128" rx="20" fill="hsl(235 62% 34%)"/>
<rect x="5" y="5" width="118" height="118" rx="16" fill="none" stroke="hsl(235 80% 64%)" stroke-width="4"/>
<path d="M64 14l38 12v34c0 30-16 48-38 58-22-10-38-28-38-58V26z" fill="hsl(235 90% 88%)" stroke="none"/>
<path d="M64 14v104 M30 52h68" fill="none" stroke="hsl(235 70% 46%)" stroke-width="6" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 510 B

+9
View File
@@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128" width="128" height="128">
<rect width="128" height="128" rx="20" fill="hsl(191 38% 30%)"/>
<path d="M0 0h30L0 30z" fill="hsl(191 44% 22%)"/>
<rect x="5" y="5" width="118" height="118" rx="16" fill="none" stroke="hsl(191 80% 64%)" stroke-width="4"/>
<path d="M64 20a44 44 0 1 1 0 88 44 44 0 0 1 0-88z" fill="hsl(191 55% 82%)" stroke="none"/>
<path d="M64 50a14 14 0 1 1 0 28 14 14 0 0 1 0-28z" fill="hsl(191 40% 52%)" stroke="none"/>
</svg>

After

Width:  |  Height:  |  Size: 503 B

+15 -1
View File
@@ -893,5 +893,19 @@
"RINGBRP.Coverage.head": "head",
"RINGBRP.Sheet.Unarmoured": "bare",
"RINGBRP.Sheet.ByLocation": "by location",
"RINGBRP.Item.Coverage": "Covers"
"RINGBRP.Item.Coverage": "Covers",
"RINGBRP.Shield.Raised": "raised — {n} points where it covers",
"RINGBRP.Shield.InTheWay": "raised — you are shooting around it ({mod}%)",
"RINGBRP.Shield.Wear": "{name} takes the blow — {n}/{max}",
"RINGBRP.Shield.Broken": "{name} comes apart. {n}/{max} — it is firewood.",
"RINGBRP.Sheet.RaiseHint": "Raise or lower this shield. Raised it protects what it covers and you can defend with it; lowered it is weight.",
"RINGBRP.Sheet.ShieldUp": "raised",
"RINGBRP.Sheet.ShieldDown": "slung",
"RINGBRP.Sheet.ShieldBroken": "broken",
"RINGBRP.Sheet.ShieldHint": "Take the blow on the shield. It wears against anything better than an ordinary hit.",
"RINGBRP.Notify.ShieldBroken": "{name} is broken. It cannot be raised.",
"RINGBRP.Item.IsShield": "Is a shield",
"RINGBRP.Item.Raised": "Raised",
"RINGBRP.Item.Wear": "Wear",
"RINGBRP.Item.WearMax": "Breaks at"
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1 -1
View File
@@ -1 +1 @@
MANIFEST-000004
MANIFEST-000002
+1 -5
View File
@@ -1,5 +1 @@
2026/08/11-10:31:15.815927 175f73000 Recovering log #3
2026/08/11-10:31:15.816054 175f73000 Level-0 table #5: started
2026/08/11-10:31:15.816169 175f73000 Level-0 table #5: 3399 bytes OK
2026/08/11-10:31:15.816387 175f73000 Delete type=0 #3
2026/08/11-10:31:15.816415 175f73000 Delete type=3 #2
2026/08/11-18:22:31.919810 17373b000 Delete type=3 #1
-1
View File
@@ -1 +0,0 @@
2026/08/11-10:29:47.321526 1714a3000 Delete type=3 #1
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1 -1
View File
@@ -1 +1 @@
MANIFEST-000004
MANIFEST-000002
+1 -5
View File
@@ -1,5 +1 @@
2026/08/11-10:31:15.827053 176f8b000 Recovering log #3
2026/08/11-10:31:15.827202 176f8b000 Level-0 table #5: started
2026/08/11-10:31:15.827356 176f8b000 Level-0 table #5: 4129 bytes OK
2026/08/11-10:31:15.827638 176f8b000 Delete type=0 #3
2026/08/11-10:31:15.827685 176f8b000 Delete type=3 #2
2026/08/11-18:22:31.954955 173f47000 Delete type=3 #1
-1
View File
@@ -1 +0,0 @@
2026/08/11-10:29:47.359914 170c97000 Delete type=3 #1
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1 -1
View File
@@ -1 +1 @@
MANIFEST-000004
MANIFEST-000002
+1 -5
View File
@@ -1,5 +1 @@
2026/08/11-10:31:15.817069 177797000 Recovering log #3
2026/08/11-10:31:15.817858 177797000 Level-0 table #5: started
2026/08/11-10:31:15.818239 177797000 Level-0 table #5: 31614 bytes OK
2026/08/11-10:31:15.818545 177797000 Delete type=0 #3
2026/08/11-10:31:15.818601 177797000 Delete type=3 #2
2026/08/11-18:22:31.921916 172723000 Delete type=3 #1
-1
View File
@@ -1 +0,0 @@
2026/08/11-10:29:47.323743 171caf000 Delete type=3 #1
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1 -1
View File
@@ -1 +1 @@
MANIFEST-000004
MANIFEST-000002
+1 -5
View File
@@ -1,5 +1 @@
2026/08/11-10:31:15.803065 177797000 Recovering log #3
2026/08/11-10:31:15.804714 177797000 Level-0 table #5: started
2026/08/11-10:31:15.806503 177797000 Level-0 table #5: 223539 bytes OK
2026/08/11-10:31:15.806843 177797000 Delete type=0 #3
2026/08/11-10:31:15.806889 177797000 Delete type=3 #2
2026/08/11-18:22:31.938436 172723000 Delete type=3 #1
-1
View File
@@ -1 +0,0 @@
2026/08/11-10:29:47.341619 170c97000 Delete type=3 #1
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1 -1
View File
@@ -1 +1 @@
MANIFEST-000004
MANIFEST-000002
+1 -5
View File
@@ -1,5 +1 @@
2026/08/11-10:31:15.812365 175f73000 Recovering log #3
2026/08/11-10:31:15.812699 175f73000 Level-0 table #5: started
2026/08/11-10:31:15.812860 175f73000 Level-0 table #5: 3295 bytes OK
2026/08/11-10:31:15.813123 175f73000 Delete type=0 #3
2026/08/11-10:31:15.813163 175f73000 Delete type=3 #2
2026/08/11-18:22:31.946701 172723000 Delete type=3 #1
-1
View File
@@ -1 +0,0 @@
2026/08/11-10:29:47.352685 170c97000 Delete type=3 #1
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1 -1
View File
@@ -1 +1 @@
MANIFEST-000004
MANIFEST-000002
+1 -5
View File
@@ -1,5 +1 @@
2026/08/11-10:31:15.799594 177797000 Recovering log #3
2026/08/11-10:31:15.800464 177797000 Level-0 table #5: started
2026/08/11-10:31:15.800988 177797000 Level-0 table #5: 58133 bytes OK
2026/08/11-10:31:15.801383 177797000 Delete type=0 #3
2026/08/11-10:31:15.801428 177797000 Delete type=3 #2
2026/08/11-18:22:31.928648 172723000 Delete type=3 #1
-1
View File
@@ -1 +0,0 @@
2026/08/11-10:29:47.331121 1724bb000 Delete type=3 #1
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1 -1
View File
@@ -1 +1 @@
MANIFEST-000004
MANIFEST-000002
+1 -5
View File
@@ -1,5 +1 @@
2026/08/11-10:31:15.822560 176f8b000 Recovering log #3
2026/08/11-10:31:15.823036 176f8b000 Level-0 table #5: started
2026/08/11-10:31:15.823351 176f8b000 Level-0 table #5: 30975 bytes OK
2026/08/11-10:31:15.823593 176f8b000 Delete type=0 #3
2026/08/11-10:31:15.823625 176f8b000 Delete type=3 #2
2026/08/11-18:22:31.951456 172f2f000 Delete type=3 #1
-1
View File
@@ -1 +0,0 @@
2026/08/11-10:29:47.357907 170c97000 Delete type=3 #1
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1 -1
View File
@@ -1 +1 @@
MANIFEST-000004
MANIFEST-000002
+1 -5
View File
@@ -1,5 +1 @@
2026/08/11-10:31:15.809540 175f73000 Recovering log #3
2026/08/11-10:31:15.809752 175f73000 Level-0 table #5: started
2026/08/11-10:31:15.809950 175f73000 Level-0 table #5: 7486 bytes OK
2026/08/11-10:31:15.810216 175f73000 Delete type=0 #3
2026/08/11-10:31:15.810251 175f73000 Delete type=3 #2
2026/08/11-18:22:31.912459 172f2f000 Delete type=3 #1
-1
View File
@@ -1 +0,0 @@
2026/08/11-10:29:47.314145 1724bb000 Delete type=3 #1
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1 -1
View File
@@ -1 +1 @@
MANIFEST-000004
MANIFEST-000002
+1 -5
View File
@@ -1,5 +1 @@
2026/08/11-10:31:15.824364 17677f000 Recovering log #3
2026/08/11-10:31:15.824536 17677f000 Level-0 table #5: started
2026/08/11-10:31:15.824835 17677f000 Level-0 table #5: 16892 bytes OK
2026/08/11-10:31:15.825060 17677f000 Delete type=0 #3
2026/08/11-10:31:15.825097 17677f000 Delete type=3 #2
2026/08/11-18:22:31.945043 17373b000 Delete type=3 #1
-1
View File
@@ -1 +0,0 @@
2026/08/11-10:29:47.350616 1714a3000 Delete type=3 #1
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1 -1
View File
@@ -1 +1 @@
MANIFEST-000004
MANIFEST-000002
+1 -5
View File
@@ -1,5 +1 @@
2026/08/11-10:31:15.825859 175f73000 Recovering log #3
2026/08/11-10:31:15.825973 175f73000 Level-0 table #5: started
2026/08/11-10:31:15.826082 175f73000 Level-0 table #5: 3739 bytes OK
2026/08/11-10:31:15.826284 175f73000 Delete type=0 #3
2026/08/11-10:31:15.826314 175f73000 Delete type=3 #2
2026/08/11-18:22:31.943087 17373b000 Delete type=3 #1
-1
View File
@@ -1 +0,0 @@
2026/08/11-10:29:47.347262 1714a3000 Delete type=3 #1
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1 -1
View File
@@ -1 +1 @@
MANIFEST-000004
MANIFEST-000002
+1 -5
View File
@@ -1,5 +1 @@
2026/08/11-10:31:15.810995 177797000 Recovering log #3
2026/08/11-10:31:15.811100 177797000 Level-0 table #5: started
2026/08/11-10:31:15.811254 177797000 Level-0 table #5: 5770 bytes OK
2026/08/11-10:31:15.811516 177797000 Delete type=0 #3
2026/08/11-10:31:15.811553 177797000 Delete type=3 #2
2026/08/11-18:22:31.915613 173f47000 Delete type=3 #1
-1
View File
@@ -1 +0,0 @@
2026/08/11-10:29:47.318325 1724bb000 Delete type=3 #1
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More