/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
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.friction = 0.98;
self.bounceStrength = 0.8;
self.kick = function (forceX, forceY) {
self.velocityX += forceX;
self.velocityY += forceY;
LK.getSound('kick').play();
};
self.update = function () {
self.x += self.velocityX;
self.y += self.velocityY;
self.velocityX *= self.friction;
self.velocityY *= self.friction;
// Bounce off walls
if (self.x <= 20 || self.x >= 2028) {
self.velocityX *= -self.bounceStrength;
self.x = Math.max(20, Math.min(2028, self.x));
}
// Check goal areas
if (self.y <= 100 && self.x >= 874 && self.x <= 1174) {
// Top goal (opponent scores)
if (!self.goalScored) {
self.goalScored = true;
opponentScore++;
LK.getSound('goal').play();
LK.effects.flashScreen(0xff4500, 500);
resetBall();
}
} else if (self.y >= 2632 && self.x >= 874 && self.x <= 1174) {
// Bottom goal (player scores)
if (!self.goalScored) {
self.goalScored = true;
playerScore++;
LK.getSound('goal').play();
LK.effects.flashScreen(0x4169e1, 500);
resetBall();
}
} else {
self.goalScored = false;
}
// Bounce off top and bottom (outside goal areas)
if (self.y <= 20 && (self.x < 874 || self.x > 1174) || self.y >= 2712 && (self.x < 874 || self.x > 1174)) {
self.velocityY *= -self.bounceStrength;
self.y = Math.max(20, Math.min(2712, self.y));
}
};
return self;
});
var Lanzaguisantes1 = Container.expand(function () {
var self = Container.call(this);
var lanzaguisantesGraphics = self.attachAsset('Lanzaguisantes', {
anchorX: 0.5,
anchorY: 0.5
});
// Add blue border to indicate team 1
var blueBorder = self.addChild(LK.getAsset('player', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.8,
scaleY: 1.8,
alpha: 0.6
}));
self.speed = 2.5;
self.targetX = 1024;
self.targetY = 400;
self.aiTimer = 0;
self.abilityCooldown = 0;
self.hasBall = false;
self.isStunned = false;
self.stunDuration = 0;
self.passCooldown = 0;
self.shootPea = function () {
if (self.abilityCooldown <= 0 && self.hasBall) {
self.abilityCooldown = 120; // 2 seconds cooldown
LK.getSound('ability').play();
var newPea = new Pea();
newPea.x = self.x;
newPea.y = self.y;
// Shoot towards top goal
newPea.shoot(1024, 50);
peas.push(newPea);
game.addChild(newPea);
}
};
self.update = function () {
self.aiTimer++;
// Handle stun state
if (self.isStunned) {
self.stunDuration--;
if (self.stunDuration <= 0) {
self.isStunned = false;
self.tint = 0xFFFFFF; // Reset tint
}
return; // Skip movement and other actions while stunned
}
// Check if bot has the ball
var ballDistance = Math.sqrt(Math.pow(ball.x - self.x, 2) + Math.pow(ball.y - self.y, 2));
self.hasBall = ballDistance < 80;
// Improved AI with passing behavior
if (self.hasBall) {
// If has ball, decide whether to pass, shoot pea, or advance
var distanceToGoal = Math.sqrt(Math.pow(1024 - self.x, 2) + Math.pow(50 - self.y, 2));
var distanceToPlayer = Math.sqrt(Math.pow(player.x - self.x, 2) + Math.pow(player.y - self.y, 2));
// Use pea ability if has ball and ability is ready
if (self.abilityCooldown <= 0) {
self.shootPea();
}
// Pass to player if they're in better position
else if (self.passCooldown <= 0 && distanceToPlayer < 400 && player.y > self.y && !player.isStunned) {
var passForceX = (player.x - self.x) / distanceToPlayer * 8;
var passForceY = (player.y - self.y) / distanceToPlayer * 8;
ball.kick(passForceX, passForceY);
self.passCooldown = 60; // 1 second cooldown
}
// If close to goal, aim for goal
else if (distanceToGoal < 300) {
self.targetX = 1024;
self.targetY = 100;
}
// Otherwise advance towards goal with ball
else {
self.targetX = 1024;
self.targetY = Math.max(self.y - 100, 100);
}
} else {
// Without ball, position strategically
var opponentDistance = Math.sqrt(Math.pow(opponent.x - self.x, 2) + Math.pow(opponent.y - self.y, 2));
var lanzaguisantes2Distance = Math.sqrt(Math.pow(lanzaguisantes2.x - self.x, 2) + Math.pow(lanzaguisantes2.y - self.y, 2));
var opponentHasBall = Math.sqrt(Math.pow(ball.x - opponent.x, 2) + Math.pow(ball.y - opponent.y, 2)) < 80;
var lanzaguisantes2HasBall = Math.sqrt(Math.pow(ball.x - lanzaguisantes2.x, 2) + Math.pow(ball.y - lanzaguisantes2.y, 2)) < 80;
// If opponents have ball and are close, try to steal it
if (opponentHasBall && opponentDistance < 200 || lanzaguisantes2HasBall && lanzaguisantes2Distance < 200) {
if (opponentHasBall && opponentDistance < lanzaguisantes2Distance) {
self.targetX = opponent.x;
self.targetY = opponent.y;
} else {
self.targetX = lanzaguisantes2.x;
self.targetY = lanzaguisantes2.y;
}
} else {
// Ball anywhere on field - be more aggressive
if (player.hasBall) {
// Position for receive pass
self.targetX = player.x - 200;
self.targetY = Math.max(player.y - 150, 200);
} else {
// Always move towards ball regardless of position
self.targetX = ball.x;
self.targetY = ball.y - 80;
}
}
}
// Move towards target
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 5) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
// Keep lanzaguisantes in bounds - allow movement across entire field
self.y = Math.max(50, Math.min(2682, self.y));
self.x = Math.max(50, Math.min(1998, self.x));
// Update pass cooldown
if (self.passCooldown > 0) {
self.passCooldown--;
}
// Check ball collision
if (ballDistance < 60) {
var distanceToGoal = Math.sqrt(Math.pow(1024 - self.x, 2) + Math.pow(50 - self.y, 2));
// If close to goal, always shoot towards goal with power
if (distanceToGoal < 500) {
var kickForceX = (1024 - self.x) / distanceToGoal * 15;
var kickForceY = (50 - self.y) / distanceToGoal * 15;
} else if (self.hasBall) {
// Normal ball control when has ball
var kickForceX = (ball.x - self.x) / ballDistance * 5;
var kickForceY = (ball.y - self.y) / ballDistance * 5;
} else {
// Steal ball from opponents - kick towards own goal area
var kickForceX = (1024 - ball.x) * 0.1;
var kickForceY = (2300 - ball.y) * 0.1;
}
ball.kick(kickForceX, kickForceY);
}
// Update cooldown
if (self.abilityCooldown > 0) {
self.abilityCooldown--;
}
};
return self;
});
var Lanzaguisantes2 = Container.expand(function () {
var self = Container.call(this);
var lanzaguisantesGraphics = self.attachAsset('Lanzaguisantes', {
anchorX: 0.5,
anchorY: 0.5
});
// Add red border to indicate team 2
var redBorder = self.addChild(LK.getAsset('opponent', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.8,
scaleY: 1.8,
alpha: 0.6
}));
self.speed = 2.5;
self.targetX = 1024;
self.targetY = 2300;
self.aiTimer = 0;
self.abilityCooldown = 0;
self.hasBall = false;
self.isStunned = false;
self.stunDuration = 0;
self.passCooldown = 0;
self.shootPea = function () {
if (self.abilityCooldown <= 0 && self.hasBall) {
self.abilityCooldown = 120; // 2 seconds cooldown
LK.getSound('ability').play();
var newPea = new Pea();
newPea.x = self.x;
newPea.y = self.y;
// Shoot towards bottom goal
newPea.shoot(1024, 2682);
peas.push(newPea);
game.addChild(newPea);
}
};
self.update = function () {
self.aiTimer++;
// Handle stun state
if (self.isStunned) {
self.stunDuration--;
if (self.stunDuration <= 0) {
self.isStunned = false;
self.tint = 0xFFFFFF; // Reset tint
}
return; // Skip movement and other actions while stunned
}
// Check if bot has the ball
var ballDistance = Math.sqrt(Math.pow(ball.x - self.x, 2) + Math.pow(ball.y - self.y, 2));
self.hasBall = ballDistance < 80;
// Improved AI with passing behavior
if (self.hasBall) {
// If has ball, decide whether to pass, shoot pea, or advance
var distanceToGoal = Math.sqrt(Math.pow(1024 - self.x, 2) + Math.pow(2682 - self.y, 2));
var distanceToOpponent = Math.sqrt(Math.pow(opponent.x - self.x, 2) + Math.pow(opponent.y - self.y, 2));
// Use pea ability if has ball and ability is ready
if (self.abilityCooldown <= 0) {
self.shootPea();
}
// Pass to opponent if they're in better position
else if (self.passCooldown <= 0 && distanceToOpponent < 400 && opponent.y < self.y && !opponent.isStunned) {
var passForceX = (opponent.x - self.x) / distanceToOpponent * 8;
var passForceY = (opponent.y - self.y) / distanceToOpponent * 8;
ball.kick(passForceX, passForceY);
self.passCooldown = 60; // 1 second cooldown
}
// If close to goal, aim for goal
else if (distanceToGoal < 300) {
self.targetX = 1024;
self.targetY = 2600;
}
// Otherwise advance towards goal with ball
else {
self.targetX = 1024;
self.targetY = Math.min(self.y + 100, 2600);
}
} else {
// Without ball, position strategically
var playerDistance = Math.sqrt(Math.pow(player.x - self.x, 2) + Math.pow(player.y - self.y, 2));
var lanzaguisantes1Distance = Math.sqrt(Math.pow(lanzaguisantes1.x - self.x, 2) + Math.pow(lanzaguisantes1.y - self.y, 2));
var playerHasBall = Math.sqrt(Math.pow(ball.x - player.x, 2) + Math.pow(ball.y - player.y, 2)) < 80;
var lanzaguisantes1HasBall = Math.sqrt(Math.pow(ball.x - lanzaguisantes1.x, 2) + Math.pow(ball.y - lanzaguisantes1.y, 2)) < 80;
// If opponents have ball and are close, try to steal it
if (playerHasBall && playerDistance < 200 || lanzaguisantes1HasBall && lanzaguisantes1Distance < 200) {
if (playerHasBall && playerDistance < lanzaguisantes1Distance) {
self.targetX = player.x;
self.targetY = player.y;
} else {
self.targetX = lanzaguisantes1.x;
self.targetY = lanzaguisantes1.y;
}
} else {
// Ball anywhere on field - be more aggressive
if (opponent.hasBall) {
// Position for receive pass
self.targetX = opponent.x + 200;
self.targetY = Math.max(opponent.y - 150, 200);
} else {
// Always move towards ball regardless of position
self.targetX = ball.x;
self.targetY = ball.y + 80;
}
}
}
// Move towards target
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 5) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
// Keep lanzaguisantes in bounds - allow movement across entire field
self.y = Math.max(50, Math.min(2682, self.y));
self.x = Math.max(50, Math.min(1998, self.x));
// Update pass cooldown
if (self.passCooldown > 0) {
self.passCooldown--;
}
// Check ball collision
if (ballDistance < 60) {
var distanceToGoal = Math.sqrt(Math.pow(1024 - self.x, 2) + Math.pow(2682 - self.y, 2));
// If close to goal, always shoot towards goal with power
if (distanceToGoal < 500) {
var kickForceX = (1024 - self.x) / distanceToGoal * 15;
var kickForceY = (2682 - self.y) / distanceToGoal * 15;
} else if (self.hasBall) {
// Normal ball control when has ball
var kickForceX = (ball.x - self.x) / ballDistance * 5;
var kickForceY = (ball.y - self.y) / ballDistance * 5;
} else {
// Steal ball from opponents - kick towards own goal area
var kickForceX = (1024 - ball.x) * 0.1;
var kickForceY = (400 - ball.y) * 0.1;
}
ball.kick(kickForceX, kickForceY);
}
// Update cooldown
if (self.abilityCooldown > 0) {
self.abilityCooldown--;
}
};
return self;
});
var Opponent = Container.expand(function () {
var self = Container.call(this);
var opponentGraphics = self.attachAsset('opponent', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 3;
self.targetX = 1024;
self.targetY = 500;
self.aiTimer = 0;
self.isStunned = false;
self.stunDuration = 0;
self.hasBall = false;
self.passCooldown = 0;
self.update = function () {
self.aiTimer++;
// Handle stun state
if (self.isStunned) {
self.stunDuration--;
if (self.stunDuration <= 0) {
self.isStunned = false;
self.tint = 0xFFFFFF; // Reset tint
}
return; // Skip movement and other actions while stunned
}
// Check if bot has the ball
var ballDistance = Math.sqrt(Math.pow(ball.x - self.x, 2) + Math.pow(ball.y - self.y, 2));
self.hasBall = ballDistance < 80;
// Improved AI with passing behavior
if (self.hasBall) {
// If has ball, decide whether to pass or advance
var distanceToGoal = Math.sqrt(Math.pow(1024 - self.x, 2) + Math.pow(2682 - self.y, 2));
var distanceToLanzaguisantes2 = Math.sqrt(Math.pow(lanzaguisantes2.x - self.x, 2) + Math.pow(lanzaguisantes2.y - self.y, 2));
// Pass to lanzaguisantes2 if they're in better position and pass is available
if (self.passCooldown <= 0 && distanceToLanzaguisantes2 < 400 && lanzaguisantes2.y < self.y && !lanzaguisantes2.isStunned) {
// Pass the ball to lanzaguisantes2
var passForceX = (lanzaguisantes2.x - self.x) / distanceToLanzaguisantes2 * 8;
var passForceY = (lanzaguisantes2.y - self.y) / distanceToLanzaguisantes2 * 8;
ball.kick(passForceX, passForceY);
self.passCooldown = 60; // 1 second cooldown
} else if (distanceToGoal < 300) {
// Close to goal, aim for goal
self.targetX = 1024;
self.targetY = 2600;
} else {
// Advance towards goal with ball
self.targetX = 1024;
self.targetY = Math.min(self.y + 100, 2600);
}
} else {
// Without ball, position strategically
var playerDistance = Math.sqrt(Math.pow(player.x - self.x, 2) + Math.pow(player.y - self.y, 2));
var lanzaguisantes1Distance = Math.sqrt(Math.pow(lanzaguisantes1.x - self.x, 2) + Math.pow(lanzaguisantes1.y - self.y, 2));
var playerHasBall = Math.sqrt(Math.pow(ball.x - player.x, 2) + Math.pow(ball.y - player.y, 2)) < 80;
var lanzaguisantes1HasBall = Math.sqrt(Math.pow(ball.x - lanzaguisantes1.x, 2) + Math.pow(ball.y - lanzaguisantes1.y, 2)) < 80;
// If opponents have ball and are close, try to steal it
if (playerHasBall && playerDistance < 200 || lanzaguisantes1HasBall && lanzaguisantes1Distance < 200) {
if (playerHasBall && playerDistance < lanzaguisantes1Distance) {
self.targetX = player.x;
self.targetY = player.y;
} else {
self.targetX = lanzaguisantes1.x;
self.targetY = lanzaguisantes1.y;
}
} else {
// Ball anywhere on field - be more aggressive
if (lanzaguisantes2.hasBall) {
// Position for receive pass
self.targetX = lanzaguisantes2.x + 200;
self.targetY = Math.max(lanzaguisantes2.y - 150, 200);
} else {
// Always move towards ball regardless of position
self.targetX = ball.x;
self.targetY = ball.y - 100;
}
}
}
// Move towards target
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 5) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
// Keep opponent in bounds - allow movement across entire field
self.y = Math.max(50, Math.min(2682, self.y));
self.x = Math.max(50, Math.min(1998, self.x));
// Update pass cooldown
if (self.passCooldown > 0) {
self.passCooldown--;
}
// Check collision with lanzaguisantes2
var lanzaguisantes2Distance = Math.sqrt(Math.pow(lanzaguisantes2.x - self.x, 2) + Math.pow(lanzaguisantes2.y - self.y, 2));
if (lanzaguisantes2Distance < 70) {
// Calculate bounce direction
var bounceX = (self.x - lanzaguisantes2.x) / lanzaguisantes2Distance;
var bounceY = (self.y - lanzaguisantes2.y) / lanzaguisantes2Distance;
// Apply bounce force
self.x += bounceX * 20;
self.y += bounceY * 20;
lanzaguisantes2.x -= bounceX * 20;
lanzaguisantes2.y -= bounceY * 20;
// Keep characters in bounds
self.x = Math.max(40, Math.min(2008, self.x));
self.y = Math.max(40, Math.min(2692, self.y));
lanzaguisantes2.x = Math.max(40, Math.min(2008, lanzaguisantes2.x));
lanzaguisantes2.y = Math.max(40, Math.min(2692, lanzaguisantes2.y));
}
// Check ball collision
if (ballDistance < 60) {
var distanceToGoal = Math.sqrt(Math.pow(1024 - self.x, 2) + Math.pow(2682 - self.y, 2));
// If close to goal, always shoot towards goal with power
if (distanceToGoal < 500) {
var kickForceX = (1024 - self.x) / distanceToGoal * 15;
var kickForceY = (2682 - self.y) / distanceToGoal * 15;
} else if (self.hasBall) {
// Normal ball control when has ball
var kickForceX = (ball.x - self.x) / ballDistance * 6;
var kickForceY = (ball.y - self.y) / ballDistance * 6;
} else {
// Steal ball from player - kick towards own goal area
var kickForceX = (1024 - ball.x) * 0.1;
var kickForceY = (400 - ball.y) * 0.1;
}
ball.kick(kickForceX, kickForceY);
}
};
return self;
});
var Pea = Container.expand(function () {
var self = Container.call(this);
var peaGraphics = self.attachAsset('ball', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 0.6
});
peaGraphics.tint = 0x90EE90;
self.velocityX = 0;
self.velocityY = 0;
self.speed = 12;
self.shoot = function (targetX, targetY) {
var dx = targetX - self.x;
var dy = targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
self.velocityX = dx / distance * self.speed;
self.velocityY = dy / distance * self.speed;
}
};
self.update = function () {
self.x += self.velocityX;
self.y += self.velocityY;
// Remove pea if it goes off screen
if (self.x < 0 || self.x > 2048 || self.y < 0 || self.y > 2732) {
self.destroy();
for (var i = peas.length - 1; i >= 0; i--) {
if (peas[i] === self) {
peas.splice(i, 1);
break;
}
}
}
// Check collision with player
var playerDistance = Math.sqrt(Math.pow(player.x - self.x, 2) + Math.pow(player.y - self.y, 2));
if (playerDistance < 50) {
// Stun player for 3 seconds
player.isStunned = true;
player.stunDuration = 180; // 3 seconds at 60fps
tween(player, {
tint: 0xFF0000
}, {
duration: 500,
onFinish: function onFinish() {
tween(player, {
tint: 0xFFFFFF
}, {
duration: 500
});
}
});
self.destroy();
for (var i = peas.length - 1; i >= 0; i--) {
if (peas[i] === self) {
peas.splice(i, 1);
break;
}
}
return;
}
// Check collision with opponent
var opponentDistance = Math.sqrt(Math.pow(opponent.x - self.x, 2) + Math.pow(opponent.y - self.y, 2));
if (opponentDistance < 50) {
// Stun opponent for 3 seconds
opponent.isStunned = true;
opponent.stunDuration = 180; // 3 seconds at 60fps
tween(opponent, {
tint: 0xFF0000
}, {
duration: 500,
onFinish: function onFinish() {
tween(opponent, {
tint: 0xFFFFFF
}, {
duration: 500
});
}
});
self.destroy();
for (var i = peas.length - 1; i >= 0; i--) {
if (peas[i] === self) {
peas.splice(i, 1);
break;
}
}
return;
}
// Check collision with lanzaguisantes1
var lanzaguisantes1Distance = Math.sqrt(Math.pow(lanzaguisantes1.x - self.x, 2) + Math.pow(lanzaguisantes1.y - self.y, 2));
if (lanzaguisantes1Distance < 50) {
// Stun lanzaguisantes1 for 3 seconds
lanzaguisantes1.isStunned = true;
lanzaguisantes1.stunDuration = 180; // 3 seconds at 60fps
tween(lanzaguisantes1, {
tint: 0xFF0000
}, {
duration: 500,
onFinish: function onFinish() {
tween(lanzaguisantes1, {
tint: 0xFFFFFF
}, {
duration: 500
});
}
});
self.destroy();
for (var i = peas.length - 1; i >= 0; i--) {
if (peas[i] === self) {
peas.splice(i, 1);
break;
}
}
return;
}
// Check collision with lanzaguisantes2
var lanzaguisantes2Distance = Math.sqrt(Math.pow(lanzaguisantes2.x - self.x, 2) + Math.pow(lanzaguisantes2.y - self.y, 2));
if (lanzaguisantes2Distance < 50) {
// Stun lanzaguisantes2 for 3 seconds
lanzaguisantes2.isStunned = true;
lanzaguisantes2.stunDuration = 180; // 3 seconds at 60fps
tween(lanzaguisantes2, {
tint: 0xFF0000
}, {
duration: 500,
onFinish: function onFinish() {
tween(lanzaguisantes2, {
tint: 0xFFFFFF
}, {
duration: 500
});
}
});
self.destroy();
for (var i = peas.length - 1; i >= 0; i--) {
if (peas[i] === self) {
peas.splice(i, 1);
break;
}
}
return;
}
// Check collision with ball
var ballDistance = Math.sqrt(Math.pow(ball.x - self.x, 2) + Math.pow(ball.y - self.y, 2));
if (ballDistance < 40) {
var kickForceX = self.velocityX * 0.8;
var kickForceY = self.velocityY * 0.8;
ball.kick(kickForceX, kickForceY);
self.destroy();
for (var i = peas.length - 1; i >= 0; i--) {
if (peas[i] === self) {
peas.splice(i, 1);
break;
}
}
}
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 4;
self.targetX = 1024;
self.targetY = 2200;
self.abilityType = 'speed'; // 'speed', 'power', 'shield'
self.abilityCooldown = 0;
self.abilityDuration = 0;
self.abilityEffect = null;
self.isStunned = false;
self.stunDuration = 0;
self.activateAbility = function () {
if (self.abilityCooldown <= 0) {
self.abilityCooldown = 180; // 3 seconds at 60fps
self.abilityDuration = 120; // 2 seconds effect
LK.getSound('ability').play();
if (self.abilityType === 'speed') {
self.abilityEffect = self.addChild(LK.getAsset('speedBoostEffect', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.7
}));
self.speed = 8;
}
}
};
self.update = function () {
// Handle stun state first - prevents all actions while stunned
if (self.isStunned) {
self.stunDuration--;
if (self.stunDuration <= 0) {
self.isStunned = false;
self.tint = 0xFFFFFF; // Reset tint
}
return; // Skip movement and other actions while stunned
}
// Move towards target
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 5) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
// Update ability
if (self.abilityDuration > 0) {
self.abilityDuration--;
if (self.abilityEffect) {
self.abilityEffect.rotation += 0.1;
}
} else {
if (self.abilityEffect) {
self.removeChild(self.abilityEffect);
self.abilityEffect = null;
}
self.speed = 4;
}
if (self.abilityCooldown > 0) {
self.abilityCooldown--;
}
// Check collision with other characters
var opponentDistance = Math.sqrt(Math.pow(opponent.x - self.x, 2) + Math.pow(opponent.y - self.y, 2));
if (opponentDistance < 70) {
// Calculate bounce direction
var bounceX = (self.x - opponent.x) / opponentDistance;
var bounceY = (self.y - opponent.y) / opponentDistance;
// Apply bounce force
self.x += bounceX * 20;
self.y += bounceY * 20;
opponent.x -= bounceX * 20;
opponent.y -= bounceY * 20;
// Keep characters in bounds
self.x = Math.max(40, Math.min(2008, self.x));
self.y = Math.max(40, Math.min(2692, self.y));
opponent.x = Math.max(50, Math.min(1998, opponent.x));
opponent.y = Math.max(50, Math.min(2682, opponent.y));
}
var lanzaguisantes1Distance = Math.sqrt(Math.pow(lanzaguisantes1.x - self.x, 2) + Math.pow(lanzaguisantes1.y - self.y, 2));
if (lanzaguisantes1Distance < 70) {
// Calculate bounce direction
var bounceX = (self.x - lanzaguisantes1.x) / lanzaguisantes1Distance;
var bounceY = (self.y - lanzaguisantes1.y) / lanzaguisantes1Distance;
// Apply bounce force
self.x += bounceX * 20;
self.y += bounceY * 20;
lanzaguisantes1.x -= bounceX * 20;
lanzaguisantes1.y -= bounceY * 20;
// Keep characters in bounds
self.x = Math.max(40, Math.min(2008, self.x));
self.y = Math.max(40, Math.min(2692, self.y));
lanzaguisantes1.x = Math.max(40, Math.min(2008, lanzaguisantes1.x));
lanzaguisantes1.y = Math.max(40, Math.min(2692, lanzaguisantes1.y));
}
var lanzaguisantes2Distance = Math.sqrt(Math.pow(lanzaguisantes2.x - self.x, 2) + Math.pow(lanzaguisantes2.y - self.y, 2));
if (lanzaguisantes2Distance < 70) {
// Calculate bounce direction
var bounceX = (self.x - lanzaguisantes2.x) / lanzaguisantes2Distance;
var bounceY = (self.y - lanzaguisantes2.y) / lanzaguisantes2Distance;
// Apply bounce force
self.x += bounceX * 20;
self.y += bounceY * 20;
lanzaguisantes2.x -= bounceX * 20;
lanzaguisantes2.y -= bounceY * 20;
// Keep characters in bounds
self.x = Math.max(40, Math.min(2008, self.x));
self.y = Math.max(40, Math.min(2692, self.y));
lanzaguisantes2.x = Math.max(40, Math.min(2008, lanzaguisantes2.x));
lanzaguisantes2.y = Math.max(40, Math.min(2692, lanzaguisantes2.y));
}
// Check ball collision
var ballDistance = Math.sqrt(Math.pow(ball.x - self.x, 2) + Math.pow(ball.y - self.y, 2));
if (ballDistance < 50) {
var kickForceX = (ball.x - self.x) / ballDistance * 8;
var kickForceY = (ball.y - self.y) / ballDistance * 8;
if (self.abilityType === 'power' && self.abilityDuration > 0) {
kickForceX *= 2;
kickForceY *= 2;
}
ball.kick(kickForceX, kickForceY);
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x228b22
});
/****
* Game Code
****/
// Game variables
var playerScore = 0;
var opponentScore = 0;
var targetScore = 3;
var ball;
var player;
var opponent;
var lanzaguisantes1;
var lanzaguisantes2;
var peas = [];
// Create field elements
var field = game.addChild(LK.getAsset('field', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
}));
var centerLine = game.addChild(LK.getAsset('centerLine', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366
}));
var centerCircle = game.addChild(LK.getAsset('centerCircle', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366,
alpha: 0.3
}));
// Create walls
var leftWall = game.addChild(LK.getAsset('wall', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
}));
var rightWall = game.addChild(LK.getAsset('wall', {
anchorX: 0,
anchorY: 0,
x: 2038,
y: 0
}));
var topWall = game.addChild(LK.getAsset('wall', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
width: 2048,
height: 10
}));
var bottomWall = game.addChild(LK.getAsset('wall', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 2722,
width: 2048,
height: 10
}));
// Create goals
var topGoal = game.addChild(LK.getAsset('topGoal', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 50
}));
var bottomGoal = game.addChild(LK.getAsset('bottomGoal', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 2682
}));
// Create score display
var scoreText = new Text2('Player: 0 - Opponent: 0', {
size: 60,
fill: 0xFFFFFF
});
scoreText.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreText);
// Create ability button
var abilityButton = new Text2('SPEED BOOST', {
size: 40,
fill: 0xFFFF00
});
abilityButton.anchor.set(0.5, 1);
LK.gui.bottom.addChild(abilityButton);
// Create game objects
ball = game.addChild(new Ball());
player = game.addChild(new Player());
opponent = game.addChild(new Opponent());
lanzaguisantes1 = game.addChild(new Lanzaguisantes1());
lanzaguisantes2 = game.addChild(new Lanzaguisantes2());
function resetBall() {
LK.setTimeout(function () {
ball.x = 1024;
ball.y = 1366;
ball.velocityX = 0;
ball.velocityY = 0;
ball.goalScored = false;
// Update score display
scoreText.setText('Player: ' + playerScore + ' - Opponent: ' + opponentScore);
// Check win condition
if (playerScore >= targetScore) {
LK.showYouWin();
} else if (opponentScore >= targetScore) {
LK.showGameOver();
}
}, 1000);
}
// Initialize positions
resetBall();
player.x = 1024;
player.y = 2200;
opponent.x = 1024;
opponent.y = 500;
lanzaguisantes1.x = 800;
lanzaguisantes1.y = 2000;
lanzaguisantes2.x = 1200;
lanzaguisantes2.y = 700;
// Touch controls
game.down = function (x, y, obj) {
player.targetX = x;
player.targetY = y;
};
game.move = function (x, y, obj) {
player.targetX = x;
player.targetY = y;
};
// Ability button handler
abilityButton.down = function (x, y, obj) {
player.activateAbility();
};
game.update = function () {
// Update ability button text
if (player.abilityCooldown > 0) {
abilityButton.setText('COOLDOWN: ' + Math.ceil(player.abilityCooldown / 60));
abilityButton.tint = 0x666666;
} else {
abilityButton.setText('SPEED BOOST');
abilityButton.tint = 0xffff00;
}
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
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.friction = 0.98;
self.bounceStrength = 0.8;
self.kick = function (forceX, forceY) {
self.velocityX += forceX;
self.velocityY += forceY;
LK.getSound('kick').play();
};
self.update = function () {
self.x += self.velocityX;
self.y += self.velocityY;
self.velocityX *= self.friction;
self.velocityY *= self.friction;
// Bounce off walls
if (self.x <= 20 || self.x >= 2028) {
self.velocityX *= -self.bounceStrength;
self.x = Math.max(20, Math.min(2028, self.x));
}
// Check goal areas
if (self.y <= 100 && self.x >= 874 && self.x <= 1174) {
// Top goal (opponent scores)
if (!self.goalScored) {
self.goalScored = true;
opponentScore++;
LK.getSound('goal').play();
LK.effects.flashScreen(0xff4500, 500);
resetBall();
}
} else if (self.y >= 2632 && self.x >= 874 && self.x <= 1174) {
// Bottom goal (player scores)
if (!self.goalScored) {
self.goalScored = true;
playerScore++;
LK.getSound('goal').play();
LK.effects.flashScreen(0x4169e1, 500);
resetBall();
}
} else {
self.goalScored = false;
}
// Bounce off top and bottom (outside goal areas)
if (self.y <= 20 && (self.x < 874 || self.x > 1174) || self.y >= 2712 && (self.x < 874 || self.x > 1174)) {
self.velocityY *= -self.bounceStrength;
self.y = Math.max(20, Math.min(2712, self.y));
}
};
return self;
});
var Lanzaguisantes1 = Container.expand(function () {
var self = Container.call(this);
var lanzaguisantesGraphics = self.attachAsset('Lanzaguisantes', {
anchorX: 0.5,
anchorY: 0.5
});
// Add blue border to indicate team 1
var blueBorder = self.addChild(LK.getAsset('player', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.8,
scaleY: 1.8,
alpha: 0.6
}));
self.speed = 2.5;
self.targetX = 1024;
self.targetY = 400;
self.aiTimer = 0;
self.abilityCooldown = 0;
self.hasBall = false;
self.isStunned = false;
self.stunDuration = 0;
self.passCooldown = 0;
self.shootPea = function () {
if (self.abilityCooldown <= 0 && self.hasBall) {
self.abilityCooldown = 120; // 2 seconds cooldown
LK.getSound('ability').play();
var newPea = new Pea();
newPea.x = self.x;
newPea.y = self.y;
// Shoot towards top goal
newPea.shoot(1024, 50);
peas.push(newPea);
game.addChild(newPea);
}
};
self.update = function () {
self.aiTimer++;
// Handle stun state
if (self.isStunned) {
self.stunDuration--;
if (self.stunDuration <= 0) {
self.isStunned = false;
self.tint = 0xFFFFFF; // Reset tint
}
return; // Skip movement and other actions while stunned
}
// Check if bot has the ball
var ballDistance = Math.sqrt(Math.pow(ball.x - self.x, 2) + Math.pow(ball.y - self.y, 2));
self.hasBall = ballDistance < 80;
// Improved AI with passing behavior
if (self.hasBall) {
// If has ball, decide whether to pass, shoot pea, or advance
var distanceToGoal = Math.sqrt(Math.pow(1024 - self.x, 2) + Math.pow(50 - self.y, 2));
var distanceToPlayer = Math.sqrt(Math.pow(player.x - self.x, 2) + Math.pow(player.y - self.y, 2));
// Use pea ability if has ball and ability is ready
if (self.abilityCooldown <= 0) {
self.shootPea();
}
// Pass to player if they're in better position
else if (self.passCooldown <= 0 && distanceToPlayer < 400 && player.y > self.y && !player.isStunned) {
var passForceX = (player.x - self.x) / distanceToPlayer * 8;
var passForceY = (player.y - self.y) / distanceToPlayer * 8;
ball.kick(passForceX, passForceY);
self.passCooldown = 60; // 1 second cooldown
}
// If close to goal, aim for goal
else if (distanceToGoal < 300) {
self.targetX = 1024;
self.targetY = 100;
}
// Otherwise advance towards goal with ball
else {
self.targetX = 1024;
self.targetY = Math.max(self.y - 100, 100);
}
} else {
// Without ball, position strategically
var opponentDistance = Math.sqrt(Math.pow(opponent.x - self.x, 2) + Math.pow(opponent.y - self.y, 2));
var lanzaguisantes2Distance = Math.sqrt(Math.pow(lanzaguisantes2.x - self.x, 2) + Math.pow(lanzaguisantes2.y - self.y, 2));
var opponentHasBall = Math.sqrt(Math.pow(ball.x - opponent.x, 2) + Math.pow(ball.y - opponent.y, 2)) < 80;
var lanzaguisantes2HasBall = Math.sqrt(Math.pow(ball.x - lanzaguisantes2.x, 2) + Math.pow(ball.y - lanzaguisantes2.y, 2)) < 80;
// If opponents have ball and are close, try to steal it
if (opponentHasBall && opponentDistance < 200 || lanzaguisantes2HasBall && lanzaguisantes2Distance < 200) {
if (opponentHasBall && opponentDistance < lanzaguisantes2Distance) {
self.targetX = opponent.x;
self.targetY = opponent.y;
} else {
self.targetX = lanzaguisantes2.x;
self.targetY = lanzaguisantes2.y;
}
} else {
// Ball anywhere on field - be more aggressive
if (player.hasBall) {
// Position for receive pass
self.targetX = player.x - 200;
self.targetY = Math.max(player.y - 150, 200);
} else {
// Always move towards ball regardless of position
self.targetX = ball.x;
self.targetY = ball.y - 80;
}
}
}
// Move towards target
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 5) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
// Keep lanzaguisantes in bounds - allow movement across entire field
self.y = Math.max(50, Math.min(2682, self.y));
self.x = Math.max(50, Math.min(1998, self.x));
// Update pass cooldown
if (self.passCooldown > 0) {
self.passCooldown--;
}
// Check ball collision
if (ballDistance < 60) {
var distanceToGoal = Math.sqrt(Math.pow(1024 - self.x, 2) + Math.pow(50 - self.y, 2));
// If close to goal, always shoot towards goal with power
if (distanceToGoal < 500) {
var kickForceX = (1024 - self.x) / distanceToGoal * 15;
var kickForceY = (50 - self.y) / distanceToGoal * 15;
} else if (self.hasBall) {
// Normal ball control when has ball
var kickForceX = (ball.x - self.x) / ballDistance * 5;
var kickForceY = (ball.y - self.y) / ballDistance * 5;
} else {
// Steal ball from opponents - kick towards own goal area
var kickForceX = (1024 - ball.x) * 0.1;
var kickForceY = (2300 - ball.y) * 0.1;
}
ball.kick(kickForceX, kickForceY);
}
// Update cooldown
if (self.abilityCooldown > 0) {
self.abilityCooldown--;
}
};
return self;
});
var Lanzaguisantes2 = Container.expand(function () {
var self = Container.call(this);
var lanzaguisantesGraphics = self.attachAsset('Lanzaguisantes', {
anchorX: 0.5,
anchorY: 0.5
});
// Add red border to indicate team 2
var redBorder = self.addChild(LK.getAsset('opponent', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.8,
scaleY: 1.8,
alpha: 0.6
}));
self.speed = 2.5;
self.targetX = 1024;
self.targetY = 2300;
self.aiTimer = 0;
self.abilityCooldown = 0;
self.hasBall = false;
self.isStunned = false;
self.stunDuration = 0;
self.passCooldown = 0;
self.shootPea = function () {
if (self.abilityCooldown <= 0 && self.hasBall) {
self.abilityCooldown = 120; // 2 seconds cooldown
LK.getSound('ability').play();
var newPea = new Pea();
newPea.x = self.x;
newPea.y = self.y;
// Shoot towards bottom goal
newPea.shoot(1024, 2682);
peas.push(newPea);
game.addChild(newPea);
}
};
self.update = function () {
self.aiTimer++;
// Handle stun state
if (self.isStunned) {
self.stunDuration--;
if (self.stunDuration <= 0) {
self.isStunned = false;
self.tint = 0xFFFFFF; // Reset tint
}
return; // Skip movement and other actions while stunned
}
// Check if bot has the ball
var ballDistance = Math.sqrt(Math.pow(ball.x - self.x, 2) + Math.pow(ball.y - self.y, 2));
self.hasBall = ballDistance < 80;
// Improved AI with passing behavior
if (self.hasBall) {
// If has ball, decide whether to pass, shoot pea, or advance
var distanceToGoal = Math.sqrt(Math.pow(1024 - self.x, 2) + Math.pow(2682 - self.y, 2));
var distanceToOpponent = Math.sqrt(Math.pow(opponent.x - self.x, 2) + Math.pow(opponent.y - self.y, 2));
// Use pea ability if has ball and ability is ready
if (self.abilityCooldown <= 0) {
self.shootPea();
}
// Pass to opponent if they're in better position
else if (self.passCooldown <= 0 && distanceToOpponent < 400 && opponent.y < self.y && !opponent.isStunned) {
var passForceX = (opponent.x - self.x) / distanceToOpponent * 8;
var passForceY = (opponent.y - self.y) / distanceToOpponent * 8;
ball.kick(passForceX, passForceY);
self.passCooldown = 60; // 1 second cooldown
}
// If close to goal, aim for goal
else if (distanceToGoal < 300) {
self.targetX = 1024;
self.targetY = 2600;
}
// Otherwise advance towards goal with ball
else {
self.targetX = 1024;
self.targetY = Math.min(self.y + 100, 2600);
}
} else {
// Without ball, position strategically
var playerDistance = Math.sqrt(Math.pow(player.x - self.x, 2) + Math.pow(player.y - self.y, 2));
var lanzaguisantes1Distance = Math.sqrt(Math.pow(lanzaguisantes1.x - self.x, 2) + Math.pow(lanzaguisantes1.y - self.y, 2));
var playerHasBall = Math.sqrt(Math.pow(ball.x - player.x, 2) + Math.pow(ball.y - player.y, 2)) < 80;
var lanzaguisantes1HasBall = Math.sqrt(Math.pow(ball.x - lanzaguisantes1.x, 2) + Math.pow(ball.y - lanzaguisantes1.y, 2)) < 80;
// If opponents have ball and are close, try to steal it
if (playerHasBall && playerDistance < 200 || lanzaguisantes1HasBall && lanzaguisantes1Distance < 200) {
if (playerHasBall && playerDistance < lanzaguisantes1Distance) {
self.targetX = player.x;
self.targetY = player.y;
} else {
self.targetX = lanzaguisantes1.x;
self.targetY = lanzaguisantes1.y;
}
} else {
// Ball anywhere on field - be more aggressive
if (opponent.hasBall) {
// Position for receive pass
self.targetX = opponent.x + 200;
self.targetY = Math.max(opponent.y - 150, 200);
} else {
// Always move towards ball regardless of position
self.targetX = ball.x;
self.targetY = ball.y + 80;
}
}
}
// Move towards target
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 5) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
// Keep lanzaguisantes in bounds - allow movement across entire field
self.y = Math.max(50, Math.min(2682, self.y));
self.x = Math.max(50, Math.min(1998, self.x));
// Update pass cooldown
if (self.passCooldown > 0) {
self.passCooldown--;
}
// Check ball collision
if (ballDistance < 60) {
var distanceToGoal = Math.sqrt(Math.pow(1024 - self.x, 2) + Math.pow(2682 - self.y, 2));
// If close to goal, always shoot towards goal with power
if (distanceToGoal < 500) {
var kickForceX = (1024 - self.x) / distanceToGoal * 15;
var kickForceY = (2682 - self.y) / distanceToGoal * 15;
} else if (self.hasBall) {
// Normal ball control when has ball
var kickForceX = (ball.x - self.x) / ballDistance * 5;
var kickForceY = (ball.y - self.y) / ballDistance * 5;
} else {
// Steal ball from opponents - kick towards own goal area
var kickForceX = (1024 - ball.x) * 0.1;
var kickForceY = (400 - ball.y) * 0.1;
}
ball.kick(kickForceX, kickForceY);
}
// Update cooldown
if (self.abilityCooldown > 0) {
self.abilityCooldown--;
}
};
return self;
});
var Opponent = Container.expand(function () {
var self = Container.call(this);
var opponentGraphics = self.attachAsset('opponent', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 3;
self.targetX = 1024;
self.targetY = 500;
self.aiTimer = 0;
self.isStunned = false;
self.stunDuration = 0;
self.hasBall = false;
self.passCooldown = 0;
self.update = function () {
self.aiTimer++;
// Handle stun state
if (self.isStunned) {
self.stunDuration--;
if (self.stunDuration <= 0) {
self.isStunned = false;
self.tint = 0xFFFFFF; // Reset tint
}
return; // Skip movement and other actions while stunned
}
// Check if bot has the ball
var ballDistance = Math.sqrt(Math.pow(ball.x - self.x, 2) + Math.pow(ball.y - self.y, 2));
self.hasBall = ballDistance < 80;
// Improved AI with passing behavior
if (self.hasBall) {
// If has ball, decide whether to pass or advance
var distanceToGoal = Math.sqrt(Math.pow(1024 - self.x, 2) + Math.pow(2682 - self.y, 2));
var distanceToLanzaguisantes2 = Math.sqrt(Math.pow(lanzaguisantes2.x - self.x, 2) + Math.pow(lanzaguisantes2.y - self.y, 2));
// Pass to lanzaguisantes2 if they're in better position and pass is available
if (self.passCooldown <= 0 && distanceToLanzaguisantes2 < 400 && lanzaguisantes2.y < self.y && !lanzaguisantes2.isStunned) {
// Pass the ball to lanzaguisantes2
var passForceX = (lanzaguisantes2.x - self.x) / distanceToLanzaguisantes2 * 8;
var passForceY = (lanzaguisantes2.y - self.y) / distanceToLanzaguisantes2 * 8;
ball.kick(passForceX, passForceY);
self.passCooldown = 60; // 1 second cooldown
} else if (distanceToGoal < 300) {
// Close to goal, aim for goal
self.targetX = 1024;
self.targetY = 2600;
} else {
// Advance towards goal with ball
self.targetX = 1024;
self.targetY = Math.min(self.y + 100, 2600);
}
} else {
// Without ball, position strategically
var playerDistance = Math.sqrt(Math.pow(player.x - self.x, 2) + Math.pow(player.y - self.y, 2));
var lanzaguisantes1Distance = Math.sqrt(Math.pow(lanzaguisantes1.x - self.x, 2) + Math.pow(lanzaguisantes1.y - self.y, 2));
var playerHasBall = Math.sqrt(Math.pow(ball.x - player.x, 2) + Math.pow(ball.y - player.y, 2)) < 80;
var lanzaguisantes1HasBall = Math.sqrt(Math.pow(ball.x - lanzaguisantes1.x, 2) + Math.pow(ball.y - lanzaguisantes1.y, 2)) < 80;
// If opponents have ball and are close, try to steal it
if (playerHasBall && playerDistance < 200 || lanzaguisantes1HasBall && lanzaguisantes1Distance < 200) {
if (playerHasBall && playerDistance < lanzaguisantes1Distance) {
self.targetX = player.x;
self.targetY = player.y;
} else {
self.targetX = lanzaguisantes1.x;
self.targetY = lanzaguisantes1.y;
}
} else {
// Ball anywhere on field - be more aggressive
if (lanzaguisantes2.hasBall) {
// Position for receive pass
self.targetX = lanzaguisantes2.x + 200;
self.targetY = Math.max(lanzaguisantes2.y - 150, 200);
} else {
// Always move towards ball regardless of position
self.targetX = ball.x;
self.targetY = ball.y - 100;
}
}
}
// Move towards target
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 5) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
// Keep opponent in bounds - allow movement across entire field
self.y = Math.max(50, Math.min(2682, self.y));
self.x = Math.max(50, Math.min(1998, self.x));
// Update pass cooldown
if (self.passCooldown > 0) {
self.passCooldown--;
}
// Check collision with lanzaguisantes2
var lanzaguisantes2Distance = Math.sqrt(Math.pow(lanzaguisantes2.x - self.x, 2) + Math.pow(lanzaguisantes2.y - self.y, 2));
if (lanzaguisantes2Distance < 70) {
// Calculate bounce direction
var bounceX = (self.x - lanzaguisantes2.x) / lanzaguisantes2Distance;
var bounceY = (self.y - lanzaguisantes2.y) / lanzaguisantes2Distance;
// Apply bounce force
self.x += bounceX * 20;
self.y += bounceY * 20;
lanzaguisantes2.x -= bounceX * 20;
lanzaguisantes2.y -= bounceY * 20;
// Keep characters in bounds
self.x = Math.max(40, Math.min(2008, self.x));
self.y = Math.max(40, Math.min(2692, self.y));
lanzaguisantes2.x = Math.max(40, Math.min(2008, lanzaguisantes2.x));
lanzaguisantes2.y = Math.max(40, Math.min(2692, lanzaguisantes2.y));
}
// Check ball collision
if (ballDistance < 60) {
var distanceToGoal = Math.sqrt(Math.pow(1024 - self.x, 2) + Math.pow(2682 - self.y, 2));
// If close to goal, always shoot towards goal with power
if (distanceToGoal < 500) {
var kickForceX = (1024 - self.x) / distanceToGoal * 15;
var kickForceY = (2682 - self.y) / distanceToGoal * 15;
} else if (self.hasBall) {
// Normal ball control when has ball
var kickForceX = (ball.x - self.x) / ballDistance * 6;
var kickForceY = (ball.y - self.y) / ballDistance * 6;
} else {
// Steal ball from player - kick towards own goal area
var kickForceX = (1024 - ball.x) * 0.1;
var kickForceY = (400 - ball.y) * 0.1;
}
ball.kick(kickForceX, kickForceY);
}
};
return self;
});
var Pea = Container.expand(function () {
var self = Container.call(this);
var peaGraphics = self.attachAsset('ball', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 0.6
});
peaGraphics.tint = 0x90EE90;
self.velocityX = 0;
self.velocityY = 0;
self.speed = 12;
self.shoot = function (targetX, targetY) {
var dx = targetX - self.x;
var dy = targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
self.velocityX = dx / distance * self.speed;
self.velocityY = dy / distance * self.speed;
}
};
self.update = function () {
self.x += self.velocityX;
self.y += self.velocityY;
// Remove pea if it goes off screen
if (self.x < 0 || self.x > 2048 || self.y < 0 || self.y > 2732) {
self.destroy();
for (var i = peas.length - 1; i >= 0; i--) {
if (peas[i] === self) {
peas.splice(i, 1);
break;
}
}
}
// Check collision with player
var playerDistance = Math.sqrt(Math.pow(player.x - self.x, 2) + Math.pow(player.y - self.y, 2));
if (playerDistance < 50) {
// Stun player for 3 seconds
player.isStunned = true;
player.stunDuration = 180; // 3 seconds at 60fps
tween(player, {
tint: 0xFF0000
}, {
duration: 500,
onFinish: function onFinish() {
tween(player, {
tint: 0xFFFFFF
}, {
duration: 500
});
}
});
self.destroy();
for (var i = peas.length - 1; i >= 0; i--) {
if (peas[i] === self) {
peas.splice(i, 1);
break;
}
}
return;
}
// Check collision with opponent
var opponentDistance = Math.sqrt(Math.pow(opponent.x - self.x, 2) + Math.pow(opponent.y - self.y, 2));
if (opponentDistance < 50) {
// Stun opponent for 3 seconds
opponent.isStunned = true;
opponent.stunDuration = 180; // 3 seconds at 60fps
tween(opponent, {
tint: 0xFF0000
}, {
duration: 500,
onFinish: function onFinish() {
tween(opponent, {
tint: 0xFFFFFF
}, {
duration: 500
});
}
});
self.destroy();
for (var i = peas.length - 1; i >= 0; i--) {
if (peas[i] === self) {
peas.splice(i, 1);
break;
}
}
return;
}
// Check collision with lanzaguisantes1
var lanzaguisantes1Distance = Math.sqrt(Math.pow(lanzaguisantes1.x - self.x, 2) + Math.pow(lanzaguisantes1.y - self.y, 2));
if (lanzaguisantes1Distance < 50) {
// Stun lanzaguisantes1 for 3 seconds
lanzaguisantes1.isStunned = true;
lanzaguisantes1.stunDuration = 180; // 3 seconds at 60fps
tween(lanzaguisantes1, {
tint: 0xFF0000
}, {
duration: 500,
onFinish: function onFinish() {
tween(lanzaguisantes1, {
tint: 0xFFFFFF
}, {
duration: 500
});
}
});
self.destroy();
for (var i = peas.length - 1; i >= 0; i--) {
if (peas[i] === self) {
peas.splice(i, 1);
break;
}
}
return;
}
// Check collision with lanzaguisantes2
var lanzaguisantes2Distance = Math.sqrt(Math.pow(lanzaguisantes2.x - self.x, 2) + Math.pow(lanzaguisantes2.y - self.y, 2));
if (lanzaguisantes2Distance < 50) {
// Stun lanzaguisantes2 for 3 seconds
lanzaguisantes2.isStunned = true;
lanzaguisantes2.stunDuration = 180; // 3 seconds at 60fps
tween(lanzaguisantes2, {
tint: 0xFF0000
}, {
duration: 500,
onFinish: function onFinish() {
tween(lanzaguisantes2, {
tint: 0xFFFFFF
}, {
duration: 500
});
}
});
self.destroy();
for (var i = peas.length - 1; i >= 0; i--) {
if (peas[i] === self) {
peas.splice(i, 1);
break;
}
}
return;
}
// Check collision with ball
var ballDistance = Math.sqrt(Math.pow(ball.x - self.x, 2) + Math.pow(ball.y - self.y, 2));
if (ballDistance < 40) {
var kickForceX = self.velocityX * 0.8;
var kickForceY = self.velocityY * 0.8;
ball.kick(kickForceX, kickForceY);
self.destroy();
for (var i = peas.length - 1; i >= 0; i--) {
if (peas[i] === self) {
peas.splice(i, 1);
break;
}
}
}
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 4;
self.targetX = 1024;
self.targetY = 2200;
self.abilityType = 'speed'; // 'speed', 'power', 'shield'
self.abilityCooldown = 0;
self.abilityDuration = 0;
self.abilityEffect = null;
self.isStunned = false;
self.stunDuration = 0;
self.activateAbility = function () {
if (self.abilityCooldown <= 0) {
self.abilityCooldown = 180; // 3 seconds at 60fps
self.abilityDuration = 120; // 2 seconds effect
LK.getSound('ability').play();
if (self.abilityType === 'speed') {
self.abilityEffect = self.addChild(LK.getAsset('speedBoostEffect', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.7
}));
self.speed = 8;
}
}
};
self.update = function () {
// Handle stun state first - prevents all actions while stunned
if (self.isStunned) {
self.stunDuration--;
if (self.stunDuration <= 0) {
self.isStunned = false;
self.tint = 0xFFFFFF; // Reset tint
}
return; // Skip movement and other actions while stunned
}
// Move towards target
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 5) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
// Update ability
if (self.abilityDuration > 0) {
self.abilityDuration--;
if (self.abilityEffect) {
self.abilityEffect.rotation += 0.1;
}
} else {
if (self.abilityEffect) {
self.removeChild(self.abilityEffect);
self.abilityEffect = null;
}
self.speed = 4;
}
if (self.abilityCooldown > 0) {
self.abilityCooldown--;
}
// Check collision with other characters
var opponentDistance = Math.sqrt(Math.pow(opponent.x - self.x, 2) + Math.pow(opponent.y - self.y, 2));
if (opponentDistance < 70) {
// Calculate bounce direction
var bounceX = (self.x - opponent.x) / opponentDistance;
var bounceY = (self.y - opponent.y) / opponentDistance;
// Apply bounce force
self.x += bounceX * 20;
self.y += bounceY * 20;
opponent.x -= bounceX * 20;
opponent.y -= bounceY * 20;
// Keep characters in bounds
self.x = Math.max(40, Math.min(2008, self.x));
self.y = Math.max(40, Math.min(2692, self.y));
opponent.x = Math.max(50, Math.min(1998, opponent.x));
opponent.y = Math.max(50, Math.min(2682, opponent.y));
}
var lanzaguisantes1Distance = Math.sqrt(Math.pow(lanzaguisantes1.x - self.x, 2) + Math.pow(lanzaguisantes1.y - self.y, 2));
if (lanzaguisantes1Distance < 70) {
// Calculate bounce direction
var bounceX = (self.x - lanzaguisantes1.x) / lanzaguisantes1Distance;
var bounceY = (self.y - lanzaguisantes1.y) / lanzaguisantes1Distance;
// Apply bounce force
self.x += bounceX * 20;
self.y += bounceY * 20;
lanzaguisantes1.x -= bounceX * 20;
lanzaguisantes1.y -= bounceY * 20;
// Keep characters in bounds
self.x = Math.max(40, Math.min(2008, self.x));
self.y = Math.max(40, Math.min(2692, self.y));
lanzaguisantes1.x = Math.max(40, Math.min(2008, lanzaguisantes1.x));
lanzaguisantes1.y = Math.max(40, Math.min(2692, lanzaguisantes1.y));
}
var lanzaguisantes2Distance = Math.sqrt(Math.pow(lanzaguisantes2.x - self.x, 2) + Math.pow(lanzaguisantes2.y - self.y, 2));
if (lanzaguisantes2Distance < 70) {
// Calculate bounce direction
var bounceX = (self.x - lanzaguisantes2.x) / lanzaguisantes2Distance;
var bounceY = (self.y - lanzaguisantes2.y) / lanzaguisantes2Distance;
// Apply bounce force
self.x += bounceX * 20;
self.y += bounceY * 20;
lanzaguisantes2.x -= bounceX * 20;
lanzaguisantes2.y -= bounceY * 20;
// Keep characters in bounds
self.x = Math.max(40, Math.min(2008, self.x));
self.y = Math.max(40, Math.min(2692, self.y));
lanzaguisantes2.x = Math.max(40, Math.min(2008, lanzaguisantes2.x));
lanzaguisantes2.y = Math.max(40, Math.min(2692, lanzaguisantes2.y));
}
// Check ball collision
var ballDistance = Math.sqrt(Math.pow(ball.x - self.x, 2) + Math.pow(ball.y - self.y, 2));
if (ballDistance < 50) {
var kickForceX = (ball.x - self.x) / ballDistance * 8;
var kickForceY = (ball.y - self.y) / ballDistance * 8;
if (self.abilityType === 'power' && self.abilityDuration > 0) {
kickForceX *= 2;
kickForceY *= 2;
}
ball.kick(kickForceX, kickForceY);
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x228b22
});
/****
* Game Code
****/
// Game variables
var playerScore = 0;
var opponentScore = 0;
var targetScore = 3;
var ball;
var player;
var opponent;
var lanzaguisantes1;
var lanzaguisantes2;
var peas = [];
// Create field elements
var field = game.addChild(LK.getAsset('field', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
}));
var centerLine = game.addChild(LK.getAsset('centerLine', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366
}));
var centerCircle = game.addChild(LK.getAsset('centerCircle', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366,
alpha: 0.3
}));
// Create walls
var leftWall = game.addChild(LK.getAsset('wall', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
}));
var rightWall = game.addChild(LK.getAsset('wall', {
anchorX: 0,
anchorY: 0,
x: 2038,
y: 0
}));
var topWall = game.addChild(LK.getAsset('wall', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
width: 2048,
height: 10
}));
var bottomWall = game.addChild(LK.getAsset('wall', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 2722,
width: 2048,
height: 10
}));
// Create goals
var topGoal = game.addChild(LK.getAsset('topGoal', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 50
}));
var bottomGoal = game.addChild(LK.getAsset('bottomGoal', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 2682
}));
// Create score display
var scoreText = new Text2('Player: 0 - Opponent: 0', {
size: 60,
fill: 0xFFFFFF
});
scoreText.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreText);
// Create ability button
var abilityButton = new Text2('SPEED BOOST', {
size: 40,
fill: 0xFFFF00
});
abilityButton.anchor.set(0.5, 1);
LK.gui.bottom.addChild(abilityButton);
// Create game objects
ball = game.addChild(new Ball());
player = game.addChild(new Player());
opponent = game.addChild(new Opponent());
lanzaguisantes1 = game.addChild(new Lanzaguisantes1());
lanzaguisantes2 = game.addChild(new Lanzaguisantes2());
function resetBall() {
LK.setTimeout(function () {
ball.x = 1024;
ball.y = 1366;
ball.velocityX = 0;
ball.velocityY = 0;
ball.goalScored = false;
// Update score display
scoreText.setText('Player: ' + playerScore + ' - Opponent: ' + opponentScore);
// Check win condition
if (playerScore >= targetScore) {
LK.showYouWin();
} else if (opponentScore >= targetScore) {
LK.showGameOver();
}
}, 1000);
}
// Initialize positions
resetBall();
player.x = 1024;
player.y = 2200;
opponent.x = 1024;
opponent.y = 500;
lanzaguisantes1.x = 800;
lanzaguisantes1.y = 2000;
lanzaguisantes2.x = 1200;
lanzaguisantes2.y = 700;
// Touch controls
game.down = function (x, y, obj) {
player.targetX = x;
player.targetY = y;
};
game.move = function (x, y, obj) {
player.targetX = x;
player.targetY = y;
};
// Ability button handler
abilityButton.down = function (x, y, obj) {
player.activateAbility();
};
game.update = function () {
// Update ability button text
if (player.abilityCooldown > 0) {
abilityButton.setText('COOLDOWN: ' + Math.ceil(player.abilityCooldown / 60));
abilityButton.tint = 0x666666;
} else {
abilityButton.setText('SPEED BOOST');
abilityButton.tint = 0xffff00;
}
};