/****
* 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();
}
};