Code edit (1 edits merged)
Please save this source code
User prompt
use a new asset for particle effect, not splash ball
User prompt
lets add somoe particle effect when a block is destroyed
User prompt
Import of asset
Code edit (1 edits merged)
Please save this source code
User prompt
local storage button delete sholuld be next to the points ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
add a button to delete local storage ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
add local storage module ↪💡 Consider importing and using the following plugins: @upit/storage.v1
Code edit (1 edits merged)
Please save this source code
User prompt
add a main menu screen with the game title and a game start button. game should not start until game start is touched. main menu should cover the whole screen and the game should not be displayed.
User prompt
make numbers in brick bigge
User prompt
now some pricks are out of the screen. adjust columns and rows to adapt to thenew size of each brick
User prompt
can we make bricks bigger and adjust the grid accordingly. double their size
User prompt
create a home screen with the game title and a start game button
User prompt
reset points, player should start with 0 points unless they have points in storage ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
remove hardcoding of points
User prompt
add local storage to the game ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
make background marine blue
User prompt
Please fix the bug: 'Cannot read properties of undefined (reading 'splashBallCost')' in or related to this line: 'var splashCostText = new Text2('$' + upgrades.splashBallCost.toString(), {' Line Number: 229
User prompt
do not reste points, level, upgrades costs on game start
User prompt
Please fix the bug: 'storage.load is not a function' in or related to this line: 'storage.load();' Line Number: 132 ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
local storage is not working
User prompt
save points, level, balls purchased, and upgrades purchased in local storage so you continue next level from where you last playerd ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
Progress is not being savesd in local sgtoras. Allshoudl be stored, level, upgrades, balls, evertyhig ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
all progress of game should be stored in local storage ↪💡 Consider importing and using the following plugins: @upit/storage.v1
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1", { lastRunStartTime: "undefined", lastRunTime: 0, ballDamage: { normal: 0, splash: 0, sniper: 0, scatter: 0, smallScatter: 0 } }); /**** * 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; if (type === 'smallScatter') { ballGraphics.scaleX = 0.5; ballGraphics.scaleY = 0.5; } self.type = type; self.speed = type === 'splash' ? upgrades.splashSpeed : type === 'sniper' ? upgrades.sniperSpeed : type === 'scatter' ? upgrades.scatterSpeed : type === 'smallScatter' ? upgrades.scatterSpeed * 1.5 : 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.8)) : upgrades.normalPower; self.velocity = { x: 1, y: -1 }; self.sniperCooldown = 0; self.sniperCooldownMax = 60; self.interactive = true; self.down = function (x, y, obj) { var angle = Math.random() * 2 * Math.PI; self.velocity.x = Math.cos(angle); self.velocity.y = Math.sin(angle); var magnitude = Math.sqrt(self.velocity.x * self.velocity.x + self.velocity.y * self.velocity.y); self.velocity.x /= magnitude; self.velocity.y /= magnitude; }; self.update = function () { var SHIELD_BUFFER = 5; var MAX_STEP = BALL_RADIUS / 2; var totalDistance = self.speed; var steps = Math.ceil(totalDistance / MAX_STEP); var stepDistance = totalDistance / steps; var splashApplied = false; if (self.type === 'sniper') { self.sniperCooldown--; if (self.sniperCooldown <= 0) { 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.velocity.x = dx / magnitude; self.velocity.y = dy / magnitude; self.sniperCooldown = self.sniperCooldownMax; } } } for (var step = 0; step < steps; step++) { var dx = self.velocity.x * stepDistance; var dy = self.velocity.y * stepDistance; var nextX = self.x + dx; var nextY = self.y + dy; if (nextX < BALL_RADIUS + SHIELD_BUFFER) { nextX = BALL_RADIUS + SHIELD_BUFFER; self.velocity.x = -self.velocity.x; self.velocity.y += (Math.random() - 0.5) * 0.1; LK.getSound('bounce').play(); } else if (nextX > GAME_WIDTH - BALL_RADIUS - SHIELD_BUFFER) { nextX = GAME_WIDTH - BALL_RADIUS - SHIELD_BUFFER; self.velocity.x = -self.velocity.x; self.velocity.y += (Math.random() - 0.5) * 0.1; LK.getSound('bounce').play(); } if (nextY < BALL_RADIUS + SHIELD_BUFFER) { nextY = BALL_RADIUS + SHIELD_BUFFER; self.velocity.y = -self.velocity.y; self.velocity.x += (Math.random() - 0.5) * 0.1; LK.getSound('bounce').play(); } else if (nextY > GAME_HEIGHT - BALL_RADIUS - SHIELD_BUFFER) { nextY = GAME_HEIGHT - BALL_RADIUS - SHIELD_BUFFER; self.velocity.y = -self.velocity.y; self.velocity.x += (Math.random() - 0.5) * 0.1; LK.getSound('bounce').play(); } var ballLeft = nextX - BALL_RADIUS - SHIELD_BUFFER; var ballRight = nextX + BALL_RADIUS + SHIELD_BUFFER; var ballTop = nextY - BALL_RADIUS - SHIELD_BUFFER; var ballBottom = nextY + BALL_RADIUS + SHIELD_BUFFER; var collided = false; for (var i = bricks.length - 1; i >= 0; i--) { var brick = bricks[i]; if (brick.health <= 0) { continue; } var brickLeft = brick.x - BRICK_WIDTH / 2; var brickRight = brick.x + BRICK_WIDTH / 2; var brickTop = brick.y - BRICK_HEIGHT / 2; var brickBottom = brick.y + BRICK_HEIGHT / 2; if (ballLeft < brickRight && ballRight > brickLeft && ballTop < brickBottom && ballBottom > brickTop) { var overlapLeft = ballRight - brickLeft; var overlapRight = brickRight - ballLeft; var overlapTop = ballBottom - brickTop; var overlapBottom = brickBottom - ballTop; var minOverlap = Math.min(overlapLeft, overlapRight, overlapTop, overlapBottom); if (minOverlap === overlapLeft) { nextX = brickLeft - BALL_RADIUS - SHIELD_BUFFER; self.velocity.x = -self.velocity.x; } else if (minOverlap === overlapRight) { nextX = brickRight + BALL_RADIUS + SHIELD_BUFFER; self.velocity.x = -self.velocity.x; } else if (minOverlap === overlapTop) { nextY = brickTop - BALL_RADIUS - SHIELD_BUFFER; self.velocity.y = -self.velocity.y; } else { nextY = brickBottom + BALL_RADIUS + SHIELD_BUFFER; self.velocity.y = -self.velocity.y; } self.velocity.x += (Math.random() - 0.5) * 0.2; self.velocity.y += (Math.random() - 0.5) * 0.2; LK.getSound('bounce').play(); brick.hit(self.power); ballDamage[self.type] = (ballDamage[self.type] || 0) + self.power; storage.ballDamage = Object.assign({}, ballDamage); if (self.type === 'splash' && !splashApplied) { applySplashDamage(brick); splashApplied = true; } else if (self.type === 'scatter') { scatterOnImpact(self); self.destroy(); balls.splice(balls.indexOf(self), 1); return; } collided = true; break; } } self.x = nextX; self.y = nextY; if (collided) { break; } } var magnitude = Math.sqrt(self.velocity.x * self.velocity.x + self.velocity.y * self.velocity.y); if (magnitude > 0) { self.velocity.x /= magnitude; self.velocity.y /= magnitude; } }; }); 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); self.updateTint = function () { var baseColors = LEVEL_COLORS; var colorCount = baseColors.length; if (self.health <= colorCount) { brickGraphics.tint = baseColors[self.health - 1]; } else { var lastDigit = self.health % 10; var colorIndex = lastDigit === 0 ? 9 : lastDigit - 1; brickGraphics.tint = baseColors[colorIndex]; } }; 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, scaleX: 1.35, scaleY: 1.5 }); }); var ResetButton = Container.expand(function () { var self = Container.call(this); var buttonGraphics = self.attachAsset('resetButton', { anchorX: 0.5, anchorY: 0.5, tint: 0xffc0cb }); self.interactive = true; self.down = function () { LK.getSound('click').play(); clearLocalStorage(); storage.lastRunTime = 0; // Reset lastRunTime in storage lastRunStartTime = Date.now(); storage.lastRunStartTime = lastRunStartTime; // lastRunTime is no longer reset to ensure it persists across sessions ballDamage = { normal: 0, splash: 0, sniper: 0, scatter: 0, smallScatter: 0 }; storage.ballDamage = Object.assign({}, ballDamage); playTime = 0; storage.playTime = 0; LK.showGameOver(); }; }); 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, scaleX: 1.2, scaleY: 1.2 }); self.interactive = true; self.down = function () { LK.getSound('click').play(); self.interactive = false; var explosionColors = [0xff0000, 0x00ff00, 0x0000ff, 0xffff00, 0xff00ff, 0x00ffff]; var randomColor = explosionColors[Math.floor(Math.random() * explosionColors.length)]; LK.getSound('explosion').play(); tween(self, { tint: randomColor, scaleX: 1.5, scaleY: 1.5, alpha: 0 }, { duration: 400, easing: tween.easeOut, onFinish: function onFinish() { startTransitionSequence(); } }); }; }); var UpgradeButton = Container.expand(function () { var self = Container.call(this); var buttonGraphics = self.attachAsset('upgrade', { anchorX: 0.5, anchorY: 0.5, tint: 0x00ffff }); 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 ****/ var lastRunStartTime = storage.lastRunStartTime !== "undefined" ? parseInt(storage.lastRunStartTime) : Date.now(); var lastRunTime = storage.lastRunTime || 0; var playTime = storage.playTime || 0; var ballDamage = storage.ballDamage || { normal: 0, splash: 0, sniper: 0, scatter: 0, smallScatter: 0 }; 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; } function tintEndScreenTitle() { gameTitleContainer.children.forEach(function (child) { child.tint = 0xff33cc; }); } var frameThickness = 10; var topFrame = new Container(); var topFrameGraphics = topFrame.attachAsset('frame', { anchorX: 0, anchorY: 0, width: GAME_WIDTH, height: frameThickness }); topFrame.x = 0; topFrame.y = 0; game.addChild(topFrame); function animateTopFrameTint() { tween(topFrameGraphics, { tint: 0xff33cc }, { duration: 2000, easing: tween.easeInOut, onFinish: function onFinish() { tween(topFrameGraphics, { tint: 0x00ff99 }, { duration: 2000, easing: tween.easeInOut, onFinish: animateTopFrameTint }); } }); } animateTopFrameTint(); var bottomFrame = new Container(); var bottomFrameGraphics = bottomFrame.attachAsset('frame', { anchorX: 0, anchorY: 0, width: GAME_WIDTH, height: frameThickness }); bottomFrame.x = 0; bottomFrame.y = 2720; game.addChild(bottomFrame); // Should be bottomFrame function animateBottomFrameTint() { tween(bottomFrameGraphics, { tint: 0xff33cc }, { duration: 2000, easing: tween.easeInOut, onFinish: function onFinish() { tween(bottomFrameGraphics, { tint: 0x00ff99 }, { duration: 2000, easing: tween.easeInOut, onFinish: animateBottomFrameTint }); } }); } animateBottomFrameTint(); var leftFrame = new Container(); var leftFrameGraphics = leftFrame.attachAsset('frame', { anchorX: 0, anchorY: 0, width: frameThickness, height: GAME_HEIGHT }); leftFrame.x = 0; leftFrame.y = 0; game.addChild(leftFrame); function animateLeftFrameTint() { tween(leftFrameGraphics, { tint: 0xff33cc }, { duration: 2000, easing: tween.easeInOut, onFinish: function onFinish() { tween(leftFrameGraphics, { tint: 0x00ff99 }, { duration: 2000, easing: tween.easeInOut, onFinish: animateLeftFrameTint }); } }); } animateLeftFrameTint(); var rightFrame = new Container(); var rightFrameGraphics = rightFrame.attachAsset('frame', { anchorX: 0, anchorY: 0, width: frameThickness, height: GAME_HEIGHT }); rightFrame.x = 2040; rightFrame.y = 0; game.addChild(rightFrame); function animateRightFrameTint() { tween(rightFrameGraphics, { tint: 0xff33cc }, { duration: 2000, easing: tween.easeInOut, onFinish: function onFinish() { tween(rightFrameGraphics, { tint: 0x00ff99 }, { duration: 2000, easing: tween.easeInOut, onFinish: animateRightFrameTint }); } }); } animateRightFrameTint(); var welcomeText = null; function showEndScreen() { game.isGameOver = true; hud.visible = false; powerupContainer.visible = false; upgradeButton.visible = false; balls.forEach(function (ball) { return ball.visible = true; }); bricks.forEach(function (brick) { return brick.visible = false; }); storage.ballDamage = Object.assign({}, ballDamage); var congratsText = new Text2('Congratulations! You broke them ALL!', { size: 100, fill: 0xffffff }); congratsText.anchor.set(0.5, 0); congratsText.x = GAME_WIDTH / 2; congratsText.y = GAME_HEIGHT / 2 - 600; game.addChild(congratsText); var gameTitle = new GameTitle(); gameTitle.x = GAME_WIDTH / 2; gameTitle.y = congratsText.y - 300; gameTitleContainer.addChild(gameTitle); function animateEndTitleColor() { tintEndScreenTitle(); tween(gameTitle, { tint: 0xff33cc }, { duration: 2000, easing: tween.easeInOut, onFinish: function onFinish() { tween(gameTitle, { tint: 0x00ff99 }, { duration: 2000, easing: tween.easeInOut, onFinish: animateEndTitleColor }); } }); } animateEndTitleColor(); var playTimeText = new Text2('Total Time Played: ' + playTime + ' seconds', { size: 60, fill: 0xffffff }); playTimeText.anchor.set(0.5, 0); playTimeText.x = GAME_WIDTH / 2; playTimeText.y = congratsText.y + 200; game.addChild(playTimeText); var lastRunTimeText = new Text2('Last Run Time: ' + lastRunTime + ' seconds', { size: 60, fill: 0xffffff }); lastRunTimeText.anchor.set(0.5, 0); lastRunTimeText.x = GAME_WIDTH / 2; lastRunTimeText.y = playTimeText.y + 80; game.addChild(lastRunTimeText); var damageText = new Text2('Damage Dealt This Run:\n' + 'Normal: ' + ballDamage.normal + '\n' + 'Splash: ' + ballDamage.splash + '\n' + 'Sniper: ' + ballDamage.sniper + '\n' + 'Scatter: ' + ballDamage.scatter + '\n' + 'Small Scatter: ' + ballDamage.smallScatter, { size: 50, fill: 0xffffff, align: 'center' }); damageText.anchor.set(0.5, 0); damageText.x = GAME_WIDTH / 2; damageText.y = lastRunTimeText.y + 120; game.addChild(damageText); var noteText = new Text2('Start over with all your upgrades, or reset for a fresh new run! Your choice!', { size: 50, fill: 0xffffff }); noteText.anchor.set(0.5, 0); noteText.x = GAME_WIDTH / 2; noteText.y = damageText.y + 500; game.addChild(noteText); var endGameButton = new Container(); var buttonGraphics = endGameButton.attachAsset('endGameButton', { anchorX: 0.5, anchorY: 0.5, tint: 0x00ffff }); endGameButton.x = GAME_WIDTH / 2; endGameButton.y = noteText.y + 300; endGameButton.interactive = true; endGameButton.down = function () { storage.level = 1; storage.lastRunTime = 0; // Clear lastRunTime from storage lastRunStartTime = Date.now(); storage.lastRunStartTime = lastRunStartTime; ballDamage = { normal: 0, splash: 0, sniper: 0, scatter: 0, smallScatter: 0 }; storage.ballDamage = Object.assign({}, ballDamage); LK.showGameOver(); }; game.addChild(endGameButton); var resetButton = new ResetButton(); resetButton.x = GAME_WIDTH / 2; resetButton.y = endGameButton.y + 260; game.addChild(resetButton); LK.clearInterval(playTimeInterval); playTimeInterval = null; } var GAME_WIDTH = 2048; var GAME_HEIGHT = 2720; 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: 28, hitpoints: 1, pattern: 'clustered' }, 2: { totalBricks: 63, hitpoints: 2, pattern: 'staggered' }, 3: { totalBricks: 63, hitpoints: 3, pattern: 'cross' }, 4: { totalBricks: 77, pattern: 'custom', customPattern: [[0, 3, 3, 0, 3, 3, 0], [3, 4, 4, 3, 4, 4, 3], [3, 4, 4, 4, 4, 4, 3], [3, 4, 4, 4, 4, 4, 3], [3, 4, 4, 4, 4, 4, 3], [3, 4, 4, 4, 4, 4, 3], [3, 4, 4, 4, 4, 4, 3], [0, 3, 4, 4, 4, 3, 0], [0, 0, 3, 4, 3, 0, 0], [0, 0, 0, 3, 0, 0, 0], [0, 0, 0, 3, 0, 0, 0]] }, 5: { totalBricks: 112, pattern: 'custom', customPattern: [[0, 0, 0, 3, 0, 0, 0], [0, 0, 3, 4, 3, 0, 0], [0, 3, 4, 5, 4, 3, 0], [3, 4, 5, 6, 5, 4, 3], [4, 5, 6, 7, 6, 5, 4], [3, 5, 6, 7, 6, 5, 3], [0, 4, 5, 6, 5, 4, 0], [0, 3, 4, 5, 4, 3, 0], [0, 0, 3, 4, 3, 0, 0], [0, 0, 2, 3, 2, 0, 0], [0, 0, 1, 2, 1, 0, 0], [0, 0, 0, 1, 0, 0, 0], [0, 0, 1, 2, 1, 0, 0], [0, 0, 1, 2, 1, 0, 0]] }, 6: { totalBricks: 60, hitpoints: 10, pattern: 'cross' }, 7: { totalBricks: 70, hitpoints: 13, pattern: 'clustered' }, 8: { totalBricks: 112, hitpoints: 15, pattern: 'clustered' }, 9: { totalBricks: 112, hitpoints: 20, pattern: 'cross' }, 10: { totalBricks: 112, hitpoints: 30, pattern: 'clustered' }, 11: { totalBricks: 112, hitpoints: 50, pattern: 'clustered' }, 12: { totalBricks: 112, hitpoints: 99, pattern: 'clustered' }, 13: { totalBricks: 140, pattern: 'custom', customPattern: [[0, 222, 0, 0, 0, 222, 0], [0, 222, 0, 0, 0, 222, 0], [0, 222, 0, 0, 0, 222, 0], [0, 222, 0, 0, 0, 222, 0], [0, 222, 0, 0, 0, 222, 0], [0, 222, 0, 0, 0, 222, 0], [0, 222, 0, 0, 0, 222, 0], [0, 222, 0, 0, 0, 222, 0], [0, 222, 0, 0, 0, 222, 0], [0, 222, 0, 0, 0, 222, 0], [0, 222, 0, 0, 0, 222, 0], [0, 222, 222, 222, 222, 222, 0], [0, 222, 222, 222, 222, 222, 0]] }, 14: { totalBricks: 140, pattern: 'custom', customPattern: [[0, 333, 333, 333, 333, 333, 0], [0, 333, 333, 333, 333, 333, 0], [0, 333, 0, 0, 0, 333, 0], [0, 333, 0, 0, 0, 333, 0], [0, 333, 0, 0, 0, 333, 0], [0, 333, 333, 333, 333, 333, 0], [0, 333, 333, 333, 333, 333, 0], [0, 333, 333, 0, 0, 0, 0], [0, 333, 333, 0, 0, 0, 0], [0, 333, 333, 0, 0, 0, 0], [0, 333, 333, 0, 0, 0, 0], [0, 333, 333, 0, 0, 0, 0], [0, 333, 333, 0, 0, 0, 0], [0, 333, 333, 0, 0, 0, 0]] }, 15: { totalBricks: 140, pattern: 'custom', customPattern: [[0, 444, 444, 444, 0], [0, 444, 444, 444, 0], [0, 444, 444, 444, 0], [0, 444, 444, 444, 0], [0, 444, 444, 444, 0], [0, 444, 444, 444, 0], [0, 444, 444, 444, 0], [0, 444, 444, 444, 0], [0, 444, 444, 444, 0], [0, 444, 444, 444, 0], [0, 444, 444, 444, 0], [0, 444, 444, 444, 0], [0, 444, 444, 444, 0]] }, 16: { totalBricks: 140, pattern: 'custom', customPattern: [[555, 555, 555, 555, 555, 555, 555], [555, 555, 555, 555, 555, 555, 555], [555, 555, 555, 555, 555, 555, 555], [0, 0, 555, 555, 555, 0, 0], [0, 0, 555, 555, 555, 0, 0], [0, 0, 555, 555, 555, 0, 0], [0, 0, 555, 555, 555, 0, 0], [0, 0, 555, 555, 555, 0, 0], [0, 0, 555, 555, 555, 0, 0], [0, 0, 555, 555, 555, 0, 0], [0, 0, 555, 555, 555, 0, 0], [0, 0, 555, 555, 555, 0, 0], [0, 0, 555, 555, 555, 0, 0]] } }; var upgrades = storage.upgrades || { normalSpeed: 1, normalPower: 1, splashSpeed: 1, splashPower: 2, sniperSpeed: 1, sniperPower: 1, scatterSpeed: 1, scatterPower: 1, clickDamage: 1, normalSpeedCost: 10, normalPowerCost: 50, splashSpeedCost: 75, splashPowerCost: 75, sniperSpeedCost: 100, sniperPowerCost: 100, scatterSpeedCost: 125, scatterPowerCost: 125, clickCost: 10, normalBallCost: 10, splashBallCost: 150, sniperBallCost: 500, scatterBallCost: 2000 }; var balls = []; var ballQuantities = storage.ballQuantities || { normal: 0, splash: 0, sniper: 0, scatter: 0 }; var bricks = []; 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 }; var playTime = storage.playTime || 0; var playTimeInterval = LK.setInterval(function () { if (!game.isGameOver) { playTime += 1; lastRunTime += 1; // Increment lastRunTime by 1 second for each update storage.playTime = playTime; storage.lastRunTime = lastRunTime; // Persist lastRunTime to storage } }, 1000); game.isGameOver = false; function clearLocalStorage() { storage.score = 0; storage.level = 1; storage.unlockedTiers = { normal: true, splash: false, sniper: false, scatter: false }; unlockedTiers = Object.assign({}, storage.unlockedTiers); storage.upgrades = { normalSpeed: 1, normalPower: 1, splashSpeed: 1, splashPower: 2, sniperSpeed: 1, sniperPower: 1, scatterSpeed: 1, scatterPower: 1, clickDamage: 1, normalSpeedCost: 50, normalPowerCost: 50, splashSpeedCost: 75, splashPowerCost: 75, sniperSpeedCost: 100, sniperPowerCost: 100, scatterSpeedCost: 125, scatterPowerCost: 125, clickCost: 10, normalBallCost: 10, splashBallCost: 150, sniperBallCost: 500, scatterBallCost: 2000 }; storage.lastRunStartTime = Date.now(); storage.lastRunTime = 0; storage.ballDamage = { normal: 0, splash: 0, sniper: 0, scatter: 0, smallScatter: 0 }; score = 0; storage.ballQuantities = { normal: 0, splash: 0, sniper: 0, scatter: 0 }; storage.firstLoad = false; ballQuantities = Object.assign({}, storage.ballQuantities); level = 1; unlockedTiers = Object.assign({}, storage.unlockedTiers); scoreTxt.setText('$' + score.toString()); levelTxt.setText('Level: ' + level); updateButtonStates(); } var hud = new Container(); LK.gui.top.addChild(hud); var scoreTxt = new Text2('$0', { size: 60, fill: 0x39ff14 }); scoreTxt.anchor.set(0.3, 0); scoreTxt.x += 570; hud.addChild(scoreTxt); var levelTxt = new Text2('Level: ' + level, { size: 50, fill: 0xffffff }); 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: 40, fill: 0xffffff }); costText.anchor.set(0.5, 0); costText.y = 100; button.addChild(costText); button.interactive = true; button.down = function () { if (score < upgrades[type + 'BallCost']) { return; } if (welcomeText && welcomeText.parent) { welcomeText.parent.removeChild(welcomeText); } score -= upgrades[type + 'BallCost']; scoreTxt.setText('$' + score.toString()); createBall(type); ballQuantities[type] = (ballQuantities[type] || 0) + 1; storage.ballQuantities = Object.assign({}, ballQuantities); upgrades[type + 'BallCost'] = Math.floor(upgrades[type + 'BallCost'] * 2); 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; if (isEnabled) { ballIcon.tint = type === 'splash' ? 0xff0066 : type === 'sniper' ? 0x00ff99 : type === 'scatter' ? 0xffff00 : 0xffffff; typeText.fill = 0xffffff; costText.fill = 0x3bc7e0; } else { ballIcon.tint = 0x666666; typeText.fill = 0x666666; costText.fill = 0x666666; } }; hud.addChild(button); ballButtons[type] = button; } createBallButton('normal', -450, 10, '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); // Should be 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.replace('Speed', 'SPD').replace('Power', 'DMG'), " +").concat(upgrades[upgradeKey]), { size: upgradeKey === 'clickDamage' ? 40 : 40, fill: 0x000000, fontWeight: 'bold' }); labelText.anchor.set(0.5, 0); labelText.y = 80; contentContainer.addChild(labelText); var currentCost = baseCost * Math.pow(2, upgrades[upgradeKey] - 1); var costText = new Text2("$".concat(currentCost), { size: 40, fill: 0x000000 }); costText.anchor.set(0.5, 0); costText.y = 140; contentContainer.addChild(costText); button.interactive = true; button.down = function () { if (welcomeText && welcomeText.parent) { welcomeText.parent.removeChild(welcomeText); } var cost = baseCost * Math.pow(2, upgrades[upgradeKey] - 1); 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); var newCost = baseCost * Math.pow(2, upgrades[upgradeKey] - 1); costText.setText("$".concat(newCost)); labelText.setText("".concat(labelPrefix, " +").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); } updateButtonStates(); }; button.updateState = function () { var ballType = upgradeKey.split('Speed')[0].split('Power')[0]; var currentCost = baseCost * Math.pow(2, upgrades[upgradeKey] - 1); var isEnabled = upgradeKey === 'clickDamage' ? score >= currentCost : unlockedTiers[ballType] && (prevTier ? unlockedTiers[prevTier] : true) && score >= currentCost; 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; } labelText.fill = isEnabled ? 0x000000 : 0x666666; costText.fill = isEnabled ? 0x000000 : 0x666666; costText.setText("$".concat(currentCost)); }; powerupContainer.addChild(button); upgradeButtons[upgradeKey] = button; } var buttonWidth = 150; var spacing = 70; var totalButtons = 9; var totalWidth = totalButtons * buttonWidth + (totalButtons - 1) * spacing; var startX = (GAME_WIDTH - totalWidth) / 2; createUpgradeButton('Speed', startX, 'normalSpeedCost', 'normalSpeed', 10, 'ball', null); createUpgradeButton('Power', startX + (buttonWidth + spacing), 'normalPowerCost', 'normalPower', 50, 'ball', null); createUpgradeButton('Speed', startX + 2 * (buttonWidth + spacing), 'splashSpeedCost', 'splashSpeed', 75, 'splashBall', 'normal'); createUpgradeButton('Power', startX + 3 * (buttonWidth + spacing), 'splashPowerCost', 'splashPower', 75, 'splashBall', 'normal'); createUpgradeButton('Speed', startX + 4 * (buttonWidth + spacing), 'sniperSpeedCost', 'sniperSpeed', 100, 'sniperBall', 'splash'); createUpgradeButton('Power', startX + 5 * (buttonWidth + spacing), 'sniperPowerCost', 'sniperPower', 100, 'sniperBall', 'splash'); createUpgradeButton('Speed', startX + 6 * (buttonWidth + spacing), 'scatterSpeedCost', 'scatterSpeed', 125, 'scatterBall', 'sniper'); createUpgradeButton('Power', startX + 7 * (buttonWidth + spacing), 'scatterPowerCost', 'scatterPower', 125, 'scatterBall', 'sniper'); createUpgradeButton('Click', startX + 8 * (buttonWidth + spacing), 'clickCost', 'clickDamage', 10, 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(); } var canPurchaseAny = false; for (var key in upgradeButtons) { upgradeButtons[key].updateState(); if (upgradeButtons[key].interactive) { canPurchaseAny = true; } } upgradeButton.tint = canPurchaseAny ? 0x00ffff : 0x666666; upgradeButton.interactive = canPurchaseAny; } function applySplashDamage(brick) { var splashDamage = Math.floor(upgrades.splashPower / 2); var horizontalMaxDistance = BRICK_WIDTH + 15; var verticalMaxDistance = BRICK_HEIGHT + 15; bricks.forEach(function (adjBrick) { if (adjBrick === brick || adjBrick.health <= 0) { return; } var dx = Math.abs(adjBrick.x - brick.x); var dy = Math.abs(adjBrick.y - brick.y); var isHorizontalAdjacent = dx <= horizontalMaxDistance && dy <= BRICK_HEIGHT / 2; var isVerticalAdjacent = dy <= verticalMaxDistance && dx <= BRICK_WIDTH / 2; if (isHorizontalAdjacent || isVerticalAdjacent) { adjBrick.hit(splashDamage); ballDamage['splash'] = (ballDamage['splash'] || 0) + splashDamage; storage.ballDamage = Object.assign({}, ballDamage); } }); } function scatterOnImpact(ball) { var numBalls = 4; for (var i = 0; i < numBalls; i++) { var smallBall = new Ball('smallScatter'); smallBall.x = ball.x; smallBall.y = ball.y; var angle = Math.random() * 2 * Math.PI; var speedVariation = 0.8 + Math.random() * 0.4; smallBall.speed = upgrades.scatterSpeed * 0.8 * speedVariation; smallBall.velocity.x = Math.cos(angle); smallBall.velocity.y = Math.sin(angle); var magnitude = Math.sqrt(smallBall.velocity.x * smallBall.velocity.x + smallBall.velocity.y * smallBall.velocity.y); smallBall.velocity.x /= magnitude; smallBall.velocity.y /= magnitude; balls.push(smallBall); game.addChild(smallBall); ballDamage['scatter'] = (ballDamage['scatter'] || 0) + ball.power; storage.ballDamage = Object.assign({}, ballDamage); } } 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 createBricks() { var config = levelConfig[level] || {}; var totalBricks = config.totalBricks || 50; var baseHitpoints = config.hitpoints || 1; var pattern = config.pattern || 'grid'; var spacingX = -15; var spacingY = 0; 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) / 2 + 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); 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); 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); brickCount++; } } else if (pattern === 'cross') { var centerRow = Math.floor(rows / 2); var centerCol = Math.floor(cols / 2); for (var i = 0; i < rows && brickCount < totalBricks; i++) { for (var colOffset = -2; colOffset <= 2 && brickCount < totalBricks; colOffset++) { var col = centerCol + colOffset; if (col >= 0 && col < cols) { var x = startX + col * (BRICK_WIDTH + spacingX); var y = startY + i * (BRICK_HEIGHT + spacingY); var distanceFromCenter = Math.max(Math.abs(i - centerRow), Math.abs(colOffset)); var hitpoints = Math.max(1, baseHitpoints - distanceFromCenter); addBrick(x, y, hitpoints); brickCount++; } } } for (var j = 0; j < cols && brickCount < totalBricks; j++) { if (Math.abs(j - centerCol) > 2) { for (var rowOffset = -2; rowOffset <= 2 && brickCount < totalBricks; rowOffset++) { var row = centerRow + rowOffset; if (row >= 0 && row < rows) { var x = startX + j * (BRICK_WIDTH + spacingX); var y = startY + row * (BRICK_HEIGHT + spacingY); var distanceFromCenter = Math.max(Math.abs(j - centerCol), Math.abs(rowOffset)); var hitpoints = Math.max(1, baseHitpoints - distanceFromCenter); addBrick(x, y, hitpoints); brickCount++; } } } } for (var j = 0; j < cols && brickCount < totalBricks; j++) { if (Math.abs(j - centerCol) > 3) { for (var rowOffset = -1; rowOffset <= 1 && brickCount < totalBricks; rowOffset++) { var row = centerRow + rowOffset; if (row >= 0 && row < rows) { var x = startX + j * (BRICK_WIDTH + spacingX); var y = startY + row * (BRICK_HEIGHT + spacingY); var distanceFromCenter = Math.max(Math.abs(j - centerCol), Math.abs(rowOffset)); var hitpoints = Math.max(1, baseHitpoints - distanceFromCenter); addBrick(x, y, hitpoints); brickCount++; } } } } } else if (pattern === 'custom') { var customPattern = config.customPattern || []; var patternRows = customPattern.length; var patternCols = patternRows > 0 ? customPattern[0].length : 0; rows = Math.max(rows, patternRows); totalHeight = rows * BRICK_HEIGHT + (rows - 1) * spacingY; startY = (GAME_HEIGHT - totalHeight) / 2 + BRICK_HEIGHT / 2; if (patternRows > 0 && patternCols <= cols) { var offsetX = Math.floor((cols - patternCols) / 2); var offsetY = Math.floor((rows - patternRows) / 2); for (var i = 0; i < patternRows && brickCount < totalBricks; i++) { for (var j = 0; j < patternCols && brickCount < totalBricks; j++) { var hitpoints = customPattern[i][j]; if (hitpoints > 0) { var x = startX + (j + offsetX) * (BRICK_WIDTH + spacingX); var y = startY + (i + offsetY) * (BRICK_HEIGHT + spacingY); addBrick(x, y, hitpoints); 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) { 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); } function repositionBall(ball, index) { var spawnFromTop = index % 2 === 0; var safeX = GAME_WIDTH / 2 + (Math.random() * 200 - 100); var safeY = spawnFromTop ? BALL_RADIUS + 50 : GAME_HEIGHT - BALL_RADIUS - 50; var attempts = 0; var maxAttempts = 10; while (attempts < maxAttempts && isOverlappingWithBricks(safeX, safeY)) { safeX = GAME_WIDTH / 2 + (Math.random() * 200 - 100); safeY = spawnFromTop ? BALL_RADIUS + 50 + attempts * 50 : GAME_HEIGHT - BALL_RADIUS - 50 - attempts * 50; attempts++; } ball.x = safeX; ball.y = safeY; var angle = (Math.random() * 0.5 + 0.25) * Math.PI; ball.velocity.x = Math.cos(angle); ball.velocity.y = spawnFromTop ? Math.sin(angle) : -Math.sin(angle); var magnitude = Math.sqrt(ball.velocity.x * ball.velocity.x + ball.velocity.y * ball.velocity.y); ball.velocity.x /= magnitude; ball.velocity.y /= magnitude; } function isOverlappingWithBricks(x, y) { var ballLeft = x - BALL_RADIUS; var ballRight = x + BALL_RADIUS; var ballTop = y - BALL_RADIUS; var ballBottom = y + BALL_RADIUS; for (var i = 0; i < bricks.length; i++) { var brick = bricks[i]; var brickLeft = brick.x - BRICK_WIDTH / 2; var brickRight = brick.x + BRICK_WIDTH / 2; var brickTop = brick.y - BRICK_HEIGHT / 2; var brickBottom = brick.y + BRICK_HEIGHT / 2; if (ballLeft < brickRight && ballRight > brickLeft && ballTop < brickBottom && ballBottom > brickTop) { return true; } } return false; } function createBall() { var type = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'normal'; var ball = new Ball(type); repositionBall(ball, balls.length); balls.push(ball); game.addChild(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); } } if (bricks.length === 0 && !game.isGameOver) { if (level === Object.keys(levelConfig).length) { showEndScreen(); } else { level += 1; storage.level = level; levelTxt.setText('Level: ' + level); createBricks(); balls.forEach(function (ball, index) { repositionBall(ball, index); }); } } lastRunTime += 1; // Increment lastRunTime by 1 second for each update storage.lastRunTime = lastRunTime; 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); powerupContainer.visible = false; 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 gameTitleContainer = new Container(); game.addChild(gameTitleContainer); var gameTitle = new GameTitle(); gameTitle.x = GAME_WIDTH / 2; gameTitle.y = GAME_HEIGHT / 2 - 1050; gameTitleContainer.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: 600 }, { duration: 1000, easing: tween.bounceOut }); var startButton = new StartButton(); startButton.x = GAME_WIDTH / 2; startButton.y = GAME_HEIGHT / 2 + 400; game.addChild(startButton); function animateStartButton() { tween(startButton, { y: 1500 }, { duration: 1000, easing: tween.easeInOut, onFinish: function onFinish() { tween(startButton, { y: startButton.y + 50 }, { duration: 1000, easing: tween.easeInOut, onFinish: animateStartButton }); } }); } animateStartButton(); var resetButton = new ResetButton(); resetButton.x = GAME_WIDTH / 2; resetButton.y = GAME_HEIGHT + 200; game.addChild(resetButton); tween(resetButton, { y: startButton.y + 800 }, { duration: 1000, easing: tween.bounceOut, onFinish: function onFinish() { tween(resetButton, { y: resetButton.y + 20 }, { duration: 1000, easing: tween.easeInOut }); } }); LK.playMusic('backgroundmusic'); function startTransitionSequence() { tween(gameTitle, { y: -500, alpha: 0 }, { duration: 800, easing: tween.easeIn }); tween(resetButton, { y: GAME_HEIGHT + 200, alpha: 0 }, { duration: 800, easing: tween.easeIn, onFinish: function onFinish() { LK.setTimeout(function () { startGameLogic(); }, 500); } }); } function startGameLogic() { startButton.destroy(); gameTitle.destroy(); resetButton.visible = false; upgradeButton.visible = true; hud.visible = true; createBricks(); createBall('normal'); if (score > 0) { scoreTxt.setText('$' + score.toString()); } lastRunStartTime = Date.now(); lastRunTime = storage.lastRunTime || 0; // Initialize lastRunTime from storage if (storage.firstLoad !== true) { welcomeText = LK.getAsset('welcomemsg', { anchorX: 0.5, anchorY: 0.5, x: GAME_WIDTH / 2, y: GAME_HEIGHT / 2 + 600 }); game.addChild(welcomeText); storage.firstLoad = true; } playTimeInterval = LK.setInterval(function () { playTime += 1; storage.playTime = playTime; }, 1000); for (var type in ballQuantities) { for (var i = 0; i < ballQuantities[type]; i++) { createBall(type); } } 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); } } if (bricks.length === 0 && !game.isGameOver) { if (level === Object.keys(levelConfig).length) { showEndScreen(); } else { level += 1; storage.level = level; levelTxt.setText('Level: ' + level); createBricks(); balls.forEach(function (ball, index) { repositionBall(ball, index); }); } } updateButtonStates(); }; } function startGame() {} game.update = function () { for (var i = 0; i < stars.length; i++) { stars[i].update(); } };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
lastRunStartTime: "undefined",
lastRunTime: 0,
ballDamage: {
normal: 0,
splash: 0,
sniper: 0,
scatter: 0,
smallScatter: 0
}
});
/****
* 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;
if (type === 'smallScatter') {
ballGraphics.scaleX = 0.5;
ballGraphics.scaleY = 0.5;
}
self.type = type;
self.speed = type === 'splash' ? upgrades.splashSpeed : type === 'sniper' ? upgrades.sniperSpeed : type === 'scatter' ? upgrades.scatterSpeed : type === 'smallScatter' ? upgrades.scatterSpeed * 1.5 : 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.8)) : upgrades.normalPower;
self.velocity = {
x: 1,
y: -1
};
self.sniperCooldown = 0;
self.sniperCooldownMax = 60;
self.interactive = true;
self.down = function (x, y, obj) {
var angle = Math.random() * 2 * Math.PI;
self.velocity.x = Math.cos(angle);
self.velocity.y = Math.sin(angle);
var magnitude = Math.sqrt(self.velocity.x * self.velocity.x + self.velocity.y * self.velocity.y);
self.velocity.x /= magnitude;
self.velocity.y /= magnitude;
};
self.update = function () {
var SHIELD_BUFFER = 5;
var MAX_STEP = BALL_RADIUS / 2;
var totalDistance = self.speed;
var steps = Math.ceil(totalDistance / MAX_STEP);
var stepDistance = totalDistance / steps;
var splashApplied = false;
if (self.type === 'sniper') {
self.sniperCooldown--;
if (self.sniperCooldown <= 0) {
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.velocity.x = dx / magnitude;
self.velocity.y = dy / magnitude;
self.sniperCooldown = self.sniperCooldownMax;
}
}
}
for (var step = 0; step < steps; step++) {
var dx = self.velocity.x * stepDistance;
var dy = self.velocity.y * stepDistance;
var nextX = self.x + dx;
var nextY = self.y + dy;
if (nextX < BALL_RADIUS + SHIELD_BUFFER) {
nextX = BALL_RADIUS + SHIELD_BUFFER;
self.velocity.x = -self.velocity.x;
self.velocity.y += (Math.random() - 0.5) * 0.1;
LK.getSound('bounce').play();
} else if (nextX > GAME_WIDTH - BALL_RADIUS - SHIELD_BUFFER) {
nextX = GAME_WIDTH - BALL_RADIUS - SHIELD_BUFFER;
self.velocity.x = -self.velocity.x;
self.velocity.y += (Math.random() - 0.5) * 0.1;
LK.getSound('bounce').play();
}
if (nextY < BALL_RADIUS + SHIELD_BUFFER) {
nextY = BALL_RADIUS + SHIELD_BUFFER;
self.velocity.y = -self.velocity.y;
self.velocity.x += (Math.random() - 0.5) * 0.1;
LK.getSound('bounce').play();
} else if (nextY > GAME_HEIGHT - BALL_RADIUS - SHIELD_BUFFER) {
nextY = GAME_HEIGHT - BALL_RADIUS - SHIELD_BUFFER;
self.velocity.y = -self.velocity.y;
self.velocity.x += (Math.random() - 0.5) * 0.1;
LK.getSound('bounce').play();
}
var ballLeft = nextX - BALL_RADIUS - SHIELD_BUFFER;
var ballRight = nextX + BALL_RADIUS + SHIELD_BUFFER;
var ballTop = nextY - BALL_RADIUS - SHIELD_BUFFER;
var ballBottom = nextY + BALL_RADIUS + SHIELD_BUFFER;
var collided = false;
for (var i = bricks.length - 1; i >= 0; i--) {
var brick = bricks[i];
if (brick.health <= 0) {
continue;
}
var brickLeft = brick.x - BRICK_WIDTH / 2;
var brickRight = brick.x + BRICK_WIDTH / 2;
var brickTop = brick.y - BRICK_HEIGHT / 2;
var brickBottom = brick.y + BRICK_HEIGHT / 2;
if (ballLeft < brickRight && ballRight > brickLeft && ballTop < brickBottom && ballBottom > brickTop) {
var overlapLeft = ballRight - brickLeft;
var overlapRight = brickRight - ballLeft;
var overlapTop = ballBottom - brickTop;
var overlapBottom = brickBottom - ballTop;
var minOverlap = Math.min(overlapLeft, overlapRight, overlapTop, overlapBottom);
if (minOverlap === overlapLeft) {
nextX = brickLeft - BALL_RADIUS - SHIELD_BUFFER;
self.velocity.x = -self.velocity.x;
} else if (minOverlap === overlapRight) {
nextX = brickRight + BALL_RADIUS + SHIELD_BUFFER;
self.velocity.x = -self.velocity.x;
} else if (minOverlap === overlapTop) {
nextY = brickTop - BALL_RADIUS - SHIELD_BUFFER;
self.velocity.y = -self.velocity.y;
} else {
nextY = brickBottom + BALL_RADIUS + SHIELD_BUFFER;
self.velocity.y = -self.velocity.y;
}
self.velocity.x += (Math.random() - 0.5) * 0.2;
self.velocity.y += (Math.random() - 0.5) * 0.2;
LK.getSound('bounce').play();
brick.hit(self.power);
ballDamage[self.type] = (ballDamage[self.type] || 0) + self.power;
storage.ballDamage = Object.assign({}, ballDamage);
if (self.type === 'splash' && !splashApplied) {
applySplashDamage(brick);
splashApplied = true;
} else if (self.type === 'scatter') {
scatterOnImpact(self);
self.destroy();
balls.splice(balls.indexOf(self), 1);
return;
}
collided = true;
break;
}
}
self.x = nextX;
self.y = nextY;
if (collided) {
break;
}
}
var magnitude = Math.sqrt(self.velocity.x * self.velocity.x + self.velocity.y * self.velocity.y);
if (magnitude > 0) {
self.velocity.x /= magnitude;
self.velocity.y /= magnitude;
}
};
});
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);
self.updateTint = function () {
var baseColors = LEVEL_COLORS;
var colorCount = baseColors.length;
if (self.health <= colorCount) {
brickGraphics.tint = baseColors[self.health - 1];
} else {
var lastDigit = self.health % 10;
var colorIndex = lastDigit === 0 ? 9 : lastDigit - 1;
brickGraphics.tint = baseColors[colorIndex];
}
};
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,
scaleX: 1.35,
scaleY: 1.5
});
});
var ResetButton = Container.expand(function () {
var self = Container.call(this);
var buttonGraphics = self.attachAsset('resetButton', {
anchorX: 0.5,
anchorY: 0.5,
tint: 0xffc0cb
});
self.interactive = true;
self.down = function () {
LK.getSound('click').play();
clearLocalStorage();
storage.lastRunTime = 0; // Reset lastRunTime in storage
lastRunStartTime = Date.now();
storage.lastRunStartTime = lastRunStartTime;
// lastRunTime is no longer reset to ensure it persists across sessions
ballDamage = {
normal: 0,
splash: 0,
sniper: 0,
scatter: 0,
smallScatter: 0
};
storage.ballDamage = Object.assign({}, ballDamage);
playTime = 0;
storage.playTime = 0;
LK.showGameOver();
};
});
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,
scaleX: 1.2,
scaleY: 1.2
});
self.interactive = true;
self.down = function () {
LK.getSound('click').play();
self.interactive = false;
var explosionColors = [0xff0000, 0x00ff00, 0x0000ff, 0xffff00, 0xff00ff, 0x00ffff];
var randomColor = explosionColors[Math.floor(Math.random() * explosionColors.length)];
LK.getSound('explosion').play();
tween(self, {
tint: randomColor,
scaleX: 1.5,
scaleY: 1.5,
alpha: 0
}, {
duration: 400,
easing: tween.easeOut,
onFinish: function onFinish() {
startTransitionSequence();
}
});
};
});
var UpgradeButton = Container.expand(function () {
var self = Container.call(this);
var buttonGraphics = self.attachAsset('upgrade', {
anchorX: 0.5,
anchorY: 0.5,
tint: 0x00ffff
});
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
****/
var lastRunStartTime = storage.lastRunStartTime !== "undefined" ? parseInt(storage.lastRunStartTime) : Date.now();
var lastRunTime = storage.lastRunTime || 0;
var playTime = storage.playTime || 0;
var ballDamage = storage.ballDamage || {
normal: 0,
splash: 0,
sniper: 0,
scatter: 0,
smallScatter: 0
};
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;
}
function tintEndScreenTitle() {
gameTitleContainer.children.forEach(function (child) {
child.tint = 0xff33cc;
});
}
var frameThickness = 10;
var topFrame = new Container();
var topFrameGraphics = topFrame.attachAsset('frame', {
anchorX: 0,
anchorY: 0,
width: GAME_WIDTH,
height: frameThickness
});
topFrame.x = 0;
topFrame.y = 0;
game.addChild(topFrame);
function animateTopFrameTint() {
tween(topFrameGraphics, {
tint: 0xff33cc
}, {
duration: 2000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(topFrameGraphics, {
tint: 0x00ff99
}, {
duration: 2000,
easing: tween.easeInOut,
onFinish: animateTopFrameTint
});
}
});
}
animateTopFrameTint();
var bottomFrame = new Container();
var bottomFrameGraphics = bottomFrame.attachAsset('frame', {
anchorX: 0,
anchorY: 0,
width: GAME_WIDTH,
height: frameThickness
});
bottomFrame.x = 0;
bottomFrame.y = 2720;
game.addChild(bottomFrame); // Should be bottomFrame
function animateBottomFrameTint() {
tween(bottomFrameGraphics, {
tint: 0xff33cc
}, {
duration: 2000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(bottomFrameGraphics, {
tint: 0x00ff99
}, {
duration: 2000,
easing: tween.easeInOut,
onFinish: animateBottomFrameTint
});
}
});
}
animateBottomFrameTint();
var leftFrame = new Container();
var leftFrameGraphics = leftFrame.attachAsset('frame', {
anchorX: 0,
anchorY: 0,
width: frameThickness,
height: GAME_HEIGHT
});
leftFrame.x = 0;
leftFrame.y = 0;
game.addChild(leftFrame);
function animateLeftFrameTint() {
tween(leftFrameGraphics, {
tint: 0xff33cc
}, {
duration: 2000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(leftFrameGraphics, {
tint: 0x00ff99
}, {
duration: 2000,
easing: tween.easeInOut,
onFinish: animateLeftFrameTint
});
}
});
}
animateLeftFrameTint();
var rightFrame = new Container();
var rightFrameGraphics = rightFrame.attachAsset('frame', {
anchorX: 0,
anchorY: 0,
width: frameThickness,
height: GAME_HEIGHT
});
rightFrame.x = 2040;
rightFrame.y = 0;
game.addChild(rightFrame);
function animateRightFrameTint() {
tween(rightFrameGraphics, {
tint: 0xff33cc
}, {
duration: 2000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(rightFrameGraphics, {
tint: 0x00ff99
}, {
duration: 2000,
easing: tween.easeInOut,
onFinish: animateRightFrameTint
});
}
});
}
animateRightFrameTint();
var welcomeText = null;
function showEndScreen() {
game.isGameOver = true;
hud.visible = false;
powerupContainer.visible = false;
upgradeButton.visible = false;
balls.forEach(function (ball) {
return ball.visible = true;
});
bricks.forEach(function (brick) {
return brick.visible = false;
});
storage.ballDamage = Object.assign({}, ballDamage);
var congratsText = new Text2('Congratulations! You broke them ALL!', {
size: 100,
fill: 0xffffff
});
congratsText.anchor.set(0.5, 0);
congratsText.x = GAME_WIDTH / 2;
congratsText.y = GAME_HEIGHT / 2 - 600;
game.addChild(congratsText);
var gameTitle = new GameTitle();
gameTitle.x = GAME_WIDTH / 2;
gameTitle.y = congratsText.y - 300;
gameTitleContainer.addChild(gameTitle);
function animateEndTitleColor() {
tintEndScreenTitle();
tween(gameTitle, {
tint: 0xff33cc
}, {
duration: 2000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(gameTitle, {
tint: 0x00ff99
}, {
duration: 2000,
easing: tween.easeInOut,
onFinish: animateEndTitleColor
});
}
});
}
animateEndTitleColor();
var playTimeText = new Text2('Total Time Played: ' + playTime + ' seconds', {
size: 60,
fill: 0xffffff
});
playTimeText.anchor.set(0.5, 0);
playTimeText.x = GAME_WIDTH / 2;
playTimeText.y = congratsText.y + 200;
game.addChild(playTimeText);
var lastRunTimeText = new Text2('Last Run Time: ' + lastRunTime + ' seconds', {
size: 60,
fill: 0xffffff
});
lastRunTimeText.anchor.set(0.5, 0);
lastRunTimeText.x = GAME_WIDTH / 2;
lastRunTimeText.y = playTimeText.y + 80;
game.addChild(lastRunTimeText);
var damageText = new Text2('Damage Dealt This Run:\n' + 'Normal: ' + ballDamage.normal + '\n' + 'Splash: ' + ballDamage.splash + '\n' + 'Sniper: ' + ballDamage.sniper + '\n' + 'Scatter: ' + ballDamage.scatter + '\n' + 'Small Scatter: ' + ballDamage.smallScatter, {
size: 50,
fill: 0xffffff,
align: 'center'
});
damageText.anchor.set(0.5, 0);
damageText.x = GAME_WIDTH / 2;
damageText.y = lastRunTimeText.y + 120;
game.addChild(damageText);
var noteText = new Text2('Start over with all your upgrades, or reset for a fresh new run! Your choice!', {
size: 50,
fill: 0xffffff
});
noteText.anchor.set(0.5, 0);
noteText.x = GAME_WIDTH / 2;
noteText.y = damageText.y + 500;
game.addChild(noteText);
var endGameButton = new Container();
var buttonGraphics = endGameButton.attachAsset('endGameButton', {
anchorX: 0.5,
anchorY: 0.5,
tint: 0x00ffff
});
endGameButton.x = GAME_WIDTH / 2;
endGameButton.y = noteText.y + 300;
endGameButton.interactive = true;
endGameButton.down = function () {
storage.level = 1;
storage.lastRunTime = 0; // Clear lastRunTime from storage
lastRunStartTime = Date.now();
storage.lastRunStartTime = lastRunStartTime;
ballDamage = {
normal: 0,
splash: 0,
sniper: 0,
scatter: 0,
smallScatter: 0
};
storage.ballDamage = Object.assign({}, ballDamage);
LK.showGameOver();
};
game.addChild(endGameButton);
var resetButton = new ResetButton();
resetButton.x = GAME_WIDTH / 2;
resetButton.y = endGameButton.y + 260;
game.addChild(resetButton);
LK.clearInterval(playTimeInterval);
playTimeInterval = null;
}
var GAME_WIDTH = 2048;
var GAME_HEIGHT = 2720;
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: 28,
hitpoints: 1,
pattern: 'clustered'
},
2: {
totalBricks: 63,
hitpoints: 2,
pattern: 'staggered'
},
3: {
totalBricks: 63,
hitpoints: 3,
pattern: 'cross'
},
4: {
totalBricks: 77,
pattern: 'custom',
customPattern: [[0, 3, 3, 0, 3, 3, 0], [3, 4, 4, 3, 4, 4, 3], [3, 4, 4, 4, 4, 4, 3], [3, 4, 4, 4, 4, 4, 3], [3, 4, 4, 4, 4, 4, 3], [3, 4, 4, 4, 4, 4, 3], [3, 4, 4, 4, 4, 4, 3], [0, 3, 4, 4, 4, 3, 0], [0, 0, 3, 4, 3, 0, 0], [0, 0, 0, 3, 0, 0, 0], [0, 0, 0, 3, 0, 0, 0]]
},
5: {
totalBricks: 112,
pattern: 'custom',
customPattern: [[0, 0, 0, 3, 0, 0, 0], [0, 0, 3, 4, 3, 0, 0], [0, 3, 4, 5, 4, 3, 0], [3, 4, 5, 6, 5, 4, 3], [4, 5, 6, 7, 6, 5, 4], [3, 5, 6, 7, 6, 5, 3], [0, 4, 5, 6, 5, 4, 0], [0, 3, 4, 5, 4, 3, 0], [0, 0, 3, 4, 3, 0, 0], [0, 0, 2, 3, 2, 0, 0], [0, 0, 1, 2, 1, 0, 0], [0, 0, 0, 1, 0, 0, 0], [0, 0, 1, 2, 1, 0, 0], [0, 0, 1, 2, 1, 0, 0]]
},
6: {
totalBricks: 60,
hitpoints: 10,
pattern: 'cross'
},
7: {
totalBricks: 70,
hitpoints: 13,
pattern: 'clustered'
},
8: {
totalBricks: 112,
hitpoints: 15,
pattern: 'clustered'
},
9: {
totalBricks: 112,
hitpoints: 20,
pattern: 'cross'
},
10: {
totalBricks: 112,
hitpoints: 30,
pattern: 'clustered'
},
11: {
totalBricks: 112,
hitpoints: 50,
pattern: 'clustered'
},
12: {
totalBricks: 112,
hitpoints: 99,
pattern: 'clustered'
},
13: {
totalBricks: 140,
pattern: 'custom',
customPattern: [[0, 222, 0, 0, 0, 222, 0], [0, 222, 0, 0, 0, 222, 0], [0, 222, 0, 0, 0, 222, 0], [0, 222, 0, 0, 0, 222, 0], [0, 222, 0, 0, 0, 222, 0], [0, 222, 0, 0, 0, 222, 0], [0, 222, 0, 0, 0, 222, 0], [0, 222, 0, 0, 0, 222, 0], [0, 222, 0, 0, 0, 222, 0], [0, 222, 0, 0, 0, 222, 0], [0, 222, 0, 0, 0, 222, 0], [0, 222, 222, 222, 222, 222, 0], [0, 222, 222, 222, 222, 222, 0]]
},
14: {
totalBricks: 140,
pattern: 'custom',
customPattern: [[0, 333, 333, 333, 333, 333, 0], [0, 333, 333, 333, 333, 333, 0], [0, 333, 0, 0, 0, 333, 0], [0, 333, 0, 0, 0, 333, 0], [0, 333, 0, 0, 0, 333, 0], [0, 333, 333, 333, 333, 333, 0], [0, 333, 333, 333, 333, 333, 0], [0, 333, 333, 0, 0, 0, 0], [0, 333, 333, 0, 0, 0, 0], [0, 333, 333, 0, 0, 0, 0], [0, 333, 333, 0, 0, 0, 0], [0, 333, 333, 0, 0, 0, 0], [0, 333, 333, 0, 0, 0, 0], [0, 333, 333, 0, 0, 0, 0]]
},
15: {
totalBricks: 140,
pattern: 'custom',
customPattern: [[0, 444, 444, 444, 0], [0, 444, 444, 444, 0], [0, 444, 444, 444, 0], [0, 444, 444, 444, 0], [0, 444, 444, 444, 0], [0, 444, 444, 444, 0], [0, 444, 444, 444, 0], [0, 444, 444, 444, 0], [0, 444, 444, 444, 0], [0, 444, 444, 444, 0], [0, 444, 444, 444, 0], [0, 444, 444, 444, 0], [0, 444, 444, 444, 0]]
},
16: {
totalBricks: 140,
pattern: 'custom',
customPattern: [[555, 555, 555, 555, 555, 555, 555], [555, 555, 555, 555, 555, 555, 555], [555, 555, 555, 555, 555, 555, 555], [0, 0, 555, 555, 555, 0, 0], [0, 0, 555, 555, 555, 0, 0], [0, 0, 555, 555, 555, 0, 0], [0, 0, 555, 555, 555, 0, 0], [0, 0, 555, 555, 555, 0, 0], [0, 0, 555, 555, 555, 0, 0], [0, 0, 555, 555, 555, 0, 0], [0, 0, 555, 555, 555, 0, 0], [0, 0, 555, 555, 555, 0, 0], [0, 0, 555, 555, 555, 0, 0]]
}
};
var upgrades = storage.upgrades || {
normalSpeed: 1,
normalPower: 1,
splashSpeed: 1,
splashPower: 2,
sniperSpeed: 1,
sniperPower: 1,
scatterSpeed: 1,
scatterPower: 1,
clickDamage: 1,
normalSpeedCost: 10,
normalPowerCost: 50,
splashSpeedCost: 75,
splashPowerCost: 75,
sniperSpeedCost: 100,
sniperPowerCost: 100,
scatterSpeedCost: 125,
scatterPowerCost: 125,
clickCost: 10,
normalBallCost: 10,
splashBallCost: 150,
sniperBallCost: 500,
scatterBallCost: 2000
};
var balls = [];
var ballQuantities = storage.ballQuantities || {
normal: 0,
splash: 0,
sniper: 0,
scatter: 0
};
var bricks = [];
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
};
var playTime = storage.playTime || 0;
var playTimeInterval = LK.setInterval(function () {
if (!game.isGameOver) {
playTime += 1;
lastRunTime += 1; // Increment lastRunTime by 1 second for each update
storage.playTime = playTime;
storage.lastRunTime = lastRunTime; // Persist lastRunTime to storage
}
}, 1000);
game.isGameOver = false;
function clearLocalStorage() {
storage.score = 0;
storage.level = 1;
storage.unlockedTiers = {
normal: true,
splash: false,
sniper: false,
scatter: false
};
unlockedTiers = Object.assign({}, storage.unlockedTiers);
storage.upgrades = {
normalSpeed: 1,
normalPower: 1,
splashSpeed: 1,
splashPower: 2,
sniperSpeed: 1,
sniperPower: 1,
scatterSpeed: 1,
scatterPower: 1,
clickDamage: 1,
normalSpeedCost: 50,
normalPowerCost: 50,
splashSpeedCost: 75,
splashPowerCost: 75,
sniperSpeedCost: 100,
sniperPowerCost: 100,
scatterSpeedCost: 125,
scatterPowerCost: 125,
clickCost: 10,
normalBallCost: 10,
splashBallCost: 150,
sniperBallCost: 500,
scatterBallCost: 2000
};
storage.lastRunStartTime = Date.now();
storage.lastRunTime = 0;
storage.ballDamage = {
normal: 0,
splash: 0,
sniper: 0,
scatter: 0,
smallScatter: 0
};
score = 0;
storage.ballQuantities = {
normal: 0,
splash: 0,
sniper: 0,
scatter: 0
};
storage.firstLoad = false;
ballQuantities = Object.assign({}, storage.ballQuantities);
level = 1;
unlockedTiers = Object.assign({}, storage.unlockedTiers);
scoreTxt.setText('$' + score.toString());
levelTxt.setText('Level: ' + level);
updateButtonStates();
}
var hud = new Container();
LK.gui.top.addChild(hud);
var scoreTxt = new Text2('$0', {
size: 60,
fill: 0x39ff14
});
scoreTxt.anchor.set(0.3, 0);
scoreTxt.x += 570;
hud.addChild(scoreTxt);
var levelTxt = new Text2('Level: ' + level, {
size: 50,
fill: 0xffffff
});
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: 40,
fill: 0xffffff
});
costText.anchor.set(0.5, 0);
costText.y = 100;
button.addChild(costText);
button.interactive = true;
button.down = function () {
if (score < upgrades[type + 'BallCost']) {
return;
}
if (welcomeText && welcomeText.parent) {
welcomeText.parent.removeChild(welcomeText);
}
score -= upgrades[type + 'BallCost'];
scoreTxt.setText('$' + score.toString());
createBall(type);
ballQuantities[type] = (ballQuantities[type] || 0) + 1;
storage.ballQuantities = Object.assign({}, ballQuantities);
upgrades[type + 'BallCost'] = Math.floor(upgrades[type + 'BallCost'] * 2);
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;
if (isEnabled) {
ballIcon.tint = type === 'splash' ? 0xff0066 : type === 'sniper' ? 0x00ff99 : type === 'scatter' ? 0xffff00 : 0xffffff;
typeText.fill = 0xffffff;
costText.fill = 0x3bc7e0;
} else {
ballIcon.tint = 0x666666;
typeText.fill = 0x666666;
costText.fill = 0x666666;
}
};
hud.addChild(button);
ballButtons[type] = button;
}
createBallButton('normal', -450, 10, '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); // Should be 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.replace('Speed', 'SPD').replace('Power', 'DMG'), " +").concat(upgrades[upgradeKey]), {
size: upgradeKey === 'clickDamage' ? 40 : 40,
fill: 0x000000,
fontWeight: 'bold'
});
labelText.anchor.set(0.5, 0);
labelText.y = 80;
contentContainer.addChild(labelText);
var currentCost = baseCost * Math.pow(2, upgrades[upgradeKey] - 1);
var costText = new Text2("$".concat(currentCost), {
size: 40,
fill: 0x000000
});
costText.anchor.set(0.5, 0);
costText.y = 140;
contentContainer.addChild(costText);
button.interactive = true;
button.down = function () {
if (welcomeText && welcomeText.parent) {
welcomeText.parent.removeChild(welcomeText);
}
var cost = baseCost * Math.pow(2, upgrades[upgradeKey] - 1);
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);
var newCost = baseCost * Math.pow(2, upgrades[upgradeKey] - 1);
costText.setText("$".concat(newCost));
labelText.setText("".concat(labelPrefix, " +").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);
}
updateButtonStates();
};
button.updateState = function () {
var ballType = upgradeKey.split('Speed')[0].split('Power')[0];
var currentCost = baseCost * Math.pow(2, upgrades[upgradeKey] - 1);
var isEnabled = upgradeKey === 'clickDamage' ? score >= currentCost : unlockedTiers[ballType] && (prevTier ? unlockedTiers[prevTier] : true) && score >= currentCost;
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;
}
labelText.fill = isEnabled ? 0x000000 : 0x666666;
costText.fill = isEnabled ? 0x000000 : 0x666666;
costText.setText("$".concat(currentCost));
};
powerupContainer.addChild(button);
upgradeButtons[upgradeKey] = button;
}
var buttonWidth = 150;
var spacing = 70;
var totalButtons = 9;
var totalWidth = totalButtons * buttonWidth + (totalButtons - 1) * spacing;
var startX = (GAME_WIDTH - totalWidth) / 2;
createUpgradeButton('Speed', startX, 'normalSpeedCost', 'normalSpeed', 10, 'ball', null);
createUpgradeButton('Power', startX + (buttonWidth + spacing), 'normalPowerCost', 'normalPower', 50, 'ball', null);
createUpgradeButton('Speed', startX + 2 * (buttonWidth + spacing), 'splashSpeedCost', 'splashSpeed', 75, 'splashBall', 'normal');
createUpgradeButton('Power', startX + 3 * (buttonWidth + spacing), 'splashPowerCost', 'splashPower', 75, 'splashBall', 'normal');
createUpgradeButton('Speed', startX + 4 * (buttonWidth + spacing), 'sniperSpeedCost', 'sniperSpeed', 100, 'sniperBall', 'splash');
createUpgradeButton('Power', startX + 5 * (buttonWidth + spacing), 'sniperPowerCost', 'sniperPower', 100, 'sniperBall', 'splash');
createUpgradeButton('Speed', startX + 6 * (buttonWidth + spacing), 'scatterSpeedCost', 'scatterSpeed', 125, 'scatterBall', 'sniper');
createUpgradeButton('Power', startX + 7 * (buttonWidth + spacing), 'scatterPowerCost', 'scatterPower', 125, 'scatterBall', 'sniper');
createUpgradeButton('Click', startX + 8 * (buttonWidth + spacing), 'clickCost', 'clickDamage', 10, 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();
}
var canPurchaseAny = false;
for (var key in upgradeButtons) {
upgradeButtons[key].updateState();
if (upgradeButtons[key].interactive) {
canPurchaseAny = true;
}
}
upgradeButton.tint = canPurchaseAny ? 0x00ffff : 0x666666;
upgradeButton.interactive = canPurchaseAny;
}
function applySplashDamage(brick) {
var splashDamage = Math.floor(upgrades.splashPower / 2);
var horizontalMaxDistance = BRICK_WIDTH + 15;
var verticalMaxDistance = BRICK_HEIGHT + 15;
bricks.forEach(function (adjBrick) {
if (adjBrick === brick || adjBrick.health <= 0) {
return;
}
var dx = Math.abs(adjBrick.x - brick.x);
var dy = Math.abs(adjBrick.y - brick.y);
var isHorizontalAdjacent = dx <= horizontalMaxDistance && dy <= BRICK_HEIGHT / 2;
var isVerticalAdjacent = dy <= verticalMaxDistance && dx <= BRICK_WIDTH / 2;
if (isHorizontalAdjacent || isVerticalAdjacent) {
adjBrick.hit(splashDamage);
ballDamage['splash'] = (ballDamage['splash'] || 0) + splashDamage;
storage.ballDamage = Object.assign({}, ballDamage);
}
});
}
function scatterOnImpact(ball) {
var numBalls = 4;
for (var i = 0; i < numBalls; i++) {
var smallBall = new Ball('smallScatter');
smallBall.x = ball.x;
smallBall.y = ball.y;
var angle = Math.random() * 2 * Math.PI;
var speedVariation = 0.8 + Math.random() * 0.4;
smallBall.speed = upgrades.scatterSpeed * 0.8 * speedVariation;
smallBall.velocity.x = Math.cos(angle);
smallBall.velocity.y = Math.sin(angle);
var magnitude = Math.sqrt(smallBall.velocity.x * smallBall.velocity.x + smallBall.velocity.y * smallBall.velocity.y);
smallBall.velocity.x /= magnitude;
smallBall.velocity.y /= magnitude;
balls.push(smallBall);
game.addChild(smallBall);
ballDamage['scatter'] = (ballDamage['scatter'] || 0) + ball.power;
storage.ballDamage = Object.assign({}, ballDamage);
}
}
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 createBricks() {
var config = levelConfig[level] || {};
var totalBricks = config.totalBricks || 50;
var baseHitpoints = config.hitpoints || 1;
var pattern = config.pattern || 'grid';
var spacingX = -15;
var spacingY = 0;
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) / 2 + 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);
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);
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);
brickCount++;
}
} else if (pattern === 'cross') {
var centerRow = Math.floor(rows / 2);
var centerCol = Math.floor(cols / 2);
for (var i = 0; i < rows && brickCount < totalBricks; i++) {
for (var colOffset = -2; colOffset <= 2 && brickCount < totalBricks; colOffset++) {
var col = centerCol + colOffset;
if (col >= 0 && col < cols) {
var x = startX + col * (BRICK_WIDTH + spacingX);
var y = startY + i * (BRICK_HEIGHT + spacingY);
var distanceFromCenter = Math.max(Math.abs(i - centerRow), Math.abs(colOffset));
var hitpoints = Math.max(1, baseHitpoints - distanceFromCenter);
addBrick(x, y, hitpoints);
brickCount++;
}
}
}
for (var j = 0; j < cols && brickCount < totalBricks; j++) {
if (Math.abs(j - centerCol) > 2) {
for (var rowOffset = -2; rowOffset <= 2 && brickCount < totalBricks; rowOffset++) {
var row = centerRow + rowOffset;
if (row >= 0 && row < rows) {
var x = startX + j * (BRICK_WIDTH + spacingX);
var y = startY + row * (BRICK_HEIGHT + spacingY);
var distanceFromCenter = Math.max(Math.abs(j - centerCol), Math.abs(rowOffset));
var hitpoints = Math.max(1, baseHitpoints - distanceFromCenter);
addBrick(x, y, hitpoints);
brickCount++;
}
}
}
}
for (var j = 0; j < cols && brickCount < totalBricks; j++) {
if (Math.abs(j - centerCol) > 3) {
for (var rowOffset = -1; rowOffset <= 1 && brickCount < totalBricks; rowOffset++) {
var row = centerRow + rowOffset;
if (row >= 0 && row < rows) {
var x = startX + j * (BRICK_WIDTH + spacingX);
var y = startY + row * (BRICK_HEIGHT + spacingY);
var distanceFromCenter = Math.max(Math.abs(j - centerCol), Math.abs(rowOffset));
var hitpoints = Math.max(1, baseHitpoints - distanceFromCenter);
addBrick(x, y, hitpoints);
brickCount++;
}
}
}
}
} else if (pattern === 'custom') {
var customPattern = config.customPattern || [];
var patternRows = customPattern.length;
var patternCols = patternRows > 0 ? customPattern[0].length : 0;
rows = Math.max(rows, patternRows);
totalHeight = rows * BRICK_HEIGHT + (rows - 1) * spacingY;
startY = (GAME_HEIGHT - totalHeight) / 2 + BRICK_HEIGHT / 2;
if (patternRows > 0 && patternCols <= cols) {
var offsetX = Math.floor((cols - patternCols) / 2);
var offsetY = Math.floor((rows - patternRows) / 2);
for (var i = 0; i < patternRows && brickCount < totalBricks; i++) {
for (var j = 0; j < patternCols && brickCount < totalBricks; j++) {
var hitpoints = customPattern[i][j];
if (hitpoints > 0) {
var x = startX + (j + offsetX) * (BRICK_WIDTH + spacingX);
var y = startY + (i + offsetY) * (BRICK_HEIGHT + spacingY);
addBrick(x, y, hitpoints);
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) {
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);
}
function repositionBall(ball, index) {
var spawnFromTop = index % 2 === 0;
var safeX = GAME_WIDTH / 2 + (Math.random() * 200 - 100);
var safeY = spawnFromTop ? BALL_RADIUS + 50 : GAME_HEIGHT - BALL_RADIUS - 50;
var attempts = 0;
var maxAttempts = 10;
while (attempts < maxAttempts && isOverlappingWithBricks(safeX, safeY)) {
safeX = GAME_WIDTH / 2 + (Math.random() * 200 - 100);
safeY = spawnFromTop ? BALL_RADIUS + 50 + attempts * 50 : GAME_HEIGHT - BALL_RADIUS - 50 - attempts * 50;
attempts++;
}
ball.x = safeX;
ball.y = safeY;
var angle = (Math.random() * 0.5 + 0.25) * Math.PI;
ball.velocity.x = Math.cos(angle);
ball.velocity.y = spawnFromTop ? Math.sin(angle) : -Math.sin(angle);
var magnitude = Math.sqrt(ball.velocity.x * ball.velocity.x + ball.velocity.y * ball.velocity.y);
ball.velocity.x /= magnitude;
ball.velocity.y /= magnitude;
}
function isOverlappingWithBricks(x, y) {
var ballLeft = x - BALL_RADIUS;
var ballRight = x + BALL_RADIUS;
var ballTop = y - BALL_RADIUS;
var ballBottom = y + BALL_RADIUS;
for (var i = 0; i < bricks.length; i++) {
var brick = bricks[i];
var brickLeft = brick.x - BRICK_WIDTH / 2;
var brickRight = brick.x + BRICK_WIDTH / 2;
var brickTop = brick.y - BRICK_HEIGHT / 2;
var brickBottom = brick.y + BRICK_HEIGHT / 2;
if (ballLeft < brickRight && ballRight > brickLeft && ballTop < brickBottom && ballBottom > brickTop) {
return true;
}
}
return false;
}
function createBall() {
var type = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'normal';
var ball = new Ball(type);
repositionBall(ball, balls.length);
balls.push(ball);
game.addChild(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);
}
}
if (bricks.length === 0 && !game.isGameOver) {
if (level === Object.keys(levelConfig).length) {
showEndScreen();
} else {
level += 1;
storage.level = level;
levelTxt.setText('Level: ' + level);
createBricks();
balls.forEach(function (ball, index) {
repositionBall(ball, index);
});
}
}
lastRunTime += 1; // Increment lastRunTime by 1 second for each update
storage.lastRunTime = lastRunTime;
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);
powerupContainer.visible = false;
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 gameTitleContainer = new Container();
game.addChild(gameTitleContainer);
var gameTitle = new GameTitle();
gameTitle.x = GAME_WIDTH / 2;
gameTitle.y = GAME_HEIGHT / 2 - 1050;
gameTitleContainer.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: 600
}, {
duration: 1000,
easing: tween.bounceOut
});
var startButton = new StartButton();
startButton.x = GAME_WIDTH / 2;
startButton.y = GAME_HEIGHT / 2 + 400;
game.addChild(startButton);
function animateStartButton() {
tween(startButton, {
y: 1500
}, {
duration: 1000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(startButton, {
y: startButton.y + 50
}, {
duration: 1000,
easing: tween.easeInOut,
onFinish: animateStartButton
});
}
});
}
animateStartButton();
var resetButton = new ResetButton();
resetButton.x = GAME_WIDTH / 2;
resetButton.y = GAME_HEIGHT + 200;
game.addChild(resetButton);
tween(resetButton, {
y: startButton.y + 800
}, {
duration: 1000,
easing: tween.bounceOut,
onFinish: function onFinish() {
tween(resetButton, {
y: resetButton.y + 20
}, {
duration: 1000,
easing: tween.easeInOut
});
}
});
LK.playMusic('backgroundmusic');
function startTransitionSequence() {
tween(gameTitle, {
y: -500,
alpha: 0
}, {
duration: 800,
easing: tween.easeIn
});
tween(resetButton, {
y: GAME_HEIGHT + 200,
alpha: 0
}, {
duration: 800,
easing: tween.easeIn,
onFinish: function onFinish() {
LK.setTimeout(function () {
startGameLogic();
}, 500);
}
});
}
function startGameLogic() {
startButton.destroy();
gameTitle.destroy();
resetButton.visible = false;
upgradeButton.visible = true;
hud.visible = true;
createBricks();
createBall('normal');
if (score > 0) {
scoreTxt.setText('$' + score.toString());
}
lastRunStartTime = Date.now();
lastRunTime = storage.lastRunTime || 0; // Initialize lastRunTime from storage
if (storage.firstLoad !== true) {
welcomeText = LK.getAsset('welcomemsg', {
anchorX: 0.5,
anchorY: 0.5,
x: GAME_WIDTH / 2,
y: GAME_HEIGHT / 2 + 600
});
game.addChild(welcomeText);
storage.firstLoad = true;
}
playTimeInterval = LK.setInterval(function () {
playTime += 1;
storage.playTime = playTime;
}, 1000);
for (var type in ballQuantities) {
for (var i = 0; i < ballQuantities[type]; i++) {
createBall(type);
}
}
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);
}
}
if (bricks.length === 0 && !game.isGameOver) {
if (level === Object.keys(levelConfig).length) {
showEndScreen();
} else {
level += 1;
storage.level = level;
levelTxt.setText('Level: ' + level);
createBricks();
balls.forEach(function (ball, index) {
repositionBall(ball, index);
});
}
}
updateButtonStates();
};
}
function startGame() {}
game.update = function () {
for (var i = 0; i < stars.length; i++) {
stars[i].update();
}
};