User prompt
Oyuna günceleme getir
User prompt
Please fix the bug: 'Error: Invalid value. Only literals or 1-level deep objects/arrays containing literals are allowed.' in or related to this line: 'storage.leaderboard = leaderboard;' Line Number: 713 ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
Make this game as beautiful as GTA 2 and add a table showing the scores of the players to the login screen. ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
speed up the scorpion a little bit and make its appearance more beautiful ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
This thing is easter egg in the game if you click on the heart bars 10 times, 31 thousandlet's have points
User prompt
Make the model of the scorpion yourself
User prompt
Let there be a scorpion, a slow scorpion, this scorpion will try to burst the balloon before us.Let there be a scorpion, a slow scorpion, this scorpion will try to burst the balloon before us. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
forget the backlight
User prompt
ışıkları yavaşlat ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Develop the game yourself and add more systems and make the visuals yourself ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
büyük start yazısı
User prompt
oyun başlamıyor start yazısı koy
User prompt
make start screen
User prompt
If 5 balloons appear on the screen, you lose and a sign that says your best score ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
the game is losing health by itself, fix it, slow it down
Code edit (1 edits merged)
Please save this source code
User prompt
Bubble Pop Symphony
Initial prompt
Design a simple game and make it unique
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ var BeatRing = Container.expand(function () { var self = Container.call(this); var ringGraphics = self.attachAsset('beatRing', { anchorX: 0.5, anchorY: 0.5 }); ringGraphics.alpha = 0.3; ringGraphics.scaleX = 0.1; ringGraphics.scaleY = 0.1; self.animate = function () { tween(ringGraphics, { scaleX: 1.5, scaleY: 1.5, alpha: 0 }, { duration: 1000, easing: tween.easeOut, onFinish: function onFinish() { self.destroy(); } }); }; return self; }); var Bubble = Container.expand(function (color, isGolden, specialType) { var self = Container.call(this); var bubbleAsset = 'bubble'; if (isGolden) { bubbleAsset = 'goldenBubble'; } else if (specialType === 'explosive') { bubbleAsset = 'explosiveBubble'; } else if (specialType === 'multiplier') { bubbleAsset = 'multiplierBubble'; } else if (specialType === 'time') { bubbleAsset = 'timeBubble'; } var bubbleGraphics = self.attachAsset(bubbleAsset, { anchorX: 0.5, anchorY: 0.5 }); if (!isGolden && !specialType) { bubbleGraphics.tint = color; } self.isGolden = isGolden || false; self.specialType = specialType || null; self.color = color; self.noteIndex = Math.floor(Math.random() * 4); self.spawned = false; self.pulseTween = null; self.lastPulseTime = 0; self.floatTween = null; // Add floating motion self.startFloat = function () { if (self.floatTween) { tween.stop(self, { y: true }); } var floatAmount = 20 + Math.random() * 30; var floatDuration = 2000 + Math.random() * 1000; self.floatTween = tween(self, { y: self.y - floatAmount }, { duration: floatDuration, easing: tween.easeInOut, onFinish: function onFinish() { tween(self, { y: self.y + floatAmount }, { duration: floatDuration, easing: tween.easeInOut, onFinish: function onFinish() { if (!self.destroyed) { self.startFloat(); } } }); } }); }; // Enhanced pulsing effect with color changes self.startPulse = function () { self.lastPulseTime = LK.ticks; if (self.pulseTween) { tween.stop(bubbleGraphics, { scaleX: true, scaleY: true }); } // Add glow effect on pulse var originalTint = bubbleGraphics.tint; tween(bubbleGraphics, { tint: 0xFFFFFF }, { duration: 150, onFinish: function onFinish() { tween(bubbleGraphics, { tint: originalTint }, { duration: 150 }); } }); self.pulseTween = tween(bubbleGraphics, { scaleX: 1.3, scaleY: 1.3 }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { tween(bubbleGraphics, { scaleX: 1.0, scaleY: 1.0 }, { duration: 400, easing: tween.bounceOut }); } }); }; self.pop = function () { var popSounds = ['pop1', 'pop2', 'pop3', 'pop4']; var soundToPlay = 'miss'; // Default sound if (self.isGolden) { soundToPlay = 'miss'; // Use available sound } else if (self.specialType === 'explosive') { soundToPlay = 'explosion'; } else if (self.specialType === 'multiplier') { soundToPlay = 'multiplier'; } else if (self.specialType === 'time') { soundToPlay = 'powerup'; } else { soundToPlay = 'miss'; } LK.getSound(soundToPlay).play(); // Special effects based on bubble type if (self.specialType === 'explosive') { // Create massive explosion effect createExplosiveEffect(self.x, self.y); } else if (self.specialType === 'multiplier') { // Create sparkle multiplier effect createMultiplierEffect(self.x, self.y); } else if (self.specialType === 'time') { // Create time freeze effect createTimeEffect(self.x, self.y); } // Create particle explosion createParticleExplosion(self.x, self.y, self.color, self.isGolden); // Enhanced pop animation tween(bubbleGraphics, { scaleX: 2.0, scaleY: 2.0, alpha: 0, rotation: Math.PI * 2 }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { self.destroy(); } }); }; self.down = function (x, y, obj) { var currentTime = LK.ticks; var beatInterval = 60; var beatPosition = currentTime % beatInterval; var timingWindow = 12; // Slightly larger timing window var isPerfectTiming = beatPosition <= timingWindow || beatPosition >= beatInterval - timingWindow; // Visual feedback on touch tween(bubbleGraphics, { scaleX: 0.8, scaleY: 0.8 }, { duration: 100, easing: tween.easeOut, onFinish: function onFinish() { tween(bubbleGraphics, { scaleX: 1.0, scaleY: 1.0 }, { duration: 100, easing: tween.easeOut }); } }); // Handle special bubble effects if (self.specialType === 'explosive') { // Explosive bubbles destroy nearby bubbles handleExplosiveBubble(self.x, self.y); } else if (self.specialType === 'multiplier') { // Multiplier bubbles double next few scores activateScoreMultiplier(); } else if (self.specialType === 'time') { // Time bubbles slow down game temporarily activateTimeSlowdown(); } if (isPerfectTiming) { onBubblePopped(self, true); } else { onBubblePopped(self, false); } }; return self; }); var Particle = Container.expand(function (color, size) { var self = Container.call(this); var particleGraphics = self.attachAsset('particle', { anchorX: 0.5, anchorY: 0.5 }); particleGraphics.tint = color || 0xFFFFFF; particleGraphics.scaleX = size || 1; particleGraphics.scaleY = size || 1; self.velocity = { x: 0, y: 0 }; self.gravity = 0.2; self.life = 60; // 1 second at 60fps self.update = function () { self.x += self.velocity.x; self.y += self.velocity.y; self.velocity.y += self.gravity; self.life--; // Fade out over time particleGraphics.alpha = self.life / 60; if (self.life <= 0) { self.destroy(); } }; return self; }); var PerfectIndicator = Container.expand(function () { var self = Container.call(this); var indicator = self.attachAsset('perfectIndicator', { anchorX: 0.5, anchorY: 0.5, alpha: 0 }); self.show = function (x, y) { self.x = x; self.y = y; indicator.alpha = 1; indicator.scaleX = 0.5; indicator.scaleY = 0.5; tween(indicator, { scaleX: 1, scaleY: 1, alpha: 0 }, { duration: 500, easing: tween.easeOut, onFinish: function onFinish() { self.destroy(); } }); }; return self; }); var Scorpion = Container.expand(function () { var self = Container.call(this); // Create scorpion body var scorpionBody = self.attachAsset('scorpionBody', { anchorX: 0.5, anchorY: 0.5 }); // Create left claw var leftClaw = self.attachAsset('scorpionClaw', { anchorX: 1.0, anchorY: 0.5 }); leftClaw.x = -25; leftClaw.y = -10; leftClaw.rotation = -0.3; // Create right claw var rightClaw = self.attachAsset('scorpionClaw', { anchorX: 1.0, anchorY: 0.5 }); rightClaw.x = -25; rightClaw.y = 10; rightClaw.rotation = 0.3; // Create tail segments var tailSegments = []; for (var i = 0; i < 4; i++) { var segment = self.attachAsset('scorpionTail', { anchorX: 0.0, anchorY: 0.5 }); segment.x = 40 + i * 12; segment.y = 0; segment.rotation = i * 0.1; segment.scaleX = 0.8 - i * 0.1; tailSegments.push(segment); } // Create stinger at the end var stinger = self.attachAsset('scorpionStinger', { anchorX: 0.5, anchorY: 0.5 }); stinger.x = 88; stinger.y = -5; // Store references for animations self.body = scorpionBody; self.leftClaw = leftClaw; self.rightClaw = rightClaw; self.tailSegments = tailSegments; self.stinger = stinger; self.animationTimer = 0; self.speed = 1.5; // Moderate speed movement self.targetBubble = null; self.lastTargetTime = 0; // Find nearest bubble to target self.findTarget = function () { var nearestBubble = null; var nearestDistance = Infinity; for (var i = 0; i < bubbles.length; i++) { var bubble = bubbles[i]; var distance = Math.sqrt(Math.pow(self.x - bubble.x, 2) + Math.pow(self.y - bubble.y, 2)); if (distance < nearestDistance) { nearestDistance = distance; nearestBubble = bubble; } } return nearestBubble; }; self.update = function () { self.animationTimer++; // Animate tail swishing for (var i = 0; i < self.tailSegments.length; i++) { var segment = self.tailSegments[i]; segment.rotation = Math.sin(self.animationTimer * 0.1 + i * 0.5) * 0.2 + i * 0.1; } // Animate stinger bobbing with pulsing glow self.stinger.y = -5 + Math.sin(self.animationTimer * 0.15) * 3; // Add dangerous pulsing glow to stinger var stingerPulse = Math.sin(self.animationTimer * 0.2) * 0.5 + 0.5; var stingerGlow = 0x440000 + Math.floor(stingerPulse * 0xbb0000); self.stinger.tint = stingerGlow; self.stinger.scaleX = 1 + stingerPulse * 0.3; self.stinger.scaleY = 1 + stingerPulse * 0.3; // Animate claws opening and closing with shimmer effect var clawAnimation = Math.sin(self.animationTimer * 0.08) * 0.2; self.leftClaw.rotation = -0.3 + clawAnimation; self.rightClaw.rotation = 0.3 - clawAnimation; // Add shimmer effect to claws var shimmer = Math.sin(self.animationTimer * 0.15) * 0.3 + 0.7; self.leftClaw.alpha = shimmer; self.rightClaw.alpha = shimmer; var clawGlow = 0x444444 + Math.floor(shimmer * 0x888888); self.leftClaw.tint = clawGlow; self.rightClaw.tint = clawGlow; // Body bobbing while walking self.body.y = Math.sin(self.animationTimer * 0.12) * 2; // Beautiful rainbow color shifting effect var hue = self.animationTimer * 2 % 360; var rainbowColor = 0x8b4513; // Keep brown as base if (hue < 60) { rainbowColor = 0xff4500 + Math.floor(hue / 60 * 0x4000); } else if (hue < 120) { rainbowColor = 0xff8500 + Math.floor((hue - 60) / 60 * 0x4000); } else if (hue < 180) { rainbowColor = 0xffc500 + Math.floor((hue - 120) / 60 * 0x2000); } else if (hue < 240) { rainbowColor = 0xffe500 - Math.floor((hue - 180) / 60 * 0x6000); } else if (hue < 300) { rainbowColor = 0x9fe500 - Math.floor((hue - 240) / 60 * 0x4000); } else { rainbowColor = 0x5fe500 + Math.floor((hue - 300) / 60 * 0x6000); } self.body.tint = rainbowColor; // Find new target every 2 seconds or if current target is destroyed if (LK.ticks - self.lastTargetTime > 120 || !self.targetBubble || self.targetBubble.destroyed) { self.targetBubble = self.findTarget(); self.lastTargetTime = LK.ticks; } // Move towards target bubble if (self.targetBubble && !self.targetBubble.destroyed) { var dx = self.targetBubble.x - self.x; var dy = self.targetBubble.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 5) { // Move towards bubble self.x += dx / distance * self.speed; self.y += dy / distance * self.speed; // Rotate entire scorpion towards target self.rotation = Math.atan2(dy, dx); // Faster tail animation when moving for (var j = 0; j < self.tailSegments.length; j++) { var seg = self.tailSegments[j]; seg.rotation = Math.sin(self.animationTimer * 0.2 + j * 0.5) * 0.3 + j * 0.15; } } else { // Close enough to pop the bubble if (self.targetBubble && !self.targetBubble.destroyed) { // Attack animation - claws snap self.leftClaw.rotation = -0.8; self.rightClaw.rotation = 0.8; // Scorpion pops the bubble (counts as missed for player) for (var i = 0; i < bubbles.length; i++) { if (bubbles[i] === self.targetBubble) { bubbles.splice(i, 1); break; } } self.targetBubble.pop(); missedBeat(); // Player loses health self.targetBubble = null; } } } // Keep scorpion within screen bounds if (self.x < 50) self.x = 50; if (self.x > 1998) self.x = 1998; if (self.y < 50) self.y = 50; if (self.y > 2682) self.y = 2682; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x1a1a2e }); /**** * Game Code ****/ var gameState = 'start'; // 'start' or 'playing' var bubbles = []; var particles = []; var beatRings = []; var score = 0; var combo = 0; var health = 5; var gameSpeed = 1; var spawnTimer = 0; var beatTimer = 0; var perfectIndicators = []; var bubbleColors = [0x4A90E2, 0xFF6B6B, 0x4ECDC4, 0xFFE66D, 0x95E1D3, 0xFF69B4, 0x32CD32, 0xFFA500]; var bestScore = storage.bestScore || 0; var scorpion = null; var heartClickCount = 0; // Track clicks on heart bars for easter egg // New power-up variables var scoreMultiplier = 1; var multiplierTimer = 0; var timeSlowActive = false; var timeSlowTimer = 0; var specialEffects = []; // GTA 2 style leaderboard data - flattened for storage compatibility var leaderboardNames = storage.leaderboardNames || []; var leaderboardScores = storage.leaderboardScores || []; var playerName = storage.playerName || 'Player'; var showLeaderboard = false; // Build leaderboard from flattened arrays var leaderboard = []; for (var idx = 0; idx < leaderboardNames.length; idx++) { leaderboard.push({ name: leaderboardNames[idx], score: leaderboardScores[idx] }); } // Particle explosion function function createParticleExplosion(x, y, color, isGolden) { var particleCount = isGolden ? 15 : 8; var particleColors = isGolden ? [0xFFD700, 0xFFA500, 0xFFFF00] : [color, 0xFFFFFF]; for (var i = 0; i < particleCount; i++) { var particle = new Particle(particleColors[Math.floor(Math.random() * particleColors.length)], 0.5 + Math.random() * 0.5); particle.x = x + (Math.random() - 0.5) * 40; particle.y = y + (Math.random() - 0.5) * 40; particle.velocity.x = (Math.random() - 0.5) * 8; particle.velocity.y = (Math.random() - 0.5) * 8 - 2; particles.push(particle); game.addChild(particle); } } // Create explosive effect for explosive bubbles function createExplosiveEffect(x, y) { // Create shockwave var shockwave = LK.getAsset('beatRing', { anchorX: 0.5, anchorY: 0.5, alpha: 0.8, scaleX: 0.1, scaleY: 0.1 }); shockwave.x = x; shockwave.y = y; shockwave.tint = 0xFF4444; game.addChild(shockwave); tween(shockwave, { scaleX: 4, scaleY: 4, alpha: 0 }, { duration: 600, easing: tween.easeOut, onFinish: function onFinish() { shockwave.destroy(); } }); // Create extra particles for (var i = 0; i < 25; i++) { var particle = new Particle(0xFF4444, 1 + Math.random()); particle.x = x + (Math.random() - 0.5) * 100; particle.y = y + (Math.random() - 0.5) * 100; particle.velocity.x = (Math.random() - 0.5) * 15; particle.velocity.y = (Math.random() - 0.5) * 15 - 5; particles.push(particle); game.addChild(particle); } } // Create multiplier effect function createMultiplierEffect(x, y) { var multiplierText = new Text2('x2 SCORE!', { size: 80, fill: 0x9932CC }); multiplierText.anchor.set(0.5, 0.5); multiplierText.x = x; multiplierText.y = y; multiplierText.alpha = 0; game.addChild(multiplierText); tween(multiplierText, { alpha: 1, y: y - 100, scaleX: 1.5, scaleY: 1.5 }, { duration: 800, easing: tween.easeOut, onFinish: function onFinish() { tween(multiplierText, { alpha: 0 }, { duration: 400, onFinish: function onFinish() { multiplierText.destroy(); } }); } }); } // Create time effect function createTimeEffect(x, y) { var timeText = new Text2('TIME SLOW!', { size: 70, fill: 0x00FF7F }); timeText.anchor.set(0.5, 0.5); timeText.x = x; timeText.y = y; timeText.alpha = 0; game.addChild(timeText); tween(timeText, { alpha: 1, y: y - 80, scaleX: 1.3, scaleY: 1.3 }, { duration: 600, easing: tween.easeOut, onFinish: function onFinish() { tween(timeText, { alpha: 0 }, { duration: 300, onFinish: function onFinish() { timeText.destroy(); } }); } }); } // Handle explosive bubble chain reaction function handleExplosiveBubble(x, y) { var explosionRadius = 200; for (var i = bubbles.length - 1; i >= 0; i--) { var bubble = bubbles[i]; var distance = Math.sqrt(Math.pow(x - bubble.x, 2) + Math.pow(y - bubble.y, 2)); if (distance <= explosionRadius) { onBubblePopped(bubble, true); // Count as perfect hits } } } // Activate score multiplier power-up function activateScoreMultiplier() { scoreMultiplier = 2; multiplierTimer = 300; // 5 seconds at 60fps LK.effects.flashScreen(0x9932CC, 300); } // Activate time slowdown power-up function activateTimeSlowdown() { timeSlowActive = true; timeSlowTimer = 480; // 8 seconds at 60fps LK.effects.flashScreen(0x00FF7F, 400); } // Create beat ring effect function createBeatRing() { var ring = new BeatRing(); ring.x = 2048 / 2; ring.y = 2732 / 2; beatRings.push(ring); game.addChild(ring); ring.animate(); } // GTA 2 style city background function createCityBackground() { // Create grid pattern like GTA 2 city streets for (var x = 0; x < 2048; x += 256) { for (var y = 0; y < 2732; y += 256) { // Street lines var streetH = LK.getAsset('particle', { width: 256, height: 4, anchorX: 0, anchorY: 0, alpha: 0.2 }); streetH.x = x; streetH.y = y; streetH.tint = 0x666666; game.addChild(streetH); var streetV = LK.getAsset('particle', { width: 4, height: 256, anchorX: 0, anchorY: 0, alpha: 0.2 }); streetV.x = x; streetV.y = y; streetV.tint = 0x666666; game.addChild(streetV); } } // Add some building-like rectangles for (var i = 0; i < 20; i++) { var building = LK.getAsset('particle', { width: 60 + Math.random() * 100, height: 60 + Math.random() * 100, anchorX: 0, anchorY: 0, alpha: 0.1 }); building.x = Math.random() * 1900; building.y = Math.random() * 2600; building.tint = 0x444444; game.addChild(building); } } // Enhanced background effects function updateBackgroundEffects() { // Add subtle city ambiance effects if (LK.ticks % 180 === 0) { // Create occasional street lamp glow var glow = LK.getAsset('particle', { width: 20, height: 20, anchorX: 0.5, anchorY: 0.5, alpha: 0.5 }); glow.x = Math.random() * 2048; glow.y = Math.random() * 2732; glow.tint = 0xFFFF88; game.addChild(glow); tween(glow, { alpha: 0, scaleX: 3, scaleY: 3 }, { duration: 3000, easing: tween.easeOut, onFinish: function onFinish() { glow.destroy(); } }); } } // GTA 2 style start screen UI var titleText = new Text2('BUBBLE CITY', { size: 160, fill: 0xFFFF00 }); titleText.anchor.set(0.5, 0.5); titleText.y = -300; LK.gui.center.addChild(titleText); var subtitleText = new Text2('CRIME BEATS EDITION', { size: 60, fill: 0x00FF00 }); subtitleText.anchor.set(0.5, 0.5); subtitleText.y = -200; LK.gui.center.addChild(subtitleText); var instructionsText = new Text2('Pop bubbles in rhythm with the beats!\nTap to start your criminal career', { size: 50, fill: 0xFFFFFF }); instructionsText.anchor.set(0.5, 0.5); instructionsText.y = 100; LK.gui.center.addChild(instructionsText); // GTA 2 style leaderboard display var leaderboardTitle = new Text2('TOP CRIMINALS', { size: 80, fill: 0xFF4444 }); leaderboardTitle.anchor.set(0.5, 0.5); leaderboardTitle.y = 200; LK.gui.center.addChild(leaderboardTitle); var leaderboardText = new Text2('', { size: 50, fill: 0x00FFFF }); leaderboardText.anchor.set(0.5, 0); leaderboardText.y = 280; LK.gui.center.addChild(leaderboardText); var startBestScoreText = new Text2('Your Best: ' + bestScore, { size: 60, fill: 0x4ECDC4 }); startBestScoreText.anchor.set(0.5, 0.5); startBestScoreText.y = 550; LK.gui.center.addChild(startBestScoreText); // Function to update leaderboard display function updateLeaderboardDisplay() { var displayText = ''; for (var i = 0; i < Math.min(5, leaderboard.length); i++) { var entry = leaderboard[i]; displayText += i + 1 + '. ' + entry.name + ' - ' + entry.score + '\n'; } if (leaderboard.length === 0) { displayText = 'No scores yet!\nBe the first criminal!'; } leaderboardText.setText(displayText); } // Game UI (initially hidden) var scoreText = new Text2('Score: 0', { size: 80, fill: 0xFFFFFF }); scoreText.anchor.set(0.5, 0); scoreText.visible = false; LK.gui.top.addChild(scoreText); var comboText = new Text2('Combo: 0', { size: 60, fill: 0xFFD700 }); comboText.anchor.set(0, 0); comboText.x = 50; comboText.y = 120; comboText.visible = false; LK.gui.top.addChild(comboText); var healthText = new Text2('♥♥♥♥♥', { size: 70, fill: 0xFF4444 }); healthText.anchor.set(1, 0); healthText.visible = false; // Add click handler for easter egg healthText.down = function (x, y, obj) { if (gameState === 'playing') { heartClickCount++; // Visual feedback for click tween(healthText, { scaleX: 1.2, scaleY: 1.2 }, { duration: 100, easing: tween.easeOut, onFinish: function onFinish() { tween(healthText, { scaleX: 1.0, scaleY: 1.0 }, { duration: 100, easing: tween.easeOut }); } }); // Easter egg trigger at 10 clicks if (heartClickCount >= 10) { heartClickCount = 0; // Reset counter score += 31000; // Award 31,000 points // Create spectacular visual effect LK.effects.flashScreen(0xFFD700, 1000); // Create multiple particle explosions for (var i = 0; i < 5; i++) { var explosionX = Math.random() * 2048; var explosionY = Math.random() * 2732; createParticleExplosion(explosionX, explosionY, 0xFFD700, true); } // Show special text indicator var easterEggText = new Text2('EASTER EGG!\n+31,000 POINTS!', { size: 120, fill: 0xFFD700 }); easterEggText.anchor.set(0.5, 0.5); easterEggText.x = 1024; easterEggText.y = 1366; easterEggText.alpha = 0; easterEggText.scaleX = 0; easterEggText.scaleY = 0; game.addChild(easterEggText); // Animate the easter egg text tween(easterEggText, { alpha: 1, scaleX: 1.5, scaleY: 1.5 }, { duration: 500, easing: tween.bounceOut, onFinish: function onFinish() { tween(easterEggText, { alpha: 0, scaleX: 0.5, scaleY: 0.5, y: easterEggText.y - 200 }, { duration: 1000, easing: tween.easeOut, onFinish: function onFinish() { easterEggText.destroy(); } }); } }); updateUI(); } } }; LK.gui.topRight.addChild(healthText); var bestScoreText = new Text2('Best: ' + bestScore, { size: 50, fill: 0xFFFFFF }); bestScoreText.anchor.set(0, 0); bestScoreText.x = 50; bestScoreText.y = 200; bestScoreText.visible = false; LK.gui.top.addChild(bestScoreText); // Add score to leaderboard function addToLeaderboard(playerScore) { var newEntry = { name: playerName, score: playerScore }; leaderboard.push(newEntry); // Sort by score descending leaderboard.sort(function (a, b) { return b.score - a.score; }); // Keep only top 10 leaderboard = leaderboard.slice(0, 10); // Save to storage using flattened arrays leaderboardNames = []; leaderboardScores = []; for (var i = 0; i < leaderboard.length; i++) { leaderboardNames.push(leaderboard[i].name); leaderboardScores.push(leaderboard[i].score); } storage.leaderboardNames = leaderboardNames; storage.leaderboardScores = leaderboardScores; } function startGame() { gameState = 'playing'; // Create GTA 2 style city background createCityBackground(); // Hide start screen UI titleText.visible = false; subtitleText.visible = false; instructionsText.visible = false; startBestScoreText.visible = false; leaderboardTitle.visible = false; leaderboardText.visible = false; // Show game UI scoreText.visible = true; comboText.visible = true; healthText.visible = true; bestScoreText.visible = true; // Spawn scorpion scorpion = new Scorpion(); scorpion.x = Math.random() * 1800 + 124; scorpion.y = Math.random() * 2400 + 166; game.addChild(scorpion); } // Touch handler for starting game game.down = function (x, y, obj) { if (gameState === 'start') { startGame(); } }; function updateUI() { var displayScore = 'Score: ' + score; if (scoreMultiplier > 1) { displayScore += ' (x' + scoreMultiplier + ')'; } scoreText.setText(displayScore); var displayCombo = 'Combo: ' + combo; if (timeSlowActive) { displayCombo += ' [TIME SLOW]'; } comboText.setText(displayCombo); var hearts = ''; for (var i = 0; i < health; i++) { hearts += '♥'; } healthText.setText(hearts); // Update best score if current score is higher if (score > bestScore) { bestScore = score; storage.bestScore = bestScore; bestScoreText.setText('Best: ' + bestScore); } } function spawnBubble() { var isGolden = Math.random() < 0.15; // 15% chance for golden bubble var specialType = null; // 10% chance for special bubbles (only if not golden) if (!isGolden && Math.random() < 0.1) { var specialTypes = ['explosive', 'multiplier', 'time']; specialType = specialTypes[Math.floor(Math.random() * specialTypes.length)]; } var color = bubbleColors[Math.floor(Math.random() * bubbleColors.length)]; var bubble = new Bubble(color, isGolden, specialType); // Better spawn positioning with grid-like distribution var attempts = 0; var maxAttempts = 20; do { bubble.x = Math.random() * (2048 - 300) + 150; bubble.y = Math.random() * (2732 - 600) + 300; attempts++; } while (isTooCloseToOthers(bubble) && attempts < maxAttempts); if (attempts < maxAttempts) { bubbles.push(bubble); game.addChild(bubble); bubble.spawned = true; bubble.lastPulseTime = LK.ticks; // Enhanced spawn animation with rotation and bounce bubble.alpha = 0; bubble.scaleX = 0; bubble.scaleY = 0; bubble.rotation = Math.PI * 2; bubble.y -= 100; // Start above final position tween(bubble, { alpha: 1, scaleX: 1.2, scaleY: 1.2, rotation: 0, y: bubble.y + 100 }, { duration: 500, easing: tween.bounceOut, onFinish: function onFinish() { tween(bubble, { scaleX: 1, scaleY: 1 }, { duration: 200, easing: tween.easeOut }); bubble.startFloat(); // Start floating animation } }); // Add sparkle effect for golden bubbles if (isGolden) { createSparkleEffect(bubble); } } else { bubble.destroy(); } } function isTooCloseToOthers(bubble) { for (var i = 0; i < bubbles.length; i++) { var distance = Math.sqrt(Math.pow(bubble.x - bubbles[i].x, 2) + Math.pow(bubble.y - bubbles[i].y, 2)); if (distance < 200) { return true; } } return false; } function createSparkleEffect(bubble) { var sparkleTimer = LK.setInterval(function () { if (bubble.destroyed) { LK.clearInterval(sparkleTimer); return; } var sparkle = new Particle(0xFFD700, 0.3); sparkle.x = bubble.x + (Math.random() - 0.5) * 100; sparkle.y = bubble.y + (Math.random() - 0.5) * 100; sparkle.velocity.x = (Math.random() - 0.5) * 1; sparkle.velocity.y = (Math.random() - 0.5) * 1; sparkle.gravity = -0.05; // Float upward more slowly particles.push(sparkle); game.addChild(sparkle); }, 400); } function onBubblePopped(bubble, isPerfect) { var basePoints = bubble.isGolden ? 100 : 10; // Special bubble bonuses if (bubble.specialType === 'explosive') { basePoints = 50; } else if (bubble.specialType === 'multiplier') { basePoints = 25; } else if (bubble.specialType === 'time') { basePoints = 30; } var points = isPerfect ? basePoints * 2 : basePoints; if (isPerfect) { combo++; points *= 1 + combo * 0.1; // Show perfect indicator var indicator = new PerfectIndicator(); perfectIndicators.push(indicator); game.addChild(indicator); indicator.show(bubble.x, bubble.y); if (bubble.isGolden) { LK.effects.flashScreen(0xFFD700, 300); } } else { combo = 0; } // Apply score multiplier if active points *= scoreMultiplier; score += Math.floor(points); // Remove bubble from array for (var i = 0; i < bubbles.length; i++) { if (bubbles[i] === bubble) { bubbles.splice(i, 1); break; } } bubble.pop(); updateUI(); } function missedBeat() { combo = 0; health--; LK.effects.flashScreen(0xFF0000, 200); LK.getSound('miss').play(); if (health <= 0) { // Add score to leaderboard before game over addToLeaderboard(score); LK.showGameOver(); } updateUI(); } // Start background music LK.playMusic('bgMusic'); game.update = function () { if (gameState === 'start') { // Update best score display on start screen startBestScoreText.setText('Your Best: ' + bestScore); // Update leaderboard display updateLeaderboardDisplay(); // GTA 2 style animations titleText.rotation = Math.sin(LK.ticks * 0.01) * 0.1; subtitleText.alpha = 0.7 + Math.sin(LK.ticks * 0.05) * 0.3; leaderboardTitle.scaleX = 1 + Math.sin(LK.ticks * 0.03) * 0.1; leaderboardTitle.scaleY = 1 + Math.sin(LK.ticks * 0.03) * 0.1; return; } beatTimer++; spawnTimer++; updateBackgroundEffects(); // Update power-up timers if (multiplierTimer > 0) { multiplierTimer--; if (multiplierTimer <= 0) { scoreMultiplier = 1; } } if (timeSlowTimer > 0) { timeSlowTimer--; if (timeSlowTimer <= 0) { timeSlowActive = false; } } // Apply time slow effect to spawn rate var timeMultiplier = timeSlowActive ? 1.5 : 1; // Enhanced beat visualization if (beatTimer % 60 === 0) { // Every second - create beat ring and pulse bubbles createBeatRing(); for (var i = 0; i < bubbles.length; i++) { bubbles[i].startPulse(); } } // Update particles for (var p = particles.length - 1; p >= 0; p--) { var particle = particles[p]; if (particle.destroyed) { particles.splice(p, 1); } } // Clean up beat rings for (var r = beatRings.length - 1; r >= 0; r--) { if (beatRings[r].destroyed) { beatRings.splice(r, 1); } } // Check if 6 bubbles are on screen - game over condition (increased from 5) if (bubbles.length >= 6) { // Update best score before game over if (score > bestScore) { bestScore = score; storage.bestScore = bestScore; } // Add score to leaderboard addToLeaderboard(score); // Screen shake effect before game over LK.effects.flashScreen(0xFF0000, 500); LK.showGameOver(); } // Dynamic spawn rate based on score and combo var baseSpawnRate = Math.max(150 - Math.floor(score / 50) * 5, 80); var comboBonus = Math.min(combo * 5, 30); var spawnRate = (baseSpawnRate - comboBonus) * timeMultiplier; if (spawnTimer >= spawnRate && bubbles.length < 10) { spawnBubble(); spawnTimer = 0; } // Remove bubbles that have been on screen too long for (var i = bubbles.length - 1; i >= 0; i--) { var bubble = bubbles[i]; // Slightly longer lifetime for better gameplay if (bubble.spawned && LK.ticks - bubble.lastPulseTime > 1500) { // Warning effect before removal tween(bubble, { alpha: 0.3, scaleX: 0.8, scaleY: 0.8 }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { if (!bubble.destroyed) { bubble.destroy(); bubbles.splice(i, 1); missedBeat(); } } }); } } // Clean up perfect indicators for (var j = perfectIndicators.length - 1; j >= 0; j--) { if (perfectIndicators[j].destroyed) { perfectIndicators.splice(j, 1); } } // Update scorpion if (scorpion && !scorpion.destroyed) { // Scorpion updates automatically via its update method } // Increase difficulty gameSpeed = 1 + score / 1000; // Win condition if (score >= 3000) { // Add score to leaderboard before showing win addToLeaderboard(score); LK.showYouWin(); } }; // Initialize UI updateUI();
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var BeatRing = Container.expand(function () {
var self = Container.call(this);
var ringGraphics = self.attachAsset('beatRing', {
anchorX: 0.5,
anchorY: 0.5
});
ringGraphics.alpha = 0.3;
ringGraphics.scaleX = 0.1;
ringGraphics.scaleY = 0.1;
self.animate = function () {
tween(ringGraphics, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
self.destroy();
}
});
};
return self;
});
var Bubble = Container.expand(function (color, isGolden, specialType) {
var self = Container.call(this);
var bubbleAsset = 'bubble';
if (isGolden) {
bubbleAsset = 'goldenBubble';
} else if (specialType === 'explosive') {
bubbleAsset = 'explosiveBubble';
} else if (specialType === 'multiplier') {
bubbleAsset = 'multiplierBubble';
} else if (specialType === 'time') {
bubbleAsset = 'timeBubble';
}
var bubbleGraphics = self.attachAsset(bubbleAsset, {
anchorX: 0.5,
anchorY: 0.5
});
if (!isGolden && !specialType) {
bubbleGraphics.tint = color;
}
self.isGolden = isGolden || false;
self.specialType = specialType || null;
self.color = color;
self.noteIndex = Math.floor(Math.random() * 4);
self.spawned = false;
self.pulseTween = null;
self.lastPulseTime = 0;
self.floatTween = null;
// Add floating motion
self.startFloat = function () {
if (self.floatTween) {
tween.stop(self, {
y: true
});
}
var floatAmount = 20 + Math.random() * 30;
var floatDuration = 2000 + Math.random() * 1000;
self.floatTween = tween(self, {
y: self.y - floatAmount
}, {
duration: floatDuration,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(self, {
y: self.y + floatAmount
}, {
duration: floatDuration,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (!self.destroyed) {
self.startFloat();
}
}
});
}
});
};
// Enhanced pulsing effect with color changes
self.startPulse = function () {
self.lastPulseTime = LK.ticks;
if (self.pulseTween) {
tween.stop(bubbleGraphics, {
scaleX: true,
scaleY: true
});
}
// Add glow effect on pulse
var originalTint = bubbleGraphics.tint;
tween(bubbleGraphics, {
tint: 0xFFFFFF
}, {
duration: 150,
onFinish: function onFinish() {
tween(bubbleGraphics, {
tint: originalTint
}, {
duration: 150
});
}
});
self.pulseTween = tween(bubbleGraphics, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(bubbleGraphics, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 400,
easing: tween.bounceOut
});
}
});
};
self.pop = function () {
var popSounds = ['pop1', 'pop2', 'pop3', 'pop4'];
var soundToPlay = 'miss'; // Default sound
if (self.isGolden) {
soundToPlay = 'miss'; // Use available sound
} else if (self.specialType === 'explosive') {
soundToPlay = 'explosion';
} else if (self.specialType === 'multiplier') {
soundToPlay = 'multiplier';
} else if (self.specialType === 'time') {
soundToPlay = 'powerup';
} else {
soundToPlay = 'miss';
}
LK.getSound(soundToPlay).play();
// Special effects based on bubble type
if (self.specialType === 'explosive') {
// Create massive explosion effect
createExplosiveEffect(self.x, self.y);
} else if (self.specialType === 'multiplier') {
// Create sparkle multiplier effect
createMultiplierEffect(self.x, self.y);
} else if (self.specialType === 'time') {
// Create time freeze effect
createTimeEffect(self.x, self.y);
}
// Create particle explosion
createParticleExplosion(self.x, self.y, self.color, self.isGolden);
// Enhanced pop animation
tween(bubbleGraphics, {
scaleX: 2.0,
scaleY: 2.0,
alpha: 0,
rotation: Math.PI * 2
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
self.destroy();
}
});
};
self.down = function (x, y, obj) {
var currentTime = LK.ticks;
var beatInterval = 60;
var beatPosition = currentTime % beatInterval;
var timingWindow = 12; // Slightly larger timing window
var isPerfectTiming = beatPosition <= timingWindow || beatPosition >= beatInterval - timingWindow;
// Visual feedback on touch
tween(bubbleGraphics, {
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(bubbleGraphics, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 100,
easing: tween.easeOut
});
}
});
// Handle special bubble effects
if (self.specialType === 'explosive') {
// Explosive bubbles destroy nearby bubbles
handleExplosiveBubble(self.x, self.y);
} else if (self.specialType === 'multiplier') {
// Multiplier bubbles double next few scores
activateScoreMultiplier();
} else if (self.specialType === 'time') {
// Time bubbles slow down game temporarily
activateTimeSlowdown();
}
if (isPerfectTiming) {
onBubblePopped(self, true);
} else {
onBubblePopped(self, false);
}
};
return self;
});
var Particle = Container.expand(function (color, size) {
var self = Container.call(this);
var particleGraphics = self.attachAsset('particle', {
anchorX: 0.5,
anchorY: 0.5
});
particleGraphics.tint = color || 0xFFFFFF;
particleGraphics.scaleX = size || 1;
particleGraphics.scaleY = size || 1;
self.velocity = {
x: 0,
y: 0
};
self.gravity = 0.2;
self.life = 60; // 1 second at 60fps
self.update = function () {
self.x += self.velocity.x;
self.y += self.velocity.y;
self.velocity.y += self.gravity;
self.life--;
// Fade out over time
particleGraphics.alpha = self.life / 60;
if (self.life <= 0) {
self.destroy();
}
};
return self;
});
var PerfectIndicator = Container.expand(function () {
var self = Container.call(this);
var indicator = self.attachAsset('perfectIndicator', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0
});
self.show = function (x, y) {
self.x = x;
self.y = y;
indicator.alpha = 1;
indicator.scaleX = 0.5;
indicator.scaleY = 0.5;
tween(indicator, {
scaleX: 1,
scaleY: 1,
alpha: 0
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
self.destroy();
}
});
};
return self;
});
var Scorpion = Container.expand(function () {
var self = Container.call(this);
// Create scorpion body
var scorpionBody = self.attachAsset('scorpionBody', {
anchorX: 0.5,
anchorY: 0.5
});
// Create left claw
var leftClaw = self.attachAsset('scorpionClaw', {
anchorX: 1.0,
anchorY: 0.5
});
leftClaw.x = -25;
leftClaw.y = -10;
leftClaw.rotation = -0.3;
// Create right claw
var rightClaw = self.attachAsset('scorpionClaw', {
anchorX: 1.0,
anchorY: 0.5
});
rightClaw.x = -25;
rightClaw.y = 10;
rightClaw.rotation = 0.3;
// Create tail segments
var tailSegments = [];
for (var i = 0; i < 4; i++) {
var segment = self.attachAsset('scorpionTail', {
anchorX: 0.0,
anchorY: 0.5
});
segment.x = 40 + i * 12;
segment.y = 0;
segment.rotation = i * 0.1;
segment.scaleX = 0.8 - i * 0.1;
tailSegments.push(segment);
}
// Create stinger at the end
var stinger = self.attachAsset('scorpionStinger', {
anchorX: 0.5,
anchorY: 0.5
});
stinger.x = 88;
stinger.y = -5;
// Store references for animations
self.body = scorpionBody;
self.leftClaw = leftClaw;
self.rightClaw = rightClaw;
self.tailSegments = tailSegments;
self.stinger = stinger;
self.animationTimer = 0;
self.speed = 1.5; // Moderate speed movement
self.targetBubble = null;
self.lastTargetTime = 0;
// Find nearest bubble to target
self.findTarget = function () {
var nearestBubble = null;
var nearestDistance = Infinity;
for (var i = 0; i < bubbles.length; i++) {
var bubble = bubbles[i];
var distance = Math.sqrt(Math.pow(self.x - bubble.x, 2) + Math.pow(self.y - bubble.y, 2));
if (distance < nearestDistance) {
nearestDistance = distance;
nearestBubble = bubble;
}
}
return nearestBubble;
};
self.update = function () {
self.animationTimer++;
// Animate tail swishing
for (var i = 0; i < self.tailSegments.length; i++) {
var segment = self.tailSegments[i];
segment.rotation = Math.sin(self.animationTimer * 0.1 + i * 0.5) * 0.2 + i * 0.1;
}
// Animate stinger bobbing with pulsing glow
self.stinger.y = -5 + Math.sin(self.animationTimer * 0.15) * 3;
// Add dangerous pulsing glow to stinger
var stingerPulse = Math.sin(self.animationTimer * 0.2) * 0.5 + 0.5;
var stingerGlow = 0x440000 + Math.floor(stingerPulse * 0xbb0000);
self.stinger.tint = stingerGlow;
self.stinger.scaleX = 1 + stingerPulse * 0.3;
self.stinger.scaleY = 1 + stingerPulse * 0.3;
// Animate claws opening and closing with shimmer effect
var clawAnimation = Math.sin(self.animationTimer * 0.08) * 0.2;
self.leftClaw.rotation = -0.3 + clawAnimation;
self.rightClaw.rotation = 0.3 - clawAnimation;
// Add shimmer effect to claws
var shimmer = Math.sin(self.animationTimer * 0.15) * 0.3 + 0.7;
self.leftClaw.alpha = shimmer;
self.rightClaw.alpha = shimmer;
var clawGlow = 0x444444 + Math.floor(shimmer * 0x888888);
self.leftClaw.tint = clawGlow;
self.rightClaw.tint = clawGlow;
// Body bobbing while walking
self.body.y = Math.sin(self.animationTimer * 0.12) * 2;
// Beautiful rainbow color shifting effect
var hue = self.animationTimer * 2 % 360;
var rainbowColor = 0x8b4513; // Keep brown as base
if (hue < 60) {
rainbowColor = 0xff4500 + Math.floor(hue / 60 * 0x4000);
} else if (hue < 120) {
rainbowColor = 0xff8500 + Math.floor((hue - 60) / 60 * 0x4000);
} else if (hue < 180) {
rainbowColor = 0xffc500 + Math.floor((hue - 120) / 60 * 0x2000);
} else if (hue < 240) {
rainbowColor = 0xffe500 - Math.floor((hue - 180) / 60 * 0x6000);
} else if (hue < 300) {
rainbowColor = 0x9fe500 - Math.floor((hue - 240) / 60 * 0x4000);
} else {
rainbowColor = 0x5fe500 + Math.floor((hue - 300) / 60 * 0x6000);
}
self.body.tint = rainbowColor;
// Find new target every 2 seconds or if current target is destroyed
if (LK.ticks - self.lastTargetTime > 120 || !self.targetBubble || self.targetBubble.destroyed) {
self.targetBubble = self.findTarget();
self.lastTargetTime = LK.ticks;
}
// Move towards target bubble
if (self.targetBubble && !self.targetBubble.destroyed) {
var dx = self.targetBubble.x - self.x;
var dy = self.targetBubble.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 5) {
// Move towards bubble
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
// Rotate entire scorpion towards target
self.rotation = Math.atan2(dy, dx);
// Faster tail animation when moving
for (var j = 0; j < self.tailSegments.length; j++) {
var seg = self.tailSegments[j];
seg.rotation = Math.sin(self.animationTimer * 0.2 + j * 0.5) * 0.3 + j * 0.15;
}
} else {
// Close enough to pop the bubble
if (self.targetBubble && !self.targetBubble.destroyed) {
// Attack animation - claws snap
self.leftClaw.rotation = -0.8;
self.rightClaw.rotation = 0.8;
// Scorpion pops the bubble (counts as missed for player)
for (var i = 0; i < bubbles.length; i++) {
if (bubbles[i] === self.targetBubble) {
bubbles.splice(i, 1);
break;
}
}
self.targetBubble.pop();
missedBeat(); // Player loses health
self.targetBubble = null;
}
}
}
// Keep scorpion within screen bounds
if (self.x < 50) self.x = 50;
if (self.x > 1998) self.x = 1998;
if (self.y < 50) self.y = 50;
if (self.y > 2682) self.y = 2682;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a1a2e
});
/****
* Game Code
****/
var gameState = 'start'; // 'start' or 'playing'
var bubbles = [];
var particles = [];
var beatRings = [];
var score = 0;
var combo = 0;
var health = 5;
var gameSpeed = 1;
var spawnTimer = 0;
var beatTimer = 0;
var perfectIndicators = [];
var bubbleColors = [0x4A90E2, 0xFF6B6B, 0x4ECDC4, 0xFFE66D, 0x95E1D3, 0xFF69B4, 0x32CD32, 0xFFA500];
var bestScore = storage.bestScore || 0;
var scorpion = null;
var heartClickCount = 0; // Track clicks on heart bars for easter egg
// New power-up variables
var scoreMultiplier = 1;
var multiplierTimer = 0;
var timeSlowActive = false;
var timeSlowTimer = 0;
var specialEffects = [];
// GTA 2 style leaderboard data - flattened for storage compatibility
var leaderboardNames = storage.leaderboardNames || [];
var leaderboardScores = storage.leaderboardScores || [];
var playerName = storage.playerName || 'Player';
var showLeaderboard = false;
// Build leaderboard from flattened arrays
var leaderboard = [];
for (var idx = 0; idx < leaderboardNames.length; idx++) {
leaderboard.push({
name: leaderboardNames[idx],
score: leaderboardScores[idx]
});
}
// Particle explosion function
function createParticleExplosion(x, y, color, isGolden) {
var particleCount = isGolden ? 15 : 8;
var particleColors = isGolden ? [0xFFD700, 0xFFA500, 0xFFFF00] : [color, 0xFFFFFF];
for (var i = 0; i < particleCount; i++) {
var particle = new Particle(particleColors[Math.floor(Math.random() * particleColors.length)], 0.5 + Math.random() * 0.5);
particle.x = x + (Math.random() - 0.5) * 40;
particle.y = y + (Math.random() - 0.5) * 40;
particle.velocity.x = (Math.random() - 0.5) * 8;
particle.velocity.y = (Math.random() - 0.5) * 8 - 2;
particles.push(particle);
game.addChild(particle);
}
}
// Create explosive effect for explosive bubbles
function createExplosiveEffect(x, y) {
// Create shockwave
var shockwave = LK.getAsset('beatRing', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.8,
scaleX: 0.1,
scaleY: 0.1
});
shockwave.x = x;
shockwave.y = y;
shockwave.tint = 0xFF4444;
game.addChild(shockwave);
tween(shockwave, {
scaleX: 4,
scaleY: 4,
alpha: 0
}, {
duration: 600,
easing: tween.easeOut,
onFinish: function onFinish() {
shockwave.destroy();
}
});
// Create extra particles
for (var i = 0; i < 25; i++) {
var particle = new Particle(0xFF4444, 1 + Math.random());
particle.x = x + (Math.random() - 0.5) * 100;
particle.y = y + (Math.random() - 0.5) * 100;
particle.velocity.x = (Math.random() - 0.5) * 15;
particle.velocity.y = (Math.random() - 0.5) * 15 - 5;
particles.push(particle);
game.addChild(particle);
}
}
// Create multiplier effect
function createMultiplierEffect(x, y) {
var multiplierText = new Text2('x2 SCORE!', {
size: 80,
fill: 0x9932CC
});
multiplierText.anchor.set(0.5, 0.5);
multiplierText.x = x;
multiplierText.y = y;
multiplierText.alpha = 0;
game.addChild(multiplierText);
tween(multiplierText, {
alpha: 1,
y: y - 100,
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 800,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(multiplierText, {
alpha: 0
}, {
duration: 400,
onFinish: function onFinish() {
multiplierText.destroy();
}
});
}
});
}
// Create time effect
function createTimeEffect(x, y) {
var timeText = new Text2('TIME SLOW!', {
size: 70,
fill: 0x00FF7F
});
timeText.anchor.set(0.5, 0.5);
timeText.x = x;
timeText.y = y;
timeText.alpha = 0;
game.addChild(timeText);
tween(timeText, {
alpha: 1,
y: y - 80,
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 600,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(timeText, {
alpha: 0
}, {
duration: 300,
onFinish: function onFinish() {
timeText.destroy();
}
});
}
});
}
// Handle explosive bubble chain reaction
function handleExplosiveBubble(x, y) {
var explosionRadius = 200;
for (var i = bubbles.length - 1; i >= 0; i--) {
var bubble = bubbles[i];
var distance = Math.sqrt(Math.pow(x - bubble.x, 2) + Math.pow(y - bubble.y, 2));
if (distance <= explosionRadius) {
onBubblePopped(bubble, true); // Count as perfect hits
}
}
}
// Activate score multiplier power-up
function activateScoreMultiplier() {
scoreMultiplier = 2;
multiplierTimer = 300; // 5 seconds at 60fps
LK.effects.flashScreen(0x9932CC, 300);
}
// Activate time slowdown power-up
function activateTimeSlowdown() {
timeSlowActive = true;
timeSlowTimer = 480; // 8 seconds at 60fps
LK.effects.flashScreen(0x00FF7F, 400);
}
// Create beat ring effect
function createBeatRing() {
var ring = new BeatRing();
ring.x = 2048 / 2;
ring.y = 2732 / 2;
beatRings.push(ring);
game.addChild(ring);
ring.animate();
}
// GTA 2 style city background
function createCityBackground() {
// Create grid pattern like GTA 2 city streets
for (var x = 0; x < 2048; x += 256) {
for (var y = 0; y < 2732; y += 256) {
// Street lines
var streetH = LK.getAsset('particle', {
width: 256,
height: 4,
anchorX: 0,
anchorY: 0,
alpha: 0.2
});
streetH.x = x;
streetH.y = y;
streetH.tint = 0x666666;
game.addChild(streetH);
var streetV = LK.getAsset('particle', {
width: 4,
height: 256,
anchorX: 0,
anchorY: 0,
alpha: 0.2
});
streetV.x = x;
streetV.y = y;
streetV.tint = 0x666666;
game.addChild(streetV);
}
}
// Add some building-like rectangles
for (var i = 0; i < 20; i++) {
var building = LK.getAsset('particle', {
width: 60 + Math.random() * 100,
height: 60 + Math.random() * 100,
anchorX: 0,
anchorY: 0,
alpha: 0.1
});
building.x = Math.random() * 1900;
building.y = Math.random() * 2600;
building.tint = 0x444444;
game.addChild(building);
}
}
// Enhanced background effects
function updateBackgroundEffects() {
// Add subtle city ambiance effects
if (LK.ticks % 180 === 0) {
// Create occasional street lamp glow
var glow = LK.getAsset('particle', {
width: 20,
height: 20,
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.5
});
glow.x = Math.random() * 2048;
glow.y = Math.random() * 2732;
glow.tint = 0xFFFF88;
game.addChild(glow);
tween(glow, {
alpha: 0,
scaleX: 3,
scaleY: 3
}, {
duration: 3000,
easing: tween.easeOut,
onFinish: function onFinish() {
glow.destroy();
}
});
}
}
// GTA 2 style start screen UI
var titleText = new Text2('BUBBLE CITY', {
size: 160,
fill: 0xFFFF00
});
titleText.anchor.set(0.5, 0.5);
titleText.y = -300;
LK.gui.center.addChild(titleText);
var subtitleText = new Text2('CRIME BEATS EDITION', {
size: 60,
fill: 0x00FF00
});
subtitleText.anchor.set(0.5, 0.5);
subtitleText.y = -200;
LK.gui.center.addChild(subtitleText);
var instructionsText = new Text2('Pop bubbles in rhythm with the beats!\nTap to start your criminal career', {
size: 50,
fill: 0xFFFFFF
});
instructionsText.anchor.set(0.5, 0.5);
instructionsText.y = 100;
LK.gui.center.addChild(instructionsText);
// GTA 2 style leaderboard display
var leaderboardTitle = new Text2('TOP CRIMINALS', {
size: 80,
fill: 0xFF4444
});
leaderboardTitle.anchor.set(0.5, 0.5);
leaderboardTitle.y = 200;
LK.gui.center.addChild(leaderboardTitle);
var leaderboardText = new Text2('', {
size: 50,
fill: 0x00FFFF
});
leaderboardText.anchor.set(0.5, 0);
leaderboardText.y = 280;
LK.gui.center.addChild(leaderboardText);
var startBestScoreText = new Text2('Your Best: ' + bestScore, {
size: 60,
fill: 0x4ECDC4
});
startBestScoreText.anchor.set(0.5, 0.5);
startBestScoreText.y = 550;
LK.gui.center.addChild(startBestScoreText);
// Function to update leaderboard display
function updateLeaderboardDisplay() {
var displayText = '';
for (var i = 0; i < Math.min(5, leaderboard.length); i++) {
var entry = leaderboard[i];
displayText += i + 1 + '. ' + entry.name + ' - ' + entry.score + '\n';
}
if (leaderboard.length === 0) {
displayText = 'No scores yet!\nBe the first criminal!';
}
leaderboardText.setText(displayText);
}
// Game UI (initially hidden)
var scoreText = new Text2('Score: 0', {
size: 80,
fill: 0xFFFFFF
});
scoreText.anchor.set(0.5, 0);
scoreText.visible = false;
LK.gui.top.addChild(scoreText);
var comboText = new Text2('Combo: 0', {
size: 60,
fill: 0xFFD700
});
comboText.anchor.set(0, 0);
comboText.x = 50;
comboText.y = 120;
comboText.visible = false;
LK.gui.top.addChild(comboText);
var healthText = new Text2('♥♥♥♥♥', {
size: 70,
fill: 0xFF4444
});
healthText.anchor.set(1, 0);
healthText.visible = false;
// Add click handler for easter egg
healthText.down = function (x, y, obj) {
if (gameState === 'playing') {
heartClickCount++;
// Visual feedback for click
tween(healthText, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(healthText, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 100,
easing: tween.easeOut
});
}
});
// Easter egg trigger at 10 clicks
if (heartClickCount >= 10) {
heartClickCount = 0; // Reset counter
score += 31000; // Award 31,000 points
// Create spectacular visual effect
LK.effects.flashScreen(0xFFD700, 1000);
// Create multiple particle explosions
for (var i = 0; i < 5; i++) {
var explosionX = Math.random() * 2048;
var explosionY = Math.random() * 2732;
createParticleExplosion(explosionX, explosionY, 0xFFD700, true);
}
// Show special text indicator
var easterEggText = new Text2('EASTER EGG!\n+31,000 POINTS!', {
size: 120,
fill: 0xFFD700
});
easterEggText.anchor.set(0.5, 0.5);
easterEggText.x = 1024;
easterEggText.y = 1366;
easterEggText.alpha = 0;
easterEggText.scaleX = 0;
easterEggText.scaleY = 0;
game.addChild(easterEggText);
// Animate the easter egg text
tween(easterEggText, {
alpha: 1,
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 500,
easing: tween.bounceOut,
onFinish: function onFinish() {
tween(easterEggText, {
alpha: 0,
scaleX: 0.5,
scaleY: 0.5,
y: easterEggText.y - 200
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
easterEggText.destroy();
}
});
}
});
updateUI();
}
}
};
LK.gui.topRight.addChild(healthText);
var bestScoreText = new Text2('Best: ' + bestScore, {
size: 50,
fill: 0xFFFFFF
});
bestScoreText.anchor.set(0, 0);
bestScoreText.x = 50;
bestScoreText.y = 200;
bestScoreText.visible = false;
LK.gui.top.addChild(bestScoreText);
// Add score to leaderboard
function addToLeaderboard(playerScore) {
var newEntry = {
name: playerName,
score: playerScore
};
leaderboard.push(newEntry);
// Sort by score descending
leaderboard.sort(function (a, b) {
return b.score - a.score;
});
// Keep only top 10
leaderboard = leaderboard.slice(0, 10);
// Save to storage using flattened arrays
leaderboardNames = [];
leaderboardScores = [];
for (var i = 0; i < leaderboard.length; i++) {
leaderboardNames.push(leaderboard[i].name);
leaderboardScores.push(leaderboard[i].score);
}
storage.leaderboardNames = leaderboardNames;
storage.leaderboardScores = leaderboardScores;
}
function startGame() {
gameState = 'playing';
// Create GTA 2 style city background
createCityBackground();
// Hide start screen UI
titleText.visible = false;
subtitleText.visible = false;
instructionsText.visible = false;
startBestScoreText.visible = false;
leaderboardTitle.visible = false;
leaderboardText.visible = false;
// Show game UI
scoreText.visible = true;
comboText.visible = true;
healthText.visible = true;
bestScoreText.visible = true;
// Spawn scorpion
scorpion = new Scorpion();
scorpion.x = Math.random() * 1800 + 124;
scorpion.y = Math.random() * 2400 + 166;
game.addChild(scorpion);
}
// Touch handler for starting game
game.down = function (x, y, obj) {
if (gameState === 'start') {
startGame();
}
};
function updateUI() {
var displayScore = 'Score: ' + score;
if (scoreMultiplier > 1) {
displayScore += ' (x' + scoreMultiplier + ')';
}
scoreText.setText(displayScore);
var displayCombo = 'Combo: ' + combo;
if (timeSlowActive) {
displayCombo += ' [TIME SLOW]';
}
comboText.setText(displayCombo);
var hearts = '';
for (var i = 0; i < health; i++) {
hearts += '♥';
}
healthText.setText(hearts);
// Update best score if current score is higher
if (score > bestScore) {
bestScore = score;
storage.bestScore = bestScore;
bestScoreText.setText('Best: ' + bestScore);
}
}
function spawnBubble() {
var isGolden = Math.random() < 0.15; // 15% chance for golden bubble
var specialType = null;
// 10% chance for special bubbles (only if not golden)
if (!isGolden && Math.random() < 0.1) {
var specialTypes = ['explosive', 'multiplier', 'time'];
specialType = specialTypes[Math.floor(Math.random() * specialTypes.length)];
}
var color = bubbleColors[Math.floor(Math.random() * bubbleColors.length)];
var bubble = new Bubble(color, isGolden, specialType);
// Better spawn positioning with grid-like distribution
var attempts = 0;
var maxAttempts = 20;
do {
bubble.x = Math.random() * (2048 - 300) + 150;
bubble.y = Math.random() * (2732 - 600) + 300;
attempts++;
} while (isTooCloseToOthers(bubble) && attempts < maxAttempts);
if (attempts < maxAttempts) {
bubbles.push(bubble);
game.addChild(bubble);
bubble.spawned = true;
bubble.lastPulseTime = LK.ticks;
// Enhanced spawn animation with rotation and bounce
bubble.alpha = 0;
bubble.scaleX = 0;
bubble.scaleY = 0;
bubble.rotation = Math.PI * 2;
bubble.y -= 100; // Start above final position
tween(bubble, {
alpha: 1,
scaleX: 1.2,
scaleY: 1.2,
rotation: 0,
y: bubble.y + 100
}, {
duration: 500,
easing: tween.bounceOut,
onFinish: function onFinish() {
tween(bubble, {
scaleX: 1,
scaleY: 1
}, {
duration: 200,
easing: tween.easeOut
});
bubble.startFloat(); // Start floating animation
}
});
// Add sparkle effect for golden bubbles
if (isGolden) {
createSparkleEffect(bubble);
}
} else {
bubble.destroy();
}
}
function isTooCloseToOthers(bubble) {
for (var i = 0; i < bubbles.length; i++) {
var distance = Math.sqrt(Math.pow(bubble.x - bubbles[i].x, 2) + Math.pow(bubble.y - bubbles[i].y, 2));
if (distance < 200) {
return true;
}
}
return false;
}
function createSparkleEffect(bubble) {
var sparkleTimer = LK.setInterval(function () {
if (bubble.destroyed) {
LK.clearInterval(sparkleTimer);
return;
}
var sparkle = new Particle(0xFFD700, 0.3);
sparkle.x = bubble.x + (Math.random() - 0.5) * 100;
sparkle.y = bubble.y + (Math.random() - 0.5) * 100;
sparkle.velocity.x = (Math.random() - 0.5) * 1;
sparkle.velocity.y = (Math.random() - 0.5) * 1;
sparkle.gravity = -0.05; // Float upward more slowly
particles.push(sparkle);
game.addChild(sparkle);
}, 400);
}
function onBubblePopped(bubble, isPerfect) {
var basePoints = bubble.isGolden ? 100 : 10;
// Special bubble bonuses
if (bubble.specialType === 'explosive') {
basePoints = 50;
} else if (bubble.specialType === 'multiplier') {
basePoints = 25;
} else if (bubble.specialType === 'time') {
basePoints = 30;
}
var points = isPerfect ? basePoints * 2 : basePoints;
if (isPerfect) {
combo++;
points *= 1 + combo * 0.1;
// Show perfect indicator
var indicator = new PerfectIndicator();
perfectIndicators.push(indicator);
game.addChild(indicator);
indicator.show(bubble.x, bubble.y);
if (bubble.isGolden) {
LK.effects.flashScreen(0xFFD700, 300);
}
} else {
combo = 0;
}
// Apply score multiplier if active
points *= scoreMultiplier;
score += Math.floor(points);
// Remove bubble from array
for (var i = 0; i < bubbles.length; i++) {
if (bubbles[i] === bubble) {
bubbles.splice(i, 1);
break;
}
}
bubble.pop();
updateUI();
}
function missedBeat() {
combo = 0;
health--;
LK.effects.flashScreen(0xFF0000, 200);
LK.getSound('miss').play();
if (health <= 0) {
// Add score to leaderboard before game over
addToLeaderboard(score);
LK.showGameOver();
}
updateUI();
}
// Start background music
LK.playMusic('bgMusic');
game.update = function () {
if (gameState === 'start') {
// Update best score display on start screen
startBestScoreText.setText('Your Best: ' + bestScore);
// Update leaderboard display
updateLeaderboardDisplay();
// GTA 2 style animations
titleText.rotation = Math.sin(LK.ticks * 0.01) * 0.1;
subtitleText.alpha = 0.7 + Math.sin(LK.ticks * 0.05) * 0.3;
leaderboardTitle.scaleX = 1 + Math.sin(LK.ticks * 0.03) * 0.1;
leaderboardTitle.scaleY = 1 + Math.sin(LK.ticks * 0.03) * 0.1;
return;
}
beatTimer++;
spawnTimer++;
updateBackgroundEffects();
// Update power-up timers
if (multiplierTimer > 0) {
multiplierTimer--;
if (multiplierTimer <= 0) {
scoreMultiplier = 1;
}
}
if (timeSlowTimer > 0) {
timeSlowTimer--;
if (timeSlowTimer <= 0) {
timeSlowActive = false;
}
}
// Apply time slow effect to spawn rate
var timeMultiplier = timeSlowActive ? 1.5 : 1;
// Enhanced beat visualization
if (beatTimer % 60 === 0) {
// Every second - create beat ring and pulse bubbles
createBeatRing();
for (var i = 0; i < bubbles.length; i++) {
bubbles[i].startPulse();
}
}
// Update particles
for (var p = particles.length - 1; p >= 0; p--) {
var particle = particles[p];
if (particle.destroyed) {
particles.splice(p, 1);
}
}
// Clean up beat rings
for (var r = beatRings.length - 1; r >= 0; r--) {
if (beatRings[r].destroyed) {
beatRings.splice(r, 1);
}
}
// Check if 6 bubbles are on screen - game over condition (increased from 5)
if (bubbles.length >= 6) {
// Update best score before game over
if (score > bestScore) {
bestScore = score;
storage.bestScore = bestScore;
}
// Add score to leaderboard
addToLeaderboard(score);
// Screen shake effect before game over
LK.effects.flashScreen(0xFF0000, 500);
LK.showGameOver();
}
// Dynamic spawn rate based on score and combo
var baseSpawnRate = Math.max(150 - Math.floor(score / 50) * 5, 80);
var comboBonus = Math.min(combo * 5, 30);
var spawnRate = (baseSpawnRate - comboBonus) * timeMultiplier;
if (spawnTimer >= spawnRate && bubbles.length < 10) {
spawnBubble();
spawnTimer = 0;
}
// Remove bubbles that have been on screen too long
for (var i = bubbles.length - 1; i >= 0; i--) {
var bubble = bubbles[i];
// Slightly longer lifetime for better gameplay
if (bubble.spawned && LK.ticks - bubble.lastPulseTime > 1500) {
// Warning effect before removal
tween(bubble, {
alpha: 0.3,
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
if (!bubble.destroyed) {
bubble.destroy();
bubbles.splice(i, 1);
missedBeat();
}
}
});
}
}
// Clean up perfect indicators
for (var j = perfectIndicators.length - 1; j >= 0; j--) {
if (perfectIndicators[j].destroyed) {
perfectIndicators.splice(j, 1);
}
}
// Update scorpion
if (scorpion && !scorpion.destroyed) {
// Scorpion updates automatically via its update method
}
// Increase difficulty
gameSpeed = 1 + score / 1000;
// Win condition
if (score >= 3000) {
// Add score to leaderboard before showing win
addToLeaderboard(score);
LK.showYouWin();
}
};
// Initialize UI
updateUI();