/****
* 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;
}
};