User prompt
Make title screen and all UI 3x bigger and make it so the death message does pop up ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Make a logo that I can customize and the death messages don’t show up when I lose
User prompt
Make the death screen be able to say about 100 different and funny death messages such as “you died, oh wait, noooooo :(“
User prompt
Add a pause screen with the option of going back to the main menu
User prompt
Please fix the bug: 'Script error.' in or related to this line: 'LK.effects.flashObject(turret, 0xffffff, 200);' Line Number: 1023
User prompt
Fix everything related to bullet amount
User prompt
Please fix the bug: 'Script error.' in or related to this line: 'var velocity = turret.getShootVelocity();' Line Number: 986
User prompt
Please fix the bug: 'Script error.' in or related to this line: 'bullet.x = turret.x;' Line Number: 976
User prompt
Please fix the bug: 'Script error.' in or related to this line: 'shotsTxt.setText('Shots: ' + shotsRemaining);' Line Number: 968
User prompt
Please fix the bug: 'Script error.' in or related to this line: 'shotsTxt.setText('Shots: ' + shotsRemaining);' Line Number: 965
User prompt
Please fix the bug: 'Script error.' in or related to this line: 'shotsTxt.setText('Shots: ' + shotsRemaining);' Line Number: 965
User prompt
Please fix the bug: 'Script error.' in or related to this line: 'shotsTxt.setText('Shots: ' + shotsRemaining);' Line Number: 963
User prompt
Make baby mod literally impossible to lose and another difficulty called impossible mode where it is impossible to win do whatever in impossible. Also in baby if you lose make it say “aww you lost, that’s cute” and make it give you another life. Also add death screens and a title screen with a logo.
User prompt
Create baby mode which make the targets 5x larger and adds a bunch of powerups. Also code in a bunch of powerups and a lucky block that if you shoot it could give you a random one ↪💡 Consider importing and using the following plugins: @upit/tween.v1, @upit/storage.v1
User prompt
Please fix the bug: 'Script error.' in or related to this line: 'var power = Math.min(turret.maxPower, dragDistance / 50);' Line Number: 608
User prompt
Please fix the bug: 'Script error.' in or related to this line: 'turret.updateRotation(angle);' Line Number: 588
User prompt
Please fix the bug: 'Script error.' in or related to this line: 'var dx = x - turret.x;' Line Number: 557
User prompt
Please fix the bug: 'storage.getItem is not a function. (In 'storage.getItem('bulletBounce_difficulty')', 'storage.getItem' is undefined)' in or related to this line: 'var savedDifficulty = storage.getItem('bulletBounce_difficulty');' Line Number: 565 ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
Difficulty selection
User prompt
Make levels random and make the ball bigger
User prompt
Replace the ball with https://upit.com/#
Code edit (1 edits merged)
Please save this source code
User prompt
Bullet Bounce
Initial prompt
Gun. Do whatever you want with that.
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ var AimLine = Container.expand(function () { var self = Container.call(this); var line = self.attachAsset('aimLine', { anchorX: 0.5, anchorY: 0 }); self.update = function (angle, length) { line.rotation = angle; line.height = length; }; return self; }); var Bullet = Container.expand(function () { var self = Container.call(this); var bulletGraphics = self.attachAsset('ball', { anchorX: 0.5, anchorY: 0.5, rotation: 0 }); self.speedX = 0; self.speedY = 0; self.bounces = 0; self.isExplosive = false; // Special effect for explosive bullets if (typeof activePowerUps !== 'undefined' && activePowerUps.explosive) { bulletGraphics.tint = 0xFF0000; } // Set max bounces based on game difficulty var bouncesUpgrade = storage.bulletBounce_bouncesUpgrade || 0; var extraBounces = bouncesUpgrade * 3; // Each upgrade gives 3 extra bounces if (typeof gameDifficulty !== 'undefined') { switch (gameDifficulty) { case "easy": self.maxBounces = 10 + extraBounces; // More bounces for easy mode break; case "normal": self.maxBounces = 8 + extraBounces; break; case "hard": self.maxBounces = 5 + extraBounces; // Fewer bounces for hard mode break; default: self.maxBounces = 8 + extraBounces; } } else { self.maxBounces = 8 + extraBounces; // Default value } self.isActive = true; self.update = function () { if (!self.isActive) { return; } // Check for impossible mode - make bullets behave erratically if (typeof isImpossibleMode !== 'undefined' && isImpossibleMode) { // In impossible mode, bullets randomly change direction if (Math.random() < 0.05) { self.speedX = self.speedX * (0.8 + Math.random() * 0.4); self.speedY = self.speedY * (0.8 + Math.random() * 0.4); // Occasionally make a sharp turn if (Math.random() < 0.1) { self.speedX *= -1; } } } // Store previous position for trail effect if (!self.prevX) { self.prevX = self.x; self.prevY = self.y; self.trail = []; } // Apply velocity self.x += self.speedX; self.y += self.speedY; // Create trail effect if (Math.abs(self.x - self.prevX) > 5 || Math.abs(self.y - self.prevY) > 5) { // Create a trail particle var trailParticle = LK.getAsset('ball', { anchorX: 0.5, anchorY: 0.5, x: self.prevX, y: self.prevY, scaleX: 0.3, scaleY: 0.3, alpha: 0.5 }); if (self.isExplosive) { trailParticle.tint = 0xFF0000; } game.addChild(trailParticle); self.trail.push(trailParticle); // Fade out trail particle tween(trailParticle, { alpha: 0, scaleX: 0.1, scaleY: 0.1 }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { game.removeChild(trailParticle); // Remove from trail array var index = self.trail.indexOf(trailParticle); if (index > -1) { self.trail.splice(index, 1); } } }); // Store current position as previous for next trail self.prevX = self.x; self.prevY = self.y; } // Apply a small rotation for visual effect bulletGraphics.rotation += 0.05; // Check for wall collisions if (self.x < 30 || self.x > 2018) { self.speedX *= -0.9; // Lose some energy on bounce if (self.x < 30) { self.x = 30; } if (self.x > 2018) { self.x = 2018; } self.bounces++; LK.getSound('bounce').play(); } if (self.y < 30 || self.y > 2702) { self.speedY *= -0.9; // Lose some energy on bounce if (self.y < 30) { self.y = 30; } if (self.y > 2702) { self.y = 2702; } self.bounces++; LK.getSound('bounce').play(); } // Check if bullet should be removed if (self.bounces >= self.maxBounces || Math.abs(self.speedX) < 0.5 && Math.abs(self.speedY) < 0.5) { self.isActive = false; } }; return self; }); var DeathScreen = Container.expand(function () { var self = Container.call(this); var background = self.attachAsset('centerCircle', { anchorX: 0.5, anchorY: 0.5, width: 1000, height: 700, tint: 0x000000, alpha: 0.8 }); // Array of funny death messages self.deathMessages = ["GAME OVER... but mainly skill issue", "YOU DIED! Better luck in your next life!", "FAILURE COMPLETE! Achievement unlocked?", "RIP: Rest In Pieces", "OBLITERATED! That's gotta hurt!", "GAME OVER! Your bullets miss you already", "YOU'RE TOAST! Buttered on both sides", "K.O.! Flawless victory... for the targets", "WASTED! Grand Theft Auto would be proud", "YOU DIED! Dark Souls veterans: 'First time?'", "DEFEATED! By inanimate objects...", "TERMINATED! You'll be back, right?", "CATASTROPHIC FAILURE! NASA is impressed", "CRUSHED! Like your hopes and dreams", "GAME OVER! Have you tried turning it off and on again?", "ANNIHILATED! That's one way to exit", "YOU LOSE! But the real treasure was the bullets you wasted along the way", "EXPIRED! Like yesterday's milk", "ELIMINATED! Nothing personal, kid", "DISCONNECTED! From reality and the game", "DOOMED! Not even the Slayer could save you", "SHUTDOWN! Error: Skills not found", "PULVERIZED! Like a blender on max setting", "DESTROYED! But so is your self-esteem", "WRECKED! Like that test you didn't study for", "ERASED! Ctrl+Z won't save you now", "LIQUIDATED! Assets and all", "ERADICATED! Not even a microbe survived", "DEVASTATED! Just like your high score dreams", "DEMOLISHED! We hardly knew ye", "SHATTERED! Like your confidence", "VAPORIZED! Not even ashes remain", "DELETED! From the game and our hearts", "EXTINGUISHED! Your flame went out", "FALLEN! And you can't get up", "SQUASHED! Like a bug on a windshield", "CRUSHED! Under the weight of your mistakes", "DISMANTLED! Like furniture from IKEA", "DISSOLVED! In a pool of tears", "DISPATCHED! To the shadow realm", "BANISHED! To the void of losers", "EXECUTED! With extreme prejudice", "NULLIFIED! void player = null;", "DEACTIVATED! Sleep mode engaged", "TERMINATED! With extreme prejudice", "CRASHED! Like Windows Vista", "FLATLINED! Call the medic!... wait, too late", "WIPED OUT! Not even a stain remains", "DISINTEGRATED! Atoms scattered to the wind", "ERODED! Time wasn't on your side", "EXPIRED! Your warranty was up", "RECYCLED! At least you're eco-friendly", "DISCARDED! In the trash bin of history", "OVERTHROWN! The rebellion was successful", "DERAILED! Like your train of thought", "ABANDONED! Left to die in the wilderness", "VANQUISHED! The dragon wins this time", "RETIRED! Forced early retirement", "DISCONTINUED! No longer in production", "SCRAPPED! Back to the drawing board", "PURGED! From the system", "UNPLUGGED! No power to continue", "ARCHIVED! Filed under 'F' for 'Failure'", "DEBUNKED! Your skills were just a myth", "DETHRONED! Your reign is over", "DISCHARGED! Dishonorable, of course", "EXILED! Never to return", "FORECLOSED! Should have paid your skill mortgage", "IMPLODED! Collapsed under pressure", "OBSOLETE! Time for an upgrade", "OUTPLAYED! By rectangles...", "OVERWRITTEN! Your save file is gone", "RECALLED! Due to defects", "REJECTED! Application denied", "REPOSSESSED! Should've made those skill payments", "RESCINDED! Your license to play", "REVOKED! Your gaming privileges", "SACKED! Clean out your locker", "SCRAPPED! Parts may be salvageable", "SUPERSEDED! By a better player", "SUPERSEDED! By literally anyone else", "SUSPENDED! Pending investigation", "TORPEDOED! Abandon ship!", "VOIDED! Like your warranty", "ZAPPED! Extra crispy", "ZOMBIFIED! Braaaains... you need some", "THANOS SNAPPED! You don't feel so good...", "CTRL+ALT+DEFEATED! Forced shutdown", "BLUE SCREENED! Fatal player error", "LAG SPIKED! Connection to skills lost", "UNFRIENDED! Game doesn't like you", "GHOSTED! The game left you on read", "YEETED! Into the nearest sun", "STONKS DOWN! Your performance is bearish", "SENT TO THE RANCH! Dr. Phil approved", "RAGE QUIT! Oh wait, you actually lost", "ALT+F4'd! The easy way out", "404'd! Player skill not found", "UNSUBSCRIBED! From the living channel", "DOWNGRADED! To casual status", "FACTORY RESET! All progress lost", "UNINSTALLED! System32\\Player\\Skills.exe not found"]; var messageText = new Text2("GAME OVER", { size: 250, fill: 0xFF0000 }); messageText.anchor.set(0.5, 0.5); messageText.y = -250; self.addChild(messageText); var babyMessage = new Text2("Aww you lost, that's cute!", { size: 125, fill: 0xFF55FF }); babyMessage.anchor.set(0.5, 0.5); babyMessage.y = 50; babyMessage.visible = false; // Only shown in baby mode self.addChild(babyMessage); var restartButton = new Container(); var buttonBg = restartButton.attachAsset('powerMeter', { anchorX: 0.5, anchorY: 0.5, width: 750, height: 250, tint: 0x22cc22 }); var buttonText = new Text2("RETRY", { size: 150, fill: 0xFFFFFF }); buttonText.anchor.set(0.5, 0.5); restartButton.addChild(buttonText); restartButton.y = 400; self.addChild(restartButton); // Add button interaction restartButton.down = function () { if (isBabyMode) { // In baby mode, give an extra life instead of game over self.visible = false; shotsRemaining += 1; if (shotsTxt && typeof shotsTxt !== 'undefined') { shotsTxt.setText('Shots: ' + shotsRemaining); } // Spawn a few powerups to help for (var i = 0; i < 2; i++) { createPowerUp(turret.x + 100 + i * 100, turret.y - 100 - i * 50); } } else { LK.showGameOver(); } }; self.showMessage = function (isBaby) { babyMessage.visible = isBaby; self.visible = true; // Always show the default message "GAME OVER" messageText.setText("GAME OVER"); // Animate death screen appearance self.alpha = 0; tween(self, { alpha: 1 }, { duration: 500, easing: tween.easeOut }); }; return self; }); var Difficulty = Container.expand(function () { var self = Container.call(this); // Button base size and spacing var buttonWidth = 750; var buttonHeight = 250; var spacing = 75; // Create title var titleText = new Text2('SELECT DIFFICULTY', { size: 200, fill: 0xFFFFFF }); titleText.anchor.set(0.5, 0); titleText.y = -600; self.addChild(titleText); // Create difficulty buttons var easyButton = createButton('EASY', -buttonWidth / 2 - spacing / 2, -250, 0x44AA44); var normalButton = createButton('NORMAL', buttonWidth / 2 + spacing / 2, -250, 0x4477AA); var hardButton = createButton('HARD', -buttonWidth / 2 - spacing / 2, 75, 0xAA4444); var babyButton = createButton('BABY', buttonWidth / 2 + spacing / 2, 75, 0xFF55FF); var impossibleButton = createButton('IMPOSSIBLE', 0, 400, 0x000000); self.addChild(easyButton); self.addChild(normalButton); self.addChild(hardButton); self.addChild(babyButton); self.addChild(impossibleButton); function createButton(text, xPos, yPos, color) { var button = new Container(); // Button background var bg = button.attachAsset('powerMeter', { anchorX: 0.5, anchorY: 0.5, width: buttonWidth, height: buttonHeight, tint: color }); // Button text var txt = new Text2(text, { size: 125, fill: 0xFFFFFF }); txt.anchor.set(0.5, 0.5); button.addChild(txt); // Position button button.x = xPos; button.y = yPos; // Add interaction button.down = function () { selectDifficulty(text.toLowerCase()); }; return button; } function selectDifficulty(level) { // Set difficulty and hide selector gameDifficulty = level; self.visible = false; // Set mode flags isBabyMode = level === "baby"; isImpossibleMode = level === "impossible"; // Save selected difficulty storage.bulletBounce_difficulty = level; // Start the game with selected difficulty initGame(); } return self; }); var LuckyBlock = Container.expand(function () { var self = Container.call(this); var blockGraphics = self.attachAsset('luckyBlock', { anchorX: 0.5, anchorY: 0.5 }); // Question mark text var questionMark = new Text2("?", { size: 50, fill: 0x000000 }); questionMark.anchor.set(0.5, 0.5); self.addChild(questionMark); self.isHit = false; // Add visual effect - rotating self.update = function () { questionMark.rotation = Math.sin(LK.ticks * 0.05) * 0.2; blockGraphics.rotation = Math.sin(LK.ticks * 0.03) * 0.1; }; self.hit = function () { if (self.isHit) { return; } self.isHit = true; LK.effects.flashObject(self, 0xffffff, 300); // Animation for breaking tween(blockGraphics, { alpha: 0, scaleX: 1.5, scaleY: 1.5 }, { duration: 500, easing: tween.easeOut }); tween(questionMark, { alpha: 0, scaleX: 0.2, scaleY: 0.2 }, { duration: 500, easing: tween.easeOut, onFinish: function onFinish() { // Spawn random powerup spawnRandomPowerUp(self.x, self.y); self.visible = false; } }); }; return self; }); var PauseMenu = Container.expand(function () { var self = Container.call(this); // Background overlay var overlay = self.attachAsset('centerCircle', { anchorX: 0.5, anchorY: 0.5, width: 2048, height: 2732, tint: 0x000000, alpha: 0.7 }); // Pause menu container var menuContainer = new Container(); menuContainer.x = 0; menuContainer.y = 0; self.addChild(menuContainer); // Pause title var titleText = new Text2("PAUSED", { size: 250, fill: 0xFFFFFF }); titleText.anchor.set(0.5, 0.5); titleText.y = -375; menuContainer.addChild(titleText); // Resume button var resumeButton = new Container(); var resumeBg = resumeButton.attachAsset('powerMeter', { anchorX: 0.5, anchorY: 0.5, width: 750, height: 250, tint: 0x22cc22 }); var resumeText = new Text2("RESUME", { size: 150, fill: 0xFFFFFF }); resumeText.anchor.set(0.5, 0.5); resumeButton.addChild(resumeText); resumeButton.y = 0; menuContainer.addChild(resumeButton); // Main menu button var menuButton = new Container(); var menuBg = menuButton.attachAsset('powerMeter', { anchorX: 0.5, anchorY: 0.5, width: 750, height: 250, tint: 0x3355ff }); var menuText = new Text2("MAIN MENU", { size: 150, fill: 0xFFFFFF }); menuText.anchor.set(0.5, 0.5); menuButton.addChild(menuText); menuButton.y = 375; menuContainer.addChild(menuButton); // Button interactions resumeButton.down = function () { self.hide(); }; menuButton.down = function () { self.hide(); if (titleScreen) { titleScreen.visible = true; } // Reset game state clearLevel(); // Hide all game elements if (turret) { turret.visible = false; } if (aimLine) { aimLine.visible = false; } if (powerMeter) { powerMeter.visible = false; } if (shotsTxt) { shotsTxt.visible = false; } if (levelTxt) { levelTxt.visible = false; } }; // Show and hide methods self.show = function () { self.visible = true; // Animate appearance self.alpha = 0; tween(self, { alpha: 1 }, { duration: 300, easing: tween.easeOut }); }; self.hide = function () { // Animate disappearance tween(self, { alpha: 0 }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { self.visible = false; gamePaused = false; // Resume game when pause menu is hidden } }); }; return self; }); var PowerMeter = Container.expand(function () { var self = Container.call(this); var meterBg = self.attachAsset('powerMeter', { anchorX: 0, anchorY: 0.5, tint: 0x444444 }); var meterFill = self.attachAsset('powerMeter', { anchorX: 0, anchorY: 0.5, scaleX: 0.5 }); self.setPower = function (power, maxPower) { meterFill.scaleX = power / maxPower; }; return self; }); var PowerUp = Container.expand(function () { var self = Container.call(this); self.type = ""; self.isCollected = false; // Define powerup sizes and colors var types = { "multishot": { color: 0xff5500, text: "3x" }, "bigball": { color: 0x00aaff, text: "BIG" }, "slowmotion": { color: 0xaa00ff, text: "SLOW" }, "extralife": { color: 0xff0055, text: "LIFE" }, "explosive": { color: 0xff0000, text: "BOOM" } }; // Background circle var bg = self.attachAsset('centerCircle', { anchorX: 0.5, anchorY: 0.5, width: 60, height: 60 }); // Text label var label = new Text2("", { size: 25, fill: 0xFFFFFF }); label.anchor.set(0.5, 0.5); self.addChild(label); self.setType = function (powerupType) { self.type = powerupType; bg.tint = types[powerupType].color; label.setText(types[powerupType].text); // Add visual effect - pulsing self.update = function () { var pulse = Math.sin(LK.ticks * 0.05) * 0.1 + 1; bg.scale.set(pulse, pulse); }; }; self.collect = function () { if (self.isCollected) { return; } self.isCollected = true; LK.effects.flashObject(self, 0xffffff, 300); // Animation for collection tween(self, { alpha: 0, scaleX: 0.2, scaleY: 0.2 }, { duration: 500, easing: tween.easeOut, onFinish: function onFinish() { self.visible = false; } }); // Apply powerup effect based on type applyPowerUpEffect(self.type); }; self.down = function () { // Allow clicking on powerups too (mobile friendly) self.collect(); }; return self; }); var Shop = Container.expand(function () { var self = Container.call(this); // Background overlay var overlay = self.attachAsset('centerCircle', { anchorX: 0.5, anchorY: 0.5, width: 2048, height: 2732, tint: 0x000000, alpha: 0.8 }); // Shop container var shopContainer = new Container(); shopContainer.x = 0; shopContainer.y = 0; self.addChild(shopContainer); // Shop title var titleText = new Text2("SHOP", { size: 250, fill: 0xFFFFFF }); titleText.anchor.set(0.5, 0.5); titleText.y = -750; shopContainer.addChild(titleText); // Available currency var currencyText = new Text2("Coins: 0", { size: 150, fill: 0xFFD700 }); currencyText.anchor.set(0.5, 0.5); currencyText.y = -550; shopContainer.addChild(currencyText); // Create sections for Turret Colors and Upgrades var sections = [{ title: "TURRET COLORS", y: -300 }, { title: "UPGRADES", y: 300 }]; for (var i = 0; i < sections.length; i++) { var sectionTitle = new Text2(sections[i].title, { size: 175, fill: 0xFFFFFF }); sectionTitle.anchor.set(0.5, 0.5); sectionTitle.y = sections[i].y; shopContainer.addChild(sectionTitle); } // Create turret color options var colorOptions = [{ name: "BLUE", color: 0x3355FF, price: 100 }, { name: "RED", color: 0xFF0000, price: 100 }, { name: "GREEN", color: 0x00FF00, price: 100 }, { name: "PURPLE", color: 0x8A2BE2, price: 150 }, { name: "GOLD", color: 0xFFD700, price: 500 }]; var colorButtons = []; for (var i = 0; i < colorOptions.length; i++) { var xPos = (i - 2) * 300; var colorButton = createShopButton(colorOptions[i].name, xPos, -150, colorOptions[i].color, colorOptions[i].price); colorButton.colorData = colorOptions[i]; colorButton.down = function () { if (typeof coins !== 'undefined' && coins >= this.colorData.price) { coins -= this.colorData.price; currencyText.setText("Coins: " + coins); storage.bulletBounce_coins = coins; storage.bulletBounce_turretColor = this.colorData.color; // Apply color to turret if it exists if (turret) { var turretBase = turret.getChildAt(0); if (turretBase) { turretBase.tint = this.colorData.color; } } LK.effects.flashObject(this, 0xFFFFFF, 300); } }; shopContainer.addChild(colorButton); colorButtons.push(colorButton); } // Create upgrade options var upgradeOptions = [{ name: "MAX POWER", description: "+20% Power", price: 200, type: "power" }, { name: "EXTRA SHOT", description: "+1 Shot/Level", price: 300, type: "shots" }, { name: "BULLET LIFE", description: "+3 Bounces", price: 250, type: "bounces" }]; var upgradeButtons = []; for (var i = 0; i < upgradeOptions.length; i++) { var xPos = (i - 1) * 500; var upgradeButton = createShopButton(upgradeOptions[i].name, xPos, 500, 0x3355FF, upgradeOptions[i].price); var descText = new Text2(upgradeOptions[i].description, { size: 75, fill: 0xFFFFFF }); descText.anchor.set(0.5, 0.5); descText.y = 70; upgradeButton.addChild(descText); upgradeButton.upgradeData = upgradeOptions[i]; upgradeButton.down = function () { if (typeof coins !== 'undefined' && coins >= this.upgradeData.price) { coins -= this.upgradeData.price; currencyText.setText("Coins: " + coins); storage.bulletBounce_coins = coins; // Apply upgrade based on type switch (this.upgradeData.type) { case "power": // Increase max power var powerUpgrade = storage.bulletBounce_powerUpgrade || 0; powerUpgrade++; storage.bulletBounce_powerUpgrade = powerUpgrade; break; case "shots": // Increase shots per level var shotsUpgrade = storage.bulletBounce_shotsUpgrade || 0; shotsUpgrade++; storage.bulletBounce_shotsUpgrade = shotsUpgrade; break; case "bounces": // Increase max bounces var bouncesUpgrade = storage.bulletBounce_bouncesUpgrade || 0; bouncesUpgrade++; storage.bulletBounce_bouncesUpgrade = bouncesUpgrade; break; } LK.effects.flashObject(this, 0xFFFFFF, 300); } }; shopContainer.addChild(upgradeButton); upgradeButtons.push(upgradeButton); } // Close button var closeButton = new Container(); var closeBg = closeButton.attachAsset('powerMeter', { anchorX: 0.5, anchorY: 0.5, width: 750, height: 250, tint: 0xFF0000 }); var closeText = new Text2("CLOSE", { size: 150, fill: 0xFFFFFF }); closeText.anchor.set(0.5, 0.5); closeButton.addChild(closeText); closeButton.y = 800; shopContainer.addChild(closeButton); // Helper function to create shop buttons function createShopButton(text, xPos, yPos, color, price) { var button = new Container(); // Button background var bg = button.attachAsset('powerMeter', { anchorX: 0.5, anchorY: 0.5, width: 250, height: 250, tint: color }); // Button text var txt = new Text2(text, { size: 80, fill: 0xFFFFFF }); txt.anchor.set(0.5, 0.5); txt.y = -40; button.addChild(txt); // Price tag var priceTxt = new Text2(price + " coins", { size: 60, fill: 0xFFD700 }); priceTxt.anchor.set(0.5, 0.5); priceTxt.y = 30; button.addChild(priceTxt); // Position button button.x = xPos; button.y = yPos; return button; } // Button interactions closeButton.down = function () { self.hide(); }; // Show and hide methods self.show = function () { // Update currency display if (typeof coins !== 'undefined') { currencyText.setText("Coins: " + coins); } self.visible = true; // Animate appearance self.alpha = 0; tween(self, { alpha: 1 }, { duration: 300, easing: tween.easeOut }); }; self.hide = function () { // Animate disappearance tween(self, { alpha: 0 }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { self.visible = false; gamePaused = false; // Resume game when shop is hidden } }); }; return self; }); var Target = Container.expand(function () { var self = Container.call(this); var targetGraphics = self.attachAsset('target', { anchorX: 0.5, anchorY: 0.5 }); // Make targets 5x larger in baby mode if (typeof isBabyMode !== 'undefined' && isBabyMode) { targetGraphics.scale.set(5, 5); } self.isHit = false; self.hit = function () { if (self.isHit) { return; } self.isHit = true; LK.getSound('targetHit').play(); LK.effects.flashObject(self, 0xffffff, 300); tween(targetGraphics, { alpha: 0, scaleX: 0.2, scaleY: 0.2 }, { duration: 500, easing: tween.easeOut, onFinish: function onFinish() { targetGraphics.visible = false; } }); // Increment combo and update score with combo multiplier comboCount++; comboMultiplier = Math.min(5, 1 + Math.floor(comboCount / 2)); var scoreToAdd = 100 * comboMultiplier; // Increment score with multiplier LK.setScore(LK.getScore() + scoreToAdd); // Award coins based on combo var coinsToAdd = 5 * comboMultiplier; coins += coinsToAdd; storage.bulletBounce_coins = coins; // Show floating coin text var coinText = new Text2("+" + coinsToAdd, { size: 80, fill: 0xFFD700 }); coinText.anchor.set(0.5, 0.5); coinText.x = self.x; coinText.y = self.y; game.addChild(coinText); // Animate coin text tween(coinText, { y: coinText.y - 100, alpha: 0 }, { duration: 1000, easing: tween.easeOut, onFinish: function onFinish() { game.removeChild(coinText); } }); // Update combo text if it exists if (comboText) { comboText.setText('COMBO x' + comboMultiplier + ' (' + scoreToAdd + ' pts)'); comboText.visible = true; // Make combo text pulse for visual feedback tween(comboText, { scaleX: 1.3, scaleY: 1.3 }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { tween(comboText, { scaleX: 1.0, scaleY: 1.0 }, { duration: 200, easing: tween.easeIn }); } }); } // Reset combo timer if (comboTimer) { LK.clearTimeout(comboTimer); } // Set new combo timer - combo resets after 2.5 seconds comboTimer = LK.setTimeout(function () { comboCount = 0; comboMultiplier = 1; if (comboText) { comboText.visible = false; } }, 2500); }; return self; }); var TitleScreen = Container.expand(function () { var self = Container.call(this); // Create logo using the logo asset var logo = self.attachAsset('gameLogo', { anchorX: 0.5, anchorY: 0.5, width: 1500, height: 1500 * (700 / 1167), // Use correct aspect ratio (height/width) to maintain proportions y: -300 // Move the logo up }); // Start button var startButton = new Container(); var buttonBg = startButton.attachAsset('powerMeter', { anchorX: 0.5, anchorY: 0.5, width: 750, height: 250, tint: 0x22cc22 }); var buttonText = new Text2("PLAY", { size: 175, fill: 0xFFFFFF }); buttonText.anchor.set(0.5, 0.5); startButton.addChild(buttonText); startButton.y = 500; self.addChild(startButton); // Add button interaction startButton.down = function () { self.visible = false; difficultySelector.visible = true; }; // Animation for logo tween(logo, { scaleX: 1.1, scaleY: 1.1 }, { duration: 1000, easing: tween.easeInOut, loop: true, yoyo: true }); return self; }); var Turret = Container.expand(function () { var self = Container.call(this); var base = self.attachAsset('turret', { anchorX: 0.5, anchorY: 0.5 }); var barrel = self.attachAsset('turretBarrel', { anchorX: 0, anchorY: 0.5, x: 0, y: 0 }); self.angle = 0; self.power = 10; // Apply power upgrade if available var powerUpgrade = storage.bulletBounce_powerUpgrade || 0; self.maxPower = 20 + powerUpgrade * 4; // Each upgrade adds 20% more power // Apply saved turret color if available if (storage.bulletBounce_turretColor) { base.tint = storage.bulletBounce_turretColor; } self.isDragging = false; self.isActive = true; // Add pulse animation to make turret more dynamic self.update = function () { if (self.isActive) { var pulse = Math.sin(LK.ticks * 0.05) * 0.05 + 1; base.scale.set(pulse, pulse); } }; self.updateRotation = function (angle) { self.angle = angle; barrel.rotation = angle; }; self.setPower = function (power) { self.power = Math.max(5, Math.min(self.maxPower, power)); }; self.getShootVelocity = function () { return { x: Math.cos(self.angle) * self.power, y: Math.sin(self.angle) * self.power }; }; return self; }); var Wall = Container.expand(function () { var self = Container.call(this); var wallGraphics = self.attachAsset('wall', { anchorX: 0.5, anchorY: 0.5 }); return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x222222 }); /**** * Game Code ****/ // Game variables var turret; var bullets = []; var targets = []; var walls = []; var powerups = []; var luckyBlocks = []; var powerMeter; var aimLine; var isDragging = false; var dragStartX, dragStartY; var currentLevel = 1; var shotsRemaining = 5; var totalTargets = 0; var targetsHit = 0; var difficultySelector; var titleScreen; var deathScreen; var pauseMenu; var shopMenu; var gamePaused = false; var gameDifficulty = "normal"; // Default difficulty var isBabyMode = false; var isImpossibleMode = false; var comboCount = 0; var comboTimer = null; var comboMultiplier = 1; var comboText; var coins = 0; // Currency for shop purchases var shopButton; // Shop button reference var activePowerUps = { multishot: false, bigball: false, slowmotion: false, explosive: false }; var powerUpTimers = {}; // Function to apply powerup effects function applyPowerUpEffect(type) { switch (type) { case "multishot": activePowerUps.multishot = true; powerUpTimers.multishot = LK.setTimeout(function () { activePowerUps.multishot = false; }, 15000); // 15 seconds break; case "bigball": activePowerUps.bigball = true; powerUpTimers.bigball = LK.setTimeout(function () { activePowerUps.bigball = false; }, 20000); // 20 seconds break; case "slowmotion": activePowerUps.slowmotion = true; // Visual effect for slow motion activation LK.effects.flashScreen(0xaa00ff, 500); // Create slow motion text indicator var slowText = new Text2("SLOW MOTION", { size: 200, fill: 0xaa00ff }); slowText.anchor.set(0.5, 0.5); slowText.x = 0; slowText.y = 0; LK.gui.center.addChild(slowText); // Animate slow motion text tween(slowText, { alpha: 0, scaleX: 2, scaleY: 2 }, { duration: 1000, easing: tween.easeOut, onFinish: function onFinish() { LK.gui.center.removeChild(slowText); } }); // Slow down all existing bullets for (var i = 0; i < bullets.length; i++) { bullets[i].speedX *= 0.6; bullets[i].speedY *= 0.6; } powerUpTimers.slowmotion = LK.setTimeout(function () { activePowerUps.slowmotion = false; // Show end of slow motion effect LK.effects.flashScreen(0x444444, 300); }, 10000); // 10 seconds break; case "extralife": shotsRemaining += 3; if (shotsTxt && typeof shotsTxt !== 'undefined') { shotsTxt.setText('Shots: ' + shotsRemaining); } break; case "explosive": activePowerUps.explosive = true; powerUpTimers.explosive = LK.setTimeout(function () { activePowerUps.explosive = false; }, 12000); // 12 seconds break; } } // Function to create a random powerup function createPowerUp(x, y, type) { var powerup = new PowerUp(); powerup.x = x; powerup.y = y; powerup.setType(type || getRandomPowerUpType()); game.addChild(powerup); powerups.push(powerup); return powerup; } // Function to create a lucky block function createLuckyBlock(x, y) { var luckyBlock = new LuckyBlock(); luckyBlock.x = x; luckyBlock.y = y; game.addChild(luckyBlock); luckyBlocks.push(luckyBlock); return luckyBlock; } // Function to get random powerup type function getRandomPowerUpType() { var types = ["multishot", "bigball", "slowmotion", "extralife", "explosive"]; return types[Math.floor(Math.random() * types.length)]; } // Function to spawn random powerup at position function spawnRandomPowerUp(x, y) { createPowerUp(x, y); } // UI elements var shotsTxt; var levelTxt; function initGame() { // Check if game is starting fresh or resuming from difficulty selection if (!difficultySelector || !difficultySelector.visible) { // Create death screen first (initialized but hidden) if (!deathScreen) { deathScreen = new DeathScreen(); deathScreen.x = 2048 / 2; deathScreen.y = 2732 / 2; deathScreen.visible = false; game.addChild(deathScreen); } // Create pause menu (initialized but hidden) if (!pauseMenu) { pauseMenu = new PauseMenu(); pauseMenu.x = 2048 / 2; pauseMenu.y = 2732 / 2; pauseMenu.visible = false; game.addChild(pauseMenu); } // Create shop menu (initialized but hidden) if (!shopMenu) { shopMenu = new Shop(); shopMenu.x = 2048 / 2; shopMenu.y = 2732 / 2; shopMenu.visible = false; game.addChild(shopMenu); } // Load saved coins coins = storage.bulletBounce_coins || 0; // Load saved turret color var savedTurretColor = storage.bulletBounce_turretColor; // Create turret turret = new Turret(); turret.x = 100; turret.y = 2732 - 100; game.addChild(turret); // Create aim line aimLine = new AimLine(); aimLine.x = turret.x; aimLine.y = turret.y; game.addChild(aimLine); // Create power meter powerMeter = new PowerMeter(); powerMeter.x = 50; powerMeter.y = 2732 - 200; game.addChild(powerMeter); powerMeter.setPower(turret.power, turret.maxPower); // Setup UI shotsTxt = new Text2('Shots: ' + shotsRemaining, { size: 125, fill: 0xFFFFFF }); shotsTxt.anchor.set(0, 0); shotsTxt.y = 20; shotsTxt.x = -20; LK.gui.topRight.addChild(shotsTxt); levelTxt = new Text2('Level: ' + currentLevel, { size: 50, fill: 0xFFFFFF }); levelTxt.anchor.set(0, 0); levelTxt.y = 20; LK.gui.top.addChild(levelTxt); // Score text var scoreTxt = new Text2('Score: 0', { size: 50, fill: 0xFFFFFF }); scoreTxt.anchor.set(1, 0); scoreTxt.y = 180; scoreTxt.x = -20; LK.gui.topRight.addChild(scoreTxt); // Combo text comboText = new Text2('COMBO x1', { size: 150, fill: 0xFFFF00 }); comboText.anchor.set(0.5, 0.5); comboText.x = 0; comboText.y = -250; comboText.visible = false; LK.gui.center.addChild(comboText); // Pause button var pauseButton = new Container(); var pauseBg = pauseButton.attachAsset('centerCircle', { anchorX: 0.5, anchorY: 0.5, width: 160, height: 160, tint: 0x444444, alpha: 0.8 }); var pauseIcon = new Text2("II", { size: 80, fill: 0xFFFFFF }); pauseIcon.anchor.set(0.5, 0.5); pauseButton.addChild(pauseIcon); pauseButton.x = -50; pauseButton.y = 50; LK.gui.topRight.addChild(pauseButton); // Add pause button functionality pauseButton.down = function () { gamePaused = true; pauseMenu.show(); }; // Shop button shopButton = new Container(); var shopButtonBg = shopButton.attachAsset('shopIcon', { anchorX: 0.5, anchorY: 0.5, width: 160, height: 160, tint: 0xFFD700, alpha: 0.9 }); var shopText = new Text2("$", { size: 100, fill: 0x000000 }); shopText.anchor.set(0.5, 0.5); shopButton.addChild(shopText); shopButton.x = -50; shopButton.y = 250; LK.gui.topRight.addChild(shopButton); // Shop button functionality shopButton.down = function () { gamePaused = true; shopMenu.show(); }; // Coin display var coinDisplay = new Container(); var coinIcon = coinDisplay.attachAsset('centerCircle', { anchorX: 0.5, anchorY: 0.5, width: 80, height: 80, tint: 0xFFD700 }); var coinText = new Text2("" + coins, { size: 80, fill: 0xFFFFFF }); coinText.anchor.set(0, 0.5); coinText.x = 50; coinDisplay.addChild(coinText); coinDisplay.x = -20; coinDisplay.y = 350; LK.gui.topRight.addChild(coinDisplay); // Update coin display regularly LK.setInterval(function () { coinText.setText("" + coins); }, 100); // Update score display LK.setInterval(function () { scoreTxt.setText('Score: ' + LK.getScore()); }, 100); // Load level with current difficulty loadLevel(currentLevel); // Play background music LK.playMusic('gameMusic'); } } function loadLevel(level) { // Clear any existing level elements clearLevel(); // Update level display levelTxt.setText('Level: ' + level); // Update shots based on difficulty var shotsUpgrade = storage.bulletBounce_shotsUpgrade || 0; var extraShots = shotsUpgrade; switch (gameDifficulty) { case "baby": shotsRemaining = 50 + level + extraShots; // Way more shots in baby mode break; case "easy": shotsRemaining = 5 + level + extraShots; break; case "normal": shotsRemaining = 3 + level + extraShots; break; case "hard": shotsRemaining = 2 + Math.floor(level / 2) + extraShots; break; case "impossible": shotsRemaining = 1 + Math.floor(extraShots / 2); // Half benefit of shots for impossible mode break; default: shotsRemaining = 3 + level + extraShots; } // Add null check before updating shotsTxt if (shotsTxt && typeof shotsTxt !== 'undefined') { shotsTxt.setText('Shots: ' + shotsRemaining); } // Clear powerups and lucky blocks for (var i = powerups.length - 1; i >= 0; i--) { game.removeChild(powerups[i]); powerups.splice(i, 1); } for (var i = luckyBlocks.length - 1; i >= 0; i--) { game.removeChild(luckyBlocks[i]); luckyBlocks.splice(i, 1); } // Reset active powerups for (var key in activePowerUps) { activePowerUps[key] = false; } for (var key in powerUpTimers) { LK.clearTimeout(powerUpTimers[key]); } // Apply baby mode permanent powerups if (isBabyMode) { // In baby mode, always have these powerups active activePowerUps.multishot = true; activePowerUps.bigball = true; // Add a bunch of powerups right at the beginning for (var i = 0; i < 5; i++) { createPowerUp(300 + i * 200, 300 + i * 100); } } // Different level layouts switch (level) { case 1: // Basic level with random targets var targetCount = isBabyMode ? 3 : isImpossibleMode ? 15 : 3; for (var i = 0; i < targetCount; i++) { var targetX = 500 + Math.random() * 1000; var targetY = 300 + Math.random() * 800; // In impossible mode, place targets in very difficult positions if (isImpossibleMode) { // Place some targets behind walls, at extreme edges, etc. targetX = i % 3 === 0 ? 50 + Math.random() * 100 : 1900 + Math.random() * 100; targetY = i % 2 === 0 ? 50 + Math.random() * 100 : 2500 + Math.random() * 100; } createTarget(targetX, targetY); } // Add random walls if (isImpossibleMode) { // Create many walls in impossible mode for (var i = 0; i < 10; i++) { createWall(100 + i * 200, 400, Math.PI / 2); // Horizontal barrier } // Create a protective wall around the targets createWall(1800, 100, 0); createWall(1800, 400, 0); createWall(1800, 700, 0); } else if (!isBabyMode) { createWall(500 + Math.random() * 200, 500 + Math.random() * 200, Math.random() * Math.PI / 2); createWall(1300 + Math.random() * 200, 900 + Math.random() * 200, Math.random() * Math.PI / 2); } // Ensure at least one lucky block in level 1 and increase chance with level var luckyBlockCount = 1; // At least one guaranteed // Add more based on level-scaling chance for (var i = 0; i < 2; i++) { if (Math.random() < 0.2 + level * 0.05) { // Chance increases with level luckyBlockCount++; } } if (isBabyMode) { luckyBlockCount += 2; } for (var i = 0; i < luckyBlockCount; i++) { createLuckyBlock(500 + Math.random() * 1000, 500 + Math.random() * 800); } break; case 2: // More complex level with random targets var targetCount = isBabyMode ? 4 : isImpossibleMode ? 20 : 5; for (var i = 0; i < targetCount; i++) { var targetX = 400 + Math.random() * 1200; var targetY = 400 + Math.random() * 1100; // In impossible mode, place targets in very difficult positions if (isImpossibleMode) { targetX = i % 3 === 0 ? 50 + Math.random() * 100 : 1900 + Math.random() * 100; targetY = i % 4 === 0 ? 50 + Math.random() * 100 : 2500 + Math.random() * 100; } createTarget(targetX, targetY); } // Add random walls if (isImpossibleMode) { // Create a complex maze in impossible mode for (var i = 0; i < 15; i++) { createWall(300 + i * 100, 400, 0); createWall(300 + i * 100, 1000, 0); } } else if (!isBabyMode) { createWall(400 + Math.random() * 100, 800 + Math.random() * 100, Math.random() * Math.PI / 4); createWall(1500 + Math.random() * 100, 800 + Math.random() * 100, Math.random() * Math.PI / 4); createWall(900 + Math.random() * 200, 300 + Math.random() * 100, Math.PI / 2 + Math.random() * 0.3); createWall(900 + Math.random() * 200, 1400 + Math.random() * 100, Math.PI / 2 + Math.random() * 0.3); } // Ensure at least one lucky block in level 2 and increase chance with level var luckyBlockCount = 1; // At least one guaranteed // Add more based on level-scaling chance for (var i = 0; i < 3; i++) { if (Math.random() < 0.25 + level * 0.05) { // Chance increases with level luckyBlockCount++; } } if (isBabyMode) { luckyBlockCount += 3; } for (var i = 0; i < luckyBlockCount; i++) { createLuckyBlock(400 + Math.random() * 1200, 400 + Math.random() * 1100); } break; case 3: // Advanced level with random elements var targetCount = isBabyMode ? 5 : isImpossibleMode ? 25 : 7; for (var i = 0; i < targetCount; i++) { // Create targets in different quadrants of the screen if (isImpossibleMode) { // In impossible mode, place targets in extremely difficult positions var quadrant = i % 4; var xOffset = quadrant === 0 || quadrant === 2 ? 50 : 1900; var yOffset = quadrant === 0 || quadrant === 1 ? 50 : 2500; createTarget(xOffset + Math.random() * 50, yOffset + Math.random() * 50); } else { if (i < 2) { createTarget(300 + Math.random() * 400, 300 + Math.random() * 400); } else if (i < 4) { createTarget(1300 + Math.random() * 400, 300 + Math.random() * 400); } else if (i < 6) { createTarget(300 + Math.random() * 400, 1100 + Math.random() * 400); } else { createTarget(1300 + Math.random() * 400, 1100 + Math.random() * 400); } } } // Add walls if (isImpossibleMode) { // Create a complex defensive wall system in impossible mode for (var i = 0; i < 20; i++) { // Create a circular defensive wall var angle = i / 20 * Math.PI * 2; var radius = 800; var centerX = 1024; var centerY = 1366; var wallX = centerX + Math.cos(angle) * radius; var wallY = centerY + Math.sin(angle) * radius; createWall(wallX, wallY, angle); } } else if (!isBabyMode) { createWall(600 + Math.random() * 100, 600 + Math.random() * 100, Math.random() * 0.2); createWall(1300 + Math.random() * 100, 600 + Math.random() * 100, Math.random() * 0.2); createWall(600 + Math.random() * 100, 1200 + Math.random() * 100, Math.random() * 0.2); createWall(1300 + Math.random() * 100, 1200 + Math.random() * 100, Math.random() * 0.2); createWall(1000 + Math.random() * 100, 600 + Math.random() * 100, Math.PI / 2 + Math.random() * 0.2); createWall(1000 + Math.random() * 100, 1200 + Math.random() * 100, Math.PI / 2 + Math.random() * 0.2); } // Ensure at least one lucky block in level 3 and increase chance with level var luckyBlockCount = 1; // At least one guaranteed // Add more based on level-scaling chance for (var i = 0; i < 4; i++) { if (Math.random() < 0.3 + level * 0.05) { // Chance increases with level luckyBlockCount++; } } if (isBabyMode) { luckyBlockCount += 4; } // Place lucky blocks in each quadrant to ensure availability for (var i = 0; i < luckyBlockCount; i++) { var quadrant = i % 4; var xBase = quadrant === 0 || quadrant === 2 ? 300 : 1300; var yBase = quadrant === 0 || quadrant === 1 ? 300 : 1100; createLuckyBlock(xBase + Math.random() * 400, yBase + Math.random() * 400); } break; default: // For levels beyond 3, create a fully random level with more targets and walls // Adjust difficulty based on selected mode var difficultyMultiplier; switch (gameDifficulty) { case "baby": difficultyMultiplier = 0.3; // Much fewer targets and walls for baby mode break; case "easy": difficultyMultiplier = 0.7; // Fewer targets and walls break; case "normal": difficultyMultiplier = 1.0; // Normal difficulty break; case "hard": difficultyMultiplier = 1.5; // More targets and walls break; case "impossible": difficultyMultiplier = 3.0; // Way more targets and walls for impossible mode break; default: difficultyMultiplier = 1.0; } // Generate random number of targets based on level and difficulty var numTargets = Math.max(2, Math.floor((3 + Math.random() * level * 2) * difficultyMultiplier)); for (var i = 0; i < numTargets; i++) { var targetX = 300 + Math.random() * 1400; var targetY = 300 + Math.random() * 1500; // In impossible mode, position targets in very difficult places if (isImpossibleMode) { if (i % 3 === 0) { // Place targets at extreme edges targetX = Math.random() < 0.5 ? 50 + Math.random() * 50 : 1948 - Math.random() * 50; targetY = Math.random() < 0.5 ? 50 + Math.random() * 50 : 2682 - Math.random() * 50; } } createTarget(targetX, targetY); } // Generate random number of walls based on level and difficulty if (isImpossibleMode) { // Create complex wall patterns for impossible mode var wallCount = 20 + Math.floor(level * 2); for (var i = 0; i < wallCount; i++) { // Create walls that make targets very hard to hit var wallX = 300 + Math.random() * 1400; var wallY = 300 + Math.random() * 1500; var wallRotation = Math.random() * Math.PI; // Some walls positioned strategically to block shots if (i % 5 === 0) { wallX = 200; wallY = 400 + i / 5 * 300; wallRotation = 0; } createWall(wallX, wallY, wallRotation); } } else if (!isBabyMode) { var numWalls = Math.max(2, Math.floor(Math.random() * level * 1.5 * difficultyMultiplier)); for (var i = 0; i < numWalls; i++) { createWall(300 + Math.random() * 1400, 300 + Math.random() * 1500, Math.random() * Math.PI); } } // Add powerups - tons more in baby mode var numPowerups = isBabyMode ? 10 + Math.floor(level) : isImpossibleMode ? 0 : 1 + Math.floor(level / 3); for (var i = 0; i < numPowerups; i++) { createPowerUp(300 + Math.random() * 1400, 300 + Math.random() * 1500); } // Add lucky blocks - always at least one in each level, more in baby mode, chance increases with level // In impossible mode, still add at least one block var numLuckyBlocks = isBabyMode ? 5 + Math.floor(level / 2) : isImpossibleMode ? 1 : 1; // Add additional lucky blocks based on level progression (chance increases with level) var chanceForExtraBlocks = Math.min(0.8, 0.2 + level * 0.07); // Cap at 80% chance, slightly higher scaling // Higher levels should have more chances for blocks var numChances = Math.max(3, Math.floor(level * 1.5)); for (var i = 0; i < numChances; i++) { if (Math.random() < chanceForExtraBlocks) { numLuckyBlocks++; } } // Create lucky blocks for (var i = 0; i < numLuckyBlocks; i++) { createLuckyBlock(300 + Math.random() * 1400, 300 + Math.random() * 1500); } break; } // Track targets for win condition totalTargets = targets.length; targetsHit = 0; } function clearLevel() { // Remove all bullets for (var i = bullets.length - 1; i >= 0; i--) { game.removeChild(bullets[i]); bullets.splice(i, 1); } // Remove all targets for (var i = targets.length - 1; i >= 0; i--) { game.removeChild(targets[i]); targets.splice(i, 1); } // Remove all walls for (var i = walls.length - 1; i >= 0; i--) { game.removeChild(walls[i]); walls.splice(i, 1); } } function createTarget(x, y) { var target = new Target(); target.x = x; target.y = y; game.addChild(target); targets.push(target); return target; } function createWall(x, y, rotation) { var wall = new Wall(); wall.x = x; wall.y = y; wall.rotation = rotation; game.addChild(wall); walls.push(wall); return wall; } function fireBullet() { if (shotsRemaining <= 0) { return; } // Reset combo when firing a new bullet comboCount = 0; comboMultiplier = 1; if (comboText) { comboText.visible = false; } if (comboTimer) { LK.clearTimeout(comboTimer); comboTimer = null; } // Decrease shots remaining shotsRemaining--; if (shotsTxt && typeof shotsTxt !== 'undefined') { shotsTxt.setText('Shots: ' + shotsRemaining); } // Determine how many bullets to fire based on powerups var bulletCount = activePowerUps.multishot ? 3 : 1; for (var i = 0; i < bulletCount; i++) { // Create new bullet var bullet = new Bullet(); // Add null check to ensure turret exists before accessing its properties if (turret) { bullet.x = turret.x; bullet.y = turret.y; } else { // Set default position if turret doesn't exist bullet.x = 100; bullet.y = 2732 - 100; } // Get velocity from turret angle and power var velocity = turret ? turret.getShootVelocity() : { x: 0, y: 0 }; // If multishot, spread the bullets if (activePowerUps.multishot && bulletCount > 1) { var spreadAngle = (i - 1) * 0.2; // -0.2, 0, 0.2 radians spread var newAngle = Math.atan2(velocity.y, velocity.x) + spreadAngle; var speed = Math.sqrt(velocity.x * velocity.x + velocity.y * velocity.y); bullet.speedX = Math.cos(newAngle) * speed; bullet.speedY = Math.sin(newAngle) * speed; } else { bullet.speedX = velocity.x; bullet.speedY = velocity.y; } // Apply slowmotion powerup if (activePowerUps.slowmotion) { bullet.speedX *= 0.6; bullet.speedY *= 0.6; } // Apply big ball powerup if (activePowerUps.bigball) { bullet.scale.set(2.5, 2.5); } // Apply explosive powerup if needed if (activePowerUps.explosive) { bullet.isExplosive = true; } // Add to game game.addChild(bullet); bullets.push(bullet); } // Play sound LK.getSound('shoot').play(); // Flash turret - add null check to prevent script error if (turret) { LK.effects.flashObject(turret, 0xffffff, 200); } } function checkCollisions() { // Check all active bullets for (var i = bullets.length - 1; i >= 0; i--) { var bullet = bullets[i]; if (!bullet.isActive) { // Remove inactive bullets game.removeChild(bullet); bullets.splice(i, 1); continue; } // Check for wall collisions for (var j = 0; j < walls.length; j++) { var wall = walls[j]; if (bullet.intersects(wall)) { // Calculate bounce direction based on wall angle var wallAngle = wall.rotation; var normalAngle = wallAngle + Math.PI / 2; // Reflect velocity based on wall normal var speed = Math.sqrt(bullet.speedX * bullet.speedX + bullet.speedY * bullet.speedY); var bulletAngle = Math.atan2(bullet.speedY, bullet.speedX); var reflectionAngle = 2 * normalAngle - bulletAngle; bullet.speedX = Math.cos(reflectionAngle) * speed * 0.8; // Lose some energy bullet.speedY = Math.sin(reflectionAngle) * speed * 0.8; // Move bullet slightly away from wall to prevent multiple collisions bullet.x += bullet.speedX; bullet.y += bullet.speedY; bullet.bounces++; LK.getSound('bounce').play(); } } // Check for target collisions for (var j = 0; j < targets.length; j++) { var target = targets[j]; if (!target.isHit && bullet.intersects(target)) { target.hit(); targetsHit++; // If explosive bullet, create explosion effect if (bullet.isExplosive) { // Hit nearby targets within radius var explosionRadius = 250; for (var k = 0; k < targets.length; k++) { if (j !== k && !targets[k].isHit) { var dx = targets[k].x - target.x; var dy = targets[k].y - target.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < explosionRadius) { targets[k].hit(); targetsHit++; } } } // Visual explosion effect LK.effects.flashScreen(0xFF5500, 200); } // Check if level complete if (targetsHit >= totalTargets) { levelComplete(); } } } // Check for lucky block collisions for (var j = luckyBlocks.length - 1; j >= 0; j--) { var luckyBlock = luckyBlocks[j]; if (!luckyBlock.isHit && bullet.intersects(luckyBlock)) { luckyBlock.hit(); luckyBlocks.splice(j, 1); } } // Check for powerup collisions for (var j = powerups.length - 1; j >= 0; j--) { var powerup = powerups[j]; if (!powerup.isCollected && bullet.intersects(powerup)) { powerup.collect(); powerups.splice(j, 1); } } } } function levelComplete() { LK.setTimeout(function () { // Advance to next level currentLevel++; loadLevel(currentLevel); }, 1000); } function checkGameOver() { // Game over conditions if (shotsRemaining <= 0 && bullets.length === 0) { // Check if any targets left var anyTargetsLeft = false; for (var i = 0; i < targets.length; i++) { if (!targets[i].isHit) { anyTargetsLeft = true; break; } } if (anyTargetsLeft) { // Player has lost LK.setTimeout(function () { if (isBabyMode) { // In baby mode, show custom death screen instead of game over deathScreen.showMessage(true); } else { LK.showGameOver(); } }, 1000); } } } function calculateAimAngle(x, y) { // Calculate angle from turret to mouse/touch point // Check if turret exists before accessing its properties if (!turret) { return 0; // Return default angle if turret doesn't exist yet } var dx = x - turret.x; var dy = y - turret.y; return Math.atan2(dy, dx); } function updateAimLine() { if (turret && aimLine) { aimLine.update(turret.angle, 200); } } // Load previously selected difficulty if available var savedDifficulty = storage.bulletBounce_difficulty; if (savedDifficulty) { gameDifficulty = savedDifficulty; } // Create title screen titleScreen = new TitleScreen(); titleScreen.x = 2048 / 2; titleScreen.y = 2732 / 2; game.addChild(titleScreen); // Create difficulty selector (initially hidden) difficultySelector = new Difficulty(); difficultySelector.x = 2048 / 2; difficultySelector.y = 2732 / 2; difficultySelector.visible = false; game.addChild(difficultySelector); // If we just want to start the game directly with saved difficulty, uncomment this: // titleScreen.visible = false; // difficultySelector.visible = false; // initGame(); // Event handlers game.down = function (x, y, obj) { // Skip if game is paused if (gamePaused) { return; } isDragging = true; dragStartX = x; dragStartY = y; // Set initial angle var angle = calculateAimAngle(x, y); if (turret) { // Add null check to ensure turret exists turret.updateRotation(angle); } updateAimLine(); }; game.move = function (x, y, obj) { // Skip if game is paused if (gamePaused) { return; } if (isDragging) { // Update turret angle var angle = calculateAimAngle(x, y); if (turret) { // Add null check to ensure turret exists turret.updateRotation(angle); } // Calculate power based on drag distance var dx = x - dragStartX; var dy = y - dragStartY; var dragDistance = Math.sqrt(dx * dx + dy * dy); if (turret) { var power = Math.min(turret.maxPower, dragDistance / 50); turret.setPower(power); // Update power meter powerMeter.setPower(turret.power, turret.maxPower); } // Update aim line updateAimLine(); } }; game.up = function (x, y, obj) { // Skip if game is paused if (gamePaused) { return; } if (isDragging) { isDragging = false; fireBullet(); } }; // Game update loop game.update = function () { // Skip updates if game is paused if (gamePaused) { return; } // Update all bullets for (var i = 0; i < bullets.length; i++) { bullets[i].update(); } // Check for collisions checkCollisions(); // Check for game over checkGameOver(); };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var AimLine = Container.expand(function () {
var self = Container.call(this);
var line = self.attachAsset('aimLine', {
anchorX: 0.5,
anchorY: 0
});
self.update = function (angle, length) {
line.rotation = angle;
line.height = length;
};
return self;
});
var Bullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('ball', {
anchorX: 0.5,
anchorY: 0.5,
rotation: 0
});
self.speedX = 0;
self.speedY = 0;
self.bounces = 0;
self.isExplosive = false;
// Special effect for explosive bullets
if (typeof activePowerUps !== 'undefined' && activePowerUps.explosive) {
bulletGraphics.tint = 0xFF0000;
}
// Set max bounces based on game difficulty
var bouncesUpgrade = storage.bulletBounce_bouncesUpgrade || 0;
var extraBounces = bouncesUpgrade * 3; // Each upgrade gives 3 extra bounces
if (typeof gameDifficulty !== 'undefined') {
switch (gameDifficulty) {
case "easy":
self.maxBounces = 10 + extraBounces; // More bounces for easy mode
break;
case "normal":
self.maxBounces = 8 + extraBounces;
break;
case "hard":
self.maxBounces = 5 + extraBounces; // Fewer bounces for hard mode
break;
default:
self.maxBounces = 8 + extraBounces;
}
} else {
self.maxBounces = 8 + extraBounces; // Default value
}
self.isActive = true;
self.update = function () {
if (!self.isActive) {
return;
}
// Check for impossible mode - make bullets behave erratically
if (typeof isImpossibleMode !== 'undefined' && isImpossibleMode) {
// In impossible mode, bullets randomly change direction
if (Math.random() < 0.05) {
self.speedX = self.speedX * (0.8 + Math.random() * 0.4);
self.speedY = self.speedY * (0.8 + Math.random() * 0.4);
// Occasionally make a sharp turn
if (Math.random() < 0.1) {
self.speedX *= -1;
}
}
}
// Store previous position for trail effect
if (!self.prevX) {
self.prevX = self.x;
self.prevY = self.y;
self.trail = [];
}
// Apply velocity
self.x += self.speedX;
self.y += self.speedY;
// Create trail effect
if (Math.abs(self.x - self.prevX) > 5 || Math.abs(self.y - self.prevY) > 5) {
// Create a trail particle
var trailParticle = LK.getAsset('ball', {
anchorX: 0.5,
anchorY: 0.5,
x: self.prevX,
y: self.prevY,
scaleX: 0.3,
scaleY: 0.3,
alpha: 0.5
});
if (self.isExplosive) {
trailParticle.tint = 0xFF0000;
}
game.addChild(trailParticle);
self.trail.push(trailParticle);
// Fade out trail particle
tween(trailParticle, {
alpha: 0,
scaleX: 0.1,
scaleY: 0.1
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
game.removeChild(trailParticle);
// Remove from trail array
var index = self.trail.indexOf(trailParticle);
if (index > -1) {
self.trail.splice(index, 1);
}
}
});
// Store current position as previous for next trail
self.prevX = self.x;
self.prevY = self.y;
}
// Apply a small rotation for visual effect
bulletGraphics.rotation += 0.05;
// Check for wall collisions
if (self.x < 30 || self.x > 2018) {
self.speedX *= -0.9; // Lose some energy on bounce
if (self.x < 30) {
self.x = 30;
}
if (self.x > 2018) {
self.x = 2018;
}
self.bounces++;
LK.getSound('bounce').play();
}
if (self.y < 30 || self.y > 2702) {
self.speedY *= -0.9; // Lose some energy on bounce
if (self.y < 30) {
self.y = 30;
}
if (self.y > 2702) {
self.y = 2702;
}
self.bounces++;
LK.getSound('bounce').play();
}
// Check if bullet should be removed
if (self.bounces >= self.maxBounces || Math.abs(self.speedX) < 0.5 && Math.abs(self.speedY) < 0.5) {
self.isActive = false;
}
};
return self;
});
var DeathScreen = Container.expand(function () {
var self = Container.call(this);
var background = self.attachAsset('centerCircle', {
anchorX: 0.5,
anchorY: 0.5,
width: 1000,
height: 700,
tint: 0x000000,
alpha: 0.8
});
// Array of funny death messages
self.deathMessages = ["GAME OVER... but mainly skill issue", "YOU DIED! Better luck in your next life!", "FAILURE COMPLETE! Achievement unlocked?", "RIP: Rest In Pieces", "OBLITERATED! That's gotta hurt!", "GAME OVER! Your bullets miss you already", "YOU'RE TOAST! Buttered on both sides", "K.O.! Flawless victory... for the targets", "WASTED! Grand Theft Auto would be proud", "YOU DIED! Dark Souls veterans: 'First time?'", "DEFEATED! By inanimate objects...", "TERMINATED! You'll be back, right?", "CATASTROPHIC FAILURE! NASA is impressed", "CRUSHED! Like your hopes and dreams", "GAME OVER! Have you tried turning it off and on again?", "ANNIHILATED! That's one way to exit", "YOU LOSE! But the real treasure was the bullets you wasted along the way", "EXPIRED! Like yesterday's milk", "ELIMINATED! Nothing personal, kid", "DISCONNECTED! From reality and the game", "DOOMED! Not even the Slayer could save you", "SHUTDOWN! Error: Skills not found", "PULVERIZED! Like a blender on max setting", "DESTROYED! But so is your self-esteem", "WRECKED! Like that test you didn't study for", "ERASED! Ctrl+Z won't save you now", "LIQUIDATED! Assets and all", "ERADICATED! Not even a microbe survived", "DEVASTATED! Just like your high score dreams", "DEMOLISHED! We hardly knew ye", "SHATTERED! Like your confidence", "VAPORIZED! Not even ashes remain", "DELETED! From the game and our hearts", "EXTINGUISHED! Your flame went out", "FALLEN! And you can't get up", "SQUASHED! Like a bug on a windshield", "CRUSHED! Under the weight of your mistakes", "DISMANTLED! Like furniture from IKEA", "DISSOLVED! In a pool of tears", "DISPATCHED! To the shadow realm", "BANISHED! To the void of losers", "EXECUTED! With extreme prejudice", "NULLIFIED! void player = null;", "DEACTIVATED! Sleep mode engaged", "TERMINATED! With extreme prejudice", "CRASHED! Like Windows Vista", "FLATLINED! Call the medic!... wait, too late", "WIPED OUT! Not even a stain remains", "DISINTEGRATED! Atoms scattered to the wind", "ERODED! Time wasn't on your side", "EXPIRED! Your warranty was up", "RECYCLED! At least you're eco-friendly", "DISCARDED! In the trash bin of history", "OVERTHROWN! The rebellion was successful", "DERAILED! Like your train of thought", "ABANDONED! Left to die in the wilderness", "VANQUISHED! The dragon wins this time", "RETIRED! Forced early retirement", "DISCONTINUED! No longer in production", "SCRAPPED! Back to the drawing board", "PURGED! From the system", "UNPLUGGED! No power to continue", "ARCHIVED! Filed under 'F' for 'Failure'", "DEBUNKED! Your skills were just a myth", "DETHRONED! Your reign is over", "DISCHARGED! Dishonorable, of course", "EXILED! Never to return", "FORECLOSED! Should have paid your skill mortgage", "IMPLODED! Collapsed under pressure", "OBSOLETE! Time for an upgrade", "OUTPLAYED! By rectangles...", "OVERWRITTEN! Your save file is gone", "RECALLED! Due to defects", "REJECTED! Application denied", "REPOSSESSED! Should've made those skill payments", "RESCINDED! Your license to play", "REVOKED! Your gaming privileges", "SACKED! Clean out your locker", "SCRAPPED! Parts may be salvageable", "SUPERSEDED! By a better player", "SUPERSEDED! By literally anyone else", "SUSPENDED! Pending investigation", "TORPEDOED! Abandon ship!", "VOIDED! Like your warranty", "ZAPPED! Extra crispy", "ZOMBIFIED! Braaaains... you need some", "THANOS SNAPPED! You don't feel so good...", "CTRL+ALT+DEFEATED! Forced shutdown", "BLUE SCREENED! Fatal player error", "LAG SPIKED! Connection to skills lost", "UNFRIENDED! Game doesn't like you", "GHOSTED! The game left you on read", "YEETED! Into the nearest sun", "STONKS DOWN! Your performance is bearish", "SENT TO THE RANCH! Dr. Phil approved", "RAGE QUIT! Oh wait, you actually lost", "ALT+F4'd! The easy way out", "404'd! Player skill not found", "UNSUBSCRIBED! From the living channel", "DOWNGRADED! To casual status", "FACTORY RESET! All progress lost", "UNINSTALLED! System32\\Player\\Skills.exe not found"];
var messageText = new Text2("GAME OVER", {
size: 250,
fill: 0xFF0000
});
messageText.anchor.set(0.5, 0.5);
messageText.y = -250;
self.addChild(messageText);
var babyMessage = new Text2("Aww you lost, that's cute!", {
size: 125,
fill: 0xFF55FF
});
babyMessage.anchor.set(0.5, 0.5);
babyMessage.y = 50;
babyMessage.visible = false; // Only shown in baby mode
self.addChild(babyMessage);
var restartButton = new Container();
var buttonBg = restartButton.attachAsset('powerMeter', {
anchorX: 0.5,
anchorY: 0.5,
width: 750,
height: 250,
tint: 0x22cc22
});
var buttonText = new Text2("RETRY", {
size: 150,
fill: 0xFFFFFF
});
buttonText.anchor.set(0.5, 0.5);
restartButton.addChild(buttonText);
restartButton.y = 400;
self.addChild(restartButton);
// Add button interaction
restartButton.down = function () {
if (isBabyMode) {
// In baby mode, give an extra life instead of game over
self.visible = false;
shotsRemaining += 1;
if (shotsTxt && typeof shotsTxt !== 'undefined') {
shotsTxt.setText('Shots: ' + shotsRemaining);
}
// Spawn a few powerups to help
for (var i = 0; i < 2; i++) {
createPowerUp(turret.x + 100 + i * 100, turret.y - 100 - i * 50);
}
} else {
LK.showGameOver();
}
};
self.showMessage = function (isBaby) {
babyMessage.visible = isBaby;
self.visible = true;
// Always show the default message "GAME OVER"
messageText.setText("GAME OVER");
// Animate death screen appearance
self.alpha = 0;
tween(self, {
alpha: 1
}, {
duration: 500,
easing: tween.easeOut
});
};
return self;
});
var Difficulty = Container.expand(function () {
var self = Container.call(this);
// Button base size and spacing
var buttonWidth = 750;
var buttonHeight = 250;
var spacing = 75;
// Create title
var titleText = new Text2('SELECT DIFFICULTY', {
size: 200,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0);
titleText.y = -600;
self.addChild(titleText);
// Create difficulty buttons
var easyButton = createButton('EASY', -buttonWidth / 2 - spacing / 2, -250, 0x44AA44);
var normalButton = createButton('NORMAL', buttonWidth / 2 + spacing / 2, -250, 0x4477AA);
var hardButton = createButton('HARD', -buttonWidth / 2 - spacing / 2, 75, 0xAA4444);
var babyButton = createButton('BABY', buttonWidth / 2 + spacing / 2, 75, 0xFF55FF);
var impossibleButton = createButton('IMPOSSIBLE', 0, 400, 0x000000);
self.addChild(easyButton);
self.addChild(normalButton);
self.addChild(hardButton);
self.addChild(babyButton);
self.addChild(impossibleButton);
function createButton(text, xPos, yPos, color) {
var button = new Container();
// Button background
var bg = button.attachAsset('powerMeter', {
anchorX: 0.5,
anchorY: 0.5,
width: buttonWidth,
height: buttonHeight,
tint: color
});
// Button text
var txt = new Text2(text, {
size: 125,
fill: 0xFFFFFF
});
txt.anchor.set(0.5, 0.5);
button.addChild(txt);
// Position button
button.x = xPos;
button.y = yPos;
// Add interaction
button.down = function () {
selectDifficulty(text.toLowerCase());
};
return button;
}
function selectDifficulty(level) {
// Set difficulty and hide selector
gameDifficulty = level;
self.visible = false;
// Set mode flags
isBabyMode = level === "baby";
isImpossibleMode = level === "impossible";
// Save selected difficulty
storage.bulletBounce_difficulty = level;
// Start the game with selected difficulty
initGame();
}
return self;
});
var LuckyBlock = Container.expand(function () {
var self = Container.call(this);
var blockGraphics = self.attachAsset('luckyBlock', {
anchorX: 0.5,
anchorY: 0.5
});
// Question mark text
var questionMark = new Text2("?", {
size: 50,
fill: 0x000000
});
questionMark.anchor.set(0.5, 0.5);
self.addChild(questionMark);
self.isHit = false;
// Add visual effect - rotating
self.update = function () {
questionMark.rotation = Math.sin(LK.ticks * 0.05) * 0.2;
blockGraphics.rotation = Math.sin(LK.ticks * 0.03) * 0.1;
};
self.hit = function () {
if (self.isHit) {
return;
}
self.isHit = true;
LK.effects.flashObject(self, 0xffffff, 300);
// Animation for breaking
tween(blockGraphics, {
alpha: 0,
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 500,
easing: tween.easeOut
});
tween(questionMark, {
alpha: 0,
scaleX: 0.2,
scaleY: 0.2
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
// Spawn random powerup
spawnRandomPowerUp(self.x, self.y);
self.visible = false;
}
});
};
return self;
});
var PauseMenu = Container.expand(function () {
var self = Container.call(this);
// Background overlay
var overlay = self.attachAsset('centerCircle', {
anchorX: 0.5,
anchorY: 0.5,
width: 2048,
height: 2732,
tint: 0x000000,
alpha: 0.7
});
// Pause menu container
var menuContainer = new Container();
menuContainer.x = 0;
menuContainer.y = 0;
self.addChild(menuContainer);
// Pause title
var titleText = new Text2("PAUSED", {
size: 250,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.y = -375;
menuContainer.addChild(titleText);
// Resume button
var resumeButton = new Container();
var resumeBg = resumeButton.attachAsset('powerMeter', {
anchorX: 0.5,
anchorY: 0.5,
width: 750,
height: 250,
tint: 0x22cc22
});
var resumeText = new Text2("RESUME", {
size: 150,
fill: 0xFFFFFF
});
resumeText.anchor.set(0.5, 0.5);
resumeButton.addChild(resumeText);
resumeButton.y = 0;
menuContainer.addChild(resumeButton);
// Main menu button
var menuButton = new Container();
var menuBg = menuButton.attachAsset('powerMeter', {
anchorX: 0.5,
anchorY: 0.5,
width: 750,
height: 250,
tint: 0x3355ff
});
var menuText = new Text2("MAIN MENU", {
size: 150,
fill: 0xFFFFFF
});
menuText.anchor.set(0.5, 0.5);
menuButton.addChild(menuText);
menuButton.y = 375;
menuContainer.addChild(menuButton);
// Button interactions
resumeButton.down = function () {
self.hide();
};
menuButton.down = function () {
self.hide();
if (titleScreen) {
titleScreen.visible = true;
}
// Reset game state
clearLevel();
// Hide all game elements
if (turret) {
turret.visible = false;
}
if (aimLine) {
aimLine.visible = false;
}
if (powerMeter) {
powerMeter.visible = false;
}
if (shotsTxt) {
shotsTxt.visible = false;
}
if (levelTxt) {
levelTxt.visible = false;
}
};
// Show and hide methods
self.show = function () {
self.visible = true;
// Animate appearance
self.alpha = 0;
tween(self, {
alpha: 1
}, {
duration: 300,
easing: tween.easeOut
});
};
self.hide = function () {
// Animate disappearance
tween(self, {
alpha: 0
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
self.visible = false;
gamePaused = false; // Resume game when pause menu is hidden
}
});
};
return self;
});
var PowerMeter = Container.expand(function () {
var self = Container.call(this);
var meterBg = self.attachAsset('powerMeter', {
anchorX: 0,
anchorY: 0.5,
tint: 0x444444
});
var meterFill = self.attachAsset('powerMeter', {
anchorX: 0,
anchorY: 0.5,
scaleX: 0.5
});
self.setPower = function (power, maxPower) {
meterFill.scaleX = power / maxPower;
};
return self;
});
var PowerUp = Container.expand(function () {
var self = Container.call(this);
self.type = "";
self.isCollected = false;
// Define powerup sizes and colors
var types = {
"multishot": {
color: 0xff5500,
text: "3x"
},
"bigball": {
color: 0x00aaff,
text: "BIG"
},
"slowmotion": {
color: 0xaa00ff,
text: "SLOW"
},
"extralife": {
color: 0xff0055,
text: "LIFE"
},
"explosive": {
color: 0xff0000,
text: "BOOM"
}
};
// Background circle
var bg = self.attachAsset('centerCircle', {
anchorX: 0.5,
anchorY: 0.5,
width: 60,
height: 60
});
// Text label
var label = new Text2("", {
size: 25,
fill: 0xFFFFFF
});
label.anchor.set(0.5, 0.5);
self.addChild(label);
self.setType = function (powerupType) {
self.type = powerupType;
bg.tint = types[powerupType].color;
label.setText(types[powerupType].text);
// Add visual effect - pulsing
self.update = function () {
var pulse = Math.sin(LK.ticks * 0.05) * 0.1 + 1;
bg.scale.set(pulse, pulse);
};
};
self.collect = function () {
if (self.isCollected) {
return;
}
self.isCollected = true;
LK.effects.flashObject(self, 0xffffff, 300);
// Animation for collection
tween(self, {
alpha: 0,
scaleX: 0.2,
scaleY: 0.2
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
self.visible = false;
}
});
// Apply powerup effect based on type
applyPowerUpEffect(self.type);
};
self.down = function () {
// Allow clicking on powerups too (mobile friendly)
self.collect();
};
return self;
});
var Shop = Container.expand(function () {
var self = Container.call(this);
// Background overlay
var overlay = self.attachAsset('centerCircle', {
anchorX: 0.5,
anchorY: 0.5,
width: 2048,
height: 2732,
tint: 0x000000,
alpha: 0.8
});
// Shop container
var shopContainer = new Container();
shopContainer.x = 0;
shopContainer.y = 0;
self.addChild(shopContainer);
// Shop title
var titleText = new Text2("SHOP", {
size: 250,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.y = -750;
shopContainer.addChild(titleText);
// Available currency
var currencyText = new Text2("Coins: 0", {
size: 150,
fill: 0xFFD700
});
currencyText.anchor.set(0.5, 0.5);
currencyText.y = -550;
shopContainer.addChild(currencyText);
// Create sections for Turret Colors and Upgrades
var sections = [{
title: "TURRET COLORS",
y: -300
}, {
title: "UPGRADES",
y: 300
}];
for (var i = 0; i < sections.length; i++) {
var sectionTitle = new Text2(sections[i].title, {
size: 175,
fill: 0xFFFFFF
});
sectionTitle.anchor.set(0.5, 0.5);
sectionTitle.y = sections[i].y;
shopContainer.addChild(sectionTitle);
}
// Create turret color options
var colorOptions = [{
name: "BLUE",
color: 0x3355FF,
price: 100
}, {
name: "RED",
color: 0xFF0000,
price: 100
}, {
name: "GREEN",
color: 0x00FF00,
price: 100
}, {
name: "PURPLE",
color: 0x8A2BE2,
price: 150
}, {
name: "GOLD",
color: 0xFFD700,
price: 500
}];
var colorButtons = [];
for (var i = 0; i < colorOptions.length; i++) {
var xPos = (i - 2) * 300;
var colorButton = createShopButton(colorOptions[i].name, xPos, -150, colorOptions[i].color, colorOptions[i].price);
colorButton.colorData = colorOptions[i];
colorButton.down = function () {
if (typeof coins !== 'undefined' && coins >= this.colorData.price) {
coins -= this.colorData.price;
currencyText.setText("Coins: " + coins);
storage.bulletBounce_coins = coins;
storage.bulletBounce_turretColor = this.colorData.color;
// Apply color to turret if it exists
if (turret) {
var turretBase = turret.getChildAt(0);
if (turretBase) {
turretBase.tint = this.colorData.color;
}
}
LK.effects.flashObject(this, 0xFFFFFF, 300);
}
};
shopContainer.addChild(colorButton);
colorButtons.push(colorButton);
}
// Create upgrade options
var upgradeOptions = [{
name: "MAX POWER",
description: "+20% Power",
price: 200,
type: "power"
}, {
name: "EXTRA SHOT",
description: "+1 Shot/Level",
price: 300,
type: "shots"
}, {
name: "BULLET LIFE",
description: "+3 Bounces",
price: 250,
type: "bounces"
}];
var upgradeButtons = [];
for (var i = 0; i < upgradeOptions.length; i++) {
var xPos = (i - 1) * 500;
var upgradeButton = createShopButton(upgradeOptions[i].name, xPos, 500, 0x3355FF, upgradeOptions[i].price);
var descText = new Text2(upgradeOptions[i].description, {
size: 75,
fill: 0xFFFFFF
});
descText.anchor.set(0.5, 0.5);
descText.y = 70;
upgradeButton.addChild(descText);
upgradeButton.upgradeData = upgradeOptions[i];
upgradeButton.down = function () {
if (typeof coins !== 'undefined' && coins >= this.upgradeData.price) {
coins -= this.upgradeData.price;
currencyText.setText("Coins: " + coins);
storage.bulletBounce_coins = coins;
// Apply upgrade based on type
switch (this.upgradeData.type) {
case "power":
// Increase max power
var powerUpgrade = storage.bulletBounce_powerUpgrade || 0;
powerUpgrade++;
storage.bulletBounce_powerUpgrade = powerUpgrade;
break;
case "shots":
// Increase shots per level
var shotsUpgrade = storage.bulletBounce_shotsUpgrade || 0;
shotsUpgrade++;
storage.bulletBounce_shotsUpgrade = shotsUpgrade;
break;
case "bounces":
// Increase max bounces
var bouncesUpgrade = storage.bulletBounce_bouncesUpgrade || 0;
bouncesUpgrade++;
storage.bulletBounce_bouncesUpgrade = bouncesUpgrade;
break;
}
LK.effects.flashObject(this, 0xFFFFFF, 300);
}
};
shopContainer.addChild(upgradeButton);
upgradeButtons.push(upgradeButton);
}
// Close button
var closeButton = new Container();
var closeBg = closeButton.attachAsset('powerMeter', {
anchorX: 0.5,
anchorY: 0.5,
width: 750,
height: 250,
tint: 0xFF0000
});
var closeText = new Text2("CLOSE", {
size: 150,
fill: 0xFFFFFF
});
closeText.anchor.set(0.5, 0.5);
closeButton.addChild(closeText);
closeButton.y = 800;
shopContainer.addChild(closeButton);
// Helper function to create shop buttons
function createShopButton(text, xPos, yPos, color, price) {
var button = new Container();
// Button background
var bg = button.attachAsset('powerMeter', {
anchorX: 0.5,
anchorY: 0.5,
width: 250,
height: 250,
tint: color
});
// Button text
var txt = new Text2(text, {
size: 80,
fill: 0xFFFFFF
});
txt.anchor.set(0.5, 0.5);
txt.y = -40;
button.addChild(txt);
// Price tag
var priceTxt = new Text2(price + " coins", {
size: 60,
fill: 0xFFD700
});
priceTxt.anchor.set(0.5, 0.5);
priceTxt.y = 30;
button.addChild(priceTxt);
// Position button
button.x = xPos;
button.y = yPos;
return button;
}
// Button interactions
closeButton.down = function () {
self.hide();
};
// Show and hide methods
self.show = function () {
// Update currency display
if (typeof coins !== 'undefined') {
currencyText.setText("Coins: " + coins);
}
self.visible = true;
// Animate appearance
self.alpha = 0;
tween(self, {
alpha: 1
}, {
duration: 300,
easing: tween.easeOut
});
};
self.hide = function () {
// Animate disappearance
tween(self, {
alpha: 0
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
self.visible = false;
gamePaused = false; // Resume game when shop is hidden
}
});
};
return self;
});
var Target = Container.expand(function () {
var self = Container.call(this);
var targetGraphics = self.attachAsset('target', {
anchorX: 0.5,
anchorY: 0.5
});
// Make targets 5x larger in baby mode
if (typeof isBabyMode !== 'undefined' && isBabyMode) {
targetGraphics.scale.set(5, 5);
}
self.isHit = false;
self.hit = function () {
if (self.isHit) {
return;
}
self.isHit = true;
LK.getSound('targetHit').play();
LK.effects.flashObject(self, 0xffffff, 300);
tween(targetGraphics, {
alpha: 0,
scaleX: 0.2,
scaleY: 0.2
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
targetGraphics.visible = false;
}
});
// Increment combo and update score with combo multiplier
comboCount++;
comboMultiplier = Math.min(5, 1 + Math.floor(comboCount / 2));
var scoreToAdd = 100 * comboMultiplier;
// Increment score with multiplier
LK.setScore(LK.getScore() + scoreToAdd);
// Award coins based on combo
var coinsToAdd = 5 * comboMultiplier;
coins += coinsToAdd;
storage.bulletBounce_coins = coins;
// Show floating coin text
var coinText = new Text2("+" + coinsToAdd, {
size: 80,
fill: 0xFFD700
});
coinText.anchor.set(0.5, 0.5);
coinText.x = self.x;
coinText.y = self.y;
game.addChild(coinText);
// Animate coin text
tween(coinText, {
y: coinText.y - 100,
alpha: 0
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
game.removeChild(coinText);
}
});
// Update combo text if it exists
if (comboText) {
comboText.setText('COMBO x' + comboMultiplier + ' (' + scoreToAdd + ' pts)');
comboText.visible = true;
// Make combo text pulse for visual feedback
tween(comboText, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(comboText, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 200,
easing: tween.easeIn
});
}
});
}
// Reset combo timer
if (comboTimer) {
LK.clearTimeout(comboTimer);
}
// Set new combo timer - combo resets after 2.5 seconds
comboTimer = LK.setTimeout(function () {
comboCount = 0;
comboMultiplier = 1;
if (comboText) {
comboText.visible = false;
}
}, 2500);
};
return self;
});
var TitleScreen = Container.expand(function () {
var self = Container.call(this);
// Create logo using the logo asset
var logo = self.attachAsset('gameLogo', {
anchorX: 0.5,
anchorY: 0.5,
width: 1500,
height: 1500 * (700 / 1167),
// Use correct aspect ratio (height/width) to maintain proportions
y: -300 // Move the logo up
});
// Start button
var startButton = new Container();
var buttonBg = startButton.attachAsset('powerMeter', {
anchorX: 0.5,
anchorY: 0.5,
width: 750,
height: 250,
tint: 0x22cc22
});
var buttonText = new Text2("PLAY", {
size: 175,
fill: 0xFFFFFF
});
buttonText.anchor.set(0.5, 0.5);
startButton.addChild(buttonText);
startButton.y = 500;
self.addChild(startButton);
// Add button interaction
startButton.down = function () {
self.visible = false;
difficultySelector.visible = true;
};
// Animation for logo
tween(logo, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 1000,
easing: tween.easeInOut,
loop: true,
yoyo: true
});
return self;
});
var Turret = Container.expand(function () {
var self = Container.call(this);
var base = self.attachAsset('turret', {
anchorX: 0.5,
anchorY: 0.5
});
var barrel = self.attachAsset('turretBarrel', {
anchorX: 0,
anchorY: 0.5,
x: 0,
y: 0
});
self.angle = 0;
self.power = 10;
// Apply power upgrade if available
var powerUpgrade = storage.bulletBounce_powerUpgrade || 0;
self.maxPower = 20 + powerUpgrade * 4; // Each upgrade adds 20% more power
// Apply saved turret color if available
if (storage.bulletBounce_turretColor) {
base.tint = storage.bulletBounce_turretColor;
}
self.isDragging = false;
self.isActive = true;
// Add pulse animation to make turret more dynamic
self.update = function () {
if (self.isActive) {
var pulse = Math.sin(LK.ticks * 0.05) * 0.05 + 1;
base.scale.set(pulse, pulse);
}
};
self.updateRotation = function (angle) {
self.angle = angle;
barrel.rotation = angle;
};
self.setPower = function (power) {
self.power = Math.max(5, Math.min(self.maxPower, power));
};
self.getShootVelocity = function () {
return {
x: Math.cos(self.angle) * self.power,
y: Math.sin(self.angle) * self.power
};
};
return self;
});
var Wall = Container.expand(function () {
var self = Container.call(this);
var wallGraphics = self.attachAsset('wall', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x222222
});
/****
* Game Code
****/
// Game variables
var turret;
var bullets = [];
var targets = [];
var walls = [];
var powerups = [];
var luckyBlocks = [];
var powerMeter;
var aimLine;
var isDragging = false;
var dragStartX, dragStartY;
var currentLevel = 1;
var shotsRemaining = 5;
var totalTargets = 0;
var targetsHit = 0;
var difficultySelector;
var titleScreen;
var deathScreen;
var pauseMenu;
var shopMenu;
var gamePaused = false;
var gameDifficulty = "normal"; // Default difficulty
var isBabyMode = false;
var isImpossibleMode = false;
var comboCount = 0;
var comboTimer = null;
var comboMultiplier = 1;
var comboText;
var coins = 0; // Currency for shop purchases
var shopButton; // Shop button reference
var activePowerUps = {
multishot: false,
bigball: false,
slowmotion: false,
explosive: false
};
var powerUpTimers = {};
// Function to apply powerup effects
function applyPowerUpEffect(type) {
switch (type) {
case "multishot":
activePowerUps.multishot = true;
powerUpTimers.multishot = LK.setTimeout(function () {
activePowerUps.multishot = false;
}, 15000); // 15 seconds
break;
case "bigball":
activePowerUps.bigball = true;
powerUpTimers.bigball = LK.setTimeout(function () {
activePowerUps.bigball = false;
}, 20000); // 20 seconds
break;
case "slowmotion":
activePowerUps.slowmotion = true;
// Visual effect for slow motion activation
LK.effects.flashScreen(0xaa00ff, 500);
// Create slow motion text indicator
var slowText = new Text2("SLOW MOTION", {
size: 200,
fill: 0xaa00ff
});
slowText.anchor.set(0.5, 0.5);
slowText.x = 0;
slowText.y = 0;
LK.gui.center.addChild(slowText);
// Animate slow motion text
tween(slowText, {
alpha: 0,
scaleX: 2,
scaleY: 2
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
LK.gui.center.removeChild(slowText);
}
});
// Slow down all existing bullets
for (var i = 0; i < bullets.length; i++) {
bullets[i].speedX *= 0.6;
bullets[i].speedY *= 0.6;
}
powerUpTimers.slowmotion = LK.setTimeout(function () {
activePowerUps.slowmotion = false;
// Show end of slow motion effect
LK.effects.flashScreen(0x444444, 300);
}, 10000); // 10 seconds
break;
case "extralife":
shotsRemaining += 3;
if (shotsTxt && typeof shotsTxt !== 'undefined') {
shotsTxt.setText('Shots: ' + shotsRemaining);
}
break;
case "explosive":
activePowerUps.explosive = true;
powerUpTimers.explosive = LK.setTimeout(function () {
activePowerUps.explosive = false;
}, 12000); // 12 seconds
break;
}
}
// Function to create a random powerup
function createPowerUp(x, y, type) {
var powerup = new PowerUp();
powerup.x = x;
powerup.y = y;
powerup.setType(type || getRandomPowerUpType());
game.addChild(powerup);
powerups.push(powerup);
return powerup;
}
// Function to create a lucky block
function createLuckyBlock(x, y) {
var luckyBlock = new LuckyBlock();
luckyBlock.x = x;
luckyBlock.y = y;
game.addChild(luckyBlock);
luckyBlocks.push(luckyBlock);
return luckyBlock;
}
// Function to get random powerup type
function getRandomPowerUpType() {
var types = ["multishot", "bigball", "slowmotion", "extralife", "explosive"];
return types[Math.floor(Math.random() * types.length)];
}
// Function to spawn random powerup at position
function spawnRandomPowerUp(x, y) {
createPowerUp(x, y);
}
// UI elements
var shotsTxt;
var levelTxt;
function initGame() {
// Check if game is starting fresh or resuming from difficulty selection
if (!difficultySelector || !difficultySelector.visible) {
// Create death screen first (initialized but hidden)
if (!deathScreen) {
deathScreen = new DeathScreen();
deathScreen.x = 2048 / 2;
deathScreen.y = 2732 / 2;
deathScreen.visible = false;
game.addChild(deathScreen);
}
// Create pause menu (initialized but hidden)
if (!pauseMenu) {
pauseMenu = new PauseMenu();
pauseMenu.x = 2048 / 2;
pauseMenu.y = 2732 / 2;
pauseMenu.visible = false;
game.addChild(pauseMenu);
}
// Create shop menu (initialized but hidden)
if (!shopMenu) {
shopMenu = new Shop();
shopMenu.x = 2048 / 2;
shopMenu.y = 2732 / 2;
shopMenu.visible = false;
game.addChild(shopMenu);
}
// Load saved coins
coins = storage.bulletBounce_coins || 0;
// Load saved turret color
var savedTurretColor = storage.bulletBounce_turretColor;
// Create turret
turret = new Turret();
turret.x = 100;
turret.y = 2732 - 100;
game.addChild(turret);
// Create aim line
aimLine = new AimLine();
aimLine.x = turret.x;
aimLine.y = turret.y;
game.addChild(aimLine);
// Create power meter
powerMeter = new PowerMeter();
powerMeter.x = 50;
powerMeter.y = 2732 - 200;
game.addChild(powerMeter);
powerMeter.setPower(turret.power, turret.maxPower);
// Setup UI
shotsTxt = new Text2('Shots: ' + shotsRemaining, {
size: 125,
fill: 0xFFFFFF
});
shotsTxt.anchor.set(0, 0);
shotsTxt.y = 20;
shotsTxt.x = -20;
LK.gui.topRight.addChild(shotsTxt);
levelTxt = new Text2('Level: ' + currentLevel, {
size: 50,
fill: 0xFFFFFF
});
levelTxt.anchor.set(0, 0);
levelTxt.y = 20;
LK.gui.top.addChild(levelTxt);
// Score text
var scoreTxt = new Text2('Score: 0', {
size: 50,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(1, 0);
scoreTxt.y = 180;
scoreTxt.x = -20;
LK.gui.topRight.addChild(scoreTxt);
// Combo text
comboText = new Text2('COMBO x1', {
size: 150,
fill: 0xFFFF00
});
comboText.anchor.set(0.5, 0.5);
comboText.x = 0;
comboText.y = -250;
comboText.visible = false;
LK.gui.center.addChild(comboText);
// Pause button
var pauseButton = new Container();
var pauseBg = pauseButton.attachAsset('centerCircle', {
anchorX: 0.5,
anchorY: 0.5,
width: 160,
height: 160,
tint: 0x444444,
alpha: 0.8
});
var pauseIcon = new Text2("II", {
size: 80,
fill: 0xFFFFFF
});
pauseIcon.anchor.set(0.5, 0.5);
pauseButton.addChild(pauseIcon);
pauseButton.x = -50;
pauseButton.y = 50;
LK.gui.topRight.addChild(pauseButton);
// Add pause button functionality
pauseButton.down = function () {
gamePaused = true;
pauseMenu.show();
};
// Shop button
shopButton = new Container();
var shopButtonBg = shopButton.attachAsset('shopIcon', {
anchorX: 0.5,
anchorY: 0.5,
width: 160,
height: 160,
tint: 0xFFD700,
alpha: 0.9
});
var shopText = new Text2("$", {
size: 100,
fill: 0x000000
});
shopText.anchor.set(0.5, 0.5);
shopButton.addChild(shopText);
shopButton.x = -50;
shopButton.y = 250;
LK.gui.topRight.addChild(shopButton);
// Shop button functionality
shopButton.down = function () {
gamePaused = true;
shopMenu.show();
};
// Coin display
var coinDisplay = new Container();
var coinIcon = coinDisplay.attachAsset('centerCircle', {
anchorX: 0.5,
anchorY: 0.5,
width: 80,
height: 80,
tint: 0xFFD700
});
var coinText = new Text2("" + coins, {
size: 80,
fill: 0xFFFFFF
});
coinText.anchor.set(0, 0.5);
coinText.x = 50;
coinDisplay.addChild(coinText);
coinDisplay.x = -20;
coinDisplay.y = 350;
LK.gui.topRight.addChild(coinDisplay);
// Update coin display regularly
LK.setInterval(function () {
coinText.setText("" + coins);
}, 100);
// Update score display
LK.setInterval(function () {
scoreTxt.setText('Score: ' + LK.getScore());
}, 100);
// Load level with current difficulty
loadLevel(currentLevel);
// Play background music
LK.playMusic('gameMusic');
}
}
function loadLevel(level) {
// Clear any existing level elements
clearLevel();
// Update level display
levelTxt.setText('Level: ' + level);
// Update shots based on difficulty
var shotsUpgrade = storage.bulletBounce_shotsUpgrade || 0;
var extraShots = shotsUpgrade;
switch (gameDifficulty) {
case "baby":
shotsRemaining = 50 + level + extraShots; // Way more shots in baby mode
break;
case "easy":
shotsRemaining = 5 + level + extraShots;
break;
case "normal":
shotsRemaining = 3 + level + extraShots;
break;
case "hard":
shotsRemaining = 2 + Math.floor(level / 2) + extraShots;
break;
case "impossible":
shotsRemaining = 1 + Math.floor(extraShots / 2); // Half benefit of shots for impossible mode
break;
default:
shotsRemaining = 3 + level + extraShots;
}
// Add null check before updating shotsTxt
if (shotsTxt && typeof shotsTxt !== 'undefined') {
shotsTxt.setText('Shots: ' + shotsRemaining);
}
// Clear powerups and lucky blocks
for (var i = powerups.length - 1; i >= 0; i--) {
game.removeChild(powerups[i]);
powerups.splice(i, 1);
}
for (var i = luckyBlocks.length - 1; i >= 0; i--) {
game.removeChild(luckyBlocks[i]);
luckyBlocks.splice(i, 1);
}
// Reset active powerups
for (var key in activePowerUps) {
activePowerUps[key] = false;
}
for (var key in powerUpTimers) {
LK.clearTimeout(powerUpTimers[key]);
}
// Apply baby mode permanent powerups
if (isBabyMode) {
// In baby mode, always have these powerups active
activePowerUps.multishot = true;
activePowerUps.bigball = true;
// Add a bunch of powerups right at the beginning
for (var i = 0; i < 5; i++) {
createPowerUp(300 + i * 200, 300 + i * 100);
}
}
// Different level layouts
switch (level) {
case 1:
// Basic level with random targets
var targetCount = isBabyMode ? 3 : isImpossibleMode ? 15 : 3;
for (var i = 0; i < targetCount; i++) {
var targetX = 500 + Math.random() * 1000;
var targetY = 300 + Math.random() * 800;
// In impossible mode, place targets in very difficult positions
if (isImpossibleMode) {
// Place some targets behind walls, at extreme edges, etc.
targetX = i % 3 === 0 ? 50 + Math.random() * 100 : 1900 + Math.random() * 100;
targetY = i % 2 === 0 ? 50 + Math.random() * 100 : 2500 + Math.random() * 100;
}
createTarget(targetX, targetY);
}
// Add random walls
if (isImpossibleMode) {
// Create many walls in impossible mode
for (var i = 0; i < 10; i++) {
createWall(100 + i * 200, 400, Math.PI / 2); // Horizontal barrier
}
// Create a protective wall around the targets
createWall(1800, 100, 0);
createWall(1800, 400, 0);
createWall(1800, 700, 0);
} else if (!isBabyMode) {
createWall(500 + Math.random() * 200, 500 + Math.random() * 200, Math.random() * Math.PI / 2);
createWall(1300 + Math.random() * 200, 900 + Math.random() * 200, Math.random() * Math.PI / 2);
}
// Ensure at least one lucky block in level 1 and increase chance with level
var luckyBlockCount = 1; // At least one guaranteed
// Add more based on level-scaling chance
for (var i = 0; i < 2; i++) {
if (Math.random() < 0.2 + level * 0.05) {
// Chance increases with level
luckyBlockCount++;
}
}
if (isBabyMode) {
luckyBlockCount += 2;
}
for (var i = 0; i < luckyBlockCount; i++) {
createLuckyBlock(500 + Math.random() * 1000, 500 + Math.random() * 800);
}
break;
case 2:
// More complex level with random targets
var targetCount = isBabyMode ? 4 : isImpossibleMode ? 20 : 5;
for (var i = 0; i < targetCount; i++) {
var targetX = 400 + Math.random() * 1200;
var targetY = 400 + Math.random() * 1100;
// In impossible mode, place targets in very difficult positions
if (isImpossibleMode) {
targetX = i % 3 === 0 ? 50 + Math.random() * 100 : 1900 + Math.random() * 100;
targetY = i % 4 === 0 ? 50 + Math.random() * 100 : 2500 + Math.random() * 100;
}
createTarget(targetX, targetY);
}
// Add random walls
if (isImpossibleMode) {
// Create a complex maze in impossible mode
for (var i = 0; i < 15; i++) {
createWall(300 + i * 100, 400, 0);
createWall(300 + i * 100, 1000, 0);
}
} else if (!isBabyMode) {
createWall(400 + Math.random() * 100, 800 + Math.random() * 100, Math.random() * Math.PI / 4);
createWall(1500 + Math.random() * 100, 800 + Math.random() * 100, Math.random() * Math.PI / 4);
createWall(900 + Math.random() * 200, 300 + Math.random() * 100, Math.PI / 2 + Math.random() * 0.3);
createWall(900 + Math.random() * 200, 1400 + Math.random() * 100, Math.PI / 2 + Math.random() * 0.3);
}
// Ensure at least one lucky block in level 2 and increase chance with level
var luckyBlockCount = 1; // At least one guaranteed
// Add more based on level-scaling chance
for (var i = 0; i < 3; i++) {
if (Math.random() < 0.25 + level * 0.05) {
// Chance increases with level
luckyBlockCount++;
}
}
if (isBabyMode) {
luckyBlockCount += 3;
}
for (var i = 0; i < luckyBlockCount; i++) {
createLuckyBlock(400 + Math.random() * 1200, 400 + Math.random() * 1100);
}
break;
case 3:
// Advanced level with random elements
var targetCount = isBabyMode ? 5 : isImpossibleMode ? 25 : 7;
for (var i = 0; i < targetCount; i++) {
// Create targets in different quadrants of the screen
if (isImpossibleMode) {
// In impossible mode, place targets in extremely difficult positions
var quadrant = i % 4;
var xOffset = quadrant === 0 || quadrant === 2 ? 50 : 1900;
var yOffset = quadrant === 0 || quadrant === 1 ? 50 : 2500;
createTarget(xOffset + Math.random() * 50, yOffset + Math.random() * 50);
} else {
if (i < 2) {
createTarget(300 + Math.random() * 400, 300 + Math.random() * 400);
} else if (i < 4) {
createTarget(1300 + Math.random() * 400, 300 + Math.random() * 400);
} else if (i < 6) {
createTarget(300 + Math.random() * 400, 1100 + Math.random() * 400);
} else {
createTarget(1300 + Math.random() * 400, 1100 + Math.random() * 400);
}
}
}
// Add walls
if (isImpossibleMode) {
// Create a complex defensive wall system in impossible mode
for (var i = 0; i < 20; i++) {
// Create a circular defensive wall
var angle = i / 20 * Math.PI * 2;
var radius = 800;
var centerX = 1024;
var centerY = 1366;
var wallX = centerX + Math.cos(angle) * radius;
var wallY = centerY + Math.sin(angle) * radius;
createWall(wallX, wallY, angle);
}
} else if (!isBabyMode) {
createWall(600 + Math.random() * 100, 600 + Math.random() * 100, Math.random() * 0.2);
createWall(1300 + Math.random() * 100, 600 + Math.random() * 100, Math.random() * 0.2);
createWall(600 + Math.random() * 100, 1200 + Math.random() * 100, Math.random() * 0.2);
createWall(1300 + Math.random() * 100, 1200 + Math.random() * 100, Math.random() * 0.2);
createWall(1000 + Math.random() * 100, 600 + Math.random() * 100, Math.PI / 2 + Math.random() * 0.2);
createWall(1000 + Math.random() * 100, 1200 + Math.random() * 100, Math.PI / 2 + Math.random() * 0.2);
}
// Ensure at least one lucky block in level 3 and increase chance with level
var luckyBlockCount = 1; // At least one guaranteed
// Add more based on level-scaling chance
for (var i = 0; i < 4; i++) {
if (Math.random() < 0.3 + level * 0.05) {
// Chance increases with level
luckyBlockCount++;
}
}
if (isBabyMode) {
luckyBlockCount += 4;
}
// Place lucky blocks in each quadrant to ensure availability
for (var i = 0; i < luckyBlockCount; i++) {
var quadrant = i % 4;
var xBase = quadrant === 0 || quadrant === 2 ? 300 : 1300;
var yBase = quadrant === 0 || quadrant === 1 ? 300 : 1100;
createLuckyBlock(xBase + Math.random() * 400, yBase + Math.random() * 400);
}
break;
default:
// For levels beyond 3, create a fully random level with more targets and walls
// Adjust difficulty based on selected mode
var difficultyMultiplier;
switch (gameDifficulty) {
case "baby":
difficultyMultiplier = 0.3; // Much fewer targets and walls for baby mode
break;
case "easy":
difficultyMultiplier = 0.7; // Fewer targets and walls
break;
case "normal":
difficultyMultiplier = 1.0; // Normal difficulty
break;
case "hard":
difficultyMultiplier = 1.5; // More targets and walls
break;
case "impossible":
difficultyMultiplier = 3.0; // Way more targets and walls for impossible mode
break;
default:
difficultyMultiplier = 1.0;
}
// Generate random number of targets based on level and difficulty
var numTargets = Math.max(2, Math.floor((3 + Math.random() * level * 2) * difficultyMultiplier));
for (var i = 0; i < numTargets; i++) {
var targetX = 300 + Math.random() * 1400;
var targetY = 300 + Math.random() * 1500;
// In impossible mode, position targets in very difficult places
if (isImpossibleMode) {
if (i % 3 === 0) {
// Place targets at extreme edges
targetX = Math.random() < 0.5 ? 50 + Math.random() * 50 : 1948 - Math.random() * 50;
targetY = Math.random() < 0.5 ? 50 + Math.random() * 50 : 2682 - Math.random() * 50;
}
}
createTarget(targetX, targetY);
}
// Generate random number of walls based on level and difficulty
if (isImpossibleMode) {
// Create complex wall patterns for impossible mode
var wallCount = 20 + Math.floor(level * 2);
for (var i = 0; i < wallCount; i++) {
// Create walls that make targets very hard to hit
var wallX = 300 + Math.random() * 1400;
var wallY = 300 + Math.random() * 1500;
var wallRotation = Math.random() * Math.PI;
// Some walls positioned strategically to block shots
if (i % 5 === 0) {
wallX = 200;
wallY = 400 + i / 5 * 300;
wallRotation = 0;
}
createWall(wallX, wallY, wallRotation);
}
} else if (!isBabyMode) {
var numWalls = Math.max(2, Math.floor(Math.random() * level * 1.5 * difficultyMultiplier));
for (var i = 0; i < numWalls; i++) {
createWall(300 + Math.random() * 1400, 300 + Math.random() * 1500, Math.random() * Math.PI);
}
}
// Add powerups - tons more in baby mode
var numPowerups = isBabyMode ? 10 + Math.floor(level) : isImpossibleMode ? 0 : 1 + Math.floor(level / 3);
for (var i = 0; i < numPowerups; i++) {
createPowerUp(300 + Math.random() * 1400, 300 + Math.random() * 1500);
}
// Add lucky blocks - always at least one in each level, more in baby mode, chance increases with level
// In impossible mode, still add at least one block
var numLuckyBlocks = isBabyMode ? 5 + Math.floor(level / 2) : isImpossibleMode ? 1 : 1;
// Add additional lucky blocks based on level progression (chance increases with level)
var chanceForExtraBlocks = Math.min(0.8, 0.2 + level * 0.07); // Cap at 80% chance, slightly higher scaling
// Higher levels should have more chances for blocks
var numChances = Math.max(3, Math.floor(level * 1.5));
for (var i = 0; i < numChances; i++) {
if (Math.random() < chanceForExtraBlocks) {
numLuckyBlocks++;
}
}
// Create lucky blocks
for (var i = 0; i < numLuckyBlocks; i++) {
createLuckyBlock(300 + Math.random() * 1400, 300 + Math.random() * 1500);
}
break;
}
// Track targets for win condition
totalTargets = targets.length;
targetsHit = 0;
}
function clearLevel() {
// Remove all bullets
for (var i = bullets.length - 1; i >= 0; i--) {
game.removeChild(bullets[i]);
bullets.splice(i, 1);
}
// Remove all targets
for (var i = targets.length - 1; i >= 0; i--) {
game.removeChild(targets[i]);
targets.splice(i, 1);
}
// Remove all walls
for (var i = walls.length - 1; i >= 0; i--) {
game.removeChild(walls[i]);
walls.splice(i, 1);
}
}
function createTarget(x, y) {
var target = new Target();
target.x = x;
target.y = y;
game.addChild(target);
targets.push(target);
return target;
}
function createWall(x, y, rotation) {
var wall = new Wall();
wall.x = x;
wall.y = y;
wall.rotation = rotation;
game.addChild(wall);
walls.push(wall);
return wall;
}
function fireBullet() {
if (shotsRemaining <= 0) {
return;
}
// Reset combo when firing a new bullet
comboCount = 0;
comboMultiplier = 1;
if (comboText) {
comboText.visible = false;
}
if (comboTimer) {
LK.clearTimeout(comboTimer);
comboTimer = null;
}
// Decrease shots remaining
shotsRemaining--;
if (shotsTxt && typeof shotsTxt !== 'undefined') {
shotsTxt.setText('Shots: ' + shotsRemaining);
}
// Determine how many bullets to fire based on powerups
var bulletCount = activePowerUps.multishot ? 3 : 1;
for (var i = 0; i < bulletCount; i++) {
// Create new bullet
var bullet = new Bullet();
// Add null check to ensure turret exists before accessing its properties
if (turret) {
bullet.x = turret.x;
bullet.y = turret.y;
} else {
// Set default position if turret doesn't exist
bullet.x = 100;
bullet.y = 2732 - 100;
}
// Get velocity from turret angle and power
var velocity = turret ? turret.getShootVelocity() : {
x: 0,
y: 0
};
// If multishot, spread the bullets
if (activePowerUps.multishot && bulletCount > 1) {
var spreadAngle = (i - 1) * 0.2; // -0.2, 0, 0.2 radians spread
var newAngle = Math.atan2(velocity.y, velocity.x) + spreadAngle;
var speed = Math.sqrt(velocity.x * velocity.x + velocity.y * velocity.y);
bullet.speedX = Math.cos(newAngle) * speed;
bullet.speedY = Math.sin(newAngle) * speed;
} else {
bullet.speedX = velocity.x;
bullet.speedY = velocity.y;
}
// Apply slowmotion powerup
if (activePowerUps.slowmotion) {
bullet.speedX *= 0.6;
bullet.speedY *= 0.6;
}
// Apply big ball powerup
if (activePowerUps.bigball) {
bullet.scale.set(2.5, 2.5);
}
// Apply explosive powerup if needed
if (activePowerUps.explosive) {
bullet.isExplosive = true;
}
// Add to game
game.addChild(bullet);
bullets.push(bullet);
}
// Play sound
LK.getSound('shoot').play();
// Flash turret - add null check to prevent script error
if (turret) {
LK.effects.flashObject(turret, 0xffffff, 200);
}
}
function checkCollisions() {
// Check all active bullets
for (var i = bullets.length - 1; i >= 0; i--) {
var bullet = bullets[i];
if (!bullet.isActive) {
// Remove inactive bullets
game.removeChild(bullet);
bullets.splice(i, 1);
continue;
}
// Check for wall collisions
for (var j = 0; j < walls.length; j++) {
var wall = walls[j];
if (bullet.intersects(wall)) {
// Calculate bounce direction based on wall angle
var wallAngle = wall.rotation;
var normalAngle = wallAngle + Math.PI / 2;
// Reflect velocity based on wall normal
var speed = Math.sqrt(bullet.speedX * bullet.speedX + bullet.speedY * bullet.speedY);
var bulletAngle = Math.atan2(bullet.speedY, bullet.speedX);
var reflectionAngle = 2 * normalAngle - bulletAngle;
bullet.speedX = Math.cos(reflectionAngle) * speed * 0.8; // Lose some energy
bullet.speedY = Math.sin(reflectionAngle) * speed * 0.8;
// Move bullet slightly away from wall to prevent multiple collisions
bullet.x += bullet.speedX;
bullet.y += bullet.speedY;
bullet.bounces++;
LK.getSound('bounce').play();
}
}
// Check for target collisions
for (var j = 0; j < targets.length; j++) {
var target = targets[j];
if (!target.isHit && bullet.intersects(target)) {
target.hit();
targetsHit++;
// If explosive bullet, create explosion effect
if (bullet.isExplosive) {
// Hit nearby targets within radius
var explosionRadius = 250;
for (var k = 0; k < targets.length; k++) {
if (j !== k && !targets[k].isHit) {
var dx = targets[k].x - target.x;
var dy = targets[k].y - target.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < explosionRadius) {
targets[k].hit();
targetsHit++;
}
}
}
// Visual explosion effect
LK.effects.flashScreen(0xFF5500, 200);
}
// Check if level complete
if (targetsHit >= totalTargets) {
levelComplete();
}
}
}
// Check for lucky block collisions
for (var j = luckyBlocks.length - 1; j >= 0; j--) {
var luckyBlock = luckyBlocks[j];
if (!luckyBlock.isHit && bullet.intersects(luckyBlock)) {
luckyBlock.hit();
luckyBlocks.splice(j, 1);
}
}
// Check for powerup collisions
for (var j = powerups.length - 1; j >= 0; j--) {
var powerup = powerups[j];
if (!powerup.isCollected && bullet.intersects(powerup)) {
powerup.collect();
powerups.splice(j, 1);
}
}
}
}
function levelComplete() {
LK.setTimeout(function () {
// Advance to next level
currentLevel++;
loadLevel(currentLevel);
}, 1000);
}
function checkGameOver() {
// Game over conditions
if (shotsRemaining <= 0 && bullets.length === 0) {
// Check if any targets left
var anyTargetsLeft = false;
for (var i = 0; i < targets.length; i++) {
if (!targets[i].isHit) {
anyTargetsLeft = true;
break;
}
}
if (anyTargetsLeft) {
// Player has lost
LK.setTimeout(function () {
if (isBabyMode) {
// In baby mode, show custom death screen instead of game over
deathScreen.showMessage(true);
} else {
LK.showGameOver();
}
}, 1000);
}
}
}
function calculateAimAngle(x, y) {
// Calculate angle from turret to mouse/touch point
// Check if turret exists before accessing its properties
if (!turret) {
return 0; // Return default angle if turret doesn't exist yet
}
var dx = x - turret.x;
var dy = y - turret.y;
return Math.atan2(dy, dx);
}
function updateAimLine() {
if (turret && aimLine) {
aimLine.update(turret.angle, 200);
}
}
// Load previously selected difficulty if available
var savedDifficulty = storage.bulletBounce_difficulty;
if (savedDifficulty) {
gameDifficulty = savedDifficulty;
}
// Create title screen
titleScreen = new TitleScreen();
titleScreen.x = 2048 / 2;
titleScreen.y = 2732 / 2;
game.addChild(titleScreen);
// Create difficulty selector (initially hidden)
difficultySelector = new Difficulty();
difficultySelector.x = 2048 / 2;
difficultySelector.y = 2732 / 2;
difficultySelector.visible = false;
game.addChild(difficultySelector);
// If we just want to start the game directly with saved difficulty, uncomment this:
// titleScreen.visible = false;
// difficultySelector.visible = false;
// initGame();
// Event handlers
game.down = function (x, y, obj) {
// Skip if game is paused
if (gamePaused) {
return;
}
isDragging = true;
dragStartX = x;
dragStartY = y;
// Set initial angle
var angle = calculateAimAngle(x, y);
if (turret) {
// Add null check to ensure turret exists
turret.updateRotation(angle);
}
updateAimLine();
};
game.move = function (x, y, obj) {
// Skip if game is paused
if (gamePaused) {
return;
}
if (isDragging) {
// Update turret angle
var angle = calculateAimAngle(x, y);
if (turret) {
// Add null check to ensure turret exists
turret.updateRotation(angle);
}
// Calculate power based on drag distance
var dx = x - dragStartX;
var dy = y - dragStartY;
var dragDistance = Math.sqrt(dx * dx + dy * dy);
if (turret) {
var power = Math.min(turret.maxPower, dragDistance / 50);
turret.setPower(power);
// Update power meter
powerMeter.setPower(turret.power, turret.maxPower);
}
// Update aim line
updateAimLine();
}
};
game.up = function (x, y, obj) {
// Skip if game is paused
if (gamePaused) {
return;
}
if (isDragging) {
isDragging = false;
fireBullet();
}
};
// Game update loop
game.update = function () {
// Skip updates if game is paused
if (gamePaused) {
return;
}
// Update all bullets
for (var i = 0; i < bullets.length; i++) {
bullets[i].update();
}
// Check for collisions
checkCollisions();
// Check for game over
checkGameOver();
};
A ball with fire launching to the left with white painted text that says “BULLET BOUNCE!” At an angle on the ball. Single Game Texture. In-Game asset. 2d. No background. High contrast. No shadows
Metal bar. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
Yellow bar. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows