/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ // Coin class var Coin = Container.expand(function () { var self = Container.call(this); var coinAsset = self.attachAsset('coin', { anchorX: 0.5, anchorY: 0.5 }); self.width = coinAsset.width; self.height = coinAsset.height; self.update = function () { // Optionally animate coin }; return self; }); // Enemy class var Enemy = Container.expand(function () { var self = Container.call(this); var enemyAsset = self.attachAsset('enemy', { anchorX: 0.5, anchorY: 1 }); self.vx = -5; self.width = enemyAsset.width; self.height = enemyAsset.height; self.update = function () { // Only update enemy movement when game is playing if (gameState !== 'playing') return; self.x += self.vx; if (self.lastX === undefined) self.lastX = self.x; }; return self; }); // GoldCoin class - worth 2 points var GoldCoin = Container.expand(function () { var self = Container.call(this); var goldCoinAsset = self.attachAsset('goldCoin', { anchorX: 0.5, anchorY: 0.5 }); goldCoinAsset.tint = 0xFFD700; // Golden color self.width = goldCoinAsset.width; self.height = goldCoinAsset.height; self.update = function () { // Optionally animate gold coin }; return self; }); // Platform class var Platform = Container.expand(function () { var self = Container.call(this); var platAsset = self.attachAsset('platform', { anchorX: 0, anchorY: 0 }); self.width = platAsset.width; self.height = platAsset.height; // Allow dynamic width scaling self.setWidth = function (newWidth) { self.width = newWidth; platAsset.scaleX = newWidth / platAsset.width; }; return self; }); // Player class var Player = Container.expand(function () { var self = Container.call(this); var playerAsset = self.attachAsset('player', { anchorX: 0.5, anchorY: 1 }); self.vx = 0; self.vy = 0; self.isOnGround = false; self.width = playerAsset.width; self.height = playerAsset.height; self.update = function () { // Gravity self.vy += 2.5; // Clamp vy if (self.vy > 60) self.vy = 60; // Update sprite direction based on movement if (self.vx > 0) { // Moving right - face right (normal) playerAsset.scaleX = Math.abs(playerAsset.scaleX); } else if (self.vx < 0) { // Moving left - face left (flipped) playerAsset.scaleX = -Math.abs(playerAsset.scaleX); } self.x += self.vx; self.y += self.vy; // Save last position for collision checks if (self.lastX === undefined) self.lastX = self.x; if (self.lastY === undefined) self.lastY = self.y; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x87ceeb // sky blue }); /**** * Game Code ****/ // Import tween plugin for animations // --- Game variables --- var gameState = 'menu'; // 'menu' or 'playing' var menuContainer; var startButton; var titleText; var player; var platforms = []; var coins = []; var goldCoins = []; var enemies = []; var score = 0; var scoreTxt; var dragStartX = null; var dragStartY = null; var jumpQueued = false; var enemySpawnDelay = 0; // Delay counter for enemy spawning var gameOverContainer; var highScore = storage.highScore || 0; // --- Setup main menu --- function setupMenu() { menuContainer = new Container(); game.addChild(menuContainer); // Title text titleText = new Text2('LizardRun', { size: 200, fill: 0x00FF00, font: "'Arial Black', 'Helvetica', sans-serif", stroke: 0x000000, strokeThickness: 6, dropShadow: true, dropShadowColor: 0x000000, dropShadowBlur: 6, dropShadowAngle: Math.PI / 4, dropShadowDistance: 8 }); titleText.anchor.set(0.5, 0.5); titleText.x = 2048 / 2; titleText.y = 2732 / 2 - 300; menuContainer.addChild(titleText); // Start button background var buttonBg = LK.getAsset('player', { anchorX: 0.5, anchorY: 0.5, scaleX: 2, scaleY: 2 }); buttonBg.x = 400; buttonBg.y = 2732 / 2 + 100; menuContainer.addChild(buttonBg); // Start button text startButton = new Text2('START GAME', { size: 120, fill: 0xFFFFFF }); startButton.anchor.set(0.5, 0.5); startButton.x = 2048 / 2; startButton.y = 2732 / 2 + 100; menuContainer.addChild(startButton); // Make button interactive startButton.interactive = true; startButton.down = function () { startGame(); }; // Add enemy on right side of menu var menuEnemy = LK.getAsset('enemy', { anchorX: 0.5, anchorY: 0.5, scaleX: 2, scaleY: 2 }); menuEnemy.x = 1648; // Right side of screen menuEnemy.y = 2732 / 2 + 100; menuContainer.addChild(menuEnemy); } function startGame() { // Remove menu if (menuContainer) { menuContainer.destroy(); menuContainer = null; } // Set game state to playing gameState = 'playing'; // Setup the actual game setupLevel(); } function showCustomGameOver() { // Set game state to stop game loop gameState = 'gameOver'; // Update high score if current score is higher if (score > highScore) { highScore = score; storage.highScore = highScore; } // Remove existing game over container if it exists if (gameOverContainer) { game.removeChild(gameOverContainer); gameOverContainer.destroy(); gameOverContainer = null; } // Create game over container gameOverContainer = new Container(); game.addChild(gameOverContainer); // Background var bgOverlay = LK.getAsset('platform', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.2, scaleY: 0.8 }); bgOverlay.x = 2048 / 2; bgOverlay.y = 2732 / 2; bgOverlay.alpha = 0.8; bgOverlay.tint = 0x000000; gameOverContainer.addChild(bgOverlay); // Game Over title var gameOverTitle = new Text2('GAME OVER', { size: 180, fill: 0xFF0000, font: "'Arial Black', 'Helvetica', sans-serif", stroke: 0x000000, strokeThickness: 6, dropShadow: true, dropShadowColor: 0x000000, dropShadowBlur: 6, dropShadowAngle: Math.PI / 4, dropShadowDistance: 8 }); gameOverTitle.anchor.set(0.5, 0.5); gameOverTitle.x = 2048 / 2; gameOverTitle.y = 2732 / 2 - 300; gameOverContainer.addChild(gameOverTitle); // Current score var currentScoreText = new Text2('Score: ' + score, { size: 120, fill: 0xFFFFFF, font: "'Arial Black', 'Helvetica', sans-serif", stroke: 0xFFD700, strokeThickness: 4, dropShadow: true, dropShadowColor: 0x000000, dropShadowBlur: 4, dropShadowAngle: Math.PI / 4, dropShadowDistance: 6 }); currentScoreText.anchor.set(0.5, 0.5); currentScoreText.x = 2048 / 2; currentScoreText.y = 2732 / 2 - 100; gameOverContainer.addChild(currentScoreText); // High score var highScoreText = new Text2('High Score: ' + highScore, { size: 120, fill: 0xFFD700, font: "'Arial Black', 'Helvetica', sans-serif", stroke: 0x000000, strokeThickness: 4, dropShadow: true, dropShadowColor: 0x000000, dropShadowBlur: 4, dropShadowAngle: Math.PI / 4, dropShadowDistance: 6 }); highScoreText.anchor.set(0.5, 0.5); highScoreText.x = 2048 / 2; highScoreText.y = 2732 / 2 + 50; gameOverContainer.addChild(highScoreText); // Play again button var playAgainButton = new Text2('PLAY AGAIN', { size: 100, fill: 0x00FF00, font: "'Arial Black', 'Helvetica', sans-serif", stroke: 0x000000, strokeThickness: 3, dropShadow: true, dropShadowColor: 0x000000, dropShadowBlur: 3, dropShadowAngle: Math.PI / 4, dropShadowDistance: 5 }); playAgainButton.anchor.set(0.5, 0.5); playAgainButton.x = 2048 / 2; playAgainButton.y = 2732 / 2 + 250; playAgainButton.interactive = true; playAgainButton.down = function () { // Hide game over screen first if (gameOverContainer) { game.removeChild(gameOverContainer); gameOverContainer.destroy(); gameOverContainer = null; } // Reset score score = 0; // Clear all game objects for (var i = 0; i < coins.length; i++) { coins[i].destroy(); } for (var i = 0; i < goldCoins.length; i++) { goldCoins[i].destroy(); } for (var i = 0; i < enemies.length; i++) { enemies[i].destroy(); } for (var i = 0; i < platforms.length; i++) { platforms[i].destroy(); } if (player) player.destroy(); if (scoreTxt) { scoreTxt.destroy(); scoreTxt = null; } // Reset control states isHoldingLeft = false; isHoldingRight = false; jumpQueued = false; enemySpawnDelay = 0; // Clear arrays coins = []; goldCoins = []; enemies = []; platforms = []; player = null; // Start new game gameState = 'playing'; setupLevel(); }; gameOverContainer.addChild(playAgainButton); } // --- Setup game objects --- function setupLevel() { // Player player = new Player(); player.x = 300; player.y = 2300; // Position player on ground game.addChild(player); // Ground platform - single flat ground across entire screen var ground = new Platform(); ground.x = 0; ground.y = 2400; ground.setWidth(2500); // Extended platform width game.addChild(ground); platforms.push(ground); // Coins var coin1 = new Coin(); coin1.x = 750; coin1.y = 2300; game.addChild(coin1); coins.push(coin1); var coin2 = new Coin(); coin2.x = 1250; coin2.y = 2200; game.addChild(coin2); coins.push(coin2); var coin3 = new Coin(); coin3.x = 1700; coin3.y = 2300; game.addChild(coin3); coins.push(coin3); // Gold coins - worth 2 points each var goldCoin1 = new GoldCoin(); goldCoin1.x = 900; goldCoin1.y = 2250; game.addChild(goldCoin1); goldCoins.push(goldCoin1); var goldCoin2 = new GoldCoin(); goldCoin2.x = 1500; goldCoin2.y = 2150; game.addChild(goldCoin2); goldCoins.push(goldCoin2); // Enemies var enemy1 = new Enemy(); enemy1.x = 1000; enemy1.y = 2400; game.addChild(enemy1); enemies.push(enemy1); var enemy2 = new Enemy(); enemy2.x = 1800; enemy2.y = 2200; // Position at jumping height game.addChild(enemy2); enemies.push(enemy2); // Add more enemies at jumping height var enemy3 = new Enemy(); enemy3.x = 1400; enemy3.y = 2150; // Position at jumping height game.addChild(enemy3); enemies.push(enemy3); // Sky decorations var sun = LK.getAsset('sun', { anchorX: 0.5, anchorY: 0.5 }); sun.x = 1700; sun.y = 400; game.addChild(sun); var cloud1 = LK.getAsset('cloud', { anchorX: 0.5, anchorY: 0.5 }); cloud1.x = 500; cloud1.y = 600; game.addChild(cloud1); var cloud2 = LK.getAsset('cloud', { anchorX: 0.5, anchorY: 0.5 }); cloud2.x = 1200; cloud2.y = 500; game.addChild(cloud2); var cloud3 = LK.getAsset('cloud', { anchorX: 0.5, anchorY: 0.5 }); cloud3.x = 300; cloud3.y = 700; game.addChild(cloud3); // Score text with enhanced styling scoreTxt = new Text2('0', { size: 140, fill: 0xFFFFFF, font: "'Arial Black', 'Helvetica', sans-serif", stroke: 0xFFD700, strokeThickness: 8, dropShadow: true, dropShadowColor: 0x000000, dropShadowBlur: 4, dropShadowAngle: Math.PI / 4, dropShadowDistance: 6 }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); scoreTxt.setText(score); } // Initialize with menu setupMenu(); // --- Controls (touch left/right side to move) --- var isHoldingLeft = false; var isHoldingRight = false; game.down = function (x, y, obj) { if (gameState !== 'playing') return; if (x < 2048 / 2) { isHoldingLeft = true; isHoldingRight = false; } else { isHoldingRight = true; isHoldingLeft = false; } jumpQueued = true; // Jump when starting to hold }; game.move = function (x, y, obj) { if (gameState !== 'playing') return; // Update movement direction based on current touch position if (x < 2048 / 2) { isHoldingLeft = true; isHoldingRight = false; } else { isHoldingRight = true; isHoldingLeft = false; } }; game.up = function (x, y, obj) { if (gameState !== 'playing') return; isHoldingLeft = false; isHoldingRight = false; player.vx = 0; // Stop moving when releasing }; // --- Main game loop --- game.update = function () { // Only update game logic when playing if (gameState !== 'playing') return; // Set player velocity based on holding state if (isHoldingLeft) { player.vx = -12; // Move left when holding left side } else if (isHoldingRight) { player.vx = 12; // Move right when holding right side } else { player.vx = 0; // Stop when not holding } // Player update player.update(); // Clamp player to screen if (player.x < 0) player.x = 0; if (player.x > 2048) player.x = 2048; // Platform collision player.isOnGround = false; for (var i = 0; i < platforms.length; i++) { var plat = platforms[i]; // Simple AABB collision, only check if falling if (player.vy >= 0 && player.x + player.width / 2 > plat.x && player.x - player.width / 2 < plat.x + plat.width && player.y > plat.y && player.lastY <= plat.y) { // Land on platform player.y = plat.y; player.vy = 0; player.isOnGround = true; } } // Jump if (jumpQueued && player.isOnGround) { player.vy = -48; jumpQueued = false; } // Ensure maximum 2 coins total (regular + gold coins) at any time var totalCoins = coins.length + goldCoins.length; while (totalCoins > 2) { // Remove excess coins, prioritizing regular coins first if (coins.length > 0) { var extraCoin = coins.pop(); extraCoin.destroy(); } else if (goldCoins.length > 0) { var extraGoldCoin = goldCoins.pop(); extraGoldCoin.destroy(); } totalCoins = coins.length + goldCoins.length; } // Ensure minimum 1 coin is always present (if total is 0) if (coins.length + goldCoins.length === 0) { // Randomly decide between regular coin or gold coin if (Math.random() < 0.85) { var newCoin = new Coin(); newCoin.x = Math.random() * 2048; newCoin.y = 2300 - Math.random() * 200; game.addChild(newCoin); coins.push(newCoin); } else { var newGoldCoin = new GoldCoin(); newGoldCoin.x = Math.random() * 2048; newGoldCoin.y = 2300 - Math.random() * 300; game.addChild(newGoldCoin); goldCoins.push(newGoldCoin); } } // Coin collection and random teleportation for (var i = coins.length - 1; i >= 0; i--) { var coin = coins[i]; if (player.intersects(coin)) { score += 1; scoreTxt.setText(score); // Remove coin from arrays immediately to prevent further interactions coins.splice(i, 1); // Play coin collection animation tween(coin, { scaleX: 1.5, scaleY: 1.5, alpha: 0 }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { // Destroy the animated coin coin.destroy(); // Only create new coin if total coins is less than 2 if (coins.length + goldCoins.length < 2) { // Randomly decide between regular coin or gold coin if (Math.random() < 0.9) { var newCoin = new Coin(); newCoin.x = Math.random() * 2048; // Random x position across screen newCoin.y = 2300 - Math.random() * 200; // Random height above ground game.addChild(newCoin); coins.push(newCoin); } else { var newGoldCoin = new GoldCoin(); newGoldCoin.x = Math.random() * 2048; newGoldCoin.y = 2300 - Math.random() * 300; game.addChild(newGoldCoin); goldCoins.push(newGoldCoin); } } } }); continue; // Skip to next coin since this one was collected } // Randomly teleport coins even when not collected if (Math.random() < 0.0005) { // Small chance each frame for random teleport coin.x = Math.random() * 2048; coin.y = 2300 - Math.random() * 200; } } // Gold coin collection - worth 2 points for (var i = goldCoins.length - 1; i >= 0; i--) { var goldCoin = goldCoins[i]; if (player.intersects(goldCoin)) { score += 2; // Double points for gold coins scoreTxt.setText(score); // Remove gold coin from arrays immediately to prevent further interactions goldCoins.splice(i, 1); // Play gold coin collection animation tween(goldCoin, { scaleX: 1.8, scaleY: 1.8, alpha: 0 }, { duration: 400, easing: tween.easeOut, onFinish: function onFinish() { // Destroy the animated gold coin goldCoin.destroy(); // Only create new coin if total coins is less than 2 if (coins.length + goldCoins.length < 2) { // Randomly decide between regular coin or gold coin if (Math.random() < 0.8) { var newCoin = new Coin(); newCoin.x = Math.random() * 2048; newCoin.y = 2300 - Math.random() * 200; game.addChild(newCoin); coins.push(newCoin); } else { var newGoldCoin = new GoldCoin(); newGoldCoin.x = Math.random() * 2048; newGoldCoin.y = 2300 - Math.random() * 300; game.addChild(newGoldCoin); goldCoins.push(newGoldCoin); } } } }); continue; } // Randomly teleport gold coins at specific intervals (like regular coins) if (Math.random() < 0.001) { // Simple teleportation without nested animations goldCoin.x = Math.random() * 2048; goldCoin.y = 2300 - Math.random() * 300; } } // Update enemy spawn delay if (enemySpawnDelay > 0) { enemySpawnDelay--; } // Enemy update and collision for (var i = enemies.length - 1; i >= 0; i--) { var enemy = enemies[i]; enemy.update(); // Remove if off screen and potentially respawn with delay if (enemy.x < -200) { enemy.destroy(); enemies.splice(i, 1); // Only spawn new enemy if no delay is active if (enemySpawnDelay <= 0) { var newEnemy = new Enemy(); newEnemy.x = 2200; // Start slightly off right edge // Randomly spawn at ground level or jumping height if (Math.random() < 0.4) { newEnemy.y = 2000 + Math.random() * 150; // Higher flying height range } else { newEnemy.y = 2400; // Ground level } game.addChild(newEnemy); enemies.push(newEnemy); // Set delay for next enemy spawn (2-4 seconds at 60fps) enemySpawnDelay = 120 + Math.random() * 120; } continue; } // Player/enemy collision var wasIntersecting = enemy.lastWasIntersecting || false; var isIntersecting = player.intersects(enemy); if (!wasIntersecting && isIntersecting) { // Player hit enemy: game over LK.effects.flashScreen(0xff0000, 1000); showCustomGameOver(); return; } enemy.lastWasIntersecting = isIntersecting; } // Save lastY for player player.lastY = player.y; player.lastX = player.x; };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
// Coin class
var Coin = Container.expand(function () {
var self = Container.call(this);
var coinAsset = self.attachAsset('coin', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = coinAsset.width;
self.height = coinAsset.height;
self.update = function () {
// Optionally animate coin
};
return self;
});
// Enemy class
var Enemy = Container.expand(function () {
var self = Container.call(this);
var enemyAsset = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 1
});
self.vx = -5;
self.width = enemyAsset.width;
self.height = enemyAsset.height;
self.update = function () {
// Only update enemy movement when game is playing
if (gameState !== 'playing') return;
self.x += self.vx;
if (self.lastX === undefined) self.lastX = self.x;
};
return self;
});
// GoldCoin class - worth 2 points
var GoldCoin = Container.expand(function () {
var self = Container.call(this);
var goldCoinAsset = self.attachAsset('goldCoin', {
anchorX: 0.5,
anchorY: 0.5
});
goldCoinAsset.tint = 0xFFD700; // Golden color
self.width = goldCoinAsset.width;
self.height = goldCoinAsset.height;
self.update = function () {
// Optionally animate gold coin
};
return self;
});
// Platform class
var Platform = Container.expand(function () {
var self = Container.call(this);
var platAsset = self.attachAsset('platform', {
anchorX: 0,
anchorY: 0
});
self.width = platAsset.width;
self.height = platAsset.height;
// Allow dynamic width scaling
self.setWidth = function (newWidth) {
self.width = newWidth;
platAsset.scaleX = newWidth / platAsset.width;
};
return self;
});
// Player class
var Player = Container.expand(function () {
var self = Container.call(this);
var playerAsset = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 1
});
self.vx = 0;
self.vy = 0;
self.isOnGround = false;
self.width = playerAsset.width;
self.height = playerAsset.height;
self.update = function () {
// Gravity
self.vy += 2.5;
// Clamp vy
if (self.vy > 60) self.vy = 60;
// Update sprite direction based on movement
if (self.vx > 0) {
// Moving right - face right (normal)
playerAsset.scaleX = Math.abs(playerAsset.scaleX);
} else if (self.vx < 0) {
// Moving left - face left (flipped)
playerAsset.scaleX = -Math.abs(playerAsset.scaleX);
}
self.x += self.vx;
self.y += self.vy;
// Save last position for collision checks
if (self.lastX === undefined) self.lastX = self.x;
if (self.lastY === undefined) self.lastY = self.y;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87ceeb // sky blue
});
/****
* Game Code
****/
// Import tween plugin for animations
// --- Game variables ---
var gameState = 'menu'; // 'menu' or 'playing'
var menuContainer;
var startButton;
var titleText;
var player;
var platforms = [];
var coins = [];
var goldCoins = [];
var enemies = [];
var score = 0;
var scoreTxt;
var dragStartX = null;
var dragStartY = null;
var jumpQueued = false;
var enemySpawnDelay = 0; // Delay counter for enemy spawning
var gameOverContainer;
var highScore = storage.highScore || 0;
// --- Setup main menu ---
function setupMenu() {
menuContainer = new Container();
game.addChild(menuContainer);
// Title text
titleText = new Text2('LizardRun', {
size: 200,
fill: 0x00FF00,
font: "'Arial Black', 'Helvetica', sans-serif",
stroke: 0x000000,
strokeThickness: 6,
dropShadow: true,
dropShadowColor: 0x000000,
dropShadowBlur: 6,
dropShadowAngle: Math.PI / 4,
dropShadowDistance: 8
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 2048 / 2;
titleText.y = 2732 / 2 - 300;
menuContainer.addChild(titleText);
// Start button background
var buttonBg = LK.getAsset('player', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 2
});
buttonBg.x = 400;
buttonBg.y = 2732 / 2 + 100;
menuContainer.addChild(buttonBg);
// Start button text
startButton = new Text2('START GAME', {
size: 120,
fill: 0xFFFFFF
});
startButton.anchor.set(0.5, 0.5);
startButton.x = 2048 / 2;
startButton.y = 2732 / 2 + 100;
menuContainer.addChild(startButton);
// Make button interactive
startButton.interactive = true;
startButton.down = function () {
startGame();
};
// Add enemy on right side of menu
var menuEnemy = LK.getAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 2
});
menuEnemy.x = 1648; // Right side of screen
menuEnemy.y = 2732 / 2 + 100;
menuContainer.addChild(menuEnemy);
}
function startGame() {
// Remove menu
if (menuContainer) {
menuContainer.destroy();
menuContainer = null;
}
// Set game state to playing
gameState = 'playing';
// Setup the actual game
setupLevel();
}
function showCustomGameOver() {
// Set game state to stop game loop
gameState = 'gameOver';
// Update high score if current score is higher
if (score > highScore) {
highScore = score;
storage.highScore = highScore;
}
// Remove existing game over container if it exists
if (gameOverContainer) {
game.removeChild(gameOverContainer);
gameOverContainer.destroy();
gameOverContainer = null;
}
// Create game over container
gameOverContainer = new Container();
game.addChild(gameOverContainer);
// Background
var bgOverlay = LK.getAsset('platform', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 0.8
});
bgOverlay.x = 2048 / 2;
bgOverlay.y = 2732 / 2;
bgOverlay.alpha = 0.8;
bgOverlay.tint = 0x000000;
gameOverContainer.addChild(bgOverlay);
// Game Over title
var gameOverTitle = new Text2('GAME OVER', {
size: 180,
fill: 0xFF0000,
font: "'Arial Black', 'Helvetica', sans-serif",
stroke: 0x000000,
strokeThickness: 6,
dropShadow: true,
dropShadowColor: 0x000000,
dropShadowBlur: 6,
dropShadowAngle: Math.PI / 4,
dropShadowDistance: 8
});
gameOverTitle.anchor.set(0.5, 0.5);
gameOverTitle.x = 2048 / 2;
gameOverTitle.y = 2732 / 2 - 300;
gameOverContainer.addChild(gameOverTitle);
// Current score
var currentScoreText = new Text2('Score: ' + score, {
size: 120,
fill: 0xFFFFFF,
font: "'Arial Black', 'Helvetica', sans-serif",
stroke: 0xFFD700,
strokeThickness: 4,
dropShadow: true,
dropShadowColor: 0x000000,
dropShadowBlur: 4,
dropShadowAngle: Math.PI / 4,
dropShadowDistance: 6
});
currentScoreText.anchor.set(0.5, 0.5);
currentScoreText.x = 2048 / 2;
currentScoreText.y = 2732 / 2 - 100;
gameOverContainer.addChild(currentScoreText);
// High score
var highScoreText = new Text2('High Score: ' + highScore, {
size: 120,
fill: 0xFFD700,
font: "'Arial Black', 'Helvetica', sans-serif",
stroke: 0x000000,
strokeThickness: 4,
dropShadow: true,
dropShadowColor: 0x000000,
dropShadowBlur: 4,
dropShadowAngle: Math.PI / 4,
dropShadowDistance: 6
});
highScoreText.anchor.set(0.5, 0.5);
highScoreText.x = 2048 / 2;
highScoreText.y = 2732 / 2 + 50;
gameOverContainer.addChild(highScoreText);
// Play again button
var playAgainButton = new Text2('PLAY AGAIN', {
size: 100,
fill: 0x00FF00,
font: "'Arial Black', 'Helvetica', sans-serif",
stroke: 0x000000,
strokeThickness: 3,
dropShadow: true,
dropShadowColor: 0x000000,
dropShadowBlur: 3,
dropShadowAngle: Math.PI / 4,
dropShadowDistance: 5
});
playAgainButton.anchor.set(0.5, 0.5);
playAgainButton.x = 2048 / 2;
playAgainButton.y = 2732 / 2 + 250;
playAgainButton.interactive = true;
playAgainButton.down = function () {
// Hide game over screen first
if (gameOverContainer) {
game.removeChild(gameOverContainer);
gameOverContainer.destroy();
gameOverContainer = null;
}
// Reset score
score = 0;
// Clear all game objects
for (var i = 0; i < coins.length; i++) {
coins[i].destroy();
}
for (var i = 0; i < goldCoins.length; i++) {
goldCoins[i].destroy();
}
for (var i = 0; i < enemies.length; i++) {
enemies[i].destroy();
}
for (var i = 0; i < platforms.length; i++) {
platforms[i].destroy();
}
if (player) player.destroy();
if (scoreTxt) {
scoreTxt.destroy();
scoreTxt = null;
}
// Reset control states
isHoldingLeft = false;
isHoldingRight = false;
jumpQueued = false;
enemySpawnDelay = 0;
// Clear arrays
coins = [];
goldCoins = [];
enemies = [];
platforms = [];
player = null;
// Start new game
gameState = 'playing';
setupLevel();
};
gameOverContainer.addChild(playAgainButton);
}
// --- Setup game objects ---
function setupLevel() {
// Player
player = new Player();
player.x = 300;
player.y = 2300; // Position player on ground
game.addChild(player);
// Ground platform - single flat ground across entire screen
var ground = new Platform();
ground.x = 0;
ground.y = 2400;
ground.setWidth(2500); // Extended platform width
game.addChild(ground);
platforms.push(ground);
// Coins
var coin1 = new Coin();
coin1.x = 750;
coin1.y = 2300;
game.addChild(coin1);
coins.push(coin1);
var coin2 = new Coin();
coin2.x = 1250;
coin2.y = 2200;
game.addChild(coin2);
coins.push(coin2);
var coin3 = new Coin();
coin3.x = 1700;
coin3.y = 2300;
game.addChild(coin3);
coins.push(coin3);
// Gold coins - worth 2 points each
var goldCoin1 = new GoldCoin();
goldCoin1.x = 900;
goldCoin1.y = 2250;
game.addChild(goldCoin1);
goldCoins.push(goldCoin1);
var goldCoin2 = new GoldCoin();
goldCoin2.x = 1500;
goldCoin2.y = 2150;
game.addChild(goldCoin2);
goldCoins.push(goldCoin2);
// Enemies
var enemy1 = new Enemy();
enemy1.x = 1000;
enemy1.y = 2400;
game.addChild(enemy1);
enemies.push(enemy1);
var enemy2 = new Enemy();
enemy2.x = 1800;
enemy2.y = 2200; // Position at jumping height
game.addChild(enemy2);
enemies.push(enemy2);
// Add more enemies at jumping height
var enemy3 = new Enemy();
enemy3.x = 1400;
enemy3.y = 2150; // Position at jumping height
game.addChild(enemy3);
enemies.push(enemy3);
// Sky decorations
var sun = LK.getAsset('sun', {
anchorX: 0.5,
anchorY: 0.5
});
sun.x = 1700;
sun.y = 400;
game.addChild(sun);
var cloud1 = LK.getAsset('cloud', {
anchorX: 0.5,
anchorY: 0.5
});
cloud1.x = 500;
cloud1.y = 600;
game.addChild(cloud1);
var cloud2 = LK.getAsset('cloud', {
anchorX: 0.5,
anchorY: 0.5
});
cloud2.x = 1200;
cloud2.y = 500;
game.addChild(cloud2);
var cloud3 = LK.getAsset('cloud', {
anchorX: 0.5,
anchorY: 0.5
});
cloud3.x = 300;
cloud3.y = 700;
game.addChild(cloud3);
// Score text with enhanced styling
scoreTxt = new Text2('0', {
size: 140,
fill: 0xFFFFFF,
font: "'Arial Black', 'Helvetica', sans-serif",
stroke: 0xFFD700,
strokeThickness: 8,
dropShadow: true,
dropShadowColor: 0x000000,
dropShadowBlur: 4,
dropShadowAngle: Math.PI / 4,
dropShadowDistance: 6
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
scoreTxt.setText(score);
}
// Initialize with menu
setupMenu();
// --- Controls (touch left/right side to move) ---
var isHoldingLeft = false;
var isHoldingRight = false;
game.down = function (x, y, obj) {
if (gameState !== 'playing') return;
if (x < 2048 / 2) {
isHoldingLeft = true;
isHoldingRight = false;
} else {
isHoldingRight = true;
isHoldingLeft = false;
}
jumpQueued = true; // Jump when starting to hold
};
game.move = function (x, y, obj) {
if (gameState !== 'playing') return;
// Update movement direction based on current touch position
if (x < 2048 / 2) {
isHoldingLeft = true;
isHoldingRight = false;
} else {
isHoldingRight = true;
isHoldingLeft = false;
}
};
game.up = function (x, y, obj) {
if (gameState !== 'playing') return;
isHoldingLeft = false;
isHoldingRight = false;
player.vx = 0; // Stop moving when releasing
};
// --- Main game loop ---
game.update = function () {
// Only update game logic when playing
if (gameState !== 'playing') return;
// Set player velocity based on holding state
if (isHoldingLeft) {
player.vx = -12; // Move left when holding left side
} else if (isHoldingRight) {
player.vx = 12; // Move right when holding right side
} else {
player.vx = 0; // Stop when not holding
}
// Player update
player.update();
// Clamp player to screen
if (player.x < 0) player.x = 0;
if (player.x > 2048) player.x = 2048;
// Platform collision
player.isOnGround = false;
for (var i = 0; i < platforms.length; i++) {
var plat = platforms[i];
// Simple AABB collision, only check if falling
if (player.vy >= 0 && player.x + player.width / 2 > plat.x && player.x - player.width / 2 < plat.x + plat.width && player.y > plat.y && player.lastY <= plat.y) {
// Land on platform
player.y = plat.y;
player.vy = 0;
player.isOnGround = true;
}
}
// Jump
if (jumpQueued && player.isOnGround) {
player.vy = -48;
jumpQueued = false;
}
// Ensure maximum 2 coins total (regular + gold coins) at any time
var totalCoins = coins.length + goldCoins.length;
while (totalCoins > 2) {
// Remove excess coins, prioritizing regular coins first
if (coins.length > 0) {
var extraCoin = coins.pop();
extraCoin.destroy();
} else if (goldCoins.length > 0) {
var extraGoldCoin = goldCoins.pop();
extraGoldCoin.destroy();
}
totalCoins = coins.length + goldCoins.length;
}
// Ensure minimum 1 coin is always present (if total is 0)
if (coins.length + goldCoins.length === 0) {
// Randomly decide between regular coin or gold coin
if (Math.random() < 0.85) {
var newCoin = new Coin();
newCoin.x = Math.random() * 2048;
newCoin.y = 2300 - Math.random() * 200;
game.addChild(newCoin);
coins.push(newCoin);
} else {
var newGoldCoin = new GoldCoin();
newGoldCoin.x = Math.random() * 2048;
newGoldCoin.y = 2300 - Math.random() * 300;
game.addChild(newGoldCoin);
goldCoins.push(newGoldCoin);
}
}
// Coin collection and random teleportation
for (var i = coins.length - 1; i >= 0; i--) {
var coin = coins[i];
if (player.intersects(coin)) {
score += 1;
scoreTxt.setText(score);
// Remove coin from arrays immediately to prevent further interactions
coins.splice(i, 1);
// Play coin collection animation
tween(coin, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
// Destroy the animated coin
coin.destroy();
// Only create new coin if total coins is less than 2
if (coins.length + goldCoins.length < 2) {
// Randomly decide between regular coin or gold coin
if (Math.random() < 0.9) {
var newCoin = new Coin();
newCoin.x = Math.random() * 2048; // Random x position across screen
newCoin.y = 2300 - Math.random() * 200; // Random height above ground
game.addChild(newCoin);
coins.push(newCoin);
} else {
var newGoldCoin = new GoldCoin();
newGoldCoin.x = Math.random() * 2048;
newGoldCoin.y = 2300 - Math.random() * 300;
game.addChild(newGoldCoin);
goldCoins.push(newGoldCoin);
}
}
}
});
continue; // Skip to next coin since this one was collected
}
// Randomly teleport coins even when not collected
if (Math.random() < 0.0005) {
// Small chance each frame for random teleport
coin.x = Math.random() * 2048;
coin.y = 2300 - Math.random() * 200;
}
}
// Gold coin collection - worth 2 points
for (var i = goldCoins.length - 1; i >= 0; i--) {
var goldCoin = goldCoins[i];
if (player.intersects(goldCoin)) {
score += 2; // Double points for gold coins
scoreTxt.setText(score);
// Remove gold coin from arrays immediately to prevent further interactions
goldCoins.splice(i, 1);
// Play gold coin collection animation
tween(goldCoin, {
scaleX: 1.8,
scaleY: 1.8,
alpha: 0
}, {
duration: 400,
easing: tween.easeOut,
onFinish: function onFinish() {
// Destroy the animated gold coin
goldCoin.destroy();
// Only create new coin if total coins is less than 2
if (coins.length + goldCoins.length < 2) {
// Randomly decide between regular coin or gold coin
if (Math.random() < 0.8) {
var newCoin = new Coin();
newCoin.x = Math.random() * 2048;
newCoin.y = 2300 - Math.random() * 200;
game.addChild(newCoin);
coins.push(newCoin);
} else {
var newGoldCoin = new GoldCoin();
newGoldCoin.x = Math.random() * 2048;
newGoldCoin.y = 2300 - Math.random() * 300;
game.addChild(newGoldCoin);
goldCoins.push(newGoldCoin);
}
}
}
});
continue;
}
// Randomly teleport gold coins at specific intervals (like regular coins)
if (Math.random() < 0.001) {
// Simple teleportation without nested animations
goldCoin.x = Math.random() * 2048;
goldCoin.y = 2300 - Math.random() * 300;
}
}
// Update enemy spawn delay
if (enemySpawnDelay > 0) {
enemySpawnDelay--;
}
// Enemy update and collision
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
enemy.update();
// Remove if off screen and potentially respawn with delay
if (enemy.x < -200) {
enemy.destroy();
enemies.splice(i, 1);
// Only spawn new enemy if no delay is active
if (enemySpawnDelay <= 0) {
var newEnemy = new Enemy();
newEnemy.x = 2200; // Start slightly off right edge
// Randomly spawn at ground level or jumping height
if (Math.random() < 0.4) {
newEnemy.y = 2000 + Math.random() * 150; // Higher flying height range
} else {
newEnemy.y = 2400; // Ground level
}
game.addChild(newEnemy);
enemies.push(newEnemy);
// Set delay for next enemy spawn (2-4 seconds at 60fps)
enemySpawnDelay = 120 + Math.random() * 120;
}
continue;
}
// Player/enemy collision
var wasIntersecting = enemy.lastWasIntersecting || false;
var isIntersecting = player.intersects(enemy);
if (!wasIntersecting && isIntersecting) {
// Player hit enemy: game over
LK.effects.flashScreen(0xff0000, 1000);
showCustomGameOver();
return;
}
enemy.lastWasIntersecting = isIntersecting;
}
// Save lastY for player
player.lastY = player.y;
player.lastX = player.x;
};