/**** 
* Plugins
****/ 
var tween = LK.import("@upit/tween.v1");
/**** 
* Classes
****/ 
var Bullet = Container.expand(function (startX, startY, targetEnemy, damage, speed) {
	var self = Container.call(this);
	self.targetEnemy = targetEnemy;
	self.damage = damage || 10;
	self.speed = speed || 5;
	self.x = startX;
	self.y = startY;
	var bulletAsset = 'ice_bullet'; // Default bullet type
	if (self.type) {
		bulletAsset = self.type + '_bullet';
	}
	var bulletGraphics = self.attachAsset(bulletAsset, {
		anchorX: 0.5,
		anchorY: 0.5
	});
	self.update = function () {
		if (!self.targetEnemy || !self.targetEnemy.parent) {
			self.destroy();
			return;
		}
		var dx = self.targetEnemy.x - self.x;
		var dy = self.targetEnemy.y - self.y;
		var distance = Math.sqrt(dx * dx + dy * dy);
		if (distance < self.speed) {
			// Apply damage to target enemy
			self.targetEnemy.health -= self.damage;
			// Play hit sound effect
			LK.getSound('enemy_hit').play();
			// Create hit particle effect based on damage type
			var hitEffect = new Container();
			game.addChild(hitEffect);
			hitEffect.x = self.targetEnemy.x;
			hitEffect.y = self.targetEnemy.y;
			// Create multiple small particles for hit effect
			for (var particle = 0; particle < 4; particle++) {
				var particleContainer = new Container();
				hitEffect.addChild(particleContainer);
				var particleGraphics = particleContainer.attachAsset('rangeCircle', {
					anchorX: 0.5,
					anchorY: 0.5
				});
				particleGraphics.width = particleGraphics.height = 8 + Math.random() * 12;
				// Color particles based on bullet type
				switch (self.type) {
					case 'laser':
						particleGraphics.tint = 0xFF0080;
						break;
					case 'ice':
						particleGraphics.tint = 0x00FFFF;
						break;
					case 'missile':
						particleGraphics.tint = 0xFF4500;
						break;
					case 'lightning':
						particleGraphics.tint = 0xFFFF00;
						break;
					case 'cannon':
						particleGraphics.tint = 0x8B4513;
						break;
					case 'tesla':
						particleGraphics.tint = 0x9932CC;
						break;
					default:
						particleGraphics.tint = 0xFF6B35;
					// Orange sparks
				}
				// Random direction for each particle
				var angle = Math.random() * Math.PI * 2;
				var distance = 20 + Math.random() * 30;
				var targetX = Math.cos(angle) * distance;
				var targetY = Math.sin(angle) * distance;
				particleContainer.alpha = 1;
				// Animate particles flying outward
				tween(particleContainer, {
					x: targetX,
					y: targetY,
					alpha: 0,
					scaleX: 0.3,
					scaleY: 0.3
				}, {
					duration: 300 + Math.random() * 200,
					easing: tween.easeOut
				});
			}
			// Clean up hit effect after animation
			LK.setTimeout(function () {
				hitEffect.destroy();
			}, 600);
			if (self.targetEnemy.health <= 0) {
				self.targetEnemy.health = 0;
			} else {
				self.targetEnemy.healthBar.width = self.targetEnemy.health / self.targetEnemy.maxHealth * 70;
			}
			// Create visual hit effects based on bullet type
			if (self.type === 'laser') {
				// Laser: bright flash and scorched mark
				var laserFlash = new Container();
				game.addChild(laserFlash);
				laserFlash.x = self.targetEnemy.x;
				laserFlash.y = self.targetEnemy.y;
				var flashGraphics = laserFlash.attachAsset('rangeCircle', {
					anchorX: 0.5,
					anchorY: 0.5
				});
				flashGraphics.width = flashGraphics.height = CELL_SIZE * 0.8;
				flashGraphics.tint = 0xFF0080;
				flashGraphics.alpha = 1;
				tween(laserFlash, {
					alpha: 0,
					scaleX: 1.5,
					scaleY: 1.5
				}, {
					duration: 200,
					easing: tween.easeOut,
					onFinish: function onFinish() {
						laserFlash.destroy();
					}
				});
			} else if (self.type === 'ice') {
				// Ice: shattering effect and frost overlay
				var iceShatter = new Container();
				game.addChild(iceShatter);
				iceShatter.x = self.targetEnemy.x;
				iceShatter.y = self.targetEnemy.y;
				var shatterGraphics = iceShatter.attachAsset('rangeCircle', {
					anchorX: 0.5,
					anchorY: 0.5
				});
				shatterGraphics.width = shatterGraphics.height = CELL_SIZE * 0.6;
				shatterGraphics.tint = 0x00FFFF;
				shatterGraphics.alpha = 0.9;
				tween(iceShatter, {
					alpha: 0,
					scaleX: 2,
					scaleY: 2
				}, {
					duration: 300,
					easing: tween.easeOut,
					onFinish: function onFinish() {
						iceShatter.destroy();
					}
				});
				// Add frost overlay on enemy if not immune
				if (!self.targetEnemy.isImmune) {
					self.targetEnemy.frostOverlay = true;
				}
			} else if (self.type === 'missile') {
				// Missile: explosion with debris and knockback
				var explosion = new Container();
				game.addChild(explosion);
				explosion.x = self.targetEnemy.x;
				explosion.y = self.targetEnemy.y;
				var explosionGraphics = explosion.attachAsset('rangeCircle', {
					anchorX: 0.5,
					anchorY: 0.5
				});
				explosionGraphics.width = explosionGraphics.height = CELL_SIZE * 1.2;
				explosionGraphics.tint = 0xFF4500;
				explosionGraphics.alpha = 1;
				tween(explosion, {
					alpha: 0,
					scaleX: 2.5,
					scaleY: 2.5
				}, {
					duration: 400,
					easing: tween.easeOut,
					onFinish: function onFinish() {
						explosion.destroy();
					}
				});
				// Knockback effect on enemy
				if (self.targetEnemy.children[0]) {
					var originalX = self.targetEnemy.x;
					var originalY = self.targetEnemy.y;
					tween(self.targetEnemy, {
						x: originalX + (Math.random() - 0.5) * 20,
						y: originalY + (Math.random() - 0.5) * 20
					}, {
						duration: 150,
						easing: tween.easeOut,
						onFinish: function onFinish() {
							tween(self.targetEnemy, {
								x: originalX,
								y: originalY
							}, {
								duration: 100,
								easing: tween.easeIn
							});
						}
					});
				}
			} else if (self.type === 'lightning') {
				// Lightning: crackling energy discharge
				var lightningStrike = new Container();
				game.addChild(lightningStrike);
				lightningStrike.x = self.targetEnemy.x;
				lightningStrike.y = self.targetEnemy.y;
				var strikeGraphics = lightningStrike.attachAsset('rangeCircle', {
					anchorX: 0.5,
					anchorY: 0.5
				});
				strikeGraphics.width = strikeGraphics.height = CELL_SIZE * 1;
				strikeGraphics.tint = 0xFFFF00;
				strikeGraphics.alpha = 1;
				// Create flickering effect
				tween(lightningStrike, {
					alpha: 0.3
				}, {
					duration: 50,
					easing: tween.linear,
					onFinish: function onFinish() {
						tween(lightningStrike, {
							alpha: 1
						}, {
							duration: 50,
							easing: tween.linear,
							onFinish: function onFinish() {
								tween(lightningStrike, {
									alpha: 0,
									scaleX: 1.8,
									scaleY: 1.8
								}, {
									duration: 200,
									easing: tween.easeOut,
									onFinish: function onFinish() {
										lightningStrike.destroy();
									}
								});
							}
						});
					}
				});
			} else if (self.type === 'cannon') {
				// Cannon: impact crater and enemy shake
				var crater = new Container();
				game.addChild(crater);
				crater.x = self.targetEnemy.x;
				crater.y = self.targetEnemy.y + 20; // Slightly below enemy
				var craterGraphics = crater.attachAsset('rangeCircle', {
					anchorX: 0.5,
					anchorY: 0.5
				});
				craterGraphics.width = craterGraphics.height = CELL_SIZE * 0.7;
				craterGraphics.tint = 0x8B4513;
				craterGraphics.alpha = 0.8;
				tween(crater, {
					alpha: 0,
					scaleX: 1.5,
					scaleY: 1.5
				}, {
					duration: 500,
					easing: tween.easeOut,
					onFinish: function onFinish() {
						crater.destroy();
					}
				});
				// Enemy shake effect
				if (self.targetEnemy.children[0]) {
					var shakeCount = 0;
					var _shakeTimer = function shakeTimer() {
						if (shakeCount < 6) {
							self.targetEnemy.x += (Math.random() - 0.5) * 8;
							self.targetEnemy.y += (Math.random() - 0.5) * 8;
							shakeCount++;
							LK.setTimeout(_shakeTimer, 30);
						}
					};
					_shakeTimer();
				}
			} else if (self.type === 'tesla') {
				// Tesla: electrical arc connecting tower to enemy
				var electricArc = new Container();
				game.addChild(electricArc);
				electricArc.x = self.targetEnemy.x;
				electricArc.y = self.targetEnemy.y;
				var arcGraphics = electricArc.attachAsset('rangeCircle', {
					anchorX: 0.5,
					anchorY: 0.5
				});
				arcGraphics.width = arcGraphics.height = CELL_SIZE * 0.9;
				arcGraphics.tint = 0x9932CC;
				arcGraphics.alpha = 1;
				// Create pulsing electric effect
				tween(electricArc, {
					scaleX: 1.3,
					scaleY: 1.3,
					alpha: 0.7
				}, {
					duration: 100,
					easing: tween.easeOut,
					onFinish: function onFinish() {
						tween(electricArc, {
							scaleX: 1,
							scaleY: 1,
							alpha: 1
						}, {
							duration: 100,
							easing: tween.easeIn,
							onFinish: function onFinish() {
								tween(electricArc, {
									alpha: 0,
									scaleX: 1.5,
									scaleY: 1.5
								}, {
									duration: 250,
									easing: tween.easeOut,
									onFinish: function onFinish() {
										electricArc.destroy();
									}
								});
							}
						});
					}
				});
			}
			// Apply special effects based on bullet type
			if (self.type === 'splash') {
				// Create visual splash effect
				var splashEffect = new EffectIndicator(self.targetEnemy.x, self.targetEnemy.y, 'splash');
				game.addChild(splashEffect);
				// Splash damage to nearby enemies
				var splashRadius = CELL_SIZE * 1.5;
				for (var i = 0; i < enemies.length; i++) {
					var otherEnemy = enemies[i];
					if (otherEnemy !== self.targetEnemy) {
						var splashDx = otherEnemy.x - self.targetEnemy.x;
						var splashDy = otherEnemy.y - self.targetEnemy.y;
						var splashDistance = Math.sqrt(splashDx * splashDx + splashDy * splashDy);
						if (splashDistance <= splashRadius) {
							// Apply splash damage (50% of original damage)
							otherEnemy.health -= self.damage * 0.5;
							if (otherEnemy.health <= 0) {
								otherEnemy.health = 0;
							} else {
								otherEnemy.healthBar.width = otherEnemy.health / otherEnemy.maxHealth * 70;
							}
						}
					}
				}
			} else if (self.type === 'slow') {
				// Prevent slow effect on immune enemies
				if (!self.targetEnemy.isImmune) {
					// Create visual slow effect
					var slowEffect = new EffectIndicator(self.targetEnemy.x, self.targetEnemy.y, 'slow');
					game.addChild(slowEffect);
					// Apply slow effect
					// Make slow percentage scale with tower level (default 50%, up to 80% at max level)
					var slowPct = 0.5;
					if (self.sourceTowerLevel !== undefined) {
						// Scale: 50% at level 1, 60% at 2, 65% at 3, 70% at 4, 75% at 5, 80% at 6
						var slowLevels = [0.5, 0.6, 0.65, 0.7, 0.75, 0.8];
						var idx = Math.max(0, Math.min(5, self.sourceTowerLevel - 1));
						slowPct = slowLevels[idx];
					}
					if (!self.targetEnemy.slowed) {
						self.targetEnemy.originalSpeed = self.targetEnemy.speed;
						self.targetEnemy.speed *= 1 - slowPct; // Slow by X%
						self.targetEnemy.slowed = true;
						self.targetEnemy.slowDuration = 180; // 3 seconds at 60 FPS
					} else {
						self.targetEnemy.slowDuration = 180; // Reset duration
					}
				}
			} else if (self.type === 'poison') {
				// Prevent poison effect on immune enemies
				if (!self.targetEnemy.isImmune) {
					// Create visual poison effect
					var poisonEffect = new EffectIndicator(self.targetEnemy.x, self.targetEnemy.y, 'poison');
					game.addChild(poisonEffect);
					// Apply poison effect
					self.targetEnemy.poisoned = true;
					self.targetEnemy.poisonDamage = self.damage * 0.2; // 20% of original damage per tick
					self.targetEnemy.poisonDuration = 300; // 5 seconds at 60 FPS
				}
			} else if (self.type === 'sniper') {
				// Create visual critical hit effect for sniper
				var sniperEffect = new EffectIndicator(self.targetEnemy.x, self.targetEnemy.y, 'sniper');
				game.addChild(sniperEffect);
			}
			self.destroy();
		} else {
			var angle = Math.atan2(dy, dx);
			self.x += Math.cos(angle) * self.speed;
			self.y += Math.sin(angle) * self.speed;
		}
	};
	return self;
});
var Cloud = Container.expand(function (x, y) {
	var self = Container.call(this);
	self.x = x;
	self.y = y;
	var cloudGraphics = self.attachAsset('cloud', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	cloudGraphics.alpha = 0.6 + Math.random() * 0.3; // Semi-transparent
	cloudGraphics.scaleX = 0.8 + Math.random() * 0.6; // Random size
	cloudGraphics.scaleY = 0.8 + Math.random() * 0.6;
	cloudGraphics.tint = 0xF0F8FF; // Alice blue
	self.moveSpeed = 0.2 + Math.random() * 0.3; // Random speed
	self.startY = y;
	self.amplitude = 20 + Math.random() * 30; // Vertical drift amplitude
	self.update = function () {
		// Move cloud slowly to the right
		self.x += self.moveSpeed;
		// Add gentle vertical drift
		self.y = self.startY + Math.sin(LK.ticks * 0.01 + self.x * 0.001) * self.amplitude;
		// Reset position when off-screen
		if (self.x > 2048 + cloudGraphics.width) {
			self.x = -cloudGraphics.width;
			self.startY = 100 + Math.random() * 300; // New random height
		}
	};
	return self;
});
var DebugCell = Container.expand(function () {
	var self = Container.call(this);
	var cellGraphics = self.attachAsset('cell', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	cellGraphics.tint = Math.random() * 0xffffff;
	var debugArrows = [];
	var numberLabel = new Text2('0', {
		size: 30,
		fill: 0xFFFFFF,
		weight: 800
	});
	numberLabel.anchor.set(.5, .5);
	self.addChild(numberLabel);
	self.update = function () {
		self.updateFootprint();
	};
	self.down = function () {
		return;
		if (self.cell.type == 0 || self.cell.type == 1) {
			self.cell.type = self.cell.type == 1 ? 0 : 1;
			if (grid.pathFind()) {
				self.cell.type = self.cell.type == 1 ? 0 : 1;
				grid.pathFind();
				var notification = game.addChild(new Notification("Path is blocked!"));
				notification.x = 2048 / 2;
				notification.y = grid.height - 50;
			}
			grid.renderDebug();
		}
	};
	self.removeArrows = function () {
		while (debugArrows.length) {
			self.removeChild(debugArrows.pop());
		}
	};
	// Water animation properties
	self.waterAnimationActive = false;
	self.footprintTimer = 0;
	self.originalTint = cellGraphics.tint;
	self.originalAlpha = cellGraphics.alpha;
	// Start water ripple animation for ocean cells
	self.startWaterAnimation = function () {
		if (self.waterAnimationActive) return;
		self.waterAnimationActive = true;
		var _animateWaterRipple = function animateWaterRipple() {
			if (!self.waterAnimationActive) return;
			// Create subtle wave effect by modulating alpha and scale
			tween(cellGraphics, {
				alpha: cellGraphics.alpha * 0.9,
				scaleX: 1.02,
				scaleY: 1.02
			}, {
				duration: 1500 + Math.random() * 1000,
				easing: tween.easeInOut,
				onFinish: function onFinish() {
					if (!self.waterAnimationActive) return;
					tween(cellGraphics, {
						alpha: self.originalAlpha,
						scaleX: 1.0,
						scaleY: 1.0
					}, {
						duration: 1500 + Math.random() * 1000,
						easing: tween.easeInOut,
						onFinish: _animateWaterRipple
					});
				}
			});
		};
		_animateWaterRipple();
	};
	// Stop water animation
	self.stopWaterAnimation = function () {
		self.waterAnimationActive = false;
		tween.stop(cellGraphics, {
			alpha: true,
			scaleX: true,
			scaleY: true
		});
		// Reset to original state
		cellGraphics.alpha = self.originalAlpha;
		cellGraphics.scaleX = 1.0;
		cellGraphics.scaleY = 1.0;
	};
	// Add enemy footprint effect
	self.addFootprint = function () {
		if (self.cell.type !== 0) return; // Only on path cells
		// Darken the cell temporarily to show footprint
		var originalTint = cellGraphics.tint;
		cellGraphics.tint = 0xE6D4A2; // Slightly darker sand
		// Reset footprint timer
		self.footprintTimer = 180; // 3 seconds at 60 FPS
	};
	// Update method for handling footprint fade
	self.updateFootprint = function () {
		if (self.footprintTimer > 0) {
			self.footprintTimer--;
			if (self.footprintTimer <= 0) {
				// Fade back to original color
				tween(cellGraphics, {
					tint: self.originalTint
				}, {
					duration: 500,
					easing: tween.easeOut
				});
			}
		}
	};
	self.render = function (data) {
		// Show beach-themed grid visuals
		cellGraphics.visible = true;
		numberLabel.visible = false;
		self.removeArrows();
		// Color cells based on type for beach theme
		if (self.cell.type === 0) {
			// Walkable path - beach sand color
			cellGraphics.tint = 0xF4E4BC;
			cellGraphics.alpha = 0.8;
		} else if (self.cell.type === 1) {
			// Wall - darker sand/rock color
			cellGraphics.tint = 0xD2B48C;
			cellGraphics.alpha = 0.9;
		} else if (self.cell.type === 2) {
			// Spawn - ocean water color
			cellGraphics.tint = 0x4682B4;
			cellGraphics.alpha = 0.8;
			// Start water ripple animation for spawn cells
			self.startWaterAnimation();
		} else if (self.cell.type === 3) {
			// Goal - tropical water color
			cellGraphics.tint = 0x00CED1;
			cellGraphics.alpha = 0.8;
			// Start water ripple animation for goal cells
			self.startWaterAnimation();
		}
	};
	// Override destroy to clean up animations
	var originalDestroy = self.destroy;
	self.destroy = function () {
		self.stopWaterAnimation();
		if (originalDestroy) {
			originalDestroy.call(self);
		}
	};
	return self;
});
// This update method was incorrectly placed here and should be removed
var EffectIndicator = Container.expand(function (x, y, type) {
	var self = Container.call(this);
	self.x = x;
	self.y = y;
	var effectGraphics = self.attachAsset('rangeCircle', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	effectGraphics.blendMode = 1;
	switch (type) {
		case 'splash':
			effectGraphics.tint = 0x39FF14;
			effectGraphics.width = effectGraphics.height = CELL_SIZE * 1.8;
			break;
		case 'slow':
			effectGraphics.tint = 0xC44FFF;
			effectGraphics.width = effectGraphics.height = CELL_SIZE * 1.2;
			break;
		case 'poison':
			effectGraphics.tint = 0x00FFA1;
			effectGraphics.width = effectGraphics.height = CELL_SIZE * 1.2;
			break;
		case 'sniper':
			effectGraphics.tint = 0xFF4500;
			effectGraphics.width = effectGraphics.height = CELL_SIZE * 0.8;
			break;
	}
	effectGraphics.alpha = 0.9;
	self.alpha = 0;
	// Animate the effect
	tween(self, {
		alpha: 0.8,
		scaleX: 1.5,
		scaleY: 1.5
	}, {
		duration: 200,
		easing: tween.easeOut,
		onFinish: function onFinish() {
			tween(self, {
				alpha: 0,
				scaleX: 2,
				scaleY: 2
			}, {
				duration: 300,
				easing: tween.easeIn,
				onFinish: function onFinish() {
					self.destroy();
				}
			});
		}
	});
	return self;
});
// Base enemy class for common functionality
var Enemy = Container.expand(function (type) {
	var self = Container.call(this);
	self.type = type || 'normal';
	self.speed = .01;
	self.cellX = 0;
	self.cellY = 0;
	self.currentCellX = 0;
	self.currentCellY = 0;
	self.currentTarget = undefined;
	self.maxHealth = 100;
	self.health = self.maxHealth;
	self.bulletsTargetingThis = [];
	self.waveNumber = currentWave;
	self.isFlying = false;
	self.isImmune = false;
	self.isBoss = false;
	// Check if this is a boss wave
	// Check if this is a boss wave
	// Apply different stats based on enemy type
	switch (self.type) {
		case 'fast':
			self.speed *= 2; // Twice as fast
			self.maxHealth = 100;
			break;
		case 'immune':
			self.isImmune = true;
			self.maxHealth = 80;
			break;
		case 'flying':
			self.isFlying = true;
			self.maxHealth = 80;
			break;
		case 'swarm':
			self.maxHealth = 50; // Weaker enemies
			break;
		case 'normal':
		default:
			// Normal enemy uses default values
			break;
	}
	if (currentWave % 10 === 0 && currentWave > 0 && type !== 'swarm') {
		self.isBoss = true;
		// Boss enemies have 20x health and are larger
		self.maxHealth *= 20;
		// Slower speed for bosses
		self.speed = self.speed * 0.7;
	}
	self.health = self.maxHealth;
	// Get appropriate asset for this enemy type
	var assetId = 'enemy';
	if (self.type !== 'normal') {
		assetId = 'enemy_' + self.type;
	}
	var enemyGraphics = self.attachAsset(assetId, {
		anchorX: 0.5,
		anchorY: 0.5
	});
	// Scale up boss enemies
	if (self.isBoss) {
		enemyGraphics.scaleX = 1.8;
		enemyGraphics.scaleY = 1.8;
	}
	// Fall back to regular enemy asset if specific type asset not found
	// Apply tint to differentiate enemy types
	/*switch (self.type) {
		case 'fast':
			enemyGraphics.tint = 0x00AAFF; // Blue for fast enemies
			break;
		case 'immune':
			enemyGraphics.tint = 0xAA0000; // Red for immune enemies
			break;
		case 'flying':
			enemyGraphics.tint = 0xFFFF00; // Yellow for flying enemies
			break;
		case 'swarm':
			enemyGraphics.tint = 0xFF00FF; // Pink for swarm enemies
			break;
	}*/
	// Create shadow for flying enemies
	if (self.isFlying) {
		// Create a shadow container that will be added to the shadow layer
		self.shadow = new Container();
		// Clone the enemy graphics for the shadow
		var shadowGraphics = self.shadow.attachAsset(assetId || 'enemy', {
			anchorX: 0.5,
			anchorY: 0.5
		});
		// Apply shadow effect
		shadowGraphics.tint = 0x000000; // Black shadow
		shadowGraphics.alpha = 0.4; // Semi-transparent
		// If this is a boss, scale up the shadow to match
		if (self.isBoss) {
			shadowGraphics.scaleX = 1.8;
			shadowGraphics.scaleY = 1.8;
		}
		// Position shadow slightly offset
		self.shadow.x = 20; // Offset right
		self.shadow.y = 20; // Offset down
		// Ensure shadow has the same rotation as the enemy
		shadowGraphics.rotation = enemyGraphics.rotation;
	}
	var healthBarOutline = self.attachAsset('healthBarOutline', {
		anchorX: 0,
		anchorY: 0.5
	});
	var healthBarBG = self.attachAsset('healthBar', {
		anchorX: 0,
		anchorY: 0.5
	});
	var healthBar = self.attachAsset('healthBar', {
		anchorX: 0,
		anchorY: 0.5
	});
	healthBarBG.y = healthBarOutline.y = healthBar.y = -enemyGraphics.height / 2 - 10;
	healthBarOutline.x = -healthBarOutline.width / 2;
	healthBarBG.x = healthBar.x = -healthBar.width / 2 - .5;
	healthBar.tint = 0x00ff00;
	healthBarBG.tint = 0xff0000;
	self.healthBar = healthBar;
	self.update = function () {
		if (self.health <= 0) {
			self.health = 0;
			self.healthBar.width = 0;
		}
		// Handle slow effect
		if (self.isImmune) {
			// Immune enemies cannot be slowed or poisoned, clear any such effects
			self.slowed = false;
			self.slowEffect = false;
			self.poisoned = false;
			self.poisonEffect = false;
			// Reset speed to original if needed
			if (self.originalSpeed !== undefined) {
				self.speed = self.originalSpeed;
			}
		} else {
			// Handle slow effect
			if (self.slowed) {
				// Visual indication of slowed status
				if (!self.slowEffect) {
					self.slowEffect = true;
				}
				self.slowDuration--;
				if (self.slowDuration <= 0) {
					self.speed = self.originalSpeed;
					self.slowed = false;
					self.slowEffect = false;
					// Only reset tint if not poisoned
					if (!self.poisoned) {
						enemyGraphics.tint = 0xFFFFFF; // Reset tint
					}
				}
			}
			// Handle poison effect
			if (self.poisoned) {
				// Visual indication of poisoned status
				if (!self.poisonEffect) {
					self.poisonEffect = true;
				}
				// Apply poison damage every 30 frames (twice per second)
				if (LK.ticks % 30 === 0) {
					self.health -= self.poisonDamage;
					if (self.health <= 0) {
						self.health = 0;
					}
					self.healthBar.width = self.health / self.maxHealth * 70;
				}
				self.poisonDuration--;
				if (self.poisonDuration <= 0) {
					self.poisoned = false;
					self.poisonEffect = false;
					// Only reset tint if not slowed
					if (!self.slowed) {
						enemyGraphics.tint = 0xFFFFFF; // Reset tint
					}
				}
			}
		}
		// Set tint based on effect status
		if (self.isImmune) {
			enemyGraphics.tint = 0xFFFFFF;
		} else if (self.poisoned && self.slowed) {
			// Combine poison (0x00FFAA) and slow (0x9900FF) colors
			// Simple average: R: (0+153)/2=76, G: (255+0)/2=127, B: (170+255)/2=212
			enemyGraphics.tint = 0x4C7FD4;
		} else if (self.poisoned) {
			enemyGraphics.tint = 0x00FFAA;
		} else if (self.slowed) {
			enemyGraphics.tint = 0x9900FF;
		} else {
			enemyGraphics.tint = 0xFFFFFF;
		}
		if (self.currentTarget) {
			var ox = self.currentTarget.x - self.currentCellX;
			var oy = self.currentTarget.y - self.currentCellY;
			if (ox !== 0 || oy !== 0) {
				var angle = Math.atan2(oy, ox);
				if (enemyGraphics.targetRotation === undefined) {
					enemyGraphics.targetRotation = angle;
					enemyGraphics.rotation = angle;
				} else {
					if (Math.abs(angle - enemyGraphics.targetRotation) > 0.05) {
						tween.stop(enemyGraphics, {
							rotation: true
						});
						// Calculate the shortest angle to rotate
						var currentRotation = enemyGraphics.rotation;
						var angleDiff = angle - currentRotation;
						// Normalize angle difference to -PI to PI range for shortest path
						while (angleDiff > Math.PI) {
							angleDiff -= Math.PI * 2;
						}
						while (angleDiff < -Math.PI) {
							angleDiff += Math.PI * 2;
						}
						enemyGraphics.targetRotation = angle;
						tween(enemyGraphics, {
							rotation: currentRotation + angleDiff
						}, {
							duration: 250,
							easing: tween.easeOut
						});
					}
				}
			}
		}
		healthBarOutline.y = healthBarBG.y = healthBar.y = -enemyGraphics.height / 2 - 10;
	};
	return self;
});
var EnvironmentalElement = Container.expand(function (type, x, y) {
	var self = Container.call(this);
	self.type = type;
	self.x = x;
	self.y = y;
	var elementGraphics;
	switch (type) {
		case 'palm_tree':
			elementGraphics = self.attachAsset('palm_tree', {
				anchorX: 0.5,
				anchorY: 1.0
			});
			elementGraphics.tint = 0x228B22; // Forest green
			// Add swaying animation
			self.startSwayAnimation = function () {
				var _swayTween2 = function swayTween() {
					tween(elementGraphics, {
						rotation: (Math.random() - 0.5) * 0.15
					}, {
						duration: 3000 + Math.random() * 2000,
						easing: tween.easeInOut,
						onFinish: _swayTween2
					});
				};
				_swayTween2();
			};
			self.startSwayAnimation();
			break;
		case 'rock':
			elementGraphics = self.attachAsset('rock', {
				anchorX: 0.5,
				anchorY: 0.5
			});
			elementGraphics.tint = 0x696969; // Dim gray
			elementGraphics.scaleX = 0.8 + Math.random() * 0.4; // Random size variation
			elementGraphics.scaleY = 0.8 + Math.random() * 0.4;
			break;
		case 'seashell':
			elementGraphics = self.attachAsset('seashell', {
				anchorX: 0.5,
				anchorY: 0.5
			});
			elementGraphics.tint = 0xFFF8DC; // Cornsilk
			elementGraphics.rotation = Math.random() * Math.PI * 2; // Random rotation
			elementGraphics.scaleX = 0.6 + Math.random() * 0.4;
			elementGraphics.scaleY = 0.6 + Math.random() * 0.4;
			break;
	}
	return self;
});
var GoldIndicator = Container.expand(function (value, x, y) {
	var self = Container.call(this);
	var goldText = new Text2("+" + value, {
		size: 45,
		fill: 0xFFD700,
		weight: 800
	});
	goldText.anchor.set(0.5, 0.5);
	self.addChild(goldText);
	self.x = x;
	self.y = y;
	self.alpha = 0;
	self.scaleX = 0.5;
	self.scaleY = 0.5;
	tween(self, {
		alpha: 1,
		scaleX: 1.2,
		scaleY: 1.2,
		y: y - 40
	}, {
		duration: 50,
		easing: tween.easeOut,
		onFinish: function onFinish() {
			tween(self, {
				alpha: 0,
				scaleX: 1.5,
				scaleY: 1.5,
				y: y - 80
			}, {
				duration: 600,
				easing: tween.easeIn,
				delay: 800,
				onFinish: function onFinish() {
					self.destroy();
				}
			});
		}
	});
	return self;
});
var Grid = Container.expand(function (gridWidth, gridHeight) {
	var self = Container.call(this);
	self.cells = [];
	self.spawns = [];
	self.goals = [];
	for (var i = 0; i < gridWidth; i++) {
		self.cells[i] = [];
		for (var j = 0; j < gridHeight; j++) {
			self.cells[i][j] = {
				score: 0,
				pathId: 0,
				towersInRange: []
			};
		}
	}
	/*
	Cell Types
	0: Transparent floor (walkable path)
	1: Wall (blocks movement)
	2: Spawn
	3: Goal
	*/
	// First, fill everything with walls
	for (var i = 0; i < gridWidth; i++) {
		for (var j = 0; j < gridHeight; j++) {
			var cell = self.cells[i][j];
			cell.type = 1; // Start with walls everywhere
			cell.x = i;
			cell.y = j;
		}
	}
	// Create a maze-like street pattern with defined corridors
	// Main vertical corridor down the center
	var centerX = Math.floor(gridWidth / 2);
	for (var j = 5; j < gridHeight - 4; j++) {
		self.cells[centerX][j].type = 0; // Clear path
		self.cells[centerX - 1][j].type = 0; // Make corridor 2 tiles wide
	}
	// Create horizontal connecting streets at regular intervals
	var streetInterval = 6;
	for (var streetY = 8; streetY < gridHeight - 8; streetY += streetInterval) {
		// Left side street
		for (var i = 2; i < centerX - 1; i++) {
			self.cells[i][streetY].type = 0;
			self.cells[i][streetY + 1].type = 0; // Make streets 2 tiles wide
		}
		// Right side street
		for (var i = centerX + 2; i < gridWidth - 2; i++) {
			self.cells[i][streetY].type = 0;
			self.cells[i][streetY + 1].type = 0; // Make streets 2 tiles wide
		}
	}
	// Create some vertical side streets for more complexity
	for (var sideStreetX = 6; sideStreetX < gridWidth - 6; sideStreetX += 8) {
		if (sideStreetX === centerX || sideStreetX === centerX - 1) continue; // Skip center
		for (var j = 8; j < gridHeight - 8; j++) {
			if (j % streetInterval < 2) continue; // Don't overwrite horizontal streets
			self.cells[sideStreetX][j].type = 0;
		}
	}
	// Create spawn area (entrance to the maze)
	for (var i = centerX - 3; i <= centerX + 3; i++) {
		for (var j = 0; j <= 4; j++) {
			if (j === 0) {
				self.cells[i][j].type = 2; // Spawn points
				self.spawns.push(self.cells[i][j]);
			} else {
				self.cells[i][j].type = 0; // Clear entrance path
			}
		}
	}
	// Create goal area (exit from the maze)
	for (var i = centerX - 3; i <= centerX + 3; i++) {
		for (var j = gridHeight - 4; j < gridHeight; j++) {
			if (j === gridHeight - 1) {
				self.cells[i][j].type = 3; // Goal points
				self.goals.push(self.cells[i][j]);
			} else {
				self.cells[i][j].type = 0; // Clear exit path
			}
		}
	}
	// Set up cell relationships after creating the maze
	for (var i = 0; i < gridWidth; i++) {
		for (var j = 0; j < gridHeight; j++) {
			var cell = self.cells[i][j];
			cell.upLeft = self.cells[i - 1] && self.cells[i - 1][j - 1];
			cell.up = self.cells[i - 1] && self.cells[i - 1][j];
			cell.upRight = self.cells[i - 1] && self.cells[i - 1][j + 1];
			cell.left = self.cells[i][j - 1];
			cell.right = self.cells[i][j + 1];
			cell.downLeft = self.cells[i + 1] && self.cells[i + 1][j - 1];
			cell.down = self.cells[i + 1] && self.cells[i + 1][j];
			cell.downRight = self.cells[i + 1] && self.cells[i + 1][j + 1];
			cell.neighbors = [cell.upLeft, cell.up, cell.upRight, cell.right, cell.downRight, cell.down, cell.downLeft, cell.left];
			cell.targets = [];
			if (j > 3 && j <= gridHeight - 4) {
				var debugCell = new DebugCell();
				self.addChild(debugCell);
				debugCell.cell = cell;
				debugCell.x = i * CELL_SIZE;
				debugCell.y = j * CELL_SIZE;
				cell.debugCell = debugCell;
			}
		}
	}
	self.getCell = function (x, y) {
		return self.cells[x] && self.cells[x][y];
	};
	self.pathFind = function () {
		var before = new Date().getTime();
		var toProcess = self.goals.concat([]);
		maxScore = 0;
		pathId += 1;
		for (var a = 0; a < toProcess.length; a++) {
			toProcess[a].pathId = pathId;
		}
		function processNode(node, targetValue, targetNode) {
			if (node && node.type != 1) {
				if (node.pathId < pathId || targetValue < node.score) {
					node.targets = [targetNode];
				} else if (node.pathId == pathId && targetValue == node.score) {
					node.targets.push(targetNode);
				}
				if (node.pathId < pathId || targetValue < node.score) {
					node.score = targetValue;
					if (node.pathId != pathId) {
						toProcess.push(node);
					}
					node.pathId = pathId;
					if (targetValue > maxScore) {
						maxScore = targetValue;
					}
				}
			}
		}
		while (toProcess.length) {
			var nodes = toProcess;
			toProcess = [];
			for (var a = 0; a < nodes.length; a++) {
				var node = nodes[a];
				var targetScore = node.score + 14142;
				if (node.up && node.left && node.up.type != 1 && node.left.type != 1) {
					processNode(node.upLeft, targetScore, node);
				}
				if (node.up && node.right && node.up.type != 1 && node.right.type != 1) {
					processNode(node.upRight, targetScore, node);
				}
				if (node.down && node.right && node.down.type != 1 && node.right.type != 1) {
					processNode(node.downRight, targetScore, node);
				}
				if (node.down && node.left && node.down.type != 1 && node.left.type != 1) {
					processNode(node.downLeft, targetScore, node);
				}
				targetScore = node.score + 10000;
				processNode(node.up, targetScore, node);
				processNode(node.right, targetScore, node);
				processNode(node.down, targetScore, node);
				processNode(node.left, targetScore, node);
			}
		}
		for (var a = 0; a < self.spawns.length; a++) {
			if (self.spawns[a].pathId != pathId) {
				console.warn("Spawn blocked");
				return true;
			}
		}
		for (var a = 0; a < enemies.length; a++) {
			var enemy = enemies[a];
			// Skip enemies that haven't entered the viewable area yet
			if (enemy.currentCellY < 4) {
				continue;
			}
			// Skip flying enemies from path check as they can fly over obstacles
			if (enemy.isFlying) {
				continue;
			}
			var target = self.getCell(enemy.cellX, enemy.cellY);
			if (enemy.currentTarget) {
				if (enemy.currentTarget.pathId != pathId) {
					if (!target || target.pathId != pathId) {
						console.warn("Enemy blocked 1 ");
						return true;
					}
				}
			} else if (!target || target.pathId != pathId) {
				console.warn("Enemy blocked 2");
				return true;
			}
		}
		console.log("Speed", new Date().getTime() - before);
	};
	self.renderDebug = function () {
		for (var i = 0; i < gridWidth; i++) {
			for (var j = 0; j < gridHeight; j++) {
				var debugCell = self.cells[i][j].debugCell;
				if (debugCell) {
					debugCell.render(self.cells[i][j]);
				}
			}
		}
	};
	self.updateEnemy = function (enemy) {
		var cell = grid.getCell(enemy.cellX, enemy.cellY);
		if (cell.type == 3) {
			return true;
		}
		if (enemy.isFlying && enemy.shadow) {
			enemy.shadow.x = enemy.x + 20; // Match enemy x-position + offset
			enemy.shadow.y = enemy.y + 20; // Match enemy y-position + offset
			// Match shadow rotation with enemy rotation
			if (enemy.children[0] && enemy.shadow.children[0]) {
				enemy.shadow.children[0].rotation = enemy.children[0].rotation;
			}
		}
		// Check if the enemy has reached the entry area (y position is at least 5)
		var hasReachedEntryArea = enemy.currentCellY >= 4;
		// If enemy hasn't reached the entry area yet, just move down vertically
		if (!hasReachedEntryArea) {
			// Move directly downward
			enemy.currentCellY += enemy.speed;
			// Rotate enemy graphic to face downward (PI/2 radians = 90 degrees)
			var angle = Math.PI / 2;
			if (enemy.children[0] && enemy.children[0].targetRotation === undefined) {
				enemy.children[0].targetRotation = angle;
				enemy.children[0].rotation = angle;
			} else if (enemy.children[0]) {
				if (Math.abs(angle - enemy.children[0].targetRotation) > 0.05) {
					tween.stop(enemy.children[0], {
						rotation: true
					});
					// Calculate the shortest angle to rotate
					var currentRotation = enemy.children[0].rotation;
					var angleDiff = angle - currentRotation;
					// Normalize angle difference to -PI to PI range for shortest path
					while (angleDiff > Math.PI) {
						angleDiff -= Math.PI * 2;
					}
					while (angleDiff < -Math.PI) {
						angleDiff += Math.PI * 2;
					}
					// Set target rotation and animate to it
					enemy.children[0].targetRotation = angle;
					tween(enemy.children[0], {
						rotation: currentRotation + angleDiff
					}, {
						duration: 250,
						easing: tween.easeOut
					});
				}
			}
			// Update enemy's position
			enemy.x = grid.x + enemy.currentCellX * CELL_SIZE;
			enemy.y = grid.y + enemy.currentCellY * CELL_SIZE;
			// Add footprint effect on path cells for non-flying enemies
			if (!enemy.isFlying) {
				var currentCell = self.getCell(Math.floor(enemy.currentCellX), Math.floor(enemy.currentCellY));
				if (currentCell && currentCell.type === 0 && currentCell.debugCell) {
					// Only add footprint if enemy has moved to a new cell
					if (!enemy.lastFootprintX || !enemy.lastFootprintY || Math.floor(enemy.currentCellX) !== enemy.lastFootprintX || Math.floor(enemy.currentCellY) !== enemy.lastFootprintY) {
						currentCell.debugCell.addFootprint();
						enemy.lastFootprintX = Math.floor(enemy.currentCellX);
						enemy.lastFootprintY = Math.floor(enemy.currentCellY);
					}
				}
			}
			// If enemy has now reached the entry area, update cell coordinates
			if (enemy.currentCellY >= 4) {
				enemy.cellX = Math.round(enemy.currentCellX);
				enemy.cellY = Math.round(enemy.currentCellY);
			}
			return false;
		}
		// After reaching entry area, handle flying enemies differently
		if (enemy.isFlying) {
			// Flying enemies head straight to the closest goal
			if (!enemy.flyingTarget) {
				// Set flying target to the closest goal
				enemy.flyingTarget = self.goals[0];
				// Find closest goal if there are multiple
				if (self.goals.length > 1) {
					var closestDist = Infinity;
					for (var i = 0; i < self.goals.length; i++) {
						var goal = self.goals[i];
						var dx = goal.x - enemy.cellX;
						var dy = goal.y - enemy.cellY;
						var dist = dx * dx + dy * dy;
						if (dist < closestDist) {
							closestDist = dist;
							enemy.flyingTarget = goal;
						}
					}
				}
			}
			// Move directly toward the goal
			var ox = enemy.flyingTarget.x - enemy.currentCellX;
			var oy = enemy.flyingTarget.y - enemy.currentCellY;
			var dist = Math.sqrt(ox * ox + oy * oy);
			if (dist < enemy.speed) {
				// Reached the goal
				return true;
			}
			var angle = Math.atan2(oy, ox);
			// Rotate enemy graphic to match movement direction
			if (enemy.children[0] && enemy.children[0].targetRotation === undefined) {
				enemy.children[0].targetRotation = angle;
				enemy.children[0].rotation = angle;
			} else if (enemy.children[0]) {
				if (Math.abs(angle - enemy.children[0].targetRotation) > 0.05) {
					tween.stop(enemy.children[0], {
						rotation: true
					});
					// Calculate the shortest angle to rotate
					var currentRotation = enemy.children[0].rotation;
					var angleDiff = angle - currentRotation;
					// Normalize angle difference to -PI to PI range for shortest path
					while (angleDiff > Math.PI) {
						angleDiff -= Math.PI * 2;
					}
					while (angleDiff < -Math.PI) {
						angleDiff += Math.PI * 2;
					}
					// Set target rotation and animate to it
					enemy.children[0].targetRotation = angle;
					tween(enemy.children[0], {
						rotation: currentRotation + angleDiff
					}, {
						duration: 250,
						easing: tween.easeOut
					});
				}
			}
			// Update the cell position to track where the flying enemy is
			enemy.cellX = Math.round(enemy.currentCellX);
			enemy.cellY = Math.round(enemy.currentCellY);
			enemy.currentCellX += Math.cos(angle) * enemy.speed;
			enemy.currentCellY += Math.sin(angle) * enemy.speed;
			enemy.x = grid.x + enemy.currentCellX * CELL_SIZE;
			enemy.y = grid.y + enemy.currentCellY * CELL_SIZE;
			// Update shadow position if this is a flying enemy
			return false;
		}
		// Handle normal pathfinding enemies
		if (!enemy.currentTarget) {
			enemy.currentTarget = cell.targets[0];
		}
		if (enemy.currentTarget) {
			if (cell.score < enemy.currentTarget.score) {
				enemy.currentTarget = cell;
			}
			var ox = enemy.currentTarget.x - enemy.currentCellX;
			var oy = enemy.currentTarget.y - enemy.currentCellY;
			var dist = Math.sqrt(ox * ox + oy * oy);
			if (dist < enemy.speed) {
				enemy.cellX = Math.round(enemy.currentCellX);
				enemy.cellY = Math.round(enemy.currentCellY);
				enemy.currentTarget = undefined;
				return;
			}
			var angle = Math.atan2(oy, ox);
			enemy.currentCellX += Math.cos(angle) * enemy.speed;
			enemy.currentCellY += Math.sin(angle) * enemy.speed;
		}
		enemy.x = grid.x + enemy.currentCellX * CELL_SIZE;
		enemy.y = grid.y + enemy.currentCellY * CELL_SIZE;
		// Add footprint effect for ground enemies on path
		if (!enemy.isFlying) {
			var currentCell = self.getCell(Math.floor(enemy.currentCellX), Math.floor(enemy.currentCellY));
			if (currentCell && currentCell.type === 0 && currentCell.debugCell) {
				// Only add footprint if enemy has moved to a new cell
				if (!enemy.lastFootprintX || !enemy.lastFootprintY || Math.floor(enemy.currentCellX) !== enemy.lastFootprintX || Math.floor(enemy.currentCellY) !== enemy.lastFootprintY) {
					currentCell.debugCell.addFootprint();
					enemy.lastFootprintX = Math.floor(enemy.currentCellX);
					enemy.lastFootprintY = Math.floor(enemy.currentCellY);
				}
			}
		}
	};
});
var NextWaveButton = Container.expand(function () {
	var self = Container.call(this);
	var buttonBackground = self.attachAsset('notification', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	buttonBackground.width = 300;
	buttonBackground.height = 100;
	buttonBackground.tint = 0x0088FF;
	var buttonText = new Text2("Next Wave", {
		size: 50,
		fill: 0xFFFFFF,
		weight: 800
	});
	buttonText.anchor.set(0.5, 0.5);
	self.addChild(buttonText);
	self.enabled = false;
	self.visible = false;
	self.update = function () {
		if (waveIndicator && waveIndicator.gameStarted && currentWave < totalWaves) {
			self.enabled = true;
			self.visible = true;
			buttonBackground.tint = 0x0088FF;
			self.alpha = 1;
		} else {
			self.enabled = false;
			self.visible = false;
			buttonBackground.tint = 0x888888;
			self.alpha = 0.7;
		}
	};
	self.down = function () {
		if (!self.enabled) {
			return;
		}
		// Play UI click sound
		LK.getSound('ui_click').play();
		if (waveIndicator.gameStarted && currentWave < totalWaves) {
			currentWave++; // Increment to the next wave directly
			waveTimer = 0; // Reset wave timer
			waveInProgress = true;
			waveSpawned = false;
			// Get the type of the current wave (which is now the next wave)
			var waveType = waveIndicator.getWaveTypeName(currentWave);
			var enemyCount = waveIndicator.getEnemyCount(currentWave);
			var waveAlert = new WaveAlert(currentWave, waveType + " ACTIVATED", enemyCount);
			game.addChild(waveAlert);
		}
	};
	return self;
});
var Notification = Container.expand(function (message) {
	var self = Container.call(this);
	var notificationGraphics = self.attachAsset('notification', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	notificationGraphics.tint = 0x2C3E50;
	var notificationText = new Text2(message, {
		size: 52,
		fill: 0xECF0F1,
		weight: 800
	});
	notificationText.anchor.set(0.5, 0.5);
	notificationGraphics.width = notificationText.width + 40;
	notificationGraphics.height = notificationText.height + 20;
	self.addChild(notificationText);
	self.alpha = 1;
	var fadeOutTime = 120;
	self.update = function () {
		if (fadeOutTime > 0) {
			fadeOutTime--;
			self.alpha = Math.min(fadeOutTime / 120 * 2, 1);
		} else {
			self.destroy();
		}
	};
	return self;
});
var SourceTower = Container.expand(function (towerType) {
	var self = Container.call(this);
	self.towerType = towerType || 'laser';
	// Create tower asset based on tower type
	var towerAsset = self.towerType + '_tower';
	var baseGraphics = self.attachAsset(towerAsset, {
		anchorX: 0.5,
		anchorY: 0.5,
		scaleX: 1.3,
		scaleY: 1.3
	});
	var towerCost = getTowerCost(self.towerType);
	// Add tower type label only
	var typeLabel = new Text2(self.towerType.charAt(0).toUpperCase() + self.towerType.slice(1), {
		size: 40,
		fill: 0xFFFFFF,
		weight: 800
	});
	typeLabel.anchor.set(0.5, 0.5);
	typeLabel.y = 0; // Center the text
	self.addChild(typeLabel);
	self.update = function () {
		// Check if player can afford this tower
		var canAfford = gold >= getTowerCost(self.towerType);
		// Set opacity based on affordability
		self.alpha = canAfford ? 1 : 0.5;
	};
	return self;
});
var Tower = Container.expand(function (id) {
	var self = Container.call(this);
	self.id = id || 'laser';
	self.level = 1;
	self.maxLevel = 6;
	self.gridX = 0;
	self.gridY = 0;
	self.range = 3 * CELL_SIZE;
	// Standardized method to get the current range of the tower
	self.getRange = function () {
		// Always calculate range based on tower type and level
		switch (self.id) {
			case 'laser':
				// Laser: base 4, +0.6 per level
				return (4 + (self.level - 1) * 0.6) * CELL_SIZE;
			case 'ice':
				// Ice: base 3, +0.4 per level
				return (3 + (self.level - 1) * 0.4) * CELL_SIZE;
			case 'missile':
				// Missile: base 5, +0.7 per level
				return (5 + (self.level - 1) * 0.7) * CELL_SIZE;
			case 'lightning':
				// Lightning: base 2.5, +0.3 per level
				return (2.5 + (self.level - 1) * 0.3) * CELL_SIZE;
			case 'cannon':
				// Cannon: base 3.5, +0.5 per level
				return (3.5 + (self.level - 1) * 0.5) * CELL_SIZE;
			case 'tesla':
				// Tesla: base 2.8, +0.4 per level
				return (2.8 + (self.level - 1) * 0.4) * CELL_SIZE;
			default:
				// Default to laser stats
				return (4 + (self.level - 1) * 0.6) * CELL_SIZE;
		}
	};
	self.cellsInRange = [];
	self.fireRate = 60;
	self.bulletSpeed = 5;
	self.damage = 10;
	self.lastFired = 0;
	self.targetEnemy = null;
	switch (self.id) {
		case 'laser':
			self.fireRate = 45;
			self.damage = 12;
			self.bulletSpeed = 8;
			break;
		case 'ice':
			self.fireRate = 70;
			self.damage = 8;
			self.bulletSpeed = 4;
			break;
		case 'missile':
			self.fireRate = 90;
			self.damage = 20;
			self.bulletSpeed = 6;
			break;
		case 'lightning':
			self.fireRate = 30;
			self.damage = 6;
			self.bulletSpeed = 12;
			break;
		case 'cannon':
			self.fireRate = 80;
			self.damage = 18;
			self.bulletSpeed = 5;
			break;
		case 'tesla':
			self.fireRate = 40;
			self.damage = 10;
			self.bulletSpeed = 10;
			break;
	}
	// Create tower asset based on tower type
	var towerAsset = self.id + '_tower';
	var baseGraphics = self.attachAsset(towerAsset, {
		anchorX: 0.5,
		anchorY: 0.5
	});
	var levelIndicators = [];
	var maxDots = self.maxLevel;
	var dotSpacing = baseGraphics.width / (maxDots + 1);
	var dotSize = CELL_SIZE / 6;
	for (var i = 0; i < maxDots; i++) {
		var dot = new Container();
		var outlineCircle = dot.attachAsset('towerLevelIndicator', {
			anchorX: 0.5,
			anchorY: 0.5
		});
		outlineCircle.width = dotSize + 4;
		outlineCircle.height = dotSize + 4;
		outlineCircle.tint = 0x2F4F2F; // Dark olive green for turtle theme
		var towerLevelIndicator = dot.attachAsset('towerLevelIndicator', {
			anchorX: 0.5,
			anchorY: 0.5
		});
		towerLevelIndicator.width = dotSize;
		towerLevelIndicator.height = dotSize;
		towerLevelIndicator.tint = 0x90EE90; // Light green for turtle theme
		dot.x = -CELL_SIZE + dotSpacing * (i + 1);
		dot.y = CELL_SIZE * 0.7;
		self.addChild(dot);
		levelIndicators.push(dot);
	}
	var gunContainer = new Container();
	self.addChild(gunContainer);
	var gunGraphics = gunContainer.attachAsset('gun_barrel', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	// Color gun based on tower type
	switch (self.id) {
		case 'laser':
			gunGraphics.tint = 0xFF0080;
			break;
		case 'ice':
			gunGraphics.tint = 0x00FFFF;
			break;
		case 'missile':
			gunGraphics.tint = 0x808080;
			break;
		case 'lightning':
			gunGraphics.tint = 0xFFFF00;
			break;
		case 'cannon':
			gunGraphics.tint = 0x8B4513;
			break;
		case 'tesla':
			gunGraphics.tint = 0x9932CC;
			break;
		default:
			gunGraphics.tint = 0xFF0080;
	}
	self.updateLevelIndicators = function () {
		for (var i = 0; i < maxDots; i++) {
			var dot = levelIndicators[i];
			var towerLevelIndicator = dot.children[1];
			if (i < self.level) {
				towerLevelIndicator.tint = 0xFFD700; // Gold for active turtle levels
			} else {
				switch (self.id) {
					case 'laser':
						towerLevelIndicator.tint = 0xFF0080; // Pink laser
						break;
					case 'ice':
						towerLevelIndicator.tint = 0x00FFFF; // Cyan ice
						break;
					case 'missile':
						towerLevelIndicator.tint = 0x808080; // Gray missile
						break;
					case 'lightning':
						towerLevelIndicator.tint = 0xFFFF00; // Yellow lightning
						break;
					case 'cannon':
						towerLevelIndicator.tint = 0x8B4513; // Brown cannon
						break;
					case 'tesla':
						towerLevelIndicator.tint = 0x9932CC; // Purple tesla
						break;
					default:
						towerLevelIndicator.tint = 0xFF0080;
					// Default to laser
				}
			}
		}
	};
	self.updateLevelIndicators();
	// Initialize idle animation properties
	self.idleAnimationActive = false;
	self.upgradeAnimationActive = false;
	// Start idle animation based on tower type
	self.startIdleAnimation = function () {
		if (self.idleAnimationActive || self.upgradeAnimationActive) return;
		self.idleAnimationActive = true;
		switch (self.id) {
			case 'laser':
				// Subtle sway and flickering lights for laser towers
				var _swayTween = function swayTween() {
					if (!self.idleAnimationActive) return;
					tween(baseGraphics, {
						rotation: baseGraphics.rotation + (Math.random() - 0.5) * 0.1
					}, {
						duration: 2000 + Math.random() * 1000,
						easing: tween.easeInOut,
						onFinish: _swayTween
					});
				};
				_swayTween();
				// Flickering light effect
				var _flickerTween = function flickerTween() {
					if (!self.idleAnimationActive) return;
					tween(baseGraphics, {
						alpha: 0.8 + Math.random() * 0.2
					}, {
						duration: 300 + Math.random() * 200,
						easing: tween.linear,
						onFinish: _flickerTween
					});
				};
				_flickerTween();
				break;
			case 'ice':
				// Gentle pulsing for ice towers
				var _pulseTween = function pulseTween() {
					if (!self.idleAnimationActive) return;
					tween(baseGraphics, {
						scaleX: 1.02,
						scaleY: 1.02
					}, {
						duration: 1500,
						easing: tween.easeInOut,
						onFinish: function onFinish() {
							if (!self.idleAnimationActive) return;
							tween(baseGraphics, {
								scaleX: 1.0,
								scaleY: 1.0
							}, {
								duration: 1500,
								easing: tween.easeInOut,
								onFinish: _pulseTween
							});
						}
					});
				};
				_pulseTween();
				break;
			case 'tesla':
				// Electric crackling effect
				var crackleOffset = 0;
				var _crackleTween = function crackleTween() {
					if (!self.idleAnimationActive) return;
					crackleOffset += (Math.random() - 0.5) * 0.05;
					tween(baseGraphics, {
						x: crackleOffset,
						y: crackleOffset * 0.5
					}, {
						duration: 100 + Math.random() * 100,
						easing: tween.linear,
						onFinish: _crackleTween
					});
				};
				_crackleTween();
				break;
			default:
				// Default subtle breathing animation
				var _breatheTween = function breatheTween() {
					if (!self.idleAnimationActive) return;
					tween(baseGraphics, {
						scaleX: 1.01,
						scaleY: 1.01
					}, {
						duration: 2000,
						easing: tween.easeInOut,
						onFinish: function onFinish() {
							if (!self.idleAnimationActive) return;
							tween(baseGraphics, {
								scaleX: 1.0,
								scaleY: 1.0
							}, {
								duration: 2000,
								easing: tween.easeInOut,
								onFinish: _breatheTween
							});
						}
					});
				};
				_breatheTween();
		}
	};
	// Stop idle animation
	self.stopIdleAnimation = function () {
		self.idleAnimationActive = false;
		tween.stop(baseGraphics, {
			rotation: true,
			alpha: true,
			scaleX: true,
			scaleY: true,
			x: true,
			y: true
		});
		// Reset to default state
		baseGraphics.rotation = 0;
		baseGraphics.alpha = 1;
		baseGraphics.scaleX = 1;
		baseGraphics.scaleY = 1;
		baseGraphics.x = 0;
		baseGraphics.y = 0;
	};
	// Start idle animation immediately
	self.startIdleAnimation();
	self.refreshCellsInRange = function () {
		for (var i = 0; i < self.cellsInRange.length; i++) {
			var cell = self.cellsInRange[i];
			var towerIndex = cell.towersInRange.indexOf(self);
			if (towerIndex !== -1) {
				cell.towersInRange.splice(towerIndex, 1);
			}
		}
		self.cellsInRange = [];
		var rangeRadius = self.getRange() / CELL_SIZE;
		var centerX = self.gridX + 1;
		var centerY = self.gridY + 1;
		var minI = Math.floor(centerX - rangeRadius - 0.5);
		var maxI = Math.ceil(centerX + rangeRadius + 0.5);
		var minJ = Math.floor(centerY - rangeRadius - 0.5);
		var maxJ = Math.ceil(centerY + rangeRadius + 0.5);
		for (var i = minI; i <= maxI; i++) {
			for (var j = minJ; j <= maxJ; j++) {
				var closestX = Math.max(i, Math.min(centerX, i + 1));
				var closestY = Math.max(j, Math.min(centerY, j + 1));
				var deltaX = closestX - centerX;
				var deltaY = closestY - centerY;
				var distanceSquared = deltaX * deltaX + deltaY * deltaY;
				if (distanceSquared <= rangeRadius * rangeRadius) {
					var cell = grid.getCell(i, j);
					if (cell) {
						self.cellsInRange.push(cell);
						cell.towersInRange.push(self);
					}
				}
			}
		}
		grid.renderDebug();
	};
	self.getTotalValue = function () {
		var baseTowerCost = getTowerCost(self.id);
		var totalInvestment = baseTowerCost;
		var baseUpgradeCost = baseTowerCost; // Upgrade cost now scales with base tower cost
		for (var i = 1; i < self.level; i++) {
			totalInvestment += Math.floor(baseUpgradeCost * Math.pow(2, i - 1));
		}
		return totalInvestment;
	};
	self.upgrade = function () {
		if (self.level < self.maxLevel) {
			// Exponential upgrade cost: base cost * (2 ^ (level-1)), scaled by tower base cost
			var baseUpgradeCost = getTowerCost(self.id);
			var upgradeCost;
			// Make last upgrade level extra expensive
			if (self.level === self.maxLevel - 1) {
				upgradeCost = Math.floor(baseUpgradeCost * Math.pow(2, self.level - 1) * 3.5 / 2); // Half the cost for final upgrade
			} else {
				upgradeCost = Math.floor(baseUpgradeCost * Math.pow(2, self.level - 1));
			}
			if (gold >= upgradeCost) {
				setGold(gold - upgradeCost);
				self.level++;
				// No need to update self.range here; getRange() is now the source of truth
				// Apply tower-specific upgrades based on type
				if (self.id === 'rapid') {
					if (self.level === self.maxLevel) {
						// Extra powerful last upgrade (double the effect)
						self.fireRate = Math.max(4, 30 - self.level * 9); // double the effect
						self.damage = 5 + self.level * 10; // double the effect
						self.bulletSpeed = 7 + self.level * 2.4; // double the effect
					} else {
						self.fireRate = Math.max(15, 30 - self.level * 3); // Fast tower gets faster with upgrades
						self.damage = 5 + self.level * 3;
						self.bulletSpeed = 7 + self.level * 0.7;
					}
				} else {
					if (self.level === self.maxLevel) {
						// Extra powerful last upgrade for all other towers (double the effect)
						self.fireRate = Math.max(5, 60 - self.level * 24); // double the effect
						self.damage = 10 + self.level * 20; // double the effect
						self.bulletSpeed = 5 + self.level * 2.4; // double the effect
					} else {
						self.fireRate = Math.max(20, 60 - self.level * 8);
						self.damage = 10 + self.level * 5;
						self.bulletSpeed = 5 + self.level * 0.5;
					}
				}
				self.refreshCellsInRange();
				self.updateLevelIndicators();
				// Play upgrade animation
				self.upgradeAnimationActive = true;
				self.stopIdleAnimation();
				// Glow effect and transformation animation
				tween(baseGraphics, {
					scaleX: 1.3,
					scaleY: 1.3,
					alpha: 1.5
				}, {
					duration: 200,
					easing: tween.easeOut,
					onFinish: function onFinish() {
						// Bright flash
						tween(baseGraphics, {
							alpha: 2.0
						}, {
							duration: 100,
							easing: tween.linear,
							onFinish: function onFinish() {
								// Return to normal with power glow
								tween(baseGraphics, {
									scaleX: 1.05,
									scaleY: 1.05,
									alpha: 1.0
								}, {
									duration: 300,
									easing: tween.easeInOut,
									onFinish: function onFinish() {
										// Final settle
										tween(baseGraphics, {
											scaleX: 1.0,
											scaleY: 1.0
										}, {
											duration: 200,
											easing: tween.easeOut,
											onFinish: function onFinish() {
												self.upgradeAnimationActive = false;
												self.startIdleAnimation();
											}
										});
									}
								});
							}
						});
					}
				});
				// Add particle burst effect around the tower
				var upgradeEffect = new Container();
				game.addChild(upgradeEffect);
				upgradeEffect.x = self.x;
				upgradeEffect.y = self.y;
				// Create multiple expanding rings for upgrade effect
				for (var ring = 0; ring < 3; ring++) {
					var ringContainer = new Container();
					upgradeEffect.addChild(ringContainer);
					var ringGraphics = ringContainer.attachAsset('rangeCircle', {
						anchorX: 0.5,
						anchorY: 0.5
					});
					ringGraphics.width = ringGraphics.height = CELL_SIZE * 0.5;
					ringGraphics.alpha = 0.8;
					// Different colors based on tower type
					switch (self.id) {
						case 'laser':
							ringGraphics.tint = 0xFF0080;
							break;
						case 'ice':
							ringGraphics.tint = 0x00FFFF;
							break;
						case 'missile':
							ringGraphics.tint = 0x808080;
							break;
						case 'lightning':
							ringGraphics.tint = 0xFFFF00;
							break;
						case 'cannon':
							ringGraphics.tint = 0x8B4513;
							break;
						case 'tesla':
							ringGraphics.tint = 0x9932CC;
							break;
						default:
							ringGraphics.tint = 0xFFD700;
					}
					// Stagger the ring animations
					LK.setTimeout(function () {
						tween(ringContainer, {
							alpha: 0,
							scaleX: 4,
							scaleY: 4
						}, {
							duration: 600,
							easing: tween.easeOut,
							onFinish: function onFinish() {
								upgradeEffect.removeChild(ringContainer);
								if (upgradeEffect.children.length === 0) {
									upgradeEffect.destroy();
								}
							}
						});
					}, ring * 150);
				}
				if (self.level > 1) {
					var levelDot = levelIndicators[self.level - 1].children[1];
					tween(levelDot, {
						scaleX: 1.5,
						scaleY: 1.5
					}, {
						duration: 300,
						easing: tween.elasticOut,
						onFinish: function onFinish() {
							tween(levelDot, {
								scaleX: 1,
								scaleY: 1
							}, {
								duration: 200,
								easing: tween.easeOut
							});
						}
					});
				}
				return true;
			} else {
				var notification = game.addChild(new Notification("Not enough gold to upgrade!"));
				notification.x = 2048 / 2;
				notification.y = grid.height - 50;
				return false;
			}
		}
		return false;
	};
	self.findTarget = function () {
		var closestEnemy = null;
		var closestScore = Infinity;
		for (var i = 0; i < enemies.length; i++) {
			var enemy = enemies[i];
			var dx = enemy.x - self.x;
			var dy = enemy.y - self.y;
			var distance = Math.sqrt(dx * dx + dy * dy);
			// Check if enemy is in range
			if (distance <= self.getRange()) {
				// Handle flying enemies differently - they can be targeted regardless of path
				if (enemy.isFlying) {
					// For flying enemies, prioritize by distance to the goal
					if (enemy.flyingTarget) {
						var goalX = enemy.flyingTarget.x;
						var goalY = enemy.flyingTarget.y;
						var distToGoal = Math.sqrt((goalX - enemy.cellX) * (goalX - enemy.cellX) + (goalY - enemy.cellY) * (goalY - enemy.cellY));
						// Use distance to goal as score
						if (distToGoal < closestScore) {
							closestScore = distToGoal;
							closestEnemy = enemy;
						}
					} else {
						// If no flying target yet (shouldn't happen), prioritize by distance to tower
						if (distance < closestScore) {
							closestScore = distance;
							closestEnemy = enemy;
						}
					}
				} else {
					// For ground enemies, use the original path-based targeting
					// Get the cell for this enemy
					var cell = grid.getCell(enemy.cellX, enemy.cellY);
					if (cell && cell.pathId === pathId) {
						// Use the cell's score (distance to exit) for prioritization
						// Lower score means closer to exit
						if (cell.score < closestScore) {
							closestScore = cell.score;
							closestEnemy = enemy;
						}
					}
				}
			}
		}
		if (!closestEnemy) {
			self.targetEnemy = null;
		}
		return closestEnemy;
	};
	self.update = function () {
		self.targetEnemy = self.findTarget();
		if (self.targetEnemy) {
			var dx = self.targetEnemy.x - self.x;
			var dy = self.targetEnemy.y - self.y;
			var angle = Math.atan2(dy, dx);
			gunContainer.rotation = angle;
			// Make the tower base also rotate to face the target
			baseGraphics.rotation = angle;
			if (LK.ticks - self.lastFired >= self.fireRate) {
				self.fire();
				self.lastFired = LK.ticks;
			}
		}
	};
	self.down = function (x, y, obj) {
		// Play tower selection sound
		LK.getSound('ui_select').play();
		var existingMenus = game.children.filter(function (child) {
			return child instanceof UpgradeMenu;
		});
		var hasOwnMenu = false;
		var rangeCircle = null;
		for (var i = 0; i < game.children.length; i++) {
			if (game.children[i].isTowerRange && game.children[i].tower === self) {
				rangeCircle = game.children[i];
				break;
			}
		}
		for (var i = 0; i < existingMenus.length; i++) {
			if (existingMenus[i].tower === self) {
				hasOwnMenu = true;
				break;
			}
		}
		if (hasOwnMenu) {
			for (var i = 0; i < existingMenus.length; i++) {
				if (existingMenus[i].tower === self) {
					hideUpgradeMenu(existingMenus[i]);
				}
			}
			if (rangeCircle) {
				game.removeChild(rangeCircle);
			}
			selectedTower = null;
			grid.renderDebug();
			return;
		}
		for (var i = 0; i < existingMenus.length; i++) {
			existingMenus[i].destroy();
		}
		for (var i = game.children.length - 1; i >= 0; i--) {
			if (game.children[i].isTowerRange) {
				game.removeChild(game.children[i]);
			}
		}
		selectedTower = self;
		var rangeIndicator = new Container();
		rangeIndicator.isTowerRange = true;
		rangeIndicator.tower = self;
		game.addChild(rangeIndicator);
		rangeIndicator.x = self.x;
		rangeIndicator.y = self.y;
		var rangeGraphics = rangeIndicator.attachAsset('rangeCircle', {
			anchorX: 0.5,
			anchorY: 0.5
		});
		rangeGraphics.width = rangeGraphics.height = self.getRange() * 2;
		rangeGraphics.alpha = 0.25;
		rangeGraphics.tint = 0x74B9FF;
		var upgradeMenu = new UpgradeMenu(self);
		game.addChild(upgradeMenu);
		upgradeMenu.x = 2048 / 2;
		tween(upgradeMenu, {
			y: 2732 - 225
		}, {
			duration: 200,
			easing: tween.backOut
		});
		// Add detailed tower information panel
		var towerInfoPanel = new TowerInfoPanel(self);
		game.addChild(towerInfoPanel);
		towerInfoPanel.x = 2048 / 2;
		tween(towerInfoPanel, {
			y: 2732 - 650
		}, {
			duration: 250,
			easing: tween.backOut
		});
		grid.renderDebug();
	};
	self.isInRange = function (enemy) {
		if (!enemy) {
			return false;
		}
		var dx = enemy.x - self.x;
		var dy = enemy.y - self.y;
		var distance = Math.sqrt(dx * dx + dy * dy);
		return distance <= self.getRange();
	};
	self.fire = function () {
		if (self.targetEnemy) {
			var potentialDamage = 0;
			for (var i = 0; i < self.targetEnemy.bulletsTargetingThis.length; i++) {
				potentialDamage += self.targetEnemy.bulletsTargetingThis[i].damage;
			}
			if (self.targetEnemy.health > potentialDamage) {
				var bulletX = self.x + Math.cos(gunContainer.rotation) * 40;
				var bulletY = self.y + Math.sin(gunContainer.rotation) * 40;
				var bullet = new Bullet(bulletX, bulletY, self.targetEnemy, self.damage, self.bulletSpeed);
				// Set bullet type based on tower type
				bullet.type = self.id;
				// Customize bullet appearance based on tower type
				switch (self.id) {
					case 'laser':
						bullet.children[0].tint = 0xFF0080;
						bullet.children[0].width = 25;
						bullet.children[0].height = 25;
						break;
					case 'ice':
						bullet.children[0].tint = 0x00FFFF;
						bullet.children[0].width = 30;
						bullet.children[0].height = 30;
						break;
					case 'missile':
						bullet.children[0].tint = 0x808080;
						bullet.children[0].width = 35;
						bullet.children[0].height = 35;
						break;
					case 'lightning':
						bullet.children[0].tint = 0xFFFF00;
						bullet.children[0].width = 20;
						bullet.children[0].height = 20;
						break;
					case 'cannon':
						bullet.children[0].tint = 0x8B4513;
						bullet.children[0].width = 40;
						bullet.children[0].height = 40;
						break;
					case 'tesla':
						bullet.children[0].tint = 0x9932CC;
						bullet.children[0].width = 25;
						bullet.children[0].height = 25;
						break;
				}
				game.addChild(bullet);
				bullets.push(bullet);
				self.targetEnemy.bulletsTargetingThis.push(bullet);
				// Play shooting sound effect based on tower type
				var soundId = self.id + '_shoot';
				LK.getSound(soundId).play();
				// Create muzzle flash effect at firing position
				var muzzleFlash = new Container();
				game.addChild(muzzleFlash);
				muzzleFlash.x = bulletX;
				muzzleFlash.y = bulletY;
				var flashGraphics = muzzleFlash.attachAsset('rangeCircle', {
					anchorX: 0.5,
					anchorY: 0.5
				});
				// Customize muzzle flash based on tower type
				switch (self.id) {
					case 'laser':
						flashGraphics.width = flashGraphics.height = CELL_SIZE * 0.4;
						flashGraphics.tint = 0xFF0080;
						break;
					case 'ice':
						flashGraphics.width = flashGraphics.height = CELL_SIZE * 0.3;
						flashGraphics.tint = 0x00FFFF;
						break;
					case 'missile':
						flashGraphics.width = flashGraphics.height = CELL_SIZE * 0.6;
						flashGraphics.tint = 0xFF4500;
						break;
					case 'lightning':
						flashGraphics.width = flashGraphics.height = CELL_SIZE * 0.2;
						flashGraphics.tint = 0xFFFF00;
						break;
					case 'cannon':
						flashGraphics.width = flashGraphics.height = CELL_SIZE * 0.8;
						flashGraphics.tint = 0xFF6600;
						break;
					case 'tesla':
						flashGraphics.width = flashGraphics.height = CELL_SIZE * 0.35;
						flashGraphics.tint = 0x9932CC;
						break;
					default:
						flashGraphics.width = flashGraphics.height = CELL_SIZE * 0.4;
						flashGraphics.tint = 0xFFFFFF;
				}
				flashGraphics.alpha = 1;
				// Quick flash animation
				tween(muzzleFlash, {
					alpha: 0,
					scaleX: 1.8,
					scaleY: 1.8
				}, {
					duration: 120,
					easing: tween.easeOut,
					onFinish: function onFinish() {
						muzzleFlash.destroy();
					}
				});
				// --- Fire recoil effect for gunContainer ---
				// Stop any ongoing recoil tweens before starting a new one
				tween.stop(gunContainer, {
					x: true,
					y: true,
					scaleX: true,
					scaleY: true
				});
				// Always use the original resting position for recoil, never accumulate offset
				if (gunContainer._restX === undefined) {
					gunContainer._restX = 0;
				}
				if (gunContainer._restY === undefined) {
					gunContainer._restY = 0;
				}
				if (gunContainer._restScaleX === undefined) {
					gunContainer._restScaleX = 1;
				}
				if (gunContainer._restScaleY === undefined) {
					gunContainer._restScaleY = 1;
				}
				// Reset to resting position before animating (in case of interrupted tweens)
				gunContainer.x = gunContainer._restX;
				gunContainer.y = gunContainer._restY;
				gunContainer.scaleX = gunContainer._restScaleX;
				gunContainer.scaleY = gunContainer._restScaleY;
				// Calculate recoil offset (recoil back along the gun's rotation)
				var recoilDistance = 8;
				var recoilX = -Math.cos(gunContainer.rotation) * recoilDistance;
				var recoilY = -Math.sin(gunContainer.rotation) * recoilDistance;
				// Animate recoil back from the resting position
				tween(gunContainer, {
					x: gunContainer._restX + recoilX,
					y: gunContainer._restY + recoilY
				}, {
					duration: 60,
					easing: tween.cubicOut,
					onFinish: function onFinish() {
						// Animate return to original position/scale
						tween(gunContainer, {
							x: gunContainer._restX,
							y: gunContainer._restY
						}, {
							duration: 90,
							easing: tween.cubicIn
						});
					}
				});
			}
		}
	};
	self.placeOnGrid = function (gridX, gridY) {
		self.gridX = gridX;
		self.gridY = gridY;
		self.x = grid.x + gridX * CELL_SIZE + CELL_SIZE / 2;
		self.y = grid.y + gridY * CELL_SIZE + CELL_SIZE / 2;
		for (var i = 0; i < 2; i++) {
			for (var j = 0; j < 2; j++) {
				var cell = grid.getCell(gridX + i, gridY + j);
				if (cell) {
					cell.type = 1;
				}
			}
		}
		self.refreshCellsInRange();
	};
	// Override destroy to clean up animations
	var originalDestroy = self.destroy;
	self.destroy = function () {
		self.stopIdleAnimation();
		if (originalDestroy) {
			originalDestroy.call(self);
		}
	};
	return self;
});
var TowerInfoPanel = Container.expand(function (tower) {
	var self = Container.call(this);
	self.tower = tower;
	self.y = 2732 + 300;
	var panelBackground = self.attachAsset('notification', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	panelBackground.width = 1800;
	panelBackground.height = 400;
	panelBackground.tint = 0x2C3E50;
	panelBackground.alpha = 0.95;
	// Tower name and level
	var titleText = new Text2(self.tower.id.charAt(0).toUpperCase() + self.tower.id.slice(1) + ' Tower - Level ' + self.tower.level, {
		size: 60,
		fill: 0xFFFFFF,
		weight: 800
	});
	titleText.anchor.set(0.5, 0);
	titleText.x = 0;
	titleText.y = -160;
	self.addChild(titleText);
	// Detailed stats
	var dpsValue = (self.tower.damage * (60 / self.tower.fireRate)).toFixed(1);
	var rangeValue = (self.tower.getRange() / CELL_SIZE).toFixed(1);
	var statsText = new Text2('DPS: ' + dpsValue + ' | Range: ' + rangeValue + ' tiles | Speed: ' + self.tower.bulletSpeed, {
		size: 45,
		fill: 0xECF0F1,
		weight: 400
	});
	statsText.anchor.set(0.5, 0);
	statsText.x = 0;
	statsText.y = -90;
	self.addChild(statsText);
	// Special abilities description
	var abilityText = '';
	switch (self.tower.id) {
		case 'laser':
			abilityText = 'High damage, fast firing laser tower with good range';
			break;
		case 'ice':
			abilityText = 'Slows enemies and applies frost effects';
			break;
		case 'missile':
			abilityText = 'Heavy damage with explosive splash effect';
			break;
		case 'lightning':
			abilityText = 'Fast, low damage with electrical chain effects';
			break;
		case 'cannon':
			abilityText = 'Powerful impact with knockback and crater effects';
			break;
		case 'tesla':
			abilityText = 'Energy discharge with electrical arc effects';
			break;
	}
	var abilityDesc = new Text2(abilityText, {
		size: 38,
		fill: 0xBDC3C7,
		weight: 400
	});
	abilityDesc.anchor.set(0.5, 0);
	abilityDesc.x = 0;
	abilityDesc.y = -30;
	self.addChild(abilityDesc);
	// Bullet preview
	var bulletPreview = new Container();
	bulletPreview.x = -600;
	bulletPreview.y = 50;
	self.addChild(bulletPreview);
	var previewLabel = new Text2('Bullet Preview:', {
		size: 35,
		fill: 0xFFFFFF,
		weight: 600
	});
	previewLabel.anchor.set(0.5, 0.5);
	previewLabel.y = -40;
	bulletPreview.addChild(previewLabel);
	// Animated bullet preview
	var bulletAsset = self.tower.id + '_bullet';
	var bulletGraphics = bulletPreview.attachAsset(bulletAsset, {
		anchorX: 0.5,
		anchorY: 0.5
	});
	// Scale up the bullet for visibility
	bulletGraphics.scaleX = 2;
	bulletGraphics.scaleY = 2;
	// Apply tower-specific coloring
	switch (self.tower.id) {
		case 'laser':
			bulletGraphics.tint = 0xFF0080;
			break;
		case 'ice':
			bulletGraphics.tint = 0x00FFFF;
			break;
		case 'missile':
			bulletGraphics.tint = 0x808080;
			break;
		case 'lightning':
			bulletGraphics.tint = 0xFFFF00;
			break;
		case 'cannon':
			bulletGraphics.tint = 0x8B4513;
			break;
		case 'tesla':
			bulletGraphics.tint = 0x9932CC;
			break;
	}
	// Animate the bullet preview
	var _animateBullet = function animateBullet() {
		tween(bulletGraphics, {
			rotation: bulletGraphics.rotation + Math.PI * 2
		}, {
			duration: 2000,
			easing: tween.linear,
			onFinish: _animateBullet
		});
	};
	_animateBullet();
	// Upgrade cost preview (right side)
	if (self.tower.level < self.tower.maxLevel) {
		var upgradePreview = new Container();
		upgradePreview.x = 600;
		upgradePreview.y = 50;
		self.addChild(upgradePreview);
		var upgradeLabel = new Text2('Next Upgrade:', {
			size: 35,
			fill: 0xFFFFFF,
			weight: 600
		});
		upgradeLabel.anchor.set(0.5, 0.5);
		upgradeLabel.y = -40;
		upgradePreview.addChild(upgradeLabel);
		// Calculate next level stats
		var nextDamage = self.tower.damage + 5;
		var nextFireRate = Math.max(20, self.tower.fireRate - 8);
		var nextDPS = (nextDamage * (60 / nextFireRate)).toFixed(1);
		var nextRange = ((self.tower.getRange() + CELL_SIZE * 0.5) / CELL_SIZE).toFixed(1);
		var upgradeStatsText = new Text2('DPS: ' + nextDPS + ' | Range: ' + nextRange + ' tiles', {
			size: 32,
			fill: 0x2ECC71,
			weight: 400
		});
		upgradeStatsText.anchor.set(0.5, 0.5);
		upgradeStatsText.y = 10;
		upgradePreview.addChild(upgradeStatsText);
	}
	self.updateStats = function () {
		var dpsValue = (self.tower.damage * (60 / self.tower.fireRate)).toFixed(1);
		var rangeValue = (self.tower.getRange() / CELL_SIZE).toFixed(1);
		titleText.setText(self.tower.id.charAt(0).toUpperCase() + self.tower.id.slice(1) + ' Tower - Level ' + self.tower.level);
		statsText.setText('DPS: ' + dpsValue + ' | Range: ' + rangeValue + ' tiles | Speed: ' + self.tower.bulletSpeed);
	};
	return self;
});
var TowerPreview = Container.expand(function () {
	var self = Container.call(this);
	var towerRange = 3;
	var rangeInPixels = towerRange * CELL_SIZE;
	self.towerType = 'default';
	self.hasEnoughGold = true;
	var rangeIndicator = new Container();
	self.addChild(rangeIndicator);
	var rangeGraphics = rangeIndicator.attachAsset('rangeCircle', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	rangeGraphics.alpha = 0.3;
	var previewGraphics = self.attachAsset('towerpreview', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	previewGraphics.width = CELL_SIZE * 2;
	previewGraphics.height = CELL_SIZE * 2;
	self.canPlace = false;
	self.gridX = 0;
	self.gridY = 0;
	self.blockedByEnemy = false;
	self.update = function () {
		var previousHasEnoughGold = self.hasEnoughGold;
		self.hasEnoughGold = gold >= getTowerCost(self.towerType);
		// Only update appearance if the affordability status has changed
		if (previousHasEnoughGold !== self.hasEnoughGold) {
			self.updateAppearance();
		}
	};
	self.updateAppearance = function () {
		// Use Tower class to get the source of truth for range
		var tempTower = new Tower(self.towerType);
		var previewRange = tempTower.getRange();
		// Clean up tempTower to avoid memory leaks
		if (tempTower && tempTower.destroy) {
			tempTower.destroy();
		}
		// Set range indicator using unified range logic
		rangeGraphics.width = rangeGraphics.height = previewRange * 2;
		// Use single tower asset for preview
		var previewAsset = 'towerpreview'; // Use existing preview asset
		// Update the preview graphics appearance
		previewGraphics.tint = 0xFFFFFF; // Keep white tint as base
		if (!self.canPlace || !self.hasEnoughGold) {
			previewGraphics.tint = 0xFF0000;
			previewGraphics.alpha = 0.6;
		} else {
			previewGraphics.alpha = 0.8;
		}
	};
	self.updatePlacementStatus = function () {
		var validGridPlacement = true;
		if (self.gridY <= 4 || self.gridY + 1 >= grid.cells[0].length - 4) {
			validGridPlacement = false;
		} else {
			for (var i = 0; i < 2; i++) {
				for (var j = 0; j < 2; j++) {
					var cell = grid.getCell(self.gridX + i, self.gridY + j);
					// Allow towers to be placed on walls (type 1) but not on paths (type 0), spawns (type 2), or goals (type 3)
					if (!cell || cell.type === 0 || cell.type === 2 || cell.type === 3) {
						validGridPlacement = false;
						break;
					}
				}
				if (!validGridPlacement) {
					break;
				}
			}
		}
		self.blockedByEnemy = false;
		if (validGridPlacement) {
			for (var i = 0; i < enemies.length; i++) {
				var enemy = enemies[i];
				if (enemy.currentCellY < 4) {
					continue;
				}
				// Only check non-flying enemies, flying enemies can pass over towers
				if (!enemy.isFlying) {
					if (enemy.cellX >= self.gridX && enemy.cellX < self.gridX + 2 && enemy.cellY >= self.gridY && enemy.cellY < self.gridY + 2) {
						self.blockedByEnemy = true;
						break;
					}
					if (enemy.currentTarget) {
						var targetX = enemy.currentTarget.x;
						var targetY = enemy.currentTarget.y;
						if (targetX >= self.gridX && targetX < self.gridX + 2 && targetY >= self.gridY && targetY < self.gridY + 2) {
							self.blockedByEnemy = true;
							break;
						}
					}
				}
			}
		}
		self.canPlace = validGridPlacement && !self.blockedByEnemy;
		self.hasEnoughGold = gold >= getTowerCost(self.towerType);
		self.updateAppearance();
	};
	self.checkPlacement = function () {
		self.updatePlacementStatus();
	};
	self.snapToGrid = function (x, y) {
		var gridPosX = x - grid.x;
		var gridPosY = y - grid.y;
		self.gridX = Math.floor(gridPosX / CELL_SIZE);
		self.gridY = Math.floor(gridPosY / CELL_SIZE);
		self.x = grid.x + self.gridX * CELL_SIZE + CELL_SIZE / 2;
		self.y = grid.y + self.gridY * CELL_SIZE + CELL_SIZE / 2;
		self.checkPlacement();
	};
	return self;
});
var UpgradeMenu = Container.expand(function (tower) {
	var self = Container.call(this);
	self.tower = tower;
	self.y = 2732 + 225;
	var menuBackground = self.attachAsset('notification', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	menuBackground.width = 2048;
	menuBackground.height = 500;
	menuBackground.tint = 0x444444;
	menuBackground.alpha = 0.9;
	var towerTypeText = new Text2(self.tower.id.charAt(0).toUpperCase() + self.tower.id.slice(1) + ' Tower', {
		size: 60,
		fill: 0xFFFFFF,
		weight: 800
	});
	towerTypeText.anchor.set(0, 0);
	towerTypeText.x = -700;
	towerTypeText.y = -120;
	self.addChild(towerTypeText);
	var statsText = new Text2('Lvl ' + self.tower.level + '/' + self.tower.maxLevel + ' | Dmg: ' + self.tower.damage + ' | Rate: ' + (60 / self.tower.fireRate).toFixed(1) + '/s', {
		size: 50,
		fill: 0xFFFFFF,
		weight: 400
	});
	statsText.anchor.set(0, 0.5);
	statsText.x = -700;
	statsText.y = -40;
	self.addChild(statsText);
	var buttonsContainer = new Container();
	buttonsContainer.x = 500;
	self.addChild(buttonsContainer);
	var upgradeButton = new Container();
	buttonsContainer.addChild(upgradeButton);
	var buttonBackground = upgradeButton.attachAsset('notification', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	buttonBackground.width = 500;
	buttonBackground.height = 150;
	var isMaxLevel = self.tower.level >= self.tower.maxLevel;
	// Exponential upgrade cost: base cost * (2 ^ (level-1)), scaled by tower base cost
	var baseUpgradeCost = getTowerCost(self.tower.id);
	var upgradeCost;
	if (isMaxLevel) {
		upgradeCost = 0;
	} else if (self.tower.level === self.tower.maxLevel - 1) {
		upgradeCost = Math.floor(baseUpgradeCost * Math.pow(2, self.tower.level - 1) * 3.5 / 2);
	} else {
		upgradeCost = Math.floor(baseUpgradeCost * Math.pow(2, self.tower.level - 1));
	}
	buttonBackground.tint = isMaxLevel ? 0x888888 : gold >= upgradeCost ? 0x00AA00 : 0x888888;
	var buttonText = new Text2(isMaxLevel ? 'Max Level' : 'Upgrade: ' + upgradeCost, {
		size: 50,
		fill: 0xFFFFFF,
		weight: 800
	});
	buttonText.anchor.set(0.5, 0.5);
	upgradeButton.addChild(buttonText);
	var sellButton = new Container();
	buttonsContainer.addChild(sellButton);
	var sellButtonBackground = sellButton.attachAsset('notification', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	sellButtonBackground.width = 500;
	sellButtonBackground.height = 150;
	sellButtonBackground.tint = 0xCC0000;
	var totalInvestment = self.tower.getTotalValue ? self.tower.getTotalValue() : 0;
	var sellValue = getTowerSellValue(totalInvestment);
	var sellButtonText = new Text2('Sell: +' + sellValue, {
		size: 50,
		fill: 0xFFFFFF,
		weight: 800
	});
	sellButtonText.anchor.set(0.5, 0.5);
	sellButton.addChild(sellButtonText);
	upgradeButton.y = -85;
	sellButton.y = 85;
	var closeButton = new Container();
	self.addChild(closeButton);
	var closeBackground = closeButton.attachAsset('notification', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	closeBackground.width = 90;
	closeBackground.height = 90;
	closeBackground.tint = 0xAA0000;
	var closeText = new Text2('X', {
		size: 68,
		fill: 0xFFFFFF,
		weight: 800
	});
	closeText.anchor.set(0.5, 0.5);
	closeButton.addChild(closeText);
	closeButton.x = menuBackground.width / 2 - 57;
	closeButton.y = -menuBackground.height / 2 + 57;
	upgradeButton.down = function (x, y, obj) {
		// Play UI click sound
		LK.getSound('ui_click').play();
		if (self.tower.level >= self.tower.maxLevel) {
			var notification = game.addChild(new Notification("Tower is already at max level!"));
			notification.x = 2048 / 2;
			notification.y = grid.height - 50;
			return;
		}
		if (self.tower.upgrade()) {
			// Exponential upgrade cost: base cost * (2 ^ (level-1)), scaled by tower base cost
			var baseUpgradeCost = getTowerCost(self.tower.id);
			if (self.tower.level >= self.tower.maxLevel) {
				upgradeCost = 0;
			} else if (self.tower.level === self.tower.maxLevel - 1) {
				upgradeCost = Math.floor(baseUpgradeCost * Math.pow(2, self.tower.level - 1) * 3.5 / 2);
			} else {
				upgradeCost = Math.floor(baseUpgradeCost * Math.pow(2, self.tower.level - 1));
			}
			statsText.setText('Lvl ' + self.tower.level + '/' + self.tower.maxLevel + ' | Dmg: ' + self.tower.damage + ' | Rate: ' + (60 / self.tower.fireRate).toFixed(1) + '/s');
			buttonText.setText('Upgrade: ' + upgradeCost);
			var totalInvestment = self.tower.getTotalValue ? self.tower.getTotalValue() : 0;
			var sellValue = Math.floor(totalInvestment * 0.6);
			sellButtonText.setText('Sell: +' + sellValue);
			if (self.tower.level >= self.tower.maxLevel) {
				buttonBackground.tint = 0x888888;
				buttonText.setText('Max Level');
			}
			var rangeCircle = null;
			for (var i = 0; i < game.children.length; i++) {
				if (game.children[i].isTowerRange && game.children[i].tower === self.tower) {
					rangeCircle = game.children[i];
					break;
				}
			}
			if (rangeCircle) {
				var rangeGraphics = rangeCircle.children[0];
				rangeGraphics.width = rangeGraphics.height = self.tower.getRange() * 2;
			} else {
				var newRangeIndicator = new Container();
				newRangeIndicator.isTowerRange = true;
				newRangeIndicator.tower = self.tower;
				game.addChildAt(newRangeIndicator, 0);
				newRangeIndicator.x = self.tower.x;
				newRangeIndicator.y = self.tower.y;
				var rangeGraphics = newRangeIndicator.attachAsset('rangeCircle', {
					anchorX: 0.5,
					anchorY: 0.5
				});
				rangeGraphics.width = rangeGraphics.height = self.tower.getRange() * 2;
				rangeGraphics.alpha = 0.3;
			}
			tween(self, {
				scaleX: 1.05,
				scaleY: 1.05
			}, {
				duration: 100,
				easing: tween.easeOut,
				onFinish: function onFinish() {
					tween(self, {
						scaleX: 1,
						scaleY: 1
					}, {
						duration: 100,
						easing: tween.easeIn
					});
				}
			});
		}
	};
	sellButton.down = function (x, y, obj) {
		// Play UI click sound
		LK.getSound('ui_click').play();
		var totalInvestment = self.tower.getTotalValue ? self.tower.getTotalValue() : 0;
		var sellValue = getTowerSellValue(totalInvestment);
		setGold(gold + sellValue);
		var notification = game.addChild(new Notification("Tower sold for " + sellValue + " gold!"));
		notification.x = 2048 / 2;
		notification.y = grid.height - 50;
		var gridX = self.tower.gridX;
		var gridY = self.tower.gridY;
		for (var i = 0; i < 2; i++) {
			for (var j = 0; j < 2; j++) {
				var cell = grid.getCell(gridX + i, gridY + j);
				if (cell) {
					cell.type = 0;
					var towerIndex = cell.towersInRange.indexOf(self.tower);
					if (towerIndex !== -1) {
						cell.towersInRange.splice(towerIndex, 1);
					}
				}
			}
		}
		// Stop any ongoing animations before destroying tower
		if (self.tower.stopIdleAnimation) {
			self.tower.stopIdleAnimation();
		}
		if (selectedTower === self.tower) {
			selectedTower = null;
		}
		var towerIndex = towers.indexOf(self.tower);
		if (towerIndex !== -1) {
			towers.splice(towerIndex, 1);
		}
		towerLayer.removeChild(self.tower);
		grid.pathFind();
		grid.renderDebug();
		self.destroy();
		for (var i = 0; i < game.children.length; i++) {
			if (game.children[i].isTowerRange && game.children[i].tower === self.tower) {
				game.removeChild(game.children[i]);
				break;
			}
		}
	};
	closeButton.down = function (x, y, obj) {
		// Play UI click sound
		LK.getSound('ui_click').play();
		hideUpgradeMenu(self);
		selectedTower = null;
		grid.renderDebug();
	};
	self.update = function () {
		if (self.tower.level >= self.tower.maxLevel) {
			if (buttonText.text !== 'Max Level') {
				buttonText.setText('Max Level');
				buttonBackground.tint = 0x888888;
			}
			return;
		}
		// Exponential upgrade cost: base cost * (2 ^ (level-1)), scaled by tower base cost
		var baseUpgradeCost = getTowerCost(self.tower.id);
		var currentUpgradeCost;
		if (self.tower.level >= self.tower.maxLevel) {
			currentUpgradeCost = 0;
		} else if (self.tower.level === self.tower.maxLevel - 1) {
			currentUpgradeCost = Math.floor(baseUpgradeCost * Math.pow(2, self.tower.level - 1) * 3.5 / 2);
		} else {
			currentUpgradeCost = Math.floor(baseUpgradeCost * Math.pow(2, self.tower.level - 1));
		}
		var canAfford = gold >= currentUpgradeCost;
		buttonBackground.tint = canAfford ? 0x00AA00 : 0x888888;
		var newText = 'Upgrade: ' + currentUpgradeCost;
		if (buttonText.text !== newText) {
			buttonText.setText(newText);
		}
	};
	return self;
});
var WaveAlert = Container.expand(function (waveNumber, waveType, enemyCount) {
	var self = Container.call(this);
	// Create alert background
	var alertBackground = self.attachAsset('notification', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	alertBackground.width = 1200;
	alertBackground.height = 200;
	alertBackground.tint = 0xFF4444; // Red alert color
	// Create wave number text
	var waveText = new Text2("WAVE " + waveNumber, {
		size: 80,
		fill: 0xFFFFFF,
		weight: 800
	});
	waveText.anchor.set(0.5, 0.5);
	waveText.y = -30;
	self.addChild(waveText);
	// Create wave type and enemy count text
	var typeText = new Text2(waveType + " - " + enemyCount + " ENEMIES", {
		size: 50,
		fill: 0xFFFFFF,
		weight: 600
	});
	typeText.anchor.set(0.5, 0.5);
	typeText.y = 30;
	self.addChild(typeText);
	// Start off-screen
	self.x = 2048 / 2;
	self.y = -200;
	self.alpha = 0;
	self.scaleX = 0.5;
	self.scaleY = 0.5;
	// Play wave alert sound
	LK.getSound('wave_alert').play();
	// Animate entrance
	tween(self, {
		y: 400,
		alpha: 1,
		scaleX: 1.2,
		scaleY: 1.2
	}, {
		duration: 500,
		easing: tween.backOut,
		onFinish: function onFinish() {
			// Scale down slightly and hold
			tween(self, {
				scaleX: 1,
				scaleY: 1
			}, {
				duration: 200,
				easing: tween.easeOut,
				onFinish: function onFinish() {
					// Hold for a moment then fade out
					LK.setTimeout(function () {
						tween(self, {
							y: 200,
							alpha: 0,
							scaleX: 0.8,
							scaleY: 0.8
						}, {
							duration: 400,
							easing: tween.easeIn,
							onFinish: function onFinish() {
								self.destroy();
							}
						});
					}, 1500);
				}
			});
		}
	});
	return self;
});
var WaveIndicator = Container.expand(function () {
	var self = Container.call(this);
	self.gameStarted = false;
	self.waveMarkers = [];
	self.waveTypes = [];
	self.enemyCounts = [];
	self.indicatorWidth = 0;
	self.lastBossType = null; // Track the last boss type to avoid repeating
	var blockWidth = 400;
	var totalBlocksWidth = blockWidth * totalWaves;
	var startMarker = new Container();
	var startBlock = startMarker.attachAsset('notification', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	startBlock.width = blockWidth - 10;
	startBlock.height = 70 * 2;
	startBlock.tint = 0x00AA00;
	var startText = new Text2("Start Game", {
		size: 50,
		fill: 0xFFFFFF,
		weight: 800
	});
	startText.anchor.set(0.5, 0.5);
	startMarker.addChild(startText);
	startMarker.x = -self.indicatorWidth;
	self.addChild(startMarker);
	self.waveMarkers.push(startMarker);
	startMarker.down = function () {
		if (!self.gameStarted) {
			self.gameStarted = true;
			currentWave = 0;
			waveTimer = nextWaveTime;
			startBlock.tint = 0x00FF00;
			startText.setText("Started!");
			var notification = game.addChild(new Notification("Game started! Wave 1 incoming!"));
			notification.x = 2048 / 2;
			notification.y = grid.height - 150;
		}
	};
	for (var i = 0; i < totalWaves; i++) {
		var marker = new Container();
		var block = marker.attachAsset('notification', {
			anchorX: 0.5,
			anchorY: 0.5
		});
		block.width = blockWidth - 10;
		block.height = 70 * 2;
		// --- Begin new unified wave logic ---
		var waveType = "normal";
		var enemyType = "normal";
		var enemyCount = 10;
		var isBossWave = (i + 1) % 10 === 0;
		// Ensure all types appear in early waves
		if (i === 0) {
			block.tint = 0xAAAAAA;
			waveType = "Normal";
			enemyType = "normal";
			enemyCount = 10;
		} else if (i === 1) {
			block.tint = 0x00AAFF;
			waveType = "Fast";
			enemyType = "fast";
			enemyCount = 10;
		} else if (i === 2) {
			block.tint = 0xAA0000;
			waveType = "Immune";
			enemyType = "immune";
			enemyCount = 10;
		} else if (i === 3) {
			block.tint = 0xFFFF00;
			waveType = "Flying";
			enemyType = "flying";
			enemyCount = 10;
		} else if (i === 4) {
			block.tint = 0xFF00FF;
			waveType = "Swarm";
			enemyType = "swarm";
			enemyCount = 30;
		} else if (isBossWave) {
			// Boss waves: cycle through all boss types, last boss is always flying
			var bossTypes = ['normal', 'fast', 'immune', 'flying'];
			var bossTypeIndex = Math.floor((i + 1) / 10) - 1;
			if (i === totalWaves - 1) {
				// Last boss is always flying
				enemyType = 'flying';
				waveType = "Boss Flying";
				block.tint = 0xFFFF00;
			} else {
				enemyType = bossTypes[bossTypeIndex % bossTypes.length];
				switch (enemyType) {
					case 'normal':
						block.tint = 0xAAAAAA;
						waveType = "Boss Normal";
						break;
					case 'fast':
						block.tint = 0x00AAFF;
						waveType = "Boss Fast";
						break;
					case 'immune':
						block.tint = 0xAA0000;
						waveType = "Boss Immune";
						break;
					case 'flying':
						block.tint = 0xFFFF00;
						waveType = "Boss Flying";
						break;
				}
			}
			enemyCount = 1;
			// Make the wave indicator for boss waves stand out
			// Set boss wave color to the color of the wave type
			switch (enemyType) {
				case 'normal':
					block.tint = 0xAAAAAA;
					break;
				case 'fast':
					block.tint = 0x00AAFF;
					break;
				case 'immune':
					block.tint = 0xAA0000;
					break;
				case 'flying':
					block.tint = 0xFFFF00;
					break;
				default:
					block.tint = 0xFF0000;
					break;
			}
		} else if ((i + 1) % 5 === 0) {
			// Every 5th non-boss wave is fast
			block.tint = 0x00AAFF;
			waveType = "Fast";
			enemyType = "fast";
			enemyCount = 10;
		} else if ((i + 1) % 4 === 0) {
			// Every 4th non-boss wave is immune
			block.tint = 0xAA0000;
			waveType = "Immune";
			enemyType = "immune";
			enemyCount = 10;
		} else if ((i + 1) % 7 === 0) {
			// Every 7th non-boss wave is flying
			block.tint = 0xFFFF00;
			waveType = "Flying";
			enemyType = "flying";
			enemyCount = 10;
		} else if ((i + 1) % 3 === 0) {
			// Every 3rd non-boss wave is swarm
			block.tint = 0xFF00FF;
			waveType = "Swarm";
			enemyType = "swarm";
			enemyCount = 30;
		} else {
			block.tint = 0xAAAAAA;
			waveType = "Normal";
			enemyType = "normal";
			enemyCount = 10;
		}
		// --- End new unified wave logic ---
		// Mark boss waves with a special visual indicator
		if (isBossWave && enemyType !== 'swarm') {
			// Add a crown or some indicator to the wave marker for boss waves
			var bossIndicator = marker.attachAsset('towerLevelIndicator', {
				anchorX: 0.5,
				anchorY: 0.5
			});
			bossIndicator.width = 30;
			bossIndicator.height = 30;
			bossIndicator.tint = 0xFFD700; // Gold color
			bossIndicator.y = -block.height / 2 - 15;
			// Change the wave type text to indicate boss
			waveType = "BOSS";
		}
		// Store the wave type and enemy count
		self.waveTypes[i] = enemyType;
		self.enemyCounts[i] = enemyCount;
		// Add wave type text - simplified
		var waveTypeText = new Text2(waveType, {
			size: 50,
			fill: 0xFFFFFF,
			weight: 800
		});
		waveTypeText.anchor.set(0.5, 0.5);
		waveTypeText.y = -15;
		marker.addChild(waveTypeText);
		// Main wave number text - simplified
		var waveNum = new Text2((i + 1).toString(), {
			size: 40,
			fill: 0xFFFFFF,
			weight: 800
		});
		waveNum.anchor.set(0.5, 0.5);
		waveNum.y = 20;
		marker.addChild(waveNum);
		marker.x = -self.indicatorWidth + (i + 1) * blockWidth;
		self.addChild(marker);
		self.waveMarkers.push(marker);
	}
	// Get wave type for a specific wave number
	self.getWaveType = function (waveNumber) {
		if (waveNumber < 1 || waveNumber > totalWaves) {
			return "normal";
		}
		// If this is a boss wave (waveNumber % 10 === 0), and the type is the same as lastBossType
		// then we should return a different boss type
		var waveType = self.waveTypes[waveNumber - 1];
		return waveType;
	};
	// Get enemy count for a specific wave number
	self.getEnemyCount = function (waveNumber) {
		if (waveNumber < 1 || waveNumber > totalWaves) {
			return 10;
		}
		return self.enemyCounts[waveNumber - 1];
	};
	// Get display name for a wave type
	self.getWaveTypeName = function (waveNumber) {
		var type = self.getWaveType(waveNumber);
		var typeName = type.charAt(0).toUpperCase() + type.slice(1);
		// Add boss prefix for boss waves (every 10th wave)
		if (waveNumber % 10 === 0 && waveNumber > 0 && type !== 'swarm') {
			typeName = "BOSS";
		}
		return typeName;
	};
	self.positionIndicator = new Container();
	var indicator = self.positionIndicator.attachAsset('towerLevelIndicator', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	indicator.width = blockWidth - 10;
	indicator.height = 16;
	indicator.tint = 0xffad0e;
	indicator.y = -65;
	var indicator2 = self.positionIndicator.attachAsset('towerLevelIndicator', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	indicator2.width = blockWidth - 10;
	indicator2.height = 16;
	indicator2.tint = 0xffad0e;
	indicator2.y = 65;
	var leftWall = self.positionIndicator.attachAsset('towerLevelIndicator', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	leftWall.width = 16;
	leftWall.height = 146;
	leftWall.tint = 0xffad0e;
	leftWall.x = -(blockWidth - 16) / 2;
	var rightWall = self.positionIndicator.attachAsset('towerLevelIndicator', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	rightWall.width = 16;
	rightWall.height = 146;
	rightWall.tint = 0xffad0e;
	rightWall.x = (blockWidth - 16) / 2;
	self.addChild(self.positionIndicator);
	self.update = function () {
		var progress = waveTimer / nextWaveTime;
		var moveAmount = (progress + currentWave) * blockWidth;
		for (var i = 0; i < self.waveMarkers.length; i++) {
			var marker = self.waveMarkers[i];
			marker.x = -moveAmount + i * blockWidth;
		}
		self.positionIndicator.x = 0;
		for (var i = 0; i < totalWaves + 1; i++) {
			var marker = self.waveMarkers[i];
			if (i === 0) {
				continue;
			}
			var block = marker.children[0];
			if (i - 1 < currentWave) {
				block.alpha = .5;
			}
		}
		self.handleWaveProgression = function () {
			if (!self.gameStarted) {
				return;
			}
			if (currentWave < totalWaves) {
				waveTimer++;
				if (waveTimer >= nextWaveTime) {
					waveTimer = 0;
					currentWave++;
					waveInProgress = true;
					waveSpawned = false;
					if (currentWave != 1) {
						var waveType = self.getWaveTypeName(currentWave);
						var enemyCount = self.getEnemyCount(currentWave);
						var waveAlert = new WaveAlert(currentWave, waveType + " INCOMING", enemyCount);
						game.addChild(waveAlert);
					}
				}
			}
		};
		self.handleWaveProgression();
	};
	return self;
});
var WavePreviewPanel = Container.expand(function () {
	var self = Container.call(this);
	self.x = 2048 - 400;
	self.y = 300;
	self.visible = false;
	var panelBackground = self.attachAsset('notification', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	panelBackground.width = 350;
	panelBackground.height = 500;
	panelBackground.tint = 0x34495E;
	panelBackground.alpha = 0.9;
	var titleText = new Text2('Next Wave', {
		size: 45,
		fill: 0xFFFFFF,
		weight: 800
	});
	titleText.anchor.set(0.5, 0);
	titleText.x = 0;
	titleText.y = -220;
	self.addChild(titleText);
	var waveNumberText = new Text2('', {
		size: 55,
		fill: 0xE74C3C,
		weight: 800
	});
	waveNumberText.anchor.set(0.5, 0);
	waveNumberText.x = 0;
	waveNumberText.y = -160;
	self.addChild(waveNumberText);
	var enemyTypeText = new Text2('', {
		size: 40,
		fill: 0xF39C12,
		weight: 600
	});
	enemyTypeText.anchor.set(0.5, 0);
	enemyTypeText.x = 0;
	enemyTypeText.y = -100;
	self.addChild(enemyTypeText);
	var enemyCountText = new Text2('', {
		size: 35,
		fill: 0x3498DB,
		weight: 500
	});
	enemyCountText.anchor.set(0.5, 0);
	enemyCountText.x = 0;
	enemyCountText.y = -50;
	self.addChild(enemyCountText);
	// Enemy preview sprite
	var enemyPreview = new Container();
	enemyPreview.x = 0;
	enemyPreview.y = 50;
	self.addChild(enemyPreview);
	var enemySprite = null;
	self.updatePreview = function (waveNumber) {
		if (waveNumber > totalWaves) {
			self.visible = false;
			return;
		}
		self.visible = true;
		waveNumberText.setText('Wave ' + waveNumber);
		var waveType = waveIndicator.getWaveType(waveNumber);
		var enemyCount = waveIndicator.getEnemyCount(waveNumber);
		var isBossWave = waveNumber % 10 === 0 && waveNumber > 0;
		// Update text based on wave type
		var typeDisplayName = waveType.charAt(0).toUpperCase() + waveType.slice(1);
		if (isBossWave && waveType !== 'swarm') {
			typeDisplayName = 'BOSS ' + typeDisplayName;
		}
		enemyTypeText.setText(typeDisplayName);
		enemyCountText.setText(enemyCount + ' Enemies');
		// Remove old enemy sprite
		if (enemySprite) {
			enemyPreview.removeChild(enemySprite);
		}
		// Add new enemy sprite preview
		var assetId = 'enemy';
		if (waveType !== 'normal') {
			assetId = 'enemy_' + waveType;
		}
		enemySprite = enemyPreview.attachAsset(assetId, {
			anchorX: 0.5,
			anchorY: 0.5
		});
		// Scale based on boss status
		if (isBossWave && waveType !== 'swarm') {
			enemySprite.scaleX = 1.5;
			enemySprite.scaleY = 1.5;
		}
		// Add floating animation
		var _floatAnimation = function floatAnimation() {
			tween(enemySprite, {
				y: 10
			}, {
				duration: 1500,
				easing: tween.easeInOut,
				onFinish: function onFinish() {
					tween(enemySprite, {
						y: -10
					}, {
						duration: 1500,
						easing: tween.easeInOut,
						onFinish: _floatAnimation
					});
				}
			});
		};
		_floatAnimation();
		// Special effects for boss waves
		if (isBossWave && waveType !== 'swarm') {
			// Add crown indicator
			var crownIndicator = enemyPreview.attachAsset('towerLevelIndicator', {
				anchorX: 0.5,
				anchorY: 0.5
			});
			crownIndicator.width = 25;
			crownIndicator.height = 25;
			crownIndicator.tint = 0xFFD700;
			crownIndicator.y = -60;
			// Pulsing animation for crown
			var _pulseCrown = function pulseCrown() {
				tween(crownIndicator, {
					scaleX: 1.3,
					scaleY: 1.3
				}, {
					duration: 800,
					easing: tween.easeInOut,
					onFinish: function onFinish() {
						tween(crownIndicator, {
							scaleX: 1.0,
							scaleY: 1.0
						}, {
							duration: 800,
							easing: tween.easeInOut,
							onFinish: _pulseCrown
						});
					}
				});
			};
			_pulseCrown();
		}
	};
	return self;
});
/**** 
* Initialize Game
****/ 
var game = new LK.Game({
	backgroundColor: 0xF4E4BC
});
/**** 
* Game Code
****/ 
var isHidingUpgradeMenu = false;
function hideUpgradeMenu(menu) {
	if (isHidingUpgradeMenu) {
		return;
	}
	isHidingUpgradeMenu = true;
	// Also hide any tower info panels
	var towerInfoPanels = game.children.filter(function (child) {
		return child instanceof TowerInfoPanel;
	});
	for (var i = 0; i < towerInfoPanels.length; i++) {
		(function (panel) {
			tween(panel, {
				y: 2732 + 300
			}, {
				duration: 150,
				easing: tween.easeIn,
				onFinish: function onFinish() {
					if (panel && panel.destroy) {
						panel.destroy();
					}
				}
			});
		})(towerInfoPanels[i]);
	}
	tween(menu, {
		y: 2732 + 225
	}, {
		duration: 150,
		easing: tween.easeIn,
		onFinish: function onFinish() {
			menu.destroy();
			isHidingUpgradeMenu = false;
		}
	});
}
var CELL_SIZE = 76;
var pathId = 1;
var maxScore = 0;
var enemies = [];
var towers = [];
var bullets = [];
var defenses = [];
var selectedTower = null;
var gold = 80;
var lives = 20;
var score = 0;
var currentWave = 0;
var totalWaves = 50;
var waveTimer = 0;
var waveInProgress = false;
var waveSpawned = false;
var nextWaveTime = 12000 / 2;
var sourceTower = null;
var enemiesToSpawn = 10; // Default number of enemies per wave
var goldText = new Text2('Gold: ' + gold, {
	size: 65,
	fill: 0xDAA520,
	weight: 800
});
goldText.anchor.set(0.5, 0.5);
var livesText = new Text2('Lives: ' + lives, {
	size: 65,
	fill: 0x1E90FF,
	weight: 800
});
livesText.anchor.set(0.5, 0.5);
var scoreText = new Text2('Score: ' + score, {
	size: 65,
	fill: 0xFF7F50,
	weight: 800
});
scoreText.anchor.set(0.5, 0.5);
var topMargin = 50;
var centerX = 2048 / 2;
var spacing = 400;
LK.gui.top.addChild(goldText);
LK.gui.top.addChild(livesText);
LK.gui.top.addChild(scoreText);
livesText.x = 0;
livesText.y = topMargin;
goldText.x = -spacing;
goldText.y = topMargin;
scoreText.x = spacing;
scoreText.y = topMargin;
function updateUI() {
	goldText.setText('Gold: ' + gold);
	livesText.setText('Lives: ' + lives);
	scoreText.setText('Score: ' + score);
}
function setGold(value) {
	gold = value;
	updateUI();
}
var debugLayer = new Container();
var towerLayer = new Container();
// Create three separate layers for enemy hierarchy
var enemyLayerBottom = new Container(); // For normal enemies
var enemyLayerMiddle = new Container(); // For shadows
var enemyLayerTop = new Container(); // For flying enemies
var enemyLayer = new Container(); // Main container to hold all enemy layers
// Add layers in correct order (bottom first, then middle for shadows, then top)
enemyLayer.addChild(enemyLayerBottom);
enemyLayer.addChild(enemyLayerMiddle);
enemyLayer.addChild(enemyLayerTop);
var grid = new Grid(24, 29 + 6);
grid.x = 150;
grid.y = 200 - CELL_SIZE * 4;
grid.pathFind();
grid.renderDebug();
debugLayer.addChild(grid);
game.addChild(debugLayer);
game.addChild(towerLayer);
game.addChild(enemyLayer);
var offset = 0;
var towerPreview = new TowerPreview();
game.addChild(towerPreview);
towerPreview.visible = false;
var isDragging = false;
function wouldBlockPath(gridX, gridY) {
	var cells = [];
	for (var i = 0; i < 2; i++) {
		for (var j = 0; j < 2; j++) {
			var cell = grid.getCell(gridX + i, gridY + j);
			if (cell) {
				cells.push({
					cell: cell,
					originalType: cell.type
				});
				cell.type = 1;
			}
		}
	}
	var blocked = grid.pathFind();
	for (var i = 0; i < cells.length; i++) {
		cells[i].cell.type = cells[i].originalType;
	}
	grid.pathFind();
	grid.renderDebug();
	return blocked;
}
function getTowerCost(towerType) {
	var cost = 15;
	switch (towerType) {
		case 'laser':
			cost = 15;
			break;
		case 'ice':
			cost = 25;
			break;
		case 'missile':
			cost = 40;
			break;
		case 'lightning':
			cost = 20;
			break;
		case 'cannon':
			cost = 35;
			break;
		case 'tesla':
			cost = 30;
			break;
	}
	return cost;
}
function getTowerSellValue(totalValue) {
	return waveIndicator && waveIndicator.gameStarted ? Math.floor(totalValue * 0.6) : totalValue;
}
function placeTower(gridX, gridY, towerType) {
	var towerCost = getTowerCost(towerType);
	var canAfford = gold >= towerCost;
	if (canAfford) {
		var tower = new Tower(towerType || 'default');
		tower.placeOnGrid(gridX, gridY);
		towerLayer.addChild(tower);
		towers.push(tower);
		setGold(gold - towerCost);
		grid.pathFind();
		grid.renderDebug();
		return true;
	} else {
		var notification = game.addChild(new Notification("Not enough gold!"));
		notification.x = 2048 / 2;
		notification.y = grid.height - 50;
		return false;
	}
}
game.down = function (x, y, obj) {
	var upgradeMenuVisible = game.children.some(function (child) {
		return child instanceof UpgradeMenu;
	});
	if (upgradeMenuVisible) {
		return;
	}
	for (var i = 0; i < sourceTowers.length; i++) {
		var tower = sourceTowers[i];
		if (x >= tower.x - tower.width / 2 && x <= tower.x + tower.width / 2 && y >= tower.y - tower.height / 2 && y <= tower.y + tower.height / 2) {
			towerPreview.visible = true;
			isDragging = true;
			towerPreview.towerType = tower.towerType;
			towerPreview.updateAppearance();
			// Apply the same offset as in move handler to ensure consistency when starting drag
			towerPreview.snapToGrid(x, y - CELL_SIZE * 1.5);
			break;
		}
	}
};
game.move = function (x, y, obj) {
	if (isDragging) {
		// Shift the y position upward by 1.5 tiles to show preview above finger
		towerPreview.snapToGrid(x, y - CELL_SIZE * 1.5);
	}
};
game.up = function (x, y, obj) {
	var clickedOnTower = false;
	for (var i = 0; i < towers.length; i++) {
		var tower = towers[i];
		var towerLeft = tower.x - tower.width / 2;
		var towerRight = tower.x + tower.width / 2;
		var towerTop = tower.y - tower.height / 2;
		var towerBottom = tower.y + tower.height / 2;
		if (x >= towerLeft && x <= towerRight && y >= towerTop && y <= towerBottom) {
			clickedOnTower = true;
			break;
		}
	}
	var upgradeMenus = game.children.filter(function (child) {
		return child instanceof UpgradeMenu;
	});
	if (upgradeMenus.length > 0 && !isDragging && !clickedOnTower) {
		var clickedOnMenu = false;
		for (var i = 0; i < upgradeMenus.length; i++) {
			var menu = upgradeMenus[i];
			var menuWidth = 2048;
			var menuHeight = 450;
			var menuLeft = menu.x - menuWidth / 2;
			var menuRight = menu.x + menuWidth / 2;
			var menuTop = menu.y - menuHeight / 2;
			var menuBottom = menu.y + menuHeight / 2;
			if (x >= menuLeft && x <= menuRight && y >= menuTop && y <= menuBottom) {
				clickedOnMenu = true;
				break;
			}
		}
		if (!clickedOnMenu) {
			for (var i = 0; i < upgradeMenus.length; i++) {
				var menu = upgradeMenus[i];
				hideUpgradeMenu(menu);
			}
			for (var i = game.children.length - 1; i >= 0; i--) {
				if (game.children[i].isTowerRange) {
					game.removeChild(game.children[i]);
				}
			}
			selectedTower = null;
			grid.renderDebug();
		}
	}
	if (isDragging) {
		isDragging = false;
		if (towerPreview.canPlace) {
			if (!wouldBlockPath(towerPreview.gridX, towerPreview.gridY)) {
				placeTower(towerPreview.gridX, towerPreview.gridY, towerPreview.towerType);
			} else {
				var notification = game.addChild(new Notification("Tower would block the path!"));
				notification.x = 2048 / 2;
				notification.y = grid.height - 50;
			}
		} else if (towerPreview.blockedByEnemy) {
			var notification = game.addChild(new Notification("Cannot build: Enemy in the way!"));
			notification.x = 2048 / 2;
			notification.y = grid.height - 50;
		} else if (towerPreview.visible) {
			var notification = game.addChild(new Notification("Cannot build here!"));
			notification.x = 2048 / 2;
			notification.y = grid.height - 50;
		}
		towerPreview.visible = false;
		if (isDragging) {
			var upgradeMenus = game.children.filter(function (child) {
				return child instanceof UpgradeMenu;
			});
			for (var i = 0; i < upgradeMenus.length; i++) {
				upgradeMenus[i].destroy();
			}
		}
	}
};
var waveIndicator = new WaveIndicator();
waveIndicator.x = 2048 / 2;
waveIndicator.y = 2732 - 80;
game.addChild(waveIndicator);
var nextWaveButtonContainer = new Container();
var nextWaveButton = new NextWaveButton();
nextWaveButton.x = 2048 - 200;
nextWaveButton.y = 2732 - 100 + 20;
nextWaveButtonContainer.addChild(nextWaveButton);
game.addChild(nextWaveButtonContainer);
// Add wave preview panel
var wavePreviewPanel = new WavePreviewPanel();
game.addChild(wavePreviewPanel);
var towerTypes = ['laser', 'ice', 'missile', 'lightning', 'cannon', 'tesla'];
var sourceTowers = [];
var towerSpacing = 300; // Increase spacing for larger towers
var startX = 2048 / 2 - towerTypes.length * towerSpacing / 2 + towerSpacing / 2;
var towerY = 2732 - CELL_SIZE * 3 - 90;
for (var i = 0; i < towerTypes.length; i++) {
	var tower = new SourceTower(towerTypes[i]);
	tower.x = startX + i * towerSpacing;
	tower.y = towerY;
	towerLayer.addChild(tower);
	sourceTowers.push(tower);
	// Add cost display text below each tower
	var costText = new Text2('Cost: ' + getTowerCost(towerTypes[i]), {
		size: 35,
		fill: 0xFFD700,
		weight: 800
	});
	costText.anchor.set(0.5, 0.5);
	costText.x = tower.x;
	costText.y = tower.y + 120; // Position below the tower
	towerLayer.addChild(costText);
}
// Start playing background music
LK.playMusic('game_music', {
	loop: true,
	fade: {
		start: 0,
		end: 0.3,
		duration: 2000
	}
});
sourceTower = null;
enemiesToSpawn = 10;
// Create environmental elements layer
var environmentLayer = new Container();
game.addChildAt(environmentLayer, 0); // Add behind everything else
// Create background clouds
var clouds = [];
for (var i = 0; i < 8; i++) {
	var cloud = new Cloud(Math.random() * 2048,
	// Random x position
	100 + Math.random() * 300 // Random y position in sky
	);
	environmentLayer.addChild(cloud);
	clouds.push(cloud);
}
// Add environmental decorations around the grid edges
var environmentalElements = [];
// Add palm trees along the sides of the grid
var palmTreePositions = [{
	x: grid.x - 100,
	y: grid.y + 300
}, {
	x: grid.x - 80,
	y: grid.y + 800
}, {
	x: grid.x - 120,
	y: grid.y + 1300
}, {
	x: grid.x + grid.cells.length * CELL_SIZE + 80,
	y: grid.y + 400
}, {
	x: grid.x + grid.cells.length * CELL_SIZE + 100,
	y: grid.y + 900
}, {
	x: grid.x + grid.cells.length * CELL_SIZE + 60,
	y: grid.y + 1400
}];
for (var i = 0; i < palmTreePositions.length; i++) {
	var pos = palmTreePositions[i];
	var palmTree = new EnvironmentalElement('palm_tree', pos.x, pos.y);
	environmentLayer.addChild(palmTree);
	environmentalElements.push(palmTree);
}
// Add rocks scattered around the edges
var rockPositions = [{
	x: grid.x - 60,
	y: grid.y + 200
}, {
	x: grid.x - 40,
	y: grid.y + 600
}, {
	x: grid.x - 80,
	y: grid.y + 1100
}, {
	x: grid.x - 50,
	y: grid.y + 1600
}, {
	x: grid.x + grid.cells.length * CELL_SIZE + 40,
	y: grid.y + 250
}, {
	x: grid.x + grid.cells.length * CELL_SIZE + 60,
	y: grid.y + 700
}, {
	x: grid.x + grid.cells.length * CELL_SIZE + 30,
	y: grid.y + 1200
}, {
	x: grid.x + grid.cells.length * CELL_SIZE + 70,
	y: grid.y + 1500
}];
for (var i = 0; i < rockPositions.length; i++) {
	var pos = rockPositions[i];
	var rock = new EnvironmentalElement('rock', pos.x, pos.y);
	environmentLayer.addChild(rock);
	environmentalElements.push(rock);
}
// Add seashells scattered around the beach areas
var seashellPositions = [{
	x: grid.x - 30,
	y: grid.y + 150
}, {
	x: grid.x - 70,
	y: grid.y + 500
}, {
	x: grid.x - 20,
	y: grid.y + 850
}, {
	x: grid.x - 90,
	y: grid.y + 1250
}, {
	x: grid.x - 45,
	y: grid.y + 1550
}, {
	x: grid.x + grid.cells.length * CELL_SIZE + 20,
	y: grid.y + 180
}, {
	x: grid.x + grid.cells.length * CELL_SIZE + 50,
	y: grid.y + 550
}, {
	x: grid.x + grid.cells.length * CELL_SIZE + 15,
	y: grid.y + 950
}, {
	x: grid.x + grid.cells.length * CELL_SIZE + 45,
	y: grid.y + 1350
}, {
	x: grid.x + grid.cells.length * CELL_SIZE + 25,
	y: grid.y + 1650
}];
for (var i = 0; i < seashellPositions.length; i++) {
	var pos = seashellPositions[i];
	var seashell = new EnvironmentalElement('seashell', pos.x, pos.y);
	environmentLayer.addChild(seashell);
	environmentalElements.push(seashell);
}
game.update = function () {
	if (waveInProgress) {
		if (!waveSpawned) {
			waveSpawned = true;
			// Get wave type and enemy count from the wave indicator
			var waveType = waveIndicator.getWaveType(currentWave);
			var enemyCount = waveIndicator.getEnemyCount(currentWave);
			// Check if this is a boss wave
			var isBossWave = currentWave % 10 === 0 && currentWave > 0;
			if (isBossWave && waveType !== 'swarm') {
				// Boss waves have just 1 enemy regardless of what the wave indicator says
				enemyCount = 1;
				// Show boss announcement with enhanced alert
				var bossAlert = new WaveAlert(currentWave, "⚠️ BOSS " + waveType.toUpperCase() + " ⚠️", enemyCount);
				game.addChild(bossAlert);
			}
			// Show wave alert for all waves except wave 1
			if (currentWave > 1 && !isBossWave) {
				var waveAlert = new WaveAlert(currentWave, waveType.toUpperCase(), enemyCount);
				game.addChild(waveAlert);
			}
			// Spawn the appropriate number of enemies
			for (var i = 0; i < enemyCount; i++) {
				var enemy = new Enemy(waveType);
				// Add enemy to the appropriate layer based on type
				if (enemy.isFlying) {
					// Add flying enemy to the top layer
					enemyLayerTop.addChild(enemy);
					// If it's a flying enemy, add its shadow to the middle layer
					if (enemy.shadow) {
						enemyLayerMiddle.addChild(enemy.shadow);
					}
				} else {
					// Add normal/ground enemies to the bottom layer
					enemyLayerBottom.addChild(enemy);
				}
				// Scale difficulty with wave number but don't apply to boss
				// as bosses already have their health multiplier
				// Use exponential scaling for health
				var healthMultiplier = Math.pow(1.12, currentWave); // ~20% increase per wave
				enemy.maxHealth = Math.round(enemy.maxHealth * healthMultiplier);
				enemy.health = enemy.maxHealth;
				// Increment speed slightly with wave number
				//enemy.speed = enemy.speed + currentWave * 0.002;
				// All enemy types now spawn in the middle 6 tiles at the top spacing
				var gridWidth = 24;
				var midPoint = Math.floor(gridWidth / 2); // 12
				// Find a column that isn't occupied by another enemy that's not yet in view
				var availableColumns = [];
				for (var col = midPoint - 3; col < midPoint + 3; col++) {
					var columnOccupied = false;
					// Check if any enemy is already in this column but not yet in view
					for (var e = 0; e < enemies.length; e++) {
						if (enemies[e].cellX === col && enemies[e].currentCellY < 4) {
							columnOccupied = true;
							break;
						}
					}
					if (!columnOccupied) {
						availableColumns.push(col);
					}
				}
				// If all columns are occupied, use original random method
				var spawnX;
				if (availableColumns.length > 0) {
					// Choose a random unoccupied column
					spawnX = availableColumns[Math.floor(Math.random() * availableColumns.length)];
				} else {
					// Fallback to random if all columns are occupied
					spawnX = midPoint - 3 + Math.floor(Math.random() * 6); // x from 9 to 14
				}
				var spawnY = -1 - Math.random() * 5; // Random distance above the grid for spreading
				enemy.cellX = spawnX;
				enemy.cellY = 5; // Position after entry
				enemy.currentCellX = spawnX;
				enemy.currentCellY = spawnY;
				enemy.waveNumber = currentWave;
				enemies.push(enemy);
			}
		}
		var currentWaveEnemiesRemaining = false;
		for (var i = 0; i < enemies.length; i++) {
			if (enemies[i].waveNumber === currentWave) {
				currentWaveEnemiesRemaining = true;
				break;
			}
		}
		if (waveSpawned && !currentWaveEnemiesRemaining) {
			waveInProgress = false;
			waveSpawned = false;
		}
	}
	for (var a = enemies.length - 1; a >= 0; a--) {
		var enemy = enemies[a];
		if (enemy.health <= 0) {
			for (var i = 0; i < enemy.bulletsTargetingThis.length; i++) {
				var bullet = enemy.bulletsTargetingThis[i];
				bullet.targetEnemy = null;
			}
			// Play enemy death sound effect
			LK.getSound('enemy_death').play();
			// Play gold earning sound effect
			LK.getSound('gold_earn').play();
			// Create death animation before cleanup
			var deathAnimation = function deathAnimation() {
				// Stop any ongoing rotation tweens on the enemy
				if (enemy.children[0]) {
					tween.stop(enemy.children[0], {
						rotation: true
					});
				}
				// Stop any movement tweens
				tween.stop(enemy, {
					x: true,
					y: true
				});
				// Fade out and implode effect
				tween(enemy, {
					alpha: 0,
					scaleX: 0.3,
					scaleY: 0.3
				}, {
					duration: 300,
					easing: tween.easeIn,
					onFinish: function onFinish() {
						// Clean up shadow if it's a flying enemy
						if (enemy.isFlying && enemy.shadow) {
							enemyLayerMiddle.removeChild(enemy.shadow);
							enemy.shadow = null;
						}
						// Remove enemy from the appropriate layer
						if (enemy.isFlying) {
							enemyLayerTop.removeChild(enemy);
						} else {
							enemyLayerBottom.removeChild(enemy);
						}
					}
				});
				// Add a burst of particles effect with a quick expanding circle
				var burstEffect = new Container();
				game.addChild(burstEffect);
				burstEffect.x = enemy.x;
				burstEffect.y = enemy.y;
				var burstGraphics = burstEffect.attachAsset('rangeCircle', {
					anchorX: 0.5,
					anchorY: 0.5
				});
				burstGraphics.width = burstGraphics.height = CELL_SIZE * 0.3;
				burstGraphics.tint = 0xFF6B35;
				burstGraphics.alpha = 0.8;
				tween(burstEffect, {
					alpha: 0,
					scaleX: 2.5,
					scaleY: 2.5
				}, {
					duration: 400,
					easing: tween.easeOut,
					onFinish: function onFinish() {
						burstEffect.destroy();
					}
				});
			};
			// Execute death animation
			deathAnimation();
			// Boss enemies give more gold and score
			var goldEarned = enemy.isBoss ? Math.floor(50 + (enemy.waveNumber - 1) * 5) : Math.floor(1 + (enemy.waveNumber - 1) * 0.5);
			var goldIndicator = new GoldIndicator(goldEarned, enemy.x, enemy.y);
			game.addChild(goldIndicator);
			// Create gold collection particle effect
			var goldCollectionEffect = new Container();
			game.addChild(goldCollectionEffect);
			goldCollectionEffect.x = enemy.x;
			goldCollectionEffect.y = enemy.y;
			// Create sparkling particles that move toward gold counter
			for (var sparkle = 0; sparkle < 6; sparkle++) {
				var sparkleContainer = new Container();
				goldCollectionEffect.addChild(sparkleContainer);
				var sparkleGraphics = sparkleContainer.attachAsset('rangeCircle', {
					anchorX: 0.5,
					anchorY: 0.5
				});
				sparkleGraphics.width = sparkleGraphics.height = 6 + Math.random() * 8;
				sparkleGraphics.tint = 0xFFD700; // Golden color
				sparkleGraphics.alpha = 1;
				// Initial random spread
				var initialAngle = Math.random() * Math.PI * 2;
				var initialDistance = Math.random() * 25;
				sparkleContainer.x = Math.cos(initialAngle) * initialDistance;
				sparkleContainer.y = Math.sin(initialAngle) * initialDistance;
				// Calculate target position (gold counter position)
				var targetX = -spacing - goldCollectionEffect.x; // Relative to gold counter
				var targetY = topMargin - goldCollectionEffect.y;
				// First phase: sparkle outward briefly
				tween(sparkleContainer, {
					x: sparkleContainer.x * 1.5,
					y: sparkleContainer.y * 1.5,
					scaleX: 1.2,
					scaleY: 1.2
				}, {
					duration: 150,
					easing: tween.easeOut,
					onFinish: function onFinish() {
						// Second phase: fly toward gold counter
						tween(sparkleContainer, {
							x: targetX + (Math.random() - 0.5) * 40,
							y: targetY + (Math.random() - 0.5) * 20,
							alpha: 0,
							scaleX: 0.3,
							scaleY: 0.3
						}, {
							duration: 800 + Math.random() * 300,
							easing: tween.easeIn
						});
					}
				});
			}
			// Clean up gold collection effect after animation
			LK.setTimeout(function () {
				goldCollectionEffect.destroy();
			}, 1200);
			setGold(gold + goldEarned);
			// Give more score for defeating a boss
			var scoreValue = enemy.isBoss ? 100 : 5;
			score += scoreValue;
			// Add a notification for boss defeat
			if (enemy.isBoss) {
				var notification = game.addChild(new Notification("Boss defeated! +" + goldEarned + " gold!"));
				notification.x = 2048 / 2;
				notification.y = grid.height - 150;
			}
			updateUI();
			enemies.splice(a, 1);
			continue;
		}
		if (grid.updateEnemy(enemy)) {
			// Clean up shadow if it's a flying enemy
			if (enemy.isFlying && enemy.shadow) {
				enemyLayerMiddle.removeChild(enemy.shadow);
				enemy.shadow = null;
			}
			// Remove enemy from the appropriate layer
			if (enemy.isFlying) {
				enemyLayerTop.removeChild(enemy);
			} else {
				enemyLayerBottom.removeChild(enemy);
			}
			enemies.splice(a, 1);
			lives = Math.max(0, lives - 1);
			updateUI();
			if (lives <= 0) {
				LK.getSound('game_over').play();
				LK.showGameOver();
			}
		}
	}
	for (var i = bullets.length - 1; i >= 0; i--) {
		if (!bullets[i].parent) {
			if (bullets[i].targetEnemy) {
				var targetEnemy = bullets[i].targetEnemy;
				var bulletIndex = targetEnemy.bulletsTargetingThis.indexOf(bullets[i]);
				if (bulletIndex !== -1) {
					targetEnemy.bulletsTargetingThis.splice(bulletIndex, 1);
				}
			}
			bullets.splice(i, 1);
		}
	}
	if (towerPreview.visible) {
		towerPreview.checkPlacement();
	}
	// Update environmental elements
	for (var i = 0; i < clouds.length; i++) {
		clouds[i].update();
	}
	// Update wave preview panel
	if (waveIndicator.gameStarted && currentWave < totalWaves) {
		wavePreviewPanel.updatePreview(currentWave + 1);
	} else {
		wavePreviewPanel.visible = false;
	}
	if (currentWave >= totalWaves && enemies.length === 0 && !waveInProgress) {
		LK.showYouWin();
	}
}; /**** 
* Plugins
****/ 
var tween = LK.import("@upit/tween.v1");
/**** 
* Classes
****/ 
var Bullet = Container.expand(function (startX, startY, targetEnemy, damage, speed) {
	var self = Container.call(this);
	self.targetEnemy = targetEnemy;
	self.damage = damage || 10;
	self.speed = speed || 5;
	self.x = startX;
	self.y = startY;
	var bulletAsset = 'ice_bullet'; // Default bullet type
	if (self.type) {
		bulletAsset = self.type + '_bullet';
	}
	var bulletGraphics = self.attachAsset(bulletAsset, {
		anchorX: 0.5,
		anchorY: 0.5
	});
	self.update = function () {
		if (!self.targetEnemy || !self.targetEnemy.parent) {
			self.destroy();
			return;
		}
		var dx = self.targetEnemy.x - self.x;
		var dy = self.targetEnemy.y - self.y;
		var distance = Math.sqrt(dx * dx + dy * dy);
		if (distance < self.speed) {
			// Apply damage to target enemy
			self.targetEnemy.health -= self.damage;
			// Play hit sound effect
			LK.getSound('enemy_hit').play();
			// Create hit particle effect based on damage type
			var hitEffect = new Container();
			game.addChild(hitEffect);
			hitEffect.x = self.targetEnemy.x;
			hitEffect.y = self.targetEnemy.y;
			// Create multiple small particles for hit effect
			for (var particle = 0; particle < 4; particle++) {
				var particleContainer = new Container();
				hitEffect.addChild(particleContainer);
				var particleGraphics = particleContainer.attachAsset('rangeCircle', {
					anchorX: 0.5,
					anchorY: 0.5
				});
				particleGraphics.width = particleGraphics.height = 8 + Math.random() * 12;
				// Color particles based on bullet type
				switch (self.type) {
					case 'laser':
						particleGraphics.tint = 0xFF0080;
						break;
					case 'ice':
						particleGraphics.tint = 0x00FFFF;
						break;
					case 'missile':
						particleGraphics.tint = 0xFF4500;
						break;
					case 'lightning':
						particleGraphics.tint = 0xFFFF00;
						break;
					case 'cannon':
						particleGraphics.tint = 0x8B4513;
						break;
					case 'tesla':
						particleGraphics.tint = 0x9932CC;
						break;
					default:
						particleGraphics.tint = 0xFF6B35;
					// Orange sparks
				}
				// Random direction for each particle
				var angle = Math.random() * Math.PI * 2;
				var distance = 20 + Math.random() * 30;
				var targetX = Math.cos(angle) * distance;
				var targetY = Math.sin(angle) * distance;
				particleContainer.alpha = 1;
				// Animate particles flying outward
				tween(particleContainer, {
					x: targetX,
					y: targetY,
					alpha: 0,
					scaleX: 0.3,
					scaleY: 0.3
				}, {
					duration: 300 + Math.random() * 200,
					easing: tween.easeOut
				});
			}
			// Clean up hit effect after animation
			LK.setTimeout(function () {
				hitEffect.destroy();
			}, 600);
			if (self.targetEnemy.health <= 0) {
				self.targetEnemy.health = 0;
			} else {
				self.targetEnemy.healthBar.width = self.targetEnemy.health / self.targetEnemy.maxHealth * 70;
			}
			// Create visual hit effects based on bullet type
			if (self.type === 'laser') {
				// Laser: bright flash and scorched mark
				var laserFlash = new Container();
				game.addChild(laserFlash);
				laserFlash.x = self.targetEnemy.x;
				laserFlash.y = self.targetEnemy.y;
				var flashGraphics = laserFlash.attachAsset('rangeCircle', {
					anchorX: 0.5,
					anchorY: 0.5
				});
				flashGraphics.width = flashGraphics.height = CELL_SIZE * 0.8;
				flashGraphics.tint = 0xFF0080;
				flashGraphics.alpha = 1;
				tween(laserFlash, {
					alpha: 0,
					scaleX: 1.5,
					scaleY: 1.5
				}, {
					duration: 200,
					easing: tween.easeOut,
					onFinish: function onFinish() {
						laserFlash.destroy();
					}
				});
			} else if (self.type === 'ice') {
				// Ice: shattering effect and frost overlay
				var iceShatter = new Container();
				game.addChild(iceShatter);
				iceShatter.x = self.targetEnemy.x;
				iceShatter.y = self.targetEnemy.y;
				var shatterGraphics = iceShatter.attachAsset('rangeCircle', {
					anchorX: 0.5,
					anchorY: 0.5
				});
				shatterGraphics.width = shatterGraphics.height = CELL_SIZE * 0.6;
				shatterGraphics.tint = 0x00FFFF;
				shatterGraphics.alpha = 0.9;
				tween(iceShatter, {
					alpha: 0,
					scaleX: 2,
					scaleY: 2
				}, {
					duration: 300,
					easing: tween.easeOut,
					onFinish: function onFinish() {
						iceShatter.destroy();
					}
				});
				// Add frost overlay on enemy if not immune
				if (!self.targetEnemy.isImmune) {
					self.targetEnemy.frostOverlay = true;
				}
			} else if (self.type === 'missile') {
				// Missile: explosion with debris and knockback
				var explosion = new Container();
				game.addChild(explosion);
				explosion.x = self.targetEnemy.x;
				explosion.y = self.targetEnemy.y;
				var explosionGraphics = explosion.attachAsset('rangeCircle', {
					anchorX: 0.5,
					anchorY: 0.5
				});
				explosionGraphics.width = explosionGraphics.height = CELL_SIZE * 1.2;
				explosionGraphics.tint = 0xFF4500;
				explosionGraphics.alpha = 1;
				tween(explosion, {
					alpha: 0,
					scaleX: 2.5,
					scaleY: 2.5
				}, {
					duration: 400,
					easing: tween.easeOut,
					onFinish: function onFinish() {
						explosion.destroy();
					}
				});
				// Knockback effect on enemy
				if (self.targetEnemy.children[0]) {
					var originalX = self.targetEnemy.x;
					var originalY = self.targetEnemy.y;
					tween(self.targetEnemy, {
						x: originalX + (Math.random() - 0.5) * 20,
						y: originalY + (Math.random() - 0.5) * 20
					}, {
						duration: 150,
						easing: tween.easeOut,
						onFinish: function onFinish() {
							tween(self.targetEnemy, {
								x: originalX,
								y: originalY
							}, {
								duration: 100,
								easing: tween.easeIn
							});
						}
					});
				}
			} else if (self.type === 'lightning') {
				// Lightning: crackling energy discharge
				var lightningStrike = new Container();
				game.addChild(lightningStrike);
				lightningStrike.x = self.targetEnemy.x;
				lightningStrike.y = self.targetEnemy.y;
				var strikeGraphics = lightningStrike.attachAsset('rangeCircle', {
					anchorX: 0.5,
					anchorY: 0.5
				});
				strikeGraphics.width = strikeGraphics.height = CELL_SIZE * 1;
				strikeGraphics.tint = 0xFFFF00;
				strikeGraphics.alpha = 1;
				// Create flickering effect
				tween(lightningStrike, {
					alpha: 0.3
				}, {
					duration: 50,
					easing: tween.linear,
					onFinish: function onFinish() {
						tween(lightningStrike, {
							alpha: 1
						}, {
							duration: 50,
							easing: tween.linear,
							onFinish: function onFinish() {
								tween(lightningStrike, {
									alpha: 0,
									scaleX: 1.8,
									scaleY: 1.8
								}, {
									duration: 200,
									easing: tween.easeOut,
									onFinish: function onFinish() {
										lightningStrike.destroy();
									}
								});
							}
						});
					}
				});
			} else if (self.type === 'cannon') {
				// Cannon: impact crater and enemy shake
				var crater = new Container();
				game.addChild(crater);
				crater.x = self.targetEnemy.x;
				crater.y = self.targetEnemy.y + 20; // Slightly below enemy
				var craterGraphics = crater.attachAsset('rangeCircle', {
					anchorX: 0.5,
					anchorY: 0.5
				});
				craterGraphics.width = craterGraphics.height = CELL_SIZE * 0.7;
				craterGraphics.tint = 0x8B4513;
				craterGraphics.alpha = 0.8;
				tween(crater, {
					alpha: 0,
					scaleX: 1.5,
					scaleY: 1.5
				}, {
					duration: 500,
					easing: tween.easeOut,
					onFinish: function onFinish() {
						crater.destroy();
					}
				});
				// Enemy shake effect
				if (self.targetEnemy.children[0]) {
					var shakeCount = 0;
					var _shakeTimer = function shakeTimer() {
						if (shakeCount < 6) {
							self.targetEnemy.x += (Math.random() - 0.5) * 8;
							self.targetEnemy.y += (Math.random() - 0.5) * 8;
							shakeCount++;
							LK.setTimeout(_shakeTimer, 30);
						}
					};
					_shakeTimer();
				}
			} else if (self.type === 'tesla') {
				// Tesla: electrical arc connecting tower to enemy
				var electricArc = new Container();
				game.addChild(electricArc);
				electricArc.x = self.targetEnemy.x;
				electricArc.y = self.targetEnemy.y;
				var arcGraphics = electricArc.attachAsset('rangeCircle', {
					anchorX: 0.5,
					anchorY: 0.5
				});
				arcGraphics.width = arcGraphics.height = CELL_SIZE * 0.9;
				arcGraphics.tint = 0x9932CC;
				arcGraphics.alpha = 1;
				// Create pulsing electric effect
				tween(electricArc, {
					scaleX: 1.3,
					scaleY: 1.3,
					alpha: 0.7
				}, {
					duration: 100,
					easing: tween.easeOut,
					onFinish: function onFinish() {
						tween(electricArc, {
							scaleX: 1,
							scaleY: 1,
							alpha: 1
						}, {
							duration: 100,
							easing: tween.easeIn,
							onFinish: function onFinish() {
								tween(electricArc, {
									alpha: 0,
									scaleX: 1.5,
									scaleY: 1.5
								}, {
									duration: 250,
									easing: tween.easeOut,
									onFinish: function onFinish() {
										electricArc.destroy();
									}
								});
							}
						});
					}
				});
			}
			// Apply special effects based on bullet type
			if (self.type === 'splash') {
				// Create visual splash effect
				var splashEffect = new EffectIndicator(self.targetEnemy.x, self.targetEnemy.y, 'splash');
				game.addChild(splashEffect);
				// Splash damage to nearby enemies
				var splashRadius = CELL_SIZE * 1.5;
				for (var i = 0; i < enemies.length; i++) {
					var otherEnemy = enemies[i];
					if (otherEnemy !== self.targetEnemy) {
						var splashDx = otherEnemy.x - self.targetEnemy.x;
						var splashDy = otherEnemy.y - self.targetEnemy.y;
						var splashDistance = Math.sqrt(splashDx * splashDx + splashDy * splashDy);
						if (splashDistance <= splashRadius) {
							// Apply splash damage (50% of original damage)
							otherEnemy.health -= self.damage * 0.5;
							if (otherEnemy.health <= 0) {
								otherEnemy.health = 0;
							} else {
								otherEnemy.healthBar.width = otherEnemy.health / otherEnemy.maxHealth * 70;
							}
						}
					}
				}
			} else if (self.type === 'slow') {
				// Prevent slow effect on immune enemies
				if (!self.targetEnemy.isImmune) {
					// Create visual slow effect
					var slowEffect = new EffectIndicator(self.targetEnemy.x, self.targetEnemy.y, 'slow');
					game.addChild(slowEffect);
					// Apply slow effect
					// Make slow percentage scale with tower level (default 50%, up to 80% at max level)
					var slowPct = 0.5;
					if (self.sourceTowerLevel !== undefined) {
						// Scale: 50% at level 1, 60% at 2, 65% at 3, 70% at 4, 75% at 5, 80% at 6
						var slowLevels = [0.5, 0.6, 0.65, 0.7, 0.75, 0.8];
						var idx = Math.max(0, Math.min(5, self.sourceTowerLevel - 1));
						slowPct = slowLevels[idx];
					}
					if (!self.targetEnemy.slowed) {
						self.targetEnemy.originalSpeed = self.targetEnemy.speed;
						self.targetEnemy.speed *= 1 - slowPct; // Slow by X%
						self.targetEnemy.slowed = true;
						self.targetEnemy.slowDuration = 180; // 3 seconds at 60 FPS
					} else {
						self.targetEnemy.slowDuration = 180; // Reset duration
					}
				}
			} else if (self.type === 'poison') {
				// Prevent poison effect on immune enemies
				if (!self.targetEnemy.isImmune) {
					// Create visual poison effect
					var poisonEffect = new EffectIndicator(self.targetEnemy.x, self.targetEnemy.y, 'poison');
					game.addChild(poisonEffect);
					// Apply poison effect
					self.targetEnemy.poisoned = true;
					self.targetEnemy.poisonDamage = self.damage * 0.2; // 20% of original damage per tick
					self.targetEnemy.poisonDuration = 300; // 5 seconds at 60 FPS
				}
			} else if (self.type === 'sniper') {
				// Create visual critical hit effect for sniper
				var sniperEffect = new EffectIndicator(self.targetEnemy.x, self.targetEnemy.y, 'sniper');
				game.addChild(sniperEffect);
			}
			self.destroy();
		} else {
			var angle = Math.atan2(dy, dx);
			self.x += Math.cos(angle) * self.speed;
			self.y += Math.sin(angle) * self.speed;
		}
	};
	return self;
});
var Cloud = Container.expand(function (x, y) {
	var self = Container.call(this);
	self.x = x;
	self.y = y;
	var cloudGraphics = self.attachAsset('cloud', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	cloudGraphics.alpha = 0.6 + Math.random() * 0.3; // Semi-transparent
	cloudGraphics.scaleX = 0.8 + Math.random() * 0.6; // Random size
	cloudGraphics.scaleY = 0.8 + Math.random() * 0.6;
	cloudGraphics.tint = 0xF0F8FF; // Alice blue
	self.moveSpeed = 0.2 + Math.random() * 0.3; // Random speed
	self.startY = y;
	self.amplitude = 20 + Math.random() * 30; // Vertical drift amplitude
	self.update = function () {
		// Move cloud slowly to the right
		self.x += self.moveSpeed;
		// Add gentle vertical drift
		self.y = self.startY + Math.sin(LK.ticks * 0.01 + self.x * 0.001) * self.amplitude;
		// Reset position when off-screen
		if (self.x > 2048 + cloudGraphics.width) {
			self.x = -cloudGraphics.width;
			self.startY = 100 + Math.random() * 300; // New random height
		}
	};
	return self;
});
var DebugCell = Container.expand(function () {
	var self = Container.call(this);
	var cellGraphics = self.attachAsset('cell', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	cellGraphics.tint = Math.random() * 0xffffff;
	var debugArrows = [];
	var numberLabel = new Text2('0', {
		size: 30,
		fill: 0xFFFFFF,
		weight: 800
	});
	numberLabel.anchor.set(.5, .5);
	self.addChild(numberLabel);
	self.update = function () {
		self.updateFootprint();
	};
	self.down = function () {
		return;
		if (self.cell.type == 0 || self.cell.type == 1) {
			self.cell.type = self.cell.type == 1 ? 0 : 1;
			if (grid.pathFind()) {
				self.cell.type = self.cell.type == 1 ? 0 : 1;
				grid.pathFind();
				var notification = game.addChild(new Notification("Path is blocked!"));
				notification.x = 2048 / 2;
				notification.y = grid.height - 50;
			}
			grid.renderDebug();
		}
	};
	self.removeArrows = function () {
		while (debugArrows.length) {
			self.removeChild(debugArrows.pop());
		}
	};
	// Water animation properties
	self.waterAnimationActive = false;
	self.footprintTimer = 0;
	self.originalTint = cellGraphics.tint;
	self.originalAlpha = cellGraphics.alpha;
	// Start water ripple animation for ocean cells
	self.startWaterAnimation = function () {
		if (self.waterAnimationActive) return;
		self.waterAnimationActive = true;
		var _animateWaterRipple = function animateWaterRipple() {
			if (!self.waterAnimationActive) return;
			// Create subtle wave effect by modulating alpha and scale
			tween(cellGraphics, {
				alpha: cellGraphics.alpha * 0.9,
				scaleX: 1.02,
				scaleY: 1.02
			}, {
				duration: 1500 + Math.random() * 1000,
				easing: tween.easeInOut,
				onFinish: function onFinish() {
					if (!self.waterAnimationActive) return;
					tween(cellGraphics, {
						alpha: self.originalAlpha,
						scaleX: 1.0,
						scaleY: 1.0
					}, {
						duration: 1500 + Math.random() * 1000,
						easing: tween.easeInOut,
						onFinish: _animateWaterRipple
					});
				}
			});
		};
		_animateWaterRipple();
	};
	// Stop water animation
	self.stopWaterAnimation = function () {
		self.waterAnimationActive = false;
		tween.stop(cellGraphics, {
			alpha: true,
			scaleX: true,
			scaleY: true
		});
		// Reset to original state
		cellGraphics.alpha = self.originalAlpha;
		cellGraphics.scaleX = 1.0;
		cellGraphics.scaleY = 1.0;
	};
	// Add enemy footprint effect
	self.addFootprint = function () {
		if (self.cell.type !== 0) return; // Only on path cells
		// Darken the cell temporarily to show footprint
		var originalTint = cellGraphics.tint;
		cellGraphics.tint = 0xE6D4A2; // Slightly darker sand
		// Reset footprint timer
		self.footprintTimer = 180; // 3 seconds at 60 FPS
	};
	// Update method for handling footprint fade
	self.updateFootprint = function () {
		if (self.footprintTimer > 0) {
			self.footprintTimer--;
			if (self.footprintTimer <= 0) {
				// Fade back to original color
				tween(cellGraphics, {
					tint: self.originalTint
				}, {
					duration: 500,
					easing: tween.easeOut
				});
			}
		}
	};
	self.render = function (data) {
		// Show beach-themed grid visuals
		cellGraphics.visible = true;
		numberLabel.visible = false;
		self.removeArrows();
		// Color cells based on type for beach theme
		if (self.cell.type === 0) {
			// Walkable path - beach sand color
			cellGraphics.tint = 0xF4E4BC;
			cellGraphics.alpha = 0.8;
		} else if (self.cell.type === 1) {
			// Wall - darker sand/rock color
			cellGraphics.tint = 0xD2B48C;
			cellGraphics.alpha = 0.9;
		} else if (self.cell.type === 2) {
			// Spawn - ocean water color
			cellGraphics.tint = 0x4682B4;
			cellGraphics.alpha = 0.8;
			// Start water ripple animation for spawn cells
			self.startWaterAnimation();
		} else if (self.cell.type === 3) {
			// Goal - tropical water color
			cellGraphics.tint = 0x00CED1;
			cellGraphics.alpha = 0.8;
			// Start water ripple animation for goal cells
			self.startWaterAnimation();
		}
	};
	// Override destroy to clean up animations
	var originalDestroy = self.destroy;
	self.destroy = function () {
		self.stopWaterAnimation();
		if (originalDestroy) {
			originalDestroy.call(self);
		}
	};
	return self;
});
// This update method was incorrectly placed here and should be removed
var EffectIndicator = Container.expand(function (x, y, type) {
	var self = Container.call(this);
	self.x = x;
	self.y = y;
	var effectGraphics = self.attachAsset('rangeCircle', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	effectGraphics.blendMode = 1;
	switch (type) {
		case 'splash':
			effectGraphics.tint = 0x39FF14;
			effectGraphics.width = effectGraphics.height = CELL_SIZE * 1.8;
			break;
		case 'slow':
			effectGraphics.tint = 0xC44FFF;
			effectGraphics.width = effectGraphics.height = CELL_SIZE * 1.2;
			break;
		case 'poison':
			effectGraphics.tint = 0x00FFA1;
			effectGraphics.width = effectGraphics.height = CELL_SIZE * 1.2;
			break;
		case 'sniper':
			effectGraphics.tint = 0xFF4500;
			effectGraphics.width = effectGraphics.height = CELL_SIZE * 0.8;
			break;
	}
	effectGraphics.alpha = 0.9;
	self.alpha = 0;
	// Animate the effect
	tween(self, {
		alpha: 0.8,
		scaleX: 1.5,
		scaleY: 1.5
	}, {
		duration: 200,
		easing: tween.easeOut,
		onFinish: function onFinish() {
			tween(self, {
				alpha: 0,
				scaleX: 2,
				scaleY: 2
			}, {
				duration: 300,
				easing: tween.easeIn,
				onFinish: function onFinish() {
					self.destroy();
				}
			});
		}
	});
	return self;
});
// Base enemy class for common functionality
var Enemy = Container.expand(function (type) {
	var self = Container.call(this);
	self.type = type || 'normal';
	self.speed = .01;
	self.cellX = 0;
	self.cellY = 0;
	self.currentCellX = 0;
	self.currentCellY = 0;
	self.currentTarget = undefined;
	self.maxHealth = 100;
	self.health = self.maxHealth;
	self.bulletsTargetingThis = [];
	self.waveNumber = currentWave;
	self.isFlying = false;
	self.isImmune = false;
	self.isBoss = false;
	// Check if this is a boss wave
	// Check if this is a boss wave
	// Apply different stats based on enemy type
	switch (self.type) {
		case 'fast':
			self.speed *= 2; // Twice as fast
			self.maxHealth = 100;
			break;
		case 'immune':
			self.isImmune = true;
			self.maxHealth = 80;
			break;
		case 'flying':
			self.isFlying = true;
			self.maxHealth = 80;
			break;
		case 'swarm':
			self.maxHealth = 50; // Weaker enemies
			break;
		case 'normal':
		default:
			// Normal enemy uses default values
			break;
	}
	if (currentWave % 10 === 0 && currentWave > 0 && type !== 'swarm') {
		self.isBoss = true;
		// Boss enemies have 20x health and are larger
		self.maxHealth *= 20;
		// Slower speed for bosses
		self.speed = self.speed * 0.7;
	}
	self.health = self.maxHealth;
	// Get appropriate asset for this enemy type
	var assetId = 'enemy';
	if (self.type !== 'normal') {
		assetId = 'enemy_' + self.type;
	}
	var enemyGraphics = self.attachAsset(assetId, {
		anchorX: 0.5,
		anchorY: 0.5
	});
	// Scale up boss enemies
	if (self.isBoss) {
		enemyGraphics.scaleX = 1.8;
		enemyGraphics.scaleY = 1.8;
	}
	// Fall back to regular enemy asset if specific type asset not found
	// Apply tint to differentiate enemy types
	/*switch (self.type) {
		case 'fast':
			enemyGraphics.tint = 0x00AAFF; // Blue for fast enemies
			break;
		case 'immune':
			enemyGraphics.tint = 0xAA0000; // Red for immune enemies
			break;
		case 'flying':
			enemyGraphics.tint = 0xFFFF00; // Yellow for flying enemies
			break;
		case 'swarm':
			enemyGraphics.tint = 0xFF00FF; // Pink for swarm enemies
			break;
	}*/
	// Create shadow for flying enemies
	if (self.isFlying) {
		// Create a shadow container that will be added to the shadow layer
		self.shadow = new Container();
		// Clone the enemy graphics for the shadow
		var shadowGraphics = self.shadow.attachAsset(assetId || 'enemy', {
			anchorX: 0.5,
			anchorY: 0.5
		});
		// Apply shadow effect
		shadowGraphics.tint = 0x000000; // Black shadow
		shadowGraphics.alpha = 0.4; // Semi-transparent
		// If this is a boss, scale up the shadow to match
		if (self.isBoss) {
			shadowGraphics.scaleX = 1.8;
			shadowGraphics.scaleY = 1.8;
		}
		// Position shadow slightly offset
		self.shadow.x = 20; // Offset right
		self.shadow.y = 20; // Offset down
		// Ensure shadow has the same rotation as the enemy
		shadowGraphics.rotation = enemyGraphics.rotation;
	}
	var healthBarOutline = self.attachAsset('healthBarOutline', {
		anchorX: 0,
		anchorY: 0.5
	});
	var healthBarBG = self.attachAsset('healthBar', {
		anchorX: 0,
		anchorY: 0.5
	});
	var healthBar = self.attachAsset('healthBar', {
		anchorX: 0,
		anchorY: 0.5
	});
	healthBarBG.y = healthBarOutline.y = healthBar.y = -enemyGraphics.height / 2 - 10;
	healthBarOutline.x = -healthBarOutline.width / 2;
	healthBarBG.x = healthBar.x = -healthBar.width / 2 - .5;
	healthBar.tint = 0x00ff00;
	healthBarBG.tint = 0xff0000;
	self.healthBar = healthBar;
	self.update = function () {
		if (self.health <= 0) {
			self.health = 0;
			self.healthBar.width = 0;
		}
		// Handle slow effect
		if (self.isImmune) {
			// Immune enemies cannot be slowed or poisoned, clear any such effects
			self.slowed = false;
			self.slowEffect = false;
			self.poisoned = false;
			self.poisonEffect = false;
			// Reset speed to original if needed
			if (self.originalSpeed !== undefined) {
				self.speed = self.originalSpeed;
			}
		} else {
			// Handle slow effect
			if (self.slowed) {
				// Visual indication of slowed status
				if (!self.slowEffect) {
					self.slowEffect = true;
				}
				self.slowDuration--;
				if (self.slowDuration <= 0) {
					self.speed = self.originalSpeed;
					self.slowed = false;
					self.slowEffect = false;
					// Only reset tint if not poisoned
					if (!self.poisoned) {
						enemyGraphics.tint = 0xFFFFFF; // Reset tint
					}
				}
			}
			// Handle poison effect
			if (self.poisoned) {
				// Visual indication of poisoned status
				if (!self.poisonEffect) {
					self.poisonEffect = true;
				}
				// Apply poison damage every 30 frames (twice per second)
				if (LK.ticks % 30 === 0) {
					self.health -= self.poisonDamage;
					if (self.health <= 0) {
						self.health = 0;
					}
					self.healthBar.width = self.health / self.maxHealth * 70;
				}
				self.poisonDuration--;
				if (self.poisonDuration <= 0) {
					self.poisoned = false;
					self.poisonEffect = false;
					// Only reset tint if not slowed
					if (!self.slowed) {
						enemyGraphics.tint = 0xFFFFFF; // Reset tint
					}
				}
			}
		}
		// Set tint based on effect status
		if (self.isImmune) {
			enemyGraphics.tint = 0xFFFFFF;
		} else if (self.poisoned && self.slowed) {
			// Combine poison (0x00FFAA) and slow (0x9900FF) colors
			// Simple average: R: (0+153)/2=76, G: (255+0)/2=127, B: (170+255)/2=212
			enemyGraphics.tint = 0x4C7FD4;
		} else if (self.poisoned) {
			enemyGraphics.tint = 0x00FFAA;
		} else if (self.slowed) {
			enemyGraphics.tint = 0x9900FF;
		} else {
			enemyGraphics.tint = 0xFFFFFF;
		}
		if (self.currentTarget) {
			var ox = self.currentTarget.x - self.currentCellX;
			var oy = self.currentTarget.y - self.currentCellY;
			if (ox !== 0 || oy !== 0) {
				var angle = Math.atan2(oy, ox);
				if (enemyGraphics.targetRotation === undefined) {
					enemyGraphics.targetRotation = angle;
					enemyGraphics.rotation = angle;
				} else {
					if (Math.abs(angle - enemyGraphics.targetRotation) > 0.05) {
						tween.stop(enemyGraphics, {
							rotation: true
						});
						// Calculate the shortest angle to rotate
						var currentRotation = enemyGraphics.rotation;
						var angleDiff = angle - currentRotation;
						// Normalize angle difference to -PI to PI range for shortest path
						while (angleDiff > Math.PI) {
							angleDiff -= Math.PI * 2;
						}
						while (angleDiff < -Math.PI) {
							angleDiff += Math.PI * 2;
						}
						enemyGraphics.targetRotation = angle;
						tween(enemyGraphics, {
							rotation: currentRotation + angleDiff
						}, {
							duration: 250,
							easing: tween.easeOut
						});
					}
				}
			}
		}
		healthBarOutline.y = healthBarBG.y = healthBar.y = -enemyGraphics.height / 2 - 10;
	};
	return self;
});
var EnvironmentalElement = Container.expand(function (type, x, y) {
	var self = Container.call(this);
	self.type = type;
	self.x = x;
	self.y = y;
	var elementGraphics;
	switch (type) {
		case 'palm_tree':
			elementGraphics = self.attachAsset('palm_tree', {
				anchorX: 0.5,
				anchorY: 1.0
			});
			elementGraphics.tint = 0x228B22; // Forest green
			// Add swaying animation
			self.startSwayAnimation = function () {
				var _swayTween2 = function swayTween() {
					tween(elementGraphics, {
						rotation: (Math.random() - 0.5) * 0.15
					}, {
						duration: 3000 + Math.random() * 2000,
						easing: tween.easeInOut,
						onFinish: _swayTween2
					});
				};
				_swayTween2();
			};
			self.startSwayAnimation();
			break;
		case 'rock':
			elementGraphics = self.attachAsset('rock', {
				anchorX: 0.5,
				anchorY: 0.5
			});
			elementGraphics.tint = 0x696969; // Dim gray
			elementGraphics.scaleX = 0.8 + Math.random() * 0.4; // Random size variation
			elementGraphics.scaleY = 0.8 + Math.random() * 0.4;
			break;
		case 'seashell':
			elementGraphics = self.attachAsset('seashell', {
				anchorX: 0.5,
				anchorY: 0.5
			});
			elementGraphics.tint = 0xFFF8DC; // Cornsilk
			elementGraphics.rotation = Math.random() * Math.PI * 2; // Random rotation
			elementGraphics.scaleX = 0.6 + Math.random() * 0.4;
			elementGraphics.scaleY = 0.6 + Math.random() * 0.4;
			break;
	}
	return self;
});
var GoldIndicator = Container.expand(function (value, x, y) {
	var self = Container.call(this);
	var goldText = new Text2("+" + value, {
		size: 45,
		fill: 0xFFD700,
		weight: 800
	});
	goldText.anchor.set(0.5, 0.5);
	self.addChild(goldText);
	self.x = x;
	self.y = y;
	self.alpha = 0;
	self.scaleX = 0.5;
	self.scaleY = 0.5;
	tween(self, {
		alpha: 1,
		scaleX: 1.2,
		scaleY: 1.2,
		y: y - 40
	}, {
		duration: 50,
		easing: tween.easeOut,
		onFinish: function onFinish() {
			tween(self, {
				alpha: 0,
				scaleX: 1.5,
				scaleY: 1.5,
				y: y - 80
			}, {
				duration: 600,
				easing: tween.easeIn,
				delay: 800,
				onFinish: function onFinish() {
					self.destroy();
				}
			});
		}
	});
	return self;
});
var Grid = Container.expand(function (gridWidth, gridHeight) {
	var self = Container.call(this);
	self.cells = [];
	self.spawns = [];
	self.goals = [];
	for (var i = 0; i < gridWidth; i++) {
		self.cells[i] = [];
		for (var j = 0; j < gridHeight; j++) {
			self.cells[i][j] = {
				score: 0,
				pathId: 0,
				towersInRange: []
			};
		}
	}
	/*
	Cell Types
	0: Transparent floor (walkable path)
	1: Wall (blocks movement)
	2: Spawn
	3: Goal
	*/
	// First, fill everything with walls
	for (var i = 0; i < gridWidth; i++) {
		for (var j = 0; j < gridHeight; j++) {
			var cell = self.cells[i][j];
			cell.type = 1; // Start with walls everywhere
			cell.x = i;
			cell.y = j;
		}
	}
	// Create a maze-like street pattern with defined corridors
	// Main vertical corridor down the center
	var centerX = Math.floor(gridWidth / 2);
	for (var j = 5; j < gridHeight - 4; j++) {
		self.cells[centerX][j].type = 0; // Clear path
		self.cells[centerX - 1][j].type = 0; // Make corridor 2 tiles wide
	}
	// Create horizontal connecting streets at regular intervals
	var streetInterval = 6;
	for (var streetY = 8; streetY < gridHeight - 8; streetY += streetInterval) {
		// Left side street
		for (var i = 2; i < centerX - 1; i++) {
			self.cells[i][streetY].type = 0;
			self.cells[i][streetY + 1].type = 0; // Make streets 2 tiles wide
		}
		// Right side street
		for (var i = centerX + 2; i < gridWidth - 2; i++) {
			self.cells[i][streetY].type = 0;
			self.cells[i][streetY + 1].type = 0; // Make streets 2 tiles wide
		}
	}
	// Create some vertical side streets for more complexity
	for (var sideStreetX = 6; sideStreetX < gridWidth - 6; sideStreetX += 8) {
		if (sideStreetX === centerX || sideStreetX === centerX - 1) continue; // Skip center
		for (var j = 8; j < gridHeight - 8; j++) {
			if (j % streetInterval < 2) continue; // Don't overwrite horizontal streets
			self.cells[sideStreetX][j].type = 0;
		}
	}
	// Create spawn area (entrance to the maze)
	for (var i = centerX - 3; i <= centerX + 3; i++) {
		for (var j = 0; j <= 4; j++) {
			if (j === 0) {
				self.cells[i][j].type = 2; // Spawn points
				self.spawns.push(self.cells[i][j]);
			} else {
				self.cells[i][j].type = 0; // Clear entrance path
			}
		}
	}
	// Create goal area (exit from the maze)
	for (var i = centerX - 3; i <= centerX + 3; i++) {
		for (var j = gridHeight - 4; j < gridHeight; j++) {
			if (j === gridHeight - 1) {
				self.cells[i][j].type = 3; // Goal points
				self.goals.push(self.cells[i][j]);
			} else {
				self.cells[i][j].type = 0; // Clear exit path
			}
		}
	}
	// Set up cell relationships after creating the maze
	for (var i = 0; i < gridWidth; i++) {
		for (var j = 0; j < gridHeight; j++) {
			var cell = self.cells[i][j];
			cell.upLeft = self.cells[i - 1] && self.cells[i - 1][j - 1];
			cell.up = self.cells[i - 1] && self.cells[i - 1][j];
			cell.upRight = self.cells[i - 1] && self.cells[i - 1][j + 1];
			cell.left = self.cells[i][j - 1];
			cell.right = self.cells[i][j + 1];
			cell.downLeft = self.cells[i + 1] && self.cells[i + 1][j - 1];
			cell.down = self.cells[i + 1] && self.cells[i + 1][j];
			cell.downRight = self.cells[i + 1] && self.cells[i + 1][j + 1];
			cell.neighbors = [cell.upLeft, cell.up, cell.upRight, cell.right, cell.downRight, cell.down, cell.downLeft, cell.left];
			cell.targets = [];
			if (j > 3 && j <= gridHeight - 4) {
				var debugCell = new DebugCell();
				self.addChild(debugCell);
				debugCell.cell = cell;
				debugCell.x = i * CELL_SIZE;
				debugCell.y = j * CELL_SIZE;
				cell.debugCell = debugCell;
			}
		}
	}
	self.getCell = function (x, y) {
		return self.cells[x] && self.cells[x][y];
	};
	self.pathFind = function () {
		var before = new Date().getTime();
		var toProcess = self.goals.concat([]);
		maxScore = 0;
		pathId += 1;
		for (var a = 0; a < toProcess.length; a++) {
			toProcess[a].pathId = pathId;
		}
		function processNode(node, targetValue, targetNode) {
			if (node && node.type != 1) {
				if (node.pathId < pathId || targetValue < node.score) {
					node.targets = [targetNode];
				} else if (node.pathId == pathId && targetValue == node.score) {
					node.targets.push(targetNode);
				}
				if (node.pathId < pathId || targetValue < node.score) {
					node.score = targetValue;
					if (node.pathId != pathId) {
						toProcess.push(node);
					}
					node.pathId = pathId;
					if (targetValue > maxScore) {
						maxScore = targetValue;
					}
				}
			}
		}
		while (toProcess.length) {
			var nodes = toProcess;
			toProcess = [];
			for (var a = 0; a < nodes.length; a++) {
				var node = nodes[a];
				var targetScore = node.score + 14142;
				if (node.up && node.left && node.up.type != 1 && node.left.type != 1) {
					processNode(node.upLeft, targetScore, node);
				}
				if (node.up && node.right && node.up.type != 1 && node.right.type != 1) {
					processNode(node.upRight, targetScore, node);
				}
				if (node.down && node.right && node.down.type != 1 && node.right.type != 1) {
					processNode(node.downRight, targetScore, node);
				}
				if (node.down && node.left && node.down.type != 1 && node.left.type != 1) {
					processNode(node.downLeft, targetScore, node);
				}
				targetScore = node.score + 10000;
				processNode(node.up, targetScore, node);
				processNode(node.right, targetScore, node);
				processNode(node.down, targetScore, node);
				processNode(node.left, targetScore, node);
			}
		}
		for (var a = 0; a < self.spawns.length; a++) {
			if (self.spawns[a].pathId != pathId) {
				console.warn("Spawn blocked");
				return true;
			}
		}
		for (var a = 0; a < enemies.length; a++) {
			var enemy = enemies[a];
			// Skip enemies that haven't entered the viewable area yet
			if (enemy.currentCellY < 4) {
				continue;
			}
			// Skip flying enemies from path check as they can fly over obstacles
			if (enemy.isFlying) {
				continue;
			}
			var target = self.getCell(enemy.cellX, enemy.cellY);
			if (enemy.currentTarget) {
				if (enemy.currentTarget.pathId != pathId) {
					if (!target || target.pathId != pathId) {
						console.warn("Enemy blocked 1 ");
						return true;
					}
				}
			} else if (!target || target.pathId != pathId) {
				console.warn("Enemy blocked 2");
				return true;
			}
		}
		console.log("Speed", new Date().getTime() - before);
	};
	self.renderDebug = function () {
		for (var i = 0; i < gridWidth; i++) {
			for (var j = 0; j < gridHeight; j++) {
				var debugCell = self.cells[i][j].debugCell;
				if (debugCell) {
					debugCell.render(self.cells[i][j]);
				}
			}
		}
	};
	self.updateEnemy = function (enemy) {
		var cell = grid.getCell(enemy.cellX, enemy.cellY);
		if (cell.type == 3) {
			return true;
		}
		if (enemy.isFlying && enemy.shadow) {
			enemy.shadow.x = enemy.x + 20; // Match enemy x-position + offset
			enemy.shadow.y = enemy.y + 20; // Match enemy y-position + offset
			// Match shadow rotation with enemy rotation
			if (enemy.children[0] && enemy.shadow.children[0]) {
				enemy.shadow.children[0].rotation = enemy.children[0].rotation;
			}
		}
		// Check if the enemy has reached the entry area (y position is at least 5)
		var hasReachedEntryArea = enemy.currentCellY >= 4;
		// If enemy hasn't reached the entry area yet, just move down vertically
		if (!hasReachedEntryArea) {
			// Move directly downward
			enemy.currentCellY += enemy.speed;
			// Rotate enemy graphic to face downward (PI/2 radians = 90 degrees)
			var angle = Math.PI / 2;
			if (enemy.children[0] && enemy.children[0].targetRotation === undefined) {
				enemy.children[0].targetRotation = angle;
				enemy.children[0].rotation = angle;
			} else if (enemy.children[0]) {
				if (Math.abs(angle - enemy.children[0].targetRotation) > 0.05) {
					tween.stop(enemy.children[0], {
						rotation: true
					});
					// Calculate the shortest angle to rotate
					var currentRotation = enemy.children[0].rotation;
					var angleDiff = angle - currentRotation;
					// Normalize angle difference to -PI to PI range for shortest path
					while (angleDiff > Math.PI) {
						angleDiff -= Math.PI * 2;
					}
					while (angleDiff < -Math.PI) {
						angleDiff += Math.PI * 2;
					}
					// Set target rotation and animate to it
					enemy.children[0].targetRotation = angle;
					tween(enemy.children[0], {
						rotation: currentRotation + angleDiff
					}, {
						duration: 250,
						easing: tween.easeOut
					});
				}
			}
			// Update enemy's position
			enemy.x = grid.x + enemy.currentCellX * CELL_SIZE;
			enemy.y = grid.y + enemy.currentCellY * CELL_SIZE;
			// Add footprint effect on path cells for non-flying enemies
			if (!enemy.isFlying) {
				var currentCell = self.getCell(Math.floor(enemy.currentCellX), Math.floor(enemy.currentCellY));
				if (currentCell && currentCell.type === 0 && currentCell.debugCell) {
					// Only add footprint if enemy has moved to a new cell
					if (!enemy.lastFootprintX || !enemy.lastFootprintY || Math.floor(enemy.currentCellX) !== enemy.lastFootprintX || Math.floor(enemy.currentCellY) !== enemy.lastFootprintY) {
						currentCell.debugCell.addFootprint();
						enemy.lastFootprintX = Math.floor(enemy.currentCellX);
						enemy.lastFootprintY = Math.floor(enemy.currentCellY);
					}
				}
			}
			// If enemy has now reached the entry area, update cell coordinates
			if (enemy.currentCellY >= 4) {
				enemy.cellX = Math.round(enemy.currentCellX);
				enemy.cellY = Math.round(enemy.currentCellY);
			}
			return false;
		}
		// After reaching entry area, handle flying enemies differently
		if (enemy.isFlying) {
			// Flying enemies head straight to the closest goal
			if (!enemy.flyingTarget) {
				// Set flying target to the closest goal
				enemy.flyingTarget = self.goals[0];
				// Find closest goal if there are multiple
				if (self.goals.length > 1) {
					var closestDist = Infinity;
					for (var i = 0; i < self.goals.length; i++) {
						var goal = self.goals[i];
						var dx = goal.x - enemy.cellX;
						var dy = goal.y - enemy.cellY;
						var dist = dx * dx + dy * dy;
						if (dist < closestDist) {
							closestDist = dist;
							enemy.flyingTarget = goal;
						}
					}
				}
			}
			// Move directly toward the goal
			var ox = enemy.flyingTarget.x - enemy.currentCellX;
			var oy = enemy.flyingTarget.y - enemy.currentCellY;
			var dist = Math.sqrt(ox * ox + oy * oy);
			if (dist < enemy.speed) {
				// Reached the goal
				return true;
			}
			var angle = Math.atan2(oy, ox);
			// Rotate enemy graphic to match movement direction
			if (enemy.children[0] && enemy.children[0].targetRotation === undefined) {
				enemy.children[0].targetRotation = angle;
				enemy.children[0].rotation = angle;
			} else if (enemy.children[0]) {
				if (Math.abs(angle - enemy.children[0].targetRotation) > 0.05) {
					tween.stop(enemy.children[0], {
						rotation: true
					});
					// Calculate the shortest angle to rotate
					var currentRotation = enemy.children[0].rotation;
					var angleDiff = angle - currentRotation;
					// Normalize angle difference to -PI to PI range for shortest path
					while (angleDiff > Math.PI) {
						angleDiff -= Math.PI * 2;
					}
					while (angleDiff < -Math.PI) {
						angleDiff += Math.PI * 2;
					}
					// Set target rotation and animate to it
					enemy.children[0].targetRotation = angle;
					tween(enemy.children[0], {
						rotation: currentRotation + angleDiff
					}, {
						duration: 250,
						easing: tween.easeOut
					});
				}
			}
			// Update the cell position to track where the flying enemy is
			enemy.cellX = Math.round(enemy.currentCellX);
			enemy.cellY = Math.round(enemy.currentCellY);
			enemy.currentCellX += Math.cos(angle) * enemy.speed;
			enemy.currentCellY += Math.sin(angle) * enemy.speed;
			enemy.x = grid.x + enemy.currentCellX * CELL_SIZE;
			enemy.y = grid.y + enemy.currentCellY * CELL_SIZE;
			// Update shadow position if this is a flying enemy
			return false;
		}
		// Handle normal pathfinding enemies
		if (!enemy.currentTarget) {
			enemy.currentTarget = cell.targets[0];
		}
		if (enemy.currentTarget) {
			if (cell.score < enemy.currentTarget.score) {
				enemy.currentTarget = cell;
			}
			var ox = enemy.currentTarget.x - enemy.currentCellX;
			var oy = enemy.currentTarget.y - enemy.currentCellY;
			var dist = Math.sqrt(ox * ox + oy * oy);
			if (dist < enemy.speed) {
				enemy.cellX = Math.round(enemy.currentCellX);
				enemy.cellY = Math.round(enemy.currentCellY);
				enemy.currentTarget = undefined;
				return;
			}
			var angle = Math.atan2(oy, ox);
			enemy.currentCellX += Math.cos(angle) * enemy.speed;
			enemy.currentCellY += Math.sin(angle) * enemy.speed;
		}
		enemy.x = grid.x + enemy.currentCellX * CELL_SIZE;
		enemy.y = grid.y + enemy.currentCellY * CELL_SIZE;
		// Add footprint effect for ground enemies on path
		if (!enemy.isFlying) {
			var currentCell = self.getCell(Math.floor(enemy.currentCellX), Math.floor(enemy.currentCellY));
			if (currentCell && currentCell.type === 0 && currentCell.debugCell) {
				// Only add footprint if enemy has moved to a new cell
				if (!enemy.lastFootprintX || !enemy.lastFootprintY || Math.floor(enemy.currentCellX) !== enemy.lastFootprintX || Math.floor(enemy.currentCellY) !== enemy.lastFootprintY) {
					currentCell.debugCell.addFootprint();
					enemy.lastFootprintX = Math.floor(enemy.currentCellX);
					enemy.lastFootprintY = Math.floor(enemy.currentCellY);
				}
			}
		}
	};
});
var NextWaveButton = Container.expand(function () {
	var self = Container.call(this);
	var buttonBackground = self.attachAsset('notification', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	buttonBackground.width = 300;
	buttonBackground.height = 100;
	buttonBackground.tint = 0x0088FF;
	var buttonText = new Text2("Next Wave", {
		size: 50,
		fill: 0xFFFFFF,
		weight: 800
	});
	buttonText.anchor.set(0.5, 0.5);
	self.addChild(buttonText);
	self.enabled = false;
	self.visible = false;
	self.update = function () {
		if (waveIndicator && waveIndicator.gameStarted && currentWave < totalWaves) {
			self.enabled = true;
			self.visible = true;
			buttonBackground.tint = 0x0088FF;
			self.alpha = 1;
		} else {
			self.enabled = false;
			self.visible = false;
			buttonBackground.tint = 0x888888;
			self.alpha = 0.7;
		}
	};
	self.down = function () {
		if (!self.enabled) {
			return;
		}
		// Play UI click sound
		LK.getSound('ui_click').play();
		if (waveIndicator.gameStarted && currentWave < totalWaves) {
			currentWave++; // Increment to the next wave directly
			waveTimer = 0; // Reset wave timer
			waveInProgress = true;
			waveSpawned = false;
			// Get the type of the current wave (which is now the next wave)
			var waveType = waveIndicator.getWaveTypeName(currentWave);
			var enemyCount = waveIndicator.getEnemyCount(currentWave);
			var waveAlert = new WaveAlert(currentWave, waveType + " ACTIVATED", enemyCount);
			game.addChild(waveAlert);
		}
	};
	return self;
});
var Notification = Container.expand(function (message) {
	var self = Container.call(this);
	var notificationGraphics = self.attachAsset('notification', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	notificationGraphics.tint = 0x2C3E50;
	var notificationText = new Text2(message, {
		size: 52,
		fill: 0xECF0F1,
		weight: 800
	});
	notificationText.anchor.set(0.5, 0.5);
	notificationGraphics.width = notificationText.width + 40;
	notificationGraphics.height = notificationText.height + 20;
	self.addChild(notificationText);
	self.alpha = 1;
	var fadeOutTime = 120;
	self.update = function () {
		if (fadeOutTime > 0) {
			fadeOutTime--;
			self.alpha = Math.min(fadeOutTime / 120 * 2, 1);
		} else {
			self.destroy();
		}
	};
	return self;
});
var SourceTower = Container.expand(function (towerType) {
	var self = Container.call(this);
	self.towerType = towerType || 'laser';
	// Create tower asset based on tower type
	var towerAsset = self.towerType + '_tower';
	var baseGraphics = self.attachAsset(towerAsset, {
		anchorX: 0.5,
		anchorY: 0.5,
		scaleX: 1.3,
		scaleY: 1.3
	});
	var towerCost = getTowerCost(self.towerType);
	// Add tower type label only
	var typeLabel = new Text2(self.towerType.charAt(0).toUpperCase() + self.towerType.slice(1), {
		size: 40,
		fill: 0xFFFFFF,
		weight: 800
	});
	typeLabel.anchor.set(0.5, 0.5);
	typeLabel.y = 0; // Center the text
	self.addChild(typeLabel);
	self.update = function () {
		// Check if player can afford this tower
		var canAfford = gold >= getTowerCost(self.towerType);
		// Set opacity based on affordability
		self.alpha = canAfford ? 1 : 0.5;
	};
	return self;
});
var Tower = Container.expand(function (id) {
	var self = Container.call(this);
	self.id = id || 'laser';
	self.level = 1;
	self.maxLevel = 6;
	self.gridX = 0;
	self.gridY = 0;
	self.range = 3 * CELL_SIZE;
	// Standardized method to get the current range of the tower
	self.getRange = function () {
		// Always calculate range based on tower type and level
		switch (self.id) {
			case 'laser':
				// Laser: base 4, +0.6 per level
				return (4 + (self.level - 1) * 0.6) * CELL_SIZE;
			case 'ice':
				// Ice: base 3, +0.4 per level
				return (3 + (self.level - 1) * 0.4) * CELL_SIZE;
			case 'missile':
				// Missile: base 5, +0.7 per level
				return (5 + (self.level - 1) * 0.7) * CELL_SIZE;
			case 'lightning':
				// Lightning: base 2.5, +0.3 per level
				return (2.5 + (self.level - 1) * 0.3) * CELL_SIZE;
			case 'cannon':
				// Cannon: base 3.5, +0.5 per level
				return (3.5 + (self.level - 1) * 0.5) * CELL_SIZE;
			case 'tesla':
				// Tesla: base 2.8, +0.4 per level
				return (2.8 + (self.level - 1) * 0.4) * CELL_SIZE;
			default:
				// Default to laser stats
				return (4 + (self.level - 1) * 0.6) * CELL_SIZE;
		}
	};
	self.cellsInRange = [];
	self.fireRate = 60;
	self.bulletSpeed = 5;
	self.damage = 10;
	self.lastFired = 0;
	self.targetEnemy = null;
	switch (self.id) {
		case 'laser':
			self.fireRate = 45;
			self.damage = 12;
			self.bulletSpeed = 8;
			break;
		case 'ice':
			self.fireRate = 70;
			self.damage = 8;
			self.bulletSpeed = 4;
			break;
		case 'missile':
			self.fireRate = 90;
			self.damage = 20;
			self.bulletSpeed = 6;
			break;
		case 'lightning':
			self.fireRate = 30;
			self.damage = 6;
			self.bulletSpeed = 12;
			break;
		case 'cannon':
			self.fireRate = 80;
			self.damage = 18;
			self.bulletSpeed = 5;
			break;
		case 'tesla':
			self.fireRate = 40;
			self.damage = 10;
			self.bulletSpeed = 10;
			break;
	}
	// Create tower asset based on tower type
	var towerAsset = self.id + '_tower';
	var baseGraphics = self.attachAsset(towerAsset, {
		anchorX: 0.5,
		anchorY: 0.5
	});
	var levelIndicators = [];
	var maxDots = self.maxLevel;
	var dotSpacing = baseGraphics.width / (maxDots + 1);
	var dotSize = CELL_SIZE / 6;
	for (var i = 0; i < maxDots; i++) {
		var dot = new Container();
		var outlineCircle = dot.attachAsset('towerLevelIndicator', {
			anchorX: 0.5,
			anchorY: 0.5
		});
		outlineCircle.width = dotSize + 4;
		outlineCircle.height = dotSize + 4;
		outlineCircle.tint = 0x2F4F2F; // Dark olive green for turtle theme
		var towerLevelIndicator = dot.attachAsset('towerLevelIndicator', {
			anchorX: 0.5,
			anchorY: 0.5
		});
		towerLevelIndicator.width = dotSize;
		towerLevelIndicator.height = dotSize;
		towerLevelIndicator.tint = 0x90EE90; // Light green for turtle theme
		dot.x = -CELL_SIZE + dotSpacing * (i + 1);
		dot.y = CELL_SIZE * 0.7;
		self.addChild(dot);
		levelIndicators.push(dot);
	}
	var gunContainer = new Container();
	self.addChild(gunContainer);
	var gunGraphics = gunContainer.attachAsset('gun_barrel', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	// Color gun based on tower type
	switch (self.id) {
		case 'laser':
			gunGraphics.tint = 0xFF0080;
			break;
		case 'ice':
			gunGraphics.tint = 0x00FFFF;
			break;
		case 'missile':
			gunGraphics.tint = 0x808080;
			break;
		case 'lightning':
			gunGraphics.tint = 0xFFFF00;
			break;
		case 'cannon':
			gunGraphics.tint = 0x8B4513;
			break;
		case 'tesla':
			gunGraphics.tint = 0x9932CC;
			break;
		default:
			gunGraphics.tint = 0xFF0080;
	}
	self.updateLevelIndicators = function () {
		for (var i = 0; i < maxDots; i++) {
			var dot = levelIndicators[i];
			var towerLevelIndicator = dot.children[1];
			if (i < self.level) {
				towerLevelIndicator.tint = 0xFFD700; // Gold for active turtle levels
			} else {
				switch (self.id) {
					case 'laser':
						towerLevelIndicator.tint = 0xFF0080; // Pink laser
						break;
					case 'ice':
						towerLevelIndicator.tint = 0x00FFFF; // Cyan ice
						break;
					case 'missile':
						towerLevelIndicator.tint = 0x808080; // Gray missile
						break;
					case 'lightning':
						towerLevelIndicator.tint = 0xFFFF00; // Yellow lightning
						break;
					case 'cannon':
						towerLevelIndicator.tint = 0x8B4513; // Brown cannon
						break;
					case 'tesla':
						towerLevelIndicator.tint = 0x9932CC; // Purple tesla
						break;
					default:
						towerLevelIndicator.tint = 0xFF0080;
					// Default to laser
				}
			}
		}
	};
	self.updateLevelIndicators();
	// Initialize idle animation properties
	self.idleAnimationActive = false;
	self.upgradeAnimationActive = false;
	// Start idle animation based on tower type
	self.startIdleAnimation = function () {
		if (self.idleAnimationActive || self.upgradeAnimationActive) return;
		self.idleAnimationActive = true;
		switch (self.id) {
			case 'laser':
				// Subtle sway and flickering lights for laser towers
				var _swayTween = function swayTween() {
					if (!self.idleAnimationActive) return;
					tween(baseGraphics, {
						rotation: baseGraphics.rotation + (Math.random() - 0.5) * 0.1
					}, {
						duration: 2000 + Math.random() * 1000,
						easing: tween.easeInOut,
						onFinish: _swayTween
					});
				};
				_swayTween();
				// Flickering light effect
				var _flickerTween = function flickerTween() {
					if (!self.idleAnimationActive) return;
					tween(baseGraphics, {
						alpha: 0.8 + Math.random() * 0.2
					}, {
						duration: 300 + Math.random() * 200,
						easing: tween.linear,
						onFinish: _flickerTween
					});
				};
				_flickerTween();
				break;
			case 'ice':
				// Gentle pulsing for ice towers
				var _pulseTween = function pulseTween() {
					if (!self.idleAnimationActive) return;
					tween(baseGraphics, {
						scaleX: 1.02,
						scaleY: 1.02
					}, {
						duration: 1500,
						easing: tween.easeInOut,
						onFinish: function onFinish() {
							if (!self.idleAnimationActive) return;
							tween(baseGraphics, {
								scaleX: 1.0,
								scaleY: 1.0
							}, {
								duration: 1500,
								easing: tween.easeInOut,
								onFinish: _pulseTween
							});
						}
					});
				};
				_pulseTween();
				break;
			case 'tesla':
				// Electric crackling effect
				var crackleOffset = 0;
				var _crackleTween = function crackleTween() {
					if (!self.idleAnimationActive) return;
					crackleOffset += (Math.random() - 0.5) * 0.05;
					tween(baseGraphics, {
						x: crackleOffset,
						y: crackleOffset * 0.5
					}, {
						duration: 100 + Math.random() * 100,
						easing: tween.linear,
						onFinish: _crackleTween
					});
				};
				_crackleTween();
				break;
			default:
				// Default subtle breathing animation
				var _breatheTween = function breatheTween() {
					if (!self.idleAnimationActive) return;
					tween(baseGraphics, {
						scaleX: 1.01,
						scaleY: 1.01
					}, {
						duration: 2000,
						easing: tween.easeInOut,
						onFinish: function onFinish() {
							if (!self.idleAnimationActive) return;
							tween(baseGraphics, {
								scaleX: 1.0,
								scaleY: 1.0
							}, {
								duration: 2000,
								easing: tween.easeInOut,
								onFinish: _breatheTween
							});
						}
					});
				};
				_breatheTween();
		}
	};
	// Stop idle animation
	self.stopIdleAnimation = function () {
		self.idleAnimationActive = false;
		tween.stop(baseGraphics, {
			rotation: true,
			alpha: true,
			scaleX: true,
			scaleY: true,
			x: true,
			y: true
		});
		// Reset to default state
		baseGraphics.rotation = 0;
		baseGraphics.alpha = 1;
		baseGraphics.scaleX = 1;
		baseGraphics.scaleY = 1;
		baseGraphics.x = 0;
		baseGraphics.y = 0;
	};
	// Start idle animation immediately
	self.startIdleAnimation();
	self.refreshCellsInRange = function () {
		for (var i = 0; i < self.cellsInRange.length; i++) {
			var cell = self.cellsInRange[i];
			var towerIndex = cell.towersInRange.indexOf(self);
			if (towerIndex !== -1) {
				cell.towersInRange.splice(towerIndex, 1);
			}
		}
		self.cellsInRange = [];
		var rangeRadius = self.getRange() / CELL_SIZE;
		var centerX = self.gridX + 1;
		var centerY = self.gridY + 1;
		var minI = Math.floor(centerX - rangeRadius - 0.5);
		var maxI = Math.ceil(centerX + rangeRadius + 0.5);
		var minJ = Math.floor(centerY - rangeRadius - 0.5);
		var maxJ = Math.ceil(centerY + rangeRadius + 0.5);
		for (var i = minI; i <= maxI; i++) {
			for (var j = minJ; j <= maxJ; j++) {
				var closestX = Math.max(i, Math.min(centerX, i + 1));
				var closestY = Math.max(j, Math.min(centerY, j + 1));
				var deltaX = closestX - centerX;
				var deltaY = closestY - centerY;
				var distanceSquared = deltaX * deltaX + deltaY * deltaY;
				if (distanceSquared <= rangeRadius * rangeRadius) {
					var cell = grid.getCell(i, j);
					if (cell) {
						self.cellsInRange.push(cell);
						cell.towersInRange.push(self);
					}
				}
			}
		}
		grid.renderDebug();
	};
	self.getTotalValue = function () {
		var baseTowerCost = getTowerCost(self.id);
		var totalInvestment = baseTowerCost;
		var baseUpgradeCost = baseTowerCost; // Upgrade cost now scales with base tower cost
		for (var i = 1; i < self.level; i++) {
			totalInvestment += Math.floor(baseUpgradeCost * Math.pow(2, i - 1));
		}
		return totalInvestment;
	};
	self.upgrade = function () {
		if (self.level < self.maxLevel) {
			// Exponential upgrade cost: base cost * (2 ^ (level-1)), scaled by tower base cost
			var baseUpgradeCost = getTowerCost(self.id);
			var upgradeCost;
			// Make last upgrade level extra expensive
			if (self.level === self.maxLevel - 1) {
				upgradeCost = Math.floor(baseUpgradeCost * Math.pow(2, self.level - 1) * 3.5 / 2); // Half the cost for final upgrade
			} else {
				upgradeCost = Math.floor(baseUpgradeCost * Math.pow(2, self.level - 1));
			}
			if (gold >= upgradeCost) {
				setGold(gold - upgradeCost);
				self.level++;
				// No need to update self.range here; getRange() is now the source of truth
				// Apply tower-specific upgrades based on type
				if (self.id === 'rapid') {
					if (self.level === self.maxLevel) {
						// Extra powerful last upgrade (double the effect)
						self.fireRate = Math.max(4, 30 - self.level * 9); // double the effect
						self.damage = 5 + self.level * 10; // double the effect
						self.bulletSpeed = 7 + self.level * 2.4; // double the effect
					} else {
						self.fireRate = Math.max(15, 30 - self.level * 3); // Fast tower gets faster with upgrades
						self.damage = 5 + self.level * 3;
						self.bulletSpeed = 7 + self.level * 0.7;
					}
				} else {
					if (self.level === self.maxLevel) {
						// Extra powerful last upgrade for all other towers (double the effect)
						self.fireRate = Math.max(5, 60 - self.level * 24); // double the effect
						self.damage = 10 + self.level * 20; // double the effect
						self.bulletSpeed = 5 + self.level * 2.4; // double the effect
					} else {
						self.fireRate = Math.max(20, 60 - self.level * 8);
						self.damage = 10 + self.level * 5;
						self.bulletSpeed = 5 + self.level * 0.5;
					}
				}
				self.refreshCellsInRange();
				self.updateLevelIndicators();
				// Play upgrade animation
				self.upgradeAnimationActive = true;
				self.stopIdleAnimation();
				// Glow effect and transformation animation
				tween(baseGraphics, {
					scaleX: 1.3,
					scaleY: 1.3,
					alpha: 1.5
				}, {
					duration: 200,
					easing: tween.easeOut,
					onFinish: function onFinish() {
						// Bright flash
						tween(baseGraphics, {
							alpha: 2.0
						}, {
							duration: 100,
							easing: tween.linear,
							onFinish: function onFinish() {
								// Return to normal with power glow
								tween(baseGraphics, {
									scaleX: 1.05,
									scaleY: 1.05,
									alpha: 1.0
								}, {
									duration: 300,
									easing: tween.easeInOut,
									onFinish: function onFinish() {
										// Final settle
										tween(baseGraphics, {
											scaleX: 1.0,
											scaleY: 1.0
										}, {
											duration: 200,
											easing: tween.easeOut,
											onFinish: function onFinish() {
												self.upgradeAnimationActive = false;
												self.startIdleAnimation();
											}
										});
									}
								});
							}
						});
					}
				});
				// Add particle burst effect around the tower
				var upgradeEffect = new Container();
				game.addChild(upgradeEffect);
				upgradeEffect.x = self.x;
				upgradeEffect.y = self.y;
				// Create multiple expanding rings for upgrade effect
				for (var ring = 0; ring < 3; ring++) {
					var ringContainer = new Container();
					upgradeEffect.addChild(ringContainer);
					var ringGraphics = ringContainer.attachAsset('rangeCircle', {
						anchorX: 0.5,
						anchorY: 0.5
					});
					ringGraphics.width = ringGraphics.height = CELL_SIZE * 0.5;
					ringGraphics.alpha = 0.8;
					// Different colors based on tower type
					switch (self.id) {
						case 'laser':
							ringGraphics.tint = 0xFF0080;
							break;
						case 'ice':
							ringGraphics.tint = 0x00FFFF;
							break;
						case 'missile':
							ringGraphics.tint = 0x808080;
							break;
						case 'lightning':
							ringGraphics.tint = 0xFFFF00;
							break;
						case 'cannon':
							ringGraphics.tint = 0x8B4513;
							break;
						case 'tesla':
							ringGraphics.tint = 0x9932CC;
							break;
						default:
							ringGraphics.tint = 0xFFD700;
					}
					// Stagger the ring animations
					LK.setTimeout(function () {
						tween(ringContainer, {
							alpha: 0,
							scaleX: 4,
							scaleY: 4
						}, {
							duration: 600,
							easing: tween.easeOut,
							onFinish: function onFinish() {
								upgradeEffect.removeChild(ringContainer);
								if (upgradeEffect.children.length === 0) {
									upgradeEffect.destroy();
								}
							}
						});
					}, ring * 150);
				}
				if (self.level > 1) {
					var levelDot = levelIndicators[self.level - 1].children[1];
					tween(levelDot, {
						scaleX: 1.5,
						scaleY: 1.5
					}, {
						duration: 300,
						easing: tween.elasticOut,
						onFinish: function onFinish() {
							tween(levelDot, {
								scaleX: 1,
								scaleY: 1
							}, {
								duration: 200,
								easing: tween.easeOut
							});
						}
					});
				}
				return true;
			} else {
				var notification = game.addChild(new Notification("Not enough gold to upgrade!"));
				notification.x = 2048 / 2;
				notification.y = grid.height - 50;
				return false;
			}
		}
		return false;
	};
	self.findTarget = function () {
		var closestEnemy = null;
		var closestScore = Infinity;
		for (var i = 0; i < enemies.length; i++) {
			var enemy = enemies[i];
			var dx = enemy.x - self.x;
			var dy = enemy.y - self.y;
			var distance = Math.sqrt(dx * dx + dy * dy);
			// Check if enemy is in range
			if (distance <= self.getRange()) {
				// Handle flying enemies differently - they can be targeted regardless of path
				if (enemy.isFlying) {
					// For flying enemies, prioritize by distance to the goal
					if (enemy.flyingTarget) {
						var goalX = enemy.flyingTarget.x;
						var goalY = enemy.flyingTarget.y;
						var distToGoal = Math.sqrt((goalX - enemy.cellX) * (goalX - enemy.cellX) + (goalY - enemy.cellY) * (goalY - enemy.cellY));
						// Use distance to goal as score
						if (distToGoal < closestScore) {
							closestScore = distToGoal;
							closestEnemy = enemy;
						}
					} else {
						// If no flying target yet (shouldn't happen), prioritize by distance to tower
						if (distance < closestScore) {
							closestScore = distance;
							closestEnemy = enemy;
						}
					}
				} else {
					// For ground enemies, use the original path-based targeting
					// Get the cell for this enemy
					var cell = grid.getCell(enemy.cellX, enemy.cellY);
					if (cell && cell.pathId === pathId) {
						// Use the cell's score (distance to exit) for prioritization
						// Lower score means closer to exit
						if (cell.score < closestScore) {
							closestScore = cell.score;
							closestEnemy = enemy;
						}
					}
				}
			}
		}
		if (!closestEnemy) {
			self.targetEnemy = null;
		}
		return closestEnemy;
	};
	self.update = function () {
		self.targetEnemy = self.findTarget();
		if (self.targetEnemy) {
			var dx = self.targetEnemy.x - self.x;
			var dy = self.targetEnemy.y - self.y;
			var angle = Math.atan2(dy, dx);
			gunContainer.rotation = angle;
			// Make the tower base also rotate to face the target
			baseGraphics.rotation = angle;
			if (LK.ticks - self.lastFired >= self.fireRate) {
				self.fire();
				self.lastFired = LK.ticks;
			}
		}
	};
	self.down = function (x, y, obj) {
		// Play tower selection sound
		LK.getSound('ui_select').play();
		var existingMenus = game.children.filter(function (child) {
			return child instanceof UpgradeMenu;
		});
		var hasOwnMenu = false;
		var rangeCircle = null;
		for (var i = 0; i < game.children.length; i++) {
			if (game.children[i].isTowerRange && game.children[i].tower === self) {
				rangeCircle = game.children[i];
				break;
			}
		}
		for (var i = 0; i < existingMenus.length; i++) {
			if (existingMenus[i].tower === self) {
				hasOwnMenu = true;
				break;
			}
		}
		if (hasOwnMenu) {
			for (var i = 0; i < existingMenus.length; i++) {
				if (existingMenus[i].tower === self) {
					hideUpgradeMenu(existingMenus[i]);
				}
			}
			if (rangeCircle) {
				game.removeChild(rangeCircle);
			}
			selectedTower = null;
			grid.renderDebug();
			return;
		}
		for (var i = 0; i < existingMenus.length; i++) {
			existingMenus[i].destroy();
		}
		for (var i = game.children.length - 1; i >= 0; i--) {
			if (game.children[i].isTowerRange) {
				game.removeChild(game.children[i]);
			}
		}
		selectedTower = self;
		var rangeIndicator = new Container();
		rangeIndicator.isTowerRange = true;
		rangeIndicator.tower = self;
		game.addChild(rangeIndicator);
		rangeIndicator.x = self.x;
		rangeIndicator.y = self.y;
		var rangeGraphics = rangeIndicator.attachAsset('rangeCircle', {
			anchorX: 0.5,
			anchorY: 0.5
		});
		rangeGraphics.width = rangeGraphics.height = self.getRange() * 2;
		rangeGraphics.alpha = 0.25;
		rangeGraphics.tint = 0x74B9FF;
		var upgradeMenu = new UpgradeMenu(self);
		game.addChild(upgradeMenu);
		upgradeMenu.x = 2048 / 2;
		tween(upgradeMenu, {
			y: 2732 - 225
		}, {
			duration: 200,
			easing: tween.backOut
		});
		// Add detailed tower information panel
		var towerInfoPanel = new TowerInfoPanel(self);
		game.addChild(towerInfoPanel);
		towerInfoPanel.x = 2048 / 2;
		tween(towerInfoPanel, {
			y: 2732 - 650
		}, {
			duration: 250,
			easing: tween.backOut
		});
		grid.renderDebug();
	};
	self.isInRange = function (enemy) {
		if (!enemy) {
			return false;
		}
		var dx = enemy.x - self.x;
		var dy = enemy.y - self.y;
		var distance = Math.sqrt(dx * dx + dy * dy);
		return distance <= self.getRange();
	};
	self.fire = function () {
		if (self.targetEnemy) {
			var potentialDamage = 0;
			for (var i = 0; i < self.targetEnemy.bulletsTargetingThis.length; i++) {
				potentialDamage += self.targetEnemy.bulletsTargetingThis[i].damage;
			}
			if (self.targetEnemy.health > potentialDamage) {
				var bulletX = self.x + Math.cos(gunContainer.rotation) * 40;
				var bulletY = self.y + Math.sin(gunContainer.rotation) * 40;
				var bullet = new Bullet(bulletX, bulletY, self.targetEnemy, self.damage, self.bulletSpeed);
				// Set bullet type based on tower type
				bullet.type = self.id;
				// Customize bullet appearance based on tower type
				switch (self.id) {
					case 'laser':
						bullet.children[0].tint = 0xFF0080;
						bullet.children[0].width = 25;
						bullet.children[0].height = 25;
						break;
					case 'ice':
						bullet.children[0].tint = 0x00FFFF;
						bullet.children[0].width = 30;
						bullet.children[0].height = 30;
						break;
					case 'missile':
						bullet.children[0].tint = 0x808080;
						bullet.children[0].width = 35;
						bullet.children[0].height = 35;
						break;
					case 'lightning':
						bullet.children[0].tint = 0xFFFF00;
						bullet.children[0].width = 20;
						bullet.children[0].height = 20;
						break;
					case 'cannon':
						bullet.children[0].tint = 0x8B4513;
						bullet.children[0].width = 40;
						bullet.children[0].height = 40;
						break;
					case 'tesla':
						bullet.children[0].tint = 0x9932CC;
						bullet.children[0].width = 25;
						bullet.children[0].height = 25;
						break;
				}
				game.addChild(bullet);
				bullets.push(bullet);
				self.targetEnemy.bulletsTargetingThis.push(bullet);
				// Play shooting sound effect based on tower type
				var soundId = self.id + '_shoot';
				LK.getSound(soundId).play();
				// Create muzzle flash effect at firing position
				var muzzleFlash = new Container();
				game.addChild(muzzleFlash);
				muzzleFlash.x = bulletX;
				muzzleFlash.y = bulletY;
				var flashGraphics = muzzleFlash.attachAsset('rangeCircle', {
					anchorX: 0.5,
					anchorY: 0.5
				});
				// Customize muzzle flash based on tower type
				switch (self.id) {
					case 'laser':
						flashGraphics.width = flashGraphics.height = CELL_SIZE * 0.4;
						flashGraphics.tint = 0xFF0080;
						break;
					case 'ice':
						flashGraphics.width = flashGraphics.height = CELL_SIZE * 0.3;
						flashGraphics.tint = 0x00FFFF;
						break;
					case 'missile':
						flashGraphics.width = flashGraphics.height = CELL_SIZE * 0.6;
						flashGraphics.tint = 0xFF4500;
						break;
					case 'lightning':
						flashGraphics.width = flashGraphics.height = CELL_SIZE * 0.2;
						flashGraphics.tint = 0xFFFF00;
						break;
					case 'cannon':
						flashGraphics.width = flashGraphics.height = CELL_SIZE * 0.8;
						flashGraphics.tint = 0xFF6600;
						break;
					case 'tesla':
						flashGraphics.width = flashGraphics.height = CELL_SIZE * 0.35;
						flashGraphics.tint = 0x9932CC;
						break;
					default:
						flashGraphics.width = flashGraphics.height = CELL_SIZE * 0.4;
						flashGraphics.tint = 0xFFFFFF;
				}
				flashGraphics.alpha = 1;
				// Quick flash animation
				tween(muzzleFlash, {
					alpha: 0,
					scaleX: 1.8,
					scaleY: 1.8
				}, {
					duration: 120,
					easing: tween.easeOut,
					onFinish: function onFinish() {
						muzzleFlash.destroy();
					}
				});
				// --- Fire recoil effect for gunContainer ---
				// Stop any ongoing recoil tweens before starting a new one
				tween.stop(gunContainer, {
					x: true,
					y: true,
					scaleX: true,
					scaleY: true
				});
				// Always use the original resting position for recoil, never accumulate offset
				if (gunContainer._restX === undefined) {
					gunContainer._restX = 0;
				}
				if (gunContainer._restY === undefined) {
					gunContainer._restY = 0;
				}
				if (gunContainer._restScaleX === undefined) {
					gunContainer._restScaleX = 1;
				}
				if (gunContainer._restScaleY === undefined) {
					gunContainer._restScaleY = 1;
				}
				// Reset to resting position before animating (in case of interrupted tweens)
				gunContainer.x = gunContainer._restX;
				gunContainer.y = gunContainer._restY;
				gunContainer.scaleX = gunContainer._restScaleX;
				gunContainer.scaleY = gunContainer._restScaleY;
				// Calculate recoil offset (recoil back along the gun's rotation)
				var recoilDistance = 8;
				var recoilX = -Math.cos(gunContainer.rotation) * recoilDistance;
				var recoilY = -Math.sin(gunContainer.rotation) * recoilDistance;
				// Animate recoil back from the resting position
				tween(gunContainer, {
					x: gunContainer._restX + recoilX,
					y: gunContainer._restY + recoilY
				}, {
					duration: 60,
					easing: tween.cubicOut,
					onFinish: function onFinish() {
						// Animate return to original position/scale
						tween(gunContainer, {
							x: gunContainer._restX,
							y: gunContainer._restY
						}, {
							duration: 90,
							easing: tween.cubicIn
						});
					}
				});
			}
		}
	};
	self.placeOnGrid = function (gridX, gridY) {
		self.gridX = gridX;
		self.gridY = gridY;
		self.x = grid.x + gridX * CELL_SIZE + CELL_SIZE / 2;
		self.y = grid.y + gridY * CELL_SIZE + CELL_SIZE / 2;
		for (var i = 0; i < 2; i++) {
			for (var j = 0; j < 2; j++) {
				var cell = grid.getCell(gridX + i, gridY + j);
				if (cell) {
					cell.type = 1;
				}
			}
		}
		self.refreshCellsInRange();
	};
	// Override destroy to clean up animations
	var originalDestroy = self.destroy;
	self.destroy = function () {
		self.stopIdleAnimation();
		if (originalDestroy) {
			originalDestroy.call(self);
		}
	};
	return self;
});
var TowerInfoPanel = Container.expand(function (tower) {
	var self = Container.call(this);
	self.tower = tower;
	self.y = 2732 + 300;
	var panelBackground = self.attachAsset('notification', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	panelBackground.width = 1800;
	panelBackground.height = 400;
	panelBackground.tint = 0x2C3E50;
	panelBackground.alpha = 0.95;
	// Tower name and level
	var titleText = new Text2(self.tower.id.charAt(0).toUpperCase() + self.tower.id.slice(1) + ' Tower - Level ' + self.tower.level, {
		size: 60,
		fill: 0xFFFFFF,
		weight: 800
	});
	titleText.anchor.set(0.5, 0);
	titleText.x = 0;
	titleText.y = -160;
	self.addChild(titleText);
	// Detailed stats
	var dpsValue = (self.tower.damage * (60 / self.tower.fireRate)).toFixed(1);
	var rangeValue = (self.tower.getRange() / CELL_SIZE).toFixed(1);
	var statsText = new Text2('DPS: ' + dpsValue + ' | Range: ' + rangeValue + ' tiles | Speed: ' + self.tower.bulletSpeed, {
		size: 45,
		fill: 0xECF0F1,
		weight: 400
	});
	statsText.anchor.set(0.5, 0);
	statsText.x = 0;
	statsText.y = -90;
	self.addChild(statsText);
	// Special abilities description
	var abilityText = '';
	switch (self.tower.id) {
		case 'laser':
			abilityText = 'High damage, fast firing laser tower with good range';
			break;
		case 'ice':
			abilityText = 'Slows enemies and applies frost effects';
			break;
		case 'missile':
			abilityText = 'Heavy damage with explosive splash effect';
			break;
		case 'lightning':
			abilityText = 'Fast, low damage with electrical chain effects';
			break;
		case 'cannon':
			abilityText = 'Powerful impact with knockback and crater effects';
			break;
		case 'tesla':
			abilityText = 'Energy discharge with electrical arc effects';
			break;
	}
	var abilityDesc = new Text2(abilityText, {
		size: 38,
		fill: 0xBDC3C7,
		weight: 400
	});
	abilityDesc.anchor.set(0.5, 0);
	abilityDesc.x = 0;
	abilityDesc.y = -30;
	self.addChild(abilityDesc);
	// Bullet preview
	var bulletPreview = new Container();
	bulletPreview.x = -600;
	bulletPreview.y = 50;
	self.addChild(bulletPreview);
	var previewLabel = new Text2('Bullet Preview:', {
		size: 35,
		fill: 0xFFFFFF,
		weight: 600
	});
	previewLabel.anchor.set(0.5, 0.5);
	previewLabel.y = -40;
	bulletPreview.addChild(previewLabel);
	// Animated bullet preview
	var bulletAsset = self.tower.id + '_bullet';
	var bulletGraphics = bulletPreview.attachAsset(bulletAsset, {
		anchorX: 0.5,
		anchorY: 0.5
	});
	// Scale up the bullet for visibility
	bulletGraphics.scaleX = 2;
	bulletGraphics.scaleY = 2;
	// Apply tower-specific coloring
	switch (self.tower.id) {
		case 'laser':
			bulletGraphics.tint = 0xFF0080;
			break;
		case 'ice':
			bulletGraphics.tint = 0x00FFFF;
			break;
		case 'missile':
			bulletGraphics.tint = 0x808080;
			break;
		case 'lightning':
			bulletGraphics.tint = 0xFFFF00;
			break;
		case 'cannon':
			bulletGraphics.tint = 0x8B4513;
			break;
		case 'tesla':
			bulletGraphics.tint = 0x9932CC;
			break;
	}
	// Animate the bullet preview
	var _animateBullet = function animateBullet() {
		tween(bulletGraphics, {
			rotation: bulletGraphics.rotation + Math.PI * 2
		}, {
			duration: 2000,
			easing: tween.linear,
			onFinish: _animateBullet
		});
	};
	_animateBullet();
	// Upgrade cost preview (right side)
	if (self.tower.level < self.tower.maxLevel) {
		var upgradePreview = new Container();
		upgradePreview.x = 600;
		upgradePreview.y = 50;
		self.addChild(upgradePreview);
		var upgradeLabel = new Text2('Next Upgrade:', {
			size: 35,
			fill: 0xFFFFFF,
			weight: 600
		});
		upgradeLabel.anchor.set(0.5, 0.5);
		upgradeLabel.y = -40;
		upgradePreview.addChild(upgradeLabel);
		// Calculate next level stats
		var nextDamage = self.tower.damage + 5;
		var nextFireRate = Math.max(20, self.tower.fireRate - 8);
		var nextDPS = (nextDamage * (60 / nextFireRate)).toFixed(1);
		var nextRange = ((self.tower.getRange() + CELL_SIZE * 0.5) / CELL_SIZE).toFixed(1);
		var upgradeStatsText = new Text2('DPS: ' + nextDPS + ' | Range: ' + nextRange + ' tiles', {
			size: 32,
			fill: 0x2ECC71,
			weight: 400
		});
		upgradeStatsText.anchor.set(0.5, 0.5);
		upgradeStatsText.y = 10;
		upgradePreview.addChild(upgradeStatsText);
	}
	self.updateStats = function () {
		var dpsValue = (self.tower.damage * (60 / self.tower.fireRate)).toFixed(1);
		var rangeValue = (self.tower.getRange() / CELL_SIZE).toFixed(1);
		titleText.setText(self.tower.id.charAt(0).toUpperCase() + self.tower.id.slice(1) + ' Tower - Level ' + self.tower.level);
		statsText.setText('DPS: ' + dpsValue + ' | Range: ' + rangeValue + ' tiles | Speed: ' + self.tower.bulletSpeed);
	};
	return self;
});
var TowerPreview = Container.expand(function () {
	var self = Container.call(this);
	var towerRange = 3;
	var rangeInPixels = towerRange * CELL_SIZE;
	self.towerType = 'default';
	self.hasEnoughGold = true;
	var rangeIndicator = new Container();
	self.addChild(rangeIndicator);
	var rangeGraphics = rangeIndicator.attachAsset('rangeCircle', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	rangeGraphics.alpha = 0.3;
	var previewGraphics = self.attachAsset('towerpreview', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	previewGraphics.width = CELL_SIZE * 2;
	previewGraphics.height = CELL_SIZE * 2;
	self.canPlace = false;
	self.gridX = 0;
	self.gridY = 0;
	self.blockedByEnemy = false;
	self.update = function () {
		var previousHasEnoughGold = self.hasEnoughGold;
		self.hasEnoughGold = gold >= getTowerCost(self.towerType);
		// Only update appearance if the affordability status has changed
		if (previousHasEnoughGold !== self.hasEnoughGold) {
			self.updateAppearance();
		}
	};
	self.updateAppearance = function () {
		// Use Tower class to get the source of truth for range
		var tempTower = new Tower(self.towerType);
		var previewRange = tempTower.getRange();
		// Clean up tempTower to avoid memory leaks
		if (tempTower && tempTower.destroy) {
			tempTower.destroy();
		}
		// Set range indicator using unified range logic
		rangeGraphics.width = rangeGraphics.height = previewRange * 2;
		// Use single tower asset for preview
		var previewAsset = 'towerpreview'; // Use existing preview asset
		// Update the preview graphics appearance
		previewGraphics.tint = 0xFFFFFF; // Keep white tint as base
		if (!self.canPlace || !self.hasEnoughGold) {
			previewGraphics.tint = 0xFF0000;
			previewGraphics.alpha = 0.6;
		} else {
			previewGraphics.alpha = 0.8;
		}
	};
	self.updatePlacementStatus = function () {
		var validGridPlacement = true;
		if (self.gridY <= 4 || self.gridY + 1 >= grid.cells[0].length - 4) {
			validGridPlacement = false;
		} else {
			for (var i = 0; i < 2; i++) {
				for (var j = 0; j < 2; j++) {
					var cell = grid.getCell(self.gridX + i, self.gridY + j);
					// Allow towers to be placed on walls (type 1) but not on paths (type 0), spawns (type 2), or goals (type 3)
					if (!cell || cell.type === 0 || cell.type === 2 || cell.type === 3) {
						validGridPlacement = false;
						break;
					}
				}
				if (!validGridPlacement) {
					break;
				}
			}
		}
		self.blockedByEnemy = false;
		if (validGridPlacement) {
			for (var i = 0; i < enemies.length; i++) {
				var enemy = enemies[i];
				if (enemy.currentCellY < 4) {
					continue;
				}
				// Only check non-flying enemies, flying enemies can pass over towers
				if (!enemy.isFlying) {
					if (enemy.cellX >= self.gridX && enemy.cellX < self.gridX + 2 && enemy.cellY >= self.gridY && enemy.cellY < self.gridY + 2) {
						self.blockedByEnemy = true;
						break;
					}
					if (enemy.currentTarget) {
						var targetX = enemy.currentTarget.x;
						var targetY = enemy.currentTarget.y;
						if (targetX >= self.gridX && targetX < self.gridX + 2 && targetY >= self.gridY && targetY < self.gridY + 2) {
							self.blockedByEnemy = true;
							break;
						}
					}
				}
			}
		}
		self.canPlace = validGridPlacement && !self.blockedByEnemy;
		self.hasEnoughGold = gold >= getTowerCost(self.towerType);
		self.updateAppearance();
	};
	self.checkPlacement = function () {
		self.updatePlacementStatus();
	};
	self.snapToGrid = function (x, y) {
		var gridPosX = x - grid.x;
		var gridPosY = y - grid.y;
		self.gridX = Math.floor(gridPosX / CELL_SIZE);
		self.gridY = Math.floor(gridPosY / CELL_SIZE);
		self.x = grid.x + self.gridX * CELL_SIZE + CELL_SIZE / 2;
		self.y = grid.y + self.gridY * CELL_SIZE + CELL_SIZE / 2;
		self.checkPlacement();
	};
	return self;
});
var UpgradeMenu = Container.expand(function (tower) {
	var self = Container.call(this);
	self.tower = tower;
	self.y = 2732 + 225;
	var menuBackground = self.attachAsset('notification', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	menuBackground.width = 2048;
	menuBackground.height = 500;
	menuBackground.tint = 0x444444;
	menuBackground.alpha = 0.9;
	var towerTypeText = new Text2(self.tower.id.charAt(0).toUpperCase() + self.tower.id.slice(1) + ' Tower', {
		size: 60,
		fill: 0xFFFFFF,
		weight: 800
	});
	towerTypeText.anchor.set(0, 0);
	towerTypeText.x = -700;
	towerTypeText.y = -120;
	self.addChild(towerTypeText);
	var statsText = new Text2('Lvl ' + self.tower.level + '/' + self.tower.maxLevel + ' | Dmg: ' + self.tower.damage + ' | Rate: ' + (60 / self.tower.fireRate).toFixed(1) + '/s', {
		size: 50,
		fill: 0xFFFFFF,
		weight: 400
	});
	statsText.anchor.set(0, 0.5);
	statsText.x = -700;
	statsText.y = -40;
	self.addChild(statsText);
	var buttonsContainer = new Container();
	buttonsContainer.x = 500;
	self.addChild(buttonsContainer);
	var upgradeButton = new Container();
	buttonsContainer.addChild(upgradeButton);
	var buttonBackground = upgradeButton.attachAsset('notification', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	buttonBackground.width = 500;
	buttonBackground.height = 150;
	var isMaxLevel = self.tower.level >= self.tower.maxLevel;
	// Exponential upgrade cost: base cost * (2 ^ (level-1)), scaled by tower base cost
	var baseUpgradeCost = getTowerCost(self.tower.id);
	var upgradeCost;
	if (isMaxLevel) {
		upgradeCost = 0;
	} else if (self.tower.level === self.tower.maxLevel - 1) {
		upgradeCost = Math.floor(baseUpgradeCost * Math.pow(2, self.tower.level - 1) * 3.5 / 2);
	} else {
		upgradeCost = Math.floor(baseUpgradeCost * Math.pow(2, self.tower.level - 1));
	}
	buttonBackground.tint = isMaxLevel ? 0x888888 : gold >= upgradeCost ? 0x00AA00 : 0x888888;
	var buttonText = new Text2(isMaxLevel ? 'Max Level' : 'Upgrade: ' + upgradeCost, {
		size: 50,
		fill: 0xFFFFFF,
		weight: 800
	});
	buttonText.anchor.set(0.5, 0.5);
	upgradeButton.addChild(buttonText);
	var sellButton = new Container();
	buttonsContainer.addChild(sellButton);
	var sellButtonBackground = sellButton.attachAsset('notification', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	sellButtonBackground.width = 500;
	sellButtonBackground.height = 150;
	sellButtonBackground.tint = 0xCC0000;
	var totalInvestment = self.tower.getTotalValue ? self.tower.getTotalValue() : 0;
	var sellValue = getTowerSellValue(totalInvestment);
	var sellButtonText = new Text2('Sell: +' + sellValue, {
		size: 50,
		fill: 0xFFFFFF,
		weight: 800
	});
	sellButtonText.anchor.set(0.5, 0.5);
	sellButton.addChild(sellButtonText);
	upgradeButton.y = -85;
	sellButton.y = 85;
	var closeButton = new Container();
	self.addChild(closeButton);
	var closeBackground = closeButton.attachAsset('notification', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	closeBackground.width = 90;
	closeBackground.height = 90;
	closeBackground.tint = 0xAA0000;
	var closeText = new Text2('X', {
		size: 68,
		fill: 0xFFFFFF,
		weight: 800
	});
	closeText.anchor.set(0.5, 0.5);
	closeButton.addChild(closeText);
	closeButton.x = menuBackground.width / 2 - 57;
	closeButton.y = -menuBackground.height / 2 + 57;
	upgradeButton.down = function (x, y, obj) {
		// Play UI click sound
		LK.getSound('ui_click').play();
		if (self.tower.level >= self.tower.maxLevel) {
			var notification = game.addChild(new Notification("Tower is already at max level!"));
			notification.x = 2048 / 2;
			notification.y = grid.height - 50;
			return;
		}
		if (self.tower.upgrade()) {
			// Exponential upgrade cost: base cost * (2 ^ (level-1)), scaled by tower base cost
			var baseUpgradeCost = getTowerCost(self.tower.id);
			if (self.tower.level >= self.tower.maxLevel) {
				upgradeCost = 0;
			} else if (self.tower.level === self.tower.maxLevel - 1) {
				upgradeCost = Math.floor(baseUpgradeCost * Math.pow(2, self.tower.level - 1) * 3.5 / 2);
			} else {
				upgradeCost = Math.floor(baseUpgradeCost * Math.pow(2, self.tower.level - 1));
			}
			statsText.setText('Lvl ' + self.tower.level + '/' + self.tower.maxLevel + ' | Dmg: ' + self.tower.damage + ' | Rate: ' + (60 / self.tower.fireRate).toFixed(1) + '/s');
			buttonText.setText('Upgrade: ' + upgradeCost);
			var totalInvestment = self.tower.getTotalValue ? self.tower.getTotalValue() : 0;
			var sellValue = Math.floor(totalInvestment * 0.6);
			sellButtonText.setText('Sell: +' + sellValue);
			if (self.tower.level >= self.tower.maxLevel) {
				buttonBackground.tint = 0x888888;
				buttonText.setText('Max Level');
			}
			var rangeCircle = null;
			for (var i = 0; i < game.children.length; i++) {
				if (game.children[i].isTowerRange && game.children[i].tower === self.tower) {
					rangeCircle = game.children[i];
					break;
				}
			}
			if (rangeCircle) {
				var rangeGraphics = rangeCircle.children[0];
				rangeGraphics.width = rangeGraphics.height = self.tower.getRange() * 2;
			} else {
				var newRangeIndicator = new Container();
				newRangeIndicator.isTowerRange = true;
				newRangeIndicator.tower = self.tower;
				game.addChildAt(newRangeIndicator, 0);
				newRangeIndicator.x = self.tower.x;
				newRangeIndicator.y = self.tower.y;
				var rangeGraphics = newRangeIndicator.attachAsset('rangeCircle', {
					anchorX: 0.5,
					anchorY: 0.5
				});
				rangeGraphics.width = rangeGraphics.height = self.tower.getRange() * 2;
				rangeGraphics.alpha = 0.3;
			}
			tween(self, {
				scaleX: 1.05,
				scaleY: 1.05
			}, {
				duration: 100,
				easing: tween.easeOut,
				onFinish: function onFinish() {
					tween(self, {
						scaleX: 1,
						scaleY: 1
					}, {
						duration: 100,
						easing: tween.easeIn
					});
				}
			});
		}
	};
	sellButton.down = function (x, y, obj) {
		// Play UI click sound
		LK.getSound('ui_click').play();
		var totalInvestment = self.tower.getTotalValue ? self.tower.getTotalValue() : 0;
		var sellValue = getTowerSellValue(totalInvestment);
		setGold(gold + sellValue);
		var notification = game.addChild(new Notification("Tower sold for " + sellValue + " gold!"));
		notification.x = 2048 / 2;
		notification.y = grid.height - 50;
		var gridX = self.tower.gridX;
		var gridY = self.tower.gridY;
		for (var i = 0; i < 2; i++) {
			for (var j = 0; j < 2; j++) {
				var cell = grid.getCell(gridX + i, gridY + j);
				if (cell) {
					cell.type = 0;
					var towerIndex = cell.towersInRange.indexOf(self.tower);
					if (towerIndex !== -1) {
						cell.towersInRange.splice(towerIndex, 1);
					}
				}
			}
		}
		// Stop any ongoing animations before destroying tower
		if (self.tower.stopIdleAnimation) {
			self.tower.stopIdleAnimation();
		}
		if (selectedTower === self.tower) {
			selectedTower = null;
		}
		var towerIndex = towers.indexOf(self.tower);
		if (towerIndex !== -1) {
			towers.splice(towerIndex, 1);
		}
		towerLayer.removeChild(self.tower);
		grid.pathFind();
		grid.renderDebug();
		self.destroy();
		for (var i = 0; i < game.children.length; i++) {
			if (game.children[i].isTowerRange && game.children[i].tower === self.tower) {
				game.removeChild(game.children[i]);
				break;
			}
		}
	};
	closeButton.down = function (x, y, obj) {
		// Play UI click sound
		LK.getSound('ui_click').play();
		hideUpgradeMenu(self);
		selectedTower = null;
		grid.renderDebug();
	};
	self.update = function () {
		if (self.tower.level >= self.tower.maxLevel) {
			if (buttonText.text !== 'Max Level') {
				buttonText.setText('Max Level');
				buttonBackground.tint = 0x888888;
			}
			return;
		}
		// Exponential upgrade cost: base cost * (2 ^ (level-1)), scaled by tower base cost
		var baseUpgradeCost = getTowerCost(self.tower.id);
		var currentUpgradeCost;
		if (self.tower.level >= self.tower.maxLevel) {
			currentUpgradeCost = 0;
		} else if (self.tower.level === self.tower.maxLevel - 1) {
			currentUpgradeCost = Math.floor(baseUpgradeCost * Math.pow(2, self.tower.level - 1) * 3.5 / 2);
		} else {
			currentUpgradeCost = Math.floor(baseUpgradeCost * Math.pow(2, self.tower.level - 1));
		}
		var canAfford = gold >= currentUpgradeCost;
		buttonBackground.tint = canAfford ? 0x00AA00 : 0x888888;
		var newText = 'Upgrade: ' + currentUpgradeCost;
		if (buttonText.text !== newText) {
			buttonText.setText(newText);
		}
	};
	return self;
});
var WaveAlert = Container.expand(function (waveNumber, waveType, enemyCount) {
	var self = Container.call(this);
	// Create alert background
	var alertBackground = self.attachAsset('notification', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	alertBackground.width = 1200;
	alertBackground.height = 200;
	alertBackground.tint = 0xFF4444; // Red alert color
	// Create wave number text
	var waveText = new Text2("WAVE " + waveNumber, {
		size: 80,
		fill: 0xFFFFFF,
		weight: 800
	});
	waveText.anchor.set(0.5, 0.5);
	waveText.y = -30;
	self.addChild(waveText);
	// Create wave type and enemy count text
	var typeText = new Text2(waveType + " - " + enemyCount + " ENEMIES", {
		size: 50,
		fill: 0xFFFFFF,
		weight: 600
	});
	typeText.anchor.set(0.5, 0.5);
	typeText.y = 30;
	self.addChild(typeText);
	// Start off-screen
	self.x = 2048 / 2;
	self.y = -200;
	self.alpha = 0;
	self.scaleX = 0.5;
	self.scaleY = 0.5;
	// Play wave alert sound
	LK.getSound('wave_alert').play();
	// Animate entrance
	tween(self, {
		y: 400,
		alpha: 1,
		scaleX: 1.2,
		scaleY: 1.2
	}, {
		duration: 500,
		easing: tween.backOut,
		onFinish: function onFinish() {
			// Scale down slightly and hold
			tween(self, {
				scaleX: 1,
				scaleY: 1
			}, {
				duration: 200,
				easing: tween.easeOut,
				onFinish: function onFinish() {
					// Hold for a moment then fade out
					LK.setTimeout(function () {
						tween(self, {
							y: 200,
							alpha: 0,
							scaleX: 0.8,
							scaleY: 0.8
						}, {
							duration: 400,
							easing: tween.easeIn,
							onFinish: function onFinish() {
								self.destroy();
							}
						});
					}, 1500);
				}
			});
		}
	});
	return self;
});
var WaveIndicator = Container.expand(function () {
	var self = Container.call(this);
	self.gameStarted = false;
	self.waveMarkers = [];
	self.waveTypes = [];
	self.enemyCounts = [];
	self.indicatorWidth = 0;
	self.lastBossType = null; // Track the last boss type to avoid repeating
	var blockWidth = 400;
	var totalBlocksWidth = blockWidth * totalWaves;
	var startMarker = new Container();
	var startBlock = startMarker.attachAsset('notification', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	startBlock.width = blockWidth - 10;
	startBlock.height = 70 * 2;
	startBlock.tint = 0x00AA00;
	var startText = new Text2("Start Game", {
		size: 50,
		fill: 0xFFFFFF,
		weight: 800
	});
	startText.anchor.set(0.5, 0.5);
	startMarker.addChild(startText);
	startMarker.x = -self.indicatorWidth;
	self.addChild(startMarker);
	self.waveMarkers.push(startMarker);
	startMarker.down = function () {
		if (!self.gameStarted) {
			self.gameStarted = true;
			currentWave = 0;
			waveTimer = nextWaveTime;
			startBlock.tint = 0x00FF00;
			startText.setText("Started!");
			var notification = game.addChild(new Notification("Game started! Wave 1 incoming!"));
			notification.x = 2048 / 2;
			notification.y = grid.height - 150;
		}
	};
	for (var i = 0; i < totalWaves; i++) {
		var marker = new Container();
		var block = marker.attachAsset('notification', {
			anchorX: 0.5,
			anchorY: 0.5
		});
		block.width = blockWidth - 10;
		block.height = 70 * 2;
		// --- Begin new unified wave logic ---
		var waveType = "normal";
		var enemyType = "normal";
		var enemyCount = 10;
		var isBossWave = (i + 1) % 10 === 0;
		// Ensure all types appear in early waves
		if (i === 0) {
			block.tint = 0xAAAAAA;
			waveType = "Normal";
			enemyType = "normal";
			enemyCount = 10;
		} else if (i === 1) {
			block.tint = 0x00AAFF;
			waveType = "Fast";
			enemyType = "fast";
			enemyCount = 10;
		} else if (i === 2) {
			block.tint = 0xAA0000;
			waveType = "Immune";
			enemyType = "immune";
			enemyCount = 10;
		} else if (i === 3) {
			block.tint = 0xFFFF00;
			waveType = "Flying";
			enemyType = "flying";
			enemyCount = 10;
		} else if (i === 4) {
			block.tint = 0xFF00FF;
			waveType = "Swarm";
			enemyType = "swarm";
			enemyCount = 30;
		} else if (isBossWave) {
			// Boss waves: cycle through all boss types, last boss is always flying
			var bossTypes = ['normal', 'fast', 'immune', 'flying'];
			var bossTypeIndex = Math.floor((i + 1) / 10) - 1;
			if (i === totalWaves - 1) {
				// Last boss is always flying
				enemyType = 'flying';
				waveType = "Boss Flying";
				block.tint = 0xFFFF00;
			} else {
				enemyType = bossTypes[bossTypeIndex % bossTypes.length];
				switch (enemyType) {
					case 'normal':
						block.tint = 0xAAAAAA;
						waveType = "Boss Normal";
						break;
					case 'fast':
						block.tint = 0x00AAFF;
						waveType = "Boss Fast";
						break;
					case 'immune':
						block.tint = 0xAA0000;
						waveType = "Boss Immune";
						break;
					case 'flying':
						block.tint = 0xFFFF00;
						waveType = "Boss Flying";
						break;
				}
			}
			enemyCount = 1;
			// Make the wave indicator for boss waves stand out
			// Set boss wave color to the color of the wave type
			switch (enemyType) {
				case 'normal':
					block.tint = 0xAAAAAA;
					break;
				case 'fast':
					block.tint = 0x00AAFF;
					break;
				case 'immune':
					block.tint = 0xAA0000;
					break;
				case 'flying':
					block.tint = 0xFFFF00;
					break;
				default:
					block.tint = 0xFF0000;
					break;
			}
		} else if ((i + 1) % 5 === 0) {
			// Every 5th non-boss wave is fast
			block.tint = 0x00AAFF;
			waveType = "Fast";
			enemyType = "fast";
			enemyCount = 10;
		} else if ((i + 1) % 4 === 0) {
			// Every 4th non-boss wave is immune
			block.tint = 0xAA0000;
			waveType = "Immune";
			enemyType = "immune";
			enemyCount = 10;
		} else if ((i + 1) % 7 === 0) {
			// Every 7th non-boss wave is flying
			block.tint = 0xFFFF00;
			waveType = "Flying";
			enemyType = "flying";
			enemyCount = 10;
		} else if ((i + 1) % 3 === 0) {
			// Every 3rd non-boss wave is swarm
			block.tint = 0xFF00FF;
			waveType = "Swarm";
			enemyType = "swarm";
			enemyCount = 30;
		} else {
			block.tint = 0xAAAAAA;
			waveType = "Normal";
			enemyType = "normal";
			enemyCount = 10;
		}
		// --- End new unified wave logic ---
		// Mark boss waves with a special visual indicator
		if (isBossWave && enemyType !== 'swarm') {
			// Add a crown or some indicator to the wave marker for boss waves
			var bossIndicator = marker.attachAsset('towerLevelIndicator', {
				anchorX: 0.5,
				anchorY: 0.5
			});
			bossIndicator.width = 30;
			bossIndicator.height = 30;
			bossIndicator.tint = 0xFFD700; // Gold color
			bossIndicator.y = -block.height / 2 - 15;
			// Change the wave type text to indicate boss
			waveType = "BOSS";
		}
		// Store the wave type and enemy count
		self.waveTypes[i] = enemyType;
		self.enemyCounts[i] = enemyCount;
		// Add wave type text - simplified
		var waveTypeText = new Text2(waveType, {
			size: 50,
			fill: 0xFFFFFF,
			weight: 800
		});
		waveTypeText.anchor.set(0.5, 0.5);
		waveTypeText.y = -15;
		marker.addChild(waveTypeText);
		// Main wave number text - simplified
		var waveNum = new Text2((i + 1).toString(), {
			size: 40,
			fill: 0xFFFFFF,
			weight: 800
		});
		waveNum.anchor.set(0.5, 0.5);
		waveNum.y = 20;
		marker.addChild(waveNum);
		marker.x = -self.indicatorWidth + (i + 1) * blockWidth;
		self.addChild(marker);
		self.waveMarkers.push(marker);
	}
	// Get wave type for a specific wave number
	self.getWaveType = function (waveNumber) {
		if (waveNumber < 1 || waveNumber > totalWaves) {
			return "normal";
		}
		// If this is a boss wave (waveNumber % 10 === 0), and the type is the same as lastBossType
		// then we should return a different boss type
		var waveType = self.waveTypes[waveNumber - 1];
		return waveType;
	};
	// Get enemy count for a specific wave number
	self.getEnemyCount = function (waveNumber) {
		if (waveNumber < 1 || waveNumber > totalWaves) {
			return 10;
		}
		return self.enemyCounts[waveNumber - 1];
	};
	// Get display name for a wave type
	self.getWaveTypeName = function (waveNumber) {
		var type = self.getWaveType(waveNumber);
		var typeName = type.charAt(0).toUpperCase() + type.slice(1);
		// Add boss prefix for boss waves (every 10th wave)
		if (waveNumber % 10 === 0 && waveNumber > 0 && type !== 'swarm') {
			typeName = "BOSS";
		}
		return typeName;
	};
	self.positionIndicator = new Container();
	var indicator = self.positionIndicator.attachAsset('towerLevelIndicator', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	indicator.width = blockWidth - 10;
	indicator.height = 16;
	indicator.tint = 0xffad0e;
	indicator.y = -65;
	var indicator2 = self.positionIndicator.attachAsset('towerLevelIndicator', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	indicator2.width = blockWidth - 10;
	indicator2.height = 16;
	indicator2.tint = 0xffad0e;
	indicator2.y = 65;
	var leftWall = self.positionIndicator.attachAsset('towerLevelIndicator', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	leftWall.width = 16;
	leftWall.height = 146;
	leftWall.tint = 0xffad0e;
	leftWall.x = -(blockWidth - 16) / 2;
	var rightWall = self.positionIndicator.attachAsset('towerLevelIndicator', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	rightWall.width = 16;
	rightWall.height = 146;
	rightWall.tint = 0xffad0e;
	rightWall.x = (blockWidth - 16) / 2;
	self.addChild(self.positionIndicator);
	self.update = function () {
		var progress = waveTimer / nextWaveTime;
		var moveAmount = (progress + currentWave) * blockWidth;
		for (var i = 0; i < self.waveMarkers.length; i++) {
			var marker = self.waveMarkers[i];
			marker.x = -moveAmount + i * blockWidth;
		}
		self.positionIndicator.x = 0;
		for (var i = 0; i < totalWaves + 1; i++) {
			var marker = self.waveMarkers[i];
			if (i === 0) {
				continue;
			}
			var block = marker.children[0];
			if (i - 1 < currentWave) {
				block.alpha = .5;
			}
		}
		self.handleWaveProgression = function () {
			if (!self.gameStarted) {
				return;
			}
			if (currentWave < totalWaves) {
				waveTimer++;
				if (waveTimer >= nextWaveTime) {
					waveTimer = 0;
					currentWave++;
					waveInProgress = true;
					waveSpawned = false;
					if (currentWave != 1) {
						var waveType = self.getWaveTypeName(currentWave);
						var enemyCount = self.getEnemyCount(currentWave);
						var waveAlert = new WaveAlert(currentWave, waveType + " INCOMING", enemyCount);
						game.addChild(waveAlert);
					}
				}
			}
		};
		self.handleWaveProgression();
	};
	return self;
});
var WavePreviewPanel = Container.expand(function () {
	var self = Container.call(this);
	self.x = 2048 - 400;
	self.y = 300;
	self.visible = false;
	var panelBackground = self.attachAsset('notification', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	panelBackground.width = 350;
	panelBackground.height = 500;
	panelBackground.tint = 0x34495E;
	panelBackground.alpha = 0.9;
	var titleText = new Text2('Next Wave', {
		size: 45,
		fill: 0xFFFFFF,
		weight: 800
	});
	titleText.anchor.set(0.5, 0);
	titleText.x = 0;
	titleText.y = -220;
	self.addChild(titleText);
	var waveNumberText = new Text2('', {
		size: 55,
		fill: 0xE74C3C,
		weight: 800
	});
	waveNumberText.anchor.set(0.5, 0);
	waveNumberText.x = 0;
	waveNumberText.y = -160;
	self.addChild(waveNumberText);
	var enemyTypeText = new Text2('', {
		size: 40,
		fill: 0xF39C12,
		weight: 600
	});
	enemyTypeText.anchor.set(0.5, 0);
	enemyTypeText.x = 0;
	enemyTypeText.y = -100;
	self.addChild(enemyTypeText);
	var enemyCountText = new Text2('', {
		size: 35,
		fill: 0x3498DB,
		weight: 500
	});
	enemyCountText.anchor.set(0.5, 0);
	enemyCountText.x = 0;
	enemyCountText.y = -50;
	self.addChild(enemyCountText);
	// Enemy preview sprite
	var enemyPreview = new Container();
	enemyPreview.x = 0;
	enemyPreview.y = 50;
	self.addChild(enemyPreview);
	var enemySprite = null;
	self.updatePreview = function (waveNumber) {
		if (waveNumber > totalWaves) {
			self.visible = false;
			return;
		}
		self.visible = true;
		waveNumberText.setText('Wave ' + waveNumber);
		var waveType = waveIndicator.getWaveType(waveNumber);
		var enemyCount = waveIndicator.getEnemyCount(waveNumber);
		var isBossWave = waveNumber % 10 === 0 && waveNumber > 0;
		// Update text based on wave type
		var typeDisplayName = waveType.charAt(0).toUpperCase() + waveType.slice(1);
		if (isBossWave && waveType !== 'swarm') {
			typeDisplayName = 'BOSS ' + typeDisplayName;
		}
		enemyTypeText.setText(typeDisplayName);
		enemyCountText.setText(enemyCount + ' Enemies');
		// Remove old enemy sprite
		if (enemySprite) {
			enemyPreview.removeChild(enemySprite);
		}
		// Add new enemy sprite preview
		var assetId = 'enemy';
		if (waveType !== 'normal') {
			assetId = 'enemy_' + waveType;
		}
		enemySprite = enemyPreview.attachAsset(assetId, {
			anchorX: 0.5,
			anchorY: 0.5
		});
		// Scale based on boss status
		if (isBossWave && waveType !== 'swarm') {
			enemySprite.scaleX = 1.5;
			enemySprite.scaleY = 1.5;
		}
		// Add floating animation
		var _floatAnimation = function floatAnimation() {
			tween(enemySprite, {
				y: 10
			}, {
				duration: 1500,
				easing: tween.easeInOut,
				onFinish: function onFinish() {
					tween(enemySprite, {
						y: -10
					}, {
						duration: 1500,
						easing: tween.easeInOut,
						onFinish: _floatAnimation
					});
				}
			});
		};
		_floatAnimation();
		// Special effects for boss waves
		if (isBossWave && waveType !== 'swarm') {
			// Add crown indicator
			var crownIndicator = enemyPreview.attachAsset('towerLevelIndicator', {
				anchorX: 0.5,
				anchorY: 0.5
			});
			crownIndicator.width = 25;
			crownIndicator.height = 25;
			crownIndicator.tint = 0xFFD700;
			crownIndicator.y = -60;
			// Pulsing animation for crown
			var _pulseCrown = function pulseCrown() {
				tween(crownIndicator, {
					scaleX: 1.3,
					scaleY: 1.3
				}, {
					duration: 800,
					easing: tween.easeInOut,
					onFinish: function onFinish() {
						tween(crownIndicator, {
							scaleX: 1.0,
							scaleY: 1.0
						}, {
							duration: 800,
							easing: tween.easeInOut,
							onFinish: _pulseCrown
						});
					}
				});
			};
			_pulseCrown();
		}
	};
	return self;
});
/**** 
* Initialize Game
****/ 
var game = new LK.Game({
	backgroundColor: 0xF4E4BC
});
/**** 
* Game Code
****/ 
var isHidingUpgradeMenu = false;
function hideUpgradeMenu(menu) {
	if (isHidingUpgradeMenu) {
		return;
	}
	isHidingUpgradeMenu = true;
	// Also hide any tower info panels
	var towerInfoPanels = game.children.filter(function (child) {
		return child instanceof TowerInfoPanel;
	});
	for (var i = 0; i < towerInfoPanels.length; i++) {
		(function (panel) {
			tween(panel, {
				y: 2732 + 300
			}, {
				duration: 150,
				easing: tween.easeIn,
				onFinish: function onFinish() {
					if (panel && panel.destroy) {
						panel.destroy();
					}
				}
			});
		})(towerInfoPanels[i]);
	}
	tween(menu, {
		y: 2732 + 225
	}, {
		duration: 150,
		easing: tween.easeIn,
		onFinish: function onFinish() {
			menu.destroy();
			isHidingUpgradeMenu = false;
		}
	});
}
var CELL_SIZE = 76;
var pathId = 1;
var maxScore = 0;
var enemies = [];
var towers = [];
var bullets = [];
var defenses = [];
var selectedTower = null;
var gold = 80;
var lives = 20;
var score = 0;
var currentWave = 0;
var totalWaves = 50;
var waveTimer = 0;
var waveInProgress = false;
var waveSpawned = false;
var nextWaveTime = 12000 / 2;
var sourceTower = null;
var enemiesToSpawn = 10; // Default number of enemies per wave
var goldText = new Text2('Gold: ' + gold, {
	size: 65,
	fill: 0xDAA520,
	weight: 800
});
goldText.anchor.set(0.5, 0.5);
var livesText = new Text2('Lives: ' + lives, {
	size: 65,
	fill: 0x1E90FF,
	weight: 800
});
livesText.anchor.set(0.5, 0.5);
var scoreText = new Text2('Score: ' + score, {
	size: 65,
	fill: 0xFF7F50,
	weight: 800
});
scoreText.anchor.set(0.5, 0.5);
var topMargin = 50;
var centerX = 2048 / 2;
var spacing = 400;
LK.gui.top.addChild(goldText);
LK.gui.top.addChild(livesText);
LK.gui.top.addChild(scoreText);
livesText.x = 0;
livesText.y = topMargin;
goldText.x = -spacing;
goldText.y = topMargin;
scoreText.x = spacing;
scoreText.y = topMargin;
function updateUI() {
	goldText.setText('Gold: ' + gold);
	livesText.setText('Lives: ' + lives);
	scoreText.setText('Score: ' + score);
}
function setGold(value) {
	gold = value;
	updateUI();
}
var debugLayer = new Container();
var towerLayer = new Container();
// Create three separate layers for enemy hierarchy
var enemyLayerBottom = new Container(); // For normal enemies
var enemyLayerMiddle = new Container(); // For shadows
var enemyLayerTop = new Container(); // For flying enemies
var enemyLayer = new Container(); // Main container to hold all enemy layers
// Add layers in correct order (bottom first, then middle for shadows, then top)
enemyLayer.addChild(enemyLayerBottom);
enemyLayer.addChild(enemyLayerMiddle);
enemyLayer.addChild(enemyLayerTop);
var grid = new Grid(24, 29 + 6);
grid.x = 150;
grid.y = 200 - CELL_SIZE * 4;
grid.pathFind();
grid.renderDebug();
debugLayer.addChild(grid);
game.addChild(debugLayer);
game.addChild(towerLayer);
game.addChild(enemyLayer);
var offset = 0;
var towerPreview = new TowerPreview();
game.addChild(towerPreview);
towerPreview.visible = false;
var isDragging = false;
function wouldBlockPath(gridX, gridY) {
	var cells = [];
	for (var i = 0; i < 2; i++) {
		for (var j = 0; j < 2; j++) {
			var cell = grid.getCell(gridX + i, gridY + j);
			if (cell) {
				cells.push({
					cell: cell,
					originalType: cell.type
				});
				cell.type = 1;
			}
		}
	}
	var blocked = grid.pathFind();
	for (var i = 0; i < cells.length; i++) {
		cells[i].cell.type = cells[i].originalType;
	}
	grid.pathFind();
	grid.renderDebug();
	return blocked;
}
function getTowerCost(towerType) {
	var cost = 15;
	switch (towerType) {
		case 'laser':
			cost = 15;
			break;
		case 'ice':
			cost = 25;
			break;
		case 'missile':
			cost = 40;
			break;
		case 'lightning':
			cost = 20;
			break;
		case 'cannon':
			cost = 35;
			break;
		case 'tesla':
			cost = 30;
			break;
	}
	return cost;
}
function getTowerSellValue(totalValue) {
	return waveIndicator && waveIndicator.gameStarted ? Math.floor(totalValue * 0.6) : totalValue;
}
function placeTower(gridX, gridY, towerType) {
	var towerCost = getTowerCost(towerType);
	var canAfford = gold >= towerCost;
	if (canAfford) {
		var tower = new Tower(towerType || 'default');
		tower.placeOnGrid(gridX, gridY);
		towerLayer.addChild(tower);
		towers.push(tower);
		setGold(gold - towerCost);
		grid.pathFind();
		grid.renderDebug();
		return true;
	} else {
		var notification = game.addChild(new Notification("Not enough gold!"));
		notification.x = 2048 / 2;
		notification.y = grid.height - 50;
		return false;
	}
}
game.down = function (x, y, obj) {
	var upgradeMenuVisible = game.children.some(function (child) {
		return child instanceof UpgradeMenu;
	});
	if (upgradeMenuVisible) {
		return;
	}
	for (var i = 0; i < sourceTowers.length; i++) {
		var tower = sourceTowers[i];
		if (x >= tower.x - tower.width / 2 && x <= tower.x + tower.width / 2 && y >= tower.y - tower.height / 2 && y <= tower.y + tower.height / 2) {
			towerPreview.visible = true;
			isDragging = true;
			towerPreview.towerType = tower.towerType;
			towerPreview.updateAppearance();
			// Apply the same offset as in move handler to ensure consistency when starting drag
			towerPreview.snapToGrid(x, y - CELL_SIZE * 1.5);
			break;
		}
	}
};
game.move = function (x, y, obj) {
	if (isDragging) {
		// Shift the y position upward by 1.5 tiles to show preview above finger
		towerPreview.snapToGrid(x, y - CELL_SIZE * 1.5);
	}
};
game.up = function (x, y, obj) {
	var clickedOnTower = false;
	for (var i = 0; i < towers.length; i++) {
		var tower = towers[i];
		var towerLeft = tower.x - tower.width / 2;
		var towerRight = tower.x + tower.width / 2;
		var towerTop = tower.y - tower.height / 2;
		var towerBottom = tower.y + tower.height / 2;
		if (x >= towerLeft && x <= towerRight && y >= towerTop && y <= towerBottom) {
			clickedOnTower = true;
			break;
		}
	}
	var upgradeMenus = game.children.filter(function (child) {
		return child instanceof UpgradeMenu;
	});
	if (upgradeMenus.length > 0 && !isDragging && !clickedOnTower) {
		var clickedOnMenu = false;
		for (var i = 0; i < upgradeMenus.length; i++) {
			var menu = upgradeMenus[i];
			var menuWidth = 2048;
			var menuHeight = 450;
			var menuLeft = menu.x - menuWidth / 2;
			var menuRight = menu.x + menuWidth / 2;
			var menuTop = menu.y - menuHeight / 2;
			var menuBottom = menu.y + menuHeight / 2;
			if (x >= menuLeft && x <= menuRight && y >= menuTop && y <= menuBottom) {
				clickedOnMenu = true;
				break;
			}
		}
		if (!clickedOnMenu) {
			for (var i = 0; i < upgradeMenus.length; i++) {
				var menu = upgradeMenus[i];
				hideUpgradeMenu(menu);
			}
			for (var i = game.children.length - 1; i >= 0; i--) {
				if (game.children[i].isTowerRange) {
					game.removeChild(game.children[i]);
				}
			}
			selectedTower = null;
			grid.renderDebug();
		}
	}
	if (isDragging) {
		isDragging = false;
		if (towerPreview.canPlace) {
			if (!wouldBlockPath(towerPreview.gridX, towerPreview.gridY)) {
				placeTower(towerPreview.gridX, towerPreview.gridY, towerPreview.towerType);
			} else {
				var notification = game.addChild(new Notification("Tower would block the path!"));
				notification.x = 2048 / 2;
				notification.y = grid.height - 50;
			}
		} else if (towerPreview.blockedByEnemy) {
			var notification = game.addChild(new Notification("Cannot build: Enemy in the way!"));
			notification.x = 2048 / 2;
			notification.y = grid.height - 50;
		} else if (towerPreview.visible) {
			var notification = game.addChild(new Notification("Cannot build here!"));
			notification.x = 2048 / 2;
			notification.y = grid.height - 50;
		}
		towerPreview.visible = false;
		if (isDragging) {
			var upgradeMenus = game.children.filter(function (child) {
				return child instanceof UpgradeMenu;
			});
			for (var i = 0; i < upgradeMenus.length; i++) {
				upgradeMenus[i].destroy();
			}
		}
	}
};
var waveIndicator = new WaveIndicator();
waveIndicator.x = 2048 / 2;
waveIndicator.y = 2732 - 80;
game.addChild(waveIndicator);
var nextWaveButtonContainer = new Container();
var nextWaveButton = new NextWaveButton();
nextWaveButton.x = 2048 - 200;
nextWaveButton.y = 2732 - 100 + 20;
nextWaveButtonContainer.addChild(nextWaveButton);
game.addChild(nextWaveButtonContainer);
// Add wave preview panel
var wavePreviewPanel = new WavePreviewPanel();
game.addChild(wavePreviewPanel);
var towerTypes = ['laser', 'ice', 'missile', 'lightning', 'cannon', 'tesla'];
var sourceTowers = [];
var towerSpacing = 300; // Increase spacing for larger towers
var startX = 2048 / 2 - towerTypes.length * towerSpacing / 2 + towerSpacing / 2;
var towerY = 2732 - CELL_SIZE * 3 - 90;
for (var i = 0; i < towerTypes.length; i++) {
	var tower = new SourceTower(towerTypes[i]);
	tower.x = startX + i * towerSpacing;
	tower.y = towerY;
	towerLayer.addChild(tower);
	sourceTowers.push(tower);
	// Add cost display text below each tower
	var costText = new Text2('Cost: ' + getTowerCost(towerTypes[i]), {
		size: 35,
		fill: 0xFFD700,
		weight: 800
	});
	costText.anchor.set(0.5, 0.5);
	costText.x = tower.x;
	costText.y = tower.y + 120; // Position below the tower
	towerLayer.addChild(costText);
}
// Start playing background music
LK.playMusic('game_music', {
	loop: true,
	fade: {
		start: 0,
		end: 0.3,
		duration: 2000
	}
});
sourceTower = null;
enemiesToSpawn = 10;
// Create environmental elements layer
var environmentLayer = new Container();
game.addChildAt(environmentLayer, 0); // Add behind everything else
// Create background clouds
var clouds = [];
for (var i = 0; i < 8; i++) {
	var cloud = new Cloud(Math.random() * 2048,
	// Random x position
	100 + Math.random() * 300 // Random y position in sky
	);
	environmentLayer.addChild(cloud);
	clouds.push(cloud);
}
// Add environmental decorations around the grid edges
var environmentalElements = [];
// Add palm trees along the sides of the grid
var palmTreePositions = [{
	x: grid.x - 100,
	y: grid.y + 300
}, {
	x: grid.x - 80,
	y: grid.y + 800
}, {
	x: grid.x - 120,
	y: grid.y + 1300
}, {
	x: grid.x + grid.cells.length * CELL_SIZE + 80,
	y: grid.y + 400
}, {
	x: grid.x + grid.cells.length * CELL_SIZE + 100,
	y: grid.y + 900
}, {
	x: grid.x + grid.cells.length * CELL_SIZE + 60,
	y: grid.y + 1400
}];
for (var i = 0; i < palmTreePositions.length; i++) {
	var pos = palmTreePositions[i];
	var palmTree = new EnvironmentalElement('palm_tree', pos.x, pos.y);
	environmentLayer.addChild(palmTree);
	environmentalElements.push(palmTree);
}
// Add rocks scattered around the edges
var rockPositions = [{
	x: grid.x - 60,
	y: grid.y + 200
}, {
	x: grid.x - 40,
	y: grid.y + 600
}, {
	x: grid.x - 80,
	y: grid.y + 1100
}, {
	x: grid.x - 50,
	y: grid.y + 1600
}, {
	x: grid.x + grid.cells.length * CELL_SIZE + 40,
	y: grid.y + 250
}, {
	x: grid.x + grid.cells.length * CELL_SIZE + 60,
	y: grid.y + 700
}, {
	x: grid.x + grid.cells.length * CELL_SIZE + 30,
	y: grid.y + 1200
}, {
	x: grid.x + grid.cells.length * CELL_SIZE + 70,
	y: grid.y + 1500
}];
for (var i = 0; i < rockPositions.length; i++) {
	var pos = rockPositions[i];
	var rock = new EnvironmentalElement('rock', pos.x, pos.y);
	environmentLayer.addChild(rock);
	environmentalElements.push(rock);
}
// Add seashells scattered around the beach areas
var seashellPositions = [{
	x: grid.x - 30,
	y: grid.y + 150
}, {
	x: grid.x - 70,
	y: grid.y + 500
}, {
	x: grid.x - 20,
	y: grid.y + 850
}, {
	x: grid.x - 90,
	y: grid.y + 1250
}, {
	x: grid.x - 45,
	y: grid.y + 1550
}, {
	x: grid.x + grid.cells.length * CELL_SIZE + 20,
	y: grid.y + 180
}, {
	x: grid.x + grid.cells.length * CELL_SIZE + 50,
	y: grid.y + 550
}, {
	x: grid.x + grid.cells.length * CELL_SIZE + 15,
	y: grid.y + 950
}, {
	x: grid.x + grid.cells.length * CELL_SIZE + 45,
	y: grid.y + 1350
}, {
	x: grid.x + grid.cells.length * CELL_SIZE + 25,
	y: grid.y + 1650
}];
for (var i = 0; i < seashellPositions.length; i++) {
	var pos = seashellPositions[i];
	var seashell = new EnvironmentalElement('seashell', pos.x, pos.y);
	environmentLayer.addChild(seashell);
	environmentalElements.push(seashell);
}
game.update = function () {
	if (waveInProgress) {
		if (!waveSpawned) {
			waveSpawned = true;
			// Get wave type and enemy count from the wave indicator
			var waveType = waveIndicator.getWaveType(currentWave);
			var enemyCount = waveIndicator.getEnemyCount(currentWave);
			// Check if this is a boss wave
			var isBossWave = currentWave % 10 === 0 && currentWave > 0;
			if (isBossWave && waveType !== 'swarm') {
				// Boss waves have just 1 enemy regardless of what the wave indicator says
				enemyCount = 1;
				// Show boss announcement with enhanced alert
				var bossAlert = new WaveAlert(currentWave, "⚠️ BOSS " + waveType.toUpperCase() + " ⚠️", enemyCount);
				game.addChild(bossAlert);
			}
			// Show wave alert for all waves except wave 1
			if (currentWave > 1 && !isBossWave) {
				var waveAlert = new WaveAlert(currentWave, waveType.toUpperCase(), enemyCount);
				game.addChild(waveAlert);
			}
			// Spawn the appropriate number of enemies
			for (var i = 0; i < enemyCount; i++) {
				var enemy = new Enemy(waveType);
				// Add enemy to the appropriate layer based on type
				if (enemy.isFlying) {
					// Add flying enemy to the top layer
					enemyLayerTop.addChild(enemy);
					// If it's a flying enemy, add its shadow to the middle layer
					if (enemy.shadow) {
						enemyLayerMiddle.addChild(enemy.shadow);
					}
				} else {
					// Add normal/ground enemies to the bottom layer
					enemyLayerBottom.addChild(enemy);
				}
				// Scale difficulty with wave number but don't apply to boss
				// as bosses already have their health multiplier
				// Use exponential scaling for health
				var healthMultiplier = Math.pow(1.12, currentWave); // ~20% increase per wave
				enemy.maxHealth = Math.round(enemy.maxHealth * healthMultiplier);
				enemy.health = enemy.maxHealth;
				// Increment speed slightly with wave number
				//enemy.speed = enemy.speed + currentWave * 0.002;
				// All enemy types now spawn in the middle 6 tiles at the top spacing
				var gridWidth = 24;
				var midPoint = Math.floor(gridWidth / 2); // 12
				// Find a column that isn't occupied by another enemy that's not yet in view
				var availableColumns = [];
				for (var col = midPoint - 3; col < midPoint + 3; col++) {
					var columnOccupied = false;
					// Check if any enemy is already in this column but not yet in view
					for (var e = 0; e < enemies.length; e++) {
						if (enemies[e].cellX === col && enemies[e].currentCellY < 4) {
							columnOccupied = true;
							break;
						}
					}
					if (!columnOccupied) {
						availableColumns.push(col);
					}
				}
				// If all columns are occupied, use original random method
				var spawnX;
				if (availableColumns.length > 0) {
					// Choose a random unoccupied column
					spawnX = availableColumns[Math.floor(Math.random() * availableColumns.length)];
				} else {
					// Fallback to random if all columns are occupied
					spawnX = midPoint - 3 + Math.floor(Math.random() * 6); // x from 9 to 14
				}
				var spawnY = -1 - Math.random() * 5; // Random distance above the grid for spreading
				enemy.cellX = spawnX;
				enemy.cellY = 5; // Position after entry
				enemy.currentCellX = spawnX;
				enemy.currentCellY = spawnY;
				enemy.waveNumber = currentWave;
				enemies.push(enemy);
			}
		}
		var currentWaveEnemiesRemaining = false;
		for (var i = 0; i < enemies.length; i++) {
			if (enemies[i].waveNumber === currentWave) {
				currentWaveEnemiesRemaining = true;
				break;
			}
		}
		if (waveSpawned && !currentWaveEnemiesRemaining) {
			waveInProgress = false;
			waveSpawned = false;
		}
	}
	for (var a = enemies.length - 1; a >= 0; a--) {
		var enemy = enemies[a];
		if (enemy.health <= 0) {
			for (var i = 0; i < enemy.bulletsTargetingThis.length; i++) {
				var bullet = enemy.bulletsTargetingThis[i];
				bullet.targetEnemy = null;
			}
			// Play enemy death sound effect
			LK.getSound('enemy_death').play();
			// Play gold earning sound effect
			LK.getSound('gold_earn').play();
			// Create death animation before cleanup
			var deathAnimation = function deathAnimation() {
				// Stop any ongoing rotation tweens on the enemy
				if (enemy.children[0]) {
					tween.stop(enemy.children[0], {
						rotation: true
					});
				}
				// Stop any movement tweens
				tween.stop(enemy, {
					x: true,
					y: true
				});
				// Fade out and implode effect
				tween(enemy, {
					alpha: 0,
					scaleX: 0.3,
					scaleY: 0.3
				}, {
					duration: 300,
					easing: tween.easeIn,
					onFinish: function onFinish() {
						// Clean up shadow if it's a flying enemy
						if (enemy.isFlying && enemy.shadow) {
							enemyLayerMiddle.removeChild(enemy.shadow);
							enemy.shadow = null;
						}
						// Remove enemy from the appropriate layer
						if (enemy.isFlying) {
							enemyLayerTop.removeChild(enemy);
						} else {
							enemyLayerBottom.removeChild(enemy);
						}
					}
				});
				// Add a burst of particles effect with a quick expanding circle
				var burstEffect = new Container();
				game.addChild(burstEffect);
				burstEffect.x = enemy.x;
				burstEffect.y = enemy.y;
				var burstGraphics = burstEffect.attachAsset('rangeCircle', {
					anchorX: 0.5,
					anchorY: 0.5
				});
				burstGraphics.width = burstGraphics.height = CELL_SIZE * 0.3;
				burstGraphics.tint = 0xFF6B35;
				burstGraphics.alpha = 0.8;
				tween(burstEffect, {
					alpha: 0,
					scaleX: 2.5,
					scaleY: 2.5
				}, {
					duration: 400,
					easing: tween.easeOut,
					onFinish: function onFinish() {
						burstEffect.destroy();
					}
				});
			};
			// Execute death animation
			deathAnimation();
			// Boss enemies give more gold and score
			var goldEarned = enemy.isBoss ? Math.floor(50 + (enemy.waveNumber - 1) * 5) : Math.floor(1 + (enemy.waveNumber - 1) * 0.5);
			var goldIndicator = new GoldIndicator(goldEarned, enemy.x, enemy.y);
			game.addChild(goldIndicator);
			// Create gold collection particle effect
			var goldCollectionEffect = new Container();
			game.addChild(goldCollectionEffect);
			goldCollectionEffect.x = enemy.x;
			goldCollectionEffect.y = enemy.y;
			// Create sparkling particles that move toward gold counter
			for (var sparkle = 0; sparkle < 6; sparkle++) {
				var sparkleContainer = new Container();
				goldCollectionEffect.addChild(sparkleContainer);
				var sparkleGraphics = sparkleContainer.attachAsset('rangeCircle', {
					anchorX: 0.5,
					anchorY: 0.5
				});
				sparkleGraphics.width = sparkleGraphics.height = 6 + Math.random() * 8;
				sparkleGraphics.tint = 0xFFD700; // Golden color
				sparkleGraphics.alpha = 1;
				// Initial random spread
				var initialAngle = Math.random() * Math.PI * 2;
				var initialDistance = Math.random() * 25;
				sparkleContainer.x = Math.cos(initialAngle) * initialDistance;
				sparkleContainer.y = Math.sin(initialAngle) * initialDistance;
				// Calculate target position (gold counter position)
				var targetX = -spacing - goldCollectionEffect.x; // Relative to gold counter
				var targetY = topMargin - goldCollectionEffect.y;
				// First phase: sparkle outward briefly
				tween(sparkleContainer, {
					x: sparkleContainer.x * 1.5,
					y: sparkleContainer.y * 1.5,
					scaleX: 1.2,
					scaleY: 1.2
				}, {
					duration: 150,
					easing: tween.easeOut,
					onFinish: function onFinish() {
						// Second phase: fly toward gold counter
						tween(sparkleContainer, {
							x: targetX + (Math.random() - 0.5) * 40,
							y: targetY + (Math.random() - 0.5) * 20,
							alpha: 0,
							scaleX: 0.3,
							scaleY: 0.3
						}, {
							duration: 800 + Math.random() * 300,
							easing: tween.easeIn
						});
					}
				});
			}
			// Clean up gold collection effect after animation
			LK.setTimeout(function () {
				goldCollectionEffect.destroy();
			}, 1200);
			setGold(gold + goldEarned);
			// Give more score for defeating a boss
			var scoreValue = enemy.isBoss ? 100 : 5;
			score += scoreValue;
			// Add a notification for boss defeat
			if (enemy.isBoss) {
				var notification = game.addChild(new Notification("Boss defeated! +" + goldEarned + " gold!"));
				notification.x = 2048 / 2;
				notification.y = grid.height - 150;
			}
			updateUI();
			enemies.splice(a, 1);
			continue;
		}
		if (grid.updateEnemy(enemy)) {
			// Clean up shadow if it's a flying enemy
			if (enemy.isFlying && enemy.shadow) {
				enemyLayerMiddle.removeChild(enemy.shadow);
				enemy.shadow = null;
			}
			// Remove enemy from the appropriate layer
			if (enemy.isFlying) {
				enemyLayerTop.removeChild(enemy);
			} else {
				enemyLayerBottom.removeChild(enemy);
			}
			enemies.splice(a, 1);
			lives = Math.max(0, lives - 1);
			updateUI();
			if (lives <= 0) {
				LK.getSound('game_over').play();
				LK.showGameOver();
			}
		}
	}
	for (var i = bullets.length - 1; i >= 0; i--) {
		if (!bullets[i].parent) {
			if (bullets[i].targetEnemy) {
				var targetEnemy = bullets[i].targetEnemy;
				var bulletIndex = targetEnemy.bulletsTargetingThis.indexOf(bullets[i]);
				if (bulletIndex !== -1) {
					targetEnemy.bulletsTargetingThis.splice(bulletIndex, 1);
				}
			}
			bullets.splice(i, 1);
		}
	}
	if (towerPreview.visible) {
		towerPreview.checkPlacement();
	}
	// Update environmental elements
	for (var i = 0; i < clouds.length; i++) {
		clouds[i].update();
	}
	// Update wave preview panel
	if (waveIndicator.gameStarted && currentWave < totalWaves) {
		wavePreviewPanel.updatePreview(currentWave + 1);
	} else {
		wavePreviewPanel.visible = false;
	}
	if (currentWave >= totalWaves && enemies.length === 0 && !waveInProgress) {
		LK.showYouWin();
	}
};
 a turtle with a laser on her shell, pixel art. In-Game asset. 2d. High contrast. No shadows
 a turtle with a lightning bolt gun on her shell, pixelart. In-Game asset. 2d. High contrast. No shadows
 a turtle with a canon on her shell, pixelart. In-Game asset. 2d. High contrast. No shadows
 a turtle having a misile on her shell, pixelart. In-Game asset. 2d. High contrast. No shadows
 a turtle with an ice gun on her shell, pixelart. In-Game asset. 2d. High contrast. No shadows
 a turtle with a tesla tower on her shell, pixelart. In-Game asset. 2d. High contrast. No shadows
 seagull, pixelart, walking down. In-Game asset. 2d. High contrast. No shadows
 seagull looking to the right, pixelart, flying. In-Game asset. 2d. High contrast. No shadows
 seagull looking to the right, pixelart, running. In-Game asset. 2d. High contrast. No shadows
 very big seagull looking to the right, pixelart, walking. In-Game asset. 2d. High contrast. No shadows
 palm tree, pixelart. In-Game asset. 2d. High contrast. No shadows
 rock, pixelart. In-Game asset. 2d. High contrast. No shadows
 seashell, pixelart. In-Game asset. 2d. High contrast. No shadows
laser_shoot
Sound effect
ice_shoot
Sound effect
missile_shoot
Sound effect
lightning_shoot
Sound effect
cannon_shoot
Sound effect
tesla_shoot
Sound effect
enemy_hit
Sound effect
wave_alert
Sound effect
enemy_death
Sound effect
game_music
Music
game_over
Sound effect
ui_click
Sound effect
ui_select
Sound effect
gold_earn
Sound effect