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>
This commit is contained in:
1 parent
5faaa99467
commit
a09a5d7559
23 files changed
+392
-103
No files matched your search
+12
-14
@@ -13,26 +13,23 @@
|
||||
html, body { margin: 0; height: 100%; background: #10141c; display: grid; place-items: center; }
|
||||
canvas { image-rendering: pixelated; width: min(96vw, calc(96vh * 16 / 9)); aspect-ratio: 16/9; touch-action: none; }
|
||||
#hint { position: fixed; bottom: 8px; width: 100%; text-align: center;
|
||||
font: 12px monospace; color: #6b7688; }
|
||||
font: 400 10px Silkscreen, monospace; color: #6b7688; }
|
||||
@media (pointer: coarse) { #hint, #intro .kb { display: none; } }
|
||||
#intro { position: fixed; inset: 0; z-index: 10; touch-action: none; display: grid; place-items: center;
|
||||
background: radial-gradient(circle at 50% 34%, rgba(34,16,20,0.92), rgba(8,10,16,0.96)); }
|
||||
#intro .card { font: 13px monospace; color: #dfe6f2; text-align: center; line-height: 1.9;
|
||||
#intro .card { font: 400 11px Silkscreen, monospace; color: #dfe6f2; text-align: center; line-height: 1.9;
|
||||
padding: 18px 26px; max-width: 92vw; animation: drop 0.5s ease-out; }
|
||||
@keyframes drop { from { transform: translateY(-14px); opacity: 0; } }
|
||||
#intro .logo { line-height: 0.9; margin-bottom: 8px; }
|
||||
#intro .logo span { display: block; font-weight: 900; }
|
||||
#intro .l1 { font-size: 46px; letter-spacing: 3px; color: #e0231a;
|
||||
text-shadow: 0 2px 0 #7a0f0a, 0 4px 0 #550906, 0 6px 1px rgba(0,0,0,0.4), 0 0 20px rgba(224,35,26,0.35); }
|
||||
#intro .l2 { font-size: 30px; letter-spacing: 9px; color: #eef23a; margin-top: 2px;
|
||||
text-shadow: 0 2px 0 #59610f, 0 0 14px rgba(238,242,58,0.3); }
|
||||
#intro .slogan { margin: 8px 0 16px; font-size: 13px; font-style: italic; color: #b6a0a4; letter-spacing: 0.5px; }
|
||||
#intro .slogan b { color: #e0231a; font-style: normal; }
|
||||
/* the logo is generated pixel art now (assets/logo.png), not stacked CSS text */
|
||||
#intro .logo { display: block; width: min(320px, 78vw); height: auto; margin: 0 auto 8px;
|
||||
image-rendering: pixelated; filter: drop-shadow(0 3px 0 rgba(0,0,0,0.45)); }
|
||||
#intro .slogan { margin: 8px 0 16px; font-size: 12px; color: #b6a0a4; letter-spacing: 0.5px; }
|
||||
#intro .slogan b { color: #c9564a; }
|
||||
#intro .how { display: inline-block; text-align: left; color: #cbd4e2; line-height: 2; }
|
||||
#intro .how p { margin: 0; }
|
||||
#intro .how span { display: inline-block; width: 30px; }
|
||||
#intro em { color: #8a96ab; font-style: normal; font-size: 12px; }
|
||||
#intro .start { margin-top: 18px; color: #f8e048; font-weight: bold; letter-spacing: 1px; animation: blink 1.1s step-end infinite; }
|
||||
#intro .start { margin-top: 18px; color: #e0cc72; font-weight: bold; letter-spacing: 1px; animation: blink 1.1s step-end infinite; }
|
||||
@keyframes blink { 50% { opacity: 0.25; } }
|
||||
</style>
|
||||
</head>
|
||||
@@ -41,10 +38,11 @@
|
||||
<div id="hint">move: WASD/arrows · serve/swing: SPACE · aim: hold a direction while swinging · T: tuning panel</div>
|
||||
<div id="intro">
|
||||
<div class="card">
|
||||
<div class="logo"><span class="l1">SPLATTER</span><span class="l2">TENNIS</span></div>
|
||||
<img class="logo" src="assets/logo.png" alt="Splatter Tennis">
|
||||
|
||||
<p class="slogan">Fifteen. Thirty. Forty. <b>Dead.</b></p>
|
||||
<div class="how">
|
||||
<p><span>👆</span> tap — serve & swing</p>
|
||||
<p><span>👆</span> tap — serve + swing</p>
|
||||
<p><span>👉</span> flick — aimed shot</p>
|
||||
<p><span>✋</span> drag — run <em>(auto-run is on)</em></p>
|
||||
<p class="kb"><span>⌨️</span> WASD / arrows + SPACE</p>
|
||||
@@ -52,6 +50,6 @@
|
||||
<p class="start">TAP TO START</p>
|
||||
</div>
|
||||
</div>
|
||||
<script type="module" src="main.js?v=20"></script>
|
||||
<script type="module" src="main.js?v=26"></script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in new issue
Block a user