/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var Boss = Container.expand(function (type) { var self = Container.call(this); self.type = type || 'skeleton'; var assetName = self.type; var graphics = self.attachAsset(assetName, { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, // Make boss bigger scaleY: 1.5 }); // Boss has much higher stats switch (self.type) { case 'skeleton': self.health = 500 + currentRoom * 100; self.speed = 1; self.damage = 18; break; case 'zombie': self.health = 600 + currentRoom * 120; self.speed = 0.8; self.damage = 24; break; case 'spider': self.health = 400 + currentRoom * 80; self.speed = 1.5; self.damage = 15; break; } self.maxHealth = self.health; self.lastHealthPercent = 1.0; // Track last health percentage for half health detection self.halfHealthOrbSpawned = false; // Prevent multiple orb spawns // Create health bar self.healthBarBg = self.attachAsset('wall', { anchorX: 0.5, anchorY: 0.5, scaleX: 2, scaleY: 0.2, y: -80 }); self.healthBarBg.tint = 0x666666; self.healthBar = self.attachAsset('wall', { anchorX: 0, anchorY: 0.5, scaleX: 2, scaleY: 0.15, x: -64, y: -80 }); self.healthBar.tint = 0x00ff00; self.lastAttack = 0; self.attackRate = 45; // Faster attack rate self.isBoss = true; // Boss special attack timer self.specialAttackTimer = 0; self.specialAttackCooldown = 120; // 2 seconds at 60fps self.takeDamage = function (damage) { self.health -= damage; // Update health bar var healthPercent = self.health / self.maxHealth; self.healthBar.scaleX = 2 * healthPercent; // Check if boss health dropped to half (50% or below) for the first time if (self.lastHealthPercent > 0.5 && healthPercent <= 0.5 && !self.halfHealthOrbSpawned) { // Spawn health orb at random location var healthOrb = new HealthOrb(); healthOrb.x = 300 + Math.random() * (2048 - 600); // Random x position avoiding walls healthOrb.y = 400 + Math.random() * (2000 - 400); // Random y position avoiding walls healthOrbs.push(healthOrb); game.addChild(healthOrb); self.halfHealthOrbSpawned = true; // Mark as spawned to prevent duplicates } // Update last health percentage for next comparison self.lastHealthPercent = healthPercent; // Change health bar color based on health if (healthPercent > 0.6) { self.healthBar.tint = 0x00ff00; // Green } else if (healthPercent > 0.3) { self.healthBar.tint = 0xffff00; // Yellow } else { self.healthBar.tint = 0xff0000; // Red } // Flash red when taking damage (different color for boss) tween(graphics, { tint: 0xFF4444 }, { duration: 150, onFinish: function onFinish() { tween(graphics, { tint: 0xFFFFFF }, { duration: 150 }); } }); if (self.health <= 0) { self.die(); } }; self.die = function () { LK.getSound('enemyDeath').play(); enemiesKilled++; bossKilled = true; // Remove from enemies array for (var i = enemies.length - 1; i >= 0; i--) { if (enemies[i] === self) { enemies.splice(i, 1); break; } } self.destroy(); }; self.moveTowardsPlayer = function () { 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; } }; self.canAttack = function () { return LK.ticks - self.lastAttack >= self.attackRate; }; self.attackPlayer = function () { if (self.canAttack()) { player.takeDamage(self.damage); self.lastAttack = LK.ticks; } }; self.weaponMode = 0; // 0 = scatter, 1 = laser, 2 = flamethrower self.weaponTimer = 0; self.weaponSwitchInterval = 120; // Switch weapons every 2 seconds self.scatterAttack = function () { // Shoot bigger bullets in all directions var directions = 12; for (var i = 0; i < directions; i++) { var angle = i / directions * Math.PI * 2; var bossBullet = new BossBullet(); bossBullet.x = self.x; bossBullet.y = self.y; bossBullet.velocityX = Math.cos(angle) * 4; bossBullet.velocityY = Math.sin(angle) * 4; bossBullets.push(bossBullet); game.addChild(bossBullet); } }; self.laserAttack = function () { // Create deadly lasers fired from boss position outward in different directions for (var i = 0; i < 8; i++) { var laser = new Laser(); laser.angle = i / 8 * Math.PI * 2; // Spread lasers in 8 directions // Position laser at boss location laser.x = self.x; laser.y = self.y; // Set laser rotation to point outward from boss laser.rotation = laser.angle; // Move laser outward from boss by half its length so one end stays at boss var offsetDistance = 200; // Half of laser length (400/2) laser.x += Math.cos(laser.angle) * offsetDistance; laser.y += Math.sin(laser.angle) * offsetDistance; lasers.push(laser); game.addChild(laser); } }; self.flamethrowerAttack = function () { // Spray flames randomly everywhere for (var i = 0; i < 8; i++) { var flame = new Flame(); flame.x = self.x; flame.y = self.y; var angle = Math.random() * Math.PI * 2; var speed = 2 + Math.random() * 3; flame.velocityX = Math.cos(angle) * speed; flame.velocityY = Math.sin(angle) * speed; flames.push(flame); game.addChild(flame); } }; self.specialAttack = function () { switch (self.weaponMode) { case 0: self.scatterAttack(); break; case 1: self.laserAttack(); break; case 2: self.flamethrowerAttack(); break; } self.specialAttackTimer = 0; }; self.update = function () { self.moveTowardsPlayer(); // Regular attack when close var dx = player.x - self.x; var dy = player.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < 80) { self.attackPlayer(); } // Weapon switching timer self.weaponTimer++; if (self.weaponTimer >= self.weaponSwitchInterval) { self.weaponMode = (self.weaponMode + 1) % 3; // Cycle through 0, 1, 2 self.weaponTimer = 0; } // Special attack timer self.specialAttackTimer++; if (self.specialAttackTimer >= self.specialAttackCooldown) { self.specialAttack(); } }; return self; }); var BossBullet = Container.expand(function () { var self = Container.call(this); var graphics = self.attachAsset('bossBullet', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.6, scaleY: 0.6 }); self.velocityX = 0; self.velocityY = 0; self.damage = 12; self.lifetime = 300; // 5 seconds at 60fps self.age = 0; self.update = function () { self.x += self.velocityX; self.y += self.velocityY; self.age++; // Remove if lifetime exceeded or off screen if (self.age >= self.lifetime || self.x < 0 || self.x > 2048 || self.y < 0 || self.y > 2732) { self.removeBullet(); } }; self.removeBullet = function () { for (var i = bossBullets.length - 1; i >= 0; i--) { if (bossBullets[i] === self) { bossBullets.splice(i, 1); break; } } self.destroy(); }; return self; }); var Bullet = Container.expand(function () { var self = Container.call(this); var graphics = self.attachAsset('bullet', { anchorX: 0.5, anchorY: 0.5 }); self.velocityX = 0; self.velocityY = 0; self.damage = 25; self.update = function () { self.x += self.velocityX; self.y += self.velocityY; // Remove if off screen if (self.x < 0 || self.x > 2048 || self.y < 0 || self.y > 2732) { self.removeBullet(); } }; self.removeBullet = function () { for (var i = bullets.length - 1; i >= 0; i--) { if (bullets[i] === self) { bullets.splice(i, 1); break; } } self.destroy(); }; return self; }); var Chest = Container.expand(function () { var self = Container.call(this); var graphics = self.attachAsset('chest', { anchorX: 0.5, anchorY: 0.5 }); self.opened = false; self.open = function () { if (self.opened) return; self.opened = true; graphics.tint = 0x8B4513; // Darken to show it's opened LK.getSound('chestOpen').play(); // Spawn weapon var weapon = new Weapon(); weapon.x = self.x; weapon.y = self.y - 50; weapons.push(weapon); game.addChild(weapon); }; return self; }); var Enemy = Container.expand(function (type) { var self = Container.call(this); self.type = type || 'skeleton'; var assetName = self.type; var graphics = self.attachAsset(assetName, { anchorX: 0.5, anchorY: 0.5 }); // Set properties based on enemy type switch (self.type) { case 'skeleton': self.health = 30; self.speed = 1.5; self.damage = 15; break; case 'zombie': self.health = 50; self.speed = 1; self.damage = 20; break; case 'spider': self.health = 20; self.speed = 2.5; self.damage = 10; break; } self.maxHealth = self.health; self.lastAttack = 0; self.attackRate = 60; // frames between attacks self.takeDamage = function (damage) { self.health -= damage; // Flash white when taking damage tween(graphics, { tint: 0xFFFFFF }, { duration: 100, onFinish: function onFinish() { tween(graphics, { tint: 0xFFFFFF }, { duration: 100 }); } }); if (self.health <= 0) { self.die(); } }; self.die = function () { LK.getSound('enemyDeath').play(); enemiesKilled++; // Remove from enemies array for (var i = enemies.length - 1; i >= 0; i--) { if (enemies[i] === self) { enemies.splice(i, 1); break; } } self.destroy(); }; self.moveTowardsPlayer = function () { 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; } }; self.canAttack = function () { return LK.ticks - self.lastAttack >= self.attackRate; }; self.attackPlayer = function () { if (self.canAttack()) { player.takeDamage(self.damage); self.lastAttack = LK.ticks; } }; self.update = function () { self.moveTowardsPlayer(); // Check if close enough to attack player var dx = player.x - self.x; var dy = player.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < 60) { self.attackPlayer(); } }; return self; }); var Flame = Container.expand(function () { var self = Container.call(this); var graphics = self.attachAsset('flame', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.7, scaleY: 0.7 }); self.velocityX = 0; self.velocityY = 0; self.damage = 5; self.lifetime = 180; // 3 seconds at 60fps self.age = 0; self.update = function () { self.x += self.velocityX; self.y += self.velocityY; self.age++; // Add random movement for flame effect self.velocityX += (Math.random() - 0.5) * 0.5; self.velocityY += (Math.random() - 0.5) * 0.5; // Fade out over time graphics.alpha = 1 - self.age / self.lifetime; // Random scale variation var scale = 0.5 + Math.random() * 0.5; graphics.scaleX = scale; graphics.scaleY = scale; // Remove if lifetime exceeded if (self.age >= self.lifetime) { self.removeFlame(); } }; self.removeFlame = function () { for (var i = flames.length - 1; i >= 0; i--) { if (flames[i] === self) { flames.splice(i, 1); break; } } self.destroy(); }; return self; }); var HealthOrb = Container.expand(function () { var self = Container.call(this); var graphics = self.attachAsset('healthOrb', { anchorX: 0.5, anchorY: 0.5 }); self.healAmount = 30; self.pickup = function () { // Restore player health player.health = Math.min(player.maxHealth, player.health + self.healAmount); LK.getSound('weaponPickup').play(); // Reuse weapon pickup sound // Remove from healthOrbs array for (var i = healthOrbs.length - 1; i >= 0; i--) { if (healthOrbs[i] === self) { healthOrbs.splice(i, 1); break; } } self.destroy(); }; return self; }); var Laser = Container.expand(function () { var self = Container.call(this); var graphics = self.attachAsset('laser', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.5, scaleY: 0.5 }); self.damage = 6; self.lifetime = 60; // 1 second at 60fps self.age = 0; self.lastDamage = 0; // Track when damage was last dealt self.damageCooldown = 30; // 0.5 seconds between damage self.update = function () { self.age++; // Pulse effect var pulse = Math.sin(self.age * 0.3) * 0.3 + 0.7; graphics.alpha = pulse; // Remove after lifetime if (self.age >= self.lifetime) { self.removeLaser(); } }; self.removeLaser = function () { for (var i = lasers.length - 1; i >= 0; i--) { if (lasers[i] === self) { lasers.splice(i, 1); break; } } self.destroy(); }; return self; }); var PistolBullet = Container.expand(function () { var self = Container.call(this); var graphics = self.attachAsset('bullet', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.8, scaleY: 0.8 }); self.velocityX = 0; self.velocityY = 0; self.damage = 25; self.update = function () { self.x += self.velocityX; self.y += self.velocityY; // Remove if off screen if (self.x < 0 || self.x > 2048 || self.y < 0 || self.y > 2732) { self.removeBullet(); } }; self.removeBullet = function () { for (var i = pistolBullets.length - 1; i >= 0; i--) { if (pistolBullets[i] === self) { pistolBullets.splice(i, 1); break; } } self.destroy(); }; return self; }); var Player = Container.expand(function () { var self = Container.call(this); var legs = self.attachAsset('floor', { anchorX: 0.5, anchorY: 0, x: 0, y: 67, scaleX: 0.8, scaleY: 0.6 }); var body = self.attachAsset('playerBody', { anchorX: 0.5, anchorY: 0.5 }); var head = self.attachAsset('playerHead', { anchorX: 0.5, anchorY: 1.0, x: 0, y: -40 }); var graphics = body; // Keep reference for damage effects // Add weapon graphics to player var pistolGraphic = self.attachAsset('pistol', { anchorX: 0, anchorY: 0.5, x: 40, y: 0, scaleX: 2, scaleY: 2 }); var shotgunGraphic = self.attachAsset('shotgun', { anchorX: 0, anchorY: 0.5, x: 40, y: 0, scaleX: 2, scaleY: 2 }); var sniperGraphic = self.attachAsset('sniper', { anchorX: 0, anchorY: 0.5, x: 40, y: 0, scaleX: 2, scaleY: 2 }); // Create smaller hitbox for collision detection self.hitbox = self.attachAsset('playerBody', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.6, scaleY: 0.6, alpha: 0 // Make invisible }); self.health = 100; self.maxHealth = 100; self.speed = 4; self.weapon = 'pistol'; // Start with pistol self.currentWeaponIndex = 0; self.weapons = ['pistol', 'shotgun', 'sniper']; self.fireRate = 15; // frames between shots self.lastShot = 0; self.takeDamage = function (damage) { self.health -= damage; if (self.health <= 0) { self.health = 0; LK.showGameOver(); } // Flash red when taking damage tween(graphics, { tint: 0xFF0000 }, { duration: 200, onFinish: function onFinish() { tween(graphics, { tint: 0xFFFFFF }, { duration: 200 }); } }); }; self.moveTowards = function (targetX, targetY) { var dx = targetX - self.x; var dy = targetY - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 10) { self.x += dx / distance * self.speed; self.y += dy / distance * self.speed; // Keep player within room bounds self.x = Math.max(128, Math.min(2048 - 128, self.x)); self.y = Math.max(200, Math.min(2400, self.y)); } // Update weapon rotation to face target direction var angle = Math.atan2(dy, dx); pistolGraphic.rotation = angle; shotgunGraphic.rotation = angle; sniperGraphic.rotation = angle; }; self.canShoot = function () { return LK.ticks - self.lastShot >= self.fireRate; }; self.shoot = function (targetX, targetY) { if (!self.canShoot()) return; var dx = targetX - self.x; var dy = targetY - self.y; var distance = Math.sqrt(dx * dx + dy * dy); var normalizedX = dx / distance; var normalizedY = dy / distance; if (self.weapon === 'pistol') { var bullet = new PistolBullet(); bullet.x = self.x; bullet.y = self.y; bullet.velocityX = normalizedX * 8; bullet.velocityY = normalizedY * 8; pistolBullets.push(bullet); game.addChild(bullet); self.fireRate = 30; } else if (self.weapon === 'shotgun') { // Fire 3 bullets in spread pattern for (var i = 0; i < 3; i++) { var bullet = new ShotgunBullet(); bullet.x = self.x; bullet.y = self.y; // Add spread to the shots var spreadAngle = (i - 1) * 0.3; // -0.3, 0, 0.3 radians spread var angle = Math.atan2(dy, dx) + spreadAngle; bullet.velocityX = Math.cos(angle) * 6; bullet.velocityY = Math.sin(angle) * 6; shotgunBullets.push(bullet); game.addChild(bullet); } self.fireRate = 120; // 2 second cooldown } else if (self.weapon === 'sniper') { var bullet = new SniperBullet(); bullet.x = self.x; bullet.y = self.y; bullet.velocityX = normalizedX * 12; // Faster bullet bullet.velocityY = normalizedY * 12; sniperBullets.push(bullet); game.addChild(bullet); self.fireRate = 180; // 3 second cooldown } self.lastShot = LK.ticks; LK.getSound('shoot').play(); }; self.updateWeaponGraphics = function () { // Hide all weapons first pistolGraphic.alpha = 0; shotgunGraphic.alpha = 0; sniperGraphic.alpha = 0; // Show current weapon switch (self.weapon) { case 'pistol': pistolGraphic.alpha = 1; break; case 'shotgun': shotgunGraphic.alpha = 1; break; case 'sniper': sniperGraphic.alpha = 1; break; } }; self.switchWeapon = function () { self.currentWeaponIndex = (self.currentWeaponIndex + 1) % self.weapons.length; self.weapon = self.weapons[self.currentWeaponIndex]; // Update weapon indicator weaponText.setText('Weapon: ' + self.weapon.charAt(0).toUpperCase() + self.weapon.slice(1)); // Update weapon graphics self.updateWeaponGraphics(); }; // Override intersects method to use smaller hitbox self.intersects = function (other) { return self.hitbox.intersects(other); }; // Initialize weapon graphics self.updateWeaponGraphics(); return self; }); var ShotgunBullet = Container.expand(function () { var self = Container.call(this); var graphics = self.attachAsset('bullet', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.6, scaleY: 0.6 }); self.velocityX = 0; self.velocityY = 0; self.damage = 15; self.distanceTraveled = 0; self.maxDistance = 300; // Shotgun has limited range self.update = function () { self.x += self.velocityX; self.y += self.velocityY; self.distanceTraveled += Math.sqrt(self.velocityX * self.velocityX + self.velocityY * self.velocityY); // Remove if off screen or max distance reached if (self.x < 0 || self.x > 2048 || self.y < 0 || self.y > 2732 || self.distanceTraveled > self.maxDistance) { self.removeBullet(); } }; self.removeBullet = function () { for (var i = shotgunBullets.length - 1; i >= 0; i--) { if (shotgunBullets[i] === self) { shotgunBullets.splice(i, 1); break; } } self.destroy(); }; return self; }); var SniperBullet = Container.expand(function () { var self = Container.call(this); var graphics = self.attachAsset('bullet', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.2, scaleY: 1.2 }); graphics.tint = 0xffd700; // Golden color for sniper bullets self.velocityX = 0; self.velocityY = 0; self.damage = 60; // High damage for long range self.distanceTraveled = 0; self.update = function () { self.x += self.velocityX; self.y += self.velocityY; self.distanceTraveled += Math.sqrt(self.velocityX * self.velocityX + self.velocityY * self.velocityY); // Remove if off screen if (self.x < 0 || self.x > 2048 || self.y < 0 || self.y > 2732) { self.removeBullet(); } }; self.removeBullet = function () { for (var i = sniperBullets.length - 1; i >= 0; i--) { if (sniperBullets[i] === self) { sniperBullets.splice(i, 1); break; } } self.destroy(); }; return self; }); var Weapon = Container.expand(function () { var self = Container.call(this); var graphics = self.attachAsset('weapon', { anchorX: 0.5, anchorY: 0.5 }); self.weaponType = 'rifle'; self.pickup = function () { player.weapon = self.weaponType; player.fireRate = Math.max(5, player.fireRate - 3); // Faster firing LK.getSound('weaponPickup').play(); // Update weapon indicator weaponText.setText('Weapon: ' + self.weaponType.charAt(0).toUpperCase() + self.weaponType.slice(1)); // Update weapon graphics player.updateWeaponGraphics(); // Remove from weapons array for (var i = weapons.length - 1; i >= 0; i--) { if (weapons[i] === self) { weapons.splice(i, 1); break; } } self.destroy(); }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x1a1a1a }); /**** * Game Code ****/ // Game variables var player; var enemies = []; var bullets = []; var pistolBullets = []; var shotgunBullets = []; var sniperBullets = []; var bossBullets = []; var lasers = []; var flames = []; var weapons = []; var healthOrbs = []; var chest; var currentRoom = 1; var enemiesKilled = 0; var totalEnemiesInRoom = 0; var roomCleared = false; var targetX = 1024; var targetY = 1366; var bossSpawned = false; var bossKilled = false; // UI elements var scoreText = new Text2('Room: 1', { size: 60, fill: 0xFFFFFF }); scoreText.anchor.set(0.5, 0); LK.gui.top.addChild(scoreText); var healthText = new Text2('Health: 100', { size: 50, fill: 0x00FF00 }); healthText.anchor.set(0, 0); healthText.x = 20; healthText.y = 20; LK.gui.topLeft.addChild(healthText); // Weapon indicator in top right var weaponText = new Text2('Weapon: Pistol', { size: 50, fill: 0xFFFFFF }); weaponText.anchor.set(1, 0); weaponText.y = 20; LK.gui.topRight.addChild(weaponText); // Visual weapon icons var pistolIcon = LK.getAsset('pistol', { anchorX: 1, anchorY: 0, x: -20, y: 80 }); var shotgunIcon = LK.getAsset('shotgun', { anchorX: 1, anchorY: 0, x: -20, y: 120 }); var sniperIcon = LK.getAsset('sniper', { anchorX: 1, anchorY: 0, x: -20, y: 160 }); LK.gui.topRight.addChild(pistolIcon); LK.gui.topRight.addChild(shotgunIcon); LK.gui.topRight.addChild(sniperIcon); // Function to update weapon icon highlights function updateWeaponIcons() { pistolIcon.alpha = player.weapon === 'pistol' ? 1.0 : 0.5; shotgunIcon.alpha = player.weapon === 'shotgun' ? 1.0 : 0.5; sniperIcon.alpha = player.weapon === 'sniper' ? 1.0 : 0.5; } // Create floor tiles function createRoom() { // Clear existing room elements roomCleared = false; enemiesKilled = 0; // Create floor for (var x = 0; x < 32; x++) { for (var y = 3; y < 38; y++) { var floor = game.addChild(LK.getAsset('floor', { x: x * 64, y: y * 64 })); } } // Create walls around the room for (var x = 0; x < 32; x++) { // Top wall var topWall = game.addChild(LK.getAsset('wall', { x: x * 64, y: 2 * 64 })); // Bottom wall var bottomWall = game.addChild(LK.getAsset('wall', { x: x * 64, y: 38 * 64 })); } for (var y = 2; y < 39; y++) { // Left wall var leftWall = game.addChild(LK.getAsset('wall', { x: 0, y: y * 64 })); // Right wall var rightWall = game.addChild(LK.getAsset('wall', { x: 31 * 64, y: y * 64 })); } } function getRoomType() { if (currentRoom <= 20) return "Tutorial"; if (currentRoom <= 40) return "Swarm"; if (currentRoom <= 60) return "Elite"; if (currentRoom <= 80) return "Nightmare"; return "Hell"; } function spawnEnemiesForRoomType() { var roomType = getRoomType(); bossSpawned = false; bossKilled = false; switch (roomType) { case "Tutorial": spawnTutorialEnemies(); break; case "Swarm": spawnSwarmEnemies(); break; case "Elite": spawnEliteEnemies(); break; case "Nightmare": spawnNightmareEnemies(); break; case "Hell": spawnHellEnemies(); break; } } function spawnTutorialEnemies() { var enemyTypes = ['skeleton', 'zombie', 'spider']; var numEnemies = Math.min(5 + currentRoom * 2, 15); totalEnemiesInRoom = numEnemies; for (var i = 0; i < numEnemies; i++) { var enemyType = enemyTypes[Math.floor(Math.random() * enemyTypes.length)]; var enemy = new Enemy(enemyType); // Random position in room, avoiding walls enemy.x = 200 + Math.random() * (2048 - 400); enemy.y = 300 + Math.random() * (2200 - 400); enemies.push(enemy); game.addChild(enemy); } } function spawnSwarmEnemies() { // Rooms 21-40: More enemies, faster movement var enemyTypes = ['skeleton', 'zombie', 'spider']; var numEnemies = Math.min(8 + (currentRoom - 20) * 3, 25); totalEnemiesInRoom = numEnemies; for (var i = 0; i < numEnemies; i++) { var enemyType = enemyTypes[Math.floor(Math.random() * enemyTypes.length)]; var enemy = new Enemy(enemyType); // Boost stats for swarm section enemy.speed *= 1.5; enemy.health = Math.floor(enemy.health * 0.7); // Less health but more enemies enemy.x = 200 + Math.random() * (2048 - 400); enemy.y = 300 + Math.random() * (2200 - 400); enemies.push(enemy); game.addChild(enemy); } } function spawnEliteEnemies() { // Rooms 41-60: Fewer but much stronger enemies var enemyTypes = ['skeleton', 'zombie', 'spider']; var numEnemies = Math.min(3 + Math.floor((currentRoom - 40) / 2), 8); totalEnemiesInRoom = numEnemies; for (var i = 0; i < numEnemies; i++) { var enemyType = enemyTypes[Math.floor(Math.random() * enemyTypes.length)]; var enemy = new Enemy(enemyType); // Elite stats - much stronger enemy.health *= 3; enemy.maxHealth = enemy.health; enemy.damage *= 2; enemy.speed *= 1.2; // Make them visually bigger to show they're elite enemy.scaleX = 1.5; enemy.scaleY = 1.5; enemy.x = 200 + Math.random() * (2048 - 400); enemy.y = 300 + Math.random() * (2200 - 400); enemies.push(enemy); game.addChild(enemy); } } function spawnNightmareEnemies() { // Rooms 61-80: Mix of swarm and elite + boss every 5 rooms var enemyTypes = ['skeleton', 'zombie', 'spider']; var numRegular = Math.min(6 + Math.floor((currentRoom - 60) / 3), 12); var numElite = Math.min(2 + Math.floor((currentRoom - 60) / 5), 4); totalEnemiesInRoom = numRegular + numElite; // Spawn regular enemies for (var i = 0; i < numRegular; i++) { var enemyType = enemyTypes[Math.floor(Math.random() * enemyTypes.length)]; var enemy = new Enemy(enemyType); enemy.speed *= 1.3; enemy.health *= 1.5; enemy.maxHealth = enemy.health; enemy.x = 200 + Math.random() * (2048 - 400); enemy.y = 300 + Math.random() * (2200 - 400); enemies.push(enemy); game.addChild(enemy); } // Spawn elite enemies for (var i = 0; i < numElite; i++) { var enemyType = enemyTypes[Math.floor(Math.random() * enemyTypes.length)]; var enemy = new Enemy(enemyType); enemy.health *= 4; enemy.maxHealth = enemy.health; enemy.damage *= 2.5; enemy.speed *= 1.4; enemy.scaleX = 1.8; enemy.scaleY = 1.8; enemy.x = 200 + Math.random() * (2048 - 400); enemy.y = 300 + Math.random() * (2200 - 400); enemies.push(enemy); game.addChild(enemy); } } function spawnHellEnemies() { // Rooms 81-100: Ultimate challenge - everything is maxed var enemyTypes = ['skeleton', 'zombie', 'spider']; var numRegular = Math.min(10 + (currentRoom - 80), 20); var numElite = Math.min(3 + Math.floor((currentRoom - 80) / 2), 6); var numBosses = currentRoom % 10 === 0 ? 2 : 1; // Double boss every 10th room totalEnemiesInRoom = numRegular + numElite + numBosses; bossSpawned = true; // Always spawn boss in hell mode bossKilled = false; // Spawn regular enemies for (var i = 0; i < numRegular; i++) { var enemyType = enemyTypes[Math.floor(Math.random() * enemyTypes.length)]; var enemy = new Enemy(enemyType); enemy.speed *= 2; enemy.health *= 2; enemy.maxHealth = enemy.health; enemy.damage *= 1.5; enemy.x = 200 + Math.random() * (2048 - 400); enemy.y = 300 + Math.random() * (2200 - 400); enemies.push(enemy); game.addChild(enemy); } // Spawn elite enemies for (var i = 0; i < numElite; i++) { var enemyType = enemyTypes[Math.floor(Math.random() * enemyTypes.length)]; var enemy = new Enemy(enemyType); enemy.health *= 6; enemy.maxHealth = enemy.health; enemy.damage *= 3; enemy.speed *= 1.8; enemy.scaleX = 2; enemy.scaleY = 2; enemy.x = 200 + Math.random() * (2048 - 400); enemy.y = 300 + Math.random() * (2200 - 400); enemies.push(enemy); game.addChild(enemy); } // Spawn boss(es) for (var i = 0; i < numBosses; i++) { var bossType = enemyTypes[Math.floor(Math.random() * enemyTypes.length)]; var boss = new Boss(bossType); // Hell mode boss gets extra stats boss.health *= 2; boss.maxHealth = boss.health; boss.damage *= 1.5; boss.speed *= 1.3; boss.scaleX = 2.5; boss.scaleY = 2.5; boss.x = 1024 + (i * 200 - 100); // Spread multiple bosses boss.y = 1000; enemies.push(boss); game.addChild(boss); } } function spawnEnemies() { spawnTutorialEnemies(); } function spawnBoss() { if (!bossSpawned) { var enemyTypes = ['skeleton', 'zombie', 'spider']; var bossType = enemyTypes[Math.floor(Math.random() * enemyTypes.length)]; var boss = new Boss(bossType); // Scale boss based on room type var roomType = getRoomType(); if (roomType === "Elite") { boss.health *= 1.5; boss.maxHealth = boss.health; boss.damage *= 1.3; } else if (roomType === "Nightmare") { boss.health *= 2; boss.maxHealth = boss.health; boss.damage *= 1.5; boss.speed *= 1.2; } else if (roomType === "Hell") { boss.health *= 3; boss.maxHealth = boss.health; boss.damage *= 2; boss.speed *= 1.5; boss.scaleX = 2; boss.scaleY = 2; } // Spawn boss in center of room boss.x = 1024; boss.y = 1000; enemies.push(boss); game.addChild(boss); bossSpawned = true; totalEnemiesInRoom++; // Add boss to total count } } function spawnChest() { chest = new Chest(); chest.x = 1024; chest.y = 1366; game.addChild(chest); // Spawn health orb at random location in room var healthOrb = new HealthOrb(); healthOrb.x = 300 + Math.random() * (2048 - 600); // Random x position avoiding walls healthOrb.y = 400 + Math.random() * (2000 - 400); // Random y position avoiding walls healthOrbs.push(healthOrb); game.addChild(healthOrb); } function nextRoom() { currentRoom++; if (currentRoom > 100) { // Player has completed all 100 rooms - show victory LK.showYouWin(); return; } scoreText.setText('Room: ' + currentRoom + ' - ' + getRoomType()); // Clear current enemies and bullets for (var i = enemies.length - 1; i >= 0; i--) { enemies[i].destroy(); } enemies = []; for (var i = bullets.length - 1; i >= 0; i--) { bullets[i].destroy(); } bullets = []; // Clear pistol bullets for (var i = pistolBullets.length - 1; i >= 0; i--) { pistolBullets[i].destroy(); } pistolBullets = []; // Clear shotgun bullets for (var i = shotgunBullets.length - 1; i >= 0; i--) { shotgunBullets[i].destroy(); } shotgunBullets = []; // Clear sniper bullets for (var i = sniperBullets.length - 1; i >= 0; i--) { sniperBullets[i].destroy(); } sniperBullets = []; // Clear boss projectiles for (var i = bossBullets.length - 1; i >= 0; i--) { bossBullets[i].destroy(); } bossBullets = []; for (var i = lasers.length - 1; i >= 0; i--) { lasers[i].destroy(); } lasers = []; for (var i = flames.length - 1; i >= 0; i--) { flames[i].destroy(); } flames = []; // Remove chest if exists if (chest) { chest.destroy(); chest = null; } // Clear health orbs for (var i = healthOrbs.length - 1; i >= 0; i--) { healthOrbs[i].destroy(); } healthOrbs = []; // Spawn enemies based on room type spawnEnemiesForRoomType(); } // Initialize first room createRoom(); // Create player player = new Player(); player.x = 1024; player.y = 2000; game.addChild(player); // Spawn initial enemies spawnEnemies(); // Initialize weapon icons updateWeaponIcons(); // Touch controls game.move = function (x, y, obj) { targetX = x; targetY = y; }; // Right mouse button support for weapon switching game.rightDown = function (x, y, obj) { // Right click - switch weapon player.switchWeapon(); updateWeaponIcons(); }; var touchStartTime = 0; var touchHoldThreshold = 60; // 1 second hold at 60fps game.down = function (x, y, obj) { targetX = x; targetY = y; touchStartTime = LK.ticks; // Record when touch started }; game.up = function (x, y, obj) { var touchDuration = LK.ticks - touchStartTime; // Check if it was a long press (hold for 1+ seconds) if (touchDuration >= touchHoldThreshold) { // Long press - switch weapon player.switchWeapon(); updateWeaponIcons(); } else { // Short tap - shoot towards touch point player.shoot(x, y); } }; // Main game loop game.update = function () { // Update health display healthText.setText('Health: ' + player.health); healthText.fill = player.health > 50 ? "#00ff00" : player.health > 25 ? "#ffff00" : "#ff0000"; // Move player towards target player.moveTowards(targetX, targetY); // Check pistol bullet collisions with enemies for (var i = pistolBullets.length - 1; i >= 0; i--) { var bullet = pistolBullets[i]; for (var j = enemies.length - 1; j >= 0; j--) { var enemy = enemies[j]; if (bullet.intersects(enemy)) { enemy.takeDamage(bullet.damage); bullet.removeBullet(); break; } } } // Check shotgun bullet collisions with enemies (close range = high damage, far range = low damage) for (var i = shotgunBullets.length - 1; i >= 0; i--) { var bullet = shotgunBullets[i]; for (var j = enemies.length - 1; j >= 0; j--) { var enemy = enemies[j]; if (bullet.intersects(enemy)) { // Calculate damage based on distance traveled (closer = more damage) var damageMultiplier = Math.max(0.5, 1 - bullet.distanceTraveled / bullet.maxDistance); var actualDamage = Math.floor(bullet.damage * damageMultiplier * 4); // Up to 4x damage at close range enemy.takeDamage(actualDamage); bullet.removeBullet(); break; } } } // Check sniper bullet collisions with enemies (far range = high damage, close range = lower damage) for (var i = sniperBullets.length - 1; i >= 0; i--) { var bullet = sniperBullets[i]; for (var j = enemies.length - 1; j >= 0; j--) { var enemy = enemies[j]; if (bullet.intersects(enemy)) { // Calculate damage based on distance traveled (farther = more damage) var damageMultiplier = Math.min(2, 0.5 + bullet.distanceTraveled / 400); // Up to 2x damage at long range var actualDamage = Math.floor(bullet.damage * damageMultiplier); enemy.takeDamage(actualDamage); bullet.removeBullet(); break; } } } // Check old bullet collisions with enemies (keep for compatibility) for (var i = bullets.length - 1; i >= 0; i--) { var bullet = bullets[i]; var hitEnemy = false; for (var j = enemies.length - 1; j >= 0; j--) { var enemy = enemies[j]; if (bullet.intersects(enemy)) { enemy.takeDamage(bullet.damage); bullet.removeBullet(); hitEnemy = true; break; } } } // Check boss bullet collisions with player for (var i = bossBullets.length - 1; i >= 0; i--) { var bossBullet = bossBullets[i]; if (bossBullet.intersects(player)) { player.takeDamage(bossBullet.damage); bossBullet.removeBullet(); } } // Check laser collisions with player for (var i = lasers.length - 1; i >= 0; i--) { var laser = lasers[i]; if (laser.intersects(player)) { // Only damage if cooldown has passed if (LK.ticks - laser.lastDamage >= laser.damageCooldown) { player.takeDamage(laser.damage); laser.lastDamage = LK.ticks; } } } // Check flame collisions with player for (var i = flames.length - 1; i >= 0; i--) { var flame = flames[i]; if (flame.intersects(player)) { player.takeDamage(flame.damage); } } // Spawn boss when most enemies are killed (except in Hell mode where boss spawns immediately) var roomType = getRoomType(); if (!bossSpawned && roomType !== "Hell") { var spawnThreshold = roomType === "Tutorial" ? 0.8 : roomType === "Swarm" ? 0.9 : 0.7; if (enemiesKilled >= Math.floor(totalEnemiesInRoom * spawnThreshold)) { spawnBoss(); } } // Check if room is cleared (including boss) if (enemies.length === 0 && totalEnemiesInRoom > 0 && !roomCleared && bossKilled) { roomCleared = true; spawnChest(); } // Check chest interaction if (chest && !chest.opened && player.intersects(chest)) { chest.open(); } // Check weapon pickup for (var i = weapons.length - 1; i >= 0; i--) { var weapon = weapons[i]; if (player.intersects(weapon)) { weapon.pickup(); } } // Check health orb pickup for (var i = healthOrbs.length - 1; i >= 0; i--) { var healthOrb = healthOrbs[i]; if (player.intersects(healthOrb)) { healthOrb.pickup(); } } // Auto advance to next room after chest is opened and weapon picked up if (roomCleared && chest && chest.opened && weapons.length === 0) { if (LK.ticks % 180 === 0) { // Wait 3 seconds nextRoom(); } } };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Boss = Container.expand(function (type) {
var self = Container.call(this);
self.type = type || 'skeleton';
var assetName = self.type;
var graphics = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
// Make boss bigger
scaleY: 1.5
});
// Boss has much higher stats
switch (self.type) {
case 'skeleton':
self.health = 500 + currentRoom * 100;
self.speed = 1;
self.damage = 18;
break;
case 'zombie':
self.health = 600 + currentRoom * 120;
self.speed = 0.8;
self.damage = 24;
break;
case 'spider':
self.health = 400 + currentRoom * 80;
self.speed = 1.5;
self.damage = 15;
break;
}
self.maxHealth = self.health;
self.lastHealthPercent = 1.0; // Track last health percentage for half health detection
self.halfHealthOrbSpawned = false; // Prevent multiple orb spawns
// Create health bar
self.healthBarBg = self.attachAsset('wall', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 0.2,
y: -80
});
self.healthBarBg.tint = 0x666666;
self.healthBar = self.attachAsset('wall', {
anchorX: 0,
anchorY: 0.5,
scaleX: 2,
scaleY: 0.15,
x: -64,
y: -80
});
self.healthBar.tint = 0x00ff00;
self.lastAttack = 0;
self.attackRate = 45; // Faster attack rate
self.isBoss = true;
// Boss special attack timer
self.specialAttackTimer = 0;
self.specialAttackCooldown = 120; // 2 seconds at 60fps
self.takeDamage = function (damage) {
self.health -= damage;
// Update health bar
var healthPercent = self.health / self.maxHealth;
self.healthBar.scaleX = 2 * healthPercent;
// Check if boss health dropped to half (50% or below) for the first time
if (self.lastHealthPercent > 0.5 && healthPercent <= 0.5 && !self.halfHealthOrbSpawned) {
// Spawn health orb at random location
var healthOrb = new HealthOrb();
healthOrb.x = 300 + Math.random() * (2048 - 600); // Random x position avoiding walls
healthOrb.y = 400 + Math.random() * (2000 - 400); // Random y position avoiding walls
healthOrbs.push(healthOrb);
game.addChild(healthOrb);
self.halfHealthOrbSpawned = true; // Mark as spawned to prevent duplicates
}
// Update last health percentage for next comparison
self.lastHealthPercent = healthPercent;
// Change health bar color based on health
if (healthPercent > 0.6) {
self.healthBar.tint = 0x00ff00; // Green
} else if (healthPercent > 0.3) {
self.healthBar.tint = 0xffff00; // Yellow
} else {
self.healthBar.tint = 0xff0000; // Red
}
// Flash red when taking damage (different color for boss)
tween(graphics, {
tint: 0xFF4444
}, {
duration: 150,
onFinish: function onFinish() {
tween(graphics, {
tint: 0xFFFFFF
}, {
duration: 150
});
}
});
if (self.health <= 0) {
self.die();
}
};
self.die = function () {
LK.getSound('enemyDeath').play();
enemiesKilled++;
bossKilled = true;
// Remove from enemies array
for (var i = enemies.length - 1; i >= 0; i--) {
if (enemies[i] === self) {
enemies.splice(i, 1);
break;
}
}
self.destroy();
};
self.moveTowardsPlayer = function () {
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;
}
};
self.canAttack = function () {
return LK.ticks - self.lastAttack >= self.attackRate;
};
self.attackPlayer = function () {
if (self.canAttack()) {
player.takeDamage(self.damage);
self.lastAttack = LK.ticks;
}
};
self.weaponMode = 0; // 0 = scatter, 1 = laser, 2 = flamethrower
self.weaponTimer = 0;
self.weaponSwitchInterval = 120; // Switch weapons every 2 seconds
self.scatterAttack = function () {
// Shoot bigger bullets in all directions
var directions = 12;
for (var i = 0; i < directions; i++) {
var angle = i / directions * Math.PI * 2;
var bossBullet = new BossBullet();
bossBullet.x = self.x;
bossBullet.y = self.y;
bossBullet.velocityX = Math.cos(angle) * 4;
bossBullet.velocityY = Math.sin(angle) * 4;
bossBullets.push(bossBullet);
game.addChild(bossBullet);
}
};
self.laserAttack = function () {
// Create deadly lasers fired from boss position outward in different directions
for (var i = 0; i < 8; i++) {
var laser = new Laser();
laser.angle = i / 8 * Math.PI * 2; // Spread lasers in 8 directions
// Position laser at boss location
laser.x = self.x;
laser.y = self.y;
// Set laser rotation to point outward from boss
laser.rotation = laser.angle;
// Move laser outward from boss by half its length so one end stays at boss
var offsetDistance = 200; // Half of laser length (400/2)
laser.x += Math.cos(laser.angle) * offsetDistance;
laser.y += Math.sin(laser.angle) * offsetDistance;
lasers.push(laser);
game.addChild(laser);
}
};
self.flamethrowerAttack = function () {
// Spray flames randomly everywhere
for (var i = 0; i < 8; i++) {
var flame = new Flame();
flame.x = self.x;
flame.y = self.y;
var angle = Math.random() * Math.PI * 2;
var speed = 2 + Math.random() * 3;
flame.velocityX = Math.cos(angle) * speed;
flame.velocityY = Math.sin(angle) * speed;
flames.push(flame);
game.addChild(flame);
}
};
self.specialAttack = function () {
switch (self.weaponMode) {
case 0:
self.scatterAttack();
break;
case 1:
self.laserAttack();
break;
case 2:
self.flamethrowerAttack();
break;
}
self.specialAttackTimer = 0;
};
self.update = function () {
self.moveTowardsPlayer();
// Regular attack when close
var dx = player.x - self.x;
var dy = player.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 80) {
self.attackPlayer();
}
// Weapon switching timer
self.weaponTimer++;
if (self.weaponTimer >= self.weaponSwitchInterval) {
self.weaponMode = (self.weaponMode + 1) % 3; // Cycle through 0, 1, 2
self.weaponTimer = 0;
}
// Special attack timer
self.specialAttackTimer++;
if (self.specialAttackTimer >= self.specialAttackCooldown) {
self.specialAttack();
}
};
return self;
});
var BossBullet = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('bossBullet', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 0.6
});
self.velocityX = 0;
self.velocityY = 0;
self.damage = 12;
self.lifetime = 300; // 5 seconds at 60fps
self.age = 0;
self.update = function () {
self.x += self.velocityX;
self.y += self.velocityY;
self.age++;
// Remove if lifetime exceeded or off screen
if (self.age >= self.lifetime || self.x < 0 || self.x > 2048 || self.y < 0 || self.y > 2732) {
self.removeBullet();
}
};
self.removeBullet = function () {
for (var i = bossBullets.length - 1; i >= 0; i--) {
if (bossBullets[i] === self) {
bossBullets.splice(i, 1);
break;
}
}
self.destroy();
};
return self;
});
var Bullet = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.velocityX = 0;
self.velocityY = 0;
self.damage = 25;
self.update = function () {
self.x += self.velocityX;
self.y += self.velocityY;
// Remove if off screen
if (self.x < 0 || self.x > 2048 || self.y < 0 || self.y > 2732) {
self.removeBullet();
}
};
self.removeBullet = function () {
for (var i = bullets.length - 1; i >= 0; i--) {
if (bullets[i] === self) {
bullets.splice(i, 1);
break;
}
}
self.destroy();
};
return self;
});
var Chest = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('chest', {
anchorX: 0.5,
anchorY: 0.5
});
self.opened = false;
self.open = function () {
if (self.opened) return;
self.opened = true;
graphics.tint = 0x8B4513; // Darken to show it's opened
LK.getSound('chestOpen').play();
// Spawn weapon
var weapon = new Weapon();
weapon.x = self.x;
weapon.y = self.y - 50;
weapons.push(weapon);
game.addChild(weapon);
};
return self;
});
var Enemy = Container.expand(function (type) {
var self = Container.call(this);
self.type = type || 'skeleton';
var assetName = self.type;
var graphics = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
// Set properties based on enemy type
switch (self.type) {
case 'skeleton':
self.health = 30;
self.speed = 1.5;
self.damage = 15;
break;
case 'zombie':
self.health = 50;
self.speed = 1;
self.damage = 20;
break;
case 'spider':
self.health = 20;
self.speed = 2.5;
self.damage = 10;
break;
}
self.maxHealth = self.health;
self.lastAttack = 0;
self.attackRate = 60; // frames between attacks
self.takeDamage = function (damage) {
self.health -= damage;
// Flash white when taking damage
tween(graphics, {
tint: 0xFFFFFF
}, {
duration: 100,
onFinish: function onFinish() {
tween(graphics, {
tint: 0xFFFFFF
}, {
duration: 100
});
}
});
if (self.health <= 0) {
self.die();
}
};
self.die = function () {
LK.getSound('enemyDeath').play();
enemiesKilled++;
// Remove from enemies array
for (var i = enemies.length - 1; i >= 0; i--) {
if (enemies[i] === self) {
enemies.splice(i, 1);
break;
}
}
self.destroy();
};
self.moveTowardsPlayer = function () {
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;
}
};
self.canAttack = function () {
return LK.ticks - self.lastAttack >= self.attackRate;
};
self.attackPlayer = function () {
if (self.canAttack()) {
player.takeDamage(self.damage);
self.lastAttack = LK.ticks;
}
};
self.update = function () {
self.moveTowardsPlayer();
// Check if close enough to attack player
var dx = player.x - self.x;
var dy = player.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 60) {
self.attackPlayer();
}
};
return self;
});
var Flame = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('flame', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.7,
scaleY: 0.7
});
self.velocityX = 0;
self.velocityY = 0;
self.damage = 5;
self.lifetime = 180; // 3 seconds at 60fps
self.age = 0;
self.update = function () {
self.x += self.velocityX;
self.y += self.velocityY;
self.age++;
// Add random movement for flame effect
self.velocityX += (Math.random() - 0.5) * 0.5;
self.velocityY += (Math.random() - 0.5) * 0.5;
// Fade out over time
graphics.alpha = 1 - self.age / self.lifetime;
// Random scale variation
var scale = 0.5 + Math.random() * 0.5;
graphics.scaleX = scale;
graphics.scaleY = scale;
// Remove if lifetime exceeded
if (self.age >= self.lifetime) {
self.removeFlame();
}
};
self.removeFlame = function () {
for (var i = flames.length - 1; i >= 0; i--) {
if (flames[i] === self) {
flames.splice(i, 1);
break;
}
}
self.destroy();
};
return self;
});
var HealthOrb = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('healthOrb', {
anchorX: 0.5,
anchorY: 0.5
});
self.healAmount = 30;
self.pickup = function () {
// Restore player health
player.health = Math.min(player.maxHealth, player.health + self.healAmount);
LK.getSound('weaponPickup').play(); // Reuse weapon pickup sound
// Remove from healthOrbs array
for (var i = healthOrbs.length - 1; i >= 0; i--) {
if (healthOrbs[i] === self) {
healthOrbs.splice(i, 1);
break;
}
}
self.destroy();
};
return self;
});
var Laser = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('laser', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5
});
self.damage = 6;
self.lifetime = 60; // 1 second at 60fps
self.age = 0;
self.lastDamage = 0; // Track when damage was last dealt
self.damageCooldown = 30; // 0.5 seconds between damage
self.update = function () {
self.age++;
// Pulse effect
var pulse = Math.sin(self.age * 0.3) * 0.3 + 0.7;
graphics.alpha = pulse;
// Remove after lifetime
if (self.age >= self.lifetime) {
self.removeLaser();
}
};
self.removeLaser = function () {
for (var i = lasers.length - 1; i >= 0; i--) {
if (lasers[i] === self) {
lasers.splice(i, 1);
break;
}
}
self.destroy();
};
return self;
});
var PistolBullet = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8
});
self.velocityX = 0;
self.velocityY = 0;
self.damage = 25;
self.update = function () {
self.x += self.velocityX;
self.y += self.velocityY;
// Remove if off screen
if (self.x < 0 || self.x > 2048 || self.y < 0 || self.y > 2732) {
self.removeBullet();
}
};
self.removeBullet = function () {
for (var i = pistolBullets.length - 1; i >= 0; i--) {
if (pistolBullets[i] === self) {
pistolBullets.splice(i, 1);
break;
}
}
self.destroy();
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var legs = self.attachAsset('floor', {
anchorX: 0.5,
anchorY: 0,
x: 0,
y: 67,
scaleX: 0.8,
scaleY: 0.6
});
var body = self.attachAsset('playerBody', {
anchorX: 0.5,
anchorY: 0.5
});
var head = self.attachAsset('playerHead', {
anchorX: 0.5,
anchorY: 1.0,
x: 0,
y: -40
});
var graphics = body; // Keep reference for damage effects
// Add weapon graphics to player
var pistolGraphic = self.attachAsset('pistol', {
anchorX: 0,
anchorY: 0.5,
x: 40,
y: 0,
scaleX: 2,
scaleY: 2
});
var shotgunGraphic = self.attachAsset('shotgun', {
anchorX: 0,
anchorY: 0.5,
x: 40,
y: 0,
scaleX: 2,
scaleY: 2
});
var sniperGraphic = self.attachAsset('sniper', {
anchorX: 0,
anchorY: 0.5,
x: 40,
y: 0,
scaleX: 2,
scaleY: 2
});
// Create smaller hitbox for collision detection
self.hitbox = self.attachAsset('playerBody', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 0.6,
alpha: 0 // Make invisible
});
self.health = 100;
self.maxHealth = 100;
self.speed = 4;
self.weapon = 'pistol'; // Start with pistol
self.currentWeaponIndex = 0;
self.weapons = ['pistol', 'shotgun', 'sniper'];
self.fireRate = 15; // frames between shots
self.lastShot = 0;
self.takeDamage = function (damage) {
self.health -= damage;
if (self.health <= 0) {
self.health = 0;
LK.showGameOver();
}
// Flash red when taking damage
tween(graphics, {
tint: 0xFF0000
}, {
duration: 200,
onFinish: function onFinish() {
tween(graphics, {
tint: 0xFFFFFF
}, {
duration: 200
});
}
});
};
self.moveTowards = function (targetX, targetY) {
var dx = targetX - self.x;
var dy = targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 10) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
// Keep player within room bounds
self.x = Math.max(128, Math.min(2048 - 128, self.x));
self.y = Math.max(200, Math.min(2400, self.y));
}
// Update weapon rotation to face target direction
var angle = Math.atan2(dy, dx);
pistolGraphic.rotation = angle;
shotgunGraphic.rotation = angle;
sniperGraphic.rotation = angle;
};
self.canShoot = function () {
return LK.ticks - self.lastShot >= self.fireRate;
};
self.shoot = function (targetX, targetY) {
if (!self.canShoot()) return;
var dx = targetX - self.x;
var dy = targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
var normalizedX = dx / distance;
var normalizedY = dy / distance;
if (self.weapon === 'pistol') {
var bullet = new PistolBullet();
bullet.x = self.x;
bullet.y = self.y;
bullet.velocityX = normalizedX * 8;
bullet.velocityY = normalizedY * 8;
pistolBullets.push(bullet);
game.addChild(bullet);
self.fireRate = 30;
} else if (self.weapon === 'shotgun') {
// Fire 3 bullets in spread pattern
for (var i = 0; i < 3; i++) {
var bullet = new ShotgunBullet();
bullet.x = self.x;
bullet.y = self.y;
// Add spread to the shots
var spreadAngle = (i - 1) * 0.3; // -0.3, 0, 0.3 radians spread
var angle = Math.atan2(dy, dx) + spreadAngle;
bullet.velocityX = Math.cos(angle) * 6;
bullet.velocityY = Math.sin(angle) * 6;
shotgunBullets.push(bullet);
game.addChild(bullet);
}
self.fireRate = 120; // 2 second cooldown
} else if (self.weapon === 'sniper') {
var bullet = new SniperBullet();
bullet.x = self.x;
bullet.y = self.y;
bullet.velocityX = normalizedX * 12; // Faster bullet
bullet.velocityY = normalizedY * 12;
sniperBullets.push(bullet);
game.addChild(bullet);
self.fireRate = 180; // 3 second cooldown
}
self.lastShot = LK.ticks;
LK.getSound('shoot').play();
};
self.updateWeaponGraphics = function () {
// Hide all weapons first
pistolGraphic.alpha = 0;
shotgunGraphic.alpha = 0;
sniperGraphic.alpha = 0;
// Show current weapon
switch (self.weapon) {
case 'pistol':
pistolGraphic.alpha = 1;
break;
case 'shotgun':
shotgunGraphic.alpha = 1;
break;
case 'sniper':
sniperGraphic.alpha = 1;
break;
}
};
self.switchWeapon = function () {
self.currentWeaponIndex = (self.currentWeaponIndex + 1) % self.weapons.length;
self.weapon = self.weapons[self.currentWeaponIndex];
// Update weapon indicator
weaponText.setText('Weapon: ' + self.weapon.charAt(0).toUpperCase() + self.weapon.slice(1));
// Update weapon graphics
self.updateWeaponGraphics();
};
// Override intersects method to use smaller hitbox
self.intersects = function (other) {
return self.hitbox.intersects(other);
};
// Initialize weapon graphics
self.updateWeaponGraphics();
return self;
});
var ShotgunBullet = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 0.6
});
self.velocityX = 0;
self.velocityY = 0;
self.damage = 15;
self.distanceTraveled = 0;
self.maxDistance = 300; // Shotgun has limited range
self.update = function () {
self.x += self.velocityX;
self.y += self.velocityY;
self.distanceTraveled += Math.sqrt(self.velocityX * self.velocityX + self.velocityY * self.velocityY);
// Remove if off screen or max distance reached
if (self.x < 0 || self.x > 2048 || self.y < 0 || self.y > 2732 || self.distanceTraveled > self.maxDistance) {
self.removeBullet();
}
};
self.removeBullet = function () {
for (var i = shotgunBullets.length - 1; i >= 0; i--) {
if (shotgunBullets[i] === self) {
shotgunBullets.splice(i, 1);
break;
}
}
self.destroy();
};
return self;
});
var SniperBullet = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2
});
graphics.tint = 0xffd700; // Golden color for sniper bullets
self.velocityX = 0;
self.velocityY = 0;
self.damage = 60; // High damage for long range
self.distanceTraveled = 0;
self.update = function () {
self.x += self.velocityX;
self.y += self.velocityY;
self.distanceTraveled += Math.sqrt(self.velocityX * self.velocityX + self.velocityY * self.velocityY);
// Remove if off screen
if (self.x < 0 || self.x > 2048 || self.y < 0 || self.y > 2732) {
self.removeBullet();
}
};
self.removeBullet = function () {
for (var i = sniperBullets.length - 1; i >= 0; i--) {
if (sniperBullets[i] === self) {
sniperBullets.splice(i, 1);
break;
}
}
self.destroy();
};
return self;
});
var Weapon = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('weapon', {
anchorX: 0.5,
anchorY: 0.5
});
self.weaponType = 'rifle';
self.pickup = function () {
player.weapon = self.weaponType;
player.fireRate = Math.max(5, player.fireRate - 3); // Faster firing
LK.getSound('weaponPickup').play();
// Update weapon indicator
weaponText.setText('Weapon: ' + self.weaponType.charAt(0).toUpperCase() + self.weaponType.slice(1));
// Update weapon graphics
player.updateWeaponGraphics();
// Remove from weapons array
for (var i = weapons.length - 1; i >= 0; i--) {
if (weapons[i] === self) {
weapons.splice(i, 1);
break;
}
}
self.destroy();
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a1a1a
});
/****
* Game Code
****/
// Game variables
var player;
var enemies = [];
var bullets = [];
var pistolBullets = [];
var shotgunBullets = [];
var sniperBullets = [];
var bossBullets = [];
var lasers = [];
var flames = [];
var weapons = [];
var healthOrbs = [];
var chest;
var currentRoom = 1;
var enemiesKilled = 0;
var totalEnemiesInRoom = 0;
var roomCleared = false;
var targetX = 1024;
var targetY = 1366;
var bossSpawned = false;
var bossKilled = false;
// UI elements
var scoreText = new Text2('Room: 1', {
size: 60,
fill: 0xFFFFFF
});
scoreText.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreText);
var healthText = new Text2('Health: 100', {
size: 50,
fill: 0x00FF00
});
healthText.anchor.set(0, 0);
healthText.x = 20;
healthText.y = 20;
LK.gui.topLeft.addChild(healthText);
// Weapon indicator in top right
var weaponText = new Text2('Weapon: Pistol', {
size: 50,
fill: 0xFFFFFF
});
weaponText.anchor.set(1, 0);
weaponText.y = 20;
LK.gui.topRight.addChild(weaponText);
// Visual weapon icons
var pistolIcon = LK.getAsset('pistol', {
anchorX: 1,
anchorY: 0,
x: -20,
y: 80
});
var shotgunIcon = LK.getAsset('shotgun', {
anchorX: 1,
anchorY: 0,
x: -20,
y: 120
});
var sniperIcon = LK.getAsset('sniper', {
anchorX: 1,
anchorY: 0,
x: -20,
y: 160
});
LK.gui.topRight.addChild(pistolIcon);
LK.gui.topRight.addChild(shotgunIcon);
LK.gui.topRight.addChild(sniperIcon);
// Function to update weapon icon highlights
function updateWeaponIcons() {
pistolIcon.alpha = player.weapon === 'pistol' ? 1.0 : 0.5;
shotgunIcon.alpha = player.weapon === 'shotgun' ? 1.0 : 0.5;
sniperIcon.alpha = player.weapon === 'sniper' ? 1.0 : 0.5;
}
// Create floor tiles
function createRoom() {
// Clear existing room elements
roomCleared = false;
enemiesKilled = 0;
// Create floor
for (var x = 0; x < 32; x++) {
for (var y = 3; y < 38; y++) {
var floor = game.addChild(LK.getAsset('floor', {
x: x * 64,
y: y * 64
}));
}
}
// Create walls around the room
for (var x = 0; x < 32; x++) {
// Top wall
var topWall = game.addChild(LK.getAsset('wall', {
x: x * 64,
y: 2 * 64
}));
// Bottom wall
var bottomWall = game.addChild(LK.getAsset('wall', {
x: x * 64,
y: 38 * 64
}));
}
for (var y = 2; y < 39; y++) {
// Left wall
var leftWall = game.addChild(LK.getAsset('wall', {
x: 0,
y: y * 64
}));
// Right wall
var rightWall = game.addChild(LK.getAsset('wall', {
x: 31 * 64,
y: y * 64
}));
}
}
function getRoomType() {
if (currentRoom <= 20) return "Tutorial";
if (currentRoom <= 40) return "Swarm";
if (currentRoom <= 60) return "Elite";
if (currentRoom <= 80) return "Nightmare";
return "Hell";
}
function spawnEnemiesForRoomType() {
var roomType = getRoomType();
bossSpawned = false;
bossKilled = false;
switch (roomType) {
case "Tutorial":
spawnTutorialEnemies();
break;
case "Swarm":
spawnSwarmEnemies();
break;
case "Elite":
spawnEliteEnemies();
break;
case "Nightmare":
spawnNightmareEnemies();
break;
case "Hell":
spawnHellEnemies();
break;
}
}
function spawnTutorialEnemies() {
var enemyTypes = ['skeleton', 'zombie', 'spider'];
var numEnemies = Math.min(5 + currentRoom * 2, 15);
totalEnemiesInRoom = numEnemies;
for (var i = 0; i < numEnemies; i++) {
var enemyType = enemyTypes[Math.floor(Math.random() * enemyTypes.length)];
var enemy = new Enemy(enemyType);
// Random position in room, avoiding walls
enemy.x = 200 + Math.random() * (2048 - 400);
enemy.y = 300 + Math.random() * (2200 - 400);
enemies.push(enemy);
game.addChild(enemy);
}
}
function spawnSwarmEnemies() {
// Rooms 21-40: More enemies, faster movement
var enemyTypes = ['skeleton', 'zombie', 'spider'];
var numEnemies = Math.min(8 + (currentRoom - 20) * 3, 25);
totalEnemiesInRoom = numEnemies;
for (var i = 0; i < numEnemies; i++) {
var enemyType = enemyTypes[Math.floor(Math.random() * enemyTypes.length)];
var enemy = new Enemy(enemyType);
// Boost stats for swarm section
enemy.speed *= 1.5;
enemy.health = Math.floor(enemy.health * 0.7); // Less health but more enemies
enemy.x = 200 + Math.random() * (2048 - 400);
enemy.y = 300 + Math.random() * (2200 - 400);
enemies.push(enemy);
game.addChild(enemy);
}
}
function spawnEliteEnemies() {
// Rooms 41-60: Fewer but much stronger enemies
var enemyTypes = ['skeleton', 'zombie', 'spider'];
var numEnemies = Math.min(3 + Math.floor((currentRoom - 40) / 2), 8);
totalEnemiesInRoom = numEnemies;
for (var i = 0; i < numEnemies; i++) {
var enemyType = enemyTypes[Math.floor(Math.random() * enemyTypes.length)];
var enemy = new Enemy(enemyType);
// Elite stats - much stronger
enemy.health *= 3;
enemy.maxHealth = enemy.health;
enemy.damage *= 2;
enemy.speed *= 1.2;
// Make them visually bigger to show they're elite
enemy.scaleX = 1.5;
enemy.scaleY = 1.5;
enemy.x = 200 + Math.random() * (2048 - 400);
enemy.y = 300 + Math.random() * (2200 - 400);
enemies.push(enemy);
game.addChild(enemy);
}
}
function spawnNightmareEnemies() {
// Rooms 61-80: Mix of swarm and elite + boss every 5 rooms
var enemyTypes = ['skeleton', 'zombie', 'spider'];
var numRegular = Math.min(6 + Math.floor((currentRoom - 60) / 3), 12);
var numElite = Math.min(2 + Math.floor((currentRoom - 60) / 5), 4);
totalEnemiesInRoom = numRegular + numElite;
// Spawn regular enemies
for (var i = 0; i < numRegular; i++) {
var enemyType = enemyTypes[Math.floor(Math.random() * enemyTypes.length)];
var enemy = new Enemy(enemyType);
enemy.speed *= 1.3;
enemy.health *= 1.5;
enemy.maxHealth = enemy.health;
enemy.x = 200 + Math.random() * (2048 - 400);
enemy.y = 300 + Math.random() * (2200 - 400);
enemies.push(enemy);
game.addChild(enemy);
}
// Spawn elite enemies
for (var i = 0; i < numElite; i++) {
var enemyType = enemyTypes[Math.floor(Math.random() * enemyTypes.length)];
var enemy = new Enemy(enemyType);
enemy.health *= 4;
enemy.maxHealth = enemy.health;
enemy.damage *= 2.5;
enemy.speed *= 1.4;
enemy.scaleX = 1.8;
enemy.scaleY = 1.8;
enemy.x = 200 + Math.random() * (2048 - 400);
enemy.y = 300 + Math.random() * (2200 - 400);
enemies.push(enemy);
game.addChild(enemy);
}
}
function spawnHellEnemies() {
// Rooms 81-100: Ultimate challenge - everything is maxed
var enemyTypes = ['skeleton', 'zombie', 'spider'];
var numRegular = Math.min(10 + (currentRoom - 80), 20);
var numElite = Math.min(3 + Math.floor((currentRoom - 80) / 2), 6);
var numBosses = currentRoom % 10 === 0 ? 2 : 1; // Double boss every 10th room
totalEnemiesInRoom = numRegular + numElite + numBosses;
bossSpawned = true; // Always spawn boss in hell mode
bossKilled = false;
// Spawn regular enemies
for (var i = 0; i < numRegular; i++) {
var enemyType = enemyTypes[Math.floor(Math.random() * enemyTypes.length)];
var enemy = new Enemy(enemyType);
enemy.speed *= 2;
enemy.health *= 2;
enemy.maxHealth = enemy.health;
enemy.damage *= 1.5;
enemy.x = 200 + Math.random() * (2048 - 400);
enemy.y = 300 + Math.random() * (2200 - 400);
enemies.push(enemy);
game.addChild(enemy);
}
// Spawn elite enemies
for (var i = 0; i < numElite; i++) {
var enemyType = enemyTypes[Math.floor(Math.random() * enemyTypes.length)];
var enemy = new Enemy(enemyType);
enemy.health *= 6;
enemy.maxHealth = enemy.health;
enemy.damage *= 3;
enemy.speed *= 1.8;
enemy.scaleX = 2;
enemy.scaleY = 2;
enemy.x = 200 + Math.random() * (2048 - 400);
enemy.y = 300 + Math.random() * (2200 - 400);
enemies.push(enemy);
game.addChild(enemy);
}
// Spawn boss(es)
for (var i = 0; i < numBosses; i++) {
var bossType = enemyTypes[Math.floor(Math.random() * enemyTypes.length)];
var boss = new Boss(bossType);
// Hell mode boss gets extra stats
boss.health *= 2;
boss.maxHealth = boss.health;
boss.damage *= 1.5;
boss.speed *= 1.3;
boss.scaleX = 2.5;
boss.scaleY = 2.5;
boss.x = 1024 + (i * 200 - 100); // Spread multiple bosses
boss.y = 1000;
enemies.push(boss);
game.addChild(boss);
}
}
function spawnEnemies() {
spawnTutorialEnemies();
}
function spawnBoss() {
if (!bossSpawned) {
var enemyTypes = ['skeleton', 'zombie', 'spider'];
var bossType = enemyTypes[Math.floor(Math.random() * enemyTypes.length)];
var boss = new Boss(bossType);
// Scale boss based on room type
var roomType = getRoomType();
if (roomType === "Elite") {
boss.health *= 1.5;
boss.maxHealth = boss.health;
boss.damage *= 1.3;
} else if (roomType === "Nightmare") {
boss.health *= 2;
boss.maxHealth = boss.health;
boss.damage *= 1.5;
boss.speed *= 1.2;
} else if (roomType === "Hell") {
boss.health *= 3;
boss.maxHealth = boss.health;
boss.damage *= 2;
boss.speed *= 1.5;
boss.scaleX = 2;
boss.scaleY = 2;
}
// Spawn boss in center of room
boss.x = 1024;
boss.y = 1000;
enemies.push(boss);
game.addChild(boss);
bossSpawned = true;
totalEnemiesInRoom++; // Add boss to total count
}
}
function spawnChest() {
chest = new Chest();
chest.x = 1024;
chest.y = 1366;
game.addChild(chest);
// Spawn health orb at random location in room
var healthOrb = new HealthOrb();
healthOrb.x = 300 + Math.random() * (2048 - 600); // Random x position avoiding walls
healthOrb.y = 400 + Math.random() * (2000 - 400); // Random y position avoiding walls
healthOrbs.push(healthOrb);
game.addChild(healthOrb);
}
function nextRoom() {
currentRoom++;
if (currentRoom > 100) {
// Player has completed all 100 rooms - show victory
LK.showYouWin();
return;
}
scoreText.setText('Room: ' + currentRoom + ' - ' + getRoomType());
// Clear current enemies and bullets
for (var i = enemies.length - 1; i >= 0; i--) {
enemies[i].destroy();
}
enemies = [];
for (var i = bullets.length - 1; i >= 0; i--) {
bullets[i].destroy();
}
bullets = [];
// Clear pistol bullets
for (var i = pistolBullets.length - 1; i >= 0; i--) {
pistolBullets[i].destroy();
}
pistolBullets = [];
// Clear shotgun bullets
for (var i = shotgunBullets.length - 1; i >= 0; i--) {
shotgunBullets[i].destroy();
}
shotgunBullets = [];
// Clear sniper bullets
for (var i = sniperBullets.length - 1; i >= 0; i--) {
sniperBullets[i].destroy();
}
sniperBullets = [];
// Clear boss projectiles
for (var i = bossBullets.length - 1; i >= 0; i--) {
bossBullets[i].destroy();
}
bossBullets = [];
for (var i = lasers.length - 1; i >= 0; i--) {
lasers[i].destroy();
}
lasers = [];
for (var i = flames.length - 1; i >= 0; i--) {
flames[i].destroy();
}
flames = [];
// Remove chest if exists
if (chest) {
chest.destroy();
chest = null;
}
// Clear health orbs
for (var i = healthOrbs.length - 1; i >= 0; i--) {
healthOrbs[i].destroy();
}
healthOrbs = [];
// Spawn enemies based on room type
spawnEnemiesForRoomType();
}
// Initialize first room
createRoom();
// Create player
player = new Player();
player.x = 1024;
player.y = 2000;
game.addChild(player);
// Spawn initial enemies
spawnEnemies();
// Initialize weapon icons
updateWeaponIcons();
// Touch controls
game.move = function (x, y, obj) {
targetX = x;
targetY = y;
};
// Right mouse button support for weapon switching
game.rightDown = function (x, y, obj) {
// Right click - switch weapon
player.switchWeapon();
updateWeaponIcons();
};
var touchStartTime = 0;
var touchHoldThreshold = 60; // 1 second hold at 60fps
game.down = function (x, y, obj) {
targetX = x;
targetY = y;
touchStartTime = LK.ticks; // Record when touch started
};
game.up = function (x, y, obj) {
var touchDuration = LK.ticks - touchStartTime;
// Check if it was a long press (hold for 1+ seconds)
if (touchDuration >= touchHoldThreshold) {
// Long press - switch weapon
player.switchWeapon();
updateWeaponIcons();
} else {
// Short tap - shoot towards touch point
player.shoot(x, y);
}
};
// Main game loop
game.update = function () {
// Update health display
healthText.setText('Health: ' + player.health);
healthText.fill = player.health > 50 ? "#00ff00" : player.health > 25 ? "#ffff00" : "#ff0000";
// Move player towards target
player.moveTowards(targetX, targetY);
// Check pistol bullet collisions with enemies
for (var i = pistolBullets.length - 1; i >= 0; i--) {
var bullet = pistolBullets[i];
for (var j = enemies.length - 1; j >= 0; j--) {
var enemy = enemies[j];
if (bullet.intersects(enemy)) {
enemy.takeDamage(bullet.damage);
bullet.removeBullet();
break;
}
}
}
// Check shotgun bullet collisions with enemies (close range = high damage, far range = low damage)
for (var i = shotgunBullets.length - 1; i >= 0; i--) {
var bullet = shotgunBullets[i];
for (var j = enemies.length - 1; j >= 0; j--) {
var enemy = enemies[j];
if (bullet.intersects(enemy)) {
// Calculate damage based on distance traveled (closer = more damage)
var damageMultiplier = Math.max(0.5, 1 - bullet.distanceTraveled / bullet.maxDistance);
var actualDamage = Math.floor(bullet.damage * damageMultiplier * 4); // Up to 4x damage at close range
enemy.takeDamage(actualDamage);
bullet.removeBullet();
break;
}
}
}
// Check sniper bullet collisions with enemies (far range = high damage, close range = lower damage)
for (var i = sniperBullets.length - 1; i >= 0; i--) {
var bullet = sniperBullets[i];
for (var j = enemies.length - 1; j >= 0; j--) {
var enemy = enemies[j];
if (bullet.intersects(enemy)) {
// Calculate damage based on distance traveled (farther = more damage)
var damageMultiplier = Math.min(2, 0.5 + bullet.distanceTraveled / 400); // Up to 2x damage at long range
var actualDamage = Math.floor(bullet.damage * damageMultiplier);
enemy.takeDamage(actualDamage);
bullet.removeBullet();
break;
}
}
}
// Check old bullet collisions with enemies (keep for compatibility)
for (var i = bullets.length - 1; i >= 0; i--) {
var bullet = bullets[i];
var hitEnemy = false;
for (var j = enemies.length - 1; j >= 0; j--) {
var enemy = enemies[j];
if (bullet.intersects(enemy)) {
enemy.takeDamage(bullet.damage);
bullet.removeBullet();
hitEnemy = true;
break;
}
}
}
// Check boss bullet collisions with player
for (var i = bossBullets.length - 1; i >= 0; i--) {
var bossBullet = bossBullets[i];
if (bossBullet.intersects(player)) {
player.takeDamage(bossBullet.damage);
bossBullet.removeBullet();
}
}
// Check laser collisions with player
for (var i = lasers.length - 1; i >= 0; i--) {
var laser = lasers[i];
if (laser.intersects(player)) {
// Only damage if cooldown has passed
if (LK.ticks - laser.lastDamage >= laser.damageCooldown) {
player.takeDamage(laser.damage);
laser.lastDamage = LK.ticks;
}
}
}
// Check flame collisions with player
for (var i = flames.length - 1; i >= 0; i--) {
var flame = flames[i];
if (flame.intersects(player)) {
player.takeDamage(flame.damage);
}
}
// Spawn boss when most enemies are killed (except in Hell mode where boss spawns immediately)
var roomType = getRoomType();
if (!bossSpawned && roomType !== "Hell") {
var spawnThreshold = roomType === "Tutorial" ? 0.8 : roomType === "Swarm" ? 0.9 : 0.7;
if (enemiesKilled >= Math.floor(totalEnemiesInRoom * spawnThreshold)) {
spawnBoss();
}
}
// Check if room is cleared (including boss)
if (enemies.length === 0 && totalEnemiesInRoom > 0 && !roomCleared && bossKilled) {
roomCleared = true;
spawnChest();
}
// Check chest interaction
if (chest && !chest.opened && player.intersects(chest)) {
chest.open();
}
// Check weapon pickup
for (var i = weapons.length - 1; i >= 0; i--) {
var weapon = weapons[i];
if (player.intersects(weapon)) {
weapon.pickup();
}
}
// Check health orb pickup
for (var i = healthOrbs.length - 1; i >= 0; i--) {
var healthOrb = healthOrbs[i];
if (player.intersects(healthOrb)) {
healthOrb.pickup();
}
}
// Auto advance to next room after chest is opened and weapon picked up
if (roomCleared && chest && chest.opened && weapons.length === 0) {
if (LK.ticks % 180 === 0) {
// Wait 3 seconds
nextRoom();
}
}
};
pixelart bullet. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
pixel art gray weepon. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
pixelart skeleton. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
pixelart cave spider. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
pixelart zombies. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
pixelart fire. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
pixelart ıce bullet. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
pixelart powerfull laser. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
pixelart sniper. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
pixelart sniper. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
pixelart human head. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
pixelart military armor. In-Game asset. 2d. High contrast. No shadows
pixel art green orb. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat