User prompt
bütün levellere sayı ve harf yaz birlikte level arttıkce zorlukta artsın
User prompt
eğer top sağ ve sol duvara 10 kere çarptıysa yukarı duvara doğru fırlasın yükselsin çok hızlı olsun
User prompt
2x = 10 olsun 4x = 20x olsun
User prompt
daha hızlı yükselsin
User prompt
eğer top sağ ve sol duvara 10 kere çarptıysa yukarı duvara doğru hızlıca yükselsin bütün toplar için geçerli eğer 15 den fazla çarptıysa daha da hızlı yukarı duvara yükselsin oyunun her yerinde geçerli bu kural
User prompt
eğer top sağ ve sol duvara 10 kere çarptıysa yukarı duvara doğru yükselsin bütün toplar için geçerli
User prompt
ters kontrol kaldır powerup dan
User prompt
sayıyla harfleri yan yana da yapabilirsin
User prompt
level atladıkca tuğlalar rastgele ama belli bir düzende olmalı mesela bir harf olmalı bazen bir sayı rastgele yani
User prompt
tuğllalar hiç bir zaman birbirine değmesin ama her level atladığında farklı yerlerde geçilmesi çok zor bölümler yap
User prompt
level atladıkca zorlaştır her level atladığında daha da zor olsun
User prompt
şekilleri boşver
User prompt
level başlangıçlarında aşırı kasıyor düzelt
User prompt
bütün toplar her ne olursa olsun tuğlalara vurabilsin
User prompt
tuğlanın canı kaç olursa olsun laser ve fireball değdiği bütün tuğlalar yok olsun
User prompt
laser ve fireball değdiği bütün tuğlaları silsin görüntü olarak da normal de de silsin
User prompt
laser ve fireball çıkma ihtimalini çok arttır
User prompt
lasesr ve fireball la yok edilen tuğlalar hemen kaybolsun
User prompt
laser çıkma ihtimalini arttır
User prompt
bu ışınlanan laser en yukarıya kadar gitsin ve önüne gelen bütün tuğlaları yok etsin patlama efektiyle ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
laser powerup gelince kırmızı bir lazer ışınlasın paddle
User prompt
level 6 ve sonrası çok kasıyor onu düzelt lütfen
User prompt
powerup da +1 ball +2 ball +3 ball gibi şeyler de ekle
User prompt
bazı tuğlalar yok olsa bile görüntüsü kalıyor düzelt onu
User prompt
oyundaki bütün bugları temizle
/**** * 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; } // Laser/fireball: instantly destroy all blocks touched if ((self.fireball || typeof paddle !== "undefined" && paddle && paddle.laserBeam) && typeof blocks !== "undefined" && blocks && blocks.length) { for (var i = blocks.length - 1; i >= 0; i--) { var block = blocks[i]; if (block && self.intersects && self.intersects(block)) { block.hp = 0; if (block.updateHpBar) block.updateHpBar(); block.alpha = 0; block.scaleX = block.scaleY = 1.5; block.destroy(); if (block.parent) block.parent.removeChild(block); blocks.splice(i, 1); } } } } }; 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.blockType = 'normal'; // will be set by spawnBlocks 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(); } }); }; // --- HITBOX LOGIC --- // Returns the hitbox for this block, depending on its type and current transform self.getHitbox = function () { var bx = self.x, by = self.y; var w = self.blockSprite.width * (self.scaleX || 1); var h = self.blockSprite.height * (self.scaleY || 1); var rot = self.blockSprite.rotation || 0; if (self.blockType === 'round') { var r = w / 2; return { type: 'circle', x: bx, y: by, r: r }; } else if (self.blockType === 'diagonal') { var hw = w / 2, hh = h / 2; return { type: 'rotbox', x: bx, y: by, hw: hw, hh: hh, rot: rot }; } else { var hw = w / 2, hh = h / 2; return { type: 'box', x: bx, y: by, hw: hw, hh: hh }; } }; // Override .intersects for block to support all hitbox types self.intersects = function (other) { var myHitbox = self.getHitbox(); var otherHitbox = typeof other.getHitbox === "function" ? other.getHitbox() : null; if (!otherHitbox) { // Fallback: use bounding box var hw = self.blockSprite.width * (self.scaleX || 1) / 2; var hh = self.blockSprite.height * (self.scaleY || 1) / 2; return other.x > self.x - hw && other.x < self.x + hw && other.y > self.y - hh && other.y < self.y + hh; } // Circle-circle if (myHitbox.type === 'circle' && otherHitbox.type === 'circle') { var dx = myHitbox.x - otherHitbox.x; var dy = myHitbox.y - otherHitbox.y; var dist = Math.sqrt(dx * dx + dy * dy); return dist < myHitbox.r + otherHitbox.r; } // Box-box (AABB) if (myHitbox.type === 'box' && otherHitbox.type === 'box') { return Math.abs(myHitbox.x - otherHitbox.x) < myHitbox.hw + otherHitbox.hw && Math.abs(myHitbox.y - otherHitbox.y) < myHitbox.hh + otherHitbox.hh; } // Rotated box - circle if (myHitbox.type === 'rotbox' && otherHitbox.type === 'circle') { // Rotate circle center into box's local space var cos = Math.cos(-myHitbox.rot), sin = Math.sin(-myHitbox.rot); var dx = otherHitbox.x - myHitbox.x, dy = otherHitbox.y - myHitbox.y; var localX = dx * cos - dy * sin; var localY = dx * sin + dy * cos; // Clamp to box var clampedX = Math.max(-myHitbox.hw, Math.min(myHitbox.hw, localX)); var clampedY = Math.max(-myHitbox.hh, Math.min(myHitbox.hh, localY)); var distX = localX - clampedX, distY = localY - clampedY; return distX * distX + distY * distY < otherHitbox.r * otherHitbox.r; } // Circle - rotated box (swap) if (myHitbox.type === 'circle' && otherHitbox.type === 'rotbox') { // Use the same logic, swap order var cos = Math.cos(-otherHitbox.rot), sin = Math.sin(-otherHitbox.rot); var dx = myHitbox.x - otherHitbox.x, dy = myHitbox.y - otherHitbox.y; var localX = dx * cos - dy * sin; var localY = dx * sin + dy * cos; var clampedX = Math.max(-otherHitbox.hw, Math.min(otherHitbox.hw, localX)); var clampedY = Math.max(-otherHitbox.hh, Math.min(otherHitbox.hh, localY)); var distX = localX - clampedX, distY = localY - clampedY; return distX * distX + distY * distY < myHitbox.r * myHitbox.r; } // Rotated box - box (AABB fallback, not perfect) if (myHitbox.type === 'rotbox' && otherHitbox.type === 'box') { // Approximate by using AABB of rotated box var rot = myHitbox.rot; var hw = Math.abs(myHitbox.hw * Math.cos(rot)) + Math.abs(myHitbox.hh * Math.sin(rot)); var hh = Math.abs(myHitbox.hw * Math.sin(rot)) + Math.abs(myHitbox.hh * Math.cos(rot)); return Math.abs(myHitbox.x - otherHitbox.x) < hw + otherHitbox.hw && Math.abs(myHitbox.y - otherHitbox.y) < hh + otherHitbox.hh; } if (myHitbox.type === 'box' && otherHitbox.type === 'rotbox') { // Approximate by using AABB of rotated box var rot = otherHitbox.rot; var hw = Math.abs(otherHitbox.hw * Math.cos(rot)) + Math.abs(otherHitbox.hh * Math.sin(rot)); var hh = Math.abs(otherHitbox.hw * Math.sin(rot)) + Math.abs(otherHitbox.hh * Math.cos(rot)); return Math.abs(myHitbox.x - otherHitbox.x) < myHitbox.hw + hw && Math.abs(myHitbox.y - otherHitbox.y) < myHitbox.hh + hh; } // Fallback: treat as AABB var hw1 = myHitbox.hw || self.blockSprite.width * (self.scaleX || 1) / 2; var hh1 = myHitbox.hh || self.blockSprite.height * (self.scaleY || 1) / 2; var hw2 = otherHitbox.hw || (other.width ? other.width / 2 : 0); var hh2 = otherHitbox.hh || (other.height ? other.height / 2 : 0); return Math.abs(myHitbox.x - otherHitbox.x) < hw1 + hw2 && Math.abs(myHitbox.y - otherHitbox.y) < hh1 + hh2; }; 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; // --- Wall bounce tracking for powerup balls --- if (typeof self.leftWallHits === "undefined") self.leftWallHits = 0; if (typeof self.rightWallHits === "undefined") self.rightWallHits = 0; if (typeof self.wallBouncesNoBlock === "undefined") self.wallBouncesNoBlock = 0; if (typeof self.hasHitBlockSinceReset === "undefined") self.hasHitBlockSinceReset = false; var hitWall = false; // 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; self.leftWallHits++; hitWall = true; } if (self.x + self.radius > 2048) { self.x = 2048 - self.radius; self.vx = -self.vx; self.rightWallHits++; hitWall = true; } if (hitWall) { if (!self.hasHitBlockSinceReset) { self.wallBouncesNoBlock++; } } // If left or right wall hit more than 15 times without hitting any block, return ball to player if (self.wallBouncesNoBlock >= 15 && typeof paddle !== "undefined" && paddle) { self.x = paddle.x; self.y = paddle.y - paddle.height / 2 - self.radius - 10; self.vx = 0; self.vy = 0; self.sticky = true; self.leftWallHits = 0; self.rightWallHits = 0; self.wallBouncesNoBlock = 0; self.hasHitBlockSinceReset = false; } 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") { if (self.fireball || typeof paddle !== "undefined" && paddle && paddle.laserBeam) { block.hp = 0; if (block.updateHpBar) block.updateHpBar(); block.alpha = 0; block.scaleX = block.scaleY = 1.5; block.destroy(); if (block.parent) block.parent.removeChild(block); blocks.splice(i, 1); i--; } else { 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; // --- Wall bounce tracking for powerup balls --- if (typeof self.leftWallHits === "undefined") self.leftWallHits = 0; if (typeof self.rightWallHits === "undefined") self.rightWallHits = 0; if (typeof self.wallBouncesNoBlock === "undefined") self.wallBouncesNoBlock = 0; if (typeof self.hasHitBlockSinceReset === "undefined") self.hasHitBlockSinceReset = false; var hitWall = false; // 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; self.leftWallHits++; hitWall = true; } if (self.x + self.radius > 2048) { self.x = 2048 - self.radius; self.vx = -self.vx; self.rightWallHits++; hitWall = true; } if (hitWall) { if (!self.hasHitBlockSinceReset) { self.wallBouncesNoBlock++; } } // If left or right wall hit more than 15 times without hitting any block, return ball to player if (self.wallBouncesNoBlock >= 15 && typeof paddle !== "undefined" && paddle) { self.x = paddle.x; self.y = paddle.y - paddle.height / 2 - self.radius - 10; self.vx = 0; self.vy = 0; self.sticky = true; self.leftWallHits = 0; self.rightWallHits = 0; self.wallBouncesNoBlock = 0; self.hasHitBlockSinceReset = false; } 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") { if (self.fireball || typeof paddle !== "undefined" && paddle && paddle.laserBeam) { block.hp = 0; if (block.updateHpBar) block.updateHpBar(); block.alpha = 0; block.scaleX = block.scaleY = 1.5; block.destroy(); if (block.parent) block.parent.removeChild(block); blocks.splice(i, 1); i--; } else { 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; // --- Wall bounce tracking for powerup balls --- if (typeof self.leftWallHits === "undefined") self.leftWallHits = 0; if (typeof self.rightWallHits === "undefined") self.rightWallHits = 0; if (typeof self.wallBouncesNoBlock === "undefined") self.wallBouncesNoBlock = 0; if (typeof self.hasHitBlockSinceReset === "undefined") self.hasHitBlockSinceReset = false; var hitWall = false; // 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; self.leftWallHits++; hitWall = true; } if (self.x + self.radius > 2048) { self.x = 2048 - self.radius; self.vx = -self.vx; self.rightWallHits++; hitWall = true; } if (hitWall) { if (!self.hasHitBlockSinceReset) { self.wallBouncesNoBlock++; } } // If left or right wall hit more than 15 times without hitting any block, return ball to player if (self.wallBouncesNoBlock >= 15 && typeof paddle !== "undefined" && paddle) { self.x = paddle.x; self.y = paddle.y - paddle.height / 2 - self.radius - 10; self.vx = 0; self.vy = 0; self.sticky = true; self.leftWallHits = 0; self.rightWallHits = 0; self.wallBouncesNoBlock = 0; self.hasHitBlockSinceReset = false; } 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") { if (self.fireball || typeof paddle !== "undefined" && paddle && paddle.laserBeam) { block.hp = 0; if (block.updateHpBar) block.updateHpBar(); block.alpha = 0; block.scaleX = block.scaleY = 1.5; block.destroy(); if (block.parent) block.parent.removeChild(block); blocks.splice(i, 1); i--; } else { 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 ****/ // Sounds // Power-up // Block colors // Paddle // Ball // Game variables function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } var paddle = null, ball = null, blocks = [], powerups = [], powerupBalls = []; // holds PowerupBall1/2/3 instances if (!window.extraBalls || !Array.isArray(window.extraBalls)) window.extraBalls = []; if (!window.activePowerups || !Array.isArray(window.activePowerups)) window.activePowerups = []; if (!window.powerupTimers || !Array.isArray(window.powerupTimers)) window.powerupTimers = []; if (!window.powerupIcons || !Array.isArray(window.powerupIcons)) window.powerupIcons = []; var lives = 3; var level = 1; var isBallLaunched = false; var dragPaddle = false; // Always reset score to zero at the start of each game and persist it var score = 0; 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); // Show current level and pattern chars if available if (typeof charArr !== "undefined" && charArr.length) { levelTxt.setText('Level ' + level + ': ' + charArr.join(' ')); } else { 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; // Reduce margin as level increases for more density var marginX = Math.max(10, 40 - level * 2); var marginY = Math.max(10, 40 - level * 2); var colors = ['red', 'green', 'blue', 'yellow']; // --- New: Generate block positions in a grid to form random letters and/or numbers side by side per level --- // Returns an array of {x, y} positions for blocks, forming multiple random letters/numbers in a row function getPatternBlockPositions(level, blockW, blockH) { // Define 5x7 bitmap font for A-Z, 0-9 (1=block, 0=empty) var font = { "A": [[0, 1, 1, 1, 0], [1, 0, 0, 0, 1], [1, 0, 0, 0, 1], [1, 1, 1, 1, 1], [1, 0, 0, 0, 1], [1, 0, 0, 0, 1], [1, 0, 0, 0, 1]], "B": [[1, 1, 1, 1, 0], [1, 0, 0, 0, 1], [1, 0, 0, 0, 1], [1, 1, 1, 1, 0], [1, 0, 0, 0, 1], [1, 0, 0, 0, 1], [1, 1, 1, 1, 0]], "C": [[0, 1, 1, 1, 1], [1, 0, 0, 0, 0], [1, 0, 0, 0, 0], [1, 0, 0, 0, 0], [1, 0, 0, 0, 0], [1, 0, 0, 0, 0], [0, 1, 1, 1, 1]], "D": [[1, 1, 1, 0, 0], [1, 0, 0, 1, 0], [1, 0, 0, 0, 1], [1, 0, 0, 0, 1], [1, 0, 0, 0, 1], [1, 0, 0, 1, 0], [1, 1, 1, 0, 0]], "E": [[1, 1, 1, 1, 1], [1, 0, 0, 0, 0], [1, 0, 0, 0, 0], [1, 1, 1, 1, 0], [1, 0, 0, 0, 0], [1, 0, 0, 0, 0], [1, 1, 1, 1, 1]], "F": [[1, 1, 1, 1, 1], [1, 0, 0, 0, 0], [1, 0, 0, 0, 0], [1, 1, 1, 1, 0], [1, 0, 0, 0, 0], [1, 0, 0, 0, 0], [1, 0, 0, 0, 0]], "G": [[0, 1, 1, 1, 1], [1, 0, 0, 0, 0], [1, 0, 0, 0, 0], [1, 0, 1, 1, 1], [1, 0, 0, 0, 1], [1, 0, 0, 0, 1], [0, 1, 1, 1, 1]], "H": [[1, 0, 0, 0, 1], [1, 0, 0, 0, 1], [1, 0, 0, 0, 1], [1, 1, 1, 1, 1], [1, 0, 0, 0, 1], [1, 0, 0, 0, 1], [1, 0, 0, 0, 1]], "I": [[1, 1, 1, 1, 1], [0, 0, 1, 0, 0], [0, 0, 1, 0, 0], [0, 0, 1, 0, 0], [0, 0, 1, 0, 0], [0, 0, 1, 0, 0], [1, 1, 1, 1, 1]], "J": [[0, 0, 0, 1, 1], [0, 0, 0, 0, 1], [0, 0, 0, 0, 1], [0, 0, 0, 0, 1], [1, 0, 0, 0, 1], [1, 0, 0, 0, 1], [0, 1, 1, 1, 0]], "K": [[1, 0, 0, 0, 1], [1, 0, 0, 1, 0], [1, 0, 1, 0, 0], [1, 1, 0, 0, 0], [1, 0, 1, 0, 0], [1, 0, 0, 1, 0], [1, 0, 0, 0, 1]], "L": [[1, 0, 0, 0, 0], [1, 0, 0, 0, 0], [1, 0, 0, 0, 0], [1, 0, 0, 0, 0], [1, 0, 0, 0, 0], [1, 0, 0, 0, 0], [1, 1, 1, 1, 1]], "M": [[1, 0, 0, 0, 1], [1, 1, 0, 1, 1], [1, 0, 1, 0, 1], [1, 0, 0, 0, 1], [1, 0, 0, 0, 1], [1, 0, 0, 0, 1], [1, 0, 0, 0, 1]], "N": [[1, 0, 0, 0, 1], [1, 1, 0, 0, 1], [1, 0, 1, 0, 1], [1, 0, 0, 1, 1], [1, 0, 0, 0, 1], [1, 0, 0, 0, 1], [1, 0, 0, 0, 1]], "O": [[0, 1, 1, 1, 0], [1, 0, 0, 0, 1], [1, 0, 0, 0, 1], [1, 0, 0, 0, 1], [1, 0, 0, 0, 1], [1, 0, 0, 0, 1], [0, 1, 1, 1, 0]], "P": [[1, 1, 1, 1, 0], [1, 0, 0, 0, 1], [1, 0, 0, 0, 1], [1, 1, 1, 1, 0], [1, 0, 0, 0, 0], [1, 0, 0, 0, 0], [1, 0, 0, 0, 0]], "Q": [[0, 1, 1, 1, 0], [1, 0, 0, 0, 1], [1, 0, 0, 0, 1], [1, 0, 0, 0, 1], [1, 0, 1, 0, 1], [1, 0, 0, 1, 0], [0, 1, 1, 0, 1]], "R": [[1, 1, 1, 1, 0], [1, 0, 0, 0, 1], [1, 0, 0, 0, 1], [1, 1, 1, 1, 0], [1, 0, 1, 0, 0], [1, 0, 0, 1, 0], [1, 0, 0, 0, 1]], "S": [[0, 1, 1, 1, 1], [1, 0, 0, 0, 0], [1, 0, 0, 0, 0], [0, 1, 1, 1, 0], [0, 0, 0, 0, 1], [0, 0, 0, 0, 1], [1, 1, 1, 1, 0]], "T": [[1, 1, 1, 1, 1], [0, 0, 1, 0, 0], [0, 0, 1, 0, 0], [0, 0, 1, 0, 0], [0, 0, 1, 0, 0], [0, 0, 1, 0, 0], [0, 0, 1, 0, 0]], "U": [[1, 0, 0, 0, 1], [1, 0, 0, 0, 1], [1, 0, 0, 0, 1], [1, 0, 0, 0, 1], [1, 0, 0, 0, 1], [1, 0, 0, 0, 1], [0, 1, 1, 1, 0]], "V": [[1, 0, 0, 0, 1], [1, 0, 0, 0, 1], [1, 0, 0, 0, 1], [1, 0, 0, 0, 1], [0, 1, 0, 1, 0], [0, 1, 0, 1, 0], [0, 0, 1, 0, 0]], "W": [[1, 0, 0, 0, 1], [1, 0, 0, 0, 1], [1, 0, 0, 0, 1], [1, 0, 1, 0, 1], [1, 1, 0, 1, 1], [1, 1, 0, 1, 1], [1, 0, 0, 0, 1]], "X": [[1, 0, 0, 0, 1], [1, 0, 0, 0, 1], [0, 1, 0, 1, 0], [0, 0, 1, 0, 0], [0, 1, 0, 1, 0], [1, 0, 0, 0, 1], [1, 0, 0, 0, 1]], "Y": [[1, 0, 0, 0, 1], [1, 0, 0, 0, 1], [0, 1, 0, 1, 0], [0, 0, 1, 0, 0], [0, 0, 1, 0, 0], [0, 0, 1, 0, 0], [0, 0, 1, 0, 0]], "Z": [[1, 1, 1, 1, 1], [0, 0, 0, 0, 1], [0, 0, 0, 1, 0], [0, 0, 1, 0, 0], [0, 1, 0, 0, 0], [1, 0, 0, 0, 0], [1, 1, 1, 1, 1]], "0": [[0, 1, 1, 1, 0], [1, 0, 0, 0, 1], [1, 0, 1, 0, 1], [1, 0, 0, 0, 1], [1, 0, 0, 0, 1], [1, 0, 0, 0, 1], [0, 1, 1, 1, 0]], "1": [[0, 0, 1, 0, 0], [0, 1, 1, 0, 0], [1, 0, 1, 0, 0], [0, 0, 1, 0, 0], [0, 0, 1, 0, 0], [0, 0, 1, 0, 0], [1, 1, 1, 1, 1]], "2": [[0, 1, 1, 1, 0], [1, 0, 0, 0, 1], [0, 0, 0, 0, 1], [0, 0, 0, 1, 0], [0, 0, 1, 0, 0], [0, 1, 0, 0, 0], [1, 1, 1, 1, 1]], "3": [[1, 1, 1, 1, 0], [0, 0, 0, 0, 1], [0, 0, 0, 1, 0], [0, 0, 1, 1, 0], [0, 0, 0, 0, 1], [1, 0, 0, 0, 1], [0, 1, 1, 1, 0]], "4": [[0, 0, 0, 1, 0], [0, 0, 1, 1, 0], [0, 1, 0, 1, 0], [1, 0, 0, 1, 0], [1, 1, 1, 1, 1], [0, 0, 0, 1, 0], [0, 0, 0, 1, 0]], "5": [[1, 1, 1, 1, 1], [1, 0, 0, 0, 0], [1, 1, 1, 1, 0], [0, 0, 0, 0, 1], [0, 0, 0, 0, 1], [1, 0, 0, 0, 1], [0, 1, 1, 1, 0]], "6": [[0, 1, 1, 1, 0], [1, 0, 0, 0, 1], [1, 0, 0, 0, 0], [1, 1, 1, 1, 0], [1, 0, 0, 0, 1], [1, 0, 0, 0, 1], [0, 1, 1, 1, 0]], "7": [[1, 1, 1, 1, 1], [0, 0, 0, 0, 1], [0, 0, 0, 1, 0], [0, 0, 1, 0, 0], [0, 1, 0, 0, 0], [0, 1, 0, 0, 0], [0, 1, 0, 0, 0]], "8": [[0, 1, 1, 1, 0], [1, 0, 0, 0, 1], [1, 0, 0, 0, 1], [0, 1, 1, 1, 0], [1, 0, 0, 0, 1], [1, 0, 0, 0, 1], [0, 1, 1, 1, 0]], "9": [[0, 1, 1, 1, 0], [1, 0, 0, 0, 1], [1, 0, 0, 0, 1], [0, 1, 1, 1, 1], [0, 0, 0, 0, 1], [1, 0, 0, 0, 1], [0, 1, 1, 1, 0]] }; // Decide how many chars to show side by side (1-6, more as level increases) var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; var numChars = 1 + Math.floor((level - 1) / 2); numChars = Math.min(numChars, 6); // max 6 chars side by side for high difficulty // Pick random chars for this level (can be letter or number, allow repeats) var charArr = []; for (var i = 0; i < numChars; i++) { var idx = Math.floor(Math.random() * chars.length); charArr.push(chars[idx]); } // If level 1, always show a single letter for clarity if (level === 1) charArr = [chars[0]]; // If no pattern found for any char, fallback to random grid var allPatternsExist = true; for (var i = 0; i < charArr.length; i++) { if (!font[charArr[i]]) { allPatternsExist = false; break; } } if (!allPatternsExist) { // fallback: random grid var positions = []; var rows = 7, cols = 5 * numChars + (numChars - 1); // wider grid for more chars var totalW = cols * blockW + (cols - 1) * 20; var totalH = rows * blockH + (rows - 1) * 20; var startX = (2048 - totalW) / 2 + blockW / 2; var startY = 400; for (var r = 0; r < rows; r++) { for (var c = 0; c < cols; c++) { if (Math.random() < 0.7) { positions.push({ x: startX + c * (blockW + 20), y: startY + r * (blockH + 20) }); } } } return positions; } // Center the pattern(s) in the play area var rows = 7; var colsPerChar = 5; var gapCols = 1; // 1 column gap between chars var totalCols = numChars * colsPerChar + (numChars - 1) * gapCols; var totalW = totalCols * blockW + (totalCols - 1) * marginX; var totalH = rows * blockH + (rows - 1) * marginY; var startX = (2048 - totalW) / 2 + blockW / 2; var startY = 400; var positions = []; for (var ch = 0; ch < charArr.length; ch++) { var pattern = font[charArr[ch]]; for (var r = 0; r < rows; r++) { for (var c = 0; c < colsPerChar; c++) { if (pattern[r][c]) { // Calculate x offset for this char var charOffset = ch * (colsPerChar + gapCols) * (blockW + marginX); positions.push({ x: startX + charOffset + c * (blockW + marginX), y: startY + r * (blockH + marginY) }); } } } } return positions; } // Only normal blocks, no shape/orientation logic function getShapePattern(level, rows, cols) { // Not used anymore, but kept for compatibility var arr = []; for (var r = 0; r < rows; r++) { arr[r] = []; for (var c = 0; c < cols; c++) { arr[r][c] = { type: 'normal', present: true }; } } return arr; } // 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 // --- OPTIMIZATION: For level 6+, reduce number of blocks per row/col to improve performance --- var rows, cols; if (level >= 6) { rows = Math.min(2 + Math.floor(level / 3), 7); cols = Math.min(4 + Math.floor(level / 4), 9); } else { rows = Math.min(3 + Math.floor(level / 2), 10); 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; // --- New: get block layout in a random letter/number pattern for this level --- var blockPositions = getPatternBlockPositions(level, blockW, blockH); // --- OPTIMIZATION: Batch block creation to avoid frame stutter at level start --- var blocksToAdd = []; for (var i = 0; i < blockPositions.length; i++) { var block = new Block(); var color = colors[(i + level) % colors.length]; block.setColor(color); block.blockType = 'normal'; block.blockSprite.height = blockH; block.blockSprite.width = blockW; block.blockSprite.rotation = 0; block.x = blockPositions[i].x; block.y = blockPositions[i].y; // Increase block HP as level increases (scales faster with level) // Difficulty scaling: more chars, more HP, less margin, more blocks as level increases var hpBonus = Math.floor(level / 2) + Math.floor(level / 5); block.hp = 1 + Math.floor(level / 3) + Math.floor(i / 4) + hpBonus; block.maxHp = block.hp; block.setHp(block.hp, block.maxHp); blocksToAdd.push(block); } // Add all blocks to game and blocks array in a single batch to reduce layout thrashing for (var i = 0; i < blocksToAdd.length; i++) { game.addChild(blocksToAdd[i]); blocks.push(blocksToAdd[i]); } } // 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(); // Fun, engaging, and bug-free powerup types (50+) // Removed boring/annoying types, added creative/fun ones, and ensured all are bug-free var workingTypes = [ // Paddle/ball size & speed 'expand', 'shrink', 'bigball', 'smallball', 'paddleup', 'paddledown', 'paddlefast', 'paddleslow', // Ball count 'multiball', 'ballsplit', 'ballclone', '+1ball', '+2ball', '+3ball', // Score 'scorex2', 'scorex3', // Block effects 'blockbomb', 'blockrowclear', 'blockcolclear', 'blockheal', 'blockrandom', 'blockmove', 'blockshrink', 'blockexpand', 'blockshield', 'blockswap', 'blockteleport', 'blockregen', 'blockexplode', 'blockinvisible', // Ball physics 'slow', 'fast', 'ballheavy', 'balllight', 'ballcurve', 'ballzigzag', 'ballrandom', 'ballteleport', 'ballinvisible', 'superball', // Paddle physics 'ghost', 'paddleghost', 'paddleleft', 'paddleright', 'paddleteleport', 'magnet', // Special/fun 'sticky', 'life', // MASSIVELY increase laser and fireball frequency: 'laser', 'laser', 'laser', 'laser', 'laser', 'laser', 'laser', 'laser', 'laser', 'laser', 'laser', 'laser', 'laser', 'laser', 'laser', 'laser', 'laser', 'laser', 'laser', 'laser', 'fireball', 'fireball', 'fireball', 'fireball', 'fireball', 'fireball', 'fireball', 'fireball', 'fireball', 'fireball', 'fireball', 'fireball', 'fireball', 'fireball', 'fireball', 'fireball', 'fireball', 'fireball', 'fireball', 'fireball', // Other powerups 'iceball', 'portal', 'gravity', 'mirror', 'shrinkall', 'expandall', 'swapall', 'rainbow', 'cloneblock', 'blockfall', 'blockrise', 'blockrain', 'blockquake', 'blockfreeze', 'blockflash', 'blockmirror', 'blockportal', 'blockgravity', 'blockmagnet', 'blocklaser', 'blockfire', 'blockice', 'blockghost', 'blocksuper', 'blockrandomhp', 'blockbonus', 'blocktrap', 'blockshieldall', 'blockswapall', 'blockteleportall', 'blockregenall', 'blockexplodeall']; // 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); if (!powerups || !Array.isArray(powerups)) powerups = []; powerups.push(power); } // Helper: next level function nextLevel() { level++; lives = Math.min(5, lives + 1); // Add 1 life, max 5 // --- Difficulty scaling: increase ball speed, block HP, and decrease powerup spawn interval --- if (ball) { // Increase main ball speed a bit every level, up to a max ball.speed = Math.min(22 + level * 1.2, 60); // If ball is moving, scale its velocity to new speed var speed = Math.sqrt(ball.vx * ball.vx + ball.vy * ball.vy); if (speed > 0) { var scale = ball.speed / speed; ball.vx *= scale; ball.vy *= scale; } } // Store global difficulty for use in spawnBlocks and powerup logic window.difficultyLevel = level; // Decrease powerup spawn interval (min 120 ticks) game.powerupSpawnInterval = Math.max(120, 360 - level * 12); // Clear combo timer on level up if (comboTimer) { LK.clearTimeout(comboTimer); comboTimer = null; } // Remove all falling powerup balls if (!powerupBalls || !Array.isArray(powerupBalls)) powerupBalls = []; if (powerupBalls && powerupBalls.length) { for (var i = powerupBalls.length - 1; i >= 0; i--) { if (powerupBalls[i]) powerupBalls[i].destroy(); powerupBalls.splice(i, 1); } } // Remove all extra balls if (!window.extraBalls || !Array.isArray(window.extraBalls)) window.extraBalls = []; 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); } } updateGUI(); spawnBlocks(); spawnPaddle(); spawnBall(); levelCleared = false; // Clear all powerup timers and arrays if (window.powerupTimers && Array.isArray(window.powerupTimers)) { for (var t = 0; t < window.powerupTimers.length; t++) { if (window.powerupTimers[t]) LK.clearTimeout(window.powerupTimers[t]); } window.powerupTimers = []; } if (window.activePowerups && Array.isArray(window.activePowerups)) window.activePowerups = []; if (window.powerupIcons && Array.isArray(window.powerupIcons)) { for (var i = 0; i < window.powerupIcons.length; i++) { if (window.powerupIcons[i] && typeof window.powerupIcons[i].destroy === "function") window.powerupIcons[i].destroy(); } window.powerupIcons = []; } } // 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 || !Array.isArray(powerupBalls)) powerupBalls = []; 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 // Clear all powerup timers and arrays if (window.powerupTimers && Array.isArray(window.powerupTimers)) { for (var t = 0; t < window.powerupTimers.length; t++) { if (window.powerupTimers[t]) LK.clearTimeout(window.powerupTimers[t]); } window.powerupTimers = []; } if (window.activePowerups && Array.isArray(window.activePowerups)) window.activePowerups = []; if (window.powerupIcons && Array.isArray(window.powerupIcons)) { for (var i = 0; i < window.powerupIcons.length; i++) { if (window.powerupIcons[i] && typeof window.powerupIcons[i].destroy === "function") window.powerupIcons[i].destroy(); } window.powerupIcons = []; } 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 || !Array.isArray(window.extraBalls)) window.extraBalls = []; 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); } } window.extraBalls = []; spawnPaddle(); spawnBall(); } // Helper: win function winGame() { LK.effects.flashScreen(0x00ff00, 1000); // Defensive: ensure arrays exist before clearing if (!window.powerupTimers || !Array.isArray(window.powerupTimers)) window.powerupTimers = []; if (!window.activePowerups || !Array.isArray(window.activePowerups)) window.activePowerups = []; if (!window.powerupIcons || !Array.isArray(window.powerupIcons)) window.powerupIcons = []; // Clear all powerup timers and arrays for (var t = 0; t < window.powerupTimers.length; t++) { if (window.powerupTimers[t]) LK.clearTimeout(window.powerupTimers[t]); } window.powerupTimers = []; window.activePowerups = []; for (var i = 0; i < window.powerupIcons.length; i++) { if (window.powerupIcons[i] && typeof window.powerupIcons[i].destroy === "function") window.powerupIcons[i].destroy(); } window.powerupIcons = []; LK.showYouWin(); } // Initialize first level highScore = storage.highScore || 0; // Always start with score zero, persistently score = 0; storage.score = 0; highScoreTxt.setText('High: ' + highScore); window.extraBalls = []; // Clear all powerup timers and arrays at game start if (window.powerupTimers && Array.isArray(window.powerupTimers)) { for (var t = 0; t < window.powerupTimers.length; t++) { if (window.powerupTimers[t]) LK.clearTimeout(window.powerupTimers[t]); } window.powerupTimers = []; } if (window.activePowerups && Array.isArray(window.activePowerups)) window.activePowerups = []; if (window.powerupIcons && Array.isArray(window.powerupIcons)) { for (var i = 0; i < window.powerupIcons.length; i++) { if (window.powerupIcons[i] && typeof window.powerupIcons[i].destroy === "function") window.powerupIcons[i].destroy(); } window.powerupIcons = []; } spawnBlocks(); spawnPaddle(); spawnBall(); updateGUI(); // Touch controls game.down = function (x, y, obj) { // Launch ball if it's sticky and user taps in lower 40% of screen if (y > 2732 * 0.6 && ballStickyToPaddle) { // 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) { // Always move paddle horizontally with finger/mouse X, no need to hold down var targetX = x; // Use a weighted average for smoothing (0.7 current, 0.3 target) if (typeof paddle.x === "number") { paddle.moveTo(paddle.x * 0.7 + targetX * 0.3); } else { paddle.moveTo(targetX); } // If ball is sticky, move it with paddle if (ballStickyToPaddle) { ball.x = paddle.x; } }; game.up = function (x, y, obj) { // No need to set dragPaddle, paddle always follows move }; // Main update loop game.update = function () { // Ball update if (ball) ball.update(); // --- OPTIMIZATION: Only update extra balls if any exist --- if (!window.extraBalls || !Array.isArray(window.extraBalls)) window.extraBalls = []; if (window.extraBalls.length) { // Cache paddle hitbox if paddle exists and is not moving var cachedPaddleHitbox = typeof paddle !== "undefined" && paddle && typeof paddle.getHitbox === "function" ? paddle.getHitbox() : null; // --- OPTIMIZATION: For level 6+, only update every other extra ball per frame --- var ebStep = level >= 6 ? 2 : 1; for (var eb = window.extraBalls.length - 1; eb >= 0; eb -= ebStep) { var ebBall = window.extraBalls[eb]; if (ebBall && ebBall.update) ebBall.update(); // --- Track left/right wall bounces for extra balls --- if (typeof ebBall.leftWallHits === "undefined") ebBall.leftWallHits = 0; if (typeof ebBall.rightWallHits === "undefined") ebBall.rightWallHits = 0; if (typeof ebBall.wallBouncesNoBlock === "undefined") ebBall.wallBouncesNoBlock = 0; if (typeof ebBall.hasHitBlockSinceReset === "undefined") ebBall.hasHitBlockSinceReset = false; var ebHitWall = false; if (ebBall.x - ebBall.radius < 0) { ebBall.x = ebBall.radius; ebBall.vx = -ebBall.vx; LK.getSound('hit').play(); ebBall.leftWallHits++; ebHitWall = true; } if (ebBall.x + ebBall.radius > 2048) { ebBall.x = 2048 - ebBall.radius; ebBall.vx = -ebBall.vx; LK.getSound('hit').play(); ebBall.rightWallHits++; ebHitWall = true; } if (ebHitWall) { if (!ebBall.hasHitBlockSinceReset) { ebBall.wallBouncesNoBlock++; } } // If left or right wall hit more than 15 times without hitting any block, return ball to player if (ebBall.wallBouncesNoBlock >= 15) { ebBall.x = paddle.x; ebBall.y = paddle.y - paddle.height / 2 - ebBall.radius - 10; ebBall.vx = 0; ebBall.vy = 0; ebBall.sticky = true; ebBall.leftWallHits = 0; ebBall.rightWallHits = 0; ebBall.wallBouncesNoBlock = 0; ebBall.hasHitBlockSinceReset = false; } // Remove if off screen if (ebBall && ebBall.y - ebBall.radius > 2732) { ebBall.destroy(); window.extraBalls.splice(eb, 1); continue; } // Paddle collision for extra balls (skip if paddle not present) if (ebBall && cachedPaddleHitbox) { if (ebBall.y + ebBall.radius >= cachedPaddleHitbox.top && ebBall.y - ebBall.radius <= cachedPaddleHitbox.bottom && ebBall.x + ebBall.radius >= cachedPaddleHitbox.left && ebBall.x - ebBall.radius <= cachedPaddleHitbox.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(); } } } } // --- OPTIMIZATION: Only update powerup balls if any exist --- if (!powerupBalls || !Array.isArray(powerupBalls)) powerupBalls = []; if (powerupBalls && powerupBalls.length) { var cachedPaddleHitboxPB = typeof paddle !== "undefined" && paddle && typeof paddle.getHitbox === "function" ? paddle.getHitbox() : null; // --- OPTIMIZATION: For level 6+, only update every other powerup ball per frame --- var pbStep = level >= 6 ? 2 : 1; for (var pbIdx = powerupBalls.length - 1; pbIdx >= 0; pbIdx -= pbStep) { var pbBall = powerupBalls[pbIdx]; if (pbBall && pbBall.update) pbBall.update(); // --- Track left/right wall bounces for powerup balls --- if (typeof pbBall.leftWallHits === "undefined") pbBall.leftWallHits = 0; if (typeof pbBall.rightWallHits === "undefined") pbBall.rightWallHits = 0; if (typeof pbBall.wallBouncesNoBlock === "undefined") pbBall.wallBouncesNoBlock = 0; if (typeof pbBall.hasHitBlockSinceReset === "undefined") pbBall.hasHitBlockSinceReset = false; var pbHitWall = false; if (pbBall.x - pbBall.radius < 0) { pbBall.x = pbBall.radius; pbBall.vx = -pbBall.vx; LK.getSound('hit').play(); pbBall.leftWallHits++; pbHitWall = true; } if (pbBall.x + pbBall.radius > 2048) { pbBall.x = 2048 - pbBall.radius; pbBall.vx = -pbBall.vx; LK.getSound('hit').play(); pbBall.rightWallHits++; pbHitWall = true; } if (pbHitWall) { if (!pbBall.hasHitBlockSinceReset) { pbBall.wallBouncesNoBlock++; } } // If left or right wall hit more than 15 times without hitting any block, return ball to player if (pbBall.wallBouncesNoBlock >= 15) { pbBall.x = paddle.x; pbBall.y = paddle.y - paddle.height / 2 - pbBall.radius - 10; pbBall.vx = 0; pbBall.vy = 0; pbBall.sticky = true; pbBall.leftWallHits = 0; pbBall.rightWallHits = 0; pbBall.wallBouncesNoBlock = 0; pbBall.hasHitBlockSinceReset = false; } // Remove if off screen if (pbBall && pbBall.y - pbBall.radius > 2732) { pbBall.destroy(); powerupBalls.splice(pbIdx, 1); continue; } // Paddle collision for powerup balls (skip if paddle not present) if (pbBall && cachedPaddleHitboxPB) { if (pbBall.y + pbBall.radius >= cachedPaddleHitboxPB.top && pbBall.y - pbBall.radius <= cachedPaddleHitboxPB.bottom && pbBall.x + pbBall.radius >= cachedPaddleHitboxPB.left && pbBall.x - pbBall.radius <= cachedPaddleHitboxPB.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 // --- OPTIMIZATION: For level 6+, only update every other powerup per frame --- var powerupStep = level >= 6 ? 2 : 1; for (var i = powerups.length - 1; i >= 0; i -= powerupStep) { var p = powerups[i]; // OPTIMIZATION: Skip destroyed/invalid powerups immediately if (!p || typeof p.update !== "function" || p.destroyed) { powerups.splice(i, 1); continue; } // Defensive: Only call update if p is valid and not destroyed try { if (p && typeof p.update === "function" && !p.destroyed) { p.update(); } } catch (e) { // Remove problematic powerup to prevent further errors powerups.splice(i, 1); continue; } // 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; } } } // No level restriction: powerups are always collectible if (collected) { // --- Powerup balls update and collection --- } // --- OPTIMIZATION: Remove powerup ball update from this loop, handled above --- 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 || !Array.isArray(window.activePowerups)) window.activePowerups = []; if (!window.powerupTimers || !Array.isArray(window.powerupTimers)) window.powerupTimers = []; if (!window.powerupIcons || !Array.isArray(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 removed 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 === '+1ball') { // Add 1 extra ball if (!window.extraBalls) window.extraBalls = []; if (window.extraBalls.length >= 4) return; var newBall = new Ball(); newBall.x = ball.x; newBall.y = ball.y; // Give a random direction, but not too vertical var angle = -Math.PI / 4 + Math.random() * (Math.PI / 2); // -45 to +45 deg var speed = ball.speed || 22; newBall.vx = Math.cos(angle) * speed; newBall.vy = Math.sin(angle) * speed; newBall.sticky = false; game.addChild(newBall); window.extraBalls.push(newBall); } else if (p.type === '+2ball') { // Add 2 extra balls if (!window.extraBalls) window.extraBalls = []; var ballsToAdd = Math.min(2, 4 - window.extraBalls.length); for (var b = 0; b < ballsToAdd; b++) { var newBall = new Ball(); newBall.x = ball.x; newBall.y = ball.y; // Spread angles var angle = -Math.PI / 3 + b * (Math.PI / 3); // -60, 0 deg var speed = ball.speed || 22; newBall.vx = Math.cos(angle) * speed; newBall.vy = Math.sin(angle) * speed; newBall.sticky = false; game.addChild(newBall); window.extraBalls.push(newBall); } } else if (p.type === '+3ball') { // Add 3 extra balls if (!window.extraBalls) window.extraBalls = []; var ballsToAdd = Math.min(3, 4 - window.extraBalls.length); for (var b = 0; b < ballsToAdd; b++) { var newBall = new Ball(); newBall.x = ball.x; newBall.y = ball.y; // Spread angles var angle = -Math.PI / 3 + b * (Math.PI / 3) / (ballsToAdd > 1 ? ballsToAdd - 1 : 1); // -60, 0, +60 deg var speed = ball.speed || 22; newBall.vx = Math.cos(angle) * speed; newBall.vy = Math.sin(angle) * speed; newBall.sticky = false; game.addChild(newBall); window.extraBalls.push(newBall); } } else 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) if (!window.extraBalls) window.extraBalls = []; // Prevent more than 5 balls at once if (window.extraBalls.length >= 4) return; // Only add if not already present at this position (avoid duplicate bug) var alreadyExists = false; for (var i = 0; i < window.extraBalls.length; i++) { var eb = window.extraBalls[i]; if (eb && Math.abs(eb.x - ball.x) < 2 && Math.abs(eb.y - ball.y) < 2) { alreadyExists = true; break; } } if (!alreadyExists) { 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); 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(); // Remove block from game scene to prevent ghost graphics if (blocks[idx].parent) blocks[idx].parent.removeChild(blocks[idx]); 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(); // Remove block from game scene to prevent ghost graphics if (blocks[row[j]].parent) blocks[row[j]].parent.removeChild(blocks[row[j]]); 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(); // Remove block from game scene to prevent ghost graphics if (blocks[col[j]].parent) blocks[col[j]].parent.removeChild(blocks[col[j]]); 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(); // Remove block from game scene to prevent ghost graphics if (blocks[idx].parent) blocks[idx].parent.removeChild(blocks[idx]); 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 === 'laser') { // Laser powerup: show a red laser beam from paddle for a short time if (!paddle.laserBeam) { // Create a red rectangle as the laser beam var laserWidth = paddle.width * (paddle.scaleX || 1) * 0.25; var laserHeight = paddle.y - paddle.height / 2; // from paddle to very top var laser = LK.getAsset('block_red', { anchorX: 0.5, anchorY: 1, width: laserWidth, height: laserHeight, x: paddle.x, y: paddle.y - paddle.height / 2, alpha: 0.7 }); laser.zIndex = 1000; paddle.laserBeam = laser; game.addChild(laser); // Animate laser alpha in and out tween(laser, { alpha: 0.9 }, { duration: 120, easing: tween.easeIn, onFinish: function onFinish() { tween(laser, { alpha: 0.0 }, { duration: 400, delay: 400, easing: tween.easeOut, onFinish: function onFinish() { if (laser && laser.parent) laser.parent.removeChild(laser); paddle.laserBeam = null; } }); } }); // Destroy all blocks in the laser's vertical path, with explosion effect for (var i = blocks.length - 1; i >= 0; i--) { var block = blocks[i]; if (block && block.x >= paddle.x - laserWidth / 2 && block.x <= paddle.x + laserWidth / 2 && block.y < paddle.y - paddle.height / 2 // only above paddle ) { // Instantly destroy block (no animation) block.hp = 0; if (block.updateHpBar) block.updateHpBar(); block.alpha = 0; block.scaleX = block.scaleY = 2.2; block.destroy(); if (block.parent) block.parent.removeChild(block); blocks.splice(i, 1); score += 10; updateGUI(); LK.getSound('break').play(); } } } // Removed 'reverse' (ters kontrol) powerup effect } 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 === 'fireball') { // Fireball: Ball burns through blocks for a while, destroys all blocks it touches, and shows fire effect ball.fireball = true; // Add a fire visual effect to the ball if (!ball.fireEffect) { ball.fireEffect = ball.attachAsset('ball', { anchorX: 0.5, anchorY: 0.5, width: ball.width * 1.5, height: ball.height * 1.5, tint: 0xff6600, alpha: 0.5 }); ball.fireEffect.zIndex = 99; } // Animate fire effect (pulsing) if (!ball.fireTween) { ball.fireTween = tween(ball.fireEffect, { alpha: 0.2 }, { duration: 300, yoyo: true, repeat: 10, onFinish: function onFinish() { if (ball.fireEffect) ball.fireEffect.alpha = 0.5; } }); } LK.setTimeout(function () { ball.fireball = false; if (ball.fireEffect) { ball.removeChild(ball.fireEffect); ball.fireEffect = null; } if (ball.fireTween) { ball.fireTween.stop(); ball.fireTween = null; } }, 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') { // All balls (main + extra) invisible for a while if (ball) { ball.alpha = 0.1; LK.setTimeout(function () { if (ball) ball.alpha = 1; }, 6000); } if (window.extraBalls && window.extraBalls.length) { for (var eb = 0; eb < window.extraBalls.length; eb++) { if (window.extraBalls[eb]) { window.extraBalls[eb].alpha = 0.1; (function (ebBall) { LK.setTimeout(function () { if (ebBall) ebBall.alpha = 1; }, 6000); })(window.extraBalls[eb]); } } } if (powerupBalls && powerupBalls.length) { for (var pb = 0; pb < powerupBalls.length; pb++) { if (powerupBalls[pb]) { powerupBalls[pb].alpha = 0.1; (function (pbBall) { LK.setTimeout(function () { if (pbBall) pbBall.alpha = 1; }, 6000); })(powerupBalls[pb]); } } } } p.destroy(); powerups.splice(i, 1); } } } // Ball physics if (ball && _typeof(ball) === "object" && !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 15 or more times without hitting any block, make ALL balls accelerate upward even faster if (ball.wallBouncesNoBlock >= 15) { var accelerateUpwardFaster = function accelerateUpwardFaster(b) { // Only accelerate if not already moving extremely fast upward if (b.vy > -Math.abs(b.speed ? b.speed * 20 : 440)) { b.vx = 0; b.vy = -Math.abs(b.speed ? b.speed * 20 : 440); // Use 20x speed or 440 as fallback b.sticky = false; } if (typeof b.leftWallHits !== "undefined") b.leftWallHits = 0; if (typeof b.rightWallHits !== "undefined") b.rightWallHits = 0; if (typeof b.wallBouncesNoBlock !== "undefined") b.wallBouncesNoBlock = 0; if (typeof b.hasHitBlockSinceReset !== "undefined") b.hasHitBlockSinceReset = false; }; // Main ball if (ball) accelerateUpwardFaster(ball); // Extra balls if (window.extraBalls && window.extraBalls.length) { for (var eb = 0; eb < window.extraBalls.length; eb++) { if (window.extraBalls[eb]) accelerateUpwardFaster(window.extraBalls[eb]); } } // Powerup balls if (powerupBalls && powerupBalls.length) { for (var pb = 0; pb < powerupBalls.length; pb++) { if (powerupBalls[pb]) accelerateUpwardFaster(powerupBalls[pb]); } } } else if (ball.wallBouncesNoBlock >= 10) { // Helper to accelerate a ball upward extremely fast var shootUpward = function shootUpward(b) { // Only accelerate if not already moving extremely fast upward if (b.vy > -Math.abs(b.speed ? b.speed * 30 : 660)) { b.vx = 0; b.vy = -Math.abs(b.speed ? b.speed * 30 : 660); // Use 30x speed or 660 as fallback b.sticky = false; } if (typeof b.leftWallHits !== "undefined") b.leftWallHits = 0; if (typeof b.rightWallHits !== "undefined") b.rightWallHits = 0; if (typeof b.wallBouncesNoBlock !== "undefined") b.wallBouncesNoBlock = 0; if (typeof b.hasHitBlockSinceReset !== "undefined") b.hasHitBlockSinceReset = false; }; // Main ball if (ball) shootUpward(ball); // Extra balls if (window.extraBalls && window.extraBalls.length) { for (var eb = 0; eb < window.extraBalls.length; eb++) { if (window.extraBalls[eb]) shootUpward(window.extraBalls[eb]); } } // Powerup balls if (powerupBalls && powerupBalls.length) { for (var pb = 0; pb < powerupBalls.length; pb++) { if (powerupBalls[pb]) shootUpward(powerupBalls[pb]); } } } // Paddle collision if (paddle && 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; // Always place ball visually above paddle after collision ball.y = paddle.y - paddle.height / 2 - ball.radius - 2; if (ball.y + ball.radius > hitbox.top) { 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 if (!blocks || !Array.isArray(blocks)) blocks = []; // --- OPTIMIZATION: For level 6+, only check a subset of blocks per frame to reduce lag --- var blockCheckStep = level >= 6 ? 2 : 1; for (var i = blocks.length - 1; i >= 0; i -= blockCheckStep) { var block = blocks[i]; // OPTIMIZATION: Skip destroyed or off-screen blocks if (!block || block.destroyed || block.y < -200 || block.y > 2732 + 200) continue; var hit = false; // Check main ball if (ball && block && ball.intersects(block)) { // Fireball: instantly destroy block and keep moving if (ball.fireball) { // Burn block: show a flash and destroy instantly (no animation) if (block && block.blockSprite) { LK.effects.flashObject(block, 0xff6600, 80); } block.hp = 0; if (block.updateHpBar) block.updateHpBar(); block.alpha = 0; block.scaleX = block.scaleY = 1.5; block.destroy(); if (block.parent) block.parent.removeChild(block); blocks.splice(i, 1); 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); if (Math.random() < powerupDropRate) { spawnPowerUp(block.x, block.y); } hit = true; // Do not reflect ball, let it keep moving } else { // 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; } if (window.extraBalls && window.extraBalls.length) { for (var eb = 0; eb < window.extraBalls.length; eb++) { var ebBall = window.extraBalls[eb]; if (ebBall && typeof ebBall.hasHitBlockSinceReset !== "undefined") { ebBall.hasHitBlockSinceReset = true; ebBall.wallBouncesNoBlock = 0; } } } if (powerupBalls && powerupBalls.length) { for (var pb = 0; pb < powerupBalls.length; pb++) { var pbBall = powerupBalls[pb]; if (pbBall && typeof pbBall.hasHitBlockSinceReset !== "undefined") { pbBall.hasHitBlockSinceReset = true; pbBall.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(); // Remove block from game scene to prevent ghost graphics if (block.parent) block.parent.removeChild(block); 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; if (comboTimer) { LK.clearTimeout(comboTimer); comboTimer = null; } 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), but decrease interval as level increases (min 120) game.powerupSpawnInterval = Math.max(120, 360 + Math.floor(Math.random() * 360) - level * 12); } 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), but decrease interval as level increases (min 180) game.powerupBallInterval = Math.max(180, 480 + Math.floor(Math.random() * 480) - level * 16); } 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 || !Array.isArray(window.activePowerups)) window.activePowerups = []; if (!window.powerupIcons || !Array.isArray(window.powerupIcons)) window.powerupIcons = []; if (window.activePowerups.length && 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
@@ -729,9 +729,14 @@
highScoreTxt.setText('High: ' + highScore);
var hearts = '';
for (var i = 0; i < lives; i++) hearts += '♥';
livesTxt.setText(hearts);
- levelTxt.setText('Level ' + level);
+ // Show current level and pattern chars if available
+ if (typeof charArr !== "undefined" && charArr.length) {
+ levelTxt.setText('Level ' + level + ': ' + charArr.join(' '));
+ } else {
+ levelTxt.setText('Level ' + level);
+ }
}
// Helper: reset combo
function resetCombo() {
combo = 0;
@@ -756,10 +761,11 @@
blocks = [];
// Dynamic block pattern logic
var blockW = 200,
blockH = 80;
- var marginX = 40,
- marginY = 40;
+ // Reduce margin as level increases for more density
+ var marginX = Math.max(10, 40 - level * 2);
+ var marginY = Math.max(10, 40 - level * 2);
var colors = ['red', 'green', 'blue', 'yellow'];
// --- New: Generate block positions in a grid to form random letters and/or numbers side by side per level ---
// Returns an array of {x, y} positions for blocks, forming multiple random letters/numbers in a row
function getPatternBlockPositions(level, blockW, blockH) {
@@ -801,12 +807,12 @@
"7": [[1, 1, 1, 1, 1], [0, 0, 0, 0, 1], [0, 0, 0, 1, 0], [0, 0, 1, 0, 0], [0, 1, 0, 0, 0], [0, 1, 0, 0, 0], [0, 1, 0, 0, 0]],
"8": [[0, 1, 1, 1, 0], [1, 0, 0, 0, 1], [1, 0, 0, 0, 1], [0, 1, 1, 1, 0], [1, 0, 0, 0, 1], [1, 0, 0, 0, 1], [0, 1, 1, 1, 0]],
"9": [[0, 1, 1, 1, 0], [1, 0, 0, 0, 1], [1, 0, 0, 0, 1], [0, 1, 1, 1, 1], [0, 0, 0, 0, 1], [1, 0, 0, 0, 1], [0, 1, 1, 1, 0]]
};
- // Decide how many chars to show side by side (1-4, more as level increases)
+ // Decide how many chars to show side by side (1-6, more as level increases)
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
- var numChars = 1 + Math.floor((level - 1) / 3);
- numChars = Math.min(numChars, 4); // max 4 chars side by side
+ var numChars = 1 + Math.floor((level - 1) / 2);
+ numChars = Math.min(numChars, 6); // max 6 chars side by side for high difficulty
// Pick random chars for this level (can be letter or number, allow repeats)
var charArr = [];
for (var i = 0; i < numChars; i++) {
var idx = Math.floor(Math.random() * chars.length);
@@ -847,10 +853,10 @@
var rows = 7;
var colsPerChar = 5;
var gapCols = 1; // 1 column gap between chars
var totalCols = numChars * colsPerChar + (numChars - 1) * gapCols;
- var totalW = totalCols * blockW + (totalCols - 1) * 20;
- var totalH = rows * blockH + (rows - 1) * 20;
+ var totalW = totalCols * blockW + (totalCols - 1) * marginX;
+ var totalH = rows * blockH + (rows - 1) * marginY;
var startX = (2048 - totalW) / 2 + blockW / 2;
var startY = 400;
var positions = [];
for (var ch = 0; ch < charArr.length; ch++) {
@@ -858,12 +864,12 @@
for (var r = 0; r < rows; r++) {
for (var c = 0; c < colsPerChar; c++) {
if (pattern[r][c]) {
// Calculate x offset for this char
- var charOffset = ch * (colsPerChar + gapCols) * (blockW + 20);
+ var charOffset = ch * (colsPerChar + gapCols) * (blockW + marginX);
positions.push({
- x: startX + charOffset + c * (blockW + 20),
- y: startY + r * (blockH + 20)
+ x: startX + charOffset + c * (blockW + marginX),
+ y: startY + r * (blockH + marginY)
});
}
}
}
@@ -944,10 +950,11 @@
block.blockSprite.rotation = 0;
block.x = blockPositions[i].x;
block.y = blockPositions[i].y;
// Increase block HP as level increases (scales faster with level)
- var hpBonus = Math.floor(level / 2);
- block.hp = 1 + Math.floor(level / 4) + Math.floor(i / 5) + hpBonus;
+ // Difficulty scaling: more chars, more HP, less margin, more blocks as level increases
+ var hpBonus = Math.floor(level / 2) + Math.floor(level / 5);
+ block.hp = 1 + Math.floor(level / 3) + Math.floor(i / 4) + hpBonus;
block.maxHp = block.hp;
block.setHp(block.hp, block.maxHp);
blocksToAdd.push(block);
}