User prompt
Can you also add a design for the bosses, Place one bomb in a different location on the map for each wave — only one bomb per wave. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
The game is almost finished now! But can you remove the overpower from the pistol during the final boss fight? Make it strong, but not too much. Also, make the enemies much tougher in the last wave.
User prompt
Perfect! Can you add a better design for the zombies and the main character, place health pickups on the map, and decorate the map with visual details?” ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Code it, and put a character design ↪💡 Consider importing and using the following plugins: @upit/tween.v1
Code edit (1 edits merged)
Please save this source code
User prompt
Zombie Waves: Key to Survival
Initial prompt
Zombie Waves Fight zombies through five rounds. At the end of each round, there is a strong enemy that’s different from the regular ones. After defeating it, you get a key that leads you to the next round, and so on. In the final round, you face a very powerful enemy, and the player is given a strong weapon to fight him. The game should have classic-style graphics, with control buttons, and a designed character appearance.
/**** * 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); } } };
===================================================================
--- original.js
+++ change.js
@@ -5,15 +5,189 @@
/****
* 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) {
@@ -30,8 +204,19 @@
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);
@@ -389,8 +574,9 @@
var player;
var zombies = [];
var bullets = [];
var healthPickups = [];
+var bombs = [];
var boss = null;
var keys = 0;
var currentWave = 1;
var waveInProgress = false;
@@ -475,8 +661,10 @@
zombiesToSpawn = 5 + currentWave * 3;
spawnTimer = 0;
waveText.setText('Wave ' + currentWave);
instructionText.setText('');
+ // Spawn bomb for this wave
+ spawnBombForWave(currentWave);
if (currentWave > 1) {
player.upgrade();
}
}
@@ -646,8 +834,41 @@
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');
@@ -710,23 +931,37 @@
// Bullet collision detection
for (var i = bullets.length - 1; i >= 0; i--) {
var bullet = bullets[i];
var hit = false;
- // 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);
+ // 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();