User prompt
move click x2 a little up
User prompt
actually click should say +1 not x1
User prompt
click should be +1 and the updated to +2 and so on
User prompt
on game start there has to be one ball already
User prompt
on game start load current points, and if it is not empty, use it to replace the $0 placehodler
Code edit (3 edits merged)
Please save this source code
User prompt
in new levelballs will randomly spawn from the top or the bottom of the screen but not in a position where there is a brick
User prompt
can we have the grid centered in the y axis when loaded
User prompt
when balls are spawned either from the top or the bottom, make sure it ese even, so if there are 4 balls, two top and 2 bottom.
User prompt
close upgrades when player click on a brick
User prompt
upgrade button should be greyed out if player don't have enough money to buy upgrade
User prompt
Rename upgrade button for UPGRADES!
Code edit (1 edits merged)
Please save this source code
User prompt
move welcome message 50 pixels down
Code edit (1 edits merged)
Please save this source code
User prompt
upgrade button shoudl be greyed out if none of the powerups can be purachesd
User prompt
first notmal extra ball should cost 25
User prompt
update price of first ball to 25 in button
Code edit (2 edits merged)
Please save this source code
User prompt
normal ball should always double its prize
User prompt
when upgrades button is disabled also close powerups
User prompt
can we crate a frame arroudn the whole screen? it will be used to give the feel ing that balls are bouncing there
User prompt
frame shoudl use its own asset frame
User prompt
Frame is only covering the top left of the screen
User prompt
can we move thhe left and bottom frames closer to the center of the screen
/****
* Plugins
****/
var storage = LK.import("@upit/storage.v1");
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Ball = Container.expand(function () {
var self = Container.call(this);
var type = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'normal';
var ballGraphics = self.attachAsset('ball', {
anchorX: 0.5,
anchorY: 0.5
});
ballGraphics.tint = type === 'splash' ? 0xff0066 : type === 'sniper' ? 0x00ff99 : type === 'scatter' ? 0xffff00 : type === 'smallScatter' ? 0xffff00 : 0xffffff;
self.type = type;
self.speed = type === 'splash' ? upgrades.splashSpeed : type === 'sniper' ? upgrades.sniperSpeed : type === 'scatter' ? upgrades.scatterSpeed : type === 'smallScatter' ? upgrades.scatterSpeed * 0.8 : upgrades.normalSpeed;
self.power = type === 'splash' ? upgrades.splashPower : type === 'sniper' ? upgrades.sniperPower : type === 'scatter' ? upgrades.scatterPower : type === 'smallScatter' ? Math.max(1, Math.floor(upgrades.scatterPower * 0.5)) : upgrades.normalPower;
self.direction = {
x: 1,
y: -1
};
self.sniperCooldown = 0; // Add cooldown tracker
self.sniperCooldownMax = 60; // Lock onto a new target every 1 second (assuming 60 FPS)
self.interactive = true;
self.down = function (x, y, obj) {
// Calculate new random direction on click
var angle = Math.random() * 2 * Math.PI;
self.direction.x = Math.cos(angle);
self.direction.y = Math.sin(angle);
// Normalize direction
var magnitude = Math.sqrt(self.direction.x * self.direction.x + self.direction.y * self.direction.y);
self.direction.x /= magnitude;
self.direction.y /= magnitude;
};
self.update = function () {
var gridSize = levelConfig[level] ? levelConfig[level].gridSize : 200;
var stepSize = self.speed;
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.direction.x = dx / magnitude;
self.direction.y = dy / magnitude;
self.sniperCooldown = self.sniperCooldownMax; // Reset cooldown
}
}
}
var dx = self.direction.x * stepSize;
var dy = self.direction.y * stepSize;
self.x += dx;
self.y += dy;
if (self.x <= BALL_RADIUS || self.x >= GAME_WIDTH - BALL_RADIUS) {
self.direction.x *= -1;
self.x = Math.max(BALL_RADIUS, Math.min(GAME_WIDTH - BALL_RADIUS, self.x));
self.direction.y += (Math.random() - 0.5) * 0.1; // Add slight randomness to y direction
LK.getSound('bounce').play();
}
if (self.y <= BALL_RADIUS || self.y >= GAME_HEIGHT - BALL_RADIUS) {
self.direction.y *= -1;
self.y = Math.max(BALL_RADIUS, Math.min(GAME_HEIGHT - BALL_RADIUS, self.y));
self.direction.x += (Math.random() - 0.5) * 0.1; // Add slight randomness to x direction
LK.getSound('bounce').play();
}
if (!isNearBricks(self.x, self.y)) {
return;
}
var gridXMin = Math.floor((self.x - BALL_RADIUS) / gridSize);
var gridXMax = Math.floor((self.x + BALL_RADIUS) / gridSize);
var gridYMin = Math.floor((self.y - BALL_RADIUS) / gridSize);
var gridYMax = Math.floor((self.y + BALL_RADIUS) / gridSize);
var hasCollided = false;
for (var gx = gridXMin; gx <= gridXMax && !hasCollided; gx++) {
for (var gy = gridYMin; gy <= gridYMax && !hasCollided; gy++) {
var gridKey = "".concat(gx, ",").concat(gy);
var cellBricks = brickGrid[gridKey];
if (!cellBricks || cellBricks.length === 0) {
continue;
}
for (var j = cellBricks.length - 1; j >= 0; j--) {
var brick = cellBricks[j];
if (!brick || brick.health <= 0 || !self.intersects(brick)) {
continue;
}
handleBallBrickCollision(self, brick);
brick.hit(self.power);
if (self.type === 'splash' && brick.health > 0) {
applySplashDamage(brick, gridSize);
} else if (self.type === 'scatter') {
scatterOnImpact(self);
self.destroy();
balls.splice(balls.indexOf(self), 1);
hasCollided = true;
break;
}
if (brick.health <= 0) {
cellBricks.splice(j, 1);
}
hasCollided = true;
break;
}
}
}
};
});
var Brick = Container.expand(function () {
var self = Container.call(this);
var brickGraphics = self.attachAsset('brick', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 1;
self.maxHealth = 1;
self.healthText = new Text2(self.health.toString(), {
size: 50,
fill: 0x000000,
fontWeight: 'bold'
});
self.healthText.anchor.set(0.5, 0.5);
self.addChild(self.healthText);
// Helper function to convert hex color to RGB
function hexToRGB(hex) {
return {
r: hex >> 16 & 0xff,
g: hex >> 8 & 0xff,
b: hex & 0xff
};
}
// Helper function to convert RGB back to hex
function rgbToHex(r, g, b) {
return (Math.floor(r) << 16) + (Math.floor(g) << 8) + Math.floor(b);
}
// Update brick tint based on health
self.updateTint = function () {
var baseColors = LEVEL_COLORS;
var colorCount = baseColors.length; // 10 colors
if (self.health <= colorCount) {
// For HP 1-10, use the direct color from LEVEL_COLORS
brickGraphics.tint = baseColors[self.health - 1];
} else {
// For HP > 10, use the last digit to determine the color
var lastDigit = self.health % 10; // Get the last digit (0-9)
// Map last digit 0 to index 9 (HP 10), 1 to index 0 (HP 1), etc.
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(); // Show game over when reset is pressed
};
});
var Star = Container.expand(function () {
var self = Container.call(this);
var starGraphics = self.attachAsset('star', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = Math.random() * 2 + 1;
self.update = function () {
self.y += self.speed;
if (self.y > GAME_HEIGHT) {
self.y = 0;
self.x = Math.random() * GAME_WIDTH;
}
};
});
var StartButton = Container.expand(function () {
var self = Container.call(this);
var buttonGraphics = self.attachAsset('startButton', {
anchorX: 0.5,
anchorY: 0.5
});
var buttonText = new Text2('START', {
size: 80,
fill: 0x000000,
fontWeight: 'bold'
});
buttonText.anchor.set(0.5, 0.5);
self.addChild(buttonText);
self.interactive = true;
self.down = function () {
LK.getSound('click').play();
startGame();
};
});
var UpgradeButton = Container.expand(function () {
var self = Container.call(this);
var buttonGraphics = self.attachAsset('upgrade', {
anchorX: 0.5,
anchorY: 0.5,
tint: 0x00ffff
});
var buttonText = new Text2('UPGRADE', {
size: 50,
fill: 0x000000,
fontWeight: 'bold'
});
buttonText.anchor.set(0.5, 0.5);
self.addChild(buttonText);
self.interactive = true;
self.down = function () {
LK.getSound('click').play();
powerupContainer.visible = !powerupContainer.visible;
};
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a1a2e
});
/****
* Game Code
****/
var welcomeText = null;
function showEndScreen() {
game.isGameOver = true; // Set the flag to indicate the end screen is up
// Hide all game elements
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);
// Add game title with color switching
var gameTitle = new GameTitle();
gameTitle.x = GAME_WIDTH / 2;
gameTitle.y = congratsText.y - 400;
game.addChild(gameTitle);
function animateEndTitleColor() {
tween(gameTitle, {
tint: 0xff33cc
}, {
duration: 2000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(gameTitle, {
tint: 0x00ff99
}, {
duration: 2000,
easing: tween.easeInOut,
onFinish: animateEndTitleColor
});
}
});
}
animateEndTitleColor();
// Add play time text
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);
// Add a note below the time played
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);
// Add end game button
var endGameButton = new Container();
var buttonGraphics = endGameButton.attachAsset('endGameButton', {
anchorX: 0.5,
anchorY: 0.5,
tint: 0x00ffff // Match the upgrade button tint
});
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 - 200;
endGameButton.interactive = true;
endGameButton.down = function () {
LK.showGameOver();
};
game.addChild(endGameButton);
// Add reset button
var resetButton = new ResetButton();
resetButton.x = GAME_WIDTH / 2;
resetButton.y = endGameButton.y + 200;
game.addChild(resetButton);
// Debugging: Log to ensure animations are triggered
LK.clearInterval(playTimeInterval);
playTimeInterval = null; // Reset the interval variable to null
playTimeInterval = null; // Reset the interval variable to null
}
function _toConsumableArray2(r) {
return _arrayWithoutHoles2(r) || _iterableToArray2(r) || _unsupportedIterableToArray2(r) || _nonIterableSpread2();
}
function _nonIterableSpread2() {
throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
function _unsupportedIterableToArray2(r, a) {
if (r) {
if ("string" == typeof r) {
return _arrayLikeToArray2(r, a);
}
var t = {}.toString.call(r).slice(8, -1);
return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray2(r, a) : void 0;
}
}
function _iterableToArray2(r) {
if ("undefined" != typeof Symbol && null != r[Symbol.iterator] || null != r["@@iterator"]) {
return Array.from(r);
}
}
function _arrayWithoutHoles2(r) {
if (Array.isArray(r)) {
return _arrayLikeToArray2(r);
}
}
function _arrayLikeToArray2(r, a) {
(null == a || a > r.length) && (a = r.length);
for (var e = 0, n = Array(a); e < a; e++) {
n[e] = r[e];
}
return n;
}
function _toConsumableArray(r) {
return _arrayWithoutHoles(r) || _iterableToArray(r) || _unsupportedIterableToArray(r) || _nonIterableSpread();
}
function _nonIterableSpread() {
throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
function _unsupportedIterableToArray(r, a) {
if (r) {
if ("string" == typeof r) {
return _arrayLikeToArray(r, a);
}
var t = {}.toString.call(r).slice(8, -1);
return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0;
}
}
function _iterableToArray(r) {
if ("undefined" != typeof Symbol && null != r[Symbol.iterator] || null != r["@@iterator"]) {
return Array.from(r);
}
}
function _arrayWithoutHoles(r) {
if (Array.isArray(r)) {
return _arrayLikeToArray(r);
}
}
function _arrayLikeToArray(r, a) {
(null == a || a > r.length) && (a = r.length);
for (var e = 0, n = Array(a); e < a; e++) {
n[e] = r[e];
}
return n;
}
var GAME_WIDTH = 2048;
var GAME_HEIGHT = 2632;
var BALL_RADIUS = 50;
var BRICK_WIDTH = 300;
var BRICK_HEIGHT = 99;
var LEVEL_COLORS = [0xff00ff, 0x00ffff, 0xffff00, 0xff0066, 0x00ff99, 0xff33cc, 0x66ff33, 0xcc00ff, 0x33ffcc, 0xff3300];
var levelConfig = {
1: {
totalBricks: 70,
hitpoints: 1,
gridSize: 400,
pattern: 'grid'
},
2: {
totalBricks: 70,
hitpoints: 3,
gridSize: 400,
pattern: 'grid'
},
3: {
totalBricks: 84,
hitpoints: 6,
gridSize: 320,
pattern: 'staggered'
},
4: {
totalBricks: 90,
hitpoints: 10,
gridSize: 500,
pattern: 'clustered'
},
5: {
totalBricks: 96,
hitpoints: 12,
gridSize: 240,
pattern: 'grid'
},
6: {
totalBricks: 112,
hitpoints: 15,
gridSize: 220,
pattern: 'clustered'
},
7: {
totalBricks: 112,
hitpoints: 20,
gridSize: 200,
pattern: 'grid'
},
8: {
totalBricks: 112,
hitpoints: 99,
gridSize: 180,
pattern: 'clustered'
},
9: {
totalBricks: 112,
hitpoints: 997,
gridSize: 160,
pattern: 'grid'
},
10: {
totalBricks: 112,
hitpoints: 9998,
gridSize: 140,
pattern: 'clustered'
}
};
var upgrades = storage.upgrades || {
normalSpeed: 1,
normalPower: 1,
splashSpeed: 1,
splashPower: 1,
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: 50,
splashBallCost: 100,
sniperBallCost: 500,
scatterBallCost: 2000
};
var balls = [];
var ballQuantities = storage.ballQuantities || {
normal: 0,
splash: 0,
sniper: 0,
scatter: 0
};
var bricks = [];
var brickGrid = {};
var score = storage.score || 0;
var level = storage.level || 1;
var brickGridBounds = null;
var unlockedTiers = storage.unlockedTiers || {
normal: true,
splash: false,
sniper: false,
scatter: false
};
var playTime = storage.playTime || 0;
playTimeInterval = LK.setInterval(function () {
if (!game.isGameOver) {
// Check if the game is not over
playTime += 1;
storage.playTime = playTime;
console.log('Time Played2:', playTime, 'seconds');
}
}, 1000);
var playTimeInterval = null;
game.isGameOver = false; // Initialize the flag to indicate the end screen is not up
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: 1,
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: 50,
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();
}
// HUD Setup
var hud = new Container();
LK.gui.top.addChild(hud);
var scoreTxt = new Text2('$0', {
size: 60,
fill: 0x39ff14 // Neon green color
});
scoreTxt.anchor.set(0.3, 0);
scoreTxt.x += 450;
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
});
// Set initial tint
ballIcon.tint = type === 'splash' ? 0xff0066 : type === 'sniper' ? 0x00ff99 : type === 'scatter' ? 0xffff00 : 0xffffff; // Normal ball
contentContainer.addChild(ballIcon);
var displayType = type === 'scatter' ? 'Multi' : type.charAt(0).toUpperCase() + type.slice(1);
var typeText = new Text2(displayType, {
size: 30,
fill: 0xffffff,
// Initial white color
fontWeight: 'bold'
});
typeText.anchor.set(0.5, 0);
typeText.y = -50;
button.addChild(typeText);
var costText = new Text2('$' + (type === 'sniper' ? 500 : type === 'splash' ? 150 : type === 'scatter' ? 2000 : upgrades[type + 'BallCost']), {
size: 40,
fill: 0xffffff // Change to white color
});
costText.anchor.set(0.5, 0);
costText.y = 100;
button.addChild(costText);
button.interactive = true;
button.down = function () {
if (score < upgrades[type + 'BallCost']) {
return;
}
// Remove welcome message if it exists
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'] * 1.3);
storage.upgrades = Object.assign({}, upgrades);
costText.setText('$' + upgrades[type + 'BallCost']);
if (!unlockedTiers[type]) {
unlockedTiers[type] = true;
storage.unlockedTiers = Object.assign({}, unlockedTiers);
updateButtonStates();
}
};
button.updateState = function () {
var isEnabled = (prevTier ? unlockedTiers[prevTier] : true) && score >= upgrades[type + 'BallCost'];
buttonGraphics.tint = isEnabled ? 0x00ffff : 0x666666; // Button background tint
button.interactive = isEnabled;
// Update ballIcon tint based on enabled state
if (isEnabled) {
ballIcon.tint = type === 'splash' ? 0xff0066 : type === 'sniper' ? 0x00ff99 : type === 'scatter' ? 0xffff00 : 0xffffff; // Restore original ball color
typeText.fill = 0xffffff; // White for ball name
costText.fill = 0x00ffff; // Cyan for price
} else {
ballIcon.tint = 0x666666; // Grey out ball icon
typeText.fill = 0x666666; // Grey out ball name
costText.fill = 0x666666; // Grey out price
}
};
hud.addChild(button);
ballButtons[type] = button;
}
createBallButton('normal', -450, upgrades.normalBallCost, 'ball', null);
createBallButton('splash', -300, upgrades.splashBallCost, 'splashBall', 'normal');
createBallButton('sniper', -150, upgrades.sniperBallCost, 'sniperBall', 'splash');
createBallButton('scatter', 0, upgrades.scatterBallCost, 'scatterBall', 'sniper');
var clearStorageButton = LK.getAsset('button', {
size: 0,
fill: 0x1a1a2e,
anchorX: 0.5,
anchorY: 0,
x: scoreTxt.width + 12000,
y: 0
});
var clearStorageText = new Text2('', {
size: 0,
fill: 0xffffff
});
clearStorageText.anchor.set(0.5, 0);
clearStorageText.y = 100;
clearStorageButton.addChild(clearStorageText);
clearStorageButton.down = clearLocalStorage;
hud.addChild(clearStorageButton);
var powerupContainer = new Container();
powerupContainer.y = 1800;
powerupContainer.visible = false;
game.addChild(powerupContainer);
var bottomHud = new Container();
bottomHud.y = GAME_HEIGHT - 200;
game.addChild(bottomHud);
var upgradeButtons = {};
function createUpgradeButton(labelPrefix, x, costKey, upgradeKey, baseCost, iconType, prevTier) {
var button = new Container();
var buttonGraphics = button.attachAsset('powerupbutton', {
anchorX: 0.5,
anchorY: 0,
tint: 0x1a1a2e
});
button.x = x + 60;
button.y = 350;
button.addChild(buttonGraphics);
var contentContainer = new Container();
button.addChild(contentContainer);
var icon = null;
if (iconType) {
icon = button.attachAsset('ball', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5,
y: 40,
tint: iconType === 'splashBall' ? 0xff0066 : iconType === 'sniperBall' ? 0x00ff99 : iconType === 'scatterBall' ? 0xffff00 : iconType === 'ball' ? 0xffffff : 0x00ffff // Fallback
});
contentContainer.addChild(icon);
}
var labelText = new Text2("".concat(labelPrefix, " x").concat(upgrades[upgradeKey]), {
size: upgradeKey === 'clickDamage' ? 50 : 30,
// Increase size for Click x button text
fill: 0x000000,
fontWeight: 'bold'
});
labelText.anchor.set(0.5, 0);
labelText.y = 80;
contentContainer.addChild(labelText);
var costText = new Text2('$' + (baseCost * upgrades[upgradeKey]).toString(), {
size: 40,
fill: 0x000000
});
costText.anchor.set(0.5, 0);
costText.y = 140;
contentContainer.addChild(costText);
button.interactive = true;
button.down = function () {
// Remove welcome message if it exists
if (welcomeText && welcomeText.parent) {
welcomeText.parent.removeChild(welcomeText);
}
var cost = baseCost * upgrades[upgradeKey];
var ballType = upgradeKey.split('Speed')[0].split('Power')[0];
if (upgradeKey === 'clickDamage') {
if (score < cost) {
return;
}
} else {
if (!unlockedTiers[ballType] || score < cost) {
return;
}
}
score -= cost;
LK.getSound('click').play();
upgrades[upgradeKey]++;
storage.upgrades = Object.assign({}, upgrades);
costText.setText('$' + (baseCost * upgrades[upgradeKey]).toString());
labelText.setText("".concat(labelPrefix, " x").concat(upgrades[upgradeKey]));
scoreTxt.setText('$' + score.toString());
balls.forEach(function (b) {
if (b.type === 'normal' && upgradeKey.includes('normal')) {
b[upgradeKey.includes('Speed') ? 'speed' : 'power'] = upgrades[upgradeKey];
} else if (b.type === 'splash' && upgradeKey.includes('splash')) {
b[upgradeKey.includes('Speed') ? 'speed' : 'power'] = upgrades[upgradeKey];
} else if (b.type === 'sniper' && upgradeKey.includes('sniper')) {
b[upgradeKey.includes('Speed') ? 'speed' : 'power'] = upgrades[upgradeKey];
} else if ((b.type === 'scatter' || b.type === 'smallScatter') && upgradeKey.includes('scatter')) {
b[upgradeKey.includes('Speed') ? 'speed' : 'power'] = upgrades[upgradeKey];
}
});
if (upgradeKey === 'clickDamage') {
upgrades.clickDamage = upgrades[upgradeKey];
storage.upgrades = Object.assign({}, upgrades);
}
};
button.updateState = function () {
var ballType = upgradeKey.split('Speed')[0].split('Power')[0];
var isEnabled = upgradeKey === 'clickDamage' ? score >= baseCost * upgrades[upgradeKey] : unlockedTiers[ballType] && (prevTier ? unlockedTiers[prevTier] : true) && score >= baseCost * upgrades[upgradeKey];
buttonGraphics.tint = isEnabled ? 0x00ffff : 0x666666; // Button background tint
button.interactive = isEnabled;
// Update icon tint based on enabled state
if (icon) {
if (isEnabled) {
// Restore original ball color when enabled
icon.tint = iconType === 'splashBall' ? 0xff0066 : iconType === 'sniperBall' ? 0x00ff99 : iconType === 'scatterBall' ? 0xffff00 : iconType === 'ball' ? 0xffffff : 0x00ffff;
} else {
// Grey out when disabled
icon.tint = 0x666666;
}
}
};
powerupContainer.addChild(button);
upgradeButtons[upgradeKey] = button;
}
var buttonWidth = 150;
var spacing = 50;
var totalButtons = 9;
var totalWidth = totalButtons * buttonWidth + (totalButtons - 1) * spacing;
var startX = (GAME_WIDTH - totalWidth) / 2;
createUpgradeButton('Speed', startX, 'normalSpeedCost', 'normalSpeed', 50, 'ball', null);
createUpgradeButton('Power', startX + (buttonWidth + spacing), 'normalPowerCost', 'normalPower', 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();
}
for (var key in upgradeButtons) {
upgradeButtons[key].updateState();
}
}
function handleBallBrickCollision(ball, brick) {
var relativeX = (ball.x - brick.x) / (brick.width / 2);
var relativeY = (ball.y - brick.y) / (brick.height / 2);
if (Math.abs(relativeX) > Math.abs(relativeY)) {
ball.direction.x = -ball.direction.x + relativeX * 0.5;
ball.x = brick.x + (relativeX > 0 ? brick.width / 2 + BALL_RADIUS : -brick.width / 2 - BALL_RADIUS);
} else {
ball.direction.y = -ball.direction.y;
ball.y = brick.y + (relativeY > 0 ? brick.height / 2 + BALL_RADIUS : -brick.height / 2 - BALL_RADIUS);
}
// Ensure ball is fully outside the brick
ball.x = Math.max(BALL_RADIUS, Math.min(GAME_WIDTH - BALL_RADIUS, ball.x));
ball.y = Math.max(BALL_RADIUS, Math.min(GAME_HEIGHT - BALL_RADIUS, ball.y));
// Ensure ball does not overlap with brick
ball.x = Math.max(BALL_RADIUS, Math.min(GAME_WIDTH - BALL_RADIUS, ball.x));
ball.y = Math.max(BALL_RADIUS, Math.min(GAME_HEIGHT - BALL_RADIUS, ball.y));
LK.getSound('bounce').play();
var angleAdjustment = 0.1; // Base angle adjustment
var trajectoryAngle = Math.abs(Math.atan2(ball.direction.y, ball.direction.x));
if (trajectoryAngle < 0.1 || trajectoryAngle > Math.PI - 0.1) {
angleAdjustment = 0.3; // Increase angle adjustment for nearly straight trajectories
}
ball.direction.x += (Math.random() - 0.5) * angleAdjustment;
var magnitude = Math.sqrt(ball.direction.x * ball.direction.x + ball.direction.y * ball.direction.y);
ball.direction.x /= magnitude;
ball.direction.y /= magnitude;
}
function applySplashDamage(brick, gridSize) {
var gridX = Math.floor(brick.x / gridSize);
var gridY = Math.floor(brick.y / gridSize);
for (var key in brickGrid) {
brickGrid[key].forEach(function (adjBrick) {
if (adjBrick && adjBrick.health > 0) {
var dx = adjBrick.x - brick.x;
var dy = adjBrick.y - brick.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= 200) {
adjBrick.hit(brick.health > 0 ? brick.health : upgrades.splashPower);
}
}
});
}
}
function scatterOnImpact(ball) {
for (var i = 0; i < 4; i++) {
var smallBall = new Ball('smallScatter');
smallBall.x = ball.x;
smallBall.y = ball.y;
var angle = i / 4 * 2 * Math.PI;
smallBall.direction.x = Math.cos(angle);
smallBall.direction.y = Math.sin(angle);
balls.push(smallBall);
game.addChild(smallBall);
}
}
function findNearestBrick(x, y) {
if (bricks.length === 0) {
return null;
}
return bricks.reduce(function (closest, brick) {
var dx = brick.x - x;
var dy = brick.y - y;
var distance = Math.sqrt(dx * dx + dy * dy);
return !closest || distance < closest.distance ? {
brick: brick,
distance: distance
} : closest;
}, null).brick;
}
function isNearBricks(x, y) {
if (!brickGridBounds) {
return true;
}
var buffer = BALL_RADIUS * 2;
return x >= brickGridBounds.minX - buffer && x <= brickGridBounds.maxX + buffer && y >= brickGridBounds.minY - buffer && y <= brickGridBounds.maxY + buffer;
}
function createBricks() {
var config = levelConfig[level] || {};
var totalBricks = config.totalBricks || 50;
var baseHitpoints = config.hitpoints || 1; // Base HP from level config
var gridSize = config.gridSize || 200;
var pattern = config.pattern || 'grid';
var spacingX = 1; // Reduced spacing between columns
var spacingY = 2;
brickGrid = {};
bricks = [];
var cols = 7; // Set to 7 columns
var rows = Math.ceil(totalBricks / cols);
var totalWidth = cols * BRICK_WIDTH + (cols - 1) * spacingX;
var totalHeight = rows * BRICK_HEIGHT + (rows - 1) * spacingY;
var startX = (GAME_WIDTH - totalWidth) / 2 + BRICK_WIDTH / 2;
var startY = (GAME_HEIGHT - totalHeight) / 3 + BRICK_HEIGHT / 2;
var brickCount = 0;
if (pattern === 'grid') {
for (var i = 0; i < rows && brickCount < totalBricks; i++) {
for (var j = 0; j < cols && brickCount < totalBricks; j++) {
addBrick(startX + j * (BRICK_WIDTH + spacingX), startY + i * (BRICK_HEIGHT + spacingY), baseHitpoints, gridSize);
brickCount++;
}
}
} else if (pattern === 'staggered') {
// Staggered pattern with HP gradient from center
var centerRow = Math.floor(rows / 2); // Approximate center row
var centerCol = Math.floor(cols / 2); // Approximate center column
for (var i = 0; i < rows && brickCount < totalBricks; i++) {
var offsetX = 0; // No offset for staggered grid
for (var j = 0; j < cols && brickCount < totalBricks; j++) {
var x = startX + offsetX + j * (BRICK_WIDTH + spacingX);
var y = startY + i * (BRICK_HEIGHT + spacingY);
// Calculate distance from center (Manhattan distance for simplicity)
var rowDistance = Math.abs(i - centerRow);
var colDistance = Math.abs(j - centerCol);
var maxDistance = Math.max(centerRow, centerCol); // Max possible distance to edge
var distance = Math.max(rowDistance, colDistance);
// HP decreases linearly from center to edge
// Center gets baseHitpoints, edges get at least 1 HP
var hitpoints = Math.max(1, Math.round(baseHitpoints * (1 - distance / maxDistance)));
addBrick(x, y, hitpoints, gridSize);
brickCount++;
}
}
} else if (pattern === 'clustered') {
// Clustered pattern: fill from outside in
var centerRow = Math.floor(rows / 2);
var centerCol = Math.floor(cols / 2);
var maxDistance = Math.max(centerRow, centerCol);
// Create a list of all possible grid positions with their distances
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
});
}
}
// Sort positions by distance (descending) to fill from outside in
positions.sort(function (a, b) {
return b.distance - a.distance;
});
// Place bricks up to totalBricks limit
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);
// Optional: HP can still vary if desired, higher on edges here
var hitpoints = Math.max(1, Math.round(baseHitpoints * (distance / maxDistance)));
addBrick(x, y, hitpoints, gridSize);
brickCount++;
}
} else if (pattern === 'diagonal') {
// [Unchanged diagonal pattern code]
var stepX = (GAME_WIDTH - BRICK_WIDTH) / (totalBricks - 1);
var stepY = GAME_HEIGHT / 3 / (totalBricks - 1);
for (var i = 0; i < totalBricks; i++) {
var offsetX = i % 2 === 0 ? BRICK_WIDTH / 4 : 0;
addBrick(startX + i * stepX + offsetX, startY + i * stepY, baseHitpoints, gridSize);
brickCount++;
}
} else if (pattern === 'sparse') {
// Sparse pattern: groups of 3 rows with 2-row gaps
var groupSize = 3; // 3 rows per group
var gapSize = 2; // 2 rows gap
var cycleLength = groupSize + gapSize; // Total rows in one cycle (3 + 2 = 5)
while (brickCount < totalBricks) {
// Pick a random column
var col = Math.floor(Math.random() * cols);
var x = startX + col * (BRICK_WIDTH + spacingX);
// Pick a random group start row, ensuring space for 3 rows
var maxGroupStart = rows - groupSize; // Leave room for 3 rows
var groupStart = Math.floor(Math.random() * Math.floor(maxGroupStart / cycleLength)) * cycleLength;
// Place bricks in the 3 rows of the group
for (var rowOffset = 0; rowOffset < groupSize && brickCount < totalBricks; rowOffset++) {
var row = groupStart + rowOffset;
if (row >= rows) {
continue;
} // Skip if beyond grid bounds
var y = startY + row * (BRICK_HEIGHT + spacingY);
// Check for collision to maintain sparsity
if (!bricks.some(function (b) {
return b.x === x && b.y === y;
})) {
addBrick(x, y, baseHitpoints, gridSize);
brickCount++;
}
}
}
}
brickGridBounds = {
minX: Math.min.apply(Math, _toConsumableArray2(bricks.map(function (b) {
return b.x - BRICK_WIDTH / 2;
}))),
maxX: Math.max.apply(Math, _toConsumableArray2(bricks.map(function (b) {
return b.x + BRICK_WIDTH / 2;
}))),
minY: Math.min.apply(Math, _toConsumableArray2(bricks.map(function (b) {
return b.y - BRICK_HEIGHT / 2;
}))),
maxY: Math.max.apply(Math, _toConsumableArray2(bricks.map(function (b) {
return b.y + BRICK_HEIGHT / 2;
})))
};
}
function addBrick(x, y, hitpoints, gridSize) {
var brick = new Brick();
brick.x = x;
brick.y = y;
brick.health = hitpoints;
brick.maxHealth = hitpoints;
brick.healthText.setText(brick.health.toString());
brick.updateTint();
bricks.push(brick);
game.addChild(brick);
var gridX = Math.floor(brick.x / gridSize);
var gridY = Math.floor(brick.y / gridSize);
var gridKey = "".concat(gridX, ",").concat(gridY);
if (!brickGrid[gridKey]) {
brickGrid[gridKey] = [];
}
brickGrid[gridKey].push(brick);
}
function createBall() {
var type = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'normal';
var ball = new Ball(type);
var gridBottom = brickGridBounds ? brickGridBounds.maxY + 150 : GAME_HEIGHT * 0.7;
ball.x = GAME_WIDTH / 2 + (Math.random() * 200 - 100);
ball.y = Math.min(gridBottom + 100, GAME_HEIGHT - BALL_RADIUS * 2);
var angle = (Math.random() * 0.5 + 0.25) * Math.PI;
ball.direction.x = Math.cos(angle);
ball.direction.y = -Math.sin(angle);
var magnitude = Math.sqrt(ball.direction.x * ball.direction.x + ball.direction.y * ball.direction.y);
ball.direction.x /= magnitude;
ball.direction.y /= magnitude;
for (var i = 0; i < bricks.length; i++) {
if (ball.intersects(bricks[i])) {
ball.y = brickGridBounds.maxY + BALL_RADIUS + 10;
break;
}
}
balls.push(ball);
game.addChild(ball);
}
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();
// Increment playTime by 1 second (assuming update is called 60 times per second)
// Removed playTime increment to ensure it only prints once
};
game.down = function (x, y, obj) {
if (!bricks || !Array.isArray(bricks)) {
return;
}
x = Number(x);
y = Number(y);
for (var i = 0; i < bricks.length; i++) {
var brick = bricks[i];
if (!brick.x || !brick.y || !brick.width || !brick.height) {
continue;
}
if (x >= brick.x - brick.width / 2 && x <= brick.x + brick.width / 2 && y >= brick.y - brick.height / 2 && y <= brick.y + brick.height / 2) {
brick.hit(upgrades.clickDamage);
LK.getSound('click').play();
scoreTxt.setText('$' + score.toString());
storage.score = score;
return;
}
}
};
var stars = [];
for (var i = 0; i < 100; i++) {
var star = new Star();
star.x = Math.random() * GAME_WIDTH;
star.y = Math.random() * GAME_HEIGHT;
stars.push(star);
game.addChildAt(star, 0);
}
var 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();
// Check if it's the first time the game is loaded
if (storage.firstLoad !== true) {
// Display welcome message
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 + 600;
game.addChild(welcomeText);
// Set firstLoad to true to prevent showing the message again
storage.firstLoad = true;
}
playTimeInterval = LK.setInterval(function () {
playTime += 1;
// Ensure playTime is printed once
console.log('Time Played:', playTime, 'seconds');
}, 1000);
// Load balls from storage
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();
}
};