/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Obstacle: Acid Pool
var AcidPool = Container.expand(function () {
var self = Container.call(this);
var acid = self.attachAsset('acid_pool', {
anchorX: 0.5,
anchorY: 0.5
});
self.damage = 1;
return self;
});
// Bird class: each bird has a type, unique ability, and can be controlled
var Bird = Container.expand(function () {
var self = Container.call(this);
// Properties
self.type = 'red'; // 'red', 'blue', 'yellow'
self.isActive = false;
self.isOnGround = false;
self.isBubbled = false;
self.bubbleTimer = 0;
// Attach correct asset
var assetId = 'bird_red';
if (self.type === 'blue') assetId = 'bird_blue';
if (self.type === 'yellow') assetId = 'bird_yellow';
var birdSprite = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5
});
// Physics
self.vx = 0;
self.vy = 0;
self.gravity = 1.5;
self.jumpPower = -38;
self.moveSpeed = 18;
// Ability cooldowns
self.abilityReady = true;
self.abilityCooldown = 0;
// Set bird type and adjust stats
self.setType = function (type) {
self.type = type;
if (type === 'red') {
birdSprite.assetId = 'bird_red';
self.jumpPower = -38;
self.moveSpeed = 18;
} else if (type === 'blue') {
birdSprite.assetId = 'bird_blue';
self.jumpPower = -32;
self.moveSpeed = 24;
} else if (type === 'yellow') {
birdSprite.assetId = 'bird_yellow';
self.jumpPower = -28;
self.moveSpeed = 16;
}
};
// Activate/deactivate
self.setActive = function (active) {
self.isActive = active;
birdSprite.alpha = active ? 1 : 0.5;
if (active) {
tween(self, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 120,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1
}, {
duration: 120,
easing: tween.easeIn
});
}
});
}
};
// Bird jump
self.jump = function () {
if (self.isOnGround && !self.isBubbled) {
self.vy = self.jumpPower;
self.isOnGround = false;
LK.getSound('jump').play();
}
};
// Bird ability
self.useAbility = function () {
if (!self.abilityReady || self.isBubbled) return;
if (self.type === 'red') {
// Red: invulnerable for 1s (flashes)
self.abilityReady = false;
self.invulnerable = true;
var flashCount = 0;
var flashTimer = LK.setInterval(function () {
birdSprite.alpha = birdSprite.alpha === 1 ? 0.3 : 1;
flashCount++;
if (flashCount > 8) {
LK.clearInterval(flashTimer);
birdSprite.alpha = self.isActive ? 1 : 0.5;
self.invulnerable = false;
self.abilityCooldown = 60;
}
}, 120);
} else if (self.type === 'blue') {
// Blue: dash forward
self.abilityReady = false;
self.vx += (self.facingRight ? 1 : -1) * 60;
self.abilityCooldown = 60;
} else if (self.type === 'yellow') {
// Yellow: bubble float (becomes bubbled for 1.2s)
self.abilityReady = false;
self.isBubbled = true;
self.bubbleTimer = 72;
LK.getSound('bubble_pop').play();
self.abilityCooldown = 90;
}
};
// Update per frame
self.update = function () {
// Ability cooldown
if (!self.abilityReady) {
self.abilityCooldown--;
if (self.abilityCooldown <= 0) {
self.abilityReady = true;
}
}
// Bubble float
if (self.isBubbled) {
self.vy = -6;
self.bubbleTimer--;
if (self.bubbleTimer <= 0) {
self.isBubbled = false;
}
} else {
// Gravity
self.vy += self.gravity;
}
// Clamp vy
if (self.vy > 40) self.vy = 40;
// Move
self.x += self.vx;
self.y += self.vy;
// Friction
self.vx *= 0.82;
if (Math.abs(self.vx) < 1) self.vx = 0;
// Clamp to stomach bounds
if (self.x < 200) self.x = 200;
if (self.x > 2048 - 200) self.x = 2048 - 200;
if (self.y < 300) self.y = 300;
if (self.y > 2732 - 200) {
self.y = 2732 - 200;
self.vy = 0;
self.isOnGround = true;
}
};
return self;
});
// Obstacle: Bubble (for yellow bird to float)
var Bubble = Container.expand(function () {
var self = Container.call(this);
var bubble = self.attachAsset('bubble', {
anchorX: 0.5,
anchorY: 0.5
});
self.active = true;
return self;
});
// Exit: Esophagus
var Exit = Container.expand(function () {
var self = Container.call(this);
var exit = self.attachAsset('exit', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
// Obstacle: Spike
var Spike = Container.expand(function () {
var self = Container.call(this);
var spike = self.attachAsset('spike', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Music
// Sound effects
// Exit: esophagus (brown ellipse)
// Obstacles: acid pool (green), spike (purple triangle), bubble (blue ellipse)
// Crab stomach background (large ellipse, light pink)
// Bird assets: three birds, each with a unique color and shape
// Play music
LK.playMusic('crabbelly_theme');
// Add stomach background
var stomach = LK.getAsset('crab_stomach', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366
});
game.addChild(stomach);
// Obstacles
var obstacles = [];
var acid1 = new AcidPool();
acid1.x = 1024;
acid1.y = 1800;
game.addChild(acid1);
obstacles.push(acid1);
var acid2 = new AcidPool();
acid2.x = 600;
acid2.y = 1200;
game.addChild(acid2);
obstacles.push(acid2);
var spike1 = new Spike();
spike1.x = 1500;
spike1.y = 900;
game.addChild(spike1);
obstacles.push(spike1);
var spike2 = new Spike();
spike2.x = 500;
spike2.y = 600;
game.addChild(spike2);
obstacles.push(spike2);
var bubble1 = new Bubble();
bubble1.x = 900;
bubble1.y = 1550;
game.addChild(bubble1);
obstacles.push(bubble1);
var bubble2 = new Bubble();
bubble2.x = 1300;
bubble2.y = 700;
game.addChild(bubble2);
obstacles.push(bubble2);
// Exit
var exit = new Exit();
exit.x = 1024;
exit.y = 400;
game.addChild(exit);
// Birds
var birds = [];
var birdRed = new Bird();
birdRed.setType('red');
birdRed.x = 1024;
birdRed.y = 2200;
birdRed.facingRight = true;
game.addChild(birdRed);
birds.push(birdRed);
var birdBlue = new Bird();
birdBlue.setType('blue');
birdBlue.x = 1124;
birdBlue.y = 2250;
birdBlue.facingRight = true;
game.addChild(birdBlue);
birds.push(birdBlue);
var birdYellow = new Bird();
birdYellow.setType('yellow');
birdYellow.x = 924;
birdYellow.y = 2250;
birdYellow.facingRight = true;
game.addChild(birdYellow);
birds.push(birdYellow);
// Active bird index
var activeBirdIndex = 0;
birds[activeBirdIndex].setActive(true);
// Health and timer
var health = 3;
var maxTime = 60 * 30; // 30 seconds
var timeLeft = maxTime;
// Score text (shows time left)
var timerTxt = new Text2('Time: 30', {
size: 90,
fill: '#fff'
});
timerTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(timerTxt);
var healthTxt = new Text2('❤️❤️❤️', {
size: 90,
fill: '#fff'
});
healthTxt.anchor.set(0.5, 0);
LK.gui.topRight.addChild(healthTxt);
// Switch bird button (bottom center)
var switchTxt = new Text2('Switch Bird', {
size: 80,
fill: '#fff'
});
switchTxt.anchor.set(0.5, 0.5);
LK.gui.bottom.addChild(switchTxt);
// Ability button (bottom right)
var abilityTxt = new Text2('Ability', {
size: 80,
fill: '#fff'
});
abilityTxt.anchor.set(0.5, 0.5);
LK.gui.bottomRight.addChild(abilityTxt);
// Dragging
var dragNode = null;
var dragOffsetX = 0;
var dragOffsetY = 0;
// Touch controls: tap left/right to move, tap above to jump, tap switch/ability
var lastTouchX = 0;
var lastTouchY = 0;
// Helper: update health display
function updateHealth() {
var s = '';
for (var i = 0; i < health; i++) s += '❤️';
healthTxt.setText(s);
}
// Helper: update timer display
function updateTimer() {
var t = Math.ceil(timeLeft / 60);
timerTxt.setText('Time: ' + t);
}
// Helper: switch active bird
function switchBird() {
birds[activeBirdIndex].setActive(false);
activeBirdIndex = (activeBirdIndex + 1) % birds.length;
birds[activeBirdIndex].setActive(true);
LK.getSound('switch').play();
}
// Helper: use ability
function useAbility() {
birds[activeBirdIndex].useAbility();
}
// Helper: check collision between two objects
function collides(a, b) {
var dx = a.x - b.x;
var dy = a.y - b.y;
var dist = Math.sqrt(dx * dx + dy * dy);
var r1 = (a.width ? a.width : 120) / 2;
var r2 = (b.width ? b.width : 120) / 2;
return dist < (r1 + r2) * 0.8;
}
// Game move handler (drag to move bird, tap to jump)
game.move = function (x, y, obj) {
// Only allow drag if started on bird
if (dragNode) {
dragNode.x = x - dragOffsetX;
dragNode.y = y - dragOffsetY;
}
lastTouchX = x;
lastTouchY = y;
};
// Touch down: check if on bird, switch, or ability
game.down = function (x, y, obj) {
// Check if on switch button
var guiSwitch = LK.gui.bottom.toLocal({
x: x,
y: y
});
if (switchTxt.containsPoint && switchTxt.containsPoint(guiSwitch)) {
switchBird();
return;
}
// Check if on ability button
var guiAbility = LK.gui.bottomRight.toLocal({
x: x,
y: y
});
if (abilityTxt.containsPoint && abilityTxt.containsPoint(guiAbility)) {
useAbility();
return;
}
// Check if on active bird
var bird = birds[activeBirdIndex];
var bx = bird.x,
by = bird.y;
var dx = x - bx,
dy = y - by;
if (dx * dx + dy * dy < 100 * 100) {
dragNode = bird;
dragOffsetX = x - bird.x;
dragOffsetY = y - bird.y;
} else {
// If tap above bird, jump
if (y < bird.y - 80) {
bird.jump();
} else if (x < bird.x - 60) {
bird.vx = -bird.moveSpeed;
bird.facingRight = false;
} else if (x > bird.x + 60) {
bird.vx = bird.moveSpeed;
bird.facingRight = true;
}
}
};
// Touch up: stop drag
game.up = function (x, y, obj) {
dragNode = null;
};
// Main update loop
game.update = function () {
// Timer
timeLeft--;
if (timeLeft < 0) {
LK.getSound('lose').play();
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
return;
}
updateTimer();
// Update birds
for (var i = 0; i < birds.length; i++) {
birds[i].update();
}
// Collisions: obstacles
for (var i = 0; i < birds.length; i++) {
var bird = birds[i];
// Acid
for (var j = 0; j < obstacles.length; j++) {
var obs = obstacles[j];
if (obs instanceof AcidPool) {
if (collides(bird, obs)) {
if (!bird.invulnerable && !bird.isBubbled) {
health--;
updateHealth();
LK.getSound('acid_sizzle').play();
LK.effects.flashObject(bird, 0x00ff00, 600);
bird.invulnerable = true;
// Temporary invulnerability
(function (b) {
var t = LK.setTimeout(function () {
b.invulnerable = false;
}, 900);
})(bird);
if (health <= 0) {
LK.getSound('lose').play();
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
return;
}
}
}
}
// Spikes
if (obs instanceof Spike) {
if (collides(bird, obs)) {
if (!bird.invulnerable && !bird.isBubbled) {
health--;
updateHealth();
LK.effects.flashObject(bird, 0xaa00ff, 600);
bird.invulnerable = true;
(function (b) {
var t = LK.setTimeout(function () {
b.invulnerable = false;
}, 900);
})(bird);
if (health <= 0) {
LK.getSound('lose').play();
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
return;
}
}
}
}
// Bubbles
if (obs instanceof Bubble && obs.active) {
if (collides(bird, obs)) {
if (bird.type === 'yellow' && !bird.isBubbled) {
bird.isBubbled = true;
bird.bubbleTimer = 72;
obs.active = false;
obs.visible = false;
LK.getSound('bubble_pop').play();
}
}
}
}
// Exit
if (collides(bird, exit)) {
// Only if all birds reach exit, win
var allAtExit = true;
for (var k = 0; k < birds.length; k++) {
if (!collides(birds[k], exit)) allAtExit = false;
}
if (allAtExit) {
LK.getSound('win').play();
LK.effects.flashScreen(0x00ff00, 1000);
LK.showYouWin();
return;
}
}
}
};
// Initial health/timer display
updateHealth();
updateTimer(); /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Obstacle: Acid Pool
var AcidPool = Container.expand(function () {
var self = Container.call(this);
var acid = self.attachAsset('acid_pool', {
anchorX: 0.5,
anchorY: 0.5
});
self.damage = 1;
return self;
});
// Bird class: each bird has a type, unique ability, and can be controlled
var Bird = Container.expand(function () {
var self = Container.call(this);
// Properties
self.type = 'red'; // 'red', 'blue', 'yellow'
self.isActive = false;
self.isOnGround = false;
self.isBubbled = false;
self.bubbleTimer = 0;
// Attach correct asset
var assetId = 'bird_red';
if (self.type === 'blue') assetId = 'bird_blue';
if (self.type === 'yellow') assetId = 'bird_yellow';
var birdSprite = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5
});
// Physics
self.vx = 0;
self.vy = 0;
self.gravity = 1.5;
self.jumpPower = -38;
self.moveSpeed = 18;
// Ability cooldowns
self.abilityReady = true;
self.abilityCooldown = 0;
// Set bird type and adjust stats
self.setType = function (type) {
self.type = type;
if (type === 'red') {
birdSprite.assetId = 'bird_red';
self.jumpPower = -38;
self.moveSpeed = 18;
} else if (type === 'blue') {
birdSprite.assetId = 'bird_blue';
self.jumpPower = -32;
self.moveSpeed = 24;
} else if (type === 'yellow') {
birdSprite.assetId = 'bird_yellow';
self.jumpPower = -28;
self.moveSpeed = 16;
}
};
// Activate/deactivate
self.setActive = function (active) {
self.isActive = active;
birdSprite.alpha = active ? 1 : 0.5;
if (active) {
tween(self, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 120,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1
}, {
duration: 120,
easing: tween.easeIn
});
}
});
}
};
// Bird jump
self.jump = function () {
if (self.isOnGround && !self.isBubbled) {
self.vy = self.jumpPower;
self.isOnGround = false;
LK.getSound('jump').play();
}
};
// Bird ability
self.useAbility = function () {
if (!self.abilityReady || self.isBubbled) return;
if (self.type === 'red') {
// Red: invulnerable for 1s (flashes)
self.abilityReady = false;
self.invulnerable = true;
var flashCount = 0;
var flashTimer = LK.setInterval(function () {
birdSprite.alpha = birdSprite.alpha === 1 ? 0.3 : 1;
flashCount++;
if (flashCount > 8) {
LK.clearInterval(flashTimer);
birdSprite.alpha = self.isActive ? 1 : 0.5;
self.invulnerable = false;
self.abilityCooldown = 60;
}
}, 120);
} else if (self.type === 'blue') {
// Blue: dash forward
self.abilityReady = false;
self.vx += (self.facingRight ? 1 : -1) * 60;
self.abilityCooldown = 60;
} else if (self.type === 'yellow') {
// Yellow: bubble float (becomes bubbled for 1.2s)
self.abilityReady = false;
self.isBubbled = true;
self.bubbleTimer = 72;
LK.getSound('bubble_pop').play();
self.abilityCooldown = 90;
}
};
// Update per frame
self.update = function () {
// Ability cooldown
if (!self.abilityReady) {
self.abilityCooldown--;
if (self.abilityCooldown <= 0) {
self.abilityReady = true;
}
}
// Bubble float
if (self.isBubbled) {
self.vy = -6;
self.bubbleTimer--;
if (self.bubbleTimer <= 0) {
self.isBubbled = false;
}
} else {
// Gravity
self.vy += self.gravity;
}
// Clamp vy
if (self.vy > 40) self.vy = 40;
// Move
self.x += self.vx;
self.y += self.vy;
// Friction
self.vx *= 0.82;
if (Math.abs(self.vx) < 1) self.vx = 0;
// Clamp to stomach bounds
if (self.x < 200) self.x = 200;
if (self.x > 2048 - 200) self.x = 2048 - 200;
if (self.y < 300) self.y = 300;
if (self.y > 2732 - 200) {
self.y = 2732 - 200;
self.vy = 0;
self.isOnGround = true;
}
};
return self;
});
// Obstacle: Bubble (for yellow bird to float)
var Bubble = Container.expand(function () {
var self = Container.call(this);
var bubble = self.attachAsset('bubble', {
anchorX: 0.5,
anchorY: 0.5
});
self.active = true;
return self;
});
// Exit: Esophagus
var Exit = Container.expand(function () {
var self = Container.call(this);
var exit = self.attachAsset('exit', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
// Obstacle: Spike
var Spike = Container.expand(function () {
var self = Container.call(this);
var spike = self.attachAsset('spike', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Music
// Sound effects
// Exit: esophagus (brown ellipse)
// Obstacles: acid pool (green), spike (purple triangle), bubble (blue ellipse)
// Crab stomach background (large ellipse, light pink)
// Bird assets: three birds, each with a unique color and shape
// Play music
LK.playMusic('crabbelly_theme');
// Add stomach background
var stomach = LK.getAsset('crab_stomach', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366
});
game.addChild(stomach);
// Obstacles
var obstacles = [];
var acid1 = new AcidPool();
acid1.x = 1024;
acid1.y = 1800;
game.addChild(acid1);
obstacles.push(acid1);
var acid2 = new AcidPool();
acid2.x = 600;
acid2.y = 1200;
game.addChild(acid2);
obstacles.push(acid2);
var spike1 = new Spike();
spike1.x = 1500;
spike1.y = 900;
game.addChild(spike1);
obstacles.push(spike1);
var spike2 = new Spike();
spike2.x = 500;
spike2.y = 600;
game.addChild(spike2);
obstacles.push(spike2);
var bubble1 = new Bubble();
bubble1.x = 900;
bubble1.y = 1550;
game.addChild(bubble1);
obstacles.push(bubble1);
var bubble2 = new Bubble();
bubble2.x = 1300;
bubble2.y = 700;
game.addChild(bubble2);
obstacles.push(bubble2);
// Exit
var exit = new Exit();
exit.x = 1024;
exit.y = 400;
game.addChild(exit);
// Birds
var birds = [];
var birdRed = new Bird();
birdRed.setType('red');
birdRed.x = 1024;
birdRed.y = 2200;
birdRed.facingRight = true;
game.addChild(birdRed);
birds.push(birdRed);
var birdBlue = new Bird();
birdBlue.setType('blue');
birdBlue.x = 1124;
birdBlue.y = 2250;
birdBlue.facingRight = true;
game.addChild(birdBlue);
birds.push(birdBlue);
var birdYellow = new Bird();
birdYellow.setType('yellow');
birdYellow.x = 924;
birdYellow.y = 2250;
birdYellow.facingRight = true;
game.addChild(birdYellow);
birds.push(birdYellow);
// Active bird index
var activeBirdIndex = 0;
birds[activeBirdIndex].setActive(true);
// Health and timer
var health = 3;
var maxTime = 60 * 30; // 30 seconds
var timeLeft = maxTime;
// Score text (shows time left)
var timerTxt = new Text2('Time: 30', {
size: 90,
fill: '#fff'
});
timerTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(timerTxt);
var healthTxt = new Text2('❤️❤️❤️', {
size: 90,
fill: '#fff'
});
healthTxt.anchor.set(0.5, 0);
LK.gui.topRight.addChild(healthTxt);
// Switch bird button (bottom center)
var switchTxt = new Text2('Switch Bird', {
size: 80,
fill: '#fff'
});
switchTxt.anchor.set(0.5, 0.5);
LK.gui.bottom.addChild(switchTxt);
// Ability button (bottom right)
var abilityTxt = new Text2('Ability', {
size: 80,
fill: '#fff'
});
abilityTxt.anchor.set(0.5, 0.5);
LK.gui.bottomRight.addChild(abilityTxt);
// Dragging
var dragNode = null;
var dragOffsetX = 0;
var dragOffsetY = 0;
// Touch controls: tap left/right to move, tap above to jump, tap switch/ability
var lastTouchX = 0;
var lastTouchY = 0;
// Helper: update health display
function updateHealth() {
var s = '';
for (var i = 0; i < health; i++) s += '❤️';
healthTxt.setText(s);
}
// Helper: update timer display
function updateTimer() {
var t = Math.ceil(timeLeft / 60);
timerTxt.setText('Time: ' + t);
}
// Helper: switch active bird
function switchBird() {
birds[activeBirdIndex].setActive(false);
activeBirdIndex = (activeBirdIndex + 1) % birds.length;
birds[activeBirdIndex].setActive(true);
LK.getSound('switch').play();
}
// Helper: use ability
function useAbility() {
birds[activeBirdIndex].useAbility();
}
// Helper: check collision between two objects
function collides(a, b) {
var dx = a.x - b.x;
var dy = a.y - b.y;
var dist = Math.sqrt(dx * dx + dy * dy);
var r1 = (a.width ? a.width : 120) / 2;
var r2 = (b.width ? b.width : 120) / 2;
return dist < (r1 + r2) * 0.8;
}
// Game move handler (drag to move bird, tap to jump)
game.move = function (x, y, obj) {
// Only allow drag if started on bird
if (dragNode) {
dragNode.x = x - dragOffsetX;
dragNode.y = y - dragOffsetY;
}
lastTouchX = x;
lastTouchY = y;
};
// Touch down: check if on bird, switch, or ability
game.down = function (x, y, obj) {
// Check if on switch button
var guiSwitch = LK.gui.bottom.toLocal({
x: x,
y: y
});
if (switchTxt.containsPoint && switchTxt.containsPoint(guiSwitch)) {
switchBird();
return;
}
// Check if on ability button
var guiAbility = LK.gui.bottomRight.toLocal({
x: x,
y: y
});
if (abilityTxt.containsPoint && abilityTxt.containsPoint(guiAbility)) {
useAbility();
return;
}
// Check if on active bird
var bird = birds[activeBirdIndex];
var bx = bird.x,
by = bird.y;
var dx = x - bx,
dy = y - by;
if (dx * dx + dy * dy < 100 * 100) {
dragNode = bird;
dragOffsetX = x - bird.x;
dragOffsetY = y - bird.y;
} else {
// If tap above bird, jump
if (y < bird.y - 80) {
bird.jump();
} else if (x < bird.x - 60) {
bird.vx = -bird.moveSpeed;
bird.facingRight = false;
} else if (x > bird.x + 60) {
bird.vx = bird.moveSpeed;
bird.facingRight = true;
}
}
};
// Touch up: stop drag
game.up = function (x, y, obj) {
dragNode = null;
};
// Main update loop
game.update = function () {
// Timer
timeLeft--;
if (timeLeft < 0) {
LK.getSound('lose').play();
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
return;
}
updateTimer();
// Update birds
for (var i = 0; i < birds.length; i++) {
birds[i].update();
}
// Collisions: obstacles
for (var i = 0; i < birds.length; i++) {
var bird = birds[i];
// Acid
for (var j = 0; j < obstacles.length; j++) {
var obs = obstacles[j];
if (obs instanceof AcidPool) {
if (collides(bird, obs)) {
if (!bird.invulnerable && !bird.isBubbled) {
health--;
updateHealth();
LK.getSound('acid_sizzle').play();
LK.effects.flashObject(bird, 0x00ff00, 600);
bird.invulnerable = true;
// Temporary invulnerability
(function (b) {
var t = LK.setTimeout(function () {
b.invulnerable = false;
}, 900);
})(bird);
if (health <= 0) {
LK.getSound('lose').play();
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
return;
}
}
}
}
// Spikes
if (obs instanceof Spike) {
if (collides(bird, obs)) {
if (!bird.invulnerable && !bird.isBubbled) {
health--;
updateHealth();
LK.effects.flashObject(bird, 0xaa00ff, 600);
bird.invulnerable = true;
(function (b) {
var t = LK.setTimeout(function () {
b.invulnerable = false;
}, 900);
})(bird);
if (health <= 0) {
LK.getSound('lose').play();
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
return;
}
}
}
}
// Bubbles
if (obs instanceof Bubble && obs.active) {
if (collides(bird, obs)) {
if (bird.type === 'yellow' && !bird.isBubbled) {
bird.isBubbled = true;
bird.bubbleTimer = 72;
obs.active = false;
obs.visible = false;
LK.getSound('bubble_pop').play();
}
}
}
}
// Exit
if (collides(bird, exit)) {
// Only if all birds reach exit, win
var allAtExit = true;
for (var k = 0; k < birds.length; k++) {
if (!collides(birds[k], exit)) allAtExit = false;
}
if (allAtExit) {
LK.getSound('win').play();
LK.effects.flashScreen(0x00ff00, 1000);
LK.showYouWin();
return;
}
}
}
};
// Initial health/timer display
updateHealth();
updateTimer();