Code edit (1 edits merged)
Please save this source code
User prompt
Cecil's Avian Escape
Initial prompt
Okay, here's a comic strip based on your prompt, focusing on visual humor and a bit of the absurd: Title: Crab's Conundrum Panel 1 Setting: A sunny beach. A large, comical-looking crab sits, bulging slightly. Visual: The crab is bright red and has a HUGE, satisfied grin. Tiny bird feathers stick out of the corners of its mouth. Caption: "Cecil the Crab had a VERY ambitious lunch..." Panel 2 Setting: The same beach, slightly to the side of the crab. Visual: A majestic Falcon, a bald Eagle, a red-billed Chough, a small Robin, and a tall Stork stand, looking perplexed. They are all perfectly clean and unharmed. Falcon: (Speech balloon, looking stern) "Well, this is awkward." Panel 3 Setting: Close-up on the crab. He now looks uncomfortable. His eyes are wide with panic. A single, tiny twig pokes out of his mouth this time. Visual: The crab is sweating - maybe showing a little tear. Cecil the Crab: (Speech balloon, a shaky whisper) "Oh dear..." Panel 4 Setting: Wide shot showing the crab and the birds again. Visual: The birds are now circling the crab warily. The stork towers over the other birds, peering down incredulously. Eagle: (Speech balloon, pointing a wing) "I SAW him eat them! Where did you all go?!" Panel 5 Setting: An X-Ray view of the crab. Visual: The insides of the crab are a bunch of chaos. But you can clearly see the Falcon, Eagle, Chough, Robin, and Stork. The birds are crammed together and are seemingly holding onto each other while inside the crab's stomach. Caption: "Inside Cecil's tummy, however, it was like a very strange avian support group." Panel 6 Setting: Back to the beach scene. The crab is now surrounded by the birds. The crab suddenly explodes, the birds go flying. Visual: The crab is now in pieces, and the birds are once again outside. Caption: "And that was the last time that Cecil the Crab ate such a strange meal." I hope you enjoy this comedic take on your prompt! Let me know if you'd like to try another one. With Keyboard
/**** * 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();