@@ -17,7 +17,7 @@ const PADDLE_WIDTH = 20; |
| 17 | 17 | const PADDLE_HEIGHT = 80; |
| 18 | 18 | |
| 19 | 19 | // Enhanced movement constants (tuned for faster response) |
| 20 | | -const SUPPORT_SPEED = 6.5; // Bumped up |
| 20 | +const SUPPORT_SPEED = 6.5; // Bumped up from 4.5 |
| 21 | 21 | const SUPPORT_ACCEL = 1.2; // Increased acceleration |
| 22 | 22 | const INPUT_SMOOTHING = 0.25; // More responsive |
| 23 | 23 | const SUPPORT_MAX_SPEED = 8; // Higher max speed |
@@ -35,22 +35,22 @@ const SPRING_STIFFNESS = 0.025; |
| 35 | 35 | |
| 36 | 36 | // Visual enhancement constants |
| 37 | 37 | const TRAIL_SEGMENTS = 8; |
| 38 | | -const PADDLE_GLOW_DISTANCE = 25; |
| 39 | | -const SPRING_GLOW_INTENSITY = 120; // More intense glow |
| 38 | +const PADDLE_GLOW_DISTANCE = 25; // self explanatory |
| 39 | +const SPRING_GLOW_INTENSITY = 120; // self explanatory. so why add a comment? because I'm anal. |
| 40 | 40 | |
| 41 | 41 | // Particle system constants |
| 42 | | -const MAX_PARTICLES = 100; |
| 43 | | -const PARTICLE_LIFE = 60; |
| 44 | | -const IMPACT_PARTICLES = 8; |
| 45 | | -const SPRING_PARTICLE_RATE = 0.3; |
| 42 | +const MAX_PARTICLES = 100; // dont get carried away |
| 43 | +const PARTICLE_LIFE = 60; // dont get carried away |
| 44 | +const IMPACT_PARTICLES = 8; // dont get carried away |
| 45 | +const SPRING_PARTICLE_RATE = 0.3; // get carried away. |
| 46 | 46 | |
| 47 | 47 | // Bop system constants |
| 48 | | -const BOP_FORCE = 1.0; |
| 49 | | -const BOP_DURATION = 300; |
| 50 | | -const BOP_COOLDOWN = 500; |
| 51 | | -const ANCHOR_RECOIL = 40; // How far the anchor moves backward during bop |
| 52 | | -const BOP_RANGE = 600; // How far the paddle can thrust forward |
| 53 | | -const BOP_VELOCITY_BOOST = 12; // Initial velocity boost for paddle |
| 48 | +const BOP_FORCE = 1.0; // self explanatory. BOP it. |
| 49 | +const BOP_RANGE = 50; // also self explanatory. TWIST it. |
| 50 | +const BOP_DURATION = 300; // traversal duration. SHAKE it. |
| 51 | +const BOP_COOLDOWN = 500; // also also self expl. PULL it. |
| 52 | +const ANCHOR_RECOIL = 40; // How far the anchor moves backward during bop |
| 53 | +const BOP_VELOCITY_BOOST = 12; // Initial velocity boost for paddle |
| 54 | 54 | |
| 55 | 55 | // Game variables |
| 56 | 56 | let ball; |
@@ -351,7 +351,8 @@ const AI_SETTINGS = { |
| 351 | 351 | speed: 0.8, // Increased from 0.6 |
| 352 | 352 | prediction: 0.3, // 30% prediction vs reaction |
| 353 | 353 | aggression: 0.2, // Low aggression |
| 354 | | - oscillation: 0.3 // Minimal oscillation |
| 354 | + oscillation: 0.3, // Minimal oscillation |
| 355 | + bopChance: 0.25 // 25% chance to bop when in range |
| 355 | 356 | }, |
| 356 | 357 | medium: { |
| 357 | 358 | reactionTime: 250, |
@@ -359,7 +360,8 @@ const AI_SETTINGS = { |
| 359 | 360 | speed: 1.0, // Increased from 0.8 |
| 360 | 361 | prediction: 0.6, |
| 361 | 362 | aggression: 0.5, // Moderate aggression |
| 362 | | - oscillation: 0.7 // Good oscillation technique |
| 363 | + oscillation: 0.7, // Good oscillation technique |
| 364 | + bopChance: 0.55 // 55% chance to bop when in range |
| 363 | 365 | }, |
| 364 | 366 | hard: { |
| 365 | 367 | reactionTime: 150, |
@@ -367,7 +369,8 @@ const AI_SETTINGS = { |
| 367 | 369 | speed: 1.2, // Increased from 1.0 |
| 368 | 370 | prediction: 0.8, |
| 369 | 371 | aggression: 0.8, // High aggression |
| 370 | | - oscillation: 1.0 // Master-level oscillation |
| 372 | + oscillation: 1.0, // Master-level oscillation |
| 373 | + bopChance: 0.85 // 85% chance to bop when in range |
| 371 | 374 | } |
| 372 | 375 | }; |
| 373 | 376 | |
@@ -744,33 +747,64 @@ function handleAITracking(currentTime, ballPos, ballVel, aiSettings) { |
| 744 | 747 | aiState.targetY = lerp(aiState.targetY, targetAnchorY, trackingIntensity); |
| 745 | 748 | |
| 746 | 749 | // AI Bop decision logic |
| 747 | | - if (ballApproaching && ballDistance < 150 && !aiState.consideringBop) { |
| 748 | | - // Consider bopping if ball is close and conditions are right |
| 749 | | - let shouldConsiderBop = ballSpeed > 8 && // Fast incoming ball |
| 750 | | - Math.abs(ballVel.y) < 4 && // Not too much vertical movement |
| 751 | | - random() < aiSettings.aggression * 0.4; // Chance based on aggression |
| 750 | + if (ballApproaching && !aiState.consideringBop && !bopState.right.active) { |
| 751 | + // Calculate if ball will be within bop range |
| 752 | + let timeToReach = ballDistance / Math.abs(ballVel.x); |
| 753 | + let predictedBallY = ballPos.y + ballVel.y * timeToReach; |
| 754 | + |
| 755 | + // Account for wall bounces in prediction |
| 756 | + if (predictedBallY < 50) { |
| 757 | + predictedBallY = 100 - predictedBallY; |
| 758 | + } else if (predictedBallY > height - 50) { |
| 759 | + predictedBallY = 2 * (height - 50) - predictedBallY; |
| 760 | + } |
| 761 | + |
| 762 | + // Check if paddle will be close enough to ball for effective bop |
| 763 | + let paddleY = rightPaddle.position.y; |
| 764 | + let distanceToIntercept = Math.abs(predictedBallY - paddleY); |
| 765 | + |
| 766 | + // Bop is effective if paddle is within a reasonable range of the ball |
| 767 | + let bopEffectiveRange = PADDLE_HEIGHT / 2 + 30; // Paddle can reach ball with bop |
| 768 | + |
| 769 | + // Consider bopping if: |
| 770 | + // 1. Ball is approaching at good speed |
| 771 | + // 2. Ball will be within bop effective range |
| 772 | + // 3. Ball is at the right distance for timing |
| 773 | + // 4. Random chance based on difficulty |
| 774 | + let shouldConsiderBop = ballSpeed > 5 && // Minimum speed worth bopping |
| 775 | + distanceToIntercept < bopEffectiveRange && |
| 776 | + ballDistance > 80 && ballDistance < 200 && // Sweet spot for bop timing |
| 777 | + currentTime - bopState.right.lastBopTime > BOP_COOLDOWN && |
| 778 | + random() < aiSettings.bopChance; // Difficulty-based chance |
| 752 | 779 | |
| 753 | 780 | if (shouldConsiderBop) { |
| 754 | 781 | aiState.consideringBop = true; |
| 755 | 782 | aiState.bopDecisionTime = currentTime; |
| 783 | + // Adjust bop timing based on ball speed and distance |
| 784 | + aiState.bopTiming = Math.max(50, Math.min(200, ballDistance * 2 - ballSpeed * 10)); |
| 756 | 785 | } |
| 757 | 786 | } |
| 758 | 787 | |
| 759 | 788 | // Execute bop at the right moment |
| 760 | 789 | if (aiState.consideringBop && ballApproaching) { |
| 761 | 790 | let timeToBop = currentTime - aiState.bopDecisionTime; |
| 791 | + let paddleY = rightPaddle.position.y; |
| 792 | + let distanceToBall = Math.abs(ballPos.y - paddleY); |
| 793 | + |
| 794 | + // Refined bop execution conditions |
| 762 | 795 | let shouldBop = timeToBop > aiState.bopTiming && |
| 763 | | - ballDistance < 100 && |
| 764 | | - !bopState.right.active && |
| 765 | | - currentTime - bopState.right.lastBopTime > BOP_COOLDOWN; |
| 796 | + ballDistance < 150 && // Close enough |
| 797 | + distanceToBall < PADDLE_HEIGHT / 2 + 20 && // Paddle can reach ball |
| 798 | + !bopState.right.active; |
| 766 | 799 | |
| 767 | 800 | if (shouldBop) { |
| 768 | 801 | activateBop('right', currentTime); |
| 769 | 802 | aiState.consideringBop = false; |
| 803 | + console.log(`AI BOP! Difficulty: ${aiState.difficulty}, Speed: ${ballSpeed.toFixed(1)}`); |
| 770 | 804 | } |
| 771 | 805 | |
| 772 | | - // Cancel bop consideration if ball gets too far |
| 773 | | - if (ballDistance > 150) { |
| 806 | + // Cancel bop if opportunity missed |
| 807 | + if (ballDistance > 200 || ballDistance < 50) { |
| 774 | 808 | aiState.consideringBop = false; |
| 775 | 809 | } |
| 776 | 810 | } |
@@ -1209,7 +1243,7 @@ function drawSinglePaddleEnhanced(paddle, ballDistance) { |
| 1209 | 1243 | if (isLeft && bopState.left.active) { |
| 1210 | 1244 | let bopProgress = (millis() - bopState.left.startTime) / bopState.left.duration; |
| 1211 | 1245 | bopGlow = (1 - bopProgress) * 100; // Fade out over bop duration |
| 1212 | | - } else if (!isLeft && !isAI && bopState.right.active) { |
| 1246 | + } else if (!isLeft && bopState.right.active) { |
| 1213 | 1247 | let bopProgress = (millis() - bopState.right.startTime) / bopState.right.duration; |
| 1214 | 1248 | bopGlow = (1 - bopProgress) * 100; |
| 1215 | 1249 | } |
@@ -1244,8 +1278,14 @@ function drawSinglePaddleEnhanced(paddle, ballDistance) { |
| 1244 | 1278 | } |
| 1245 | 1279 | |
| 1246 | 1280 | // Bop color override |
| 1247 | | - if ((isLeft && bopState.left.active) || (!isLeft && !isAI && bopState.right.active)) { |
| 1281 | + if ((isLeft && bopState.left.active) || (!isLeft && bopState.right.active)) { |
| 1248 | 1282 | paddleColor = [255, 255, 100]; // Bright yellow during bop |
| 1283 | + |
| 1284 | + // Special effect for AI bop |
| 1285 | + if (isAI && bopState.right.active) { |
| 1286 | + paddleColor = [255, 50, 255]; // Purple for AI bop |
| 1287 | + glowIntensity = Math.min(255, glowIntensity + 50); // Extra glow |
| 1288 | + } |
| 1249 | 1289 | } |
| 1250 | 1290 | |
| 1251 | 1291 | // Draw enhanced glow effect first |
@@ -1389,6 +1429,15 @@ function drawDebugInfo() { |
| 1389 | 1429 | } else if (aiState.mode === 'SWINGING') { |
| 1390 | 1430 | fill(255, 50, 50, 200); |
| 1391 | 1431 | text("⚡ AI POWER SWING!", 10, 175); |
| 1432 | + } else if (aiState.consideringBop) { |
| 1433 | + fill(255, 255, 100, 200); |
| 1434 | + text("💥 AI PREPARING BOP!", 10, 175); |
| 1435 | + } |
| 1436 | + |
| 1437 | + // Bop status |
| 1438 | + if (bopState.right.active) { |
| 1439 | + fill(255, 255, 0, 255); |
| 1440 | + text("🚀 AI BOPPING!", 10, 190); |
| 1392 | 1441 | } |
| 1393 | 1442 | } |
| 1394 | 1443 | |