/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var BackgroundCloud = Container.expand(function () { var self = Container.call(this); var cloud = self.attachAsset('cloud', { anchorX: 0.5, anchorY: 0.5 }); cloud.alpha = 0.7; self.speed = 0.3 + Math.random() * 0.5; // Floating animation self.update = function () { self.x += self.speed; if (self.x > 2200) { self.x = -150; } var time = LK.ticks * 0.01; self.y += Math.sin(time) * 0.1; }; return self; }); var BackgroundFlower = Container.expand(function () { var self = Container.call(this); var flower = self.attachAsset('flower', { anchorX: 0.5, anchorY: 0.5 }); flower.alpha = 0.9; // Gentle bobbing self.update = function () { var time = LK.ticks * 0.025; flower.scaleX = 1 + Math.sin(time) * 0.1; flower.scaleY = 1 + Math.sin(time) * 0.1; }; return self; }); var BackgroundGrass = Container.expand(function () { var self = Container.call(this); var grass = self.attachAsset('grass', { anchorX: 0.5, anchorY: 0.5 }); grass.alpha = 0.8; // Gentle swaying self.update = function () { var time = LK.ticks * 0.03; grass.rotation = Math.sin(time) * 0.05; }; return self; }); var BackgroundMountain = Container.expand(function () { var self = Container.call(this); var mountain = self.attachAsset('mountain', { anchorX: 0.5, anchorY: 1 }); mountain.alpha = 0.3; mountain.tint = 0x4682B4; return self; }); var BackgroundTree = Container.expand(function () { var self = Container.call(this); var trunk = self.attachAsset('tree', { anchorX: 0.5, anchorY: 1 }); var leaves = self.attachAsset('treeLeaves', { anchorX: 0.5, anchorY: 1 }); leaves.y = -40; // Add subtle swaying animation self.update = function () { var time = LK.ticks * 0.02; leaves.rotation = Math.sin(time) * 0.1; trunk.rotation = Math.sin(time) * 0.05; }; return self; }); var Ball = Container.expand(function () { var self = Container.call(this); var ballGraphics = self.attachAsset('ball', { anchorX: 0.5, anchorY: 0.5 }); self.velocityX = 0; self.velocityY = 0; self.speed = 12; self.reset = function () { self.x = 1024; self.y = 1366; var angle = (Math.random() > 0.5 ? 1 : -1) * (Math.PI / 6 + Math.random() * Math.PI / 3); self.velocityX = Math.sin(angle) * self.speed; // Ball only goes towards player (positive Y) or AI (negative Y), not random self.velocityY = Math.cos(angle) * self.speed * (Math.random() > 0.5 ? 1 : -1); }; self.update = function () { self.x += self.velocityX; self.y += self.velocityY; // Bounce off left and right walls if (self.x <= 30 || self.x >= 2018) { self.velocityX = -self.velocityX; } }; return self; }); var Card = Container.expand(function (cardType) { var self = Container.call(this); var cardGraphics = self.attachAsset('card', { anchorX: 0.5, anchorY: 0.5 }); self.cardType = cardType; self.used = false; self.cooldown = 0; self.usageCount = 0; // Track how many times this card has been used self.maxUsage = 2; // Maximum number of times this card can be used var iconAsset = ''; var emojiText = ''; switch (cardType) { case 'speed': iconAsset = 'speedBoostIcon'; emojiText = '⚡'; break; case 'explosion': iconAsset = 'explosionIcon'; emojiText = '💥'; break; case 'pierce': iconAsset = 'pierceIcon'; emojiText = '🔥'; break; case 'slow': iconAsset = 'slowIcon'; emojiText = '❄️'; break; case 'illusion': iconAsset = 'illusionIcon'; emojiText = '🎭'; break; } var icon = self.attachAsset(iconAsset, { anchorX: 0.5, anchorY: 0.5 }); var emoji = new Text2(emojiText, { size: 40, fill: '#FFFFFF' }); emoji.anchor.set(0.5, 0.5); self.addChild(emoji); self.down = function (x, y, obj) { if (!self.used && self.cooldown <= 0 && self.usageCount < self.maxUsage) { self.used = true; self.cooldown = 300; // 5 seconds at 60fps self.usageCount++; // Increment usage counter // Play card selection sound LK.getSound('cardSelect').play(); // Card activation animation tween(self, { scaleX: 1.3, scaleY: 1.3 }, { duration: 150, easing: tween.easeOut, onFinish: function onFinish() { tween(self, { scaleX: 1, scaleY: 1 }, { duration: 150, easing: tween.easeOut }); } }); // Glowing effect tween(cardGraphics, { tint: 0xFFFFFF }, { duration: 100, onFinish: function onFinish() { tween(cardGraphics, { alpha: 0.5, tint: 0x666666 }, { duration: 200 }); } }); tween(icon, { alpha: 0.5, scaleX: 1.2, scaleY: 1.2 }, { duration: 200, onFinish: function onFinish() { tween(icon, { scaleX: 1, scaleY: 1 }, { duration: 200 }); } }); // Floating particles from card for (var i = 0; i < 3; i++) { var particle = game.addChild(new Particle()); particle.x = self.x + (Math.random() - 0.5) * 60; particle.y = self.y + (Math.random() - 0.5) * 60; particle.velocityX = (Math.random() - 0.5) * 6; particle.velocityY = -Math.random() * 8 - 2; particle.life = 40; particle.maxLife = 40; particles.push(particle); } switch (self.cardType) { case 'speed': activateSpeedBoost(); break; case 'explosion': activateExplosion(); break; case 'pierce': activatePierce(); break; case 'slow': activateSlow(); break; case 'illusion': activateIllusion(); break; } // Check if card has reached usage limit - trigger destruction immediately if (self.usageCount >= self.maxUsage) { self.hasExploded = true; // Create explosion particles around the card for (var i = 0; i < 12; i++) { var particle = game.addChild(new Particle()); particle.x = self.x + (Math.random() - 0.5) * 100; particle.y = self.y + (Math.random() - 0.5) * 120; particle.velocityX = (Math.random() - 0.5) * 15; particle.velocityY = (Math.random() - 0.5) * 15; particle.life = 60; particle.maxLife = 60; // Random bright colors for explosion effect var colors = [0xff4444, 0xffaa44, 0xffff44, 0xff8844, 0xaa44ff]; var randomColor = colors[Math.floor(Math.random() * colors.length)]; tween(particle, { tint: randomColor }, { duration: 200 }); particles.push(particle); } // Create card fragments that fly out for (var i = 0; i < 6; i++) { var fragment = game.addChild(new Container()); var fragmentGraphics = fragment.attachAsset('card', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.3, scaleY: 0.3 }); fragment.x = self.x; fragment.y = self.y; var angle = i / 6 * Math.PI * 2; var speed = 8 + Math.random() * 6; var fragmentVelX = Math.cos(angle) * speed; var fragmentVelY = Math.sin(angle) * speed; // Fragment animation - fly out and fade tween(fragment, { x: fragment.x + fragmentVelX * 20, y: fragment.y + fragmentVelY * 20, rotation: (Math.random() - 0.5) * 4, alpha: 0, scaleX: 0.1, scaleY: 0.1 }, { duration: 1000 + Math.random() * 500, easing: tween.easeOut, onFinish: function onFinish() { fragment.destroy(); } }); } // Card shake and explosion effect var originalX = self.x; var originalY = self.y; tween(self, { x: originalX + 15, y: originalY + 15, scaleX: 1.3, scaleY: 1.3 }, { duration: 100, onFinish: function onFinish() { tween(self, { x: originalX - 15, y: originalY - 15, scaleX: 1.1, scaleY: 1.1 }, { duration: 100, onFinish: function onFinish() { tween(self, { x: originalX, y: originalY, scaleX: 0.8, scaleY: 0.8 }, { duration: 100 }); } }); } }); // Card is permanently disabled - make it visually distinct tween(cardGraphics, { alpha: 0.3, tint: 0x666666 }, { duration: 300, easing: tween.easeOut }); tween(icon, { alpha: 0.3 }, { duration: 300, easing: tween.easeOut }); } } }; self.move = function (x, y, obj) { // Hover effect removed }; self.up = function (x, y, obj) { if (!self.used && self.cooldown <= 0) { // Card unhover animation - return to normal size tween(self, { scaleX: 0.8, scaleY: 0.8 }, { duration: 100, easing: tween.easeOut }); } }; self.update = function () { if (self.cooldown > 0) { self.cooldown--; if (self.cooldown <= 0 && self.usageCount < self.maxUsage) { self.used = false; // Card reactivation animation tween(cardGraphics, { alpha: 1, tint: 0xFFFFFF }, { duration: 300, easing: tween.easeOut }); tween(icon, { alpha: 1 }, { duration: 300, easing: tween.easeOut }); // Sparkle effect on reactivation for (var i = 0; i < 5; i++) { var particle = game.addChild(new Particle()); particle.x = self.x + (Math.random() - 0.5) * 80; particle.y = self.y + (Math.random() - 0.5) * 100; particle.velocityX = (Math.random() - 0.5) * 4; particle.velocityY = (Math.random() - 0.5) * 4; particle.life = 20; particle.maxLife = 20; tween(particle, { tint: 0xFFFFFF }, { duration: 100 }); particles.push(particle); } } else if (self.cooldown <= 0 && self.usageCount >= self.maxUsage) { // Card destruction effect - only trigger once if (!self.hasExploded) { self.hasExploded = true; // Create explosion particles around the card for (var i = 0; i < 12; i++) { var particle = game.addChild(new Particle()); particle.x = self.x + (Math.random() - 0.5) * 100; particle.y = self.y + (Math.random() - 0.5) * 120; particle.velocityX = (Math.random() - 0.5) * 15; particle.velocityY = (Math.random() - 0.5) * 15; particle.life = 60; particle.maxLife = 60; // Random bright colors for explosion effect var colors = [0xff4444, 0xffaa44, 0xffff44, 0xff8844, 0xaa44ff]; var randomColor = colors[Math.floor(Math.random() * colors.length)]; tween(particle, { tint: randomColor }, { duration: 200 }); particles.push(particle); } // Create card fragments that fly out for (var i = 0; i < 6; i++) { var fragment = game.addChild(new Container()); var fragmentGraphics = fragment.attachAsset('card', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.3, scaleY: 0.3 }); fragment.x = self.x; fragment.y = self.y; var angle = i / 6 * Math.PI * 2; var speed = 8 + Math.random() * 6; var fragmentVelX = Math.cos(angle) * speed; var fragmentVelY = Math.sin(angle) * speed; // Fragment animation - fly out and fade tween(fragment, { x: fragment.x + fragmentVelX * 20, y: fragment.y + fragmentVelY * 20, rotation: (Math.random() - 0.5) * 4, alpha: 0, scaleX: 0.1, scaleY: 0.1 }, { duration: 1000 + Math.random() * 500, easing: tween.easeOut, onFinish: function onFinish() { fragment.destroy(); } }); } // Card shake and explosion effect var originalX = self.x; var originalY = self.y; tween(self, { x: originalX + 15, y: originalY + 15, scaleX: 1.3, scaleY: 1.3 }, { duration: 100, onFinish: function onFinish() { tween(self, { x: originalX - 15, y: originalY - 15, scaleX: 1.1, scaleY: 1.1 }, { duration: 100, onFinish: function onFinish() { tween(self, { x: originalX, y: originalY, scaleX: 0.8, scaleY: 0.8 }, { duration: 100 }); } }); } }); } // Card is permanently disabled - make it visually distinct tween(cardGraphics, { alpha: 0.3, tint: 0x666666 }, { duration: 300, easing: tween.easeOut }); tween(icon, { alpha: 0.3 }, { duration: 300, easing: tween.easeOut }); } } else if (!self.used) { // Idle floating animation - maintain original Y position with small float var time = LK.ticks * 0.1; var originalY = self.y; if (self.originalY === undefined) { self.originalY = self.y; } self.y = self.originalY + Math.sin(time + self.cardType.length) * 5; // Subtle glow effect if (LK.ticks % 180 === 0) { tween(cardGraphics, { tint: 0xFFFFFF }, { duration: 200, easing: tween.easeInOut, onFinish: function onFinish() { tween(cardGraphics, { tint: 0x4a90e2 }, { duration: 200, easing: tween.easeInOut }); } }); } } }; return self; }); var Castle = Container.expand(function () { var self = Container.call(this); var castleGraphics = self.attachAsset('castle', { anchorX: 0.5, anchorY: 0.5 }); return self; }); var House = Container.expand(function () { var self = Container.call(this); var houseGraphics = self.attachAsset('house', { anchorX: 0.5, anchorY: 0.5 }); return self; }); var Paddle = Container.expand(function () { var self = Container.call(this); var paddleGraphics = self.attachAsset('paddle', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 8; self.targetX = self.x; self.moveTowards = function (targetX) { self.targetX = targetX; if (self.targetX < 100) self.targetX = 100; if (self.targetX > 1948) self.targetX = 1948; }; self.update = function () { if (Math.abs(self.x - self.targetX) > 2) { if (self.x < self.targetX) { self.x += self.speed; } else { self.x -= self.speed; } } }; return self; }); var Particle = Container.expand(function () { var self = Container.call(this); var particleGraphics = self.attachAsset('particle', { anchorX: 0.5, anchorY: 0.5 }); self.velocityX = (Math.random() - 0.5) * 8; self.velocityY = (Math.random() - 0.5) * 8; self.life = 30; self.maxLife = 30; self.update = function () { self.x += self.velocityX; self.y += self.velocityY; self.life--; var alpha = self.life / self.maxLife; particleGraphics.alpha = alpha; if (self.life <= 0) { self.destroy(); particles.splice(particles.indexOf(self), 1); } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x228B22 }); /**** * Game Code ****/ // Simple background - removed complex gradient overlay var gameTime = 90; var playerScore = 0; var aiScore = 0; var particles = []; var cards = []; var speedBoostActive = false; var speedBoostTimer = 0; var explosionActive = false; var pierceActive = false; var pierceTimer = 0; var pierceUser = null; // Track who used pierce power-up var slowActive = false; var slowTimer = 0; var illusionActive = false; var illusionTimer = 0; var illusionBalls = []; var aiStunned = false; var aiStunTimer = 0; var playerStunned = false; var playerStunTimer = 0; // Create UI elements var timerText = new Text2('90', { size: 80, fill: 0xFFFFFF }); timerText.anchor.set(0.5, 0); LK.gui.top.addChild(timerText); var playerScoreText = new Text2('0', { size: 60, fill: 0x4A90E2 }); playerScoreText.anchor.set(0, 1); LK.gui.bottomLeft.addChild(playerScoreText); var aiScoreText = new Text2('0', { size: 60, fill: 0xE24A4A }); aiScoreText.anchor.set(0, 0); LK.gui.topLeft.addChild(aiScoreText); // Create tutorial overlay var tutorialOverlay = game.addChild(new Container()); tutorialOverlay.x = 0; tutorialOverlay.y = 0; // Create semi-transparent background var tutorialBg = tutorialOverlay.attachAsset('card', { anchorX: 0, anchorY: 0, scaleX: 25.6, scaleY: 27.32, alpha: 0.8 }); tutorialBg.tint = 0x000000; // Create tutorial title var tutorialTitle = new Text2('POWER-UPS TUTORIAL', { size: 80, fill: '#FFFFFF' }); tutorialTitle.anchor.set(0.5, 0.5); tutorialTitle.x = 1024; tutorialTitle.y = 400; tutorialOverlay.addChild(tutorialTitle); // Create game objective text var objectiveText = new Text2('GAME OBJECTIVE: Score more goals than your opponent in 90 seconds!', { size: 55, fill: '#FFD700' }); objectiveText.anchor.set(0.5, 0.5); objectiveText.x = 1024; objectiveText.y = 550; tutorialOverlay.addChild(objectiveText); // Create power-up instruction texts var instructions = ['⚡ SPEED BOOST: Makes ball move faster for 5 seconds', '💥 EXPLOSION: Ball explodes and stuns opponent for 5 seconds', '🔥 PIERCE: Ball goes through opponent paddle for 3 seconds', '❄️ SLOW: Makes ball move slower for 5 seconds']; var instructionTexts = []; for (var i = 0; i < instructions.length; i++) { var instructionText = new Text2(instructions[i], { size: 50, fill: '#FFFFFF' }); instructionText.anchor.set(0.5, 0.5); instructionText.x = 1024; instructionText.y = 650 + i * 120; tutorialOverlay.addChild(instructionText); instructionTexts.push(instructionText); } // Create start button var startButton = tutorialOverlay.attachAsset('card', { anchorX: 0.5, anchorY: 0.5, scaleX: 3, scaleY: 1.5 }); startButton.x = 1024; startButton.y = 1400; startButton.tint = 0x4A90E2; var startButtonText = new Text2('TAP TO START', { size: 60, fill: '#FFFFFF' }); startButtonText.anchor.set(0.5, 0.5); startButtonText.x = 1024; startButtonText.y = 1400; tutorialOverlay.addChild(startButtonText); // Add tutorial click handler tutorialOverlay.down = function (x, y, obj) { // Hide tutorial with fade animation tween(tutorialOverlay, { alpha: 0 }, { duration: 500, easing: tween.easeOut, onFinish: function onFinish() { tutorialOverlay.visible = false; } }); }; // Create game objects var playerPaddle = game.addChild(new Paddle()); playerPaddle.x = 1024; playerPaddle.y = 2300; var aiPaddle = game.addChild(new Paddle()); aiPaddle.x = 1024; aiPaddle.y = 432; var playerCastle = game.addChild(new Castle()); playerCastle.x = 1024; playerCastle.y = 2542; // Add houses around player castle // Left side houses in a row var playerHouse1L = game.addChild(new House()); playerHouse1L.x = 350; playerHouse1L.y = 2500; var playerHouse2L = game.addChild(new House()); playerHouse2L.x = 500; playerHouse2L.y = 2500; var playerHouse3L = game.addChild(new House()); playerHouse3L.x = 650; playerHouse3L.y = 2500; // Right side houses in a row var playerHouse1R = game.addChild(new House()); playerHouse1R.x = 1398; playerHouse1R.y = 2500; var playerHouse2R = game.addChild(new House()); playerHouse2R.x = 1548; playerHouse2R.y = 2500; var playerHouse3R = game.addChild(new House()); playerHouse3R.x = 1698; playerHouse3R.y = 2500; var aiCastle = game.addChild(new Castle()); aiCastle.x = 1024; aiCastle.y = 190; // Add houses around AI castle // Left side houses in a row var aiHouse1L = game.addChild(new House()); aiHouse1L.x = 350; aiHouse1L.y = 232; var aiHouse2L = game.addChild(new House()); aiHouse2L.x = 500; aiHouse2L.y = 232; var aiHouse3L = game.addChild(new House()); aiHouse3L.x = 650; aiHouse3L.y = 232; // Right side houses in a row var aiHouse1R = game.addChild(new House()); aiHouse1R.x = 1398; aiHouse1R.y = 232; var aiHouse2R = game.addChild(new House()); aiHouse2R.x = 1548; aiHouse2R.y = 232; var aiHouse3R = game.addChild(new House()); aiHouse3R.x = 1698; aiHouse3R.y = 232; // Clean green background - no decorative elements var ball = game.addChild(new Ball()); ball.reset(); // Create player cards stacked vertically on left side var cardTypes = ['speed', 'explosion', 'pierce', 'slow', 'illusion']; var cardX = 150; // Left side position var cardStartY = 1600; // Starting Y position (moved up for 5 cards) var cardSpacing = 160; // Space between cards (reduced for 5 cards) for (var i = 0; i < 5; i++) { var card = game.addChild(new Card(cardTypes[i])); card.x = cardX; card.y = cardStartY + i * cardSpacing; card.scaleX = 0.8; card.scaleY = 0.8; cards.push(card); } // Create AI cards stacked vertically on right side - hidden from player var aiCards = []; var aiCardX = 1898; // Right side position var aiCardStartY = 800; // Starting Y position for AI for (var i = 0; i < 5; i++) { var aiCard = game.addChild(new Card(cardTypes[i])); aiCard.x = aiCardX; aiCard.y = aiCardStartY + i * cardSpacing; // Hide AI cards from player aiCard.visible = false; // Disable AI card interaction aiCard.down = function () {}; // Override down function to prevent interaction aiCards.push(aiCard); } // AI power-up usage logic with enhanced tracking var aiPowerUpTimer = 0; var aiPowerUpCooldown = 300; // 5 seconds between AI power-up usage var aiCardUsageHistory = []; var aiLastUsedCard = null; var aiPreferredCards = ['explosion', 'slow', 'speed', 'pierce']; // AI preference order function destroyBuildings() { // Determine which side lost and which buildings to destroy var losingBuildings = []; var winningCastle = null; var winningHouses = []; if (playerScore > aiScore) { // Player won, destroy AI buildings losingBuildings = [aiCastle, aiHouse1L, aiHouse2L, aiHouse3L, aiHouse1R, aiHouse2R, aiHouse3R]; winningCastle = playerCastle; winningHouses = [playerHouse1L, playerHouse2L, playerHouse3L, playerHouse1R, playerHouse2R, playerHouse3R]; } else { // AI won, destroy player buildings losingBuildings = [playerCastle, playerHouse1L, playerHouse2L, playerHouse3L, playerHouse1R, playerHouse2R, playerHouse3R]; winningCastle = aiCastle; winningHouses = [aiHouse1L, aiHouse2L, aiHouse3L, aiHouse1R, aiHouse2R, aiHouse3R]; } // Apply blue tint to winning castle and houses if (winningCastle) { tween(winningCastle, { tint: 0x4A90E2 }, { duration: 500, easing: tween.easeOut }); } for (var j = 0; j < winningHouses.length; j++) { tween(winningHouses[j], { tint: 0x4A90E2 }, { duration: 500, easing: tween.easeOut }); } // Destroy losing buildings with staggered timing for (var i = 0; i < losingBuildings.length; i++) { var building = losingBuildings[i]; var delay = i * 100; // 100ms delay between each building LK.setTimeout(function (currentBuilding) { return function () { // Create explosion effect at building location createExplosionEffect(currentBuilding.x, currentBuilding.y); // Animate building destruction tween(currentBuilding, { alpha: 0, scaleX: 0.3, scaleY: 0.3, rotation: Math.random() * 0.5 - 0.25 }, { duration: 800, easing: tween.easeOut, onFinish: function onFinish() { currentBuilding.visible = false; } }); }; }(building), delay); } } var gameTimer = LK.setInterval(function () { gameTime--; timerText.setText(gameTime.toString()); if (gameTime <= 0) { LK.clearInterval(gameTimer); // Start destruction animation before showing game over destroyBuildings(); // Show game over after destruction animation completes LK.setTimeout(function () { if (playerScore > aiScore) { LK.showYouWin(); } else { LK.showGameOver(); } }, 2000); // Wait 2 seconds for destruction animation to complete } }, 1000); var touchX = 1024; function createParticles(x, y) { for (var i = 0; i < 8; i++) { var particle = game.addChild(new Particle()); particle.x = x; particle.y = y; particles.push(particle); } } function activateSpeedBoost(isAI) { if (isAI) { // AI version - use AI card for (var i = 0; i < aiCards.length; i++) { if (aiCards[i].cardType === 'speed' && !aiCards[i].used && aiCards[i].cooldown <= 0 && aiCards[i].usageCount < aiCards[i].maxUsage) { aiCards[i].used = true; aiCards[i].cooldown = 300; aiCards[i].usageCount++; // Increment AI card usage counter // Track AI card usage aiLastUsedCard = 'speed'; aiCardUsageHistory.push({ type: 'speed', time: LK.ticks }); // Create visual effect at AI paddle to show card usage createParticles(aiPaddle.x, aiPaddle.y); break; } } } speedBoostActive = true; speedBoostTimer = 300; // 5 seconds at 60fps ball.speed = 24; // Increased from 18 to 24 for more noticeable effect // Play speed boost sound LK.getSound('speedSound').play(); // Speed boost animation with pulsing glow tween(ball, { tint: 0xFFFF00 }, { duration: 200 }); tween(ball, { scaleX: 1.2, scaleY: 1.2 }, { duration: 200, easing: tween.easeOut }); // Create pulsing effect function createPulse() { if (speedBoostActive) { tween(ball, { scaleX: 1.3, scaleY: 1.3 }, { duration: 300, easing: tween.easeInOut, onFinish: function onFinish() { if (speedBoostActive) { tween(ball, { scaleX: 1.1, scaleY: 1.1 }, { duration: 300, easing: tween.easeInOut, onFinish: createPulse }); } } }); } } createPulse(); // Create lightning particles around ball for (var i = 0; i < 5; i++) { var particle = game.addChild(new Particle()); particle.x = ball.x + (Math.random() - 0.5) * 80; particle.y = ball.y + (Math.random() - 0.5) * 80; particle.velocityX = (Math.random() - 0.5) * 12; particle.velocityY = (Math.random() - 0.5) * 12; particle.life = 20; particle.maxLife = 20; tween(particle, { tint: 0xFFFF00 }, { duration: 100 }); particles.push(particle); } // Create lightning trail effect that follows ball function createLightningTrail() { if (speedBoostActive) { // Create zigzag lightning behind ball for (var i = 0; i < 3; i++) { var particle = game.addChild(new Particle()); // Create zigzag pattern var angle = Math.random() * Math.PI * 2; var distance = 40 + Math.random() * 60; particle.x = ball.x - ball.velocityX * (i + 1) * 2 + Math.cos(angle) * distance; particle.y = ball.y - ball.velocityY * (i + 1) * 2 + Math.sin(angle) * distance; particle.velocityX = (Math.random() - 0.5) * 8; particle.velocityY = (Math.random() - 0.5) * 8; particle.life = 15; particle.maxLife = 15; tween(particle, { tint: 0xFFFFFF, scaleX: 1.5, scaleY: 0.3 }, { duration: 50, onFinish: function onFinish() { tween(particle, { tint: 0xFFFF00, scaleX: 0.8, scaleY: 0.8 }, { duration: 100 }); } }); particles.push(particle); } LK.setTimeout(createLightningTrail, 50); } } createLightningTrail(); } function activateExplosion(isAI) { if (isAI) { // AI version - use AI card for (var i = 0; i < aiCards.length; i++) { if (aiCards[i].cardType === 'explosion' && !aiCards[i].used && aiCards[i].cooldown <= 0 && aiCards[i].usageCount < aiCards[i].maxUsage) { aiCards[i].used = true; aiCards[i].cooldown = 300; aiCards[i].usageCount++; // Increment AI card usage counter // Track AI card usage aiLastUsedCard = 'explosion'; aiCardUsageHistory.push({ type: 'explosion', time: LK.ticks }); // Create visual effect at AI paddle to show card usage createParticles(aiPaddle.x, aiPaddle.y); break; } } } explosionActive = true; // Create multiple explosion waves createExplosionEffect(ball.x, ball.y); LK.setTimeout(function () { createExplosionEffect(ball.x, ball.y); }, 150); LK.setTimeout(function () { createExplosionEffect(ball.x, ball.y); }, 300); // Create expanding shockwave tween(ball, { scaleX: 3, scaleY: 3, alpha: 0.3 }, { duration: 500, easing: tween.easeOut, onFinish: function onFinish() { tween(ball, { scaleX: 1, scaleY: 1, alpha: 1 }, { duration: 200 }); } }); // Determine target based on who used explosion var targetPaddle = isAI ? playerPaddle : aiPaddle; var targetStunned = isAI ? 'playerStunned' : 'aiStunned'; var targetStunTimer = isAI ? 'playerStunTimer' : 'aiStunTimer'; // Make target paddle shake var _shakeTarget = function shakeTarget() { var isTargetStunned = isAI ? playerStunned : aiStunned; if (isTargetStunned) { var originalX = targetPaddle.x; tween(targetPaddle, { x: originalX + 5 }, { duration: 50, onFinish: function onFinish() { tween(targetPaddle, { x: originalX - 5 }, { duration: 50, onFinish: function onFinish() { tween(targetPaddle, { x: originalX }, { duration: 50, onFinish: _shakeTarget }); } }); } }); } }; // Stun the correct target if (isAI) { playerStunned = true; playerStunTimer = 300; // 5 seconds at 60fps } else { aiStunned = true; aiStunTimer = 300; // 5 seconds at 60fps } // Visual effect for stunned target with shaking tween(targetPaddle, { tint: 0xFF0000 }, { duration: 100 }); _shakeTarget(); } function activatePierce(isAI) { if (isAI) { // AI version - use AI card for (var i = 0; i < aiCards.length; i++) { if (aiCards[i].cardType === 'pierce' && !aiCards[i].used && aiCards[i].cooldown <= 0 && aiCards[i].usageCount < aiCards[i].maxUsage) { aiCards[i].used = true; aiCards[i].cooldown = 300; aiCards[i].usageCount++; // Increment AI card usage counter // Track AI card usage aiLastUsedCard = 'pierce'; aiCardUsageHistory.push({ type: 'pierce', time: LK.ticks }); // Create visual effect at AI paddle to show card usage createParticles(aiPaddle.x, aiPaddle.y); break; } } } pierceActive = true; pierceTimer = 180; // 3 seconds at 60fps pierceUser = isAI ? 'ai' : 'player'; // Track who used pierce // Pierce animation with glowing effect tween(ball, { tint: 0x44FF44 }, { duration: 200 }); // Make ball semi-transparent when AI uses pierce to show it's non-collidable if (isAI) { tween(ball, { alpha: 0.5 }, { duration: 200 }); } // Create rotating energy effect function createEnergyRing() { if (pierceActive) { for (var i = 0; i < 6; i++) { var particle = game.addChild(new Particle()); var angle = i / 6 * Math.PI * 2; var radius = 50; particle.x = ball.x + Math.cos(angle) * radius; particle.y = ball.y + Math.sin(angle) * radius; particle.velocityX = Math.cos(angle) * 3; particle.velocityY = Math.sin(angle) * 3; particle.life = 15; particle.maxLife = 15; tween(particle, { tint: 0x44FF44 }, { duration: 100 }); particles.push(particle); } LK.setTimeout(createEnergyRing, 200); } } createEnergyRing(); // Create glowing aura tween(ball, { scaleX: 1.1, scaleY: 1.1 }, { duration: 300, easing: tween.easeInOut, onFinish: function onFinish() { if (pierceActive) { tween(ball, { scaleX: 1.3, scaleY: 1.3 }, { duration: 300, easing: tween.easeInOut, onFinish: function onFinish() { if (pierceActive) { activatePierce(); } } }); } } }); // Auto-disable pierce after exactly 3 seconds LK.setTimeout(function () { if (pierceActive) { pierceActive = false; pierceTimer = 0; pierceUser = null; // Reset ball to original state tween(ball, { tint: 0xFFFFFF, scaleX: 1, scaleY: 1, alpha: 1 }, { duration: 200, easing: tween.easeOut }); } }, 3000); } function activateIllusion(isAI) { if (isAI) { // AI version - use AI card for (var i = 0; i < aiCards.length; i++) { if (aiCards[i].cardType === 'illusion' && !aiCards[i].used && aiCards[i].cooldown <= 0 && aiCards[i].usageCount < aiCards[i].maxUsage) { aiCards[i].used = true; aiCards[i].cooldown = 300; aiCards[i].usageCount++; // Track AI card usage aiLastUsedCard = 'illusion'; aiCardUsageHistory.push({ type: 'illusion', time: LK.ticks }); // Create visual effect at AI paddle to show card usage createParticles(aiPaddle.x, aiPaddle.y); break; } } } illusionActive = true; illusionTimer = 300; // 5 seconds at 60fps // Clear any existing illusion balls for (var i = illusionBalls.length - 1; i >= 0; i--) { illusionBalls[i].destroy(); } illusionBalls = []; // Create 4 fake balls around the real ball for (var i = 0; i < 4; i++) { var fakeBall = game.addChild(new Ball()); var angle = i / 4 * Math.PI * 2; var radius = 80 + Math.random() * 40; fakeBall.x = ball.x + Math.cos(angle) * radius; fakeBall.y = ball.y + Math.sin(angle) * radius; // Make fake balls move at exactly the same speed as real ball fakeBall.velocityX = ball.velocityX; fakeBall.velocityY = ball.velocityY; fakeBall.speed = ball.speed; // Make fake balls same size as real ball fakeBall.scaleX = 1; fakeBall.scaleY = 1; fakeBall.alpha = 0.8; fakeBall.tint = 0x9966cc; illusionBalls.push(fakeBall); // Add floating animation to fake balls tween(fakeBall, { scaleX: 1.1, scaleY: 1.1 }, { duration: 500, easing: tween.easeInOut, onFinish: function onFinish() { if (illusionActive) { tween(fakeBall, { scaleX: 0.9, scaleY: 0.9 }, { duration: 500, easing: tween.easeInOut }); } } }); } // Add shimmer effect to real ball tween(ball, { tint: 0x9966cc, alpha: 0.9 }, { duration: 200 }); // Create magical particles around all balls function createMagicalParticles() { if (illusionActive) { // Particles around real ball for (var i = 0; i < 3; i++) { var particle = game.addChild(new Particle()); var angle = Math.random() * Math.PI * 2; var radius = 50; particle.x = ball.x + Math.cos(angle) * radius; particle.y = ball.y + Math.sin(angle) * radius; particle.velocityX = Math.cos(angle) * 2; particle.velocityY = Math.sin(angle) * 2; particle.life = 25; particle.maxLife = 25; tween(particle, { tint: 0xcc66ff }, { duration: 100 }); particles.push(particle); } // Particles around fake balls for (var j = 0; j < illusionBalls.length; j++) { var fakeBall = illusionBalls[j]; if (fakeBall && !fakeBall.destroyed) { var particle = game.addChild(new Particle()); var angle = Math.random() * Math.PI * 2; var radius = 40; particle.x = fakeBall.x + Math.cos(angle) * radius; particle.y = fakeBall.y + Math.sin(angle) * radius; particle.velocityX = Math.cos(angle) * 1.5; particle.velocityY = Math.sin(angle) * 1.5; particle.life = 20; particle.maxLife = 20; tween(particle, { tint: 0x9966cc }, { duration: 100 }); particles.push(particle); } } LK.setTimeout(createMagicalParticles, 300); } } createMagicalParticles(); } function activateSlow(isAI) { if (isAI) { // AI version - use AI card for (var i = 0; i < aiCards.length; i++) { if (aiCards[i].cardType === 'slow' && !aiCards[i].used && aiCards[i].cooldown <= 0 && aiCards[i].usageCount < aiCards[i].maxUsage) { aiCards[i].used = true; aiCards[i].cooldown = 300; aiCards[i].usageCount++; // Increment AI card usage counter // Track AI card usage aiLastUsedCard = 'slow'; aiCardUsageHistory.push({ type: 'slow', time: LK.ticks }); // Create visual effect at AI paddle to show card usage createParticles(aiPaddle.x, aiPaddle.y); break; } } } slowActive = true; slowTimer = 300; // 5 seconds at 60fps ball.speed = 6; // Slow animation with ice effect tween(ball, { tint: 0x4444FF }, { duration: 200 }); tween(ball, { scaleX: 0.8, scaleY: 0.8 }, { duration: 200, easing: tween.easeOut }); // Create ice crystal particles function createIceCrystals() { if (slowActive) { for (var i = 0; i < 8; i++) { var particle = game.addChild(new Particle()); var angle = i / 8 * Math.PI * 2; var radius = 60; particle.x = ball.x + Math.cos(angle) * radius; particle.y = ball.y + Math.sin(angle) * radius; particle.velocityX = Math.cos(angle) * 2; particle.velocityY = Math.sin(angle) * 2; particle.life = 30; particle.maxLife = 30; tween(particle, { tint: 0x88CCFF }, { duration: 150 }); particles.push(particle); } LK.setTimeout(createIceCrystals, 400); } } createIceCrystals(); // Create freezing aura that pulses function createFreezingAura() { if (slowActive) { tween(ball, { scaleX: 0.6, scaleY: 0.6 }, { duration: 500, easing: tween.easeInOut, onFinish: function onFinish() { if (slowActive) { tween(ball, { scaleX: 0.9, scaleY: 0.9 }, { duration: 500, easing: tween.easeInOut, onFinish: createFreezingAura }); } } }); } } createFreezingAura(); } function createExplosionEffect(x, y) { // Create screen shake effect var originalX = game.x; var originalY = game.y; tween(game, { x: originalX + 10, y: originalY + 10 }, { duration: 50, onFinish: function onFinish() { tween(game, { x: originalX - 10, y: originalY - 10 }, { duration: 50, onFinish: function onFinish() { tween(game, { x: originalX, y: originalY }, { duration: 50 }); } }); } }); // Create explosion particles with color variations for (var i = 0; i < 15; i++) { var particle = game.addChild(new Particle()); particle.x = x + (Math.random() - 0.5) * 120; particle.y = y + (Math.random() - 0.5) * 120; particle.velocityX = (Math.random() - 0.5) * 15; particle.velocityY = (Math.random() - 0.5) * 15; particle.life = 40; particle.maxLife = 40; // Add color tinting with tween var colors = [0xff4444, 0xffaa44, 0xffff44, 0xff8844]; var randomColor = colors[Math.floor(Math.random() * colors.length)]; tween(particle, { tint: randomColor }, { duration: 200 }); particles.push(particle); } } function checkPaddleCollision(paddle, ball) { var paddleLeft = paddle.x - 100; var paddleRight = paddle.x + 100; var paddleTop = paddle.y - 20; var paddleBottom = paddle.y + 20; var ballLeft = ball.x - 30; var ballRight = ball.x + 30; var ballTop = ball.y - 30; var ballBottom = ball.y + 30; return ballRight > paddleLeft && ballLeft < paddleRight && ballBottom > paddleTop && ballTop < paddleBottom; } game.down = function (x, y, obj) { // Don't handle paddle movement during tutorial if (tutorialOverlay.visible) { return; } touchX = x; if (!playerStunned) { playerPaddle.moveTowards(x); } }; game.move = function (x, y, obj) { // Don't handle paddle movement during tutorial if (tutorialOverlay.visible) { return; } touchX = x; if (!playerStunned) { playerPaddle.moveTowards(x); } // Card hover effects removed }; game.update = function () { // Don't run game logic during tutorial if (tutorialOverlay.visible) { return; } // Handle power-up timers if (speedBoostActive) { speedBoostTimer--; if (speedBoostTimer <= 0) { speedBoostActive = false; ball.speed = 12; tween(ball, { tint: 0xFFFFFF }, { duration: 200 }); tween(ball, { scaleX: 1, scaleY: 1 }, { duration: 200, easing: tween.easeOut }); } } if (slowActive) { slowTimer--; if (slowTimer <= 0) { slowActive = false; ball.speed = 12; tween(ball, { tint: 0xFFFFFF }, { duration: 200 }); tween(ball, { scaleX: 1, scaleY: 1 }, { duration: 200, easing: tween.easeOut }); } } if (illusionActive) { illusionTimer--; if (illusionTimer <= 0) { illusionActive = false; // Reset real ball appearance tween(ball, { tint: 0xFFFFFF, alpha: 1 }, { duration: 300 }); // Remove all fake balls with fade effect for (var i = 0; i < illusionBalls.length; i++) { var fakeBall = illusionBalls[i]; if (fakeBall && !fakeBall.destroyed) { tween(fakeBall, { alpha: 0, scaleX: 0.5, scaleY: 0.5 }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { if (fakeBall && !fakeBall.destroyed) { fakeBall.destroy(); } } }); } } illusionBalls = []; } } if (pierceActive) { pierceTimer--; if (pierceTimer <= 0) { pierceActive = false; pierceUser = null; // Reset pierce user tween(ball, { tint: 0xFFFFFF, alpha: 1 }, { duration: 200 }); tween(ball, { scaleX: 1, scaleY: 1 }, { duration: 200, easing: tween.easeOut }); } } if (aiStunned) { aiStunTimer--; if (aiStunTimer <= 0) { aiStunned = false; tween(aiPaddle, { tint: 0xFFFFFF }, { duration: 200 }); } } if (playerStunned) { playerStunTimer--; if (playerStunTimer <= 0) { playerStunned = false; tween(playerPaddle, { tint: 0xFFFFFF }, { duration: 200 }); } } // Update fake balls during illusion if (illusionActive) { for (var i = illusionBalls.length - 1; i >= 0; i--) { var fakeBall = illusionBalls[i]; if (fakeBall && !fakeBall.destroyed) { // Apply speed boost effect to fake balls too if (speedBoostActive) { fakeBall.speed = 24; } else if (slowActive) { fakeBall.speed = 6; } else { fakeBall.speed = 12; } // Update fake ball position fakeBall.x += fakeBall.velocityX; fakeBall.y += fakeBall.velocityY; // Bounce fake balls off walls if (fakeBall.x <= 30 || fakeBall.x >= 2018) { fakeBall.velocityX = -fakeBall.velocityX; } // Remove fake balls that go off screen if (fakeBall.y < -100 || fakeBall.y > 2832) { fakeBall.destroy(); illusionBalls.splice(i, 1); } } else { illusionBalls.splice(i, 1); } } } // AI paddle follows ball with some delay (unless stunned) if (!aiStunned) { var targetBall = ball; // Default to real ball // If illusion is active, AI gets confused and targets random ball if (illusionActive && illusionBalls.length > 0) { // AI picks a random ball to follow (including real ball) var allBalls = [ball]; for (var i = 0; i < illusionBalls.length; i++) { if (illusionBalls[i] && !illusionBalls[i].destroyed) { allBalls.push(illusionBalls[i]); } } // AI randomly switches target every 30 frames during illusion if (LK.ticks % 30 === 0) { targetBall = allBalls[Math.floor(Math.random() * allBalls.length)]; } else if (game.aiTargetBall && !game.aiTargetBall.destroyed) { targetBall = game.aiTargetBall; } game.aiTargetBall = targetBall; } var targetX = targetBall.x + targetBall.velocityX * 10; if (targetX < 100) targetX = 100; if (targetX > 1948) targetX = 1948; aiPaddle.moveTowards(targetX); } // AI power-up usage logic with strategic decision making aiPowerUpTimer++; if (aiPowerUpTimer >= aiPowerUpCooldown) { // Use the ball AI is currently tracking for strategic decisions var currentTargetBall = ball; if (illusionActive && game.aiTargetBall && !game.aiTargetBall.destroyed) { currentTargetBall = game.aiTargetBall; } // Calculate strategic variables based on AI's current target var ballDistanceToAI = Math.abs(currentTargetBall.y - aiPaddle.y); var ballDistanceToPlayer = Math.abs(currentTargetBall.y - playerPaddle.y); var ballMovingTowardAI = currentTargetBall.velocityY < 0; var ballMovingTowardPlayer = currentTargetBall.velocityY > 0; var aiScore_playerScore_diff = aiScore - playerScore; var ballSpeed = Math.sqrt(currentTargetBall.velocityX * currentTargetBall.velocityX + currentTargetBall.velocityY * currentTargetBall.velocityY); var timeToReachAI = ballDistanceToAI / Math.abs(currentTargetBall.velocityY); var timeToReachPlayer = ballDistanceToPlayer / Math.abs(currentTargetBall.velocityY); var paddleCanReach = Math.abs(aiPaddle.x - (currentTargetBall.x + currentTargetBall.velocityX * timeToReachAI)) < 150; var ballNearAIGoal = currentTargetBall.y < 542 && ballMovingTowardAI; var ballNearPlayerGoal = currentTargetBall.y > 2190 && ballMovingTowardPlayer; var needsToScore = aiScore_playerScore_diff < 0; var gameTimeRunningOut = gameTime < 30; // Priority 1: Critical defense - ball about to hit AI goal if (ballNearAIGoal && ballMovingTowardAI && ballDistanceToAI < 550) { // Use slow to prevent goal when ball is about to hit AI castle for (var i = 0; i < aiCards.length; i++) { if (aiCards[i].cardType === 'slow' && !aiCards[i].used && aiCards[i].cooldown <= 0 && aiCards[i].usageCount < aiCards[i].maxUsage) { activateSlow(true); aiPowerUpTimer = 0; aiPowerUpCooldown = 180; // Quick cooldown for critical defense return; } } // If no slow available, use explosion as backup for (var i = 0; i < aiCards.length; i++) { if (aiCards[i].cardType === 'explosion' && !aiCards[i].used && aiCards[i].cooldown <= 0 && aiCards[i].usageCount < aiCards[i].maxUsage) { activateExplosion(true); aiPowerUpTimer = 0; aiPowerUpCooldown = 180; return; } } } // Priority 2: Aggressive scoring when AI needs points and ball is approaching AI if ((needsToScore || gameTimeRunningOut) && ballMovingTowardAI && ballDistanceToAI < 600 && ballDistanceToAI > 200) { // Use pierce to score when ball is approaching and AI needs points for (var i = 0; i < aiCards.length; i++) { if (aiCards[i].cardType === 'pierce' && !aiCards[i].used && aiCards[i].cooldown <= 0 && aiCards[i].usageCount < aiCards[i].maxUsage) { activatePierce(true); aiPowerUpTimer = 0; aiPowerUpCooldown = 250; return; } } } // Priority 3: Speed boost when AI can't reach ball in time if (ballMovingTowardAI && ballDistanceToAI < 500 && !paddleCanReach && timeToReachAI < 45) { // Use speed boost to make ball move faster so AI can potentially reach it for (var i = 0; i < aiCards.length; i++) { if (aiCards[i].cardType === 'speed' && !aiCards[i].used && aiCards[i].cooldown <= 0 && aiCards[i].usageCount < aiCards[i].maxUsage) { activateSpeedBoost(true); aiPowerUpTimer = 0; aiPowerUpCooldown = 300; return; } } } // Priority 4: Explosion when ball is approaching AI (general defense) if (ballMovingTowardAI && ballDistanceToAI < 400 && ballDistanceToAI > 100) { // Use explosion to stun and disrupt when ball is approaching for (var i = 0; i < aiCards.length; i++) { if (aiCards[i].cardType === 'explosion' && !aiCards[i].used && aiCards[i].cooldown <= 0) { activateExplosion(true); aiPowerUpTimer = 0; aiPowerUpCooldown = 320; return; } } } // Priority 5: Opportunistic offensive moves if (ballMovingTowardPlayer && needsToScore && Math.random() < 0.6) { // Use speed when ball is going toward player and AI needs to score for (var i = 0; i < aiCards.length; i++) { if (aiCards[i].cardType === 'speed' && !aiCards[i].used && aiCards[i].cooldown <= 0) { activateSpeedBoost(true); aiPowerUpTimer = 0; aiPowerUpCooldown = 400; return; } } } // Priority 6: Strategic slow usage if (ballMovingTowardPlayer && ballSpeed > 10 && ballDistanceToPlayer < 300 && Math.random() < 0.4) { // Use slow to make it harder for player to return the ball for (var i = 0; i < aiCards.length; i++) { if (aiCards[i].cardType === 'slow' && !aiCards[i].used && aiCards[i].cooldown <= 0) { activateSlow(true); aiPowerUpTimer = 0; aiPowerUpCooldown = 350; return; } } } // Priority 7: Illusion when ball is moving toward player and AI wants to confuse if (ballMovingTowardPlayer && ballDistanceToPlayer < 800 && Math.random() < 0.3) { // Use illusion to confuse player when ball is approaching them for (var i = 0; i < aiCards.length; i++) { if (aiCards[i].cardType === 'illusion' && !aiCards[i].used && aiCards[i].cooldown <= 0 && aiCards[i].usageCount < aiCards[i].maxUsage) { activateIllusion(true); aiPowerUpTimer = 0; aiPowerUpCooldown = 400; return; } } } // Priority 8: Random usage with low probability to add unpredictability if (Math.random() < 0.1) { var availableCards = []; for (var i = 0; i < aiCards.length; i++) { if (!aiCards[i].used && aiCards[i].cooldown <= 0 && aiCards[i].usageCount < aiCards[i].maxUsage) { availableCards.push(aiCards[i].cardType); } } if (availableCards.length > 0) { var randomCard = availableCards[Math.floor(Math.random() * availableCards.length)]; if (randomCard === 'speed') { activateSpeedBoost(true); } else if (randomCard === 'explosion') { activateExplosion(true); } else if (randomCard === 'pierce') { activatePierce(true); } else if (randomCard === 'slow') { activateSlow(true); } else if (randomCard === 'illusion') { activateIllusion(true); } aiPowerUpTimer = 0; aiPowerUpCooldown = 500 + Math.random() * 200; } } } // Check paddle collisions if (checkPaddleCollision(playerPaddle, ball) && !(pierceActive && pierceUser === 'ai')) { if (ball.velocityY > 0) { ball.velocityY = -ball.velocityY; var hitOffset = (ball.x - playerPaddle.x) / 100; ball.velocityX += hitOffset * 2; createParticles(ball.x, ball.y); LK.getSound('paddleHit').play(); } } if (checkPaddleCollision(aiPaddle, ball) && !(pierceActive && pierceUser === 'player')) { if (ball.velocityY < 0) { ball.velocityY = -ball.velocityY; var hitOffset = (ball.x - aiPaddle.x) / 100; ball.velocityX += hitOffset * 2; createParticles(ball.x, ball.y); LK.getSound('paddleHit').play(); } } // Check goal scoring if (ball.y < 90) { playerScore++; playerScoreText.setText(playerScore.toString()); LK.setScore(playerScore); ball.reset(); LK.getSound('goal').play(); } if (ball.y > 2642) { aiScore++; aiScoreText.setText(aiScore.toString()); ball.reset(); LK.getSound('goal').play(); } // Check collision with castle areas to create explosion particles if (ball.y <= 422 && ball.y >= 90 && ball.x >= 200 && ball.x <= 1848) { // Ball hit AI castle/houses area createExplosionEffect(ball.x, ball.y); } if (ball.y >= 2310 && ball.y <= 2642 && ball.x >= 200 && ball.x <= 1848) { // Ball hit player castle/houses area createExplosionEffect(ball.x, ball.y); } // Limit ball speed var maxSpeed = 12; var currentSpeed = Math.sqrt(ball.velocityX * ball.velocityX + ball.velocityY * ball.velocityY); if (currentSpeed > maxSpeed) { ball.velocityX = ball.velocityX / currentSpeed * maxSpeed; ball.velocityY = ball.velocityY / currentSpeed * maxSpeed; } };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var BackgroundCloud = Container.expand(function () {
var self = Container.call(this);
var cloud = self.attachAsset('cloud', {
anchorX: 0.5,
anchorY: 0.5
});
cloud.alpha = 0.7;
self.speed = 0.3 + Math.random() * 0.5;
// Floating animation
self.update = function () {
self.x += self.speed;
if (self.x > 2200) {
self.x = -150;
}
var time = LK.ticks * 0.01;
self.y += Math.sin(time) * 0.1;
};
return self;
});
var BackgroundFlower = Container.expand(function () {
var self = Container.call(this);
var flower = self.attachAsset('flower', {
anchorX: 0.5,
anchorY: 0.5
});
flower.alpha = 0.9;
// Gentle bobbing
self.update = function () {
var time = LK.ticks * 0.025;
flower.scaleX = 1 + Math.sin(time) * 0.1;
flower.scaleY = 1 + Math.sin(time) * 0.1;
};
return self;
});
var BackgroundGrass = Container.expand(function () {
var self = Container.call(this);
var grass = self.attachAsset('grass', {
anchorX: 0.5,
anchorY: 0.5
});
grass.alpha = 0.8;
// Gentle swaying
self.update = function () {
var time = LK.ticks * 0.03;
grass.rotation = Math.sin(time) * 0.05;
};
return self;
});
var BackgroundMountain = Container.expand(function () {
var self = Container.call(this);
var mountain = self.attachAsset('mountain', {
anchorX: 0.5,
anchorY: 1
});
mountain.alpha = 0.3;
mountain.tint = 0x4682B4;
return self;
});
var BackgroundTree = Container.expand(function () {
var self = Container.call(this);
var trunk = self.attachAsset('tree', {
anchorX: 0.5,
anchorY: 1
});
var leaves = self.attachAsset('treeLeaves', {
anchorX: 0.5,
anchorY: 1
});
leaves.y = -40;
// Add subtle swaying animation
self.update = function () {
var time = LK.ticks * 0.02;
leaves.rotation = Math.sin(time) * 0.1;
trunk.rotation = Math.sin(time) * 0.05;
};
return self;
});
var Ball = Container.expand(function () {
var self = Container.call(this);
var ballGraphics = self.attachAsset('ball', {
anchorX: 0.5,
anchorY: 0.5
});
self.velocityX = 0;
self.velocityY = 0;
self.speed = 12;
self.reset = function () {
self.x = 1024;
self.y = 1366;
var angle = (Math.random() > 0.5 ? 1 : -1) * (Math.PI / 6 + Math.random() * Math.PI / 3);
self.velocityX = Math.sin(angle) * self.speed;
// Ball only goes towards player (positive Y) or AI (negative Y), not random
self.velocityY = Math.cos(angle) * self.speed * (Math.random() > 0.5 ? 1 : -1);
};
self.update = function () {
self.x += self.velocityX;
self.y += self.velocityY;
// Bounce off left and right walls
if (self.x <= 30 || self.x >= 2018) {
self.velocityX = -self.velocityX;
}
};
return self;
});
var Card = Container.expand(function (cardType) {
var self = Container.call(this);
var cardGraphics = self.attachAsset('card', {
anchorX: 0.5,
anchorY: 0.5
});
self.cardType = cardType;
self.used = false;
self.cooldown = 0;
self.usageCount = 0; // Track how many times this card has been used
self.maxUsage = 2; // Maximum number of times this card can be used
var iconAsset = '';
var emojiText = '';
switch (cardType) {
case 'speed':
iconAsset = 'speedBoostIcon';
emojiText = '⚡';
break;
case 'explosion':
iconAsset = 'explosionIcon';
emojiText = '💥';
break;
case 'pierce':
iconAsset = 'pierceIcon';
emojiText = '🔥';
break;
case 'slow':
iconAsset = 'slowIcon';
emojiText = '❄️';
break;
case 'illusion':
iconAsset = 'illusionIcon';
emojiText = '🎭';
break;
}
var icon = self.attachAsset(iconAsset, {
anchorX: 0.5,
anchorY: 0.5
});
var emoji = new Text2(emojiText, {
size: 40,
fill: '#FFFFFF'
});
emoji.anchor.set(0.5, 0.5);
self.addChild(emoji);
self.down = function (x, y, obj) {
if (!self.used && self.cooldown <= 0 && self.usageCount < self.maxUsage) {
self.used = true;
self.cooldown = 300; // 5 seconds at 60fps
self.usageCount++; // Increment usage counter
// Play card selection sound
LK.getSound('cardSelect').play();
// Card activation animation
tween(self, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1
}, {
duration: 150,
easing: tween.easeOut
});
}
});
// Glowing effect
tween(cardGraphics, {
tint: 0xFFFFFF
}, {
duration: 100,
onFinish: function onFinish() {
tween(cardGraphics, {
alpha: 0.5,
tint: 0x666666
}, {
duration: 200
});
}
});
tween(icon, {
alpha: 0.5,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 200,
onFinish: function onFinish() {
tween(icon, {
scaleX: 1,
scaleY: 1
}, {
duration: 200
});
}
});
// Floating particles from card
for (var i = 0; i < 3; i++) {
var particle = game.addChild(new Particle());
particle.x = self.x + (Math.random() - 0.5) * 60;
particle.y = self.y + (Math.random() - 0.5) * 60;
particle.velocityX = (Math.random() - 0.5) * 6;
particle.velocityY = -Math.random() * 8 - 2;
particle.life = 40;
particle.maxLife = 40;
particles.push(particle);
}
switch (self.cardType) {
case 'speed':
activateSpeedBoost();
break;
case 'explosion':
activateExplosion();
break;
case 'pierce':
activatePierce();
break;
case 'slow':
activateSlow();
break;
case 'illusion':
activateIllusion();
break;
}
// Check if card has reached usage limit - trigger destruction immediately
if (self.usageCount >= self.maxUsage) {
self.hasExploded = true;
// Create explosion particles around the card
for (var i = 0; i < 12; i++) {
var particle = game.addChild(new Particle());
particle.x = self.x + (Math.random() - 0.5) * 100;
particle.y = self.y + (Math.random() - 0.5) * 120;
particle.velocityX = (Math.random() - 0.5) * 15;
particle.velocityY = (Math.random() - 0.5) * 15;
particle.life = 60;
particle.maxLife = 60;
// Random bright colors for explosion effect
var colors = [0xff4444, 0xffaa44, 0xffff44, 0xff8844, 0xaa44ff];
var randomColor = colors[Math.floor(Math.random() * colors.length)];
tween(particle, {
tint: randomColor
}, {
duration: 200
});
particles.push(particle);
}
// Create card fragments that fly out
for (var i = 0; i < 6; i++) {
var fragment = game.addChild(new Container());
var fragmentGraphics = fragment.attachAsset('card', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3,
scaleY: 0.3
});
fragment.x = self.x;
fragment.y = self.y;
var angle = i / 6 * Math.PI * 2;
var speed = 8 + Math.random() * 6;
var fragmentVelX = Math.cos(angle) * speed;
var fragmentVelY = Math.sin(angle) * speed;
// Fragment animation - fly out and fade
tween(fragment, {
x: fragment.x + fragmentVelX * 20,
y: fragment.y + fragmentVelY * 20,
rotation: (Math.random() - 0.5) * 4,
alpha: 0,
scaleX: 0.1,
scaleY: 0.1
}, {
duration: 1000 + Math.random() * 500,
easing: tween.easeOut,
onFinish: function onFinish() {
fragment.destroy();
}
});
}
// Card shake and explosion effect
var originalX = self.x;
var originalY = self.y;
tween(self, {
x: originalX + 15,
y: originalY + 15,
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 100,
onFinish: function onFinish() {
tween(self, {
x: originalX - 15,
y: originalY - 15,
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 100,
onFinish: function onFinish() {
tween(self, {
x: originalX,
y: originalY,
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 100
});
}
});
}
});
// Card is permanently disabled - make it visually distinct
tween(cardGraphics, {
alpha: 0.3,
tint: 0x666666
}, {
duration: 300,
easing: tween.easeOut
});
tween(icon, {
alpha: 0.3
}, {
duration: 300,
easing: tween.easeOut
});
}
}
};
self.move = function (x, y, obj) {
// Hover effect removed
};
self.up = function (x, y, obj) {
if (!self.used && self.cooldown <= 0) {
// Card unhover animation - return to normal size
tween(self, {
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 100,
easing: tween.easeOut
});
}
};
self.update = function () {
if (self.cooldown > 0) {
self.cooldown--;
if (self.cooldown <= 0 && self.usageCount < self.maxUsage) {
self.used = false;
// Card reactivation animation
tween(cardGraphics, {
alpha: 1,
tint: 0xFFFFFF
}, {
duration: 300,
easing: tween.easeOut
});
tween(icon, {
alpha: 1
}, {
duration: 300,
easing: tween.easeOut
});
// Sparkle effect on reactivation
for (var i = 0; i < 5; i++) {
var particle = game.addChild(new Particle());
particle.x = self.x + (Math.random() - 0.5) * 80;
particle.y = self.y + (Math.random() - 0.5) * 100;
particle.velocityX = (Math.random() - 0.5) * 4;
particle.velocityY = (Math.random() - 0.5) * 4;
particle.life = 20;
particle.maxLife = 20;
tween(particle, {
tint: 0xFFFFFF
}, {
duration: 100
});
particles.push(particle);
}
} else if (self.cooldown <= 0 && self.usageCount >= self.maxUsage) {
// Card destruction effect - only trigger once
if (!self.hasExploded) {
self.hasExploded = true;
// Create explosion particles around the card
for (var i = 0; i < 12; i++) {
var particle = game.addChild(new Particle());
particle.x = self.x + (Math.random() - 0.5) * 100;
particle.y = self.y + (Math.random() - 0.5) * 120;
particle.velocityX = (Math.random() - 0.5) * 15;
particle.velocityY = (Math.random() - 0.5) * 15;
particle.life = 60;
particle.maxLife = 60;
// Random bright colors for explosion effect
var colors = [0xff4444, 0xffaa44, 0xffff44, 0xff8844, 0xaa44ff];
var randomColor = colors[Math.floor(Math.random() * colors.length)];
tween(particle, {
tint: randomColor
}, {
duration: 200
});
particles.push(particle);
}
// Create card fragments that fly out
for (var i = 0; i < 6; i++) {
var fragment = game.addChild(new Container());
var fragmentGraphics = fragment.attachAsset('card', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3,
scaleY: 0.3
});
fragment.x = self.x;
fragment.y = self.y;
var angle = i / 6 * Math.PI * 2;
var speed = 8 + Math.random() * 6;
var fragmentVelX = Math.cos(angle) * speed;
var fragmentVelY = Math.sin(angle) * speed;
// Fragment animation - fly out and fade
tween(fragment, {
x: fragment.x + fragmentVelX * 20,
y: fragment.y + fragmentVelY * 20,
rotation: (Math.random() - 0.5) * 4,
alpha: 0,
scaleX: 0.1,
scaleY: 0.1
}, {
duration: 1000 + Math.random() * 500,
easing: tween.easeOut,
onFinish: function onFinish() {
fragment.destroy();
}
});
}
// Card shake and explosion effect
var originalX = self.x;
var originalY = self.y;
tween(self, {
x: originalX + 15,
y: originalY + 15,
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 100,
onFinish: function onFinish() {
tween(self, {
x: originalX - 15,
y: originalY - 15,
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 100,
onFinish: function onFinish() {
tween(self, {
x: originalX,
y: originalY,
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 100
});
}
});
}
});
}
// Card is permanently disabled - make it visually distinct
tween(cardGraphics, {
alpha: 0.3,
tint: 0x666666
}, {
duration: 300,
easing: tween.easeOut
});
tween(icon, {
alpha: 0.3
}, {
duration: 300,
easing: tween.easeOut
});
}
} else if (!self.used) {
// Idle floating animation - maintain original Y position with small float
var time = LK.ticks * 0.1;
var originalY = self.y;
if (self.originalY === undefined) {
self.originalY = self.y;
}
self.y = self.originalY + Math.sin(time + self.cardType.length) * 5;
// Subtle glow effect
if (LK.ticks % 180 === 0) {
tween(cardGraphics, {
tint: 0xFFFFFF
}, {
duration: 200,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(cardGraphics, {
tint: 0x4a90e2
}, {
duration: 200,
easing: tween.easeInOut
});
}
});
}
}
};
return self;
});
var Castle = Container.expand(function () {
var self = Container.call(this);
var castleGraphics = self.attachAsset('castle', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
var House = Container.expand(function () {
var self = Container.call(this);
var houseGraphics = self.attachAsset('house', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
var Paddle = Container.expand(function () {
var self = Container.call(this);
var paddleGraphics = self.attachAsset('paddle', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.targetX = self.x;
self.moveTowards = function (targetX) {
self.targetX = targetX;
if (self.targetX < 100) self.targetX = 100;
if (self.targetX > 1948) self.targetX = 1948;
};
self.update = function () {
if (Math.abs(self.x - self.targetX) > 2) {
if (self.x < self.targetX) {
self.x += self.speed;
} else {
self.x -= self.speed;
}
}
};
return self;
});
var Particle = Container.expand(function () {
var self = Container.call(this);
var particleGraphics = self.attachAsset('particle', {
anchorX: 0.5,
anchorY: 0.5
});
self.velocityX = (Math.random() - 0.5) * 8;
self.velocityY = (Math.random() - 0.5) * 8;
self.life = 30;
self.maxLife = 30;
self.update = function () {
self.x += self.velocityX;
self.y += self.velocityY;
self.life--;
var alpha = self.life / self.maxLife;
particleGraphics.alpha = alpha;
if (self.life <= 0) {
self.destroy();
particles.splice(particles.indexOf(self), 1);
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x228B22
});
/****
* Game Code
****/
// Simple background - removed complex gradient overlay
var gameTime = 90;
var playerScore = 0;
var aiScore = 0;
var particles = [];
var cards = [];
var speedBoostActive = false;
var speedBoostTimer = 0;
var explosionActive = false;
var pierceActive = false;
var pierceTimer = 0;
var pierceUser = null; // Track who used pierce power-up
var slowActive = false;
var slowTimer = 0;
var illusionActive = false;
var illusionTimer = 0;
var illusionBalls = [];
var aiStunned = false;
var aiStunTimer = 0;
var playerStunned = false;
var playerStunTimer = 0;
// Create UI elements
var timerText = new Text2('90', {
size: 80,
fill: 0xFFFFFF
});
timerText.anchor.set(0.5, 0);
LK.gui.top.addChild(timerText);
var playerScoreText = new Text2('0', {
size: 60,
fill: 0x4A90E2
});
playerScoreText.anchor.set(0, 1);
LK.gui.bottomLeft.addChild(playerScoreText);
var aiScoreText = new Text2('0', {
size: 60,
fill: 0xE24A4A
});
aiScoreText.anchor.set(0, 0);
LK.gui.topLeft.addChild(aiScoreText);
// Create tutorial overlay
var tutorialOverlay = game.addChild(new Container());
tutorialOverlay.x = 0;
tutorialOverlay.y = 0;
// Create semi-transparent background
var tutorialBg = tutorialOverlay.attachAsset('card', {
anchorX: 0,
anchorY: 0,
scaleX: 25.6,
scaleY: 27.32,
alpha: 0.8
});
tutorialBg.tint = 0x000000;
// Create tutorial title
var tutorialTitle = new Text2('POWER-UPS TUTORIAL', {
size: 80,
fill: '#FFFFFF'
});
tutorialTitle.anchor.set(0.5, 0.5);
tutorialTitle.x = 1024;
tutorialTitle.y = 400;
tutorialOverlay.addChild(tutorialTitle);
// Create game objective text
var objectiveText = new Text2('GAME OBJECTIVE: Score more goals than your opponent in 90 seconds!', {
size: 55,
fill: '#FFD700'
});
objectiveText.anchor.set(0.5, 0.5);
objectiveText.x = 1024;
objectiveText.y = 550;
tutorialOverlay.addChild(objectiveText);
// Create power-up instruction texts
var instructions = ['⚡ SPEED BOOST: Makes ball move faster for 5 seconds', '💥 EXPLOSION: Ball explodes and stuns opponent for 5 seconds', '🔥 PIERCE: Ball goes through opponent paddle for 3 seconds', '❄️ SLOW: Makes ball move slower for 5 seconds'];
var instructionTexts = [];
for (var i = 0; i < instructions.length; i++) {
var instructionText = new Text2(instructions[i], {
size: 50,
fill: '#FFFFFF'
});
instructionText.anchor.set(0.5, 0.5);
instructionText.x = 1024;
instructionText.y = 650 + i * 120;
tutorialOverlay.addChild(instructionText);
instructionTexts.push(instructionText);
}
// Create start button
var startButton = tutorialOverlay.attachAsset('card', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3,
scaleY: 1.5
});
startButton.x = 1024;
startButton.y = 1400;
startButton.tint = 0x4A90E2;
var startButtonText = new Text2('TAP TO START', {
size: 60,
fill: '#FFFFFF'
});
startButtonText.anchor.set(0.5, 0.5);
startButtonText.x = 1024;
startButtonText.y = 1400;
tutorialOverlay.addChild(startButtonText);
// Add tutorial click handler
tutorialOverlay.down = function (x, y, obj) {
// Hide tutorial with fade animation
tween(tutorialOverlay, {
alpha: 0
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
tutorialOverlay.visible = false;
}
});
};
// Create game objects
var playerPaddle = game.addChild(new Paddle());
playerPaddle.x = 1024;
playerPaddle.y = 2300;
var aiPaddle = game.addChild(new Paddle());
aiPaddle.x = 1024;
aiPaddle.y = 432;
var playerCastle = game.addChild(new Castle());
playerCastle.x = 1024;
playerCastle.y = 2542;
// Add houses around player castle
// Left side houses in a row
var playerHouse1L = game.addChild(new House());
playerHouse1L.x = 350;
playerHouse1L.y = 2500;
var playerHouse2L = game.addChild(new House());
playerHouse2L.x = 500;
playerHouse2L.y = 2500;
var playerHouse3L = game.addChild(new House());
playerHouse3L.x = 650;
playerHouse3L.y = 2500;
// Right side houses in a row
var playerHouse1R = game.addChild(new House());
playerHouse1R.x = 1398;
playerHouse1R.y = 2500;
var playerHouse2R = game.addChild(new House());
playerHouse2R.x = 1548;
playerHouse2R.y = 2500;
var playerHouse3R = game.addChild(new House());
playerHouse3R.x = 1698;
playerHouse3R.y = 2500;
var aiCastle = game.addChild(new Castle());
aiCastle.x = 1024;
aiCastle.y = 190;
// Add houses around AI castle
// Left side houses in a row
var aiHouse1L = game.addChild(new House());
aiHouse1L.x = 350;
aiHouse1L.y = 232;
var aiHouse2L = game.addChild(new House());
aiHouse2L.x = 500;
aiHouse2L.y = 232;
var aiHouse3L = game.addChild(new House());
aiHouse3L.x = 650;
aiHouse3L.y = 232;
// Right side houses in a row
var aiHouse1R = game.addChild(new House());
aiHouse1R.x = 1398;
aiHouse1R.y = 232;
var aiHouse2R = game.addChild(new House());
aiHouse2R.x = 1548;
aiHouse2R.y = 232;
var aiHouse3R = game.addChild(new House());
aiHouse3R.x = 1698;
aiHouse3R.y = 232;
// Clean green background - no decorative elements
var ball = game.addChild(new Ball());
ball.reset();
// Create player cards stacked vertically on left side
var cardTypes = ['speed', 'explosion', 'pierce', 'slow', 'illusion'];
var cardX = 150; // Left side position
var cardStartY = 1600; // Starting Y position (moved up for 5 cards)
var cardSpacing = 160; // Space between cards (reduced for 5 cards)
for (var i = 0; i < 5; i++) {
var card = game.addChild(new Card(cardTypes[i]));
card.x = cardX;
card.y = cardStartY + i * cardSpacing;
card.scaleX = 0.8;
card.scaleY = 0.8;
cards.push(card);
}
// Create AI cards stacked vertically on right side - hidden from player
var aiCards = [];
var aiCardX = 1898; // Right side position
var aiCardStartY = 800; // Starting Y position for AI
for (var i = 0; i < 5; i++) {
var aiCard = game.addChild(new Card(cardTypes[i]));
aiCard.x = aiCardX;
aiCard.y = aiCardStartY + i * cardSpacing;
// Hide AI cards from player
aiCard.visible = false;
// Disable AI card interaction
aiCard.down = function () {}; // Override down function to prevent interaction
aiCards.push(aiCard);
}
// AI power-up usage logic with enhanced tracking
var aiPowerUpTimer = 0;
var aiPowerUpCooldown = 300; // 5 seconds between AI power-up usage
var aiCardUsageHistory = [];
var aiLastUsedCard = null;
var aiPreferredCards = ['explosion', 'slow', 'speed', 'pierce']; // AI preference order
function destroyBuildings() {
// Determine which side lost and which buildings to destroy
var losingBuildings = [];
var winningCastle = null;
var winningHouses = [];
if (playerScore > aiScore) {
// Player won, destroy AI buildings
losingBuildings = [aiCastle, aiHouse1L, aiHouse2L, aiHouse3L, aiHouse1R, aiHouse2R, aiHouse3R];
winningCastle = playerCastle;
winningHouses = [playerHouse1L, playerHouse2L, playerHouse3L, playerHouse1R, playerHouse2R, playerHouse3R];
} else {
// AI won, destroy player buildings
losingBuildings = [playerCastle, playerHouse1L, playerHouse2L, playerHouse3L, playerHouse1R, playerHouse2R, playerHouse3R];
winningCastle = aiCastle;
winningHouses = [aiHouse1L, aiHouse2L, aiHouse3L, aiHouse1R, aiHouse2R, aiHouse3R];
}
// Apply blue tint to winning castle and houses
if (winningCastle) {
tween(winningCastle, {
tint: 0x4A90E2
}, {
duration: 500,
easing: tween.easeOut
});
}
for (var j = 0; j < winningHouses.length; j++) {
tween(winningHouses[j], {
tint: 0x4A90E2
}, {
duration: 500,
easing: tween.easeOut
});
}
// Destroy losing buildings with staggered timing
for (var i = 0; i < losingBuildings.length; i++) {
var building = losingBuildings[i];
var delay = i * 100; // 100ms delay between each building
LK.setTimeout(function (currentBuilding) {
return function () {
// Create explosion effect at building location
createExplosionEffect(currentBuilding.x, currentBuilding.y);
// Animate building destruction
tween(currentBuilding, {
alpha: 0,
scaleX: 0.3,
scaleY: 0.3,
rotation: Math.random() * 0.5 - 0.25
}, {
duration: 800,
easing: tween.easeOut,
onFinish: function onFinish() {
currentBuilding.visible = false;
}
});
};
}(building), delay);
}
}
var gameTimer = LK.setInterval(function () {
gameTime--;
timerText.setText(gameTime.toString());
if (gameTime <= 0) {
LK.clearInterval(gameTimer);
// Start destruction animation before showing game over
destroyBuildings();
// Show game over after destruction animation completes
LK.setTimeout(function () {
if (playerScore > aiScore) {
LK.showYouWin();
} else {
LK.showGameOver();
}
}, 2000); // Wait 2 seconds for destruction animation to complete
}
}, 1000);
var touchX = 1024;
function createParticles(x, y) {
for (var i = 0; i < 8; i++) {
var particle = game.addChild(new Particle());
particle.x = x;
particle.y = y;
particles.push(particle);
}
}
function activateSpeedBoost(isAI) {
if (isAI) {
// AI version - use AI card
for (var i = 0; i < aiCards.length; i++) {
if (aiCards[i].cardType === 'speed' && !aiCards[i].used && aiCards[i].cooldown <= 0 && aiCards[i].usageCount < aiCards[i].maxUsage) {
aiCards[i].used = true;
aiCards[i].cooldown = 300;
aiCards[i].usageCount++; // Increment AI card usage counter
// Track AI card usage
aiLastUsedCard = 'speed';
aiCardUsageHistory.push({
type: 'speed',
time: LK.ticks
});
// Create visual effect at AI paddle to show card usage
createParticles(aiPaddle.x, aiPaddle.y);
break;
}
}
}
speedBoostActive = true;
speedBoostTimer = 300; // 5 seconds at 60fps
ball.speed = 24; // Increased from 18 to 24 for more noticeable effect
// Play speed boost sound
LK.getSound('speedSound').play();
// Speed boost animation with pulsing glow
tween(ball, {
tint: 0xFFFF00
}, {
duration: 200
});
tween(ball, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 200,
easing: tween.easeOut
});
// Create pulsing effect
function createPulse() {
if (speedBoostActive) {
tween(ball, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 300,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (speedBoostActive) {
tween(ball, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 300,
easing: tween.easeInOut,
onFinish: createPulse
});
}
}
});
}
}
createPulse();
// Create lightning particles around ball
for (var i = 0; i < 5; i++) {
var particle = game.addChild(new Particle());
particle.x = ball.x + (Math.random() - 0.5) * 80;
particle.y = ball.y + (Math.random() - 0.5) * 80;
particle.velocityX = (Math.random() - 0.5) * 12;
particle.velocityY = (Math.random() - 0.5) * 12;
particle.life = 20;
particle.maxLife = 20;
tween(particle, {
tint: 0xFFFF00
}, {
duration: 100
});
particles.push(particle);
}
// Create lightning trail effect that follows ball
function createLightningTrail() {
if (speedBoostActive) {
// Create zigzag lightning behind ball
for (var i = 0; i < 3; i++) {
var particle = game.addChild(new Particle());
// Create zigzag pattern
var angle = Math.random() * Math.PI * 2;
var distance = 40 + Math.random() * 60;
particle.x = ball.x - ball.velocityX * (i + 1) * 2 + Math.cos(angle) * distance;
particle.y = ball.y - ball.velocityY * (i + 1) * 2 + Math.sin(angle) * distance;
particle.velocityX = (Math.random() - 0.5) * 8;
particle.velocityY = (Math.random() - 0.5) * 8;
particle.life = 15;
particle.maxLife = 15;
tween(particle, {
tint: 0xFFFFFF,
scaleX: 1.5,
scaleY: 0.3
}, {
duration: 50,
onFinish: function onFinish() {
tween(particle, {
tint: 0xFFFF00,
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 100
});
}
});
particles.push(particle);
}
LK.setTimeout(createLightningTrail, 50);
}
}
createLightningTrail();
}
function activateExplosion(isAI) {
if (isAI) {
// AI version - use AI card
for (var i = 0; i < aiCards.length; i++) {
if (aiCards[i].cardType === 'explosion' && !aiCards[i].used && aiCards[i].cooldown <= 0 && aiCards[i].usageCount < aiCards[i].maxUsage) {
aiCards[i].used = true;
aiCards[i].cooldown = 300;
aiCards[i].usageCount++; // Increment AI card usage counter
// Track AI card usage
aiLastUsedCard = 'explosion';
aiCardUsageHistory.push({
type: 'explosion',
time: LK.ticks
});
// Create visual effect at AI paddle to show card usage
createParticles(aiPaddle.x, aiPaddle.y);
break;
}
}
}
explosionActive = true;
// Create multiple explosion waves
createExplosionEffect(ball.x, ball.y);
LK.setTimeout(function () {
createExplosionEffect(ball.x, ball.y);
}, 150);
LK.setTimeout(function () {
createExplosionEffect(ball.x, ball.y);
}, 300);
// Create expanding shockwave
tween(ball, {
scaleX: 3,
scaleY: 3,
alpha: 0.3
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(ball, {
scaleX: 1,
scaleY: 1,
alpha: 1
}, {
duration: 200
});
}
});
// Determine target based on who used explosion
var targetPaddle = isAI ? playerPaddle : aiPaddle;
var targetStunned = isAI ? 'playerStunned' : 'aiStunned';
var targetStunTimer = isAI ? 'playerStunTimer' : 'aiStunTimer';
// Make target paddle shake
var _shakeTarget = function shakeTarget() {
var isTargetStunned = isAI ? playerStunned : aiStunned;
if (isTargetStunned) {
var originalX = targetPaddle.x;
tween(targetPaddle, {
x: originalX + 5
}, {
duration: 50,
onFinish: function onFinish() {
tween(targetPaddle, {
x: originalX - 5
}, {
duration: 50,
onFinish: function onFinish() {
tween(targetPaddle, {
x: originalX
}, {
duration: 50,
onFinish: _shakeTarget
});
}
});
}
});
}
};
// Stun the correct target
if (isAI) {
playerStunned = true;
playerStunTimer = 300; // 5 seconds at 60fps
} else {
aiStunned = true;
aiStunTimer = 300; // 5 seconds at 60fps
}
// Visual effect for stunned target with shaking
tween(targetPaddle, {
tint: 0xFF0000
}, {
duration: 100
});
_shakeTarget();
}
function activatePierce(isAI) {
if (isAI) {
// AI version - use AI card
for (var i = 0; i < aiCards.length; i++) {
if (aiCards[i].cardType === 'pierce' && !aiCards[i].used && aiCards[i].cooldown <= 0 && aiCards[i].usageCount < aiCards[i].maxUsage) {
aiCards[i].used = true;
aiCards[i].cooldown = 300;
aiCards[i].usageCount++; // Increment AI card usage counter
// Track AI card usage
aiLastUsedCard = 'pierce';
aiCardUsageHistory.push({
type: 'pierce',
time: LK.ticks
});
// Create visual effect at AI paddle to show card usage
createParticles(aiPaddle.x, aiPaddle.y);
break;
}
}
}
pierceActive = true;
pierceTimer = 180; // 3 seconds at 60fps
pierceUser = isAI ? 'ai' : 'player'; // Track who used pierce
// Pierce animation with glowing effect
tween(ball, {
tint: 0x44FF44
}, {
duration: 200
});
// Make ball semi-transparent when AI uses pierce to show it's non-collidable
if (isAI) {
tween(ball, {
alpha: 0.5
}, {
duration: 200
});
}
// Create rotating energy effect
function createEnergyRing() {
if (pierceActive) {
for (var i = 0; i < 6; i++) {
var particle = game.addChild(new Particle());
var angle = i / 6 * Math.PI * 2;
var radius = 50;
particle.x = ball.x + Math.cos(angle) * radius;
particle.y = ball.y + Math.sin(angle) * radius;
particle.velocityX = Math.cos(angle) * 3;
particle.velocityY = Math.sin(angle) * 3;
particle.life = 15;
particle.maxLife = 15;
tween(particle, {
tint: 0x44FF44
}, {
duration: 100
});
particles.push(particle);
}
LK.setTimeout(createEnergyRing, 200);
}
}
createEnergyRing();
// Create glowing aura
tween(ball, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 300,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (pierceActive) {
tween(ball, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 300,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (pierceActive) {
activatePierce();
}
}
});
}
}
});
// Auto-disable pierce after exactly 3 seconds
LK.setTimeout(function () {
if (pierceActive) {
pierceActive = false;
pierceTimer = 0;
pierceUser = null;
// Reset ball to original state
tween(ball, {
tint: 0xFFFFFF,
scaleX: 1,
scaleY: 1,
alpha: 1
}, {
duration: 200,
easing: tween.easeOut
});
}
}, 3000);
}
function activateIllusion(isAI) {
if (isAI) {
// AI version - use AI card
for (var i = 0; i < aiCards.length; i++) {
if (aiCards[i].cardType === 'illusion' && !aiCards[i].used && aiCards[i].cooldown <= 0 && aiCards[i].usageCount < aiCards[i].maxUsage) {
aiCards[i].used = true;
aiCards[i].cooldown = 300;
aiCards[i].usageCount++;
// Track AI card usage
aiLastUsedCard = 'illusion';
aiCardUsageHistory.push({
type: 'illusion',
time: LK.ticks
});
// Create visual effect at AI paddle to show card usage
createParticles(aiPaddle.x, aiPaddle.y);
break;
}
}
}
illusionActive = true;
illusionTimer = 300; // 5 seconds at 60fps
// Clear any existing illusion balls
for (var i = illusionBalls.length - 1; i >= 0; i--) {
illusionBalls[i].destroy();
}
illusionBalls = [];
// Create 4 fake balls around the real ball
for (var i = 0; i < 4; i++) {
var fakeBall = game.addChild(new Ball());
var angle = i / 4 * Math.PI * 2;
var radius = 80 + Math.random() * 40;
fakeBall.x = ball.x + Math.cos(angle) * radius;
fakeBall.y = ball.y + Math.sin(angle) * radius;
// Make fake balls move at exactly the same speed as real ball
fakeBall.velocityX = ball.velocityX;
fakeBall.velocityY = ball.velocityY;
fakeBall.speed = ball.speed;
// Make fake balls same size as real ball
fakeBall.scaleX = 1;
fakeBall.scaleY = 1;
fakeBall.alpha = 0.8;
fakeBall.tint = 0x9966cc;
illusionBalls.push(fakeBall);
// Add floating animation to fake balls
tween(fakeBall, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (illusionActive) {
tween(fakeBall, {
scaleX: 0.9,
scaleY: 0.9
}, {
duration: 500,
easing: tween.easeInOut
});
}
}
});
}
// Add shimmer effect to real ball
tween(ball, {
tint: 0x9966cc,
alpha: 0.9
}, {
duration: 200
});
// Create magical particles around all balls
function createMagicalParticles() {
if (illusionActive) {
// Particles around real ball
for (var i = 0; i < 3; i++) {
var particle = game.addChild(new Particle());
var angle = Math.random() * Math.PI * 2;
var radius = 50;
particle.x = ball.x + Math.cos(angle) * radius;
particle.y = ball.y + Math.sin(angle) * radius;
particle.velocityX = Math.cos(angle) * 2;
particle.velocityY = Math.sin(angle) * 2;
particle.life = 25;
particle.maxLife = 25;
tween(particle, {
tint: 0xcc66ff
}, {
duration: 100
});
particles.push(particle);
}
// Particles around fake balls
for (var j = 0; j < illusionBalls.length; j++) {
var fakeBall = illusionBalls[j];
if (fakeBall && !fakeBall.destroyed) {
var particle = game.addChild(new Particle());
var angle = Math.random() * Math.PI * 2;
var radius = 40;
particle.x = fakeBall.x + Math.cos(angle) * radius;
particle.y = fakeBall.y + Math.sin(angle) * radius;
particle.velocityX = Math.cos(angle) * 1.5;
particle.velocityY = Math.sin(angle) * 1.5;
particle.life = 20;
particle.maxLife = 20;
tween(particle, {
tint: 0x9966cc
}, {
duration: 100
});
particles.push(particle);
}
}
LK.setTimeout(createMagicalParticles, 300);
}
}
createMagicalParticles();
}
function activateSlow(isAI) {
if (isAI) {
// AI version - use AI card
for (var i = 0; i < aiCards.length; i++) {
if (aiCards[i].cardType === 'slow' && !aiCards[i].used && aiCards[i].cooldown <= 0 && aiCards[i].usageCount < aiCards[i].maxUsage) {
aiCards[i].used = true;
aiCards[i].cooldown = 300;
aiCards[i].usageCount++; // Increment AI card usage counter
// Track AI card usage
aiLastUsedCard = 'slow';
aiCardUsageHistory.push({
type: 'slow',
time: LK.ticks
});
// Create visual effect at AI paddle to show card usage
createParticles(aiPaddle.x, aiPaddle.y);
break;
}
}
}
slowActive = true;
slowTimer = 300; // 5 seconds at 60fps
ball.speed = 6;
// Slow animation with ice effect
tween(ball, {
tint: 0x4444FF
}, {
duration: 200
});
tween(ball, {
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 200,
easing: tween.easeOut
});
// Create ice crystal particles
function createIceCrystals() {
if (slowActive) {
for (var i = 0; i < 8; i++) {
var particle = game.addChild(new Particle());
var angle = i / 8 * Math.PI * 2;
var radius = 60;
particle.x = ball.x + Math.cos(angle) * radius;
particle.y = ball.y + Math.sin(angle) * radius;
particle.velocityX = Math.cos(angle) * 2;
particle.velocityY = Math.sin(angle) * 2;
particle.life = 30;
particle.maxLife = 30;
tween(particle, {
tint: 0x88CCFF
}, {
duration: 150
});
particles.push(particle);
}
LK.setTimeout(createIceCrystals, 400);
}
}
createIceCrystals();
// Create freezing aura that pulses
function createFreezingAura() {
if (slowActive) {
tween(ball, {
scaleX: 0.6,
scaleY: 0.6
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (slowActive) {
tween(ball, {
scaleX: 0.9,
scaleY: 0.9
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: createFreezingAura
});
}
}
});
}
}
createFreezingAura();
}
function createExplosionEffect(x, y) {
// Create screen shake effect
var originalX = game.x;
var originalY = game.y;
tween(game, {
x: originalX + 10,
y: originalY + 10
}, {
duration: 50,
onFinish: function onFinish() {
tween(game, {
x: originalX - 10,
y: originalY - 10
}, {
duration: 50,
onFinish: function onFinish() {
tween(game, {
x: originalX,
y: originalY
}, {
duration: 50
});
}
});
}
});
// Create explosion particles with color variations
for (var i = 0; i < 15; i++) {
var particle = game.addChild(new Particle());
particle.x = x + (Math.random() - 0.5) * 120;
particle.y = y + (Math.random() - 0.5) * 120;
particle.velocityX = (Math.random() - 0.5) * 15;
particle.velocityY = (Math.random() - 0.5) * 15;
particle.life = 40;
particle.maxLife = 40;
// Add color tinting with tween
var colors = [0xff4444, 0xffaa44, 0xffff44, 0xff8844];
var randomColor = colors[Math.floor(Math.random() * colors.length)];
tween(particle, {
tint: randomColor
}, {
duration: 200
});
particles.push(particle);
}
}
function checkPaddleCollision(paddle, ball) {
var paddleLeft = paddle.x - 100;
var paddleRight = paddle.x + 100;
var paddleTop = paddle.y - 20;
var paddleBottom = paddle.y + 20;
var ballLeft = ball.x - 30;
var ballRight = ball.x + 30;
var ballTop = ball.y - 30;
var ballBottom = ball.y + 30;
return ballRight > paddleLeft && ballLeft < paddleRight && ballBottom > paddleTop && ballTop < paddleBottom;
}
game.down = function (x, y, obj) {
// Don't handle paddle movement during tutorial
if (tutorialOverlay.visible) {
return;
}
touchX = x;
if (!playerStunned) {
playerPaddle.moveTowards(x);
}
};
game.move = function (x, y, obj) {
// Don't handle paddle movement during tutorial
if (tutorialOverlay.visible) {
return;
}
touchX = x;
if (!playerStunned) {
playerPaddle.moveTowards(x);
}
// Card hover effects removed
};
game.update = function () {
// Don't run game logic during tutorial
if (tutorialOverlay.visible) {
return;
}
// Handle power-up timers
if (speedBoostActive) {
speedBoostTimer--;
if (speedBoostTimer <= 0) {
speedBoostActive = false;
ball.speed = 12;
tween(ball, {
tint: 0xFFFFFF
}, {
duration: 200
});
tween(ball, {
scaleX: 1,
scaleY: 1
}, {
duration: 200,
easing: tween.easeOut
});
}
}
if (slowActive) {
slowTimer--;
if (slowTimer <= 0) {
slowActive = false;
ball.speed = 12;
tween(ball, {
tint: 0xFFFFFF
}, {
duration: 200
});
tween(ball, {
scaleX: 1,
scaleY: 1
}, {
duration: 200,
easing: tween.easeOut
});
}
}
if (illusionActive) {
illusionTimer--;
if (illusionTimer <= 0) {
illusionActive = false;
// Reset real ball appearance
tween(ball, {
tint: 0xFFFFFF,
alpha: 1
}, {
duration: 300
});
// Remove all fake balls with fade effect
for (var i = 0; i < illusionBalls.length; i++) {
var fakeBall = illusionBalls[i];
if (fakeBall && !fakeBall.destroyed) {
tween(fakeBall, {
alpha: 0,
scaleX: 0.5,
scaleY: 0.5
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
if (fakeBall && !fakeBall.destroyed) {
fakeBall.destroy();
}
}
});
}
}
illusionBalls = [];
}
}
if (pierceActive) {
pierceTimer--;
if (pierceTimer <= 0) {
pierceActive = false;
pierceUser = null; // Reset pierce user
tween(ball, {
tint: 0xFFFFFF,
alpha: 1
}, {
duration: 200
});
tween(ball, {
scaleX: 1,
scaleY: 1
}, {
duration: 200,
easing: tween.easeOut
});
}
}
if (aiStunned) {
aiStunTimer--;
if (aiStunTimer <= 0) {
aiStunned = false;
tween(aiPaddle, {
tint: 0xFFFFFF
}, {
duration: 200
});
}
}
if (playerStunned) {
playerStunTimer--;
if (playerStunTimer <= 0) {
playerStunned = false;
tween(playerPaddle, {
tint: 0xFFFFFF
}, {
duration: 200
});
}
}
// Update fake balls during illusion
if (illusionActive) {
for (var i = illusionBalls.length - 1; i >= 0; i--) {
var fakeBall = illusionBalls[i];
if (fakeBall && !fakeBall.destroyed) {
// Apply speed boost effect to fake balls too
if (speedBoostActive) {
fakeBall.speed = 24;
} else if (slowActive) {
fakeBall.speed = 6;
} else {
fakeBall.speed = 12;
}
// Update fake ball position
fakeBall.x += fakeBall.velocityX;
fakeBall.y += fakeBall.velocityY;
// Bounce fake balls off walls
if (fakeBall.x <= 30 || fakeBall.x >= 2018) {
fakeBall.velocityX = -fakeBall.velocityX;
}
// Remove fake balls that go off screen
if (fakeBall.y < -100 || fakeBall.y > 2832) {
fakeBall.destroy();
illusionBalls.splice(i, 1);
}
} else {
illusionBalls.splice(i, 1);
}
}
}
// AI paddle follows ball with some delay (unless stunned)
if (!aiStunned) {
var targetBall = ball; // Default to real ball
// If illusion is active, AI gets confused and targets random ball
if (illusionActive && illusionBalls.length > 0) {
// AI picks a random ball to follow (including real ball)
var allBalls = [ball];
for (var i = 0; i < illusionBalls.length; i++) {
if (illusionBalls[i] && !illusionBalls[i].destroyed) {
allBalls.push(illusionBalls[i]);
}
}
// AI randomly switches target every 30 frames during illusion
if (LK.ticks % 30 === 0) {
targetBall = allBalls[Math.floor(Math.random() * allBalls.length)];
} else if (game.aiTargetBall && !game.aiTargetBall.destroyed) {
targetBall = game.aiTargetBall;
}
game.aiTargetBall = targetBall;
}
var targetX = targetBall.x + targetBall.velocityX * 10;
if (targetX < 100) targetX = 100;
if (targetX > 1948) targetX = 1948;
aiPaddle.moveTowards(targetX);
}
// AI power-up usage logic with strategic decision making
aiPowerUpTimer++;
if (aiPowerUpTimer >= aiPowerUpCooldown) {
// Use the ball AI is currently tracking for strategic decisions
var currentTargetBall = ball;
if (illusionActive && game.aiTargetBall && !game.aiTargetBall.destroyed) {
currentTargetBall = game.aiTargetBall;
}
// Calculate strategic variables based on AI's current target
var ballDistanceToAI = Math.abs(currentTargetBall.y - aiPaddle.y);
var ballDistanceToPlayer = Math.abs(currentTargetBall.y - playerPaddle.y);
var ballMovingTowardAI = currentTargetBall.velocityY < 0;
var ballMovingTowardPlayer = currentTargetBall.velocityY > 0;
var aiScore_playerScore_diff = aiScore - playerScore;
var ballSpeed = Math.sqrt(currentTargetBall.velocityX * currentTargetBall.velocityX + currentTargetBall.velocityY * currentTargetBall.velocityY);
var timeToReachAI = ballDistanceToAI / Math.abs(currentTargetBall.velocityY);
var timeToReachPlayer = ballDistanceToPlayer / Math.abs(currentTargetBall.velocityY);
var paddleCanReach = Math.abs(aiPaddle.x - (currentTargetBall.x + currentTargetBall.velocityX * timeToReachAI)) < 150;
var ballNearAIGoal = currentTargetBall.y < 542 && ballMovingTowardAI;
var ballNearPlayerGoal = currentTargetBall.y > 2190 && ballMovingTowardPlayer;
var needsToScore = aiScore_playerScore_diff < 0;
var gameTimeRunningOut = gameTime < 30;
// Priority 1: Critical defense - ball about to hit AI goal
if (ballNearAIGoal && ballMovingTowardAI && ballDistanceToAI < 550) {
// Use slow to prevent goal when ball is about to hit AI castle
for (var i = 0; i < aiCards.length; i++) {
if (aiCards[i].cardType === 'slow' && !aiCards[i].used && aiCards[i].cooldown <= 0 && aiCards[i].usageCount < aiCards[i].maxUsage) {
activateSlow(true);
aiPowerUpTimer = 0;
aiPowerUpCooldown = 180; // Quick cooldown for critical defense
return;
}
}
// If no slow available, use explosion as backup
for (var i = 0; i < aiCards.length; i++) {
if (aiCards[i].cardType === 'explosion' && !aiCards[i].used && aiCards[i].cooldown <= 0 && aiCards[i].usageCount < aiCards[i].maxUsage) {
activateExplosion(true);
aiPowerUpTimer = 0;
aiPowerUpCooldown = 180;
return;
}
}
}
// Priority 2: Aggressive scoring when AI needs points and ball is approaching AI
if ((needsToScore || gameTimeRunningOut) && ballMovingTowardAI && ballDistanceToAI < 600 && ballDistanceToAI > 200) {
// Use pierce to score when ball is approaching and AI needs points
for (var i = 0; i < aiCards.length; i++) {
if (aiCards[i].cardType === 'pierce' && !aiCards[i].used && aiCards[i].cooldown <= 0 && aiCards[i].usageCount < aiCards[i].maxUsage) {
activatePierce(true);
aiPowerUpTimer = 0;
aiPowerUpCooldown = 250;
return;
}
}
}
// Priority 3: Speed boost when AI can't reach ball in time
if (ballMovingTowardAI && ballDistanceToAI < 500 && !paddleCanReach && timeToReachAI < 45) {
// Use speed boost to make ball move faster so AI can potentially reach it
for (var i = 0; i < aiCards.length; i++) {
if (aiCards[i].cardType === 'speed' && !aiCards[i].used && aiCards[i].cooldown <= 0 && aiCards[i].usageCount < aiCards[i].maxUsage) {
activateSpeedBoost(true);
aiPowerUpTimer = 0;
aiPowerUpCooldown = 300;
return;
}
}
}
// Priority 4: Explosion when ball is approaching AI (general defense)
if (ballMovingTowardAI && ballDistanceToAI < 400 && ballDistanceToAI > 100) {
// Use explosion to stun and disrupt when ball is approaching
for (var i = 0; i < aiCards.length; i++) {
if (aiCards[i].cardType === 'explosion' && !aiCards[i].used && aiCards[i].cooldown <= 0) {
activateExplosion(true);
aiPowerUpTimer = 0;
aiPowerUpCooldown = 320;
return;
}
}
}
// Priority 5: Opportunistic offensive moves
if (ballMovingTowardPlayer && needsToScore && Math.random() < 0.6) {
// Use speed when ball is going toward player and AI needs to score
for (var i = 0; i < aiCards.length; i++) {
if (aiCards[i].cardType === 'speed' && !aiCards[i].used && aiCards[i].cooldown <= 0) {
activateSpeedBoost(true);
aiPowerUpTimer = 0;
aiPowerUpCooldown = 400;
return;
}
}
}
// Priority 6: Strategic slow usage
if (ballMovingTowardPlayer && ballSpeed > 10 && ballDistanceToPlayer < 300 && Math.random() < 0.4) {
// Use slow to make it harder for player to return the ball
for (var i = 0; i < aiCards.length; i++) {
if (aiCards[i].cardType === 'slow' && !aiCards[i].used && aiCards[i].cooldown <= 0) {
activateSlow(true);
aiPowerUpTimer = 0;
aiPowerUpCooldown = 350;
return;
}
}
}
// Priority 7: Illusion when ball is moving toward player and AI wants to confuse
if (ballMovingTowardPlayer && ballDistanceToPlayer < 800 && Math.random() < 0.3) {
// Use illusion to confuse player when ball is approaching them
for (var i = 0; i < aiCards.length; i++) {
if (aiCards[i].cardType === 'illusion' && !aiCards[i].used && aiCards[i].cooldown <= 0 && aiCards[i].usageCount < aiCards[i].maxUsage) {
activateIllusion(true);
aiPowerUpTimer = 0;
aiPowerUpCooldown = 400;
return;
}
}
}
// Priority 8: Random usage with low probability to add unpredictability
if (Math.random() < 0.1) {
var availableCards = [];
for (var i = 0; i < aiCards.length; i++) {
if (!aiCards[i].used && aiCards[i].cooldown <= 0 && aiCards[i].usageCount < aiCards[i].maxUsage) {
availableCards.push(aiCards[i].cardType);
}
}
if (availableCards.length > 0) {
var randomCard = availableCards[Math.floor(Math.random() * availableCards.length)];
if (randomCard === 'speed') {
activateSpeedBoost(true);
} else if (randomCard === 'explosion') {
activateExplosion(true);
} else if (randomCard === 'pierce') {
activatePierce(true);
} else if (randomCard === 'slow') {
activateSlow(true);
} else if (randomCard === 'illusion') {
activateIllusion(true);
}
aiPowerUpTimer = 0;
aiPowerUpCooldown = 500 + Math.random() * 200;
}
}
}
// Check paddle collisions
if (checkPaddleCollision(playerPaddle, ball) && !(pierceActive && pierceUser === 'ai')) {
if (ball.velocityY > 0) {
ball.velocityY = -ball.velocityY;
var hitOffset = (ball.x - playerPaddle.x) / 100;
ball.velocityX += hitOffset * 2;
createParticles(ball.x, ball.y);
LK.getSound('paddleHit').play();
}
}
if (checkPaddleCollision(aiPaddle, ball) && !(pierceActive && pierceUser === 'player')) {
if (ball.velocityY < 0) {
ball.velocityY = -ball.velocityY;
var hitOffset = (ball.x - aiPaddle.x) / 100;
ball.velocityX += hitOffset * 2;
createParticles(ball.x, ball.y);
LK.getSound('paddleHit').play();
}
}
// Check goal scoring
if (ball.y < 90) {
playerScore++;
playerScoreText.setText(playerScore.toString());
LK.setScore(playerScore);
ball.reset();
LK.getSound('goal').play();
}
if (ball.y > 2642) {
aiScore++;
aiScoreText.setText(aiScore.toString());
ball.reset();
LK.getSound('goal').play();
}
// Check collision with castle areas to create explosion particles
if (ball.y <= 422 && ball.y >= 90 && ball.x >= 200 && ball.x <= 1848) {
// Ball hit AI castle/houses area
createExplosionEffect(ball.x, ball.y);
}
if (ball.y >= 2310 && ball.y <= 2642 && ball.x >= 200 && ball.x <= 1848) {
// Ball hit player castle/houses area
createExplosionEffect(ball.x, ball.y);
}
// Limit ball speed
var maxSpeed = 12;
var currentSpeed = Math.sqrt(ball.velocityX * ball.velocityX + ball.velocityY * ball.velocityY);
if (currentSpeed > maxSpeed) {
ball.velocityX = ball.velocityX / currentSpeed * maxSpeed;
ball.velocityY = ball.velocityY / currentSpeed * maxSpeed;
}
};