/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Bird = Container.expand(function () {
var self = Container.call(this);
var birdGraphics = self.attachAsset('bird', {
anchorX: 0.5,
anchorY: 0.5
});
self.velocity = 0;
self.gravity = 0.5;
self.flapPower = -10;
self.maxFallSpeed = 10;
self.rotation = 0;
self.flap = function () {
self.velocity = self.flapPower;
LK.getSound('flap').play();
// Animate bird flap
tween(birdGraphics, {
rotation: -0.3
}, {
duration: 150,
easing: tween.easeOut
});
tween(birdGraphics, {
rotation: 0
}, {
duration: 300,
easing: tween.easeInOut
});
};
self.die = function () {
// Remove current bird graphics
birdGraphics.destroy();
// Add dead bird graphics
birdGraphics = self.attachAsset('deadBird', {
anchorX: 0.5,
anchorY: 0.5
});
};
self.reset = function () {
// Remove current bird graphics
birdGraphics.destroy();
// Add normal bird graphics
birdGraphics = self.attachAsset('bird', {
anchorX: 0.5,
anchorY: 0.5
});
};
self.update = function () {
// Only apply physics when game is started and not showing menu or countdown
if (!gameStarted || showingMenu || showingCountdown) {
return;
}
// Apply gravity
self.velocity += self.gravity;
// Limit fall speed
if (self.velocity > self.maxFallSpeed) {
self.velocity = self.maxFallSpeed;
}
// Update position
self.y += self.velocity;
// Rotate bird based on velocity
var targetRotation = Math.min(Math.PI / 4, self.velocity * 0.05);
birdGraphics.rotation = targetRotation;
};
return self;
});
var GameOverScreen = Container.expand(function () {
var self = Container.call(this);
// Semi-transparent overlay
var overlay = self.attachAsset('ground', {
anchorX: 0,
anchorY: 0,
width: 2048,
height: 2732,
alpha: 0.7,
tint: 0x000000
});
overlay.width = 2048;
overlay.height = 2732;
// Game Over text
var gameOverText = new Text2('GAME OVER', {
size: 120,
fill: 0xFF0000
});
gameOverText.anchor.set(0.5, 0.5);
gameOverText.x = 1024;
gameOverText.y = 800;
self.addChild(gameOverText);
// Score text
var scoreText = new Text2('Score: 0', {
size: 80,
fill: 0xFFFFFF
});
scoreText.anchor.set(0.5, 0.5);
scoreText.x = 1024;
scoreText.y = 950;
self.addChild(scoreText);
// High score text
var highScoreText = new Text2('High Score: 0', {
size: 80,
fill: 0xFFD700
});
highScoreText.anchor.set(0.5, 0.5);
highScoreText.x = 1024;
highScoreText.y = 1050;
self.addChild(highScoreText);
// Play Again button
var playAgainButton = self.attachAsset('ground', {
anchorX: 0.5,
anchorY: 0.5,
width: 300,
height: 80,
tint: 0x00AA00
});
playAgainButton.x = 1024;
playAgainButton.y = 1200;
var playAgainText = new Text2('PLAY AGAIN', {
size: 40,
fill: 0xFFFFFF
});
playAgainText.anchor.set(0.5, 0.5);
playAgainText.x = 1024;
playAgainText.y = 1200;
self.addChild(playAgainText);
// Menu button
var menuButton = self.attachAsset('ground', {
anchorX: 0.5,
anchorY: 0.5,
width: 300,
height: 80,
tint: 0x0066CC
});
menuButton.x = 1024;
menuButton.y = 1320;
var menuText = new Text2('MAIN MENU', {
size: 40,
fill: 0xFFFFFF
});
menuText.anchor.set(0.5, 0.5);
menuText.x = 1024;
menuText.y = 1320;
self.addChild(menuText);
self.show = function (currentScore, highScore) {
scoreText.setText('Score: ' + currentScore);
highScoreText.setText('High Score: ' + highScore);
self.visible = true;
};
self.hide = function () {
self.visible = false;
};
// Button handlers
playAgainButton.down = function () {
// Reset game state
gameStarted = false;
isGameOver = false;
showingMenu = true;
bird.velocity = 0;
bird.y = 1366;
bird.reset();
LK.setScore(0);
scoreTxt.setText('0');
if (instructionTxt) instructionTxt.visible = true;
// Clear all pipes
for (var i = pipes.length - 1; i >= 0; i--) {
pipes[i].destroy();
}
pipes = [];
pipeSpawnTimer = 0;
// Reset background to level 0
if (currentBackgroundLevel !== 0) {
currentBackgroundLevel = 0;
background.destroy();
background = game.addChild(LK.getAsset('background1', {
anchorX: 0,
anchorY: 0
}));
background.x = 0;
background.y = 0;
game.setChildIndex(background, 0);
}
// Hide game over screen and show main menu
self.hide();
bird.visible = false; // Hide bird when returning to menu
mainMenu.show();
};
menuButton.down = function () {
// Reset game state (same as play again for now)
gameStarted = false;
isGameOver = false;
showingMenu = true;
bird.velocity = 0;
bird.y = 1366;
bird.reset();
LK.setScore(0);
scoreTxt.setText('0');
if (instructionTxt) instructionTxt.visible = true;
// Clear all pipes
for (var i = pipes.length - 1; i >= 0; i--) {
pipes[i].destroy();
}
pipes = [];
pipeSpawnTimer = 0;
// Reset background to level 0
if (currentBackgroundLevel !== 0) {
currentBackgroundLevel = 0;
background.destroy();
background = game.addChild(LK.getAsset('background1', {
anchorX: 0,
anchorY: 0
}));
background.x = 0;
background.y = 0;
game.setChildIndex(background, 0);
}
// Hide game over screen and show main menu
self.hide();
bird.visible = false; // Hide bird when returning to menu
mainMenu.show();
};
self.visible = false;
return self;
});
var MainMenu = Container.expand(function () {
var self = Container.call(this);
// Game title
var titleText = new Text2('FLAPPY PLANE', {
size: 120,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 800;
self.addChild(titleText);
// Play button
var playButton = self.attachAsset('ground', {
anchorX: 0.5,
anchorY: 0.5,
width: 300,
height: 80,
tint: 0x00AA00
});
playButton.x = 1024;
playButton.y = 1200;
var playText = new Text2('PLAY', {
size: 50,
fill: 0xFFFFFF
});
playText.anchor.set(0.5, 0.5);
playText.x = 1024;
playText.y = 1200;
self.addChild(playText);
self.show = function () {
self.visible = true;
if (instructionTxt) instructionTxt.visible = false; // Hide instruction text when showing menu
};
self.hide = function () {
self.visible = false;
};
// Button handler
playButton.down = function () {
// Start countdown
showingMenu = false;
showingCountdown = true;
countdownValue = 3;
countdownTimer = 0;
countdownTxt.setText('3');
countdownTxt.visible = true;
bird.visible = true; // Show bird when starting countdown
self.hide();
};
return self;
});
var Pipe = Container.expand(function () {
var self = Container.call(this);
self.speed = -4;
self.gapSize = 400;
self.passed = false;
var topPipe = self.attachAsset('pipeTop', {
anchorX: 0.5,
anchorY: 1
});
var bottomPipe = self.attachAsset('pipeBottom', {
anchorX: 0.5,
anchorY: 0
});
self.setGapPosition = function (gapY) {
// Position top pipe so it extends from screen top (0) to gap
// Top pipe anchored at bottom (anchorY: 1), so y position is where bottom edge should be
topPipe.y = gapY - self.gapSize / 2;
// Position bottom pipe so it extends from gap to screen bottom (2732)
// Bottom pipe anchored at top (anchorY: 0), so y position is where top edge should be
bottomPipe.y = gapY + self.gapSize / 2;
};
self.update = function () {
self.x += self.speed;
};
self.checkCollision = function (bird) {
var birdBounds = {
x: bird.x - 30,
y: bird.y - 30,
width: 60,
height: 60
};
var topPipeBounds = {
x: self.x - 60,
y: topPipe.y - 2732,
width: 120,
height: 2732
};
var bottomPipeBounds = {
x: self.x - 60,
y: bottomPipe.y,
width: 120,
height: 2732
};
return self.intersectsBounds(birdBounds, topPipeBounds) || self.intersectsBounds(birdBounds, bottomPipeBounds);
};
self.intersectsBounds = function (a, b) {
return a.x < b.x + b.width && a.x + a.width > b.x && a.y < b.y + b.height && a.y + a.height > b.y;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87CEEB
});
/****
* Game Code
****/
// Game state
var gameStarted = false;
var bird;
var pipes = [];
var ground;
var pipeSpawnTimer = 0;
var pipeSpawnDelay = 120; // 2 seconds at 60fps
var currentBackgroundLevel = 0;
var background;
var gameOverScreen;
var isGameOver = false;
var mainMenu;
var showingMenu = true;
var showingCountdown = false;
var countdownValue = 3;
var countdownTimer = 0;
// Create background
background = game.addChild(LK.getAsset('background1', {
anchorX: 0,
anchorY: 0
}));
background.x = 0;
background.y = 0;
// Create bird
bird = game.addChild(new Bird());
bird.x = 400;
bird.y = 1366; // Center of screen
bird.visible = false; // Hide bird initially when showing menu
// Create ground
ground = game.addChild(LK.getAsset('ground', {
anchorX: 0,
anchorY: 0
}));
ground.x = 0;
ground.y = 2732 - 100;
// Create custom game over screen
gameOverScreen = game.addChild(new GameOverScreen());
// Create main menu
mainMenu = game.addChild(new MainMenu());
mainMenu.show();
// Set initial score to 0 for normal gameplay
LK.setScore(0);
// Create score display
var scoreTxt = new Text2('0', {
size: 120,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
scoreTxt.y = 150;
// Create instruction text
var instructionTxt = new Text2('TAP TO FLY', {
size: 80,
fill: 0xFFFFFF
});
instructionTxt.anchor.set(0.5, 0.5);
game.addChild(instructionTxt);
instructionTxt.x = 1024;
instructionTxt.y = 800;
instructionTxt.visible = false; // Hide instruction text when showing menu
// Create countdown text
var countdownTxt = new Text2('3', {
size: 200,
fill: 0xFFFFFF
});
countdownTxt.anchor.set(0.5, 0.5);
game.addChild(countdownTxt);
countdownTxt.x = 1024;
countdownTxt.y = 1366;
countdownTxt.visible = false;
// Spawn pipe function
function spawnPipe() {
var pipe = new Pipe();
var minY = 600;
var maxY = 2132;
var totalRange = maxY - minY;
var currentScore = LK.getScore();
var randomnessMultiplier;
// Adjust randomness based on score
if (currentScore < 50) {
// Early game: 4-5% less extreme (reduce range by ~20-25%)
randomnessMultiplier = 0.75;
} else {
// Late game (50+): 2-3% more extreme but 1% easier to prevent impossible pipes
randomnessMultiplier = 1.14;
}
;
var adjustedRange = totalRange * randomnessMultiplier;
var rangeOffset = (totalRange - adjustedRange) / 2;
var adjustedMinY = minY + rangeOffset;
var adjustedMaxY = maxY - rangeOffset;
var gapY = adjustedMinY + Math.random() * adjustedRange;
pipe.setGapPosition(gapY);
pipe.x = 2048 + 60;
pipes.push(pipe);
game.addChild(pipe);
}
// Game input handling
game.down = function (x, y, obj) {
if (showingMenu || showingCountdown) return; // Don't allow input when menu or countdown is showing
if (gameStarted && !isGameOver) {
bird.flap();
}
};
// Main game loop
game.update = function () {
// Handle countdown
if (showingCountdown) {
countdownTimer++;
if (countdownTimer >= 60) {
// 1 second at 60fps
countdownTimer = 0;
countdownValue--;
if (countdownValue > 0) {
countdownTxt.setText(countdownValue.toString());
tween(countdownTxt, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 200,
easing: tween.easeOut
});
tween(countdownTxt, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 300,
easing: tween.easeIn
});
} else {
// Countdown finished, start game
showingCountdown = false;
gameStarted = true;
countdownTxt.visible = false;
if (instructionTxt) instructionTxt.visible = true;
spawnPipe();
}
}
return;
}
if (showingMenu || !gameStarted || isGameOver) return;
// Update bird
bird.update();
// Check bird boundaries
if (bird.y <= 0 || bird.y >= ground.y - 30) {
// Hit ceiling or ground
bird.die();
// Stop bird at ground level and prevent going behind ground
if (bird.y >= ground.y - 30) {
bird.y = ground.y - 30;
bird.velocity = 0;
}
// Save high score
var currentScore = LK.getScore();
var highScore = storage.highScore || 0;
if (currentScore > highScore) {
storage.highScore = currentScore;
highScore = currentScore; // Update local highScore variable
}
LK.getSound('hit').play();
gameOverScreen.show(currentScore, highScore);
isGameOver = true;
return;
}
// Additional check to ensure bird never goes behind ground even during normal flight
if (bird.y > ground.y - 30) {
bird.y = ground.y - 30;
bird.velocity = 0;
}
// Spawn pipes
pipeSpawnTimer++;
if (pipeSpawnTimer >= pipeSpawnDelay) {
spawnPipe();
pipeSpawnTimer = 0;
}
// Update pipes
for (var i = pipes.length - 1; i >= 0; i--) {
var pipe = pipes[i];
pipe.update();
// Check collision
if (pipe.checkCollision(bird)) {
bird.die();
// Save high score
var currentScore = LK.getScore();
var highScore = storage.highScore || 0;
if (currentScore > highScore) {
storage.highScore = currentScore;
highScore = currentScore; // Update local highScore variable
}
LK.getSound('hit').play();
gameOverScreen.show(currentScore, highScore);
isGameOver = true;
return;
}
// Check if bird passed pipe
if (!pipe.passed && bird.x > pipe.x) {
pipe.passed = true;
LK.setScore(LK.getScore() + 1);
scoreTxt.setText(LK.getScore());
LK.getSound('check').play();
// Change background every 20 points
var newBackgroundLevel = Math.floor(LK.getScore() / 20) % 5;
if (newBackgroundLevel !== currentBackgroundLevel) {
currentBackgroundLevel = newBackgroundLevel;
var backgroundAssets = ['background1', 'background2', 'background3', 'background4', 'background5'];
background.destroy();
background = game.addChild(LK.getAsset(backgroundAssets[currentBackgroundLevel], {
anchorX: 0,
anchorY: 0
}));
background.x = 0;
background.y = 0;
// Move background to back
game.setChildIndex(background, 0);
}
}
// Remove pipes that are off screen
if (pipe.x < -120) {
pipe.destroy();
pipes.splice(i, 1);
}
}
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Bird = Container.expand(function () {
var self = Container.call(this);
var birdGraphics = self.attachAsset('bird', {
anchorX: 0.5,
anchorY: 0.5
});
self.velocity = 0;
self.gravity = 0.5;
self.flapPower = -10;
self.maxFallSpeed = 10;
self.rotation = 0;
self.flap = function () {
self.velocity = self.flapPower;
LK.getSound('flap').play();
// Animate bird flap
tween(birdGraphics, {
rotation: -0.3
}, {
duration: 150,
easing: tween.easeOut
});
tween(birdGraphics, {
rotation: 0
}, {
duration: 300,
easing: tween.easeInOut
});
};
self.die = function () {
// Remove current bird graphics
birdGraphics.destroy();
// Add dead bird graphics
birdGraphics = self.attachAsset('deadBird', {
anchorX: 0.5,
anchorY: 0.5
});
};
self.reset = function () {
// Remove current bird graphics
birdGraphics.destroy();
// Add normal bird graphics
birdGraphics = self.attachAsset('bird', {
anchorX: 0.5,
anchorY: 0.5
});
};
self.update = function () {
// Only apply physics when game is started and not showing menu or countdown
if (!gameStarted || showingMenu || showingCountdown) {
return;
}
// Apply gravity
self.velocity += self.gravity;
// Limit fall speed
if (self.velocity > self.maxFallSpeed) {
self.velocity = self.maxFallSpeed;
}
// Update position
self.y += self.velocity;
// Rotate bird based on velocity
var targetRotation = Math.min(Math.PI / 4, self.velocity * 0.05);
birdGraphics.rotation = targetRotation;
};
return self;
});
var GameOverScreen = Container.expand(function () {
var self = Container.call(this);
// Semi-transparent overlay
var overlay = self.attachAsset('ground', {
anchorX: 0,
anchorY: 0,
width: 2048,
height: 2732,
alpha: 0.7,
tint: 0x000000
});
overlay.width = 2048;
overlay.height = 2732;
// Game Over text
var gameOverText = new Text2('GAME OVER', {
size: 120,
fill: 0xFF0000
});
gameOverText.anchor.set(0.5, 0.5);
gameOverText.x = 1024;
gameOverText.y = 800;
self.addChild(gameOverText);
// Score text
var scoreText = new Text2('Score: 0', {
size: 80,
fill: 0xFFFFFF
});
scoreText.anchor.set(0.5, 0.5);
scoreText.x = 1024;
scoreText.y = 950;
self.addChild(scoreText);
// High score text
var highScoreText = new Text2('High Score: 0', {
size: 80,
fill: 0xFFD700
});
highScoreText.anchor.set(0.5, 0.5);
highScoreText.x = 1024;
highScoreText.y = 1050;
self.addChild(highScoreText);
// Play Again button
var playAgainButton = self.attachAsset('ground', {
anchorX: 0.5,
anchorY: 0.5,
width: 300,
height: 80,
tint: 0x00AA00
});
playAgainButton.x = 1024;
playAgainButton.y = 1200;
var playAgainText = new Text2('PLAY AGAIN', {
size: 40,
fill: 0xFFFFFF
});
playAgainText.anchor.set(0.5, 0.5);
playAgainText.x = 1024;
playAgainText.y = 1200;
self.addChild(playAgainText);
// Menu button
var menuButton = self.attachAsset('ground', {
anchorX: 0.5,
anchorY: 0.5,
width: 300,
height: 80,
tint: 0x0066CC
});
menuButton.x = 1024;
menuButton.y = 1320;
var menuText = new Text2('MAIN MENU', {
size: 40,
fill: 0xFFFFFF
});
menuText.anchor.set(0.5, 0.5);
menuText.x = 1024;
menuText.y = 1320;
self.addChild(menuText);
self.show = function (currentScore, highScore) {
scoreText.setText('Score: ' + currentScore);
highScoreText.setText('High Score: ' + highScore);
self.visible = true;
};
self.hide = function () {
self.visible = false;
};
// Button handlers
playAgainButton.down = function () {
// Reset game state
gameStarted = false;
isGameOver = false;
showingMenu = true;
bird.velocity = 0;
bird.y = 1366;
bird.reset();
LK.setScore(0);
scoreTxt.setText('0');
if (instructionTxt) instructionTxt.visible = true;
// Clear all pipes
for (var i = pipes.length - 1; i >= 0; i--) {
pipes[i].destroy();
}
pipes = [];
pipeSpawnTimer = 0;
// Reset background to level 0
if (currentBackgroundLevel !== 0) {
currentBackgroundLevel = 0;
background.destroy();
background = game.addChild(LK.getAsset('background1', {
anchorX: 0,
anchorY: 0
}));
background.x = 0;
background.y = 0;
game.setChildIndex(background, 0);
}
// Hide game over screen and show main menu
self.hide();
bird.visible = false; // Hide bird when returning to menu
mainMenu.show();
};
menuButton.down = function () {
// Reset game state (same as play again for now)
gameStarted = false;
isGameOver = false;
showingMenu = true;
bird.velocity = 0;
bird.y = 1366;
bird.reset();
LK.setScore(0);
scoreTxt.setText('0');
if (instructionTxt) instructionTxt.visible = true;
// Clear all pipes
for (var i = pipes.length - 1; i >= 0; i--) {
pipes[i].destroy();
}
pipes = [];
pipeSpawnTimer = 0;
// Reset background to level 0
if (currentBackgroundLevel !== 0) {
currentBackgroundLevel = 0;
background.destroy();
background = game.addChild(LK.getAsset('background1', {
anchorX: 0,
anchorY: 0
}));
background.x = 0;
background.y = 0;
game.setChildIndex(background, 0);
}
// Hide game over screen and show main menu
self.hide();
bird.visible = false; // Hide bird when returning to menu
mainMenu.show();
};
self.visible = false;
return self;
});
var MainMenu = Container.expand(function () {
var self = Container.call(this);
// Game title
var titleText = new Text2('FLAPPY PLANE', {
size: 120,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 800;
self.addChild(titleText);
// Play button
var playButton = self.attachAsset('ground', {
anchorX: 0.5,
anchorY: 0.5,
width: 300,
height: 80,
tint: 0x00AA00
});
playButton.x = 1024;
playButton.y = 1200;
var playText = new Text2('PLAY', {
size: 50,
fill: 0xFFFFFF
});
playText.anchor.set(0.5, 0.5);
playText.x = 1024;
playText.y = 1200;
self.addChild(playText);
self.show = function () {
self.visible = true;
if (instructionTxt) instructionTxt.visible = false; // Hide instruction text when showing menu
};
self.hide = function () {
self.visible = false;
};
// Button handler
playButton.down = function () {
// Start countdown
showingMenu = false;
showingCountdown = true;
countdownValue = 3;
countdownTimer = 0;
countdownTxt.setText('3');
countdownTxt.visible = true;
bird.visible = true; // Show bird when starting countdown
self.hide();
};
return self;
});
var Pipe = Container.expand(function () {
var self = Container.call(this);
self.speed = -4;
self.gapSize = 400;
self.passed = false;
var topPipe = self.attachAsset('pipeTop', {
anchorX: 0.5,
anchorY: 1
});
var bottomPipe = self.attachAsset('pipeBottom', {
anchorX: 0.5,
anchorY: 0
});
self.setGapPosition = function (gapY) {
// Position top pipe so it extends from screen top (0) to gap
// Top pipe anchored at bottom (anchorY: 1), so y position is where bottom edge should be
topPipe.y = gapY - self.gapSize / 2;
// Position bottom pipe so it extends from gap to screen bottom (2732)
// Bottom pipe anchored at top (anchorY: 0), so y position is where top edge should be
bottomPipe.y = gapY + self.gapSize / 2;
};
self.update = function () {
self.x += self.speed;
};
self.checkCollision = function (bird) {
var birdBounds = {
x: bird.x - 30,
y: bird.y - 30,
width: 60,
height: 60
};
var topPipeBounds = {
x: self.x - 60,
y: topPipe.y - 2732,
width: 120,
height: 2732
};
var bottomPipeBounds = {
x: self.x - 60,
y: bottomPipe.y,
width: 120,
height: 2732
};
return self.intersectsBounds(birdBounds, topPipeBounds) || self.intersectsBounds(birdBounds, bottomPipeBounds);
};
self.intersectsBounds = function (a, b) {
return a.x < b.x + b.width && a.x + a.width > b.x && a.y < b.y + b.height && a.y + a.height > b.y;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87CEEB
});
/****
* Game Code
****/
// Game state
var gameStarted = false;
var bird;
var pipes = [];
var ground;
var pipeSpawnTimer = 0;
var pipeSpawnDelay = 120; // 2 seconds at 60fps
var currentBackgroundLevel = 0;
var background;
var gameOverScreen;
var isGameOver = false;
var mainMenu;
var showingMenu = true;
var showingCountdown = false;
var countdownValue = 3;
var countdownTimer = 0;
// Create background
background = game.addChild(LK.getAsset('background1', {
anchorX: 0,
anchorY: 0
}));
background.x = 0;
background.y = 0;
// Create bird
bird = game.addChild(new Bird());
bird.x = 400;
bird.y = 1366; // Center of screen
bird.visible = false; // Hide bird initially when showing menu
// Create ground
ground = game.addChild(LK.getAsset('ground', {
anchorX: 0,
anchorY: 0
}));
ground.x = 0;
ground.y = 2732 - 100;
// Create custom game over screen
gameOverScreen = game.addChild(new GameOverScreen());
// Create main menu
mainMenu = game.addChild(new MainMenu());
mainMenu.show();
// Set initial score to 0 for normal gameplay
LK.setScore(0);
// Create score display
var scoreTxt = new Text2('0', {
size: 120,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
scoreTxt.y = 150;
// Create instruction text
var instructionTxt = new Text2('TAP TO FLY', {
size: 80,
fill: 0xFFFFFF
});
instructionTxt.anchor.set(0.5, 0.5);
game.addChild(instructionTxt);
instructionTxt.x = 1024;
instructionTxt.y = 800;
instructionTxt.visible = false; // Hide instruction text when showing menu
// Create countdown text
var countdownTxt = new Text2('3', {
size: 200,
fill: 0xFFFFFF
});
countdownTxt.anchor.set(0.5, 0.5);
game.addChild(countdownTxt);
countdownTxt.x = 1024;
countdownTxt.y = 1366;
countdownTxt.visible = false;
// Spawn pipe function
function spawnPipe() {
var pipe = new Pipe();
var minY = 600;
var maxY = 2132;
var totalRange = maxY - minY;
var currentScore = LK.getScore();
var randomnessMultiplier;
// Adjust randomness based on score
if (currentScore < 50) {
// Early game: 4-5% less extreme (reduce range by ~20-25%)
randomnessMultiplier = 0.75;
} else {
// Late game (50+): 2-3% more extreme but 1% easier to prevent impossible pipes
randomnessMultiplier = 1.14;
}
;
var adjustedRange = totalRange * randomnessMultiplier;
var rangeOffset = (totalRange - adjustedRange) / 2;
var adjustedMinY = minY + rangeOffset;
var adjustedMaxY = maxY - rangeOffset;
var gapY = adjustedMinY + Math.random() * adjustedRange;
pipe.setGapPosition(gapY);
pipe.x = 2048 + 60;
pipes.push(pipe);
game.addChild(pipe);
}
// Game input handling
game.down = function (x, y, obj) {
if (showingMenu || showingCountdown) return; // Don't allow input when menu or countdown is showing
if (gameStarted && !isGameOver) {
bird.flap();
}
};
// Main game loop
game.update = function () {
// Handle countdown
if (showingCountdown) {
countdownTimer++;
if (countdownTimer >= 60) {
// 1 second at 60fps
countdownTimer = 0;
countdownValue--;
if (countdownValue > 0) {
countdownTxt.setText(countdownValue.toString());
tween(countdownTxt, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 200,
easing: tween.easeOut
});
tween(countdownTxt, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 300,
easing: tween.easeIn
});
} else {
// Countdown finished, start game
showingCountdown = false;
gameStarted = true;
countdownTxt.visible = false;
if (instructionTxt) instructionTxt.visible = true;
spawnPipe();
}
}
return;
}
if (showingMenu || !gameStarted || isGameOver) return;
// Update bird
bird.update();
// Check bird boundaries
if (bird.y <= 0 || bird.y >= ground.y - 30) {
// Hit ceiling or ground
bird.die();
// Stop bird at ground level and prevent going behind ground
if (bird.y >= ground.y - 30) {
bird.y = ground.y - 30;
bird.velocity = 0;
}
// Save high score
var currentScore = LK.getScore();
var highScore = storage.highScore || 0;
if (currentScore > highScore) {
storage.highScore = currentScore;
highScore = currentScore; // Update local highScore variable
}
LK.getSound('hit').play();
gameOverScreen.show(currentScore, highScore);
isGameOver = true;
return;
}
// Additional check to ensure bird never goes behind ground even during normal flight
if (bird.y > ground.y - 30) {
bird.y = ground.y - 30;
bird.velocity = 0;
}
// Spawn pipes
pipeSpawnTimer++;
if (pipeSpawnTimer >= pipeSpawnDelay) {
spawnPipe();
pipeSpawnTimer = 0;
}
// Update pipes
for (var i = pipes.length - 1; i >= 0; i--) {
var pipe = pipes[i];
pipe.update();
// Check collision
if (pipe.checkCollision(bird)) {
bird.die();
// Save high score
var currentScore = LK.getScore();
var highScore = storage.highScore || 0;
if (currentScore > highScore) {
storage.highScore = currentScore;
highScore = currentScore; // Update local highScore variable
}
LK.getSound('hit').play();
gameOverScreen.show(currentScore, highScore);
isGameOver = true;
return;
}
// Check if bird passed pipe
if (!pipe.passed && bird.x > pipe.x) {
pipe.passed = true;
LK.setScore(LK.getScore() + 1);
scoreTxt.setText(LK.getScore());
LK.getSound('check').play();
// Change background every 20 points
var newBackgroundLevel = Math.floor(LK.getScore() / 20) % 5;
if (newBackgroundLevel !== currentBackgroundLevel) {
currentBackgroundLevel = newBackgroundLevel;
var backgroundAssets = ['background1', 'background2', 'background3', 'background4', 'background5'];
background.destroy();
background = game.addChild(LK.getAsset(backgroundAssets[currentBackgroundLevel], {
anchorX: 0,
anchorY: 0
}));
background.x = 0;
background.y = 0;
// Move background to back
game.setChildIndex(background, 0);
}
}
// Remove pipes that are off screen
if (pipe.x < -120) {
pipe.destroy();
pipes.splice(i, 1);
}
}
};