/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var facekit = LK.import("@upit/facekit.v1"); /**** * Classes ****/ // Fast obstacle (normal size, very fast) var FastObstacle = Container.expand(function () { var self = Container.call(this); // Tint to distinguish var asset = self.attachAsset('obstacle', { anchorX: 0.5, anchorY: 0.5 }); asset.tint = 0x3498db; // Add update method self.update = function () { self.x += self.vx; self.y += self.vy; }; return self; }); // Golden Bird Powerup class var GoldenBirdPowerup = Container.expand(function () { var self = Container.call(this); // Use a separate golden bird asset for the powerup var asset = self.attachAsset('goldenbird', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.1, scaleY: 1.1 }); self.update = function () { // Powerup falls down self.y += self.vy; }; return self; }); // Large obstacle (slower, bigger) var LargeObstacle = Container.expand(function () { var self = Container.call(this); // Override asset var asset = self.attachAsset('obstacle', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 1.5 }); // Add update method self.update = function () { self.x += self.vx; self.y += self.vy; }; return self; }); // Obstacle base class var Obstacle = Container.expand(function () { var self = Container.call(this); // Default asset, can be overridden by subclasses var obstacleAsset = self.attachAsset('obstacle', { anchorX: 0.5, anchorY: 0.5 }); self.vx = 0; self.vy = 0; self.update = function () { self.x += self.vx; self.y += self.vy; }; return self; }); // Player class var Player = Container.expand(function () { var self = Container.call(this); self.playerAsset = self.attachAsset('player', { anchorX: 0.5, anchorY: 0.5 }); self.goldenAsset = null; // No update needed; position is set by facekit in main game loop return self; }); // Small obstacle (faster, smaller) var SmallObstacle = Container.expand(function () { var self = Container.call(this); // Override asset var asset = self.attachAsset('obstacle', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.7, scaleY: 0.7 }); // Add update method self.update = function () { self.x += self.vx; self.y += self.vy; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x181818 }); /**** * Game Code ****/ // Obstacle: red box // Player avatar: green ellipse // Score display var scoreTxt = new Text2('0', { size: 120, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // Player instance var player = new Player(); game.addChild(player); // Start at center player.x = 2048 / 2; player.y = 2732 / 2; // --- Start Screen Overlay --- var startOverlay = new Container(); var overlayBg = LK.getAsset('obstacle', { anchorX: 0.5, anchorY: 0.5, scaleX: 10, scaleY: 10 }); overlayBg.alpha = 0.85; overlayBg.tint = 0x222222; startOverlay.addChild(overlayBg); var titleText = new Text2("Golden Bird Dodge", { size: 160, fill: 0xFFD700 }); titleText.anchor.set(0.5, 0.5); titleText.x = 2048 / 2; titleText.y = 900; startOverlay.addChild(titleText); var subtitleText = new Text2("Move your head to control!\nDodge obstacles, grab the golden bird!", { size: 70, fill: 0xFFFFFF }); subtitleText.anchor.set(0.5, 0.5); subtitleText.x = 2048 / 2; subtitleText.y = 1200; startOverlay.addChild(subtitleText); var playBtn = new Text2("PLAY", { size: 140, fill: 0x00FF88 }); playBtn.anchor.set(0.5, 0.5); playBtn.x = 2048 / 2; playBtn.y = 1700; playBtn.interactive = true; playBtn.buttonMode = true; startOverlay.addChild(playBtn); // Center overlay background overlayBg.x = 2048 / 2; overlayBg.y = 2732 / 2; // Add overlay to GUI (so it's always on top) LK.gui.center.addChild(startOverlay); // Pause game logic until play is pressed var gameStarted = false; game.update = function () { if (!gameStarted) return; if (isGameOver) return; // Facekit: update player position // Use noseTip if available, else mouthCenter var fx = facekit.noseTip && facekit.noseTip.x ? facekit.noseTip.x : facekit.mouthCenter.x; var fy = facekit.noseTip && facekit.noseTip.y ? facekit.noseTip.y : facekit.mouthCenter.y; // Clamp to keep player inside screen (with margin for player size) var margin = player.width / 2 + 10; player.x = clamp(fx, margin, 2048 - margin); player.y = clamp(fy, margin, 2732 - margin); // Update obstacles for (var i = obstacles.length - 1; i >= 0; i--) { var obs = obstacles[i]; obs.update(); // Remove if out of bounds if (obs.x < -200 || obs.x > 2048 + 200 || obs.y < -200 || obs.y > 2732 + 200) { obs.destroy(); obstacles.splice(i, 1); continue; } // Collision detection if (obs.intersects(player)) { if (!isImmune) { // Game over isGameOver = true; LK.effects.flashScreen(0xff0000, 800); LK.showGameOver(); return; } else { // If immune, just destroy the obstacle obs.destroy(); obstacles.splice(i, 1); continue; } } } // Score: survive longer = higher score if (LK.ticks % 6 === 0) { // ~10 times per second setScore(LK.getScore() + 1); } // Spawn obstacles at shorter intervals, increase frequency more aggressively as score increases var spawnInterval = 50 - Math.min(35, Math.floor(LK.getScore() / 20) * 5); // Much faster as score increases if (LK.ticks % spawnInterval === 0) { spawnObstacle(); } // Spawn golden bird powerup every 600-900 ticks, if not already present if (LK.ticks % (600 + Math.floor(Math.random() * 300)) === 0 && powerups.length === 0 && !isImmune) { var powerup = new GoldenBirdPowerup(); powerup.x = 200 + Math.random() * (2048 - 400); powerup.y = -80; powerup.vy = 7 + Math.random() * 3; powerups.push(powerup); game.addChild(powerup); } // Update powerups for (var pi = powerups.length - 1; pi >= 0; pi--) { var pup = powerups[pi]; pup.update(); // Remove if out of bounds if (pup.y > 2732 + 200) { pup.destroy(); powerups.splice(pi, 1); continue; } // Collision with player if (pup.intersects(player)) { isImmune = true; immuneTicks = 600; // 10 seconds at 60fps pup.destroy(); powerups.splice(pi, 1); // Flash player gold LK.effects.flashObject(player, 0xFFD700, 800); // Change player to golden bird asset if (player.goldenAsset) { player.removeChild(player.goldenAsset); player.goldenAsset.destroy(); player.goldenAsset = null; } if (player.playerAsset) { player.playerAsset.visible = false; } player.goldenAsset = player.attachAsset('goldenbird', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.1, scaleY: 1.1 }); } } // Handle immunity timer if (isImmune) { immuneTicks--; // Flicker player to show immunity if (LK.ticks % 20 < 10) { player.alpha = 0.6; } else { player.alpha = 1.0; } if (immuneTicks <= 0) { isImmune = false; player.alpha = 1.0; // Restore player asset and remove golden bird asset if (player.goldenAsset) { player.removeChild(player.goldenAsset); player.goldenAsset.destroy(); player.goldenAsset = null; } if (player.playerAsset) { player.playerAsset.visible = true; } } } }; // Play button handler playBtn.down = function (x, y, obj) { if (gameStarted) return; gameStarted = true; if (startOverlay.parent) { startOverlay.parent.removeChild(startOverlay); } }; // Obstacles array var obstacles = []; // Powerup array var powerups = []; // Game state var isGameOver = false; var lastScore = 0; // Immunity state var isImmune = false; var immuneTicks = 0; // Helper: Clamp value between min and max function clamp(val, min, max) { if (val < min) return min; if (val > max) return max; return val; } // Helper: Spawn a batch of new obstacles from random edges, with higher speed function spawnObstacle() { // Spawn 1-2 obstacles per interval for less density var numToSpawn = 1 + Math.floor(Math.random() * 2); // 1-2 for (var n = 0; n < numToSpawn; n++) { // Randomly choose obstacle type var typeRand = Math.random(); var obs, speed; var x = 200 + Math.random() * (2048 - 400); var y = -80; if (typeRand < 0.2) { // 20% Large, slow (slower than before) obs = new LargeObstacle(); speed = 5 + Math.random() * 2; // 5-7 px/frame } else if (typeRand < 0.5) { // 30% Small, fast (but slower than before) obs = new SmallObstacle(); speed = 8 + Math.random() * 4; // 8-12 px/frame } else if (typeRand < 0.7) { // 20% Fast, normal size, blue (but slower than before) obs = new FastObstacle(); speed = 10 + Math.random() * 4; // 10-14 px/frame } else { // 30% Normal (slower than before) obs = new Obstacle(); speed = 7 + Math.random() * 5; // 7-12 px/frame } obs.x = x; obs.y = y; obs.vx = 0; obs.vy = speed; obstacles.push(obs); game.addChild(obs); } } // Score logic function setScore(val) { LK.setScore(val); scoreTxt.setText(val); } // Main update loop game.update = function () { if (isGameOver) return; // Facekit: update player position // Use noseTip if available, else mouthCenter var fx = facekit.noseTip && facekit.noseTip.x ? facekit.noseTip.x : facekit.mouthCenter.x; var fy = facekit.noseTip && facekit.noseTip.y ? facekit.noseTip.y : facekit.mouthCenter.y; // Clamp to keep player inside screen (with margin for player size) var margin = player.width / 2 + 10; player.x = clamp(fx, margin, 2048 - margin); player.y = clamp(fy, margin, 2732 - margin); // Update obstacles for (var i = obstacles.length - 1; i >= 0; i--) { var obs = obstacles[i]; obs.update(); // Remove if out of bounds if (obs.x < -200 || obs.x > 2048 + 200 || obs.y < -200 || obs.y > 2732 + 200) { obs.destroy(); obstacles.splice(i, 1); continue; } // Collision detection if (obs.intersects(player)) { if (!isImmune) { // Game over isGameOver = true; LK.effects.flashScreen(0xff0000, 800); LK.showGameOver(); return; } else { // If immune, just destroy the obstacle obs.destroy(); obstacles.splice(i, 1); continue; } } } // Score: survive longer = higher score if (LK.ticks % 6 === 0) { // ~10 times per second setScore(LK.getScore() + 1); } // Spawn obstacles at shorter intervals, increase frequency more aggressively as score increases var spawnInterval = 50 - Math.min(35, Math.floor(LK.getScore() / 20) * 5); // Much faster as score increases if (LK.ticks % spawnInterval === 0) { spawnObstacle(); } // Spawn golden bird powerup every 600-900 ticks, if not already present if (LK.ticks % (600 + Math.floor(Math.random() * 300)) === 0 && powerups.length === 0 && !isImmune) { var powerup = new GoldenBirdPowerup(); powerup.x = 200 + Math.random() * (2048 - 400); powerup.y = -80; powerup.vy = 7 + Math.random() * 3; powerups.push(powerup); game.addChild(powerup); } // Update powerups for (var pi = powerups.length - 1; pi >= 0; pi--) { var pup = powerups[pi]; pup.update(); // Remove if out of bounds if (pup.y > 2732 + 200) { pup.destroy(); powerups.splice(pi, 1); continue; } // Collision with player if (pup.intersects(player)) { isImmune = true; immuneTicks = 600; // 10 seconds at 60fps pup.destroy(); powerups.splice(pi, 1); // Flash player gold LK.effects.flashObject(player, 0xFFD700, 800); // Change player to golden bird asset if (player.goldenAsset) { player.removeChild(player.goldenAsset); player.goldenAsset.destroy(); player.goldenAsset = null; } if (player.playerAsset) { player.playerAsset.visible = false; } player.goldenAsset = player.attachAsset('goldenbird', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.1, scaleY: 1.1 }); } } // Handle immunity timer if (isImmune) { immuneTicks--; // Flicker player to show immunity if (LK.ticks % 20 < 10) { player.alpha = 0.6; } else { player.alpha = 1.0; } if (immuneTicks <= 0) { isImmune = false; player.alpha = 1.0; // Restore player asset and remove golden bird asset if (player.goldenAsset) { player.removeChild(player.goldenAsset); player.goldenAsset.destroy(); player.goldenAsset = null; } if (player.playerAsset) { player.playerAsset.visible = true; } } } }; // Reset state on new game game.on('reset', function () { // Remove all obstacles for (var i = 0; i < obstacles.length; i++) { obstacles[i].destroy(); } obstacles = []; // Remove all powerups for (var j = 0; j < powerups.length; j++) { powerups[j].destroy(); } powerups = []; setScore(0); isGameOver = false; isImmune = false; immuneTicks = 0; player.alpha = 1.0; // Remove golden bird asset if present and restore player asset if (player.goldenAsset) { player.removeChild(player.goldenAsset); player.goldenAsset.destroy(); player.goldenAsset = null; } if (player.playerAsset) { player.playerAsset.visible = true; } // Center player player.x = 2048 / 2; player.y = 2732 / 2; }); // Ensure player is not in top-left 100x100 area player.x = 2048 / 2; player.y = 2732 / 2; if (player.x < 180) player.x = 180; if (player.y < 180) player.y = 180;
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var facekit = LK.import("@upit/facekit.v1");
/****
* Classes
****/
// Fast obstacle (normal size, very fast)
var FastObstacle = Container.expand(function () {
var self = Container.call(this);
// Tint to distinguish
var asset = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5
});
asset.tint = 0x3498db;
// Add update method
self.update = function () {
self.x += self.vx;
self.y += self.vy;
};
return self;
});
// Golden Bird Powerup class
var GoldenBirdPowerup = Container.expand(function () {
var self = Container.call(this);
// Use a separate golden bird asset for the powerup
var asset = self.attachAsset('goldenbird', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.1,
scaleY: 1.1
});
self.update = function () {
// Powerup falls down
self.y += self.vy;
};
return self;
});
// Large obstacle (slower, bigger)
var LargeObstacle = Container.expand(function () {
var self = Container.call(this);
// Override asset
var asset = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5
});
// Add update method
self.update = function () {
self.x += self.vx;
self.y += self.vy;
};
return self;
});
// Obstacle base class
var Obstacle = Container.expand(function () {
var self = Container.call(this);
// Default asset, can be overridden by subclasses
var obstacleAsset = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5
});
self.vx = 0;
self.vy = 0;
self.update = function () {
self.x += self.vx;
self.y += self.vy;
};
return self;
});
// Player class
var Player = Container.expand(function () {
var self = Container.call(this);
self.playerAsset = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
self.goldenAsset = null;
// No update needed; position is set by facekit in main game loop
return self;
});
// Small obstacle (faster, smaller)
var SmallObstacle = Container.expand(function () {
var self = Container.call(this);
// Override asset
var asset = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.7,
scaleY: 0.7
});
// Add update method
self.update = function () {
self.x += self.vx;
self.y += self.vy;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x181818
});
/****
* Game Code
****/
// Obstacle: red box
// Player avatar: green ellipse
// Score display
var scoreTxt = new Text2('0', {
size: 120,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Player instance
var player = new Player();
game.addChild(player);
// Start at center
player.x = 2048 / 2;
player.y = 2732 / 2;
// --- Start Screen Overlay ---
var startOverlay = new Container();
var overlayBg = LK.getAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 10,
scaleY: 10
});
overlayBg.alpha = 0.85;
overlayBg.tint = 0x222222;
startOverlay.addChild(overlayBg);
var titleText = new Text2("Golden Bird Dodge", {
size: 160,
fill: 0xFFD700
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 2048 / 2;
titleText.y = 900;
startOverlay.addChild(titleText);
var subtitleText = new Text2("Move your head to control!\nDodge obstacles, grab the golden bird!", {
size: 70,
fill: 0xFFFFFF
});
subtitleText.anchor.set(0.5, 0.5);
subtitleText.x = 2048 / 2;
subtitleText.y = 1200;
startOverlay.addChild(subtitleText);
var playBtn = new Text2("PLAY", {
size: 140,
fill: 0x00FF88
});
playBtn.anchor.set(0.5, 0.5);
playBtn.x = 2048 / 2;
playBtn.y = 1700;
playBtn.interactive = true;
playBtn.buttonMode = true;
startOverlay.addChild(playBtn);
// Center overlay background
overlayBg.x = 2048 / 2;
overlayBg.y = 2732 / 2;
// Add overlay to GUI (so it's always on top)
LK.gui.center.addChild(startOverlay);
// Pause game logic until play is pressed
var gameStarted = false;
game.update = function () {
if (!gameStarted) return;
if (isGameOver) return;
// Facekit: update player position
// Use noseTip if available, else mouthCenter
var fx = facekit.noseTip && facekit.noseTip.x ? facekit.noseTip.x : facekit.mouthCenter.x;
var fy = facekit.noseTip && facekit.noseTip.y ? facekit.noseTip.y : facekit.mouthCenter.y;
// Clamp to keep player inside screen (with margin for player size)
var margin = player.width / 2 + 10;
player.x = clamp(fx, margin, 2048 - margin);
player.y = clamp(fy, margin, 2732 - margin);
// Update obstacles
for (var i = obstacles.length - 1; i >= 0; i--) {
var obs = obstacles[i];
obs.update();
// Remove if out of bounds
if (obs.x < -200 || obs.x > 2048 + 200 || obs.y < -200 || obs.y > 2732 + 200) {
obs.destroy();
obstacles.splice(i, 1);
continue;
}
// Collision detection
if (obs.intersects(player)) {
if (!isImmune) {
// Game over
isGameOver = true;
LK.effects.flashScreen(0xff0000, 800);
LK.showGameOver();
return;
} else {
// If immune, just destroy the obstacle
obs.destroy();
obstacles.splice(i, 1);
continue;
}
}
}
// Score: survive longer = higher score
if (LK.ticks % 6 === 0) {
// ~10 times per second
setScore(LK.getScore() + 1);
}
// Spawn obstacles at shorter intervals, increase frequency more aggressively as score increases
var spawnInterval = 50 - Math.min(35, Math.floor(LK.getScore() / 20) * 5); // Much faster as score increases
if (LK.ticks % spawnInterval === 0) {
spawnObstacle();
}
// Spawn golden bird powerup every 600-900 ticks, if not already present
if (LK.ticks % (600 + Math.floor(Math.random() * 300)) === 0 && powerups.length === 0 && !isImmune) {
var powerup = new GoldenBirdPowerup();
powerup.x = 200 + Math.random() * (2048 - 400);
powerup.y = -80;
powerup.vy = 7 + Math.random() * 3;
powerups.push(powerup);
game.addChild(powerup);
}
// Update powerups
for (var pi = powerups.length - 1; pi >= 0; pi--) {
var pup = powerups[pi];
pup.update();
// Remove if out of bounds
if (pup.y > 2732 + 200) {
pup.destroy();
powerups.splice(pi, 1);
continue;
}
// Collision with player
if (pup.intersects(player)) {
isImmune = true;
immuneTicks = 600; // 10 seconds at 60fps
pup.destroy();
powerups.splice(pi, 1);
// Flash player gold
LK.effects.flashObject(player, 0xFFD700, 800);
// Change player to golden bird asset
if (player.goldenAsset) {
player.removeChild(player.goldenAsset);
player.goldenAsset.destroy();
player.goldenAsset = null;
}
if (player.playerAsset) {
player.playerAsset.visible = false;
}
player.goldenAsset = player.attachAsset('goldenbird', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.1,
scaleY: 1.1
});
}
}
// Handle immunity timer
if (isImmune) {
immuneTicks--;
// Flicker player to show immunity
if (LK.ticks % 20 < 10) {
player.alpha = 0.6;
} else {
player.alpha = 1.0;
}
if (immuneTicks <= 0) {
isImmune = false;
player.alpha = 1.0;
// Restore player asset and remove golden bird asset
if (player.goldenAsset) {
player.removeChild(player.goldenAsset);
player.goldenAsset.destroy();
player.goldenAsset = null;
}
if (player.playerAsset) {
player.playerAsset.visible = true;
}
}
}
};
// Play button handler
playBtn.down = function (x, y, obj) {
if (gameStarted) return;
gameStarted = true;
if (startOverlay.parent) {
startOverlay.parent.removeChild(startOverlay);
}
};
// Obstacles array
var obstacles = [];
// Powerup array
var powerups = [];
// Game state
var isGameOver = false;
var lastScore = 0;
// Immunity state
var isImmune = false;
var immuneTicks = 0;
// Helper: Clamp value between min and max
function clamp(val, min, max) {
if (val < min) return min;
if (val > max) return max;
return val;
}
// Helper: Spawn a batch of new obstacles from random edges, with higher speed
function spawnObstacle() {
// Spawn 1-2 obstacles per interval for less density
var numToSpawn = 1 + Math.floor(Math.random() * 2); // 1-2
for (var n = 0; n < numToSpawn; n++) {
// Randomly choose obstacle type
var typeRand = Math.random();
var obs, speed;
var x = 200 + Math.random() * (2048 - 400);
var y = -80;
if (typeRand < 0.2) {
// 20% Large, slow (slower than before)
obs = new LargeObstacle();
speed = 5 + Math.random() * 2; // 5-7 px/frame
} else if (typeRand < 0.5) {
// 30% Small, fast (but slower than before)
obs = new SmallObstacle();
speed = 8 + Math.random() * 4; // 8-12 px/frame
} else if (typeRand < 0.7) {
// 20% Fast, normal size, blue (but slower than before)
obs = new FastObstacle();
speed = 10 + Math.random() * 4; // 10-14 px/frame
} else {
// 30% Normal (slower than before)
obs = new Obstacle();
speed = 7 + Math.random() * 5; // 7-12 px/frame
}
obs.x = x;
obs.y = y;
obs.vx = 0;
obs.vy = speed;
obstacles.push(obs);
game.addChild(obs);
}
}
// Score logic
function setScore(val) {
LK.setScore(val);
scoreTxt.setText(val);
}
// Main update loop
game.update = function () {
if (isGameOver) return;
// Facekit: update player position
// Use noseTip if available, else mouthCenter
var fx = facekit.noseTip && facekit.noseTip.x ? facekit.noseTip.x : facekit.mouthCenter.x;
var fy = facekit.noseTip && facekit.noseTip.y ? facekit.noseTip.y : facekit.mouthCenter.y;
// Clamp to keep player inside screen (with margin for player size)
var margin = player.width / 2 + 10;
player.x = clamp(fx, margin, 2048 - margin);
player.y = clamp(fy, margin, 2732 - margin);
// Update obstacles
for (var i = obstacles.length - 1; i >= 0; i--) {
var obs = obstacles[i];
obs.update();
// Remove if out of bounds
if (obs.x < -200 || obs.x > 2048 + 200 || obs.y < -200 || obs.y > 2732 + 200) {
obs.destroy();
obstacles.splice(i, 1);
continue;
}
// Collision detection
if (obs.intersects(player)) {
if (!isImmune) {
// Game over
isGameOver = true;
LK.effects.flashScreen(0xff0000, 800);
LK.showGameOver();
return;
} else {
// If immune, just destroy the obstacle
obs.destroy();
obstacles.splice(i, 1);
continue;
}
}
}
// Score: survive longer = higher score
if (LK.ticks % 6 === 0) {
// ~10 times per second
setScore(LK.getScore() + 1);
}
// Spawn obstacles at shorter intervals, increase frequency more aggressively as score increases
var spawnInterval = 50 - Math.min(35, Math.floor(LK.getScore() / 20) * 5); // Much faster as score increases
if (LK.ticks % spawnInterval === 0) {
spawnObstacle();
}
// Spawn golden bird powerup every 600-900 ticks, if not already present
if (LK.ticks % (600 + Math.floor(Math.random() * 300)) === 0 && powerups.length === 0 && !isImmune) {
var powerup = new GoldenBirdPowerup();
powerup.x = 200 + Math.random() * (2048 - 400);
powerup.y = -80;
powerup.vy = 7 + Math.random() * 3;
powerups.push(powerup);
game.addChild(powerup);
}
// Update powerups
for (var pi = powerups.length - 1; pi >= 0; pi--) {
var pup = powerups[pi];
pup.update();
// Remove if out of bounds
if (pup.y > 2732 + 200) {
pup.destroy();
powerups.splice(pi, 1);
continue;
}
// Collision with player
if (pup.intersects(player)) {
isImmune = true;
immuneTicks = 600; // 10 seconds at 60fps
pup.destroy();
powerups.splice(pi, 1);
// Flash player gold
LK.effects.flashObject(player, 0xFFD700, 800);
// Change player to golden bird asset
if (player.goldenAsset) {
player.removeChild(player.goldenAsset);
player.goldenAsset.destroy();
player.goldenAsset = null;
}
if (player.playerAsset) {
player.playerAsset.visible = false;
}
player.goldenAsset = player.attachAsset('goldenbird', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.1,
scaleY: 1.1
});
}
}
// Handle immunity timer
if (isImmune) {
immuneTicks--;
// Flicker player to show immunity
if (LK.ticks % 20 < 10) {
player.alpha = 0.6;
} else {
player.alpha = 1.0;
}
if (immuneTicks <= 0) {
isImmune = false;
player.alpha = 1.0;
// Restore player asset and remove golden bird asset
if (player.goldenAsset) {
player.removeChild(player.goldenAsset);
player.goldenAsset.destroy();
player.goldenAsset = null;
}
if (player.playerAsset) {
player.playerAsset.visible = true;
}
}
}
};
// Reset state on new game
game.on('reset', function () {
// Remove all obstacles
for (var i = 0; i < obstacles.length; i++) {
obstacles[i].destroy();
}
obstacles = [];
// Remove all powerups
for (var j = 0; j < powerups.length; j++) {
powerups[j].destroy();
}
powerups = [];
setScore(0);
isGameOver = false;
isImmune = false;
immuneTicks = 0;
player.alpha = 1.0;
// Remove golden bird asset if present and restore player asset
if (player.goldenAsset) {
player.removeChild(player.goldenAsset);
player.goldenAsset.destroy();
player.goldenAsset = null;
}
if (player.playerAsset) {
player.playerAsset.visible = true;
}
// Center player
player.x = 2048 / 2;
player.y = 2732 / 2;
});
// Ensure player is not in top-left 100x100 area
player.x = 2048 / 2;
player.y = 2732 / 2;
if (player.x < 180) player.x = 180;
if (player.y < 180) player.y = 180;