Files
wuwiandClaude Opus 5 a09a5d7559 Polish round: modal system, softened palette, pixel font, and the 14 plan items
The three foundations first, since everything else leans on them:

- G.modal is the only thing that stops the world. update() early-returns on it
  so no zombie steps, no ball flight, no spawn timers run while a popup is on
  screen — you can read it without being eaten. Dismissal swallows the gesture;
  the pointerdown dismisser is registered only on pointer:fine, because on touch
  pointerdown beats touchstart and a shared handler cleared the modal and then
  served with the same thumb.
- UI in config.js is the accent palette. The arcade primaries were shouting over
  the artwork; the gore reds deliberately stay loud.
- Every canvas font string now goes through F(). Bare `monospace` resolved to
  Courier — a slab serif — which was the "generic serif" bug, not taste.

Then: weapons are only ever WON. deliverTin() has exactly one caller now
(resolveInterlude('P')); the old game schedule calls the DUEL instead, fired off
proceedAfterClear() where the court is guaranteed empty. Four popups (splatter
tutorial with a generated illustration, first duel, weapon received, horde),
wCap 9 with the tin amounts tunable, a 5-hp brute that comes apart over four
wound skins, fire that ignites the neighbours and chains outward, a three-stage
death where you become the biggest gore mound on the court, and a 7s changeover
with a ball boy who finally has a mop.

New art: assets/logo.png, x_howto, z_gw2-4, x_boymop1-4, plus assets/_key.py for
standalone art (chroma-key + crop, no frame splitting, no forced sprite height).

Benchmarks re-run and holding: CPU returns 23/24 serves, 30 human-tapped points
= 50 hits / 0 whiffs, 26 simulated games with no exceptions.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 05:28:04 +03:00

27 lines
1000 B
Python

# chroma-key a generated image on its corner colour, despeckle, crop to the content bbox,
# and (optionally) downscale to a target width. For standalone art (logo, popup panels) —
# unlike assets/_slice.py this does NOT split frames or force a sprite height.
import sys
from PIL import Image
KEY_D2 = 60 * 60
def main(src, dst, target_w=None):
im = Image.open(src).convert('RGBA')
w, h = im.size
px = im.load()
bg = px[0, 0][:3]
for x in range(w):
for y in range(h):
r, g, b, a = px[x, y]
if (r - bg[0]) ** 2 + (g - bg[1]) ** 2 + (b - bg[2]) ** 2 < KEY_D2 or (min(r, b) > g + 40):
px[x, y] = (0, 0, 0, 0)
im = im.crop(im.getbbox())
if target_w and im.width > target_w:
im = im.resize((target_w, max(1, round(im.height * target_w / im.width))), Image.LANCZOS)
im.save(dst)
print(dst, im.size)
if __name__ == '__main__':
main(sys.argv[1], sys.argv[2], int(sys.argv[3]) if len(sys.argv) > 3 else None)