Commit Graph
451 Commits
Author SHA1 Message Date
wuwiandClaude Fable 5 c1365cb467 The season log records the stacked doors
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-31 13:09:00 +02:00
wuwiandClaude Fable 5 da2f4cc7da The run-end's buttons stop being chips: stacked where there is height
A 100px label in a 120px half-button left ten pixels of air, which reads as a
squeezed chip. A tall screen has the room, so the two doors stack full width —
filled primary over ghost secondary, the same shape the modal footer and the
splash already use. A short landscape cannot stack, so there the box itself
grows to 344 and the halves come out comfortable instead.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-31 13:07:55 +02:00
wuwiandClaude Fable 5 0f2ea2b05e The season log records the measures pass
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-31 12:19:17 +02:00
wuwiandClaude Fable 5 e5a0d1a5f9 The overlays get their measures: narrower boxes, full-width buttons, air
Oliver's second pass over the new box language. The modal is narrower and the
button spans it; every row got real spacing and the footer its own margin, so
the end screens stop reading as cramped. The xp bar is taller and its ramp now
rides the FILL — spanning the gradient across the whole track meant an early
level only ever showed one flat green inch of it. An illustrated announcement
puts its two lines beside the picture, under the headline, instead of in a
boxed row below its own icon.

The poster wall's section headings leave the announcement face to the posters
(it read as a second card headline), drop the left band and the rule, and let
air do the grouping. On the splash, OTHER GAMES gets its gap to the stats row,
and the boot shows nothing but the court, the title and the loading bar — the
plaque skeleton was still a plaque telling a level-25 player he was level 1.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-31 12:15:23 +02:00
wuwiandClaude Fable 5 e2cb13e46d The season log records the sections
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-31 11:46:30 +02:00
wuwiandClaude Fable 5 25c10b46d7 The poster wall gets rooms, not labels: three real sections
Oliver: the group headings were "sehr klein". Each group is a section now with
its posters inside it — the announcement face one size under the screen title,
a rule closing the header, and an inset band down the left. The canonical room
wears the primary's colour, because it is the one that moves your career.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-31 11:40:57 +02:00
wuwiandClaude Fable 5 5c7521f867 THE SEASON, phases 1-4: the front door is the fixture, and the club remembers
The tournament is the canonical path, so the game finally says so. The splash's
primary button names the next cup fixture and enters the draw; the poster wall
moved one tap down and deals three named rooms (the season, the training, out of
competition) with the cup at the top. Two profile fields remember every attempt
— rounds ever reached per league, and the body that put you out — so a lost cup
is a line on the board instead of a null event, and the training court offers
that body first, marked. The promotion names what waits in the new league.

Overlays consolidated in the same pass: three modal drawers became one box with
a masthead, hairline-separated rows and a real footer button (any tap still
dismisses), and the run-end summary wears the same box — headline, xp, mark, and
the fixture with ENTER THE DRAW beside PLAY AGAIN. The cup's own exit is a
ceremony now: OUT IN THE QUARTER-FINAL, your best standing beside it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-31 11:14:25 +02:00
wuwiandClaude Fable 5 063b01d2a5 The season: the tournament becomes the story — the plan of record
Oliver's read: the modes feel decoupled, the menus and end screens enforce no
story-line. Settled in discussion: the tournament is the canonical path and
everything else is Beiwerk, a cup attempt is one evening (no resume, no
seeding), classic stays a side room. The spine — the splash names the next
fixture, the club remembers your attempts, the side modes are described in cup
terms, every ending hands over, and the season opens inside the promotion.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-31 10:50:59 +02:00
wuwiandClaude Fable 5 41f1b6815f The copy audit: the climb card stops calling itself a death match, the machine gets named, the drill decrypts, the duel names both stakes
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-30 17:25:41 +02:00
wuwiandClaude Fable 5 7ece8312f3 The tournament card speaks plainly: your league round by round, win it all, move up
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-30 17:22:37 +02:00
wuwiandClaude Fable 5 bfb3631ec3 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>
2026-08-30 16:54:05 +02:00
wuwiandClaude Fable 5 00f121bec7 The walk-on gets its own face: mugshot masthead, the measure, the round strip — no more wall of text
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-30 16:22:20 +02:00
wuwiandClaude Fable 5 7fb595ca22 The look stops lying: the fallback never reports done, and the women's frame gets room to stand tall
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-30 16:16:54 +02:00
wuwiandClaude Fable 5 a38c804069 Training pays a quarter, and the level bar wears its endpoints
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-30 15:58:34 +02:00
wuwiandClaude Fable 5 7d39032907 The weapon licence, the peer line, and the steward gets his vest back
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-30 15:52:51 +02:00
wuwiandClaude Fable 5 7e2649c888 The level is you, in the league: quicker legs, a wider window, and a curve that pays on the way up
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-30 13:28:24 +02:00
wuwiandClaude Fable 5 7dc72009eb The leagues are cleared for main — the verdict is in
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-29 16:55:55 +02:00
wuwiandClaude Fable 5 7a9b61fc6b The boot gate learns about the profile: skeleton plaque, locked splash, a 4s cap
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-29 16:55:00 +02:00
wuwiandClaude Fable 5 04ef49ef3b The climb's sub line breaks where it means to
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-29 16:51:34 +02:00
wuwiandClaude Fable 5 a5a681f9f2 The cup climbs: easy to hard, deterministic — the shuffle retires with its screen
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-29 16:49:36 +02:00
wuwiandClaude Fable 5 dbfd085493 The draw screen retires: the league cup rolls inside the run, ENTER THE DRAW moves onto the setup button
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-29 16:46:34 +02:00
wuwiandClaude Fable 5 812bea3234 Plaque polish: the goal line finds its gold (--warm never existed in CSS), the chevron centres, the name grows
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-29 16:41:52 +02:00
wuwiandClaude Fable 5 3aaf2b9baf The plaque becomes a letterhead: crest left, the level line writ large beside it, the bar full-width
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-29 16:39:42 +02:00
wuwiandClaude Fable 5 1a39c9fdcb The plaque floats mid-court on glass, the blood pool breathes, and the 94vw card stops scrolling sideways
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-29 16:36:48 +02:00
wuwiandClaude Fable 5 689550d2d5 The climb: the arcade runs the whole earned ladder, the tempo follows the territory, the court is yours
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-29 16:24:43 +02:00
wuwiandClaude Fable 5 041827c4dc The league has its court: the tournament setup shows the home venue, not a choice
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-29 16:12:20 +02:00
wuwiandClaude Fable 5 d4780de76d The setup grows a league face: badge, felled tally, the band's roster — and a bigger ribbon
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-29 16:10:31 +02:00
wuwiandClaude Fable 5 5c34632513 Drop the stale duplicate ribbon rule the last edit left behind
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-29 16:05:50 +02:00
wuwiandClaude Fable 5 54e5db8ea2 The ribbon keeps its shadow (drop-shadow outlives the mask) and fades later
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-29 16:04:42 +02:00
wuwiandClaude Fable 5 77e6418e8e The way-up ribbon, final form: horizontal over the border, dissolving through a Bayer pixel fade
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-29 16:03:26 +02:00
wuwiandClaude Fable 5 3ba3d9dac0 The way-up ribbon, third cut: the wrap bug — one measured line, snug band, honest overhang
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-29 16:00:51 +02:00
wuwiandClaude Fable 5 6ea06ee42b The way-up ribbon hangs over the border — sized to stay out of the neighbouring poster
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-29 15:57:10 +02:00
wuwiandClaude Fable 5 fa6db7f77b The leagues, the polish round: the way-up ribbon, upright chevrons, the breathing badges, the card counts up
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-29 15:51:59 +02:00
wuwiandClaude Fable 5 87ac3294bc The leagues, the plaque round: the meta speaks — the player plaque, the goal line, the way-up tag, the centred ceremony
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-29 15:30:24 +02:00
wuwiandClaude Fable 5 787877bce6 The leagues, phase 4: the duel learns, tin tiers by league, level buffs, the board wears the badge
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-29 12:53:12 +02:00
wuwiandClaude Fable 5 76d88030e5 The leagues, phase 3: the character card, the honours collection, the skill trail
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-29 12:48:34 +02:00
wuwiandClaude Fable 5 16bf5b9fed The leagues, phase 2: five bands, pace tiers, locked courts, the members' ground, the promotion ceremony
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-29 12:43:23 +02:00
wuwiandClaude Fable 5 9133c0d351 The leagues, phase 1: the profile clock ticks — xp, levels, the identity screen, the server mirror
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-29 12:22:33 +02:00
wuwiandClaude Fable 5 bc10216d0b The leagues: the meta-progression plan of record — five leagues, XP by play, tin-tier gore coupling
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-29 11:55:27 +02:00
wuwiandClaude Fable 5 98b01fea5a The trail reads as motion, never as residue: the ghosts fade with the sprint
The lingering doubles were a real bug, not a taste problem: racketAI's
!incoming return kept the last chase's vx/vz on the body, so the speed
ghosts trailed a zombie that had already stopped. The velocity dies with
the chase now (tested). The look is halved with it: ghost alphas
0.22/0.10 -> 0.11/0.05, trailK 0.05 -> 0.035 — a hint of speed in the
corner of the eye, never a second body.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-28 16:36:19 +02:00
wuwiandClaude Fable 5 4ea9af5442 The surprise is an opening, not a free point: THE READ turned down a notch
Oliver, same day: 'die Gegner sind jetzt etwas zuu einfach.' The habit takes
four same-lane shots to learn (was three) and the wrong-foot costs about a
third less on every dial: surpRead 0.85->0.6, surpSlow 0.45->0.3, surpFlub
0.35->0.22. The whiff-window test pin rides down with it (0.25->0.15); the
slower-than-the-coach pin still holds.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-28 16:34:38 +02:00
wuwiandClaude Fable 5 43784c5b33 The staff learn your habits, the walk-on waits for your tap, and the club opens a practice court
Four answers to one phone report:
- THE READ: repeat a lane and the opponent anticipates it; break the habit
  and it is wrong-footed for that ball — scaled by rung, so surprise is the
  weapon that opens the ladder past the HEAD COACH (TUNE block THE READ).
- The opponent walk-on is a real modal now (head, punchline, tap to dismiss)
  in arcade, tournament and training — readable without a clock.
- THE TRAINING COURT: third card under ZOMBIE TENNIS, pick any rung, one
  game at a time, the felled body walks straight back on, nothing banks,
  bleeding out is a popup instead of a death. mode_train.png rolled in the
  banner style.
- MOVEMENT READS: sprinting feet kick per-court dust and fast bodies leave
  a two-ghost speed streak (trailMin gates both; feel verdict pending).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-28 16:08:18 +02:00
wuwiandClaude Opus 5 a42dc6fc81 The lob is out: a gesture the opponent's position redefines is unpredictable
Oliver, on the build: "all good but the lob. please remove it - this is
totally unpredictable."

Reverted in full — oppAtNet, farBody, TUNE.lobArm, TUNE.lobApex,
SHOT_DEPTH.lob, the branch in doHit and its two tests. The forward flick is
the smash it always was. Everything else from that commit stays: the pace
feedback (streak, crack, camera kick) and the death match's blood alignment.

The reason is worth more than the code, and PROJECT.md carries it as a rule:
a gesture whose MEANING depends on where the opponent is standing is
unpredictable however well the shot is tuned. It is the same objection
input.js already states about the slingshot — the direction you moved is the
thing you asked for, no invisible modes — and the written comparison let
option (c) past it on the argument that context cannot mis-fire. It can: you
do not track where the far body is standing while a ball is coming at you, so
one thumb movement produced two shots for reasons you were not watching. The
LOB word at contact explained the surprise; it did not prevent it.

If it comes back it needs a gesture of its own. The ballistics are solved and
are in the history at 4f4534e.

111 tests pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-22 13:13:56 +02:00
wuwiandClaude Opus 5 4f4534e272 The lob is the flick you already have, and a game stops being a death
Three from Oliver.

THE LOB. He picked option (c) from the written comparison — "no additional
gesture to learn" — and corrected the premise: the zombie does come to the
net (racketAI chases to the far service line, the classic CPU to z 9.5). So
the forward flick keeps its one meaning, "put it deep behind him", and the
game picks the trajectory that actually does that: a smash while he is back,
over his head once he is in. oppAtNet() is the whole switch, and it reads the
BODY, not O — in the match O is the invisible tennis body and only moves
between points, so it would have answered "on his mark" all rally.

lobArm 7.0 m is measured, not taste: a lob peaking at lobApex passes a man 7 m
from the net at almost exactly CONTACT.maxY, so the shot arms exactly as far
out as it can still clear him. The pace is derived from the height, so the same
arc comes off the baseline and off mid-court, and SHOT_DEPTH.lob is deeper than
deep — at the ordinary deep target the ball passed 2.05 m over him at the edge
of the band, which is inside his reach.

PACE, SEEN FROM THE STRIKER. "the opponent sees the ball really fast - the
player barely sees the speed increase." Measured: the ball is the same ball on
both phones, the camera is not — it compresses your own ball into the distance
and expands theirs as it arrives. Pace untouched; three channels added on the
striker's side: the ghost streak now reads pace (3-8 ghosts, was 3 at every
speed), the crack is pitched by it, and a fast ball kicks the camera. The
serve's own ladder is 49.2 plain / 50.8-52.4 hard, because serveArc's loft
margin takes 10% of the +9 back — widening that is his call, and PROJECT.md
records the knob and the risk.

THE DEATH MATCH. "each player has 8 blood points but the player explodes for
the winner already at 0:40." Two clocks: a game is four points, a life is
eight. A won game is only a game now (flash, serve, heal); the burst moved to
the death itself, on the dead message, where both phones agree. The summary
waits 1.4 s for the gore, because update() freezes the world on G.done and
would have hung a still frame of an explosion behind the trophy screen. And
bleedO tracks THEIR blood off the pos stream instead of our points-this-game,
which reset every game and ignored the heal.

4 new tests, 113 pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-22 12:58:48 +02:00
wuwiandClaude Opus 5 6e07dc74bb The receiver waits behind his line, and a missed serve buys a second one
Oliver, three notes; two of them are here.

"when the opponent serves the player does not stand at the baseline - but
that's how it should be." setupPoint's receiving marks sat over a metre
INSIDE the court on both sides (22.5 near, 1.2 far) — where you stand to
attack a second serve, not where you wait for a first one. They mirror the
server's depth now, in the one line every serving mode shares.

"when serving results in an out there should be a second service... the
umpire should announce 2nd service and the player must get another chance."
True in tennis mode since forever and absent everywhere else: the duel, the
match and the death match had no service rule at all, so a serve long or
into the net was judged as an ordinary ground stroke — a point, and in the
match a point costs blood. The out branch of onBounce's interlude block
routes a serve to fault() instead, so the second serve, the HUD line, the
umpire and the double fault all come for free.

Three things it dragged out with it:
 - fault()'s double-fault branch called pointTo — TENNIS scoring — which in
   an interlude banks a number and skips the mode that owns the court. It
   goes through resolveInterlude there now. Nothing had ever reached that
   line from a duel or a match, because those modes could not fault.
 - startDuel never reset G.serveNum: harmless while a duel could not fault,
   the next duel's death loop the moment it can.
 - the match puts the body back on its receiving mark for the second serve
   (placeReceiver, split out of matchReady, which resets the serve number).

"Fault. Second service." is one voiceSeq — a second voice() call would be
dropped, one man one microphone. New recording via the usual say/afconvert.

The service BOX is still not enforced in the match, deliberately: this is
strictly more forgiving than what shipped. The lob gesture is OPEN 14.

4 new tests, 110 pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-22 09:40:48 +02:00
wuwiandClaude Opus 5 3d7e66449a A new run rewinds the clock; the camera flashes did not rewind with it
Oliver: "I start a new zombie tennis match. when playing and scoring no
fotograph flashes appear, but at a random place in the crowd some frozen
midflash circle is visible. after a few points the flashes unfreeze."

Both halves are the same cause. resetState() puts G.idleT back to 0 for every
new run, while the flash list and its "next at" live in render.js's own closure
and survive it. So the new run inherited a flashNext from the old one — no
photographs at all until the clock climbed back past it — and any flash still in
flight was left with a t0 in its own future, which makes its fade
1 - (now - t0)/life permanently greater than 1: a shutter stuck at full
brightness in the stand until something else cleared it.

tickFlashes now notices the clock moving backwards and clears and re-arms. One
guard, in the only place that owns the state, so it covers any future mode that
rewinds the clock as well. Verified on the canvas: after a rewind there is no
permanently-bright pixel in the stand, and the photographers resume within
seconds instead of staying dark.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 15:04:33 +02:00
wuwiandClaude Opus 5 ca3e72d0a9 The white flashing was the top fade: the live raisers were not repainting it
Oliver: "what is this white flashing while applause in the upper right corner of
the crowd". With the photographers switched off entirely it was still there, so
it was never them — it was mine, from this morning. The top fade is baked into
the cached stand, and drawRaisers was painting a spectator's box straight over
it with only the dim applied. Every time that spectator stood up his box lost
the fade and lit up; every time he sat down it came back. Measured on the canvas:
a 146-luminance jump, twice a second, in exactly the two places raisers sit near
the top of the picture.

Both stand treatments now come from one function each and are applied in both
places. The gradient is built in user space, so filling a raiser's sub-rect with
it picks up the right alpha for free. Probed again after the fix: zero bright
pops during applause and during the wave, against the four hot spots before.

While chasing it, two real detector faults turned up and are fixed too: a blob
may not be a pale block (a re-rolled cream shirt is "new material" by the
stand-turned-person test but nothing rises), and it may not cover something
already bright (a genuine arm drawn over a white shirt deletes a white block,
which flashes just as hard). 94 raisers survive both rules.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 14:55:13 +02:00
wuwiandClaude Opus 5 93c4a21be5 The applause moves down out from under the scoreboard
Oliver: "in the upper parts of the crowd there seems to be a lot more action
going on, but I can barely see it because the hud is covering it." Measured, 31
of 134 raisers were being drawn under the board or the blood panel — a quarter
of every cheer spent where nobody can see it.

The cause is structural: a raise is only detectable where there is empty stand
above somebody's head, and the sheet only offers that above the top row of each
seating block. One of those rows is at the very top of the picture, which is
where the scoreboard lives.

So both ends are fixed. The two arms-up rolls now ask for the standers to be in
the lower two thirds only, and the pass drops any raiser whose box centre falls
under the board or the blood panel. 92 of 101 raisers now sit below the board's
bottom edge, against 103 of 134 before — and because "how many join in" is a
fraction of the table, pruning the invisible ones makes every cheer denser where
it counts. Measured churn during a won game: 2-5.5% of the mid and lower stand
changing every 90 ms, against 0.1-1% under the HUD.

The re-roll also came back with 41 spectators owning both raised poses, up from
28, so more of them clap rather than just going up and down.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 14:40:22 +02:00
wuwiandClaude Opus 5 b34a9159a7 The HUD lifts off the crowd, and the frame is dark at both ends
A drop shadow under the scoreboard, the game/sets panel, the BLOOD panel and
every rack tile — and the same numbers in CSS on the quit chip, because half the
overlay lifting off the backdrop while the other half sits flat on it is worse
than neither. Canvas shadow offsets are device pixels and are not scaled by the
transform, so hudShadow multiplies by RES; without that a 3px shadow is three
quarters of a logical pixel on a 4x phone.

And the top of the frame now fades out of black the way the bottom already fades
into it (TUNE.standTop) — Oliver: "the bottom of the screen falls off into a
black tone.. please make the top come out of a black tone". The stand comes up
out of the dark instead of being cut off at the top edge, which also gives the
scoreboard something to sit on.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 14:21:12 +02:00
wuwiandClaude Opus 5 d0c9ea4f0d PROJECT: the two stand dims do not stack, and centre court is graded off a lifted target
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 14:12:33 +02:00