/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ var Arrow = Container.expand(function () { var self = Container.call(this); // Create arrow shaft var arrowShaft = LK.getAsset('powerupBubble', { anchorX: 0.5, anchorY: 0.5 }); arrowShaft.scale.set(0.4, 1.8); // Longer, slimmer shaft arrowShaft.tint = 0xFFAA00; // Brighter orange color self.addChild(arrowShaft); // Add arrow tip (triangle shape approximation) var arrowTip = LK.getAsset('powerupBubble', { anchorX: 0.5, anchorY: 0.5 }); arrowTip.scale.set(1.0, 1.0); arrowTip.tint = 0xFFAA00; arrowTip.y = -60; self.addChild(arrowTip); // Add feathers at the back of the arrow var leftFeather = LK.getAsset('powerupBubble', { anchorX: 0.5, anchorY: 0.5 }); leftFeather.scale.set(0.5, 0.5); leftFeather.tint = 0xFFFFFF; leftFeather.x = -15; leftFeather.y = 50; self.addChild(leftFeather); var rightFeather = LK.getAsset('powerupBubble', { anchorX: 0.5, anchorY: 0.5 }); rightFeather.scale.set(0.5, 0.5); rightFeather.tint = 0xFFFFFF; rightFeather.x = 15; rightFeather.y = 50; self.addChild(rightFeather); // Add glow effect var glow = LK.getAsset('powerupBubble', { anchorX: 0.5, anchorY: 0.5 }); glow.scale.set(2.0, 2.0); glow.tint = 0xFFAA00; glow.alpha = 0.3; self.addChild(glow); // Animate the glow tween(glow, { scaleX: 2.2, scaleY: 2.2, alpha: 0.2 }, { duration: 1000, easing: tween.easeInOut, yoyo: true, repeat: -1 }); // Timer for popping bubbles self.timer = null; self.isActive = true; // Method to pop a random bubble every 2 seconds self.startPopping = function () { self.timer = LK.setInterval(function () { self.popRandomBubble(); }, 2000); }; // Stop popping bubbles self.stopPopping = function () { if (self.timer) { LK.clearInterval(self.timer); self.timer = null; } }; // Function to pop a random bubble self.popRandomBubble = function () { // Find valid bubbles (not bombs, and active) var validBubbles = bubbles.filter(function (bubble) { return bubble.isActive && bubble.type !== 'bomb'; }); if (validBubbles.length > 0) { // Get a random bubble from valid ones var randomIndex = Math.floor(Math.random() * validBubbles.length); var targetBubble = validBubbles[randomIndex]; // Pop it targetBubble.pop(); // Create a more visually interesting arrow projectile var arrowProjectile = new Container(); // Arrow projectile body var projectileBody = LK.getAsset('powerupBubble', { anchorX: 0.5, anchorY: 0.5 }); projectileBody.scale.set(0.3, 0.7); projectileBody.tint = 0xFFAA00; arrowProjectile.addChild(projectileBody); // Arrow projectile tip var projectileTip = LK.getAsset('powerupBubble', { anchorX: 0.5, anchorY: 0.5 }); projectileTip.scale.set(0.6, 0.5); projectileTip.tint = 0xFFAA00; projectileTip.y = -20; arrowProjectile.addChild(projectileTip); // Trail effect var trail = LK.getAsset('powerupBubble', { anchorX: 0.5, anchorY: 0.5 }); trail.scale.set(0.2, 1.0); trail.tint = 0xFFDD00; trail.alpha = 0.7; trail.y = 25; arrowProjectile.addChild(trail); // Set position arrowProjectile.x = self.x; arrowProjectile.y = self.y; game.addChild(arrowProjectile); // Calculate angle to target var angleToTarget = Math.atan2(targetBubble.y - self.y, targetBubble.x - self.x); arrowProjectile.rotation = angleToTarget + Math.PI / 2; // Animate projectile to target with a slight arc var midX = (self.x + targetBubble.x) / 2; var midY = (self.y + targetBubble.y) / 2 - 50; // Arc up a bit // First part of animation - to midpoint with slight arc tween(arrowProjectile, { x: midX, y: midY, scaleX: 1.1, scaleY: 1.1 }, { duration: 150, easing: tween.easeOut, onFinish: function onFinish() { // Second part - to target tween(arrowProjectile, { x: targetBubble.x, y: targetBubble.y, scaleX: 0.8, scaleY: 0.8, alpha: 0.8 }, { duration: 150, easing: tween.easeIn, onFinish: function onFinish() { // Flash effect at impact var impact = LK.getAsset('powerupBubble', { anchorX: 0.5, anchorY: 0.5 }); impact.scale.set(1.0, 1.0); impact.tint = 0xFFFF00; impact.alpha = 0.8; impact.x = targetBubble.x; impact.y = targetBubble.y; game.addChild(impact); // Animate impact flash tween(impact, { scaleX: 2.0, scaleY: 2.0, alpha: 0 }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { game.removeChild(impact); } }); game.removeChild(arrowProjectile); } }); } }); } }; return self; }); var AutoShooter = Container.expand(function () { var self = Container.call(this); self.isActive = false; self.timer = null; // Visual representation of auto-shooter as a gun var shooterGraphics = self.attachAsset('gunImage', { anchorX: 0.5, anchorY: 0.5 }); // Customize appearance shooterGraphics.scale.set(1.2); // Add visual indicator var shooterText = new Text2('AUTO', { size: 50, fill: 0xFFFFFF }); shooterText.anchor.set(0.5, 0.5); self.addChild(shooterText); // Start the auto-shooter self.activate = function () { if (self.isActive) { return; } self.isActive = true; // Pulse animation to show it's active tween(shooterGraphics, { scaleX: 1.8, scaleY: 1.8 }, { duration: 1000, easing: tween.easeInOut }); // Set up timer to pop a bubble every second self.timer = LK.setInterval(function () { self.popRandomBubble(); }, 1000); }; self.deactivate = function () { if (!self.isActive) { return; } self.isActive = false; if (self.timer) { LK.clearInterval(self.timer); self.timer = null; } // Return to normal size tween(shooterGraphics, { scaleX: 1.5, scaleY: 1.5 }, { duration: 500, easing: tween.easeOut }); }; // Function to pop a random bubble self.popRandomBubble = function () { // Find valid bubbles (not bombs, and active) var validBubbles = bubbles.filter(function (bubble) { return bubble.isActive && bubble.type !== 'bomb'; }); if (validBubbles.length > 0) { // Get a random bubble from valid ones var randomIndex = Math.floor(Math.random() * validBubbles.length); var targetBubble = validBubbles[randomIndex]; // Pop it targetBubble.pop(); // Visual effect from auto-shooter to bubble - resembles a bullet var beam = LK.getAsset('gunImage', { anchorX: 0.5, anchorY: 0.5 }); beam.scale.set(0.3, 0.15); // Larger beam size beam.tint = 0xFF0000; beam.x = self.x; beam.y = self.y; game.addChild(beam); // Add glow effect beam.alpha = 0.9; // Animate beam to target bubble with more dramatic effect tween(beam, { x: targetBubble.x, y: targetBubble.y, scaleX: 0.5, // Grow while traveling scaleY: 0.25, alpha: 0 }, { duration: 400, // Slightly longer duration for better visibility easing: tween.linear, onFinish: function onFinish() { game.removeChild(beam); } }); } }; return self; }); var Bubble = Container.expand(function (type, speed) { var self = Container.call(this); self.type = type || 'standard'; self.speed = speed || 2; self.points = 1; self.isActive = true; var assetId = 'standardBubble'; if (self.type === 'bonus') { assetId = 'bonusBubble'; self.points = 5; } else if (self.type === 'bomb') { assetId = 'bombBubble'; self.points = -10; } else if (self.type === 'powerup') { assetId = 'powerupBubble'; self.points = 2; } var bubbleGraphics = self.attachAsset(assetId, { anchorX: 0.5, anchorY: 0.5 }); if (self.type === 'bonus') { bubbleGraphics.scale.set(1.2); } else if (self.type === 'powerup') { bubbleGraphics.scale.set(0.9); } // Add some interactivity by scaling bubble on press self.down = function (x, y, obj) { if (!self.isActive) { return; } // Scale down bubble when pressed tween(bubbleGraphics, { scaleX: 0.8, scaleY: 0.8 }, { duration: 100, easing: tween.easeOut }); self.pop(); }; self.pop = function () { if (!self.isActive) { return; } self.isActive = false; // Play sound based on bubble type if (self.type === 'bomb') { LK.getSound('explosion').play(); } else if (self.type === 'bonus') { LK.getSound('bonus').play(); } else if (self.type === 'powerup') { LK.getSound('powerup').play(); } else { LK.getSound('pop').play(); } // Animation for popping tween(bubbleGraphics, { scaleX: 1.5, scaleY: 1.5, alpha: 0 }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { if (self.parent) { self.parent.removeChild(self); } } }); // Update score LK.setScore(LK.getScore() + self.points); // Handle special effects if (self.type === 'bomb') { // Flash screen red for bomb LK.effects.flashScreen(0xe74c3c, 300); } else if (self.type === 'powerup') { // Slow down all bubbles if (slowDownTimer) { LK.clearTimeout(slowDownTimer); } bubbleSpeed = baseSpeed * 0.5; slowDownTimer = LK.setTimeout(function () { bubbleSpeed = baseSpeed; }, 5000); } }; self.update = function () { if (!self.isActive) { return; } // Move bubble upward self.y -= self.speed; // Slight side-to-side movement self.x += Math.sin(LK.ticks / 20 + self.id * 0.3) * 0.8; // Remove if bubble goes off-screen if (self.y < -150) { // If bomb escapes screen, that's good if (self.type === 'bomb') { LK.setScore(LK.getScore() + 2); } // If it's a bonus and escapes, that's bad else if (self.type === 'bonus') { LK.setScore(LK.getScore() - 2); } if (self.parent) { self.parent.removeChild(self); } } }; return self; }); var Shop = Container.expand(function () { var self = Container.call(this); // Create shop panel background var panelBg = self.attachAsset('standardBubble', { anchorX: 0.5, anchorY: 0.5 }); // Stretch to make panel panelBg.scale.set(10, 6); panelBg.alpha = 0.9; // Add title var titleText = new Text2('Bubble Shop', { size: 100, fill: 0xFFFFFF }); titleText.anchor.set(0.5, 0); titleText.y = -300; self.addChild(titleText); // Add close button var closeButton = new Text2('Close', { size: 60, fill: 0xFFFFFF }); closeButton.anchor.set(0.5, 0.5); closeButton.y = 300; closeButton.interactive = true; closeButton.down = function () { closeShop(); }; self.addChild(closeButton); // Add shop items // Arrow item var item1 = new Text2('Arrow: 100 pts', { size: 60, fill: 0xFFFFFF }); item1.anchor.set(0.5, 0.5); item1.y = -100; // Position above auto shooter item1.interactive = true; item1.down = function () { // Check if player has enough points if (LK.getScore() >= 100) { // Deduct points LK.setScore(LK.getScore() - 100); // Create and position arrow at left middle of screen var arrow = new Arrow(); arrow.x = 100; // Position at left edge arrow.y = 2732 / 2; // Middle of screen height game.addChild(arrow); // Start the arrow's bubble popping behavior arrow.startPopping(); // Visual feedback tween(item1, { scaleX: 1.2, scaleY: 1.2 }, { duration: 100, easing: tween.easeOut, onFinish: function onFinish() { tween(item1, { scaleX: 1.0, scaleY: 1.0 }, { duration: 100, easing: tween.easeIn }); } }); // Close shop after purchase closeShop(); } }; self.addChild(item1); // Auto shooter item var item3 = new Text2('Auto Shooter: 150 pts', { size: 60, fill: 0xFFFFFF }); item3.anchor.set(0.5, 0.5); item3.y = 0; // Centered position item3.interactive = true; item3.down = function () { // Check if player has enough points if (LK.getScore() >= 150) { // Deduct points LK.setScore(LK.getScore() - 150); // Increment auto-shooter count autoShooterCount++; // Activate auto-shooter activateAutoShooter(); // Visual feedback tween(item3, { scaleX: 1.2, scaleY: 1.2 }, { duration: 100, easing: tween.easeOut, onFinish: function onFinish() { tween(item3, { scaleX: 1.0, scaleY: 1.0 }, { duration: 100, easing: tween.easeIn }); } }); // Close shop after purchase closeShop(); } }; self.addChild(item3); // Initialize with hidden state self.visible = false; return self; }); var ShopButton = Container.expand(function () { var self = Container.call(this); // Create shop button visual var buttonGraphics = self.attachAsset('standardBubble', { anchorX: 0.5, anchorY: 0.5 }); // Customize appearance buttonGraphics.scale.set(1.2); // Add text label var buttonText = new Text2('Shop', { size: 70, fill: 0xFFFFFF }); buttonText.anchor.set(0.5, 0.5); self.addChild(buttonText); // Handle click self.down = function (x, y, obj) { tween(buttonGraphics, { scaleX: 0.7, scaleY: 0.7 }, { duration: 100, easing: tween.easeOut }); // Open shop openShop(); }; self.up = function (x, y, obj) { tween(buttonGraphics, { scaleX: 0.8, scaleY: 0.8 }, { duration: 100, easing: tween.easeOut }); }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x87CEEB }); /**** * Game Code ****/ // Game variables var bubbles = []; var level = 1; var baseSpeed = 2; var bubbleSpeed = baseSpeed; var spawnRate = 60; // Ticks between bubble spawns var maxBubbles = 10; var timer = 0; var scoreDisplay; var levelDisplay; var slowDownTimer = null; var bubbleIdCounter = 0; var shopButton; var shopPanel; var isShopOpen = false; var autoShooter; var secondGun; var autoShooterCount = 0; var autoShooterActive = false; var arrows = []; // Track arrows for management // Auto-shooter functions function activateAutoShooter() { if (!autoShooterActive) { autoShooterActive = true; // Create auto-shooter if it doesn't exist if (!autoShooter) { // Create first gun autoShooter = new AutoShooter(); autoShooter.x = 2048 - 100; // Position at right side of screen autoShooter.y = 2732 / 2; // Position at vertical center game.addChild(autoShooter); // Check if we already have a second gun if (!secondGun && autoShooterCount > 1) { // Create second gun symmetrically on left side secondGun = new AutoShooter(); secondGun.x = 100; // Position at left side of screen (symmetrical to first gun) secondGun.y = 2732 / 2; // Position at vertical center game.addChild(secondGun); secondGun.activate(); } } // Activate it autoShooter.activate(); } } function deactivateAutoShooter() { if (autoShooterActive) { autoShooterActive = false; if (autoShooter) { autoShooter.deactivate(); } if (secondGun) { secondGun.deactivate(); } } } // Shop functions function openShop() { if (!isShopOpen) { isShopOpen = true; shopPanel.visible = true; // Animate shop opening shopPanel.scale.set(0.1); tween(shopPanel, { scaleX: 1, scaleY: 1 }, { duration: 300, easing: tween.easeOut }); } } function closeShop() { if (isShopOpen) { // Animate shop closing tween(shopPanel, { scaleX: 0.1, scaleY: 0.1 }, { duration: 300, easing: tween.easeIn, onFinish: function onFinish() { shopPanel.visible = false; isShopOpen = false; } }); } } // Set up UI function setupUI() { // Score display scoreDisplay = new Text2('Score: 0', { size: 80, fill: 0xFFFFFF }); scoreDisplay.anchor.set(0.5, 0); LK.gui.top.addChild(scoreDisplay); // Level display levelDisplay = new Text2('Level: ' + level, { size: 80, fill: 0xFFFFFF }); levelDisplay.anchor.set(1, 0); LK.gui.topLeft.addChild(levelDisplay); // Position them properly levelDisplay.x = -120; levelDisplay.y = 120; // Keep away from top left corner scoreDisplay.y = 20; // Add shop button to top right shopButton = new ShopButton(); shopButton.x = -100; shopButton.y = 100; LK.gui.topRight.addChild(shopButton); // Create shop panel shopPanel = new Shop(); shopPanel.visible = false; LK.gui.center.addChild(shopPanel); } // Spawn a new bubble function spawnBubble() { if (bubbles.length >= maxBubbles) { return; } var type = 'standard'; var rand = Math.random(); // Determine bubble type based on probability if (rand < 0.05) { type = 'bomb'; } else if (rand < 0.15) { type = 'bonus'; } else if (rand < 0.20) { type = 'powerup'; } // Create bubble var bubble = new Bubble(type, bubbleSpeed); bubble.id = bubbleIdCounter++; // Position at random X at bottom of screen bubble.x = Math.random() * (2048 - 200) + 100; bubble.y = 2732 + 100; // Add bubble to game and array game.addChild(bubble); bubbles.push(bubble); } // Remove bubble from array function removeBubble(bubble) { var index = bubbles.indexOf(bubble); if (index > -1) { bubbles.splice(index, 1); } } // Update game difficulty based on level function updateDifficulty() { baseSpeed = 2 + level * 0.5; bubbleSpeed = baseSpeed; spawnRate = Math.max(20, 60 - level * 5); maxBubbles = 10 + level; } // Update level based on score function checkLevelUp() { var newLevel = Math.floor(LK.getScore() / 50) + 1; if (newLevel > level) { level = newLevel; levelDisplay.setText('Level: ' + level); updateDifficulty(); // Flash screen to indicate level up LK.effects.flashScreen(0x2ecc71, 500); } } // Initialize game function initGame() { LK.setScore(0); level = 1; timer = 0; // No gameTime limit - infinite gameplay bubbleSpeed = baseSpeed; bubbleIdCounter = 0; // Reset auto-shooter if (autoShooter && autoShooter.parent) { autoShooter.deactivate(); game.removeChild(autoShooter); } if (secondGun && secondGun.parent) { secondGun.deactivate(); game.removeChild(secondGun); } autoShooter = null; secondGun = null; autoShooterCount = 0; autoShooterActive = false; // Clear any arrows var allChildren = game.children.slice(); for (var i = 0; i < allChildren.length; i++) { if (allChildren[i] instanceof Arrow) { if (allChildren[i].timer) { allChildren[i].stopPopping(); } game.removeChild(allChildren[i]); } } // Clear any existing bubbles for (var i = bubbles.length - 1; i >= 0; i--) { game.removeChild(bubbles[i]); } bubbles = []; // Set up UI setupUI(); // Update difficulty updateDifficulty(); // Start background music LK.playMusic('bgMusic', { fade: { start: 0, end: 0.4, duration: 1000 } }); } // No update timer function needed for infinite gameplay // End game - will only be called manually when reaching score milestones function endGame() { if (LK.getScore() >= 100) { LK.showYouWin(); } else { LK.showGameOver(); } } // Initialize the game initGame(); // Main game update function game.update = function () { timer++; // Update score display scoreDisplay.setText('Score: ' + LK.getScore()); // Check for level up checkLevelUp(); // Spawn bubbles at regular intervals if (timer % spawnRate === 0) { spawnBubble(); } // Clean up bubbles that are no longer in the scene for (var i = bubbles.length - 1; i >= 0; i--) { if (!bubbles[i].parent) { removeBubble(bubbles[i]); } } }; // Handle game clicks/taps game.down = function (x, y, obj) { // Just handle click/tap on the game background // Bubbles have their own click handlers }; // Handle mouse/touch movement game.move = function (x, y, obj) { // No implementation needed for this game };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Arrow = Container.expand(function () {
var self = Container.call(this);
// Create arrow shaft
var arrowShaft = LK.getAsset('powerupBubble', {
anchorX: 0.5,
anchorY: 0.5
});
arrowShaft.scale.set(0.4, 1.8); // Longer, slimmer shaft
arrowShaft.tint = 0xFFAA00; // Brighter orange color
self.addChild(arrowShaft);
// Add arrow tip (triangle shape approximation)
var arrowTip = LK.getAsset('powerupBubble', {
anchorX: 0.5,
anchorY: 0.5
});
arrowTip.scale.set(1.0, 1.0);
arrowTip.tint = 0xFFAA00;
arrowTip.y = -60;
self.addChild(arrowTip);
// Add feathers at the back of the arrow
var leftFeather = LK.getAsset('powerupBubble', {
anchorX: 0.5,
anchorY: 0.5
});
leftFeather.scale.set(0.5, 0.5);
leftFeather.tint = 0xFFFFFF;
leftFeather.x = -15;
leftFeather.y = 50;
self.addChild(leftFeather);
var rightFeather = LK.getAsset('powerupBubble', {
anchorX: 0.5,
anchorY: 0.5
});
rightFeather.scale.set(0.5, 0.5);
rightFeather.tint = 0xFFFFFF;
rightFeather.x = 15;
rightFeather.y = 50;
self.addChild(rightFeather);
// Add glow effect
var glow = LK.getAsset('powerupBubble', {
anchorX: 0.5,
anchorY: 0.5
});
glow.scale.set(2.0, 2.0);
glow.tint = 0xFFAA00;
glow.alpha = 0.3;
self.addChild(glow);
// Animate the glow
tween(glow, {
scaleX: 2.2,
scaleY: 2.2,
alpha: 0.2
}, {
duration: 1000,
easing: tween.easeInOut,
yoyo: true,
repeat: -1
});
// Timer for popping bubbles
self.timer = null;
self.isActive = true;
// Method to pop a random bubble every 2 seconds
self.startPopping = function () {
self.timer = LK.setInterval(function () {
self.popRandomBubble();
}, 2000);
};
// Stop popping bubbles
self.stopPopping = function () {
if (self.timer) {
LK.clearInterval(self.timer);
self.timer = null;
}
};
// Function to pop a random bubble
self.popRandomBubble = function () {
// Find valid bubbles (not bombs, and active)
var validBubbles = bubbles.filter(function (bubble) {
return bubble.isActive && bubble.type !== 'bomb';
});
if (validBubbles.length > 0) {
// Get a random bubble from valid ones
var randomIndex = Math.floor(Math.random() * validBubbles.length);
var targetBubble = validBubbles[randomIndex];
// Pop it
targetBubble.pop();
// Create a more visually interesting arrow projectile
var arrowProjectile = new Container();
// Arrow projectile body
var projectileBody = LK.getAsset('powerupBubble', {
anchorX: 0.5,
anchorY: 0.5
});
projectileBody.scale.set(0.3, 0.7);
projectileBody.tint = 0xFFAA00;
arrowProjectile.addChild(projectileBody);
// Arrow projectile tip
var projectileTip = LK.getAsset('powerupBubble', {
anchorX: 0.5,
anchorY: 0.5
});
projectileTip.scale.set(0.6, 0.5);
projectileTip.tint = 0xFFAA00;
projectileTip.y = -20;
arrowProjectile.addChild(projectileTip);
// Trail effect
var trail = LK.getAsset('powerupBubble', {
anchorX: 0.5,
anchorY: 0.5
});
trail.scale.set(0.2, 1.0);
trail.tint = 0xFFDD00;
trail.alpha = 0.7;
trail.y = 25;
arrowProjectile.addChild(trail);
// Set position
arrowProjectile.x = self.x;
arrowProjectile.y = self.y;
game.addChild(arrowProjectile);
// Calculate angle to target
var angleToTarget = Math.atan2(targetBubble.y - self.y, targetBubble.x - self.x);
arrowProjectile.rotation = angleToTarget + Math.PI / 2;
// Animate projectile to target with a slight arc
var midX = (self.x + targetBubble.x) / 2;
var midY = (self.y + targetBubble.y) / 2 - 50; // Arc up a bit
// First part of animation - to midpoint with slight arc
tween(arrowProjectile, {
x: midX,
y: midY,
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
// Second part - to target
tween(arrowProjectile, {
x: targetBubble.x,
y: targetBubble.y,
scaleX: 0.8,
scaleY: 0.8,
alpha: 0.8
}, {
duration: 150,
easing: tween.easeIn,
onFinish: function onFinish() {
// Flash effect at impact
var impact = LK.getAsset('powerupBubble', {
anchorX: 0.5,
anchorY: 0.5
});
impact.scale.set(1.0, 1.0);
impact.tint = 0xFFFF00;
impact.alpha = 0.8;
impact.x = targetBubble.x;
impact.y = targetBubble.y;
game.addChild(impact);
// Animate impact flash
tween(impact, {
scaleX: 2.0,
scaleY: 2.0,
alpha: 0
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
game.removeChild(impact);
}
});
game.removeChild(arrowProjectile);
}
});
}
});
}
};
return self;
});
var AutoShooter = Container.expand(function () {
var self = Container.call(this);
self.isActive = false;
self.timer = null;
// Visual representation of auto-shooter as a gun
var shooterGraphics = self.attachAsset('gunImage', {
anchorX: 0.5,
anchorY: 0.5
});
// Customize appearance
shooterGraphics.scale.set(1.2);
// Add visual indicator
var shooterText = new Text2('AUTO', {
size: 50,
fill: 0xFFFFFF
});
shooterText.anchor.set(0.5, 0.5);
self.addChild(shooterText);
// Start the auto-shooter
self.activate = function () {
if (self.isActive) {
return;
}
self.isActive = true;
// Pulse animation to show it's active
tween(shooterGraphics, {
scaleX: 1.8,
scaleY: 1.8
}, {
duration: 1000,
easing: tween.easeInOut
});
// Set up timer to pop a bubble every second
self.timer = LK.setInterval(function () {
self.popRandomBubble();
}, 1000);
};
self.deactivate = function () {
if (!self.isActive) {
return;
}
self.isActive = false;
if (self.timer) {
LK.clearInterval(self.timer);
self.timer = null;
}
// Return to normal size
tween(shooterGraphics, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 500,
easing: tween.easeOut
});
};
// Function to pop a random bubble
self.popRandomBubble = function () {
// Find valid bubbles (not bombs, and active)
var validBubbles = bubbles.filter(function (bubble) {
return bubble.isActive && bubble.type !== 'bomb';
});
if (validBubbles.length > 0) {
// Get a random bubble from valid ones
var randomIndex = Math.floor(Math.random() * validBubbles.length);
var targetBubble = validBubbles[randomIndex];
// Pop it
targetBubble.pop();
// Visual effect from auto-shooter to bubble - resembles a bullet
var beam = LK.getAsset('gunImage', {
anchorX: 0.5,
anchorY: 0.5
});
beam.scale.set(0.3, 0.15); // Larger beam size
beam.tint = 0xFF0000;
beam.x = self.x;
beam.y = self.y;
game.addChild(beam);
// Add glow effect
beam.alpha = 0.9;
// Animate beam to target bubble with more dramatic effect
tween(beam, {
x: targetBubble.x,
y: targetBubble.y,
scaleX: 0.5,
// Grow while traveling
scaleY: 0.25,
alpha: 0
}, {
duration: 400,
// Slightly longer duration for better visibility
easing: tween.linear,
onFinish: function onFinish() {
game.removeChild(beam);
}
});
}
};
return self;
});
var Bubble = Container.expand(function (type, speed) {
var self = Container.call(this);
self.type = type || 'standard';
self.speed = speed || 2;
self.points = 1;
self.isActive = true;
var assetId = 'standardBubble';
if (self.type === 'bonus') {
assetId = 'bonusBubble';
self.points = 5;
} else if (self.type === 'bomb') {
assetId = 'bombBubble';
self.points = -10;
} else if (self.type === 'powerup') {
assetId = 'powerupBubble';
self.points = 2;
}
var bubbleGraphics = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5
});
if (self.type === 'bonus') {
bubbleGraphics.scale.set(1.2);
} else if (self.type === 'powerup') {
bubbleGraphics.scale.set(0.9);
}
// Add some interactivity by scaling bubble on press
self.down = function (x, y, obj) {
if (!self.isActive) {
return;
}
// Scale down bubble when pressed
tween(bubbleGraphics, {
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 100,
easing: tween.easeOut
});
self.pop();
};
self.pop = function () {
if (!self.isActive) {
return;
}
self.isActive = false;
// Play sound based on bubble type
if (self.type === 'bomb') {
LK.getSound('explosion').play();
} else if (self.type === 'bonus') {
LK.getSound('bonus').play();
} else if (self.type === 'powerup') {
LK.getSound('powerup').play();
} else {
LK.getSound('pop').play();
}
// Animation for popping
tween(bubbleGraphics, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
if (self.parent) {
self.parent.removeChild(self);
}
}
});
// Update score
LK.setScore(LK.getScore() + self.points);
// Handle special effects
if (self.type === 'bomb') {
// Flash screen red for bomb
LK.effects.flashScreen(0xe74c3c, 300);
} else if (self.type === 'powerup') {
// Slow down all bubbles
if (slowDownTimer) {
LK.clearTimeout(slowDownTimer);
}
bubbleSpeed = baseSpeed * 0.5;
slowDownTimer = LK.setTimeout(function () {
bubbleSpeed = baseSpeed;
}, 5000);
}
};
self.update = function () {
if (!self.isActive) {
return;
}
// Move bubble upward
self.y -= self.speed;
// Slight side-to-side movement
self.x += Math.sin(LK.ticks / 20 + self.id * 0.3) * 0.8;
// Remove if bubble goes off-screen
if (self.y < -150) {
// If bomb escapes screen, that's good
if (self.type === 'bomb') {
LK.setScore(LK.getScore() + 2);
}
// If it's a bonus and escapes, that's bad
else if (self.type === 'bonus') {
LK.setScore(LK.getScore() - 2);
}
if (self.parent) {
self.parent.removeChild(self);
}
}
};
return self;
});
var Shop = Container.expand(function () {
var self = Container.call(this);
// Create shop panel background
var panelBg = self.attachAsset('standardBubble', {
anchorX: 0.5,
anchorY: 0.5
});
// Stretch to make panel
panelBg.scale.set(10, 6);
panelBg.alpha = 0.9;
// Add title
var titleText = new Text2('Bubble Shop', {
size: 100,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0);
titleText.y = -300;
self.addChild(titleText);
// Add close button
var closeButton = new Text2('Close', {
size: 60,
fill: 0xFFFFFF
});
closeButton.anchor.set(0.5, 0.5);
closeButton.y = 300;
closeButton.interactive = true;
closeButton.down = function () {
closeShop();
};
self.addChild(closeButton);
// Add shop items
// Arrow item
var item1 = new Text2('Arrow: 100 pts', {
size: 60,
fill: 0xFFFFFF
});
item1.anchor.set(0.5, 0.5);
item1.y = -100; // Position above auto shooter
item1.interactive = true;
item1.down = function () {
// Check if player has enough points
if (LK.getScore() >= 100) {
// Deduct points
LK.setScore(LK.getScore() - 100);
// Create and position arrow at left middle of screen
var arrow = new Arrow();
arrow.x = 100; // Position at left edge
arrow.y = 2732 / 2; // Middle of screen height
game.addChild(arrow);
// Start the arrow's bubble popping behavior
arrow.startPopping();
// Visual feedback
tween(item1, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(item1, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 100,
easing: tween.easeIn
});
}
});
// Close shop after purchase
closeShop();
}
};
self.addChild(item1);
// Auto shooter item
var item3 = new Text2('Auto Shooter: 150 pts', {
size: 60,
fill: 0xFFFFFF
});
item3.anchor.set(0.5, 0.5);
item3.y = 0; // Centered position
item3.interactive = true;
item3.down = function () {
// Check if player has enough points
if (LK.getScore() >= 150) {
// Deduct points
LK.setScore(LK.getScore() - 150);
// Increment auto-shooter count
autoShooterCount++;
// Activate auto-shooter
activateAutoShooter();
// Visual feedback
tween(item3, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(item3, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 100,
easing: tween.easeIn
});
}
});
// Close shop after purchase
closeShop();
}
};
self.addChild(item3);
// Initialize with hidden state
self.visible = false;
return self;
});
var ShopButton = Container.expand(function () {
var self = Container.call(this);
// Create shop button visual
var buttonGraphics = self.attachAsset('standardBubble', {
anchorX: 0.5,
anchorY: 0.5
});
// Customize appearance
buttonGraphics.scale.set(1.2);
// Add text label
var buttonText = new Text2('Shop', {
size: 70,
fill: 0xFFFFFF
});
buttonText.anchor.set(0.5, 0.5);
self.addChild(buttonText);
// Handle click
self.down = function (x, y, obj) {
tween(buttonGraphics, {
scaleX: 0.7,
scaleY: 0.7
}, {
duration: 100,
easing: tween.easeOut
});
// Open shop
openShop();
};
self.up = function (x, y, obj) {
tween(buttonGraphics, {
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 100,
easing: tween.easeOut
});
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87CEEB
});
/****
* Game Code
****/
// Game variables
var bubbles = [];
var level = 1;
var baseSpeed = 2;
var bubbleSpeed = baseSpeed;
var spawnRate = 60; // Ticks between bubble spawns
var maxBubbles = 10;
var timer = 0;
var scoreDisplay;
var levelDisplay;
var slowDownTimer = null;
var bubbleIdCounter = 0;
var shopButton;
var shopPanel;
var isShopOpen = false;
var autoShooter;
var secondGun;
var autoShooterCount = 0;
var autoShooterActive = false;
var arrows = []; // Track arrows for management
// Auto-shooter functions
function activateAutoShooter() {
if (!autoShooterActive) {
autoShooterActive = true;
// Create auto-shooter if it doesn't exist
if (!autoShooter) {
// Create first gun
autoShooter = new AutoShooter();
autoShooter.x = 2048 - 100; // Position at right side of screen
autoShooter.y = 2732 / 2; // Position at vertical center
game.addChild(autoShooter);
// Check if we already have a second gun
if (!secondGun && autoShooterCount > 1) {
// Create second gun symmetrically on left side
secondGun = new AutoShooter();
secondGun.x = 100; // Position at left side of screen (symmetrical to first gun)
secondGun.y = 2732 / 2; // Position at vertical center
game.addChild(secondGun);
secondGun.activate();
}
}
// Activate it
autoShooter.activate();
}
}
function deactivateAutoShooter() {
if (autoShooterActive) {
autoShooterActive = false;
if (autoShooter) {
autoShooter.deactivate();
}
if (secondGun) {
secondGun.deactivate();
}
}
}
// Shop functions
function openShop() {
if (!isShopOpen) {
isShopOpen = true;
shopPanel.visible = true;
// Animate shop opening
shopPanel.scale.set(0.1);
tween(shopPanel, {
scaleX: 1,
scaleY: 1
}, {
duration: 300,
easing: tween.easeOut
});
}
}
function closeShop() {
if (isShopOpen) {
// Animate shop closing
tween(shopPanel, {
scaleX: 0.1,
scaleY: 0.1
}, {
duration: 300,
easing: tween.easeIn,
onFinish: function onFinish() {
shopPanel.visible = false;
isShopOpen = false;
}
});
}
}
// Set up UI
function setupUI() {
// Score display
scoreDisplay = new Text2('Score: 0', {
size: 80,
fill: 0xFFFFFF
});
scoreDisplay.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreDisplay);
// Level display
levelDisplay = new Text2('Level: ' + level, {
size: 80,
fill: 0xFFFFFF
});
levelDisplay.anchor.set(1, 0);
LK.gui.topLeft.addChild(levelDisplay);
// Position them properly
levelDisplay.x = -120;
levelDisplay.y = 120; // Keep away from top left corner
scoreDisplay.y = 20;
// Add shop button to top right
shopButton = new ShopButton();
shopButton.x = -100;
shopButton.y = 100;
LK.gui.topRight.addChild(shopButton);
// Create shop panel
shopPanel = new Shop();
shopPanel.visible = false;
LK.gui.center.addChild(shopPanel);
}
// Spawn a new bubble
function spawnBubble() {
if (bubbles.length >= maxBubbles) {
return;
}
var type = 'standard';
var rand = Math.random();
// Determine bubble type based on probability
if (rand < 0.05) {
type = 'bomb';
} else if (rand < 0.15) {
type = 'bonus';
} else if (rand < 0.20) {
type = 'powerup';
}
// Create bubble
var bubble = new Bubble(type, bubbleSpeed);
bubble.id = bubbleIdCounter++;
// Position at random X at bottom of screen
bubble.x = Math.random() * (2048 - 200) + 100;
bubble.y = 2732 + 100;
// Add bubble to game and array
game.addChild(bubble);
bubbles.push(bubble);
}
// Remove bubble from array
function removeBubble(bubble) {
var index = bubbles.indexOf(bubble);
if (index > -1) {
bubbles.splice(index, 1);
}
}
// Update game difficulty based on level
function updateDifficulty() {
baseSpeed = 2 + level * 0.5;
bubbleSpeed = baseSpeed;
spawnRate = Math.max(20, 60 - level * 5);
maxBubbles = 10 + level;
}
// Update level based on score
function checkLevelUp() {
var newLevel = Math.floor(LK.getScore() / 50) + 1;
if (newLevel > level) {
level = newLevel;
levelDisplay.setText('Level: ' + level);
updateDifficulty();
// Flash screen to indicate level up
LK.effects.flashScreen(0x2ecc71, 500);
}
}
// Initialize game
function initGame() {
LK.setScore(0);
level = 1;
timer = 0;
// No gameTime limit - infinite gameplay
bubbleSpeed = baseSpeed;
bubbleIdCounter = 0;
// Reset auto-shooter
if (autoShooter && autoShooter.parent) {
autoShooter.deactivate();
game.removeChild(autoShooter);
}
if (secondGun && secondGun.parent) {
secondGun.deactivate();
game.removeChild(secondGun);
}
autoShooter = null;
secondGun = null;
autoShooterCount = 0;
autoShooterActive = false;
// Clear any arrows
var allChildren = game.children.slice();
for (var i = 0; i < allChildren.length; i++) {
if (allChildren[i] instanceof Arrow) {
if (allChildren[i].timer) {
allChildren[i].stopPopping();
}
game.removeChild(allChildren[i]);
}
}
// Clear any existing bubbles
for (var i = bubbles.length - 1; i >= 0; i--) {
game.removeChild(bubbles[i]);
}
bubbles = [];
// Set up UI
setupUI();
// Update difficulty
updateDifficulty();
// Start background music
LK.playMusic('bgMusic', {
fade: {
start: 0,
end: 0.4,
duration: 1000
}
});
}
// No update timer function needed for infinite gameplay
// End game - will only be called manually when reaching score milestones
function endGame() {
if (LK.getScore() >= 100) {
LK.showYouWin();
} else {
LK.showGameOver();
}
}
// Initialize the game
initGame();
// Main game update function
game.update = function () {
timer++;
// Update score display
scoreDisplay.setText('Score: ' + LK.getScore());
// Check for level up
checkLevelUp();
// Spawn bubbles at regular intervals
if (timer % spawnRate === 0) {
spawnBubble();
}
// Clean up bubbles that are no longer in the scene
for (var i = bubbles.length - 1; i >= 0; i--) {
if (!bubbles[i].parent) {
removeBubble(bubbles[i]);
}
}
};
// Handle game clicks/taps
game.down = function (x, y, obj) {
// Just handle click/tap on the game background
// Bubbles have their own click handlers
};
// Handle mouse/touch movement
game.move = function (x, y, obj) {
// No implementation needed for this game
};