User prompt
When a Zombie dies, the turret still facing the dead Zombie and continues to shoot.
User prompt
Add background asset
User prompt
Add background
User prompt
make SSC rotate randomly after some time on a regular basis ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
add cardboard zombie (when dead will spawn a random zombie (except Crowzombie)),box zombies (same as Cardboard zombies but spawns three),football zombies,Buried zombies: appear randomly on the map (not too close to home) ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
add wall(block zombies and get attacked by zombies) and attractor(attract all zombies) ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Add Electrical turret,Spike turret,ball turret ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
S.S.C spawn 15 sun instead of 10 sun
User prompt
change boss zombie, divided into three types: Shaman zombie: summon fake zombie, heal when low on health, Ninja zombie: teleport, clone, Crazy zombie: dig the ground, buff other zombies, Soldier zombie: go in groups with 4 other and two tanks ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
S.S.C only spawn 10sun instead of 20sun
User prompt
Runners and Tough only appear after 45s
User prompt
added cooldown for building turrets
User prompt
change the zombie spawn mechanism, only when there are 1-2 zombies left, then the next wave will be born, SSC only spawns 10 suns in about 15-25 seconds
User prompt
When zombies are not in range, turrets will turn back to default direction and stop shooting.
User prompt
Add arrows, when pressed will switch to pages containing turrets to make the game UI more compact
User prompt
Add turret: Frozen turret (causes freezing, slows down), Flame thrower (causes fire damage over time, if the zombie is frozen, slowing down will end that effect), Acid turret: is a flame thrower but the damage over time is smaller but will not affect the freezing effect), Automatic bullet turret: very large range, bullets will automatically chase the enemy, in return the damage is small, Minigun turret, rocket launcher ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Special zombies only appear if the player defends the house for 100s
User prompt
added new zombies: Basic zombies now include Tough zombies and Runners, special zombies: Umbrella zombie (umbrella works like a shield), Crows Zombie (when dead, it will release 3 crow zombies)
User prompt
Increases turret damage and slows zombies down a bit
User prompt
Day only turns Night when progress bar runs out
User prompt
increase the range of turrets
User prompt
Make gridcell full screen,except house area
User prompt
make the drag and drop area and build turret full screen, except the house area
User prompt
When not in range, the turrets will stop firing and wait until another zombie enters, increasing the turret's firing range further, when dragging and dropping the turret, the player can see how far the turret's firing range is.
User prompt
increase gridcell size
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ var AcidTurret = Container.expand(function () { var self = Container.call(this); var tower = self.attachAsset('acidTurret', { anchorX: 0.5, anchorY: 0.5 }); self.range = 140; self.fireRate = 45; self.fireTimer = 0; self.health = 100; self.maxHealth = 100; self.update = function () { self.fireTimer++; if (self.fireTimer >= self.fireRate) { self.fireTimer = 0; self.acidAttack(); } }; self.acidAttack = function () { LK.getSound('laser').play(); for (var i = 0; i < zombies.length; i++) { var dx = zombies[i].x - self.x; var dy = zombies[i].y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < self.range) { // Apply acid effect (doesn't remove freezing) zombies[i].acidDuration = 240; // 4 seconds of acid zombies[i].acidDamage = 2; // Smaller damage per tick } } var acidEffect = self.attachAsset('acidEffect', { anchorX: 0.5, anchorY: 0.5 }); acidEffect.width = self.range * 2; acidEffect.height = self.range * 2; acidEffect.alpha = 0.6; tween(acidEffect, { alpha: 0, scaleX: 1.4, scaleY: 1.4 }, { duration: 600, onFinish: function onFinish() { acidEffect.destroy(); } }); }; self.takeDamage = function (damage) { self.health -= damage; if (self.health <= 0) { self.destroy(); return true; } LK.effects.flashObject(self, 0xFF0000, 300); return false; }; return self; }); var AutoTurret = Container.expand(function () { var self = Container.call(this); var tower = self.attachAsset('autoTurret', { anchorX: 0.5, anchorY: 0.5 }); self.damage = 8; self.range = 400; self.fireRate = 40; self.fireTimer = 0; self.health = 140; self.maxHealth = 140; self.update = function () { self.fireTimer++; if (self.fireTimer >= self.fireRate) { self.fireTimer = 0; self.findAndShoot(); } }; self.findAndShoot = function () { var target = null; var minDist = self.range; for (var i = 0; i < zombies.length; i++) { var dx = zombies[i].x - self.x; var dy = zombies[i].y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist <= self.range) { if (dist < minDist) { minDist = dist; target = zombies[i]; } } } if (target) { // Aim turret at target var dx = target.x - self.x; var dy = target.y - self.y; var angle = Math.atan2(dy, dx) + Math.PI / 2; self.children[0].rotation = angle; self.shootHomingBullet(); } else { // No target in range, return to default direction (facing up) self.children[0].rotation = 0; } }; self.shootHomingBullet = function () { LK.getSound('laser').play(); var bullet = new HomingBullet(); bullet.x = self.x; bullet.y = self.y; bullet.damage = self.damage; game.addChild(bullet); }; self.takeDamage = function (damage) { self.health -= damage; if (self.health <= 0) { self.destroy(); return true; } LK.effects.flashObject(self, 0xFF0000, 300); return false; }; return self; }); 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 = 25; self.targetX = 0; self.targetY = 0; self.update = function () { var dx = self.targetX - self.x; var dy = self.targetY - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 5) { self.x += dx / dist * self.speed; self.y += dy / dist * self.speed; } else { // Hit target area, check for zombie collision for (var i = 0; i < zombies.length; i++) { var zdx = zombies[i].x - self.x; var zdy = zombies[i].y - self.y; var zdist = Math.sqrt(zdx * zdx + zdy * zdy); if (zdist < 30) { zombies[i].takeDamage(self.damage); self.destroy(); return; } } self.destroy(); } }; return self; }); var ClassicTurret = Container.expand(function () { var self = Container.call(this); var tower = self.attachAsset('classicTurret', { anchorX: 0.5, anchorY: 0.5 }); self.damage = 25; self.range = 250; self.fireRate = 45; self.fireTimer = 0; self.health = 120; self.maxHealth = 120; self.update = function () { self.fireTimer++; if (self.fireTimer >= self.fireRate) { self.fireTimer = 0; self.findAndShoot(); } }; self.findAndShoot = function () { var target = null; var minDist = self.range; for (var i = 0; i < zombies.length; i++) { var dx = zombies[i].x - self.x; var dy = zombies[i].y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist <= self.range) { if (dist < minDist) { minDist = dist; target = zombies[i]; } } } if (target) { // Aim turret at target var dx = target.x - self.x; var dy = target.y - self.y; var angle = Math.atan2(dy, dx) + Math.PI / 2; self.children[0].rotation = angle; self.shootBullet(target); } else { // No target in range, return to default direction (facing up) self.children[0].rotation = 0; } }; self.shootBullet = function (target) { LK.getSound('laser').play(); var bullet = new Bullet(); bullet.x = self.x; bullet.y = self.y; bullet.targetX = target.x; bullet.targetY = target.y; bullet.damage = self.damage; game.addChild(bullet); }; self.takeDamage = function (damage) { self.health -= damage; if (self.health <= 0) { self.destroy(); return true; } LK.effects.flashObject(self, 0xFF0000, 300); return false; }; return self; }); var CrazyZombie = Container.expand(function () { var self = Container.call(this); var bossGraphics = self.attachAsset('crazyZombie', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 0.3; self.health = 500; self.damage = 25; self.target = null; self.attackTimer = 0; self.frozen = 0; self.digTimer = 0; self.buffTimer = 0; self.isUnderground = false; self.update = function () { // Handle burning effect if (self.burnDuration > 0) { self.burnDuration--; if (self.burnDuration % 10 === 0) { self.takeDamage(self.burnDamage || 3); } } // Handle acid effect if (self.acidDuration > 0) { self.acidDuration--; if (self.acidDuration % 15 === 0) { self.takeDamage(self.acidDamage || 2); } } if (self.frozen > 0) { self.frozen--; bossGraphics.tint = 0x87CEEB; self.speed = 0.15; } else if (self.burnDuration > 0) { bossGraphics.tint = 0xFF4500; self.speed = 0.3; } else if (self.acidDuration > 0) { bossGraphics.tint = 0x32CD32; self.speed = 0.3; } else { bossGraphics.tint = 0x8b4513; self.speed = 0.3; } // Dig underground ability self.digTimer++; if (self.digTimer >= 360) { // Every 6 seconds self.digTimer = 0; self.digUnderground(); } // Buff other zombies self.buffTimer++; if (self.buffTimer >= 180) { // Every 3 seconds self.buffTimer = 0; self.buffOtherZombies(); } if (!self.isUnderground) { if (!self.target || self.target.destroyed) { self.findTarget(); } if (self.target) { var dx = self.target.x - self.x; var dy = self.target.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 60) { self.x += dx / dist * self.speed; self.y += dy / dist * self.speed; } else { self.attackTimer++; if (self.attackTimer >= 45) { self.attackTimer = 0; if (self.target.takeDamage) { if (self.target.takeDamage(self.damage)) { self.target = null; } } } } } else { self.y += self.speed; if (self.y > 2732) { lives -= 4; self.destroy(); } } } }; self.digUnderground = function () { self.isUnderground = true; self.alpha = 0.3; // Make translucent // Emerge near a random turret or house var targets = towers.concat(houses); if (targets.length > 0) { var randomTarget = targets[Math.floor(Math.random() * targets.length)]; tween(self, { x: randomTarget.x + (Math.random() - 0.5) * 100, y: randomTarget.y + (Math.random() - 0.5) * 100 }, { duration: 1000, onFinish: function onFinish() { self.isUnderground = false; self.alpha = 1; LK.effects.flashObject(self, 0x8b4513, 300); } }); } }; self.buffOtherZombies = function () { for (var i = 0; i < zombies.length; i++) { var zombie = zombies[i]; if (zombie !== self) { var dx = zombie.x - self.x; var dy = zombie.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < 200) { // Buff range zombie.speed *= 1.2; // Increase speed zombie.damage = Math.floor(zombie.damage * 1.1); // Increase damage LK.effects.flashObject(zombie, 0xFFD700, 200); } } } }; self.findTarget = function () { var minDist = Infinity; self.target = null; for (var i = 0; i < houses.length; i++) { var houseLeft = houses[i].x - 1024; var houseRight = houses[i].x + 1024; var targetX = Math.max(houseLeft, Math.min(houseRight, self.x)); var dx = targetX - self.x; var dy = houses[i].y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < minDist) { minDist = dist; self.target = { x: targetX, y: houses[i].y, takeDamage: houses[i].takeDamage.bind(houses[i]), destroyed: houses[i].destroyed }; } } }; self.takeDamage = function (damage) { if (self.isUnderground) { damage = Math.floor(damage * 0.5); // Take less damage underground } self.health -= damage; if (self.health <= 0) { LK.setScore(LK.getScore() + 200); LK.getSound('zombieDeath').play(); self.destroy(); return true; } LK.effects.flashObject(self, 0xFFFFFF, 200); return false; }; self.freeze = function () { self.frozen = 120; }; return self; }); var CrowZombie = Container.expand(function () { var self = Container.call(this); var zombieGraphics = self.attachAsset('crowZombie', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 1.8; self.health = 15; self.damage = 5; self.target = null; self.attackTimer = 0; self.frozen = 0; self.update = function () { if (self.frozen > 0) { self.frozen--; zombieGraphics.tint = 0x87CEEB; self.speed = 0.9; } else { zombieGraphics.tint = 0xFFFFFF; self.speed = 1.8; } if (!self.target || self.target.destroyed) { self.findTarget(); } if (self.target) { var dx = self.target.x - self.x; var dy = self.target.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 30) { self.x += dx / dist * self.speed; self.y += dy / dist * self.speed; } else { self.attackTimer++; if (self.attackTimer >= 45) { self.attackTimer = 0; if (self.target.takeDamage) { if (self.target.takeDamage(self.damage)) { self.target = null; } } } } } else { self.y += self.speed; if (self.y > 2732) { lives--; self.destroy(); } } }; self.findTarget = function () { var minDist = Infinity; self.target = null; for (var i = 0; i < houses.length; i++) { var houseLeft = houses[i].x - 1024; var houseRight = houses[i].x + 1024; var targetX = Math.max(houseLeft, Math.min(houseRight, self.x)); var dx = targetX - self.x; var dy = houses[i].y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < minDist) { minDist = dist; self.target = { x: targetX, y: houses[i].y, takeDamage: houses[i].takeDamage.bind(houses[i]), destroyed: houses[i].destroyed }; } } }; self.takeDamage = function (damage) { self.health -= damage; if (self.health <= 0) { LK.setScore(LK.getScore() + 5); LK.getSound('zombieDeath').play(); self.destroy(); return true; } LK.effects.flashObject(self, 0xFFFFFF, 200); return false; }; self.freeze = function () { self.frozen = 120; }; return self; }); var CrowsZombie = Container.expand(function () { var self = Container.call(this); var zombieGraphics = self.attachAsset('crowsZombie', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 0.7; self.health = 60; self.damage = 10; self.target = null; self.attackTimer = 0; self.frozen = 0; self.update = function () { if (self.frozen > 0) { self.frozen--; zombieGraphics.tint = 0x87CEEB; self.speed = 0.35; } else { zombieGraphics.tint = 0xFFFFFF; self.speed = 0.7; } if (!self.target || self.target.destroyed) { self.findTarget(); } if (self.target) { var dx = self.target.x - self.x; var dy = self.target.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 50) { self.x += dx / dist * self.speed; self.y += dy / dist * self.speed; } else { self.attackTimer++; if (self.attackTimer >= 60) { self.attackTimer = 0; if (self.target.takeDamage) { if (self.target.takeDamage(self.damage)) { self.target = null; } } } } } else { self.y += self.speed; if (self.y > 2732) { lives--; self.destroy(); } } }; self.findTarget = function () { var minDist = Infinity; self.target = null; for (var i = 0; i < houses.length; i++) { var houseLeft = houses[i].x - 1024; var houseRight = houses[i].x + 1024; var targetX = Math.max(houseLeft, Math.min(houseRight, self.x)); var dx = targetX - self.x; var dy = houses[i].y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < minDist) { minDist = dist; self.target = { x: targetX, y: houses[i].y, takeDamage: houses[i].takeDamage.bind(houses[i]), destroyed: houses[i].destroyed }; } } }; self.takeDamage = function (damage) { self.health -= damage; if (self.health <= 0) { LK.setScore(LK.getScore() + 15); LK.getSound('zombieDeath').play(); // Spawn 3 crow zombies for (var i = 0; i < 3; i++) { var crow = new CrowZombie(); crow.x = self.x + (Math.random() - 0.5) * 60; crow.y = self.y + (Math.random() - 0.5) * 60; zombies.push(crow); game.addChild(crow); } self.destroy(); return true; } LK.effects.flashObject(self, 0xFFFFFF, 200); return false; }; self.freeze = function () { self.frozen = 180; }; return self; }); var DoubleBarrelTurret = Container.expand(function () { var self = Container.call(this); var tower = self.attachAsset('doubleBarrelTurret', { anchorX: 0.5, anchorY: 0.5 }); self.damage = 20; self.range = 280; self.fireRate = 35; self.fireTimer = 0; self.health = 140; self.maxHealth = 140; self.update = function () { self.fireTimer++; if (self.fireTimer >= self.fireRate) { self.fireTimer = 0; self.findAndShoot(); } }; self.findAndShoot = function () { var targets = []; // Find all zombies in range and sort by distance var zombiesInRange = []; for (var i = 0; i < zombies.length; i++) { var dx = zombies[i].x - self.x; var dy = zombies[i].y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist <= self.range) { zombiesInRange.push({ zombie: zombies[i], distance: dist }); } } // Sort by distance and take closest 2 zombiesInRange.sort(function (a, b) { return a.distance - b.distance; }); for (var i = 0; i < Math.min(2, zombiesInRange.length); i++) { targets.push(zombiesInRange[i].zombie); } if (targets.length > 0) { // Aim at closest target var dx = targets[0].x - self.x; var dy = targets[0].y - self.y; var angle = Math.atan2(dy, dx) + Math.PI / 2; self.children[0].rotation = angle; // Shoot two bullets for (var i = 0; i < targets.length; i++) { self.shootBullet(targets[i]); } } else { // No targets in range, return to default direction (facing up) self.children[0].rotation = 0; } }; self.shootBullet = function (target) { LK.getSound('laser').play(); var bullet = new Bullet(); bullet.x = self.x; bullet.y = self.y; bullet.targetX = target.x; bullet.targetY = target.y; bullet.damage = self.damage; game.addChild(bullet); }; self.takeDamage = function (damage) { self.health -= damage; if (self.health <= 0) { self.destroy(); return true; } LK.effects.flashObject(self, 0xFF0000, 300); return false; }; return self; }); var ExplosiveTower = Container.expand(function () { var self = Container.call(this); var tower = self.attachAsset('explosiveTower', { anchorX: 0.5, anchorY: 0.5 }); self.damage = 75; self.range = 280; self.explosionRange = 100; self.fireRate = 90; self.fireTimer = 0; self.health = 120; self.maxHealth = 120; self.update = function () { self.fireTimer++; if (self.fireTimer >= self.fireRate) { self.fireTimer = 0; self.findAndExplode(); } }; self.findAndExplode = function () { var target = null; var minDist = self.range; for (var i = 0; i < zombies.length; i++) { var dx = zombies[i].x - self.x; var dy = zombies[i].y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist <= self.range) { if (dist < minDist) { minDist = dist; target = zombies[i]; } } } if (target) { self.explode(target.x, target.y); } }; self.explode = function (x, y) { LK.getSound('explode').play(); var explosion = game.addChild(LK.getAsset('explosion', { anchorX: 0.5, anchorY: 0.5 })); explosion.x = x; explosion.y = y; explosion.alpha = 0.8; tween(explosion, { alpha: 0, scaleX: 2, scaleY: 2 }, { duration: 300, onFinish: function onFinish() { explosion.destroy(); } }); for (var i = 0; i < zombies.length; i++) { var dx = zombies[i].x - x; var dy = zombies[i].y - y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < self.explosionRange) { zombies[i].takeDamage(self.damage); } } }; self.takeDamage = function (damage) { self.health -= damage; if (self.health <= 0) { self.destroy(); return true; } LK.effects.flashObject(self, 0xFF0000, 300); return false; }; return self; }); var FakeZombie = Container.expand(function () { var self = Container.call(this); var zombieGraphics = self.attachAsset('fakeZombie', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 0.8; self.health = 10; self.damage = 3; self.target = null; self.attackTimer = 0; self.frozen = 0; self.lifeTimer = 0; self.maxLife = 600; // 10 seconds self.update = function () { self.lifeTimer++; if (self.lifeTimer >= self.maxLife) { // Fade away after 10 seconds tween(self, { alpha: 0 }, { duration: 300, onFinish: function onFinish() { self.destroy(); } }); return; } if (self.frozen > 0) { self.frozen--; zombieGraphics.tint = 0x87CEEB; self.speed = 0.4; } else { zombieGraphics.tint = 0xdda0dd; self.speed = 0.8; } if (!self.target || self.target.destroyed) { self.findTarget(); } if (self.target) { var dx = self.target.x - self.x; var dy = self.target.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 30) { self.x += dx / dist * self.speed; self.y += dy / dist * self.speed; } else { self.attackTimer++; if (self.attackTimer >= 60) { self.attackTimer = 0; if (self.target.takeDamage) { if (self.target.takeDamage(self.damage)) { self.target = null; } } } } } else { self.y += self.speed; if (self.y > 2732) { self.destroy(); } } }; self.findTarget = function () { var minDist = Infinity; self.target = null; for (var i = 0; i < houses.length; i++) { var houseLeft = houses[i].x - 1024; var houseRight = houses[i].x + 1024; var targetX = Math.max(houseLeft, Math.min(houseRight, self.x)); var dx = targetX - self.x; var dy = houses[i].y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < minDist) { minDist = dist; self.target = { x: targetX, y: houses[i].y, takeDamage: houses[i].takeDamage.bind(houses[i]), destroyed: houses[i].destroyed }; } } }; self.takeDamage = function (damage) { self.health -= damage; if (self.health <= 0) { LK.setScore(LK.getScore() + 2); LK.getSound('zombieDeath').play(); self.destroy(); return true; } LK.effects.flashObject(self, 0xFFFFFF, 200); return false; }; self.freeze = function () { self.frozen = 60; }; return self; }); var FlameThrowerTurret = Container.expand(function () { var self = Container.call(this); var tower = self.attachAsset('flameThrowerTurret', { anchorX: 0.5, anchorY: 0.5 }); self.range = 150; self.fireRate = 30; self.fireTimer = 0; self.health = 110; self.maxHealth = 110; self.update = function () { self.fireTimer++; if (self.fireTimer >= self.fireRate) { self.fireTimer = 0; self.flameAttack(); } }; self.flameAttack = function () { LK.getSound('flame').play(); for (var i = 0; i < zombies.length; i++) { var dx = zombies[i].x - self.x; var dy = zombies[i].y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < self.range) { // Remove freezing effect if zombie is frozen if (zombies[i].frozen > 0) { zombies[i].frozen = 0; } // Apply burning effect zombies[i].burnDuration = 180; // 3 seconds of burning zombies[i].burnDamage = 3; // Damage per tick } } var flameEffect = self.attachAsset('fireEffect', { anchorX: 0.5, anchorY: 0.5 }); flameEffect.width = self.range * 2; flameEffect.height = self.range * 2; flameEffect.alpha = 0.7; tween(flameEffect, { alpha: 0, scaleX: 1.3, scaleY: 1.3 }, { duration: 400, onFinish: function onFinish() { flameEffect.destroy(); } }); }; self.takeDamage = function (damage) { self.health -= damage; if (self.health <= 0) { self.destroy(); return true; } LK.effects.flashObject(self, 0xFF0000, 300); return false; }; return self; }); var FreezeTower = Container.expand(function () { var self = Container.call(this); var tower = self.attachAsset('freezeTower', { anchorX: 0.5, anchorY: 0.5 }); self.range = 150; self.fireRate = 120; self.fireTimer = 0; self.health = 100; self.maxHealth = 100; self.update = function () { self.fireTimer++; if (self.fireTimer >= self.fireRate) { self.fireTimer = 0; self.freezeArea(); } }; self.freezeArea = function () { LK.getSound('freeze').play(); for (var i = 0; i < zombies.length; i++) { var dx = zombies[i].x - self.x; var dy = zombies[i].y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < self.range) { zombies[i].freeze(); } } var freezeEffect = self.attachAsset('explosion', { anchorX: 0.5, anchorY: 0.5 }); freezeEffect.tint = 0x00BFFF; freezeEffect.alpha = 0.5; freezeEffect.width = self.range * 2; freezeEffect.height = self.range * 2; tween(freezeEffect, { alpha: 0, scaleX: 1.5, scaleY: 1.5 }, { duration: 500, onFinish: function onFinish() { freezeEffect.destroy(); } }); }; self.takeDamage = function (damage) { self.health -= damage; if (self.health <= 0) { self.destroy(); return true; } LK.effects.flashObject(self, 0xFF0000, 300); return false; }; return self; }); var FrozenTurret = Container.expand(function () { var self = Container.call(this); var tower = self.attachAsset('frozenTurret', { anchorX: 0.5, anchorY: 0.5 }); self.range = 200; self.fireRate = 90; self.fireTimer = 0; self.health = 120; self.maxHealth = 120; self.update = function () { self.fireTimer++; if (self.fireTimer >= self.fireRate) { self.fireTimer = 0; self.freezeArea(); } }; self.freezeArea = function () { LK.getSound('freeze').play(); for (var i = 0; i < zombies.length; i++) { var dx = zombies[i].x - self.x; var dy = zombies[i].y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < self.range) { zombies[i].freeze(); } } var freezeEffect = self.attachAsset('explosion', { anchorX: 0.5, anchorY: 0.5 }); freezeEffect.tint = 0x00FFFF; freezeEffect.alpha = 0.5; freezeEffect.width = self.range * 2; freezeEffect.height = self.range * 2; tween(freezeEffect, { alpha: 0, scaleX: 1.5, scaleY: 1.5 }, { duration: 500, onFinish: function onFinish() { freezeEffect.destroy(); } }); }; self.takeDamage = function (damage) { self.health -= damage; if (self.health <= 0) { self.destroy(); return true; } LK.effects.flashObject(self, 0xFF0000, 300); return false; }; return self; }); var HomingBullet = Container.expand(function () { var self = Container.call(this); var bulletGraphics = self.attachAsset('homingBullet', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 6; self.damage = 8; self.target = null; self.turnSpeed = 0.15; self.update = function () { // Find closest target if we don't have one or target is destroyed if (!self.target || self.target.destroyed || self.target.health <= 0) { self.findTarget(); } if (self.target) { // Move towards target with homing behavior var dx = self.target.x - self.x; var dy = self.target.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 10) { // Normalize direction and move var dirX = dx / dist; var dirY = dy / dist; self.x += dirX * self.speed; self.y += dirY * self.speed; // Rotate bullet to face movement direction var angle = Math.atan2(dy, dx); bulletGraphics.rotation = angle; } else { // Hit target self.target.takeDamage(self.damage); self.destroy(); } } else { // No target, move forward self.y += self.speed; if (self.y > 2732) { self.destroy(); } } }; self.findTarget = function () { var minDist = 400; // Max homing range self.target = null; for (var i = 0; i < zombies.length; i++) { if (zombies[i].destroyed || zombies[i].health <= 0) continue; var dx = zombies[i].x - self.x; var dy = zombies[i].y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < minDist) { minDist = dist; self.target = zombies[i]; } } }; return self; }); var House = Container.expand(function () { var self = Container.call(this); var houseGraphics = self.attachAsset('house', { anchorX: 0.5, anchorY: 0.5 }); // Scale house to span the width of the screen houseGraphics.width = 2048; // Full screen width houseGraphics.height = 100; // Keep original height self.health = 500; // Increased health since it's the only house self.maxHealth = 500; self.takeDamage = function (damage) { self.health -= damage; if (self.health <= 0) { lives -= 10; // Lose more lives when the main house is destroyed self.destroy(); return true; } LK.effects.flashObject(self, 0xFF0000, 300); return false; }; return self; }); var LaserTower = Container.expand(function () { var self = Container.call(this); var tower = self.attachAsset('laserTower', { anchorX: 0.5, anchorY: 0.5 }); self.damage = 30; self.range = 280; self.fireRate = 30; self.fireTimer = 0; self.health = 150; self.maxHealth = 150; self.update = function () { self.fireTimer++; if (self.fireTimer >= self.fireRate) { self.fireTimer = 0; self.findAndShoot(); } }; self.findAndShoot = function () { var target = null; var minDist = self.range; for (var i = 0; i < zombies.length; i++) { var dx = zombies[i].x - self.x; var dy = zombies[i].y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist <= self.range) { if (dist < minDist) { minDist = dist; target = zombies[i]; } } } if (target) { self.shootLaser(target); } }; self.shootLaser = function (target) { LK.getSound('laser').play(); var laser = self.attachAsset('laserBeam', { anchorX: 0.5, anchorY: 1 }); var dx = target.x - self.x; var dy = target.y - self.y; var angle = Math.atan2(dy, dx) + Math.PI / 2; laser.rotation = angle; var dist = Math.sqrt(dx * dx + dy * dy); laser.height = dist; tween(laser, { alpha: 0 }, { duration: 200, onFinish: function onFinish() { laser.destroy(); } }); target.takeDamage(self.damage); }; self.takeDamage = function (damage) { self.health -= damage; if (self.health <= 0) { self.destroy(); return true; } LK.effects.flashObject(self, 0xFF0000, 300); return false; }; return self; }); var MinigunTurret = Container.expand(function () { var self = Container.call(this); var tower = self.attachAsset('minigunTurret', { anchorX: 0.5, anchorY: 0.5 }); self.damage = 12; self.range = 220; self.fireRate = 10; // Very fast firing self.fireTimer = 0; self.health = 130; self.maxHealth = 130; self.update = function () { self.fireTimer++; if (self.fireTimer >= self.fireRate) { self.fireTimer = 0; self.findAndShoot(); } }; self.findAndShoot = function () { var target = null; var minDist = self.range; for (var i = 0; i < zombies.length; i++) { var dx = zombies[i].x - self.x; var dy = zombies[i].y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist <= self.range) { if (dist < minDist) { minDist = dist; target = zombies[i]; } } } if (target) { // Aim turret at target var dx = target.x - self.x; var dy = target.y - self.y; var angle = Math.atan2(dy, dx) + Math.PI / 2; self.children[0].rotation = angle; self.shootBullet(target); } else { // No target in range, return to default direction (facing up) self.children[0].rotation = 0; } }; self.shootBullet = function (target) { LK.getSound('minigun').play(); var bullet = new Bullet(); bullet.x = self.x; bullet.y = self.y; bullet.targetX = target.x; bullet.targetY = target.y; bullet.damage = self.damage; game.addChild(bullet); }; self.takeDamage = function (damage) { self.health -= damage; if (self.health <= 0) { self.destroy(); return true; } LK.effects.flashObject(self, 0xFF0000, 300); return false; }; return self; }); var NinjaZombie = Container.expand(function () { var self = Container.call(this); var bossGraphics = self.attachAsset('ninjaZombie', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 0.4; self.health = 350; self.damage = 30; self.target = null; self.attackTimer = 0; self.frozen = 0; self.teleportTimer = 0; self.cloneTimer = 0; self.canClone = true; self.update = function () { // Handle burning effect if (self.burnDuration > 0) { self.burnDuration--; if (self.burnDuration % 10 === 0) { self.takeDamage(self.burnDamage || 3); } } // Handle acid effect if (self.acidDuration > 0) { self.acidDuration--; if (self.acidDuration % 15 === 0) { self.takeDamage(self.acidDamage || 2); } } if (self.frozen > 0) { self.frozen--; bossGraphics.tint = 0x87CEEB; self.speed = 0.2; } else if (self.burnDuration > 0) { bossGraphics.tint = 0xFF4500; self.speed = 0.4; } else if (self.acidDuration > 0) { bossGraphics.tint = 0x32CD32; self.speed = 0.4; } else { bossGraphics.tint = 0x2f2f2f; self.speed = 0.4; } // Teleport ability self.teleportTimer++; if (self.teleportTimer >= 240) { // Every 4 seconds self.teleportTimer = 0; self.teleport(); } // Clone ability when health is low if (self.health < 175 && self.canClone) { self.canClone = false; self.createClone(); } if (!self.target || self.target.destroyed) { self.findTarget(); } if (self.target) { var dx = self.target.x - self.x; var dy = self.target.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 60) { self.x += dx / dist * self.speed; self.y += dy / dist * self.speed; } else { self.attackTimer++; if (self.attackTimer >= 30) { // Faster attacks self.attackTimer = 0; if (self.target.takeDamage) { if (self.target.takeDamage(self.damage)) { self.target = null; } } } } } else { self.y += self.speed; if (self.y > 2732) { lives -= 3; self.destroy(); } } }; self.teleport = function () { // Teleport to random location self.x = Math.random() * 2048; self.y = Math.random() * 1000 + 200; LK.effects.flashObject(self, 0x8A2BE2, 300); tween(self, { alpha: 0.5 }, { duration: 100, onFinish: function onFinish() { tween(self, { alpha: 1 }, { duration: 100 }); } }); }; self.createClone = function () { var clone = new NinjaZombie(); clone.x = self.x + (Math.random() - 0.5) * 200; clone.y = self.y + (Math.random() - 0.5) * 200; clone.health = 100; // Clone has less health clone.canClone = false; // Clones can't clone zombies.push(clone); game.addChild(clone); LK.effects.flashObject(self, 0x800080, 500); }; self.findTarget = function () { var minDist = Infinity; self.target = null; for (var i = 0; i < houses.length; i++) { var houseLeft = houses[i].x - 1024; var houseRight = houses[i].x + 1024; var targetX = Math.max(houseLeft, Math.min(houseRight, self.x)); var dx = targetX - self.x; var dy = houses[i].y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < minDist) { minDist = dist; self.target = { x: targetX, y: houses[i].y, takeDamage: houses[i].takeDamage.bind(houses[i]), destroyed: houses[i].destroyed }; } } }; self.takeDamage = function (damage) { self.health -= damage; if (self.health <= 0) { LK.setScore(LK.getScore() + 150); LK.getSound('zombieDeath').play(); self.destroy(); return true; } LK.effects.flashObject(self, 0xFFFFFF, 200); return false; }; self.freeze = function () { self.frozen = 120; }; return self; }); var Rocket = Container.expand(function () { var self = Container.call(this); var rocketGraphics = self.attachAsset('rocket', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 10; self.damage = 80; self.explosionRange = 120; self.targetX = 0; self.targetY = 0; self.update = function () { var dx = self.targetX - self.x; var dy = self.targetY - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 8) { self.x += dx / dist * self.speed; self.y += dy / dist * self.speed; // Rotate rocket to face movement direction var angle = Math.atan2(dy, dx) + Math.PI / 2; rocketGraphics.rotation = angle; } else { // Explode at target location self.explode(); } }; self.explode = function () { LK.getSound('explode').play(); var explosion = game.addChild(LK.getAsset('explosion', { anchorX: 0.5, anchorY: 0.5 })); explosion.x = self.x; explosion.y = self.y; explosion.alpha = 0.9; explosion.width = self.explosionRange * 2; explosion.height = self.explosionRange * 2; tween(explosion, { alpha: 0, scaleX: 1.5, scaleY: 1.5 }, { duration: 400, onFinish: function onFinish() { explosion.destroy(); } }); // Damage all zombies in explosion range for (var i = 0; i < zombies.length; i++) { var dx = zombies[i].x - self.x; var dy = zombies[i].y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < self.explosionRange) { zombies[i].takeDamage(self.damage); } } self.destroy(); }; return self; }); var RocketTurret = Container.expand(function () { var self = Container.call(this); var tower = self.attachAsset('rocketTurret', { anchorX: 0.5, anchorY: 0.5 }); self.damage = 80; self.range = 300; self.explosionRange = 120; self.fireRate = 120; // Slow but powerful self.fireTimer = 0; self.health = 150; self.maxHealth = 150; self.update = function () { self.fireTimer++; if (self.fireTimer >= self.fireRate) { self.fireTimer = 0; self.findAndShoot(); } }; self.findAndShoot = function () { var target = null; var minDist = self.range; for (var i = 0; i < zombies.length; i++) { var dx = zombies[i].x - self.x; var dy = zombies[i].y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist <= self.range) { if (dist < minDist) { minDist = dist; target = zombies[i]; } } } if (target) { // Aim turret at target var dx = target.x - self.x; var dy = target.y - self.y; var angle = Math.atan2(dy, dx) + Math.PI / 2; self.children[0].rotation = angle; self.shootRocket(target); } else { // No target in range, return to default direction (facing up) self.children[0].rotation = 0; } }; self.shootRocket = function (target) { LK.getSound('rocket').play(); var rocket = new Rocket(); rocket.x = self.x; rocket.y = self.y; rocket.targetX = target.x; rocket.targetY = target.y; rocket.damage = self.damage; rocket.explosionRange = self.explosionRange; game.addChild(rocket); }; self.takeDamage = function (damage) { self.health -= damage; if (self.health <= 0) { self.destroy(); return true; } LK.effects.flashObject(self, 0xFF0000, 300); return false; }; return self; }); var RunnerZombie = Container.expand(function () { var self = Container.call(this); var zombieGraphics = self.attachAsset('runnerZombie', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 1.4; self.health = 30; self.damage = 8; self.target = null; self.attackTimer = 0; self.frozen = 0; self.update = function () { if (self.frozen > 0) { self.frozen--; zombieGraphics.tint = 0x87CEEB; self.speed = 0.7; } else { zombieGraphics.tint = 0xFFFFFF; self.speed = 1.4; } if (!self.target || self.target.destroyed) { self.findTarget(); } if (self.target) { var dx = self.target.x - self.x; var dy = self.target.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 50) { self.x += dx / dist * self.speed; self.y += dy / dist * self.speed; } else { self.attackTimer++; if (self.attackTimer >= 60) { self.attackTimer = 0; if (self.target.takeDamage) { if (self.target.takeDamage(self.damage)) { self.target = null; } } } } } else { self.y += self.speed; if (self.y > 2732) { lives--; self.destroy(); } } }; self.findTarget = function () { var minDist = Infinity; self.target = null; for (var i = 0; i < houses.length; i++) { var houseLeft = houses[i].x - 1024; var houseRight = houses[i].x + 1024; var targetX = Math.max(houseLeft, Math.min(houseRight, self.x)); var dx = targetX - self.x; var dy = houses[i].y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < minDist) { minDist = dist; self.target = { x: targetX, y: houses[i].y, takeDamage: houses[i].takeDamage.bind(houses[i]), destroyed: houses[i].destroyed }; } } }; self.takeDamage = function (damage) { self.health -= damage; if (self.health <= 0) { LK.setScore(LK.getScore() + 8); LK.getSound('zombieDeath').play(); self.destroy(); return true; } LK.effects.flashObject(self, 0xFFFFFF, 200); return false; }; self.freeze = function () { self.frozen = 180; }; return self; }); var ShamanZombie = Container.expand(function () { var self = Container.call(this); var bossGraphics = self.attachAsset('shamanZombie', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 0.25; self.health = 400; self.damage = 20; self.target = null; self.attackTimer = 0; self.frozen = 0; self.summonTimer = 0; self.healTimer = 0; self.lowHealthHealed = false; self.update = function () { // Handle burning effect if (self.burnDuration > 0) { self.burnDuration--; if (self.burnDuration % 10 === 0) { self.takeDamage(self.burnDamage || 3); } } // Handle acid effect if (self.acidDuration > 0) { self.acidDuration--; if (self.acidDuration % 15 === 0) { self.takeDamage(self.acidDamage || 2); } } if (self.frozen > 0) { self.frozen--; bossGraphics.tint = 0x87CEEB; self.speed = 0.125; } else if (self.burnDuration > 0) { bossGraphics.tint = 0xFF4500; self.speed = 0.25; } else if (self.acidDuration > 0) { bossGraphics.tint = 0x32CD32; self.speed = 0.25; } else { bossGraphics.tint = 0x800080; self.speed = 0.25; } // Healing when low on health if (self.health < 100 && !self.lowHealthHealed) { self.healTimer++; if (self.healTimer >= 120) { // 2 seconds self.health += 150; self.lowHealthHealed = true; LK.effects.flashObject(self, 0x00FF00, 500); } } // Summon fake zombies periodically self.summonTimer++; if (self.summonTimer >= 300) { // Every 5 seconds self.summonTimer = 0; self.summonFakeZombies(); } if (!self.target || self.target.destroyed) { self.findTarget(); } if (self.target) { var dx = self.target.x - self.x; var dy = self.target.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 60) { self.x += dx / dist * self.speed; self.y += dy / dist * self.speed; } else { self.attackTimer++; if (self.attackTimer >= 45) { self.attackTimer = 0; if (self.target.takeDamage) { if (self.target.takeDamage(self.damage)) { self.target = null; } } } } } else { self.y += self.speed; if (self.y > 2732) { lives -= 3; self.destroy(); } } }; self.summonFakeZombies = function () { for (var i = 0; i < 2; i++) { var fakeZombie = new FakeZombie(); fakeZombie.x = self.x + (Math.random() - 0.5) * 100; fakeZombie.y = self.y + (Math.random() - 0.5) * 100; zombies.push(fakeZombie); game.addChild(fakeZombie); } LK.effects.flashObject(self, 0x800080, 300); }; self.findTarget = function () { var minDist = Infinity; self.target = null; // Prioritize houses first for (var i = 0; i < houses.length; i++) { // Create a virtual target point spread across the house width var houseLeft = houses[i].x - 1024; // Left edge of house var houseRight = houses[i].x + 1024; // Right edge of house var targetX = Math.max(houseLeft, Math.min(houseRight, self.x)); // Clamp zombie x to house width var dx = targetX - self.x; var dy = houses[i].y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < minDist) { minDist = dist; self.target = { x: targetX, y: houses[i].y, takeDamage: houses[i].takeDamage.bind(houses[i]), destroyed: houses[i].destroyed }; } } // Skip solar collectors and towers - they are not targeted by boss zombies }; self.takeDamage = function (damage) { self.health -= damage; if (self.health <= 0) { LK.setScore(LK.getScore() + 150); LK.getSound('zombieDeath').play(); self.destroy(); return true; } LK.effects.flashObject(self, 0xFFFFFF, 200); return false; }; self.freeze = function () { self.frozen = 120; }; return self; }); var SolarCollector = Container.expand(function () { var self = Container.call(this); var collector = self.attachAsset('solarCollector', { anchorX: 0.5, anchorY: 0.5 }); self.energyRate = 1; self.health = 100; self.maxHealth = 100; self.isDaytime = true; self.energyTimer = 0; self.energyGenerationTime = Math.floor(Math.random() * 300) + 600; // Random 10-15 seconds (600-900 frames at 60fps) self.update = function () { self.energyTimer++; if (self.energyTimer >= self.energyGenerationTime) { self.energyTimer = 0; // Set new random interval for next generation self.energyGenerationTime = Math.floor(Math.random() * 300) + 600; // Random 10-15 seconds var energyGain = 10; energy += energyGain; LK.getSound('collect').play(); var energyText = new Text2('+' + energyGain, { size: 30, fill: 0xFFD700 }); energyText.anchor.set(0.5, 0.5); energyText.x = 0; energyText.y = -40; self.addChild(energyText); tween(energyText, { y: -80, alpha: 0 }, { duration: 1000, onFinish: function onFinish() { energyText.destroy(); } }); } collector.tint = self.isDaytime ? 0xFFD700 : 0xB8860B; }; self.takeDamage = function (damage) { self.health -= damage; if (self.health <= 0) { self.destroy(); return true; } LK.effects.flashObject(self, 0xFF0000, 300); return false; }; return self; }); var SoldierZombie = Container.expand(function () { var self = Container.call(this); var bossGraphics = self.attachAsset('soldierZombie', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 0.35; self.health = 300; self.damage = 22; self.target = null; self.attackTimer = 0; self.frozen = 0; self.groupSpawned = false; self.update = function () { // Handle burning effect if (self.burnDuration > 0) { self.burnDuration--; if (self.burnDuration % 10 === 0) { self.takeDamage(self.burnDamage || 3); } } // Handle acid effect if (self.acidDuration > 0) { self.acidDuration--; if (self.acidDuration % 15 === 0) { self.takeDamage(self.acidDamage || 2); } } if (self.frozen > 0) { self.frozen--; bossGraphics.tint = 0x87CEEB; self.speed = 0.175; } else if (self.burnDuration > 0) { bossGraphics.tint = 0xFF4500; self.speed = 0.35; } else if (self.acidDuration > 0) { bossGraphics.tint = 0x32CD32; self.speed = 0.35; } else { bossGraphics.tint = 0x556b2f; self.speed = 0.35; } // Spawn group when health gets low if (self.health < 150 && !self.groupSpawned) { self.groupSpawned = true; self.spawnGroup(); } if (!self.target || self.target.destroyed) { self.findTarget(); } if (self.target) { var dx = self.target.x - self.x; var dy = self.target.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 60) { self.x += dx / dist * self.speed; self.y += dy / dist * self.speed; } else { self.attackTimer++; if (self.attackTimer >= 45) { self.attackTimer = 0; if (self.target.takeDamage) { if (self.target.takeDamage(self.damage)) { self.target = null; } } } } } else { self.y += self.speed; if (self.y > 2732) { lives -= 3; self.destroy(); } } }; self.spawnGroup = function () { // Spawn 4 regular zombies for (var i = 0; i < 4; i++) { var soldier = new Zombie(); soldier.x = self.x + (Math.random() - 0.5) * 150; soldier.y = self.y + (Math.random() - 0.5) * 150; soldier.health = 40; // Slightly stronger soldier.damage = 12; zombies.push(soldier); game.addChild(soldier); } // Spawn 2 tank zombies for (var i = 0; i < 2; i++) { var tank = new TankZombie(); tank.x = self.x + (Math.random() - 0.5) * 200; tank.y = self.y + (Math.random() - 0.5) * 200; zombies.push(tank); game.addChild(tank); } LK.effects.flashObject(self, 0x556b2f, 500); }; self.findTarget = function () { var minDist = Infinity; self.target = null; for (var i = 0; i < houses.length; i++) { var houseLeft = houses[i].x - 1024; var houseRight = houses[i].x + 1024; var targetX = Math.max(houseLeft, Math.min(houseRight, self.x)); var dx = targetX - self.x; var dy = houses[i].y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < minDist) { minDist = dist; self.target = { x: targetX, y: houses[i].y, takeDamage: houses[i].takeDamage.bind(houses[i]), destroyed: houses[i].destroyed }; } } }; self.takeDamage = function (damage) { self.health -= damage; if (self.health <= 0) { LK.setScore(LK.getScore() + 150); LK.getSound('zombieDeath').play(); self.destroy(); return true; } LK.effects.flashObject(self, 0xFFFFFF, 200); return false; }; self.freeze = function () { self.frozen = 120; }; return self; }); var TankZombie = Container.expand(function () { var self = Container.call(this); var zombieGraphics = self.attachAsset('tankZombie', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 0.2; self.health = 100; self.damage = 18; self.target = null; self.attackTimer = 0; self.frozen = 0; self.update = function () { if (self.frozen > 0) { self.frozen--; zombieGraphics.tint = 0x87CEEB; self.speed = 0.1; } else { zombieGraphics.tint = 0x696969; self.speed = 0.2; } if (!self.target || self.target.destroyed) { self.findTarget(); } if (self.target) { var dx = self.target.x - self.x; var dy = self.target.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 50) { self.x += dx / dist * self.speed; self.y += dy / dist * self.speed; } else { self.attackTimer++; if (self.attackTimer >= 60) { self.attackTimer = 0; if (self.target.takeDamage) { if (self.target.takeDamage(self.damage)) { self.target = null; } } } } } else { self.y += self.speed; if (self.y > 2732) { lives--; self.destroy(); } } }; self.findTarget = function () { var minDist = Infinity; self.target = null; for (var i = 0; i < houses.length; i++) { var houseLeft = houses[i].x - 1024; var houseRight = houses[i].x + 1024; var targetX = Math.max(houseLeft, Math.min(houseRight, self.x)); var dx = targetX - self.x; var dy = houses[i].y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < minDist) { minDist = dist; self.target = { x: targetX, y: houses[i].y, takeDamage: houses[i].takeDamage.bind(houses[i]), destroyed: houses[i].destroyed }; } } }; self.takeDamage = function (damage) { self.health -= damage; if (self.health <= 0) { LK.setScore(LK.getScore() + 20); LK.getSound('zombieDeath').play(); self.destroy(); return true; } LK.effects.flashObject(self, 0xFFFFFF, 200); return false; }; self.freeze = function () { self.frozen = 120; }; return self; }); var ToughZombie = Container.expand(function () { var self = Container.call(this); var zombieGraphics = self.attachAsset('toughZombie', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 0.4; self.health = 120; self.damage = 15; self.target = null; self.attackTimer = 0; self.frozen = 0; self.update = function () { if (self.frozen > 0) { self.frozen--; zombieGraphics.tint = 0x87CEEB; self.speed = 0.2; } else { zombieGraphics.tint = 0xFFFFFF; self.speed = 0.4; } if (!self.target || self.target.destroyed) { self.findTarget(); } if (self.target) { var dx = self.target.x - self.x; var dy = self.target.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 50) { self.x += dx / dist * self.speed; self.y += dy / dist * self.speed; } else { self.attackTimer++; if (self.attackTimer >= 60) { self.attackTimer = 0; if (self.target.takeDamage) { if (self.target.takeDamage(self.damage)) { self.target = null; } } } } } else { self.y += self.speed; if (self.y > 2732) { lives--; self.destroy(); } } }; self.findTarget = function () { var minDist = Infinity; self.target = null; for (var i = 0; i < houses.length; i++) { var houseLeft = houses[i].x - 1024; var houseRight = houses[i].x + 1024; var targetX = Math.max(houseLeft, Math.min(houseRight, self.x)); var dx = targetX - self.x; var dy = houses[i].y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < minDist) { minDist = dist; self.target = { x: targetX, y: houses[i].y, takeDamage: houses[i].takeDamage.bind(houses[i]), destroyed: houses[i].destroyed }; } } }; self.takeDamage = function (damage) { self.health -= damage; if (self.health <= 0) { LK.setScore(LK.getScore() + 15); LK.getSound('zombieDeath').play(); self.destroy(); return true; } LK.effects.flashObject(self, 0xFFFFFF, 200); return false; }; self.freeze = function () { self.frozen = 180; }; return self; }); var TripleBarrelTurret = Container.expand(function () { var self = Container.call(this); var tower = self.attachAsset('tripleBarrelTurret', { anchorX: 0.5, anchorY: 0.5 }); self.damage = 15; self.range = 300; self.fireRate = 30; self.fireTimer = 0; self.health = 160; self.maxHealth = 160; self.update = function () { self.fireTimer++; if (self.fireTimer >= self.fireRate) { self.fireTimer = 0; self.findAndShoot(); } }; self.findAndShoot = function () { var targets = []; // Find all zombies in range and sort by distance var zombiesInRange = []; for (var i = 0; i < zombies.length; i++) { var dx = zombies[i].x - self.x; var dy = zombies[i].y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist <= self.range) { zombiesInRange.push({ zombie: zombies[i], distance: dist }); } } // Sort by distance and take closest 3 zombiesInRange.sort(function (a, b) { return a.distance - b.distance; }); for (var i = 0; i < Math.min(3, zombiesInRange.length); i++) { targets.push(zombiesInRange[i].zombie); } if (targets.length > 0) { // Aim at closest target var dx = targets[0].x - self.x; var dy = targets[0].y - self.y; var angle = Math.atan2(dy, dx) + Math.PI / 2; self.children[0].rotation = angle; // Shoot three bullets for (var i = 0; i < targets.length; i++) { self.shootBullet(targets[i]); } } else { // No targets in range, return to default direction (facing up) self.children[0].rotation = 0; } }; self.shootBullet = function (target) { LK.getSound('laser').play(); var bullet = new Bullet(); bullet.x = self.x; bullet.y = self.y; bullet.targetX = target.x; bullet.targetY = target.y; bullet.damage = self.damage; game.addChild(bullet); }; self.takeDamage = function (damage) { self.health -= damage; if (self.health <= 0) { self.destroy(); return true; } LK.effects.flashObject(self, 0xFF0000, 300); return false; }; return self; }); var UmbrellaZombie = Container.expand(function () { var self = Container.call(this); var zombieGraphics = self.attachAsset('umbrellaZombie', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 0.6; self.health = 50; self.damage = 10; self.umbrellaHealth = 40; self.hasUmbrella = true; self.target = null; self.attackTimer = 0; self.frozen = 0; self.update = function () { if (self.frozen > 0) { self.frozen--; zombieGraphics.tint = 0x87CEEB; self.speed = 0.3; } else { zombieGraphics.tint = self.hasUmbrella ? 0x9370DB : 0xFFFFFF; self.speed = 0.6; } if (!self.target || self.target.destroyed) { self.findTarget(); } if (self.target) { var dx = self.target.x - self.x; var dy = self.target.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 50) { self.x += dx / dist * self.speed; self.y += dy / dist * self.speed; } else { self.attackTimer++; if (self.attackTimer >= 60) { self.attackTimer = 0; if (self.target.takeDamage) { if (self.target.takeDamage(self.damage)) { self.target = null; } } } } } else { self.y += self.speed; if (self.y > 2732) { lives--; self.destroy(); } } }; self.findTarget = function () { var minDist = Infinity; self.target = null; for (var i = 0; i < houses.length; i++) { var houseLeft = houses[i].x - 1024; var houseRight = houses[i].x + 1024; var targetX = Math.max(houseLeft, Math.min(houseRight, self.x)); var dx = targetX - self.x; var dy = houses[i].y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < minDist) { minDist = dist; self.target = { x: targetX, y: houses[i].y, takeDamage: houses[i].takeDamage.bind(houses[i]), destroyed: houses[i].destroyed }; } } }; self.takeDamage = function (damage) { if (self.hasUmbrella) { self.umbrellaHealth -= damage; if (self.umbrellaHealth <= 0) { self.hasUmbrella = false; LK.effects.flashObject(self, 0xFF0000, 300); } else { LK.effects.flashObject(self, 0x9370DB, 200); } return false; } else { self.health -= damage; if (self.health <= 0) { LK.setScore(LK.getScore() + 12); LK.getSound('zombieDeath').play(); self.destroy(); return true; } LK.effects.flashObject(self, 0xFFFFFF, 200); return false; } }; self.freeze = function () { self.frozen = 180; }; return self; }); var Zombie = Container.expand(function () { var self = Container.call(this); var zombieGraphics = self.attachAsset('zombie', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 0.7; self.health = 50; self.damage = 10; self.target = null; self.attackTimer = 0; self.frozen = 0; self.update = function () { // Handle burning effect if (self.burnDuration > 0) { self.burnDuration--; if (self.burnDuration % 10 === 0) { // Damage every 10 frames self.takeDamage(self.burnDamage || 3); } } // Handle acid effect if (self.acidDuration > 0) { self.acidDuration--; if (self.acidDuration % 15 === 0) { // Damage every 15 frames self.takeDamage(self.acidDamage || 2); } } if (self.frozen > 0) { self.frozen--; zombieGraphics.tint = 0x87CEEB; self.speed = 0.3; } else if (self.burnDuration > 0) { zombieGraphics.tint = 0xFF4500; // Orange tint for burning self.speed = 0.7; } else if (self.acidDuration > 0) { zombieGraphics.tint = 0x32CD32; // Green tint for acid self.speed = 0.7; } else { zombieGraphics.tint = 0xFFFFFF; self.speed = 0.7; } if (!self.target || self.target.destroyed) { self.findTarget(); } if (self.target) { var dx = self.target.x - self.x; var dy = self.target.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 50) { self.x += dx / dist * self.speed; self.y += dy / dist * self.speed; } else { self.attackTimer++; if (self.attackTimer >= 60) { self.attackTimer = 0; if (self.target.takeDamage) { if (self.target.takeDamage(self.damage)) { self.target = null; } } } } } else { // Move towards bottom of screen when no target self.y += self.speed; if (self.y > 2732) { lives--; self.destroy(); } } }; self.findTarget = function () { var minDist = Infinity; self.target = null; // Prioritize houses first for (var i = 0; i < houses.length; i++) { // Create a virtual target point spread across the house width var houseLeft = houses[i].x - 1024; // Left edge of house var houseRight = houses[i].x + 1024; // Right edge of house var targetX = Math.max(houseLeft, Math.min(houseRight, self.x)); // Clamp zombie x to house width var dx = targetX - self.x; var dy = houses[i].y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < minDist) { minDist = dist; self.target = { x: targetX, y: houses[i].y, takeDamage: houses[i].takeDamage.bind(houses[i]), destroyed: houses[i].destroyed }; } } // Skip solar collectors and towers - they are not targeted by zombies }; self.takeDamage = function (damage) { self.health -= damage; if (self.health <= 0) { LK.setScore(LK.getScore() + 10); LK.getSound('zombieDeath').play(); self.destroy(); return true; } LK.effects.flashObject(self, 0xFFFFFF, 200); return false; }; self.freeze = function () { self.frozen = 180; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x87CEEB }); /**** * Game Code ****/ var energy = 100; var wave = 0; var lives = 20; var isDaytime = true; var dayTimer = 0; var waveTimer = 0; var zombieSpawnTimer = 0; var zombiesPerWave = 5; var zombiesToSpawn = 0; var bossProgress = 0; var bossProgressMax = 1000; // Will be 300 seconds at 60fps with new increment rate var currentStage = 1; var bossActive = false; var difficultyMultiplier = 1; var easyPeriodTimer = 0; var easyPeriodDuration = 2100; // 35 seconds at 60fps var isEasyPeriod = true; var gameTimer = 0; // Track total game time in frames (60fps) var specialZombiesUnlocked = false; // Track if special zombies are unlocked var currentPage = 0; // Current turret page (0 = basic turrets, 1 = special turrets, 2 = advanced turrets) var maxPages = 2; // Total number of pages (0, 1, 2) var solarCollectors = []; var towers = []; var zombies = []; var houses = []; var gridCells = []; var selectedTower = null; var placementMode = false; var placementCost = 0; var dragging = false; var dragTurret = null; var GRID_SIZE = 120; var GRID_COLS = Math.floor(2048 / GRID_SIZE); var GRID_ROWS = Math.floor(2200 / GRID_SIZE); // Extend to house area boundary var energyText = new Text2('Energy: ' + energy, { size: 60, fill: 0xFFD700 }); energyText.anchor.set(0.5, 0); LK.gui.top.addChild(energyText); var waveText = new Text2('Wave: ' + wave, { size: 50, fill: 0xFFFFFF }); waveText.anchor.set(0, 0); waveText.x = -900; waveText.y = 100; LK.gui.top.addChild(waveText); var livesText = new Text2('Lives: ' + lives, { size: 50, fill: 0xFF0000 }); livesText.anchor.set(1, 0); livesText.x = 900; livesText.y = 100; LK.gui.top.addChild(livesText); var dayText = new Text2('Day', { size: 40, fill: 0xFFD700 }); dayText.anchor.set(0.5, 0); dayText.y = 80; LK.gui.top.addChild(dayText); var progressBarBg = LK.getAsset('progressBarBg', { anchorX: 0.5, anchorY: 0.5 }); progressBarBg.x = 0; progressBarBg.y = 150; LK.gui.top.addChild(progressBarBg); var progressBarFill = LK.getAsset('progressBarFill', { anchorX: 0, anchorY: 0.5 }); progressBarFill.x = -200; progressBarFill.y = 150; progressBarFill.width = 0; LK.gui.top.addChild(progressBarFill); var progressText = new Text2('Boss Progress', { size: 30, fill: 0xFFFFFF }); progressText.anchor.set(0.5, 0.5); progressText.x = 0; progressText.y = 180; LK.gui.top.addChild(progressText); var stageText = new Text2('Stage: 1', { size: 35, fill: 0xFFD700 }); stageText.anchor.set(0, 0); stageText.x = -900; stageText.y = 150; LK.gui.top.addChild(stageText); var solarButton = new Container(); var solarButtonBg = solarButton.attachAsset('solarCollector', { anchorX: 0.5, anchorY: 0.5 }); solarButtonBg.width = 100; solarButtonBg.height = 100; var solarButtonText = new Text2('Solar: 25', { size: 25, fill: 0xFFD700 }); solarButtonText.anchor.set(0.5, 0.5); solarButtonText.y = -60; solarButton.addChild(solarButtonText); solarButton.x = -300; LK.gui.bottom.addChild(solarButton); var classicButton = new Container(); var classicButtonBg = classicButton.attachAsset('classicTurret', { anchorX: 0.5, anchorY: 0.5 }); classicButtonBg.width = 100; classicButtonBg.height = 100; var classicButtonText = new Text2('Solar: 50', { size: 25, fill: 0xFFD700 }); classicButtonText.anchor.set(0.5, 0.5); classicButtonText.y = -60; classicButton.addChild(classicButtonText); classicButton.x = -100; LK.gui.bottom.addChild(classicButton); var doubleButton = new Container(); var doubleButtonBg = doubleButton.attachAsset('doubleBarrelTurret', { anchorX: 0.5, anchorY: 0.5 }); doubleButtonBg.width = 100; doubleButtonBg.height = 100; var doubleButtonText = new Text2('Solar: 75', { size: 25, fill: 0xFFD700 }); doubleButtonText.anchor.set(0.5, 0.5); doubleButtonText.y = -60; doubleButton.addChild(doubleButtonText); doubleButton.x = 100; LK.gui.bottom.addChild(doubleButton); var tripleButton = new Container(); var tripleButtonBg = tripleButton.attachAsset('tripleBarrelTurret', { anchorX: 0.5, anchorY: 0.5 }); tripleButtonBg.width = 100; tripleButtonBg.height = 100; var tripleButtonText = new Text2('Solar: 100', { size: 25, fill: 0xFFD700 }); tripleButtonText.anchor.set(0.5, 0.5); tripleButtonText.y = -60; tripleButton.addChild(tripleButtonText); tripleButton.x = 300; LK.gui.bottom.addChild(tripleButton); var frozenButton = new Container(); var frozenButtonBg = frozenButton.attachAsset('frozenTurret', { anchorX: 0.5, anchorY: 0.5 }); frozenButtonBg.width = 100; frozenButtonBg.height = 100; var frozenButtonText = new Text2('Solar: 60', { size: 25, fill: 0xFFD700 }); frozenButtonText.anchor.set(0.5, 0.5); frozenButtonText.y = -60; frozenButton.addChild(frozenButtonText); frozenButton.x = -200; frozenButton.y = -150; LK.gui.bottom.addChild(frozenButton); var flameButton = new Container(); var flameButtonBg = flameButton.attachAsset('flameThrowerTurret', { anchorX: 0.5, anchorY: 0.5 }); flameButtonBg.width = 100; flameButtonBg.height = 100; var flameButtonText = new Text2('Solar: 80', { size: 25, fill: 0xFFD700 }); flameButtonText.anchor.set(0.5, 0.5); flameButtonText.y = -60; flameButton.addChild(flameButtonText); flameButton.x = 0; flameButton.y = -150; LK.gui.bottom.addChild(flameButton); var acidButton = new Container(); var acidButtonBg = acidButton.attachAsset('acidTurret', { anchorX: 0.5, anchorY: 0.5 }); acidButtonBg.width = 100; acidButtonBg.height = 100; var acidButtonText = new Text2('Solar: 70', { size: 25, fill: 0xFFD700 }); acidButtonText.anchor.set(0.5, 0.5); acidButtonText.y = -60; acidButton.addChild(acidButtonText); acidButton.x = 200; acidButton.y = -150; LK.gui.bottom.addChild(acidButton); var autoButton = new Container(); var autoButtonBg = autoButton.attachAsset('autoTurret', { anchorX: 0.5, anchorY: 0.5 }); autoButtonBg.width = 100; autoButtonBg.height = 100; var autoButtonText = new Text2('Solar: 120', { size: 25, fill: 0xFFD700 }); autoButtonText.anchor.set(0.5, 0.5); autoButtonText.y = -60; autoButton.addChild(autoButtonText); autoButton.x = -100; autoButton.y = -250; LK.gui.bottom.addChild(autoButton); var minigunButton = new Container(); var minigunButtonBg = minigunButton.attachAsset('minigunTurret', { anchorX: 0.5, anchorY: 0.5 }); minigunButtonBg.width = 100; minigunButtonBg.height = 100; var minigunButtonText = new Text2('Solar: 90', { size: 25, fill: 0xFFD700 }); minigunButtonText.anchor.set(0.5, 0.5); minigunButtonText.y = -60; minigunButton.addChild(minigunButtonText); minigunButton.x = 100; minigunButton.y = -250; LK.gui.bottom.addChild(minigunButton); var rocketButton = new Container(); var rocketButtonBg = rocketButton.attachAsset('rocketTurret', { anchorX: 0.5, anchorY: 0.5 }); rocketButtonBg.width = 100; rocketButtonBg.height = 100; var rocketButtonText = new Text2('Solar: 150', { size: 25, fill: 0xFFD700 }); rocketButtonText.anchor.set(0.5, 0.5); rocketButtonText.y = -60; rocketButton.addChild(rocketButtonText); rocketButton.x = 0; rocketButton.y = -250; LK.gui.bottom.addChild(rocketButton); // Create navigation arrows var leftArrow = new Container(); var leftArrowBg = leftArrow.attachAsset('gridCell', { anchorX: 0.5, anchorY: 0.5 }); leftArrowBg.width = 80; leftArrowBg.height = 80; leftArrowBg.tint = 0x4169E1; var leftArrowText = new Text2('<', { size: 60, fill: 0xFFFFFF }); leftArrowText.anchor.set(0.5, 0.5); leftArrow.addChild(leftArrowText); leftArrow.x = -450; leftArrow.y = 0; LK.gui.bottom.addChild(leftArrow); var rightArrow = new Container(); var rightArrowBg = rightArrow.attachAsset('gridCell', { anchorX: 0.5, anchorY: 0.5 }); rightArrowBg.width = 80; rightArrowBg.height = 80; rightArrowBg.tint = 0x4169E1; var rightArrowText = new Text2('>', { size: 60, fill: 0xFFFFFF }); rightArrowText.anchor.set(0.5, 0.5); rightArrow.addChild(rightArrowText); rightArrow.x = 450; rightArrow.y = 0; LK.gui.bottom.addChild(rightArrow); // Create page indicator text var pageText = new Text2('Page 1/3', { size: 30, fill: 0xFFFFFF }); pageText.anchor.set(0.5, 0.5); pageText.x = 0; pageText.y = 100; LK.gui.bottom.addChild(pageText); for (var row = 0; row < GRID_ROWS; row++) { for (var col = 0; col < GRID_COLS; col++) { var cell = game.addChild(LK.getAsset('gridCell', { anchorX: 0, anchorY: 0 })); cell.x = col * GRID_SIZE; cell.y = row * GRID_SIZE + 200; cell.alpha = 0.1; cell.gridX = col; cell.gridY = row; cell.occupied = false; gridCells.push(cell); } } // Create single house spanning the bottom of screen var house = new House(); house.x = 1024; // Center of screen width (2048/2) house.y = 2500; houses.push(house); game.addChild(house); // Initialize turret page display updateTurretPage(); solarButton.down = function (x, y, obj) { if (energy < 25) { // Flash button red to indicate insufficient energy LK.effects.flashObject(solarButton, 0xff0000, 300); return; } dragging = true; selectedTower = 'solar'; placementCost = 25; dragTurret = game.addChild(LK.getAsset('solarCollector', { anchorX: 0.5, anchorY: 0.5 })); dragTurret.width = 120; dragTurret.height = 120; dragTurret.alpha = 0.8; // Convert GUI position to game coordinates var globalPos = game.toLocal({ x: solarButton.x, y: solarButton.y }); dragTurret.x = globalPos.x; dragTurret.y = globalPos.y; // Flash button green to indicate drag started LK.effects.flashObject(solarButton, 0x00ff00, 200); }; classicButton.down = function (x, y, obj) { if (energy < 50) return; dragging = true; selectedTower = 'classic'; placementCost = 50; dragTurret = game.addChild(LK.getAsset('classicTurret', { anchorX: 0.5, anchorY: 0.5 })); dragTurret.width = 60; dragTurret.height = 60; dragTurret.alpha = 0.7; // Convert GUI position to game coordinates var globalPos = game.toLocal({ x: classicButton.x, y: classicButton.y }); dragTurret.x = globalPos.x; dragTurret.y = globalPos.y; }; doubleButton.down = function (x, y, obj) { if (energy < 75) return; dragging = true; selectedTower = 'double'; placementCost = 75; dragTurret = game.addChild(LK.getAsset('doubleBarrelTurret', { anchorX: 0.5, anchorY: 0.5 })); dragTurret.width = 60; dragTurret.height = 60; dragTurret.alpha = 0.7; // Convert GUI position to game coordinates var globalPos = game.toLocal({ x: doubleButton.x, y: doubleButton.y }); dragTurret.x = globalPos.x; dragTurret.y = globalPos.y; }; tripleButton.down = function (x, y, obj) { if (energy < 100) return; dragging = true; selectedTower = 'triple'; placementCost = 100; dragTurret = game.addChild(LK.getAsset('tripleBarrelTurret', { anchorX: 0.5, anchorY: 0.5 })); dragTurret.width = 60; dragTurret.height = 60; dragTurret.alpha = 0.7; // Convert GUI position to game coordinates var globalPos = game.toLocal({ x: tripleButton.x, y: tripleButton.y }); dragTurret.x = globalPos.x; dragTurret.y = globalPos.y; }; frozenButton.down = function (x, y, obj) { if (energy < 60) return; dragging = true; selectedTower = 'frozen'; placementCost = 60; dragTurret = game.addChild(LK.getAsset('frozenTurret', { anchorX: 0.5, anchorY: 0.5 })); dragTurret.width = 60; dragTurret.height = 60; dragTurret.alpha = 0.7; var globalPos = game.toLocal({ x: frozenButton.x, y: frozenButton.y }); dragTurret.x = globalPos.x; dragTurret.y = globalPos.y; }; flameButton.down = function (x, y, obj) { if (energy < 80) return; dragging = true; selectedTower = 'flame'; placementCost = 80; dragTurret = game.addChild(LK.getAsset('flameThrowerTurret', { anchorX: 0.5, anchorY: 0.5 })); dragTurret.width = 60; dragTurret.height = 60; dragTurret.alpha = 0.7; var globalPos = game.toLocal({ x: flameButton.x, y: flameButton.y }); dragTurret.x = globalPos.x; dragTurret.y = globalPos.y; }; acidButton.down = function (x, y, obj) { if (energy < 70) return; dragging = true; selectedTower = 'acid'; placementCost = 70; dragTurret = game.addChild(LK.getAsset('acidTurret', { anchorX: 0.5, anchorY: 0.5 })); dragTurret.width = 60; dragTurret.height = 60; dragTurret.alpha = 0.7; var globalPos = game.toLocal({ x: acidButton.x, y: acidButton.y }); dragTurret.x = globalPos.x; dragTurret.y = globalPos.y; }; autoButton.down = function (x, y, obj) { if (energy < 120) return; dragging = true; selectedTower = 'auto'; placementCost = 120; dragTurret = game.addChild(LK.getAsset('autoTurret', { anchorX: 0.5, anchorY: 0.5 })); dragTurret.width = 60; dragTurret.height = 60; dragTurret.alpha = 0.7; var globalPos = game.toLocal({ x: autoButton.x, y: autoButton.y }); dragTurret.x = globalPos.x; dragTurret.y = globalPos.y; }; minigunButton.down = function (x, y, obj) { if (energy < 90) return; dragging = true; selectedTower = 'minigun'; placementCost = 90; dragTurret = game.addChild(LK.getAsset('minigunTurret', { anchorX: 0.5, anchorY: 0.5 })); dragTurret.width = 60; dragTurret.height = 60; dragTurret.alpha = 0.7; var globalPos = game.toLocal({ x: minigunButton.x, y: minigunButton.y }); dragTurret.x = globalPos.x; dragTurret.y = globalPos.y; }; rocketButton.down = function (x, y, obj) { if (energy < 150) return; dragging = true; selectedTower = 'rocket'; placementCost = 150; dragTurret = game.addChild(LK.getAsset('rocketTurret', { anchorX: 0.5, anchorY: 0.5 })); dragTurret.width = 60; dragTurret.height = 60; dragTurret.alpha = 0.7; var globalPos = game.toLocal({ x: rocketButton.x, y: rocketButton.y }); dragTurret.x = globalPos.x; dragTurret.y = globalPos.y; }; leftArrow.down = function (x, y, obj) { if (currentPage > 0) { currentPage--; updateTurretPage(); LK.effects.flashObject(leftArrow, 0x00ff00, 200); } else { LK.effects.flashObject(leftArrow, 0xff0000, 200); } }; rightArrow.down = function (x, y, obj) { if (currentPage < maxPages) { currentPage++; updateTurretPage(); LK.effects.flashObject(rightArrow, 0x00ff00, 200); } else { LK.effects.flashObject(rightArrow, 0xff0000, 200); } }; function updateTurretPage() { // Hide all turret buttons first solarButton.visible = false; classicButton.visible = false; doubleButton.visible = false; tripleButton.visible = false; frozenButton.visible = false; flameButton.visible = false; acidButton.visible = false; autoButton.visible = false; minigunButton.visible = false; rocketButton.visible = false; // Page 0: Basic turrets (solar, classic, double, triple) if (currentPage === 0) { solarButton.visible = true; classicButton.visible = true; doubleButton.visible = true; tripleButton.visible = true; } // Page 1: Special turrets (frozen, flame, acid) else if (currentPage === 1) { frozenButton.visible = true; flameButton.visible = true; acidButton.visible = true; // Reposition special turrets to bottom row frozenButton.x = -200; frozenButton.y = 0; flameButton.x = 0; flameButton.y = 0; acidButton.x = 200; acidButton.y = 0; } // Page 2: Advanced turrets (auto, minigun, rocket) else if (currentPage === 2) { autoButton.visible = true; minigunButton.visible = true; rocketButton.visible = true; // Reposition advanced turrets to bottom row autoButton.x = -200; autoButton.y = 0; minigunButton.x = 0; minigunButton.y = 0; rocketButton.x = 200; rocketButton.y = 0; } // Update page indicator pageText.setText('Page ' + (currentPage + 1) + '/' + (maxPages + 1)); // Update arrow visibility/opacity leftArrow.alpha = currentPage > 0 ? 1.0 : 0.5; rightArrow.alpha = currentPage < maxPages ? 1.0 : 0.5; } function highlightValidCells(show) { for (var i = 0; i < gridCells.length; i++) { if (!gridCells[i].occupied) { gridCells[i].alpha = show ? 0.3 : 0.1; gridCells[i].tint = show ? 0x00ff00 : 0xffffff; } else { gridCells[i].alpha = show ? 0.2 : 0.1; gridCells[i].tint = show ? 0xff0000 : 0xffffff; } } } game.down = function (x, y, obj) { // Allow starting drag from anywhere in the game area when not already dragging if (!dragging) { // Check if we clicked near any existing turret buttons area var buttonAreaY = 2732 - 200; // Near bottom of screen if (y > buttonAreaY) { // Near button area, let button handlers manage this return; } // Check if we clicked in the house area (exclude from drag zone) var houseAreaY = 2400; // Above house area if (y > houseAreaY) { // In house area, don't allow dragging return; } } }; game.move = function (x, y, obj) { if (dragging && dragTurret) { dragTurret.x = x; dragTurret.y = y; highlightValidCells(true); // Show range circle for turrets (not solar collectors) if (selectedTower !== 'solar' && !dragTurret.rangeCircle) { var range = 250; // Default range if (selectedTower === 'double') range = 280; if (selectedTower === 'triple') range = 300; if (selectedTower === 'explosive') range = 280; if (selectedTower === 'laser') range = 280; if (selectedTower === 'freeze') range = 150; if (selectedTower === 'frozen') range = 200; if (selectedTower === 'flame') range = 150; if (selectedTower === 'acid') range = 140; if (selectedTower === 'auto') range = 400; if (selectedTower === 'minigun') range = 220; if (selectedTower === 'rocket') range = 300; dragTurret.rangeCircle = dragTurret.addChild(LK.getAsset('rangeCircle', { anchorX: 0.5, anchorY: 0.5 })); dragTurret.rangeCircle.width = range * 2; dragTurret.rangeCircle.height = range * 2; dragTurret.rangeCircle.alpha = 0.2; dragTurret.rangeCircle.tint = 0x00ff00; } // Show visual feedback for valid placement area var gridX = Math.floor(x / GRID_SIZE); var gridY = Math.floor((y - 200) / GRID_SIZE); var houseAreaY = 2400; // House area boundary // Check if in house area (exclude from placement) var inHouseArea = y > houseAreaY; if (gridX >= 0 && gridX < GRID_COLS && gridY >= 0 && gridY < GRID_ROWS && !inHouseArea) { var cellIndex = gridY * GRID_COLS + gridX; if (cellIndex < gridCells.length && !gridCells[cellIndex].occupied) { dragTurret.tint = 0x00ff00; // Green tint for valid placement dragTurret.alpha = 0.8; } else { dragTurret.tint = 0xff0000; // Red tint for invalid placement dragTurret.alpha = 0.5; } } else { dragTurret.tint = 0xff0000; // Red tint for out of bounds or house area dragTurret.alpha = 0.5; } } }; game.up = function (x, y, obj) { if (dragging && dragTurret) { highlightValidCells(false); var gridX = Math.floor(x / GRID_SIZE); var gridY = Math.floor((y - 200) / GRID_SIZE); var validPlacement = false; var houseAreaY = 2400; // House area boundary var inHouseArea = y > houseAreaY; // Expanded validation - check if within game bounds, valid grid, and not in house area if (gridX >= 0 && gridX < GRID_COLS && gridY >= 0 && gridY < GRID_ROWS && !inHouseArea) { var cellIndex = gridY * GRID_COLS + gridX; if (cellIndex < gridCells.length && !gridCells[cellIndex].occupied && energy >= placementCost) { validPlacement = true; energy -= placementCost; gridCells[cellIndex].occupied = true; var newX = gridX * GRID_SIZE + GRID_SIZE / 2; var newY = gridY * GRID_SIZE + GRID_SIZE / 2 + 200; if (selectedTower === 'solar') { var collector = new SolarCollector(); collector.x = newX; collector.y = newY; solarCollectors.push(collector); game.addChild(collector); } else if (selectedTower === 'classic') { var tower = new ClassicTurret(); tower.x = newX; tower.y = newY; towers.push(tower); game.addChild(tower); } else if (selectedTower === 'double') { var tower = new DoubleBarrelTurret(); tower.x = newX; tower.y = newY; towers.push(tower); game.addChild(tower); } else if (selectedTower === 'triple') { var tower = new TripleBarrelTurret(); tower.x = newX; tower.y = newY; towers.push(tower); game.addChild(tower); } else if (selectedTower === 'frozen') { var tower = new FrozenTurret(); tower.x = newX; tower.y = newY; towers.push(tower); game.addChild(tower); } else if (selectedTower === 'flame') { var tower = new FlameThrowerTurret(); tower.x = newX; tower.y = newY; towers.push(tower); game.addChild(tower); } else if (selectedTower === 'acid') { var tower = new AcidTurret(); tower.x = newX; tower.y = newY; towers.push(tower); game.addChild(tower); } else if (selectedTower === 'auto') { var tower = new AutoTurret(); tower.x = newX; tower.y = newY; towers.push(tower); game.addChild(tower); } else if (selectedTower === 'minigun') { var tower = new MinigunTurret(); tower.x = newX; tower.y = newY; towers.push(tower); game.addChild(tower); } else if (selectedTower === 'rocket') { var tower = new RocketTurret(); tower.x = newX; tower.y = newY; towers.push(tower); game.addChild(tower); } LK.getSound('build').play(); // Flash effect for successful placement LK.effects.flashObject(dragTurret, 0x00ff00, 300); } else { // Invalid placement - show error feedback LK.effects.flashObject(dragTurret, 0xff0000, 300); } } else { // Out of bounds or house area - show error feedback LK.effects.flashObject(dragTurret, 0xff0000, 300); } // Clean up drag state dragTurret.destroy(); dragTurret = null; dragging = false; selectedTower = null; placementCost = 0; } }; function spawnZombie(isBoss) { var zombie; if (isBoss) { // Randomly select boss type var bossRoll = Math.random(); if (bossRoll < 0.25) { zombie = new ShamanZombie(); } else if (bossRoll < 0.5) { zombie = new NinjaZombie(); } else if (bossRoll < 0.75) { zombie = new CrazyZombie(); } else { zombie = new SoldierZombie(); } zombie.health *= difficultyMultiplier; zombie.damage = Math.floor(zombie.damage * difficultyMultiplier); bossActive = true; } else { // Determine zombie type based on probability and game time var zombieRoll = Math.random(); var stageMultiplier = Math.min(currentStage, 5); // Cap at stage 5 for variety if (!specialZombiesUnlocked) { // First 100 seconds: only basic zombies, tough zombies, and runners if (zombieRoll < 0.6) { // 60% Basic zombie zombie = new Zombie(); } else if (zombieRoll < 0.8) { // 20% Tough zombie zombie = new ToughZombie(); } else { // 20% Runner zombie zombie = new RunnerZombie(); } } else { // After 100 seconds: all zombie types available if (zombieRoll < 0.5) { // 50% Basic zombie zombie = new Zombie(); } else if (zombieRoll < 0.65) { // 15% Tough zombie zombie = new ToughZombie(); } else if (zombieRoll < 0.8) { // 15% Runner zombie zombie = new RunnerZombie(); } else if (zombieRoll < 0.9) { // 10% Umbrella zombie zombie = new UmbrellaZombie(); } else { // 10% Crows zombie zombie = new CrowsZombie(); } } if (!isEasyPeriod) { zombie.health = Math.floor(zombie.health * difficultyMultiplier); zombie.damage = Math.floor(zombie.damage * difficultyMultiplier); if (zombie.speed) { zombie.speed *= Math.min(difficultyMultiplier * 0.3 + 0.7, 2); } } } // Spawn only from top of screen zombie.x = Math.random() * 2048; zombie.y = 150; zombies.push(zombie); game.addChild(zombie); } function onBossDefeated() { bossActive = false; bossProgress = 0; currentStage++; difficultyMultiplier += 0.5; // Switch day/night cycle isDaytime = !isDaytime; dayText.setText(isDaytime ? 'Day' : 'Night'); dayText.tint = isDaytime ? 0xFFD700 : 0x4169E1; game.setBackgroundColor(isDaytime ? 0x87CEEB : 0x191970); // Update solar collectors for (var i = 0; i < solarCollectors.length; i++) { solarCollectors[i].isDaytime = isDaytime; } // Flash screen effect for stage transition LK.effects.flashScreen(isDaytime ? 0xFFD700 : 0x191970, 1000); // Increase boss progress requirement by 25 seconds (1500 frames at 60fps) bossProgressMax += 84; // 25 seconds * 60fps * 0.056 increment = ~84 progress units } game.update = function () { // Track total game time and unlock special zombies after 100 seconds gameTimer++; if (!specialZombiesUnlocked && gameTimer >= 6000) { // 100 seconds * 60 fps = 6000 frames specialZombiesUnlocked = true; // Flash screen to indicate special zombies are now available LK.effects.flashScreen(0x800080, 500); // Purple flash } // Handle easy period for new players if (isEasyPeriod) { easyPeriodTimer++; if (easyPeriodTimer >= easyPeriodDuration) { isEasyPeriod = false; } } // Update progress bar based on time and zombie kills if (!bossActive) { var progressIncrease = isEasyPeriod ? 0.028 : 0.056; // Reduced to make 300 seconds initially bossProgress += progressIncrease; // Check if boss should spawn if (bossProgress >= bossProgressMax) { spawnZombie(true); } } // Update progress bar visual var progressPercent = Math.min(bossProgress / bossProgressMax, 1); progressBarFill.width = 400 * progressPercent; // Change progress bar color based on proximity to boss if (progressPercent > 0.8) { progressBarFill.tint = 0xff0000; // Red when close to boss } else if (progressPercent > 0.6) { progressBarFill.tint = 0xffaa00; // Orange } else { progressBarFill.tint = 0xff6600; // Default orange } // Animate progress bar when close to boss spawn if (progressPercent > 0.9 && !bossActive) { var pulseScale = 1 + Math.sin(LK.ticks * 0.3) * 0.1; progressBarFill.scaleY = pulseScale; progressBarBg.scaleY = pulseScale; } else { progressBarFill.scaleY = 1; progressBarBg.scaleY = 1; } // Day/night cycle is now only controlled by boss defeats // Remove automatic timer-based day/night switching // Wave-based zombie spawning with limited numbers per wave if (zombiesToSpawn <= 0 && !bossActive) { waveTimer++; // Random delay between waves (10-25 seconds based on difficulty) var minWaveDelay = isEasyPeriod ? 600 : Math.max(600, 900 - Math.floor(difficultyMultiplier * 60)); // 10-15 seconds var maxWaveDelay = isEasyPeriod ? 1500 : Math.max(900, 1500 - Math.floor(difficultyMultiplier * 30)); // 15-25 seconds var randomWaveDelay = Math.floor(Math.random() * (maxWaveDelay - minWaveDelay + 1)) + minWaveDelay; if (waveTimer >= randomWaveDelay) { waveTimer = 0; wave++; // Spawn 3-4 zombies per wave depending on difficulty var baseZombies = isEasyPeriod ? 3 : 3; var maxZombies = isEasyPeriod ? 3 : 4; // Increase chance of 4 zombies as difficulty rises var zombieCount = baseZombies; if (!isEasyPeriod && Math.random() < (difficultyMultiplier - 1) * 0.3) { zombieCount = maxZombies; } zombiesPerWave = zombieCount; zombiesToSpawn = zombiesPerWave; } } else if (zombiesToSpawn > 0) { zombieSpawnTimer++; // Spawn zombies quickly within a wave (every 0.5-1 second) var spawnDelay = isEasyPeriod ? 60 : Math.max(30, 60 - Math.floor(difficultyMultiplier * 5)); if (zombieSpawnTimer >= spawnDelay) { zombieSpawnTimer = 0; zombiesToSpawn--; spawnZombie(false); } } for (var i = solarCollectors.length - 1; i >= 0; i--) { if (solarCollectors[i].destroyed) { solarCollectors.splice(i, 1); } } for (var i = towers.length - 1; i >= 0; i--) { if (towers[i].destroyed) { towers.splice(i, 1); } } for (var i = zombies.length - 1; i >= 0; i--) { if (zombies[i].destroyed) { // Check if destroyed zombie was a boss if ((zombies[i] instanceof ShamanZombie || zombies[i] instanceof NinjaZombie || zombies[i] instanceof CrazyZombie || zombies[i] instanceof SoldierZombie) && bossActive) { // Boss zombies detected by type onBossDefeated(); } else if (!bossActive) { // Regular zombie kill increases boss progress slightly bossProgress += 0.1; // Reduced to match slower progress bar } zombies.splice(i, 1); } } for (var i = houses.length - 1; i >= 0; i--) { if (houses[i].destroyed) { houses.splice(i, 1); } } energyText.setText('Energy: ' + energy); waveText.setText('Wave: ' + wave); livesText.setText('Lives: ' + lives); stageText.setText('Stage: ' + currentStage); // Update progress text based on state if (bossActive) { progressText.setText('BOSS ACTIVE!'); progressText.tint = 0xff0000; var pulseAlpha = 0.5 + Math.sin(LK.ticks * 0.5) * 0.5; progressText.alpha = pulseAlpha; } else if (isEasyPeriod) { progressText.setText('Preparation Time'); progressText.tint = 0x00ff00; progressText.alpha = 1; } else { progressText.setText('Boss Progress: ' + Math.floor(progressPercent * 100) + '%'); progressText.tint = 0xFFFFFF; progressText.alpha = 1; } if (lives <= 0) { LK.showGameOver(); } }; LK.playMusic('gameMusic');
===================================================================
--- original.js
+++ change.js
@@ -129,118 +129,8 @@
return false;
};
return self;
});
-var BossZombie = Container.expand(function () {
- var self = Container.call(this);
- var bossGraphics = self.attachAsset('bossZombie', {
- anchorX: 0.5,
- anchorY: 0.5
- });
- self.speed = 0.3;
- self.health = 500;
- self.damage = 25;
- self.target = null;
- self.attackTimer = 0;
- self.frozen = 0;
- self.update = function () {
- // Handle burning effect
- if (self.burnDuration > 0) {
- self.burnDuration--;
- if (self.burnDuration % 10 === 0) {
- self.takeDamage(self.burnDamage || 3);
- }
- }
- // Handle acid effect
- if (self.acidDuration > 0) {
- self.acidDuration--;
- if (self.acidDuration % 15 === 0) {
- self.takeDamage(self.acidDamage || 2);
- }
- }
- if (self.frozen > 0) {
- self.frozen--;
- bossGraphics.tint = 0x87CEEB;
- self.speed = 0.15;
- } else if (self.burnDuration > 0) {
- bossGraphics.tint = 0xFF4500;
- self.speed = 0.3;
- } else if (self.acidDuration > 0) {
- bossGraphics.tint = 0x32CD32;
- self.speed = 0.3;
- } else {
- bossGraphics.tint = 0xFFFFFF;
- self.speed = 0.3;
- }
- if (!self.target || self.target.destroyed) {
- self.findTarget();
- }
- if (self.target) {
- var dx = self.target.x - self.x;
- var dy = self.target.y - self.y;
- var dist = Math.sqrt(dx * dx + dy * dy);
- if (dist > 60) {
- self.x += dx / dist * self.speed;
- self.y += dy / dist * self.speed;
- } else {
- self.attackTimer++;
- if (self.attackTimer >= 45) {
- self.attackTimer = 0;
- if (self.target.takeDamage) {
- if (self.target.takeDamage(self.damage)) {
- self.target = null;
- }
- }
- }
- }
- } else {
- self.x += self.speed;
- if (self.x > 2048) {
- lives -= 5;
- self.destroy();
- }
- }
- };
- self.findTarget = function () {
- var minDist = Infinity;
- self.target = null;
- // Prioritize houses first
- for (var i = 0; i < houses.length; i++) {
- // Create a virtual target point spread across the house width
- var houseLeft = houses[i].x - 1024; // Left edge of house
- var houseRight = houses[i].x + 1024; // Right edge of house
- var targetX = Math.max(houseLeft, Math.min(houseRight, self.x)); // Clamp zombie x to house width
- var dx = targetX - self.x;
- var dy = houses[i].y - self.y;
- var dist = Math.sqrt(dx * dx + dy * dy);
- if (dist < minDist) {
- minDist = dist;
- self.target = {
- x: targetX,
- y: houses[i].y,
- takeDamage: houses[i].takeDamage.bind(houses[i]),
- destroyed: houses[i].destroyed
- };
- }
- }
- // Skip solar collectors and towers - they are not targeted by boss zombies
- };
- self.takeDamage = function (damage) {
- self.health -= damage;
- if (self.health <= 0) {
- LK.setScore(LK.getScore() + 100);
- LK.getSound('zombieDeath').play();
- self.destroy();
- return true;
- }
- LK.effects.flashObject(self, 0xFFFFFF, 200);
- return false;
- };
- self.freeze = function () {
- self.frozen = 120;
- };
- return self;
-});
var Bullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('bullet', {
anchorX: 0.5,
@@ -339,8 +229,173 @@
return false;
};
return self;
});
+var CrazyZombie = Container.expand(function () {
+ var self = Container.call(this);
+ var bossGraphics = self.attachAsset('crazyZombie', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.speed = 0.3;
+ self.health = 500;
+ self.damage = 25;
+ self.target = null;
+ self.attackTimer = 0;
+ self.frozen = 0;
+ self.digTimer = 0;
+ self.buffTimer = 0;
+ self.isUnderground = false;
+ self.update = function () {
+ // Handle burning effect
+ if (self.burnDuration > 0) {
+ self.burnDuration--;
+ if (self.burnDuration % 10 === 0) {
+ self.takeDamage(self.burnDamage || 3);
+ }
+ }
+ // Handle acid effect
+ if (self.acidDuration > 0) {
+ self.acidDuration--;
+ if (self.acidDuration % 15 === 0) {
+ self.takeDamage(self.acidDamage || 2);
+ }
+ }
+ if (self.frozen > 0) {
+ self.frozen--;
+ bossGraphics.tint = 0x87CEEB;
+ self.speed = 0.15;
+ } else if (self.burnDuration > 0) {
+ bossGraphics.tint = 0xFF4500;
+ self.speed = 0.3;
+ } else if (self.acidDuration > 0) {
+ bossGraphics.tint = 0x32CD32;
+ self.speed = 0.3;
+ } else {
+ bossGraphics.tint = 0x8b4513;
+ self.speed = 0.3;
+ }
+ // Dig underground ability
+ self.digTimer++;
+ if (self.digTimer >= 360) {
+ // Every 6 seconds
+ self.digTimer = 0;
+ self.digUnderground();
+ }
+ // Buff other zombies
+ self.buffTimer++;
+ if (self.buffTimer >= 180) {
+ // Every 3 seconds
+ self.buffTimer = 0;
+ self.buffOtherZombies();
+ }
+ if (!self.isUnderground) {
+ if (!self.target || self.target.destroyed) {
+ self.findTarget();
+ }
+ if (self.target) {
+ var dx = self.target.x - self.x;
+ var dy = self.target.y - self.y;
+ var dist = Math.sqrt(dx * dx + dy * dy);
+ if (dist > 60) {
+ self.x += dx / dist * self.speed;
+ self.y += dy / dist * self.speed;
+ } else {
+ self.attackTimer++;
+ if (self.attackTimer >= 45) {
+ self.attackTimer = 0;
+ if (self.target.takeDamage) {
+ if (self.target.takeDamage(self.damage)) {
+ self.target = null;
+ }
+ }
+ }
+ }
+ } else {
+ self.y += self.speed;
+ if (self.y > 2732) {
+ lives -= 4;
+ self.destroy();
+ }
+ }
+ }
+ };
+ self.digUnderground = function () {
+ self.isUnderground = true;
+ self.alpha = 0.3; // Make translucent
+ // Emerge near a random turret or house
+ var targets = towers.concat(houses);
+ if (targets.length > 0) {
+ var randomTarget = targets[Math.floor(Math.random() * targets.length)];
+ tween(self, {
+ x: randomTarget.x + (Math.random() - 0.5) * 100,
+ y: randomTarget.y + (Math.random() - 0.5) * 100
+ }, {
+ duration: 1000,
+ onFinish: function onFinish() {
+ self.isUnderground = false;
+ self.alpha = 1;
+ LK.effects.flashObject(self, 0x8b4513, 300);
+ }
+ });
+ }
+ };
+ self.buffOtherZombies = function () {
+ for (var i = 0; i < zombies.length; i++) {
+ var zombie = zombies[i];
+ if (zombie !== self) {
+ var dx = zombie.x - self.x;
+ var dy = zombie.y - self.y;
+ var dist = Math.sqrt(dx * dx + dy * dy);
+ if (dist < 200) {
+ // Buff range
+ zombie.speed *= 1.2; // Increase speed
+ zombie.damage = Math.floor(zombie.damage * 1.1); // Increase damage
+ LK.effects.flashObject(zombie, 0xFFD700, 200);
+ }
+ }
+ }
+ };
+ self.findTarget = function () {
+ var minDist = Infinity;
+ self.target = null;
+ for (var i = 0; i < houses.length; i++) {
+ var houseLeft = houses[i].x - 1024;
+ var houseRight = houses[i].x + 1024;
+ var targetX = Math.max(houseLeft, Math.min(houseRight, self.x));
+ var dx = targetX - self.x;
+ var dy = houses[i].y - self.y;
+ var dist = Math.sqrt(dx * dx + dy * dy);
+ if (dist < minDist) {
+ minDist = dist;
+ self.target = {
+ x: targetX,
+ y: houses[i].y,
+ takeDamage: houses[i].takeDamage.bind(houses[i]),
+ destroyed: houses[i].destroyed
+ };
+ }
+ }
+ };
+ self.takeDamage = function (damage) {
+ if (self.isUnderground) {
+ damage = Math.floor(damage * 0.5); // Take less damage underground
+ }
+ self.health -= damage;
+ if (self.health <= 0) {
+ LK.setScore(LK.getScore() + 200);
+ LK.getSound('zombieDeath').play();
+ self.destroy();
+ return true;
+ }
+ LK.effects.flashObject(self, 0xFFFFFF, 200);
+ return false;
+ };
+ self.freeze = function () {
+ self.frozen = 120;
+ };
+ return self;
+});
var CrowZombie = Container.expand(function () {
var self = Container.call(this);
var zombieGraphics = self.attachAsset('crowZombie', {
anchorX: 0.5,
@@ -675,8 +730,109 @@
return false;
};
return self;
});
+var FakeZombie = Container.expand(function () {
+ var self = Container.call(this);
+ var zombieGraphics = self.attachAsset('fakeZombie', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.speed = 0.8;
+ self.health = 10;
+ self.damage = 3;
+ self.target = null;
+ self.attackTimer = 0;
+ self.frozen = 0;
+ self.lifeTimer = 0;
+ self.maxLife = 600; // 10 seconds
+ self.update = function () {
+ self.lifeTimer++;
+ if (self.lifeTimer >= self.maxLife) {
+ // Fade away after 10 seconds
+ tween(self, {
+ alpha: 0
+ }, {
+ duration: 300,
+ onFinish: function onFinish() {
+ self.destroy();
+ }
+ });
+ return;
+ }
+ if (self.frozen > 0) {
+ self.frozen--;
+ zombieGraphics.tint = 0x87CEEB;
+ self.speed = 0.4;
+ } else {
+ zombieGraphics.tint = 0xdda0dd;
+ self.speed = 0.8;
+ }
+ if (!self.target || self.target.destroyed) {
+ self.findTarget();
+ }
+ if (self.target) {
+ var dx = self.target.x - self.x;
+ var dy = self.target.y - self.y;
+ var dist = Math.sqrt(dx * dx + dy * dy);
+ if (dist > 30) {
+ self.x += dx / dist * self.speed;
+ self.y += dy / dist * self.speed;
+ } else {
+ self.attackTimer++;
+ if (self.attackTimer >= 60) {
+ self.attackTimer = 0;
+ if (self.target.takeDamage) {
+ if (self.target.takeDamage(self.damage)) {
+ self.target = null;
+ }
+ }
+ }
+ }
+ } else {
+ self.y += self.speed;
+ if (self.y > 2732) {
+ self.destroy();
+ }
+ }
+ };
+ self.findTarget = function () {
+ var minDist = Infinity;
+ self.target = null;
+ for (var i = 0; i < houses.length; i++) {
+ var houseLeft = houses[i].x - 1024;
+ var houseRight = houses[i].x + 1024;
+ var targetX = Math.max(houseLeft, Math.min(houseRight, self.x));
+ var dx = targetX - self.x;
+ var dy = houses[i].y - self.y;
+ var dist = Math.sqrt(dx * dx + dy * dy);
+ if (dist < minDist) {
+ minDist = dist;
+ self.target = {
+ x: targetX,
+ y: houses[i].y,
+ takeDamage: houses[i].takeDamage.bind(houses[i]),
+ destroyed: houses[i].destroyed
+ };
+ }
+ }
+ };
+ self.takeDamage = function (damage) {
+ self.health -= damage;
+ if (self.health <= 0) {
+ LK.setScore(LK.getScore() + 2);
+ LK.getSound('zombieDeath').play();
+ self.destroy();
+ return true;
+ }
+ LK.effects.flashObject(self, 0xFFFFFF, 200);
+ return false;
+ };
+ self.freeze = function () {
+ self.frozen = 60;
+ };
+ return self;
+});
var FlameThrowerTurret = Container.expand(function () {
var self = Container.call(this);
var tower = self.attachAsset('flameThrowerTurret', {
anchorX: 0.5,
@@ -1071,8 +1227,159 @@
return false;
};
return self;
});
+var NinjaZombie = Container.expand(function () {
+ var self = Container.call(this);
+ var bossGraphics = self.attachAsset('ninjaZombie', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.speed = 0.4;
+ self.health = 350;
+ self.damage = 30;
+ self.target = null;
+ self.attackTimer = 0;
+ self.frozen = 0;
+ self.teleportTimer = 0;
+ self.cloneTimer = 0;
+ self.canClone = true;
+ self.update = function () {
+ // Handle burning effect
+ if (self.burnDuration > 0) {
+ self.burnDuration--;
+ if (self.burnDuration % 10 === 0) {
+ self.takeDamage(self.burnDamage || 3);
+ }
+ }
+ // Handle acid effect
+ if (self.acidDuration > 0) {
+ self.acidDuration--;
+ if (self.acidDuration % 15 === 0) {
+ self.takeDamage(self.acidDamage || 2);
+ }
+ }
+ if (self.frozen > 0) {
+ self.frozen--;
+ bossGraphics.tint = 0x87CEEB;
+ self.speed = 0.2;
+ } else if (self.burnDuration > 0) {
+ bossGraphics.tint = 0xFF4500;
+ self.speed = 0.4;
+ } else if (self.acidDuration > 0) {
+ bossGraphics.tint = 0x32CD32;
+ self.speed = 0.4;
+ } else {
+ bossGraphics.tint = 0x2f2f2f;
+ self.speed = 0.4;
+ }
+ // Teleport ability
+ self.teleportTimer++;
+ if (self.teleportTimer >= 240) {
+ // Every 4 seconds
+ self.teleportTimer = 0;
+ self.teleport();
+ }
+ // Clone ability when health is low
+ if (self.health < 175 && self.canClone) {
+ self.canClone = false;
+ self.createClone();
+ }
+ if (!self.target || self.target.destroyed) {
+ self.findTarget();
+ }
+ if (self.target) {
+ var dx = self.target.x - self.x;
+ var dy = self.target.y - self.y;
+ var dist = Math.sqrt(dx * dx + dy * dy);
+ if (dist > 60) {
+ self.x += dx / dist * self.speed;
+ self.y += dy / dist * self.speed;
+ } else {
+ self.attackTimer++;
+ if (self.attackTimer >= 30) {
+ // Faster attacks
+ self.attackTimer = 0;
+ if (self.target.takeDamage) {
+ if (self.target.takeDamage(self.damage)) {
+ self.target = null;
+ }
+ }
+ }
+ }
+ } else {
+ self.y += self.speed;
+ if (self.y > 2732) {
+ lives -= 3;
+ self.destroy();
+ }
+ }
+ };
+ self.teleport = function () {
+ // Teleport to random location
+ self.x = Math.random() * 2048;
+ self.y = Math.random() * 1000 + 200;
+ LK.effects.flashObject(self, 0x8A2BE2, 300);
+ tween(self, {
+ alpha: 0.5
+ }, {
+ duration: 100,
+ onFinish: function onFinish() {
+ tween(self, {
+ alpha: 1
+ }, {
+ duration: 100
+ });
+ }
+ });
+ };
+ self.createClone = function () {
+ var clone = new NinjaZombie();
+ clone.x = self.x + (Math.random() - 0.5) * 200;
+ clone.y = self.y + (Math.random() - 0.5) * 200;
+ clone.health = 100; // Clone has less health
+ clone.canClone = false; // Clones can't clone
+ zombies.push(clone);
+ game.addChild(clone);
+ LK.effects.flashObject(self, 0x800080, 500);
+ };
+ self.findTarget = function () {
+ var minDist = Infinity;
+ self.target = null;
+ for (var i = 0; i < houses.length; i++) {
+ var houseLeft = houses[i].x - 1024;
+ var houseRight = houses[i].x + 1024;
+ var targetX = Math.max(houseLeft, Math.min(houseRight, self.x));
+ var dx = targetX - self.x;
+ var dy = houses[i].y - self.y;
+ var dist = Math.sqrt(dx * dx + dy * dy);
+ if (dist < minDist) {
+ minDist = dist;
+ self.target = {
+ x: targetX,
+ y: houses[i].y,
+ takeDamage: houses[i].takeDamage.bind(houses[i]),
+ destroyed: houses[i].destroyed
+ };
+ }
+ }
+ };
+ self.takeDamage = function (damage) {
+ self.health -= damage;
+ if (self.health <= 0) {
+ LK.setScore(LK.getScore() + 150);
+ LK.getSound('zombieDeath').play();
+ self.destroy();
+ return true;
+ }
+ LK.effects.flashObject(self, 0xFFFFFF, 200);
+ return false;
+ };
+ self.freeze = function () {
+ self.frozen = 120;
+ };
+ return self;
+});
var Rocket = Container.expand(function () {
var self = Container.call(this);
var rocketGraphics = self.attachAsset('rocket', {
anchorX: 0.5,
@@ -1286,8 +1593,148 @@
self.frozen = 180;
};
return self;
});
+var ShamanZombie = Container.expand(function () {
+ var self = Container.call(this);
+ var bossGraphics = self.attachAsset('shamanZombie', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.speed = 0.25;
+ self.health = 400;
+ self.damage = 20;
+ self.target = null;
+ self.attackTimer = 0;
+ self.frozen = 0;
+ self.summonTimer = 0;
+ self.healTimer = 0;
+ self.lowHealthHealed = false;
+ self.update = function () {
+ // Handle burning effect
+ if (self.burnDuration > 0) {
+ self.burnDuration--;
+ if (self.burnDuration % 10 === 0) {
+ self.takeDamage(self.burnDamage || 3);
+ }
+ }
+ // Handle acid effect
+ if (self.acidDuration > 0) {
+ self.acidDuration--;
+ if (self.acidDuration % 15 === 0) {
+ self.takeDamage(self.acidDamage || 2);
+ }
+ }
+ if (self.frozen > 0) {
+ self.frozen--;
+ bossGraphics.tint = 0x87CEEB;
+ self.speed = 0.125;
+ } else if (self.burnDuration > 0) {
+ bossGraphics.tint = 0xFF4500;
+ self.speed = 0.25;
+ } else if (self.acidDuration > 0) {
+ bossGraphics.tint = 0x32CD32;
+ self.speed = 0.25;
+ } else {
+ bossGraphics.tint = 0x800080;
+ self.speed = 0.25;
+ }
+ // Healing when low on health
+ if (self.health < 100 && !self.lowHealthHealed) {
+ self.healTimer++;
+ if (self.healTimer >= 120) {
+ // 2 seconds
+ self.health += 150;
+ self.lowHealthHealed = true;
+ LK.effects.flashObject(self, 0x00FF00, 500);
+ }
+ }
+ // Summon fake zombies periodically
+ self.summonTimer++;
+ if (self.summonTimer >= 300) {
+ // Every 5 seconds
+ self.summonTimer = 0;
+ self.summonFakeZombies();
+ }
+ if (!self.target || self.target.destroyed) {
+ self.findTarget();
+ }
+ if (self.target) {
+ var dx = self.target.x - self.x;
+ var dy = self.target.y - self.y;
+ var dist = Math.sqrt(dx * dx + dy * dy);
+ if (dist > 60) {
+ self.x += dx / dist * self.speed;
+ self.y += dy / dist * self.speed;
+ } else {
+ self.attackTimer++;
+ if (self.attackTimer >= 45) {
+ self.attackTimer = 0;
+ if (self.target.takeDamage) {
+ if (self.target.takeDamage(self.damage)) {
+ self.target = null;
+ }
+ }
+ }
+ }
+ } else {
+ self.y += self.speed;
+ if (self.y > 2732) {
+ lives -= 3;
+ self.destroy();
+ }
+ }
+ };
+ self.summonFakeZombies = function () {
+ for (var i = 0; i < 2; i++) {
+ var fakeZombie = new FakeZombie();
+ fakeZombie.x = self.x + (Math.random() - 0.5) * 100;
+ fakeZombie.y = self.y + (Math.random() - 0.5) * 100;
+ zombies.push(fakeZombie);
+ game.addChild(fakeZombie);
+ }
+ LK.effects.flashObject(self, 0x800080, 300);
+ };
+ self.findTarget = function () {
+ var minDist = Infinity;
+ self.target = null;
+ // Prioritize houses first
+ for (var i = 0; i < houses.length; i++) {
+ // Create a virtual target point spread across the house width
+ var houseLeft = houses[i].x - 1024; // Left edge of house
+ var houseRight = houses[i].x + 1024; // Right edge of house
+ var targetX = Math.max(houseLeft, Math.min(houseRight, self.x)); // Clamp zombie x to house width
+ var dx = targetX - self.x;
+ var dy = houses[i].y - self.y;
+ var dist = Math.sqrt(dx * dx + dy * dy);
+ if (dist < minDist) {
+ minDist = dist;
+ self.target = {
+ x: targetX,
+ y: houses[i].y,
+ takeDamage: houses[i].takeDamage.bind(houses[i]),
+ destroyed: houses[i].destroyed
+ };
+ }
+ }
+ // Skip solar collectors and towers - they are not targeted by boss zombies
+ };
+ self.takeDamage = function (damage) {
+ self.health -= damage;
+ if (self.health <= 0) {
+ LK.setScore(LK.getScore() + 150);
+ LK.getSound('zombieDeath').play();
+ self.destroy();
+ return true;
+ }
+ LK.effects.flashObject(self, 0xFFFFFF, 200);
+ return false;
+ };
+ self.freeze = function () {
+ self.frozen = 120;
+ };
+ return self;
+});
var SolarCollector = Container.expand(function () {
var self = Container.call(this);
var collector = self.attachAsset('solarCollector', {
anchorX: 0.5,
@@ -1338,8 +1785,229 @@
return false;
};
return self;
});
+var SoldierZombie = Container.expand(function () {
+ var self = Container.call(this);
+ var bossGraphics = self.attachAsset('soldierZombie', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.speed = 0.35;
+ self.health = 300;
+ self.damage = 22;
+ self.target = null;
+ self.attackTimer = 0;
+ self.frozen = 0;
+ self.groupSpawned = false;
+ self.update = function () {
+ // Handle burning effect
+ if (self.burnDuration > 0) {
+ self.burnDuration--;
+ if (self.burnDuration % 10 === 0) {
+ self.takeDamage(self.burnDamage || 3);
+ }
+ }
+ // Handle acid effect
+ if (self.acidDuration > 0) {
+ self.acidDuration--;
+ if (self.acidDuration % 15 === 0) {
+ self.takeDamage(self.acidDamage || 2);
+ }
+ }
+ if (self.frozen > 0) {
+ self.frozen--;
+ bossGraphics.tint = 0x87CEEB;
+ self.speed = 0.175;
+ } else if (self.burnDuration > 0) {
+ bossGraphics.tint = 0xFF4500;
+ self.speed = 0.35;
+ } else if (self.acidDuration > 0) {
+ bossGraphics.tint = 0x32CD32;
+ self.speed = 0.35;
+ } else {
+ bossGraphics.tint = 0x556b2f;
+ self.speed = 0.35;
+ }
+ // Spawn group when health gets low
+ if (self.health < 150 && !self.groupSpawned) {
+ self.groupSpawned = true;
+ self.spawnGroup();
+ }
+ if (!self.target || self.target.destroyed) {
+ self.findTarget();
+ }
+ if (self.target) {
+ var dx = self.target.x - self.x;
+ var dy = self.target.y - self.y;
+ var dist = Math.sqrt(dx * dx + dy * dy);
+ if (dist > 60) {
+ self.x += dx / dist * self.speed;
+ self.y += dy / dist * self.speed;
+ } else {
+ self.attackTimer++;
+ if (self.attackTimer >= 45) {
+ self.attackTimer = 0;
+ if (self.target.takeDamage) {
+ if (self.target.takeDamage(self.damage)) {
+ self.target = null;
+ }
+ }
+ }
+ }
+ } else {
+ self.y += self.speed;
+ if (self.y > 2732) {
+ lives -= 3;
+ self.destroy();
+ }
+ }
+ };
+ self.spawnGroup = function () {
+ // Spawn 4 regular zombies
+ for (var i = 0; i < 4; i++) {
+ var soldier = new Zombie();
+ soldier.x = self.x + (Math.random() - 0.5) * 150;
+ soldier.y = self.y + (Math.random() - 0.5) * 150;
+ soldier.health = 40; // Slightly stronger
+ soldier.damage = 12;
+ zombies.push(soldier);
+ game.addChild(soldier);
+ }
+ // Spawn 2 tank zombies
+ for (var i = 0; i < 2; i++) {
+ var tank = new TankZombie();
+ tank.x = self.x + (Math.random() - 0.5) * 200;
+ tank.y = self.y + (Math.random() - 0.5) * 200;
+ zombies.push(tank);
+ game.addChild(tank);
+ }
+ LK.effects.flashObject(self, 0x556b2f, 500);
+ };
+ self.findTarget = function () {
+ var minDist = Infinity;
+ self.target = null;
+ for (var i = 0; i < houses.length; i++) {
+ var houseLeft = houses[i].x - 1024;
+ var houseRight = houses[i].x + 1024;
+ var targetX = Math.max(houseLeft, Math.min(houseRight, self.x));
+ var dx = targetX - self.x;
+ var dy = houses[i].y - self.y;
+ var dist = Math.sqrt(dx * dx + dy * dy);
+ if (dist < minDist) {
+ minDist = dist;
+ self.target = {
+ x: targetX,
+ y: houses[i].y,
+ takeDamage: houses[i].takeDamage.bind(houses[i]),
+ destroyed: houses[i].destroyed
+ };
+ }
+ }
+ };
+ self.takeDamage = function (damage) {
+ self.health -= damage;
+ if (self.health <= 0) {
+ LK.setScore(LK.getScore() + 150);
+ LK.getSound('zombieDeath').play();
+ self.destroy();
+ return true;
+ }
+ LK.effects.flashObject(self, 0xFFFFFF, 200);
+ return false;
+ };
+ self.freeze = function () {
+ self.frozen = 120;
+ };
+ return self;
+});
+var TankZombie = Container.expand(function () {
+ var self = Container.call(this);
+ var zombieGraphics = self.attachAsset('tankZombie', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.speed = 0.2;
+ self.health = 100;
+ self.damage = 18;
+ self.target = null;
+ self.attackTimer = 0;
+ self.frozen = 0;
+ self.update = function () {
+ if (self.frozen > 0) {
+ self.frozen--;
+ zombieGraphics.tint = 0x87CEEB;
+ self.speed = 0.1;
+ } else {
+ zombieGraphics.tint = 0x696969;
+ self.speed = 0.2;
+ }
+ if (!self.target || self.target.destroyed) {
+ self.findTarget();
+ }
+ if (self.target) {
+ var dx = self.target.x - self.x;
+ var dy = self.target.y - self.y;
+ var dist = Math.sqrt(dx * dx + dy * dy);
+ if (dist > 50) {
+ self.x += dx / dist * self.speed;
+ self.y += dy / dist * self.speed;
+ } else {
+ self.attackTimer++;
+ if (self.attackTimer >= 60) {
+ self.attackTimer = 0;
+ if (self.target.takeDamage) {
+ if (self.target.takeDamage(self.damage)) {
+ self.target = null;
+ }
+ }
+ }
+ }
+ } else {
+ self.y += self.speed;
+ if (self.y > 2732) {
+ lives--;
+ self.destroy();
+ }
+ }
+ };
+ self.findTarget = function () {
+ var minDist = Infinity;
+ self.target = null;
+ for (var i = 0; i < houses.length; i++) {
+ var houseLeft = houses[i].x - 1024;
+ var houseRight = houses[i].x + 1024;
+ var targetX = Math.max(houseLeft, Math.min(houseRight, self.x));
+ var dx = targetX - self.x;
+ var dy = houses[i].y - self.y;
+ var dist = Math.sqrt(dx * dx + dy * dy);
+ if (dist < minDist) {
+ minDist = dist;
+ self.target = {
+ x: targetX,
+ y: houses[i].y,
+ takeDamage: houses[i].takeDamage.bind(houses[i]),
+ destroyed: houses[i].destroyed
+ };
+ }
+ }
+ };
+ self.takeDamage = function (damage) {
+ self.health -= damage;
+ if (self.health <= 0) {
+ LK.setScore(LK.getScore() + 20);
+ LK.getSound('zombieDeath').play();
+ self.destroy();
+ return true;
+ }
+ LK.effects.flashObject(self, 0xFFFFFF, 200);
+ return false;
+ };
+ self.freeze = function () {
+ self.frozen = 120;
+ };
+ return self;
+});
var ToughZombie = Container.expand(function () {
var self = Container.call(this);
var zombieGraphics = self.attachAsset('toughZombie', {
anchorX: 0.5,
@@ -2499,9 +3167,19 @@
};
function spawnZombie(isBoss) {
var zombie;
if (isBoss) {
- zombie = new BossZombie();
+ // Randomly select boss type
+ var bossRoll = Math.random();
+ if (bossRoll < 0.25) {
+ zombie = new ShamanZombie();
+ } else if (bossRoll < 0.5) {
+ zombie = new NinjaZombie();
+ } else if (bossRoll < 0.75) {
+ zombie = new CrazyZombie();
+ } else {
+ zombie = new SoldierZombie();
+ }
zombie.health *= difficultyMultiplier;
zombie.damage = Math.floor(zombie.damage * difficultyMultiplier);
bossActive = true;
} else {
@@ -2662,10 +3340,10 @@
}
for (var i = zombies.length - 1; i >= 0; i--) {
if (zombies[i].destroyed) {
// Check if destroyed zombie was a boss
- if (zombies[i].health > 100 && bossActive) {
- // Boss zombies have much higher health
+ if ((zombies[i] instanceof ShamanZombie || zombies[i] instanceof NinjaZombie || zombies[i] instanceof CrazyZombie || zombies[i] instanceof SoldierZombie) && bossActive) {
+ // Boss zombies detected by type
onBossDefeated();
} else if (!bossActive) {
// Regular zombie kill increases boss progress slightly
bossProgress += 0.1; // Reduced to match slower progress bar
top view of a garden. 2d. No shadows. In-game assetmodern, simple
top view of a grassy area. In-Game asset. 2d. High contrast. No shadows
Top view of a house. In-Game asset. 2d. High contrast. No shadows
Acid turret. In-Game asset. 2d. High contrast. No shadows
A zombie. In-Game asset. 2d. High contrast. No shadows
solar collector. In-Game asset. 2d. High contrast. No shadows
A turret. In-Game asset. 2d. High contrast. No shadows
A duo barrel turret. In-Game asset. 2d. High contrast. No shadows
Triple barrel turret. In-Game asset. 2d. High contrast. No shadows
Tough zombie. In-Game asset. 2d. High contrast. No shadows
Light-skin runner zombie. In-Game asset. 2d. High contrast. No shadows
a zombie raises an umbrella forward. In-Game asset. 2d. High contrast. No shadows
Zombie dressed as a crow. In-Game asset. 2d. High contrast. No shadows
Zombie crow. In-Game asset. 2d. High contrast. No shadows
Ice turret. In-Game asset. 2d. High contrast. No shadows
Flame thrower turret. In-Game asset. 2d. High contrast. No shadows
Homing bullet turret. In-Game asset. 2d. High contrast. No shadows
Minigun turret. In-Game asset. 2d. High contrast. No shadows
Rocket launcher turret. In-Game asset. 2d. High contrast. No shadows
Missile. In-Game asset. 2d. High contrast. No shadows
Old abandoned, broken tank. In-Game asset. 2d. High contrast. No shadows
Ninja zombie. In-Game asset. 2d. High contrast. No shadows
Grey zombie. In-Game asset. 2d. High contrast. No shadows
Soldier zombie. In-Game asset. 2d. High contrast. No shadows
Very crazy zombie. In-Game asset. 2d. High contrast. No shadows
A shaman zombie,head wearing a cow skull. In-Game asset. 2d. High contrast. No shadows
Spike turret. In-Game asset. 2d. High contrast. No shadows
Ball turret. In-Game asset. 2d. High contrast. No shadows
Electrical turret. In-Game asset. 2d. High contrast. No shadows
Sci-fi wall. In-Game asset. 2d. High contrast. No shadows
A shield with radar. In-Game asset. 2d. High contrast. No shadows
Football zombie. In-Game asset. 2d. High contrast. No shadows
a zombie covered in mud. In-Game asset. 2d. High contrast. No shadows
A cardboard. In-Game asset. 2d. High contrast. No shadows
A wooden crate. In-Game asset. 2d. High contrast. No shadows