/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1", { coins: 0, highScore: 0, ballLevel: 1, speedLevel: 1 }); /**** * Classes ****/ var Ball = Container.expand(function () { var self = Container.call(this); var ballGraphics = self.attachAsset('ball', { anchorX: 0.5, anchorY: 0.5, scaleX: 1, scaleY: 1 }); self.velocityY = 0; self.gravity = 0.5; self.jumpForce = -15; self.grounded = false; self.canJump = true; self.health = 3; self.speed = 5 + (storage.speedLevel - 1) * 2; self.radius = 50; self.power = storage.ballLevel; // Set color based on ball level/power if (self.power === 2) { ballGraphics.tint = 0xFF6600; // Orange for fireball } else if (self.power === 3) { ballGraphics.tint = 0xFFD700; // Gold color for golden ball } else if (self.power === 4) { ballGraphics.tint = 0x666666; // Dark gray for steel } else if (self.power === 5) { ballGraphics.tint = 0x88CCFF; // Light blue for ice ball } else if (self.power === 6) { ballGraphics.tint = 0x00FF00; // Green color for poison ball } else if (self.power === 7) { ballGraphics.tint = 0x333333; // Dark color for dark ball } self.jump = function (isDoubleJump) { if (self.grounded && self.canJump) { // Higher jump force for double-click if (isDoubleJump) { self.velocityY = self.jumpForce * 1.5; // 50% higher jump LK.effects.flashObject(self, 0x00FF00, 300); // Visual feedback - green flash } else { self.velocityY = self.jumpForce; } self.grounded = false; LK.getSound('jump').play(); // Jump cooldown self.canJump = false; LK.setTimeout(function () { self.canJump = true; }, 300); } }; self.takeDamage = function () { self.health--; LK.getSound('damage').play(); LK.effects.flashObject(self, 0xFF0000, 500); // Visual feedback for health loss var healthLossText = new Text2("-1 Health!", { size: 90, fill: 0xFF0000, stroke: 0x000000, strokeThickness: 5 }); healthLossText.anchor.set(0.5, 0.5); healthLossText.x = self.x; healthLossText.y = self.y - 100; game.addChild(healthLossText); // Animate and remove tween(healthLossText, { y: healthLossText.y - 150, alpha: 0 }, { duration: 800, onFinish: function onFinish() { game.removeChild(healthLossText); } }); // Update the health display updateHealthDisplay(); if (self.health <= 0) { LK.showGameOver(); } }; self.update = function () { // Apply gravity self.velocityY += self.gravity; self.y += self.velocityY; // Visual rotation to simulate rolling ballGraphics.rotation += 0.05 * self.speed; // Ground collision if (self.y + self.radius > groundY) { self.y = groundY - self.radius; self.velocityY = 0; self.grounded = true; } }; return self; }); var Coin = Container.expand(function () { var self = Container.call(this); var coinGraphics = self.attachAsset('coin', { anchorX: 0.5, anchorY: 0.5 }); self.collected = false; self.collect = function () { if (!self.collected) { self.collected = true; LK.getSound('coinCollect').play(); // Animation tween(self, { scaleX: 1.5, scaleY: 1.5, alpha: 0 }, { duration: 300, onFinish: function onFinish() { self.destroy(); } }); // Update coins storage.coins += 1; updateCoinsDisplay(); } }; self.update = function () { // Coin rotation animation coinGraphics.rotation += 0.05; // Move coin toward player self.x -= ball.speed; // Floating animation self.y += Math.sin(LK.ticks / 10) * 0.5; // Remove if passed player if (self.x < -100) { if (collectibles.indexOf(self) !== -1) { collectibles.splice(collectibles.indexOf(self), 1); } self.destroy(); } }; return self; }); var Glass = Container.expand(function () { var self = Container.call(this); var glassGraphics = self.attachAsset('glass', { anchorX: 0.5, anchorY: 0.5, alpha: 0.7 }); self.width = glassGraphics.width; self.height = glassGraphics.height; self.broken = false; self.health = 1; self.type = 'normal'; // normal, moving, trap self.movingDirection = 1; self.movingSpeed = 0; self.lastPassed = false; // Track if ball has passed through this glass self.setup = function (type, difficulty) { self.type = type || 'normal'; self.health = difficulty || 1; if (self.type === 'moving') { self.movingSpeed = Math.random() * 3 + 2; glassGraphics.tint = 0x33CCFF; } else if (self.type === 'trap') { glassGraphics.tint = 0xFF3333; } }; self["break"] = function () { if (!self.broken) { self.broken = true; LK.getSound('glassBreak').play(); // Create glass breaking particles self.createParticles(); // Remove from active obstacles after animation LK.setTimeout(function () { if (obstacles.includes(self)) { var index = obstacles.indexOf(self); if (index !== -1) { obstacles.splice(index, 1); } } self.destroy(); }, 100); } }; self.createParticles = function () { for (var i = 0; i < 8; i++) { var particle = LK.getAsset('particleGlass', { anchorX: 0.5, anchorY: 0.5, x: self.x + (Math.random() * 100 - 50), y: self.y + (Math.random() * 100 - 50), alpha: 0.8 }); game.addChild(particle); // Animate particle flying out var targetX = particle.x + (Math.random() * 200 - 100); var targetY = particle.y + (Math.random() * 200 - 100); tween(particle, { x: targetX, y: targetY, alpha: 0, rotation: Math.random() * Math.PI * 2 }, { duration: 500, onFinish: function onFinish() { particle.destroy(); } }); } }; self.takeDamage = function (power) { self.health -= power; LK.effects.flashObject(self, 0xFFFFFF, 100); if (self.health <= 0) { self["break"](); // If trap glass, damage player if (self.type === 'trap') { ball.takeDamage(); } // Add score based on glass type var scoreValue = 10; if (self.type === 'moving') scoreValue = 20; if (self.type === 'trap') scoreValue = 30; LK.setScore(LK.getScore() + scoreValue); updateScoreDisplay(); } else { // Just show crack effect glassGraphics.alpha -= 0.2; } }; self.update = function () { if (self.type === 'moving' && !self.broken) { self.y += self.movingDirection * self.movingSpeed; // Reverse direction when reaching boundaries if (self.y < 500 || self.y > groundY - 400) { self.movingDirection *= -1; } } // Move obstacle toward player (creating the effect of moving forward) self.x -= ball.speed; // Remove if passed player if (self.x < -300) { if (obstacles.includes(self)) { var index = obstacles.indexOf(self); if (index !== -1) { obstacles.splice(index, 1); } } self.destroy(); } }; return self; }); var Powerup = Container.expand(function () { var self = Container.call(this); var type = "speed"; // Default var graphics; self.setup = function (powerupType) { type = powerupType || "speed"; if (type === "speed") { graphics = self.attachAsset('speedBoost', { anchorX: 0.5, anchorY: 0.5 }); } else if (type === "health") { graphics = self.attachAsset('health', { anchorX: 0.5, anchorY: 0.5 }); } }; self.collect = function () { LK.getSound('coinCollect').play(); if (type === "speed") { // Temporary speed boost var originalSpeed = ball.speed; ball.speed += 5; LK.setTimeout(function () { ball.speed = originalSpeed; }, 5000); } else if (type === "health") { // Health up to maximum 5 if (ball.health < 5) { ball.health++; updateHealthDisplay(); } } // Animation tween(self, { scaleX: 1.5, scaleY: 1.5, alpha: 0 }, { duration: 300, onFinish: function onFinish() { self.destroy(); } }); if (collectibles.indexOf(self) !== -1) { collectibles.splice(collectibles.indexOf(self), 1); } }; self.update = function () { // Rotate the powerup if (graphics) { graphics.rotation += 0.05; } // Move powerup toward player self.x -= ball.speed; // Floating animation self.y += Math.sin(LK.ticks / 8) * 0.7; // Remove if passed player if (self.x < -100) { if (collectibles.indexOf(self) !== -1) { collectibles.splice(collectibles.indexOf(self), 1); } self.destroy(); } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x87CEEB // Sky blue background }); /**** * Game Code ****/ // Game variables var obstacles = []; var collectibles = []; var groundY = 2732 - 250; // Ground position var distance = 0; var nextObstacleDistance = 500; var level = 1; var levelDistance = 2500; // Distance required to advance a level - increased for longer levels var isPaused = false; var upgradeMenuOpen = false; var lastClickTime = 0; var doubleClickSpeed = 300; // Time in ms between clicks to count as double-click var fireballUsesCount = 0; // Track the number of times the fireball button is used var maxFireballUses = 3; // Maximum number of times the fireball button can be used // Initialize ground var ground = LK.getAsset('ground', { anchorX: 0.5, anchorY: 0, scaleX: 1, scaleY: 1 }); ground.x = 2048 / 2; ground.y = groundY; game.addChild(ground); // Create player ball with steel (metal) appearance var ball = new Ball(); ball.x = 400; ball.y = groundY - ball.radius; // Set initial ball to be steel/metal type ball.power = 4; // Level 4 is steel/metal ball ball.children[0].tint = 0x666666; // Dark gray for steel game.addChild(ball); // Create UI elements // Main menu button var menuButton = new Container(); var menuButtonBg = LK.getAsset('glass', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.3, scaleY: 0.25, tint: 0x555555 }); menuButton.addChild(menuButtonBg); var menuButtonText = new Text2("MENU", { size: 70, fill: 0xFFFFFF, stroke: 0x000000, strokeThickness: 3 }); menuButtonText.anchor.set(0.5, 0.5); menuButtonText.y = 10; // Position the text a bit lower in the button menuButton.addChild(menuButtonText); menuButton.x = 120; menuButton.y = 200; // Moved further down LK.gui.topLeft.addChild(menuButton); // Fireball button removed // Handle menu button interactions menuButton.interactive = true; menuButton.down = function (x, y, obj) { LK.effects.flashObject(menuButton, 0xFFFFFF, 200); // Toggle pause when menu button is clicked isPaused = !isPaused; if (isPaused) { // Create store button var storeButton = new Container(); var storeButtonBg = LK.getAsset('glass', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.4, scaleY: 0.3, tint: 0x333333 }); storeButton.addChild(storeButtonBg); var storeButtonText = new Text2("STORE", { size: 80, fill: 0xFFD700, stroke: 0x000000, strokeThickness: 4 }); storeButtonText.anchor.set(0.5, 0.5); storeButtonText.y = 10; storeButton.addChild(storeButtonText); storeButton.x = 2048 / 2; storeButton.y = 2732 / 2; game.addChild(storeButton); // Store button interaction storeButton.interactive = true; storeButton.down = function (x, y, obj) { LK.effects.flashObject(storeButton, 0xFFFFFF, 200); // Game should stay paused when store button is clicked isPaused = true; // Remove store button when clicked game.removeChild(storeButton); // Create full-screen store page var storePage = new Container(); // Background covering the entire screen var storePageBg = LK.getAsset('glass', { anchorX: 0.5, anchorY: 0.5, scaleX: 20, scaleY: 40, tint: 0x222222, alpha: 0.95 }); storePage.addChild(storePageBg); storePage.x = 2048 / 2; storePage.y = 2732 / 2; game.addChild(storePage); // Store page title var storeTitle = new Text2("STORE", { size: 150, fill: 0xFFD700, stroke: 0x000000, strokeThickness: 6 }); storeTitle.anchor.set(0.5, 0.5); storeTitle.y = -1000; storePage.addChild(storeTitle); // Display available coins var coinsDisplay = new Text2("Your Coins: " + storage.coins, { size: 100, fill: 0xFFFFFF, stroke: 0x000000, strokeThickness: 4 }); coinsDisplay.anchor.set(0.5, 0.5); coinsDisplay.y = -800; storePage.addChild(coinsDisplay); // Fireball upgrade button var fireballButton = new Container(); var fireballBg = LK.getAsset('glass', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.8, scaleY: 0.4, tint: 0xFF6600 }); fireballButton.addChild(fireballBg); var fireballText = new Text2("FIREBALL BALL - 1000 COINS", { size: 90, fill: 0xFFFFFF, stroke: 0x000000, strokeThickness: 4 }); fireballText.anchor.set(0.5, 0.5); fireballButton.addChild(fireballText); fireballButton.y = -400; storePage.addChild(fireballButton); // Add interactive property fireballButton.interactive = true; fireballButton.down = function () { if (storage.coins >= 1000 && storage.ballLevel < 2) { // Purchase successful storage.coins -= 1000; storage.ballLevel = 2; updateCoinsDisplay(); // Update ball appearance immediately if (ball) { ball.power = 2; ball.children[0].tint = 0xFF6600; // Orange for fireball // Make fireball button visible since player now has fireball fireballButton.visible = true; } // Update ball level text ballLevelTxt.setText("Ball Power: " + storage.ballLevel); // Success message var successMsg = new Text2("PURCHASED! Fireball Activated!", { size: 80, fill: 0x00FF00, stroke: 0x000000, strokeThickness: 4 }); successMsg.anchor.set(0.5, 0.5); successMsg.y = 400; storePage.addChild(successMsg); // Make message disappear after a while LK.setTimeout(function () { storePage.removeChild(successMsg); }, 2000); } else if (storage.ballLevel >= 2) { // If fireball is already purchased, activate it if (ball.power !== 2) { // Change ball to fireball appearance ball.power = 2; ball.children[0].tint = 0xFF6600; // Orange for fireball // Success message var activateMsg = new Text2("Fireball Activated!", { size: 80, fill: 0x00FF00, stroke: 0x000000, strokeThickness: 4 }); activateMsg.anchor.set(0.5, 0.5); activateMsg.y = 400; storePage.addChild(activateMsg); // Make message disappear after a while LK.setTimeout(function () { storePage.removeChild(activateMsg); }, 2000); } else { // Already activated var alreadyMsg = new Text2("Fireball Already Active!", { size: 80, fill: 0xFFFF00, stroke: 0x000000, strokeThickness: 4 }); alreadyMsg.anchor.set(0.5, 0.5); alreadyMsg.y = 400; storePage.addChild(alreadyMsg); // Make message disappear after a while LK.setTimeout(function () { storePage.removeChild(alreadyMsg); }, 2000); } } else { // Not enough coins var errorMsg = new Text2("Not enough coins!", { size: 80, fill: 0xFF0000, stroke: 0x000000, strokeThickness: 4 }); errorMsg.anchor.set(0.5, 0.5); errorMsg.y = 400; storePage.addChild(errorMsg); // Make message disappear after a while LK.setTimeout(function () { storePage.removeChild(errorMsg); }, 2000); } // Update coins display coinsDisplay.setText("Your Coins: " + storage.coins); }; // Golden Ball upgrade button var goldenBallButton = new Container(); var goldenBallBg = LK.getAsset('glass', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.8, scaleY: 0.4, tint: 0xFFD700 // Gold color }); goldenBallButton.addChild(goldenBallBg); var goldenBallText = new Text2("GOLDEN BALL - 2000 COINS", { size: 90, fill: 0xFFFFFF, stroke: 0x000000, strokeThickness: 4 }); goldenBallText.anchor.set(0.5, 0.5); goldenBallButton.addChild(goldenBallText); goldenBallButton.y = -200; // Position below the fireball button storePage.addChild(goldenBallButton); // Add interactive property goldenBallButton.interactive = true; goldenBallButton.down = function () { if (storage.coins >= 2000 && storage.ballLevel < 3) { // Purchase successful storage.coins -= 2000; storage.ballLevel = 3; updateCoinsDisplay(); // Update ball appearance immediately if (ball) { ball.power = 3; ball.children[0].tint = 0xFFD700; // Gold color for golden ball } // Update ball level text ballLevelTxt.setText("Ball Power: " + storage.ballLevel); // Success message var successMsg = new Text2("PURCHASED! Golden Ball Activated!", { size: 80, fill: 0x00FF00, stroke: 0x000000, strokeThickness: 4 }); successMsg.anchor.set(0.5, 0.5); successMsg.y = 400; storePage.addChild(successMsg); // Make message disappear after a while LK.setTimeout(function () { storePage.removeChild(successMsg); }, 2000); } else if (storage.ballLevel >= 3) { // If golden ball is already purchased, activate it if (ball.power !== 3) { // Change ball to golden ball appearance ball.power = 3; ball.children[0].tint = 0xFFD700; // Gold color for golden ball // Success message var activateMsg = new Text2("Golden Ball Activated!", { size: 80, fill: 0x00FF00, stroke: 0x000000, strokeThickness: 4 }); activateMsg.anchor.set(0.5, 0.5); activateMsg.y = 400; storePage.addChild(activateMsg); // Make message disappear after a while LK.setTimeout(function () { storePage.removeChild(activateMsg); }, 2000); } else { // Already activated var alreadyMsg = new Text2("Golden Ball Already Active!", { size: 80, fill: 0xFFFF00, stroke: 0x000000, strokeThickness: 4 }); alreadyMsg.anchor.set(0.5, 0.5); alreadyMsg.y = 400; storePage.addChild(alreadyMsg); // Make message disappear after a while LK.setTimeout(function () { storePage.removeChild(alreadyMsg); }, 2000); } } else { // Not enough coins var errorMsg = new Text2("Not enough coins!", { size: 80, fill: 0xFF0000, stroke: 0x000000, strokeThickness: 4 }); errorMsg.anchor.set(0.5, 0.5); errorMsg.y = 400; storePage.addChild(errorMsg); // Make message disappear after a while LK.setTimeout(function () { storePage.removeChild(errorMsg); }, 2000); } // Update coins display coinsDisplay.setText("Your Coins: " + storage.coins); }; // Ice Ball upgrade button var iceBallButton = new Container(); var iceBallBg = LK.getAsset('glass', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.8, scaleY: 0.4, tint: 0x88CCFF // Light blue color for ice }); iceBallButton.addChild(iceBallBg); var iceBallText = new Text2("ICE BALL - 3000 COINS", { size: 90, fill: 0xFFFFFF, stroke: 0x000000, strokeThickness: 4 }); iceBallText.anchor.set(0.5, 0.5); iceBallButton.addChild(iceBallText); iceBallButton.y = 0; // Position below the golden ball button storePage.addChild(iceBallButton); // Poison Ball upgrade button var poisonBallButton = new Container(); var poisonBallBg = LK.getAsset('glass', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.8, scaleY: 0.4, tint: 0x00FF00 // Green color for poison }); poisonBallButton.addChild(poisonBallBg); var poisonBallText = new Text2("POISON BALL - 4000 COINS", { size: 90, fill: 0xFFFFFF, stroke: 0x000000, strokeThickness: 4 }); poisonBallText.anchor.set(0.5, 0.5); poisonBallButton.addChild(poisonBallText); poisonBallButton.y = 200; // Position below the ice ball button storePage.addChild(poisonBallButton); // Dark Ball upgrade button var darkBallButton = new Container(); var darkBallBg = LK.getAsset('glass', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.8, scaleY: 0.4, tint: 0x333333 // Dark color for dark ball }); darkBallButton.addChild(darkBallBg); var darkBallText = new Text2("DARK BALL - 5000 COINS", { size: 90, fill: 0xFFFFFF, stroke: 0x000000, strokeThickness: 4 }); darkBallText.anchor.set(0.5, 0.5); darkBallButton.addChild(darkBallText); darkBallButton.y = 400; // Position below the poison ball button storePage.addChild(darkBallButton); // Add interactive property iceBallButton.interactive = true; iceBallButton.down = function () { if (storage.coins >= 3000 && storage.ballLevel < 5) { // Purchase successful storage.coins -= 3000; storage.ballLevel = 5; updateCoinsDisplay(); // Update ball appearance immediately if (ball) { ball.power = 5; ball.children[0].tint = 0x88CCFF; // Light blue color for ice ball } // Update ball level text ballLevelTxt.setText("Ball Power: " + storage.ballLevel); // Success message var successMsg = new Text2("PURCHASED! Ice Ball Activated!", { size: 80, fill: 0x00FF00, stroke: 0x000000, strokeThickness: 4 }); successMsg.anchor.set(0.5, 0.5); successMsg.y = 400; storePage.addChild(successMsg); // Make message disappear after a while LK.setTimeout(function () { storePage.removeChild(successMsg); }, 2000); } else if (storage.ballLevel >= 5) { // If ice ball is already purchased, activate it if (ball.power !== 5) { // Change ball to ice ball appearance ball.power = 5; ball.children[0].tint = 0x88CCFF; // Light blue color for ice ball // Success message var activateMsg = new Text2("Ice Ball Activated!", { size: 80, fill: 0x00FF00, stroke: 0x000000, strokeThickness: 4 }); activateMsg.anchor.set(0.5, 0.5); activateMsg.y = 400; storePage.addChild(activateMsg); // Make message disappear after a while LK.setTimeout(function () { storePage.removeChild(activateMsg); }, 2000); } else { // Already activated var alreadyMsg = new Text2("Ice Ball Already Active!", { size: 80, fill: 0xFFFF00, stroke: 0x000000, strokeThickness: 4 }); alreadyMsg.anchor.set(0.5, 0.5); alreadyMsg.y = 400; storePage.addChild(alreadyMsg); // Make message disappear after a while LK.setTimeout(function () { storePage.removeChild(alreadyMsg); }, 2000); } } else { // Not enough coins var errorMsg = new Text2("Not enough coins!", { size: 80, fill: 0xFF0000, stroke: 0x000000, strokeThickness: 4 }); errorMsg.anchor.set(0.5, 0.5); errorMsg.y = 400; storePage.addChild(errorMsg); // Make message disappear after a while LK.setTimeout(function () { storePage.removeChild(errorMsg); }, 2000); } // Update coins display coinsDisplay.setText("Your Coins: " + storage.coins); }; // Add interactive property for Poison Ball poisonBallButton.interactive = true; poisonBallButton.down = function () { if (storage.coins >= 4000 && storage.ballLevel < 6) { // Purchase successful storage.coins -= 4000; storage.ballLevel = 6; updateCoinsDisplay(); // Update ball appearance immediately if (ball) { ball.power = 6; ball.children[0].tint = 0x00FF00; // Green color for poison ball } // Update ball level text ballLevelTxt.setText("Ball Power: " + storage.ballLevel); // Success message var successMsg = new Text2("PURCHASED! Poison Ball Activated!", { size: 80, fill: 0x00FF00, stroke: 0x000000, strokeThickness: 4 }); successMsg.anchor.set(0.5, 0.5); successMsg.y = 400; storePage.addChild(successMsg); // Make message disappear after a while LK.setTimeout(function () { storePage.removeChild(successMsg); }, 2000); } else if (storage.ballLevel >= 6) { // If poison ball is already purchased, activate it if (ball.power !== 6) { // Change ball to poison ball appearance ball.power = 6; ball.children[0].tint = 0x00FF00; // Green color for poison ball // Success message var activateMsg = new Text2("Poison Ball Activated!", { size: 80, fill: 0x00FF00, stroke: 0x000000, strokeThickness: 4 }); activateMsg.anchor.set(0.5, 0.5); activateMsg.y = 400; storePage.addChild(activateMsg); // Make message disappear after a while LK.setTimeout(function () { storePage.removeChild(activateMsg); }, 2000); } else { // Already activated var alreadyMsg = new Text2("Poison Ball Already Active!", { size: 80, fill: 0xFFFF00, stroke: 0x000000, strokeThickness: 4 }); alreadyMsg.anchor.set(0.5, 0.5); alreadyMsg.y = 400; storePage.addChild(alreadyMsg); // Make message disappear after a while LK.setTimeout(function () { storePage.removeChild(alreadyMsg); }, 2000); } } else { // Not enough coins var errorMsg = new Text2("Not enough coins!", { size: 80, fill: 0xFF0000, stroke: 0x000000, strokeThickness: 4 }); errorMsg.anchor.set(0.5, 0.5); errorMsg.y = 400; storePage.addChild(errorMsg); // Make message disappear after a while LK.setTimeout(function () { storePage.removeChild(errorMsg); }, 2000); } // Update coins display coinsDisplay.setText("Your Coins: " + storage.coins); }; // Add interactive property for Dark Ball darkBallButton.interactive = true; darkBallButton.down = function () { if (storage.coins >= 5000 && storage.ballLevel < 7) { // Purchase successful storage.coins -= 5000; storage.ballLevel = 7; updateCoinsDisplay(); // Update ball appearance immediately if (ball) { ball.power = 7; ball.children[0].tint = 0x333333; // Dark color for dark ball } // Update ball level text ballLevelTxt.setText("Ball Power: " + storage.ballLevel); // Success message var successMsg = new Text2("PURCHASED! Dark Ball Activated!", { size: 80, fill: 0x00FF00, stroke: 0x000000, strokeThickness: 4 }); successMsg.anchor.set(0.5, 0.5); successMsg.y = 400; storePage.addChild(successMsg); // Make message disappear after a while LK.setTimeout(function () { storePage.removeChild(successMsg); }, 2000); } else if (storage.ballLevel >= 7) { // If dark ball is already purchased, activate it if (ball.power !== 7) { // Change ball to dark ball appearance ball.power = 7; ball.children[0].tint = 0x333333; // Dark color for dark ball // Success message var activateMsg = new Text2("Dark Ball Activated!", { size: 80, fill: 0x00FF00, stroke: 0x000000, strokeThickness: 4 }); activateMsg.anchor.set(0.5, 0.5); activateMsg.y = 400; storePage.addChild(activateMsg); // Make message disappear after a while LK.setTimeout(function () { storePage.removeChild(activateMsg); }, 2000); } else { // Already activated var alreadyMsg = new Text2("Dark Ball Already Active!", { size: 80, fill: 0xFFFF00, stroke: 0x000000, strokeThickness: 4 }); alreadyMsg.anchor.set(0.5, 0.5); alreadyMsg.y = 400; storePage.addChild(alreadyMsg); // Make message disappear after a while LK.setTimeout(function () { storePage.removeChild(alreadyMsg); }, 2000); } } else { // Not enough coins var errorMsg = new Text2("Not enough coins!", { size: 80, fill: 0xFF0000, stroke: 0x000000, strokeThickness: 4 }); errorMsg.anchor.set(0.5, 0.5); errorMsg.y = 400; storePage.addChild(errorMsg); // Make message disappear after a while LK.setTimeout(function () { storePage.removeChild(errorMsg); }, 2000); } // Update coins display coinsDisplay.setText("Your Coins: " + storage.coins); }; // Next button var nextButton = new Container(); var nextButtonBg = LK.getAsset('glass', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.4, scaleY: 0.3, tint: 0x006600 }); nextButton.addChild(nextButtonBg); var nextButtonText = new Text2("NEXT", { size: 80, fill: 0xFFFFFF, stroke: 0x000000, strokeThickness: 4 }); nextButtonText.anchor.set(0.5, 0.5); nextButton.addChild(nextButtonText); nextButton.x = 250; nextButton.y = 1000; storePage.addChild(nextButton); // Add interactive property nextButton.interactive = true; nextButton.down = function () { // Flash button when clicked LK.effects.flashObject(nextButton, 0xFFFFFF, 200); // Hide current store page storePage.visible = false; // Create new page var newPage = new Container(); // Background covering the entire screen var newPageBg = LK.getAsset('glass', { anchorX: 0.5, anchorY: 0.5, scaleX: 20, scaleY: 40, tint: 0x222266, // Slightly different background color alpha: 0.95 }); newPage.addChild(newPageBg); newPage.x = 2048 / 2; newPage.y = 2732 / 2; game.addChild(newPage); // New page title var newPageTitle = new Text2("MORE UPGRADES", { size: 150, fill: 0xFFD700, stroke: 0x000000, strokeThickness: 6 }); newPageTitle.anchor.set(0.5, 0.5); newPageTitle.y = -1000; newPage.addChild(newPageTitle); // Display available coins var coinsDisplay = new Text2("Your Coins: " + storage.coins, { size: 100, fill: 0xFFFFFF, stroke: 0x000000, strokeThickness: 4 }); coinsDisplay.anchor.set(0.5, 0.5); coinsDisplay.y = -800; newPage.addChild(coinsDisplay); // Add Fire Power Button var firePowerButton = new Container(); var firePowerBg = LK.getAsset('glass', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.8, scaleY: 0.4, tint: 0xFF3300 // Fire red-orange color }); firePowerButton.addChild(firePowerBg); var firePowerText = new Text2("FIRE POWER - 2000 COINS", { size: 90, fill: 0xFFFFFF, stroke: 0x000000, strokeThickness: 4 }); firePowerText.anchor.set(0.5, 0.5); firePowerButton.addChild(firePowerText); firePowerButton.y = -600; // Position above speed upgrade button newPage.addChild(firePowerButton); // Add Coming Soon text for Super Powers section var comingSoonText = new Text2("COMING SOON", { size: 120, fill: 0xFFFF00, stroke: 0x000000, strokeThickness: 6 }); comingSoonText.anchor.set(0.5, 0.5); comingSoonText.y = -350; // Position below the Fire Power button newPage.addChild(comingSoonText); // Add interactive property firePowerButton.interactive = true; firePowerButton.down = function () { // Create fireballButton on the main game screen var fireballButtonOnGame = new Container(); var fireballButtonBg = LK.getAsset('glass', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.6, scaleY: 0.3, tint: 0xFF6600 // Orange for fireball }); fireballButtonOnGame.addChild(fireballButtonBg); var fireballButtonText = new Text2("Ateş Topu Butonu (3/3)", { size: 90, fill: 0xFFFFFF, stroke: 0x000000, strokeThickness: 4 }); fireballButtonText.anchor.set(0.5, 0.5); fireballButtonOnGame.addChild(fireballButtonText); fireballButtonOnGame.x = 400; // Position more to the left side fireballButtonOnGame.y = 2732 / 2; game.addChild(fireballButtonOnGame); // Make fireball button interactive fireballButtonOnGame.interactive = true; fireballButtonOnGame.down = function () { // Check if we still have uses left if (fireballUsesCount < maxFireballUses) { // Increment the usage counter fireballUsesCount++; // Update the button text to show remaining uses fireballButtonText.setText("Ateş Topu Butonu (" + (maxFireballUses - fireballUsesCount) + "/3)"); // Burn all glass obstacles for (var i = 0; i < obstacles.length; i++) { var obstacle = obstacles[i]; if (obstacle instanceof Glass && !obstacle.broken) { // Create fire effect on the glass LK.effects.flashObject(obstacle, 0xFF6600, 500); // Make glass burn with animated tint tween(obstacle.children[0], { tint: 0xFF3300 }, { duration: 500, onFinish: function onFinish() { // Store obstacle reference in a safer way to avoid cross-origin issues var glassObstacle = obstacle; // Check if glass obstacle exists and isn't broken yet if (glassObstacle && typeof glassObstacle.broken !== 'undefined' && !glassObstacle.broken) { glassObstacle["break"](); } } }); } } // If we've used all attempts, remove the button if (fireballUsesCount >= maxFireballUses) { // Show message before removing fireballButtonText.setText("Ateş Topu Hakkı Bitti!"); // Wait a moment to show the message before removing LK.setTimeout(function () { // Remove button from game game.removeChild(fireballButtonOnGame); }, 1000); } } }; // Display only fireball button text when button is clicked var fireballText = new Text2("Ateş Topu Butonu", { size: 100, fill: 0xFFFF00, stroke: 0x000000, strokeThickness: 5 }); fireballText.anchor.set(0.5, 0.5); fireballText.y = -200; newPage.addChild(fireballText); if (storage.coins >= 2000) { // Purchase successful storage.coins -= 2000; updateCoinsDisplay(); // Add fire power effect var successMsg = new Text2("PURCHASED! Fire Power Upgraded!", { size: 80, fill: 0x00FF00, stroke: 0x000000, strokeThickness: 4 }); successMsg.anchor.set(0.5, 0.5); successMsg.y = 400; newPage.addChild(successMsg); // Make message disappear after a while LK.setTimeout(function () { newPage.removeChild(successMsg); }, 2000); // Update coins display coinsDisplay.setText("Your Coins: " + storage.coins); } else { // Not enough coins var errorMsg = new Text2("Not enough coins!", { size: 80, fill: 0xFF0000, stroke: 0x000000, strokeThickness: 4 }); errorMsg.anchor.set(0.5, 0.5); errorMsg.y = 400; newPage.addChild(errorMsg); // Make message disappear after a while LK.setTimeout(function () { newPage.removeChild(errorMsg); }, 2000); } }; // Add speed upgrade button var speedUpgradeButton = new Container(); var speedUpgradeBg = LK.getAsset('glass', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.8, scaleY: 0.4, tint: 0x00AAFF }); speedUpgradeButton.addChild(speedUpgradeBg); var speedUpgradeText = new Text2("SPEED UPGRADE - 1000 COINS", { size: 90, fill: 0xFFFFFF, stroke: 0x000000, strokeThickness: 4 }); speedUpgradeText.anchor.set(0.5, 0.5); speedUpgradeButton.addChild(speedUpgradeText); speedUpgradeButton.y = -400; newPage.addChild(speedUpgradeButton); // Add interactive property speedUpgradeButton.interactive = true; speedUpgradeButton.down = function () { if (storage.coins >= 1000) { // Purchase successful storage.coins -= 1000; storage.speedLevel += 1; updateCoinsDisplay(); // Update speed display ball.speed = 5 + (storage.speedLevel - 1) * 2; speedLevelTxt.setText("Speed: " + storage.speedLevel); // Success message var successMsg = new Text2("PURCHASED! Speed Upgraded!", { size: 80, fill: 0x00FF00, stroke: 0x000000, strokeThickness: 4 }); successMsg.anchor.set(0.5, 0.5); successMsg.y = 400; newPage.addChild(successMsg); // Make message disappear after a while LK.setTimeout(function () { newPage.removeChild(successMsg); }, 2000); // Update coins display coinsDisplay.setText("Your Coins: " + storage.coins); } else { // Not enough coins var errorMsg = new Text2("Not enough coins!", { size: 80, fill: 0xFF0000, stroke: 0x000000, strokeThickness: 4 }); errorMsg.anchor.set(0.5, 0.5); errorMsg.y = 400; newPage.addChild(errorMsg); // Make message disappear after a while LK.setTimeout(function () { newPage.removeChild(errorMsg); }, 2000); } }; // Close button var closeButton = new Container(); var closeButtonBg = LK.getAsset('glass', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.4, scaleY: 0.3, tint: 0x990000 }); closeButton.addChild(closeButtonBg); var closeButtonText = new Text2("CLOSE", { size: 80, fill: 0xFFFFFF, stroke: 0x000000, strokeThickness: 4 }); closeButtonText.anchor.set(0.5, 0.5); closeButton.addChild(closeButtonText); closeButton.x = 0; closeButton.y = 1000; newPage.addChild(closeButton); // Add interactive property closeButton.interactive = true; closeButton.down = function () { game.removeChild(newPage); storePage.visible = true; }; // Back button var backButton = new Container(); var backButtonBg = LK.getAsset('glass', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.4, scaleY: 0.3, tint: 0x006666 }); backButton.addChild(backButtonBg); var backButtonText = new Text2("BACK", { size: 80, fill: 0xFFFFFF, stroke: 0x000000, strokeThickness: 4 }); backButtonText.anchor.set(0.5, 0.5); backButton.addChild(backButtonText); backButton.x = -250; backButton.y = 1000; newPage.addChild(backButton); // Add interactive property backButton.interactive = true; backButton.down = function () { game.removeChild(newPage); storePage.visible = true; }; }; // Close button var closeButton = new Container(); var closeButtonBg = LK.getAsset('glass', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.4, scaleY: 0.3, tint: 0x990000 }); closeButton.addChild(closeButtonBg); var closeButtonText = new Text2("CLOSE", { size: 80, fill: 0xFFFFFF, stroke: 0x000000, strokeThickness: 4 }); closeButtonText.anchor.set(0.5, 0.5); closeButton.addChild(closeButtonText); closeButton.x = -250; closeButton.y = 1000; storePage.addChild(closeButton); // Add interactive property closeButton.interactive = true; closeButton.down = function () { game.removeChild(storePage); }; }; } else { // Remove all store UI elements if game is resumed for (var i = game.children.length - 1; i >= 0; i--) { var child = game.children[i]; if (child instanceof Container && child.children.length > 0 && child.children[0].tint === 0x333333) { game.removeChild(child); } } } }; var scoreTxt = new Text2("Score: 0", { size: 85, fill: 0xFFFFFF, stroke: 0x000000, strokeThickness: 4 }); scoreTxt.anchor.set(0, 0); LK.gui.topRight.addChild(scoreTxt); var coinsTxt = new Text2("Coins: " + storage.coins, { size: 85, fill: 0xFFD700, stroke: 0x000000, strokeThickness: 4 }); coinsTxt.anchor.set(0, 0); coinsTxt.y = 60; LK.gui.topRight.addChild(coinsTxt); var levelTxt = new Text2("Level: 1", { size: 90, fill: 0xFFFFFF, stroke: 0x000000, strokeThickness: 5 }); levelTxt.anchor.set(0.5, 0); LK.gui.top.addChild(levelTxt); var healthDisplay = new Container(); LK.gui.topLeft.addChild(healthDisplay); healthDisplay.x = 120; // Add some margin from the left edge healthDisplay.y = 50; // Show stats and upgrades var ballLevelTxt = new Text2("Ball Power: " + storage.ballLevel, { size: 75, fill: 0xFFFFFF, stroke: 0x000000, strokeThickness: 4 }); ballLevelTxt.anchor.set(0, 0); ballLevelTxt.y = 150; LK.gui.topRight.addChild(ballLevelTxt); var speedLevelTxt = new Text2("Speed: " + storage.speedLevel, { size: 75, fill: 0xFFFFFF, stroke: 0x000000, strokeThickness: 4 }); speedLevelTxt.anchor.set(0, 0); speedLevelTxt.y = 200; LK.gui.topRight.addChild(speedLevelTxt); function updateHealthDisplay() { // Clear existing health icons while (healthDisplay.children.length > 0) { healthDisplay.removeChild(healthDisplay.children[0]); } // Add health icons for (var i = 0; i < ball.health; i++) { var healthIcon = LK.getAsset('health', { anchorX: 0.5, anchorY: 0.5, x: i * 40, y: 0, scaleX: 1, scaleY: 1 }); healthDisplay.addChild(healthIcon); } } function updateScoreDisplay() { scoreTxt.setText("Score: " + LK.getScore()); } function updateCoinsDisplay() { coinsTxt.setText("Coins: " + storage.coins); } function updateLevelDisplay() { levelTxt.setText("Level: " + level); } function createGlassObstacle() { var glass = new Glass(); // Determine glass type based on level var typeRand = Math.random(); var type = 'normal'; if (level >= 3 && typeRand > 0.7) { type = 'moving'; } else if (level >= 5 && typeRand > 0.8) { type = 'trap'; } // Create additional obstacles for longer levels if (Math.random() > 0.6) { var secondGlass = new Glass(); var secondType = 'normal'; if (level >= 2 && typeRand > 0.6) { secondType = 'moving'; } else if (level >= 4 && typeRand > 0.85) { secondType = 'trap'; } secondGlass.setup(secondType, Math.min(Math.floor(level / 2) + 1, 5)); secondGlass.x = 2500 + Math.random() * 300 + 200; secondGlass.y = Math.random() * (groundY - 700) + 500; game.addChild(secondGlass); obstacles.push(secondGlass); } // Difficulty increases with level var difficulty = Math.min(Math.floor(level / 2) + 1, 5); glass.setup(type, difficulty); // Position glass ahead of player glass.x = 2500; // Random vertical position for some obstacles if (type === 'normal') { glass.y = groundY - glass.height / 2; } else { glass.y = Math.random() * (groundY - 700) + 500; } game.addChild(glass); obstacles.push(glass); // Sometimes add coins near obstacles if (Math.random() > 0.5) { createCoin(glass.x + Math.random() * 300 - 150, glass.y - 200 - Math.random() * 200); } // Sometimes add powerups if (Math.random() > 0.9) { if (Math.random() > 0.7) { createPowerup("health", glass.x + Math.random() * 300 - 150, glass.y - 300); } else { createPowerup("speed", glass.x + Math.random() * 300 - 150, glass.y - 300); } } // Sometimes add ground obstacles if (level >= 2 && Math.random() > 0.7) { var groundObstacle = LK.getAsset('obstacleGround', { anchorX: 0.5, anchorY: 0.5, x: glass.x - 300 - Math.random() * 300, y: groundY - 50 }); game.addChild(groundObstacle); // Add to obstacles array var obstacleObj = { object: groundObstacle, x: groundObstacle.x, update: function update() { this.x -= ball.speed; this.object.x = this.x; if (this.object.x < -200) { game.removeChild(this.object); var index = obstacles.indexOf(this); if (index !== -1) { obstacles.splice(index, 1); } } } }; obstacles.push(obstacleObj); } } function createCoin(x, y) { var coin = new Coin(); coin.x = x; coin.y = y; game.addChild(coin); collectibles.push(coin); } function createPowerup(type, x, y) { var powerup = new Powerup(); powerup.setup(type); powerup.x = x; powerup.y = y; game.addChild(powerup); collectibles.push(powerup); } function checkCollisions() { // Check collision with obstacles for (var i = 0; i < obstacles.length; i++) { var obstacle = obstacles[i]; // Handle different obstacle types if (obstacle instanceof Glass) { // Distance-based check for glass var dx = ball.x - obstacle.x; var dy = ball.y - obstacle.y; var distance = Math.sqrt(dx * dx + dy * dy); // Consider the obstacle width if (distance < ball.radius + obstacle.width / 2 && !obstacle.broken) { // Handle collision if (ball.power >= obstacle.health) { obstacle.takeDamage(ball.power); } else { // Ball not powerful enough - take damage ball.takeDamage(); // Player loses health when can't break glass obstacle.takeDamage(ball.power); } } // Check if ball passed through unbroken glass if (ball.x > obstacle.x && !obstacle.lastPassed && !obstacle.broken) { obstacle.lastPassed = true; ball.takeDamage(); // Lose health when passing through unbroken glass } } else if (obstacle.object && obstacle.object.name === 'obstacleGround') { // Simple box collision for ground obstacles var obstacleObj = obstacle.object; var ballRight = ball.x + ball.radius; var ballLeft = ball.x - ball.radius; var ballBottom = ball.y + ball.radius; var obstacleLeft = obstacleObj.x - obstacleObj.width / 2; var obstacleRight = obstacleObj.x + obstacleObj.width / 2; var obstacleTop = obstacleObj.y - obstacleObj.height / 2; if (ballRight > obstacleLeft && ballLeft < obstacleRight && ballBottom > obstacleTop) { // Collision with ground obstacle (jump over it!) ball.takeDamage(); LK.effects.flashObject(obstacleObj, 0xFF0000, 300); } } } // Check collision with collectibles for (var j = collectibles.length - 1; j >= 0; j--) { var collectible = collectibles[j]; var cdx = ball.x - collectible.x; var cdy = ball.y - collectible.y; var cDistance = Math.sqrt(cdx * cdx + cdy * cdy); if (cDistance < ball.radius + 25) { // Collect item if (collectible instanceof Coin) { collectible.collect(); collectibles.splice(j, 1); } else if (collectible instanceof Powerup) { collectible.collect(); // Removing from collectibles is handled in the collect method } } } } function updateLevel() { if (distance >= level * levelDistance) { level++; updateLevelDisplay(); LK.effects.flashScreen(0xFFFFFF, 500); // Award coins for completing level storage.coins += 500; updateCoinsDisplay(); // Show level up message var levelUpText = new Text2("Level " + level + " Complete! +500 coins", { size: 130, fill: 0xFFFFFF, stroke: 0x000000, strokeThickness: 10 }); levelUpText.anchor.set(0.5, 0.5); levelUpText.x = 2048 / 2; levelUpText.y = 2732 / 2; game.addChild(levelUpText); // Animate and remove tween(levelUpText, { alpha: 0, scaleX: 2, scaleY: 2 }, { duration: 1000, onFinish: function onFinish() { game.removeChild(levelUpText); } }); } } // Initialize game state updateHealthDisplay(); updateScoreDisplay(); updateCoinsDisplay(); updateLevelDisplay(); // Play background music LK.playMusic('gameMusic'); // Handle game controls game.down = function (x, y, obj) { // Get current time for double-click detection var currentTime = Date.now(); var timeDiff = currentTime - lastClickTime; // Check if this is a double-click if (timeDiff < doubleClickSpeed) { // This is a double-click - jump higher ball.jump(true); // Reset click timer to prevent triple-click detection lastClickTime = 0; } else { // This is a single click - normal jump ball.jump(false); // Store time of this click lastClickTime = currentTime; } }; game.move = function (x, y, obj) { // Not used for main game controls }; game.up = function (x, y, obj) { // Not used for main game controls }; // Main game update loop game.update = function () { if (isPaused || upgradeMenuOpen) return; // Update player ball.update(); // Update obstacles for (var i = obstacles.length - 1; i >= 0; i--) { var obstacle = obstacles[i]; obstacle.update(); } // Update collectibles for (var j = collectibles.length - 1; j >= 0; j--) { var collectible = collectibles[j]; collectible.update(); } // Check for collisions checkCollisions(); // Update distance traveled distance += ball.speed; // Check for level advancement updateLevel(); // Generate obstacles if (distance > nextObstacleDistance) { createGlassObstacle(); // Increased obstacle spacing to match longer levels nextObstacleDistance = distance + 700 + Math.random() * 800; } // Update high score if (LK.getScore() > storage.highScore) { storage.highScore = LK.getScore(); } };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
coins: 0,
highScore: 0,
ballLevel: 1,
speedLevel: 1
});
/****
* Classes
****/
var Ball = Container.expand(function () {
var self = Container.call(this);
var ballGraphics = self.attachAsset('ball', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1,
scaleY: 1
});
self.velocityY = 0;
self.gravity = 0.5;
self.jumpForce = -15;
self.grounded = false;
self.canJump = true;
self.health = 3;
self.speed = 5 + (storage.speedLevel - 1) * 2;
self.radius = 50;
self.power = storage.ballLevel;
// Set color based on ball level/power
if (self.power === 2) {
ballGraphics.tint = 0xFF6600; // Orange for fireball
} else if (self.power === 3) {
ballGraphics.tint = 0xFFD700; // Gold color for golden ball
} else if (self.power === 4) {
ballGraphics.tint = 0x666666; // Dark gray for steel
} else if (self.power === 5) {
ballGraphics.tint = 0x88CCFF; // Light blue for ice ball
} else if (self.power === 6) {
ballGraphics.tint = 0x00FF00; // Green color for poison ball
} else if (self.power === 7) {
ballGraphics.tint = 0x333333; // Dark color for dark ball
}
self.jump = function (isDoubleJump) {
if (self.grounded && self.canJump) {
// Higher jump force for double-click
if (isDoubleJump) {
self.velocityY = self.jumpForce * 1.5; // 50% higher jump
LK.effects.flashObject(self, 0x00FF00, 300); // Visual feedback - green flash
} else {
self.velocityY = self.jumpForce;
}
self.grounded = false;
LK.getSound('jump').play();
// Jump cooldown
self.canJump = false;
LK.setTimeout(function () {
self.canJump = true;
}, 300);
}
};
self.takeDamage = function () {
self.health--;
LK.getSound('damage').play();
LK.effects.flashObject(self, 0xFF0000, 500);
// Visual feedback for health loss
var healthLossText = new Text2("-1 Health!", {
size: 90,
fill: 0xFF0000,
stroke: 0x000000,
strokeThickness: 5
});
healthLossText.anchor.set(0.5, 0.5);
healthLossText.x = self.x;
healthLossText.y = self.y - 100;
game.addChild(healthLossText);
// Animate and remove
tween(healthLossText, {
y: healthLossText.y - 150,
alpha: 0
}, {
duration: 800,
onFinish: function onFinish() {
game.removeChild(healthLossText);
}
});
// Update the health display
updateHealthDisplay();
if (self.health <= 0) {
LK.showGameOver();
}
};
self.update = function () {
// Apply gravity
self.velocityY += self.gravity;
self.y += self.velocityY;
// Visual rotation to simulate rolling
ballGraphics.rotation += 0.05 * self.speed;
// Ground collision
if (self.y + self.radius > groundY) {
self.y = groundY - self.radius;
self.velocityY = 0;
self.grounded = true;
}
};
return self;
});
var Coin = Container.expand(function () {
var self = Container.call(this);
var coinGraphics = self.attachAsset('coin', {
anchorX: 0.5,
anchorY: 0.5
});
self.collected = false;
self.collect = function () {
if (!self.collected) {
self.collected = true;
LK.getSound('coinCollect').play();
// Animation
tween(self, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0
}, {
duration: 300,
onFinish: function onFinish() {
self.destroy();
}
});
// Update coins
storage.coins += 1;
updateCoinsDisplay();
}
};
self.update = function () {
// Coin rotation animation
coinGraphics.rotation += 0.05;
// Move coin toward player
self.x -= ball.speed;
// Floating animation
self.y += Math.sin(LK.ticks / 10) * 0.5;
// Remove if passed player
if (self.x < -100) {
if (collectibles.indexOf(self) !== -1) {
collectibles.splice(collectibles.indexOf(self), 1);
}
self.destroy();
}
};
return self;
});
var Glass = Container.expand(function () {
var self = Container.call(this);
var glassGraphics = self.attachAsset('glass', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.7
});
self.width = glassGraphics.width;
self.height = glassGraphics.height;
self.broken = false;
self.health = 1;
self.type = 'normal'; // normal, moving, trap
self.movingDirection = 1;
self.movingSpeed = 0;
self.lastPassed = false; // Track if ball has passed through this glass
self.setup = function (type, difficulty) {
self.type = type || 'normal';
self.health = difficulty || 1;
if (self.type === 'moving') {
self.movingSpeed = Math.random() * 3 + 2;
glassGraphics.tint = 0x33CCFF;
} else if (self.type === 'trap') {
glassGraphics.tint = 0xFF3333;
}
};
self["break"] = function () {
if (!self.broken) {
self.broken = true;
LK.getSound('glassBreak').play();
// Create glass breaking particles
self.createParticles();
// Remove from active obstacles after animation
LK.setTimeout(function () {
if (obstacles.includes(self)) {
var index = obstacles.indexOf(self);
if (index !== -1) {
obstacles.splice(index, 1);
}
}
self.destroy();
}, 100);
}
};
self.createParticles = function () {
for (var i = 0; i < 8; i++) {
var particle = LK.getAsset('particleGlass', {
anchorX: 0.5,
anchorY: 0.5,
x: self.x + (Math.random() * 100 - 50),
y: self.y + (Math.random() * 100 - 50),
alpha: 0.8
});
game.addChild(particle);
// Animate particle flying out
var targetX = particle.x + (Math.random() * 200 - 100);
var targetY = particle.y + (Math.random() * 200 - 100);
tween(particle, {
x: targetX,
y: targetY,
alpha: 0,
rotation: Math.random() * Math.PI * 2
}, {
duration: 500,
onFinish: function onFinish() {
particle.destroy();
}
});
}
};
self.takeDamage = function (power) {
self.health -= power;
LK.effects.flashObject(self, 0xFFFFFF, 100);
if (self.health <= 0) {
self["break"]();
// If trap glass, damage player
if (self.type === 'trap') {
ball.takeDamage();
}
// Add score based on glass type
var scoreValue = 10;
if (self.type === 'moving') scoreValue = 20;
if (self.type === 'trap') scoreValue = 30;
LK.setScore(LK.getScore() + scoreValue);
updateScoreDisplay();
} else {
// Just show crack effect
glassGraphics.alpha -= 0.2;
}
};
self.update = function () {
if (self.type === 'moving' && !self.broken) {
self.y += self.movingDirection * self.movingSpeed;
// Reverse direction when reaching boundaries
if (self.y < 500 || self.y > groundY - 400) {
self.movingDirection *= -1;
}
}
// Move obstacle toward player (creating the effect of moving forward)
self.x -= ball.speed;
// Remove if passed player
if (self.x < -300) {
if (obstacles.includes(self)) {
var index = obstacles.indexOf(self);
if (index !== -1) {
obstacles.splice(index, 1);
}
}
self.destroy();
}
};
return self;
});
var Powerup = Container.expand(function () {
var self = Container.call(this);
var type = "speed"; // Default
var graphics;
self.setup = function (powerupType) {
type = powerupType || "speed";
if (type === "speed") {
graphics = self.attachAsset('speedBoost', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (type === "health") {
graphics = self.attachAsset('health', {
anchorX: 0.5,
anchorY: 0.5
});
}
};
self.collect = function () {
LK.getSound('coinCollect').play();
if (type === "speed") {
// Temporary speed boost
var originalSpeed = ball.speed;
ball.speed += 5;
LK.setTimeout(function () {
ball.speed = originalSpeed;
}, 5000);
} else if (type === "health") {
// Health up to maximum 5
if (ball.health < 5) {
ball.health++;
updateHealthDisplay();
}
}
// Animation
tween(self, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0
}, {
duration: 300,
onFinish: function onFinish() {
self.destroy();
}
});
if (collectibles.indexOf(self) !== -1) {
collectibles.splice(collectibles.indexOf(self), 1);
}
};
self.update = function () {
// Rotate the powerup
if (graphics) {
graphics.rotation += 0.05;
}
// Move powerup toward player
self.x -= ball.speed;
// Floating animation
self.y += Math.sin(LK.ticks / 8) * 0.7;
// Remove if passed player
if (self.x < -100) {
if (collectibles.indexOf(self) !== -1) {
collectibles.splice(collectibles.indexOf(self), 1);
}
self.destroy();
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87CEEB // Sky blue background
});
/****
* Game Code
****/
// Game variables
var obstacles = [];
var collectibles = [];
var groundY = 2732 - 250; // Ground position
var distance = 0;
var nextObstacleDistance = 500;
var level = 1;
var levelDistance = 2500; // Distance required to advance a level - increased for longer levels
var isPaused = false;
var upgradeMenuOpen = false;
var lastClickTime = 0;
var doubleClickSpeed = 300; // Time in ms between clicks to count as double-click
var fireballUsesCount = 0; // Track the number of times the fireball button is used
var maxFireballUses = 3; // Maximum number of times the fireball button can be used
// Initialize ground
var ground = LK.getAsset('ground', {
anchorX: 0.5,
anchorY: 0,
scaleX: 1,
scaleY: 1
});
ground.x = 2048 / 2;
ground.y = groundY;
game.addChild(ground);
// Create player ball with steel (metal) appearance
var ball = new Ball();
ball.x = 400;
ball.y = groundY - ball.radius;
// Set initial ball to be steel/metal type
ball.power = 4; // Level 4 is steel/metal ball
ball.children[0].tint = 0x666666; // Dark gray for steel
game.addChild(ball);
// Create UI elements
// Main menu button
var menuButton = new Container();
var menuButtonBg = LK.getAsset('glass', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3,
scaleY: 0.25,
tint: 0x555555
});
menuButton.addChild(menuButtonBg);
var menuButtonText = new Text2("MENU", {
size: 70,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 3
});
menuButtonText.anchor.set(0.5, 0.5);
menuButtonText.y = 10; // Position the text a bit lower in the button
menuButton.addChild(menuButtonText);
menuButton.x = 120;
menuButton.y = 200; // Moved further down
LK.gui.topLeft.addChild(menuButton);
// Fireball button removed
// Handle menu button interactions
menuButton.interactive = true;
menuButton.down = function (x, y, obj) {
LK.effects.flashObject(menuButton, 0xFFFFFF, 200);
// Toggle pause when menu button is clicked
isPaused = !isPaused;
if (isPaused) {
// Create store button
var storeButton = new Container();
var storeButtonBg = LK.getAsset('glass', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.4,
scaleY: 0.3,
tint: 0x333333
});
storeButton.addChild(storeButtonBg);
var storeButtonText = new Text2("STORE", {
size: 80,
fill: 0xFFD700,
stroke: 0x000000,
strokeThickness: 4
});
storeButtonText.anchor.set(0.5, 0.5);
storeButtonText.y = 10;
storeButton.addChild(storeButtonText);
storeButton.x = 2048 / 2;
storeButton.y = 2732 / 2;
game.addChild(storeButton);
// Store button interaction
storeButton.interactive = true;
storeButton.down = function (x, y, obj) {
LK.effects.flashObject(storeButton, 0xFFFFFF, 200);
// Game should stay paused when store button is clicked
isPaused = true;
// Remove store button when clicked
game.removeChild(storeButton);
// Create full-screen store page
var storePage = new Container();
// Background covering the entire screen
var storePageBg = LK.getAsset('glass', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 20,
scaleY: 40,
tint: 0x222222,
alpha: 0.95
});
storePage.addChild(storePageBg);
storePage.x = 2048 / 2;
storePage.y = 2732 / 2;
game.addChild(storePage);
// Store page title
var storeTitle = new Text2("STORE", {
size: 150,
fill: 0xFFD700,
stroke: 0x000000,
strokeThickness: 6
});
storeTitle.anchor.set(0.5, 0.5);
storeTitle.y = -1000;
storePage.addChild(storeTitle);
// Display available coins
var coinsDisplay = new Text2("Your Coins: " + storage.coins, {
size: 100,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 4
});
coinsDisplay.anchor.set(0.5, 0.5);
coinsDisplay.y = -800;
storePage.addChild(coinsDisplay);
// Fireball upgrade button
var fireballButton = new Container();
var fireballBg = LK.getAsset('glass', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.4,
tint: 0xFF6600
});
fireballButton.addChild(fireballBg);
var fireballText = new Text2("FIREBALL BALL - 1000 COINS", {
size: 90,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 4
});
fireballText.anchor.set(0.5, 0.5);
fireballButton.addChild(fireballText);
fireballButton.y = -400;
storePage.addChild(fireballButton);
// Add interactive property
fireballButton.interactive = true;
fireballButton.down = function () {
if (storage.coins >= 1000 && storage.ballLevel < 2) {
// Purchase successful
storage.coins -= 1000;
storage.ballLevel = 2;
updateCoinsDisplay();
// Update ball appearance immediately
if (ball) {
ball.power = 2;
ball.children[0].tint = 0xFF6600; // Orange for fireball
// Make fireball button visible since player now has fireball
fireballButton.visible = true;
}
// Update ball level text
ballLevelTxt.setText("Ball Power: " + storage.ballLevel);
// Success message
var successMsg = new Text2("PURCHASED! Fireball Activated!", {
size: 80,
fill: 0x00FF00,
stroke: 0x000000,
strokeThickness: 4
});
successMsg.anchor.set(0.5, 0.5);
successMsg.y = 400;
storePage.addChild(successMsg);
// Make message disappear after a while
LK.setTimeout(function () {
storePage.removeChild(successMsg);
}, 2000);
} else if (storage.ballLevel >= 2) {
// If fireball is already purchased, activate it
if (ball.power !== 2) {
// Change ball to fireball appearance
ball.power = 2;
ball.children[0].tint = 0xFF6600; // Orange for fireball
// Success message
var activateMsg = new Text2("Fireball Activated!", {
size: 80,
fill: 0x00FF00,
stroke: 0x000000,
strokeThickness: 4
});
activateMsg.anchor.set(0.5, 0.5);
activateMsg.y = 400;
storePage.addChild(activateMsg);
// Make message disappear after a while
LK.setTimeout(function () {
storePage.removeChild(activateMsg);
}, 2000);
} else {
// Already activated
var alreadyMsg = new Text2("Fireball Already Active!", {
size: 80,
fill: 0xFFFF00,
stroke: 0x000000,
strokeThickness: 4
});
alreadyMsg.anchor.set(0.5, 0.5);
alreadyMsg.y = 400;
storePage.addChild(alreadyMsg);
// Make message disappear after a while
LK.setTimeout(function () {
storePage.removeChild(alreadyMsg);
}, 2000);
}
} else {
// Not enough coins
var errorMsg = new Text2("Not enough coins!", {
size: 80,
fill: 0xFF0000,
stroke: 0x000000,
strokeThickness: 4
});
errorMsg.anchor.set(0.5, 0.5);
errorMsg.y = 400;
storePage.addChild(errorMsg);
// Make message disappear after a while
LK.setTimeout(function () {
storePage.removeChild(errorMsg);
}, 2000);
}
// Update coins display
coinsDisplay.setText("Your Coins: " + storage.coins);
};
// Golden Ball upgrade button
var goldenBallButton = new Container();
var goldenBallBg = LK.getAsset('glass', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.4,
tint: 0xFFD700 // Gold color
});
goldenBallButton.addChild(goldenBallBg);
var goldenBallText = new Text2("GOLDEN BALL - 2000 COINS", {
size: 90,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 4
});
goldenBallText.anchor.set(0.5, 0.5);
goldenBallButton.addChild(goldenBallText);
goldenBallButton.y = -200; // Position below the fireball button
storePage.addChild(goldenBallButton);
// Add interactive property
goldenBallButton.interactive = true;
goldenBallButton.down = function () {
if (storage.coins >= 2000 && storage.ballLevel < 3) {
// Purchase successful
storage.coins -= 2000;
storage.ballLevel = 3;
updateCoinsDisplay();
// Update ball appearance immediately
if (ball) {
ball.power = 3;
ball.children[0].tint = 0xFFD700; // Gold color for golden ball
}
// Update ball level text
ballLevelTxt.setText("Ball Power: " + storage.ballLevel);
// Success message
var successMsg = new Text2("PURCHASED! Golden Ball Activated!", {
size: 80,
fill: 0x00FF00,
stroke: 0x000000,
strokeThickness: 4
});
successMsg.anchor.set(0.5, 0.5);
successMsg.y = 400;
storePage.addChild(successMsg);
// Make message disappear after a while
LK.setTimeout(function () {
storePage.removeChild(successMsg);
}, 2000);
} else if (storage.ballLevel >= 3) {
// If golden ball is already purchased, activate it
if (ball.power !== 3) {
// Change ball to golden ball appearance
ball.power = 3;
ball.children[0].tint = 0xFFD700; // Gold color for golden ball
// Success message
var activateMsg = new Text2("Golden Ball Activated!", {
size: 80,
fill: 0x00FF00,
stroke: 0x000000,
strokeThickness: 4
});
activateMsg.anchor.set(0.5, 0.5);
activateMsg.y = 400;
storePage.addChild(activateMsg);
// Make message disappear after a while
LK.setTimeout(function () {
storePage.removeChild(activateMsg);
}, 2000);
} else {
// Already activated
var alreadyMsg = new Text2("Golden Ball Already Active!", {
size: 80,
fill: 0xFFFF00,
stroke: 0x000000,
strokeThickness: 4
});
alreadyMsg.anchor.set(0.5, 0.5);
alreadyMsg.y = 400;
storePage.addChild(alreadyMsg);
// Make message disappear after a while
LK.setTimeout(function () {
storePage.removeChild(alreadyMsg);
}, 2000);
}
} else {
// Not enough coins
var errorMsg = new Text2("Not enough coins!", {
size: 80,
fill: 0xFF0000,
stroke: 0x000000,
strokeThickness: 4
});
errorMsg.anchor.set(0.5, 0.5);
errorMsg.y = 400;
storePage.addChild(errorMsg);
// Make message disappear after a while
LK.setTimeout(function () {
storePage.removeChild(errorMsg);
}, 2000);
}
// Update coins display
coinsDisplay.setText("Your Coins: " + storage.coins);
};
// Ice Ball upgrade button
var iceBallButton = new Container();
var iceBallBg = LK.getAsset('glass', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.4,
tint: 0x88CCFF // Light blue color for ice
});
iceBallButton.addChild(iceBallBg);
var iceBallText = new Text2("ICE BALL - 3000 COINS", {
size: 90,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 4
});
iceBallText.anchor.set(0.5, 0.5);
iceBallButton.addChild(iceBallText);
iceBallButton.y = 0; // Position below the golden ball button
storePage.addChild(iceBallButton);
// Poison Ball upgrade button
var poisonBallButton = new Container();
var poisonBallBg = LK.getAsset('glass', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.4,
tint: 0x00FF00 // Green color for poison
});
poisonBallButton.addChild(poisonBallBg);
var poisonBallText = new Text2("POISON BALL - 4000 COINS", {
size: 90,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 4
});
poisonBallText.anchor.set(0.5, 0.5);
poisonBallButton.addChild(poisonBallText);
poisonBallButton.y = 200; // Position below the ice ball button
storePage.addChild(poisonBallButton);
// Dark Ball upgrade button
var darkBallButton = new Container();
var darkBallBg = LK.getAsset('glass', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.4,
tint: 0x333333 // Dark color for dark ball
});
darkBallButton.addChild(darkBallBg);
var darkBallText = new Text2("DARK BALL - 5000 COINS", {
size: 90,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 4
});
darkBallText.anchor.set(0.5, 0.5);
darkBallButton.addChild(darkBallText);
darkBallButton.y = 400; // Position below the poison ball button
storePage.addChild(darkBallButton);
// Add interactive property
iceBallButton.interactive = true;
iceBallButton.down = function () {
if (storage.coins >= 3000 && storage.ballLevel < 5) {
// Purchase successful
storage.coins -= 3000;
storage.ballLevel = 5;
updateCoinsDisplay();
// Update ball appearance immediately
if (ball) {
ball.power = 5;
ball.children[0].tint = 0x88CCFF; // Light blue color for ice ball
}
// Update ball level text
ballLevelTxt.setText("Ball Power: " + storage.ballLevel);
// Success message
var successMsg = new Text2("PURCHASED! Ice Ball Activated!", {
size: 80,
fill: 0x00FF00,
stroke: 0x000000,
strokeThickness: 4
});
successMsg.anchor.set(0.5, 0.5);
successMsg.y = 400;
storePage.addChild(successMsg);
// Make message disappear after a while
LK.setTimeout(function () {
storePage.removeChild(successMsg);
}, 2000);
} else if (storage.ballLevel >= 5) {
// If ice ball is already purchased, activate it
if (ball.power !== 5) {
// Change ball to ice ball appearance
ball.power = 5;
ball.children[0].tint = 0x88CCFF; // Light blue color for ice ball
// Success message
var activateMsg = new Text2("Ice Ball Activated!", {
size: 80,
fill: 0x00FF00,
stroke: 0x000000,
strokeThickness: 4
});
activateMsg.anchor.set(0.5, 0.5);
activateMsg.y = 400;
storePage.addChild(activateMsg);
// Make message disappear after a while
LK.setTimeout(function () {
storePage.removeChild(activateMsg);
}, 2000);
} else {
// Already activated
var alreadyMsg = new Text2("Ice Ball Already Active!", {
size: 80,
fill: 0xFFFF00,
stroke: 0x000000,
strokeThickness: 4
});
alreadyMsg.anchor.set(0.5, 0.5);
alreadyMsg.y = 400;
storePage.addChild(alreadyMsg);
// Make message disappear after a while
LK.setTimeout(function () {
storePage.removeChild(alreadyMsg);
}, 2000);
}
} else {
// Not enough coins
var errorMsg = new Text2("Not enough coins!", {
size: 80,
fill: 0xFF0000,
stroke: 0x000000,
strokeThickness: 4
});
errorMsg.anchor.set(0.5, 0.5);
errorMsg.y = 400;
storePage.addChild(errorMsg);
// Make message disappear after a while
LK.setTimeout(function () {
storePage.removeChild(errorMsg);
}, 2000);
}
// Update coins display
coinsDisplay.setText("Your Coins: " + storage.coins);
};
// Add interactive property for Poison Ball
poisonBallButton.interactive = true;
poisonBallButton.down = function () {
if (storage.coins >= 4000 && storage.ballLevel < 6) {
// Purchase successful
storage.coins -= 4000;
storage.ballLevel = 6;
updateCoinsDisplay();
// Update ball appearance immediately
if (ball) {
ball.power = 6;
ball.children[0].tint = 0x00FF00; // Green color for poison ball
}
// Update ball level text
ballLevelTxt.setText("Ball Power: " + storage.ballLevel);
// Success message
var successMsg = new Text2("PURCHASED! Poison Ball Activated!", {
size: 80,
fill: 0x00FF00,
stroke: 0x000000,
strokeThickness: 4
});
successMsg.anchor.set(0.5, 0.5);
successMsg.y = 400;
storePage.addChild(successMsg);
// Make message disappear after a while
LK.setTimeout(function () {
storePage.removeChild(successMsg);
}, 2000);
} else if (storage.ballLevel >= 6) {
// If poison ball is already purchased, activate it
if (ball.power !== 6) {
// Change ball to poison ball appearance
ball.power = 6;
ball.children[0].tint = 0x00FF00; // Green color for poison ball
// Success message
var activateMsg = new Text2("Poison Ball Activated!", {
size: 80,
fill: 0x00FF00,
stroke: 0x000000,
strokeThickness: 4
});
activateMsg.anchor.set(0.5, 0.5);
activateMsg.y = 400;
storePage.addChild(activateMsg);
// Make message disappear after a while
LK.setTimeout(function () {
storePage.removeChild(activateMsg);
}, 2000);
} else {
// Already activated
var alreadyMsg = new Text2("Poison Ball Already Active!", {
size: 80,
fill: 0xFFFF00,
stroke: 0x000000,
strokeThickness: 4
});
alreadyMsg.anchor.set(0.5, 0.5);
alreadyMsg.y = 400;
storePage.addChild(alreadyMsg);
// Make message disappear after a while
LK.setTimeout(function () {
storePage.removeChild(alreadyMsg);
}, 2000);
}
} else {
// Not enough coins
var errorMsg = new Text2("Not enough coins!", {
size: 80,
fill: 0xFF0000,
stroke: 0x000000,
strokeThickness: 4
});
errorMsg.anchor.set(0.5, 0.5);
errorMsg.y = 400;
storePage.addChild(errorMsg);
// Make message disappear after a while
LK.setTimeout(function () {
storePage.removeChild(errorMsg);
}, 2000);
}
// Update coins display
coinsDisplay.setText("Your Coins: " + storage.coins);
};
// Add interactive property for Dark Ball
darkBallButton.interactive = true;
darkBallButton.down = function () {
if (storage.coins >= 5000 && storage.ballLevel < 7) {
// Purchase successful
storage.coins -= 5000;
storage.ballLevel = 7;
updateCoinsDisplay();
// Update ball appearance immediately
if (ball) {
ball.power = 7;
ball.children[0].tint = 0x333333; // Dark color for dark ball
}
// Update ball level text
ballLevelTxt.setText("Ball Power: " + storage.ballLevel);
// Success message
var successMsg = new Text2("PURCHASED! Dark Ball Activated!", {
size: 80,
fill: 0x00FF00,
stroke: 0x000000,
strokeThickness: 4
});
successMsg.anchor.set(0.5, 0.5);
successMsg.y = 400;
storePage.addChild(successMsg);
// Make message disappear after a while
LK.setTimeout(function () {
storePage.removeChild(successMsg);
}, 2000);
} else if (storage.ballLevel >= 7) {
// If dark ball is already purchased, activate it
if (ball.power !== 7) {
// Change ball to dark ball appearance
ball.power = 7;
ball.children[0].tint = 0x333333; // Dark color for dark ball
// Success message
var activateMsg = new Text2("Dark Ball Activated!", {
size: 80,
fill: 0x00FF00,
stroke: 0x000000,
strokeThickness: 4
});
activateMsg.anchor.set(0.5, 0.5);
activateMsg.y = 400;
storePage.addChild(activateMsg);
// Make message disappear after a while
LK.setTimeout(function () {
storePage.removeChild(activateMsg);
}, 2000);
} else {
// Already activated
var alreadyMsg = new Text2("Dark Ball Already Active!", {
size: 80,
fill: 0xFFFF00,
stroke: 0x000000,
strokeThickness: 4
});
alreadyMsg.anchor.set(0.5, 0.5);
alreadyMsg.y = 400;
storePage.addChild(alreadyMsg);
// Make message disappear after a while
LK.setTimeout(function () {
storePage.removeChild(alreadyMsg);
}, 2000);
}
} else {
// Not enough coins
var errorMsg = new Text2("Not enough coins!", {
size: 80,
fill: 0xFF0000,
stroke: 0x000000,
strokeThickness: 4
});
errorMsg.anchor.set(0.5, 0.5);
errorMsg.y = 400;
storePage.addChild(errorMsg);
// Make message disappear after a while
LK.setTimeout(function () {
storePage.removeChild(errorMsg);
}, 2000);
}
// Update coins display
coinsDisplay.setText("Your Coins: " + storage.coins);
};
// Next button
var nextButton = new Container();
var nextButtonBg = LK.getAsset('glass', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.4,
scaleY: 0.3,
tint: 0x006600
});
nextButton.addChild(nextButtonBg);
var nextButtonText = new Text2("NEXT", {
size: 80,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 4
});
nextButtonText.anchor.set(0.5, 0.5);
nextButton.addChild(nextButtonText);
nextButton.x = 250;
nextButton.y = 1000;
storePage.addChild(nextButton);
// Add interactive property
nextButton.interactive = true;
nextButton.down = function () {
// Flash button when clicked
LK.effects.flashObject(nextButton, 0xFFFFFF, 200);
// Hide current store page
storePage.visible = false;
// Create new page
var newPage = new Container();
// Background covering the entire screen
var newPageBg = LK.getAsset('glass', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 20,
scaleY: 40,
tint: 0x222266,
// Slightly different background color
alpha: 0.95
});
newPage.addChild(newPageBg);
newPage.x = 2048 / 2;
newPage.y = 2732 / 2;
game.addChild(newPage);
// New page title
var newPageTitle = new Text2("MORE UPGRADES", {
size: 150,
fill: 0xFFD700,
stroke: 0x000000,
strokeThickness: 6
});
newPageTitle.anchor.set(0.5, 0.5);
newPageTitle.y = -1000;
newPage.addChild(newPageTitle);
// Display available coins
var coinsDisplay = new Text2("Your Coins: " + storage.coins, {
size: 100,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 4
});
coinsDisplay.anchor.set(0.5, 0.5);
coinsDisplay.y = -800;
newPage.addChild(coinsDisplay);
// Add Fire Power Button
var firePowerButton = new Container();
var firePowerBg = LK.getAsset('glass', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.4,
tint: 0xFF3300 // Fire red-orange color
});
firePowerButton.addChild(firePowerBg);
var firePowerText = new Text2("FIRE POWER - 2000 COINS", {
size: 90,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 4
});
firePowerText.anchor.set(0.5, 0.5);
firePowerButton.addChild(firePowerText);
firePowerButton.y = -600; // Position above speed upgrade button
newPage.addChild(firePowerButton);
// Add Coming Soon text for Super Powers section
var comingSoonText = new Text2("COMING SOON", {
size: 120,
fill: 0xFFFF00,
stroke: 0x000000,
strokeThickness: 6
});
comingSoonText.anchor.set(0.5, 0.5);
comingSoonText.y = -350; // Position below the Fire Power button
newPage.addChild(comingSoonText);
// Add interactive property
firePowerButton.interactive = true;
firePowerButton.down = function () {
// Create fireballButton on the main game screen
var fireballButtonOnGame = new Container();
var fireballButtonBg = LK.getAsset('glass', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 0.3,
tint: 0xFF6600 // Orange for fireball
});
fireballButtonOnGame.addChild(fireballButtonBg);
var fireballButtonText = new Text2("Ateş Topu Butonu (3/3)", {
size: 90,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 4
});
fireballButtonText.anchor.set(0.5, 0.5);
fireballButtonOnGame.addChild(fireballButtonText);
fireballButtonOnGame.x = 400; // Position more to the left side
fireballButtonOnGame.y = 2732 / 2;
game.addChild(fireballButtonOnGame);
// Make fireball button interactive
fireballButtonOnGame.interactive = true;
fireballButtonOnGame.down = function () {
// Check if we still have uses left
if (fireballUsesCount < maxFireballUses) {
// Increment the usage counter
fireballUsesCount++;
// Update the button text to show remaining uses
fireballButtonText.setText("Ateş Topu Butonu (" + (maxFireballUses - fireballUsesCount) + "/3)");
// Burn all glass obstacles
for (var i = 0; i < obstacles.length; i++) {
var obstacle = obstacles[i];
if (obstacle instanceof Glass && !obstacle.broken) {
// Create fire effect on the glass
LK.effects.flashObject(obstacle, 0xFF6600, 500);
// Make glass burn with animated tint
tween(obstacle.children[0], {
tint: 0xFF3300
}, {
duration: 500,
onFinish: function onFinish() {
// Store obstacle reference in a safer way to avoid cross-origin issues
var glassObstacle = obstacle;
// Check if glass obstacle exists and isn't broken yet
if (glassObstacle && typeof glassObstacle.broken !== 'undefined' && !glassObstacle.broken) {
glassObstacle["break"]();
}
}
});
}
}
// If we've used all attempts, remove the button
if (fireballUsesCount >= maxFireballUses) {
// Show message before removing
fireballButtonText.setText("Ateş Topu Hakkı Bitti!");
// Wait a moment to show the message before removing
LK.setTimeout(function () {
// Remove button from game
game.removeChild(fireballButtonOnGame);
}, 1000);
}
}
};
// Display only fireball button text when button is clicked
var fireballText = new Text2("Ateş Topu Butonu", {
size: 100,
fill: 0xFFFF00,
stroke: 0x000000,
strokeThickness: 5
});
fireballText.anchor.set(0.5, 0.5);
fireballText.y = -200;
newPage.addChild(fireballText);
if (storage.coins >= 2000) {
// Purchase successful
storage.coins -= 2000;
updateCoinsDisplay();
// Add fire power effect
var successMsg = new Text2("PURCHASED! Fire Power Upgraded!", {
size: 80,
fill: 0x00FF00,
stroke: 0x000000,
strokeThickness: 4
});
successMsg.anchor.set(0.5, 0.5);
successMsg.y = 400;
newPage.addChild(successMsg);
// Make message disappear after a while
LK.setTimeout(function () {
newPage.removeChild(successMsg);
}, 2000);
// Update coins display
coinsDisplay.setText("Your Coins: " + storage.coins);
} else {
// Not enough coins
var errorMsg = new Text2("Not enough coins!", {
size: 80,
fill: 0xFF0000,
stroke: 0x000000,
strokeThickness: 4
});
errorMsg.anchor.set(0.5, 0.5);
errorMsg.y = 400;
newPage.addChild(errorMsg);
// Make message disappear after a while
LK.setTimeout(function () {
newPage.removeChild(errorMsg);
}, 2000);
}
};
// Add speed upgrade button
var speedUpgradeButton = new Container();
var speedUpgradeBg = LK.getAsset('glass', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.4,
tint: 0x00AAFF
});
speedUpgradeButton.addChild(speedUpgradeBg);
var speedUpgradeText = new Text2("SPEED UPGRADE - 1000 COINS", {
size: 90,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 4
});
speedUpgradeText.anchor.set(0.5, 0.5);
speedUpgradeButton.addChild(speedUpgradeText);
speedUpgradeButton.y = -400;
newPage.addChild(speedUpgradeButton);
// Add interactive property
speedUpgradeButton.interactive = true;
speedUpgradeButton.down = function () {
if (storage.coins >= 1000) {
// Purchase successful
storage.coins -= 1000;
storage.speedLevel += 1;
updateCoinsDisplay();
// Update speed display
ball.speed = 5 + (storage.speedLevel - 1) * 2;
speedLevelTxt.setText("Speed: " + storage.speedLevel);
// Success message
var successMsg = new Text2("PURCHASED! Speed Upgraded!", {
size: 80,
fill: 0x00FF00,
stroke: 0x000000,
strokeThickness: 4
});
successMsg.anchor.set(0.5, 0.5);
successMsg.y = 400;
newPage.addChild(successMsg);
// Make message disappear after a while
LK.setTimeout(function () {
newPage.removeChild(successMsg);
}, 2000);
// Update coins display
coinsDisplay.setText("Your Coins: " + storage.coins);
} else {
// Not enough coins
var errorMsg = new Text2("Not enough coins!", {
size: 80,
fill: 0xFF0000,
stroke: 0x000000,
strokeThickness: 4
});
errorMsg.anchor.set(0.5, 0.5);
errorMsg.y = 400;
newPage.addChild(errorMsg);
// Make message disappear after a while
LK.setTimeout(function () {
newPage.removeChild(errorMsg);
}, 2000);
}
};
// Close button
var closeButton = new Container();
var closeButtonBg = LK.getAsset('glass', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.4,
scaleY: 0.3,
tint: 0x990000
});
closeButton.addChild(closeButtonBg);
var closeButtonText = new Text2("CLOSE", {
size: 80,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 4
});
closeButtonText.anchor.set(0.5, 0.5);
closeButton.addChild(closeButtonText);
closeButton.x = 0;
closeButton.y = 1000;
newPage.addChild(closeButton);
// Add interactive property
closeButton.interactive = true;
closeButton.down = function () {
game.removeChild(newPage);
storePage.visible = true;
};
// Back button
var backButton = new Container();
var backButtonBg = LK.getAsset('glass', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.4,
scaleY: 0.3,
tint: 0x006666
});
backButton.addChild(backButtonBg);
var backButtonText = new Text2("BACK", {
size: 80,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 4
});
backButtonText.anchor.set(0.5, 0.5);
backButton.addChild(backButtonText);
backButton.x = -250;
backButton.y = 1000;
newPage.addChild(backButton);
// Add interactive property
backButton.interactive = true;
backButton.down = function () {
game.removeChild(newPage);
storePage.visible = true;
};
};
// Close button
var closeButton = new Container();
var closeButtonBg = LK.getAsset('glass', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.4,
scaleY: 0.3,
tint: 0x990000
});
closeButton.addChild(closeButtonBg);
var closeButtonText = new Text2("CLOSE", {
size: 80,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 4
});
closeButtonText.anchor.set(0.5, 0.5);
closeButton.addChild(closeButtonText);
closeButton.x = -250;
closeButton.y = 1000;
storePage.addChild(closeButton);
// Add interactive property
closeButton.interactive = true;
closeButton.down = function () {
game.removeChild(storePage);
};
};
} else {
// Remove all store UI elements if game is resumed
for (var i = game.children.length - 1; i >= 0; i--) {
var child = game.children[i];
if (child instanceof Container && child.children.length > 0 && child.children[0].tint === 0x333333) {
game.removeChild(child);
}
}
}
};
var scoreTxt = new Text2("Score: 0", {
size: 85,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 4
});
scoreTxt.anchor.set(0, 0);
LK.gui.topRight.addChild(scoreTxt);
var coinsTxt = new Text2("Coins: " + storage.coins, {
size: 85,
fill: 0xFFD700,
stroke: 0x000000,
strokeThickness: 4
});
coinsTxt.anchor.set(0, 0);
coinsTxt.y = 60;
LK.gui.topRight.addChild(coinsTxt);
var levelTxt = new Text2("Level: 1", {
size: 90,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 5
});
levelTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(levelTxt);
var healthDisplay = new Container();
LK.gui.topLeft.addChild(healthDisplay);
healthDisplay.x = 120; // Add some margin from the left edge
healthDisplay.y = 50;
// Show stats and upgrades
var ballLevelTxt = new Text2("Ball Power: " + storage.ballLevel, {
size: 75,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 4
});
ballLevelTxt.anchor.set(0, 0);
ballLevelTxt.y = 150;
LK.gui.topRight.addChild(ballLevelTxt);
var speedLevelTxt = new Text2("Speed: " + storage.speedLevel, {
size: 75,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 4
});
speedLevelTxt.anchor.set(0, 0);
speedLevelTxt.y = 200;
LK.gui.topRight.addChild(speedLevelTxt);
function updateHealthDisplay() {
// Clear existing health icons
while (healthDisplay.children.length > 0) {
healthDisplay.removeChild(healthDisplay.children[0]);
}
// Add health icons
for (var i = 0; i < ball.health; i++) {
var healthIcon = LK.getAsset('health', {
anchorX: 0.5,
anchorY: 0.5,
x: i * 40,
y: 0,
scaleX: 1,
scaleY: 1
});
healthDisplay.addChild(healthIcon);
}
}
function updateScoreDisplay() {
scoreTxt.setText("Score: " + LK.getScore());
}
function updateCoinsDisplay() {
coinsTxt.setText("Coins: " + storage.coins);
}
function updateLevelDisplay() {
levelTxt.setText("Level: " + level);
}
function createGlassObstacle() {
var glass = new Glass();
// Determine glass type based on level
var typeRand = Math.random();
var type = 'normal';
if (level >= 3 && typeRand > 0.7) {
type = 'moving';
} else if (level >= 5 && typeRand > 0.8) {
type = 'trap';
}
// Create additional obstacles for longer levels
if (Math.random() > 0.6) {
var secondGlass = new Glass();
var secondType = 'normal';
if (level >= 2 && typeRand > 0.6) {
secondType = 'moving';
} else if (level >= 4 && typeRand > 0.85) {
secondType = 'trap';
}
secondGlass.setup(secondType, Math.min(Math.floor(level / 2) + 1, 5));
secondGlass.x = 2500 + Math.random() * 300 + 200;
secondGlass.y = Math.random() * (groundY - 700) + 500;
game.addChild(secondGlass);
obstacles.push(secondGlass);
}
// Difficulty increases with level
var difficulty = Math.min(Math.floor(level / 2) + 1, 5);
glass.setup(type, difficulty);
// Position glass ahead of player
glass.x = 2500;
// Random vertical position for some obstacles
if (type === 'normal') {
glass.y = groundY - glass.height / 2;
} else {
glass.y = Math.random() * (groundY - 700) + 500;
}
game.addChild(glass);
obstacles.push(glass);
// Sometimes add coins near obstacles
if (Math.random() > 0.5) {
createCoin(glass.x + Math.random() * 300 - 150, glass.y - 200 - Math.random() * 200);
}
// Sometimes add powerups
if (Math.random() > 0.9) {
if (Math.random() > 0.7) {
createPowerup("health", glass.x + Math.random() * 300 - 150, glass.y - 300);
} else {
createPowerup("speed", glass.x + Math.random() * 300 - 150, glass.y - 300);
}
}
// Sometimes add ground obstacles
if (level >= 2 && Math.random() > 0.7) {
var groundObstacle = LK.getAsset('obstacleGround', {
anchorX: 0.5,
anchorY: 0.5,
x: glass.x - 300 - Math.random() * 300,
y: groundY - 50
});
game.addChild(groundObstacle);
// Add to obstacles array
var obstacleObj = {
object: groundObstacle,
x: groundObstacle.x,
update: function update() {
this.x -= ball.speed;
this.object.x = this.x;
if (this.object.x < -200) {
game.removeChild(this.object);
var index = obstacles.indexOf(this);
if (index !== -1) {
obstacles.splice(index, 1);
}
}
}
};
obstacles.push(obstacleObj);
}
}
function createCoin(x, y) {
var coin = new Coin();
coin.x = x;
coin.y = y;
game.addChild(coin);
collectibles.push(coin);
}
function createPowerup(type, x, y) {
var powerup = new Powerup();
powerup.setup(type);
powerup.x = x;
powerup.y = y;
game.addChild(powerup);
collectibles.push(powerup);
}
function checkCollisions() {
// Check collision with obstacles
for (var i = 0; i < obstacles.length; i++) {
var obstacle = obstacles[i];
// Handle different obstacle types
if (obstacle instanceof Glass) {
// Distance-based check for glass
var dx = ball.x - obstacle.x;
var dy = ball.y - obstacle.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Consider the obstacle width
if (distance < ball.radius + obstacle.width / 2 && !obstacle.broken) {
// Handle collision
if (ball.power >= obstacle.health) {
obstacle.takeDamage(ball.power);
} else {
// Ball not powerful enough - take damage
ball.takeDamage(); // Player loses health when can't break glass
obstacle.takeDamage(ball.power);
}
}
// Check if ball passed through unbroken glass
if (ball.x > obstacle.x && !obstacle.lastPassed && !obstacle.broken) {
obstacle.lastPassed = true;
ball.takeDamage(); // Lose health when passing through unbroken glass
}
} else if (obstacle.object && obstacle.object.name === 'obstacleGround') {
// Simple box collision for ground obstacles
var obstacleObj = obstacle.object;
var ballRight = ball.x + ball.radius;
var ballLeft = ball.x - ball.radius;
var ballBottom = ball.y + ball.radius;
var obstacleLeft = obstacleObj.x - obstacleObj.width / 2;
var obstacleRight = obstacleObj.x + obstacleObj.width / 2;
var obstacleTop = obstacleObj.y - obstacleObj.height / 2;
if (ballRight > obstacleLeft && ballLeft < obstacleRight && ballBottom > obstacleTop) {
// Collision with ground obstacle (jump over it!)
ball.takeDamage();
LK.effects.flashObject(obstacleObj, 0xFF0000, 300);
}
}
}
// Check collision with collectibles
for (var j = collectibles.length - 1; j >= 0; j--) {
var collectible = collectibles[j];
var cdx = ball.x - collectible.x;
var cdy = ball.y - collectible.y;
var cDistance = Math.sqrt(cdx * cdx + cdy * cdy);
if (cDistance < ball.radius + 25) {
// Collect item
if (collectible instanceof Coin) {
collectible.collect();
collectibles.splice(j, 1);
} else if (collectible instanceof Powerup) {
collectible.collect();
// Removing from collectibles is handled in the collect method
}
}
}
}
function updateLevel() {
if (distance >= level * levelDistance) {
level++;
updateLevelDisplay();
LK.effects.flashScreen(0xFFFFFF, 500);
// Award coins for completing level
storage.coins += 500;
updateCoinsDisplay();
// Show level up message
var levelUpText = new Text2("Level " + level + " Complete! +500 coins", {
size: 130,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 10
});
levelUpText.anchor.set(0.5, 0.5);
levelUpText.x = 2048 / 2;
levelUpText.y = 2732 / 2;
game.addChild(levelUpText);
// Animate and remove
tween(levelUpText, {
alpha: 0,
scaleX: 2,
scaleY: 2
}, {
duration: 1000,
onFinish: function onFinish() {
game.removeChild(levelUpText);
}
});
}
}
// Initialize game state
updateHealthDisplay();
updateScoreDisplay();
updateCoinsDisplay();
updateLevelDisplay();
// Play background music
LK.playMusic('gameMusic');
// Handle game controls
game.down = function (x, y, obj) {
// Get current time for double-click detection
var currentTime = Date.now();
var timeDiff = currentTime - lastClickTime;
// Check if this is a double-click
if (timeDiff < doubleClickSpeed) {
// This is a double-click - jump higher
ball.jump(true);
// Reset click timer to prevent triple-click detection
lastClickTime = 0;
} else {
// This is a single click - normal jump
ball.jump(false);
// Store time of this click
lastClickTime = currentTime;
}
};
game.move = function (x, y, obj) {
// Not used for main game controls
};
game.up = function (x, y, obj) {
// Not used for main game controls
};
// Main game update loop
game.update = function () {
if (isPaused || upgradeMenuOpen) return;
// Update player
ball.update();
// Update obstacles
for (var i = obstacles.length - 1; i >= 0; i--) {
var obstacle = obstacles[i];
obstacle.update();
}
// Update collectibles
for (var j = collectibles.length - 1; j >= 0; j--) {
var collectible = collectibles[j];
collectible.update();
}
// Check for collisions
checkCollisions();
// Update distance traveled
distance += ball.speed;
// Check for level advancement
updateLevel();
// Generate obstacles
if (distance > nextObstacleDistance) {
createGlassObstacle();
// Increased obstacle spacing to match longer levels
nextObstacleDistance = distance + 700 + Math.random() * 800;
}
// Update high score
if (LK.getScore() > storage.highScore) {
storage.highScore = LK.getScore();
}
};