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>
This commit is contained in:
1 parent
3d7e66449a
commit
6e07dc74bb
7 files changed
+182
-19
No files matched your search
+21
-12
@@ -71,6 +71,10 @@ export function startDuel() {
|
||||
G.weapon = 0; // tennis rules for the duel: plain balls, no tins
|
||||
G.mgT = 0; endBurst(); pellets.length = 0; // ...and a burst in progress is over. The umpire has restored order.
|
||||
G.interlude = { introT: 2.4 }; // the intro beat: serving blocked while the umpire announces
|
||||
G.serveNum = 1; // ...on a FIRST serve. A duel now faults (gameplay's onBounce), and
|
||||
// nothing else resets it here — a fault in the last duel would have
|
||||
// armed second-serve in the next one, which is the death loop this
|
||||
// file already carries a note about, one mode over.
|
||||
G.boy = null; // ...and the mop boy is waved off the court (sendBoy is barred for the duration)
|
||||
ball.active = false; setupPoint();
|
||||
zombieDuck(true); // fade the zombie music out for the 1:1 duel
|
||||
@@ -420,18 +424,23 @@ export function matchReady() { // exported for net.js: a paired death match re-p
|
||||
// when it is the one serving. setupPoint has just placed O — the invisible tennis body the serve
|
||||
// machinery is driven off — on exactly that spot, so the zombie simply takes it. One source for
|
||||
// the mark, which is what stops the picture and the ballistics disagreeing about where it stands.
|
||||
if (rz) {
|
||||
// WHERE A RETURNER ACTUALLY STANDS. The tennis mark is 1.3 m off the centre line, and from
|
||||
// there he bisects nothing: the T is one step away and the corner of the box is four. Measured
|
||||
// headless, a flush serve with full sway into that corner was returned 0 times out of 20 at
|
||||
// EVERY rung of the ladder — Oliver: "the first ten zombies can be killed the moment you
|
||||
// serve". He now stands out on the bisector of the two serves he has to cover, which is what a
|
||||
// real receiver does, and O takes the same mark so the picture and the ballistics still agree.
|
||||
if (score.server === 'P') O.x = Math.sign(O.x || 1) * TUNE.matchRecvX;
|
||||
rz.x = O.x; rz.z = score.server === 'O' ? O.z : 1.0;
|
||||
rz.pose = 'idle'; rz.swingT = -1; rz.swingCool = 0.3;
|
||||
rz.showFrame = null; rz.serveHold = 0;
|
||||
}
|
||||
if (rz) placeReceiver(rz);
|
||||
}
|
||||
|
||||
// WHERE A RETURNER ACTUALLY STANDS. The tennis mark is 1.3 m off the centre line, and from there he
|
||||
// bisects nothing: the T is one step away and the corner of the box is four. Measured headless, a
|
||||
// flush serve with full sway into that corner was returned 0 times out of 20 at EVERY rung of the
|
||||
// ladder — Oliver: "the first ten zombies can be killed the moment you serve". He now stands out on
|
||||
// the bisector of the two serves he has to cover, which is what a real receiver does, and O takes
|
||||
// the same mark so the picture and the ballistics still agree.
|
||||
// Split out of matchReady because a SECOND SERVE needs the same mark without matchReady's rest:
|
||||
// that call resets G.serveNum, which is the one thing a fault has just spent (see fault()).
|
||||
export function placeReceiver(rz = zombies.find(z => z.variant === 'r')) {
|
||||
if (!rz) return;
|
||||
if (score.server === 'P') O.x = Math.sign(O.x || 1) * TUNE.matchRecvX;
|
||||
rz.x = O.x; rz.z = score.server === 'O' ? O.z : 1.0;
|
||||
rz.pose = 'idle'; rz.swingT = -1; rz.swingCool = 0.3;
|
||||
rz.showFrame = null; rz.serveHold = 0;
|
||||
}
|
||||
|
||||
// ---- THE FALL. A point won knocks the body DOWN and it drags itself back up MINUS THE LIMB.
|
||||
|
||||
Reference in new issue
Block a user