/**** * 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');