PURPOSE
Renders the compact rounded “damage-tag pill” — a small labeled badge that marks a card or weapon entry with its damage type (bullet, energy, fire, bomb). One consistent pill shape is used across every reward card and weapon list so the player recognizes damage type at a glance. Also exposes a solid accent color for non-pill UI that just needs a tag color swatch.
OWNS
- The damage-tag visual identity: gradient palette (
TAG_GRADIENT) and solid accent palette (TAG_SOLID), keyed byDamageTag. - Pill geometry constants at
uiScale = 1:PILL_HEIGHT_BASE,PILL_PADDING_BASE,FONT_SIZE_BASE. - The pill path geometry helper
_pillPath(rounded rectangle clamped so the corner radius never exceeds half the height/width). - The pill render itself: gradient fill, thin black border, white label with black stroke outline, font set to
700 <size>px 'Space Grotesk',sans-serif, label uppercased. - Explicit canvas shadow reset inside
ctx.save()/restore()so the pill never inherits a drop shadow from the surrounding card render.
READS FROM
DamageTagtype (../../data/weapons/_types) — the discriminator that keys both palettes.- The caller’s
CanvasRenderingContext2Dfor measuring label width and drawing. - The caller-provided
uiScalemultiplier (scales height, padding, font size, border line width, and label stroke width).
PUSHES TO
- The caller’s canvas: gradient-filled rounded pill, border stroke, stroked-then-filled label centered at
(cx, cy + 0.5). - Returns
{ width, height }measured in scaled pixels so callers can lay out adjacent elements (icon, name, stats) next to the pill.
DOES NOT
- Does not own layout: the caller picks
(cx, cy)anduiScale; the pill draws centered on that anchor and reports its size back. - Does not cache gradients, paths, or measured widths — every call rebuilds the linear gradient and re-measures the label.
- Does not clip, translate, or scale the caller’s transform — only saves/restores shadow and text/stroke state.
- Does not pick the damage tag — the caller passes it in; the function assumes the tag exists in both palette records.
- Does not handle missing/unknown tags defensively (a tag absent from
TAG_GRADIENTwould crash on the gradient lookup — by-design strict on internal data). - Does not draw icons or any text other than the uppercased tag name.
Signals
- Pure render module. No events emitted, no store subscriptions, no telemetry.
damageTagColor(tag)is the read-only accent-color signal for other UI (returns a hex string fromTAG_SOLID).
Entry points
damageTagColor(tag: DamageTag): string— solid hex color for a tag, matched to the pill’s top gradient stop so pill and non-pill UI read consistently.drawDamageTagPill(ctx, cx, cy, tag, uiScale): { width, height }— draws the pill centered at(cx, cy)and returns its rendered size in scaled pixels.
Pattern notes
- Two palettes deliberately decoupled:
TAG_GRADIENT(top/bottom stops, drives the pill fill) andTAG_SOLID(single color, drives non-pill swatches).TAG_SOLIDis documented as matching eachTAG_GRADIENT.topso the two surfaces stay visually aligned. - Gradient is built in screen-space (
createLinearGradient(0, y, 0, y + h)) — vertical top-to-bottom, no transform-awareness. - Corner radius is set to
h / 2(full-pill rounding), and_pillPathclamps further tomin(r, h/2, w/2)so very narrow pills degrade gracefully. - Label is drawn twice: a black stroke pass (
lineWidth = max(2, 2 * uiScale),lineJoin = 'round',miterLimit = 2) followed by a white fill pass — gives the readable outline rule the game uses for all on-canvas text. - All sizing is multiplicative on
uiScale;lineWidthfor the border usesMath.max(1, uiScale)to guarantee at least one pixel on small scales. - The explicit shadow reset (
shadowColor='transparent',shadowBlur=0, both offsets0) insidesave/restoreis the historical fix called out in the file header — without it, the pill inherits a card’s drop shadow and the label blurs. - Constants are intentionally named with
_BASEsuffix to flag that they are pre-uiScalevalues.