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
+1
-1
@@ -117,6 +117,6 @@
|
|||||||
<div id="courts"><h2>CHOOSE YOUR COURT</h2></div>
|
<div id="courts"><h2>CHOOSE YOUR COURT</h2></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script type="module" src="main.js?v=89"></script>
|
<script type="module" src="main.js?v=90"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -208,6 +208,14 @@ function quadXZ(x1, z1, x2, z2, color) { // filled court-plane rect (screen trap
|
|||||||
ctx.fillStyle = color; ctx.beginPath();
|
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();
|
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) {
|
function drawShadow(px, py, rx, ry, alpha = 0.28) {
|
||||||
ctx.fillStyle = `rgba(0,0,0,${alpha})`;
|
ctx.fillStyle = `rgba(0,0,0,${alpha})`;
|
||||||
ctx.beginPath(); ctx.ellipse(px, py, rx, Math.max(1, ry), 0, 0, 7); ctx.fill();
|
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;
|
ctx.save(); ctx.lineWidth = 1;
|
||||||
for (let i = 0; i < g.n; i++) {
|
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;
|
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
|
// rounded slots: five separate tiles read as five buttons, which is what they are
|
||||||
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
|
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
|
// 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;
|
const box = g.h - pad * 2, icx = x + pad + box / 2, icy = g.y0 + g.h / 2;
|
||||||
ctx.globalAlpha = dim ? 0.35 : 1;
|
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 = 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
|
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.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
|
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', 2, bH + 7);
|
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++) {
|
for (let k = 0; k < TUNE.killsPerPoint; k++) {
|
||||||
ctx.beginPath(); ctx.arc(32 + k * 8, bH + 7, 2.4, 0, 7);
|
ctx.beginPath(); ctx.arc(32 + k * 8, bH + 7, 2.4, 0, 7);
|
||||||
if (k < G.killCount) { ctx.fillStyle = bd.gold; ctx.fill(); }
|
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);
|
ctx.fillStyle = UI.warm; ctx.font = F(8);
|
||||||
const touch = IS_TOUCH, switchHint = touch ? 'TAP THE RACK TO SWITCH' : '1-5 TO SWITCH';
|
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 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.weapon === 4 ? (touch ? 'TAP TO UNLOAD THE GUN' : 'SPACE UNLOADS THE GUN')
|
||||||
: G.aim ? 'PULL BACK: FURTHER = HARDER'
|
: G.aim ? 'PULL BACK: FURTHER = HARDER'
|
||||||
: G.weapon > 0 ? `SERVE FIRES: ${sel} · ${switchHint}`
|
: G.weapon > 0 ? `SERVE FIRES: ${sel} · ${switchHint}`
|
||||||
|
|||||||
Reference in new issue
Block a user