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 Attractor = Container.expand(function () {
var self = Container.call(this);
var attractorGraphics = self.attachAsset('attractor', {
anchorX: 0.5,
anchorY: 0.5
});
self.attractionRange = 300;
self.health = 80;
self.maxHealth = 80;
self.attractionTimer = 0;
// Pulse animation for attractor
self.update = function () {
self.attractionTimer++;
// Create pulsing effect
var pulseScale = 1 + Math.sin(self.attractionTimer * 0.15) * 0.3;
attractorGraphics.scaleX = pulseScale;
attractorGraphics.scaleY = pulseScale;
// Attract zombies in range
if (self.attractionTimer % 30 === 0) {
self.attractZombies();
}
};
self.attractZombies = function () {
for (var i = 0; i < zombies.length; i++) {
var zombie = zombies[i];
var dx = self.x - zombie.x;
var dy = self.y - zombie.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < self.attractionRange && dist > 30) {
// Override zombie's current target to move toward attractor
var attractForce = 0.3;
zombie.x += dx / dist * attractForce;
zombie.y += dy / dist * attractForce;
// Visual effect for attraction
if (Math.random() < 0.1) {
var sparkle = game.addChild(LK.getAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
}));
sparkle.x = zombie.x + (Math.random() - 0.5) * 20;
sparkle.y = zombie.y + (Math.random() - 0.5) * 20;
sparkle.tint = 0xff69b4;
sparkle.width = 4;
sparkle.height = 4;
tween(sparkle, {
x: self.x,
y: self.y,
alpha: 0
}, {
duration: 500,
onFinish: function onFinish() {
sparkle.destroy();
}
});
}
}
}
};
self.takeDamage = function (damage) {
self.health -= damage;
if (self.health <= 0) {
// Find and mark grid cell as unoccupied when attractor is destroyed
var gridX = Math.floor((self.x - GRID_SIZE / 2) / GRID_SIZE);
var gridY = Math.floor((self.y - GRID_SIZE / 2 - 200) / GRID_SIZE);
if (gridX >= 0 && gridX < GRID_COLS && gridY >= 0 && gridY < GRID_ROWS) {
var cellIndex = gridY * GRID_COLS + gridX;
if (cellIndex < gridCells.length) {
gridCells[cellIndex].occupied = false;
}
}
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++) {
// Skip destroyed or dead zombies
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 <= 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 BallTurret = Container.expand(function () {
var self = Container.call(this);
var tower = self.attachAsset('ballTurret', {
anchorX: 0.5,
anchorY: 0.5
});
self.damage = 40;
self.range = 250;
self.fireRate = 75;
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++) {
// Skip destroyed or dead zombies
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 <= self.range) {
if (dist < minDist) {
minDist = dist;
target = zombies[i];
}
}
}
if (target) {
self.shootEnergyBall(target);
}
};
self.shootEnergyBall = function (target) {
LK.getSound('laser').play();
var ball = new EnergyBall();
ball.x = self.x;
ball.y = self.y;
ball.targetX = target.x;
ball.targetY = target.y;
ball.damage = self.damage;
ball.bounces = 3;
game.addChild(ball);
};
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 BoxZombie = Container.expand(function () {
var self = Container.call(this);
var zombieGraphics = self.attachAsset('boxZombie', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 0.3;
self.health = 80;
self.damage = 15;
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--;
zombieGraphics.tint = 0x87CEEB;
self.speed = 0.15;
} else if (self.burnDuration > 0) {
zombieGraphics.tint = 0xFF4500;
self.speed = 0.3;
} else if (self.acidDuration > 0) {
zombieGraphics.tint = 0x32CD32;
self.speed = 0.3;
} else {
zombieGraphics.tint = 0x8b4513;
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 > 50) {
// Check for wall collisions before moving
var nextX = self.x + dx / dist * self.speed;
var nextY = self.y + dy / dist * self.speed;
var blocked = false;
// Check collision with walls
for (var w = 0; w < walls.length; w++) {
var wallDx = nextX - walls[w].x;
var wallDy = nextY - walls[w].y;
var wallDist = Math.sqrt(wallDx * wallDx + wallDy * wallDy);
if (wallDist < 80) {
self.target = walls[w];
blocked = true;
break;
}
}
if (!blocked) {
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();
// Spawn three random zombies (excluding CrowZombie)
for (var i = 0; i < 3; i++) {
self.spawnRandomZombie();
}
self.destroy();
return true;
}
LK.effects.flashObject(self, 0xFFFFFF, 200);
return false;
};
self.spawnRandomZombie = function () {
var zombieTypes = [function () {
return new Zombie();
}, function () {
return new ToughZombie();
}, function () {
return new RunnerZombie();
}, function () {
return new UmbrellaZombie();
}, function () {
return new CrowsZombie();
}, function () {
return new TankZombie();
}];
var randomIndex = Math.floor(Math.random() * zombieTypes.length);
var newZombie = zombieTypes[randomIndex]();
newZombie.x = self.x + (Math.random() - 0.5) * 80;
newZombie.y = self.y + (Math.random() - 0.5) * 80;
zombies.push(newZombie);
game.addChild(newZombie);
};
self.freeze = function () {
self.frozen = 180;
};
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 BuriedZombie = Container.expand(function () {
var self = Container.call(this);
var zombieGraphics = self.attachAsset('buriedZombie', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 0.8;
self.health = 40;
self.damage = 12;
self.target = null;
self.attackTimer = 0;
self.frozen = 0;
self.emergingTimer = 0;
self.isEmerging = true;
// Start buried (underground)
self.alpha = 0.3;
self.emergingTimer = 120; // 2 seconds to emerge
self.update = function () {
// Handle emerging from ground
if (self.isEmerging) {
self.emergingTimer--;
if (self.emergingTimer <= 0) {
self.isEmerging = false;
self.alpha = 1;
// Flash effect when emerging
LK.effects.flashObject(self, 0x8b7355, 300);
}
return;
}
// 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--;
zombieGraphics.tint = 0x87CEEB;
self.speed = 0.4;
} else if (self.burnDuration > 0) {
zombieGraphics.tint = 0xFF4500;
self.speed = 0.8;
} else if (self.acidDuration > 0) {
zombieGraphics.tint = 0x32CD32;
self.speed = 0.8;
} else {
zombieGraphics.tint = 0x8b7355;
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 > 50) {
// Check for wall collisions before moving
var nextX = self.x + dx / dist * self.speed;
var nextY = self.y + dy / dist * self.speed;
var blocked = false;
// Check collision with walls
for (var w = 0; w < walls.length; w++) {
var wallDx = nextX - walls[w].x;
var wallDy = nextY - walls[w].y;
var wallDist = Math.sqrt(wallDx * wallDx + wallDy * wallDy);
if (wallDist < 80) {
self.target = walls[w];
blocked = true;
break;
}
}
if (!blocked) {
self.x += dx / dist * self.speed;
self.y += dy / dist * self.speed;
}
} else {
self.attackTimer++;
if (self.attackTimer >= 50) {
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) {
// Take reduced damage while emerging
if (self.isEmerging) {
damage = Math.floor(damage * 0.5);
}
self.health -= damage;
if (self.health <= 0) {
LK.setScore(LK.getScore() + 14);
LK.getSound('zombieDeath').play();
self.destroy();
return true;
}
LK.effects.flashObject(self, 0xFFFFFF, 200);
return false;
};
self.freeze = function () {
self.frozen = 150;
};
return self;
});
var CardboardZombie = Container.expand(function () {
var self = Container.call(this);
var zombieGraphics = self.attachAsset('cardboardZombie', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 0.5;
self.health = 35;
self.damage = 8;
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--;
zombieGraphics.tint = 0x87CEEB;
self.speed = 0.25;
} else if (self.burnDuration > 0) {
zombieGraphics.tint = 0xFF4500;
self.speed = 0.5;
} else if (self.acidDuration > 0) {
zombieGraphics.tint = 0x32CD32;
self.speed = 0.5;
} else {
zombieGraphics.tint = 0xd2b48c;
self.speed = 0.5;
}
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) {
// Check for wall collisions before moving
var nextX = self.x + dx / dist * self.speed;
var nextY = self.y + dy / dist * self.speed;
var blocked = false;
// Check collision with walls
for (var w = 0; w < walls.length; w++) {
var wallDx = nextX - walls[w].x;
var wallDy = nextY - walls[w].y;
var wallDist = Math.sqrt(wallDx * wallDx + wallDy * wallDy);
if (wallDist < 80) {
self.target = walls[w];
blocked = true;
break;
}
}
if (!blocked) {
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() + 12);
LK.getSound('zombieDeath').play();
// Spawn a random zombie (excluding CrowZombie)
self.spawnRandomZombie();
self.destroy();
return true;
}
LK.effects.flashObject(self, 0xFFFFFF, 200);
return false;
};
self.spawnRandomZombie = function () {
var zombieTypes = [function () {
return new Zombie();
}, function () {
return new ToughZombie();
}, function () {
return new RunnerZombie();
}, function () {
return new UmbrellaZombie();
}, function () {
return new CrowsZombie();
}, function () {
return new TankZombie();
}];
var randomIndex = Math.floor(Math.random() * zombieTypes.length);
var newZombie = zombieTypes[randomIndex]();
newZombie.x = self.x + (Math.random() - 0.5) * 40;
newZombie.y = self.y + (Math.random() - 0.5) * 40;
zombies.push(newZombie);
game.addChild(newZombie);
};
self.freeze = function () {
self.frozen = 180;
};
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++) {
// Skip destroyed or dead zombies
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 <= 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++) {
// Skip destroyed or dead zombies
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 <= 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 ElectricalTurret = Container.expand(function () {
var self = Container.call(this);
var tower = self.attachAsset('electricalTurret', {
anchorX: 0.5,
anchorY: 0.5
});
self.damage = 35;
self.range = 200;
self.fireRate = 60;
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++) {
// Skip destroyed or dead zombies
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 <= self.range) {
if (dist < minDist) {
minDist = dist;
target = zombies[i];
}
}
}
if (target) {
self.shootElectricalBolt(target);
}
};
self.shootElectricalBolt = function (target) {
LK.getSound('laser').play();
// Create electrical bolt effect
var bolt = self.attachAsset('electricalBolt', {
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;
bolt.rotation = angle;
var dist = Math.sqrt(dx * dx + dy * dy);
bolt.height = dist;
bolt.alpha = 1;
tween(bolt, {
alpha: 0
}, {
duration: 300,
onFinish: function onFinish() {
bolt.destroy();
}
});
// Damage primary target
target.takeDamage(self.damage);
// Chain to nearby zombies
self.chainLightning(target, 2, self.damage * 0.7);
};
self.chainLightning = function (sourceZombie, remainingChains, chainDamage) {
if (remainingChains <= 0) return;
var chainRange = 120;
var nextTarget = null;
var minDist = chainRange;
for (var i = 0; i < zombies.length; i++) {
if (zombies[i] === sourceZombie) continue;
var dx = zombies[i].x - sourceZombie.x;
var dy = zombies[i].y - sourceZombie.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist <= chainRange && dist < minDist) {
minDist = dist;
nextTarget = zombies[i];
}
}
if (nextTarget) {
// Create chain bolt visual
var chainBolt = game.addChild(LK.getAsset('electricalBolt', {
anchorX: 0.5,
anchorY: 1
}));
chainBolt.x = sourceZombie.x;
chainBolt.y = sourceZombie.y;
var dx = nextTarget.x - sourceZombie.x;
var dy = nextTarget.y - sourceZombie.y;
var angle = Math.atan2(dy, dx) + Math.PI / 2;
chainBolt.rotation = angle;
var dist = Math.sqrt(dx * dx + dy * dy);
chainBolt.height = dist;
chainBolt.width = 4;
chainBolt.tint = 0x00ffff;
tween(chainBolt, {
alpha: 0
}, {
duration: 200,
onFinish: function onFinish() {
chainBolt.destroy();
}
});
// Damage chain target
nextTarget.takeDamage(Math.floor(chainDamage));
// Continue chain
self.chainLightning(nextTarget, remainingChains - 1, chainDamage * 0.8);
}
};
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 EnergyBall = Container.expand(function () {
var self = Container.call(this);
var ballGraphics = self.attachAsset('energyBall', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 7;
self.damage = 40;
self.targetX = 0;
self.targetY = 0;
self.bounces = 3;
self.hasHitTarget = false;
self.update = function () {
if (!self.hasHitTarget) {
// Move toward initial target
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 10) {
self.x += dx / dist * self.speed;
self.y += dy / dist * self.speed;
// Rotate ball for visual effect
ballGraphics.rotation += 0.2;
} else {
self.hasHitTarget = true;
self.findNextTarget();
}
} else {
// Ball is bouncing between targets
if (self.targetX !== 0 && self.targetY !== 0) {
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 15) {
self.x += dx / dist * self.speed;
self.y += dy / dist * self.speed;
ballGraphics.rotation += 0.2;
} else {
self.findNextTarget();
}
} else {
// No more targets, destroy ball
self.destroy();
}
}
// 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 < 25) {
zombies[i].takeDamage(self.damage);
self.findNextTarget();
break;
}
}
};
self.findNextTarget = function () {
if (self.bounces <= 0) {
self.destroy();
return;
}
self.bounces--;
var bounceRange = 150;
var nextTarget = null;
var minDist = bounceRange;
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 <= bounceRange && dist < minDist) {
minDist = dist;
nextTarget = zombies[i];
}
}
if (nextTarget) {
self.targetX = nextTarget.x;
self.targetY = nextTarget.y;
// Flash effect when bouncing
tween(ballGraphics, {
scaleX: 1.5,
scaleY: 1.5,
tint: 0xffffff
}, {
duration: 100,
onFinish: function onFinish() {
tween(ballGraphics, {
scaleX: 1,
scaleY: 1,
tint: 0x9370db
}, {
duration: 100
});
}
});
} else {
self.targetX = 0;
self.targetY = 0;
}
};
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++) {
// Skip destroyed or dead zombies
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 <= 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 FootballZombie = Container.expand(function () {
var self = Container.call(this);
var zombieGraphics = self.attachAsset('footballZombie', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 0.6;
self.health = 150;
self.damage = 20;
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--;
zombieGraphics.tint = 0x87CEEB;
self.speed = 0.3;
} else if (self.burnDuration > 0) {
zombieGraphics.tint = 0xFF4500;
self.speed = 0.6;
} else if (self.acidDuration > 0) {
zombieGraphics.tint = 0x32CD32;
self.speed = 0.6;
} else {
zombieGraphics.tint = 0x2e8b57;
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) {
// Check for wall collisions before moving
var nextX = self.x + dx / dist * self.speed;
var nextY = self.y + dy / dist * self.speed;
var blocked = false;
// Check collision with walls
for (var w = 0; w < walls.length; w++) {
var wallDx = nextX - walls[w].x;
var wallDy = nextY - walls[w].y;
var wallDist = Math.sqrt(wallDx * wallDx + wallDy * wallDy);
if (wallDist < 80) {
self.target = walls[w];
blocked = true;
break;
}
}
if (!blocked) {
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 -= 2;
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() + 25);
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 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++) {
// Skip destroyed or dead zombies
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++) {
// Skip destroyed or dead zombies
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 <= 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++) {
// Skip destroyed or dead zombies
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 <= 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++) {
// Skip destroyed or dead zombies
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 <= 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.rotationTimer = 0;
self.rotationInterval = Math.floor(Math.random() * 600) + 300; // Random 5-15 seconds for rotation
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 = 15;
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();
}
});
}
// Handle random rotation
self.rotationTimer++;
if (self.rotationTimer >= self.rotationInterval) {
self.rotationTimer = 0;
// Set new random interval for next rotation
self.rotationInterval = Math.floor(Math.random() * 600) + 300; // Random 5-15 seconds
// Rotate to a random angle with smooth animation
var targetRotation = Math.random() * Math.PI * 2; // Random angle between 0 and 2π
tween(collector, {
rotation: targetRotation
}, {
duration: 800,
easing: tween.easeInOut
});
}
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 SpikeTurret = Container.expand(function () {
var self = Container.call(this);
var tower = self.attachAsset('spikeTurret', {
anchorX: 0.5,
anchorY: 0.5
});
self.damage = 50;
self.range = 180;
self.fireRate = 90;
self.fireTimer = 0;
self.health = 100;
self.maxHealth = 100;
self.update = function () {
self.fireTimer++;
if (self.fireTimer >= self.fireRate) {
self.fireTimer = 0;
self.findAndSpike();
}
};
self.findAndSpike = function () {
var target = null;
var minDist = self.range;
for (var i = 0; i < zombies.length; i++) {
// Skip destroyed or dead zombies
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 <= self.range) {
if (dist < minDist) {
minDist = dist;
target = zombies[i];
}
}
}
if (target) {
self.createSpikes(target);
}
};
self.createSpikes = function (target) {
LK.getSound('build').play();
// Create multiple spikes around target area
for (var i = 0; i < 5; i++) {
var spike = game.addChild(LK.getAsset('spike', {
anchorX: 0.5,
anchorY: 1
}));
spike.x = target.x + (Math.random() - 0.5) * 80;
spike.y = target.y + (Math.random() - 0.5) * 40;
spike.scaleY = 0;
spike.alpha = 0.8;
spike.tint = 0x8b4513;
// Animate spikes emerging from ground
tween(spike, {
scaleY: 1
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
// Check for zombie collision after spike emerges
for (var j = 0; j < zombies.length; j++) {
var dx = zombies[j].x - spike.x;
var dy = zombies[j].y - spike.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < 40) {
zombies[j].takeDamage(self.damage);
}
}
// Retract spikes after 1 second
tween(spike, {
scaleY: 0,
alpha: 0
}, {
duration: 300,
onFinish: function onFinish() {
spike.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 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++) {
// Skip destroyed or dead zombies
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 <= 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 Wall = Container.expand(function () {
var self = Container.call(this);
var wallGraphics = self.attachAsset('wall', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 200;
self.maxHealth = 200;
self.takeDamage = function (damage) {
self.health -= damage;
if (self.health <= 0) {
// Find and mark grid cell as unoccupied when wall is destroyed
var gridX = Math.floor((self.x - GRID_SIZE / 2) / GRID_SIZE);
var gridY = Math.floor((self.y - GRID_SIZE / 2 - 200) / GRID_SIZE);
if (gridX >= 0 && gridX < GRID_COLS && gridY >= 0 && gridY < GRID_ROWS) {
var cellIndex = gridY * GRID_COLS + gridX;
if (cellIndex < gridCells.length) {
gridCells[cellIndex].occupied = false;
}
}
self.destroy();
return true;
}
LK.effects.flashObject(self, 0xFF0000, 300);
return false;
};
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) {
// Check for wall collisions before moving
var nextX = self.x + dx / dist * self.speed;
var nextY = self.y + dy / dist * self.speed;
var blocked = false;
// Check collision with walls
for (var w = 0; w < walls.length; w++) {
var wallDx = nextX - walls[w].x;
var wallDy = nextY - walls[w].y;
var wallDist = Math.sqrt(wallDx * wallDx + wallDy * wallDy);
if (wallDist < 80) {
// Zombie is blocked by wall, attack it instead
self.target = walls[w];
blocked = true;
break;
}
}
if (!blocked) {
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: 0x228B22
});
/****
* Game Code
****/
// Add background image
var background = game.addChild(LK.getAsset('background', {
anchorX: 0,
anchorY: 0
}));
background.x = 0;
background.y = 0;
background.width = 2048;
background.height = 2732;
// Send background to back layer
game.setChildIndex(background, 0);
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, 3 = experimental turrets)
var maxPages = 3; // Total number of pages (0, 1, 2, 3)
var solarCollectors = [];
var towers = [];
var zombies = [];
var houses = [];
var walls = [];
var attractors = [];
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);
var electricalButton = new Container();
var electricalButtonBg = electricalButton.attachAsset('electricalTurret', {
anchorX: 0.5,
anchorY: 0.5
});
electricalButtonBg.width = 100;
electricalButtonBg.height = 100;
var electricalButtonText = new Text2('Solar: 110', {
size: 25,
fill: 0xFFD700
});
electricalButtonText.anchor.set(0.5, 0.5);
electricalButtonText.y = -60;
electricalButton.addChild(electricalButtonText);
electricalButton.x = -300;
electricalButton.y = -350;
LK.gui.bottom.addChild(electricalButton);
var spikeButton = new Container();
var spikeButtonBg = spikeButton.attachAsset('spikeTurret', {
anchorX: 0.5,
anchorY: 0.5
});
spikeButtonBg.width = 100;
spikeButtonBg.height = 100;
var spikeButtonText = new Text2('Solar: 85', {
size: 25,
fill: 0xFFD700
});
spikeButtonText.anchor.set(0.5, 0.5);
spikeButtonText.y = -60;
spikeButton.addChild(spikeButtonText);
spikeButton.x = -100;
spikeButton.y = -350;
LK.gui.bottom.addChild(spikeButton);
var ballButton = new Container();
var ballButtonBg = ballButton.attachAsset('ballTurret', {
anchorX: 0.5,
anchorY: 0.5
});
ballButtonBg.width = 100;
ballButtonBg.height = 100;
var ballButtonText = new Text2('Solar: 95', {
size: 25,
fill: 0xFFD700
});
ballButtonText.anchor.set(0.5, 0.5);
ballButtonText.y = -60;
ballButton.addChild(ballButtonText);
ballButton.x = 100;
ballButton.y = -350;
LK.gui.bottom.addChild(ballButton);
var wallButton = new Container();
var wallButtonBg = wallButton.attachAsset('wall', {
anchorX: 0.5,
anchorY: 0.5
});
wallButtonBg.width = 100;
wallButtonBg.height = 100;
var wallButtonText = new Text2('Solar: 30', {
size: 25,
fill: 0xFFD700
});
wallButtonText.anchor.set(0.5, 0.5);
wallButtonText.y = -60;
wallButton.addChild(wallButtonText);
wallButton.x = 300;
wallButton.y = -350;
LK.gui.bottom.addChild(wallButton);
var attractorButton = new Container();
var attractorButtonBg = attractorButton.attachAsset('attractor', {
anchorX: 0.5,
anchorY: 0.5
});
attractorButtonBg.width = 100;
attractorButtonBg.height = 100;
var attractorButtonText = new Text2('Solar: 50', {
size: 25,
fill: 0xFFD700
});
attractorButtonText.anchor.set(0.5, 0.5);
attractorButtonText.y = -60;
attractorButton.addChild(attractorButtonText);
attractorButton.x = -300;
attractorButton.y = -450;
LK.gui.bottom.addChild(attractorButton);
// 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;
};
electricalButton.down = function (x, y, obj) {
if (energy < 110) return;
dragging = true;
selectedTower = 'electrical';
placementCost = 110;
dragTurret = game.addChild(LK.getAsset('electricalTurret', {
anchorX: 0.5,
anchorY: 0.5
}));
dragTurret.width = 60;
dragTurret.height = 60;
dragTurret.alpha = 0.7;
var globalPos = game.toLocal({
x: electricalButton.x,
y: electricalButton.y
});
dragTurret.x = globalPos.x;
dragTurret.y = globalPos.y;
};
spikeButton.down = function (x, y, obj) {
if (energy < 85) return;
dragging = true;
selectedTower = 'spike';
placementCost = 85;
dragTurret = game.addChild(LK.getAsset('spikeTurret', {
anchorX: 0.5,
anchorY: 0.5
}));
dragTurret.width = 60;
dragTurret.height = 60;
dragTurret.alpha = 0.7;
var globalPos = game.toLocal({
x: spikeButton.x,
y: spikeButton.y
});
dragTurret.x = globalPos.x;
dragTurret.y = globalPos.y;
};
ballButton.down = function (x, y, obj) {
if (energy < 95) return;
dragging = true;
selectedTower = 'ball';
placementCost = 95;
dragTurret = game.addChild(LK.getAsset('ballTurret', {
anchorX: 0.5,
anchorY: 0.5
}));
dragTurret.width = 60;
dragTurret.height = 60;
dragTurret.alpha = 0.7;
var globalPos = game.toLocal({
x: ballButton.x,
y: ballButton.y
});
dragTurret.x = globalPos.x;
dragTurret.y = globalPos.y;
};
wallButton.down = function (x, y, obj) {
if (energy < 30) return;
dragging = true;
selectedTower = 'wall';
placementCost = 30;
dragTurret = game.addChild(LK.getAsset('wall', {
anchorX: 0.5,
anchorY: 0.5
}));
dragTurret.width = 120;
dragTurret.height = 120;
dragTurret.alpha = 0.7;
var globalPos = game.toLocal({
x: wallButton.x,
y: wallButton.y
});
dragTurret.x = globalPos.x;
dragTurret.y = globalPos.y;
};
attractorButton.down = function (x, y, obj) {
if (energy < 50) return;
dragging = true;
selectedTower = 'attractor';
placementCost = 50;
dragTurret = game.addChild(LK.getAsset('attractor', {
anchorX: 0.5,
anchorY: 0.5
}));
dragTurret.width = 80;
dragTurret.height = 80;
dragTurret.alpha = 0.7;
var globalPos = game.toLocal({
x: attractorButton.x,
y: attractorButton.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;
electricalButton.visible = false;
spikeButton.visible = false;
ballButton.visible = false;
wallButton.visible = false;
attractorButton.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;
}
// Page 3: Experimental turrets (electrical, spike, ball, wall, attractor)
else if (currentPage === 3) {
electricalButton.visible = true;
spikeButton.visible = true;
ballButton.visible = true;
wallButton.visible = true;
attractorButton.visible = true;
// Reposition experimental turrets to bottom row
electricalButton.x = -300;
electricalButton.y = 0;
spikeButton.x = -100;
spikeButton.y = 0;
ballButton.x = 100;
ballButton.y = 0;
wallButton.x = 300;
wallButton.y = 0;
attractorButton.x = 0;
attractorButton.y = -100;
}
// 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;
if (selectedTower === 'electrical') range = 200;
if (selectedTower === 'spike') range = 180;
if (selectedTower === 'ball') range = 250;
if (selectedTower === 'attractor') 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);
} else if (selectedTower === 'electrical') {
var tower = new ElectricalTurret();
tower.x = newX;
tower.y = newY;
towers.push(tower);
game.addChild(tower);
} else if (selectedTower === 'spike') {
var tower = new SpikeTurret();
tower.x = newX;
tower.y = newY;
towers.push(tower);
game.addChild(tower);
} else if (selectedTower === 'ball') {
var tower = new BallTurret();
tower.x = newX;
tower.y = newY;
towers.push(tower);
game.addChild(tower);
} else if (selectedTower === 'wall') {
var wall = new Wall();
wall.x = newX;
wall.y = newY;
walls.push(wall);
game.addChild(wall);
} else if (selectedTower === 'attractor') {
var attractor = new Attractor();
attractor.x = newX;
attractor.y = newY;
attractors.push(attractor);
game.addChild(attractor);
}
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.35) {
// 35% Basic zombie
zombie = new Zombie();
} else if (zombieRoll < 0.48) {
// 13% Tough zombie
zombie = new ToughZombie();
} else if (zombieRoll < 0.61) {
// 13% Runner zombie
zombie = new RunnerZombie();
} else if (zombieRoll < 0.72) {
// 11% Umbrella zombie
zombie = new UmbrellaZombie();
} else if (zombieRoll < 0.81) {
// 9% Crows zombie
zombie = new CrowsZombie();
} else if (zombieRoll < 0.88) {
// 7% Cardboard zombie
zombie = new CardboardZombie();
} else if (zombieRoll < 0.94) {
// 6% Box zombie
zombie = new BoxZombie();
} else {
// 6% Football zombie
zombie = new FootballZombie();
}
}
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
}
// Spawn buried zombies randomly on the map
if (specialZombiesUnlocked && Math.random() < 0.001) {
// 0.1% chance per frame
var buriedZombie = new BuriedZombie();
// Spawn anywhere on the map but not too close to house
var minDistanceFromHouse = 300;
var validPosition = false;
var attempts = 0;
while (!validPosition && attempts < 10) {
buriedZombie.x = Math.random() * 1800 + 124; // Keep away from edges
buriedZombie.y = Math.random() * 1800 + 400; // Keep away from top and house
// Check distance from house
var distanceFromHouse = Math.sqrt(Math.pow(buriedZombie.x - 1024, 2) + Math.pow(buriedZombie.y - 2500, 2));
if (distanceFromHouse >= minDistanceFromHouse && buriedZombie.y < 2200) {
validPosition = true;
}
attempts++;
}
if (validPosition) {
zombies.push(buriedZombie);
game.addChild(buriedZombie);
}
}
// 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);
}
}
for (var i = walls.length - 1; i >= 0; i--) {
if (walls[i].destroyed) {
walls.splice(i, 1);
}
}
for (var i = attractors.length - 1; i >= 0; i--) {
if (attractors[i].destroyed) {
attractors.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
@@ -166,8 +166,10 @@
self.findAndShoot = function () {
var target = null;
var minDist = self.range;
for (var i = 0; i < zombies.length; i++) {
+ // Skip destroyed or dead zombies
+ 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 <= self.range) {
@@ -230,8 +232,10 @@
self.findAndShoot = function () {
var target = null;
var minDist = self.range;
for (var i = 0; i < zombies.length; i++) {
+ // Skip destroyed or dead zombies
+ 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 <= self.range) {
@@ -763,8 +767,10 @@
self.findAndShoot = function () {
var target = null;
var minDist = self.range;
for (var i = 0; i < zombies.length; i++) {
+ // Skip destroyed or dead zombies
+ 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 <= self.range) {
@@ -1177,8 +1183,10 @@
var targets = [];
// Find all zombies in range and sort by distance
var zombiesInRange = [];
for (var i = 0; i < zombies.length; i++) {
+ // Skip destroyed or dead zombies
+ 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 <= self.range) {
@@ -1253,8 +1261,10 @@
self.findAndShoot = function () {
var target = null;
var minDist = self.range;
for (var i = 0; i < zombies.length; i++) {
+ // Skip destroyed or dead zombies
+ 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 <= self.range) {
@@ -1476,8 +1486,10 @@
self.findAndExplode = function () {
var target = null;
var minDist = self.range;
for (var i = 0; i < zombies.length; i++) {
+ // Skip destroyed or dead zombies
+ 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 <= self.range) {
@@ -1979,8 +1991,9 @@
self.findTarget = function () {
var minDist = 400; // Max homing range
self.target = null;
for (var i = 0; i < zombies.length; i++) {
+ // Skip destroyed or dead zombies
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);
@@ -2037,8 +2050,10 @@
self.findAndShoot = function () {
var target = null;
var minDist = self.range;
for (var i = 0; i < zombies.length; i++) {
+ // Skip destroyed or dead zombies
+ 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 <= self.range) {
@@ -2107,8 +2122,10 @@
self.findAndShoot = function () {
var target = null;
var minDist = self.range;
for (var i = 0; i < zombies.length; i++) {
+ // Skip destroyed or dead zombies
+ 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 <= self.range) {
@@ -2385,8 +2402,10 @@
self.findAndShoot = function () {
var target = null;
var minDist = self.range;
for (var i = 0; i < zombies.length; i++) {
+ // Skip destroyed or dead zombies
+ 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 <= self.range) {
@@ -2882,8 +2901,10 @@
self.findAndSpike = function () {
var target = null;
var minDist = self.range;
for (var i = 0; i < zombies.length; i++) {
+ // Skip destroyed or dead zombies
+ 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 <= self.range) {
@@ -3148,8 +3169,10 @@
var targets = [];
// Find all zombies in range and sort by distance
var zombiesInRange = [];
for (var i = 0; i < zombies.length; i++) {
+ // Skip destroyed or dead zombies
+ 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 <= self.range) {
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