User prompt
Please fix the bug: 's.apply(...).then is not a function' in or related to this line: 'self.lastY = self.y;' Line Number: 417
User prompt
fix bugs
User prompt
Please fix the bug: 's.apply(...).then is not a function' in or related to this line: 'self.lastY = self.y;' Line Number: 417
User prompt
Please fix the bug: 's.apply(...).then is not a function' in or related to this line: 'self.lastY = self.y;' Line Number: 436
User prompt
Please fix the bug: 's.apply(...).then is not a function' in or related to this line: 'self.lastY = self.y;' Line Number: 429
User prompt
Please fix the bug: 's.apply(...).then is not a function' in or related to this line: 'self.lastY = self.y;' Line Number: 417
User prompt
make the sun bird throwlable if want
User prompt
Please fix the bug: 's.apply(...).then is not a function' in or related to this line: 'self.lastY = self.y;' Line Number: 417
User prompt
Please fix the bug: 's.apply(...).then is not a function' in or related to this line: 'self.lastY = self.y;' Line Number: 417
User prompt
Please fix the bug: 's.apply(...).then is not a function' in or related to this line: 'self.lastY = self.y;' Line Number: 417
User prompt
Please fix the bug: 's.apply(...).then is not a function' in or related to this line: 'self.lastY = self.y;' Line Number: 417
User prompt
detailed birds
User prompt
add trees
User prompt
add bottom woods under pig
User prompt
add support woods for the box because roof is falling
User prompt
make woods shaped big box
User prompt
extra two birds
User prompt
sun
User prompt
trees and blue air
User prompt
add back ground
User prompt
add something you want it game
User prompt
deletele second pig
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Bird class var Bird = Container.expand(function () { var self = Container.call(this); // Attach a red ellipse as the bird var birdAsset = self.attachAsset('bird', { anchorX: 0.5, anchorY: 0.5 }); // Attach a yellow triangle as the bird's nose (beak) var beak = self.attachAsset('beakTriangle', { anchorX: 0.1, // anchor at left base of triangle anchorY: 0.5 }); // Make the beak look like a triangle by scaling and rotating beak.width = birdAsset.width * 0.48; beak.height = birdAsset.height * 0.36; beak.x = birdAsset.width * 0.56; beak.y = 0; beak.rotation = Math.PI / 8; // slight angle for style beak.mask = undefined; // no mask, but visually it's a triangle by size/position // Physics properties self.vx = 0; self.vy = 0; self.launched = false; self.radius = birdAsset.width / 2; self.gravity = 0.35; self.friction = 0.998; self.bounce = 0.5; self.stopped = false; // Called every tick self.update = function () { // Track lastX and lastY for physics events if (typeof self.lastX === "undefined") self.lastX = self.x; if (typeof self.lastY === "undefined") self.lastY = self.y; if (self.launched && !self.stopped) { // Standard projectile physics, no tracking or homing self.vy += self.gravity; self.x += self.vx; self.y += self.vy; // Friction self.vx *= self.friction; self.vy *= self.friction; // Floor collision if (self.y + self.radius > groundY) { self.y = groundY - self.radius; if (Math.abs(self.vy) > 2) { self.vy = -self.vy * self.bounce; self.vx *= 0.8; self.stopped = false; // Don't stop if still bouncing } else { self.vy = 0; self.vx *= 0.8; // Only stop if both vx and vy are very small and bird is on ground if (Math.abs(self.vx) < 1 && Math.abs(self.vy) < 0.5) { self.stopped = true; } else { self.stopped = false; } } } // Wall collision if (self.x - self.radius < 0) { self.x = self.radius; self.vx = -self.vx * self.bounce; } if (self.x + self.radius > 2048) { self.x = 2048 - self.radius; self.vx = -self.vx * self.bounce; } } // Update lastX and lastY after movement self.lastX = self.x; self.lastY = self.y; }; // Simple circle collision self.intersectsCircle = function (other) { var dx = self.x - other.x; var dy = self.y - other.y; var dist = Math.sqrt(dx * dx + dy * dy); return dist < self.radius + other.radius; }; return self; }); // Block class (rectangular, can be horizontal or vertical) var Block = Container.expand(function () { var self = Container.call(this); // Attach a brown box as the block var blockAsset = self.attachAsset('block', { anchorX: 0.5, anchorY: 0.5 }); self.width = blockAsset.width; self.height = blockAsset.height; self.vx = 0; self.vy = 0; self.gravity = 0.7; self.friction = 0.98; self.bounce = 0.3; self.stopped = false; // Add health property for wood blocks self.health = 100; // Called every tick self.update = function () { // Track lastX and lastY for physics events if (typeof self.lastX === "undefined") self.lastX = self.x; if (typeof self.lastY === "undefined") self.lastY = self.y; if (!self.stopped) { // Apply gravity to wood blocks self.vy += self.gravity; self.x += self.vx; self.y += self.vy; self.vx *= self.friction; self.vy *= self.friction; // --- Ragdoll effect: angular physics --- if (typeof self.angularVelocity === "undefined") self.angularVelocity = 0; if (typeof self.angle === "undefined") self.angle = self.rotation || 0; // Apply angular velocity to rotation self.angle += self.angularVelocity; self.rotation = self.angle; // Dampen angular velocity (simulate friction) self.angularVelocity *= 0.97; // Block-on-block collision (stand or crash) for (var i = 0; i < blocks.length; i++) { var other = blocks[i]; if (other === self) continue; // Axis-aligned bounding box collision var selfLeft = self.x - self.width / 2; var selfRight = self.x + self.width / 2; var selfBottom = self.y + self.height / 2; var selfTop = self.y - self.height / 2; var otherLeft = other.x - other.width / 2; var otherRight = other.x + other.width / 2; var otherTop = other.y - other.height / 2; var otherBottom = other.y + other.height / 2; // Check if horizontally overlapping and self is falling onto other if (selfRight > otherLeft && selfLeft < otherRight && selfBottom > otherTop && selfTop < otherTop && self.vy > 0 && self.y < other.y) { // Land on top of the other block self.y = otherTop - self.height / 2; // If falling fast, bounce/crash if (Math.abs(self.vy) > 2) { self.vy = -self.vy * self.bounce; self.vx *= 0.8; // Transfer some force to the block below (crash effect) other.vx += self.vx * 0.2; other.vy += self.vy * 0.2; // --- Ragdoll: add angular velocity based on impact --- var impact = self.vx * 0.1 + self.vy * 0.1; if (typeof self.angularVelocity === "undefined") self.angularVelocity = 0; self.angularVelocity += impact * (Math.random() - 0.5) * 0.2; if (typeof other.angularVelocity === "undefined") other.angularVelocity = 0; other.angularVelocity += impact * (Math.random() - 0.5) * 0.2; // Reduce health for both blocks based on impact var damage = Math.min(30, Math.max(5, Math.floor(Math.abs(self.vy) * 2))); self.health -= damage; other.health -= Math.floor(damage / 2); // Destroy self if health <= 0 if (self.health <= 0) { self.destroy(); return; } // Destroy other if health <= 0 if (other.health <= 0) { other.destroy(); } // Bird-block collision damage is handled in game.update } else { self.vy = 0; self.vx *= 0.8; if (Math.abs(self.vx) < 1) { self.stopped = true; } } } // Also allow side-to-side collision (prevent overlap horizontally) if (selfBottom > otherTop && selfTop < otherBottom) { // Check if self is moving right into other's left side if (selfRight > otherLeft && selfLeft < otherLeft && self.vx > 0) { self.x = otherLeft - self.width / 2; self.vx = -self.vx * self.bounce; // Transfer some force to the other block (right hit) other.vx += self.vx * 0.2; // --- Ragdoll: add angular velocity based on side impact --- var impact = self.vx * 0.1; if (typeof self.angularVelocity === "undefined") self.angularVelocity = 0; self.angularVelocity += impact * 0.1; if (typeof other.angularVelocity === "undefined") other.angularVelocity = 0; other.angularVelocity -= impact * 0.1; } // Check if self is moving left into other's right side if (selfLeft < otherRight && selfRight > otherRight && self.vx < 0) { self.x = otherRight + self.width / 2; self.vx = -self.vx * self.bounce; // Transfer some force to the other block (left hit) other.vx += self.vx * 0.2; // --- Ragdoll: add angular velocity based on side impact --- var impact = self.vx * 0.1; if (typeof self.angularVelocity === "undefined") self.angularVelocity = 0; self.angularVelocity -= impact * 0.1; if (typeof other.angularVelocity === "undefined") other.angularVelocity = 0; other.angularVelocity += impact * 0.1; } } } // Floor collision if (self.y + self.height / 2 > groundY) { self.y = groundY - self.height / 2; if (Math.abs(self.vy) > 2) { self.vy = -self.vy * self.bounce; self.vx *= 0.8; } else { self.vy = 0; self.vx *= 0.8; if (Math.abs(self.vx) < 1) { self.stopped = true; } } } // Wall collision if (self.x - self.width / 2 < 0) { self.x = self.width / 2; self.vx = -self.vx * self.bounce; } if (self.x + self.width / 2 > 2048) { self.x = 2048 - self.width / 2; self.vx = -self.vx * self.bounce; } } // Update lastX and lastY after movement self.lastX = self.x; self.lastY = self.y; }; // Simple rectangle collision with circle (bird) self.intersectsCircle = function (circle) { var cx = circle.x; var cy = circle.y; var rx = self.x; var ry = self.y; var hw = self.width / 2; var hh = self.height / 2; // Closest point on rectangle to circle center var closestX = Math.max(rx - hw, Math.min(cx, rx + hw)); var closestY = Math.max(ry - hh, Math.min(cy, ry + hh)); var dx = cx - closestX; var dy = cy - closestY; return dx * dx + dy * dy < circle.radius * circle.radius; }; return self; }); // Pig class var Pig = Container.expand(function () { var self = Container.call(this); // Attach a green ellipse as the pig var pigAsset = self.attachAsset('pig', { anchorX: 0.5, anchorY: 0.5 }); self.radius = pigAsset.width / 2; self.alive = true; // Physics properties for pigs self.vx = 0; self.vy = 0; self.gravity = 0.7; self.friction = 0.98; self.bounce = 0.3; self.stopped = false; // Called every tick self.update = function () { if (!self.alive) return; // Track lastX and lastY for physics events if (typeof self.lastX === "undefined") self.lastX = self.x; if (typeof self.lastY === "undefined") self.lastY = self.y; if (!self.stopped) { self.vy += self.gravity; self.x += self.vx; self.y += self.vy; self.vx *= self.friction; self.vy *= self.friction; // Pig-on-block collision: prevent pig from going inside the wood blocks for (var i = 0; i < blocks.length; i++) { var block = blocks[i]; // Axis-aligned bounding box for block var blockLeft = block.x - block.width / 2; var blockRight = block.x + block.width / 2; var blockTop = block.y - block.height / 2; var blockBottom = block.y + block.height / 2; // Only check if pig is above block and falling if (self.y < block.y && self.vy > 0) { // Check horizontal overlap if (self.x + self.radius > blockLeft && self.x - self.radius < blockRight) { // Check if pig's bottom is entering block's top if (self.y + self.radius > blockTop && self.y - self.radius < blockBottom) { // Place pig on top of block self.y = blockTop - self.radius; if (Math.abs(self.vy) > 2) { self.vy = -self.vy * self.bounce; self.vx *= 0.8; // Transfer some force to the block below (crash effect) block.vx += self.vx * 0.2; block.vy += self.vy * 0.2; } else { self.vy = 0; self.vx *= 0.8; if (Math.abs(self.vx) < 1) { self.stopped = true; } } } } } } // Floor collision if (self.y + self.radius > groundY) { self.y = groundY - self.radius; if (Math.abs(self.vy) > 2) { self.vy = -self.vy * self.bounce; self.vx *= 0.8; } else { self.vy = 0; self.vx *= 0.8; if (Math.abs(self.vx) < 1) { self.stopped = true; } } } // Wall collision if (self.x - self.radius < 0) { self.x = self.radius; self.vx = -self.vx * self.bounce; } if (self.x + self.radius > 2048) { self.x = 2048 - self.radius; self.vx = -self.vx * self.bounce; } } // Update lastX and lastY after movement self.lastX = self.x; self.lastY = self.y; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x87ceeb // Sky blue }); /**** * Game Code ****/ // --- Asset Initialization (shapes) --- // --- Game Variables --- var birds = []; var pigs = []; var blocks = []; var currentBird = null; var birdIndex = 0; var isDragging = false; var dragStart = { x: 0, y: 0 }; var dragEnd = { x: 0, y: 0 }; var slingshotX = 180; var slingshotY = 2000; // Raised slingshot higher (was 2450) var slingshotBandLength = 180; var maxBirds = 3; var groundY = 2650; var level = 1; var launching = false; var score = 0; // --- GUI --- var scoreTxt = new Text2('0', { size: 120, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); var birdsLeftTxt = new Text2('', { size: 80, fill: 0xFFFFFF }); birdsLeftTxt.anchor.set(0.5, 0); LK.gui.topRight.addChild(birdsLeftTxt); // --- Draw Ground --- var ground = LK.getAsset('ground', { anchorX: 0, anchorY: 0 }); ground.x = 0; ground.y = groundY; game.addChild(ground); // --- Draw Little Mountain below the slingshot --- var mountainWidth = 400; var mountainHeight = 120; var mountain = LK.getAsset('block', { anchorX: 0.5, anchorY: 1 }); mountain.width = mountainWidth; mountain.height = mountainHeight; mountain.x = slingshotX; mountain.y = groundY + 10; // Slightly below ground for overlap mountain.tint = 0x8b7765; // Optional: make it look more like a mountain game.addChild(mountain); // --- Draw Slingshot --- var slingshot = LK.getAsset('slingshot', { anchorX: 0.5, anchorY: 1 }); slingshot.x = slingshotX; slingshot.y = slingshotY + 90; game.addChild(slingshot); // --- Level Setup --- function setupLevel() { // Clear previous for (var i = 0; i < birds.length; i++) birds[i].destroy(); for (var i = 0; i < pigs.length; i++) pigs[i].destroy(); for (var i = 0; i < blocks.length; i++) blocks[i].destroy(); birds = []; pigs = []; blocks = []; birdIndex = 0; score = 0; LK.setScore(0); scoreTxt.setText('0'); launching = false; // Place birds for (var i = 0; i < maxBirds; i++) { var bird = new Bird(); bird.x = slingshotX - 120 - i * 80; bird.y = slingshotY + 40; birds.push(bird); game.addChild(bird); } currentBird = birds[birdIndex]; // Place blocks to form a house shape var houseCenterX = 1500; var houseBottomY = groundY - 100; var wallW = 180; var wallH = 40; var wallHeight = 4; // number of vertical wall blocks var roofW = 180; var roofH = 40; // --- House base: two vertical walls (left/right), 4 blocks high --- for (var i = 0; i < wallHeight; i++) { // Left wall var leftWall = new Block(); leftWall.x = houseCenterX - wallW / 2 + wallH / 2; leftWall.y = houseBottomY - i * wallW; leftWall.rotation = Math.PI / 2; blocks.push(leftWall); game.addChild(leftWall); // Right wall var rightWall = new Block(); rightWall.x = houseCenterX + wallW / 2 - wallH / 2; rightWall.y = houseBottomY - i * wallW; rightWall.rotation = Math.PI / 2; blocks.push(rightWall); game.addChild(rightWall); } // --- House base: bottom block (floor) --- var houseFloor = new Block(); houseFloor.x = houseCenterX; houseFloor.y = houseBottomY + wallH / 2; blocks.push(houseFloor); game.addChild(houseFloor); // --- House base: top block (ceiling) --- var houseCeiling = new Block(); houseCeiling.x = houseCenterX; houseCeiling.y = houseBottomY - (wallHeight - 1) * wallW - wallH / 2; blocks.push(houseCeiling); game.addChild(houseCeiling); // --- House roof: two diagonal blocks (left/right) --- var roofLeft = new Block(); roofLeft.x = houseCenterX - wallW / 2 + wallH / 2 + 30; roofLeft.y = houseCeiling.y - wallW / 2 + 10; roofLeft.rotation = Math.PI / 4; // 45 degrees blocks.push(roofLeft); game.addChild(roofLeft); var roofRight = new Block(); roofRight.x = houseCenterX + wallW / 2 - wallH / 2 - 30; roofRight.y = houseCeiling.y - wallW / 2 + 10; roofRight.rotation = -Math.PI / 4; // -45 degrees blocks.push(roofRight); game.addChild(roofRight); // --- House roof: top horizontal block (roof ridge) --- var roofRidge = new Block(); roofRidge.x = houseCenterX; roofRidge.y = houseCeiling.y - wallW / 2 - roofH / 2 + 10; blocks.push(roofRidge); game.addChild(roofRidge); // Place pig on the house ceiling var pig1 = new Pig(); pig1.x = houseCeiling.x; pig1.y = houseCeiling.y - houseCeiling.height / 2 - pig1.radius - 50; pigs.push(pig1); game.addChild(pig1); // (Second pig inside the house removed) // --- Ensure blocks and pigs are not hanging in the air --- for (var i = 0; i < blocks.length; i++) { var block = blocks[i]; // Check if block is supported by ground or another block var supported = false; // Supported by ground if (Math.abs(block.y + block.height / 2 - groundY) < 2) { supported = true; } // Supported by another block below for (var j = 0; j < blocks.length; j++) { if (i === j) continue; var other = blocks[j]; var blockBottom = block.y + block.height / 2; var otherTop = other.y - other.height / 2; var horizontalOverlap = block.x + block.width / 2 > other.x - other.width / 2 && block.x - block.width / 2 < other.x + other.width / 2; if (horizontalOverlap && Math.abs(blockBottom - otherTop) < 2) { supported = true; break; } } block.stopped = !supported ? false : block.stopped; } for (var i = 0; i < pigs.length; i++) { var pig = pigs[i]; // Check if pig is supported by ground or a block var supported = false; // Supported by ground if (Math.abs(pig.y + pig.radius - groundY) < 2) { supported = true; } // Supported by a block below for (var j = 0; j < blocks.length; j++) { var block = blocks[j]; var pigBottom = pig.y + pig.radius; var blockTop = block.y - block.height / 2; var horizontalOverlap = pig.x + pig.radius > block.x - block.width / 2 && pig.x - pig.radius < block.x + block.width / 2; if (horizontalOverlap && Math.abs(pigBottom - blockTop) < 2) { supported = true; break; } } pig.stopped = !supported ? false : pig.stopped; } updateBirdsLeft(); } // --- Update birds left text --- function updateBirdsLeft() { birdsLeftTxt.setText('Birds: ' + (maxBirds - birdIndex)); } // --- Drag and Launch Logic --- function getDistance(x1, y1, x2, y2) { var dx = x2 - x1; var dy = y2 - y1; return Math.sqrt(dx * dx + dy * dy); } function getAngle(x1, y1, x2, y2) { return Math.atan2(y2 - y1, x2 - x1); } // Only allow drag if not launched game.down = function (x, y, obj) { if (launching) return; if (!currentBird || currentBird.launched) return; // Only allow drag if touch is near the bird var dist = getDistance(x, y, currentBird.x, currentBird.y); if (dist < currentBird.radius * 1.5) { isDragging = true; dragStart.x = x; dragStart.y = y; } }; game.move = function (x, y, obj) { if (isDragging && currentBird && !currentBird.launched) { // Limit drag distance var dx = x - slingshotX; var dy = y - slingshotY; var dist = getDistance(x, y, slingshotX, slingshotY); if (dist > slingshotBandLength) { var angle = getAngle(slingshotX, slingshotY, x, y); dx = Math.cos(angle) * slingshotBandLength; dy = Math.sin(angle) * slingshotBandLength; } currentBird.x = slingshotX + dx; currentBird.y = slingshotY + dy; dragEnd.x = currentBird.x; dragEnd.y = currentBird.y; } }; game.up = function (x, y, obj) { if (isDragging && currentBird && !currentBird.launched) { isDragging = false; // Calculate launch velocity var dx = slingshotX - currentBird.x; var dy = slingshotY - currentBird.y; currentBird.vx = dx * 0.18; currentBird.vy = dy * 0.18; currentBird.launched = true; launching = true; // Animate slingshot back tween(currentBird, { x: currentBird.x, y: currentBird.y }, { duration: 80 }); } }; // --- Main Game Loop --- game.update = function () { // Update all birds for (var i = 0; i < birds.length; i++) { birds[i].update(); } // Update all pigs for (var i = 0; i < pigs.length; i++) { pigs[i].update(); } // Update all blocks for (var i = 0; i < blocks.length; i++) { blocks[i].update(); } // --- Check support for each block: if not supported, set stopped = false so it falls --- for (var i = 0; i < blocks.length; i++) { var block = blocks[i]; // Check if block is supported by ground or another block var supported = false; // Supported by ground if (Math.abs(block.y + block.height / 2 - groundY) < 2) { supported = true; } // Supported by another block below for (var j = 0; j < blocks.length; j++) { if (i === j) continue; var other = blocks[j]; var blockBottom = block.y + block.height / 2; var otherTop = other.y - other.height / 2; var horizontalOverlap = block.x + block.width / 2 > other.x - other.width / 2 && block.x - block.width / 2 < other.x + other.width / 2; if (horizontalOverlap && Math.abs(blockBottom - otherTop) < 2) { supported = true; break; } } // If not supported, set stopped = false so it will fall if (!supported) { block.stopped = false; } } // Bird-block collision for (var i = 0; i < birds.length; i++) { var bird = birds[i]; if (!bird.launched) continue; if (bird.stopped) continue; // Collide with blocks for (var j = 0; j < blocks.length; j++) { var block = blocks[j]; // Remove block.stopped check so birds can always touch wood if (block.intersectsCircle(bird)) { // Calculate collision angle and force var angle = getAngle(bird.x, bird.y, block.x, block.y); var force = Math.sqrt(bird.vx * bird.vx + bird.vy * bird.vy) * 0.5; // Apply force to block block.vx += Math.cos(angle) * force * 0.7; block.vy += Math.sin(angle) * force * 0.7; // --- Ragdoll: add angular velocity to block based on bird impact --- var impact = force * 0.2; if (typeof block.angularVelocity === "undefined") block.angularVelocity = 0; block.angularVelocity += impact * (Math.random() - 0.5); // Apply bounce to bird bird.vx = -bird.vx * 0.4; bird.vy = -bird.vy * 0.4; // Move bird out of block to prevent sticking var overlap = bird.radius + Math.max(block.width, block.height) / 2 - getDistance(bird.x, bird.y, block.x, block.y); if (overlap > 0) { bird.x += Math.cos(angle) * overlap * 0.5; bird.y += Math.sin(angle) * overlap * 0.5; } // Optionally, mark block as not stopped to allow it to move if hit hard block.stopped = false; // Birds deal damage to woods on collision var birdDamage = Math.min(40, Math.max(5, Math.floor(force * 2))); block.health -= birdDamage; if (block.health <= 0) { block.destroy(); blocks.splice(j, 1); j--; // Adjust index since we removed a block continue; } } } } // Bird-pig collision for (var i = 0; i < birds.length; i++) { var bird = birds[i]; if (!bird.launched) continue; if (bird.stopped) continue; for (var j = 0; j < pigs.length; j++) { var pig = pigs[j]; if (!pig.alive) continue; if (bird.intersectsCircle(pig)) { pig.alive = false; pig.visible = false; LK.setScore(LK.getScore() + 1000); scoreTxt.setText(LK.getScore()); } } } // Block-pig collision (falling blocks can kill pigs) for (var i = 0; i < blocks.length; i++) { var block = blocks[i]; if (block.stopped) continue; for (var j = 0; j < pigs.length; j++) { var pig = pigs[j]; if (!pig.alive) continue; // Treat pig as circle, block as rectangle if (block.intersectsCircle(pig)) { // Only kill pig if block is moving fast enough (e.g. falling hard) var blockSpeed = Math.sqrt(block.vx * block.vx + block.vy * block.vy); if (blockSpeed > 8) { pig.alive = false; pig.visible = false; LK.setScore(LK.getScore() + 1000); scoreTxt.setText(LK.getScore()); } } } } // --- Check support for each pig: if not supported, set stopped = false so it falls --- for (var i = 0; i < pigs.length; i++) { var pig = pigs[i]; if (!pig.alive) continue; var supported = false; // Supported by ground if (Math.abs(pig.y + pig.radius - groundY) < 2) { supported = true; } // Supported by a block below for (var j = 0; j < blocks.length; j++) { var block = blocks[j]; var pigBottom = pig.y + pig.radius; var blockTop = block.y - block.height / 2; var horizontalOverlap = pig.x + pig.radius > block.x - block.width / 2 && pig.x - pig.radius < block.x + block.width / 2; if (horizontalOverlap && Math.abs(pigBottom - blockTop) < 2) { supported = true; break; } } if (!supported) { pig.stopped = false; } } // Remove dead pigs var allPigsDead = true; for (var i = 0; i < pigs.length; i++) { if (pigs[i].alive) { allPigsDead = false; break; } } // If all pigs dead, win (only if there was at least one pig) if (pigs.length > 0 && allPigsDead) { LK.showYouWin(); return; } // If current bird stopped, move to next bird if (currentBird && currentBird.launched && currentBird.stopped && !isDragging && launching) { birdIndex++; updateBirdsLeft(); if (birdIndex < birds.length) { currentBird = birds[birdIndex]; // Move next bird to slingshot tween(currentBird, { x: slingshotX, y: slingshotY }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { launching = false; } }); } else { // Out of birds, game over LK.showGameOver(); return; } launching = false; } }; // --- Start Game --- setupLevel();
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Bird class
var Bird = Container.expand(function () {
var self = Container.call(this);
// Attach a red ellipse as the bird
var birdAsset = self.attachAsset('bird', {
anchorX: 0.5,
anchorY: 0.5
});
// Attach a yellow triangle as the bird's nose (beak)
var beak = self.attachAsset('beakTriangle', {
anchorX: 0.1,
// anchor at left base of triangle
anchorY: 0.5
});
// Make the beak look like a triangle by scaling and rotating
beak.width = birdAsset.width * 0.48;
beak.height = birdAsset.height * 0.36;
beak.x = birdAsset.width * 0.56;
beak.y = 0;
beak.rotation = Math.PI / 8; // slight angle for style
beak.mask = undefined; // no mask, but visually it's a triangle by size/position
// Physics properties
self.vx = 0;
self.vy = 0;
self.launched = false;
self.radius = birdAsset.width / 2;
self.gravity = 0.35;
self.friction = 0.998;
self.bounce = 0.5;
self.stopped = false;
// Called every tick
self.update = function () {
// Track lastX and lastY for physics events
if (typeof self.lastX === "undefined") self.lastX = self.x;
if (typeof self.lastY === "undefined") self.lastY = self.y;
if (self.launched && !self.stopped) {
// Standard projectile physics, no tracking or homing
self.vy += self.gravity;
self.x += self.vx;
self.y += self.vy;
// Friction
self.vx *= self.friction;
self.vy *= self.friction;
// Floor collision
if (self.y + self.radius > groundY) {
self.y = groundY - self.radius;
if (Math.abs(self.vy) > 2) {
self.vy = -self.vy * self.bounce;
self.vx *= 0.8;
self.stopped = false; // Don't stop if still bouncing
} else {
self.vy = 0;
self.vx *= 0.8;
// Only stop if both vx and vy are very small and bird is on ground
if (Math.abs(self.vx) < 1 && Math.abs(self.vy) < 0.5) {
self.stopped = true;
} else {
self.stopped = false;
}
}
}
// Wall collision
if (self.x - self.radius < 0) {
self.x = self.radius;
self.vx = -self.vx * self.bounce;
}
if (self.x + self.radius > 2048) {
self.x = 2048 - self.radius;
self.vx = -self.vx * self.bounce;
}
}
// Update lastX and lastY after movement
self.lastX = self.x;
self.lastY = self.y;
};
// Simple circle collision
self.intersectsCircle = function (other) {
var dx = self.x - other.x;
var dy = self.y - other.y;
var dist = Math.sqrt(dx * dx + dy * dy);
return dist < self.radius + other.radius;
};
return self;
});
// Block class (rectangular, can be horizontal or vertical)
var Block = Container.expand(function () {
var self = Container.call(this);
// Attach a brown box as the block
var blockAsset = self.attachAsset('block', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = blockAsset.width;
self.height = blockAsset.height;
self.vx = 0;
self.vy = 0;
self.gravity = 0.7;
self.friction = 0.98;
self.bounce = 0.3;
self.stopped = false;
// Add health property for wood blocks
self.health = 100;
// Called every tick
self.update = function () {
// Track lastX and lastY for physics events
if (typeof self.lastX === "undefined") self.lastX = self.x;
if (typeof self.lastY === "undefined") self.lastY = self.y;
if (!self.stopped) {
// Apply gravity to wood blocks
self.vy += self.gravity;
self.x += self.vx;
self.y += self.vy;
self.vx *= self.friction;
self.vy *= self.friction;
// --- Ragdoll effect: angular physics ---
if (typeof self.angularVelocity === "undefined") self.angularVelocity = 0;
if (typeof self.angle === "undefined") self.angle = self.rotation || 0;
// Apply angular velocity to rotation
self.angle += self.angularVelocity;
self.rotation = self.angle;
// Dampen angular velocity (simulate friction)
self.angularVelocity *= 0.97;
// Block-on-block collision (stand or crash)
for (var i = 0; i < blocks.length; i++) {
var other = blocks[i];
if (other === self) continue;
// Axis-aligned bounding box collision
var selfLeft = self.x - self.width / 2;
var selfRight = self.x + self.width / 2;
var selfBottom = self.y + self.height / 2;
var selfTop = self.y - self.height / 2;
var otherLeft = other.x - other.width / 2;
var otherRight = other.x + other.width / 2;
var otherTop = other.y - other.height / 2;
var otherBottom = other.y + other.height / 2;
// Check if horizontally overlapping and self is falling onto other
if (selfRight > otherLeft && selfLeft < otherRight && selfBottom > otherTop && selfTop < otherTop && self.vy > 0 && self.y < other.y) {
// Land on top of the other block
self.y = otherTop - self.height / 2;
// If falling fast, bounce/crash
if (Math.abs(self.vy) > 2) {
self.vy = -self.vy * self.bounce;
self.vx *= 0.8;
// Transfer some force to the block below (crash effect)
other.vx += self.vx * 0.2;
other.vy += self.vy * 0.2;
// --- Ragdoll: add angular velocity based on impact ---
var impact = self.vx * 0.1 + self.vy * 0.1;
if (typeof self.angularVelocity === "undefined") self.angularVelocity = 0;
self.angularVelocity += impact * (Math.random() - 0.5) * 0.2;
if (typeof other.angularVelocity === "undefined") other.angularVelocity = 0;
other.angularVelocity += impact * (Math.random() - 0.5) * 0.2;
// Reduce health for both blocks based on impact
var damage = Math.min(30, Math.max(5, Math.floor(Math.abs(self.vy) * 2)));
self.health -= damage;
other.health -= Math.floor(damage / 2);
// Destroy self if health <= 0
if (self.health <= 0) {
self.destroy();
return;
}
// Destroy other if health <= 0
if (other.health <= 0) {
other.destroy();
}
// Bird-block collision damage is handled in game.update
} else {
self.vy = 0;
self.vx *= 0.8;
if (Math.abs(self.vx) < 1) {
self.stopped = true;
}
}
}
// Also allow side-to-side collision (prevent overlap horizontally)
if (selfBottom > otherTop && selfTop < otherBottom) {
// Check if self is moving right into other's left side
if (selfRight > otherLeft && selfLeft < otherLeft && self.vx > 0) {
self.x = otherLeft - self.width / 2;
self.vx = -self.vx * self.bounce;
// Transfer some force to the other block (right hit)
other.vx += self.vx * 0.2;
// --- Ragdoll: add angular velocity based on side impact ---
var impact = self.vx * 0.1;
if (typeof self.angularVelocity === "undefined") self.angularVelocity = 0;
self.angularVelocity += impact * 0.1;
if (typeof other.angularVelocity === "undefined") other.angularVelocity = 0;
other.angularVelocity -= impact * 0.1;
}
// Check if self is moving left into other's right side
if (selfLeft < otherRight && selfRight > otherRight && self.vx < 0) {
self.x = otherRight + self.width / 2;
self.vx = -self.vx * self.bounce;
// Transfer some force to the other block (left hit)
other.vx += self.vx * 0.2;
// --- Ragdoll: add angular velocity based on side impact ---
var impact = self.vx * 0.1;
if (typeof self.angularVelocity === "undefined") self.angularVelocity = 0;
self.angularVelocity -= impact * 0.1;
if (typeof other.angularVelocity === "undefined") other.angularVelocity = 0;
other.angularVelocity += impact * 0.1;
}
}
}
// Floor collision
if (self.y + self.height / 2 > groundY) {
self.y = groundY - self.height / 2;
if (Math.abs(self.vy) > 2) {
self.vy = -self.vy * self.bounce;
self.vx *= 0.8;
} else {
self.vy = 0;
self.vx *= 0.8;
if (Math.abs(self.vx) < 1) {
self.stopped = true;
}
}
}
// Wall collision
if (self.x - self.width / 2 < 0) {
self.x = self.width / 2;
self.vx = -self.vx * self.bounce;
}
if (self.x + self.width / 2 > 2048) {
self.x = 2048 - self.width / 2;
self.vx = -self.vx * self.bounce;
}
}
// Update lastX and lastY after movement
self.lastX = self.x;
self.lastY = self.y;
};
// Simple rectangle collision with circle (bird)
self.intersectsCircle = function (circle) {
var cx = circle.x;
var cy = circle.y;
var rx = self.x;
var ry = self.y;
var hw = self.width / 2;
var hh = self.height / 2;
// Closest point on rectangle to circle center
var closestX = Math.max(rx - hw, Math.min(cx, rx + hw));
var closestY = Math.max(ry - hh, Math.min(cy, ry + hh));
var dx = cx - closestX;
var dy = cy - closestY;
return dx * dx + dy * dy < circle.radius * circle.radius;
};
return self;
});
// Pig class
var Pig = Container.expand(function () {
var self = Container.call(this);
// Attach a green ellipse as the pig
var pigAsset = self.attachAsset('pig', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = pigAsset.width / 2;
self.alive = true;
// Physics properties for pigs
self.vx = 0;
self.vy = 0;
self.gravity = 0.7;
self.friction = 0.98;
self.bounce = 0.3;
self.stopped = false;
// Called every tick
self.update = function () {
if (!self.alive) return;
// Track lastX and lastY for physics events
if (typeof self.lastX === "undefined") self.lastX = self.x;
if (typeof self.lastY === "undefined") self.lastY = self.y;
if (!self.stopped) {
self.vy += self.gravity;
self.x += self.vx;
self.y += self.vy;
self.vx *= self.friction;
self.vy *= self.friction;
// Pig-on-block collision: prevent pig from going inside the wood blocks
for (var i = 0; i < blocks.length; i++) {
var block = blocks[i];
// Axis-aligned bounding box for block
var blockLeft = block.x - block.width / 2;
var blockRight = block.x + block.width / 2;
var blockTop = block.y - block.height / 2;
var blockBottom = block.y + block.height / 2;
// Only check if pig is above block and falling
if (self.y < block.y && self.vy > 0) {
// Check horizontal overlap
if (self.x + self.radius > blockLeft && self.x - self.radius < blockRight) {
// Check if pig's bottom is entering block's top
if (self.y + self.radius > blockTop && self.y - self.radius < blockBottom) {
// Place pig on top of block
self.y = blockTop - self.radius;
if (Math.abs(self.vy) > 2) {
self.vy = -self.vy * self.bounce;
self.vx *= 0.8;
// Transfer some force to the block below (crash effect)
block.vx += self.vx * 0.2;
block.vy += self.vy * 0.2;
} else {
self.vy = 0;
self.vx *= 0.8;
if (Math.abs(self.vx) < 1) {
self.stopped = true;
}
}
}
}
}
}
// Floor collision
if (self.y + self.radius > groundY) {
self.y = groundY - self.radius;
if (Math.abs(self.vy) > 2) {
self.vy = -self.vy * self.bounce;
self.vx *= 0.8;
} else {
self.vy = 0;
self.vx *= 0.8;
if (Math.abs(self.vx) < 1) {
self.stopped = true;
}
}
}
// Wall collision
if (self.x - self.radius < 0) {
self.x = self.radius;
self.vx = -self.vx * self.bounce;
}
if (self.x + self.radius > 2048) {
self.x = 2048 - self.radius;
self.vx = -self.vx * self.bounce;
}
}
// Update lastX and lastY after movement
self.lastX = self.x;
self.lastY = self.y;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87ceeb // Sky blue
});
/****
* Game Code
****/
// --- Asset Initialization (shapes) ---
// --- Game Variables ---
var birds = [];
var pigs = [];
var blocks = [];
var currentBird = null;
var birdIndex = 0;
var isDragging = false;
var dragStart = {
x: 0,
y: 0
};
var dragEnd = {
x: 0,
y: 0
};
var slingshotX = 180;
var slingshotY = 2000; // Raised slingshot higher (was 2450)
var slingshotBandLength = 180;
var maxBirds = 3;
var groundY = 2650;
var level = 1;
var launching = false;
var score = 0;
// --- GUI ---
var scoreTxt = new Text2('0', {
size: 120,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
var birdsLeftTxt = new Text2('', {
size: 80,
fill: 0xFFFFFF
});
birdsLeftTxt.anchor.set(0.5, 0);
LK.gui.topRight.addChild(birdsLeftTxt);
// --- Draw Ground ---
var ground = LK.getAsset('ground', {
anchorX: 0,
anchorY: 0
});
ground.x = 0;
ground.y = groundY;
game.addChild(ground);
// --- Draw Little Mountain below the slingshot ---
var mountainWidth = 400;
var mountainHeight = 120;
var mountain = LK.getAsset('block', {
anchorX: 0.5,
anchorY: 1
});
mountain.width = mountainWidth;
mountain.height = mountainHeight;
mountain.x = slingshotX;
mountain.y = groundY + 10; // Slightly below ground for overlap
mountain.tint = 0x8b7765; // Optional: make it look more like a mountain
game.addChild(mountain);
// --- Draw Slingshot ---
var slingshot = LK.getAsset('slingshot', {
anchorX: 0.5,
anchorY: 1
});
slingshot.x = slingshotX;
slingshot.y = slingshotY + 90;
game.addChild(slingshot);
// --- Level Setup ---
function setupLevel() {
// Clear previous
for (var i = 0; i < birds.length; i++) birds[i].destroy();
for (var i = 0; i < pigs.length; i++) pigs[i].destroy();
for (var i = 0; i < blocks.length; i++) blocks[i].destroy();
birds = [];
pigs = [];
blocks = [];
birdIndex = 0;
score = 0;
LK.setScore(0);
scoreTxt.setText('0');
launching = false;
// Place birds
for (var i = 0; i < maxBirds; i++) {
var bird = new Bird();
bird.x = slingshotX - 120 - i * 80;
bird.y = slingshotY + 40;
birds.push(bird);
game.addChild(bird);
}
currentBird = birds[birdIndex];
// Place blocks to form a house shape
var houseCenterX = 1500;
var houseBottomY = groundY - 100;
var wallW = 180;
var wallH = 40;
var wallHeight = 4; // number of vertical wall blocks
var roofW = 180;
var roofH = 40;
// --- House base: two vertical walls (left/right), 4 blocks high ---
for (var i = 0; i < wallHeight; i++) {
// Left wall
var leftWall = new Block();
leftWall.x = houseCenterX - wallW / 2 + wallH / 2;
leftWall.y = houseBottomY - i * wallW;
leftWall.rotation = Math.PI / 2;
blocks.push(leftWall);
game.addChild(leftWall);
// Right wall
var rightWall = new Block();
rightWall.x = houseCenterX + wallW / 2 - wallH / 2;
rightWall.y = houseBottomY - i * wallW;
rightWall.rotation = Math.PI / 2;
blocks.push(rightWall);
game.addChild(rightWall);
}
// --- House base: bottom block (floor) ---
var houseFloor = new Block();
houseFloor.x = houseCenterX;
houseFloor.y = houseBottomY + wallH / 2;
blocks.push(houseFloor);
game.addChild(houseFloor);
// --- House base: top block (ceiling) ---
var houseCeiling = new Block();
houseCeiling.x = houseCenterX;
houseCeiling.y = houseBottomY - (wallHeight - 1) * wallW - wallH / 2;
blocks.push(houseCeiling);
game.addChild(houseCeiling);
// --- House roof: two diagonal blocks (left/right) ---
var roofLeft = new Block();
roofLeft.x = houseCenterX - wallW / 2 + wallH / 2 + 30;
roofLeft.y = houseCeiling.y - wallW / 2 + 10;
roofLeft.rotation = Math.PI / 4; // 45 degrees
blocks.push(roofLeft);
game.addChild(roofLeft);
var roofRight = new Block();
roofRight.x = houseCenterX + wallW / 2 - wallH / 2 - 30;
roofRight.y = houseCeiling.y - wallW / 2 + 10;
roofRight.rotation = -Math.PI / 4; // -45 degrees
blocks.push(roofRight);
game.addChild(roofRight);
// --- House roof: top horizontal block (roof ridge) ---
var roofRidge = new Block();
roofRidge.x = houseCenterX;
roofRidge.y = houseCeiling.y - wallW / 2 - roofH / 2 + 10;
blocks.push(roofRidge);
game.addChild(roofRidge);
// Place pig on the house ceiling
var pig1 = new Pig();
pig1.x = houseCeiling.x;
pig1.y = houseCeiling.y - houseCeiling.height / 2 - pig1.radius - 50;
pigs.push(pig1);
game.addChild(pig1);
// (Second pig inside the house removed)
// --- Ensure blocks and pigs are not hanging in the air ---
for (var i = 0; i < blocks.length; i++) {
var block = blocks[i];
// Check if block is supported by ground or another block
var supported = false;
// Supported by ground
if (Math.abs(block.y + block.height / 2 - groundY) < 2) {
supported = true;
}
// Supported by another block below
for (var j = 0; j < blocks.length; j++) {
if (i === j) continue;
var other = blocks[j];
var blockBottom = block.y + block.height / 2;
var otherTop = other.y - other.height / 2;
var horizontalOverlap = block.x + block.width / 2 > other.x - other.width / 2 && block.x - block.width / 2 < other.x + other.width / 2;
if (horizontalOverlap && Math.abs(blockBottom - otherTop) < 2) {
supported = true;
break;
}
}
block.stopped = !supported ? false : block.stopped;
}
for (var i = 0; i < pigs.length; i++) {
var pig = pigs[i];
// Check if pig is supported by ground or a block
var supported = false;
// Supported by ground
if (Math.abs(pig.y + pig.radius - groundY) < 2) {
supported = true;
}
// Supported by a block below
for (var j = 0; j < blocks.length; j++) {
var block = blocks[j];
var pigBottom = pig.y + pig.radius;
var blockTop = block.y - block.height / 2;
var horizontalOverlap = pig.x + pig.radius > block.x - block.width / 2 && pig.x - pig.radius < block.x + block.width / 2;
if (horizontalOverlap && Math.abs(pigBottom - blockTop) < 2) {
supported = true;
break;
}
}
pig.stopped = !supported ? false : pig.stopped;
}
updateBirdsLeft();
}
// --- Update birds left text ---
function updateBirdsLeft() {
birdsLeftTxt.setText('Birds: ' + (maxBirds - birdIndex));
}
// --- Drag and Launch Logic ---
function getDistance(x1, y1, x2, y2) {
var dx = x2 - x1;
var dy = y2 - y1;
return Math.sqrt(dx * dx + dy * dy);
}
function getAngle(x1, y1, x2, y2) {
return Math.atan2(y2 - y1, x2 - x1);
}
// Only allow drag if not launched
game.down = function (x, y, obj) {
if (launching) return;
if (!currentBird || currentBird.launched) return;
// Only allow drag if touch is near the bird
var dist = getDistance(x, y, currentBird.x, currentBird.y);
if (dist < currentBird.radius * 1.5) {
isDragging = true;
dragStart.x = x;
dragStart.y = y;
}
};
game.move = function (x, y, obj) {
if (isDragging && currentBird && !currentBird.launched) {
// Limit drag distance
var dx = x - slingshotX;
var dy = y - slingshotY;
var dist = getDistance(x, y, slingshotX, slingshotY);
if (dist > slingshotBandLength) {
var angle = getAngle(slingshotX, slingshotY, x, y);
dx = Math.cos(angle) * slingshotBandLength;
dy = Math.sin(angle) * slingshotBandLength;
}
currentBird.x = slingshotX + dx;
currentBird.y = slingshotY + dy;
dragEnd.x = currentBird.x;
dragEnd.y = currentBird.y;
}
};
game.up = function (x, y, obj) {
if (isDragging && currentBird && !currentBird.launched) {
isDragging = false;
// Calculate launch velocity
var dx = slingshotX - currentBird.x;
var dy = slingshotY - currentBird.y;
currentBird.vx = dx * 0.18;
currentBird.vy = dy * 0.18;
currentBird.launched = true;
launching = true;
// Animate slingshot back
tween(currentBird, {
x: currentBird.x,
y: currentBird.y
}, {
duration: 80
});
}
};
// --- Main Game Loop ---
game.update = function () {
// Update all birds
for (var i = 0; i < birds.length; i++) {
birds[i].update();
}
// Update all pigs
for (var i = 0; i < pigs.length; i++) {
pigs[i].update();
}
// Update all blocks
for (var i = 0; i < blocks.length; i++) {
blocks[i].update();
}
// --- Check support for each block: if not supported, set stopped = false so it falls ---
for (var i = 0; i < blocks.length; i++) {
var block = blocks[i];
// Check if block is supported by ground or another block
var supported = false;
// Supported by ground
if (Math.abs(block.y + block.height / 2 - groundY) < 2) {
supported = true;
}
// Supported by another block below
for (var j = 0; j < blocks.length; j++) {
if (i === j) continue;
var other = blocks[j];
var blockBottom = block.y + block.height / 2;
var otherTop = other.y - other.height / 2;
var horizontalOverlap = block.x + block.width / 2 > other.x - other.width / 2 && block.x - block.width / 2 < other.x + other.width / 2;
if (horizontalOverlap && Math.abs(blockBottom - otherTop) < 2) {
supported = true;
break;
}
}
// If not supported, set stopped = false so it will fall
if (!supported) {
block.stopped = false;
}
}
// Bird-block collision
for (var i = 0; i < birds.length; i++) {
var bird = birds[i];
if (!bird.launched) continue;
if (bird.stopped) continue;
// Collide with blocks
for (var j = 0; j < blocks.length; j++) {
var block = blocks[j];
// Remove block.stopped check so birds can always touch wood
if (block.intersectsCircle(bird)) {
// Calculate collision angle and force
var angle = getAngle(bird.x, bird.y, block.x, block.y);
var force = Math.sqrt(bird.vx * bird.vx + bird.vy * bird.vy) * 0.5;
// Apply force to block
block.vx += Math.cos(angle) * force * 0.7;
block.vy += Math.sin(angle) * force * 0.7;
// --- Ragdoll: add angular velocity to block based on bird impact ---
var impact = force * 0.2;
if (typeof block.angularVelocity === "undefined") block.angularVelocity = 0;
block.angularVelocity += impact * (Math.random() - 0.5);
// Apply bounce to bird
bird.vx = -bird.vx * 0.4;
bird.vy = -bird.vy * 0.4;
// Move bird out of block to prevent sticking
var overlap = bird.radius + Math.max(block.width, block.height) / 2 - getDistance(bird.x, bird.y, block.x, block.y);
if (overlap > 0) {
bird.x += Math.cos(angle) * overlap * 0.5;
bird.y += Math.sin(angle) * overlap * 0.5;
}
// Optionally, mark block as not stopped to allow it to move if hit hard
block.stopped = false;
// Birds deal damage to woods on collision
var birdDamage = Math.min(40, Math.max(5, Math.floor(force * 2)));
block.health -= birdDamage;
if (block.health <= 0) {
block.destroy();
blocks.splice(j, 1);
j--; // Adjust index since we removed a block
continue;
}
}
}
}
// Bird-pig collision
for (var i = 0; i < birds.length; i++) {
var bird = birds[i];
if (!bird.launched) continue;
if (bird.stopped) continue;
for (var j = 0; j < pigs.length; j++) {
var pig = pigs[j];
if (!pig.alive) continue;
if (bird.intersectsCircle(pig)) {
pig.alive = false;
pig.visible = false;
LK.setScore(LK.getScore() + 1000);
scoreTxt.setText(LK.getScore());
}
}
}
// Block-pig collision (falling blocks can kill pigs)
for (var i = 0; i < blocks.length; i++) {
var block = blocks[i];
if (block.stopped) continue;
for (var j = 0; j < pigs.length; j++) {
var pig = pigs[j];
if (!pig.alive) continue;
// Treat pig as circle, block as rectangle
if (block.intersectsCircle(pig)) {
// Only kill pig if block is moving fast enough (e.g. falling hard)
var blockSpeed = Math.sqrt(block.vx * block.vx + block.vy * block.vy);
if (blockSpeed > 8) {
pig.alive = false;
pig.visible = false;
LK.setScore(LK.getScore() + 1000);
scoreTxt.setText(LK.getScore());
}
}
}
}
// --- Check support for each pig: if not supported, set stopped = false so it falls ---
for (var i = 0; i < pigs.length; i++) {
var pig = pigs[i];
if (!pig.alive) continue;
var supported = false;
// Supported by ground
if (Math.abs(pig.y + pig.radius - groundY) < 2) {
supported = true;
}
// Supported by a block below
for (var j = 0; j < blocks.length; j++) {
var block = blocks[j];
var pigBottom = pig.y + pig.radius;
var blockTop = block.y - block.height / 2;
var horizontalOverlap = pig.x + pig.radius > block.x - block.width / 2 && pig.x - pig.radius < block.x + block.width / 2;
if (horizontalOverlap && Math.abs(pigBottom - blockTop) < 2) {
supported = true;
break;
}
}
if (!supported) {
pig.stopped = false;
}
}
// Remove dead pigs
var allPigsDead = true;
for (var i = 0; i < pigs.length; i++) {
if (pigs[i].alive) {
allPigsDead = false;
break;
}
}
// If all pigs dead, win (only if there was at least one pig)
if (pigs.length > 0 && allPigsDead) {
LK.showYouWin();
return;
}
// If current bird stopped, move to next bird
if (currentBird && currentBird.launched && currentBird.stopped && !isDragging && launching) {
birdIndex++;
updateBirdsLeft();
if (birdIndex < birds.length) {
currentBird = birds[birdIndex];
// Move next bird to slingshot
tween(currentBird, {
x: slingshotX,
y: slingshotY
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
launching = false;
}
});
} else {
// Out of birds, game over
LK.showGameOver();
return;
}
launching = false;
}
};
// --- Start Game ---
setupLevel();