Give every opponent a style, and split the soft ball into drop and lob
Step 2 of the rally rework: eight styles as dials on the opponent rows — pusher, retriever, slicer, big hitter, baseliner, touch, net rusher, all-court — read where the CPU chooses its shot (moonball, drop, lob, spin), plans its approach and serves (serve and volley sprints in). The walk-on names the style in one line so the player can plan against it. A player lobbed at the net now backpedals and blocks, like the CPU. Oliver on v363: the down-flick "always felt like a lob". The length is read at contact and a thumb has usually travelled on; on the continuum even 50 px floated the ball. Below dropMax (90 px) it is now the drop, above it the lob. Also un-flakes the machine-squeak test (seed 31337). v364. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
72760a9d1e
commit
180288ef8f
7 files changed
+268
-42
No files matched your search
+42
-18
@@ -94,6 +94,7 @@ export const TUNE = {
|
||||
lowVolleyQ: 0.3, // quality cap on a volley struck below the tape: you can only block it up
|
||||
scoopY: 0.45, // m: a ball met lower than this after its bounce is dug out, same cap
|
||||
// THE SOFT BALL (step 3): the down-flick, its length = how far. Drop at one end, lob at the other.
|
||||
dropMax: 90, // px: a down-flick shorter than this is the DROP, this long or longer the LOB
|
||||
dropNet: 2.0, // m past the net a shortest down-flick lands
|
||||
dropApex: 2.0, // m its arc peaks at
|
||||
dropSpin: 0.3, // share of its pace a drop keeps through the bounce (backspin: it dies)
|
||||
@@ -1220,54 +1221,77 @@ export const bossArt = ch => ch % 10 === 0 ? BOSS_ART[BOSS_ART.length - 1]
|
||||
// a ladder is a hierarchy, not an arrival order, so the spectator belongs at the bottom with the
|
||||
// ball boy and the head coach belongs up beside the club pro. Every new rung wears a family rolled
|
||||
// off the body it already has in the gore mode, same rule as the twelve before it.
|
||||
// THE STYLES (step 2 of the rally rework, 2026-10-14 — PROJECT.md "THE RALLY GOES FLAT"). How a body
|
||||
// PLAYS, as dials, so a style is data and never a branch. Read in trySwing (what it does with the
|
||||
// ball), rxPlan (when it comes in), racketAI (serve and volley) and doHit (the spin it carries).
|
||||
// Context-chosen shots are fine HERE — the one-gesture rule binds the player's thumb, and a lob or
|
||||
// a drop announces itself in its own arc.
|
||||
// loft share of rally balls sent back as a high deep MOONBALL (the soft ball's long end)
|
||||
// drop chance of a DROP when it stands inside its own baseline and you stand behind yours
|
||||
// lob chance of a LOB when you stand at the net
|
||||
// spin the bounce its rally balls carry: >1 heavy topspin (kicks up and on), <1 slice (low)
|
||||
// charge m it lowers its short-ball line: it comes in on balls a baseliner would stay back for
|
||||
// sv serves and volleys
|
||||
// tag the walk-on's one line about it — the player has to know what he is facing to plan
|
||||
export const STYLES = {
|
||||
pusher: { loft: 0.45, lob: 0.5, tag: 'PUSHER — IT ALL COMES BACK HIGH' },
|
||||
retriever: { loft: 0.15, lob: 0.35, tag: 'RETRIEVER — RUNS DOWN EVERYTHING' },
|
||||
slicer: { spin: 0.8, drop: 0.25, tag: 'SLICER — LOW BALLS THAT STAY DOWN' },
|
||||
power: { spin: 1.15, tag: 'BIG HITTER — HEAVY TOPSPIN' },
|
||||
baseliner: { spin: 1.07, lob: 0.25, tag: 'BASELINER — PAINTS THE LINES' },
|
||||
touch: { drop: 0.45, lob: 0.55, tag: 'TOUCH — DROPS YOU WHEN YOU HANG BACK' },
|
||||
rusher: { charge: 1.6, sv: true, tag: 'NET RUSHER — PASS HIM OR LOB HIM' },
|
||||
allcourt: { loft: 0.08, drop: 0.2, lob: 0.4, spin: 1.07, charge: 0.8, tag: 'ALL-COURT — A BIT OF EVERYTHING' },
|
||||
};
|
||||
|
||||
const OPPONENTS = [
|
||||
{ short: 'SHAMBLER', name: 'THE SHAMBLER', fam: 'sh',
|
||||
{ short: 'SHAMBLER', style: 'pusher', name: 'THE SHAMBLER', fam: 'sh',
|
||||
line: 'HE WAS HERE BEFORE ANY OF THIS.', speed: 0.72, pace: 0.72, wide: 0.75, flub: 1.35 },
|
||||
{ short: 'BALL BOY', name: 'THE BALL BOY', fam: 'bb',
|
||||
{ short: 'BALL BOY', style: 'retriever', name: 'THE BALL BOY', fam: 'bb',
|
||||
line: 'HE STILL FETCHES. HE JUST BITES NOW.', speed: 0.80, pace: 0.78, wide: 0.82, flub: 1.25 },
|
||||
{ short: 'PUNTER', name: 'THE SPECTATOR', fam: 'sp',
|
||||
{ short: 'PUNTER', style: 'pusher', name: 'THE SPECTATOR', fam: 'sp',
|
||||
line: 'HE PAID FOR THE WHOLE SEAT.', speed: 0.86, pace: 0.82, wide: 0.86, flub: 1.18 },
|
||||
{ short: 'KEEPER', name: 'THE GROUNDSKEEPER', fam: 'gk',
|
||||
{ short: 'KEEPER', style: 'slicer', name: 'THE GROUNDSKEEPER', fam: 'gk',
|
||||
line: 'HE MOWED THIS LAWN FOR THIRTY YEARS.', speed: 0.9, pace: 0.88, wide: 0.9, flub: 1.05 },
|
||||
// 5: the heavy of the ground staff. Slow legs, but everything he touches comes back flat and hard
|
||||
{ short: 'ROLLER', name: 'THE ROLLER MAN', fam: 'rm', h: 1.95,
|
||||
{ short: 'ROLLER', style: 'power', name: 'THE ROLLER MAN', fam: 'rm', h: 1.95,
|
||||
line: 'HE FLATTENED THIS COURT. THEN THE REST.', speed: 0.88, pace: 0.98, wide: 0.92, flub: 1.0 },
|
||||
{ short: 'STEWARD', name: 'THE STEWARD', fam: 'st',
|
||||
{ short: 'STEWARD', style: 'pusher', name: 'THE STEWARD', fam: 'st',
|
||||
line: 'HE IS ASKING YOU TO LEAVE.', speed: 0.98, pace: 0.95, wide: 1.0, flub: 0.98 },
|
||||
// 7-8: the entertainment, and then the legs
|
||||
{ short: 'MASCOT', name: 'THE MASCOT', fam: 'ma',
|
||||
{ short: 'MASCOT', style: 'touch', name: 'THE MASCOT', fam: 'ma',
|
||||
line: 'THE CROWD STILL LOVES HIM.', speed: 1.02, pace: 0.98, wide: 1.05, flub: 0.95 },
|
||||
{ short: 'RUNNER', name: 'THE RUNNER', fam: 'ru',
|
||||
{ short: 'RUNNER', style: 'retriever', name: 'THE RUNNER', fam: 'ru',
|
||||
line: 'HE COVERS THE COURT. ALL OF IT.', speed: 1.22, pace: 1.0, wide: 1.05, flub: 0.92 },
|
||||
// 9-12: the trades and the officials — width arrives here
|
||||
{ short: 'SELLER', name: 'THE STRAWBERRY SELLER', fam: 'sb',
|
||||
{ short: 'SELLER', style: 'slicer', name: 'THE STRAWBERRY SELLER', fam: 'sb',
|
||||
line: 'CREAM WITH THAT?', speed: 1.04, pace: 1.06, wide: 1.10, flub: 0.90 },
|
||||
{ short: 'STRINGER', name: 'THE STRINGER', fam: 'sg',
|
||||
{ short: 'STRINGER', style: 'power', name: 'THE STRINGER', fam: 'sg',
|
||||
line: 'HE STRUNG YOUR RACKET. HE KNOWS ITS LIMIT.', speed: 1.06, pace: 1.12, wide: 1.14, flub: 0.86 },
|
||||
{ short: 'JUDGE', name: 'THE LINE JUDGE', fam: 'lj',
|
||||
{ short: 'JUDGE', style: 'baseliner', name: 'THE LINE JUDGE', fam: 'lj',
|
||||
line: 'EVERY BALL OF YOURS IS OUT.', speed: 1.05, pace: 1.08, wide: 1.28, flub: 0.84 },
|
||||
{ short: 'UMPIRE', name: 'THE CHAIR UMPIRE', fam: 'cu',
|
||||
{ short: 'UMPIRE', style: 'rusher', name: 'THE CHAIR UMPIRE', fam: 'cu',
|
||||
line: 'HIS CALL WAS ALWAYS FINAL.', speed: 1.08, pace: 1.14, wide: 1.26, flub: 0.80 },
|
||||
{ short: 'PHYSIO', name: 'THE PHYSIO', fam: 'ph',
|
||||
{ short: 'PHYSIO', style: 'touch', name: 'THE PHYSIO', fam: 'ph',
|
||||
line: 'HE KNOWS EXACTLY WHERE IT HURTS.', speed: 1.12, pace: 1.16, wide: 1.24, flub: 0.78 },
|
||||
{ short: 'COACH', name: 'THE HEAD COACH', fam: 'hc',
|
||||
{ short: 'COACH', style: 'allcourt', name: 'THE HEAD COACH', fam: 'hc',
|
||||
line: 'HE TAUGHT YOU EVERY SHOT YOU HAVE.', speed: 1.20, pace: 1.20, wide: 1.22, flub: 0.72 },
|
||||
{ short: 'CLUB PRO', name: 'THE CLUB PRO',
|
||||
{ short: 'CLUB PRO', style: 'rusher', name: 'THE CLUB PRO',
|
||||
line: 'HE GAVE LESSONS ON THIS COURT.', speed: 1.16, pace: 1.22, wide: 1.20, flub: 0.70 },
|
||||
// THE BRUTE, not "THE BULK": the gore mode's stats screen has called this body THE BRUTE since it
|
||||
// was the pole-dragger (ENEMIES.g below), and one character may not carry two names.
|
||||
// Final-league returns hunt the open court (smart) and have individual quality bands. Earlier
|
||||
// opponents keep random lanes and racketQMax. Brute's legs must clear even league 4's fastest.
|
||||
{ short: 'BRUTE', name: 'THE BRUTE', fam: 'br', h: 2.0,
|
||||
{ short: 'BRUTE', style: 'power', name: 'THE BRUTE', fam: 'br', h: 2.0,
|
||||
line: 'NOTHING GETS PAST HIM TWICE.', speed: 1.12, pace: 1.34, wide: 1.24, flub: 0.66, smart: 0.50, qMin: 0.62, qMax: 0.76 },
|
||||
{ short: 'DIRECTOR', name: 'THE DIRECTOR', fam: 'dr',
|
||||
{ short: 'DIRECTOR', style: 'rusher', name: 'THE DIRECTOR', fam: 'dr',
|
||||
line: 'HE CAN HAVE YOU REMOVED.', speed: 1.26, pace: 1.28, wide: 1.28, flub: 0.60, smart: 0.70, qMin: 0.68, qMax: 0.83 },
|
||||
// 18: the man whose name is on the honours board. `h` is WORLD height (zombieH reads it, so the
|
||||
// serve aims at the body actually drawn): four family rolls could not make the art itself bulky
|
||||
// against the pose sheet — see _famroll.py's pair-reference note — so his size is stature.
|
||||
// Six new rungs stand between him and the start now, so he took the step up Oliver asked for:
|
||||
// he reads the court faster, hits harder, paints wider, and misses half as often as he did.
|
||||
{ short: 'CHAMPION', name: 'THE CHAMPION', fam: 'ch', h: 2.1,
|
||||
{ short: 'CHAMPION', style: 'allcourt', name: 'THE CHAMPION', fam: 'ch', h: 2.1,
|
||||
line: 'HE RETURNS EVERYTHING.', speed: 1.45, pace: 1.42, wide: 1.36, flub: 0.38, smart: 0.85, qMin: 0.74, qMax: 0.90 },
|
||||
];
|
||||
|
||||
|
||||
Reference in new issue
Block a user