@@ -41,12 +41,12 @@ const IMPACT_PARTICLES = 8; |
| 41 | const SPRING_PARTICLE_RATE = 0.3; | 41 | const SPRING_PARTICLE_RATE = 0.3; |
| 42 | | 42 | |
| 43 | // Bop system constants | 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 | const BOP_RANGE = 40; // also self explanatory. TWIST it. | 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 | const BOP_COOLDOWN = 500; // also also self expl. PULL it. | 47 | const BOP_COOLDOWN = 500; // also also self expl. PULL it. |
| 48 | const ANCHOR_RECOIL = 40; // How far the anchor moves backward during bop | 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 | // ============= BOP SYSTEM ============= | 51 | // ============= BOP SYSTEM ============= |
| 52 | let bopState = { | 52 | let bopState = { |
@@ -103,42 +103,38 @@ function activateBop(side, currentTime, paddle, support, engine, particles) { |
| 103 | // Calculate direction from support to paddle | 103 | // Calculate direction from support to paddle |
| 104 | let dx = paddle.position.x - support.position.x; | 104 | let dx = paddle.position.x - support.position.x; |
| 105 | let dy = paddle.position.y - support.position.y; | 105 | let dy = paddle.position.y - support.position.y; |
| 106 | - | 106 | + |
| 107 | // Normalize direction | 107 | // Normalize direction |
| 108 | let magnitude = Math.sqrt(dx * dx + dy * dy); | 108 | let magnitude = Math.sqrt(dx * dx + dy * dy); |
| 109 | if (magnitude > 0) { | 109 | if (magnitude > 0) { |
| 110 | dx /= magnitude; | 110 | dx /= magnitude; |
| 111 | dy /= magnitude; | 111 | dy /= magnitude; |
| | 112 | + // Calculate anchor recoil distance |
| | 113 | + let anchorRecoilDistance = ANCHOR_RECOIL * 0.3; |
| | 114 | + |
| | 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 }); |
| | 118 | + |
| | 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; |
| 112 | | 123 | |
| 113 | - // Calculate anchor recoil distance (reduced for gentler motion) | 124 | + // Now apply forward thrust from this new position |
| 114 | - let anchorRecoilDistance = ANCHOR_RECOIL * 0.3; // Reduced from 0.4 | 125 | + let forwardSpeed = BOP_VELOCITY_BOOST * 1.0; // Restored to full power |
| 115 | - | | |
| 116 | - // Move the support BACKWARD (recoil effect) | | |
| 117 | - let newSupportX = support.position.x - dx * anchorRecoilDistance; | | |
| 118 | - let newSupportY = support.position.y - dy * anchorRecoilDistance; | | |
| 119 | - | | |
| 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 | | |
| 130 | Body.setVelocity(paddle, { | 126 | Body.setVelocity(paddle, { |
| 131 | x: paddle.velocity.x + dx * forwardSpeed, | 127 | x: paddle.velocity.x + dx * forwardSpeed, |
| 132 | y: paddle.velocity.y + dy * forwardSpeed | 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 | Body.applyForce(paddle, paddle.position, { | 132 | Body.applyForce(paddle, paddle.position, { |
| 137 | - x: dx * bopState[side].power * BOP_RANGE * 0.05, // Reduced from 0.1 | 133 | + x: dx * bopState[side].power * BOP_RANGE * 0.08, |
| 138 | - y: dy * bopState[side].power * BOP_RANGE * 0.05 | 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 | for (let i = 0; i < 5; i++) { | 138 | for (let i = 0; i < 5; i++) { |
| 143 | let angle = Math.atan2(dy, dx) + (Math.random() - 0.5) * 0.5; | 139 | let angle = Math.atan2(dy, dx) + (Math.random() - 0.5) * 0.5; |
| 144 | let speed = Math.random() * 4 + 2; | 140 | let speed = Math.random() * 4 + 2; |
@@ -176,6 +172,7 @@ function updateBopStates(currentTime, leftSupport, rightSupport, leftPaddle, rig |
| 176 | let currentX = support.position.x; | 172 | let currentX = support.position.x; |
| 177 | let currentY = support.position.y; | 173 | let currentY = support.position.y; |
| 178 | | 174 | |
| | 175 | + // Smooth return motion with easing |
| 179 | let returnSpeed = 0.15 * (1 - Math.pow(1 - progress, 3)); | 176 | let returnSpeed = 0.15 * (1 - Math.pow(1 - progress, 3)); |
| 180 | let newX = currentX + (bopState.left.originalPos.x - currentX) * returnSpeed; | 177 | let newX = currentX + (bopState.left.originalPos.x - currentX) * returnSpeed; |
| 181 | let newY = currentY + (bopState.left.originalPos.y - currentY) * returnSpeed; | 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 | if (bopState.right.active) { | 188 | if (bopState.right.active) { |
| 192 | let elapsed = currentTime - bopState.right.startTime; | 189 | let elapsed = currentTime - bopState.right.startTime; |
| 193 | let progress = elapsed / bopState.right.duration; | 190 | let progress = elapsed / bopState.right.duration; |
@@ -392,12 +389,9 @@ function setupCollisionHandlers(engine, ball, leftPaddle, rightPaddle, particles |
| 392 | if (contactPoint) { | 389 | if (contactPoint) { |
| 393 | isValidCollision = true; | 390 | isValidCollision = true; |
| 394 | } else { | 391 | } else { |
| 395 | - // Fallback distance check | 392 | + // Use penetration depth as fallback to avoid false positives |
| 396 | - let dx = ball.position.x - paddle.position.x; | 393 | + const depth = collision.depth || 0; |
| 397 | - let dy = ball.position.y - paddle.position.y; | 394 | + isValidCollision = depth > 0.5; |
| 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; | | |
| 401 | } | 395 | } |
| 402 | | 396 | |
| 403 | if (isValidCollision) { | 397 | if (isValidCollision) { |