/**** * Classes ****/ // Base block (foundation) var BaseBlock = Container.expand(function () { var self = Container.call(this); var baseAsset = self.attachAsset('baseBlock', { anchorX: 0.5, anchorY: 0.5, width: 600, height: 100 }); return self; }); // Block class for stackable blocks var Block = Container.expand(function () { var self = Container.call(this); // Default block width/height, can be overridden self.blockWidth = 600; self.blockHeight = 100; self.isPerfect = false; // Attach block asset var blockAsset = self.attachAsset('block', { anchorX: 0.5, anchorY: 0.5, width: self.blockWidth, height: self.blockHeight }); self.setBlockSize = function (width, height) { self.blockWidth = width; self.blockHeight = height; blockAsset.width = width; blockAsset.height = height; }; self.setBlockColor = function (colorId) { blockAsset.color = colorId; blockAsset.tint = colorId; }; self.flashPerfect = function () { // Flash green for perfect drop (no tween) blockAsset.tint = 0x2ecc40; // Restore after a short delay LK.setTimeout(function () { blockAsset.tint = 0x3498db; }, 200); }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x888888 }); /**** * Game Code ****/ // (Background is now set by backgroundColor, no rectangle needed) // Sound for perfect drop // Perfect effect: green rectangle // Base block: dark gray // Block asset: blue rectangle // Game constants var GAME_WIDTH = 2048; var GAME_HEIGHT = 2732; var BLOCK_START_WIDTH = 600; var BLOCK_HEIGHT = 100; var BLOCK_MIN_WIDTH = 60; var BLOCK_MOVE_MARGIN = 120; // Margin from edge for block movement var BASE_Y = GAME_HEIGHT - 300; // Y position for base block // Game state var stackBlocks = []; // Array of stacked blocks (bottom to top) var currentBlock = null; // The moving block var currentDirection = 1; // 1: right, -1: left var currentSpeed = 8; // Initial horizontal speed (pixels per frame) var blockLevel = 0; // Number of blocks stacked (score) var isDropping = false; // Prevent double tap var lastBlockX = GAME_WIDTH / 2; // X position of last stacked block var lastBlockWidth = BLOCK_START_WIDTH; // Width of last stacked block var lastBlockY = BASE_Y - BLOCK_HEIGHT; // Y of last stacked block var gameOver = false; // Score display var scoreTxt = new Text2('0', { size: 120, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // Helper: Center X function getCenterX() { return GAME_WIDTH / 2; } // Create base block var baseBlock = new BaseBlock(); baseBlock.x = getCenterX(); baseBlock.y = BASE_Y; game.addChild(baseBlock); // Add base block to stack stackBlocks.push({ x: baseBlock.x, y: baseBlock.y, width: BLOCK_START_WIDTH, height: BLOCK_HEIGHT, blockObj: baseBlock }); // Start first block function spawnBlock() { var block = new Block(); block.setBlockSize(lastBlockWidth, BLOCK_HEIGHT); block.x = BLOCK_MOVE_MARGIN + block.blockWidth / 2; block.y = lastBlockY - BLOCK_HEIGHT; blockLevel += 1; currentDirection = 1; currentSpeed = 8 + Math.floor(blockLevel / 8); // Increase speed as tower grows isDropping = false; currentBlock = block; // Change block color each time a new block is spawned // Use a simple color palette and pick a random color different from the previous block var blockColors = [0x008f1d, 0x3498db, 0xe67e22, 0xe74c3c, 0x9b59b6, 0xf1c40f, 0x1abc9c]; if (typeof lastBlockColorIdx === "undefined") { var lastBlockColorIdx = -1; } var colorIdx; do { colorIdx = Math.floor(Math.random() * blockColors.length); } while (colorIdx === lastBlockColorIdx && blockColors.length > 1); lastBlockColorIdx = colorIdx; block.setBlockColor(blockColors[colorIdx]); game.addChild(block); } // Drop logic function dropBlock() { if (isDropping || gameOver) { return; } isDropping = true; // Get last stacked block var prev = stackBlocks[stackBlocks.length - 1]; var prevLeft = prev.x - prev.width / 2; var prevRight = prev.x + prev.width / 2; var currLeft = currentBlock.x - currentBlock.blockWidth / 2; var currRight = currentBlock.x + currentBlock.blockWidth / 2; // Calculate overlap var overlapLeft = Math.max(prevLeft, currLeft); var overlapRight = Math.min(prevRight, currRight); var overlapWidth = overlapRight - overlapLeft; // Perfect drop threshold (within 6px) var perfect = Math.abs(currLeft - prevLeft) < 6 && Math.abs(currRight - prevRight) < 6; if (overlapWidth <= 0) { // No overlap, game over endGame(); return; } // Trim block to overlap if (overlapWidth < currentBlock.blockWidth) { // Animate trimming var trimAmount = currentBlock.blockWidth - overlapWidth; var trimSide = currLeft < prevLeft ? 'left' : 'right'; // Instantly set block to new width and position (no tween) var newX = overlapLeft + overlapWidth / 2; currentBlock.x = newX; currentBlock.setBlockSize(overlapWidth, BLOCK_HEIGHT); } // Perfect drop effect if (perfect) { currentBlock.flashPerfect(); LK.getSound('perfect').play(); LK.setScore(LK.getScore() + 2); } else { LK.getSound('drop').play(); LK.setScore(LK.getScore() + 1); } scoreTxt.setText(LK.getScore()); // Instantly set block drop (no tween) currentBlock.y = lastBlockY; // Add to stack stackBlocks.push({ x: currentBlock.x, y: lastBlockY, width: overlapWidth, height: BLOCK_HEIGHT, blockObj: currentBlock }); lastBlockX = currentBlock.x; lastBlockWidth = overlapWidth; lastBlockY -= BLOCK_HEIGHT; // Check for too small if (overlapWidth < BLOCK_MIN_WIDTH) { endGame(); return; } // Spawn next block spawnBlock(); } // End game function endGame() { if (gameOver) { return; } gameOver = true; LK.effects.flashScreen(0xff0000, 800); LK.showGameOver(); } // Handle tap anywhere except top left 100x100 game.down = function (x, y, obj) { if (gameOver) { return; } if (x < 100 && y < 100) { return; } // Don't allow tap in top left dropBlock(); }; // Move block horizontally game.update = function () { if (gameOver) { return; } if (!currentBlock) { // First block lastBlockX = getCenterX(); lastBlockWidth = BLOCK_START_WIDTH; lastBlockY = BASE_Y - BLOCK_HEIGHT; spawnBlock(); LK.setScore(0); scoreTxt.setText('0'); return; } if (!isDropping) { // Move block left/right currentBlock.x += currentDirection * currentSpeed; var leftEdge = BLOCK_MOVE_MARGIN + currentBlock.blockWidth / 2; var rightEdge = GAME_WIDTH - BLOCK_MOVE_MARGIN - currentBlock.blockWidth / 2; if (currentBlock.x >= rightEdge) { currentBlock.x = rightEdge; currentDirection = -1; } if (currentBlock.x <= leftEdge) { currentBlock.x = leftEdge; currentDirection = 1; } } }; // Reset game state on restart game.on('reset', function () { // Remove all blocks except base for (var i = 1; i < stackBlocks.length; i++) { if (stackBlocks[i].blockObj) { stackBlocks[i].blockObj.destroy(); } } stackBlocks = [stackBlocks[0]]; lastBlockX = getCenterX(); lastBlockWidth = BLOCK_START_WIDTH; lastBlockY = BASE_Y - BLOCK_HEIGHT; blockLevel = 0; isDropping = false; gameOver = false; if (currentBlock) { currentBlock.destroy(); currentBlock = null; } LK.setScore(0); scoreTxt.setText('0'); spawnBlock(); });
/****
* Classes
****/
// Base block (foundation)
var BaseBlock = Container.expand(function () {
var self = Container.call(this);
var baseAsset = self.attachAsset('baseBlock', {
anchorX: 0.5,
anchorY: 0.5,
width: 600,
height: 100
});
return self;
});
// Block class for stackable blocks
var Block = Container.expand(function () {
var self = Container.call(this);
// Default block width/height, can be overridden
self.blockWidth = 600;
self.blockHeight = 100;
self.isPerfect = false;
// Attach block asset
var blockAsset = self.attachAsset('block', {
anchorX: 0.5,
anchorY: 0.5,
width: self.blockWidth,
height: self.blockHeight
});
self.setBlockSize = function (width, height) {
self.blockWidth = width;
self.blockHeight = height;
blockAsset.width = width;
blockAsset.height = height;
};
self.setBlockColor = function (colorId) {
blockAsset.color = colorId;
blockAsset.tint = colorId;
};
self.flashPerfect = function () {
// Flash green for perfect drop (no tween)
blockAsset.tint = 0x2ecc40;
// Restore after a short delay
LK.setTimeout(function () {
blockAsset.tint = 0x3498db;
}, 200);
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x888888
});
/****
* Game Code
****/
// (Background is now set by backgroundColor, no rectangle needed)
// Sound for perfect drop
// Perfect effect: green rectangle
// Base block: dark gray
// Block asset: blue rectangle
// Game constants
var GAME_WIDTH = 2048;
var GAME_HEIGHT = 2732;
var BLOCK_START_WIDTH = 600;
var BLOCK_HEIGHT = 100;
var BLOCK_MIN_WIDTH = 60;
var BLOCK_MOVE_MARGIN = 120; // Margin from edge for block movement
var BASE_Y = GAME_HEIGHT - 300; // Y position for base block
// Game state
var stackBlocks = []; // Array of stacked blocks (bottom to top)
var currentBlock = null; // The moving block
var currentDirection = 1; // 1: right, -1: left
var currentSpeed = 8; // Initial horizontal speed (pixels per frame)
var blockLevel = 0; // Number of blocks stacked (score)
var isDropping = false; // Prevent double tap
var lastBlockX = GAME_WIDTH / 2; // X position of last stacked block
var lastBlockWidth = BLOCK_START_WIDTH; // Width of last stacked block
var lastBlockY = BASE_Y - BLOCK_HEIGHT; // Y of last stacked block
var gameOver = false;
// Score display
var scoreTxt = new Text2('0', {
size: 120,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Helper: Center X
function getCenterX() {
return GAME_WIDTH / 2;
}
// Create base block
var baseBlock = new BaseBlock();
baseBlock.x = getCenterX();
baseBlock.y = BASE_Y;
game.addChild(baseBlock);
// Add base block to stack
stackBlocks.push({
x: baseBlock.x,
y: baseBlock.y,
width: BLOCK_START_WIDTH,
height: BLOCK_HEIGHT,
blockObj: baseBlock
});
// Start first block
function spawnBlock() {
var block = new Block();
block.setBlockSize(lastBlockWidth, BLOCK_HEIGHT);
block.x = BLOCK_MOVE_MARGIN + block.blockWidth / 2;
block.y = lastBlockY - BLOCK_HEIGHT;
blockLevel += 1;
currentDirection = 1;
currentSpeed = 8 + Math.floor(blockLevel / 8); // Increase speed as tower grows
isDropping = false;
currentBlock = block;
// Change block color each time a new block is spawned
// Use a simple color palette and pick a random color different from the previous block
var blockColors = [0x008f1d, 0x3498db, 0xe67e22, 0xe74c3c, 0x9b59b6, 0xf1c40f, 0x1abc9c];
if (typeof lastBlockColorIdx === "undefined") {
var lastBlockColorIdx = -1;
}
var colorIdx;
do {
colorIdx = Math.floor(Math.random() * blockColors.length);
} while (colorIdx === lastBlockColorIdx && blockColors.length > 1);
lastBlockColorIdx = colorIdx;
block.setBlockColor(blockColors[colorIdx]);
game.addChild(block);
}
// Drop logic
function dropBlock() {
if (isDropping || gameOver) {
return;
}
isDropping = true;
// Get last stacked block
var prev = stackBlocks[stackBlocks.length - 1];
var prevLeft = prev.x - prev.width / 2;
var prevRight = prev.x + prev.width / 2;
var currLeft = currentBlock.x - currentBlock.blockWidth / 2;
var currRight = currentBlock.x + currentBlock.blockWidth / 2;
// Calculate overlap
var overlapLeft = Math.max(prevLeft, currLeft);
var overlapRight = Math.min(prevRight, currRight);
var overlapWidth = overlapRight - overlapLeft;
// Perfect drop threshold (within 6px)
var perfect = Math.abs(currLeft - prevLeft) < 6 && Math.abs(currRight - prevRight) < 6;
if (overlapWidth <= 0) {
// No overlap, game over
endGame();
return;
}
// Trim block to overlap
if (overlapWidth < currentBlock.blockWidth) {
// Animate trimming
var trimAmount = currentBlock.blockWidth - overlapWidth;
var trimSide = currLeft < prevLeft ? 'left' : 'right';
// Instantly set block to new width and position (no tween)
var newX = overlapLeft + overlapWidth / 2;
currentBlock.x = newX;
currentBlock.setBlockSize(overlapWidth, BLOCK_HEIGHT);
}
// Perfect drop effect
if (perfect) {
currentBlock.flashPerfect();
LK.getSound('perfect').play();
LK.setScore(LK.getScore() + 2);
} else {
LK.getSound('drop').play();
LK.setScore(LK.getScore() + 1);
}
scoreTxt.setText(LK.getScore());
// Instantly set block drop (no tween)
currentBlock.y = lastBlockY;
// Add to stack
stackBlocks.push({
x: currentBlock.x,
y: lastBlockY,
width: overlapWidth,
height: BLOCK_HEIGHT,
blockObj: currentBlock
});
lastBlockX = currentBlock.x;
lastBlockWidth = overlapWidth;
lastBlockY -= BLOCK_HEIGHT;
// Check for too small
if (overlapWidth < BLOCK_MIN_WIDTH) {
endGame();
return;
}
// Spawn next block
spawnBlock();
}
// End game
function endGame() {
if (gameOver) {
return;
}
gameOver = true;
LK.effects.flashScreen(0xff0000, 800);
LK.showGameOver();
}
// Handle tap anywhere except top left 100x100
game.down = function (x, y, obj) {
if (gameOver) {
return;
}
if (x < 100 && y < 100) {
return;
} // Don't allow tap in top left
dropBlock();
};
// Move block horizontally
game.update = function () {
if (gameOver) {
return;
}
if (!currentBlock) {
// First block
lastBlockX = getCenterX();
lastBlockWidth = BLOCK_START_WIDTH;
lastBlockY = BASE_Y - BLOCK_HEIGHT;
spawnBlock();
LK.setScore(0);
scoreTxt.setText('0');
return;
}
if (!isDropping) {
// Move block left/right
currentBlock.x += currentDirection * currentSpeed;
var leftEdge = BLOCK_MOVE_MARGIN + currentBlock.blockWidth / 2;
var rightEdge = GAME_WIDTH - BLOCK_MOVE_MARGIN - currentBlock.blockWidth / 2;
if (currentBlock.x >= rightEdge) {
currentBlock.x = rightEdge;
currentDirection = -1;
}
if (currentBlock.x <= leftEdge) {
currentBlock.x = leftEdge;
currentDirection = 1;
}
}
};
// Reset game state on restart
game.on('reset', function () {
// Remove all blocks except base
for (var i = 1; i < stackBlocks.length; i++) {
if (stackBlocks[i].blockObj) {
stackBlocks[i].blockObj.destroy();
}
}
stackBlocks = [stackBlocks[0]];
lastBlockX = getCenterX();
lastBlockWidth = BLOCK_START_WIDTH;
lastBlockY = BASE_Y - BLOCK_HEIGHT;
blockLevel = 0;
isDropping = false;
gameOver = false;
if (currentBlock) {
currentBlock.destroy();
currentBlock = null;
}
LK.setScore(0);
scoreTxt.setText('0');
spawnBlock();
});