@@ -41,12 +41,12 @@ const IMPACT_PARTICLES = 8; |
| 41 | 41 | const SPRING_PARTICLE_RATE = 0.3; |
| 42 | 42 | |
| 43 | 43 | // Bop system constants |
| 44 | | -const BOP_FORCE = 0.5; // self explanatory. BOP it. |
| 44 | +const BOP_FORCE = 0.2; // self explanatory. BOP it. |
| 45 | 45 | const BOP_RANGE = 40; // also self explanatory. TWIST it. |
| 46 | | -const BOP_DURATION = 900; // traversal duration. SHAKE it. |
| 46 | +const BOP_DURATION = 1500; // traversal duration. SHAKE it. |
| 47 | 47 | const BOP_COOLDOWN = 500; // also also self expl. PULL it. |
| 48 | 48 | const ANCHOR_RECOIL = 40; // How far the anchor moves backward during bop |
| 49 | | -const BOP_VELOCITY_BOOST = 6; // Initial velocity boost for paddle |
| 49 | +const BOP_VELOCITY_BOOST = 5; // Initial velocity boost for paddle |
| 50 | 50 | |
| 51 | 51 | // ============= BOP SYSTEM ============= |
| 52 | 52 | let bopState = { |
@@ -109,36 +109,32 @@ function activateBop(side, currentTime, paddle, support, engine, particles) { |
| 109 | 109 | if (magnitude > 0) { |
| 110 | 110 | dx /= magnitude; |
| 111 | 111 | dy /= magnitude; |
| 112 | + // Calculate anchor recoil distance |
| 113 | + let anchorRecoilDistance = ANCHOR_RECOIL * 0.3; |
| 112 | 114 | |
| 113 | | - // Calculate anchor recoil distance (reduced for gentler motion) |
| 114 | | - let anchorRecoilDistance = ANCHOR_RECOIL * 0.3; // Reduced from 0.4 |
| 115 | + // Move support and paddle in one solver step (no teleport → no ghost collisions) |
| 116 | + Body.translate(support, { x: -dx * anchorRecoilDistance, y: -dy * anchorRecoilDistance }); |
| 117 | + Body.translate(paddle, { x: -dx * anchorRecoilDistance, y: -dy * anchorRecoilDistance }); |
| 115 | 118 | |
| 116 | | - // Move the support BACKWARD (recoil effect) |
| 117 | | - let newSupportX = support.position.x - dx * anchorRecoilDistance; |
| 118 | | - let newSupportY = support.position.y - dy * anchorRecoilDistance; |
| 119 | + // Remember where the support started so we can ease it back later |
| 120 | + const preSupportPos = { x: support.position.x + dx * anchorRecoilDistance, |
| 121 | + y: support.position.y + dy * anchorRecoilDistance }; |
| 122 | + bopState[side].originalPos = preSupportPos; |
| 119 | 123 | |
| 120 | | - Body.setPosition(support, { x: newSupportX, y: newSupportY }); |
| 121 | | - |
| 122 | | - // Store original support position for recovery |
| 123 | | - bopState[side].originalPos = { |
| 124 | | - x: support.position.x + dx * anchorRecoilDistance, |
| 125 | | - y: support.position.y + dy * anchorRecoilDistance |
| 126 | | - }; |
| 127 | | - |
| 128 | | - // Set paddle velocity directly for immediate forward thrust (gentler) |
| 129 | | - let forwardSpeed = BOP_VELOCITY_BOOST * 0.8; // Reduced intensity |
| 124 | + // Now apply forward thrust from this new position |
| 125 | + let forwardSpeed = BOP_VELOCITY_BOOST * 1.0; // Restored to full power |
| 130 | 126 | Body.setVelocity(paddle, { |
| 131 | 127 | x: paddle.velocity.x + dx * forwardSpeed, |
| 132 | 128 | y: paddle.velocity.y + dy * forwardSpeed |
| 133 | 129 | }); |
| 134 | 130 | |
| 135 | | - // Apply a forward force for continued acceleration (gentler) |
| 131 | + // Apply a forward force for continued acceleration |
| 136 | 132 | Body.applyForce(paddle, paddle.position, { |
| 137 | | - x: dx * bopState[side].power * BOP_RANGE * 0.05, // Reduced from 0.1 |
| 138 | | - y: dy * bopState[side].power * BOP_RANGE * 0.05 |
| 133 | + x: dx * bopState[side].power * BOP_RANGE * 0.08, |
| 134 | + y: dy * bopState[side].power * BOP_RANGE * 0.08 |
| 139 | 135 | }); |
| 140 | 136 | |
| 141 | | - // Create particle burst for visual feedback |
| 137 | + // Create particle burst for visual feedback at the support position |
| 142 | 138 | for (let i = 0; i < 5; i++) { |
| 143 | 139 | let angle = Math.atan2(dy, dx) + (Math.random() - 0.5) * 0.5; |
| 144 | 140 | let speed = Math.random() * 4 + 2; |
@@ -176,6 +172,7 @@ function updateBopStates(currentTime, leftSupport, rightSupport, leftPaddle, rig |
| 176 | 172 | let currentX = support.position.x; |
| 177 | 173 | let currentY = support.position.y; |
| 178 | 174 | |
| 175 | + // Smooth return motion with easing |
| 179 | 176 | let returnSpeed = 0.15 * (1 - Math.pow(1 - progress, 3)); |
| 180 | 177 | let newX = currentX + (bopState.left.originalPos.x - currentX) * returnSpeed; |
| 181 | 178 | let newY = currentY + (bopState.left.originalPos.y - currentY) * returnSpeed; |
@@ -187,7 +184,7 @@ function updateBopStates(currentTime, leftSupport, rightSupport, leftPaddle, rig |
| 187 | 184 | } |
| 188 | 185 | } |
| 189 | 186 | |
| 190 | | - // Update right bop |
| 187 | + // Update right bop (same logic) |
| 191 | 188 | if (bopState.right.active) { |
| 192 | 189 | let elapsed = currentTime - bopState.right.startTime; |
| 193 | 190 | let progress = elapsed / bopState.right.duration; |
@@ -392,12 +389,9 @@ function setupCollisionHandlers(engine, ball, leftPaddle, rightPaddle, particles |
| 392 | 389 | if (contactPoint) { |
| 393 | 390 | isValidCollision = true; |
| 394 | 391 | } else { |
| 395 | | - // Fallback distance check |
| 396 | | - let dx = ball.position.x - paddle.position.x; |
| 397 | | - let dy = ball.position.y - paddle.position.y; |
| 398 | | - let distance = Math.sqrt(dx * dx + dy * dy); |
| 399 | | - let collisionThreshold = BALL_RADIUS + Math.max(PADDLE_WIDTH, PADDLE_HEIGHT)/2 + 10; |
| 400 | | - isValidCollision = distance < collisionThreshold; |
| 392 | + // Use penetration depth as fallback to avoid false positives |
| 393 | + const depth = collision.depth || 0; |
| 394 | + isValidCollision = depth > 0.5; |
| 401 | 395 | } |
| 402 | 396 | |
| 403 | 397 | if (isValidCollision) { |