Code edit (10 edits merged)
Please save this source code
User prompt
Please fix the bug: 'c is not defined' in or related to this line: 'c;' Line Number: 336
Code edit (13 edits merged)
Please save this source code
User prompt
When a the level ends, balls need to be pushed very fast to either the top or the bottom of the screen, in the same x position they currently were
Code edit (10 edits merged)
Please save this source code
User prompt
When a level is compelted apply a tween effect to the balls, and have them explode into the edges of the screen ↪💡 Consider importing and using the following plugins: @upit/tween.v1
Code edit (6 edits merged)
Please save this source code
User prompt
balls should start a little faster just a little, but do not change anyvalue of the buttons or price
Code edit (7 edits merged)
Please save this source code
User prompt
remove the comments form the custom patternsan put them in one line
Code edit (2 edits merged)
Please save this source code
User prompt
when you click on restart, delete from local storage the current level
User prompt
add 4 new levels
Code edit (16 edits merged)
Please save this source code
User prompt
Please fix the bug: 'TypeError: Cannot read properties of undefined (reading 'getLocalPosition')' in or related to this line: 'tween(p, arguments[0]);' Line Number: 1470 ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Please fix the bug: 'TypeError: Cannot read properties of undefined (reading 'getLocalPosition')' in or related to this line: 'tween(p, arguments[0]);' Line Number: 1470
Code edit (1 edits merged)
Please save this source code
User prompt
Make game title bigger font an container
User prompt
make the container 10% less wide
User prompt
move game title and container 100 pixels up
User prompt
move start button 100 pixels down and increase it size
User prompt
increase font of start
User prompt
move game title a little up
User prompt
move game title in homepage a little up
User prompt
move reset button in homepge 100 pixels up
/****
* Plugins
****/
var storage = LK.import("@upit/storage.v1");
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Ball = Container.expand(function () {
var self = Container.call(this);
var type = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'normal';
var ballGraphics = self.attachAsset('ball', {
anchorX: 0.5,
anchorY: 0.5
});
ballGraphics.tint = type === 'splash' ? 0xff0066 : type === 'sniper' ? 0x00ff99 : type === 'scatter' ? 0xffff00 : type === 'smallScatter' ? 0xffff00 : 0xffffff;
self.type = type;
self.speed = type === 'splash' ? upgrades.splashSpeed : type === 'sniper' ? upgrades.sniperSpeed : type === 'scatter' ? upgrades.scatterSpeed : type === 'smallScatter' ? upgrades.scatterSpeed * 0.8 : upgrades.normalSpeed;
self.power = type === 'splash' ? upgrades.splashPower : type === 'sniper' ? upgrades.sniperPower : type === 'scatter' ? upgrades.scatterPower : type === 'smallScatter' ? Math.max(1, Math.floor(upgrades.scatterPower * 0.5)) : upgrades.normalPower;
self.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; // Smaller steps to prevent tunneling
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;
// Move and check collisions in sub-steps
var nextX = self.x + dx;
var nextY = self.y + dy;
// Wall collisions
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();
}
// Brick collisions
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;
// AABB overlap check
if (ballLeft < brickRight && ballRight > brickLeft && ballTop < brickBottom && ballBottom > brickTop) {
// Determine collision side
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);
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; // Only handle one collision per sub-step
}
}
self.x = nextX;
self.y = nextY;
if (collided) {
break;
} // Stop sub-stepping after a collision
}
// Normalize velocity
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
});
var titleText = new Text2('Idle BrickBreaker', {
size: 150,
fill: 0x000000
});
titleText.anchor.set(0.5, 0.5);
self.addChild(titleText);
});
var ResetButton = Container.expand(function () {
var self = Container.call(this);
var buttonGraphics = self.attachAsset('resetButton', {
anchorX: 0.5,
anchorY: 0.5,
tint: 0xff6666
});
var buttonText = new Text2('RESET', {
size: 50,
fill: 0x000000,
fontWeight: 'bold'
});
buttonText.anchor.set(0.5, 0.5);
self.addChild(buttonText);
self.interactive = true;
self.down = function () {
LK.getSound('click').play();
clearLocalStorage();
playTime = 0;
storage.playTime = playTime;
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
});
var buttonText = new Text2('START', {
size: 80,
fill: 0x000000,
fontWeight: 'bold'
});
buttonText.anchor.set(0.5, 0.5);
self.addChild(buttonText);
self.interactive = true;
self.down = function () {
LK.getSound('click').play();
startGame();
};
});
var UpgradeButton = Container.expand(function () {
var self = Container.call(this);
var buttonGraphics = self.attachAsset('upgrade', {
anchorX: 0.5,
anchorY: 0.5,
tint: 0x00ffff
});
var buttonText = new Text2('UPGRADES', {
size: 50,
fill: 0x000000,
fontWeight: 'bold'
});
buttonText.anchor.set(0.5, 0.5);
self.addChild(buttonText);
self.interactive = true;
self.down = function () {
LK.getSound('click').play();
powerupContainer.visible = !powerupContainer.visible;
};
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a1a2e
});
/****
* Game Code
****/
c;
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);
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;
});
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 - 500 + 550;
game.addChild(congratsText);
var gameTitle = new GameTitle();
gameTitle.x = GAME_WIDTH / 2;
gameTitle.y = congratsText.y - 400;
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('Time Played: ' + playTime + ' seconds', {
size: 80,
fill: 0xffffff
});
playTimeText.anchor.set(0.5, 0);
playTimeText.x = GAME_WIDTH / 2;
playTimeText.y = congratsText.y + 300;
game.addChild(playTimeText);
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 = playTimeText.y + 400;
game.addChild(noteText);
var endGameButton = new Container();
var buttonGraphics = endGameButton.attachAsset('endGameButton', {
anchorX: 0.5,
anchorY: 0.5,
tint: 0x00ffff
});
var buttonText = new Text2('RESTART!', {
size: 50,
fill: 0x000000,
fontWeight: 'bold'
});
buttonText.anchor.set(0.5, 0.5);
endGameButton.addChild(buttonText);
endGameButton.x = GAME_WIDTH / 2;
endGameButton.y = GAME_HEIGHT - 240;
endGameButton.interactive = true;
endGameButton.down = function () {
LK.showGameOver();
};
game.addChild(endGameButton);
var resetButton = new ResetButton();
resetButton.x = GAME_WIDTH / 2;
resetButton.y = endGameButton.y + 160;
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: 63,
hitpoints: 2,
pattern: 'clustered'
},
2: {
totalBricks: 77,
hitpoints: 3,
pattern: 'clustered'
},
3: {
totalBricks: 91,
hitpoints: 6,
pattern: 'staggered'
},
4: {
totalBricks: 90,
hitpoints: 10,
pattern: 'cross'
},
5: {
totalBricks: 96,
hitpoints: 12,
pattern: 'staggered'
},
6: {
totalBricks: 112,
hitpoints: 15,
pattern: 'clustered'
},
7: {
totalBricks: 112,
hitpoints: 20,
pattern: 'staggered'
},
8: {
totalBricks: 112,
hitpoints: 99,
pattern: 'clustered'
},
9: {
totalBricks: 112,
hitpoints: 997,
pattern: 'staggered'
},
10: {
totalBricks: 112,
hitpoints: 9998,
pattern: 'clustered'
}
};
var upgrades = storage.upgrades || {
normalSpeed: 1,
normalPower: 1,
splashSpeed: 1,
splashPower: 2,
sniperSpeed: 1,
sniperPower: 1,
scatterSpeed: 1,
scatterPower: 1,
clickDamage: 1,
normalSpeedCost: 25,
normalPowerCost: 50,
splashSpeedCost: 75,
splashPowerCost: 75,
sniperSpeedCost: 100,
sniperPowerCost: 100,
scatterSpeedCost: 125,
scatterPowerCost: 125,
clickCost: 25,
normalBallCost: 25,
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;
storage.playTime = playTime;
console.log('Time Played2:', playTime, 'seconds');
}
}, 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: 25,
normalBallCost: 25,
splashBallCost: 150,
sniperBallCost: 500,
scatterBallCost: 2000
};
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 = 0x00ffff;
} else {
ballIcon.tint = 0x666666;
typeText.fill = 0x666666;
costText.fill = 0x666666;
}
};
hud.addChild(button);
ballButtons[type] = button;
}
createBallButton('normal', -450, 25, 'ball', null);
createBallButton('splash', -300, upgrades.splashBallCost, 'splashBall', 'normal');
createBallButton('sniper', -150, upgrades.sniperBallCost, 'sniperBall', 'splash');
createBallButton('scatter', 0, upgrades.scatterBallCost, 'scatterBall', 'sniper');
var clearStorageButton = LK.getAsset('button', {
size: 0,
fill: 0x1a1a2e,
anchorX: 0.5,
anchorY: 0,
x: scoreTxt.width + 12000,
y: 0
});
var clearStorageText = new Text2('', {
size: 0,
fill: 0xffffff
});
clearStorageText.anchor.set(0.5, 0);
clearStorageText.y = 100;
clearStorageButton.addChild(clearStorageText);
clearStorageButton.down = clearLocalStorage;
hud.addChild(clearStorageButton);
var powerupContainer = new Container();
powerupContainer.y = 1800;
powerupContainer.visible = false;
game.addChild(powerupContainer);
var bottomHud = new Container();
bottomHud.y = GAME_HEIGHT - 200;
game.addChild(bottomHud);
var upgradeButtons = {};
function createUpgradeButton(labelPrefix, x, costKey, upgradeKey, baseCost, iconType, prevTier) {
var button = new Container();
var buttonGraphics = button.attachAsset('powerupbutton', {
anchorX: 0.5,
anchorY: 0,
tint: 0x1a1a2e
});
button.x = x + 60;
button.y = 350;
button.addChild(buttonGraphics);
var contentContainer = new Container();
button.addChild(contentContainer);
var icon = null;
if (iconType) {
icon = button.attachAsset('ball', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5,
y: 40,
tint: iconType === 'splashBall' ? 0xff0066 : iconType === 'sniperBall' ? 0x00ff99 : iconType === 'scatterBall' ? 0xffff00 : iconType === 'ball' ? 0xffffff : 0x00ffff
});
contentContainer.addChild(icon);
}
var labelText = new Text2("".concat(labelPrefix.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', 25, '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', 25, null, null);
var upgradeButton = new UpgradeButton();
upgradeButton.x = GAME_WIDTH / 2;
upgradeButton.y = GAME_HEIGHT - 100;
upgradeButton.visible = false;
game.addChild(upgradeButton);
game.setChildIndex(upgradeButton, game.children.length - 1);
hud.visible = false;
function updateButtonStates() {
for (var type in ballButtons) {
ballButtons[type].updateState();
}
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);
}
});
}
function scatterOnImpact(ball) {
for (var i = 0; i < 4; i++) {
var smallBall = new Ball('smallScatter');
smallBall.x = ball.x;
smallBall.y = ball.y;
var angle = i / 4 * 2 * Math.PI;
smallBall.velocity.x = Math.cos(angle);
smallBall.velocity.y = Math.sin(angle);
balls.push(smallBall);
game.addChild(smallBall);
}
}
function findNearestBrick(x, y) {
if (bricks.length === 0) {
return null;
}
return bricks.reduce(function (closest, brick) {
var dx = brick.x - x;
var dy = brick.y - y;
var distance = Math.sqrt(dx * dx + dy * dy);
return !closest || distance < closest.distance ? {
brick: brick,
distance: distance
} : closest;
}, null).brick;
}
function 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);
// Create thick vertical section (5 columns wide)
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) {
// Stay within bounds
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++;
}
}
}
// Create thick horizontal section (5 rows wide) with extended sides
for (var j = 0; j < cols && brickCount < totalBricks; j++) {
if (Math.abs(j - centerCol) > 2) {
// Skip the vertical section to avoid duplicates
for (var rowOffset = -2; rowOffset <= 2 && brickCount < totalBricks; rowOffset++) {
var row = centerRow + rowOffset;
if (row >= 0 && row < rows) {
// Stay within bounds
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++;
}
}
}
}
// Add extra side extensions to horizontal arms
for (var j = 0; j < cols && brickCount < totalBricks; j++) {
if (Math.abs(j - centerCol) > 3) {
// Extend beyond the main horizontal section
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++;
}
}
}
}
}
brickGridBounds = {
minX: Math.min.apply(Math, _toConsumableArray(bricks.map(function (b) {
return b.x - BRICK_WIDTH / 2;
}))),
maxX: Math.max.apply(Math, _toConsumableArray(bricks.map(function (b) {
return b.x + BRICK_WIDTH / 2;
}))),
minY: Math.min.apply(Math, _toConsumableArray(bricks.map(function (b) {
return b.y - BRICK_HEIGHT / 2;
}))),
maxY: Math.max.apply(Math, _toConsumableArray(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 createBall() {
var type = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'normal';
var ball = new Ball(type);
var gridBottom = brickGridBounds ? brickGridBounds.maxY + 150 : GAME_HEIGHT * 0.7;
var spawnFromTop = balls.length % 2 === 0;
ball.x = GAME_WIDTH / 2 + (Math.random() * 200 - 100);
ball.y = spawnFromTop ? BALL_RADIUS : GAME_HEIGHT - BALL_RADIUS;
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;
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) {
if (level === Object.keys(levelConfig).length) {
showEndScreen();
} else {
level += 1;
storage.level = level;
levelTxt.setText('Level: ' + level);
createBricks();
balls.forEach(function (ball, index) {
ball.x = GAME_WIDTH / 2 + (Math.random() * 200 - 100);
ball.y = index % 2 === 0 ? BALL_RADIUS : GAME_HEIGHT - BALL_RADIUS;
var angle = (Math.random() * 0.5 + 0.25) * Math.PI;
ball.velocity.x = Math.cos(angle);
ball.velocity.y = index % 2 === 0 ? 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;
});
}
}
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 - 600;
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: GAME_HEIGHT / 2 - 500
}, {
duration: 1000,
easing: tween.bounceOut
});
var startButton = new StartButton();
startButton.x = GAME_WIDTH / 2;
startButton.y = GAME_HEIGHT / 2 + 300;
startButton.tint = 0x00ff99;
game.addChild(startButton);
tween(startButton, {
y: startButton.y - 20
}, {
duration: 1000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(startButton, {
y: startButton.y + 20
}, {
duration: 1000,
easing: tween.easeInOut
});
}
});
var resetButton = new ResetButton();
resetButton.x = GAME_WIDTH / 2;
resetButton.y = startButton.y + 900;
game.addChild(resetButton);
tween(resetButton, {
y: resetButton.y - 20
}, {
duration: 1000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(resetButton, {
y: resetButton.y + 20
}, {
duration: 1000,
easing: tween.easeInOut
});
}
});
LK.playMusic('backgroundmusic');
function startGame() {
startButton.destroy();
gameTitle.destroy();
resetButton.visible = false;
upgradeButton.visible = true;
hud.visible = true;
createBricks();
createBall('normal');
if (score > 0) {
scoreTxt.setText('$' + score.toString());
}
if (storage.firstLoad !== true) {
welcomeText = new Text2('Welcome! Click to cash in and power up!', {
size: 90,
fill: 0xffffff
});
welcomeText.anchor.set(0.5, 0);
welcomeText.x = GAME_WIDTH / 2;
welcomeText.y = GAME_HEIGHT / 2 + 650;
game.addChild(welcomeText);
storage.firstLoad = true;
}
playTimeInterval = LK.setInterval(function () {
playTime += 1;
console.log('Time Played:', playTime, 'seconds');
}, 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) {
if (level === Object.keys(levelConfig).length) {
showEndScreen();
} else {
level += 1;
storage.level = level;
levelTxt.setText('Level: ' + level);
createBricks();
}
}
updateButtonStates();
};
}
game.update = function () {
for (var i = 0; i < stars.length; i++) {
stars[i].update();
}
};