/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var Bullet = Container.expand(function () { var self = Container.call(this); var bulletGraphics = self.attachAsset('bullet', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 8; self.damage = player ? player.damage : 20; self.direction = { x: 0, y: 0 }; self.update = function () { self.x += self.direction.x * self.speed; self.y += self.direction.y * self.speed; if (self.x < -50 || self.x > 2098 || self.y < -50 || self.y > 2782) { self.shouldRemove = true; } }; return self; }); var Player = Container.expand(function () { var self = Container.call(this); var playerGraphics = self.attachAsset('player', { anchorX: 0.5, anchorY: 0.5 }); self.health = 100; self.maxHealth = 100; self.damage = 20; self.fireRate = 30; // Lower = faster self.speed = 4; self.lastShot = 0; self.takeDamage = function (amount) { self.health -= amount; if (self.health <= 0) { self.health = 0; LK.showGameOver(); } else { LK.effects.flashObject(self, 0xff0000, 300); LK.getSound('playerHit').play(); } }; self.heal = function (amount) { self.health = Math.min(self.maxHealth, self.health + amount); }; return self; }); var XPOrb = Container.expand(function () { var self = Container.call(this); var orbGraphics = self.attachAsset('xpOrb', { anchorX: 0.5, anchorY: 0.5 }); self.value = 10; self.collectDistance = 30; self.update = function () { var dx = player.x - self.x; var dy = player.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < self.collectDistance) { self.x += dx / distance * 6; self.y += dy / distance * 6; if (distance < 20) { currentXP += self.value; checkLevelUp(); self.shouldRemove = true; } } }; return self; }); var Zombie = Container.expand(function (type) { var self = Container.call(this); self.type = type || 'normal'; self.speed = 1; self.health = 40; self.damage = 25; self.xpValue = 10; var assetId = 'zombie'; if (self.type === 'fast') { assetId = 'fastZombie'; self.speed = 2.5; self.health = 25; self.damage = 20; self.xpValue = 15; } else if (self.type === 'strong') { assetId = 'strongZombie'; self.speed = 0.8; self.health = 80; self.damage = 40; self.xpValue = 25; } var zombieGraphics = self.attachAsset(assetId, { anchorX: 0.5, anchorY: 0.5 }); self.takeDamage = function (amount) { self.health -= amount; LK.effects.flashObject(self, 0xffffff, 200); if (self.health <= 0) { self.isDead = true; LK.getSound('zombieHit').play(); } }; self.update = function () { if (self.isDead) return; var dx = player.x - self.x; var dy = player.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 5) { self.x += dx / distance * self.speed; self.y += dy / distance * self.speed; } else { // Attack player if (!self.lastAttack || LK.ticks - self.lastAttack > 60) { player.takeDamage(self.damage); self.lastAttack = LK.ticks; } } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x2C3E50 }); /**** * Game Code ****/ var player; var zombies = []; var bullets = []; var xpOrbs = []; var currentXP = 0; var currentLevel = 1; var xpToNextLevel = 100; var waveNumber = 1; var zombiesSpawned = 0; var zombiesInWave = 10; var spawnTimer = 0; var spawnRate = 120; // frames between spawns var isShowingUpgrade = false; var upgrades = []; // UI Elements var healthBar, xpBar, levelText, waveText, scoreText; function initializeUI() { // Health bar background var healthBg = LK.getAsset('healthBg', { width: 400, height: 20, color: 0x800000, shape: 'box' }); healthBg.x = 100; healthBg.y = 100; LK.gui.topLeft.addChild(healthBg); // Health bar healthBar = LK.getAsset('healthBar', { width: 400, height: 20, color: 0x00ff00, shape: 'box' }); healthBar.x = 100; healthBar.y = 100; LK.gui.topLeft.addChild(healthBar); // XP bar background var xpBg = LK.getAsset('xpBg', { width: 400, height: 15, color: 0x404040, shape: 'box' }); xpBg.x = 100; xpBg.y = 130; LK.gui.topLeft.addChild(xpBg); // XP bar xpBar = LK.getAsset('xpBar', { width: 400, height: 15, color: 0x00FFFF, shape: 'box' }); xpBar.x = 100; xpBar.y = 130; LK.gui.topLeft.addChild(xpBar); // Level text levelText = new Text2('Level: 1', { size: 40, fill: '#ffffff' }); levelText.anchor.set(0, 0); levelText.x = 100; levelText.y = 50; LK.gui.topLeft.addChild(levelText); // Wave text waveText = new Text2('Wave: 1', { size: 30, fill: '#ffffff' }); waveText.anchor.set(0.5, 0); waveText.x = 1024; waveText.y = 50; LK.gui.top.addChild(waveText); // Score text scoreText = new Text2('Score: 0', { size: 30, fill: '#ffffff' }); scoreText.anchor.set(1, 0); scoreText.x = 1948; scoreText.y = 50; LK.gui.topRight.addChild(scoreText); } function updateUI() { if (healthBar) { var healthPercent = player.health / player.maxHealth; healthBar.width = 400 * healthPercent; } if (xpBar) { var xpPercent = currentXP / xpToNextLevel; xpBar.width = 400 * Math.min(xpPercent, 1); } if (levelText) { levelText.setText('Level: ' + currentLevel); } if (waveText) { waveText.setText('Wave: ' + waveNumber); } if (scoreText) { scoreText.setText('Score: ' + LK.getScore()); } } function checkLevelUp() { if (currentXP >= xpToNextLevel) { currentXP -= xpToNextLevel; currentLevel++; xpToNextLevel = Math.floor(xpToNextLevel * 1.5); LK.getSound('levelUp').play(); showUpgradeScreen(); } } function showUpgradeScreen() { isShowingUpgrade = true; // Create upgrade options var availableUpgrades = [{ name: 'Damage +20%', apply: function apply() { player.damage = Math.floor(player.damage * 1.2); } }, { name: 'Fire Rate +25%', apply: function apply() { player.fireRate = Math.max(5, Math.floor(player.fireRate * 0.75)); } }, { name: 'Max Health +50', apply: function apply() { player.maxHealth += 50; player.heal(50); } }, { name: 'Speed +20%', apply: function apply() { player.speed = player.speed * 1.2; } }, { name: 'Health Regen', apply: function apply() { player.hasRegen = true; } }]; // Create upgrade UI (simplified - just pick first 3) var upgradeOptions = []; for (var i = 0; i < 3 && i < availableUpgrades.length; i++) { upgradeOptions.push(availableUpgrades[i]); } // Create upgrade buttons var upgradeContainer = new Container(); upgradeContainer.x = 1024; upgradeContainer.y = 1366; var bgPanel = LK.getAsset('upgradePanel', { width: 800, height: 600, color: 0x1a1a1a, shape: 'box' }); upgradeContainer.addChild(bgPanel); var titleText = new Text2('LEVEL UP! Choose an upgrade:', { size: 50, fill: '#00FFFF' }); titleText.anchor.set(0.5, 0.5); titleText.y = -200; upgradeContainer.addChild(titleText); for (var i = 0; i < upgradeOptions.length; i++) { var option = upgradeOptions[i]; var button = LK.getAsset('upgradeButton' + i, { width: 600, height: 100, color: 0x0066CC, shape: 'box' }); button.y = -50 + i * 120; button.upgradeFunction = option.apply; var buttonText = new Text2(option.name, { size: 40, fill: '#ffffff' }); buttonText.anchor.set(0.5, 0.5); button.addChild(buttonText); button.down = function (x, y, obj) { this.upgradeFunction(); game.removeChild(upgradeContainer); isShowingUpgrade = false; }; upgradeContainer.addChild(button); } game.addChild(upgradeContainer); } function spawnZombie() { var type = 'normal'; var rand = Math.random(); if (waveNumber > 3 && rand < 0.3) type = 'fast'; if (waveNumber > 5 && rand < 0.2) type = 'strong'; var zombie = new Zombie(type); // Spawn from edges var edge = Math.floor(Math.random() * 4); if (edge === 0) { // top zombie.x = Math.random() * 2048; zombie.y = -50; } else if (edge === 1) { // right zombie.x = 2098; zombie.y = Math.random() * 2732; } else if (edge === 2) { // bottom zombie.x = Math.random() * 2048; zombie.y = 2782; } else { // left zombie.x = -50; zombie.y = Math.random() * 2732; } zombies.push(zombie); game.addChild(zombie); zombiesSpawned++; } function startNextWave() { waveNumber++; zombiesSpawned = 0; zombiesInWave = Math.floor(10 + waveNumber * 2.5); spawnRate = Math.max(30, 120 - waveNumber * 5); } // Initialize player player = new Player(); player.x = 1024; player.y = 1366; game.addChild(player); initializeUI(); var targetX = player.x; var targetY = player.y; game.move = function (x, y, obj) { if (isShowingUpgrade) return; targetX = x; targetY = y; }; game.down = function (x, y, obj) { if (isShowingUpgrade) return; targetX = x; targetY = y; }; game.update = function () { if (isShowingUpgrade) return; // Move player towards target var dx = targetX - player.x; var dy = targetY - player.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 5) { player.x += dx / distance * player.speed; player.y += dy / distance * player.speed; } // Keep player in bounds player.x = Math.max(40, Math.min(2008, player.x)); player.y = Math.max(40, Math.min(2692, player.y)); // Player health regen if (player.hasRegen && LK.ticks % 120 === 0) { player.heal(5); } // Auto-shoot at nearest zombie if (zombies.length > 0 && LK.ticks - player.lastShot >= player.fireRate) { var nearestZombie = null; var nearestDistance = Infinity; for (var i = 0; i < zombies.length; i++) { if (zombies[i].isDead) continue; var zdx = zombies[i].x - player.x; var zdy = zombies[i].y - player.y; var zdist = Math.sqrt(zdx * zdx + zdy * zdy); if (zdist < nearestDistance) { nearestDistance = zdist; nearestZombie = zombies[i]; } } if (nearestZombie && nearestDistance < 400) { var bullet = new Bullet(); bullet.x = player.x; bullet.y = player.y; var bdx = nearestZombie.x - player.x; var bdy = nearestZombie.y - player.y; var bdist = Math.sqrt(bdx * bdx + bdy * bdy); bullet.direction.x = bdx / bdist; bullet.direction.y = bdy / bdist; bullets.push(bullet); game.addChild(bullet); player.lastShot = LK.ticks; LK.getSound('shoot').play(); } } // Update bullets for (var i = bullets.length - 1; i >= 0; i--) { var bullet = bullets[i]; if (bullet.shouldRemove) { bullet.destroy(); bullets.splice(i, 1); continue; } // Check bullet-zombie collisions for (var j = zombies.length - 1; j >= 0; j--) { var zombie = zombies[j]; if (zombie.isDead) continue; if (bullet.intersects(zombie)) { zombie.takeDamage(bullet.damage); bullet.shouldRemove = true; if (zombie.isDead) { LK.setScore(LK.getScore() + zombie.xpValue); // Create XP orb var xpOrb = new XPOrb(); xpOrb.x = zombie.x; xpOrb.y = zombie.y; xpOrb.value = zombie.xpValue; xpOrbs.push(xpOrb); game.addChild(xpOrb); } break; } } } // Update zombies for (var i = zombies.length - 1; i >= 0; i--) { var zombie = zombies[i]; if (zombie.isDead) { zombie.destroy(); zombies.splice(i, 1); } } // Update XP orbs for (var i = xpOrbs.length - 1; i >= 0; i--) { var orb = xpOrbs[i]; if (orb.shouldRemove) { orb.destroy(); xpOrbs.splice(i, 1); } } // Spawn zombies spawnTimer++; if (spawnTimer >= spawnRate && zombiesSpawned < zombiesInWave) { spawnZombie(); spawnTimer = 0; } // Check if wave is complete if (zombiesSpawned >= zombiesInWave && zombies.length === 0) { startNextWave(); } updateUI(); };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Bullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.damage = player ? player.damage : 20;
self.direction = {
x: 0,
y: 0
};
self.update = function () {
self.x += self.direction.x * self.speed;
self.y += self.direction.y * self.speed;
if (self.x < -50 || self.x > 2098 || self.y < -50 || self.y > 2782) {
self.shouldRemove = true;
}
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 100;
self.maxHealth = 100;
self.damage = 20;
self.fireRate = 30; // Lower = faster
self.speed = 4;
self.lastShot = 0;
self.takeDamage = function (amount) {
self.health -= amount;
if (self.health <= 0) {
self.health = 0;
LK.showGameOver();
} else {
LK.effects.flashObject(self, 0xff0000, 300);
LK.getSound('playerHit').play();
}
};
self.heal = function (amount) {
self.health = Math.min(self.maxHealth, self.health + amount);
};
return self;
});
var XPOrb = Container.expand(function () {
var self = Container.call(this);
var orbGraphics = self.attachAsset('xpOrb', {
anchorX: 0.5,
anchorY: 0.5
});
self.value = 10;
self.collectDistance = 30;
self.update = function () {
var dx = player.x - self.x;
var dy = player.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < self.collectDistance) {
self.x += dx / distance * 6;
self.y += dy / distance * 6;
if (distance < 20) {
currentXP += self.value;
checkLevelUp();
self.shouldRemove = true;
}
}
};
return self;
});
var Zombie = Container.expand(function (type) {
var self = Container.call(this);
self.type = type || 'normal';
self.speed = 1;
self.health = 40;
self.damage = 25;
self.xpValue = 10;
var assetId = 'zombie';
if (self.type === 'fast') {
assetId = 'fastZombie';
self.speed = 2.5;
self.health = 25;
self.damage = 20;
self.xpValue = 15;
} else if (self.type === 'strong') {
assetId = 'strongZombie';
self.speed = 0.8;
self.health = 80;
self.damage = 40;
self.xpValue = 25;
}
var zombieGraphics = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5
});
self.takeDamage = function (amount) {
self.health -= amount;
LK.effects.flashObject(self, 0xffffff, 200);
if (self.health <= 0) {
self.isDead = true;
LK.getSound('zombieHit').play();
}
};
self.update = function () {
if (self.isDead) return;
var dx = player.x - self.x;
var dy = player.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 5) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
} else {
// Attack player
if (!self.lastAttack || LK.ticks - self.lastAttack > 60) {
player.takeDamage(self.damage);
self.lastAttack = LK.ticks;
}
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2C3E50
});
/****
* Game Code
****/
var player;
var zombies = [];
var bullets = [];
var xpOrbs = [];
var currentXP = 0;
var currentLevel = 1;
var xpToNextLevel = 100;
var waveNumber = 1;
var zombiesSpawned = 0;
var zombiesInWave = 10;
var spawnTimer = 0;
var spawnRate = 120; // frames between spawns
var isShowingUpgrade = false;
var upgrades = [];
// UI Elements
var healthBar, xpBar, levelText, waveText, scoreText;
function initializeUI() {
// Health bar background
var healthBg = LK.getAsset('healthBg', {
width: 400,
height: 20,
color: 0x800000,
shape: 'box'
});
healthBg.x = 100;
healthBg.y = 100;
LK.gui.topLeft.addChild(healthBg);
// Health bar
healthBar = LK.getAsset('healthBar', {
width: 400,
height: 20,
color: 0x00ff00,
shape: 'box'
});
healthBar.x = 100;
healthBar.y = 100;
LK.gui.topLeft.addChild(healthBar);
// XP bar background
var xpBg = LK.getAsset('xpBg', {
width: 400,
height: 15,
color: 0x404040,
shape: 'box'
});
xpBg.x = 100;
xpBg.y = 130;
LK.gui.topLeft.addChild(xpBg);
// XP bar
xpBar = LK.getAsset('xpBar', {
width: 400,
height: 15,
color: 0x00FFFF,
shape: 'box'
});
xpBar.x = 100;
xpBar.y = 130;
LK.gui.topLeft.addChild(xpBar);
// Level text
levelText = new Text2('Level: 1', {
size: 40,
fill: '#ffffff'
});
levelText.anchor.set(0, 0);
levelText.x = 100;
levelText.y = 50;
LK.gui.topLeft.addChild(levelText);
// Wave text
waveText = new Text2('Wave: 1', {
size: 30,
fill: '#ffffff'
});
waveText.anchor.set(0.5, 0);
waveText.x = 1024;
waveText.y = 50;
LK.gui.top.addChild(waveText);
// Score text
scoreText = new Text2('Score: 0', {
size: 30,
fill: '#ffffff'
});
scoreText.anchor.set(1, 0);
scoreText.x = 1948;
scoreText.y = 50;
LK.gui.topRight.addChild(scoreText);
}
function updateUI() {
if (healthBar) {
var healthPercent = player.health / player.maxHealth;
healthBar.width = 400 * healthPercent;
}
if (xpBar) {
var xpPercent = currentXP / xpToNextLevel;
xpBar.width = 400 * Math.min(xpPercent, 1);
}
if (levelText) {
levelText.setText('Level: ' + currentLevel);
}
if (waveText) {
waveText.setText('Wave: ' + waveNumber);
}
if (scoreText) {
scoreText.setText('Score: ' + LK.getScore());
}
}
function checkLevelUp() {
if (currentXP >= xpToNextLevel) {
currentXP -= xpToNextLevel;
currentLevel++;
xpToNextLevel = Math.floor(xpToNextLevel * 1.5);
LK.getSound('levelUp').play();
showUpgradeScreen();
}
}
function showUpgradeScreen() {
isShowingUpgrade = true;
// Create upgrade options
var availableUpgrades = [{
name: 'Damage +20%',
apply: function apply() {
player.damage = Math.floor(player.damage * 1.2);
}
}, {
name: 'Fire Rate +25%',
apply: function apply() {
player.fireRate = Math.max(5, Math.floor(player.fireRate * 0.75));
}
}, {
name: 'Max Health +50',
apply: function apply() {
player.maxHealth += 50;
player.heal(50);
}
}, {
name: 'Speed +20%',
apply: function apply() {
player.speed = player.speed * 1.2;
}
}, {
name: 'Health Regen',
apply: function apply() {
player.hasRegen = true;
}
}];
// Create upgrade UI (simplified - just pick first 3)
var upgradeOptions = [];
for (var i = 0; i < 3 && i < availableUpgrades.length; i++) {
upgradeOptions.push(availableUpgrades[i]);
}
// Create upgrade buttons
var upgradeContainer = new Container();
upgradeContainer.x = 1024;
upgradeContainer.y = 1366;
var bgPanel = LK.getAsset('upgradePanel', {
width: 800,
height: 600,
color: 0x1a1a1a,
shape: 'box'
});
upgradeContainer.addChild(bgPanel);
var titleText = new Text2('LEVEL UP! Choose an upgrade:', {
size: 50,
fill: '#00FFFF'
});
titleText.anchor.set(0.5, 0.5);
titleText.y = -200;
upgradeContainer.addChild(titleText);
for (var i = 0; i < upgradeOptions.length; i++) {
var option = upgradeOptions[i];
var button = LK.getAsset('upgradeButton' + i, {
width: 600,
height: 100,
color: 0x0066CC,
shape: 'box'
});
button.y = -50 + i * 120;
button.upgradeFunction = option.apply;
var buttonText = new Text2(option.name, {
size: 40,
fill: '#ffffff'
});
buttonText.anchor.set(0.5, 0.5);
button.addChild(buttonText);
button.down = function (x, y, obj) {
this.upgradeFunction();
game.removeChild(upgradeContainer);
isShowingUpgrade = false;
};
upgradeContainer.addChild(button);
}
game.addChild(upgradeContainer);
}
function spawnZombie() {
var type = 'normal';
var rand = Math.random();
if (waveNumber > 3 && rand < 0.3) type = 'fast';
if (waveNumber > 5 && rand < 0.2) type = 'strong';
var zombie = new Zombie(type);
// Spawn from edges
var edge = Math.floor(Math.random() * 4);
if (edge === 0) {
// top
zombie.x = Math.random() * 2048;
zombie.y = -50;
} else if (edge === 1) {
// right
zombie.x = 2098;
zombie.y = Math.random() * 2732;
} else if (edge === 2) {
// bottom
zombie.x = Math.random() * 2048;
zombie.y = 2782;
} else {
// left
zombie.x = -50;
zombie.y = Math.random() * 2732;
}
zombies.push(zombie);
game.addChild(zombie);
zombiesSpawned++;
}
function startNextWave() {
waveNumber++;
zombiesSpawned = 0;
zombiesInWave = Math.floor(10 + waveNumber * 2.5);
spawnRate = Math.max(30, 120 - waveNumber * 5);
}
// Initialize player
player = new Player();
player.x = 1024;
player.y = 1366;
game.addChild(player);
initializeUI();
var targetX = player.x;
var targetY = player.y;
game.move = function (x, y, obj) {
if (isShowingUpgrade) return;
targetX = x;
targetY = y;
};
game.down = function (x, y, obj) {
if (isShowingUpgrade) return;
targetX = x;
targetY = y;
};
game.update = function () {
if (isShowingUpgrade) return;
// Move player towards target
var dx = targetX - player.x;
var dy = targetY - player.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 5) {
player.x += dx / distance * player.speed;
player.y += dy / distance * player.speed;
}
// Keep player in bounds
player.x = Math.max(40, Math.min(2008, player.x));
player.y = Math.max(40, Math.min(2692, player.y));
// Player health regen
if (player.hasRegen && LK.ticks % 120 === 0) {
player.heal(5);
}
// Auto-shoot at nearest zombie
if (zombies.length > 0 && LK.ticks - player.lastShot >= player.fireRate) {
var nearestZombie = null;
var nearestDistance = Infinity;
for (var i = 0; i < zombies.length; i++) {
if (zombies[i].isDead) continue;
var zdx = zombies[i].x - player.x;
var zdy = zombies[i].y - player.y;
var zdist = Math.sqrt(zdx * zdx + zdy * zdy);
if (zdist < nearestDistance) {
nearestDistance = zdist;
nearestZombie = zombies[i];
}
}
if (nearestZombie && nearestDistance < 400) {
var bullet = new Bullet();
bullet.x = player.x;
bullet.y = player.y;
var bdx = nearestZombie.x - player.x;
var bdy = nearestZombie.y - player.y;
var bdist = Math.sqrt(bdx * bdx + bdy * bdy);
bullet.direction.x = bdx / bdist;
bullet.direction.y = bdy / bdist;
bullets.push(bullet);
game.addChild(bullet);
player.lastShot = LK.ticks;
LK.getSound('shoot').play();
}
}
// Update bullets
for (var i = bullets.length - 1; i >= 0; i--) {
var bullet = bullets[i];
if (bullet.shouldRemove) {
bullet.destroy();
bullets.splice(i, 1);
continue;
}
// Check bullet-zombie collisions
for (var j = zombies.length - 1; j >= 0; j--) {
var zombie = zombies[j];
if (zombie.isDead) continue;
if (bullet.intersects(zombie)) {
zombie.takeDamage(bullet.damage);
bullet.shouldRemove = true;
if (zombie.isDead) {
LK.setScore(LK.getScore() + zombie.xpValue);
// Create XP orb
var xpOrb = new XPOrb();
xpOrb.x = zombie.x;
xpOrb.y = zombie.y;
xpOrb.value = zombie.xpValue;
xpOrbs.push(xpOrb);
game.addChild(xpOrb);
}
break;
}
}
}
// Update zombies
for (var i = zombies.length - 1; i >= 0; i--) {
var zombie = zombies[i];
if (zombie.isDead) {
zombie.destroy();
zombies.splice(i, 1);
}
}
// Update XP orbs
for (var i = xpOrbs.length - 1; i >= 0; i--) {
var orb = xpOrbs[i];
if (orb.shouldRemove) {
orb.destroy();
xpOrbs.splice(i, 1);
}
}
// Spawn zombies
spawnTimer++;
if (spawnTimer >= spawnRate && zombiesSpawned < zombiesInWave) {
spawnZombie();
spawnTimer = 0;
}
// Check if wave is complete
if (zombiesSpawned >= zombiesInWave && zombies.length === 0) {
startNextWave();
}
updateUI();
};