/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Define a class for flying bird enemies
var Bird = Container.expand(function () {
var self = Container.call(this);
var birdGraphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
// Apply a tint to make it look different from regular enemies
birdGraphics.tint = 0x00FFFF;
// Create a hitbox for the bird
self.hitArea = new Rectangle(-30, -30, 60, 60);
self.speed = 6;
self.verticalSpeed = 0;
self.amplitude = 100; // How high the bird flies up and down
self.frequency = 0.05; // How fast the bird completes a wave cycle
self.initialY = 0; // Will store the initial Y position
self.time = 0; // Time counter for sine wave movement
self.update = function () {
// Store last position for collision detection
if (self.lastX === undefined) {
self.lastX = self.x;
self.lastY = self.y;
self.lastWasIntersecting = false;
}
// Bird moves regardless of collisions with other enemies
// Move bird horizontally
self.x -= self.speed;
// Update time counter
self.time += self.frequency;
// Apply sine wave vertical movement
self.y = self.initialY + Math.sin(self.time) * self.amplitude;
// Rotate bird slightly to match flight direction
self.rotation = Math.sin(self.time) * 0.2;
// Remove bird if it's off screen
if (self.x < -50) {
// Remove from enemies array when reaching the left side
for (var i = 0; i < enemies.length; i++) {
if (enemies[i] === self) {
enemies.splice(i, 1);
break;
}
}
self.destroy();
}
// Update last positions
self.lastX = self.x;
self.lastY = self.y;
};
return self;
});
// Define a class for enemies
var Enemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
// Create a much smaller hitbox for the enemy (reduce the collision area)
self.hitArea = new Rectangle(-30, -30, 60, 60);
self.speed = 8;
self.update = function () {
// Store last position for collision detection
if (self.lastX === undefined) {
self.lastX = self.x;
self.lastY = self.y;
self.lastWasIntersecting = false;
}
// Enemy moves regardless of collisions with other enemies
self.x -= self.speed;
// Make the enemy roll by rotating it in the opposite direction
self.rotation -= 0.1;
if (self.x < -50) {
// Remove from enemies array when reaching the left side
for (var i = 0; i < enemies.length; i++) {
if (enemies[i] === self) {
enemies.splice(i, 1);
break;
}
}
self.destroy();
}
// Update last positions
self.lastX = self.x;
self.lastY = self.y;
};
});
// Define a class for enemies that fall from the sky
var FallingEnemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
// Apply a purple tint to make it visually distinct
enemyGraphics.tint = 0x9900FF;
// Create a hitbox for the falling enemy
self.hitArea = new Rectangle(-35, -35, 70, 70);
self.fallSpeed = 10;
self.rotationSpeed = 0.08;
self.update = function () {
// Store last position for collision detection
if (self.lastX === undefined) {
self.lastX = self.x;
self.lastY = self.y;
self.lastWasIntersecting = false;
}
// FallingEnemy moves regardless of collisions with other enemies
// Move enemy down
self.y += self.fallSpeed;
// Rotate enemy as it falls
self.rotation += self.rotationSpeed;
// Remove if off screen
if (self.y > 2732 + 100) {
// Remove from enemies array when reaching the bottom
for (var i = 0; i < enemies.length; i++) {
if (enemies[i] === self) {
enemies.splice(i, 1);
break;
}
}
self.destroy();
}
// Update last positions
self.lastX = self.x;
self.lastY = self.y;
};
return self;
});
// Define a class for heart display
var Heart = Container.expand(function () {
var self = Container.call(this);
// Create a heart using the heart asset
var heartShape = self.attachAsset('heart', {
anchorX: 0.5,
anchorY: 0.5
});
// Animate heart slightly
self.animate = function () {
tween(self, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 800,
easing: tween.easeInOutQuad,
onFinish: function onFinish() {
tween(self, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 800,
easing: tween.easeInOutQuad,
onFinish: self.animate
});
}
});
};
return self;
});
// Define a class for heart pickups that fly in the sky
var HeartPickup = Container.expand(function () {
var self = Container.call(this);
var heartGraphics = self.attachAsset('heart', {
anchorX: 0.5,
anchorY: 0.5
});
// Add visual effects to make the heart look more like a pickup
heartGraphics.tint = 0xFF8FB8; // Lighter pink color
// Create a hitbox for the heart
self.hitArea = new Rectangle(-35, -35, 70, 70);
// Movement properties
self.speed = 3 + Math.random() * 2; // Random speed between 3-5
self.floatAmplitude = 40 + Math.random() * 30; // Random float height
self.floatSpeed = 0.03 + Math.random() * 0.02; // Random float speed
self.initialY = 0;
self.time = Math.random() * Math.PI * 2; // Random starting position in float cycle
// Wings have been removed
// Wing animation method removed
// No wing animation needed
self.update = function () {
// Store last position for collision detection
if (self.lastX === undefined) {
self.lastX = self.x;
self.lastY = self.y;
self.lastWasIntersecting = false;
}
// Move heart horizontally
self.x -= self.speed;
// Apply floating movement
self.time += self.floatSpeed;
self.y = self.initialY + Math.sin(self.time) * self.floatAmplitude;
// Remove heart if it's off screen
if (self.x < -100) {
// Remove from hearts array
for (var i = 0; i < heartPickups.length; i++) {
if (heartPickups[i] === self) {
heartPickups.splice(i, 1);
break;
}
}
self.destroy();
}
// Update last positions
self.lastX = self.x;
self.lastY = self.y;
};
return self;
});
//<Assets used in the game will automatically appear here>
// Define a class for the player character
var Player = Container.expand(function () {
var self = Container.call(this);
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
// Create a smaller hitbox for the player to make collisions more precise
self.hitArea = new Rectangle(-50, -30, 100, 60);
self.speed = 5;
self.jumpHeight = 40;
self.isJumping = false;
self.velocityY = 0;
self.invulnerable = false;
self.jumpsRemaining = 2; // Allow for double jump (initial + 1 more in air)
self.update = function () {
if (self.isJumping) {
self.y += self.velocityY;
self.velocityY += 0.9; // Gravity effect
if (self.y >= 2732 / 2) {
// Ground level
self.y = 2732 / 2;
self.isJumping = false;
self.velocityY = 0;
self.jumpsRemaining = 2; // Reset jumps when landing
// Add landing animation - slight squash effect
tween(self, {
scaleX: 1.3,
scaleY: 0.7
}, {
duration: 200,
easing: tween.easeOutQuad,
onFinish: function onFinish() {
// Return to normal scale
tween(self, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 300,
easing: tween.easeOutElastic
});
}
});
}
}
// Check for falling enemies above the player
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
if (enemy instanceof FallingEnemy && enemy.y > self.y - 400 && enemy.y < self.y && Math.abs(enemy.x - self.x) < 100 && self.isJumping && self.velocityY < 0) {
// If player is jumping upward into a falling enemy, destroy it
enemies.splice(i, 1);
enemy.destroy();
// Give bonus score
// Check if score was reset to 0 and restart music
if (LK.getScore() === 0) {
LK.stopMusic();
LK.playMusic('epicMusic', {
loop: true,
fade: {
start: 0,
end: 0.8,
duration: 2000
}
});
}
LK.setScore(LK.getScore() + 5);
scoreText.setText(LK.getScore());
// Add visual effect for killing enemy
LK.effects.flashObject(self, 0x00ff00, 500);
break;
}
}
};
self.jump = function () {
// Allow jumping if either on ground or have jumps remaining
if (!self.isJumping || self.jumpsRemaining > 0) {
// Only play jump sound on first jump (not on double jump)
if (!self.isJumping) {
// Play jump sound effect only for the initial jump
LK.getSound('jump').play();
}
// If already jumping, this is a double jump
if (self.isJumping) {
self.jumpsRemaining--;
// Second jump has significantly less height
self.velocityY = -self.jumpHeight * 0.6;
} else {
// First jump
self.isJumping = true;
self.jumpsRemaining = 1; // One more jump available after initial jump
self.velocityY = -self.jumpHeight;
}
// Add a jump animation - squash before stretching up
tween(self, {
scaleX: 1.2,
scaleY: 0.8
}, {
duration: 150,
easing: tween.easeOutQuad,
onFinish: function onFinish() {
// Stretch when jumping up
tween(self, {
scaleX: 0.9,
scaleY: 1.3
}, {
duration: 300,
easing: tween.easeOutQuad,
onFinish: function onFinish() {
// Return to normal scale gradually
tween(self, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 400,
easing: tween.easeInOutQuad
});
}
});
}
});
}
};
});
// Define a class for enemies that shoot from the right side
var ShootingEnemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
// Apply a green tint to make it visually distinct
enemyGraphics.tint = 0x00FF00;
// Create a hitbox for the shooting enemy
self.hitArea = new Rectangle(-30, -30, 60, 60);
// Movement properties - much faster
self.speed = 10; // Significantly faster than regular enemies
self.update = function () {
// Store last position for collision detection
if (self.lastX === undefined) {
self.lastX = self.x;
self.lastY = self.y;
self.lastWasIntersecting = false;
}
// Move enemy much faster
self.x -= self.speed * 2.5;
// Make the enemy roll by rotating it in the opposite direction
self.rotation -= 0.1;
// Remove if off screen
if (self.x < -100) {
// Remove from enemies array
for (var i = 0; i < enemies.length; i++) {
if (enemies[i] === self) {
enemies.splice(i, 1);
break;
}
}
self.destroy();
}
// Update last positions
self.lastX = self.x;
self.lastY = self.y;
};
return self;
});
// Portal class removed as it's no longer needed
// Define a class for slow, big enemies
var SlowEnemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
// Make it slightly larger but smaller than before
enemyGraphics.scale.set(1.8, 1.8);
// Apply a reddish tint to make it look more threatening
enemyGraphics.tint = 0xFF5555;
// Create a hitbox that matches the new smaller size
self.hitArea = new Rectangle(-55, -55, 110, 110);
// Faster speed compared to previous value
self.speed = 5;
self.update = function () {
// Store last position for collision detection
if (self.lastX === undefined) {
self.lastX = self.x;
self.lastY = self.y;
self.lastWasIntersecting = false;
}
// SlowEnemy moves regardless of collisions with other enemies
// Move enemy slowly
self.x -= self.speed;
// Make the enemy roll but slower than regular enemies
self.rotation -= 0.04;
// Remove if off screen
if (self.x < -100) {
// Remove from enemies array when reaching the left side
for (var i = 0; i < enemies.length; i++) {
if (enemies[i] === self) {
enemies.splice(i, 1);
break;
}
}
self.destroy();
}
// Update last positions
self.lastX = self.x;
self.lastY = self.y;
};
return self;
});
// Define a class for small, fast enemies
var SmallEnemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
// Scale down the enemy to make it smaller
enemyGraphics.scale.set(0.7, 0.7);
// Apply a yellow tint to make it visually distinct
enemyGraphics.tint = 0xFFFF00;
// Create a hitbox that matches the smaller size
self.hitArea = new Rectangle(-20, -20, 40, 40);
// Faster speed compared to normal enemy
self.speed = 12;
self.update = function () {
// Store last position for collision detection
if (self.lastX === undefined) {
self.lastX = self.x;
self.lastY = self.y;
self.lastWasIntersecting = false;
}
// Move enemy fast
self.x -= self.speed;
// Make the enemy roll by rotating it in the opposite direction
self.rotation -= 0.15;
// Remove if off screen
if (self.x < -50) {
// Remove from enemies array when reaching the left side
for (var i = 0; i < enemies.length; i++) {
if (enemies[i] === self) {
enemies.splice(i, 1);
break;
}
}
self.destroy();
}
// Update last positions
self.lastX = self.x;
self.lastY = self.y;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x654321 // Darker brown background
});
/****
* Game Code
****/
// Using same asset as enemy but will be tinted differently
var background = game.addChild(LK.getAsset('background', {
anchorX: 0,
anchorY: 0
}));
background.x = 0;
background.y = 0;
// Add a second background for the empty space
var background2 = game.addChild(LK.getAsset('background', {
anchorX: 0,
anchorY: 0
}));
background2.x = background.width;
background2.y = 0;
// Initialize player
var player = game.addChild(new Player());
player.x = 2048 / 2;
player.y = 2732 / 2;
// Enemies will spawn from the right edge without a portal
// Initialize enemies
var enemies = [];
var enemySpawnInterval = 100;
var enemySpawnCounter = 0;
// Create a new Text2 object to display the score
var scoreText = new Text2('0', {
size: 100,
fill: 0xFFFFFF
});
// Add instruction text
var instructionText = new Text2('Tap once to jump, tap again for double jump!', {
size: 64,
fill: 0xFFFFFF
});
LK.gui.bottom.addChild(instructionText);
instructionText.x = 2048 / 2;
instructionText.y = -100;
instructionText.anchor.set(0.5, 0.5);
// Fade out instruction after 5 seconds
LK.setTimeout(function () {
tween(instructionText, {
alpha: 0
}, {
duration: 1000,
easing: tween.easeInCubic
});
}, 5000);
// Add the score text to the game GUI at the top center of the screen
LK.gui.top.addChild(scoreText);
scoreText.x = 2048 / 2;
scoreText.y = 0;
// Initialize lives counter
var lives = 3;
var hearts = [];
var heartsContainer = new Container();
game.addChild(heartsContainer);
// Position hearts container in the middle of the screen
heartsContainer.x = 2048 / 2;
heartsContainer.y = 150;
// Create heart display
function updateHeartDisplay() {
// Clear existing hearts
while (hearts.length > 0) {
var heart = hearts.pop();
heart.destroy();
}
// Create new hearts based on current lives
for (var i = 0; i < lives; i++) {
var heart = new Heart();
heart.x = (i - (lives - 1) / 2) * 100; // Distribute hearts evenly
heart.y = 0;
hearts.push(heart);
heartsContainer.addChild(heart);
heart.animate();
}
}
// Initial heart display
updateHeartDisplay();
// Play epic background music with fade-in effect
LK.playMusic('epicMusic', {
fade: {
start: 0,
end: 0.8,
duration: 2000
}
});
// Add listener for game reset to restart music
LK.on('gameReset', function () {
// Restart the music when game resets from the beginning (second 0)
LK.stopMusic(); // Stop current music to ensure we start from the beginning
LK.playMusic('epicMusic', {
loop: true,
fade: {
start: 0,
end: 0.8,
duration: 2000
}
});
});
// Add listener for game over to restart music
LK.on('gameOver', function () {
// Stop current music to ensure we start from the beginning
LK.stopMusic();
// Restart the music from the beginning
LK.playMusic('epicMusic', {
loop: true,
fade: {
start: 0,
end: 0.8,
duration: 2000
}
});
});
// Initialize array for heart pickups
var heartPickups = [];
// Variables for heart spawn control
var heartSpawnInterval = 600; // Less frequent than enemies
var heartSpawnCounter = 0;
// Handle game updates
game.update = function () {
player.update();
// No portal to update
// Spawn hearts occasionally
heartSpawnCounter++;
if (heartSpawnCounter >= heartSpawnInterval) {
// Only spawn heart if player has less than maximum lives
if (lives < 3) {
var heartPickup = new HeartPickup();
heartPickup.x = 2048 + 100; // Start right of screen
heartPickup.y = player.y; // Spawn at the same y position as the player
heartPickup.initialY = heartPickup.y;
heartPickups.push(heartPickup);
game.addChild(heartPickup);
// Add spawn effect
heartPickup.scale.set(0.1, 0.1);
tween(heartPickup, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 500,
easing: tween.easeOutElastic
});
}
// Reset heart spawn counter and randomize next interval
heartSpawnInterval = Math.floor(Math.random() * 400) + 600; // Between 600-1000
heartSpawnCounter = 0;
}
// Update all heart pickups
for (var i = heartPickups.length - 1; i >= 0; i--) {
heartPickups[i].update();
// Check if player collected the heart
if (player.intersects(heartPickups[i])) {
// Regenerate one heart if not at max
if (lives < 3) {
lives++;
updateHeartDisplay();
// Play yeah sound when gaining a heart
LK.getSound('yeah').play();
// Show healing effect
LK.effects.flashObject(player, 0x00FF00, 500);
// Show text effect
var healText = new Text2('+1', {
size: 80,
fill: 0x00FF00
});
healText.anchor.set(0.5, 0.5);
healText.x = player.x;
healText.y = player.y - 100;
game.addChild(healText);
// Animate and remove text
tween(healText, {
y: healText.y - 150,
alpha: 0
}, {
duration: 1000,
easing: tween.easeOutCubic,
onFinish: function onFinish() {
healText.destroy();
}
});
}
// Remove heart pickup
heartPickups[i].destroy();
heartPickups.splice(i, 1);
}
}
// Spawn enemies
enemySpawnCounter++;
if (enemySpawnCounter >= enemySpawnInterval) {
// Enemies spawn without portal effects
// Track if a slow enemy is present in the game
var hasSlowEnemy = false;
for (var i = 0; i < enemies.length; i++) {
if (enemies[i] instanceof SlowEnemy) {
hasSlowEnemy = true;
break;
}
}
// Ensure first three enemies are normal enemies
var spawnChance = 0;
if (LK.getScore() < 3) {
// Force normal enemy for first three spawns
spawnChance = 0;
} else {
// Randomly decide between enemy types
spawnChance = Math.random();
}
// If a slow enemy is present, don't spawn normal enemies
if (hasSlowEnemy && spawnChance < 0.4) {
// Redirect to either bird or falling enemy
spawnChance = 0.4 + spawnChance * 0.6; // Scale between 0.4 and 0.7
}
if (spawnChance < 0.25) {
// 25% chance for regular enemy (only if no slow enemy is present)
var enemy = new Enemy();
enemy.x = 2048; // Right edge of screen
// Set enemy Y position to match player when player is on ground
enemy.y = !player.isJumping ? player.y : 2732 / 2; // Match player Y if on ground, otherwise middle of screen
enemies.push(enemy);
game.addChild(enemy);
// Apply rolling animation with tween in the opposite direction
tween(enemy, {
rotation: -Math.PI * 4
}, {
duration: 2000,
easing: tween.linear,
onFinish: function onFinish() {
// Continuously roll the enemy in the opposite direction
tween(enemy, {
rotation: enemy.rotation - Math.PI * 4
}, {
duration: 2000,
easing: tween.linear
});
}
});
} else if (spawnChance < 0.35) {
// 10% chance for small, fast enemy
var smallEnemy = new SmallEnemy();
smallEnemy.x = 2048; // Right edge of screen
// Set enemy Y position to match player when player is on ground
smallEnemy.y = !player.isJumping ? player.y : 2732 / 2; // Match player Y if on ground, otherwise middle of screen
enemies.push(smallEnemy);
game.addChild(smallEnemy);
// Apply faster rolling animation with tween
tween(smallEnemy, {
rotation: -Math.PI * 6
}, {
duration: 1500,
easing: tween.linear,
onFinish: function onFinish() {
// Continuously roll the enemy in the opposite direction
tween(smallEnemy, {
rotation: smallEnemy.rotation - Math.PI * 6
}, {
duration: 1500,
easing: tween.linear
});
}
});
} else if (spawnChance < 0.55) {
// 20% chance for bird
var bird = new Bird();
bird.x = 2048; // Right edge of screen
bird.y = Math.random() * (2732 / 2 - 400) + 400; // Random position between 400 and half the screen height
bird.initialY = bird.y; // Set the initial Y position for the sine wave
enemies.push(bird);
game.addChild(bird);
// Apply flapping wing animation
tween(bird, {
scaleY: 0.85
}, {
duration: 300,
easing: tween.easeInOutQuad,
onFinish: function onFinish() {
// Flap wings function that will call itself repeatedly
function flapWings() {
tween(bird, {
scaleY: 1.15
}, {
duration: 300,
easing: tween.easeInOutQuad,
onFinish: function onFinish() {
tween(bird, {
scaleY: 0.85
}, {
duration: 300,
easing: tween.easeInOutQuad,
onFinish: flapWings
});
}
});
}
flapWings();
}
});
} else if (spawnChance < 0.75) {
// 20% chance for shooting enemy
// Create warning stripe before shooting enemy appears
var warningStripe = game.addChild(LK.getAsset('hitbox', {
anchorX: 0,
anchorY: 0.5,
width: 2048,
height: 50
}));
warningStripe.y = Math.random() * (2732 / 2 - 200) + 200; // Random position higher than ground
warningStripe.tint = 0xFF0000; // Red warning stripe
warningStripe.alpha = 0.7;
// Flash warning stripe
tween(warningStripe, {
alpha: 0.3
}, {
duration: 300,
easing: tween.linear,
onFinish: function onFinish() {
tween(warningStripe, {
alpha: 0.7
}, {
duration: 300,
easing: tween.linear,
onFinish: function onFinish() {
// Spawn shooting enemy after warning
var shootEnemy = new ShootingEnemy();
shootEnemy.x = 2048; // Right edge of screen
shootEnemy.y = warningStripe.y; // Use same Y position as warning stripe
enemies.push(shootEnemy);
game.addChild(shootEnemy);
// Remove warning stripe
warningStripe.destroy();
}
});
}
});
} else if (spawnChance < 0.85) {
// 10% chance for slow, big enemy
var slowEnemy = new SlowEnemy();
slowEnemy.x = 2048; // Right edge of screen
// Set slow enemy Y position to match player when player is on ground
slowEnemy.y = !player.isJumping ? player.y : 2732 / 2; // Match player Y if on ground, otherwise middle of screen
enemies.push(slowEnemy);
game.addChild(slowEnemy);
// Apply slow stomping animation
tween(slowEnemy, {
rotation: -Math.PI * 2
}, {
duration: 4000,
easing: tween.linear,
onFinish: function onFinish() {
// Continuously roll the enemy in the opposite direction but slower
tween(slowEnemy, {
rotation: slowEnemy.rotation - Math.PI * 2
}, {
duration: 4000,
easing: tween.linear
});
}
});
} else {
// 15% chance for falling enemy
var fallingEnemy = new FallingEnemy();
// Position falling enemy at the player's x position at the top of the screen
fallingEnemy.x = player.x;
fallingEnemy.y = -100; // Start above the screen
enemies.push(fallingEnemy);
game.addChild(fallingEnemy);
// Apply spinning animation
tween(fallingEnemy, {
rotation: Math.PI * 4
}, {
duration: 2000,
easing: tween.linear,
onFinish: function onFinish() {
// Continuously spin the enemy
tween(fallingEnemy, {
rotation: fallingEnemy.rotation + Math.PI * 4
}, {
duration: 2000,
easing: tween.linear
});
}
});
}
// Randomize the spawn interval for the next enemy
enemySpawnInterval = Math.floor(Math.random() * 150) + 50;
enemySpawnCounter = 0;
}
// Update enemies
for (var j = enemies.length - 1; j >= 0; j--) {
if (enemies[j]) {
// Check if enemy exists before accessing
enemies[j].update();
if (enemies[j] && player.intersects(enemies[j]) && !player.invulnerable) {
// Handle collision
lives--;
updateHeartDisplay();
// Play aua sound when player is hit
LK.getSound('aua').play();
// Make player invulnerable temporarily
player.invulnerable = true;
player.alpha = 0.5;
// Flash the screen red
LK.effects.flashScreen(0xff0000, 500);
// Remove the enemy
enemies[j].destroy();
enemies.splice(j, 1);
// Check if player has run out of lives
if (lives <= 0) {
// Stop and restart music from the beginning before showing game over
LK.stopMusic();
LK.playMusic('epicMusic', {
loop: true,
fade: {
start: 0,
end: 0.8,
duration: 2000
}
});
LK.showGameOver();
} else {
// Make player vulnerable again after a short time
LK.setTimeout(function () {
player.invulnerable = false;
player.alpha = 1;
}, 1500);
}
} else if (enemies[j] && player.x > enemies[j].x && !enemies[j].passed) {
enemies[j].passed = true;
LK.setScore(LK.getScore() + 1);
scoreText.setText(LK.getScore());
}
} // Close the enemy existence check
}
};
// Handle player jump
game.down = function (x, y, obj) {
player.jump();
};
// Import tween plugin for animations /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Define a class for flying bird enemies
var Bird = Container.expand(function () {
var self = Container.call(this);
var birdGraphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
// Apply a tint to make it look different from regular enemies
birdGraphics.tint = 0x00FFFF;
// Create a hitbox for the bird
self.hitArea = new Rectangle(-30, -30, 60, 60);
self.speed = 6;
self.verticalSpeed = 0;
self.amplitude = 100; // How high the bird flies up and down
self.frequency = 0.05; // How fast the bird completes a wave cycle
self.initialY = 0; // Will store the initial Y position
self.time = 0; // Time counter for sine wave movement
self.update = function () {
// Store last position for collision detection
if (self.lastX === undefined) {
self.lastX = self.x;
self.lastY = self.y;
self.lastWasIntersecting = false;
}
// Bird moves regardless of collisions with other enemies
// Move bird horizontally
self.x -= self.speed;
// Update time counter
self.time += self.frequency;
// Apply sine wave vertical movement
self.y = self.initialY + Math.sin(self.time) * self.amplitude;
// Rotate bird slightly to match flight direction
self.rotation = Math.sin(self.time) * 0.2;
// Remove bird if it's off screen
if (self.x < -50) {
// Remove from enemies array when reaching the left side
for (var i = 0; i < enemies.length; i++) {
if (enemies[i] === self) {
enemies.splice(i, 1);
break;
}
}
self.destroy();
}
// Update last positions
self.lastX = self.x;
self.lastY = self.y;
};
return self;
});
// Define a class for enemies
var Enemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
// Create a much smaller hitbox for the enemy (reduce the collision area)
self.hitArea = new Rectangle(-30, -30, 60, 60);
self.speed = 8;
self.update = function () {
// Store last position for collision detection
if (self.lastX === undefined) {
self.lastX = self.x;
self.lastY = self.y;
self.lastWasIntersecting = false;
}
// Enemy moves regardless of collisions with other enemies
self.x -= self.speed;
// Make the enemy roll by rotating it in the opposite direction
self.rotation -= 0.1;
if (self.x < -50) {
// Remove from enemies array when reaching the left side
for (var i = 0; i < enemies.length; i++) {
if (enemies[i] === self) {
enemies.splice(i, 1);
break;
}
}
self.destroy();
}
// Update last positions
self.lastX = self.x;
self.lastY = self.y;
};
});
// Define a class for enemies that fall from the sky
var FallingEnemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
// Apply a purple tint to make it visually distinct
enemyGraphics.tint = 0x9900FF;
// Create a hitbox for the falling enemy
self.hitArea = new Rectangle(-35, -35, 70, 70);
self.fallSpeed = 10;
self.rotationSpeed = 0.08;
self.update = function () {
// Store last position for collision detection
if (self.lastX === undefined) {
self.lastX = self.x;
self.lastY = self.y;
self.lastWasIntersecting = false;
}
// FallingEnemy moves regardless of collisions with other enemies
// Move enemy down
self.y += self.fallSpeed;
// Rotate enemy as it falls
self.rotation += self.rotationSpeed;
// Remove if off screen
if (self.y > 2732 + 100) {
// Remove from enemies array when reaching the bottom
for (var i = 0; i < enemies.length; i++) {
if (enemies[i] === self) {
enemies.splice(i, 1);
break;
}
}
self.destroy();
}
// Update last positions
self.lastX = self.x;
self.lastY = self.y;
};
return self;
});
// Define a class for heart display
var Heart = Container.expand(function () {
var self = Container.call(this);
// Create a heart using the heart asset
var heartShape = self.attachAsset('heart', {
anchorX: 0.5,
anchorY: 0.5
});
// Animate heart slightly
self.animate = function () {
tween(self, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 800,
easing: tween.easeInOutQuad,
onFinish: function onFinish() {
tween(self, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 800,
easing: tween.easeInOutQuad,
onFinish: self.animate
});
}
});
};
return self;
});
// Define a class for heart pickups that fly in the sky
var HeartPickup = Container.expand(function () {
var self = Container.call(this);
var heartGraphics = self.attachAsset('heart', {
anchorX: 0.5,
anchorY: 0.5
});
// Add visual effects to make the heart look more like a pickup
heartGraphics.tint = 0xFF8FB8; // Lighter pink color
// Create a hitbox for the heart
self.hitArea = new Rectangle(-35, -35, 70, 70);
// Movement properties
self.speed = 3 + Math.random() * 2; // Random speed between 3-5
self.floatAmplitude = 40 + Math.random() * 30; // Random float height
self.floatSpeed = 0.03 + Math.random() * 0.02; // Random float speed
self.initialY = 0;
self.time = Math.random() * Math.PI * 2; // Random starting position in float cycle
// Wings have been removed
// Wing animation method removed
// No wing animation needed
self.update = function () {
// Store last position for collision detection
if (self.lastX === undefined) {
self.lastX = self.x;
self.lastY = self.y;
self.lastWasIntersecting = false;
}
// Move heart horizontally
self.x -= self.speed;
// Apply floating movement
self.time += self.floatSpeed;
self.y = self.initialY + Math.sin(self.time) * self.floatAmplitude;
// Remove heart if it's off screen
if (self.x < -100) {
// Remove from hearts array
for (var i = 0; i < heartPickups.length; i++) {
if (heartPickups[i] === self) {
heartPickups.splice(i, 1);
break;
}
}
self.destroy();
}
// Update last positions
self.lastX = self.x;
self.lastY = self.y;
};
return self;
});
//<Assets used in the game will automatically appear here>
// Define a class for the player character
var Player = Container.expand(function () {
var self = Container.call(this);
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
// Create a smaller hitbox for the player to make collisions more precise
self.hitArea = new Rectangle(-50, -30, 100, 60);
self.speed = 5;
self.jumpHeight = 40;
self.isJumping = false;
self.velocityY = 0;
self.invulnerable = false;
self.jumpsRemaining = 2; // Allow for double jump (initial + 1 more in air)
self.update = function () {
if (self.isJumping) {
self.y += self.velocityY;
self.velocityY += 0.9; // Gravity effect
if (self.y >= 2732 / 2) {
// Ground level
self.y = 2732 / 2;
self.isJumping = false;
self.velocityY = 0;
self.jumpsRemaining = 2; // Reset jumps when landing
// Add landing animation - slight squash effect
tween(self, {
scaleX: 1.3,
scaleY: 0.7
}, {
duration: 200,
easing: tween.easeOutQuad,
onFinish: function onFinish() {
// Return to normal scale
tween(self, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 300,
easing: tween.easeOutElastic
});
}
});
}
}
// Check for falling enemies above the player
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
if (enemy instanceof FallingEnemy && enemy.y > self.y - 400 && enemy.y < self.y && Math.abs(enemy.x - self.x) < 100 && self.isJumping && self.velocityY < 0) {
// If player is jumping upward into a falling enemy, destroy it
enemies.splice(i, 1);
enemy.destroy();
// Give bonus score
// Check if score was reset to 0 and restart music
if (LK.getScore() === 0) {
LK.stopMusic();
LK.playMusic('epicMusic', {
loop: true,
fade: {
start: 0,
end: 0.8,
duration: 2000
}
});
}
LK.setScore(LK.getScore() + 5);
scoreText.setText(LK.getScore());
// Add visual effect for killing enemy
LK.effects.flashObject(self, 0x00ff00, 500);
break;
}
}
};
self.jump = function () {
// Allow jumping if either on ground or have jumps remaining
if (!self.isJumping || self.jumpsRemaining > 0) {
// Only play jump sound on first jump (not on double jump)
if (!self.isJumping) {
// Play jump sound effect only for the initial jump
LK.getSound('jump').play();
}
// If already jumping, this is a double jump
if (self.isJumping) {
self.jumpsRemaining--;
// Second jump has significantly less height
self.velocityY = -self.jumpHeight * 0.6;
} else {
// First jump
self.isJumping = true;
self.jumpsRemaining = 1; // One more jump available after initial jump
self.velocityY = -self.jumpHeight;
}
// Add a jump animation - squash before stretching up
tween(self, {
scaleX: 1.2,
scaleY: 0.8
}, {
duration: 150,
easing: tween.easeOutQuad,
onFinish: function onFinish() {
// Stretch when jumping up
tween(self, {
scaleX: 0.9,
scaleY: 1.3
}, {
duration: 300,
easing: tween.easeOutQuad,
onFinish: function onFinish() {
// Return to normal scale gradually
tween(self, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 400,
easing: tween.easeInOutQuad
});
}
});
}
});
}
};
});
// Define a class for enemies that shoot from the right side
var ShootingEnemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
// Apply a green tint to make it visually distinct
enemyGraphics.tint = 0x00FF00;
// Create a hitbox for the shooting enemy
self.hitArea = new Rectangle(-30, -30, 60, 60);
// Movement properties - much faster
self.speed = 10; // Significantly faster than regular enemies
self.update = function () {
// Store last position for collision detection
if (self.lastX === undefined) {
self.lastX = self.x;
self.lastY = self.y;
self.lastWasIntersecting = false;
}
// Move enemy much faster
self.x -= self.speed * 2.5;
// Make the enemy roll by rotating it in the opposite direction
self.rotation -= 0.1;
// Remove if off screen
if (self.x < -100) {
// Remove from enemies array
for (var i = 0; i < enemies.length; i++) {
if (enemies[i] === self) {
enemies.splice(i, 1);
break;
}
}
self.destroy();
}
// Update last positions
self.lastX = self.x;
self.lastY = self.y;
};
return self;
});
// Portal class removed as it's no longer needed
// Define a class for slow, big enemies
var SlowEnemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
// Make it slightly larger but smaller than before
enemyGraphics.scale.set(1.8, 1.8);
// Apply a reddish tint to make it look more threatening
enemyGraphics.tint = 0xFF5555;
// Create a hitbox that matches the new smaller size
self.hitArea = new Rectangle(-55, -55, 110, 110);
// Faster speed compared to previous value
self.speed = 5;
self.update = function () {
// Store last position for collision detection
if (self.lastX === undefined) {
self.lastX = self.x;
self.lastY = self.y;
self.lastWasIntersecting = false;
}
// SlowEnemy moves regardless of collisions with other enemies
// Move enemy slowly
self.x -= self.speed;
// Make the enemy roll but slower than regular enemies
self.rotation -= 0.04;
// Remove if off screen
if (self.x < -100) {
// Remove from enemies array when reaching the left side
for (var i = 0; i < enemies.length; i++) {
if (enemies[i] === self) {
enemies.splice(i, 1);
break;
}
}
self.destroy();
}
// Update last positions
self.lastX = self.x;
self.lastY = self.y;
};
return self;
});
// Define a class for small, fast enemies
var SmallEnemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
// Scale down the enemy to make it smaller
enemyGraphics.scale.set(0.7, 0.7);
// Apply a yellow tint to make it visually distinct
enemyGraphics.tint = 0xFFFF00;
// Create a hitbox that matches the smaller size
self.hitArea = new Rectangle(-20, -20, 40, 40);
// Faster speed compared to normal enemy
self.speed = 12;
self.update = function () {
// Store last position for collision detection
if (self.lastX === undefined) {
self.lastX = self.x;
self.lastY = self.y;
self.lastWasIntersecting = false;
}
// Move enemy fast
self.x -= self.speed;
// Make the enemy roll by rotating it in the opposite direction
self.rotation -= 0.15;
// Remove if off screen
if (self.x < -50) {
// Remove from enemies array when reaching the left side
for (var i = 0; i < enemies.length; i++) {
if (enemies[i] === self) {
enemies.splice(i, 1);
break;
}
}
self.destroy();
}
// Update last positions
self.lastX = self.x;
self.lastY = self.y;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x654321 // Darker brown background
});
/****
* Game Code
****/
// Using same asset as enemy but will be tinted differently
var background = game.addChild(LK.getAsset('background', {
anchorX: 0,
anchorY: 0
}));
background.x = 0;
background.y = 0;
// Add a second background for the empty space
var background2 = game.addChild(LK.getAsset('background', {
anchorX: 0,
anchorY: 0
}));
background2.x = background.width;
background2.y = 0;
// Initialize player
var player = game.addChild(new Player());
player.x = 2048 / 2;
player.y = 2732 / 2;
// Enemies will spawn from the right edge without a portal
// Initialize enemies
var enemies = [];
var enemySpawnInterval = 100;
var enemySpawnCounter = 0;
// Create a new Text2 object to display the score
var scoreText = new Text2('0', {
size: 100,
fill: 0xFFFFFF
});
// Add instruction text
var instructionText = new Text2('Tap once to jump, tap again for double jump!', {
size: 64,
fill: 0xFFFFFF
});
LK.gui.bottom.addChild(instructionText);
instructionText.x = 2048 / 2;
instructionText.y = -100;
instructionText.anchor.set(0.5, 0.5);
// Fade out instruction after 5 seconds
LK.setTimeout(function () {
tween(instructionText, {
alpha: 0
}, {
duration: 1000,
easing: tween.easeInCubic
});
}, 5000);
// Add the score text to the game GUI at the top center of the screen
LK.gui.top.addChild(scoreText);
scoreText.x = 2048 / 2;
scoreText.y = 0;
// Initialize lives counter
var lives = 3;
var hearts = [];
var heartsContainer = new Container();
game.addChild(heartsContainer);
// Position hearts container in the middle of the screen
heartsContainer.x = 2048 / 2;
heartsContainer.y = 150;
// Create heart display
function updateHeartDisplay() {
// Clear existing hearts
while (hearts.length > 0) {
var heart = hearts.pop();
heart.destroy();
}
// Create new hearts based on current lives
for (var i = 0; i < lives; i++) {
var heart = new Heart();
heart.x = (i - (lives - 1) / 2) * 100; // Distribute hearts evenly
heart.y = 0;
hearts.push(heart);
heartsContainer.addChild(heart);
heart.animate();
}
}
// Initial heart display
updateHeartDisplay();
// Play epic background music with fade-in effect
LK.playMusic('epicMusic', {
fade: {
start: 0,
end: 0.8,
duration: 2000
}
});
// Add listener for game reset to restart music
LK.on('gameReset', function () {
// Restart the music when game resets from the beginning (second 0)
LK.stopMusic(); // Stop current music to ensure we start from the beginning
LK.playMusic('epicMusic', {
loop: true,
fade: {
start: 0,
end: 0.8,
duration: 2000
}
});
});
// Add listener for game over to restart music
LK.on('gameOver', function () {
// Stop current music to ensure we start from the beginning
LK.stopMusic();
// Restart the music from the beginning
LK.playMusic('epicMusic', {
loop: true,
fade: {
start: 0,
end: 0.8,
duration: 2000
}
});
});
// Initialize array for heart pickups
var heartPickups = [];
// Variables for heart spawn control
var heartSpawnInterval = 600; // Less frequent than enemies
var heartSpawnCounter = 0;
// Handle game updates
game.update = function () {
player.update();
// No portal to update
// Spawn hearts occasionally
heartSpawnCounter++;
if (heartSpawnCounter >= heartSpawnInterval) {
// Only spawn heart if player has less than maximum lives
if (lives < 3) {
var heartPickup = new HeartPickup();
heartPickup.x = 2048 + 100; // Start right of screen
heartPickup.y = player.y; // Spawn at the same y position as the player
heartPickup.initialY = heartPickup.y;
heartPickups.push(heartPickup);
game.addChild(heartPickup);
// Add spawn effect
heartPickup.scale.set(0.1, 0.1);
tween(heartPickup, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 500,
easing: tween.easeOutElastic
});
}
// Reset heart spawn counter and randomize next interval
heartSpawnInterval = Math.floor(Math.random() * 400) + 600; // Between 600-1000
heartSpawnCounter = 0;
}
// Update all heart pickups
for (var i = heartPickups.length - 1; i >= 0; i--) {
heartPickups[i].update();
// Check if player collected the heart
if (player.intersects(heartPickups[i])) {
// Regenerate one heart if not at max
if (lives < 3) {
lives++;
updateHeartDisplay();
// Play yeah sound when gaining a heart
LK.getSound('yeah').play();
// Show healing effect
LK.effects.flashObject(player, 0x00FF00, 500);
// Show text effect
var healText = new Text2('+1', {
size: 80,
fill: 0x00FF00
});
healText.anchor.set(0.5, 0.5);
healText.x = player.x;
healText.y = player.y - 100;
game.addChild(healText);
// Animate and remove text
tween(healText, {
y: healText.y - 150,
alpha: 0
}, {
duration: 1000,
easing: tween.easeOutCubic,
onFinish: function onFinish() {
healText.destroy();
}
});
}
// Remove heart pickup
heartPickups[i].destroy();
heartPickups.splice(i, 1);
}
}
// Spawn enemies
enemySpawnCounter++;
if (enemySpawnCounter >= enemySpawnInterval) {
// Enemies spawn without portal effects
// Track if a slow enemy is present in the game
var hasSlowEnemy = false;
for (var i = 0; i < enemies.length; i++) {
if (enemies[i] instanceof SlowEnemy) {
hasSlowEnemy = true;
break;
}
}
// Ensure first three enemies are normal enemies
var spawnChance = 0;
if (LK.getScore() < 3) {
// Force normal enemy for first three spawns
spawnChance = 0;
} else {
// Randomly decide between enemy types
spawnChance = Math.random();
}
// If a slow enemy is present, don't spawn normal enemies
if (hasSlowEnemy && spawnChance < 0.4) {
// Redirect to either bird or falling enemy
spawnChance = 0.4 + spawnChance * 0.6; // Scale between 0.4 and 0.7
}
if (spawnChance < 0.25) {
// 25% chance for regular enemy (only if no slow enemy is present)
var enemy = new Enemy();
enemy.x = 2048; // Right edge of screen
// Set enemy Y position to match player when player is on ground
enemy.y = !player.isJumping ? player.y : 2732 / 2; // Match player Y if on ground, otherwise middle of screen
enemies.push(enemy);
game.addChild(enemy);
// Apply rolling animation with tween in the opposite direction
tween(enemy, {
rotation: -Math.PI * 4
}, {
duration: 2000,
easing: tween.linear,
onFinish: function onFinish() {
// Continuously roll the enemy in the opposite direction
tween(enemy, {
rotation: enemy.rotation - Math.PI * 4
}, {
duration: 2000,
easing: tween.linear
});
}
});
} else if (spawnChance < 0.35) {
// 10% chance for small, fast enemy
var smallEnemy = new SmallEnemy();
smallEnemy.x = 2048; // Right edge of screen
// Set enemy Y position to match player when player is on ground
smallEnemy.y = !player.isJumping ? player.y : 2732 / 2; // Match player Y if on ground, otherwise middle of screen
enemies.push(smallEnemy);
game.addChild(smallEnemy);
// Apply faster rolling animation with tween
tween(smallEnemy, {
rotation: -Math.PI * 6
}, {
duration: 1500,
easing: tween.linear,
onFinish: function onFinish() {
// Continuously roll the enemy in the opposite direction
tween(smallEnemy, {
rotation: smallEnemy.rotation - Math.PI * 6
}, {
duration: 1500,
easing: tween.linear
});
}
});
} else if (spawnChance < 0.55) {
// 20% chance for bird
var bird = new Bird();
bird.x = 2048; // Right edge of screen
bird.y = Math.random() * (2732 / 2 - 400) + 400; // Random position between 400 and half the screen height
bird.initialY = bird.y; // Set the initial Y position for the sine wave
enemies.push(bird);
game.addChild(bird);
// Apply flapping wing animation
tween(bird, {
scaleY: 0.85
}, {
duration: 300,
easing: tween.easeInOutQuad,
onFinish: function onFinish() {
// Flap wings function that will call itself repeatedly
function flapWings() {
tween(bird, {
scaleY: 1.15
}, {
duration: 300,
easing: tween.easeInOutQuad,
onFinish: function onFinish() {
tween(bird, {
scaleY: 0.85
}, {
duration: 300,
easing: tween.easeInOutQuad,
onFinish: flapWings
});
}
});
}
flapWings();
}
});
} else if (spawnChance < 0.75) {
// 20% chance for shooting enemy
// Create warning stripe before shooting enemy appears
var warningStripe = game.addChild(LK.getAsset('hitbox', {
anchorX: 0,
anchorY: 0.5,
width: 2048,
height: 50
}));
warningStripe.y = Math.random() * (2732 / 2 - 200) + 200; // Random position higher than ground
warningStripe.tint = 0xFF0000; // Red warning stripe
warningStripe.alpha = 0.7;
// Flash warning stripe
tween(warningStripe, {
alpha: 0.3
}, {
duration: 300,
easing: tween.linear,
onFinish: function onFinish() {
tween(warningStripe, {
alpha: 0.7
}, {
duration: 300,
easing: tween.linear,
onFinish: function onFinish() {
// Spawn shooting enemy after warning
var shootEnemy = new ShootingEnemy();
shootEnemy.x = 2048; // Right edge of screen
shootEnemy.y = warningStripe.y; // Use same Y position as warning stripe
enemies.push(shootEnemy);
game.addChild(shootEnemy);
// Remove warning stripe
warningStripe.destroy();
}
});
}
});
} else if (spawnChance < 0.85) {
// 10% chance for slow, big enemy
var slowEnemy = new SlowEnemy();
slowEnemy.x = 2048; // Right edge of screen
// Set slow enemy Y position to match player when player is on ground
slowEnemy.y = !player.isJumping ? player.y : 2732 / 2; // Match player Y if on ground, otherwise middle of screen
enemies.push(slowEnemy);
game.addChild(slowEnemy);
// Apply slow stomping animation
tween(slowEnemy, {
rotation: -Math.PI * 2
}, {
duration: 4000,
easing: tween.linear,
onFinish: function onFinish() {
// Continuously roll the enemy in the opposite direction but slower
tween(slowEnemy, {
rotation: slowEnemy.rotation - Math.PI * 2
}, {
duration: 4000,
easing: tween.linear
});
}
});
} else {
// 15% chance for falling enemy
var fallingEnemy = new FallingEnemy();
// Position falling enemy at the player's x position at the top of the screen
fallingEnemy.x = player.x;
fallingEnemy.y = -100; // Start above the screen
enemies.push(fallingEnemy);
game.addChild(fallingEnemy);
// Apply spinning animation
tween(fallingEnemy, {
rotation: Math.PI * 4
}, {
duration: 2000,
easing: tween.linear,
onFinish: function onFinish() {
// Continuously spin the enemy
tween(fallingEnemy, {
rotation: fallingEnemy.rotation + Math.PI * 4
}, {
duration: 2000,
easing: tween.linear
});
}
});
}
// Randomize the spawn interval for the next enemy
enemySpawnInterval = Math.floor(Math.random() * 150) + 50;
enemySpawnCounter = 0;
}
// Update enemies
for (var j = enemies.length - 1; j >= 0; j--) {
if (enemies[j]) {
// Check if enemy exists before accessing
enemies[j].update();
if (enemies[j] && player.intersects(enemies[j]) && !player.invulnerable) {
// Handle collision
lives--;
updateHeartDisplay();
// Play aua sound when player is hit
LK.getSound('aua').play();
// Make player invulnerable temporarily
player.invulnerable = true;
player.alpha = 0.5;
// Flash the screen red
LK.effects.flashScreen(0xff0000, 500);
// Remove the enemy
enemies[j].destroy();
enemies.splice(j, 1);
// Check if player has run out of lives
if (lives <= 0) {
// Stop and restart music from the beginning before showing game over
LK.stopMusic();
LK.playMusic('epicMusic', {
loop: true,
fade: {
start: 0,
end: 0.8,
duration: 2000
}
});
LK.showGameOver();
} else {
// Make player vulnerable again after a short time
LK.setTimeout(function () {
player.invulnerable = false;
player.alpha = 1;
}, 1500);
}
} else if (enemies[j] && player.x > enemies[j].x && !enemies[j].passed) {
enemies[j].passed = true;
LK.setScore(LK.getScore() + 1);
scoreText.setText(LK.getScore());
}
} // Close the enemy existence check
}
};
// Handle player jump
game.down = function (x, y, obj) {
player.jump();
};
// Import tween plugin for animations