Three modes, picked up front — and CLASSIC TENNIS is one of them
The setup screen picks WHAT before WHO and WHERE. Three rows out of a table, in the court row's own markup: CLASSIC TENNIS is not the opener with the turn switched off and left there — it is the mode the other two are built on, so it gets real match scoring: six games with a two-game lead is a set, best of TUNE.classicSets is the match, the board shows the sets column (hidden until now outside splatter) and the bar under it says which set this is. The CPU stops rubber-banding and flubs less: aiFlubLoss exists to get a new player PAST the opener, and a mode whose whole promise is a real match cannot be won by an opponent that falls apart the moment he goes ahead. No tiebreak — 6-6 plays on to 8-6. Marked ponytail. That needed a second ENDING, the one with no corpse in it: G.done, the same summary and gated tap as the death screen, its own words (GAME, SET, MATCH. / BEATEN.) and the man still standing. The three endings now share one block rather than three copies of it. Caught by the first classic test: stats banked only the FIRST classic run of a session. `banked` was cleared by the next KILL, and a classic match has no kills in it at all — every later match was swallowed by the double-bank guard. beginRun() is the honest reset and restartRun calls it. ZOMBIE TENNIS from the menu is one line; the back room keeps its door. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
66fdad3d74
commit
c89d4a3ff9
31 files changed
+472
-123
No files matched your search
+23
-1
@@ -31,6 +31,12 @@ export const TUNE = {
|
||||
// faces an opponent that visibly starts falling apart rather than a wall.
|
||||
aiQualityMin: 0.45, aiQualityMax: 0.72, aiFlub: 0.18,
|
||||
aiFlubLoss: 0.08,
|
||||
// ...and in CLASSIC TENNIS the same CPU has to last a whole match rather than one game, so the
|
||||
// rubber band is turned off there (aiFlubLoss would have him in pieces by the second set) and the
|
||||
// cap is raised: a mode whose whole promise is "real tennis" cannot be won by attrition.
|
||||
classicSets: 2, // sets to win the match — best of three
|
||||
classicQualityMax: 0.86,
|
||||
classicFlub: 0.10,
|
||||
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
|
||||
@@ -485,6 +491,9 @@ export const APPLAUSE_T = 4.2;
|
||||
// It is deliberately long. Oliver: "the movies surround and bite them, blood spills, and then the
|
||||
// person basically drowns in the zombies" — that cannot happen in half a second.
|
||||
export const EATEN_BURST = 2.3, EATEN_SUMMARY = 3.6, EATEN_RESTART = 4.6;
|
||||
// ...and the same two beats for the ending with no corpse in it (a classic match played out). It is
|
||||
// shorter because there is no animation to sit through — only the crowd finishing, and the summary.
|
||||
export const DONE_RESTART = 2.0;
|
||||
export const EATEN_FEED_R = 1.15; // m — how tight the feeding ring packs around the body
|
||||
|
||||
// UI accent colours, one place so the whole HUD can be re-tuned at once. Deliberately
|
||||
@@ -539,6 +548,19 @@ export const COURTS = [
|
||||
// dx/apex are optional and override BOUNCE_IDLE's pair: she reaches further out and higher than he
|
||||
// does, so the pre-serve ball has to meet a different palm. Same rule as BOUNCE_IDLE — measured off
|
||||
// the drawn hand by eye, not calculated.
|
||||
// THE THREE MODES, picked before a run and nowhere else. Pure DATA, like TOURS and COURTS: adding
|
||||
// one is a row here plus its dispatch line in main.start(), never markup.
|
||||
// classic — real tennis, best of TUNE.classicSets sets, nothing ever turns
|
||||
// zombie — the exhibition match (beats.startMatch): one game per opponent, blood only
|
||||
// gore — the original: the opening match TURNS and you survive what walks on
|
||||
// `tile` is an existing sprite rather than new art on purpose — each one is the thing you will be
|
||||
// looking at for the whole mode, which is a better advert than a drawn icon of it.
|
||||
export const MODES = [
|
||||
{ id: 'classic', name: 'CLASSIC TENNIS', sub: 'REAL TENNIS · BEST OF THREE SETS', tile: 'o_swing1' },
|
||||
{ id: 'zombie', name: 'ZOMBIE TENNIS', sub: 'ONE GAME PER OPPONENT · BLOOD ONLY', tile: 'z_r1' },
|
||||
{ id: 'gore', name: 'SPLATTER GORE', sub: 'THE MATCH TURNS · SURVIVE IT', tile: 'z_boss1' },
|
||||
];
|
||||
|
||||
export const TOURS = [
|
||||
{ id: 'men', name: "MEN'S SINGLES", tile: 'tour_m', sfx: null },
|
||||
{ id: 'women', name: "WOMEN'S SINGLES", tile: 'tour_w', sfx: '_f', dx: 0.46, apex: 0.78 },
|
||||
@@ -666,7 +688,7 @@ export const ENEMIES = {
|
||||
// per run, and he is NOT the racket zombie below — that one keeps coming back. Missing this row
|
||||
// is what put a second "SHAMBLER" on the screen: an unmapped key falls back to the shambler art.
|
||||
c: { name: 'THE OPPONENT', spr: 'z_c1', sub: 'HE TURNED MID-MATCH' },
|
||||
r: { name: 'THE CLUB PRO', spr: 'z_c1', sub: 'STILL HAS A BACKHAND' },
|
||||
r: { name: 'THE CLUB PRO', spr: 'z_r1', sub: 'STILL HAS A BACKHAND' },
|
||||
m: { name: 'THE BALL MACHINE', spr: 'z_mach1', sub: 'FIRES BACK · FURNITURE' },
|
||||
};
|
||||
for (let i = 0; i < BOSS_ART.length; i++) // the chapter bosses, named where they already are
|
||||
|
||||
Reference in new issue
Block a user