User prompt
Add the all power-ups in the game
User prompt
Add new 10 power-ups
User prompt
But background is a new asset
User prompt
Add a background
User prompt
Make the all levels diffrent
User prompt
Make the all levels different and use two different block1 and block2 use it in every levels
User prompt
I don’t want blocklnd remove the blocklnd and add new two block there are normal blocks
User prompt
The blokınd are normal blocks too make them block no in the blocks
User prompt
Please fix the bug: 'Timeout.tick error: Can't find variable: levelText' in or related to this line: 'if (!levelText) {' Line Number: 576
User prompt
Make 30 level
User prompt
Add the blocklnd in the game
User prompt
Add the blocklnd to And have less space between the right and left of the screen
User prompt
Reduce the blocks
User prompt
If all breakable blocks are broken, let a new different blocks come with milk animation, of course, the game should not end.
User prompt
Make the hearts circle like the first heart circle the 2 circle the 3 circle but do these asset make a new asset
User prompt
Make the hearts circle like the first heart circle the 2 circle the 3 circle but do these asset make a new asset
Code edit (1 edits merged)
Please save this source code
User prompt
Block Breaker Blitz
Initial prompt
🧱 Block Breaker Game – English Description What is it? Block breaker games are classic arcade games where you bounce a ball with a paddle to destroy blocks arranged at the top of the screen. How to Play? The player controls a paddle at the bottom of the screen that can move left and right. A ball bounces off the paddle and hits the blocks. When the ball hits a block, the block disappears. The goal is to destroy all the blocks on the screen. If the ball falls below the paddle, you lose a life. Extra Features: Some blocks drop power-ups, such as: Multi-ball Paddle extension Laser shooting Some blocks may be indestructible or take multiple hits.
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Ball class var Ball = Container.expand(function () { var self = Container.call(this); var ballGfx = self.attachAsset('ball', { anchorX: 0.5, anchorY: 0.5 }); self.radius = ballGfx.width / 2; self.vx = 0; self.vy = 0; self.speed = 22; self.stuck = true; // Ball stuck to paddle at start self.update = function () { if (self.stuck) { // Ball follows paddle self.x = paddle.x; self.y = paddle.y - paddle.height / 2 - self.radius; return; } self.x += self.vx; self.y += self.vy; }; return self; }); // Block class var Block = Container.expand(function () { var self = Container.call(this); self.type = 1; // 1: normal, 2: strong, 3: indestructible self.hits = 1; self.destroyed = false; self.powerup = false; self.setType = function (type) { self.type = type; if (type === 1) { self.attachAsset('block1', { anchorX: 0.5, anchorY: 0.5 }); self.hits = 1; } else if (type === 2) { self.attachAsset('block2', { anchorX: 0.5, anchorY: 0.5 }); self.hits = 2; } else if (type === 3) { self.attachAsset('blockInd', { anchorX: 0.5, anchorY: 0.5 }); self.hits = 9999; } }; return self; }); // HeartCircle class for animated heart lives var HeartCircle = Container.expand(function () { var self = Container.call(this); var heartGfx = self.attachAsset('heartCircle', { anchorX: 0.5, anchorY: 0.5 }); self.radius = 60; self.angle = 0; self.setPosition = function (cx, cy, r, angle) { self.x = cx + r * Math.cos(angle); self.y = cy + r * Math.sin(angle); }; self.setScale = function (scale) { self.scaleX = self.scaleY = scale; }; return self; }); // Paddle class var Paddle = Container.expand(function () { var self = Container.call(this); var paddleGfx = self.attachAsset('paddle', { anchorX: 0.5, anchorY: 0.5 }); self.width = paddleGfx.width; self.height = paddleGfx.height; self.extended = false; self.extensionTimeout = null; self.extend = function () { if (self.extended) return; self.extended = true; tween(self, { scaleX: 1.7 }, { duration: 300, easing: tween.easeOut }); if (self.extensionTimeout) LK.clearTimeout(self.extensionTimeout); self.extensionTimeout = LK.setTimeout(function () { tween(self, { scaleX: 1 }, { duration: 300, easing: tween.easeIn }); self.extended = false; }, 7000); }; return self; }); // Powerup class var Powerup = Container.expand(function () { var self = Container.call(this); var gfx = self.attachAsset('powerup', { anchorX: 0.5, anchorY: 0.5 }); // 10 powerup types var types = ['extend', // 0: Extend paddle 'shrink', // 1: Shrink paddle 'multi', // 2: Multi-ball 'slow', // 3: Slow ball 'fast', // 4: Fast ball 'catch', // 5: Sticky paddle 'life', // 6: Extra life 'bigball', // 7: Big ball 'smallball', // 8: Small ball 'score' // 9: Bonus score ]; // Randomly assign type if not set self.type = types[Math.floor(Math.random() * types.length)]; self.vy = 10; self.update = function () { self.y += self.vy; }; // Set color based on type for visual feedback var colorMap = { 'extend': 0x44ff44, 'shrink': 0xff4444, 'multi': 0x44aaff, 'slow': 0x8888ff, 'fast': 0xffaa00, 'catch': 0xff00ff, 'life': 0xffe066, 'bigball': 0x00ffff, 'smallball': 0x888888, 'score': 0xffffff }; if (gfx) gfx.tint = colorMap[self.type] || 0x44ff44; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x181830 }); /**** * Game Code ****/ // background asset // Game constants // Ball: white circle // Paddle: blue rectangle // Block: colored rectangle (normal) // Block: strong (2 hits) // Block: indestructible // Powerup: green circle var GAME_W = 2048; var GAME_H = 2732; // Add background image to the game scene var background = LK.getAsset('background', { anchorX: 0.5, anchorY: 0.5, x: GAME_W / 2, y: GAME_H / 2, scaleX: GAME_W / LK.getAsset('background', {}).width, scaleY: GAME_H / LK.getAsset('background', {}).height, alpha: 1 }); game.addChildAt(background, 0); var BLOCK_ROWS = 4; var BLOCK_COLS = 6; var BLOCK_MARGIN_X = 8; var BLOCK_MARGIN_Y = 24; var BLOCK_START_Y = 320; var LIVES_START = 3; // Game state var paddle, ball; var blocks = []; var powerups = []; var lives = LIVES_START; var score = 0; var balls = []; var isLaunching = false; var lastTouchX = GAME_W / 2; var gameOver = false; // GUI var scoreTxt = new Text2('0', { size: 100, fill: "#fff" }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // Level text for displaying current level (used in level transitions) var levelText = null; // Heart circle lives var heartCircles = []; var heartCircleCenter = { x: GAME_W - 220, y: 120 }; var heartCircleRadius = 90; var heartCircleScale = 1.0; // Helper: update GUI function updateScore() { scoreTxt.setText(score); } function updateLives() { // Remove old hearts for (var i = 0; i < heartCircles.length; ++i) { heartCircles[i].destroy(); } heartCircles = []; // Place hearts in a circle, one after another var total = lives; var baseAngle = -Math.PI / 2; for (var i = 0; i < total; ++i) { var hc = new HeartCircle(); var angle = baseAngle + i * (Math.PI * 2) / Math.max(3, total); hc.setPosition(heartCircleCenter.x, heartCircleCenter.y, heartCircleRadius, angle); hc.setScale(1.0); LK.gui.topRight.addChild(hc); heartCircles.push(hc); } } // Helper: reset game state function resetGame() { // Remove old blocks for (var i = 0; i < blocks.length; ++i) blocks[i].destroy(); blocks = []; // Remove old balls for (var i = 0; i < balls.length; ++i) balls[i].destroy(); balls = []; // Remove powerups for (var i = 0; i < powerups.length; ++i) powerups[i].destroy(); powerups = []; // Reset score/lives score = 0; lives = LIVES_START; updateScore(); updateLives(); gameOver = false; // Create blocks var blockW = LK.getAsset('block1', {}).width; var blockH = LK.getAsset('block1', {}).height; var blockIndW = LK.getAsset('blockInd', {}).width; var blockIndH = LK.getAsset('blockInd', {}).height; var totalW = BLOCK_COLS * blockW + (BLOCK_COLS - 1) * BLOCK_MARGIN_X; var startX = (GAME_W - totalW) / 2 + blockW / 2; for (var row = 0; row < BLOCK_ROWS; ++row) { for (var col = 0; col < BLOCK_COLS; ++col) { var b = new Block(); // Use both block1 and block2 in every level, and make each level different // Example: alternate block1/block2, and shift pattern by level for variety var levelPattern = typeof currentLevel !== "undefined" ? currentLevel : 1; if ((row + col + levelPattern) % 2 === 0) { b.setType(1); } else { b.setType(2); } // Always use normal block size/position b.x = startX + col * (blockW + BLOCK_MARGIN_X); b.y = BLOCK_START_Y + row * (blockH + BLOCK_MARGIN_Y); b.width = blockW; b.height = blockH; // 15% chance to drop powerup if (Math.random() < 0.15) { // Assign a random type to the block's powerup property var powerupTypes = ['extend', 'shrink', 'multi', 'slow', 'fast', 'catch', 'life', 'bigball', 'smallball', 'score']; b.powerup = powerupTypes[Math.floor(Math.random() * powerupTypes.length)]; } blocks.push(b); game.addChild(b); } } // Create paddle if (paddle) paddle.destroy(); paddle = new Paddle(); paddle.x = GAME_W / 2; paddle.y = GAME_H - 220; game.addChild(paddle); // Create ball spawnBall(); } // Helper: spawn a new ball (stuck to paddle) function spawnBall() { var b = new Ball(); b.x = paddle.x; b.y = paddle.y - paddle.height / 2 - b.radius; b.stuck = true; b.vx = 0; b.vy = 0; balls.push(b); game.addChild(b); ball = b; } // Helper: launch ball function launchBall() { if (!ball || !ball.stuck) return; var angle = (Math.random() * 0.5 + 0.25) * Math.PI; // 45-135 deg ball.vx = ball.speed * Math.cos(angle); ball.vy = -ball.speed * Math.abs(Math.sin(angle)); ball.stuck = false; } // Helper: check collision (AABB) function rectsIntersect(a, b) { return a.x - a.width / 2 < b.x + b.width / 2 && a.x + a.width / 2 > b.x - b.width / 2 && a.y - a.height / 2 < b.y + b.height / 2 && a.y + a.height / 2 > b.y - b.height / 2; } // Helper: check ball-block collision (circle-rect) function ballBlockCollision(ball, block) { var bx = block.x, by = block.y, bw = block.width, bh = block.height; var cx = ball.x, cy = ball.y, r = ball.radius; // Clamp point var px = Math.max(bx - bw / 2, Math.min(cx, bx + bw / 2)); var py = Math.max(by - bh / 2, Math.min(cy, by + bh / 2)); var dx = cx - px, dy = cy - py; return dx * dx + dy * dy < r * r; } // Helper: reflect ball on block function reflectBall(ball, block) { // Find side of collision var overlapX = block.x - block.width / 2 - (ball.x + ball.radius); var overlapY = block.y - block.height / 2 - (ball.y + ball.radius); var prevX = ball.x - ball.vx, prevY = ball.y - ball.vy; // Check which axis ball came from var fromLeft = prevX < block.x - block.width / 2; var fromRight = prevX > block.x + block.width / 2; var fromTop = prevY < block.y - block.height / 2; var fromBottom = prevY > block.y + block.height / 2; // Simple: invert vy if hit top/bottom, vx if hit left/right if (Math.abs(ball.vx) > Math.abs(ball.vy)) { ball.vx *= -1; } else { ball.vy *= -1; } } // Helper: reflect ball on paddle function reflectBallPaddle(ball, paddle) { // Calculate hit position relative to paddle center (-1 to 1) var rel = (ball.x - paddle.x) / (paddle.width * (paddle.scaleX || 1) / 2); rel = Math.max(-1, Math.min(1, rel)); var angle = rel * Math.PI / 3 + Math.PI / 2; // -60deg to +60deg from vertical var speed = ball.speed; ball.vx = speed * Math.sin(angle); ball.vy = -Math.abs(speed * Math.cos(angle)); } // Helper: spawn powerup function spawnPowerup(x, y, type) { var p = new Powerup(); p.x = x; p.y = y; if (type) p.type = type; powerups.push(p); game.addChild(p); } // Game move handler (drag paddle) game.move = function (x, y, obj) { if (gameOver) return; // Clamp paddle to screen var px = Math.max(paddle.width / 2, Math.min(GAME_W - paddle.width / 2, x)); paddle.x = px; lastTouchX = px; // If ball is stuck, move it too for (var i = 0; i < balls.length; ++i) { if (balls[i].stuck) balls[i].x = px; } }; // Game down handler (launch ball) game.down = function (x, y, obj) { if (gameOver) return; if (ball && ball.stuck) { launchBall(); } }; // Main game update game.update = function () { if (gameOver) return; // Update balls for (var i = balls.length - 1; i >= 0; --i) { var b = balls[i]; b.update(); // Wall collisions if (b.x - b.radius < 0) { b.x = b.radius; b.vx = Math.abs(b.vx); } if (b.x + b.radius > GAME_W) { b.x = GAME_W - b.radius; b.vx = -Math.abs(b.vx); } if (b.y - b.radius < 0) { b.y = b.radius; b.vy = Math.abs(b.vy); } // Paddle collision if (!b.stuck && rectsIntersect(b, paddle)) { reflectBallPaddle(b, paddle); // Nudge ball out of paddle b.y = paddle.y - paddle.height / 2 - b.radius - 2; } // Block collisions for (var j = blocks.length - 1; j >= 0; --j) { var block = blocks[j]; if (block.destroyed) continue; if (ballBlockCollision(b, block)) { // Hit! if (block.type !== 3) { block.hits -= 1; if (block.hits <= 0) { block.destroyed = true; tween(block, { alpha: 0 }, { duration: 200, onFinish: function onFinish() { block.destroy(); } }); blocks.splice(j, 1); score += 100; updateScore(); // Powerup drop if (block.powerup) { spawnPowerup(block.x, block.y, block.powerup); } // Win condition var anyLeft = false; for (var k = 0; k < blocks.length; ++k) { if (blocks[k].type !== 3) { anyLeft = true; break; } } if (!anyLeft) { // Animate all remaining blocks out (milk animation) for (var m = 0; m < blocks.length; ++m) { var milkBlock = blocks[m]; tween(milkBlock, { scaleY: 0.1, alpha: 0 }, { duration: 500, easing: tween.easeIn, onFinish: function (blk) { return function () { blk.destroy(); }; }(milkBlock) }); } blocks = []; // After animation, spawn a new wave of blocks for the next level LK.setTimeout(function () { // Level system: 30 levels, each with unique block patterns if (typeof currentLevel === "undefined") { currentLevel = 1; } else { currentLevel += 1; } if (currentLevel > 30) currentLevel = 30; // Cap at 30 // Helper: get block type for a given level, row, col function getBlockTypeForLevel(level, row, col) { // Level 1-5: simple, more normal, some strong, rare indestructible if (level <= 5) { if (row === 0 && col % 2 === 0) return 2; if (row === 1 && col % 3 === 0) return 2; if (row === 2 && col % 5 === 0) return 3; return 1; } // Level 6-10: more strong, some indestructible if (level <= 10) { if ((row + col) % 4 === 0) return 3; if ((row + col) % 2 === 0) return 2; return 1; } // Level 11-15: checkerboard indestructible, strong if (level <= 15) { if ((row + col) % 2 === 0) return 3; if (row * col % 3 === 0) return 2; return 1; } // Level 16-20: border indestructible, center strong if (level <= 20) { if (row === 0 || row === BLOCK_ROWS - 1 || col === 0 || col === BLOCK_COLS - 1) return 3; if ((row + col) % 2 === 1) return 2; return 1; } // Level 21-25: stripes of indestructible, strong if (level <= 25) { if (col % 2 === 0) return 3; if (row % 2 === 0) return 2; return 1; } // Level 26-30: lots of indestructible, strong in center if (level <= 30) { if ((row === 1 || row === BLOCK_ROWS - 2) && (col === 2 || col === BLOCK_COLS - 3)) return 2; if ((row + col) % 2 === 0) return 3; return 1; } return 1; } // Helper: get powerup chance for a given level function getPowerupChanceForLevel(level) { if (level <= 5) return 0.20; if (level <= 10) return 0.18; if (level <= 15) return 0.16; if (level <= 20) return 0.14; if (level <= 25) return 0.12; return 0.10; } // Show level number (optional: can be removed if not wanted) if (!levelText) { levelText = new Text2('Level ' + currentLevel, { size: 120, fill: "#fff" }); levelText.anchor.set(0.5, 0.5); LK.gui.center.addChild(levelText); } else { levelText.setText('Level ' + currentLevel); levelText.visible = true; } // Hide after 1s LK.setTimeout(function () { if (levelText) levelText.visible = false; }, 1000); // Block placement var blockW = LK.getAsset('block1', {}).width; var blockH = LK.getAsset('block1', {}).height; var blockIndW = LK.getAsset('blockInd', {}).width; var blockIndH = LK.getAsset('blockInd', {}).height; var totalW = BLOCK_COLS * blockW + (BLOCK_COLS - 1) * BLOCK_MARGIN_X; var startX = (GAME_W - totalW) / 2 + blockW / 2; for (var row = 0; row < BLOCK_ROWS; ++row) { for (var col = 0; col < BLOCK_COLS; ++col) { var b = new Block(); // Use both block1 and block2 in every level, and make each level different // Example: alternate block1/block2, and shift pattern by level for variety if ((row + col + currentLevel) % 2 === 0) { b.setType(1); } else { b.setType(2); } // Always use normal block size/position b.x = startX + col * (blockW + BLOCK_MARGIN_X); b.y = BLOCK_START_Y + row * (blockH + BLOCK_MARGIN_Y); b.width = blockW; b.height = blockH; // Powerup chance by level var powerupChance = getPowerupChanceForLevel(currentLevel); if (Math.random() < powerupChance) b.powerup = true; blocks.push(b); game.addChild(b); // Animate in (milk drop) b.scaleY = 0.1; b.alpha = 0; tween(b, { scaleY: 1, alpha: 1 }, { duration: 500, easing: tween.easeOut }); } } }, 550); // Wait for milk animation out return; } } } // Flash block LK.effects.flashObject(block, 0xffffff, 100); // Reflect ball reflectBall(b, block); break; } } // Ball falls below paddle if (b.y - b.radius > GAME_H) { b.destroy(); balls.splice(i, 1); if (balls.length === 0) { lives -= 1; updateLives(); if (lives <= 0) { gameOver = true; LK.effects.flashScreen(0xff0000, 1000); LK.showGameOver(); return; } else { spawnBall(); } } } } // Update powerups for (var i = powerups.length - 1; i >= 0; --i) { var p = powerups[i]; p.update(); // Paddle catch if (rectsIntersect(p, paddle)) { // Powerup effect logic if (p.type === 'extend') { paddle.extend(); } else if (p.type === 'shrink') { // Shrink paddle for 7s if (!paddle.shrunk) { paddle.shrunk = true; tween(paddle, { scaleX: 0.6 }, { duration: 300, easing: tween.easeOut }); if (paddle.shrinkTimeout) LK.clearTimeout(paddle.shrinkTimeout); paddle.shrinkTimeout = LK.setTimeout(function () { tween(paddle, { scaleX: 1 }, { duration: 300, easing: tween.easeIn }); paddle.shrunk = false; }, 7000); } } else if (p.type === 'multi') { // Multi-ball: spawn 2 extra balls for (var mb = 0; mb < 2; ++mb) { var newBall = new Ball(); newBall.x = ball.x; newBall.y = ball.y; newBall.stuck = false; var angle = (Math.random() * 0.5 + 0.25) * Math.PI; newBall.vx = ball.speed * Math.cos(angle) * (Math.random() < 0.5 ? 1 : -1); newBall.vy = -ball.speed * Math.abs(Math.sin(angle)); balls.push(newBall); game.addChild(newBall); } } else if (p.type === 'slow') { // Slow all balls for 7s for (var sb = 0; sb < balls.length; ++sb) { var b = balls[sb]; if (!b.slowed) { b.slowed = true; b.speed = 12; var mag = Math.sqrt(b.vx * b.vx + b.vy * b.vy); if (mag > 0) { b.vx *= 12 / mag; b.vy *= 12 / mag; } } } if (!game.slowTimeout) { game.slowTimeout = LK.setTimeout(function () { for (var sb = 0; sb < balls.length; ++sb) { var b = balls[sb]; if (b.slowed) { b.speed = 22; b.slowed = false; var mag = Math.sqrt(b.vx * b.vx + b.vy * b.vy); if (mag > 0) { b.vx *= 22 / mag; b.vy *= 22 / mag; } } } game.slowTimeout = null; }, 7000); } } else if (p.type === 'fast') { // Speed up all balls for 7s for (var fb = 0; fb < balls.length; ++fb) { var b = balls[fb]; if (!b.fasted) { b.fasted = true; b.speed = 36; var mag = Math.sqrt(b.vx * b.vx + b.vy * b.vy); if (mag > 0) { b.vx *= 36 / mag; b.vy *= 36 / mag; } } } if (!game.fastTimeout) { game.fastTimeout = LK.setTimeout(function () { for (var fb = 0; fb < balls.length; ++fb) { var b = balls[fb]; if (b.fasted) { b.speed = 22; b.fasted = false; var mag = Math.sqrt(b.vx * b.vx + b.vy * b.vy); if (mag > 0) { b.vx *= 22 / mag; b.vy *= 22 / mag; } } } game.fastTimeout = null; }, 7000); } } else if (p.type === 'catch') { // Sticky paddle for 7s paddle.sticky = true; if (paddle.stickyTimeout) LK.clearTimeout(paddle.stickyTimeout); paddle.stickyTimeout = LK.setTimeout(function () { paddle.sticky = false; }, 7000); } else if (p.type === 'life') { // Extra life lives += 1; updateLives(); } else if (p.type === 'bigball') { // Make all balls big for 7s for (var bb = 0; bb < balls.length; ++bb) { var b = balls[bb]; if (!b.bigged) { b.bigged = true; b.scaleX = b.scaleY = 1.5; b.radius = LK.getAsset('ball', {}).width * 0.75; } } if (!game.bigTimeout) { game.bigTimeout = LK.setTimeout(function () { for (var bb = 0; bb < balls.length; ++bb) { var b = balls[bb]; if (b.bigged) { b.scaleX = b.scaleY = 1; b.radius = LK.getAsset('ball', {}).width / 2; b.bigged = false; } } game.bigTimeout = null; }, 7000); } } else if (p.type === 'smallball') { // Make all balls small for 7s for (var sb = 0; sb < balls.length; ++sb) { var b = balls[sb]; if (!b.smalled) { b.smalled = true; b.scaleX = b.scaleY = 0.6; b.radius = LK.getAsset('ball', {}).width * 0.3; } } if (!game.smallTimeout) { game.smallTimeout = LK.setTimeout(function () { for (var sb = 0; sb < balls.length; ++sb) { var b = balls[sb]; if (b.smalled) { b.scaleX = b.scaleY = 1; b.radius = LK.getAsset('ball', {}).width / 2; b.smalled = false; } } game.smallTimeout = null; }, 7000); } } else if (p.type === 'score') { // Bonus score score += 500; updateScore(); } p.destroy(); powerups.splice(i, 1); } else if (p.y - p.height / 2 > GAME_H) { p.destroy(); powerups.splice(i, 1); } } }; // Start game resetGame();
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Ball class
var Ball = Container.expand(function () {
var self = Container.call(this);
var ballGfx = self.attachAsset('ball', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = ballGfx.width / 2;
self.vx = 0;
self.vy = 0;
self.speed = 22;
self.stuck = true; // Ball stuck to paddle at start
self.update = function () {
if (self.stuck) {
// Ball follows paddle
self.x = paddle.x;
self.y = paddle.y - paddle.height / 2 - self.radius;
return;
}
self.x += self.vx;
self.y += self.vy;
};
return self;
});
// Block class
var Block = Container.expand(function () {
var self = Container.call(this);
self.type = 1; // 1: normal, 2: strong, 3: indestructible
self.hits = 1;
self.destroyed = false;
self.powerup = false;
self.setType = function (type) {
self.type = type;
if (type === 1) {
self.attachAsset('block1', {
anchorX: 0.5,
anchorY: 0.5
});
self.hits = 1;
} else if (type === 2) {
self.attachAsset('block2', {
anchorX: 0.5,
anchorY: 0.5
});
self.hits = 2;
} else if (type === 3) {
self.attachAsset('blockInd', {
anchorX: 0.5,
anchorY: 0.5
});
self.hits = 9999;
}
};
return self;
});
// HeartCircle class for animated heart lives
var HeartCircle = Container.expand(function () {
var self = Container.call(this);
var heartGfx = self.attachAsset('heartCircle', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = 60;
self.angle = 0;
self.setPosition = function (cx, cy, r, angle) {
self.x = cx + r * Math.cos(angle);
self.y = cy + r * Math.sin(angle);
};
self.setScale = function (scale) {
self.scaleX = self.scaleY = scale;
};
return self;
});
// Paddle class
var Paddle = Container.expand(function () {
var self = Container.call(this);
var paddleGfx = self.attachAsset('paddle', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = paddleGfx.width;
self.height = paddleGfx.height;
self.extended = false;
self.extensionTimeout = null;
self.extend = function () {
if (self.extended) return;
self.extended = true;
tween(self, {
scaleX: 1.7
}, {
duration: 300,
easing: tween.easeOut
});
if (self.extensionTimeout) LK.clearTimeout(self.extensionTimeout);
self.extensionTimeout = LK.setTimeout(function () {
tween(self, {
scaleX: 1
}, {
duration: 300,
easing: tween.easeIn
});
self.extended = false;
}, 7000);
};
return self;
});
// Powerup class
var Powerup = Container.expand(function () {
var self = Container.call(this);
var gfx = self.attachAsset('powerup', {
anchorX: 0.5,
anchorY: 0.5
});
// 10 powerup types
var types = ['extend',
// 0: Extend paddle
'shrink',
// 1: Shrink paddle
'multi',
// 2: Multi-ball
'slow',
// 3: Slow ball
'fast',
// 4: Fast ball
'catch',
// 5: Sticky paddle
'life',
// 6: Extra life
'bigball',
// 7: Big ball
'smallball',
// 8: Small ball
'score' // 9: Bonus score
];
// Randomly assign type if not set
self.type = types[Math.floor(Math.random() * types.length)];
self.vy = 10;
self.update = function () {
self.y += self.vy;
};
// Set color based on type for visual feedback
var colorMap = {
'extend': 0x44ff44,
'shrink': 0xff4444,
'multi': 0x44aaff,
'slow': 0x8888ff,
'fast': 0xffaa00,
'catch': 0xff00ff,
'life': 0xffe066,
'bigball': 0x00ffff,
'smallball': 0x888888,
'score': 0xffffff
};
if (gfx) gfx.tint = colorMap[self.type] || 0x44ff44;
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x181830
});
/****
* Game Code
****/
// background asset
// Game constants
// Ball: white circle
// Paddle: blue rectangle
// Block: colored rectangle (normal)
// Block: strong (2 hits)
// Block: indestructible
// Powerup: green circle
var GAME_W = 2048;
var GAME_H = 2732;
// Add background image to the game scene
var background = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
x: GAME_W / 2,
y: GAME_H / 2,
scaleX: GAME_W / LK.getAsset('background', {}).width,
scaleY: GAME_H / LK.getAsset('background', {}).height,
alpha: 1
});
game.addChildAt(background, 0);
var BLOCK_ROWS = 4;
var BLOCK_COLS = 6;
var BLOCK_MARGIN_X = 8;
var BLOCK_MARGIN_Y = 24;
var BLOCK_START_Y = 320;
var LIVES_START = 3;
// Game state
var paddle, ball;
var blocks = [];
var powerups = [];
var lives = LIVES_START;
var score = 0;
var balls = [];
var isLaunching = false;
var lastTouchX = GAME_W / 2;
var gameOver = false;
// GUI
var scoreTxt = new Text2('0', {
size: 100,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Level text for displaying current level (used in level transitions)
var levelText = null;
// Heart circle lives
var heartCircles = [];
var heartCircleCenter = {
x: GAME_W - 220,
y: 120
};
var heartCircleRadius = 90;
var heartCircleScale = 1.0;
// Helper: update GUI
function updateScore() {
scoreTxt.setText(score);
}
function updateLives() {
// Remove old hearts
for (var i = 0; i < heartCircles.length; ++i) {
heartCircles[i].destroy();
}
heartCircles = [];
// Place hearts in a circle, one after another
var total = lives;
var baseAngle = -Math.PI / 2;
for (var i = 0; i < total; ++i) {
var hc = new HeartCircle();
var angle = baseAngle + i * (Math.PI * 2) / Math.max(3, total);
hc.setPosition(heartCircleCenter.x, heartCircleCenter.y, heartCircleRadius, angle);
hc.setScale(1.0);
LK.gui.topRight.addChild(hc);
heartCircles.push(hc);
}
}
// Helper: reset game state
function resetGame() {
// Remove old blocks
for (var i = 0; i < blocks.length; ++i) blocks[i].destroy();
blocks = [];
// Remove old balls
for (var i = 0; i < balls.length; ++i) balls[i].destroy();
balls = [];
// Remove powerups
for (var i = 0; i < powerups.length; ++i) powerups[i].destroy();
powerups = [];
// Reset score/lives
score = 0;
lives = LIVES_START;
updateScore();
updateLives();
gameOver = false;
// Create blocks
var blockW = LK.getAsset('block1', {}).width;
var blockH = LK.getAsset('block1', {}).height;
var blockIndW = LK.getAsset('blockInd', {}).width;
var blockIndH = LK.getAsset('blockInd', {}).height;
var totalW = BLOCK_COLS * blockW + (BLOCK_COLS - 1) * BLOCK_MARGIN_X;
var startX = (GAME_W - totalW) / 2 + blockW / 2;
for (var row = 0; row < BLOCK_ROWS; ++row) {
for (var col = 0; col < BLOCK_COLS; ++col) {
var b = new Block();
// Use both block1 and block2 in every level, and make each level different
// Example: alternate block1/block2, and shift pattern by level for variety
var levelPattern = typeof currentLevel !== "undefined" ? currentLevel : 1;
if ((row + col + levelPattern) % 2 === 0) {
b.setType(1);
} else {
b.setType(2);
}
// Always use normal block size/position
b.x = startX + col * (blockW + BLOCK_MARGIN_X);
b.y = BLOCK_START_Y + row * (blockH + BLOCK_MARGIN_Y);
b.width = blockW;
b.height = blockH;
// 15% chance to drop powerup
if (Math.random() < 0.15) {
// Assign a random type to the block's powerup property
var powerupTypes = ['extend', 'shrink', 'multi', 'slow', 'fast', 'catch', 'life', 'bigball', 'smallball', 'score'];
b.powerup = powerupTypes[Math.floor(Math.random() * powerupTypes.length)];
}
blocks.push(b);
game.addChild(b);
}
}
// Create paddle
if (paddle) paddle.destroy();
paddle = new Paddle();
paddle.x = GAME_W / 2;
paddle.y = GAME_H - 220;
game.addChild(paddle);
// Create ball
spawnBall();
}
// Helper: spawn a new ball (stuck to paddle)
function spawnBall() {
var b = new Ball();
b.x = paddle.x;
b.y = paddle.y - paddle.height / 2 - b.radius;
b.stuck = true;
b.vx = 0;
b.vy = 0;
balls.push(b);
game.addChild(b);
ball = b;
}
// Helper: launch ball
function launchBall() {
if (!ball || !ball.stuck) return;
var angle = (Math.random() * 0.5 + 0.25) * Math.PI; // 45-135 deg
ball.vx = ball.speed * Math.cos(angle);
ball.vy = -ball.speed * Math.abs(Math.sin(angle));
ball.stuck = false;
}
// Helper: check collision (AABB)
function rectsIntersect(a, b) {
return a.x - a.width / 2 < b.x + b.width / 2 && a.x + a.width / 2 > b.x - b.width / 2 && a.y - a.height / 2 < b.y + b.height / 2 && a.y + a.height / 2 > b.y - b.height / 2;
}
// Helper: check ball-block collision (circle-rect)
function ballBlockCollision(ball, block) {
var bx = block.x,
by = block.y,
bw = block.width,
bh = block.height;
var cx = ball.x,
cy = ball.y,
r = ball.radius;
// Clamp point
var px = Math.max(bx - bw / 2, Math.min(cx, bx + bw / 2));
var py = Math.max(by - bh / 2, Math.min(cy, by + bh / 2));
var dx = cx - px,
dy = cy - py;
return dx * dx + dy * dy < r * r;
}
// Helper: reflect ball on block
function reflectBall(ball, block) {
// Find side of collision
var overlapX = block.x - block.width / 2 - (ball.x + ball.radius);
var overlapY = block.y - block.height / 2 - (ball.y + ball.radius);
var prevX = ball.x - ball.vx,
prevY = ball.y - ball.vy;
// Check which axis ball came from
var fromLeft = prevX < block.x - block.width / 2;
var fromRight = prevX > block.x + block.width / 2;
var fromTop = prevY < block.y - block.height / 2;
var fromBottom = prevY > block.y + block.height / 2;
// Simple: invert vy if hit top/bottom, vx if hit left/right
if (Math.abs(ball.vx) > Math.abs(ball.vy)) {
ball.vx *= -1;
} else {
ball.vy *= -1;
}
}
// Helper: reflect ball on paddle
function reflectBallPaddle(ball, paddle) {
// Calculate hit position relative to paddle center (-1 to 1)
var rel = (ball.x - paddle.x) / (paddle.width * (paddle.scaleX || 1) / 2);
rel = Math.max(-1, Math.min(1, rel));
var angle = rel * Math.PI / 3 + Math.PI / 2; // -60deg to +60deg from vertical
var speed = ball.speed;
ball.vx = speed * Math.sin(angle);
ball.vy = -Math.abs(speed * Math.cos(angle));
}
// Helper: spawn powerup
function spawnPowerup(x, y, type) {
var p = new Powerup();
p.x = x;
p.y = y;
if (type) p.type = type;
powerups.push(p);
game.addChild(p);
}
// Game move handler (drag paddle)
game.move = function (x, y, obj) {
if (gameOver) return;
// Clamp paddle to screen
var px = Math.max(paddle.width / 2, Math.min(GAME_W - paddle.width / 2, x));
paddle.x = px;
lastTouchX = px;
// If ball is stuck, move it too
for (var i = 0; i < balls.length; ++i) {
if (balls[i].stuck) balls[i].x = px;
}
};
// Game down handler (launch ball)
game.down = function (x, y, obj) {
if (gameOver) return;
if (ball && ball.stuck) {
launchBall();
}
};
// Main game update
game.update = function () {
if (gameOver) return;
// Update balls
for (var i = balls.length - 1; i >= 0; --i) {
var b = balls[i];
b.update();
// Wall collisions
if (b.x - b.radius < 0) {
b.x = b.radius;
b.vx = Math.abs(b.vx);
}
if (b.x + b.radius > GAME_W) {
b.x = GAME_W - b.radius;
b.vx = -Math.abs(b.vx);
}
if (b.y - b.radius < 0) {
b.y = b.radius;
b.vy = Math.abs(b.vy);
}
// Paddle collision
if (!b.stuck && rectsIntersect(b, paddle)) {
reflectBallPaddle(b, paddle);
// Nudge ball out of paddle
b.y = paddle.y - paddle.height / 2 - b.radius - 2;
}
// Block collisions
for (var j = blocks.length - 1; j >= 0; --j) {
var block = blocks[j];
if (block.destroyed) continue;
if (ballBlockCollision(b, block)) {
// Hit!
if (block.type !== 3) {
block.hits -= 1;
if (block.hits <= 0) {
block.destroyed = true;
tween(block, {
alpha: 0
}, {
duration: 200,
onFinish: function onFinish() {
block.destroy();
}
});
blocks.splice(j, 1);
score += 100;
updateScore();
// Powerup drop
if (block.powerup) {
spawnPowerup(block.x, block.y, block.powerup);
}
// Win condition
var anyLeft = false;
for (var k = 0; k < blocks.length; ++k) {
if (blocks[k].type !== 3) {
anyLeft = true;
break;
}
}
if (!anyLeft) {
// Animate all remaining blocks out (milk animation)
for (var m = 0; m < blocks.length; ++m) {
var milkBlock = blocks[m];
tween(milkBlock, {
scaleY: 0.1,
alpha: 0
}, {
duration: 500,
easing: tween.easeIn,
onFinish: function (blk) {
return function () {
blk.destroy();
};
}(milkBlock)
});
}
blocks = [];
// After animation, spawn a new wave of blocks for the next level
LK.setTimeout(function () {
// Level system: 30 levels, each with unique block patterns
if (typeof currentLevel === "undefined") {
currentLevel = 1;
} else {
currentLevel += 1;
}
if (currentLevel > 30) currentLevel = 30; // Cap at 30
// Helper: get block type for a given level, row, col
function getBlockTypeForLevel(level, row, col) {
// Level 1-5: simple, more normal, some strong, rare indestructible
if (level <= 5) {
if (row === 0 && col % 2 === 0) return 2;
if (row === 1 && col % 3 === 0) return 2;
if (row === 2 && col % 5 === 0) return 3;
return 1;
}
// Level 6-10: more strong, some indestructible
if (level <= 10) {
if ((row + col) % 4 === 0) return 3;
if ((row + col) % 2 === 0) return 2;
return 1;
}
// Level 11-15: checkerboard indestructible, strong
if (level <= 15) {
if ((row + col) % 2 === 0) return 3;
if (row * col % 3 === 0) return 2;
return 1;
}
// Level 16-20: border indestructible, center strong
if (level <= 20) {
if (row === 0 || row === BLOCK_ROWS - 1 || col === 0 || col === BLOCK_COLS - 1) return 3;
if ((row + col) % 2 === 1) return 2;
return 1;
}
// Level 21-25: stripes of indestructible, strong
if (level <= 25) {
if (col % 2 === 0) return 3;
if (row % 2 === 0) return 2;
return 1;
}
// Level 26-30: lots of indestructible, strong in center
if (level <= 30) {
if ((row === 1 || row === BLOCK_ROWS - 2) && (col === 2 || col === BLOCK_COLS - 3)) return 2;
if ((row + col) % 2 === 0) return 3;
return 1;
}
return 1;
}
// Helper: get powerup chance for a given level
function getPowerupChanceForLevel(level) {
if (level <= 5) return 0.20;
if (level <= 10) return 0.18;
if (level <= 15) return 0.16;
if (level <= 20) return 0.14;
if (level <= 25) return 0.12;
return 0.10;
}
// Show level number (optional: can be removed if not wanted)
if (!levelText) {
levelText = new Text2('Level ' + currentLevel, {
size: 120,
fill: "#fff"
});
levelText.anchor.set(0.5, 0.5);
LK.gui.center.addChild(levelText);
} else {
levelText.setText('Level ' + currentLevel);
levelText.visible = true;
}
// Hide after 1s
LK.setTimeout(function () {
if (levelText) levelText.visible = false;
}, 1000);
// Block placement
var blockW = LK.getAsset('block1', {}).width;
var blockH = LK.getAsset('block1', {}).height;
var blockIndW = LK.getAsset('blockInd', {}).width;
var blockIndH = LK.getAsset('blockInd', {}).height;
var totalW = BLOCK_COLS * blockW + (BLOCK_COLS - 1) * BLOCK_MARGIN_X;
var startX = (GAME_W - totalW) / 2 + blockW / 2;
for (var row = 0; row < BLOCK_ROWS; ++row) {
for (var col = 0; col < BLOCK_COLS; ++col) {
var b = new Block();
// Use both block1 and block2 in every level, and make each level different
// Example: alternate block1/block2, and shift pattern by level for variety
if ((row + col + currentLevel) % 2 === 0) {
b.setType(1);
} else {
b.setType(2);
}
// Always use normal block size/position
b.x = startX + col * (blockW + BLOCK_MARGIN_X);
b.y = BLOCK_START_Y + row * (blockH + BLOCK_MARGIN_Y);
b.width = blockW;
b.height = blockH;
// Powerup chance by level
var powerupChance = getPowerupChanceForLevel(currentLevel);
if (Math.random() < powerupChance) b.powerup = true;
blocks.push(b);
game.addChild(b);
// Animate in (milk drop)
b.scaleY = 0.1;
b.alpha = 0;
tween(b, {
scaleY: 1,
alpha: 1
}, {
duration: 500,
easing: tween.easeOut
});
}
}
}, 550); // Wait for milk animation out
return;
}
}
}
// Flash block
LK.effects.flashObject(block, 0xffffff, 100);
// Reflect ball
reflectBall(b, block);
break;
}
}
// Ball falls below paddle
if (b.y - b.radius > GAME_H) {
b.destroy();
balls.splice(i, 1);
if (balls.length === 0) {
lives -= 1;
updateLives();
if (lives <= 0) {
gameOver = true;
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
return;
} else {
spawnBall();
}
}
}
}
// Update powerups
for (var i = powerups.length - 1; i >= 0; --i) {
var p = powerups[i];
p.update();
// Paddle catch
if (rectsIntersect(p, paddle)) {
// Powerup effect logic
if (p.type === 'extend') {
paddle.extend();
} else if (p.type === 'shrink') {
// Shrink paddle for 7s
if (!paddle.shrunk) {
paddle.shrunk = true;
tween(paddle, {
scaleX: 0.6
}, {
duration: 300,
easing: tween.easeOut
});
if (paddle.shrinkTimeout) LK.clearTimeout(paddle.shrinkTimeout);
paddle.shrinkTimeout = LK.setTimeout(function () {
tween(paddle, {
scaleX: 1
}, {
duration: 300,
easing: tween.easeIn
});
paddle.shrunk = false;
}, 7000);
}
} else if (p.type === 'multi') {
// Multi-ball: spawn 2 extra balls
for (var mb = 0; mb < 2; ++mb) {
var newBall = new Ball();
newBall.x = ball.x;
newBall.y = ball.y;
newBall.stuck = false;
var angle = (Math.random() * 0.5 + 0.25) * Math.PI;
newBall.vx = ball.speed * Math.cos(angle) * (Math.random() < 0.5 ? 1 : -1);
newBall.vy = -ball.speed * Math.abs(Math.sin(angle));
balls.push(newBall);
game.addChild(newBall);
}
} else if (p.type === 'slow') {
// Slow all balls for 7s
for (var sb = 0; sb < balls.length; ++sb) {
var b = balls[sb];
if (!b.slowed) {
b.slowed = true;
b.speed = 12;
var mag = Math.sqrt(b.vx * b.vx + b.vy * b.vy);
if (mag > 0) {
b.vx *= 12 / mag;
b.vy *= 12 / mag;
}
}
}
if (!game.slowTimeout) {
game.slowTimeout = LK.setTimeout(function () {
for (var sb = 0; sb < balls.length; ++sb) {
var b = balls[sb];
if (b.slowed) {
b.speed = 22;
b.slowed = false;
var mag = Math.sqrt(b.vx * b.vx + b.vy * b.vy);
if (mag > 0) {
b.vx *= 22 / mag;
b.vy *= 22 / mag;
}
}
}
game.slowTimeout = null;
}, 7000);
}
} else if (p.type === 'fast') {
// Speed up all balls for 7s
for (var fb = 0; fb < balls.length; ++fb) {
var b = balls[fb];
if (!b.fasted) {
b.fasted = true;
b.speed = 36;
var mag = Math.sqrt(b.vx * b.vx + b.vy * b.vy);
if (mag > 0) {
b.vx *= 36 / mag;
b.vy *= 36 / mag;
}
}
}
if (!game.fastTimeout) {
game.fastTimeout = LK.setTimeout(function () {
for (var fb = 0; fb < balls.length; ++fb) {
var b = balls[fb];
if (b.fasted) {
b.speed = 22;
b.fasted = false;
var mag = Math.sqrt(b.vx * b.vx + b.vy * b.vy);
if (mag > 0) {
b.vx *= 22 / mag;
b.vy *= 22 / mag;
}
}
}
game.fastTimeout = null;
}, 7000);
}
} else if (p.type === 'catch') {
// Sticky paddle for 7s
paddle.sticky = true;
if (paddle.stickyTimeout) LK.clearTimeout(paddle.stickyTimeout);
paddle.stickyTimeout = LK.setTimeout(function () {
paddle.sticky = false;
}, 7000);
} else if (p.type === 'life') {
// Extra life
lives += 1;
updateLives();
} else if (p.type === 'bigball') {
// Make all balls big for 7s
for (var bb = 0; bb < balls.length; ++bb) {
var b = balls[bb];
if (!b.bigged) {
b.bigged = true;
b.scaleX = b.scaleY = 1.5;
b.radius = LK.getAsset('ball', {}).width * 0.75;
}
}
if (!game.bigTimeout) {
game.bigTimeout = LK.setTimeout(function () {
for (var bb = 0; bb < balls.length; ++bb) {
var b = balls[bb];
if (b.bigged) {
b.scaleX = b.scaleY = 1;
b.radius = LK.getAsset('ball', {}).width / 2;
b.bigged = false;
}
}
game.bigTimeout = null;
}, 7000);
}
} else if (p.type === 'smallball') {
// Make all balls small for 7s
for (var sb = 0; sb < balls.length; ++sb) {
var b = balls[sb];
if (!b.smalled) {
b.smalled = true;
b.scaleX = b.scaleY = 0.6;
b.radius = LK.getAsset('ball', {}).width * 0.3;
}
}
if (!game.smallTimeout) {
game.smallTimeout = LK.setTimeout(function () {
for (var sb = 0; sb < balls.length; ++sb) {
var b = balls[sb];
if (b.smalled) {
b.scaleX = b.scaleY = 1;
b.radius = LK.getAsset('ball', {}).width / 2;
b.smalled = false;
}
}
game.smallTimeout = null;
}, 7000);
}
} else if (p.type === 'score') {
// Bonus score
score += 500;
updateScore();
}
p.destroy();
powerups.splice(i, 1);
} else if (p.y - p.height / 2 > GAME_H) {
p.destroy();
powerups.splice(i, 1);
}
}
};
// Start game
resetGame();