/****
* 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();
});