/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// IceBlock: Represents a single moving or stacked ice block
var IceBlock = Container.expand(function () {
var self = Container.call(this);
// Attach the ice block asset
var block = self.attachAsset('iceBlock', {
anchorX: 0.5,
anchorY: 1
});
// Set block size (will be updated on creation)
self.setSize = function (w, h) {
block.width = w;
block.height = h;
self.width = w;
self.height = h;
};
// Animate block drop
self.dropTo = function (targetY, duration, onFinish) {
tween(self, {
y: targetY
}, {
duration: duration,
easing: tween.cubicIn,
onFinish: onFinish
});
};
// Animate block shake (for miss)
self.shake = function () {
var origX = self.x;
tween(self, {
x: origX - 30
}, {
duration: 60,
easing: tween.linear,
onFinish: function onFinish() {
tween(self, {
x: origX + 30
}, {
duration: 60,
easing: tween.linear,
onFinish: function onFinish() {
tween(self, {
x: origX
}, {
duration: 60
});
}
});
}
});
};
return self;
});
// IceShard: Represents a falling broken piece
var IceShard = Container.expand(function () {
var self = Container.call(this);
var shard = self.attachAsset('iceShard', {
anchorX: 0.5,
anchorY: 1
});
self.setSize = function (w, h) {
shard.width = w;
shard.height = h;
self.width = w;
self.height = h;
};
// Animate falling and fading out
self.fallAndFade = function (_onFinish) {
tween(self, {
y: self.y + 400,
alpha: 0
}, {
duration: 600,
easing: tween.cubicIn,
onFinish: function onFinish() {
if (_onFinish) _onFinish();
}
});
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a2a3a
});
/****
* Game Code
****/
// Shard (falling broken piece)
// Tower base (static base block)
// Ice block (main block for stacking)
// Game constants
var GAME_WIDTH = 2048;
var GAME_HEIGHT = 2732;
var BASE_Y = GAME_HEIGHT - 200;
var BLOCK_START_Y = 400;
var BLOCK_MIN_WIDTH = 120;
var BLOCK_HEIGHT = 100;
var BLOCK_MOVE_SPEED = 12; // px per frame
// Game state
var stack = []; // Array of stacked blocks (bottom to top)
var movingBlock = null; // The currently moving block
var movingDir = 1; // 1: right, -1: left
var movingSpeed = BLOCK_MOVE_SPEED;
var blockWidth = 600;
var blockHeight = BLOCK_HEIGHT;
var blockX = GAME_WIDTH / 2;
var blockY = BLOCK_START_Y;
var canDrop = true;
var score = 0;
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);
// Tower base
var baseBlock = new IceBlock();
baseBlock.setSize(700, 120);
baseBlock.x = GAME_WIDTH / 2;
baseBlock.y = BASE_Y;
game.addChild(baseBlock);
stack.push(baseBlock);
// Helper: Add a new moving block at the top
function spawnMovingBlock() {
blockWidth = stack[stack.length - 1].width;
if (blockWidth < BLOCK_MIN_WIDTH) {
endGame();
return;
}
blockHeight = BLOCK_HEIGHT;
blockX = 0 + blockWidth / 2 + 80; // Start from left
blockY = BLOCK_START_Y;
movingDir = 1;
movingSpeed = BLOCK_MOVE_SPEED + Math.floor(score / 10) * 2; // Slightly faster as score increases
movingBlock = new IceBlock();
movingBlock.setSize(blockWidth, blockHeight);
movingBlock.x = blockX;
movingBlock.y = blockY;
movingBlock.alpha = 1;
game.addChild(movingBlock);
canDrop = true;
}
// Helper: End the game
function endGame() {
if (gameOver) return;
gameOver = true;
LK.showGameOver();
}
// Helper: Update score display
function updateScore() {
scoreTxt.setText(score);
}
// Helper: Drop the moving block
function dropBlock() {
if (!movingBlock || !canDrop || gameOver) return;
canDrop = false;
// Find the top block to stack on
var prevBlock = stack[stack.length - 1];
var prevX = prevBlock.x;
var prevY = prevBlock.y;
var prevW = prevBlock.width;
// Target Y for stacking
var targetY = prevY - blockHeight;
// Animate drop
movingBlock.dropTo(targetY, 220, function () {
// Check overlap
var leftEdge = movingBlock.x - blockWidth / 2;
var rightEdge = movingBlock.x + blockWidth / 2;
var prevLeft = prevX - prevW / 2;
var prevRight = prevX + prevW / 2;
var overlapLeft = Math.max(leftEdge, prevLeft);
var overlapRight = Math.min(rightEdge, prevRight);
var overlapWidth = overlapRight - overlapLeft;
if (overlapWidth <= 0) {
// Missed completely
movingBlock.shake();
tween(movingBlock, {
alpha: 0
}, {
duration: 300,
onFinish: function onFinish() {
endGame();
}
});
return;
}
// If overhang, break off the left or right part
var overhangLeft = overlapLeft - leftEdge;
var overhangRight = rightEdge - overlapRight;
// Animate left shard
if (overhangLeft > 2) {
var shardL = new IceShard();
shardL.setSize(overhangLeft, blockHeight);
shardL.x = leftEdge + overhangLeft / 2;
shardL.y = targetY;
game.addChild(shardL);
shardL.fallAndFade(function () {
shardL.destroy();
});
}
// Animate right shard
if (overhangRight > 2) {
var shardR = new IceShard();
shardR.setSize(overhangRight, blockHeight);
shardR.x = rightEdge - overhangRight / 2;
shardR.y = targetY;
game.addChild(shardR);
shardR.fallAndFade(function () {
shardR.destroy();
});
}
// Shrink the block to the overlap
movingBlock.setSize(overlapWidth, blockHeight);
movingBlock.x = overlapLeft + overlapWidth / 2;
movingBlock.y = targetY;
stack.push(movingBlock);
// Update score
score += 1;
updateScore();
// Move camera up if needed (optional: not implemented, as LK handles scaling)
// Spawn next block
spawnMovingBlock();
});
}
// Handle tap/click to drop block
game.down = function (x, y, obj) {
if (canDrop && !gameOver) {
dropBlock();
}
};
// Main game update loop
game.update = function () {
if (gameOver) return;
// Move the moving block horizontally
if (movingBlock && canDrop) {
blockX += movingDir * movingSpeed;
// Clamp and bounce at edges
var minX = blockWidth / 2 + 80;
var maxX = GAME_WIDTH - blockWidth / 2 - 80;
if (blockX > maxX) {
blockX = maxX;
movingDir = -1;
}
if (blockX < minX) {
blockX = minX;
movingDir = 1;
}
movingBlock.x = blockX;
}
};
// Start the game
function startGame() {
// Reset state
for (var i = stack.length - 1; i >= 1; i--) {
stack[i].destroy();
stack.splice(i, 1);
}
if (movingBlock) {
movingBlock.destroy();
movingBlock = null;
}
score = 0;
updateScore();
gameOver = false;
spawnMovingBlock();
}
// On game start
startGame();
// On game over, restart game when LK resets
// (LK will re-initialize the Game class, so no need to handle reset here) /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// IceBlock: Represents a single moving or stacked ice block
var IceBlock = Container.expand(function () {
var self = Container.call(this);
// Attach the ice block asset
var block = self.attachAsset('iceBlock', {
anchorX: 0.5,
anchorY: 1
});
// Set block size (will be updated on creation)
self.setSize = function (w, h) {
block.width = w;
block.height = h;
self.width = w;
self.height = h;
};
// Animate block drop
self.dropTo = function (targetY, duration, onFinish) {
tween(self, {
y: targetY
}, {
duration: duration,
easing: tween.cubicIn,
onFinish: onFinish
});
};
// Animate block shake (for miss)
self.shake = function () {
var origX = self.x;
tween(self, {
x: origX - 30
}, {
duration: 60,
easing: tween.linear,
onFinish: function onFinish() {
tween(self, {
x: origX + 30
}, {
duration: 60,
easing: tween.linear,
onFinish: function onFinish() {
tween(self, {
x: origX
}, {
duration: 60
});
}
});
}
});
};
return self;
});
// IceShard: Represents a falling broken piece
var IceShard = Container.expand(function () {
var self = Container.call(this);
var shard = self.attachAsset('iceShard', {
anchorX: 0.5,
anchorY: 1
});
self.setSize = function (w, h) {
shard.width = w;
shard.height = h;
self.width = w;
self.height = h;
};
// Animate falling and fading out
self.fallAndFade = function (_onFinish) {
tween(self, {
y: self.y + 400,
alpha: 0
}, {
duration: 600,
easing: tween.cubicIn,
onFinish: function onFinish() {
if (_onFinish) _onFinish();
}
});
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a2a3a
});
/****
* Game Code
****/
// Shard (falling broken piece)
// Tower base (static base block)
// Ice block (main block for stacking)
// Game constants
var GAME_WIDTH = 2048;
var GAME_HEIGHT = 2732;
var BASE_Y = GAME_HEIGHT - 200;
var BLOCK_START_Y = 400;
var BLOCK_MIN_WIDTH = 120;
var BLOCK_HEIGHT = 100;
var BLOCK_MOVE_SPEED = 12; // px per frame
// Game state
var stack = []; // Array of stacked blocks (bottom to top)
var movingBlock = null; // The currently moving block
var movingDir = 1; // 1: right, -1: left
var movingSpeed = BLOCK_MOVE_SPEED;
var blockWidth = 600;
var blockHeight = BLOCK_HEIGHT;
var blockX = GAME_WIDTH / 2;
var blockY = BLOCK_START_Y;
var canDrop = true;
var score = 0;
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);
// Tower base
var baseBlock = new IceBlock();
baseBlock.setSize(700, 120);
baseBlock.x = GAME_WIDTH / 2;
baseBlock.y = BASE_Y;
game.addChild(baseBlock);
stack.push(baseBlock);
// Helper: Add a new moving block at the top
function spawnMovingBlock() {
blockWidth = stack[stack.length - 1].width;
if (blockWidth < BLOCK_MIN_WIDTH) {
endGame();
return;
}
blockHeight = BLOCK_HEIGHT;
blockX = 0 + blockWidth / 2 + 80; // Start from left
blockY = BLOCK_START_Y;
movingDir = 1;
movingSpeed = BLOCK_MOVE_SPEED + Math.floor(score / 10) * 2; // Slightly faster as score increases
movingBlock = new IceBlock();
movingBlock.setSize(blockWidth, blockHeight);
movingBlock.x = blockX;
movingBlock.y = blockY;
movingBlock.alpha = 1;
game.addChild(movingBlock);
canDrop = true;
}
// Helper: End the game
function endGame() {
if (gameOver) return;
gameOver = true;
LK.showGameOver();
}
// Helper: Update score display
function updateScore() {
scoreTxt.setText(score);
}
// Helper: Drop the moving block
function dropBlock() {
if (!movingBlock || !canDrop || gameOver) return;
canDrop = false;
// Find the top block to stack on
var prevBlock = stack[stack.length - 1];
var prevX = prevBlock.x;
var prevY = prevBlock.y;
var prevW = prevBlock.width;
// Target Y for stacking
var targetY = prevY - blockHeight;
// Animate drop
movingBlock.dropTo(targetY, 220, function () {
// Check overlap
var leftEdge = movingBlock.x - blockWidth / 2;
var rightEdge = movingBlock.x + blockWidth / 2;
var prevLeft = prevX - prevW / 2;
var prevRight = prevX + prevW / 2;
var overlapLeft = Math.max(leftEdge, prevLeft);
var overlapRight = Math.min(rightEdge, prevRight);
var overlapWidth = overlapRight - overlapLeft;
if (overlapWidth <= 0) {
// Missed completely
movingBlock.shake();
tween(movingBlock, {
alpha: 0
}, {
duration: 300,
onFinish: function onFinish() {
endGame();
}
});
return;
}
// If overhang, break off the left or right part
var overhangLeft = overlapLeft - leftEdge;
var overhangRight = rightEdge - overlapRight;
// Animate left shard
if (overhangLeft > 2) {
var shardL = new IceShard();
shardL.setSize(overhangLeft, blockHeight);
shardL.x = leftEdge + overhangLeft / 2;
shardL.y = targetY;
game.addChild(shardL);
shardL.fallAndFade(function () {
shardL.destroy();
});
}
// Animate right shard
if (overhangRight > 2) {
var shardR = new IceShard();
shardR.setSize(overhangRight, blockHeight);
shardR.x = rightEdge - overhangRight / 2;
shardR.y = targetY;
game.addChild(shardR);
shardR.fallAndFade(function () {
shardR.destroy();
});
}
// Shrink the block to the overlap
movingBlock.setSize(overlapWidth, blockHeight);
movingBlock.x = overlapLeft + overlapWidth / 2;
movingBlock.y = targetY;
stack.push(movingBlock);
// Update score
score += 1;
updateScore();
// Move camera up if needed (optional: not implemented, as LK handles scaling)
// Spawn next block
spawnMovingBlock();
});
}
// Handle tap/click to drop block
game.down = function (x, y, obj) {
if (canDrop && !gameOver) {
dropBlock();
}
};
// Main game update loop
game.update = function () {
if (gameOver) return;
// Move the moving block horizontally
if (movingBlock && canDrop) {
blockX += movingDir * movingSpeed;
// Clamp and bounce at edges
var minX = blockWidth / 2 + 80;
var maxX = GAME_WIDTH - blockWidth / 2 - 80;
if (blockX > maxX) {
blockX = maxX;
movingDir = -1;
}
if (blockX < minX) {
blockX = minX;
movingDir = 1;
}
movingBlock.x = blockX;
}
};
// Start the game
function startGame() {
// Reset state
for (var i = stack.length - 1; i >= 1; i--) {
stack[i].destroy();
stack.splice(i, 1);
}
if (movingBlock) {
movingBlock.destroy();
movingBlock = null;
}
score = 0;
updateScore();
gameOver = false;
spawnMovingBlock();
}
// On game start
startGame();
// On game over, restart game when LK resets
// (LK will re-initialize the Game class, so no need to handle reset here)