Give the run strip a panel, round the rack, fix the duel's hint
The NEXT/GAME row was bare text on the court. That was legible over one dark green lawn and is not over a sunlit clay crowd or a floodlit blue one, so it gets its own rounded panel — the same thing the scoreboard art above it already relies on. It states the sets banked now too; they had only ever appeared in the scoreboard's own per-row column. The weapon rack's five slots are rounded, which is what they are: buttons. The duel is played under tennis rules — no slingshot, no rack, serve to the box (see the !G.interlude guards in solveShot and the aim reticle) — but the bottom hint was still telling you to pull back to aim, which is the one thing that does nothing there. It reads as a tennis serve now. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
af30db2ff3
commit
60f7ce2cf1
2 files changed
+28
-8
No files matched your search
@@ -208,6 +208,14 @@ function quadXZ(x1, z1, x2, z2, color) { // filled court-plane rect (screen trap
|
||||
ctx.fillStyle = color; ctx.beginPath();
|
||||
ctx.moveTo(a.x, a.y); ctx.lineTo(b.x, b.y); ctx.lineTo(c.x, c.y); ctx.lineTo(d.x, d.y); ctx.fill();
|
||||
}
|
||||
// Rounded panel. ctx.roundRect is native everywhere this game runs (Safari 16.4+), and the square
|
||||
// fallback only matters on a browser that could not render the rest of this anyway.
|
||||
function panel(x, y, w, h, r, fill, stroke) {
|
||||
ctx.beginPath();
|
||||
if (ctx.roundRect) ctx.roundRect(x, y, w, h, r); else ctx.rect(x, y, w, h);
|
||||
if (fill) { ctx.fillStyle = fill; ctx.fill(); }
|
||||
if (stroke) { ctx.strokeStyle = stroke; ctx.stroke(); }
|
||||
}
|
||||
function drawShadow(px, py, rx, ry, alpha = 0.28) {
|
||||
ctx.fillStyle = `rgba(0,0,0,${alpha})`;
|
||||
ctx.beginPath(); ctx.ellipse(px, py, rx, Math.max(1, ry), 0, 0, 7); ctx.fill();
|
||||
@@ -601,8 +609,10 @@ function drawRack() {
|
||||
ctx.save(); ctx.lineWidth = 1;
|
||||
for (let i = 0; i < g.n; i++) {
|
||||
const x = g.x0 + i * g.tw, sel = G.weapon === i, dim = stock[i] <= 0 && i > 0;
|
||||
ctx.fillStyle = sel ? '#123a24' : '#0a2418'; ctx.fillRect(x + 1, g.y0 + 1, g.tw - 2, g.h - 2); // sunk slot
|
||||
ctx.strokeStyle = sel ? UI.pick : '#05160e'; ctx.strokeRect(x + 1.5, g.y0 + 1.5, g.tw - 3, g.h - 3); // gold frame when live
|
||||
// rounded slots: five separate tiles read as five buttons, which is what they are
|
||||
panel(x + 1.5, g.y0 + 1.5, g.tw - 3, g.h - 3, 4,
|
||||
sel ? '#123a24' : '#0a2418', // sunk slot
|
||||
sel ? UI.pick : '#05160e'); // gold frame when live
|
||||
// icon: left, vertically centred, padded off the edge
|
||||
const box = g.h - pad * 2, icx = x + pad + box / 2, icy = g.y0 + g.h / 2;
|
||||
ctx.globalAlpha = dim ? 0.35 : 1;
|
||||
@@ -1009,12 +1019,19 @@ export function render() {
|
||||
ctx.font = F(8); ctx.fillStyle = bd.cream; ctx.fillText(String(score.games[i]), 90, cy); // games
|
||||
ctx.font = F(8); ctx.fillStyle = svr ? bd.gold : bd.cream; ctx.fillText(ptTxt(i), 126, cy); // points, in the art's slot
|
||||
}
|
||||
if (splat) { // which game you're in, permanently — the 1.1s GAME N flash was the only place it showed
|
||||
if (splat) {
|
||||
// This row used to be bare text on the court. That was fine over one dark green lawn and is
|
||||
// unreadable over a sunlit clay crowd or a floodlit blue one, so it gets its own panel — the
|
||||
// same trick the scoreboard art above it already relies on.
|
||||
ctx.lineWidth = 1;
|
||||
panel(0.5, bH + 1.5, bW - 1, 12, 3, 'rgba(8,26,18,0.82)', hexA(bd.hi, 0.35));
|
||||
// which game you're in, and the sets banked, permanently — the 1.1s GAME N flash and the
|
||||
// scoreboard's own per-row sets column were the only places either showed.
|
||||
ctx.textAlign = 'right'; ctx.font = F(6, 0); ctx.fillStyle = bd.dim;
|
||||
ctx.fillText('GAME ' + (G.runGames + 1), bW, bH + 7);
|
||||
ctx.fillText(`GAME ${G.runGames + 1} · ${G.sets[0]} SET${G.sets[0] === 1 ? '' : 'S'}`, bW - 3, bH + 7);
|
||||
}
|
||||
if (splat && TUNE.killPips && TUNE.killsPerPoint > 1) { // kills-to-next-point pips, just under the board — no box
|
||||
ctx.textAlign = 'left'; ctx.font = F(6, 0); ctx.fillStyle = bd.dim; ctx.fillText('NEXT', 2, bH + 7);
|
||||
if (splat && TUNE.killPips && TUNE.killsPerPoint > 1) { // kills-to-next-point pips, on the panel above
|
||||
ctx.textAlign = 'left'; ctx.font = F(6, 0); ctx.fillStyle = bd.dim; ctx.fillText('NEXT', 4, bH + 7);
|
||||
for (let k = 0; k < TUNE.killsPerPoint; k++) {
|
||||
ctx.beginPath(); ctx.arc(32 + k * 8, bH + 7, 2.4, 0, 7);
|
||||
if (k < G.killCount) { ctx.fillStyle = bd.gold; ctx.fill(); }
|
||||
@@ -1068,7 +1085,10 @@ export function render() {
|
||||
ctx.fillStyle = UI.warm; ctx.font = F(8);
|
||||
const touch = IS_TOUCH, switchHint = touch ? 'TAP THE RACK TO SWITCH' : '1-5 TO SWITCH';
|
||||
const sel = ['PLAIN', 'BOMB', 'TRIPLE', 'FIRE', 'GUN'][G.weapon];
|
||||
const txt = G.mode === 'splatter'
|
||||
// The DUEL is played under tennis rules — no slingshot, no rack, serve to the box (see the
|
||||
// !G.interlude guards in solveShot and the aim reticle). It was still being told to pull back
|
||||
// to aim, which is the one thing that does nothing there.
|
||||
const txt = G.mode === 'splatter' && !G.interlude
|
||||
? (G.weapon === 4 ? (touch ? 'TAP TO UNLOAD THE GUN' : 'SPACE UNLOADS THE GUN')
|
||||
: G.aim ? 'PULL BACK: FURTHER = HARDER'
|
||||
: G.weapon > 0 ? `SERVE FIRES: ${sel} · ${switchHint}`
|
||||
|
||||
Reference in new issue
Block a user