/**** 
* 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();
		}
	}
};