/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Ball class var Ball = Container.expand(function () { var self = Container.call(this); var ballSprite = self.attachAsset('ball', { anchorX: 0.5, anchorY: 0.5 }); self.radius = ballSprite.width / 2; self.vx = 0; self.vy = 0; self.maxSpeed = 60; self.friction = 0.96; self.owner = null; // If not null, ball is attached to player self.update = function () { if (self.owner) { // Stick to owner's feet var angle = self.owner.facing; self.x = self.owner.x + Math.cos(angle) * (self.owner.radius + self.radius + 10); self.y = self.owner.y + Math.sin(angle) * (self.owner.radius + self.radius + 10); self.vx = 0; self.vy = 0; } else { // Move ball self.x += self.vx; self.y += self.vy; self.vx *= self.friction; self.vy *= self.friction; if (Math.abs(self.vx) < 0.1) self.vx = 0; if (Math.abs(self.vy) < 0.1) self.vy = 0; } }; // Kick the ball in a direction self.kick = function (angle, power) { self.owner = null; self.vx = Math.cos(angle) * power; self.vy = Math.sin(angle) * power; if (Math.sqrt(self.vx * self.vx + self.vy * self.vy) > self.maxSpeed) { var mag = Math.sqrt(self.vx * self.vx + self.vy * self.vy); self.vx = self.vx / mag * self.maxSpeed; self.vy = self.vy / mag * self.maxSpeed; } LK.getSound('kick').play(); }; return self; }); // Defender class var Defender = Container.expand(function () { var self = Container.call(this); var defenderSprite = self.attachAsset('defender', { anchorX: 0.5, anchorY: 0.5 }); self.radius = defenderSprite.width / 2; self.speed = 18 + Math.random() * 8; self.facing = Math.PI / 2; self.target = null; // Ball or player self.cooldown = 0; // For tackle attempts self.update = function () { if (self.cooldown > 0) self.cooldown--; // Simple AI: move toward ball if not owned, else toward player var tx, ty; if (ball.owner) { tx = player.x; ty = player.y; } else { tx = ball.x; ty = ball.y; } var dx = tx - self.x; var dy = ty - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 1) { self.facing = Math.atan2(dy, dx); var moveDist = Math.min(self.speed, dist); self.x += Math.cos(self.facing) * moveDist; self.y += Math.sin(self.facing) * moveDist; } }; // Tackle attempt self.tryTackle = function () { if (self.cooldown > 0) return false; if (ball.owner === player) { var dx = self.x - player.x; var dy = self.y - player.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < self.radius + player.radius + 10) { // Successful tackle ball.owner = null; ball.vx = (Math.random() - 0.5) * 20; ball.vy = (Math.random() - 0.5) * 20; self.cooldown = 60; LK.getSound('tackle').play(); // Flash player red LK.effects.flashObject(player, 0xff0000, 400); return true; } } return false; }; return self; }); // Goalkeeper class var Goalkeeper = Container.expand(function () { var self = Container.call(this); var keeperSprite = self.attachAsset('goalkeeper', { anchorX: 0.5, anchorY: 0.5 }); self.radius = keeperSprite.width / 2; self.speed = 22; self.facing = -Math.PI / 2; self.goalX = 0; self.goalY = 0; self.range = 320; // How far left/right can move self.update = function () { // Track ball's y, but only move left/right var targetX = Math.max(goal.x - self.range, Math.min(goal.x + self.range, ball.x)); var dx = targetX - self.x; if (Math.abs(dx) > 2) { self.x += Math.sign(dx) * Math.min(self.speed, Math.abs(dx)); } // Try to block ball if close var dist = Math.sqrt((self.x - ball.x) * (self.x - ball.x) + (self.y - ball.y) * (self.y - ball.y)); if (dist < self.radius + ball.radius + 10 && !ball.owner) { // Block: stop ball, send it back ball.vx = -ball.vx * 0.7; ball.vy = -Math.abs(ball.vy) * 0.7; LK.effects.flashObject(self, 0x00ff00, 300); } }; return self; }); // Player class var Player = Container.expand(function () { var self = Container.call(this); var playerSprite = self.attachAsset('player', { anchorX: 0.5, anchorY: 0.5 }); self.radius = playerSprite.width / 2; self.speed = 32; self.facing = -Math.PI / 2; // Up self.hasBall = false; self.update = function () { // Facing is set by movement direction }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x006600 }); /**** * Game Code ****/ // Sound effects // Pitch outline // Goal area // Goalkeeper // Opponent (defender) // Ball // Player (footballer) // Pitch var pitch = LK.getAsset('pitch', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2 }); game.addChild(pitch); // Goal (top) var goal = LK.getAsset('goal', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 120 }); game.addChild(goal); // Goalkeeper var goalkeeper = new Goalkeeper(); goalkeeper.x = 2048 / 2; goalkeeper.y = goal.y + 80; game.addChild(goalkeeper); // Player var player = new Player(); player.x = 2048 / 2; player.y = 2732 - 400; game.addChild(player); // Ball var ball = new Ball(); ball.x = player.x; ball.y = player.y - player.radius - ball.radius - 10; ball.owner = player; game.addChild(ball); // Defenders var defenders = []; function spawnDefenders(level) { // Remove old defenders for (var i = 0; i < defenders.length; ++i) { defenders[i].destroy(); } defenders = []; // Place defenders in a line, with some random offset var n = 2 + Math.floor(level / 2); for (var i = 0; i < n; ++i) { var d = new Defender(); d.x = 2048 / 2 + (i - (n - 1) / 2) * 320 + (Math.random() - 0.5) * 60; d.y = 1100 + i * 80 + Math.random() * 60; defenders.push(d); game.addChild(d); } } var level = 1; spawnDefenders(level); // Score and timer var score = 0; var scoreTxt = new Text2('0', { size: 120, fill: 0xFFF700 }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); var timer = 60; // seconds var timerTxt = new Text2('1:00', { size: 90, fill: 0xFFFFFF }); timerTxt.anchor.set(0.5, 0); LK.gui.top.addChild(timerTxt); timerTxt.y = 120; // Touch controls var dragNode = null; var dragOffsetX = 0; var dragOffsetY = 0; var lastTouchX = 0; var lastTouchY = 0; var isKicking = false; var kickStartX = 0; var kickStartY = 0; var kickStartTime = 0; // Helper: clamp player inside pitch function clampPlayer() { var minX = pitch.x - pitch.width / 2 + player.radius + 20; var maxX = pitch.x + pitch.width / 2 - player.radius - 20; var minY = pitch.y - pitch.height / 2 + player.radius + 20; var maxY = pitch.y + pitch.height / 2 - player.radius - 20; player.x = Math.max(minX, Math.min(maxX, player.x)); player.y = Math.max(minY, Math.min(maxY, player.y)); } // Helper: clamp ball inside pitch function clampBall() { var minX = pitch.x - pitch.width / 2 + ball.radius + 10; var maxX = pitch.x + pitch.width / 2 - ball.radius - 10; var minY = pitch.y - pitch.height / 2 + ball.radius + 10; var maxY = pitch.y + pitch.height / 2 - ball.radius - 10; ball.x = Math.max(minX, Math.min(maxX, ball.x)); ball.y = Math.max(minY, Math.min(maxY, ball.y)); } // Move handler: drag to move player, swipe to kick function handleMove(x, y, obj) { lastTouchX = x; lastTouchY = y; if (dragNode === player) { // Move player toward touch, but limit speed var dx = x - player.x; var dy = y - player.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 0) { var moveDist = Math.min(player.speed, dist); player.x += dx / dist * moveDist; player.y += dy / dist * moveDist; player.facing = Math.atan2(dy, dx); } clampPlayer(); } } game.move = handleMove; game.down = function (x, y, obj) { // If touch is near player, start drag var dx = x - player.x; var dy = y - player.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < player.radius + 60) { dragNode = player; kickStartX = x; kickStartY = y; kickStartTime = LK.ticks; isKicking = false; } }; game.up = function (x, y, obj) { if (dragNode === player) { // If swipe is fast and long enough, kick var dx = x - kickStartX; var dy = y - kickStartY; var dist = Math.sqrt(dx * dx + dy * dy); var dt = LK.ticks - kickStartTime; if (dist > 80 && dt < 40 && ball.owner === player) { // Kick in swipe direction, power based on swipe length var angle = Math.atan2(dy, dx); var power = Math.min(60, dist / 2 + 18); ball.kick(angle, power); isKicking = true; } } dragNode = null; }; // Timer logic var timerInterval = LK.setInterval(function () { if (timer > 0) { timer--; var min = Math.floor(timer / 60); var sec = timer % 60; timerTxt.setText(min + ":" + (sec < 10 ? "0" : "") + sec); if (timer === 0) { LK.getSound('whistle').play(); LK.showGameOver(); } } }, 1000); // Goal detection function checkGoal() { // Ball inside goal area (top) if (!ball.owner && ball.y - ball.radius < goal.y + goal.height / 2 && ball.y + ball.radius > goal.y - goal.height / 2 && ball.x > goal.x - goal.width / 2 + 40 && ball.x < goal.x + goal.width / 2 - 40) { // Score! score++; LK.setScore(score); scoreTxt.setText(score); LK.getSound('goal').play(); LK.effects.flashScreen(0xffff00, 600); // Reset positions resetPositions(); // Increase difficulty every 2 goals if (score % 2 === 0) { level++; spawnDefenders(level); } } } // Reset player, ball, keeper positions function resetPositions() { player.x = 2048 / 2; player.y = 2732 - 400; player.facing = -Math.PI / 2; ball.x = player.x; ball.y = player.y - player.radius - ball.radius - 10; ball.owner = player; ball.vx = 0; ball.vy = 0; goalkeeper.x = 2048 / 2; goalkeeper.y = goal.y + 80; for (var i = 0; i < defenders.length; ++i) { defenders[i].x = 2048 / 2 + (i - (defenders.length - 1) / 2) * 320 + (Math.random() - 0.5) * 60; defenders[i].y = 1100 + i * 80 + Math.random() * 60; defenders[i].cooldown = 0; } } // Main update loop game.update = function () { // Update player player.update(); // Update ball ball.update(); clampBall(); // Ball pickup if (!ball.owner) { var dx = player.x - ball.x; var dy = player.y - ball.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < player.radius + ball.radius + 10) { ball.owner = player; } } // Update defenders for (var i = 0; i < defenders.length; ++i) { defenders[i].update(); defenders[i].tryTackle(); } // Update goalkeeper goalkeeper.update(); // Prevent player/defender overlap for (var i = 0; i < defenders.length; ++i) { var d = defenders[i]; var dx = d.x - player.x; var dy = d.y - player.y; var dist = Math.sqrt(dx * dx + dy * dy); var minDist = d.radius + player.radius + 8; if (dist < minDist) { var overlap = minDist - dist; if (dist > 0) { d.x += dx / dist * overlap / 2; d.y += dy / dist * overlap / 2; player.x -= dx / dist * overlap / 2; player.y -= dy / dist * overlap / 2; clampPlayer(); } } } // Prevent defenders from overlapping each other for (var i = 0; i < defenders.length; ++i) { for (var j = i + 1; j < defenders.length; ++j) { var d1 = defenders[i]; var d2 = defenders[j]; var dx = d2.x - d1.x; var dy = d2.y - d1.y; var dist = Math.sqrt(dx * dx + dy * dy); var minDist = d1.radius + d2.radius + 4; if (dist < minDist && dist > 0) { var overlap = minDist - dist; d1.x -= dx / dist * overlap / 2; d1.y -= dy / dist * overlap / 2; d2.x += dx / dist * overlap / 2; d2.y += dy / dist * overlap / 2; } } } // Prevent ball from sticking to defenders for (var i = 0; i < defenders.length; ++i) { var d = defenders[i]; var dx = d.x - ball.x; var dy = d.y - ball.y; var dist = Math.sqrt(dx * dx + dy * dy); var minDist = d.radius + ball.radius + 2; if (dist < minDist && !ball.owner) { var overlap = minDist - dist; if (dist > 0) { ball.x -= dx / dist * overlap / 2; ball.y -= dy / dist * overlap / 2; d.x += dx / dist * overlap / 2; d.y += dy / dist * overlap / 2; } } } // Prevent player from entering goal area var goalAreaY = goal.y + goal.height / 2 + player.radius + 10; if (player.y - player.radius < goalAreaY) { player.y = goalAreaY + player.radius; } // Check for goal checkGoal(); };
===================================================================
--- original.js
+++ change.js
@@ -1,6 +1,453 @@
-/****
+/****
+* Plugins
+****/
+var tween = LK.import("@upit/tween.v1");
+
+/****
+* Classes
+****/
+// Ball class
+var Ball = Container.expand(function () {
+ var self = Container.call(this);
+ var ballSprite = self.attachAsset('ball', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.radius = ballSprite.width / 2;
+ self.vx = 0;
+ self.vy = 0;
+ self.maxSpeed = 60;
+ self.friction = 0.96;
+ self.owner = null; // If not null, ball is attached to player
+ self.update = function () {
+ if (self.owner) {
+ // Stick to owner's feet
+ var angle = self.owner.facing;
+ self.x = self.owner.x + Math.cos(angle) * (self.owner.radius + self.radius + 10);
+ self.y = self.owner.y + Math.sin(angle) * (self.owner.radius + self.radius + 10);
+ self.vx = 0;
+ self.vy = 0;
+ } else {
+ // Move ball
+ self.x += self.vx;
+ self.y += self.vy;
+ self.vx *= self.friction;
+ self.vy *= self.friction;
+ if (Math.abs(self.vx) < 0.1) self.vx = 0;
+ if (Math.abs(self.vy) < 0.1) self.vy = 0;
+ }
+ };
+ // Kick the ball in a direction
+ self.kick = function (angle, power) {
+ self.owner = null;
+ self.vx = Math.cos(angle) * power;
+ self.vy = Math.sin(angle) * power;
+ if (Math.sqrt(self.vx * self.vx + self.vy * self.vy) > self.maxSpeed) {
+ var mag = Math.sqrt(self.vx * self.vx + self.vy * self.vy);
+ self.vx = self.vx / mag * self.maxSpeed;
+ self.vy = self.vy / mag * self.maxSpeed;
+ }
+ LK.getSound('kick').play();
+ };
+ return self;
+});
+// Defender class
+var Defender = Container.expand(function () {
+ var self = Container.call(this);
+ var defenderSprite = self.attachAsset('defender', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.radius = defenderSprite.width / 2;
+ self.speed = 18 + Math.random() * 8;
+ self.facing = Math.PI / 2;
+ self.target = null; // Ball or player
+ self.cooldown = 0; // For tackle attempts
+ self.update = function () {
+ if (self.cooldown > 0) self.cooldown--;
+ // Simple AI: move toward ball if not owned, else toward player
+ var tx, ty;
+ if (ball.owner) {
+ tx = player.x;
+ ty = player.y;
+ } else {
+ tx = ball.x;
+ ty = ball.y;
+ }
+ var dx = tx - self.x;
+ var dy = ty - self.y;
+ var dist = Math.sqrt(dx * dx + dy * dy);
+ if (dist > 1) {
+ self.facing = Math.atan2(dy, dx);
+ var moveDist = Math.min(self.speed, dist);
+ self.x += Math.cos(self.facing) * moveDist;
+ self.y += Math.sin(self.facing) * moveDist;
+ }
+ };
+ // Tackle attempt
+ self.tryTackle = function () {
+ if (self.cooldown > 0) return false;
+ if (ball.owner === player) {
+ var dx = self.x - player.x;
+ var dy = self.y - player.y;
+ var dist = Math.sqrt(dx * dx + dy * dy);
+ if (dist < self.radius + player.radius + 10) {
+ // Successful tackle
+ ball.owner = null;
+ ball.vx = (Math.random() - 0.5) * 20;
+ ball.vy = (Math.random() - 0.5) * 20;
+ self.cooldown = 60;
+ LK.getSound('tackle').play();
+ // Flash player red
+ LK.effects.flashObject(player, 0xff0000, 400);
+ return true;
+ }
+ }
+ return false;
+ };
+ return self;
+});
+// Goalkeeper class
+var Goalkeeper = Container.expand(function () {
+ var self = Container.call(this);
+ var keeperSprite = self.attachAsset('goalkeeper', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.radius = keeperSprite.width / 2;
+ self.speed = 22;
+ self.facing = -Math.PI / 2;
+ self.goalX = 0;
+ self.goalY = 0;
+ self.range = 320; // How far left/right can move
+ self.update = function () {
+ // Track ball's y, but only move left/right
+ var targetX = Math.max(goal.x - self.range, Math.min(goal.x + self.range, ball.x));
+ var dx = targetX - self.x;
+ if (Math.abs(dx) > 2) {
+ self.x += Math.sign(dx) * Math.min(self.speed, Math.abs(dx));
+ }
+ // Try to block ball if close
+ var dist = Math.sqrt((self.x - ball.x) * (self.x - ball.x) + (self.y - ball.y) * (self.y - ball.y));
+ if (dist < self.radius + ball.radius + 10 && !ball.owner) {
+ // Block: stop ball, send it back
+ ball.vx = -ball.vx * 0.7;
+ ball.vy = -Math.abs(ball.vy) * 0.7;
+ LK.effects.flashObject(self, 0x00ff00, 300);
+ }
+ };
+ return self;
+});
+// Player class
+var Player = Container.expand(function () {
+ var self = Container.call(this);
+ var playerSprite = self.attachAsset('player', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.radius = playerSprite.width / 2;
+ self.speed = 32;
+ self.facing = -Math.PI / 2; // Up
+ self.hasBall = false;
+ self.update = function () {
+ // Facing is set by movement direction
+ };
+ return self;
+});
+
+/****
* Initialize Game
-****/
+****/
var game = new LK.Game({
- backgroundColor: 0x000000
-});
\ No newline at end of file
+ backgroundColor: 0x006600
+});
+
+/****
+* Game Code
+****/
+// Sound effects
+// Pitch outline
+// Goal area
+// Goalkeeper
+// Opponent (defender)
+// Ball
+// Player (footballer)
+// Pitch
+var pitch = LK.getAsset('pitch', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ x: 2048 / 2,
+ y: 2732 / 2
+});
+game.addChild(pitch);
+// Goal (top)
+var goal = LK.getAsset('goal', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ x: 2048 / 2,
+ y: 120
+});
+game.addChild(goal);
+// Goalkeeper
+var goalkeeper = new Goalkeeper();
+goalkeeper.x = 2048 / 2;
+goalkeeper.y = goal.y + 80;
+game.addChild(goalkeeper);
+// Player
+var player = new Player();
+player.x = 2048 / 2;
+player.y = 2732 - 400;
+game.addChild(player);
+// Ball
+var ball = new Ball();
+ball.x = player.x;
+ball.y = player.y - player.radius - ball.radius - 10;
+ball.owner = player;
+game.addChild(ball);
+// Defenders
+var defenders = [];
+function spawnDefenders(level) {
+ // Remove old defenders
+ for (var i = 0; i < defenders.length; ++i) {
+ defenders[i].destroy();
+ }
+ defenders = [];
+ // Place defenders in a line, with some random offset
+ var n = 2 + Math.floor(level / 2);
+ for (var i = 0; i < n; ++i) {
+ var d = new Defender();
+ d.x = 2048 / 2 + (i - (n - 1) / 2) * 320 + (Math.random() - 0.5) * 60;
+ d.y = 1100 + i * 80 + Math.random() * 60;
+ defenders.push(d);
+ game.addChild(d);
+ }
+}
+var level = 1;
+spawnDefenders(level);
+// Score and timer
+var score = 0;
+var scoreTxt = new Text2('0', {
+ size: 120,
+ fill: 0xFFF700
+});
+scoreTxt.anchor.set(0.5, 0);
+LK.gui.top.addChild(scoreTxt);
+var timer = 60; // seconds
+var timerTxt = new Text2('1:00', {
+ size: 90,
+ fill: 0xFFFFFF
+});
+timerTxt.anchor.set(0.5, 0);
+LK.gui.top.addChild(timerTxt);
+timerTxt.y = 120;
+// Touch controls
+var dragNode = null;
+var dragOffsetX = 0;
+var dragOffsetY = 0;
+var lastTouchX = 0;
+var lastTouchY = 0;
+var isKicking = false;
+var kickStartX = 0;
+var kickStartY = 0;
+var kickStartTime = 0;
+// Helper: clamp player inside pitch
+function clampPlayer() {
+ var minX = pitch.x - pitch.width / 2 + player.radius + 20;
+ var maxX = pitch.x + pitch.width / 2 - player.radius - 20;
+ var minY = pitch.y - pitch.height / 2 + player.radius + 20;
+ var maxY = pitch.y + pitch.height / 2 - player.radius - 20;
+ player.x = Math.max(minX, Math.min(maxX, player.x));
+ player.y = Math.max(minY, Math.min(maxY, player.y));
+}
+// Helper: clamp ball inside pitch
+function clampBall() {
+ var minX = pitch.x - pitch.width / 2 + ball.radius + 10;
+ var maxX = pitch.x + pitch.width / 2 - ball.radius - 10;
+ var minY = pitch.y - pitch.height / 2 + ball.radius + 10;
+ var maxY = pitch.y + pitch.height / 2 - ball.radius - 10;
+ ball.x = Math.max(minX, Math.min(maxX, ball.x));
+ ball.y = Math.max(minY, Math.min(maxY, ball.y));
+}
+// Move handler: drag to move player, swipe to kick
+function handleMove(x, y, obj) {
+ lastTouchX = x;
+ lastTouchY = y;
+ if (dragNode === player) {
+ // Move player toward touch, but limit speed
+ var dx = x - player.x;
+ var dy = y - player.y;
+ var dist = Math.sqrt(dx * dx + dy * dy);
+ if (dist > 0) {
+ var moveDist = Math.min(player.speed, dist);
+ player.x += dx / dist * moveDist;
+ player.y += dy / dist * moveDist;
+ player.facing = Math.atan2(dy, dx);
+ }
+ clampPlayer();
+ }
+}
+game.move = handleMove;
+game.down = function (x, y, obj) {
+ // If touch is near player, start drag
+ var dx = x - player.x;
+ var dy = y - player.y;
+ var dist = Math.sqrt(dx * dx + dy * dy);
+ if (dist < player.radius + 60) {
+ dragNode = player;
+ kickStartX = x;
+ kickStartY = y;
+ kickStartTime = LK.ticks;
+ isKicking = false;
+ }
+};
+game.up = function (x, y, obj) {
+ if (dragNode === player) {
+ // If swipe is fast and long enough, kick
+ var dx = x - kickStartX;
+ var dy = y - kickStartY;
+ var dist = Math.sqrt(dx * dx + dy * dy);
+ var dt = LK.ticks - kickStartTime;
+ if (dist > 80 && dt < 40 && ball.owner === player) {
+ // Kick in swipe direction, power based on swipe length
+ var angle = Math.atan2(dy, dx);
+ var power = Math.min(60, dist / 2 + 18);
+ ball.kick(angle, power);
+ isKicking = true;
+ }
+ }
+ dragNode = null;
+};
+// Timer logic
+var timerInterval = LK.setInterval(function () {
+ if (timer > 0) {
+ timer--;
+ var min = Math.floor(timer / 60);
+ var sec = timer % 60;
+ timerTxt.setText(min + ":" + (sec < 10 ? "0" : "") + sec);
+ if (timer === 0) {
+ LK.getSound('whistle').play();
+ LK.showGameOver();
+ }
+ }
+}, 1000);
+// Goal detection
+function checkGoal() {
+ // Ball inside goal area (top)
+ if (!ball.owner && ball.y - ball.radius < goal.y + goal.height / 2 && ball.y + ball.radius > goal.y - goal.height / 2 && ball.x > goal.x - goal.width / 2 + 40 && ball.x < goal.x + goal.width / 2 - 40) {
+ // Score!
+ score++;
+ LK.setScore(score);
+ scoreTxt.setText(score);
+ LK.getSound('goal').play();
+ LK.effects.flashScreen(0xffff00, 600);
+ // Reset positions
+ resetPositions();
+ // Increase difficulty every 2 goals
+ if (score % 2 === 0) {
+ level++;
+ spawnDefenders(level);
+ }
+ }
+}
+// Reset player, ball, keeper positions
+function resetPositions() {
+ player.x = 2048 / 2;
+ player.y = 2732 - 400;
+ player.facing = -Math.PI / 2;
+ ball.x = player.x;
+ ball.y = player.y - player.radius - ball.radius - 10;
+ ball.owner = player;
+ ball.vx = 0;
+ ball.vy = 0;
+ goalkeeper.x = 2048 / 2;
+ goalkeeper.y = goal.y + 80;
+ for (var i = 0; i < defenders.length; ++i) {
+ defenders[i].x = 2048 / 2 + (i - (defenders.length - 1) / 2) * 320 + (Math.random() - 0.5) * 60;
+ defenders[i].y = 1100 + i * 80 + Math.random() * 60;
+ defenders[i].cooldown = 0;
+ }
+}
+// Main update loop
+game.update = function () {
+ // Update player
+ player.update();
+ // Update ball
+ ball.update();
+ clampBall();
+ // Ball pickup
+ if (!ball.owner) {
+ var dx = player.x - ball.x;
+ var dy = player.y - ball.y;
+ var dist = Math.sqrt(dx * dx + dy * dy);
+ if (dist < player.radius + ball.radius + 10) {
+ ball.owner = player;
+ }
+ }
+ // Update defenders
+ for (var i = 0; i < defenders.length; ++i) {
+ defenders[i].update();
+ defenders[i].tryTackle();
+ }
+ // Update goalkeeper
+ goalkeeper.update();
+ // Prevent player/defender overlap
+ for (var i = 0; i < defenders.length; ++i) {
+ var d = defenders[i];
+ var dx = d.x - player.x;
+ var dy = d.y - player.y;
+ var dist = Math.sqrt(dx * dx + dy * dy);
+ var minDist = d.radius + player.radius + 8;
+ if (dist < minDist) {
+ var overlap = minDist - dist;
+ if (dist > 0) {
+ d.x += dx / dist * overlap / 2;
+ d.y += dy / dist * overlap / 2;
+ player.x -= dx / dist * overlap / 2;
+ player.y -= dy / dist * overlap / 2;
+ clampPlayer();
+ }
+ }
+ }
+ // Prevent defenders from overlapping each other
+ for (var i = 0; i < defenders.length; ++i) {
+ for (var j = i + 1; j < defenders.length; ++j) {
+ var d1 = defenders[i];
+ var d2 = defenders[j];
+ var dx = d2.x - d1.x;
+ var dy = d2.y - d1.y;
+ var dist = Math.sqrt(dx * dx + dy * dy);
+ var minDist = d1.radius + d2.radius + 4;
+ if (dist < minDist && dist > 0) {
+ var overlap = minDist - dist;
+ d1.x -= dx / dist * overlap / 2;
+ d1.y -= dy / dist * overlap / 2;
+ d2.x += dx / dist * overlap / 2;
+ d2.y += dy / dist * overlap / 2;
+ }
+ }
+ }
+ // Prevent ball from sticking to defenders
+ for (var i = 0; i < defenders.length; ++i) {
+ var d = defenders[i];
+ var dx = d.x - ball.x;
+ var dy = d.y - ball.y;
+ var dist = Math.sqrt(dx * dx + dy * dy);
+ var minDist = d.radius + ball.radius + 2;
+ if (dist < minDist && !ball.owner) {
+ var overlap = minDist - dist;
+ if (dist > 0) {
+ ball.x -= dx / dist * overlap / 2;
+ ball.y -= dy / dist * overlap / 2;
+ d.x += dx / dist * overlap / 2;
+ d.y += dy / dist * overlap / 2;
+ }
+ }
+ }
+ // Prevent player from entering goal area
+ var goalAreaY = goal.y + goal.height / 2 + player.radius + 10;
+ if (player.y - player.radius < goalAreaY) {
+ player.y = goalAreaY + player.radius;
+ }
+ // Check for goal
+ checkGoal();
+};
\ No newline at end of file