/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ var ArmoredVehicle = Container.expand(function () { var self = Container.call(this); // Main vehicle body using tank zombie as base var vehicleBody = self.attachAsset('tankZombie', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.2, scaleY: 0.8 }); // Change tint to military green vehicleBody.tint = 0x4a5d23; // Add armor plating details using wall assets var frontArmor = self.attachAsset('wall', { anchorX: 0.5, anchorY: 0.5, x: 80, y: 0, scaleX: 0.3, scaleY: 0.8 }); frontArmor.tint = 0x2d3016; var sideArmor1 = self.attachAsset('wall', { anchorX: 0.5, anchorY: 0.5, x: 0, y: -40, scaleX: 1.0, scaleY: 0.2 }); sideArmor1.tint = 0x2d3016; var sideArmor2 = self.attachAsset('wall', { anchorX: 0.5, anchorY: 0.5, x: 0, y: 40, scaleX: 1.0, scaleY: 0.2 }); sideArmor2.tint = 0x2d3016; // Add weapon turret using rifle var turret = self.attachAsset('rifle', { anchorX: 0.2, anchorY: 0.5, x: 0, y: -20, scaleX: 1.5, scaleY: 1.5 }); turret.tint = 0x2d3016; return self; }); var Bullet = Container.expand(function (startX, startY, targetX, targetY, damage) { var self = Container.call(this); var bulletGraphics = self.attachAsset('bullet', { anchorX: 0.5, anchorY: 0.5 }); self.damage = damage; self.speed = 8; // Calculate direction var dx = targetX - startX; var dy = targetY - startY; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 0) { self.dirX = dx / distance; self.dirY = dy / distance; } else { self.dirX = 1; self.dirY = 0; } // Set rotation to match direction bulletGraphics.rotation = Math.atan2(dy, dx); self.update = function () { self.x += self.dirX * self.speed; self.y += self.dirY * self.speed; }; return self; }); var Chest = Container.expand(function () { var self = Container.call(this); // Use wall asset to create chest appearance var chestBody = self.attachAsset('wall', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.8, scaleY: 0.6 }); chestBody.tint = 0x8B4513; // Brown color for chest // Add chest lid using another wall asset var chestLid = self.attachAsset('wall', { anchorX: 0.5, anchorY: 0.5, y: -20, scaleX: 0.9, scaleY: 0.3 }); chestLid.tint = 0x654321; // Darker brown for lid self.opened = false; self.cost = 3; // Costs 3 bullets to open self.openChest = function () { if (self.opened) return false; // Check if player has enough bullets if (currentMagazineBullets + totalBullets < self.cost) { return false; // Not enough bullets } // Deduct bullets (prioritize magazine bullets first) var bulletsToDeduct = self.cost; if (currentMagazineBullets >= bulletsToDeduct) { currentMagazineBullets -= bulletsToDeduct; } else { bulletsToDeduct -= currentMagazineBullets; currentMagazineBullets = 0; totalBullets -= bulletsToDeduct; } self.opened = true; // Visual feedback - change chest appearance chestLid.y = -30; // Lift the lid chestBody.tint = 0x555555; // Darken the chest // Generate random item var chestDropTypes = ['rifle', 'pistol', 'rifle', 'pistol', 'ammo', 'healthPack', 'food', 'energyDrink', 'helmet', 'helmet2', 'helmet3', 'armor', 'backpack', 'kebab', 'm249']; var dropType = chestDropTypes[Math.floor(Math.random() * chestDropTypes.length)]; // Create and spawn the item var drop = new Item(dropType); drop.x = self.x; drop.y = self.y + 60; // Spawn below the chest items.push(drop); game.addChild(drop); // Play pickup sound for feedback LK.getSound('pickup').play(); return true; }; // Handle interaction self.down = function (x, y, obj) { self.openChest(); }; return self; }); var Item = Container.expand(function (itemType) { var self = Container.call(this); var itemGraphics = self.attachAsset(itemType, { anchorX: 0.5, anchorY: 0.5 }); self.type = itemType; self.collected = false; self.collect = function () { if (self.collected) return false; var success = false; switch (self.type) { case 'helmet': case 'helmet2': case 'helmet3': case 'armor': case 'backpack': player.equipArmor(self.type); success = true; break; case 'healthPack': player.heal(player.maxHealth); success = true; break; case 'food': player.heal(20); success = true; break; case 'kebab': player.heal(20); success = true; break; case 'energyDrink': // Store original values for restoration var originalSpeed = player.speed; var originalShootCooldown = player.shootCooldown; // Apply speed boost player.speed = player.baseSpeed * 1.5; // Apply fire rate boost (lower cooldown = faster shooting) player.shootCooldown = Math.max(1, player.shootCooldown * 0.6); // Use tween to smoothly fade back to normal after 10 seconds tween(player, { speed: originalSpeed }, { duration: 10000, easing: tween.easeOut }); // Restore shoot cooldown after 10 seconds LK.setTimeout(function () { player.shootCooldown = originalShootCooldown; }, 10000); success = true; break; case 'rifle': case 'pistol': case 'm249': player.equipWeapon(self.type); // Add bullets when picking up weapons if (self.type === 'pistol') { totalBullets += 45; // Pistol comes with 45 bullets (1.5 magazines) } else if (self.type === 'shotgun') { totalBullets += 80; // Shotgun comes with 80 shells (10 magazines of 8) } else if (self.type === 'rifle') { totalBullets += 60; // Rifle comes with 60 bullets (2 magazines) } else if (self.type === 'm249') { totalBullets += 300; // M249 comes with 300 bullets (2 magazines of 150) } success = true; break; case 'ammo': totalBullets += 30; // Add one magazine worth of bullets LK.setScore(LK.getScore() + 5); success = true; break; } if (success) { self.collected = true; LK.getSound('pickup').play(); return true; } return false; }; return self; }); var Player = Container.expand(function () { var self = Container.call(this); var playerGraphics = self.attachAsset('player', { anchorX: 0.5, anchorY: 0.5 }); // Player stats self.maxHealth = 100; self.health = self.maxHealth; self.speed = 3; self.baseSpeed = 3; self.weapon = 'knife'; self.weaponDamage = 25; self.attackCooldown = 0; self.shootCooldown = 0; self.isRangedWeapon = false; self.armor = 0; self.inventorySize = 3; self.inventory = []; // Equipment visuals self.weaponSprite = null; self.helmetSprite = null; self.armorSprite = null; self.backpackSprite = null; self.equipWeapon = function (weaponType) { if (self.weaponSprite) { self.removeChild(self.weaponSprite); } self.weapon = weaponType; self.weaponSprite = self.attachAsset(weaponType, { anchorX: 0.5, anchorY: 0.5, x: 35, y: 0 }); switch (weaponType) { case 'rifle': self.weaponDamage = 60; self.isRangedWeapon = true; break; case 'pistol': self.weaponDamage = 40; self.isRangedWeapon = true; break; case 'm249': self.weaponDamage = 25; self.isRangedWeapon = true; break; } }; self.equipArmor = function (armorType) { if ((armorType === 'helmet' || armorType === 'helmet2' || armorType === 'helmet3') && !self.helmetSprite) { var helmetAsset = armorType; self.helmetSprite = self.attachAsset(helmetAsset, { anchorX: 0.5, anchorY: 0.5, x: 0, y: -35 }); // Different armor values for different helmet levels if (armorType === 'helmet') { self.armor += 20; // Level 1 helmet gives 20 armor } else if (armorType === 'helmet2') { self.armor += 30; // Level 2 helmet gives 30 armor } else if (armorType === 'helmet3') { self.armor += 50; // Level 3 helmet gives 50 armor } } else if (armorType === 'armor' && !self.armorSprite) { self.armorSprite = self.attachAsset('armor', { anchorX: 0.5, anchorY: 0.5, x: 0, y: 10 }); self.armor += 50; // Increase armor by 50 } else if (armorType === 'backpack' && !self.backpackSprite) { self.backpackSprite = self.attachAsset('backpack', { anchorX: 0.5, anchorY: 0.5, x: 0, y: 40 }); self.inventorySize += 3; self.armor += 20; // Give 20 armor points maxMagazineCapacity += 200; // Give 200 extra magazine slots } }; self.takeDamage = function (damage) { // First use armor to absorb damage if (self.armor > 0) { var armorAbsorbed = Math.min(damage, self.armor); self.armor -= armorAbsorbed; damage -= armorAbsorbed; } // Any remaining damage goes to health if (damage > 0) { self.health -= damage; } LK.effects.flashObject(self, 0xff0000, 200); if (self.health <= 0) { LK.showGameOver(); } }; self.heal = function (amount) { self.health = Math.min(self.maxHealth, self.health + amount); }; self.update = function () { if (self.attackCooldown > 0) { self.attackCooldown--; } if (self.shootCooldown > 0) { self.shootCooldown--; } }; // Start with pistol self.equipWeapon('pistol'); return self; }); var Shop = Container.expand(function () { var self = Container.call(this); self.isOpen = false; self.shopUI = null; self.items = [{ name: 'Energy Drink', price: 50, type: 'energyDrink' }, { name: '30 Bullets', price: 50, type: 'ammo' }, { name: 'Health Pack', price: 100, type: 'healthPack' }, { name: 'Sandwich', price: 20, type: 'food' }, { name: 'Kebab', price: 25, type: 'kebab' }, { name: 'Pistol', price: 200, type: 'pistol' }, { name: 'M249', price: 500, type: 'm249' }]; self.openShop = function () { if (self.isOpen) return; self.isOpen = true; // Create shop background var shopBg = LK.getAsset('wall', { anchorX: 0.5, anchorY: 0.5, scaleX: 8, scaleY: 12 }); shopBg.tint = 0x333333; shopBg.alpha = 0.9; shopBg.x = 1024; shopBg.y = 1366; game.addChild(shopBg); // Create shop title var titleText = new Text2('MILITARY SHOP', { size: 60, fill: 0xFFFFFF }); titleText.anchor.set(0.5, 0.5); titleText.x = 1024; titleText.y = 800; game.addChild(titleText); // Create score display var scoreDisplay = new Text2('Your Score: ' + LK.getScore(), { size: 40, fill: 0xFFD700 }); scoreDisplay.anchor.set(0.5, 0.5); scoreDisplay.x = 1024; scoreDisplay.y = 900; game.addChild(scoreDisplay); // Create item buttons var itemButtons = []; for (var i = 0; i < self.items.length; i++) { var item = self.items[i]; var yPos = 1000 + i * 150; // Item button background var buttonBg = LK.getAsset('wall', { anchorX: 0.5, anchorY: 0.5, scaleX: 6, scaleY: 1.2 }); buttonBg.tint = LK.getScore() >= item.price ? 0x4a5d23 : 0x8b4513; buttonBg.x = 1024; buttonBg.y = yPos; game.addChild(buttonBg); // Item text var itemText = new Text2(item.name + ' - ' + item.price + ' points', { size: 35, fill: LK.getScore() >= item.price ? 0xFFFFFF : 0x888888 }); itemText.anchor.set(0.5, 0.5); itemText.x = 1024; itemText.y = yPos; game.addChild(itemText); // Store references for interaction buttonBg.itemIndex = i; buttonBg.down = function (x, y, obj) { self.buyItem(obj.itemIndex); }; itemButtons.push({ bg: buttonBg, text: itemText }); } // Close button var closeButton = LK.getAsset('wall', { anchorX: 0.5, anchorY: 0.5, scaleX: 3, scaleY: 1 }); closeButton.tint = 0xff0000; closeButton.x = 1024; closeButton.y = 1800; game.addChild(closeButton); var closeText = new Text2('CLOSE', { size: 40, fill: 0xFFFFFF }); closeText.anchor.set(0.5, 0.5); closeText.x = 1024; closeText.y = 1800; game.addChild(closeText); closeButton.down = function () { self.closeShop(); }; // Store UI elements for cleanup self.shopUI = { elements: [shopBg, titleText, scoreDisplay, closeButton, closeText].concat(itemButtons.map(function (btn) { return btn.bg; }), itemButtons.map(function (btn) { return btn.text; })) }; }; self.buyItem = function (itemIndex) { // Validate itemIndex to prevent undefined access if (itemIndex < 0 || itemIndex >= self.items.length) { return; // Exit early if invalid index } var item = self.items[itemIndex]; // Additional safety check to ensure item exists if (!item || typeof item.price === 'undefined') { return; // Exit early if item is invalid } var currentScore = LK.getScore(); if (currentScore >= item.price) { // Deduct score LK.setScore(currentScore - item.price); // Apply item effect switch (item.type) { case 'energyDrink': // Store original values for restoration var originalSpeed = player.speed; var originalShootCooldown = player.shootCooldown; // Apply speed boost player.speed = player.baseSpeed * 1.5; // Apply fire rate boost player.shootCooldown = Math.max(1, player.shootCooldown * 0.6); // Use tween to smoothly fade back to normal after 10 seconds tween(player, { speed: originalSpeed }, { duration: 10000, easing: tween.easeOut }); // Restore shoot cooldown after 10 seconds LK.setTimeout(function () { player.shootCooldown = originalShootCooldown; }, 10000); break; case 'ammo': totalBullets += 30; break; case 'healthPack': player.heal(player.maxHealth); break; case 'food': player.heal(20); break; case 'kebab': player.heal(20); break; case 'pistol': player.equipWeapon('pistol'); totalBullets += 45; // Pistol comes with 45 bullets break; case 'm249': player.equipWeapon('m249'); totalBullets += 300; // M249 comes with 300 bullets (2 magazines of 150) break; } LK.getSound('pickup').play(); self.closeShop(); self.openShop(); // Refresh shop display } }; self.closeShop = function () { if (!self.isOpen) return; self.isOpen = false; // Clean up UI elements if (self.shopUI) { for (var i = 0; i < self.shopUI.elements.length; i++) { if (self.shopUI.elements[i] && self.shopUI.elements[i].destroy) { self.shopUI.elements[i].destroy(); } } self.shopUI = null; } }; return self; }); var Soldier = Container.expand(function () { var self = Container.call(this); var soldierGraphics = self.attachAsset('player', { anchorX: 0.5, anchorY: 0.5 }); // Equip soldier with rifle var rifleSprite = self.attachAsset('rifle', { anchorX: 0.5, anchorY: 0.5, x: 35, y: 0 }); // Equip soldier with helmet var helmetSprite = self.attachAsset('helmet', { anchorX: 0.5, anchorY: 0.5, x: 0, y: -35 }); // Equip soldier with armor var armorSprite = self.attachAsset('armor', { anchorX: 0.5, anchorY: 0.5, x: 0, y: 10 }); return self; }); var Zombie = Container.expand(function (type) { var self = Container.call(this); var zombieType = type || 'zombie'; var zombieGraphics = self.attachAsset(zombieType, { anchorX: 0.5, anchorY: 0.5, scaleX: 0.5, // Reduce width by 50% scaleY: 0.5 // Reduce height by 50% }); // Zombie stats based on type - all zombies now require exactly 5 bullets (300 health) switch (zombieType) { case 'zombie': self.health = 300; // 5 bullets in all sections if (currentSection === 3) { self.speed = 1.0; // Much faster in section 3 self.damage = 30; // High damage in section 3 } else if (currentSection === 2) { self.speed = 0.7; // Faster in section 2 self.damage = 20; // More damage in section 2 } else { self.speed = 0.5; // Base speed self.damage = 15; // Base damage } self.points = 10; break; case 'fastZombie': self.health = 300; // 5 bullets in all sections if (currentSection === 3) { self.speed = 2.8; // Extremely fast in section 3 self.damage = 18; // High damage in section 3 } else if (currentSection === 2) { self.speed = 2.0; // Much faster in section 2 self.damage = 12; // More damage in section 2 } else { self.speed = 1.5; // Base fast speed self.damage = 8; // Base damage } self.points = 15; break; case 'tankZombie': self.health = 1200; // 20 bullets (1200/60 damage per bullet) if (currentSection === 3) { self.speed = 0.8; // Faster tank in section 3 self.damage = 40; // Massive damage in section 3 } else if (currentSection === 2) { self.speed = 0.6; // Faster in section 2 self.damage = 25; // Much more damage in section 2 } else { self.speed = 0.4; // Base tank speed self.damage = 15; // Base tank damage } self.points = 25; break; } self.maxHealth = self.health; self.type = zombieType; self.attackCooldown = 0; self.takeDamage = function (damage) { self.health -= damage; LK.effects.flashObject(self, 0xff0000, 150); if (self.health <= 0) { return true; // Dead } return false; }; self.update = function () { if (self.attackCooldown > 0) { self.attackCooldown--; } // Always aggressively pursue player position var dx = player.x - self.x; var dy = player.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 0) { // Increase pursuit speed based on distance - zombies move faster when player is farther var pursuitMultiplier = Math.min(2, distance / 200); var actualSpeed = self.speed * (1 + pursuitMultiplier); self.x += dx / distance * actualSpeed; self.y += dy / distance * actualSpeed; } // Attack player if close enough - adjusted for much bigger zombies var attackRange = zombieType === 'tankZombie' ? 110 : zombieType === 'zombie' ? 90 : 80; if (distance < attackRange && self.attackCooldown <= 0) { player.takeDamage(self.damage); self.attackCooldown = 60; // 1 second at 60fps LK.getSound('hit').play(); } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x2c2c2c }); /**** * Game Code ****/ // Sounds // Mall elements // Items // Zombies // Player and weapons // Game variables var shop = new Shop(); var player; var zombies = []; var bullets = []; var items = []; var chests = []; var walls = []; var zombieSpawnTimer = 0; var zombieSpawnRate = 180; // 3 seconds at 60fps var difficultyTimer = 0; var gameTime = 0; // Level progression variables var currentLevel = 1; var maxLevels = 5; var normalZombiesKilled = 0; var fastZombiesKilled = 0; var tankZombiesKilled = 0; var normalZombiesToKill = 15; var fastZombiesToKill = 5; var tankZombiesToKill = 1; var currentSection = 1; var maxSections = 3; // Added section 3 // Magazine system var totalBullets = 120; // Added 30 extra bullets (90 + 30) var bulletsPerMagazine = 15; var maxMagazineCapacity = 500; // Maximum magazine capacity var currentMagazineBullets = bulletsPerMagazine; function getBulletsPerMagazine() { var baseCapacity; switch (player.weapon) { case 'rifle': baseCapacity = 30; break; case 'pistol': baseCapacity = 15; break; case 'm249': baseCapacity = 150; break; default: baseCapacity = 30; break; } return Math.min(baseCapacity, maxMagazineCapacity); } var isReloading = false; var reloadTimer = 0; var reloadTime = 60; // 1 second at 60fps // UI Elements var healthText = new Text2('Health: 100', { size: 40, fill: 0xFF0000 // Change health text color to red }); healthText.anchor.set(0, 0); LK.gui.topLeft.addChild(healthText); healthText.x = 120; // Avoid menu icon var scoreText = new Text2('Score: 0', { size: 40, fill: 0xFFFFFF }); scoreText.anchor.set(0.5, 0); LK.gui.top.addChild(scoreText); var weaponText = new Text2('Weapon: Knife', { size: 35, fill: 0xFFFF00 }); weaponText.anchor.set(1, 0); LK.gui.topRight.addChild(weaponText); var ammoText = new Text2('Ammo: 30/90', { size: 35, fill: 0x00FFFF }); ammoText.anchor.set(1, 0); ammoText.y = 50; LK.gui.topRight.addChild(ammoText); // Create player player = game.addChild(new Player()); player.x = 1024; player.y = 1366; // Create mall layout function createMallLayout() { // Create walls around the perimeter for (var x = 0; x < 2048; x += 100) { var topWall = game.addChild(LK.getAsset('wall', { anchorX: 0.5, anchorY: 0.5 })); topWall.x = x + 50; topWall.y = 50; walls.push(topWall); var bottomWall = game.addChild(LK.getAsset('wall', { anchorX: 0.5, anchorY: 0.5 })); bottomWall.x = x + 50; bottomWall.y = 2682; walls.push(bottomWall); } for (var y = 100; y < 2632; y += 100) { var leftWall = game.addChild(LK.getAsset('wall', { anchorX: 0.5, anchorY: 0.5 })); leftWall.x = 50; leftWall.y = y + 50; walls.push(leftWall); var rightWall = game.addChild(LK.getAsset('wall', { anchorX: 0.5, anchorY: 0.5 })); rightWall.x = 1998; rightWall.y = y + 50; walls.push(rightWall); } } function spawnChest() { // Spawn chest at random location in playable area, avoiding walls var chest = game.addChild(new Chest()); // Random position in playable area (avoiding walls and edges) chest.x = Math.random() * 1700 + 200; // 200 to 1900 chest.y = Math.random() * 2200 + 300; // 300 to 2500 chests.push(chest); } function spawnZombie() { var zombieTypes, zombieType; // Different spawn patterns for each section if (currentSection === 3) { // Section 3: Extremely aggressive spawning with high chance of dangerous zombies zombieTypes = ['zombie', 'fastZombie', 'fastZombie', 'tankZombie', 'tankZombie']; zombieType = zombieTypes[Math.floor(Math.random() * zombieTypes.length)]; // In section 3, frequently spawn multiple zombies at once if (Math.random() < 0.6) { // 60% chance to spawn extra zombie var extraZombie = game.addChild(new Zombie('fastZombie')); // Spawn extra zombie at different edge var extraSide = Math.floor(Math.random() * 4); switch (extraSide) { case 0: extraZombie.x = Math.random() * 1948 + 100; extraZombie.y = 150; break; case 1: extraZombie.x = 1898; extraZombie.y = Math.random() * 2432 + 150; break; case 2: extraZombie.x = Math.random() * 1948 + 100; extraZombie.y = 2582; break; case 3: extraZombie.x = 150; extraZombie.y = Math.random() * 2432 + 150; break; } zombies.push(extraZombie); // 25% chance to spawn a third zombie in section 3 if (Math.random() < 0.25) { var thirdZombie = game.addChild(new Zombie('zombie')); var thirdSide = Math.floor(Math.random() * 4); switch (thirdSide) { case 0: thirdZombie.x = Math.random() * 1948 + 100; thirdZombie.y = 150; break; case 1: thirdZombie.x = 1898; thirdZombie.y = Math.random() * 2432 + 150; break; case 2: thirdZombie.x = Math.random() * 1948 + 100; thirdZombie.y = 2582; break; case 3: thirdZombie.x = 150; thirdZombie.y = Math.random() * 2432 + 150; break; } zombies.push(thirdZombie); } } } else if (currentSection === 2) { // Section 2: More aggressive spawning with higher chance of dangerous zombies zombieTypes = ['zombie', 'zombie', 'fastZombie', 'fastZombie', 'tankZombie']; zombieType = zombieTypes[Math.floor(Math.random() * zombieTypes.length)]; // In section 2, occasionally spawn multiple zombies at once if (Math.random() < 0.3) { // 30% chance to spawn extra zombie var extraZombie = game.addChild(new Zombie('zombie')); // Spawn extra zombie at different edge var extraSide = Math.floor(Math.random() * 4); switch (extraSide) { case 0: extraZombie.x = Math.random() * 1948 + 100; extraZombie.y = 150; break; case 1: extraZombie.x = 1898; extraZombie.y = Math.random() * 2432 + 150; break; case 2: extraZombie.x = Math.random() * 1948 + 100; extraZombie.y = 2582; break; case 3: extraZombie.x = 150; extraZombie.y = Math.random() * 2432 + 150; break; } zombies.push(extraZombie); } } else { // Section 1: Original spawn pattern zombieTypes = ['zombie', 'zombie', 'zombie', 'fastZombie', 'tankZombie']; zombieType = zombieTypes[Math.floor(Math.random() * zombieTypes.length)]; // Make tank zombies rarer early on if (zombieType === 'tankZombie' && gameTime < 1800) { zombieType = 'zombie'; } } var zombie = game.addChild(new Zombie(zombieType)); // Spawn at random edge var side = Math.floor(Math.random() * 4); switch (side) { case 0: // Top zombie.x = Math.random() * 1948 + 100; zombie.y = 150; break; case 1: // Right zombie.x = 1898; zombie.y = Math.random() * 2432 + 150; break; case 2: // Bottom zombie.x = Math.random() * 1948 + 100; zombie.y = 2582; break; case 3: // Left zombie.x = 150; zombie.y = Math.random() * 2432 + 150; break; } zombies.push(zombie); } function checkCollisions() { // Player vs items for (var i = items.length - 1; i >= 0; i--) { var item = items[i]; if (player.intersects(item) && item.collect()) { item.destroy(); items.splice(i, 1); } } // Bullet vs zombie collisions for (var b = bullets.length - 1; b >= 0; b--) { var bullet = bullets[b]; var bulletHit = false; // Check if bullet is out of bounds if (bullet.x < 0 || bullet.x > 2048 || bullet.y < 0 || bullet.y > 2732) { bullet.destroy(); bullets.splice(b, 1); continue; } // Check bullet vs zombies for (var z = zombies.length - 1; z >= 0; z--) { var zombie = zombies[z]; if (bullet.intersects(zombie)) { if (zombie.takeDamage(bullet.damage)) { // Zombie died LK.setScore(LK.getScore() + zombie.points); LK.getSound('zombieHit').play(); // Random drop chance if (Math.random() < 0.50) { var dropTypes = ['rifle', 'pistol', 'rifle', 'pistol', 'ammo', 'healthPack', 'food', 'energyDrink', 'helmet', 'helmet2', 'helmet3', 'kebab', 'm249']; var dropType = dropTypes[Math.floor(Math.random() * dropTypes.length)]; var drop = new Item(dropType); drop.x = zombie.x; drop.y = zombie.y; items.push(drop); game.addChild(drop); } // Track zombie kills for level progression switch (zombie.type) { case 'zombie': normalZombiesKilled++; break; case 'fastZombie': fastZombiesKilled++; break; case 'tankZombie': tankZombiesKilled++; break; } zombie.destroy(); zombies.splice(z, 1); } bullet.destroy(); bullets.splice(b, 1); bulletHit = true; break; } } if (bulletHit) continue; } // Player melee attacks zombies (only for knife) if (!player.isRangedWeapon && player.attackCooldown <= 0) { for (var z = zombies.length - 1; z >= 0; z--) { var zombie = zombies[z]; var distance = Math.sqrt((player.x - zombie.x) * (player.x - zombie.x) + (player.y - zombie.y) * (player.y - zombie.y)); var meleeRange = zombie.type === 'tankZombie' ? 120 : zombie.type === 'zombie' ? 100 : 90; if (distance < meleeRange) { if (zombie.takeDamage(player.weaponDamage)) { // Zombie died LK.setScore(LK.getScore() + zombie.points); LK.getSound('zombieHit').play(); // Random drop chance if (Math.random() < 0.50) { var dropTypes = ['rifle', 'pistol', 'rifle', 'pistol', 'ammo', 'healthPack', 'food', 'energyDrink', 'helmet', 'helmet2', 'helmet3', 'kebab', 'm249']; var dropType = dropTypes[Math.floor(Math.random() * dropTypes.length)]; var drop = new Item(dropType); drop.x = zombie.x; drop.y = zombie.y; items.push(drop); game.addChild(drop); } zombie.destroy(); zombies.splice(z, 1); } player.attackCooldown = 30; // Half second break; } } } } function updateUI() { // Create health and armor text with different colors using tween to animate color changes var healthValue = Math.max(0, Math.floor(player.health)); var armorValue = player.armor; // Use tween to smoothly transition health text color based on health level if (healthValue < 30) { // Critical health - flash red tween(healthText, { tint: 0xFF0000 }, { duration: 500, easing: tween.easeInOut }); } else if (healthValue < 60) { // Low health - orange tween(healthText, { tint: 0xFF8800 }, { duration: 300, easing: tween.easeOut }); } else { // Good health - red tween(healthText, { tint: 0xFF0000 }, { duration: 200, easing: tween.easeOut }); } healthText.setText('Health: ' + healthValue); // Create separate armor display with cyan color if (!game.armorText) { game.armorText = new Text2('Armor: ' + armorValue, { size: 40, fill: 0x00FFFF // Cyan color for armor }); game.armorText.anchor.set(0, 0); game.armorText.x = 120; // Same x as health text game.armorText.y = 50; // Below health text LK.gui.topLeft.addChild(game.armorText); } else { game.armorText.setText('Armor: ' + armorValue); // Use tween to animate armor text color based on armor level if (armorValue > 50) { tween(game.armorText, { tint: 0x00FFFF }, { duration: 200, easing: tween.easeOut }); // Bright cyan } else if (armorValue > 20) { tween(game.armorText, { tint: 0x0088AA }, { duration: 300, easing: tween.easeOut }); // Darker cyan } else { tween(game.armorText, { tint: 0x004466 }, { duration: 500, easing: tween.easeOut }); // Very dark cyan } } scoreText.setText('Score: ' + LK.getScore()); weaponText.setText('Weapon: ' + player.weapon.charAt(0).toUpperCase() + player.weapon.slice(1)); if (isReloading) { ammoText.setText('Reloading... ' + Math.ceil(reloadTimer / 60) + 's'); } else { ammoText.setText('Ammo: ' + currentMagazineBullets + '/' + totalBullets); } } // Input handling var targetX = player.x; var targetY = player.y; var isPressed = false; var keys = { w: false, a: false, s: false, d: false }; function tryShoot(x, y) { if (player.isRangedWeapon && player.shootCooldown <= 0 && currentMagazineBullets > 0 && !isReloading) { // Regular single bullet for rifle, pistol, and m249 - all bullets do 60 damage var bullet = new Bullet(player.x, player.y, x, y, 60); bullet.x = player.x; bullet.y = player.y; bullets.push(bullet); game.addChild(bullet); LK.getSound('shoot').play(); currentMagazineBullets--; // Set cooldown based on weapon type switch (player.weapon) { case 'rifle': player.shootCooldown = 15; // 0.25 seconds break; case 'pistol': player.shootCooldown = 15; // 0.25 seconds - faster firing break; case 'm249': player.shootCooldown = 9; // 7 bullets per second (60fps / 7 ≈ 8.6 frames, rounded to 9) break; } } } function tryReload() { var weaponMagazineSize = getBulletsPerMagazine(); if (isReloading || currentMagazineBullets >= weaponMagazineSize || totalBullets <= 0) { return; } var bulletsNeeded = weaponMagazineSize - currentMagazineBullets; var bulletsToReload = Math.min(bulletsNeeded, totalBullets); if (bulletsToReload > 0) { isReloading = true; // Set reload time based on weapon type switch (player.weapon) { case 'm249': reloadTimer = 300; // 5 seconds at 60fps break; case 'pistol': reloadTimer = 60; // 1 second at 60fps break; default: reloadTimer = reloadTime; // Default reload time break; } } } game.down = function (x, y, obj) { targetX = x; targetY = y; isPressed = true; tryShoot(x, y); }; game.move = function (x, y, obj) { targetX = x; targetY = y; // Rotate weapon to follow cursor if (player.weaponSprite) { var dx = x - player.x; var dy = y - player.y; player.weaponSprite.rotation = Math.atan2(dy, dx); } if (isPressed) { tryShoot(x, y); } }; game.up = function (x, y, obj) { isPressed = false; }; // Note: LK engine sandbox doesn't support document API // WASD movement will be handled through touch/mouse simulation // Touch controls for mobile compatibility var touchStartX = 0; var touchStartY = 0; var isTouching = false; // Initialize mall createMallLayout(); // Spawn initial chests for section 1 (max 2) for (var i = 0; i < 2; i++) { spawnChest(); } // Add armor charging station var armorCharger = new Item('armor'); armorCharger.x = 500; armorCharger.y = 500; items.push(armorCharger); game.addChild(armorCharger); game.update = function () { gameTime++; // Move player toward cursor position continuously var moveX = 0; var moveY = 0; var dx = targetX - player.x; var dy = targetY - player.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 10) { // Only move if not very close to target var dirX = dx / distance; var dirY = dy / distance; moveX = dirX * player.speed; moveY = dirY * player.speed; } player.x += moveX; player.y += moveY; // Continuous shooting when pressed if (isPressed) { tryShoot(targetX, targetY); } // Keep player in bounds player.x = Math.max(100, Math.min(1948, player.x)); player.y = Math.max(150, Math.min(2582, player.y)); // Spawn zombies zombieSpawnTimer++; if (zombieSpawnTimer >= zombieSpawnRate) { spawnZombie(); zombieSpawnTimer = 0; } // Spawn chests randomly in each section if (Math.random() < 0.001 && chests.length < 2) { // Low chance, max 2 chests per section spawnChest(); } // Increase difficulty over time difficultyTimer++; if (difficultyTimer >= 1800) { // Every 30 seconds if (currentSection === 3) { // Section 3: Extremely aggressive difficulty scaling zombieSpawnRate = Math.max(25, zombieSpawnRate - 20); } else if (currentSection === 2) { // Section 2: More aggressive difficulty scaling zombieSpawnRate = Math.max(45, zombieSpawnRate - 15); } else { // Section 1: Original difficulty scaling zombieSpawnRate = Math.max(60, zombieSpawnRate - 10); } difficultyTimer = 0; } // Handle reload system if (isReloading) { reloadTimer--; if (reloadTimer <= 0) { var weaponMagazineSize = getBulletsPerMagazine(); var bulletsNeeded = weaponMagazineSize - currentMagazineBullets; var bulletsToReload = Math.min(bulletsNeeded, totalBullets); currentMagazineBullets += bulletsToReload; totalBullets -= bulletsToReload; isReloading = false; } } else if (currentMagazineBullets <= 0 && totalBullets > 0) { // Auto reload when magazine is empty tryReload(); } // Check level completion - victory when all required zombies are killed if (normalZombiesKilled >= normalZombiesToKill && fastZombiesKilled >= fastZombiesToKill && tankZombiesKilled >= tankZombiesToKill) { // Stop spawning zombies by setting spawn rate to impossible value zombieSpawnRate = 999999; // Clear any remaining zombies on screen for (var z = zombies.length - 1; z >= 0; z--) { zombies[z].destroy(); zombies.splice(z, 1); } // Display appropriate completion message based on section var messageText; if (currentSection === 1) { messageText = 'Section 1 Complete!'; } else if (currentSection === 2) { messageText = 'Section 2 Complete!'; } else { messageText = 'All Sections Complete! You Survived!'; } var victoryText = new Text2(messageText, { size: currentSection === 3 ? 70 : 80, fill: currentSection === 3 ? 0xFFD700 : 0x00FF00 }); victoryText.anchor.set(0.5, 0.5); victoryText.x = 1024; victoryText.y = 1366; game.addChild(victoryText); // After 3 seconds, show soldiers and armored car LK.setTimeout(function () { victoryText.destroy(); // Create soldier 1 var soldier1 = game.addChild(new Soldier()); soldier1.x = 300; soldier1.y = 1366; // Create soldier 2 var soldier2 = game.addChild(new Soldier()); soldier2.x = 1748; soldier2.y = 1366; // Create armored military vehicle var armoredVehicle = game.addChild(new ArmoredVehicle()); armoredVehicle.x = 1024; armoredVehicle.y = 200; // Clear all items on the ground when tank vehicle shop becomes available for (var i = items.length - 1; i >= 0; i--) { items[i].destroy(); items.splice(i, 1); } // Add shop interaction to vehicle armoredVehicle.down = function (x, y, obj) { shop.openShop(); }; // Add soldier interaction for progression soldier1.down = function (x, y, obj) { if (currentSection === 1) { // Update to section 2 currentSection = 2; // Reset kill counters for section 2 normalZombiesKilled = 0; fastZombiesKilled = 0; tankZombiesKilled = 0; // Update requirements for section 2 normalZombiesToKill = 20; fastZombiesToKill = 7; tankZombiesToKill = 2; // Reset spawn rate and restart zombie spawning - faster spawning for section 2 zombieSpawnRate = 120; // Faster spawning (2 seconds instead of 3) zombieSpawnTimer = 0; // Hide soldiers and armored vehicle when episode two starts soldier1.destroy(); soldier2.destroy(); armoredVehicle.destroy(); shopInstructionText.destroy(); finalText.destroy(); // Display section 2 start message var sectionText = new Text2('Section 2 - Contact established!', { size: 60, fill: 0x00FF00 }); sectionText.anchor.set(0.5, 0.5); sectionText.x = 1024; sectionText.y = 1366; game.addChild(sectionText); // Remove message after 3 seconds LK.setTimeout(function () { sectionText.destroy(); }, 3000); } else if (currentSection === 2) { // Update to section 3 currentSection = 3; // Reset kill counters for section 3 normalZombiesKilled = 0; fastZombiesKilled = 0; tankZombiesKilled = 0; // Update requirements for section 3 (much harder) normalZombiesToKill = 35; fastZombiesToKill = 15; tankZombiesToKill = 5; // Reset spawn rate and restart zombie spawning - very fast spawning for section 3 zombieSpawnRate = 80; // Very fast spawning (1.33 seconds) zombieSpawnTimer = 0; // Display section 3 start message var sectionText = new Text2('Final Section 3 - Survive the Onslaught!', { size: 50, fill: 0xFF0000 // Red text for danger }); sectionText.anchor.set(0.5, 0.5); sectionText.x = 1024; sectionText.y = 1366; game.addChild(sectionText); // Remove message after 4 seconds LK.setTimeout(function () { sectionText.destroy(); }, 4000); } // Victory elements already cleared when section 2 started // Clear all chests for new section for (var c = chests.length - 1; c >= 0; c--) { chests[c].destroy(); chests.splice(c, 1); } // Clear all items on the ground for new section for (var i = items.length - 1; i >= 0; i--) { items[i].destroy(); items.splice(i, 1); } }; soldier2.down = function (x, y, obj) { // Same interaction as soldier1 soldier1.down(x, y, obj); }; // Add instruction text var shopInstructionText = new Text2('Click vehicle to open shop', { size: 40, fill: 0xFFFFFF }); shopInstructionText.anchor.set(0.5, 0.5); shopInstructionText.x = 1024; shopInstructionText.y = 400; game.addChild(shopInstructionText); // Show final victory message only after section 3 if (currentSection === 3) { var finalText = new Text2('You are rescued!', { size: 100, fill: 0xFFD700 }); finalText.anchor.set(0.5, 0.5); finalText.x = 1024; finalText.y = 1000; game.addChild(finalText); // Show you win after soldiers arrive LK.setTimeout(function () { LK.showYouWin(); }, 3000); } else { var finalText = new Text2('Contact the soldier to continue!', { size: 60, fill: 0xFFFFFF }); finalText.anchor.set(0.5, 0.5); finalText.x = 1024; finalText.y = 1000; game.addChild(finalText); } }, 3000); } // Update game objects player.update(); for (var z = 0; z < zombies.length; z++) { zombies[z].update(); } for (var b = 0; b < bullets.length; b++) { bullets[b].update(); } // Check collisions checkCollisions(); // Update UI updateUI(); };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var ArmoredVehicle = Container.expand(function () {
var self = Container.call(this);
// Main vehicle body using tank zombie as base
var vehicleBody = self.attachAsset('tankZombie', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 0.8
});
// Change tint to military green
vehicleBody.tint = 0x4a5d23;
// Add armor plating details using wall assets
var frontArmor = self.attachAsset('wall', {
anchorX: 0.5,
anchorY: 0.5,
x: 80,
y: 0,
scaleX: 0.3,
scaleY: 0.8
});
frontArmor.tint = 0x2d3016;
var sideArmor1 = self.attachAsset('wall', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: -40,
scaleX: 1.0,
scaleY: 0.2
});
sideArmor1.tint = 0x2d3016;
var sideArmor2 = self.attachAsset('wall', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 40,
scaleX: 1.0,
scaleY: 0.2
});
sideArmor2.tint = 0x2d3016;
// Add weapon turret using rifle
var turret = self.attachAsset('rifle', {
anchorX: 0.2,
anchorY: 0.5,
x: 0,
y: -20,
scaleX: 1.5,
scaleY: 1.5
});
turret.tint = 0x2d3016;
return self;
});
var Bullet = Container.expand(function (startX, startY, targetX, targetY, damage) {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.damage = damage;
self.speed = 8;
// Calculate direction
var dx = targetX - startX;
var dy = targetY - startY;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
self.dirX = dx / distance;
self.dirY = dy / distance;
} else {
self.dirX = 1;
self.dirY = 0;
}
// Set rotation to match direction
bulletGraphics.rotation = Math.atan2(dy, dx);
self.update = function () {
self.x += self.dirX * self.speed;
self.y += self.dirY * self.speed;
};
return self;
});
var Chest = Container.expand(function () {
var self = Container.call(this);
// Use wall asset to create chest appearance
var chestBody = self.attachAsset('wall', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.6
});
chestBody.tint = 0x8B4513; // Brown color for chest
// Add chest lid using another wall asset
var chestLid = self.attachAsset('wall', {
anchorX: 0.5,
anchorY: 0.5,
y: -20,
scaleX: 0.9,
scaleY: 0.3
});
chestLid.tint = 0x654321; // Darker brown for lid
self.opened = false;
self.cost = 3; // Costs 3 bullets to open
self.openChest = function () {
if (self.opened) return false;
// Check if player has enough bullets
if (currentMagazineBullets + totalBullets < self.cost) {
return false; // Not enough bullets
}
// Deduct bullets (prioritize magazine bullets first)
var bulletsToDeduct = self.cost;
if (currentMagazineBullets >= bulletsToDeduct) {
currentMagazineBullets -= bulletsToDeduct;
} else {
bulletsToDeduct -= currentMagazineBullets;
currentMagazineBullets = 0;
totalBullets -= bulletsToDeduct;
}
self.opened = true;
// Visual feedback - change chest appearance
chestLid.y = -30; // Lift the lid
chestBody.tint = 0x555555; // Darken the chest
// Generate random item
var chestDropTypes = ['rifle', 'pistol', 'rifle', 'pistol', 'ammo', 'healthPack', 'food', 'energyDrink', 'helmet', 'helmet2', 'helmet3', 'armor', 'backpack', 'kebab', 'm249'];
var dropType = chestDropTypes[Math.floor(Math.random() * chestDropTypes.length)];
// Create and spawn the item
var drop = new Item(dropType);
drop.x = self.x;
drop.y = self.y + 60; // Spawn below the chest
items.push(drop);
game.addChild(drop);
// Play pickup sound for feedback
LK.getSound('pickup').play();
return true;
};
// Handle interaction
self.down = function (x, y, obj) {
self.openChest();
};
return self;
});
var Item = Container.expand(function (itemType) {
var self = Container.call(this);
var itemGraphics = self.attachAsset(itemType, {
anchorX: 0.5,
anchorY: 0.5
});
self.type = itemType;
self.collected = false;
self.collect = function () {
if (self.collected) return false;
var success = false;
switch (self.type) {
case 'helmet':
case 'helmet2':
case 'helmet3':
case 'armor':
case 'backpack':
player.equipArmor(self.type);
success = true;
break;
case 'healthPack':
player.heal(player.maxHealth);
success = true;
break;
case 'food':
player.heal(20);
success = true;
break;
case 'kebab':
player.heal(20);
success = true;
break;
case 'energyDrink':
// Store original values for restoration
var originalSpeed = player.speed;
var originalShootCooldown = player.shootCooldown;
// Apply speed boost
player.speed = player.baseSpeed * 1.5;
// Apply fire rate boost (lower cooldown = faster shooting)
player.shootCooldown = Math.max(1, player.shootCooldown * 0.6);
// Use tween to smoothly fade back to normal after 10 seconds
tween(player, {
speed: originalSpeed
}, {
duration: 10000,
easing: tween.easeOut
});
// Restore shoot cooldown after 10 seconds
LK.setTimeout(function () {
player.shootCooldown = originalShootCooldown;
}, 10000);
success = true;
break;
case 'rifle':
case 'pistol':
case 'm249':
player.equipWeapon(self.type);
// Add bullets when picking up weapons
if (self.type === 'pistol') {
totalBullets += 45; // Pistol comes with 45 bullets (1.5 magazines)
} else if (self.type === 'shotgun') {
totalBullets += 80; // Shotgun comes with 80 shells (10 magazines of 8)
} else if (self.type === 'rifle') {
totalBullets += 60; // Rifle comes with 60 bullets (2 magazines)
} else if (self.type === 'm249') {
totalBullets += 300; // M249 comes with 300 bullets (2 magazines of 150)
}
success = true;
break;
case 'ammo':
totalBullets += 30; // Add one magazine worth of bullets
LK.setScore(LK.getScore() + 5);
success = true;
break;
}
if (success) {
self.collected = true;
LK.getSound('pickup').play();
return true;
}
return false;
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
// Player stats
self.maxHealth = 100;
self.health = self.maxHealth;
self.speed = 3;
self.baseSpeed = 3;
self.weapon = 'knife';
self.weaponDamage = 25;
self.attackCooldown = 0;
self.shootCooldown = 0;
self.isRangedWeapon = false;
self.armor = 0;
self.inventorySize = 3;
self.inventory = [];
// Equipment visuals
self.weaponSprite = null;
self.helmetSprite = null;
self.armorSprite = null;
self.backpackSprite = null;
self.equipWeapon = function (weaponType) {
if (self.weaponSprite) {
self.removeChild(self.weaponSprite);
}
self.weapon = weaponType;
self.weaponSprite = self.attachAsset(weaponType, {
anchorX: 0.5,
anchorY: 0.5,
x: 35,
y: 0
});
switch (weaponType) {
case 'rifle':
self.weaponDamage = 60;
self.isRangedWeapon = true;
break;
case 'pistol':
self.weaponDamage = 40;
self.isRangedWeapon = true;
break;
case 'm249':
self.weaponDamage = 25;
self.isRangedWeapon = true;
break;
}
};
self.equipArmor = function (armorType) {
if ((armorType === 'helmet' || armorType === 'helmet2' || armorType === 'helmet3') && !self.helmetSprite) {
var helmetAsset = armorType;
self.helmetSprite = self.attachAsset(helmetAsset, {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: -35
});
// Different armor values for different helmet levels
if (armorType === 'helmet') {
self.armor += 20; // Level 1 helmet gives 20 armor
} else if (armorType === 'helmet2') {
self.armor += 30; // Level 2 helmet gives 30 armor
} else if (armorType === 'helmet3') {
self.armor += 50; // Level 3 helmet gives 50 armor
}
} else if (armorType === 'armor' && !self.armorSprite) {
self.armorSprite = self.attachAsset('armor', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 10
});
self.armor += 50; // Increase armor by 50
} else if (armorType === 'backpack' && !self.backpackSprite) {
self.backpackSprite = self.attachAsset('backpack', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 40
});
self.inventorySize += 3;
self.armor += 20; // Give 20 armor points
maxMagazineCapacity += 200; // Give 200 extra magazine slots
}
};
self.takeDamage = function (damage) {
// First use armor to absorb damage
if (self.armor > 0) {
var armorAbsorbed = Math.min(damage, self.armor);
self.armor -= armorAbsorbed;
damage -= armorAbsorbed;
}
// Any remaining damage goes to health
if (damage > 0) {
self.health -= damage;
}
LK.effects.flashObject(self, 0xff0000, 200);
if (self.health <= 0) {
LK.showGameOver();
}
};
self.heal = function (amount) {
self.health = Math.min(self.maxHealth, self.health + amount);
};
self.update = function () {
if (self.attackCooldown > 0) {
self.attackCooldown--;
}
if (self.shootCooldown > 0) {
self.shootCooldown--;
}
};
// Start with pistol
self.equipWeapon('pistol');
return self;
});
var Shop = Container.expand(function () {
var self = Container.call(this);
self.isOpen = false;
self.shopUI = null;
self.items = [{
name: 'Energy Drink',
price: 50,
type: 'energyDrink'
}, {
name: '30 Bullets',
price: 50,
type: 'ammo'
}, {
name: 'Health Pack',
price: 100,
type: 'healthPack'
}, {
name: 'Sandwich',
price: 20,
type: 'food'
}, {
name: 'Kebab',
price: 25,
type: 'kebab'
}, {
name: 'Pistol',
price: 200,
type: 'pistol'
}, {
name: 'M249',
price: 500,
type: 'm249'
}];
self.openShop = function () {
if (self.isOpen) return;
self.isOpen = true;
// Create shop background
var shopBg = LK.getAsset('wall', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 8,
scaleY: 12
});
shopBg.tint = 0x333333;
shopBg.alpha = 0.9;
shopBg.x = 1024;
shopBg.y = 1366;
game.addChild(shopBg);
// Create shop title
var titleText = new Text2('MILITARY SHOP', {
size: 60,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 800;
game.addChild(titleText);
// Create score display
var scoreDisplay = new Text2('Your Score: ' + LK.getScore(), {
size: 40,
fill: 0xFFD700
});
scoreDisplay.anchor.set(0.5, 0.5);
scoreDisplay.x = 1024;
scoreDisplay.y = 900;
game.addChild(scoreDisplay);
// Create item buttons
var itemButtons = [];
for (var i = 0; i < self.items.length; i++) {
var item = self.items[i];
var yPos = 1000 + i * 150;
// Item button background
var buttonBg = LK.getAsset('wall', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 6,
scaleY: 1.2
});
buttonBg.tint = LK.getScore() >= item.price ? 0x4a5d23 : 0x8b4513;
buttonBg.x = 1024;
buttonBg.y = yPos;
game.addChild(buttonBg);
// Item text
var itemText = new Text2(item.name + ' - ' + item.price + ' points', {
size: 35,
fill: LK.getScore() >= item.price ? 0xFFFFFF : 0x888888
});
itemText.anchor.set(0.5, 0.5);
itemText.x = 1024;
itemText.y = yPos;
game.addChild(itemText);
// Store references for interaction
buttonBg.itemIndex = i;
buttonBg.down = function (x, y, obj) {
self.buyItem(obj.itemIndex);
};
itemButtons.push({
bg: buttonBg,
text: itemText
});
}
// Close button
var closeButton = LK.getAsset('wall', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3,
scaleY: 1
});
closeButton.tint = 0xff0000;
closeButton.x = 1024;
closeButton.y = 1800;
game.addChild(closeButton);
var closeText = new Text2('CLOSE', {
size: 40,
fill: 0xFFFFFF
});
closeText.anchor.set(0.5, 0.5);
closeText.x = 1024;
closeText.y = 1800;
game.addChild(closeText);
closeButton.down = function () {
self.closeShop();
};
// Store UI elements for cleanup
self.shopUI = {
elements: [shopBg, titleText, scoreDisplay, closeButton, closeText].concat(itemButtons.map(function (btn) {
return btn.bg;
}), itemButtons.map(function (btn) {
return btn.text;
}))
};
};
self.buyItem = function (itemIndex) {
// Validate itemIndex to prevent undefined access
if (itemIndex < 0 || itemIndex >= self.items.length) {
return; // Exit early if invalid index
}
var item = self.items[itemIndex];
// Additional safety check to ensure item exists
if (!item || typeof item.price === 'undefined') {
return; // Exit early if item is invalid
}
var currentScore = LK.getScore();
if (currentScore >= item.price) {
// Deduct score
LK.setScore(currentScore - item.price);
// Apply item effect
switch (item.type) {
case 'energyDrink':
// Store original values for restoration
var originalSpeed = player.speed;
var originalShootCooldown = player.shootCooldown;
// Apply speed boost
player.speed = player.baseSpeed * 1.5;
// Apply fire rate boost
player.shootCooldown = Math.max(1, player.shootCooldown * 0.6);
// Use tween to smoothly fade back to normal after 10 seconds
tween(player, {
speed: originalSpeed
}, {
duration: 10000,
easing: tween.easeOut
});
// Restore shoot cooldown after 10 seconds
LK.setTimeout(function () {
player.shootCooldown = originalShootCooldown;
}, 10000);
break;
case 'ammo':
totalBullets += 30;
break;
case 'healthPack':
player.heal(player.maxHealth);
break;
case 'food':
player.heal(20);
break;
case 'kebab':
player.heal(20);
break;
case 'pistol':
player.equipWeapon('pistol');
totalBullets += 45; // Pistol comes with 45 bullets
break;
case 'm249':
player.equipWeapon('m249');
totalBullets += 300; // M249 comes with 300 bullets (2 magazines of 150)
break;
}
LK.getSound('pickup').play();
self.closeShop();
self.openShop(); // Refresh shop display
}
};
self.closeShop = function () {
if (!self.isOpen) return;
self.isOpen = false;
// Clean up UI elements
if (self.shopUI) {
for (var i = 0; i < self.shopUI.elements.length; i++) {
if (self.shopUI.elements[i] && self.shopUI.elements[i].destroy) {
self.shopUI.elements[i].destroy();
}
}
self.shopUI = null;
}
};
return self;
});
var Soldier = Container.expand(function () {
var self = Container.call(this);
var soldierGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
// Equip soldier with rifle
var rifleSprite = self.attachAsset('rifle', {
anchorX: 0.5,
anchorY: 0.5,
x: 35,
y: 0
});
// Equip soldier with helmet
var helmetSprite = self.attachAsset('helmet', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: -35
});
// Equip soldier with armor
var armorSprite = self.attachAsset('armor', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 10
});
return self;
});
var Zombie = Container.expand(function (type) {
var self = Container.call(this);
var zombieType = type || 'zombie';
var zombieGraphics = self.attachAsset(zombieType, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
// Reduce width by 50%
scaleY: 0.5 // Reduce height by 50%
});
// Zombie stats based on type - all zombies now require exactly 5 bullets (300 health)
switch (zombieType) {
case 'zombie':
self.health = 300; // 5 bullets in all sections
if (currentSection === 3) {
self.speed = 1.0; // Much faster in section 3
self.damage = 30; // High damage in section 3
} else if (currentSection === 2) {
self.speed = 0.7; // Faster in section 2
self.damage = 20; // More damage in section 2
} else {
self.speed = 0.5; // Base speed
self.damage = 15; // Base damage
}
self.points = 10;
break;
case 'fastZombie':
self.health = 300; // 5 bullets in all sections
if (currentSection === 3) {
self.speed = 2.8; // Extremely fast in section 3
self.damage = 18; // High damage in section 3
} else if (currentSection === 2) {
self.speed = 2.0; // Much faster in section 2
self.damage = 12; // More damage in section 2
} else {
self.speed = 1.5; // Base fast speed
self.damage = 8; // Base damage
}
self.points = 15;
break;
case 'tankZombie':
self.health = 1200; // 20 bullets (1200/60 damage per bullet)
if (currentSection === 3) {
self.speed = 0.8; // Faster tank in section 3
self.damage = 40; // Massive damage in section 3
} else if (currentSection === 2) {
self.speed = 0.6; // Faster in section 2
self.damage = 25; // Much more damage in section 2
} else {
self.speed = 0.4; // Base tank speed
self.damage = 15; // Base tank damage
}
self.points = 25;
break;
}
self.maxHealth = self.health;
self.type = zombieType;
self.attackCooldown = 0;
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xff0000, 150);
if (self.health <= 0) {
return true; // Dead
}
return false;
};
self.update = function () {
if (self.attackCooldown > 0) {
self.attackCooldown--;
}
// Always aggressively pursue player position
var dx = player.x - self.x;
var dy = player.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
// Increase pursuit speed based on distance - zombies move faster when player is farther
var pursuitMultiplier = Math.min(2, distance / 200);
var actualSpeed = self.speed * (1 + pursuitMultiplier);
self.x += dx / distance * actualSpeed;
self.y += dy / distance * actualSpeed;
}
// Attack player if close enough - adjusted for much bigger zombies
var attackRange = zombieType === 'tankZombie' ? 110 : zombieType === 'zombie' ? 90 : 80;
if (distance < attackRange && self.attackCooldown <= 0) {
player.takeDamage(self.damage);
self.attackCooldown = 60; // 1 second at 60fps
LK.getSound('hit').play();
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2c2c2c
});
/****
* Game Code
****/
// Sounds
// Mall elements
// Items
// Zombies
// Player and weapons
// Game variables
var shop = new Shop();
var player;
var zombies = [];
var bullets = [];
var items = [];
var chests = [];
var walls = [];
var zombieSpawnTimer = 0;
var zombieSpawnRate = 180; // 3 seconds at 60fps
var difficultyTimer = 0;
var gameTime = 0;
// Level progression variables
var currentLevel = 1;
var maxLevels = 5;
var normalZombiesKilled = 0;
var fastZombiesKilled = 0;
var tankZombiesKilled = 0;
var normalZombiesToKill = 15;
var fastZombiesToKill = 5;
var tankZombiesToKill = 1;
var currentSection = 1;
var maxSections = 3; // Added section 3
// Magazine system
var totalBullets = 120; // Added 30 extra bullets (90 + 30)
var bulletsPerMagazine = 15;
var maxMagazineCapacity = 500; // Maximum magazine capacity
var currentMagazineBullets = bulletsPerMagazine;
function getBulletsPerMagazine() {
var baseCapacity;
switch (player.weapon) {
case 'rifle':
baseCapacity = 30;
break;
case 'pistol':
baseCapacity = 15;
break;
case 'm249':
baseCapacity = 150;
break;
default:
baseCapacity = 30;
break;
}
return Math.min(baseCapacity, maxMagazineCapacity);
}
var isReloading = false;
var reloadTimer = 0;
var reloadTime = 60; // 1 second at 60fps
// UI Elements
var healthText = new Text2('Health: 100', {
size: 40,
fill: 0xFF0000 // Change health text color to red
});
healthText.anchor.set(0, 0);
LK.gui.topLeft.addChild(healthText);
healthText.x = 120; // Avoid menu icon
var scoreText = new Text2('Score: 0', {
size: 40,
fill: 0xFFFFFF
});
scoreText.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreText);
var weaponText = new Text2('Weapon: Knife', {
size: 35,
fill: 0xFFFF00
});
weaponText.anchor.set(1, 0);
LK.gui.topRight.addChild(weaponText);
var ammoText = new Text2('Ammo: 30/90', {
size: 35,
fill: 0x00FFFF
});
ammoText.anchor.set(1, 0);
ammoText.y = 50;
LK.gui.topRight.addChild(ammoText);
// Create player
player = game.addChild(new Player());
player.x = 1024;
player.y = 1366;
// Create mall layout
function createMallLayout() {
// Create walls around the perimeter
for (var x = 0; x < 2048; x += 100) {
var topWall = game.addChild(LK.getAsset('wall', {
anchorX: 0.5,
anchorY: 0.5
}));
topWall.x = x + 50;
topWall.y = 50;
walls.push(topWall);
var bottomWall = game.addChild(LK.getAsset('wall', {
anchorX: 0.5,
anchorY: 0.5
}));
bottomWall.x = x + 50;
bottomWall.y = 2682;
walls.push(bottomWall);
}
for (var y = 100; y < 2632; y += 100) {
var leftWall = game.addChild(LK.getAsset('wall', {
anchorX: 0.5,
anchorY: 0.5
}));
leftWall.x = 50;
leftWall.y = y + 50;
walls.push(leftWall);
var rightWall = game.addChild(LK.getAsset('wall', {
anchorX: 0.5,
anchorY: 0.5
}));
rightWall.x = 1998;
rightWall.y = y + 50;
walls.push(rightWall);
}
}
function spawnChest() {
// Spawn chest at random location in playable area, avoiding walls
var chest = game.addChild(new Chest());
// Random position in playable area (avoiding walls and edges)
chest.x = Math.random() * 1700 + 200; // 200 to 1900
chest.y = Math.random() * 2200 + 300; // 300 to 2500
chests.push(chest);
}
function spawnZombie() {
var zombieTypes, zombieType;
// Different spawn patterns for each section
if (currentSection === 3) {
// Section 3: Extremely aggressive spawning with high chance of dangerous zombies
zombieTypes = ['zombie', 'fastZombie', 'fastZombie', 'tankZombie', 'tankZombie'];
zombieType = zombieTypes[Math.floor(Math.random() * zombieTypes.length)];
// In section 3, frequently spawn multiple zombies at once
if (Math.random() < 0.6) {
// 60% chance to spawn extra zombie
var extraZombie = game.addChild(new Zombie('fastZombie'));
// Spawn extra zombie at different edge
var extraSide = Math.floor(Math.random() * 4);
switch (extraSide) {
case 0:
extraZombie.x = Math.random() * 1948 + 100;
extraZombie.y = 150;
break;
case 1:
extraZombie.x = 1898;
extraZombie.y = Math.random() * 2432 + 150;
break;
case 2:
extraZombie.x = Math.random() * 1948 + 100;
extraZombie.y = 2582;
break;
case 3:
extraZombie.x = 150;
extraZombie.y = Math.random() * 2432 + 150;
break;
}
zombies.push(extraZombie);
// 25% chance to spawn a third zombie in section 3
if (Math.random() < 0.25) {
var thirdZombie = game.addChild(new Zombie('zombie'));
var thirdSide = Math.floor(Math.random() * 4);
switch (thirdSide) {
case 0:
thirdZombie.x = Math.random() * 1948 + 100;
thirdZombie.y = 150;
break;
case 1:
thirdZombie.x = 1898;
thirdZombie.y = Math.random() * 2432 + 150;
break;
case 2:
thirdZombie.x = Math.random() * 1948 + 100;
thirdZombie.y = 2582;
break;
case 3:
thirdZombie.x = 150;
thirdZombie.y = Math.random() * 2432 + 150;
break;
}
zombies.push(thirdZombie);
}
}
} else if (currentSection === 2) {
// Section 2: More aggressive spawning with higher chance of dangerous zombies
zombieTypes = ['zombie', 'zombie', 'fastZombie', 'fastZombie', 'tankZombie'];
zombieType = zombieTypes[Math.floor(Math.random() * zombieTypes.length)];
// In section 2, occasionally spawn multiple zombies at once
if (Math.random() < 0.3) {
// 30% chance to spawn extra zombie
var extraZombie = game.addChild(new Zombie('zombie'));
// Spawn extra zombie at different edge
var extraSide = Math.floor(Math.random() * 4);
switch (extraSide) {
case 0:
extraZombie.x = Math.random() * 1948 + 100;
extraZombie.y = 150;
break;
case 1:
extraZombie.x = 1898;
extraZombie.y = Math.random() * 2432 + 150;
break;
case 2:
extraZombie.x = Math.random() * 1948 + 100;
extraZombie.y = 2582;
break;
case 3:
extraZombie.x = 150;
extraZombie.y = Math.random() * 2432 + 150;
break;
}
zombies.push(extraZombie);
}
} else {
// Section 1: Original spawn pattern
zombieTypes = ['zombie', 'zombie', 'zombie', 'fastZombie', 'tankZombie'];
zombieType = zombieTypes[Math.floor(Math.random() * zombieTypes.length)];
// Make tank zombies rarer early on
if (zombieType === 'tankZombie' && gameTime < 1800) {
zombieType = 'zombie';
}
}
var zombie = game.addChild(new Zombie(zombieType));
// Spawn at random edge
var side = Math.floor(Math.random() * 4);
switch (side) {
case 0:
// Top
zombie.x = Math.random() * 1948 + 100;
zombie.y = 150;
break;
case 1:
// Right
zombie.x = 1898;
zombie.y = Math.random() * 2432 + 150;
break;
case 2:
// Bottom
zombie.x = Math.random() * 1948 + 100;
zombie.y = 2582;
break;
case 3:
// Left
zombie.x = 150;
zombie.y = Math.random() * 2432 + 150;
break;
}
zombies.push(zombie);
}
function checkCollisions() {
// Player vs items
for (var i = items.length - 1; i >= 0; i--) {
var item = items[i];
if (player.intersects(item) && item.collect()) {
item.destroy();
items.splice(i, 1);
}
}
// Bullet vs zombie collisions
for (var b = bullets.length - 1; b >= 0; b--) {
var bullet = bullets[b];
var bulletHit = false;
// Check if bullet is out of bounds
if (bullet.x < 0 || bullet.x > 2048 || bullet.y < 0 || bullet.y > 2732) {
bullet.destroy();
bullets.splice(b, 1);
continue;
}
// Check bullet vs zombies
for (var z = zombies.length - 1; z >= 0; z--) {
var zombie = zombies[z];
if (bullet.intersects(zombie)) {
if (zombie.takeDamage(bullet.damage)) {
// Zombie died
LK.setScore(LK.getScore() + zombie.points);
LK.getSound('zombieHit').play();
// Random drop chance
if (Math.random() < 0.50) {
var dropTypes = ['rifle', 'pistol', 'rifle', 'pistol', 'ammo', 'healthPack', 'food', 'energyDrink', 'helmet', 'helmet2', 'helmet3', 'kebab', 'm249'];
var dropType = dropTypes[Math.floor(Math.random() * dropTypes.length)];
var drop = new Item(dropType);
drop.x = zombie.x;
drop.y = zombie.y;
items.push(drop);
game.addChild(drop);
}
// Track zombie kills for level progression
switch (zombie.type) {
case 'zombie':
normalZombiesKilled++;
break;
case 'fastZombie':
fastZombiesKilled++;
break;
case 'tankZombie':
tankZombiesKilled++;
break;
}
zombie.destroy();
zombies.splice(z, 1);
}
bullet.destroy();
bullets.splice(b, 1);
bulletHit = true;
break;
}
}
if (bulletHit) continue;
}
// Player melee attacks zombies (only for knife)
if (!player.isRangedWeapon && player.attackCooldown <= 0) {
for (var z = zombies.length - 1; z >= 0; z--) {
var zombie = zombies[z];
var distance = Math.sqrt((player.x - zombie.x) * (player.x - zombie.x) + (player.y - zombie.y) * (player.y - zombie.y));
var meleeRange = zombie.type === 'tankZombie' ? 120 : zombie.type === 'zombie' ? 100 : 90;
if (distance < meleeRange) {
if (zombie.takeDamage(player.weaponDamage)) {
// Zombie died
LK.setScore(LK.getScore() + zombie.points);
LK.getSound('zombieHit').play();
// Random drop chance
if (Math.random() < 0.50) {
var dropTypes = ['rifle', 'pistol', 'rifle', 'pistol', 'ammo', 'healthPack', 'food', 'energyDrink', 'helmet', 'helmet2', 'helmet3', 'kebab', 'm249'];
var dropType = dropTypes[Math.floor(Math.random() * dropTypes.length)];
var drop = new Item(dropType);
drop.x = zombie.x;
drop.y = zombie.y;
items.push(drop);
game.addChild(drop);
}
zombie.destroy();
zombies.splice(z, 1);
}
player.attackCooldown = 30; // Half second
break;
}
}
}
}
function updateUI() {
// Create health and armor text with different colors using tween to animate color changes
var healthValue = Math.max(0, Math.floor(player.health));
var armorValue = player.armor;
// Use tween to smoothly transition health text color based on health level
if (healthValue < 30) {
// Critical health - flash red
tween(healthText, {
tint: 0xFF0000
}, {
duration: 500,
easing: tween.easeInOut
});
} else if (healthValue < 60) {
// Low health - orange
tween(healthText, {
tint: 0xFF8800
}, {
duration: 300,
easing: tween.easeOut
});
} else {
// Good health - red
tween(healthText, {
tint: 0xFF0000
}, {
duration: 200,
easing: tween.easeOut
});
}
healthText.setText('Health: ' + healthValue);
// Create separate armor display with cyan color
if (!game.armorText) {
game.armorText = new Text2('Armor: ' + armorValue, {
size: 40,
fill: 0x00FFFF // Cyan color for armor
});
game.armorText.anchor.set(0, 0);
game.armorText.x = 120; // Same x as health text
game.armorText.y = 50; // Below health text
LK.gui.topLeft.addChild(game.armorText);
} else {
game.armorText.setText('Armor: ' + armorValue);
// Use tween to animate armor text color based on armor level
if (armorValue > 50) {
tween(game.armorText, {
tint: 0x00FFFF
}, {
duration: 200,
easing: tween.easeOut
}); // Bright cyan
} else if (armorValue > 20) {
tween(game.armorText, {
tint: 0x0088AA
}, {
duration: 300,
easing: tween.easeOut
}); // Darker cyan
} else {
tween(game.armorText, {
tint: 0x004466
}, {
duration: 500,
easing: tween.easeOut
}); // Very dark cyan
}
}
scoreText.setText('Score: ' + LK.getScore());
weaponText.setText('Weapon: ' + player.weapon.charAt(0).toUpperCase() + player.weapon.slice(1));
if (isReloading) {
ammoText.setText('Reloading... ' + Math.ceil(reloadTimer / 60) + 's');
} else {
ammoText.setText('Ammo: ' + currentMagazineBullets + '/' + totalBullets);
}
}
// Input handling
var targetX = player.x;
var targetY = player.y;
var isPressed = false;
var keys = {
w: false,
a: false,
s: false,
d: false
};
function tryShoot(x, y) {
if (player.isRangedWeapon && player.shootCooldown <= 0 && currentMagazineBullets > 0 && !isReloading) {
// Regular single bullet for rifle, pistol, and m249 - all bullets do 60 damage
var bullet = new Bullet(player.x, player.y, x, y, 60);
bullet.x = player.x;
bullet.y = player.y;
bullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot').play();
currentMagazineBullets--;
// Set cooldown based on weapon type
switch (player.weapon) {
case 'rifle':
player.shootCooldown = 15; // 0.25 seconds
break;
case 'pistol':
player.shootCooldown = 15; // 0.25 seconds - faster firing
break;
case 'm249':
player.shootCooldown = 9; // 7 bullets per second (60fps / 7 ≈ 8.6 frames, rounded to 9)
break;
}
}
}
function tryReload() {
var weaponMagazineSize = getBulletsPerMagazine();
if (isReloading || currentMagazineBullets >= weaponMagazineSize || totalBullets <= 0) {
return;
}
var bulletsNeeded = weaponMagazineSize - currentMagazineBullets;
var bulletsToReload = Math.min(bulletsNeeded, totalBullets);
if (bulletsToReload > 0) {
isReloading = true;
// Set reload time based on weapon type
switch (player.weapon) {
case 'm249':
reloadTimer = 300; // 5 seconds at 60fps
break;
case 'pistol':
reloadTimer = 60; // 1 second at 60fps
break;
default:
reloadTimer = reloadTime; // Default reload time
break;
}
}
}
game.down = function (x, y, obj) {
targetX = x;
targetY = y;
isPressed = true;
tryShoot(x, y);
};
game.move = function (x, y, obj) {
targetX = x;
targetY = y;
// Rotate weapon to follow cursor
if (player.weaponSprite) {
var dx = x - player.x;
var dy = y - player.y;
player.weaponSprite.rotation = Math.atan2(dy, dx);
}
if (isPressed) {
tryShoot(x, y);
}
};
game.up = function (x, y, obj) {
isPressed = false;
};
// Note: LK engine sandbox doesn't support document API
// WASD movement will be handled through touch/mouse simulation
// Touch controls for mobile compatibility
var touchStartX = 0;
var touchStartY = 0;
var isTouching = false;
// Initialize mall
createMallLayout();
// Spawn initial chests for section 1 (max 2)
for (var i = 0; i < 2; i++) {
spawnChest();
}
// Add armor charging station
var armorCharger = new Item('armor');
armorCharger.x = 500;
armorCharger.y = 500;
items.push(armorCharger);
game.addChild(armorCharger);
game.update = function () {
gameTime++;
// Move player toward cursor position continuously
var moveX = 0;
var moveY = 0;
var dx = targetX - player.x;
var dy = targetY - player.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 10) {
// Only move if not very close to target
var dirX = dx / distance;
var dirY = dy / distance;
moveX = dirX * player.speed;
moveY = dirY * player.speed;
}
player.x += moveX;
player.y += moveY;
// Continuous shooting when pressed
if (isPressed) {
tryShoot(targetX, targetY);
}
// Keep player in bounds
player.x = Math.max(100, Math.min(1948, player.x));
player.y = Math.max(150, Math.min(2582, player.y));
// Spawn zombies
zombieSpawnTimer++;
if (zombieSpawnTimer >= zombieSpawnRate) {
spawnZombie();
zombieSpawnTimer = 0;
}
// Spawn chests randomly in each section
if (Math.random() < 0.001 && chests.length < 2) {
// Low chance, max 2 chests per section
spawnChest();
}
// Increase difficulty over time
difficultyTimer++;
if (difficultyTimer >= 1800) {
// Every 30 seconds
if (currentSection === 3) {
// Section 3: Extremely aggressive difficulty scaling
zombieSpawnRate = Math.max(25, zombieSpawnRate - 20);
} else if (currentSection === 2) {
// Section 2: More aggressive difficulty scaling
zombieSpawnRate = Math.max(45, zombieSpawnRate - 15);
} else {
// Section 1: Original difficulty scaling
zombieSpawnRate = Math.max(60, zombieSpawnRate - 10);
}
difficultyTimer = 0;
}
// Handle reload system
if (isReloading) {
reloadTimer--;
if (reloadTimer <= 0) {
var weaponMagazineSize = getBulletsPerMagazine();
var bulletsNeeded = weaponMagazineSize - currentMagazineBullets;
var bulletsToReload = Math.min(bulletsNeeded, totalBullets);
currentMagazineBullets += bulletsToReload;
totalBullets -= bulletsToReload;
isReloading = false;
}
} else if (currentMagazineBullets <= 0 && totalBullets > 0) {
// Auto reload when magazine is empty
tryReload();
}
// Check level completion - victory when all required zombies are killed
if (normalZombiesKilled >= normalZombiesToKill && fastZombiesKilled >= fastZombiesToKill && tankZombiesKilled >= tankZombiesToKill) {
// Stop spawning zombies by setting spawn rate to impossible value
zombieSpawnRate = 999999;
// Clear any remaining zombies on screen
for (var z = zombies.length - 1; z >= 0; z--) {
zombies[z].destroy();
zombies.splice(z, 1);
}
// Display appropriate completion message based on section
var messageText;
if (currentSection === 1) {
messageText = 'Section 1 Complete!';
} else if (currentSection === 2) {
messageText = 'Section 2 Complete!';
} else {
messageText = 'All Sections Complete! You Survived!';
}
var victoryText = new Text2(messageText, {
size: currentSection === 3 ? 70 : 80,
fill: currentSection === 3 ? 0xFFD700 : 0x00FF00
});
victoryText.anchor.set(0.5, 0.5);
victoryText.x = 1024;
victoryText.y = 1366;
game.addChild(victoryText);
// After 3 seconds, show soldiers and armored car
LK.setTimeout(function () {
victoryText.destroy();
// Create soldier 1
var soldier1 = game.addChild(new Soldier());
soldier1.x = 300;
soldier1.y = 1366;
// Create soldier 2
var soldier2 = game.addChild(new Soldier());
soldier2.x = 1748;
soldier2.y = 1366;
// Create armored military vehicle
var armoredVehicle = game.addChild(new ArmoredVehicle());
armoredVehicle.x = 1024;
armoredVehicle.y = 200;
// Clear all items on the ground when tank vehicle shop becomes available
for (var i = items.length - 1; i >= 0; i--) {
items[i].destroy();
items.splice(i, 1);
}
// Add shop interaction to vehicle
armoredVehicle.down = function (x, y, obj) {
shop.openShop();
};
// Add soldier interaction for progression
soldier1.down = function (x, y, obj) {
if (currentSection === 1) {
// Update to section 2
currentSection = 2;
// Reset kill counters for section 2
normalZombiesKilled = 0;
fastZombiesKilled = 0;
tankZombiesKilled = 0;
// Update requirements for section 2
normalZombiesToKill = 20;
fastZombiesToKill = 7;
tankZombiesToKill = 2;
// Reset spawn rate and restart zombie spawning - faster spawning for section 2
zombieSpawnRate = 120; // Faster spawning (2 seconds instead of 3)
zombieSpawnTimer = 0;
// Hide soldiers and armored vehicle when episode two starts
soldier1.destroy();
soldier2.destroy();
armoredVehicle.destroy();
shopInstructionText.destroy();
finalText.destroy();
// Display section 2 start message
var sectionText = new Text2('Section 2 - Contact established!', {
size: 60,
fill: 0x00FF00
});
sectionText.anchor.set(0.5, 0.5);
sectionText.x = 1024;
sectionText.y = 1366;
game.addChild(sectionText);
// Remove message after 3 seconds
LK.setTimeout(function () {
sectionText.destroy();
}, 3000);
} else if (currentSection === 2) {
// Update to section 3
currentSection = 3;
// Reset kill counters for section 3
normalZombiesKilled = 0;
fastZombiesKilled = 0;
tankZombiesKilled = 0;
// Update requirements for section 3 (much harder)
normalZombiesToKill = 35;
fastZombiesToKill = 15;
tankZombiesToKill = 5;
// Reset spawn rate and restart zombie spawning - very fast spawning for section 3
zombieSpawnRate = 80; // Very fast spawning (1.33 seconds)
zombieSpawnTimer = 0;
// Display section 3 start message
var sectionText = new Text2('Final Section 3 - Survive the Onslaught!', {
size: 50,
fill: 0xFF0000 // Red text for danger
});
sectionText.anchor.set(0.5, 0.5);
sectionText.x = 1024;
sectionText.y = 1366;
game.addChild(sectionText);
// Remove message after 4 seconds
LK.setTimeout(function () {
sectionText.destroy();
}, 4000);
}
// Victory elements already cleared when section 2 started
// Clear all chests for new section
for (var c = chests.length - 1; c >= 0; c--) {
chests[c].destroy();
chests.splice(c, 1);
}
// Clear all items on the ground for new section
for (var i = items.length - 1; i >= 0; i--) {
items[i].destroy();
items.splice(i, 1);
}
};
soldier2.down = function (x, y, obj) {
// Same interaction as soldier1
soldier1.down(x, y, obj);
};
// Add instruction text
var shopInstructionText = new Text2('Click vehicle to open shop', {
size: 40,
fill: 0xFFFFFF
});
shopInstructionText.anchor.set(0.5, 0.5);
shopInstructionText.x = 1024;
shopInstructionText.y = 400;
game.addChild(shopInstructionText);
// Show final victory message only after section 3
if (currentSection === 3) {
var finalText = new Text2('You are rescued!', {
size: 100,
fill: 0xFFD700
});
finalText.anchor.set(0.5, 0.5);
finalText.x = 1024;
finalText.y = 1000;
game.addChild(finalText);
// Show you win after soldiers arrive
LK.setTimeout(function () {
LK.showYouWin();
}, 3000);
} else {
var finalText = new Text2('Contact the soldier to continue!', {
size: 60,
fill: 0xFFFFFF
});
finalText.anchor.set(0.5, 0.5);
finalText.x = 1024;
finalText.y = 1000;
game.addChild(finalText);
}
}, 3000);
}
// Update game objects
player.update();
for (var z = 0; z < zombies.length; z++) {
zombies[z].update();
}
for (var b = 0; b < bullets.length; b++) {
bullets[b].update();
}
// Check collisions
checkCollisions();
// Update UI
updateUI();
};
military knife. In-Game asset. 2d. High contrast. No shadows. miltary knife
bullet. In-Game asset. 2d. High contrast. No shadows
ak 47 . No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
soldier without weopen. In-Game asset. 2d. High contrast. No shadows
healty pack middle white plus. In-Game asset. 2d. High contrast. No shadows
bulletproof armor. In-Game asset. 2d. High contrast. No shadows
redbull energy drink. In-Game asset. 2d. High contrast. No shadows
ammo box but gray. In-Game asset. 2d. High contrast. No shadows
sandwich. In-Game asset. 2d. High contrast. No shadows
brown backpack. In-Game asset. 2d. High contrast. No shadows
glock 18. In-Game asset. 2d. High contrast. No shadows
kebab. In-Game asset. 2d. High contrast. No shadows
m249. In-Game asset. 2d. High contrast. No shadows
gray cement. In-Game asset. 2d. High contrast. No shadows
biker helmet. In-Game asset. 2d. High contrast. No shadows
helmet look like soo good bullet proff. In-Game asset. 2d. High contrast. No shadows