User prompt
"Reduce the zombies' health by half." "Double the number of bullets in a single magazine and reduce the zombie damage by half."
User prompt
"Reduce the speed of the zombies and remove the money system from the game. Zombies should come continuously, and the wave system should also be removed."
User prompt
The score doesn't reset until death, and the coins are collected automatically
User prompt
When a zombie is killed, there is a chance it drops a loot box on the ground. Loot boxes do not open automatically—the player must shoot the box to open it. Once opened, the box releases a random reward, such as: 💊 Medkit: Restores a portion of the player's HP. 🔫 Ammo: Refills part of the magazine or reserve bullets. 💵 Money: Increases the player’s score. All rewards are automatically collected once the box is opened. In addition to box rewards, each zombie kill still grants $50 automatically as score.
Code edit (1 edits merged)
Please save this source code
User prompt
Zombie Siege: Stationary Survival
Initial prompt
Elbette! İşte tüm verdiğin bilgileri içeren, güncel, temiz ve geliştiriciye veya yapay zekaya doğrudan verilebilecek şekilde yazılmış **tek parça İngilizce oyun tasarım promptu**: --- **🧠 Prompt: Side-View Zombie Survival Shooter Game Design** I want to design a **2D side-view zombie survival shooter game** where the player is **stationary behind cover** on the **left side of the screen** and shoots at zombies that approach from the **right side**. The player can only **aim and shoot using the mouse**—there are no movement controls. --- ### 🎮 Core Gameplay * The player is placed behind cover and **cannot move**. * The player aims with the **mouse** and shoots zombies approaching from the right. * **Zombies walk from right to left** toward the player. * The player starts with **100 health (HP)**. * Each zombie attack deals **10 damage** if they reach the player. * The game ends when the player’s HP reaches 0. * The player earns **\$50 per zombie kill**. This money is used purely as a **score system**. * The goal is to **survive as long as possible** and achieve the **highest score**. --- ### 🧟 Zombie System * Zombies spawn in waves and slowly walk toward the player. * **Zombie health starts at 15 HP** and increases by **+5 HP per level**: * Level 1: 15 HP * Level 2: 20 HP * Level 3: 25 HP * etc. * The **number of zombies starts at 5 in Level 1** and **increases by +1 with each level**: * Level 1: 5 zombies * Level 2: 6 zombies * Level 3: 7 zombies * etc. * This scaling system increases difficulty steadily while keeping the 2D side-view manageable. --- ### 🎁 Loot and Reward System * When a zombie is killed, there is a chance for a **loot box** to drop. * Loot boxes may contain: * 💊 **Medkits**: Restore a portion of the player’s HP. * 🔫 **Ammo**: Refill bullets. * 💵 **Money**: Adds to the player’s score. * **Ammo management** is important. Players must reload when the magazine is empty. --- ### 🔫 Weapon Progression System The player starts with a basic weapon and gains new weapons every **5 levels**. Each weapon has different damage and magazine size: 1. **Levels 1–4** * Weapon: Pistol * Damage per bullet: 5 * Magazine: 30 bullets 2. **Levels 5–9** * Weapon: Dual Pistols * Damage: 5 * Magazine: 60 bullets 3. **Levels 10–14** * Weapon: SMG * Damage: 7 * Magazine: 40 bullets 4. **Levels 15–19** * Weapon: Faster SMG (different look) * Damage: 8 * Magazine: 50 bullets 5. **Levels 20–24** * Weapon: Shotgun * Fires: 4 pellets per shot (area damage) * Damage per pellet: 3 * Magazine: 20 shells 6. **Levels 25–29** * Weapon: Advanced Shotgun * Fires: 5 pellets per shot * Damage per pellet: 4 * Magazine: 30 shells 7. **Levels 30–34** * Weapon: M16 Assault Rifle * Damage: 30 per bullet * Magazine: 30 bullets 8. **Levels 35+** * Weapon: AK-47 * Damage: 35 per bullet * Magazine: 30 bullets --- ### 🎯 Summary * **Genre**: 2D Side-View Survival Shooter * **Perspective**: Player is visible on-screen, stationary, on the left side * **Mechanics**: Mouse-only aiming and shooting * **Enemy Behavior**: Zombies walk from right to left * **Progression**: Increasing zombie health and quantity per wave * **Goal**: Stay alive, manage ammo and HP, kill zombies, and achieve the highest score ---
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Bullet class var Bullet = Container.expand(function () { var self = Container.call(this); // Attach bullet asset (yellow box) var bulletSprite = self.attachAsset('bullet', { anchorX: 0.5, anchorY: 0.5 }); self.speed = -32; self.damage = 1; // Defensive: initialize vx/vy to 0 self.vx = 0; self.vy = 0; // Defensive: initialize lastX/lastY for state tracking self.lastX = self.x; self.lastY = self.y; self.update = function () { // Defensive: track lastX/lastY for good practice self.lastX = self.x; self.lastY = self.y; self.x += self.vx; self.y += self.vy; }; return self; }); // LootBox class var LootBox = Container.expand(function () { var self = Container.call(this); // Attach lootbox asset (orange ellipse) var lootSprite = self.attachAsset('lootbox', { anchorX: 0.5, anchorY: 0.5 }); self.type = 'ammo'; // 'ammo', 'medkit', 'money' self.value = 1; self.speed = 2.5; self.update = function () { self.x -= self.speed; }; return self; }); // Player class (stationary) var Player = Container.expand(function () { var self = Container.call(this); // Attach player asset (red box) var playerSprite = self.attachAsset('player', { anchorX: 0.5, anchorY: 0.5 }); // Gun barrel position (relative to player) self.getGunPos = function () { // Gun at right edge, center vertically return { x: self.x + playerSprite.width / 2, y: self.y }; }; return self; }); // Zombie class var Zombie = Container.expand(function (style) { var self = Container.call(this); // Default to style 1 if not provided if (typeof style !== "number" || style < 1 || style > 4) style = 1; var zombieAssetId = "zombie" + style; // Attach zombie asset (varied style) var zombieSprite = self.attachAsset(zombieAssetId, { anchorX: 0.5, anchorY: 1 }); // Set up stats self.maxHealth = 1; self.health = 1; self.speed = 2; self.reward = 10; // money for killing // Health bar var healthBar = self.addChild(LK.getAsset('zombieHealthBar', { anchorX: 0.5, anchorY: 0.5, width: 60, height: 10, color: 0xff0000 })); healthBar.y = -zombieSprite.height + 10; // Update health bar self.updateHealthBar = function () { healthBar.width = 60 * (self.health / self.maxHealth); }; // Called every tick self.update = function () { self.x -= self.speed; // Health bar follows self.updateHealthBar(); }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x222222 }); /**** * Game Code ****/ // Add background image (centered, covers full game area) // --- Asset Initialization --- var background = LK.getAsset('background', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, width: 2048, height: 2732 }); game.addChild(background); // --- Game State --- var player = null; var bullets = []; var zombies = []; var lootboxes = []; var ammo = 10; var maxAmmo = 10; var health = 5; var maxHealth = 5; var level = 1; var zombiesToSpawn = 0; var zombiesSpawned = 0; var zombiesKilled = 0; var spawnTimer = 0; var reloadTimer = 0; var isReloading = false; var weaponIndex = 0; // --- Zombie Speed Scaling --- var zombieBaseSpeedMin = 2.0; var zombieBaseSpeedMax = 2.5; var zombieSpeedIncreasePerMinute = 0.5; // total increase per minute var zombieMaxSpeed = 4.0; // maximum allowed zombie speed var gameStartTick = null; // will be set at game start var weapons = [{ name: "Pistol", damage: 1, ammo: 20, // doubled from 10 reload: 60, bulletSpeed: 18, color: 0xf7e12b }, { name: "SMG", damage: 1, ammo: 40, // doubled from 20 reload: 40, bulletSpeed: 20, color: 0x00bfff }, { name: "Shotgun", damage: 3, ammo: 10, // doubled from 5 reload: 90, bulletSpeed: 14, color: 0xffffff }]; var unlockedWeapons = 1; // Start with 1 weapon // --- UI Elements --- // Weapon name text (top center, above score) var weaponNameTxt = new Text2('', { size: 70, fill: "#fff" }); weaponNameTxt.anchor.set(0.5, 1); weaponNameTxt.x = LK.gui.top.width / 2; weaponNameTxt.y = 0; LK.gui.top.addChild(weaponNameTxt); var scoreTxt = new Text2('Score: 0', { size: 100, fill: "#fff" }); // Place score at true top center scoreTxt.anchor.set(0.5, 0); // Position: top center scoreTxt.x = LK.gui.top.width / 2; scoreTxt.y = 0; LK.gui.top.addChild(scoreTxt); // Animate score color as rainbow using tween plugin var rainbowColors = [0xFF0000, // red 0xFF7F00, // orange 0xFFFF00, // yellow 0x00FF00, // green 0x0000FF, // blue 0x4B0082, // indigo 0x9400D3 // violet ]; var rainbowIndex = 0; function animateScoreColor() { var nextIndex = (rainbowIndex + 1) % rainbowColors.length; var fromColor = rainbowColors[rainbowIndex]; var toColor = rainbowColors[nextIndex]; // Animate over 400ms for smooth transition tween(scoreTxt, { tint: toColor }, { duration: 400, easing: tween.linear, onFinish: function onFinish() { rainbowIndex = nextIndex; animateScoreColor(); } }); } animateScoreColor(); // Money UI removed var ammoTxt = new Text2('Ammo: 10 / 10', { size: 60, fill: "#fff" }); ammoTxt.anchor.set(0, 0); LK.gui.bottomLeft.addChild(ammoTxt); var healthTxt = new Text2('♥♥♥♥♥', { size: 100, fill: 0xFF4D4D }); healthTxt.anchor.set(0, 1); healthTxt.x = 20; healthTxt.y = -10; LK.gui.bottomLeft.addChild(healthTxt); // --- Bullet Count UI --- var bulletCountTxt = new Text2('Bullets: 0', { size: 70, fill: "#fff" }); bulletCountTxt.anchor.set(1, 1); bulletCountTxt.x = LK.gui.bottomRight.width - 20; bulletCountTxt.y = LK.gui.bottomRight.height - 20; LK.gui.bottomRight.addChild(bulletCountTxt); // No rainbow color animation for bullet count text // --- Helper Functions --- function updateUI() { scoreTxt.setText('Score: ' + zombiesKilled); ammoTxt.setText('Ammo: ' + ammo + ' / ' + maxAmmo); var hearts = ''; for (var i = 0; i < health; i++) hearts += '♥'; for (var i = health; i < maxHealth; i++) hearts += '♡'; healthTxt.setText(hearts); // Show weapon name if (weapons && weapons[weaponIndex]) { weaponNameTxt.setText(weapons[weaponIndex].name); } else { weaponNameTxt.setText(''); } // Update bullet count text to show true ammo in reserve bulletCountTxt.setText('Bullets: ' + ammo); } function startLevel(lvl) { // No level or wave logic needed zombiesKilled = 0; spawnTimer = 0; } function spawnZombie() { // Pick a random zombie style (1-4) var zombieStyle = 1 + Math.floor(Math.random() * 4); var z = new Zombie(zombieStyle); // Position at right edge, random y z.x = 2048 + 80; z.y = 400 + Math.floor(Math.random() * (2732 - 800)); // Set fixed health and slow speed for zombies (health halved) if (zombieStyle === 2) { z.maxHealth = 5; // more health for zombie2 z.health = z.maxHealth; } else if (zombieStyle === 3) { z.maxHealth = 0.75; // less health for zombie3 z.health = z.maxHealth; } else if (zombieStyle === 4) { z.maxHealth = 2.25; // 1.5x health for zombie4 z.health = z.maxHealth; } else { z.maxHealth = 1.5; // default health for zombie1 z.health = z.maxHealth; } // --- Calculate elapsed minutes since game start --- var elapsedTicks = gameStartTick !== null ? LK.ticks - gameStartTick : 0; var elapsedMinutes = elapsedTicks / (60 * 60); // 60 ticks per second * 60 seconds // --- Calculate speed increase --- var speedIncrease = zombieSpeedIncreasePerMinute * elapsedMinutes; var minSpeed = zombieBaseSpeedMin + speedIncrease; var maxSpeed = zombieBaseSpeedMax + speedIncrease; // --- Clamp to max speed --- if (minSpeed > zombieMaxSpeed) minSpeed = zombieMaxSpeed; if (maxSpeed > zombieMaxSpeed) maxSpeed = zombieMaxSpeed; // --- Assign speed within range --- z.speed = minSpeed + Math.random() * (maxSpeed - minSpeed); zombies.push(z); game.addChild(z); // Play zombie voice sound on spawn LK.getSound('zombie_voice').play(); } function spawnLootBox(x, y) { var loot = new LootBox(); loot.x = x; loot.y = y; // Randomize type var r = Math.random(); if (r < 0.5) { loot.type = 'ammo'; loot.value = 5 + Math.floor(Math.random() * 5); } else { loot.type = 'medkit'; loot.value = 1; } lootboxes.push(loot); game.addChild(loot); } // --- Game Setup --- player = new Player(); player.x = 180; player.y = 2732 / 2; game.addChild(player); // --- Input Handling --- var lastShotTick = 0; var dragNode = null; // Mouse/touch move: aim gun game.move = function (x, y, obj) { // No drag, just aiming // Optionally, could rotate gun sprite, but for now, just use for shooting }; // Mouse/touch down: shoot game.down = function (x, y, obj) { // Only shoot if not reloading if (isReloading) return; if (ammo <= 0) { // Start reload isReloading = true; reloadTimer = 0; return; } // Fire bullet var gunPos = player.getGunPos(); var b = new Bullet(); b.x = gunPos.x; b.y = gunPos.y; // Defensive: initialize lastX/lastY for state tracking b.lastX = b.x; b.lastY = b.y; // Aim: calculate angle to (x, y) var dx = x - gunPos.x; var dy = y - gunPos.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist === 0) dist = 1; b.speed = weapons[weaponIndex].bulletSpeed; b.damage = weapons[weaponIndex].damage; // Set bullet direction b.vx = b.speed * (dx / dist); b.vy = b.speed * (dy / dist); // No need to overwrite update, Bullet class now uses vx/vy bullets.push(b); game.addChild(b); // Play pistol sound LK.getSound('pistol').play(); ammo--; updateUI(); }; // Mouse/touch up: nothing game.up = function (x, y, obj) {}; // --- Weapon Switching removed (no weapon name or tap to switch) --- // --- Main Game Loop --- game.update = function () { // --- Spawning zombies continuously --- spawnTimer++; if (spawnTimer > 55) { spawnZombie(); spawnTimer = 0; } // --- Bullets update --- for (var i = bullets.length - 1; i >= 0; i--) { var b = bullets[i]; b.update(); // Remove if off screen if (b.x > 2048 + 100 || b.y < -100 || b.y > 2732 + 100) { b.destroy(); bullets.splice(i, 1); continue; } // Check collision with zombies var bulletDestroyed = false; for (var j = zombies.length - 1; j >= 0; j--) { var z = zombies[j]; if (b.intersects(z)) { z.health -= b.damage; if (z.health <= 0) { // Zombie dies zombiesKilled++; // Only spawn a lootbox 30% of the time if (Math.random() < 0.3) { spawnLootBox(z.x, z.y - 40); } z.destroy(); zombies.splice(j, 1); } b.destroy(); bullets.splice(i, 1); bulletDestroyed = true; break; } } if (bulletDestroyed) continue; // Check collision with lootboxes (shoot to open) for (var k = lootboxes.length - 1; k >= 0; k--) { var l = lootboxes[k]; if (b.intersects(l)) { // Open lootbox and grant reward if (l.type === 'ammo') { ammo += l.value; if (ammo > maxAmmo) ammo = maxAmmo; } else if (l.type === 'medkit') { health += l.value; if (health > maxHealth) health = maxHealth; } // Increase score when shooting and opening a lootbox zombiesKilled++; l.destroy(); lootboxes.splice(k, 1); b.destroy(); bullets.splice(i, 1); bulletDestroyed = true; break; } } if (bulletDestroyed) continue; } // --- Zombies update --- for (var i = zombies.length - 1; i >= 0; i--) { var z = zombies[i]; z.update(); // If zombie reaches player if (z.x < player.x + 60) { // Damage player (reduced by half) health -= 0.5; // Play player voice sound when losing health LK.getSound('player_voice').play(); LK.effects.flashObject(player, 0xff0000, 400); z.destroy(); zombies.splice(i, 1); if (health <= 0) { LK.effects.flashScreen(0xff0000, 1000); LK.showGameOver(); return; } } } // --- Lootboxes update --- for (var i = lootboxes.length - 1; i >= 0; i--) { var l = lootboxes[i]; l.update(); // If off screen if (l.x < -100) { l.destroy(); lootboxes.splice(i, 1); continue; } // No automatic pickup; lootboxes must be shot to open } // --- Reloading --- if (isReloading) { reloadTimer++; if (reloadTimer > weapons[weaponIndex].reload) { isReloading = false; ammo = maxAmmo; updateUI(); } } // No level progression or waves updateUI(); }; // --- Start Game --- ammo = weapons[weaponIndex].ammo; maxAmmo = weapons[weaponIndex].ammo; health = maxHealth; zombiesKilled = 0; unlockedWeapons = 1; weaponIndex = 0; gameStartTick = LK.ticks; // Track when the game started for zombie speed scaling LK.playMusic('theme'); startLevel(1); updateUI();
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Bullet class
var Bullet = Container.expand(function () {
var self = Container.call(this);
// Attach bullet asset (yellow box)
var bulletSprite = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = -32;
self.damage = 1;
// Defensive: initialize vx/vy to 0
self.vx = 0;
self.vy = 0;
// Defensive: initialize lastX/lastY for state tracking
self.lastX = self.x;
self.lastY = self.y;
self.update = function () {
// Defensive: track lastX/lastY for good practice
self.lastX = self.x;
self.lastY = self.y;
self.x += self.vx;
self.y += self.vy;
};
return self;
});
// LootBox class
var LootBox = Container.expand(function () {
var self = Container.call(this);
// Attach lootbox asset (orange ellipse)
var lootSprite = self.attachAsset('lootbox', {
anchorX: 0.5,
anchorY: 0.5
});
self.type = 'ammo'; // 'ammo', 'medkit', 'money'
self.value = 1;
self.speed = 2.5;
self.update = function () {
self.x -= self.speed;
};
return self;
});
// Player class (stationary)
var Player = Container.expand(function () {
var self = Container.call(this);
// Attach player asset (red box)
var playerSprite = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
// Gun barrel position (relative to player)
self.getGunPos = function () {
// Gun at right edge, center vertically
return {
x: self.x + playerSprite.width / 2,
y: self.y
};
};
return self;
});
// Zombie class
var Zombie = Container.expand(function (style) {
var self = Container.call(this);
// Default to style 1 if not provided
if (typeof style !== "number" || style < 1 || style > 4) style = 1;
var zombieAssetId = "zombie" + style;
// Attach zombie asset (varied style)
var zombieSprite = self.attachAsset(zombieAssetId, {
anchorX: 0.5,
anchorY: 1
});
// Set up stats
self.maxHealth = 1;
self.health = 1;
self.speed = 2;
self.reward = 10; // money for killing
// Health bar
var healthBar = self.addChild(LK.getAsset('zombieHealthBar', {
anchorX: 0.5,
anchorY: 0.5,
width: 60,
height: 10,
color: 0xff0000
}));
healthBar.y = -zombieSprite.height + 10;
// Update health bar
self.updateHealthBar = function () {
healthBar.width = 60 * (self.health / self.maxHealth);
};
// Called every tick
self.update = function () {
self.x -= self.speed;
// Health bar follows
self.updateHealthBar();
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x222222
});
/****
* Game Code
****/
// Add background image (centered, covers full game area)
// --- Asset Initialization ---
var background = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2,
width: 2048,
height: 2732
});
game.addChild(background);
// --- Game State ---
var player = null;
var bullets = [];
var zombies = [];
var lootboxes = [];
var ammo = 10;
var maxAmmo = 10;
var health = 5;
var maxHealth = 5;
var level = 1;
var zombiesToSpawn = 0;
var zombiesSpawned = 0;
var zombiesKilled = 0;
var spawnTimer = 0;
var reloadTimer = 0;
var isReloading = false;
var weaponIndex = 0;
// --- Zombie Speed Scaling ---
var zombieBaseSpeedMin = 2.0;
var zombieBaseSpeedMax = 2.5;
var zombieSpeedIncreasePerMinute = 0.5; // total increase per minute
var zombieMaxSpeed = 4.0; // maximum allowed zombie speed
var gameStartTick = null; // will be set at game start
var weapons = [{
name: "Pistol",
damage: 1,
ammo: 20,
// doubled from 10
reload: 60,
bulletSpeed: 18,
color: 0xf7e12b
}, {
name: "SMG",
damage: 1,
ammo: 40,
// doubled from 20
reload: 40,
bulletSpeed: 20,
color: 0x00bfff
}, {
name: "Shotgun",
damage: 3,
ammo: 10,
// doubled from 5
reload: 90,
bulletSpeed: 14,
color: 0xffffff
}];
var unlockedWeapons = 1; // Start with 1 weapon
// --- UI Elements ---
// Weapon name text (top center, above score)
var weaponNameTxt = new Text2('', {
size: 70,
fill: "#fff"
});
weaponNameTxt.anchor.set(0.5, 1);
weaponNameTxt.x = LK.gui.top.width / 2;
weaponNameTxt.y = 0;
LK.gui.top.addChild(weaponNameTxt);
var scoreTxt = new Text2('Score: 0', {
size: 100,
fill: "#fff"
});
// Place score at true top center
scoreTxt.anchor.set(0.5, 0);
// Position: top center
scoreTxt.x = LK.gui.top.width / 2;
scoreTxt.y = 0;
LK.gui.top.addChild(scoreTxt);
// Animate score color as rainbow using tween plugin
var rainbowColors = [0xFF0000,
// red
0xFF7F00,
// orange
0xFFFF00,
// yellow
0x00FF00,
// green
0x0000FF,
// blue
0x4B0082,
// indigo
0x9400D3 // violet
];
var rainbowIndex = 0;
function animateScoreColor() {
var nextIndex = (rainbowIndex + 1) % rainbowColors.length;
var fromColor = rainbowColors[rainbowIndex];
var toColor = rainbowColors[nextIndex];
// Animate over 400ms for smooth transition
tween(scoreTxt, {
tint: toColor
}, {
duration: 400,
easing: tween.linear,
onFinish: function onFinish() {
rainbowIndex = nextIndex;
animateScoreColor();
}
});
}
animateScoreColor();
// Money UI removed
var ammoTxt = new Text2('Ammo: 10 / 10', {
size: 60,
fill: "#fff"
});
ammoTxt.anchor.set(0, 0);
LK.gui.bottomLeft.addChild(ammoTxt);
var healthTxt = new Text2('♥♥♥♥♥', {
size: 100,
fill: 0xFF4D4D
});
healthTxt.anchor.set(0, 1);
healthTxt.x = 20;
healthTxt.y = -10;
LK.gui.bottomLeft.addChild(healthTxt);
// --- Bullet Count UI ---
var bulletCountTxt = new Text2('Bullets: 0', {
size: 70,
fill: "#fff"
});
bulletCountTxt.anchor.set(1, 1);
bulletCountTxt.x = LK.gui.bottomRight.width - 20;
bulletCountTxt.y = LK.gui.bottomRight.height - 20;
LK.gui.bottomRight.addChild(bulletCountTxt);
// No rainbow color animation for bullet count text
// --- Helper Functions ---
function updateUI() {
scoreTxt.setText('Score: ' + zombiesKilled);
ammoTxt.setText('Ammo: ' + ammo + ' / ' + maxAmmo);
var hearts = '';
for (var i = 0; i < health; i++) hearts += '♥';
for (var i = health; i < maxHealth; i++) hearts += '♡';
healthTxt.setText(hearts);
// Show weapon name
if (weapons && weapons[weaponIndex]) {
weaponNameTxt.setText(weapons[weaponIndex].name);
} else {
weaponNameTxt.setText('');
}
// Update bullet count text to show true ammo in reserve
bulletCountTxt.setText('Bullets: ' + ammo);
}
function startLevel(lvl) {
// No level or wave logic needed
zombiesKilled = 0;
spawnTimer = 0;
}
function spawnZombie() {
// Pick a random zombie style (1-4)
var zombieStyle = 1 + Math.floor(Math.random() * 4);
var z = new Zombie(zombieStyle);
// Position at right edge, random y
z.x = 2048 + 80;
z.y = 400 + Math.floor(Math.random() * (2732 - 800));
// Set fixed health and slow speed for zombies (health halved)
if (zombieStyle === 2) {
z.maxHealth = 5; // more health for zombie2
z.health = z.maxHealth;
} else if (zombieStyle === 3) {
z.maxHealth = 0.75; // less health for zombie3
z.health = z.maxHealth;
} else if (zombieStyle === 4) {
z.maxHealth = 2.25; // 1.5x health for zombie4
z.health = z.maxHealth;
} else {
z.maxHealth = 1.5; // default health for zombie1
z.health = z.maxHealth;
}
// --- Calculate elapsed minutes since game start ---
var elapsedTicks = gameStartTick !== null ? LK.ticks - gameStartTick : 0;
var elapsedMinutes = elapsedTicks / (60 * 60); // 60 ticks per second * 60 seconds
// --- Calculate speed increase ---
var speedIncrease = zombieSpeedIncreasePerMinute * elapsedMinutes;
var minSpeed = zombieBaseSpeedMin + speedIncrease;
var maxSpeed = zombieBaseSpeedMax + speedIncrease;
// --- Clamp to max speed ---
if (minSpeed > zombieMaxSpeed) minSpeed = zombieMaxSpeed;
if (maxSpeed > zombieMaxSpeed) maxSpeed = zombieMaxSpeed;
// --- Assign speed within range ---
z.speed = minSpeed + Math.random() * (maxSpeed - minSpeed);
zombies.push(z);
game.addChild(z);
// Play zombie voice sound on spawn
LK.getSound('zombie_voice').play();
}
function spawnLootBox(x, y) {
var loot = new LootBox();
loot.x = x;
loot.y = y;
// Randomize type
var r = Math.random();
if (r < 0.5) {
loot.type = 'ammo';
loot.value = 5 + Math.floor(Math.random() * 5);
} else {
loot.type = 'medkit';
loot.value = 1;
}
lootboxes.push(loot);
game.addChild(loot);
}
// --- Game Setup ---
player = new Player();
player.x = 180;
player.y = 2732 / 2;
game.addChild(player);
// --- Input Handling ---
var lastShotTick = 0;
var dragNode = null;
// Mouse/touch move: aim gun
game.move = function (x, y, obj) {
// No drag, just aiming
// Optionally, could rotate gun sprite, but for now, just use for shooting
};
// Mouse/touch down: shoot
game.down = function (x, y, obj) {
// Only shoot if not reloading
if (isReloading) return;
if (ammo <= 0) {
// Start reload
isReloading = true;
reloadTimer = 0;
return;
}
// Fire bullet
var gunPos = player.getGunPos();
var b = new Bullet();
b.x = gunPos.x;
b.y = gunPos.y;
// Defensive: initialize lastX/lastY for state tracking
b.lastX = b.x;
b.lastY = b.y;
// Aim: calculate angle to (x, y)
var dx = x - gunPos.x;
var dy = y - gunPos.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist === 0) dist = 1;
b.speed = weapons[weaponIndex].bulletSpeed;
b.damage = weapons[weaponIndex].damage;
// Set bullet direction
b.vx = b.speed * (dx / dist);
b.vy = b.speed * (dy / dist);
// No need to overwrite update, Bullet class now uses vx/vy
bullets.push(b);
game.addChild(b);
// Play pistol sound
LK.getSound('pistol').play();
ammo--;
updateUI();
};
// Mouse/touch up: nothing
game.up = function (x, y, obj) {};
// --- Weapon Switching removed (no weapon name or tap to switch) ---
// --- Main Game Loop ---
game.update = function () {
// --- Spawning zombies continuously ---
spawnTimer++;
if (spawnTimer > 55) {
spawnZombie();
spawnTimer = 0;
}
// --- Bullets update ---
for (var i = bullets.length - 1; i >= 0; i--) {
var b = bullets[i];
b.update();
// Remove if off screen
if (b.x > 2048 + 100 || b.y < -100 || b.y > 2732 + 100) {
b.destroy();
bullets.splice(i, 1);
continue;
}
// Check collision with zombies
var bulletDestroyed = false;
for (var j = zombies.length - 1; j >= 0; j--) {
var z = zombies[j];
if (b.intersects(z)) {
z.health -= b.damage;
if (z.health <= 0) {
// Zombie dies
zombiesKilled++;
// Only spawn a lootbox 30% of the time
if (Math.random() < 0.3) {
spawnLootBox(z.x, z.y - 40);
}
z.destroy();
zombies.splice(j, 1);
}
b.destroy();
bullets.splice(i, 1);
bulletDestroyed = true;
break;
}
}
if (bulletDestroyed) continue;
// Check collision with lootboxes (shoot to open)
for (var k = lootboxes.length - 1; k >= 0; k--) {
var l = lootboxes[k];
if (b.intersects(l)) {
// Open lootbox and grant reward
if (l.type === 'ammo') {
ammo += l.value;
if (ammo > maxAmmo) ammo = maxAmmo;
} else if (l.type === 'medkit') {
health += l.value;
if (health > maxHealth) health = maxHealth;
}
// Increase score when shooting and opening a lootbox
zombiesKilled++;
l.destroy();
lootboxes.splice(k, 1);
b.destroy();
bullets.splice(i, 1);
bulletDestroyed = true;
break;
}
}
if (bulletDestroyed) continue;
}
// --- Zombies update ---
for (var i = zombies.length - 1; i >= 0; i--) {
var z = zombies[i];
z.update();
// If zombie reaches player
if (z.x < player.x + 60) {
// Damage player (reduced by half)
health -= 0.5;
// Play player voice sound when losing health
LK.getSound('player_voice').play();
LK.effects.flashObject(player, 0xff0000, 400);
z.destroy();
zombies.splice(i, 1);
if (health <= 0) {
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
return;
}
}
}
// --- Lootboxes update ---
for (var i = lootboxes.length - 1; i >= 0; i--) {
var l = lootboxes[i];
l.update();
// If off screen
if (l.x < -100) {
l.destroy();
lootboxes.splice(i, 1);
continue;
}
// No automatic pickup; lootboxes must be shot to open
}
// --- Reloading ---
if (isReloading) {
reloadTimer++;
if (reloadTimer > weapons[weaponIndex].reload) {
isReloading = false;
ammo = maxAmmo;
updateUI();
}
}
// No level progression or waves
updateUI();
};
// --- Start Game ---
ammo = weapons[weaponIndex].ammo;
maxAmmo = weapons[weaponIndex].ammo;
health = maxHealth;
zombiesKilled = 0;
unlockedWeapons = 1;
weaponIndex = 0;
gameStartTick = LK.ticks; // Track when the game started for zombie speed scaling
LK.playMusic('theme');
startLevel(1);
updateUI();