/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Bomb = Container.expand(function () {
var self = Container.call(this);
var bombBase = self.attachAsset('bomb', {
anchorX: 0.5,
anchorY: 0.5
});
var fuse = self.attachAsset('bombFuse', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: -35
});
var spark = self.attachAsset('bombSpark', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: -40
});
self.health = 25;
self.exploded = false;
self.sparkOffset = 0;
self.update = function () {
// Animate the spark on the fuse
self.sparkOffset += 0.15;
spark.alpha = 0.8 + Math.sin(self.sparkOffset) * 0.2;
spark.scaleX = 0.8 + Math.sin(self.sparkOffset * 2) * 0.3;
spark.scaleY = 0.8 + Math.sin(self.sparkOffset * 2) * 0.3;
// Gentle bobbing animation
self.y += Math.sin(LK.ticks * 0.05) * 0.3;
};
self.explode = function () {
if (self.exploded) return;
self.exploded = true;
// Create explosion effect
LK.effects.flashScreen(0xff4500, 300);
// Damage nearby enemies and player
var explosionRadius = 150;
for (var i = 0; i < zombies.length; i++) {
var zombie = zombies[i];
var distance = Math.sqrt(Math.pow(zombie.x - self.x, 2) + Math.pow(zombie.y - self.y, 2));
if (distance < explosionRadius) {
if (zombie.takeDamage(75)) {
LK.getSound('zombieDeath').play();
LK.setScore(LK.getScore() + 10);
zombie.destroy();
zombies.splice(i, 1);
i--;
}
}
}
// Check boss damage
if (boss) {
var bossDistance = Math.sqrt(Math.pow(boss.x - self.x, 2) + Math.pow(boss.y - self.y, 2));
if (bossDistance < explosionRadius) {
boss.takeDamage(100);
}
}
// Check player damage
if (player) {
var playerDistance = Math.sqrt(Math.pow(player.x - self.x, 2) + Math.pow(player.y - self.y, 2));
if (playerDistance < explosionRadius) {
player.takeDamage(30);
}
}
// Remove bomb
self.destroy();
var index = bombs.indexOf(self);
if (index > -1) bombs.splice(index, 1);
};
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xffffff, 100);
if (self.health <= 0) {
self.explode();
return true;
}
return false;
};
return self;
});
var Boss = Container.expand(function (type) {
var self = Container.call(this);
var bossAsset = type === 5 ? 'finalBoss' : 'boss' + type;
var bossGraphics = self.attachAsset(bossAsset, {
anchorX: 0.5,
anchorY: 0.5
});
// Add unique design elements for each boss
if (type === 1) {
// Speed boss with spikes
var eyes1 = self.attachAsset('boss1Eyes', {
anchorX: 0.5,
anchorY: 0.5,
x: -15,
y: -10
});
var eyes2 = self.attachAsset('boss1Eyes', {
anchorX: 0.5,
anchorY: 0.5,
x: 15,
y: -10
});
var spike1 = self.attachAsset('boss1Spikes', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: -35,
rotation: 0
});
var spike2 = self.attachAsset('boss1Spikes', {
anchorX: 0.5,
anchorY: 0.5,
x: -25,
y: 0,
rotation: 1.57
});
var spike3 = self.attachAsset('boss1Spikes', {
anchorX: 0.5,
anchorY: 0.5,
x: 25,
y: 0,
rotation: -1.57
});
} else if (type === 2) {
// Summoner boss with horns and claws
var horn = self.attachAsset('boss2Horn', {
anchorX: 0.5,
anchorY: 1,
x: 0,
y: -40
});
var claw1 = self.attachAsset('boss2Claws', {
anchorX: 0.5,
anchorY: 0.5,
x: -35,
y: 0
});
var claw2 = self.attachAsset('boss2Claws', {
anchorX: 0.5,
anchorY: 0.5,
x: 35,
y: 0
});
} else if (type === 3) {
// Screamer boss with armor and gem
var armor = self.attachAsset('boss3Armor', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 0
});
var gem = self.attachAsset('boss3Gem', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: -20
});
self.gemRef = gem; // Store reference for pulsing animation
} else if (type === 4) {
// Healer boss with wings and crown
var wing1 = self.attachAsset('boss4Wings', {
anchorX: 0.5,
anchorY: 0.5,
x: -50,
y: 0
});
var wing2 = self.attachAsset('boss4Wings', {
anchorX: 0.5,
anchorY: 0.5,
x: 50,
y: 0
});
var crown = self.attachAsset('boss4Crown', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: -50
});
self.wing1Ref = wing1;
self.wing2Ref = wing2;
}
self.type = type;
self.health = 150 + type * 50;
// Make final boss extra tough
if (type === 5) {
self.health = 500; // Much higher health for final boss
}
self.maxHealth = self.health;
self.speed = 0.8 + type * 0.2;
self.damage = 15 + type * 5;
// Increase final boss damage
if (type === 5) {
self.damage = 40; // Much higher damage for final boss
}
self.lastAttack = 0;
self.attackCooldown = 90 - type * 10;
self.lastSpecial = 0;
self.specialCooldown = 300;
self.update = function () {
// Boss-specific animations
if (self.type === 3 && self.gemRef) {
// Pulsing gem animation for screamer boss
self.gemRef.scaleX = 1 + Math.sin(LK.ticks * 0.1) * 0.3;
self.gemRef.scaleY = 1 + Math.sin(LK.ticks * 0.1) * 0.3;
} else if (self.type === 4 && self.wing1Ref && self.wing2Ref) {
// Flapping wings animation for healer boss
var wingFlap = Math.sin(LK.ticks * 0.15) * 0.2;
self.wing1Ref.rotation = wingFlap;
self.wing2Ref.rotation = -wingFlap;
}
if (player) {
var dx = player.x - self.x;
var dy = player.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
if (distance < 80 && LK.ticks - self.lastAttack > self.attackCooldown) {
player.takeDamage(self.damage);
self.lastAttack = LK.ticks;
LK.effects.flashScreen(0xff0000, 200);
}
if (LK.ticks - self.lastSpecial > self.specialCooldown) {
self.specialAttack();
self.lastSpecial = LK.ticks;
}
}
};
self.specialAttack = function () {
if (self.type === 1) {
self.speed *= 2;
tween(self, {
speed: 0.8
}, {
duration: 2000
});
} else if (self.type === 2) {
for (var i = 0; i < 3; i++) {
var minion = new Zombie();
minion.x = self.x + (Math.random() - 0.5) * 200;
minion.y = self.y + (Math.random() - 0.5) * 200;
minion.health = 25;
zombies.push(minion);
game.addChild(minion);
}
} else if (self.type === 3) {
LK.effects.flashScreen(0x800080, 500);
player.takeDamage(20);
} else if (self.type === 4) {
self.health += 25;
self.health = Math.min(self.health, self.maxHealth);
LK.effects.flashObject(self, 0x00ff00, 500);
} else if (self.type === 5) {
for (var i = 0; i < 8; i++) {
var angle = i / 8 * Math.PI * 2;
var shockwave = new Zombie();
shockwave.x = self.x;
shockwave.y = self.y;
shockwave.speed = 3;
shockwave.moveAngle = angle;
shockwave.health = 1;
shockwave.damage = 25;
shockwave.update = function () {
this.x += Math.cos(this.moveAngle) * this.speed;
this.y += Math.sin(this.moveAngle) * this.speed;
if (this.x < 0 || this.x > 2048 || this.y < 0 || this.y > 2732) {
this.destroy();
var index = zombies.indexOf(this);
if (index > -1) zombies.splice(index, 1);
}
};
zombies.push(shockwave);
game.addChild(shockwave);
}
}
};
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xffffff, 150);
return self.health <= 0;
};
return self;
});
var Bullet = Container.expand(function (isSuper) {
var self = Container.call(this);
var bulletAsset = isSuper ? 'superBullet' : 'bullet';
var bulletGraphics = self.attachAsset(bulletAsset, {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.damage = isSuper ? 30 : 25;
self.dx = 0;
self.dy = 0;
self.update = function () {
self.x += self.dx * self.speed;
self.y += self.dy * self.speed;
if (self.x < -50 || self.x > 2098 || self.y < -50 || self.y > 2782) {
self.destroy();
var index = bullets.indexOf(self);
if (index > -1) bullets.splice(index, 1);
}
};
return self;
});
var HealthPickup = Container.expand(function () {
var self = Container.call(this);
var healthBase = self.attachAsset('healthPickup', {
anchorX: 0.5,
anchorY: 0.5
});
var crossH = self.attachAsset('healthCross', {
anchorX: 0.5,
anchorY: 0.5
});
var crossV = self.attachAsset('healthCrossV', {
anchorX: 0.5,
anchorY: 0.5
});
self.healAmount = 30;
self.collected = false;
self.pulseDirection = 1;
self.update = function () {
// Gentle pulsing animation
self.scaleX += 0.01 * self.pulseDirection;
self.scaleY += 0.01 * self.pulseDirection;
if (self.scaleX > 1.2) self.pulseDirection = -1;
if (self.scaleX < 0.9) self.pulseDirection = 1;
self.rotation += 0.02;
if (player && !self.collected && self.intersects(player)) {
self.collected = true;
player.heal(self.healAmount);
LK.effects.flashObject(player, 0x00ff00, 500);
self.destroy();
var index = healthPickups.indexOf(self);
if (index > -1) healthPickups.splice(index, 1);
}
};
return self;
});
var Key = Container.expand(function () {
var self = Container.call(this);
var keyGraphics = self.attachAsset('key', {
anchorX: 0.5,
anchorY: 0.5
});
self.collected = false;
self.update = function () {
self.rotation += 0.05;
if (player && !self.collected && self.intersects(player)) {
self.collected = true;
keys++;
LK.getSound('keyCollect').play();
LK.effects.flashObject(self, 0xffffff, 300);
self.destroy();
}
};
return self;
});
var MapDecoration = Container.expand(function (type) {
var self = Container.call(this);
if (type === 'rock') {
var rock = self.attachAsset('rock', {
anchorX: 0.5,
anchorY: 0.5
});
self.scaleX = 0.8 + Math.random() * 0.4;
self.scaleY = 0.8 + Math.random() * 0.4;
self.rotation = Math.random() * Math.PI * 2;
} else if (type === 'tree') {
var trunk = self.attachAsset('tree', {
anchorX: 0.5,
anchorY: 1
});
var crown = self.attachAsset('treeCrown', {
anchorX: 0.5,
anchorY: 0.8,
y: -60
});
self.scaleX = 0.7 + Math.random() * 0.6;
self.scaleY = 0.7 + Math.random() * 0.6;
} else if (type === 'grass') {
var grass = self.attachAsset('grass', {
anchorX: 0.5,
anchorY: 0.5
});
self.scaleX = 0.5 + Math.random() * 0.8;
self.scaleY = 0.5 + Math.random() * 0.8;
self.rotation = Math.random() * Math.PI * 2;
self.alpha = 0.6 + Math.random() * 0.4;
}
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
// Create character design with multiple parts
var playerBase = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
var playerBody = self.attachAsset('playerBody', {
anchorX: 0.5,
anchorY: 0.5,
y: -5
});
var playerHead = self.attachAsset('playerHead', {
anchorX: 0.5,
anchorY: 0.5,
y: -25
});
self.health = 100;
self.maxHealth = 100;
self.speed = 5;
self.lastShot = 0;
self.shootCooldown = 15;
self.weaponLevel = 1;
self.velocityX = 0;
self.velocityY = 0;
self.friction = 0.85;
self.smoothMove = function (moveX, moveY) {
// Add acceleration instead of instant movement
self.velocityX += moveX * 0.3;
self.velocityY += moveY * 0.3;
// Cap maximum velocity
var maxVel = self.speed;
if (Math.abs(self.velocityX) > maxVel) self.velocityX = self.velocityX > 0 ? maxVel : -maxVel;
if (Math.abs(self.velocityY) > maxVel) self.velocityY = self.velocityY > 0 ? maxVel : -maxVel;
};
self.updateMovement = function () {
// Apply velocity to position
self.x += self.velocityX;
self.y += self.velocityY;
// Apply friction when not moving
self.velocityX *= self.friction;
self.velocityY *= self.friction;
// Keep player in bounds
self.x = Math.max(40, Math.min(2008, self.x));
self.y = Math.max(40, Math.min(2692, self.y));
// Rotate character slightly based on movement direction
if (Math.abs(self.velocityX) > 0.1 || Math.abs(self.velocityY) > 0.1) {
var targetRotation = Math.atan2(self.velocityY, self.velocityX) * 0.1;
tween(playerBase, {
rotation: targetRotation
}, {
duration: 200
});
}
};
self.takeDamage = function (damage) {
self.health -= damage;
if (self.health <= 0) {
self.health = 0;
LK.showGameOver();
}
LK.effects.flashObject(self, 0xff0000, 300);
};
self.heal = function (amount) {
self.health = Math.min(self.maxHealth, self.health + amount);
// Visual healing feedback
tween(self, {
tint: 0x00ff00
}, {
duration: 300,
onFinish: function onFinish() {
tween(self, {
tint: 0xffffff
}, {
duration: 200
});
}
});
};
self.upgrade = function () {
self.weaponLevel++;
self.shootCooldown = Math.max(5, self.shootCooldown - 2);
if (self.weaponLevel >= 5) {
self.shootCooldown = 8;
}
// Visual upgrade feedback
tween(self, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 300,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1
}, {
duration: 200
});
}
});
};
return self;
});
var Zombie = Container.expand(function () {
var self = Container.call(this);
// Create zombie with multiple body parts for better design
var zombieBase = self.attachAsset('zombie', {
anchorX: 0.5,
anchorY: 0.5
});
var zombieBody = self.attachAsset('zombieBody', {
anchorX: 0.5,
anchorY: 0.5,
y: -5
});
var zombieHead = self.attachAsset('zombieHead', {
anchorX: 0.5,
anchorY: 0.5,
y: -25
});
var zombieArms = self.attachAsset('zombieArms', {
anchorX: 0.5,
anchorY: 0.5,
y: -8
});
// Add some visual variety
self.scaleX = 0.8 + Math.random() * 0.4;
self.scaleY = 0.8 + Math.random() * 0.4;
zombieHead.tint = Math.random() > 0.5 ? 0x9acd32 : 0x556b2f;
self.wobbleOffset = Math.random() * Math.PI * 2;
self.health = 50;
self.speed = 1.5;
self.damage = 10;
self.lastAttack = 0;
self.attackCooldown = 60;
self.update = function () {
// Add wobble animation while moving
self.rotation = Math.sin(LK.ticks * 0.1 + self.wobbleOffset) * 0.1;
zombieArms.rotation = Math.sin(LK.ticks * 0.15 + self.wobbleOffset) * 0.3;
if (player) {
var dx = player.x - self.x;
var dy = player.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
if (distance < 50 && LK.ticks - self.lastAttack > self.attackCooldown) {
player.takeDamage(self.damage);
self.lastAttack = LK.ticks;
}
}
};
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xffffff, 100);
return self.health <= 0;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2d1b1b
});
/****
* Game Code
****/
var player;
var zombies = [];
var bullets = [];
var healthPickups = [];
var bombs = [];
var boss = null;
var keys = 0;
var currentWave = 1;
var waveInProgress = false;
var zombiesSpawned = 0;
var zombiesToSpawn = 0;
var spawnTimer = 0;
var healthSpawnTimer = 0;
var moveButton, shootButton, joystickKnob;
var isDragging = false;
var isShootPressed = false;
var joystickRadius = 120;
var joystickCenterX = 200;
var joystickCenterY = 2500;
var currentMoveX = 0;
var currentMoveY = 0;
// UI Elements
var waveText = new Text2('Wave 1', {
size: 80,
fill: 0xFFFFFF
});
waveText.anchor.set(0.5, 0);
LK.gui.top.addChild(waveText);
var healthText = new Text2('Health: 100', {
size: 60,
fill: 0xFF0000
});
healthText.anchor.set(0, 0);
healthText.x = 120;
healthText.y = 20;
LK.gui.topLeft.addChild(healthText);
var keysText = new Text2('Keys: 0', {
size: 60,
fill: 0xFFD700
});
keysText.anchor.set(1, 0);
LK.gui.topRight.addChild(keysText);
var instructionText = new Text2('Survive the zombie waves!\nDefeat bosses to get keys!', {
size: 50,
fill: 0xFFFFFF
});
instructionText.anchor.set(0.5, 0.5);
instructionText.x = 0;
instructionText.y = -200;
LK.gui.center.addChild(instructionText);
// Initialize player
player = new Player();
player.x = 1024;
player.y = 1366;
game.addChild(player);
// Control buttons with enhanced design
moveButton = game.addChild(LK.getAsset('moveButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.6,
scaleY: 1.6
}));
moveButton.x = joystickCenterX;
moveButton.y = joystickCenterY;
moveButton.alpha = 0.4;
// Add joystick knob
joystickKnob = game.addChild(LK.getAsset('joystickKnob', {
anchorX: 0.5,
anchorY: 0.5
}));
joystickKnob.x = joystickCenterX;
joystickKnob.y = joystickCenterY;
joystickKnob.alpha = 0.8;
shootButton = game.addChild(LK.getAsset('shootButton', {
anchorX: 0.5,
anchorY: 0.5
}));
shootButton.x = 1850;
shootButton.y = 2500;
shootButton.alpha = 0.6;
function startWave() {
if (currentWave > 5) {
LK.showYouWin();
return;
}
waveInProgress = true;
zombiesSpawned = 0;
zombiesToSpawn = 5 + currentWave * 3;
spawnTimer = 0;
waveText.setText('Wave ' + currentWave);
instructionText.setText('');
// Spawn bomb for this wave
spawnBombForWave(currentWave);
if (currentWave > 1) {
player.upgrade();
}
}
function spawnZombie() {
var zombie = new Zombie();
var side = Math.floor(Math.random() * 4);
switch (side) {
case 0:
// Top
zombie.x = Math.random() * 2048;
zombie.y = -30;
break;
case 1:
// Right
zombie.x = 2078;
zombie.y = Math.random() * 2732;
break;
case 2:
// Bottom
zombie.x = Math.random() * 2048;
zombie.y = 2762;
break;
case 3:
// Left
zombie.x = -30;
zombie.y = Math.random() * 2732;
break;
}
zombie.health = 50 + currentWave * 15;
zombie.speed = 1.5 + currentWave * 0.4;
// Make final wave zombies extra tough
if (currentWave === 5) {
zombie.health += 50;
zombie.speed += 0.5;
zombie.damage = 20; // Increased damage for final wave
}
zombies.push(zombie);
game.addChild(zombie);
}
function spawnBoss() {
boss = new Boss(currentWave);
boss.x = 1024;
boss.y = 200;
game.addChild(boss);
}
function shootBullet() {
if (LK.ticks - player.lastShot < player.shootCooldown) return;
var isSuper = player.weaponLevel >= 5;
var bullet = new Bullet(isSuper);
bullet.x = player.x;
bullet.y = player.y;
var nearestEnemy = null;
var minDistance = Infinity;
for (var i = 0; i < zombies.length; i++) {
var distance = Math.sqrt(Math.pow(zombies[i].x - player.x, 2) + Math.pow(zombies[i].y - player.y, 2));
if (distance < minDistance) {
minDistance = distance;
nearestEnemy = zombies[i];
}
}
if (boss) {
var bossDistance = Math.sqrt(Math.pow(boss.x - player.x, 2) + Math.pow(boss.y - player.y, 2));
if (bossDistance < minDistance) {
nearestEnemy = boss;
}
}
if (nearestEnemy) {
var dx = nearestEnemy.x - bullet.x;
var dy = nearestEnemy.y - bullet.y;
var distance = Math.sqrt(dx * dx + dy * dy);
bullet.dx = dx / distance;
bullet.dy = dy / distance;
} else {
bullet.dx = 0;
bullet.dy = -1;
}
bullets.push(bullet);
game.addChild(bullet);
player.lastShot = LK.ticks;
LK.getSound('shoot').play();
}
// Touch controls with smooth analog movement
moveButton.down = function (x, y, obj) {
isDragging = true;
// Visual feedback - make joystick base slightly larger
tween(moveButton, {
scaleX: 1.8,
scaleY: 1.8,
alpha: 0.6
}, {
duration: 150
});
};
shootButton.down = function (x, y, obj) {
isShootPressed = true;
// Visual feedback for shoot button
tween(shootButton, {
scaleX: 0.9,
scaleY: 0.9,
tint: 0xffaa00
}, {
duration: 100
});
shootBullet();
};
shootButton.up = function (x, y, obj) {
isShootPressed = false;
// Return shoot button to normal
tween(shootButton, {
scaleX: 1,
scaleY: 1,
tint: 0xffffff
}, {
duration: 150
});
};
game.move = function (x, y, obj) {
if (isDragging) {
var dx = x - joystickCenterX;
var dy = y - joystickCenterY;
var distance = Math.sqrt(dx * dx + dy * dy);
// Limit knob position within joystick radius
if (distance > joystickRadius) {
dx = dx / distance * joystickRadius;
dy = dy / distance * joystickRadius;
distance = joystickRadius;
}
// Update knob position smoothly
joystickKnob.x = joystickCenterX + dx;
joystickKnob.y = joystickCenterY + dy;
// Calculate movement with smooth analog control
var intensity = distance / joystickRadius;
currentMoveX = dx / joystickRadius * player.speed * intensity;
currentMoveY = dy / joystickRadius * player.speed * intensity;
// Apply smooth movement to player
if (player) {
player.smoothMove(currentMoveX, currentMoveY);
}
}
};
game.up = function (x, y, obj) {
if (isDragging) {
isDragging = false;
currentMoveX = 0;
currentMoveY = 0;
// Return joystick to center with smooth animation
tween(joystickKnob, {
x: joystickCenterX,
y: joystickCenterY
}, {
duration: 200,
easing: tween.easeOut
});
tween(moveButton, {
scaleX: 1.6,
scaleY: 1.6,
alpha: 0.4
}, {
duration: 200
});
}
isShootPressed = false;
};
function spawnHealthPickup() {
var health = new HealthPickup();
health.x = 200 + Math.random() * 1648; // Keep away from edges
health.y = 200 + Math.random() * 2332;
healthPickups.push(health);
game.addChild(health);
}
function spawnBombForWave(wave) {
// Remove existing bomb if any
for (var i = bombs.length - 1; i >= 0; i--) {
bombs[i].destroy();
bombs.splice(i, 1);
}
var bomb = new Bomb();
// Position bomb at different locations based on wave
switch (wave) {
case 1:
bomb.x = 400;
bomb.y = 600;
break;
case 2:
bomb.x = 1600;
bomb.y = 1000;
break;
case 3:
bomb.x = 800;
bomb.y = 2000;
break;
case 4:
bomb.x = 1400;
bomb.y = 1600;
break;
case 5:
bomb.x = 1024;
bomb.y = 1000;
break;
}
bombs.push(bomb);
game.addChild(bomb);
}
function createMapDecorations() {
// Add trees around the map
for (var i = 0; i < 8; i++) {
var tree = new MapDecoration('tree');
tree.x = 100 + Math.random() * 1848;
tree.y = 100 + Math.random() * 2532;
game.addChild(tree);
}
// Add rocks scattered around
for (var i = 0; i < 15; i++) {
var rock = new MapDecoration('rock');
rock.x = 100 + Math.random() * 1848;
rock.y = 100 + Math.random() * 2532;
game.addChild(rock);
}
// Add grass patches
for (var i = 0; i < 25; i++) {
var grass = new MapDecoration('grass');
grass.x = Math.random() * 2048;
grass.y = Math.random() * 2732;
game.addChild(grass);
}
}
// Create map decorations
createMapDecorations();
// Start first wave
startWave();
LK.playMusic('gameMusic');
game.update = function () {
// Update UI
healthText.setText('Health: ' + player.health);
keysText.setText('Keys: ' + keys);
// Update player movement
if (player) {
player.updateMovement();
}
// Spawn health pickups occasionally
healthSpawnTimer++;
if (healthSpawnTimer >= 1800 && healthPickups.length < 2) {
// Every 30 seconds max 2 on map
spawnHealthPickup();
healthSpawnTimer = 0;
}
// Smooth continuous shooting when button held
if (isShootPressed) {
shootBullet();
}
// Spawn zombies during wave
if (waveInProgress && zombiesSpawned < zombiesToSpawn) {
spawnTimer++;
if (spawnTimer >= 60) {
spawnZombie();
zombiesSpawned++;
spawnTimer = 0;
}
}
// Check if wave is complete
if (waveInProgress && zombiesSpawned >= zombiesToSpawn && zombies.length === 0 && !boss) {
spawnBoss();
}
// Bullet collision detection
for (var i = bullets.length - 1; i >= 0; i--) {
var bullet = bullets[i];
var hit = false;
// Check bomb collisions
for (var k = bombs.length - 1; k >= 0; k--) {
if (bullet.intersects(bombs[k])) {
if (bombs[k].takeDamage(bullet.damage)) {
// Bomb will handle its own destruction and explosion
}
bullet.destroy();
bullets.splice(i, 1);
hit = true;
break;
}
}
if (!hit) {
// Check zombie collisions
for (var j = zombies.length - 1; j >= 0; j--) {
if (bullet.intersects(zombies[j])) {
if (zombies[j].takeDamage(bullet.damage)) {
LK.getSound('zombieDeath').play();
LK.setScore(LK.getScore() + 10);
zombies[j].destroy();
zombies.splice(j, 1);
}
bullet.destroy();
bullets.splice(i, 1);
hit = true;
break;
}
}
}
// Check boss collision
if (!hit && boss && bullet.intersects(boss)) {
if (boss.takeDamage(bullet.damage)) {
LK.getSound('bossDefeat').play();
LK.setScore(LK.getScore() + 100);
// Drop key
var key = new Key();
key.x = boss.x;
key.y = boss.y;
game.addChild(key);
boss.destroy();
boss = null;
waveInProgress = false;
currentWave++;
LK.setTimeout(function () {
if (currentWave <= 5) {
startWave();
} else {
instructionText.setText('Congratulations!\nYou survived all waves!');
LK.setTimeout(function () {
LK.showYouWin();
}, 3000);
}
}, 2000);
}
bullet.destroy();
bullets.splice(i, 1);
}
}
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Bomb = Container.expand(function () {
var self = Container.call(this);
var bombBase = self.attachAsset('bomb', {
anchorX: 0.5,
anchorY: 0.5
});
var fuse = self.attachAsset('bombFuse', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: -35
});
var spark = self.attachAsset('bombSpark', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: -40
});
self.health = 25;
self.exploded = false;
self.sparkOffset = 0;
self.update = function () {
// Animate the spark on the fuse
self.sparkOffset += 0.15;
spark.alpha = 0.8 + Math.sin(self.sparkOffset) * 0.2;
spark.scaleX = 0.8 + Math.sin(self.sparkOffset * 2) * 0.3;
spark.scaleY = 0.8 + Math.sin(self.sparkOffset * 2) * 0.3;
// Gentle bobbing animation
self.y += Math.sin(LK.ticks * 0.05) * 0.3;
};
self.explode = function () {
if (self.exploded) return;
self.exploded = true;
// Create explosion effect
LK.effects.flashScreen(0xff4500, 300);
// Damage nearby enemies and player
var explosionRadius = 150;
for (var i = 0; i < zombies.length; i++) {
var zombie = zombies[i];
var distance = Math.sqrt(Math.pow(zombie.x - self.x, 2) + Math.pow(zombie.y - self.y, 2));
if (distance < explosionRadius) {
if (zombie.takeDamage(75)) {
LK.getSound('zombieDeath').play();
LK.setScore(LK.getScore() + 10);
zombie.destroy();
zombies.splice(i, 1);
i--;
}
}
}
// Check boss damage
if (boss) {
var bossDistance = Math.sqrt(Math.pow(boss.x - self.x, 2) + Math.pow(boss.y - self.y, 2));
if (bossDistance < explosionRadius) {
boss.takeDamage(100);
}
}
// Check player damage
if (player) {
var playerDistance = Math.sqrt(Math.pow(player.x - self.x, 2) + Math.pow(player.y - self.y, 2));
if (playerDistance < explosionRadius) {
player.takeDamage(30);
}
}
// Remove bomb
self.destroy();
var index = bombs.indexOf(self);
if (index > -1) bombs.splice(index, 1);
};
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xffffff, 100);
if (self.health <= 0) {
self.explode();
return true;
}
return false;
};
return self;
});
var Boss = Container.expand(function (type) {
var self = Container.call(this);
var bossAsset = type === 5 ? 'finalBoss' : 'boss' + type;
var bossGraphics = self.attachAsset(bossAsset, {
anchorX: 0.5,
anchorY: 0.5
});
// Add unique design elements for each boss
if (type === 1) {
// Speed boss with spikes
var eyes1 = self.attachAsset('boss1Eyes', {
anchorX: 0.5,
anchorY: 0.5,
x: -15,
y: -10
});
var eyes2 = self.attachAsset('boss1Eyes', {
anchorX: 0.5,
anchorY: 0.5,
x: 15,
y: -10
});
var spike1 = self.attachAsset('boss1Spikes', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: -35,
rotation: 0
});
var spike2 = self.attachAsset('boss1Spikes', {
anchorX: 0.5,
anchorY: 0.5,
x: -25,
y: 0,
rotation: 1.57
});
var spike3 = self.attachAsset('boss1Spikes', {
anchorX: 0.5,
anchorY: 0.5,
x: 25,
y: 0,
rotation: -1.57
});
} else if (type === 2) {
// Summoner boss with horns and claws
var horn = self.attachAsset('boss2Horn', {
anchorX: 0.5,
anchorY: 1,
x: 0,
y: -40
});
var claw1 = self.attachAsset('boss2Claws', {
anchorX: 0.5,
anchorY: 0.5,
x: -35,
y: 0
});
var claw2 = self.attachAsset('boss2Claws', {
anchorX: 0.5,
anchorY: 0.5,
x: 35,
y: 0
});
} else if (type === 3) {
// Screamer boss with armor and gem
var armor = self.attachAsset('boss3Armor', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 0
});
var gem = self.attachAsset('boss3Gem', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: -20
});
self.gemRef = gem; // Store reference for pulsing animation
} else if (type === 4) {
// Healer boss with wings and crown
var wing1 = self.attachAsset('boss4Wings', {
anchorX: 0.5,
anchorY: 0.5,
x: -50,
y: 0
});
var wing2 = self.attachAsset('boss4Wings', {
anchorX: 0.5,
anchorY: 0.5,
x: 50,
y: 0
});
var crown = self.attachAsset('boss4Crown', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: -50
});
self.wing1Ref = wing1;
self.wing2Ref = wing2;
}
self.type = type;
self.health = 150 + type * 50;
// Make final boss extra tough
if (type === 5) {
self.health = 500; // Much higher health for final boss
}
self.maxHealth = self.health;
self.speed = 0.8 + type * 0.2;
self.damage = 15 + type * 5;
// Increase final boss damage
if (type === 5) {
self.damage = 40; // Much higher damage for final boss
}
self.lastAttack = 0;
self.attackCooldown = 90 - type * 10;
self.lastSpecial = 0;
self.specialCooldown = 300;
self.update = function () {
// Boss-specific animations
if (self.type === 3 && self.gemRef) {
// Pulsing gem animation for screamer boss
self.gemRef.scaleX = 1 + Math.sin(LK.ticks * 0.1) * 0.3;
self.gemRef.scaleY = 1 + Math.sin(LK.ticks * 0.1) * 0.3;
} else if (self.type === 4 && self.wing1Ref && self.wing2Ref) {
// Flapping wings animation for healer boss
var wingFlap = Math.sin(LK.ticks * 0.15) * 0.2;
self.wing1Ref.rotation = wingFlap;
self.wing2Ref.rotation = -wingFlap;
}
if (player) {
var dx = player.x - self.x;
var dy = player.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
if (distance < 80 && LK.ticks - self.lastAttack > self.attackCooldown) {
player.takeDamage(self.damage);
self.lastAttack = LK.ticks;
LK.effects.flashScreen(0xff0000, 200);
}
if (LK.ticks - self.lastSpecial > self.specialCooldown) {
self.specialAttack();
self.lastSpecial = LK.ticks;
}
}
};
self.specialAttack = function () {
if (self.type === 1) {
self.speed *= 2;
tween(self, {
speed: 0.8
}, {
duration: 2000
});
} else if (self.type === 2) {
for (var i = 0; i < 3; i++) {
var minion = new Zombie();
minion.x = self.x + (Math.random() - 0.5) * 200;
minion.y = self.y + (Math.random() - 0.5) * 200;
minion.health = 25;
zombies.push(minion);
game.addChild(minion);
}
} else if (self.type === 3) {
LK.effects.flashScreen(0x800080, 500);
player.takeDamage(20);
} else if (self.type === 4) {
self.health += 25;
self.health = Math.min(self.health, self.maxHealth);
LK.effects.flashObject(self, 0x00ff00, 500);
} else if (self.type === 5) {
for (var i = 0; i < 8; i++) {
var angle = i / 8 * Math.PI * 2;
var shockwave = new Zombie();
shockwave.x = self.x;
shockwave.y = self.y;
shockwave.speed = 3;
shockwave.moveAngle = angle;
shockwave.health = 1;
shockwave.damage = 25;
shockwave.update = function () {
this.x += Math.cos(this.moveAngle) * this.speed;
this.y += Math.sin(this.moveAngle) * this.speed;
if (this.x < 0 || this.x > 2048 || this.y < 0 || this.y > 2732) {
this.destroy();
var index = zombies.indexOf(this);
if (index > -1) zombies.splice(index, 1);
}
};
zombies.push(shockwave);
game.addChild(shockwave);
}
}
};
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xffffff, 150);
return self.health <= 0;
};
return self;
});
var Bullet = Container.expand(function (isSuper) {
var self = Container.call(this);
var bulletAsset = isSuper ? 'superBullet' : 'bullet';
var bulletGraphics = self.attachAsset(bulletAsset, {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.damage = isSuper ? 30 : 25;
self.dx = 0;
self.dy = 0;
self.update = function () {
self.x += self.dx * self.speed;
self.y += self.dy * self.speed;
if (self.x < -50 || self.x > 2098 || self.y < -50 || self.y > 2782) {
self.destroy();
var index = bullets.indexOf(self);
if (index > -1) bullets.splice(index, 1);
}
};
return self;
});
var HealthPickup = Container.expand(function () {
var self = Container.call(this);
var healthBase = self.attachAsset('healthPickup', {
anchorX: 0.5,
anchorY: 0.5
});
var crossH = self.attachAsset('healthCross', {
anchorX: 0.5,
anchorY: 0.5
});
var crossV = self.attachAsset('healthCrossV', {
anchorX: 0.5,
anchorY: 0.5
});
self.healAmount = 30;
self.collected = false;
self.pulseDirection = 1;
self.update = function () {
// Gentle pulsing animation
self.scaleX += 0.01 * self.pulseDirection;
self.scaleY += 0.01 * self.pulseDirection;
if (self.scaleX > 1.2) self.pulseDirection = -1;
if (self.scaleX < 0.9) self.pulseDirection = 1;
self.rotation += 0.02;
if (player && !self.collected && self.intersects(player)) {
self.collected = true;
player.heal(self.healAmount);
LK.effects.flashObject(player, 0x00ff00, 500);
self.destroy();
var index = healthPickups.indexOf(self);
if (index > -1) healthPickups.splice(index, 1);
}
};
return self;
});
var Key = Container.expand(function () {
var self = Container.call(this);
var keyGraphics = self.attachAsset('key', {
anchorX: 0.5,
anchorY: 0.5
});
self.collected = false;
self.update = function () {
self.rotation += 0.05;
if (player && !self.collected && self.intersects(player)) {
self.collected = true;
keys++;
LK.getSound('keyCollect').play();
LK.effects.flashObject(self, 0xffffff, 300);
self.destroy();
}
};
return self;
});
var MapDecoration = Container.expand(function (type) {
var self = Container.call(this);
if (type === 'rock') {
var rock = self.attachAsset('rock', {
anchorX: 0.5,
anchorY: 0.5
});
self.scaleX = 0.8 + Math.random() * 0.4;
self.scaleY = 0.8 + Math.random() * 0.4;
self.rotation = Math.random() * Math.PI * 2;
} else if (type === 'tree') {
var trunk = self.attachAsset('tree', {
anchorX: 0.5,
anchorY: 1
});
var crown = self.attachAsset('treeCrown', {
anchorX: 0.5,
anchorY: 0.8,
y: -60
});
self.scaleX = 0.7 + Math.random() * 0.6;
self.scaleY = 0.7 + Math.random() * 0.6;
} else if (type === 'grass') {
var grass = self.attachAsset('grass', {
anchorX: 0.5,
anchorY: 0.5
});
self.scaleX = 0.5 + Math.random() * 0.8;
self.scaleY = 0.5 + Math.random() * 0.8;
self.rotation = Math.random() * Math.PI * 2;
self.alpha = 0.6 + Math.random() * 0.4;
}
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
// Create character design with multiple parts
var playerBase = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
var playerBody = self.attachAsset('playerBody', {
anchorX: 0.5,
anchorY: 0.5,
y: -5
});
var playerHead = self.attachAsset('playerHead', {
anchorX: 0.5,
anchorY: 0.5,
y: -25
});
self.health = 100;
self.maxHealth = 100;
self.speed = 5;
self.lastShot = 0;
self.shootCooldown = 15;
self.weaponLevel = 1;
self.velocityX = 0;
self.velocityY = 0;
self.friction = 0.85;
self.smoothMove = function (moveX, moveY) {
// Add acceleration instead of instant movement
self.velocityX += moveX * 0.3;
self.velocityY += moveY * 0.3;
// Cap maximum velocity
var maxVel = self.speed;
if (Math.abs(self.velocityX) > maxVel) self.velocityX = self.velocityX > 0 ? maxVel : -maxVel;
if (Math.abs(self.velocityY) > maxVel) self.velocityY = self.velocityY > 0 ? maxVel : -maxVel;
};
self.updateMovement = function () {
// Apply velocity to position
self.x += self.velocityX;
self.y += self.velocityY;
// Apply friction when not moving
self.velocityX *= self.friction;
self.velocityY *= self.friction;
// Keep player in bounds
self.x = Math.max(40, Math.min(2008, self.x));
self.y = Math.max(40, Math.min(2692, self.y));
// Rotate character slightly based on movement direction
if (Math.abs(self.velocityX) > 0.1 || Math.abs(self.velocityY) > 0.1) {
var targetRotation = Math.atan2(self.velocityY, self.velocityX) * 0.1;
tween(playerBase, {
rotation: targetRotation
}, {
duration: 200
});
}
};
self.takeDamage = function (damage) {
self.health -= damage;
if (self.health <= 0) {
self.health = 0;
LK.showGameOver();
}
LK.effects.flashObject(self, 0xff0000, 300);
};
self.heal = function (amount) {
self.health = Math.min(self.maxHealth, self.health + amount);
// Visual healing feedback
tween(self, {
tint: 0x00ff00
}, {
duration: 300,
onFinish: function onFinish() {
tween(self, {
tint: 0xffffff
}, {
duration: 200
});
}
});
};
self.upgrade = function () {
self.weaponLevel++;
self.shootCooldown = Math.max(5, self.shootCooldown - 2);
if (self.weaponLevel >= 5) {
self.shootCooldown = 8;
}
// Visual upgrade feedback
tween(self, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 300,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1
}, {
duration: 200
});
}
});
};
return self;
});
var Zombie = Container.expand(function () {
var self = Container.call(this);
// Create zombie with multiple body parts for better design
var zombieBase = self.attachAsset('zombie', {
anchorX: 0.5,
anchorY: 0.5
});
var zombieBody = self.attachAsset('zombieBody', {
anchorX: 0.5,
anchorY: 0.5,
y: -5
});
var zombieHead = self.attachAsset('zombieHead', {
anchorX: 0.5,
anchorY: 0.5,
y: -25
});
var zombieArms = self.attachAsset('zombieArms', {
anchorX: 0.5,
anchorY: 0.5,
y: -8
});
// Add some visual variety
self.scaleX = 0.8 + Math.random() * 0.4;
self.scaleY = 0.8 + Math.random() * 0.4;
zombieHead.tint = Math.random() > 0.5 ? 0x9acd32 : 0x556b2f;
self.wobbleOffset = Math.random() * Math.PI * 2;
self.health = 50;
self.speed = 1.5;
self.damage = 10;
self.lastAttack = 0;
self.attackCooldown = 60;
self.update = function () {
// Add wobble animation while moving
self.rotation = Math.sin(LK.ticks * 0.1 + self.wobbleOffset) * 0.1;
zombieArms.rotation = Math.sin(LK.ticks * 0.15 + self.wobbleOffset) * 0.3;
if (player) {
var dx = player.x - self.x;
var dy = player.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
if (distance < 50 && LK.ticks - self.lastAttack > self.attackCooldown) {
player.takeDamage(self.damage);
self.lastAttack = LK.ticks;
}
}
};
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xffffff, 100);
return self.health <= 0;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2d1b1b
});
/****
* Game Code
****/
var player;
var zombies = [];
var bullets = [];
var healthPickups = [];
var bombs = [];
var boss = null;
var keys = 0;
var currentWave = 1;
var waveInProgress = false;
var zombiesSpawned = 0;
var zombiesToSpawn = 0;
var spawnTimer = 0;
var healthSpawnTimer = 0;
var moveButton, shootButton, joystickKnob;
var isDragging = false;
var isShootPressed = false;
var joystickRadius = 120;
var joystickCenterX = 200;
var joystickCenterY = 2500;
var currentMoveX = 0;
var currentMoveY = 0;
// UI Elements
var waveText = new Text2('Wave 1', {
size: 80,
fill: 0xFFFFFF
});
waveText.anchor.set(0.5, 0);
LK.gui.top.addChild(waveText);
var healthText = new Text2('Health: 100', {
size: 60,
fill: 0xFF0000
});
healthText.anchor.set(0, 0);
healthText.x = 120;
healthText.y = 20;
LK.gui.topLeft.addChild(healthText);
var keysText = new Text2('Keys: 0', {
size: 60,
fill: 0xFFD700
});
keysText.anchor.set(1, 0);
LK.gui.topRight.addChild(keysText);
var instructionText = new Text2('Survive the zombie waves!\nDefeat bosses to get keys!', {
size: 50,
fill: 0xFFFFFF
});
instructionText.anchor.set(0.5, 0.5);
instructionText.x = 0;
instructionText.y = -200;
LK.gui.center.addChild(instructionText);
// Initialize player
player = new Player();
player.x = 1024;
player.y = 1366;
game.addChild(player);
// Control buttons with enhanced design
moveButton = game.addChild(LK.getAsset('moveButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.6,
scaleY: 1.6
}));
moveButton.x = joystickCenterX;
moveButton.y = joystickCenterY;
moveButton.alpha = 0.4;
// Add joystick knob
joystickKnob = game.addChild(LK.getAsset('joystickKnob', {
anchorX: 0.5,
anchorY: 0.5
}));
joystickKnob.x = joystickCenterX;
joystickKnob.y = joystickCenterY;
joystickKnob.alpha = 0.8;
shootButton = game.addChild(LK.getAsset('shootButton', {
anchorX: 0.5,
anchorY: 0.5
}));
shootButton.x = 1850;
shootButton.y = 2500;
shootButton.alpha = 0.6;
function startWave() {
if (currentWave > 5) {
LK.showYouWin();
return;
}
waveInProgress = true;
zombiesSpawned = 0;
zombiesToSpawn = 5 + currentWave * 3;
spawnTimer = 0;
waveText.setText('Wave ' + currentWave);
instructionText.setText('');
// Spawn bomb for this wave
spawnBombForWave(currentWave);
if (currentWave > 1) {
player.upgrade();
}
}
function spawnZombie() {
var zombie = new Zombie();
var side = Math.floor(Math.random() * 4);
switch (side) {
case 0:
// Top
zombie.x = Math.random() * 2048;
zombie.y = -30;
break;
case 1:
// Right
zombie.x = 2078;
zombie.y = Math.random() * 2732;
break;
case 2:
// Bottom
zombie.x = Math.random() * 2048;
zombie.y = 2762;
break;
case 3:
// Left
zombie.x = -30;
zombie.y = Math.random() * 2732;
break;
}
zombie.health = 50 + currentWave * 15;
zombie.speed = 1.5 + currentWave * 0.4;
// Make final wave zombies extra tough
if (currentWave === 5) {
zombie.health += 50;
zombie.speed += 0.5;
zombie.damage = 20; // Increased damage for final wave
}
zombies.push(zombie);
game.addChild(zombie);
}
function spawnBoss() {
boss = new Boss(currentWave);
boss.x = 1024;
boss.y = 200;
game.addChild(boss);
}
function shootBullet() {
if (LK.ticks - player.lastShot < player.shootCooldown) return;
var isSuper = player.weaponLevel >= 5;
var bullet = new Bullet(isSuper);
bullet.x = player.x;
bullet.y = player.y;
var nearestEnemy = null;
var minDistance = Infinity;
for (var i = 0; i < zombies.length; i++) {
var distance = Math.sqrt(Math.pow(zombies[i].x - player.x, 2) + Math.pow(zombies[i].y - player.y, 2));
if (distance < minDistance) {
minDistance = distance;
nearestEnemy = zombies[i];
}
}
if (boss) {
var bossDistance = Math.sqrt(Math.pow(boss.x - player.x, 2) + Math.pow(boss.y - player.y, 2));
if (bossDistance < minDistance) {
nearestEnemy = boss;
}
}
if (nearestEnemy) {
var dx = nearestEnemy.x - bullet.x;
var dy = nearestEnemy.y - bullet.y;
var distance = Math.sqrt(dx * dx + dy * dy);
bullet.dx = dx / distance;
bullet.dy = dy / distance;
} else {
bullet.dx = 0;
bullet.dy = -1;
}
bullets.push(bullet);
game.addChild(bullet);
player.lastShot = LK.ticks;
LK.getSound('shoot').play();
}
// Touch controls with smooth analog movement
moveButton.down = function (x, y, obj) {
isDragging = true;
// Visual feedback - make joystick base slightly larger
tween(moveButton, {
scaleX: 1.8,
scaleY: 1.8,
alpha: 0.6
}, {
duration: 150
});
};
shootButton.down = function (x, y, obj) {
isShootPressed = true;
// Visual feedback for shoot button
tween(shootButton, {
scaleX: 0.9,
scaleY: 0.9,
tint: 0xffaa00
}, {
duration: 100
});
shootBullet();
};
shootButton.up = function (x, y, obj) {
isShootPressed = false;
// Return shoot button to normal
tween(shootButton, {
scaleX: 1,
scaleY: 1,
tint: 0xffffff
}, {
duration: 150
});
};
game.move = function (x, y, obj) {
if (isDragging) {
var dx = x - joystickCenterX;
var dy = y - joystickCenterY;
var distance = Math.sqrt(dx * dx + dy * dy);
// Limit knob position within joystick radius
if (distance > joystickRadius) {
dx = dx / distance * joystickRadius;
dy = dy / distance * joystickRadius;
distance = joystickRadius;
}
// Update knob position smoothly
joystickKnob.x = joystickCenterX + dx;
joystickKnob.y = joystickCenterY + dy;
// Calculate movement with smooth analog control
var intensity = distance / joystickRadius;
currentMoveX = dx / joystickRadius * player.speed * intensity;
currentMoveY = dy / joystickRadius * player.speed * intensity;
// Apply smooth movement to player
if (player) {
player.smoothMove(currentMoveX, currentMoveY);
}
}
};
game.up = function (x, y, obj) {
if (isDragging) {
isDragging = false;
currentMoveX = 0;
currentMoveY = 0;
// Return joystick to center with smooth animation
tween(joystickKnob, {
x: joystickCenterX,
y: joystickCenterY
}, {
duration: 200,
easing: tween.easeOut
});
tween(moveButton, {
scaleX: 1.6,
scaleY: 1.6,
alpha: 0.4
}, {
duration: 200
});
}
isShootPressed = false;
};
function spawnHealthPickup() {
var health = new HealthPickup();
health.x = 200 + Math.random() * 1648; // Keep away from edges
health.y = 200 + Math.random() * 2332;
healthPickups.push(health);
game.addChild(health);
}
function spawnBombForWave(wave) {
// Remove existing bomb if any
for (var i = bombs.length - 1; i >= 0; i--) {
bombs[i].destroy();
bombs.splice(i, 1);
}
var bomb = new Bomb();
// Position bomb at different locations based on wave
switch (wave) {
case 1:
bomb.x = 400;
bomb.y = 600;
break;
case 2:
bomb.x = 1600;
bomb.y = 1000;
break;
case 3:
bomb.x = 800;
bomb.y = 2000;
break;
case 4:
bomb.x = 1400;
bomb.y = 1600;
break;
case 5:
bomb.x = 1024;
bomb.y = 1000;
break;
}
bombs.push(bomb);
game.addChild(bomb);
}
function createMapDecorations() {
// Add trees around the map
for (var i = 0; i < 8; i++) {
var tree = new MapDecoration('tree');
tree.x = 100 + Math.random() * 1848;
tree.y = 100 + Math.random() * 2532;
game.addChild(tree);
}
// Add rocks scattered around
for (var i = 0; i < 15; i++) {
var rock = new MapDecoration('rock');
rock.x = 100 + Math.random() * 1848;
rock.y = 100 + Math.random() * 2532;
game.addChild(rock);
}
// Add grass patches
for (var i = 0; i < 25; i++) {
var grass = new MapDecoration('grass');
grass.x = Math.random() * 2048;
grass.y = Math.random() * 2732;
game.addChild(grass);
}
}
// Create map decorations
createMapDecorations();
// Start first wave
startWave();
LK.playMusic('gameMusic');
game.update = function () {
// Update UI
healthText.setText('Health: ' + player.health);
keysText.setText('Keys: ' + keys);
// Update player movement
if (player) {
player.updateMovement();
}
// Spawn health pickups occasionally
healthSpawnTimer++;
if (healthSpawnTimer >= 1800 && healthPickups.length < 2) {
// Every 30 seconds max 2 on map
spawnHealthPickup();
healthSpawnTimer = 0;
}
// Smooth continuous shooting when button held
if (isShootPressed) {
shootBullet();
}
// Spawn zombies during wave
if (waveInProgress && zombiesSpawned < zombiesToSpawn) {
spawnTimer++;
if (spawnTimer >= 60) {
spawnZombie();
zombiesSpawned++;
spawnTimer = 0;
}
}
// Check if wave is complete
if (waveInProgress && zombiesSpawned >= zombiesToSpawn && zombies.length === 0 && !boss) {
spawnBoss();
}
// Bullet collision detection
for (var i = bullets.length - 1; i >= 0; i--) {
var bullet = bullets[i];
var hit = false;
// Check bomb collisions
for (var k = bombs.length - 1; k >= 0; k--) {
if (bullet.intersects(bombs[k])) {
if (bombs[k].takeDamage(bullet.damage)) {
// Bomb will handle its own destruction and explosion
}
bullet.destroy();
bullets.splice(i, 1);
hit = true;
break;
}
}
if (!hit) {
// Check zombie collisions
for (var j = zombies.length - 1; j >= 0; j--) {
if (bullet.intersects(zombies[j])) {
if (zombies[j].takeDamage(bullet.damage)) {
LK.getSound('zombieDeath').play();
LK.setScore(LK.getScore() + 10);
zombies[j].destroy();
zombies.splice(j, 1);
}
bullet.destroy();
bullets.splice(i, 1);
hit = true;
break;
}
}
}
// Check boss collision
if (!hit && boss && bullet.intersects(boss)) {
if (boss.takeDamage(bullet.damage)) {
LK.getSound('bossDefeat').play();
LK.setScore(LK.getScore() + 100);
// Drop key
var key = new Key();
key.x = boss.x;
key.y = boss.y;
game.addChild(key);
boss.destroy();
boss = null;
waveInProgress = false;
currentWave++;
LK.setTimeout(function () {
if (currentWave <= 5) {
startWave();
} else {
instructionText.setText('Congratulations!\nYou survived all waves!');
LK.setTimeout(function () {
LK.showYouWin();
}, 3000);
}
}, 2000);
}
bullet.destroy();
bullets.splice(i, 1);
}
}
};