/****
* 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.speed = 8;
self.maxSpeed = 15;
self.lastX = 0;
self.lastY = 0;
self.update = function () {
// Store last position for collision detection
self.lastX = self.x;
self.lastY = self.y;
self.x += self.velocityX;
self.y += self.velocityY;
// Bounce off left and right walls
if (self.x <= 40 || self.x >= 2048 - 40) {
self.velocityX = -self.velocityX;
self.x = self.x <= 40 ? 40 : 2048 - 40;
}
// Bounce off top and bottom walls (but not in goal areas)
if (self.y <= 40 && (self.x < 824 || self.x > 1224) || self.y >= 2732 - 40 && (self.x < 824 || self.x > 1224)) {
self.velocityY = -self.velocityY;
self.y = self.y <= 40 ? 40 : 2732 - 40;
}
};
self.reset = function () {
self.x = 1024;
self.y = 1366;
self.velocityX = 0;
self.velocityY = 0;
};
return self;
});
var MenuButton = Container.expand(function (text, onClick) {
var self = Container.call(this);
var buttonGraphics = self.attachAsset('menuButton', {
anchorX: 0.5,
anchorY: 0.5
});
var buttonText = new Text2(text, {
size: 60,
fill: 0xFFFFFF
});
buttonText.anchor.set(0.5, 0.5);
self.addChild(buttonText);
self.onClick = onClick;
self.down = function (x, y, obj) {
if (self.onClick) {
self.onClick();
}
};
return self;
});
var Paddle = Container.expand(function (isPlayer) {
var self = Container.call(this);
var paddleGraphics = self.attachAsset(isPlayer ? 'playerPaddle' : 'aiPaddle', {
anchorX: 0.5,
anchorY: 0.5
});
self.isPlayer = isPlayer;
self.lastX = 0;
self.lastY = 0;
self.update = function () {
self.lastX = self.x;
self.lastY = self.y;
// Keep paddle in bounds
if (self.x < 80) self.x = 80;
if (self.x > 2048 - 80) self.x = 2048 - 80;
if (self.isPlayer) {
// Player paddle stays in bottom half
if (self.y < 1366 + 100) self.y = 1366 + 100;
if (self.y > 2732 - 80) self.y = 2732 - 80;
} else {
// AI paddle can move in top 2/3 of field for better positioning
if (self.y < 80) self.y = 80;
if (self.y > 1800) self.y = 1800; // Allow more forward movement
}
};
return self;
});
/****
* Initialize Game
****/
// Game variables
var game = new LK.Game({
backgroundColor: 0x228B22
});
/****
* Game Code
****/
var gameMode = '1vs1'; // '1vs1' or '2vs2'
var gameState = 'menu'; // 'menu', 'playing'
// Game variables
var level = 1;
var playerScore = 0;
var aiScore = 0;
var goalsToWin = 3;
// Game objects (declared globally but created in game modes)
var ball;
var playerPaddle;
var aiPaddle;
var aiTeammate1; // For 2vs2 mode
var aiTeammate2; // For 2vs2 mode
var fieldElements = [];
var menuButtons = [];
var dragPaddle = null;
// UI elements
var levelText;
var scoreText;
var menuTitle;
// Show main menu
function showMainMenu() {
// Clear any existing field elements
clearField();
// Create game preview image at top
var gamePreview = game.addChild(LK.getAsset('gamePreview', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 600
}));
// Add mini soccer field elements to preview
var previewBall = game.addChild(LK.getAsset('soccerBall', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 600,
scaleX: 0.8,
scaleY: 0.8
}));
var previewPlayer = game.addChild(LK.getAsset('playerPaddle', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 720,
scaleX: 0.6,
scaleY: 0.6
}));
var previewAI = game.addChild(LK.getAsset('aiPaddle', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 480,
scaleX: 0.6,
scaleY: 0.6
}));
// Create menu title
menuTitle = new Text2('SOCCER GAME', {
size: 120,
fill: 0xFFFFFF
});
menuTitle.anchor.set(0.5, 0.5);
menuTitle.x = 1024;
menuTitle.y = 300;
game.addChild(menuTitle);
// Create 1vs1 button - larger and positioned lower
var button1vs1 = game.addChild(new MenuButton('1 vs 1', function () {
startGame('1vs1');
}));
button1vs1.x = 1024;
button1vs1.y = 1800;
menuButtons.push(button1vs1);
// Create 2vs2 button - larger and positioned lower
var button2vs2 = game.addChild(new MenuButton('2 vs 2', function () {
startGame('2vs2');
}));
button2vs2.x = 1024;
button2vs2.y = 2000;
menuButtons.push(button2vs2);
gameState = 'menu';
}
function clearField() {
// Remove all field elements
for (var i = fieldElements.length - 1; i >= 0; i--) {
fieldElements[i].destroy();
}
fieldElements = [];
// Remove menu buttons
for (var i = menuButtons.length - 1; i >= 0; i--) {
menuButtons[i].destroy();
}
menuButtons = [];
// Remove menu title
if (menuTitle) {
menuTitle.destroy();
menuTitle = null;
}
}
function createField() {
// Create checkered grass field background covering entire field
for (var row = 0; row < 43; row++) {
for (var col = 0; col < 32; col++) {
var isLight = (row + col) % 2 === 0;
var grassSquare = game.addChild(LK.getAsset(isLight ? 'grassSquareLight' : 'grassSquareDark', {
anchorX: 0,
anchorY: 0,
x: col * 64,
y: row * 64,
scaleX: 1.0,
scaleY: 1.0
}));
fieldElements.push(grassSquare);
}
}
// Create center circle line using multiple small white boxes in a circle (soccer field style)
var centerCircleRadius = 150;
var circleSegments = 60;
for (var i = 0; i < circleSegments; i++) {
var angle = i / circleSegments * Math.PI * 2;
var lineX = 1024 + Math.cos(angle) * centerCircleRadius;
var lineY = 1366 + Math.sin(angle) * centerCircleRadius;
var circleSegment = game.addChild(LK.getAsset('fieldLine', {
anchorX: 0.5,
anchorY: 0.5,
x: lineX,
y: lineY,
scaleX: 0.01,
scaleY: 0.8,
rotation: angle + Math.PI / 2
}));
fieldElements.push(circleSegment);
}
// Create center dot
var centerDot = game.addChild(LK.getAsset('ball', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366,
scaleX: 0.2,
scaleY: 0.2
}));
fieldElements.push(centerDot);
// Create field elements
var centerLine = game.addChild(LK.getAsset('fieldLine', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366
}));
fieldElements.push(centerLine);
// Create top goal (AI goal)
var topGoalPost = game.addChild(LK.getAsset('goalPost', {
anchorX: 0.5,
anchorY: 1.0,
x: 1024,
y: 80
}));
fieldElements.push(topGoalPost);
// Create top goal net with grid pattern - more visible
for (var i = 0; i < 9; i++) {
var netLineVertical = game.addChild(LK.getAsset('fieldLine', {
anchorX: 0.5,
anchorY: 0.0,
x: 824 + i * 44,
y: 10,
scaleX: 0.15,
scaleY: 20
}));
fieldElements.push(netLineVertical);
}
for (var j = 0; j < 5; j++) {
var netLineHorizontal = game.addChild(LK.getAsset('fieldLine', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 10 + j * 18,
scaleX: 0.25,
scaleY: 4
}));
fieldElements.push(netLineHorizontal);
}
// Create bottom goal (Player goal)
var bottomGoalPost = game.addChild(LK.getAsset('goalPost', {
anchorX: 0.5,
anchorY: 0.0,
x: 1024,
y: 2652
}));
fieldElements.push(bottomGoalPost);
// Create bottom goal net with grid pattern - more visible
for (var i = 0; i < 9; i++) {
var netLineVertical = game.addChild(LK.getAsset('fieldLine', {
anchorX: 0.5,
anchorY: 1.0,
x: 824 + i * 44,
y: 2722,
scaleX: 0.15,
scaleY: 20
}));
fieldElements.push(netLineVertical);
}
for (var j = 0; j < 5; j++) {
var netLineHorizontal = game.addChild(LK.getAsset('fieldLine', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 2722 - j * 18,
scaleX: 0.25,
scaleY: 4
}));
fieldElements.push(netLineHorizontal);
}
}
function startGame(mode) {
gameMode = mode;
clearField();
createField();
// Create game objects
ball = game.addChild(new Ball());
playerPaddle = game.addChild(new Paddle(true));
aiPaddle = game.addChild(new Paddle(false));
if (gameMode === '2vs2') {
// Create AI teammates - one red opponent and one green teammate
aiTeammate1 = game.addChild(new Paddle(false)); // Red opponent 1
aiTeammate2 = game.addChild(new Paddle(true)); // Green teammate (player's ally)
// Position teammates
aiTeammate1.x = 700; // Left side AI opponent
aiTeammate1.y = 800;
aiTeammate2.x = 1348; // Right side AI opponent
aiTeammate2.y = 800;
}
// Reset game state
level = 1;
playerScore = 0;
aiScore = 0;
// Position initial objects
ball.reset();
playerPaddle.x = 1024;
playerPaddle.y = 2200;
aiPaddle.x = 1024;
aiPaddle.y = 532;
// Create UI
levelText = new Text2('Level ' + level + ' (' + gameMode + ')', {
size: 80,
fill: 0xFFFFFF
});
levelText.anchor.set(0.5, 0);
LK.gui.top.addChild(levelText);
scoreText = new Text2('You: ' + playerScore + ' - AI: ' + aiScore, {
size: 60,
fill: 0xFFFFFF
});
scoreText.anchor.set(0.5, 0);
scoreText.y = 100;
LK.gui.top.addChild(scoreText);
gameState = 'playing';
}
// Touch controls
game.down = function (x, y, obj) {
if (gameState !== 'playing') return;
var distance = Math.sqrt(Math.pow(x - playerPaddle.x, 2) + Math.pow(y - playerPaddle.y, 2));
if (distance < 120) {
dragPaddle = playerPaddle;
}
};
game.move = function (x, y, obj) {
if (dragPaddle && gameState === 'playing') {
dragPaddle.x = x;
dragPaddle.y = y;
}
};
game.up = function (x, y, obj) {
dragPaddle = null;
};
function updateAI(paddle, isTeammate, position) {
var aiSpeed = 3 + (level - 1) * 0.5;
var ballTargetX = ball.x + ball.velocityX * 10;
var ballTargetY = ball.y + ball.velocityY * 5;
// Horizontal movement
if (paddle.x < ballTargetX - 20) {
paddle.x += aiSpeed;
} else if (paddle.x > ballTargetX + 20) {
paddle.x -= aiSpeed;
}
// Vertical movement based on position
if (position === 'defender') {
// Defensive AI behavior
if (ball.velocityY < 0 && ball.y < 1000) {
if (paddle.y > ballTargetY - 50 && paddle.y > 200) {
paddle.y -= aiSpeed * 0.7;
}
} else {
if (paddle.y < 400) {
paddle.y += aiSpeed * 0.5;
}
}
} else if (position === 'midfielder') {
// Midfielder AI behavior
if (paddle.y < ballTargetY - 30) {
paddle.y += aiSpeed * 0.6;
} else if (paddle.y > ballTargetY + 30) {
paddle.y -= aiSpeed * 0.6;
}
// Keep in middle area
if (paddle.y < 600) paddle.y = 600;
if (paddle.y > 1400) paddle.y = 1400;
} else if (position === 'support') {
// Support player AI behavior
if (paddle.y > ballTargetY + 30) {
paddle.y -= aiSpeed * 0.6;
} else if (paddle.y < ballTargetY - 30) {
paddle.y += aiSpeed * 0.6;
}
// Keep in player area
if (paddle.y < 1500) paddle.y = 1500;
if (paddle.y > 2500) paddle.y = 2500;
}
}
function handleBallCollision(paddle) {
var ballPaddleDistance = Math.sqrt(Math.pow(ball.x - paddle.x, 2) + Math.pow(ball.y - paddle.y, 2));
var lastBallPaddleDistance = Math.sqrt(Math.pow(ball.lastX - paddle.lastX, 2) + Math.pow(ball.lastY - paddle.lastY, 2));
var collisionRadius = 120;
if (ballPaddleDistance <= collisionRadius && lastBallPaddleDistance > collisionRadius || ball.intersects(paddle)) {
var dx = ball.x - paddle.x;
var dy = ball.y - paddle.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
var pushDistance = collisionRadius + 10;
var newBallX = paddle.x + dx / distance * pushDistance;
var newBallY = paddle.y + dy / distance * pushDistance;
newBallX = Math.max(40, Math.min(2048 - 40, newBallX));
newBallY = Math.max(40, Math.min(2732 - 40, newBallY));
ball.x = newBallX;
ball.y = newBallY;
var minVelocity = 3;
// Check if this is an AI paddle (not player)
if (!paddle.isPlayer) {
// AI always hits ball towards player goal (bottom of screen)
ball.velocityX = dx / distance * ball.speed;
ball.velocityY = Math.abs(dy / distance * ball.speed); // Always positive Y (towards bottom)
} else {
// Player paddle uses normal physics
ball.velocityX = Math.max(Math.abs(dx / distance * ball.speed), minVelocity) * (dx > 0 ? 1 : -1);
ball.velocityY = Math.max(Math.abs(dy / distance * ball.speed), minVelocity) * (dy > 0 ? 1 : -1);
var paddleSpeedX = paddle.x - paddle.lastX;
var paddleSpeedY = paddle.y - paddle.lastY;
ball.velocityX += paddleSpeedX * 0.3;
ball.velocityY += paddleSpeedY * 0.3;
}
if (ball.x <= 80) ball.velocityX = Math.abs(ball.velocityX);
if (ball.x >= 2048 - 80) ball.velocityX = -Math.abs(ball.velocityX);
if (ball.y <= 80) ball.velocityY = Math.abs(ball.velocityY);
if (ball.y >= 2732 - 80) ball.velocityY = -Math.abs(ball.velocityY);
LK.getSound('hit').play();
}
}
}
game.update = function () {
if (gameState === 'menu') {
return;
}
if (gameState !== 'playing') return;
// AI Logic
updateAI(aiPaddle, false, 'defender');
if (gameMode === '2vs2') {
updateAI(aiTeammate1, true, 'midfielder');
updateAI(aiTeammate2, true, 'support');
}
// Ball collision detection
handleBallCollision(playerPaddle);
handleBallCollision(aiPaddle);
if (gameMode === '2vs2') {
handleBallCollision(aiTeammate1);
handleBallCollision(aiTeammate2);
}
// Goal detection
if (ball.y <= 20 && ball.x >= 824 && ball.x <= 1224) {
// Player scores
playerScore++;
LK.getSound('goal').play();
tween(ball, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 200,
onFinish: function onFinish() {
tween(ball, {
scaleX: 1,
scaleY: 1
}, {
duration: 200
});
}
});
checkWinCondition();
} else if (ball.y >= 2712 && ball.x >= 824 && ball.x <= 1224) {
// AI scores
aiScore++;
LK.getSound('goal').play();
tween(ball, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 200,
onFinish: function onFinish() {
tween(ball, {
scaleX: 1,
scaleY: 1
}, {
duration: 200
});
}
});
checkWinCondition();
}
// Emergency unstuck mechanism
var ballCurrentSpeed = Math.sqrt(ball.velocityX * ball.velocityX + ball.velocityY * ball.velocityY);
var isNearWall = ball.x <= 100 || ball.x >= 2048 - 100 || ball.y <= 100 || ball.y >= 2732 - 100;
var paddleDistances = [Math.sqrt(Math.pow(ball.x - playerPaddle.x, 2) + Math.pow(ball.y - playerPaddle.y, 2)), Math.sqrt(Math.pow(ball.x - aiPaddle.x, 2) + Math.pow(ball.y - aiPaddle.y, 2))];
if (gameMode === '2vs2') {
paddleDistances.push(Math.sqrt(Math.pow(ball.x - aiTeammate1.x, 2) + Math.pow(ball.y - aiTeammate1.y, 2)));
paddleDistances.push(Math.sqrt(Math.pow(ball.x - aiTeammate2.x, 2) + Math.pow(ball.y - aiTeammate2.y, 2)));
}
var isNearPaddle = false;
for (var i = 0; i < paddleDistances.length; i++) {
if (paddleDistances[i] <= 140) {
isNearPaddle = true;
break;
}
}
if (ballCurrentSpeed < 2 || isNearWall && ballCurrentSpeed < 4 || isNearPaddle && ballCurrentSpeed < 3) {
var pushX = 0;
var pushY = 0;
if (ball.x <= 100) pushX = 6;
if (ball.x >= 2048 - 100) pushX = -6;
if (ball.y <= 100) pushY = 6;
if (ball.y >= 2732 - 100) pushY = -6;
// Push away from all paddles
var allPaddles = [playerPaddle, aiPaddle];
if (gameMode === '2vs2') {
allPaddles.push(aiTeammate1, aiTeammate2);
}
for (var i = 0; i < allPaddles.length; i++) {
var paddle = allPaddles[i];
var distance = Math.sqrt(Math.pow(ball.x - paddle.x, 2) + Math.pow(ball.y - paddle.y, 2));
if (distance <= 140 && distance > 0) {
pushX += (ball.x - paddle.x) / distance * 5;
pushY += (ball.y - paddle.y) / distance * 5;
}
}
if (pushX === 0 && pushY === 0) {
pushX = Math.random() > 0.5 ? 5 : -5;
pushY = Math.random() > 0.5 ? 5 : -5;
}
ball.velocityX = pushX;
ball.velocityY = pushY;
ball.x += pushX * 2;
ball.y += pushY * 2;
ball.x = Math.max(40, Math.min(2048 - 40, ball.x));
ball.y = Math.max(40, Math.min(2732 - 40, ball.y));
}
// Increase ball speed over time in current level
ball.speed = Math.min(ball.maxSpeed, 8 + (level - 1) * 1.5);
// Update score display
scoreText.setText('You: ' + playerScore + ' - AI: ' + aiScore);
};
function checkWinCondition() {
if (playerScore >= goalsToWin) {
// Player wins level
level++;
playerScore = 0;
aiScore = 0;
levelText.setText('Level ' + level + ' (' + gameMode + ')');
// Flash screen green for level completion
LK.effects.flashScreen(0x00ff00, 1000);
// Reset positions
ball.reset();
playerPaddle.x = 1024;
playerPaddle.y = 2200;
aiPaddle.x = 1024;
aiPaddle.y = 532;
if (gameMode === '2vs2') {
aiTeammate1.x = 700;
aiTeammate1.y = 800;
aiTeammate2.x = 1348;
aiTeammate2.y = 1900;
}
// Check for game completion
if (level > 10) {
LK.showYouWin();
}
} else if (aiScore >= goalsToWin) {
// AI wins - game over
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
} else {
// Continue playing - reset ball
LK.setTimeout(function () {
ball.reset();
}, 1000);
}
}
// Initialize game with menu
showMainMenu(); /****
* 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.speed = 8;
self.maxSpeed = 15;
self.lastX = 0;
self.lastY = 0;
self.update = function () {
// Store last position for collision detection
self.lastX = self.x;
self.lastY = self.y;
self.x += self.velocityX;
self.y += self.velocityY;
// Bounce off left and right walls
if (self.x <= 40 || self.x >= 2048 - 40) {
self.velocityX = -self.velocityX;
self.x = self.x <= 40 ? 40 : 2048 - 40;
}
// Bounce off top and bottom walls (but not in goal areas)
if (self.y <= 40 && (self.x < 824 || self.x > 1224) || self.y >= 2732 - 40 && (self.x < 824 || self.x > 1224)) {
self.velocityY = -self.velocityY;
self.y = self.y <= 40 ? 40 : 2732 - 40;
}
};
self.reset = function () {
self.x = 1024;
self.y = 1366;
self.velocityX = 0;
self.velocityY = 0;
};
return self;
});
var MenuButton = Container.expand(function (text, onClick) {
var self = Container.call(this);
var buttonGraphics = self.attachAsset('menuButton', {
anchorX: 0.5,
anchorY: 0.5
});
var buttonText = new Text2(text, {
size: 60,
fill: 0xFFFFFF
});
buttonText.anchor.set(0.5, 0.5);
self.addChild(buttonText);
self.onClick = onClick;
self.down = function (x, y, obj) {
if (self.onClick) {
self.onClick();
}
};
return self;
});
var Paddle = Container.expand(function (isPlayer) {
var self = Container.call(this);
var paddleGraphics = self.attachAsset(isPlayer ? 'playerPaddle' : 'aiPaddle', {
anchorX: 0.5,
anchorY: 0.5
});
self.isPlayer = isPlayer;
self.lastX = 0;
self.lastY = 0;
self.update = function () {
self.lastX = self.x;
self.lastY = self.y;
// Keep paddle in bounds
if (self.x < 80) self.x = 80;
if (self.x > 2048 - 80) self.x = 2048 - 80;
if (self.isPlayer) {
// Player paddle stays in bottom half
if (self.y < 1366 + 100) self.y = 1366 + 100;
if (self.y > 2732 - 80) self.y = 2732 - 80;
} else {
// AI paddle can move in top 2/3 of field for better positioning
if (self.y < 80) self.y = 80;
if (self.y > 1800) self.y = 1800; // Allow more forward movement
}
};
return self;
});
/****
* Initialize Game
****/
// Game variables
var game = new LK.Game({
backgroundColor: 0x228B22
});
/****
* Game Code
****/
var gameMode = '1vs1'; // '1vs1' or '2vs2'
var gameState = 'menu'; // 'menu', 'playing'
// Game variables
var level = 1;
var playerScore = 0;
var aiScore = 0;
var goalsToWin = 3;
// Game objects (declared globally but created in game modes)
var ball;
var playerPaddle;
var aiPaddle;
var aiTeammate1; // For 2vs2 mode
var aiTeammate2; // For 2vs2 mode
var fieldElements = [];
var menuButtons = [];
var dragPaddle = null;
// UI elements
var levelText;
var scoreText;
var menuTitle;
// Show main menu
function showMainMenu() {
// Clear any existing field elements
clearField();
// Create game preview image at top
var gamePreview = game.addChild(LK.getAsset('gamePreview', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 600
}));
// Add mini soccer field elements to preview
var previewBall = game.addChild(LK.getAsset('soccerBall', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 600,
scaleX: 0.8,
scaleY: 0.8
}));
var previewPlayer = game.addChild(LK.getAsset('playerPaddle', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 720,
scaleX: 0.6,
scaleY: 0.6
}));
var previewAI = game.addChild(LK.getAsset('aiPaddle', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 480,
scaleX: 0.6,
scaleY: 0.6
}));
// Create menu title
menuTitle = new Text2('SOCCER GAME', {
size: 120,
fill: 0xFFFFFF
});
menuTitle.anchor.set(0.5, 0.5);
menuTitle.x = 1024;
menuTitle.y = 300;
game.addChild(menuTitle);
// Create 1vs1 button - larger and positioned lower
var button1vs1 = game.addChild(new MenuButton('1 vs 1', function () {
startGame('1vs1');
}));
button1vs1.x = 1024;
button1vs1.y = 1800;
menuButtons.push(button1vs1);
// Create 2vs2 button - larger and positioned lower
var button2vs2 = game.addChild(new MenuButton('2 vs 2', function () {
startGame('2vs2');
}));
button2vs2.x = 1024;
button2vs2.y = 2000;
menuButtons.push(button2vs2);
gameState = 'menu';
}
function clearField() {
// Remove all field elements
for (var i = fieldElements.length - 1; i >= 0; i--) {
fieldElements[i].destroy();
}
fieldElements = [];
// Remove menu buttons
for (var i = menuButtons.length - 1; i >= 0; i--) {
menuButtons[i].destroy();
}
menuButtons = [];
// Remove menu title
if (menuTitle) {
menuTitle.destroy();
menuTitle = null;
}
}
function createField() {
// Create checkered grass field background covering entire field
for (var row = 0; row < 43; row++) {
for (var col = 0; col < 32; col++) {
var isLight = (row + col) % 2 === 0;
var grassSquare = game.addChild(LK.getAsset(isLight ? 'grassSquareLight' : 'grassSquareDark', {
anchorX: 0,
anchorY: 0,
x: col * 64,
y: row * 64,
scaleX: 1.0,
scaleY: 1.0
}));
fieldElements.push(grassSquare);
}
}
// Create center circle line using multiple small white boxes in a circle (soccer field style)
var centerCircleRadius = 150;
var circleSegments = 60;
for (var i = 0; i < circleSegments; i++) {
var angle = i / circleSegments * Math.PI * 2;
var lineX = 1024 + Math.cos(angle) * centerCircleRadius;
var lineY = 1366 + Math.sin(angle) * centerCircleRadius;
var circleSegment = game.addChild(LK.getAsset('fieldLine', {
anchorX: 0.5,
anchorY: 0.5,
x: lineX,
y: lineY,
scaleX: 0.01,
scaleY: 0.8,
rotation: angle + Math.PI / 2
}));
fieldElements.push(circleSegment);
}
// Create center dot
var centerDot = game.addChild(LK.getAsset('ball', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366,
scaleX: 0.2,
scaleY: 0.2
}));
fieldElements.push(centerDot);
// Create field elements
var centerLine = game.addChild(LK.getAsset('fieldLine', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366
}));
fieldElements.push(centerLine);
// Create top goal (AI goal)
var topGoalPost = game.addChild(LK.getAsset('goalPost', {
anchorX: 0.5,
anchorY: 1.0,
x: 1024,
y: 80
}));
fieldElements.push(topGoalPost);
// Create top goal net with grid pattern - more visible
for (var i = 0; i < 9; i++) {
var netLineVertical = game.addChild(LK.getAsset('fieldLine', {
anchorX: 0.5,
anchorY: 0.0,
x: 824 + i * 44,
y: 10,
scaleX: 0.15,
scaleY: 20
}));
fieldElements.push(netLineVertical);
}
for (var j = 0; j < 5; j++) {
var netLineHorizontal = game.addChild(LK.getAsset('fieldLine', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 10 + j * 18,
scaleX: 0.25,
scaleY: 4
}));
fieldElements.push(netLineHorizontal);
}
// Create bottom goal (Player goal)
var bottomGoalPost = game.addChild(LK.getAsset('goalPost', {
anchorX: 0.5,
anchorY: 0.0,
x: 1024,
y: 2652
}));
fieldElements.push(bottomGoalPost);
// Create bottom goal net with grid pattern - more visible
for (var i = 0; i < 9; i++) {
var netLineVertical = game.addChild(LK.getAsset('fieldLine', {
anchorX: 0.5,
anchorY: 1.0,
x: 824 + i * 44,
y: 2722,
scaleX: 0.15,
scaleY: 20
}));
fieldElements.push(netLineVertical);
}
for (var j = 0; j < 5; j++) {
var netLineHorizontal = game.addChild(LK.getAsset('fieldLine', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 2722 - j * 18,
scaleX: 0.25,
scaleY: 4
}));
fieldElements.push(netLineHorizontal);
}
}
function startGame(mode) {
gameMode = mode;
clearField();
createField();
// Create game objects
ball = game.addChild(new Ball());
playerPaddle = game.addChild(new Paddle(true));
aiPaddle = game.addChild(new Paddle(false));
if (gameMode === '2vs2') {
// Create AI teammates - one red opponent and one green teammate
aiTeammate1 = game.addChild(new Paddle(false)); // Red opponent 1
aiTeammate2 = game.addChild(new Paddle(true)); // Green teammate (player's ally)
// Position teammates
aiTeammate1.x = 700; // Left side AI opponent
aiTeammate1.y = 800;
aiTeammate2.x = 1348; // Right side AI opponent
aiTeammate2.y = 800;
}
// Reset game state
level = 1;
playerScore = 0;
aiScore = 0;
// Position initial objects
ball.reset();
playerPaddle.x = 1024;
playerPaddle.y = 2200;
aiPaddle.x = 1024;
aiPaddle.y = 532;
// Create UI
levelText = new Text2('Level ' + level + ' (' + gameMode + ')', {
size: 80,
fill: 0xFFFFFF
});
levelText.anchor.set(0.5, 0);
LK.gui.top.addChild(levelText);
scoreText = new Text2('You: ' + playerScore + ' - AI: ' + aiScore, {
size: 60,
fill: 0xFFFFFF
});
scoreText.anchor.set(0.5, 0);
scoreText.y = 100;
LK.gui.top.addChild(scoreText);
gameState = 'playing';
}
// Touch controls
game.down = function (x, y, obj) {
if (gameState !== 'playing') return;
var distance = Math.sqrt(Math.pow(x - playerPaddle.x, 2) + Math.pow(y - playerPaddle.y, 2));
if (distance < 120) {
dragPaddle = playerPaddle;
}
};
game.move = function (x, y, obj) {
if (dragPaddle && gameState === 'playing') {
dragPaddle.x = x;
dragPaddle.y = y;
}
};
game.up = function (x, y, obj) {
dragPaddle = null;
};
function updateAI(paddle, isTeammate, position) {
var aiSpeed = 3 + (level - 1) * 0.5;
var ballTargetX = ball.x + ball.velocityX * 10;
var ballTargetY = ball.y + ball.velocityY * 5;
// Horizontal movement
if (paddle.x < ballTargetX - 20) {
paddle.x += aiSpeed;
} else if (paddle.x > ballTargetX + 20) {
paddle.x -= aiSpeed;
}
// Vertical movement based on position
if (position === 'defender') {
// Defensive AI behavior
if (ball.velocityY < 0 && ball.y < 1000) {
if (paddle.y > ballTargetY - 50 && paddle.y > 200) {
paddle.y -= aiSpeed * 0.7;
}
} else {
if (paddle.y < 400) {
paddle.y += aiSpeed * 0.5;
}
}
} else if (position === 'midfielder') {
// Midfielder AI behavior
if (paddle.y < ballTargetY - 30) {
paddle.y += aiSpeed * 0.6;
} else if (paddle.y > ballTargetY + 30) {
paddle.y -= aiSpeed * 0.6;
}
// Keep in middle area
if (paddle.y < 600) paddle.y = 600;
if (paddle.y > 1400) paddle.y = 1400;
} else if (position === 'support') {
// Support player AI behavior
if (paddle.y > ballTargetY + 30) {
paddle.y -= aiSpeed * 0.6;
} else if (paddle.y < ballTargetY - 30) {
paddle.y += aiSpeed * 0.6;
}
// Keep in player area
if (paddle.y < 1500) paddle.y = 1500;
if (paddle.y > 2500) paddle.y = 2500;
}
}
function handleBallCollision(paddle) {
var ballPaddleDistance = Math.sqrt(Math.pow(ball.x - paddle.x, 2) + Math.pow(ball.y - paddle.y, 2));
var lastBallPaddleDistance = Math.sqrt(Math.pow(ball.lastX - paddle.lastX, 2) + Math.pow(ball.lastY - paddle.lastY, 2));
var collisionRadius = 120;
if (ballPaddleDistance <= collisionRadius && lastBallPaddleDistance > collisionRadius || ball.intersects(paddle)) {
var dx = ball.x - paddle.x;
var dy = ball.y - paddle.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
var pushDistance = collisionRadius + 10;
var newBallX = paddle.x + dx / distance * pushDistance;
var newBallY = paddle.y + dy / distance * pushDistance;
newBallX = Math.max(40, Math.min(2048 - 40, newBallX));
newBallY = Math.max(40, Math.min(2732 - 40, newBallY));
ball.x = newBallX;
ball.y = newBallY;
var minVelocity = 3;
// Check if this is an AI paddle (not player)
if (!paddle.isPlayer) {
// AI always hits ball towards player goal (bottom of screen)
ball.velocityX = dx / distance * ball.speed;
ball.velocityY = Math.abs(dy / distance * ball.speed); // Always positive Y (towards bottom)
} else {
// Player paddle uses normal physics
ball.velocityX = Math.max(Math.abs(dx / distance * ball.speed), minVelocity) * (dx > 0 ? 1 : -1);
ball.velocityY = Math.max(Math.abs(dy / distance * ball.speed), minVelocity) * (dy > 0 ? 1 : -1);
var paddleSpeedX = paddle.x - paddle.lastX;
var paddleSpeedY = paddle.y - paddle.lastY;
ball.velocityX += paddleSpeedX * 0.3;
ball.velocityY += paddleSpeedY * 0.3;
}
if (ball.x <= 80) ball.velocityX = Math.abs(ball.velocityX);
if (ball.x >= 2048 - 80) ball.velocityX = -Math.abs(ball.velocityX);
if (ball.y <= 80) ball.velocityY = Math.abs(ball.velocityY);
if (ball.y >= 2732 - 80) ball.velocityY = -Math.abs(ball.velocityY);
LK.getSound('hit').play();
}
}
}
game.update = function () {
if (gameState === 'menu') {
return;
}
if (gameState !== 'playing') return;
// AI Logic
updateAI(aiPaddle, false, 'defender');
if (gameMode === '2vs2') {
updateAI(aiTeammate1, true, 'midfielder');
updateAI(aiTeammate2, true, 'support');
}
// Ball collision detection
handleBallCollision(playerPaddle);
handleBallCollision(aiPaddle);
if (gameMode === '2vs2') {
handleBallCollision(aiTeammate1);
handleBallCollision(aiTeammate2);
}
// Goal detection
if (ball.y <= 20 && ball.x >= 824 && ball.x <= 1224) {
// Player scores
playerScore++;
LK.getSound('goal').play();
tween(ball, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 200,
onFinish: function onFinish() {
tween(ball, {
scaleX: 1,
scaleY: 1
}, {
duration: 200
});
}
});
checkWinCondition();
} else if (ball.y >= 2712 && ball.x >= 824 && ball.x <= 1224) {
// AI scores
aiScore++;
LK.getSound('goal').play();
tween(ball, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 200,
onFinish: function onFinish() {
tween(ball, {
scaleX: 1,
scaleY: 1
}, {
duration: 200
});
}
});
checkWinCondition();
}
// Emergency unstuck mechanism
var ballCurrentSpeed = Math.sqrt(ball.velocityX * ball.velocityX + ball.velocityY * ball.velocityY);
var isNearWall = ball.x <= 100 || ball.x >= 2048 - 100 || ball.y <= 100 || ball.y >= 2732 - 100;
var paddleDistances = [Math.sqrt(Math.pow(ball.x - playerPaddle.x, 2) + Math.pow(ball.y - playerPaddle.y, 2)), Math.sqrt(Math.pow(ball.x - aiPaddle.x, 2) + Math.pow(ball.y - aiPaddle.y, 2))];
if (gameMode === '2vs2') {
paddleDistances.push(Math.sqrt(Math.pow(ball.x - aiTeammate1.x, 2) + Math.pow(ball.y - aiTeammate1.y, 2)));
paddleDistances.push(Math.sqrt(Math.pow(ball.x - aiTeammate2.x, 2) + Math.pow(ball.y - aiTeammate2.y, 2)));
}
var isNearPaddle = false;
for (var i = 0; i < paddleDistances.length; i++) {
if (paddleDistances[i] <= 140) {
isNearPaddle = true;
break;
}
}
if (ballCurrentSpeed < 2 || isNearWall && ballCurrentSpeed < 4 || isNearPaddle && ballCurrentSpeed < 3) {
var pushX = 0;
var pushY = 0;
if (ball.x <= 100) pushX = 6;
if (ball.x >= 2048 - 100) pushX = -6;
if (ball.y <= 100) pushY = 6;
if (ball.y >= 2732 - 100) pushY = -6;
// Push away from all paddles
var allPaddles = [playerPaddle, aiPaddle];
if (gameMode === '2vs2') {
allPaddles.push(aiTeammate1, aiTeammate2);
}
for (var i = 0; i < allPaddles.length; i++) {
var paddle = allPaddles[i];
var distance = Math.sqrt(Math.pow(ball.x - paddle.x, 2) + Math.pow(ball.y - paddle.y, 2));
if (distance <= 140 && distance > 0) {
pushX += (ball.x - paddle.x) / distance * 5;
pushY += (ball.y - paddle.y) / distance * 5;
}
}
if (pushX === 0 && pushY === 0) {
pushX = Math.random() > 0.5 ? 5 : -5;
pushY = Math.random() > 0.5 ? 5 : -5;
}
ball.velocityX = pushX;
ball.velocityY = pushY;
ball.x += pushX * 2;
ball.y += pushY * 2;
ball.x = Math.max(40, Math.min(2048 - 40, ball.x));
ball.y = Math.max(40, Math.min(2732 - 40, ball.y));
}
// Increase ball speed over time in current level
ball.speed = Math.min(ball.maxSpeed, 8 + (level - 1) * 1.5);
// Update score display
scoreText.setText('You: ' + playerScore + ' - AI: ' + aiScore);
};
function checkWinCondition() {
if (playerScore >= goalsToWin) {
// Player wins level
level++;
playerScore = 0;
aiScore = 0;
levelText.setText('Level ' + level + ' (' + gameMode + ')');
// Flash screen green for level completion
LK.effects.flashScreen(0x00ff00, 1000);
// Reset positions
ball.reset();
playerPaddle.x = 1024;
playerPaddle.y = 2200;
aiPaddle.x = 1024;
aiPaddle.y = 532;
if (gameMode === '2vs2') {
aiTeammate1.x = 700;
aiTeammate1.y = 800;
aiTeammate2.x = 1348;
aiTeammate2.y = 1900;
}
// Check for game completion
if (level > 10) {
LK.showYouWin();
}
} else if (aiScore >= goalsToWin) {
// AI wins - game over
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
} else {
// Continue playing - reset ball
LK.setTimeout(function () {
ball.reset();
}, 1000);
}
}
// Initialize game with menu
showMainMenu();