/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var Ball = Container.expand(function () { var self = Container.call(this); var ballGraphics = self.attachAsset('ball', { anchorX: 0.5, anchorY: 0.5 }); self.velocityX = 0; self.velocityY = 0; self.speed = 20; self.isActive = false; self.hasHitBlock = false; self.update = function () { if (!self.isActive) { return; } self.x += self.velocityX; self.y += self.velocityY; // Bounce off walls if (self.x <= 20 || self.x >= 2028) { self.velocityX = -self.velocityX; self.x = Math.max(20, Math.min(2028, self.x)); } // Bounce off top if (self.y <= 20) { self.velocityY = -self.velocityY; self.y = 20; } // Check if ball reached bottom if (self.y >= 2700) { self.isActive = false; ballsActive--; } }; return self; }); var Block = Container.expand(function () { var self = Container.call(this); var blockGraphics = self.attachAsset('block', { anchorX: 0.5, anchorY: 0.5 }); self.hitPoints = 1; self.maxHitPoints = 1; self.numberText = new Text2('1', { size: 30, fill: 0xFFFFFF }); self.numberText.anchor.set(0.5, 0.5); self.addChild(self.numberText); self.setHitPoints = function (points) { self.hitPoints = points; self.maxHitPoints = points; self.numberText.setText(points.toString()); // Color based on hit points var intensity = Math.min(points / 10, 1); var red = Math.floor(255 * intensity); var green = Math.floor(100 * (1 - intensity)); var blue = Math.floor(100 * (1 - intensity)); var color = red << 16 | green << 8 | blue; blockGraphics.tint = color; }; self.hit = function () { self.hitPoints--; self.numberText.setText(self.hitPoints.toString()); if (self.hitPoints <= 0) { LK.getSound('blockDestroy').play(); return true; // Block destroyed } else { LK.getSound('blockHit').play(); // Flash effect tween(blockGraphics, { tint: 0xffffff }, { duration: 100, onFinish: function onFinish() { var intensity = Math.min(self.hitPoints / 10, 1); var red = Math.floor(255 * intensity); var green = Math.floor(100 * (1 - intensity)); var blue = Math.floor(100 * (1 - intensity)); var color = red << 16 | green << 8 | blue; tween(blockGraphics, { tint: color }, { duration: 100 }); } }); return false; // Block still alive } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x1a1a2e }); /**** * Game Code ****/ // Game state variables var blocks = []; var balls = []; var ballsActive = 0; var maxBalls = 0; var gameState = 'aiming'; // 'aiming', 'shooting', 'waiting' var aimAngle = 0; var shooterX = 1024; var shooterY = 2600; var level = 1; var ballsToShoot = level; var aimLine = null; // UI Elements var ballCountText = new Text2(level.toString(), { size: 40, fill: 0xFFFFFF }); ballCountText.anchor.set(0.5, 0.5); ballCountText.x = 150; ballCountText.y = 2650; game.addChild(ballCountText); var levelText = new Text2('Level 1', { size: 50, fill: 0xFFFFFF }); levelText.anchor.set(0.5, 0); LK.gui.top.addChild(levelText); // Create aim line aimLine = LK.getAsset('aimLine', { anchorX: 0.5, anchorY: 1 }); aimLine.x = shooterX; aimLine.y = shooterY; aimLine.visible = false; game.addChild(aimLine); // Initialize first level function createBlockRow(rowY, startLevel) { var blockWidth = 130; var spacing = 140; // Block width + small gap var startX = 100; var maxX = 1948; // Keep blocks within screen bounds var currentX = startX; // Calculate maximum blocks for this level (decreases as level increases) var maxBlocksForLevel = Math.max(3, 12 - Math.floor(startLevel / 2)); var blocksCreated = 0; while (currentX + blockWidth / 2 <= maxX && blocksCreated < maxBlocksForLevel) { // Create a block at current position var block = new Block(); block.x = currentX + blockWidth / 2; block.y = rowY; var hitPoints; var chance = Math.random(); if (chance < 0.05) { // 5% chance for 16x level hit points hitPoints = startLevel * 16; } else if (chance < 0.20) { // 15% chance for 8x level hit points (5% + 15% = 20%) hitPoints = startLevel * 8; } else if (chance < 0.45) { // 25% chance for 4x level hit points (20% + 25% = 45%) hitPoints = startLevel * 4; } else if (chance < 0.95) { // 50% chance for 2x level hit points (45% + 50% = 95%) hitPoints = startLevel * 2; } else { // 5% chance for normal hit points hitPoints = Math.floor(Math.random() * startLevel) + 1; } block.setHitPoints(hitPoints); blocks.push(block); game.addChild(block); blocksCreated++; // Determine gap size for next block var gapChance = Math.random(); var gapSize; if (gapChance < 0.50) { // 50% chance for 1 space gap gapSize = 1; } else if (gapChance < 0.85) { // 35% chance for 2 spaces gap (50% + 35% = 85%) gapSize = 2; } else if (gapChance < 0.95) { // 10% chance for 3 spaces gap (85% + 10% = 95%) gapSize = 3; } else if (gapChance < 0.99) { // 4% chance for 4 spaces gap (95% + 4% = 99%) gapSize = 4; } else { // 1% chance for 5 spaces gap gapSize = 5; } // Move to next position (block width + gap) currentX += spacing + (gapSize - 1) * spacing; } } // Create initial blocks createBlockRow(800, level); function checkBallBlockCollisions() { for (var i = balls.length - 1; i >= 0; i--) { var ball = balls[i]; if (!ball.isActive) { continue; } for (var j = blocks.length - 1; j >= 0; j--) { var block = blocks[j]; if (ball.intersects(block)) { // Simple bounce physics var dx = ball.x - block.x; var dy = ball.y - block.y; if (Math.abs(dx) > Math.abs(dy)) { ball.velocityX = -ball.velocityX; } else { ball.velocityY = -ball.velocityY; } // Hit the block if (block.hit()) { // Block destroyed block.destroy(); blocks.splice(j, 1); } ball.hasHitBlock = true; break; } } } } function moveBlocksDown() { for (var i = 0; i < blocks.length; i++) { blocks[i].y += 120; // Move down by block height } // Check if any block reached bottom for (var i = 0; i < blocks.length; i++) { if (blocks[i].y >= 2500) { LK.showGameOver(); return; } } } function nextLevel() { level++; ballsToShoot = level; ballCountText.setText(ballsToShoot.toString()); levelText.setText('Level ' + level); // Move existing blocks down moveBlocksDown(); // Add new row at top createBlockRow(800, level); // Check win condition if (blocks.length === 0) { // Add multiple rows for next level for (var row = 0; row < 3; row++) { createBlockRow(800 + row * 120, level); } } gameState = 'aiming'; } function shootBall() { if (ballsToShoot <= 0) { return; } var ball = new Ball(); ball.x = shooterX; ball.y = shooterY; ball.velocityX = Math.cos(aimAngle) * ball.speed; ball.velocityY = Math.sin(aimAngle) * ball.speed; ball.isActive = true; balls.push(ball); game.addChild(ball); ballsActive++; maxBalls++; ballsToShoot--; ballCountText.setText(ballsToShoot.toString()); LK.getSound('shoot').play(); } game.move = function (x, y, obj) { if (gameState !== 'aiming') { return; } var dx = x - shooterX; var dy = y - shooterY; aimAngle = Math.atan2(dy, dx); // Limit aim angle to upward direction if (aimAngle > -0.1 && aimAngle < Math.PI + 0.1) { aimAngle = Math.max(Math.min(aimAngle, -0.1), -Math.PI + 0.1); } // Update aim line aimLine.rotation = aimAngle + Math.PI / 2; aimLine.visible = true; }; game.down = function (x, y, obj) { if (gameState === 'aiming' && ballsToShoot > 0) { gameState = 'shooting'; aimLine.visible = false; // Shoot first ball immediately shootBall(); // Set up timer for remaining balls if (ballsToShoot > 0) { var shootTimer = LK.setInterval(function () { if (ballsToShoot > 0) { shootBall(); } else { LK.clearInterval(shootTimer); } }, 150); } } }; game.update = function () { // Update balls for (var i = balls.length - 1; i >= 0; i--) { var ball = balls[i]; if (!ball.isActive) { ball.destroy(); balls.splice(i, 1); } } // Check collisions checkBallBlockCollisions(); // Check if all balls are done if (gameState === 'shooting' && ballsActive === 0 && ballsToShoot === 0) { ballsToShoot = level + 1; ballCountText.setText(ballsToShoot.toString()); maxBalls = 0; // Wait a moment then start next level LK.setTimeout(function () { nextLevel(); }, 500); } // Reset ballsActive counter ballsActive = 0; for (var i = 0; i < balls.length; i++) { if (balls[i].isActive) { ballsActive++; } } };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Ball = Container.expand(function () {
var self = Container.call(this);
var ballGraphics = self.attachAsset('ball', {
anchorX: 0.5,
anchorY: 0.5
});
self.velocityX = 0;
self.velocityY = 0;
self.speed = 20;
self.isActive = false;
self.hasHitBlock = false;
self.update = function () {
if (!self.isActive) {
return;
}
self.x += self.velocityX;
self.y += self.velocityY;
// Bounce off walls
if (self.x <= 20 || self.x >= 2028) {
self.velocityX = -self.velocityX;
self.x = Math.max(20, Math.min(2028, self.x));
}
// Bounce off top
if (self.y <= 20) {
self.velocityY = -self.velocityY;
self.y = 20;
}
// Check if ball reached bottom
if (self.y >= 2700) {
self.isActive = false;
ballsActive--;
}
};
return self;
});
var Block = Container.expand(function () {
var self = Container.call(this);
var blockGraphics = self.attachAsset('block', {
anchorX: 0.5,
anchorY: 0.5
});
self.hitPoints = 1;
self.maxHitPoints = 1;
self.numberText = new Text2('1', {
size: 30,
fill: 0xFFFFFF
});
self.numberText.anchor.set(0.5, 0.5);
self.addChild(self.numberText);
self.setHitPoints = function (points) {
self.hitPoints = points;
self.maxHitPoints = points;
self.numberText.setText(points.toString());
// Color based on hit points
var intensity = Math.min(points / 10, 1);
var red = Math.floor(255 * intensity);
var green = Math.floor(100 * (1 - intensity));
var blue = Math.floor(100 * (1 - intensity));
var color = red << 16 | green << 8 | blue;
blockGraphics.tint = color;
};
self.hit = function () {
self.hitPoints--;
self.numberText.setText(self.hitPoints.toString());
if (self.hitPoints <= 0) {
LK.getSound('blockDestroy').play();
return true; // Block destroyed
} else {
LK.getSound('blockHit').play();
// Flash effect
tween(blockGraphics, {
tint: 0xffffff
}, {
duration: 100,
onFinish: function onFinish() {
var intensity = Math.min(self.hitPoints / 10, 1);
var red = Math.floor(255 * intensity);
var green = Math.floor(100 * (1 - intensity));
var blue = Math.floor(100 * (1 - intensity));
var color = red << 16 | green << 8 | blue;
tween(blockGraphics, {
tint: color
}, {
duration: 100
});
}
});
return false; // Block still alive
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a1a2e
});
/****
* Game Code
****/
// Game state variables
var blocks = [];
var balls = [];
var ballsActive = 0;
var maxBalls = 0;
var gameState = 'aiming'; // 'aiming', 'shooting', 'waiting'
var aimAngle = 0;
var shooterX = 1024;
var shooterY = 2600;
var level = 1;
var ballsToShoot = level;
var aimLine = null;
// UI Elements
var ballCountText = new Text2(level.toString(), {
size: 40,
fill: 0xFFFFFF
});
ballCountText.anchor.set(0.5, 0.5);
ballCountText.x = 150;
ballCountText.y = 2650;
game.addChild(ballCountText);
var levelText = new Text2('Level 1', {
size: 50,
fill: 0xFFFFFF
});
levelText.anchor.set(0.5, 0);
LK.gui.top.addChild(levelText);
// Create aim line
aimLine = LK.getAsset('aimLine', {
anchorX: 0.5,
anchorY: 1
});
aimLine.x = shooterX;
aimLine.y = shooterY;
aimLine.visible = false;
game.addChild(aimLine);
// Initialize first level
function createBlockRow(rowY, startLevel) {
var blockWidth = 130;
var spacing = 140; // Block width + small gap
var startX = 100;
var maxX = 1948; // Keep blocks within screen bounds
var currentX = startX;
// Calculate maximum blocks for this level (decreases as level increases)
var maxBlocksForLevel = Math.max(3, 12 - Math.floor(startLevel / 2));
var blocksCreated = 0;
while (currentX + blockWidth / 2 <= maxX && blocksCreated < maxBlocksForLevel) {
// Create a block at current position
var block = new Block();
block.x = currentX + blockWidth / 2;
block.y = rowY;
var hitPoints;
var chance = Math.random();
if (chance < 0.05) {
// 5% chance for 16x level hit points
hitPoints = startLevel * 16;
} else if (chance < 0.20) {
// 15% chance for 8x level hit points (5% + 15% = 20%)
hitPoints = startLevel * 8;
} else if (chance < 0.45) {
// 25% chance for 4x level hit points (20% + 25% = 45%)
hitPoints = startLevel * 4;
} else if (chance < 0.95) {
// 50% chance for 2x level hit points (45% + 50% = 95%)
hitPoints = startLevel * 2;
} else {
// 5% chance for normal hit points
hitPoints = Math.floor(Math.random() * startLevel) + 1;
}
block.setHitPoints(hitPoints);
blocks.push(block);
game.addChild(block);
blocksCreated++;
// Determine gap size for next block
var gapChance = Math.random();
var gapSize;
if (gapChance < 0.50) {
// 50% chance for 1 space gap
gapSize = 1;
} else if (gapChance < 0.85) {
// 35% chance for 2 spaces gap (50% + 35% = 85%)
gapSize = 2;
} else if (gapChance < 0.95) {
// 10% chance for 3 spaces gap (85% + 10% = 95%)
gapSize = 3;
} else if (gapChance < 0.99) {
// 4% chance for 4 spaces gap (95% + 4% = 99%)
gapSize = 4;
} else {
// 1% chance for 5 spaces gap
gapSize = 5;
}
// Move to next position (block width + gap)
currentX += spacing + (gapSize - 1) * spacing;
}
}
// Create initial blocks
createBlockRow(800, level);
function checkBallBlockCollisions() {
for (var i = balls.length - 1; i >= 0; i--) {
var ball = balls[i];
if (!ball.isActive) {
continue;
}
for (var j = blocks.length - 1; j >= 0; j--) {
var block = blocks[j];
if (ball.intersects(block)) {
// Simple bounce physics
var dx = ball.x - block.x;
var dy = ball.y - block.y;
if (Math.abs(dx) > Math.abs(dy)) {
ball.velocityX = -ball.velocityX;
} else {
ball.velocityY = -ball.velocityY;
}
// Hit the block
if (block.hit()) {
// Block destroyed
block.destroy();
blocks.splice(j, 1);
}
ball.hasHitBlock = true;
break;
}
}
}
}
function moveBlocksDown() {
for (var i = 0; i < blocks.length; i++) {
blocks[i].y += 120; // Move down by block height
}
// Check if any block reached bottom
for (var i = 0; i < blocks.length; i++) {
if (blocks[i].y >= 2500) {
LK.showGameOver();
return;
}
}
}
function nextLevel() {
level++;
ballsToShoot = level;
ballCountText.setText(ballsToShoot.toString());
levelText.setText('Level ' + level);
// Move existing blocks down
moveBlocksDown();
// Add new row at top
createBlockRow(800, level);
// Check win condition
if (blocks.length === 0) {
// Add multiple rows for next level
for (var row = 0; row < 3; row++) {
createBlockRow(800 + row * 120, level);
}
}
gameState = 'aiming';
}
function shootBall() {
if (ballsToShoot <= 0) {
return;
}
var ball = new Ball();
ball.x = shooterX;
ball.y = shooterY;
ball.velocityX = Math.cos(aimAngle) * ball.speed;
ball.velocityY = Math.sin(aimAngle) * ball.speed;
ball.isActive = true;
balls.push(ball);
game.addChild(ball);
ballsActive++;
maxBalls++;
ballsToShoot--;
ballCountText.setText(ballsToShoot.toString());
LK.getSound('shoot').play();
}
game.move = function (x, y, obj) {
if (gameState !== 'aiming') {
return;
}
var dx = x - shooterX;
var dy = y - shooterY;
aimAngle = Math.atan2(dy, dx);
// Limit aim angle to upward direction
if (aimAngle > -0.1 && aimAngle < Math.PI + 0.1) {
aimAngle = Math.max(Math.min(aimAngle, -0.1), -Math.PI + 0.1);
}
// Update aim line
aimLine.rotation = aimAngle + Math.PI / 2;
aimLine.visible = true;
};
game.down = function (x, y, obj) {
if (gameState === 'aiming' && ballsToShoot > 0) {
gameState = 'shooting';
aimLine.visible = false;
// Shoot first ball immediately
shootBall();
// Set up timer for remaining balls
if (ballsToShoot > 0) {
var shootTimer = LK.setInterval(function () {
if (ballsToShoot > 0) {
shootBall();
} else {
LK.clearInterval(shootTimer);
}
}, 150);
}
}
};
game.update = function () {
// Update balls
for (var i = balls.length - 1; i >= 0; i--) {
var ball = balls[i];
if (!ball.isActive) {
ball.destroy();
balls.splice(i, 1);
}
}
// Check collisions
checkBallBlockCollisions();
// Check if all balls are done
if (gameState === 'shooting' && ballsActive === 0 && ballsToShoot === 0) {
ballsToShoot = level + 1;
ballCountText.setText(ballsToShoot.toString());
maxBalls = 0;
// Wait a moment then start next level
LK.setTimeout(function () {
nextLevel();
}, 500);
}
// Reset ballsActive counter
ballsActive = 0;
for (var i = 0; i < balls.length; i++) {
if (balls[i].isActive) {
ballsActive++;
}
}
};