User prompt
reduce the boss health ind 7th dungeon
User prompt
make the boss in dungeon 7 die after all of his soldiers die
User prompt
make the dungeon 7 boss die after all of its soldiers die
User prompt
Every time an enemy is killed, the money automatically increases and the amount of money increases depending on the difficulty of the enemy. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Every time a character is killed, the money automatically increases and the amount of money increases depending on the difficulty of the enemy. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
character is going to left by itself right now can you fix it
User prompt
character is going to left by itself right now can you fix it
User prompt
can you fix the caharecter going left by itself
User prompt
Please fix the bug: 'TypeError: Cannot read properties of undefined (reading 'update')' in or related to this line: 'coins[i].update();' Line Number: 5013
User prompt
Center the upgrade menu on the screen so that the entire menu is visible ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Center the upgrade menu on the screen so that the entire menu is visible
User prompt
Put the upgrade menu to top left corner
User prompt
Put the upgrade menu to top left corner
User prompt
Put the upgrade menu to center of the screen
User prompt
put rhe upgrades menu center of the screen
User prompt
rearrange the upgrades buttn and the upgrades ui position for better visibilty
User prompt
- Create an upgrade tree with categories: Combat, Defense, Utility - Combat upgrades: increased damage, faster attacks, knife penetration - Defense upgrades: more health, damage reduction, temporary invincibility - Utility upgrades: faster movement, more starting knives, coin magnetism ↪💡 Consider importing and using the following plugins: @upit/storage.v1, @upit/tween.v1
User prompt
- **Scouts**: Fast enemies that alert others and flee from combat - **Berserkers**: Enemies that become more dangerous when low on health - **Archers**: Long-range enemies that keep distance and shoot projectiles - **Shields**: Enemies that block projectiles and protect others ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
- Implement group AI where enemies communicate their states - Add flanking behavior where enemies try to surround the player - Create "leader" enemies that buff nearby allies - Implement pack hunting where fast enemies herd the player toward stronger ones ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
- Implement damage numbers that float up from enemies when hit - Add screen flash effects for critical hits or boss phase transitions - Create weapon trail effects for the hero's sword attacks - Add glowing outlines to indicate different enemy states (alerted, enraged, etc.) ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
- Expand blood particles with different sizes, speeds, and lifetimes - Add impact sparks when knives hit enemies - Create dust clouds when enemies or player move quickly - Add magical effects for power-ups and special abilities ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
- Create a `ScreenShake` class that applies temporary camera offset modifications - Add shake on: boss attacks, player hits, enemy deaths, knife impacts - Different shake intensities and durations for different events - Smooth shake decay using tween easing functions ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
- Add state machines to bosses: `IDLE`, `PURSUING`, `ATTACKING`, `STUNNED`, `ENRAGED` - Implement predictive targeting where bosses anticipate player movement - Add boss "tells" - visual warnings before major attacks - Create vulnerability windows where bosses can take extra damage after certain attacks ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
can you implement the second part(2. Enhanced Boss Mechanics and Variety (Priority 2)) ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
yes can you please implement everything you listed to me ↪💡 Consider importing and using the following plugins: @upit/tween.v1
/**** 
* Plugins
****/ 
var tween = LK.import("@upit/tween.v1");
/**** 
* Classes
****/ 
var BloodParticle = Container.expand(function () {
	var self = Container.call(this);
	var particleGraphics = self.attachAsset('bloodParticle', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	self.velocityX = (Math.random() - 0.5) * 8;
	self.velocityY = -Math.random() * 6 - 2;
	self.gravity = 0.3;
	self.lifetime = 60; // 1 second at 60fps
	self.reset = function (startX, startY) {
		self.x = startX;
		self.y = startY;
		self.velocityX = (Math.random() - 0.5) * 8;
		self.velocityY = -Math.random() * 6 - 2;
		self.lifetime = 60;
		particleGraphics.alpha = 1;
		self.visible = true;
	};
	self.update = function () {
		self.x += self.velocityX;
		self.y += self.velocityY;
		self.velocityY += self.gravity;
		// Fade out over time
		self.lifetime--;
		particleGraphics.alpha = self.lifetime / 60;
		if (self.lifetime <= 0) {
			// Remove from bloodParticles array
			for (var i = bloodParticles.length - 1; i >= 0; i--) {
				if (bloodParticles[i] === self) {
					bloodParticles.splice(i, 1);
					break;
				}
			}
			returnToPool(self, 'bloodParticle');
		}
	};
	return self;
});
var Boss = Container.expand(function () {
	var self = Container.call(this);
	var bossGraphics = self.attachAsset('boss', {
		anchorX: 0.5,
		anchorY: 1.0
	});
	self.health = 50;
	self.maxHealth = 50;
	self.speed = 3; // Increased base speed
	self.attackCooldown = 0;
	self.specialAttackCooldown = 0;
	self.phase = 1; // Boss has 3 phases
	self.lastX = 0;
	self.lastY = 0;
	self.isEnraged = false;
	self.projectileAttackCooldown = 0;
	self.chargeAttackCooldown = 0;
	self.isCharging = false;
	self.chargingTarget = null;
	self.spinAttackCooldown = 0;
	self.isSpinning = false;
	self.update = function () {
		var distanceToHero = Math.sqrt(Math.pow(self.x - hero.x, 2) + Math.pow(self.y - hero.y, 2));
		// Different behavior based on health phases
		if (self.health > self.maxHealth * 0.66) {
			self.phase = 1;
			bossGraphics.tint = 0xffffff; // Normal color
		} else if (self.health > self.maxHealth * 0.33) {
			self.phase = 2;
			bossGraphics.tint = 0xffaa00; // Orange when damaged
			if (!self.isEnraged) {
				self.isEnraged = true;
				self.speed *= 2; // Double speed in phase 2
				// Visual effect for enrage
				tween(bossGraphics, {
					scaleX: 1.2,
					scaleY: 1.2
				}, {
					duration: 300,
					easing: tween.bounceOut
				});
				tween(bossGraphics, {
					scaleX: 1,
					scaleY: 1
				}, {
					duration: 300,
					easing: tween.bounceOut
				});
			}
		} else {
			self.phase = 3;
			bossGraphics.tint = 0xff0000; // Red when critically damaged
			if (self.speed < 5) {
				self.speed = 5; // Maximum speed in final phase
				// Intense visual effect for final phase
				tween(bossGraphics, {
					scaleX: 1.3,
					scaleY: 1.3
				}, {
					duration: 200,
					easing: tween.elasticOut
				});
				tween(bossGraphics, {
					scaleX: 1,
					scaleY: 1
				}, {
					duration: 200,
					easing: tween.elasticOut
				});
			}
		}
		// Chase hero
		var dx = hero.x - self.x;
		var dy = hero.y - self.y;
		var distance = Math.sqrt(dx * dx + dy * dy);
		if (distance > 0) {
			self.lastX = self.x;
			self.lastY = self.y;
			self.x += dx / distance * self.speed;
			self.y += dy / distance * self.speed;
			// Face direction of movement
			bossGraphics.scaleX = dx > 0 ? 1 : -1;
		}
		// Melee attack
		if (distanceToHero < 150 && self.attackCooldown <= 0) {
			hero.takeDamage();
			self.attackCooldown = 90; // 1.5 seconds
			LK.effects.flashObject(self, 0xffffff, 200);
		}
		// Projectile attack (phase 2 and 3)
		if (self.phase >= 2 && self.projectileAttackCooldown <= 0 && distanceToHero > 200) {
			self.fireProjectile();
			self.projectileAttackCooldown = self.phase === 3 ? 60 : 120; // Faster in phase 3
		}
		// Charge attack (phase 2 and 3)
		if (self.phase >= 2 && self.chargeAttackCooldown <= 0 && distanceToHero > 300 && !self.isCharging) {
			self.chargeAttack();
			self.chargeAttackCooldown = self.phase === 3 ? 240 : 360; // Faster in phase 3
		}
		// Spin attack (phase 3 only)
		if (self.phase === 3 && self.spinAttackCooldown <= 0 && distanceToHero < 250 && !self.isSpinning) {
			self.spinAttack();
			self.spinAttackCooldown = 420; // 7 seconds
		}
		// Special area attack (phase 3 only)
		if (self.phase === 3 && self.specialAttackCooldown <= 0) {
			self.areaAttack();
			self.specialAttackCooldown = 300; // 5 seconds
		}
		if (self.attackCooldown > 0) self.attackCooldown--;
		if (self.specialAttackCooldown > 0) self.specialAttackCooldown--;
		if (self.projectileAttackCooldown > 0) self.projectileAttackCooldown--;
		if (self.chargeAttackCooldown > 0) self.chargeAttackCooldown--;
		if (self.spinAttackCooldown > 0) self.spinAttackCooldown--;
	};
	self.fireProjectile = function () {
		// Visual charging effect before firing
		tween(bossGraphics, {
			tint: 0x8800ff
		}, {
			duration: 200
		});
		tween(bossGraphics, {
			tint: 0xffffff
		}, {
			duration: 200
		});
		var projectile = game.addChild(new BossProjectile());
		projectile.x = self.x;
		projectile.y = self.y - 200;
		// Calculate direction to hero
		var dx = hero.x - self.x;
		var dy = hero.y - self.y;
		var distance = Math.sqrt(dx * dx + dy * dy);
		if (distance > 0) {
			var speed = self.phase === 3 ? 12 : 8; // Faster projectiles in phase 3
			projectile.velocityX = dx / distance * speed;
			projectile.velocityY = dy / distance * speed;
		}
		// Add visual trail effect to projectile
		var projectileGraphics = projectile.getChildAt(0);
		tween(projectileGraphics, {
			tint: 0xff4444
		}, {
			duration: 100
		});
		tween(projectileGraphics, {
			tint: 0xffffff
		}, {
			duration: 100
		});
		bossProjectiles.push(projectile);
	};
	self.chargeAttack = function () {
		self.isCharging = true;
		self.chargingTarget = {
			x: hero.x,
			y: hero.y
		};
		// Visual warning effect - boss glows red
		tween(bossGraphics, {
			tint: 0xff4444
		}, {
			duration: 800,
			easing: tween.easeIn
		});
		// Scale up slightly to show power building
		tween(bossGraphics, {
			scaleX: 1.2,
			scaleY: 1.2
		}, {
			duration: 800,
			easing: tween.easeOut
		});
		// After warning, execute charge
		LK.setTimeout(function () {
			if (!self.chargingTarget) return;
			// Flash effect
			LK.effects.flashObject(self, 0xffffff, 300);
			// Calculate charge direction
			var dx = self.chargingTarget.x - self.x;
			var dy = self.chargingTarget.y - self.y;
			var distance = Math.sqrt(dx * dx + dy * dy);
			if (distance > 0) {
				// Charge with high speed
				var chargeSpeed = 12;
				var chargeDistance = 400;
				var targetX = self.x + dx / distance * chargeDistance;
				var targetY = self.y + dy / distance * chargeDistance;
				// Keep within bounds
				targetX = Math.max(100, Math.min(targetX, currentLevelData.width - 100));
				targetY = Math.max(1600, Math.min(targetY, 2400));
				tween(self, {
					x: targetX,
					y: targetY
				}, {
					duration: 300,
					easing: tween.easeOut,
					onFinish: function onFinish() {
						// Reset visual effects
						tween(bossGraphics, {
							tint: 0xffffff,
							scaleX: 1,
							scaleY: 1
						}, {
							duration: 500
						});
						self.isCharging = false;
						self.chargingTarget = null;
					}
				});
				// Damage hero if in path during charge
				LK.setTimeout(function () {
					var distanceToHero = Math.sqrt(Math.pow(self.x - hero.x, 2) + Math.pow(self.y - hero.y, 2));
					if (distanceToHero < 150) {
						hero.takeDamage();
					}
				}, 150);
			}
		}, 800);
	};
	self.spinAttack = function () {
		self.isSpinning = true;
		// Visual spin effect
		tween(bossGraphics, {
			rotation: Math.PI * 4
		}, {
			duration: 1500,
			easing: tween.easeInOut,
			onFinish: function onFinish() {
				bossGraphics.rotation = 0;
				self.isSpinning = false;
			}
		});
		// Scale and tint effect during spin
		tween(bossGraphics, {
			scaleX: 1.5,
			scaleY: 1.5,
			tint: 0xff8800
		}, {
			duration: 750,
			easing: tween.easeOut
		});
		tween(bossGraphics, {
			scaleX: 1,
			scaleY: 1,
			tint: 0xffffff
		}, {
			duration: 750,
			easing: tween.easeIn
		});
		// Multiple damage checks during spin
		for (var i = 0; i < 5; i++) {
			LK.setTimeout(function () {
				var distanceToHero = Math.sqrt(Math.pow(self.x - hero.x, 2) + Math.pow(self.y - hero.y, 2));
				if (distanceToHero < 200) {
					hero.takeDamage();
					LK.effects.flashObject(hero, 0xff0000, 200);
				}
			}, i * 300);
		}
	};
	self.areaAttack = function () {
		// Create warning effect
		LK.effects.flashScreen(0xff4444, 1000);
		// Pulsing boss effect
		tween(bossGraphics, {
			scaleX: 1.3,
			scaleY: 1.3
		}, {
			duration: 500,
			easing: tween.easeInOut
		});
		tween(bossGraphics, {
			scaleX: 1,
			scaleY: 1
		}, {
			duration: 500,
			easing: tween.easeInOut
		});
		// Damage hero if close
		var distanceToHero = Math.sqrt(Math.pow(self.x - hero.x, 2) + Math.pow(self.y - hero.y, 2));
		if (distanceToHero < 400) {
			LK.setTimeout(function () {
				if (Math.sqrt(Math.pow(self.x - hero.x, 2) + Math.pow(self.y - hero.y, 2)) < 400) {
					hero.takeDamage();
				}
			}, 1000);
		}
	};
	self.takeDamage = function (fromKnife) {
		self.health--;
		LK.effects.flashObject(self, 0xffffff, 200);
		// Enhanced visual feedback with scaling
		tween(bossGraphics, {
			scaleX: 1.1,
			scaleY: 1.1
		}, {
			duration: 100,
			easing: tween.easeOut
		});
		tween(bossGraphics, {
			scaleX: 1,
			scaleY: 1
		}, {
			duration: 100,
			easing: tween.easeOut
		});
		// Create blood particles
		for (var p = 0; p < 15; p++) {
			var bloodParticle = game.addChild(getFromPool('bloodParticle'));
			bloodParticle.reset(self.x + (Math.random() - 0.5) * 60, self.y - 100 + (Math.random() - 0.5) * 60);
			bloodParticles.push(bloodParticle);
		}
		if (self.health <= 0) {
			self.die();
		}
	};
	self.die = function () {
		// Big explosion effect
		LK.effects.flashScreen(0xffffff, 2000);
		// Drop multiple coins
		for (var i = 0; i < 10; i++) {
			var coin = game.addChild(new Coin());
			coin.x = self.x + (Math.random() - 0.5) * 200;
			coin.y = self.y + (Math.random() - 0.5) * 200;
			coins.push(coin);
		}
		// Massive score bonus
		LK.setScore(LK.getScore() + 1000);
		hero.addCombo();
		// Remove from enemies array
		for (var i = enemies.length - 1; i >= 0; i--) {
			if (enemies[i] === self) {
				enemies.splice(i, 1);
				break;
			}
		}
		self.destroy();
		updateScoreDisplay();
		updateEnemiesLeftDisplay();
	};
	return self;
});
var BossProjectile = Container.expand(function () {
	var self = Container.call(this);
	var projectileGraphics = self.attachAsset('bossProjectile', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	self.velocityX = 0;
	self.velocityY = 0;
	self.lifetime = 300; // 5 seconds
	self.update = function () {
		self.x += self.velocityX;
		self.y += self.velocityY;
		self.lifetime--;
		// Set rotation to face movement direction
		if (self.velocityX !== 0 || self.velocityY !== 0) {
			var angle = Math.atan2(self.velocityY, self.velocityX);
			projectileGraphics.rotation = angle;
		}
		// Check collision with hero
		var distanceToHero = Math.sqrt(Math.pow(self.x - hero.x, 2) + Math.pow(self.y - hero.y, 2));
		if (distanceToHero < 80) {
			hero.takeDamage();
			self.destroy();
			// Remove from bossProjectiles array
			for (var i = bossProjectiles.length - 1; i >= 0; i--) {
				if (bossProjectiles[i] === self) {
					bossProjectiles.splice(i, 1);
					break;
				}
			}
			return;
		}
		// Remove if lifetime expired or off screen
		if (self.lifetime <= 0 || self.x < -100 || self.x > currentLevelData.width + 100 || self.y < -100 || self.y > 3000) {
			self.destroy();
			// Remove from bossProjectiles array
			for (var i = bossProjectiles.length - 1; i >= 0; i--) {
				if (bossProjectiles[i] === self) {
					bossProjectiles.splice(i, 1);
					break;
				}
			}
		}
	};
	return self;
});
var Coin = Container.expand(function () {
	var self = Container.call(this);
	var coinGraphics = self.attachAsset('coin', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	self.collectTimer = 0;
	self.update = function () {
		// Auto-collect after short delay
		self.collectTimer++;
		if (self.collectTimer > 30) {
			// 0.5 seconds
			self.collect();
		}
		// Spin animation
		coinGraphics.rotation += 0.1;
	};
	self.collect = function () {
		LK.getSound('coin').play();
		// Remove from coins array
		for (var i = coins.length - 1; i >= 0; i--) {
			if (coins[i] === self) {
				coins.splice(i, 1);
				break;
			}
		}
		self.destroy();
	};
	return self;
});
var Enemy = Container.expand(function (enemyType) {
	var self = Container.call(this);
	self.enemyType = enemyType || 'basic';
	// Choose asset based on enemy type
	var assetName = 'enemy';
	if (self.enemyType === 'basic') {
		assetName = 'enemyBasic';
	} else if (self.enemyType === 'strong') {
		assetName = 'enemyStrong';
	} else if (self.enemyType === 'fast') {
		assetName = 'enemyFast';
	} else if (self.enemyType === 'tank') {
		assetName = 'enemyTank';
	} else if (self.enemyType === 'hunter') {
		assetName = 'enemyHunter';
	} else if (self.enemyType === 'assassin') {
		assetName = 'enemyAssassin';
	}
	var enemyGraphics = self.attachAsset(assetName, {
		anchorX: 0.5,
		anchorY: 1.0
	});
	self.health = 2;
	self.speed = 1;
	self.attackCooldown = 0;
	self.fromLeft = true;
	self.lastX = 0;
	self.lastY = 0;
	self.alerted = false;
	self.alertedByKnife = false;
	self.update = function () {
		var distanceToHero = Math.sqrt(Math.pow(self.x - hero.x, 2) + Math.pow(self.y - hero.y, 2));
		// Different sight ranges for different enemy types
		var sightRange = 500;
		if (self.enemyType === 'hunter') {
			sightRange = 800; // Hunters have better sight
		} else if (self.enemyType === 'assassin') {
			sightRange = 600; // Assassins have good sight
		} else if (self.enemyType === 'tank') {
			sightRange = 400; // Tanks have poor sight
		}
		var canSeeHero = distanceToHero < sightRange;
		if (canSeeHero || self.alerted) {
			// Chase hero
			var dx = hero.x - self.x;
			var dy = hero.y - self.y;
			var distance = Math.sqrt(dx * dx + dy * dy);
			if (distance > 0) {
				var moveSpeed = self.speed;
				// Special movement for assassin type - teleport ability
				if (self.enemyType === 'assassin' && distanceToHero > 300 && distanceToHero < 600 && Math.random() < 0.02) {
					// Teleport closer to hero
					var teleportDistance = 150;
					var teleportX = hero.x + (Math.random() - 0.5) * teleportDistance;
					var teleportY = hero.y + (Math.random() - 0.5) * teleportDistance;
					// Keep within bounds
					teleportX = Math.max(100, Math.min(teleportX, currentLevelData.width - 100));
					teleportY = Math.max(1600, Math.min(teleportY, 2400));
					self.x = teleportX;
					self.y = teleportY;
					// Flash effect for teleport
					LK.effects.flashObject(self, 0x9b59b6, 300);
					var newX = self.x;
					var newY = self.y;
				} else {
					// Hunter type - faster when far from hero
					if (self.enemyType === 'hunter' && distanceToHero > 400) {
						moveSpeed *= 1.5; // 50% speed boost when hunting from distance
					}
					var newX = self.x + dx / distance * moveSpeed;
					var newY = self.y + dy / distance * moveSpeed;
				}
				// Check collision with other enemies before moving using spatial partitioning
				var wouldCollide = false;
				var nearbyEnemies = spatialGrid.getNearbyObjects(newX, newY, 100);
				for (var i = 0; i < nearbyEnemies.length; i++) {
					var otherEnemy = nearbyEnemies[i];
					if (otherEnemy === self) continue; // Skip self
					var edx = newX - otherEnemy.x;
					var edy = newY - otherEnemy.y;
					var edistance = Math.sqrt(edx * edx + edy * edy);
					if (edistance < 70) {
						// Collision threshold between enemies
						wouldCollide = true;
						break;
					}
				}
				// Only move if no collision would occur
				if (!wouldCollide) {
					self.lastX = self.x;
					self.lastY = self.y;
					self.x = newX;
					self.y = newY;
				}
				// Face direction of movement
				enemyGraphics.scaleX = dx > 0 ? 1 : -1;
			}
			// Attack hero if close enough
			if (distanceToHero < 100 && self.attackCooldown <= 0) {
				hero.takeDamage();
				self.attackCooldown = 120; // 2 seconds at 60fps
			}
		} else if (!self.alerted) {
			// Only roam if not alerted - alerted enemies keep chasing even when they can't see hero
			if (!self.roamDirection || Math.random() < 0.01) {
				self.roamDirection = {
					x: (Math.random() - 0.5) * 2,
					y: (Math.random() - 0.5) * 2
				};
			}
			var newX = self.x + self.roamDirection.x * self.speed * 0.5;
			var newY = self.y + self.roamDirection.y * self.speed * 0.5;
			// Check collision with other enemies before roaming
			var wouldCollide = false;
			for (var i = 0; i < enemies.length; i++) {
				var otherEnemy = enemies[i];
				if (otherEnemy === self) continue; // Skip self
				var edx = newX - otherEnemy.x;
				var edy = newY - otherEnemy.y;
				var edistance = Math.sqrt(edx * edx + edy * edy);
				if (edistance < 70) {
					// Collision threshold between enemies
					wouldCollide = true;
					break;
				}
			}
			// Keep within level bounds and check collisions
			if (!wouldCollide && newX > 100 && newX < currentLevelData.width - 100) {
				self.lastX = self.x;
				self.x = newX;
				enemyGraphics.scaleX = self.roamDirection.x > 0 ? 1 : -1;
			}
			if (!wouldCollide && newY > 1600 && newY < 2400) {
				self.lastY = self.y;
				self.y = newY;
			}
		} else {
			// Alerted enemies that can't see hero still try to chase in last known direction
			var dx = hero.x - self.x;
			var dy = hero.y - self.y;
			var distance = Math.sqrt(dx * dx + dy * dy);
			if (distance > 0) {
				var newX = self.x + dx / distance * self.speed;
				var newY = self.y + dy / distance * self.speed;
				// Check collision with other enemies before moving
				var wouldCollide = false;
				for (var i = 0; i < enemies.length; i++) {
					var otherEnemy = enemies[i];
					if (otherEnemy === self) continue; // Skip self
					var edx = newX - otherEnemy.x;
					var edy = newY - otherEnemy.y;
					var edistance = Math.sqrt(edx * edx + edy * edy);
					if (edistance < 70) {
						// Collision threshold between enemies
						wouldCollide = true;
						break;
					}
				}
				// Only move if no collision would occur
				if (!wouldCollide) {
					self.lastX = self.x;
					self.lastY = self.y;
					self.x = newX;
					self.y = newY;
				}
				// Face direction of movement
				enemyGraphics.scaleX = dx > 0 ? 1 : -1;
			}
		}
		if (self.attackCooldown > 0) {
			self.attackCooldown--;
		}
	};
	self.takeDamage = function (fromKnife) {
		self.health--;
		LK.effects.flashObject(self, 0xffffff, 200);
		// Create knockback effect
		var knockbackForce = 40;
		var knockbackDirection = fromKnife ? 1 : hero.x < self.x ? 1 : -1;
		var targetX = self.x + knockbackDirection * knockbackForce;
		var targetY = self.y - 20; // Slight upward knockback
		// Apply knockback with bounds checking
		targetX = Math.max(100, Math.min(targetX, currentLevelData.width - 100));
		targetY = Math.max(1600, Math.min(targetY, 2400));
		tween(self, {
			x: targetX,
			y: targetY
		}, {
			duration: 200,
			easing: tween.easeOut
		});
		// Create blood particles
		for (var p = 0; p < 12; p++) {
			var bloodParticle = game.addChild(getFromPool('bloodParticle'));
			bloodParticle.reset(self.x + (Math.random() - 0.5) * 40, self.y - 60 + (Math.random() - 0.5) * 40);
			bloodParticles.push(bloodParticle);
		}
		if (fromKnife) {
			self.alerted = true;
			self.alertedByKnife = true;
		}
		if (self.health <= 0) {
			self.die();
		}
	};
	self.die = function () {
		// Drop coin
		var coin = game.addChild(new Coin());
		coin.x = self.x;
		coin.y = self.y;
		coins.push(coin);
		// 10% chance to drop health potion
		if (Math.random() < 0.1) {
			var healthPotion = game.addChild(new HealthPotion());
			healthPotion.x = self.x + (Math.random() - 0.5) * 80; // Slight random offset
			healthPotion.y = self.y + (Math.random() - 0.5) * 80;
			healthPotions.push(healthPotion);
		}
		// Add score and combo
		var baseScore = 10;
		var comboMultiplier = Math.floor(hero.comboCount / 5) + 1;
		var finalScore = baseScore * comboMultiplier;
		LK.setScore(LK.getScore() + finalScore);
		hero.addCombo();
		// Remove from enemies array
		for (var i = enemies.length - 1; i >= 0; i--) {
			if (enemies[i] === self) {
				enemies.splice(i, 1);
				break;
			}
		}
		self.destroy();
		updateScoreDisplay();
		updateEnemiesLeftDisplay();
	};
	return self;
});
var EnemyWarning = Container.expand(function () {
	var self = Container.call(this);
	self.targetEnemy = null;
	self.direction = 'left'; // 'left', 'right', 'up', 'down'
	self.warningGraphics = null;
	self.lastAlpha = 0;
	self.isVisible = false;
	self.setDirection = function (direction) {
		if (self.warningGraphics) {
			self.warningGraphics.destroy();
		}
		var assetName = 'warningArrow' + direction.charAt(0).toUpperCase() + direction.slice(1);
		self.warningGraphics = self.attachAsset(assetName, {
			anchorX: 0.5,
			anchorY: 0.5,
			alpha: 0
		});
		self.direction = direction;
	};
	self.update = function () {
		if (!self.targetEnemy || !self.warningGraphics) return;
		// Check if enemy is still alive
		var enemyExists = false;
		for (var i = 0; i < enemies.length; i++) {
			if (enemies[i] === self.targetEnemy) {
				enemyExists = true;
				break;
			}
		}
		if (!enemyExists) {
			self.hide();
			return;
		}
		// Calculate distance from hero to enemy
		var distanceToHero = Math.sqrt(Math.pow(self.targetEnemy.x - hero.x, 2) + Math.pow(self.targetEnemy.y - hero.y, 2));
		// Check if enemy is visible on screen
		var enemyScreenX = self.targetEnemy.x - camera.x;
		var enemyScreenY = self.targetEnemy.y - camera.y;
		var isOnScreen = enemyScreenX >= -100 && enemyScreenX <= 2148 && enemyScreenY >= -100 && enemyScreenY <= 2832;
		if (isOnScreen) {
			self.hide();
			return;
		}
		// Calculate warning opacity based on distance (closer = more visible)
		var maxDistance = 800;
		var minDistance = 300;
		var targetAlpha = 0;
		if (distanceToHero <= maxDistance) {
			var normalizedDistance = Math.max(0, Math.min(1, (maxDistance - distanceToHero) / (maxDistance - minDistance)));
			targetAlpha = normalizedDistance * 0.8;
		}
		// Smooth alpha transition
		if (Math.abs(targetAlpha - self.lastAlpha) > 0.01) {
			tween.stop(self.warningGraphics, {
				alpha: true
			});
			tween(self.warningGraphics, {
				alpha: targetAlpha
			}, {
				duration: 200
			});
			self.lastAlpha = targetAlpha;
		}
		// Position warning at screen edge
		var screenCenterX = 1024;
		var screenCenterY = 1366;
		var dx = self.targetEnemy.x - hero.x;
		var dy = self.targetEnemy.y - hero.y;
		if (Math.abs(dx) > Math.abs(dy)) {
			// Horizontal warning
			if (dx > 0) {
				self.setDirection('right');
				self.x = screenCenterX + 900;
				self.y = screenCenterY + Math.max(-600, Math.min(600, dy * 0.5));
			} else {
				self.setDirection('left');
				self.x = screenCenterX - 900;
				self.y = screenCenterY + Math.max(-600, Math.min(600, dy * 0.5));
			}
		} else {
			// Vertical warning
			if (dy > 0) {
				self.setDirection('down');
				self.x = screenCenterX + Math.max(-800, Math.min(800, dx * 0.5));
				self.y = screenCenterY + 1200;
			} else {
				self.setDirection('up');
				self.x = screenCenterX + Math.max(-800, Math.min(800, dx * 0.5));
				self.y = screenCenterY - 1200;
			}
		}
	};
	self.hide = function () {
		if (self.warningGraphics && self.warningGraphics.alpha > 0) {
			tween.stop(self.warningGraphics, {
				alpha: true
			});
			tween(self.warningGraphics, {
				alpha: 0
			}, {
				duration: 300
			});
			self.lastAlpha = 0;
		}
	};
	return self;
});
var HealthPotion = Container.expand(function () {
	var self = Container.call(this);
	var potionGraphics = self.attachAsset('healthPotion', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	self.collectTimer = 0;
	self.update = function () {
		// Auto-collect after short delay
		self.collectTimer++;
		if (self.collectTimer > 30) {
			// 0.5 seconds
			self.collect();
		}
		// Gentle floating animation
		potionGraphics.y = Math.sin(LK.ticks * 0.1) * 5;
		potionGraphics.rotation += 0.05;
	};
	self.collect = function () {
		LK.getSound('powerup').play();
		hero.heal();
		// Remove from healthPotions array
		for (var i = healthPotions.length - 1; i >= 0; i--) {
			if (healthPotions[i] === self) {
				healthPotions.splice(i, 1);
				break;
			}
		}
		self.destroy();
	};
	return self;
});
var Hero = Container.expand(function () {
	var self = Container.call(this);
	var heroGraphics = self.attachAsset('hero', {
		anchorX: 0.5,
		anchorY: 1.0
	});
	self.maxHealth = 5;
	self.health = self.maxHealth;
	self.isAttacking = false;
	self.invulnerable = false;
	self.damageBoost = false;
	self.comboCount = 0;
	self.slashCooldown = 0;
	self.isSlashing = false;
	self.slashGraphics = null;
	self.lastX = 0;
	self.lastY = 0;
	self.isWalking = false;
	self.walkAnimationActive = false;
	self.walkAnimationFrame = 0;
	self.walkAnimationTimer = 0;
	self.walkAnimationSpeed = 10; // frames between texture changes
	self.attack = function (targetX) {
		if (self.isAttacking || self.slashCooldown > 0) return;
		self.isAttacking = true;
		self.isSlashing = true;
		self.slashCooldown = 24; // 0.4 seconds at 60fps
		// Face direction of attack
		if (targetX < self.x) {
			heroGraphics.scaleX = -1;
		} else {
			heroGraphics.scaleX = 1;
		}
		// Replace hero graphic with slash animation
		self.removeChild(heroGraphics);
		self.slashGraphics = self.attachAsset('heroSlash', {
			anchorX: 0.5,
			anchorY: 1.0
		});
		// Match facing direction
		self.slashGraphics.scaleX = heroGraphics.scaleX;
		// Attack animation
		tween(self.slashGraphics, {
			scaleY: 1.2
		}, {
			duration: 100
		});
		tween(self.slashGraphics, {
			scaleY: 1.0
		}, {
			duration: 100,
			onFinish: function onFinish() {
				// Return to normal hero graphic
				self.removeChild(self.slashGraphics);
				heroGraphics = self.attachAsset('hero', {
					anchorX: 0.5,
					anchorY: 1.0
				});
				// Maintain facing direction
				heroGraphics.scaleX = self.slashGraphics.scaleX;
				self.isAttacking = false;
				self.isSlashing = false;
				self.slashGraphics = null;
			}
		});
		// Create sword slash effect
		var effect = game.addChild(LK.getAsset('slashEffect', {
			anchorX: 0.5,
			anchorY: 0.5,
			alpha: 0.9
		}));
		effect.x = self.x + heroGraphics.scaleX * 120;
		effect.y = self.y - 80;
		// Set slash rotation and scale based on direction
		effect.rotation = heroGraphics.scaleX > 0 ? -0.3 : 0.3; // Diagonal slash
		effect.scaleX = heroGraphics.scaleX > 0 ? 1 : -1; // Mirror for left attacks
		tween(effect, {
			scaleX: effect.scaleX * 4,
			scaleY: 4,
			alpha: 0
		}, {
			duration: 150,
			onFinish: function onFinish() {
				effect.destroy();
			}
		});
		LK.getSound('slash').play();
	};
	self.takeDamage = function () {
		if (self.invulnerable) return;
		self.health--;
		self.comboCount = 0;
		// Flash red when hit
		LK.effects.flashObject(self, 0xff0000, 500);
		// Temporary invulnerability
		self.invulnerable = true;
		LK.setTimeout(function () {
			self.invulnerable = false;
		}, 1000);
		LK.getSound('hit').play();
		updateHealthDisplay();
		if (self.health <= 0) {
			LK.showGameOver();
		}
	};
	self.heal = function () {
		if (self.health < self.maxHealth) {
			self.health++;
			updateHealthDisplay();
		}
	};
	self.addCombo = function () {
		self.comboCount++;
	};
	self.startWalkAnimation = function () {
		if (self.walkAnimationActive) return;
		self.walkAnimationActive = true;
		self.walkAnimationFrame = 0;
		self.walkAnimationTimer = 0;
	};
	self.stopWalkAnimation = function () {
		self.isWalking = false;
		self.walkAnimationActive = false;
		// Store current scale direction before removing graphics
		var currentScaleX = heroGraphics.scaleX;
		// Reset to default hero texture
		self.removeChild(heroGraphics);
		heroGraphics = self.attachAsset('hero', {
			anchorX: 0.5,
			anchorY: 1.0
		});
		// Maintain the current scale direction
		if (currentScaleX < 0) {
			heroGraphics.scaleX = -1;
		}
	};
	self.move = function (direction) {
		var speed = 8;
		var newX = self.x;
		var newY = self.y;
		var didMove = false;
		if (direction === 'left' && self.x > 100) {
			newX = self.x - speed;
			heroGraphics.scaleX = -1;
			didMove = true;
		} else if (direction === 'right' && self.x < currentLevelData.width - 100) {
			newX = self.x + speed;
			heroGraphics.scaleX = 1;
			didMove = true;
		} else if (direction === 'up' && self.y > 1600) {
			newY = self.y - speed;
			didMove = true;
		} else if (direction === 'down' && self.y < 2400) {
			newY = self.y + speed;
			didMove = true;
		}
		// Check collision with enemies before moving using spatial partitioning
		var wouldCollide = false;
		var nearbyEnemies = spatialGrid.getNearbyObjects(newX, newY, 150);
		for (var i = 0; i < nearbyEnemies.length; i++) {
			var enemy = nearbyEnemies[i];
			var dx = newX - enemy.x;
			var dy = newY - enemy.y;
			var distance = Math.sqrt(dx * dx + dy * dy);
			if (distance < 120) {
				// Increased collision threshold to prevent overlap
				wouldCollide = true;
				break;
			}
		}
		// Only move if no collision would occur
		if (!wouldCollide && didMove) {
			self.lastX = self.x;
			self.lastY = self.y;
			self.x = newX;
			self.y = newY;
			// Start walking animation
			if (!self.isWalking) {
				self.isWalking = true;
				self.startWalkAnimation();
			}
			// Update walking animation texture cycling
			if (self.walkAnimationActive) {
				self.walkAnimationTimer++;
				if (self.walkAnimationTimer >= self.walkAnimationSpeed) {
					self.walkAnimationTimer = 0;
					self.walkAnimationFrame = (self.walkAnimationFrame + 1) % 4;
					// Cycle through textures: hero, heroWalk1, heroWalk2, heroWalk3
					var textureNames = ['hero', 'heroWalk1', 'heroWalk2', 'heroWalk3'];
					// Store current scale direction before removing graphics
					var currentScaleX = heroGraphics.scaleX;
					// Remove current graphics and add new one with correct texture
					self.removeChild(heroGraphics);
					heroGraphics = self.attachAsset(textureNames[self.walkAnimationFrame], {
						anchorX: 0.5,
						anchorY: 1.0
					});
					// Maintain the current scale direction
					if (currentScaleX < 0) {
						heroGraphics.scaleX = -1;
					}
				}
			}
		}
		// Update camera target
		camera.targetX = self.x - 1024;
		camera.targetY = self.y - 1366;
		// Clamp camera to level bounds
		camera.targetX = Math.max(0, Math.min(camera.targetX, currentLevelData.width - 2048));
		camera.targetY = Math.max(0, Math.min(camera.targetY, currentLevelData.height - 2732));
	};
	self.update = function () {
		if (self.slashCooldown > 0) {
			self.slashCooldown--;
		}
	};
	return self;
});
var Knife = Container.expand(function () {
	var self = Container.call(this);
	var knifeGraphics = self.attachAsset('knife', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	self.speed = 15;
	self.direction = 1; // 1 for right, -1 for left
	self.routePoints = []; // Points to follow along the route
	self.currentRouteIndex = 0; // Current target point index
	self.velocityX = 0; // Velocity for precise targeting
	self.velocityY = 0; // Velocity for precise targeting
	self.lastX = 0;
	self.lastY = 0;
	self.setRoute = function (routePoints) {
		self.routePoints = routePoints;
		self.currentRouteIndex = 0;
	};
	self.update = function () {
		self.lastX = self.x;
		self.lastY = self.y;
		// Follow route if available
		if (self.routePoints.length > 0 && self.currentRouteIndex < self.routePoints.length) {
			var targetPoint = self.routePoints[self.currentRouteIndex];
			var dx = targetPoint.x - self.x;
			var dy = targetPoint.y - self.y;
			var distance = Math.sqrt(dx * dx + dy * dy);
			if (distance < 20) {
				// Close enough to current target, move to next point
				self.currentRouteIndex++;
			} else {
				// Move toward current target point
				if (distance > 0) {
					self.x += dx / distance * self.speed;
					self.y += dy / distance * self.speed;
					// Calculate rotation angle to face target direction
					var angle = Math.atan2(dy, dx);
					knifeGraphics.rotation = angle;
				}
			}
		} else if (self.velocityX !== 0 || self.velocityY !== 0) {
			// Use velocity if set, otherwise use direction-based movement
			self.x += self.velocityX;
			self.y += self.velocityY;
			// Calculate rotation for velocity-based movement
			var angle = Math.atan2(self.velocityY, self.velocityX);
			knifeGraphics.rotation = angle;
		} else {
			self.x += self.speed * self.direction;
			self.y -= 2; // Slight upward arc
			// Calculate rotation for direction-based movement
			var angle = Math.atan2(-2, self.speed * self.direction);
			knifeGraphics.rotation = angle;
		}
		// Check if knife went off screen
		if (self.x < -100 || self.x > currentLevelData.width + 100 || self.y < -100 || self.y > 2900) {
			self.destroy();
			// Remove from knives array
			for (var i = knives.length - 1; i >= 0; i--) {
				if (knives[i] === self) {
					knives.splice(i, 1);
					break;
				}
			}
		}
		// Check collision with enemies (hit enemy center)
		for (var i = 0; i < enemies.length; i++) {
			var enemy = enemies[i];
			var dx = self.x - enemy.x;
			var dy = self.y - (enemy.y - 70); // Target enemy center
			var distance = Math.sqrt(dx * dx + dy * dy);
			if (distance < 50) {
				// Hit enemy center - deal damage only to first enemy encountered
				enemy.takeDamage(true);
				self.destroy();
				// Remove from knives array
				for (var j = knives.length - 1; j >= 0; j--) {
					if (knives[j] === self) {
						knives.splice(j, 1);
						break;
					}
				}
				return; // Exit update immediately to prevent further movement or collision checks
			}
		}
	};
	return self;
});
var PowerUp = Container.expand(function () {
	var self = Container.call(this);
	var powerupGraphics = self.attachAsset('powerup', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	self.type = 'health'; // 'health', 'invulnerable', 'damage'
	self.lifetime = 600; // 10 seconds
	self.update = function () {
		self.lifetime--;
		if (self.lifetime <= 0) {
			self.expire();
		}
		// Check collision with hero
		if (self.intersects(hero)) {
			self.collect();
		}
		// Pulse animation
		var scale = 1 + Math.sin(LK.ticks * 0.2) * 0.2;
		powerupGraphics.scaleX = scale;
		powerupGraphics.scaleY = scale;
	};
	self.collect = function () {
		LK.getSound('powerup').play();
		if (self.type === 'health') {
			hero.heal();
		} else if (self.type === 'invulnerable') {
			hero.invulnerable = true;
			LK.setTimeout(function () {
				hero.invulnerable = false;
			}, 5000);
		} else if (self.type === 'damage') {
			hero.damageBoost = true;
			LK.setTimeout(function () {
				hero.damageBoost = false;
			}, 5000);
		}
		// Remove from powerups array
		for (var i = powerups.length - 1; i >= 0; i--) {
			if (powerups[i] === self) {
				powerups.splice(i, 1);
				break;
			}
		}
		self.destroy();
	};
	self.expire = function () {
		// Remove from powerups array
		for (var i = powerups.length - 1; i >= 0; i--) {
			if (powerups[i] === self) {
				powerups.splice(i, 1);
				break;
			}
		}
		self.destroy();
	};
	return self;
});
var RouteEffect = Container.expand(function () {
	var self = Container.call(this);
	self.routePoints = [];
	self.routePositions = []; // Store just the x,y coordinates for knife to follow
	self.targetEnemy = null;
	self.createRoute = function (enemy) {
		self.targetEnemy = enemy;
		self.clearRoute();
		// Calculate direct path from hero center to enemy center
		var heroStartX = hero.x;
		var heroStartY = hero.y - 80; // Middle of hero asset
		var enemyMiddleX = enemy.x;
		var enemyMiddleY = enemy.y - 70; // Middle of enemy asset (enemy height is 140, so middle is -70 from bottom)
		var dx = enemyMiddleX - heroStartX;
		var dy = enemyMiddleY - heroStartY;
		var distance = Math.sqrt(dx * dx + dy * dy);
		if (distance > 0) {
			// Create route points along the path
			var numPoints = Math.floor(distance / 50); // Point every 50 pixels
			for (var i = 0; i <= numPoints; i++) {
				var t = i / numPoints;
				var x = heroStartX + dx * t;
				var y = heroStartY + dy * t;
				var routePoint = self.addChild(LK.getAsset('routeLine', {
					anchorX: 0.5,
					anchorY: 0.5,
					alpha: 0
				}));
				routePoint.x = x;
				routePoint.y = y;
				self.routePoints.push(routePoint);
				// Store position for knife to follow
				self.routePositions.push({
					x: x,
					y: y
				});
				// Animate route points appearing with delay
				tween(routePoint, {
					alpha: 0.8,
					scaleX: 2,
					scaleY: 2
				}, {
					duration: 100 + i * 20
				});
			}
			// Add impact effect at enemy position
			var impactEffect = self.addChild(LK.getAsset('routeEffect', {
				anchorX: 0.5,
				anchorY: 0.5,
				alpha: 0,
				scaleX: 0.5,
				scaleY: 0.5
			}));
			impactEffect.x = enemyMiddleX;
			impactEffect.y = enemyMiddleY;
			self.routePoints.push(impactEffect);
			// Animate impact effect
			tween(impactEffect, {
				alpha: 1,
				scaleX: 3,
				scaleY: 3
			}, {
				duration: 300,
				easing: tween.easeOut
			});
			// Remove route after 0.5 seconds
			LK.setTimeout(function () {
				self.destroy();
				// Remove from routeEffects array
				for (var i = routeEffects.length - 1; i >= 0; i--) {
					if (routeEffects[i] === self) {
						routeEffects.splice(i, 1);
						break;
					}
				}
			}, 500);
		}
	};
	self.clearRoute = function () {
		for (var i = self.routePoints.length - 1; i >= 0; i--) {
			self.routePoints[i].destroy();
		}
		self.routePoints = [];
	};
	self.fadeOut = function () {
		for (var i = 0; i < self.routePoints.length; i++) {
			tween(self.routePoints[i], {
				alpha: 0,
				scaleX: 0.1,
				scaleY: 0.1
			}, {
				duration: 200
			});
		}
		LK.setTimeout(function () {
			self.destroy();
		}, 300);
	};
	return self;
});
/**** 
* Initialize Game
****/ 
var game = new LK.Game({
	backgroundColor: 0x2c3e50,
	title: 'Dungeon Crawler'
});
/**** 
* Game Code
****/ 
// Game variables
var hero;
var enemies = [];
var coins = [];
var powerups = [];
var enemyWarnings = [];
var knives = [];
var knivesRemaining = 5;
var routeEffects = [];
var bloodParticles = [];
var bossProjectiles = [];
var currentDungeon = 1;
var dungeonComplete = false;
var hearts = [];
var healthPotions = [];
// Object pooling system
var objectPools = {
	bloodParticle: [],
	bossProjectile: [],
	coin: [],
	healthPotion: []
};
// Spatial partitioning system
var spatialGrid = {
	cellSize: 200,
	grid: {},
	clear: function clear() {
		this.grid = {};
	},
	getCellKey: function getCellKey(x, y) {
		var cellX = Math.floor(x / this.cellSize);
		var cellY = Math.floor(y / this.cellSize);
		return cellX + ',' + cellY;
	},
	addObject: function addObject(obj, x, y) {
		var key = this.getCellKey(x, y);
		if (!this.grid[key]) {
			this.grid[key] = [];
		}
		this.grid[key].push(obj);
	},
	getNearbyObjects: function getNearbyObjects(x, y, radius) {
		var nearby = [];
		var cellRadius = Math.ceil(radius / this.cellSize);
		var centerCellX = Math.floor(x / this.cellSize);
		var centerCellY = Math.floor(y / this.cellSize);
		for (var dx = -cellRadius; dx <= cellRadius; dx++) {
			for (var dy = -cellRadius; dy <= cellRadius; dy++) {
				var key = centerCellX + dx + ',' + (centerCellY + dy);
				if (this.grid[key]) {
					nearby = nearby.concat(this.grid[key]);
				}
			}
		}
		return nearby;
	}
};
function getFromPool(type) {
	if (objectPools[type] && objectPools[type].length > 0) {
		var obj = objectPools[type].pop();
		obj.visible = true;
		return obj;
	}
	// Create new object if pool is empty
	if (type === 'bloodParticle') {
		return new BloodParticle();
	} else if (type === 'bossProjectile') {
		return new BossProjectile();
	} else if (type === 'coin') {
		return new Coin();
	} else if (type === 'healthPotion') {
		return new HealthPotion();
	}
	return null;
}
function returnToPool(obj, type) {
	if (!objectPools[type]) {
		objectPools[type] = [];
	}
	obj.visible = false;
	obj.x = -1000; // Move off screen
	obj.y = -1000;
	objectPools[type].push(obj);
}
// Movement state tracking
var movementState = {
	left: false,
	right: false,
	up: false,
	down: false
};
// Camera system
var camera = {
	x: 0,
	y: 0,
	targetX: 0,
	targetY: 0,
	smoothing: 0.1
};
// Dungeon configuration
var levels = [{
	enemies: [{
		type: 'basic',
		count: 3
	}],
	width: 4096,
	height: 2732
}, {
	enemies: [{
		type: 'basic',
		count: 5
	}, {
		type: 'strong',
		count: 2
	}],
	width: 5120,
	height: 2732
}, {
	enemies: [{
		type: 'basic',
		count: 7
	}, {
		type: 'strong',
		count: 3
	}, {
		type: 'fast',
		count: 2
	}],
	width: 6144,
	height: 2732
}, {
	enemies: [{
		type: 'basic',
		count: 5
	}, {
		type: 'strong',
		count: 4
	}, {
		type: 'fast',
		count: 3
	}, {
		type: 'tank',
		count: 2
	}],
	width: 7168,
	height: 2732
}, {
	enemies: [{
		type: 'basic',
		count: 8
	}, {
		type: 'strong',
		count: 4
	}, {
		type: 'fast',
		count: 4
	}, {
		type: 'tank',
		count: 2
	}, {
		type: 'hunter',
		count: 3
	}],
	width: 8192,
	height: 2732
}, {
	enemies: [{
		type: 'boss',
		count: 1
	}],
	width: 9216,
	height: 2732
}, {
	enemies: [{
		type: 'basic',
		count: 10
	}, {
		type: 'strong',
		count: 6
	}, {
		type: 'fast',
		count: 5
	}, {
		type: 'tank',
		count: 3
	}, {
		type: 'hunter',
		count: 4
	}, {
		type: 'assassin',
		count: 2
	}],
	width: 10240,
	height: 2732
}, {
	enemies: [{
		type: 'basic',
		count: 12
	}, {
		type: 'strong',
		count: 8
	}, {
		type: 'fast',
		count: 6
	}, {
		type: 'tank',
		count: 4
	}, {
		type: 'hunter',
		count: 5
	}, {
		type: 'assassin',
		count: 4
	}],
	width: 12288,
	height: 2732
}];
var currentLevelData = levels[0];
// UI Elements
var scoreText = new Text2('Score: 0', {
	size: 80,
	fill: 0xFFFFFF
});
scoreText.anchor.set(0, 0);
scoreText.x = 150;
scoreText.y = 50;
LK.gui.topLeft.addChild(scoreText);
var levelText = new Text2('Dungeon: 1', {
	size: 80,
	fill: 0xFFFFFF
});
levelText.anchor.set(0.5, 0);
LK.gui.top.addChild(levelText);
levelText.y = 50;
var comboText = new Text2('Combo: 0x', {
	size: 60,
	fill: 0xFFFF00
});
comboText.anchor.set(1, 0);
LK.gui.topRight.addChild(comboText);
comboText.x = -50;
comboText.y = 120;
var knivesText = new Text2('Knives: 5', {
	size: 60,
	fill: 0x8e44ad
});
knivesText.anchor.set(1, 0);
LK.gui.topRight.addChild(knivesText);
knivesText.x = -50;
knivesText.y = 190;
var enemiesLeftText = new Text2('Enemies: 0', {
	size: 60,
	fill: 0xff4444
});
enemiesLeftText.anchor.set(1, 0);
LK.gui.topRight.addChild(enemiesLeftText);
enemiesLeftText.x = -50;
enemiesLeftText.y = 260;
// Create movement and attack buttons
var leftButton = LK.getAsset('leftButton', {
	anchorX: 0.5,
	anchorY: 0.5,
	alpha: 0.8,
	scaleX: 1.5,
	scaleY: 1.5
});
leftButton.x = 80;
leftButton.y = -300;
LK.gui.bottomLeft.addChild(leftButton);
var rightButton = LK.getAsset('rightButton', {
	anchorX: 0.5,
	anchorY: 0.5,
	alpha: 0.8,
	scaleX: 1.5,
	scaleY: 1.5
});
rightButton.x = 480;
rightButton.y = -300;
LK.gui.bottomLeft.addChild(rightButton);
var upButton = LK.getAsset('upButton', {
	anchorX: 0.5,
	anchorY: 0.5,
	alpha: 0.8,
	scaleX: 1.5,
	scaleY: 1.5
});
upButton.x = 280;
upButton.y = -480;
LK.gui.bottomLeft.addChild(upButton);
var downButton = LK.getAsset('downButton', {
	anchorX: 0.5,
	anchorY: 0.5,
	alpha: 0.8,
	scaleX: 1.5,
	scaleY: 1.5
});
downButton.x = 280;
downButton.y = -120;
LK.gui.bottomLeft.addChild(downButton);
var attackButton = LK.getAsset('attackButton', {
	anchorX: 0.5,
	anchorY: 0.5,
	alpha: 0.9
});
attackButton.x = -150;
attackButton.y = -200;
LK.gui.bottomRight.addChild(attackButton);
var knifeButton = LK.getAsset('knifeButton', {
	anchorX: 0.5,
	anchorY: 0.5,
	alpha: 0.9
});
knifeButton.x = -350;
knifeButton.y = -200;
LK.gui.bottomRight.addChild(knifeButton);
// Create background grid
var backgroundTiles = [];
function createBackgroundGrid() {
	// Clear existing background tiles
	for (var i = backgroundTiles.length - 1; i >= 0; i--) {
		backgroundTiles[i].destroy();
	}
	backgroundTiles = [];
	var tileSize = 200;
	var tilesX = Math.ceil(currentLevelData.width / tileSize) + 2;
	var tilesY = Math.ceil(currentLevelData.height / tileSize) + 2;
	for (var x = 0; x < tilesX; x++) {
		for (var y = 0; y < tilesY; y++) {
			var tile = game.addChild(LK.getAsset('backgroundTile', {
				anchorX: 0,
				anchorY: 0,
				alpha: 0.3
			}));
			tile.x = x * tileSize;
			tile.y = y * tileSize;
			// Add subtle pattern variation
			if ((x + y) % 2 === 0) {
				tile.alpha = 0.2;
			}
			backgroundTiles.push(tile);
		}
	}
}
// Add background
var background = game.addChild(LK.getAsset('background', {
	anchorX: 0,
	anchorY: 0,
	x: 0,
	y: 0,
	alpha: 1
}));
// Create hero
hero = game.addChild(new Hero());
hero.x = 1024; // Center of screen
hero.y = 2300; // Near bottom
// Create health display
function updateHealthDisplay() {
	// Remove existing hearts
	for (var i = hearts.length - 1; i >= 0; i--) {
		hearts[i].destroy();
	}
	hearts = [];
	// Create new hearts
	for (var i = 0; i < hero.health; i++) {
		var heart = LK.getAsset('heart', {
			anchorX: 0.5,
			anchorY: 0.5
		});
		heart.x = 200 + i * 80;
		heart.y = 200;
		LK.gui.topLeft.addChild(heart);
		hearts.push(heart);
	}
}
function updateScoreDisplay() {
	scoreText.setText('Score: ' + LK.getScore());
	var comboMultiplier = Math.floor(hero.comboCount / 5) + 1;
	comboText.setText('Combo: ' + comboMultiplier + 'x');
}
function updateKnivesDisplay() {
	knivesText.setText('Knives: ' + knivesRemaining);
	// Update button alpha based on availability
	knifeButton.alpha = knivesRemaining > 0 ? 0.9 : 0.3;
}
function updateEnemiesLeftDisplay() {
	enemiesLeftText.setText('Enemies: ' + enemies.length);
}
function initializeLevel() {
	// Show boss fight alert for final dungeon
	if (currentDungeon === levels.length) {
		LK.setTimeout(function () {
			alert('FINAL BOSS APPROACHING! Prepare for the ultimate challenge!');
		}, 500);
	}
	// Clear existing enemies
	for (var i = enemies.length - 1; i >= 0; i--) {
		enemies[i].destroy();
	}
	enemies = [];
	// Clear existing warnings
	for (var i = enemyWarnings.length - 1; i >= 0; i--) {
		enemyWarnings[i].destroy();
	}
	enemyWarnings = [];
	// Clear existing knives
	for (var i = knives.length - 1; i >= 0; i--) {
		knives[i].destroy();
	}
	knives = [];
	// Clear existing route effects
	for (var i = routeEffects.length - 1; i >= 0; i--) {
		routeEffects[i].destroy();
	}
	routeEffects = [];
	// Clear existing blood particles
	for (var i = bloodParticles.length - 1; i >= 0; i--) {
		bloodParticles[i].destroy();
	}
	bloodParticles = [];
	// Clear existing boss projectiles
	for (var i = bossProjectiles.length - 1; i >= 0; i--) {
		bossProjectiles[i].destroy();
	}
	bossProjectiles = [];
	// Clear existing health potions
	for (var i = healthPotions.length - 1; i >= 0; i--) {
		healthPotions[i].destroy();
	}
	healthPotions = [];
	// Reset knife count
	knivesRemaining = 5;
	currentLevelData = levels[currentDungeon - 1] || levels[levels.length - 1];
	dungeonComplete = false;
	// Spawn enemies for this level
	for (var j = 0; j < currentLevelData.enemies.length; j++) {
		var enemyGroup = currentLevelData.enemies[j];
		for (var k = 0; k < enemyGroup.count; k++) {
			spawnEnemy(enemyGroup.type);
		}
	}
	levelText.setText('Dungeon: ' + currentDungeon);
	updateKnivesDisplay();
	updateEnemiesLeftDisplay();
	// Create background grid for this level
	createBackgroundGrid();
}
function spawnEnemy(type) {
	var enemy;
	if (type === 'boss') {
		enemy = game.addChild(new Boss());
		// Boss spawns at center of level
		enemy.x = currentLevelData.width / 2;
		enemy.y = 2200;
	} else {
		enemy = game.addChild(new Enemy(type));
		// Find a spawn position that's not too close to the hero
		var minDistanceFromPlayer = 500; // Minimum distance from player
		var attempts = 0;
		var maxAttempts = 20;
		var enemyX, enemyY;
		do {
			// Random spawn position within level bounds
			enemyX = 200 + Math.random() * (currentLevelData.width - 400);
			enemyY = 1700 + Math.random() * 600;
			// Calculate distance from hero
			var dx = enemyX - hero.x;
			var dy = enemyY - hero.y;
			var distanceFromPlayer = Math.sqrt(dx * dx + dy * dy);
			attempts++;
			// If far enough from player or we've tried too many times, use this position
			if (distanceFromPlayer >= minDistanceFromPlayer || attempts >= maxAttempts) {
				enemy.x = enemyX;
				enemy.y = enemyY;
				break;
			}
		} while (attempts < maxAttempts);
		// Set enemy properties based on type
		if (type === 'basic') {
			enemy.health = 2;
			enemy.speed = 2;
		} else if (type === 'strong') {
			enemy.health = 4;
			enemy.speed = 0.8;
		} else if (type === 'fast') {
			enemy.health = 1;
			enemy.speed = 4;
		} else if (type === 'tank') {
			enemy.health = 8;
			enemy.speed = 0.5;
		} else if (type === 'hunter') {
			enemy.health = 3;
			enemy.speed = 2.5;
		} else if (type === 'assassin') {
			enemy.health = 2;
			enemy.speed = 3;
		}
	}
	enemies.push(enemy);
}
function spawnPowerUp() {
	var powerup = game.addChild(new PowerUp());
	powerup.x = 500 + Math.random() * 1048; // Random x position
	powerup.y = 1800 + Math.random() * 400; // Above ground level
	// Random powerup type
	var types = ['health', 'invulnerable', 'damage'];
	powerup.type = types[Math.floor(Math.random() * types.length)];
	// Color by type
	var powerupGraphics = powerup.getChildAt(0);
	if (powerup.type === 'health') {
		powerupGraphics.tint = 0x00ff00; // Green
	} else if (powerup.type === 'invulnerable') {
		powerupGraphics.tint = 0x0088ff; // Blue
	} else if (powerup.type === 'damage') {
		powerupGraphics.tint = 0xff8800; // Orange
	}
	powerups.push(powerup);
}
function findNearestEnemy(x, y) {
	var nearest = null;
	var shortestDistance = Infinity;
	for (var i = 0; i < enemies.length; i++) {
		var enemy = enemies[i];
		var distance = Math.sqrt(Math.pow(enemy.x - x, 2) + Math.pow(enemy.y - y, 2));
		if (distance < shortestDistance) {
			shortestDistance = distance;
			nearest = enemy;
		}
	}
	return nearest;
}
function isVisible(obj, buffer) {
	buffer = buffer || 100;
	var objScreenX = obj.x - camera.x;
	var objScreenY = obj.y - camera.y;
	return objScreenX >= -buffer && objScreenX <= 2048 + buffer && objScreenY >= -buffer && objScreenY <= 2732 + buffer;
}
function updateEnemyWarnings() {
	// Remove warnings for dead enemies
	for (var i = enemyWarnings.length - 1; i >= 0; i--) {
		var warning = enemyWarnings[i];
		var enemyExists = false;
		for (var j = 0; j < enemies.length; j++) {
			if (enemies[j] === warning.targetEnemy) {
				enemyExists = true;
				break;
			}
		}
		if (!enemyExists) {
			warning.destroy();
			enemyWarnings.splice(i, 1);
		}
	}
	// Create warnings for new enemies
	for (var i = 0; i < enemies.length; i++) {
		var enemy = enemies[i];
		var hasWarning = false;
		for (var j = 0; j < enemyWarnings.length; j++) {
			if (enemyWarnings[j].targetEnemy === enemy) {
				hasWarning = true;
				break;
			}
		}
		if (!hasWarning) {
			var warning = LK.gui.center.addChild(new EnemyWarning());
			warning.targetEnemy = enemy;
			warning.setDirection('left');
			enemyWarnings.push(warning);
		}
	}
}
// Initialize UI
updateHealthDisplay();
updateScoreDisplay();
updateKnivesDisplay();
updateEnemiesLeftDisplay();
// Initialize first level
initializeLevel();
// Create initial background grid
createBackgroundGrid();
// Set initial camera position
camera.targetX = hero.x - 1024;
camera.targetY = hero.y - 1366;
camera.x = camera.targetX;
camera.y = camera.targetY;
// Button event handlers
leftButton.down = function (x, y, obj) {
	movementState.left = true;
};
leftButton.up = function (x, y, obj) {
	movementState.left = false;
};
// Global function to check which button is under a given position
function getButtonUnderPosition(screenX, screenY) {
	// Convert GUI coordinates and check bounds for each button (accounting for 1.5x scale)
	var scaledButtonWidth = 200 * 1.5;
	var scaledButtonHeight = 200 * 1.5;
	var leftBounds = {
		x: leftButton.x - scaledButtonWidth / 2,
		y: leftButton.y - scaledButtonHeight / 2,
		width: scaledButtonWidth,
		height: scaledButtonHeight
	};
	var rightBounds = {
		x: rightButton.x - scaledButtonWidth / 2,
		y: rightButton.y - scaledButtonHeight / 2,
		width: scaledButtonWidth,
		height: scaledButtonHeight
	};
	var upBounds = {
		x: upButton.x - scaledButtonWidth / 2,
		y: upButton.y - scaledButtonHeight / 2,
		width: scaledButtonWidth,
		height: scaledButtonHeight
	};
	var downBounds = {
		x: downButton.x - scaledButtonWidth / 2,
		y: downButton.y - scaledButtonHeight / 2,
		width: scaledButtonWidth,
		height: scaledButtonHeight
	};
	// Adjust screen coordinates relative to bottomLeft GUI
	var relativeX = screenX;
	var relativeY = screenY - (2732 - 500); // Approximate bottomLeft offset
	if (relativeX >= leftBounds.x && relativeX <= leftBounds.x + leftBounds.width && relativeY >= leftBounds.y && relativeY <= leftBounds.y + leftBounds.height) {
		return 'left';
	}
	if (relativeX >= rightBounds.x && relativeX <= rightBounds.x + rightBounds.width && relativeY >= rightBounds.y && relativeY <= rightBounds.y + rightBounds.height) {
		return 'right';
	}
	if (relativeX >= upBounds.x && relativeX <= upBounds.x + upBounds.width && relativeY >= upBounds.y && relativeY <= upBounds.y + upBounds.height) {
		return 'up';
	}
	if (relativeX >= downBounds.x && relativeX <= downBounds.x + downBounds.width && relativeY >= downBounds.y && relativeY <= downBounds.y + downBounds.height) {
		return 'down';
	}
	return null;
}
// Global movement handling function
function handleMovementInput(direction) {
	// Reset all movement states first
	movementState.left = false;
	movementState.right = false;
	movementState.up = false;
	movementState.down = false;
	// Set the active direction
	if (direction === 'left') {
		movementState.left = true;
	} else if (direction === 'right') {
		movementState.right = true;
	} else if (direction === 'up') {
		movementState.up = true;
	} else if (direction === 'down') {
		movementState.down = true;
	}
}
leftButton.move = function (x, y, obj) {
	var currentButton = getButtonUnderPosition(x, y);
	if (currentButton) {
		handleMovementInput(currentButton);
	} else {
		handleMovementInput('left');
	}
};
rightButton.down = function (x, y, obj) {
	movementState.right = true;
};
rightButton.up = function (x, y, obj) {
	movementState.right = false;
};
rightButton.move = function (x, y, obj) {
	var currentButton = getButtonUnderPosition(x, y);
	if (currentButton) {
		handleMovementInput(currentButton);
	} else {
		handleMovementInput('right');
	}
};
upButton.down = function (x, y, obj) {
	movementState.up = true;
};
upButton.up = function (x, y, obj) {
	movementState.up = false;
};
upButton.move = function (x, y, obj) {
	var currentButton = getButtonUnderPosition(x, y);
	if (currentButton) {
		handleMovementInput(currentButton);
	} else {
		handleMovementInput('up');
	}
};
downButton.down = function (x, y, obj) {
	movementState.down = true;
};
downButton.up = function (x, y, obj) {
	movementState.down = false;
};
downButton.move = function (x, y, obj) {
	var currentButton = getButtonUnderPosition(x, y);
	if (currentButton) {
		handleMovementInput(currentButton);
	} else {
		handleMovementInput('down');
	}
};
attackButton.down = function (x, y, obj) {
	if (!hero.isAttacking) {
		var nearestEnemy = findNearestEnemy(hero.x, hero.y);
		if (nearestEnemy) {
			hero.attack(nearestEnemy.x);
			// Check if attack hits with increased range
			var distanceToEnemy = Math.sqrt(Math.pow(hero.x - nearestEnemy.x, 2) + Math.pow(hero.y - nearestEnemy.y, 2));
			if (distanceToEnemy < 350) {
				// Increased from 250 to 350
				var damage = hero.damageBoost ? 2 : 1;
				for (var i = 0; i < damage; i++) {
					nearestEnemy.takeDamage();
				}
			}
		} else {
			// Attack in hero's facing direction
			hero.attack(hero.x + (hero.getChildAt(0).scaleX > 0 ? 100 : -100));
		}
	}
};
knifeButton.down = function (x, y, obj) {
	if (knivesRemaining > 0) {
		// Find nearest enemy for targeting
		var nearestEnemy = findNearestEnemy(hero.x, hero.y);
		if (nearestEnemy) {
			// Clear existing route effects before creating new one
			for (var i = routeEffects.length - 1; i >= 0; i--) {
				routeEffects[i].destroy();
				routeEffects.splice(i, 1);
			}
			// Create route visualization
			var routeEffect = game.addChild(new RouteEffect());
			routeEffect.createRoute(nearestEnemy);
			routeEffects.push(routeEffect);
			// Throw knife to follow the route
			var knife = game.addChild(new Knife());
			knife.x = hero.x;
			knife.y = hero.y - 80;
			// Set the route for the knife to follow
			knife.setRoute(routeEffect.routePositions);
			// Rotation will be handled automatically in knife update based on target direction
			knives.push(knife);
			knivesRemaining--;
			updateKnivesDisplay();
			LK.getSound('knifeThrow').play();
		} else {
			// No enemy found, throw in hero facing direction
			var knife = game.addChild(new Knife());
			knife.x = hero.x;
			knife.y = hero.y - 80;
			var heroGraphics = hero.getChildAt(0);
			knife.direction = heroGraphics.scaleX > 0 ? 1 : -1;
			// Rotation will be handled automatically in knife update based on movement direction
			knives.push(knife);
			knivesRemaining--;
			updateKnivesDisplay();
			LK.getSound('knifeThrow').play();
		}
	}
};
// Game input (fallback for screen taps outside buttons)
game.down = function (x, y, obj) {
	// Convert screen coordinates to world coordinates
	var worldX = x + camera.x;
	var worldY = y + camera.y;
	// Check if tap is for movement or attack
	var distanceToHero = Math.sqrt(Math.pow(worldX - hero.x, 2) + Math.pow(worldY - hero.y, 2));
	if (distanceToHero > 200) {
		// Movement - move toward tap position
		var dx = worldX - hero.x;
		var dy = worldY - hero.y;
		if (Math.abs(dx) > Math.abs(dy)) {
			hero.move(dx > 0 ? 'right' : 'left');
		} else {
			hero.move(dy > 0 ? 'down' : 'up');
		}
	} else {
		// Attack
		if (!hero.isAttacking) {
			var nearestEnemy = findNearestEnemy(worldX, worldY);
			if (nearestEnemy) {
				hero.attack(nearestEnemy.x);
				// Check if attack hits with increased range
				var distanceToEnemy = Math.sqrt(Math.pow(hero.x - nearestEnemy.x, 2) + Math.pow(hero.y - nearestEnemy.y, 2));
				if (distanceToEnemy < 350) {
					// Increased from 250 to 350
					var damage = hero.damageBoost ? 2 : 1;
					for (var i = 0; i < damage; i++) {
						nearestEnemy.takeDamage();
					}
				}
			} else {
				// Attack in direction of tap
				hero.attack(worldX);
			}
		}
	}
};
// Main game loop
game.update = function () {
	// Track if hero is moving this frame
	var wasMoving = hero.isWalking;
	var isMovingThisFrame = false;
	// Handle continuous movement based on button states
	if (movementState.left) {
		hero.move('left');
		isMovingThisFrame = true;
	}
	if (movementState.right) {
		hero.move('right');
		isMovingThisFrame = true;
	}
	if (movementState.up) {
		hero.move('up');
		isMovingThisFrame = true;
	}
	if (movementState.down) {
		hero.move('down');
		isMovingThisFrame = true;
	}
	// Stop walking animation if no movement this frame
	if (wasMoving && !isMovingThisFrame) {
		hero.stopWalkAnimation();
	}
	// Update camera position smoothly
	camera.x += (camera.targetX - camera.x) * camera.smoothing;
	camera.y += (camera.targetY - camera.y) * camera.smoothing;
	// Apply camera position to game
	game.x = -camera.x;
	game.y = -camera.y;
	// Check for hero-enemy collisions and apply push-back using spatial partitioning
	var nearbyEnemies = spatialGrid.getNearbyObjects(hero.x, hero.y, 150);
	for (var i = 0; i < nearbyEnemies.length; i++) {
		var enemy = nearbyEnemies[i];
		var dx = hero.x - enemy.x;
		var dy = hero.y - enemy.y;
		var distance = Math.sqrt(dx * dx + dy * dy);
		// If too close, push hero away from enemy
		if (distance < 100 && distance > 0) {
			var pushForce = (100 - distance) * 0.3;
			var pushX = dx / distance * pushForce;
			var pushY = dy / distance * pushForce;
			// Apply push with bounds checking
			var newHeroX = hero.x + pushX;
			var newHeroY = hero.y + pushY;
			// Keep hero within level bounds
			if (newHeroX > 100 && newHeroX < currentLevelData.width - 100) {
				hero.x = newHeroX;
			}
			if (newHeroY > 1600 && newHeroY < 2400) {
				hero.y = newHeroY;
			}
			// Update camera target when hero is pushed
			camera.targetX = hero.x - 1024;
			camera.targetY = hero.y - 1366;
			camera.targetX = Math.max(0, Math.min(camera.targetX, currentLevelData.width - 2048));
			camera.targetY = Math.max(0, Math.min(camera.targetY, currentLevelData.height - 2732));
		}
	}
	// Clear and rebuild spatial grid
	spatialGrid.clear();
	for (var i = 0; i < enemies.length; i++) {
		spatialGrid.addObject(enemies[i], enemies[i].x, enemies[i].y);
	}
	// Update all game objects with culling
	for (var i = enemies.length - 1; i >= 0; i--) {
		var enemy = enemies[i];
		if (isVisible(enemy, 300)) {
			// Only update visible enemies + buffer
			enemy.update();
		}
	}
	for (var i = coins.length - 1; i >= 0; i--) {
		if (isVisible(coins[i], 100)) {
			coins[i].update();
		}
	}
	for (var i = powerups.length - 1; i >= 0; i--) {
		if (isVisible(powerups[i], 100)) {
			powerups[i].update();
		}
	}
	for (var i = healthPotions.length - 1; i >= 0; i--) {
		if (isVisible(healthPotions[i], 100)) {
			healthPotions[i].update();
		}
	}
	for (var i = knives.length - 1; i >= 0; i--) {
		knives[i].update(); // Always update knives as they move fast
	}
	for (var i = bloodParticles.length - 1; i >= 0; i--) {
		if (isVisible(bloodParticles[i], 50)) {
			bloodParticles[i].update();
		}
	}
	for (var i = bossProjectiles.length - 1; i >= 0; i--) {
		bossProjectiles[i].update(); // Always update projectiles as they move fast
	}
	// Clean up destroyed route effects
	for (var i = routeEffects.length - 1; i >= 0; i--) {
		var routeEffect = routeEffects[i];
		if (!routeEffect.parent) {
			routeEffects.splice(i, 1);
		}
	}
	// Update enemy warnings
	updateEnemyWarnings();
	for (var i = 0; i < enemyWarnings.length; i++) {
		enemyWarnings[i].update();
	}
	// Check for dungeon completion
	if (!dungeonComplete && enemies.length === 0) {
		dungeonComplete = true;
		currentDungeon++;
		if (currentDungeon <= levels.length) {
			// Start next dungeon after delay
			LK.setTimeout(function () {
				initializeLevel();
			}, 2000);
		} else {
			// All dungeons completed
			LK.showYouWin();
		}
	}
}; /**** 
* Plugins
****/ 
var tween = LK.import("@upit/tween.v1");
/**** 
* Classes
****/ 
var BloodParticle = Container.expand(function () {
	var self = Container.call(this);
	var particleGraphics = self.attachAsset('bloodParticle', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	self.velocityX = (Math.random() - 0.5) * 8;
	self.velocityY = -Math.random() * 6 - 2;
	self.gravity = 0.3;
	self.lifetime = 60; // 1 second at 60fps
	self.reset = function (startX, startY) {
		self.x = startX;
		self.y = startY;
		self.velocityX = (Math.random() - 0.5) * 8;
		self.velocityY = -Math.random() * 6 - 2;
		self.lifetime = 60;
		particleGraphics.alpha = 1;
		self.visible = true;
	};
	self.update = function () {
		self.x += self.velocityX;
		self.y += self.velocityY;
		self.velocityY += self.gravity;
		// Fade out over time
		self.lifetime--;
		particleGraphics.alpha = self.lifetime / 60;
		if (self.lifetime <= 0) {
			// Remove from bloodParticles array
			for (var i = bloodParticles.length - 1; i >= 0; i--) {
				if (bloodParticles[i] === self) {
					bloodParticles.splice(i, 1);
					break;
				}
			}
			returnToPool(self, 'bloodParticle');
		}
	};
	return self;
});
var Boss = Container.expand(function () {
	var self = Container.call(this);
	var bossGraphics = self.attachAsset('boss', {
		anchorX: 0.5,
		anchorY: 1.0
	});
	self.health = 50;
	self.maxHealth = 50;
	self.speed = 3; // Increased base speed
	self.attackCooldown = 0;
	self.specialAttackCooldown = 0;
	self.phase = 1; // Boss has 3 phases
	self.lastX = 0;
	self.lastY = 0;
	self.isEnraged = false;
	self.projectileAttackCooldown = 0;
	self.chargeAttackCooldown = 0;
	self.isCharging = false;
	self.chargingTarget = null;
	self.spinAttackCooldown = 0;
	self.isSpinning = false;
	self.update = function () {
		var distanceToHero = Math.sqrt(Math.pow(self.x - hero.x, 2) + Math.pow(self.y - hero.y, 2));
		// Different behavior based on health phases
		if (self.health > self.maxHealth * 0.66) {
			self.phase = 1;
			bossGraphics.tint = 0xffffff; // Normal color
		} else if (self.health > self.maxHealth * 0.33) {
			self.phase = 2;
			bossGraphics.tint = 0xffaa00; // Orange when damaged
			if (!self.isEnraged) {
				self.isEnraged = true;
				self.speed *= 2; // Double speed in phase 2
				// Visual effect for enrage
				tween(bossGraphics, {
					scaleX: 1.2,
					scaleY: 1.2
				}, {
					duration: 300,
					easing: tween.bounceOut
				});
				tween(bossGraphics, {
					scaleX: 1,
					scaleY: 1
				}, {
					duration: 300,
					easing: tween.bounceOut
				});
			}
		} else {
			self.phase = 3;
			bossGraphics.tint = 0xff0000; // Red when critically damaged
			if (self.speed < 5) {
				self.speed = 5; // Maximum speed in final phase
				// Intense visual effect for final phase
				tween(bossGraphics, {
					scaleX: 1.3,
					scaleY: 1.3
				}, {
					duration: 200,
					easing: tween.elasticOut
				});
				tween(bossGraphics, {
					scaleX: 1,
					scaleY: 1
				}, {
					duration: 200,
					easing: tween.elasticOut
				});
			}
		}
		// Chase hero
		var dx = hero.x - self.x;
		var dy = hero.y - self.y;
		var distance = Math.sqrt(dx * dx + dy * dy);
		if (distance > 0) {
			self.lastX = self.x;
			self.lastY = self.y;
			self.x += dx / distance * self.speed;
			self.y += dy / distance * self.speed;
			// Face direction of movement
			bossGraphics.scaleX = dx > 0 ? 1 : -1;
		}
		// Melee attack
		if (distanceToHero < 150 && self.attackCooldown <= 0) {
			hero.takeDamage();
			self.attackCooldown = 90; // 1.5 seconds
			LK.effects.flashObject(self, 0xffffff, 200);
		}
		// Projectile attack (phase 2 and 3)
		if (self.phase >= 2 && self.projectileAttackCooldown <= 0 && distanceToHero > 200) {
			self.fireProjectile();
			self.projectileAttackCooldown = self.phase === 3 ? 60 : 120; // Faster in phase 3
		}
		// Charge attack (phase 2 and 3)
		if (self.phase >= 2 && self.chargeAttackCooldown <= 0 && distanceToHero > 300 && !self.isCharging) {
			self.chargeAttack();
			self.chargeAttackCooldown = self.phase === 3 ? 240 : 360; // Faster in phase 3
		}
		// Spin attack (phase 3 only)
		if (self.phase === 3 && self.spinAttackCooldown <= 0 && distanceToHero < 250 && !self.isSpinning) {
			self.spinAttack();
			self.spinAttackCooldown = 420; // 7 seconds
		}
		// Special area attack (phase 3 only)
		if (self.phase === 3 && self.specialAttackCooldown <= 0) {
			self.areaAttack();
			self.specialAttackCooldown = 300; // 5 seconds
		}
		if (self.attackCooldown > 0) self.attackCooldown--;
		if (self.specialAttackCooldown > 0) self.specialAttackCooldown--;
		if (self.projectileAttackCooldown > 0) self.projectileAttackCooldown--;
		if (self.chargeAttackCooldown > 0) self.chargeAttackCooldown--;
		if (self.spinAttackCooldown > 0) self.spinAttackCooldown--;
	};
	self.fireProjectile = function () {
		// Visual charging effect before firing
		tween(bossGraphics, {
			tint: 0x8800ff
		}, {
			duration: 200
		});
		tween(bossGraphics, {
			tint: 0xffffff
		}, {
			duration: 200
		});
		var projectile = game.addChild(new BossProjectile());
		projectile.x = self.x;
		projectile.y = self.y - 200;
		// Calculate direction to hero
		var dx = hero.x - self.x;
		var dy = hero.y - self.y;
		var distance = Math.sqrt(dx * dx + dy * dy);
		if (distance > 0) {
			var speed = self.phase === 3 ? 12 : 8; // Faster projectiles in phase 3
			projectile.velocityX = dx / distance * speed;
			projectile.velocityY = dy / distance * speed;
		}
		// Add visual trail effect to projectile
		var projectileGraphics = projectile.getChildAt(0);
		tween(projectileGraphics, {
			tint: 0xff4444
		}, {
			duration: 100
		});
		tween(projectileGraphics, {
			tint: 0xffffff
		}, {
			duration: 100
		});
		bossProjectiles.push(projectile);
	};
	self.chargeAttack = function () {
		self.isCharging = true;
		self.chargingTarget = {
			x: hero.x,
			y: hero.y
		};
		// Visual warning effect - boss glows red
		tween(bossGraphics, {
			tint: 0xff4444
		}, {
			duration: 800,
			easing: tween.easeIn
		});
		// Scale up slightly to show power building
		tween(bossGraphics, {
			scaleX: 1.2,
			scaleY: 1.2
		}, {
			duration: 800,
			easing: tween.easeOut
		});
		// After warning, execute charge
		LK.setTimeout(function () {
			if (!self.chargingTarget) return;
			// Flash effect
			LK.effects.flashObject(self, 0xffffff, 300);
			// Calculate charge direction
			var dx = self.chargingTarget.x - self.x;
			var dy = self.chargingTarget.y - self.y;
			var distance = Math.sqrt(dx * dx + dy * dy);
			if (distance > 0) {
				// Charge with high speed
				var chargeSpeed = 12;
				var chargeDistance = 400;
				var targetX = self.x + dx / distance * chargeDistance;
				var targetY = self.y + dy / distance * chargeDistance;
				// Keep within bounds
				targetX = Math.max(100, Math.min(targetX, currentLevelData.width - 100));
				targetY = Math.max(1600, Math.min(targetY, 2400));
				tween(self, {
					x: targetX,
					y: targetY
				}, {
					duration: 300,
					easing: tween.easeOut,
					onFinish: function onFinish() {
						// Reset visual effects
						tween(bossGraphics, {
							tint: 0xffffff,
							scaleX: 1,
							scaleY: 1
						}, {
							duration: 500
						});
						self.isCharging = false;
						self.chargingTarget = null;
					}
				});
				// Damage hero if in path during charge
				LK.setTimeout(function () {
					var distanceToHero = Math.sqrt(Math.pow(self.x - hero.x, 2) + Math.pow(self.y - hero.y, 2));
					if (distanceToHero < 150) {
						hero.takeDamage();
					}
				}, 150);
			}
		}, 800);
	};
	self.spinAttack = function () {
		self.isSpinning = true;
		// Visual spin effect
		tween(bossGraphics, {
			rotation: Math.PI * 4
		}, {
			duration: 1500,
			easing: tween.easeInOut,
			onFinish: function onFinish() {
				bossGraphics.rotation = 0;
				self.isSpinning = false;
			}
		});
		// Scale and tint effect during spin
		tween(bossGraphics, {
			scaleX: 1.5,
			scaleY: 1.5,
			tint: 0xff8800
		}, {
			duration: 750,
			easing: tween.easeOut
		});
		tween(bossGraphics, {
			scaleX: 1,
			scaleY: 1,
			tint: 0xffffff
		}, {
			duration: 750,
			easing: tween.easeIn
		});
		// Multiple damage checks during spin
		for (var i = 0; i < 5; i++) {
			LK.setTimeout(function () {
				var distanceToHero = Math.sqrt(Math.pow(self.x - hero.x, 2) + Math.pow(self.y - hero.y, 2));
				if (distanceToHero < 200) {
					hero.takeDamage();
					LK.effects.flashObject(hero, 0xff0000, 200);
				}
			}, i * 300);
		}
	};
	self.areaAttack = function () {
		// Create warning effect
		LK.effects.flashScreen(0xff4444, 1000);
		// Pulsing boss effect
		tween(bossGraphics, {
			scaleX: 1.3,
			scaleY: 1.3
		}, {
			duration: 500,
			easing: tween.easeInOut
		});
		tween(bossGraphics, {
			scaleX: 1,
			scaleY: 1
		}, {
			duration: 500,
			easing: tween.easeInOut
		});
		// Damage hero if close
		var distanceToHero = Math.sqrt(Math.pow(self.x - hero.x, 2) + Math.pow(self.y - hero.y, 2));
		if (distanceToHero < 400) {
			LK.setTimeout(function () {
				if (Math.sqrt(Math.pow(self.x - hero.x, 2) + Math.pow(self.y - hero.y, 2)) < 400) {
					hero.takeDamage();
				}
			}, 1000);
		}
	};
	self.takeDamage = function (fromKnife) {
		self.health--;
		LK.effects.flashObject(self, 0xffffff, 200);
		// Enhanced visual feedback with scaling
		tween(bossGraphics, {
			scaleX: 1.1,
			scaleY: 1.1
		}, {
			duration: 100,
			easing: tween.easeOut
		});
		tween(bossGraphics, {
			scaleX: 1,
			scaleY: 1
		}, {
			duration: 100,
			easing: tween.easeOut
		});
		// Create blood particles
		for (var p = 0; p < 15; p++) {
			var bloodParticle = game.addChild(getFromPool('bloodParticle'));
			bloodParticle.reset(self.x + (Math.random() - 0.5) * 60, self.y - 100 + (Math.random() - 0.5) * 60);
			bloodParticles.push(bloodParticle);
		}
		if (self.health <= 0) {
			self.die();
		}
	};
	self.die = function () {
		// Big explosion effect
		LK.effects.flashScreen(0xffffff, 2000);
		// Drop multiple coins
		for (var i = 0; i < 10; i++) {
			var coin = game.addChild(new Coin());
			coin.x = self.x + (Math.random() - 0.5) * 200;
			coin.y = self.y + (Math.random() - 0.5) * 200;
			coins.push(coin);
		}
		// Massive score bonus
		LK.setScore(LK.getScore() + 1000);
		hero.addCombo();
		// Remove from enemies array
		for (var i = enemies.length - 1; i >= 0; i--) {
			if (enemies[i] === self) {
				enemies.splice(i, 1);
				break;
			}
		}
		self.destroy();
		updateScoreDisplay();
		updateEnemiesLeftDisplay();
	};
	return self;
});
var BossProjectile = Container.expand(function () {
	var self = Container.call(this);
	var projectileGraphics = self.attachAsset('bossProjectile', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	self.velocityX = 0;
	self.velocityY = 0;
	self.lifetime = 300; // 5 seconds
	self.update = function () {
		self.x += self.velocityX;
		self.y += self.velocityY;
		self.lifetime--;
		// Set rotation to face movement direction
		if (self.velocityX !== 0 || self.velocityY !== 0) {
			var angle = Math.atan2(self.velocityY, self.velocityX);
			projectileGraphics.rotation = angle;
		}
		// Check collision with hero
		var distanceToHero = Math.sqrt(Math.pow(self.x - hero.x, 2) + Math.pow(self.y - hero.y, 2));
		if (distanceToHero < 80) {
			hero.takeDamage();
			self.destroy();
			// Remove from bossProjectiles array
			for (var i = bossProjectiles.length - 1; i >= 0; i--) {
				if (bossProjectiles[i] === self) {
					bossProjectiles.splice(i, 1);
					break;
				}
			}
			return;
		}
		// Remove if lifetime expired or off screen
		if (self.lifetime <= 0 || self.x < -100 || self.x > currentLevelData.width + 100 || self.y < -100 || self.y > 3000) {
			self.destroy();
			// Remove from bossProjectiles array
			for (var i = bossProjectiles.length - 1; i >= 0; i--) {
				if (bossProjectiles[i] === self) {
					bossProjectiles.splice(i, 1);
					break;
				}
			}
		}
	};
	return self;
});
var Coin = Container.expand(function () {
	var self = Container.call(this);
	var coinGraphics = self.attachAsset('coin', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	self.collectTimer = 0;
	self.update = function () {
		// Auto-collect after short delay
		self.collectTimer++;
		if (self.collectTimer > 30) {
			// 0.5 seconds
			self.collect();
		}
		// Spin animation
		coinGraphics.rotation += 0.1;
	};
	self.collect = function () {
		LK.getSound('coin').play();
		// Remove from coins array
		for (var i = coins.length - 1; i >= 0; i--) {
			if (coins[i] === self) {
				coins.splice(i, 1);
				break;
			}
		}
		self.destroy();
	};
	return self;
});
var Enemy = Container.expand(function (enemyType) {
	var self = Container.call(this);
	self.enemyType = enemyType || 'basic';
	// Choose asset based on enemy type
	var assetName = 'enemy';
	if (self.enemyType === 'basic') {
		assetName = 'enemyBasic';
	} else if (self.enemyType === 'strong') {
		assetName = 'enemyStrong';
	} else if (self.enemyType === 'fast') {
		assetName = 'enemyFast';
	} else if (self.enemyType === 'tank') {
		assetName = 'enemyTank';
	} else if (self.enemyType === 'hunter') {
		assetName = 'enemyHunter';
	} else if (self.enemyType === 'assassin') {
		assetName = 'enemyAssassin';
	}
	var enemyGraphics = self.attachAsset(assetName, {
		anchorX: 0.5,
		anchorY: 1.0
	});
	self.health = 2;
	self.speed = 1;
	self.attackCooldown = 0;
	self.fromLeft = true;
	self.lastX = 0;
	self.lastY = 0;
	self.alerted = false;
	self.alertedByKnife = false;
	self.update = function () {
		var distanceToHero = Math.sqrt(Math.pow(self.x - hero.x, 2) + Math.pow(self.y - hero.y, 2));
		// Different sight ranges for different enemy types
		var sightRange = 500;
		if (self.enemyType === 'hunter') {
			sightRange = 800; // Hunters have better sight
		} else if (self.enemyType === 'assassin') {
			sightRange = 600; // Assassins have good sight
		} else if (self.enemyType === 'tank') {
			sightRange = 400; // Tanks have poor sight
		}
		var canSeeHero = distanceToHero < sightRange;
		if (canSeeHero || self.alerted) {
			// Chase hero
			var dx = hero.x - self.x;
			var dy = hero.y - self.y;
			var distance = Math.sqrt(dx * dx + dy * dy);
			if (distance > 0) {
				var moveSpeed = self.speed;
				// Special movement for assassin type - teleport ability
				if (self.enemyType === 'assassin' && distanceToHero > 300 && distanceToHero < 600 && Math.random() < 0.02) {
					// Teleport closer to hero
					var teleportDistance = 150;
					var teleportX = hero.x + (Math.random() - 0.5) * teleportDistance;
					var teleportY = hero.y + (Math.random() - 0.5) * teleportDistance;
					// Keep within bounds
					teleportX = Math.max(100, Math.min(teleportX, currentLevelData.width - 100));
					teleportY = Math.max(1600, Math.min(teleportY, 2400));
					self.x = teleportX;
					self.y = teleportY;
					// Flash effect for teleport
					LK.effects.flashObject(self, 0x9b59b6, 300);
					var newX = self.x;
					var newY = self.y;
				} else {
					// Hunter type - faster when far from hero
					if (self.enemyType === 'hunter' && distanceToHero > 400) {
						moveSpeed *= 1.5; // 50% speed boost when hunting from distance
					}
					var newX = self.x + dx / distance * moveSpeed;
					var newY = self.y + dy / distance * moveSpeed;
				}
				// Check collision with other enemies before moving using spatial partitioning
				var wouldCollide = false;
				var nearbyEnemies = spatialGrid.getNearbyObjects(newX, newY, 100);
				for (var i = 0; i < nearbyEnemies.length; i++) {
					var otherEnemy = nearbyEnemies[i];
					if (otherEnemy === self) continue; // Skip self
					var edx = newX - otherEnemy.x;
					var edy = newY - otherEnemy.y;
					var edistance = Math.sqrt(edx * edx + edy * edy);
					if (edistance < 70) {
						// Collision threshold between enemies
						wouldCollide = true;
						break;
					}
				}
				// Only move if no collision would occur
				if (!wouldCollide) {
					self.lastX = self.x;
					self.lastY = self.y;
					self.x = newX;
					self.y = newY;
				}
				// Face direction of movement
				enemyGraphics.scaleX = dx > 0 ? 1 : -1;
			}
			// Attack hero if close enough
			if (distanceToHero < 100 && self.attackCooldown <= 0) {
				hero.takeDamage();
				self.attackCooldown = 120; // 2 seconds at 60fps
			}
		} else if (!self.alerted) {
			// Only roam if not alerted - alerted enemies keep chasing even when they can't see hero
			if (!self.roamDirection || Math.random() < 0.01) {
				self.roamDirection = {
					x: (Math.random() - 0.5) * 2,
					y: (Math.random() - 0.5) * 2
				};
			}
			var newX = self.x + self.roamDirection.x * self.speed * 0.5;
			var newY = self.y + self.roamDirection.y * self.speed * 0.5;
			// Check collision with other enemies before roaming
			var wouldCollide = false;
			for (var i = 0; i < enemies.length; i++) {
				var otherEnemy = enemies[i];
				if (otherEnemy === self) continue; // Skip self
				var edx = newX - otherEnemy.x;
				var edy = newY - otherEnemy.y;
				var edistance = Math.sqrt(edx * edx + edy * edy);
				if (edistance < 70) {
					// Collision threshold between enemies
					wouldCollide = true;
					break;
				}
			}
			// Keep within level bounds and check collisions
			if (!wouldCollide && newX > 100 && newX < currentLevelData.width - 100) {
				self.lastX = self.x;
				self.x = newX;
				enemyGraphics.scaleX = self.roamDirection.x > 0 ? 1 : -1;
			}
			if (!wouldCollide && newY > 1600 && newY < 2400) {
				self.lastY = self.y;
				self.y = newY;
			}
		} else {
			// Alerted enemies that can't see hero still try to chase in last known direction
			var dx = hero.x - self.x;
			var dy = hero.y - self.y;
			var distance = Math.sqrt(dx * dx + dy * dy);
			if (distance > 0) {
				var newX = self.x + dx / distance * self.speed;
				var newY = self.y + dy / distance * self.speed;
				// Check collision with other enemies before moving
				var wouldCollide = false;
				for (var i = 0; i < enemies.length; i++) {
					var otherEnemy = enemies[i];
					if (otherEnemy === self) continue; // Skip self
					var edx = newX - otherEnemy.x;
					var edy = newY - otherEnemy.y;
					var edistance = Math.sqrt(edx * edx + edy * edy);
					if (edistance < 70) {
						// Collision threshold between enemies
						wouldCollide = true;
						break;
					}
				}
				// Only move if no collision would occur
				if (!wouldCollide) {
					self.lastX = self.x;
					self.lastY = self.y;
					self.x = newX;
					self.y = newY;
				}
				// Face direction of movement
				enemyGraphics.scaleX = dx > 0 ? 1 : -1;
			}
		}
		if (self.attackCooldown > 0) {
			self.attackCooldown--;
		}
	};
	self.takeDamage = function (fromKnife) {
		self.health--;
		LK.effects.flashObject(self, 0xffffff, 200);
		// Create knockback effect
		var knockbackForce = 40;
		var knockbackDirection = fromKnife ? 1 : hero.x < self.x ? 1 : -1;
		var targetX = self.x + knockbackDirection * knockbackForce;
		var targetY = self.y - 20; // Slight upward knockback
		// Apply knockback with bounds checking
		targetX = Math.max(100, Math.min(targetX, currentLevelData.width - 100));
		targetY = Math.max(1600, Math.min(targetY, 2400));
		tween(self, {
			x: targetX,
			y: targetY
		}, {
			duration: 200,
			easing: tween.easeOut
		});
		// Create blood particles
		for (var p = 0; p < 12; p++) {
			var bloodParticle = game.addChild(getFromPool('bloodParticle'));
			bloodParticle.reset(self.x + (Math.random() - 0.5) * 40, self.y - 60 + (Math.random() - 0.5) * 40);
			bloodParticles.push(bloodParticle);
		}
		if (fromKnife) {
			self.alerted = true;
			self.alertedByKnife = true;
		}
		if (self.health <= 0) {
			self.die();
		}
	};
	self.die = function () {
		// Drop coin
		var coin = game.addChild(new Coin());
		coin.x = self.x;
		coin.y = self.y;
		coins.push(coin);
		// 10% chance to drop health potion
		if (Math.random() < 0.1) {
			var healthPotion = game.addChild(new HealthPotion());
			healthPotion.x = self.x + (Math.random() - 0.5) * 80; // Slight random offset
			healthPotion.y = self.y + (Math.random() - 0.5) * 80;
			healthPotions.push(healthPotion);
		}
		// Add score and combo
		var baseScore = 10;
		var comboMultiplier = Math.floor(hero.comboCount / 5) + 1;
		var finalScore = baseScore * comboMultiplier;
		LK.setScore(LK.getScore() + finalScore);
		hero.addCombo();
		// Remove from enemies array
		for (var i = enemies.length - 1; i >= 0; i--) {
			if (enemies[i] === self) {
				enemies.splice(i, 1);
				break;
			}
		}
		self.destroy();
		updateScoreDisplay();
		updateEnemiesLeftDisplay();
	};
	return self;
});
var EnemyWarning = Container.expand(function () {
	var self = Container.call(this);
	self.targetEnemy = null;
	self.direction = 'left'; // 'left', 'right', 'up', 'down'
	self.warningGraphics = null;
	self.lastAlpha = 0;
	self.isVisible = false;
	self.setDirection = function (direction) {
		if (self.warningGraphics) {
			self.warningGraphics.destroy();
		}
		var assetName = 'warningArrow' + direction.charAt(0).toUpperCase() + direction.slice(1);
		self.warningGraphics = self.attachAsset(assetName, {
			anchorX: 0.5,
			anchorY: 0.5,
			alpha: 0
		});
		self.direction = direction;
	};
	self.update = function () {
		if (!self.targetEnemy || !self.warningGraphics) return;
		// Check if enemy is still alive
		var enemyExists = false;
		for (var i = 0; i < enemies.length; i++) {
			if (enemies[i] === self.targetEnemy) {
				enemyExists = true;
				break;
			}
		}
		if (!enemyExists) {
			self.hide();
			return;
		}
		// Calculate distance from hero to enemy
		var distanceToHero = Math.sqrt(Math.pow(self.targetEnemy.x - hero.x, 2) + Math.pow(self.targetEnemy.y - hero.y, 2));
		// Check if enemy is visible on screen
		var enemyScreenX = self.targetEnemy.x - camera.x;
		var enemyScreenY = self.targetEnemy.y - camera.y;
		var isOnScreen = enemyScreenX >= -100 && enemyScreenX <= 2148 && enemyScreenY >= -100 && enemyScreenY <= 2832;
		if (isOnScreen) {
			self.hide();
			return;
		}
		// Calculate warning opacity based on distance (closer = more visible)
		var maxDistance = 800;
		var minDistance = 300;
		var targetAlpha = 0;
		if (distanceToHero <= maxDistance) {
			var normalizedDistance = Math.max(0, Math.min(1, (maxDistance - distanceToHero) / (maxDistance - minDistance)));
			targetAlpha = normalizedDistance * 0.8;
		}
		// Smooth alpha transition
		if (Math.abs(targetAlpha - self.lastAlpha) > 0.01) {
			tween.stop(self.warningGraphics, {
				alpha: true
			});
			tween(self.warningGraphics, {
				alpha: targetAlpha
			}, {
				duration: 200
			});
			self.lastAlpha = targetAlpha;
		}
		// Position warning at screen edge
		var screenCenterX = 1024;
		var screenCenterY = 1366;
		var dx = self.targetEnemy.x - hero.x;
		var dy = self.targetEnemy.y - hero.y;
		if (Math.abs(dx) > Math.abs(dy)) {
			// Horizontal warning
			if (dx > 0) {
				self.setDirection('right');
				self.x = screenCenterX + 900;
				self.y = screenCenterY + Math.max(-600, Math.min(600, dy * 0.5));
			} else {
				self.setDirection('left');
				self.x = screenCenterX - 900;
				self.y = screenCenterY + Math.max(-600, Math.min(600, dy * 0.5));
			}
		} else {
			// Vertical warning
			if (dy > 0) {
				self.setDirection('down');
				self.x = screenCenterX + Math.max(-800, Math.min(800, dx * 0.5));
				self.y = screenCenterY + 1200;
			} else {
				self.setDirection('up');
				self.x = screenCenterX + Math.max(-800, Math.min(800, dx * 0.5));
				self.y = screenCenterY - 1200;
			}
		}
	};
	self.hide = function () {
		if (self.warningGraphics && self.warningGraphics.alpha > 0) {
			tween.stop(self.warningGraphics, {
				alpha: true
			});
			tween(self.warningGraphics, {
				alpha: 0
			}, {
				duration: 300
			});
			self.lastAlpha = 0;
		}
	};
	return self;
});
var HealthPotion = Container.expand(function () {
	var self = Container.call(this);
	var potionGraphics = self.attachAsset('healthPotion', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	self.collectTimer = 0;
	self.update = function () {
		// Auto-collect after short delay
		self.collectTimer++;
		if (self.collectTimer > 30) {
			// 0.5 seconds
			self.collect();
		}
		// Gentle floating animation
		potionGraphics.y = Math.sin(LK.ticks * 0.1) * 5;
		potionGraphics.rotation += 0.05;
	};
	self.collect = function () {
		LK.getSound('powerup').play();
		hero.heal();
		// Remove from healthPotions array
		for (var i = healthPotions.length - 1; i >= 0; i--) {
			if (healthPotions[i] === self) {
				healthPotions.splice(i, 1);
				break;
			}
		}
		self.destroy();
	};
	return self;
});
var Hero = Container.expand(function () {
	var self = Container.call(this);
	var heroGraphics = self.attachAsset('hero', {
		anchorX: 0.5,
		anchorY: 1.0
	});
	self.maxHealth = 5;
	self.health = self.maxHealth;
	self.isAttacking = false;
	self.invulnerable = false;
	self.damageBoost = false;
	self.comboCount = 0;
	self.slashCooldown = 0;
	self.isSlashing = false;
	self.slashGraphics = null;
	self.lastX = 0;
	self.lastY = 0;
	self.isWalking = false;
	self.walkAnimationActive = false;
	self.walkAnimationFrame = 0;
	self.walkAnimationTimer = 0;
	self.walkAnimationSpeed = 10; // frames between texture changes
	self.attack = function (targetX) {
		if (self.isAttacking || self.slashCooldown > 0) return;
		self.isAttacking = true;
		self.isSlashing = true;
		self.slashCooldown = 24; // 0.4 seconds at 60fps
		// Face direction of attack
		if (targetX < self.x) {
			heroGraphics.scaleX = -1;
		} else {
			heroGraphics.scaleX = 1;
		}
		// Replace hero graphic with slash animation
		self.removeChild(heroGraphics);
		self.slashGraphics = self.attachAsset('heroSlash', {
			anchorX: 0.5,
			anchorY: 1.0
		});
		// Match facing direction
		self.slashGraphics.scaleX = heroGraphics.scaleX;
		// Attack animation
		tween(self.slashGraphics, {
			scaleY: 1.2
		}, {
			duration: 100
		});
		tween(self.slashGraphics, {
			scaleY: 1.0
		}, {
			duration: 100,
			onFinish: function onFinish() {
				// Return to normal hero graphic
				self.removeChild(self.slashGraphics);
				heroGraphics = self.attachAsset('hero', {
					anchorX: 0.5,
					anchorY: 1.0
				});
				// Maintain facing direction
				heroGraphics.scaleX = self.slashGraphics.scaleX;
				self.isAttacking = false;
				self.isSlashing = false;
				self.slashGraphics = null;
			}
		});
		// Create sword slash effect
		var effect = game.addChild(LK.getAsset('slashEffect', {
			anchorX: 0.5,
			anchorY: 0.5,
			alpha: 0.9
		}));
		effect.x = self.x + heroGraphics.scaleX * 120;
		effect.y = self.y - 80;
		// Set slash rotation and scale based on direction
		effect.rotation = heroGraphics.scaleX > 0 ? -0.3 : 0.3; // Diagonal slash
		effect.scaleX = heroGraphics.scaleX > 0 ? 1 : -1; // Mirror for left attacks
		tween(effect, {
			scaleX: effect.scaleX * 4,
			scaleY: 4,
			alpha: 0
		}, {
			duration: 150,
			onFinish: function onFinish() {
				effect.destroy();
			}
		});
		LK.getSound('slash').play();
	};
	self.takeDamage = function () {
		if (self.invulnerable) return;
		self.health--;
		self.comboCount = 0;
		// Flash red when hit
		LK.effects.flashObject(self, 0xff0000, 500);
		// Temporary invulnerability
		self.invulnerable = true;
		LK.setTimeout(function () {
			self.invulnerable = false;
		}, 1000);
		LK.getSound('hit').play();
		updateHealthDisplay();
		if (self.health <= 0) {
			LK.showGameOver();
		}
	};
	self.heal = function () {
		if (self.health < self.maxHealth) {
			self.health++;
			updateHealthDisplay();
		}
	};
	self.addCombo = function () {
		self.comboCount++;
	};
	self.startWalkAnimation = function () {
		if (self.walkAnimationActive) return;
		self.walkAnimationActive = true;
		self.walkAnimationFrame = 0;
		self.walkAnimationTimer = 0;
	};
	self.stopWalkAnimation = function () {
		self.isWalking = false;
		self.walkAnimationActive = false;
		// Store current scale direction before removing graphics
		var currentScaleX = heroGraphics.scaleX;
		// Reset to default hero texture
		self.removeChild(heroGraphics);
		heroGraphics = self.attachAsset('hero', {
			anchorX: 0.5,
			anchorY: 1.0
		});
		// Maintain the current scale direction
		if (currentScaleX < 0) {
			heroGraphics.scaleX = -1;
		}
	};
	self.move = function (direction) {
		var speed = 8;
		var newX = self.x;
		var newY = self.y;
		var didMove = false;
		if (direction === 'left' && self.x > 100) {
			newX = self.x - speed;
			heroGraphics.scaleX = -1;
			didMove = true;
		} else if (direction === 'right' && self.x < currentLevelData.width - 100) {
			newX = self.x + speed;
			heroGraphics.scaleX = 1;
			didMove = true;
		} else if (direction === 'up' && self.y > 1600) {
			newY = self.y - speed;
			didMove = true;
		} else if (direction === 'down' && self.y < 2400) {
			newY = self.y + speed;
			didMove = true;
		}
		// Check collision with enemies before moving using spatial partitioning
		var wouldCollide = false;
		var nearbyEnemies = spatialGrid.getNearbyObjects(newX, newY, 150);
		for (var i = 0; i < nearbyEnemies.length; i++) {
			var enemy = nearbyEnemies[i];
			var dx = newX - enemy.x;
			var dy = newY - enemy.y;
			var distance = Math.sqrt(dx * dx + dy * dy);
			if (distance < 120) {
				// Increased collision threshold to prevent overlap
				wouldCollide = true;
				break;
			}
		}
		// Only move if no collision would occur
		if (!wouldCollide && didMove) {
			self.lastX = self.x;
			self.lastY = self.y;
			self.x = newX;
			self.y = newY;
			// Start walking animation
			if (!self.isWalking) {
				self.isWalking = true;
				self.startWalkAnimation();
			}
			// Update walking animation texture cycling
			if (self.walkAnimationActive) {
				self.walkAnimationTimer++;
				if (self.walkAnimationTimer >= self.walkAnimationSpeed) {
					self.walkAnimationTimer = 0;
					self.walkAnimationFrame = (self.walkAnimationFrame + 1) % 4;
					// Cycle through textures: hero, heroWalk1, heroWalk2, heroWalk3
					var textureNames = ['hero', 'heroWalk1', 'heroWalk2', 'heroWalk3'];
					// Store current scale direction before removing graphics
					var currentScaleX = heroGraphics.scaleX;
					// Remove current graphics and add new one with correct texture
					self.removeChild(heroGraphics);
					heroGraphics = self.attachAsset(textureNames[self.walkAnimationFrame], {
						anchorX: 0.5,
						anchorY: 1.0
					});
					// Maintain the current scale direction
					if (currentScaleX < 0) {
						heroGraphics.scaleX = -1;
					}
				}
			}
		}
		// Update camera target
		camera.targetX = self.x - 1024;
		camera.targetY = self.y - 1366;
		// Clamp camera to level bounds
		camera.targetX = Math.max(0, Math.min(camera.targetX, currentLevelData.width - 2048));
		camera.targetY = Math.max(0, Math.min(camera.targetY, currentLevelData.height - 2732));
	};
	self.update = function () {
		if (self.slashCooldown > 0) {
			self.slashCooldown--;
		}
	};
	return self;
});
var Knife = Container.expand(function () {
	var self = Container.call(this);
	var knifeGraphics = self.attachAsset('knife', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	self.speed = 15;
	self.direction = 1; // 1 for right, -1 for left
	self.routePoints = []; // Points to follow along the route
	self.currentRouteIndex = 0; // Current target point index
	self.velocityX = 0; // Velocity for precise targeting
	self.velocityY = 0; // Velocity for precise targeting
	self.lastX = 0;
	self.lastY = 0;
	self.setRoute = function (routePoints) {
		self.routePoints = routePoints;
		self.currentRouteIndex = 0;
	};
	self.update = function () {
		self.lastX = self.x;
		self.lastY = self.y;
		// Follow route if available
		if (self.routePoints.length > 0 && self.currentRouteIndex < self.routePoints.length) {
			var targetPoint = self.routePoints[self.currentRouteIndex];
			var dx = targetPoint.x - self.x;
			var dy = targetPoint.y - self.y;
			var distance = Math.sqrt(dx * dx + dy * dy);
			if (distance < 20) {
				// Close enough to current target, move to next point
				self.currentRouteIndex++;
			} else {
				// Move toward current target point
				if (distance > 0) {
					self.x += dx / distance * self.speed;
					self.y += dy / distance * self.speed;
					// Calculate rotation angle to face target direction
					var angle = Math.atan2(dy, dx);
					knifeGraphics.rotation = angle;
				}
			}
		} else if (self.velocityX !== 0 || self.velocityY !== 0) {
			// Use velocity if set, otherwise use direction-based movement
			self.x += self.velocityX;
			self.y += self.velocityY;
			// Calculate rotation for velocity-based movement
			var angle = Math.atan2(self.velocityY, self.velocityX);
			knifeGraphics.rotation = angle;
		} else {
			self.x += self.speed * self.direction;
			self.y -= 2; // Slight upward arc
			// Calculate rotation for direction-based movement
			var angle = Math.atan2(-2, self.speed * self.direction);
			knifeGraphics.rotation = angle;
		}
		// Check if knife went off screen
		if (self.x < -100 || self.x > currentLevelData.width + 100 || self.y < -100 || self.y > 2900) {
			self.destroy();
			// Remove from knives array
			for (var i = knives.length - 1; i >= 0; i--) {
				if (knives[i] === self) {
					knives.splice(i, 1);
					break;
				}
			}
		}
		// Check collision with enemies (hit enemy center)
		for (var i = 0; i < enemies.length; i++) {
			var enemy = enemies[i];
			var dx = self.x - enemy.x;
			var dy = self.y - (enemy.y - 70); // Target enemy center
			var distance = Math.sqrt(dx * dx + dy * dy);
			if (distance < 50) {
				// Hit enemy center - deal damage only to first enemy encountered
				enemy.takeDamage(true);
				self.destroy();
				// Remove from knives array
				for (var j = knives.length - 1; j >= 0; j--) {
					if (knives[j] === self) {
						knives.splice(j, 1);
						break;
					}
				}
				return; // Exit update immediately to prevent further movement or collision checks
			}
		}
	};
	return self;
});
var PowerUp = Container.expand(function () {
	var self = Container.call(this);
	var powerupGraphics = self.attachAsset('powerup', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	self.type = 'health'; // 'health', 'invulnerable', 'damage'
	self.lifetime = 600; // 10 seconds
	self.update = function () {
		self.lifetime--;
		if (self.lifetime <= 0) {
			self.expire();
		}
		// Check collision with hero
		if (self.intersects(hero)) {
			self.collect();
		}
		// Pulse animation
		var scale = 1 + Math.sin(LK.ticks * 0.2) * 0.2;
		powerupGraphics.scaleX = scale;
		powerupGraphics.scaleY = scale;
	};
	self.collect = function () {
		LK.getSound('powerup').play();
		if (self.type === 'health') {
			hero.heal();
		} else if (self.type === 'invulnerable') {
			hero.invulnerable = true;
			LK.setTimeout(function () {
				hero.invulnerable = false;
			}, 5000);
		} else if (self.type === 'damage') {
			hero.damageBoost = true;
			LK.setTimeout(function () {
				hero.damageBoost = false;
			}, 5000);
		}
		// Remove from powerups array
		for (var i = powerups.length - 1; i >= 0; i--) {
			if (powerups[i] === self) {
				powerups.splice(i, 1);
				break;
			}
		}
		self.destroy();
	};
	self.expire = function () {
		// Remove from powerups array
		for (var i = powerups.length - 1; i >= 0; i--) {
			if (powerups[i] === self) {
				powerups.splice(i, 1);
				break;
			}
		}
		self.destroy();
	};
	return self;
});
var RouteEffect = Container.expand(function () {
	var self = Container.call(this);
	self.routePoints = [];
	self.routePositions = []; // Store just the x,y coordinates for knife to follow
	self.targetEnemy = null;
	self.createRoute = function (enemy) {
		self.targetEnemy = enemy;
		self.clearRoute();
		// Calculate direct path from hero center to enemy center
		var heroStartX = hero.x;
		var heroStartY = hero.y - 80; // Middle of hero asset
		var enemyMiddleX = enemy.x;
		var enemyMiddleY = enemy.y - 70; // Middle of enemy asset (enemy height is 140, so middle is -70 from bottom)
		var dx = enemyMiddleX - heroStartX;
		var dy = enemyMiddleY - heroStartY;
		var distance = Math.sqrt(dx * dx + dy * dy);
		if (distance > 0) {
			// Create route points along the path
			var numPoints = Math.floor(distance / 50); // Point every 50 pixels
			for (var i = 0; i <= numPoints; i++) {
				var t = i / numPoints;
				var x = heroStartX + dx * t;
				var y = heroStartY + dy * t;
				var routePoint = self.addChild(LK.getAsset('routeLine', {
					anchorX: 0.5,
					anchorY: 0.5,
					alpha: 0
				}));
				routePoint.x = x;
				routePoint.y = y;
				self.routePoints.push(routePoint);
				// Store position for knife to follow
				self.routePositions.push({
					x: x,
					y: y
				});
				// Animate route points appearing with delay
				tween(routePoint, {
					alpha: 0.8,
					scaleX: 2,
					scaleY: 2
				}, {
					duration: 100 + i * 20
				});
			}
			// Add impact effect at enemy position
			var impactEffect = self.addChild(LK.getAsset('routeEffect', {
				anchorX: 0.5,
				anchorY: 0.5,
				alpha: 0,
				scaleX: 0.5,
				scaleY: 0.5
			}));
			impactEffect.x = enemyMiddleX;
			impactEffect.y = enemyMiddleY;
			self.routePoints.push(impactEffect);
			// Animate impact effect
			tween(impactEffect, {
				alpha: 1,
				scaleX: 3,
				scaleY: 3
			}, {
				duration: 300,
				easing: tween.easeOut
			});
			// Remove route after 0.5 seconds
			LK.setTimeout(function () {
				self.destroy();
				// Remove from routeEffects array
				for (var i = routeEffects.length - 1; i >= 0; i--) {
					if (routeEffects[i] === self) {
						routeEffects.splice(i, 1);
						break;
					}
				}
			}, 500);
		}
	};
	self.clearRoute = function () {
		for (var i = self.routePoints.length - 1; i >= 0; i--) {
			self.routePoints[i].destroy();
		}
		self.routePoints = [];
	};
	self.fadeOut = function () {
		for (var i = 0; i < self.routePoints.length; i++) {
			tween(self.routePoints[i], {
				alpha: 0,
				scaleX: 0.1,
				scaleY: 0.1
			}, {
				duration: 200
			});
		}
		LK.setTimeout(function () {
			self.destroy();
		}, 300);
	};
	return self;
});
/**** 
* Initialize Game
****/ 
var game = new LK.Game({
	backgroundColor: 0x2c3e50,
	title: 'Dungeon Crawler'
});
/**** 
* Game Code
****/ 
// Game variables
var hero;
var enemies = [];
var coins = [];
var powerups = [];
var enemyWarnings = [];
var knives = [];
var knivesRemaining = 5;
var routeEffects = [];
var bloodParticles = [];
var bossProjectiles = [];
var currentDungeon = 1;
var dungeonComplete = false;
var hearts = [];
var healthPotions = [];
// Object pooling system
var objectPools = {
	bloodParticle: [],
	bossProjectile: [],
	coin: [],
	healthPotion: []
};
// Spatial partitioning system
var spatialGrid = {
	cellSize: 200,
	grid: {},
	clear: function clear() {
		this.grid = {};
	},
	getCellKey: function getCellKey(x, y) {
		var cellX = Math.floor(x / this.cellSize);
		var cellY = Math.floor(y / this.cellSize);
		return cellX + ',' + cellY;
	},
	addObject: function addObject(obj, x, y) {
		var key = this.getCellKey(x, y);
		if (!this.grid[key]) {
			this.grid[key] = [];
		}
		this.grid[key].push(obj);
	},
	getNearbyObjects: function getNearbyObjects(x, y, radius) {
		var nearby = [];
		var cellRadius = Math.ceil(radius / this.cellSize);
		var centerCellX = Math.floor(x / this.cellSize);
		var centerCellY = Math.floor(y / this.cellSize);
		for (var dx = -cellRadius; dx <= cellRadius; dx++) {
			for (var dy = -cellRadius; dy <= cellRadius; dy++) {
				var key = centerCellX + dx + ',' + (centerCellY + dy);
				if (this.grid[key]) {
					nearby = nearby.concat(this.grid[key]);
				}
			}
		}
		return nearby;
	}
};
function getFromPool(type) {
	if (objectPools[type] && objectPools[type].length > 0) {
		var obj = objectPools[type].pop();
		obj.visible = true;
		return obj;
	}
	// Create new object if pool is empty
	if (type === 'bloodParticle') {
		return new BloodParticle();
	} else if (type === 'bossProjectile') {
		return new BossProjectile();
	} else if (type === 'coin') {
		return new Coin();
	} else if (type === 'healthPotion') {
		return new HealthPotion();
	}
	return null;
}
function returnToPool(obj, type) {
	if (!objectPools[type]) {
		objectPools[type] = [];
	}
	obj.visible = false;
	obj.x = -1000; // Move off screen
	obj.y = -1000;
	objectPools[type].push(obj);
}
// Movement state tracking
var movementState = {
	left: false,
	right: false,
	up: false,
	down: false
};
// Camera system
var camera = {
	x: 0,
	y: 0,
	targetX: 0,
	targetY: 0,
	smoothing: 0.1
};
// Dungeon configuration
var levels = [{
	enemies: [{
		type: 'basic',
		count: 3
	}],
	width: 4096,
	height: 2732
}, {
	enemies: [{
		type: 'basic',
		count: 5
	}, {
		type: 'strong',
		count: 2
	}],
	width: 5120,
	height: 2732
}, {
	enemies: [{
		type: 'basic',
		count: 7
	}, {
		type: 'strong',
		count: 3
	}, {
		type: 'fast',
		count: 2
	}],
	width: 6144,
	height: 2732
}, {
	enemies: [{
		type: 'basic',
		count: 5
	}, {
		type: 'strong',
		count: 4
	}, {
		type: 'fast',
		count: 3
	}, {
		type: 'tank',
		count: 2
	}],
	width: 7168,
	height: 2732
}, {
	enemies: [{
		type: 'basic',
		count: 8
	}, {
		type: 'strong',
		count: 4
	}, {
		type: 'fast',
		count: 4
	}, {
		type: 'tank',
		count: 2
	}, {
		type: 'hunter',
		count: 3
	}],
	width: 8192,
	height: 2732
}, {
	enemies: [{
		type: 'boss',
		count: 1
	}],
	width: 9216,
	height: 2732
}, {
	enemies: [{
		type: 'basic',
		count: 10
	}, {
		type: 'strong',
		count: 6
	}, {
		type: 'fast',
		count: 5
	}, {
		type: 'tank',
		count: 3
	}, {
		type: 'hunter',
		count: 4
	}, {
		type: 'assassin',
		count: 2
	}],
	width: 10240,
	height: 2732
}, {
	enemies: [{
		type: 'basic',
		count: 12
	}, {
		type: 'strong',
		count: 8
	}, {
		type: 'fast',
		count: 6
	}, {
		type: 'tank',
		count: 4
	}, {
		type: 'hunter',
		count: 5
	}, {
		type: 'assassin',
		count: 4
	}],
	width: 12288,
	height: 2732
}];
var currentLevelData = levels[0];
// UI Elements
var scoreText = new Text2('Score: 0', {
	size: 80,
	fill: 0xFFFFFF
});
scoreText.anchor.set(0, 0);
scoreText.x = 150;
scoreText.y = 50;
LK.gui.topLeft.addChild(scoreText);
var levelText = new Text2('Dungeon: 1', {
	size: 80,
	fill: 0xFFFFFF
});
levelText.anchor.set(0.5, 0);
LK.gui.top.addChild(levelText);
levelText.y = 50;
var comboText = new Text2('Combo: 0x', {
	size: 60,
	fill: 0xFFFF00
});
comboText.anchor.set(1, 0);
LK.gui.topRight.addChild(comboText);
comboText.x = -50;
comboText.y = 120;
var knivesText = new Text2('Knives: 5', {
	size: 60,
	fill: 0x8e44ad
});
knivesText.anchor.set(1, 0);
LK.gui.topRight.addChild(knivesText);
knivesText.x = -50;
knivesText.y = 190;
var enemiesLeftText = new Text2('Enemies: 0', {
	size: 60,
	fill: 0xff4444
});
enemiesLeftText.anchor.set(1, 0);
LK.gui.topRight.addChild(enemiesLeftText);
enemiesLeftText.x = -50;
enemiesLeftText.y = 260;
// Create movement and attack buttons
var leftButton = LK.getAsset('leftButton', {
	anchorX: 0.5,
	anchorY: 0.5,
	alpha: 0.8,
	scaleX: 1.5,
	scaleY: 1.5
});
leftButton.x = 80;
leftButton.y = -300;
LK.gui.bottomLeft.addChild(leftButton);
var rightButton = LK.getAsset('rightButton', {
	anchorX: 0.5,
	anchorY: 0.5,
	alpha: 0.8,
	scaleX: 1.5,
	scaleY: 1.5
});
rightButton.x = 480;
rightButton.y = -300;
LK.gui.bottomLeft.addChild(rightButton);
var upButton = LK.getAsset('upButton', {
	anchorX: 0.5,
	anchorY: 0.5,
	alpha: 0.8,
	scaleX: 1.5,
	scaleY: 1.5
});
upButton.x = 280;
upButton.y = -480;
LK.gui.bottomLeft.addChild(upButton);
var downButton = LK.getAsset('downButton', {
	anchorX: 0.5,
	anchorY: 0.5,
	alpha: 0.8,
	scaleX: 1.5,
	scaleY: 1.5
});
downButton.x = 280;
downButton.y = -120;
LK.gui.bottomLeft.addChild(downButton);
var attackButton = LK.getAsset('attackButton', {
	anchorX: 0.5,
	anchorY: 0.5,
	alpha: 0.9
});
attackButton.x = -150;
attackButton.y = -200;
LK.gui.bottomRight.addChild(attackButton);
var knifeButton = LK.getAsset('knifeButton', {
	anchorX: 0.5,
	anchorY: 0.5,
	alpha: 0.9
});
knifeButton.x = -350;
knifeButton.y = -200;
LK.gui.bottomRight.addChild(knifeButton);
// Create background grid
var backgroundTiles = [];
function createBackgroundGrid() {
	// Clear existing background tiles
	for (var i = backgroundTiles.length - 1; i >= 0; i--) {
		backgroundTiles[i].destroy();
	}
	backgroundTiles = [];
	var tileSize = 200;
	var tilesX = Math.ceil(currentLevelData.width / tileSize) + 2;
	var tilesY = Math.ceil(currentLevelData.height / tileSize) + 2;
	for (var x = 0; x < tilesX; x++) {
		for (var y = 0; y < tilesY; y++) {
			var tile = game.addChild(LK.getAsset('backgroundTile', {
				anchorX: 0,
				anchorY: 0,
				alpha: 0.3
			}));
			tile.x = x * tileSize;
			tile.y = y * tileSize;
			// Add subtle pattern variation
			if ((x + y) % 2 === 0) {
				tile.alpha = 0.2;
			}
			backgroundTiles.push(tile);
		}
	}
}
// Add background
var background = game.addChild(LK.getAsset('background', {
	anchorX: 0,
	anchorY: 0,
	x: 0,
	y: 0,
	alpha: 1
}));
// Create hero
hero = game.addChild(new Hero());
hero.x = 1024; // Center of screen
hero.y = 2300; // Near bottom
// Create health display
function updateHealthDisplay() {
	// Remove existing hearts
	for (var i = hearts.length - 1; i >= 0; i--) {
		hearts[i].destroy();
	}
	hearts = [];
	// Create new hearts
	for (var i = 0; i < hero.health; i++) {
		var heart = LK.getAsset('heart', {
			anchorX: 0.5,
			anchorY: 0.5
		});
		heart.x = 200 + i * 80;
		heart.y = 200;
		LK.gui.topLeft.addChild(heart);
		hearts.push(heart);
	}
}
function updateScoreDisplay() {
	scoreText.setText('Score: ' + LK.getScore());
	var comboMultiplier = Math.floor(hero.comboCount / 5) + 1;
	comboText.setText('Combo: ' + comboMultiplier + 'x');
}
function updateKnivesDisplay() {
	knivesText.setText('Knives: ' + knivesRemaining);
	// Update button alpha based on availability
	knifeButton.alpha = knivesRemaining > 0 ? 0.9 : 0.3;
}
function updateEnemiesLeftDisplay() {
	enemiesLeftText.setText('Enemies: ' + enemies.length);
}
function initializeLevel() {
	// Show boss fight alert for final dungeon
	if (currentDungeon === levels.length) {
		LK.setTimeout(function () {
			alert('FINAL BOSS APPROACHING! Prepare for the ultimate challenge!');
		}, 500);
	}
	// Clear existing enemies
	for (var i = enemies.length - 1; i >= 0; i--) {
		enemies[i].destroy();
	}
	enemies = [];
	// Clear existing warnings
	for (var i = enemyWarnings.length - 1; i >= 0; i--) {
		enemyWarnings[i].destroy();
	}
	enemyWarnings = [];
	// Clear existing knives
	for (var i = knives.length - 1; i >= 0; i--) {
		knives[i].destroy();
	}
	knives = [];
	// Clear existing route effects
	for (var i = routeEffects.length - 1; i >= 0; i--) {
		routeEffects[i].destroy();
	}
	routeEffects = [];
	// Clear existing blood particles
	for (var i = bloodParticles.length - 1; i >= 0; i--) {
		bloodParticles[i].destroy();
	}
	bloodParticles = [];
	// Clear existing boss projectiles
	for (var i = bossProjectiles.length - 1; i >= 0; i--) {
		bossProjectiles[i].destroy();
	}
	bossProjectiles = [];
	// Clear existing health potions
	for (var i = healthPotions.length - 1; i >= 0; i--) {
		healthPotions[i].destroy();
	}
	healthPotions = [];
	// Reset knife count
	knivesRemaining = 5;
	currentLevelData = levels[currentDungeon - 1] || levels[levels.length - 1];
	dungeonComplete = false;
	// Spawn enemies for this level
	for (var j = 0; j < currentLevelData.enemies.length; j++) {
		var enemyGroup = currentLevelData.enemies[j];
		for (var k = 0; k < enemyGroup.count; k++) {
			spawnEnemy(enemyGroup.type);
		}
	}
	levelText.setText('Dungeon: ' + currentDungeon);
	updateKnivesDisplay();
	updateEnemiesLeftDisplay();
	// Create background grid for this level
	createBackgroundGrid();
}
function spawnEnemy(type) {
	var enemy;
	if (type === 'boss') {
		enemy = game.addChild(new Boss());
		// Boss spawns at center of level
		enemy.x = currentLevelData.width / 2;
		enemy.y = 2200;
	} else {
		enemy = game.addChild(new Enemy(type));
		// Find a spawn position that's not too close to the hero
		var minDistanceFromPlayer = 500; // Minimum distance from player
		var attempts = 0;
		var maxAttempts = 20;
		var enemyX, enemyY;
		do {
			// Random spawn position within level bounds
			enemyX = 200 + Math.random() * (currentLevelData.width - 400);
			enemyY = 1700 + Math.random() * 600;
			// Calculate distance from hero
			var dx = enemyX - hero.x;
			var dy = enemyY - hero.y;
			var distanceFromPlayer = Math.sqrt(dx * dx + dy * dy);
			attempts++;
			// If far enough from player or we've tried too many times, use this position
			if (distanceFromPlayer >= minDistanceFromPlayer || attempts >= maxAttempts) {
				enemy.x = enemyX;
				enemy.y = enemyY;
				break;
			}
		} while (attempts < maxAttempts);
		// Set enemy properties based on type
		if (type === 'basic') {
			enemy.health = 2;
			enemy.speed = 2;
		} else if (type === 'strong') {
			enemy.health = 4;
			enemy.speed = 0.8;
		} else if (type === 'fast') {
			enemy.health = 1;
			enemy.speed = 4;
		} else if (type === 'tank') {
			enemy.health = 8;
			enemy.speed = 0.5;
		} else if (type === 'hunter') {
			enemy.health = 3;
			enemy.speed = 2.5;
		} else if (type === 'assassin') {
			enemy.health = 2;
			enemy.speed = 3;
		}
	}
	enemies.push(enemy);
}
function spawnPowerUp() {
	var powerup = game.addChild(new PowerUp());
	powerup.x = 500 + Math.random() * 1048; // Random x position
	powerup.y = 1800 + Math.random() * 400; // Above ground level
	// Random powerup type
	var types = ['health', 'invulnerable', 'damage'];
	powerup.type = types[Math.floor(Math.random() * types.length)];
	// Color by type
	var powerupGraphics = powerup.getChildAt(0);
	if (powerup.type === 'health') {
		powerupGraphics.tint = 0x00ff00; // Green
	} else if (powerup.type === 'invulnerable') {
		powerupGraphics.tint = 0x0088ff; // Blue
	} else if (powerup.type === 'damage') {
		powerupGraphics.tint = 0xff8800; // Orange
	}
	powerups.push(powerup);
}
function findNearestEnemy(x, y) {
	var nearest = null;
	var shortestDistance = Infinity;
	for (var i = 0; i < enemies.length; i++) {
		var enemy = enemies[i];
		var distance = Math.sqrt(Math.pow(enemy.x - x, 2) + Math.pow(enemy.y - y, 2));
		if (distance < shortestDistance) {
			shortestDistance = distance;
			nearest = enemy;
		}
	}
	return nearest;
}
function isVisible(obj, buffer) {
	buffer = buffer || 100;
	var objScreenX = obj.x - camera.x;
	var objScreenY = obj.y - camera.y;
	return objScreenX >= -buffer && objScreenX <= 2048 + buffer && objScreenY >= -buffer && objScreenY <= 2732 + buffer;
}
function updateEnemyWarnings() {
	// Remove warnings for dead enemies
	for (var i = enemyWarnings.length - 1; i >= 0; i--) {
		var warning = enemyWarnings[i];
		var enemyExists = false;
		for (var j = 0; j < enemies.length; j++) {
			if (enemies[j] === warning.targetEnemy) {
				enemyExists = true;
				break;
			}
		}
		if (!enemyExists) {
			warning.destroy();
			enemyWarnings.splice(i, 1);
		}
	}
	// Create warnings for new enemies
	for (var i = 0; i < enemies.length; i++) {
		var enemy = enemies[i];
		var hasWarning = false;
		for (var j = 0; j < enemyWarnings.length; j++) {
			if (enemyWarnings[j].targetEnemy === enemy) {
				hasWarning = true;
				break;
			}
		}
		if (!hasWarning) {
			var warning = LK.gui.center.addChild(new EnemyWarning());
			warning.targetEnemy = enemy;
			warning.setDirection('left');
			enemyWarnings.push(warning);
		}
	}
}
// Initialize UI
updateHealthDisplay();
updateScoreDisplay();
updateKnivesDisplay();
updateEnemiesLeftDisplay();
// Initialize first level
initializeLevel();
// Create initial background grid
createBackgroundGrid();
// Set initial camera position
camera.targetX = hero.x - 1024;
camera.targetY = hero.y - 1366;
camera.x = camera.targetX;
camera.y = camera.targetY;
// Button event handlers
leftButton.down = function (x, y, obj) {
	movementState.left = true;
};
leftButton.up = function (x, y, obj) {
	movementState.left = false;
};
// Global function to check which button is under a given position
function getButtonUnderPosition(screenX, screenY) {
	// Convert GUI coordinates and check bounds for each button (accounting for 1.5x scale)
	var scaledButtonWidth = 200 * 1.5;
	var scaledButtonHeight = 200 * 1.5;
	var leftBounds = {
		x: leftButton.x - scaledButtonWidth / 2,
		y: leftButton.y - scaledButtonHeight / 2,
		width: scaledButtonWidth,
		height: scaledButtonHeight
	};
	var rightBounds = {
		x: rightButton.x - scaledButtonWidth / 2,
		y: rightButton.y - scaledButtonHeight / 2,
		width: scaledButtonWidth,
		height: scaledButtonHeight
	};
	var upBounds = {
		x: upButton.x - scaledButtonWidth / 2,
		y: upButton.y - scaledButtonHeight / 2,
		width: scaledButtonWidth,
		height: scaledButtonHeight
	};
	var downBounds = {
		x: downButton.x - scaledButtonWidth / 2,
		y: downButton.y - scaledButtonHeight / 2,
		width: scaledButtonWidth,
		height: scaledButtonHeight
	};
	// Adjust screen coordinates relative to bottomLeft GUI
	var relativeX = screenX;
	var relativeY = screenY - (2732 - 500); // Approximate bottomLeft offset
	if (relativeX >= leftBounds.x && relativeX <= leftBounds.x + leftBounds.width && relativeY >= leftBounds.y && relativeY <= leftBounds.y + leftBounds.height) {
		return 'left';
	}
	if (relativeX >= rightBounds.x && relativeX <= rightBounds.x + rightBounds.width && relativeY >= rightBounds.y && relativeY <= rightBounds.y + rightBounds.height) {
		return 'right';
	}
	if (relativeX >= upBounds.x && relativeX <= upBounds.x + upBounds.width && relativeY >= upBounds.y && relativeY <= upBounds.y + upBounds.height) {
		return 'up';
	}
	if (relativeX >= downBounds.x && relativeX <= downBounds.x + downBounds.width && relativeY >= downBounds.y && relativeY <= downBounds.y + downBounds.height) {
		return 'down';
	}
	return null;
}
// Global movement handling function
function handleMovementInput(direction) {
	// Reset all movement states first
	movementState.left = false;
	movementState.right = false;
	movementState.up = false;
	movementState.down = false;
	// Set the active direction
	if (direction === 'left') {
		movementState.left = true;
	} else if (direction === 'right') {
		movementState.right = true;
	} else if (direction === 'up') {
		movementState.up = true;
	} else if (direction === 'down') {
		movementState.down = true;
	}
}
leftButton.move = function (x, y, obj) {
	var currentButton = getButtonUnderPosition(x, y);
	if (currentButton) {
		handleMovementInput(currentButton);
	} else {
		handleMovementInput('left');
	}
};
rightButton.down = function (x, y, obj) {
	movementState.right = true;
};
rightButton.up = function (x, y, obj) {
	movementState.right = false;
};
rightButton.move = function (x, y, obj) {
	var currentButton = getButtonUnderPosition(x, y);
	if (currentButton) {
		handleMovementInput(currentButton);
	} else {
		handleMovementInput('right');
	}
};
upButton.down = function (x, y, obj) {
	movementState.up = true;
};
upButton.up = function (x, y, obj) {
	movementState.up = false;
};
upButton.move = function (x, y, obj) {
	var currentButton = getButtonUnderPosition(x, y);
	if (currentButton) {
		handleMovementInput(currentButton);
	} else {
		handleMovementInput('up');
	}
};
downButton.down = function (x, y, obj) {
	movementState.down = true;
};
downButton.up = function (x, y, obj) {
	movementState.down = false;
};
downButton.move = function (x, y, obj) {
	var currentButton = getButtonUnderPosition(x, y);
	if (currentButton) {
		handleMovementInput(currentButton);
	} else {
		handleMovementInput('down');
	}
};
attackButton.down = function (x, y, obj) {
	if (!hero.isAttacking) {
		var nearestEnemy = findNearestEnemy(hero.x, hero.y);
		if (nearestEnemy) {
			hero.attack(nearestEnemy.x);
			// Check if attack hits with increased range
			var distanceToEnemy = Math.sqrt(Math.pow(hero.x - nearestEnemy.x, 2) + Math.pow(hero.y - nearestEnemy.y, 2));
			if (distanceToEnemy < 350) {
				// Increased from 250 to 350
				var damage = hero.damageBoost ? 2 : 1;
				for (var i = 0; i < damage; i++) {
					nearestEnemy.takeDamage();
				}
			}
		} else {
			// Attack in hero's facing direction
			hero.attack(hero.x + (hero.getChildAt(0).scaleX > 0 ? 100 : -100));
		}
	}
};
knifeButton.down = function (x, y, obj) {
	if (knivesRemaining > 0) {
		// Find nearest enemy for targeting
		var nearestEnemy = findNearestEnemy(hero.x, hero.y);
		if (nearestEnemy) {
			// Clear existing route effects before creating new one
			for (var i = routeEffects.length - 1; i >= 0; i--) {
				routeEffects[i].destroy();
				routeEffects.splice(i, 1);
			}
			// Create route visualization
			var routeEffect = game.addChild(new RouteEffect());
			routeEffect.createRoute(nearestEnemy);
			routeEffects.push(routeEffect);
			// Throw knife to follow the route
			var knife = game.addChild(new Knife());
			knife.x = hero.x;
			knife.y = hero.y - 80;
			// Set the route for the knife to follow
			knife.setRoute(routeEffect.routePositions);
			// Rotation will be handled automatically in knife update based on target direction
			knives.push(knife);
			knivesRemaining--;
			updateKnivesDisplay();
			LK.getSound('knifeThrow').play();
		} else {
			// No enemy found, throw in hero facing direction
			var knife = game.addChild(new Knife());
			knife.x = hero.x;
			knife.y = hero.y - 80;
			var heroGraphics = hero.getChildAt(0);
			knife.direction = heroGraphics.scaleX > 0 ? 1 : -1;
			// Rotation will be handled automatically in knife update based on movement direction
			knives.push(knife);
			knivesRemaining--;
			updateKnivesDisplay();
			LK.getSound('knifeThrow').play();
		}
	}
};
// Game input (fallback for screen taps outside buttons)
game.down = function (x, y, obj) {
	// Convert screen coordinates to world coordinates
	var worldX = x + camera.x;
	var worldY = y + camera.y;
	// Check if tap is for movement or attack
	var distanceToHero = Math.sqrt(Math.pow(worldX - hero.x, 2) + Math.pow(worldY - hero.y, 2));
	if (distanceToHero > 200) {
		// Movement - move toward tap position
		var dx = worldX - hero.x;
		var dy = worldY - hero.y;
		if (Math.abs(dx) > Math.abs(dy)) {
			hero.move(dx > 0 ? 'right' : 'left');
		} else {
			hero.move(dy > 0 ? 'down' : 'up');
		}
	} else {
		// Attack
		if (!hero.isAttacking) {
			var nearestEnemy = findNearestEnemy(worldX, worldY);
			if (nearestEnemy) {
				hero.attack(nearestEnemy.x);
				// Check if attack hits with increased range
				var distanceToEnemy = Math.sqrt(Math.pow(hero.x - nearestEnemy.x, 2) + Math.pow(hero.y - nearestEnemy.y, 2));
				if (distanceToEnemy < 350) {
					// Increased from 250 to 350
					var damage = hero.damageBoost ? 2 : 1;
					for (var i = 0; i < damage; i++) {
						nearestEnemy.takeDamage();
					}
				}
			} else {
				// Attack in direction of tap
				hero.attack(worldX);
			}
		}
	}
};
// Main game loop
game.update = function () {
	// Track if hero is moving this frame
	var wasMoving = hero.isWalking;
	var isMovingThisFrame = false;
	// Handle continuous movement based on button states
	if (movementState.left) {
		hero.move('left');
		isMovingThisFrame = true;
	}
	if (movementState.right) {
		hero.move('right');
		isMovingThisFrame = true;
	}
	if (movementState.up) {
		hero.move('up');
		isMovingThisFrame = true;
	}
	if (movementState.down) {
		hero.move('down');
		isMovingThisFrame = true;
	}
	// Stop walking animation if no movement this frame
	if (wasMoving && !isMovingThisFrame) {
		hero.stopWalkAnimation();
	}
	// Update camera position smoothly
	camera.x += (camera.targetX - camera.x) * camera.smoothing;
	camera.y += (camera.targetY - camera.y) * camera.smoothing;
	// Apply camera position to game
	game.x = -camera.x;
	game.y = -camera.y;
	// Check for hero-enemy collisions and apply push-back using spatial partitioning
	var nearbyEnemies = spatialGrid.getNearbyObjects(hero.x, hero.y, 150);
	for (var i = 0; i < nearbyEnemies.length; i++) {
		var enemy = nearbyEnemies[i];
		var dx = hero.x - enemy.x;
		var dy = hero.y - enemy.y;
		var distance = Math.sqrt(dx * dx + dy * dy);
		// If too close, push hero away from enemy
		if (distance < 100 && distance > 0) {
			var pushForce = (100 - distance) * 0.3;
			var pushX = dx / distance * pushForce;
			var pushY = dy / distance * pushForce;
			// Apply push with bounds checking
			var newHeroX = hero.x + pushX;
			var newHeroY = hero.y + pushY;
			// Keep hero within level bounds
			if (newHeroX > 100 && newHeroX < currentLevelData.width - 100) {
				hero.x = newHeroX;
			}
			if (newHeroY > 1600 && newHeroY < 2400) {
				hero.y = newHeroY;
			}
			// Update camera target when hero is pushed
			camera.targetX = hero.x - 1024;
			camera.targetY = hero.y - 1366;
			camera.targetX = Math.max(0, Math.min(camera.targetX, currentLevelData.width - 2048));
			camera.targetY = Math.max(0, Math.min(camera.targetY, currentLevelData.height - 2732));
		}
	}
	// Clear and rebuild spatial grid
	spatialGrid.clear();
	for (var i = 0; i < enemies.length; i++) {
		spatialGrid.addObject(enemies[i], enemies[i].x, enemies[i].y);
	}
	// Update all game objects with culling
	for (var i = enemies.length - 1; i >= 0; i--) {
		var enemy = enemies[i];
		if (isVisible(enemy, 300)) {
			// Only update visible enemies + buffer
			enemy.update();
		}
	}
	for (var i = coins.length - 1; i >= 0; i--) {
		if (isVisible(coins[i], 100)) {
			coins[i].update();
		}
	}
	for (var i = powerups.length - 1; i >= 0; i--) {
		if (isVisible(powerups[i], 100)) {
			powerups[i].update();
		}
	}
	for (var i = healthPotions.length - 1; i >= 0; i--) {
		if (isVisible(healthPotions[i], 100)) {
			healthPotions[i].update();
		}
	}
	for (var i = knives.length - 1; i >= 0; i--) {
		knives[i].update(); // Always update knives as they move fast
	}
	for (var i = bloodParticles.length - 1; i >= 0; i--) {
		if (isVisible(bloodParticles[i], 50)) {
			bloodParticles[i].update();
		}
	}
	for (var i = bossProjectiles.length - 1; i >= 0; i--) {
		bossProjectiles[i].update(); // Always update projectiles as they move fast
	}
	// Clean up destroyed route effects
	for (var i = routeEffects.length - 1; i >= 0; i--) {
		var routeEffect = routeEffects[i];
		if (!routeEffect.parent) {
			routeEffects.splice(i, 1);
		}
	}
	// Update enemy warnings
	updateEnemyWarnings();
	for (var i = 0; i < enemyWarnings.length; i++) {
		enemyWarnings[i].update();
	}
	// Check for dungeon completion
	if (!dungeonComplete && enemies.length === 0) {
		dungeonComplete = true;
		currentDungeon++;
		if (currentDungeon <= levels.length) {
			// Start next dungeon after delay
			LK.setTimeout(function () {
				initializeLevel();
			}, 2000);
		} else {
			// All dungeons completed
			LK.showYouWin();
		}
	}
};