Sprites.load() now awaits img.decode() before a tier image counts as
ready, so every sprite is fully decoded during the title screen and
first-draw decode jitter disappears on weak machines. Headless smoke
keeps working via the typeof-decode fallback. Asset URLs bump to v=56.
- Shift dash: 0.45*v*0.6 instant burst then 0.6s of 50% slow (net 95% of
walking distance); slow doubles as cooldown, no dash while idle/slowed.
- Generic ammo refills scale with the live pressure dial at 80% of the HP
multiplier via ammoPressureMul().
- Hearts at full HP stay on the floor (magnet ignores them) instead of
converting to score.
- Muzzle audit: flame FX and pellets share one right-hand origin, Spinal
Tap beam starts at the muzzle, saw flash suppressed (blade sparks are
the effect).
- Debug REFILL/INF AMMO and the ammo row now cover a spent special
holstered behind the Bone Popper.
- Sfx.duck() defaults its args; a no-arg call crashed the loop with a
non-finite AudioParam value (found via the dash E2E).
Four-stage keyer in flood_key_magenta: unchanged border flood, strict
interior-pocket key, is_magenta fringe erosion, and despill; hd_assets
inherits it via import. New --reprocess mode re-keys assets/raw sources
without API calls (skips tiles, rebuilds sheets, never touches the
manifest). Pure-key #FF00FF pockets are gone from every HD webp.
Cache-bust v=54 -> v=55 across index.html and sfxbank.js, bump the game
version to 0.8.0, document the HD Remaster setting in gameplay.md and the
changelog, and extend the smoke suite with 4x sheet-geometry bounds checks,
HD loader fallback URL assertions, and toggle persistence/reload checks.
218 stills + 24 sheets generated by tools/hd_assets.py at quality 90,
~220MB total. Loaded only when HD Remaster is enabled; standard PNGs
remain the default and the per-sprite fallback.
draw_sprites.py renderers take scale/smooth/src_dir/out_dir keywords with
SD-identical defaults (verified pixel-for-pixel against the committed
sheets); smooth swaps NEAREST for LANCZOS/BICUBIC. tools/hd_assets.py
derives the 4x WebP tier from assets/raw sources: flood-key, crop,
margin/fit/center without palette quantization, then renders 4x sheets
through a temp PNG bridge.
Sprites.load() serves assets/hd/<name>.webp in HD mode and falls back once
to the SD PNG per sprite; sheet frame sizes derive from sheet geometry
(width/8 for actor and legs sheets, height for the one-row death strip) so
both tiers address correctly.
G.hdRemaster persisted in localStorage (meatslicer_hd_remaster), toggled
from the title screen via H key or the bottom-left tag; activation reloads
so the asset tier and canvas resolution swap at boot. draw() applies a
uniform hdScale() transform, the room-layer cache renders at render scale,
and image smoothing follows the tier.