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>
This commit is contained in:
1 parent
9133c0d351
commit
16bf5b9fed
12 files changed
+406
-71
No files matched your search
+13
-7
@@ -1,6 +1,6 @@
|
||||
// Game logic: ballistics, swings, serves, rules/scoring, AI, and the per-frame update.
|
||||
|
||||
import { TUNE, UI, bossArt, APPLAUSE_T, EATEN_BURST, EATEN_SUMMARY, COURT, CONTACT, VOLLEY, ZONE_DEPTH, HOME, P_BOUNDS, TOSS, SHOT_DEPTH, POSE_TIME, BOUNCE_IDLE, zombieH, Z_CHEST , WEAPONS, RACK_MAX , BARREL_WEAPONS, racketStage, matchWound, CUP_ROUNDS, LADDER } from './config.js';
|
||||
import { TUNE, UI, bossArt, APPLAUSE_T, EATEN_BURST, EATEN_SUMMARY, COURT, CONTACT, VOLLEY, ZONE_DEPTH, HOME, P_BOUNDS, TOSS, SHOT_DEPTH, POSE_TIME, BOUNCE_IDLE, zombieH, Z_CHEST , WEAPONS, RACK_MAX , BARREL_WEAPONS, racketStage, matchWound, LADDER } from './config.js';
|
||||
import { P, O, ball, score, crowd, G, court, tour, other, deuceSide, setupPoint, sendBoy, resetState } from './state.js';
|
||||
import { audio, pock, swish, whoosh, applause, voice, voiceSeq, sfx, aww, zombieMusic, zombieDuck, menuDuck, splat, shriek, boom } from './audio.js';
|
||||
import { haptic } from './haptics.js';
|
||||
@@ -158,6 +158,12 @@ export const swayOf = aim => aim.mag === undefined ? aim.ax
|
||||
// as flat rather than resetting the lesson it just failed to use.
|
||||
export const flatCount = (streak, aim) => (aim && Math.abs(swayOf(aim)) > 0.15 ? 0 : streak + 1);
|
||||
|
||||
// THE LEAGUES' pace tier: solo match modes run faster per league, everything else is factor 1 —
|
||||
// league 1 IS the shipped game (the calibration rule). Multiplies ball pace on BOTH rackets
|
||||
// (doHit, both serve paths) and the opponent's legs (rzChaseSpeed), so the world speeds up as one
|
||||
// piece and the reach tests keep believing the legs they measure. Netplay is a human and exempt.
|
||||
export const leaguePace = () => G.match && !G.net ? 1 + (G.league - 1) * TUNE.leaguePace : 1;
|
||||
|
||||
function doHit(ch, quality, aim) {
|
||||
// aim: {ax: -1|0|1, az: -1|0|1} from held keys / flick at contact (az -1 = deep, 1 = short)
|
||||
const toFar = ch.side === 'P';
|
||||
@@ -173,7 +179,7 @@ function doHit(ch, quality, aim) {
|
||||
ball.z = ch.z + (toFar ? -0.3 : 0.3);
|
||||
const err = () => (Math.random() * 2 - 1) * TUNE.aimError * (1 - quality);
|
||||
let tx, tz, aimedLane = false, sway = 0;
|
||||
let s = TUNE.baseShotSpeed + quality * TUNE.qualityShotSpeed;
|
||||
let s = (TUNE.baseShotSpeed + quality * TUNE.qualityShotSpeed) * leaguePace();
|
||||
const ft = G.mode !== 'tennis' && !G.interlude && ch.side === 'P' // the duel aims like tennis: lanes, not free-aim
|
||||
? (aim && aim.fx !== undefined ? flickTarget(ch, aim) : { zt: zombieTarget(aim) }) : null;
|
||||
if (ft && ft.zt) {
|
||||
@@ -379,7 +385,7 @@ function splatServeTarget(sv, aim, s, x0, z0, ex = 0) {
|
||||
// The launch itself: topspin gravity, then trade pace for arc until the net is genuinely cleared.
|
||||
// A shambler just the other side of the tape is only reachable on a loft.
|
||||
function serveArc(x0, y0, z0, tx, tz, quality, ty = 0.08, hard = 0) { // hard: the aimed strike's 0..1
|
||||
let s = TUNE.serveSpeed + quality * TUNE.serveQualitySpeed + hard * TUNE.serveHard;
|
||||
let s = (TUNE.serveSpeed + quality * TUNE.serveQualitySpeed + hard * TUNE.serveHard) * leaguePace();
|
||||
// the harder you hit it, the harder it dips — and the HARD serve dives harder still
|
||||
// (serveHardSpin), which is what buys its net clearance: MEASURED with the replica solver,
|
||||
// +9 m/s with the old dip was cut BELOW the plain flush by the loft loop (47.2 vs 49.3).
|
||||
@@ -468,7 +474,7 @@ export function hitServe(sv, quality, aim = null, perfect = quality > 0.999) {
|
||||
const sx = aim && aim.sx !== undefined ? aim.sx : 0;
|
||||
const hard = (aim && aim.hard) || 0;
|
||||
const pace = hard * TUNE.serveHard;
|
||||
const s = TUNE.serveSpeed + quality * TUNE.serveQualitySpeed + pace;
|
||||
const s = (TUNE.serveSpeed + quality * TUNE.serveQualitySpeed + pace) * leaguePace();
|
||||
// splatServeTarget + serveArc are shared with previewServe, so the arc drawn under your thumb
|
||||
// is the one the ball flies. Only the aim error is added here and left out of the preview.
|
||||
const st = splatServeTarget(sv, aim, s, ball.x, ball.z, (Math.random() * 2 - 1) * (1 - quality) * 0.8);
|
||||
@@ -1126,10 +1132,10 @@ function matchFallWillPlay() {
|
||||
const cup = G.match.cup;
|
||||
const gameWin = score.pts[0] + 1 >= 4 && score.pts[0] + 1 - score.pts[1] >= 2;
|
||||
// a game win is only THE KILL when it ends the round — a cup mid-round game keeps the body
|
||||
if (gameWin) return !cup || cup.g + 1 >= CUP_ROUNDS[cup.round].need;
|
||||
if (gameWin) return !cup || cup.g + 1 >= cup.rounds[cup.round].need;
|
||||
// the same divisor matchPoint will use — the first version kept the deathmatch /4 and in the
|
||||
// cup it promised falls that matchPoint then didn't deliver (a route to the stale-latch lock)
|
||||
const div = 4 * ((cup && CUP_ROUNDS[cup.round].need) || 1);
|
||||
const div = 4 * ((cup && cup.rounds[cup.round].need) || 1);
|
||||
const after = Math.max(1, rz.hp - Math.ceil(rz.hpMax / div));
|
||||
return racketStage({ hp: after, hpMax: rz.hpMax }) !== racketStage(rz); // a limb
|
||||
}
|
||||
@@ -1145,7 +1151,7 @@ function matchFallWillPlay() {
|
||||
// One definition, because the read boost, the wrong-foot slow and the surprise whiff must all
|
||||
// answer "how smart is this one" identically or the twist pulls in three directions at once.
|
||||
const learnK = rz => G.match && !G.net ? Math.min(1, Math.max(0, (G.match.opp - 1) / (LADDER - 1))) : 0;
|
||||
const rzChaseSpeed = rz => TUNE.racketSpeed
|
||||
const rzChaseSpeed = rz => TUNE.racketSpeed * leaguePace()
|
||||
* (G.match ? (TUNE.matchSpeedBase + (G.match.opp - 1) * TUNE.matchSpeedPer) * ((rz.foe && rz.foe.speed) || 1) : 1)
|
||||
* (G.wasServe && ball.lastHit === 'P' ? TUNE.aiReturnDash : 1)
|
||||
// wrong-footed: a body mid-lean is slow to turn — THE READ's one-ball cost, and it lives HERE
|
||||
|
||||
Reference in new issue
Block a user