diff --git a/Archive.zip b/Archive.zip index 4f9ca4f..10119ea 100644 Binary files a/Archive.zip and b/Archive.zip differ diff --git a/thiola-pong/constants.js b/thiola-pong/constants.js index 84564d5..6826d1a 100644 --- a/thiola-pong/constants.js +++ b/thiola-pong/constants.js @@ -2,11 +2,14 @@ const PADDLE_W = 12; const PADDLE_H = 90; const PADDLE_MARGIN = 42; const BALL_R = 7; -const BASE_SPEED = 5; +const BASE_SPEED = 3.5; const WIN_SCORE = 5; const MAX_LEVEL = 3; const WALL_WIDTH = 32; +const PADDLE_SPIN_FACTOR = 0.5; +const BALL_RESUME_DELAY = 800; // ms pause after dismissing a goal message + const LEVEL_COVERAGE = [0, 0, 40, 70]; const LEVEL_CPU_SPEED = [0, 1.6, 3.0, 3.8]; diff --git a/thiola-pong/game.js b/thiola-pong/game.js index 03eb558..cddb6ac 100644 --- a/thiola-pong/game.js +++ b/thiola-pong/game.js @@ -116,7 +116,7 @@ goalFlash.removeEventListener('click', cont); if (playerScore >= WIN_SCORE) { advanceLevel(); } else if (cpuScore >= WIN_SCORE) { showGameOver(); } - else { paused = false; resetBall(isPlayerGoal ? -1 : 1); } + else { resetBall(isPlayerGoal ? -1 : 1); setTimeout(() => { paused = false; }, BALL_RESUME_DELAY); } } setTimeout(() => { @@ -247,17 +247,19 @@ if (e.key === 'w' || e.key === 'W') keyW = false; if (e.key === 's' || e.key === 'S') keyS = false; }); - canvas.addEventListener('mousemove', e => { + document.addEventListener('mousemove', e => { const rect = canvas.getBoundingClientRect(); mouseY = (e.clientY - rect.top) * (H / rect.height); }); - canvas.addEventListener('mouseleave', () => { mouseY = null; }); // ─── Update ─── function update() { if (!running || paused) return; glowPhase += 0.03; + const prevPlayerY = playerY; + const prevCpuY = cpuY; + // Player 1 (left) — PvP: W/S only; CPU mode: mouse OR W/S if (gameMode === 'pvp') { if (keyW) playerY -= 6; @@ -282,6 +284,9 @@ } cpuY = Math.max(0, Math.min(H - PADDLE_H, cpuY)); + const playerVY = playerY - prevPlayerY; + const cpuVY = cpuY - prevCpuY; + // Ball movement ballX += ballVX; ballY += ballVY; if (ballY - BALL_R <= 0) { ballY = BALL_R; ballVY = Math.abs(ballVY); } @@ -292,7 +297,7 @@ if (ballX + BALL_R >= W - PADDLE_MARGIN - PADDLE_W) { if (ballX + BALL_R <= W - PADDLE_MARGIN + PADDLE_W && ballY >= cpuY && ballY <= cpuY + PADDLE_H) { ballVX = -Math.abs(ballVX) * 1.03; - ballVY += ((ballY - cpuY)/PADDLE_H - 0.5) * 3; + ballVY += ((ballY - cpuY)/PADDLE_H - 0.5) * 3 + cpuVY * PADDLE_SPIN_FACTOR; ballX = W - PADDLE_MARGIN - PADDLE_W - BALL_R; spawnParticles(ballX, ballY, '#ff6b35', 8); } else if (ballX > W + BALL_R) { @@ -305,7 +310,7 @@ if (ballX - BALL_R <= PADDLE_MARGIN + PADDLE_W) { if (ballX - BALL_R >= PADDLE_MARGIN - PADDLE_W && ballY >= playerY && ballY <= playerY + PADDLE_H) { ballVX = Math.abs(ballVX) * 1.03; - ballVY += ((ballY - playerY)/PADDLE_H - 0.5) * 3; + ballVY += ((ballY - playerY)/PADDLE_H - 0.5) * 3 + playerVY * PADDLE_SPIN_FACTOR; ballX = PADDLE_MARGIN + PADDLE_W + BALL_R; spawnParticles(ballX, ballY, '#4ecdc4', 8); } else if (ballX - BALL_R <= WALL_WIDTH && coveragePercent > 0) {