Bring the swing log to every rally mode, and time the gold ring

Oliver wants to hand over a log of why he missed balls. It existed behind
a scoreboard tap but was gated on G.mode === 'tennis', so the Climb, the
cup and training (which run as 'splatter') logged nothing. It now writes
in every rally mode: hits with shot, tap age and any quality cap, misses
with the new let-it-bounce reason, and a WON/LOST line naming why each
point went. Tapping the open log copies all 200 entries with a header.

Its first catch: the gold ring went gold at a distance, which is too
early for the slow balls steps 1-3 brought in. It is now timed off the
ball's real path to his reach (against where his run is taking him):
gold means touch now. ringTime 0 restores the old ring. v365.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
wuwiandClaude Opus 5.5 committed 2026-10-14 11:41:05 +02:00
1 parent 180288ef8f
commit d110e12321
8 files changed
+225 -41

No files matched your search

+25 -1
View File
@@ -8,7 +8,7 @@ import { P, O, ball, score, G, setupPoint, sendBoy, selectCourt, selectTour, sel
import { update, restartRun, keyPressed, solveShot, inServeBox, inCourt, forceZombies, startGore, splatPoint, deliverTin, selectWeapon, grantWeapon, showModal, dismissModal, flickTarget, dragServe, toggleTestMode } from './gameplay.js';
import { startDuel, resolveInterlude, startHorde, startBossFight, endBossFight, startDrill, endDrill, startMatch, endMatch, startBench, endBench, FALL, benchFall, startNetVs, endNetVs } from './beats.js';
import { netConnect, netHooks, netAgain } from './net.js';
import { fitViewport, render, proj, RES, SPRV, LOAD, SPR, drawLook, endHit } from './render.js';
import { fitViewport, render, proj, RES, SPRV, LOAD, SPR, drawLook, endHit, logGeom } from './render.js';
import { voice, ambient, menuMusic, menuDuck } from './audio.js';
import { zombies, spawnZombie, spawnRacketZombie, spawnMachine, spawnChapterBoss, piles, gore, parts, pellets, extras, shots, pops, explode, hurtPlayer, killPlayer , spawnBarrel } from './zombies.js';
import { cv } from './dom.js';
@@ -1078,7 +1078,31 @@ cv.addEventListener('pointerdown', e => {
const x = (e.clientX - r.left) / r.width * (cv.width / RES); // logical coords
const y = (e.clientY - r.top) / r.height * (cv.height / RES);
if (x >= 6 && x <= 138 && y >= 6 && y <= 36) G.logShow = !G.logShow; // the scoreboard
else if (G.logShow) { // ...and a tap on the open log COPIES all of it, to paste into a debug session
const L = logGeom();
if (x >= L.x && x <= L.x + L.w && y >= L.y && y <= L.y + L.h) copySwingLog();
}
});
// The whole ring as text (Oliver, 2026-10-14: "ich würde dir das gerne mal zum Debuggen geben"):
// a header naming the build, the mode and the body across the net, then one line per entry with
// its measured fields. Clipboard first (a tap is the user gesture it needs); the share sheet where
// a webview refuses it.
function copySwingLog() {
const v = (document.querySelector('script[src*="main.js"]')?.src.match(/v=(\d+)/) || [])[1] || '?';
const foe = G.match && !G.net ? matchFoe(G.match.opp) : null;
const head = `SPLATTER TENNIS v${v} · ${G.match ? (G.match.cup ? 'cup' : G.match.train ? 'training' : 'climb') : G.classic ? 'classic' : G.mode}`
+ (foe ? ` · ${foe.short} (${foe.style})` : G.diff ? ` · ${G.diff.name}` : '') + ` · ${new Date().toISOString()}`;
const lines = G.missLog.map(e => {
const { at, result, reason, ...rest } = e;
const extra = Object.keys(rest).length ? ' {' + Object.entries(rest).map(([k, val]) => k + '=' + val).join(' ') + '}' : '';
return `${at}s ${result} ${reason}${extra}`;
});
const text = [head, ...lines].join('\n');
const done = ok => { G.flash = { text: ok ? 'SWING LOG COPIED' : 'COPY FAILED', t: 1.2 }; };
const share = () => navigator.share ? navigator.share({ text }).then(() => done(true), () => done(false)) : done(false);
if (navigator.clipboard && navigator.clipboard.writeText) navigator.clipboard.writeText(text).then(() => done(true), share);
else share();
}