/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Ball = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('ball', {
anchorX: 0.5,
anchorY: 0.5
});
self.velocityX = 0;
self.velocityY = 0;
self.friction = 0.95;
self.update = function () {
self.x += self.velocityX;
self.y += self.velocityY;
self.velocityX *= self.friction;
self.velocityY *= self.friction;
// No barrier: Ball can move freely within the field bounds
if (self.x < 100) {
self.x = 100;
}
if (self.x > 1948) {
self.x = 1948;
}
if (self.y < 200) {
self.y = 200;
}
if (self.y > 2500) {
self.y = 2500;
}
};
self.kick = function (targetX, targetY, power) {
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 * power;
self.velocityY = dy / distance * power;
}
LK.getSound('kick').play();
};
return self;
});
var Opponent = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('opponent', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 2; // Slower opponent speed
self.team = 'opponent';
self.targetX = 0;
self.targetY = 0;
self.update = function () {
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;
}
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 3;
self.isSelected = false;
self.team = 'player';
self.originalColor = 0x0066cc;
self.select = function () {
self.isSelected = true;
};
self.deselect = function () {
self.isSelected = false;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x228b22
});
/****
* Game Code
****/
// Game field setup
var field = game.addChild(LK.getAsset('field', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366
}));
var centerLine = game.addChild(LK.getAsset('centerLine', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366
}));
var topGoal = game.addChild(LK.getAsset('goal', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 150,
alpha: 1,
// visible goal
width: 500,
// hitbox is now the same as the visible goal, not bigger
height: 200 // increased height to make goal even taller
}));
var bottomGoal = game.addChild(LK.getAsset('goal', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 2480,
// moved up by 100px
alpha: 1,
// visible goal
width: 500,
// hitbox is now the same as the visible goal, not bigger
height: 200 // increased height to make goal even taller
}));
// Game state
var players = [];
var opponents = [];
var selectedPlayer = null;
var isDragging = false;
var dragStartX = 0;
var dragStartY = 0;
var playerScore = 0;
var opponentScore = 0;
var gameTime = 120; // 2 minutes in seconds
var gameStarted = false;
// Ball possession and immunity
var ballHolder = null; // The player or opponent currently holding the ball
var lastBallHolder = null;
var globalImmunityUntil = 0; // Timestamp until which everyone is immune
var IMMUNITY_DURATION = 2000; // ms
// Create ball
var ball = game.addChild(new Ball());
ball.x = 1024;
ball.y = 1366;
// Create player team (bottom half)
for (var i = 0; i < 5; i++) {
var player = game.addChild(new Player());
player.x = 400 + i * 300;
player.y = 1800 + i % 2 * 200;
player._id = "player" + i;
players.push(player);
}
// Create opponent team (top half)
for (var i = 0; i < 5; i++) {
var opponent = game.addChild(new Opponent());
opponent.x = 400 + i * 300;
opponent.y = 900 + i % 2 * 200;
opponent.targetX = opponent.x;
opponent.targetY = opponent.y;
opponent._id = "opponent" + i;
opponents.push(opponent);
}
// UI elements
var scoreText = new Text2('Player: 0 - Opponent: 0', {
size: 60,
fill: 0xFFFFFF
});
scoreText.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreText);
// Only the throw button is used for controls now
var sizeControl = game.addChild(LK.getAsset('ball', {
anchorX: 0.5,
anchorY: 0.5,
x: 1624,
y: 2532,
alpha: 0.5,
scaleX: 2,
scaleY: 2
}));
var timeText = new Text2('2:00', {
size: 50,
fill: 0xFFFFFF
});
timeText.anchor.set(1, 0);
LK.gui.topRight.addChild(timeText);
var instructionText = new Text2('Control any blue player! Tap near a player to select them, then tap where to move. Non-selected players are AI bots. Tap big ball icon to make everything bigger!', {
size: 40,
fill: 0xFFFFFF
});
instructionText.anchor.set(0.5, 1);
LK.gui.bottom.addChild(instructionText);
// --- Move Buttons (Right Side) ---
var moveBtnSize = 180;
var moveBtnAlpha = 0.7;
var moveBtnOffset = 60;
// Up Button
var upBtn = new Text2('▲', {
size: moveBtnSize,
fill: 0xFFFF00,
font: "Impact, Arial Black, Tahoma"
});
upBtn.anchor.set(0.5, 0.5);
upBtn.alpha = moveBtnAlpha;
upBtn.interactive = true;
upBtn.buttonMode = true;
LK.gui.bottomRight.addChild(upBtn);
// Down Button
var downBtn = new Text2('▼', {
size: moveBtnSize,
fill: 0xFFFF00,
font: "Impact, Arial Black, Tahoma"
});
downBtn.anchor.set(0.5, 0.5);
downBtn.alpha = moveBtnAlpha;
downBtn.interactive = true;
downBtn.buttonMode = true;
LK.gui.bottomRight.addChild(downBtn);
// Left Button
var leftBtn = new Text2('◀', {
size: moveBtnSize,
fill: 0xFFFF00,
font: "Impact, Arial Black, Tahoma"
});
leftBtn.anchor.set(0.5, 0.5);
leftBtn.alpha = moveBtnAlpha;
leftBtn.interactive = true;
leftBtn.buttonMode = true;
LK.gui.bottomRight.addChild(leftBtn);
// Right Button
var rightBtn = new Text2('▶', {
size: moveBtnSize,
fill: 0xFFFF00,
font: "Impact, Arial Black, Tahoma"
});
rightBtn.anchor.set(0.5, 0.5);
rightBtn.alpha = moveBtnAlpha;
rightBtn.interactive = true;
rightBtn.buttonMode = true;
LK.gui.bottomRight.addChild(rightBtn);
// Position move buttons relative to each other (bottom right corner)
LK.setTimeout(function () {
// Use LK.gui.bottomRight's width/height for layout
// Move the move buttons a little bit further left by increasing the offset
var leftwardOffset = 320; // Amount to move left (in px) - increased for "a little bit left"
// Move the move buttons higher up by increasing the upward offset
var upwardOffset = 350; // Amount to move up (in px)
var baseX = LK.gui.bottomRight.width - moveBtnSize - moveBtnOffset - leftwardOffset;
var baseY = LK.gui.bottomRight.height - moveBtnSize - moveBtnOffset - upwardOffset;
upBtn.x = baseX + moveBtnSize;
upBtn.y = baseY - moveBtnSize * 0.7;
downBtn.x = baseX + moveBtnSize;
downBtn.y = baseY + moveBtnSize * 0.7;
leftBtn.x = baseX;
leftBtn.y = baseY;
rightBtn.x = baseX + moveBtnSize * 2;
rightBtn.y = baseY;
}, 0);
// --- Throw Button (Left Side) ---
var throwButton = new Text2('THROW', {
size: 160,
fill: 0xFFFF00,
font: "Impact, Arial Black, Tahoma"
});
throwButton.anchor.set(0, 1);
throwButton.interactive = true;
throwButton.buttonMode = true;
LK.gui.bottomLeft.addChild(throwButton);
// Handle throw button tap
throwButton.down = function (x, y, obj) {
// Only allow throw if player is holding the ball
if (ballHolder && ballHolder.team === 'player') {
// Throw the ball towards the top goal (opponent's goal)
ballHolder = null; // Release the ball
// Give a strong upward kick towards the center of the top goal
ball.kick(1024, 150, 15); // (no change needed, but included for clarity)
}
};
// Move button handlers
// --- Hold-to-move logic for move buttons ---
var moveHoldInterval = 80; // ms between repeated moves while holding
var upBtnTimer = null;
var downBtnTimer = null;
var leftBtnTimer = null;
var rightBtnTimer = null;
upBtn.down = function () {
moveSelectedPlayer(0, -120);
if (upBtnTimer) LK.clearInterval(upBtnTimer);
upBtnTimer = LK.setInterval(function () {
moveSelectedPlayer(0, -120);
}, moveHoldInterval);
};
upBtn.up = function () {
if (upBtnTimer) {
LK.clearInterval(upBtnTimer);
upBtnTimer = null;
}
};
downBtn.down = function () {
moveSelectedPlayer(0, 120);
if (downBtnTimer) LK.clearInterval(downBtnTimer);
downBtnTimer = LK.setInterval(function () {
moveSelectedPlayer(0, 120);
}, moveHoldInterval);
};
downBtn.up = function () {
if (downBtnTimer) {
LK.clearInterval(downBtnTimer);
downBtnTimer = null;
}
};
leftBtn.down = function () {
moveSelectedPlayer(-120, 0);
if (leftBtnTimer) LK.clearInterval(leftBtnTimer);
leftBtnTimer = LK.setInterval(function () {
moveSelectedPlayer(-120, 0);
}, moveHoldInterval);
};
leftBtn.up = function () {
if (leftBtnTimer) {
LK.clearInterval(leftBtnTimer);
leftBtnTimer = null;
}
};
rightBtn.down = function () {
moveSelectedPlayer(120, 0);
if (rightBtnTimer) LK.clearInterval(rightBtnTimer);
rightBtnTimer = LK.setInterval(function () {
moveSelectedPlayer(120, 0);
}, moveHoldInterval);
};
rightBtn.up = function () {
if (rightBtnTimer) {
LK.clearInterval(rightBtnTimer);
rightBtnTimer = null;
}
};
// Game timer
var gameTimer = LK.setInterval(function () {
if (gameStarted && gameTime > 0) {
gameTime--;
var minutes = Math.floor(gameTime / 60);
var seconds = gameTime % 60;
timeText.setText(minutes + ':' + (seconds < 10 ? '0' : '') + seconds);
if (gameTime <= 0) {
// Game over
if (playerScore > opponentScore) {
LK.showYouWin();
} else {
LK.showGameOver();
}
LK.clearInterval(gameTimer);
}
}
}, 1000);
// Start game after short delay
LK.setTimeout(function () {
gameStarted = true;
}, 1000);
function updateScore() {
scoreText.setText('Player: ' + playerScore + ' - Opponent: ' + opponentScore);
}
function findClosestPlayer(x, y) {
var closestPlayer = null;
var minDistance = Infinity;
// Check all players to find the closest one
for (var i = 0; i < players.length; i++) {
var player = players[i];
var dx = player.x - x;
var dy = player.y - y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 150 && distance < minDistance) {
minDistance = distance;
closestPlayer = player;
}
}
return closestPlayer;
}
function deselectAllPlayers() {
for (var i = 0; i < players.length; i++) {
players[i].deselect();
}
selectedPlayer = null;
}
function checkGoal() {
// Use intersects for goal detection, only trigger on the frame the ball enters the goal
if (typeof ball.lastWasInTopGoal === "undefined") ball.lastWasInTopGoal = false;
if (typeof ball.lastWasInBottomGoal === "undefined") ball.lastWasInBottomGoal = false;
var nowInTopGoal = ball.intersects(topGoal);
var nowInBottomGoal = ball.intersects(bottomGoal);
// Top goal (opponent's goal)
if (!ball.lastWasInTopGoal && nowInTopGoal) {
playerScore++;
updateScore();
LK.getSound('goal').play();
resetBallPosition();
ball.lastWasInTopGoal = nowInTopGoal;
ball.lastWasInBottomGoal = nowInBottomGoal;
return true;
}
// Bottom goal (player's goal)
if (!ball.lastWasInBottomGoal && nowInBottomGoal) {
opponentScore++;
updateScore();
LK.getSound('goal').play();
resetBallPosition();
ball.lastWasInTopGoal = nowInTopGoal;
ball.lastWasInBottomGoal = nowInBottomGoal;
return true;
}
ball.lastWasInTopGoal = nowInTopGoal;
ball.lastWasInBottomGoal = nowInBottomGoal;
return false;
}
function resetBallPosition() {
ball.x = 1024;
ball.y = 1366;
ball.velocityX = 0;
ball.velocityY = 0;
}
function updateOpponentAI() {
// If an opponent is holding the ball, make them only run into the goal (never throw or kick)
if (ballHolder && ballHolder.team === 'opponent') {
for (var i = 0; i < opponents.length; i++) {
var opponent = opponents[i];
if (opponent === ballHolder) {
// Always run directly at the center, but allow even closer to the bottom goal (just above the goal line)
opponent.targetX = 1024;
opponent.targetY = 2500; // was 2600, now allows closer (goal y=2480, goal height=40, so 2500 is just inside)
// --- NEW LOGIC: If the ball is following the opponent and he stops, throw at our goal ---
// We'll consider "stopped" as not moving towards the target anymore (distance to target < 5)
if (typeof opponent.lastX === "undefined") opponent.lastX = opponent.x;
if (typeof opponent.lastY === "undefined") opponent.lastY = opponent.y;
var dx = opponent.targetX - opponent.x;
var dy = opponent.targetY - opponent.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// If the opponent is stopped (distance to target < 5) and still holding the ball, throw at our goal
if (distance < 5 && ballHolder === opponent) {
// Only throw if the ball is still on the opponent (not already thrown)
// Throw the ball towards the bottom goal (our goal)
// Move the ball slightly ahead of the opponent so it visually leaves the player
var throwDx = 1024 - opponent.x;
var throwDy = 2480 - opponent.y;
var throwDist = Math.sqrt(throwDx * throwDx + throwDy * throwDy);
if (throwDist > 0) {
ball.x = opponent.x + throwDx / throwDist * 60;
ball.y = opponent.y + throwDy / throwDist * 60;
}
ballHolder = null; // Release the ball
// Actually throw the ball: set velocity and move the ball off the opponent
ball.kick(1024, 2480, 18); // Kick towards center of our goal with a bit more power
}
opponent.lastX = opponent.x;
opponent.lastY = opponent.y;
// --- END NEW LOGIC ---
} else {
opponent.targetX = 400 + i * 300;
opponent.targetY = 600 + i % 2 * 300;
}
}
return;
}
// Only the closest opponent to the ball runs at the ball, others idle in place
var closestIdx = -1;
var minDist = Infinity;
for (var i = 0; i < opponents.length; i++) {
var dx = ball.x - opponents[i].x;
var dy = ball.y - opponents[i].y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < minDist) {
minDist = dist;
closestIdx = i;
}
}
for (var i = 0; i < opponents.length; i++) {
if (i === closestIdx) {
opponents[i].targetX = ball.x;
opponents[i].targetY = ball.y;
} else {
// Idle in place (stay at current position)
opponents[i].targetX = opponents[i].x;
opponents[i].targetY = opponents[i].y;
}
}
// If opponent touches ball, do not allow them to kick towards player goal
// (Disable opponent goal attempts)
}
function updatePlayerAI() {
// AI for non-selected players only
for (var i = 0; i < players.length; i++) {
var player = players[i];
// Skip the currently selected player
if (player === selectedPlayer) continue;
// Defensive AI: Move to defend the goal, not chase the ball
// Assign each player a defensive position in front of the goal
var defendX = 600 + i * 200; // Spread out in front of goal horizontally
var defendY = 2350 + i % 2 * 100; // Two rows in front of goal
// If the ball is very close to the goal, collapse to the center to block
if (ball.y > 2200) {
defendX = 1024 + (i - 2) * 90; // Cluster in front of goal center
defendY = 2450 + i % 2 * 40;
}
// Move towards defensive position
var dx = defendX - player.x;
var dy = defendY - player.y;
var distance = Math.sqrt(dx * dx + dy * dy);
var moveSpeed = 7.5 * 8; // Keep fast repositioning
if (distance > 10) {
player.x += dx / distance * moveSpeed;
player.y += dy / distance * moveSpeed;
}
}
}
// Movement control zones
var controlZoneSize = 200;
var leftZone = {
x: 100,
y: 2532,
width: controlZoneSize,
height: controlZoneSize
};
var rightZone = {
x: 1748,
y: 2532,
width: controlZoneSize,
height: controlZoneSize
};
var upZone = {
x: 924,
y: 2432,
width: controlZoneSize,
height: controlZoneSize
};
var downZone = {
x: 924,
y: 2632,
width: controlZoneSize,
height: controlZoneSize
};
var sizeUpZone = {
x: 1524,
y: 2432,
width: controlZoneSize,
height: controlZoneSize
};
function isInZone(x, y, zone) {
return x >= zone.x && x <= zone.x + zone.width && y >= zone.y && y <= zone.y + zone.width;
}
function moveSelectedPlayer(dx, dy) {
if (!selectedPlayer) return;
var newX = selectedPlayer.x + dx;
var newY = selectedPlayer.y + dy;
// Keep within field bounds
newX = Math.max(100, Math.min(1948, newX));
newY = Math.max(200, Math.min(2500, newY)); // Allow movement to entire field
tween(selectedPlayer, {
x: newX,
y: newY
}, {
duration: 200,
easing: tween.easeOut
});
}
function makeBigger() {
// Make ball bigger
tween(ball, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 500,
easing: tween.easeOut
});
// Make all players bigger
for (var i = 0; i < players.length; i++) {
tween(players[i], {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 500,
easing: tween.easeOut
});
}
// Make all opponents bigger
for (var i = 0; i < opponents.length; i++) {
tween(opponents[i], {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 500,
easing: tween.easeOut
});
}
}
game.down = function (x, y, obj) {
if (!gameStarted) return;
// Check size up zone first
if (isInZone(x, y, sizeUpZone)) {
makeBigger();
return;
}
// If a player is selected, move them to the tapped location
if (selectedPlayer) {
// Allow movement anywhere on the field
var targetX = Math.max(100, Math.min(1948, x));
var targetY = Math.max(200, Math.min(2500, y)); // Allow movement to entire field
tween(selectedPlayer, {
x: targetX,
y: targetY
}, {
duration: 400,
easing: tween.easeOut
});
// Player kick is disabled
} else {
// Find and select closest player
var clickedPlayer = findClosestPlayer(x, y);
if (clickedPlayer) {
deselectAllPlayers();
clickedPlayer.select();
selectedPlayer = clickedPlayer;
}
}
};
game.move = function (x, y, obj) {
// Move function simplified - main control is now tap-to-move in down event
};
game.up = function (x, y, obj) {
// Up function simplified - main control is now tap-to-move
};
game.update = function () {
if (!gameStarted) return;
// Track lastY for goal detection
if (typeof ball.lastY === "undefined") ball.lastY = ball.y;
var prevBallY = ball.y;
ball.update();
ball.lastY = prevBallY;
// --- Ball possession and immunity logic ---
var now = Date.now();
var newHolder = null;
// If no one is holding the ball, allow anyone to take it by touching the ball
if (!ballHolder && now > globalImmunityUntil) {
// Check if any player touches the ball and can take it
for (var i = 0; i < players.length; i++) {
var player = players[i];
var dx = ball.x - player.x;
var dy = ball.y - player.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < 100) {
newHolder = player;
break;
}
}
// If no player, check if any opponent can take the ball
if (!newHolder) {
for (var i = 0; i < opponents.length; i++) {
var opponent = opponents[i];
var dx = ball.x - opponent.x;
var dy = ball.y - opponent.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < 100) {
newHolder = opponent;
break;
}
}
}
} else if (ballHolder && now > globalImmunityUntil) {
// If someone is holding the ball, check if another player (not the holder) touches the holder to steal the ball
// Check for player-to-player steal
if (ballHolder.team === 'player') {
for (var i = 0; i < players.length; i++) {
var otherPlayer = players[i];
if (otherPlayer !== ballHolder) {
var dx = ballHolder.x - otherPlayer.x;
var dy = ballHolder.y - otherPlayer.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < 100) {
newHolder = otherPlayer;
break;
}
}
}
}
// Check for opponent-to-opponent steal
if (!newHolder && ballHolder.team === 'opponent') {
for (var i = 0; i < opponents.length; i++) {
var otherOpponent = opponents[i];
if (otherOpponent !== ballHolder) {
var dx = ballHolder.x - otherOpponent.x;
var dy = ballHolder.y - otherOpponent.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < 100) {
newHolder = otherOpponent;
break;
}
}
}
}
// Check for opponent stealing from player
if (!newHolder && ballHolder.team === 'player') {
for (var i = 0; i < opponents.length; i++) {
var opponent = opponents[i];
var dx = ballHolder.x - opponent.x;
var dy = ballHolder.y - opponent.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < 100) {
newHolder = opponent;
break;
}
}
}
// Check for player stealing from opponent
if (!newHolder && ballHolder.team === 'opponent') {
for (var i = 0; i < players.length; i++) {
var player = players[i];
var dx = ballHolder.x - player.x;
var dy = ballHolder.y - player.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < 100) {
newHolder = player;
break;
}
}
}
}
// If someone new takes the ball
if (newHolder && newHolder !== ballHolder) {
// Set global immunity for everyone for 2 seconds
globalImmunityUntil = now + IMMUNITY_DURATION;
ballHolder = newHolder;
// Move ball to holder's position
ball.x = ballHolder.x;
ball.y = ballHolder.y;
ball.velocityX = 0;
ball.velocityY = 0;
// Player keeps the ball after gaining possession (do not drop)
}
// If someone is holding the ball, keep ball on them
if (ballHolder) {
ball.x = ballHolder.x;
ball.y = ballHolder.y;
}
// --- End ball possession and immunity logic ---
// Update opponents
for (var i = 0; i < opponents.length; i++) {
opponents[i].update();
}
// Update AI
if (LK.ticks % 30 === 0) {
// Update AI every half second
updateOpponentAI();
updatePlayerAI();
}
// Check for goals
checkGoal();
// Keep players within field bounds
for (var i = 0; i < players.length; i++) {
var player = players[i];
if (player.y < 200) {
player.y = 200;
}
if (player.y > 2500) {
player.y = 2500;
}
}
// Keep opponents within field bounds (allow anywhere on field)
for (var i = 0; i < opponents.length; i++) {
var opponent = opponents[i];
if (opponent.x < 100) {
opponent.x = 100;
}
if (opponent.x > 1948) {
opponent.x = 1948;
}
if (opponent.y < 200) {
opponent.y = 200;
}
if (opponent.y > 2500) {
opponent.y = 2500;
}
}
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Ball = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('ball', {
anchorX: 0.5,
anchorY: 0.5
});
self.velocityX = 0;
self.velocityY = 0;
self.friction = 0.95;
self.update = function () {
self.x += self.velocityX;
self.y += self.velocityY;
self.velocityX *= self.friction;
self.velocityY *= self.friction;
// No barrier: Ball can move freely within the field bounds
if (self.x < 100) {
self.x = 100;
}
if (self.x > 1948) {
self.x = 1948;
}
if (self.y < 200) {
self.y = 200;
}
if (self.y > 2500) {
self.y = 2500;
}
};
self.kick = function (targetX, targetY, power) {
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 * power;
self.velocityY = dy / distance * power;
}
LK.getSound('kick').play();
};
return self;
});
var Opponent = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('opponent', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 2; // Slower opponent speed
self.team = 'opponent';
self.targetX = 0;
self.targetY = 0;
self.update = function () {
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;
}
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 3;
self.isSelected = false;
self.team = 'player';
self.originalColor = 0x0066cc;
self.select = function () {
self.isSelected = true;
};
self.deselect = function () {
self.isSelected = false;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x228b22
});
/****
* Game Code
****/
// Game field setup
var field = game.addChild(LK.getAsset('field', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366
}));
var centerLine = game.addChild(LK.getAsset('centerLine', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366
}));
var topGoal = game.addChild(LK.getAsset('goal', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 150,
alpha: 1,
// visible goal
width: 500,
// hitbox is now the same as the visible goal, not bigger
height: 200 // increased height to make goal even taller
}));
var bottomGoal = game.addChild(LK.getAsset('goal', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 2480,
// moved up by 100px
alpha: 1,
// visible goal
width: 500,
// hitbox is now the same as the visible goal, not bigger
height: 200 // increased height to make goal even taller
}));
// Game state
var players = [];
var opponents = [];
var selectedPlayer = null;
var isDragging = false;
var dragStartX = 0;
var dragStartY = 0;
var playerScore = 0;
var opponentScore = 0;
var gameTime = 120; // 2 minutes in seconds
var gameStarted = false;
// Ball possession and immunity
var ballHolder = null; // The player or opponent currently holding the ball
var lastBallHolder = null;
var globalImmunityUntil = 0; // Timestamp until which everyone is immune
var IMMUNITY_DURATION = 2000; // ms
// Create ball
var ball = game.addChild(new Ball());
ball.x = 1024;
ball.y = 1366;
// Create player team (bottom half)
for (var i = 0; i < 5; i++) {
var player = game.addChild(new Player());
player.x = 400 + i * 300;
player.y = 1800 + i % 2 * 200;
player._id = "player" + i;
players.push(player);
}
// Create opponent team (top half)
for (var i = 0; i < 5; i++) {
var opponent = game.addChild(new Opponent());
opponent.x = 400 + i * 300;
opponent.y = 900 + i % 2 * 200;
opponent.targetX = opponent.x;
opponent.targetY = opponent.y;
opponent._id = "opponent" + i;
opponents.push(opponent);
}
// UI elements
var scoreText = new Text2('Player: 0 - Opponent: 0', {
size: 60,
fill: 0xFFFFFF
});
scoreText.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreText);
// Only the throw button is used for controls now
var sizeControl = game.addChild(LK.getAsset('ball', {
anchorX: 0.5,
anchorY: 0.5,
x: 1624,
y: 2532,
alpha: 0.5,
scaleX: 2,
scaleY: 2
}));
var timeText = new Text2('2:00', {
size: 50,
fill: 0xFFFFFF
});
timeText.anchor.set(1, 0);
LK.gui.topRight.addChild(timeText);
var instructionText = new Text2('Control any blue player! Tap near a player to select them, then tap where to move. Non-selected players are AI bots. Tap big ball icon to make everything bigger!', {
size: 40,
fill: 0xFFFFFF
});
instructionText.anchor.set(0.5, 1);
LK.gui.bottom.addChild(instructionText);
// --- Move Buttons (Right Side) ---
var moveBtnSize = 180;
var moveBtnAlpha = 0.7;
var moveBtnOffset = 60;
// Up Button
var upBtn = new Text2('▲', {
size: moveBtnSize,
fill: 0xFFFF00,
font: "Impact, Arial Black, Tahoma"
});
upBtn.anchor.set(0.5, 0.5);
upBtn.alpha = moveBtnAlpha;
upBtn.interactive = true;
upBtn.buttonMode = true;
LK.gui.bottomRight.addChild(upBtn);
// Down Button
var downBtn = new Text2('▼', {
size: moveBtnSize,
fill: 0xFFFF00,
font: "Impact, Arial Black, Tahoma"
});
downBtn.anchor.set(0.5, 0.5);
downBtn.alpha = moveBtnAlpha;
downBtn.interactive = true;
downBtn.buttonMode = true;
LK.gui.bottomRight.addChild(downBtn);
// Left Button
var leftBtn = new Text2('◀', {
size: moveBtnSize,
fill: 0xFFFF00,
font: "Impact, Arial Black, Tahoma"
});
leftBtn.anchor.set(0.5, 0.5);
leftBtn.alpha = moveBtnAlpha;
leftBtn.interactive = true;
leftBtn.buttonMode = true;
LK.gui.bottomRight.addChild(leftBtn);
// Right Button
var rightBtn = new Text2('▶', {
size: moveBtnSize,
fill: 0xFFFF00,
font: "Impact, Arial Black, Tahoma"
});
rightBtn.anchor.set(0.5, 0.5);
rightBtn.alpha = moveBtnAlpha;
rightBtn.interactive = true;
rightBtn.buttonMode = true;
LK.gui.bottomRight.addChild(rightBtn);
// Position move buttons relative to each other (bottom right corner)
LK.setTimeout(function () {
// Use LK.gui.bottomRight's width/height for layout
// Move the move buttons a little bit further left by increasing the offset
var leftwardOffset = 320; // Amount to move left (in px) - increased for "a little bit left"
// Move the move buttons higher up by increasing the upward offset
var upwardOffset = 350; // Amount to move up (in px)
var baseX = LK.gui.bottomRight.width - moveBtnSize - moveBtnOffset - leftwardOffset;
var baseY = LK.gui.bottomRight.height - moveBtnSize - moveBtnOffset - upwardOffset;
upBtn.x = baseX + moveBtnSize;
upBtn.y = baseY - moveBtnSize * 0.7;
downBtn.x = baseX + moveBtnSize;
downBtn.y = baseY + moveBtnSize * 0.7;
leftBtn.x = baseX;
leftBtn.y = baseY;
rightBtn.x = baseX + moveBtnSize * 2;
rightBtn.y = baseY;
}, 0);
// --- Throw Button (Left Side) ---
var throwButton = new Text2('THROW', {
size: 160,
fill: 0xFFFF00,
font: "Impact, Arial Black, Tahoma"
});
throwButton.anchor.set(0, 1);
throwButton.interactive = true;
throwButton.buttonMode = true;
LK.gui.bottomLeft.addChild(throwButton);
// Handle throw button tap
throwButton.down = function (x, y, obj) {
// Only allow throw if player is holding the ball
if (ballHolder && ballHolder.team === 'player') {
// Throw the ball towards the top goal (opponent's goal)
ballHolder = null; // Release the ball
// Give a strong upward kick towards the center of the top goal
ball.kick(1024, 150, 15); // (no change needed, but included for clarity)
}
};
// Move button handlers
// --- Hold-to-move logic for move buttons ---
var moveHoldInterval = 80; // ms between repeated moves while holding
var upBtnTimer = null;
var downBtnTimer = null;
var leftBtnTimer = null;
var rightBtnTimer = null;
upBtn.down = function () {
moveSelectedPlayer(0, -120);
if (upBtnTimer) LK.clearInterval(upBtnTimer);
upBtnTimer = LK.setInterval(function () {
moveSelectedPlayer(0, -120);
}, moveHoldInterval);
};
upBtn.up = function () {
if (upBtnTimer) {
LK.clearInterval(upBtnTimer);
upBtnTimer = null;
}
};
downBtn.down = function () {
moveSelectedPlayer(0, 120);
if (downBtnTimer) LK.clearInterval(downBtnTimer);
downBtnTimer = LK.setInterval(function () {
moveSelectedPlayer(0, 120);
}, moveHoldInterval);
};
downBtn.up = function () {
if (downBtnTimer) {
LK.clearInterval(downBtnTimer);
downBtnTimer = null;
}
};
leftBtn.down = function () {
moveSelectedPlayer(-120, 0);
if (leftBtnTimer) LK.clearInterval(leftBtnTimer);
leftBtnTimer = LK.setInterval(function () {
moveSelectedPlayer(-120, 0);
}, moveHoldInterval);
};
leftBtn.up = function () {
if (leftBtnTimer) {
LK.clearInterval(leftBtnTimer);
leftBtnTimer = null;
}
};
rightBtn.down = function () {
moveSelectedPlayer(120, 0);
if (rightBtnTimer) LK.clearInterval(rightBtnTimer);
rightBtnTimer = LK.setInterval(function () {
moveSelectedPlayer(120, 0);
}, moveHoldInterval);
};
rightBtn.up = function () {
if (rightBtnTimer) {
LK.clearInterval(rightBtnTimer);
rightBtnTimer = null;
}
};
// Game timer
var gameTimer = LK.setInterval(function () {
if (gameStarted && gameTime > 0) {
gameTime--;
var minutes = Math.floor(gameTime / 60);
var seconds = gameTime % 60;
timeText.setText(minutes + ':' + (seconds < 10 ? '0' : '') + seconds);
if (gameTime <= 0) {
// Game over
if (playerScore > opponentScore) {
LK.showYouWin();
} else {
LK.showGameOver();
}
LK.clearInterval(gameTimer);
}
}
}, 1000);
// Start game after short delay
LK.setTimeout(function () {
gameStarted = true;
}, 1000);
function updateScore() {
scoreText.setText('Player: ' + playerScore + ' - Opponent: ' + opponentScore);
}
function findClosestPlayer(x, y) {
var closestPlayer = null;
var minDistance = Infinity;
// Check all players to find the closest one
for (var i = 0; i < players.length; i++) {
var player = players[i];
var dx = player.x - x;
var dy = player.y - y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 150 && distance < minDistance) {
minDistance = distance;
closestPlayer = player;
}
}
return closestPlayer;
}
function deselectAllPlayers() {
for (var i = 0; i < players.length; i++) {
players[i].deselect();
}
selectedPlayer = null;
}
function checkGoal() {
// Use intersects for goal detection, only trigger on the frame the ball enters the goal
if (typeof ball.lastWasInTopGoal === "undefined") ball.lastWasInTopGoal = false;
if (typeof ball.lastWasInBottomGoal === "undefined") ball.lastWasInBottomGoal = false;
var nowInTopGoal = ball.intersects(topGoal);
var nowInBottomGoal = ball.intersects(bottomGoal);
// Top goal (opponent's goal)
if (!ball.lastWasInTopGoal && nowInTopGoal) {
playerScore++;
updateScore();
LK.getSound('goal').play();
resetBallPosition();
ball.lastWasInTopGoal = nowInTopGoal;
ball.lastWasInBottomGoal = nowInBottomGoal;
return true;
}
// Bottom goal (player's goal)
if (!ball.lastWasInBottomGoal && nowInBottomGoal) {
opponentScore++;
updateScore();
LK.getSound('goal').play();
resetBallPosition();
ball.lastWasInTopGoal = nowInTopGoal;
ball.lastWasInBottomGoal = nowInBottomGoal;
return true;
}
ball.lastWasInTopGoal = nowInTopGoal;
ball.lastWasInBottomGoal = nowInBottomGoal;
return false;
}
function resetBallPosition() {
ball.x = 1024;
ball.y = 1366;
ball.velocityX = 0;
ball.velocityY = 0;
}
function updateOpponentAI() {
// If an opponent is holding the ball, make them only run into the goal (never throw or kick)
if (ballHolder && ballHolder.team === 'opponent') {
for (var i = 0; i < opponents.length; i++) {
var opponent = opponents[i];
if (opponent === ballHolder) {
// Always run directly at the center, but allow even closer to the bottom goal (just above the goal line)
opponent.targetX = 1024;
opponent.targetY = 2500; // was 2600, now allows closer (goal y=2480, goal height=40, so 2500 is just inside)
// --- NEW LOGIC: If the ball is following the opponent and he stops, throw at our goal ---
// We'll consider "stopped" as not moving towards the target anymore (distance to target < 5)
if (typeof opponent.lastX === "undefined") opponent.lastX = opponent.x;
if (typeof opponent.lastY === "undefined") opponent.lastY = opponent.y;
var dx = opponent.targetX - opponent.x;
var dy = opponent.targetY - opponent.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// If the opponent is stopped (distance to target < 5) and still holding the ball, throw at our goal
if (distance < 5 && ballHolder === opponent) {
// Only throw if the ball is still on the opponent (not already thrown)
// Throw the ball towards the bottom goal (our goal)
// Move the ball slightly ahead of the opponent so it visually leaves the player
var throwDx = 1024 - opponent.x;
var throwDy = 2480 - opponent.y;
var throwDist = Math.sqrt(throwDx * throwDx + throwDy * throwDy);
if (throwDist > 0) {
ball.x = opponent.x + throwDx / throwDist * 60;
ball.y = opponent.y + throwDy / throwDist * 60;
}
ballHolder = null; // Release the ball
// Actually throw the ball: set velocity and move the ball off the opponent
ball.kick(1024, 2480, 18); // Kick towards center of our goal with a bit more power
}
opponent.lastX = opponent.x;
opponent.lastY = opponent.y;
// --- END NEW LOGIC ---
} else {
opponent.targetX = 400 + i * 300;
opponent.targetY = 600 + i % 2 * 300;
}
}
return;
}
// Only the closest opponent to the ball runs at the ball, others idle in place
var closestIdx = -1;
var minDist = Infinity;
for (var i = 0; i < opponents.length; i++) {
var dx = ball.x - opponents[i].x;
var dy = ball.y - opponents[i].y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < minDist) {
minDist = dist;
closestIdx = i;
}
}
for (var i = 0; i < opponents.length; i++) {
if (i === closestIdx) {
opponents[i].targetX = ball.x;
opponents[i].targetY = ball.y;
} else {
// Idle in place (stay at current position)
opponents[i].targetX = opponents[i].x;
opponents[i].targetY = opponents[i].y;
}
}
// If opponent touches ball, do not allow them to kick towards player goal
// (Disable opponent goal attempts)
}
function updatePlayerAI() {
// AI for non-selected players only
for (var i = 0; i < players.length; i++) {
var player = players[i];
// Skip the currently selected player
if (player === selectedPlayer) continue;
// Defensive AI: Move to defend the goal, not chase the ball
// Assign each player a defensive position in front of the goal
var defendX = 600 + i * 200; // Spread out in front of goal horizontally
var defendY = 2350 + i % 2 * 100; // Two rows in front of goal
// If the ball is very close to the goal, collapse to the center to block
if (ball.y > 2200) {
defendX = 1024 + (i - 2) * 90; // Cluster in front of goal center
defendY = 2450 + i % 2 * 40;
}
// Move towards defensive position
var dx = defendX - player.x;
var dy = defendY - player.y;
var distance = Math.sqrt(dx * dx + dy * dy);
var moveSpeed = 7.5 * 8; // Keep fast repositioning
if (distance > 10) {
player.x += dx / distance * moveSpeed;
player.y += dy / distance * moveSpeed;
}
}
}
// Movement control zones
var controlZoneSize = 200;
var leftZone = {
x: 100,
y: 2532,
width: controlZoneSize,
height: controlZoneSize
};
var rightZone = {
x: 1748,
y: 2532,
width: controlZoneSize,
height: controlZoneSize
};
var upZone = {
x: 924,
y: 2432,
width: controlZoneSize,
height: controlZoneSize
};
var downZone = {
x: 924,
y: 2632,
width: controlZoneSize,
height: controlZoneSize
};
var sizeUpZone = {
x: 1524,
y: 2432,
width: controlZoneSize,
height: controlZoneSize
};
function isInZone(x, y, zone) {
return x >= zone.x && x <= zone.x + zone.width && y >= zone.y && y <= zone.y + zone.width;
}
function moveSelectedPlayer(dx, dy) {
if (!selectedPlayer) return;
var newX = selectedPlayer.x + dx;
var newY = selectedPlayer.y + dy;
// Keep within field bounds
newX = Math.max(100, Math.min(1948, newX));
newY = Math.max(200, Math.min(2500, newY)); // Allow movement to entire field
tween(selectedPlayer, {
x: newX,
y: newY
}, {
duration: 200,
easing: tween.easeOut
});
}
function makeBigger() {
// Make ball bigger
tween(ball, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 500,
easing: tween.easeOut
});
// Make all players bigger
for (var i = 0; i < players.length; i++) {
tween(players[i], {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 500,
easing: tween.easeOut
});
}
// Make all opponents bigger
for (var i = 0; i < opponents.length; i++) {
tween(opponents[i], {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 500,
easing: tween.easeOut
});
}
}
game.down = function (x, y, obj) {
if (!gameStarted) return;
// Check size up zone first
if (isInZone(x, y, sizeUpZone)) {
makeBigger();
return;
}
// If a player is selected, move them to the tapped location
if (selectedPlayer) {
// Allow movement anywhere on the field
var targetX = Math.max(100, Math.min(1948, x));
var targetY = Math.max(200, Math.min(2500, y)); // Allow movement to entire field
tween(selectedPlayer, {
x: targetX,
y: targetY
}, {
duration: 400,
easing: tween.easeOut
});
// Player kick is disabled
} else {
// Find and select closest player
var clickedPlayer = findClosestPlayer(x, y);
if (clickedPlayer) {
deselectAllPlayers();
clickedPlayer.select();
selectedPlayer = clickedPlayer;
}
}
};
game.move = function (x, y, obj) {
// Move function simplified - main control is now tap-to-move in down event
};
game.up = function (x, y, obj) {
// Up function simplified - main control is now tap-to-move
};
game.update = function () {
if (!gameStarted) return;
// Track lastY for goal detection
if (typeof ball.lastY === "undefined") ball.lastY = ball.y;
var prevBallY = ball.y;
ball.update();
ball.lastY = prevBallY;
// --- Ball possession and immunity logic ---
var now = Date.now();
var newHolder = null;
// If no one is holding the ball, allow anyone to take it by touching the ball
if (!ballHolder && now > globalImmunityUntil) {
// Check if any player touches the ball and can take it
for (var i = 0; i < players.length; i++) {
var player = players[i];
var dx = ball.x - player.x;
var dy = ball.y - player.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < 100) {
newHolder = player;
break;
}
}
// If no player, check if any opponent can take the ball
if (!newHolder) {
for (var i = 0; i < opponents.length; i++) {
var opponent = opponents[i];
var dx = ball.x - opponent.x;
var dy = ball.y - opponent.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < 100) {
newHolder = opponent;
break;
}
}
}
} else if (ballHolder && now > globalImmunityUntil) {
// If someone is holding the ball, check if another player (not the holder) touches the holder to steal the ball
// Check for player-to-player steal
if (ballHolder.team === 'player') {
for (var i = 0; i < players.length; i++) {
var otherPlayer = players[i];
if (otherPlayer !== ballHolder) {
var dx = ballHolder.x - otherPlayer.x;
var dy = ballHolder.y - otherPlayer.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < 100) {
newHolder = otherPlayer;
break;
}
}
}
}
// Check for opponent-to-opponent steal
if (!newHolder && ballHolder.team === 'opponent') {
for (var i = 0; i < opponents.length; i++) {
var otherOpponent = opponents[i];
if (otherOpponent !== ballHolder) {
var dx = ballHolder.x - otherOpponent.x;
var dy = ballHolder.y - otherOpponent.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < 100) {
newHolder = otherOpponent;
break;
}
}
}
}
// Check for opponent stealing from player
if (!newHolder && ballHolder.team === 'player') {
for (var i = 0; i < opponents.length; i++) {
var opponent = opponents[i];
var dx = ballHolder.x - opponent.x;
var dy = ballHolder.y - opponent.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < 100) {
newHolder = opponent;
break;
}
}
}
// Check for player stealing from opponent
if (!newHolder && ballHolder.team === 'opponent') {
for (var i = 0; i < players.length; i++) {
var player = players[i];
var dx = ballHolder.x - player.x;
var dy = ballHolder.y - player.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < 100) {
newHolder = player;
break;
}
}
}
}
// If someone new takes the ball
if (newHolder && newHolder !== ballHolder) {
// Set global immunity for everyone for 2 seconds
globalImmunityUntil = now + IMMUNITY_DURATION;
ballHolder = newHolder;
// Move ball to holder's position
ball.x = ballHolder.x;
ball.y = ballHolder.y;
ball.velocityX = 0;
ball.velocityY = 0;
// Player keeps the ball after gaining possession (do not drop)
}
// If someone is holding the ball, keep ball on them
if (ballHolder) {
ball.x = ballHolder.x;
ball.y = ballHolder.y;
}
// --- End ball possession and immunity logic ---
// Update opponents
for (var i = 0; i < opponents.length; i++) {
opponents[i].update();
}
// Update AI
if (LK.ticks % 30 === 0) {
// Update AI every half second
updateOpponentAI();
updatePlayerAI();
}
// Check for goals
checkGoal();
// Keep players within field bounds
for (var i = 0; i < players.length; i++) {
var player = players[i];
if (player.y < 200) {
player.y = 200;
}
if (player.y > 2500) {
player.y = 2500;
}
}
// Keep opponents within field bounds (allow anywhere on field)
for (var i = 0; i < opponents.length; i++) {
var opponent = opponents[i];
if (opponent.x < 100) {
opponent.x = 100;
}
if (opponent.x > 1948) {
opponent.x = 1948;
}
if (opponent.y < 200) {
opponent.y = 200;
}
if (opponent.y > 2500) {
opponent.y = 2500;
}
}
};