User prompt
Please fix the bug: 'Uncaught TypeError: obj.upgradeFunction is not a function' in or related to this line: 'obj.upgradeFunction();' Line Number: 347
User prompt
Please fix the bug: 'Uncaught TypeError: Cannot read properties of undefined (reading 'apply')' in or related to this line: 'upgradeOptions[index].apply();' Line Number: 348
Code edit (1 edits merged)
Please save this source code
User prompt
Zombie XP Survival
User prompt
zombies killed to xp to levels which get you upgrades to kill more zombies
Initial prompt
zombie killer game kill zombies -
/**** * 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.upgradeIndex = i; 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) { var index = obj.upgradeIndex; upgradeOptions[index].apply(); 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(); };
===================================================================
--- original.js
+++ change.js
@@ -1,6 +1,490 @@
-/****
+/****
+* 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: 0x000000
-});
\ No newline at end of file
+ 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.upgradeIndex = i;
+ 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) {
+ var index = obj.upgradeIndex;
+ upgradeOptions[index].apply();
+ 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();
+};
\ No newline at end of file