Code edit (1 edits merged)
Please save this source code
User prompt
now, can we also load the balls in storage ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
can we also add to storage the quantity of each ball type that has been purchased
User prompt
Please fix the bug: 'Cannot read properties of undefined (reading 'length')' in or related to this line: 'for (var i = 0; i < bricks.length; i++) {' Line Number: 919
User prompt
Please fix the bug: 'Cannot read properties of undefined (reading 'length')' in or related to this line: 'ball.direction.y /= magnitude;' Line Number: 918
User prompt
Please fix the bug: 'Cannot read properties of undefined (reading 'length')' in or related to this line: 'for (var i = 0; i < bricks.length; i++) {' Line Number: 918
User prompt
Please fix the bug: 'Cannot read properties of undefined (reading 'length')' in or related to this line: 'for (var i = 0; i < bricks.length; i++) {' Line Number: 918
User prompt
Please fix the bug: 'Cannot read properties of undefined (reading 'length')' in or related to this line: 'for (var i = 0; i < bricks.length; i++) {' Line Number: 917
User prompt
Please fix the bug: 'Cannot read properties of undefined (reading 'length')' in or related to this line: 'for (var i = 0; i < bricks.length; i++) {' Line Number: 917
User prompt
Please fix the bug: 'Cannot read properties of undefined (reading 'length')' in or related to this line: 'for (var i = 0; i < bricks.length; i++) {' Line Number: 917
User prompt
Please fix the bug: 'Cannot read properties of undefined (reading 'length')' in or related to this line: 'for (var i = 0; i < bricks.length; i++) {' Line Number: 917
User prompt
Please fix the bug: 'storage.balls.map is not a function' in or related to this line: 'var balls = storage.balls ? storage.balls.map(function (type) {' Line Number: 448
Code edit (1 edits merged)
Please save this source code
User prompt
when balls are loaded from storage, their position should not matter
User prompt
Please fix the bug: 'Script error.' in or related to this line: 'return {' Line Number: 573
User prompt
Please fix the bug: 'Uncaught TypeError: storage.balls.forEach is not a function' in or related to this line: 'storage.balls.forEach(function (ballData) {' Line Number: 1118
User prompt
when balls are loaded from sotarte spawn them all in random positions
User prompt
for bought balls, only save in storage the ball type and the amount, not the quantity
User prompt
when player taps in any brick close upgrade display
User prompt
if all upgrades are greyed out, then grey out upgrade button too
User prompt
click x1 should also be greyed out disabled if player cant afford it
User prompt
upgrade button should be greyed out when player don't have money for any upgrade
User prompt
make numbers in bricks bold
User prompt
mkae text in buttons bold
User prompt
can we reduce the distancebetween ech column in the grid
/**** * Plugins ****/ var storage = LK.import("@upit/storage.v1"); var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var Ball = Container.expand(function () { var self = Container.call(this); var type = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'normal'; var ballGraphics = self.attachAsset('ball', { anchorX: 0.5, anchorY: 0.5 }); ballGraphics.tint = type === 'splash' ? 0xff0066 : type === 'sniper' ? 0x00ff99 : type === 'scatter' ? 0xffff00 : type === 'smallScatter' ? 0xffff00 : 0xffffff; self.type = type; self.speed = type === 'splash' ? upgrades.splashSpeed : type === 'sniper' ? upgrades.sniperSpeed : type === 'scatter' ? upgrades.scatterSpeed : type === 'smallScatter' ? upgrades.scatterSpeed * 0.8 : upgrades.normalSpeed; self.power = type === 'splash' ? upgrades.splashPower : type === 'sniper' ? upgrades.sniperPower : type === 'scatter' ? upgrades.scatterPower : type === 'smallScatter' ? Math.max(1, Math.floor(upgrades.scatterPower * 0.5)) : upgrades.normalPower; self.direction = { x: 1, y: -1 }; self.update = function () { var gridSize = levelConfig[level] ? levelConfig[level].gridSize : 200; var stepSize = self.speed; if (self.type === 'sniper') { var nearestBrick = findNearestBrick(self.x, self.y); if (nearestBrick) { var dx = nearestBrick.x - self.x; var dy = nearestBrick.y - self.y; var magnitude = Math.sqrt(dx * dx + dy * dy); self.direction.x = dx / magnitude; self.direction.y = dy / magnitude; } } var dx = self.direction.x * stepSize; var dy = self.direction.y * stepSize; self.x += dx; self.y += dy; if (self.x <= BALL_RADIUS || self.x >= GAME_WIDTH - BALL_RADIUS) { self.direction.x *= -1; self.x = Math.max(BALL_RADIUS, Math.min(GAME_WIDTH - BALL_RADIUS, self.x)); LK.getSound('bounce').play(); } if (self.y <= BALL_RADIUS || self.y >= GAME_HEIGHT - BALL_RADIUS) { self.direction.y *= -1; self.y = Math.max(BALL_RADIUS, Math.min(GAME_HEIGHT - BALL_RADIUS, self.y)); LK.getSound('bounce').play(); } if (!isNearBricks(self.x, self.y)) { return; } var gridXMin = Math.floor((self.x - BALL_RADIUS) / gridSize); var gridXMax = Math.floor((self.x + BALL_RADIUS) / gridSize); var gridYMin = Math.floor((self.y - BALL_RADIUS) / gridSize); var gridYMax = Math.floor((self.y + BALL_RADIUS) / gridSize); var hasCollided = false; for (var gx = gridXMin; gx <= gridXMax && !hasCollided; gx++) { for (var gy = gridYMin; gy <= gridYMax && !hasCollided; gy++) { var gridKey = "".concat(gx, ",").concat(gy); var cellBricks = brickGrid[gridKey]; if (!cellBricks || cellBricks.length === 0) { continue; } for (var j = cellBricks.length - 1; j >= 0; j--) { var brick = cellBricks[j]; if (!brick || brick.health <= 0 || !self.intersects(brick)) { continue; } handleBallBrickCollision(self, brick); brick.hit(self.power); if (self.type === 'splash' && brick.health > 0) { applySplashDamage(brick, gridSize); } else if (self.type === 'scatter') { scatterOnImpact(self); self.destroy(); balls.splice(balls.indexOf(self), 1); hasCollided = true; break; } if (brick.health <= 0) { cellBricks.splice(j, 1); } hasCollided = true; break; } } } }; }); var Brick = Container.expand(function () { var self = Container.call(this); var brickGraphics = self.attachAsset('brick', { anchorX: 0.5, anchorY: 0.5 }); self.health = 1; self.maxHealth = 1; self.healthText = new Text2(self.health.toString(), { size: 50, fill: 0x000000, fontWeight: 'bold' }); self.healthText.anchor.set(0.5, 0.5); self.addChild(self.healthText); // Helper function to convert hex color to RGB function hexToRGB(hex) { return { r: hex >> 16 & 0xff, g: hex >> 8 & 0xff, b: hex & 0xff }; } // Helper function to convert RGB back to hex function rgbToHex(r, g, b) { return (Math.floor(r) << 16) + (Math.floor(g) << 8) + Math.floor(b); } // Update brick tint based on health self.updateTint = function () { var baseColors = LEVEL_COLORS; var colorCount = baseColors.length; if (self.health <= colorCount) { brickGraphics.tint = baseColors[self.health - 1]; } else { var baseIndex = (self.health - 1) % colorCount; var baseColor = hexToRGB(baseColors[baseIndex]); var factor = Math.floor((self.health - 1) / colorCount); var darkenFactor = Math.max(0, 1 - factor * 0.1); var r = baseColor.r * darkenFactor; var g = baseColor.g * darkenFactor; var b = baseColor.b * darkenFactor; r = Math.max(0, Math.min(255, r)); g = Math.max(0, Math.min(255, g)); b = Math.max(0, Math.min(255, b)); brickGraphics.tint = rgbToHex(r, g, b); } }; self.updateTint(); self.hit = function () { var damage = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1; self.health -= damage; if (self.health <= 0) { LK.getSound('explosion').play(); score += self.maxHealth === 1 ? 1 : self.maxHealth; scoreTxt.setText('$' + score.toString()); storage.score = score; var brickIndex = bricks.indexOf(self); if (brickIndex !== -1) { bricks.splice(brickIndex, 1); } var explosionColors = [0xff0000, 0x00ff00, 0x0000ff, 0xffff00, 0xff00ff, 0x00ffff]; var randomColor = explosionColors[Math.floor(Math.random() * explosionColors.length)]; var randomScale = Math.random() * 1.5 + 1.5; var randomDuration = Math.random() * 300 + 400; tween(self, { tint: randomColor, scaleX: randomScale, scaleY: randomScale, alpha: 0 }, { duration: randomDuration, easing: tween.easeOut, onFinish: function onFinish() { self.destroy(); } }); } else { self.healthText.setText(self.health.toString()); self.updateTint(); } }; }); var GameTitle = Container.expand(function () { var self = Container.call(this); var titleGraphics = self.attachAsset('gametitle', { anchorX: 0.5, anchorY: 0.5 }); var titleText = new Text2('Idle BrickBreaker', { size: 150, fill: 0x000000 }); titleText.anchor.set(0.5, 0.5); self.addChild(titleText); }); var ResetButton = Container.expand(function () { var self = Container.call(this); var buttonGraphics = self.attachAsset('resetButton', { anchorX: 0.5, anchorY: 0.5, tint: 0xff6666 }); var buttonText = new Text2('RESET', { size: 50, fill: 0x000000, fontWeight: 'bold' }); buttonText.anchor.set(0.5, 0.5); self.addChild(buttonText); self.interactive = true; self.down = function () { LK.getSound('click').play(); clearLocalStorage(); }; }); var Star = Container.expand(function () { var self = Container.call(this); var starGraphics = self.attachAsset('star', { anchorX: 0.5, anchorY: 0.5 }); self.speed = Math.random() * 2 + 1; self.update = function () { self.y += self.speed; if (self.y > GAME_HEIGHT) { self.y = 0; self.x = Math.random() * GAME_WIDTH; } }; }); var StartButton = Container.expand(function () { var self = Container.call(this); var buttonGraphics = self.attachAsset('startButton', { anchorX: 0.5, anchorY: 0.5 }); var buttonText = new Text2('START', { size: 80, fill: 0x000000, fontWeight: 'bold' }); buttonText.anchor.set(0.5, 0.5); self.addChild(buttonText); self.interactive = true; self.down = function () { LK.getSound('click').play(); startGame(); }; }); var UpgradeButton = Container.expand(function () { var self = Container.call(this); var buttonGraphics = self.attachAsset('upgrade', { anchorX: 0.5, anchorY: 0.5, tint: 0x00ffff }); var buttonText = new Text2('UPGRADE', { size: 50, fill: 0x000000, fontWeight: 'bold' }); buttonText.anchor.set(0.5, 0.5); self.addChild(buttonText); self.interactive = true; self.down = function () { LK.getSound('click').play(); powerupContainer.visible = !powerupContainer.visible; }; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x1a1a2e }); /**** * Game Code ****/ function _toConsumableArray2(r) { return _arrayWithoutHoles2(r) || _iterableToArray2(r) || _unsupportedIterableToArray2(r) || _nonIterableSpread2(); } function _nonIterableSpread2() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _unsupportedIterableToArray2(r, a) { if (r) { if ("string" == typeof r) { return _arrayLikeToArray2(r, a); } var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray2(r, a) : void 0; } } function _iterableToArray2(r) { if ("undefined" != typeof Symbol && null != r[Symbol.iterator] || null != r["@@iterator"]) { return Array.from(r); } } function _arrayWithoutHoles2(r) { if (Array.isArray(r)) { return _arrayLikeToArray2(r); } } function _arrayLikeToArray2(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) { n[e] = r[e]; } return n; } function _toConsumableArray(r) { return _arrayWithoutHoles(r) || _iterableToArray(r) || _unsupportedIterableToArray(r) || _nonIterableSpread(); } function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) { return _arrayLikeToArray(r, a); } var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } } function _iterableToArray(r) { if ("undefined" != typeof Symbol && null != r[Symbol.iterator] || null != r["@@iterator"]) { return Array.from(r); } } function _arrayWithoutHoles(r) { if (Array.isArray(r)) { return _arrayLikeToArray(r); } } function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) { n[e] = r[e]; } return n; } var GAME_WIDTH = 2048; var GAME_HEIGHT = 2632; var BALL_RADIUS = 50; var BRICK_WIDTH = 300; var BRICK_HEIGHT = 99; var LEVEL_COLORS = [0xff00ff, 0x00ffff, 0xffff00, 0xff0066, 0x00ff99, 0xff33cc, 0x66ff33, 0xcc00ff, 0x33ffcc, 0xff3300]; var levelConfig = { 1: { totalBricks: 60, hitpoints: 1, gridSize: 400, pattern: 'grid' }, 2: { totalBricks: 66, hitpoints: 3, gridSize: 200, pattern: 'grid' }, 3: { totalBricks: 84, hitpoints: 6, gridSize: 320, pattern: 'staggered' }, 4: { totalBricks: 102, hitpoints: 10, gridSize: 500, pattern: 'clustered' }, 5: { totalBricks: 96, hitpoints: 12, gridSize: 240, pattern: 'grid' }, 6: { totalBricks: 106, hitpoints: 15, gridSize: 220, pattern: 'clustered' }, 7: { totalBricks: 106, hitpoints: 20, gridSize: 200, pattern: 'grid' }, 8: { totalBricks: 106, hitpoints: 99, gridSize: 180, pattern: 'clustered' }, 9: { totalBricks: 106, hitpoints: 9999, gridSize: 160, pattern: 'grid' }, 10: { totalBricks: 106, hitpoints: 999999, gridSize: 140, pattern: 'clustered' } }; var upgrades = storage.upgrades || { normalSpeed: 1, normalPower: 1, splashSpeed: 1, splashPower: 1, sniperSpeed: 1.5, sniperPower: 2, scatterSpeed: 1, scatterPower: 1, clickDamage: 1, normalSpeedCost: 50, normalPowerCost: 75, splashSpeedCost: 50, splashPowerCost: 75, sniperSpeedCost: 75, sniperPowerCost: 100, scatterSpeedCost: 50, scatterPowerCost: 75, clickCost: 25, normalBallCost: 50, splashBallCost: 100, sniperBallCost: 500, scatterBallCost: 2000 }; var balls = Array.isArray(storage.balls) ? storage.balls.map(function (type) { return createBall(type, true); }) : []; var bricks = Array.isArray(storage.bricks) ? storage.bricks : []; bricks = Array.isArray(bricks) ? bricks : []; var brickGrid = {}; var score = storage.score || 0; var level = storage.level || 1; var brickGridBounds = null; var unlockedTiers = storage.unlockedTiers || { normal: true, splash: false, sniper: false, scatter: false }; function clearLocalStorage() { storage.score = 0; storage.level = 1; storage.unlockedTiers = { normal: true, splash: false, sniper: false, scatter: false }; storage.balls = []; // Clear stored balls storage.upgrades = { normalSpeed: 1, normalPower: 1, splashSpeed: 1, splashPower: 1, sniperSpeed: 1.5, sniperPower: 2, scatterSpeed: 1, scatterPower: 1, clickDamage: 1, normalSpeedCost: 50, normalPowerCost: 75, splashSpeedCost: 50, splashPowerCost: 75, sniperSpeedCost: 75, sniperPowerCost: 100, scatterSpeedCost: 50, scatterPowerCost: 75, clickCost: 25, normalBallCost: 50, splashBallCost: 100, sniperBallCost: 500, scatterBallCost: 2000 }; score = 0; level = 1; balls.forEach(function (ball) { return ball.destroy(); }); // Destroy existing balls balls = []; unlockedTiers = Object.assign({}, storage.unlockedTiers); scoreTxt.setText('$' + score.toString()); levelTxt.setText('Level: ' + level); updateButtonStates(); } // HUD Setup var hud = new Container(); LK.gui.top.addChild(hud); var scoreTxt = new Text2('$0', { size: 60, fill: 0x00ffff }); scoreTxt.anchor.set(0.3, 0); scoreTxt.x += 450; hud.addChild(scoreTxt); var levelTxt = new Text2('Level: ' + level, { size: 60, fill: 0x00ffff }); levelTxt.anchor.set(1, 0); levelTxt.x = scoreTxt.x + 100; levelTxt.y = scoreTxt.height + 10; hud.addChild(levelTxt); var ballButtons = {}; function createBallButton(type, x, cost, asset, prevTier) { var button = new Container(); var buttonGraphics = button.attachAsset('button', { anchorX: 0.5, anchorY: 0, tint: 0x1a1a2e }); button.y = 50; button.x = x + 20; var contentContainer = new Container(); button.addChild(contentContainer); var ballIcon = button.attachAsset('ball', { anchorX: 0.5, anchorY: -0.5, scaleX: 0.6, scaleY: 0.6, y: -10 }); ballIcon.tint = type === 'splash' ? 0xff0066 : type === 'sniper' ? 0x00ff99 : type === 'scatter' ? 0xffff00 : 0xffffff; contentContainer.addChild(ballIcon); var displayType = type === 'scatter' ? 'Multi' : type.charAt(0).toUpperCase() + type.slice(1); var typeText = new Text2(displayType, { size: 30, fill: 0xffffff, fontWeight: 'bold' }); typeText.anchor.set(0.5, 0); typeText.y = -50; button.addChild(typeText); var costText = new Text2('$' + upgrades[type + 'BallCost'], { size: 50, fill: 0x00ffff }); costText.anchor.set(0.5, 0); costText.y = 100; button.addChild(costText); button.interactive = true; button.down = function () { if (score < upgrades[type + 'BallCost']) { return; } score -= upgrades[type + 'BallCost']; scoreTxt.setText('$' + score.toString()); createBall(type); upgrades[type + 'BallCost'] = Math.floor(upgrades[type + 'BallCost'] * 1.3); storage.upgrades = Object.assign({}, upgrades); costText.setText('$' + upgrades[type + 'BallCost']); if (!unlockedTiers[type]) { unlockedTiers[type] = true; storage.unlockedTiers = Object.assign({}, unlockedTiers); updateButtonStates(); } }; button.updateState = function () { var isEnabled = (prevTier ? unlockedTiers[prevTier] : true) && score >= upgrades[type + 'BallCost']; buttonGraphics.tint = isEnabled ? 0x00ffff : 0x666666; button.interactive = isEnabled; ballIcon.tint = isEnabled ? type === 'splash' ? 0xff0066 : type === 'sniper' ? 0x00ff99 : type === 'scatter' ? 0xffff00 : 0xffffff : 0x666666; typeText.fill = isEnabled ? 0xffffff : 0x666666; costText.fill = isEnabled ? 0x00ffff : 0x666666; }; hud.addChild(button); ballButtons[type] = button; } createBallButton('normal', -450, upgrades.normalBallCost, 'ball', null); createBallButton('splash', -300, upgrades.splashBallCost, 'splashBall', 'normal'); createBallButton('sniper', -150, upgrades.sniperBallCost, 'sniperBall', 'splash'); createBallButton('scatter', 0, upgrades.scatterBallCost, 'scatterBall', 'sniper'); var clearStorageButton = LK.getAsset('button', { size: 0, fill: 0x1a1a2e, anchorX: 0.5, anchorY: 0, x: scoreTxt.width + 12000, y: 0 }); var clearStorageText = new Text2('', { size: 0, fill: 0xffffff }); clearStorageText.anchor.set(0.5, 0); clearStorageText.y = 100; clearStorageButton.addChild(clearStorageText); clearStorageButton.down = clearLocalStorage; hud.addChild(clearStorageButton); var powerupContainer = new Container(); powerupContainer.y = 1800; powerupContainer.visible = false; game.addChild(powerupContainer); var bottomHud = new Container(); bottomHud.y = GAME_HEIGHT - 200; game.addChild(bottomHud); var upgradeButtons = {}; function createUpgradeButton(labelPrefix, x, costKey, upgradeKey, baseCost, iconType, prevTier) { var button = new Container(); var buttonGraphics = button.attachAsset('powerupbutton', { anchorX: 0.5, anchorY: 0, tint: 0x1a1a2e }); button.x = x + 60; button.y = 350; button.addChild(buttonGraphics); var contentContainer = new Container(); button.addChild(contentContainer); var icon = null; if (iconType) { icon = button.attachAsset('ball', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.5, scaleY: 0.5, y: 40, tint: iconType === 'splashBall' ? 0xff0066 : iconType === 'sniperBall' ? 0x00ff99 : iconType === 'scatterBall' ? 0xffff00 : iconType === 'ball' ? 0xffffff : 0x00ffff }); contentContainer.addChild(icon); } var labelText = new Text2("".concat(labelPrefix, " x").concat(upgrades[upgradeKey]), { size: 30, fill: 0x000000, fontWeight: 'bold' }); labelText.anchor.set(0.5, 0); labelText.y = 100; contentContainer.addChild(labelText); var costText = new Text2('$' + (baseCost * upgrades[upgradeKey]).toString(), { size: 40, fill: 0x000000 }); costText.anchor.set(0.5, 0); costText.y = 140; contentContainer.addChild(costText); button.interactive = true; button.down = function () { var cost = baseCost * upgrades[upgradeKey]; var ballType = upgradeKey.split('Speed')[0].split('Power')[0]; if (upgradeKey === 'clickDamage') { if (score < cost) { return; } } else { if (!unlockedTiers[ballType] || score < cost) { return; } } score -= cost; LK.getSound('click').play(); upgrades[upgradeKey]++; storage.upgrades = Object.assign({}, upgrades); costText.setText('$' + (baseCost * upgrades[upgradeKey]).toString()); labelText.setText("".concat(labelPrefix, " x").concat(upgrades[upgradeKey])); scoreTxt.setText('$' + score.toString()); balls.forEach(function (b) { if (b.type === 'normal' && upgradeKey.includes('normal')) { b[upgradeKey.includes('Speed') ? 'speed' : 'power'] = upgrades[upgradeKey]; } else if (b.type === 'splash' && upgradeKey.includes('splash')) { b[upgradeKey.includes('Speed') ? 'speed' : 'power'] = upgrades[upgradeKey]; } else if (b.type === 'sniper' && upgradeKey.includes('sniper')) { b[upgradeKey.includes('Speed') ? 'speed' : 'power'] = upgrades[upgradeKey]; } else if ((b.type === 'scatter' || b.type === 'smallScatter') && upgradeKey.includes('scatter')) { b[upgradeKey.includes('Speed') ? 'speed' : 'power'] = upgrades[upgradeKey]; } }); if (upgradeKey === 'clickDamage') { upgrades.clickDamage = upgrades[upgradeKey]; storage.upgrades = Object.assign({}, upgrades); } }; button.updateState = function () { var ballType = upgradeKey.split('Speed')[0].split('Power')[0]; var isEnabled = upgradeKey === 'clickDamage' ? score >= baseCost * upgrades[upgradeKey] : unlockedTiers[ballType] && (prevTier ? unlockedTiers[prevTier] : true) && score >= baseCost * upgrades[upgradeKey]; buttonGraphics.tint = isEnabled ? 0x00ffff : 0x666666; button.interactive = isEnabled; if (icon) { icon.tint = isEnabled ? iconType === 'splashBall' ? 0xff0066 : iconType === 'sniperBall' ? 0x00ff99 : iconType === 'scatterBall' ? 0xffff00 : iconType === 'ball' ? 0xffffff : 0x00ffff : 0x666666; } }; powerupContainer.addChild(button); upgradeButtons[upgradeKey] = button; } var buttonWidth = 150; var spacing = 50; var totalButtons = 9; var totalWidth = totalButtons * buttonWidth + (totalButtons - 1) * spacing; var startX = (GAME_WIDTH - totalWidth) / 2; createUpgradeButton('Speed', startX, 'normalSpeedCost', 'normalSpeed', 50, 'ball', null); createUpgradeButton('Power', startX + (buttonWidth + spacing), 'normalPowerCost', 'normalPower', 75, 'ball', null); createUpgradeButton('Speed', startX + 2 * (buttonWidth + spacing), 'splashSpeedCost', 'splashSpeed', 50, 'splashBall', 'normal'); createUpgradeButton('Power', startX + 3 * (buttonWidth + spacing), 'splashPowerCost', 'splashPower', 75, 'splashBall', 'normal'); createUpgradeButton('Speed', startX + 4 * (buttonWidth + spacing), 'sniperSpeedCost', 'sniperSpeed', 75, 'sniperBall', 'splash'); createUpgradeButton('Power', startX + 5 * (buttonWidth + spacing), 'sniperPowerCost', 'sniperPower', 100, 'sniperBall', 'splash'); createUpgradeButton('Speed', startX + 6 * (buttonWidth + spacing), 'scatterSpeedCost', 'scatterSpeed', 50, 'scatterBall', 'sniper'); createUpgradeButton('Power', startX + 7 * (buttonWidth + spacing), 'scatterPowerCost', 'scatterPower', 75, 'scatterBall', 'sniper'); createUpgradeButton('Click', startX + 8 * (buttonWidth + spacing), 'clickCost', 'clickDamage', 25, null, null); var upgradeButton = new UpgradeButton(); upgradeButton.x = GAME_WIDTH / 2; upgradeButton.y = GAME_HEIGHT - 100; upgradeButton.visible = false; game.addChild(upgradeButton); game.setChildIndex(upgradeButton, game.children.length - 1); hud.visible = false; function updateButtonStates() { for (var type in ballButtons) { ballButtons[type].updateState(); } for (var key in upgradeButtons) { upgradeButtons[key].updateState(); } } function handleBallBrickCollision(ball, brick) { var relativeX = (ball.x - brick.x) / (brick.width / 2); var relativeY = (ball.y - brick.y) / (brick.height / 2); if (Math.abs(relativeX) > Math.abs(relativeY)) { ball.direction.x = -ball.direction.x + relativeX * 0.5; ball.x = brick.x + (relativeX > 0 ? brick.width / 2 + BALL_RADIUS : -brick.width / 2 - BALL_RADIUS); } else { ball.direction.y = -ball.direction.y; ball.y = brick.y + (relativeY > 0 ? brick.height / 2 + BALL_RADIUS : -brick.height / 2 - BALL_RADIUS); } LK.getSound('bounce').play(); ball.direction.x += (Math.random() - 0.5) * 0.1; var magnitude = Math.sqrt(ball.direction.x * ball.direction.x + ball.direction.y * ball.direction.y); ball.direction.x /= magnitude; ball.direction.y /= magnitude; } function applySplashDamage(brick, gridSize) { var gridX = Math.floor(brick.x / gridSize); var gridY = Math.floor(brick.y / gridSize); var adjacentKeys = ["".concat(gridX - 1, ",").concat(gridY), "".concat(gridX + 1, ",").concat(gridY), "".concat(gridX, ",").concat(gridY - 1), "".concat(gridX, ",").concat(gridY + 1)]; adjacentKeys.forEach(function (key) { if (brickGrid[key]) { brickGrid[key].forEach(function (adjBrick) { if (adjBrick && adjBrick.health > 0) { adjBrick.hit(Math.floor(upgrades.splashPower * 0.25)); } }); } }); } function scatterOnImpact(ball) { for (var i = 0; i < 4; i++) { var smallBall = new Ball('smallScatter'); smallBall.x = ball.x; smallBall.y = ball.y; var angle = i / 4 * 2 * Math.PI; smallBall.direction.x = Math.cos(angle); smallBall.direction.y = Math.sin(angle); balls.push(smallBall); game.addChild(smallBall); } } function findNearestBrick(x, y) { if (bricks.length === 0) { return null; } return bricks.reduce(function (closest, brick) { var dx = brick.x - x; var dy = brick.y - y; var distance = Math.sqrt(dx * dx + dy * dy); return !closest || distance < closest.distance ? { brick: brick, distance: distance } : closest; }, null).brick; } function isNearBricks(x, y) { if (!brickGridBounds) { return true; } var buffer = BALL_RADIUS * 2; return x >= brickGridBounds.minX - buffer && x <= brickGridBounds.maxX + buffer && y >= brickGridBounds.minY - buffer && y <= brickGridBounds.maxY + buffer; } function createBricks() { var config = levelConfig[level] || {}; var totalBricks = config.totalBricks || 50; var baseHitpoints = config.hitpoints || 1; var gridSize = config.gridSize || 200; var pattern = config.pattern || 'grid'; var spacingX = 1; var spacingY = 2; brickGrid = {}; bricks = Array.isArray(bricks) ? bricks : []; var cols = 7; var rows = Math.ceil(totalBricks / cols); var totalWidth = cols * BRICK_WIDTH + (cols - 1) * spacingX; var totalHeight = rows * BRICK_HEIGHT + (rows - 1) * spacingY; var startX = (GAME_WIDTH - totalWidth) / 2 + BRICK_WIDTH / 2; var startY = (GAME_HEIGHT - totalHeight) / 3 + BRICK_HEIGHT / 2; var brickCount = 0; if (pattern === 'grid') { for (var i = 0; i < rows && brickCount < totalBricks; i++) { for (var j = 0; j < cols && brickCount < totalBricks; j++) { addBrick(startX + j * (BRICK_WIDTH + spacingX), startY + i * (BRICK_HEIGHT + spacingY), baseHitpoints, gridSize); brickCount++; } } } else if (pattern === 'staggered') { var centerRow = Math.floor(rows / 2); var centerCol = Math.floor(cols / 2); for (var i = 0; i < rows && brickCount < totalBricks; i++) { var offsetX = 0; for (var j = 0; j < cols && brickCount < totalBricks; j++) { var x = startX + offsetX + j * (BRICK_WIDTH + spacingX); var y = startY + i * (BRICK_HEIGHT + spacingY); var rowDistance = Math.abs(i - centerRow); var colDistance = Math.abs(j - centerCol); var maxDistance = Math.max(centerRow, centerCol); var distance = Math.max(rowDistance, colDistance); var hitpoints = Math.max(1, Math.round(baseHitpoints * (1 - distance / maxDistance))); addBrick(x, y, hitpoints, gridSize); brickCount++; } } } else if (pattern === 'clustered') { var centerRow = Math.floor(rows / 2); var centerCol = Math.floor(cols / 2); var maxDistance = Math.max(centerRow, centerCol); var positions = []; for (var i = 0; i < rows; i++) { for (var j = 0; j < cols; j++) { var rowDistance = Math.abs(i - centerRow); var colDistance = Math.abs(j - centerCol); var distance = Math.max(rowDistance, colDistance); positions.push({ i: i, j: j, distance: distance }); } } positions.sort(function (a, b) { return b.distance - a.distance; }); for (var k = 0; k < positions.length && brickCount < totalBricks; k++) { var pos = positions[k]; var i = pos.i; var j = pos.j; var distance = pos.distance; var x = startX + j * (BRICK_WIDTH + spacingX); var y = startY + i * (BRICK_HEIGHT + spacingY); var hitpoints = Math.max(1, Math.round(baseHitpoints * (distance / maxDistance))); addBrick(x, y, hitpoints, gridSize); brickCount++; } } brickGridBounds = { minX: Math.min.apply(Math, _toConsumableArray2(bricks.map(function (b) { return b.x - BRICK_WIDTH / 2; }))), maxX: Math.max.apply(Math, _toConsumableArray2(bricks.map(function (b) { return b.x + BRICK_WIDTH / 2; }))), minY: Math.min.apply(Math, _toConsumableArray2(bricks.map(function (b) { return b.y - BRICK_HEIGHT / 2; }))), maxY: Math.max.apply(Math, _toConsumableArray2(bricks.map(function (b) { return b.y + BRICK_HEIGHT / 2; }))) }; } function addBrick(x, y, hitpoints, gridSize) { var brick = new Brick(); brick.x = x; brick.y = y; brick.health = hitpoints; brick.maxHealth = hitpoints; brick.healthText.setText(brick.health.toString()); brick.updateTint(); bricks.push(brick); game.addChild(brick); var gridX = Math.floor(brick.x / gridSize); var gridY = Math.floor(brick.y / gridSize); var gridKey = "".concat(gridX, ",").concat(gridY); if (!brickGrid[gridKey]) { brickGrid[gridKey] = []; } brickGrid[gridKey].push(brick); } function createBall() { var type = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'normal'; var isLoading = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; var ball = new Ball(type); var gridBottom = brickGridBounds ? brickGridBounds.maxY + 150 : GAME_HEIGHT * 0.7; ball.x = GAME_WIDTH / 2 + (Math.random() * 200 - 100); ball.y = Math.min(gridBottom + 100, GAME_HEIGHT - BALL_RADIUS * 2); var angle = (Math.random() * 0.5 + 0.25) * Math.PI; ball.direction.x = Math.cos(angle); ball.direction.y = -Math.sin(angle); var magnitude = Math.sqrt(ball.direction.x * ball.direction.x + ball.direction.y * ball.direction.y); ball.direction.x /= magnitude; ball.direction.y /= magnitude; for (var i = 0; i < bricks.length; i++) { if (ball.intersects(bricks[i])) { ball.y = brickGridBounds.maxY + BALL_RADIUS + 10; break; } } balls.push(ball); game.addChild(ball); if (!isLoading) { storage.balls = balls.map(function (b) { return b.type; }); } return ball; } game.update = function () { for (var i = 0; i < stars.length; i++) { stars[i].update(); } for (var i = balls.length - 1; i >= 0; i--) { var ball = balls[i]; ball.update(); if (ball.y > GAME_HEIGHT + BALL_RADIUS) { ball.destroy(); balls.splice(i, 1); storage.balls = balls.map(function (b) { return b.type; }); // Update storage when a ball is removed } } if (bricks.length === 0) { if (level === Object.keys(levelConfig).length) { LK.showGameOver(); } else { level += 1; storage.level = level; levelTxt.setText('Level: ' + level); createBricks(); } } updateButtonStates(); }; game.down = function (x, y, obj) { if (!bricks || !Array.isArray(bricks)) { return; } x = Number(x); y = Number(y); for (var i = 0; i < bricks.length; i++) { var brick = bricks[i]; if (!brick.x || !brick.y || !brick.width || !brick.height) { continue; } if (x >= brick.x - brick.width / 2 && x <= brick.x + brick.width / 2 && y >= brick.y - brick.height / 2 && y <= brick.y + brick.height / 2) { brick.hit(upgrades.clickDamage); LK.getSound('click').play(); scoreTxt.setText('$' + score.toString()); storage.score = score; return; } } }; var stars = []; for (var i = 0; i < 100; i++) { var star = new Star(); star.x = Math.random() * GAME_WIDTH; star.y = Math.random() * GAME_HEIGHT; stars.push(star); game.addChildAt(star, 0); } var gameTitle = new GameTitle(); gameTitle.x = GAME_WIDTH / 2; gameTitle.y = GAME_HEIGHT / 2 - 600; game.addChild(gameTitle); function animateTitleColor() { tween(gameTitle, { tint: 0xff33cc }, { duration: 2000, easing: tween.easeInOut, onFinish: function onFinish() { tween(gameTitle, { tint: 0x00ff99 }, { duration: 2000, easing: tween.easeInOut, onFinish: animateTitleColor }); } }); } animateTitleColor(); tween(gameTitle, { y: GAME_HEIGHT / 2 - 500 }, { duration: 1000, easing: tween.bounceOut }); var startButton = new StartButton(); startButton.x = GAME_WIDTH / 2; startButton.y = GAME_HEIGHT / 2 + 300; startButton.tint = 0x00ff99; game.addChild(startButton); tween(startButton, { y: startButton.y - 20 }, { duration: 1000, easing: tween.easeInOut, onFinish: function onFinish() { tween(startButton, { y: startButton.y + 20 }, { duration: 1000, easing: tween.easeInOut }); } }); var resetButton = new ResetButton(); resetButton.x = GAME_WIDTH / 2; resetButton.y = startButton.y + 900; game.addChild(resetButton); tween(resetButton, { y: resetButton.y - 20 }, { duration: 1000, easing: tween.easeInOut, onFinish: function onFinish() { tween(resetButton, { y: resetButton.y + 20 }, { duration: 1000, easing: tween.easeInOut }); } }); LK.playMusic('backgroundmusic'); function startGame() { startButton.destroy(); gameTitle.destroy(); resetButton.visible = false; upgradeButton.visible = true; hud.visible = true; createBricks(); balls.forEach(function (ball) { return game.addChild(ball); }); updateButtonStates(); game.update = function () { for (var i = 0; i < stars.length; i++) { stars[i].update(); } for (var i = balls.length - 1; i >= 0; i--) { var ball = balls[i]; ball.update(); if (ball.y > GAME_HEIGHT + BALL_RADIUS) { ball.destroy(); balls.splice(i, 1); storage.balls = balls.map(function (b) { return b.type; }); } } if (bricks.length === 0) { if (level === Object.keys(levelConfig).length) { LK.showGameOver(); } else { level += 1; storage.level = level; levelTxt.setText('Level: ' + level); createBricks(); } } updateButtonStates(); }; } game.update = function () { for (var i = 0; i < stars.length; i++) { stars[i].update(); } };
===================================================================
--- original.js
+++ change.js
@@ -429,8 +429,9 @@
var balls = Array.isArray(storage.balls) ? storage.balls.map(function (type) {
return createBall(type, true);
}) : [];
var bricks = Array.isArray(storage.bricks) ? storage.bricks : [];
+bricks = Array.isArray(bricks) ? bricks : [];
var brickGrid = {};
var score = storage.score || 0;
var level = storage.level || 1;
var brickGridBounds = null;