/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
highScore: 0,
totalKills: 0
});
/****
* Classes
****/
var Bullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 20;
self.dirX = 0;
self.dirY = 0;
self.damage = 10;
self.update = function () {
self.x += self.dirX * self.speed;
self.y += self.dirY * self.speed;
};
return self;
});
var Powerup = Container.expand(function (type) {
var self = Container.call(this);
self.type = type || 'health';
var assetId;
switch (self.type) {
case 'health':
assetId = 'healthPack';
break;
case 'ammo':
assetId = 'ammoPack';
break;
case 'rapidFire':
assetId = 'rapidFirePowerup';
break;
case 'damageBoost':
assetId = 'damageBoostPowerup';
break;
}
var powerupGraphics = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5
});
self.lifespan = 300; // 5 seconds at 60fps
self.update = function () {
self.lifespan--;
// Make powerup blink when about to expire
if (self.lifespan < 60) {
self.visible = LK.ticks % 10 < 5;
}
};
return self;
});
var Survivor = Container.expand(function () {
var self = Container.call(this);
var survivorGraphics = self.attachAsset('survivor', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 100;
self.maxHealth = 100;
self.ammo = 30;
self.maxAmmo = 30;
self.speed = 10;
self.fireRate = 10; // Ticks between shots
self.fireCooldown = 0;
self.damageMultiplier = 1;
self.rapidFireTimer = 0;
self.damageBoostTimer = 0;
self.move = function (direction) {
if (direction.x) {
self.x += direction.x * self.speed;
}
if (direction.y) {
self.y += direction.y * self.speed;
}
// Keep player within game bounds
if (self.x < 50) {
self.x = 50;
}
if (self.x > 2048 - 50) {
self.x = 2048 - 50;
}
if (self.y < 50) {
self.y = 50;
}
if (self.y > 2732 - 50) {
self.y = 2732 - 50;
}
};
self.shoot = function (targetX, targetY) {
if (self.ammo <= 0 || self.fireCooldown > 0) {
return null;
}
self.ammo--;
self.fireCooldown = self.rapidFireTimer > 0 ? Math.floor(self.fireRate / 2) : self.fireRate;
var bullet = new Bullet();
bullet.x = self.x;
bullet.y = self.y;
// Calculate direction vector
var dirX = targetX - self.x;
var dirY = targetY - self.y;
// Normalize direction
var length = Math.sqrt(dirX * dirX + dirY * dirY);
bullet.dirX = dirX / length;
bullet.dirY = dirY / length;
// Set damage based on player's current damage multiplier
bullet.damage = 10 * self.damageMultiplier;
LK.getSound('shoot').play();
return bullet;
};
self.takeDamage = function (amount) {
self.health -= amount;
LK.getSound('playerHit').play();
// Flash player red when taking damage
LK.effects.flashObject(self, 0xff0000, 300);
if (self.health <= 0) {
self.health = 0;
return true; // Player is dead
}
return false;
};
self.collectPowerup = function (type) {
LK.getSound('powerup').play();
switch (type) {
case 'health':
self.health = Math.min(self.health + 25, self.maxHealth);
break;
case 'ammo':
self.ammo = Math.min(self.ammo + 15, self.maxAmmo);
break;
case 'rapidFire':
self.rapidFireTimer = 300; // 5 seconds at 60fps
LK.effects.flashObject(self, 0x9b59b6, 300);
break;
case 'damageBoost':
self.damageMultiplier = 2;
self.damageBoostTimer = 300; // 5 seconds at 60fps
LK.effects.flashObject(self, 0xe67e22, 300);
break;
}
};
self.update = function () {
if (self.fireCooldown > 0) {
self.fireCooldown--;
}
if (self.rapidFireTimer > 0) {
self.rapidFireTimer--;
if (self.rapidFireTimer === 0) {
// End rapid fire effect
}
}
if (self.damageBoostTimer > 0) {
self.damageBoostTimer--;
if (self.damageBoostTimer === 0) {
self.damageMultiplier = 1;
}
}
};
return self;
});
var Zombie = Container.expand(function () {
var self = Container.call(this);
var zombieGraphics = self.attachAsset('zombie', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 30;
self.speed = 3;
self.damage = 10;
self.attackCooldown = 0;
self.attackRate = 30; // Ticks between attacks
self.update = function () {
if (self.attackCooldown > 0) {
self.attackCooldown--;
}
// Movement is handled in the game update function
};
self.takeDamage = function (amount) {
self.health -= amount;
LK.getSound('zombieHit').play();
// Flash zombie green when taking damage
LK.effects.flashObject(self, 0xff0000, 200);
if (self.health <= 0) {
return true; // Zombie is dead
}
return false;
};
self.canAttack = function () {
return self.attackCooldown === 0;
};
self.attack = function () {
self.attackCooldown = self.attackRate;
return self.damage;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a1a1a
});
/****
* Game Code
****/
// Game state variables
var player;
var zombies = [];
var bullets = [];
var powerups = [];
var direction = {
x: 0,
y: 0
};
var targetX = 0;
var targetY = 0;
var isShooting = false;
var wave = 1;
var zombiesKilled = 0;
var waveZombies = 5; // Starting zombies per wave
var score = 0;
var gameOver = false;
var powerupSpawnChance = 0.01; // 1% chance per tick to spawn a powerup
// UI elements
var scoreText;
var waveText;
var healthBar;
var ammoText;
var gameOverText;
// Initialize game
function initGame() {
// Create player
player = new Survivor();
player.x = 2048 / 2;
player.y = 2732 / 2;
game.addChild(player);
// Initialize UI elements
createUI();
// Start first wave
startWave(wave);
// Start background music
LK.playMusic('gameMusic');
}
function createUI() {
// Score text
scoreText = new Text2('Score: 0', {
size: 90,
fill: 0xFFFFFF
});
scoreText.anchor.set(0, 0);
scoreText.x = 50;
scoreText.y = 50;
LK.gui.addChild(scoreText);
// Wave text
waveText = new Text2('Wave: 1', {
size: 90,
fill: 0xFFFFFF
});
waveText.anchor.set(1, 0);
waveText.x = 2048 - 50;
waveText.y = 50;
LK.gui.addChild(waveText);
// Health bar
healthBar = new Container();
var healthBarBg = LK.getAsset('bullet', {
anchorX: 0,
anchorY: 0,
width: 600,
height: 45,
tint: 0x333333
});
var healthBarFill = LK.getAsset('bullet', {
anchorX: 0,
anchorY: 0,
width: 600,
height: 45,
tint: 0xe74c3c
});
healthBar.addChild(healthBarBg);
healthBar.addChild(healthBarFill);
healthBar.fillBar = healthBarFill;
healthBar.x = 50;
healthBar.y = 120;
LK.gui.addChild(healthBar);
// Ammo text
ammoText = new Text2('Ammo: 30/30', {
size: 60,
fill: 0xFFFFFF
});
ammoText.anchor.set(0, 0);
ammoText.x = 50;
ammoText.y = 160;
LK.gui.addChild(ammoText);
// Shoot button
var shootButton = new Container();
var shootButtonBg = LK.getAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5,
width: 200,
height: 200,
tint: 0x3498db
});
shootButton.addChild(shootButtonBg);
shootButton.x = 2048 - 100;
shootButton.y = 2732 - 100;
shootButton.interactive = true;
shootButton.on('down', function () {
isShooting = true;
});
shootButton.on('up', function () {
isShooting = false;
});
LK.gui.addChild(shootButton);
}
function startWave(waveNumber) {
wave = waveNumber;
waveZombies = 5 + (wave - 1) * 3;
waveText.setText('Wave: ' + wave);
// Spawn initial zombies
for (var i = 0; i < Math.min(waveZombies, 10); i++) {
spawnZombie();
}
}
function spawnZombie() {
var zombie = new Zombie();
// Determine spawn location (outside the screen)
var side = Math.floor(Math.random() * 4); // 0: top, 1: right, 2: bottom, 3: left
switch (side) {
case 0:
// Top
zombie.x = Math.random() * 2048;
zombie.y = -100;
break;
case 1:
// Right
zombie.x = 2048 + 100;
zombie.y = Math.random() * 2732;
break;
case 2:
// Bottom
zombie.x = Math.random() * 2048;
zombie.y = 2732 + 100;
break;
case 3:
// Left
zombie.x = -100;
zombie.y = Math.random() * 2732;
break;
}
// Adjust zombie difficulty based on wave
zombie.health = 30 + (wave - 1) * 10;
zombie.speed = 3 + (wave - 1) * 0.2;
if (wave > 5) {
zombie.damage = 10 + (wave - 5) * 2;
}
game.addChild(zombie);
zombies.push(zombie);
}
function spawnPowerup() {
// Determine random position on screen
var x = 100 + Math.random() * (2048 - 200);
var y = 100 + Math.random() * (2732 - 200);
// Determine powerup type
var types = ['health', 'ammo', 'rapidFire', 'damageBoost'];
var type = types[Math.floor(Math.random() * types.length)];
var powerup = new Powerup(type);
powerup.x = x;
powerup.y = y;
game.addChild(powerup);
powerups.push(powerup);
}
function updateUI() {
// Update score
scoreText.setText('Score: ' + score);
// Update health bar
var healthPercent = player.health / player.maxHealth;
healthBar.fillBar.width = 400 * healthPercent;
// Update ammo text
ammoText.setText('Ammo: ' + player.ammo + '/' + player.maxAmmo);
}
// Mouse/Touch input handlers
game.down = function (x, y, obj) {
targetX = x;
targetY = y;
isShooting = true;
};
game.move = function (x, y, obj) {
targetX = x;
targetY = y;
};
game.up = function (x, y, obj) {
isShooting = false;
};
// Main game update loop
game.update = function () {
if (gameOver) {
return;
}
// Update player
player.update();
// Handle player movement based on touch position
// Move player toward touch point
var dx = targetX - player.x;
var dy = targetY - player.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
// Normalize direction
direction.x = dx / distance;
direction.y = dy / distance;
// Rotate player to face the target
player.rotation = Math.atan2(dy, dx);
// Move player towards the target
player.move(direction);
}
// Try to shoot only if the shoot button is pressed
if (isShooting && player.fireCooldown === 0 && player.ammo > 0) {
var bullet = player.shoot(targetX, targetY);
if (bullet) {
game.addChild(bullet);
bullets.push(bullet);
}
}
// Update zombies
for (var i = zombies.length - 1; i >= 0; i--) {
var zombie = zombies[i];
zombie.update();
// Move zombie towards player
var dx = player.x - zombie.x;
var dy = player.y - zombie.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
zombie.x += dx / distance * zombie.speed;
zombie.y += dy / distance * zombie.speed;
}
// Check for collision with player
if (zombie.intersects(player)) {
if (zombie.canAttack()) {
var damage = zombie.attack();
var playerDead = player.takeDamage(damage);
if (playerDead) {
handleGameOver();
return;
}
}
}
}
// Update bullets
for (var i = bullets.length - 1; i >= 0; i--) {
var bullet = bullets[i];
bullet.update();
// Check if bullet is off screen
if (bullet.x < -50 || bullet.x > 2048 + 50 || bullet.y < -50 || bullet.y > 2732 + 50) {
game.removeChild(bullet);
bullets.splice(i, 1);
continue;
}
// Check for collisions with zombies
var hitZombie = false;
for (var j = zombies.length - 1; j >= 0; j--) {
var zombie = zombies[j];
if (bullet.intersects(zombie)) {
hitZombie = true;
var zombieDead = zombie.takeDamage(bullet.damage);
if (zombieDead) {
game.removeChild(zombie);
zombies.splice(j, 1);
zombiesKilled++;
score += 100 * wave;
// Spawn more zombies if needed for the current wave
if (zombies.length + zombiesKilled < waveZombies) {
spawnZombie();
}
// Check if wave is complete
if (zombiesKilled >= waveZombies && zombies.length === 0) {
zombiesKilled = 0;
wave++;
startWave(wave);
// Give player ammo bonus between waves
player.ammo = Math.min(player.ammo + 15, player.maxAmmo);
// Give player health bonus between waves
player.health = Math.min(player.health + 20, player.maxHealth);
}
}
break;
}
}
if (hitZombie) {
game.removeChild(bullet);
bullets.splice(i, 1);
}
}
// Update powerups
for (var i = powerups.length - 1; i >= 0; i--) {
var powerup = powerups[i];
powerup.update();
// Check if powerup expired
if (powerup.lifespan <= 0) {
game.removeChild(powerup);
powerups.splice(i, 1);
continue;
}
// Check for collision with player
if (powerup.intersects(player)) {
player.collectPowerup(powerup.type);
game.removeChild(powerup);
powerups.splice(i, 1);
}
}
// Random chance to spawn powerup
if (Math.random() < powerupSpawnChance && powerups.length < 3) {
spawnPowerup();
}
// Update UI
updateUI();
};
function handleGameOver() {
gameOver = true;
// Save high score
if (score > storage.highScore) {
storage.highScore = score;
}
// Update total kills
storage.totalKills += zombiesKilled;
// Show game over screen
LK.showGameOver();
}
// Initialize the game
initGame(); /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
highScore: 0,
totalKills: 0
});
/****
* Classes
****/
var Bullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 20;
self.dirX = 0;
self.dirY = 0;
self.damage = 10;
self.update = function () {
self.x += self.dirX * self.speed;
self.y += self.dirY * self.speed;
};
return self;
});
var Powerup = Container.expand(function (type) {
var self = Container.call(this);
self.type = type || 'health';
var assetId;
switch (self.type) {
case 'health':
assetId = 'healthPack';
break;
case 'ammo':
assetId = 'ammoPack';
break;
case 'rapidFire':
assetId = 'rapidFirePowerup';
break;
case 'damageBoost':
assetId = 'damageBoostPowerup';
break;
}
var powerupGraphics = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5
});
self.lifespan = 300; // 5 seconds at 60fps
self.update = function () {
self.lifespan--;
// Make powerup blink when about to expire
if (self.lifespan < 60) {
self.visible = LK.ticks % 10 < 5;
}
};
return self;
});
var Survivor = Container.expand(function () {
var self = Container.call(this);
var survivorGraphics = self.attachAsset('survivor', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 100;
self.maxHealth = 100;
self.ammo = 30;
self.maxAmmo = 30;
self.speed = 10;
self.fireRate = 10; // Ticks between shots
self.fireCooldown = 0;
self.damageMultiplier = 1;
self.rapidFireTimer = 0;
self.damageBoostTimer = 0;
self.move = function (direction) {
if (direction.x) {
self.x += direction.x * self.speed;
}
if (direction.y) {
self.y += direction.y * self.speed;
}
// Keep player within game bounds
if (self.x < 50) {
self.x = 50;
}
if (self.x > 2048 - 50) {
self.x = 2048 - 50;
}
if (self.y < 50) {
self.y = 50;
}
if (self.y > 2732 - 50) {
self.y = 2732 - 50;
}
};
self.shoot = function (targetX, targetY) {
if (self.ammo <= 0 || self.fireCooldown > 0) {
return null;
}
self.ammo--;
self.fireCooldown = self.rapidFireTimer > 0 ? Math.floor(self.fireRate / 2) : self.fireRate;
var bullet = new Bullet();
bullet.x = self.x;
bullet.y = self.y;
// Calculate direction vector
var dirX = targetX - self.x;
var dirY = targetY - self.y;
// Normalize direction
var length = Math.sqrt(dirX * dirX + dirY * dirY);
bullet.dirX = dirX / length;
bullet.dirY = dirY / length;
// Set damage based on player's current damage multiplier
bullet.damage = 10 * self.damageMultiplier;
LK.getSound('shoot').play();
return bullet;
};
self.takeDamage = function (amount) {
self.health -= amount;
LK.getSound('playerHit').play();
// Flash player red when taking damage
LK.effects.flashObject(self, 0xff0000, 300);
if (self.health <= 0) {
self.health = 0;
return true; // Player is dead
}
return false;
};
self.collectPowerup = function (type) {
LK.getSound('powerup').play();
switch (type) {
case 'health':
self.health = Math.min(self.health + 25, self.maxHealth);
break;
case 'ammo':
self.ammo = Math.min(self.ammo + 15, self.maxAmmo);
break;
case 'rapidFire':
self.rapidFireTimer = 300; // 5 seconds at 60fps
LK.effects.flashObject(self, 0x9b59b6, 300);
break;
case 'damageBoost':
self.damageMultiplier = 2;
self.damageBoostTimer = 300; // 5 seconds at 60fps
LK.effects.flashObject(self, 0xe67e22, 300);
break;
}
};
self.update = function () {
if (self.fireCooldown > 0) {
self.fireCooldown--;
}
if (self.rapidFireTimer > 0) {
self.rapidFireTimer--;
if (self.rapidFireTimer === 0) {
// End rapid fire effect
}
}
if (self.damageBoostTimer > 0) {
self.damageBoostTimer--;
if (self.damageBoostTimer === 0) {
self.damageMultiplier = 1;
}
}
};
return self;
});
var Zombie = Container.expand(function () {
var self = Container.call(this);
var zombieGraphics = self.attachAsset('zombie', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 30;
self.speed = 3;
self.damage = 10;
self.attackCooldown = 0;
self.attackRate = 30; // Ticks between attacks
self.update = function () {
if (self.attackCooldown > 0) {
self.attackCooldown--;
}
// Movement is handled in the game update function
};
self.takeDamage = function (amount) {
self.health -= amount;
LK.getSound('zombieHit').play();
// Flash zombie green when taking damage
LK.effects.flashObject(self, 0xff0000, 200);
if (self.health <= 0) {
return true; // Zombie is dead
}
return false;
};
self.canAttack = function () {
return self.attackCooldown === 0;
};
self.attack = function () {
self.attackCooldown = self.attackRate;
return self.damage;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a1a1a
});
/****
* Game Code
****/
// Game state variables
var player;
var zombies = [];
var bullets = [];
var powerups = [];
var direction = {
x: 0,
y: 0
};
var targetX = 0;
var targetY = 0;
var isShooting = false;
var wave = 1;
var zombiesKilled = 0;
var waveZombies = 5; // Starting zombies per wave
var score = 0;
var gameOver = false;
var powerupSpawnChance = 0.01; // 1% chance per tick to spawn a powerup
// UI elements
var scoreText;
var waveText;
var healthBar;
var ammoText;
var gameOverText;
// Initialize game
function initGame() {
// Create player
player = new Survivor();
player.x = 2048 / 2;
player.y = 2732 / 2;
game.addChild(player);
// Initialize UI elements
createUI();
// Start first wave
startWave(wave);
// Start background music
LK.playMusic('gameMusic');
}
function createUI() {
// Score text
scoreText = new Text2('Score: 0', {
size: 90,
fill: 0xFFFFFF
});
scoreText.anchor.set(0, 0);
scoreText.x = 50;
scoreText.y = 50;
LK.gui.addChild(scoreText);
// Wave text
waveText = new Text2('Wave: 1', {
size: 90,
fill: 0xFFFFFF
});
waveText.anchor.set(1, 0);
waveText.x = 2048 - 50;
waveText.y = 50;
LK.gui.addChild(waveText);
// Health bar
healthBar = new Container();
var healthBarBg = LK.getAsset('bullet', {
anchorX: 0,
anchorY: 0,
width: 600,
height: 45,
tint: 0x333333
});
var healthBarFill = LK.getAsset('bullet', {
anchorX: 0,
anchorY: 0,
width: 600,
height: 45,
tint: 0xe74c3c
});
healthBar.addChild(healthBarBg);
healthBar.addChild(healthBarFill);
healthBar.fillBar = healthBarFill;
healthBar.x = 50;
healthBar.y = 120;
LK.gui.addChild(healthBar);
// Ammo text
ammoText = new Text2('Ammo: 30/30', {
size: 60,
fill: 0xFFFFFF
});
ammoText.anchor.set(0, 0);
ammoText.x = 50;
ammoText.y = 160;
LK.gui.addChild(ammoText);
// Shoot button
var shootButton = new Container();
var shootButtonBg = LK.getAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5,
width: 200,
height: 200,
tint: 0x3498db
});
shootButton.addChild(shootButtonBg);
shootButton.x = 2048 - 100;
shootButton.y = 2732 - 100;
shootButton.interactive = true;
shootButton.on('down', function () {
isShooting = true;
});
shootButton.on('up', function () {
isShooting = false;
});
LK.gui.addChild(shootButton);
}
function startWave(waveNumber) {
wave = waveNumber;
waveZombies = 5 + (wave - 1) * 3;
waveText.setText('Wave: ' + wave);
// Spawn initial zombies
for (var i = 0; i < Math.min(waveZombies, 10); i++) {
spawnZombie();
}
}
function spawnZombie() {
var zombie = new Zombie();
// Determine spawn location (outside the screen)
var side = Math.floor(Math.random() * 4); // 0: top, 1: right, 2: bottom, 3: left
switch (side) {
case 0:
// Top
zombie.x = Math.random() * 2048;
zombie.y = -100;
break;
case 1:
// Right
zombie.x = 2048 + 100;
zombie.y = Math.random() * 2732;
break;
case 2:
// Bottom
zombie.x = Math.random() * 2048;
zombie.y = 2732 + 100;
break;
case 3:
// Left
zombie.x = -100;
zombie.y = Math.random() * 2732;
break;
}
// Adjust zombie difficulty based on wave
zombie.health = 30 + (wave - 1) * 10;
zombie.speed = 3 + (wave - 1) * 0.2;
if (wave > 5) {
zombie.damage = 10 + (wave - 5) * 2;
}
game.addChild(zombie);
zombies.push(zombie);
}
function spawnPowerup() {
// Determine random position on screen
var x = 100 + Math.random() * (2048 - 200);
var y = 100 + Math.random() * (2732 - 200);
// Determine powerup type
var types = ['health', 'ammo', 'rapidFire', 'damageBoost'];
var type = types[Math.floor(Math.random() * types.length)];
var powerup = new Powerup(type);
powerup.x = x;
powerup.y = y;
game.addChild(powerup);
powerups.push(powerup);
}
function updateUI() {
// Update score
scoreText.setText('Score: ' + score);
// Update health bar
var healthPercent = player.health / player.maxHealth;
healthBar.fillBar.width = 400 * healthPercent;
// Update ammo text
ammoText.setText('Ammo: ' + player.ammo + '/' + player.maxAmmo);
}
// Mouse/Touch input handlers
game.down = function (x, y, obj) {
targetX = x;
targetY = y;
isShooting = true;
};
game.move = function (x, y, obj) {
targetX = x;
targetY = y;
};
game.up = function (x, y, obj) {
isShooting = false;
};
// Main game update loop
game.update = function () {
if (gameOver) {
return;
}
// Update player
player.update();
// Handle player movement based on touch position
// Move player toward touch point
var dx = targetX - player.x;
var dy = targetY - player.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
// Normalize direction
direction.x = dx / distance;
direction.y = dy / distance;
// Rotate player to face the target
player.rotation = Math.atan2(dy, dx);
// Move player towards the target
player.move(direction);
}
// Try to shoot only if the shoot button is pressed
if (isShooting && player.fireCooldown === 0 && player.ammo > 0) {
var bullet = player.shoot(targetX, targetY);
if (bullet) {
game.addChild(bullet);
bullets.push(bullet);
}
}
// Update zombies
for (var i = zombies.length - 1; i >= 0; i--) {
var zombie = zombies[i];
zombie.update();
// Move zombie towards player
var dx = player.x - zombie.x;
var dy = player.y - zombie.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
zombie.x += dx / distance * zombie.speed;
zombie.y += dy / distance * zombie.speed;
}
// Check for collision with player
if (zombie.intersects(player)) {
if (zombie.canAttack()) {
var damage = zombie.attack();
var playerDead = player.takeDamage(damage);
if (playerDead) {
handleGameOver();
return;
}
}
}
}
// Update bullets
for (var i = bullets.length - 1; i >= 0; i--) {
var bullet = bullets[i];
bullet.update();
// Check if bullet is off screen
if (bullet.x < -50 || bullet.x > 2048 + 50 || bullet.y < -50 || bullet.y > 2732 + 50) {
game.removeChild(bullet);
bullets.splice(i, 1);
continue;
}
// Check for collisions with zombies
var hitZombie = false;
for (var j = zombies.length - 1; j >= 0; j--) {
var zombie = zombies[j];
if (bullet.intersects(zombie)) {
hitZombie = true;
var zombieDead = zombie.takeDamage(bullet.damage);
if (zombieDead) {
game.removeChild(zombie);
zombies.splice(j, 1);
zombiesKilled++;
score += 100 * wave;
// Spawn more zombies if needed for the current wave
if (zombies.length + zombiesKilled < waveZombies) {
spawnZombie();
}
// Check if wave is complete
if (zombiesKilled >= waveZombies && zombies.length === 0) {
zombiesKilled = 0;
wave++;
startWave(wave);
// Give player ammo bonus between waves
player.ammo = Math.min(player.ammo + 15, player.maxAmmo);
// Give player health bonus between waves
player.health = Math.min(player.health + 20, player.maxHealth);
}
}
break;
}
}
if (hitZombie) {
game.removeChild(bullet);
bullets.splice(i, 1);
}
}
// Update powerups
for (var i = powerups.length - 1; i >= 0; i--) {
var powerup = powerups[i];
powerup.update();
// Check if powerup expired
if (powerup.lifespan <= 0) {
game.removeChild(powerup);
powerups.splice(i, 1);
continue;
}
// Check for collision with player
if (powerup.intersects(player)) {
player.collectPowerup(powerup.type);
game.removeChild(powerup);
powerups.splice(i, 1);
}
}
// Random chance to spawn powerup
if (Math.random() < powerupSpawnChance && powerups.length < 3) {
spawnPowerup();
}
// Update UI
updateUI();
};
function handleGameOver() {
gameOver = true;
// Save high score
if (score > storage.highScore) {
storage.highScore = score;
}
// Update total kills
storage.totalKills += zombiesKilled;
// Show game over screen
LK.showGameOver();
}
// Initialize the game
initGame();