/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ var Ball = Container.expand(function () { var self = Container.call(this); var ballGraphics = self.attachAsset('ball', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 10; self.directionX = 0; self.directionY = -1; self.active = false; self.radius = ballGraphics.width / 2; self.reset = function (paddle) { self.active = false; self.x = paddle.x; self.y = paddle.y - paddle.height / 2 - self.radius; self.directionX = 0; self.directionY = -1; }; self.launch = function () { if (!self.active) { self.active = true; // Random angle between -45 and 45 degrees var angle = (Math.random() * 90 - 45) * Math.PI / 180; self.directionX = Math.sin(angle); self.directionY = -Math.cos(angle); } }; self.update = function () { if (self.active) { self.x += self.directionX * self.speed; self.y += self.directionY * self.speed; // Wall collision if (self.x - self.radius < 0) { self.x = self.radius; self.directionX = -self.directionX; LK.getSound('hit').play(); } else if (self.x + self.radius > 2048) { self.x = 2048 - self.radius; self.directionX = -self.directionX; LK.getSound('hit').play(); } // Ceiling collision if (self.y - self.radius < 0) { self.y = self.radius; self.directionY = -self.directionY; LK.getSound('hit').play(); } } }; self.bounceOff = function (object) { // Calculate bounce angle based on where the ball hit the object var relativeX = (self.x - object.x) / (object.width / 2); relativeX = Math.max(-1, Math.min(1, relativeX)); // Clamp between -1 and 1 // Calculate new direction - bouncing upward (for paddle) self.directionX = relativeX * 0.8; self.directionY = -Math.sqrt(1 - self.directionX * self.directionX); // Normalize var magnitude = Math.sqrt(self.directionX * self.directionX + self.directionY * self.directionY); self.directionX /= magnitude; self.directionY /= magnitude; LK.getSound('hit').play(); }; return self; }); var Brick = Container.expand(function () { var self = Container.call(this); var brickGraphics = self.attachAsset('brick', { anchorX: 0.5, anchorY: 0.5 }); self.width = brickGraphics.width; self.height = brickGraphics.height; self.hitPoints = 1; self.powerupChance = 0.2; self.setColor = function (color) { brickGraphics.tint = color; }; self.hit = function () { self.hitPoints--; if (self.hitPoints <= 0) { LK.getSound('break').play(); return true; // Brick destroyed } else { // Make brick darker brickGraphics.tint = (brickGraphics.tint & 0xfefefe) >> 1; LK.getSound('hit').play(); return false; // Brick not destroyed yet } }; return self; }); var Bullet = Container.expand(function () { var self = Container.call(this); var bulletGraphics = self.attachAsset('bullet', { anchorX: 0.5, anchorY: 0.5 }); self.speed = -10; self.update = function () { self.y += self.speed; }; 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.normalWidth = paddleGraphics.width; self.expand = function () { tween(paddleGraphics, { width: self.normalWidth * 1.5 }, { duration: 300, easing: tween.easeOut }); self.width = self.normalWidth * 1.5; LK.setTimeout(function () { tween(paddleGraphics, { width: self.normalWidth }, { duration: 300, easing: tween.easeOut }); self.width = self.normalWidth; }, 10000); // 10 seconds }; return self; }); var PowerUp = Container.expand(function () { var self = Container.call(this); var powerupGraphics; self.setType = function (type) { self.type = type; switch (type) { case 'expand': powerupGraphics = self.attachAsset('powerup_expand', { anchorX: 0.5, anchorY: 0.5 }); break; case 'multiball': powerupGraphics = self.attachAsset('powerup_multiball', { anchorX: 0.5, anchorY: 0.5 }); break; case 'slow': powerupGraphics = self.attachAsset('powerup_slow', { anchorX: 0.5, anchorY: 0.5 }); break; } }; self.type = 'expand'; // Default powerup type self.fallSpeed = 5; // Set different colors for different powerup types self.setType = function (type) { self.type = type; switch (type) { case 'expand': powerupGraphics = self.attachAsset('powerup_expand', { anchorX: 0.5, anchorY: 0.5 }); break; case 'multiball': powerupGraphics = self.attachAsset('powerup_multiball', { anchorX: 0.5, anchorY: 0.5 }); break; case 'slow': powerupGraphics = self.attachAsset('powerup_slow', { anchorX: 0.5, anchorY: 0.5 }); break; case 'grow': powerupGraphics = self.attachAsset('powerup_grow', { anchorX: 0.5, anchorY: 0.5 }); break; case 'laser': powerupGraphics = self.attachAsset('powerup_laser', { anchorX: 0.5, anchorY: 0.5 }); break; case 'freeze': powerupGraphics = self.attachAsset('powerup_freeze', { anchorX: 0.5, anchorY: 0.5 }); break; case 'shrink': powerupGraphics = self.attachAsset('powerup_shrink', { anchorX: 0.5, anchorY: 0.5 }); break; case 'invert': powerupGraphics = self.attachAsset('powerup_invert', { anchorX: 0.5, anchorY: 0.5 }); break; } }; self.update = function () { self.y += self.fallSpeed; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x222222 }); /**** * Game Code ****/ // Add background image // New visual for 'grow' powerup Paddle.prototype.setTint = function (color) { this.children[0].tint = color; // Assuming the first child is paddleGraphics }; var background = LK.getAsset('background', { anchorX: 0.5, anchorY: 0.5 }); background.x = 2048 / 2; background.y = 2732 / 2; game.addChild(background); // Initialize game elements var level = 1; var paddle = null; var balls = []; var bricks = []; var bullets = []; // Define bullets array to track bullet instances var powerups = []; var gameState = 'ready'; // 'ready', 'playing', 'gameover' var dragNode = null; // Colors for bricks var BRICK_COLORS = [0xFF0000, // Red 0xFF7F00, // Orange 0xFFFF00, // Yellow 0x00FF00, // Green 0x0000FF, // Blue 0x4B0082, // Indigo 0x9400D3 // Violet ]; // Create UI elements var lives = 100; // Initial number of lives var livesTxt = new Text2('Lives: ' + lives, { size: 80, fill: 0xFFFFFF }); livesTxt.anchor.set(0.5, 0); livesTxt.x = 165; // Move a few pixels further to the right LK.gui.topLeft.addChild(livesTxt); var scoreTxt = new Text2('0', { size: 80, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); var levelTxt = new Text2('Level: 1', { size: 60, fill: 0xFFFFFF }); levelTxt.anchor.set(0, 0); levelTxt.x = 100; levelTxt.y = 20; LK.gui.topRight.addChild(levelTxt); var instructionTxt = new Text2('Tap to start - Drag paddle to play', { size: 60, fill: 0xFFFFFF }); instructionTxt.anchor.set(0.5, 0); instructionTxt.y = 150; LK.gui.top.addChild(instructionTxt); // Initialize game elements function initGame() { // Reset score LK.setScore(0); scoreTxt.setText('0'); // Create paddle paddle = new Paddle(); paddle.x = 2048 / 2; paddle.y = 2732 - 150; game.addChild(paddle); // Assign the down event handler to the paddle after it is initialized paddle.down = function (x, y, obj) { // Check if enough time has passed since the last bullet was fired if (!paddle.lastBulletTime || Date.now() - paddle.lastBulletTime > 500) { // 500ms interval // Fire a bullet from the paddle var bullet = new Bullet(); bullet.x = paddle.x; bullet.y = paddle.y - paddle.height / 2; game.addChild(bullet); bullets.push(bullet); // Play bullet sound LK.getSound('shoot').play(); // Update the last bullet time paddle.lastBulletTime = Date.now(); } }; // Create initial ball createBall(); // Create brick layout for level 1 createLevel(1); // Set game state gameState = 'ready'; instructionTxt.visible = true; // Start background music LK.playMusic('bgmusic', { fade: { start: 0, end: 0.3, duration: 1000 } }); } function createBall() { var ball = new Ball(); ball.reset(paddle); game.addChild(ball); balls.push(ball); return ball; } function createLevel(level) { // Clear existing bricks for (var i = bricks.length - 1; i >= 0; i--) { bricks[i].destroy(); } bricks = []; levelTxt.setText('Level: ' + level); // Define grid dimensions based on level var rows = Math.min(5 + Math.floor(level / 2), 10); var cols = Math.min(10 + Math.floor(level / 3), 13); var brickWidth = 150; var brickHeight = 60; var padding = 10; var totalWidth = cols * (brickWidth + padding) - padding; var startX = (2048 - totalWidth) / 2 + brickWidth / 2; var startY = 200; // Create brick layout for (var row = 0; row < rows; row++) { for (var col = 0; col < cols; col++) { var brick = new Brick(); brick.attachAsset('brick' + row % BRICK_COLORS.length, { anchorX: 0.5, anchorY: 0.5 }); // Position brick brick.x = startX + col * (brickWidth + padding); brick.y = startY + row * (brickHeight + padding); // Color based on row brick.setColor(BRICK_COLORS[row % BRICK_COLORS.length]); // Higher levels have stronger bricks if (level > 2 && row < 2) { brick.hitPoints = 2; } game.addChild(brick); bricks.push(brick); } } } function startGame() { if (gameState === 'ready') { gameState = 'playing'; instructionTxt.visible = false; // Launch the first ball if (balls.length > 0) { balls[0].launch(); } } } function checkCollisions() { // Ball-paddle collision for (var i = 0; i < balls.length; i++) { var ball = balls[i]; if (ball.active) { // Check for 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 && ball.directionY > 0) { // Only bounce if moving downward ball.y = paddle.y - paddle.height / 2 - ball.radius; ball.bounceOff(paddle); } // Check for brick collisions for (var j = bricks.length - 1; j >= 0; 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 which side of the brick was hit var dx = ball.x - brick.x; var dy = ball.y - brick.y; var width = brick.width / 2 + ball.radius; var height = brick.height / 2 + ball.radius; var crossWidth = width * dy; var crossHeight = height * dx; if (Math.abs(dx) <= width && Math.abs(dy) <= height) { if (crossWidth > crossHeight) { if (crossWidth > -crossHeight) { // Bottom collision ball.directionY = Math.abs(ball.directionY); } else { // Left collision ball.directionX = -Math.abs(ball.directionX); } } else { if (crossWidth > -crossHeight) { // Right collision ball.directionX = Math.abs(ball.directionX); } else { // Top collision ball.directionY = -Math.abs(ball.directionY); } } // Hit the brick if (brick.hit()) { // Brick destroyed LK.setScore(LK.getScore() + 10); scoreTxt.setText(LK.getScore()); // Check if score is a multiple of 200 if (LK.getScore() % 200 === 0) { lives++; // Increase lives by 1 livesTxt.setText('Lives: ' + lives); // Update lives display } // Chance to spawn powerup if (Math.random() < brick.powerupChance) { spawnPowerup(brick.x, brick.y); } bricks.splice(j, 1); brick.destroy(); } break; // Only hit one brick per frame } } } // Check for bullet-brick collisions for (var b = bullets.length - 1; b >= 0; b--) { var bullet = bullets[b]; for (var j = bricks.length - 1; j >= 0; j--) { var brick = bricks[j]; if (bullet.x + bullet.width / 2 > brick.x - brick.width / 2 && bullet.x - bullet.width / 2 < brick.x + brick.width / 2 && bullet.y + bullet.height / 2 > brick.y - brick.height / 2 && bullet.y - bullet.height / 2 < brick.y + brick.height / 2) { // Hit the brick if (brick.hit()) { LK.getSound('hit').play(); // Play sound when bullet hits a brick // Brick destroyed LK.setScore(LK.getScore() + 10); scoreTxt.setText(LK.getScore()); // Chance to spawn powerup from bullet-destroyed brick if (Math.random() < brick.powerupChance) { spawnPowerup(brick.x, brick.y); } bricks.splice(j, 1); brick.destroy(); } // Destroy the bullet bullets.splice(b, 1); bullet.destroy(); break; } } } // Check if ball is below screen if (ball.y - ball.radius > 2732) { // Reset the ball position to the paddle lives--; // Decrease lives count livesTxt.setText('Lives: ' + lives); // Update lives display if (lives > 0) { ball.reset(paddle); ball.launch(); LK.getSound('fall').play(); } else { gameOver(); // Trigger game over if no lives left } } } } // Powerup-paddle collision for (var k = powerups.length - 1; k >= 0; k--) { var powerup = powerups[k]; if (powerup.y - 20 > 2732) { // Powerup fell off screen game.removeChild(powerup); powerups.splice(k, 1); } else 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) { // Collected powerup applyPowerup(powerup.type); LK.getSound('powerup').play(); game.removeChild(powerup); powerups.splice(k, 1); } } // Check if all bricks are gone if (bricks.length === 0 && gameState === 'playing') { levelComplete(); } } function spawnPowerup(x, y) { var powerup = new PowerUp(); powerup.x = x; powerup.y = y; // Random powerup type var types = ['expand', 'multiball', 'slow', 'grow', 'laser', 'freeze', 'shrink', 'invert']; var randomType = types[Math.floor(Math.random() * types.length)]; powerup.setType(randomType); game.addChild(powerup); powerups.push(powerup); } function applyPowerup(type) { switch (type) { case 'expand': paddle.expand(); break; case 'multiball': // Create two new balls for (var i = 0; i < 2; i++) { var newBall = createBall(); newBall.x = balls[0].x; newBall.y = balls[0].y; // Random direction var angle = Math.random() * 360 * Math.PI / 180; newBall.directionX = Math.sin(angle); newBall.directionY = Math.cos(angle); if (newBall.directionY > 0) { newBall.directionY = -newBall.directionY; // Make sure ball goes upward } newBall.active = true; } break; case 'grow': for (var i = 0; i < balls.length; i++) { tween(balls[i], { scaleX: 2.0, scaleY: 2.0 }, { duration: 300, easing: tween.easeOut }); balls[i].radius *= 2.0; } LK.setTimeout(function () { for (var i = 0; i < balls.length; i++) { tween(balls[i], { scaleX: 1, scaleY: 1 }, { duration: 300, easing: tween.easeOut }); balls[i].radius /= 2.0; } }, 10000); break; case 'slow': // Slow down all balls for 10 seconds for (var j = 0; j < balls.length; j++) { balls[j].speed = 5; } LK.setTimeout(function () { for (var j = 0; j < balls.length; j++) { balls[j].speed = 10; } }, 10000); break; case 'laser': // Implement laser effect: allow the paddle to shoot lasers for a short duration // Example: Change paddle color to indicate laser mode paddle.setTint(0xFF0000); // Change paddle color to red LK.getSound('laser').play(); // Play laser sound when laser powerup is activated var laserInterval = LK.setInterval(function () { var laser = new Bullet(); laser.x = paddle.x; laser.y = paddle.y - paddle.height / 2; game.addChild(laser); bullets.push(laser); LK.getSound('laser').play(); // Play laser sound on each shot }, 500); // Fire laser every 500ms LK.setTimeout(function () { LK.clearInterval(laserInterval); paddle.setTint(0xFFFFFF); // Revert paddle color after laser duration }, 10000); // Laser mode lasts for 10 seconds break; case 'freeze': // Freeze all balls for 5 seconds for (var j = 0; j < balls.length; j++) { balls[j].speed = 0; } LK.setTimeout(function () { for (var j = 0; j < balls.length; j++) { balls[j].speed = 10; // Restore original speed } }, 5000); // Freeze duration break; case 'shrink': // Shrink all balls for 10 seconds for (var i = 0; i < balls.length; i++) { tween(balls[i], { scaleX: 0.5, scaleY: 0.5 }, { duration: 300, easing: tween.easeOut }); balls[i].radius *= 0.5; } LK.setTimeout(function () { for (var i = 0; i < balls.length; i++) { tween(balls[i], { scaleX: 1, scaleY: 1 }, { duration: 300, easing: tween.easeOut }); balls[i].radius /= 0.5; } }, 10000); break; case 'invert': // Invert the direction of all balls for 5 seconds for (var j = 0; j < balls.length; j++) { balls[j].directionX = -balls[j].directionX; balls[j].directionY = -balls[j].directionY; } LK.setTimeout(function () { for (var j = 0; j < balls.length; j++) { balls[j].directionX = -balls[j].directionX; balls[j].directionY = -balls[j].directionY; } }, 5000); // Invert duration break; } } function levelComplete() { // Increment level level++; // Clear powerups for (var i = powerups.length - 1; i >= 0; i--) { game.removeChild(powerups[i]); } powerups = []; // Reset balls for (var j = balls.length - 1; j >= 0; j--) { game.removeChild(balls[j]); } balls = []; // Create new ball createBall(); // Create new level createLevel(level); // Show level message instructionTxt.setText('Level ' + level + ' - Tap to start'); instructionTxt.visible = true; // Reset game state gameState = 'ready'; } function gameOver() { gameState = 'gameover'; // Clear powerups for (var i = powerups.length - 1; i >= 0; i--) { game.removeChild(powerups[i]); } powerups = []; // Show game over LK.effects.flashScreen(0xFF0000, 1000); LK.showGameOver(); } // Event handlers function handleMove(x, y, obj) { if (dragNode && gameState === 'playing') { dragNode.x = Math.max(paddle.width / 2, Math.min(2048 - paddle.width / 2, x)); // If ball is attached to paddle, move it too for (var i = 0; i < balls.length; i++) { if (!balls[i].active) { balls[i].x = dragNode.x; } } } } game.down = function (x, y, obj) { if (gameState === 'ready') { startGame(); } dragNode = paddle; handleMove(x, y, obj); }; game.up = function (x, y, obj) { dragNode = null; }; game.move = handleMove; game.update = function () { // Update balls for (var i = 0; i < balls.length; i++) { balls[i].update(); } // Update bullets for (var k = bullets.length - 1; k >= 0; k--) { var bullet = bullets[k]; bullet.update(); // Remove bullet if it goes off-screen if (bullet.y < 0) { bullets.splice(k, 1); bullet.destroy(); } } // Update powerups for (var j = 0; j < powerups.length; j++) { powerups[j].update(); } // Check collisions if (gameState === 'playing') { checkCollisions(); } }; // Initialize the game initGame();
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Ball = Container.expand(function () {
var self = Container.call(this);
var ballGraphics = self.attachAsset('ball', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 10;
self.directionX = 0;
self.directionY = -1;
self.active = false;
self.radius = ballGraphics.width / 2;
self.reset = function (paddle) {
self.active = false;
self.x = paddle.x;
self.y = paddle.y - paddle.height / 2 - self.radius;
self.directionX = 0;
self.directionY = -1;
};
self.launch = function () {
if (!self.active) {
self.active = true;
// Random angle between -45 and 45 degrees
var angle = (Math.random() * 90 - 45) * Math.PI / 180;
self.directionX = Math.sin(angle);
self.directionY = -Math.cos(angle);
}
};
self.update = function () {
if (self.active) {
self.x += self.directionX * self.speed;
self.y += self.directionY * self.speed;
// Wall collision
if (self.x - self.radius < 0) {
self.x = self.radius;
self.directionX = -self.directionX;
LK.getSound('hit').play();
} else if (self.x + self.radius > 2048) {
self.x = 2048 - self.radius;
self.directionX = -self.directionX;
LK.getSound('hit').play();
}
// Ceiling collision
if (self.y - self.radius < 0) {
self.y = self.radius;
self.directionY = -self.directionY;
LK.getSound('hit').play();
}
}
};
self.bounceOff = function (object) {
// Calculate bounce angle based on where the ball hit the object
var relativeX = (self.x - object.x) / (object.width / 2);
relativeX = Math.max(-1, Math.min(1, relativeX)); // Clamp between -1 and 1
// Calculate new direction - bouncing upward (for paddle)
self.directionX = relativeX * 0.8;
self.directionY = -Math.sqrt(1 - self.directionX * self.directionX);
// Normalize
var magnitude = Math.sqrt(self.directionX * self.directionX + self.directionY * self.directionY);
self.directionX /= magnitude;
self.directionY /= magnitude;
LK.getSound('hit').play();
};
return self;
});
var Brick = Container.expand(function () {
var self = Container.call(this);
var brickGraphics = self.attachAsset('brick', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = brickGraphics.width;
self.height = brickGraphics.height;
self.hitPoints = 1;
self.powerupChance = 0.2;
self.setColor = function (color) {
brickGraphics.tint = color;
};
self.hit = function () {
self.hitPoints--;
if (self.hitPoints <= 0) {
LK.getSound('break').play();
return true; // Brick destroyed
} else {
// Make brick darker
brickGraphics.tint = (brickGraphics.tint & 0xfefefe) >> 1;
LK.getSound('hit').play();
return false; // Brick not destroyed yet
}
};
return self;
});
var Bullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = -10;
self.update = function () {
self.y += self.speed;
};
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.normalWidth = paddleGraphics.width;
self.expand = function () {
tween(paddleGraphics, {
width: self.normalWidth * 1.5
}, {
duration: 300,
easing: tween.easeOut
});
self.width = self.normalWidth * 1.5;
LK.setTimeout(function () {
tween(paddleGraphics, {
width: self.normalWidth
}, {
duration: 300,
easing: tween.easeOut
});
self.width = self.normalWidth;
}, 10000); // 10 seconds
};
return self;
});
var PowerUp = Container.expand(function () {
var self = Container.call(this);
var powerupGraphics;
self.setType = function (type) {
self.type = type;
switch (type) {
case 'expand':
powerupGraphics = self.attachAsset('powerup_expand', {
anchorX: 0.5,
anchorY: 0.5
});
break;
case 'multiball':
powerupGraphics = self.attachAsset('powerup_multiball', {
anchorX: 0.5,
anchorY: 0.5
});
break;
case 'slow':
powerupGraphics = self.attachAsset('powerup_slow', {
anchorX: 0.5,
anchorY: 0.5
});
break;
}
};
self.type = 'expand'; // Default powerup type
self.fallSpeed = 5;
// Set different colors for different powerup types
self.setType = function (type) {
self.type = type;
switch (type) {
case 'expand':
powerupGraphics = self.attachAsset('powerup_expand', {
anchorX: 0.5,
anchorY: 0.5
});
break;
case 'multiball':
powerupGraphics = self.attachAsset('powerup_multiball', {
anchorX: 0.5,
anchorY: 0.5
});
break;
case 'slow':
powerupGraphics = self.attachAsset('powerup_slow', {
anchorX: 0.5,
anchorY: 0.5
});
break;
case 'grow':
powerupGraphics = self.attachAsset('powerup_grow', {
anchorX: 0.5,
anchorY: 0.5
});
break;
case 'laser':
powerupGraphics = self.attachAsset('powerup_laser', {
anchorX: 0.5,
anchorY: 0.5
});
break;
case 'freeze':
powerupGraphics = self.attachAsset('powerup_freeze', {
anchorX: 0.5,
anchorY: 0.5
});
break;
case 'shrink':
powerupGraphics = self.attachAsset('powerup_shrink', {
anchorX: 0.5,
anchorY: 0.5
});
break;
case 'invert':
powerupGraphics = self.attachAsset('powerup_invert', {
anchorX: 0.5,
anchorY: 0.5
});
break;
}
};
self.update = function () {
self.y += self.fallSpeed;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x222222
});
/****
* Game Code
****/
// Add background image
// New visual for 'grow' powerup
Paddle.prototype.setTint = function (color) {
this.children[0].tint = color; // Assuming the first child is paddleGraphics
};
var background = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5
});
background.x = 2048 / 2;
background.y = 2732 / 2;
game.addChild(background);
// Initialize game elements
var level = 1;
var paddle = null;
var balls = [];
var bricks = [];
var bullets = []; // Define bullets array to track bullet instances
var powerups = [];
var gameState = 'ready'; // 'ready', 'playing', 'gameover'
var dragNode = null;
// Colors for bricks
var BRICK_COLORS = [0xFF0000,
// Red
0xFF7F00,
// Orange
0xFFFF00,
// Yellow
0x00FF00,
// Green
0x0000FF,
// Blue
0x4B0082,
// Indigo
0x9400D3 // Violet
];
// Create UI elements
var lives = 100; // Initial number of lives
var livesTxt = new Text2('Lives: ' + lives, {
size: 80,
fill: 0xFFFFFF
});
livesTxt.anchor.set(0.5, 0);
livesTxt.x = 165; // Move a few pixels further to the right
LK.gui.topLeft.addChild(livesTxt);
var scoreTxt = new Text2('0', {
size: 80,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
var levelTxt = new Text2('Level: 1', {
size: 60,
fill: 0xFFFFFF
});
levelTxt.anchor.set(0, 0);
levelTxt.x = 100;
levelTxt.y = 20;
LK.gui.topRight.addChild(levelTxt);
var instructionTxt = new Text2('Tap to start - Drag paddle to play', {
size: 60,
fill: 0xFFFFFF
});
instructionTxt.anchor.set(0.5, 0);
instructionTxt.y = 150;
LK.gui.top.addChild(instructionTxt);
// Initialize game elements
function initGame() {
// Reset score
LK.setScore(0);
scoreTxt.setText('0');
// Create paddle
paddle = new Paddle();
paddle.x = 2048 / 2;
paddle.y = 2732 - 150;
game.addChild(paddle);
// Assign the down event handler to the paddle after it is initialized
paddle.down = function (x, y, obj) {
// Check if enough time has passed since the last bullet was fired
if (!paddle.lastBulletTime || Date.now() - paddle.lastBulletTime > 500) {
// 500ms interval
// Fire a bullet from the paddle
var bullet = new Bullet();
bullet.x = paddle.x;
bullet.y = paddle.y - paddle.height / 2;
game.addChild(bullet);
bullets.push(bullet);
// Play bullet sound
LK.getSound('shoot').play();
// Update the last bullet time
paddle.lastBulletTime = Date.now();
}
};
// Create initial ball
createBall();
// Create brick layout for level 1
createLevel(1);
// Set game state
gameState = 'ready';
instructionTxt.visible = true;
// Start background music
LK.playMusic('bgmusic', {
fade: {
start: 0,
end: 0.3,
duration: 1000
}
});
}
function createBall() {
var ball = new Ball();
ball.reset(paddle);
game.addChild(ball);
balls.push(ball);
return ball;
}
function createLevel(level) {
// Clear existing bricks
for (var i = bricks.length - 1; i >= 0; i--) {
bricks[i].destroy();
}
bricks = [];
levelTxt.setText('Level: ' + level);
// Define grid dimensions based on level
var rows = Math.min(5 + Math.floor(level / 2), 10);
var cols = Math.min(10 + Math.floor(level / 3), 13);
var brickWidth = 150;
var brickHeight = 60;
var padding = 10;
var totalWidth = cols * (brickWidth + padding) - padding;
var startX = (2048 - totalWidth) / 2 + brickWidth / 2;
var startY = 200;
// Create brick layout
for (var row = 0; row < rows; row++) {
for (var col = 0; col < cols; col++) {
var brick = new Brick();
brick.attachAsset('brick' + row % BRICK_COLORS.length, {
anchorX: 0.5,
anchorY: 0.5
});
// Position brick
brick.x = startX + col * (brickWidth + padding);
brick.y = startY + row * (brickHeight + padding);
// Color based on row
brick.setColor(BRICK_COLORS[row % BRICK_COLORS.length]);
// Higher levels have stronger bricks
if (level > 2 && row < 2) {
brick.hitPoints = 2;
}
game.addChild(brick);
bricks.push(brick);
}
}
}
function startGame() {
if (gameState === 'ready') {
gameState = 'playing';
instructionTxt.visible = false;
// Launch the first ball
if (balls.length > 0) {
balls[0].launch();
}
}
}
function checkCollisions() {
// Ball-paddle collision
for (var i = 0; i < balls.length; i++) {
var ball = balls[i];
if (ball.active) {
// Check for 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 && ball.directionY > 0) {
// Only bounce if moving downward
ball.y = paddle.y - paddle.height / 2 - ball.radius;
ball.bounceOff(paddle);
}
// Check for brick collisions
for (var j = bricks.length - 1; j >= 0; 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 which side of the brick was hit
var dx = ball.x - brick.x;
var dy = ball.y - brick.y;
var width = brick.width / 2 + ball.radius;
var height = brick.height / 2 + ball.radius;
var crossWidth = width * dy;
var crossHeight = height * dx;
if (Math.abs(dx) <= width && Math.abs(dy) <= height) {
if (crossWidth > crossHeight) {
if (crossWidth > -crossHeight) {
// Bottom collision
ball.directionY = Math.abs(ball.directionY);
} else {
// Left collision
ball.directionX = -Math.abs(ball.directionX);
}
} else {
if (crossWidth > -crossHeight) {
// Right collision
ball.directionX = Math.abs(ball.directionX);
} else {
// Top collision
ball.directionY = -Math.abs(ball.directionY);
}
}
// Hit the brick
if (brick.hit()) {
// Brick destroyed
LK.setScore(LK.getScore() + 10);
scoreTxt.setText(LK.getScore());
// Check if score is a multiple of 200
if (LK.getScore() % 200 === 0) {
lives++; // Increase lives by 1
livesTxt.setText('Lives: ' + lives); // Update lives display
}
// Chance to spawn powerup
if (Math.random() < brick.powerupChance) {
spawnPowerup(brick.x, brick.y);
}
bricks.splice(j, 1);
brick.destroy();
}
break; // Only hit one brick per frame
}
}
}
// Check for bullet-brick collisions
for (var b = bullets.length - 1; b >= 0; b--) {
var bullet = bullets[b];
for (var j = bricks.length - 1; j >= 0; j--) {
var brick = bricks[j];
if (bullet.x + bullet.width / 2 > brick.x - brick.width / 2 && bullet.x - bullet.width / 2 < brick.x + brick.width / 2 && bullet.y + bullet.height / 2 > brick.y - brick.height / 2 && bullet.y - bullet.height / 2 < brick.y + brick.height / 2) {
// Hit the brick
if (brick.hit()) {
LK.getSound('hit').play(); // Play sound when bullet hits a brick
// Brick destroyed
LK.setScore(LK.getScore() + 10);
scoreTxt.setText(LK.getScore());
// Chance to spawn powerup from bullet-destroyed brick
if (Math.random() < brick.powerupChance) {
spawnPowerup(brick.x, brick.y);
}
bricks.splice(j, 1);
brick.destroy();
}
// Destroy the bullet
bullets.splice(b, 1);
bullet.destroy();
break;
}
}
}
// Check if ball is below screen
if (ball.y - ball.radius > 2732) {
// Reset the ball position to the paddle
lives--; // Decrease lives count
livesTxt.setText('Lives: ' + lives); // Update lives display
if (lives > 0) {
ball.reset(paddle);
ball.launch();
LK.getSound('fall').play();
} else {
gameOver(); // Trigger game over if no lives left
}
}
}
}
// Powerup-paddle collision
for (var k = powerups.length - 1; k >= 0; k--) {
var powerup = powerups[k];
if (powerup.y - 20 > 2732) {
// Powerup fell off screen
game.removeChild(powerup);
powerups.splice(k, 1);
} else 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) {
// Collected powerup
applyPowerup(powerup.type);
LK.getSound('powerup').play();
game.removeChild(powerup);
powerups.splice(k, 1);
}
}
// Check if all bricks are gone
if (bricks.length === 0 && gameState === 'playing') {
levelComplete();
}
}
function spawnPowerup(x, y) {
var powerup = new PowerUp();
powerup.x = x;
powerup.y = y;
// Random powerup type
var types = ['expand', 'multiball', 'slow', 'grow', 'laser', 'freeze', 'shrink', 'invert'];
var randomType = types[Math.floor(Math.random() * types.length)];
powerup.setType(randomType);
game.addChild(powerup);
powerups.push(powerup);
}
function applyPowerup(type) {
switch (type) {
case 'expand':
paddle.expand();
break;
case 'multiball':
// Create two new balls
for (var i = 0; i < 2; i++) {
var newBall = createBall();
newBall.x = balls[0].x;
newBall.y = balls[0].y;
// Random direction
var angle = Math.random() * 360 * Math.PI / 180;
newBall.directionX = Math.sin(angle);
newBall.directionY = Math.cos(angle);
if (newBall.directionY > 0) {
newBall.directionY = -newBall.directionY; // Make sure ball goes upward
}
newBall.active = true;
}
break;
case 'grow':
for (var i = 0; i < balls.length; i++) {
tween(balls[i], {
scaleX: 2.0,
scaleY: 2.0
}, {
duration: 300,
easing: tween.easeOut
});
balls[i].radius *= 2.0;
}
LK.setTimeout(function () {
for (var i = 0; i < balls.length; i++) {
tween(balls[i], {
scaleX: 1,
scaleY: 1
}, {
duration: 300,
easing: tween.easeOut
});
balls[i].radius /= 2.0;
}
}, 10000);
break;
case 'slow':
// Slow down all balls for 10 seconds
for (var j = 0; j < balls.length; j++) {
balls[j].speed = 5;
}
LK.setTimeout(function () {
for (var j = 0; j < balls.length; j++) {
balls[j].speed = 10;
}
}, 10000);
break;
case 'laser':
// Implement laser effect: allow the paddle to shoot lasers for a short duration
// Example: Change paddle color to indicate laser mode
paddle.setTint(0xFF0000); // Change paddle color to red
LK.getSound('laser').play(); // Play laser sound when laser powerup is activated
var laserInterval = LK.setInterval(function () {
var laser = new Bullet();
laser.x = paddle.x;
laser.y = paddle.y - paddle.height / 2;
game.addChild(laser);
bullets.push(laser);
LK.getSound('laser').play(); // Play laser sound on each shot
}, 500); // Fire laser every 500ms
LK.setTimeout(function () {
LK.clearInterval(laserInterval);
paddle.setTint(0xFFFFFF); // Revert paddle color after laser duration
}, 10000); // Laser mode lasts for 10 seconds
break;
case 'freeze':
// Freeze all balls for 5 seconds
for (var j = 0; j < balls.length; j++) {
balls[j].speed = 0;
}
LK.setTimeout(function () {
for (var j = 0; j < balls.length; j++) {
balls[j].speed = 10; // Restore original speed
}
}, 5000); // Freeze duration
break;
case 'shrink':
// Shrink all balls for 10 seconds
for (var i = 0; i < balls.length; i++) {
tween(balls[i], {
scaleX: 0.5,
scaleY: 0.5
}, {
duration: 300,
easing: tween.easeOut
});
balls[i].radius *= 0.5;
}
LK.setTimeout(function () {
for (var i = 0; i < balls.length; i++) {
tween(balls[i], {
scaleX: 1,
scaleY: 1
}, {
duration: 300,
easing: tween.easeOut
});
balls[i].radius /= 0.5;
}
}, 10000);
break;
case 'invert':
// Invert the direction of all balls for 5 seconds
for (var j = 0; j < balls.length; j++) {
balls[j].directionX = -balls[j].directionX;
balls[j].directionY = -balls[j].directionY;
}
LK.setTimeout(function () {
for (var j = 0; j < balls.length; j++) {
balls[j].directionX = -balls[j].directionX;
balls[j].directionY = -balls[j].directionY;
}
}, 5000); // Invert duration
break;
}
}
function levelComplete() {
// Increment level
level++;
// Clear powerups
for (var i = powerups.length - 1; i >= 0; i--) {
game.removeChild(powerups[i]);
}
powerups = [];
// Reset balls
for (var j = balls.length - 1; j >= 0; j--) {
game.removeChild(balls[j]);
}
balls = [];
// Create new ball
createBall();
// Create new level
createLevel(level);
// Show level message
instructionTxt.setText('Level ' + level + ' - Tap to start');
instructionTxt.visible = true;
// Reset game state
gameState = 'ready';
}
function gameOver() {
gameState = 'gameover';
// Clear powerups
for (var i = powerups.length - 1; i >= 0; i--) {
game.removeChild(powerups[i]);
}
powerups = [];
// Show game over
LK.effects.flashScreen(0xFF0000, 1000);
LK.showGameOver();
}
// Event handlers
function handleMove(x, y, obj) {
if (dragNode && gameState === 'playing') {
dragNode.x = Math.max(paddle.width / 2, Math.min(2048 - paddle.width / 2, x));
// If ball is attached to paddle, move it too
for (var i = 0; i < balls.length; i++) {
if (!balls[i].active) {
balls[i].x = dragNode.x;
}
}
}
}
game.down = function (x, y, obj) {
if (gameState === 'ready') {
startGame();
}
dragNode = paddle;
handleMove(x, y, obj);
};
game.up = function (x, y, obj) {
dragNode = null;
};
game.move = handleMove;
game.update = function () {
// Update balls
for (var i = 0; i < balls.length; i++) {
balls[i].update();
}
// Update bullets
for (var k = bullets.length - 1; k >= 0; k--) {
var bullet = bullets[k];
bullet.update();
// Remove bullet if it goes off-screen
if (bullet.y < 0) {
bullets.splice(k, 1);
bullet.destroy();
}
}
// Update powerups
for (var j = 0; j < powerups.length; j++) {
powerups[j].update();
}
// Check collisions
if (gameState === 'playing') {
checkCollisions();
}
};
// Initialize the game
initGame();