/**** * 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;
}
});