/**** * 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);
}
}
};