import { Graphics } from 'pixi.js'; /** Matches `.health-bar`/`.health-bar__fill` in App.css. */ const WIDTH = 28; const HEIGHT = 5; const BORDER_WIDTH = 1; // The fill sits inside the border's own inner edge, not flush with the bar's outer bounds — Pixi // centers a stroke on its path by default (half the stroke width paints inward), so a fill drawn // flush to (0,0)-(WIDTH,HEIGHT) would paint over that inward half on the top/bottom/left edges, // visibly overlapping the border. The original CSS version didn't have this problem: `.health- // bar__fill` was a child div sized to the *parent's content box*, which excludes its border by // definition. const INNER_WIDTH = WIDTH - BORDER_WIDTH * 2; const INNER_HEIGHT = HEIGHT - BORDER_WIDTH * 2; export function createHealthBar(): Graphics { return new Graphics(); } /** Redraws a health bar's background/border/fill for the given hp ratio (0-1). Position (the * bottom-center anchor CSS did via `transform: translate(-50%, -100%)`) is the caller's job — * see setHealthBarPosition. */ export function updateHealthBar(g: Graphics, ratio: number): void { const clamped = Math.max(0, Math.min(1, ratio)); g.clear(); g.rect(0, 0, WIDTH, HEIGHT) .fill({ color: 0x000000, alpha: 0.5 }) .stroke({ width: BORDER_WIDTH, color: 0x1b1611 }); if (clamped > 0) g.rect(BORDER_WIDTH, BORDER_WIDTH, INNER_WIDTH * clamped, INNER_HEIGHT).fill(0x4caf50); } /** Positions a health bar so (x, yAboveSprite) is its bottom-center, same anchor as the original * CSS transform. Rounded to the nearest whole world-pixel: a vector shape rasterized (without * antialiasing — see PixelPerfectWorld) at a fractional position can flip a full pixel row in or * out depending on exactly where the fraction falls, and `yAboveSprite` tracks a jumping rabbit's * continuously-changing height — left unrounded, the bar's fill visibly "jumps" a pixel in height * frame to frame for as long as the jump lasts (most noticeable there specifically, since the * position — and so the sub-pixel fraction — is otherwise constant while idle). */ export function setHealthBarPosition(g: Graphics, x: number, yAboveSprite: number): void { g.position.set(Math.round(x - WIDTH / 2), Math.round(yAboveSprite - HEIGHT)); }