/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var CutPiece = Container.expand(function () {
var self = Container.call(this);
var piece = self.attachAsset('cutPiece', {
anchorX: 0.5,
anchorY: 0.5
});
self.animate = function (direction, blockWidth) {
// Set initial appearance
piece.alpha = 1;
piece.width = blockWidth;
// Animate piece falling and rotating
var fallDirection = direction === 'left' ? -1 : 1;
tween(self, {
x: self.x + fallDirection * 400,
y: self.y + 600,
rotation: fallDirection * Math.PI * 2
}, {
duration: 1200,
easing: tween.easeIn
});
tween(piece, {
alpha: 0
}, {
duration: 1200,
easing: tween.linear,
onFinish: function onFinish() {
self.destroyed = true;
returnCutPieceToPool(self);
}
});
};
return self;
});
var Particle = Container.expand(function () {
var self = Container.call(this);
var particle = self.attachAsset('particle', {
anchorX: 0.5,
anchorY: 0.5
});
particle.width = 28;
particle.height = 28;
self.animate = function (velocityX, velocityY, color) {
// Set particle color with slight brightness variation
particle.tint = color || 0xFFFFFF;
// Randomize particle size for more variety
var sizeVariation = 0.5 + Math.random() * 1.0; // Size between 0.5x and 1.5x
particle.width = 28 * sizeVariation;
particle.height = 28 * sizeVariation;
// Set initial velocity with slight randomness
self.velocityX = velocityX + (Math.random() - 0.5) * 2;
self.velocityY = velocityY + (Math.random() - 0.5) * 2;
// Vary gravity for different particle behavior
self.gravity = 0.3 + Math.random() * 0.4; // Gravity between 0.3 and 0.7
// Vary particle lifetime for more dynamic effect
self.life = 45 + Math.random() * 30; // Lifetime between 45-75 frames
self.maxLife = self.life;
// Add initial scale animation for spawn effect
particle.scaleX = 0.1;
particle.scaleY = 0.1;
tween(particle, {
scaleX: sizeVariation,
scaleY: sizeVariation
}, {
duration: 200,
easing: tween.easeOut
});
};
self.update = function () {
if (self.life <= 0) {
self.destroyed = true;
returnParticleToPool(self);
return;
}
// Update position with velocity
self.x += self.velocityX;
self.y += self.velocityY;
// Apply gravity with slight randomness
self.velocityY += self.gravity + (Math.random() - 0.5) * 0.1;
// Apply air resistance to create more realistic movement
self.velocityX *= 0.98;
self.velocityY *= 0.99;
// Add slight rotation for visual interest
particle.rotation += self.velocityX * 0.02;
// Enhanced fade out with scale reduction
var alpha = self.life / self.maxLife;
particle.alpha = alpha;
particle.scaleX = alpha * 0.8 + 0.2; // Scale down as it fades
particle.scaleY = alpha * 0.8 + 0.2;
// Reduce life
self.life--;
};
return self;
});
var StackedBlock = Container.expand(function () {
var self = Container.call(this);
var block = self.attachAsset('block', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x808080
});
/****
* Game Code
****/
// Game state
var stackedBlocks = [];
var particles = [];
var gameHeight = 2732;
var gameWidth = 2048;
var groundLevel = gameHeight - 120;
var stackHeight = 0;
var gameActive = true;
// Object pools for memory optimization
var particlePool = [];
var cutPiecePool = [];
var swingingBlockPool = [];
// Pool management functions
function getParticleFromPool() {
if (particlePool.length > 0) {
var particle = particlePool.pop();
particle.visible = true;
particle.destroyed = false;
return particle;
}
return new Particle();
}
function returnParticleToPool(particle) {
if (particle && !particle.pooled) {
particle.visible = false;
particle.pooled = true;
particlePool.push(particle);
}
}
function getCutPieceFromPool() {
if (cutPiecePool.length > 0) {
var cutPiece = cutPiecePool.pop();
cutPiece.visible = true;
cutPiece.destroyed = false;
return cutPiece;
}
return new CutPiece();
}
function returnCutPieceToPool(cutPiece) {
if (cutPiece && !cutPiece.pooled) {
cutPiece.visible = false;
cutPiece.pooled = true;
cutPiecePool.push(cutPiece);
}
}
function getSwingingBlockFromPool() {
if (swingingBlockPool.length > 0) {
var block = swingingBlockPool.pop();
block.visible = true;
block.destroyed = false;
block.falling = false;
block.rotationStarted = false;
block.velocityY = 0;
block.rotation = 0;
block.scaleX = 1;
block.scaleY = 1;
block.width = 350;
block.pooled = false;
return block;
}
return LK.getAsset('swingingBlock', {
anchorX: 0.5,
anchorY: 0.5
});
}
function returnSwingingBlockToPool(block) {
if (block && !block.pooled) {
block.visible = false;
block.pooled = true;
swingingBlockPool.push(block);
}
}
// Create foundation block
var foundation = new StackedBlock();
foundation.x = gameWidth / 2;
foundation.y = gameHeight - 120; // Move to bottom of screen
game.addChild(foundation);
stackedBlocks.push(foundation);
stackHeight = foundation.y;
// Create rope hanging from top center
var rope = LK.getAsset('rope', {
anchorX: 0.5,
anchorY: 0
});
rope.x = gameWidth / 2;
rope.y = 0; // Rope starts at top of screen
game.addChild(rope);
// Create swinging block at the end of rope
var swingingBlock = getSwingingBlockFromPool();
swingingBlock.x = 0;
swingingBlock.y = rope.height + 100; // Extend the rope length by moving block further down
rope.addChild(swingingBlock);
// Natural pendulum swing variables
var swingTime = 0;
var swingSpeed = 0.015; // Controls swing speed - reduced for slower initial swing
var swingAmplitude = 1.2; // Controls swing range (max rotation) - increased for wider swing
// Score display
var scoreTxt = new Text2('Blocks: 0', {
size: 80,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Touch handler to drop the block
game.down = function (x, y, obj) {
if (!gameActive || !swingingBlock.parent || swingingBlock.falling) return;
// Get the current world position of the swinging block
var worldPos = rope.toGlobal(swingingBlock.position);
var gamePos = game.toLocal(worldPos);
// Detach from rope and add to game
rope.removeChild(swingingBlock);
game.addChild(swingingBlock);
// Set position in game coordinates
swingingBlock.x = gamePos.x;
swingingBlock.y = gamePos.y;
// Add falling velocity
swingingBlock.velocityY = 25; // Start with constant speed
swingingBlock.falling = true;
// Play falling sound
LK.getSound('fall').play();
};
// Update function for falling blocks
game.update = function () {
// Update particles
for (var i = particles.length - 1; i >= 0; i--) {
var particle = particles[i];
if (particle.destroyed) {
particles.splice(i, 1);
}
}
// Update rope swing with natural pendulum motion
if (rope) {
// Gradually increase swing speed based on score - every 5 blocks
var currentSwingSpeed = swingSpeed + Math.floor(LK.getScore() / 5) * 0.005; // Increase speed by 0.005 every 5 blocks
swingTime += currentSwingSpeed;
rope.rotation = Math.sin(swingTime) * swingAmplitude;
// Add vertical swinging motion to the swinging block only if it exists and is not falling
if (swingingBlock && swingingBlock.parent === rope && !swingingBlock.falling) {
var verticalOffset = Math.cos(swingTime * 2) * 30; // Vertical swing with smaller amplitude
swingingBlock.y = rope.height + 100 + verticalOffset;
}
}
if (swingingBlock.falling) {
// Use constant falling speed instead of accelerating gravity
swingingBlock.velocityY = 25; // Fixed falling speed
swingingBlock.y += swingingBlock.velocityY;
// Rotation animation removed - blocks fall straight down
if (!swingingBlock.rotationStarted) {
swingingBlock.rotationStarted = true;
}
// Check collision with any stacked block using built-in intersects method
var hasCollision = false;
for (var i = 0; i < stackedBlocks.length; i++) {
var block = stackedBlocks[i];
if (swingingBlock.intersects(block)) {
hasCollision = true;
break;
}
}
if (hasCollision) {
// Find the topmost block that the swinging block collided with
var targetBlock = null;
var highestY = gameHeight;
for (var k = 0; k < stackedBlocks.length; k++) {
if (swingingBlock.intersects(stackedBlocks[k]) && stackedBlocks[k].y < highestY) {
targetBlock = stackedBlocks[k];
highestY = stackedBlocks[k].y;
}
}
if (targetBlock) {
// Check if block is close enough to the top of the target block to land
var blockBottom = swingingBlock.y + swingingBlock.height / 2;
var targetTop = targetBlock.y - targetBlock.height / 2;
var landingDistance = Math.abs(blockBottom - targetTop);
// Only land if we're within reasonable landing distance (less than falling speed)
if (landingDistance <= 30) {
// Award 1 point
LK.setScore(LK.getScore() + 1);
scoreTxt.setText('Blocks: ' + LK.getScore());
// Calculate collision overlap and trim the block
var targetLeft = targetBlock.x - targetBlock.width / 2;
var targetRight = targetBlock.x + targetBlock.width / 2;
var swingingLeft = swingingBlock.x - swingingBlock.width / 2;
var swingingRight = swingingBlock.x + swingingBlock.width / 2;
// Find the overlapping area
var overlapLeft = Math.max(targetLeft, swingingLeft);
var overlapRight = Math.min(targetRight, swingingRight);
var overlapWidth = overlapRight - overlapLeft;
// Only proceed if there's actual overlap
if (overlapWidth > 0) {
// Place block exactly on top of target block
swingingBlock.falling = false;
swingingBlock.velocityY = 0;
swingingBlock.y = targetBlock.y - 300; // Place exactly one block height above
// Stop rotation animation and reset to straight position
tween.stop(swingingBlock, {
rotation: true
});
swingingBlock.rotation = 0;
// Add collision impact animation
// Scale the landing block and target block for impact effect
var originalScale = swingingBlock.scaleX;
swingingBlock.scaleX = originalScale * 1.2;
swingingBlock.scaleY = originalScale * 1.2;
targetBlock.scaleX = originalScale * 1.1;
targetBlock.scaleY = originalScale * 1.1;
// Animate back to normal size
tween(swingingBlock, {
scaleX: originalScale,
scaleY: originalScale
}, {
duration: 300,
easing: tween.easeOut
});
tween(targetBlock, {
scaleX: originalScale,
scaleY: originalScale
}, {
duration: 300,
easing: tween.easeOut
});
// Create enhanced particle explosion effect
var particleCount = 20; // Increased from 8 to 20
var collisionCenterX = swingingBlock.x;
var collisionCenterY = swingingBlock.y;
for (var p = 0; p < particleCount; p++) {
var particle = getParticleFromPool();
// Larger spawn area for more spread
particle.x = collisionCenterX + (Math.random() - 0.5) * 80;
particle.y = collisionCenterY + (Math.random() - 0.5) * 50;
// Create more varied velocity directions with bursts
var angle = Math.PI * 2 * p / particleCount + (Math.random() - 0.5) * 0.8;
var speed = 12 + Math.random() * 12; // Increased speed range
var velocityX = Math.cos(angle) * speed;
var velocityY = Math.sin(angle) * speed - Math.random() * 8; // More upward bias
// Random particle colors (yellows, oranges, whites)
var colors = [0xFFD700, 0xFFA500, 0xFFFFFF, 0xFFE4B5, 0xFFF8DC];
var color = colors[Math.floor(Math.random() * colors.length)];
particle.animate(velocityX, velocityY, color);
particles.push(particle);
game.addChild(particle);
}
// Add secondary burst of smaller, faster particles
var secondaryParticleCount = 15;
for (var s = 0; s < secondaryParticleCount; s++) {
var secondaryParticle = getParticleFromPool();
// Tighter spawn area for secondary burst
secondaryParticle.x = collisionCenterX + (Math.random() - 0.5) * 30;
secondaryParticle.y = collisionCenterY + (Math.random() - 0.5) * 20;
// Faster, more chaotic movement
var secondaryAngle = Math.random() * Math.PI * 2;
var secondarySpeed = 15 + Math.random() * 15;
var secondaryVelocityX = Math.cos(secondaryAngle) * secondarySpeed;
var secondaryVelocityY = Math.sin(secondaryAngle) * secondarySpeed - Math.random() * 10;
// Brighter colors for secondary particles
var secondaryColors = [0xFFFFFF, 0xFFFF00, 0xFF6600, 0xFF3300];
var secondaryColor = secondaryColors[Math.floor(Math.random() * secondaryColors.length)];
secondaryParticle.animate(secondaryVelocityX, secondaryVelocityY, secondaryColor);
particles.push(secondaryParticle);
game.addChild(secondaryParticle);
}
// Play collision sound
LK.getSound('collision').play();
// Add screen shake effect by moving the game container
var originalGameX = game.x;
var originalGameY = game.y;
var shakeIntensity = 15;
// Quick shake animation
tween(game, {
x: originalGameX + shakeIntensity,
y: originalGameY + shakeIntensity * 0.5
}, {
duration: 50,
easing: tween.linear,
onFinish: function onFinish() {
tween(game, {
x: originalGameX - shakeIntensity * 0.8,
y: originalGameY - shakeIntensity * 0.3
}, {
duration: 50,
easing: tween.linear,
onFinish: function onFinish() {
tween(game, {
x: originalGameX,
y: originalGameY
}, {
duration: 100,
easing: tween.easeOut
});
}
});
}
});
// Create cut pieces for trimmed parts
var leftTrimmed = swingingLeft < overlapLeft;
var rightTrimmed = swingingRight > overlapRight;
if (leftTrimmed) {
// Create cut piece for left trimmed part
var leftCutPiece = getCutPieceFromPool();
game.addChild(leftCutPiece);
var leftTrimWidth = overlapLeft - swingingLeft;
leftCutPiece.x = swingingLeft + leftTrimWidth / 2;
leftCutPiece.y = swingingBlock.y;
leftCutPiece.animate('left', leftTrimWidth);
}
if (rightTrimmed) {
// Create cut piece for right trimmed part
var rightCutPiece = getCutPieceFromPool();
game.addChild(rightCutPiece);
var rightTrimWidth = swingingRight - overlapRight;
rightCutPiece.x = overlapRight + rightTrimWidth / 2;
rightCutPiece.y = swingingBlock.y;
rightCutPiece.animate('right', rightTrimWidth);
}
// Calculate how much of the original block remains
var originalWidth = 350; // Original block width
var remainingPercentage = overlapWidth / originalWidth * 100;
// Check if 70% or more was trimmed (only 30% or less remains)
if (remainingPercentage <= 30) {
// Game over - too much of the block was cut off
gameActive = false;
LK.showGameOver();
return;
}
// Trim the block to only show the overlapping area
var newCenterX = overlapLeft + overlapWidth / 2;
swingingBlock.x = newCenterX;
swingingBlock.width = overlapWidth;
stackedBlocks.push(swingingBlock);
} else {
// No overlap, block falls off
swingingBlock.falling = true;
}
// Update stack height to the new top
stackHeight = swingingBlock.y;
// After 2 blocks are placed (3rd block landing), start sliding all blocks down
if (LK.getScore() > 2) {
// Animate all blocks sliding down by one block height (300px)
for (var b = 0; b < stackedBlocks.length; b++) {
var block = stackedBlocks[b];
tween(block, {
y: block.y + 300
}, {
duration: 800,
easing: tween.easeInOut
});
}
// Get the bottom block for destruction after animation
var bottomBlock = stackedBlocks[0];
// Set a timeout to destroy the bottom block after animation completes
LK.setTimeout(function () {
bottomBlock.destroy();
stackedBlocks.splice(0, 1);
}, 800);
}
// Create new swinging block
swingingBlock = getSwingingBlockFromPool();
swingingBlock.x = 0;
swingingBlock.y = rope.height + 100;
swingingBlock.rotationStarted = false; // Reset rotation flag
rope.addChild(swingingBlock);
// Start background music
LK.playMusic('bgmusic');
} else {
// Not close enough to land - continue falling
swingingBlock.falling = true;
}
}
}
// Check if block has fallen below the screen
else if (swingingBlock.y > gameHeight + 200) {
// Game over - player missed the target
gameActive = false;
LK.showGameOver();
}
}
};
; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var CutPiece = Container.expand(function () {
var self = Container.call(this);
var piece = self.attachAsset('cutPiece', {
anchorX: 0.5,
anchorY: 0.5
});
self.animate = function (direction, blockWidth) {
// Set initial appearance
piece.alpha = 1;
piece.width = blockWidth;
// Animate piece falling and rotating
var fallDirection = direction === 'left' ? -1 : 1;
tween(self, {
x: self.x + fallDirection * 400,
y: self.y + 600,
rotation: fallDirection * Math.PI * 2
}, {
duration: 1200,
easing: tween.easeIn
});
tween(piece, {
alpha: 0
}, {
duration: 1200,
easing: tween.linear,
onFinish: function onFinish() {
self.destroyed = true;
returnCutPieceToPool(self);
}
});
};
return self;
});
var Particle = Container.expand(function () {
var self = Container.call(this);
var particle = self.attachAsset('particle', {
anchorX: 0.5,
anchorY: 0.5
});
particle.width = 28;
particle.height = 28;
self.animate = function (velocityX, velocityY, color) {
// Set particle color with slight brightness variation
particle.tint = color || 0xFFFFFF;
// Randomize particle size for more variety
var sizeVariation = 0.5 + Math.random() * 1.0; // Size between 0.5x and 1.5x
particle.width = 28 * sizeVariation;
particle.height = 28 * sizeVariation;
// Set initial velocity with slight randomness
self.velocityX = velocityX + (Math.random() - 0.5) * 2;
self.velocityY = velocityY + (Math.random() - 0.5) * 2;
// Vary gravity for different particle behavior
self.gravity = 0.3 + Math.random() * 0.4; // Gravity between 0.3 and 0.7
// Vary particle lifetime for more dynamic effect
self.life = 45 + Math.random() * 30; // Lifetime between 45-75 frames
self.maxLife = self.life;
// Add initial scale animation for spawn effect
particle.scaleX = 0.1;
particle.scaleY = 0.1;
tween(particle, {
scaleX: sizeVariation,
scaleY: sizeVariation
}, {
duration: 200,
easing: tween.easeOut
});
};
self.update = function () {
if (self.life <= 0) {
self.destroyed = true;
returnParticleToPool(self);
return;
}
// Update position with velocity
self.x += self.velocityX;
self.y += self.velocityY;
// Apply gravity with slight randomness
self.velocityY += self.gravity + (Math.random() - 0.5) * 0.1;
// Apply air resistance to create more realistic movement
self.velocityX *= 0.98;
self.velocityY *= 0.99;
// Add slight rotation for visual interest
particle.rotation += self.velocityX * 0.02;
// Enhanced fade out with scale reduction
var alpha = self.life / self.maxLife;
particle.alpha = alpha;
particle.scaleX = alpha * 0.8 + 0.2; // Scale down as it fades
particle.scaleY = alpha * 0.8 + 0.2;
// Reduce life
self.life--;
};
return self;
});
var StackedBlock = Container.expand(function () {
var self = Container.call(this);
var block = self.attachAsset('block', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x808080
});
/****
* Game Code
****/
// Game state
var stackedBlocks = [];
var particles = [];
var gameHeight = 2732;
var gameWidth = 2048;
var groundLevel = gameHeight - 120;
var stackHeight = 0;
var gameActive = true;
// Object pools for memory optimization
var particlePool = [];
var cutPiecePool = [];
var swingingBlockPool = [];
// Pool management functions
function getParticleFromPool() {
if (particlePool.length > 0) {
var particle = particlePool.pop();
particle.visible = true;
particle.destroyed = false;
return particle;
}
return new Particle();
}
function returnParticleToPool(particle) {
if (particle && !particle.pooled) {
particle.visible = false;
particle.pooled = true;
particlePool.push(particle);
}
}
function getCutPieceFromPool() {
if (cutPiecePool.length > 0) {
var cutPiece = cutPiecePool.pop();
cutPiece.visible = true;
cutPiece.destroyed = false;
return cutPiece;
}
return new CutPiece();
}
function returnCutPieceToPool(cutPiece) {
if (cutPiece && !cutPiece.pooled) {
cutPiece.visible = false;
cutPiece.pooled = true;
cutPiecePool.push(cutPiece);
}
}
function getSwingingBlockFromPool() {
if (swingingBlockPool.length > 0) {
var block = swingingBlockPool.pop();
block.visible = true;
block.destroyed = false;
block.falling = false;
block.rotationStarted = false;
block.velocityY = 0;
block.rotation = 0;
block.scaleX = 1;
block.scaleY = 1;
block.width = 350;
block.pooled = false;
return block;
}
return LK.getAsset('swingingBlock', {
anchorX: 0.5,
anchorY: 0.5
});
}
function returnSwingingBlockToPool(block) {
if (block && !block.pooled) {
block.visible = false;
block.pooled = true;
swingingBlockPool.push(block);
}
}
// Create foundation block
var foundation = new StackedBlock();
foundation.x = gameWidth / 2;
foundation.y = gameHeight - 120; // Move to bottom of screen
game.addChild(foundation);
stackedBlocks.push(foundation);
stackHeight = foundation.y;
// Create rope hanging from top center
var rope = LK.getAsset('rope', {
anchorX: 0.5,
anchorY: 0
});
rope.x = gameWidth / 2;
rope.y = 0; // Rope starts at top of screen
game.addChild(rope);
// Create swinging block at the end of rope
var swingingBlock = getSwingingBlockFromPool();
swingingBlock.x = 0;
swingingBlock.y = rope.height + 100; // Extend the rope length by moving block further down
rope.addChild(swingingBlock);
// Natural pendulum swing variables
var swingTime = 0;
var swingSpeed = 0.015; // Controls swing speed - reduced for slower initial swing
var swingAmplitude = 1.2; // Controls swing range (max rotation) - increased for wider swing
// Score display
var scoreTxt = new Text2('Blocks: 0', {
size: 80,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Touch handler to drop the block
game.down = function (x, y, obj) {
if (!gameActive || !swingingBlock.parent || swingingBlock.falling) return;
// Get the current world position of the swinging block
var worldPos = rope.toGlobal(swingingBlock.position);
var gamePos = game.toLocal(worldPos);
// Detach from rope and add to game
rope.removeChild(swingingBlock);
game.addChild(swingingBlock);
// Set position in game coordinates
swingingBlock.x = gamePos.x;
swingingBlock.y = gamePos.y;
// Add falling velocity
swingingBlock.velocityY = 25; // Start with constant speed
swingingBlock.falling = true;
// Play falling sound
LK.getSound('fall').play();
};
// Update function for falling blocks
game.update = function () {
// Update particles
for (var i = particles.length - 1; i >= 0; i--) {
var particle = particles[i];
if (particle.destroyed) {
particles.splice(i, 1);
}
}
// Update rope swing with natural pendulum motion
if (rope) {
// Gradually increase swing speed based on score - every 5 blocks
var currentSwingSpeed = swingSpeed + Math.floor(LK.getScore() / 5) * 0.005; // Increase speed by 0.005 every 5 blocks
swingTime += currentSwingSpeed;
rope.rotation = Math.sin(swingTime) * swingAmplitude;
// Add vertical swinging motion to the swinging block only if it exists and is not falling
if (swingingBlock && swingingBlock.parent === rope && !swingingBlock.falling) {
var verticalOffset = Math.cos(swingTime * 2) * 30; // Vertical swing with smaller amplitude
swingingBlock.y = rope.height + 100 + verticalOffset;
}
}
if (swingingBlock.falling) {
// Use constant falling speed instead of accelerating gravity
swingingBlock.velocityY = 25; // Fixed falling speed
swingingBlock.y += swingingBlock.velocityY;
// Rotation animation removed - blocks fall straight down
if (!swingingBlock.rotationStarted) {
swingingBlock.rotationStarted = true;
}
// Check collision with any stacked block using built-in intersects method
var hasCollision = false;
for (var i = 0; i < stackedBlocks.length; i++) {
var block = stackedBlocks[i];
if (swingingBlock.intersects(block)) {
hasCollision = true;
break;
}
}
if (hasCollision) {
// Find the topmost block that the swinging block collided with
var targetBlock = null;
var highestY = gameHeight;
for (var k = 0; k < stackedBlocks.length; k++) {
if (swingingBlock.intersects(stackedBlocks[k]) && stackedBlocks[k].y < highestY) {
targetBlock = stackedBlocks[k];
highestY = stackedBlocks[k].y;
}
}
if (targetBlock) {
// Check if block is close enough to the top of the target block to land
var blockBottom = swingingBlock.y + swingingBlock.height / 2;
var targetTop = targetBlock.y - targetBlock.height / 2;
var landingDistance = Math.abs(blockBottom - targetTop);
// Only land if we're within reasonable landing distance (less than falling speed)
if (landingDistance <= 30) {
// Award 1 point
LK.setScore(LK.getScore() + 1);
scoreTxt.setText('Blocks: ' + LK.getScore());
// Calculate collision overlap and trim the block
var targetLeft = targetBlock.x - targetBlock.width / 2;
var targetRight = targetBlock.x + targetBlock.width / 2;
var swingingLeft = swingingBlock.x - swingingBlock.width / 2;
var swingingRight = swingingBlock.x + swingingBlock.width / 2;
// Find the overlapping area
var overlapLeft = Math.max(targetLeft, swingingLeft);
var overlapRight = Math.min(targetRight, swingingRight);
var overlapWidth = overlapRight - overlapLeft;
// Only proceed if there's actual overlap
if (overlapWidth > 0) {
// Place block exactly on top of target block
swingingBlock.falling = false;
swingingBlock.velocityY = 0;
swingingBlock.y = targetBlock.y - 300; // Place exactly one block height above
// Stop rotation animation and reset to straight position
tween.stop(swingingBlock, {
rotation: true
});
swingingBlock.rotation = 0;
// Add collision impact animation
// Scale the landing block and target block for impact effect
var originalScale = swingingBlock.scaleX;
swingingBlock.scaleX = originalScale * 1.2;
swingingBlock.scaleY = originalScale * 1.2;
targetBlock.scaleX = originalScale * 1.1;
targetBlock.scaleY = originalScale * 1.1;
// Animate back to normal size
tween(swingingBlock, {
scaleX: originalScale,
scaleY: originalScale
}, {
duration: 300,
easing: tween.easeOut
});
tween(targetBlock, {
scaleX: originalScale,
scaleY: originalScale
}, {
duration: 300,
easing: tween.easeOut
});
// Create enhanced particle explosion effect
var particleCount = 20; // Increased from 8 to 20
var collisionCenterX = swingingBlock.x;
var collisionCenterY = swingingBlock.y;
for (var p = 0; p < particleCount; p++) {
var particle = getParticleFromPool();
// Larger spawn area for more spread
particle.x = collisionCenterX + (Math.random() - 0.5) * 80;
particle.y = collisionCenterY + (Math.random() - 0.5) * 50;
// Create more varied velocity directions with bursts
var angle = Math.PI * 2 * p / particleCount + (Math.random() - 0.5) * 0.8;
var speed = 12 + Math.random() * 12; // Increased speed range
var velocityX = Math.cos(angle) * speed;
var velocityY = Math.sin(angle) * speed - Math.random() * 8; // More upward bias
// Random particle colors (yellows, oranges, whites)
var colors = [0xFFD700, 0xFFA500, 0xFFFFFF, 0xFFE4B5, 0xFFF8DC];
var color = colors[Math.floor(Math.random() * colors.length)];
particle.animate(velocityX, velocityY, color);
particles.push(particle);
game.addChild(particle);
}
// Add secondary burst of smaller, faster particles
var secondaryParticleCount = 15;
for (var s = 0; s < secondaryParticleCount; s++) {
var secondaryParticle = getParticleFromPool();
// Tighter spawn area for secondary burst
secondaryParticle.x = collisionCenterX + (Math.random() - 0.5) * 30;
secondaryParticle.y = collisionCenterY + (Math.random() - 0.5) * 20;
// Faster, more chaotic movement
var secondaryAngle = Math.random() * Math.PI * 2;
var secondarySpeed = 15 + Math.random() * 15;
var secondaryVelocityX = Math.cos(secondaryAngle) * secondarySpeed;
var secondaryVelocityY = Math.sin(secondaryAngle) * secondarySpeed - Math.random() * 10;
// Brighter colors for secondary particles
var secondaryColors = [0xFFFFFF, 0xFFFF00, 0xFF6600, 0xFF3300];
var secondaryColor = secondaryColors[Math.floor(Math.random() * secondaryColors.length)];
secondaryParticle.animate(secondaryVelocityX, secondaryVelocityY, secondaryColor);
particles.push(secondaryParticle);
game.addChild(secondaryParticle);
}
// Play collision sound
LK.getSound('collision').play();
// Add screen shake effect by moving the game container
var originalGameX = game.x;
var originalGameY = game.y;
var shakeIntensity = 15;
// Quick shake animation
tween(game, {
x: originalGameX + shakeIntensity,
y: originalGameY + shakeIntensity * 0.5
}, {
duration: 50,
easing: tween.linear,
onFinish: function onFinish() {
tween(game, {
x: originalGameX - shakeIntensity * 0.8,
y: originalGameY - shakeIntensity * 0.3
}, {
duration: 50,
easing: tween.linear,
onFinish: function onFinish() {
tween(game, {
x: originalGameX,
y: originalGameY
}, {
duration: 100,
easing: tween.easeOut
});
}
});
}
});
// Create cut pieces for trimmed parts
var leftTrimmed = swingingLeft < overlapLeft;
var rightTrimmed = swingingRight > overlapRight;
if (leftTrimmed) {
// Create cut piece for left trimmed part
var leftCutPiece = getCutPieceFromPool();
game.addChild(leftCutPiece);
var leftTrimWidth = overlapLeft - swingingLeft;
leftCutPiece.x = swingingLeft + leftTrimWidth / 2;
leftCutPiece.y = swingingBlock.y;
leftCutPiece.animate('left', leftTrimWidth);
}
if (rightTrimmed) {
// Create cut piece for right trimmed part
var rightCutPiece = getCutPieceFromPool();
game.addChild(rightCutPiece);
var rightTrimWidth = swingingRight - overlapRight;
rightCutPiece.x = overlapRight + rightTrimWidth / 2;
rightCutPiece.y = swingingBlock.y;
rightCutPiece.animate('right', rightTrimWidth);
}
// Calculate how much of the original block remains
var originalWidth = 350; // Original block width
var remainingPercentage = overlapWidth / originalWidth * 100;
// Check if 70% or more was trimmed (only 30% or less remains)
if (remainingPercentage <= 30) {
// Game over - too much of the block was cut off
gameActive = false;
LK.showGameOver();
return;
}
// Trim the block to only show the overlapping area
var newCenterX = overlapLeft + overlapWidth / 2;
swingingBlock.x = newCenterX;
swingingBlock.width = overlapWidth;
stackedBlocks.push(swingingBlock);
} else {
// No overlap, block falls off
swingingBlock.falling = true;
}
// Update stack height to the new top
stackHeight = swingingBlock.y;
// After 2 blocks are placed (3rd block landing), start sliding all blocks down
if (LK.getScore() > 2) {
// Animate all blocks sliding down by one block height (300px)
for (var b = 0; b < stackedBlocks.length; b++) {
var block = stackedBlocks[b];
tween(block, {
y: block.y + 300
}, {
duration: 800,
easing: tween.easeInOut
});
}
// Get the bottom block for destruction after animation
var bottomBlock = stackedBlocks[0];
// Set a timeout to destroy the bottom block after animation completes
LK.setTimeout(function () {
bottomBlock.destroy();
stackedBlocks.splice(0, 1);
}, 800);
}
// Create new swinging block
swingingBlock = getSwingingBlockFromPool();
swingingBlock.x = 0;
swingingBlock.y = rope.height + 100;
swingingBlock.rotationStarted = false; // Reset rotation flag
rope.addChild(swingingBlock);
// Start background music
LK.playMusic('bgmusic');
} else {
// Not close enough to land - continue falling
swingingBlock.falling = true;
}
}
}
// Check if block has fallen below the screen
else if (swingingBlock.y > gameHeight + 200) {
// Game over - player missed the target
gameActive = false;
LK.showGameOver();
}
}
};
;