4-frame ball boy run cycle; steady two-frame serve bounce stance

- boy runs on a proper 4-phase cycle (stride, pass, mirrored stride, pass)
- server holds the crouched bounce stance through pretoss instead of
  flickering to standing idle; hand-down/hand-up frames sync with the ball,
  which now bounces floor-to-hand height

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
wuwiandClaude Fable 5 committed 2026-07-11 14:55:21 +02:00
1 parent 456e168c27
commit 34f31a65a2
12 files changed
+6 -6

No files matched your search

Binary file not shown.

Before

Width:  |  Height:  |  Size: 763 KiB

After

Width:  |  Height:  |  Size: 945 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 555 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.3 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 KiB

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

+6 -6
View File
@@ -206,9 +206,9 @@ function say(text, delay = 0) { // fallback when a recorded umpire line is missi
// ---------------------------------------------------------------- sprites ----
// assets/<name>.png; game falls back to code-drawn figures until sprites load.
const SPR = {};
for (const n of ['p_idle1','p_idle2','p_bounce','p_toss','p_smash','p_run1','p_run2','p_swing1','p_swing2',
'o_idle1','o_idle2','o_bounce','o_toss','o_smash','o_run1','o_run2','o_swing1','o_swing2',
'x_ump','x_boyrun1','x_boyrun2']) {
for (const n of ['p_idle1','p_idle2','p_bounce','p_bounce2','p_toss','p_smash','p_run1','p_run2','p_swing1','p_swing2',
'o_idle1','o_idle2','o_bounce','o_bounce2','o_toss','o_smash','o_run1','o_run2','o_swing1','o_swing2',
'x_ump','x_boyrun1','x_boyrun2','x_boyrun3','x_boyrun4']) {
const img = new Image();
img.onload = () => SPR[n] = img;
img.src = 'assets/' + n + '.png';
@@ -610,7 +610,7 @@ function drawStatic(name, x, z, hMeters, bob = 0) {
ctx.drawImage(img, p.x - w / 2, p.y - h - bob, w, h);
}
function drawBoy(b) {
const img = SPR[Math.floor(b.runT * 10) % 2 ? 'x_boyrun1' : 'x_boyrun2']; if (!img) return;
const img = SPR['x_boyrun' + (Math.floor(b.runT * 14) % 4 + 1)]; if (!img) return;
const p = proj(b.x, 0, b.z);
const h = 1.2 * p.s, w = h * img.width / img.height;
ctx.fillStyle = 'rgba(0,0,0,0.25)';
@@ -635,7 +635,7 @@ function drawChar(ch) {
const pre = ch.side === 'P' ? 'p_' : 'o_';
let name = pre + (Math.floor(idleT * 1.6) % 2 ? 'idle1' : 'idle2'); // idle weight shift
if (phase === 'pretoss' && ch.side === score.server && ch.pose === 'idle')
name = pre + (Math.abs(Math.sin(idleT * 3.6)) < 0.5 ? 'bounce' : 'idle1'); // crouch as the ball drops
name = pre + (Math.abs(Math.sin(idleT * 3.6)) > 0.55 ? 'bounce2' : 'bounce'); // hand rises as the ball comes up
if (ch.pose === 'run') name = pre + (Math.floor(ch.runT * 9) % 2 ? 'run1' : 'run2');
if (receiverWaiting) name = pre + (sway > 0 ? 'run1' : 'run2'); // split-step shuffle
if (ch.pose === 'swing') name = pre + (ch.poseT < 0.15 ? 'swing1' : 'swing2');
@@ -732,7 +732,7 @@ function render() {
// server idly bouncing the ball while waiting
if (phase === 'pretoss') {
const sv = score.server === 'P' ? P : O;
const bx = sv.x - 0.4 * (sv.side === 'P' ? 1 : -1), by = Math.abs(Math.sin(idleT * 3.6)) * 0.55;
const bx = sv.x - 0.4 * (sv.side === 'P' ? 1 : -1), by = Math.abs(Math.sin(idleT * 3.6)) * 0.78; // floor to hand
const sh = proj(bx, 0, sv.z);
ctx.fillStyle = 'rgba(0,0,0,0.3)';
ctx.beginPath(); ctx.ellipse(sh.x, sh.y, Math.max(1.5, sh.s * 0.08), Math.max(1, sh.s * 0.035), 0, 0, 7); ctx.fill();