Add stat×5 percentile to all character sheets (all three scenarios)

StatBlock updated in both BRP (v4) and Stormbringer build scripts:
- Header band now 20pt (was 12pt), split into two zones
- Upper zone: stat label (STR, CON, SIZ etc.) in white/gold
- Thin accent divider line
- Lower zone: stat×5 percentile ('55%', '80%' etc.) same style
- Main stat value in light cell unchanged
- Page budget: +8pt per character, well within buffer on all sheets

Affects all six characters in all three scenarios:
  Night Crawler, Day One, Stormbringer Kelen's Pact
This commit is contained in:
ChaosiumCon Prep
2026-05-18 10:09:16 +00:00
parent c214c1d5b9
commit 0bc8a002f5
2 changed files with 69 additions and 25 deletions
+35 -17
View File
@@ -271,31 +271,49 @@ class CharHeader(Flowable):
c.saveState(); c.translate(cx,cy); c.rotate(45); c.rect(-2,-2,4,4,fill=1,stroke=0); c.restoreState()
class StatBlock(Flowable):
"""Seven characteristic boxes + dark derived-stats strip below."""
"""Seven characteristic boxes + dark derived-stats strip below.
Header band split: label (top) · thin rule · stat×5% (bottom)."""
def __init__(self, stats, derived, width, t):
super().__init__(); self._s=stats; self._d=derived; self.width=width; self._t=t
self.height = 62 # 36 (boxes) + 14 (derived strip) + 12 (padding)
self.height = 70 # was 62 — +8pt for expanded header
def wrap(self,a,b): return (self.width,self.height)
def draw(self):
c=self.canv; t=self._t; w=self.width; n=len(self._s); bw=(w-4)/n
BOX_H = 36
# Characteristic boxes
BOX_H = 44 # was 36 — header now 20pt instead of 12pt
HDR_H = 20
for i,(label,val) in enumerate(self._s):
x=i*bw+2
# Dark header row inside box
c.setFillColor(t.stat_hdr_bg); c.rect(x,BOX_H-12,bw-1,12,fill=1,stroke=0)
# Light value cell
c.setFillColor(t.stat_cell_bg); c.rect(x,14,bw-1,BOX_H-12,fill=1,stroke=0)
# Border
c.setStrokeColor(t.rule); c.setLineWidth(0.5); c.rect(x,14,bw-1,22,fill=0,stroke=1)
# Label (in dark header)
c.setFillColor(t.stat_hdr_text); c.setFont('Helvetica-Bold',7)
c.drawCentredString(x+(bw-1)/2,BOX_H-9,label)
# Value (in light cell)
c.setFillColor(t.stat_cell_text); c.setFont(t.font_head,14)
c.drawCentredString(x+(bw-1)/2,17,str(val))
try: pct_str = f"{int(val)*5}%"
except: pct_str = ''
# Dark derived stats strip
# Dark header (20pt)
c.setFillColor(t.stat_hdr_bg)
c.rect(x, BOX_H-HDR_H, bw-1, HDR_H, fill=1, stroke=0)
# Light value cell (same 10pt as before)
c.setFillColor(t.stat_cell_bg)
c.rect(x, 14, bw-1, BOX_H-HDR_H-14, fill=1, stroke=0)
# Full box border
c.setStrokeColor(t.rule); c.setLineWidth(0.5)
c.rect(x, 14, bw-1, BOX_H-14, fill=0, stroke=1)
# Thin divider inside header
c.setStrokeColor(t.accent); c.setLineWidth(0.3)
c.line(x+3, BOX_H-10, x+bw-4, BOX_H-10)
# Label — upper header band
c.setFillColor(t.stat_hdr_text); c.setFont('Helvetica-Bold', 7)
c.drawCentredString(x+(bw-1)/2, BOX_H-7, label)
# ×5 percentile — lower header band
if pct_str:
c.setFillColor(t.stat_hdr_text); c.setFont('Helvetica-Bold', 6.5)
c.drawCentredString(x+(bw-1)/2, BOX_H-18, pct_str)
# Main stat value — light cell (unchanged position)
c.setFillColor(t.stat_cell_text); c.setFont(t.font_head, 14)
c.drawCentredString(x+(bw-1)/2, 17, str(val))
# Dark derived stats strip (unchanged)
c.setFillColor(t.derived_bg); c.rect(0,0,w,13,fill=1,stroke=0)
c.setStrokeColor(t.rule); c.setLineWidth(0.3); c.rect(0,0,w,13,fill=0,stroke=1)
x_pos=6
@@ -107,21 +107,47 @@ class CharacterHeader(Flowable):
c.rect(-2,-2,4,4,fill=1,stroke=0); c.restoreState()
class StatBlock(Flowable):
"""Characteristic boxes with label (top) · divider · stat×5% (bottom) in header."""
def __init__(self, stats, derived, width):
super().__init__()
self._s=stats; self._d=derived; self.width=width; self.height=56
self._s=stats; self._d=derived; self.width=width; self.height=64 # was 56 +8pt
def wrap(self, aW, aH): return (self.width, self.height)
def draw(self):
c=self.canv; w=self.width; n=len(self._s); bw=(w-4)/n
BOX_TOP = 42 # was 34 — header now 20pt
HDR_H = 20 # was 12
for i,(label,val) in enumerate(self._s):
x=i*bw+2
c.setFillColor(STAT_HEADER); c.rect(x,22,bw-1,12,fill=1,stroke=0)
c.setFillColor(PARCHMENT_DARK); c.rect(x,8,bw-1,14,fill=1,stroke=0)
c.setStrokeColor(RULE_COLOR); c.setLineWidth(0.5); c.rect(x,8,bw-1,26,fill=0,stroke=1)
c.setFillColor(GOLD_LIGHT); c.setFont('Helvetica-Bold',7)
c.drawCentredString(x+(bw-1)/2,28,label)
c.setFillColor(INK); c.setFont('Times-Bold',13)
c.drawCentredString(x+(bw-1)/2,11,str(val))
try: pct_str = f"{int(val)*5}%"
except: pct_str = ''
# Dark header (20pt)
c.setFillColor(STAT_HEADER)
c.rect(x, BOX_TOP-HDR_H, bw-1, HDR_H, fill=1, stroke=0)
# Light value cell (same 14pt as before)
c.setFillColor(PARCHMENT_DARK)
c.rect(x, 8, bw-1, BOX_TOP-HDR_H-8, fill=1, stroke=0)
# Full box border
c.setStrokeColor(RULE_COLOR); c.setLineWidth(0.5)
c.rect(x, 8, bw-1, BOX_TOP-8, fill=0, stroke=1)
# Thin gold divider inside header
c.setStrokeColor(GOLD_LIGHT); c.setLineWidth(0.3)
c.line(x+3, BOX_TOP-10, x+bw-4, BOX_TOP-10)
# Label — upper header band
c.setFillColor(GOLD_LIGHT); c.setFont('Helvetica-Bold', 7)
c.drawCentredString(x+(bw-1)/2, BOX_TOP-7, label)
# ×5 percentile — lower header band
if pct_str:
c.setFillColor(GOLD_LIGHT); c.setFont('Helvetica-Bold', 6.5)
c.drawCentredString(x+(bw-1)/2, BOX_TOP-18, pct_str)
# Main stat value — light cell (unchanged)
c.setFillColor(INK); c.setFont('Times-Bold', 13)
c.drawCentredString(x+(bw-1)/2, 11, str(val))
x_pos=2
for lbl,val in self._d:
c.setFillColor(CRIMSON); c.setFont('Helvetica-Bold',7.5)