/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1", { highscore: 0, weaponLevel: 1 }); /**** * Classes ****/ var Barrel = Container.expand(function (x, y) { var self = Container.call(this); var barrelGraphic = self.attachAsset('barrel', { anchorX: 0.5, anchorY: 0.5 }); self.x = x; self.y = y; self.update = function () { for (var i = bullets.length - 1; i >= 0; i--) { if (bullets[i].intersects(self)) { bullets[i].destroy(); bullets.splice(i, 1); self.explode(); return true; } } return false; }; self.explode = function () { // Check for zombies in explosion area LK.getSound('explode').play(); zombies.forEach(function (zombie) { if (zombie.intersects(self)) { if (zombie.takeDamage(100)) { // Inflict damage to zombie and check if it dies zombies.splice(zombies.indexOf(zombie), 1); // Remove zombie from the array if it dies zombie.destroy(); // Destroy the zombie } } }); var explosion = LK.getAsset('explosionEffect', { anchorX: 0.5, anchorY: 0.5, x: self.x, y: self.y }); game.addChild(explosion); tween(explosion, {}, { duration: 500, onFinish: function onFinish() { explosion.destroy(); } }); self.destroy(); }; return self; }); var Bomb = Container.expand(function (x, y) { var self = Container.call(this); var bombGraphic = self.attachAsset('smallBomb', { anchorX: 0.5, anchorY: 0.5 }); self.x = x; self.y = y; self.explode = function () { // Check for player in red area LK.getSound('explode').play(); if (player.intersects(self)) { player.takeDamage(100); // Inflict damage to player } // Check for zombies in red area zombies.forEach(function (zombie) { if (zombie.intersects(self)) { if (zombie.takeDamage(100)) { // Inflict damage to zombie and check if it dies zombies.splice(zombies.indexOf(zombie), 1); // Remove zombie from the array if it dies zombie.destroy(); // Destroy the zombie } } }); // Check for crates in red area obstacles.forEach(function (obstacle) { if (obstacle.type === 'crate' && obstacle.intersects(self)) { obstacle.destroy(); // Destroy the crate obstacles.splice(obstacles.indexOf(obstacle), 1); // Remove crate from the array } }); // Add explosion effect var explosion = LK.getAsset('explosionEffect', { anchorX: 0.5, anchorY: 0.5, x: self.x, y: self.y }); game.addChild(explosion); tween(explosion, {}, { duration: 500, onFinish: function onFinish() { explosion.destroy(); } }); self.destroy(); // Remove bomb after explosion }; return self; }); var Bullet = Container.expand(function (startX, startY, targetX, targetY, damage) { var self = Container.call(this); var bulletGraphic = self.attachAsset('bullet', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 15; self.damage = damage || 10; // Calculate direction vector var dx = targetX - startX; var dy = targetY - startY; var distance = Math.sqrt(dx * dx + dy * dy); self.vx = dx / distance * self.speed; self.vy = dy / distance * self.speed; self.x = startX; self.y = startY; self.update = function () { self.x += self.vx; self.y += self.vy; bulletGraphic.rotation = Math.atan2(self.vy, self.vx); // Check if bullet is out of bounds if (self.x < 0 || self.x > 2048 || self.y < 0 || self.y > 2732) { return true; // Remove bullet } // Check proximity to barrels for (var i = 0; i < obstacles.length; i++) { if (obstacles[i] instanceof Barrel && Math.abs(self.x - obstacles[i].x) < 50 && Math.abs(self.y - obstacles[i].y) < 50) { return true; // Remove bullet } } // Check proximity to zombies for (var j = 0; j < zombies.length; j++) { if (Math.abs(self.x - zombies[j].x) < 50 && Math.abs(self.y - zombies[j].y) < 50) { return true; // Remove bullet } } return false; }; return self; }); var Missile = Container.expand(function (startX, startY, targetX, targetY, damage) { var self = Container.call(this); var missileGraphic = self.attachAsset('missile', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 10; self.damage = damage || 20; var dx = targetX - startX; var dy = targetY - startY; var distance = Math.sqrt(dx * dx + dy * dy); self.vx = dx / distance * self.speed; self.vy = dy / distance * self.speed; self.x = startX; self.y = startY; self.update = function () { self.x += self.vx; self.y += self.vy; missileGraphic.rotation = Math.atan2(self.vy, self.vx); if (self.x < 0 || self.x > 2048 || self.y < 0 || self.y > 2732) { return true; } // Check for collision with zombies for (var j = 0; j < zombies.length; j++) { if (self.intersects(zombies[j])) { // Trigger explosion effect self.explode(); return true; // Remove missile } } return false; }; self.explode = function () { // Play explosion sound LK.getSound('explode').play(); // Check for zombies in explosion area zombies.forEach(function (zombie) { if (zombie.intersects(self)) { if (zombie.takeDamage(self.damage)) { // Remove zombie if it dies zombies.splice(zombies.indexOf(zombie), 1); zombie.destroy(); } } }); // Add explosion effect var explosion = LK.getAsset('explosionEffect', { anchorX: 0.5, anchorY: 0.5, x: self.x, y: self.y }); game.addChild(explosion); tween(explosion, {}, { duration: 500, onFinish: function onFinish() { explosion.destroy(); } }); // Destroy the missile after explosion self.destroy(); }; return self; }); var Obstacle = Container.expand(function (x, y, type) { var self = Container.call(this); var obstacleGraphic = self.attachAsset(type === 'stone' ? 'stone' : type === 'crate' ? 'crate' : 'bush', { anchorX: 0.5, anchorY: 0.5 }); self.x = x; self.y = y; self.type = type; // Store the type of obstacle self.update = function () { // Check for bullet collision with crate if (self.type === 'crate') { for (var i = bullets.length - 1; i >= 0; i--) { if (bullets[i].intersects(self)) { // Display bullet effect at the point of impact var bulletEffect = LK.getAsset('bulleteffect', { anchorX: 0.5, anchorY: 0.5, x: bullets[i].x, y: bullets[i].y }); game.addChild(bulletEffect); tween(bulletEffect, {}, { duration: 200, onFinish: function onFinish() { bulletEffect.destroy(); } }); bullets[i].destroy(); bullets.splice(i, 1); // 50% chance to drop a pickup when crate is destroyed if (Math.random() < 0.5) { spawnPickup(self.x, self.y); } self.destroy(); return true; // Remove crate } } } return false; }; self.spawnPickup = spawnPickup; // Make spawnPickup accessible return self; }); var Pickup = Container.expand(function (x, y, type) { var self = Container.call(this); var pickupGraphic = self.attachAsset(type === 'health' ? 'healthPack' : 'ammoBox', { anchorX: 0.5, anchorY: 0.5 }); self.type = type; // 'health' or 'ammo' self.x = x; self.y = y; self.lifespan = 10000; // milliseconds until pickup disappears self.creationTime = Date.now(); self.update = function () { // Make pickup pulse var age = Date.now() - self.creationTime; var pulse = Math.sin(age / 200) * 0.2 + 1; self.scale.set(pulse, pulse); // Check if pickup should expire if (age > self.lifespan) { return true; // Remove pickup } return false; }; self.collect = function (player) { LK.getSound('pickup').play(); if (self.type === 'health') { player.heal(25); } else if (self.type === 'ammo') { player.ammo = player.maxAmmo; } }; return self; }); var Player = Container.expand(function () { var self = Container.call(this); var playerWalkingGraphic = self.attachAsset('player1', { // Walking animation anchorX: 0.5, anchorY: 0.5 }); var playerIdleGraphic = self.attachAsset('player', { // Idle animation anchorX: 0.5, anchorY: 0.5 }); playerIdleGraphic.visible = true; // Start with idle animation playerWalkingGraphic.visible = false; // Hide walking animation initially self.health = 100; self.maxHealth = 100; self.ammo = 30; self.maxAmmo = 30; self.reloadTime = 1000; // milliseconds self.isReloading = false; self.speed = 7; self.weaponLevel = storage.weaponLevel || 0.5; // Reduced initial weapon level self.bulletDamage = 10 * self.weaponLevel; // Adjusted bullet damage to prevent one-hit kills in wave 1 self.lastShotTime = 0; self.shootDelay = 300; // milliseconds between shots self.moveTowards = function (targetX, targetY) { var dx = targetX - self.x; var dy = targetY - self.y; var distance = Math.sqrt(dx * dx + dy * dy); // Play walking sound if player is moving if (distance > 0) { LK.getSound('walk').play(); if (!self.walkingTimeout) { self.walkingTimeout = LK.setTimeout(function () { playerWalkingGraphic.visible = true; // Show walking animation playerIdleGraphic.visible = false; // Hide idle animation }, 2000); } } else { playerWalkingGraphic.visible = false; // Hide walking animation playerIdleGraphic.visible = true; // Show idle animation if (self.walkingTimeout) { LK.clearTimeout(self.walkingTimeout); self.walkingTimeout = null; } } if (distance > self.speed) { self.x += dx / distance * self.speed; self.y += dy / distance * self.speed; } else { self.x = targetX; self.y = targetY; } // Keep player within bounds self.x = Math.max(50, Math.min(2048 - 50, self.x)); self.y = Math.max(50, Math.min(2732 - 50, self.y)); }; self.shoot = function (targetX, targetY) { var currentTime = Date.now(); if (!self.infiniteAmmo && self.ammo <= 0) { self.reload(); return null; } if (self.isReloading) { return null; } if (currentTime - self.lastShotTime < self.shootDelay) { return null; } self.lastShotTime = currentTime; self.ammo--; LK.getSound('shoot').play(); LK.getSound('shoot').play(); // Switch to player2 appearance when shooting playerIdleGraphic.visible = false; playerWalkingGraphic.visible = false; var playerShootingGraphic = self.attachAsset('player2', { anchorX: 0.5, anchorY: 0.5 }); self.addChild(playerShootingGraphic); // Revert back to idle or walking appearance after shooting LK.setTimeout(function () { playerShootingGraphic.destroy(); if (self.vx !== 0 || self.vy !== 0) { playerWalkingGraphic.visible = true; } else { playerIdleGraphic.visible = true; } }, self.shootDelay); // 25% chance for a critical hit var criticalHit = Math.random() < 0.25; var damage = criticalHit ? self.bulletDamage * 2 : self.bulletDamage; var bullet = new Bullet(self.x, self.y, targetX, targetY, damage); // Removed critical effect display return bullet; }; self.reload = function () { if (!self.isReloading && self.ammo < self.maxAmmo) { self.isReloading = true; LK.setTimeout(function () { self.ammo = self.maxAmmo; self.isReloading = false; }, self.reloadTime); } }; self.takeDamage = function (damage) { if (self.children.some(function (child) { return child.name === 'shieldEffect'; })) { // If shield is active, prevent damage return false; } LK.getSound('playerHit').play(); LK.effects.flashObject(self, 0xff0000, 300); self.health -= damage; if (self.health <= 0) { self.health = 0; return true; // player died } return false; // player still alive }; self.heal = function (amount) { self.health = Math.min(self.maxHealth, self.health + amount); }; self.upgradeWeapon = function () { self.weaponLevel++; self.bulletDamage = 10 * self.weaponLevel; storage.weaponLevel = self.weaponLevel; }; self.activateMissileMode = function () { var originalShoot = self.shoot; // Store the original shoot function self.missileMode = true; self.shoot = function (targetX, targetY) { var currentTime = Date.now(); if (!self.infiniteAmmo && self.ammo <= 0) { self.reload(); return null; } if (self.isReloading) { return null; } if (currentTime - self.lastShotTime < self.shootDelay) { return null; } self.lastShotTime = currentTime; self.ammo--; LK.getSound('shoot').play(); var missile = new Missile(self.x, self.y, targetX, targetY, self.bulletDamage); return missile; }; LK.setTimeout(function () { self.missileMode = false; self.shoot = originalShoot; }, 20000); // Missile mode lasts for 20 seconds }; self.activateShield = function () { // Logic to activate shield console.log("Shield activated for player!"); var shieldGraphic = self.attachAsset('shieldEffect', { anchorX: 0.5, anchorY: 0.5 }); shieldGraphic.alpha = 0.5; // Make the shield semi-transparent self.addChild(shieldGraphic); // Add shield effect next to health bar var shieldBar = LK.getAsset('shieldEffect', { anchorX: 0, anchorY: 0.5 }); LK.gui.bottomLeft.addChild(shieldBar); shieldBar.x = healthBar.x + healthBar.width + 10; shieldBar.y = healthBar.y; // Add shield countdown text next to shield effect var shieldCountdownText = new Text2('20', { size: 40, fill: 0xFFFFFF }); shieldCountdownText.anchor.set(0, 0.5); LK.gui.bottomLeft.addChild(shieldCountdownText); shieldCountdownText.x = shieldBar.x + shieldBar.width + 10; shieldCountdownText.y = shieldBar.y; var countdown = 20; var countdownInterval = LK.setInterval(function () { countdown--; shieldCountdownText.setText(countdown.toString()); if (countdown <= 0) { LK.clearInterval(countdownInterval); shieldBar.destroy(); shieldCountdownText.destroy(); } }, 1000); // Remove shield after 20 seconds LK.setTimeout(function () { shieldGraphic.destroy(); }, 20000); }; return self; }); var ShieldChest = Container.expand(function (x, y) { var self = Container.call(this); var shieldChestGraphic = self.attachAsset('shieldchest', { anchorX: 0.5, anchorY: 0.5 }); self.x = x; self.y = y; self.update = function () { // Make shield chest pulse var age = Date.now() - self.creationTime; var pulse = Math.sin(age / 200) * 0.2 + 1; self.scale.set(pulse, pulse); return false; }; self.collect = function (player) { LK.getSound('pickup').play(); // Display 'Shield Activated' text on screen var shieldText = new Text2('Shield Activated', { size: 150, fill: 0x000000 }); shieldText.anchor.set(0.5, 0.5); shieldText.x = 2048 / 2; shieldText.y = 2732 / 4; // Position higher on the screen game.addChild(shieldText); tween(shieldText, { alpha: 0 }, { duration: 4000, onFinish: function onFinish() { shieldText.destroy(); } }); player.activateShield(); // Display countdown timer for shield effect var shieldCountdownText = new Text2('20', { size: 60, fill: 0xFFFFFF }); shieldCountdownText.anchor.set(1, 1); shieldCountdownText.x = 2048; shieldCountdownText.y = 2732; LK.gui.bottomRight.addChild(shieldCountdownText); // Display shield effect text in the bottom left corner var shieldEffectText = new Text2('Shield Active', { size: 60, fill: 0xFFFFFF }); shieldEffectText.anchor.set(0, 1); shieldEffectText.x = 20; shieldEffectText.y = 2732; LK.gui.bottomLeft.addChild(shieldEffectText); var countdown = 20; var countdownInterval = LK.setInterval(function () { countdown--; shieldCountdownText.setText(countdown.toString()); if (countdown <= 0) { LK.clearInterval(countdownInterval); shieldCountdownText.destroy(); shieldEffectText.destroy(); // Remove shield effect text } }, 1000); self.destroy(); }; return self; }); var Upgrader = Container.expand(function (x, y) { var self = Container.call(this); var upgraderGraphic = self.attachAsset('Upgrade', { anchorX: 0.5, anchorY: 0.5 }); self.x = x; self.y = y; self.update = function () { // Make upgrader pulse var age = Date.now() - self.creationTime; var pulse = Math.sin(age / 200) * 0.2 + 1; self.scale.set(pulse, pulse); return false; }; self.collect = function (player) { LK.getSound('pickup').play(); // Display 'Rocket Upgrade' text on screen var rocketUpgradeText = new Text2('Rocket Upgrade', { size: 150, fill: 0x000000 }); rocketUpgradeText.anchor.set(0.5, 0.5); rocketUpgradeText.x = 2048 / 2; rocketUpgradeText.y = 2732 / 4; // Position higher on the screen game.addChild(rocketUpgradeText); tween(rocketUpgradeText, { alpha: 0 }, { duration: 4000, onFinish: function onFinish() { rocketUpgradeText.destroy(); } }); player.activateMissileMode(); self.destroy(); }; return self; }); var Zombie = Container.expand(function (x, y, health, speed, damage) { var self = Container.call(this); var zombieGraphic = self.attachAsset('zombie', { anchorX: 0.5, anchorY: 0.5 }); // Add health bar background var healthBarBg = LK.getAsset('healthBarBg', { anchorX: 0.5, anchorY: 1.0, x: 0, y: -zombieGraphic.height / 2 - 10, scaleX: 0.5, // Reduce the width of the health bar background scaleY: 0.5 // Reduce the height of the health bar background }); self.addChild(healthBarBg); // Add health bar var healthBar = LK.getAsset('healthBar', { anchorX: 0.5, anchorY: 1.0, x: 0, y: -zombieGraphic.height / 2 - 10, scaleX: 0.5, // Reduce the width of the health bar scaleY: 0.5 // Reduce the height of the health bar }); self.addChild(healthBar); self.health = health || 30; self.maxHealth = self.health; self.speed = speed || 3; // Increased speed for regular zombies self.damage = damage || 10; self.attackCooldown = 1000; // milliseconds self.lastAttackTime = 0; self.x = x; self.y = y; self.moveTowards = function (targetX, targetY) { var dx = targetX - self.x; var dy = targetY - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 0) { self.x += dx / distance * self.speed; self.y += dy / distance * self.speed; } }; self.attack = function (player) { var currentTime = Date.now(); if (currentTime - self.lastAttackTime >= self.attackCooldown) { self.lastAttackTime = currentTime; return player.takeDamage(self.damage); } return false; }; self.takeDamage = function (damage) { self.health -= damage; // Update health bar scale var healthPercentage = self.health / self.maxHealth; healthBar.scale.x = healthPercentage; // Flash zombie when hit LK.effects.flashObject(self, 0xff0000, 200); LK.getSound('zombieHit').play(); // Change color based on health percentage var healthPercentage = self.health / self.maxHealth; var green = Math.floor(204 * healthPercentage); zombieGraphic.tint = 0 << 16 | green << 8 | 0; if (self.health <= 0) { if (damage > player.bulletDamage) { // Check if it was a critical hit var criticalEffect = LK.getAsset('criticalEffect', { anchorX: 0.5, anchorY: 0.5, x: self.x, y: self.y }); game.addChild(criticalEffect); tween(criticalEffect, {}, { // Use tween to extend the duration duration: 500, // Extend duration to 500ms onFinish: function onFinish() { criticalEffect.destroy(); } }); } // 20% chance to trigger infinite ammo power-up if (Math.random() < 0.2) { player.infiniteAmmo = true; LK.setTimeout(function () { player.infiniteAmmo = false; }, 10000); // 10 seconds duration } // Remove health bar healthBar.destroy(); healthBarBg.destroy(); // Add dead zombie appearance var deadZombie = LK.getAsset('deathzombie', { anchorX: 0.5, anchorY: 0.5, x: self.x, y: self.y }); game.addChild(deadZombie); tween(deadZombie, {}, { duration: 2000, onFinish: function onFinish() { deadZombie.destroy(); } }); return true; // zombie died } return false; // zombie still alive }; return self; }); var SludgeZombie = Zombie.expand(function (x, y, health, speed, damage) { var self = Zombie.call(this, x, y, health, speed, damage); self.removeChild(self.getChildAt(0)); // Remove the default zombie graphic var sludgeZombieGraphic = self.attachAsset('slimeZombie', { anchorX: 0.5, anchorY: 0.5 }); self.speed = speed || 1.5; // Set speed for sludge zombies self.health = health || 40; // Set health for sludge zombies self.damage = damage || 10; // Set damage for sludge zombies // Override takeDamage to spawn new zombies upon death var originalTakeDamage = self.takeDamage; self.takeDamage = function (damage) { var died = originalTakeDamage.call(self, damage); if (died) { for (var i = 0; i < 3; i++) { var newZombie = new Zombie(self.x + Math.random() * 50 - 25, self.y + Math.random() * 50 - 25, 20, 1, 5); zombies.push(newZombie); game.addChild(newZombie); } } return died; }; return self; }); var SlimeZombie = Zombie.expand(function (x, y, health, speed, damage) { var self = Zombie.call(this, x, y, health, speed, damage); self.removeChild(self.getChildAt(0)); // Remove the default zombie graphic var slimeZombieGraphic = self.attachAsset('zombie', { anchorX: 0.5, anchorY: 0.5 }); self.speed = speed || 1.0; // Set speed for slime zombies self.health = health || 30; // Set health for slime zombies self.damage = damage || 5; // Set damage for slime zombies // Override takeDamage to spawn new zombies upon death var originalTakeDamage = self.takeDamage; self.takeDamage = function (damage) { var died = originalTakeDamage.call(self, damage); if (died) { for (var i = 0; i < 3; i++) { var newZombie = new Zombie(self.x + Math.random() * 50 - 25, self.y + Math.random() * 50 - 25, 10, 0.5, 2); zombies.push(newZombie); game.addChild(newZombie); } } return died; }; return self; }); var SkeletonZombie = Zombie.expand(function (x, y, health, speed, damage) { var self = Zombie.call(this, x, y, health, speed, damage); self.removeChild(self.getChildAt(0)); // Remove the default zombie graphic var skeletonZombieGraphic = self.attachAsset('skeletonZombie', { anchorX: 0.5, anchorY: 0.5 }); self.speed = speed || 2.5; // Set speed for skeleton zombies self.health = health || 100; // Increase health for skeleton zombies to prevent one-hit kills self.damage = damage || 15; // Set damage for skeleton zombies return self; }); var FastZombie = Zombie.expand(function (x, y, health, speed, damage) { var self = Zombie.call(this, x, y, health, speed, damage); self.removeChild(self.getChildAt(0)); // Remove the default zombie graphic var fastZombieGraphic = self.attachAsset('fastZombie', { anchorX: 0.5, anchorY: 0.5 }); self.speed = speed || 600; // Further increased speed for fast zombies return self; }); var BossZombie = Zombie.expand(function (x, y, waveNumber) { var self = Zombie.call(this, x, y, bossHealth, bossSpeed, bossDamage); // Calculate boss stats based on wave number var bossHealth = Math.max(5000, 5000 + waveNumber * 500); // Ensure minimum health of 5000 var bossSpeed = 12.0 + waveNumber * 1.5; // Significantly increase base speed and scaling factor for boss var bossDamage = 50 + waveNumber * 10; // Replace the zombie graphic with a boss graphic self.removeChild(self.getChildAt(0)); // Remove the zombie graphic var bossGraphic = self.attachAsset('bossZombie', { anchorX: 0.5, anchorY: 0.5 }); self.scoreValue = 100; // More points for killing a boss return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000000 }); /**** * Game Code ****/ function spawnSlimeZombie(health, speed, damage) { var edge = Math.floor(Math.random() * 4); // 0: top, 1: right, 2: bottom, 3: left var x, y; switch (edge) { case 0: x = Math.random() * 2048; y = -100; break; case 1: x = 2048 + 100; y = Math.random() * 2732; break; case 2: x = Math.random() * 2048; y = 2732 + 100; break; case 3: x = -100; y = Math.random() * 2732; break; } var slimeZombie = new SlimeZombie(x, y, health, speed, damage); zombies.push(slimeZombie); game.addChild(slimeZombie); } function spawnSludgeZombie(health, speed, damage) { var edge = Math.floor(Math.random() * 4); // 0: top, 1: right, 2: bottom, 3: left var x, y; switch (edge) { case 0: x = Math.random() * 2048; y = -100; break; case 1: x = 2048 + 100; y = Math.random() * 2732; break; case 2: x = Math.random() * 2048; y = 2732 + 100; break; case 3: x = -100; y = Math.random() * 2732; break; } var sludgeZombie = new SludgeZombie(x, y, health, speed, damage); zombies.push(sludgeZombie); game.addChild(sludgeZombie); } function spawnSkeletonZombie(health, speed, damage) { var edge = Math.floor(Math.random() * 4); // 0: top, 1: right, 2: bottom, 3: left var x, y; switch (edge) { case 0: x = Math.random() * 2048; y = -100; break; case 1: x = 2048 + 100; y = Math.random() * 2732; break; case 2: x = Math.random() * 2048; y = 2732 + 100; break; case 3: x = -100; y = Math.random() * 2732; break; } var skeletonZombie = new SkeletonZombie(x, y, health, speed, damage); zombies.push(skeletonZombie); game.addChild(skeletonZombie); } function spawnFastZombie(health, speed, damage) { var edge = Math.floor(Math.random() * 4); // 0: top, 1: right, 2: bottom, 3: left var x, y; switch (edge) { case 0: x = Math.random() * 2048; y = -100; break; case 1: x = 2048 + 100; y = Math.random() * 2732; break; case 2: x = Math.random() * 2048; y = 2732 + 100; break; case 3: x = -100; y = Math.random() * 2732; break; } var fastZombie = new FastZombie(x, y, health, speed, damage); zombies.push(fastZombie); game.addChild(fastZombie); } // Game state variables var player; var legendaryChest; // Define legendaryChest in the global scope var obstacles = []; var bullets = []; var bulletHitZombies = {}; // Use a plain object to track which zombies have been hit by which bullets var zombies = []; var missiles = []; var pickups = []; var wave = 1; var zombiesKilled = 0; var score = 0; var gameStartTime; var waveStartTime; var isWaveActive = false; var targetPosition = { x: 0, y: 0 }; var isGameOver = false; // UI elements var scoreText; var waveText; var healthBar; var healthBarBg; var ammoBar; var ammoBarBg; var gameStatusText; var maxScoreText; // Declare maxScoreText in the global scope function initGame() { // Reset game state wave = 1; zombiesKilled = 0; score = 0; bullets = []; zombies = []; pickups = []; isWaveActive = false; isGameOver = false; gameStartTime = Date.now(); // Set background var background = LK.getAsset('backgroundImage', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2 }); game.addChild(background); background.zIndex = -1; // Ensure the background is rendered behind other elements // Play background music LK.playMusic('Ozone', { fade: { start: 0, end: 0.3, duration: 1000 } }); // Create player at center of screen player = new Player(); player.infiniteAmmo = false; // Initialize infinite ammo property player.x = 2048 / 2; player.y = 2732 / 2; // Add max score display maxScoreText = new Text2('Max Score: ' + storage.highscore, { size: 40, fill: 0xFFFFFF }); maxScoreText.anchor.set(0.5, 0); LK.gui.top.addChild(maxScoreText); maxScoreText.x = 0; maxScoreText.y = 80; if (player) { player.lastX = player.x; // Initialize lastX for tracking player.lastY = player.y; // Initialize lastY for tracking } game.addChild(player); // Add obstacles var obstacles = []; // Add barrels at random positions for (var i = 0; i < 5; i++) { var randomX = Math.random() * 2048; var randomY = Math.random() * 2732; var barrel = new Barrel(randomX, randomY); obstacles.push(barrel); game.addChild(barrel); } // Create an object at the right corner of the screen var rightCornerObject = LK.getAsset('crate', { anchorX: 1.0, anchorY: 0.5, x: 2048, y: 1366 }); game.addChild(rightCornerObject); // Add an abandoned car to the top-right corner of the screen var abandonedCarTopRight = LK.getAsset('abandonedCar', { anchorX: 1.0, anchorY: 0.0, x: 2048, y: 0 }); game.addChild(abandonedCarTopRight); abandonedCarTopRight.zIndex = 1; // Ensure the car is rendered in front of the background // Add abandoned cars to their original positions for (var i = 0; i < 3; i++) { var randomX = Math.random() * 2048; var randomY = Math.random() * 2732; var abandonedCar = LK.getAsset('abandonedCar', { anchorX: 0.5, anchorY: 0.5, x: randomX, y: randomY }); game.addChild(abandonedCar); abandonedCar.zIndex = 1; // Ensure the car is rendered in front of the background } // Add decorative elements to the corners obstacles.push(new Obstacle(150, 150, 'bush')); // Near top-left corner obstacles.push(new Obstacle(1898, 150, 'bush')); // Near top-right corner obstacles.push(new Obstacle(150, 2582, 'bush')); // Near bottom-left corner obstacles.push(new Obstacle(1898, 2582, 'bush')); // Near bottom-right corner obstacles.push(new Obstacle(150, 150, 'crate')); // Near top-left corner obstacles.push(new Obstacle(1898, 150, 'crate')); // Near top-right corner obstacles.push(new Obstacle(150, 2582, 'crate')); // Near bottom-left corner obstacles.push(new Obstacle(1898, 2582, 'crate')); // Near bottom-right corner obstacles.push(new Barrel(200, 200)); // Near top-left corner obstacles.push(new Barrel(1848, 200)); // Near top-right corner obstacles.push(new Barrel(200, 2432)); // Near bottom-left corner obstacles.push(new Barrel(1848, 2432)); // Near bottom-right corner // Add more bushes for (var i = 0; i < 50; i++) { // Increased from 20 to 50 for more bushes var randomX = Math.random() * 2048; var randomY = Math.random() * 2732; obstacles.push(new Obstacle(randomX, randomY, 'bush')); } // Add small stone patterns for (var i = 0; i < 100; i++) { // Increased from 50 to 100 for more stones var randomX = Math.random() * 2048; var randomY = Math.random() * 2732; obstacles.push(new Obstacle(randomX, randomY, 'stone')); } // Add more crate obstacles at random positions for (var i = 0; i < 10; i++) { var randomX = Math.random() * 2048; var randomY = Math.random() * 2732; obstacles.push(new Obstacle(randomX, randomY, 'crate')); } obstacles.forEach(function (obstacle) { game.addChild(obstacle); }); // Initialize UI createUI(); // Start first wave startWave(); } function createUI() { // Score text scoreText = new Text2('Score: 0', { size: 60, fill: 0xFFFFFF }); scoreText.anchor.set(0.5, 0); LK.gui.top.addChild(scoreText); scoreText.x = 0; scoreText.y = 20; // Wave text waveText = new Text2('Wave: 1', { size: 60, fill: 0xFFFFFF }); waveText.anchor.set(0, 0); LK.gui.topLeft.addChild(waveText); waveText.x = 20; waveText.y = 20; // Game status text (wave start, game over, etc.) gameStatusText = new Text2('', { size: 100, fill: 0xFFFFFF }); gameStatusText.anchor.set(0.5, 0.5); LK.gui.center.addChild(gameStatusText); gameStatusText.alpha = 0; // Adjust gameStatusText position to be slightly below the wave text gameStatusText.y = waveText.y + 70; // Health bar background healthBarBg = LK.getAsset('healthBarBg', { anchorX: 0, anchorY: 0.5 }); LK.gui.bottomLeft.addChild(healthBarBg); healthBarBg.x = 20; healthBarBg.y = -50; // Health bar healthBar = LK.getAsset('healthBar', { anchorX: 0, anchorY: 0.5 }); LK.gui.bottomLeft.addChild(healthBar); healthBar.x = 20; healthBar.y = -50; // Ammo bar background ammoBarBg = LK.getAsset('ammoBarBg', { anchorX: 0, anchorY: 0.5 }); LK.gui.bottomLeft.addChild(ammoBarBg); ammoBarBg.x = 20; ammoBarBg.y = -100; // Ammo bar ammoBar = LK.getAsset('ammoBar', { anchorX: 0, anchorY: 0.5 }); LK.gui.bottomLeft.addChild(ammoBar); ammoBar.x = 20; ammoBar.y = -100; // Game status text (wave start, game over, etc.) gameStatusText = new Text2('', { size: 100, fill: 0xFFFFFF }); gameStatusText.anchor.set(0.5, 0.5); LK.gui.center.addChild(gameStatusText); gameStatusText.alpha = 0; } function updateUI() { // Update score scoreText.setText('Score: ' + score); // Update player name and score display player.getChildAt(0).text = 'Player ' + (Math.floor(Math.random() * 1000) + 1) + ' | Score: ' + score; // Update max score display maxScoreText.setText('Max Score: ' + storage.highscore); // Update wave waveText.setText('Wave: ' + wave); // Update health bar var healthPercent = player.health / player.maxHealth; healthBar.scale.x = healthPercent; // Update ammo bar var ammoPercent = player.ammo / player.maxAmmo; ammoBar.scale.x = ammoPercent; // Change ammo bar color when reloading if (player.isReloading) { ammoBar.tint = 0xff6600; } else { ammoBar.tint = 0xcc9900; } } function startWave() { waveStartTime = Date.now(); isWaveActive = false; // Display countdown before wave starts var countdown = 7; gameStatusText.setText('Wave ' + wave + ' in ' + countdown); gameStatusText.alpha = 1; var countdownInterval = LK.setInterval(function () { countdown--; if (countdown > 0) { gameStatusText.setText('Wave ' + wave + ' in ' + countdown); } else { LK.clearInterval(countdownInterval); isWaveActive = true; gameStatusText.setText('WAVE ' + wave); tween(gameStatusText, { alpha: 0 }, { duration: 1500, easing: tween.easeIn }); LK.getSound('newWave').play(); // Randomly spawn a ShieldChest or Upgrader if (Math.random() < 0.5) { var shieldChest = new ShieldChest(Math.random() * 2048, Math.random() * 2732); game.addChild(shieldChest); pickups.push(shieldChest); } else { var upgrader = new Upgrader(Math.random() * 2048, Math.random() * 2732); game.addChild(upgrader); pickups.push(upgrader); } } }, 1000); // Spawn zombies based on wave number var zombieCount = wave === 1 ? 10 : wave === 2 ? 3 : wave === 3 ? 4 : 5 + wave * 2; // Increased zombie count for wave 1 var zombieHealth = wave === 1 ? 20 : wave === 2 ? 25 : wave === 3 ? 30 : 30 + wave * 5; // Reduced health for first three waves var zombieSpeed = wave === 1 ? 1.0 : wave === 2 ? 1.1 : wave === 3 ? 1.2 : 2 + wave * 0.1; // Reduced speed for first three waves var zombieDamage = wave === 1 ? 3 : wave === 2 ? 4 : wave === 3 ? 5 : 10 + wave * 2; // Reduced damage for first three waves // Schedule zombie spawns over time // Delay zombie spawning until countdown is complete var spawnInterval = LK.setInterval(function () { if (isWaveActive && zombies.length < zombieCount) { if (wave >= 3 && Math.random() < 0.3 + wave * 0.05) { // Increase chance to spawn a sludge zombie as waves progress spawnSludgeZombie(zombieHealth, zombieSpeed, zombieDamage); } else if (Math.random() < 0.2 - wave * 0.01) { // Decrease chance to spawn a skeleton zombie initially, but increase as waves progress spawnSkeletonZombie(zombieHealth, zombieSpeed, zombieDamage); } else if (Math.random() < 0.2) { // 20% chance to spawn a fast zombie spawnFastZombie(zombieHealth, zombieSpeed * 2, zombieDamage); // Double the speed for fast zombies } else { spawnZombie(zombieHealth, zombieSpeed, zombieDamage); } } else if (zombies.length >= zombieCount) { LK.clearInterval(spawnInterval); } }, 500); // Spawn a boss every 5 waves if (wave % 5 === 0) { LK.setTimeout(function () { spawnBossZombie(); }, 5000); } } function spawnZombie(health, speed, damage) { var edge = Math.floor(Math.random() * 4); // 0: top, 1: right, 2: bottom, 3: left var x, y; switch (edge) { case 0: // top x = Math.random() * 2048; y = -100; break; case 1: // right x = 2048 + 100; y = Math.random() * 2732; break; case 2: // bottom x = Math.random() * 2048; y = 2732 + 100; break; case 3: // left x = -100; y = Math.random() * 2732; break; } var zombie = new Zombie(x, y, health, speed, damage); zombies.push(zombie); game.addChild(zombie); } function spawnBossZombie() { // Spawn boss at a random edge var edge = Math.floor(Math.random() * 4); var x, y; switch (edge) { case 0: // top x = Math.random() * 2048; y = -150; break; case 1: // right x = 2048 + 150; y = Math.random() * 2732; break; case 2: // bottom x = Math.random() * 2048; y = 2732 + 150; break; case 3: // left x = -150; y = Math.random() * 2732; break; } var boss = new BossZombie(x, y, wave); zombies.push(boss); game.addChild(boss); // Play boss appear sound LK.getSound('bossAppear').play(); // Show boss alert gameStatusText.setText('BOSS ZOMBIE APPEARED!'); gameStatusText.alpha = 1; tween(gameStatusText, { alpha: 0 }, { duration: 2000, easing: tween.easeIn }); } function spawnPickup(x, y) { // 30% chance for health, 70% for ammo var type = Math.random() < 0.3 ? 'health' : 'ammo'; var pickup = new Pickup(x, y, type); pickups.push(pickup); game.addChild(pickup); } function checkWaveCompletion() { if (isWaveActive && zombies.length === 0 && !isGameOver && zombiesKilled >= 3 + wave) { isWaveActive = false; // Display wave complete message gameStatusText.setText('WAVE ' + wave + ' COMPLETE!'); gameStatusText.alpha = 1; // Give player a weapon upgrade every 3 waves if (wave % 3 === 0) { player.upgradeWeapon(); // Show upgrade message LK.setTimeout(function () { gameStatusText.setText('WEAPON UPGRADED!'); }, 2000); } // Spawn crates as objects after wave completion for (var i = 0; i < 5; i++) { var randomX = Math.random() * 2048; var randomY = Math.random() * 2732; var crate = new Obstacle(randomX, randomY, 'crate'); obstacles.push(crate); game.addChild(crate); } // Spawn barrels at the end of each wave for (var i = 0; i < 3; i++) { var randomX = Math.random() * 2048; var randomY = Math.random() * 2732; var barrel = new Barrel(randomX, randomY); obstacles.push(barrel); game.addChild(barrel); } // Start next wave after a 5-second delay LK.setTimeout(function () { wave++; gameStatusText.alpha = 0; startWave(); }, 5000); } } // Event handling game.down = function (x, y) { if (isGameOver) { return; } targetPosition.x = x; targetPosition.y = y; // Player shoots at clicked position var bullet = player.shoot(x, y); if (bullet) { bullets.push(bullet); game.addChild(bullet); } }; game.move = function (x, y) { targetPosition.x = x; targetPosition.y = y; }; game.up = function () { // Not needed for this game }; // Main game update loop game.update = function () { if (isGameOver) { return; } // Check for player collision with obstacles obstacles.forEach(function (obstacle) { if (player.intersects(obstacle) && obstacle.type !== 'crate') { // Prevent player from moving through the obstacle, except crates player.x = player.lastX; player.y = player.lastY; } }); // Update player movement if (player) { player.lastX = player.x; // Track lastX before moving player.lastY = player.y; // Track lastY before moving player.moveTowards(targetPosition.x, targetPosition.y); // Check if player is near the legendary chest if (!player.nearLegendaryChest && player.intersects(legendaryChest)) { player.nearLegendaryChest = true; player.legendaryChestTimer = Date.now(); } else if (player.nearLegendaryChest && !player.intersects(legendaryChest)) { player.nearLegendaryChest = false; player.legendaryChestTimer = null; } // Open the legendary chest if the player stands next to it for 3 seconds if (player.nearLegendaryChest && Date.now() - player.legendaryChestTimer >= 3000) { // Logic to open the chest console.log("Legendary chest opened!"); legendaryChest.destroy(); legendaryChest = null; // Set legendaryChest to null after destroying it // Change appearance to legendary2 var legendary2 = LK.getAsset('legendary2', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, visible: true // Make legendary2 visible }); game.addChild(legendary2); // Randomly decide to spawn an upgrader or shield chest if (Math.random() < 0.5) { var upgrader = new Upgrader(2048 / 2, 2732 / 2); game.addChild(upgrader); pickups.push(upgrader); } else { var shieldChest = new ShieldChest(2048 / 2, 2732 / 2); game.addChild(shieldChest); pickups.push(shieldChest); } player.nearLegendaryChest = false; player.legendaryChestTimer = null; } // Regenerate health slowly if not at max health if (player.health < player.maxHealth) { player.health = Math.min(player.maxHealth, player.health + 0.05); // Regenerate 0.05 health per update } } // Update bullets for (var i = bullets.length - 1; i >= 0; i--) { var shouldRemove = bullets[i].update(); if (shouldRemove) { bullets[i].destroy(); bullets.splice(i, 1); continue; } // Check bullet collision with zombies for (var j = zombies.length - 1; j >= 0; j--) { if (bullets[i] && bullets[i].intersects(zombies[j])) { // Display bullet effect at the point of impact var bulletEffect = LK.getAsset('bulleteffect', { anchorX: 0.5, anchorY: 0.5, x: bullets[i].x, y: bullets[i].y }); game.addChild(bulletEffect); tween(bulletEffect, {}, { duration: 200, onFinish: function onFinish() { bulletEffect.destroy(); } }); // Check if this bullet has already hit this zombie if (!bulletHitZombies[bullets[i]]) { bulletHitZombies[bullets[i]] = []; } if (!bulletHitZombies[bullets[i]].includes(zombies[j])) { bulletHitZombies[bullets[i]].push(zombies[j]); var zombieDied = zombies[j].takeDamage(bullets[i].damage); // Remove bullet after hit bullets[i].destroy(); bullets.splice(i, 1); if (zombieDied) { // Increase score and zombie kill counter var scoreToAdd = zombies[j] instanceof BossZombie ? 100 : 10; score += scoreToAdd; zombiesKilled++; // Chance to drop pickup if (Math.random() < 0.3) { spawnPickup(zombies[j].x, zombies[j].y); } // Remove zombie zombies[j].destroy(); zombies.splice(j, 1); } break; } } } } // Update obstacles for (var i = obstacles.length - 1; i >= 0; i--) { var shouldRemove = obstacles[i].update(); if (shouldRemove) { obstacles[i].destroy(); obstacles.splice(i, 1); } } // Update zombies for (var i = zombies.length - 1; i >= 0; i--) { // Move zombie towards player zombies[i].moveTowards(player.x, player.y); // Check if zombie reached player if (zombies[i].intersects(player)) { var playerDied = zombies[i].attack(player); if (playerDied) { handleGameOver(); } } } for (var i = pickups.length - 1; i >= 0; i--) { var shouldRemove = pickups[i].update(); if (shouldRemove) { pickups[i].destroy(); pickups.splice(i, 1); continue; } // Check if player collects pickup if (player.intersects(pickups[i])) { pickups[i].collect(player); pickups[i].destroy(); pickups.splice(i, 1); } } // Update UI updateUI(); // Spawn bomb in red area every 10 seconds, only if wave is active if (isWaveActive && LK.ticks % 600 === 0) { var bombX = Math.random() * 2048; var bombY = Math.random() * 2732; var bomb = new Bomb(bombX, bombY); game.addChild(bomb); // Trigger explosion after 3 seconds LK.setTimeout(function () { bomb.explode(); }, 3000); } // Check if wave is complete checkWaveCompletion(); // Auto-reload when empty and not already reloading if (player.ammo === 0 && !player.isReloading) { player.reload(); } }; function handleGameOver() { isGameOver = true; // Fade out background music LK.playMusic('gameMusic', { fade: { start: 0.3, end: 0, duration: 1000 } }); // Update high score if needed if (score > storage.highscore) { storage.highscore = score; } // Show score in game over dialog LK.setScore(score); // Show game over screen after a short delay LK.setTimeout(function () { LK.showGameOver(); }, 1000); } initGame(); // Start the game immediately
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
highscore: 0,
weaponLevel: 1
});
/****
* Classes
****/
var Barrel = Container.expand(function (x, y) {
var self = Container.call(this);
var barrelGraphic = self.attachAsset('barrel', {
anchorX: 0.5,
anchorY: 0.5
});
self.x = x;
self.y = y;
self.update = function () {
for (var i = bullets.length - 1; i >= 0; i--) {
if (bullets[i].intersects(self)) {
bullets[i].destroy();
bullets.splice(i, 1);
self.explode();
return true;
}
}
return false;
};
self.explode = function () {
// Check for zombies in explosion area
LK.getSound('explode').play();
zombies.forEach(function (zombie) {
if (zombie.intersects(self)) {
if (zombie.takeDamage(100)) {
// Inflict damage to zombie and check if it dies
zombies.splice(zombies.indexOf(zombie), 1); // Remove zombie from the array if it dies
zombie.destroy(); // Destroy the zombie
}
}
});
var explosion = LK.getAsset('explosionEffect', {
anchorX: 0.5,
anchorY: 0.5,
x: self.x,
y: self.y
});
game.addChild(explosion);
tween(explosion, {}, {
duration: 500,
onFinish: function onFinish() {
explosion.destroy();
}
});
self.destroy();
};
return self;
});
var Bomb = Container.expand(function (x, y) {
var self = Container.call(this);
var bombGraphic = self.attachAsset('smallBomb', {
anchorX: 0.5,
anchorY: 0.5
});
self.x = x;
self.y = y;
self.explode = function () {
// Check for player in red area
LK.getSound('explode').play();
if (player.intersects(self)) {
player.takeDamage(100); // Inflict damage to player
}
// Check for zombies in red area
zombies.forEach(function (zombie) {
if (zombie.intersects(self)) {
if (zombie.takeDamage(100)) {
// Inflict damage to zombie and check if it dies
zombies.splice(zombies.indexOf(zombie), 1); // Remove zombie from the array if it dies
zombie.destroy(); // Destroy the zombie
}
}
});
// Check for crates in red area
obstacles.forEach(function (obstacle) {
if (obstacle.type === 'crate' && obstacle.intersects(self)) {
obstacle.destroy(); // Destroy the crate
obstacles.splice(obstacles.indexOf(obstacle), 1); // Remove crate from the array
}
});
// Add explosion effect
var explosion = LK.getAsset('explosionEffect', {
anchorX: 0.5,
anchorY: 0.5,
x: self.x,
y: self.y
});
game.addChild(explosion);
tween(explosion, {}, {
duration: 500,
onFinish: function onFinish() {
explosion.destroy();
}
});
self.destroy(); // Remove bomb after explosion
};
return self;
});
var Bullet = Container.expand(function (startX, startY, targetX, targetY, damage) {
var self = Container.call(this);
var bulletGraphic = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 15;
self.damage = damage || 10;
// Calculate direction vector
var dx = targetX - startX;
var dy = targetY - startY;
var distance = Math.sqrt(dx * dx + dy * dy);
self.vx = dx / distance * self.speed;
self.vy = dy / distance * self.speed;
self.x = startX;
self.y = startY;
self.update = function () {
self.x += self.vx;
self.y += self.vy;
bulletGraphic.rotation = Math.atan2(self.vy, self.vx);
// Check if bullet is out of bounds
if (self.x < 0 || self.x > 2048 || self.y < 0 || self.y > 2732) {
return true; // Remove bullet
}
// Check proximity to barrels
for (var i = 0; i < obstacles.length; i++) {
if (obstacles[i] instanceof Barrel && Math.abs(self.x - obstacles[i].x) < 50 && Math.abs(self.y - obstacles[i].y) < 50) {
return true; // Remove bullet
}
}
// Check proximity to zombies
for (var j = 0; j < zombies.length; j++) {
if (Math.abs(self.x - zombies[j].x) < 50 && Math.abs(self.y - zombies[j].y) < 50) {
return true; // Remove bullet
}
}
return false;
};
return self;
});
var Missile = Container.expand(function (startX, startY, targetX, targetY, damage) {
var self = Container.call(this);
var missileGraphic = self.attachAsset('missile', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 10;
self.damage = damage || 20;
var dx = targetX - startX;
var dy = targetY - startY;
var distance = Math.sqrt(dx * dx + dy * dy);
self.vx = dx / distance * self.speed;
self.vy = dy / distance * self.speed;
self.x = startX;
self.y = startY;
self.update = function () {
self.x += self.vx;
self.y += self.vy;
missileGraphic.rotation = Math.atan2(self.vy, self.vx);
if (self.x < 0 || self.x > 2048 || self.y < 0 || self.y > 2732) {
return true;
}
// Check for collision with zombies
for (var j = 0; j < zombies.length; j++) {
if (self.intersects(zombies[j])) {
// Trigger explosion effect
self.explode();
return true; // Remove missile
}
}
return false;
};
self.explode = function () {
// Play explosion sound
LK.getSound('explode').play();
// Check for zombies in explosion area
zombies.forEach(function (zombie) {
if (zombie.intersects(self)) {
if (zombie.takeDamage(self.damage)) {
// Remove zombie if it dies
zombies.splice(zombies.indexOf(zombie), 1);
zombie.destroy();
}
}
});
// Add explosion effect
var explosion = LK.getAsset('explosionEffect', {
anchorX: 0.5,
anchorY: 0.5,
x: self.x,
y: self.y
});
game.addChild(explosion);
tween(explosion, {}, {
duration: 500,
onFinish: function onFinish() {
explosion.destroy();
}
});
// Destroy the missile after explosion
self.destroy();
};
return self;
});
var Obstacle = Container.expand(function (x, y, type) {
var self = Container.call(this);
var obstacleGraphic = self.attachAsset(type === 'stone' ? 'stone' : type === 'crate' ? 'crate' : 'bush', {
anchorX: 0.5,
anchorY: 0.5
});
self.x = x;
self.y = y;
self.type = type; // Store the type of obstacle
self.update = function () {
// Check for bullet collision with crate
if (self.type === 'crate') {
for (var i = bullets.length - 1; i >= 0; i--) {
if (bullets[i].intersects(self)) {
// Display bullet effect at the point of impact
var bulletEffect = LK.getAsset('bulleteffect', {
anchorX: 0.5,
anchorY: 0.5,
x: bullets[i].x,
y: bullets[i].y
});
game.addChild(bulletEffect);
tween(bulletEffect, {}, {
duration: 200,
onFinish: function onFinish() {
bulletEffect.destroy();
}
});
bullets[i].destroy();
bullets.splice(i, 1);
// 50% chance to drop a pickup when crate is destroyed
if (Math.random() < 0.5) {
spawnPickup(self.x, self.y);
}
self.destroy();
return true; // Remove crate
}
}
}
return false;
};
self.spawnPickup = spawnPickup; // Make spawnPickup accessible
return self;
});
var Pickup = Container.expand(function (x, y, type) {
var self = Container.call(this);
var pickupGraphic = self.attachAsset(type === 'health' ? 'healthPack' : 'ammoBox', {
anchorX: 0.5,
anchorY: 0.5
});
self.type = type; // 'health' or 'ammo'
self.x = x;
self.y = y;
self.lifespan = 10000; // milliseconds until pickup disappears
self.creationTime = Date.now();
self.update = function () {
// Make pickup pulse
var age = Date.now() - self.creationTime;
var pulse = Math.sin(age / 200) * 0.2 + 1;
self.scale.set(pulse, pulse);
// Check if pickup should expire
if (age > self.lifespan) {
return true; // Remove pickup
}
return false;
};
self.collect = function (player) {
LK.getSound('pickup').play();
if (self.type === 'health') {
player.heal(25);
} else if (self.type === 'ammo') {
player.ammo = player.maxAmmo;
}
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var playerWalkingGraphic = self.attachAsset('player1', {
// Walking animation
anchorX: 0.5,
anchorY: 0.5
});
var playerIdleGraphic = self.attachAsset('player', {
// Idle animation
anchorX: 0.5,
anchorY: 0.5
});
playerIdleGraphic.visible = true; // Start with idle animation
playerWalkingGraphic.visible = false; // Hide walking animation initially
self.health = 100;
self.maxHealth = 100;
self.ammo = 30;
self.maxAmmo = 30;
self.reloadTime = 1000; // milliseconds
self.isReloading = false;
self.speed = 7;
self.weaponLevel = storage.weaponLevel || 0.5; // Reduced initial weapon level
self.bulletDamage = 10 * self.weaponLevel; // Adjusted bullet damage to prevent one-hit kills in wave 1
self.lastShotTime = 0;
self.shootDelay = 300; // milliseconds between shots
self.moveTowards = function (targetX, targetY) {
var dx = targetX - self.x;
var dy = targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Play walking sound if player is moving
if (distance > 0) {
LK.getSound('walk').play();
if (!self.walkingTimeout) {
self.walkingTimeout = LK.setTimeout(function () {
playerWalkingGraphic.visible = true; // Show walking animation
playerIdleGraphic.visible = false; // Hide idle animation
}, 2000);
}
} else {
playerWalkingGraphic.visible = false; // Hide walking animation
playerIdleGraphic.visible = true; // Show idle animation
if (self.walkingTimeout) {
LK.clearTimeout(self.walkingTimeout);
self.walkingTimeout = null;
}
}
if (distance > self.speed) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
} else {
self.x = targetX;
self.y = targetY;
}
// Keep player within bounds
self.x = Math.max(50, Math.min(2048 - 50, self.x));
self.y = Math.max(50, Math.min(2732 - 50, self.y));
};
self.shoot = function (targetX, targetY) {
var currentTime = Date.now();
if (!self.infiniteAmmo && self.ammo <= 0) {
self.reload();
return null;
}
if (self.isReloading) {
return null;
}
if (currentTime - self.lastShotTime < self.shootDelay) {
return null;
}
self.lastShotTime = currentTime;
self.ammo--;
LK.getSound('shoot').play();
LK.getSound('shoot').play();
// Switch to player2 appearance when shooting
playerIdleGraphic.visible = false;
playerWalkingGraphic.visible = false;
var playerShootingGraphic = self.attachAsset('player2', {
anchorX: 0.5,
anchorY: 0.5
});
self.addChild(playerShootingGraphic);
// Revert back to idle or walking appearance after shooting
LK.setTimeout(function () {
playerShootingGraphic.destroy();
if (self.vx !== 0 || self.vy !== 0) {
playerWalkingGraphic.visible = true;
} else {
playerIdleGraphic.visible = true;
}
}, self.shootDelay);
// 25% chance for a critical hit
var criticalHit = Math.random() < 0.25;
var damage = criticalHit ? self.bulletDamage * 2 : self.bulletDamage;
var bullet = new Bullet(self.x, self.y, targetX, targetY, damage);
// Removed critical effect display
return bullet;
};
self.reload = function () {
if (!self.isReloading && self.ammo < self.maxAmmo) {
self.isReloading = true;
LK.setTimeout(function () {
self.ammo = self.maxAmmo;
self.isReloading = false;
}, self.reloadTime);
}
};
self.takeDamage = function (damage) {
if (self.children.some(function (child) {
return child.name === 'shieldEffect';
})) {
// If shield is active, prevent damage
return false;
}
LK.getSound('playerHit').play();
LK.effects.flashObject(self, 0xff0000, 300);
self.health -= damage;
if (self.health <= 0) {
self.health = 0;
return true; // player died
}
return false; // player still alive
};
self.heal = function (amount) {
self.health = Math.min(self.maxHealth, self.health + amount);
};
self.upgradeWeapon = function () {
self.weaponLevel++;
self.bulletDamage = 10 * self.weaponLevel;
storage.weaponLevel = self.weaponLevel;
};
self.activateMissileMode = function () {
var originalShoot = self.shoot; // Store the original shoot function
self.missileMode = true;
self.shoot = function (targetX, targetY) {
var currentTime = Date.now();
if (!self.infiniteAmmo && self.ammo <= 0) {
self.reload();
return null;
}
if (self.isReloading) {
return null;
}
if (currentTime - self.lastShotTime < self.shootDelay) {
return null;
}
self.lastShotTime = currentTime;
self.ammo--;
LK.getSound('shoot').play();
var missile = new Missile(self.x, self.y, targetX, targetY, self.bulletDamage);
return missile;
};
LK.setTimeout(function () {
self.missileMode = false;
self.shoot = originalShoot;
}, 20000); // Missile mode lasts for 20 seconds
};
self.activateShield = function () {
// Logic to activate shield
console.log("Shield activated for player!");
var shieldGraphic = self.attachAsset('shieldEffect', {
anchorX: 0.5,
anchorY: 0.5
});
shieldGraphic.alpha = 0.5; // Make the shield semi-transparent
self.addChild(shieldGraphic);
// Add shield effect next to health bar
var shieldBar = LK.getAsset('shieldEffect', {
anchorX: 0,
anchorY: 0.5
});
LK.gui.bottomLeft.addChild(shieldBar);
shieldBar.x = healthBar.x + healthBar.width + 10;
shieldBar.y = healthBar.y;
// Add shield countdown text next to shield effect
var shieldCountdownText = new Text2('20', {
size: 40,
fill: 0xFFFFFF
});
shieldCountdownText.anchor.set(0, 0.5);
LK.gui.bottomLeft.addChild(shieldCountdownText);
shieldCountdownText.x = shieldBar.x + shieldBar.width + 10;
shieldCountdownText.y = shieldBar.y;
var countdown = 20;
var countdownInterval = LK.setInterval(function () {
countdown--;
shieldCountdownText.setText(countdown.toString());
if (countdown <= 0) {
LK.clearInterval(countdownInterval);
shieldBar.destroy();
shieldCountdownText.destroy();
}
}, 1000);
// Remove shield after 20 seconds
LK.setTimeout(function () {
shieldGraphic.destroy();
}, 20000);
};
return self;
});
var ShieldChest = Container.expand(function (x, y) {
var self = Container.call(this);
var shieldChestGraphic = self.attachAsset('shieldchest', {
anchorX: 0.5,
anchorY: 0.5
});
self.x = x;
self.y = y;
self.update = function () {
// Make shield chest pulse
var age = Date.now() - self.creationTime;
var pulse = Math.sin(age / 200) * 0.2 + 1;
self.scale.set(pulse, pulse);
return false;
};
self.collect = function (player) {
LK.getSound('pickup').play();
// Display 'Shield Activated' text on screen
var shieldText = new Text2('Shield Activated', {
size: 150,
fill: 0x000000
});
shieldText.anchor.set(0.5, 0.5);
shieldText.x = 2048 / 2;
shieldText.y = 2732 / 4; // Position higher on the screen
game.addChild(shieldText);
tween(shieldText, {
alpha: 0
}, {
duration: 4000,
onFinish: function onFinish() {
shieldText.destroy();
}
});
player.activateShield();
// Display countdown timer for shield effect
var shieldCountdownText = new Text2('20', {
size: 60,
fill: 0xFFFFFF
});
shieldCountdownText.anchor.set(1, 1);
shieldCountdownText.x = 2048;
shieldCountdownText.y = 2732;
LK.gui.bottomRight.addChild(shieldCountdownText);
// Display shield effect text in the bottom left corner
var shieldEffectText = new Text2('Shield Active', {
size: 60,
fill: 0xFFFFFF
});
shieldEffectText.anchor.set(0, 1);
shieldEffectText.x = 20;
shieldEffectText.y = 2732;
LK.gui.bottomLeft.addChild(shieldEffectText);
var countdown = 20;
var countdownInterval = LK.setInterval(function () {
countdown--;
shieldCountdownText.setText(countdown.toString());
if (countdown <= 0) {
LK.clearInterval(countdownInterval);
shieldCountdownText.destroy();
shieldEffectText.destroy(); // Remove shield effect text
}
}, 1000);
self.destroy();
};
return self;
});
var Upgrader = Container.expand(function (x, y) {
var self = Container.call(this);
var upgraderGraphic = self.attachAsset('Upgrade', {
anchorX: 0.5,
anchorY: 0.5
});
self.x = x;
self.y = y;
self.update = function () {
// Make upgrader pulse
var age = Date.now() - self.creationTime;
var pulse = Math.sin(age / 200) * 0.2 + 1;
self.scale.set(pulse, pulse);
return false;
};
self.collect = function (player) {
LK.getSound('pickup').play();
// Display 'Rocket Upgrade' text on screen
var rocketUpgradeText = new Text2('Rocket Upgrade', {
size: 150,
fill: 0x000000
});
rocketUpgradeText.anchor.set(0.5, 0.5);
rocketUpgradeText.x = 2048 / 2;
rocketUpgradeText.y = 2732 / 4; // Position higher on the screen
game.addChild(rocketUpgradeText);
tween(rocketUpgradeText, {
alpha: 0
}, {
duration: 4000,
onFinish: function onFinish() {
rocketUpgradeText.destroy();
}
});
player.activateMissileMode();
self.destroy();
};
return self;
});
var Zombie = Container.expand(function (x, y, health, speed, damage) {
var self = Container.call(this);
var zombieGraphic = self.attachAsset('zombie', {
anchorX: 0.5,
anchorY: 0.5
});
// Add health bar background
var healthBarBg = LK.getAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 1.0,
x: 0,
y: -zombieGraphic.height / 2 - 10,
scaleX: 0.5,
// Reduce the width of the health bar background
scaleY: 0.5 // Reduce the height of the health bar background
});
self.addChild(healthBarBg);
// Add health bar
var healthBar = LK.getAsset('healthBar', {
anchorX: 0.5,
anchorY: 1.0,
x: 0,
y: -zombieGraphic.height / 2 - 10,
scaleX: 0.5,
// Reduce the width of the health bar
scaleY: 0.5 // Reduce the height of the health bar
});
self.addChild(healthBar);
self.health = health || 30;
self.maxHealth = self.health;
self.speed = speed || 3; // Increased speed for regular zombies
self.damage = damage || 10;
self.attackCooldown = 1000; // milliseconds
self.lastAttackTime = 0;
self.x = x;
self.y = y;
self.moveTowards = function (targetX, targetY) {
var dx = targetX - self.x;
var dy = targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
};
self.attack = function (player) {
var currentTime = Date.now();
if (currentTime - self.lastAttackTime >= self.attackCooldown) {
self.lastAttackTime = currentTime;
return player.takeDamage(self.damage);
}
return false;
};
self.takeDamage = function (damage) {
self.health -= damage;
// Update health bar scale
var healthPercentage = self.health / self.maxHealth;
healthBar.scale.x = healthPercentage;
// Flash zombie when hit
LK.effects.flashObject(self, 0xff0000, 200);
LK.getSound('zombieHit').play();
// Change color based on health percentage
var healthPercentage = self.health / self.maxHealth;
var green = Math.floor(204 * healthPercentage);
zombieGraphic.tint = 0 << 16 | green << 8 | 0;
if (self.health <= 0) {
if (damage > player.bulletDamage) {
// Check if it was a critical hit
var criticalEffect = LK.getAsset('criticalEffect', {
anchorX: 0.5,
anchorY: 0.5,
x: self.x,
y: self.y
});
game.addChild(criticalEffect);
tween(criticalEffect, {}, {
// Use tween to extend the duration
duration: 500,
// Extend duration to 500ms
onFinish: function onFinish() {
criticalEffect.destroy();
}
});
}
// 20% chance to trigger infinite ammo power-up
if (Math.random() < 0.2) {
player.infiniteAmmo = true;
LK.setTimeout(function () {
player.infiniteAmmo = false;
}, 10000); // 10 seconds duration
}
// Remove health bar
healthBar.destroy();
healthBarBg.destroy();
// Add dead zombie appearance
var deadZombie = LK.getAsset('deathzombie', {
anchorX: 0.5,
anchorY: 0.5,
x: self.x,
y: self.y
});
game.addChild(deadZombie);
tween(deadZombie, {}, {
duration: 2000,
onFinish: function onFinish() {
deadZombie.destroy();
}
});
return true; // zombie died
}
return false; // zombie still alive
};
return self;
});
var SludgeZombie = Zombie.expand(function (x, y, health, speed, damage) {
var self = Zombie.call(this, x, y, health, speed, damage);
self.removeChild(self.getChildAt(0)); // Remove the default zombie graphic
var sludgeZombieGraphic = self.attachAsset('slimeZombie', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = speed || 1.5; // Set speed for sludge zombies
self.health = health || 40; // Set health for sludge zombies
self.damage = damage || 10; // Set damage for sludge zombies
// Override takeDamage to spawn new zombies upon death
var originalTakeDamage = self.takeDamage;
self.takeDamage = function (damage) {
var died = originalTakeDamage.call(self, damage);
if (died) {
for (var i = 0; i < 3; i++) {
var newZombie = new Zombie(self.x + Math.random() * 50 - 25, self.y + Math.random() * 50 - 25, 20, 1, 5);
zombies.push(newZombie);
game.addChild(newZombie);
}
}
return died;
};
return self;
});
var SlimeZombie = Zombie.expand(function (x, y, health, speed, damage) {
var self = Zombie.call(this, x, y, health, speed, damage);
self.removeChild(self.getChildAt(0)); // Remove the default zombie graphic
var slimeZombieGraphic = self.attachAsset('zombie', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = speed || 1.0; // Set speed for slime zombies
self.health = health || 30; // Set health for slime zombies
self.damage = damage || 5; // Set damage for slime zombies
// Override takeDamage to spawn new zombies upon death
var originalTakeDamage = self.takeDamage;
self.takeDamage = function (damage) {
var died = originalTakeDamage.call(self, damage);
if (died) {
for (var i = 0; i < 3; i++) {
var newZombie = new Zombie(self.x + Math.random() * 50 - 25, self.y + Math.random() * 50 - 25, 10, 0.5, 2);
zombies.push(newZombie);
game.addChild(newZombie);
}
}
return died;
};
return self;
});
var SkeletonZombie = Zombie.expand(function (x, y, health, speed, damage) {
var self = Zombie.call(this, x, y, health, speed, damage);
self.removeChild(self.getChildAt(0)); // Remove the default zombie graphic
var skeletonZombieGraphic = self.attachAsset('skeletonZombie', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = speed || 2.5; // Set speed for skeleton zombies
self.health = health || 100; // Increase health for skeleton zombies to prevent one-hit kills
self.damage = damage || 15; // Set damage for skeleton zombies
return self;
});
var FastZombie = Zombie.expand(function (x, y, health, speed, damage) {
var self = Zombie.call(this, x, y, health, speed, damage);
self.removeChild(self.getChildAt(0)); // Remove the default zombie graphic
var fastZombieGraphic = self.attachAsset('fastZombie', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = speed || 600; // Further increased speed for fast zombies
return self;
});
var BossZombie = Zombie.expand(function (x, y, waveNumber) {
var self = Zombie.call(this, x, y, bossHealth, bossSpeed, bossDamage);
// Calculate boss stats based on wave number
var bossHealth = Math.max(5000, 5000 + waveNumber * 500); // Ensure minimum health of 5000
var bossSpeed = 12.0 + waveNumber * 1.5; // Significantly increase base speed and scaling factor for boss
var bossDamage = 50 + waveNumber * 10;
// Replace the zombie graphic with a boss graphic
self.removeChild(self.getChildAt(0)); // Remove the zombie graphic
var bossGraphic = self.attachAsset('bossZombie', {
anchorX: 0.5,
anchorY: 0.5
});
self.scoreValue = 100; // More points for killing a boss
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
function spawnSlimeZombie(health, speed, damage) {
var edge = Math.floor(Math.random() * 4); // 0: top, 1: right, 2: bottom, 3: left
var x, y;
switch (edge) {
case 0:
x = Math.random() * 2048;
y = -100;
break;
case 1:
x = 2048 + 100;
y = Math.random() * 2732;
break;
case 2:
x = Math.random() * 2048;
y = 2732 + 100;
break;
case 3:
x = -100;
y = Math.random() * 2732;
break;
}
var slimeZombie = new SlimeZombie(x, y, health, speed, damage);
zombies.push(slimeZombie);
game.addChild(slimeZombie);
}
function spawnSludgeZombie(health, speed, damage) {
var edge = Math.floor(Math.random() * 4); // 0: top, 1: right, 2: bottom, 3: left
var x, y;
switch (edge) {
case 0:
x = Math.random() * 2048;
y = -100;
break;
case 1:
x = 2048 + 100;
y = Math.random() * 2732;
break;
case 2:
x = Math.random() * 2048;
y = 2732 + 100;
break;
case 3:
x = -100;
y = Math.random() * 2732;
break;
}
var sludgeZombie = new SludgeZombie(x, y, health, speed, damage);
zombies.push(sludgeZombie);
game.addChild(sludgeZombie);
}
function spawnSkeletonZombie(health, speed, damage) {
var edge = Math.floor(Math.random() * 4); // 0: top, 1: right, 2: bottom, 3: left
var x, y;
switch (edge) {
case 0:
x = Math.random() * 2048;
y = -100;
break;
case 1:
x = 2048 + 100;
y = Math.random() * 2732;
break;
case 2:
x = Math.random() * 2048;
y = 2732 + 100;
break;
case 3:
x = -100;
y = Math.random() * 2732;
break;
}
var skeletonZombie = new SkeletonZombie(x, y, health, speed, damage);
zombies.push(skeletonZombie);
game.addChild(skeletonZombie);
}
function spawnFastZombie(health, speed, damage) {
var edge = Math.floor(Math.random() * 4); // 0: top, 1: right, 2: bottom, 3: left
var x, y;
switch (edge) {
case 0:
x = Math.random() * 2048;
y = -100;
break;
case 1:
x = 2048 + 100;
y = Math.random() * 2732;
break;
case 2:
x = Math.random() * 2048;
y = 2732 + 100;
break;
case 3:
x = -100;
y = Math.random() * 2732;
break;
}
var fastZombie = new FastZombie(x, y, health, speed, damage);
zombies.push(fastZombie);
game.addChild(fastZombie);
}
// Game state variables
var player;
var legendaryChest; // Define legendaryChest in the global scope
var obstacles = [];
var bullets = [];
var bulletHitZombies = {}; // Use a plain object to track which zombies have been hit by which bullets
var zombies = [];
var missiles = [];
var pickups = [];
var wave = 1;
var zombiesKilled = 0;
var score = 0;
var gameStartTime;
var waveStartTime;
var isWaveActive = false;
var targetPosition = {
x: 0,
y: 0
};
var isGameOver = false;
// UI elements
var scoreText;
var waveText;
var healthBar;
var healthBarBg;
var ammoBar;
var ammoBarBg;
var gameStatusText;
var maxScoreText; // Declare maxScoreText in the global scope
function initGame() {
// Reset game state
wave = 1;
zombiesKilled = 0;
score = 0;
bullets = [];
zombies = [];
pickups = [];
isWaveActive = false;
isGameOver = false;
gameStartTime = Date.now();
// Set background
var background = LK.getAsset('backgroundImage', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2
});
game.addChild(background);
background.zIndex = -1; // Ensure the background is rendered behind other elements
// Play background music
LK.playMusic('Ozone', {
fade: {
start: 0,
end: 0.3,
duration: 1000
}
});
// Create player at center of screen
player = new Player();
player.infiniteAmmo = false; // Initialize infinite ammo property
player.x = 2048 / 2;
player.y = 2732 / 2;
// Add max score display
maxScoreText = new Text2('Max Score: ' + storage.highscore, {
size: 40,
fill: 0xFFFFFF
});
maxScoreText.anchor.set(0.5, 0);
LK.gui.top.addChild(maxScoreText);
maxScoreText.x = 0;
maxScoreText.y = 80;
if (player) {
player.lastX = player.x; // Initialize lastX for tracking
player.lastY = player.y; // Initialize lastY for tracking
}
game.addChild(player);
// Add obstacles
var obstacles = [];
// Add barrels at random positions
for (var i = 0; i < 5; i++) {
var randomX = Math.random() * 2048;
var randomY = Math.random() * 2732;
var barrel = new Barrel(randomX, randomY);
obstacles.push(barrel);
game.addChild(barrel);
}
// Create an object at the right corner of the screen
var rightCornerObject = LK.getAsset('crate', {
anchorX: 1.0,
anchorY: 0.5,
x: 2048,
y: 1366
});
game.addChild(rightCornerObject);
// Add an abandoned car to the top-right corner of the screen
var abandonedCarTopRight = LK.getAsset('abandonedCar', {
anchorX: 1.0,
anchorY: 0.0,
x: 2048,
y: 0
});
game.addChild(abandonedCarTopRight);
abandonedCarTopRight.zIndex = 1; // Ensure the car is rendered in front of the background
// Add abandoned cars to their original positions
for (var i = 0; i < 3; i++) {
var randomX = Math.random() * 2048;
var randomY = Math.random() * 2732;
var abandonedCar = LK.getAsset('abandonedCar', {
anchorX: 0.5,
anchorY: 0.5,
x: randomX,
y: randomY
});
game.addChild(abandonedCar);
abandonedCar.zIndex = 1; // Ensure the car is rendered in front of the background
}
// Add decorative elements to the corners
obstacles.push(new Obstacle(150, 150, 'bush')); // Near top-left corner
obstacles.push(new Obstacle(1898, 150, 'bush')); // Near top-right corner
obstacles.push(new Obstacle(150, 2582, 'bush')); // Near bottom-left corner
obstacles.push(new Obstacle(1898, 2582, 'bush')); // Near bottom-right corner
obstacles.push(new Obstacle(150, 150, 'crate')); // Near top-left corner
obstacles.push(new Obstacle(1898, 150, 'crate')); // Near top-right corner
obstacles.push(new Obstacle(150, 2582, 'crate')); // Near bottom-left corner
obstacles.push(new Obstacle(1898, 2582, 'crate')); // Near bottom-right corner
obstacles.push(new Barrel(200, 200)); // Near top-left corner
obstacles.push(new Barrel(1848, 200)); // Near top-right corner
obstacles.push(new Barrel(200, 2432)); // Near bottom-left corner
obstacles.push(new Barrel(1848, 2432)); // Near bottom-right corner
// Add more bushes
for (var i = 0; i < 50; i++) {
// Increased from 20 to 50 for more bushes
var randomX = Math.random() * 2048;
var randomY = Math.random() * 2732;
obstacles.push(new Obstacle(randomX, randomY, 'bush'));
}
// Add small stone patterns
for (var i = 0; i < 100; i++) {
// Increased from 50 to 100 for more stones
var randomX = Math.random() * 2048;
var randomY = Math.random() * 2732;
obstacles.push(new Obstacle(randomX, randomY, 'stone'));
}
// Add more crate obstacles at random positions
for (var i = 0; i < 10; i++) {
var randomX = Math.random() * 2048;
var randomY = Math.random() * 2732;
obstacles.push(new Obstacle(randomX, randomY, 'crate'));
}
obstacles.forEach(function (obstacle) {
game.addChild(obstacle);
});
// Initialize UI
createUI();
// Start first wave
startWave();
}
function createUI() {
// Score text
scoreText = new Text2('Score: 0', {
size: 60,
fill: 0xFFFFFF
});
scoreText.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreText);
scoreText.x = 0;
scoreText.y = 20;
// Wave text
waveText = new Text2('Wave: 1', {
size: 60,
fill: 0xFFFFFF
});
waveText.anchor.set(0, 0);
LK.gui.topLeft.addChild(waveText);
waveText.x = 20;
waveText.y = 20;
// Game status text (wave start, game over, etc.)
gameStatusText = new Text2('', {
size: 100,
fill: 0xFFFFFF
});
gameStatusText.anchor.set(0.5, 0.5);
LK.gui.center.addChild(gameStatusText);
gameStatusText.alpha = 0;
// Adjust gameStatusText position to be slightly below the wave text
gameStatusText.y = waveText.y + 70;
// Health bar background
healthBarBg = LK.getAsset('healthBarBg', {
anchorX: 0,
anchorY: 0.5
});
LK.gui.bottomLeft.addChild(healthBarBg);
healthBarBg.x = 20;
healthBarBg.y = -50;
// Health bar
healthBar = LK.getAsset('healthBar', {
anchorX: 0,
anchorY: 0.5
});
LK.gui.bottomLeft.addChild(healthBar);
healthBar.x = 20;
healthBar.y = -50;
// Ammo bar background
ammoBarBg = LK.getAsset('ammoBarBg', {
anchorX: 0,
anchorY: 0.5
});
LK.gui.bottomLeft.addChild(ammoBarBg);
ammoBarBg.x = 20;
ammoBarBg.y = -100;
// Ammo bar
ammoBar = LK.getAsset('ammoBar', {
anchorX: 0,
anchorY: 0.5
});
LK.gui.bottomLeft.addChild(ammoBar);
ammoBar.x = 20;
ammoBar.y = -100;
// Game status text (wave start, game over, etc.)
gameStatusText = new Text2('', {
size: 100,
fill: 0xFFFFFF
});
gameStatusText.anchor.set(0.5, 0.5);
LK.gui.center.addChild(gameStatusText);
gameStatusText.alpha = 0;
}
function updateUI() {
// Update score
scoreText.setText('Score: ' + score);
// Update player name and score display
player.getChildAt(0).text = 'Player ' + (Math.floor(Math.random() * 1000) + 1) + ' | Score: ' + score;
// Update max score display
maxScoreText.setText('Max Score: ' + storage.highscore);
// Update wave
waveText.setText('Wave: ' + wave);
// Update health bar
var healthPercent = player.health / player.maxHealth;
healthBar.scale.x = healthPercent;
// Update ammo bar
var ammoPercent = player.ammo / player.maxAmmo;
ammoBar.scale.x = ammoPercent;
// Change ammo bar color when reloading
if (player.isReloading) {
ammoBar.tint = 0xff6600;
} else {
ammoBar.tint = 0xcc9900;
}
}
function startWave() {
waveStartTime = Date.now();
isWaveActive = false;
// Display countdown before wave starts
var countdown = 7;
gameStatusText.setText('Wave ' + wave + ' in ' + countdown);
gameStatusText.alpha = 1;
var countdownInterval = LK.setInterval(function () {
countdown--;
if (countdown > 0) {
gameStatusText.setText('Wave ' + wave + ' in ' + countdown);
} else {
LK.clearInterval(countdownInterval);
isWaveActive = true;
gameStatusText.setText('WAVE ' + wave);
tween(gameStatusText, {
alpha: 0
}, {
duration: 1500,
easing: tween.easeIn
});
LK.getSound('newWave').play();
// Randomly spawn a ShieldChest or Upgrader
if (Math.random() < 0.5) {
var shieldChest = new ShieldChest(Math.random() * 2048, Math.random() * 2732);
game.addChild(shieldChest);
pickups.push(shieldChest);
} else {
var upgrader = new Upgrader(Math.random() * 2048, Math.random() * 2732);
game.addChild(upgrader);
pickups.push(upgrader);
}
}
}, 1000);
// Spawn zombies based on wave number
var zombieCount = wave === 1 ? 10 : wave === 2 ? 3 : wave === 3 ? 4 : 5 + wave * 2; // Increased zombie count for wave 1
var zombieHealth = wave === 1 ? 20 : wave === 2 ? 25 : wave === 3 ? 30 : 30 + wave * 5; // Reduced health for first three waves
var zombieSpeed = wave === 1 ? 1.0 : wave === 2 ? 1.1 : wave === 3 ? 1.2 : 2 + wave * 0.1; // Reduced speed for first three waves
var zombieDamage = wave === 1 ? 3 : wave === 2 ? 4 : wave === 3 ? 5 : 10 + wave * 2; // Reduced damage for first three waves
// Schedule zombie spawns over time
// Delay zombie spawning until countdown is complete
var spawnInterval = LK.setInterval(function () {
if (isWaveActive && zombies.length < zombieCount) {
if (wave >= 3 && Math.random() < 0.3 + wave * 0.05) {
// Increase chance to spawn a sludge zombie as waves progress
spawnSludgeZombie(zombieHealth, zombieSpeed, zombieDamage);
} else if (Math.random() < 0.2 - wave * 0.01) {
// Decrease chance to spawn a skeleton zombie initially, but increase as waves progress
spawnSkeletonZombie(zombieHealth, zombieSpeed, zombieDamage);
} else if (Math.random() < 0.2) {
// 20% chance to spawn a fast zombie
spawnFastZombie(zombieHealth, zombieSpeed * 2, zombieDamage); // Double the speed for fast zombies
} else {
spawnZombie(zombieHealth, zombieSpeed, zombieDamage);
}
} else if (zombies.length >= zombieCount) {
LK.clearInterval(spawnInterval);
}
}, 500);
// Spawn a boss every 5 waves
if (wave % 5 === 0) {
LK.setTimeout(function () {
spawnBossZombie();
}, 5000);
}
}
function spawnZombie(health, speed, damage) {
var edge = Math.floor(Math.random() * 4); // 0: top, 1: right, 2: bottom, 3: left
var x, y;
switch (edge) {
case 0:
// top
x = Math.random() * 2048;
y = -100;
break;
case 1:
// right
x = 2048 + 100;
y = Math.random() * 2732;
break;
case 2:
// bottom
x = Math.random() * 2048;
y = 2732 + 100;
break;
case 3:
// left
x = -100;
y = Math.random() * 2732;
break;
}
var zombie = new Zombie(x, y, health, speed, damage);
zombies.push(zombie);
game.addChild(zombie);
}
function spawnBossZombie() {
// Spawn boss at a random edge
var edge = Math.floor(Math.random() * 4);
var x, y;
switch (edge) {
case 0:
// top
x = Math.random() * 2048;
y = -150;
break;
case 1:
// right
x = 2048 + 150;
y = Math.random() * 2732;
break;
case 2:
// bottom
x = Math.random() * 2048;
y = 2732 + 150;
break;
case 3:
// left
x = -150;
y = Math.random() * 2732;
break;
}
var boss = new BossZombie(x, y, wave);
zombies.push(boss);
game.addChild(boss);
// Play boss appear sound
LK.getSound('bossAppear').play();
// Show boss alert
gameStatusText.setText('BOSS ZOMBIE APPEARED!');
gameStatusText.alpha = 1;
tween(gameStatusText, {
alpha: 0
}, {
duration: 2000,
easing: tween.easeIn
});
}
function spawnPickup(x, y) {
// 30% chance for health, 70% for ammo
var type = Math.random() < 0.3 ? 'health' : 'ammo';
var pickup = new Pickup(x, y, type);
pickups.push(pickup);
game.addChild(pickup);
}
function checkWaveCompletion() {
if (isWaveActive && zombies.length === 0 && !isGameOver && zombiesKilled >= 3 + wave) {
isWaveActive = false;
// Display wave complete message
gameStatusText.setText('WAVE ' + wave + ' COMPLETE!');
gameStatusText.alpha = 1;
// Give player a weapon upgrade every 3 waves
if (wave % 3 === 0) {
player.upgradeWeapon();
// Show upgrade message
LK.setTimeout(function () {
gameStatusText.setText('WEAPON UPGRADED!');
}, 2000);
}
// Spawn crates as objects after wave completion
for (var i = 0; i < 5; i++) {
var randomX = Math.random() * 2048;
var randomY = Math.random() * 2732;
var crate = new Obstacle(randomX, randomY, 'crate');
obstacles.push(crate);
game.addChild(crate);
}
// Spawn barrels at the end of each wave
for (var i = 0; i < 3; i++) {
var randomX = Math.random() * 2048;
var randomY = Math.random() * 2732;
var barrel = new Barrel(randomX, randomY);
obstacles.push(barrel);
game.addChild(barrel);
}
// Start next wave after a 5-second delay
LK.setTimeout(function () {
wave++;
gameStatusText.alpha = 0;
startWave();
}, 5000);
}
}
// Event handling
game.down = function (x, y) {
if (isGameOver) {
return;
}
targetPosition.x = x;
targetPosition.y = y;
// Player shoots at clicked position
var bullet = player.shoot(x, y);
if (bullet) {
bullets.push(bullet);
game.addChild(bullet);
}
};
game.move = function (x, y) {
targetPosition.x = x;
targetPosition.y = y;
};
game.up = function () {
// Not needed for this game
};
// Main game update loop
game.update = function () {
if (isGameOver) {
return;
}
// Check for player collision with obstacles
obstacles.forEach(function (obstacle) {
if (player.intersects(obstacle) && obstacle.type !== 'crate') {
// Prevent player from moving through the obstacle, except crates
player.x = player.lastX;
player.y = player.lastY;
}
});
// Update player movement
if (player) {
player.lastX = player.x; // Track lastX before moving
player.lastY = player.y; // Track lastY before moving
player.moveTowards(targetPosition.x, targetPosition.y);
// Check if player is near the legendary chest
if (!player.nearLegendaryChest && player.intersects(legendaryChest)) {
player.nearLegendaryChest = true;
player.legendaryChestTimer = Date.now();
} else if (player.nearLegendaryChest && !player.intersects(legendaryChest)) {
player.nearLegendaryChest = false;
player.legendaryChestTimer = null;
}
// Open the legendary chest if the player stands next to it for 3 seconds
if (player.nearLegendaryChest && Date.now() - player.legendaryChestTimer >= 3000) {
// Logic to open the chest
console.log("Legendary chest opened!");
legendaryChest.destroy();
legendaryChest = null; // Set legendaryChest to null after destroying it
// Change appearance to legendary2
var legendary2 = LK.getAsset('legendary2', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2,
visible: true // Make legendary2 visible
});
game.addChild(legendary2);
// Randomly decide to spawn an upgrader or shield chest
if (Math.random() < 0.5) {
var upgrader = new Upgrader(2048 / 2, 2732 / 2);
game.addChild(upgrader);
pickups.push(upgrader);
} else {
var shieldChest = new ShieldChest(2048 / 2, 2732 / 2);
game.addChild(shieldChest);
pickups.push(shieldChest);
}
player.nearLegendaryChest = false;
player.legendaryChestTimer = null;
}
// Regenerate health slowly if not at max health
if (player.health < player.maxHealth) {
player.health = Math.min(player.maxHealth, player.health + 0.05); // Regenerate 0.05 health per update
}
}
// Update bullets
for (var i = bullets.length - 1; i >= 0; i--) {
var shouldRemove = bullets[i].update();
if (shouldRemove) {
bullets[i].destroy();
bullets.splice(i, 1);
continue;
}
// Check bullet collision with zombies
for (var j = zombies.length - 1; j >= 0; j--) {
if (bullets[i] && bullets[i].intersects(zombies[j])) {
// Display bullet effect at the point of impact
var bulletEffect = LK.getAsset('bulleteffect', {
anchorX: 0.5,
anchorY: 0.5,
x: bullets[i].x,
y: bullets[i].y
});
game.addChild(bulletEffect);
tween(bulletEffect, {}, {
duration: 200,
onFinish: function onFinish() {
bulletEffect.destroy();
}
});
// Check if this bullet has already hit this zombie
if (!bulletHitZombies[bullets[i]]) {
bulletHitZombies[bullets[i]] = [];
}
if (!bulletHitZombies[bullets[i]].includes(zombies[j])) {
bulletHitZombies[bullets[i]].push(zombies[j]);
var zombieDied = zombies[j].takeDamage(bullets[i].damage);
// Remove bullet after hit
bullets[i].destroy();
bullets.splice(i, 1);
if (zombieDied) {
// Increase score and zombie kill counter
var scoreToAdd = zombies[j] instanceof BossZombie ? 100 : 10;
score += scoreToAdd;
zombiesKilled++;
// Chance to drop pickup
if (Math.random() < 0.3) {
spawnPickup(zombies[j].x, zombies[j].y);
}
// Remove zombie
zombies[j].destroy();
zombies.splice(j, 1);
}
break;
}
}
}
}
// Update obstacles
for (var i = obstacles.length - 1; i >= 0; i--) {
var shouldRemove = obstacles[i].update();
if (shouldRemove) {
obstacles[i].destroy();
obstacles.splice(i, 1);
}
}
// Update zombies
for (var i = zombies.length - 1; i >= 0; i--) {
// Move zombie towards player
zombies[i].moveTowards(player.x, player.y);
// Check if zombie reached player
if (zombies[i].intersects(player)) {
var playerDied = zombies[i].attack(player);
if (playerDied) {
handleGameOver();
}
}
}
for (var i = pickups.length - 1; i >= 0; i--) {
var shouldRemove = pickups[i].update();
if (shouldRemove) {
pickups[i].destroy();
pickups.splice(i, 1);
continue;
}
// Check if player collects pickup
if (player.intersects(pickups[i])) {
pickups[i].collect(player);
pickups[i].destroy();
pickups.splice(i, 1);
}
}
// Update UI
updateUI();
// Spawn bomb in red area every 10 seconds, only if wave is active
if (isWaveActive && LK.ticks % 600 === 0) {
var bombX = Math.random() * 2048;
var bombY = Math.random() * 2732;
var bomb = new Bomb(bombX, bombY);
game.addChild(bomb);
// Trigger explosion after 3 seconds
LK.setTimeout(function () {
bomb.explode();
}, 3000);
}
// Check if wave is complete
checkWaveCompletion();
// Auto-reload when empty and not already reloading
if (player.ammo === 0 && !player.isReloading) {
player.reload();
}
};
function handleGameOver() {
isGameOver = true;
// Fade out background music
LK.playMusic('gameMusic', {
fade: {
start: 0.3,
end: 0,
duration: 1000
}
});
// Update high score if needed
if (score > storage.highscore) {
storage.highscore = score;
}
// Show score in game over dialog
LK.setScore(score);
// Show game over screen after a short delay
LK.setTimeout(function () {
LK.showGameOver();
}, 1000);
}
initGame(); // Start the game immediately