/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
highScore: 0
});
/****
* Classes
****/
var Ball = Container.expand(function () {
var self = Container.call(this);
var ballGraphics = self.attachAsset('ball', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = ballGraphics.width / 2;
self.speedX = 0;
self.speedY = 0;
self.active = false;
self.launch = function (speedX, speedY) {
self.speedX = speedX;
self.speedY = speedY;
self.active = true;
};
self.reset = function (paddleX, paddleY) {
self.x = paddleX;
self.y = paddleY - 30;
self.speedX = 0;
self.speedY = 0;
self.active = false;
};
self.update = function () {
if (!self.active) {
return;
}
// Move ball
self.x += self.speedX;
self.y += self.speedY;
// Handle wall collisions
if (self.x - self.radius <= 0 || self.x + self.radius >= 2048) {
self.speedX *= -1;
LK.getSound('hit').play();
}
if (self.y - self.radius <= 0) {
self.speedY *= -1;
LK.getSound('hit').play();
}
};
return self;
});
var Brick = Container.expand(function (type, row, column) {
var self = Container.call(this);
var assetId = 'brick';
if (type === 'reinforced') {
assetId = 'reinforcedBrick';
} else if (type === 'explosive') {
assetId = 'explosiveBrick';
} else if (type === 'mystery') {
assetId = 'mysteryBrick';
}
var brickGraphics = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5
});
self.type = type || 'normal';
self.width = brickGraphics.width;
self.height = brickGraphics.height;
self.row = row || 0;
self.column = column || 0;
self.health = type === 'reinforced' ? 2 : 1;
self.hit = function () {
self.health--;
if (self.health <= 0) {
return true; // Brick destroyed
} else {
// Show brick is damaged
tween(brickGraphics, {
alpha: 0.7
}, {
duration: 100,
onFinish: function onFinish() {
tween(brickGraphics, {
alpha: 1
}, {
duration: 100
});
}
});
return false; // Brick still alive
}
};
return self;
});
var Paddle = Container.expand(function () {
var self = Container.call(this);
var paddleGraphics = self.attachAsset('paddle', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = paddleGraphics.width;
self.height = paddleGraphics.height;
self.moveTo = function (x) {
// Ensure paddle stays within screen bounds
var minX = self.width / 2;
var maxX = 2048 - self.width / 2;
self.x = Math.max(minX, Math.min(maxX, x));
};
return self;
});
var PowerUp = Container.expand(function (type) {
var self = Container.call(this);
var powerupGraphics = self.attachAsset('powerup', {
anchorX: 0.5,
anchorY: 0.5
});
self.type = type || getRandomPowerUpType();
self.speed = 3;
function getRandomPowerUpType() {
var types = ['multiball', 'widen', 'slow'];
return types[Math.floor(Math.random() * types.length)];
}
// Set color based on power-up type
switch (self.type) {
case 'multiball':
powerupGraphics.tint = 0xff0000; // Red
break;
case 'widen':
powerupGraphics.tint = 0x00ff00; // Green
break;
case 'slow':
powerupGraphics.tint = 0x0000ff; // Blue
break;
}
self.update = function () {
self.y += self.speed;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000055
});
/****
* Game Code
****/
// Game constants
var BRICK_SPACING = 10;
var BRICK_ROWS_VISIBLE = 10;
var BRICK_COLUMNS = 12;
var SCROLL_SPEED = 0.3;
var STARTING_LIVES = 3;
// Game variables
var paddle;
var balls = [];
var bricks = [];
var powerups = [];
var score = 0;
var lives = STARTING_LIVES;
var level = 1;
var scrollPosition = 0;
var highScore = storage.highScore || 0;
var isPaused = false;
var isGameOver = false;
// UI elements
var scoreTxt;
var livesTxt;
var levelTxt;
var highScoreTxt;
function initializeGame() {
// Reset game state
score = 0;
lives = STARTING_LIVES;
level = 1;
scrollPosition = 0;
isGameOver = false;
// Clear existing game objects
while (bricks.length > 0) {
var brick = bricks.pop();
brick.destroy();
}
while (balls.length > 0) {
var ball = balls.pop();
ball.destroy();
}
while (powerups.length > 0) {
var powerup = powerups.pop();
powerup.destroy();
}
if (paddle) {
paddle.destroy();
}
// Create paddle
paddle = new Paddle();
paddle.x = 2048 / 2;
paddle.y = 2732 - 100;
game.addChild(paddle);
// Create initial ball
createBall();
// Generate initial bricks
generateBricks();
// Create UI
createUI();
// Play background music
LK.playMusic('gameMusic', {
fade: {
start: 0,
end: 0.4,
duration: 1000
}
});
}
function createBall() {
var ball = new Ball();
ball.reset(paddle.x, paddle.y);
game.addChild(ball);
balls.push(ball);
return ball;
}
function createUI() {
// Remove existing UI if any
if (scoreTxt) {
scoreTxt.destroy();
}
if (livesTxt) {
livesTxt.destroy();
}
if (levelTxt) {
levelTxt.destroy();
}
if (highScoreTxt) {
highScoreTxt.destroy();
}
// Score text
scoreTxt = new Text2('Score: 0', {
size: 60,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0, 0);
scoreTxt.x = 20;
scoreTxt.y = 20;
LK.gui.addChild(scoreTxt);
// Lives text
livesTxt = new Text2('Lives: ' + lives, {
size: 60,
fill: 0xFFFFFF
});
livesTxt.anchor.set(1, 0);
livesTxt.x = 2048 - 20;
livesTxt.y = 20;
LK.gui.addChild(livesTxt);
// Level text
levelTxt = new Text2('Level: ' + level, {
size: 60,
fill: 0xFFFFFF
});
levelTxt.anchor.set(0.5, 0);
levelTxt.x = 2048 / 2;
levelTxt.y = 20;
LK.gui.addChild(levelTxt);
// High score text
highScoreTxt = new Text2('High Score: ' + highScore, {
size: 50,
fill: 0xFFFF00
});
highScoreTxt.anchor.set(0.5, 0);
highScoreTxt.x = 2048 / 2;
highScoreTxt.y = 80;
LK.gui.addChild(highScoreTxt);
}
function updateUI() {
scoreTxt.setText('Score: ' + score);
livesTxt.setText('Lives: ' + lives);
levelTxt.setText('Level: ' + level);
highScoreTxt.setText('High Score: ' + highScore);
}
function generateBricks() {
var brickWidth = 150 + BRICK_SPACING;
var brickHeight = 60 + BRICK_SPACING;
var startX = (2048 - BRICK_COLUMNS * brickWidth) / 2 + brickWidth / 2;
// Generate rows beyond the visible area
for (var row = -5; row < BRICK_ROWS_VISIBLE; row++) {
for (var col = 0; col < BRICK_COLUMNS; col++) {
// Skip some bricks for variety (more gaps at higher levels)
if (Math.random() < 0.1 * (level * 0.15)) {
continue;
}
var brickType = 'normal';
// Add special bricks based on probability and level
var specialBrickChance = Math.min(0.5, 0.05 + level * 0.02);
if (Math.random() < specialBrickChance) {
var rand = Math.random();
if (rand < 0.4) {
brickType = 'reinforced';
} else if (rand < 0.7) {
brickType = 'explosive';
} else {
brickType = 'mystery';
}
}
var brick = new Brick(brickType, row, col);
brick.x = startX + col * brickWidth;
brick.y = row * brickHeight;
game.addChild(brick);
bricks.push(brick);
}
}
}
function addNewRowOfBricks() {
var brickWidth = 150 + BRICK_SPACING;
var brickHeight = 60 + BRICK_SPACING;
var startX = (2048 - BRICK_COLUMNS * brickWidth) / 2 + brickWidth / 2;
var row = -1; // Add new row at the top
for (var col = 0; col < BRICK_COLUMNS; col++) {
// Skip some bricks for variety
if (Math.random() < 0.1 * (level * 0.15)) {
continue;
}
var brickType = 'normal';
// Add special bricks based on probability and level
var specialBrickChance = Math.min(0.5, 0.05 + level * 0.02);
if (Math.random() < specialBrickChance) {
var rand = Math.random();
if (rand < 0.4) {
brickType = 'reinforced';
} else if (rand < 0.7) {
brickType = 'explosive';
} else {
brickType = 'mystery';
}
}
var brick = new Brick(brickType, row, col);
brick.x = startX + col * brickWidth;
brick.y = row * brickHeight;
game.addChild(brick);
bricks.push(brick);
}
}
function scrollBricks() {
var brickHeight = 60 + BRICK_SPACING;
scrollPosition += SCROLL_SPEED * (1 + level * 0.1);
// When scroll position exceeds a brick height, add a new row
if (scrollPosition >= brickHeight) {
scrollPosition -= brickHeight;
addNewRowOfBricks();
}
// Move all bricks down
for (var i = 0; i < bricks.length; i++) {
bricks[i].y += SCROLL_SPEED * (1 + level * 0.1);
// Remove bricks that have gone off the bottom of the screen
if (bricks[i].y > 2732 + bricks[i].height) {
bricks[i].destroy();
bricks.splice(i, 1);
i--;
// Lose a life if a brick reaches the bottom
loseLife();
}
}
}
function loseLife() {
lives--;
updateUI();
// Flash screen red
LK.effects.flashScreen(0xff0000, 500);
if (lives <= 0) {
gameOver();
}
}
function gameOver() {
isGameOver = true;
// Update high score if needed
if (score > highScore) {
highScore = score;
storage.highScore = highScore;
updateUI();
}
// Show game over screen
LK.showGameOver();
}
function handleBallCollisions() {
for (var i = 0; i < balls.length; i++) {
var ball = balls[i];
if (!ball.active) {
continue;
}
// Check if ball is below the screen
if (ball.y > 2732 + ball.radius) {
ball.destroy();
balls.splice(i, 1);
i--;
// If no balls left, lose a life
if (balls.length === 0) {
loseLife();
if (!isGameOver) {
createBall();
}
}
continue;
}
// Paddle collision
if (ball.y + ball.radius > paddle.y - paddle.height / 2 && ball.y - ball.radius < paddle.y + paddle.height / 2 && ball.x + ball.radius > paddle.x - paddle.width / 2 && ball.x - ball.radius < paddle.x + paddle.width / 2) {
// Calculate bounce angle based on where ball hit the paddle
var relativeIntersectX = ball.x - paddle.x;
var normalizedRelativeIntersectionX = relativeIntersectX / (paddle.width / 2);
var bounceAngle = normalizedRelativeIntersectionX * (Math.PI / 3); // Max 60 degrees
var ballSpeed = Math.sqrt(ball.speedX * ball.speedX + ball.speedY * ball.speedY);
ball.speedX = ballSpeed * Math.sin(bounceAngle);
ball.speedY = -ballSpeed * Math.cos(bounceAngle);
// Ensure the ball is moving upward
if (ball.speedY > 0) {
ball.speedY *= -1;
}
// Increase speed slightly
var speedMultiplier = 1.01;
ball.speedX *= speedMultiplier;
ball.speedY *= speedMultiplier;
LK.getSound('hit').play();
}
// Brick collision
for (var j = 0; j < bricks.length; j++) {
var brick = bricks[j];
if (ball.x + ball.radius > brick.x - brick.width / 2 && ball.x - ball.radius < brick.x + brick.width / 2 && ball.y + ball.radius > brick.y - brick.height / 2 && ball.y - ball.radius < brick.y + brick.height / 2) {
// Determine collision direction
var dx = ball.x - brick.x;
var dy = ball.y - brick.y;
// Horizontal or vertical collision?
if (Math.abs(dx) / (brick.width / 2) > Math.abs(dy) / (brick.height / 2)) {
ball.speedX *= -1;
} else {
ball.speedY *= -1;
}
var destroyed = brick.hit();
if (destroyed) {
// Calculate points based on brick type and row
var points = 10;
switch (brick.type) {
case 'reinforced':
points = 20;
break;
case 'explosive':
points = 15;
break;
case 'mystery':
points = 25;
break;
}
// Higher rows worth more
points += Math.max(0, 5 - brick.row) * 2;
score += points;
updateUI();
LK.getSound('break').play();
// Handle special brick effects
if (brick.type === 'explosive') {
// Destroy adjacent bricks
handleExplosion(brick);
} else if (brick.type === 'mystery') {
// Spawn power-up
spawnPowerUp(brick.x, brick.y);
}
// Remove brick
brick.destroy();
bricks.splice(j, 1);
j--;
} else {
LK.getSound('hit').play();
}
// Only process one brick collision per frame
break;
}
}
}
}
function handleExplosion(brick) {
LK.getSound('explosion').play();
LK.effects.flashObject(brick, 0xffff00, 300);
// Define explosion radius (in grid units)
var explosionRadius = 1;
// Check all bricks
for (var i = bricks.length - 1; i >= 0; i--) {
var targetBrick = bricks[i];
// Calculate grid distance
var rowDist = Math.abs(targetBrick.row - brick.row);
var colDist = Math.abs(targetBrick.column - brick.column);
// If within explosion radius
if (rowDist <= explosionRadius && colDist <= explosionRadius && targetBrick !== brick) {
// Award points
score += 5;
// Remove brick
targetBrick.destroy();
bricks.splice(i, 1);
}
}
updateUI();
}
function spawnPowerUp(x, y) {
var powerup = new PowerUp();
powerup.x = x;
powerup.y = y;
game.addChild(powerup);
powerups.push(powerup);
}
function handlePowerUps() {
for (var i = powerups.length - 1; i >= 0; i--) {
var powerup = powerups[i];
powerup.update();
// Check if power-up is off screen
if (powerup.y > 2732 + 20) {
powerup.destroy();
powerups.splice(i, 1);
continue;
}
// Check for collision with paddle
if (powerup.y + 20 > paddle.y - paddle.height / 2 && powerup.y - 20 < paddle.y + paddle.height / 2 && powerup.x + 20 > paddle.x - paddle.width / 2 && powerup.x - 20 < paddle.x + paddle.width / 2) {
// Apply power-up effect
applyPowerUp(powerup.type);
// Remove power-up
powerup.destroy();
powerups.splice(i, 1);
LK.getSound('powerup').play();
}
}
}
function applyPowerUp(type) {
switch (type) {
case 'multiball':
// Add 2 more balls
for (var i = 0; i < 2; i++) {
var ball = createBall();
// Launch in random direction
var angle = Math.PI * 1.5 + (Math.random() * Math.PI - Math.PI / 2);
var speed = 8 + Math.random() * 2;
ball.launch(Math.cos(angle) * speed, Math.sin(angle) * speed);
}
break;
case 'widen':
// Make paddle wider
tween.stop(paddle);
tween(paddle, {
scaleX: 1.5
}, {
duration: 300,
onFinish: function onFinish() {
// Return to normal after 10 seconds
LK.setTimeout(function () {
tween(paddle, {
scaleX: 1
}, {
duration: 300
});
}, 10000);
}
});
break;
case 'slow':
// Slow down scroll speed temporarily
var originalScrollSpeed = SCROLL_SPEED;
SCROLL_SPEED *= 0.5;
// Return to normal after 8 seconds
LK.setTimeout(function () {
SCROLL_SPEED = originalScrollSpeed;
}, 8000);
break;
}
}
function checkLevelUp() {
// Level up every 1000 points
var newLevel = Math.floor(score / 1000) + 1;
if (newLevel > level) {
level = newLevel;
updateUI();
// Flash screen green for level up
LK.effects.flashScreen(0x00ff00, 500);
}
}
// Input handlers
var isDragging = false;
game.down = function (x, y, obj) {
if (isGameOver) {
return;
}
isDragging = true;
paddle.moveTo(x);
// Launch ball if not active
if (balls.length === 1 && !balls[0].active) {
var angle = Math.PI * 1.5 + (Math.random() * 0.4 - 0.2); // Mostly upward
var speed = 8 + level * 0.3; // Speed increases with level
balls[0].launch(Math.cos(angle) * speed, Math.sin(angle) * speed);
}
};
game.up = function (x, y, obj) {
isDragging = false;
};
game.move = function (x, y, obj) {
if (isDragging) {
paddle.moveTo(x);
}
// If ball is not active, make it follow the paddle
for (var i = 0; i < balls.length; i++) {
if (!balls[i].active) {
balls[i].x = paddle.x;
}
}
};
// Main game update loop
game.update = function () {
if (isGameOver) {
return;
}
// Scroll bricks down
scrollBricks();
// Update balls
for (var i = 0; i < balls.length; i++) {
balls[i].update();
}
// Check collisions
handleBallCollisions();
// Handle power-ups
handlePowerUps();
// Check for level up
checkLevelUp();
};
// Initialize the game
initializeGame();
// Play background music
LK.playMusic('gameMusic'); /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
highScore: 0
});
/****
* Classes
****/
var Ball = Container.expand(function () {
var self = Container.call(this);
var ballGraphics = self.attachAsset('ball', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = ballGraphics.width / 2;
self.speedX = 0;
self.speedY = 0;
self.active = false;
self.launch = function (speedX, speedY) {
self.speedX = speedX;
self.speedY = speedY;
self.active = true;
};
self.reset = function (paddleX, paddleY) {
self.x = paddleX;
self.y = paddleY - 30;
self.speedX = 0;
self.speedY = 0;
self.active = false;
};
self.update = function () {
if (!self.active) {
return;
}
// Move ball
self.x += self.speedX;
self.y += self.speedY;
// Handle wall collisions
if (self.x - self.radius <= 0 || self.x + self.radius >= 2048) {
self.speedX *= -1;
LK.getSound('hit').play();
}
if (self.y - self.radius <= 0) {
self.speedY *= -1;
LK.getSound('hit').play();
}
};
return self;
});
var Brick = Container.expand(function (type, row, column) {
var self = Container.call(this);
var assetId = 'brick';
if (type === 'reinforced') {
assetId = 'reinforcedBrick';
} else if (type === 'explosive') {
assetId = 'explosiveBrick';
} else if (type === 'mystery') {
assetId = 'mysteryBrick';
}
var brickGraphics = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5
});
self.type = type || 'normal';
self.width = brickGraphics.width;
self.height = brickGraphics.height;
self.row = row || 0;
self.column = column || 0;
self.health = type === 'reinforced' ? 2 : 1;
self.hit = function () {
self.health--;
if (self.health <= 0) {
return true; // Brick destroyed
} else {
// Show brick is damaged
tween(brickGraphics, {
alpha: 0.7
}, {
duration: 100,
onFinish: function onFinish() {
tween(brickGraphics, {
alpha: 1
}, {
duration: 100
});
}
});
return false; // Brick still alive
}
};
return self;
});
var Paddle = Container.expand(function () {
var self = Container.call(this);
var paddleGraphics = self.attachAsset('paddle', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = paddleGraphics.width;
self.height = paddleGraphics.height;
self.moveTo = function (x) {
// Ensure paddle stays within screen bounds
var minX = self.width / 2;
var maxX = 2048 - self.width / 2;
self.x = Math.max(minX, Math.min(maxX, x));
};
return self;
});
var PowerUp = Container.expand(function (type) {
var self = Container.call(this);
var powerupGraphics = self.attachAsset('powerup', {
anchorX: 0.5,
anchorY: 0.5
});
self.type = type || getRandomPowerUpType();
self.speed = 3;
function getRandomPowerUpType() {
var types = ['multiball', 'widen', 'slow'];
return types[Math.floor(Math.random() * types.length)];
}
// Set color based on power-up type
switch (self.type) {
case 'multiball':
powerupGraphics.tint = 0xff0000; // Red
break;
case 'widen':
powerupGraphics.tint = 0x00ff00; // Green
break;
case 'slow':
powerupGraphics.tint = 0x0000ff; // Blue
break;
}
self.update = function () {
self.y += self.speed;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000055
});
/****
* Game Code
****/
// Game constants
var BRICK_SPACING = 10;
var BRICK_ROWS_VISIBLE = 10;
var BRICK_COLUMNS = 12;
var SCROLL_SPEED = 0.3;
var STARTING_LIVES = 3;
// Game variables
var paddle;
var balls = [];
var bricks = [];
var powerups = [];
var score = 0;
var lives = STARTING_LIVES;
var level = 1;
var scrollPosition = 0;
var highScore = storage.highScore || 0;
var isPaused = false;
var isGameOver = false;
// UI elements
var scoreTxt;
var livesTxt;
var levelTxt;
var highScoreTxt;
function initializeGame() {
// Reset game state
score = 0;
lives = STARTING_LIVES;
level = 1;
scrollPosition = 0;
isGameOver = false;
// Clear existing game objects
while (bricks.length > 0) {
var brick = bricks.pop();
brick.destroy();
}
while (balls.length > 0) {
var ball = balls.pop();
ball.destroy();
}
while (powerups.length > 0) {
var powerup = powerups.pop();
powerup.destroy();
}
if (paddle) {
paddle.destroy();
}
// Create paddle
paddle = new Paddle();
paddle.x = 2048 / 2;
paddle.y = 2732 - 100;
game.addChild(paddle);
// Create initial ball
createBall();
// Generate initial bricks
generateBricks();
// Create UI
createUI();
// Play background music
LK.playMusic('gameMusic', {
fade: {
start: 0,
end: 0.4,
duration: 1000
}
});
}
function createBall() {
var ball = new Ball();
ball.reset(paddle.x, paddle.y);
game.addChild(ball);
balls.push(ball);
return ball;
}
function createUI() {
// Remove existing UI if any
if (scoreTxt) {
scoreTxt.destroy();
}
if (livesTxt) {
livesTxt.destroy();
}
if (levelTxt) {
levelTxt.destroy();
}
if (highScoreTxt) {
highScoreTxt.destroy();
}
// Score text
scoreTxt = new Text2('Score: 0', {
size: 60,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0, 0);
scoreTxt.x = 20;
scoreTxt.y = 20;
LK.gui.addChild(scoreTxt);
// Lives text
livesTxt = new Text2('Lives: ' + lives, {
size: 60,
fill: 0xFFFFFF
});
livesTxt.anchor.set(1, 0);
livesTxt.x = 2048 - 20;
livesTxt.y = 20;
LK.gui.addChild(livesTxt);
// Level text
levelTxt = new Text2('Level: ' + level, {
size: 60,
fill: 0xFFFFFF
});
levelTxt.anchor.set(0.5, 0);
levelTxt.x = 2048 / 2;
levelTxt.y = 20;
LK.gui.addChild(levelTxt);
// High score text
highScoreTxt = new Text2('High Score: ' + highScore, {
size: 50,
fill: 0xFFFF00
});
highScoreTxt.anchor.set(0.5, 0);
highScoreTxt.x = 2048 / 2;
highScoreTxt.y = 80;
LK.gui.addChild(highScoreTxt);
}
function updateUI() {
scoreTxt.setText('Score: ' + score);
livesTxt.setText('Lives: ' + lives);
levelTxt.setText('Level: ' + level);
highScoreTxt.setText('High Score: ' + highScore);
}
function generateBricks() {
var brickWidth = 150 + BRICK_SPACING;
var brickHeight = 60 + BRICK_SPACING;
var startX = (2048 - BRICK_COLUMNS * brickWidth) / 2 + brickWidth / 2;
// Generate rows beyond the visible area
for (var row = -5; row < BRICK_ROWS_VISIBLE; row++) {
for (var col = 0; col < BRICK_COLUMNS; col++) {
// Skip some bricks for variety (more gaps at higher levels)
if (Math.random() < 0.1 * (level * 0.15)) {
continue;
}
var brickType = 'normal';
// Add special bricks based on probability and level
var specialBrickChance = Math.min(0.5, 0.05 + level * 0.02);
if (Math.random() < specialBrickChance) {
var rand = Math.random();
if (rand < 0.4) {
brickType = 'reinforced';
} else if (rand < 0.7) {
brickType = 'explosive';
} else {
brickType = 'mystery';
}
}
var brick = new Brick(brickType, row, col);
brick.x = startX + col * brickWidth;
brick.y = row * brickHeight;
game.addChild(brick);
bricks.push(brick);
}
}
}
function addNewRowOfBricks() {
var brickWidth = 150 + BRICK_SPACING;
var brickHeight = 60 + BRICK_SPACING;
var startX = (2048 - BRICK_COLUMNS * brickWidth) / 2 + brickWidth / 2;
var row = -1; // Add new row at the top
for (var col = 0; col < BRICK_COLUMNS; col++) {
// Skip some bricks for variety
if (Math.random() < 0.1 * (level * 0.15)) {
continue;
}
var brickType = 'normal';
// Add special bricks based on probability and level
var specialBrickChance = Math.min(0.5, 0.05 + level * 0.02);
if (Math.random() < specialBrickChance) {
var rand = Math.random();
if (rand < 0.4) {
brickType = 'reinforced';
} else if (rand < 0.7) {
brickType = 'explosive';
} else {
brickType = 'mystery';
}
}
var brick = new Brick(brickType, row, col);
brick.x = startX + col * brickWidth;
brick.y = row * brickHeight;
game.addChild(brick);
bricks.push(brick);
}
}
function scrollBricks() {
var brickHeight = 60 + BRICK_SPACING;
scrollPosition += SCROLL_SPEED * (1 + level * 0.1);
// When scroll position exceeds a brick height, add a new row
if (scrollPosition >= brickHeight) {
scrollPosition -= brickHeight;
addNewRowOfBricks();
}
// Move all bricks down
for (var i = 0; i < bricks.length; i++) {
bricks[i].y += SCROLL_SPEED * (1 + level * 0.1);
// Remove bricks that have gone off the bottom of the screen
if (bricks[i].y > 2732 + bricks[i].height) {
bricks[i].destroy();
bricks.splice(i, 1);
i--;
// Lose a life if a brick reaches the bottom
loseLife();
}
}
}
function loseLife() {
lives--;
updateUI();
// Flash screen red
LK.effects.flashScreen(0xff0000, 500);
if (lives <= 0) {
gameOver();
}
}
function gameOver() {
isGameOver = true;
// Update high score if needed
if (score > highScore) {
highScore = score;
storage.highScore = highScore;
updateUI();
}
// Show game over screen
LK.showGameOver();
}
function handleBallCollisions() {
for (var i = 0; i < balls.length; i++) {
var ball = balls[i];
if (!ball.active) {
continue;
}
// Check if ball is below the screen
if (ball.y > 2732 + ball.radius) {
ball.destroy();
balls.splice(i, 1);
i--;
// If no balls left, lose a life
if (balls.length === 0) {
loseLife();
if (!isGameOver) {
createBall();
}
}
continue;
}
// Paddle collision
if (ball.y + ball.radius > paddle.y - paddle.height / 2 && ball.y - ball.radius < paddle.y + paddle.height / 2 && ball.x + ball.radius > paddle.x - paddle.width / 2 && ball.x - ball.radius < paddle.x + paddle.width / 2) {
// Calculate bounce angle based on where ball hit the paddle
var relativeIntersectX = ball.x - paddle.x;
var normalizedRelativeIntersectionX = relativeIntersectX / (paddle.width / 2);
var bounceAngle = normalizedRelativeIntersectionX * (Math.PI / 3); // Max 60 degrees
var ballSpeed = Math.sqrt(ball.speedX * ball.speedX + ball.speedY * ball.speedY);
ball.speedX = ballSpeed * Math.sin(bounceAngle);
ball.speedY = -ballSpeed * Math.cos(bounceAngle);
// Ensure the ball is moving upward
if (ball.speedY > 0) {
ball.speedY *= -1;
}
// Increase speed slightly
var speedMultiplier = 1.01;
ball.speedX *= speedMultiplier;
ball.speedY *= speedMultiplier;
LK.getSound('hit').play();
}
// Brick collision
for (var j = 0; j < bricks.length; j++) {
var brick = bricks[j];
if (ball.x + ball.radius > brick.x - brick.width / 2 && ball.x - ball.radius < brick.x + brick.width / 2 && ball.y + ball.radius > brick.y - brick.height / 2 && ball.y - ball.radius < brick.y + brick.height / 2) {
// Determine collision direction
var dx = ball.x - brick.x;
var dy = ball.y - brick.y;
// Horizontal or vertical collision?
if (Math.abs(dx) / (brick.width / 2) > Math.abs(dy) / (brick.height / 2)) {
ball.speedX *= -1;
} else {
ball.speedY *= -1;
}
var destroyed = brick.hit();
if (destroyed) {
// Calculate points based on brick type and row
var points = 10;
switch (brick.type) {
case 'reinforced':
points = 20;
break;
case 'explosive':
points = 15;
break;
case 'mystery':
points = 25;
break;
}
// Higher rows worth more
points += Math.max(0, 5 - brick.row) * 2;
score += points;
updateUI();
LK.getSound('break').play();
// Handle special brick effects
if (brick.type === 'explosive') {
// Destroy adjacent bricks
handleExplosion(brick);
} else if (brick.type === 'mystery') {
// Spawn power-up
spawnPowerUp(brick.x, brick.y);
}
// Remove brick
brick.destroy();
bricks.splice(j, 1);
j--;
} else {
LK.getSound('hit').play();
}
// Only process one brick collision per frame
break;
}
}
}
}
function handleExplosion(brick) {
LK.getSound('explosion').play();
LK.effects.flashObject(brick, 0xffff00, 300);
// Define explosion radius (in grid units)
var explosionRadius = 1;
// Check all bricks
for (var i = bricks.length - 1; i >= 0; i--) {
var targetBrick = bricks[i];
// Calculate grid distance
var rowDist = Math.abs(targetBrick.row - brick.row);
var colDist = Math.abs(targetBrick.column - brick.column);
// If within explosion radius
if (rowDist <= explosionRadius && colDist <= explosionRadius && targetBrick !== brick) {
// Award points
score += 5;
// Remove brick
targetBrick.destroy();
bricks.splice(i, 1);
}
}
updateUI();
}
function spawnPowerUp(x, y) {
var powerup = new PowerUp();
powerup.x = x;
powerup.y = y;
game.addChild(powerup);
powerups.push(powerup);
}
function handlePowerUps() {
for (var i = powerups.length - 1; i >= 0; i--) {
var powerup = powerups[i];
powerup.update();
// Check if power-up is off screen
if (powerup.y > 2732 + 20) {
powerup.destroy();
powerups.splice(i, 1);
continue;
}
// Check for collision with paddle
if (powerup.y + 20 > paddle.y - paddle.height / 2 && powerup.y - 20 < paddle.y + paddle.height / 2 && powerup.x + 20 > paddle.x - paddle.width / 2 && powerup.x - 20 < paddle.x + paddle.width / 2) {
// Apply power-up effect
applyPowerUp(powerup.type);
// Remove power-up
powerup.destroy();
powerups.splice(i, 1);
LK.getSound('powerup').play();
}
}
}
function applyPowerUp(type) {
switch (type) {
case 'multiball':
// Add 2 more balls
for (var i = 0; i < 2; i++) {
var ball = createBall();
// Launch in random direction
var angle = Math.PI * 1.5 + (Math.random() * Math.PI - Math.PI / 2);
var speed = 8 + Math.random() * 2;
ball.launch(Math.cos(angle) * speed, Math.sin(angle) * speed);
}
break;
case 'widen':
// Make paddle wider
tween.stop(paddle);
tween(paddle, {
scaleX: 1.5
}, {
duration: 300,
onFinish: function onFinish() {
// Return to normal after 10 seconds
LK.setTimeout(function () {
tween(paddle, {
scaleX: 1
}, {
duration: 300
});
}, 10000);
}
});
break;
case 'slow':
// Slow down scroll speed temporarily
var originalScrollSpeed = SCROLL_SPEED;
SCROLL_SPEED *= 0.5;
// Return to normal after 8 seconds
LK.setTimeout(function () {
SCROLL_SPEED = originalScrollSpeed;
}, 8000);
break;
}
}
function checkLevelUp() {
// Level up every 1000 points
var newLevel = Math.floor(score / 1000) + 1;
if (newLevel > level) {
level = newLevel;
updateUI();
// Flash screen green for level up
LK.effects.flashScreen(0x00ff00, 500);
}
}
// Input handlers
var isDragging = false;
game.down = function (x, y, obj) {
if (isGameOver) {
return;
}
isDragging = true;
paddle.moveTo(x);
// Launch ball if not active
if (balls.length === 1 && !balls[0].active) {
var angle = Math.PI * 1.5 + (Math.random() * 0.4 - 0.2); // Mostly upward
var speed = 8 + level * 0.3; // Speed increases with level
balls[0].launch(Math.cos(angle) * speed, Math.sin(angle) * speed);
}
};
game.up = function (x, y, obj) {
isDragging = false;
};
game.move = function (x, y, obj) {
if (isDragging) {
paddle.moveTo(x);
}
// If ball is not active, make it follow the paddle
for (var i = 0; i < balls.length; i++) {
if (!balls[i].active) {
balls[i].x = paddle.x;
}
}
};
// Main game update loop
game.update = function () {
if (isGameOver) {
return;
}
// Scroll bricks down
scrollBricks();
// Update balls
for (var i = 0; i < balls.length; i++) {
balls[i].update();
}
// Check collisions
handleBallCollisions();
// Handle power-ups
handlePowerUps();
// Check for level up
checkLevelUp();
};
// Initialize the game
initializeGame();
// Play background music
LK.playMusic('gameMusic');