/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
highScores: ["1000", "750", "500", "250", "100"]
});
/****
* Classes
****/
// Define a class for scrolling background
var Background = Container.expand(function () {
var self = Container.call(this);
// Create two background images for continuous scrolling
var bg1 = self.attachAsset('background', {
anchorX: 0,
anchorY: 0
});
var bg2 = self.attachAsset('background', {
anchorX: 0,
anchorY: 0
});
// Position the second background right after the first one
bg2.x = bg1.width;
self.speed = 2; // Scrolling speed
// Track positions for animation
self.update = function () {
// Move both backgrounds to the left
bg1.x -= self.speed;
bg2.x -= self.speed;
// When the first background moves completely off-screen to the left
if (bg1.x <= -bg1.width) {
bg1.x = bg2.x + bg2.width;
}
// When the second background moves completely off-screen to the left
if (bg2.x <= -bg2.width) {
bg2.x = bg1.x + bg1.width;
}
};
return self;
});
// Define a class for enemies
var Enemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 5;
self.type = Math.floor(Math.random() * 3); // Different enemy types for variety
self.passed = false;
// Apply visual variations based on enemy type
if (self.type === 1) {
// Slightly larger enemy
enemyGraphics.scale.set(1.2, 1.2);
} else if (self.type === 2) {
// Rotated enemy
enemyGraphics.rotation = Math.PI / 6; // 30 degrees rotation
}
self.update = function () {
self.x -= self.speed;
if (self.x < -50) {
self.destroy();
}
};
});
// Define a class for fast enemies
var EnemyFast = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8; // Faster than regular enemies
self.type = 'fast';
self.passed = false;
// Apply visual variations - make it smaller and red tinted
enemyGraphics.scale.set(0.8, 0.8);
enemyGraphics.tint = 0xFF0000; // Red tint
self.update = function () {
self.x -= self.speed;
if (self.x < -50) {
self.destroy();
}
};
});
// Level class to manage level configuration
var Level = Container.expand(function () {
var self = Container.call(this);
// Level data properties
self.levelNumber = 1;
self.enemySpeed = 5;
self.enemySpawnRate = 100;
self.backgroundColor = 0x87CEEB;
// Level text display
var levelText = new Text2('LEVEL 1', {
size: 80,
fill: 0xFFFFFF
});
levelText.anchor.set(0.5, 0.5);
self.addChild(levelText);
// Show level indicator briefly
self.showLevelIndicator = function (level) {
self.levelNumber = level;
levelText.setText('LEVEL ' + level);
self.visible = true;
// Fade in level text
levelText.alpha = 0;
tween(levelText, {
alpha: 1
}, {
duration: 500
});
// Hide after delay
LK.setTimeout(function () {
// Fade out
tween(levelText, {
alpha: 0
}, {
duration: 500,
onFinish: function onFinish() {
self.visible = false;
}
});
}, 2000);
};
// Configure level properties based on level number
self.configure = function (level) {
self.levelNumber = level;
// Increasing difficulty with each level (reduced scaling)
self.enemySpeed = 5 + level * 0.2; // Increased enemy speed
self.enemySpawnRate = Math.max(60, 120 - level * 0.8); // Less frequent spawns
// Change background color every 10 levels
var colorIndex = Math.floor((level - 1) / 10);
var backgroundColors = [0x87CEEB,
// Sky blue
0xFFA500,
// Orange
0x9370DB,
// Purple
0x228B22,
// Forest green
0xB22222 // Fire brick red
];
self.backgroundColor = backgroundColors[colorIndex % backgroundColors.length];
// Show level text
self.showLevelIndicator(level);
return self;
};
// Initially hide the level indicator
self.visible = false;
return self;
});
//<Assets used in the game will automatically appear here>
// Define a class for the player character
var Player = Container.expand(function () {
var self = Container.call(this);
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 15; // Increased speed for easier jumps
self.jumpHeight = 35; // Moderately increased jump height
self.isJumping = false;
self.velocityY = 0;
self.isInvincible = false;
self.invincibilityTime = 0;
self.update = function () {
if (self.isJumping) {
self.y += self.velocityY;
self.velocityY += 0.5; // Decreased gravity effect for slower fall
if (self.y >= 2732 / 2) {
self.y = 2732 / 2;
self.isJumping = false;
self.velocityY = 0;
}
}
// Handle invincibility
if (self.isInvincible) {
self.invincibilityTime--;
playerGraphics.alpha = Math.sin(LK.ticks * 0.2) * 0.5 + 0.5; // Flashing effect
if (self.invincibilityTime <= 0) {
self.isInvincible = false;
playerGraphics.alpha = 1;
}
}
};
self.jump = function () {
if (!self.isJumping) {
self.isJumping = true;
self.velocityY = -self.jumpHeight;
LK.getSound('jump').play();
}
};
});
// Import tween plugin for animations
// PowerUp class to provide player with special abilities
var PowerUp = Container.expand(function () {
var self = Container.call(this);
// Create a simple colorful circle for the power-up
var powerUpGraphics = self.attachAsset('centerCircle', {
anchorX: 0.5,
anchorY: 0.5
});
// Set a bright color to make it stand out
powerUpGraphics.tint = 0xFFFF00; // Yellow color
powerUpGraphics.scale.set(0.5, 0.5);
self.speed = 4; // Move slightly slower than enemies
self.type = 'invincibility'; // Default type
// Add a pulsing animation effect
self.update = function () {
self.x -= self.speed;
// Pulse animation
var scale = 0.5 + Math.sin(LK.ticks * 0.1) * 0.1;
powerUpGraphics.scale.set(scale, scale);
// Remove if off-screen
if (self.x < -50) {
self.destroy();
}
};
return self;
});
// Scoreboard class to display high scores
var Scoreboard = Container.expand(function () {
var self = Container.call(this);
// Create a background for the scoreboard
var background = self.attachAsset('soundOff', {
anchorX: 0.5,
anchorY: 0.5
});
// Scale the background to make it larger
background.width = 800;
background.height = 1200;
background.alpha = 0.8;
// Create title text with improved styling
var titleText = new Text2('HIGH SCORES', {
size: 90,
fill: 0xFFD700
});
titleText.anchor.set(0.5, 0);
titleText.y = -background.height / 2 + 50;
self.addChild(titleText);
// Create current score display with improved styling
var currentScoreLabel = new Text2('YOUR SCORE:', {
size: 65,
fill: 0xFFFFFF
});
currentScoreLabel.anchor.set(0.5, 0);
currentScoreLabel.y = -background.height / 2 + 140;
self.addChild(currentScoreLabel);
var currentScoreText = new Text2('0', {
size: 80,
fill: 0xFFD700 // Gold color
});
currentScoreText.anchor.set(0.5, 0);
currentScoreText.y = -background.height / 2 + 200;
self.addChild(currentScoreText);
// Array to store score text objects
self.scoreItems = [];
// Add decorative divider
var divider = self.attachAsset('soundOff', {
anchorX: 0.5,
anchorY: 0.5
});
divider.width = 700;
divider.height = 4;
divider.y = -background.height / 2 + 240;
divider.alpha = 0.5;
divider.tint = 0xFFD700;
// Method to update the scoreboard with new scores
self.updateScores = function (scores) {
// Update current score
currentScoreText.setText(LK.getScore().toString());
// Clear previous score texts
for (var i = 0; i < self.scoreItems.length; i++) {
self.scoreItems[i].destroy();
}
self.scoreItems = [];
// Add new score items with improved visual hierarchy
for (var i = 0; i < scores.length && i < 5; i++) {
// Determine if this is the current score that was just achieved
var isCurrentScore = LK.getScore() === parseInt(scores[i]);
// Add rank indicator
var rankText = new Text2(i + 1 + '.', {
size: 60,
fill: isCurrentScore ? 0xFFD700 : 0xFFFFFF
});
rankText.anchor.set(1, 0);
rankText.x = -100;
rankText.y = -background.height / 2 + 280 + i * 90;
self.addChild(rankText);
self.scoreItems.push(rankText);
// Add score value with separated styling
var scoreText = new Text2(scores[i], {
size: 65,
fill: isCurrentScore ? 0xFFD700 : 0xFFFFFF // Gold color for current score
});
scoreText.anchor.set(0, 0);
scoreText.x = -70;
scoreText.y = -background.height / 2 + 280 + i * 90;
// Add highlight effect for current score
if (isCurrentScore) {
// Create a background highlight
var highlight = self.attachAsset('soundOff', {
anchorX: 0.5,
anchorY: 0.5,
width: 600,
height: 70
});
highlight.x = 0;
highlight.y = scoreText.y + scoreText.height / 2;
highlight.alpha = 0.2;
highlight.tint = 0xFFD700; // Gold tint
self.scoreItems.push(highlight);
// Add pulsing animation to highlight
tween(highlight, {
alpha: 0.05
}, {
duration: 800,
repeat: -1,
yoyo: true
});
// Add "NEW!" indicator for new high score
if (i === 0 && isCurrentScore) {
var newIndicator = new Text2('NEW!', {
size: 50,
fill: 0xFF0000
});
newIndicator.anchor.set(0, 0.5);
newIndicator.x = 150;
newIndicator.y = highlight.y;
self.addChild(newIndicator);
self.scoreItems.push(newIndicator);
// Add blinking animation
tween(newIndicator, {
alpha: 0.5
}, {
duration: 500,
repeat: -1,
yoyo: true
});
}
}
self.addChild(scoreText);
self.scoreItems.push(scoreText);
}
// Update title with achievements
if (LK.getScore() > 0) {
var achievementText = "";
if (LK.getScore() >= parseInt(scores[0])) {
achievementText = "NEW HIGH SCORE!";
} else if (LK.getScore() >= parseInt(scores[4])) {
achievementText = "TOP 5 SCORE!";
}
if (achievementText) {
var achievement = new Text2(achievementText, {
size: 50,
fill: 0xFF9900
});
achievement.anchor.set(0.5, 0);
achievement.y = -background.height / 2 + 100;
self.addChild(achievement);
self.scoreItems.push(achievement);
}
}
};
// Add improved close button
var closeButton = new Container();
closeButton.x = 0;
closeButton.y = background.height / 2 - 100;
closeButton.interactive = true;
// Add button background for better visibility
var buttonBg = closeButton.attachAsset('soundOff', {
anchorX: 0.5,
anchorY: 0.5,
width: 200,
height: 80
});
buttonBg.alpha = 0.4;
buttonBg.tint = 0x444444;
// Add button text
var buttonText = new Text2('CLOSE', {
size: 60,
fill: 0xFFFFFF
});
buttonText.anchor.set(0.5, 0.5);
closeButton.addChild(buttonText);
closeButton.down = function () {
self.visible = false;
};
self.addChild(closeButton);
// Initially hide scoreboard
self.visible = false;
return self;
});
// Sound toggle button class
var SoundButton = Container.expand(function () {
var self = Container.call(this);
// Track sound state
self.soundOn = true;
// Create both button states
var soundOnGraphics = self.attachAsset('soundOn', {
anchorX: 0.5,
anchorY: 0.5
});
var soundOffGraphics = self.attachAsset('soundOff', {
anchorX: 0.5,
anchorY: 0.5
});
// Initially hide the sound off icon
soundOffGraphics.visible = false;
// Handle button press
self.down = function () {
self.soundOn = !self.soundOn;
soundOnGraphics.visible = self.soundOn;
soundOffGraphics.visible = !self.soundOn;
// Set global sound volume based on state
LK.setVolume(self.soundOn ? 1 : 0);
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87CEEB // Sky blue background
});
/****
* Game Code
****/
// Initialize current level
var currentLevel = 1;
var levelsCompleted = 0;
var scoreToNextLevel = 5; // Reduced initial points for level progression
var lastLevel = 50;
var powerUps = []; // Array to store power-ups
var powerUpSpawnCounter = 0;
var powerUpSpawnInterval = 600; // Spawn power-up every 10 seconds (60fps * 10)
// Create level indicator
var levelManager = new Level();
LK.gui.center.addChild(levelManager);
levelManager.configure(currentLevel);
// Create scrolling background
var background = game.addChild(new Background());
background.x = 0;
background.y = 0;
// Initialize player
var player = game.addChild(new Player());
player.x = 2048 / 2;
player.y = 2732 / 2;
player.isInvincible = true; // Start player with invincibility
player.invincibilityTime = 180; // 3 seconds at 60fps
// Play background music with fade in effect
LK.playMusic('Music1', {
fade: {
start: 0,
end: 1,
duration: 1000
}
});
// Add sound toggle button to top right corner
var soundButton = new SoundButton();
soundButton.x = 2048 - 80;
soundButton.y = 80;
LK.gui.topRight.addChild(soundButton);
// Initialize enemies
var enemies = [];
var enemySpawnInterval = 100;
var enemySpawnCounter = 0;
// Add level display text
var levelDisplayText = new Text2('Level: 1', {
size: 60,
fill: 0xFFFFFF
});
levelDisplayText.anchor.set(0, 0);
levelDisplayText.x = 150;
levelDisplayText.y = 150;
LK.gui.topLeft.addChild(levelDisplayText);
// Create a new Text2 object to display the score
var scoreText = new Text2('Score: 0', {
size: 100,
fill: 0xFFFFFF
});
// Position the score text correctly
scoreText.anchor.set(0.5, 0);
// Add the score text to the game GUI at the top center of the screen
LK.gui.top.addChild(scoreText);
// Create scoreboard button
var scoreboardButton = new Text2('SCORES', {
size: 60,
fill: 0xFFFFFF
});
scoreboardButton.anchor.set(0.5, 0);
LK.gui.topLeft.addChild(scoreboardButton);
scoreboardButton.x = 150;
scoreboardButton.y = 80;
// Create and initialize the scoreboard
var scoreboard = new Scoreboard();
scoreboard.x = 2048 / 2;
scoreboard.y = 2732 / 2;
LK.gui.center.addChild(scoreboard);
// Create high score notification text
var highScoreNotification = new Text2('', {
size: 120,
fill: 0xFFD700 // Gold color
});
highScoreNotification.anchor.set(0.5, 0);
highScoreNotification.y = 350;
highScoreNotification.visible = false;
LK.gui.top.addChild(highScoreNotification);
// Add persistent high score display in the corner
var bestScoreText = new Text2('BEST: 0', {
size: 50,
fill: 0xFFD700
});
bestScoreText.anchor.set(1, 0);
bestScoreText.x = -100;
bestScoreText.y = 150;
LK.gui.topRight.addChild(bestScoreText);
// Safely set best score text
if (highScores && highScores.length > 0) {
bestScoreText.setText('BEST: ' + highScores[0]);
} else {
bestScoreText.setText('BEST: 0');
}
// Get highScores from storage (plugin handles fallback values)
var highScores = storage.highScores || ["1000", "750", "500", "250", "100"];
// Make sure highScores is an array
if (!Array.isArray(highScores)) {
highScores = ["1000", "750", "500", "250", "100"];
storage.highScores = highScores;
}
// Display current high scores on startup
scoreboard.updateScores(highScores);
// Initialize high score display elements
highScoreNotification.alpha = 1;
console.log("Loaded high scores:", highScores);
// Add intro animation for scoreboard
LK.setTimeout(function () {
// Show scoreboard briefly at start of game
scoreboard.visible = true;
// Shrink from large
scoreboard.scale.set(1.2, 1.2);
scoreboard.alpha = 0;
tween(scoreboard, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 500,
onFinish: function onFinish() {
// Hide after showing briefly
LK.setTimeout(function () {
tween(scoreboard, {
alpha: 0
}, {
duration: 400,
onFinish: function onFinish() {
scoreboard.visible = false;
scoreboard.alpha = 1;
}
});
}, 2000);
}
});
}, 500);
// Make scoreboard button interactive
scoreboardButton.interactive = true;
scoreboardButton.down = function () {
scoreboard.visible = true;
};
// Handle game updates
game.update = function () {
// Update background scrolling
background.update();
player.update();
// Check if player has advanced to the next level
var currentScore = LK.getScore();
if (currentScore >= scoreToNextLevel && currentLevel < lastLevel) {
// Advance to next level
currentLevel++;
scoreToNextLevel += currentLevel * 3; // Reduced points needed for next level
// Configure the new level
levelManager.configure(currentLevel);
levelDisplayText.setText('Level: ' + currentLevel);
// Update game difficulty based on level settings
enemySpawnInterval = levelManager.enemySpawnRate;
// Update background color
game.setBackgroundColor(levelManager.backgroundColor);
// Special effect for level completion
LK.effects.flashScreen(0x00FF00, 500); // Green flash for level up
// Give player invincibility after level up
player.isInvincible = true;
player.invincibilityTime = 180; // 3 seconds at 60fps
}
// Win condition - completing all 50 levels
if (currentLevel >= lastLevel && currentScore >= scoreToNextLevel) {
// Player has completed all levels
LK.showYouWin();
}
// Spawn enemies with level-adjusted difficulty
enemySpawnCounter++;
if (enemySpawnCounter >= enemySpawnInterval) {
// Randomly select enemy type based on current level
var enemyType = Math.random();
var enemy;
// Increase chance of fast enemies as level increases
var fastEnemyChance = Math.min(0.5, currentLevel * 0.05);
if (enemyType < fastEnemyChance) {
// Spawn a fast enemy
enemy = new EnemyFast();
} else {
// Spawn a regular enemy
enemy = new Enemy();
}
enemy.x = 2048 + 500; // Increased spawn distance by 500 pixels
enemy.y = 2732 / 2 + (Math.random() * 200 - 100); // Add some vertical variance
// Adjust enemy speed based on current level
if (enemy.type !== 'fast') {
enemy.speed = levelManager.enemySpeed;
}
enemies.push(enemy);
game.addChild(enemy);
// Randomize the spawn interval for the next enemy
// Make spawn interval longer for easier gameplay
var randomVariance = Math.floor(Math.random() * 80) + 40;
// Significantly increase the spawn interval by multiplying it by 2
enemySpawnInterval = Math.max(120, levelManager.enemySpawnRate * 2 - randomVariance);
enemySpawnCounter = 0;
}
// Handle power-up spawning
powerUpSpawnCounter++;
if (powerUpSpawnCounter >= powerUpSpawnInterval) {
var powerUp = new PowerUp();
powerUp.x = 2048 + 200;
powerUp.y = 2732 / 2 + (Math.random() * 400 - 200); // Random vertical position
powerUps.push(powerUp);
game.addChild(powerUp);
powerUpSpawnCounter = 0;
// Adjust spawn rate based on level
powerUpSpawnInterval = Math.max(300, 600 - currentLevel * 5);
}
// Update power-ups
for (var i = powerUps.length - 1; i >= 0; i--) {
powerUps[i].update();
// Check for collision with player
if (player.intersects(powerUps[i])) {
// Apply power-up effect
player.isInvincible = true;
player.invincibilityTime = 300; // 5 seconds of invincibility
// Visual feedback
LK.effects.flashScreen(0xFFFF00, 500); // Yellow flash
// Remove power-up
powerUps[i].destroy();
powerUps.splice(i, 1);
}
// Remove power-ups that are off-screen
else if (powerUps[i].x < -50) {
powerUps[i].destroy();
powerUps.splice(i, 1);
}
}
// Update enemies
for (var j = enemies.length - 1; j >= 0; j--) {
enemies[j].update();
if (player.intersects(enemies[j]) && !player.isInvincible) {
LK.effects.flashScreen(0xff0000, 1000);
LK.getSound('gameover').play();
// Check if this is a new high score with improved notification
var isNewHighScore = false;
var currentScore = LK.getScore();
var highScoreRank = -1;
// Check if player achieved any high score position
for (var i = 0; i < highScores.length; i++) {
if (currentScore > parseInt(highScores[i])) {
highScoreRank = i + 1;
break;
}
}
if (highScoreRank > 0) {
isNewHighScore = true;
if (highScoreRank === 1) {
highScoreNotification.setText("NEW TOP SCORE: " + currentScore + "!");
} else {
highScoreNotification.setText("NEW #" + highScoreRank + " HIGH SCORE!");
}
highScoreNotification.visible = true;
// Improved animation effect
highScoreNotification.scale.set(1.2, 1.2);
// Create sequence of animations
tween(highScoreNotification, {
alpha: 0.7,
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 400,
repeat: 4,
yoyo: true,
onFinish: function onFinish() {
highScoreNotification.alpha = 1;
highScoreNotification.scale.set(1, 1);
}
});
}
// Update high scores with current score
var currentScore = LK.getScore();
// Create a copy of the array to check if scores changed
var oldHighScores = [];
for (var i = 0; i < highScores.length; i++) {
oldHighScores.push(highScores[i]);
}
var scoreInserted = false;
for (var k = 0; k < highScores.length; k++) {
if (currentScore > parseInt(highScores[k])) {
// Insert new high score and shift others down
highScores.splice(k, 0, currentScore.toString());
highScores.pop(); // Remove lowest score
scoreInserted = true;
break;
}
}
// Only save if the scores actually changed
if (scoreInserted) {
try {
// Save updated high scores to storage
storage.highScores = highScores;
console.log("High scores updated and saved to storage");
} catch (e) {
console.log("Error saving high scores:", e);
}
}
// Update scoreboard with new high scores
scoreboard.updateScores(highScores);
// Show scoreboard when game is over
scoreboard.visible = true;
LK.showGameOver();
} else if (player.x > enemies[j].x && !enemies[j].passed) {
enemies[j].passed = true;
LK.setScore(LK.getScore() + 2); // Double points for passing enemies
scoreText.setText('Score: ' + LK.getScore());
}
}
};
// Handle player jump
game.down = function (x, y, obj) {
player.jump();
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
highScores: ["1000", "750", "500", "250", "100"]
});
/****
* Classes
****/
// Define a class for scrolling background
var Background = Container.expand(function () {
var self = Container.call(this);
// Create two background images for continuous scrolling
var bg1 = self.attachAsset('background', {
anchorX: 0,
anchorY: 0
});
var bg2 = self.attachAsset('background', {
anchorX: 0,
anchorY: 0
});
// Position the second background right after the first one
bg2.x = bg1.width;
self.speed = 2; // Scrolling speed
// Track positions for animation
self.update = function () {
// Move both backgrounds to the left
bg1.x -= self.speed;
bg2.x -= self.speed;
// When the first background moves completely off-screen to the left
if (bg1.x <= -bg1.width) {
bg1.x = bg2.x + bg2.width;
}
// When the second background moves completely off-screen to the left
if (bg2.x <= -bg2.width) {
bg2.x = bg1.x + bg1.width;
}
};
return self;
});
// Define a class for enemies
var Enemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 5;
self.type = Math.floor(Math.random() * 3); // Different enemy types for variety
self.passed = false;
// Apply visual variations based on enemy type
if (self.type === 1) {
// Slightly larger enemy
enemyGraphics.scale.set(1.2, 1.2);
} else if (self.type === 2) {
// Rotated enemy
enemyGraphics.rotation = Math.PI / 6; // 30 degrees rotation
}
self.update = function () {
self.x -= self.speed;
if (self.x < -50) {
self.destroy();
}
};
});
// Define a class for fast enemies
var EnemyFast = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8; // Faster than regular enemies
self.type = 'fast';
self.passed = false;
// Apply visual variations - make it smaller and red tinted
enemyGraphics.scale.set(0.8, 0.8);
enemyGraphics.tint = 0xFF0000; // Red tint
self.update = function () {
self.x -= self.speed;
if (self.x < -50) {
self.destroy();
}
};
});
// Level class to manage level configuration
var Level = Container.expand(function () {
var self = Container.call(this);
// Level data properties
self.levelNumber = 1;
self.enemySpeed = 5;
self.enemySpawnRate = 100;
self.backgroundColor = 0x87CEEB;
// Level text display
var levelText = new Text2('LEVEL 1', {
size: 80,
fill: 0xFFFFFF
});
levelText.anchor.set(0.5, 0.5);
self.addChild(levelText);
// Show level indicator briefly
self.showLevelIndicator = function (level) {
self.levelNumber = level;
levelText.setText('LEVEL ' + level);
self.visible = true;
// Fade in level text
levelText.alpha = 0;
tween(levelText, {
alpha: 1
}, {
duration: 500
});
// Hide after delay
LK.setTimeout(function () {
// Fade out
tween(levelText, {
alpha: 0
}, {
duration: 500,
onFinish: function onFinish() {
self.visible = false;
}
});
}, 2000);
};
// Configure level properties based on level number
self.configure = function (level) {
self.levelNumber = level;
// Increasing difficulty with each level (reduced scaling)
self.enemySpeed = 5 + level * 0.2; // Increased enemy speed
self.enemySpawnRate = Math.max(60, 120 - level * 0.8); // Less frequent spawns
// Change background color every 10 levels
var colorIndex = Math.floor((level - 1) / 10);
var backgroundColors = [0x87CEEB,
// Sky blue
0xFFA500,
// Orange
0x9370DB,
// Purple
0x228B22,
// Forest green
0xB22222 // Fire brick red
];
self.backgroundColor = backgroundColors[colorIndex % backgroundColors.length];
// Show level text
self.showLevelIndicator(level);
return self;
};
// Initially hide the level indicator
self.visible = false;
return self;
});
//<Assets used in the game will automatically appear here>
// Define a class for the player character
var Player = Container.expand(function () {
var self = Container.call(this);
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 15; // Increased speed for easier jumps
self.jumpHeight = 35; // Moderately increased jump height
self.isJumping = false;
self.velocityY = 0;
self.isInvincible = false;
self.invincibilityTime = 0;
self.update = function () {
if (self.isJumping) {
self.y += self.velocityY;
self.velocityY += 0.5; // Decreased gravity effect for slower fall
if (self.y >= 2732 / 2) {
self.y = 2732 / 2;
self.isJumping = false;
self.velocityY = 0;
}
}
// Handle invincibility
if (self.isInvincible) {
self.invincibilityTime--;
playerGraphics.alpha = Math.sin(LK.ticks * 0.2) * 0.5 + 0.5; // Flashing effect
if (self.invincibilityTime <= 0) {
self.isInvincible = false;
playerGraphics.alpha = 1;
}
}
};
self.jump = function () {
if (!self.isJumping) {
self.isJumping = true;
self.velocityY = -self.jumpHeight;
LK.getSound('jump').play();
}
};
});
// Import tween plugin for animations
// PowerUp class to provide player with special abilities
var PowerUp = Container.expand(function () {
var self = Container.call(this);
// Create a simple colorful circle for the power-up
var powerUpGraphics = self.attachAsset('centerCircle', {
anchorX: 0.5,
anchorY: 0.5
});
// Set a bright color to make it stand out
powerUpGraphics.tint = 0xFFFF00; // Yellow color
powerUpGraphics.scale.set(0.5, 0.5);
self.speed = 4; // Move slightly slower than enemies
self.type = 'invincibility'; // Default type
// Add a pulsing animation effect
self.update = function () {
self.x -= self.speed;
// Pulse animation
var scale = 0.5 + Math.sin(LK.ticks * 0.1) * 0.1;
powerUpGraphics.scale.set(scale, scale);
// Remove if off-screen
if (self.x < -50) {
self.destroy();
}
};
return self;
});
// Scoreboard class to display high scores
var Scoreboard = Container.expand(function () {
var self = Container.call(this);
// Create a background for the scoreboard
var background = self.attachAsset('soundOff', {
anchorX: 0.5,
anchorY: 0.5
});
// Scale the background to make it larger
background.width = 800;
background.height = 1200;
background.alpha = 0.8;
// Create title text with improved styling
var titleText = new Text2('HIGH SCORES', {
size: 90,
fill: 0xFFD700
});
titleText.anchor.set(0.5, 0);
titleText.y = -background.height / 2 + 50;
self.addChild(titleText);
// Create current score display with improved styling
var currentScoreLabel = new Text2('YOUR SCORE:', {
size: 65,
fill: 0xFFFFFF
});
currentScoreLabel.anchor.set(0.5, 0);
currentScoreLabel.y = -background.height / 2 + 140;
self.addChild(currentScoreLabel);
var currentScoreText = new Text2('0', {
size: 80,
fill: 0xFFD700 // Gold color
});
currentScoreText.anchor.set(0.5, 0);
currentScoreText.y = -background.height / 2 + 200;
self.addChild(currentScoreText);
// Array to store score text objects
self.scoreItems = [];
// Add decorative divider
var divider = self.attachAsset('soundOff', {
anchorX: 0.5,
anchorY: 0.5
});
divider.width = 700;
divider.height = 4;
divider.y = -background.height / 2 + 240;
divider.alpha = 0.5;
divider.tint = 0xFFD700;
// Method to update the scoreboard with new scores
self.updateScores = function (scores) {
// Update current score
currentScoreText.setText(LK.getScore().toString());
// Clear previous score texts
for (var i = 0; i < self.scoreItems.length; i++) {
self.scoreItems[i].destroy();
}
self.scoreItems = [];
// Add new score items with improved visual hierarchy
for (var i = 0; i < scores.length && i < 5; i++) {
// Determine if this is the current score that was just achieved
var isCurrentScore = LK.getScore() === parseInt(scores[i]);
// Add rank indicator
var rankText = new Text2(i + 1 + '.', {
size: 60,
fill: isCurrentScore ? 0xFFD700 : 0xFFFFFF
});
rankText.anchor.set(1, 0);
rankText.x = -100;
rankText.y = -background.height / 2 + 280 + i * 90;
self.addChild(rankText);
self.scoreItems.push(rankText);
// Add score value with separated styling
var scoreText = new Text2(scores[i], {
size: 65,
fill: isCurrentScore ? 0xFFD700 : 0xFFFFFF // Gold color for current score
});
scoreText.anchor.set(0, 0);
scoreText.x = -70;
scoreText.y = -background.height / 2 + 280 + i * 90;
// Add highlight effect for current score
if (isCurrentScore) {
// Create a background highlight
var highlight = self.attachAsset('soundOff', {
anchorX: 0.5,
anchorY: 0.5,
width: 600,
height: 70
});
highlight.x = 0;
highlight.y = scoreText.y + scoreText.height / 2;
highlight.alpha = 0.2;
highlight.tint = 0xFFD700; // Gold tint
self.scoreItems.push(highlight);
// Add pulsing animation to highlight
tween(highlight, {
alpha: 0.05
}, {
duration: 800,
repeat: -1,
yoyo: true
});
// Add "NEW!" indicator for new high score
if (i === 0 && isCurrentScore) {
var newIndicator = new Text2('NEW!', {
size: 50,
fill: 0xFF0000
});
newIndicator.anchor.set(0, 0.5);
newIndicator.x = 150;
newIndicator.y = highlight.y;
self.addChild(newIndicator);
self.scoreItems.push(newIndicator);
// Add blinking animation
tween(newIndicator, {
alpha: 0.5
}, {
duration: 500,
repeat: -1,
yoyo: true
});
}
}
self.addChild(scoreText);
self.scoreItems.push(scoreText);
}
// Update title with achievements
if (LK.getScore() > 0) {
var achievementText = "";
if (LK.getScore() >= parseInt(scores[0])) {
achievementText = "NEW HIGH SCORE!";
} else if (LK.getScore() >= parseInt(scores[4])) {
achievementText = "TOP 5 SCORE!";
}
if (achievementText) {
var achievement = new Text2(achievementText, {
size: 50,
fill: 0xFF9900
});
achievement.anchor.set(0.5, 0);
achievement.y = -background.height / 2 + 100;
self.addChild(achievement);
self.scoreItems.push(achievement);
}
}
};
// Add improved close button
var closeButton = new Container();
closeButton.x = 0;
closeButton.y = background.height / 2 - 100;
closeButton.interactive = true;
// Add button background for better visibility
var buttonBg = closeButton.attachAsset('soundOff', {
anchorX: 0.5,
anchorY: 0.5,
width: 200,
height: 80
});
buttonBg.alpha = 0.4;
buttonBg.tint = 0x444444;
// Add button text
var buttonText = new Text2('CLOSE', {
size: 60,
fill: 0xFFFFFF
});
buttonText.anchor.set(0.5, 0.5);
closeButton.addChild(buttonText);
closeButton.down = function () {
self.visible = false;
};
self.addChild(closeButton);
// Initially hide scoreboard
self.visible = false;
return self;
});
// Sound toggle button class
var SoundButton = Container.expand(function () {
var self = Container.call(this);
// Track sound state
self.soundOn = true;
// Create both button states
var soundOnGraphics = self.attachAsset('soundOn', {
anchorX: 0.5,
anchorY: 0.5
});
var soundOffGraphics = self.attachAsset('soundOff', {
anchorX: 0.5,
anchorY: 0.5
});
// Initially hide the sound off icon
soundOffGraphics.visible = false;
// Handle button press
self.down = function () {
self.soundOn = !self.soundOn;
soundOnGraphics.visible = self.soundOn;
soundOffGraphics.visible = !self.soundOn;
// Set global sound volume based on state
LK.setVolume(self.soundOn ? 1 : 0);
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87CEEB // Sky blue background
});
/****
* Game Code
****/
// Initialize current level
var currentLevel = 1;
var levelsCompleted = 0;
var scoreToNextLevel = 5; // Reduced initial points for level progression
var lastLevel = 50;
var powerUps = []; // Array to store power-ups
var powerUpSpawnCounter = 0;
var powerUpSpawnInterval = 600; // Spawn power-up every 10 seconds (60fps * 10)
// Create level indicator
var levelManager = new Level();
LK.gui.center.addChild(levelManager);
levelManager.configure(currentLevel);
// Create scrolling background
var background = game.addChild(new Background());
background.x = 0;
background.y = 0;
// Initialize player
var player = game.addChild(new Player());
player.x = 2048 / 2;
player.y = 2732 / 2;
player.isInvincible = true; // Start player with invincibility
player.invincibilityTime = 180; // 3 seconds at 60fps
// Play background music with fade in effect
LK.playMusic('Music1', {
fade: {
start: 0,
end: 1,
duration: 1000
}
});
// Add sound toggle button to top right corner
var soundButton = new SoundButton();
soundButton.x = 2048 - 80;
soundButton.y = 80;
LK.gui.topRight.addChild(soundButton);
// Initialize enemies
var enemies = [];
var enemySpawnInterval = 100;
var enemySpawnCounter = 0;
// Add level display text
var levelDisplayText = new Text2('Level: 1', {
size: 60,
fill: 0xFFFFFF
});
levelDisplayText.anchor.set(0, 0);
levelDisplayText.x = 150;
levelDisplayText.y = 150;
LK.gui.topLeft.addChild(levelDisplayText);
// Create a new Text2 object to display the score
var scoreText = new Text2('Score: 0', {
size: 100,
fill: 0xFFFFFF
});
// Position the score text correctly
scoreText.anchor.set(0.5, 0);
// Add the score text to the game GUI at the top center of the screen
LK.gui.top.addChild(scoreText);
// Create scoreboard button
var scoreboardButton = new Text2('SCORES', {
size: 60,
fill: 0xFFFFFF
});
scoreboardButton.anchor.set(0.5, 0);
LK.gui.topLeft.addChild(scoreboardButton);
scoreboardButton.x = 150;
scoreboardButton.y = 80;
// Create and initialize the scoreboard
var scoreboard = new Scoreboard();
scoreboard.x = 2048 / 2;
scoreboard.y = 2732 / 2;
LK.gui.center.addChild(scoreboard);
// Create high score notification text
var highScoreNotification = new Text2('', {
size: 120,
fill: 0xFFD700 // Gold color
});
highScoreNotification.anchor.set(0.5, 0);
highScoreNotification.y = 350;
highScoreNotification.visible = false;
LK.gui.top.addChild(highScoreNotification);
// Add persistent high score display in the corner
var bestScoreText = new Text2('BEST: 0', {
size: 50,
fill: 0xFFD700
});
bestScoreText.anchor.set(1, 0);
bestScoreText.x = -100;
bestScoreText.y = 150;
LK.gui.topRight.addChild(bestScoreText);
// Safely set best score text
if (highScores && highScores.length > 0) {
bestScoreText.setText('BEST: ' + highScores[0]);
} else {
bestScoreText.setText('BEST: 0');
}
// Get highScores from storage (plugin handles fallback values)
var highScores = storage.highScores || ["1000", "750", "500", "250", "100"];
// Make sure highScores is an array
if (!Array.isArray(highScores)) {
highScores = ["1000", "750", "500", "250", "100"];
storage.highScores = highScores;
}
// Display current high scores on startup
scoreboard.updateScores(highScores);
// Initialize high score display elements
highScoreNotification.alpha = 1;
console.log("Loaded high scores:", highScores);
// Add intro animation for scoreboard
LK.setTimeout(function () {
// Show scoreboard briefly at start of game
scoreboard.visible = true;
// Shrink from large
scoreboard.scale.set(1.2, 1.2);
scoreboard.alpha = 0;
tween(scoreboard, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 500,
onFinish: function onFinish() {
// Hide after showing briefly
LK.setTimeout(function () {
tween(scoreboard, {
alpha: 0
}, {
duration: 400,
onFinish: function onFinish() {
scoreboard.visible = false;
scoreboard.alpha = 1;
}
});
}, 2000);
}
});
}, 500);
// Make scoreboard button interactive
scoreboardButton.interactive = true;
scoreboardButton.down = function () {
scoreboard.visible = true;
};
// Handle game updates
game.update = function () {
// Update background scrolling
background.update();
player.update();
// Check if player has advanced to the next level
var currentScore = LK.getScore();
if (currentScore >= scoreToNextLevel && currentLevel < lastLevel) {
// Advance to next level
currentLevel++;
scoreToNextLevel += currentLevel * 3; // Reduced points needed for next level
// Configure the new level
levelManager.configure(currentLevel);
levelDisplayText.setText('Level: ' + currentLevel);
// Update game difficulty based on level settings
enemySpawnInterval = levelManager.enemySpawnRate;
// Update background color
game.setBackgroundColor(levelManager.backgroundColor);
// Special effect for level completion
LK.effects.flashScreen(0x00FF00, 500); // Green flash for level up
// Give player invincibility after level up
player.isInvincible = true;
player.invincibilityTime = 180; // 3 seconds at 60fps
}
// Win condition - completing all 50 levels
if (currentLevel >= lastLevel && currentScore >= scoreToNextLevel) {
// Player has completed all levels
LK.showYouWin();
}
// Spawn enemies with level-adjusted difficulty
enemySpawnCounter++;
if (enemySpawnCounter >= enemySpawnInterval) {
// Randomly select enemy type based on current level
var enemyType = Math.random();
var enemy;
// Increase chance of fast enemies as level increases
var fastEnemyChance = Math.min(0.5, currentLevel * 0.05);
if (enemyType < fastEnemyChance) {
// Spawn a fast enemy
enemy = new EnemyFast();
} else {
// Spawn a regular enemy
enemy = new Enemy();
}
enemy.x = 2048 + 500; // Increased spawn distance by 500 pixels
enemy.y = 2732 / 2 + (Math.random() * 200 - 100); // Add some vertical variance
// Adjust enemy speed based on current level
if (enemy.type !== 'fast') {
enemy.speed = levelManager.enemySpeed;
}
enemies.push(enemy);
game.addChild(enemy);
// Randomize the spawn interval for the next enemy
// Make spawn interval longer for easier gameplay
var randomVariance = Math.floor(Math.random() * 80) + 40;
// Significantly increase the spawn interval by multiplying it by 2
enemySpawnInterval = Math.max(120, levelManager.enemySpawnRate * 2 - randomVariance);
enemySpawnCounter = 0;
}
// Handle power-up spawning
powerUpSpawnCounter++;
if (powerUpSpawnCounter >= powerUpSpawnInterval) {
var powerUp = new PowerUp();
powerUp.x = 2048 + 200;
powerUp.y = 2732 / 2 + (Math.random() * 400 - 200); // Random vertical position
powerUps.push(powerUp);
game.addChild(powerUp);
powerUpSpawnCounter = 0;
// Adjust spawn rate based on level
powerUpSpawnInterval = Math.max(300, 600 - currentLevel * 5);
}
// Update power-ups
for (var i = powerUps.length - 1; i >= 0; i--) {
powerUps[i].update();
// Check for collision with player
if (player.intersects(powerUps[i])) {
// Apply power-up effect
player.isInvincible = true;
player.invincibilityTime = 300; // 5 seconds of invincibility
// Visual feedback
LK.effects.flashScreen(0xFFFF00, 500); // Yellow flash
// Remove power-up
powerUps[i].destroy();
powerUps.splice(i, 1);
}
// Remove power-ups that are off-screen
else if (powerUps[i].x < -50) {
powerUps[i].destroy();
powerUps.splice(i, 1);
}
}
// Update enemies
for (var j = enemies.length - 1; j >= 0; j--) {
enemies[j].update();
if (player.intersects(enemies[j]) && !player.isInvincible) {
LK.effects.flashScreen(0xff0000, 1000);
LK.getSound('gameover').play();
// Check if this is a new high score with improved notification
var isNewHighScore = false;
var currentScore = LK.getScore();
var highScoreRank = -1;
// Check if player achieved any high score position
for (var i = 0; i < highScores.length; i++) {
if (currentScore > parseInt(highScores[i])) {
highScoreRank = i + 1;
break;
}
}
if (highScoreRank > 0) {
isNewHighScore = true;
if (highScoreRank === 1) {
highScoreNotification.setText("NEW TOP SCORE: " + currentScore + "!");
} else {
highScoreNotification.setText("NEW #" + highScoreRank + " HIGH SCORE!");
}
highScoreNotification.visible = true;
// Improved animation effect
highScoreNotification.scale.set(1.2, 1.2);
// Create sequence of animations
tween(highScoreNotification, {
alpha: 0.7,
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 400,
repeat: 4,
yoyo: true,
onFinish: function onFinish() {
highScoreNotification.alpha = 1;
highScoreNotification.scale.set(1, 1);
}
});
}
// Update high scores with current score
var currentScore = LK.getScore();
// Create a copy of the array to check if scores changed
var oldHighScores = [];
for (var i = 0; i < highScores.length; i++) {
oldHighScores.push(highScores[i]);
}
var scoreInserted = false;
for (var k = 0; k < highScores.length; k++) {
if (currentScore > parseInt(highScores[k])) {
// Insert new high score and shift others down
highScores.splice(k, 0, currentScore.toString());
highScores.pop(); // Remove lowest score
scoreInserted = true;
break;
}
}
// Only save if the scores actually changed
if (scoreInserted) {
try {
// Save updated high scores to storage
storage.highScores = highScores;
console.log("High scores updated and saved to storage");
} catch (e) {
console.log("Error saving high scores:", e);
}
}
// Update scoreboard with new high scores
scoreboard.updateScores(highScores);
// Show scoreboard when game is over
scoreboard.visible = true;
LK.showGameOver();
} else if (player.x > enemies[j].x && !enemies[j].passed) {
enemies[j].passed = true;
LK.setScore(LK.getScore() + 2); // Double points for passing enemies
scoreText.setText('Score: ' + LK.getScore());
}
}
};
// Handle player jump
game.down = function (x, y, obj) {
player.jump();
};