User prompt
Make a shop where you can buy upgrades and different color turrets and make the shop icon an asset
User prompt
Make a level editor
User prompt
Stop it from auto firing and make it so you can press two arrow sprites to go up or down
User prompt
Remove all level design from side scroll mode
User prompt
Add a button to play the new mode
User prompt
Add the button to play the new mode ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Add a side scroller mode where you can swipe up or down to make the turret go up or down lanes where enemies are coming from the right
User prompt
Make the hit boxes of the walls not kill you on touch
User prompt
Make all level stuff 1x again and zoom back in
User prompt
Undo
User prompt
Everything in the levels 2x besides from what you already did and zoom out
User prompt
Make all level sprites bigger and make zoom out larger
User prompt
Make lucky block an asset
User prompt
Make score text 1x and make pause button 2x
User prompt
Make level text, pause button 1x and make difficulty button close together without overlapping and move select difficulty text up a bit
User prompt
Make all UI 2.5x except for the logo and evenly spaced everything apart so no overlap
User prompt
Move the logo up
User prompt
Make the logo 3x
User prompt
More
User prompt
Make it so there is at least one lucky block in each level and the chance of more spawning goes up by the levels
User prompt
More
User prompt
You just squished it more. Make it taller
User prompt
Make the logo not squished
User prompt
Take the customization out of the game and make the logo an asset
/**** * 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, scaleX: 1, scaleY: 1 }); 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 if (typeof gameDifficulty !== 'undefined') { switch (gameDifficulty) { case "easy": self.maxBounces = 10; // More bounces for easy mode break; case "normal": self.maxBounces = 8; break; case "hard": self.maxBounces = 5; // Fewer bounces for hard mode break; default: self.maxBounces = 8; } } else { self.maxBounces = 8; // 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 < 60 || self.x > 1988) { self.speedX *= -0.9; // Lose some energy on bounce if (self.x < 60) { self.x = 60; } if (self.x > 1988) { self.x = 1988; } self.bounces++; LK.getSound('bounce').play(); } if (self.y < 60 || self.y > 2672) { self.speedY *= -0.9; // Lose some energy on bounce if (self.y < 60) { self.y = 60; } if (self.y > 2672) { self.y = 2672; } 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 LevelEditor = Container.expand(function () { var self = Container.call(this); // Editor state self.currentTool = "target"; // current selected tool self.isPlacing = false; // whether we're currently placing an object self.savedLevels = []; // array to store custom levels self.gridSize = 50; // snap to grid size self.showGrid = true; // whether to show grid // Grid background var grid = new Container(); self.addChild(grid); // Editor UI background var editorBg = self.attachAsset('centerCircle', { anchorX: 0.5, anchorY: 0.5, width: 2048, height: 2732, tint: 0x222222, alpha: 0.9 }); // Title var titleText = new Text2("LEVEL EDITOR", { size: 180, fill: 0xFFFFFF }); titleText.anchor.set(0.5, 0); titleText.y = 50; self.addChild(titleText); // Tool buttons var toolsContainer = new Container(); toolsContainer.y = 250; self.addChild(toolsContainer); // Create tool buttons var tools = [{ name: "target", color: 0x44AA44, label: "TARGET" }, { name: "wall", color: 0x4477AA, label: "WALL" }, { name: "luckyBlock", color: 0xAA8844, label: "LUCKY BLOCK" }, { name: "powerUp", color: 0xAA44AA, label: "POWER-UP" }, { name: "clear", color: 0xAA4444, label: "CLEAR" }]; var toolButtons = []; var buttonWidth = 300; var buttonHeight = 120; var spacing = 20; var buttonsPerRow = 3; for (var i = 0; i < tools.length; i++) { var row = Math.floor(i / buttonsPerRow); var col = i % buttonsPerRow; var xPos = (col - buttonsPerRow / 2 + 0.5) * (buttonWidth + spacing); var yPos = row * (buttonHeight + spacing); var button = createButton(tools[i].label, xPos, yPos, tools[i].color, tools[i].name); toolsContainer.addChild(button); toolButtons.push(button); } // Create save and test buttons var saveButton = createButton("SAVE LEVEL", -200, 650, 0x22AA22, "save"); var testButton = createButton("TEST LEVEL", 200, 650, 0xAA22AA, "test"); var backButton = createButton("BACK", 0, 800, 0xAA2222, "back"); self.addChild(saveButton); self.addChild(testButton); self.addChild(backButton); // Grid toggle button var gridButton = createButton("TOGGLE GRID", 0, 550, 0x444444, "grid"); self.addChild(gridButton); // Edit area var editArea = new Container(); editArea.y = 900; self.addChild(editArea); var editAreaBg = editArea.attachAsset('centerCircle', { anchorX: 0.5, anchorY: 0, width: 1800, height: 1500, tint: 0x111111, alpha: 0.5 }); // Level items container var levelItems = new Container(); editArea.addChild(levelItems); function createButton(text, xPos, yPos, color, action) { var button = new Container(); // Button background var bg = button.attachAsset('powerMeter', { anchorX: 0.5, anchorY: 0.5, width: action === "save" || action === "test" || action === "back" || action === "grid" ? 350 : buttonWidth, height: buttonHeight, tint: color }); // Button text var txt = new Text2(text, { size: 60, 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 () { if (tools.some(function (t) { return t.name === action; })) { // Tool selection self.currentTool = action; updateButtonHighlights(); } else if (action === "save") { saveCurrentLevel(); } else if (action === "test") { testCurrentLevel(); } else if (action === "back") { exitEditor(); } else if (action === "grid") { toggleGrid(); } }; return button; } // Update button highlights based on selected tool function updateButtonHighlights() { for (var i = 0; i < toolButtons.length; i++) { var isSelected = tools[i].name === self.currentTool; var scale = isSelected ? 1.1 : 1.0; toolButtons[i].scale.set(scale, scale); // Add glow effect to selected tool if (isSelected) { LK.effects.flashObject(toolButtons[i], 0xFFFFFF, 300); } } } // Save current level function saveCurrentLevel() { var level = { targets: [], walls: [], luckyBlocks: [], powerUps: [] }; // Save all placed items for (var i = 0; i < levelItems.children.length; i++) { var item = levelItems.children[i]; if (item.itemType === "target") { level.targets.push({ x: item.x, y: item.y }); } else if (item.itemType === "wall") { level.walls.push({ x: item.x, y: item.y, rotation: item.rotation }); } else if (item.itemType === "luckyBlock") { level.luckyBlocks.push({ x: item.x, y: item.y }); } else if (item.itemType === "powerUp") { level.powerUps.push({ x: item.x, y: item.y, type: item.powerUpType }); } } self.savedLevels.push(level); // Save to storage storage.customLevels = JSON.stringify(self.savedLevels); // Show save confirmation var saveConfirm = new Text2("Level Saved!", { size: 100, fill: 0x22FF22 }); saveConfirm.anchor.set(0.5, 0.5); saveConfirm.x = 0; saveConfirm.y = 400; self.addChild(saveConfirm); // Fade out confirmation tween(saveConfirm, { alpha: 0, y: 350 }, { duration: 2000, easing: tween.easeOut, onFinish: function onFinish() { self.removeChild(saveConfirm); } }); } // Test the current level function testCurrentLevel() { // Create a temporary level var tempLevel = { targets: [], walls: [], luckyBlocks: [], powerUps: [] }; // Add all placed items for (var i = 0; i < levelItems.children.length; i++) { var item = levelItems.children[i]; if (item.itemType === "target") { tempLevel.targets.push({ x: item.x, y: item.y }); } else if (item.itemType === "wall") { tempLevel.walls.push({ x: item.x, y: item.y, rotation: item.rotation }); } else if (item.itemType === "luckyBlock") { tempLevel.luckyBlocks.push({ x: item.x, y: item.y }); } else if (item.itemType === "powerUp") { tempLevel.powerUps.push({ x: item.x, y: item.y, type: item.powerUpType }); } } // Store current test level storage.currentTestLevel = JSON.stringify(tempLevel); // Hide editor and start test mode self.visible = false; currentTestingCustomLevel = true; initGame(); } // Exit editor back to main menu function exitEditor() { self.visible = false; if (titleScreen) { titleScreen.visible = true; } } // Toggle grid display function toggleGrid() { self.showGrid = !self.showGrid; updateGrid(); } // Update grid display function updateGrid() { // Clear existing grid while (grid.children.length > 0) { grid.removeChild(grid.children[0]); } // If grid is disabled, stop here if (!self.showGrid) { return; } // Draw grid lines for (var x = -900; x <= 900; x += self.gridSize) { var vLine = LK.getAsset('aimLine', { anchorX: 0.5, anchorY: 0, height: 1500, width: 1, tint: 0x444444, alpha: 0.3 }); vLine.x = x; vLine.y = 0; grid.addChild(vLine); } for (var y = 0; y <= 1500; y += self.gridSize) { var hLine = LK.getAsset('aimLine', { anchorX: 0, anchorY: 0.5, height: 1, width: 1800, tint: 0x444444, alpha: 0.3, rotation: Math.PI / 2 }); hLine.x = -900; hLine.y = y; grid.addChild(hLine); } } // Clear all placed items function clearItems() { while (levelItems.children.length > 0) { levelItems.removeChild(levelItems.children[0]); } } // Event handlers for editing editArea.down = function (x, y, obj) { // Convert to edit area coordinates var editX = x; var editY = y - editArea.y; if (self.currentTool === "clear") { clearItems(); return; } self.isPlacing = true; // Snap to grid if (self.showGrid) { editX = Math.round(editX / self.gridSize) * self.gridSize; editY = Math.round(editY / self.gridSize) * self.gridSize; } // Check that we're within bounds if (editX < -900 || editX > 900 || editY < 0 || editY > 1500) { return; } // Place item based on current tool if (self.currentTool === "target") { placeTarget(editX, editY); } else if (self.currentTool === "wall") { placeWall(editX, editY); } else if (self.currentTool === "luckyBlock") { placeLuckyBlock(editX, editY); } else if (self.currentTool === "powerUp") { placePowerUp(editX, editY); } }; editArea.move = function (x, y, obj) { if (!self.isPlacing) { return; } // For drag operations if needed }; editArea.up = function (x, y, obj) { self.isPlacing = false; }; // Placement functions function placeTarget(x, y) { var target = LK.getAsset('target', { anchorX: 0.5, anchorY: 0.5, scaleX: 1, scaleY: 1 }); target.x = x; target.y = y; target.itemType = "target"; // Mark item type for saving target.interactive = true; // Allow targets to be deleted by clicking them again target.down = function () { levelItems.removeChild(target); }; levelItems.addChild(target); } function placeWall(x, y) { var wall = LK.getAsset('wall', { anchorX: 0.5, anchorY: 0.5, scaleX: 1, scaleY: 1 }); wall.x = x; wall.y = y; wall.rotation = 0; // Default rotation wall.itemType = "wall"; // Mark item type for saving wall.interactive = true; // Allow walls to be rotated or deleted wall.down = function () { if (Date.now() - (wall.lastClick || 0) < 300) { // Double click to delete levelItems.removeChild(wall); } else { // Single click to rotate wall.rotation += Math.PI / 4; if (wall.rotation >= Math.PI * 2) { wall.rotation -= Math.PI * 2; } } wall.lastClick = Date.now(); }; levelItems.addChild(wall); } function placeLuckyBlock(x, y) { var luckyBlock = LK.getAsset('luckyBlock', { anchorX: 0.5, anchorY: 0.5, scaleX: 1, scaleY: 1 }); luckyBlock.x = x; luckyBlock.y = y; luckyBlock.itemType = "luckyBlock"; // Mark item type for saving luckyBlock.interactive = true; // Add a question mark var questionMark = new Text2("?", { size: 50, fill: 0x000000 }); questionMark.anchor.set(0.5, 0.5); luckyBlock.addChild(questionMark); // Allow lucky blocks to be deleted by clicking them again luckyBlock.down = function () { levelItems.removeChild(luckyBlock); }; levelItems.addChild(luckyBlock); } function placePowerUp(x, y) { var powerUpTypes = ["multishot", "bigball", "slowmotion", "extralife", "explosive"]; var typeColors = [0xff5500, 0x00aaff, 0xaa00ff, 0xff0055, 0xff0000]; var typeTexts = ["3x", "BIG", "SLOW", "LIFE", "BOOM"]; // Cycle through power-up types var typeIndex = levelItems.powerUpTypeIndex || 0; var type = powerUpTypes[typeIndex]; var color = typeColors[typeIndex]; var text = typeTexts[typeIndex]; // Update for next placement typeIndex = (typeIndex + 1) % powerUpTypes.length; levelItems.powerUpTypeIndex = typeIndex; // Create power-up var powerUp = new Container(); var bg = powerUp.attachAsset('centerCircle', { anchorX: 0.5, anchorY: 0.5, width: 240, height: 240, tint: color }); var label = new Text2(text, { size: 25, fill: 0xFFFFFF }); label.anchor.set(0.5, 0.5); powerUp.addChild(label); powerUp.x = x; powerUp.y = y; powerUp.itemType = "powerUp"; // Mark item type for saving powerUp.powerUpType = type; // Remember specific power-up type powerUp.interactive = true; // Allow power-ups to be deleted by clicking them again powerUp.down = function () { levelItems.removeChild(powerUp); }; levelItems.addChild(powerUp); } // Initialize updateButtonHighlights(); updateGrid(); // Load saved levels if available var loadedLevels = storage.customLevels; if (loadedLevels) { try { self.savedLevels = JSON.parse(loadedLevels); } catch (e) { self.savedLevels = []; } } return self; }); var LuckyBlock = Container.expand(function () { var self = Container.call(this); var blockGraphics = self.attachAsset('luckyBlock', { anchorX: 0.5, anchorY: 0.5, scaleX: 1, scaleY: 1 }); // 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 (currentTestingCustomLevel && levelEditor) { // Return to level editor if we were testing a custom level currentTestingCustomLevel = false; levelEditor.visible = true; } else 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: 240, height: 240 }); // 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 Target = Container.expand(function () { var self = Container.call(this); var targetGraphics = self.attachAsset('target', { anchorX: 0.5, anchorY: 0.5, scaleX: 1, scaleY: 1 }); // 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); // 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 = 350; self.addChild(startButton); // Level editor button var editorButton = new Container(); var editorBg = editorButton.attachAsset('powerMeter', { anchorX: 0.5, anchorY: 0.5, width: 750, height: 250, tint: 0x4477AA }); var editorText = new Text2("LEVEL EDITOR", { size: 130, fill: 0xFFFFFF }); editorText.anchor.set(0.5, 0.5); editorButton.addChild(editorText); editorButton.y = 650; self.addChild(editorButton); // Add button interactions startButton.down = function () { self.visible = false; difficultySelector.visible = true; }; editorButton.down = function () { self.visible = false; if (!levelEditor) { levelEditor = new LevelEditor(); levelEditor.x = 2048 / 2; levelEditor.y = 0; game.addChild(levelEditor); } else { levelEditor.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, scaleX: 1, scaleY: 1 }); var barrel = self.attachAsset('turretBarrel', { anchorX: 0, anchorY: 0.5, x: 0, y: 0, scaleX: 1, scaleY: 1 }); self.angle = 0; self.power = 10; self.maxPower = 20; 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, scaleX: 1, scaleY: 1 }); return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x222222, viewport: { width: 2048, height: 2732 } // Zoom back in }); /**** * 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 levelEditor; 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 currentTestingCustomLevel = false; 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) && (!levelEditor || !levelEditor.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 turret turret = new Turret(); turret.x = 100; // Reset turret position turret.y = 2732 - 100; // Reset turret position 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); // Back to editor button for custom levels var backToEditorButton; if (currentTestingCustomLevel) { backToEditorButton = new Container(); var buttonBg = backToEditorButton.attachAsset('centerCircle', { anchorX: 0.5, anchorY: 0.5, width: 320, height: 320, tint: 0x4477AA, alpha: 0.8 }); var buttonText = new Text2("EDIT", { size: 60, fill: 0xFFFFFF }); buttonText.anchor.set(0.5, 0.5); backToEditorButton.addChild(buttonText); backToEditorButton.x = -50; backToEditorButton.y = 170; LK.gui.topRight.addChild(backToEditorButton); backToEditorButton.down = function () { currentTestingCustomLevel = false; // Hide 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; } // Clear level clearLevel(); // Show level editor if (levelEditor) { levelEditor.visible = true; } }; } // Pause button var pauseButton = new Container(); var pauseBg = pauseButton.attachAsset('centerCircle', { anchorX: 0.5, anchorY: 0.5, width: 320, height: 320, 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(); }; // 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 if (currentTestingCustomLevel) { levelTxt.setText('Custom Level'); } else { levelTxt.setText('Level: ' + level); } // Update shots based on difficulty switch (gameDifficulty) { case "baby": shotsRemaining = 50 + level; // Way more shots in baby mode break; case "easy": shotsRemaining = 5 + level; break; case "normal": shotsRemaining = 3 + level; break; case "hard": shotsRemaining = 2 + Math.floor(level / 2); break; case "impossible": shotsRemaining = 1; // Just one shot in impossible mode break; default: shotsRemaining = 3 + level; } // 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); } } // Check if we're loading a custom level if (currentTestingCustomLevel) { var customLevel = storage.currentTestLevel; if (customLevel) { try { var levelData = JSON.parse(customLevel); // Create targets if (levelData.targets) { for (var i = 0; i < levelData.targets.length; i++) { var target = levelData.targets[i]; createTarget(target.x + 1024, target.y + 900); } } // Create walls if (levelData.walls) { for (var i = 0; i < levelData.walls.length; i++) { var wall = levelData.walls[i]; createWall(wall.x + 1024, wall.y + 900, wall.rotation); } } // Create lucky blocks if (levelData.luckyBlocks) { for (var i = 0; i < levelData.luckyBlocks.length; i++) { var block = levelData.luckyBlocks[i]; createLuckyBlock(block.x + 1024, block.y + 900); } } // Create power-ups if (levelData.powerUps) { for (var i = 0; i < levelData.powerUps.length; i++) { var powerUp = levelData.powerUps[i]; createPowerUp(powerUp.x + 1024, powerUp.y + 900, powerUp.type); } } // Set target count totalTargets = targets.length; targetsHit = 0; return; // Skip default level generation } catch (e) { console.log("Error loading custom level", e); } } // If custom level loading failed, revert to normal mode currentTestingCustomLevel = false; } // 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 = 300 + Math.random() * 1400; // Spread targets out more var targetY = 300 + Math.random() * 1500; // In impossible mode, place targets in very difficult positions if (isImpossibleMode) { // Place some targets behind walls, at extreme edges, etc. targetX = i % 3 === 0 ? 100 + Math.random() * 200 : 1700 + Math.random() * 200; targetY = i % 2 === 0 ? 100 + Math.random() * 200 : 2400 + Math.random() * 200; } 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 = 300 + Math.random() * 1400; var targetY = 300 + Math.random() * 1500; // 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)) { // Walls don't kill bullets on touch anymore - just bounce // 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(); };
===================================================================
--- original.js
+++ change.js
@@ -290,156 +290,471 @@
initGame();
}
return self;
});
-var LaneScrolling = Container.expand(function () {
+var LevelEditor = Container.expand(function () {
var self = Container.call(this);
- self.currentLane = 1; // Default middle lane (0=top, 1=middle, 2=bottom)
- self.laneCount = 3;
- self.laneHeight = 240; // Space between lanes
- self.laneY = []; // Y positions for each lane
- self.isActive = false;
- // Initialize lane positions
- self.initLanes = function () {
- var baseY = 2732 / 2;
- for (var i = 0; i < self.laneCount; i++) {
- self.laneY[i] = baseY + (i - 1) * self.laneHeight;
+ // Editor state
+ self.currentTool = "target"; // current selected tool
+ self.isPlacing = false; // whether we're currently placing an object
+ self.savedLevels = []; // array to store custom levels
+ self.gridSize = 50; // snap to grid size
+ self.showGrid = true; // whether to show grid
+ // Grid background
+ var grid = new Container();
+ self.addChild(grid);
+ // Editor UI background
+ var editorBg = self.attachAsset('centerCircle', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ width: 2048,
+ height: 2732,
+ tint: 0x222222,
+ alpha: 0.9
+ });
+ // Title
+ var titleText = new Text2("LEVEL EDITOR", {
+ size: 180,
+ fill: 0xFFFFFF
+ });
+ titleText.anchor.set(0.5, 0);
+ titleText.y = 50;
+ self.addChild(titleText);
+ // Tool buttons
+ var toolsContainer = new Container();
+ toolsContainer.y = 250;
+ self.addChild(toolsContainer);
+ // Create tool buttons
+ var tools = [{
+ name: "target",
+ color: 0x44AA44,
+ label: "TARGET"
+ }, {
+ name: "wall",
+ color: 0x4477AA,
+ label: "WALL"
+ }, {
+ name: "luckyBlock",
+ color: 0xAA8844,
+ label: "LUCKY BLOCK"
+ }, {
+ name: "powerUp",
+ color: 0xAA44AA,
+ label: "POWER-UP"
+ }, {
+ name: "clear",
+ color: 0xAA4444,
+ label: "CLEAR"
+ }];
+ var toolButtons = [];
+ var buttonWidth = 300;
+ var buttonHeight = 120;
+ var spacing = 20;
+ var buttonsPerRow = 3;
+ for (var i = 0; i < tools.length; i++) {
+ var row = Math.floor(i / buttonsPerRow);
+ var col = i % buttonsPerRow;
+ var xPos = (col - buttonsPerRow / 2 + 0.5) * (buttonWidth + spacing);
+ var yPos = row * (buttonHeight + spacing);
+ var button = createButton(tools[i].label, xPos, yPos, tools[i].color, tools[i].name);
+ toolsContainer.addChild(button);
+ toolButtons.push(button);
+ }
+ // Create save and test buttons
+ var saveButton = createButton("SAVE LEVEL", -200, 650, 0x22AA22, "save");
+ var testButton = createButton("TEST LEVEL", 200, 650, 0xAA22AA, "test");
+ var backButton = createButton("BACK", 0, 800, 0xAA2222, "back");
+ self.addChild(saveButton);
+ self.addChild(testButton);
+ self.addChild(backButton);
+ // Grid toggle button
+ var gridButton = createButton("TOGGLE GRID", 0, 550, 0x444444, "grid");
+ self.addChild(gridButton);
+ // Edit area
+ var editArea = new Container();
+ editArea.y = 900;
+ self.addChild(editArea);
+ var editAreaBg = editArea.attachAsset('centerCircle', {
+ anchorX: 0.5,
+ anchorY: 0,
+ width: 1800,
+ height: 1500,
+ tint: 0x111111,
+ alpha: 0.5
+ });
+ // Level items container
+ var levelItems = new Container();
+ editArea.addChild(levelItems);
+ function createButton(text, xPos, yPos, color, action) {
+ var button = new Container();
+ // Button background
+ var bg = button.attachAsset('powerMeter', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ width: action === "save" || action === "test" || action === "back" || action === "grid" ? 350 : buttonWidth,
+ height: buttonHeight,
+ tint: color
+ });
+ // Button text
+ var txt = new Text2(text, {
+ size: 60,
+ 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 () {
+ if (tools.some(function (t) {
+ return t.name === action;
+ })) {
+ // Tool selection
+ self.currentTool = action;
+ updateButtonHighlights();
+ } else if (action === "save") {
+ saveCurrentLevel();
+ } else if (action === "test") {
+ testCurrentLevel();
+ } else if (action === "back") {
+ exitEditor();
+ } else if (action === "grid") {
+ toggleGrid();
+ }
+ };
+ return button;
+ }
+ // Update button highlights based on selected tool
+ function updateButtonHighlights() {
+ for (var i = 0; i < toolButtons.length; i++) {
+ var isSelected = tools[i].name === self.currentTool;
+ var scale = isSelected ? 1.1 : 1.0;
+ toolButtons[i].scale.set(scale, scale);
+ // Add glow effect to selected tool
+ if (isSelected) {
+ LK.effects.flashObject(toolButtons[i], 0xFFFFFF, 300);
+ }
}
- };
- // Change lane (direction: -1 for up, 1 for down)
- self.changeLane = function (direction) {
- var newLane = self.currentLane + direction;
- if (newLane >= 0 && newLane < self.laneCount) {
- self.currentLane = newLane;
- // Move turret to new lane
- if (turret) {
- tween(turret, {
- y: self.laneY[self.currentLane]
- }, {
- duration: 200,
- easing: tween.easeOut
+ }
+ // Save current level
+ function saveCurrentLevel() {
+ var level = {
+ targets: [],
+ walls: [],
+ luckyBlocks: [],
+ powerUps: []
+ };
+ // Save all placed items
+ for (var i = 0; i < levelItems.children.length; i++) {
+ var item = levelItems.children[i];
+ if (item.itemType === "target") {
+ level.targets.push({
+ x: item.x,
+ y: item.y
});
+ } else if (item.itemType === "wall") {
+ level.walls.push({
+ x: item.x,
+ y: item.y,
+ rotation: item.rotation
+ });
+ } else if (item.itemType === "luckyBlock") {
+ level.luckyBlocks.push({
+ x: item.x,
+ y: item.y
+ });
+ } else if (item.itemType === "powerUp") {
+ level.powerUps.push({
+ x: item.x,
+ y: item.y,
+ type: item.powerUpType
+ });
}
}
- };
- // Start side-scroller mode
- self.start = function () {
- self.isActive = true;
- self.initLanes();
- // Set turret position to middle lane
- self.currentLane = 1;
- if (turret) {
- turret.x = 200;
- turret.y = self.laneY[self.currentLane];
- turret.updateRotation(0); // Face right
+ self.savedLevels.push(level);
+ // Save to storage
+ storage.customLevels = JSON.stringify(self.savedLevels);
+ // Show save confirmation
+ var saveConfirm = new Text2("Level Saved!", {
+ size: 100,
+ fill: 0x22FF22
+ });
+ saveConfirm.anchor.set(0.5, 0.5);
+ saveConfirm.x = 0;
+ saveConfirm.y = 400;
+ self.addChild(saveConfirm);
+ // Fade out confirmation
+ tween(saveConfirm, {
+ alpha: 0,
+ y: 350
+ }, {
+ duration: 2000,
+ easing: tween.easeOut,
+ onFinish: function onFinish() {
+ self.removeChild(saveConfirm);
+ }
+ });
+ }
+ // Test the current level
+ function testCurrentLevel() {
+ // Create a temporary level
+ var tempLevel = {
+ targets: [],
+ walls: [],
+ luckyBlocks: [],
+ powerUps: []
+ };
+ // Add all placed items
+ for (var i = 0; i < levelItems.children.length; i++) {
+ var item = levelItems.children[i];
+ if (item.itemType === "target") {
+ tempLevel.targets.push({
+ x: item.x,
+ y: item.y
+ });
+ } else if (item.itemType === "wall") {
+ tempLevel.walls.push({
+ x: item.x,
+ y: item.y,
+ rotation: item.rotation
+ });
+ } else if (item.itemType === "luckyBlock") {
+ tempLevel.luckyBlocks.push({
+ x: item.x,
+ y: item.y
+ });
+ } else if (item.itemType === "powerUp") {
+ tempLevel.powerUps.push({
+ x: item.x,
+ y: item.y,
+ type: item.powerUpType
+ });
+ }
}
- // Create arrow controls if they don't exist
- if (!upArrow) {
- upArrow = new Container();
- var upArrowBg = upArrow.attachAsset('centerCircle', {
- anchorX: 0.5,
- anchorY: 0.5,
- width: 160,
- height: 160,
- tint: 0x3355ff,
- alpha: 0.8
- });
- var upArrowText = new Text2("↑", {
- size: 100,
- fill: 0xFFFFFF
- });
- upArrowText.anchor.set(0.5, 0.5);
- upArrow.addChild(upArrowText);
- upArrow.x = 150;
- upArrow.y = 2200;
- game.addChild(upArrow);
- // Handle up arrow press
- upArrow.down = function () {
- if (self.isActive) {
- self.changeLane(-1);
- }
- };
+ // Store current test level
+ storage.currentTestLevel = JSON.stringify(tempLevel);
+ // Hide editor and start test mode
+ self.visible = false;
+ currentTestingCustomLevel = true;
+ initGame();
+ }
+ // Exit editor back to main menu
+ function exitEditor() {
+ self.visible = false;
+ if (titleScreen) {
+ titleScreen.visible = true;
}
- if (!downArrow) {
- downArrow = new Container();
- var downArrowBg = downArrow.attachAsset('centerCircle', {
+ }
+ // Toggle grid display
+ function toggleGrid() {
+ self.showGrid = !self.showGrid;
+ updateGrid();
+ }
+ // Update grid display
+ function updateGrid() {
+ // Clear existing grid
+ while (grid.children.length > 0) {
+ grid.removeChild(grid.children[0]);
+ }
+ // If grid is disabled, stop here
+ if (!self.showGrid) {
+ return;
+ }
+ // Draw grid lines
+ for (var x = -900; x <= 900; x += self.gridSize) {
+ var vLine = LK.getAsset('aimLine', {
anchorX: 0.5,
- anchorY: 0.5,
- width: 160,
- height: 160,
- tint: 0x3355ff,
- alpha: 0.8
+ anchorY: 0,
+ height: 1500,
+ width: 1,
+ tint: 0x444444,
+ alpha: 0.3
});
- var downArrowText = new Text2("↓", {
- size: 100,
- fill: 0xFFFFFF
- });
- downArrowText.anchor.set(0.5, 0.5);
- downArrow.addChild(downArrowText);
- downArrow.x = 350;
- downArrow.y = 2200;
- game.addChild(downArrow);
- // Handle down arrow press
- downArrow.down = function () {
- if (self.isActive) {
- self.changeLane(1);
- }
- };
+ vLine.x = x;
+ vLine.y = 0;
+ grid.addChild(vLine);
}
- // Create fire button
- if (!fireButton) {
- var fireButton = new Container();
- var fireBg = fireButton.attachAsset('centerCircle', {
- anchorX: 0.5,
+ for (var y = 0; y <= 1500; y += self.gridSize) {
+ var hLine = LK.getAsset('aimLine', {
+ anchorX: 0,
anchorY: 0.5,
- width: 240,
- height: 240,
- tint: 0xff3333,
- alpha: 0.8
+ height: 1,
+ width: 1800,
+ tint: 0x444444,
+ alpha: 0.3,
+ rotation: Math.PI / 2
});
- var fireText = new Text2("FIRE", {
- size: 80,
- fill: 0xFFFFFF
- });
- fireText.anchor.set(0.5, 0.5);
- fireButton.addChild(fireText);
- fireButton.x = 1800;
- fireButton.y = 2200;
- game.addChild(fireButton);
- // Handle fire button press
- fireButton.down = function () {
- if (self.isActive) {
- fireBullet();
- }
- };
+ hLine.x = -900;
+ hLine.y = y;
+ grid.addChild(hLine);
}
- // Show controls
- upArrow.visible = true;
- downArrow.visible = true;
- if (fireButton) {
- fireButton.visible = true;
+ }
+ // Clear all placed items
+ function clearItems() {
+ while (levelItems.children.length > 0) {
+ levelItems.removeChild(levelItems.children[0]);
}
- };
- // Stop side-scroller mode
- self.stop = function () {
- self.isActive = false;
- // Hide all controls
- if (upArrow) {
- upArrow.visible = false;
+ }
+ // Event handlers for editing
+ editArea.down = function (x, y, obj) {
+ // Convert to edit area coordinates
+ var editX = x;
+ var editY = y - editArea.y;
+ if (self.currentTool === "clear") {
+ clearItems();
+ return;
}
- if (downArrow) {
- downArrow.visible = false;
+ self.isPlacing = true;
+ // Snap to grid
+ if (self.showGrid) {
+ editX = Math.round(editX / self.gridSize) * self.gridSize;
+ editY = Math.round(editY / self.gridSize) * self.gridSize;
}
- if (fireButton) {
- fireButton.visible = false;
+ // Check that we're within bounds
+ if (editX < -900 || editX > 900 || editY < 0 || editY > 1500) {
+ return;
}
+ // Place item based on current tool
+ if (self.currentTool === "target") {
+ placeTarget(editX, editY);
+ } else if (self.currentTool === "wall") {
+ placeWall(editX, editY);
+ } else if (self.currentTool === "luckyBlock") {
+ placeLuckyBlock(editX, editY);
+ } else if (self.currentTool === "powerUp") {
+ placePowerUp(editX, editY);
+ }
};
- // Update method
- self.update = function () {
- if (!self.isActive) {
+ editArea.move = function (x, y, obj) {
+ if (!self.isPlacing) {
return;
}
+ // For drag operations if needed
};
+ editArea.up = function (x, y, obj) {
+ self.isPlacing = false;
+ };
+ // Placement functions
+ function placeTarget(x, y) {
+ var target = LK.getAsset('target', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ scaleX: 1,
+ scaleY: 1
+ });
+ target.x = x;
+ target.y = y;
+ target.itemType = "target"; // Mark item type for saving
+ target.interactive = true;
+ // Allow targets to be deleted by clicking them again
+ target.down = function () {
+ levelItems.removeChild(target);
+ };
+ levelItems.addChild(target);
+ }
+ function placeWall(x, y) {
+ var wall = LK.getAsset('wall', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ scaleX: 1,
+ scaleY: 1
+ });
+ wall.x = x;
+ wall.y = y;
+ wall.rotation = 0; // Default rotation
+ wall.itemType = "wall"; // Mark item type for saving
+ wall.interactive = true;
+ // Allow walls to be rotated or deleted
+ wall.down = function () {
+ if (Date.now() - (wall.lastClick || 0) < 300) {
+ // Double click to delete
+ levelItems.removeChild(wall);
+ } else {
+ // Single click to rotate
+ wall.rotation += Math.PI / 4;
+ if (wall.rotation >= Math.PI * 2) {
+ wall.rotation -= Math.PI * 2;
+ }
+ }
+ wall.lastClick = Date.now();
+ };
+ levelItems.addChild(wall);
+ }
+ function placeLuckyBlock(x, y) {
+ var luckyBlock = LK.getAsset('luckyBlock', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ scaleX: 1,
+ scaleY: 1
+ });
+ luckyBlock.x = x;
+ luckyBlock.y = y;
+ luckyBlock.itemType = "luckyBlock"; // Mark item type for saving
+ luckyBlock.interactive = true;
+ // Add a question mark
+ var questionMark = new Text2("?", {
+ size: 50,
+ fill: 0x000000
+ });
+ questionMark.anchor.set(0.5, 0.5);
+ luckyBlock.addChild(questionMark);
+ // Allow lucky blocks to be deleted by clicking them again
+ luckyBlock.down = function () {
+ levelItems.removeChild(luckyBlock);
+ };
+ levelItems.addChild(luckyBlock);
+ }
+ function placePowerUp(x, y) {
+ var powerUpTypes = ["multishot", "bigball", "slowmotion", "extralife", "explosive"];
+ var typeColors = [0xff5500, 0x00aaff, 0xaa00ff, 0xff0055, 0xff0000];
+ var typeTexts = ["3x", "BIG", "SLOW", "LIFE", "BOOM"];
+ // Cycle through power-up types
+ var typeIndex = levelItems.powerUpTypeIndex || 0;
+ var type = powerUpTypes[typeIndex];
+ var color = typeColors[typeIndex];
+ var text = typeTexts[typeIndex];
+ // Update for next placement
+ typeIndex = (typeIndex + 1) % powerUpTypes.length;
+ levelItems.powerUpTypeIndex = typeIndex;
+ // Create power-up
+ var powerUp = new Container();
+ var bg = powerUp.attachAsset('centerCircle', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ width: 240,
+ height: 240,
+ tint: color
+ });
+ var label = new Text2(text, {
+ size: 25,
+ fill: 0xFFFFFF
+ });
+ label.anchor.set(0.5, 0.5);
+ powerUp.addChild(label);
+ powerUp.x = x;
+ powerUp.y = y;
+ powerUp.itemType = "powerUp"; // Mark item type for saving
+ powerUp.powerUpType = type; // Remember specific power-up type
+ powerUp.interactive = true;
+ // Allow power-ups to be deleted by clicking them again
+ powerUp.down = function () {
+ levelItems.removeChild(powerUp);
+ };
+ levelItems.addChild(powerUp);
+ }
+ // Initialize
+ updateButtonHighlights();
+ updateGrid();
+ // Load saved levels if available
+ var loadedLevels = storage.customLevels;
+ if (loadedLevels) {
+ try {
+ self.savedLevels = JSON.parse(loadedLevels);
+ } catch (e) {
+ self.savedLevels = [];
+ }
+ }
return self;
});
var LuckyBlock = Container.expand(function () {
var self = Container.call(this);
@@ -533,25 +848,8 @@
resumeText.anchor.set(0.5, 0.5);
resumeButton.addChild(resumeText);
resumeButton.y = 0;
menuContainer.addChild(resumeButton);
- // Side scroller mode toggle button
- var sideScrollButton = new Container();
- var sideScrollBg = sideScrollButton.attachAsset('powerMeter', {
- anchorX: 0.5,
- anchorY: 0.5,
- width: 750,
- height: 250,
- tint: 0x33cc55
- });
- var sideScrollText = new Text2("SIDE SCROLLER", {
- size: 110,
- fill: 0xFFFFFF
- });
- sideScrollText.anchor.set(0.5, 0.5);
- sideScrollButton.addChild(sideScrollText);
- sideScrollButton.y = 375;
- menuContainer.addChild(sideScrollButton);
// Main menu button
var menuButton = new Container();
var menuBg = menuButton.attachAsset('powerMeter', {
anchorX: 0.5,
@@ -565,57 +863,25 @@
fill: 0xFFFFFF
});
menuText.anchor.set(0.5, 0.5);
menuButton.addChild(menuText);
- menuButton.y = 750;
+ menuButton.y = 375;
menuContainer.addChild(menuButton);
// Button interactions
resumeButton.down = function () {
self.hide();
};
- sideScrollButton.down = function () {
- self.hide();
- // Toggle side scroll mode
- sideScrollMode = !sideScrollMode;
- if (sideScrollMode) {
- // Start side scroll mode
- if (!laneScrolling) {
- laneScrolling = new LaneScrolling();
- game.addChild(laneScrolling);
- }
- // Clear current level
- clearLevel();
- // Start side scrolling
- laneScrolling.start();
- // Position turret for side scrolling
- if (turret) {
- turret.updateRotation(0); // Face right
- }
- // Update UI
- if (levelTxt) {
- levelTxt.setText('SIDE SCROLLER MODE');
- }
- } else {
- // Stop side scroll mode and return to normal gameplay
- if (laneScrolling) {
- laneScrolling.stop();
- }
- // Load regular level
- loadLevel(currentLevel);
- }
- };
menuButton.down = function () {
self.hide();
- if (titleScreen) {
+ if (currentTestingCustomLevel && levelEditor) {
+ // Return to level editor if we were testing a custom level
+ currentTestingCustomLevel = false;
+ levelEditor.visible = true;
+ } else if (titleScreen) {
titleScreen.visible = true;
}
// Reset game state
clearLevel();
- // Stop side scroll mode if active
- if (sideScrollMode && laneScrolling) {
- sideScrollMode = false;
- laneScrolling.stop();
- }
// Hide all game elements
if (turret) {
turret.visible = false;
}
@@ -853,40 +1119,43 @@
fill: 0xFFFFFF
});
buttonText.anchor.set(0.5, 0.5);
startButton.addChild(buttonText);
- startButton.y = 500;
+ startButton.y = 350;
self.addChild(startButton);
- // Add button interaction
- startButton.down = function () {
- self.visible = false;
- difficultySelector.visible = true;
- };
- // Side scroller mode button
- var sideScrollButton = new Container();
- var sideScrollBg = sideScrollButton.attachAsset('powerMeter', {
+ // Level editor button
+ var editorButton = new Container();
+ var editorBg = editorButton.attachAsset('powerMeter', {
anchorX: 0.5,
anchorY: 0.5,
width: 750,
height: 250,
- tint: 0x33cc55
+ tint: 0x4477AA
});
- var sideScrollText = new Text2("SIDE SCROLLER", {
- size: 110,
+ var editorText = new Text2("LEVEL EDITOR", {
+ size: 130,
fill: 0xFFFFFF
});
- sideScrollText.anchor.set(0.5, 0.5);
- sideScrollButton.addChild(sideScrollText);
- sideScrollButton.y = 700;
- self.addChild(sideScrollButton);
- // Add side scroll button interaction
- sideScrollButton.down = function () {
+ editorText.anchor.set(0.5, 0.5);
+ editorButton.addChild(editorText);
+ editorButton.y = 650;
+ self.addChild(editorButton);
+ // Add button interactions
+ startButton.down = function () {
self.visible = false;
- // Start game directly in side scroller mode
- gameDifficulty = "normal";
- sideScrollMode = true;
- initGame();
+ difficultySelector.visible = true;
};
+ editorButton.down = function () {
+ self.visible = false;
+ if (!levelEditor) {
+ levelEditor = new LevelEditor();
+ levelEditor.x = 2048 / 2;
+ levelEditor.y = 0;
+ game.addChild(levelEditor);
+ } else {
+ levelEditor.visible = true;
+ }
+ };
// Animation for logo
tween(logo, {
scaleX: 1.1,
scaleY: 1.1
@@ -984,23 +1253,18 @@
var difficultySelector;
var titleScreen;
var deathScreen;
var pauseMenu;
+var levelEditor;
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 sideScrollMode = false; // Flag for side scroller mode
-var laneScrolling; // Lane scrolling controller
-var swipeStartY = 0; // For tracking swipe gestures
-var swipeThreshold = 50; // Minimum swipe distance for lane change
-var upArrow; // Up arrow control
-var downArrow; // Down arrow control
-var fireButton; // Fire button for side-scroller mode
+var currentTestingCustomLevel = false;
var activePowerUps = {
multishot: false,
bigball: false,
slowmotion: false,
@@ -1104,9 +1368,9 @@
var shotsTxt;
var levelTxt;
function initGame() {
// Check if game is starting fresh or resuming from difficulty selection
- if (!difficultySelector || !difficultySelector.visible) {
+ if ((!difficultySelector || !difficultySelector.visible) && (!levelEditor || !levelEditor.visible)) {
// Create death screen first (initialized but hidden)
if (!deathScreen) {
deathScreen = new DeathScreen();
deathScreen.x = 2048 / 2;
@@ -1172,8 +1436,55 @@
comboText.x = 0;
comboText.y = -250;
comboText.visible = false;
LK.gui.center.addChild(comboText);
+ // Back to editor button for custom levels
+ var backToEditorButton;
+ if (currentTestingCustomLevel) {
+ backToEditorButton = new Container();
+ var buttonBg = backToEditorButton.attachAsset('centerCircle', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ width: 320,
+ height: 320,
+ tint: 0x4477AA,
+ alpha: 0.8
+ });
+ var buttonText = new Text2("EDIT", {
+ size: 60,
+ fill: 0xFFFFFF
+ });
+ buttonText.anchor.set(0.5, 0.5);
+ backToEditorButton.addChild(buttonText);
+ backToEditorButton.x = -50;
+ backToEditorButton.y = 170;
+ LK.gui.topRight.addChild(backToEditorButton);
+ backToEditorButton.down = function () {
+ currentTestingCustomLevel = false;
+ // Hide 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;
+ }
+ // Clear level
+ clearLevel();
+ // Show level editor
+ if (levelEditor) {
+ levelEditor.visible = true;
+ }
+ };
+ }
// Pause button
var pauseButton = new Container();
var pauseBg = pauseButton.attachAsset('centerCircle', {
anchorX: 0.5,
@@ -1210,9 +1521,13 @@
function loadLevel(level) {
// Clear any existing level elements
clearLevel();
// Update level display
- levelTxt.setText('Level: ' + level);
+ if (currentTestingCustomLevel) {
+ levelTxt.setText('Custom Level');
+ } else {
+ levelTxt.setText('Level: ' + level);
+ }
// Update shots based on difficulty
switch (gameDifficulty) {
case "baby":
shotsRemaining = 50 + level; // Way more shots in baby mode
@@ -1261,8 +1576,53 @@
for (var i = 0; i < 5; i++) {
createPowerUp(300 + i * 200, 300 + i * 100);
}
}
+ // Check if we're loading a custom level
+ if (currentTestingCustomLevel) {
+ var customLevel = storage.currentTestLevel;
+ if (customLevel) {
+ try {
+ var levelData = JSON.parse(customLevel);
+ // Create targets
+ if (levelData.targets) {
+ for (var i = 0; i < levelData.targets.length; i++) {
+ var target = levelData.targets[i];
+ createTarget(target.x + 1024, target.y + 900);
+ }
+ }
+ // Create walls
+ if (levelData.walls) {
+ for (var i = 0; i < levelData.walls.length; i++) {
+ var wall = levelData.walls[i];
+ createWall(wall.x + 1024, wall.y + 900, wall.rotation);
+ }
+ }
+ // Create lucky blocks
+ if (levelData.luckyBlocks) {
+ for (var i = 0; i < levelData.luckyBlocks.length; i++) {
+ var block = levelData.luckyBlocks[i];
+ createLuckyBlock(block.x + 1024, block.y + 900);
+ }
+ }
+ // Create power-ups
+ if (levelData.powerUps) {
+ for (var i = 0; i < levelData.powerUps.length; i++) {
+ var powerUp = levelData.powerUps[i];
+ createPowerUp(powerUp.x + 1024, powerUp.y + 900, powerUp.type);
+ }
+ }
+ // Set target count
+ totalTargets = targets.length;
+ targetsHit = 0;
+ return; // Skip default level generation
+ } catch (e) {
+ console.log("Error loading custom level", e);
+ }
+ }
+ // If custom level loading failed, revert to normal mode
+ currentTestingCustomLevel = false;
+ }
// Different level layouts
switch (level) {
case 1:
// Basic level with random targets
@@ -1536,10 +1896,9 @@
walls.push(wall);
return wall;
}
function fireBullet() {
- if (shotsRemaining <= 0 && !sideScrollMode) {
- // In side scroller mode, infinite bullets
+ if (shotsRemaining <= 0) {
return;
}
// Reset combo when firing a new bullet
comboCount = 0;
@@ -1550,14 +1909,12 @@
if (comboTimer) {
LK.clearTimeout(comboTimer);
comboTimer = null;
}
- // In normal mode, decrease shots remaining
- if (!sideScrollMode) {
- shotsRemaining--;
- if (shotsTxt && typeof shotsTxt !== 'undefined') {
- shotsTxt.setText('Shots: ' + shotsRemaining);
- }
+ // 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++) {
@@ -1572,35 +1929,19 @@
bullet.x = 100;
bullet.y = 2732 - 100;
}
// Get velocity from turret angle and power
- var velocity;
- if (sideScrollMode) {
- // In side scroller mode, bullets always go right
- velocity = {
- x: 20,
- // Fast horizontal movement
- y: 0 // No vertical movement
- };
- } else {
- velocity = turret ? turret.getShootVelocity() : {
- x: 0,
- y: 0
- };
- }
+ var velocity = turret ? turret.getShootVelocity() : {
+ x: 0,
+ y: 0
+ };
// If multishot, spread the bullets
if (activePowerUps.multishot && bulletCount > 1) {
- if (sideScrollMode) {
- // In side scroller mode, spread vertically
- bullet.y += (i - 1) * 30;
- } else {
- // In normal mode, spread based on angle
- 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;
- }
+ 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;
}
@@ -1778,15 +2119,8 @@
// Skip if game is paused
if (gamePaused) {
return;
}
- // Different handling for side scroll mode
- if (sideScrollMode) {
- // Just record the start position for swipe detection
- isDragging = true;
- swipeStartY = y;
- return;
- }
isDragging = true;
dragStartX = x;
dragStartY = y;
// Set initial angle
@@ -1829,30 +2163,9 @@
return;
}
if (isDragging) {
isDragging = false;
- // Different handling for side scroll mode
- if (sideScrollMode) {
- // Detect swipe up or down
- var swipeDist = y - swipeStartY;
- if (Math.abs(swipeDist) > swipeThreshold) {
- if (swipeDist < 0) {
- // Swipe up - move turret up a lane
- if (laneScrolling) {
- laneScrolling.changeLane(-1);
- }
- } else {
- // Swipe down - move turret down a lane
- if (laneScrolling) {
- laneScrolling.changeLane(1);
- }
- }
- }
- // Don't auto-fire on touch up in side scroller mode
- } else {
- // Normal mode - fire bullet
- fireBullet();
- }
+ fireBullet();
}
};
// Game update loop
game.update = function () {
@@ -1863,21 +2176,9 @@
// Update all bullets
for (var i = 0; i < bullets.length; i++) {
bullets[i].update();
}
- // Different updates based on game mode
- if (sideScrollMode) {
- // Update side scroll controller
- if (laneScrolling) {
- laneScrolling.update();
- }
- // No auto-firing in side-scroller mode anymore
- // Check for collisions
- checkCollisions();
- } else {
- // Normal mode updates
- // Check for collisions
- checkCollisions();
- // Check for game over
- checkGameOver();
- }
+ // Check for collisions
+ checkCollisions();
+ // Check for game over
+ checkGameOver();
};
\ No newline at end of file
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