The rules cards get zones: boxed rows, threat colour only for the threat, the blood pool as pips
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
00f121bec7
commit
bfb3631ec3
4 files changed
+66
-4
No files matched your search
@@ -491,6 +491,15 @@ zombie (now an occasional opponent, `TUNE.racketEvery`, not the every-game fixtu
|
|||||||
`RETURNED`, `DOUBLE KILL`) is a footnote to something you already watched happen. Sharing the loud
|
`RETURNED`, `DOUBLE KILL`) is a footnote to something you already watched happen. Sharing the loud
|
||||||
FACE with the milestone cards is right; sharing their SIZE made every footnote read as a milestone.
|
FACE with the milestone cards is right; sharing their SIZE made every footnote read as a milestone.
|
||||||
|
|
||||||
|
## Session log: the rules cards get zones too (2026-08-29, Oliver: "wichtige Infos, schwer zu erfassen")
|
||||||
|
|
||||||
|
Same disease as the walk-on, same medicine: the three mode modals (THE TOURNAMENT, DEATH MATCH,
|
||||||
|
THE TRAINING COURT) carried compound em-dash sentences edge to edge. `render.drawRulesModal`
|
||||||
|
(driven by `m.rules: [{lead, sub, hot, pips}]`): each rule is its own boxed row — the statement
|
||||||
|
in cream, the consequence beneath it DIM for information and the modal's hot colour only when it
|
||||||
|
threatens (`hot`), and a rule about blood shows the pool as the HUD's own pips instead of a naked
|
||||||
|
number. Entrance lanes, freeze and tap rules unchanged; title/body still carry the words.
|
||||||
|
|
||||||
## Session log: the walk-on gets its own face (2026-08-29, Oliver: "buried in a wall of text")
|
## Session log: the walk-on gets its own face (2026-08-29, Oliver: "buried in a wall of text")
|
||||||
|
|
||||||
The opponent intro carried six kinds of information as one modal line stack. It wears designated
|
The opponent intro carried six kinds of information as one modal line stack. It wears designated
|
||||||
|
|||||||
+1
-1
@@ -871,6 +871,6 @@ if (location.hostname !== 'splatter-tennis.wuwi.one' && !window.Capacitor?.isNat
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script type="module" src="src/main.js?v=337"></script>
|
<script type="module" src="src/main.js?v=338"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
+11
-3
@@ -390,19 +390,27 @@ export function startMatch(cup = false, draw = null, train = 0) {
|
|||||||
G.seen.train = true;
|
G.seen.train = true;
|
||||||
showModal({ title: 'THE TRAINING COURT', color: UI.hot,
|
showModal({ title: 'THE TRAINING COURT', color: UI.hot,
|
||||||
body: ['ONE BODY, ONE GAME AT A TIME',
|
body: ['ONE BODY, ONE GAME AT A TIME',
|
||||||
'FELL IT — OR BLEED OUT — AND IT COMES BACK', 'NO RUN IS BANKED — REPS PAY QUARTER XP'] });
|
'FELL IT — OR BLEED OUT — AND IT COMES BACK', 'NO RUN IS BANKED — REPS PAY QUARTER XP'],
|
||||||
|
rules: [{ lead: 'ONE BODY, ONE GAME AT A TIME', sub: 'FELL IT OR BLEED OUT — IT COMES BACK' },
|
||||||
|
{ lead: 'NO RUN IS BANKED', sub: 'REPS PAY QUARTER XP' }] });
|
||||||
} else if (cup && !G.seen.cup) {
|
} else if (cup && !G.seen.cup) {
|
||||||
G.seen.cup = true;
|
G.seen.cup = true;
|
||||||
showModal({ title: 'THE TOURNAMENT', color: UI.hot,
|
showModal({ title: 'THE TOURNAMENT', color: UI.hot,
|
||||||
body: ["UP YOUR LEAGUE'S DEAD HIERARCHY — THE FINAL IS SET",
|
body: ["UP YOUR LEAGUE'S DEAD HIERARCHY — THE FINAL IS SET",
|
||||||
'ITS POINTS DRAW BLOOD — ' + TUNE.matchPlayerHp + ' AND YOU ARE EATEN'] });
|
'ITS POINTS DRAW BLOOD — ' + TUNE.matchPlayerHp + ' AND YOU ARE EATEN'],
|
||||||
|
rules: [{ lead: "UP YOUR LEAGUE'S DEAD HIERARCHY", sub: 'THE FINAL IS SET' },
|
||||||
|
{ lead: 'ITS POINTS DRAW BLOOD', sub: 'AT ZERO YOU ARE EATEN', hot: true,
|
||||||
|
pips: TUNE.matchPlayerHp }] });
|
||||||
} else if (!cup && !train && !G.seen.match) {
|
} else if (!cup && !train && !G.seen.match) {
|
||||||
G.seen.match = true;
|
G.seen.match = true;
|
||||||
// Oliver's copy direction: name the stake, count the blood. TUNE.matchPlayerHp is the real
|
// Oliver's copy direction: name the stake, count the blood. TUNE.matchPlayerHp is the real
|
||||||
// number, so the modal can never drift from the pool it describes.
|
// number, so the modal can never drift from the pool it describes.
|
||||||
showModal({ title: 'DEATH MATCH', color: UI.hot,
|
showModal({ title: 'DEATH MATCH', color: UI.hot,
|
||||||
body: ['WIN THE POINT — WOUND THE ZOMBIE',
|
body: ['WIN THE POINT — WOUND THE ZOMBIE',
|
||||||
'ITS POINTS DRAW BLOOD — ' + TUNE.matchPlayerHp + ' AND YOU ARE EATEN'] });
|
'ITS POINTS DRAW BLOOD — ' + TUNE.matchPlayerHp + ' AND YOU ARE EATEN'],
|
||||||
|
rules: [{ lead: 'WIN THE POINT', sub: 'WOUND THE ZOMBIE' },
|
||||||
|
{ lead: 'ITS POINTS DRAW BLOOD', sub: 'AT ZERO YOU ARE EATEN', hot: true,
|
||||||
|
pips: TUNE.matchPlayerHp }] });
|
||||||
}
|
}
|
||||||
matchOpponent();
|
matchOpponent();
|
||||||
matchReady();
|
matchReady();
|
||||||
|
|||||||
@@ -2341,6 +2341,50 @@ function drawFlickHint() {
|
|||||||
const BACK_C = 1.70158;
|
const BACK_C = 1.70158;
|
||||||
const easeBack = p => 1 + (BACK_C + 1) * (p - 1) ** 3 + BACK_C * (p - 1) ** 2;
|
const easeBack = p => 1 + (BACK_C + 1) * (p - 1) ** 3 + BACK_C * (p - 1) ** 2;
|
||||||
|
|
||||||
|
// THE RULES CARD (2026-08-29, Oliver: the mode modals were walls of text too). Each rule is its
|
||||||
|
// own boxed row — the statement in cream over its consequence in the modal's colour — and a rule
|
||||||
|
// about BLOOD shows the pool as the game's own pips instead of a naked number. Driven by
|
||||||
|
// m.rules: [{ lead, sub, pips }]; title/body keep carrying the words for the suite.
|
||||||
|
function drawRulesModal(m, t, dx) {
|
||||||
|
const boxW = Math.min(W - 28, 240), bx = Math.round(W / 2 - boxW / 2);
|
||||||
|
const rowH = r => (r.sub ? 34 : 22) + (r.pips ? 12 : 0);
|
||||||
|
const gap = 8;
|
||||||
|
const blockH = 30 + m.rules.reduce((a, r) => a + rowH(r) + gap, 0) - gap;
|
||||||
|
let y = Math.round(H / 2 - blockH / 2);
|
||||||
|
const hx = dx(0, 1);
|
||||||
|
ctx.font = B(24); ctx.fillStyle = m.color || UI.warm;
|
||||||
|
if (hx) for (const [f, a] of [[0.7, 0.12], [0.35, 0.26]]) {
|
||||||
|
ctx.save(); ctx.globalAlpha = a;
|
||||||
|
ctx.fillText(m.title, W / 2 + Math.round(hx * (1 + f)), y + 8);
|
||||||
|
ctx.restore();
|
||||||
|
}
|
||||||
|
ctx.fillText(m.title, W / 2 + hx, y + 8);
|
||||||
|
y += 30;
|
||||||
|
m.rules.forEach((r, i) => {
|
||||||
|
const h = rowH(r), rx = dx(i + 1, -1);
|
||||||
|
panel(bx + rx, y, boxW, h, 3, 'rgba(8,26,18,0.85)', 'rgba(120,150,130,0.4)');
|
||||||
|
ctx.font = F(8, 700);
|
||||||
|
shadowText(r.lead, W / 2 + rx, y + 12, UI.cream);
|
||||||
|
// the consequence line: DIM for information, the modal's hot colour only when it threatens
|
||||||
|
if (r.sub) { ctx.font = F(8, 0); shadowText(r.sub, W / 2 + rx, y + 25, r.hot ? (m.color || UI.warm) : UI.dim); }
|
||||||
|
if (r.pips) { // the pool, in the HUD's own pip language
|
||||||
|
const pg = 9, pw = (r.pips - 1) * pg;
|
||||||
|
for (let k = 0; k < r.pips; k++) {
|
||||||
|
ctx.beginPath(); ctx.arc(W / 2 + rx - pw / 2 + k * pg, y + h - 9, 2.6, 0, 7);
|
||||||
|
ctx.fillStyle = '#8a1a12'; ctx.fill();
|
||||||
|
ctx.strokeStyle = 'rgba(0,0,0,0.5)'; ctx.lineWidth = 1; ctx.stroke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
y += h + gap;
|
||||||
|
});
|
||||||
|
if (t >= 1 && Math.floor(G.idleT * 2) % 2) {
|
||||||
|
ctx.font = F(8);
|
||||||
|
shadowText(IS_TOUCH ? 'TAP TO CONTINUE' : 'PRESS ANY KEY', W / 2,
|
||||||
|
Math.round(H / 2 + blockH / 2 + 24), UI.warm);
|
||||||
|
}
|
||||||
|
ctx.textBaseline = 'top';
|
||||||
|
}
|
||||||
|
|
||||||
// THE WALK-ON'S OWN FACE (2026-08-29, Oliver: the opponent card was "a wall of text" — six kinds
|
// THE WALK-ON'S OWN FACE (2026-08-29, Oliver: the opponent card was "a wall of text" — six kinds
|
||||||
// of information in one line stack). Designated zones instead: the MASTHEAD (mugshot beside the
|
// of information in one line stack). Designated zones instead: the MASTHEAD (mugshot beside the
|
||||||
// name with the slogan under it), THE MEASURE (its level boxed against yours — the number wears
|
// name with the slogan under it), THE MEASURE (its level boxed against yours — the number wears
|
||||||
@@ -2417,6 +2461,7 @@ function drawModal(m) {
|
|||||||
ctx.fillRect(0, 0, W, H);
|
ctx.fillRect(0, 0, W, H);
|
||||||
ctx.textAlign = 'center'; ctx.textBaseline = 'middle';
|
ctx.textAlign = 'center'; ctx.textBaseline = 'middle';
|
||||||
if (m.foe) return drawFoeModal(m, t, dx);
|
if (m.foe) return drawFoeModal(m, t, dx);
|
||||||
|
if (m.rules) return drawRulesModal(m, t, dx);
|
||||||
const body = m.body || [], img = m.icon && SPR[m.icon];
|
const body = m.body || [], img = m.icon && SPR[m.icon];
|
||||||
// cap the illustration against the viewport, not just the asset: at 270px tall (landscape) a
|
// cap the illustration against the viewport, not just the asset: at 270px tall (landscape) a
|
||||||
// portrait-sized icon pushed the TAP prompt down onto the weapon rack
|
// portrait-sized icon pushed the TAP prompt down onto the weapon rack
|
||||||
|
|||||||
Reference in new issue
Block a user