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