/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Ball Class var Ball = Container.expand(function () { var self = Container.call(this); var ball = self.attachAsset('ball', { anchorX: 0.5, anchorY: 0.5 }); self.radius = ball.width / 2; // Ball velocity self.vx = 0; self.vy = 0; self.update = function () { self.x += self.vx; self.y += self.vy; }; return self; }); // Block Class var Block = Container.expand(function () { var self = Container.call(this); // Color variety: assign a colorId based on blockType, but also randomize for more variety var colorIds = ['block', 'block2', 'block3']; var colorId; var r = Math.random(); if (r < 0.1) { self.hitPoints = 3; colorId = 'block4'; // Unique color for 3-hit blocks } else if (r < 0.3) { self.hitPoints = 2; colorId = 'block5'; // Unique color for 2-hit blocks } else { self.hitPoints = 1; if (typeof self.blockType === "number" && self.blockType >= 1 && self.blockType <= 3) { colorId = colorIds[self.blockType - 1]; } else { colorId = colorIds[Math.floor(Math.random() * colorIds.length)]; } } // If blockType is 3, always use the unique color for 3-hit blocks if (typeof self.blockType === "number" && self.blockType === 3) { self.hitPoints = 3; colorId = 'block4'; } // If blockType is 2, always use the unique color for 2-hit blocks if (typeof self.blockType === "number" && self.blockType === 2) { self.hitPoints = 2; colorId = 'block5'; } var block = self.attachAsset(colorId, { anchorX: 0.5, anchorY: 0.5 }); self.width = block.width; self.height = block.height; self.updateBlockVisual = function () { // Remove old block asset if present if (block && block.parent === self) { self.removeChild(block); } // Pick color asset based on hitPoints var colorAsset = 'block'; if (self.hitPoints === 3) { colorAsset = 'block4'; } else if (self.hitPoints === 2) { colorAsset = 'block5'; } else if (self.hitPoints === 1) { // Use original colorId if available, else fallback if (typeof self.blockType === "number" && self.blockType >= 1 && self.blockType <= 3) { var colorIds = ['block', 'block2', 'block3']; colorAsset = colorIds[self.blockType - 1]; } else { colorAsset = 'block'; } } block = self.attachAsset(colorAsset, { anchorX: 0.5, anchorY: 0.5 }); self.width = block.width; self.height = block.height; // Set alpha for visual feedback if (self.hitPoints === 3) { block.alpha = 1; } else if (self.hitPoints === 2) { block.alpha = 0.7; } else { block.alpha = 0.4; } }; self.updateBlockVisual(); return self; }); // Platform (Paddle) Class var Platform = Container.expand(function () { var self = Container.call(this); var plat = self.attachAsset('platform', { anchorX: 0.5, anchorY: 0.5 }); self.width = plat.width; self.height = plat.height; return self; }); // Powerup Class var Powerup = Container.expand(function () { var self = Container.call(this); // Define multiple powerup types var types = [{ type: 'enlarge', label: 'E', color: 'block2' }, { type: 'shrink', label: 'S', color: 'block3' }, { type: 'life', label: '♥', color: 'block' }, { type: 'slow', label: '⏳', color: 'block2' }, { type: 'fast', label: '⚡', color: 'block3' }]; // Randomly pick a powerup type var chosen = types[Math.floor(Math.random() * types.length)]; self.type = chosen.type; // Use different asset color for each powerup type var asset = self.attachAsset(chosen.color, { anchorX: 0.5, anchorY: 0.5 }); asset.width = 100; asset.height = 60; // Add a text label for the powerup type var txt = new Text2(chosen.label, { size: 60, fill: "#fff" }); txt.anchor.set(0.5, 0.5); txt.x = 0; txt.y = 0; self.addChild(txt); // Set speed self.vy = 10; self.update = function () { self.y += self.vy; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x222222 }); /**** * Game Code ****/ // <-- Use a new image asset id for a baseball bat // Game constants // Platform (paddle) // Ball // Block // Block (second color for variety) // Block (third color for variety) // 3-hit // 2-hit unique color var GAME_WIDTH = 2048; var GAME_HEIGHT = 2732; var PLATFORM_Y = GAME_HEIGHT - 200; var BALL_START_Y = PLATFORM_Y - 80; var BALL_SPEED = 18; var BALL_MIN_ANGLE = Math.PI / 8; // ~22.5deg, to avoid too shallow var BALL_MAX_ANGLE = Math.PI - Math.PI / 8; var PLATFORM_SPEED = 60; // Not used, as platform is dragged directly var BLOCK_ROWS = 5; var BLOCK_COLS = 7; var BLOCK_MARGIN_X = 30; var BLOCK_MARGIN_Y = 30; var BLOCK_TOP_OFFSET = 350; var BLOCK_TYPES = [1, 2, 3]; // Game state var platform; var ball; var blocks = []; var powerups = []; // Track falling powerups var score = 0; var lives = 3; var isBallLaunched = false; var dragPlatform = false; var lastTouchX = 0; var scoreTxt; var livesTxt; // Helper: Reset ball to platform function resetBall() { ball.x = platform.x; ball.y = BALL_START_Y; ball.vx = 0; ball.vy = 0; isBallLaunched = false; // Ball speed decreases after level 10 for more challenge (slower ball) // Make speed decrease less aggressive and minimum speed higher for better gameplay if (currentLevel >= 10) { BALL_SPEED = 14 - Math.min(currentLevel - 10, 6) * 0.6; // 14, 13.4, 12.8, ..., min 10 if (BALL_SPEED < 10) BALL_SPEED = 10; } else { BALL_SPEED = 18 + currentLevel * 2; if (BALL_SPEED > 40) BALL_SPEED = 40; } } // Helper: Launch ball function launchBall() { if (!isBallLaunched) { // Randomize initial angle between 45 and 135 degrees (upwards) var angle = Math.PI / 4 + Math.random() * (Math.PI / 2); ball.vx = BALL_SPEED * Math.cos(angle); ball.vy = -BALL_SPEED * Math.sin(angle); isBallLaunched = true; } } // Helper: Clamp value function clamp(val, min, max) { if (val < min) return min; if (val > max) return max; return val; } // Level patterns: 20 unique block layouts (1: block, 0: empty, 2/3: alt color) var LEVEL_PATTERNS = [ // Level 1: Full grid [[1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1]], // Level 2: Checkerboard [[1, 0, 1, 0, 1, 0, 1], [0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1], [0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1]], // Level 3: Pyramid [[0, 0, 0, 1, 0, 0, 0], [0, 0, 1, 1, 1, 0, 0], [0, 1, 1, 1, 1, 1, 0], [1, 1, 1, 1, 1, 1, 1], [0, 0, 0, 0, 0, 0, 0]], // Level 4: Hollow rectangle [[2, 2, 2, 2, 2, 2, 2], [2, 0, 0, 0, 0, 0, 2], [2, 0, 0, 0, 0, 0, 2], [2, 0, 0, 0, 0, 0, 2], [2, 2, 2, 2, 2, 2, 2]], // Level 5: Diagonal [[3, 0, 0, 0, 0, 0, 3], [0, 3, 0, 0, 0, 3, 0], [0, 0, 3, 0, 3, 0, 0], [0, 0, 0, 3, 0, 0, 0], [0, 0, 3, 0, 3, 0, 0]], // Level 6: Two triangles [[1, 0, 0, 0, 0, 0, 1], [1, 1, 0, 0, 0, 1, 1], [1, 1, 1, 0, 1, 1, 1], [1, 1, 0, 0, 0, 1, 1], [1, 0, 0, 0, 0, 0, 1]], // Level 7: Alternating rows [[1, 2, 1, 2, 1, 2, 1], [2, 1, 2, 1, 2, 1, 2], [1, 2, 1, 2, 1, 2, 1], [2, 1, 2, 1, 2, 1, 2], [1, 2, 1, 2, 1, 2, 1]], // Level 8: Center cross [[0, 0, 3, 3, 3, 0, 0], [0, 0, 3, 3, 3, 0, 0], [3, 3, 3, 3, 3, 3, 3], [0, 0, 3, 3, 3, 0, 0], [0, 0, 3, 3, 3, 0, 0]], // Level 9: Edges only [[1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 0, 1], [1, 1, 1, 1, 1, 1, 1]], // Level 10: Zigzag [[1, 0, 0, 0, 0, 0, 1], [0, 1, 0, 0, 0, 1, 0], [0, 0, 1, 0, 1, 0, 0], [0, 1, 0, 0, 0, 1, 0], [1, 0, 0, 0, 0, 0, 1]], // Level 11: Dense center [[0, 0, 1, 1, 1, 0, 0], [0, 1, 1, 1, 1, 1, 0], [1, 1, 2, 2, 2, 1, 1], [0, 1, 1, 1, 1, 1, 0], [0, 0, 1, 1, 1, 0, 0]], // Level 12: Alternating columns [[1, 2, 1, 2, 1, 2, 1], [1, 2, 1, 2, 1, 2, 1], [1, 2, 1, 2, 1, 2, 1], [1, 2, 1, 2, 1, 2, 1], [1, 2, 1, 2, 1, 2, 1]], // Level 13: Arrow [[0, 0, 0, 3, 0, 0, 0], [0, 0, 3, 3, 3, 0, 0], [0, 3, 3, 3, 3, 3, 0], [3, 3, 3, 3, 3, 3, 3], [0, 0, 0, 0, 0, 0, 0]], // Level 14: Corners [[2, 0, 0, 0, 0, 0, 2], [0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0], [2, 0, 0, 0, 0, 0, 2]], // Level 15: Double cross [[0, 2, 0, 2, 0, 2, 0], [2, 2, 2, 2, 2, 2, 2], [0, 2, 0, 2, 0, 2, 0], [2, 2, 2, 2, 2, 2, 2], [0, 2, 0, 2, 0, 2, 0]], // Level 16: X shape [[3, 0, 0, 0, 0, 0, 3], [0, 3, 0, 0, 0, 3, 0], [0, 0, 3, 0, 3, 0, 0], [0, 3, 0, 0, 0, 3, 0], [3, 0, 0, 0, 0, 0, 3]], // Level 17: Center diamond [[0, 0, 1, 0, 1, 0, 0], [0, 1, 1, 1, 1, 1, 0], [1, 1, 2, 2, 2, 1, 1], [0, 1, 1, 1, 1, 1, 0], [0, 0, 1, 0, 1, 0, 0]], // Level 18: Top and bottom [[1, 1, 1, 1, 1, 1, 1], [0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0], [1, 1, 1, 1, 1, 1, 1]], // Level 19: Alternating blocks [[1, 0, 2, 0, 3, 0, 1], [0, 2, 0, 3, 0, 1, 0], [2, 0, 3, 0, 1, 0, 2], [0, 3, 0, 1, 0, 2, 0], [3, 0, 1, 0, 2, 0, 3]], // Level 20: Dense grid [[1, 2, 3, 1, 2, 3, 1], [2, 3, 1, 2, 3, 1, 2], [3, 1, 2, 3, 1, 2, 3], [1, 2, 3, 1, 2, 3, 1], [2, 3, 1, 2, 3, 1, 2]]]; var currentLevel = 0; var MAX_LEVEL = LEVEL_PATTERNS.length; // Helper: Create blocks function createBlocks() { // Remove old blocks for (var i = 0; i < blocks.length; i++) { blocks[i].destroy(); } blocks = []; // Calculate block size and spacing var blockAsset = LK.getAsset('block', { anchorX: 0.5, anchorY: 0.5 }); var blockW = blockAsset.width; var blockH = blockAsset.height; // Double the number of columns after level 10 var blockCols = BLOCK_COLS; var pattern = LEVEL_PATTERNS[currentLevel % LEVEL_PATTERNS.length]; var patternRowLength = pattern[0] ? pattern[0].length : BLOCK_COLS; if (currentLevel >= 10) { blockCols = BLOCK_COLS * 2; // For doubled columns, repeat the pattern horizontally // We'll use pattern[row][col % patternRowLength] for type } // --- Calculate scaling and spacing so all blocks fit on screen --- var totalRows = BLOCK_ROWS + (currentLevel >= 10 ? 7 + Math.min(currentLevel - 10, 5) : Math.min(Math.floor(currentLevel / 2), 3)); var maxBlockAreaW = GAME_WIDTH - 2 * 30; // 30px margin left/right var maxBlockAreaH = GAME_HEIGHT * 0.45; // Use top 45% of screen for blocks var blockAreaY = BLOCK_TOP_OFFSET; var blockAreaH = maxBlockAreaH; // Calculate scale to fit horizontally and vertically var scaleX = maxBlockAreaW / (blockCols * blockW + (blockCols - 1) * BLOCK_MARGIN_X); var scaleY = blockAreaH / (totalRows * blockH + (totalRows - 1) * BLOCK_MARGIN_Y); var blockScale = Math.min(scaleX, scaleY, 1); // Never scale up, only down var scaledBlockW = blockW * blockScale; var scaledBlockH = blockH * blockScale; var scaledMarginX = BLOCK_MARGIN_X * blockScale; var scaledMarginY = BLOCK_MARGIN_Y * blockScale; var totalW = blockCols * scaledBlockW + (blockCols - 1) * scaledMarginX; var totalH = totalRows * scaledBlockH + (totalRows - 1) * scaledMarginY; var startX = (GAME_WIDTH - totalW) / 2 + scaledBlockW / 2; var startY = blockAreaY; // Add more rows as level increases (up to 8, or up to 12 after level 10 for more challenge) var extraRows; if (currentLevel >= 10) { // After level 10, increase block rows more aggressively extraRows = 7 + Math.min(currentLevel - 10, 5); // 7,8,9,10,11,12 rows (so 12-17 rows total) } else { extraRows = Math.min(Math.floor(currentLevel / 2), 3); } for (var row = 0; row < BLOCK_ROWS + extraRows; row++) { for (var col = 0; col < blockCols; col++) { var type; if (currentLevel >= 10) { // Repeat the pattern horizontally for doubled columns type = pattern[row] && pattern[row][col % patternRowLength] ? pattern[row][col % patternRowLength] : 0; } else { type = pattern[row] && pattern[row][col] ? pattern[row][col] : 0; } if (type === 0) continue; var b = new Block(); b.blockType = type; // Remove any previous children (for safety) b.removeChildren(); // Assign a random colorId for each block for more color variety var colorIds = ['block', 'block2', 'block3']; var colorId; if (typeof b.blockType === "number" && b.blockType >= 1 && b.blockType <= 3) { // Pick a random color, but prefer the type's color colorId = colorIds[Math.floor(Math.random() * colorIds.length)]; } else { colorId = colorIds[Math.floor(Math.random() * colorIds.length)]; } var asset = b.attachAsset(colorId, { anchorX: 0.5, anchorY: 0.5 }); asset.width = scaledBlockW; asset.height = scaledBlockH; b.width = scaledBlockW; b.height = scaledBlockH; b.x = startX + col * (scaledBlockW + scaledMarginX); b.y = startY + row * (scaledBlockH + scaledMarginY); game.addChild(b); blocks.push(b); } } } // Helper: Reset game state function resetGame() { score = 0; lives = 3; currentLevel = 0; LK.setScore(0); if (scoreTxt) scoreTxt.setText('0'); if (livesTxt) livesTxt.setText('♥♥♥'); createBlocks(); // Increase ball speed with level (capped for sanity) BALL_SPEED = 18 + currentLevel * 2; if (BALL_SPEED > 40) BALL_SPEED = 40; // Reduce platform width as level increases (min 200) platform.width = 400 - currentLevel * 20; if (platform.width < 200) platform.width = 200; resetBall(); } // Helper: Update lives display function updateLives() { var s = ''; for (var i = 0; i < lives; i++) s += '♥'; livesTxt.setText(s); } // Helper: Ball-Block collision function ballBlockCollision(ball, block) { // AABB collision var dx = Math.abs(ball.x - block.x); var dy = Math.abs(ball.y - block.y); var bw = block.width / 2; var bh = block.height / 2; var br = ball.radius; if (dx > bw + br) return false; if (dy > bh + br) return false; // Find overlap var overlapX = bw + br - dx; var overlapY = bh + br - dy; if (overlapX > 0 && overlapY > 0) { // Determine collision side: reflect ball accordingly if (overlapX < overlapY) { ball.vx = -ball.vx; } else { ball.vy = -ball.vy; } // After bounce, normalize speed to keep it constant var angle = Math.atan2(ball.vy, ball.vx); ball.vx = BALL_SPEED * Math.cos(angle); ball.vy = BALL_SPEED * Math.sin(angle); return true; } return false; } // Helper: Ball-Platform collision function ballPlatformCollision(ball, platform) { // AABB collision var dx = Math.abs(ball.x - platform.x); var dy = Math.abs(ball.y - platform.y); var pw = platform.width / 2; var ph = platform.height / 2; var br = ball.radius; if (dx > pw + br) return false; if (dy > ph + br) return false; // Only reflect if ball is moving down if (ball.vy > 0) { // Calculate hit position (-1 left, 0 center, 1 right) var hit = (ball.x - platform.x) / pw; // Clamp hit hit = clamp(hit, -1, 1); // Reflect angle: 150deg (left) to 30deg (right) var angle = Math.PI - Math.PI / 3 * (hit + 1) / 2; // 150 to 30 deg ball.vx = BALL_SPEED * Math.cos(angle); ball.vy = -BALL_SPEED * Math.sin(angle); // After bounce, normalize speed to keep it constant var norm = Math.sqrt(ball.vx * ball.vx + ball.vy * ball.vy); if (norm !== 0) { ball.vx = BALL_SPEED * (ball.vx / norm); ball.vy = BALL_SPEED * (ball.vy / norm); } } return true; } // Helper: Ball-Wall collision function ballWallCollision(ball) { // Left wall if (ball.x - ball.radius < 0) { ball.x = ball.radius; // Calculate current angle var angle = Math.atan2(ball.vy, -ball.vx); // Clamp angle to avoid too shallow (force more vertical) if (Math.abs(angle) < BALL_MIN_ANGLE) { angle = BALL_MIN_ANGLE * (ball.vy < 0 ? -1 : 1); } ball.vx = BALL_SPEED * Math.cos(angle); ball.vy = BALL_SPEED * Math.sin(angle); } // Right wall if (ball.x + ball.radius > GAME_WIDTH) { ball.x = GAME_WIDTH - ball.radius; // Calculate current angle var angle = Math.atan2(ball.vy, -ball.vx); // Clamp angle to avoid too shallow (force more vertical) if (Math.abs(angle) < BALL_MIN_ANGLE) { angle = BALL_MIN_ANGLE * (ball.vy < 0 ? -1 : 1); } ball.vx = BALL_SPEED * Math.cos(angle); ball.vy = BALL_SPEED * Math.sin(angle); } // Top wall if (ball.y - ball.radius < 0) { ball.y = ball.radius; ball.vy = -ball.vy; } } // Helper: Ball out of bounds function ballOutOfBounds(ball) { return ball.y - ball.radius > GAME_HEIGHT; } // GUI: Score scoreTxt = new Text2('0', { size: 120, fill: "#fff" }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // GUI: Lives livesTxt = new Text2('♥♥♥', { size: 100, fill: 0xE74C3C }); livesTxt.anchor.set(1, 0); LK.gui.topRight.addChild(livesTxt); // Create platform platform = new Platform(); platform.x = GAME_WIDTH / 2; platform.y = PLATFORM_Y; game.addChild(platform); // Create ball ball = new Ball(); resetBall(); game.addChild(ball); // Create blocks createBlocks(); // Input: Drag platform game.down = function (x, y, obj) { // Only allow drag if touch is near platform if (Math.abs(y - platform.y) < 200) { dragPlatform = true; lastTouchX = x; // If ball not launched, move ball with platform if (!isBallLaunched) { ball.x = platform.x; } } }; game.move = function (x, y, obj) { if (dragPlatform) { // Move platform horizontally, clamp to screen platform.x = clamp(x, platform.width / 2, GAME_WIDTH - platform.width / 2); // If ball not launched, move ball with platform if (!isBallLaunched) { ball.x = platform.x; } } }; game.up = function (x, y, obj) { if (dragPlatform) { // On release, if ball not launched, launch it if (!isBallLaunched) { launchBall(); } dragPlatform = false; } }; // Main game loop game.update = function () { // Ball movement if (isBallLaunched) { ball.update(); ballWallCollision(ball); // Platform collision ballPlatformCollision(ball, platform); // Block collisions for (var i = blocks.length - 1; i >= 0; i--) { if (ballBlockCollision(ball, blocks[i])) { // Decrement hit points blocks[i].hitPoints--; if (blocks[i].hitPoints > 0) { // Update visual feedback if (typeof blocks[i].updateBlockVisual === "function") { blocks[i].updateBlockVisual(); } } else { // Powerup drop chance decreases as level increases (min 2% after level 10) var powerupChance; if (currentLevel >= 10) { powerupChance = 0.10 - (currentLevel - 10) * 0.01; if (powerupChance < 0.02) powerupChance = 0.02; } else { powerupChance = 0.25 - currentLevel * 0.02; if (powerupChance < 0.05) powerupChance = 0.05; } if (Math.random() < powerupChance) { var p = new Powerup(); p.x = blocks[i].x; p.y = blocks[i].y; game.addChild(p); powerups.push(p); } blocks[i].destroy(); blocks.splice(i, 1); score += 10; LK.setScore(score); scoreTxt.setText(score); } break; // Only one block per frame } } // Powerup logic for (var i = powerups.length - 1; i >= 0; i--) { var p = powerups[i]; p.update(); // If powerup intersects platform, apply effect if (p.intersects(platform)) { // Activate powerup effect if (p.type === 'enlarge') { // Enlarge platform for 10 seconds var originalWidth = platform.width; platform.width *= 1.5; if (platform.width > 800) platform.width = 800; // Schedule shrink after 10 seconds LK.setTimeout(function () { // Only shrink if platform is still enlarged if (platform.width > originalWidth) { platform.width = originalWidth; } }, 10000); } else if (p.type === 'shrink') { // Shrink platform for 10 seconds var originalWidth = platform.width; platform.width *= 0.6; if (platform.width < 120) platform.width = 120; LK.setTimeout(function () { if (platform.width < originalWidth) { platform.width = originalWidth; } }, 10000); } else if (p.type === 'life') { // Add a life (max 5) if (lives < 5) { lives++; updateLives(); } } else if (p.type === 'slow') { // Slow down ball for 8 seconds var oldSpeed = BALL_SPEED; BALL_SPEED = Math.max(10, BALL_SPEED * 0.6); // Adjust ball velocity to new speed var angle = Math.atan2(ball.vy, ball.vx); ball.vx = BALL_SPEED * Math.cos(angle); ball.vy = BALL_SPEED * Math.sin(angle); LK.setTimeout(function () { BALL_SPEED = oldSpeed; // Re-adjust ball velocity to restored speed var angle2 = Math.atan2(ball.vy, ball.vx); ball.vx = BALL_SPEED * Math.cos(angle2); ball.vy = BALL_SPEED * Math.sin(angle2); }, 8000); } else if (p.type === 'fast') { // Speed up ball for 8 seconds var oldSpeed = BALL_SPEED; BALL_SPEED = Math.min(50, BALL_SPEED * 1.5); var angle = Math.atan2(ball.vy, ball.vx); ball.vx = BALL_SPEED * Math.cos(angle); ball.vy = BALL_SPEED * Math.sin(angle); LK.setTimeout(function () { BALL_SPEED = oldSpeed; var angle2 = Math.atan2(ball.vy, ball.vx); ball.vx = BALL_SPEED * Math.cos(angle2); ball.vy = BALL_SPEED * Math.sin(angle2); }, 8000); } p.destroy(); powerups.splice(i, 1); continue; } // Remove if out of bounds if (p.y > GAME_HEIGHT + 100) { p.destroy(); powerups.splice(i, 1); } } // Win condition: all blocks destroyed if (blocks.length === 0) { currentLevel++; if (currentLevel >= MAX_LEVEL) { LK.showYouWin(); return; } else { createBlocks(); resetBall(); return; } } // Ball out of bounds if (ballOutOfBounds(ball)) { lives--; updateLives(); if (lives <= 0) { LK.showGameOver(); return; } resetBall(); } } }; // --- Level Selection Screen --- var levelSelectContainers = []; function showLevelSelect() { // Remove any previous containers for (var i = 0; i < levelSelectContainers.length; i++) { levelSelectContainers[i].destroy(); } levelSelectContainers = []; // Title with shadow and color var title = new Text2("Bölüm Seç", { size: 220, fill: 0xFFE066, font: "Impact, Arial Black, Tahoma" }); title.anchor.set(0.5, 0); title.x = GAME_WIDTH / 2; title.y = 220; // Drop shadow style is not supported on Text2 in this engine, so skip these lines game.addChild(title); levelSelectContainers.push(title); // Level buttons (arrange in 4 rows of 5) var btnW = 340, btnH = 200, gapX = 90, gapY = 90; var totalW = 5 * btnW + 4 * gapX; var startX = (GAME_WIDTH - totalW) / 2 + btnW / 2; var startY = 600; var btnColors = [0xe74c3c, 0x2ecc71, 0x9b59b6, 0xf39c12, 0x34495e]; for (var i = 0; i < MAX_LEVEL; i++) { (function (levelIdx) { var btn = new Container(); // Button background with color variety and shadow var colorIdx = levelIdx % btnColors.length; var bg = LK.getAsset('block', { anchorX: 0.5, anchorY: 0.5 }); bg.width = btnW; bg.height = btnH; bg.tint = btnColors[colorIdx]; bg.alpha = 0.93; btn.addChild(bg); // Simulate rounded corners by overlaying a slightly smaller block of lighter color var inner = LK.getAsset('block', { anchorX: 0.5, anchorY: 0.5 }); inner.width = btnW - 32; inner.height = btnH - 32; inner.tint = 0xffffff; inner.alpha = 0.13; btn.addChild(inner); // Drop shadow effect (simulate by offsetting a dark block behind) var shadow = LK.getAsset('block', { anchorX: 0.5, anchorY: 0.5 }); shadow.width = btnW; shadow.height = btnH; shadow.tint = 0x222222; shadow.alpha = 0.25; shadow.x = 10; shadow.y = 18; btn.addChildAt(shadow, 0); // Level number text var txt = new Text2((levelIdx + 1).toString(), { size: 110, fill: "#fff", font: "Impact, Arial Black, Tahoma" }); txt.anchor.set(0.5, 0.5); txt.x = 0; txt.y = 0; // Drop shadow style is not supported on Text2 in this engine, so skip these lines btn.addChild(txt); // "Yıldız" for completed levels (simulate with a yellow star if desired) // Optionally, you could add a star icon here if you track completed levels // Position (4 rows of 5) btn.x = startX + levelIdx % 5 * (btnW + gapX); btn.y = startY + Math.floor(levelIdx / 5) * (btnH + gapY); // Animate button pop-in btn.scale.x = btn.scale.y = 0.1; tween(btn.scale, { x: 1, y: 1 }, { duration: 400 + 30 * levelIdx, easing: function easing(k) { return k; } }); // Touch handler with visual feedback btn.down = function (x, y, obj) { tween(btn.scale, { x: 0.92, y: 0.92 }, { duration: 80, // No yoyo/repeat in this tween API, so just animate down easing: function easing(k) { return k; } }); hideLevelSelect(); startGameAtLevel(levelIdx); }; game.addChild(btn); levelSelectContainers.push(btn); })(i); } } function hideLevelSelect() { for (var i = 0; i < levelSelectContainers.length; i++) { levelSelectContainers[i].destroy(); } levelSelectContainers = []; } // Start game at selected level function startGameAtLevel(levelIdx) { currentLevel = levelIdx; score = 0; lives = 3; LK.setScore(0); if (scoreTxt) scoreTxt.setText('0'); if (livesTxt) livesTxt.setText('♥♥♥'); createBlocks(); resetBall(); } // --- Override resetGame to show level select --- resetGame = function resetGame() { showLevelSelect(); }; // Show level select on game start showLevelSelect(); // Play music track '1' at game start and loop continuously LK.playMusic('1', { loop: true });
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Ball Class
var Ball = Container.expand(function () {
var self = Container.call(this);
var ball = self.attachAsset('ball', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = ball.width / 2;
// Ball velocity
self.vx = 0;
self.vy = 0;
self.update = function () {
self.x += self.vx;
self.y += self.vy;
};
return self;
});
// Block Class
var Block = Container.expand(function () {
var self = Container.call(this);
// Color variety: assign a colorId based on blockType, but also randomize for more variety
var colorIds = ['block', 'block2', 'block3'];
var colorId;
var r = Math.random();
if (r < 0.1) {
self.hitPoints = 3;
colorId = 'block4'; // Unique color for 3-hit blocks
} else if (r < 0.3) {
self.hitPoints = 2;
colorId = 'block5'; // Unique color for 2-hit blocks
} else {
self.hitPoints = 1;
if (typeof self.blockType === "number" && self.blockType >= 1 && self.blockType <= 3) {
colorId = colorIds[self.blockType - 1];
} else {
colorId = colorIds[Math.floor(Math.random() * colorIds.length)];
}
}
// If blockType is 3, always use the unique color for 3-hit blocks
if (typeof self.blockType === "number" && self.blockType === 3) {
self.hitPoints = 3;
colorId = 'block4';
}
// If blockType is 2, always use the unique color for 2-hit blocks
if (typeof self.blockType === "number" && self.blockType === 2) {
self.hitPoints = 2;
colorId = 'block5';
}
var block = self.attachAsset(colorId, {
anchorX: 0.5,
anchorY: 0.5
});
self.width = block.width;
self.height = block.height;
self.updateBlockVisual = function () {
// Remove old block asset if present
if (block && block.parent === self) {
self.removeChild(block);
}
// Pick color asset based on hitPoints
var colorAsset = 'block';
if (self.hitPoints === 3) {
colorAsset = 'block4';
} else if (self.hitPoints === 2) {
colorAsset = 'block5';
} else if (self.hitPoints === 1) {
// Use original colorId if available, else fallback
if (typeof self.blockType === "number" && self.blockType >= 1 && self.blockType <= 3) {
var colorIds = ['block', 'block2', 'block3'];
colorAsset = colorIds[self.blockType - 1];
} else {
colorAsset = 'block';
}
}
block = self.attachAsset(colorAsset, {
anchorX: 0.5,
anchorY: 0.5
});
self.width = block.width;
self.height = block.height;
// Set alpha for visual feedback
if (self.hitPoints === 3) {
block.alpha = 1;
} else if (self.hitPoints === 2) {
block.alpha = 0.7;
} else {
block.alpha = 0.4;
}
};
self.updateBlockVisual();
return self;
});
// Platform (Paddle) Class
var Platform = Container.expand(function () {
var self = Container.call(this);
var plat = self.attachAsset('platform', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = plat.width;
self.height = plat.height;
return self;
});
// Powerup Class
var Powerup = Container.expand(function () {
var self = Container.call(this);
// Define multiple powerup types
var types = [{
type: 'enlarge',
label: 'E',
color: 'block2'
}, {
type: 'shrink',
label: 'S',
color: 'block3'
}, {
type: 'life',
label: '♥',
color: 'block'
}, {
type: 'slow',
label: '⏳',
color: 'block2'
}, {
type: 'fast',
label: '⚡',
color: 'block3'
}];
// Randomly pick a powerup type
var chosen = types[Math.floor(Math.random() * types.length)];
self.type = chosen.type;
// Use different asset color for each powerup type
var asset = self.attachAsset(chosen.color, {
anchorX: 0.5,
anchorY: 0.5
});
asset.width = 100;
asset.height = 60;
// Add a text label for the powerup type
var txt = new Text2(chosen.label, {
size: 60,
fill: "#fff"
});
txt.anchor.set(0.5, 0.5);
txt.x = 0;
txt.y = 0;
self.addChild(txt);
// Set speed
self.vy = 10;
self.update = function () {
self.y += self.vy;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x222222
});
/****
* Game Code
****/
// <-- Use a new image asset id for a baseball bat
// Game constants
// Platform (paddle)
// Ball
// Block
// Block (second color for variety)
// Block (third color for variety)
// 3-hit
// 2-hit unique color
var GAME_WIDTH = 2048;
var GAME_HEIGHT = 2732;
var PLATFORM_Y = GAME_HEIGHT - 200;
var BALL_START_Y = PLATFORM_Y - 80;
var BALL_SPEED = 18;
var BALL_MIN_ANGLE = Math.PI / 8; // ~22.5deg, to avoid too shallow
var BALL_MAX_ANGLE = Math.PI - Math.PI / 8;
var PLATFORM_SPEED = 60; // Not used, as platform is dragged directly
var BLOCK_ROWS = 5;
var BLOCK_COLS = 7;
var BLOCK_MARGIN_X = 30;
var BLOCK_MARGIN_Y = 30;
var BLOCK_TOP_OFFSET = 350;
var BLOCK_TYPES = [1, 2, 3];
// Game state
var platform;
var ball;
var blocks = [];
var powerups = []; // Track falling powerups
var score = 0;
var lives = 3;
var isBallLaunched = false;
var dragPlatform = false;
var lastTouchX = 0;
var scoreTxt;
var livesTxt;
// Helper: Reset ball to platform
function resetBall() {
ball.x = platform.x;
ball.y = BALL_START_Y;
ball.vx = 0;
ball.vy = 0;
isBallLaunched = false;
// Ball speed decreases after level 10 for more challenge (slower ball)
// Make speed decrease less aggressive and minimum speed higher for better gameplay
if (currentLevel >= 10) {
BALL_SPEED = 14 - Math.min(currentLevel - 10, 6) * 0.6; // 14, 13.4, 12.8, ..., min 10
if (BALL_SPEED < 10) BALL_SPEED = 10;
} else {
BALL_SPEED = 18 + currentLevel * 2;
if (BALL_SPEED > 40) BALL_SPEED = 40;
}
}
// Helper: Launch ball
function launchBall() {
if (!isBallLaunched) {
// Randomize initial angle between 45 and 135 degrees (upwards)
var angle = Math.PI / 4 + Math.random() * (Math.PI / 2);
ball.vx = BALL_SPEED * Math.cos(angle);
ball.vy = -BALL_SPEED * Math.sin(angle);
isBallLaunched = true;
}
}
// Helper: Clamp value
function clamp(val, min, max) {
if (val < min) return min;
if (val > max) return max;
return val;
}
// Level patterns: 20 unique block layouts (1: block, 0: empty, 2/3: alt color)
var LEVEL_PATTERNS = [
// Level 1: Full grid
[[1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1]],
// Level 2: Checkerboard
[[1, 0, 1, 0, 1, 0, 1], [0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1], [0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1]],
// Level 3: Pyramid
[[0, 0, 0, 1, 0, 0, 0], [0, 0, 1, 1, 1, 0, 0], [0, 1, 1, 1, 1, 1, 0], [1, 1, 1, 1, 1, 1, 1], [0, 0, 0, 0, 0, 0, 0]],
// Level 4: Hollow rectangle
[[2, 2, 2, 2, 2, 2, 2], [2, 0, 0, 0, 0, 0, 2], [2, 0, 0, 0, 0, 0, 2], [2, 0, 0, 0, 0, 0, 2], [2, 2, 2, 2, 2, 2, 2]],
// Level 5: Diagonal
[[3, 0, 0, 0, 0, 0, 3], [0, 3, 0, 0, 0, 3, 0], [0, 0, 3, 0, 3, 0, 0], [0, 0, 0, 3, 0, 0, 0], [0, 0, 3, 0, 3, 0, 0]],
// Level 6: Two triangles
[[1, 0, 0, 0, 0, 0, 1], [1, 1, 0, 0, 0, 1, 1], [1, 1, 1, 0, 1, 1, 1], [1, 1, 0, 0, 0, 1, 1], [1, 0, 0, 0, 0, 0, 1]],
// Level 7: Alternating rows
[[1, 2, 1, 2, 1, 2, 1], [2, 1, 2, 1, 2, 1, 2], [1, 2, 1, 2, 1, 2, 1], [2, 1, 2, 1, 2, 1, 2], [1, 2, 1, 2, 1, 2, 1]],
// Level 8: Center cross
[[0, 0, 3, 3, 3, 0, 0], [0, 0, 3, 3, 3, 0, 0], [3, 3, 3, 3, 3, 3, 3], [0, 0, 3, 3, 3, 0, 0], [0, 0, 3, 3, 3, 0, 0]],
// Level 9: Edges only
[[1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 0, 1], [1, 1, 1, 1, 1, 1, 1]],
// Level 10: Zigzag
[[1, 0, 0, 0, 0, 0, 1], [0, 1, 0, 0, 0, 1, 0], [0, 0, 1, 0, 1, 0, 0], [0, 1, 0, 0, 0, 1, 0], [1, 0, 0, 0, 0, 0, 1]],
// Level 11: Dense center
[[0, 0, 1, 1, 1, 0, 0], [0, 1, 1, 1, 1, 1, 0], [1, 1, 2, 2, 2, 1, 1], [0, 1, 1, 1, 1, 1, 0], [0, 0, 1, 1, 1, 0, 0]],
// Level 12: Alternating columns
[[1, 2, 1, 2, 1, 2, 1], [1, 2, 1, 2, 1, 2, 1], [1, 2, 1, 2, 1, 2, 1], [1, 2, 1, 2, 1, 2, 1], [1, 2, 1, 2, 1, 2, 1]],
// Level 13: Arrow
[[0, 0, 0, 3, 0, 0, 0], [0, 0, 3, 3, 3, 0, 0], [0, 3, 3, 3, 3, 3, 0], [3, 3, 3, 3, 3, 3, 3], [0, 0, 0, 0, 0, 0, 0]],
// Level 14: Corners
[[2, 0, 0, 0, 0, 0, 2], [0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0], [2, 0, 0, 0, 0, 0, 2]],
// Level 15: Double cross
[[0, 2, 0, 2, 0, 2, 0], [2, 2, 2, 2, 2, 2, 2], [0, 2, 0, 2, 0, 2, 0], [2, 2, 2, 2, 2, 2, 2], [0, 2, 0, 2, 0, 2, 0]],
// Level 16: X shape
[[3, 0, 0, 0, 0, 0, 3], [0, 3, 0, 0, 0, 3, 0], [0, 0, 3, 0, 3, 0, 0], [0, 3, 0, 0, 0, 3, 0], [3, 0, 0, 0, 0, 0, 3]],
// Level 17: Center diamond
[[0, 0, 1, 0, 1, 0, 0], [0, 1, 1, 1, 1, 1, 0], [1, 1, 2, 2, 2, 1, 1], [0, 1, 1, 1, 1, 1, 0], [0, 0, 1, 0, 1, 0, 0]],
// Level 18: Top and bottom
[[1, 1, 1, 1, 1, 1, 1], [0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0], [1, 1, 1, 1, 1, 1, 1]],
// Level 19: Alternating blocks
[[1, 0, 2, 0, 3, 0, 1], [0, 2, 0, 3, 0, 1, 0], [2, 0, 3, 0, 1, 0, 2], [0, 3, 0, 1, 0, 2, 0], [3, 0, 1, 0, 2, 0, 3]],
// Level 20: Dense grid
[[1, 2, 3, 1, 2, 3, 1], [2, 3, 1, 2, 3, 1, 2], [3, 1, 2, 3, 1, 2, 3], [1, 2, 3, 1, 2, 3, 1], [2, 3, 1, 2, 3, 1, 2]]];
var currentLevel = 0;
var MAX_LEVEL = LEVEL_PATTERNS.length;
// Helper: Create blocks
function createBlocks() {
// Remove old blocks
for (var i = 0; i < blocks.length; i++) {
blocks[i].destroy();
}
blocks = [];
// Calculate block size and spacing
var blockAsset = LK.getAsset('block', {
anchorX: 0.5,
anchorY: 0.5
});
var blockW = blockAsset.width;
var blockH = blockAsset.height;
// Double the number of columns after level 10
var blockCols = BLOCK_COLS;
var pattern = LEVEL_PATTERNS[currentLevel % LEVEL_PATTERNS.length];
var patternRowLength = pattern[0] ? pattern[0].length : BLOCK_COLS;
if (currentLevel >= 10) {
blockCols = BLOCK_COLS * 2;
// For doubled columns, repeat the pattern horizontally
// We'll use pattern[row][col % patternRowLength] for type
}
// --- Calculate scaling and spacing so all blocks fit on screen ---
var totalRows = BLOCK_ROWS + (currentLevel >= 10 ? 7 + Math.min(currentLevel - 10, 5) : Math.min(Math.floor(currentLevel / 2), 3));
var maxBlockAreaW = GAME_WIDTH - 2 * 30; // 30px margin left/right
var maxBlockAreaH = GAME_HEIGHT * 0.45; // Use top 45% of screen for blocks
var blockAreaY = BLOCK_TOP_OFFSET;
var blockAreaH = maxBlockAreaH;
// Calculate scale to fit horizontally and vertically
var scaleX = maxBlockAreaW / (blockCols * blockW + (blockCols - 1) * BLOCK_MARGIN_X);
var scaleY = blockAreaH / (totalRows * blockH + (totalRows - 1) * BLOCK_MARGIN_Y);
var blockScale = Math.min(scaleX, scaleY, 1); // Never scale up, only down
var scaledBlockW = blockW * blockScale;
var scaledBlockH = blockH * blockScale;
var scaledMarginX = BLOCK_MARGIN_X * blockScale;
var scaledMarginY = BLOCK_MARGIN_Y * blockScale;
var totalW = blockCols * scaledBlockW + (blockCols - 1) * scaledMarginX;
var totalH = totalRows * scaledBlockH + (totalRows - 1) * scaledMarginY;
var startX = (GAME_WIDTH - totalW) / 2 + scaledBlockW / 2;
var startY = blockAreaY;
// Add more rows as level increases (up to 8, or up to 12 after level 10 for more challenge)
var extraRows;
if (currentLevel >= 10) {
// After level 10, increase block rows more aggressively
extraRows = 7 + Math.min(currentLevel - 10, 5); // 7,8,9,10,11,12 rows (so 12-17 rows total)
} else {
extraRows = Math.min(Math.floor(currentLevel / 2), 3);
}
for (var row = 0; row < BLOCK_ROWS + extraRows; row++) {
for (var col = 0; col < blockCols; col++) {
var type;
if (currentLevel >= 10) {
// Repeat the pattern horizontally for doubled columns
type = pattern[row] && pattern[row][col % patternRowLength] ? pattern[row][col % patternRowLength] : 0;
} else {
type = pattern[row] && pattern[row][col] ? pattern[row][col] : 0;
}
if (type === 0) continue;
var b = new Block();
b.blockType = type;
// Remove any previous children (for safety)
b.removeChildren();
// Assign a random colorId for each block for more color variety
var colorIds = ['block', 'block2', 'block3'];
var colorId;
if (typeof b.blockType === "number" && b.blockType >= 1 && b.blockType <= 3) {
// Pick a random color, but prefer the type's color
colorId = colorIds[Math.floor(Math.random() * colorIds.length)];
} else {
colorId = colorIds[Math.floor(Math.random() * colorIds.length)];
}
var asset = b.attachAsset(colorId, {
anchorX: 0.5,
anchorY: 0.5
});
asset.width = scaledBlockW;
asset.height = scaledBlockH;
b.width = scaledBlockW;
b.height = scaledBlockH;
b.x = startX + col * (scaledBlockW + scaledMarginX);
b.y = startY + row * (scaledBlockH + scaledMarginY);
game.addChild(b);
blocks.push(b);
}
}
}
// Helper: Reset game state
function resetGame() {
score = 0;
lives = 3;
currentLevel = 0;
LK.setScore(0);
if (scoreTxt) scoreTxt.setText('0');
if (livesTxt) livesTxt.setText('♥♥♥');
createBlocks();
// Increase ball speed with level (capped for sanity)
BALL_SPEED = 18 + currentLevel * 2;
if (BALL_SPEED > 40) BALL_SPEED = 40;
// Reduce platform width as level increases (min 200)
platform.width = 400 - currentLevel * 20;
if (platform.width < 200) platform.width = 200;
resetBall();
}
// Helper: Update lives display
function updateLives() {
var s = '';
for (var i = 0; i < lives; i++) s += '♥';
livesTxt.setText(s);
}
// Helper: Ball-Block collision
function ballBlockCollision(ball, block) {
// AABB collision
var dx = Math.abs(ball.x - block.x);
var dy = Math.abs(ball.y - block.y);
var bw = block.width / 2;
var bh = block.height / 2;
var br = ball.radius;
if (dx > bw + br) return false;
if (dy > bh + br) return false;
// Find overlap
var overlapX = bw + br - dx;
var overlapY = bh + br - dy;
if (overlapX > 0 && overlapY > 0) {
// Determine collision side: reflect ball accordingly
if (overlapX < overlapY) {
ball.vx = -ball.vx;
} else {
ball.vy = -ball.vy;
}
// After bounce, normalize speed to keep it constant
var angle = Math.atan2(ball.vy, ball.vx);
ball.vx = BALL_SPEED * Math.cos(angle);
ball.vy = BALL_SPEED * Math.sin(angle);
return true;
}
return false;
}
// Helper: Ball-Platform collision
function ballPlatformCollision(ball, platform) {
// AABB collision
var dx = Math.abs(ball.x - platform.x);
var dy = Math.abs(ball.y - platform.y);
var pw = platform.width / 2;
var ph = platform.height / 2;
var br = ball.radius;
if (dx > pw + br) return false;
if (dy > ph + br) return false;
// Only reflect if ball is moving down
if (ball.vy > 0) {
// Calculate hit position (-1 left, 0 center, 1 right)
var hit = (ball.x - platform.x) / pw;
// Clamp hit
hit = clamp(hit, -1, 1);
// Reflect angle: 150deg (left) to 30deg (right)
var angle = Math.PI - Math.PI / 3 * (hit + 1) / 2; // 150 to 30 deg
ball.vx = BALL_SPEED * Math.cos(angle);
ball.vy = -BALL_SPEED * Math.sin(angle);
// After bounce, normalize speed to keep it constant
var norm = Math.sqrt(ball.vx * ball.vx + ball.vy * ball.vy);
if (norm !== 0) {
ball.vx = BALL_SPEED * (ball.vx / norm);
ball.vy = BALL_SPEED * (ball.vy / norm);
}
}
return true;
}
// Helper: Ball-Wall collision
function ballWallCollision(ball) {
// Left wall
if (ball.x - ball.radius < 0) {
ball.x = ball.radius;
// Calculate current angle
var angle = Math.atan2(ball.vy, -ball.vx);
// Clamp angle to avoid too shallow (force more vertical)
if (Math.abs(angle) < BALL_MIN_ANGLE) {
angle = BALL_MIN_ANGLE * (ball.vy < 0 ? -1 : 1);
}
ball.vx = BALL_SPEED * Math.cos(angle);
ball.vy = BALL_SPEED * Math.sin(angle);
}
// Right wall
if (ball.x + ball.radius > GAME_WIDTH) {
ball.x = GAME_WIDTH - ball.radius;
// Calculate current angle
var angle = Math.atan2(ball.vy, -ball.vx);
// Clamp angle to avoid too shallow (force more vertical)
if (Math.abs(angle) < BALL_MIN_ANGLE) {
angle = BALL_MIN_ANGLE * (ball.vy < 0 ? -1 : 1);
}
ball.vx = BALL_SPEED * Math.cos(angle);
ball.vy = BALL_SPEED * Math.sin(angle);
}
// Top wall
if (ball.y - ball.radius < 0) {
ball.y = ball.radius;
ball.vy = -ball.vy;
}
}
// Helper: Ball out of bounds
function ballOutOfBounds(ball) {
return ball.y - ball.radius > GAME_HEIGHT;
}
// GUI: Score
scoreTxt = new Text2('0', {
size: 120,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// GUI: Lives
livesTxt = new Text2('♥♥♥', {
size: 100,
fill: 0xE74C3C
});
livesTxt.anchor.set(1, 0);
LK.gui.topRight.addChild(livesTxt);
// Create platform
platform = new Platform();
platform.x = GAME_WIDTH / 2;
platform.y = PLATFORM_Y;
game.addChild(platform);
// Create ball
ball = new Ball();
resetBall();
game.addChild(ball);
// Create blocks
createBlocks();
// Input: Drag platform
game.down = function (x, y, obj) {
// Only allow drag if touch is near platform
if (Math.abs(y - platform.y) < 200) {
dragPlatform = true;
lastTouchX = x;
// If ball not launched, move ball with platform
if (!isBallLaunched) {
ball.x = platform.x;
}
}
};
game.move = function (x, y, obj) {
if (dragPlatform) {
// Move platform horizontally, clamp to screen
platform.x = clamp(x, platform.width / 2, GAME_WIDTH - platform.width / 2);
// If ball not launched, move ball with platform
if (!isBallLaunched) {
ball.x = platform.x;
}
}
};
game.up = function (x, y, obj) {
if (dragPlatform) {
// On release, if ball not launched, launch it
if (!isBallLaunched) {
launchBall();
}
dragPlatform = false;
}
};
// Main game loop
game.update = function () {
// Ball movement
if (isBallLaunched) {
ball.update();
ballWallCollision(ball);
// Platform collision
ballPlatformCollision(ball, platform);
// Block collisions
for (var i = blocks.length - 1; i >= 0; i--) {
if (ballBlockCollision(ball, blocks[i])) {
// Decrement hit points
blocks[i].hitPoints--;
if (blocks[i].hitPoints > 0) {
// Update visual feedback
if (typeof blocks[i].updateBlockVisual === "function") {
blocks[i].updateBlockVisual();
}
} else {
// Powerup drop chance decreases as level increases (min 2% after level 10)
var powerupChance;
if (currentLevel >= 10) {
powerupChance = 0.10 - (currentLevel - 10) * 0.01;
if (powerupChance < 0.02) powerupChance = 0.02;
} else {
powerupChance = 0.25 - currentLevel * 0.02;
if (powerupChance < 0.05) powerupChance = 0.05;
}
if (Math.random() < powerupChance) {
var p = new Powerup();
p.x = blocks[i].x;
p.y = blocks[i].y;
game.addChild(p);
powerups.push(p);
}
blocks[i].destroy();
blocks.splice(i, 1);
score += 10;
LK.setScore(score);
scoreTxt.setText(score);
}
break; // Only one block per frame
}
}
// Powerup logic
for (var i = powerups.length - 1; i >= 0; i--) {
var p = powerups[i];
p.update();
// If powerup intersects platform, apply effect
if (p.intersects(platform)) {
// Activate powerup effect
if (p.type === 'enlarge') {
// Enlarge platform for 10 seconds
var originalWidth = platform.width;
platform.width *= 1.5;
if (platform.width > 800) platform.width = 800;
// Schedule shrink after 10 seconds
LK.setTimeout(function () {
// Only shrink if platform is still enlarged
if (platform.width > originalWidth) {
platform.width = originalWidth;
}
}, 10000);
} else if (p.type === 'shrink') {
// Shrink platform for 10 seconds
var originalWidth = platform.width;
platform.width *= 0.6;
if (platform.width < 120) platform.width = 120;
LK.setTimeout(function () {
if (platform.width < originalWidth) {
platform.width = originalWidth;
}
}, 10000);
} else if (p.type === 'life') {
// Add a life (max 5)
if (lives < 5) {
lives++;
updateLives();
}
} else if (p.type === 'slow') {
// Slow down ball for 8 seconds
var oldSpeed = BALL_SPEED;
BALL_SPEED = Math.max(10, BALL_SPEED * 0.6);
// Adjust ball velocity to new speed
var angle = Math.atan2(ball.vy, ball.vx);
ball.vx = BALL_SPEED * Math.cos(angle);
ball.vy = BALL_SPEED * Math.sin(angle);
LK.setTimeout(function () {
BALL_SPEED = oldSpeed;
// Re-adjust ball velocity to restored speed
var angle2 = Math.atan2(ball.vy, ball.vx);
ball.vx = BALL_SPEED * Math.cos(angle2);
ball.vy = BALL_SPEED * Math.sin(angle2);
}, 8000);
} else if (p.type === 'fast') {
// Speed up ball for 8 seconds
var oldSpeed = BALL_SPEED;
BALL_SPEED = Math.min(50, BALL_SPEED * 1.5);
var angle = Math.atan2(ball.vy, ball.vx);
ball.vx = BALL_SPEED * Math.cos(angle);
ball.vy = BALL_SPEED * Math.sin(angle);
LK.setTimeout(function () {
BALL_SPEED = oldSpeed;
var angle2 = Math.atan2(ball.vy, ball.vx);
ball.vx = BALL_SPEED * Math.cos(angle2);
ball.vy = BALL_SPEED * Math.sin(angle2);
}, 8000);
}
p.destroy();
powerups.splice(i, 1);
continue;
}
// Remove if out of bounds
if (p.y > GAME_HEIGHT + 100) {
p.destroy();
powerups.splice(i, 1);
}
}
// Win condition: all blocks destroyed
if (blocks.length === 0) {
currentLevel++;
if (currentLevel >= MAX_LEVEL) {
LK.showYouWin();
return;
} else {
createBlocks();
resetBall();
return;
}
}
// Ball out of bounds
if (ballOutOfBounds(ball)) {
lives--;
updateLives();
if (lives <= 0) {
LK.showGameOver();
return;
}
resetBall();
}
}
};
// --- Level Selection Screen ---
var levelSelectContainers = [];
function showLevelSelect() {
// Remove any previous containers
for (var i = 0; i < levelSelectContainers.length; i++) {
levelSelectContainers[i].destroy();
}
levelSelectContainers = [];
// Title with shadow and color
var title = new Text2("Bölüm Seç", {
size: 220,
fill: 0xFFE066,
font: "Impact, Arial Black, Tahoma"
});
title.anchor.set(0.5, 0);
title.x = GAME_WIDTH / 2;
title.y = 220;
// Drop shadow style is not supported on Text2 in this engine, so skip these lines
game.addChild(title);
levelSelectContainers.push(title);
// Level buttons (arrange in 4 rows of 5)
var btnW = 340,
btnH = 200,
gapX = 90,
gapY = 90;
var totalW = 5 * btnW + 4 * gapX;
var startX = (GAME_WIDTH - totalW) / 2 + btnW / 2;
var startY = 600;
var btnColors = [0xe74c3c, 0x2ecc71, 0x9b59b6, 0xf39c12, 0x34495e];
for (var i = 0; i < MAX_LEVEL; i++) {
(function (levelIdx) {
var btn = new Container();
// Button background with color variety and shadow
var colorIdx = levelIdx % btnColors.length;
var bg = LK.getAsset('block', {
anchorX: 0.5,
anchorY: 0.5
});
bg.width = btnW;
bg.height = btnH;
bg.tint = btnColors[colorIdx];
bg.alpha = 0.93;
btn.addChild(bg);
// Simulate rounded corners by overlaying a slightly smaller block of lighter color
var inner = LK.getAsset('block', {
anchorX: 0.5,
anchorY: 0.5
});
inner.width = btnW - 32;
inner.height = btnH - 32;
inner.tint = 0xffffff;
inner.alpha = 0.13;
btn.addChild(inner);
// Drop shadow effect (simulate by offsetting a dark block behind)
var shadow = LK.getAsset('block', {
anchorX: 0.5,
anchorY: 0.5
});
shadow.width = btnW;
shadow.height = btnH;
shadow.tint = 0x222222;
shadow.alpha = 0.25;
shadow.x = 10;
shadow.y = 18;
btn.addChildAt(shadow, 0);
// Level number text
var txt = new Text2((levelIdx + 1).toString(), {
size: 110,
fill: "#fff",
font: "Impact, Arial Black, Tahoma"
});
txt.anchor.set(0.5, 0.5);
txt.x = 0;
txt.y = 0;
// Drop shadow style is not supported on Text2 in this engine, so skip these lines
btn.addChild(txt);
// "Yıldız" for completed levels (simulate with a yellow star if desired)
// Optionally, you could add a star icon here if you track completed levels
// Position (4 rows of 5)
btn.x = startX + levelIdx % 5 * (btnW + gapX);
btn.y = startY + Math.floor(levelIdx / 5) * (btnH + gapY);
// Animate button pop-in
btn.scale.x = btn.scale.y = 0.1;
tween(btn.scale, {
x: 1,
y: 1
}, {
duration: 400 + 30 * levelIdx,
easing: function easing(k) {
return k;
}
});
// Touch handler with visual feedback
btn.down = function (x, y, obj) {
tween(btn.scale, {
x: 0.92,
y: 0.92
}, {
duration: 80,
// No yoyo/repeat in this tween API, so just animate down
easing: function easing(k) {
return k;
}
});
hideLevelSelect();
startGameAtLevel(levelIdx);
};
game.addChild(btn);
levelSelectContainers.push(btn);
})(i);
}
}
function hideLevelSelect() {
for (var i = 0; i < levelSelectContainers.length; i++) {
levelSelectContainers[i].destroy();
}
levelSelectContainers = [];
}
// Start game at selected level
function startGameAtLevel(levelIdx) {
currentLevel = levelIdx;
score = 0;
lives = 3;
LK.setScore(0);
if (scoreTxt) scoreTxt.setText('0');
if (livesTxt) livesTxt.setText('♥♥♥');
createBlocks();
resetBall();
}
// --- Override resetGame to show level select ---
resetGame = function resetGame() {
showLevelSelect();
};
// Show level select on game start
showLevelSelect();
// Play music track '1' at game start and loop continuously
LK.playMusic('1', {
loop: true
});