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