/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Block = Container.expand(function (color, width, height) {
var self = Container.call(this);
// Default values if not provided
color = color || 0x4287f5;
width = width || 100;
height = height || 100;
// Create custom block shape with specified color and dimensions
var blockShape = self.attachAsset('block', {
anchorX: 0.5,
anchorY: 0.5,
width: width,
height: height
});
// Set the block's color
blockShape.tint = color;
// Properties
self.isPlaced = false;
self.velocity = {
x: 0,
y: 0
};
self.acceleration = {
x: 0,
y: 0.2
}; // Gravity
self.onGround = false;
self.width = width;
self.height = height;
// Physics update
self.updatePhysics = function () {
if (self.isPlaced && !self.onGround) {
// Apply physics only if the block is placed but not on ground
self.velocity.x += self.acceleration.x;
self.velocity.y += self.acceleration.y;
self.x += self.velocity.x;
self.y += self.velocity.y;
// Check boundaries
if (self.x < self.width / 2) {
self.x = self.width / 2;
self.velocity.x = -self.velocity.x * 0.5; // Bounce with damping
} else if (self.x > 2048 - self.width / 2) {
self.x = 2048 - self.width / 2;
self.velocity.x = -self.velocity.x * 0.5; // Bounce with damping
}
// Check if block has hit the ground
if (self.y >= groundY - self.height / 2) {
self.y = groundY - self.height / 2;
self.velocity.y = 0;
self.velocity.x *= 0.9; // Friction
self.onGround = true;
LK.getSound('fall').play();
}
}
};
// Dragging handlers
self.down = function (x, y, obj) {
if (!self.isPlaced) {
currentDraggedBlock = self;
}
};
return self;
});
var Platform = Container.expand(function (x, y, width) {
var self = Container.call(this);
width = width || 500;
// Create platform shape
var platformShape = self.attachAsset('platform', {
anchorX: 0.5,
anchorY: 0.5,
width: width,
height: 20
});
// Position platform
self.x = x;
self.y = y;
// Platform properties
self.width = width;
self.height = 20;
return self;
});
var ScoreZone = Container.expand(function (x, y, width) {
var self = Container.call(this);
width = width || 500;
// Create score zone shape - this will be invisible in the game
var zoneShape = self.attachAsset('scoreZone', {
anchorX: 0.5,
anchorY: 0,
width: width,
height: 2000,
alpha: 0.1 // Almost invisible
});
// Position zone
self.x = x;
self.y = y;
// Zone properties
self.width = width;
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87CEEB // Sky blue background
});
/****
* Game Code
****/
// Game variables
var currentLevel = storage.level || 1;
var score = 0;
var currentDraggedBlock = null;
var blocks = [];
var placedBlocks = [];
var maxStackHeight = 0;
var groundY = 2500;
var platformY = groundY - 20;
var centerX = 2048 / 2;
var levelCompleted = false;
var windStrength = 0;
var windDirection = 1;
var blockColors = [0x4287f5, 0xff5757, 0x57ff57, 0xffff57, 0xff57ff, 0x57ffff];
var nextBlockColor = blockColors[Math.floor(Math.random() * blockColors.length)];
var nextBlockWidth = 100;
var nextBlockHeight = 100;
var blockWidths = [80, 100, 120, 150];
var blockHeights = [60, 80, 100, 120];
// Set up game environment
game.setBackgroundColor(0x87CEEB); // Sky blue background
// Create ground
var ground = game.addChild(LK.getAsset('ground', {
anchorX: 0.5,
anchorY: 0,
width: 2048,
height: 100
}));
ground.x = centerX;
ground.y = groundY;
// Create main platform
var mainPlatform = game.addChild(new Platform(centerX, platformY, 500));
// Create score zone (for checking if blocks are within scoring area)
var scoreZone = game.addChild(new ScoreZone(centerX, 0, 500));
// Score display
var scoreTxt = new Text2('Score: 0', {
size: 70,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(1, 0); // Top right anchor
LK.gui.topRight.addChild(scoreTxt);
// Level display
var levelTxt = new Text2('Level: ' + currentLevel, {
size: 70,
fill: 0xFFFFFF
});
levelTxt.anchor.set(0, 0); // Top left anchor
LK.gui.topRight.addChild(levelTxt);
levelTxt.y = 80; // Position below score
// Height display
var heightTxt = new Text2('Tower Height: 0m', {
size: 50,
fill: 0xFFFFFF
});
heightTxt.anchor.set(0.5, 0); // Top center anchor
LK.gui.top.addChild(heightTxt);
heightTxt.y = 20;
// Message text for level completion, etc.
var messageTxt = new Text2('', {
size: 80,
fill: 0xFFFF00
});
messageTxt.anchor.set(0.5, 0.5); // Center anchor
LK.gui.center.addChild(messageTxt);
messageTxt.alpha = 0;
// Create initial block
function createNewBlock() {
var newBlock = new Block(nextBlockColor, nextBlockWidth, nextBlockHeight);
newBlock.x = centerX;
newBlock.y = 200;
// Set next block's properties
nextBlockColor = blockColors[Math.floor(Math.random() * blockColors.length)];
// As levels progress, vary block sizes more
if (currentLevel > 1) {
nextBlockWidth = blockWidths[Math.floor(Math.random() * blockWidths.length)];
nextBlockHeight = blockHeights[Math.floor(Math.random() * blockHeights.length)];
}
blocks.push(newBlock);
game.addChild(newBlock);
return newBlock;
}
// Create first block
createNewBlock();
// Update score
function updateScore(points) {
score += points;
scoreTxt.setText('Score: ' + score);
}
// Update level
function setLevel(level) {
currentLevel = level;
storage.level = level;
levelTxt.setText('Level: ' + currentLevel);
// Adjust difficulty based on level
windStrength = (currentLevel - 1) * 0.02;
}
// Check tower height and update
function updateTowerHeight() {
if (placedBlocks.length === 0) {
return 0;
}
// Find the highest block's top edge
var highestY = groundY;
for (var i = 0; i < placedBlocks.length; i++) {
var blockTopY = placedBlocks[i].y - placedBlocks[i].height / 2;
if (blockTopY < highestY) {
highestY = blockTopY;
}
}
// Calculate height from ground
var height = groundY - highestY;
var heightInMeters = Math.floor(height / 50); // Convert pixels to "meters" for display
heightTxt.setText('Tower Height: ' + heightInMeters + 'm');
// Update max height if needed
if (height > maxStackHeight) {
maxStackHeight = height;
// Check for level completion
var requiredHeight = 200 + currentLevel * 100;
if (maxStackHeight >= requiredHeight && !levelCompleted) {
levelCompleted = true;
showLevelCompleteMessage();
}
}
return height;
}
// Show level complete message
function showLevelCompleteMessage() {
messageTxt.setText('Level ' + currentLevel + ' Complete!');
messageTxt.alpha = 1;
// Play success sound
LK.getSound('success').play();
// Award bonus points for level completion
updateScore(currentLevel * 100);
// Set timeout to advance to next level
LK.setTimeout(function () {
setLevel(currentLevel + 1);
levelCompleted = false;
// Hide message with fade
tween(messageTxt, {
alpha: 0
}, {
duration: 1000
});
// Create new block if needed
if (blocks.length === 0) {
createNewBlock();
}
}, 3000);
}
// Check stability of the tower
function checkTowerStability() {
// Simple implementation: if blocks are stacked too high without sufficient width, they become unstable
var unstableBlocks = 0;
for (var i = 0; i < placedBlocks.length; i++) {
var block = placedBlocks[i];
// Skip blocks that are already on the ground
if (block.onGround) {
continue;
}
var isSupported = false;
// Check if block is supported by platform
if (block.y + block.height / 2 >= platformY - 10 && block.x + block.width / 2 >= mainPlatform.x - mainPlatform.width / 2 && block.x - block.width / 2 <= mainPlatform.x + mainPlatform.width / 2) {
isSupported = true;
} else {
// Check if supported by other blocks
for (var j = 0; j < placedBlocks.length; j++) {
if (i !== j) {
var supportBlock = placedBlocks[j];
// Simple overlap check
if (block.y + block.height / 2 >= supportBlock.y - supportBlock.height / 2 - 5 && block.y + block.height / 2 <= supportBlock.y - supportBlock.height / 2 + 5 && block.x + block.width / 2 >= supportBlock.x - supportBlock.width / 2 && block.x - block.width / 2 <= supportBlock.x + supportBlock.width / 2) {
isSupported = true;
break;
}
}
}
}
if (!isSupported) {
// Make block fall
block.velocity.y = 1;
block.onGround = false;
unstableBlocks++;
}
}
return unstableBlocks;
}
// Apply wind effects based on level
function applyWindEffects() {
if (windStrength > 0) {
// Occasionally change wind direction
if (Math.random() < 0.01) {
windDirection = -windDirection;
}
// Apply wind force to blocks
for (var i = 0; i < placedBlocks.length; i++) {
var block = placedBlocks[i];
if (!block.onGround) {
block.velocity.x += windDirection * windStrength;
}
}
}
}
// Handle touch/mouse move
function handleMove(x, y, obj) {
if (currentDraggedBlock) {
currentDraggedBlock.x = x;
currentDraggedBlock.y = y;
}
}
// Game event handlers
game.move = handleMove;
game.down = function (x, y, obj) {
// Already handled in Block class
};
game.up = function (x, y, obj) {
if (currentDraggedBlock && !currentDraggedBlock.isPlaced) {
// Place the block
currentDraggedBlock.isPlaced = true;
placedBlocks.push(currentDraggedBlock);
// Play placement sound
LK.getSound('place').play();
// Check if block is within score zone
var inScoreZone = currentDraggedBlock.x + currentDraggedBlock.width / 2 >= scoreZone.x - scoreZone.width / 2 && currentDraggedBlock.x - currentDraggedBlock.width / 2 <= scoreZone.x + scoreZone.width / 2;
// Award points based on placement height and zone
var points = 10;
if (inScoreZone) {
points += 20;
}
// Bonus points for height
var height = groundY - currentDraggedBlock.y;
points += Math.floor(height / 50) * 5;
updateScore(points);
// Create a new block after a short delay
LK.setTimeout(function () {
if (!levelCompleted && blocks.length < 2) {
createNewBlock();
}
}, 500);
currentDraggedBlock = null;
}
};
// Main game update loop
game.update = function () {
// Skip updates if game is in level transition
if (messageTxt.alpha > 0) {
return;
}
// Update all placed blocks
for (var i = 0; i < placedBlocks.length; i++) {
placedBlocks[i].updatePhysics();
}
// Check tower stability
checkTowerStability();
// Apply wind effects
applyWindEffects();
// Update tower height
updateTowerHeight();
// Check for game over (structure collapse)
var unstableCount = 0;
for (var j = 0; j < placedBlocks.length; j++) {
if (placedBlocks[j].onGround && (placedBlocks[j].x < centerX - 500 || placedBlocks[j].x > centerX + 500)) {
unstableCount++;
}
}
// If too many blocks have fallen off the platform, game over
if (unstableCount > 3 && placedBlocks.length > 5) {
LK.showGameOver();
}
// Every 5 seconds, check if tower is completely stable
if (LK.ticks % 300 === 0 && placedBlocks.length > 0) {
var allStable = true;
for (var k = 0; k < placedBlocks.length; k++) {
if (!placedBlocks[k].onGround && (Math.abs(placedBlocks[k].velocity.x) > 0.1 || Math.abs(placedBlocks[k].velocity.y) > 0.1)) {
allStable = false;
break;
}
}
// If tower is completely stable and tall enough, award stability bonus
if (allStable && maxStackHeight > 200) {
updateScore(25);
// Show small message
messageTxt.setText('Stability Bonus: +25');
messageTxt.alpha = 1;
// Fade out message
tween(messageTxt, {
alpha: 0
}, {
duration: 1500
});
}
}
};
// Play background music
LK.playMusic('bgmusic', {
fade: {
start: 0,
end: 0.3,
duration: 2000
}
}); /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Block = Container.expand(function (color, width, height) {
var self = Container.call(this);
// Default values if not provided
color = color || 0x4287f5;
width = width || 100;
height = height || 100;
// Create custom block shape with specified color and dimensions
var blockShape = self.attachAsset('block', {
anchorX: 0.5,
anchorY: 0.5,
width: width,
height: height
});
// Set the block's color
blockShape.tint = color;
// Properties
self.isPlaced = false;
self.velocity = {
x: 0,
y: 0
};
self.acceleration = {
x: 0,
y: 0.2
}; // Gravity
self.onGround = false;
self.width = width;
self.height = height;
// Physics update
self.updatePhysics = function () {
if (self.isPlaced && !self.onGround) {
// Apply physics only if the block is placed but not on ground
self.velocity.x += self.acceleration.x;
self.velocity.y += self.acceleration.y;
self.x += self.velocity.x;
self.y += self.velocity.y;
// Check boundaries
if (self.x < self.width / 2) {
self.x = self.width / 2;
self.velocity.x = -self.velocity.x * 0.5; // Bounce with damping
} else if (self.x > 2048 - self.width / 2) {
self.x = 2048 - self.width / 2;
self.velocity.x = -self.velocity.x * 0.5; // Bounce with damping
}
// Check if block has hit the ground
if (self.y >= groundY - self.height / 2) {
self.y = groundY - self.height / 2;
self.velocity.y = 0;
self.velocity.x *= 0.9; // Friction
self.onGround = true;
LK.getSound('fall').play();
}
}
};
// Dragging handlers
self.down = function (x, y, obj) {
if (!self.isPlaced) {
currentDraggedBlock = self;
}
};
return self;
});
var Platform = Container.expand(function (x, y, width) {
var self = Container.call(this);
width = width || 500;
// Create platform shape
var platformShape = self.attachAsset('platform', {
anchorX: 0.5,
anchorY: 0.5,
width: width,
height: 20
});
// Position platform
self.x = x;
self.y = y;
// Platform properties
self.width = width;
self.height = 20;
return self;
});
var ScoreZone = Container.expand(function (x, y, width) {
var self = Container.call(this);
width = width || 500;
// Create score zone shape - this will be invisible in the game
var zoneShape = self.attachAsset('scoreZone', {
anchorX: 0.5,
anchorY: 0,
width: width,
height: 2000,
alpha: 0.1 // Almost invisible
});
// Position zone
self.x = x;
self.y = y;
// Zone properties
self.width = width;
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87CEEB // Sky blue background
});
/****
* Game Code
****/
// Game variables
var currentLevel = storage.level || 1;
var score = 0;
var currentDraggedBlock = null;
var blocks = [];
var placedBlocks = [];
var maxStackHeight = 0;
var groundY = 2500;
var platformY = groundY - 20;
var centerX = 2048 / 2;
var levelCompleted = false;
var windStrength = 0;
var windDirection = 1;
var blockColors = [0x4287f5, 0xff5757, 0x57ff57, 0xffff57, 0xff57ff, 0x57ffff];
var nextBlockColor = blockColors[Math.floor(Math.random() * blockColors.length)];
var nextBlockWidth = 100;
var nextBlockHeight = 100;
var blockWidths = [80, 100, 120, 150];
var blockHeights = [60, 80, 100, 120];
// Set up game environment
game.setBackgroundColor(0x87CEEB); // Sky blue background
// Create ground
var ground = game.addChild(LK.getAsset('ground', {
anchorX: 0.5,
anchorY: 0,
width: 2048,
height: 100
}));
ground.x = centerX;
ground.y = groundY;
// Create main platform
var mainPlatform = game.addChild(new Platform(centerX, platformY, 500));
// Create score zone (for checking if blocks are within scoring area)
var scoreZone = game.addChild(new ScoreZone(centerX, 0, 500));
// Score display
var scoreTxt = new Text2('Score: 0', {
size: 70,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(1, 0); // Top right anchor
LK.gui.topRight.addChild(scoreTxt);
// Level display
var levelTxt = new Text2('Level: ' + currentLevel, {
size: 70,
fill: 0xFFFFFF
});
levelTxt.anchor.set(0, 0); // Top left anchor
LK.gui.topRight.addChild(levelTxt);
levelTxt.y = 80; // Position below score
// Height display
var heightTxt = new Text2('Tower Height: 0m', {
size: 50,
fill: 0xFFFFFF
});
heightTxt.anchor.set(0.5, 0); // Top center anchor
LK.gui.top.addChild(heightTxt);
heightTxt.y = 20;
// Message text for level completion, etc.
var messageTxt = new Text2('', {
size: 80,
fill: 0xFFFF00
});
messageTxt.anchor.set(0.5, 0.5); // Center anchor
LK.gui.center.addChild(messageTxt);
messageTxt.alpha = 0;
// Create initial block
function createNewBlock() {
var newBlock = new Block(nextBlockColor, nextBlockWidth, nextBlockHeight);
newBlock.x = centerX;
newBlock.y = 200;
// Set next block's properties
nextBlockColor = blockColors[Math.floor(Math.random() * blockColors.length)];
// As levels progress, vary block sizes more
if (currentLevel > 1) {
nextBlockWidth = blockWidths[Math.floor(Math.random() * blockWidths.length)];
nextBlockHeight = blockHeights[Math.floor(Math.random() * blockHeights.length)];
}
blocks.push(newBlock);
game.addChild(newBlock);
return newBlock;
}
// Create first block
createNewBlock();
// Update score
function updateScore(points) {
score += points;
scoreTxt.setText('Score: ' + score);
}
// Update level
function setLevel(level) {
currentLevel = level;
storage.level = level;
levelTxt.setText('Level: ' + currentLevel);
// Adjust difficulty based on level
windStrength = (currentLevel - 1) * 0.02;
}
// Check tower height and update
function updateTowerHeight() {
if (placedBlocks.length === 0) {
return 0;
}
// Find the highest block's top edge
var highestY = groundY;
for (var i = 0; i < placedBlocks.length; i++) {
var blockTopY = placedBlocks[i].y - placedBlocks[i].height / 2;
if (blockTopY < highestY) {
highestY = blockTopY;
}
}
// Calculate height from ground
var height = groundY - highestY;
var heightInMeters = Math.floor(height / 50); // Convert pixels to "meters" for display
heightTxt.setText('Tower Height: ' + heightInMeters + 'm');
// Update max height if needed
if (height > maxStackHeight) {
maxStackHeight = height;
// Check for level completion
var requiredHeight = 200 + currentLevel * 100;
if (maxStackHeight >= requiredHeight && !levelCompleted) {
levelCompleted = true;
showLevelCompleteMessage();
}
}
return height;
}
// Show level complete message
function showLevelCompleteMessage() {
messageTxt.setText('Level ' + currentLevel + ' Complete!');
messageTxt.alpha = 1;
// Play success sound
LK.getSound('success').play();
// Award bonus points for level completion
updateScore(currentLevel * 100);
// Set timeout to advance to next level
LK.setTimeout(function () {
setLevel(currentLevel + 1);
levelCompleted = false;
// Hide message with fade
tween(messageTxt, {
alpha: 0
}, {
duration: 1000
});
// Create new block if needed
if (blocks.length === 0) {
createNewBlock();
}
}, 3000);
}
// Check stability of the tower
function checkTowerStability() {
// Simple implementation: if blocks are stacked too high without sufficient width, they become unstable
var unstableBlocks = 0;
for (var i = 0; i < placedBlocks.length; i++) {
var block = placedBlocks[i];
// Skip blocks that are already on the ground
if (block.onGround) {
continue;
}
var isSupported = false;
// Check if block is supported by platform
if (block.y + block.height / 2 >= platformY - 10 && block.x + block.width / 2 >= mainPlatform.x - mainPlatform.width / 2 && block.x - block.width / 2 <= mainPlatform.x + mainPlatform.width / 2) {
isSupported = true;
} else {
// Check if supported by other blocks
for (var j = 0; j < placedBlocks.length; j++) {
if (i !== j) {
var supportBlock = placedBlocks[j];
// Simple overlap check
if (block.y + block.height / 2 >= supportBlock.y - supportBlock.height / 2 - 5 && block.y + block.height / 2 <= supportBlock.y - supportBlock.height / 2 + 5 && block.x + block.width / 2 >= supportBlock.x - supportBlock.width / 2 && block.x - block.width / 2 <= supportBlock.x + supportBlock.width / 2) {
isSupported = true;
break;
}
}
}
}
if (!isSupported) {
// Make block fall
block.velocity.y = 1;
block.onGround = false;
unstableBlocks++;
}
}
return unstableBlocks;
}
// Apply wind effects based on level
function applyWindEffects() {
if (windStrength > 0) {
// Occasionally change wind direction
if (Math.random() < 0.01) {
windDirection = -windDirection;
}
// Apply wind force to blocks
for (var i = 0; i < placedBlocks.length; i++) {
var block = placedBlocks[i];
if (!block.onGround) {
block.velocity.x += windDirection * windStrength;
}
}
}
}
// Handle touch/mouse move
function handleMove(x, y, obj) {
if (currentDraggedBlock) {
currentDraggedBlock.x = x;
currentDraggedBlock.y = y;
}
}
// Game event handlers
game.move = handleMove;
game.down = function (x, y, obj) {
// Already handled in Block class
};
game.up = function (x, y, obj) {
if (currentDraggedBlock && !currentDraggedBlock.isPlaced) {
// Place the block
currentDraggedBlock.isPlaced = true;
placedBlocks.push(currentDraggedBlock);
// Play placement sound
LK.getSound('place').play();
// Check if block is within score zone
var inScoreZone = currentDraggedBlock.x + currentDraggedBlock.width / 2 >= scoreZone.x - scoreZone.width / 2 && currentDraggedBlock.x - currentDraggedBlock.width / 2 <= scoreZone.x + scoreZone.width / 2;
// Award points based on placement height and zone
var points = 10;
if (inScoreZone) {
points += 20;
}
// Bonus points for height
var height = groundY - currentDraggedBlock.y;
points += Math.floor(height / 50) * 5;
updateScore(points);
// Create a new block after a short delay
LK.setTimeout(function () {
if (!levelCompleted && blocks.length < 2) {
createNewBlock();
}
}, 500);
currentDraggedBlock = null;
}
};
// Main game update loop
game.update = function () {
// Skip updates if game is in level transition
if (messageTxt.alpha > 0) {
return;
}
// Update all placed blocks
for (var i = 0; i < placedBlocks.length; i++) {
placedBlocks[i].updatePhysics();
}
// Check tower stability
checkTowerStability();
// Apply wind effects
applyWindEffects();
// Update tower height
updateTowerHeight();
// Check for game over (structure collapse)
var unstableCount = 0;
for (var j = 0; j < placedBlocks.length; j++) {
if (placedBlocks[j].onGround && (placedBlocks[j].x < centerX - 500 || placedBlocks[j].x > centerX + 500)) {
unstableCount++;
}
}
// If too many blocks have fallen off the platform, game over
if (unstableCount > 3 && placedBlocks.length > 5) {
LK.showGameOver();
}
// Every 5 seconds, check if tower is completely stable
if (LK.ticks % 300 === 0 && placedBlocks.length > 0) {
var allStable = true;
for (var k = 0; k < placedBlocks.length; k++) {
if (!placedBlocks[k].onGround && (Math.abs(placedBlocks[k].velocity.x) > 0.1 || Math.abs(placedBlocks[k].velocity.y) > 0.1)) {
allStable = false;
break;
}
}
// If tower is completely stable and tall enough, award stability bonus
if (allStable && maxStackHeight > 200) {
updateScore(25);
// Show small message
messageTxt.setText('Stability Bonus: +25');
messageTxt.alpha = 1;
// Fade out message
tween(messageTxt, {
alpha: 0
}, {
duration: 1500
});
}
}
};
// Play background music
LK.playMusic('bgmusic', {
fade: {
start: 0,
end: 0.3,
duration: 2000
}
});