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