/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Bullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 15;
self.damage = 25;
self.targetX = 0;
self.targetY = 0;
self.directionX = 0;
self.directionY = 0;
self.setTarget = function (targetX, targetY) {
var dx = targetX - self.x;
var dy = targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
self.directionX = dx / distance;
self.directionY = dy / distance;
}
};
self.update = function () {
self.x += self.directionX * self.speed;
self.y += self.directionY * self.speed;
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 100;
self.maxHealth = 100;
self.ammo = 50;
self.maxAmmo = 100;
self.shootCooldown = 0;
self.shootRate = 15; // shoots every 15 ticks
self.rapidFireBonus = 0;
self.takeDamage = function (damage) {
self.health -= damage;
if (self.health <= 0) {
self.health = 0;
return true; // player died
}
return false;
};
self.addHealth = function (amount) {
self.health = Math.min(self.health + amount, self.maxHealth);
};
self.addAmmo = function (amount) {
self.ammo = Math.min(self.ammo + amount, self.maxAmmo);
};
self.update = function () {
if (self.shootCooldown > 0) {
self.shootCooldown--;
}
if (self.rapidFireBonus > 0) {
self.rapidFireBonus--;
}
};
return self;
});
var PowerUp = Container.expand(function () {
var self = Container.call(this);
self.type = 'health';
self.lifetime = 600; // 10 seconds at 60fps
self.update = function () {
self.lifetime--;
if (self.lifetime <= 0) {
return true; // should be removed
}
return false;
};
return self;
});
var RapidFire = PowerUp.expand(function () {
var self = PowerUp.call(this);
var packGraphics = self.attachAsset('rapidFire', {
anchorX: 0.5,
anchorY: 0.5
});
self.type = 'rapidfire';
self.value = 300; // 5 seconds of rapid fire
return self;
});
var HealthPack = PowerUp.expand(function () {
var self = PowerUp.call(this);
var packGraphics = self.attachAsset('healthPack', {
anchorX: 0.5,
anchorY: 0.5
});
self.type = 'health';
self.value = 30;
return self;
});
var AmmoPack = PowerUp.expand(function () {
var self = PowerUp.call(this);
var packGraphics = self.attachAsset('ammoPack', {
anchorX: 0.5,
anchorY: 0.5
});
self.type = 'ammo';
self.value = 25;
return self;
});
var PowerUpManager = Container.expand(function () {
var self = Container.call(this);
// Drop table configuration - weights determine relative probability
self.dropTables = {
zombie: [{
type: 'ammo',
weight: 50,
"class": AmmoPack
}, {
type: 'health',
weight: 10,
"class": HealthPack
}, {
type: 'rapidfire',
weight: 5,
"class": RapidFire
}, {
type: 'nothing',
weight: 35,
"class": null
}],
wave: [{
type: 'ammo',
weight: 30,
"class": AmmoPack
}, {
type: 'health',
weight: 20,
"class": HealthPack
}, {
type: 'rapidfire',
weight: 15,
"class": RapidFire
}, {
type: 'nothing',
weight: 35,
"class": null
}]
};
self.getWeightedDrop = function (dropTable) {
var totalWeight = 0;
for (var i = 0; i < dropTable.length; i++) {
totalWeight += dropTable[i].weight;
}
var random = Math.random() * totalWeight;
var currentWeight = 0;
for (var i = 0; i < dropTable.length; i++) {
currentWeight += dropTable[i].weight;
if (random <= currentWeight) {
return dropTable[i];
}
}
return dropTable[dropTable.length - 1]; // fallback
};
self.spawnDrop = function (x, y, tableType) {
var dropConfig = self.getWeightedDrop(self.dropTables[tableType || 'zombie']);
if (dropConfig["class"]) {
var powerUp = new dropConfig["class"]();
powerUp.x = x;
powerUp.y = y;
powerUps.push(powerUp);
game.addChild(powerUp);
return powerUp;
}
return null; // no drop
};
// Modify drop rates dynamically based on game state
self.adjustDropRates = function () {
var survivalTime = gameTime / 3600; // minutes survived
var difficultyMultiplier = 1 + survivalTime * 0.1;
// Adjust drop rates based on difficulty level
if (difficultyLevel === 2) {
// Difficulty level 2: Set specific drop rates
self.dropTables.zombie[2].weight = 10; // rapid fire 10%
self.dropTables.zombie[1].weight = 15; // health pack 15%
self.dropTables.zombie[0].weight = 50; // ammo remains 50%
self.dropTables.zombie[3].weight = 25; // nothing 25% (100 - 10 - 15 - 50)
} else {
// Original dynamic adjustment for other difficulty levels
// Increase health drops as game gets harder
if (player.health < 30) {
self.dropTables.zombie[1].weight = 20; // increase health drop chance
} else {
self.dropTables.zombie[1].weight = 10; // normal health drop chance
}
// Increase ammo drops if low on ammo
if (player.ammo < 10) {
self.dropTables.zombie[0].weight = 70; // increase ammo drop chance
} else {
self.dropTables.zombie[0].weight = 50; // normal ammo drop chance
}
}
};
return self;
});
var Zombie = Container.expand(function () {
var self = Container.call(this);
self.health = 50;
self.maxHealth = 50;
self.speed = 2;
self.damage = 20;
self.scoreValue = 10;
self.type = 'walker';
self.takeDamage = function (damage) {
self.health -= damage;
return self.health <= 0;
};
self.update = function () {
// Move toward 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;
}
};
return self;
});
var ZombieWalker = Zombie.expand(function () {
var self = Zombie.call(this);
var zombieGraphics = self.attachAsset('zombieWalker', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 75;
self.maxHealth = 75;
self.speed = 2;
self.damage = 20;
self.scoreValue = 10;
self.type = 'walker';
return self;
});
var ZombieRunner = Zombie.expand(function () {
var self = Zombie.call(this);
var zombieGraphics = self.attachAsset('zombieRunner', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 30;
self.maxHealth = 30;
self.speed = 4;
self.damage = 15;
self.scoreValue = 15;
self.type = 'runner';
return self;
});
var ZombieBrute = Zombie.expand(function () {
var self = Zombie.call(this);
var zombieGraphics = self.attachAsset('zombieBrute', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 200;
self.maxHealth = 200;
self.speed = 1;
self.damage = 35;
self.scoreValue = 25;
self.type = 'brute';
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({});
/****
* Game Code
****/
// Game variables
var player;
var bullets = [];
var zombies = [];
var powerUps = [];
var powerUpManager;
var waveNumber = 1;
var zombiesKilled = 0;
var gameTime = 0;
var spawnTimer = 0;
var powerUpTimer = 0;
var dragNode = null;
var isShootingEnabled = true; // Toggle for automatic shooting
var difficultyLevel = 1;
var lastDifficultyIncrease = 0;
var lastClickTime = 0;
var doubleClickDelay = 300; // milliseconds for double-click detection
// UI Elements with styled containers and progress bars
// Score display with background container
var scoreContainer = new Container();
var scoreBg = LK.getAsset('bullet', {
width: 200,
height: 50,
anchorX: 0,
anchorY: 0,
tint: 0x000000,
alpha: 0.7
});
scoreContainer.addChild(scoreBg);
var scoreText = new Text2('Score: 0', {
size: 32,
fill: 0xFFFFFF,
font: "'Impact', 'Arial Black', 'Tahoma', sans-serif"
});
scoreText.anchor.set(0, 0.5);
scoreText.x = 10;
scoreText.y = 25;
scoreContainer.addChild(scoreText);
scoreContainer.x = 120;
scoreContainer.y = 20;
LK.gui.topLeft.addChild(scoreContainer);
// Health bar with container and progress indicator
var healthContainer = new Container();
var healthBg = LK.getAsset('bullet', {
width: 300,
height: 40,
anchorX: 0.5,
anchorY: 0,
tint: 0x330000,
alpha: 0.8
});
healthContainer.addChild(healthBg);
var healthBar = LK.getAsset('bullet', {
width: 290,
height: 30,
anchorX: 0.5,
anchorY: 0,
tint: 0xFF0000,
alpha: 0.9
});
healthBar.y = 5;
healthContainer.addChild(healthBar);
var healthText = new Text2('Health: 100', {
size: 28,
fill: 0xFFFFFF,
font: "'Impact', 'Arial Black', 'Tahoma', sans-serif"
});
healthText.anchor.set(0.5, 0.5);
healthText.y = 20;
healthContainer.addChild(healthText);
healthContainer.y = 10;
LK.gui.top.addChild(healthContainer);
// Ammo counter with styled container
var ammoContainer = new Container();
var ammoBg = LK.getAsset('bullet', {
width: 180,
height: 50,
anchorX: 1,
anchorY: 0,
tint: 0x332200,
alpha: 0.8
});
ammoContainer.addChild(ammoBg);
var ammoBar = LK.getAsset('bullet', {
width: 170,
height: 40,
anchorX: 1,
anchorY: 0,
tint: 0xFFAA00,
alpha: 0.9
});
ammoBar.x = -5;
ammoBar.y = 5;
ammoContainer.addChild(ammoBar);
var ammoText = new Text2('Ammo: 50', {
size: 28,
fill: 0xFFFFFF,
font: "'Impact', 'Arial Black', 'Tahoma', sans-serif"
});
ammoText.anchor.set(0.5, 0.5);
ammoText.x = -90;
ammoText.y = 25;
ammoContainer.addChild(ammoText);
ammoContainer.x = -20;
ammoContainer.y = 20;
LK.gui.topRight.addChild(ammoContainer);
// Wave and difficulty info with styled backgrounds
var waveContainer = new Container();
var waveBg = LK.getAsset('bullet', {
width: 200,
height: 35,
anchorX: 0.5,
anchorY: 0,
tint: 0x003300,
alpha: 0.8
});
waveContainer.addChild(waveBg);
var waveText = new Text2('Wave: 1', {
size: 30,
fill: 0x00FF00,
font: "'Impact', 'Arial Black', 'Tahoma', sans-serif"
});
waveText.anchor.set(0.5, 0.5);
waveText.y = 17;
waveContainer.addChild(waveText);
waveContainer.y = 80;
LK.gui.top.addChild(waveContainer);
var difficultyContainer = new Container();
var difficultyBg = LK.getAsset('bullet', {
width: 220,
height: 35,
anchorX: 0.5,
anchorY: 0,
tint: 0x331100,
alpha: 0.8
});
difficultyContainer.addChild(difficultyBg);
var difficultyText = new Text2('Difficulty: 1', {
size: 28,
fill: 0xffaa00,
font: "'Impact', 'Arial Black', 'Tahoma', sans-serif"
});
difficultyText.anchor.set(0.5, 0.5);
difficultyText.y = 17;
difficultyContainer.addChild(difficultyText);
difficultyContainer.y = 120;
LK.gui.top.addChild(difficultyContainer);
// Rapid fire indicator (initially hidden)
var rapidFireContainer = new Container();
var rapidFireBg = LK.getAsset('bullet', {
width: 250,
height: 30,
anchorX: 0.5,
anchorY: 0,
tint: 0x001133,
alpha: 0.9
});
rapidFireContainer.addChild(rapidFireBg);
var rapidFireBar = LK.getAsset('bullet', {
width: 240,
height: 20,
anchorX: 0.5,
anchorY: 0,
tint: 0x0088FF,
alpha: 1.0
});
rapidFireBar.y = 5;
rapidFireContainer.addChild(rapidFireBar);
var rapidFireText = new Text2('RAPID FIRE', {
size: 24,
fill: 0xFFFFFF,
font: "'Impact', 'Arial Black', 'Tahoma', sans-serif"
});
rapidFireText.anchor.set(0.5, 0.5);
rapidFireText.y = 15;
rapidFireContainer.addChild(rapidFireText);
rapidFireContainer.y = 160;
rapidFireContainer.visible = false;
LK.gui.top.addChild(rapidFireContainer);
// Create single background image scaled to cover expanded game world
var background = game.addChild(LK.getAsset('backgroundTexture', {
anchorX: 0,
anchorY: 0,
scaleX: 65,
// Scale to cover 4096px width (4096/63)
scaleY: 86.8,
// Scale to cover 5464px height (5464/63)
x: 0,
y: 0
}));
// Initialize player at center of expanded world
player = game.addChild(new Player());
player.x = 2048; // Center of 4096px width
player.y = 2732; // Center of 5464px height
// Initialize power-up manager
powerUpManager = new PowerUpManager();
// Camera system variables
var cameraX = 0;
var cameraY = 0;
var cameraLerpSpeed = 0.1;
// Helper function to update camera position
function updateCamera() {
// Calculate target camera position (center player on screen)
var targetCameraX = -(player.x - 1024); // Half screen width (2048/2)
var targetCameraY = -(player.y - 1366); // Half screen height (2732/2)
// Clamp camera to world boundaries
var minCameraX = -(4096 - 2048); // Don't scroll past right edge
var maxCameraX = 0; // Don't scroll past left edge
var minCameraY = -(5464 - 2732); // Don't scroll past bottom edge
var maxCameraY = 0; // Don't scroll past top edge
targetCameraX = Math.max(minCameraX, Math.min(maxCameraX, targetCameraX));
targetCameraY = Math.max(minCameraY, Math.min(maxCameraY, targetCameraY));
// Smooth camera movement
cameraX += (targetCameraX - cameraX) * cameraLerpSpeed;
cameraY += (targetCameraY - cameraY) * cameraLerpSpeed;
// Apply camera position to game container
game.x = cameraX;
game.y = cameraY;
}
// Helper functions
function updateUI() {
scoreText.setText('Score: ' + LK.getScore());
// Update health bar and text with visual feedback
var healthPercent = player.health / player.maxHealth;
healthBar.scaleX = healthPercent;
healthText.setText('Health: ' + player.health);
// Change health bar color based on health level
if (player.health < 30) {
healthBar.tint = 0xFF3333; // Bright red when critical
if (Math.floor(gameTime / 15) % 2 === 0) {
// Pulse effect
healthBar.alpha = 0.6;
} else {
healthBar.alpha = 1.0;
}
} else if (player.health < 60) {
healthBar.tint = 0xFF6600; // Orange when low
healthBar.alpha = 0.9;
} else {
healthBar.tint = 0xFF0000; // Normal red
healthBar.alpha = 0.9;
}
// Update ammo bar and text with visual feedback
var ammoPercent = player.ammo / player.maxAmmo;
ammoBar.scaleX = ammoPercent;
ammoText.setText('Ammo: ' + player.ammo);
// Change ammo bar color and add warning when low
if (player.ammo < 10) {
ammoBar.tint = 0xFF4444; // Red when very low
if (Math.floor(gameTime / 20) % 2 === 0) {
// Pulse effect
ammoBar.alpha = 0.5;
} else {
ammoBar.alpha = 1.0;
}
} else if (player.ammo < 25) {
ammoBar.tint = 0xFFAA00; // Orange when low
ammoBar.alpha = 0.9;
} else {
ammoBar.tint = 0xFFCC00; // Yellow when normal
ammoBar.alpha = 0.9;
}
waveText.setText('Wave: ' + waveNumber);
difficultyText.setText('Difficulty: ' + difficultyLevel);
// Update rapid fire indicator
if (player.rapidFireBonus > 0) {
rapidFireContainer.visible = true;
var rapidFirePercent = player.rapidFireBonus / 300; // 300 is max duration
rapidFireBar.scaleX = rapidFirePercent;
// Pulsing blue effect
if (Math.floor(gameTime / 10) % 2 === 0) {
rapidFireBar.tint = 0x0088FF;
} else {
rapidFireBar.tint = 0x00AAFF;
}
} else {
rapidFireContainer.visible = false;
}
}
function getRandomSpawnPosition() {
var side = Math.floor(Math.random() * 4);
var x, y;
switch (side) {
case 0:
// top
x = Math.random() * 4096;
y = -50;
break;
case 1:
// right
x = 4146;
y = Math.random() * 5464;
break;
case 2:
// bottom
x = Math.random() * 4096;
y = 5514;
break;
case 3:
// left
x = -50;
y = Math.random() * 5464;
break;
}
return {
x: x,
y: y
};
}
function getDifficultyMultiplier() {
var timeSurvived = gameTime / 3600; // minutes survived
return 1 + timeSurvived * 0.2 + (difficultyLevel - 1) * 0.3;
}
function spawnZombie() {
var pos = getRandomSpawnPosition();
var zombie;
var rand = Math.random();
// Difficulty affects zombie type distribution
var difficultyMod = getDifficultyMultiplier();
var walkerChance = Math.max(0.4, 0.6 - difficultyLevel * 0.05);
var runnerChance = walkerChance + Math.min(0.4, 0.25 + difficultyLevel * 0.03);
if (rand < walkerChance) {
zombie = new ZombieWalker();
} else if (rand < runnerChance) {
zombie = new ZombieRunner();
} else {
zombie = new ZombieBrute();
}
// Scale zombie stats based on difficulty
zombie.health = Math.floor(zombie.health * difficultyMod);
zombie.maxHealth = zombie.health;
zombie.damage = Math.floor(zombie.damage * difficultyMod);
zombie.speed = zombie.speed * Math.min(2.0, 1 + (difficultyLevel - 1) * 0.1);
zombie.x = pos.x;
zombie.y = pos.y;
zombies.push(zombie);
game.addChild(zombie);
}
function spawnPowerUp() {
var x = Math.random() * 3600 + 248; // Keep away from edges in expanded world
var y = Math.random() * 4800 + 332;
var powerUp;
var rand = Math.random();
if (rand < 0.4) {
powerUp = new HealthPack();
} else if (rand < 0.7) {
powerUp = new AmmoPack();
} else {
powerUp = new RapidFire();
}
powerUp.x = x;
powerUp.y = y;
powerUps.push(powerUp);
game.addChild(powerUp);
}
function findNearestZombie() {
var nearestZombie = null;
var minDistance = Infinity;
for (var i = 0; i < zombies.length; i++) {
var zombie = zombies[i];
var dx = zombie.x - player.x;
var dy = zombie.y - player.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < minDistance) {
minDistance = distance;
nearestZombie = zombie;
}
}
return nearestZombie;
}
function shootAtZombie() {
if (player.ammo <= 0 || player.shootCooldown > 0) {
return;
}
var target = findNearestZombie();
if (!target) {
return;
}
var bullet = new Bullet();
bullet.x = player.x;
bullet.y = player.y;
bullet.setTarget(target.x, target.y);
bullets.push(bullet);
game.addChild(bullet);
player.ammo--;
player.shootCooldown = player.rapidFireBonus > 0 ? 5 : player.shootRate;
LK.getSound('shoot').play();
}
// Event handlers
game.down = function (x, y, obj) {
var currentTime = Date.now();
// Check for double-click
if (currentTime - lastClickTime < doubleClickDelay) {
// Double-click detected - toggle shooting
isShootingEnabled = !isShootingEnabled;
// Visual feedback - flash player briefly
if (isShootingEnabled) {
LK.effects.flashObject(player, 0x00ff00, 300); // Green flash when enabled
} else {
LK.effects.flashObject(player, 0xff0000, 300); // Red flash when disabled
}
// Reset click time to prevent triple-click issues
lastClickTime = 0;
return;
}
// Update last click time
lastClickTime = currentTime;
// Normal left-click behavior for movement
dragNode = player;
player.x = x;
player.y = y;
};
game.move = function (x, y, obj) {
if (dragNode) {
dragNode.x = x;
dragNode.y = y;
}
};
game.up = function (x, y, obj) {
dragNode = null;
};
// Main game loop
game.update = function () {
gameTime++;
// Increase difficulty every 2 minutes
if (gameTime - lastDifficultyIncrease >= 7200) {
// 2 minutes at 60fps
difficultyLevel++;
lastDifficultyIncrease = gameTime;
// Visual feedback for difficulty increase
LK.effects.flashScreen(0xffaa00, 1000);
}
// Random ammo spawning with 25% chance every 3 seconds
if (gameTime % 180 === 0) {
// Every 3 seconds at 60fps
if (Math.random() < 0.25) {
// 25% chance
var randomX = Math.random() * 3600 + 248; // Keep away from edges in expanded world
var randomY = Math.random() * 4800 + 332;
var randomAmmoPack = new AmmoPack();
randomAmmoPack.x = randomX;
randomAmmoPack.y = randomY;
powerUps.push(randomAmmoPack);
game.addChild(randomAmmoPack);
}
}
// Update spawn rate based on wave and difficulty
var difficultyMod = getDifficultyMultiplier();
var baseSpawnRate = Math.max(90 - waveNumber * 8, 20);
var adjustedSpawnRate = Math.max(15, Math.floor(baseSpawnRate / difficultyMod));
if (gameTime % adjustedSpawnRate === 0) {
spawnZombie();
}
// Spawn additional zombies in later difficulty levels
if (difficultyLevel >= 3 && gameTime % (adjustedSpawnRate * 2) === 0) {
spawnZombie();
}
if (difficultyLevel >= 5 && gameTime % (adjustedSpawnRate * 3) === 0) {
spawnZombie();
}
// Adjust drop rates based on game state
powerUpManager.adjustDropRates();
// Spawn wave-based power-ups occasionally
powerUpTimer++;
if (powerUpTimer >= 2400) {
// Every 40 seconds, spawn from wave table
var centerX = 2048 + (Math.random() - 0.5) * 1600; // Expanded spawn area around new center
var centerY = 2732 + (Math.random() - 0.5) * 1600;
powerUpManager.spawnDrop(centerX, centerY, 'wave');
powerUpTimer = 0;
}
// Try to shoot (only if shooting is enabled)
if (isShootingEnabled) {
shootAtZombie();
}
// Update bullets
for (var i = bullets.length - 1; i >= 0; i--) {
var bullet = bullets[i];
// Check if bullet is off screen in expanded world
if (bullet.x < -50 || bullet.x > 4146 || bullet.y < -50 || bullet.y > 5514) {
bullet.destroy();
bullets.splice(i, 1);
continue;
}
// Check bullet-zombie collisions
var hitZombie = false;
for (var j = zombies.length - 1; j >= 0; j--) {
var zombie = zombies[j];
if (bullet.intersects(zombie)) {
if (zombie.takeDamage(bullet.damage)) {
// Zombie is dead
LK.setScore(LK.getScore() + zombie.scoreValue);
zombiesKilled++;
// Use PowerUpManager for dynamic drops
powerUpManager.spawnDrop(zombie.x, zombie.y, 'zombie');
zombie.destroy();
zombies.splice(j, 1);
LK.getSound('zombieHit').play();
// Check for wave progression
if (zombiesKilled >= waveNumber * 10) {
waveNumber++;
zombiesKilled = 0;
}
}
bullet.destroy();
bullets.splice(i, 1);
hitZombie = true;
break;
}
}
if (hitZombie) continue;
}
// Update zombies and check player collision
for (var i = zombies.length - 1; i >= 0; i--) {
var zombie = zombies[i];
if (zombie.intersects(player)) {
if (player.takeDamage(zombie.damage)) {
// Player died
LK.showGameOver();
return;
}
LK.getSound('damage').play();
LK.effects.flashScreen(0xff0000, 500);
zombie.destroy();
zombies.splice(i, 1);
}
}
// Update power-ups and check collection
for (var i = powerUps.length - 1; i >= 0; i--) {
var powerUp = powerUps[i];
if (powerUp.update()) {
// Power-up expired
powerUp.destroy();
powerUps.splice(i, 1);
continue;
}
if (powerUp.intersects(player)) {
LK.getSound('powerup').play();
switch (powerUp.type) {
case 'health':
player.addHealth(powerUp.value);
break;
case 'ammo':
player.addAmmo(powerUp.value);
break;
case 'rapidfire':
player.rapidFireBonus = powerUp.value;
break;
}
powerUp.destroy();
powerUps.splice(i, 1);
}
}
// Update camera to follow player
updateCamera();
// Update UI
updateUI();
// Check win condition (survive for 10 minutes)
if (gameTime >= 36000) {
// 10 minutes at 60fps
LK.showYouWin();
}
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Bullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 15;
self.damage = 25;
self.targetX = 0;
self.targetY = 0;
self.directionX = 0;
self.directionY = 0;
self.setTarget = function (targetX, targetY) {
var dx = targetX - self.x;
var dy = targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
self.directionX = dx / distance;
self.directionY = dy / distance;
}
};
self.update = function () {
self.x += self.directionX * self.speed;
self.y += self.directionY * self.speed;
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 100;
self.maxHealth = 100;
self.ammo = 50;
self.maxAmmo = 100;
self.shootCooldown = 0;
self.shootRate = 15; // shoots every 15 ticks
self.rapidFireBonus = 0;
self.takeDamage = function (damage) {
self.health -= damage;
if (self.health <= 0) {
self.health = 0;
return true; // player died
}
return false;
};
self.addHealth = function (amount) {
self.health = Math.min(self.health + amount, self.maxHealth);
};
self.addAmmo = function (amount) {
self.ammo = Math.min(self.ammo + amount, self.maxAmmo);
};
self.update = function () {
if (self.shootCooldown > 0) {
self.shootCooldown--;
}
if (self.rapidFireBonus > 0) {
self.rapidFireBonus--;
}
};
return self;
});
var PowerUp = Container.expand(function () {
var self = Container.call(this);
self.type = 'health';
self.lifetime = 600; // 10 seconds at 60fps
self.update = function () {
self.lifetime--;
if (self.lifetime <= 0) {
return true; // should be removed
}
return false;
};
return self;
});
var RapidFire = PowerUp.expand(function () {
var self = PowerUp.call(this);
var packGraphics = self.attachAsset('rapidFire', {
anchorX: 0.5,
anchorY: 0.5
});
self.type = 'rapidfire';
self.value = 300; // 5 seconds of rapid fire
return self;
});
var HealthPack = PowerUp.expand(function () {
var self = PowerUp.call(this);
var packGraphics = self.attachAsset('healthPack', {
anchorX: 0.5,
anchorY: 0.5
});
self.type = 'health';
self.value = 30;
return self;
});
var AmmoPack = PowerUp.expand(function () {
var self = PowerUp.call(this);
var packGraphics = self.attachAsset('ammoPack', {
anchorX: 0.5,
anchorY: 0.5
});
self.type = 'ammo';
self.value = 25;
return self;
});
var PowerUpManager = Container.expand(function () {
var self = Container.call(this);
// Drop table configuration - weights determine relative probability
self.dropTables = {
zombie: [{
type: 'ammo',
weight: 50,
"class": AmmoPack
}, {
type: 'health',
weight: 10,
"class": HealthPack
}, {
type: 'rapidfire',
weight: 5,
"class": RapidFire
}, {
type: 'nothing',
weight: 35,
"class": null
}],
wave: [{
type: 'ammo',
weight: 30,
"class": AmmoPack
}, {
type: 'health',
weight: 20,
"class": HealthPack
}, {
type: 'rapidfire',
weight: 15,
"class": RapidFire
}, {
type: 'nothing',
weight: 35,
"class": null
}]
};
self.getWeightedDrop = function (dropTable) {
var totalWeight = 0;
for (var i = 0; i < dropTable.length; i++) {
totalWeight += dropTable[i].weight;
}
var random = Math.random() * totalWeight;
var currentWeight = 0;
for (var i = 0; i < dropTable.length; i++) {
currentWeight += dropTable[i].weight;
if (random <= currentWeight) {
return dropTable[i];
}
}
return dropTable[dropTable.length - 1]; // fallback
};
self.spawnDrop = function (x, y, tableType) {
var dropConfig = self.getWeightedDrop(self.dropTables[tableType || 'zombie']);
if (dropConfig["class"]) {
var powerUp = new dropConfig["class"]();
powerUp.x = x;
powerUp.y = y;
powerUps.push(powerUp);
game.addChild(powerUp);
return powerUp;
}
return null; // no drop
};
// Modify drop rates dynamically based on game state
self.adjustDropRates = function () {
var survivalTime = gameTime / 3600; // minutes survived
var difficultyMultiplier = 1 + survivalTime * 0.1;
// Adjust drop rates based on difficulty level
if (difficultyLevel === 2) {
// Difficulty level 2: Set specific drop rates
self.dropTables.zombie[2].weight = 10; // rapid fire 10%
self.dropTables.zombie[1].weight = 15; // health pack 15%
self.dropTables.zombie[0].weight = 50; // ammo remains 50%
self.dropTables.zombie[3].weight = 25; // nothing 25% (100 - 10 - 15 - 50)
} else {
// Original dynamic adjustment for other difficulty levels
// Increase health drops as game gets harder
if (player.health < 30) {
self.dropTables.zombie[1].weight = 20; // increase health drop chance
} else {
self.dropTables.zombie[1].weight = 10; // normal health drop chance
}
// Increase ammo drops if low on ammo
if (player.ammo < 10) {
self.dropTables.zombie[0].weight = 70; // increase ammo drop chance
} else {
self.dropTables.zombie[0].weight = 50; // normal ammo drop chance
}
}
};
return self;
});
var Zombie = Container.expand(function () {
var self = Container.call(this);
self.health = 50;
self.maxHealth = 50;
self.speed = 2;
self.damage = 20;
self.scoreValue = 10;
self.type = 'walker';
self.takeDamage = function (damage) {
self.health -= damage;
return self.health <= 0;
};
self.update = function () {
// Move toward 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;
}
};
return self;
});
var ZombieWalker = Zombie.expand(function () {
var self = Zombie.call(this);
var zombieGraphics = self.attachAsset('zombieWalker', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 75;
self.maxHealth = 75;
self.speed = 2;
self.damage = 20;
self.scoreValue = 10;
self.type = 'walker';
return self;
});
var ZombieRunner = Zombie.expand(function () {
var self = Zombie.call(this);
var zombieGraphics = self.attachAsset('zombieRunner', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 30;
self.maxHealth = 30;
self.speed = 4;
self.damage = 15;
self.scoreValue = 15;
self.type = 'runner';
return self;
});
var ZombieBrute = Zombie.expand(function () {
var self = Zombie.call(this);
var zombieGraphics = self.attachAsset('zombieBrute', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 200;
self.maxHealth = 200;
self.speed = 1;
self.damage = 35;
self.scoreValue = 25;
self.type = 'brute';
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({});
/****
* Game Code
****/
// Game variables
var player;
var bullets = [];
var zombies = [];
var powerUps = [];
var powerUpManager;
var waveNumber = 1;
var zombiesKilled = 0;
var gameTime = 0;
var spawnTimer = 0;
var powerUpTimer = 0;
var dragNode = null;
var isShootingEnabled = true; // Toggle for automatic shooting
var difficultyLevel = 1;
var lastDifficultyIncrease = 0;
var lastClickTime = 0;
var doubleClickDelay = 300; // milliseconds for double-click detection
// UI Elements with styled containers and progress bars
// Score display with background container
var scoreContainer = new Container();
var scoreBg = LK.getAsset('bullet', {
width: 200,
height: 50,
anchorX: 0,
anchorY: 0,
tint: 0x000000,
alpha: 0.7
});
scoreContainer.addChild(scoreBg);
var scoreText = new Text2('Score: 0', {
size: 32,
fill: 0xFFFFFF,
font: "'Impact', 'Arial Black', 'Tahoma', sans-serif"
});
scoreText.anchor.set(0, 0.5);
scoreText.x = 10;
scoreText.y = 25;
scoreContainer.addChild(scoreText);
scoreContainer.x = 120;
scoreContainer.y = 20;
LK.gui.topLeft.addChild(scoreContainer);
// Health bar with container and progress indicator
var healthContainer = new Container();
var healthBg = LK.getAsset('bullet', {
width: 300,
height: 40,
anchorX: 0.5,
anchorY: 0,
tint: 0x330000,
alpha: 0.8
});
healthContainer.addChild(healthBg);
var healthBar = LK.getAsset('bullet', {
width: 290,
height: 30,
anchorX: 0.5,
anchorY: 0,
tint: 0xFF0000,
alpha: 0.9
});
healthBar.y = 5;
healthContainer.addChild(healthBar);
var healthText = new Text2('Health: 100', {
size: 28,
fill: 0xFFFFFF,
font: "'Impact', 'Arial Black', 'Tahoma', sans-serif"
});
healthText.anchor.set(0.5, 0.5);
healthText.y = 20;
healthContainer.addChild(healthText);
healthContainer.y = 10;
LK.gui.top.addChild(healthContainer);
// Ammo counter with styled container
var ammoContainer = new Container();
var ammoBg = LK.getAsset('bullet', {
width: 180,
height: 50,
anchorX: 1,
anchorY: 0,
tint: 0x332200,
alpha: 0.8
});
ammoContainer.addChild(ammoBg);
var ammoBar = LK.getAsset('bullet', {
width: 170,
height: 40,
anchorX: 1,
anchorY: 0,
tint: 0xFFAA00,
alpha: 0.9
});
ammoBar.x = -5;
ammoBar.y = 5;
ammoContainer.addChild(ammoBar);
var ammoText = new Text2('Ammo: 50', {
size: 28,
fill: 0xFFFFFF,
font: "'Impact', 'Arial Black', 'Tahoma', sans-serif"
});
ammoText.anchor.set(0.5, 0.5);
ammoText.x = -90;
ammoText.y = 25;
ammoContainer.addChild(ammoText);
ammoContainer.x = -20;
ammoContainer.y = 20;
LK.gui.topRight.addChild(ammoContainer);
// Wave and difficulty info with styled backgrounds
var waveContainer = new Container();
var waveBg = LK.getAsset('bullet', {
width: 200,
height: 35,
anchorX: 0.5,
anchorY: 0,
tint: 0x003300,
alpha: 0.8
});
waveContainer.addChild(waveBg);
var waveText = new Text2('Wave: 1', {
size: 30,
fill: 0x00FF00,
font: "'Impact', 'Arial Black', 'Tahoma', sans-serif"
});
waveText.anchor.set(0.5, 0.5);
waveText.y = 17;
waveContainer.addChild(waveText);
waveContainer.y = 80;
LK.gui.top.addChild(waveContainer);
var difficultyContainer = new Container();
var difficultyBg = LK.getAsset('bullet', {
width: 220,
height: 35,
anchorX: 0.5,
anchorY: 0,
tint: 0x331100,
alpha: 0.8
});
difficultyContainer.addChild(difficultyBg);
var difficultyText = new Text2('Difficulty: 1', {
size: 28,
fill: 0xffaa00,
font: "'Impact', 'Arial Black', 'Tahoma', sans-serif"
});
difficultyText.anchor.set(0.5, 0.5);
difficultyText.y = 17;
difficultyContainer.addChild(difficultyText);
difficultyContainer.y = 120;
LK.gui.top.addChild(difficultyContainer);
// Rapid fire indicator (initially hidden)
var rapidFireContainer = new Container();
var rapidFireBg = LK.getAsset('bullet', {
width: 250,
height: 30,
anchorX: 0.5,
anchorY: 0,
tint: 0x001133,
alpha: 0.9
});
rapidFireContainer.addChild(rapidFireBg);
var rapidFireBar = LK.getAsset('bullet', {
width: 240,
height: 20,
anchorX: 0.5,
anchorY: 0,
tint: 0x0088FF,
alpha: 1.0
});
rapidFireBar.y = 5;
rapidFireContainer.addChild(rapidFireBar);
var rapidFireText = new Text2('RAPID FIRE', {
size: 24,
fill: 0xFFFFFF,
font: "'Impact', 'Arial Black', 'Tahoma', sans-serif"
});
rapidFireText.anchor.set(0.5, 0.5);
rapidFireText.y = 15;
rapidFireContainer.addChild(rapidFireText);
rapidFireContainer.y = 160;
rapidFireContainer.visible = false;
LK.gui.top.addChild(rapidFireContainer);
// Create single background image scaled to cover expanded game world
var background = game.addChild(LK.getAsset('backgroundTexture', {
anchorX: 0,
anchorY: 0,
scaleX: 65,
// Scale to cover 4096px width (4096/63)
scaleY: 86.8,
// Scale to cover 5464px height (5464/63)
x: 0,
y: 0
}));
// Initialize player at center of expanded world
player = game.addChild(new Player());
player.x = 2048; // Center of 4096px width
player.y = 2732; // Center of 5464px height
// Initialize power-up manager
powerUpManager = new PowerUpManager();
// Camera system variables
var cameraX = 0;
var cameraY = 0;
var cameraLerpSpeed = 0.1;
// Helper function to update camera position
function updateCamera() {
// Calculate target camera position (center player on screen)
var targetCameraX = -(player.x - 1024); // Half screen width (2048/2)
var targetCameraY = -(player.y - 1366); // Half screen height (2732/2)
// Clamp camera to world boundaries
var minCameraX = -(4096 - 2048); // Don't scroll past right edge
var maxCameraX = 0; // Don't scroll past left edge
var minCameraY = -(5464 - 2732); // Don't scroll past bottom edge
var maxCameraY = 0; // Don't scroll past top edge
targetCameraX = Math.max(minCameraX, Math.min(maxCameraX, targetCameraX));
targetCameraY = Math.max(minCameraY, Math.min(maxCameraY, targetCameraY));
// Smooth camera movement
cameraX += (targetCameraX - cameraX) * cameraLerpSpeed;
cameraY += (targetCameraY - cameraY) * cameraLerpSpeed;
// Apply camera position to game container
game.x = cameraX;
game.y = cameraY;
}
// Helper functions
function updateUI() {
scoreText.setText('Score: ' + LK.getScore());
// Update health bar and text with visual feedback
var healthPercent = player.health / player.maxHealth;
healthBar.scaleX = healthPercent;
healthText.setText('Health: ' + player.health);
// Change health bar color based on health level
if (player.health < 30) {
healthBar.tint = 0xFF3333; // Bright red when critical
if (Math.floor(gameTime / 15) % 2 === 0) {
// Pulse effect
healthBar.alpha = 0.6;
} else {
healthBar.alpha = 1.0;
}
} else if (player.health < 60) {
healthBar.tint = 0xFF6600; // Orange when low
healthBar.alpha = 0.9;
} else {
healthBar.tint = 0xFF0000; // Normal red
healthBar.alpha = 0.9;
}
// Update ammo bar and text with visual feedback
var ammoPercent = player.ammo / player.maxAmmo;
ammoBar.scaleX = ammoPercent;
ammoText.setText('Ammo: ' + player.ammo);
// Change ammo bar color and add warning when low
if (player.ammo < 10) {
ammoBar.tint = 0xFF4444; // Red when very low
if (Math.floor(gameTime / 20) % 2 === 0) {
// Pulse effect
ammoBar.alpha = 0.5;
} else {
ammoBar.alpha = 1.0;
}
} else if (player.ammo < 25) {
ammoBar.tint = 0xFFAA00; // Orange when low
ammoBar.alpha = 0.9;
} else {
ammoBar.tint = 0xFFCC00; // Yellow when normal
ammoBar.alpha = 0.9;
}
waveText.setText('Wave: ' + waveNumber);
difficultyText.setText('Difficulty: ' + difficultyLevel);
// Update rapid fire indicator
if (player.rapidFireBonus > 0) {
rapidFireContainer.visible = true;
var rapidFirePercent = player.rapidFireBonus / 300; // 300 is max duration
rapidFireBar.scaleX = rapidFirePercent;
// Pulsing blue effect
if (Math.floor(gameTime / 10) % 2 === 0) {
rapidFireBar.tint = 0x0088FF;
} else {
rapidFireBar.tint = 0x00AAFF;
}
} else {
rapidFireContainer.visible = false;
}
}
function getRandomSpawnPosition() {
var side = Math.floor(Math.random() * 4);
var x, y;
switch (side) {
case 0:
// top
x = Math.random() * 4096;
y = -50;
break;
case 1:
// right
x = 4146;
y = Math.random() * 5464;
break;
case 2:
// bottom
x = Math.random() * 4096;
y = 5514;
break;
case 3:
// left
x = -50;
y = Math.random() * 5464;
break;
}
return {
x: x,
y: y
};
}
function getDifficultyMultiplier() {
var timeSurvived = gameTime / 3600; // minutes survived
return 1 + timeSurvived * 0.2 + (difficultyLevel - 1) * 0.3;
}
function spawnZombie() {
var pos = getRandomSpawnPosition();
var zombie;
var rand = Math.random();
// Difficulty affects zombie type distribution
var difficultyMod = getDifficultyMultiplier();
var walkerChance = Math.max(0.4, 0.6 - difficultyLevel * 0.05);
var runnerChance = walkerChance + Math.min(0.4, 0.25 + difficultyLevel * 0.03);
if (rand < walkerChance) {
zombie = new ZombieWalker();
} else if (rand < runnerChance) {
zombie = new ZombieRunner();
} else {
zombie = new ZombieBrute();
}
// Scale zombie stats based on difficulty
zombie.health = Math.floor(zombie.health * difficultyMod);
zombie.maxHealth = zombie.health;
zombie.damage = Math.floor(zombie.damage * difficultyMod);
zombie.speed = zombie.speed * Math.min(2.0, 1 + (difficultyLevel - 1) * 0.1);
zombie.x = pos.x;
zombie.y = pos.y;
zombies.push(zombie);
game.addChild(zombie);
}
function spawnPowerUp() {
var x = Math.random() * 3600 + 248; // Keep away from edges in expanded world
var y = Math.random() * 4800 + 332;
var powerUp;
var rand = Math.random();
if (rand < 0.4) {
powerUp = new HealthPack();
} else if (rand < 0.7) {
powerUp = new AmmoPack();
} else {
powerUp = new RapidFire();
}
powerUp.x = x;
powerUp.y = y;
powerUps.push(powerUp);
game.addChild(powerUp);
}
function findNearestZombie() {
var nearestZombie = null;
var minDistance = Infinity;
for (var i = 0; i < zombies.length; i++) {
var zombie = zombies[i];
var dx = zombie.x - player.x;
var dy = zombie.y - player.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < minDistance) {
minDistance = distance;
nearestZombie = zombie;
}
}
return nearestZombie;
}
function shootAtZombie() {
if (player.ammo <= 0 || player.shootCooldown > 0) {
return;
}
var target = findNearestZombie();
if (!target) {
return;
}
var bullet = new Bullet();
bullet.x = player.x;
bullet.y = player.y;
bullet.setTarget(target.x, target.y);
bullets.push(bullet);
game.addChild(bullet);
player.ammo--;
player.shootCooldown = player.rapidFireBonus > 0 ? 5 : player.shootRate;
LK.getSound('shoot').play();
}
// Event handlers
game.down = function (x, y, obj) {
var currentTime = Date.now();
// Check for double-click
if (currentTime - lastClickTime < doubleClickDelay) {
// Double-click detected - toggle shooting
isShootingEnabled = !isShootingEnabled;
// Visual feedback - flash player briefly
if (isShootingEnabled) {
LK.effects.flashObject(player, 0x00ff00, 300); // Green flash when enabled
} else {
LK.effects.flashObject(player, 0xff0000, 300); // Red flash when disabled
}
// Reset click time to prevent triple-click issues
lastClickTime = 0;
return;
}
// Update last click time
lastClickTime = currentTime;
// Normal left-click behavior for movement
dragNode = player;
player.x = x;
player.y = y;
};
game.move = function (x, y, obj) {
if (dragNode) {
dragNode.x = x;
dragNode.y = y;
}
};
game.up = function (x, y, obj) {
dragNode = null;
};
// Main game loop
game.update = function () {
gameTime++;
// Increase difficulty every 2 minutes
if (gameTime - lastDifficultyIncrease >= 7200) {
// 2 minutes at 60fps
difficultyLevel++;
lastDifficultyIncrease = gameTime;
// Visual feedback for difficulty increase
LK.effects.flashScreen(0xffaa00, 1000);
}
// Random ammo spawning with 25% chance every 3 seconds
if (gameTime % 180 === 0) {
// Every 3 seconds at 60fps
if (Math.random() < 0.25) {
// 25% chance
var randomX = Math.random() * 3600 + 248; // Keep away from edges in expanded world
var randomY = Math.random() * 4800 + 332;
var randomAmmoPack = new AmmoPack();
randomAmmoPack.x = randomX;
randomAmmoPack.y = randomY;
powerUps.push(randomAmmoPack);
game.addChild(randomAmmoPack);
}
}
// Update spawn rate based on wave and difficulty
var difficultyMod = getDifficultyMultiplier();
var baseSpawnRate = Math.max(90 - waveNumber * 8, 20);
var adjustedSpawnRate = Math.max(15, Math.floor(baseSpawnRate / difficultyMod));
if (gameTime % adjustedSpawnRate === 0) {
spawnZombie();
}
// Spawn additional zombies in later difficulty levels
if (difficultyLevel >= 3 && gameTime % (adjustedSpawnRate * 2) === 0) {
spawnZombie();
}
if (difficultyLevel >= 5 && gameTime % (adjustedSpawnRate * 3) === 0) {
spawnZombie();
}
// Adjust drop rates based on game state
powerUpManager.adjustDropRates();
// Spawn wave-based power-ups occasionally
powerUpTimer++;
if (powerUpTimer >= 2400) {
// Every 40 seconds, spawn from wave table
var centerX = 2048 + (Math.random() - 0.5) * 1600; // Expanded spawn area around new center
var centerY = 2732 + (Math.random() - 0.5) * 1600;
powerUpManager.spawnDrop(centerX, centerY, 'wave');
powerUpTimer = 0;
}
// Try to shoot (only if shooting is enabled)
if (isShootingEnabled) {
shootAtZombie();
}
// Update bullets
for (var i = bullets.length - 1; i >= 0; i--) {
var bullet = bullets[i];
// Check if bullet is off screen in expanded world
if (bullet.x < -50 || bullet.x > 4146 || bullet.y < -50 || bullet.y > 5514) {
bullet.destroy();
bullets.splice(i, 1);
continue;
}
// Check bullet-zombie collisions
var hitZombie = false;
for (var j = zombies.length - 1; j >= 0; j--) {
var zombie = zombies[j];
if (bullet.intersects(zombie)) {
if (zombie.takeDamage(bullet.damage)) {
// Zombie is dead
LK.setScore(LK.getScore() + zombie.scoreValue);
zombiesKilled++;
// Use PowerUpManager for dynamic drops
powerUpManager.spawnDrop(zombie.x, zombie.y, 'zombie');
zombie.destroy();
zombies.splice(j, 1);
LK.getSound('zombieHit').play();
// Check for wave progression
if (zombiesKilled >= waveNumber * 10) {
waveNumber++;
zombiesKilled = 0;
}
}
bullet.destroy();
bullets.splice(i, 1);
hitZombie = true;
break;
}
}
if (hitZombie) continue;
}
// Update zombies and check player collision
for (var i = zombies.length - 1; i >= 0; i--) {
var zombie = zombies[i];
if (zombie.intersects(player)) {
if (player.takeDamage(zombie.damage)) {
// Player died
LK.showGameOver();
return;
}
LK.getSound('damage').play();
LK.effects.flashScreen(0xff0000, 500);
zombie.destroy();
zombies.splice(i, 1);
}
}
// Update power-ups and check collection
for (var i = powerUps.length - 1; i >= 0; i--) {
var powerUp = powerUps[i];
if (powerUp.update()) {
// Power-up expired
powerUp.destroy();
powerUps.splice(i, 1);
continue;
}
if (powerUp.intersects(player)) {
LK.getSound('powerup').play();
switch (powerUp.type) {
case 'health':
player.addHealth(powerUp.value);
break;
case 'ammo':
player.addAmmo(powerUp.value);
break;
case 'rapidfire':
player.rapidFireBonus = powerUp.value;
break;
}
powerUp.destroy();
powerUps.splice(i, 1);
}
}
// Update camera to follow player
updateCamera();
// Update UI
updateUI();
// Check win condition (survive for 10 minutes)
if (gameTime >= 36000) {
// 10 minutes at 60fps
LK.showYouWin();
}
};
Ammo pack. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
Bullet. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
Healt pack. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
üstten görünümlü yuvarlak asker. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
rapid fire. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
üsttem görünümlü yuvarlak zombi. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
üsttem görünümlü yuvarlak zombi. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
üsttem görünümlü yuvarlak zombi büyük kolları var tek gözünde göz maskesi var ve elinde bir sopa var. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat