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>
No files matched your search
|
Before Width: | Height: | Size: 763 KiB After Width: | Height: | Size: 945 KiB |
|
After Width: | Height: | Size: 555 KiB |
|
After Width: | Height: | Size: 1.0 MiB |
|
Before Width: | Height: | Size: 8.6 KiB After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 8.3 KiB After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 9.1 KiB After Width: | Height: | Size: 7.8 KiB |
|
Before Width: | Height: | Size: 9.2 KiB After Width: | Height: | Size: 6.4 KiB |
|
After Width: | Height: | Size: 7.5 KiB |
|
After Width: | Height: | Size: 6.2 KiB |
@@ -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();
|
||||
|
||||