/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var Bird = Container.expand(function () { var self = Container.call(this); var birdGraphics = self.attachAsset('bird', { anchorX: 0.5, anchorY: 0.5 }); self.xSpeed = 10.9375; self.ySpeed = -20; self.gravity = 1; self.lift = -15; self.flap = function () { self.ySpeed = self.lift * 1.5; LK.getSound('flap').play(); }; self._update_migrated = function () { if (game.isMouseDown) { self.ySpeed += self.gravity / 3; } else { self.ySpeed += self.gravity; } self.y += self.ySpeed; self.x += self.xSpeed; // Bounce off top and bottom boundaries if (self.y <= 0) { self.y = 0; self.ySpeed = Math.abs(self.ySpeed) * 0.8; // Bounce with some energy loss tween(self, { ySpeed: self.ySpeed * 0.5 }, { duration: 200, easing: tween.easeOut }); } if (self.y >= 2732) { self.y = 2732; self.ySpeed = -Math.abs(self.ySpeed) * 0.8; // Bounce with some energy loss tween(self, { ySpeed: self.ySpeed * 0.5 }, { duration: 200, easing: tween.easeOut }); } var targetRotation = Math.atan2(self.ySpeed, self.xSpeed * self.scale.x) / 2; birdGraphics.rotation += (targetRotation - birdGraphics.rotation) / 10; }; self.flip = function () { self.scale.x *= -1; }; }); var Flame = Container.expand(function () { var self = Container.call(this); var flameGraphics = self.attachAsset('flame', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 3; self.flickerTimer = 0; self._update_migrated = function () { // Flickering animation self.flickerTimer++; var flicker = Math.sin(self.flickerTimer * 0.3) * 0.3 + 0.7; flameGraphics.alpha = flicker; // Color animation between orange and red var colorLerp = Math.sin(self.flickerTimer * 0.2) * 0.5 + 0.5; var red = Math.floor(255 * (0.8 + colorLerp * 0.2)); var green = Math.floor(69 * (0.5 + colorLerp * 0.5)); flameGraphics.tint = red << 16 | green << 8 | 0; // Scale animation var scale = 0.8 + Math.sin(self.flickerTimer * 0.25) * 0.2; flameGraphics.scale.set(scale); }; return self; }); var Obstacle = Container.expand(function () { var self = Container.call(this); var obstacleShadow = self.attachAsset('obstacleShadow', { anchorX: 0.5, anchorY: 0.5 }); obstacleShadow.rotation = Math.PI / 4; var obstacleShadow2 = self.attachAsset('obstacleShadow2', { anchorX: 0.5, anchorY: 0.5 }); obstacleShadow2.rotation = Math.PI / 4; obstacleShadow2.y = -7; var obstacleGraphics = self.attachAsset('obstacle', { anchorX: 0.5, anchorY: 0.5 }); obstacleGraphics.rotation = Math.PI / 4; self.speed = 5; // Animation properties self.shouldRotate = Math.random() < 0.3; // 30% chance to rotate self.shouldScale = Math.random() < 0.3; // 30% chance to scale self.rotationSpeed = (Math.random() - 0.5) * 0.05; // Random rotation speed self.baseScale = 1; self.scaleRange = 0.3; // Scale variation range self._move_migrated = function (speed) { self.y += speed; // Apply rotation animation with dynamic speed multiplier if (self.shouldRotate) { var currentRotationSpeed = self.rotationSpeed * game.currentRotationSpeedMultiplier; obstacleShadow.rotation += currentRotationSpeed; obstacleShadow2.rotation += currentRotationSpeed; obstacleGraphics.rotation += currentRotationSpeed; } }; }); var Wall = Container.expand(function () { var self = Container.call(this); var wallGraphics = self.attachAsset('wall', { anchorX: 0.5, anchorY: 0.5 }); }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000000 }); /**** * Game Code ****/ var tutorialTextWhite = new Text2('Tap to Flap\nHold to Float', { size: 150, fill: '#ffffff', font: 'Impact', align: 'center' }); tutorialTextWhite.anchor.set(.5, 1); tutorialTextWhite.x = -4; tutorialTextWhite.y = -62; LK.gui.bottom.addChild(tutorialTextWhite); var tutorialText = new Text2('Tap to Flap\nHold to Float', { size: 150, fill: '#3a84f7', font: 'Impact', dropShadow: true, dropShadowColor: '#222a9a', dropShadowBlur: 5, dropShadowDistance: 7, dropShadowAngle: 0, align: 'center' }); tutorialText.anchor.set(.5, 1); tutorialText.y = -50; LK.gui.bottom.addChild(tutorialText); game.score = 0; game.obstacleSpeed = 5; game.obstacleSpeedIncrease = 0.005; game.maxHealth = 200; game.currentHealth = 200; game.speedBoostTimer = 0; game.speedBoostDuration = 0; game.isSpeedBoosted = false; game.baseObstacleSpeed = 5; game.currentSpeed = 5; game.targetSpeed = 5; game.speedTweenTimer = 0; game.rotationSpeedTweenTimer = 0; game.targetRotationSpeedMultiplier = 1; game.currentRotationSpeedMultiplier = 1; game.checkObstacleCollision = function (obstacles) { for (var i = 0; i < obstacles.length; i++) { obstacles[i]._move_migrated(); var dist = Math.sqrt(Math.pow(bird.x - obstacles[i].x, 2) + Math.pow(bird.y - obstacles[i].y, 2)); if (dist < 200) { game.currentHealth -= 25; LK.getSound('bounce').play(); // Make bird turn red when hit by obstacle (same as flame effect) tween(bird, { tint: 0xff0000 }, { duration: 100, onFinish: function onFinish() { tween(bird, { tint: 0xffffff }, { duration: 500 }); } }); // Make hearts turn red when damage is taken for (var h = 0; h < hearts.length; h++) { (function (heart) { tween(heart, { tint: 0xff0000 }, { duration: 100, onFinish: function onFinish() { tween(heart, { tint: 0xffffff }, { duration: 500 }); } }); })(hearts[h]); } obstacles[i].destroy(); obstacles.splice(i, 1); if (game.currentHealth <= 0) { LK.setScore(game.score); LK.getSound('gameOverJingle').play(); LK.showGameOver(); } break; } } }; game.setBackgroundColor(0xadd8e6); var scoreText = new Text2('0', { size: 150, fill: '#3a84f7', font: 'Impact', dropShadow: true, dropShadowColor: '#222a9a', dropShadowBlur: 5, dropShadowDistance: 7, dropShadowAngle: 0 }); scoreText.anchor.set(.5, 0); LK.gui.top.addChild(scoreText); var scoreText2 = new Text2('0', { size: 150, fill: '#ffffff', font: 'Impact' }); scoreText2.anchor.set(.5, 0); scoreText2.x = -4; scoreText2.y = -5; LK.gui.top.addChild(scoreText2); LK.gui.top.addChild(scoreText); // Hearts container var heartsContainer = new Container(); heartsContainer.x = 120; heartsContainer.y = 50; LK.gui.topLeft.addChild(heartsContainer); // Create 4 hearts for 4 lives var hearts = []; for (var h = 0; h < 4; h++) { var heart = LK.getAsset('heart', { anchorX: 0.5, anchorY: 0.5 }); heart.x = h * 100; heart.y = 0; hearts.push(heart); heartsContainer.addChild(heart); } var bird = game.addChild(new Bird()); var leftWall = game.addChild(new Wall()); leftWall.x = 0; leftWall.y = 1366; var rightWall = game.addChild(new Wall()); rightWall.x = 2048; rightWall.y = 1366; var leftObstacles = [], rightObstacles = [], bottomObstacles = []; var bottomObstacleSpawnTime = Math.floor(Math.random() * obstacleSpawnRandomness) + obstacleSpawnRandomness * 2; var obstacleSpawnRandomness = 180; var obstacleSpawnRandomnessDecrease = 0.025 * (2 / 3); var obstacleSpawnY = -500; var leftObstacleSpawnTime = Math.floor(Math.random() * obstacleSpawnRandomness) + obstacleSpawnRandomness; var rightObstacleSpawnTime = Math.floor(Math.random() * obstacleSpawnRandomness) + obstacleSpawnRandomness; var flames = []; var flameSpawnTimer = 0; var flameSpawnInterval = 120; // Spawn flames every 2 seconds bird.x = 1024; bird.y = 1366; game.isMouseDown = false; game.isZeroPressed = false; game.down = function (x, y, obj) { bird.flap(); game.isMouseDown = true; }; game.up = function (x, y, obj) { game.isMouseDown = false; }; game.keydown = function (key) { if (key === '0') { game.isZeroPressed = true; game.score++; LK.setScore(game.score); scoreText.setText(game.score); scoreText2.setText(game.score); } }; game.keyup = function (key) { if (key === '0') { game.isZeroPressed = false; } }; game.update = function () { bird._update_migrated(); if (game.isMouseDown && LK.ticks % 15 === 0) { bird.flap(); } if (game.isZeroPressed && LK.ticks % 10 === 0) { game.score++; LK.setScore(game.score); } if (game.score > 2) { tutorialText.y += 5; tutorialTextWhite.y += 5; } scoreText.setText(game.score); scoreText2.setText(game.score); // Update hearts display based on health var livesRemaining = Math.ceil(game.currentHealth / 25); // 8 hits = 1 life lost, so 25 health per life for (var h = 0; h < hearts.length; h++) { if (h < livesRemaining) { // Heart is active - keep original color hearts[h].alpha = 1; } else { // Heart is lost - make it dark and transparent hearts[h].tint = 0x333333; hearts[h].alpha = 0.3; } } game.obstacleSpeed += game.obstacleSpeedIncrease; // Speed tween system - change speed every 5 seconds (300 ticks at 60fps) if (LK.ticks - game.speedTweenTimer >= 300) { game.speedTweenTimer = LK.ticks; // Set new target speed with visible variation (±30% of base speed) var speedVariation = 0.7 + Math.random() * 0.6; // 0.7x to 1.3x speed multiplier game.targetSpeed = game.baseObstacleSpeed * speedVariation; // Tween to new speed over 1 second tween(game, { currentSpeed: game.targetSpeed }, { duration: 1000, easing: tween.easeInOut }); } // Use current tweened speed for obstacles game.obstacleSpeed = game.currentSpeed; // Rotation speed tween system - change rotation speed every 3 seconds (180 ticks at 60fps) if (LK.ticks - game.rotationSpeedTweenTimer >= 180) { game.rotationSpeedTweenTimer = LK.ticks; // Set new target rotation speed multiplier with visible variation (0.5x to 2.5x speed multiplier) var rotationSpeedVariation = 0.5 + Math.random() * 2; // 0.5x to 2.5x rotation speed multiplier game.targetRotationSpeedMultiplier = rotationSpeedVariation; // Tween to new rotation speed multiplier over 1.5 seconds tween(game, { currentRotationSpeedMultiplier: game.targetRotationSpeedMultiplier }, { duration: 1500, easing: tween.easeInOut }); } obstacleSpawnRandomness -= obstacleSpawnRandomnessDecrease; if (obstacleSpawnRandomness < 20) { obstacleSpawnRandomness = 20; } if (LK.ticks >= leftObstacleSpawnTime) { var newObstacle = game.addChildAt(new Obstacle(), 0); newObstacle.x = Math.random() * (2048 - 600) + 300; // Random x position with margins newObstacle.y = obstacleSpawnY; // Start scaling animation if needed if (newObstacle.shouldScale) { tween(newObstacle.scale, { x: newObstacle.baseScale + newObstacle.scaleRange, y: newObstacle.baseScale + newObstacle.scaleRange }, { duration: 1000 + Math.random() * 1000, easing: tween.easeInOut, onFinish: function onFinish() { tween(newObstacle.scale, { x: newObstacle.baseScale - newObstacle.scaleRange, y: newObstacle.baseScale - newObstacle.scaleRange }, { duration: 1000 + Math.random() * 1000, easing: tween.easeInOut, onFinish: function onFinish() { // Continue scaling loop if (newObstacle.shouldScale) { tween(newObstacle.scale, { x: newObstacle.baseScale + newObstacle.scaleRange, y: newObstacle.baseScale + newObstacle.scaleRange }, { duration: 1000 + Math.random() * 1000, easing: tween.easeInOut }); } } }); } }); } leftObstacles.push(newObstacle); leftObstacleSpawnTime += Math.floor(Math.random() * obstacleSpawnRandomness) + obstacleSpawnRandomness * 1.5; } if (LK.ticks >= rightObstacleSpawnTime) { var newObstacle = game.addChildAt(new Obstacle(), 0); newObstacle.x = Math.random() * (2048 - 600) + 300; // Random x position with margins newObstacle.y = obstacleSpawnY; // Start scaling animation if needed if (newObstacle.shouldScale) { tween(newObstacle.scale, { x: newObstacle.baseScale + newObstacle.scaleRange, y: newObstacle.baseScale + newObstacle.scaleRange }, { duration: 1000 + Math.random() * 1000, easing: tween.easeInOut, onFinish: function onFinish() { tween(newObstacle.scale, { x: newObstacle.baseScale - newObstacle.scaleRange, y: newObstacle.baseScale - newObstacle.scaleRange }, { duration: 1000 + Math.random() * 1000, easing: tween.easeInOut, onFinish: function onFinish() { // Continue scaling loop if (newObstacle.shouldScale) { tween(newObstacle.scale, { x: newObstacle.baseScale + newObstacle.scaleRange, y: newObstacle.baseScale + newObstacle.scaleRange }, { duration: 1000 + Math.random() * 1000, easing: tween.easeInOut }); } } }); } }); } rightObstacles.push(newObstacle); rightObstacleSpawnTime += Math.floor(Math.random() * obstacleSpawnRandomness) + obstacleSpawnRandomness * 1.5; } if (bird.intersects(leftWall) && bird.xSpeed < 0 || bird.intersects(rightWall) && bird.xSpeed > 0) { bird.xSpeed = -bird.xSpeed; bird.flip(); game.score++; LK.setScore(game.score); LK.getSound('bounce').play(); } for (var i = leftObstacles.length - 1; i >= 0; i--) { leftObstacles[i]._move_migrated(game.obstacleSpeed); if (leftObstacles[i].y > 2732 + 200) { // Destroy when off bottom of screen leftObstacles[i].destroy(); leftObstacles.splice(i, 1); } } for (var i = rightObstacles.length - 1; i >= 0; i--) { rightObstacles[i]._move_migrated(game.obstacleSpeed); if (rightObstacles[i].y > 2732 + 200) { // Destroy when off bottom of screen rightObstacles[i].destroy(); rightObstacles.splice(i, 1); } } if (LK.ticks >= bottomObstacleSpawnTime) { var newObstacle = game.addChildAt(new Obstacle(), 0); newObstacle.x = Math.random() * (2048 - 600) + 300; // Random x position with margins newObstacle.y = 2732 + 300; // Start below screen // Start scaling animation if needed if (newObstacle.shouldScale) { tween(newObstacle.scale, { x: newObstacle.baseScale + newObstacle.scaleRange, y: newObstacle.baseScale + newObstacle.scaleRange }, { duration: 1000 + Math.random() * 1000, easing: tween.easeInOut, onFinish: function onFinish() { tween(newObstacle.scale, { x: newObstacle.baseScale - newObstacle.scaleRange, y: newObstacle.baseScale - newObstacle.scaleRange }, { duration: 1000 + Math.random() * 1000, easing: tween.easeInOut, onFinish: function onFinish() { // Continue scaling loop if (newObstacle.shouldScale) { tween(newObstacle.scale, { x: newObstacle.baseScale + newObstacle.scaleRange, y: newObstacle.baseScale + newObstacle.scaleRange }, { duration: 1000 + Math.random() * 1000, easing: tween.easeInOut }); } } }); } }); } bottomObstacles.push(newObstacle); bottomObstacleSpawnTime += Math.floor(Math.random() * obstacleSpawnRandomness) + obstacleSpawnRandomness * 2; } for (var i = bottomObstacles.length - 1; i >= 0; i--) { bottomObstacles[i]._move_migrated(-game.obstacleSpeed); // Move upward (negative speed) if (bottomObstacles[i].y < -200) { // Destroy when off top of screen bottomObstacles[i].destroy(); bottomObstacles.splice(i, 1); } } game.checkObstacleCollision(leftObstacles); game.checkObstacleCollision(rightObstacles); game.checkObstacleCollision(bottomObstacles); // Spawn flames from screen edges flameSpawnTimer++; if (flameSpawnTimer >= flameSpawnInterval) { flameSpawnTimer = 0; // Spawn flame from random edge var spawnSide = Math.floor(Math.random() * 4); // 0=top, 1=right, 2=bottom, 3=left var newFlame = game.addChild(new Flame()); switch (spawnSide) { case 0: // Top newFlame.x = Math.random() * 2048; newFlame.y = -100; newFlame.moveX = (Math.random() - 0.5) * 4; newFlame.moveY = 3; break; case 1: // Right newFlame.x = 2148; newFlame.y = Math.random() * 2732; newFlame.moveX = -3; newFlame.moveY = (Math.random() - 0.5) * 4; break; case 2: // Bottom newFlame.x = Math.random() * 2048; newFlame.y = 2832; newFlame.moveX = (Math.random() - 0.5) * 4; newFlame.moveY = -3; break; case 3: // Left newFlame.x = -100; newFlame.y = Math.random() * 2732; newFlame.moveX = 3; newFlame.moveY = (Math.random() - 0.5) * 4; break; } flames.push(newFlame); } // Update flames and check collision with bird for (var f = flames.length - 1; f >= 0; f--) { var flame = flames[f]; flame._update_migrated(); // Move flame flame.x += flame.moveX; flame.y += flame.moveY; // Check collision with bird var dist = Math.sqrt(Math.pow(bird.x - flame.x, 2) + Math.pow(bird.y - flame.y, 2)); if (dist < 80) { // Bird touched flame - lose 1 life (25 health) game.currentHealth -= 25; LK.getSound('bounce').play(); // Make bird turn red when hit by flame tween(bird, { tint: 0xff0000 }, { duration: 100, onFinish: function onFinish() { tween(bird, { tint: 0xffffff }, { duration: 500 }); } }); // Make hearts turn red when damage is taken for (var h = 0; h < hearts.length; h++) { (function (heart) { tween(heart, { tint: 0xff0000 }, { duration: 100, onFinish: function onFinish() { tween(heart, { tint: 0xffffff }, { duration: 500 }); } }); })(hearts[h]); } flame.destroy(); flames.splice(f, 1); if (game.currentHealth <= 0) { LK.setScore(game.score); LK.getSound('gameOverJingle').play(); LK.showGameOver(); return; } continue; // Skip further checks for this flame } // Remove flames that are off screen if (flame.x < -200 || flame.x > 2248 || flame.y < -200 || flame.y > 2932) { flame.destroy(); flames.splice(f, 1); } } // Check if bird hits bottom of screen and dies if (bird.y >= 2732) { LK.setScore(game.score); LK.getSound('gameOverJingle').play(); LK.showGameOver(); return; } // Bird can now move freely up and down without dying };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Bird = Container.expand(function () {
var self = Container.call(this);
var birdGraphics = self.attachAsset('bird', {
anchorX: 0.5,
anchorY: 0.5
});
self.xSpeed = 10.9375;
self.ySpeed = -20;
self.gravity = 1;
self.lift = -15;
self.flap = function () {
self.ySpeed = self.lift * 1.5;
LK.getSound('flap').play();
};
self._update_migrated = function () {
if (game.isMouseDown) {
self.ySpeed += self.gravity / 3;
} else {
self.ySpeed += self.gravity;
}
self.y += self.ySpeed;
self.x += self.xSpeed;
// Bounce off top and bottom boundaries
if (self.y <= 0) {
self.y = 0;
self.ySpeed = Math.abs(self.ySpeed) * 0.8; // Bounce with some energy loss
tween(self, {
ySpeed: self.ySpeed * 0.5
}, {
duration: 200,
easing: tween.easeOut
});
}
if (self.y >= 2732) {
self.y = 2732;
self.ySpeed = -Math.abs(self.ySpeed) * 0.8; // Bounce with some energy loss
tween(self, {
ySpeed: self.ySpeed * 0.5
}, {
duration: 200,
easing: tween.easeOut
});
}
var targetRotation = Math.atan2(self.ySpeed, self.xSpeed * self.scale.x) / 2;
birdGraphics.rotation += (targetRotation - birdGraphics.rotation) / 10;
};
self.flip = function () {
self.scale.x *= -1;
};
});
var Flame = Container.expand(function () {
var self = Container.call(this);
var flameGraphics = self.attachAsset('flame', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 3;
self.flickerTimer = 0;
self._update_migrated = function () {
// Flickering animation
self.flickerTimer++;
var flicker = Math.sin(self.flickerTimer * 0.3) * 0.3 + 0.7;
flameGraphics.alpha = flicker;
// Color animation between orange and red
var colorLerp = Math.sin(self.flickerTimer * 0.2) * 0.5 + 0.5;
var red = Math.floor(255 * (0.8 + colorLerp * 0.2));
var green = Math.floor(69 * (0.5 + colorLerp * 0.5));
flameGraphics.tint = red << 16 | green << 8 | 0;
// Scale animation
var scale = 0.8 + Math.sin(self.flickerTimer * 0.25) * 0.2;
flameGraphics.scale.set(scale);
};
return self;
});
var Obstacle = Container.expand(function () {
var self = Container.call(this);
var obstacleShadow = self.attachAsset('obstacleShadow', {
anchorX: 0.5,
anchorY: 0.5
});
obstacleShadow.rotation = Math.PI / 4;
var obstacleShadow2 = self.attachAsset('obstacleShadow2', {
anchorX: 0.5,
anchorY: 0.5
});
obstacleShadow2.rotation = Math.PI / 4;
obstacleShadow2.y = -7;
var obstacleGraphics = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5
});
obstacleGraphics.rotation = Math.PI / 4;
self.speed = 5;
// Animation properties
self.shouldRotate = Math.random() < 0.3; // 30% chance to rotate
self.shouldScale = Math.random() < 0.3; // 30% chance to scale
self.rotationSpeed = (Math.random() - 0.5) * 0.05; // Random rotation speed
self.baseScale = 1;
self.scaleRange = 0.3; // Scale variation range
self._move_migrated = function (speed) {
self.y += speed;
// Apply rotation animation with dynamic speed multiplier
if (self.shouldRotate) {
var currentRotationSpeed = self.rotationSpeed * game.currentRotationSpeedMultiplier;
obstacleShadow.rotation += currentRotationSpeed;
obstacleShadow2.rotation += currentRotationSpeed;
obstacleGraphics.rotation += currentRotationSpeed;
}
};
});
var Wall = Container.expand(function () {
var self = Container.call(this);
var wallGraphics = self.attachAsset('wall', {
anchorX: 0.5,
anchorY: 0.5
});
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
var tutorialTextWhite = new Text2('Tap to Flap\nHold to Float', {
size: 150,
fill: '#ffffff',
font: 'Impact',
align: 'center'
});
tutorialTextWhite.anchor.set(.5, 1);
tutorialTextWhite.x = -4;
tutorialTextWhite.y = -62;
LK.gui.bottom.addChild(tutorialTextWhite);
var tutorialText = new Text2('Tap to Flap\nHold to Float', {
size: 150,
fill: '#3a84f7',
font: 'Impact',
dropShadow: true,
dropShadowColor: '#222a9a',
dropShadowBlur: 5,
dropShadowDistance: 7,
dropShadowAngle: 0,
align: 'center'
});
tutorialText.anchor.set(.5, 1);
tutorialText.y = -50;
LK.gui.bottom.addChild(tutorialText);
game.score = 0;
game.obstacleSpeed = 5;
game.obstacleSpeedIncrease = 0.005;
game.maxHealth = 200;
game.currentHealth = 200;
game.speedBoostTimer = 0;
game.speedBoostDuration = 0;
game.isSpeedBoosted = false;
game.baseObstacleSpeed = 5;
game.currentSpeed = 5;
game.targetSpeed = 5;
game.speedTweenTimer = 0;
game.rotationSpeedTweenTimer = 0;
game.targetRotationSpeedMultiplier = 1;
game.currentRotationSpeedMultiplier = 1;
game.checkObstacleCollision = function (obstacles) {
for (var i = 0; i < obstacles.length; i++) {
obstacles[i]._move_migrated();
var dist = Math.sqrt(Math.pow(bird.x - obstacles[i].x, 2) + Math.pow(bird.y - obstacles[i].y, 2));
if (dist < 200) {
game.currentHealth -= 25;
LK.getSound('bounce').play();
// Make bird turn red when hit by obstacle (same as flame effect)
tween(bird, {
tint: 0xff0000
}, {
duration: 100,
onFinish: function onFinish() {
tween(bird, {
tint: 0xffffff
}, {
duration: 500
});
}
});
// Make hearts turn red when damage is taken
for (var h = 0; h < hearts.length; h++) {
(function (heart) {
tween(heart, {
tint: 0xff0000
}, {
duration: 100,
onFinish: function onFinish() {
tween(heart, {
tint: 0xffffff
}, {
duration: 500
});
}
});
})(hearts[h]);
}
obstacles[i].destroy();
obstacles.splice(i, 1);
if (game.currentHealth <= 0) {
LK.setScore(game.score);
LK.getSound('gameOverJingle').play();
LK.showGameOver();
}
break;
}
}
};
game.setBackgroundColor(0xadd8e6);
var scoreText = new Text2('0', {
size: 150,
fill: '#3a84f7',
font: 'Impact',
dropShadow: true,
dropShadowColor: '#222a9a',
dropShadowBlur: 5,
dropShadowDistance: 7,
dropShadowAngle: 0
});
scoreText.anchor.set(.5, 0);
LK.gui.top.addChild(scoreText);
var scoreText2 = new Text2('0', {
size: 150,
fill: '#ffffff',
font: 'Impact'
});
scoreText2.anchor.set(.5, 0);
scoreText2.x = -4;
scoreText2.y = -5;
LK.gui.top.addChild(scoreText2);
LK.gui.top.addChild(scoreText);
// Hearts container
var heartsContainer = new Container();
heartsContainer.x = 120;
heartsContainer.y = 50;
LK.gui.topLeft.addChild(heartsContainer);
// Create 4 hearts for 4 lives
var hearts = [];
for (var h = 0; h < 4; h++) {
var heart = LK.getAsset('heart', {
anchorX: 0.5,
anchorY: 0.5
});
heart.x = h * 100;
heart.y = 0;
hearts.push(heart);
heartsContainer.addChild(heart);
}
var bird = game.addChild(new Bird());
var leftWall = game.addChild(new Wall());
leftWall.x = 0;
leftWall.y = 1366;
var rightWall = game.addChild(new Wall());
rightWall.x = 2048;
rightWall.y = 1366;
var leftObstacles = [],
rightObstacles = [],
bottomObstacles = [];
var bottomObstacleSpawnTime = Math.floor(Math.random() * obstacleSpawnRandomness) + obstacleSpawnRandomness * 2;
var obstacleSpawnRandomness = 180;
var obstacleSpawnRandomnessDecrease = 0.025 * (2 / 3);
var obstacleSpawnY = -500;
var leftObstacleSpawnTime = Math.floor(Math.random() * obstacleSpawnRandomness) + obstacleSpawnRandomness;
var rightObstacleSpawnTime = Math.floor(Math.random() * obstacleSpawnRandomness) + obstacleSpawnRandomness;
var flames = [];
var flameSpawnTimer = 0;
var flameSpawnInterval = 120; // Spawn flames every 2 seconds
bird.x = 1024;
bird.y = 1366;
game.isMouseDown = false;
game.isZeroPressed = false;
game.down = function (x, y, obj) {
bird.flap();
game.isMouseDown = true;
};
game.up = function (x, y, obj) {
game.isMouseDown = false;
};
game.keydown = function (key) {
if (key === '0') {
game.isZeroPressed = true;
game.score++;
LK.setScore(game.score);
scoreText.setText(game.score);
scoreText2.setText(game.score);
}
};
game.keyup = function (key) {
if (key === '0') {
game.isZeroPressed = false;
}
};
game.update = function () {
bird._update_migrated();
if (game.isMouseDown && LK.ticks % 15 === 0) {
bird.flap();
}
if (game.isZeroPressed && LK.ticks % 10 === 0) {
game.score++;
LK.setScore(game.score);
}
if (game.score > 2) {
tutorialText.y += 5;
tutorialTextWhite.y += 5;
}
scoreText.setText(game.score);
scoreText2.setText(game.score);
// Update hearts display based on health
var livesRemaining = Math.ceil(game.currentHealth / 25); // 8 hits = 1 life lost, so 25 health per life
for (var h = 0; h < hearts.length; h++) {
if (h < livesRemaining) {
// Heart is active - keep original color
hearts[h].alpha = 1;
} else {
// Heart is lost - make it dark and transparent
hearts[h].tint = 0x333333;
hearts[h].alpha = 0.3;
}
}
game.obstacleSpeed += game.obstacleSpeedIncrease;
// Speed tween system - change speed every 5 seconds (300 ticks at 60fps)
if (LK.ticks - game.speedTweenTimer >= 300) {
game.speedTweenTimer = LK.ticks;
// Set new target speed with visible variation (±30% of base speed)
var speedVariation = 0.7 + Math.random() * 0.6; // 0.7x to 1.3x speed multiplier
game.targetSpeed = game.baseObstacleSpeed * speedVariation;
// Tween to new speed over 1 second
tween(game, {
currentSpeed: game.targetSpeed
}, {
duration: 1000,
easing: tween.easeInOut
});
}
// Use current tweened speed for obstacles
game.obstacleSpeed = game.currentSpeed;
// Rotation speed tween system - change rotation speed every 3 seconds (180 ticks at 60fps)
if (LK.ticks - game.rotationSpeedTweenTimer >= 180) {
game.rotationSpeedTweenTimer = LK.ticks;
// Set new target rotation speed multiplier with visible variation (0.5x to 2.5x speed multiplier)
var rotationSpeedVariation = 0.5 + Math.random() * 2; // 0.5x to 2.5x rotation speed multiplier
game.targetRotationSpeedMultiplier = rotationSpeedVariation;
// Tween to new rotation speed multiplier over 1.5 seconds
tween(game, {
currentRotationSpeedMultiplier: game.targetRotationSpeedMultiplier
}, {
duration: 1500,
easing: tween.easeInOut
});
}
obstacleSpawnRandomness -= obstacleSpawnRandomnessDecrease;
if (obstacleSpawnRandomness < 20) {
obstacleSpawnRandomness = 20;
}
if (LK.ticks >= leftObstacleSpawnTime) {
var newObstacle = game.addChildAt(new Obstacle(), 0);
newObstacle.x = Math.random() * (2048 - 600) + 300; // Random x position with margins
newObstacle.y = obstacleSpawnY;
// Start scaling animation if needed
if (newObstacle.shouldScale) {
tween(newObstacle.scale, {
x: newObstacle.baseScale + newObstacle.scaleRange,
y: newObstacle.baseScale + newObstacle.scaleRange
}, {
duration: 1000 + Math.random() * 1000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(newObstacle.scale, {
x: newObstacle.baseScale - newObstacle.scaleRange,
y: newObstacle.baseScale - newObstacle.scaleRange
}, {
duration: 1000 + Math.random() * 1000,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Continue scaling loop
if (newObstacle.shouldScale) {
tween(newObstacle.scale, {
x: newObstacle.baseScale + newObstacle.scaleRange,
y: newObstacle.baseScale + newObstacle.scaleRange
}, {
duration: 1000 + Math.random() * 1000,
easing: tween.easeInOut
});
}
}
});
}
});
}
leftObstacles.push(newObstacle);
leftObstacleSpawnTime += Math.floor(Math.random() * obstacleSpawnRandomness) + obstacleSpawnRandomness * 1.5;
}
if (LK.ticks >= rightObstacleSpawnTime) {
var newObstacle = game.addChildAt(new Obstacle(), 0);
newObstacle.x = Math.random() * (2048 - 600) + 300; // Random x position with margins
newObstacle.y = obstacleSpawnY;
// Start scaling animation if needed
if (newObstacle.shouldScale) {
tween(newObstacle.scale, {
x: newObstacle.baseScale + newObstacle.scaleRange,
y: newObstacle.baseScale + newObstacle.scaleRange
}, {
duration: 1000 + Math.random() * 1000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(newObstacle.scale, {
x: newObstacle.baseScale - newObstacle.scaleRange,
y: newObstacle.baseScale - newObstacle.scaleRange
}, {
duration: 1000 + Math.random() * 1000,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Continue scaling loop
if (newObstacle.shouldScale) {
tween(newObstacle.scale, {
x: newObstacle.baseScale + newObstacle.scaleRange,
y: newObstacle.baseScale + newObstacle.scaleRange
}, {
duration: 1000 + Math.random() * 1000,
easing: tween.easeInOut
});
}
}
});
}
});
}
rightObstacles.push(newObstacle);
rightObstacleSpawnTime += Math.floor(Math.random() * obstacleSpawnRandomness) + obstacleSpawnRandomness * 1.5;
}
if (bird.intersects(leftWall) && bird.xSpeed < 0 || bird.intersects(rightWall) && bird.xSpeed > 0) {
bird.xSpeed = -bird.xSpeed;
bird.flip();
game.score++;
LK.setScore(game.score);
LK.getSound('bounce').play();
}
for (var i = leftObstacles.length - 1; i >= 0; i--) {
leftObstacles[i]._move_migrated(game.obstacleSpeed);
if (leftObstacles[i].y > 2732 + 200) {
// Destroy when off bottom of screen
leftObstacles[i].destroy();
leftObstacles.splice(i, 1);
}
}
for (var i = rightObstacles.length - 1; i >= 0; i--) {
rightObstacles[i]._move_migrated(game.obstacleSpeed);
if (rightObstacles[i].y > 2732 + 200) {
// Destroy when off bottom of screen
rightObstacles[i].destroy();
rightObstacles.splice(i, 1);
}
}
if (LK.ticks >= bottomObstacleSpawnTime) {
var newObstacle = game.addChildAt(new Obstacle(), 0);
newObstacle.x = Math.random() * (2048 - 600) + 300; // Random x position with margins
newObstacle.y = 2732 + 300; // Start below screen
// Start scaling animation if needed
if (newObstacle.shouldScale) {
tween(newObstacle.scale, {
x: newObstacle.baseScale + newObstacle.scaleRange,
y: newObstacle.baseScale + newObstacle.scaleRange
}, {
duration: 1000 + Math.random() * 1000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(newObstacle.scale, {
x: newObstacle.baseScale - newObstacle.scaleRange,
y: newObstacle.baseScale - newObstacle.scaleRange
}, {
duration: 1000 + Math.random() * 1000,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Continue scaling loop
if (newObstacle.shouldScale) {
tween(newObstacle.scale, {
x: newObstacle.baseScale + newObstacle.scaleRange,
y: newObstacle.baseScale + newObstacle.scaleRange
}, {
duration: 1000 + Math.random() * 1000,
easing: tween.easeInOut
});
}
}
});
}
});
}
bottomObstacles.push(newObstacle);
bottomObstacleSpawnTime += Math.floor(Math.random() * obstacleSpawnRandomness) + obstacleSpawnRandomness * 2;
}
for (var i = bottomObstacles.length - 1; i >= 0; i--) {
bottomObstacles[i]._move_migrated(-game.obstacleSpeed); // Move upward (negative speed)
if (bottomObstacles[i].y < -200) {
// Destroy when off top of screen
bottomObstacles[i].destroy();
bottomObstacles.splice(i, 1);
}
}
game.checkObstacleCollision(leftObstacles);
game.checkObstacleCollision(rightObstacles);
game.checkObstacleCollision(bottomObstacles);
// Spawn flames from screen edges
flameSpawnTimer++;
if (flameSpawnTimer >= flameSpawnInterval) {
flameSpawnTimer = 0;
// Spawn flame from random edge
var spawnSide = Math.floor(Math.random() * 4); // 0=top, 1=right, 2=bottom, 3=left
var newFlame = game.addChild(new Flame());
switch (spawnSide) {
case 0:
// Top
newFlame.x = Math.random() * 2048;
newFlame.y = -100;
newFlame.moveX = (Math.random() - 0.5) * 4;
newFlame.moveY = 3;
break;
case 1:
// Right
newFlame.x = 2148;
newFlame.y = Math.random() * 2732;
newFlame.moveX = -3;
newFlame.moveY = (Math.random() - 0.5) * 4;
break;
case 2:
// Bottom
newFlame.x = Math.random() * 2048;
newFlame.y = 2832;
newFlame.moveX = (Math.random() - 0.5) * 4;
newFlame.moveY = -3;
break;
case 3:
// Left
newFlame.x = -100;
newFlame.y = Math.random() * 2732;
newFlame.moveX = 3;
newFlame.moveY = (Math.random() - 0.5) * 4;
break;
}
flames.push(newFlame);
}
// Update flames and check collision with bird
for (var f = flames.length - 1; f >= 0; f--) {
var flame = flames[f];
flame._update_migrated();
// Move flame
flame.x += flame.moveX;
flame.y += flame.moveY;
// Check collision with bird
var dist = Math.sqrt(Math.pow(bird.x - flame.x, 2) + Math.pow(bird.y - flame.y, 2));
if (dist < 80) {
// Bird touched flame - lose 1 life (25 health)
game.currentHealth -= 25;
LK.getSound('bounce').play();
// Make bird turn red when hit by flame
tween(bird, {
tint: 0xff0000
}, {
duration: 100,
onFinish: function onFinish() {
tween(bird, {
tint: 0xffffff
}, {
duration: 500
});
}
});
// Make hearts turn red when damage is taken
for (var h = 0; h < hearts.length; h++) {
(function (heart) {
tween(heart, {
tint: 0xff0000
}, {
duration: 100,
onFinish: function onFinish() {
tween(heart, {
tint: 0xffffff
}, {
duration: 500
});
}
});
})(hearts[h]);
}
flame.destroy();
flames.splice(f, 1);
if (game.currentHealth <= 0) {
LK.setScore(game.score);
LK.getSound('gameOverJingle').play();
LK.showGameOver();
return;
}
continue; // Skip further checks for this flame
}
// Remove flames that are off screen
if (flame.x < -200 || flame.x > 2248 || flame.y < -200 || flame.y > 2932) {
flame.destroy();
flames.splice(f, 1);
}
}
// Check if bird hits bottom of screen and dies
if (bird.y >= 2732) {
LK.setScore(game.score);
LK.getSound('gameOverJingle').play();
LK.showGameOver();
return;
}
// Bird can now move freely up and down without dying
};