/****
* 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