User prompt
olmadı düzelt
User prompt
3 tane top gibi powerupslar ekle birsürü
User prompt
çoğaltılan top normal top gibi hareket etsin ama yok olunca can götürmesin
User prompt
süreyi göstermiyor düzelt
User prompt
powerup ların belli bir süresi olsun o süreyi de göster oyuncuya
User prompt
Please fix the bug: 'Uncaught TypeError: paddle.moveTo is not a function' in or related to this line: 'paddle.moveTo(x);' Line Number: 514
User prompt
paddle bazen topu tutmuyor hitboxunu düzgün ayarla
User prompt
top çoğaltma ve benzeri özellikler ekle çoğaltılan toplar yok olunca oyuncunun canından gitmesin
User prompt
powerup dan score sıfırlama gibi şeyleri kaldır
User prompt
eğer top hiç bir tuğlaya çarpmadan sağ ve sol duvara çarparsa 15 kere o zaman al
User prompt
güçlendiricileri tekrar kontrol et çalışmayanlar var
User prompt
güçlendiriciler çoğunlukla oyunun üst tarafında çıksın
User prompt
güçlendiriciler random bir yerde de çıkabilsin
User prompt
oyuncu her level atladığında mevcut canına 1 can eklensin
User prompt
power up ekranda da çıkabilsin top üstünden geçince alınmış sayılsın
User prompt
top sağ ve sol duvara 15 den fazla değerse oyuncuya geri ver topu
User prompt
Please fix the bug: 'storage.getItem is not a function' in or related to this line: 'var highScore = storage.getItem('highScore') || 0;' Line Number: 206 ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
Please fix the bug: 'storage.get is not a function' in or related to this line: 'var highScore = storage.get('highScore') || 0;' Line Number: 206
User prompt
high score eklemelisin
User prompt
powerupdan ne çıktığı yazmalı
User prompt
tuğlalardan powerup düşme sıklığı artsın
User prompt
sonraki leveller de farklı şekiller olsun daha zor şekiller at,eşek,zürafa gibi
User prompt
tıuğlalar ilk bölümde full olsun
User prompt
powerup 50 farklı özellik ekle bu özellikleri test et çalışanlar kalsın çalışmayanları sil
User prompt
blokların can barı gibi birşey olmalı
/****
* 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;
}
};
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
});
self.width = paddleSprite.width;
self.height = paddleSprite.height;
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;
});
/****
* 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 = [];
var lives = 3;
var level = 1;
var isBallLaunched = false;
var dragPaddle = false;
var 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);
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() {
lives--;
updateGUI();
LK.getSound('lose').play();
if (lives <= 0) {
if (score > highScore) {
highScore = score;
storage.highScore = highScore;
highScoreTxt.setText('High: ' + highScore);
}
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;
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);
}
}
}
// 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;
if (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) {
// 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();
// 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
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
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 () {
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 () {
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 () {
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 (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) {
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 (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);
}
};
// Music (optional, not required by MVP, so not included)
/* End of gamecode.js */ ===================================================================
--- original.js
+++ change.js
@@ -411,8 +411,15 @@
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
@@ -466,8 +473,9 @@
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);
}
}