User prompt
player can curve the ball, ball will not go only straightforward
User prompt
when the ball touches to the inside white lines which is the goal, player should go the next round and it should count as score
User prompt
even though I am scoring the game does not continue with the next round can you correct this
User prompt
to pass the round one goal is enough, so if the players scores in his first attempt then he goes to the next round without trying his other attempts
User prompt
ball shouldnt stop at all
User prompt
there should not be gravity and the ball should continue to the goal
Code edit (1 edits merged)
Please save this source code
User prompt
Free Kick Master
Initial prompt
I want to make a free kick goal game. Player sees the ball in the down below screen and shoots by arranging the direction and speed of the ball. Player has 3 tries to score a goal. If the player can score within these 3 tries, he will go to the next round. The next round will be harder, it can be either from a further distance or with higher number of men trying to block the ball. Goalkeeper will not move and also other men will not move.
/**** * 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
};