/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.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.gravity = 0; self.friction = 0.98; self.isMoving = false; self.spinForce = 0; self.spinDecay = 0.98; self.shoot = function (power, angle, spin) { self.velocityX = Math.cos(angle) * power; self.velocityY = Math.sin(angle) * power; self.spinForce = spin || 0; self.isMoving = true; LK.getSound('kick').play(); }; self.reset = function () { self.x = ballStartX; self.y = ballStartY; self.velocityX = 0; self.velocityY = 0; self.isMoving = false; self.spinForce = 0; }; self.update = function () { if (self.isMoving) { // Apply spin curve effect if (self.spinForce !== 0) { // Calculate perpendicular force to current velocity for curve var speed = Math.sqrt(self.velocityX * self.velocityX + self.velocityY * self.velocityY); if (speed > 0) { var normalX = -self.velocityY / speed; var normalY = self.velocityX / speed; self.velocityX += normalX * self.spinForce * 0.1; self.velocityY += normalY * self.spinForce * 0.1; } // Decay spin over time self.spinForce *= self.spinDecay; } self.x += self.velocityX; self.y += self.velocityY; // No friction applied - ball maintains constant velocity // Bounds checking if (self.x < 0 || self.x > 2048 || self.y > 2732) { self.isMoving = false; ballMissed(); } } }; return self; }); var Defender = Container.expand(function () { var self = Container.call(this); var defenderGraphics = self.attachAsset('defender', { anchorX: 0.5, anchorY: 1.0 }); return self; }); var Goalkeeper = Container.expand(function () { var self = Container.call(this); var keeperGraphics = self.attachAsset('goalkeeper', { anchorX: 0.5, anchorY: 1.0 }); return self; }); var TrajectoryDot = Container.expand(function () { var self = Container.call(this); var dotGraphics = self.attachAsset('trajectory', { anchorX: 0.5, anchorY: 0.5 }); dotGraphics.alpha = 0.6; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x7DD87D }); /**** * Game Code ****/ // Game variables var currentRound = 1; var attemptsLeft = 3; var ballStartX = 1024; var ballStartY = 2500; var goalX = 1024; var goalY = 400; var isAiming = false; var dragStartX = 0; var dragStartY = 0; var dragCurrentX = 0; var dragCurrentY = 0; var trajectoryDots = []; var defenders = []; // Player name and leaderboard variables var playerName = ''; var nameEntered = false; var nameInputText = ''; var alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; var currentLetterIndex = 0; var leaderboardNames = storage.leaderboardNames || []; var leaderboardScores = storage.leaderboardScores || []; var showingLeaderboard = false; // Swipe tracking variables var swipePath = []; var swipeStartTime = 0; var swipeSpeed = 0; var swipeCurve = 0; var maxSwipePoints = 10; // Create grass field var grass = game.addChild(LK.getAsset('grass', { anchorX: 0, anchorY: 0, x: 0, y: 0 })); // Create goal structure var goal = game.addChild(LK.getAsset('goal', { anchorX: 0.5, anchorY: 0.5, x: goalX, y: goalY + 100 })); goal.alpha = 0.3; // Make goal semi-transparent so we can see the ball // Create goalkeeper var goalkeeper = game.addChild(new Goalkeeper()); goalkeeper.x = goalX; goalkeeper.y = goalY + 450; // Move goalkeeper in front of the goal // Store goalkeeper's initial position for reset var goalkeeperInitialX = goalkeeper.x; var goalkeeperInitialY = goalkeeper.y; // Create ball var ball = game.addChild(new Ball()); ball.reset(); // UI Elements var roundText = new Text2('Round: 1', { size: 100, fill: 0xFFFFFF }); roundText.anchor.set(0.5, 0); LK.gui.top.addChild(roundText); var attemptsText = new Text2('Attempts: 3', { size: 75, fill: 0xFFFFFF }); attemptsText.anchor.set(1.0, 0); LK.gui.topRight.addChild(attemptsText); var personalBest = storage.personalBest || 0; var personalBestText = new Text2('Best: ' + personalBest, { size: 75, fill: 0x00FF00 }); personalBestText.anchor.set(0, 0); LK.gui.topLeft.addChild(personalBestText); personalBestText.x = 120; // Move away from platform menu icon var powerText = new Text2('', { size: 62, fill: 0xFFFF00 }); powerText.anchor.set(0.5, 0.5); game.addChild(powerText); var curveText = new Text2('Spin: 0', { size: 75, fill: 0xFFFFFF }); curveText.anchor.set(0.5, 0.5); curveText.x = 1024; curveText.y = 200; game.addChild(curveText); var selectedCurve = 0; var maxCurve = 20; var curveValues = [-20, -18, -16, -14, -12, -10, -8, -6, -4, -2, -1, 0, 1, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20]; var curveIndex = 11; // Start at 0 curve (index 11 in the new array) var fixedSpeed = 35; // Create curve adjustment buttons var leftCurveButton = new Text2('<', { size: 125, fill: 0xFFFF00 }); leftCurveButton.anchor.set(0.5, 0.5); leftCurveButton.x = 850; leftCurveButton.y = 200; game.addChild(leftCurveButton); var rightCurveButton = new Text2('>', { size: 125, fill: 0xFFFF00 }); rightCurveButton.anchor.set(0.5, 0.5); rightCurveButton.x = 1198; rightCurveButton.y = 200; game.addChild(rightCurveButton); // Function to update curve display function updateCurveDisplay() { selectedCurve = curveValues[curveIndex]; if (selectedCurve === 0) { curveText.setText('Spin: 0'); } else { curveText.setText('Spin: ' + (selectedCurve > 0 ? '+' : '') + selectedCurve); } console.log('Curve updated to:', selectedCurve, 'Index:', curveIndex); } // Initialize curve display updateCurveDisplay(); // Initialize defenders for current round function setupRound() { // Clear existing defenders for (var i = 0; i < defenders.length; i++) { defenders[i].destroy(); } defenders = []; // Add defenders based on round var numDefenders = Math.min(currentRound - 1, 4); for (var i = 0; i < numDefenders; i++) { var defender = game.addChild(new Defender()); if (i < 2) { // First 2 defenders stay side by side in fixed positions var spacing = 120; var startX = goalX - spacing / 2; defender.x = startX + i * spacing; defender.y = goalY + 450 + currentRound * 30; } else { // Other 2 defenders are placed randomly var randomX = goalX + (Math.random() - 0.5) * 600; // Random X within a reasonable area var randomY = goalY + 400 + Math.random() * 200; // Random Y in front of goal defender.x = randomX; defender.y = randomY; } defenders.push(defender); } // Starting from round 10, add 1 random defender in front of the goal if (currentRound >= 10) { var randomDefender = game.addChild(new Defender()); // Random position in front of goal area var randomX = goalX + (Math.random() - 0.5) * 800; // Random X within goal width area var randomY = goalY + 350 + Math.random() * 150; // Random Y in front of goal randomDefender.x = randomX; randomDefender.y = randomY; defenders.push(randomDefender); } roundText.setText('Round: ' + currentRound); attemptsText.setText('Attempts: ' + attemptsLeft); } // Create trajectory preview function createTrajectoryPreview(power, angle, spin) { // Clear existing dots for (var i = 0; i < trajectoryDots.length; i++) { trajectoryDots[i].destroy(); } trajectoryDots = []; // Calculate trajectory points var steps = 15; var stepTime = 0.8; var tempVelX = Math.cos(angle) * power * 0.7; var tempVelY = Math.sin(angle) * power * 0.7; var tempX = ball.x; var tempY = ball.y; var tempSpin = spin || 0; for (var i = 0; i < steps; i++) { // Apply spin curve effect in preview if (tempSpin !== 0) { var speed = Math.sqrt(tempVelX * tempVelX + tempVelY * tempVelY); if (speed > 0) { var normalX = -tempVelY / speed; var normalY = tempVelX / speed; tempVelX += normalX * tempSpin * 0.1; tempVelY += normalY * tempSpin * 0.1; } tempSpin *= 0.98; } tempX += tempVelX * stepTime; tempY += tempVelY * stepTime; // No gravity applied to trajectory preview tempVelX *= 0.98; tempVelY *= 0.98; if (tempY > 2732 || tempX < 0 || tempX > 2048) break; var dot = game.addChild(new TrajectoryDot()); dot.x = tempX; dot.y = tempY; trajectoryDots.push(dot); } } // Clear trajectory preview function clearTrajectoryPreview() { for (var i = 0; i < trajectoryDots.length; i++) { trajectoryDots[i].destroy(); } trajectoryDots = []; } // Calculate curve from swipe path function calculateSwipeCurve() { if (swipePath.length < 3) return 0; var totalCurvature = 0; var validSegments = 0; var curvatureSum = 0; var pathLength = 0; // Calculate curvature at each point in the path for (var i = 1; i < swipePath.length - 1; i++) { var p1 = swipePath[i - 1]; var p2 = swipePath[i]; var p3 = swipePath[i + 1]; // Calculate vectors var v1x = p2.x - p1.x; var v1y = p2.y - p1.y; var v2x = p3.x - p2.x; var v2y = p3.y - p2.y; // Calculate cross product to determine curve direction var crossProduct = v1x * v2y - v1y * v2x; // Calculate magnitudes var mag1 = Math.sqrt(v1x * v1x + v1y * v1y); var mag2 = Math.sqrt(v2x * v2x + v2y * v2y); if (mag1 > 5 && mag2 > 5) { // Only consider significant movements curvatureSum += crossProduct / (mag1 * mag2); validSegments++; pathLength += mag1; } } if (validSegments > 0) { totalCurvature = curvatureSum / validSegments; // Scale based on path length and speed var lengthFactor = Math.min(pathLength / 200, 2); var speedFactor = Math.min(swipeSpeed / 500, 2); var finalCurve = totalCurvature * lengthFactor * speedFactor * 15; return Math.max(-10, Math.min(10, finalCurve)); } return 0; } // Calculate swipe speed function calculateSwipeSpeed() { if (swipePath.length < 2) return 0; var totalDistance = 0; var totalTime = 0; for (var i = 1; i < swipePath.length; i++) { var p1 = swipePath[i - 1]; var p2 = swipePath[i]; var dx = p2.x - p1.x; var dy = p2.y - p1.y; totalDistance += Math.sqrt(dx * dx + dy * dy); totalTime += p2.time - p1.time; } return totalTime > 0 ? totalDistance / totalTime * 1000 : 0; } // Ball collision detection function checkCollisions() { // Check goalkeeper collision first - this prevents scoring if (ball.intersects(goalkeeper)) { // Enhanced goalkeeper save logic based on ball position relative to goal var goalLeft = goalX - 400; // Left side of goal var goalRight = goalX + 400; // Right side of goal var goalWidth = 800; // Total goal width var goalCenter = goalX; // Goal center X position var distanceFromCenter = Math.abs(ball.x - goalCenter); var distanceFromGoalEdge = Math.min(Math.abs(ball.x - goalLeft), Math.abs(ball.x - goalRight)); var ballSpeed = Math.sqrt(ball.velocityX * ball.velocityX + ball.velocityY * ball.velocityY); // Calculate save chance based on position in goal var saveChance = 0; // Define middle zone (center 60% of goal) and corner zones (outer 20% each side) var middleZoneWidth = goalWidth * 0.6; // 480 pixels from center var cornerZoneWidth = goalWidth * 0.2; // 160 pixels each corner if (distanceFromCenter <= middleZoneWidth / 2) { // Ball is in the middle zone - high save chance saveChance = 0.95; // 95% save chance in middle } else if (distanceFromGoalEdge <= cornerZoneWidth) { // Ball is in corner zone - very low save chance saveChance = 0.1; // Only 10% save chance in corners } else { // Ball is in transition zone between middle and corner saveChance = 0.4; // Moderate save chance in transition } // Adjust save chance based on ball speed (faster balls harder to save) if (ballSpeed > 30) saveChance -= 0.2; if (ballSpeed > 40) saveChance -= 0.2; // Ensure save chance stays within reasonable bounds saveChance = Math.max(0.05, Math.min(0.98, saveChance)); // Random save attempt - increased base save chances var adjustedSaveChance = saveChance; // Boost save chances significantly if (distanceFromCenter <= middleZoneWidth / 2) { // Ball is in the middle zone - very high save chance adjustedSaveChance = 0.85; // 85% save chance in middle } else if (distanceFromGoalEdge <= cornerZoneWidth) { // Ball is in corner zone - still some save chance adjustedSaveChance = 0.25; // 25% save chance in corners } else { // Ball is in transition zone adjustedSaveChance = 0.55; // 55% save chance in transition } // Adjust save chance based on ball speed (faster balls harder to save) if (ballSpeed > 30) adjustedSaveChance -= 0.15; if (ballSpeed > 40) adjustedSaveChance -= 0.15; // Ensure save chance stays within reasonable bounds adjustedSaveChance = Math.max(0.15, Math.min(0.95, adjustedSaveChance)); console.log('Save attempt - Ball X:', ball.x, 'Distance from center:', distanceFromCenter, 'Save chance:', adjustedSaveChance); if (Math.random() < adjustedSaveChance) { // Successful save console.log('GOALKEEPER SAVE!'); ball.isMoving = false; LK.getSound('save').play(); LK.effects.flashObject(goalkeeper, 0x00ff00, 800); // Green flash for save LK.effects.flashScreen(0x00ff00, 300); // Screen flash for dramatic effect // Animate goalkeeper dive with tween - more dramatic tween(goalkeeper, { scaleX: 1.5, scaleY: 0.7, rotation: ball.x > goalkeeper.x ? 0.3 : -0.3 // Dive towards ball }, { duration: 400, easing: tween.easeOut, onFinish: function onFinish() { tween(goalkeeper, { scaleX: 1.0, scaleY: 1.0, rotation: 0 }, { duration: 300, onFinish: function onFinish() { resetGoalkeeper(); // Reset goalkeeper position after dive animation } }); } }); ballMissed(); return; } else { // Goalkeeper fails to save - ball continues console.log('Goalkeeper missed the save'); LK.effects.flashObject(goalkeeper, 0xff6666, 300); } } // Check goal scoring - ball must be inside goal area including side lines // Goal asset is positioned at goalY + 100 and has height 584.38, width 800 var goalLeft = goalX - 400; // Left side line of goal (800/2 = 400) var goalRight = goalX + 400; // Right side line of goal var goalTop = goalY + 100 - 292; // Top of goal (goalY + 100 - height/2) var goalBottom = goalY + 100 + 292; // Bottom of goal (goalY + 100 + height/2) if (ball.x >= goalLeft && ball.x <= goalRight && ball.y >= goalTop && ball.y <= goalBottom) { // Goal scored! Ball touched inside goal area or side lines ball.isMoving = false; LK.getSound('goal').play(); LK.effects.flashScreen(0x00ff00, 500); LK.setScore(LK.getScore() + 1); // Check and update personal best var currentScore = LK.getScore(); if (currentScore > personalBest) { personalBest = currentScore; storage.personalBest = personalBest; personalBestText.setText('Best: ' + personalBest); LK.effects.flashObject(personalBestText, 0xFFFF00, 1000); } currentRound++; // Reset attempts for next round attemptsLeft = 3; LK.setTimeout(function () { ball.reset(); resetGoalkeeper(); // Reset goalkeeper position setupRound(); }, 1000); return; } // Check defender collisions for (var i = 0; i < defenders.length; i++) { if (ball.intersects(defenders[i])) { ball.isMoving = false; LK.effects.flashObject(defenders[i], 0xff0000, 500); ballMissed(); return; } } // Goal structure collision removed to allow ball to pass through for scoring } // Function to reset goalkeeper to initial position function resetGoalkeeper() { tween(goalkeeper, { x: goalkeeperInitialX, y: goalkeeperInitialY }, { duration: 800, easing: tween.easeOut }); } // Function to update name display function updateNameDisplay() { nameDisplayText.setText(nameInputText); currentLetterText.setText(alphabet[currentLetterIndex]); } // Function to add current letter to name function addCurrentLetter() { if (nameInputText.length < 8) { // Limit name length nameInputText += alphabet[currentLetterIndex]; updateNameDisplay(); } } // Function to finish name entry function finishNameEntry() { if (nameInputText.length > 0) { playerName = nameInputText; nameEntered = true; // Hide name input UI namePromptText.alpha = 0; nameDisplayText.alpha = 0; currentLetterText.alpha = 0; leftNameButton.alpha = 0; rightNameButton.alpha = 0; addLetterButton.alpha = 0; finishNameButton.alpha = 0; // Show game instructions instructionsText.alpha = 1; startButton.alpha = 1; } } // Function to add score to leaderboard function addToLeaderboard(name, score) { leaderboardNames.push(name); leaderboardScores.push(score); // Create temporary array for sorting var tempLeaderboard = []; for (var i = 0; i < leaderboardNames.length; i++) { tempLeaderboard.push({ name: leaderboardNames[i], score: leaderboardScores[i] }); } // Sort by score descending tempLeaderboard.sort(function (a, b) { return b.score - a.score; }); // Keep only top 10 if (tempLeaderboard.length > 10) { tempLeaderboard = tempLeaderboard.slice(0, 10); } // Update arrays with sorted data leaderboardNames = []; leaderboardScores = []; for (var i = 0; i < tempLeaderboard.length; i++) { leaderboardNames.push(tempLeaderboard[i].name); leaderboardScores.push(tempLeaderboard[i].score); } // Save to storage as separate arrays storage.leaderboardNames = leaderboardNames; storage.leaderboardScores = leaderboardScores; } // Function to show leaderboard function showLeaderboard() { showingLeaderboard = true; // Create leaderboard display var leaderboardText = 'TOP 10 PLAYERS:\n\n'; for (var i = 0; i < Math.min(10, leaderboardNames.length); i++) { leaderboardText += i + 1 + '. ' + leaderboardNames[i] + ' - ' + leaderboardScores[i] + '\n'; } if (leaderboardNames.length === 0) { leaderboardText += 'No scores yet!'; } var leaderboardDisplay = new Text2(leaderboardText, { size: 60, fill: 0xFFFFFF, align: 'center' }); leaderboardDisplay.anchor.set(0.5, 0.5); leaderboardDisplay.x = 1024; leaderboardDisplay.y = 1366; game.addChild(leaderboardDisplay); // Auto-hide after 5 seconds LK.setTimeout(function () { if (leaderboardDisplay && leaderboardDisplay.parent) { leaderboardDisplay.destroy(); showingLeaderboard = false; } }, 5000); } function ballMissed() { attemptsLeft--; attemptsText.setText('Attempts: ' + attemptsLeft); if (attemptsLeft <= 0) { LK.effects.flashScreen(0xff0000, 1000); // Add score to leaderboard before game over if (playerName && LK.getScore() > 0) { addToLeaderboard(playerName, LK.getScore()); } LK.setTimeout(function () { // Show leaderboard before game over if (leaderboardNames.length > 0) { showLeaderboard(); LK.setTimeout(function () { LK.showGameOver(); // Show game over screen and reset game }, 3000); } else { LK.showGameOver(); // Show game over screen and reset game } }, 1500); } else { LK.setTimeout(function () { ball.reset(); resetGoalkeeper(); // Reset goalkeeper position }, 1000); } } // Game controls are now defined as variables above game.update = function () { if (ball.isMoving) { // Goalkeeper AI - move towards ball's predicted position if (ball.y < goalY + 400) { // Only react when ball is getting close to goal var ballTargetX = ball.x + ball.velocityX * 15; // Predict where ball will be (increased prediction) var keeperSpeed = 15; // Increased goalkeeper movement speed var maxKeeperX = goalX + 350; // Right boundary for keeper movement var minKeeperX = goalX - 350; // Left boundary for keeper movement // Clamp keeper target position within goal area ballTargetX = Math.max(minKeeperX, Math.min(maxKeeperX, ballTargetX)); // Move keeper towards predicted ball position with tween animation var deltaX = ballTargetX - goalkeeper.x; if (Math.abs(deltaX) > 5) { // Use tween for smooth goalkeeper movement tween(goalkeeper, { x: ballTargetX }, { duration: 200, easing: tween.easeOut }); } } checkCollisions(); } }; // Game instructions // Name input UI var namePromptText = new Text2('ENTER YOUR NAME:', { size: 80, fill: 0xFFFFFF, align: 'center' }); namePromptText.anchor.set(0.5, 0.5); namePromptText.x = 1024; namePromptText.y = 1200; game.addChild(namePromptText); var nameDisplayText = new Text2('', { size: 100, fill: 0x00FF00, align: 'center' }); nameDisplayText.anchor.set(0.5, 0.5); nameDisplayText.x = 1024; nameDisplayText.y = 1350; game.addChild(nameDisplayText); var currentLetterText = new Text2('A', { size: 120, fill: 0xFFFF00, align: 'center' }); currentLetterText.anchor.set(0.5, 0.5); currentLetterText.x = 1024; currentLetterText.y = 1500; game.addChild(currentLetterText); var leftNameButton = new Text2('<', { size: 100, fill: 0xFFFF00 }); leftNameButton.anchor.set(0.5, 0.5); leftNameButton.x = 900; leftNameButton.y = 1500; game.addChild(leftNameButton); var rightNameButton = new Text2('>', { size: 100, fill: 0xFFFF00 }); rightNameButton.anchor.set(0.5, 0.5); rightNameButton.x = 1148; rightNameButton.y = 1500; game.addChild(rightNameButton); var addLetterButton = new Text2('ADD LETTER', { size: 60, fill: 0x00FF00 }); addLetterButton.anchor.set(0.5, 0.5); addLetterButton.x = 1024; addLetterButton.y = 1650; game.addChild(addLetterButton); var finishNameButton = new Text2('START GAME', { size: 80, fill: 0xFF0000 }); finishNameButton.anchor.set(0.5, 0.5); finishNameButton.x = 1024; finishNameButton.y = 1750; game.addChild(finishNameButton); var instructionsText = new Text2('Swipe to aim and shoot!\nUse < > buttons to adjust spin.\nSwipe direction sets aim, spin is manual.\nInside of the lines are counted as scores as well.', { size: 75, fill: 0xFFFFFF, align: 'center' }); instructionsText.anchor.set(0.5, 0.5); instructionsText.x = 1024; instructionsText.y = 1366; instructionsText.alpha = 0; // Hidden initially game.addChild(instructionsText); var startButton = new Text2('TAP TO START', { size: 100, fill: 0x00FF00 }); startButton.anchor.set(0.5, 0.5); startButton.x = 1024; startButton.y = 1600; startButton.alpha = 0; // Hidden initially game.addChild(startButton); var gameStarted = false; // Store the actual game control functions var originalDown = function originalDown(x, y, obj) { if (!ball.isMoving && ball.y > 2000) { isAiming = true; dragStartX = x; dragStartY = y; swipePath = [{ x: x, y: y, time: Date.now() }]; swipeStartTime = Date.now(); } }; var originalMove = function originalMove(x, y, obj) { if (isAiming && !ball.isMoving) { dragCurrentX = x; dragCurrentY = y; // Track swipe path var currentTime = Date.now(); swipePath.push({ x: x, y: y, time: currentTime }); // Keep only recent points if (swipePath.length > maxSwipePoints) { swipePath.shift(); } var deltaX = x - dragStartX; var deltaY = y - dragStartY; var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY); var power = fixedSpeed; var angle = Math.atan2(-deltaY, deltaX); powerText.x = ball.x; powerText.y = ball.y - 100; powerText.setText(''); // Show trajectory with current manual curve setting createTrajectoryPreview(power, angle, selectedCurve); } }; var originalUp = function originalUp(x, y, obj) { if (isAiming && !ball.isMoving) { var deltaX = x - dragStartX; var deltaY = y - dragStartY; var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY); if (distance > 20) { var power = fixedSpeed; var angle = Math.atan2(-deltaY, deltaX); // Use manually selected curve var spin = selectedCurve; ball.shoot(power, angle, spin); } isAiming = false; powerText.setText(''); clearTrajectoryPreview(); // Clear swipe path swipePath = []; } }; // Add click handlers to name input buttons leftNameButton.down = function (x, y, obj) { if (!nameEntered) { if (currentLetterIndex > 0) { currentLetterIndex--; } else { currentLetterIndex = alphabet.length - 1; } updateNameDisplay(); LK.effects.flashObject(leftNameButton, 0x00ff00, 200); } }; rightNameButton.down = function (x, y, obj) { if (!nameEntered) { if (currentLetterIndex < alphabet.length - 1) { currentLetterIndex++; } else { currentLetterIndex = 0; } updateNameDisplay(); LK.effects.flashObject(rightNameButton, 0x00ff00, 200); } }; addLetterButton.down = function (x, y, obj) { if (!nameEntered) { addCurrentLetter(); LK.effects.flashObject(addLetterButton, 0x00ff00, 200); } }; finishNameButton.down = function (x, y, obj) { if (!nameEntered) { finishNameEntry(); LK.effects.flashObject(finishNameButton, 0x00ff00, 200); } }; // Add click handlers to curve buttons leftCurveButton.down = function (x, y, obj) { if (gameStarted && !ball.isMoving) { if (curveIndex > 0) { curveIndex--; updateCurveDisplay(); LK.effects.flashObject(leftCurveButton, 0x00ff00, 200); } } }; rightCurveButton.down = function (x, y, obj) { if (gameStarted && !ball.isMoving) { if (curveIndex < curveValues.length - 1) { curveIndex++; updateCurveDisplay(); LK.effects.flashObject(rightCurveButton, 0x00ff00, 200); } } }; // Initialize name display updateNameDisplay(); // Override game controls until game starts game.down = function (x, y, obj) { if (!nameEntered) { // Still in name input phase, do nothing (buttons handle their own events) return; } if (!gameStarted) { // Start the game gameStarted = true; instructionsText.destroy(); startButton.destroy(); // Restore original controls game.down = originalDown; game.move = originalMove; game.up = originalUp; // Initialize first round setupRound(); // Call the original down handler for this event originalDown(x, y, obj); return; } }; game.move = function (x, y, obj) { // Do nothing until game starts }; game.up = function (x, y, obj) { // Do nothing until game starts };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.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.gravity = 0;
self.friction = 0.98;
self.isMoving = false;
self.spinForce = 0;
self.spinDecay = 0.98;
self.shoot = function (power, angle, spin) {
self.velocityX = Math.cos(angle) * power;
self.velocityY = Math.sin(angle) * power;
self.spinForce = spin || 0;
self.isMoving = true;
LK.getSound('kick').play();
};
self.reset = function () {
self.x = ballStartX;
self.y = ballStartY;
self.velocityX = 0;
self.velocityY = 0;
self.isMoving = false;
self.spinForce = 0;
};
self.update = function () {
if (self.isMoving) {
// Apply spin curve effect
if (self.spinForce !== 0) {
// Calculate perpendicular force to current velocity for curve
var speed = Math.sqrt(self.velocityX * self.velocityX + self.velocityY * self.velocityY);
if (speed > 0) {
var normalX = -self.velocityY / speed;
var normalY = self.velocityX / speed;
self.velocityX += normalX * self.spinForce * 0.1;
self.velocityY += normalY * self.spinForce * 0.1;
}
// Decay spin over time
self.spinForce *= self.spinDecay;
}
self.x += self.velocityX;
self.y += self.velocityY;
// No friction applied - ball maintains constant velocity
// Bounds checking
if (self.x < 0 || self.x > 2048 || self.y > 2732) {
self.isMoving = false;
ballMissed();
}
}
};
return self;
});
var Defender = Container.expand(function () {
var self = Container.call(this);
var defenderGraphics = self.attachAsset('defender', {
anchorX: 0.5,
anchorY: 1.0
});
return self;
});
var Goalkeeper = Container.expand(function () {
var self = Container.call(this);
var keeperGraphics = self.attachAsset('goalkeeper', {
anchorX: 0.5,
anchorY: 1.0
});
return self;
});
var TrajectoryDot = Container.expand(function () {
var self = Container.call(this);
var dotGraphics = self.attachAsset('trajectory', {
anchorX: 0.5,
anchorY: 0.5
});
dotGraphics.alpha = 0.6;
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x7DD87D
});
/****
* Game Code
****/
// Game variables
var currentRound = 1;
var attemptsLeft = 3;
var ballStartX = 1024;
var ballStartY = 2500;
var goalX = 1024;
var goalY = 400;
var isAiming = false;
var dragStartX = 0;
var dragStartY = 0;
var dragCurrentX = 0;
var dragCurrentY = 0;
var trajectoryDots = [];
var defenders = [];
// Player name and leaderboard variables
var playerName = '';
var nameEntered = false;
var nameInputText = '';
var alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
var currentLetterIndex = 0;
var leaderboardNames = storage.leaderboardNames || [];
var leaderboardScores = storage.leaderboardScores || [];
var showingLeaderboard = false;
// Swipe tracking variables
var swipePath = [];
var swipeStartTime = 0;
var swipeSpeed = 0;
var swipeCurve = 0;
var maxSwipePoints = 10;
// Create grass field
var grass = game.addChild(LK.getAsset('grass', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
}));
// Create goal structure
var goal = game.addChild(LK.getAsset('goal', {
anchorX: 0.5,
anchorY: 0.5,
x: goalX,
y: goalY + 100
}));
goal.alpha = 0.3; // Make goal semi-transparent so we can see the ball
// Create goalkeeper
var goalkeeper = game.addChild(new Goalkeeper());
goalkeeper.x = goalX;
goalkeeper.y = goalY + 450; // Move goalkeeper in front of the goal
// Store goalkeeper's initial position for reset
var goalkeeperInitialX = goalkeeper.x;
var goalkeeperInitialY = goalkeeper.y;
// Create ball
var ball = game.addChild(new Ball());
ball.reset();
// UI Elements
var roundText = new Text2('Round: 1', {
size: 100,
fill: 0xFFFFFF
});
roundText.anchor.set(0.5, 0);
LK.gui.top.addChild(roundText);
var attemptsText = new Text2('Attempts: 3', {
size: 75,
fill: 0xFFFFFF
});
attemptsText.anchor.set(1.0, 0);
LK.gui.topRight.addChild(attemptsText);
var personalBest = storage.personalBest || 0;
var personalBestText = new Text2('Best: ' + personalBest, {
size: 75,
fill: 0x00FF00
});
personalBestText.anchor.set(0, 0);
LK.gui.topLeft.addChild(personalBestText);
personalBestText.x = 120; // Move away from platform menu icon
var powerText = new Text2('', {
size: 62,
fill: 0xFFFF00
});
powerText.anchor.set(0.5, 0.5);
game.addChild(powerText);
var curveText = new Text2('Spin: 0', {
size: 75,
fill: 0xFFFFFF
});
curveText.anchor.set(0.5, 0.5);
curveText.x = 1024;
curveText.y = 200;
game.addChild(curveText);
var selectedCurve = 0;
var maxCurve = 20;
var curveValues = [-20, -18, -16, -14, -12, -10, -8, -6, -4, -2, -1, 0, 1, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20];
var curveIndex = 11; // Start at 0 curve (index 11 in the new array)
var fixedSpeed = 35;
// Create curve adjustment buttons
var leftCurveButton = new Text2('<', {
size: 125,
fill: 0xFFFF00
});
leftCurveButton.anchor.set(0.5, 0.5);
leftCurveButton.x = 850;
leftCurveButton.y = 200;
game.addChild(leftCurveButton);
var rightCurveButton = new Text2('>', {
size: 125,
fill: 0xFFFF00
});
rightCurveButton.anchor.set(0.5, 0.5);
rightCurveButton.x = 1198;
rightCurveButton.y = 200;
game.addChild(rightCurveButton);
// Function to update curve display
function updateCurveDisplay() {
selectedCurve = curveValues[curveIndex];
if (selectedCurve === 0) {
curveText.setText('Spin: 0');
} else {
curveText.setText('Spin: ' + (selectedCurve > 0 ? '+' : '') + selectedCurve);
}
console.log('Curve updated to:', selectedCurve, 'Index:', curveIndex);
}
// Initialize curve display
updateCurveDisplay();
// Initialize defenders for current round
function setupRound() {
// Clear existing defenders
for (var i = 0; i < defenders.length; i++) {
defenders[i].destroy();
}
defenders = [];
// Add defenders based on round
var numDefenders = Math.min(currentRound - 1, 4);
for (var i = 0; i < numDefenders; i++) {
var defender = game.addChild(new Defender());
if (i < 2) {
// First 2 defenders stay side by side in fixed positions
var spacing = 120;
var startX = goalX - spacing / 2;
defender.x = startX + i * spacing;
defender.y = goalY + 450 + currentRound * 30;
} else {
// Other 2 defenders are placed randomly
var randomX = goalX + (Math.random() - 0.5) * 600; // Random X within a reasonable area
var randomY = goalY + 400 + Math.random() * 200; // Random Y in front of goal
defender.x = randomX;
defender.y = randomY;
}
defenders.push(defender);
}
// Starting from round 10, add 1 random defender in front of the goal
if (currentRound >= 10) {
var randomDefender = game.addChild(new Defender());
// Random position in front of goal area
var randomX = goalX + (Math.random() - 0.5) * 800; // Random X within goal width area
var randomY = goalY + 350 + Math.random() * 150; // Random Y in front of goal
randomDefender.x = randomX;
randomDefender.y = randomY;
defenders.push(randomDefender);
}
roundText.setText('Round: ' + currentRound);
attemptsText.setText('Attempts: ' + attemptsLeft);
}
// Create trajectory preview
function createTrajectoryPreview(power, angle, spin) {
// Clear existing dots
for (var i = 0; i < trajectoryDots.length; i++) {
trajectoryDots[i].destroy();
}
trajectoryDots = [];
// Calculate trajectory points
var steps = 15;
var stepTime = 0.8;
var tempVelX = Math.cos(angle) * power * 0.7;
var tempVelY = Math.sin(angle) * power * 0.7;
var tempX = ball.x;
var tempY = ball.y;
var tempSpin = spin || 0;
for (var i = 0; i < steps; i++) {
// Apply spin curve effect in preview
if (tempSpin !== 0) {
var speed = Math.sqrt(tempVelX * tempVelX + tempVelY * tempVelY);
if (speed > 0) {
var normalX = -tempVelY / speed;
var normalY = tempVelX / speed;
tempVelX += normalX * tempSpin * 0.1;
tempVelY += normalY * tempSpin * 0.1;
}
tempSpin *= 0.98;
}
tempX += tempVelX * stepTime;
tempY += tempVelY * stepTime;
// No gravity applied to trajectory preview
tempVelX *= 0.98;
tempVelY *= 0.98;
if (tempY > 2732 || tempX < 0 || tempX > 2048) break;
var dot = game.addChild(new TrajectoryDot());
dot.x = tempX;
dot.y = tempY;
trajectoryDots.push(dot);
}
}
// Clear trajectory preview
function clearTrajectoryPreview() {
for (var i = 0; i < trajectoryDots.length; i++) {
trajectoryDots[i].destroy();
}
trajectoryDots = [];
}
// Calculate curve from swipe path
function calculateSwipeCurve() {
if (swipePath.length < 3) return 0;
var totalCurvature = 0;
var validSegments = 0;
var curvatureSum = 0;
var pathLength = 0;
// Calculate curvature at each point in the path
for (var i = 1; i < swipePath.length - 1; i++) {
var p1 = swipePath[i - 1];
var p2 = swipePath[i];
var p3 = swipePath[i + 1];
// Calculate vectors
var v1x = p2.x - p1.x;
var v1y = p2.y - p1.y;
var v2x = p3.x - p2.x;
var v2y = p3.y - p2.y;
// Calculate cross product to determine curve direction
var crossProduct = v1x * v2y - v1y * v2x;
// Calculate magnitudes
var mag1 = Math.sqrt(v1x * v1x + v1y * v1y);
var mag2 = Math.sqrt(v2x * v2x + v2y * v2y);
if (mag1 > 5 && mag2 > 5) {
// Only consider significant movements
curvatureSum += crossProduct / (mag1 * mag2);
validSegments++;
pathLength += mag1;
}
}
if (validSegments > 0) {
totalCurvature = curvatureSum / validSegments;
// Scale based on path length and speed
var lengthFactor = Math.min(pathLength / 200, 2);
var speedFactor = Math.min(swipeSpeed / 500, 2);
var finalCurve = totalCurvature * lengthFactor * speedFactor * 15;
return Math.max(-10, Math.min(10, finalCurve));
}
return 0;
}
// Calculate swipe speed
function calculateSwipeSpeed() {
if (swipePath.length < 2) return 0;
var totalDistance = 0;
var totalTime = 0;
for (var i = 1; i < swipePath.length; i++) {
var p1 = swipePath[i - 1];
var p2 = swipePath[i];
var dx = p2.x - p1.x;
var dy = p2.y - p1.y;
totalDistance += Math.sqrt(dx * dx + dy * dy);
totalTime += p2.time - p1.time;
}
return totalTime > 0 ? totalDistance / totalTime * 1000 : 0;
}
// Ball collision detection
function checkCollisions() {
// Check goalkeeper collision first - this prevents scoring
if (ball.intersects(goalkeeper)) {
// Enhanced goalkeeper save logic based on ball position relative to goal
var goalLeft = goalX - 400; // Left side of goal
var goalRight = goalX + 400; // Right side of goal
var goalWidth = 800; // Total goal width
var goalCenter = goalX; // Goal center X position
var distanceFromCenter = Math.abs(ball.x - goalCenter);
var distanceFromGoalEdge = Math.min(Math.abs(ball.x - goalLeft), Math.abs(ball.x - goalRight));
var ballSpeed = Math.sqrt(ball.velocityX * ball.velocityX + ball.velocityY * ball.velocityY);
// Calculate save chance based on position in goal
var saveChance = 0;
// Define middle zone (center 60% of goal) and corner zones (outer 20% each side)
var middleZoneWidth = goalWidth * 0.6; // 480 pixels from center
var cornerZoneWidth = goalWidth * 0.2; // 160 pixels each corner
if (distanceFromCenter <= middleZoneWidth / 2) {
// Ball is in the middle zone - high save chance
saveChance = 0.95; // 95% save chance in middle
} else if (distanceFromGoalEdge <= cornerZoneWidth) {
// Ball is in corner zone - very low save chance
saveChance = 0.1; // Only 10% save chance in corners
} else {
// Ball is in transition zone between middle and corner
saveChance = 0.4; // Moderate save chance in transition
}
// Adjust save chance based on ball speed (faster balls harder to save)
if (ballSpeed > 30) saveChance -= 0.2;
if (ballSpeed > 40) saveChance -= 0.2;
// Ensure save chance stays within reasonable bounds
saveChance = Math.max(0.05, Math.min(0.98, saveChance));
// Random save attempt - increased base save chances
var adjustedSaveChance = saveChance;
// Boost save chances significantly
if (distanceFromCenter <= middleZoneWidth / 2) {
// Ball is in the middle zone - very high save chance
adjustedSaveChance = 0.85; // 85% save chance in middle
} else if (distanceFromGoalEdge <= cornerZoneWidth) {
// Ball is in corner zone - still some save chance
adjustedSaveChance = 0.25; // 25% save chance in corners
} else {
// Ball is in transition zone
adjustedSaveChance = 0.55; // 55% save chance in transition
}
// Adjust save chance based on ball speed (faster balls harder to save)
if (ballSpeed > 30) adjustedSaveChance -= 0.15;
if (ballSpeed > 40) adjustedSaveChance -= 0.15;
// Ensure save chance stays within reasonable bounds
adjustedSaveChance = Math.max(0.15, Math.min(0.95, adjustedSaveChance));
console.log('Save attempt - Ball X:', ball.x, 'Distance from center:', distanceFromCenter, 'Save chance:', adjustedSaveChance);
if (Math.random() < adjustedSaveChance) {
// Successful save
console.log('GOALKEEPER SAVE!');
ball.isMoving = false;
LK.getSound('save').play();
LK.effects.flashObject(goalkeeper, 0x00ff00, 800); // Green flash for save
LK.effects.flashScreen(0x00ff00, 300); // Screen flash for dramatic effect
// Animate goalkeeper dive with tween - more dramatic
tween(goalkeeper, {
scaleX: 1.5,
scaleY: 0.7,
rotation: ball.x > goalkeeper.x ? 0.3 : -0.3 // Dive towards ball
}, {
duration: 400,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(goalkeeper, {
scaleX: 1.0,
scaleY: 1.0,
rotation: 0
}, {
duration: 300,
onFinish: function onFinish() {
resetGoalkeeper(); // Reset goalkeeper position after dive animation
}
});
}
});
ballMissed();
return;
} else {
// Goalkeeper fails to save - ball continues
console.log('Goalkeeper missed the save');
LK.effects.flashObject(goalkeeper, 0xff6666, 300);
}
}
// Check goal scoring - ball must be inside goal area including side lines
// Goal asset is positioned at goalY + 100 and has height 584.38, width 800
var goalLeft = goalX - 400; // Left side line of goal (800/2 = 400)
var goalRight = goalX + 400; // Right side line of goal
var goalTop = goalY + 100 - 292; // Top of goal (goalY + 100 - height/2)
var goalBottom = goalY + 100 + 292; // Bottom of goal (goalY + 100 + height/2)
if (ball.x >= goalLeft && ball.x <= goalRight && ball.y >= goalTop && ball.y <= goalBottom) {
// Goal scored! Ball touched inside goal area or side lines
ball.isMoving = false;
LK.getSound('goal').play();
LK.effects.flashScreen(0x00ff00, 500);
LK.setScore(LK.getScore() + 1);
// Check and update personal best
var currentScore = LK.getScore();
if (currentScore > personalBest) {
personalBest = currentScore;
storage.personalBest = personalBest;
personalBestText.setText('Best: ' + personalBest);
LK.effects.flashObject(personalBestText, 0xFFFF00, 1000);
}
currentRound++;
// Reset attempts for next round
attemptsLeft = 3;
LK.setTimeout(function () {
ball.reset();
resetGoalkeeper(); // Reset goalkeeper position
setupRound();
}, 1000);
return;
}
// Check defender collisions
for (var i = 0; i < defenders.length; i++) {
if (ball.intersects(defenders[i])) {
ball.isMoving = false;
LK.effects.flashObject(defenders[i], 0xff0000, 500);
ballMissed();
return;
}
}
// Goal structure collision removed to allow ball to pass through for scoring
}
// Function to reset goalkeeper to initial position
function resetGoalkeeper() {
tween(goalkeeper, {
x: goalkeeperInitialX,
y: goalkeeperInitialY
}, {
duration: 800,
easing: tween.easeOut
});
}
// Function to update name display
function updateNameDisplay() {
nameDisplayText.setText(nameInputText);
currentLetterText.setText(alphabet[currentLetterIndex]);
}
// Function to add current letter to name
function addCurrentLetter() {
if (nameInputText.length < 8) {
// Limit name length
nameInputText += alphabet[currentLetterIndex];
updateNameDisplay();
}
}
// Function to finish name entry
function finishNameEntry() {
if (nameInputText.length > 0) {
playerName = nameInputText;
nameEntered = true;
// Hide name input UI
namePromptText.alpha = 0;
nameDisplayText.alpha = 0;
currentLetterText.alpha = 0;
leftNameButton.alpha = 0;
rightNameButton.alpha = 0;
addLetterButton.alpha = 0;
finishNameButton.alpha = 0;
// Show game instructions
instructionsText.alpha = 1;
startButton.alpha = 1;
}
}
// Function to add score to leaderboard
function addToLeaderboard(name, score) {
leaderboardNames.push(name);
leaderboardScores.push(score);
// Create temporary array for sorting
var tempLeaderboard = [];
for (var i = 0; i < leaderboardNames.length; i++) {
tempLeaderboard.push({
name: leaderboardNames[i],
score: leaderboardScores[i]
});
}
// Sort by score descending
tempLeaderboard.sort(function (a, b) {
return b.score - a.score;
});
// Keep only top 10
if (tempLeaderboard.length > 10) {
tempLeaderboard = tempLeaderboard.slice(0, 10);
}
// Update arrays with sorted data
leaderboardNames = [];
leaderboardScores = [];
for (var i = 0; i < tempLeaderboard.length; i++) {
leaderboardNames.push(tempLeaderboard[i].name);
leaderboardScores.push(tempLeaderboard[i].score);
}
// Save to storage as separate arrays
storage.leaderboardNames = leaderboardNames;
storage.leaderboardScores = leaderboardScores;
}
// Function to show leaderboard
function showLeaderboard() {
showingLeaderboard = true;
// Create leaderboard display
var leaderboardText = 'TOP 10 PLAYERS:\n\n';
for (var i = 0; i < Math.min(10, leaderboardNames.length); i++) {
leaderboardText += i + 1 + '. ' + leaderboardNames[i] + ' - ' + leaderboardScores[i] + '\n';
}
if (leaderboardNames.length === 0) {
leaderboardText += 'No scores yet!';
}
var leaderboardDisplay = new Text2(leaderboardText, {
size: 60,
fill: 0xFFFFFF,
align: 'center'
});
leaderboardDisplay.anchor.set(0.5, 0.5);
leaderboardDisplay.x = 1024;
leaderboardDisplay.y = 1366;
game.addChild(leaderboardDisplay);
// Auto-hide after 5 seconds
LK.setTimeout(function () {
if (leaderboardDisplay && leaderboardDisplay.parent) {
leaderboardDisplay.destroy();
showingLeaderboard = false;
}
}, 5000);
}
function ballMissed() {
attemptsLeft--;
attemptsText.setText('Attempts: ' + attemptsLeft);
if (attemptsLeft <= 0) {
LK.effects.flashScreen(0xff0000, 1000);
// Add score to leaderboard before game over
if (playerName && LK.getScore() > 0) {
addToLeaderboard(playerName, LK.getScore());
}
LK.setTimeout(function () {
// Show leaderboard before game over
if (leaderboardNames.length > 0) {
showLeaderboard();
LK.setTimeout(function () {
LK.showGameOver(); // Show game over screen and reset game
}, 3000);
} else {
LK.showGameOver(); // Show game over screen and reset game
}
}, 1500);
} else {
LK.setTimeout(function () {
ball.reset();
resetGoalkeeper(); // Reset goalkeeper position
}, 1000);
}
}
// Game controls are now defined as variables above
game.update = function () {
if (ball.isMoving) {
// Goalkeeper AI - move towards ball's predicted position
if (ball.y < goalY + 400) {
// Only react when ball is getting close to goal
var ballTargetX = ball.x + ball.velocityX * 15; // Predict where ball will be (increased prediction)
var keeperSpeed = 15; // Increased goalkeeper movement speed
var maxKeeperX = goalX + 350; // Right boundary for keeper movement
var minKeeperX = goalX - 350; // Left boundary for keeper movement
// Clamp keeper target position within goal area
ballTargetX = Math.max(minKeeperX, Math.min(maxKeeperX, ballTargetX));
// Move keeper towards predicted ball position with tween animation
var deltaX = ballTargetX - goalkeeper.x;
if (Math.abs(deltaX) > 5) {
// Use tween for smooth goalkeeper movement
tween(goalkeeper, {
x: ballTargetX
}, {
duration: 200,
easing: tween.easeOut
});
}
}
checkCollisions();
}
};
// Game instructions
// Name input UI
var namePromptText = new Text2('ENTER YOUR NAME:', {
size: 80,
fill: 0xFFFFFF,
align: 'center'
});
namePromptText.anchor.set(0.5, 0.5);
namePromptText.x = 1024;
namePromptText.y = 1200;
game.addChild(namePromptText);
var nameDisplayText = new Text2('', {
size: 100,
fill: 0x00FF00,
align: 'center'
});
nameDisplayText.anchor.set(0.5, 0.5);
nameDisplayText.x = 1024;
nameDisplayText.y = 1350;
game.addChild(nameDisplayText);
var currentLetterText = new Text2('A', {
size: 120,
fill: 0xFFFF00,
align: 'center'
});
currentLetterText.anchor.set(0.5, 0.5);
currentLetterText.x = 1024;
currentLetterText.y = 1500;
game.addChild(currentLetterText);
var leftNameButton = new Text2('<', {
size: 100,
fill: 0xFFFF00
});
leftNameButton.anchor.set(0.5, 0.5);
leftNameButton.x = 900;
leftNameButton.y = 1500;
game.addChild(leftNameButton);
var rightNameButton = new Text2('>', {
size: 100,
fill: 0xFFFF00
});
rightNameButton.anchor.set(0.5, 0.5);
rightNameButton.x = 1148;
rightNameButton.y = 1500;
game.addChild(rightNameButton);
var addLetterButton = new Text2('ADD LETTER', {
size: 60,
fill: 0x00FF00
});
addLetterButton.anchor.set(0.5, 0.5);
addLetterButton.x = 1024;
addLetterButton.y = 1650;
game.addChild(addLetterButton);
var finishNameButton = new Text2('START GAME', {
size: 80,
fill: 0xFF0000
});
finishNameButton.anchor.set(0.5, 0.5);
finishNameButton.x = 1024;
finishNameButton.y = 1750;
game.addChild(finishNameButton);
var instructionsText = new Text2('Swipe to aim and shoot!\nUse < > buttons to adjust spin.\nSwipe direction sets aim, spin is manual.\nInside of the lines are counted as scores as well.', {
size: 75,
fill: 0xFFFFFF,
align: 'center'
});
instructionsText.anchor.set(0.5, 0.5);
instructionsText.x = 1024;
instructionsText.y = 1366;
instructionsText.alpha = 0; // Hidden initially
game.addChild(instructionsText);
var startButton = new Text2('TAP TO START', {
size: 100,
fill: 0x00FF00
});
startButton.anchor.set(0.5, 0.5);
startButton.x = 1024;
startButton.y = 1600;
startButton.alpha = 0; // Hidden initially
game.addChild(startButton);
var gameStarted = false;
// Store the actual game control functions
var originalDown = function originalDown(x, y, obj) {
if (!ball.isMoving && ball.y > 2000) {
isAiming = true;
dragStartX = x;
dragStartY = y;
swipePath = [{
x: x,
y: y,
time: Date.now()
}];
swipeStartTime = Date.now();
}
};
var originalMove = function originalMove(x, y, obj) {
if (isAiming && !ball.isMoving) {
dragCurrentX = x;
dragCurrentY = y;
// Track swipe path
var currentTime = Date.now();
swipePath.push({
x: x,
y: y,
time: currentTime
});
// Keep only recent points
if (swipePath.length > maxSwipePoints) {
swipePath.shift();
}
var deltaX = x - dragStartX;
var deltaY = y - dragStartY;
var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
var power = fixedSpeed;
var angle = Math.atan2(-deltaY, deltaX);
powerText.x = ball.x;
powerText.y = ball.y - 100;
powerText.setText('');
// Show trajectory with current manual curve setting
createTrajectoryPreview(power, angle, selectedCurve);
}
};
var originalUp = function originalUp(x, y, obj) {
if (isAiming && !ball.isMoving) {
var deltaX = x - dragStartX;
var deltaY = y - dragStartY;
var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
if (distance > 20) {
var power = fixedSpeed;
var angle = Math.atan2(-deltaY, deltaX);
// Use manually selected curve
var spin = selectedCurve;
ball.shoot(power, angle, spin);
}
isAiming = false;
powerText.setText('');
clearTrajectoryPreview();
// Clear swipe path
swipePath = [];
}
};
// Add click handlers to name input buttons
leftNameButton.down = function (x, y, obj) {
if (!nameEntered) {
if (currentLetterIndex > 0) {
currentLetterIndex--;
} else {
currentLetterIndex = alphabet.length - 1;
}
updateNameDisplay();
LK.effects.flashObject(leftNameButton, 0x00ff00, 200);
}
};
rightNameButton.down = function (x, y, obj) {
if (!nameEntered) {
if (currentLetterIndex < alphabet.length - 1) {
currentLetterIndex++;
} else {
currentLetterIndex = 0;
}
updateNameDisplay();
LK.effects.flashObject(rightNameButton, 0x00ff00, 200);
}
};
addLetterButton.down = function (x, y, obj) {
if (!nameEntered) {
addCurrentLetter();
LK.effects.flashObject(addLetterButton, 0x00ff00, 200);
}
};
finishNameButton.down = function (x, y, obj) {
if (!nameEntered) {
finishNameEntry();
LK.effects.flashObject(finishNameButton, 0x00ff00, 200);
}
};
// Add click handlers to curve buttons
leftCurveButton.down = function (x, y, obj) {
if (gameStarted && !ball.isMoving) {
if (curveIndex > 0) {
curveIndex--;
updateCurveDisplay();
LK.effects.flashObject(leftCurveButton, 0x00ff00, 200);
}
}
};
rightCurveButton.down = function (x, y, obj) {
if (gameStarted && !ball.isMoving) {
if (curveIndex < curveValues.length - 1) {
curveIndex++;
updateCurveDisplay();
LK.effects.flashObject(rightCurveButton, 0x00ff00, 200);
}
}
};
// Initialize name display
updateNameDisplay();
// Override game controls until game starts
game.down = function (x, y, obj) {
if (!nameEntered) {
// Still in name input phase, do nothing (buttons handle their own events)
return;
}
if (!gameStarted) {
// Start the game
gameStarted = true;
instructionsText.destroy();
startButton.destroy();
// Restore original controls
game.down = originalDown;
game.move = originalMove;
game.up = originalUp;
// Initialize first round
setupRound();
// Call the original down handler for this event
originalDown(x, y, obj);
return;
}
};
game.move = function (x, y, obj) {
// Do nothing until game starts
};
game.up = function (x, y, obj) {
// Do nothing until game starts
};