User prompt
Very nice, add more features
User prompt
add different features
User prompt
oyunun amacını ingilizce yaz küçük bir yazıyla ve arka plan değişsin her aşamada ve kontrol kolay olsun
User prompt
oyunun amacını başta belirt ve neon ışıklı yap herşeyi
User prompt
renklendir her yeri
Code edit (1 edits merged)
Please save this source code
User prompt
Color Catcher
Initial prompt
make a simple game
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Fruit: Apple (extra points) var Apple = Container.expand(function () { var self = Container.call(this); var appleAsset = self.attachAsset('ball', { anchorX: 0.5, anchorY: 0.5 }); appleAsset.tint = 0xff2222; // Red appleAsset.scaleX = 1.1; appleAsset.scaleY = 1.1; tween(appleAsset, { alpha: 0.85 }, { duration: 350, yoyo: true, repeat: Infinity, easing: tween.easeInOut }); var baseSpeed = 10 + Math.random() * 3; if (typeof LK !== "undefined" && typeof LK.getScore === "function") { baseSpeed += Math.min(8, Math.floor(LK.getScore() / 12)); } self.speed = baseSpeed; self.type = "apple"; self.update = function () { self.y += self.speed; }; return self; }); // Ball class var Ball = Container.expand(function () { var self = Container.call(this); var ballAsset = self.attachAsset('ball', { anchorX: 0.5, anchorY: 0.5 }); // Neon color palette for balls var colors = [0x00ffff, 0xff00ff, 0xffff00, 0x00ff00, 0xff8800, 0x00ffea, 0xff0088]; ballAsset.color = colors[Math.floor(Math.random() * colors.length)]; ballAsset.tint = ballAsset.color; // Neon glow effect: animate alpha for a pulsing glow tween(ballAsset, { alpha: 0.7 }, { duration: 400, yoyo: true, repeat: Infinity, easing: tween.easeInOut }); // Adjusted for human reflexes: start slower, increase with score, cap max speed var baseSpeed = 10 + Math.random() * 4; if (typeof LK !== "undefined" && typeof LK.getScore === "function") { baseSpeed += Math.min(10, Math.floor(LK.getScore() / 8)); // increase 1 per 8 points, max +10 } self.speed = baseSpeed; self.vx = (Math.random() - 0.5) * 10; // random horizontal speed self.update = function () { self.y += self.speed; self.x += self.vx; // Bounce off left/right walls if (self.x < 60 && self.vx < 0) { self.x = 60; self.vx *= -1; } if (self.x > 2048 - 60 && self.vx > 0) { self.x = 2048 - 60; self.vx *= -1; } }; return self; }); // Fruit: Banana (wider catch area) var Banana = Container.expand(function () { var self = Container.call(this); var bananaAsset = self.attachAsset('ball', { anchorX: 0.5, anchorY: 0.5 }); bananaAsset.tint = 0xfff700; // Yellow bananaAsset.scaleX = 1.5; bananaAsset.scaleY = 0.8; tween(bananaAsset, { alpha: 0.8 }, { duration: 400, yoyo: true, repeat: Infinity, easing: tween.easeInOut }); var baseSpeed = 9 + Math.random() * 2; if (typeof LK !== "undefined" && typeof LK.getScore === "function") { baseSpeed += Math.min(7, Math.floor(LK.getScore() / 14)); } self.speed = baseSpeed; self.type = "banana"; self.update = function () { self.y += self.speed; }; return self; }); // Basket class var Basket = Container.expand(function () { var self = Container.call(this); var basketAsset = self.attachAsset('basket', { anchorX: 0.5, anchorY: 0.5 }); // Neon basket: random neon color and glow var basketColors = [0x00ffff, 0xff00ff, 0xffff00, 0x00ff00, 0xff8800, 0x00ffea, 0xff0088]; basketAsset.tint = basketColors[Math.floor(Math.random() * basketColors.length)]; tween(basketAsset, { alpha: 0.7 }, { duration: 600, yoyo: true, repeat: Infinity, easing: tween.easeInOut }); return self; }); // Bomb class var Bomb = Container.expand(function () { var self = Container.call(this); var bombAsset = self.attachAsset('bomb', { anchorX: 0.5, anchorY: 0.5 }); // Neon bomb: magenta or cyan var bombColors = [0xff00ff, 0x00ffff, 0xffff00]; bombAsset.tint = bombColors[Math.floor(Math.random() * bombColors.length)]; tween(bombAsset, { alpha: 0.6 }, { duration: 400, yoyo: true, repeat: Infinity, easing: tween.easeInOut }); // Adjusted for human reflexes: start slower, increase with score, cap max speed var baseBombSpeed = 12 + Math.random() * 5; if (typeof LK !== "undefined" && typeof LK.getScore === "function") { baseBombSpeed += Math.min(12, Math.floor(LK.getScore() / 7)); // increase 1 per 7 points, max +12 } self.speed = baseBombSpeed; self.update = function () { self.y += self.speed; }; return self; }); // Fruit: Grape (slows bombs for a short time) var Grape = Container.expand(function () { var self = Container.call(this); var grapeAsset = self.attachAsset('ball', { anchorX: 0.5, anchorY: 0.5 }); grapeAsset.tint = 0x9900ff; // Purple grapeAsset.scaleX = 1.0; grapeAsset.scaleY = 1.0; tween(grapeAsset, { alpha: 0.7 }, { duration: 300, yoyo: true, repeat: Infinity, easing: tween.easeInOut }); var baseSpeed = 11 + Math.random() * 2; if (typeof LK !== "undefined" && typeof LK.getScore === "function") { baseSpeed += Math.min(7, Math.floor(LK.getScore() / 13)); } self.speed = baseSpeed; self.type = "grape"; self.update = function () { self.y += self.speed; }; return self; }); // PowerUp class (neon star) var PowerUp = Container.expand(function () { var self = Container.call(this); // Use a ball asset for now, but tint and scale to look like a power-up var starAsset = self.attachAsset('ball', { anchorX: 0.5, anchorY: 0.5 }); // Neon star color var starColors = [0xFFD700, 0x00ffea, 0xff00ff, 0x00ff00]; starAsset.tint = starColors[Math.floor(Math.random() * starColors.length)]; starAsset.scaleX = 1.3; starAsset.scaleY = 1.3; // Animate alpha for a pulsing glow tween(starAsset, { alpha: 0.8 }, { duration: 300, yoyo: true, repeat: Infinity, easing: tween.easeInOut }); // Adjusted for human reflexes: start slower, increase with score, cap max speed var basePowerSpeed = 9 + Math.random() * 3; if (typeof LK !== "undefined" && typeof LK.getScore === "function") { basePowerSpeed += Math.min(8, Math.floor(LK.getScore() / 10)); // increase 1 per 10 points, max +8 } self.speed = basePowerSpeed; self.type = "score"; // default type self.update = function () { self.y += self.speed; }; return self; }); // ShieldPowerUp class (neon blue ring) var ShieldPowerUp = Container.expand(function () { var self = Container.call(this); var shieldAsset = self.attachAsset('ball', { anchorX: 0.5, anchorY: 0.5 }); shieldAsset.tint = 0x00ffff; shieldAsset.scaleX = 1.5; shieldAsset.scaleY = 1.5; tween(shieldAsset, { alpha: 0.6 }, { duration: 200, yoyo: true, repeat: Infinity, easing: tween.easeInOut }); // Adjusted for human reflexes: start slower, increase with score, cap max speed var baseShieldSpeed = 8 + Math.random() * 2; if (typeof LK !== "undefined" && typeof LK.getScore === "function") { baseShieldSpeed += Math.min(7, Math.floor(LK.getScore() / 12)); // increase 1 per 12 points, max +7 } self.speed = baseShieldSpeed; self.type = "shield"; self.update = function () { self.y += self.speed; }; return self; }); // Fruit: Watermelon (big, gives shield) var Watermelon = Container.expand(function () { var self = Container.call(this); var melonAsset = self.attachAsset('ball', { anchorX: 0.5, anchorY: 0.5 }); melonAsset.tint = 0x22ff44; // Green melonAsset.scaleX = 1.7; melonAsset.scaleY = 1.3; tween(melonAsset, { alpha: 0.8 }, { duration: 500, yoyo: true, repeat: Infinity, easing: tween.easeInOut }); var baseSpeed = 8 + Math.random() * 2; if (typeof LK !== "undefined" && typeof LK.getScore === "function") { baseSpeed += Math.min(6, Math.floor(LK.getScore() / 15)); } self.speed = baseSpeed; self.type = "watermelon"; self.update = function () { self.y += self.speed; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000000 }); /**** * Game Code ****/ // Sound for missing a ball // Sound for catching a bomb // Sound for catching a ball // Bomb: black ellipse // Ball: colored ellipse // Basket: wide rectangle // Game area: 2048x2732 // No background image, only black background // No background image, only black background game.setBackgroundColor(0x000000); // Neon-style game objective text at the start (small, English, subtle neon) var objectiveTxt = new Text2('Goal: Catch the balls, avoid the bombs!', { size: 54, // smaller text fill: 0x00ffff, stroke: 0x222222, strokeThickness: 8, dropShadow: true, dropShadowColor: 0x00ffff, dropShadowBlur: 12, dropShadowDistance: 0, align: "center" }); objectiveTxt.anchor.set(0.5, 0); objectiveTxt.y = 90; LK.gui.top.addChild(objectiveTxt); // Add leaderboard button for competition var leaderboardBtn = new Text2('🏆 Leaderboard', { size: 60, fill: 0xFFD700, stroke: 0x00ffff, strokeThickness: 8, dropShadow: true, dropShadowColor: 0xFFD700, dropShadowBlur: 10, dropShadowDistance: 0, align: "center" }); leaderboardBtn.anchor.set(0.5, 0); leaderboardBtn.x = 2048 - 250; leaderboardBtn.y = 90; leaderboardBtn.interactive = true; leaderboardBtn.buttonMode = true; leaderboardBtn.down = function () { if (typeof LK.showLeaderboard === "function") { LK.showLeaderboard(); } }; LK.gui.top.addChild(leaderboardBtn); // Hide objective after 2.5 seconds LK.setTimeout(function () { objectiveTxt.visible = false; }, 2500); // Score text var scoreTxt = new Text2('0', { size: 120, fill: 0x00FFEA, stroke: 0xFF00FF, strokeThickness: 18, dropShadow: true, dropShadowColor: 0x00FFFF, dropShadowBlur: 24, dropShadowDistance: 0, align: "center" }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // Health bar UI var maxHealth = 3; var health = maxHealth; var healthBarWidth = 320; var healthBarHeight = 38; var healthBarBg = LK.getAsset('ball', { anchorX: 0, anchorY: 0.5, scaleX: healthBarWidth / 100, scaleY: healthBarHeight / 100, x: 120, y: 160 }); healthBarBg.tint = 0x222222; LK.gui.top.addChild(healthBarBg); var healthBar = LK.getAsset('ball', { anchorX: 0, anchorY: 0.5, scaleX: healthBarWidth / 100, scaleY: healthBarHeight / 100, x: 120, y: 160 }); healthBar.tint = 0x00ff00; LK.gui.top.addChild(healthBar); // Health icon hearts var healthHearts = []; for (var h = 0; h < maxHealth; h++) { var heart = LK.getAsset('ball', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.32, scaleY: 0.32, x: 120 + 40 + h * 54, y: 160 }); heart.tint = 0xff2222; LK.gui.top.addChild(heart); healthHearts.push(heart); } // Misses text (shows "Missed!" when a ball is missed) var missTxt = new Text2('', { size: 90, fill: 0x00FFFF, stroke: 0xFF00FF, strokeThickness: 14, dropShadow: true, dropShadowColor: 0x00FFFF, dropShadowBlur: 18, dropShadowDistance: 0, align: "center" }); missTxt.anchor.set(0.5, 0); LK.gui.top.addChild(missTxt); missTxt.visible = false; // Basket var basket = new Basket(); game.addChild(basket); basket.y = 2732 - 180; basket.x = 2048 / 2; // Ball, bomb, and power-up arrays var balls = []; var bombs = []; var powerups = []; // Dragging var dragNode = null; // Touch indicator for user-friendly feedback var touchIndicator = LK.getAsset('ball', { anchorX: 0.5, anchorY: 0.5 }); touchIndicator.tint = 0xFFD700; touchIndicator.scaleX = 0.4; touchIndicator.scaleY = 0.4; touchIndicator.alpha = 0; game.addChild(touchIndicator); // Spawning control var spawnTimer = 0; var spawnInterval = 55; // frames between spawns, will decrease as score increases // State var lastScore = 0; var gameOver = false; // Combo scoring var comboCount = 0; var comboTimeout = null; var comboTxt = new Text2('', { size: 80, fill: 0xFFD700, stroke: 0x00ffff, strokeThickness: 10, dropShadow: true, dropShadowColor: 0xFFD700, dropShadowBlur: 12, dropShadowDistance: 0, align: "center" }); comboTxt.anchor.set(0.5, 0); comboTxt.y = 200; comboTxt.visible = false; LK.gui.top.addChild(comboTxt); // Helper: spawn a ball, bomb, or power-up function spawnFallingObject() { // 80% ball, 20% bomb, 5% power-up, 3% shield power-up, plus fruits var rand = Math.random(); var x = 150 + Math.random() * (2048 - 300); if (rand < 0.10) { var bomb = new Bomb(); bomb.x = x; bomb.y = -60; bomb.lastIntersecting = false; // Add "-" indicator for bomb var minusTxt = new Text2('-', { size: 70, fill: 0xff2222, stroke: 0x000000, strokeThickness: 8, align: "center" }); minusTxt.anchor.set(0.5, 1); minusTxt.y = -60; bomb.addChild(minusTxt); bomb.indicatorTxt = minusTxt; bombs.push(bomb); game.addChild(bomb); } else if (rand < 0.13) { // 3% chance for super bomb var superBomb = new Bomb(); superBomb.x = x; superBomb.y = -60; superBomb.lastIntersecting = false; superBomb.isSuper = true; if (superBomb.children && superBomb.children.length > 0) { superBomb.children[0].tint = 0xffffff; } // Add "-" indicator for super bomb var minusTxt = new Text2('-', { size: 70, fill: 0xff2222, stroke: 0x000000, strokeThickness: 8, align: "center" }); minusTxt.anchor.set(0.5, 1); minusTxt.y = -60; superBomb.addChild(minusTxt); superBomb.indicatorTxt = minusTxt; bombs.push(superBomb); game.addChild(superBomb); } else if (rand > 0.98) { // 2% chance for slow motion power-up var slowPower = new PowerUp(); slowPower.x = x; slowPower.y = -60; slowPower.lastIntersecting = false; slowPower.type = "slow"; if (slowPower.children && slowPower.children.length > 0) { slowPower.children[0].tint = 0x3b6eea; } // Add "+" indicator for slow powerup var plusTxt = new Text2('+', { size: 70, fill: 0x00ffea, stroke: 0x000000, strokeThickness: 8, align: "center" }); plusTxt.anchor.set(0.5, 1); plusTxt.y = -60; slowPower.addChild(plusTxt); slowPower.indicatorTxt = plusTxt; powerups.push(slowPower); game.addChild(slowPower); } else if (rand > 0.96) { // 4% chance for shield power-up var shield = new ShieldPowerUp(); shield.x = x; shield.y = -60; shield.lastIntersecting = false; // Add "+" indicator for shield var plusTxt = new Text2('+', { size: 70, fill: 0x00ffff, stroke: 0x000000, strokeThickness: 8, align: "center" }); plusTxt.anchor.set(0.5, 1); plusTxt.y = -60; shield.addChild(plusTxt); shield.indicatorTxt = plusTxt; powerups.push(shield); game.addChild(shield); } else if (rand > 0.94) { // 2% chance for score power-up var powerup = new PowerUp(); powerup.x = x; powerup.y = -60; powerup.lastIntersecting = false; // Add "+" indicator for score powerup var plusTxt = new Text2('+', { size: 70, fill: 0xFFD700, stroke: 0x000000, strokeThickness: 8, align: "center" }); plusTxt.anchor.set(0.5, 1); plusTxt.y = -60; powerup.addChild(plusTxt); powerup.indicatorTxt = plusTxt; powerups.push(powerup); game.addChild(powerup); } else if (rand > 0.92) { // 2% chance for Watermelon (shield fruit) var watermelon = new Watermelon(); watermelon.x = x; watermelon.y = -60; watermelon.lastIntersecting = false; // Add "+" indicator for watermelon var plusTxt = new Text2('+', { size: 70, fill: 0x22ff44, stroke: 0x000000, strokeThickness: 8, align: "center" }); plusTxt.anchor.set(0.5, 1); plusTxt.y = -60; watermelon.addChild(plusTxt); watermelon.indicatorTxt = plusTxt; powerups.push(watermelon); game.addChild(watermelon); } else if (rand > 0.89) { // 3% chance for Grape (slows bombs) var grape = new Grape(); grape.x = x; grape.y = -60; grape.lastIntersecting = false; // Add "+" indicator for grape var plusTxt = new Text2('+', { size: 70, fill: 0x9900ff, stroke: 0x000000, strokeThickness: 8, align: "center" }); plusTxt.anchor.set(0.5, 1); plusTxt.y = -60; grape.addChild(plusTxt); grape.indicatorTxt = plusTxt; powerups.push(grape); game.addChild(grape); } else if (rand > 0.86) { // 3% chance for Banana (wider catch area) var banana = new Banana(); banana.x = x; banana.y = -60; banana.lastIntersecting = false; // Add "+" indicator for banana var plusTxt = new Text2('+', { size: 70, fill: 0xfff700, stroke: 0x000000, strokeThickness: 8, align: "center" }); plusTxt.anchor.set(0.5, 1); plusTxt.y = -60; banana.addChild(plusTxt); banana.indicatorTxt = plusTxt; powerups.push(banana); game.addChild(banana); } else if (rand > 0.83) { // 3% chance for Apple (extra points) var apple = new Apple(); apple.x = x; apple.y = -60; apple.lastIntersecting = false; // Add "+" indicator for apple var plusTxt = new Text2('+', { size: 70, fill: 0xff2222, stroke: 0x000000, strokeThickness: 8, align: "center" }); plusTxt.anchor.set(0.5, 1); plusTxt.y = -60; apple.addChild(plusTxt); apple.indicatorTxt = plusTxt; powerups.push(apple); game.addChild(apple); } else { var ball = new Ball(); ball.x = x; ball.y = -60; ball.lastIntersecting = false; // Add "+" indicator for normal ball var plusTxt = new Text2('+', { size: 70, fill: 0x00ffff, stroke: 0x000000, strokeThickness: 8, align: "center" }); plusTxt.anchor.set(0.5, 1); plusTxt.y = -60; ball.addChild(plusTxt); ball.indicatorTxt = plusTxt; balls.push(ball); game.addChild(ball); } } // Move handler for dragging basket function handleMove(x, y, obj) { if (dragNode) { // Clamp basket within screen var halfWidth = dragNode.width / 2; var minX = halfWidth; var maxX = 2048 - halfWidth; var newX = Math.max(minX, Math.min(maxX, x)); // Only update basket.x if it is not at the edge, or if moving away from the edge // This prevents the basket from "burning" or flashing at the edge if (!(dragNode.x <= minX && newX <= minX) && // not stuck at left edge !(dragNode.x >= maxX && newX >= maxX) // not stuck at right edge ) { dragNode.x = newX; } // Move touch indicator with finger touchIndicator.x = x; touchIndicator.y = y; touchIndicator.alpha = 0.7; } } // Touch/mouse events game.down = function (x, y, obj) { // Allow drag from anywhere in lower 1/3 of screen for easier control if (y > 2732 * 2 / 3) { dragNode = basket; handleMove(x, y, obj); // Show and animate touch indicator touchIndicator.x = x; touchIndicator.y = y; touchIndicator.alpha = 1; tween(touchIndicator, { alpha: 0.7, scaleX: 0.6, scaleY: 0.6 }, { duration: 120, yoyo: true, repeat: 1, onFinish: function onFinish() { // Keep indicator visible while dragging if (!dragNode) { touchIndicator.alpha = 0; touchIndicator.scaleX = 0.4; touchIndicator.scaleY = 0.4; } } }); } }; game.move = handleMove; game.up = function (x, y, obj) { dragNode = null; touchIndicator.alpha = 0; }; // Main update loop game.update = function () { if (gameOver) return; // Change background color at each 5 points (neon palette) var neonBgColors = [0x000000, 0x0ff0fc, 0x1a0033, 0x3b6eea, 0x00ffea, 0xff00ff, 0x00ff00, 0xf75e5e, 0xf7c325]; var score = LK.getScore(); var bgIndex = Math.floor(score / 5) % neonBgColors.length; game.setBackgroundColor(neonBgColors[bgIndex]); // Challenge mode: basket moves left/right automatically every 20 points if (score > 0 && score % 20 === 0 && !basket.challengeActive) { basket.challengeActive = true; basket.challengeDir = Math.random() > 0.5 ? 1 : -1; basket.challengeTween = tween(basket, { x: basket.challengeDir > 0 ? 2048 - basket.width / 2 : basket.width / 2 }, { duration: 2200, yoyo: true, repeat: 2, easing: tween.easeInOut, onFinish: function onFinish() { basket.challengeActive = false; } }); } // Spawn balls/bombs spawnTimer++; // Decrease interval as score increases (min 35 for human reflexes) var interval = Math.max(35, spawnInterval - Math.floor(LK.getScore() / 10) * 3); if (spawnTimer >= interval) { spawnFallingObject(); spawnTimer = 0; } // Speed up all balls every 10 points if (score > 0 && score % 10 === 0 && !game.lastSpeedupScore) { for (var si = 0; si < balls.length; si++) { balls[si].speed *= 1.08; // less aggressive speedup for human reflexes balls[si].vx *= 1.05; } game.lastSpeedupScore = score; } if (score % 10 !== 0) { game.lastSpeedupScore = null; } // Update balls // Use a local array to collect indices to remove, then remove after loop to avoid array shifting var ballsToRemove = []; for (var i = balls.length - 1; i >= 0; i--) { var ball = balls[i]; if (ball.lastY === undefined) ball.lastY = ball.y; if (ball.lastIntersecting === undefined) ball.lastIntersecting = false; ball.update(); // Check for catch var intersecting = ball.intersects(basket); if (!ball.lastIntersecting && intersecting) { // Caught! comboCount++; if (comboTimeout) { LK.clearTimeout(comboTimeout); comboTimeout = null; } if (comboCount > 1) { comboTxt.setText('Combo x' + comboCount + '!'); comboTxt.visible = true; comboTxt.alpha = 1; tween(comboTxt, { alpha: 0 }, { duration: 900, onFinish: function onFinish() { comboTxt.visible = false; } }); } // Combo bonus: +1 for first, +2 for 2nd, +3 for 3rd, etc. var comboBonus = comboCount > 1 ? comboCount : 1; LK.setScore(LK.getScore() + comboBonus); // Streak bonus: every 5 consecutive catches, +10 bonus if (comboCount > 0 && comboCount % 5 === 0) { LK.setScore(LK.getScore() + 10); comboTxt.setText('Streak! +10'); comboTxt.visible = true; comboTxt.alpha = 1; tween(comboTxt, { alpha: 0 }, { duration: 900, onFinish: function onFinish() { comboTxt.visible = false; } }); } // Edge catch bonus: if ball is caught at the left/right 20% of basket var basketLeft = basket.x - basket.width / 2; var basketRight = basket.x + basket.width / 2; if (ball.x < basketLeft + basket.width * 0.2 || ball.x > basketRight - basket.width * 0.2) { LK.setScore(LK.getScore() + 3); comboTxt.setText('Edge Catch! +3'); comboTxt.visible = true; comboTxt.alpha = 1; tween(comboTxt, { alpha: 0 }, { duration: 900, onFinish: function onFinish() { comboTxt.visible = false; } }); } scoreTxt.setText(LK.getScore()); LK.getSound('catch').play(); var flashColors = [0x4ad991, 0xf75e5e, 0x3b6eea, 0xf7a325, 0xFFD700]; LK.effects.flashScreen(flashColors[Math.floor(Math.random() * flashColors.length)], 200); LK.effects.flashObject(basket, 0x4ad991, 200); tween(ball, { y: basket.y, alpha: 0 }, { duration: 200, easing: tween.easeIn, onFinish: function (ballRef) { return function () { if (ballRef && ballRef.destroy) ballRef.destroy(); }; }(ball) }); ballsToRemove.push(i); // Reset combo if no catch in 1.2s comboTimeout = LK.setTimeout(function () { comboCount = 0; comboTxt.visible = false; comboTimeout = null; }, 1200); continue; } // Missed (goes below basket) if (ball.lastY < 2732 && ball.y >= 2732 - 80) { LK.getSound('miss').play(); missTxt.setText('Missed!'); missTxt.visible = true; comboCount = 0; if (comboTimeout) { LK.clearTimeout(comboTimeout); comboTimeout = null; } comboTxt.visible = false; LK.effects.flashScreen(0xff0000, 600); health = Math.max(0, health - 1); var healthFrac = health / maxHealth; healthBar.scaleX = healthBarWidth * healthFrac / 100; if (healthFrac > 0.66) { healthBar.tint = 0x00ff00; } else if (healthFrac > 0.33) { healthBar.tint = 0xffc700; } else { healthBar.tint = 0xff2222; } for (var hi = 0; hi < healthHearts.length; hi++) { healthHearts[hi].alpha = hi < health ? 1 : 0.25; healthHearts[hi].tint = hi < health ? 0xff2222 : 0x444444; } if (health <= 0) { LK.setTimeout(function () { LK.showGameOver(); // Show leaderboard with other players' scores after game over if (typeof LK.showLeaderboard === "function") { LK.showLeaderboard({ showOthers: true }); } }, 600); gameOver = true; return; } if (ball && ball.destroy) ball.destroy(); ballsToRemove.push(i); continue; } // Off screen (cleanup) if (ball.y > 2800) { if (ball && ball.destroy) ball.destroy(); ballsToRemove.push(i); continue; } ball.lastY = ball.y; ball.lastIntersecting = intersecting; } // Remove balls after loop to avoid array shifting // Remove from highest index to lowest to avoid shifting issues ballsToRemove.sort(function (a, b) { return b - a; }); for (var btr = 0; btr < ballsToRemove.length; btr++) { balls.splice(ballsToRemove[btr], 1); } // Update bombs var bombsToRemove = []; for (var j = bombs.length - 1; j >= 0; j--) { var bomb = bombs[j]; if (bomb.lastY === undefined) bomb.lastY = bomb.y; if (bomb.lastIntersecting === undefined) bomb.lastIntersecting = false; bomb.update(); // Check for catch var intersecting = bomb.intersects(basket); if (!bomb.lastIntersecting && intersecting) { if (basket.hasShield) { basket.hasShield = false; if (basket.shieldTween) { basket.shieldTween.stop(); basket.shieldTween = null; } LK.effects.flashObject(basket, 0x00ffff, 400); LK.effects.flashScreen(0x00ffff, 200); if (bomb.isSuper) { for (var bi = balls.length - 1; bi >= 0; bi--) { balls[bi].destroy(); } balls = []; LK.setScore(LK.getScore() + 15); scoreTxt.setText(LK.getScore()); missTxt.setText('Super Bomb! Cleared!'); missTxt.visible = true; if (missTxt.hideTimeout) { LK.clearTimeout(missTxt.hideTimeout); missTxt.hideTimeout = null; } missTxt.hideTimeout = LK.setTimeout(function () { missTxt.visible = false; missTxt.hideTimeout = null; }, 1200); } if (bomb && bomb.destroy) bomb.destroy(); bombsToRemove.push(j); continue; } LK.getSound('boom').play(); LK.effects.flashObject(basket, 0xff0000, 600); comboCount = 0; if (comboTimeout) { LK.clearTimeout(comboTimeout); comboTimeout = null; } comboTxt.visible = false; LK.effects.flashScreen(0x000000, 600); health = Math.max(0, health - 1); var healthFrac = health / maxHealth; healthBar.scaleX = healthBarWidth * healthFrac / 100; if (healthFrac > 0.66) { healthBar.tint = 0x00ff00; } else if (healthFrac > 0.33) { healthBar.tint = 0xffc700; } else { healthBar.tint = 0xff2222; } for (var hi = 0; hi < healthHearts.length; hi++) { healthHearts[hi].alpha = hi < health ? 1 : 0.25; healthHearts[hi].tint = hi < health ? 0xff2222 : 0x444444; } if (health <= 0) { LK.setTimeout(function () { LK.showGameOver(); if (typeof LK.showLeaderboard === "function") { LK.showLeaderboard({ showOthers: true }); } }, 600); gameOver = true; return; } if (bomb && bomb.destroy) bomb.destroy(); bombsToRemove.push(j); continue; } // Off screen (cleanup) if (bomb.y > 2800) { if (bomb && bomb.destroy) bomb.destroy(); bombsToRemove.push(j); continue; } bomb.lastY = bomb.y; bomb.lastIntersecting = intersecting; } // Remove bombs after loop // Remove from highest index to lowest to avoid shifting issues bombsToRemove.sort(function (a, b) { return b - a; }); for (var btr = 0; btr < bombsToRemove.length; btr++) { bombs.splice(bombsToRemove[btr], 1); } // Update power-ups var powerupsToRemove = []; for (var k = powerups.length - 1; k >= 0; k--) { var powerup = powerups[k]; if (powerup.lastY === undefined) powerup.lastY = powerup.y; if (powerup.lastIntersecting === undefined) powerup.lastIntersecting = false; powerup.update(); // Check for catch var intersecting = powerup.intersects(basket); if (!powerup.lastIntersecting && intersecting) { if (powerup.type === "shield" || powerup instanceof ShieldPowerUp || powerup instanceof Watermelon) { basket.hasShield = true; LK.effects.flashObject(basket, 0x00ffff, 400); LK.effects.flashScreen(0x00ffff, 200); if (!basket.shieldTween) { basket.shieldTween = tween(basket, { alpha: 1 }, { duration: 200, yoyo: true, repeat: Infinity, easing: tween.easeInOut }); } tween(powerup, { y: basket.y, alpha: 0 }, { duration: 200, easing: tween.easeIn, onFinish: function (puRef) { return function () { if (puRef && puRef.destroy) puRef.destroy(); }; }(powerup) }); powerupsToRemove.push(k); continue; } else if (powerup.type === "slow" || powerup instanceof Grape) { for (var bi = 0; bi < balls.length; bi++) { balls[bi].speed *= 0.4; balls[bi].vx *= 0.4; } for (var bj = 0; bj < bombs.length; bj++) { bombs[bj].speed *= 0.4; } for (var bp = 0; bp < powerups.length; bp++) { powerups[bp].speed *= 0.4; } missTxt.setText('Slow Motion!'); missTxt.visible = true; if (missTxt.hideTimeout) { LK.clearTimeout(missTxt.hideTimeout); missTxt.hideTimeout = null; } missTxt.hideTimeout = LK.setTimeout(function () { missTxt.visible = false; missTxt.hideTimeout = null; }, 1200); LK.setTimeout(function () { for (var bi = 0; bi < balls.length; bi++) { balls[bi].speed /= 0.4; balls[bi].vx /= 0.4; } for (var bj = 0; bj < bombs.length; bj++) { bombs[bj].speed /= 0.4; } for (var bp = 0; bp < powerups.length; bp++) { powerups[bp].speed /= 0.4; } }, 3000); tween(powerup, { y: basket.y, alpha: 0 }, { duration: 200, easing: tween.easeIn, onFinish: function (puRef) { return function () { if (puRef && puRef.destroy) puRef.destroy(); }; }(powerup) }); powerupsToRemove.push(k); continue; } else if (powerup.type === "banana" || powerup instanceof Banana) { var originalScale = basket.scaleX || 1; basket.scaleX = originalScale * 1.5; basket.scaleY = (basket.scaleY || 1) * 1.1; LK.effects.flashObject(basket, 0xfff700, 300); missTxt.setText('Wide Basket!'); missTxt.visible = true; if (missTxt.hideTimeout) { LK.clearTimeout(missTxt.hideTimeout); missTxt.hideTimeout = null; } missTxt.hideTimeout = LK.setTimeout(function () { missTxt.visible = false; missTxt.hideTimeout = null; }, 1200); LK.setTimeout(function () { basket.scaleX = originalScale; basket.scaleY = 1; }, 4000); tween(powerup, { y: basket.y, alpha: 0 }, { duration: 200, easing: tween.easeIn, onFinish: function (puRef) { return function () { if (puRef && puRef.destroy) puRef.destroy(); }; }(powerup) }); powerupsToRemove.push(k); continue; } else if (powerup.type === "apple" || powerup instanceof Apple) { LK.setScore(LK.getScore() + 5); scoreTxt.setText(LK.getScore()); LK.getSound('catch').play(); LK.effects.flashScreen(0xff2222, 350); LK.effects.flashObject(basket, 0xff2222, 300); missTxt.setText('Apple! +5'); missTxt.visible = true; if (missTxt.hideTimeout) { LK.clearTimeout(missTxt.hideTimeout); missTxt.hideTimeout = null; } missTxt.hideTimeout = LK.setTimeout(function () { missTxt.visible = false; missTxt.hideTimeout = null; }, 1200); tween(powerup, { y: basket.y, alpha: 0 }, { duration: 200, easing: tween.easeIn, onFinish: function (puRef) { return function () { if (puRef && puRef.destroy) puRef.destroy(); }; }(powerup) }); powerupsToRemove.push(k); continue; } else { LK.setScore(LK.getScore() + 2); scoreTxt.setText(LK.getScore()); LK.getSound('catch').play(); LK.effects.flashScreen(0xFFD700, 350); LK.effects.flashObject(basket, 0xFFD700, 300); tween(powerup, { y: basket.y, alpha: 0 }, { duration: 200, easing: tween.easeIn, onFinish: function (puRef) { return function () { if (puRef && puRef.destroy) puRef.destroy(); }; }(powerup) }); powerupsToRemove.push(k); continue; } } // Off screen (cleanup) if (powerup.y > 2800) { if (powerup && powerup.destroy) powerup.destroy(); powerupsToRemove.push(k); continue; } powerup.lastY = powerup.y; powerup.lastIntersecting = intersecting; } // Remove powerups after loop // Remove from highest index to lowest to avoid shifting issues powerupsToRemove.sort(function (a, b) { return b - a; }); for (var putr = 0; putr < powerupsToRemove.length; putr++) { powerups.splice(powerupsToRemove[putr], 1); } // Hide miss text after a short time if (missTxt.visible) { if (!missTxt.hideTimeout) { missTxt.hideTimeout = LK.setTimeout(function () { missTxt.visible = false; missTxt.hideTimeout = null; }, 900); } } // Draw shield icon above basket if shield is active if (basket.hasShield) { if (!basket.shieldIcon) { basket.shieldIcon = LK.getAsset('ball', { anchorX: 0.5, anchorY: 0.5 }); basket.shieldIcon.tint = 0x00ffff; basket.shieldIcon.scaleX = 0.5; basket.shieldIcon.scaleY = 0.5; game.addChild(basket.shieldIcon); } basket.shieldIcon.x = basket.x; basket.shieldIcon.y = basket.y - 70; basket.shieldIcon.visible = true; } else if (basket.shieldIcon) { basket.shieldIcon.visible = false; } // Rainbow basket after 40 points if (LK.getScore() >= 40 && basket.children && basket.children.length > 0) { var t = LK.ticks % 60 / 60; var r = Math.floor(127 * Math.sin(2 * Math.PI * t) + 128); var g = Math.floor(127 * Math.sin(2 * Math.PI * t + 2) + 128); var b = Math.floor(127 * Math.sin(2 * Math.PI * t + 4) + 128); basket.children[0].tint = r << 16 | g << 8 | b; } // Defensive: Always update lastX and lastY for basket to avoid undefined errors if (basket) { if (basket.lastX === undefined) basket.lastX = basket.x; if (basket.lastY === undefined) basket.lastY = basket.y; basket.lastX = basket.x; basket.lastY = basket.y; } }; // Reset state on new game LK.on('gameStart', function () { // Remove all balls, bombs, and powerups for (var i = 0; i < balls.length; i++) balls[i].destroy(); for (var j = 0; j < bombs.length; j++) bombs[j].destroy(); for (var k = 0; k < powerups.length; k++) powerups[k].destroy(); balls = []; bombs = []; powerups = []; basket.x = 2048 / 2; basket.y = 2732 - 180; // Reset challenge mode state basket.challengeActive = false; basket.challengeDir = null; if (basket.challengeTween) { basket.challengeTween.stop(); basket.challengeTween = null; } // Reset shield state basket.hasShield = false; if (basket.shieldTween) { basket.shieldTween.stop(); basket.shieldTween = null; } // Randomize basket color on new game var basketColors = [0x3b6eea, 0x4ad991, 0xf75e5e, 0xf7a325, 0xFFD700]; if (basket.children && basket.children.length > 0) { basket.children[0].tint = basketColors[Math.floor(Math.random() * basketColors.length)]; } LK.setScore(0); scoreTxt.setText('0'); missTxt.setText(''); missTxt.visible = false; if (missTxt.hideTimeout) { LK.clearTimeout(missTxt.hideTimeout); missTxt.hideTimeout = null; } spawnTimer = 0; gameOver = false; game.missForgiven = false; // Reset combo comboCount = 0; if (comboTimeout) { LK.clearTimeout(comboTimeout); comboTimeout = null; } comboTxt.visible = false; // Reset health bar and hearts health = maxHealth; healthBar.scaleX = healthBarWidth / 100; healthBar.tint = 0x00ff00; for (var hi = 0; hi < healthHearts.length; hi++) { healthHearts[hi].alpha = 1; healthHearts[hi].tint = 0xff2222; } });
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Fruit: Apple (extra points)
var Apple = Container.expand(function () {
var self = Container.call(this);
var appleAsset = self.attachAsset('ball', {
anchorX: 0.5,
anchorY: 0.5
});
appleAsset.tint = 0xff2222; // Red
appleAsset.scaleX = 1.1;
appleAsset.scaleY = 1.1;
tween(appleAsset, {
alpha: 0.85
}, {
duration: 350,
yoyo: true,
repeat: Infinity,
easing: tween.easeInOut
});
var baseSpeed = 10 + Math.random() * 3;
if (typeof LK !== "undefined" && typeof LK.getScore === "function") {
baseSpeed += Math.min(8, Math.floor(LK.getScore() / 12));
}
self.speed = baseSpeed;
self.type = "apple";
self.update = function () {
self.y += self.speed;
};
return self;
});
// Ball class
var Ball = Container.expand(function () {
var self = Container.call(this);
var ballAsset = self.attachAsset('ball', {
anchorX: 0.5,
anchorY: 0.5
});
// Neon color palette for balls
var colors = [0x00ffff, 0xff00ff, 0xffff00, 0x00ff00, 0xff8800, 0x00ffea, 0xff0088];
ballAsset.color = colors[Math.floor(Math.random() * colors.length)];
ballAsset.tint = ballAsset.color;
// Neon glow effect: animate alpha for a pulsing glow
tween(ballAsset, {
alpha: 0.7
}, {
duration: 400,
yoyo: true,
repeat: Infinity,
easing: tween.easeInOut
});
// Adjusted for human reflexes: start slower, increase with score, cap max speed
var baseSpeed = 10 + Math.random() * 4;
if (typeof LK !== "undefined" && typeof LK.getScore === "function") {
baseSpeed += Math.min(10, Math.floor(LK.getScore() / 8)); // increase 1 per 8 points, max +10
}
self.speed = baseSpeed;
self.vx = (Math.random() - 0.5) * 10; // random horizontal speed
self.update = function () {
self.y += self.speed;
self.x += self.vx;
// Bounce off left/right walls
if (self.x < 60 && self.vx < 0) {
self.x = 60;
self.vx *= -1;
}
if (self.x > 2048 - 60 && self.vx > 0) {
self.x = 2048 - 60;
self.vx *= -1;
}
};
return self;
});
// Fruit: Banana (wider catch area)
var Banana = Container.expand(function () {
var self = Container.call(this);
var bananaAsset = self.attachAsset('ball', {
anchorX: 0.5,
anchorY: 0.5
});
bananaAsset.tint = 0xfff700; // Yellow
bananaAsset.scaleX = 1.5;
bananaAsset.scaleY = 0.8;
tween(bananaAsset, {
alpha: 0.8
}, {
duration: 400,
yoyo: true,
repeat: Infinity,
easing: tween.easeInOut
});
var baseSpeed = 9 + Math.random() * 2;
if (typeof LK !== "undefined" && typeof LK.getScore === "function") {
baseSpeed += Math.min(7, Math.floor(LK.getScore() / 14));
}
self.speed = baseSpeed;
self.type = "banana";
self.update = function () {
self.y += self.speed;
};
return self;
});
// Basket class
var Basket = Container.expand(function () {
var self = Container.call(this);
var basketAsset = self.attachAsset('basket', {
anchorX: 0.5,
anchorY: 0.5
});
// Neon basket: random neon color and glow
var basketColors = [0x00ffff, 0xff00ff, 0xffff00, 0x00ff00, 0xff8800, 0x00ffea, 0xff0088];
basketAsset.tint = basketColors[Math.floor(Math.random() * basketColors.length)];
tween(basketAsset, {
alpha: 0.7
}, {
duration: 600,
yoyo: true,
repeat: Infinity,
easing: tween.easeInOut
});
return self;
});
// Bomb class
var Bomb = Container.expand(function () {
var self = Container.call(this);
var bombAsset = self.attachAsset('bomb', {
anchorX: 0.5,
anchorY: 0.5
});
// Neon bomb: magenta or cyan
var bombColors = [0xff00ff, 0x00ffff, 0xffff00];
bombAsset.tint = bombColors[Math.floor(Math.random() * bombColors.length)];
tween(bombAsset, {
alpha: 0.6
}, {
duration: 400,
yoyo: true,
repeat: Infinity,
easing: tween.easeInOut
});
// Adjusted for human reflexes: start slower, increase with score, cap max speed
var baseBombSpeed = 12 + Math.random() * 5;
if (typeof LK !== "undefined" && typeof LK.getScore === "function") {
baseBombSpeed += Math.min(12, Math.floor(LK.getScore() / 7)); // increase 1 per 7 points, max +12
}
self.speed = baseBombSpeed;
self.update = function () {
self.y += self.speed;
};
return self;
});
// Fruit: Grape (slows bombs for a short time)
var Grape = Container.expand(function () {
var self = Container.call(this);
var grapeAsset = self.attachAsset('ball', {
anchorX: 0.5,
anchorY: 0.5
});
grapeAsset.tint = 0x9900ff; // Purple
grapeAsset.scaleX = 1.0;
grapeAsset.scaleY = 1.0;
tween(grapeAsset, {
alpha: 0.7
}, {
duration: 300,
yoyo: true,
repeat: Infinity,
easing: tween.easeInOut
});
var baseSpeed = 11 + Math.random() * 2;
if (typeof LK !== "undefined" && typeof LK.getScore === "function") {
baseSpeed += Math.min(7, Math.floor(LK.getScore() / 13));
}
self.speed = baseSpeed;
self.type = "grape";
self.update = function () {
self.y += self.speed;
};
return self;
});
// PowerUp class (neon star)
var PowerUp = Container.expand(function () {
var self = Container.call(this);
// Use a ball asset for now, but tint and scale to look like a power-up
var starAsset = self.attachAsset('ball', {
anchorX: 0.5,
anchorY: 0.5
});
// Neon star color
var starColors = [0xFFD700, 0x00ffea, 0xff00ff, 0x00ff00];
starAsset.tint = starColors[Math.floor(Math.random() * starColors.length)];
starAsset.scaleX = 1.3;
starAsset.scaleY = 1.3;
// Animate alpha for a pulsing glow
tween(starAsset, {
alpha: 0.8
}, {
duration: 300,
yoyo: true,
repeat: Infinity,
easing: tween.easeInOut
});
// Adjusted for human reflexes: start slower, increase with score, cap max speed
var basePowerSpeed = 9 + Math.random() * 3;
if (typeof LK !== "undefined" && typeof LK.getScore === "function") {
basePowerSpeed += Math.min(8, Math.floor(LK.getScore() / 10)); // increase 1 per 10 points, max +8
}
self.speed = basePowerSpeed;
self.type = "score"; // default type
self.update = function () {
self.y += self.speed;
};
return self;
});
// ShieldPowerUp class (neon blue ring)
var ShieldPowerUp = Container.expand(function () {
var self = Container.call(this);
var shieldAsset = self.attachAsset('ball', {
anchorX: 0.5,
anchorY: 0.5
});
shieldAsset.tint = 0x00ffff;
shieldAsset.scaleX = 1.5;
shieldAsset.scaleY = 1.5;
tween(shieldAsset, {
alpha: 0.6
}, {
duration: 200,
yoyo: true,
repeat: Infinity,
easing: tween.easeInOut
});
// Adjusted for human reflexes: start slower, increase with score, cap max speed
var baseShieldSpeed = 8 + Math.random() * 2;
if (typeof LK !== "undefined" && typeof LK.getScore === "function") {
baseShieldSpeed += Math.min(7, Math.floor(LK.getScore() / 12)); // increase 1 per 12 points, max +7
}
self.speed = baseShieldSpeed;
self.type = "shield";
self.update = function () {
self.y += self.speed;
};
return self;
});
// Fruit: Watermelon (big, gives shield)
var Watermelon = Container.expand(function () {
var self = Container.call(this);
var melonAsset = self.attachAsset('ball', {
anchorX: 0.5,
anchorY: 0.5
});
melonAsset.tint = 0x22ff44; // Green
melonAsset.scaleX = 1.7;
melonAsset.scaleY = 1.3;
tween(melonAsset, {
alpha: 0.8
}, {
duration: 500,
yoyo: true,
repeat: Infinity,
easing: tween.easeInOut
});
var baseSpeed = 8 + Math.random() * 2;
if (typeof LK !== "undefined" && typeof LK.getScore === "function") {
baseSpeed += Math.min(6, Math.floor(LK.getScore() / 15));
}
self.speed = baseSpeed;
self.type = "watermelon";
self.update = function () {
self.y += self.speed;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Sound for missing a ball
// Sound for catching a bomb
// Sound for catching a ball
// Bomb: black ellipse
// Ball: colored ellipse
// Basket: wide rectangle
// Game area: 2048x2732
// No background image, only black background
// No background image, only black background
game.setBackgroundColor(0x000000);
// Neon-style game objective text at the start (small, English, subtle neon)
var objectiveTxt = new Text2('Goal: Catch the balls, avoid the bombs!', {
size: 54,
// smaller text
fill: 0x00ffff,
stroke: 0x222222,
strokeThickness: 8,
dropShadow: true,
dropShadowColor: 0x00ffff,
dropShadowBlur: 12,
dropShadowDistance: 0,
align: "center"
});
objectiveTxt.anchor.set(0.5, 0);
objectiveTxt.y = 90;
LK.gui.top.addChild(objectiveTxt);
// Add leaderboard button for competition
var leaderboardBtn = new Text2('🏆 Leaderboard', {
size: 60,
fill: 0xFFD700,
stroke: 0x00ffff,
strokeThickness: 8,
dropShadow: true,
dropShadowColor: 0xFFD700,
dropShadowBlur: 10,
dropShadowDistance: 0,
align: "center"
});
leaderboardBtn.anchor.set(0.5, 0);
leaderboardBtn.x = 2048 - 250;
leaderboardBtn.y = 90;
leaderboardBtn.interactive = true;
leaderboardBtn.buttonMode = true;
leaderboardBtn.down = function () {
if (typeof LK.showLeaderboard === "function") {
LK.showLeaderboard();
}
};
LK.gui.top.addChild(leaderboardBtn);
// Hide objective after 2.5 seconds
LK.setTimeout(function () {
objectiveTxt.visible = false;
}, 2500);
// Score text
var scoreTxt = new Text2('0', {
size: 120,
fill: 0x00FFEA,
stroke: 0xFF00FF,
strokeThickness: 18,
dropShadow: true,
dropShadowColor: 0x00FFFF,
dropShadowBlur: 24,
dropShadowDistance: 0,
align: "center"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Health bar UI
var maxHealth = 3;
var health = maxHealth;
var healthBarWidth = 320;
var healthBarHeight = 38;
var healthBarBg = LK.getAsset('ball', {
anchorX: 0,
anchorY: 0.5,
scaleX: healthBarWidth / 100,
scaleY: healthBarHeight / 100,
x: 120,
y: 160
});
healthBarBg.tint = 0x222222;
LK.gui.top.addChild(healthBarBg);
var healthBar = LK.getAsset('ball', {
anchorX: 0,
anchorY: 0.5,
scaleX: healthBarWidth / 100,
scaleY: healthBarHeight / 100,
x: 120,
y: 160
});
healthBar.tint = 0x00ff00;
LK.gui.top.addChild(healthBar);
// Health icon hearts
var healthHearts = [];
for (var h = 0; h < maxHealth; h++) {
var heart = LK.getAsset('ball', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.32,
scaleY: 0.32,
x: 120 + 40 + h * 54,
y: 160
});
heart.tint = 0xff2222;
LK.gui.top.addChild(heart);
healthHearts.push(heart);
}
// Misses text (shows "Missed!" when a ball is missed)
var missTxt = new Text2('', {
size: 90,
fill: 0x00FFFF,
stroke: 0xFF00FF,
strokeThickness: 14,
dropShadow: true,
dropShadowColor: 0x00FFFF,
dropShadowBlur: 18,
dropShadowDistance: 0,
align: "center"
});
missTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(missTxt);
missTxt.visible = false;
// Basket
var basket = new Basket();
game.addChild(basket);
basket.y = 2732 - 180;
basket.x = 2048 / 2;
// Ball, bomb, and power-up arrays
var balls = [];
var bombs = [];
var powerups = [];
// Dragging
var dragNode = null;
// Touch indicator for user-friendly feedback
var touchIndicator = LK.getAsset('ball', {
anchorX: 0.5,
anchorY: 0.5
});
touchIndicator.tint = 0xFFD700;
touchIndicator.scaleX = 0.4;
touchIndicator.scaleY = 0.4;
touchIndicator.alpha = 0;
game.addChild(touchIndicator);
// Spawning control
var spawnTimer = 0;
var spawnInterval = 55; // frames between spawns, will decrease as score increases
// State
var lastScore = 0;
var gameOver = false;
// Combo scoring
var comboCount = 0;
var comboTimeout = null;
var comboTxt = new Text2('', {
size: 80,
fill: 0xFFD700,
stroke: 0x00ffff,
strokeThickness: 10,
dropShadow: true,
dropShadowColor: 0xFFD700,
dropShadowBlur: 12,
dropShadowDistance: 0,
align: "center"
});
comboTxt.anchor.set(0.5, 0);
comboTxt.y = 200;
comboTxt.visible = false;
LK.gui.top.addChild(comboTxt);
// Helper: spawn a ball, bomb, or power-up
function spawnFallingObject() {
// 80% ball, 20% bomb, 5% power-up, 3% shield power-up, plus fruits
var rand = Math.random();
var x = 150 + Math.random() * (2048 - 300);
if (rand < 0.10) {
var bomb = new Bomb();
bomb.x = x;
bomb.y = -60;
bomb.lastIntersecting = false;
// Add "-" indicator for bomb
var minusTxt = new Text2('-', {
size: 70,
fill: 0xff2222,
stroke: 0x000000,
strokeThickness: 8,
align: "center"
});
minusTxt.anchor.set(0.5, 1);
minusTxt.y = -60;
bomb.addChild(minusTxt);
bomb.indicatorTxt = minusTxt;
bombs.push(bomb);
game.addChild(bomb);
} else if (rand < 0.13) {
// 3% chance for super bomb
var superBomb = new Bomb();
superBomb.x = x;
superBomb.y = -60;
superBomb.lastIntersecting = false;
superBomb.isSuper = true;
if (superBomb.children && superBomb.children.length > 0) {
superBomb.children[0].tint = 0xffffff;
}
// Add "-" indicator for super bomb
var minusTxt = new Text2('-', {
size: 70,
fill: 0xff2222,
stroke: 0x000000,
strokeThickness: 8,
align: "center"
});
minusTxt.anchor.set(0.5, 1);
minusTxt.y = -60;
superBomb.addChild(minusTxt);
superBomb.indicatorTxt = minusTxt;
bombs.push(superBomb);
game.addChild(superBomb);
} else if (rand > 0.98) {
// 2% chance for slow motion power-up
var slowPower = new PowerUp();
slowPower.x = x;
slowPower.y = -60;
slowPower.lastIntersecting = false;
slowPower.type = "slow";
if (slowPower.children && slowPower.children.length > 0) {
slowPower.children[0].tint = 0x3b6eea;
}
// Add "+" indicator for slow powerup
var plusTxt = new Text2('+', {
size: 70,
fill: 0x00ffea,
stroke: 0x000000,
strokeThickness: 8,
align: "center"
});
plusTxt.anchor.set(0.5, 1);
plusTxt.y = -60;
slowPower.addChild(plusTxt);
slowPower.indicatorTxt = plusTxt;
powerups.push(slowPower);
game.addChild(slowPower);
} else if (rand > 0.96) {
// 4% chance for shield power-up
var shield = new ShieldPowerUp();
shield.x = x;
shield.y = -60;
shield.lastIntersecting = false;
// Add "+" indicator for shield
var plusTxt = new Text2('+', {
size: 70,
fill: 0x00ffff,
stroke: 0x000000,
strokeThickness: 8,
align: "center"
});
plusTxt.anchor.set(0.5, 1);
plusTxt.y = -60;
shield.addChild(plusTxt);
shield.indicatorTxt = plusTxt;
powerups.push(shield);
game.addChild(shield);
} else if (rand > 0.94) {
// 2% chance for score power-up
var powerup = new PowerUp();
powerup.x = x;
powerup.y = -60;
powerup.lastIntersecting = false;
// Add "+" indicator for score powerup
var plusTxt = new Text2('+', {
size: 70,
fill: 0xFFD700,
stroke: 0x000000,
strokeThickness: 8,
align: "center"
});
plusTxt.anchor.set(0.5, 1);
plusTxt.y = -60;
powerup.addChild(plusTxt);
powerup.indicatorTxt = plusTxt;
powerups.push(powerup);
game.addChild(powerup);
} else if (rand > 0.92) {
// 2% chance for Watermelon (shield fruit)
var watermelon = new Watermelon();
watermelon.x = x;
watermelon.y = -60;
watermelon.lastIntersecting = false;
// Add "+" indicator for watermelon
var plusTxt = new Text2('+', {
size: 70,
fill: 0x22ff44,
stroke: 0x000000,
strokeThickness: 8,
align: "center"
});
plusTxt.anchor.set(0.5, 1);
plusTxt.y = -60;
watermelon.addChild(plusTxt);
watermelon.indicatorTxt = plusTxt;
powerups.push(watermelon);
game.addChild(watermelon);
} else if (rand > 0.89) {
// 3% chance for Grape (slows bombs)
var grape = new Grape();
grape.x = x;
grape.y = -60;
grape.lastIntersecting = false;
// Add "+" indicator for grape
var plusTxt = new Text2('+', {
size: 70,
fill: 0x9900ff,
stroke: 0x000000,
strokeThickness: 8,
align: "center"
});
plusTxt.anchor.set(0.5, 1);
plusTxt.y = -60;
grape.addChild(plusTxt);
grape.indicatorTxt = plusTxt;
powerups.push(grape);
game.addChild(grape);
} else if (rand > 0.86) {
// 3% chance for Banana (wider catch area)
var banana = new Banana();
banana.x = x;
banana.y = -60;
banana.lastIntersecting = false;
// Add "+" indicator for banana
var plusTxt = new Text2('+', {
size: 70,
fill: 0xfff700,
stroke: 0x000000,
strokeThickness: 8,
align: "center"
});
plusTxt.anchor.set(0.5, 1);
plusTxt.y = -60;
banana.addChild(plusTxt);
banana.indicatorTxt = plusTxt;
powerups.push(banana);
game.addChild(banana);
} else if (rand > 0.83) {
// 3% chance for Apple (extra points)
var apple = new Apple();
apple.x = x;
apple.y = -60;
apple.lastIntersecting = false;
// Add "+" indicator for apple
var plusTxt = new Text2('+', {
size: 70,
fill: 0xff2222,
stroke: 0x000000,
strokeThickness: 8,
align: "center"
});
plusTxt.anchor.set(0.5, 1);
plusTxt.y = -60;
apple.addChild(plusTxt);
apple.indicatorTxt = plusTxt;
powerups.push(apple);
game.addChild(apple);
} else {
var ball = new Ball();
ball.x = x;
ball.y = -60;
ball.lastIntersecting = false;
// Add "+" indicator for normal ball
var plusTxt = new Text2('+', {
size: 70,
fill: 0x00ffff,
stroke: 0x000000,
strokeThickness: 8,
align: "center"
});
plusTxt.anchor.set(0.5, 1);
plusTxt.y = -60;
ball.addChild(plusTxt);
ball.indicatorTxt = plusTxt;
balls.push(ball);
game.addChild(ball);
}
}
// Move handler for dragging basket
function handleMove(x, y, obj) {
if (dragNode) {
// Clamp basket within screen
var halfWidth = dragNode.width / 2;
var minX = halfWidth;
var maxX = 2048 - halfWidth;
var newX = Math.max(minX, Math.min(maxX, x));
// Only update basket.x if it is not at the edge, or if moving away from the edge
// This prevents the basket from "burning" or flashing at the edge
if (!(dragNode.x <= minX && newX <= minX) &&
// not stuck at left edge
!(dragNode.x >= maxX && newX >= maxX) // not stuck at right edge
) {
dragNode.x = newX;
}
// Move touch indicator with finger
touchIndicator.x = x;
touchIndicator.y = y;
touchIndicator.alpha = 0.7;
}
}
// Touch/mouse events
game.down = function (x, y, obj) {
// Allow drag from anywhere in lower 1/3 of screen for easier control
if (y > 2732 * 2 / 3) {
dragNode = basket;
handleMove(x, y, obj);
// Show and animate touch indicator
touchIndicator.x = x;
touchIndicator.y = y;
touchIndicator.alpha = 1;
tween(touchIndicator, {
alpha: 0.7,
scaleX: 0.6,
scaleY: 0.6
}, {
duration: 120,
yoyo: true,
repeat: 1,
onFinish: function onFinish() {
// Keep indicator visible while dragging
if (!dragNode) {
touchIndicator.alpha = 0;
touchIndicator.scaleX = 0.4;
touchIndicator.scaleY = 0.4;
}
}
});
}
};
game.move = handleMove;
game.up = function (x, y, obj) {
dragNode = null;
touchIndicator.alpha = 0;
};
// Main update loop
game.update = function () {
if (gameOver) return;
// Change background color at each 5 points (neon palette)
var neonBgColors = [0x000000, 0x0ff0fc, 0x1a0033, 0x3b6eea, 0x00ffea, 0xff00ff, 0x00ff00, 0xf75e5e, 0xf7c325];
var score = LK.getScore();
var bgIndex = Math.floor(score / 5) % neonBgColors.length;
game.setBackgroundColor(neonBgColors[bgIndex]);
// Challenge mode: basket moves left/right automatically every 20 points
if (score > 0 && score % 20 === 0 && !basket.challengeActive) {
basket.challengeActive = true;
basket.challengeDir = Math.random() > 0.5 ? 1 : -1;
basket.challengeTween = tween(basket, {
x: basket.challengeDir > 0 ? 2048 - basket.width / 2 : basket.width / 2
}, {
duration: 2200,
yoyo: true,
repeat: 2,
easing: tween.easeInOut,
onFinish: function onFinish() {
basket.challengeActive = false;
}
});
}
// Spawn balls/bombs
spawnTimer++;
// Decrease interval as score increases (min 35 for human reflexes)
var interval = Math.max(35, spawnInterval - Math.floor(LK.getScore() / 10) * 3);
if (spawnTimer >= interval) {
spawnFallingObject();
spawnTimer = 0;
}
// Speed up all balls every 10 points
if (score > 0 && score % 10 === 0 && !game.lastSpeedupScore) {
for (var si = 0; si < balls.length; si++) {
balls[si].speed *= 1.08; // less aggressive speedup for human reflexes
balls[si].vx *= 1.05;
}
game.lastSpeedupScore = score;
}
if (score % 10 !== 0) {
game.lastSpeedupScore = null;
}
// Update balls
// Use a local array to collect indices to remove, then remove after loop to avoid array shifting
var ballsToRemove = [];
for (var i = balls.length - 1; i >= 0; i--) {
var ball = balls[i];
if (ball.lastY === undefined) ball.lastY = ball.y;
if (ball.lastIntersecting === undefined) ball.lastIntersecting = false;
ball.update();
// Check for catch
var intersecting = ball.intersects(basket);
if (!ball.lastIntersecting && intersecting) {
// Caught!
comboCount++;
if (comboTimeout) {
LK.clearTimeout(comboTimeout);
comboTimeout = null;
}
if (comboCount > 1) {
comboTxt.setText('Combo x' + comboCount + '!');
comboTxt.visible = true;
comboTxt.alpha = 1;
tween(comboTxt, {
alpha: 0
}, {
duration: 900,
onFinish: function onFinish() {
comboTxt.visible = false;
}
});
}
// Combo bonus: +1 for first, +2 for 2nd, +3 for 3rd, etc.
var comboBonus = comboCount > 1 ? comboCount : 1;
LK.setScore(LK.getScore() + comboBonus);
// Streak bonus: every 5 consecutive catches, +10 bonus
if (comboCount > 0 && comboCount % 5 === 0) {
LK.setScore(LK.getScore() + 10);
comboTxt.setText('Streak! +10');
comboTxt.visible = true;
comboTxt.alpha = 1;
tween(comboTxt, {
alpha: 0
}, {
duration: 900,
onFinish: function onFinish() {
comboTxt.visible = false;
}
});
}
// Edge catch bonus: if ball is caught at the left/right 20% of basket
var basketLeft = basket.x - basket.width / 2;
var basketRight = basket.x + basket.width / 2;
if (ball.x < basketLeft + basket.width * 0.2 || ball.x > basketRight - basket.width * 0.2) {
LK.setScore(LK.getScore() + 3);
comboTxt.setText('Edge Catch! +3');
comboTxt.visible = true;
comboTxt.alpha = 1;
tween(comboTxt, {
alpha: 0
}, {
duration: 900,
onFinish: function onFinish() {
comboTxt.visible = false;
}
});
}
scoreTxt.setText(LK.getScore());
LK.getSound('catch').play();
var flashColors = [0x4ad991, 0xf75e5e, 0x3b6eea, 0xf7a325, 0xFFD700];
LK.effects.flashScreen(flashColors[Math.floor(Math.random() * flashColors.length)], 200);
LK.effects.flashObject(basket, 0x4ad991, 200);
tween(ball, {
y: basket.y,
alpha: 0
}, {
duration: 200,
easing: tween.easeIn,
onFinish: function (ballRef) {
return function () {
if (ballRef && ballRef.destroy) ballRef.destroy();
};
}(ball)
});
ballsToRemove.push(i);
// Reset combo if no catch in 1.2s
comboTimeout = LK.setTimeout(function () {
comboCount = 0;
comboTxt.visible = false;
comboTimeout = null;
}, 1200);
continue;
}
// Missed (goes below basket)
if (ball.lastY < 2732 && ball.y >= 2732 - 80) {
LK.getSound('miss').play();
missTxt.setText('Missed!');
missTxt.visible = true;
comboCount = 0;
if (comboTimeout) {
LK.clearTimeout(comboTimeout);
comboTimeout = null;
}
comboTxt.visible = false;
LK.effects.flashScreen(0xff0000, 600);
health = Math.max(0, health - 1);
var healthFrac = health / maxHealth;
healthBar.scaleX = healthBarWidth * healthFrac / 100;
if (healthFrac > 0.66) {
healthBar.tint = 0x00ff00;
} else if (healthFrac > 0.33) {
healthBar.tint = 0xffc700;
} else {
healthBar.tint = 0xff2222;
}
for (var hi = 0; hi < healthHearts.length; hi++) {
healthHearts[hi].alpha = hi < health ? 1 : 0.25;
healthHearts[hi].tint = hi < health ? 0xff2222 : 0x444444;
}
if (health <= 0) {
LK.setTimeout(function () {
LK.showGameOver();
// Show leaderboard with other players' scores after game over
if (typeof LK.showLeaderboard === "function") {
LK.showLeaderboard({
showOthers: true
});
}
}, 600);
gameOver = true;
return;
}
if (ball && ball.destroy) ball.destroy();
ballsToRemove.push(i);
continue;
}
// Off screen (cleanup)
if (ball.y > 2800) {
if (ball && ball.destroy) ball.destroy();
ballsToRemove.push(i);
continue;
}
ball.lastY = ball.y;
ball.lastIntersecting = intersecting;
}
// Remove balls after loop to avoid array shifting
// Remove from highest index to lowest to avoid shifting issues
ballsToRemove.sort(function (a, b) {
return b - a;
});
for (var btr = 0; btr < ballsToRemove.length; btr++) {
balls.splice(ballsToRemove[btr], 1);
}
// Update bombs
var bombsToRemove = [];
for (var j = bombs.length - 1; j >= 0; j--) {
var bomb = bombs[j];
if (bomb.lastY === undefined) bomb.lastY = bomb.y;
if (bomb.lastIntersecting === undefined) bomb.lastIntersecting = false;
bomb.update();
// Check for catch
var intersecting = bomb.intersects(basket);
if (!bomb.lastIntersecting && intersecting) {
if (basket.hasShield) {
basket.hasShield = false;
if (basket.shieldTween) {
basket.shieldTween.stop();
basket.shieldTween = null;
}
LK.effects.flashObject(basket, 0x00ffff, 400);
LK.effects.flashScreen(0x00ffff, 200);
if (bomb.isSuper) {
for (var bi = balls.length - 1; bi >= 0; bi--) {
balls[bi].destroy();
}
balls = [];
LK.setScore(LK.getScore() + 15);
scoreTxt.setText(LK.getScore());
missTxt.setText('Super Bomb! Cleared!');
missTxt.visible = true;
if (missTxt.hideTimeout) {
LK.clearTimeout(missTxt.hideTimeout);
missTxt.hideTimeout = null;
}
missTxt.hideTimeout = LK.setTimeout(function () {
missTxt.visible = false;
missTxt.hideTimeout = null;
}, 1200);
}
if (bomb && bomb.destroy) bomb.destroy();
bombsToRemove.push(j);
continue;
}
LK.getSound('boom').play();
LK.effects.flashObject(basket, 0xff0000, 600);
comboCount = 0;
if (comboTimeout) {
LK.clearTimeout(comboTimeout);
comboTimeout = null;
}
comboTxt.visible = false;
LK.effects.flashScreen(0x000000, 600);
health = Math.max(0, health - 1);
var healthFrac = health / maxHealth;
healthBar.scaleX = healthBarWidth * healthFrac / 100;
if (healthFrac > 0.66) {
healthBar.tint = 0x00ff00;
} else if (healthFrac > 0.33) {
healthBar.tint = 0xffc700;
} else {
healthBar.tint = 0xff2222;
}
for (var hi = 0; hi < healthHearts.length; hi++) {
healthHearts[hi].alpha = hi < health ? 1 : 0.25;
healthHearts[hi].tint = hi < health ? 0xff2222 : 0x444444;
}
if (health <= 0) {
LK.setTimeout(function () {
LK.showGameOver();
if (typeof LK.showLeaderboard === "function") {
LK.showLeaderboard({
showOthers: true
});
}
}, 600);
gameOver = true;
return;
}
if (bomb && bomb.destroy) bomb.destroy();
bombsToRemove.push(j);
continue;
}
// Off screen (cleanup)
if (bomb.y > 2800) {
if (bomb && bomb.destroy) bomb.destroy();
bombsToRemove.push(j);
continue;
}
bomb.lastY = bomb.y;
bomb.lastIntersecting = intersecting;
}
// Remove bombs after loop
// Remove from highest index to lowest to avoid shifting issues
bombsToRemove.sort(function (a, b) {
return b - a;
});
for (var btr = 0; btr < bombsToRemove.length; btr++) {
bombs.splice(bombsToRemove[btr], 1);
}
// Update power-ups
var powerupsToRemove = [];
for (var k = powerups.length - 1; k >= 0; k--) {
var powerup = powerups[k];
if (powerup.lastY === undefined) powerup.lastY = powerup.y;
if (powerup.lastIntersecting === undefined) powerup.lastIntersecting = false;
powerup.update();
// Check for catch
var intersecting = powerup.intersects(basket);
if (!powerup.lastIntersecting && intersecting) {
if (powerup.type === "shield" || powerup instanceof ShieldPowerUp || powerup instanceof Watermelon) {
basket.hasShield = true;
LK.effects.flashObject(basket, 0x00ffff, 400);
LK.effects.flashScreen(0x00ffff, 200);
if (!basket.shieldTween) {
basket.shieldTween = tween(basket, {
alpha: 1
}, {
duration: 200,
yoyo: true,
repeat: Infinity,
easing: tween.easeInOut
});
}
tween(powerup, {
y: basket.y,
alpha: 0
}, {
duration: 200,
easing: tween.easeIn,
onFinish: function (puRef) {
return function () {
if (puRef && puRef.destroy) puRef.destroy();
};
}(powerup)
});
powerupsToRemove.push(k);
continue;
} else if (powerup.type === "slow" || powerup instanceof Grape) {
for (var bi = 0; bi < balls.length; bi++) {
balls[bi].speed *= 0.4;
balls[bi].vx *= 0.4;
}
for (var bj = 0; bj < bombs.length; bj++) {
bombs[bj].speed *= 0.4;
}
for (var bp = 0; bp < powerups.length; bp++) {
powerups[bp].speed *= 0.4;
}
missTxt.setText('Slow Motion!');
missTxt.visible = true;
if (missTxt.hideTimeout) {
LK.clearTimeout(missTxt.hideTimeout);
missTxt.hideTimeout = null;
}
missTxt.hideTimeout = LK.setTimeout(function () {
missTxt.visible = false;
missTxt.hideTimeout = null;
}, 1200);
LK.setTimeout(function () {
for (var bi = 0; bi < balls.length; bi++) {
balls[bi].speed /= 0.4;
balls[bi].vx /= 0.4;
}
for (var bj = 0; bj < bombs.length; bj++) {
bombs[bj].speed /= 0.4;
}
for (var bp = 0; bp < powerups.length; bp++) {
powerups[bp].speed /= 0.4;
}
}, 3000);
tween(powerup, {
y: basket.y,
alpha: 0
}, {
duration: 200,
easing: tween.easeIn,
onFinish: function (puRef) {
return function () {
if (puRef && puRef.destroy) puRef.destroy();
};
}(powerup)
});
powerupsToRemove.push(k);
continue;
} else if (powerup.type === "banana" || powerup instanceof Banana) {
var originalScale = basket.scaleX || 1;
basket.scaleX = originalScale * 1.5;
basket.scaleY = (basket.scaleY || 1) * 1.1;
LK.effects.flashObject(basket, 0xfff700, 300);
missTxt.setText('Wide Basket!');
missTxt.visible = true;
if (missTxt.hideTimeout) {
LK.clearTimeout(missTxt.hideTimeout);
missTxt.hideTimeout = null;
}
missTxt.hideTimeout = LK.setTimeout(function () {
missTxt.visible = false;
missTxt.hideTimeout = null;
}, 1200);
LK.setTimeout(function () {
basket.scaleX = originalScale;
basket.scaleY = 1;
}, 4000);
tween(powerup, {
y: basket.y,
alpha: 0
}, {
duration: 200,
easing: tween.easeIn,
onFinish: function (puRef) {
return function () {
if (puRef && puRef.destroy) puRef.destroy();
};
}(powerup)
});
powerupsToRemove.push(k);
continue;
} else if (powerup.type === "apple" || powerup instanceof Apple) {
LK.setScore(LK.getScore() + 5);
scoreTxt.setText(LK.getScore());
LK.getSound('catch').play();
LK.effects.flashScreen(0xff2222, 350);
LK.effects.flashObject(basket, 0xff2222, 300);
missTxt.setText('Apple! +5');
missTxt.visible = true;
if (missTxt.hideTimeout) {
LK.clearTimeout(missTxt.hideTimeout);
missTxt.hideTimeout = null;
}
missTxt.hideTimeout = LK.setTimeout(function () {
missTxt.visible = false;
missTxt.hideTimeout = null;
}, 1200);
tween(powerup, {
y: basket.y,
alpha: 0
}, {
duration: 200,
easing: tween.easeIn,
onFinish: function (puRef) {
return function () {
if (puRef && puRef.destroy) puRef.destroy();
};
}(powerup)
});
powerupsToRemove.push(k);
continue;
} else {
LK.setScore(LK.getScore() + 2);
scoreTxt.setText(LK.getScore());
LK.getSound('catch').play();
LK.effects.flashScreen(0xFFD700, 350);
LK.effects.flashObject(basket, 0xFFD700, 300);
tween(powerup, {
y: basket.y,
alpha: 0
}, {
duration: 200,
easing: tween.easeIn,
onFinish: function (puRef) {
return function () {
if (puRef && puRef.destroy) puRef.destroy();
};
}(powerup)
});
powerupsToRemove.push(k);
continue;
}
}
// Off screen (cleanup)
if (powerup.y > 2800) {
if (powerup && powerup.destroy) powerup.destroy();
powerupsToRemove.push(k);
continue;
}
powerup.lastY = powerup.y;
powerup.lastIntersecting = intersecting;
}
// Remove powerups after loop
// Remove from highest index to lowest to avoid shifting issues
powerupsToRemove.sort(function (a, b) {
return b - a;
});
for (var putr = 0; putr < powerupsToRemove.length; putr++) {
powerups.splice(powerupsToRemove[putr], 1);
}
// Hide miss text after a short time
if (missTxt.visible) {
if (!missTxt.hideTimeout) {
missTxt.hideTimeout = LK.setTimeout(function () {
missTxt.visible = false;
missTxt.hideTimeout = null;
}, 900);
}
}
// Draw shield icon above basket if shield is active
if (basket.hasShield) {
if (!basket.shieldIcon) {
basket.shieldIcon = LK.getAsset('ball', {
anchorX: 0.5,
anchorY: 0.5
});
basket.shieldIcon.tint = 0x00ffff;
basket.shieldIcon.scaleX = 0.5;
basket.shieldIcon.scaleY = 0.5;
game.addChild(basket.shieldIcon);
}
basket.shieldIcon.x = basket.x;
basket.shieldIcon.y = basket.y - 70;
basket.shieldIcon.visible = true;
} else if (basket.shieldIcon) {
basket.shieldIcon.visible = false;
}
// Rainbow basket after 40 points
if (LK.getScore() >= 40 && basket.children && basket.children.length > 0) {
var t = LK.ticks % 60 / 60;
var r = Math.floor(127 * Math.sin(2 * Math.PI * t) + 128);
var g = Math.floor(127 * Math.sin(2 * Math.PI * t + 2) + 128);
var b = Math.floor(127 * Math.sin(2 * Math.PI * t + 4) + 128);
basket.children[0].tint = r << 16 | g << 8 | b;
}
// Defensive: Always update lastX and lastY for basket to avoid undefined errors
if (basket) {
if (basket.lastX === undefined) basket.lastX = basket.x;
if (basket.lastY === undefined) basket.lastY = basket.y;
basket.lastX = basket.x;
basket.lastY = basket.y;
}
};
// Reset state on new game
LK.on('gameStart', function () {
// Remove all balls, bombs, and powerups
for (var i = 0; i < balls.length; i++) balls[i].destroy();
for (var j = 0; j < bombs.length; j++) bombs[j].destroy();
for (var k = 0; k < powerups.length; k++) powerups[k].destroy();
balls = [];
bombs = [];
powerups = [];
basket.x = 2048 / 2;
basket.y = 2732 - 180;
// Reset challenge mode state
basket.challengeActive = false;
basket.challengeDir = null;
if (basket.challengeTween) {
basket.challengeTween.stop();
basket.challengeTween = null;
}
// Reset shield state
basket.hasShield = false;
if (basket.shieldTween) {
basket.shieldTween.stop();
basket.shieldTween = null;
}
// Randomize basket color on new game
var basketColors = [0x3b6eea, 0x4ad991, 0xf75e5e, 0xf7a325, 0xFFD700];
if (basket.children && basket.children.length > 0) {
basket.children[0].tint = basketColors[Math.floor(Math.random() * basketColors.length)];
}
LK.setScore(0);
scoreTxt.setText('0');
missTxt.setText('');
missTxt.visible = false;
if (missTxt.hideTimeout) {
LK.clearTimeout(missTxt.hideTimeout);
missTxt.hideTimeout = null;
}
spawnTimer = 0;
gameOver = false;
game.missForgiven = false;
// Reset combo
comboCount = 0;
if (comboTimeout) {
LK.clearTimeout(comboTimeout);
comboTimeout = null;
}
comboTxt.visible = false;
// Reset health bar and hearts
health = maxHealth;
healthBar.scaleX = healthBarWidth / 100;
healthBar.tint = 0x00ff00;
for (var hi = 0; hi < healthHearts.length; hi++) {
healthHearts[hi].alpha = 1;
healthHearts[hi].tint = 0xff2222;
}
});