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();
===================================================================
--- original.js
+++ change.js
@@ -1,6 +1,506 @@
-/****
+/****
+* 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
-});
\ No newline at end of file
+});
+
+/****
+* 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();
\ No newline at end of file