Turn the praise down, and stop the footnotes shouting
The band made PERFECT earnable, which was the fix — but you learn a band, and then the HUD is talking on every other serve again whatever the word says. So the praise goes behind a shared cooldown (TUNE.praiseEvery, 14s), the same model the umpire's asides have used all along for exactly this problem. Eight band-perfect serves back to back now yield one word. The bright click in the pull is the reliable feedback for a good draw; the word was only ever garnish, and garnish on every plate is just food. And the free-floating slot drops 24 -> 18. It shares that slot with CLEAR THE COURT, DOUBLE KILL, RETURNED and GAME N — half of which are 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. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
9c7ee42bfa
commit
ebce08199f
6 files changed
+26
-4
No files matched your search
+7
-1
@@ -343,7 +343,13 @@ zombie (now an occasional opponent, `TUNE.racketEvery`, not the every-game fixtu
|
|||||||
`perfectMs` window, the slingshot earns it by STOPPING in `pullFull..pullFull*perfectBand`. The
|
`perfectMs` window, the slingshot earns it by STOPPING in `pullFull..pullFull*perfectBand`. The
|
||||||
draw clicks brighter when you enter that band and goes quiet when you overshoot — without that
|
draw clicks brighter when you enter that band and goes quiet when you overshoot — without that
|
||||||
audible target it would be invisible skill. The word itself rotates through `PRAISE` (cycled, not
|
audible target it would be invisible skill. The word itself rotates through `PRAISE` (cycled, not
|
||||||
random: the same word twice running reads as a stuck HUD element, not as praise).
|
random: the same word twice running reads as a stuck HUD element, not as praise) and sits behind
|
||||||
|
`TUNE.praiseEvery`, the same shared-cooldown model the umpire's asides use — you LEARN to hit the
|
||||||
|
band, and then a per-serve word is noise again whatever it says. The click is the feedback for a
|
||||||
|
good pull; the word is garnish.
|
||||||
|
- **The free-floating slot is 18px, not card-sized.** Half of what lands in it (the praise words,
|
||||||
|
`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.
|
||||||
|
|
||||||
## Session log: the sidestep, and two type registers (2026-08-03)
|
## Session log: the sidestep, and two type registers (2026-08-03)
|
||||||
|
|
||||||
|
|||||||
@@ -195,6 +195,10 @@ export const TUNE = {
|
|||||||
// needs the pull to STOP in the band pullFull..pullFull*this (130-166 px), which
|
// needs the pull to STOP in the band pullFull..pullFull*this (130-166 px), which
|
||||||
// is a target you can miss. The pull clicks when you enter it (see input.js) and
|
// is a target you can miss. The pull clicks when you enter it (see input.js) and
|
||||||
// goes quiet when you overshoot, so the window is learnable by feel.
|
// goes quiet when you overshoot, so the window is learnable by feel.
|
||||||
|
praiseEvery: 14, // s between praise words, on the umpire's-asides model (asideEvery below). The
|
||||||
|
// band alone was not enough: you learn to hit it, and then the HUD talks on
|
||||||
|
// every other serve again. The CLICK is the reliable feedback for a good pull —
|
||||||
|
// the word is garnish, and garnish on every plate is just food.
|
||||||
aimCone: 0.10, // lane half-width added per meter of range. This is what makes the pull's ANGLE
|
aimCone: 0.10, // lane half-width added per meter of range. This is what makes the pull's ANGLE
|
||||||
// mean something: at 0.45 the cone was ±10 m at the far baseline — the whole
|
// mean something: at 0.45 the cone was ±10 m at the far baseline — the whole
|
||||||
// court — so every downward pull picked the same body and the angle felt inert.
|
// court — so every downward pull picked the same body and the angle felt inert.
|
||||||
|
|||||||
+9
-1
@@ -43,7 +43,14 @@ export const pullPerfect = mag => mag >= TUNE.pullFull && mag <= TUNE.pullFull *
|
|||||||
// running is what made one word read as a stuck HUD element rather than as praise.
|
// running is what made one word read as a stuck HUD element rather than as praise.
|
||||||
const PRAISE = ['PERFECT', 'TEXTBOOK', 'IMMACULATE', 'OH, LOVELY', 'UNPLAYABLE', 'SHOT OF THE DAY'];
|
const PRAISE = ['PERFECT', 'TEXTBOOK', 'IMMACULATE', 'OH, LOVELY', 'UNPLAYABLE', 'SHOT OF THE DAY'];
|
||||||
let praiseN = 0;
|
let praiseN = 0;
|
||||||
const praise = () => { G.flash = { text: PRAISE[praiseN++ % PRAISE.length], t: 0.7 }; };
|
// ...and he does not say it every time. Same cooldown model as the asides (aside()): once you have
|
||||||
|
// learned the band you hit it constantly, and a HUD that talks on every other serve is noise
|
||||||
|
// whatever the word is. The bright click in the pull is the reliable feedback; this is the garnish.
|
||||||
|
function praise() {
|
||||||
|
if (G.praiseT > 0) return;
|
||||||
|
G.praiseT = TUNE.praiseEvery;
|
||||||
|
G.flash = { text: PRAISE[praiseN++ % PRAISE.length], t: 0.7 };
|
||||||
|
}
|
||||||
|
|
||||||
export function flickTarget(ch, aim) { // exported: render draws the live reticle with it
|
export function flickTarget(ch, aim) { // exported: render draws the live reticle with it
|
||||||
const q = pullQuality(aim.mag), f = pullFrac(aim.mag);
|
const q = pullQuality(aim.mag), f = pullFrac(aim.mag);
|
||||||
@@ -1213,6 +1220,7 @@ export function update(dt) {
|
|||||||
crowd.cheer = Math.max(0, crowd.cheer - dt);
|
crowd.cheer = Math.max(0, crowd.cheer - dt);
|
||||||
G.idleT += dt;
|
G.idleT += dt;
|
||||||
if (G.asideT > 0) G.asideT -= dt; // the asides' shared cooldown (see aside())
|
if (G.asideT > 0) G.asideT -= dt; // the asides' shared cooldown (see aside())
|
||||||
|
if (G.praiseT > 0) G.praiseT -= dt; // ...and the praise words' (see praise())
|
||||||
// the fired band unravels behind the ball. It also goes the moment the ball does — a shambler
|
// the fired band unravels behind the ball. It also goes the moment the ball does — a shambler
|
||||||
// eating the shot ends the band with the splat, which is a cut you want.
|
// eating the shot ends the band with the splat, which is a cut you want.
|
||||||
if (G.trail && ((G.trail.t += dt) >= G.trail.T || !ball.active)) G.trail = null;
|
if (G.trail && ((G.trail.t += dt) >= G.trail.T || !ball.active)) G.trail = null;
|
||||||
|
|||||||
+1
-1
@@ -184,6 +184,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script type="module" src="main.js?v=115"></script>
|
<script type="module" src="main.js?v=116"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -1578,7 +1578,10 @@ export function render() {
|
|||||||
// RETURNED, CHANGEOVER, weapon labels — everything that used to be plain white 12px text sitting
|
// RETURNED, CHANGEOVER, weapon labels — everything that used to be plain white 12px text sitting
|
||||||
// directly on the crowd. It keeps its 1px jitter; the shadow is what makes it legible anywhere.
|
// directly on the crowd. It keeps its 1px jitter; the shadow is what makes it legible anywhere.
|
||||||
if (G.flash) {
|
if (G.flash) {
|
||||||
ctx.font = B(24);
|
// 18, not 24: at the card's near-size a transient one-liner read as a milestone announcement,
|
||||||
|
// and half of what lands in this slot (PERFECT, RETURNED, DOUBLE KILL) is only ever a footnote
|
||||||
|
// to something you already watched happen. The register is right; the volume was not.
|
||||||
|
ctx.font = B(18);
|
||||||
shadowText(G.flash.text, W / 2, FLASH_Y + Math.sin(G.flash.t * 20) * 1, '#ffffff');
|
shadowText(G.flash.text, W / 2, FLASH_Y + Math.sin(G.flash.t * 20) * 1, '#ffffff');
|
||||||
}
|
}
|
||||||
// the SET milestone card (the duel's own announcement is a real modal now, not a timed card)
|
// the SET milestone card (the duel's own announcement is a real modal now, not a timed card)
|
||||||
|
|||||||
@@ -81,6 +81,7 @@ export const G = {
|
|||||||
hurtT: 0, // s of post-hit grace left; also drives the red hit flash
|
hurtT: 0, // s of post-hit grace left; also drives the red hit flash
|
||||||
hurtFlash: 0, // s of the impact wash left
|
hurtFlash: 0, // s of the impact wash left
|
||||||
asideT: 14, // s until the umpire's next unsolicited remark (TUNE.asideEvery)
|
asideT: 14, // s until the umpire's next unsolicited remark (TUNE.asideEvery)
|
||||||
|
praiseT: 0, // s until the next praise word is allowed (TUNE.praiseEvery)
|
||||||
bossDue: 0, // chapter number the schedule owes you — fired at the next hand-cleared court
|
bossDue: 0, // chapter number the schedule owes you — fired at the next hand-cleared court
|
||||||
bossFight: 0, // chapter number of the boss currently on court, 0 = none. Blocks all other spawns.
|
bossFight: 0, // chapter number of the boss currently on court, 0 = none. Blocks all other spawns.
|
||||||
slowT: 0, // s of REAL time left running the world in slow motion (a chapter boss dying)
|
slowT: 0, // s of REAL time left running the world in slow motion (a chapter boss dying)
|
||||||
|
|||||||
Reference in new issue
Block a user