Give him a sidestep, and split the messages into two voices

He died at every chapter boss because he could not move, and there were two
independent reasons for it. The movement block is 'rally'-only — a settled feel
decision the six boss AI styles were written around — and a boss is fought from
'pretoss'. On top of that canAimServe() is true through a boss fight, so his one
finger was already spent on the slingshot: there was no gesture left over that
could have meant "move".

So it is a SIDESTEP, not a walk. One committed step on a cooldown, carrying
invulnerability that beats both damage channels — the chip damage in hurtPlayer
and the instant-death contact test. The rule about walking stands untouched.

The gesture is picked by WHICH WAY the drag leaves the deadzone, decided once and
never revisited for that touch: down-ish is the pull the game already teaches,
sideways is the step. No timing window, no invisible mode, so "one gesture means
one thing" still holds. The finger-steer modes are untouched — stick.aimable is
false in a horde, so the drag is the absolute steer it always was. The apron demo
now alternates PULL DOWN and SWIPE TO DODGE, and Marvin has a page for it.

The rest of the playtest list:

- Bangers is bundled beside Silkscreen, and the register IS the category. The
  quiet face keeps the HUD, the rack, the control hints and RESUMING IN; the loud
  one takes SET, CHAPTER, EPIC!, PERFECT and every free-floating flash. Floating
  text gets one shared y and a drop shadow, so it survives crowd, clay and blood.
- Modals queue instead of being dropped. That was why the trainer and the weapon
  had to be crammed into one card after a won duel; the heal gets its own popup
  with the x_cross tin over it — a sprite loaded since that merge and drawn by
  nothing.
- weaponModal's first-sight gate moved inside the function. The barrels gated
  themselves and the tin ladder did not, so four weapons re-explained themselves
  every three games while a barrel weapon explained itself once.
- Ten rack slots. Past five it grows a second row upward rather than shrinking
  ten tiles under the thumb target; slots 0-4 stay welded to the bottom edge.
- The boss win holds for its own 3.6s and says EPIC!, with no GAME n NEXT line —
  the bar under the scoreboard already prints that permanently. That bar went
  from 6px to 7px, which is the smallest type in the game becoming words.
- The turn punches the camera in on him and the ground shakes harder at each beat.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
wuwiandClaude Opus 5 committed 2026-08-03 21:48:24 +02:00
1 parent 99774ab4be
commit 55c0f75c37
10 files changed
+345 -95

No files matched your search

+18 -1
View File
@@ -27,6 +27,15 @@ export const TUNE = {
aiQualityMin: 0.45, aiQualityMax: 0.85, aiFlub: 0.09,
aiReaction: 0.45, // s before the CPU reads your shot — well-aimed corners are winners
autoAssist: matchMedia('(pointer: coarse)').matches ? 1.15 : 0, // auto-run speed factor (mobile)
// ---- THE SIDESTEP. Free walking stays 'rally'-only (see the movement block in gameplay), which
// left you rooted to the spot through a whole boss fight — the drag is the slingshot and there was
// no gesture left over that could mean "move". A dodge is not a walk: it is one committed step
// with a cooldown, so it answers a volley without turning the serve stance into a joystick.
dodgeDist: 1.9, // m of ground the sidestep covers
dodgeT: 0.18, // s it takes — fast enough to be a reaction, slow enough to read as a step
dodgeCool: 0.5, // s before another. THE anti-spam knob: shorter and you skate across the court
dodgeIFrame: 0.28,// s untouchable, covering the step and a hair after. Beats BOTH damage channels
// (a boss volley and the instant-death body), which is the whole point of it
zSpeed: 0.7, // zombie shamble speed m/s
// ---- THE DIFFICULTY DIRECTOR. Every dial below used to bottom out by about game 12, which is why
// a 50-game run stopped changing (games 12..50 were arithmetically identical). They are all keyed
@@ -307,6 +316,9 @@ export const TUNE = {
// full pierce simply killed the whole lane outright and nothing was left to catch fire.
burnPanic: 1.0, // walk-speed multiplier while burning (1 = off; crank it for panicked sprinting)
setCardT: 2.0, // s the centered SET card holds (the 'kurze unterbrechung' before the horde)
bossWinT: 3.6, // ...and s the EPIC! card holds after a chapter boss. Its OWN number: a boss is
// the longest fight in the game and the win was over before it registered, but
// stretching setCardT would have made every routine set announcement drag too.
modalIn: 0.34, // s the modal swooshes in — icon+title from the right, body cascading from the
// left, each on easeOutBack. The gesture that dismisses is swallowed until it
// lands, so a panic tap can't skip a popup before it has been read.
@@ -527,7 +539,12 @@ export const BARREL_WEAPONS = {
laser: { weight: 2, from: 6 },
rocket: { weight: 3, from: 12 }, // the late-game answer: it should never turn up in game three
};
export const RACK_MAX = 4; // gathered weapons the rack holds; a 5th rides slot 0 until it is spent
// Gathered weapons the rack holds (plus slot 0 = BALL, so ten tiles). Was 4, and at four you were
// throwing away a barrel almost every time one popped — Oliver: "that makes replacing the ball rarer
// if not unnecessary at all". The `temp` valve below survives for the eleventh, which nobody will see.
export const RACK_MAX = 9;
export const RACK_ROW = 5; // tiles per row — past five the rack grows a SECOND row upward, so a tile
// never shrinks below the thumb target rackHit was widened for
export const MACH_H = 1.55; // was 1.15 — waist high read as a prop you could ignore. Still furniture
// (a body is PLAYER_H 1.88), but big enough to be a thing on the court.