User prompt
her oyun başladığında score sıfırlansın
User prompt
bütün toplar 15 kere sağ ve sol duvara çarparsa hiç bir şeye vurmadan bütün toplar yukarı doğru hızlı bir şekilde zıplasın
User prompt
powerupslardan çıkan toplarda 15 kere sağ ve sol duvara vurunca paddle gelsin ve atılabilir olsun
User prompt
bütün levelleri düzelt
User prompt
bazı powerup lar level5 den sonra alınmıyor düzelt hemen
User prompt
bütün toplar 15 kere sağ ve sol duvara vurursa oyuncunun eline gelsin yani paddle ve atılabilir olsun
User prompt
bazı poweruplar paddledan geçiyor paddle almıyor düzelt lütfen bütün powerupları kullanılabilirler kalsın kullanılamayanları sil
User prompt
powerup dan çıkan top da normal top olarak sayılsın
User prompt
yeni levele geçince düşen topları sıfırla
User prompt
görünmezlik powerup ları çalışmıyor lütfen düzelt
User prompt
top bug oldu paddle üstünde kaldı görüntü olarak
User prompt
basılı tutmaya gerek kalmasın mousela birlikte hareket etsin paddle sağa ve sola
User prompt
paddle kontrolü kullanıcı dostu olsun kolay kontrol edilebilsin
User prompt
oyuna giren hiç bir top oyunun sağından solundan ve üstünden çıkamasın
User prompt
bazı güçlendiriciler paddle almıyor içinden geçiyor paddlen düzelt lütfen
User prompt
scoru oyun bitince sıfırla
User prompt
score sıfırlama olmasın
User prompt
powerup lardan çıkan top çoğaltmalarda normal top olarak sayılsın ve normal topun yaptığı herşeyi yapsın
User prompt
eğer ekranda düşen top varsa normal top düşmüşse düşen top la devam etsin
User prompt
eğer düşen toplardan biri ekrandaysa ve bizim normal topu kaybetmişsek can gitmesin
User prompt
düşen toplar tuğlalara hasar verebilsin
User prompt
düşen toplar ekranın dışına yukardan çıkmasın ve tuğlalara vurabilsin yukarı vurunca geri seksin
User prompt
düşen toplar veya çoğaltılan toplar paddle ile vurulunca normal top gibi olsun yukarıdan çıkamasın
User prompt
düşen toplar veya çoğaltılan toplar normal top gibi hareket etsin paddle vurulunca normal top ne yapıyorsa onu yapsınlar
User prompt
düşen toplara ve çoğaltılan toplara paddle ile vurulabilir olsun
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ // Ball class var Ball = Container.expand(function () { var self = Container.call(this); var ballSprite = self.attachAsset('ball', { anchorX: 0.5, anchorY: 0.5 }); self.radius = ballSprite.width / 2; self.vx = 0; self.vy = 0; self.speed = 22; // Initial speed self.sticky = false; // If true, ball sticks to paddle self.launch = function (angle) { // Launch ball at angle (in radians) self.vx = Math.cos(angle) * self.speed; self.vy = Math.sin(angle) * self.speed; self.sticky = false; }; self.update = function () { if (!self.sticky) { self.x += self.vx; self.y += self.vy; // Prevent ball from leaving left, right, or top of the screen if (self.x - self.radius < 0) { self.x = self.radius; self.vx = -self.vx; } if (self.x + self.radius > 2048) { self.x = 2048 - self.radius; self.vx = -self.vx; } if (self.y - self.radius < 0) { self.y = self.radius; self.vy = -self.vy; } } }; return self; }); // Block class var Block = Container.expand(function () { var self = Container.call(this); // color: 'red', 'green', 'blue', 'yellow' self.color = 'red'; self.hp = 1; self.maxHp = 1; self.hpBarBg = null; self.hpBar = null; self.setColor = function (color) { self.color = color; if (self.blockSprite) self.removeChild(self.blockSprite); var assetId = 'block_' + color; self.blockSprite = self.attachAsset(assetId, { anchorX: 0.5, anchorY: 0.5 }); // Remove old HP bars if any if (self.hpBarBg) self.removeChild(self.hpBarBg); if (self.hpBar) self.removeChild(self.hpBar); // Add HP bar background (gray) self.hpBarBg = self.attachAsset('block_red', { anchorX: 0.5, anchorY: 0.5, width: self.blockSprite.width * 0.8, height: 14, y: -self.blockSprite.height / 2 - 18, tint: 0x444444 }); // Add HP bar (green) self.hpBar = self.attachAsset('block_green', { anchorX: 0.5, anchorY: 0.5, width: self.blockSprite.width * 0.78, height: 10, y: -self.blockSprite.height / 2 - 18, tint: 0x44ff44 }); self.hpBarBg.alpha = 0.7; self.hpBar.alpha = 0.9; self.hpBarBg.zIndex = 10; self.hpBar.zIndex = 11; self.hpBarBg.interactive = false; self.hpBar.interactive = false; self.addChild(self.hpBarBg); self.addChild(self.hpBar); self.updateHpBar(); }; self.setHp = function (hp, maxHp) { self.hp = hp; self.maxHp = maxHp || hp; self.updateHpBar(); }; self.updateHpBar = function () { if (!self.hpBar || !self.hpBarBg) return; var ratio = Math.max(0, Math.min(1, self.hp / self.maxHp)); self.hpBar.width = self.blockSprite.width * 0.78 * ratio; // Color changes: green > yellow > red if (ratio > 0.66) { self.hpBar.tint = 0x44ff44; } else if (ratio > 0.33) { self.hpBar.tint = 0xffe066; } else { self.hpBar.tint = 0xff4444; } self.hpBar.visible = self.hp > 0; self.hpBarBg.visible = self.hp > 0; }; self.breakBlock = function () { // Animate block breaking tween(self, { alpha: 0, scaleX: 1.5, scaleY: 1.5 }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { self.destroy(); } }); }; return self; }); // Paddle class var Paddle = Container.expand(function () { var self = Container.call(this); var paddleSprite = self.attachAsset('paddle', { anchorX: 0.5, anchorY: 0.5 }); // Use slightly larger hitbox for collision, but keep visual size for rendering self.width = paddleSprite.width; self.height = paddleSprite.height; // For hitbox, add a small margin to the top and bottom to ensure ball is always caught self.hitboxMarginY = 18; // pixels, adjust as needed for best feel self.getHitbox = function () { return { left: self.x - self.width / 2, right: self.x + self.width / 2, top: self.y - self.height / 2 - self.hitboxMarginY, bottom: self.y + self.height / 2 + self.hitboxMarginY }; }; self.moveTo = function (x) { // Clamp paddle within game bounds (leave 40px margin) var minX = self.width / 2 + 40; var maxX = 2048 - self.width / 2 - 40; self.x = Math.max(minX, Math.min(maxX, x)); }; return self; }); // PowerUp class var PowerUp = Container.expand(function () { var self = Container.call(this); var powerSprite = self.attachAsset('powerup', { anchorX: 0.5, anchorY: 0.5 }); self.type = 'expand'; // 'expand', 'shrink', 'life', 'sticky' self.vy = 10; self.update = function () { self.y += self.vy; }; return self; }); // --- PowerupBall1, PowerupBall2, PowerupBall3: behave like balls but are powerups --- var PowerupBall1 = Container.expand(function () { var self = Container.call(this); var sprite = self.attachAsset('ball', { anchorX: 0.5, anchorY: 0.5, tint: 0xffa500 // orange }); self.radius = sprite.width / 2; self.vx = (Math.random() - 0.5) * 18; self.vy = 14 + Math.random() * 6; self.type = 'powerupball1'; self.update = function () { self.x += self.vx; self.y += self.vy; // Prevent ball from leaving left, right, or top of the screen if (self.x - self.radius < 0) { self.x = self.radius; self.vx = -self.vx; } if (self.x + self.radius > 2048) { self.x = 2048 - self.radius; self.vx = -self.vx; } if (self.y - self.radius < 0) { self.y = self.radius; self.vy = -self.vy; LK.getSound('hit').play(); } // Bounce off paddle if (typeof paddle !== "undefined" && paddle && typeof paddle.getHitbox === "function") { var hitbox = paddle.getHitbox(); if (self.y + self.radius >= hitbox.top && self.y - self.radius <= hitbox.bottom && self.x + self.radius >= hitbox.left && self.x - self.radius <= hitbox.right && self.vy > 0) { // Reflect ball, angle based on where it hit the paddle var rel = (self.x - paddle.x) / (paddle.width / 2 * paddle.scaleX); var angle = -Math.PI / 3 + rel * (Math.PI / 3); // -60deg to 60deg var speed = Math.sqrt(self.vx * self.vx + self.vy * self.vy); self.vx = Math.cos(angle) * speed; self.vy = Math.sin(angle) * speed; self.y = paddle.y - paddle.height / 2 - self.radius - 2; if (self.y - self.radius < 0) { self.y = self.radius; if (self.vy < 0) self.vy = Math.abs(self.vy); } LK.getSound('hit').play(); } } // Bounce off blocks if (typeof blocks !== "undefined" && blocks && blocks.length) { for (var i = 0; i < blocks.length; i++) { var block = blocks[i]; if (block && self.intersects && self.intersects(block)) { var overlapX = Math.abs(self.x - block.x) - (block.blockSprite.width / 2 + self.radius); var overlapY = Math.abs(self.y - block.y) - (block.blockSprite.height / 2 + self.radius); if (overlapX > overlapY) { self.vx = -self.vx; } else { self.vy = -self.vy; } // Damage block like main ball if (typeof block.hp !== "undefined") { block.hp--; if (block.updateHpBar) block.updateHpBar(); if (block.hp <= 0) { block.breakBlock(); blocks.splice(i, 1); i--; // adjust index after removal } } LK.getSound('hit').play(); break; // Only bounce off one block per frame } } } }; return self; }); var PowerupBall2 = Container.expand(function () { var self = Container.call(this); var sprite = self.attachAsset('ball', { anchorX: 0.5, anchorY: 0.5, tint: 0x00e0ff // cyan }); self.radius = sprite.width / 2; self.vx = (Math.random() - 0.5) * 16; self.vy = 12 + Math.random() * 8; self.type = 'powerupball2'; self.update = function () { self.x += self.vx; self.y += self.vy; // Prevent ball from leaving left, right, or top of the screen if (self.x - self.radius < 0) { self.x = self.radius; self.vx = -self.vx; } if (self.x + self.radius > 2048) { self.x = 2048 - self.radius; self.vx = -self.vx; } if (self.y - self.radius < 0) { self.y = self.radius; self.vy = -self.vy; LK.getSound('hit').play(); } // Bounce off paddle if (typeof paddle !== "undefined" && paddle && typeof paddle.getHitbox === "function") { var hitbox = paddle.getHitbox(); if (self.y + self.radius >= hitbox.top && self.y - self.radius <= hitbox.bottom && self.x + self.radius >= hitbox.left && self.x - self.radius <= hitbox.right && self.vy > 0) { // Reflect ball, angle based on where it hit the paddle var rel = (self.x - paddle.x) / (paddle.width / 2 * paddle.scaleX); var angle = -Math.PI / 3 + rel * (Math.PI / 3); // -60deg to 60deg var speed = Math.sqrt(self.vx * self.vx + self.vy * self.vy); self.vx = Math.cos(angle) * speed; self.vy = Math.sin(angle) * speed; self.y = paddle.y - paddle.height / 2 - self.radius - 2; if (self.y - self.radius < 0) { self.y = self.radius; if (self.vy < 0) self.vy = Math.abs(self.vy); } LK.getSound('hit').play(); } } // Bounce off blocks if (typeof blocks !== "undefined" && blocks && blocks.length) { for (var i = 0; i < blocks.length; i++) { var block = blocks[i]; if (block && self.intersects && self.intersects(block)) { var overlapX = Math.abs(self.x - block.x) - (block.blockSprite.width / 2 + self.radius); var overlapY = Math.abs(self.y - block.y) - (block.blockSprite.height / 2 + self.radius); if (overlapX > overlapY) { self.vx = -self.vx; } else { self.vy = -self.vy; } // Damage block like main ball if (typeof block.hp !== "undefined") { block.hp--; if (block.updateHpBar) block.updateHpBar(); if (block.hp <= 0) { block.breakBlock(); blocks.splice(i, 1); i--; // adjust index after removal } } LK.getSound('hit').play(); break; // Only bounce off one block per frame } } } }; return self; }); var PowerupBall3 = Container.expand(function () { var self = Container.call(this); var sprite = self.attachAsset('ball', { anchorX: 0.5, anchorY: 0.5, tint: 0x8dff00 // lime }); self.radius = sprite.width / 2; self.vx = (Math.random() - 0.5) * 20; self.vy = 10 + Math.random() * 10; self.type = 'powerupball3'; self.update = function () { self.x += self.vx; self.y += self.vy; // Prevent ball from leaving left, right, or top of the screen if (self.x - self.radius < 0) { self.x = self.radius; self.vx = -self.vx; } if (self.x + self.radius > 2048) { self.x = 2048 - self.radius; self.vx = -self.vx; } if (self.y - self.radius < 0) { self.y = self.radius; self.vy = -self.vy; LK.getSound('hit').play(); } // Bounce off paddle if (typeof paddle !== "undefined" && paddle && typeof paddle.getHitbox === "function") { var hitbox = paddle.getHitbox(); if (self.y + self.radius >= hitbox.top && self.y - self.radius <= hitbox.bottom && self.x + self.radius >= hitbox.left && self.x - self.radius <= hitbox.right && self.vy > 0) { // Reflect ball, angle based on where it hit the paddle var rel = (self.x - paddle.x) / (paddle.width / 2 * paddle.scaleX); var angle = -Math.PI / 3 + rel * (Math.PI / 3); // -60deg to 60deg var speed = Math.sqrt(self.vx * self.vx + self.vy * self.vy); self.vx = Math.cos(angle) * speed; self.vy = Math.sin(angle) * speed; self.y = paddle.y - paddle.height / 2 - self.radius - 2; if (self.y - self.radius < 0) { self.y = self.radius; if (self.vy < 0) self.vy = Math.abs(self.vy); } LK.getSound('hit').play(); } } // Bounce off blocks if (typeof blocks !== "undefined" && blocks && blocks.length) { for (var i = 0; i < blocks.length; i++) { var block = blocks[i]; if (block && self.intersects && self.intersects(block)) { var overlapX = Math.abs(self.x - block.x) - (block.blockSprite.width / 2 + self.radius); var overlapY = Math.abs(self.y - block.y) - (block.blockSprite.height / 2 + self.radius); if (overlapX > overlapY) { self.vx = -self.vx; } else { self.vy = -self.vy; } // Damage block like main ball if (typeof block.hp !== "undefined") { block.hp--; if (block.updateHpBar) block.updateHpBar(); if (block.hp <= 0) { block.breakBlock(); blocks.splice(i, 1); i--; // adjust index after removal } } LK.getSound('hit').play(); break; // Only bounce off one block per frame } } } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x181c2c }); /**** * Game Code ****/ // Game variables // Ball // Paddle // Block colors // Power-up // Sounds var paddle, ball, blocks = [], powerups = [], powerupBalls = []; // holds PowerupBall1/2/3 instances var lives = 3; var level = 1; var isBallLaunched = false; var dragPaddle = false; // Prevent score from being reset to zero anywhere in the game var score = storage.score !== undefined ? storage.score : 0; var combo = 0; var comboTimer = null; var lastTouchX = 0; var ballStickyToPaddle = true; var levelCleared = false; // High score var highScore = storage.highScore || 0; // GUI var scoreTxt = new Text2('0', { size: 100, fill: "#fff" }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); var highScoreTxt = new Text2('High: ' + highScore, { size: 60, fill: 0xFFE066 }); highScoreTxt.anchor.set(0.5, 0); highScoreTxt.y = 90; LK.gui.top.addChild(highScoreTxt); var livesTxt = new Text2('♥♥♥', { size: 80, fill: 0xFF5E5E }); livesTxt.anchor.set(1, 0); LK.gui.topRight.addChild(livesTxt); var levelTxt = new Text2('Level 1', { size: 70, fill: 0xFFE066 }); levelTxt.anchor.set(0, 0); LK.gui.topLeft.addChild(levelTxt); // Helper: update GUI function updateGUI() { scoreTxt.setText(score); storage.score = score; // Persist score so it is never reset if (score > highScore) { highScore = score; storage.highScore = highScore; } highScoreTxt.setText('High: ' + highScore); var hearts = ''; for (var i = 0; i < lives; i++) hearts += '♥'; livesTxt.setText(hearts); levelTxt.setText('Level ' + level); } // Helper: reset combo function resetCombo() { combo = 0; if (comboTimer) { LK.clearTimeout(comboTimer); comboTimer = null; } } // Helper: start combo timer function startCombo() { if (comboTimer) LK.clearTimeout(comboTimer); comboTimer = LK.setTimeout(function () { resetCombo(); }, 1200); } // Helper: spawn blocks for current level function spawnBlocks() { // Remove old blocks for (var i = 0; i < blocks.length; i++) { blocks[i].destroy(); } blocks = []; // Dynamic block pattern logic var blockW = 200, blockH = 80; var marginX = 40, marginY = 40; var colors = ['red', 'green', 'blue', 'yellow']; // Animal shape patterns for higher levels function getAnimalPattern(level, rows, cols) { // Each pattern is a 2D array of 0/1, 1 means block present // Patterns: horse, donkey, giraffe, etc. // All patterns are 10x12 max, centered var patterns = []; // Horse (at, level 10+) patterns.push([[0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0], [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0], [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0], [0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0], [0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0], [1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1]]); // Donkey (eşek, level 15+) patterns.push([[0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0], [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0], [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0], [0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0], [0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0], [0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0]]); // Giraffe (zürafa, level 20+) patterns.push([[0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0], [0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0], [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0], [0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0], [0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0], [0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0], [0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0], [0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0]]); // Add more animal patterns as needed // Pick pattern based on level if (level >= 20) return patterns[2]; if (level >= 15) return patterns[1]; if (level >= 10) return patterns[0]; return null; } // Increase rows and columns as level increases, up to a max var rows = Math.min(3 + Math.floor(level / 2), 10); var cols = Math.min(5 + Math.floor(level / 3), 12); // For variety, pick a pattern type based on level var patternType = level % 5; // 0: full, 1: checker, 2: pyramid, 3: gaps, 4: zigzag if (level === 1) { patternType = 0; // Force full grid for first level } // Animal pattern override for higher levels var animalPattern = getAnimalPattern(level, rows, cols); if (animalPattern) { // Use animal pattern, override rows/cols rows = animalPattern.length; cols = animalPattern[0].length; patternType = -1; // Custom } var totalW = cols * blockW + (cols - 1) * marginX; var startX = (2048 - totalW) / 2 + blockW / 2; var startY = 300; for (var r = 0; r < rows; r++) { for (var c = 0; c < cols; c++) { var placeBlock = true; // Animal pattern logic if (animalPattern) { placeBlock = animalPattern[r][c] === 1; } else { // Pattern logic if (patternType === 1) { // checker if ((r + c) % 2 !== 0) placeBlock = false; } else if (patternType === 2) { // pyramid var mid = Math.floor(cols / 2); if (c < mid - r || c > mid + r) placeBlock = false; } else if (patternType === 3) { // random gaps if (Math.abs((r * cols + c + level) % 7) === 0) placeBlock = false; } else if (patternType === 4) { // zigzag if (r % 2 === 0 && c % 3 === 0 || r % 2 === 1 && c % 3 === 2) placeBlock = false; } // patternType 0: full grid } if (placeBlock) { var block = new Block(); var color = colors[(r + c + level) % colors.length]; block.setColor(color); block.x = startX + c * (blockW + marginX); block.y = startY + r * (blockH + marginY); // Increase block HP as level increases block.hp = 1 + Math.floor(level / 4) + Math.floor(r / 3); block.maxHp = block.hp; block.setHp(block.hp, block.maxHp); game.addChild(block); blocks.push(block); } } } } // Helper: spawn paddle function spawnPaddle() { if (paddle) paddle.destroy(); paddle = new Paddle(); paddle.x = 2048 / 2; paddle.y = 2732 - 180; game.addChild(paddle); } // Helper: spawn ball function spawnBall() { if (ball) ball.destroy(); ball = new Ball(); ball.x = paddle.x; ball.y = paddle.y - paddle.height / 2 - ball.radius - 10; ball.vx = 0; ball.vy = 0; ball.sticky = true; ballStickyToPaddle = true; isBallLaunched = false; // Reset wall bounce and block hit tracking ball.leftWallHits = 0; ball.rightWallHits = 0; ball.wallBouncesNoBlock = 0; ball.hasHitBlockSinceReset = false; game.addChild(ball); } // Helper: spawn powerup function spawnPowerUp(x, y) { var power = new PowerUp(); // 50 unique powerup types var types = ['expand', 'shrink', 'life', 'sticky', 'multiball', 'slow', 'fast', 'ghost', 'laser', 'bigball', 'smallball', 'superball', 'magnet', 'reverse', 'paddleup', 'paddledown', 'scorex2', 'scorex3', 'blockbomb', 'blockheal', 'blockrowclear', 'blockcolclear', 'blockrandom', 'ballsplit', 'ballclone', 'paddleleft', 'paddleright', 'ballcurve', 'ballzigzag', 'ballrandom', 'blockfreeze', 'blockmove', 'blockshrink', 'blockexpand', 'paddleghost', 'paddlefast', 'paddleslow', 'ballinvisible', 'ballheavy', 'balllight', 'blockinvisible', 'blockshield', 'blockswap', 'blockteleport', 'paddleteleport', 'ballteleport', 'scorebonus', 'scorepenalty', 'blockregen', 'blockexplode']; // Test all powerups, keep only working ones // We'll only keep those that can be implemented with current game logic and engine // Working types: var workingTypes = ['expand', 'shrink', 'life', 'sticky', 'multiball', 'slow', 'fast', 'bigball', 'smallball', 'scorex2', 'scorex3', 'blockbomb', 'blockrowclear', 'blockcolclear', 'ballsplit', 'ballclone', 'paddleup', 'paddledown', 'scorebonus', 'scorepenalty', 'blockheal', 'blockrandom', 'blockfreeze', 'blockmove', 'blockshrink', 'blockexpand', 'paddlefast', 'paddleslow', 'ballheavy', 'balllight', 'blockshield', 'blockswap', 'blockteleport', 'paddleteleport', 'ballteleport', 'blockregen', 'blockexplode', 'magnet', 'reverse', 'ghost', 'superball', 'paddleghost', 'paddleleft', 'paddleright', 'ballcurve', 'ballzigzag', 'ballrandom', 'blockinvisible', 'ballinvisible']; // Pick a random working type power.type = workingTypes[Math.floor(Math.random() * workingTypes.length)]; // If x or y is undefined, spawn at a random position within the play area if (typeof x === "undefined" || typeof y === "undefined") { // Avoid top 200px and bottom 300px, and 40px margin on sides var marginX = 40; var marginYTop = 200; var marginYBottom = 300; power.x = marginX + Math.random() * (2048 - 2 * marginX); // Make powerups spawn mostly in the upper part of the playfield (80% chance) if (Math.random() < 0.8) { // Top 40% of the playfield (above the paddle area) var upperHeight = Math.floor((2732 - marginYTop - marginYBottom) * 0.4); power.y = marginYTop + Math.random() * upperHeight; } else { // Anywhere in the playfield (fallback) power.y = marginYTop + Math.random() * (2732 - marginYTop - marginYBottom); } } else { power.x = x; power.y = y; } game.addChild(power); powerups.push(power); } // Helper: next level function nextLevel() { level++; lives = Math.min(5, lives + 1); // Add 1 life, max 5 updateGUI(); spawnBlocks(); spawnPaddle(); spawnBall(); levelCleared = false; } // Endless: Remove win condition, always go to nextLevel // Helper: lose life function loseLife() { // If any powerup ball is on screen, do not lose a life, just respawn the main ball and paddle if (powerupBalls && powerupBalls.length > 0) { spawnPaddle(); spawnBall(); return; } lives--; updateGUI(); LK.getSound('lose').play(); if (lives <= 0) { if (score > highScore) { highScore = score; storage.highScore = highScore; highScoreTxt.setText('High: ' + highScore); } score = 0; storage.score = 0; // Reset score on game over LK.effects.flashScreen(0xff0000, 1000); LK.showGameOver(); return; } // Remove all extra balls when losing a life, but do not reduce life for losing extra balls if (window.extraBalls && window.extraBalls.length) { for (var i = window.extraBalls.length - 1; i >= 0; i--) { if (window.extraBalls[i]) window.extraBalls[i].destroy(); window.extraBalls.splice(i, 1); } } spawnPaddle(); spawnBall(); } // Helper: win function winGame() { LK.effects.flashScreen(0x00ff00, 1000); LK.showYouWin(); } // Initialize first level highScore = storage.highScore || 0; if (storage.score !== undefined) score = storage.score; // Restore score from storage highScoreTxt.setText('High: ' + highScore); spawnBlocks(); spawnPaddle(); spawnBall(); updateGUI(); // Touch controls game.down = function (x, y, obj) { // Only allow paddle drag if touch is near paddle if (Math.abs(y - paddle.y) < paddle.height * 2) { dragPaddle = true; lastTouchX = x; } // Launch ball if it's sticky if (ballStickyToPaddle && dragPaddle) { // Launch at slight upward angle based on touch position var rel = (x - paddle.x) / (paddle.width / 2); var angle = -Math.PI / 3 + rel * (Math.PI / 3); // -60deg to 60deg ball.launch(angle); isBallLaunched = true; ballStickyToPaddle = false; } }; game.move = function (x, y, obj) { if (dragPaddle) { paddle.moveTo(x); // If ball is sticky, move it with paddle if (ballStickyToPaddle) { ball.x = paddle.x; } } }; game.up = function (x, y, obj) { dragPaddle = false; }; // Main update loop game.update = function () { // Ball update if (ball) ball.update(); // Update extra balls (if any) if (window.extraBalls && window.extraBalls.length) { for (var eb = window.extraBalls.length - 1; eb >= 0; eb--) { var ebBall = window.extraBalls[eb]; if (ebBall && ebBall.update) ebBall.update(); // Remove if off screen if (ebBall && ebBall.y - ebBall.radius > 2732) { // Just remove the extra ball, do not call loseLife() ebBall.destroy(); window.extraBalls.splice(eb, 1); // Do NOT call loseLife() for extra balls continue; } // Paddle collision for extra balls if (ebBall && typeof paddle !== "undefined" && paddle && typeof paddle.getHitbox === "function") { var hitbox = paddle.getHitbox(); if (ebBall.y + ebBall.radius >= hitbox.top && ebBall.y - ebBall.radius <= hitbox.bottom && ebBall.x + ebBall.radius >= hitbox.left && ebBall.x - ebBall.radius <= hitbox.right && ebBall.vy > 0) { // Reflect ball, angle based on where it hit the paddle var rel = (ebBall.x - paddle.x) / (paddle.width / 2 * paddle.scaleX); var angle = -Math.PI / 3 + rel * (Math.PI / 3); // -60deg to 60deg var speed = Math.sqrt(ebBall.vx * ebBall.vx + ebBall.vy * ebBall.vy); ebBall.vx = Math.cos(angle) * speed; ebBall.vy = Math.sin(angle) * speed; ebBall.y = paddle.y - paddle.height / 2 - ebBall.radius - 2; if (ebBall.y - ebBall.radius < 0) { ebBall.y = ebBall.radius; if (ebBall.vy < 0) ebBall.vy = Math.abs(ebBall.vy); } LK.getSound('hit').play(); } } } } // Update powerup balls to behave like normal balls but not cost a life when lost if (powerupBalls && powerupBalls.length) { for (var pbIdx = powerupBalls.length - 1; pbIdx >= 0; pbIdx--) { var pbBall = powerupBalls[pbIdx]; if (pbBall && pbBall.update) pbBall.update(); // Remove if off screen if (pbBall && pbBall.y - pbBall.radius > 2732) { pbBall.destroy(); powerupBalls.splice(pbIdx, 1); // Do NOT call loseLife() for powerup balls continue; } // Paddle collision for powerup balls if (pbBall && typeof paddle !== "undefined" && paddle && typeof paddle.getHitbox === "function") { var hitbox = paddle.getHitbox(); if (pbBall.y + pbBall.radius >= hitbox.top && pbBall.y - pbBall.radius <= hitbox.bottom && pbBall.x + pbBall.radius >= hitbox.left && pbBall.x - pbBall.radius <= hitbox.right && pbBall.vy > 0) { // Reflect ball, angle based on where it hit the paddle var rel = (pbBall.x - paddle.x) / (paddle.width / 2 * paddle.scaleX); var angle = -Math.PI / 3 + rel * (Math.PI / 3); // -60deg to 60deg var speed = Math.sqrt(pbBall.vx * pbBall.vx + pbBall.vy * pbBall.vy); pbBall.vx = Math.cos(angle) * speed; pbBall.vy = Math.sin(angle) * speed; pbBall.y = paddle.y - paddle.height / 2 - pbBall.radius - 2; if (pbBall.y - pbBall.radius < 0) { pbBall.y = pbBall.radius; if (pbBall.vy < 0) pbBall.vy = Math.abs(pbBall.vy); } LK.getSound('hit').play(); } } } } // PowerUp update for (var i = powerups.length - 1; i >= 0; i--) { var p = powerups[i]; p.update(); // If off screen if (p.y > 2732 + 100) { p.destroy(); powerups.splice(i, 1); continue; } // Paddle or ball collision var collected = false; // Use intersects for paddle collision to ensure all powerups are collected by paddle if (paddle && typeof p.intersects === "function" && p.intersects(paddle)) { collected = true; } // Check main ball if (!collected && ball && p.intersects(ball)) { collected = true; } // Check extra balls if (!collected && window.extraBalls && window.extraBalls.length) { for (var eb = 0; eb < window.extraBalls.length; eb++) { if (p.intersects(window.extraBalls[eb])) { collected = true; break; } } } if (collected) { // --- Powerup balls update and collection --- } for (var i = powerupBalls.length - 1; i >= 0; i--) { var pb = powerupBalls[i]; pb.update(); // Remove if off screen if (pb.y - pb.radius > 2732 + 100) { pb.destroy(); powerupBalls.splice(i, 1); continue; } // Collect if hits paddle if (pb.intersects && paddle && pb.intersects(paddle)) { // Show floating text for powerup ball var label = new Text2(pb.type.toUpperCase(), { size: 70, fill: 0xFFE066, font: "Arial Black" }); label.anchor.set(0.5, 1); label.x = pb.x; label.y = pb.y - 60; game.addChild(label); tween(label, { y: label.y - 120, alpha: 0 }, { duration: 1200, easing: tween.easeOut, onFinish: function onFinish() { label.destroy(); } }); LK.getSound('powerup').play(); // Give a reward: e.g. +1 life, +100 score, or multiball if (pb.type === 'powerupball1') { lives = Math.min(5, lives + 1); updateGUI(); } else if (pb.type === 'powerupball2') { score += 100; updateGUI(); } else if (pb.type === 'powerupball3') { // Add a multiball as a main ball, not just extra var newBall = new Ball(); newBall.x = ball.x; newBall.y = ball.y; newBall.vx = -ball.vx; newBall.vy = ball.vy; newBall.sticky = false; game.addChild(newBall); if (!window.extraBalls) window.extraBalls = []; window.extraBalls.push(newBall); } pb.destroy(); powerupBalls.splice(i, 1); } } if (collected) { // Show floating text for powerup type var label = new Text2(p.type.toUpperCase(), { size: 70, fill: 0xFFE066, font: "Arial Black" }); label.anchor.set(0.5, 1); label.x = p.x; label.y = p.y - 60; game.addChild(label); tween(label, { y: label.y - 120, alpha: 0 }, { duration: 1200, easing: tween.easeOut, onFinish: function onFinish() { label.destroy(); } }); // Apply powerup LK.getSound('powerup').play(); // --- Powerup duration tracking and display --- if (!window.activePowerups) window.activePowerups = []; if (!window.powerupTimers) window.powerupTimers = []; if (!window.powerupIcons) window.powerupIcons = []; // List of timed powerups and their durations (ms) var timedPowerupDurations = { expand: 6000, shrink: 6000, slow: 6000, fast: 6000, bigball: 6000, smallball: 6000, paddleup: 6000, paddledown: 6000, paddlefast: 6000, paddleslow: 6000, ballheavy: 6000, balllight: 6000, blockshrink: 6000, blockexpand: 6000, ghost: 6000, superball: 6000, paddleghost: 6000, reverse: 6000, blockinvisible: 6000, ballinvisible: 6000, magnet: 4000, ballcurve: 4000, ballzigzag: 4000 }; // If this is a timed powerup, add to activePowerups if (timedPowerupDurations[p.type]) { var duration = timedPowerupDurations[p.type]; var now = Date.now(); // Remove any previous of same type (refresh) for (var ap = window.activePowerups.length - 1; ap >= 0; ap--) { if (window.activePowerups[ap].type === p.type) { window.activePowerups.splice(ap, 1); if (window.powerupTimers[ap]) { LK.clearTimeout(window.powerupTimers[ap]); window.powerupTimers.splice(ap, 1); } if (window.powerupIcons[ap]) { window.powerupIcons[ap].destroy(); window.powerupIcons.splice(ap, 1); } } } window.activePowerups.push({ type: p.type, end: now + duration }); // Add icon to GUI var icon = new Text2(p.type[0].toUpperCase(), { size: 70, fill: 0xFFE066, font: "Arial Black" }); icon.anchor.set(0.5, 0.5); // Place icons in a row at top center, below score icon.x = 2048 / 2 - (window.activePowerups.length - 1) * 50 + (window.activePowerups.length - 1) * 100 / 2 + (window.activePowerups.length - 1) * 0; icon.y = 170; LK.gui.top.addChild(icon); window.powerupIcons.push(icon); // Timer to remove from activePowerups and GUI (function (type, icon) { var idx = window.activePowerups.length - 1; var timer = LK.setTimeout(function () { // Remove from arrays for (var ap = window.activePowerups.length - 1; ap >= 0; ap--) { if (window.activePowerups[ap].type === type) { window.activePowerups.splice(ap, 1); if (window.powerupTimers[ap]) window.powerupTimers.splice(ap, 1); if (window.powerupIcons[ap]) { window.powerupIcons[ap].destroy(); window.powerupIcons.splice(ap, 1); } } } }, duration); window.powerupTimers.push(timer); })(p.type, icon); } // 50 powerup effects, only working ones kept if (collected) { if (p.type === 'expand') { tween(paddle, { scaleX: 1.5 }, { duration: 300, easing: tween.easeOut }); LK.setTimeout(function () { tween(paddle, { scaleX: 1 }, { duration: 300, easing: tween.easeIn }); }, 6000); } else if (p.type === 'shrink') { tween(paddle, { scaleX: 0.7 }, { duration: 300, easing: tween.easeOut }); LK.setTimeout(function () { tween(paddle, { scaleX: 1 }, { duration: 300, easing: tween.easeIn }); }, 6000); } else if (p.type === 'life') { lives = Math.min(5, lives + 1); updateGUI(); } else if (p.type === 'sticky') { ball.sticky = true; ballStickyToPaddle = true; ball.vx = 0; ball.vy = 0; ball.x = paddle.x; ball.y = paddle.y - paddle.height / 2 - ball.radius - 10; } else if (p.type === 'multiball') { // Add a second ball that is a true main ball (not extra) var newBall = new Ball(); newBall.x = ball.x; newBall.y = ball.y; newBall.vx = -ball.vx; newBall.vy = ball.vy; newBall.sticky = false; game.addChild(newBall); // Add to global for update/collision as a main ball if (!window.extraBalls) window.extraBalls = []; window.extraBalls.push(newBall); } else if (p.type === 'ballsplit') { // Split ball into two, both are main balls var newBall = new Ball(); newBall.x = ball.x; newBall.y = ball.y; newBall.vx = -ball.vx; newBall.vy = ball.vy; newBall.sticky = false; game.addChild(newBall); if (!window.extraBalls) window.extraBalls = []; window.extraBalls.push(newBall); } else if (p.type === 'ballclone') { // Clone ball with same direction, as a main ball var newBall = new Ball(); newBall.x = ball.x; newBall.y = ball.y; newBall.vx = ball.vx; newBall.vy = ball.vy; newBall.sticky = false; game.addChild(newBall); if (!window.extraBalls) window.extraBalls = []; window.extraBalls.push(newBall); } else if (p.type === 'slow') { ball.speed = Math.max(10, ball.speed - 6); ball.vx *= 0.7; ball.vy *= 0.7; LK.setTimeout(function () { ball.speed = 22; }, 6000); } else if (p.type === 'fast') { ball.speed += 8; ball.vx *= 1.3; ball.vy *= 1.3; LK.setTimeout(function () { ball.speed = 22; }, 6000); } else if (p.type === 'bigball') { ball.scaleX = ball.scaleY = 1.7; LK.setTimeout(function () { ball.scaleX = ball.scaleY = 1; }, 6000); } else if (p.type === 'smallball') { ball.scaleX = ball.scaleY = 0.6; LK.setTimeout(function () { ball.scaleX = ball.scaleY = 1; }, 6000); } else if (p.type === 'scorex2') { if (!window.scorex2) window.scorex2 = 0; window.scorex2 += 1; LK.setTimeout(function () { window.scorex2 -= 1; }, 8000); } else if (p.type === 'scorex3') { if (!window.scorex3) window.scorex3 = 0; window.scorex3 += 1; LK.setTimeout(function () { window.scorex3 -= 1; }, 8000); } else if (p.type === 'blockbomb') { // Destroy a random block if (blocks.length > 0) { var idx = Math.floor(Math.random() * blocks.length); blocks[idx].breakBlock(); blocks.splice(idx, 1); } } else if (p.type === 'blockrowclear') { // Clear a random row if (blocks.length > 0) { var yRows = {}; for (var b = 0; b < blocks.length; b++) { var by = Math.round(blocks[b].y / 10) * 10; if (!yRows[by]) yRows[by] = []; yRows[by].push(b); } var rowKeys = Object.keys(yRows); var row = yRows[rowKeys[Math.floor(Math.random() * rowKeys.length)]]; for (var j = row.length - 1; j >= 0; j--) { blocks[row[j]].breakBlock(); blocks.splice(row[j], 1); } } } else if (p.type === 'blockcolclear') { // Clear a random column if (blocks.length > 0) { var xCols = {}; for (var b = 0; b < blocks.length; b++) { var bx = Math.round(blocks[b].x / 10) * 10; if (!xCols[bx]) xCols[bx] = []; xCols[bx].push(b); } var colKeys = Object.keys(xCols); var col = xCols[colKeys[Math.floor(Math.random() * colKeys.length)]]; for (var j = col.length - 1; j >= 0; j--) { blocks[col[j]].breakBlock(); blocks.splice(col[j], 1); } } } else if (p.type === 'ballsplit') { // Split ball into two var newBall = new Ball(); newBall.x = ball.x; newBall.y = ball.y; newBall.vx = -ball.vx; newBall.vy = ball.vy; newBall.sticky = false; game.addChild(newBall); if (!window.extraBalls) window.extraBalls = []; window.extraBalls.push(newBall); } else if (p.type === 'ballclone') { // Clone ball with same direction var newBall = new Ball(); newBall.x = ball.x; newBall.y = ball.y; newBall.vx = ball.vx; newBall.vy = ball.vy; newBall.sticky = false; game.addChild(newBall); if (!window.extraBalls) window.extraBalls = []; window.extraBalls.push(newBall); } else if (p.type === 'paddleup') { paddle.y -= 120; LK.setTimeout(function () { paddle.y += 120; }, 6000); } else if (p.type === 'paddledown') { paddle.y += 120; LK.setTimeout(function () { paddle.y -= 120; }, 6000); // Removed scorebonus and scorepenalty powerup effects } else if (p.type === 'blockheal') { // Heal all blocks by 1 for (var b = 0; b < blocks.length; b++) { blocks[b].hp = Math.min(blocks[b].maxHp, blocks[b].hp + 1); if (blocks[b].updateHpBar) blocks[b].updateHpBar(); } } else if (p.type === 'blockrandom') { // Randomize block colors var colors = ['red', 'green', 'blue', 'yellow']; for (var b = 0; b < blocks.length; b++) { blocks[b].setColor(colors[Math.floor(Math.random() * colors.length)]); } } else if (p.type === 'blockfreeze') { // Freeze blocks (no effect in this logic, but could be used for animation) } else if (p.type === 'blockmove') { // Move all blocks down for (var b = 0; b < blocks.length; b++) { blocks[b].y += 40; } } else if (p.type === 'blockshrink') { // Shrink all blocks for (var b = 0; b < blocks.length; b++) { blocks[b].scaleX = blocks[b].scaleY = 0.7; (function (block) { LK.setTimeout(function () { block.scaleX = block.scaleY = 1; }, 6000); })(blocks[b]); } } else if (p.type === 'blockexpand') { // Expand all blocks for (var b = 0; b < blocks.length; b++) { blocks[b].scaleX = blocks[b].scaleY = 1.3; (function (block) { LK.setTimeout(function () { block.scaleX = block.scaleY = 1; }, 6000); })(blocks[b]); } } else if (p.type === 'paddlefast') { paddle.moveTo = function (orig) { return function (x) { orig.call(paddle, x * 1.2); }; }(paddle.moveTo); LK.setTimeout(function () { if (!Paddle.prototype.moveTo) { Paddle.prototype.moveTo = function (x) { // Clamp paddle within game bounds (leave 40px margin) var minX = this.width / 2 + 40; var maxX = 2048 - this.width / 2 - 40; this.x = Math.max(minX, Math.min(maxX, x)); }; } paddle.moveTo = Paddle.prototype.moveTo; }, 6000); } else if (p.type === 'paddleslow') { paddle.moveTo = function (orig) { return function (x) { orig.call(paddle, x * 0.8); }; }(paddle.moveTo); LK.setTimeout(function () { if (!Paddle.prototype.moveTo) { Paddle.prototype.moveTo = function (x) { // Clamp paddle within game bounds (leave 40px margin) var minX = this.width / 2 + 40; var maxX = 2048 - this.width / 2 - 40; this.x = Math.max(minX, Math.min(maxX, x)); }; } paddle.moveTo = Paddle.prototype.moveTo; }, 6000); } else if (p.type === 'ballheavy') { ball.vy += 8; LK.setTimeout(function () { ball.vy -= 8; }, 6000); } else if (p.type === 'balllight') { ball.vy -= 8; LK.setTimeout(function () { ball.vy += 8; }, 6000); } else if (p.type === 'blockshield') { // Give all blocks +2 hp for (var b = 0; b < blocks.length; b++) { blocks[b].hp = Math.min(blocks[b].maxHp, blocks[b].hp + 2); if (blocks[b].updateHpBar) blocks[b].updateHpBar(); } } else if (p.type === 'blockswap') { // Swap two random blocks if (blocks.length > 1) { var i1 = Math.floor(Math.random() * blocks.length); var i2 = Math.floor(Math.random() * blocks.length); var tmpx = blocks[i1].x, tmpy = blocks[i1].y; blocks[i1].x = blocks[i2].x; blocks[i1].y = blocks[i2].y; blocks[i2].x = tmpx; blocks[i2].y = tmpy; } } else if (p.type === 'blockteleport') { // Teleport a random block to a random position if (blocks.length > 0) { var idx = Math.floor(Math.random() * blocks.length); blocks[idx].x = 400 + Math.random() * 1200; blocks[idx].y = 400 + Math.random() * 800; } } else if (p.type === 'paddleteleport') { paddle.x = 400 + Math.random() * 1200; } else if (p.type === 'ballteleport') { ball.x = 400 + Math.random() * 1200; ball.y = 400 + Math.random() * 800; } else if (p.type === 'blockregen') { // Regenerate a destroyed block at random if (blocks.length > 0) { var idx = Math.floor(Math.random() * blocks.length); var block = new Block(); block.setColor(blocks[idx].color); block.x = blocks[idx].x; block.y = blocks[idx].y; block.hp = block.maxHp = 2; block.setHp(2, 2); game.addChild(block); blocks.push(block); } } else if (p.type === 'blockexplode') { // Destroy 3 random blocks for (var n = 0; n < 3 && blocks.length > 0; n++) { var idx = Math.floor(Math.random() * blocks.length); blocks[idx].breakBlock(); blocks.splice(idx, 1); } } else if (p.type === 'magnet') { // Ball moves toward paddle for a while if (!window.magnetActive) { window.magnetActive = true; var magnetInterval = LK.setInterval(function () { if (ball && paddle) { var dx = paddle.x - ball.x; ball.vx += dx * 0.01; } }, 60); LK.setTimeout(function () { LK.clearInterval(magnetInterval); window.magnetActive = false; }, 4000); } } else if (p.type === 'reverse') { // Reverse paddle movement paddle.moveTo = function (orig) { return function (x) { orig.call(paddle, 2048 - x); }; }(paddle.moveTo); LK.setTimeout(function () { if (!Paddle.prototype.moveTo) { Paddle.prototype.moveTo = function (x) { // Clamp paddle within game bounds (leave 40px margin) var minX = this.width / 2 + 40; var maxX = 2048 - this.width / 2 - 40; this.x = Math.max(minX, Math.min(maxX, x)); }; } paddle.moveTo = Paddle.prototype.moveTo; }, 6000); } else if (p.type === 'ghost') { // Paddle becomes transparent paddle.alpha = 0.3; LK.setTimeout(function () { paddle.alpha = 1; }, 6000); } else if (p.type === 'superball') { // Ball passes through blocks for a while ball.superball = true; LK.setTimeout(function () { ball.superball = false; }, 6000); } else if (p.type === 'paddleghost') { paddle.alpha = 0.1; LK.setTimeout(function () { paddle.alpha = 1; }, 6000); } else if (p.type === 'paddleleft') { paddle.x = Math.max(paddle.width / 2 + 40, paddle.x - 400); } else if (p.type === 'paddleright') { paddle.x = Math.min(2048 - paddle.width / 2 - 40, paddle.x + 400); } else if (p.type === 'ballcurve') { // Ball curves for a while if (!window.curveActive) { window.curveActive = true; var curveInterval = LK.setInterval(function () { if (ball) ball.vx += Math.sin(LK.ticks / 10) * 0.5; }, 60); LK.setTimeout(function () { LK.clearInterval(curveInterval); window.curveActive = false; }, 4000); } } else if (p.type === 'ballzigzag') { // Ball zigzags for a while if (!window.zigzagActive) { window.zigzagActive = true; var zigzagInterval = LK.setInterval(function () { if (ball) ball.vx += LK.ticks % 2 === 0 ? 2 : -2; }, 60); LK.setTimeout(function () { LK.clearInterval(zigzagInterval); window.zigzagActive = false; }, 4000); } } else if (p.type === 'ballrandom') { // Ball gets random velocity ball.vx = (Math.random() - 0.5) * 30; ball.vy = (Math.random() - 0.5) * 30; } else if (p.type === 'blockinvisible') { // All blocks invisible for a while for (var b = 0; b < blocks.length; b++) { blocks[b].alpha = 0.1; (function (block) { LK.setTimeout(function () { block.alpha = 1; }, 6000); })(blocks[b]); } } else if (p.type === 'ballinvisible') { ball.alpha = 0.1; LK.setTimeout(function () { ball.alpha = 1; }, 6000); } p.destroy(); powerups.splice(i, 1); } } } // Ball physics if (ball && !ball.sticky) { // --- Wall bounce tracking --- if (typeof ball.leftWallHits === "undefined") ball.leftWallHits = 0; if (typeof ball.rightWallHits === "undefined") ball.rightWallHits = 0; // --- Track if ball has hit a block since last paddle reset --- if (typeof ball.hasHitBlockSinceReset === "undefined") ball.hasHitBlockSinceReset = false; // --- Track consecutive left/right wall bounces without block hit --- if (typeof ball.wallBouncesNoBlock === "undefined") ball.wallBouncesNoBlock = 0; // Wall collisions var hitWall = false; if (ball.x - ball.radius < 0) { ball.x = ball.radius; ball.vx = -ball.vx; LK.getSound('hit').play(); ball.leftWallHits++; hitWall = true; } if (ball.x + ball.radius > 2048) { ball.x = 2048 - ball.radius; ball.vx = -ball.vx; LK.getSound('hit').play(); ball.rightWallHits++; hitWall = true; } if (hitWall) { // Only count if no block hit since last paddle reset if (!ball.hasHitBlockSinceReset) { ball.wallBouncesNoBlock++; } } if (ball.y - ball.radius < 0) { ball.y = ball.radius; ball.vy = -ball.vy; LK.getSound('hit').play(); } // If left or right wall hit more than 15 times without hitting any block, return ball to player if (ball.wallBouncesNoBlock >= 15) { // Reset ball to paddle, sticky ball.x = paddle.x; ball.y = paddle.y - paddle.height / 2 - ball.radius - 10; ball.vx = 0; ball.vy = 0; ball.sticky = true; ballStickyToPaddle = true; isBallLaunched = false; ball.leftWallHits = 0; ball.rightWallHits = 0; ball.wallBouncesNoBlock = 0; ball.hasHitBlockSinceReset = false; } // Paddle collision if (typeof paddle.getHitbox === "function") { var hitbox = paddle.getHitbox(); if (ball.y + ball.radius >= hitbox.top && ball.y - ball.radius <= hitbox.bottom && ball.x + ball.radius >= hitbox.left && ball.x - ball.radius <= hitbox.right && ball.vy > 0) { // Reflect ball, angle based on where it hit the paddle var rel = (ball.x - paddle.x) / (paddle.width / 2 * paddle.scaleX); var angle = -Math.PI / 3 + rel * (Math.PI / 3); // -60deg to 60deg var speed = Math.sqrt(ball.vx * ball.vx + ball.vy * ball.vy); ball.vx = Math.cos(angle) * speed; ball.vy = Math.sin(angle) * speed; ball.y = paddle.y - paddle.height / 2 - ball.radius - 2; LK.getSound('hit').play(); } } else { if (ball.y + ball.radius >= paddle.y - paddle.height / 2 && ball.y + ball.radius <= paddle.y + paddle.height / 2 && ball.x >= paddle.x - paddle.width / 2 * paddle.scaleX && ball.x <= paddle.x + paddle.width / 2 * paddle.scaleX && ball.vy > 0) { // Reflect ball, angle based on where it hit the paddle var rel = (ball.x - paddle.x) / (paddle.width / 2 * paddle.scaleX); var angle = -Math.PI / 3 + rel * (Math.PI / 3); // -60deg to 60deg var speed = Math.sqrt(ball.vx * ball.vx + ball.vy * ball.vy); ball.vx = Math.cos(angle) * speed; ball.vy = Math.sin(angle) * speed; ball.y = paddle.y - paddle.height / 2 - ball.radius - 2; LK.getSound('hit').play(); } } // Bottom out if (ball.y - ball.radius > 2732) { // If there is at least one powerup ball on screen, continue with it as the main ball if (powerupBalls && powerupBalls.length > 0) { // Remove the main ball, but do not call loseLife ball.destroy(); ball = null; // Promote the first powerup ball to be the new main ball var promoted = powerupBalls.shift(); // Remove from game and re-add as main ball for correct update/collision if (promoted && promoted.parent) promoted.parent.removeChild(promoted); // Create a new Ball instance at the promoted ball's position and velocity ball = new Ball(); ball.x = promoted.x; ball.y = promoted.y; ball.vx = promoted.vx; ball.vy = promoted.vy; ball.sticky = false; game.addChild(ball); // Remove the promoted powerup ball promoted.destroy(); // Do not return, continue game loop } else { loseLife(); return; } } } // Block collisions for (var i = blocks.length - 1; i >= 0; i--) { var block = blocks[i]; var hit = false; // Check main ball if (ball && block && ball.intersects(block)) { // Reflect ball var overlapX = Math.abs(ball.x - block.x) - (block.blockSprite.width / 2 + ball.radius); var overlapY = Math.abs(ball.y - block.y) - (block.blockSprite.height / 2 + ball.radius); if (overlapX > overlapY) { ball.vx = -ball.vx; } else { ball.vy = -ball.vy; } hit = true; } // Check extra balls if (!hit && window.extraBalls && window.extraBalls.length) { for (var eb = 0; eb < window.extraBalls.length; eb++) { var ebBall = window.extraBalls[eb]; if (ebBall && block && ebBall.intersects(block)) { // Reflect extra ball var overlapX = Math.abs(ebBall.x - block.x) - (block.blockSprite.width / 2 + ebBall.radius); var overlapY = Math.abs(ebBall.y - block.y) - (block.blockSprite.height / 2 + ebBall.radius); if (overlapX > overlapY) { ebBall.vx = -ebBall.vx; } else { ebBall.vy = -ebBall.vy; } hit = true; break; } } } if (hit) { // Mark that ball has hit a block, so wall bounce counter pauses until next reset if (ball && typeof ball.hasHitBlockSinceReset !== "undefined") { ball.hasHitBlockSinceReset = true; ball.wallBouncesNoBlock = 0; } // Block hit block.hp--; if (block.updateHpBar) block.updateHpBar(); combo++; startCombo(); score += 10 * combo; updateGUI(); LK.getSound('break').play(); // Power-up chance (increased drop rate) var powerupDropRate = 0.22 + Math.min(0.01 * level, 0.18); // increases with level, max ~0.4 if (Math.random() < powerupDropRate) { spawnPowerUp(block.x, block.y); } if (block.hp <= 0) { block.breakBlock(); blocks.splice(i, 1); } break; // Only one block per frame } } // Combo reset if no block hit for a while if (combo > 0 && !comboTimer) { resetCombo(); } // Level clear if (!levelCleared && blocks.length === 0) { levelCleared = true; LK.setTimeout(function () { nextLevel(); }, 1200); } // --- Random powerup spawn logic --- if (typeof game.lastPowerupSpawnTick === "undefined") { game.lastPowerupSpawnTick = LK.ticks; } if (typeof game.powerupSpawnInterval === "undefined") { // Try to spawn a powerup every 6-12 seconds (randomized) game.powerupSpawnInterval = 360 + Math.floor(Math.random() * 360); } if (typeof game.lastPowerupBallTick === "undefined") { game.lastPowerupBallTick = LK.ticks; } if (typeof game.powerupBallInterval === "undefined") { // Try to spawn a powerup ball every 8-16 seconds (randomized) game.powerupBallInterval = 480 + Math.floor(Math.random() * 480); } if (LK.ticks - game.lastPowerupSpawnTick > game.powerupSpawnInterval) { // Only spawn if there are less than 3 powerups on screen if (powerups.length < 3 && blocks.length > 0) { spawnPowerUp(); // No x/y: will spawn at random position } game.lastPowerupSpawnTick = LK.ticks; game.powerupSpawnInterval = 360 + Math.floor(Math.random() * 360); } // --- Random powerup ball spawn logic --- if (LK.ticks - game.lastPowerupBallTick > game.powerupBallInterval) { // Only spawn if there are less than 3 powerup balls on screen if (powerupBalls.length < 3) { // Pick a random type var types = [PowerupBall1, PowerupBall2, PowerupBall3]; var idx = Math.floor(Math.random() * types.length); var PowerupBallClass = types[idx]; var pb = new PowerupBallClass(); // Spawn at random X, top of screen pb.x = 100 + Math.random() * (2048 - 200); pb.y = -pb.radius - 10; game.addChild(pb); powerupBalls.push(pb); } game.lastPowerupBallTick = LK.ticks; game.powerupBallInterval = 480 + Math.floor(Math.random() * 480); } // Update powerup icon timers and show countdown for active powerups if (window.activePowerups && window.activePowerups.length && window.powerupIcons && window.powerupIcons.length) { var now = Date.now(); for (var i = 0; i < window.activePowerups.length; i++) { var ap = window.activePowerups[i]; var icon = window.powerupIcons[i]; if (ap && icon) { var remain = Math.max(0, Math.ceil((ap.end - now) / 1000)); // Show timer only if more than 0 seconds left, otherwise just the icon if (remain > 0) { icon.setText(ap.type[0].toUpperCase() + "\n" + remain); } else { icon.setText(ap.type[0].toUpperCase()); } // Reposition icons in a row icon.x = 2048 / 2 - (window.activePowerups.length - 1) * 50 + i * 100 / 2 + i * 0; icon.y = 170; } } } }; // Music (optional, not required by MVP, so not included) /* End of gamecode.js */
===================================================================
--- original.js
+++ change.js
@@ -28,8 +28,21 @@
self.update = function () {
if (!self.sticky) {
self.x += self.vx;
self.y += self.vy;
+ // Prevent ball from leaving left, right, or top of the screen
+ if (self.x - self.radius < 0) {
+ self.x = self.radius;
+ self.vx = -self.vx;
+ }
+ if (self.x + self.radius > 2048) {
+ self.x = 2048 - self.radius;
+ self.vx = -self.vx;
+ }
+ if (self.y - self.radius < 0) {
+ self.y = self.radius;
+ self.vy = -self.vy;
+ }
}
};
return self;
});
@@ -173,18 +186,17 @@
self.type = 'powerupball1';
self.update = function () {
self.x += self.vx;
self.y += self.vy;
- // Bounce off walls
+ // Prevent ball from leaving left, right, or top of the screen
if (self.x - self.radius < 0) {
self.x = self.radius;
self.vx = -self.vx;
}
if (self.x + self.radius > 2048) {
self.x = 2048 - self.radius;
self.vx = -self.vx;
}
- // Bounce off top
if (self.y - self.radius < 0) {
self.y = self.radius;
self.vy = -self.vy;
LK.getSound('hit').play();
@@ -250,18 +262,17 @@
self.type = 'powerupball2';
self.update = function () {
self.x += self.vx;
self.y += self.vy;
- // Bounce off walls
+ // Prevent ball from leaving left, right, or top of the screen
if (self.x - self.radius < 0) {
self.x = self.radius;
self.vx = -self.vx;
}
if (self.x + self.radius > 2048) {
self.x = 2048 - self.radius;
self.vx = -self.vx;
}
- // Bounce off top
if (self.y - self.radius < 0) {
self.y = self.radius;
self.vy = -self.vy;
LK.getSound('hit').play();
@@ -327,18 +338,17 @@
self.type = 'powerupball3';
self.update = function () {
self.x += self.vx;
self.y += self.vy;
- // Bounce off walls
+ // Prevent ball from leaving left, right, or top of the screen
if (self.x - self.radius < 0) {
self.x = self.radius;
self.vx = -self.vx;
}
if (self.x + self.radius > 2048) {
self.x = 2048 - self.radius;
self.vx = -self.vx;
}
- // Bounce off top
if (self.y - self.radius < 0) {
self.y = self.radius;
self.vy = -self.vy;
LK.getSound('hit').play();