Wind the serve up with the pull, and stop the frames flickering
Four new frames (p_pull1..4) turn the slingshot drag into the serve's wind-up: racket above the head, behind the head, behind the back, fully coiled, picked off pullFrac — the same 0..1 that sets the shot's power — so the gesture and the man are one movement and the release snaps to the smash. The two bounce frames are one drawing now. p_bounce2 is p_bounce with the free arm sheared up 12px and nothing else touched, so the swap cannot flicker, and it follows the ball instead of its rising phase; BOUNCE_IDLE was re-measured off the sprite so the ball tops out under the palm. p_toss had the tossed ball drawn into it — cut, canvas height kept, because the game scales him off img.height. The runner: hip centres sat -2.0/+7.1/-6.0/-0.1 px off the sprite centre, a 13px lateral jump at 12 frames a second, and Oliver's diagnosis was right that all four frames leaned the same way and lifted the same leg. Frames 3 and 4 mirrored, all four padded to one width with the hip on the centre line. assets/_derive.py holds the three derivations, since "the same drawing with one small difference" is the one thing the generator cannot draw. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
f71d9293fc
commit
b1f8ea3240
16 files changed
+559
-419
No files matched your search
@@ -3,7 +3,7 @@
|
||||
import { cv, ctx } from './dom.js';
|
||||
import { TUNE, UI, EATEN_GRAB, EATEN_BURST, EATEN_SUMMARY, EATEN_RESTART, COURT, PLAYER_H, BOY_H, UMP_H, UMP_X, MACH_H, BOSS_ART, BOUNCE_IDLE, SPRITE_H } from './config.js';
|
||||
import { P, O, ball, score, crowd, G } from './state.js';
|
||||
import { predictLanding, flickTarget } from './gameplay.js';
|
||||
import { predictLanding, flickTarget, pullFrac } from './gameplay.js';
|
||||
import { zombies, gore, piles, parts, pellets, extras, shots } from './zombies.js';
|
||||
|
||||
// ---------------------------------------------------------------- palette ----
|
||||
@@ -155,9 +155,10 @@ export function proj(x, y, z) {
|
||||
|
||||
// ---------------------------------------------------------------- sprites ----
|
||||
// assets/<name>.png; game falls back to code-drawn figures until sprites load.
|
||||
const SPRV = 69; // asset cache-buster — bump with main.js?v=N (see the img.src note below)
|
||||
const SPRV = 70; // asset cache-buster — bump with main.js?v=N (see the img.src note below)
|
||||
const SPR = {};
|
||||
for (const n of ['p_idle1','p_idle2','p_bounce','p_bounce2','p_toss','p_smash','p_run1','p_run2','p_run3','p_run4','p_swing1','p_swing2','p_die1','p_die2','p_die3','p_die4','p_die5',
|
||||
'p_pull1','p_pull2','p_pull3','p_pull4', // the serve WIND-UP, one frame per quarter of the pull (see drawChar)
|
||||
'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','x_crowd','x_crowd2','x_ads',
|
||||
'x_boymop1','x_boymop2','x_boymop3','x_boymop4', // the push cycle, used only while he is stopped and working
|
||||
@@ -478,10 +479,19 @@ function drawChar(ch) {
|
||||
const pre = ch.side === 'P' ? 'p_' : 'o_';
|
||||
let name = pre + (Math.floor(G.idleT * 1.6) % 2 ? 'idle1' : 'idle2'); // idle weight shift
|
||||
if (G.phase === 'pretoss' && ch.side === score.server && ch.pose === 'idle') {
|
||||
const w = G.idleT * BOUNCE_IDLE.tempo; // same phase as the bouncing ball
|
||||
const rising = Math.sin(w) * Math.cos(w) > 0;
|
||||
name = pre + (rising ? 'bounce2' : 'bounce'); // hand up while the ball rises, pat down exactly at the apex
|
||||
// The two bounce frames are ONE drawing whose free hand sits 0.10 m apart (p_bounce2 is sheared
|
||||
// out of p_bounce, see assets/_bendarm.py) — generated as a pair they came back as two different
|
||||
// men and the swap read as a flicker. So the swap can follow the BALL: hand up when the ball is
|
||||
// up at it, hand down when the ball is near the ground. BOUNCE_IDLE.apex is measured off the
|
||||
// raised hand, so the ball meets the palm rather than passing it.
|
||||
name = pre + (Math.abs(Math.sin(G.idleT * BOUNCE_IDLE.tempo)) > 0.5 ? 'bounce2' : 'bounce');
|
||||
}
|
||||
// THE PULL IS THE WIND-UP. While the slingshot is drawn, the frame is chosen by how far back the
|
||||
// finger has come: racket above the head -> behind the head -> behind the back -> fully coiled.
|
||||
// Four drawn stages over pullFrac (the same 0..1 that sets the shot's power), so the gesture and
|
||||
// the man are the same movement, and the release snaps to p_smash.
|
||||
if (G.aim && G.aim.mag > 0 && ch.side === 'P' && score.server === 'P' && ch.pose === 'idle')
|
||||
name = 'p_pull' + (1 + Math.min(3, Math.floor(pullFrac(G.aim.mag) * 4)));
|
||||
// Four-frame run where the frames exist (the player), two where they don't (the opponent). Same
|
||||
// CYCLE rate either way — more frames subdivide the stride, they do not speed it up, exactly as
|
||||
// ZFRAMES does for the shamblers.
|
||||
|
||||
Reference in new issue
Block a user