User prompt
remove the old route when new created
User prompt
When the knife button is pressed, draw a route that shows the shortest distance between the enemy and the player, and display the effect where that route meets the player's asset. the knife meets the enemy in the middle of asset. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
add a knife throw button. enemies take damage when knife hits them. knives are limited to 5 for each level ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
When the knife button is pressed, draw a route that shows the shortest distance between the enemy and the player, and display the effect where that route meets the player's asset. remove the same thing with attack button attack button only creates punch effect not any route effect ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
add a knife throw button and do this when new button is pressed. also enemies take damage when knife hits them. knives are limited to 5 for each level ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
When the attack button is pressed, draw a route that shows the shortest distance between the enemy and the player, and display the effect where that route meets the player's asset. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
move the movement keys away from each other and prevent them from intersecting with each other
User prompt
Show a warning with an arrow on the side where the enemy is. As you move forward, the warning will change from transparent to visible and when the enemy becomes visible on the screen, the warning will disappear. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
add collision to player and mobs. they collide and They don't interfere with each other.
User prompt
make all buttons bigger and add an asset for each of them
User prompt
It should move when the movement keys are hold, not when clicked.
User prompt
add buttons for all directions to move the player, add a attack button, increase attacks range
User prompt
game is not wave based. game is level based with each level contains different type and number of enemies. you move the player and the playable map is not limited with the visble screen camera follows the char as the char moves. enemies roam randomly and starts to attack the pkayer once they sees it
Code edit (1 edits merged)
Please save this source code
User prompt
Pixel Hero Adventure
Initial prompt
a pixel game like dan the man
/**** 
* Plugins
****/ 
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/**** 
* Classes
****/ 
var ArcherProjectile = Container.expand(function () {
	var self = Container.call(this);
	var projectileGraphics = self.attachAsset('bossProjectile', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	projectileGraphics.tint = 0x8844ff; // Purple arrows
	projectileGraphics.scaleX = 0.8;
	projectileGraphics.scaleY = 0.8;
	self.velocityX = 0;
	self.velocityY = 0;
	self.lifetime = 240; // 4 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 < 60) {
			hero.takeDamage();
			self.destroy();
			// Remove from projectiles 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 projectiles array
			for (var i = bossProjectiles.length - 1; i >= 0; i--) {
				if (bossProjectiles[i] === self) {
					bossProjectiles.splice(i, 1);
					break;
				}
			}
		}
	};
	return self;
});
// Base Boss class with common functionality
var BaseBoss = Container.expand(function () {
	var self = Container.call(this);
	self.health = 50;
	self.maxHealth = 50;
	self.speed = 3;
	self.lastX = 0;
	self.lastY = 0;
	self.state = 'IDLE'; // AI states: IDLE, PURSUING, ATTACKING, STUNNED, ENRAGED
	self.stateTimer = 0;
	self.attackCooldown = 0;
	self.isInvulnerable = false;
	self.bossType = 'base';
	self.phase = 1;
	self.predictedHeroX = 0;
	self.predictedHeroY = 0;
	// Predictive targeting system
	self.updatePrediction = function () {
		var heroVelocityX = hero.x - hero.lastX;
		var heroVelocityY = hero.y - hero.lastY;
		var predictionFrames = 30; // Predict 0.5 seconds ahead
		self.predictedHeroX = hero.x + heroVelocityX * predictionFrames;
		self.predictedHeroY = hero.y + heroVelocityY * predictionFrames;
	};
	// State machine update
	self.updateState = function () {
		var distanceToHero = Math.sqrt(Math.pow(self.x - hero.x, 2) + Math.pow(self.y - hero.y, 2));
		self.stateTimer++;
		switch (self.state) {
			case 'IDLE':
				if (distanceToHero < 600) {
					self.setState('PURSUING');
				}
				break;
			case 'PURSUING':
				if (distanceToHero < 200) {
					self.setState('ATTACKING');
				} else if (distanceToHero > 800) {
					self.setState('IDLE');
				}
				break;
			case 'ATTACKING':
				if (distanceToHero > 300) {
					self.setState('PURSUING');
				}
				break;
			case 'STUNNED':
				if (self.stateTimer > 120) {
					// 2 seconds
					self.setState('ENRAGED');
				}
				break;
			case 'ENRAGED':
				if (self.health > self.maxHealth * 0.5) {
					self.setState('PURSUING');
				}
				break;
		}
	};
	self.setState = function (newState) {
		self.state = newState;
		self.stateTimer = 0;
		self.onStateChange(newState);
	};
	// Override for BossType1 specific state changes
	self.onStateChange = function (newState) {
		var graphics = self.getChildAt(0);
		if (newState === 'ENRAGED') {
			graphics.tint = 0xff4444;
			self.speed *= 2.0; // More aggressive speed boost
			// Add particle effect for enrage
			for (var i = 0; i < 8; i++) {
				var particle = game.addChild(getFromPool('bloodParticle'));
				particle.reset(self.x + (Math.random() - 0.5) * 100, self.y - 150 + (Math.random() - 0.5) * 100);
				var particleGraphics = particle.getChildAt(0);
				particleGraphics.tint = 0xff4444;
				bloodParticles.push(particle);
			}
		} else if (newState === 'STUNNED') {
			graphics.tint = 0x888888;
			self.speed *= 0.3; // More pronounced slowdown
			// Vulnerability window - takes extra damage when stunned
			self.isInvulnerable = false;
		} else if (newState === 'ATTACKING') {
			graphics.tint = 0xffaa00; // Orange tint when attacking
		} else {
			graphics.tint = 0xffffff;
		}
	};
	self.takeDamage = function (fromKnife) {
		if (self.isInvulnerable) return;
		var damage = 1;
		var isCritical = Math.random() < 0.2; // 20% critical chance on bosses
		var isVulnerable = self.state === 'STUNNED';
		if (isVulnerable) {
			damage *= 2; // Double damage when stunned
		}
		if (isCritical) {
			damage += 1; // Extra damage for critical
		}
		self.health -= damage;
		// Create damage number
		var damageNumber = game.addChild(getFromPool('damageNumber'));
		damageNumber.reset(self.x + (Math.random() - 0.5) * 60, self.y - 150, damage, isCritical || isVulnerable);
		damageNumbers.push(damageNumber);
		// Screen flash for critical hits on bosses
		if (isCritical) {
			createScreenFlash(0xFFD700, 400, 0.2); // Gold flash for critical
		}
		LK.effects.flashObject(self, isCritical ? 0xFFD700 : 0xffffff, isCritical ? 600 : 200);
		// Create enhanced blood particles with variety
		for (var p = 0; p < 10; p++) {
			var bloodParticle = game.addChild(getFromPool('bloodParticle'));
			var particleType = Math.random() < 0.3 ? 'large' : Math.random() < 0.5 ? 'small' : 'normal';
			bloodParticle.reset(self.x + (Math.random() - 0.5) * 60, self.y - 100 + (Math.random() - 0.5) * 60, particleType);
			bloodParticles.push(bloodParticle);
		}
		// Add spray particles for dramatic effect
		for (var p = 0; p < 8; p++) {
			var sprayParticle = game.addChild(getFromPool('bloodParticle'));
			sprayParticle.reset(self.x + (Math.random() - 0.5) * 40, self.y - 120 + (Math.random() - 0.5) * 40, 'spray');
			bloodParticles.push(sprayParticle);
		}
		// State transitions based on damage
		if (self.health <= self.maxHealth * 0.25 && self.state !== 'ENRAGED') {
			self.setState('ENRAGED');
		} else if (fromKnife && Math.random() < 0.3) {
			self.setState('STUNNED');
		}
		if (self.health <= 0) {
			self.die();
		}
	};
	self.die = function () {
		LK.effects.flashScreen(0xffffff, 2000);
		// Determine coin reward based on boss type
		var coinReward = 50; // Default boss reward
		if (self.bossType === 'melee') {
			coinReward = 50;
		} else if (self.bossType === 'summoner') {
			coinReward = 75;
		} else if (self.bossType === 'environmental') {
			coinReward = 100;
		}
		// Automatically give coins to upgrade tree
		upgradeTree.addCoins(coinReward);
		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);
		}
		LK.setScore(LK.getScore() + 1000);
		hero.addCombo();
		for (var i = enemies.length - 1; i >= 0; i--) {
			if (enemies[i] === self) {
				enemies.splice(i, 1);
				break;
			}
		}
		self.destroy();
		updateScoreDisplay();
		updateEnemiesLeftDisplay();
	};
	return self;
});
// BossType3: Environmental Manipulator
var BossType3 = BaseBoss.expand(function () {
	var self = BaseBoss.call(this);
	var bossGraphics = self.attachAsset('boss', {
		anchorX: 0.5,
		anchorY: 1.0
	});
	bossGraphics.tint = 0x00ff88; // Green for environmental
	self.bossType = 'environmental';
	self.hazardCooldown = 0;
	self.wallCooldown = 0;
	self.earthquakeCooldown = 0;
	self.activeHazards = [];
	self.isChanneling = false;
	self.update = function () {
		self.updatePrediction();
		self.updateState();
		var distanceToHero = Math.sqrt(Math.pow(self.x - hero.x, 2) + Math.pow(self.y - hero.y, 2));
		// Phase transitions
		if (self.health > self.maxHealth * 0.66) {
			self.phase = 1;
		} else if (self.health > self.maxHealth * 0.33) {
			self.phase = 2;
		} else {
			self.phase = 3;
		}
		// Environmental boss positioning based on state
		if (self.state === 'PURSUING' || self.state === 'ATTACKING' || self.state === 'ENRAGED') {
			// Move to strategic positions based on predicted hero movement
			var strategicX = self.state === 'ENRAGED' ? currentLevelData.width / 2 + (self.predictedHeroX > currentLevelData.width / 2 ? -300 : 300) : currentLevelData.width / 2;
			var strategicY = 2000;
			var dx = strategicX - self.x;
			var dy = strategicY - self.y;
			var distance = Math.sqrt(dx * dx + dy * dy);
			if (distance > 50) {
				self.lastX = self.x;
				self.lastY = self.y;
				var moveSpeed = self.state === 'ENRAGED' ? self.speed : self.speed * 0.5;
				self.x += dx / distance * moveSpeed;
				self.y += dy / distance * moveSpeed;
			}
		}
		// Enhanced environmental attacks with state-based timing
		if (self.state === 'ATTACKING' && self.hazardCooldown <= 0) {
			self.showHazardWarning();
		}
		if (self.phase >= 2 && self.state === 'ATTACKING' && self.wallCooldown <= 0) {
			self.showWallWarning();
		}
		if (self.phase === 3 && self.state === 'ENRAGED' && self.earthquakeCooldown <= 0) {
			self.showEarthquakeWarning();
		}
		// Cooldown updates
		if (self.hazardCooldown > 0) self.hazardCooldown--;
		if (self.wallCooldown > 0) self.wallCooldown--;
		if (self.earthquakeCooldown > 0) self.earthquakeCooldown--;
	};
	self.createHazard = function () {
		self.isChanneling = true;
		tween(bossGraphics, {
			tint: 0xff4400
		}, {
			duration: 300
		});
		// Create fire hazard near predicted hero position
		var hazard = game.addChild(LK.getAsset('hitEffect', {
			anchorX: 0.5,
			anchorY: 0.5,
			alpha: 0
		}));
		hazard.x = self.predictedHeroX + (Math.random() - 0.5) * 100;
		hazard.y = self.predictedHeroY + (Math.random() - 0.5) * 100;
		// Warning phase
		tween(hazard, {
			alpha: 0.5,
			scaleX: 2,
			scaleY: 2
		}, {
			duration: 1000
		});
		// Damage phase
		LK.setTimeout(function () {
			tween(hazard, {
				alpha: 1,
				scaleX: 3,
				scaleY: 3,
				tint: 0xff0000
			}, {
				duration: 500,
				onFinish: function onFinish() {
					// Check if hero is in hazard area
					var dx = hero.x - hazard.x;
					var dy = hero.y - hazard.y;
					var distance = Math.sqrt(dx * dx + dy * dy);
					if (distance < 100) {
						hero.takeDamage();
					}
					// Remove hazard
					hazard.destroy();
					for (var i = self.activeHazards.length - 1; i >= 0; i--) {
						if (self.activeHazards[i] === hazard) {
							self.activeHazards.splice(i, 1);
							break;
						}
					}
				}
			});
		}, 1000);
		self.activeHazards.push(hazard);
		tween(bossGraphics, {
			tint: 0x00ff88
		}, {
			duration: 300
		});
		self.isChanneling = false;
		self.hazardCooldown = self.phase === 3 ? 120 : 180;
	};
	self.createWalls = function () {
		// Create temporary walls that block movement
		for (var i = 0; i < 3; i++) {
			var wall = game.addChild(LK.getAsset('backgroundTile', {
				anchorX: 0.5,
				anchorY: 0.5,
				alpha: 0,
				tint: 0x666666
			}));
			wall.x = 300 + i * 400;
			wall.y = 1800 + Math.random() * 400;
			tween(wall, {
				alpha: 0.8,
				scaleY: 3
			}, {
				duration: 500
			});
			// Remove walls after duration
			LK.setTimeout(function () {
				tween(wall, {
					alpha: 0,
					scaleY: 0.1
				}, {
					duration: 500,
					onFinish: function onFinish() {
						wall.destroy();
					}
				});
			}, 5000);
		}
		self.wallCooldown = 420; // 7 seconds
	};
	self.showHazardWarning = function () {
		// Create multiple warning areas using predictive targeting
		var warningAreas = [{
			x: self.predictedHeroX,
			y: self.predictedHeroY
		}, {
			x: hero.x,
			y: hero.y
		},
		// Current position as backup
		{
			x: self.predictedHeroX + (Math.random() - 0.5) * 200,
			y: self.predictedHeroY + (Math.random() - 0.5) * 200
		}];
		for (var i = 0; i < warningAreas.length; i++) {
			var warning = game.addChild(LK.getAsset('hitEffect', {
				anchorX: 0.5,
				anchorY: 0.5,
				alpha: 0,
				tint: 0xff4400
			}));
			warning.x = warningAreas[i].x;
			warning.y = warningAreas[i].y;
			tween(warning, {
				alpha: 0.6,
				scaleX: 2.5,
				scaleY: 2.5
			}, {
				duration: 1500,
				onFinish: function onFinish() {
					warning.destroy();
				}
			});
		}
		// Boss visual warning
		tween(bossGraphics, {
			tint: 0xff4400,
			scaleX: 1.2,
			scaleY: 1.2
		}, {
			duration: 500,
			onFinish: function onFinish() {
				self.createHazard();
				tween(bossGraphics, {
					tint: 0x00ff88,
					scaleX: 1,
					scaleY: 1
				}, {
					duration: 300
				});
			}
		});
	};
	self.showWallWarning = function () {
		// Show where walls will appear
		var wallPositions = [{
			x: 300,
			y: 1800
		}, {
			x: 700,
			y: 2000
		}, {
			x: 1100,
			y: 1900
		}];
		for (var i = 0; i < wallPositions.length; i++) {
			var warning = game.addChild(LK.getAsset('backgroundTile', {
				anchorX: 0.5,
				anchorY: 0.5,
				alpha: 0,
				tint: 0xffaa00,
				scaleY: 0.1
			}));
			warning.x = wallPositions[i].x;
			warning.y = wallPositions[i].y;
			tween(warning, {
				alpha: 0.7,
				scaleY: 2
			}, {
				duration: 1000,
				onFinish: function onFinish() {
					warning.destroy();
				}
			});
		}
		LK.setTimeout(function () {
			self.createWalls();
		}, 1000);
	};
	self.showEarthquakeWarning = function () {
		// Screen warning for earthquake
		LK.effects.flashScreen(0x8b4513, 1500);
		// Boss charges up
		tween(bossGraphics, {
			tint: 0x8b4513,
			scaleX: 1.5,
			scaleY: 1.5
		}, {
			duration: 1500,
			onFinish: function onFinish() {
				self.earthquake();
			}
		});
	};
	self.earthquake = function () {
		self.isChanneling = true;
		// Create vulnerability window - boss can't move during earthquake
		self.speed = 0;
		LK.effects.flashScreen(0x8b4513, 2000);
		// Intense screen shake for earthquake
		triggerScreenShake(25, 1500, tween.easeInOut);
		// Screen shake effect simulation through rapid position changes
		var originalX = bossGraphics.x;
		var originalY = bossGraphics.y;
		for (var i = 0; i < 20; i++) {
			LK.setTimeout(function () {
				bossGraphics.x = originalX + (Math.random() - 0.5) * 10;
				bossGraphics.y = originalY + (Math.random() - 0.5) * 10;
			}, i * 50);
		}
		LK.setTimeout(function () {
			bossGraphics.x = originalX;
			bossGraphics.y = originalY;
			self.isChanneling = false;
			// Restore speed and brief stun
			self.speed = 3;
			self.setState('STUNNED');
			// Reset visual
			tween(bossGraphics, {
				tint: 0x00ff88,
				scaleX: 1,
				scaleY: 1
			}, {
				duration: 500
			});
		}, 1000);
		// Damage hero if on ground
		LK.setTimeout(function () {
			if (hero.y > 2200) {
				// Near ground level
				hero.takeDamage();
			}
		}, 1000);
		self.earthquakeCooldown = 600; // 10 seconds
	};
	return self;
});
// Legacy Boss class for backward compatibility (delegates to BossType1)
// BossType2: Summoner Boss
var BossType2 = BaseBoss.expand(function () {
	var self = BaseBoss.call(this);
	var bossGraphics = self.attachAsset('boss', {
		anchorX: 0.5,
		anchorY: 1.0
	});
	bossGraphics.tint = 0x9b59b6; // Purple for summoner
	self.bossType = 'summoner';
	self.summonCooldown = 0;
	self.minionCount = 0;
	self.maxMinions = 4;
	self.teleportCooldown = 0;
	self.shieldActive = false;
	self.shieldHealth = 0;
	self.update = function () {
		self.updatePrediction();
		self.updateState();
		var distanceToHero = Math.sqrt(Math.pow(self.x - hero.x, 2) + Math.pow(self.y - hero.y, 2));
		// Phase transitions affect minion count
		if (self.health > self.maxHealth * 0.66) {
			self.phase = 1;
			self.maxMinions = 2;
		} else if (self.health > self.maxHealth * 0.33) {
			self.phase = 2;
			self.maxMinions = 3;
		} else {
			self.phase = 3;
			self.maxMinions = 4;
		}
		// Enhanced movement based on state with predictive positioning
		if (self.state === 'PURSUING' || self.state === 'ATTACKING' || self.state === 'ENRAGED') {
			var optimalDistance = self.state === 'ENRAGED' ? 300 : 450;
			if (distanceToHero < optimalDistance) {
				// Move away from hero, but anticipate their movement
				var escapeX = self.x - self.predictedHeroX;
				var escapeY = self.y - self.predictedHeroY;
				var distance = Math.sqrt(escapeX * escapeX + escapeY * escapeY);
				if (distance > 0) {
					self.lastX = self.x;
					self.lastY = self.y;
					var moveSpeed = self.state === 'ENRAGED' ? self.speed * 1.5 : self.speed;
					self.x += escapeX / distance * moveSpeed;
					self.y += escapeY / distance * moveSpeed;
					bossGraphics.scaleX = escapeX > 0 ? -1 : 1; // Face away from hero
				}
			}
		}
		// Enhanced summoning with warnings
		if (self.state === 'ATTACKING' && self.summonCooldown <= 0 && self.minionCount < self.maxMinions) {
			self.showSummonWarning();
		}
		// Predictive teleport when threatened
		if (self.phase >= 2 && self.teleportCooldown <= 0 && distanceToHero < 250) {
			self.showTeleportWarning();
		}
		// Shield ability in phase 3
		if (self.phase === 3 && !self.shieldActive && self.health <= self.maxHealth * 0.25) {
			self.activateShield();
		}
		// Cooldown updates
		if (self.summonCooldown > 0) self.summonCooldown--;
		if (self.teleportCooldown > 0) self.teleportCooldown--;
	};
	self.summonMinion = function () {
		// Create a specialized minion near the boss
		var minion = game.addChild(new Minion());
		minion.x = self.x + (Math.random() - 0.5) * 200;
		minion.y = self.y + (Math.random() - 0.5) * 100;
		minion.summoner = self; // Link minion to summoner
		// Keep within bounds
		minion.x = Math.max(100, Math.min(minion.x, currentLevelData.width - 100));
		minion.y = Math.max(1600, Math.min(minion.y, 2400));
		// Visual summoning effect
		tween(bossGraphics, {
			tint: 0xff00ff
		}, {
			duration: 200
		});
		tween(bossGraphics, {
			tint: 0x9b59b6
		}, {
			duration: 200
		});
		LK.effects.flashObject(minion, 0x9b59b6, 500);
		enemies.push(minion);
		self.minionCount++;
		self.summonCooldown = self.phase === 3 ? 180 : 240; // Faster summoning in phase 3
		updateEnemiesLeftDisplay();
	};
	self.teleport = function () {
		// Teleport to a safe distance from hero
		var teleportDistance = 500;
		var angle = Math.random() * Math.PI * 2;
		var newX = hero.x + Math.cos(angle) * teleportDistance;
		var newY = hero.y + Math.sin(angle) * teleportDistance;
		// Keep within bounds
		newX = Math.max(200, Math.min(newX, currentLevelData.width - 200));
		newY = Math.max(1700, Math.min(newY, 2300));
		// Visual teleport effect
		tween(bossGraphics, {
			alpha: 0,
			scaleX: 0.1,
			scaleY: 0.1
		}, {
			duration: 300,
			onFinish: function onFinish() {
				self.x = newX;
				self.y = newY;
				tween(bossGraphics, {
					alpha: 1,
					scaleX: 1,
					scaleY: 1
				}, {
					duration: 300
				});
				LK.effects.flashObject(self, 0x9b59b6, 500);
			}
		});
		self.teleportCooldown = 300; // 5 seconds
	};
	self.activateShield = function () {
		self.shieldActive = true;
		self.shieldHealth = 10;
		self.isInvulnerable = true;
		// Visual shield effect
		tween(bossGraphics, {
			tint: 0x00ffff
		}, {
			duration: 200
		});
		LK.effects.flashObject(self, 0x00ffff, 1000);
		// Shield lasts until destroyed or timeout
		LK.setTimeout(function () {
			self.deactivateShield();
		}, 10000); // 10 seconds max
	};
	self.showSummonWarning = function () {
		// Create warning circles at summon locations
		var summonLocations = [{
			x: self.x + 150,
			y: self.y
		}, {
			x: self.x - 150,
			y: self.y
		}, {
			x: self.x,
			y: self.y + 100
		}];
		for (var i = 0; i < summonLocations.length; i++) {
			var warning = game.addChild(LK.getAsset('hitEffect', {
				anchorX: 0.5,
				anchorY: 0.5,
				alpha: 0,
				tint: 0x9b59b6
			}));
			warning.x = summonLocations[i].x;
			warning.y = summonLocations[i].y;
			tween(warning, {
				alpha: 0.7,
				scaleX: 1.5,
				scaleY: 1.5
			}, {
				duration: 1000,
				onFinish: function onFinish() {
					warning.destroy();
				}
			});
		}
		// Delay actual summon
		LK.setTimeout(function () {
			if (self.minionCount < self.maxMinions) {
				self.summonMinion();
			}
		}, 1000);
	};
	self.showTeleportWarning = function () {
		// Flash before teleporting
		tween(bossGraphics, {
			alpha: 0.3,
			tint: 0xff00ff
		}, {
			duration: 300,
			onFinish: function onFinish() {
				self.teleport();
			}
		});
	};
	self.deactivateShield = function () {
		self.shieldActive = false;
		self.isInvulnerable = false;
		// Vulnerability window after shield breaks
		self.setState('STUNNED');
		tween(bossGraphics, {
			tint: 0x9b59b6
		}, {
			duration: 500
		});
	};
	// Override takeDamage to handle shield
	var originalTakeDamage = self.takeDamage;
	self.takeDamage = function (fromKnife) {
		if (self.shieldActive) {
			self.shieldHealth--;
			LK.effects.flashObject(self, 0x00ffff, 100);
			if (self.shieldHealth <= 0) {
				self.deactivateShield();
			}
			return;
		}
		// Reduce minion count when boss takes damage
		if (self.minionCount > 0) {
			self.minionCount--;
		}
		originalTakeDamage.call(self, fromKnife);
	};
	return self;
});
// BossType1: Melee/Projectile Boss (original boss enhanced)
var BossType1 = BaseBoss.expand(function () {
	var self = BaseBoss.call(this);
	var bossGraphics = self.attachAsset('boss', {
		anchorX: 0.5,
		anchorY: 1.0
	});
	self.bossType = 'melee';
	self.projectileAttackCooldown = 0;
	self.chargeAttackCooldown = 0;
	self.isCharging = false;
	self.chargingTarget = null;
	self.spinAttackCooldown = 0;
	self.isSpinning = false;
	self.update = function () {
		self.updatePrediction();
		self.updateState();
		var distanceToHero = Math.sqrt(Math.pow(self.x - hero.x, 2) + Math.pow(self.y - hero.y, 2));
		// Phase transitions
		if (self.health > self.maxHealth * 0.66) {
			self.phase = 1;
		} else if (self.health > self.maxHealth * 0.33) {
			self.phase = 2;
		} else {
			self.phase = 3;
		}
		// Movement based on state
		if (self.state === 'PURSUING' || self.state === 'ENRAGED') {
			var targetX = self.state === 'ENRAGED' ? self.predictedHeroX : hero.x;
			var targetY = self.state === 'ENRAGED' ? self.predictedHeroY : hero.y;
			var dx = targetX - self.x;
			var dy = targetY - self.y;
			var distance = Math.sqrt(dx * dx + dy * dy);
			if (distance > 0) {
				self.lastX = self.x;
				self.lastY = self.y;
				var moveSpeed = self.speed;
				// Enraged state moves faster toward predicted position
				if (self.state === 'ENRAGED') {
					moveSpeed *= 1.8;
				}
				self.x += dx / distance * moveSpeed;
				self.y += dy / distance * moveSpeed;
				bossGraphics.scaleX = dx > 0 ? 1 : -1;
			}
		}
		// Attacks based on state with visual warnings
		if (self.state === 'ATTACKING' && self.attackCooldown <= 0) {
			if (distanceToHero < 150) {
				self.showMeleeWarning();
			} else if (self.phase >= 2 && self.projectileAttackCooldown <= 0) {
				self.showProjectileWarning();
			}
		}
		// Special attacks with warnings
		if (self.phase >= 2 && self.chargeAttackCooldown <= 0 && distanceToHero > 300 && !self.isCharging) {
			self.showChargeWarning();
		}
		if (self.phase === 3 && self.spinAttackCooldown <= 0 && distanceToHero < 250 && !self.isSpinning) {
			self.showSpinWarning();
		}
		// Cooldown updates
		if (self.attackCooldown > 0) self.attackCooldown--;
		if (self.projectileAttackCooldown > 0) self.projectileAttackCooldown--;
		if (self.chargeAttackCooldown > 0) self.chargeAttackCooldown--;
		if (self.spinAttackCooldown > 0) self.spinAttackCooldown--;
	};
	self.meleeAttack = function () {
		hero.takeDamage();
		self.attackCooldown = 90;
		LK.effects.flashObject(self, 0xffffff, 200);
		// Screen shake on boss melee attack
		triggerScreenShake(15, 300, tween.easeOut);
	};
	self.fireProjectile = function () {
		tween(bossGraphics, {
			tint: 0x8800ff
		}, {
			duration: 200
		});
		tween(bossGraphics, {
			tint: 0xffffff
		}, {
			duration: 200
		});
		// Screen shake on boss projectile attack
		triggerScreenShake(8, 200, tween.easeOut);
		var projectile = game.addChild(new BossProjectile());
		projectile.x = self.x;
		projectile.y = self.y - 200;
		var dx = self.predictedHeroX - self.x;
		var dy = self.predictedHeroY - self.y;
		var distance = Math.sqrt(dx * dx + dy * dy);
		if (distance > 0) {
			var speed = self.phase === 3 ? 12 : 8;
			projectile.velocityX = dx / distance * speed;
			projectile.velocityY = dy / distance * speed;
		}
		bossProjectiles.push(projectile);
		self.projectileAttackCooldown = self.phase === 3 ? 60 : 120;
	};
	self.chargeAttack = function () {
		self.isCharging = true;
		self.chargingTarget = {
			x: self.predictedHeroX,
			y: self.predictedHeroY
		};
		tween(bossGraphics, {
			tint: 0xff4444,
			scaleX: 1.2,
			scaleY: 1.2
		}, {
			duration: 800,
			easing: tween.easeIn
		});
		LK.setTimeout(function () {
			if (!self.chargingTarget) return;
			LK.effects.flashObject(self, 0xffffff, 300);
			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) {
				var chargeDistance = 400;
				var targetX = self.x + dx / distance * chargeDistance;
				var targetY = self.y + dy / distance * chargeDistance;
				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() {
						tween(bossGraphics, {
							tint: 0xffffff,
							scaleX: 1,
							scaleY: 1
						}, {
							duration: 500
						});
						self.isCharging = false;
						self.chargingTarget = null;
					}
				});
				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();
						// Screen shake on successful charge attack hit
						triggerScreenShake(20, 400, tween.easeOut);
					}
				}, 150);
			}
		}, 800);
		self.chargeAttackCooldown = self.phase === 3 ? 240 : 360;
	};
	self.showMeleeWarning = function () {
		// Flash red warning for 0.5 seconds before attacking
		tween(bossGraphics, {
			tint: 0xff0000,
			scaleX: bossGraphics.scaleX * 1.3,
			scaleY: 1.3
		}, {
			duration: 500,
			onFinish: function onFinish() {
				self.meleeAttack();
				tween(bossGraphics, {
					tint: 0xffffff,
					scaleX: bossGraphics.scaleX > 0 ? 1 : -1,
					scaleY: 1
				}, {
					duration: 200
				});
			}
		});
	};
	self.showProjectileWarning = function () {
		// Create warning indicator at predicted position
		var warningIndicator = game.addChild(LK.getAsset('hitEffect', {
			anchorX: 0.5,
			anchorY: 0.5,
			alpha: 0,
			tint: 0xff4444
		}));
		warningIndicator.x = self.predictedHeroX;
		warningIndicator.y = self.predictedHeroY;
		tween(warningIndicator, {
			alpha: 0.8,
			scaleX: 2,
			scaleY: 2
		}, {
			duration: 800,
			onFinish: function onFinish() {
				self.fireProjectile();
				warningIndicator.destroy();
			}
		});
	};
	self.showChargeWarning = function () {
		// Show charge direction warning
		var warningLine = game.addChild(LK.getAsset('slashEffect', {
			anchorX: 0,
			anchorY: 0.5,
			alpha: 0,
			tint: 0xff4444
		}));
		var dx = self.predictedHeroX - self.x;
		var dy = self.predictedHeroY - self.y;
		var angle = Math.atan2(dy, dx);
		warningLine.x = self.x;
		warningLine.y = self.y - 100;
		warningLine.rotation = angle;
		warningLine.scaleX = 8;
		warningLine.scaleY = 2;
		tween(warningLine, {
			alpha: 0.7
		}, {
			duration: 600,
			onFinish: function onFinish() {
				self.chargeAttack();
				warningLine.destroy();
			}
		});
	};
	self.showSpinWarning = function () {
		// Create expanding red circle warning
		var warningCircle = game.addChild(LK.getAsset('hitEffect', {
			anchorX: 0.5,
			anchorY: 0.5,
			alpha: 0,
			tint: 0xff0000
		}));
		warningCircle.x = self.x;
		warningCircle.y = self.y - 100;
		tween(warningCircle, {
			alpha: 0.6,
			scaleX: 4,
			scaleY: 4
		}, {
			duration: 1000,
			onFinish: function onFinish() {
				self.spinAttack();
				warningCircle.destroy();
			}
		});
	};
	self.spinAttack = function () {
		self.isSpinning = true;
		// Create vulnerability window - boss takes double damage during spin
		self.isInvulnerable = false;
		var originalTakeDamage = self.takeDamage;
		self.takeDamage = function (fromKnife) {
			// Double damage during spin attack
			originalTakeDamage.call(self, fromKnife);
			if (self.health > 0) {
				originalTakeDamage.call(self, fromKnife);
			}
		};
		tween(bossGraphics, {
			rotation: Math.PI * 4
		}, {
			duration: 1500,
			easing: tween.easeInOut,
			onFinish: function onFinish() {
				bossGraphics.rotation = 0;
				self.isSpinning = false;
				// Restore normal damage after spin
				self.takeDamage = originalTakeDamage;
				// Brief stunned state after spin
				self.setState('STUNNED');
			}
		});
		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
		});
		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);
					// Screen shake on spin attack hit
					triggerScreenShake(12, 250, tween.easeOut);
				}
			}, i * 300);
		}
		self.spinAttackCooldown = 420;
	};
	return self;
});
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.particleType = 'normal'; // normal, large, small, spray
	self.reset = function (startX, startY, type) {
		self.x = startX;
		self.y = startY;
		self.particleType = type || 'normal';
		// Different sizes, speeds, and lifetimes based on type
		if (self.particleType === 'large') {
			self.velocityX = (Math.random() - 0.5) * 6;
			self.velocityY = -Math.random() * 4 - 1;
			self.lifetime = 90; // Larger particles last longer
			particleGraphics.scaleX = 1.5;
			particleGraphics.scaleY = 1.5;
			particleGraphics.tint = 0x8b0000; // Dark red
		} else if (self.particleType === 'small') {
			self.velocityX = (Math.random() - 0.5) * 12; // Faster small particles
			self.velocityY = -Math.random() * 8 - 3;
			self.lifetime = 30; // Short-lived
			particleGraphics.scaleX = 0.5;
			particleGraphics.scaleY = 0.5;
			particleGraphics.tint = 0xff4444; // Bright red
		} else if (self.particleType === 'spray') {
			self.velocityX = (Math.random() - 0.5) * 16; // Very fast spray
			self.velocityY = -Math.random() * 10 - 1;
			self.lifetime = 45;
			particleGraphics.scaleX = 0.7;
			particleGraphics.scaleY = 0.7;
			particleGraphics.tint = 0xcc2222; // Medium red
		} else {
			// Normal particles
			self.velocityX = (Math.random() - 0.5) * 8;
			self.velocityY = -Math.random() * 6 - 2;
			self.lifetime = 60;
			particleGraphics.scaleX = 1;
			particleGraphics.scaleY = 1;
			particleGraphics.tint = 0x8b0000;
		}
		particleGraphics.alpha = 1;
		self.visible = true;
	};
	self.update = function () {
		self.x += self.velocityX;
		self.y += self.velocityY;
		self.velocityY += self.gravity;
		// Different fade patterns based on type
		var maxLifetime = 60;
		if (self.particleType === 'large') maxLifetime = 90;else if (self.particleType === 'small') maxLifetime = 30;else if (self.particleType === 'spray') maxLifetime = 45;
		// Fade out over time
		self.lifetime--;
		particleGraphics.alpha = self.lifetime / maxLifetime;
		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 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 DamageNumber = Container.expand(function () {
	var self = Container.call(this);
	var damageText = new Text2('', {
		size: 60,
		fill: 0xFFFFFF
	});
	damageText.anchor.set(0.5, 0.5);
	self.addChild(damageText);
	self.lifetime = 120; // 2 seconds at 60fps
	self.velocityY = -3; // Float upward
	self.fadeSpeed = 1 / 120; // Fade over lifetime
	self.reset = function (startX, startY, damage, isCritical) {
		self.x = startX;
		self.y = startY;
		self.lifetime = 120;
		self.velocityY = isCritical ? -4 : -3; // Faster rise for critical hits
		damageText.setText(damage.toString());
		damageText.alpha = 1;
		self.visible = true;
		// Different styling for critical hits
		if (isCritical) {
			damageText.fill = 0xFFD700; // Gold for critical
			damageText.size = 80; // Larger text
			tween(self, {
				scaleX: 1.3,
				scaleY: 1.3
			}, {
				duration: 200,
				easing: tween.easeOut
			});
		} else {
			damageText.fill = 0xFFFFFF; // White for normal
			damageText.size = 60;
			self.scaleX = 1;
			self.scaleY = 1;
		}
	};
	self.update = function () {
		self.y += self.velocityY;
		self.velocityY *= 0.98; // Slow down over time
		self.lifetime--;
		damageText.alpha = self.lifetime / 120; // Fade out
		if (self.lifetime <= 0) {
			// Remove from damageNumbers array
			for (var i = damageNumbers.length - 1; i >= 0; i--) {
				if (damageNumbers[i] === self) {
					damageNumbers.splice(i, 1);
					break;
				}
			}
			returnToPool(self, 'damageNumber');
		}
	};
	return self;
});
var DustCloud = Container.expand(function () {
	var self = Container.call(this);
	var dustGraphics = self.attachAsset('bloodParticle', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	dustGraphics.tint = 0x8b7355; // Brown dust color
	dustGraphics.scaleX = 0.8;
	dustGraphics.scaleY = 0.8;
	self.velocityX = 0;
	self.velocityY = 0;
	self.lifetime = 40; // Medium lifetime
	self.expandRate = 0.02; // How fast dust expands
	self.reset = function (startX, startY, direction) {
		self.x = startX;
		self.y = startY;
		// Dust moves opposite to movement direction
		var baseSpeed = 1 + Math.random() * 2;
		if (direction === 'left') {
			self.velocityX = baseSpeed;
		} else if (direction === 'right') {
			self.velocityX = -baseSpeed;
		} else if (direction === 'up') {
			self.velocityY = baseSpeed * 0.5;
		} else if (direction === 'down') {
			self.velocityY = -baseSpeed * 0.5;
		} else {
			// Random direction for general dust
			var angle = Math.random() * Math.PI * 2;
			self.velocityX = Math.cos(angle) * baseSpeed;
			self.velocityY = Math.sin(angle) * baseSpeed;
		}
		self.velocityY -= Math.random() * 1; // Slight upward drift
		dustGraphics.alpha = 0.6;
		dustGraphics.scaleX = 0.4 + Math.random() * 0.4;
		dustGraphics.scaleY = 0.4 + Math.random() * 0.4;
		self.visible = true;
	};
	self.update = function () {
		self.x += self.velocityX;
		self.y += self.velocityY;
		// Dust slows down and expands
		self.velocityX *= 0.98;
		self.velocityY *= 0.98;
		dustGraphics.scaleX += self.expandRate;
		dustGraphics.scaleY += self.expandRate;
		// Fade out over time
		self.lifetime--;
		dustGraphics.alpha = self.lifetime / 40 * 0.6;
		if (self.lifetime <= 0) {
			// Remove from dustClouds array
			for (var i = dustClouds.length - 1; i >= 0; i--) {
				if (dustClouds[i] === self) {
					dustClouds.splice(i, 1);
					break;
				}
			}
			returnToPool(self, 'dustCloud');
		}
	};
	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));
		// Initialize group AI properties if not already set
		if (!self.hasOwnProperty('isAssignedFlanker')) self.isAssignedFlanker = false;
		if (!self.hasOwnProperty('flankingTarget')) self.flankingTarget = null;
		if (!self.hasOwnProperty('packGroup')) self.packGroup = null;
		if (!self.hasOwnProperty('leaderBuff')) self.leaderBuff = null;
		if (!self.hasOwnProperty('communicationTimer')) self.communicationTimer = 0;
		// Update communication timer
		if (self.communicationTimer > 0) self.communicationTimer--;
		// Process group communications
		var communications = groupAI.getCommunications(self, 120, 400);
		for (var c = 0; c < communications.length; c++) {
			var comm = communications[c];
			if (comm.type === 'spotted_hero' && !self.alerted) {
				self.alerted = true;
				groupAI.broadcastAlert(self, 'spotted_hero', 1);
			} else if (comm.type === 'under_attack' && comm.priority >= 2) {
				self.alerted = true;
				// Move to assist ally under attack
				if (!self.isAssignedFlanker && Math.random() < 0.3) {
					self.assistTarget = {
						x: comm.x,
						y: comm.y
					};
				}
			} else if (comm.type === 'flanking_position' && !self.isAssignedFlanker && self.enemyType !== 'tank') {
				groupAI.assignFlankingPosition(self);
			}
		}
		// 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
		} else if (self.enemyType === 'leader') {
			sightRange = 700; // Leaders have good coordination sight
		}
		var canSeeHero = distanceToHero < sightRange;
		// Broadcast hero sighting
		if (canSeeHero && !self.alerted && self.communicationTimer <= 0) {
			groupAI.broadcastAlert(self, 'spotted_hero', 1);
			self.alerted = true;
			self.communicationTimer = 60; // 1 second cooldown
		}
		if (canSeeHero || self.alerted) {
			// Determine movement target based on role
			var targetX = hero.x;
			var targetY = hero.y;
			var moveSpeed = self.speed;
			// Apply leader buffs
			if (self.leaderBuff) {
				moveSpeed *= self.leaderBuff.speedMultiplier;
			}
			// Flanking behavior
			if (self.isAssignedFlanker && self.flankingTarget) {
				var flankDistance = Math.sqrt(Math.pow(self.x - self.flankingTarget.x, 2) + Math.pow(self.y - self.flankingTarget.y, 2));
				if (flankDistance > 50) {
					// Move to flanking position
					targetX = self.flankingTarget.x;
					targetY = self.flankingTarget.y;
				} else {
					// Reached flanking position, now attack hero
					targetX = hero.x;
					targetY = hero.y;
					moveSpeed *= 1.2; // Speed boost when in flanking position
				}
			}
			// Pack hunting behavior for fast enemies
			if (self.enemyType === 'fast' || self.enemyType === 'hunter') {
				var nearbyPackMembers = spatialGrid.getNearbyObjects(self.x, self.y, 200);
				var packMates = 0;
				for (var p = 0; p < nearbyPackMembers.length; p++) {
					if (nearbyPackMembers[p] !== self && (nearbyPackMembers[p].enemyType === 'fast' || nearbyPackMembers[p].enemyType === 'hunter')) {
						packMates++;
					}
				}
				if (packMates >= 1) {
					// Pack hunting behavior - try to herd hero toward stronger enemies
					var nearbyStrongEnemies = spatialGrid.getNearbyObjects(hero.x, hero.y, 600);
					var strongestEnemy = null;
					for (var s = 0; s < nearbyStrongEnemies.length; s++) {
						if (nearbyStrongEnemies[s].enemyType === 'tank' || nearbyStrongEnemies[s].enemyType === 'strong' || nearbyStrongEnemies[s].enemyType === 'leader') {
							strongestEnemy = nearbyStrongEnemies[s];
							break;
						}
					}
					if (strongestEnemy) {
						// Position to herd hero toward strongest enemy
						var herdX = hero.x + (strongestEnemy.x - hero.x) * 0.3;
						var herdY = hero.y + (strongestEnemy.y - hero.y) * 0.3;
						targetX = herdX;
						targetY = herdY;
						moveSpeed *= 1.4; // Faster when pack hunting
					}
				}
			}
			// Assist behavior
			if (self.assistTarget && !self.isAssignedFlanker) {
				var assistDistance = Math.sqrt(Math.pow(self.x - self.assistTarget.x, 2) + Math.pow(self.y - self.assistTarget.y, 2));
				if (assistDistance > 100) {
					targetX = self.assistTarget.x;
					targetY = self.assistTarget.y;
					moveSpeed *= 1.1; // Slight speed boost when assisting
				} else {
					self.assistTarget = null; // Clear assist target when reached
				}
			}
			// Calculate movement toward target
			var dx = targetX - self.x;
			var dy = targetY - self.y;
			var distance = Math.sqrt(dx * dx + dy * dy);
			if (distance > 0) {
				// 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
			var attackRange = 100;
			if (self.leaderBuff) {
				attackRange *= 1.1; // Slightly increased attack range with leader buff
			}
			if (distanceToHero < attackRange && self.attackCooldown <= 0) {
				var damage = 1;
				if (self.leaderBuff && self.leaderBuff.damageMultiplier) {
					damage = Math.floor(damage * self.leaderBuff.damageMultiplier);
				}
				hero.takeDamage();
				self.attackCooldown = 120; // 2 seconds at 60fps
			}
			// Glow effects based on state
			if (self.alerted && !self.glowOutline) {
				createGlowOutline(self, 0xff8844, 0.5); // Orange glow for alerted
			} else if (!self.alerted && self.glowOutline) {
				removeGlowOutline(self);
			}
			if (self.health <= 1 && (!self.glowOutline || self.glowOutline.tint !== 0xff0000)) {
				removeGlowOutline(self);
				createGlowOutline(self, 0xff0000, 0.8); // Red glow for low health
			} else if (self.isAssignedFlanker && (!self.glowOutline || self.glowOutline.tint !== 0x8844ff)) {
				removeGlowOutline(self);
				createGlowOutline(self, 0x8844ff, 0.4); // Blue glow for flankers
			}
		} 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) {
		var damage = 1;
		var isCritical = Math.random() < 0.15; // 15% critical chance
		if (isCritical) {
			damage = 2;
			self.health -= 2;
		} else {
			self.health--;
		}
		// Create damage number
		var damageNumber = game.addChild(getFromPool('damageNumber'));
		damageNumber.reset(self.x + (Math.random() - 0.5) * 40, self.y - 100, damage, isCritical);
		damageNumbers.push(damageNumber);
		LK.effects.flashObject(self, isCritical ? 0xFFD700 : 0xffffff, isCritical ? 400 : 200);
		// Add glow effect based on enemy state
		if (self.alerted && !self.glowOutline) {
			createGlowOutline(self, 0xff4444, 0.6); // Red glow for alerted
		} else if (self.health <= 1 && !self.glowOutline) {
			createGlowOutline(self, 0xffaa00, 0.8); // Orange glow for low health
		}
		// 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 enhanced blood particles with variety
		for (var p = 0; p < 8; p++) {
			var bloodParticle = game.addChild(getFromPool('bloodParticle'));
			var particleType = Math.random() < 0.2 ? 'large' : Math.random() < 0.6 ? 'small' : 'normal';
			bloodParticle.reset(self.x + (Math.random() - 0.5) * 40, self.y - 60 + (Math.random() - 0.5) * 40, particleType);
			bloodParticles.push(bloodParticle);
		}
		// Add impact sparks when hit by knife
		if (fromKnife) {
			for (var s = 0; s < 6; s++) {
				var spark = game.addChild(getFromPool('impactSpark'));
				var sparkType = Math.random() < 0.3 ? 'bright' : Math.random() < 0.5 ? 'orange' : 'normal';
				spark.reset(self.x + (Math.random() - 0.5) * 30, self.y - 70 + (Math.random() - 0.5) * 30, sparkType);
				impactSparks.push(spark);
			}
			self.alerted = true;
			self.alertedByKnife = true;
		}
		if (self.health <= 0) {
			self.die();
		}
	};
	self.die = function () {
		// Screen shake on enemy death (smaller intensity)
		triggerScreenShake(6, 150, tween.easeOut);
		// Determine coin reward based on enemy difficulty
		var coinReward = 1; // Default for basic enemies
		if (self.enemyType === 'basic') {
			coinReward = 1;
		} else if (self.enemyType === 'fast') {
			coinReward = 2;
		} else if (self.enemyType === 'scout') {
			coinReward = 3;
		} else if (self.enemyType === 'strong') {
			coinReward = 3;
		} else if (self.enemyType === 'archer') {
			coinReward = 4;
		} else if (self.enemyType === 'berserker') {
			coinReward = 4;
		} else if (self.enemyType === 'hunter') {
			coinReward = 5;
		} else if (self.enemyType === 'shield') {
			coinReward = 6;
		} else if (self.enemyType === 'assassin') {
			coinReward = 6;
		} else if (self.enemyType === 'tank') {
			coinReward = 8;
		} else if (self.enemyType === 'leader') {
			coinReward = 10;
		}
		// Automatically give coins to upgrade tree
		upgradeTree.addCoins(coinReward);
		// Drop visual 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 Shield = Enemy.expand(function () {
	var self = Enemy.call(this, 'shield');
	var shieldGraphics = self.attachAsset('enemyTank', {
		anchorX: 0.5,
		anchorY: 1.0
	});
	shieldGraphics.tint = 0x4488ff; // Blue tint for shields
	self.enemyType = 'shield';
	self.health = 6;
	self.speed = 1.0;
	self.shieldActive = true;
	self.protectionRadius = 200;
	self.blockCooldown = 0;
	// Override update to include shield behavior
	var originalUpdate = self.update;
	self.update = function () {
		originalUpdate.call(self);
		// Shield positioning: try to stay between hero and vulnerable allies
		var vulnerableAllies = [];
		var nearbyAllies = spatialGrid.getNearbyObjects(self.x, self.y, self.protectionRadius * 1.5);
		for (var i = 0; i < nearbyAllies.length; i++) {
			var ally = nearbyAllies[i];
			if (ally !== self && ally.enemyType && (ally.enemyType === 'archer' || ally.enemyType === 'scout' || ally.health <= 2)) {
				vulnerableAllies.push(ally);
			}
		}
		// If there are vulnerable allies, position between them and hero
		if (vulnerableAllies.length > 0) {
			var avgAllyX = 0;
			var avgAllyY = 0;
			for (var i = 0; i < vulnerableAllies.length; i++) {
				avgAllyX += vulnerableAllies[i].x;
				avgAllyY += vulnerableAllies[i].y;
			}
			avgAllyX /= vulnerableAllies.length;
			avgAllyY /= vulnerableAllies.length;
			// Position between average ally position and hero
			var protectX = avgAllyX + (hero.x - avgAllyX) * 0.3;
			var protectY = avgAllyY + (hero.y - avgAllyY) * 0.3;
			var dx = protectX - self.x;
			var dy = protectY - self.y;
			var distance = Math.sqrt(dx * dx + dy * dy);
			if (distance > 50) {
				// Move toward protection position
				var newX = self.x + dx / distance * self.speed * 0.8;
				var newY = self.y + dy / distance * self.speed * 0.8;
				// Check collision before moving
				var wouldCollide = false;
				var nearbyEnemies = spatialGrid.getNearbyObjects(newX, newY, 100);
				for (var j = 0; j < nearbyEnemies.length; j++) {
					var otherEnemy = nearbyEnemies[j];
					if (otherEnemy === self) continue;
					var edx = newX - otherEnemy.x;
					var edy = newY - otherEnemy.y;
					var edistance = Math.sqrt(edx * edx + edy * edy);
					if (edistance < 80) {
						wouldCollide = true;
						break;
					}
				}
				if (!wouldCollide) {
					// Keep within bounds
					if (newX > 100 && newX < currentLevelData.width - 100) {
						self.lastX = self.x;
						self.x = newX;
					}
					if (newY > 1600 && newY < 2400) {
						self.lastY = self.y;
						self.y = newY;
					}
					shieldGraphics.scaleX = dx > 0 ? 1 : -1;
				}
			}
		}
		if (self.blockCooldown > 0) {
			self.blockCooldown--;
		}
	};
	self.blockProjectile = function (projectile) {
		if (!self.shieldActive || self.blockCooldown > 0) return false;
		// Check if projectile is within blocking range
		var dx = projectile.x - self.x;
		var dy = projectile.y - self.y;
		var distance = Math.sqrt(dx * dx + dy * dy);
		if (distance < 120) {
			// Block the projectile
			projectile.destroy();
			// Remove from projectiles array
			for (var i = bossProjectiles.length - 1; i >= 0; i--) {
				if (bossProjectiles[i] === projectile) {
					bossProjectiles.splice(i, 1);
					break;
				}
			}
			// Visual block effect
			tween(shieldGraphics, {
				tint: 0x88aaff,
				scaleX: shieldGraphics.scaleX * 1.3,
				scaleY: 1.3
			}, {
				duration: 200,
				onFinish: function onFinish() {
					tween(shieldGraphics, {
						tint: 0x4488ff,
						scaleX: shieldGraphics.scaleX > 0 ? 1 : -1,
						scaleY: 1
					}, {
						duration: 200
					});
				}
			});
			// Create block sparks
			for (var s = 0; s < 8; s++) {
				var spark = game.addChild(getFromPool('impactSpark'));
				spark.reset(self.x + (Math.random() - 0.5) * 60, self.y - 60 + (Math.random() - 0.5) * 60, 'bright');
				impactSparks.push(spark);
			}
			self.blockCooldown = 60; // 1 second cooldown
			return true;
		}
		return false;
	};
	// Override takeDamage to handle shield protection
	var originalTakeDamage = self.takeDamage;
	self.takeDamage = function (fromKnife) {
		// Shield enemies take reduced damage from the front
		var damage = 1;
		if (fromKnife) {
			// Check if knife is coming from the front
			var dx = hero.x - self.x;
			var shieldFacing = shieldGraphics.scaleX > 0 ? 1 : -1;
			if (dx > 0 && shieldFacing > 0 || dx < 0 && shieldFacing < 0) {
				// Knife blocked by shield, take reduced damage
				damage = 0;
				// Visual block effect
				LK.effects.flashObject(self, 0x88aaff, 300);
				// Create block sparks
				for (var s = 0; s < 6; s++) {
					var spark = game.addChild(getFromPool('impactSpark'));
					spark.reset(self.x + (Math.random() - 0.5) * 50, self.y - 70 + (Math.random() - 0.5) * 50, 'bright');
					impactSparks.push(spark);
				}
				return; // No damage taken
			}
		}
		if (damage > 0) {
			originalTakeDamage.call(self, fromKnife);
		}
	};
	return self;
});
var Scout = Enemy.expand(function () {
	var self = Enemy.call(this, 'scout');
	var scoutGraphics = self.attachAsset('enemyFast', {
		anchorX: 0.5,
		anchorY: 1.0
	});
	scoutGraphics.tint = 0x88ff88; // Light green for scouts
	scoutGraphics.scaleX = 0.9;
	scoutGraphics.scaleY = 0.9;
	self.enemyType = 'scout';
	self.health = 1;
	self.speed = 5; // Very fast
	self.sightRange = 900; // Excellent sight
	self.alertCooldown = 0;
	self.fleeingFromCombat = false;
	self.lastAlertTime = 0;
	// Override update to include scout-specific behaviors
	var originalUpdate = self.update;
	self.update = function () {
		var distanceToHero = Math.sqrt(Math.pow(self.x - hero.x, 2) + Math.pow(self.y - hero.y, 2));
		// Scout-specific sight and alerting
		if (distanceToHero < self.sightRange && !self.alerted) {
			self.alerted = true;
			// Scouts immediately alert all nearby enemies
			groupAI.broadcastAlert(self, 'spotted_hero', 3); // High priority alert
			self.lastAlertTime = LK.ticks;
			// Create alert visual effect
			tween(scoutGraphics, {
				tint: 0xffff44,
				// Yellow alert color
				scaleX: 1.2,
				scaleY: 1.2
			}, {
				duration: 500,
				onFinish: function onFinish() {
					tween(scoutGraphics, {
						tint: 0x88ff88,
						scaleX: 0.9,
						scaleY: 0.9
					}, {
						duration: 300
					});
				}
			});
		}
		// Flee from combat when hero gets too close
		if (distanceToHero < 250) {
			self.fleeingFromCombat = true;
		} else if (distanceToHero > 500) {
			self.fleeingFromCombat = false;
		}
		if (self.fleeingFromCombat) {
			// Move away from hero at maximum speed
			var dx = self.x - hero.x;
			var dy = self.y - hero.y;
			var distance = Math.sqrt(dx * dx + dy * dy);
			if (distance > 0) {
				var fleeSpeed = self.speed * 1.5; // Extra fast when fleeing
				var newX = self.x + dx / distance * fleeSpeed;
				var newY = self.y + dy / distance * fleeSpeed;
				// Check collision with other enemies before moving
				var wouldCollide = false;
				var nearbyEnemies = spatialGrid.getNearbyObjects(newX, newY, 80);
				for (var i = 0; i < nearbyEnemies.length; i++) {
					var otherEnemy = nearbyEnemies[i];
					if (otherEnemy === self) continue;
					var edx = newX - otherEnemy.x;
					var edy = newY - otherEnemy.y;
					var edistance = Math.sqrt(edx * edx + edy * edy);
					if (edistance < 60) {
						wouldCollide = true;
						break;
					}
				}
				if (!wouldCollide) {
					// Keep within level bounds
					if (newX > 100 && newX < currentLevelData.width - 100) {
						self.lastX = self.x;
						self.x = newX;
					}
					if (newY > 1600 && newY < 2400) {
						self.lastY = self.y;
						self.y = newY;
					}
					scoutGraphics.scaleX = dx > 0 ? 0.9 : -0.9; // Face away from hero
				}
			}
		} else {
			// Use original enemy update when not fleeing
			originalUpdate.call(self);
		}
		// Periodic re-alerting to maintain enemy awareness
		if (self.alerted && LK.ticks - self.lastAlertTime > 300) {
			// Every 5 seconds
			groupAI.broadcastAlert(self, 'spotted_hero', 2);
			self.lastAlertTime = LK.ticks;
		}
	};
	// Override takeDamage to trigger emergency broadcast
	var originalTakeDamage = self.takeDamage;
	self.takeDamage = function (fromKnife) {
		// Emergency alert when scout is attacked
		groupAI.broadcastAlert(self, 'under_attack', 3);
		originalTakeDamage.call(self, fromKnife);
	};
	return self;
});
var Leader = Enemy.expand(function () {
	var self = Enemy.call(this, 'leader');
	var leaderGraphics = self.attachAsset('enemyStrong', {
		anchorX: 0.5,
		anchorY: 1.0
	});
	leaderGraphics.tint = 0xFFD700; // Golden tint for leaders
	leaderGraphics.scaleX = 1.2;
	leaderGraphics.scaleY = 1.2;
	self.enemyType = 'leader';
	self.health = 6;
	self.speed = 1.5;
	self.buffRadius = 250;
	self.commandRadius = 400;
	self.buffCooldown = 0;
	self.isCommanding = false;
	self.commandedAllies = [];
	// Override update to include leadership behaviors
	var originalUpdate = self.update;
	self.update = function () {
		// Call original enemy update first
		originalUpdate.call(self);
		// Leadership AI behaviors
		self.manageAllies();
		self.coordinateAttacks();
		self.provideBattlefieldIntelligence();
		// Update buff cooldown
		if (self.buffCooldown > 0) self.buffCooldown--;
	};
	self.manageAllies = function () {
		var nearbyAllies = spatialGrid.getNearbyObjects(self.x, self.y, self.commandRadius);
		self.commandedAllies = [];
		for (var i = 0; i < nearbyAllies.length; i++) {
			var ally = nearbyAllies[i];
			if (ally !== self && ally.enemyType !== 'boss' && ally.enemyType !== 'leader') {
				self.commandedAllies.push(ally);
				// Provide speed buff to nearby allies
				if (Math.sqrt(Math.pow(self.x - ally.x, 2) + Math.pow(self.y - ally.y, 2)) <= self.buffRadius) {
					if (!ally.leaderBuff) {
						ally.leaderBuff = {
							speedMultiplier: 1.3,
							damageMultiplier: 1.2,
							source: self
						};
						// Visual indication of buff
						createGlowOutline(ally, 0xFFD700, 0.3);
					}
				}
			}
		}
	};
	self.coordinateAttacks = function () {
		if (self.commandedAllies.length >= 2 && self.buffCooldown <= 0) {
			// Order flanking maneuvers
			groupAI.broadcastAlert(self, 'flanking_position', 3);
			for (var i = 0; i < Math.min(3, self.commandedAllies.length); i++) {
				var ally = self.commandedAllies[i];
				if (!ally.isAssignedFlanker) {
					groupAI.assignFlankingPosition(ally);
				}
			}
			self.buffCooldown = 300; // 5 seconds
			self.isCommanding = true;
			// Visual command effect
			tween(leaderGraphics, {
				tint: 0xFFAA00,
				scaleX: 1.4,
				scaleY: 1.4
			}, {
				duration: 500,
				onFinish: function onFinish() {
					tween(leaderGraphics, {
						tint: 0xFFD700,
						scaleX: 1.2,
						scaleY: 1.2
					}, {
						duration: 300
					});
					self.isCommanding = false;
				}
			});
		}
	};
	self.provideBattlefieldIntelligence = function () {
		// Share hero position with distant allies
		if (Math.random() < 0.05) {
			// 5% chance per frame
			groupAI.broadcastAlert(self, 'spotted_hero', 2);
		}
	};
	// Override takeDamage to rally allies when leader is threatened
	var originalTakeDamage = self.takeDamage;
	self.takeDamage = function (fromKnife) {
		originalTakeDamage.call(self, fromKnife);
		// Rally cry when damaged
		groupAI.broadcastAlert(self, 'under_attack', 3);
		// Boost nearby allies when leader is in danger
		for (var i = 0; i < self.commandedAllies.length; i++) {
			var ally = self.commandedAllies[i];
			ally.alerted = true;
			if (ally.leaderBuff) {
				ally.leaderBuff.speedMultiplier = 1.5; // Increased speed when leader threatened
			}
		}
	};
	// Override die to remove buffs from allies
	var originalDie = self.die;
	self.die = function () {
		// Remove leader buffs from all allies
		for (var i = 0; i < enemies.length; i++) {
			var enemy = enemies[i];
			if (enemy.leaderBuff && enemy.leaderBuff.source === self) {
				enemy.leaderBuff = null;
				removeGlowOutline(enemy);
			}
		}
		originalDie.call(self);
	};
	return self;
});
var Berserker = Enemy.expand(function () {
	var self = Enemy.call(this, 'berserker');
	var berserkerGraphics = self.attachAsset('enemyStrong', {
		anchorX: 0.5,
		anchorY: 1.0
	});
	berserkerGraphics.tint = 0xff6666; // Red tint for berserkers
	self.enemyType = 'berserker';
	self.health = 4;
	self.maxHealth = 4;
	self.speed = 1.5;
	self.baseSpeed = 1.5;
	self.isBerserk = false;
	self.berserkThreshold = 1; // Goes berserk at 1 health
	// Override update to include berserker rage
	var originalUpdate = self.update;
	self.update = function () {
		// Check for berserk activation
		if (!self.isBerserk && self.health <= self.berserkThreshold) {
			self.activateBerserk();
		}
		originalUpdate.call(self);
	};
	self.activateBerserk = function () {
		self.isBerserk = true;
		self.speed = self.baseSpeed * 2.5; // Much faster when berserk
		berserkerGraphics.tint = 0xff0000; // Bright red when berserk
		berserkerGraphics.scaleX = 1.3;
		berserkerGraphics.scaleY = 1.3;
		self.attackCooldown = Math.floor(self.attackCooldown * 0.5); // Faster attacks
		// Visual berserk effect
		tween(berserkerGraphics, {
			scaleX: 1.5,
			scaleY: 1.5
		}, {
			duration: 300,
			easing: tween.easeOut,
			onFinish: function onFinish() {
				tween(berserkerGraphics, {
					scaleX: 1.3,
					scaleY: 1.3
				}, {
					duration: 200
				});
			}
		});
		// Create rage particles
		for (var i = 0; i < 12; i++) {
			var particle = game.addChild(getFromPool('bloodParticle'));
			particle.reset(self.x + (Math.random() - 0.5) * 80, self.y - 80 + (Math.random() - 0.5) * 80, 'spray');
			var particleGraphics = particle.getChildAt(0);
			particleGraphics.tint = 0xff4444; // Red rage particles
			bloodParticles.push(particle);
		}
		// Broadcast rage to alert other enemies
		groupAI.broadcastAlert(self, 'under_attack', 2);
	};
	// Override takeDamage to handle berserk damage bonus
	var originalTakeDamage = self.takeDamage;
	self.takeDamage = function (fromKnife) {
		var damage = 1;
		// Berserk berserkers deal damage back to attackers
		if (self.isBerserk && !fromKnife) {
			// Only counter-attack melee attacks, not knife throws
			var distanceToHero = Math.sqrt(Math.pow(self.x - hero.x, 2) + Math.pow(self.y - hero.y, 2));
			if (distanceToHero < 150) {
				hero.takeDamage(); // Counter-attack
				LK.effects.flashObject(self, 0xff0000, 300);
			}
		}
		originalTakeDamage.call(self, fromKnife);
	};
	return self;
});
var Archer = Enemy.expand(function () {
	var self = Enemy.call(this, 'archer');
	var archerGraphics = self.attachAsset('enemyHunter', {
		anchorX: 0.5,
		anchorY: 1.0
	});
	archerGraphics.tint = 0x8844ff; // Purple tint for archers
	self.enemyType = 'archer';
	self.health = 2;
	self.speed = 1.8;
	self.shootCooldown = 0;
	self.optimalRange = 400; // Preferred distance from hero
	self.maxRange = 600; // Maximum shooting range
	// Override update to include archer behavior
	var originalUpdate = self.update;
	self.update = function () {
		var distanceToHero = Math.sqrt(Math.pow(self.x - hero.x, 2) + Math.pow(self.y - hero.y, 2));
		// Archer positioning: maintain optimal distance
		if (distanceToHero < self.optimalRange - 50) {
			// Too close, move away
			var dx = self.x - hero.x;
			var dy = self.y - hero.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 before moving
				var wouldCollide = false;
				var nearbyEnemies = spatialGrid.getNearbyObjects(newX, newY, 80);
				for (var i = 0; i < nearbyEnemies.length; i++) {
					var otherEnemy = nearbyEnemies[i];
					if (otherEnemy === self) continue;
					var edx = newX - otherEnemy.x;
					var edy = newY - otherEnemy.y;
					var edistance = Math.sqrt(edx * edx + edy * edy);
					if (edistance < 70) {
						wouldCollide = true;
						break;
					}
				}
				if (!wouldCollide) {
					// Keep within bounds
					if (newX > 100 && newX < currentLevelData.width - 100) {
						self.lastX = self.x;
						self.x = newX;
					}
					if (newY > 1600 && newY < 2400) {
						self.lastY = self.y;
						self.y = newY;
					}
					archerGraphics.scaleX = dx > 0 ? 1 : -1; // Face away from hero
				}
			}
		} else if (distanceToHero > self.optimalRange + 50) {
			// Too far, move closer (use original enemy behavior)
			originalUpdate.call(self);
		}
		// Shoot at hero if in range and cooldown is ready
		if (distanceToHero <= self.maxRange && self.shootCooldown <= 0 && self.alerted) {
			self.shootArrow();
		}
		if (self.shootCooldown > 0) {
			self.shootCooldown--;
		}
		// Update attack cooldown from original
		if (self.attackCooldown > 0) {
			self.attackCooldown--;
		}
	};
	self.shootArrow = function () {
		// Create projectile
		var arrow = game.addChild(new ArcherProjectile());
		arrow.x = self.x;
		arrow.y = self.y - 100;
		// Calculate trajectory 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 = 8;
			arrow.velocityX = dx / distance * speed;
			arrow.velocityY = dy / distance * speed;
		}
		bossProjectiles.push(arrow); // Reuse boss projectiles array
		self.shootCooldown = 180; // 3 seconds
		// Visual shooting effect
		tween(archerGraphics, {
			tint: 0xff4488,
			scaleX: archerGraphics.scaleX * 1.2,
			scaleY: 1.2
		}, {
			duration: 200,
			onFinish: function onFinish() {
				tween(archerGraphics, {
					tint: 0x8844ff,
					scaleX: archerGraphics.scaleX > 0 ? 1 : -1,
					scaleY: 1
				}, {
					duration: 200
				});
			}
		});
	};
	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;
		// Apply attack speed upgrade
		var baseSlashCooldown = 24; // 0.4 seconds at 60fps
		self.slashCooldown = Math.floor(baseSlashCooldown / (self.attackSpeedMultiplier || 1));
		// 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;
		// Create weapon trail effect
		var trail = game.addChild(getFromPool('weaponTrail'));
		var trailType = self.damageBoost ? 'power' : 'normal';
		trail.reset(self.x + heroGraphics.scaleX * 100, self.y - 80, heroGraphics.scaleX > 0 ? -0.3 : 0.3, 2, trailType);
		weaponTrails.push(trail);
		// 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;
		// Apply damage reduction
		var damageReduction = self.damageReduction || 0;
		if (Math.random() < damageReduction) {
			// Damage reduced! Show visual effect
			LK.effects.flashObject(self, 0x3498db, 300);
			return;
		}
		self.health--;
		self.comboCount = 0;
		// Flash red when hit
		LK.effects.flashObject(self, 0xff0000, 500);
		// Screen shake when player is hit
		triggerScreenShake(18, 400, tween.easeOut);
		// Temporary invulnerability with upgrade bonus
		self.invulnerable = true;
		var invulnerabilityDuration = 1000 + (self.invulnerabilityBonus || 0);
		LK.setTimeout(function () {
			self.invulnerable = false;
		}, invulnerabilityDuration);
		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 = self.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;
			// Create dust clouds when moving (occasional)
			if (Math.random() < 0.3) {
				var dustCloud = game.addChild(getFromPool('dustCloud'));
				dustCloud.reset(self.x + (Math.random() - 0.5) * 30, self.y - 10 + (Math.random() - 0.5) * 20, direction);
				dustClouds.push(dustCloud);
			}
			// 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 ImpactSpark = Container.expand(function () {
	var self = Container.call(this);
	var sparkGraphics = self.attachAsset('bloodParticle', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	sparkGraphics.tint = 0xffff44; // Yellow-white sparks
	sparkGraphics.scaleX = 0.3;
	sparkGraphics.scaleY = 0.3;
	self.velocityX = 0;
	self.velocityY = 0;
	self.lifetime = 20; // Short-lived sparks
	self.sparkType = 'normal'; // normal, bright, orange
	self.reset = function (startX, startY, type) {
		self.x = startX;
		self.y = startY;
		self.sparkType = type || 'normal';
		// Random direction and speed for sparks
		var angle = Math.random() * Math.PI * 2;
		var speed = 3 + Math.random() * 5;
		self.velocityX = Math.cos(angle) * speed;
		self.velocityY = Math.sin(angle) * speed;
		if (self.sparkType === 'bright') {
			sparkGraphics.tint = 0xffffff; // White sparks
			sparkGraphics.scaleX = 0.4;
			sparkGraphics.scaleY = 0.4;
			self.lifetime = 30;
		} else if (self.sparkType === 'orange') {
			sparkGraphics.tint = 0xff8844; // Orange sparks
			sparkGraphics.scaleX = 0.2;
			sparkGraphics.scaleY = 0.2;
			self.lifetime = 15;
		} else {
			sparkGraphics.tint = 0xffff44; // Yellow sparks
			sparkGraphics.scaleX = 0.3;
			sparkGraphics.scaleY = 0.3;
			self.lifetime = 20;
		}
		sparkGraphics.alpha = 1;
		self.visible = true;
	};
	self.update = function () {
		self.x += self.velocityX;
		self.y += self.velocityY;
		// Sparks slow down over time
		self.velocityX *= 0.95;
		self.velocityY *= 0.95;
		// Fade out quickly
		self.lifetime--;
		sparkGraphics.alpha = self.lifetime / 20;
		if (self.lifetime <= 0) {
			// Remove from impactSparks array
			for (var i = impactSparks.length - 1; i >= 0; i--) {
				if (impactSparks[i] === self) {
					impactSparks.splice(i, 1);
					break;
				}
			}
			returnToPool(self, 'impactSpark');
		}
	};
	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)
		var penetration = upgradeTree ? upgradeTree.getKnifePenetration() : 0;
		var enemiesHit = 0;
		var maxEnemiesHit = 1 + penetration;
		for (var i = 0; i < enemies.length && enemiesHit < maxEnemiesHit; 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 with upgrade multiplier
				var damage = Math.floor(hero.damageMultiplier || 1);
				for (var d = 0; d < damage; d++) {
					enemy.takeDamage(true);
				}
				// Light screen shake on knife impact
				triggerScreenShake(4, 100, tween.easeOut);
				enemiesHit++;
				// If no penetration or hit max enemies, destroy knife
				if (penetration === 0 || enemiesHit >= maxEnemiesHit) {
					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 MagicalEffect = Container.expand(function () {
	var self = Container.call(this);
	var effectGraphics = self.attachAsset('bloodParticle', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	self.velocityX = 0;
	self.velocityY = 0;
	self.lifetime = 60;
	self.effectType = 'sparkle'; // sparkle, healing, power, shield
	self.rotationSpeed = 0;
	self.reset = function (startX, startY, type) {
		self.x = startX;
		self.y = startY;
		self.effectType = type || 'sparkle';
		if (self.effectType === 'sparkle') {
			effectGraphics.tint = 0xffff88; // Golden sparkle
			effectGraphics.scaleX = 0.6;
			effectGraphics.scaleY = 0.6;
			var angle = Math.random() * Math.PI * 2;
			var speed = 2 + Math.random() * 3;
			self.velocityX = Math.cos(angle) * speed;
			self.velocityY = Math.sin(angle) * speed - 2; // Float upward
			self.rotationSpeed = (Math.random() - 0.5) * 0.2;
			self.lifetime = 80;
		} else if (self.effectType === 'healing') {
			effectGraphics.tint = 0x44ff44; // Green healing
			effectGraphics.scaleX = 0.8;
			effectGraphics.scaleY = 0.8;
			self.velocityX = (Math.random() - 0.5) * 2;
			self.velocityY = -3 - Math.random() * 2; // Rise upward
			self.rotationSpeed = 0.1;
			self.lifetime = 60;
		} else if (self.effectType === 'power') {
			effectGraphics.tint = 0xff4488; // Purple power
			effectGraphics.scaleX = 1.0;
			effectGraphics.scaleY = 1.0;
			self.velocityX = (Math.random() - 0.5) * 4;
			self.velocityY = -1 - Math.random() * 3;
			self.rotationSpeed = 0.15;
			self.lifetime = 70;
		} else if (self.effectType === 'shield') {
			effectGraphics.tint = 0x44ddff; // Blue shield
			effectGraphics.scaleX = 0.5;
			effectGraphics.scaleY = 0.5;
			var angle = Math.random() * Math.PI * 2;
			var radius = 50 + Math.random() * 30;
			self.velocityX = Math.cos(angle) * 2;
			self.velocityY = Math.sin(angle) * 2;
			self.rotationSpeed = 0.08;
			self.lifetime = 90;
		}
		effectGraphics.alpha = 1;
		self.visible = true;
	};
	self.update = function () {
		self.x += self.velocityX;
		self.y += self.velocityY;
		effectGraphics.rotation += self.rotationSpeed;
		// Different behaviors per effect type
		if (self.effectType === 'sparkle') {
			self.velocityY -= 0.05; // Continue floating up
		} else if (self.effectType === 'healing') {
			self.velocityX *= 0.99; // Slow down horizontally
		} else if (self.effectType === 'power') {
			// Pulsing effect
			var pulseScale = 1.0 + Math.sin(LK.ticks * 0.3) * 0.2;
			effectGraphics.scaleX = pulseScale;
			effectGraphics.scaleY = pulseScale;
		}
		// Fade out over time
		self.lifetime--;
		var maxLifetime = 60;
		if (self.effectType === 'sparkle') maxLifetime = 80;else if (self.effectType === 'power') maxLifetime = 70;else if (self.effectType === 'shield') maxLifetime = 90;
		effectGraphics.alpha = self.lifetime / maxLifetime;
		if (self.lifetime <= 0) {
			// Remove from magicalEffects array
			for (var i = magicalEffects.length - 1; i >= 0; i--) {
				if (magicalEffects[i] === self) {
					magicalEffects.splice(i, 1);
					break;
				}
			}
			returnToPool(self, 'magicalEffect');
		}
	};
	return self;
});
var Minion = Container.expand(function () {
	var self = Container.call(this);
	var minionGraphics = self.attachAsset('enemyFast', {
		anchorX: 0.5,
		anchorY: 1.0
	});
	minionGraphics.tint = 0x9b59b6; // Purple tint for minions
	minionGraphics.scaleX = 0.8; // Smaller than normal enemies
	minionGraphics.scaleY = 0.8;
	self.health = 1;
	self.speed = 3.5;
	self.attackCooldown = 0;
	self.lastX = 0;
	self.lastY = 0;
	self.summoner = null; // Reference to summoning boss
	self.lifetime = 1800; // 30 seconds lifetime
	self.update = function () {
		self.lifetime--;
		if (self.lifetime <= 0) {
			self.die();
			return;
		}
		var distanceToHero = Math.sqrt(Math.pow(self.x - hero.x, 2) + Math.pow(self.y - hero.y, 2));
		// Aggressive AI - always 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) {
			// Check collision with other enemies before moving
			var wouldCollide = false;
			var newX = self.x + dx / distance * self.speed;
			var newY = self.y + dy / distance * self.speed;
			var nearbyEnemies = spatialGrid.getNearbyObjects(newX, newY, 100);
			for (var i = 0; i < nearbyEnemies.length; i++) {
				var otherEnemy = nearbyEnemies[i];
				if (otherEnemy === self) continue;
				var edx = newX - otherEnemy.x;
				var edy = newY - otherEnemy.y;
				var edistance = Math.sqrt(edx * edx + edy * edy);
				if (edistance < 60) {
					// Smaller collision radius for minions
					wouldCollide = true;
					break;
				}
			}
			if (!wouldCollide) {
				self.lastX = self.x;
				self.lastY = self.y;
				self.x = newX;
				self.y = newY;
			}
			minionGraphics.scaleX = dx > 0 ? 0.8 : -0.8;
		}
		// Attack hero if close enough
		if (distanceToHero < 80 && self.attackCooldown <= 0) {
			hero.takeDamage();
			self.attackCooldown = 90; // 1.5 seconds
		}
		if (self.attackCooldown > 0) {
			self.attackCooldown--;
		}
	};
	self.takeDamage = function (fromKnife) {
		self.health--;
		LK.effects.flashObject(self, 0xffffff, 200);
		// Create smaller blood particles
		for (var p = 0; p < 8; p++) {
			var bloodParticle = game.addChild(getFromPool('bloodParticle'));
			bloodParticle.reset(self.x + (Math.random() - 0.5) * 30, self.y - 40 + (Math.random() - 0.5) * 30);
			bloodParticles.push(bloodParticle);
		}
		if (self.health <= 0) {
			self.die();
		}
	};
	self.die = function () {
		// Minions give 2 coins
		upgradeTree.addCoins(2);
		// Drop small coin
		var coin = game.addChild(new Coin());
		coin.x = self.x;
		coin.y = self.y;
		var coinGraphics = coin.getChildAt(0);
		coinGraphics.scaleX = 0.7; // Smaller coin
		coinGraphics.scaleY = 0.7;
		coins.push(coin);
		// Reduce summoner's minion count
		if (self.summoner && self.summoner.minionCount) {
			self.summoner.minionCount--;
		}
		// Small score bonus
		LK.setScore(LK.getScore() + 5);
		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 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();
		// Create magical effects based on power-up type
		var effectType = 'sparkle';
		if (self.type === 'health') {
			hero.heal();
			effectType = 'healing';
		} else if (self.type === 'invulnerable') {
			hero.invulnerable = true;
			effectType = 'shield';
			LK.setTimeout(function () {
				hero.invulnerable = false;
			}, 5000);
		} else if (self.type === 'damage') {
			hero.damageBoost = true;
			effectType = 'power';
			LK.setTimeout(function () {
				hero.damageBoost = false;
			}, 5000);
		}
		// Create magical particle effects
		for (var e = 0; e < 12; e++) {
			var magicalEffect = game.addChild(getFromPool('magicalEffect'));
			magicalEffect.reset(self.x + (Math.random() - 0.5) * 60, self.y + (Math.random() - 0.5) * 60, effectType);
			magicalEffects.push(magicalEffect);
		}
		// 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;
});
var ScreenShake = Container.expand(function () {
	var self = Container.call(this);
	self.intensity = 0;
	self.duration = 0;
	self.remainingTime = 0;
	self.offsetX = 0;
	self.offsetY = 0;
	self.isActive = false;
	self.decayEasing = tween.easeOut;
	self.shake = function (intensity, duration, easing) {
		self.intensity = intensity || 10;
		self.duration = duration || 500;
		self.remainingTime = self.duration;
		self.isActive = true;
		self.decayEasing = easing || tween.easeOut;
	};
	self.update = function () {
		if (!self.isActive) {
			self.offsetX = 0;
			self.offsetY = 0;
			return;
		}
		self.remainingTime -= 16.67; // Approximately 1 frame at 60fps
		if (self.remainingTime <= 0) {
			self.isActive = false;
			self.offsetX = 0;
			self.offsetY = 0;
			return;
		}
		// Calculate decay factor using easing function
		var progress = 1 - self.remainingTime / self.duration;
		var decayFactor = 1 - self.decayEasing(progress);
		// Generate random shake offset
		var currentIntensity = self.intensity * decayFactor;
		self.offsetX = (Math.random() - 0.5) * 2 * currentIntensity;
		self.offsetY = (Math.random() - 0.5) * 2 * currentIntensity;
	};
	self.getOffsetX = function () {
		return self.offsetX;
	};
	self.getOffsetY = function () {
		return self.offsetY;
	};
	return self;
});
var UpgradeMenu = Container.expand(function () {
	var self = Container.call(this);
	self.isVisible = false;
	self.upgradeTree = null;
	self.upgradeButtons = [];
	self.categoryHeaders = [];
	self.background = null;
	self.show = function (upgradeTree) {
		self.upgradeTree = upgradeTree;
		self.isVisible = true;
		self.x = 0;
		self.y = 0;
		self.createInterface();
		// Animate menu appearance
		self.alpha = 0;
		self.scaleX = 0.8;
		self.scaleY = 0.8;
		tween(self, {
			alpha: 1,
			scaleX: 1,
			scaleY: 1
		}, {
			duration: 300,
			easing: tween.easeOut
		});
	};
	self.hide = function () {
		tween(self, {
			alpha: 0,
			scaleX: 0.8,
			scaleY: 0.8
		}, {
			duration: 200,
			easing: tween.easeIn,
			onFinish: function onFinish() {
				self.isVisible = false;
				self.clearInterface();
			}
		});
	};
	self.createInterface = function () {
		// Create background centered on screen
		self.background = self.addChild(LK.getAsset('backgroundTile', {
			anchorX: 0.5,
			anchorY: 0.5,
			tint: 0x2c3e50,
			alpha: 0.95,
			scaleX: 10,
			scaleY: 12
		}));
		self.background.x = 0;
		self.background.y = 0;
		// Title
		var title = new Text2('UPGRADE TREE', {
			size: 100,
			fill: '#FFD700'
		});
		title.anchor.set(0.5, 0.5);
		title.x = 0;
		title.y = -1000;
		self.addChild(title);
		// Coins display
		var coinsText = new Text2('Coins: ' + self.upgradeTree.coins, {
			size: 60,
			fill: '#F1C40F'
		});
		coinsText.anchor.set(0.5, 0.5);
		coinsText.x = 0;
		coinsText.y = -900;
		self.addChild(coinsText);
		self.coinsText = coinsText;
		// Combat category
		self.createCategory('COMBAT', -600, [{
			type: 'damage',
			name: 'Damage Boost',
			desc: '+20% damage per level'
		}, {
			type: 'attackSpeed',
			name: 'Attack Speed',
			desc: '+25% attack speed per level'
		}, {
			type: 'knifePenetration',
			name: 'Knife Penetration',
			desc: 'Knives hit multiple enemies'
		}]);
		// Defense category
		self.createCategory('DEFENSE', -100, [{
			type: 'maxHealth',
			name: 'Max Health',
			desc: '+1 max health per level'
		}, {
			type: 'damageReduction',
			name: 'Damage Reduction',
			desc: '+15% damage reduction per level'
		}, {
			type: 'invulnerability',
			name: 'Invulnerability',
			desc: '+1 second invulnerability per level'
		}]);
		// Utility category
		self.createCategory('UTILITY', 400, [{
			type: 'moveSpeed',
			name: 'Move Speed',
			desc: '+25% movement speed per level'
		}, {
			type: 'startingKnives',
			name: 'Starting Knives',
			desc: '+2 starting knives per level'
		}, {
			type: 'coinMagnetism',
			name: 'Coin Magnetism',
			desc: 'Increased coin collection range'
		}]);
		// Close button
		var closeButton = self.addChild(LK.getAsset('backgroundTile', {
			anchorX: 0.5,
			anchorY: 0.5,
			tint: 0xe74c3c,
			scaleX: 2,
			scaleY: 1
		}));
		closeButton.x = 0;
		closeButton.y = 1000;
		var closeText = new Text2('CLOSE', {
			size: 60,
			fill: '#FFFFFF'
		});
		closeText.anchor.set(0.5, 0.5);
		closeText.x = 0;
		closeText.y = 1000;
		self.addChild(closeText);
		closeButton.down = function () {
			self.hide();
		};
	};
	self.createCategory = function (categoryName, startY, upgrades) {
		// Category header
		var header = new Text2(categoryName, {
			size: 80,
			fill: '#3498db'
		});
		header.anchor.set(0.5, 0.5);
		header.x = 0;
		header.y = startY;
		self.addChild(header);
		// Upgrade buttons
		for (var i = 0; i < upgrades.length; i++) {
			var upgrade = upgrades[i];
			var buttonY = startY + 100 + i * 120;
			self.createUpgradeButton(upgrade, buttonY);
		}
	};
	self.createUpgradeButton = function (upgrade, y) {
		var currentLevel = self.upgradeTree.upgrades[upgrade.type];
		var maxLevel = self.upgradeTree.upgradeCosts[upgrade.type].length;
		var canUpgrade = self.upgradeTree.canUpgrade(upgrade.type);
		var cost = currentLevel < maxLevel ? self.upgradeTree.upgradeCosts[upgrade.type][currentLevel] : 0;
		// Button background
		var buttonColor = currentLevel >= maxLevel ? 0x27ae60 : canUpgrade ? 0x3498db : 0x7f8c8d;
		var button = self.addChild(LK.getAsset('backgroundTile', {
			anchorX: 0.5,
			anchorY: 0.5,
			tint: buttonColor,
			scaleX: 8,
			scaleY: 0.8
		}));
		button.x = 0;
		button.y = y;
		// Upgrade name
		var nameText = new Text2(upgrade.name, {
			size: 50,
			fill: '#FFFFFF'
		});
		nameText.anchor.set(0.5, 0.5);
		nameText.x = -424;
		nameText.y = y - 15;
		self.addChild(nameText);
		// Level display
		var levelText = new Text2('Level: ' + currentLevel + '/' + maxLevel, {
			size: 40,
			fill: '#BDC3C7'
		});
		levelText.anchor.set(0.5, 0.5);
		levelText.x = -424;
		levelText.y = y + 15;
		self.addChild(levelText);
		// Cost display
		if (currentLevel < maxLevel) {
			var costText = new Text2('Cost: ' + cost, {
				size: 40,
				fill: canUpgrade ? '#F1C40F' : '#E74C3C'
			});
			costText.anchor.set(0.5, 0.5);
			costText.x = 376;
			costText.y = y;
			self.addChild(costText);
		} else {
			var maxText = new Text2('MAX', {
				size: 40,
				fill: '#27AE60'
			});
			maxText.anchor.set(0.5, 0.5);
			maxText.x = 376;
			maxText.y = y;
			self.addChild(maxText);
		}
		// Purchase functionality
		if (canUpgrade) {
			button.down = function () {
				if (self.upgradeTree.purchaseUpgrade(upgrade.type)) {
					// Refresh interface
					self.clearInterface();
					self.createInterface();
					// Success effect
					tween(button, {
						tint: 0x27ae60,
						scaleX: 9,
						scaleY: 0.9
					}, {
						duration: 200,
						onFinish: function onFinish() {
							tween(button, {
								tint: buttonColor,
								scaleX: 8,
								scaleY: 0.8
							}, {
								duration: 200
							});
						}
					});
				}
			};
		}
	};
	self.clearInterface = function () {
		// Remove all children
		while (self.children.length > 0) {
			self.children[0].destroy();
		}
		self.upgradeButtons = [];
		self.categoryHeaders = [];
		self.background = null;
	};
	return self;
});
var UpgradeTree = Container.expand(function () {
	var self = Container.call(this);
	// Initialize upgrade data from storage
	self.upgrades = storage.upgrades || {
		// Combat upgrades
		damage: 0,
		// 0-5: +20% damage per level
		attackSpeed: 0,
		// 0-3: +25% attack speed per level
		knifePenetration: 0,
		// 0-3: knives can hit multiple enemies
		// Defense upgrades
		maxHealth: 0,
		// 0-5: +1 max health per level
		damageReduction: 0,
		// 0-3: +15% damage reduction per level
		invulnerability: 0,
		// 0-2: +1 second invulnerability per level
		// Utility upgrades
		moveSpeed: 0,
		// 0-3: +25% movement speed per level
		startingKnives: 0,
		// 0-4: +2 starting knives per level
		coinMagnetism: 0 // 0-3: increased coin collection range per level
	};
	// Upgrade costs (coins required)
	self.upgradeCosts = {
		damage: [100, 200, 400, 800, 1600],
		attackSpeed: [150, 300, 600],
		knifePenetration: [200, 500, 1000],
		maxHealth: [80, 160, 320, 640, 1280],
		damageReduction: [120, 240, 480],
		invulnerability: [300, 600],
		moveSpeed: [100, 200, 400],
		startingKnives: [150, 300, 600, 1200],
		coinMagnetism: [100, 250, 500]
	};
	self.coins = storage.coins || 0;
	self.canUpgrade = function (upgradeType) {
		var currentLevel = self.upgrades[upgradeType];
		var maxLevel = self.upgradeCosts[upgradeType].length;
		if (currentLevel >= maxLevel) return false;
		return self.coins >= self.upgradeCosts[upgradeType][currentLevel];
	};
	self.purchaseUpgrade = function (upgradeType) {
		if (!self.canUpgrade(upgradeType)) return false;
		var currentLevel = self.upgrades[upgradeType];
		var cost = self.upgradeCosts[upgradeType][currentLevel];
		self.coins -= cost;
		self.upgrades[upgradeType]++;
		// Save to storage
		storage.upgrades = self.upgrades;
		storage.coins = self.coins;
		return true;
	};
	self.addCoins = function (amount) {
		self.coins += amount;
		storage.coins = self.coins;
	};
	// Get upgrade bonuses
	self.getDamageMultiplier = function () {
		return 1 + self.upgrades.damage * 0.2;
	};
	self.getAttackSpeedMultiplier = function () {
		return 1 + self.upgrades.attackSpeed * 0.25;
	};
	self.getKnifePenetration = function () {
		return self.upgrades.knifePenetration;
	};
	self.getMaxHealthBonus = function () {
		return self.upgrades.maxHealth;
	};
	self.getDamageReduction = function () {
		return self.upgrades.damageReduction * 0.15;
	};
	self.getInvulnerabilityBonus = function () {
		return self.upgrades.invulnerability * 1000; // milliseconds
	};
	self.getMoveSpeedMultiplier = function () {
		return 1 + self.upgrades.moveSpeed * 0.25;
	};
	self.getStartingKnivesBonus = function () {
		return self.upgrades.startingKnives * 2;
	};
	self.getCoinMagnetismRange = function () {
		return 100 + self.upgrades.coinMagnetism * 50;
	};
	return self;
});
var WeaponTrail = Container.expand(function () {
	var self = Container.call(this);
	var trailGraphics = self.attachAsset('slashEffect', {
		anchorX: 0.5,
		anchorY: 0.5,
		alpha: 0.6,
		tint: 0xFFFFFF
	});
	self.lifetime = 30; // Short trail life
	self.maxLifetime = 30;
	self.reset = function (startX, startY, rotation, scale, trailType) {
		self.x = startX;
		self.y = startY;
		self.lifetime = self.maxLifetime;
		trailGraphics.rotation = rotation;
		trailGraphics.scaleX = scale;
		trailGraphics.scaleY = scale * 0.5; // Flatter trail
		trailGraphics.alpha = 0.6;
		self.visible = true;
		// Different trail effects based to type
		if (trailType === 'critical') {
			trailGraphics.tint = 0xFFD700; // Golden trail for critical hits
			trailGraphics.scaleY = scale * 0.8; // Thicker trail
		} else if (trailType === 'power') {
			trailGraphics.tint = 0xFF4488; // Purple trail for powered attacks
		} else {
			trailGraphics.tint = 0xFFFFFF; // White trail for normal
		}
	};
	self.update = function () {
		self.lifetime--;
		// Fade and shrink over time
		var lifeRatio = self.lifetime / self.maxLifetime;
		trailGraphics.alpha = 0.6 * lifeRatio;
		trailGraphics.scaleX *= 0.98;
		trailGraphics.scaleY *= 0.96;
		if (self.lifetime <= 0) {
			// Remove from weaponTrails array
			for (var i = weaponTrails.length - 1; i >= 0; i--) {
				if (weaponTrails[i] === self) {
					weaponTrails.splice(i, 1);
					break;
				}
			}
			returnToPool(self, 'weaponTrail');
		}
	};
	return self;
});
/**** 
* Initialize Game
****/ 
var game = new LK.Game({
	backgroundColor: 0x2c3e50,
	title: 'Dungeon Crawler'
});
/**** 
* Game Code
****/ 
// Legacy Boss class for backward compatibility (delegates to BossType1)
// Game variables
var Boss = BossType1;
// Upgrade system
var upgradeTree = new UpgradeTree();
var upgradeMenu = new UpgradeMenu();
var showUpgradesButton = null;
// Visual effect functions
function createGlowOutline(target, color, intensity) {
	var outline = target.addChild(LK.getAsset('slashEffect', {
		anchorX: 0.5,
		anchorY: 0.5,
		alpha: 0,
		tint: color,
		scaleX: 1.2,
		scaleY: 1.5
	}));
	outline.x = 0;
	outline.y = -70; // Center on enemy
	target.glowOutline = outline;
	tween(outline, {
		alpha: intensity * 0.4,
		scaleX: 1.4,
		scaleY: 1.8
	}, {
		duration: 300,
		easing: tween.easeInOut
	});
	// Pulse effect
	function pulseGlow() {
		if (outline.parent) {
			tween(outline, {
				alpha: intensity * 0.2
			}, {
				duration: 500,
				easing: tween.easeInOut,
				onFinish: function onFinish() {
					if (outline.parent) {
						tween(outline, {
							alpha: intensity * 0.4
						}, {
							duration: 500,
							easing: tween.easeInOut,
							onFinish: pulseGlow
						});
					}
				}
			});
		}
	}
	pulseGlow();
}
function removeGlowOutline(target) {
	if (target.glowOutline) {
		tween(target.glowOutline, {
			alpha: 0
		}, {
			duration: 200,
			onFinish: function onFinish() {
				if (target.glowOutline) {
					target.glowOutline.destroy();
					target.glowOutline = null;
				}
			}
		});
	}
}
function createScreenFlash(color, duration, intensity) {
	var flashOverlay = LK.gui.center.addChild(LK.getAsset('backgroundTile', {
		anchorX: 0.5,
		anchorY: 0.5,
		alpha: 0,
		tint: color,
		scaleX: 20,
		scaleY: 20
	}));
	flashOverlay.x = 0;
	flashOverlay.y = 0;
	tween(flashOverlay, {
		alpha: intensity || 0.3
	}, {
		duration: duration * 0.2,
		easing: tween.easeIn,
		onFinish: function onFinish() {
			tween(flashOverlay, {
				alpha: 0
			}, {
				duration: duration * 0.8,
				easing: tween.easeOut,
				onFinish: function onFinish() {
					flashOverlay.destroy();
				}
			});
		}
	});
}
var screenShake = new ScreenShake();
// Helper function to trigger screen shake with different intensities
function triggerScreenShake(intensity, duration, easing) {
	screenShake.shake(intensity, duration, easing);
}
var hero;
var enemies = [];
var coins = [];
var powerups = [];
var enemyWarnings = [];
var knives = [];
var knivesRemaining = 5;
var routeEffects = [];
var bloodParticles = [];
var bossProjectiles = [];
var impactSparks = [];
var dustClouds = [];
var magicalEffects = [];
var currentDungeon = 1;
var dungeonComplete = false;
var hearts = [];
var healthPotions = [];
var damageNumbers = [];
var weaponTrails = [];
// Object pooling system
var objectPools = {
	bloodParticle: [],
	bossProjectile: [],
	coin: [],
	healthPotion: [],
	impactSpark: [],
	dustCloud: [],
	magicalEffect: [],
	damageNumber: [],
	weaponTrail: []
};
// 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;
	}
};
// Group AI communication system
var groupAI = {
	communications: [],
	// Store recent communications
	alertRadius: 400,
	// Radius for alert propagation
	flankingCoordination: [],
	// Store flanking coordination data
	leaderBuffs: [],
	// Store active leader buffs
	packHunters: [],
	// Track pack hunting groups
	// Communicate alert state between enemies
	broadcastAlert: function broadcastAlert(enemy, alertType, priority) {
		var communication = {
			source: enemy,
			type: alertType,
			// 'spotted_hero', 'under_attack', 'flanking_position', 'retreat'
			priority: priority || 1,
			timestamp: LK.ticks,
			x: enemy.x,
			y: enemy.y
		};
		this.communications.push(communication);
		// Clean old communications (older than 3 seconds)
		for (var i = this.communications.length - 1; i >= 0; i--) {
			if (LK.ticks - this.communications[i].timestamp > 180) {
				this.communications.splice(i, 1);
			}
		}
	},
	// Get recent communications within range
	getCommunications: function getCommunications(enemy, maxAge, maxDistance) {
		var relevant = [];
		maxAge = maxAge || 120; // 2 seconds default
		maxDistance = maxDistance || this.alertRadius;
		for (var i = 0; i < this.communications.length; i++) {
			var comm = this.communications[i];
			if (LK.ticks - comm.timestamp <= maxAge && comm.source !== enemy) {
				var distance = Math.sqrt(Math.pow(enemy.x - comm.x, 2) + Math.pow(enemy.y - comm.y, 2));
				if (distance <= maxDistance) {
					relevant.push(comm);
				}
			}
		}
		return relevant;
	},
	// Calculate optimal flanking positions
	calculateFlankingPositions: function calculateFlankingPositions(heroX, heroY) {
		var positions = [];
		var angles = [Math.PI * 0.25, Math.PI * 0.75, Math.PI * 1.25, Math.PI * 1.75]; // 45, 135, 225, 315 degrees
		var distance = 300;
		for (var i = 0; i < angles.length; i++) {
			var x = heroX + Math.cos(angles[i]) * distance;
			var y = heroY + Math.sin(angles[i]) * distance;
			// Keep within bounds
			x = Math.max(100, Math.min(x, currentLevelData.width - 100));
			y = Math.max(1600, Math.min(y, 2400));
			positions.push({
				x: x,
				y: y,
				angle: angles[i],
				occupied: false
			});
		}
		return positions;
	},
	// Assign flanking position to enemy
	assignFlankingPosition: function assignFlankingPosition(enemy) {
		var positions = this.calculateFlankingPositions(hero.x, hero.y);
		var assigned = null;
		// Find closest unoccupied position
		var minDistance = Infinity;
		for (var i = 0; i < positions.length; i++) {
			if (!positions[i].occupied) {
				var distance = Math.sqrt(Math.pow(enemy.x - positions[i].x, 2) + Math.pow(enemy.y - positions[i].y, 2));
				if (distance < minDistance) {
					minDistance = distance;
					assigned = positions[i];
				}
			}
		}
		if (assigned) {
			assigned.occupied = true;
			enemy.flankingTarget = {
				x: assigned.x,
				y: assigned.y
			};
			enemy.isAssignedFlanker = true;
		}
	}
};
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();
	} else if (type === 'impactSpark') {
		return new ImpactSpark();
	} else if (type === 'dustCloud') {
		return new DustCloud();
	} else if (type === 'magicalEffect') {
		return new MagicalEffect();
	} else if (type === 'damageNumber') {
		return new DamageNumber();
	} else if (type === 'weaponTrail') {
		return new WeaponTrail();
	}
	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
};
// Ensure movement state is properly initialized
function resetMovementState() {
	movementState.left = false;
	movementState.right = false;
	movementState.up = false;
	movementState.down = false;
}
// Initialize clean movement state
resetMovementState();
// Camera system
var camera = {
	x: 0,
	y: 0,
	targetX: 0,
	targetY: 0,
	smoothing: 0.1
};
// Dungeon configuration
var levels = [{
	enemies: [{
		type: 'basic',
		count: 3
	}, {
		type: 'scout',
		count: 1
	}],
	width: 4096,
	height: 2732
}, {
	enemies: [{
		type: 'basic',
		count: 4
	}, {
		type: 'strong',
		count: 2
	}, {
		type: 'scout',
		count: 1
	}, {
		type: 'archer',
		count: 1
	}],
	width: 5120,
	height: 2732
}, {
	enemies: [{
		type: 'basic',
		count: 5
	}, {
		type: 'strong',
		count: 2
	}, {
		type: 'fast',
		count: 2
	}, {
		type: 'scout',
		count: 2
	}, {
		type: 'archer',
		count: 1
	}, {
		type: 'berserker',
		count: 1
	}],
	width: 6144,
	height: 2732
}, {
	enemies: [{
		type: 'basic',
		count: 4
	}, {
		type: 'strong',
		count: 3
	}, {
		type: 'fast',
		count: 2
	}, {
		type: 'tank',
		count: 1
	}, {
		type: 'scout',
		count: 2
	}, {
		type: 'archer',
		count: 2
	}, {
		type: 'berserker',
		count: 1
	}, {
		type: 'shield',
		count: 1
	}],
	width: 7168,
	height: 2732
}, {
	enemies: [{
		type: 'basic',
		count: 6
	}, {
		type: 'strong',
		count: 3
	}, {
		type: 'fast',
		count: 3
	}, {
		type: 'tank',
		count: 2
	}, {
		type: 'hunter',
		count: 2
	}, {
		type: 'scout',
		count: 3
	}, {
		type: 'archer',
		count: 2
	}, {
		type: 'berserker',
		count: 2
	}, {
		type: 'shield',
		count: 1
	}],
	width: 8192,
	height: 2732
}, {
	enemies: [{
		type: 'boss1',
		count: 1
	}],
	width: 9216,
	height: 2732
}, {
	enemies: [{
		type: 'basic',
		count: 4
	}, {
		type: 'strong',
		count: 2
	}, {
		type: 'fast',
		count: 3
	}, {
		type: 'tank',
		count: 2
	}, {
		type: 'hunter',
		count: 2
	}, {
		type: 'assassin',
		count: 2
	}, {
		type: 'leader',
		count: 1
	}, {
		type: 'scout',
		count: 2
	}, {
		type: 'archer',
		count: 3
	}, {
		type: 'berserker',
		count: 2
	}, {
		type: 'shield',
		count: 2
	}, {
		type: 'boss2',
		count: 1
	}],
	width: 10240,
	height: 2732
}, {
	enemies: [{
		type: 'basic',
		count: 3
	}, {
		type: 'strong',
		count: 2
	}, {
		type: 'fast',
		count: 3
	}, {
		type: 'tank',
		count: 2
	}, {
		type: 'hunter',
		count: 3
	}, {
		type: 'assassin',
		count: 3
	}, {
		type: 'leader',
		count: 2
	}, {
		type: 'scout',
		count: 3
	}, {
		type: 'archer',
		count: 4
	}, {
		type: 'berserker',
		count: 3
	}, {
		type: 'shield',
		count: 2
	}, {
		type: 'boss3',
		count: 1
	}, {
		type: 'boss1',
		count: 1
	}],
	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;
// Add upgrades button
showUpgradesButton = LK.getAsset('backgroundTile', {
	anchorX: 0.5,
	anchorY: 0.5,
	tint: 0x9b59b6,
	scaleX: 2,
	scaleY: 0.8
});
showUpgradesButton.x = -150;
showUpgradesButton.y = 350;
LK.gui.topRight.addChild(showUpgradesButton);
var upgradesText = new Text2('UPGRADES', {
	size: 40,
	fill: 0xFFFFFF
});
upgradesText.anchor.set(0.5, 0.5);
upgradesText.x = -150;
upgradesText.y = 350;
LK.gui.topRight.addChild(upgradesText);
// Add coins display
var coinsDisplay = new Text2('Coins: ' + upgradeTree.coins, {
	size: 50,
	fill: 0xF1C40F
});
coinsDisplay.anchor.set(1, 0);
coinsDisplay.x = -50;
coinsDisplay.y = 400;
LK.gui.topRight.addChild(coinsDisplay);
showUpgradesButton.down = function () {
	if (!upgradeMenu.isVisible) {
		upgradeMenu.show(upgradeTree);
		LK.gui.center.addChild(upgradeMenu);
	}
};
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
// Apply upgrade bonuses to hero
hero.maxHealth = 5 + upgradeTree.getMaxHealthBonus();
hero.health = hero.maxHealth;
hero.baseSpeed = 8;
hero.speed = hero.baseSpeed * upgradeTree.getMoveSpeedMultiplier();
hero.damageMultiplier = upgradeTree.getDamageMultiplier();
hero.attackSpeedMultiplier = upgradeTree.getAttackSpeedMultiplier();
hero.damageReduction = upgradeTree.getDamageReduction();
hero.invulnerabilityBonus = upgradeTree.getInvulnerabilityBonus();
// 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');
	if (coinsDisplay) {
		coinsDisplay.setText('Coins: ' + upgradeTree.coins);
	}
}
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 impact sparks
	for (var i = impactSparks.length - 1; i >= 0; i--) {
		impactSparks[i].destroy();
	}
	impactSparks = [];
	// Clear existing dust clouds
	for (var i = dustClouds.length - 1; i >= 0; i--) {
		dustClouds[i].destroy();
	}
	dustClouds = [];
	// Clear existing magical effects
	for (var i = magicalEffects.length - 1; i >= 0; i--) {
		magicalEffects[i].destroy();
	}
	magicalEffects = [];
	// 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 = [];
	// Clear existing damage numbers
	for (var i = damageNumbers.length - 1; i >= 0; i--) {
		damageNumbers[i].destroy();
	}
	damageNumbers = [];
	// Clear existing weapon trails
	for (var i = weaponTrails.length - 1; i >= 0; i--) {
		weaponTrails[i].destroy();
	}
	weaponTrails = [];
	// Clear group AI data
	groupAI.communications = [];
	groupAI.flankingCoordination = [];
	groupAI.leaderBuffs = [];
	groupAI.packHunters = [];
	// Reset knife count with upgrade bonus
	knivesRemaining = 5 + upgradeTree.getStartingKnivesBonus();
	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') {
		// Randomly choose boss type based on dungeon
		var bossTypes = [BossType1, BossType2, BossType3];
		var BossClass = bossTypes[Math.floor(Math.random() * bossTypes.length)];
		enemy = game.addChild(new BossClass());
		// Boss spawns at center of level
		enemy.x = currentLevelData.width / 2;
		enemy.y = 2200;
	} else if (type === 'boss1') {
		enemy = game.addChild(new BossType1());
		enemy.x = currentLevelData.width / 2;
		enemy.y = 2200;
	} else if (type === 'boss2') {
		enemy = game.addChild(new BossType2());
		enemy.x = currentLevelData.width / 2;
		enemy.y = 2200;
	} else if (type === 'boss3') {
		enemy = game.addChild(new BossType3());
		enemy.x = currentLevelData.width / 2;
		enemy.y = 2200;
	} else if (type === 'leader') {
		enemy = game.addChild(new Leader());
		// Leaders spawn in strategic positions
		enemy.x = currentLevelData.width / 2 + (Math.random() - 0.5) * 200;
		enemy.y = 1900 + Math.random() * 200;
	} else if (type === 'scout') {
		enemy = game.addChild(new Scout());
		// Scouts spawn at level edges for early warning
		enemy.x = Math.random() < 0.5 ? 200 + Math.random() * 300 : currentLevelData.width - 500 + Math.random() * 300;
		enemy.y = 1700 + Math.random() * 600;
	} else if (type === 'berserker') {
		enemy = game.addChild(new Berserker());
		// Berserkers spawn closer to center for aggressive positioning
		enemy.x = currentLevelData.width / 2 + (Math.random() - 0.5) * 600;
		enemy.y = 1800 + Math.random() * 400;
	} else if (type === 'archer') {
		enemy = game.addChild(new Archer());
		// Archers spawn at elevated positions (back areas)
		enemy.x = 300 + Math.random() * (currentLevelData.width - 600);
		enemy.y = 1600 + Math.random() * 200; // Higher ground
	} else if (type === 'shield') {
		enemy = game.addChild(new Shield());
		// Shields spawn in protective positions
		enemy.x = currentLevelData.width / 2 + (Math.random() - 0.5) * 400;
		enemy.y = 1850 + Math.random() * 300;
	} 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;
	resetMovementState(); // Reset all movement when any button is released
};
// 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;
	resetMovementState(); // Reset all movement when any button is released
};
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;
	resetMovementState(); // Reset all movement when any button is released
};
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;
	resetMovementState(); // Reset all movement when any button is released
};
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 - only move if button is actively pressed
	if (movementState.left === true) {
		hero.move('left');
		isMovingThisFrame = true;
	}
	if (movementState.right === true) {
		hero.move('right');
		isMovingThisFrame = true;
	}
	if (movementState.up === true) {
		hero.move('up');
		isMovingThisFrame = true;
	}
	if (movementState.down === true) {
		hero.move('down');
		isMovingThisFrame = true;
	}
	// Stop walking animation if no movement this frame
	if (wasMoving && !isMovingThisFrame) {
		hero.stopWalkAnimation();
	}
	// Update screen shake
	screenShake.update();
	// 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 with screen shake offset
	game.x = -camera.x + screenShake.getOffsetX();
	game.y = -camera.y + screenShake.getOffsetY();
	// 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--) {
		// Safety check to ensure coin exists at this index
		if (coins[i] && isVisible(coins[i], 100)) {
			// Check for coin magnetism
			var coin = coins[i];
			var distanceToHero = Math.sqrt(Math.pow(coin.x - hero.x, 2) + Math.pow(coin.y - hero.y, 2));
			var magnetRange = upgradeTree.getCoinMagnetismRange();
			if (distanceToHero < magnetRange) {
				// Move coin toward hero
				var dx = hero.x - coin.x;
				var dy = hero.y - coin.y;
				var distance = Math.sqrt(dx * dx + dy * dy);
				if (distance > 0) {
					var magnetSpeed = 8;
					coin.x += dx / distance * magnetSpeed;
					coin.y += dy / distance * magnetSpeed;
				}
				// Collect if very close
				if (distanceToHero < 50) {
					upgradeTree.addCoins(10);
					coinsDisplay.setText('Coins: ' + upgradeTree.coins);
					coin.collect();
				}
			}
			// Additional safety check before calling update
			if (coins[i]) {
				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 = impactSparks.length - 1; i >= 0; i--) {
		if (isVisible(impactSparks[i], 50)) {
			impactSparks[i].update();
		}
	}
	for (var i = dustClouds.length - 1; i >= 0; i--) {
		if (isVisible(dustClouds[i], 100)) {
			dustClouds[i].update();
		}
	}
	for (var i = magicalEffects.length - 1; i >= 0; i--) {
		if (isVisible(magicalEffects[i], 100)) {
			magicalEffects[i].update();
		}
	}
	for (var i = bossProjectiles.length - 1; i >= 0; i--) {
		var projectile = bossProjectiles[i];
		// Check if any shield enemy can block this projectile
		var blocked = false;
		for (var j = 0; j < enemies.length; j++) {
			var enemy = enemies[j];
			if (enemy.enemyType === 'shield' && enemy.blockProjectile) {
				if (enemy.blockProjectile(projectile)) {
					blocked = true;
					break;
				}
			}
		}
		// Only update projectile if it wasn't blocked
		if (!blocked) {
			projectile.update(); // Always update projectiles as they move fast
		}
	}
	// Update coins display to reflect automatic coin collection
	updateScoreDisplay();
	for (var i = damageNumbers.length - 1; i >= 0; i--) {
		if (isVisible(damageNumbers[i], 100)) {
			damageNumbers[i].update();
		}
	}
	for (var i = weaponTrails.length - 1; i >= 0; i--) {
		weaponTrails[i].update(); // Always update trails for smooth effect
	}
	// 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();
	}
	// Special check for dungeon 7 boss - kill boss when all soldiers are dead
	if (currentDungeon === 7 && !dungeonComplete && enemies.length > 0) {
		var bossCount = 0;
		var summonerBoss = null;
		var otherEnemyCount = 0;
		// Count bosses and other enemies
		for (var i = 0; i < enemies.length; i++) {
			var enemy = enemies[i];
			if (enemy.bossType === 'summoner') {
				bossCount++;
				summonerBoss = enemy;
				// Reduce boss health specifically for dungeon 7
				if (summonerBoss.health > 25) {
					summonerBoss.health = 25;
					summonerBoss.maxHealth = 25;
				}
			} else {
				otherEnemyCount++;
			}
		}
		// If only summoner boss remains (all soldiers dead), kill the boss
		if (bossCount === 1 && otherEnemyCount === 0 && summonerBoss) {
			summonerBoss.die();
		}
	}
	// 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");
var storage = LK.import("@upit/storage.v1");
/**** 
* Classes
****/ 
var ArcherProjectile = Container.expand(function () {
	var self = Container.call(this);
	var projectileGraphics = self.attachAsset('bossProjectile', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	projectileGraphics.tint = 0x8844ff; // Purple arrows
	projectileGraphics.scaleX = 0.8;
	projectileGraphics.scaleY = 0.8;
	self.velocityX = 0;
	self.velocityY = 0;
	self.lifetime = 240; // 4 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 < 60) {
			hero.takeDamage();
			self.destroy();
			// Remove from projectiles 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 projectiles array
			for (var i = bossProjectiles.length - 1; i >= 0; i--) {
				if (bossProjectiles[i] === self) {
					bossProjectiles.splice(i, 1);
					break;
				}
			}
		}
	};
	return self;
});
// Base Boss class with common functionality
var BaseBoss = Container.expand(function () {
	var self = Container.call(this);
	self.health = 50;
	self.maxHealth = 50;
	self.speed = 3;
	self.lastX = 0;
	self.lastY = 0;
	self.state = 'IDLE'; // AI states: IDLE, PURSUING, ATTACKING, STUNNED, ENRAGED
	self.stateTimer = 0;
	self.attackCooldown = 0;
	self.isInvulnerable = false;
	self.bossType = 'base';
	self.phase = 1;
	self.predictedHeroX = 0;
	self.predictedHeroY = 0;
	// Predictive targeting system
	self.updatePrediction = function () {
		var heroVelocityX = hero.x - hero.lastX;
		var heroVelocityY = hero.y - hero.lastY;
		var predictionFrames = 30; // Predict 0.5 seconds ahead
		self.predictedHeroX = hero.x + heroVelocityX * predictionFrames;
		self.predictedHeroY = hero.y + heroVelocityY * predictionFrames;
	};
	// State machine update
	self.updateState = function () {
		var distanceToHero = Math.sqrt(Math.pow(self.x - hero.x, 2) + Math.pow(self.y - hero.y, 2));
		self.stateTimer++;
		switch (self.state) {
			case 'IDLE':
				if (distanceToHero < 600) {
					self.setState('PURSUING');
				}
				break;
			case 'PURSUING':
				if (distanceToHero < 200) {
					self.setState('ATTACKING');
				} else if (distanceToHero > 800) {
					self.setState('IDLE');
				}
				break;
			case 'ATTACKING':
				if (distanceToHero > 300) {
					self.setState('PURSUING');
				}
				break;
			case 'STUNNED':
				if (self.stateTimer > 120) {
					// 2 seconds
					self.setState('ENRAGED');
				}
				break;
			case 'ENRAGED':
				if (self.health > self.maxHealth * 0.5) {
					self.setState('PURSUING');
				}
				break;
		}
	};
	self.setState = function (newState) {
		self.state = newState;
		self.stateTimer = 0;
		self.onStateChange(newState);
	};
	// Override for BossType1 specific state changes
	self.onStateChange = function (newState) {
		var graphics = self.getChildAt(0);
		if (newState === 'ENRAGED') {
			graphics.tint = 0xff4444;
			self.speed *= 2.0; // More aggressive speed boost
			// Add particle effect for enrage
			for (var i = 0; i < 8; i++) {
				var particle = game.addChild(getFromPool('bloodParticle'));
				particle.reset(self.x + (Math.random() - 0.5) * 100, self.y - 150 + (Math.random() - 0.5) * 100);
				var particleGraphics = particle.getChildAt(0);
				particleGraphics.tint = 0xff4444;
				bloodParticles.push(particle);
			}
		} else if (newState === 'STUNNED') {
			graphics.tint = 0x888888;
			self.speed *= 0.3; // More pronounced slowdown
			// Vulnerability window - takes extra damage when stunned
			self.isInvulnerable = false;
		} else if (newState === 'ATTACKING') {
			graphics.tint = 0xffaa00; // Orange tint when attacking
		} else {
			graphics.tint = 0xffffff;
		}
	};
	self.takeDamage = function (fromKnife) {
		if (self.isInvulnerable) return;
		var damage = 1;
		var isCritical = Math.random() < 0.2; // 20% critical chance on bosses
		var isVulnerable = self.state === 'STUNNED';
		if (isVulnerable) {
			damage *= 2; // Double damage when stunned
		}
		if (isCritical) {
			damage += 1; // Extra damage for critical
		}
		self.health -= damage;
		// Create damage number
		var damageNumber = game.addChild(getFromPool('damageNumber'));
		damageNumber.reset(self.x + (Math.random() - 0.5) * 60, self.y - 150, damage, isCritical || isVulnerable);
		damageNumbers.push(damageNumber);
		// Screen flash for critical hits on bosses
		if (isCritical) {
			createScreenFlash(0xFFD700, 400, 0.2); // Gold flash for critical
		}
		LK.effects.flashObject(self, isCritical ? 0xFFD700 : 0xffffff, isCritical ? 600 : 200);
		// Create enhanced blood particles with variety
		for (var p = 0; p < 10; p++) {
			var bloodParticle = game.addChild(getFromPool('bloodParticle'));
			var particleType = Math.random() < 0.3 ? 'large' : Math.random() < 0.5 ? 'small' : 'normal';
			bloodParticle.reset(self.x + (Math.random() - 0.5) * 60, self.y - 100 + (Math.random() - 0.5) * 60, particleType);
			bloodParticles.push(bloodParticle);
		}
		// Add spray particles for dramatic effect
		for (var p = 0; p < 8; p++) {
			var sprayParticle = game.addChild(getFromPool('bloodParticle'));
			sprayParticle.reset(self.x + (Math.random() - 0.5) * 40, self.y - 120 + (Math.random() - 0.5) * 40, 'spray');
			bloodParticles.push(sprayParticle);
		}
		// State transitions based on damage
		if (self.health <= self.maxHealth * 0.25 && self.state !== 'ENRAGED') {
			self.setState('ENRAGED');
		} else if (fromKnife && Math.random() < 0.3) {
			self.setState('STUNNED');
		}
		if (self.health <= 0) {
			self.die();
		}
	};
	self.die = function () {
		LK.effects.flashScreen(0xffffff, 2000);
		// Determine coin reward based on boss type
		var coinReward = 50; // Default boss reward
		if (self.bossType === 'melee') {
			coinReward = 50;
		} else if (self.bossType === 'summoner') {
			coinReward = 75;
		} else if (self.bossType === 'environmental') {
			coinReward = 100;
		}
		// Automatically give coins to upgrade tree
		upgradeTree.addCoins(coinReward);
		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);
		}
		LK.setScore(LK.getScore() + 1000);
		hero.addCombo();
		for (var i = enemies.length - 1; i >= 0; i--) {
			if (enemies[i] === self) {
				enemies.splice(i, 1);
				break;
			}
		}
		self.destroy();
		updateScoreDisplay();
		updateEnemiesLeftDisplay();
	};
	return self;
});
// BossType3: Environmental Manipulator
var BossType3 = BaseBoss.expand(function () {
	var self = BaseBoss.call(this);
	var bossGraphics = self.attachAsset('boss', {
		anchorX: 0.5,
		anchorY: 1.0
	});
	bossGraphics.tint = 0x00ff88; // Green for environmental
	self.bossType = 'environmental';
	self.hazardCooldown = 0;
	self.wallCooldown = 0;
	self.earthquakeCooldown = 0;
	self.activeHazards = [];
	self.isChanneling = false;
	self.update = function () {
		self.updatePrediction();
		self.updateState();
		var distanceToHero = Math.sqrt(Math.pow(self.x - hero.x, 2) + Math.pow(self.y - hero.y, 2));
		// Phase transitions
		if (self.health > self.maxHealth * 0.66) {
			self.phase = 1;
		} else if (self.health > self.maxHealth * 0.33) {
			self.phase = 2;
		} else {
			self.phase = 3;
		}
		// Environmental boss positioning based on state
		if (self.state === 'PURSUING' || self.state === 'ATTACKING' || self.state === 'ENRAGED') {
			// Move to strategic positions based on predicted hero movement
			var strategicX = self.state === 'ENRAGED' ? currentLevelData.width / 2 + (self.predictedHeroX > currentLevelData.width / 2 ? -300 : 300) : currentLevelData.width / 2;
			var strategicY = 2000;
			var dx = strategicX - self.x;
			var dy = strategicY - self.y;
			var distance = Math.sqrt(dx * dx + dy * dy);
			if (distance > 50) {
				self.lastX = self.x;
				self.lastY = self.y;
				var moveSpeed = self.state === 'ENRAGED' ? self.speed : self.speed * 0.5;
				self.x += dx / distance * moveSpeed;
				self.y += dy / distance * moveSpeed;
			}
		}
		// Enhanced environmental attacks with state-based timing
		if (self.state === 'ATTACKING' && self.hazardCooldown <= 0) {
			self.showHazardWarning();
		}
		if (self.phase >= 2 && self.state === 'ATTACKING' && self.wallCooldown <= 0) {
			self.showWallWarning();
		}
		if (self.phase === 3 && self.state === 'ENRAGED' && self.earthquakeCooldown <= 0) {
			self.showEarthquakeWarning();
		}
		// Cooldown updates
		if (self.hazardCooldown > 0) self.hazardCooldown--;
		if (self.wallCooldown > 0) self.wallCooldown--;
		if (self.earthquakeCooldown > 0) self.earthquakeCooldown--;
	};
	self.createHazard = function () {
		self.isChanneling = true;
		tween(bossGraphics, {
			tint: 0xff4400
		}, {
			duration: 300
		});
		// Create fire hazard near predicted hero position
		var hazard = game.addChild(LK.getAsset('hitEffect', {
			anchorX: 0.5,
			anchorY: 0.5,
			alpha: 0
		}));
		hazard.x = self.predictedHeroX + (Math.random() - 0.5) * 100;
		hazard.y = self.predictedHeroY + (Math.random() - 0.5) * 100;
		// Warning phase
		tween(hazard, {
			alpha: 0.5,
			scaleX: 2,
			scaleY: 2
		}, {
			duration: 1000
		});
		// Damage phase
		LK.setTimeout(function () {
			tween(hazard, {
				alpha: 1,
				scaleX: 3,
				scaleY: 3,
				tint: 0xff0000
			}, {
				duration: 500,
				onFinish: function onFinish() {
					// Check if hero is in hazard area
					var dx = hero.x - hazard.x;
					var dy = hero.y - hazard.y;
					var distance = Math.sqrt(dx * dx + dy * dy);
					if (distance < 100) {
						hero.takeDamage();
					}
					// Remove hazard
					hazard.destroy();
					for (var i = self.activeHazards.length - 1; i >= 0; i--) {
						if (self.activeHazards[i] === hazard) {
							self.activeHazards.splice(i, 1);
							break;
						}
					}
				}
			});
		}, 1000);
		self.activeHazards.push(hazard);
		tween(bossGraphics, {
			tint: 0x00ff88
		}, {
			duration: 300
		});
		self.isChanneling = false;
		self.hazardCooldown = self.phase === 3 ? 120 : 180;
	};
	self.createWalls = function () {
		// Create temporary walls that block movement
		for (var i = 0; i < 3; i++) {
			var wall = game.addChild(LK.getAsset('backgroundTile', {
				anchorX: 0.5,
				anchorY: 0.5,
				alpha: 0,
				tint: 0x666666
			}));
			wall.x = 300 + i * 400;
			wall.y = 1800 + Math.random() * 400;
			tween(wall, {
				alpha: 0.8,
				scaleY: 3
			}, {
				duration: 500
			});
			// Remove walls after duration
			LK.setTimeout(function () {
				tween(wall, {
					alpha: 0,
					scaleY: 0.1
				}, {
					duration: 500,
					onFinish: function onFinish() {
						wall.destroy();
					}
				});
			}, 5000);
		}
		self.wallCooldown = 420; // 7 seconds
	};
	self.showHazardWarning = function () {
		// Create multiple warning areas using predictive targeting
		var warningAreas = [{
			x: self.predictedHeroX,
			y: self.predictedHeroY
		}, {
			x: hero.x,
			y: hero.y
		},
		// Current position as backup
		{
			x: self.predictedHeroX + (Math.random() - 0.5) * 200,
			y: self.predictedHeroY + (Math.random() - 0.5) * 200
		}];
		for (var i = 0; i < warningAreas.length; i++) {
			var warning = game.addChild(LK.getAsset('hitEffect', {
				anchorX: 0.5,
				anchorY: 0.5,
				alpha: 0,
				tint: 0xff4400
			}));
			warning.x = warningAreas[i].x;
			warning.y = warningAreas[i].y;
			tween(warning, {
				alpha: 0.6,
				scaleX: 2.5,
				scaleY: 2.5
			}, {
				duration: 1500,
				onFinish: function onFinish() {
					warning.destroy();
				}
			});
		}
		// Boss visual warning
		tween(bossGraphics, {
			tint: 0xff4400,
			scaleX: 1.2,
			scaleY: 1.2
		}, {
			duration: 500,
			onFinish: function onFinish() {
				self.createHazard();
				tween(bossGraphics, {
					tint: 0x00ff88,
					scaleX: 1,
					scaleY: 1
				}, {
					duration: 300
				});
			}
		});
	};
	self.showWallWarning = function () {
		// Show where walls will appear
		var wallPositions = [{
			x: 300,
			y: 1800
		}, {
			x: 700,
			y: 2000
		}, {
			x: 1100,
			y: 1900
		}];
		for (var i = 0; i < wallPositions.length; i++) {
			var warning = game.addChild(LK.getAsset('backgroundTile', {
				anchorX: 0.5,
				anchorY: 0.5,
				alpha: 0,
				tint: 0xffaa00,
				scaleY: 0.1
			}));
			warning.x = wallPositions[i].x;
			warning.y = wallPositions[i].y;
			tween(warning, {
				alpha: 0.7,
				scaleY: 2
			}, {
				duration: 1000,
				onFinish: function onFinish() {
					warning.destroy();
				}
			});
		}
		LK.setTimeout(function () {
			self.createWalls();
		}, 1000);
	};
	self.showEarthquakeWarning = function () {
		// Screen warning for earthquake
		LK.effects.flashScreen(0x8b4513, 1500);
		// Boss charges up
		tween(bossGraphics, {
			tint: 0x8b4513,
			scaleX: 1.5,
			scaleY: 1.5
		}, {
			duration: 1500,
			onFinish: function onFinish() {
				self.earthquake();
			}
		});
	};
	self.earthquake = function () {
		self.isChanneling = true;
		// Create vulnerability window - boss can't move during earthquake
		self.speed = 0;
		LK.effects.flashScreen(0x8b4513, 2000);
		// Intense screen shake for earthquake
		triggerScreenShake(25, 1500, tween.easeInOut);
		// Screen shake effect simulation through rapid position changes
		var originalX = bossGraphics.x;
		var originalY = bossGraphics.y;
		for (var i = 0; i < 20; i++) {
			LK.setTimeout(function () {
				bossGraphics.x = originalX + (Math.random() - 0.5) * 10;
				bossGraphics.y = originalY + (Math.random() - 0.5) * 10;
			}, i * 50);
		}
		LK.setTimeout(function () {
			bossGraphics.x = originalX;
			bossGraphics.y = originalY;
			self.isChanneling = false;
			// Restore speed and brief stun
			self.speed = 3;
			self.setState('STUNNED');
			// Reset visual
			tween(bossGraphics, {
				tint: 0x00ff88,
				scaleX: 1,
				scaleY: 1
			}, {
				duration: 500
			});
		}, 1000);
		// Damage hero if on ground
		LK.setTimeout(function () {
			if (hero.y > 2200) {
				// Near ground level
				hero.takeDamage();
			}
		}, 1000);
		self.earthquakeCooldown = 600; // 10 seconds
	};
	return self;
});
// Legacy Boss class for backward compatibility (delegates to BossType1)
// BossType2: Summoner Boss
var BossType2 = BaseBoss.expand(function () {
	var self = BaseBoss.call(this);
	var bossGraphics = self.attachAsset('boss', {
		anchorX: 0.5,
		anchorY: 1.0
	});
	bossGraphics.tint = 0x9b59b6; // Purple for summoner
	self.bossType = 'summoner';
	self.summonCooldown = 0;
	self.minionCount = 0;
	self.maxMinions = 4;
	self.teleportCooldown = 0;
	self.shieldActive = false;
	self.shieldHealth = 0;
	self.update = function () {
		self.updatePrediction();
		self.updateState();
		var distanceToHero = Math.sqrt(Math.pow(self.x - hero.x, 2) + Math.pow(self.y - hero.y, 2));
		// Phase transitions affect minion count
		if (self.health > self.maxHealth * 0.66) {
			self.phase = 1;
			self.maxMinions = 2;
		} else if (self.health > self.maxHealth * 0.33) {
			self.phase = 2;
			self.maxMinions = 3;
		} else {
			self.phase = 3;
			self.maxMinions = 4;
		}
		// Enhanced movement based on state with predictive positioning
		if (self.state === 'PURSUING' || self.state === 'ATTACKING' || self.state === 'ENRAGED') {
			var optimalDistance = self.state === 'ENRAGED' ? 300 : 450;
			if (distanceToHero < optimalDistance) {
				// Move away from hero, but anticipate their movement
				var escapeX = self.x - self.predictedHeroX;
				var escapeY = self.y - self.predictedHeroY;
				var distance = Math.sqrt(escapeX * escapeX + escapeY * escapeY);
				if (distance > 0) {
					self.lastX = self.x;
					self.lastY = self.y;
					var moveSpeed = self.state === 'ENRAGED' ? self.speed * 1.5 : self.speed;
					self.x += escapeX / distance * moveSpeed;
					self.y += escapeY / distance * moveSpeed;
					bossGraphics.scaleX = escapeX > 0 ? -1 : 1; // Face away from hero
				}
			}
		}
		// Enhanced summoning with warnings
		if (self.state === 'ATTACKING' && self.summonCooldown <= 0 && self.minionCount < self.maxMinions) {
			self.showSummonWarning();
		}
		// Predictive teleport when threatened
		if (self.phase >= 2 && self.teleportCooldown <= 0 && distanceToHero < 250) {
			self.showTeleportWarning();
		}
		// Shield ability in phase 3
		if (self.phase === 3 && !self.shieldActive && self.health <= self.maxHealth * 0.25) {
			self.activateShield();
		}
		// Cooldown updates
		if (self.summonCooldown > 0) self.summonCooldown--;
		if (self.teleportCooldown > 0) self.teleportCooldown--;
	};
	self.summonMinion = function () {
		// Create a specialized minion near the boss
		var minion = game.addChild(new Minion());
		minion.x = self.x + (Math.random() - 0.5) * 200;
		minion.y = self.y + (Math.random() - 0.5) * 100;
		minion.summoner = self; // Link minion to summoner
		// Keep within bounds
		minion.x = Math.max(100, Math.min(minion.x, currentLevelData.width - 100));
		minion.y = Math.max(1600, Math.min(minion.y, 2400));
		// Visual summoning effect
		tween(bossGraphics, {
			tint: 0xff00ff
		}, {
			duration: 200
		});
		tween(bossGraphics, {
			tint: 0x9b59b6
		}, {
			duration: 200
		});
		LK.effects.flashObject(minion, 0x9b59b6, 500);
		enemies.push(minion);
		self.minionCount++;
		self.summonCooldown = self.phase === 3 ? 180 : 240; // Faster summoning in phase 3
		updateEnemiesLeftDisplay();
	};
	self.teleport = function () {
		// Teleport to a safe distance from hero
		var teleportDistance = 500;
		var angle = Math.random() * Math.PI * 2;
		var newX = hero.x + Math.cos(angle) * teleportDistance;
		var newY = hero.y + Math.sin(angle) * teleportDistance;
		// Keep within bounds
		newX = Math.max(200, Math.min(newX, currentLevelData.width - 200));
		newY = Math.max(1700, Math.min(newY, 2300));
		// Visual teleport effect
		tween(bossGraphics, {
			alpha: 0,
			scaleX: 0.1,
			scaleY: 0.1
		}, {
			duration: 300,
			onFinish: function onFinish() {
				self.x = newX;
				self.y = newY;
				tween(bossGraphics, {
					alpha: 1,
					scaleX: 1,
					scaleY: 1
				}, {
					duration: 300
				});
				LK.effects.flashObject(self, 0x9b59b6, 500);
			}
		});
		self.teleportCooldown = 300; // 5 seconds
	};
	self.activateShield = function () {
		self.shieldActive = true;
		self.shieldHealth = 10;
		self.isInvulnerable = true;
		// Visual shield effect
		tween(bossGraphics, {
			tint: 0x00ffff
		}, {
			duration: 200
		});
		LK.effects.flashObject(self, 0x00ffff, 1000);
		// Shield lasts until destroyed or timeout
		LK.setTimeout(function () {
			self.deactivateShield();
		}, 10000); // 10 seconds max
	};
	self.showSummonWarning = function () {
		// Create warning circles at summon locations
		var summonLocations = [{
			x: self.x + 150,
			y: self.y
		}, {
			x: self.x - 150,
			y: self.y
		}, {
			x: self.x,
			y: self.y + 100
		}];
		for (var i = 0; i < summonLocations.length; i++) {
			var warning = game.addChild(LK.getAsset('hitEffect', {
				anchorX: 0.5,
				anchorY: 0.5,
				alpha: 0,
				tint: 0x9b59b6
			}));
			warning.x = summonLocations[i].x;
			warning.y = summonLocations[i].y;
			tween(warning, {
				alpha: 0.7,
				scaleX: 1.5,
				scaleY: 1.5
			}, {
				duration: 1000,
				onFinish: function onFinish() {
					warning.destroy();
				}
			});
		}
		// Delay actual summon
		LK.setTimeout(function () {
			if (self.minionCount < self.maxMinions) {
				self.summonMinion();
			}
		}, 1000);
	};
	self.showTeleportWarning = function () {
		// Flash before teleporting
		tween(bossGraphics, {
			alpha: 0.3,
			tint: 0xff00ff
		}, {
			duration: 300,
			onFinish: function onFinish() {
				self.teleport();
			}
		});
	};
	self.deactivateShield = function () {
		self.shieldActive = false;
		self.isInvulnerable = false;
		// Vulnerability window after shield breaks
		self.setState('STUNNED');
		tween(bossGraphics, {
			tint: 0x9b59b6
		}, {
			duration: 500
		});
	};
	// Override takeDamage to handle shield
	var originalTakeDamage = self.takeDamage;
	self.takeDamage = function (fromKnife) {
		if (self.shieldActive) {
			self.shieldHealth--;
			LK.effects.flashObject(self, 0x00ffff, 100);
			if (self.shieldHealth <= 0) {
				self.deactivateShield();
			}
			return;
		}
		// Reduce minion count when boss takes damage
		if (self.minionCount > 0) {
			self.minionCount--;
		}
		originalTakeDamage.call(self, fromKnife);
	};
	return self;
});
// BossType1: Melee/Projectile Boss (original boss enhanced)
var BossType1 = BaseBoss.expand(function () {
	var self = BaseBoss.call(this);
	var bossGraphics = self.attachAsset('boss', {
		anchorX: 0.5,
		anchorY: 1.0
	});
	self.bossType = 'melee';
	self.projectileAttackCooldown = 0;
	self.chargeAttackCooldown = 0;
	self.isCharging = false;
	self.chargingTarget = null;
	self.spinAttackCooldown = 0;
	self.isSpinning = false;
	self.update = function () {
		self.updatePrediction();
		self.updateState();
		var distanceToHero = Math.sqrt(Math.pow(self.x - hero.x, 2) + Math.pow(self.y - hero.y, 2));
		// Phase transitions
		if (self.health > self.maxHealth * 0.66) {
			self.phase = 1;
		} else if (self.health > self.maxHealth * 0.33) {
			self.phase = 2;
		} else {
			self.phase = 3;
		}
		// Movement based on state
		if (self.state === 'PURSUING' || self.state === 'ENRAGED') {
			var targetX = self.state === 'ENRAGED' ? self.predictedHeroX : hero.x;
			var targetY = self.state === 'ENRAGED' ? self.predictedHeroY : hero.y;
			var dx = targetX - self.x;
			var dy = targetY - self.y;
			var distance = Math.sqrt(dx * dx + dy * dy);
			if (distance > 0) {
				self.lastX = self.x;
				self.lastY = self.y;
				var moveSpeed = self.speed;
				// Enraged state moves faster toward predicted position
				if (self.state === 'ENRAGED') {
					moveSpeed *= 1.8;
				}
				self.x += dx / distance * moveSpeed;
				self.y += dy / distance * moveSpeed;
				bossGraphics.scaleX = dx > 0 ? 1 : -1;
			}
		}
		// Attacks based on state with visual warnings
		if (self.state === 'ATTACKING' && self.attackCooldown <= 0) {
			if (distanceToHero < 150) {
				self.showMeleeWarning();
			} else if (self.phase >= 2 && self.projectileAttackCooldown <= 0) {
				self.showProjectileWarning();
			}
		}
		// Special attacks with warnings
		if (self.phase >= 2 && self.chargeAttackCooldown <= 0 && distanceToHero > 300 && !self.isCharging) {
			self.showChargeWarning();
		}
		if (self.phase === 3 && self.spinAttackCooldown <= 0 && distanceToHero < 250 && !self.isSpinning) {
			self.showSpinWarning();
		}
		// Cooldown updates
		if (self.attackCooldown > 0) self.attackCooldown--;
		if (self.projectileAttackCooldown > 0) self.projectileAttackCooldown--;
		if (self.chargeAttackCooldown > 0) self.chargeAttackCooldown--;
		if (self.spinAttackCooldown > 0) self.spinAttackCooldown--;
	};
	self.meleeAttack = function () {
		hero.takeDamage();
		self.attackCooldown = 90;
		LK.effects.flashObject(self, 0xffffff, 200);
		// Screen shake on boss melee attack
		triggerScreenShake(15, 300, tween.easeOut);
	};
	self.fireProjectile = function () {
		tween(bossGraphics, {
			tint: 0x8800ff
		}, {
			duration: 200
		});
		tween(bossGraphics, {
			tint: 0xffffff
		}, {
			duration: 200
		});
		// Screen shake on boss projectile attack
		triggerScreenShake(8, 200, tween.easeOut);
		var projectile = game.addChild(new BossProjectile());
		projectile.x = self.x;
		projectile.y = self.y - 200;
		var dx = self.predictedHeroX - self.x;
		var dy = self.predictedHeroY - self.y;
		var distance = Math.sqrt(dx * dx + dy * dy);
		if (distance > 0) {
			var speed = self.phase === 3 ? 12 : 8;
			projectile.velocityX = dx / distance * speed;
			projectile.velocityY = dy / distance * speed;
		}
		bossProjectiles.push(projectile);
		self.projectileAttackCooldown = self.phase === 3 ? 60 : 120;
	};
	self.chargeAttack = function () {
		self.isCharging = true;
		self.chargingTarget = {
			x: self.predictedHeroX,
			y: self.predictedHeroY
		};
		tween(bossGraphics, {
			tint: 0xff4444,
			scaleX: 1.2,
			scaleY: 1.2
		}, {
			duration: 800,
			easing: tween.easeIn
		});
		LK.setTimeout(function () {
			if (!self.chargingTarget) return;
			LK.effects.flashObject(self, 0xffffff, 300);
			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) {
				var chargeDistance = 400;
				var targetX = self.x + dx / distance * chargeDistance;
				var targetY = self.y + dy / distance * chargeDistance;
				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() {
						tween(bossGraphics, {
							tint: 0xffffff,
							scaleX: 1,
							scaleY: 1
						}, {
							duration: 500
						});
						self.isCharging = false;
						self.chargingTarget = null;
					}
				});
				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();
						// Screen shake on successful charge attack hit
						triggerScreenShake(20, 400, tween.easeOut);
					}
				}, 150);
			}
		}, 800);
		self.chargeAttackCooldown = self.phase === 3 ? 240 : 360;
	};
	self.showMeleeWarning = function () {
		// Flash red warning for 0.5 seconds before attacking
		tween(bossGraphics, {
			tint: 0xff0000,
			scaleX: bossGraphics.scaleX * 1.3,
			scaleY: 1.3
		}, {
			duration: 500,
			onFinish: function onFinish() {
				self.meleeAttack();
				tween(bossGraphics, {
					tint: 0xffffff,
					scaleX: bossGraphics.scaleX > 0 ? 1 : -1,
					scaleY: 1
				}, {
					duration: 200
				});
			}
		});
	};
	self.showProjectileWarning = function () {
		// Create warning indicator at predicted position
		var warningIndicator = game.addChild(LK.getAsset('hitEffect', {
			anchorX: 0.5,
			anchorY: 0.5,
			alpha: 0,
			tint: 0xff4444
		}));
		warningIndicator.x = self.predictedHeroX;
		warningIndicator.y = self.predictedHeroY;
		tween(warningIndicator, {
			alpha: 0.8,
			scaleX: 2,
			scaleY: 2
		}, {
			duration: 800,
			onFinish: function onFinish() {
				self.fireProjectile();
				warningIndicator.destroy();
			}
		});
	};
	self.showChargeWarning = function () {
		// Show charge direction warning
		var warningLine = game.addChild(LK.getAsset('slashEffect', {
			anchorX: 0,
			anchorY: 0.5,
			alpha: 0,
			tint: 0xff4444
		}));
		var dx = self.predictedHeroX - self.x;
		var dy = self.predictedHeroY - self.y;
		var angle = Math.atan2(dy, dx);
		warningLine.x = self.x;
		warningLine.y = self.y - 100;
		warningLine.rotation = angle;
		warningLine.scaleX = 8;
		warningLine.scaleY = 2;
		tween(warningLine, {
			alpha: 0.7
		}, {
			duration: 600,
			onFinish: function onFinish() {
				self.chargeAttack();
				warningLine.destroy();
			}
		});
	};
	self.showSpinWarning = function () {
		// Create expanding red circle warning
		var warningCircle = game.addChild(LK.getAsset('hitEffect', {
			anchorX: 0.5,
			anchorY: 0.5,
			alpha: 0,
			tint: 0xff0000
		}));
		warningCircle.x = self.x;
		warningCircle.y = self.y - 100;
		tween(warningCircle, {
			alpha: 0.6,
			scaleX: 4,
			scaleY: 4
		}, {
			duration: 1000,
			onFinish: function onFinish() {
				self.spinAttack();
				warningCircle.destroy();
			}
		});
	};
	self.spinAttack = function () {
		self.isSpinning = true;
		// Create vulnerability window - boss takes double damage during spin
		self.isInvulnerable = false;
		var originalTakeDamage = self.takeDamage;
		self.takeDamage = function (fromKnife) {
			// Double damage during spin attack
			originalTakeDamage.call(self, fromKnife);
			if (self.health > 0) {
				originalTakeDamage.call(self, fromKnife);
			}
		};
		tween(bossGraphics, {
			rotation: Math.PI * 4
		}, {
			duration: 1500,
			easing: tween.easeInOut,
			onFinish: function onFinish() {
				bossGraphics.rotation = 0;
				self.isSpinning = false;
				// Restore normal damage after spin
				self.takeDamage = originalTakeDamage;
				// Brief stunned state after spin
				self.setState('STUNNED');
			}
		});
		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
		});
		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);
					// Screen shake on spin attack hit
					triggerScreenShake(12, 250, tween.easeOut);
				}
			}, i * 300);
		}
		self.spinAttackCooldown = 420;
	};
	return self;
});
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.particleType = 'normal'; // normal, large, small, spray
	self.reset = function (startX, startY, type) {
		self.x = startX;
		self.y = startY;
		self.particleType = type || 'normal';
		// Different sizes, speeds, and lifetimes based on type
		if (self.particleType === 'large') {
			self.velocityX = (Math.random() - 0.5) * 6;
			self.velocityY = -Math.random() * 4 - 1;
			self.lifetime = 90; // Larger particles last longer
			particleGraphics.scaleX = 1.5;
			particleGraphics.scaleY = 1.5;
			particleGraphics.tint = 0x8b0000; // Dark red
		} else if (self.particleType === 'small') {
			self.velocityX = (Math.random() - 0.5) * 12; // Faster small particles
			self.velocityY = -Math.random() * 8 - 3;
			self.lifetime = 30; // Short-lived
			particleGraphics.scaleX = 0.5;
			particleGraphics.scaleY = 0.5;
			particleGraphics.tint = 0xff4444; // Bright red
		} else if (self.particleType === 'spray') {
			self.velocityX = (Math.random() - 0.5) * 16; // Very fast spray
			self.velocityY = -Math.random() * 10 - 1;
			self.lifetime = 45;
			particleGraphics.scaleX = 0.7;
			particleGraphics.scaleY = 0.7;
			particleGraphics.tint = 0xcc2222; // Medium red
		} else {
			// Normal particles
			self.velocityX = (Math.random() - 0.5) * 8;
			self.velocityY = -Math.random() * 6 - 2;
			self.lifetime = 60;
			particleGraphics.scaleX = 1;
			particleGraphics.scaleY = 1;
			particleGraphics.tint = 0x8b0000;
		}
		particleGraphics.alpha = 1;
		self.visible = true;
	};
	self.update = function () {
		self.x += self.velocityX;
		self.y += self.velocityY;
		self.velocityY += self.gravity;
		// Different fade patterns based on type
		var maxLifetime = 60;
		if (self.particleType === 'large') maxLifetime = 90;else if (self.particleType === 'small') maxLifetime = 30;else if (self.particleType === 'spray') maxLifetime = 45;
		// Fade out over time
		self.lifetime--;
		particleGraphics.alpha = self.lifetime / maxLifetime;
		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 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 DamageNumber = Container.expand(function () {
	var self = Container.call(this);
	var damageText = new Text2('', {
		size: 60,
		fill: 0xFFFFFF
	});
	damageText.anchor.set(0.5, 0.5);
	self.addChild(damageText);
	self.lifetime = 120; // 2 seconds at 60fps
	self.velocityY = -3; // Float upward
	self.fadeSpeed = 1 / 120; // Fade over lifetime
	self.reset = function (startX, startY, damage, isCritical) {
		self.x = startX;
		self.y = startY;
		self.lifetime = 120;
		self.velocityY = isCritical ? -4 : -3; // Faster rise for critical hits
		damageText.setText(damage.toString());
		damageText.alpha = 1;
		self.visible = true;
		// Different styling for critical hits
		if (isCritical) {
			damageText.fill = 0xFFD700; // Gold for critical
			damageText.size = 80; // Larger text
			tween(self, {
				scaleX: 1.3,
				scaleY: 1.3
			}, {
				duration: 200,
				easing: tween.easeOut
			});
		} else {
			damageText.fill = 0xFFFFFF; // White for normal
			damageText.size = 60;
			self.scaleX = 1;
			self.scaleY = 1;
		}
	};
	self.update = function () {
		self.y += self.velocityY;
		self.velocityY *= 0.98; // Slow down over time
		self.lifetime--;
		damageText.alpha = self.lifetime / 120; // Fade out
		if (self.lifetime <= 0) {
			// Remove from damageNumbers array
			for (var i = damageNumbers.length - 1; i >= 0; i--) {
				if (damageNumbers[i] === self) {
					damageNumbers.splice(i, 1);
					break;
				}
			}
			returnToPool(self, 'damageNumber');
		}
	};
	return self;
});
var DustCloud = Container.expand(function () {
	var self = Container.call(this);
	var dustGraphics = self.attachAsset('bloodParticle', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	dustGraphics.tint = 0x8b7355; // Brown dust color
	dustGraphics.scaleX = 0.8;
	dustGraphics.scaleY = 0.8;
	self.velocityX = 0;
	self.velocityY = 0;
	self.lifetime = 40; // Medium lifetime
	self.expandRate = 0.02; // How fast dust expands
	self.reset = function (startX, startY, direction) {
		self.x = startX;
		self.y = startY;
		// Dust moves opposite to movement direction
		var baseSpeed = 1 + Math.random() * 2;
		if (direction === 'left') {
			self.velocityX = baseSpeed;
		} else if (direction === 'right') {
			self.velocityX = -baseSpeed;
		} else if (direction === 'up') {
			self.velocityY = baseSpeed * 0.5;
		} else if (direction === 'down') {
			self.velocityY = -baseSpeed * 0.5;
		} else {
			// Random direction for general dust
			var angle = Math.random() * Math.PI * 2;
			self.velocityX = Math.cos(angle) * baseSpeed;
			self.velocityY = Math.sin(angle) * baseSpeed;
		}
		self.velocityY -= Math.random() * 1; // Slight upward drift
		dustGraphics.alpha = 0.6;
		dustGraphics.scaleX = 0.4 + Math.random() * 0.4;
		dustGraphics.scaleY = 0.4 + Math.random() * 0.4;
		self.visible = true;
	};
	self.update = function () {
		self.x += self.velocityX;
		self.y += self.velocityY;
		// Dust slows down and expands
		self.velocityX *= 0.98;
		self.velocityY *= 0.98;
		dustGraphics.scaleX += self.expandRate;
		dustGraphics.scaleY += self.expandRate;
		// Fade out over time
		self.lifetime--;
		dustGraphics.alpha = self.lifetime / 40 * 0.6;
		if (self.lifetime <= 0) {
			// Remove from dustClouds array
			for (var i = dustClouds.length - 1; i >= 0; i--) {
				if (dustClouds[i] === self) {
					dustClouds.splice(i, 1);
					break;
				}
			}
			returnToPool(self, 'dustCloud');
		}
	};
	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));
		// Initialize group AI properties if not already set
		if (!self.hasOwnProperty('isAssignedFlanker')) self.isAssignedFlanker = false;
		if (!self.hasOwnProperty('flankingTarget')) self.flankingTarget = null;
		if (!self.hasOwnProperty('packGroup')) self.packGroup = null;
		if (!self.hasOwnProperty('leaderBuff')) self.leaderBuff = null;
		if (!self.hasOwnProperty('communicationTimer')) self.communicationTimer = 0;
		// Update communication timer
		if (self.communicationTimer > 0) self.communicationTimer--;
		// Process group communications
		var communications = groupAI.getCommunications(self, 120, 400);
		for (var c = 0; c < communications.length; c++) {
			var comm = communications[c];
			if (comm.type === 'spotted_hero' && !self.alerted) {
				self.alerted = true;
				groupAI.broadcastAlert(self, 'spotted_hero', 1);
			} else if (comm.type === 'under_attack' && comm.priority >= 2) {
				self.alerted = true;
				// Move to assist ally under attack
				if (!self.isAssignedFlanker && Math.random() < 0.3) {
					self.assistTarget = {
						x: comm.x,
						y: comm.y
					};
				}
			} else if (comm.type === 'flanking_position' && !self.isAssignedFlanker && self.enemyType !== 'tank') {
				groupAI.assignFlankingPosition(self);
			}
		}
		// 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
		} else if (self.enemyType === 'leader') {
			sightRange = 700; // Leaders have good coordination sight
		}
		var canSeeHero = distanceToHero < sightRange;
		// Broadcast hero sighting
		if (canSeeHero && !self.alerted && self.communicationTimer <= 0) {
			groupAI.broadcastAlert(self, 'spotted_hero', 1);
			self.alerted = true;
			self.communicationTimer = 60; // 1 second cooldown
		}
		if (canSeeHero || self.alerted) {
			// Determine movement target based on role
			var targetX = hero.x;
			var targetY = hero.y;
			var moveSpeed = self.speed;
			// Apply leader buffs
			if (self.leaderBuff) {
				moveSpeed *= self.leaderBuff.speedMultiplier;
			}
			// Flanking behavior
			if (self.isAssignedFlanker && self.flankingTarget) {
				var flankDistance = Math.sqrt(Math.pow(self.x - self.flankingTarget.x, 2) + Math.pow(self.y - self.flankingTarget.y, 2));
				if (flankDistance > 50) {
					// Move to flanking position
					targetX = self.flankingTarget.x;
					targetY = self.flankingTarget.y;
				} else {
					// Reached flanking position, now attack hero
					targetX = hero.x;
					targetY = hero.y;
					moveSpeed *= 1.2; // Speed boost when in flanking position
				}
			}
			// Pack hunting behavior for fast enemies
			if (self.enemyType === 'fast' || self.enemyType === 'hunter') {
				var nearbyPackMembers = spatialGrid.getNearbyObjects(self.x, self.y, 200);
				var packMates = 0;
				for (var p = 0; p < nearbyPackMembers.length; p++) {
					if (nearbyPackMembers[p] !== self && (nearbyPackMembers[p].enemyType === 'fast' || nearbyPackMembers[p].enemyType === 'hunter')) {
						packMates++;
					}
				}
				if (packMates >= 1) {
					// Pack hunting behavior - try to herd hero toward stronger enemies
					var nearbyStrongEnemies = spatialGrid.getNearbyObjects(hero.x, hero.y, 600);
					var strongestEnemy = null;
					for (var s = 0; s < nearbyStrongEnemies.length; s++) {
						if (nearbyStrongEnemies[s].enemyType === 'tank' || nearbyStrongEnemies[s].enemyType === 'strong' || nearbyStrongEnemies[s].enemyType === 'leader') {
							strongestEnemy = nearbyStrongEnemies[s];
							break;
						}
					}
					if (strongestEnemy) {
						// Position to herd hero toward strongest enemy
						var herdX = hero.x + (strongestEnemy.x - hero.x) * 0.3;
						var herdY = hero.y + (strongestEnemy.y - hero.y) * 0.3;
						targetX = herdX;
						targetY = herdY;
						moveSpeed *= 1.4; // Faster when pack hunting
					}
				}
			}
			// Assist behavior
			if (self.assistTarget && !self.isAssignedFlanker) {
				var assistDistance = Math.sqrt(Math.pow(self.x - self.assistTarget.x, 2) + Math.pow(self.y - self.assistTarget.y, 2));
				if (assistDistance > 100) {
					targetX = self.assistTarget.x;
					targetY = self.assistTarget.y;
					moveSpeed *= 1.1; // Slight speed boost when assisting
				} else {
					self.assistTarget = null; // Clear assist target when reached
				}
			}
			// Calculate movement toward target
			var dx = targetX - self.x;
			var dy = targetY - self.y;
			var distance = Math.sqrt(dx * dx + dy * dy);
			if (distance > 0) {
				// 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
			var attackRange = 100;
			if (self.leaderBuff) {
				attackRange *= 1.1; // Slightly increased attack range with leader buff
			}
			if (distanceToHero < attackRange && self.attackCooldown <= 0) {
				var damage = 1;
				if (self.leaderBuff && self.leaderBuff.damageMultiplier) {
					damage = Math.floor(damage * self.leaderBuff.damageMultiplier);
				}
				hero.takeDamage();
				self.attackCooldown = 120; // 2 seconds at 60fps
			}
			// Glow effects based on state
			if (self.alerted && !self.glowOutline) {
				createGlowOutline(self, 0xff8844, 0.5); // Orange glow for alerted
			} else if (!self.alerted && self.glowOutline) {
				removeGlowOutline(self);
			}
			if (self.health <= 1 && (!self.glowOutline || self.glowOutline.tint !== 0xff0000)) {
				removeGlowOutline(self);
				createGlowOutline(self, 0xff0000, 0.8); // Red glow for low health
			} else if (self.isAssignedFlanker && (!self.glowOutline || self.glowOutline.tint !== 0x8844ff)) {
				removeGlowOutline(self);
				createGlowOutline(self, 0x8844ff, 0.4); // Blue glow for flankers
			}
		} 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) {
		var damage = 1;
		var isCritical = Math.random() < 0.15; // 15% critical chance
		if (isCritical) {
			damage = 2;
			self.health -= 2;
		} else {
			self.health--;
		}
		// Create damage number
		var damageNumber = game.addChild(getFromPool('damageNumber'));
		damageNumber.reset(self.x + (Math.random() - 0.5) * 40, self.y - 100, damage, isCritical);
		damageNumbers.push(damageNumber);
		LK.effects.flashObject(self, isCritical ? 0xFFD700 : 0xffffff, isCritical ? 400 : 200);
		// Add glow effect based on enemy state
		if (self.alerted && !self.glowOutline) {
			createGlowOutline(self, 0xff4444, 0.6); // Red glow for alerted
		} else if (self.health <= 1 && !self.glowOutline) {
			createGlowOutline(self, 0xffaa00, 0.8); // Orange glow for low health
		}
		// 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 enhanced blood particles with variety
		for (var p = 0; p < 8; p++) {
			var bloodParticle = game.addChild(getFromPool('bloodParticle'));
			var particleType = Math.random() < 0.2 ? 'large' : Math.random() < 0.6 ? 'small' : 'normal';
			bloodParticle.reset(self.x + (Math.random() - 0.5) * 40, self.y - 60 + (Math.random() - 0.5) * 40, particleType);
			bloodParticles.push(bloodParticle);
		}
		// Add impact sparks when hit by knife
		if (fromKnife) {
			for (var s = 0; s < 6; s++) {
				var spark = game.addChild(getFromPool('impactSpark'));
				var sparkType = Math.random() < 0.3 ? 'bright' : Math.random() < 0.5 ? 'orange' : 'normal';
				spark.reset(self.x + (Math.random() - 0.5) * 30, self.y - 70 + (Math.random() - 0.5) * 30, sparkType);
				impactSparks.push(spark);
			}
			self.alerted = true;
			self.alertedByKnife = true;
		}
		if (self.health <= 0) {
			self.die();
		}
	};
	self.die = function () {
		// Screen shake on enemy death (smaller intensity)
		triggerScreenShake(6, 150, tween.easeOut);
		// Determine coin reward based on enemy difficulty
		var coinReward = 1; // Default for basic enemies
		if (self.enemyType === 'basic') {
			coinReward = 1;
		} else if (self.enemyType === 'fast') {
			coinReward = 2;
		} else if (self.enemyType === 'scout') {
			coinReward = 3;
		} else if (self.enemyType === 'strong') {
			coinReward = 3;
		} else if (self.enemyType === 'archer') {
			coinReward = 4;
		} else if (self.enemyType === 'berserker') {
			coinReward = 4;
		} else if (self.enemyType === 'hunter') {
			coinReward = 5;
		} else if (self.enemyType === 'shield') {
			coinReward = 6;
		} else if (self.enemyType === 'assassin') {
			coinReward = 6;
		} else if (self.enemyType === 'tank') {
			coinReward = 8;
		} else if (self.enemyType === 'leader') {
			coinReward = 10;
		}
		// Automatically give coins to upgrade tree
		upgradeTree.addCoins(coinReward);
		// Drop visual 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 Shield = Enemy.expand(function () {
	var self = Enemy.call(this, 'shield');
	var shieldGraphics = self.attachAsset('enemyTank', {
		anchorX: 0.5,
		anchorY: 1.0
	});
	shieldGraphics.tint = 0x4488ff; // Blue tint for shields
	self.enemyType = 'shield';
	self.health = 6;
	self.speed = 1.0;
	self.shieldActive = true;
	self.protectionRadius = 200;
	self.blockCooldown = 0;
	// Override update to include shield behavior
	var originalUpdate = self.update;
	self.update = function () {
		originalUpdate.call(self);
		// Shield positioning: try to stay between hero and vulnerable allies
		var vulnerableAllies = [];
		var nearbyAllies = spatialGrid.getNearbyObjects(self.x, self.y, self.protectionRadius * 1.5);
		for (var i = 0; i < nearbyAllies.length; i++) {
			var ally = nearbyAllies[i];
			if (ally !== self && ally.enemyType && (ally.enemyType === 'archer' || ally.enemyType === 'scout' || ally.health <= 2)) {
				vulnerableAllies.push(ally);
			}
		}
		// If there are vulnerable allies, position between them and hero
		if (vulnerableAllies.length > 0) {
			var avgAllyX = 0;
			var avgAllyY = 0;
			for (var i = 0; i < vulnerableAllies.length; i++) {
				avgAllyX += vulnerableAllies[i].x;
				avgAllyY += vulnerableAllies[i].y;
			}
			avgAllyX /= vulnerableAllies.length;
			avgAllyY /= vulnerableAllies.length;
			// Position between average ally position and hero
			var protectX = avgAllyX + (hero.x - avgAllyX) * 0.3;
			var protectY = avgAllyY + (hero.y - avgAllyY) * 0.3;
			var dx = protectX - self.x;
			var dy = protectY - self.y;
			var distance = Math.sqrt(dx * dx + dy * dy);
			if (distance > 50) {
				// Move toward protection position
				var newX = self.x + dx / distance * self.speed * 0.8;
				var newY = self.y + dy / distance * self.speed * 0.8;
				// Check collision before moving
				var wouldCollide = false;
				var nearbyEnemies = spatialGrid.getNearbyObjects(newX, newY, 100);
				for (var j = 0; j < nearbyEnemies.length; j++) {
					var otherEnemy = nearbyEnemies[j];
					if (otherEnemy === self) continue;
					var edx = newX - otherEnemy.x;
					var edy = newY - otherEnemy.y;
					var edistance = Math.sqrt(edx * edx + edy * edy);
					if (edistance < 80) {
						wouldCollide = true;
						break;
					}
				}
				if (!wouldCollide) {
					// Keep within bounds
					if (newX > 100 && newX < currentLevelData.width - 100) {
						self.lastX = self.x;
						self.x = newX;
					}
					if (newY > 1600 && newY < 2400) {
						self.lastY = self.y;
						self.y = newY;
					}
					shieldGraphics.scaleX = dx > 0 ? 1 : -1;
				}
			}
		}
		if (self.blockCooldown > 0) {
			self.blockCooldown--;
		}
	};
	self.blockProjectile = function (projectile) {
		if (!self.shieldActive || self.blockCooldown > 0) return false;
		// Check if projectile is within blocking range
		var dx = projectile.x - self.x;
		var dy = projectile.y - self.y;
		var distance = Math.sqrt(dx * dx + dy * dy);
		if (distance < 120) {
			// Block the projectile
			projectile.destroy();
			// Remove from projectiles array
			for (var i = bossProjectiles.length - 1; i >= 0; i--) {
				if (bossProjectiles[i] === projectile) {
					bossProjectiles.splice(i, 1);
					break;
				}
			}
			// Visual block effect
			tween(shieldGraphics, {
				tint: 0x88aaff,
				scaleX: shieldGraphics.scaleX * 1.3,
				scaleY: 1.3
			}, {
				duration: 200,
				onFinish: function onFinish() {
					tween(shieldGraphics, {
						tint: 0x4488ff,
						scaleX: shieldGraphics.scaleX > 0 ? 1 : -1,
						scaleY: 1
					}, {
						duration: 200
					});
				}
			});
			// Create block sparks
			for (var s = 0; s < 8; s++) {
				var spark = game.addChild(getFromPool('impactSpark'));
				spark.reset(self.x + (Math.random() - 0.5) * 60, self.y - 60 + (Math.random() - 0.5) * 60, 'bright');
				impactSparks.push(spark);
			}
			self.blockCooldown = 60; // 1 second cooldown
			return true;
		}
		return false;
	};
	// Override takeDamage to handle shield protection
	var originalTakeDamage = self.takeDamage;
	self.takeDamage = function (fromKnife) {
		// Shield enemies take reduced damage from the front
		var damage = 1;
		if (fromKnife) {
			// Check if knife is coming from the front
			var dx = hero.x - self.x;
			var shieldFacing = shieldGraphics.scaleX > 0 ? 1 : -1;
			if (dx > 0 && shieldFacing > 0 || dx < 0 && shieldFacing < 0) {
				// Knife blocked by shield, take reduced damage
				damage = 0;
				// Visual block effect
				LK.effects.flashObject(self, 0x88aaff, 300);
				// Create block sparks
				for (var s = 0; s < 6; s++) {
					var spark = game.addChild(getFromPool('impactSpark'));
					spark.reset(self.x + (Math.random() - 0.5) * 50, self.y - 70 + (Math.random() - 0.5) * 50, 'bright');
					impactSparks.push(spark);
				}
				return; // No damage taken
			}
		}
		if (damage > 0) {
			originalTakeDamage.call(self, fromKnife);
		}
	};
	return self;
});
var Scout = Enemy.expand(function () {
	var self = Enemy.call(this, 'scout');
	var scoutGraphics = self.attachAsset('enemyFast', {
		anchorX: 0.5,
		anchorY: 1.0
	});
	scoutGraphics.tint = 0x88ff88; // Light green for scouts
	scoutGraphics.scaleX = 0.9;
	scoutGraphics.scaleY = 0.9;
	self.enemyType = 'scout';
	self.health = 1;
	self.speed = 5; // Very fast
	self.sightRange = 900; // Excellent sight
	self.alertCooldown = 0;
	self.fleeingFromCombat = false;
	self.lastAlertTime = 0;
	// Override update to include scout-specific behaviors
	var originalUpdate = self.update;
	self.update = function () {
		var distanceToHero = Math.sqrt(Math.pow(self.x - hero.x, 2) + Math.pow(self.y - hero.y, 2));
		// Scout-specific sight and alerting
		if (distanceToHero < self.sightRange && !self.alerted) {
			self.alerted = true;
			// Scouts immediately alert all nearby enemies
			groupAI.broadcastAlert(self, 'spotted_hero', 3); // High priority alert
			self.lastAlertTime = LK.ticks;
			// Create alert visual effect
			tween(scoutGraphics, {
				tint: 0xffff44,
				// Yellow alert color
				scaleX: 1.2,
				scaleY: 1.2
			}, {
				duration: 500,
				onFinish: function onFinish() {
					tween(scoutGraphics, {
						tint: 0x88ff88,
						scaleX: 0.9,
						scaleY: 0.9
					}, {
						duration: 300
					});
				}
			});
		}
		// Flee from combat when hero gets too close
		if (distanceToHero < 250) {
			self.fleeingFromCombat = true;
		} else if (distanceToHero > 500) {
			self.fleeingFromCombat = false;
		}
		if (self.fleeingFromCombat) {
			// Move away from hero at maximum speed
			var dx = self.x - hero.x;
			var dy = self.y - hero.y;
			var distance = Math.sqrt(dx * dx + dy * dy);
			if (distance > 0) {
				var fleeSpeed = self.speed * 1.5; // Extra fast when fleeing
				var newX = self.x + dx / distance * fleeSpeed;
				var newY = self.y + dy / distance * fleeSpeed;
				// Check collision with other enemies before moving
				var wouldCollide = false;
				var nearbyEnemies = spatialGrid.getNearbyObjects(newX, newY, 80);
				for (var i = 0; i < nearbyEnemies.length; i++) {
					var otherEnemy = nearbyEnemies[i];
					if (otherEnemy === self) continue;
					var edx = newX - otherEnemy.x;
					var edy = newY - otherEnemy.y;
					var edistance = Math.sqrt(edx * edx + edy * edy);
					if (edistance < 60) {
						wouldCollide = true;
						break;
					}
				}
				if (!wouldCollide) {
					// Keep within level bounds
					if (newX > 100 && newX < currentLevelData.width - 100) {
						self.lastX = self.x;
						self.x = newX;
					}
					if (newY > 1600 && newY < 2400) {
						self.lastY = self.y;
						self.y = newY;
					}
					scoutGraphics.scaleX = dx > 0 ? 0.9 : -0.9; // Face away from hero
				}
			}
		} else {
			// Use original enemy update when not fleeing
			originalUpdate.call(self);
		}
		// Periodic re-alerting to maintain enemy awareness
		if (self.alerted && LK.ticks - self.lastAlertTime > 300) {
			// Every 5 seconds
			groupAI.broadcastAlert(self, 'spotted_hero', 2);
			self.lastAlertTime = LK.ticks;
		}
	};
	// Override takeDamage to trigger emergency broadcast
	var originalTakeDamage = self.takeDamage;
	self.takeDamage = function (fromKnife) {
		// Emergency alert when scout is attacked
		groupAI.broadcastAlert(self, 'under_attack', 3);
		originalTakeDamage.call(self, fromKnife);
	};
	return self;
});
var Leader = Enemy.expand(function () {
	var self = Enemy.call(this, 'leader');
	var leaderGraphics = self.attachAsset('enemyStrong', {
		anchorX: 0.5,
		anchorY: 1.0
	});
	leaderGraphics.tint = 0xFFD700; // Golden tint for leaders
	leaderGraphics.scaleX = 1.2;
	leaderGraphics.scaleY = 1.2;
	self.enemyType = 'leader';
	self.health = 6;
	self.speed = 1.5;
	self.buffRadius = 250;
	self.commandRadius = 400;
	self.buffCooldown = 0;
	self.isCommanding = false;
	self.commandedAllies = [];
	// Override update to include leadership behaviors
	var originalUpdate = self.update;
	self.update = function () {
		// Call original enemy update first
		originalUpdate.call(self);
		// Leadership AI behaviors
		self.manageAllies();
		self.coordinateAttacks();
		self.provideBattlefieldIntelligence();
		// Update buff cooldown
		if (self.buffCooldown > 0) self.buffCooldown--;
	};
	self.manageAllies = function () {
		var nearbyAllies = spatialGrid.getNearbyObjects(self.x, self.y, self.commandRadius);
		self.commandedAllies = [];
		for (var i = 0; i < nearbyAllies.length; i++) {
			var ally = nearbyAllies[i];
			if (ally !== self && ally.enemyType !== 'boss' && ally.enemyType !== 'leader') {
				self.commandedAllies.push(ally);
				// Provide speed buff to nearby allies
				if (Math.sqrt(Math.pow(self.x - ally.x, 2) + Math.pow(self.y - ally.y, 2)) <= self.buffRadius) {
					if (!ally.leaderBuff) {
						ally.leaderBuff = {
							speedMultiplier: 1.3,
							damageMultiplier: 1.2,
							source: self
						};
						// Visual indication of buff
						createGlowOutline(ally, 0xFFD700, 0.3);
					}
				}
			}
		}
	};
	self.coordinateAttacks = function () {
		if (self.commandedAllies.length >= 2 && self.buffCooldown <= 0) {
			// Order flanking maneuvers
			groupAI.broadcastAlert(self, 'flanking_position', 3);
			for (var i = 0; i < Math.min(3, self.commandedAllies.length); i++) {
				var ally = self.commandedAllies[i];
				if (!ally.isAssignedFlanker) {
					groupAI.assignFlankingPosition(ally);
				}
			}
			self.buffCooldown = 300; // 5 seconds
			self.isCommanding = true;
			// Visual command effect
			tween(leaderGraphics, {
				tint: 0xFFAA00,
				scaleX: 1.4,
				scaleY: 1.4
			}, {
				duration: 500,
				onFinish: function onFinish() {
					tween(leaderGraphics, {
						tint: 0xFFD700,
						scaleX: 1.2,
						scaleY: 1.2
					}, {
						duration: 300
					});
					self.isCommanding = false;
				}
			});
		}
	};
	self.provideBattlefieldIntelligence = function () {
		// Share hero position with distant allies
		if (Math.random() < 0.05) {
			// 5% chance per frame
			groupAI.broadcastAlert(self, 'spotted_hero', 2);
		}
	};
	// Override takeDamage to rally allies when leader is threatened
	var originalTakeDamage = self.takeDamage;
	self.takeDamage = function (fromKnife) {
		originalTakeDamage.call(self, fromKnife);
		// Rally cry when damaged
		groupAI.broadcastAlert(self, 'under_attack', 3);
		// Boost nearby allies when leader is in danger
		for (var i = 0; i < self.commandedAllies.length; i++) {
			var ally = self.commandedAllies[i];
			ally.alerted = true;
			if (ally.leaderBuff) {
				ally.leaderBuff.speedMultiplier = 1.5; // Increased speed when leader threatened
			}
		}
	};
	// Override die to remove buffs from allies
	var originalDie = self.die;
	self.die = function () {
		// Remove leader buffs from all allies
		for (var i = 0; i < enemies.length; i++) {
			var enemy = enemies[i];
			if (enemy.leaderBuff && enemy.leaderBuff.source === self) {
				enemy.leaderBuff = null;
				removeGlowOutline(enemy);
			}
		}
		originalDie.call(self);
	};
	return self;
});
var Berserker = Enemy.expand(function () {
	var self = Enemy.call(this, 'berserker');
	var berserkerGraphics = self.attachAsset('enemyStrong', {
		anchorX: 0.5,
		anchorY: 1.0
	});
	berserkerGraphics.tint = 0xff6666; // Red tint for berserkers
	self.enemyType = 'berserker';
	self.health = 4;
	self.maxHealth = 4;
	self.speed = 1.5;
	self.baseSpeed = 1.5;
	self.isBerserk = false;
	self.berserkThreshold = 1; // Goes berserk at 1 health
	// Override update to include berserker rage
	var originalUpdate = self.update;
	self.update = function () {
		// Check for berserk activation
		if (!self.isBerserk && self.health <= self.berserkThreshold) {
			self.activateBerserk();
		}
		originalUpdate.call(self);
	};
	self.activateBerserk = function () {
		self.isBerserk = true;
		self.speed = self.baseSpeed * 2.5; // Much faster when berserk
		berserkerGraphics.tint = 0xff0000; // Bright red when berserk
		berserkerGraphics.scaleX = 1.3;
		berserkerGraphics.scaleY = 1.3;
		self.attackCooldown = Math.floor(self.attackCooldown * 0.5); // Faster attacks
		// Visual berserk effect
		tween(berserkerGraphics, {
			scaleX: 1.5,
			scaleY: 1.5
		}, {
			duration: 300,
			easing: tween.easeOut,
			onFinish: function onFinish() {
				tween(berserkerGraphics, {
					scaleX: 1.3,
					scaleY: 1.3
				}, {
					duration: 200
				});
			}
		});
		// Create rage particles
		for (var i = 0; i < 12; i++) {
			var particle = game.addChild(getFromPool('bloodParticle'));
			particle.reset(self.x + (Math.random() - 0.5) * 80, self.y - 80 + (Math.random() - 0.5) * 80, 'spray');
			var particleGraphics = particle.getChildAt(0);
			particleGraphics.tint = 0xff4444; // Red rage particles
			bloodParticles.push(particle);
		}
		// Broadcast rage to alert other enemies
		groupAI.broadcastAlert(self, 'under_attack', 2);
	};
	// Override takeDamage to handle berserk damage bonus
	var originalTakeDamage = self.takeDamage;
	self.takeDamage = function (fromKnife) {
		var damage = 1;
		// Berserk berserkers deal damage back to attackers
		if (self.isBerserk && !fromKnife) {
			// Only counter-attack melee attacks, not knife throws
			var distanceToHero = Math.sqrt(Math.pow(self.x - hero.x, 2) + Math.pow(self.y - hero.y, 2));
			if (distanceToHero < 150) {
				hero.takeDamage(); // Counter-attack
				LK.effects.flashObject(self, 0xff0000, 300);
			}
		}
		originalTakeDamage.call(self, fromKnife);
	};
	return self;
});
var Archer = Enemy.expand(function () {
	var self = Enemy.call(this, 'archer');
	var archerGraphics = self.attachAsset('enemyHunter', {
		anchorX: 0.5,
		anchorY: 1.0
	});
	archerGraphics.tint = 0x8844ff; // Purple tint for archers
	self.enemyType = 'archer';
	self.health = 2;
	self.speed = 1.8;
	self.shootCooldown = 0;
	self.optimalRange = 400; // Preferred distance from hero
	self.maxRange = 600; // Maximum shooting range
	// Override update to include archer behavior
	var originalUpdate = self.update;
	self.update = function () {
		var distanceToHero = Math.sqrt(Math.pow(self.x - hero.x, 2) + Math.pow(self.y - hero.y, 2));
		// Archer positioning: maintain optimal distance
		if (distanceToHero < self.optimalRange - 50) {
			// Too close, move away
			var dx = self.x - hero.x;
			var dy = self.y - hero.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 before moving
				var wouldCollide = false;
				var nearbyEnemies = spatialGrid.getNearbyObjects(newX, newY, 80);
				for (var i = 0; i < nearbyEnemies.length; i++) {
					var otherEnemy = nearbyEnemies[i];
					if (otherEnemy === self) continue;
					var edx = newX - otherEnemy.x;
					var edy = newY - otherEnemy.y;
					var edistance = Math.sqrt(edx * edx + edy * edy);
					if (edistance < 70) {
						wouldCollide = true;
						break;
					}
				}
				if (!wouldCollide) {
					// Keep within bounds
					if (newX > 100 && newX < currentLevelData.width - 100) {
						self.lastX = self.x;
						self.x = newX;
					}
					if (newY > 1600 && newY < 2400) {
						self.lastY = self.y;
						self.y = newY;
					}
					archerGraphics.scaleX = dx > 0 ? 1 : -1; // Face away from hero
				}
			}
		} else if (distanceToHero > self.optimalRange + 50) {
			// Too far, move closer (use original enemy behavior)
			originalUpdate.call(self);
		}
		// Shoot at hero if in range and cooldown is ready
		if (distanceToHero <= self.maxRange && self.shootCooldown <= 0 && self.alerted) {
			self.shootArrow();
		}
		if (self.shootCooldown > 0) {
			self.shootCooldown--;
		}
		// Update attack cooldown from original
		if (self.attackCooldown > 0) {
			self.attackCooldown--;
		}
	};
	self.shootArrow = function () {
		// Create projectile
		var arrow = game.addChild(new ArcherProjectile());
		arrow.x = self.x;
		arrow.y = self.y - 100;
		// Calculate trajectory 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 = 8;
			arrow.velocityX = dx / distance * speed;
			arrow.velocityY = dy / distance * speed;
		}
		bossProjectiles.push(arrow); // Reuse boss projectiles array
		self.shootCooldown = 180; // 3 seconds
		// Visual shooting effect
		tween(archerGraphics, {
			tint: 0xff4488,
			scaleX: archerGraphics.scaleX * 1.2,
			scaleY: 1.2
		}, {
			duration: 200,
			onFinish: function onFinish() {
				tween(archerGraphics, {
					tint: 0x8844ff,
					scaleX: archerGraphics.scaleX > 0 ? 1 : -1,
					scaleY: 1
				}, {
					duration: 200
				});
			}
		});
	};
	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;
		// Apply attack speed upgrade
		var baseSlashCooldown = 24; // 0.4 seconds at 60fps
		self.slashCooldown = Math.floor(baseSlashCooldown / (self.attackSpeedMultiplier || 1));
		// 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;
		// Create weapon trail effect
		var trail = game.addChild(getFromPool('weaponTrail'));
		var trailType = self.damageBoost ? 'power' : 'normal';
		trail.reset(self.x + heroGraphics.scaleX * 100, self.y - 80, heroGraphics.scaleX > 0 ? -0.3 : 0.3, 2, trailType);
		weaponTrails.push(trail);
		// 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;
		// Apply damage reduction
		var damageReduction = self.damageReduction || 0;
		if (Math.random() < damageReduction) {
			// Damage reduced! Show visual effect
			LK.effects.flashObject(self, 0x3498db, 300);
			return;
		}
		self.health--;
		self.comboCount = 0;
		// Flash red when hit
		LK.effects.flashObject(self, 0xff0000, 500);
		// Screen shake when player is hit
		triggerScreenShake(18, 400, tween.easeOut);
		// Temporary invulnerability with upgrade bonus
		self.invulnerable = true;
		var invulnerabilityDuration = 1000 + (self.invulnerabilityBonus || 0);
		LK.setTimeout(function () {
			self.invulnerable = false;
		}, invulnerabilityDuration);
		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 = self.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;
			// Create dust clouds when moving (occasional)
			if (Math.random() < 0.3) {
				var dustCloud = game.addChild(getFromPool('dustCloud'));
				dustCloud.reset(self.x + (Math.random() - 0.5) * 30, self.y - 10 + (Math.random() - 0.5) * 20, direction);
				dustClouds.push(dustCloud);
			}
			// 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 ImpactSpark = Container.expand(function () {
	var self = Container.call(this);
	var sparkGraphics = self.attachAsset('bloodParticle', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	sparkGraphics.tint = 0xffff44; // Yellow-white sparks
	sparkGraphics.scaleX = 0.3;
	sparkGraphics.scaleY = 0.3;
	self.velocityX = 0;
	self.velocityY = 0;
	self.lifetime = 20; // Short-lived sparks
	self.sparkType = 'normal'; // normal, bright, orange
	self.reset = function (startX, startY, type) {
		self.x = startX;
		self.y = startY;
		self.sparkType = type || 'normal';
		// Random direction and speed for sparks
		var angle = Math.random() * Math.PI * 2;
		var speed = 3 + Math.random() * 5;
		self.velocityX = Math.cos(angle) * speed;
		self.velocityY = Math.sin(angle) * speed;
		if (self.sparkType === 'bright') {
			sparkGraphics.tint = 0xffffff; // White sparks
			sparkGraphics.scaleX = 0.4;
			sparkGraphics.scaleY = 0.4;
			self.lifetime = 30;
		} else if (self.sparkType === 'orange') {
			sparkGraphics.tint = 0xff8844; // Orange sparks
			sparkGraphics.scaleX = 0.2;
			sparkGraphics.scaleY = 0.2;
			self.lifetime = 15;
		} else {
			sparkGraphics.tint = 0xffff44; // Yellow sparks
			sparkGraphics.scaleX = 0.3;
			sparkGraphics.scaleY = 0.3;
			self.lifetime = 20;
		}
		sparkGraphics.alpha = 1;
		self.visible = true;
	};
	self.update = function () {
		self.x += self.velocityX;
		self.y += self.velocityY;
		// Sparks slow down over time
		self.velocityX *= 0.95;
		self.velocityY *= 0.95;
		// Fade out quickly
		self.lifetime--;
		sparkGraphics.alpha = self.lifetime / 20;
		if (self.lifetime <= 0) {
			// Remove from impactSparks array
			for (var i = impactSparks.length - 1; i >= 0; i--) {
				if (impactSparks[i] === self) {
					impactSparks.splice(i, 1);
					break;
				}
			}
			returnToPool(self, 'impactSpark');
		}
	};
	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)
		var penetration = upgradeTree ? upgradeTree.getKnifePenetration() : 0;
		var enemiesHit = 0;
		var maxEnemiesHit = 1 + penetration;
		for (var i = 0; i < enemies.length && enemiesHit < maxEnemiesHit; 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 with upgrade multiplier
				var damage = Math.floor(hero.damageMultiplier || 1);
				for (var d = 0; d < damage; d++) {
					enemy.takeDamage(true);
				}
				// Light screen shake on knife impact
				triggerScreenShake(4, 100, tween.easeOut);
				enemiesHit++;
				// If no penetration or hit max enemies, destroy knife
				if (penetration === 0 || enemiesHit >= maxEnemiesHit) {
					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 MagicalEffect = Container.expand(function () {
	var self = Container.call(this);
	var effectGraphics = self.attachAsset('bloodParticle', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	self.velocityX = 0;
	self.velocityY = 0;
	self.lifetime = 60;
	self.effectType = 'sparkle'; // sparkle, healing, power, shield
	self.rotationSpeed = 0;
	self.reset = function (startX, startY, type) {
		self.x = startX;
		self.y = startY;
		self.effectType = type || 'sparkle';
		if (self.effectType === 'sparkle') {
			effectGraphics.tint = 0xffff88; // Golden sparkle
			effectGraphics.scaleX = 0.6;
			effectGraphics.scaleY = 0.6;
			var angle = Math.random() * Math.PI * 2;
			var speed = 2 + Math.random() * 3;
			self.velocityX = Math.cos(angle) * speed;
			self.velocityY = Math.sin(angle) * speed - 2; // Float upward
			self.rotationSpeed = (Math.random() - 0.5) * 0.2;
			self.lifetime = 80;
		} else if (self.effectType === 'healing') {
			effectGraphics.tint = 0x44ff44; // Green healing
			effectGraphics.scaleX = 0.8;
			effectGraphics.scaleY = 0.8;
			self.velocityX = (Math.random() - 0.5) * 2;
			self.velocityY = -3 - Math.random() * 2; // Rise upward
			self.rotationSpeed = 0.1;
			self.lifetime = 60;
		} else if (self.effectType === 'power') {
			effectGraphics.tint = 0xff4488; // Purple power
			effectGraphics.scaleX = 1.0;
			effectGraphics.scaleY = 1.0;
			self.velocityX = (Math.random() - 0.5) * 4;
			self.velocityY = -1 - Math.random() * 3;
			self.rotationSpeed = 0.15;
			self.lifetime = 70;
		} else if (self.effectType === 'shield') {
			effectGraphics.tint = 0x44ddff; // Blue shield
			effectGraphics.scaleX = 0.5;
			effectGraphics.scaleY = 0.5;
			var angle = Math.random() * Math.PI * 2;
			var radius = 50 + Math.random() * 30;
			self.velocityX = Math.cos(angle) * 2;
			self.velocityY = Math.sin(angle) * 2;
			self.rotationSpeed = 0.08;
			self.lifetime = 90;
		}
		effectGraphics.alpha = 1;
		self.visible = true;
	};
	self.update = function () {
		self.x += self.velocityX;
		self.y += self.velocityY;
		effectGraphics.rotation += self.rotationSpeed;
		// Different behaviors per effect type
		if (self.effectType === 'sparkle') {
			self.velocityY -= 0.05; // Continue floating up
		} else if (self.effectType === 'healing') {
			self.velocityX *= 0.99; // Slow down horizontally
		} else if (self.effectType === 'power') {
			// Pulsing effect
			var pulseScale = 1.0 + Math.sin(LK.ticks * 0.3) * 0.2;
			effectGraphics.scaleX = pulseScale;
			effectGraphics.scaleY = pulseScale;
		}
		// Fade out over time
		self.lifetime--;
		var maxLifetime = 60;
		if (self.effectType === 'sparkle') maxLifetime = 80;else if (self.effectType === 'power') maxLifetime = 70;else if (self.effectType === 'shield') maxLifetime = 90;
		effectGraphics.alpha = self.lifetime / maxLifetime;
		if (self.lifetime <= 0) {
			// Remove from magicalEffects array
			for (var i = magicalEffects.length - 1; i >= 0; i--) {
				if (magicalEffects[i] === self) {
					magicalEffects.splice(i, 1);
					break;
				}
			}
			returnToPool(self, 'magicalEffect');
		}
	};
	return self;
});
var Minion = Container.expand(function () {
	var self = Container.call(this);
	var minionGraphics = self.attachAsset('enemyFast', {
		anchorX: 0.5,
		anchorY: 1.0
	});
	minionGraphics.tint = 0x9b59b6; // Purple tint for minions
	minionGraphics.scaleX = 0.8; // Smaller than normal enemies
	minionGraphics.scaleY = 0.8;
	self.health = 1;
	self.speed = 3.5;
	self.attackCooldown = 0;
	self.lastX = 0;
	self.lastY = 0;
	self.summoner = null; // Reference to summoning boss
	self.lifetime = 1800; // 30 seconds lifetime
	self.update = function () {
		self.lifetime--;
		if (self.lifetime <= 0) {
			self.die();
			return;
		}
		var distanceToHero = Math.sqrt(Math.pow(self.x - hero.x, 2) + Math.pow(self.y - hero.y, 2));
		// Aggressive AI - always 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) {
			// Check collision with other enemies before moving
			var wouldCollide = false;
			var newX = self.x + dx / distance * self.speed;
			var newY = self.y + dy / distance * self.speed;
			var nearbyEnemies = spatialGrid.getNearbyObjects(newX, newY, 100);
			for (var i = 0; i < nearbyEnemies.length; i++) {
				var otherEnemy = nearbyEnemies[i];
				if (otherEnemy === self) continue;
				var edx = newX - otherEnemy.x;
				var edy = newY - otherEnemy.y;
				var edistance = Math.sqrt(edx * edx + edy * edy);
				if (edistance < 60) {
					// Smaller collision radius for minions
					wouldCollide = true;
					break;
				}
			}
			if (!wouldCollide) {
				self.lastX = self.x;
				self.lastY = self.y;
				self.x = newX;
				self.y = newY;
			}
			minionGraphics.scaleX = dx > 0 ? 0.8 : -0.8;
		}
		// Attack hero if close enough
		if (distanceToHero < 80 && self.attackCooldown <= 0) {
			hero.takeDamage();
			self.attackCooldown = 90; // 1.5 seconds
		}
		if (self.attackCooldown > 0) {
			self.attackCooldown--;
		}
	};
	self.takeDamage = function (fromKnife) {
		self.health--;
		LK.effects.flashObject(self, 0xffffff, 200);
		// Create smaller blood particles
		for (var p = 0; p < 8; p++) {
			var bloodParticle = game.addChild(getFromPool('bloodParticle'));
			bloodParticle.reset(self.x + (Math.random() - 0.5) * 30, self.y - 40 + (Math.random() - 0.5) * 30);
			bloodParticles.push(bloodParticle);
		}
		if (self.health <= 0) {
			self.die();
		}
	};
	self.die = function () {
		// Minions give 2 coins
		upgradeTree.addCoins(2);
		// Drop small coin
		var coin = game.addChild(new Coin());
		coin.x = self.x;
		coin.y = self.y;
		var coinGraphics = coin.getChildAt(0);
		coinGraphics.scaleX = 0.7; // Smaller coin
		coinGraphics.scaleY = 0.7;
		coins.push(coin);
		// Reduce summoner's minion count
		if (self.summoner && self.summoner.minionCount) {
			self.summoner.minionCount--;
		}
		// Small score bonus
		LK.setScore(LK.getScore() + 5);
		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 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();
		// Create magical effects based on power-up type
		var effectType = 'sparkle';
		if (self.type === 'health') {
			hero.heal();
			effectType = 'healing';
		} else if (self.type === 'invulnerable') {
			hero.invulnerable = true;
			effectType = 'shield';
			LK.setTimeout(function () {
				hero.invulnerable = false;
			}, 5000);
		} else if (self.type === 'damage') {
			hero.damageBoost = true;
			effectType = 'power';
			LK.setTimeout(function () {
				hero.damageBoost = false;
			}, 5000);
		}
		// Create magical particle effects
		for (var e = 0; e < 12; e++) {
			var magicalEffect = game.addChild(getFromPool('magicalEffect'));
			magicalEffect.reset(self.x + (Math.random() - 0.5) * 60, self.y + (Math.random() - 0.5) * 60, effectType);
			magicalEffects.push(magicalEffect);
		}
		// 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;
});
var ScreenShake = Container.expand(function () {
	var self = Container.call(this);
	self.intensity = 0;
	self.duration = 0;
	self.remainingTime = 0;
	self.offsetX = 0;
	self.offsetY = 0;
	self.isActive = false;
	self.decayEasing = tween.easeOut;
	self.shake = function (intensity, duration, easing) {
		self.intensity = intensity || 10;
		self.duration = duration || 500;
		self.remainingTime = self.duration;
		self.isActive = true;
		self.decayEasing = easing || tween.easeOut;
	};
	self.update = function () {
		if (!self.isActive) {
			self.offsetX = 0;
			self.offsetY = 0;
			return;
		}
		self.remainingTime -= 16.67; // Approximately 1 frame at 60fps
		if (self.remainingTime <= 0) {
			self.isActive = false;
			self.offsetX = 0;
			self.offsetY = 0;
			return;
		}
		// Calculate decay factor using easing function
		var progress = 1 - self.remainingTime / self.duration;
		var decayFactor = 1 - self.decayEasing(progress);
		// Generate random shake offset
		var currentIntensity = self.intensity * decayFactor;
		self.offsetX = (Math.random() - 0.5) * 2 * currentIntensity;
		self.offsetY = (Math.random() - 0.5) * 2 * currentIntensity;
	};
	self.getOffsetX = function () {
		return self.offsetX;
	};
	self.getOffsetY = function () {
		return self.offsetY;
	};
	return self;
});
var UpgradeMenu = Container.expand(function () {
	var self = Container.call(this);
	self.isVisible = false;
	self.upgradeTree = null;
	self.upgradeButtons = [];
	self.categoryHeaders = [];
	self.background = null;
	self.show = function (upgradeTree) {
		self.upgradeTree = upgradeTree;
		self.isVisible = true;
		self.x = 0;
		self.y = 0;
		self.createInterface();
		// Animate menu appearance
		self.alpha = 0;
		self.scaleX = 0.8;
		self.scaleY = 0.8;
		tween(self, {
			alpha: 1,
			scaleX: 1,
			scaleY: 1
		}, {
			duration: 300,
			easing: tween.easeOut
		});
	};
	self.hide = function () {
		tween(self, {
			alpha: 0,
			scaleX: 0.8,
			scaleY: 0.8
		}, {
			duration: 200,
			easing: tween.easeIn,
			onFinish: function onFinish() {
				self.isVisible = false;
				self.clearInterface();
			}
		});
	};
	self.createInterface = function () {
		// Create background centered on screen
		self.background = self.addChild(LK.getAsset('backgroundTile', {
			anchorX: 0.5,
			anchorY: 0.5,
			tint: 0x2c3e50,
			alpha: 0.95,
			scaleX: 10,
			scaleY: 12
		}));
		self.background.x = 0;
		self.background.y = 0;
		// Title
		var title = new Text2('UPGRADE TREE', {
			size: 100,
			fill: '#FFD700'
		});
		title.anchor.set(0.5, 0.5);
		title.x = 0;
		title.y = -1000;
		self.addChild(title);
		// Coins display
		var coinsText = new Text2('Coins: ' + self.upgradeTree.coins, {
			size: 60,
			fill: '#F1C40F'
		});
		coinsText.anchor.set(0.5, 0.5);
		coinsText.x = 0;
		coinsText.y = -900;
		self.addChild(coinsText);
		self.coinsText = coinsText;
		// Combat category
		self.createCategory('COMBAT', -600, [{
			type: 'damage',
			name: 'Damage Boost',
			desc: '+20% damage per level'
		}, {
			type: 'attackSpeed',
			name: 'Attack Speed',
			desc: '+25% attack speed per level'
		}, {
			type: 'knifePenetration',
			name: 'Knife Penetration',
			desc: 'Knives hit multiple enemies'
		}]);
		// Defense category
		self.createCategory('DEFENSE', -100, [{
			type: 'maxHealth',
			name: 'Max Health',
			desc: '+1 max health per level'
		}, {
			type: 'damageReduction',
			name: 'Damage Reduction',
			desc: '+15% damage reduction per level'
		}, {
			type: 'invulnerability',
			name: 'Invulnerability',
			desc: '+1 second invulnerability per level'
		}]);
		// Utility category
		self.createCategory('UTILITY', 400, [{
			type: 'moveSpeed',
			name: 'Move Speed',
			desc: '+25% movement speed per level'
		}, {
			type: 'startingKnives',
			name: 'Starting Knives',
			desc: '+2 starting knives per level'
		}, {
			type: 'coinMagnetism',
			name: 'Coin Magnetism',
			desc: 'Increased coin collection range'
		}]);
		// Close button
		var closeButton = self.addChild(LK.getAsset('backgroundTile', {
			anchorX: 0.5,
			anchorY: 0.5,
			tint: 0xe74c3c,
			scaleX: 2,
			scaleY: 1
		}));
		closeButton.x = 0;
		closeButton.y = 1000;
		var closeText = new Text2('CLOSE', {
			size: 60,
			fill: '#FFFFFF'
		});
		closeText.anchor.set(0.5, 0.5);
		closeText.x = 0;
		closeText.y = 1000;
		self.addChild(closeText);
		closeButton.down = function () {
			self.hide();
		};
	};
	self.createCategory = function (categoryName, startY, upgrades) {
		// Category header
		var header = new Text2(categoryName, {
			size: 80,
			fill: '#3498db'
		});
		header.anchor.set(0.5, 0.5);
		header.x = 0;
		header.y = startY;
		self.addChild(header);
		// Upgrade buttons
		for (var i = 0; i < upgrades.length; i++) {
			var upgrade = upgrades[i];
			var buttonY = startY + 100 + i * 120;
			self.createUpgradeButton(upgrade, buttonY);
		}
	};
	self.createUpgradeButton = function (upgrade, y) {
		var currentLevel = self.upgradeTree.upgrades[upgrade.type];
		var maxLevel = self.upgradeTree.upgradeCosts[upgrade.type].length;
		var canUpgrade = self.upgradeTree.canUpgrade(upgrade.type);
		var cost = currentLevel < maxLevel ? self.upgradeTree.upgradeCosts[upgrade.type][currentLevel] : 0;
		// Button background
		var buttonColor = currentLevel >= maxLevel ? 0x27ae60 : canUpgrade ? 0x3498db : 0x7f8c8d;
		var button = self.addChild(LK.getAsset('backgroundTile', {
			anchorX: 0.5,
			anchorY: 0.5,
			tint: buttonColor,
			scaleX: 8,
			scaleY: 0.8
		}));
		button.x = 0;
		button.y = y;
		// Upgrade name
		var nameText = new Text2(upgrade.name, {
			size: 50,
			fill: '#FFFFFF'
		});
		nameText.anchor.set(0.5, 0.5);
		nameText.x = -424;
		nameText.y = y - 15;
		self.addChild(nameText);
		// Level display
		var levelText = new Text2('Level: ' + currentLevel + '/' + maxLevel, {
			size: 40,
			fill: '#BDC3C7'
		});
		levelText.anchor.set(0.5, 0.5);
		levelText.x = -424;
		levelText.y = y + 15;
		self.addChild(levelText);
		// Cost display
		if (currentLevel < maxLevel) {
			var costText = new Text2('Cost: ' + cost, {
				size: 40,
				fill: canUpgrade ? '#F1C40F' : '#E74C3C'
			});
			costText.anchor.set(0.5, 0.5);
			costText.x = 376;
			costText.y = y;
			self.addChild(costText);
		} else {
			var maxText = new Text2('MAX', {
				size: 40,
				fill: '#27AE60'
			});
			maxText.anchor.set(0.5, 0.5);
			maxText.x = 376;
			maxText.y = y;
			self.addChild(maxText);
		}
		// Purchase functionality
		if (canUpgrade) {
			button.down = function () {
				if (self.upgradeTree.purchaseUpgrade(upgrade.type)) {
					// Refresh interface
					self.clearInterface();
					self.createInterface();
					// Success effect
					tween(button, {
						tint: 0x27ae60,
						scaleX: 9,
						scaleY: 0.9
					}, {
						duration: 200,
						onFinish: function onFinish() {
							tween(button, {
								tint: buttonColor,
								scaleX: 8,
								scaleY: 0.8
							}, {
								duration: 200
							});
						}
					});
				}
			};
		}
	};
	self.clearInterface = function () {
		// Remove all children
		while (self.children.length > 0) {
			self.children[0].destroy();
		}
		self.upgradeButtons = [];
		self.categoryHeaders = [];
		self.background = null;
	};
	return self;
});
var UpgradeTree = Container.expand(function () {
	var self = Container.call(this);
	// Initialize upgrade data from storage
	self.upgrades = storage.upgrades || {
		// Combat upgrades
		damage: 0,
		// 0-5: +20% damage per level
		attackSpeed: 0,
		// 0-3: +25% attack speed per level
		knifePenetration: 0,
		// 0-3: knives can hit multiple enemies
		// Defense upgrades
		maxHealth: 0,
		// 0-5: +1 max health per level
		damageReduction: 0,
		// 0-3: +15% damage reduction per level
		invulnerability: 0,
		// 0-2: +1 second invulnerability per level
		// Utility upgrades
		moveSpeed: 0,
		// 0-3: +25% movement speed per level
		startingKnives: 0,
		// 0-4: +2 starting knives per level
		coinMagnetism: 0 // 0-3: increased coin collection range per level
	};
	// Upgrade costs (coins required)
	self.upgradeCosts = {
		damage: [100, 200, 400, 800, 1600],
		attackSpeed: [150, 300, 600],
		knifePenetration: [200, 500, 1000],
		maxHealth: [80, 160, 320, 640, 1280],
		damageReduction: [120, 240, 480],
		invulnerability: [300, 600],
		moveSpeed: [100, 200, 400],
		startingKnives: [150, 300, 600, 1200],
		coinMagnetism: [100, 250, 500]
	};
	self.coins = storage.coins || 0;
	self.canUpgrade = function (upgradeType) {
		var currentLevel = self.upgrades[upgradeType];
		var maxLevel = self.upgradeCosts[upgradeType].length;
		if (currentLevel >= maxLevel) return false;
		return self.coins >= self.upgradeCosts[upgradeType][currentLevel];
	};
	self.purchaseUpgrade = function (upgradeType) {
		if (!self.canUpgrade(upgradeType)) return false;
		var currentLevel = self.upgrades[upgradeType];
		var cost = self.upgradeCosts[upgradeType][currentLevel];
		self.coins -= cost;
		self.upgrades[upgradeType]++;
		// Save to storage
		storage.upgrades = self.upgrades;
		storage.coins = self.coins;
		return true;
	};
	self.addCoins = function (amount) {
		self.coins += amount;
		storage.coins = self.coins;
	};
	// Get upgrade bonuses
	self.getDamageMultiplier = function () {
		return 1 + self.upgrades.damage * 0.2;
	};
	self.getAttackSpeedMultiplier = function () {
		return 1 + self.upgrades.attackSpeed * 0.25;
	};
	self.getKnifePenetration = function () {
		return self.upgrades.knifePenetration;
	};
	self.getMaxHealthBonus = function () {
		return self.upgrades.maxHealth;
	};
	self.getDamageReduction = function () {
		return self.upgrades.damageReduction * 0.15;
	};
	self.getInvulnerabilityBonus = function () {
		return self.upgrades.invulnerability * 1000; // milliseconds
	};
	self.getMoveSpeedMultiplier = function () {
		return 1 + self.upgrades.moveSpeed * 0.25;
	};
	self.getStartingKnivesBonus = function () {
		return self.upgrades.startingKnives * 2;
	};
	self.getCoinMagnetismRange = function () {
		return 100 + self.upgrades.coinMagnetism * 50;
	};
	return self;
});
var WeaponTrail = Container.expand(function () {
	var self = Container.call(this);
	var trailGraphics = self.attachAsset('slashEffect', {
		anchorX: 0.5,
		anchorY: 0.5,
		alpha: 0.6,
		tint: 0xFFFFFF
	});
	self.lifetime = 30; // Short trail life
	self.maxLifetime = 30;
	self.reset = function (startX, startY, rotation, scale, trailType) {
		self.x = startX;
		self.y = startY;
		self.lifetime = self.maxLifetime;
		trailGraphics.rotation = rotation;
		trailGraphics.scaleX = scale;
		trailGraphics.scaleY = scale * 0.5; // Flatter trail
		trailGraphics.alpha = 0.6;
		self.visible = true;
		// Different trail effects based to type
		if (trailType === 'critical') {
			trailGraphics.tint = 0xFFD700; // Golden trail for critical hits
			trailGraphics.scaleY = scale * 0.8; // Thicker trail
		} else if (trailType === 'power') {
			trailGraphics.tint = 0xFF4488; // Purple trail for powered attacks
		} else {
			trailGraphics.tint = 0xFFFFFF; // White trail for normal
		}
	};
	self.update = function () {
		self.lifetime--;
		// Fade and shrink over time
		var lifeRatio = self.lifetime / self.maxLifetime;
		trailGraphics.alpha = 0.6 * lifeRatio;
		trailGraphics.scaleX *= 0.98;
		trailGraphics.scaleY *= 0.96;
		if (self.lifetime <= 0) {
			// Remove from weaponTrails array
			for (var i = weaponTrails.length - 1; i >= 0; i--) {
				if (weaponTrails[i] === self) {
					weaponTrails.splice(i, 1);
					break;
				}
			}
			returnToPool(self, 'weaponTrail');
		}
	};
	return self;
});
/**** 
* Initialize Game
****/ 
var game = new LK.Game({
	backgroundColor: 0x2c3e50,
	title: 'Dungeon Crawler'
});
/**** 
* Game Code
****/ 
// Legacy Boss class for backward compatibility (delegates to BossType1)
// Game variables
var Boss = BossType1;
// Upgrade system
var upgradeTree = new UpgradeTree();
var upgradeMenu = new UpgradeMenu();
var showUpgradesButton = null;
// Visual effect functions
function createGlowOutline(target, color, intensity) {
	var outline = target.addChild(LK.getAsset('slashEffect', {
		anchorX: 0.5,
		anchorY: 0.5,
		alpha: 0,
		tint: color,
		scaleX: 1.2,
		scaleY: 1.5
	}));
	outline.x = 0;
	outline.y = -70; // Center on enemy
	target.glowOutline = outline;
	tween(outline, {
		alpha: intensity * 0.4,
		scaleX: 1.4,
		scaleY: 1.8
	}, {
		duration: 300,
		easing: tween.easeInOut
	});
	// Pulse effect
	function pulseGlow() {
		if (outline.parent) {
			tween(outline, {
				alpha: intensity * 0.2
			}, {
				duration: 500,
				easing: tween.easeInOut,
				onFinish: function onFinish() {
					if (outline.parent) {
						tween(outline, {
							alpha: intensity * 0.4
						}, {
							duration: 500,
							easing: tween.easeInOut,
							onFinish: pulseGlow
						});
					}
				}
			});
		}
	}
	pulseGlow();
}
function removeGlowOutline(target) {
	if (target.glowOutline) {
		tween(target.glowOutline, {
			alpha: 0
		}, {
			duration: 200,
			onFinish: function onFinish() {
				if (target.glowOutline) {
					target.glowOutline.destroy();
					target.glowOutline = null;
				}
			}
		});
	}
}
function createScreenFlash(color, duration, intensity) {
	var flashOverlay = LK.gui.center.addChild(LK.getAsset('backgroundTile', {
		anchorX: 0.5,
		anchorY: 0.5,
		alpha: 0,
		tint: color,
		scaleX: 20,
		scaleY: 20
	}));
	flashOverlay.x = 0;
	flashOverlay.y = 0;
	tween(flashOverlay, {
		alpha: intensity || 0.3
	}, {
		duration: duration * 0.2,
		easing: tween.easeIn,
		onFinish: function onFinish() {
			tween(flashOverlay, {
				alpha: 0
			}, {
				duration: duration * 0.8,
				easing: tween.easeOut,
				onFinish: function onFinish() {
					flashOverlay.destroy();
				}
			});
		}
	});
}
var screenShake = new ScreenShake();
// Helper function to trigger screen shake with different intensities
function triggerScreenShake(intensity, duration, easing) {
	screenShake.shake(intensity, duration, easing);
}
var hero;
var enemies = [];
var coins = [];
var powerups = [];
var enemyWarnings = [];
var knives = [];
var knivesRemaining = 5;
var routeEffects = [];
var bloodParticles = [];
var bossProjectiles = [];
var impactSparks = [];
var dustClouds = [];
var magicalEffects = [];
var currentDungeon = 1;
var dungeonComplete = false;
var hearts = [];
var healthPotions = [];
var damageNumbers = [];
var weaponTrails = [];
// Object pooling system
var objectPools = {
	bloodParticle: [],
	bossProjectile: [],
	coin: [],
	healthPotion: [],
	impactSpark: [],
	dustCloud: [],
	magicalEffect: [],
	damageNumber: [],
	weaponTrail: []
};
// 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;
	}
};
// Group AI communication system
var groupAI = {
	communications: [],
	// Store recent communications
	alertRadius: 400,
	// Radius for alert propagation
	flankingCoordination: [],
	// Store flanking coordination data
	leaderBuffs: [],
	// Store active leader buffs
	packHunters: [],
	// Track pack hunting groups
	// Communicate alert state between enemies
	broadcastAlert: function broadcastAlert(enemy, alertType, priority) {
		var communication = {
			source: enemy,
			type: alertType,
			// 'spotted_hero', 'under_attack', 'flanking_position', 'retreat'
			priority: priority || 1,
			timestamp: LK.ticks,
			x: enemy.x,
			y: enemy.y
		};
		this.communications.push(communication);
		// Clean old communications (older than 3 seconds)
		for (var i = this.communications.length - 1; i >= 0; i--) {
			if (LK.ticks - this.communications[i].timestamp > 180) {
				this.communications.splice(i, 1);
			}
		}
	},
	// Get recent communications within range
	getCommunications: function getCommunications(enemy, maxAge, maxDistance) {
		var relevant = [];
		maxAge = maxAge || 120; // 2 seconds default
		maxDistance = maxDistance || this.alertRadius;
		for (var i = 0; i < this.communications.length; i++) {
			var comm = this.communications[i];
			if (LK.ticks - comm.timestamp <= maxAge && comm.source !== enemy) {
				var distance = Math.sqrt(Math.pow(enemy.x - comm.x, 2) + Math.pow(enemy.y - comm.y, 2));
				if (distance <= maxDistance) {
					relevant.push(comm);
				}
			}
		}
		return relevant;
	},
	// Calculate optimal flanking positions
	calculateFlankingPositions: function calculateFlankingPositions(heroX, heroY) {
		var positions = [];
		var angles = [Math.PI * 0.25, Math.PI * 0.75, Math.PI * 1.25, Math.PI * 1.75]; // 45, 135, 225, 315 degrees
		var distance = 300;
		for (var i = 0; i < angles.length; i++) {
			var x = heroX + Math.cos(angles[i]) * distance;
			var y = heroY + Math.sin(angles[i]) * distance;
			// Keep within bounds
			x = Math.max(100, Math.min(x, currentLevelData.width - 100));
			y = Math.max(1600, Math.min(y, 2400));
			positions.push({
				x: x,
				y: y,
				angle: angles[i],
				occupied: false
			});
		}
		return positions;
	},
	// Assign flanking position to enemy
	assignFlankingPosition: function assignFlankingPosition(enemy) {
		var positions = this.calculateFlankingPositions(hero.x, hero.y);
		var assigned = null;
		// Find closest unoccupied position
		var minDistance = Infinity;
		for (var i = 0; i < positions.length; i++) {
			if (!positions[i].occupied) {
				var distance = Math.sqrt(Math.pow(enemy.x - positions[i].x, 2) + Math.pow(enemy.y - positions[i].y, 2));
				if (distance < minDistance) {
					minDistance = distance;
					assigned = positions[i];
				}
			}
		}
		if (assigned) {
			assigned.occupied = true;
			enemy.flankingTarget = {
				x: assigned.x,
				y: assigned.y
			};
			enemy.isAssignedFlanker = true;
		}
	}
};
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();
	} else if (type === 'impactSpark') {
		return new ImpactSpark();
	} else if (type === 'dustCloud') {
		return new DustCloud();
	} else if (type === 'magicalEffect') {
		return new MagicalEffect();
	} else if (type === 'damageNumber') {
		return new DamageNumber();
	} else if (type === 'weaponTrail') {
		return new WeaponTrail();
	}
	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
};
// Ensure movement state is properly initialized
function resetMovementState() {
	movementState.left = false;
	movementState.right = false;
	movementState.up = false;
	movementState.down = false;
}
// Initialize clean movement state
resetMovementState();
// Camera system
var camera = {
	x: 0,
	y: 0,
	targetX: 0,
	targetY: 0,
	smoothing: 0.1
};
// Dungeon configuration
var levels = [{
	enemies: [{
		type: 'basic',
		count: 3
	}, {
		type: 'scout',
		count: 1
	}],
	width: 4096,
	height: 2732
}, {
	enemies: [{
		type: 'basic',
		count: 4
	}, {
		type: 'strong',
		count: 2
	}, {
		type: 'scout',
		count: 1
	}, {
		type: 'archer',
		count: 1
	}],
	width: 5120,
	height: 2732
}, {
	enemies: [{
		type: 'basic',
		count: 5
	}, {
		type: 'strong',
		count: 2
	}, {
		type: 'fast',
		count: 2
	}, {
		type: 'scout',
		count: 2
	}, {
		type: 'archer',
		count: 1
	}, {
		type: 'berserker',
		count: 1
	}],
	width: 6144,
	height: 2732
}, {
	enemies: [{
		type: 'basic',
		count: 4
	}, {
		type: 'strong',
		count: 3
	}, {
		type: 'fast',
		count: 2
	}, {
		type: 'tank',
		count: 1
	}, {
		type: 'scout',
		count: 2
	}, {
		type: 'archer',
		count: 2
	}, {
		type: 'berserker',
		count: 1
	}, {
		type: 'shield',
		count: 1
	}],
	width: 7168,
	height: 2732
}, {
	enemies: [{
		type: 'basic',
		count: 6
	}, {
		type: 'strong',
		count: 3
	}, {
		type: 'fast',
		count: 3
	}, {
		type: 'tank',
		count: 2
	}, {
		type: 'hunter',
		count: 2
	}, {
		type: 'scout',
		count: 3
	}, {
		type: 'archer',
		count: 2
	}, {
		type: 'berserker',
		count: 2
	}, {
		type: 'shield',
		count: 1
	}],
	width: 8192,
	height: 2732
}, {
	enemies: [{
		type: 'boss1',
		count: 1
	}],
	width: 9216,
	height: 2732
}, {
	enemies: [{
		type: 'basic',
		count: 4
	}, {
		type: 'strong',
		count: 2
	}, {
		type: 'fast',
		count: 3
	}, {
		type: 'tank',
		count: 2
	}, {
		type: 'hunter',
		count: 2
	}, {
		type: 'assassin',
		count: 2
	}, {
		type: 'leader',
		count: 1
	}, {
		type: 'scout',
		count: 2
	}, {
		type: 'archer',
		count: 3
	}, {
		type: 'berserker',
		count: 2
	}, {
		type: 'shield',
		count: 2
	}, {
		type: 'boss2',
		count: 1
	}],
	width: 10240,
	height: 2732
}, {
	enemies: [{
		type: 'basic',
		count: 3
	}, {
		type: 'strong',
		count: 2
	}, {
		type: 'fast',
		count: 3
	}, {
		type: 'tank',
		count: 2
	}, {
		type: 'hunter',
		count: 3
	}, {
		type: 'assassin',
		count: 3
	}, {
		type: 'leader',
		count: 2
	}, {
		type: 'scout',
		count: 3
	}, {
		type: 'archer',
		count: 4
	}, {
		type: 'berserker',
		count: 3
	}, {
		type: 'shield',
		count: 2
	}, {
		type: 'boss3',
		count: 1
	}, {
		type: 'boss1',
		count: 1
	}],
	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;
// Add upgrades button
showUpgradesButton = LK.getAsset('backgroundTile', {
	anchorX: 0.5,
	anchorY: 0.5,
	tint: 0x9b59b6,
	scaleX: 2,
	scaleY: 0.8
});
showUpgradesButton.x = -150;
showUpgradesButton.y = 350;
LK.gui.topRight.addChild(showUpgradesButton);
var upgradesText = new Text2('UPGRADES', {
	size: 40,
	fill: 0xFFFFFF
});
upgradesText.anchor.set(0.5, 0.5);
upgradesText.x = -150;
upgradesText.y = 350;
LK.gui.topRight.addChild(upgradesText);
// Add coins display
var coinsDisplay = new Text2('Coins: ' + upgradeTree.coins, {
	size: 50,
	fill: 0xF1C40F
});
coinsDisplay.anchor.set(1, 0);
coinsDisplay.x = -50;
coinsDisplay.y = 400;
LK.gui.topRight.addChild(coinsDisplay);
showUpgradesButton.down = function () {
	if (!upgradeMenu.isVisible) {
		upgradeMenu.show(upgradeTree);
		LK.gui.center.addChild(upgradeMenu);
	}
};
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
// Apply upgrade bonuses to hero
hero.maxHealth = 5 + upgradeTree.getMaxHealthBonus();
hero.health = hero.maxHealth;
hero.baseSpeed = 8;
hero.speed = hero.baseSpeed * upgradeTree.getMoveSpeedMultiplier();
hero.damageMultiplier = upgradeTree.getDamageMultiplier();
hero.attackSpeedMultiplier = upgradeTree.getAttackSpeedMultiplier();
hero.damageReduction = upgradeTree.getDamageReduction();
hero.invulnerabilityBonus = upgradeTree.getInvulnerabilityBonus();
// 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');
	if (coinsDisplay) {
		coinsDisplay.setText('Coins: ' + upgradeTree.coins);
	}
}
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 impact sparks
	for (var i = impactSparks.length - 1; i >= 0; i--) {
		impactSparks[i].destroy();
	}
	impactSparks = [];
	// Clear existing dust clouds
	for (var i = dustClouds.length - 1; i >= 0; i--) {
		dustClouds[i].destroy();
	}
	dustClouds = [];
	// Clear existing magical effects
	for (var i = magicalEffects.length - 1; i >= 0; i--) {
		magicalEffects[i].destroy();
	}
	magicalEffects = [];
	// 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 = [];
	// Clear existing damage numbers
	for (var i = damageNumbers.length - 1; i >= 0; i--) {
		damageNumbers[i].destroy();
	}
	damageNumbers = [];
	// Clear existing weapon trails
	for (var i = weaponTrails.length - 1; i >= 0; i--) {
		weaponTrails[i].destroy();
	}
	weaponTrails = [];
	// Clear group AI data
	groupAI.communications = [];
	groupAI.flankingCoordination = [];
	groupAI.leaderBuffs = [];
	groupAI.packHunters = [];
	// Reset knife count with upgrade bonus
	knivesRemaining = 5 + upgradeTree.getStartingKnivesBonus();
	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') {
		// Randomly choose boss type based on dungeon
		var bossTypes = [BossType1, BossType2, BossType3];
		var BossClass = bossTypes[Math.floor(Math.random() * bossTypes.length)];
		enemy = game.addChild(new BossClass());
		// Boss spawns at center of level
		enemy.x = currentLevelData.width / 2;
		enemy.y = 2200;
	} else if (type === 'boss1') {
		enemy = game.addChild(new BossType1());
		enemy.x = currentLevelData.width / 2;
		enemy.y = 2200;
	} else if (type === 'boss2') {
		enemy = game.addChild(new BossType2());
		enemy.x = currentLevelData.width / 2;
		enemy.y = 2200;
	} else if (type === 'boss3') {
		enemy = game.addChild(new BossType3());
		enemy.x = currentLevelData.width / 2;
		enemy.y = 2200;
	} else if (type === 'leader') {
		enemy = game.addChild(new Leader());
		// Leaders spawn in strategic positions
		enemy.x = currentLevelData.width / 2 + (Math.random() - 0.5) * 200;
		enemy.y = 1900 + Math.random() * 200;
	} else if (type === 'scout') {
		enemy = game.addChild(new Scout());
		// Scouts spawn at level edges for early warning
		enemy.x = Math.random() < 0.5 ? 200 + Math.random() * 300 : currentLevelData.width - 500 + Math.random() * 300;
		enemy.y = 1700 + Math.random() * 600;
	} else if (type === 'berserker') {
		enemy = game.addChild(new Berserker());
		// Berserkers spawn closer to center for aggressive positioning
		enemy.x = currentLevelData.width / 2 + (Math.random() - 0.5) * 600;
		enemy.y = 1800 + Math.random() * 400;
	} else if (type === 'archer') {
		enemy = game.addChild(new Archer());
		// Archers spawn at elevated positions (back areas)
		enemy.x = 300 + Math.random() * (currentLevelData.width - 600);
		enemy.y = 1600 + Math.random() * 200; // Higher ground
	} else if (type === 'shield') {
		enemy = game.addChild(new Shield());
		// Shields spawn in protective positions
		enemy.x = currentLevelData.width / 2 + (Math.random() - 0.5) * 400;
		enemy.y = 1850 + Math.random() * 300;
	} 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;
	resetMovementState(); // Reset all movement when any button is released
};
// 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;
	resetMovementState(); // Reset all movement when any button is released
};
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;
	resetMovementState(); // Reset all movement when any button is released
};
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;
	resetMovementState(); // Reset all movement when any button is released
};
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 - only move if button is actively pressed
	if (movementState.left === true) {
		hero.move('left');
		isMovingThisFrame = true;
	}
	if (movementState.right === true) {
		hero.move('right');
		isMovingThisFrame = true;
	}
	if (movementState.up === true) {
		hero.move('up');
		isMovingThisFrame = true;
	}
	if (movementState.down === true) {
		hero.move('down');
		isMovingThisFrame = true;
	}
	// Stop walking animation if no movement this frame
	if (wasMoving && !isMovingThisFrame) {
		hero.stopWalkAnimation();
	}
	// Update screen shake
	screenShake.update();
	// 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 with screen shake offset
	game.x = -camera.x + screenShake.getOffsetX();
	game.y = -camera.y + screenShake.getOffsetY();
	// 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--) {
		// Safety check to ensure coin exists at this index
		if (coins[i] && isVisible(coins[i], 100)) {
			// Check for coin magnetism
			var coin = coins[i];
			var distanceToHero = Math.sqrt(Math.pow(coin.x - hero.x, 2) + Math.pow(coin.y - hero.y, 2));
			var magnetRange = upgradeTree.getCoinMagnetismRange();
			if (distanceToHero < magnetRange) {
				// Move coin toward hero
				var dx = hero.x - coin.x;
				var dy = hero.y - coin.y;
				var distance = Math.sqrt(dx * dx + dy * dy);
				if (distance > 0) {
					var magnetSpeed = 8;
					coin.x += dx / distance * magnetSpeed;
					coin.y += dy / distance * magnetSpeed;
				}
				// Collect if very close
				if (distanceToHero < 50) {
					upgradeTree.addCoins(10);
					coinsDisplay.setText('Coins: ' + upgradeTree.coins);
					coin.collect();
				}
			}
			// Additional safety check before calling update
			if (coins[i]) {
				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 = impactSparks.length - 1; i >= 0; i--) {
		if (isVisible(impactSparks[i], 50)) {
			impactSparks[i].update();
		}
	}
	for (var i = dustClouds.length - 1; i >= 0; i--) {
		if (isVisible(dustClouds[i], 100)) {
			dustClouds[i].update();
		}
	}
	for (var i = magicalEffects.length - 1; i >= 0; i--) {
		if (isVisible(magicalEffects[i], 100)) {
			magicalEffects[i].update();
		}
	}
	for (var i = bossProjectiles.length - 1; i >= 0; i--) {
		var projectile = bossProjectiles[i];
		// Check if any shield enemy can block this projectile
		var blocked = false;
		for (var j = 0; j < enemies.length; j++) {
			var enemy = enemies[j];
			if (enemy.enemyType === 'shield' && enemy.blockProjectile) {
				if (enemy.blockProjectile(projectile)) {
					blocked = true;
					break;
				}
			}
		}
		// Only update projectile if it wasn't blocked
		if (!blocked) {
			projectile.update(); // Always update projectiles as they move fast
		}
	}
	// Update coins display to reflect automatic coin collection
	updateScoreDisplay();
	for (var i = damageNumbers.length - 1; i >= 0; i--) {
		if (isVisible(damageNumbers[i], 100)) {
			damageNumbers[i].update();
		}
	}
	for (var i = weaponTrails.length - 1; i >= 0; i--) {
		weaponTrails[i].update(); // Always update trails for smooth effect
	}
	// 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();
	}
	// Special check for dungeon 7 boss - kill boss when all soldiers are dead
	if (currentDungeon === 7 && !dungeonComplete && enemies.length > 0) {
		var bossCount = 0;
		var summonerBoss = null;
		var otherEnemyCount = 0;
		// Count bosses and other enemies
		for (var i = 0; i < enemies.length; i++) {
			var enemy = enemies[i];
			if (enemy.bossType === 'summoner') {
				bossCount++;
				summonerBoss = enemy;
				// Reduce boss health specifically for dungeon 7
				if (summonerBoss.health > 25) {
					summonerBoss.health = 25;
					summonerBoss.maxHealth = 25;
				}
			} else {
				otherEnemyCount++;
			}
		}
		// If only summoner boss remains (all soldiers dead), kill the boss
		if (bossCount === 1 && otherEnemyCount === 0 && summonerBoss) {
			summonerBoss.die();
		}
	}
	// 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();
		}
	}
};