/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1", { highScore: 0, leaderboard: [] }); /**** * Classes ****/ var Bubble = Container.expand(function (isGolden, isBonus) { var self = Container.call(this); self.isGolden = isGolden || false; self.isBonus = isBonus || false; self.speed = Math.random() * 2 + 1; // Random speed between 1-3 self.points = self.isBonus ? 259 : self.isGolden ? 50 : 10; var assetName = self.isBonus ? 'bonusBubble' : self.isGolden ? 'goldenBubble' : 'bubble'; var bubbleGraphics = self.attachAsset(assetName, { anchorX: 0.5, anchorY: 0.5 }); // Bonus bubbles get extreme pulsing effect if (self.isBonus) { tween(bubbleGraphics, { scaleX: 1.5, scaleY: 1.5, rotation: Math.PI / 4 }, { duration: 400, easing: tween.bounceOut, onFinish: function onFinish() { tween(bubbleGraphics, { scaleX: 0.8, scaleY: 0.8, rotation: -Math.PI / 4 }, { duration: 400, easing: tween.bounceOut, onFinish: function onFinish() { // Loop the extreme pulsing effect if (self.parent) { tween(bubbleGraphics, { scaleX: 1.5, scaleY: 1.5, rotation: Math.PI / 4 }, { duration: 400, easing: tween.bounceOut }); } } }); } }); } else if (self.isGolden) { tween(bubbleGraphics, { scaleX: 1.2, scaleY: 1.2 }, { duration: 800, easing: tween.easeInOut, onFinish: function onFinish() { tween(bubbleGraphics, { scaleX: 1, scaleY: 1 }, { duration: 800, easing: tween.easeInOut, onFinish: function onFinish() { // Loop the pulsing effect if (self.parent) { // Only continue if bubble still exists tween(bubbleGraphics, { scaleX: 1.2, scaleY: 1.2 }, { duration: 800, easing: tween.easeInOut }); } } }); } }); } // Add slight floating animation var floatOffset = Math.random() * Math.PI * 2; self.update = function () { self.y -= self.speed; // Add slight horizontal wobble self.x += Math.sin(LK.ticks * 0.05 + floatOffset) * 0.5; // Add special effects for different bubble types if (self.isBonus) { // Bonus bubbles get super fast rainbow cycling and extra wobble var fastColorPhase = (LK.ticks * 0.1 + floatOffset) % (Math.PI * 2); var red = Math.floor(Math.sin(fastColorPhase) * 127 + 128); var green = Math.floor(Math.sin(fastColorPhase + 2.094) * 127 + 128); var blue = Math.floor(Math.sin(fastColorPhase + 4.188) * 127 + 128); var rainbowColor = red << 16 | green << 8 | blue; bubbleGraphics.tint = rainbowColor; // Extra wobble for bonus bubbles self.x += Math.sin(LK.ticks * 0.1 + floatOffset) * 2; } else if (!self.isGolden) { var colorPhase = (LK.ticks * 0.02 + floatOffset) % (Math.PI * 2); var red = Math.floor(Math.sin(colorPhase) * 127 + 128); var green = Math.floor(Math.sin(colorPhase + 2.094) * 127 + 128); var blue = Math.floor(Math.sin(colorPhase + 4.188) * 127 + 128); var rainbowColor = red << 16 | green << 8 | blue; bubbleGraphics.tint = rainbowColor; } }; self.down = function (x, y, obj) { self.pop(); }; self.pop = function () { // Combo system comboCount++; comboTimer = 180; // 3 seconds to maintain combo var comboMultiplier = Math.min(Math.floor(comboCount / 5) + 1, 5); var finalPoints = self.points * comboMultiplier; // Add points to score LK.setScore(LK.getScore() + finalPoints); // Create floating score indicator var scoreIndicator = new Text2('+' + finalPoints, { size: 50 + comboMultiplier * 10, fill: self.isGolden ? 0xFFD700 : 0x00FF88 }); scoreIndicator.anchor.set(0.5, 0.5); scoreIndicator.x = self.x; scoreIndicator.y = self.y; game.addChild(scoreIndicator); // Animate the floating score tween(scoreIndicator, { y: self.y - 100, alpha: 0, scaleX: 1.5, scaleY: 1.5 }, { duration: 1200, easing: tween.easeOut, onFinish: function onFinish() { scoreIndicator.destroy(); } }); // Show combo text for exciting combos if (comboCount >= 5 && comboCount % 5 === 0) { comboTxt.setText('COMBO x' + comboMultiplier + '!'); comboTxt.alpha = 1; comboTxt.scaleX = 1.5; comboTxt.scaleY = 1.5; tween(comboTxt, { alpha: 0, scaleX: 1, scaleY: 1 }, { duration: 1500 }); // Screen shake for big combos screenShakeIntensity = Math.min(comboMultiplier * 2, 10); // Add screen zoom effect for mega combos if (comboCount >= 10) { tween(game, { scaleX: 1.05, scaleY: 1.05 }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { tween(game, { scaleX: 1, scaleY: 1 }, { duration: 300, easing: tween.bounceOut }); } }); } } // Play pop sound and special effects if (self.isBonus) { LK.getSound('bonusPop').play(); // Bonus bubble creates intense orange screen flash LK.effects.flashScreen(0xFF6B35, 600); // Extra screen shake for bonus bubbles screenShakeIntensity = Math.max(screenShakeIntensity, 15); } else if (self.isGolden) { LK.getSound('goldenPop').play(); // Golden bubble creates screen flash LK.effects.flashScreen(0xFFD700, 300); } else { LK.getSound('pop').play(); } // Create particle burst effect var particleCount = self.isBonus ? 12 : 6; for (var p = 0; p < particleCount; p++) { var particle = LK.getAsset('bubble', { anchorX: 0.5, anchorY: 0.5, scaleX: self.isBonus ? 0.5 : 0.3, scaleY: self.isBonus ? 0.5 : 0.3, x: self.x, y: self.y }); particle.tint = self.isBonus ? 0xFF6B35 : self.isGolden ? 0xFFD700 : bubbleGraphics.tint; game.addChild(particle); var angle = p / particleCount * Math.PI * 2; var distance = self.isBonus ? 120 + Math.random() * 60 : 80 + Math.random() * 40; var targetX = self.x + Math.cos(angle) * distance; var targetY = self.y + Math.sin(angle) * distance; tween(particle, { x: targetX, y: targetY, alpha: 0, scaleX: 0, scaleY: 0, rotation: Math.random() * Math.PI * 2 }, { duration: self.isBonus ? 800 + Math.random() * 400 : 400 + Math.random() * 200, easing: tween.easeOut, onFinish: function onFinish() { particle.destroy(); } }); } // Enhanced pop animation with random rotation var randomRotation = (Math.random() - 0.5) * Math.PI; tween(self, { scaleX: 2, scaleY: 2, alpha: 0, rotation: randomRotation }, { duration: 250, easing: tween.bounceOut, onFinish: function onFinish() { self.destroy(); } }); // Remove from bubbles array for (var i = bubbles.length - 1; i >= 0; i--) { if (bubbles[i] === self) { bubbles.splice(i, 1); break; } } }; return self; }); var Coin = Container.expand(function () { var self = Container.call(this); self.speed = Math.random() * 1.5 + 1; // Random speed between 1-2.5 self.value = 10; var coinGraphics = self.attachAsset('coin', { anchorX: 0.5, anchorY: 0.5 }); // Add spinning animation tween(coinGraphics, { rotation: Math.PI * 2 }, { duration: 1000, easing: tween.linear, onFinish: function onFinish() { if (self.parent) { // Loop the spinning tween(coinGraphics, { rotation: Math.PI * 4 }, { duration: 1000, easing: tween.linear }); } } }); // Add pulsing effect tween(coinGraphics, { scaleX: 1.2, scaleY: 1.2 }, { duration: 800, easing: tween.easeInOut, onFinish: function onFinish() { tween(coinGraphics, { scaleX: 1, scaleY: 1 }, { duration: 800, easing: tween.easeInOut, onFinish: function onFinish() { if (self.parent) { // Loop the pulsing tween(coinGraphics, { scaleX: 1.2, scaleY: 1.2 }, { duration: 800, easing: tween.easeInOut }); } } }); } }); self.update = function () { self.y += self.speed; // Add slight floating wobble self.x += Math.sin(LK.ticks * 0.08) * 0.8; }; self.down = function (x, y, obj) { self.collect(); }; self.collect = function () { // Add coins to player money playerMoney += self.value; // Play collect sound LK.getSound('coinCollect').play(); // Create floating money indicator var moneyIndicator = new Text2('+$' + self.value, { size: 50, fill: 0xFFD700 }); moneyIndicator.anchor.set(0.5, 0.5); moneyIndicator.x = self.x; moneyIndicator.y = self.y; game.addChild(moneyIndicator); // Animate the floating money indicator tween(moneyIndicator, { y: self.y - 80, alpha: 0, scaleX: 1.5, scaleY: 1.5 }, { duration: 1000, easing: tween.easeOut, onFinish: function onFinish() { moneyIndicator.destroy(); } }); // Update money display moneyTxt.setText('Money: $' + playerMoney); // Coin collection animation tween(self, { scaleX: 2, scaleY: 2, alpha: 0, rotation: Math.PI * 2 }, { duration: 300, easing: tween.bounceOut, onFinish: function onFinish() { self.destroy(); } }); // Remove from coins array for (var i = coins.length - 1; i >= 0; i--) { if (coins[i] === self) { coins.splice(i, 1); break; } } }; return self; }); var Enemy = Container.expand(function () { var self = Container.call(this); self.speed = Math.random() * 4 + 3; // Random speed between 3-7 self.hitPlayer = false; var enemyGraphics = self.attachAsset('enemySpike', { anchorX: 0.5, anchorY: 0.5 }); // Add menacing rotation animation tween(enemyGraphics, { rotation: Math.PI * 2 }, { duration: 800, easing: tween.linear, onFinish: function onFinish() { if (self.parent) { // Loop the spinning tween(enemyGraphics, { rotation: Math.PI * 4 }, { duration: 800, easing: tween.linear }); } } }); // Add threatening pulsing effect tween(enemyGraphics, { scaleX: 1.3, scaleY: 1.3 }, { duration: 400, easing: tween.easeInOut, onFinish: function onFinish() { tween(enemyGraphics, { scaleX: 0.9, scaleY: 0.9 }, { duration: 400, easing: tween.easeInOut, onFinish: function onFinish() { if (self.parent) { // Loop the pulsing tween(enemyGraphics, { scaleX: 1.3, scaleY: 1.3 }, { duration: 400, easing: tween.easeInOut }); } } }); } }); self.update = function () { self.y += self.speed; // Add aggressive zigzag movement self.x += Math.sin(LK.ticks * 0.15) * 2; }; self.down = function (x, y, obj) { if (!self.hasHitPlayer) { self.hitPlayer(); } }; self.hitPlayer = function () { if (self.hasHitPlayer) return; self.hasHitPlayer = true; // Play enemy hit sound LK.getSound('enemyHit').play(); // Create death indicator var deathIndicator = new Text2('GAME OVER!', { size: 80, fill: 0xff0000 }); deathIndicator.anchor.set(0.5, 0.5); deathIndicator.x = self.x; deathIndicator.y = self.y; game.addChild(deathIndicator); // Animate the death indicator tween(deathIndicator, { y: self.y - 120, alpha: 0, scaleX: 2, scaleY: 2 }, { duration: 1500, easing: tween.easeOut, onFinish: function onFinish() { deathIndicator.destroy(); } }); // Enemy hit effect tween(self, { scaleX: 3, scaleY: 3, alpha: 0, rotation: Math.PI * 4 }, { duration: 500, easing: tween.bounceOut, onFinish: function onFinish() { self.destroy(); } }); // Remove from enemies array for (var i = enemies.length - 1; i >= 0; i--) { if (enemies[i] === self) { enemies.splice(i, 1); break; } } // Flash screen red and trigger game over immediately LK.effects.flashScreen(0xff0000, 1000); screenShakeIntensity = Math.max(screenShakeIntensity, 20); // Update high score and leaderboard before returning to menu var currentScore = LK.getScore(); if (currentScore > storage.highScore) { storage.highScore = currentScore; } updateLeaderboard(currentScore); // Clean up game objects for (var cleanBubble = bubbles.length - 1; cleanBubble >= 0; cleanBubble--) { bubbles[cleanBubble].destroy(); } bubbles = []; for (var cleanEnemy = enemies.length - 1; cleanEnemy >= 0; cleanEnemy--) { enemies[cleanEnemy].destroy(); } enemies = []; for (var cleanObstacle = obstacles.length - 1; cleanObstacle >= 0; cleanObstacle--) { obstacles[cleanObstacle].destroy(); } obstacles = []; for (var cleanCoin = coins.length - 1; cleanCoin >= 0; cleanCoin--) { coins[cleanCoin].destroy(); } coins = []; // Return to menu after short delay LK.setTimeout(function () { gameState = 'menu'; gameUIContainer.visible = false; gameUIContainer.alpha = 0; menuContainer.visible = true; tween(menuContainer, { alpha: 1 }, { duration: 500, easing: tween.easeInOut, onFinish: function onFinish() { animateMenu(); } }); }, 2000); }; return self; }); var Obstacle = Container.expand(function () { var self = Container.call(this); self.speed = Math.random() * 4 + 5; // Random speed between 5-9 (much faster) var obstacleGraphics = self.attachAsset('obstacle', { anchorX: 0.5, anchorY: 0.5 }); // Add menacing glow effect tween(obstacleGraphics, { scaleX: 1.1, scaleY: 1.1 }, { duration: 600, easing: tween.easeInOut, onFinish: function onFinish() { tween(obstacleGraphics, { scaleX: 0.9, scaleY: 0.9 }, { duration: 600, easing: tween.easeInOut, onFinish: function onFinish() { if (self.parent) { // Loop the pulsing tween(obstacleGraphics, { scaleX: 1.1, scaleY: 1.1 }, { duration: 600, easing: tween.easeInOut }); } } }); } }); self.update = function () { self.y += self.speed; }; self.down = function (x, y, obj) { if (!self.hasHitPlayer) { self.hitPlayer(); } }; self.hitPlayer = function () { if (self.hasHitPlayer) return; self.hasHitPlayer = true; // Play enemy hit sound LK.getSound('enemyHit').play(); // Create death indicator var deathIndicator = new Text2('GAME OVER!', { size: 80, fill: 0xff0000 }); deathIndicator.anchor.set(0.5, 0.5); deathIndicator.x = self.x; deathIndicator.y = self.y; game.addChild(deathIndicator); // Animate the death indicator tween(deathIndicator, { y: self.y - 120, alpha: 0, scaleX: 2, scaleY: 2 }, { duration: 1500, easing: tween.easeOut, onFinish: function onFinish() { deathIndicator.destroy(); } }); // Obstacle hit effect tween(self, { scaleX: 3, scaleY: 3, alpha: 0, rotation: Math.PI * 4 }, { duration: 500, easing: tween.bounceOut, onFinish: function onFinish() { self.destroy(); } }); // Remove from obstacles array for (var i = obstacles.length - 1; i >= 0; i--) { if (obstacles[i] === self) { obstacles.splice(i, 1); break; } } // Flash screen red and trigger game over immediately LK.effects.flashScreen(0xff0000, 1000); screenShakeIntensity = Math.max(screenShakeIntensity, 20); // Update high score and leaderboard before returning to menu var currentScore = LK.getScore(); if (currentScore > storage.highScore) { storage.highScore = currentScore; } updateLeaderboard(currentScore); // Clean up game objects for (var cleanBubble = bubbles.length - 1; cleanBubble >= 0; cleanBubble--) { bubbles[cleanBubble].destroy(); } bubbles = []; for (var cleanEnemy = enemies.length - 1; cleanEnemy >= 0; cleanEnemy--) { enemies[cleanEnemy].destroy(); } enemies = []; for (var cleanObstacle = obstacles.length - 1; cleanObstacle >= 0; cleanObstacle--) { obstacles[cleanObstacle].destroy(); } obstacles = []; for (var cleanCoin = coins.length - 1; cleanCoin >= 0; cleanCoin--) { coins[cleanCoin].destroy(); } coins = []; // Return to menu after short delay LK.setTimeout(function () { gameState = 'menu'; gameUIContainer.visible = false; gameUIContainer.alpha = 0; menuContainer.visible = true; tween(menuContainer, { alpha: 1 }, { duration: 500, easing: tween.easeInOut, onFinish: function onFinish() { animateMenu(); } }); }, 2000); }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x87CEEB }); /**** * Game Code ****/ // Game state management var gameState = 'menu'; // 'menu', 'playing', 'gameOver', 'leaderboard' var bubbles = []; var bubblesEscaped = 0; var maxEscapedBubbles = 10; var spawnTimer = 0; var spawnInterval = 80; // Start spawning faster (80 ticks = ~1.3 seconds) var difficultyTimer = 0; var comboCount = 0; var comboTimer = 0; var screenShakeIntensity = 0; var enemies = []; var enemySpawnTimer = 0; var enemySpawnInterval = 400; // Spawn enemies every 6.7 seconds var obstacles = []; var obstacleSpawnTimer = 0; var obstacleSpawnInterval = 120; // Spawn obstacles every 2 seconds (faster spawning) var coins = []; var coinSpawnTimer = 0; var coinSpawnInterval = 150; // Spawn coins every 2.5 seconds var playerMoney = 0; var showingLeaderboard = false; var leaderboardContainer = new Container(); // Main Menu Elements var menuContainer = new Container(); // Add cover image var coverImage = LK.getAsset('coverImage', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 600, scaleX: 1.5, scaleY: 1.2 }); coverImage.tint = 0x87CEEB; menuContainer.addChild(coverImage); // Add decorative bubbles on cover image var decorBubbles = []; for (var d = 0; d < 8; d++) { var decorBubble = LK.getAsset('bubble', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.3 + Math.random() * 0.4, scaleY: 0.3 + Math.random() * 0.4, x: 1024 + (Math.random() - 0.5) * 500, y: 600 + (Math.random() - 0.5) * 300 }); decorBubble.alpha = 0.7; decorBubbles.push(decorBubble); menuContainer.addChild(decorBubble); } // Add golden decorative bubbles for (var g = 0; g < 3; g++) { var goldenDecor = LK.getAsset('goldenBubble', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.25, scaleY: 0.25, x: 1024 + (Math.random() - 0.5) * 400, y: 600 + (Math.random() - 0.5) * 250 }); goldenDecor.alpha = 0.8; decorBubbles.push(goldenDecor); menuContainer.addChild(goldenDecor); } var titleTxt = new Text2('BUBBLE POP FRENZY', { size: 120, fill: 0xFFD700 }); titleTxt.anchor.set(0.5, 0.5); titleTxt.x = 1024; titleTxt.y = 450; menuContainer.addChild(titleTxt); var playBtn = LK.getAsset('bubble', { anchorX: 0.5, anchorY: 0.5, scaleX: 2.5, scaleY: 2.5, x: 1024, y: 1100 }); playBtn.tint = 0x00FF88; menuContainer.addChild(playBtn); var playTxt = new Text2('TAP TO PLAY', { size: 80, fill: 0xFFFFFF }); playTxt.anchor.set(0.5, 0.5); playTxt.x = 1024; playTxt.y = 1100; menuContainer.addChild(playTxt); var bestScoreMenuTxt = new Text2('Best Score: ' + storage.highScore, { size: 60, fill: 0xFFD700 }); bestScoreMenuTxt.anchor.set(0.5, 0.5); bestScoreMenuTxt.x = 1024; bestScoreMenuTxt.y = 1300; menuContainer.addChild(bestScoreMenuTxt); var instructionsTxt = new Text2('Pop bubbles before they escape!\nGolden bubbles = 50 points\nBonus bubbles = 259 points\nAvoid red spikes - they kill you!', { size: 45, fill: 0xFFFFFF }); instructionsTxt.anchor.set(0.5, 0.5); instructionsTxt.x = 1024; instructionsTxt.y = 1550; menuContainer.addChild(instructionsTxt); var leaderboardBtn = LK.getAsset('bubble', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.8, scaleY: 1.8, x: 1024, y: 1800 }); leaderboardBtn.tint = 0xFFD700; menuContainer.addChild(leaderboardBtn); var leaderboardBtnTxt = new Text2('LEADERBOARD', { size: 60, fill: 0x000000 }); leaderboardBtnTxt.anchor.set(0.5, 0.5); leaderboardBtnTxt.x = 1024; leaderboardBtnTxt.y = 1800; menuContainer.addChild(leaderboardBtnTxt); game.addChild(menuContainer); // Create leaderboard container var leaderboardBg = LK.getAsset('coverImage', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 1366, scaleX: 1.8, scaleY: 2.2 }); leaderboardBg.tint = 0x2c3e50; leaderboardContainer.addChild(leaderboardBg); var leaderboardTitle = new Text2('š LEADERBOARD š', { size: 90, fill: 0xFFD700 }); leaderboardTitle.anchor.set(0.5, 0.5); leaderboardTitle.x = 1024; leaderboardTitle.y = 500; leaderboardContainer.addChild(leaderboardTitle); var backBtn = LK.getAsset('bubble', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 1.5, x: 1024, y: 2200 }); backBtn.tint = 0xFF6B35; leaderboardContainer.addChild(backBtn); var backBtnTxt = new Text2('BACK', { size: 60, fill: 0xFFFFFF }); backBtnTxt.anchor.set(0.5, 0.5); backBtnTxt.x = 1024; backBtnTxt.y = 2200; leaderboardContainer.addChild(backBtnTxt); leaderboardContainer.visible = false; game.addChild(leaderboardContainer); // Animate menu elements function animateMenu() { // Cover image gentle pulsing tween(coverImage, { scaleX: 1.6, scaleY: 1.3, alpha: 0.9 }, { duration: 2500, easing: tween.easeInOut, onFinish: function onFinish() { tween(coverImage, { scaleX: 1.5, scaleY: 1.2, alpha: 1 }, { duration: 2500, easing: tween.easeInOut }); } }); // Animate decorative bubbles for (var db = 0; db < decorBubbles.length; db++) { var bubble = decorBubbles[db]; var delay = db * 200; LK.setTimeout(function (b) { return function () { tween(b, { y: b.y - 20, rotation: Math.PI / 4 }, { duration: 1500 + Math.random() * 1000, easing: tween.easeInOut, onFinish: function onFinish() { tween(b, { y: b.y + 20, rotation: -Math.PI / 4 }, { duration: 1500 + Math.random() * 1000, easing: tween.easeInOut }); } }); }; }(bubble), delay); } // Title pulsing animation tween(titleTxt, { scaleX: 1.2, scaleY: 1.2 }, { duration: 1500, easing: tween.easeInOut, onFinish: function onFinish() { tween(titleTxt, { scaleX: 1, scaleY: 1 }, { duration: 1500, easing: tween.easeInOut, onFinish: function onFinish() { if (gameState === 'menu') { animateMenu(); } } }); } }); // Play button floating animation tween(playBtn, { y: 1080 }, { duration: 2000, easing: tween.easeInOut, onFinish: function onFinish() { tween(playBtn, { y: 1120 }, { duration: 2000, easing: tween.easeInOut, onFinish: function onFinish() { if (gameState === 'menu') { animateMenu(); } } }); } }); // Sync play text with button tween(playTxt, { y: 1080 }, { duration: 2000, easing: tween.easeInOut, onFinish: function onFinish() { tween(playTxt, { y: 1120 }, { duration: 2000, easing: tween.easeInOut }); } }); // Leaderboard button floating animation tween(leaderboardBtn, { y: 1780 }, { duration: 2200, easing: tween.easeInOut, onFinish: function onFinish() { tween(leaderboardBtn, { y: 1820 }, { duration: 2200, easing: tween.easeInOut, onFinish: function onFinish() { if (gameState === 'menu') { animateMenu(); } } }); } }); // Sync leaderboard text with button tween(leaderboardBtnTxt, { y: 1780 }, { duration: 2200, easing: tween.easeInOut, onFinish: function onFinish() { tween(leaderboardBtnTxt, { y: 1820 }, { duration: 2200, easing: tween.easeInOut }); } }); // Rainbow color cycling for title var colorTimer = 0; function cycleColors() { if (gameState !== 'menu') return; colorTimer += 0.05; var red = Math.floor(Math.sin(colorTimer) * 127 + 128); var green = Math.floor(Math.sin(colorTimer + 2.094) * 127 + 128); var blue = Math.floor(Math.sin(colorTimer + 4.188) * 127 + 128); var rainbowColor = red << 16 | green << 8 | blue; titleTxt.tint = rainbowColor; LK.setTimeout(cycleColors, 50); } cycleColors(); } function updateLeaderboard(newScore) { if (newScore <= 0) return; // Initialize storage.leaderboard if it doesn't exist or is invalid if (!storage.leaderboard || !Array.isArray(storage.leaderboard)) { storage.leaderboard = []; } // Get current leaderboard and create a safe copy var currentBoard = []; try { currentBoard = storage.leaderboard.slice(); // Create a copy } catch (e) { // If slice fails, start with empty array currentBoard = []; storage.leaderboard = []; } // Add new score with timestamp var scoreEntry = { score: newScore, date: new Date().toLocaleDateString() }; // Ensure currentBoard is still an array before pushing if (!Array.isArray(currentBoard)) { currentBoard = []; } currentBoard.push(scoreEntry); // Sort by score (highest first) and keep top 10 currentBoard.sort(function (a, b) { return b.score - a.score; }); // Keep only top 10 scores if (currentBoard.length > 10) { currentBoard = currentBoard.slice(0, 10); } // Save back to storage storage.leaderboard = currentBoard; } function showLeaderboard() { if (showingLeaderboard) return; showingLeaderboard = true; gameState = 'leaderboard'; // Hide menu menuContainer.visible = false; // Clear previous leaderboard entries for (var i = leaderboardContainer.children.length - 1; i >= 0; i--) { var child = leaderboardContainer.children[i]; if (child.isScoreEntry) { leaderboardContainer.removeChild(child); } } // Display leaderboard scores var leaderboard = storage.leaderboard || []; for (var i = 0; i < Math.min(leaderboard.length, 10); i++) { var entry = leaderboard[i]; var position = i + 1; var medal = position === 1 ? 'š„' : position === 2 ? 'š„' : position === 3 ? 'š„' : position + '.'; var entryText = new Text2(medal + ' ' + entry.score + ' pts - ' + entry.date, { size: 55, fill: position <= 3 ? 0xFFD700 : 0xFFFFFF }); entryText.anchor.set(0.5, 0.5); entryText.x = 1024; entryText.y = 650 + i * 120; entryText.isScoreEntry = true; leaderboardContainer.addChild(entryText); // Add entrance animation entryText.alpha = 0; entryText.x = 1024 + 200; tween(entryText, { alpha: 1, x: 1024 }, { duration: 300 + i * 100, easing: tween.easeOut }); } // Show empty message if no scores if (leaderboard.length === 0) { var emptyText = new Text2('No scores yet!\nStart playing to set records!', { size: 60, fill: 0xAAAAAAA }); emptyText.anchor.set(0.5, 0.5); emptyText.x = 1024; emptyText.y = 1200; emptyText.isScoreEntry = true; leaderboardContainer.addChild(emptyText); } // Show leaderboard with animation leaderboardContainer.visible = true; leaderboardContainer.alpha = 0; tween(leaderboardContainer, { alpha: 1 }, { duration: 500, easing: tween.easeInOut }); } function hideLeaderboard() { if (!showingLeaderboard) return; showingLeaderboard = false; gameState = 'menu'; tween(leaderboardContainer, { alpha: 0 }, { duration: 300, easing: tween.easeInOut, onFinish: function onFinish() { leaderboardContainer.visible = false; menuContainer.visible = true; menuContainer.alpha = 1; animateMenu(); } }); } // Start menu animations animateMenu(); // Play button interaction playBtn.down = function (x, y, obj) { startGame(); }; playTxt.down = function (x, y, obj) { startGame(); }; // Leaderboard button interactions leaderboardBtn.down = function (x, y, obj) { if (gameState === 'menu') { showLeaderboard(); } }; leaderboardBtnTxt.down = function (x, y, obj) { if (gameState === 'menu') { showLeaderboard(); } }; // Back button interactions backBtn.down = function (x, y, obj) { if (gameState === 'leaderboard') { hideLeaderboard(); } }; backBtnTxt.down = function (x, y, obj) { if (gameState === 'leaderboard') { hideLeaderboard(); } }; function startGame() { if (gameState !== 'menu') return; gameState = 'playing'; menuContainer.alpha = 0; menuContainer.visible = false; // Show game UI gameUIContainer.visible = true; gameUIContainer.alpha = 1; // Reset game variables bubbles = []; bubblesEscaped = 0; spawnTimer = 0; spawnInterval = 80; difficultyTimer = 0; comboCount = 0; comboTimer = 0; screenShakeIntensity = 0; enemies = []; enemySpawnTimer = 0; obstacles = []; obstacleSpawnTimer = 0; coins = []; coinSpawnTimer = 0; playerMoney = 0; LK.setScore(0); // Update displays scoreTxt.setText('Score: 0'); currentScoreTxt.setText('Score: 0'); escapedTxt.setText('Escaped: 0/10'); dangerTxt.setText('DANGER LEVEL: LOW'); moneyTxt.setText('Money: $0'); } // Game UI Container var gameUIContainer = new Container(); // UI Elements var scoreTxt = new Text2('Score: 0', { size: 60, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); var escapedTxt = new Text2('Escaped: 0/10', { size: 50, fill: 0xFFFFFF }); escapedTxt.anchor.set(1, 0); escapedTxt.x = -20; escapedTxt.y = 80; LK.gui.topRight.addChild(escapedTxt); gameUIContainer.addChild(escapedTxt); var highScoreTxt = new Text2('Best: ' + storage.highScore, { size: 45, fill: 0xFFD700 }); highScoreTxt.anchor.set(0, 0); highScoreTxt.x = 20; highScoreTxt.y = 80; LK.gui.topLeft.addChild(highScoreTxt); var currentScoreTxt = new Text2('Score: 0', { size: 50, fill: 0x00FF88 }); currentScoreTxt.anchor.set(0, 0); currentScoreTxt.x = 20; currentScoreTxt.y = 200; LK.gui.topLeft.addChild(currentScoreTxt); var comboTxt = new Text2('', { size: 70, fill: 0xFF6B35 }); comboTxt.anchor.set(0.5, 0.5); LK.gui.center.addChild(comboTxt); gameUIContainer.addChild(comboTxt); var dangerTxt = new Text2('DANGER LEVEL: LOW', { size: 50, fill: 0xff6666 }); dangerTxt.anchor.set(0.5, 0); dangerTxt.y = 140; LK.gui.top.addChild(dangerTxt); gameUIContainer.addChild(dangerTxt); var moneyTxt = new Text2('Money: $0', { size: 55, fill: 0xFFD700 }); moneyTxt.anchor.set(0, 0); moneyTxt.x = 20; moneyTxt.y = 260; LK.gui.topLeft.addChild(moneyTxt); // Initially hide game UI gameUIContainer.visible = false; gameUIContainer.alpha = 0; function spawnBubble() { if (gameState !== 'playing') return; var isBonus = Math.random() < 0.02; // 2% chance for bonus bubble (259 points) var isGolden = !isBonus && Math.random() < 0.1; // 10% chance for golden bubble (only if not bonus) var bubble = new Bubble(isGolden, isBonus); // Random spawn position across screen width bubble.x = Math.random() * (2048 - 200) + 100; bubble.y = 2732 + 60; // Start just below screen bubbles.push(bubble); game.addChild(bubble); } function spawnEnemy() { if (gameState !== 'playing') return; var enemy = new Enemy(); // Random spawn position across screen width enemy.x = Math.random() * (2048 - 200) + 100; enemy.y = -120; // Start above screen enemies.push(enemy); game.addChild(enemy); } function spawnObstacle() { if (gameState !== 'playing') return; var obstacle = new Obstacle(); // Random spawn position across screen width obstacle.x = Math.random() * (2048 - 400) + 200; obstacle.y = -80; // Start above screen obstacles.push(obstacle); game.addChild(obstacle); } function spawnCoin() { if (gameState !== 'playing') return; var coin = new Coin(); // Random spawn position across screen width coin.x = Math.random() * (2048 - 200) + 100; coin.y = -60; // Start above screen coins.push(coin); game.addChild(coin); } function updateDifficulty() { difficultyTimer++; // Increase difficulty every 7 seconds (420 ticks) - faster progression if (difficultyTimer % 420 === 0) { // Decrease spawn interval (spawn faster) spawnInterval = Math.max(25, spawnInterval - 8); // Increase bubble speed more dramatically for (var i = 0; i < bubbles.length; i++) { bubbles[i].speed += 0.4; } } // Every 30 seconds, spawn a burst of golden bubbles for excitement if (difficultyTimer % 1800 === 0) { for (var j = 0; j < 3; j++) { LK.setTimeout(function () { var goldenBubble = new Bubble(true); goldenBubble.x = Math.random() * (2048 - 200) + 100; goldenBubble.y = 2732 + 60; goldenBubble.speed *= 0.7; // Slower so players can catch them bubbles.push(goldenBubble); game.addChild(goldenBubble); }, j * 300); } } // Every 45 seconds, spawn bonus bubble rain for mega excitement if (difficultyTimer % 2700 === 0) { for (var k = 0; k < 5; k++) { LK.setTimeout(function () { var bonusBubble = new Bubble(false, true); bonusBubble.x = Math.random() * (2048 - 200) + 100; bonusBubble.y = 2732 + 60; bonusBubble.speed *= 0.5; // Much slower so players can definitely catch them bubbles.push(bonusBubble); game.addChild(bonusBubble); }, k * 200); } } } game.update = function () { // Only run game logic when playing if (gameState !== 'playing') { // Update best score display in menu if (gameState === 'menu') { bestScoreMenuTxt.setText('Best Score: ' + storage.highScore); } return; } updateDifficulty(); // Combo timer countdown if (comboTimer > 0) { comboTimer--; if (comboTimer === 0) { comboCount = 0; // Reset combo when timer expires } } // Spawn enemies enemySpawnTimer++; if (enemySpawnTimer >= enemySpawnInterval) { spawnEnemy(); enemySpawnTimer = 0; // Increase danger level over time var dangerLevel = Math.floor(difficultyTimer / 600); // Every 10 seconds var dangerText = dangerLevel < 3 ? 'LOW' : dangerLevel < 6 ? 'MEDIUM' : dangerLevel < 10 ? 'HIGH' : 'EXTREME'; dangerTxt.setText('DANGER LEVEL: ' + dangerText); if (dangerLevel >= 3) { dangerTxt.tint = 0xff4444; } if (dangerLevel >= 6) { dangerTxt.tint = 0xff0000; } } // Spawn obstacles obstacleSpawnTimer++; if (obstacleSpawnTimer >= obstacleSpawnInterval) { spawnObstacle(); obstacleSpawnTimer = 0; } // Spawn coins coinSpawnTimer++; if (coinSpawnTimer >= coinSpawnInterval) { spawnCoin(); coinSpawnTimer = 0; } // Screen shake effect if (screenShakeIntensity > 0) { game.x = (Math.random() - 0.5) * screenShakeIntensity; game.y = (Math.random() - 0.5) * screenShakeIntensity; screenShakeIntensity *= 0.9; // Gradually reduce shake if (screenShakeIntensity < 0.1) { screenShakeIntensity = 0; game.x = 0; game.y = 0; } } // Spawn bubbles faster spawnTimer++; if (spawnTimer >= spawnInterval) { spawnBubble(); spawnTimer = 0; } // Check for bubbles that escaped for (var i = bubbles.length - 1; i >= 0; i--) { var bubble = bubbles[i]; // Check if bubble escaped (reached top of screen) if (bubble.y < -60) { bubblesEscaped++; bubble.destroy(); bubbles.splice(i, 1); // Reset combo when bubble escapes comboCount = 0; comboTimer = 0; // Update escaped counter escapedTxt.setText('Escaped: ' + bubblesEscaped + '/' + maxEscapedBubbles); // Check game over condition if (bubblesEscaped >= maxEscapedBubbles) { // Update high score before game over var currentScore = LK.getScore(); if (currentScore > storage.highScore) { storage.highScore = currentScore; highScoreTxt.setText('Best: ' + storage.highScore); } // Add score to leaderboard updateLeaderboard(currentScore); // Clean up game objects for (var cleanBubble = bubbles.length - 1; cleanBubble >= 0; cleanBubble--) { bubbles[cleanBubble].destroy(); } bubbles = []; for (var cleanEnemy = enemies.length - 1; cleanEnemy >= 0; cleanEnemy--) { enemies[cleanEnemy].destroy(); } enemies = []; for (var cleanObstacle = obstacles.length - 1; cleanObstacle >= 0; cleanObstacle--) { obstacles[cleanObstacle].destroy(); } obstacles = []; for (var cleanCoin = coins.length - 1; cleanCoin >= 0; cleanCoin--) { coins[cleanCoin].destroy(); } coins = []; // Return to menu after short delay LK.setTimeout(function () { gameState = 'menu'; gameUIContainer.visible = false; gameUIContainer.alpha = 0; menuContainer.visible = true; tween(menuContainer, { alpha: 1 }, { duration: 500, easing: tween.easeInOut, onFinish: function onFinish() { animateMenu(); } }); }, 2000); LK.showGameOver(); return; } } } // Check for enemies that fell off screen for (var e = enemies.length - 1; e >= 0; e--) { var enemy = enemies[e]; if (enemy.y > 2732 + 120) { enemy.destroy(); enemies.splice(e, 1); } } // Check for obstacles that fell off screen for (var o = obstacles.length - 1; o >= 0; o--) { var obstacle = obstacles[o]; if (obstacle.y > 2732 + 100) { obstacle.destroy(); obstacles.splice(o, 1); } } // Check for coins that fell off screen for (var c = coins.length - 1; c >= 0; c--) { var coin = coins[c]; if (coin.y > 2732 + 60) { coin.destroy(); coins.splice(c, 1); } } // Update score displays scoreTxt.setText('Score: ' + LK.getScore()); currentScoreTxt.setText('Score: ' + LK.getScore()); // Dynamic background color based on score for atmosphere var currentScore = LK.getScore(); var colorPhase = currentScore * 0.01 % (Math.PI * 2); var bgRed = Math.floor(Math.sin(colorPhase) * 40 + 100); var bgGreen = Math.floor(Math.sin(colorPhase + 2.094) * 40 + 150); var bgBlue = Math.floor(Math.sin(colorPhase + 4.188) * 40 + 200); var dynamicBgColor = bgRed << 16 | bgGreen << 8 | bgBlue; game.setBackgroundColor(dynamicBgColor); // Update high score display in real-time if beaten var currentScore = LK.getScore(); if (currentScore > storage.highScore) { storage.highScore = currentScore; highScoreTxt.setText('Best: ' + storage.highScore); highScoreTxt.tint = 0x00FF00; // Green highlight for new record } };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
highScore: 0,
leaderboard: []
});
/****
* Classes
****/
var Bubble = Container.expand(function (isGolden, isBonus) {
var self = Container.call(this);
self.isGolden = isGolden || false;
self.isBonus = isBonus || false;
self.speed = Math.random() * 2 + 1; // Random speed between 1-3
self.points = self.isBonus ? 259 : self.isGolden ? 50 : 10;
var assetName = self.isBonus ? 'bonusBubble' : self.isGolden ? 'goldenBubble' : 'bubble';
var bubbleGraphics = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
// Bonus bubbles get extreme pulsing effect
if (self.isBonus) {
tween(bubbleGraphics, {
scaleX: 1.5,
scaleY: 1.5,
rotation: Math.PI / 4
}, {
duration: 400,
easing: tween.bounceOut,
onFinish: function onFinish() {
tween(bubbleGraphics, {
scaleX: 0.8,
scaleY: 0.8,
rotation: -Math.PI / 4
}, {
duration: 400,
easing: tween.bounceOut,
onFinish: function onFinish() {
// Loop the extreme pulsing effect
if (self.parent) {
tween(bubbleGraphics, {
scaleX: 1.5,
scaleY: 1.5,
rotation: Math.PI / 4
}, {
duration: 400,
easing: tween.bounceOut
});
}
}
});
}
});
} else if (self.isGolden) {
tween(bubbleGraphics, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(bubbleGraphics, {
scaleX: 1,
scaleY: 1
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Loop the pulsing effect
if (self.parent) {
// Only continue if bubble still exists
tween(bubbleGraphics, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 800,
easing: tween.easeInOut
});
}
}
});
}
});
}
// Add slight floating animation
var floatOffset = Math.random() * Math.PI * 2;
self.update = function () {
self.y -= self.speed;
// Add slight horizontal wobble
self.x += Math.sin(LK.ticks * 0.05 + floatOffset) * 0.5;
// Add special effects for different bubble types
if (self.isBonus) {
// Bonus bubbles get super fast rainbow cycling and extra wobble
var fastColorPhase = (LK.ticks * 0.1 + floatOffset) % (Math.PI * 2);
var red = Math.floor(Math.sin(fastColorPhase) * 127 + 128);
var green = Math.floor(Math.sin(fastColorPhase + 2.094) * 127 + 128);
var blue = Math.floor(Math.sin(fastColorPhase + 4.188) * 127 + 128);
var rainbowColor = red << 16 | green << 8 | blue;
bubbleGraphics.tint = rainbowColor;
// Extra wobble for bonus bubbles
self.x += Math.sin(LK.ticks * 0.1 + floatOffset) * 2;
} else if (!self.isGolden) {
var colorPhase = (LK.ticks * 0.02 + floatOffset) % (Math.PI * 2);
var red = Math.floor(Math.sin(colorPhase) * 127 + 128);
var green = Math.floor(Math.sin(colorPhase + 2.094) * 127 + 128);
var blue = Math.floor(Math.sin(colorPhase + 4.188) * 127 + 128);
var rainbowColor = red << 16 | green << 8 | blue;
bubbleGraphics.tint = rainbowColor;
}
};
self.down = function (x, y, obj) {
self.pop();
};
self.pop = function () {
// Combo system
comboCount++;
comboTimer = 180; // 3 seconds to maintain combo
var comboMultiplier = Math.min(Math.floor(comboCount / 5) + 1, 5);
var finalPoints = self.points * comboMultiplier;
// Add points to score
LK.setScore(LK.getScore() + finalPoints);
// Create floating score indicator
var scoreIndicator = new Text2('+' + finalPoints, {
size: 50 + comboMultiplier * 10,
fill: self.isGolden ? 0xFFD700 : 0x00FF88
});
scoreIndicator.anchor.set(0.5, 0.5);
scoreIndicator.x = self.x;
scoreIndicator.y = self.y;
game.addChild(scoreIndicator);
// Animate the floating score
tween(scoreIndicator, {
y: self.y - 100,
alpha: 0,
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 1200,
easing: tween.easeOut,
onFinish: function onFinish() {
scoreIndicator.destroy();
}
});
// Show combo text for exciting combos
if (comboCount >= 5 && comboCount % 5 === 0) {
comboTxt.setText('COMBO x' + comboMultiplier + '!');
comboTxt.alpha = 1;
comboTxt.scaleX = 1.5;
comboTxt.scaleY = 1.5;
tween(comboTxt, {
alpha: 0,
scaleX: 1,
scaleY: 1
}, {
duration: 1500
});
// Screen shake for big combos
screenShakeIntensity = Math.min(comboMultiplier * 2, 10);
// Add screen zoom effect for mega combos
if (comboCount >= 10) {
tween(game, {
scaleX: 1.05,
scaleY: 1.05
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(game, {
scaleX: 1,
scaleY: 1
}, {
duration: 300,
easing: tween.bounceOut
});
}
});
}
}
// Play pop sound and special effects
if (self.isBonus) {
LK.getSound('bonusPop').play();
// Bonus bubble creates intense orange screen flash
LK.effects.flashScreen(0xFF6B35, 600);
// Extra screen shake for bonus bubbles
screenShakeIntensity = Math.max(screenShakeIntensity, 15);
} else if (self.isGolden) {
LK.getSound('goldenPop').play();
// Golden bubble creates screen flash
LK.effects.flashScreen(0xFFD700, 300);
} else {
LK.getSound('pop').play();
}
// Create particle burst effect
var particleCount = self.isBonus ? 12 : 6;
for (var p = 0; p < particleCount; p++) {
var particle = LK.getAsset('bubble', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: self.isBonus ? 0.5 : 0.3,
scaleY: self.isBonus ? 0.5 : 0.3,
x: self.x,
y: self.y
});
particle.tint = self.isBonus ? 0xFF6B35 : self.isGolden ? 0xFFD700 : bubbleGraphics.tint;
game.addChild(particle);
var angle = p / particleCount * Math.PI * 2;
var distance = self.isBonus ? 120 + Math.random() * 60 : 80 + Math.random() * 40;
var targetX = self.x + Math.cos(angle) * distance;
var targetY = self.y + Math.sin(angle) * distance;
tween(particle, {
x: targetX,
y: targetY,
alpha: 0,
scaleX: 0,
scaleY: 0,
rotation: Math.random() * Math.PI * 2
}, {
duration: self.isBonus ? 800 + Math.random() * 400 : 400 + Math.random() * 200,
easing: tween.easeOut,
onFinish: function onFinish() {
particle.destroy();
}
});
}
// Enhanced pop animation with random rotation
var randomRotation = (Math.random() - 0.5) * Math.PI;
tween(self, {
scaleX: 2,
scaleY: 2,
alpha: 0,
rotation: randomRotation
}, {
duration: 250,
easing: tween.bounceOut,
onFinish: function onFinish() {
self.destroy();
}
});
// Remove from bubbles array
for (var i = bubbles.length - 1; i >= 0; i--) {
if (bubbles[i] === self) {
bubbles.splice(i, 1);
break;
}
}
};
return self;
});
var Coin = Container.expand(function () {
var self = Container.call(this);
self.speed = Math.random() * 1.5 + 1; // Random speed between 1-2.5
self.value = 10;
var coinGraphics = self.attachAsset('coin', {
anchorX: 0.5,
anchorY: 0.5
});
// Add spinning animation
tween(coinGraphics, {
rotation: Math.PI * 2
}, {
duration: 1000,
easing: tween.linear,
onFinish: function onFinish() {
if (self.parent) {
// Loop the spinning
tween(coinGraphics, {
rotation: Math.PI * 4
}, {
duration: 1000,
easing: tween.linear
});
}
}
});
// Add pulsing effect
tween(coinGraphics, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(coinGraphics, {
scaleX: 1,
scaleY: 1
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (self.parent) {
// Loop the pulsing
tween(coinGraphics, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 800,
easing: tween.easeInOut
});
}
}
});
}
});
self.update = function () {
self.y += self.speed;
// Add slight floating wobble
self.x += Math.sin(LK.ticks * 0.08) * 0.8;
};
self.down = function (x, y, obj) {
self.collect();
};
self.collect = function () {
// Add coins to player money
playerMoney += self.value;
// Play collect sound
LK.getSound('coinCollect').play();
// Create floating money indicator
var moneyIndicator = new Text2('+$' + self.value, {
size: 50,
fill: 0xFFD700
});
moneyIndicator.anchor.set(0.5, 0.5);
moneyIndicator.x = self.x;
moneyIndicator.y = self.y;
game.addChild(moneyIndicator);
// Animate the floating money indicator
tween(moneyIndicator, {
y: self.y - 80,
alpha: 0,
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
moneyIndicator.destroy();
}
});
// Update money display
moneyTxt.setText('Money: $' + playerMoney);
// Coin collection animation
tween(self, {
scaleX: 2,
scaleY: 2,
alpha: 0,
rotation: Math.PI * 2
}, {
duration: 300,
easing: tween.bounceOut,
onFinish: function onFinish() {
self.destroy();
}
});
// Remove from coins array
for (var i = coins.length - 1; i >= 0; i--) {
if (coins[i] === self) {
coins.splice(i, 1);
break;
}
}
};
return self;
});
var Enemy = Container.expand(function () {
var self = Container.call(this);
self.speed = Math.random() * 4 + 3; // Random speed between 3-7
self.hitPlayer = false;
var enemyGraphics = self.attachAsset('enemySpike', {
anchorX: 0.5,
anchorY: 0.5
});
// Add menacing rotation animation
tween(enemyGraphics, {
rotation: Math.PI * 2
}, {
duration: 800,
easing: tween.linear,
onFinish: function onFinish() {
if (self.parent) {
// Loop the spinning
tween(enemyGraphics, {
rotation: Math.PI * 4
}, {
duration: 800,
easing: tween.linear
});
}
}
});
// Add threatening pulsing effect
tween(enemyGraphics, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 400,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(enemyGraphics, {
scaleX: 0.9,
scaleY: 0.9
}, {
duration: 400,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (self.parent) {
// Loop the pulsing
tween(enemyGraphics, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 400,
easing: tween.easeInOut
});
}
}
});
}
});
self.update = function () {
self.y += self.speed;
// Add aggressive zigzag movement
self.x += Math.sin(LK.ticks * 0.15) * 2;
};
self.down = function (x, y, obj) {
if (!self.hasHitPlayer) {
self.hitPlayer();
}
};
self.hitPlayer = function () {
if (self.hasHitPlayer) return;
self.hasHitPlayer = true;
// Play enemy hit sound
LK.getSound('enemyHit').play();
// Create death indicator
var deathIndicator = new Text2('GAME OVER!', {
size: 80,
fill: 0xff0000
});
deathIndicator.anchor.set(0.5, 0.5);
deathIndicator.x = self.x;
deathIndicator.y = self.y;
game.addChild(deathIndicator);
// Animate the death indicator
tween(deathIndicator, {
y: self.y - 120,
alpha: 0,
scaleX: 2,
scaleY: 2
}, {
duration: 1500,
easing: tween.easeOut,
onFinish: function onFinish() {
deathIndicator.destroy();
}
});
// Enemy hit effect
tween(self, {
scaleX: 3,
scaleY: 3,
alpha: 0,
rotation: Math.PI * 4
}, {
duration: 500,
easing: tween.bounceOut,
onFinish: function onFinish() {
self.destroy();
}
});
// Remove from enemies array
for (var i = enemies.length - 1; i >= 0; i--) {
if (enemies[i] === self) {
enemies.splice(i, 1);
break;
}
}
// Flash screen red and trigger game over immediately
LK.effects.flashScreen(0xff0000, 1000);
screenShakeIntensity = Math.max(screenShakeIntensity, 20);
// Update high score and leaderboard before returning to menu
var currentScore = LK.getScore();
if (currentScore > storage.highScore) {
storage.highScore = currentScore;
}
updateLeaderboard(currentScore);
// Clean up game objects
for (var cleanBubble = bubbles.length - 1; cleanBubble >= 0; cleanBubble--) {
bubbles[cleanBubble].destroy();
}
bubbles = [];
for (var cleanEnemy = enemies.length - 1; cleanEnemy >= 0; cleanEnemy--) {
enemies[cleanEnemy].destroy();
}
enemies = [];
for (var cleanObstacle = obstacles.length - 1; cleanObstacle >= 0; cleanObstacle--) {
obstacles[cleanObstacle].destroy();
}
obstacles = [];
for (var cleanCoin = coins.length - 1; cleanCoin >= 0; cleanCoin--) {
coins[cleanCoin].destroy();
}
coins = [];
// Return to menu after short delay
LK.setTimeout(function () {
gameState = 'menu';
gameUIContainer.visible = false;
gameUIContainer.alpha = 0;
menuContainer.visible = true;
tween(menuContainer, {
alpha: 1
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
animateMenu();
}
});
}, 2000);
};
return self;
});
var Obstacle = Container.expand(function () {
var self = Container.call(this);
self.speed = Math.random() * 4 + 5; // Random speed between 5-9 (much faster)
var obstacleGraphics = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5
});
// Add menacing glow effect
tween(obstacleGraphics, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 600,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(obstacleGraphics, {
scaleX: 0.9,
scaleY: 0.9
}, {
duration: 600,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (self.parent) {
// Loop the pulsing
tween(obstacleGraphics, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 600,
easing: tween.easeInOut
});
}
}
});
}
});
self.update = function () {
self.y += self.speed;
};
self.down = function (x, y, obj) {
if (!self.hasHitPlayer) {
self.hitPlayer();
}
};
self.hitPlayer = function () {
if (self.hasHitPlayer) return;
self.hasHitPlayer = true;
// Play enemy hit sound
LK.getSound('enemyHit').play();
// Create death indicator
var deathIndicator = new Text2('GAME OVER!', {
size: 80,
fill: 0xff0000
});
deathIndicator.anchor.set(0.5, 0.5);
deathIndicator.x = self.x;
deathIndicator.y = self.y;
game.addChild(deathIndicator);
// Animate the death indicator
tween(deathIndicator, {
y: self.y - 120,
alpha: 0,
scaleX: 2,
scaleY: 2
}, {
duration: 1500,
easing: tween.easeOut,
onFinish: function onFinish() {
deathIndicator.destroy();
}
});
// Obstacle hit effect
tween(self, {
scaleX: 3,
scaleY: 3,
alpha: 0,
rotation: Math.PI * 4
}, {
duration: 500,
easing: tween.bounceOut,
onFinish: function onFinish() {
self.destroy();
}
});
// Remove from obstacles array
for (var i = obstacles.length - 1; i >= 0; i--) {
if (obstacles[i] === self) {
obstacles.splice(i, 1);
break;
}
}
// Flash screen red and trigger game over immediately
LK.effects.flashScreen(0xff0000, 1000);
screenShakeIntensity = Math.max(screenShakeIntensity, 20);
// Update high score and leaderboard before returning to menu
var currentScore = LK.getScore();
if (currentScore > storage.highScore) {
storage.highScore = currentScore;
}
updateLeaderboard(currentScore);
// Clean up game objects
for (var cleanBubble = bubbles.length - 1; cleanBubble >= 0; cleanBubble--) {
bubbles[cleanBubble].destroy();
}
bubbles = [];
for (var cleanEnemy = enemies.length - 1; cleanEnemy >= 0; cleanEnemy--) {
enemies[cleanEnemy].destroy();
}
enemies = [];
for (var cleanObstacle = obstacles.length - 1; cleanObstacle >= 0; cleanObstacle--) {
obstacles[cleanObstacle].destroy();
}
obstacles = [];
for (var cleanCoin = coins.length - 1; cleanCoin >= 0; cleanCoin--) {
coins[cleanCoin].destroy();
}
coins = [];
// Return to menu after short delay
LK.setTimeout(function () {
gameState = 'menu';
gameUIContainer.visible = false;
gameUIContainer.alpha = 0;
menuContainer.visible = true;
tween(menuContainer, {
alpha: 1
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
animateMenu();
}
});
}, 2000);
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87CEEB
});
/****
* Game Code
****/
// Game state management
var gameState = 'menu'; // 'menu', 'playing', 'gameOver', 'leaderboard'
var bubbles = [];
var bubblesEscaped = 0;
var maxEscapedBubbles = 10;
var spawnTimer = 0;
var spawnInterval = 80; // Start spawning faster (80 ticks = ~1.3 seconds)
var difficultyTimer = 0;
var comboCount = 0;
var comboTimer = 0;
var screenShakeIntensity = 0;
var enemies = [];
var enemySpawnTimer = 0;
var enemySpawnInterval = 400; // Spawn enemies every 6.7 seconds
var obstacles = [];
var obstacleSpawnTimer = 0;
var obstacleSpawnInterval = 120; // Spawn obstacles every 2 seconds (faster spawning)
var coins = [];
var coinSpawnTimer = 0;
var coinSpawnInterval = 150; // Spawn coins every 2.5 seconds
var playerMoney = 0;
var showingLeaderboard = false;
var leaderboardContainer = new Container();
// Main Menu Elements
var menuContainer = new Container();
// Add cover image
var coverImage = LK.getAsset('coverImage', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 600,
scaleX: 1.5,
scaleY: 1.2
});
coverImage.tint = 0x87CEEB;
menuContainer.addChild(coverImage);
// Add decorative bubbles on cover image
var decorBubbles = [];
for (var d = 0; d < 8; d++) {
var decorBubble = LK.getAsset('bubble', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3 + Math.random() * 0.4,
scaleY: 0.3 + Math.random() * 0.4,
x: 1024 + (Math.random() - 0.5) * 500,
y: 600 + (Math.random() - 0.5) * 300
});
decorBubble.alpha = 0.7;
decorBubbles.push(decorBubble);
menuContainer.addChild(decorBubble);
}
// Add golden decorative bubbles
for (var g = 0; g < 3; g++) {
var goldenDecor = LK.getAsset('goldenBubble', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.25,
scaleY: 0.25,
x: 1024 + (Math.random() - 0.5) * 400,
y: 600 + (Math.random() - 0.5) * 250
});
goldenDecor.alpha = 0.8;
decorBubbles.push(goldenDecor);
menuContainer.addChild(goldenDecor);
}
var titleTxt = new Text2('BUBBLE POP FRENZY', {
size: 120,
fill: 0xFFD700
});
titleTxt.anchor.set(0.5, 0.5);
titleTxt.x = 1024;
titleTxt.y = 450;
menuContainer.addChild(titleTxt);
var playBtn = LK.getAsset('bubble', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.5,
scaleY: 2.5,
x: 1024,
y: 1100
});
playBtn.tint = 0x00FF88;
menuContainer.addChild(playBtn);
var playTxt = new Text2('TAP TO PLAY', {
size: 80,
fill: 0xFFFFFF
});
playTxt.anchor.set(0.5, 0.5);
playTxt.x = 1024;
playTxt.y = 1100;
menuContainer.addChild(playTxt);
var bestScoreMenuTxt = new Text2('Best Score: ' + storage.highScore, {
size: 60,
fill: 0xFFD700
});
bestScoreMenuTxt.anchor.set(0.5, 0.5);
bestScoreMenuTxt.x = 1024;
bestScoreMenuTxt.y = 1300;
menuContainer.addChild(bestScoreMenuTxt);
var instructionsTxt = new Text2('Pop bubbles before they escape!\nGolden bubbles = 50 points\nBonus bubbles = 259 points\nAvoid red spikes - they kill you!', {
size: 45,
fill: 0xFFFFFF
});
instructionsTxt.anchor.set(0.5, 0.5);
instructionsTxt.x = 1024;
instructionsTxt.y = 1550;
menuContainer.addChild(instructionsTxt);
var leaderboardBtn = LK.getAsset('bubble', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.8,
scaleY: 1.8,
x: 1024,
y: 1800
});
leaderboardBtn.tint = 0xFFD700;
menuContainer.addChild(leaderboardBtn);
var leaderboardBtnTxt = new Text2('LEADERBOARD', {
size: 60,
fill: 0x000000
});
leaderboardBtnTxt.anchor.set(0.5, 0.5);
leaderboardBtnTxt.x = 1024;
leaderboardBtnTxt.y = 1800;
menuContainer.addChild(leaderboardBtnTxt);
game.addChild(menuContainer);
// Create leaderboard container
var leaderboardBg = LK.getAsset('coverImage', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366,
scaleX: 1.8,
scaleY: 2.2
});
leaderboardBg.tint = 0x2c3e50;
leaderboardContainer.addChild(leaderboardBg);
var leaderboardTitle = new Text2('š LEADERBOARD š', {
size: 90,
fill: 0xFFD700
});
leaderboardTitle.anchor.set(0.5, 0.5);
leaderboardTitle.x = 1024;
leaderboardTitle.y = 500;
leaderboardContainer.addChild(leaderboardTitle);
var backBtn = LK.getAsset('bubble', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5,
x: 1024,
y: 2200
});
backBtn.tint = 0xFF6B35;
leaderboardContainer.addChild(backBtn);
var backBtnTxt = new Text2('BACK', {
size: 60,
fill: 0xFFFFFF
});
backBtnTxt.anchor.set(0.5, 0.5);
backBtnTxt.x = 1024;
backBtnTxt.y = 2200;
leaderboardContainer.addChild(backBtnTxt);
leaderboardContainer.visible = false;
game.addChild(leaderboardContainer);
// Animate menu elements
function animateMenu() {
// Cover image gentle pulsing
tween(coverImage, {
scaleX: 1.6,
scaleY: 1.3,
alpha: 0.9
}, {
duration: 2500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(coverImage, {
scaleX: 1.5,
scaleY: 1.2,
alpha: 1
}, {
duration: 2500,
easing: tween.easeInOut
});
}
});
// Animate decorative bubbles
for (var db = 0; db < decorBubbles.length; db++) {
var bubble = decorBubbles[db];
var delay = db * 200;
LK.setTimeout(function (b) {
return function () {
tween(b, {
y: b.y - 20,
rotation: Math.PI / 4
}, {
duration: 1500 + Math.random() * 1000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(b, {
y: b.y + 20,
rotation: -Math.PI / 4
}, {
duration: 1500 + Math.random() * 1000,
easing: tween.easeInOut
});
}
});
};
}(bubble), delay);
}
// Title pulsing animation
tween(titleTxt, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(titleTxt, {
scaleX: 1,
scaleY: 1
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (gameState === 'menu') {
animateMenu();
}
}
});
}
});
// Play button floating animation
tween(playBtn, {
y: 1080
}, {
duration: 2000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(playBtn, {
y: 1120
}, {
duration: 2000,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (gameState === 'menu') {
animateMenu();
}
}
});
}
});
// Sync play text with button
tween(playTxt, {
y: 1080
}, {
duration: 2000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(playTxt, {
y: 1120
}, {
duration: 2000,
easing: tween.easeInOut
});
}
});
// Leaderboard button floating animation
tween(leaderboardBtn, {
y: 1780
}, {
duration: 2200,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(leaderboardBtn, {
y: 1820
}, {
duration: 2200,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (gameState === 'menu') {
animateMenu();
}
}
});
}
});
// Sync leaderboard text with button
tween(leaderboardBtnTxt, {
y: 1780
}, {
duration: 2200,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(leaderboardBtnTxt, {
y: 1820
}, {
duration: 2200,
easing: tween.easeInOut
});
}
});
// Rainbow color cycling for title
var colorTimer = 0;
function cycleColors() {
if (gameState !== 'menu') return;
colorTimer += 0.05;
var red = Math.floor(Math.sin(colorTimer) * 127 + 128);
var green = Math.floor(Math.sin(colorTimer + 2.094) * 127 + 128);
var blue = Math.floor(Math.sin(colorTimer + 4.188) * 127 + 128);
var rainbowColor = red << 16 | green << 8 | blue;
titleTxt.tint = rainbowColor;
LK.setTimeout(cycleColors, 50);
}
cycleColors();
}
function updateLeaderboard(newScore) {
if (newScore <= 0) return;
// Initialize storage.leaderboard if it doesn't exist or is invalid
if (!storage.leaderboard || !Array.isArray(storage.leaderboard)) {
storage.leaderboard = [];
}
// Get current leaderboard and create a safe copy
var currentBoard = [];
try {
currentBoard = storage.leaderboard.slice(); // Create a copy
} catch (e) {
// If slice fails, start with empty array
currentBoard = [];
storage.leaderboard = [];
}
// Add new score with timestamp
var scoreEntry = {
score: newScore,
date: new Date().toLocaleDateString()
};
// Ensure currentBoard is still an array before pushing
if (!Array.isArray(currentBoard)) {
currentBoard = [];
}
currentBoard.push(scoreEntry);
// Sort by score (highest first) and keep top 10
currentBoard.sort(function (a, b) {
return b.score - a.score;
});
// Keep only top 10 scores
if (currentBoard.length > 10) {
currentBoard = currentBoard.slice(0, 10);
}
// Save back to storage
storage.leaderboard = currentBoard;
}
function showLeaderboard() {
if (showingLeaderboard) return;
showingLeaderboard = true;
gameState = 'leaderboard';
// Hide menu
menuContainer.visible = false;
// Clear previous leaderboard entries
for (var i = leaderboardContainer.children.length - 1; i >= 0; i--) {
var child = leaderboardContainer.children[i];
if (child.isScoreEntry) {
leaderboardContainer.removeChild(child);
}
}
// Display leaderboard scores
var leaderboard = storage.leaderboard || [];
for (var i = 0; i < Math.min(leaderboard.length, 10); i++) {
var entry = leaderboard[i];
var position = i + 1;
var medal = position === 1 ? 'š„' : position === 2 ? 'š„' : position === 3 ? 'š„' : position + '.';
var entryText = new Text2(medal + ' ' + entry.score + ' pts - ' + entry.date, {
size: 55,
fill: position <= 3 ? 0xFFD700 : 0xFFFFFF
});
entryText.anchor.set(0.5, 0.5);
entryText.x = 1024;
entryText.y = 650 + i * 120;
entryText.isScoreEntry = true;
leaderboardContainer.addChild(entryText);
// Add entrance animation
entryText.alpha = 0;
entryText.x = 1024 + 200;
tween(entryText, {
alpha: 1,
x: 1024
}, {
duration: 300 + i * 100,
easing: tween.easeOut
});
}
// Show empty message if no scores
if (leaderboard.length === 0) {
var emptyText = new Text2('No scores yet!\nStart playing to set records!', {
size: 60,
fill: 0xAAAAAAA
});
emptyText.anchor.set(0.5, 0.5);
emptyText.x = 1024;
emptyText.y = 1200;
emptyText.isScoreEntry = true;
leaderboardContainer.addChild(emptyText);
}
// Show leaderboard with animation
leaderboardContainer.visible = true;
leaderboardContainer.alpha = 0;
tween(leaderboardContainer, {
alpha: 1
}, {
duration: 500,
easing: tween.easeInOut
});
}
function hideLeaderboard() {
if (!showingLeaderboard) return;
showingLeaderboard = false;
gameState = 'menu';
tween(leaderboardContainer, {
alpha: 0
}, {
duration: 300,
easing: tween.easeInOut,
onFinish: function onFinish() {
leaderboardContainer.visible = false;
menuContainer.visible = true;
menuContainer.alpha = 1;
animateMenu();
}
});
}
// Start menu animations
animateMenu();
// Play button interaction
playBtn.down = function (x, y, obj) {
startGame();
};
playTxt.down = function (x, y, obj) {
startGame();
};
// Leaderboard button interactions
leaderboardBtn.down = function (x, y, obj) {
if (gameState === 'menu') {
showLeaderboard();
}
};
leaderboardBtnTxt.down = function (x, y, obj) {
if (gameState === 'menu') {
showLeaderboard();
}
};
// Back button interactions
backBtn.down = function (x, y, obj) {
if (gameState === 'leaderboard') {
hideLeaderboard();
}
};
backBtnTxt.down = function (x, y, obj) {
if (gameState === 'leaderboard') {
hideLeaderboard();
}
};
function startGame() {
if (gameState !== 'menu') return;
gameState = 'playing';
menuContainer.alpha = 0;
menuContainer.visible = false;
// Show game UI
gameUIContainer.visible = true;
gameUIContainer.alpha = 1;
// Reset game variables
bubbles = [];
bubblesEscaped = 0;
spawnTimer = 0;
spawnInterval = 80;
difficultyTimer = 0;
comboCount = 0;
comboTimer = 0;
screenShakeIntensity = 0;
enemies = [];
enemySpawnTimer = 0;
obstacles = [];
obstacleSpawnTimer = 0;
coins = [];
coinSpawnTimer = 0;
playerMoney = 0;
LK.setScore(0);
// Update displays
scoreTxt.setText('Score: 0');
currentScoreTxt.setText('Score: 0');
escapedTxt.setText('Escaped: 0/10');
dangerTxt.setText('DANGER LEVEL: LOW');
moneyTxt.setText('Money: $0');
}
// Game UI Container
var gameUIContainer = new Container();
// UI Elements
var scoreTxt = new Text2('Score: 0', {
size: 60,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
var escapedTxt = new Text2('Escaped: 0/10', {
size: 50,
fill: 0xFFFFFF
});
escapedTxt.anchor.set(1, 0);
escapedTxt.x = -20;
escapedTxt.y = 80;
LK.gui.topRight.addChild(escapedTxt);
gameUIContainer.addChild(escapedTxt);
var highScoreTxt = new Text2('Best: ' + storage.highScore, {
size: 45,
fill: 0xFFD700
});
highScoreTxt.anchor.set(0, 0);
highScoreTxt.x = 20;
highScoreTxt.y = 80;
LK.gui.topLeft.addChild(highScoreTxt);
var currentScoreTxt = new Text2('Score: 0', {
size: 50,
fill: 0x00FF88
});
currentScoreTxt.anchor.set(0, 0);
currentScoreTxt.x = 20;
currentScoreTxt.y = 200;
LK.gui.topLeft.addChild(currentScoreTxt);
var comboTxt = new Text2('', {
size: 70,
fill: 0xFF6B35
});
comboTxt.anchor.set(0.5, 0.5);
LK.gui.center.addChild(comboTxt);
gameUIContainer.addChild(comboTxt);
var dangerTxt = new Text2('DANGER LEVEL: LOW', {
size: 50,
fill: 0xff6666
});
dangerTxt.anchor.set(0.5, 0);
dangerTxt.y = 140;
LK.gui.top.addChild(dangerTxt);
gameUIContainer.addChild(dangerTxt);
var moneyTxt = new Text2('Money: $0', {
size: 55,
fill: 0xFFD700
});
moneyTxt.anchor.set(0, 0);
moneyTxt.x = 20;
moneyTxt.y = 260;
LK.gui.topLeft.addChild(moneyTxt);
// Initially hide game UI
gameUIContainer.visible = false;
gameUIContainer.alpha = 0;
function spawnBubble() {
if (gameState !== 'playing') return;
var isBonus = Math.random() < 0.02; // 2% chance for bonus bubble (259 points)
var isGolden = !isBonus && Math.random() < 0.1; // 10% chance for golden bubble (only if not bonus)
var bubble = new Bubble(isGolden, isBonus);
// Random spawn position across screen width
bubble.x = Math.random() * (2048 - 200) + 100;
bubble.y = 2732 + 60; // Start just below screen
bubbles.push(bubble);
game.addChild(bubble);
}
function spawnEnemy() {
if (gameState !== 'playing') return;
var enemy = new Enemy();
// Random spawn position across screen width
enemy.x = Math.random() * (2048 - 200) + 100;
enemy.y = -120; // Start above screen
enemies.push(enemy);
game.addChild(enemy);
}
function spawnObstacle() {
if (gameState !== 'playing') return;
var obstacle = new Obstacle();
// Random spawn position across screen width
obstacle.x = Math.random() * (2048 - 400) + 200;
obstacle.y = -80; // Start above screen
obstacles.push(obstacle);
game.addChild(obstacle);
}
function spawnCoin() {
if (gameState !== 'playing') return;
var coin = new Coin();
// Random spawn position across screen width
coin.x = Math.random() * (2048 - 200) + 100;
coin.y = -60; // Start above screen
coins.push(coin);
game.addChild(coin);
}
function updateDifficulty() {
difficultyTimer++;
// Increase difficulty every 7 seconds (420 ticks) - faster progression
if (difficultyTimer % 420 === 0) {
// Decrease spawn interval (spawn faster)
spawnInterval = Math.max(25, spawnInterval - 8);
// Increase bubble speed more dramatically
for (var i = 0; i < bubbles.length; i++) {
bubbles[i].speed += 0.4;
}
}
// Every 30 seconds, spawn a burst of golden bubbles for excitement
if (difficultyTimer % 1800 === 0) {
for (var j = 0; j < 3; j++) {
LK.setTimeout(function () {
var goldenBubble = new Bubble(true);
goldenBubble.x = Math.random() * (2048 - 200) + 100;
goldenBubble.y = 2732 + 60;
goldenBubble.speed *= 0.7; // Slower so players can catch them
bubbles.push(goldenBubble);
game.addChild(goldenBubble);
}, j * 300);
}
}
// Every 45 seconds, spawn bonus bubble rain for mega excitement
if (difficultyTimer % 2700 === 0) {
for (var k = 0; k < 5; k++) {
LK.setTimeout(function () {
var bonusBubble = new Bubble(false, true);
bonusBubble.x = Math.random() * (2048 - 200) + 100;
bonusBubble.y = 2732 + 60;
bonusBubble.speed *= 0.5; // Much slower so players can definitely catch them
bubbles.push(bonusBubble);
game.addChild(bonusBubble);
}, k * 200);
}
}
}
game.update = function () {
// Only run game logic when playing
if (gameState !== 'playing') {
// Update best score display in menu
if (gameState === 'menu') {
bestScoreMenuTxt.setText('Best Score: ' + storage.highScore);
}
return;
}
updateDifficulty();
// Combo timer countdown
if (comboTimer > 0) {
comboTimer--;
if (comboTimer === 0) {
comboCount = 0; // Reset combo when timer expires
}
}
// Spawn enemies
enemySpawnTimer++;
if (enemySpawnTimer >= enemySpawnInterval) {
spawnEnemy();
enemySpawnTimer = 0;
// Increase danger level over time
var dangerLevel = Math.floor(difficultyTimer / 600); // Every 10 seconds
var dangerText = dangerLevel < 3 ? 'LOW' : dangerLevel < 6 ? 'MEDIUM' : dangerLevel < 10 ? 'HIGH' : 'EXTREME';
dangerTxt.setText('DANGER LEVEL: ' + dangerText);
if (dangerLevel >= 3) {
dangerTxt.tint = 0xff4444;
}
if (dangerLevel >= 6) {
dangerTxt.tint = 0xff0000;
}
}
// Spawn obstacles
obstacleSpawnTimer++;
if (obstacleSpawnTimer >= obstacleSpawnInterval) {
spawnObstacle();
obstacleSpawnTimer = 0;
}
// Spawn coins
coinSpawnTimer++;
if (coinSpawnTimer >= coinSpawnInterval) {
spawnCoin();
coinSpawnTimer = 0;
}
// Screen shake effect
if (screenShakeIntensity > 0) {
game.x = (Math.random() - 0.5) * screenShakeIntensity;
game.y = (Math.random() - 0.5) * screenShakeIntensity;
screenShakeIntensity *= 0.9; // Gradually reduce shake
if (screenShakeIntensity < 0.1) {
screenShakeIntensity = 0;
game.x = 0;
game.y = 0;
}
}
// Spawn bubbles faster
spawnTimer++;
if (spawnTimer >= spawnInterval) {
spawnBubble();
spawnTimer = 0;
}
// Check for bubbles that escaped
for (var i = bubbles.length - 1; i >= 0; i--) {
var bubble = bubbles[i];
// Check if bubble escaped (reached top of screen)
if (bubble.y < -60) {
bubblesEscaped++;
bubble.destroy();
bubbles.splice(i, 1);
// Reset combo when bubble escapes
comboCount = 0;
comboTimer = 0;
// Update escaped counter
escapedTxt.setText('Escaped: ' + bubblesEscaped + '/' + maxEscapedBubbles);
// Check game over condition
if (bubblesEscaped >= maxEscapedBubbles) {
// Update high score before game over
var currentScore = LK.getScore();
if (currentScore > storage.highScore) {
storage.highScore = currentScore;
highScoreTxt.setText('Best: ' + storage.highScore);
}
// Add score to leaderboard
updateLeaderboard(currentScore);
// Clean up game objects
for (var cleanBubble = bubbles.length - 1; cleanBubble >= 0; cleanBubble--) {
bubbles[cleanBubble].destroy();
}
bubbles = [];
for (var cleanEnemy = enemies.length - 1; cleanEnemy >= 0; cleanEnemy--) {
enemies[cleanEnemy].destroy();
}
enemies = [];
for (var cleanObstacle = obstacles.length - 1; cleanObstacle >= 0; cleanObstacle--) {
obstacles[cleanObstacle].destroy();
}
obstacles = [];
for (var cleanCoin = coins.length - 1; cleanCoin >= 0; cleanCoin--) {
coins[cleanCoin].destroy();
}
coins = [];
// Return to menu after short delay
LK.setTimeout(function () {
gameState = 'menu';
gameUIContainer.visible = false;
gameUIContainer.alpha = 0;
menuContainer.visible = true;
tween(menuContainer, {
alpha: 1
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
animateMenu();
}
});
}, 2000);
LK.showGameOver();
return;
}
}
}
// Check for enemies that fell off screen
for (var e = enemies.length - 1; e >= 0; e--) {
var enemy = enemies[e];
if (enemy.y > 2732 + 120) {
enemy.destroy();
enemies.splice(e, 1);
}
}
// Check for obstacles that fell off screen
for (var o = obstacles.length - 1; o >= 0; o--) {
var obstacle = obstacles[o];
if (obstacle.y > 2732 + 100) {
obstacle.destroy();
obstacles.splice(o, 1);
}
}
// Check for coins that fell off screen
for (var c = coins.length - 1; c >= 0; c--) {
var coin = coins[c];
if (coin.y > 2732 + 60) {
coin.destroy();
coins.splice(c, 1);
}
}
// Update score displays
scoreTxt.setText('Score: ' + LK.getScore());
currentScoreTxt.setText('Score: ' + LK.getScore());
// Dynamic background color based on score for atmosphere
var currentScore = LK.getScore();
var colorPhase = currentScore * 0.01 % (Math.PI * 2);
var bgRed = Math.floor(Math.sin(colorPhase) * 40 + 100);
var bgGreen = Math.floor(Math.sin(colorPhase + 2.094) * 40 + 150);
var bgBlue = Math.floor(Math.sin(colorPhase + 4.188) * 40 + 200);
var dynamicBgColor = bgRed << 16 | bgGreen << 8 | bgBlue;
game.setBackgroundColor(dynamicBgColor);
// Update high score display in real-time if beaten
var currentScore = LK.getScore();
if (currentScore > storage.highScore) {
storage.highScore = currentScore;
highScoreTxt.setText('Best: ' + storage.highScore);
highScoreTxt.tint = 0x00FF00; // Green highlight for new record
}
};