/**** 
* Plugins
****/ 
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
	unlockedWorlds: 1,
	completedWaves: 0,
	digiviceC: false,
	digiviceB: false,
	digiviceA: false,
	worldLevels: {},
	language: "en",
	securityPoints: 0
});
/**** 
* 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 bulletGraphics = self.attachAsset('bullet', {
		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;
			if (self.targetEnemy.health <= 0) {
				self.targetEnemy.health = 0;
			} else {
				self.targetEnemy.healthBar.width = self.targetEnemy.health / self.targetEnemy.maxHealth * 70;
			}
			// 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);
				// Play splash attack sound
				LK.getSound('splashAttack').play();
				// 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);
					// Play freeze effect sound for slow attacks
					LK.getSound('freezeEffect').play();
					// 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);
					// Play poison effect sound
					LK.getSound('poisonEffect').play();
					// 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);
			}
			// Special Digimon evolutionary line effects
			if (self.type === 'agumon' && self.burnChance) {
				// Agumon line: Fire/burn effects
				if (!self.targetEnemy.isImmune && Math.random() < self.burnChance) {
					self.targetEnemy.burning = true;
					self.targetEnemy.burnDamage = self.damage * 0.15;
					self.targetEnemy.burnDuration = 240; // 4 seconds
					var burnEffect = new EffectIndicator(self.targetEnemy.x, self.targetEnemy.y, 'burn');
					game.addChild(burnEffect);
					// Play burn effect sound
					LK.getSound('burnEffect').play();
				}
			} else if (self.type === 'gabumon' && self.freezeChance) {
				// Gabumon line: Ice/freeze effects
				if (!self.targetEnemy.isImmune && Math.random() < self.freezeChance) {
					self.targetEnemy.frozen = true;
					self.targetEnemy.frozenDuration = 120; // 2 seconds
					if (!self.targetEnemy.originalSpeed) {
						self.targetEnemy.originalSpeed = self.targetEnemy.speed;
					}
					self.targetEnemy.speed = 0; // Completely frozen
					var freezeEffect = new EffectIndicator(self.targetEnemy.x, self.targetEnemy.y, 'freeze');
					game.addChild(freezeEffect);
					// Play freeze effect sound
					LK.getSound('freezeEffect').play();
				}
			} else if (self.type === 'tentomon' && self.paralyzeChance) {
				// Tentomon line: Electric paralysis affecting multiple enemies
				if (!self.targetEnemy.isImmune && Math.random() < self.paralyzeChance) {
					// Paralyze main target
					self.targetEnemy.paralyzed = true;
					self.targetEnemy.paralyzeDuration = 180; // 3 seconds
					if (!self.targetEnemy.originalSpeed) {
						self.targetEnemy.originalSpeed = self.targetEnemy.speed;
					}
					self.targetEnemy.speed *= 0.1; // Nearly stopped
					var paralyzeEffect = new EffectIndicator(self.targetEnemy.x, self.targetEnemy.y, 'paralyze');
					game.addChild(paralyzeEffect);
					// Spread paralysis to nearby enemies
					for (var i = 0; i < enemies.length; i++) {
						var nearbyEnemy = enemies[i];
						if (nearbyEnemy !== self.targetEnemy && !nearbyEnemy.isImmune) {
							var dx = nearbyEnemy.x - self.targetEnemy.x;
							var dy = nearbyEnemy.y - self.targetEnemy.y;
							var distance = Math.sqrt(dx * dx + dy * dy);
							if (distance <= self.paralyzeArea) {
								nearbyEnemy.paralyzed = true;
								nearbyEnemy.paralyzeDuration = 120; // Shorter for spread effect
								if (!nearbyEnemy.originalSpeed) {
									nearbyEnemy.originalSpeed = nearbyEnemy.speed;
								}
								nearbyEnemy.speed *= 0.3;
								var spreadEffect = new EffectIndicator(nearbyEnemy.x, nearbyEnemy.y, 'paralyze');
								game.addChild(spreadEffect);
							}
						}
					}
				}
			} else if (self.type === 'palmon' && self.poisonSpread) {
				// Palmon line: Spreading poison effects
				if (!self.targetEnemy.isImmune) {
					// Apply poison to main target
					self.targetEnemy.poisoned = true;
					self.targetEnemy.poisonDamage = self.damage * 0.25;
					self.targetEnemy.poisonDuration = 360; // 6 seconds
					var poisonEffect = new EffectIndicator(self.targetEnemy.x, self.targetEnemy.y, 'poison');
					game.addChild(poisonEffect);
					// Spread poison to nearby enemies with a chance
					for (var i = 0; i < enemies.length; i++) {
						var nearbyEnemy = enemies[i];
						if (nearbyEnemy !== self.targetEnemy && !nearbyEnemy.isImmune) {
							var dx = nearbyEnemy.x - self.targetEnemy.x;
							var dy = nearbyEnemy.y - self.targetEnemy.y;
							var distance = Math.sqrt(dx * dx + dy * dy);
							if (distance <= self.poisonRadius) {
								// Use poisonSpreadChance if defined, otherwise default to 30%
								var spreadChance = typeof self.poisonSpreadChance === "number" ? self.poisonSpreadChance : 0.3;
								if (Math.random() < spreadChance) {
									nearbyEnemy.poisoned = true;
									nearbyEnemy.poisonDamage = self.damage * 0.15; // Weaker spread poison
									nearbyEnemy.poisonDuration = 240;
									var spreadPoisonEffect = new EffectIndicator(nearbyEnemy.x, nearbyEnemy.y, 'poison');
									game.addChild(spreadPoisonEffect);
								}
							}
						}
					}
				}
			} else if (self.type === 'gomamon' && self.moistureEffect) {
				// Gomamon line: Moisture effects that affect fire/ice interactions
				if (!self.targetEnemy.isImmune) {
					self.targetEnemy.moist = true;
					self.targetEnemy.moistDuration = self.moistureDuration;
					self.targetEnemy.fireResistance = 0.5; // Reduced fire damage
					self.targetEnemy.freezeVulnerability = 1.5; // Increased freeze chance
					var moistEffect = new EffectIndicator(self.targetEnemy.x, self.targetEnemy.y, 'moist');
					game.addChild(moistEffect);
				}
			}
			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 Coin = Container.expand(function (x, y, value) {
	var self = Container.call(this);
	self.value = value || 5;
	self.x = x;
	self.y = y;
	self.collected = false;
	self.walkSpeed = 0.5;
	self.direction = Math.random() * Math.PI * 2;
	self.changeDirectionTimer = 0;
	var coinGraphics = self.attachAsset('fairy', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	coinGraphics.width = 50;
	coinGraphics.height = 55;
	coinGraphics.tint = 0xFFD700;
	// Add fluttering animation
	var _flutterAnimation = function flutterAnimation() {
		tween(coinGraphics, {
			scaleY: 1.2,
			y: -8
		}, {
			duration: 800,
			easing: tween.easeInOut,
			onFinish: function onFinish() {
				tween(coinGraphics, {
					scaleY: 1.0,
					y: 0
				}, {
					duration: 800,
					easing: tween.easeInOut,
					onFinish: _flutterAnimation
				});
			}
		});
	};
	_flutterAnimation();
	// Add gentle rotation for fairy-like movement
	var _rotateAnimation = function rotateAnimation() {
		tween(coinGraphics, {
			rotation: coinGraphics.rotation + 0.3
		}, {
			duration: 1000,
			easing: tween.easeInOut,
			onFinish: function onFinish() {
				tween(coinGraphics, {
					rotation: coinGraphics.rotation - 0.6
				}, {
					duration: 2000,
					easing: tween.easeInOut,
					onFinish: function onFinish() {
						tween(coinGraphics, {
							rotation: coinGraphics.rotation + 0.3
						}, {
							duration: 1000,
							easing: tween.easeInOut,
							onFinish: _rotateAnimation
						});
					}
				});
			}
		});
	};
	_rotateAnimation();
	// Add coin value text
	var valueText = new Text2(self.value.toString(), {
		size: 20,
		fill: 0x000000,
		weight: 800
	});
	valueText.anchor.set(0.5, 0.5);
	self.addChild(valueText);
	self.update = function () {
		if (self.collected) return;
		// Change direction occasionally
		self.changeDirectionTimer++;
		if (self.changeDirectionTimer > 120) {
			// Change direction every 2 seconds
			self.direction = Math.random() * Math.PI * 2;
			self.changeDirectionTimer = 0;
		}
		// Move in current direction with more fairy-like floating movement
		var newX = self.x + Math.cos(self.direction) * self.walkSpeed;
		var newY = self.y + Math.sin(self.direction) * self.walkSpeed * 0.7; // Slower vertical movement
		// Add slight vertical bobbing for fairy effect
		newY += Math.sin(LK.ticks * 0.05) * 0.3;
		// Keep within playable bounds
		var minX = grid.x + CELL_SIZE;
		var maxX = grid.x + grid.cells.length * CELL_SIZE - CELL_SIZE;
		var minY = grid.y + CELL_SIZE;
		var maxY = grid.y + grid.cells[0].length * CELL_SIZE - CELL_SIZE;
		if (newX < minX || newX > maxX) {
			self.direction = Math.PI - self.direction; // Bounce horizontally
		} else {
			self.x = newX;
		}
		if (newY < minY || newY > maxY) {
			self.direction = -self.direction; // Bounce vertically
		} else {
			self.y = newY;
		}
		// Check if player clicks on coin
	};
	self.down = function () {
		if (!self.collected) {
			self.collected = true;
			setGold(gold + self.value);
			// Add 10 security score points
			score += 10;
			// Save security points to storage
			storage.securityPoints = score;
			updateUI();
			// Play coin collect sound
			LK.getSound('coinCollect').play();
			var goldIndicator = new GoldIndicator(self.value, self.x, self.y);
			game.addChild(goldIndicator);
			// Remove coin from coins array
			var coinIndex = coins.indexOf(self);
			if (coinIndex !== -1) {
				coins.splice(coinIndex, 1);
			}
			self.destroy();
		}
	};
	return self;
});
var ComicPanel = Container.expand(function (imagePath, text) {
	var self = Container.call(this);
	// Panel background - larger panel
	var panelBg = self.attachAsset('notification', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	panelBg.width = 700;
	panelBg.height = 500;
	panelBg.tint = 0x222222;
	panelBg.alpha = 0.9;
	// Add border effect
	var border = self.attachAsset('notification', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	border.width = 710;
	border.height = 510;
	border.tint = 0x000000;
	border.alpha = 0.8;
	self.addChildAt(border, 0);
	// Text area - larger and repositioned
	var textBg = self.attachAsset('notification', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	textBg.width = 660;
	textBg.height = 160;
	textBg.tint = 0xffffff;
	textBg.alpha = 0.95;
	textBg.y = 170;
	self.addChild(textBg);
	// Character image if provided - repositioned
	if (imagePath) {
		// Starting from glacier world (world 3), use Digimon ally images
		var shouldUseDigimonImages = currentWorld >= 3;
		var characterImage;
		if (shouldUseDigimonImages && imagePath) {
			// Use actual Digimon asset images for glacier world and beyond
			characterImage = self.attachAsset(imagePath, {
				anchorX: 0.5,
				anchorY: 0.5
			});
		} else {
			// Use default notification asset for forest and desert worlds
			characterImage = self.attachAsset('notification', {
				anchorX: 0.5,
				anchorY: 0.5
			});
		}
		characterImage.width = 200;
		characterImage.height = 200;
		characterImage.y = -80;
		self.addChild(characterImage);
	}
	// Story text - improved sizing and positioning
	var storyText = new Text2(text, {
		size: 28,
		fill: 0xFFFFFF,
		stroke: 0x000000,
		strokeThickness: 4,
		weight: 800
	});
	storyText.anchor.set(0.5, 0.5);
	storyText.y = 170;
	storyText.wordWrap = true;
	storyText.wordWrapWidth = 600;
	storyText.maxWidth = 600;
	self.addChild(storyText);
	// Add Next button for navigation
	var nextButton = new Container();
	var nextButtonBg = nextButton.attachAsset('notification', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	nextButtonBg.width = 150;
	nextButtonBg.height = 60;
	nextButtonBg.tint = 0x00AA00;
	var nextButtonText = new Text2('Next', {
		size: 40,
		fill: 0xFFFFFF,
		stroke: 0x000000,
		strokeThickness: 2,
		weight: 800
	});
	nextButtonText.anchor.set(0.5, 0.5);
	nextButton.addChild(nextButtonText);
	nextButton.x = 0;
	nextButton.y = 280;
	self.addChild(nextButton);
	// Store reference to next button for external access
	self.nextButton = nextButton;
	// Scale animation entrance
	self.scaleX = 0.3;
	self.scaleY = 0.3;
	self.alpha = 0;
	self.show = function () {
		tween(self, {
			scaleX: 1,
			scaleY: 1,
			alpha: 1
		}, {
			duration: 400,
			easing: tween.backOut
		});
	};
	self.hide = function (callback) {
		tween(self, {
			scaleX: 0.8,
			scaleY: 0.8,
			alpha: 0
		}, {
			duration: 300,
			easing: tween.easeIn,
			onFinish: callback
		});
	};
	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 = [];
	// Removed number label to improve performance
	// Numbers were causing lag due to text rendering overhead
	self.update = function () {};
	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());
		}
	};
	self.render = function (data) {
		// Renderizado visual desactivado para evitar interferencia con tiles del mundo
		// Solo mantiene la lógica de pathfinding sin elementos visuales
		// Los tiles del mundo se renderizan a través de WorldRenderer
		// Actualizar las flechas solo si hay una torre seleccionada para debug
		if (selectedTower && data.towersInRange && data.towersInRange.indexOf(selectedTower) !== -1) {
			// Mostrar flechas solo para torres seleccionadas
			while (debugArrows.length > data.targets.length) {
				self.removeChild(debugArrows.pop());
			}
			for (var a = 0; a < data.targets.length; a++) {
				var destination = data.targets[a];
				var ox = destination.x - data.x;
				var oy = destination.y - data.y;
				var angle = Math.atan2(oy, ox);
				if (!debugArrows[a]) {
					debugArrows[a] = LK.getAsset('arrow', {
						anchorX: -.5,
						anchorY: 0.5
					});
					debugArrows[a].alpha = .5;
					self.addChildAt(debugArrows[a], 1);
				}
				debugArrows[a].rotation = angle;
			}
		} else {
			// Remover flechas si no hay torre seleccionada
			self.removeArrows();
		}
		// Hacer el gráfico de celda invisible para no interferir con tiles
		cellGraphics.alpha = 0;
	};
});
var DigimonShop = Container.expand(function () {
	var self = Container.call(this);
	self.visible = false;
	self.y = 2732;
	var shopBackground = self.attachAsset('notification', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	shopBackground.width = 2048;
	shopBackground.height = 600;
	shopBackground.tint = 0x222222;
	shopBackground.alpha = 0.95;
	var titleText = new Text2('Digimon Firewall Shop', {
		size: 80,
		fill: 0xFFFFFF,
		weight: 800
	});
	titleText.anchor.set(0.5, 0.5);
	titleText.y = -250;
	self.addChild(titleText);
	// Create shop items container
	var itemsContainer = new Container();
	itemsContainer.y = -50;
	self.addChild(itemsContainer);
	// Digivice items data
	var digiviceItems = [{
		id: 'digiviceC',
		name: 'Digivice C',
		cost: 500,
		description: 'Unlocks Champion Level'
	}, {
		id: 'digiviceB',
		name: 'Digivice B',
		cost: 2000,
		description: 'Unlocks Ultimate Level'
	}, {
		id: 'digiviceA',
		name: 'Digivice A',
		cost: 8000,
		description: 'Unlocks Mega Level'
	}];
	// Create shop item buttons
	for (var i = 0; i < digiviceItems.length; i++) {
		var item = digiviceItems[i];
		var itemButton = new Container();
		itemButton.x = -600 + i * 400;
		itemButton.y = 0;
		itemsContainer.addChild(itemButton);
		var itemBg = itemButton.attachAsset('notification', {
			anchorX: 0.5,
			anchorY: 0.5
		});
		itemBg.width = 350;
		itemBg.height = 200;
		var itemNameText = new Text2(item.name, {
			size: 50,
			fill: 0xFFFFFF,
			weight: 800
		});
		itemNameText.anchor.set(0.5, 0.5);
		itemNameText.y = -50;
		itemButton.addChild(itemNameText);
		var itemDescText = new Text2(item.description, {
			size: 35,
			fill: 0xCCCCCC,
			weight: 400
		});
		itemDescText.anchor.set(0.5, 0.5);
		itemDescText.y = -10;
		itemButton.addChild(itemDescText);
		var itemCostText = new Text2(item.cost + ' points', {
			size: 45,
			fill: 0xFFD700,
			weight: 800
		});
		itemCostText.anchor.set(0.5, 0.5);
		itemCostText.y = 40;
		itemButton.addChild(itemCostText);
		// Create purchase functionality
		(function (itemData, button, background, costText) {
			button.update = function () {
				var owned = storage[itemData.id] || false;
				var canAfford = score >= itemData.cost;
				if (owned) {
					background.tint = 0x00AA00;
					costText.setText('OWNED');
					button.alpha = 0.7;
				} else if (canAfford) {
					background.tint = 0x4444FF;
					costText.setText(itemData.cost + ' points');
					button.alpha = 1.0;
				} else {
					background.tint = 0x666666;
					costText.setText(itemData.cost + ' points');
					button.alpha = 0.5;
				}
			};
			button.down = function () {
				var owned = storage[itemData.id] || false;
				var canAfford = score >= itemData.cost;
				if (owned) {
					var notification = game.addChild(new Notification("You already own " + itemData.name + "!"));
					notification.x = 2048 / 2;
					notification.y = grid.height - 50;
				} else if (canAfford) {
					score -= itemData.cost;
					updateUI();
					storage[itemData.id] = true;
					var notification = game.addChild(new Notification("Purchased " + itemData.name + "!"));
					notification.x = 2048 / 2;
					notification.y = grid.height - 50;
				} else {
					var notification = game.addChild(new Notification("Not enough security points for " + itemData.name + "!"));
					notification.x = 2048 / 2;
					notification.y = grid.height - 50;
				}
			};
		})(item, itemButton, itemBg, itemCostText);
	}
	var closeButton = new Container();
	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 = shopBackground.width / 2 - 57;
	closeButton.y = -shopBackground.height / 2 + 57;
	self.addChild(closeButton);
	closeButton.down = function () {
		self.hide();
	};
	self.show = function () {
		self.visible = true;
		tween(self, {
			y: 2732 - 300
		}, {
			duration: 300,
			easing: tween.backOut
		});
	};
	self.hide = function () {
		tween(self, {
			y: 2732
		}, {
			duration: 200,
			easing: tween.easeIn,
			onFinish: function onFinish() {
				self.visible = false;
			}
		});
	};
	return self;
});
var DigimonSummonMenu = Container.expand(function () {
	var self = Container.call(this);
	self.visible = false;
	self.y = 2732;
	var menuBackground = self.attachAsset('notification', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	menuBackground.width = 2048;
	menuBackground.height = 600;
	menuBackground.tint = 0x222222;
	menuBackground.alpha = 0.95;
	var titleText = new Text2('Summon Digimon Allies', {
		size: 80,
		fill: 0xFFFFFF,
		weight: 800
	});
	titleText.anchor.set(0.5, 0.5);
	titleText.y = -250;
	self.addChild(titleText);
	// Create summon items container
	var itemsContainer = new Container();
	itemsContainer.y = -50;
	self.addChild(itemsContainer);
	// Available Digimon based on unlocked Digivices
	function getAvailableDigimon() {
		// Todas las líneas evolutivas principales completas
		var available = [
		// Agumon line - COMPLETE
		{
			id: 'agumon',
			name: 'Agumon',
			description: 'Rookie Fire Dragon',
			reqDigivice: null
		}, {
			id: 'greymon',
			name: 'Greymon',
			description: 'Champion Fire Dragon',
			reqDigivice: 'digiviceC',
			reqBaseTower: 'agumon'
		}, {
			id: 'metalgreymon',
			name: 'MetalGreymon',
			description: 'Ultimate Cyborg Dragon',
			reqDigivice: 'digiviceB',
			reqBaseTower: 'agumon'
		}, {
			id: 'wargreymon',
			name: 'WarGreymon',
			description: 'Mega Dragon Warrior',
			reqDigivice: 'digiviceA',
			reqBaseTower: 'agumon'
		},
		// Gabumon line - COMPLETE
		{
			id: 'gabumon',
			name: 'Gabumon',
			description: 'Rookie Ranged Wolf',
			reqDigivice: null
		}, {
			id: 'garurumon',
			name: 'Garurumon',
			description: 'Champion Ice Wolf',
			reqDigivice: 'digiviceC',
			reqBaseTower: 'gabumon'
		}, {
			id: 'weregarurumon',
			name: 'WereGarurumon',
			description: 'Ultimate Beast Warrior',
			reqDigivice: 'digiviceB',
			reqBaseTower: 'gabumon'
		}, {
			id: 'metalgarurumon',
			name: 'MetalGarurumon',
			description: 'Mega Ice Wolf',
			reqDigivice: 'digiviceA',
			reqBaseTower: 'gabumon'
		},
		// Tentomon line - COMPLETE
		{
			id: 'tentomon',
			name: 'Tentomon',
			description: 'Rookie Electric Insect',
			reqDigivice: null
		}, {
			id: 'kabuterimon',
			name: 'Kabuterimon',
			description: 'Champion Electric Insect',
			reqDigivice: 'digiviceC',
			reqBaseTower: 'tentomon'
		}, {
			id: 'megakabuterimon',
			name: 'MegaKabuterimon',
			description: 'Ultimate Giant Insect',
			reqDigivice: 'digiviceB',
			reqBaseTower: 'tentomon'
		}, {
			id: 'herculeskabuterimon',
			name: 'HerculesKabuterimon',
			description: 'Mega Hercules Insect',
			reqDigivice: 'digiviceA',
			reqBaseTower: 'tentomon'
		},
		// Palmon line - COMPLETE
		{
			id: 'palmon',
			name: 'Palmon',
			description: 'Rookie Plant Fighter',
			reqDigivice: null
		}, {
			id: 'togemon',
			name: 'Togemon',
			description: 'Champion Cactus',
			reqDigivice: 'digiviceC',
			reqBaseTower: 'palmon'
		}, {
			id: 'lillymon',
			name: 'Lillymon',
			description: 'Ultimate Flower Fairy',
			reqDigivice: 'digiviceB',
			reqBaseTower: 'palmon'
		}, {
			id: 'rosemon',
			name: 'Rosemon',
			description: 'Mega Rose Queen',
			reqDigivice: 'digiviceA',
			reqBaseTower: 'palmon'
		},
		// Gomamon line - COMPLETE
		{
			id: 'gomamon',
			name: 'Gomamon',
			description: 'Rookie Aquatic Mammal',
			reqDigivice: null
		}, {
			id: 'ikkakumon',
			name: 'Ikkakumon',
			description: 'Champion Sea Beast',
			reqDigivice: 'digiviceC',
			reqBaseTower: 'gomamon'
		}, {
			id: 'zudomon',
			name: 'Zudomon',
			description: 'Ultimate Hammer Warrior',
			reqDigivice: 'digiviceB',
			reqBaseTower: 'gomamon'
		}, {
			id: 'vikemon',
			name: 'Vikemon',
			description: 'Mega Viking Beast',
			reqDigivice: 'digiviceA',
			reqBaseTower: 'gomamon'
		},
		// Patamon line - COMPLETE
		{
			id: 'patamon',
			name: 'Patamon',
			description: 'Rookie Angel',
			reqDigivice: null
		}, {
			id: 'angemon',
			name: 'Angemon',
			description: 'Champion Angel',
			reqDigivice: 'digiviceC',
			reqBaseTower: 'patamon'
		}, {
			id: 'magnaangemon',
			name: 'MagnaAngemon',
			description: 'Ultimate Holy Angel',
			reqDigivice: 'digiviceB',
			reqBaseTower: 'patamon'
		}, {
			id: 'seraphimon',
			name: 'Seraphimon',
			description: 'Mega Seraph',
			reqDigivice: 'digiviceA',
			reqBaseTower: 'patamon'
		},
		// UlforceVeedramon - FINAL LEVEL UNLOCK
		{
			id: 'UlforceVeedramon',
			name: 'UlforceVeedramon',
			description: 'Ultimate Speed Dragon',
			reqDigivice: 'digiviceA',
			reqBaseTower: null // No base tower required
		}];
		return available;
	}
	function updateDigimonButtons() {
		// Clear existing buttons
		while (itemsContainer.children.length > 0) {
			itemsContainer.removeChild(itemsContainer.children[0]);
		}
		// Show ALL Digimon, but disable buttons if requirements not met
		var allDigimon = getAvailableDigimon();
		// If a filterLevel is set (from C, B, or A button), filter Digimon by evolution level
		var filterLevel = self._filterLevel;
		if (filterLevel) {
			// Map Digimon id to evolution level
			var digimonLevelMap = {
				koromon: "rookie",
				tsunomon: "rookie",
				greymon: "champion",
				garurumon: "champion",
				kabuterimon: "champion",
				metalgreymon: "ultimate",
				weregarurumon: "ultimate",
				megakabuterimon: "ultimate",
				wargreymon: "mega"
			};
			allDigimon = allDigimon.filter(function (d) {
				return (digimonLevelMap[d.id] || "").toLowerCase() === filterLevel.toLowerCase();
			});
		}
		// If no Digimon to show, display message
		if (allDigimon.length === 0) {
			var noDigimonText = new Text2("No Digimon available for this level", {
				size: 40,
				fill: 0xFFFFFF,
				weight: 600
			});
			noDigimonText.anchor.set(0.5, 0.5);
			itemsContainer.addChild(noDigimonText);
			return;
		}
		// Layout: 3 per row, center horizontally
		var buttonsPerRow = Math.min(3, allDigimon.length);
		var buttonSpacing = 400;
		var startX = -((buttonsPerRow - 1) * buttonSpacing) / 2;
		for (var i = 0; i < allDigimon.length; i++) {
			var digimon = allDigimon[i];
			var row = Math.floor(i / buttonsPerRow);
			var col = i % buttonsPerRow;
			var digimonButton = new Container();
			digimonButton.x = startX + col * buttonSpacing;
			digimonButton.y = row * 180;
			itemsContainer.addChild(digimonButton);
			// Use correct asset for Digimon - Complete mapping for all evolutionary lines
			var digimonAssetMap = {
				// Agumon line - COMPLETE
				agumon: 'agumon',
				greymon: 'greymon',
				metalgreymon: 'metalgreymon',
				wargreymon: 'wargreymon',
				// Gabumon line - COMPLETE
				gabumon: 'gabumon',
				garurumon: 'garurumon',
				weregarurumon: 'weregarurumon',
				metalgarurumon: 'metalgarurumon',
				// Tentomon line - COMPLETE
				tentomon: 'tentomon',
				kabuterimon: 'kabuterimon',
				megakabuterimon: 'megakabuterimon',
				herculeskabuterimon: 'herculeskabuterimon',
				// Palmon line - COMPLETE
				palmon: 'palmon',
				togemon: 'togemon',
				lillymon: 'lillymon',
				rosemon: 'rosemon',
				// Gomamon line - COMPLETE
				gomamon: 'gomamon',
				ikkakumon: 'ikkakumon',
				zudomon: 'zudomon',
				vikemon: 'vikemon',
				// Patamon line - COMPLETE
				patamon: 'patamon',
				angemon: 'angemon',
				magnaangemon: 'magnaangemon',
				seraphimon: 'seraphimon',
				// UlforceVeedramon - Final unlock
				UlforceVeedramon: 'UlforceVeedramon'
			};
			var assetId = digimonAssetMap[digimon.id] || 'agumon';
			var digimonArt = digimonButton.attachAsset(assetId, {
				anchorX: 0.5,
				anchorY: 0.5
			});
			digimonArt.width = 90;
			digimonArt.height = 90;
			digimonArt.y = -60;
			var buttonBg = digimonButton.attachAsset('notification', {
				anchorX: 0.5,
				anchorY: 0.5
			});
			buttonBg.width = 350;
			buttonBg.height = 160;
			var nameText = new Text2(digimon.name, {
				size: 45,
				fill: 0xFFFFFF,
				weight: 800
			});
			nameText.anchor.set(0.5, 0.5);
			nameText.y = -40;
			digimonButton.addChild(nameText);
			var descText = new Text2(digimon.description, {
				size: 28,
				fill: 0xCCCCCC,
				weight: 400
			});
			descText.anchor.set(0.5, 0.5);
			descText.y = -10;
			digimonButton.addChild(descText);
			var costText = new Text2('', {
				size: 35,
				fill: 0xFFD700,
				weight: 800
			});
			costText.anchor.set(0.5, 0.5);
			costText.y = 30;
			digimonButton.addChild(costText);
			// Show reason if not available
			var reasonText = null;
			(function (digimonData, button, background, costText) {
				button.update = function () {
					var hasSpace = alliedUnits.length < maxAlliedUnits;
					// Calculate cooldown based on Digivice requirement
					var requiredCooldown = summonCooldown; // Default fallback
					if (digimonData.reqDigivice === 'digiviceC') {
						requiredCooldown = 50 * 60; // 50 seconds at 60 FPS
					} else if (digimonData.reqDigivice === 'digiviceB') {
						requiredCooldown = 100 * 60; // 1 minute 40 seconds at 60 FPS
					} else if (digimonData.reqDigivice === 'digiviceA') {
						requiredCooldown = 130 * 60; // 2 minutes 10 seconds at 60 FPS
					}
					var isAvailable = !summonCooldown || LK.ticks - lastSummonTime > requiredCooldown;
					var hasDigivice = !digimonData.reqDigivice || storage[digimonData.reqDigivice];
					// Check for base tower requirement
					var hasBaseTower = true;
					if (digimonData.reqBaseTower) {
						hasBaseTower = false;
						for (var i = 0; i < towers.length; i++) {
							if (towers[i].id === digimonData.reqBaseTower) {
								hasBaseTower = true;
								break;
							}
						}
					}
					// Special case: UlforceVeedramon only unlocks in final level (wave 60/level 10 of world 6)
					var isUlforceVeedramon = digimonData.id === 'UlforceVeedramon';
					var isFinalLevel = currentWave >= 60 && currentWorld >= 6;
					if (isUlforceVeedramon && !isFinalLevel) {
						hasBaseTower = false;
					}
					// Show disabled state and reason if not available
					if (!hasBaseTower) {
						background.tint = 0x333333;
						button.alpha = 0.5;
						if (!button._reasonText) {
							button._reasonText = new Text2("Requires " + digimonData.reqBaseTower.toUpperCase() + " tower in field", {
								size: 24,
								fill: 0xFF6600,
								weight: 800
							});
							button._reasonText.anchor.set(0.5, 0.5);
							button._reasonText.y = 60;
							button.addChild(button._reasonText);
						}
						costText.setText(''); // No cost shown
					} else if (!hasDigivice) {
						background.tint = 0x333333;
						button.alpha = 0.5;
						if (!button._reasonText) {
							button._reasonText = new Text2("Requires " + (digimonData.reqDigivice === "digiviceC" ? "Digivice C" : digimonData.reqDigivice === "digiviceB" ? "Digivice B" : "Digivice A"), {
								size: 24,
								fill: 0xFF6600,
								weight: 800
							});
							button._reasonText.anchor.set(0.5, 0.5);
							button._reasonText.y = 60;
							button.addChild(button._reasonText);
						}
						costText.setText('');
					} else if (!hasSpace) {
						background.tint = 0x666666;
						button.alpha = 0.7;
						if (!button._reasonText) {
							button._reasonText = new Text2("Max allies reached", {
								size: 24,
								fill: 0xFF6600,
								weight: 800
							});
							button._reasonText.anchor.set(0.5, 0.5);
							button._reasonText.y = 60;
							button.addChild(button._reasonText);
						}
						costText.setText('');
					} else if (!isAvailable) {
						background.tint = 0x666666;
						button.alpha = 0.7;
						// Calculate cooldown based on Digivice requirement
						var requiredCooldown = summonCooldown; // Default fallback
						if (digimonData.reqDigivice === 'digiviceC') {
							requiredCooldown = 50 * 60; // 50 seconds
						} else if (digimonData.reqDigivice === 'digiviceB') {
							requiredCooldown = 100 * 60; // 1 minute 40 seconds
						} else if (digimonData.reqDigivice === 'digiviceA') {
							requiredCooldown = 130 * 60; // 2 minutes 10 seconds
						}
						var remainingCooldown = Math.ceil((requiredCooldown - (LK.ticks - lastSummonTime)) / 60);
						if (!button._reasonText) {
							button._reasonText = new Text2("Cooldown: " + remainingCooldown + "s", {
								size: 24,
								fill: 0xFF6600,
								weight: 800
							});
							button._reasonText.anchor.set(0.5, 0.5);
							button._reasonText.y = 60;
							button.addChild(button._reasonText);
						} else {
							button._reasonText.setText("Cooldown: " + remainingCooldown + "s");
						}
						costText.setText('');
					} else {
						background.tint = 0x4444FF;
						button.alpha = 1.0;
						if (button._reasonText) {
							button.removeChild(button._reasonText);
							button._reasonText = null;
						}
						costText.setText('Ready!');
					}
				};
				button.down = function () {
					var hasSpace = alliedUnits.length < maxAlliedUnits;
					// Calculate cooldown based on Digivice requirement
					var requiredCooldown = summonCooldown; // Default fallback
					if (digimonData.reqDigivice === 'digiviceC') {
						requiredCooldown = 50 * 60; // 50 seconds at 60 FPS
					} else if (digimonData.reqDigivice === 'digiviceB') {
						requiredCooldown = 100 * 60; // 1 minute 40 seconds at 60 FPS
					} else if (digimonData.reqDigivice === 'digiviceA') {
						requiredCooldown = 130 * 60; // 2 minutes 10 seconds at 60 FPS
					}
					var isAvailable = !summonCooldown || LK.ticks - lastSummonTime > requiredCooldown;
					var hasDigivice = !digimonData.reqDigivice || storage[digimonData.reqDigivice];
					// Check for base tower requirement
					var hasBaseTower = true;
					if (digimonData.reqBaseTower) {
						hasBaseTower = false;
						for (var i = 0; i < towers.length; i++) {
							if (towers[i].id === digimonData.reqBaseTower) {
								hasBaseTower = true;
								break;
							}
						}
					}
					if (!hasBaseTower) {
						var notification = game.addChild(new Notification("Requires " + digimonData.reqBaseTower.toUpperCase() + " tower in field to summon " + digimonData.name + "!"));
						notification.x = 2048 / 2;
						notification.y = grid.height - 50;
					} else if (!hasDigivice) {
						var notification = game.addChild(new Notification("Requires " + (digimonData.reqDigivice === "digiviceC" ? "Digivice C" : digimonData.reqDigivice === "digiviceB" ? "Digivice B" : "Digivice A") + " to summon " + digimonData.name + "!"));
						notification.x = 2048 / 2;
						notification.y = grid.height - 50;
					} else if (!hasSpace) {
						var notification = game.addChild(new Notification("¡Límite de aliados alcanzado!"));
						notification.x = 2048 / 2;
						notification.y = grid.height - 50;
					} else if (!isAvailable) {
						// Calculate cooldown based on Digivice requirement
						var requiredCooldown = summonCooldown; // Default fallback
						if (digimonData.reqDigivice === 'digiviceC') {
							requiredCooldown = 50 * 60; // 50 seconds
						} else if (digimonData.reqDigivice === 'digiviceB') {
							requiredCooldown = 100 * 60; // 1 minute 40 seconds
						} else if (digimonData.reqDigivice === 'digiviceA') {
							requiredCooldown = 130 * 60; // 2 minutes 10 seconds
						}
						var remainingCooldown = Math.ceil((requiredCooldown - (LK.ticks - lastSummonTime)) / 60);
						var notification = game.addChild(new Notification("Summon cooldown: " + remainingCooldown + "s"));
						notification.x = 2048 / 2;
						notification.y = grid.height - 50;
					} else {
						// Summon the Digimon (no cost, just cooldown)
						var newUnit = new DigimonUnit(digimonData.id, 1);
						// Find corresponding base tower to spawn from
						var spawnTower = null;
						if (digimonData.reqBaseTower) {
							for (var i = 0; i < towers.length; i++) {
								if (towers[i].id === digimonData.reqBaseTower) {
									spawnTower = towers[i];
									break;
								}
							}
						}
						// Set spawn position - summoned Digimon spawn from exclusive allied spawn (spawntwo) y avanzan hacia la meta de aliados (goaltwo)
						if (grid.spawntwo && grid.spawntwo.length > 0) {
							var startSpawn = grid.spawntwo[Math.floor(Math.random() * grid.spawntwo.length)];
							newUnit.cellX = startSpawn.x;
							newUnit.cellY = startSpawn.y;
							newUnit.currentCellX = startSpawn.x;
							newUnit.currentCellY = startSpawn.y;
							newUnit.x = grid.x + newUnit.currentCellX * CELL_SIZE;
							newUnit.y = grid.y + newUnit.currentCellY * CELL_SIZE;
						}
						enemyLayerTop.addChild(newUnit); // Add to top layer so they appear above enemies
						alliedUnits.push(newUnit);
						lastSummonTime = LK.ticks;
						// Add flash effect when manually summoning
						tween(newUnit, {
							alpha: 0.3,
							scaleX: 1.5,
							scaleY: 1.5
						}, {
							duration: 150,
							easing: tween.easeOut,
							onFinish: function onFinish() {
								tween(newUnit, {
									alpha: 1.0,
									scaleX: 1.0,
									scaleY: 1.0
								}, {
									duration: 200,
									easing: tween.easeInOut
								});
							}
						});
						// Play success summon sound for manual summoning
						LK.getSound('purchaseSuccess').play();
						var notification = game.addChild(new Notification(digimonData.name + " summoned!"));
						notification.x = 2048 / 2;
						notification.y = grid.height - 50;
						self.hide();
					}
				};
			})(digimon, digimonButton, buttonBg, costText);
		}
	}
	// Close button
	var closeButton = new Container();
	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;
	self.addChild(closeButton);
	closeButton.down = function () {
		self.hide();
	};
	self.show = function (filterLevel) {
		// If a filterLevel is provided, filter Digimon by evolution level
		self._filterLevel = filterLevel || null;
		updateDigimonButtons();
		self.visible = true;
		tween(self, {
			y: 2732 - 300
		}, {
			duration: 300,
			easing: tween.backOut
		});
	};
	self.hide = function () {
		self._filterLevel = null; // Reset filter when closing
		tween(self, {
			y: 2732
		}, {
			duration: 200,
			easing: tween.easeIn,
			onFinish: function onFinish() {
				self.visible = false;
			}
		});
	};
	self.update = function () {
		// Update all button states
		for (var i = 0; i < itemsContainer.children.length; i++) {
			var button = itemsContainer.children[i];
			if (button.update) button.update();
		}
	};
	return self;
});
var DigimonUnit = Container.expand(function (type, level) {
	var self = Container.call(this);
	self.type = type || 'koromon';
	self.level = level || 1;
	self.health = 100 + (self.level - 1) * 50;
	self.maxHealth = self.health;
	self.damage = (20 + (self.level - 1) * 10) * 3;
	self.speed = 2.0; // Increased speed for better movement
	self.range = CELL_SIZE * 1.5;
	self.attackType = 'melee'; // 'melee', 'ranged', 'area'
	self.lastAttacked = 0;
	self.attackRate = 90; // Frames between attacks
	self.targetEnemy = null;
	self.isDead = false;
	// --- Digimon UI contextual elements ---
	var digimonNames = {
		// Agumon line
		agumon: "Agumon",
		greymon: "Greymon",
		metalgreymon: "MetalGreymon",
		wargreymon: "WarGreymon",
		// Gabumon line
		gabumon: "Gabumon",
		garurumon: "Garurumon",
		weregarurumon: "WereGarurumon",
		metalgarurumon: "MetalGarurumon",
		// Tentomon line
		tentomon: "Tentomon",
		kabuterimon: "Kabuterimon",
		megakabuterimon: "MegaKabuterimon",
		herculeskabuterimon: "HerculesKabuterimon",
		// Palmon line
		palmon: "Palmon",
		togemon: "Togemon",
		lillymon: "Lillymon",
		rosemon: "Rosemon",
		// Gomamon line
		gomamon: "Gomamon",
		ikkakumon: "Ikkakumon",
		zudomon: "Zudomon",
		vikemon: "Vikemon",
		// Patamon line
		patamon: "Patamon",
		angemon: "Angemon",
		magnaangemon: "MagnaAngemon",
		seraphimon: "Seraphimon",
		UlforceVeedramon: "UlforceVeedramon"
	};
	var digimonLevels = {
		agumon: "Rookie",
		greymon: "Champion",
		metalgreymon: "Ultimate",
		wargreymon: "Mega",
		gabumon: "Rookie",
		garurumon: "Champion",
		weregarurumon: "Ultimate",
		metalgarurumon: "Mega",
		tentomon: "Rookie",
		kabuterimon: "Champion",
		megakabuterimon: "Ultimate",
		herculeskabuterimon: "Mega",
		palmon: "Rookie",
		togemon: "Champion",
		lillymon: "Ultimate",
		rosemon: "Mega",
		gomamon: "Rookie",
		ikkakumon: "Champion",
		zudomon: "Ultimate",
		vikemon: "Mega",
		patamon: "Rookie",
		angemon: "Champion",
		magnaangemon: "Ultimate",
		seraphimon: "Mega",
		UlforceVeedramon: "Mega"
	};
	var digimonDescriptions = {
		agumon: "Rookie Fire Dragon",
		greymon: "Champion Fire Dragon",
		metalgreymon: "Ultimate Cyborg Dragon",
		wargreymon: "Mega Dragon Warrior",
		gabumon: "Rookie Ranged Wolf",
		garurumon: "Champion Ice Wolf",
		weregarurumon: "Ultimate Beast Warrior",
		metalgarurumon: "Mega Ice Wolf",
		tentomon: "Rookie Electric Insect",
		kabuterimon: "Champion Electric Insect",
		megakabuterimon: "Ultimate Giant Insect",
		herculeskabuterimon: "Mega Insect",
		palmon: "Rookie Plant Fighter",
		togemon: "Champion Cactus",
		lillymon: "Ultimate Flower Fairy",
		rosemon: "Mega Rose Queen",
		gomamon: "Rookie Aquatic Mammal",
		ikkakumon: "Champion Sea Beast",
		zudomon: "Ultimate Hammer Warrior",
		vikemon: "Mega Viking Beast",
		patamon: "Rookie Angel",
		angemon: "Champion Angel",
		magnaangemon: "Ultimate Holy Angel",
		seraphimon: "Mega Seraph",
		UlforceVeedramon: "Ultimate Speed Dragon"
	};
	var digimonColor = {
		agumon: 0xFF8C00,
		greymon: 0xFF4500,
		metalgreymon: 0xFF6347,
		wargreymon: 0xFFD700,
		gabumon: 0xAAAAFF,
		garurumon: 0x0066FF,
		weregarurumon: 0x0066CC,
		metalgarurumon: 0x3399FF,
		tentomon: 0xFFAA00,
		kabuterimon: 0x33CC00,
		megakabuterimon: 0x228B22,
		herculeskabuterimon: 0xFFD700,
		palmon: 0x33CC00,
		togemon: 0x228B22,
		lillymon: 0xFFB6C1,
		rosemon: 0xFF69B4,
		gomamon: 0x00BFFF,
		ikkakumon: 0xB0E0E6,
		zudomon: 0x4682B4,
		vikemon: 0xAA291A,
		patamon: 0xFFD700,
		angemon: 0x87CEEB,
		magnaangemon: 0x14719f,
		seraphimon: 0x6ddca4,
		UlforceVeedramon: 0x0080FF
	};
	// Set properties based on Digimon type and level
	switch (self.type) {
		// Agumon line
		case 'agumon':
			self.attackType = 'melee';
			self.damage = (15 + (self.level - 1) * 8) * 3;
			self.range = CELL_SIZE * 1.5;
			self.health = 100 + (self.level - 1) * 40;
			self.speed = 2.0;
			self.maxHealth = 130; // Fire rookie with decent health
			break;
		case 'greymon':
			self.attackType = 'ranged';
			self.damage = 70 * 3;
			self.range = CELL_SIZE * 2.5;
			self.health = 150 + (self.level - 1) * 65;
			self.speed = 1.3;
			break;
		case 'metalgreymon':
			self.attackType = 'area';
			self.damage = 120 * 3;
			self.range = CELL_SIZE * 2.8;
			self.health = 240 + (self.level - 1) * 100;
			self.speed = 1.4;
			break;
		case 'wargreymon':
			self.attackType = 'area';
			self.damage = 180 * 3 * 7; // 7x attack multiplier for Wargreymon
			self.range = CELL_SIZE * 4.5;
			self.health = 450 + (self.level - 1) * 170;
			self.speed = 1.3;
			break;
		// Gabumon line
		case 'gabumon':
			self.attackType = 'ranged';
			self.damage = (16 + (self.level - 1) * 7) * 3;
			self.range = CELL_SIZE * 2.2;
			break;
		case 'garurumon':
			self.attackType = 'ranged';
			self.damage = 70 * 3;
			self.range = CELL_SIZE * 2.7;
			self.health = 140 + (self.level - 1) * 60;
			self.speed = 1.2;
			break;
		case 'weregarurumon':
			self.attackType = 'ranged';
			self.damage = 120 * 3;
			self.range = CELL_SIZE * 3.2;
			self.health = 220 + (self.level - 1) * 90;
			self.speed = 1.5;
			break;
		case 'metalgarurumon':
			self.attackType = 'ranged';
			self.damage = 170 * 3;
			self.range = CELL_SIZE * 3.5;
			self.health = 400 + (self.level - 1) * 150;
			self.speed = 1.2;
			break;
		// Tentomon line
		case 'tentomon':
			self.attackType = 'ranged';
			self.damage = (15 + (self.level - 1) * 7) * 3;
			self.range = CELL_SIZE * 2.0;
			break;
		case 'kabuterimon':
			self.attackType = 'area';
			self.damage = 70 * 3;
			self.health = 140 + (self.level - 1) * 60;
			self.range = CELL_SIZE * 2.2;
			break;
		case 'megakabuterimon':
			self.attackType = 'area';
			self.damage = 120 * 3;
			self.health = 230 + (self.level - 1) * 90;
			self.range = CELL_SIZE * 2.5;
			break;
		case 'herculeskabuterimon':
			self.attackType = 'area';
			self.damage = 170 * 3;
			self.health = 400 + (self.level - 1) * 150;
			self.range = CELL_SIZE * 3.0;
			break;
		// Palmon line
		case 'palmon':
			self.attackType = 'melee';
			self.damage = (14 + (self.level - 1) * 6) * 3;
			break;
		case 'togemon':
			self.attackType = 'area';
			self.damage = 70 * 3;
			self.health = 120 + (self.level - 1) * 50;
			self.range = CELL_SIZE * 1.8;
			self.maxHealth = 120; // Champion cactus with good health
			break;
		case 'lillymon':
			self.attackType = 'area';
			self.damage = 120 * 3;
			self.health = 200 + (self.level - 1) * 80;
			self.range = CELL_SIZE * 2.2;
			break;
		case 'rosemon':
			self.attackType = 'area';
			self.damage = 170 * 3;
			self.health = 350 + (self.level - 1) * 120;
			self.range = CELL_SIZE * 2.7;
			break;
		// Gomamon line
		case 'gomamon':
			self.attackType = 'melee';
			self.damage = (15 + (self.level - 1) * 7) * 3;
			break;
		case 'ikkakumon':
			self.attackType = 'area';
			self.damage = 70 * 3;
			self.health = 130 + (self.level - 1) * 55;
			self.range = CELL_SIZE * 1.8;
			self.maxHealth = 130; // Champion sea beast with high health
			break;
		case 'zudomon':
			self.attackType = 'area';
			self.damage = 120 * 3;
			self.health = 210 + (self.level - 1) * 90;
			self.range = CELL_SIZE * 2.3;
			break;
		case 'vikemon':
			self.attackType = 'area';
			self.damage = 170 * 3;
			self.health = 400 + (self.level - 1) * 150;
			self.range = CELL_SIZE * 2.8;
			break;
		// Patamon line
		case 'patamon':
			self.attackType = 'ranged';
			self.damage = (12 + (self.level - 1) * 6) * 3;
			self.range = CELL_SIZE * 2.0;
			break;
		case 'angemon':
			self.attackType = 'ranged';
			self.damage = 70 * 3;
			self.range = CELL_SIZE * 2.5;
			self.health = 120 + (self.level - 1) * 50;
			self.maxHealth = 120; // Champion angel with balanced stats
			break;
		case 'magnaangemon':
			self.attackType = 'ranged';
			self.damage = 120 * 3;
			self.range = CELL_SIZE * 3.0;
			self.health = 200 + (self.level - 1) * 80;
			break;
		case 'seraphimon':
			self.attackType = 'ranged';
			self.damage = 170 * 3;
			self.range = CELL_SIZE * 3.5;
			self.health = 400 + (self.level - 1) * 150;
			break;
		case 'UlforceVeedramon':
			self.attackType = 'area';
			self.damage = 200 * 3 * 15; // 15x attack multiplier
			self.range = CELL_SIZE * 5.0;
			self.health = 500 + (self.level - 1) * 200;
			self.speed = 3.0; // Ultra fast
			break;
	}
	self.maxHealth = self.health;
	// Use correct asset for all allied units
	var digimonAssetMap = {
		// Agumon line
		agumon: 'agumon',
		greymon: 'greymon',
		metalgreymon: 'metalgreymon',
		wargreymon: 'wargreymon',
		// Gabumon line
		gabumon: 'gabumon',
		garurumon: 'garurumon',
		weregarurumon: 'weregarurumon',
		metalgarurumon: 'metalgarurumon',
		// Tentomon line
		tentomon: 'tentomon',
		kabuterimon: 'kabuterimon',
		megakabuterimon: 'megakabuterimon',
		herculeskabuterimon: 'herculeskabuterimon',
		// Palmon line
		palmon: 'palmon',
		togemon: 'togemon',
		lillymon: 'lillymon',
		rosemon: 'rosemon',
		// Gomamon line
		gomamon: 'gomamon',
		ikkakumon: 'ikkakumon',
		zudomon: 'zudomon',
		vikemon: 'vikemon',
		// Patamon line
		patamon: 'patamon',
		angemon: 'angemon',
		magnaangemon: 'magnaangemon',
		seraphimon: 'seraphimon',
		// UlforceVeedramon
		UlforceVeedramon: 'UlforceVeedramon'
	};
	var assetId = digimonAssetMap[self.type] || 'agumon';
	var unitGraphics = self.attachAsset(assetId, {
		anchorX: 0.5,
		anchorY: 0.5
	});
	// Remove any tinting to keep original colors
	unitGraphics.tint = 0xFFFFFF;
	// Tint and scale based on type
	switch (self.type) {
		case 'greymon':
			unitGraphics.scaleX = 1.5;
			unitGraphics.scaleY = 1.5;
			break;
		case 'garurumon':
			unitGraphics.scaleX = 1.5;
			unitGraphics.scaleY = 1.5;
			break;
		case 'kabuterimon':
			unitGraphics.scaleX = 1.5;
			unitGraphics.scaleY = 1.5;
			break;
		case 'togemon':
		case 'ikkakumon':
		case 'angemon':
			unitGraphics.scaleX = 1.5;
			unitGraphics.scaleY = 1.5;
			break;
		case 'metalgreymon':
			unitGraphics.scaleX = 1.75;
			unitGraphics.scaleY = 1.75;
			break;
		case 'weregarurumon':
			unitGraphics.scaleX = 1.75;
			unitGraphics.scaleY = 1.75;
			break;
		case 'megakabuterimon':
			unitGraphics.scaleX = 1.75;
			unitGraphics.scaleY = 1.75;
			break;
		case 'lillymon':
		case 'zudomon':
		case 'magnaangemon':
			unitGraphics.scaleX = 1.75;
			unitGraphics.scaleY = 1.75;
			break;
		case 'wargreymon':
			unitGraphics.scaleX = 2.5;
			unitGraphics.scaleY = 2.5;
			break;
		case 'metalgarurumon':
		case 'herculeskabuterimon':
		case 'rosemon':
		case 'vikemon':
		case 'seraphimon':
			unitGraphics.scaleX = 2.0;
			unitGraphics.scaleY = 2.0;
			break;
		case 'UlforceVeedramon':
			unitGraphics.scaleX = 2.5;
			unitGraphics.scaleY = 2.5;
			break;
	}
	// --- Level badge (top left of unit) ---
	var badgeColors = {
		Rookie: 0x00BFFF,
		Champion: 0x32CD32,
		Ultimate: 0xFFD700,
		Mega: 0xFF4500
	};
	var levelName = digimonLevels[self.type] || "Rookie";
	var badgeColor = badgeColors[levelName] || 0x00BFFF;
	var badge = self.attachAsset('towerLevelIndicator', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	badge.width = 44;
	badge.height = 44;
	badge.x = -unitGraphics.width / 2 + 22;
	badge.y = -unitGraphics.height / 2 + 22;
	badge.tint = badgeColor;
	var badgeText = new Text2(levelName.charAt(0), {
		size: 32,
		fill: 0xffffff,
		weight: 800
	});
	badgeText.anchor.set(0.5, 0.5);
	badgeText.x = badge.x;
	badgeText.y = badge.y;
	self.addChild(badge);
	self.addChild(badgeText);
	// --- Name popup (on summon, above unit) ---
	var namePopup = new Text2(digimonNames[self.type] || self.type, {
		size: 60,
		fill: 0xffffff,
		weight: 800
	});
	namePopup.anchor.set(0.5, 1.0);
	namePopup.x = 0;
	namePopup.y = -unitGraphics.height / 2 - 30;
	namePopup.alpha = 0;
	self.addChild(namePopup);
	// Animate name popup on spawn
	tween(namePopup, {
		alpha: 1,
		y: namePopup.y - 30
	}, {
		duration: 350,
		easing: tween.easeOut,
		onFinish: function onFinish() {
			tween(namePopup, {
				alpha: 0,
				y: namePopup.y - 60
			}, {
				duration: 700,
				delay: 700,
				easing: tween.easeIn,
				onFinish: function onFinish() {
					namePopup.visible = false;
				}
			});
		}
	});
	// Play summon sound and effect for all Digimon
	if (typeof LK !== "undefined" && LK.getSound) {
		LK.getSound('voiceSummon').play();
	}
	LK.effects.flashObject(self, 0x00FF00, 400);
	// --- Tooltip on touch (shows name, level, description) ---
	var tooltip = new Container();
	tooltip.visible = false;
	var tooltipBg = tooltip.attachAsset('notification', {
		anchorX: 0.5,
		anchorY: 1.0
	});
	tooltipBg.width = 340;
	tooltipBg.height = 120;
	tooltipBg.tint = 0x222222;
	tooltipBg.alpha = 0.95;
	var tooltipName = new Text2(digimonNames[self.type] || self.type, {
		size: 38,
		fill: 0xffffff,
		weight: 800
	});
	tooltipName.anchor.set(0.5, 0);
	tooltipName.y = -50;
	var tooltipLevel = new Text2(levelName, {
		size: 28,
		fill: badgeColor,
		weight: 800
	});
	tooltipLevel.anchor.set(0.5, 0);
	tooltipLevel.y = -15;
	var tooltipDesc = new Text2(digimonDescriptions[self.type] || "", {
		size: 24,
		fill: 0xcccccc,
		weight: 400
	});
	tooltipDesc.anchor.set(0.5, 0);
	tooltipDesc.y = 15;
	tooltip.addChild(tooltipBg);
	tooltip.addChild(tooltipName);
	tooltip.addChild(tooltipLevel);
	tooltip.addChild(tooltipDesc);
	tooltip.x = 0;
	tooltip.y = -unitGraphics.height / 2 - 10;
	self.addChild(tooltip);
	// Health bar
	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 = -unitGraphics.height / 2 - 15;
	healthBarOutline.x = -healthBarOutline.width / 2;
	healthBarBG.x = healthBar.x = -healthBar.width / 2 - 0.5;
	healthBar.tint = 0x00ff00;
	healthBarBG.tint = 0xff0000;
	self.healthBar = healthBar;
	// Movement properties
	self.cellX = 0;
	self.cellY = 0;
	self.currentCellX = 0;
	self.currentCellY = 0;
	self.currentTarget = null;
	self.pathTargets = [];
	// Initialize at exclusive allied spawn positions (spawntwo)
	if (grid.spawntwo && grid.spawntwo.length > 0) {
		var startSpawn = grid.spawntwo[Math.floor(Math.random() * grid.spawntwo.length)];
		self.cellX = startSpawn.x;
		self.cellY = startSpawn.y;
		self.currentCellX = startSpawn.x;
		self.currentCellY = startSpawn.y;
		self.x = grid.x + self.currentCellX * CELL_SIZE;
		self.y = grid.y + self.currentCellY * CELL_SIZE;
	}
	// --- Touch interaction for tooltip ---
	self.down = function () {
		tooltip.visible = true;
		tooltip.alpha = 0;
		tween(tooltip, {
			alpha: 1
		}, {
			duration: 120,
			easing: tween.easeOut
		});
		// Hide after 2 seconds
		LK.setTimeout(function () {
			tween(tooltip, {
				alpha: 0
			}, {
				duration: 200,
				easing: tween.easeIn,
				onFinish: function onFinish() {
					tooltip.visible = false;
				}
			});
		}, 2000);
	};
	self.findNearbyEnemies = function () {
		var nearbyEnemies = [];
		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);
			if (distance <= self.range) {
				nearbyEnemies.push(enemy);
			}
		}
		return nearbyEnemies;
	};
	self.attack = function (targetEnemy) {
		if (LK.ticks - self.lastAttacked < self.attackRate) return;
		self.lastAttacked = LK.ticks;
		// All allied attacks are area attacks
		var nearbyEnemies = self.findNearbyEnemies();
		for (var i = 0; i < nearbyEnemies.length; i++) {
			var enemy = nearbyEnemies[i];
			enemy.health -= self.damage;
			if (enemy.health <= 0) {
				enemy.health = 0;
			} else {
				enemy.healthBar.width = enemy.health / enemy.maxHealth * 70;
			}
		}
		// Visual splash effect
		var splashEffect = new EffectIndicator(self.x, self.y, 'splash');
		game.addChild(splashEffect);
	};
	self.update = function () {
		if (self.isDead) return;
		// --- Visual feedback for status effects (UX/UI improvement) ---
		if (self.burning && LK.ticks % 45 === 0) {
			var burnFx = new EffectIndicator(self.x, self.y, 'burn');
			if (game && game.addChild) game.addChild(burnFx);
		}
		if (self.frozen && LK.ticks % 30 === 0) {
			var freezeFx = new EffectIndicator(self.x, self.y, 'freeze');
			if (game && game.addChild) game.addChild(freezeFx);
		}
		if (self.poisoned && LK.ticks % 30 === 0) {
			var poisonFx = new EffectIndicator(self.x, self.y, 'poison');
			if (game && game.addChild) game.addChild(poisonFx);
		}
		if (self.paralyzed && LK.ticks % 30 === 0) {
			var paralyzeFx = new EffectIndicator(self.x, self.y, 'paralyze');
			if (game && game.addChild) game.addChild(paralyzeFx);
		}
		if (self.moist && LK.ticks % 60 === 0) {
			var moistFx = new EffectIndicator(self.x, self.y, 'moist');
			if (game && game.addChild) game.addChild(moistFx);
		}
		if (self.healing && LK.ticks % 30 === 0) {
			var healFx = new EffectIndicator(self.x, self.y, 'heal');
			if (game && game.addChild) game.addChild(healFx);
		}
		// Update health bar
		if (self.health <= 0) {
			self.isDead = true;
			self.healthBar.width = 0;
			// Show disappearance animation (fade out and scale up)
			tween(self, {
				alpha: 0,
				scaleX: 1.5,
				scaleY: 1.5
			}, {
				duration: 600,
				easing: tween.easeIn,
				onFinish: function onFinish() {
					// Remove from allied units array and destroy after delay
					var unitIndex = alliedUnits.indexOf(self);
					if (unitIndex !== -1) alliedUnits.splice(unitIndex, 1);
					self.destroy();
				}
			});
			// Show notification of defeat
			var defeatNote = game.addChild(new Notification((digimonNames[self.type] || self.type) + " defeated!"));
			defeatNote.x = 2048 / 2;
			defeatNote.y = grid.height - 120;
			return;
		}
		self.healthBar.width = self.health / self.maxHealth * 70;
		// --- Check if reached the end of the path (exclusive allied spawn cell) and award bits ---
		// Defensive: Only check if not already dead or being removed
		if (!self.isDead) {
			// Find current cell
			var currCellX = Math.round((self.x - grid.x) / CELL_SIZE);
			var currCellY = Math.round((self.y - grid.y) / CELL_SIZE);
			currCellX = Math.max(0, Math.min(grid.cells.length - 1, currCellX));
			currCellY = Math.max(0, Math.min(grid.cells[0].length - 1, currCellY));
			var currCell = grid.getCell(currCellX, currCellY);
			// Good practice: track last cell type to detect arrival at exclusive spawn
			if (self.lastCellType === undefined) self.lastCellType = currCell ? currCell.type : null;
			// Check if this cell is in goaltwo (exclusive allied goal)
			var isInGoalTwo = false;
			if (grid.goaltwo && grid.goaltwo.length > 0 && currCell) {
				for (var i = 0; i < grid.goaltwo.length; i++) {
					if (grid.goaltwo[i] === currCell) {
						isInGoalTwo = true;
						break;
					}
				}
			}
			if (isInGoalTwo && self.lastCellType !== 3) {
				// Award 10 bits
				setGold(gold + 10);
				// Show notification
				var rewardNote = game.addChild(new Notification("+10 bits! " + (digimonNames[self.type] || self.type) + " reached the goal!"));
				rewardNote.x = self.x;
				rewardNote.y = self.y - 60;
				// Remove from alliedUnits array and destroy
				var idx = alliedUnits.indexOf(self);
				if (idx !== -1) alliedUnits.splice(idx, 1);
				// Animate disappearance
				tween(self, {
					alpha: 0,
					scaleX: 1.5,
					scaleY: 1.5
				}, {
					duration: 400,
					easing: tween.easeIn,
					onFinish: function onFinish() {
						self.destroy();
					}
				});
				// Prevent further updates
				self.isDead = true;
				return;
			}
			// Update lastCellType for next frame
			self.lastCellType = currCell ? currCell.type : null;
		}
		// Find and attack nearby enemies
		var nearbyEnemies = self.findNearbyEnemies();
		if (nearbyEnemies.length > 0) {
			// Stop and attack closest enemy
			var closestEnemy = nearbyEnemies[0];
			var closestDistance = Infinity;
			for (var i = 0; i < nearbyEnemies.length; i++) {
				var enemy = nearbyEnemies[i];
				var dx = enemy.x - self.x;
				var dy = enemy.y - self.y;
				var distance = Math.sqrt(dx * dx + dy * dy);
				if (distance < closestDistance) {
					closestDistance = distance;
					closestEnemy = enemy;
				}
			}
			self.targetEnemy = closestEnemy;
			self.attack(closestEnemy);
			// Face the enemy
			var dx = closestEnemy.x - self.x;
			var dy = closestEnemy.y - self.y;
			var angle = Math.atan2(dy, dx);
			unitGraphics.rotation = angle;
			return; // Don't move while attacking
		}
		self.targetEnemy = null;
		// --- Simple Enemy Following Logic ---
		// Find the closest enemy to follow
		var closestEnemy = null;
		var closestDistance = 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);
			if (distance < closestDistance) {
				closestDistance = distance;
				closestEnemy = enemy;
			}
		}
		// If we found an enemy, move towards it
		if (closestEnemy) {
			var dx = closestEnemy.x - self.x;
			var dy = closestEnemy.y - self.y;
			var distance = Math.sqrt(dx * dx + dy * dy);
			// Only move if not too close (to avoid clustering)
			if (distance > self.range * 0.8) {
				var angle = Math.atan2(dy, dx);
				var moveSpeed = self.speed * gameSpeed;
				self.x += Math.cos(angle) * moveSpeed;
				self.y += Math.sin(angle) * moveSpeed;
				// Face the enemy
				if (self.children && self.children[0]) {
					self.children[0].rotation = angle;
				}
			}
		} else {
			// No enemies found, move towards the center of the map
			var centerX = grid.x + grid.cells.length * CELL_SIZE / 2;
			var centerY = grid.y + grid.cells[0].length * CELL_SIZE / 2;
			var dx = centerX - self.x;
			var dy = centerY - self.y;
			var distance = Math.sqrt(dx * dx + dy * dy);
			if (distance > CELL_SIZE) {
				var angle = Math.atan2(dy, dx);
				var moveSpeed = self.speed * gameSpeed * 0.5; // Slower when no enemies
				self.x += Math.cos(angle) * moveSpeed;
				self.y += Math.sin(angle) * moveSpeed;
				// Face movement direction
				if (self.children && self.children[0]) {
					self.children[0].rotation = angle;
				}
			}
		}
		// Keep units within reasonable bounds
		var minX = grid.x + CELL_SIZE;
		var maxX = grid.x + 23 * CELL_SIZE;
		var minY = grid.y + CELL_SIZE;
		var maxY = grid.y + 34 * CELL_SIZE;
		self.x = Math.max(minX, Math.min(maxX, self.x));
		self.y = Math.max(minY, Math.min(maxY, self.y));
	};
	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 = 0x33CC00;
			effectGraphics.width = effectGraphics.height = CELL_SIZE * 1.5;
			break;
		case 'slow':
			effectGraphics.tint = 0x9900FF;
			effectGraphics.width = effectGraphics.height = CELL_SIZE;
			break;
		case 'poison':
			effectGraphics.tint = 0x00FFAA;
			effectGraphics.width = effectGraphics.height = CELL_SIZE;
			break;
		case 'sniper':
			effectGraphics.tint = 0xFF5500;
			effectGraphics.width = effectGraphics.height = CELL_SIZE;
			break;
		case 'burn':
			effectGraphics.tint = 0xFF4400;
			effectGraphics.width = effectGraphics.height = CELL_SIZE * 1.2;
			break;
		case 'freeze':
			effectGraphics.tint = 0x66CCFF;
			effectGraphics.width = effectGraphics.height = CELL_SIZE * 1.3;
			break;
		case 'paralyze':
			effectGraphics.tint = 0xFFFF00;
			effectGraphics.width = effectGraphics.height = CELL_SIZE * 1.1;
			break;
		case 'moist':
			effectGraphics.tint = 0x0099CC;
			effectGraphics.width = effectGraphics.height = CELL_SIZE * 0.9;
			break;
		case 'heal':
			effectGraphics.tint = 0x00FF88;
			effectGraphics.width = effectGraphics.height = CELL_SIZE * 1.4;
			break;
	}
	effectGraphics.alpha = 0.7;
	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;
	self.worldNumber = currentWorld; // Track which world this enemy belongs to
	// 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;
	}
	// --- ENEMY ATTACK TOWER LOGIC ---
	// Enemies will attack towers if they are adjacent or on the same cell
	self.attackTowerCooldown = 0;
	self.attackTowerRate = 60; // Try to attack a tower every 1 second
	self.update = function () {
		if (self.health <= 0) {
			self.health = 0;
			self.healthBar.width = 0;
		}
		// --- Boss Attack Patterns ---
		// Only apply to boss enemies
		if (self.isBoss) {
			// World 1 Boss: Snorlax (sleep nearby towers)
			if (self.worldNumber === 1) {
				// Snorlax sleeps towers within 2.5 cells every 6 seconds
				if (!self._snorlaxSleepTimer) self._snorlaxSleepTimer = 0;
				self._snorlaxSleepTimer++;
				if (self._snorlaxSleepTimer > 360) {
					// 6 seconds at 60 FPS
					self._snorlaxSleepTimer = 0;
					// Find all towers within 2.5 cells
					for (var i = 0; i < towers.length; i++) {
						var tower = towers[i];
						var dx = tower.x - self.x;
						var dy = tower.y - self.y;
						var dist = Math.sqrt(dx * dx + dy * dy);
						if (dist <= CELL_SIZE * 2.5) {
							// Put tower to sleep for 4 seconds (240 ticks)
							if (!tower._sleepTimer || tower._sleepTimer <= 0) {
								tower._sleepTimer = 240;
								// Visual feedback: flash blue
								LK.effects.flashObject(tower, 0x3399FF, 800);
								// Show notification
								var note = game.addChild(new Notification("Snorlax put a tower to sleep!"));
								note.x = tower.x;
								note.y = tower.y - 100;
							}
						}
					}
				}
			}
			// World 2 Boss: Rhydon (stuns towers in a line every 7s)
			if (self.worldNumber === 2) {
				if (!self._rhydonStunTimer) self._rhydonStunTimer = 0;
				self._rhydonStunTimer++;
				if (self._rhydonStunTimer > 420) {
					// 7 seconds
					self._rhydonStunTimer = 0;
					// Stun towers in a horizontal line (same y, +/- 1 cell)
					for (var i = 0; i < towers.length; i++) {
						var tower = towers[i];
						var dy = Math.abs(tower.y - self.y);
						if (dy <= CELL_SIZE * 1.2) {
							if (!tower._stunTimer || tower._stunTimer <= 0) {
								tower._stunTimer = 120; // 2 seconds
								LK.effects.flashObject(tower, 0xAAAAAA, 600);
								var note = game.addChild(new Notification("Rhydon stunned a tower!"));
								note.x = tower.x;
								note.y = tower.y - 100;
							}
						}
					}
				}
			}
			// World 3 Boss: Articuno (freezes all towers every 10s)
			if (self.worldNumber === 3) {
				if (!self._articunoFreezeTimer) self._articunoFreezeTimer = 0;
				self._articunoFreezeTimer++;
				if (self._articunoFreezeTimer > 600) {
					// 10 seconds
					self._articunoFreezeTimer = 0;
					for (var i = 0; i < towers.length; i++) {
						var tower = towers[i];
						if (!tower._freezeTimer || tower._freezeTimer <= 0) {
							tower._freezeTimer = 90; // 1.5 seconds
							LK.effects.flashObject(tower, 0x66CCFF, 500);
							var note = game.addChild(new Notification("Articuno froze a tower!"));
							note.x = tower.x;
							note.y = tower.y - 100;
						}
					}
				}
			}
			// World 4 Boss: Machamp (smashes nearest tower every 8s, heavy damage)
			if (self.worldNumber === 4) {
				if (!self._machampSmashTimer) self._machampSmashTimer = 0;
				self._machampSmashTimer++;
				if (self._machampSmashTimer > 480) {
					// 8 seconds
					self._machampSmashTimer = 0;
					// Find nearest tower
					var minDist = Infinity,
						nearest = null;
					for (var i = 0; i < towers.length; i++) {
						var tower = towers[i];
						var dx = tower.x - self.x;
						var dy = tower.y - self.y;
						var dist = Math.sqrt(dx * dx + dy * dy);
						if (dist < minDist) {
							minDist = dist;
							nearest = tower;
						}
					}
					if (nearest) {
						nearest.health = Math.max(0, nearest.health - 40);
						nearest.towerHealthBar.width = nearest.health / nearest.maxHealth * 76;
						LK.effects.flashObject(nearest, 0xFF0000, 600);
						var note = game.addChild(new Notification("Machamp SMASH! -40 HP"));
						note.x = nearest.x;
						note.y = nearest.y - 100;
					}
				}
			}
			// World 5 Boss: Groudon (Tech Lab - high-tech cyber attacks every 8s)
			if (self.worldNumber === 5) {
				if (!self._groudonCyberTimer) self._groudonCyberTimer = 0;
				self._groudonCyberTimer++;
				if (self._groudonCyberTimer > 480) {
					// 8 seconds
					self._groudonCyberTimer = 0;
					// Cyber attack: overload 3 random towers with system errors
					var availableTowers = towers.slice();
					var targetsToOverload = Math.min(3, availableTowers.length);
					for (var t = 0; t < targetsToOverload; t++) {
						if (availableTowers.length === 0) break;
						var randomIndex = Math.floor(Math.random() * availableTowers.length);
						var tower = availableTowers[randomIndex];
						availableTowers.splice(randomIndex, 1);
						if (!tower._overloadTimer || tower._overloadTimer <= 0) {
							tower._overloadTimer = 150; // 2.5 seconds disabled
							LK.effects.flashObject(tower, 0x00FFFF, 800);
							var note = game.addChild(new Notification("Groudon's volcanic eruption disabled a tower!"));
							note.x = tower.x;
							note.y = tower.y - 100;
						}
					}
				}
			}
			// World 6 Boss: Mewtwo (Inferno - FINAL BOSS with escalating psychic attacks)
			if (self.worldNumber === 6) {
				if (!self._mewtwoPhaseTimer) self._mewtwoPhaseTimer = 0;
				if (!self._mewtwoPhase) self._mewtwoPhase = 1;
				self._mewtwoPhaseTimer++;
				// Phase-based attacks based on remaining health
				var healthPercent = self.health / self.maxHealth;
				if (healthPercent > 0.66) {
					self._mewtwoPhase = 1;
				} else if (healthPercent > 0.33) {
					self._mewtwoPhase = 2;
				} else {
					self._mewtwoPhase = 3;
				}
				// Phase 1: Mind control towers (every 6s)
				if (self._mewtwoPhase === 1 && self._mewtwoPhaseTimer > 360) {
					self._mewtwoPhaseTimer = 0;
					if (towers.length > 0) {
						var idx = Math.floor(Math.random() * towers.length);
						var tower = towers[idx];
						if (!tower._mindControlTimer || tower._mindControlTimer <= 0) {
							tower._mindControlTimer = 120; // 2 seconds mind controlled
							LK.effects.flashObject(tower, 0xFF00FF, 600);
							var note = game.addChild(new Notification("Mewtwo mind-controlled a tower!"));
							note.x = tower.x;
							note.y = tower.y - 100;
						}
					}
					// Phase 2: Psychic barrier reflects bullets (every 8s)
				} else if (self._mewtwoPhase === 2 && self._mewtwoPhaseTimer > 480) {
					self._mewtwoPhaseTimer = 0;
					self._psychicBarrier = 180; // 3 seconds of bullet reflection
					LK.effects.flashObject(self, 0xFFFFFF, 1000);
					var note = game.addChild(new Notification("Mewtwo activated psychic barrier!"));
					note.x = 2048 / 2;
					note.y = grid.height - 100;
					// Phase 3: Desperation - multiple random attacks (every 4s)
				} else if (self._mewtwoPhase === 3 && self._mewtwoPhaseTimer > 240) {
					self._mewtwoPhaseTimer = 0;
					// Random between mind control, barrier, and mass disable
					var attackType = Math.floor(Math.random() * 3);
					if (attackType === 0) {
						// Mass mind control
						for (var i = 0; i < Math.min(2, towers.length); i++) {
							var tower = towers[Math.floor(Math.random() * towers.length)];
							if (!tower._mindControlTimer || tower._mindControlTimer <= 0) {
								tower._mindControlTimer = 90;
								LK.effects.flashObject(tower, 0xFF00FF, 400);
							}
						}
						var note = game.addChild(new Notification("Mewtwo's desperation attack!"));
					} else if (attackType === 1) {
						// Psychic barrier
						self._psychicBarrier = 120;
						LK.effects.flashObject(self, 0xFFFFFF, 600);
						var note = game.addChild(new Notification("Mewtwo's psychic shield!"));
					} else {
						// Mass disable
						for (var i = 0; i < Math.min(3, towers.length); i++) {
							var tower = towers[Math.floor(Math.random() * towers.length)];
							if (!tower._disableTimer || tower._disableTimer <= 0) {
								tower._disableTimer = 90;
								LK.effects.flashObject(tower, 0x666666, 400);
							}
						}
						var note = game.addChild(new Notification("Mewtwo disabled multiple towers!"));
					}
					note.x = 2048 / 2;
					note.y = grid.height - 100;
				}
			}
		}
		// --- Tower status effect handling (sleep, stun, freeze, burn, disable) ---
		// This is global for all towers, but we add it here for boss effects
		for (var i = 0; i < towers.length; i++) {
			var t = towers[i];
			// Sleep disables fire/update
			if (t._sleepTimer && t._sleepTimer > 0) {
				t._sleepTimer--;
				t.alpha = 0.5;
				t.update = function () {}; // No-op
				if (t._sleepTimer === 0) {
					t.alpha = 1;
					delete t.update;
				}
				continue;
			}
			// Stun disables fire/update
			if (t._stunTimer && t._stunTimer > 0) {
				t._stunTimer--;
				t.alpha = 0.7;
				t.update = function () {};
				if (t._stunTimer === 0) {
					t.alpha = 1;
					delete t.update;
				}
				continue;
			}
			// Freeze disables fire/update
			if (t._freezeTimer && t._freezeTimer > 0) {
				t._freezeTimer--;
				t.alpha = 0.7;
				t.update = function () {};
				if (t._freezeTimer === 0) {
					t.alpha = 1;
					delete t.update;
				}
				continue;
			}
			// Burn: take damage over time
			if (t._burnTimer && t._burnTimer > 0) {
				t._burnTimer--;
				if (LK.ticks % 30 === 0) {
					t.health = Math.max(0, t.health - 3);
					t.towerHealthBar.width = t.health / t.maxHealth * 76;
					LK.effects.flashObject(t, 0xFF4400, 200);
				}
				if (t._burnTimer === 0) {
					t.alpha = 1;
				}
			}
			// Disable: disables fire/update
			if (t._disableTimer && t._disableTimer > 0) {
				t._disableTimer--;
				t.alpha = 0.3;
				t.update = function () {};
				if (t._disableTimer === 0) {
					t.alpha = 1;
					delete t.update;
				}
				continue;
			}
		}
		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;
					var slowFx = new EffectIndicator(self.x, self.y, 'slow');
					if (game && game.addChild) game.addChild(slowFx);
				}
				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;
					var poisonFx = new EffectIndicator(self.x, self.y, 'poison');
					if (game && game.addChild) game.addChild(poisonFx);
				}
				// 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
					}
				}
			}
			// Handle burn effect (Agumon line)
			if (self.burning) {
				if (LK.ticks % 45 === 0) {
					// Every 0.75 seconds
					var burnDamage = self.burnDamage;
					// Reduce burn damage if enemy is moist
					if (self.moist && self.fireResistance) {
						burnDamage *= self.fireResistance;
					}
					self.health -= burnDamage;
					if (self.health <= 0) self.health = 0;else self.healthBar.width = self.health / self.maxHealth * 70;
					var burnFx = new EffectIndicator(self.x, self.y, 'burn');
					if (game && game.addChild) game.addChild(burnFx);
				}
				self.burnDuration--;
				if (self.burnDuration <= 0) {
					self.burning = false;
				}
			}
			// Handle freeze effect (Gabumon line)
			if (self.frozen) {
				if (LK.ticks % 30 === 0) {
					var freezeFx = new EffectIndicator(self.x, self.y, 'freeze');
					if (game && game.addChild) game.addChild(freezeFx);
				}
				self.frozenDuration--;
				if (self.frozenDuration <= 0) {
					self.frozen = false;
					if (self.originalSpeed !== undefined) {
						self.speed = self.originalSpeed;
					}
				}
			}
			// Handle paralysis effect (Tentomon line)
			if (self.paralyzed) {
				if (LK.ticks % 30 === 0) {
					var paralyzeFx = new EffectIndicator(self.x, self.y, 'paralyze');
					if (game && game.addChild) game.addChild(paralyzeFx);
				}
				self.paralyzeDuration--;
				if (self.paralyzeDuration <= 0) {
					self.paralyzed = false;
					if (self.originalSpeed !== undefined) {
						self.speed = self.originalSpeed;
					}
				}
			}
			// Handle moisture effect (Gomamon line)
			if (self.moist) {
				if (LK.ticks % 60 === 0) {
					var moistFx = new EffectIndicator(self.x, self.y, 'moist');
					if (game && game.addChild) game.addChild(moistFx);
				}
				self.moistDuration--;
				if (self.moistDuration <= 0) {
					self.moist = false;
					self.fireResistance = 1.0;
					self.freezeVulnerability = 1.0;
				}
			}
		}
		// 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;
		}
		// --- ENEMY ATTACK TOWER AND ALLIED UNIT LOGIC ---
		// Enemies will attack towers and allied units if they are adjacent or on the same cell
		if (!self.isFlying && !self.isBoss) {
			self.attackTowerCooldown--;
			if (self.attackTowerCooldown <= 0) {
				// First check for allied units to attack (priority target)
				var foundAlly = null;
				for (var i = 0; i < alliedUnits.length; i++) {
					var ally = alliedUnits[i];
					if (ally.isDead || !ally.parent) continue;
					// Check if ally is in melee range (adjacent cells)
					var dx = Math.abs(ally.currentCellX - self.cellX);
					var dy = Math.abs(ally.currentCellY - self.cellY);
					if (dx <= 1.5 && dy <= 1.5) {
						foundAlly = ally;
						break;
					}
				}
				if (foundAlly) {
					// Deal damage to the allied unit
					var baseDamage = 4 + (self.worldNumber - 1) * 20; // Base damage increases by 20 per world
					// Increase damage with world and wave
					var worldMultiplier = 1 + (self.worldNumber - 1) * 0.4;
					var waveMultiplier = 1 + currentWave * 0.05;
					var totalDamage = Math.ceil(baseDamage * worldMultiplier * waveMultiplier);
					// Check if the allied unit is directly in front of the enemy (within 0.5 cell in the direction the enemy is facing)
					var facingBonus = 1;
					if (foundAlly && self.children && self.children[0]) {
						var enemyAngle = self.children[0].rotation;
						// Calculate vector from enemy to ally
						var dx = foundAlly.x - self.x;
						var dy = foundAlly.y - self.y;
						var dist = Math.sqrt(dx * dx + dy * dy);
						if (dist > 0) {
							// Normalize direction
							var dirX = Math.cos(enemyAngle);
							var dirY = Math.sin(enemyAngle);
							// Project vector to ally onto facing direction
							var dot = (dx * dirX + dy * dirY) / dist;
							// If dot > 0.7 (roughly within 45 degrees in front), and distance is close (within 1 cell)
							if (dot > 0.7 && dist < CELL_SIZE * 1.2) {
								facingBonus = 2; // Double damage if in front
							}
						}
					}
					totalDamage *= facingBonus;
					foundAlly.health = Math.max(0, foundAlly.health - totalDamage);
					// Play enemy hit sound
					LK.getSound('enemyHit').play();
					// Show damage indicator on ally
					var damageIndicator = new Notification("-" + totalDamage + " HP!");
					damageIndicator.x = foundAlly.x;
					damageIndicator.y = foundAlly.y - 60;
					if (game && game.addChild) game.addChild(damageIndicator);
					// Visual feedback - flash ally red when hit
					LK.effects.flashObject(foundAlly, 0xFF0000, 300);
					self.attackTowerCooldown = Math.max(25, self.attackTowerRate - Math.floor(currentWave * 0.5));
				} else {
					// No allies nearby, check for towers in adjacent cells (including current cell)
					var foundTower = null;
					for (var i = 0; i < towers.length; i++) {
						var tower = towers[i];
						// Check if tower is in a 2x2 area adjacent to enemy's cell
						var dx = Math.abs(tower.gridX + 0.5 - self.cellX);
						var dy = Math.abs(tower.gridY + 0.5 - self.cellY);
						if (dx <= 1.5 && dy <= 1.5) {
							foundTower = tower;
							break;
						}
					}
					if (foundTower) {
						// Deal damage to the tower
						var baseDamage = 3 + (self.worldNumber - 1) * 20; // Base damage increases by 20 per world
						// Increase damage with world and wave
						var worldMultiplier = 1 + (self.worldNumber - 1) * 0.4;
						var waveMultiplier = 1 + currentWave * 0.05;
						var totalDamage = Math.ceil(baseDamage * worldMultiplier * waveMultiplier);
						foundTower.health = Math.max(0, foundTower.health - totalDamage);
						foundTower.towerHealthBar.width = foundTower.health / foundTower.maxHealth * 76;
						// Play system damage sound
						LK.getSound('systemDamage').play();
						// Show damage indicator
						var damageIndicator = new Notification("-" + totalDamage + " Tower Health!");
						damageIndicator.x = foundTower.x;
						damageIndicator.y = foundTower.y - 80;
						if (game && game.addChild) game.addChild(damageIndicator);
						// If tower destroyed, remove from game
						if (foundTower.health <= 0) {
							var idx = towers.indexOf(foundTower);
							if (idx !== -1) towers.splice(idx, 1);
							if (foundTower.parent) foundTower.parent.removeChild(foundTower);
						}
						self.attackTowerCooldown = Math.max(30, self.attackTowerRate - Math.floor(currentWave * 0.5)); // Faster attacks as game progresses
					} else {
						self.attackTowerCooldown = 15; // Check again soon
					}
				}
			}
		}
		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;
	};
	// Apply world-based scaling to all enemy types
	self.applyWorldScaling = function () {
		var worldMultiplier = 1;
		var speedMultiplier = 1;
		// Scale stats based on world progression
		switch (self.worldNumber) {
			case 1:
				// Forest - Base stats
				worldMultiplier = 1;
				speedMultiplier = 1;
				break;
			case 2:
				// Desert - 25% more health, 10% faster
				worldMultiplier = 1.25;
				speedMultiplier = 1.1;
				break;
			case 3:
				// Glacier - 50% more health, slightly slower
				worldMultiplier = 1.5;
				speedMultiplier = 0.95;
				break;
			case 4:
				// Village - 75% more health, 15% faster
				worldMultiplier = 1.75;
				speedMultiplier = 1.15;
				break;
			case 5:
				// Tech Lab - Double health, 20% faster
				worldMultiplier = 2.0;
				speedMultiplier = 1.2;
				break;
			case 6:
				// Inferno - Triple health, 25% faster
				worldMultiplier = 3.0;
				speedMultiplier = 1.25;
				break;
			default:
				worldMultiplier = 1;
				speedMultiplier = 1;
		}
		// Apply scaling only to non-boss enemies - bosses have fixed health values
		if (!self.isBoss) {
			self.maxHealth = Math.floor(self.maxHealth * worldMultiplier);
		}
		self.speed *= speedMultiplier;
	};
	// Apply world scaling
	self.applyWorldScaling();
	if (currentWave % 10 === 0 && currentWave > 0 && type !== 'swarm') {
		self.isBoss = true;
		// Boss enemies have 20x health and are larger
		self.maxHealth *= 20;
		// Special case: Set boss base health before scaling by world - these values are final and won't be affected by world multipliers
		if (self.worldNumber === 1) {
			self.maxHealth = 600; // Rhydon: 600 base health (no multiplier applied to maintain exact health)
		} else if (self.worldNumber === 2) {
			self.maxHealth = 600; // Rhydon: 600 base health (no multiplier applied to maintain exact health)
		} else if (self.worldNumber === 3) {
			self.maxHealth = 700; // Articuno: 700 base health (no multiplier applied to maintain exact health)
		} else if (self.worldNumber === 4) {
			self.maxHealth = 800; // Machamp: 800 base health (no multiplier applied to maintain exact health)
		} else if (self.worldNumber === 5) {
			self.maxHealth = 900; // Groudon: 900 base health (no multiplier applied to maintain exact health)
		} else if (self.worldNumber === 6) {
			self.maxHealth = 1000; // Mewtwo: 1000 base health (no multiplier applied to maintain exact health)
		}
		// Slower speed for bosses
		self.speed = self.speed * 0.7;
	}
	self.health = self.maxHealth;
	// Get appropriate asset for this virus type
	var assetId = 'virus';
	if (self.isBoss) {
		// Use world-specific boss assets
		switch (self.worldNumber) {
			case 1:
				assetId = 'boss_snorlax';
				break;
			case 2:
				assetId = 'boss_rhydon';
				break;
			case 3:
				assetId = 'boss_articuno';
				break;
			case 4:
				assetId = 'boss_machamp';
				break;
			case 5:
				assetId = 'boss_groudon';
				break;
			case 6:
				assetId = 'boss_mewtwo';
				break;
			default:
				assetId = 'virus';
		}
	} else if (self.type !== 'normal') {
		assetId = 'virus_' + self.type;
	}
	var enemyGraphics = self.attachAsset(assetId, {
		anchorX: 0.5,
		anchorY: 0.5
	});
	// Scale up boss enemies (but less since they already have larger base assets)
	if (self.isBoss) {
		enemyGraphics.scaleX = 1.2;
		enemyGraphics.scaleY = 1.2;
	}
	// Apply world-based color tinting system
	self.getWorldTint = function () {
		var baseTint = 0xFFFFFF; // Default white tint
		// Get base color based on world
		switch (self.worldNumber) {
			case 1:
				// Forest - Green tints
				baseTint = 0x90EE90;
				break;
			case 2:
				// Desert - Sand/yellow tints
				baseTint = 0xF4A460;
				break;
			case 3:
				// Glacier - Blue/ice tints
				baseTint = 0xADD8E6;
				break;
			case 4:
				// Village - Brown/earth tints
				baseTint = 0xD2B48C;
				break;
			case 5:
				// Tech Lab - Metallic/silver tints
				baseTint = 0xC0C0C0;
				break;
			case 6:
				// Inferno - Red/fire tints
				baseTint = 0xFF6B6B;
				break;
			default:
				baseTint = 0xFFFFFF;
		}
		// Modify base tint slightly based on enemy type while keeping world theme
		switch (self.type) {
			case 'fast':
				// Make it slightly more blue-ish while keeping world color
				baseTint = tween.linear(baseTint, 0x0080FF, 0.3);
				break;
			case 'immune':
				// Make it slightly more red-ish while keeping world color
				baseTint = tween.linear(baseTint, 0xFF4444, 0.3);
				break;
			case 'flying':
				// Make it brighter while keeping world color
				baseTint = Math.min(0xFFFFFF, baseTint + 0x202020);
				break;
			case 'swarm':
				// Make it slightly darker while keeping world color
				baseTint = Math.max(0x404040, baseTint - 0x202020);
				break;
		}
		return baseTint;
	};
	// Apply world-based tinting
	var worldTint = self.getWorldTint();
	enemyGraphics.tint = worldTint;
	// 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
					}
				}
			}
			// Handle burn effect (Agumon line)
			if (self.burning) {
				if (LK.ticks % 45 === 0) {
					// Every 0.75 seconds
					var burnDamage = self.burnDamage;
					// Reduce burn damage if enemy is moist
					if (self.moist && self.fireResistance) {
						burnDamage *= self.fireResistance;
					}
					self.health -= burnDamage;
					if (self.health <= 0) self.health = 0;else self.healthBar.width = self.health / self.maxHealth * 70;
				}
				self.burnDuration--;
				if (self.burnDuration <= 0) {
					self.burning = false;
				}
			}
			// Handle freeze effect (Gabumon line)
			if (self.frozen) {
				self.frozenDuration--;
				if (self.frozenDuration <= 0) {
					self.frozen = false;
					if (self.originalSpeed !== undefined) {
						self.speed = self.originalSpeed;
					}
				}
			}
			// Handle paralysis effect (Tentomon line)
			if (self.paralyzed) {
				self.paralyzeDuration--;
				if (self.paralyzeDuration <= 0) {
					self.paralyzed = false;
					if (self.originalSpeed !== undefined) {
						self.speed = self.originalSpeed;
					}
				}
			}
			// Handle moisture effect (Gomamon line)
			if (self.moist) {
				self.moistDuration--;
				if (self.moistDuration <= 0) {
					self.moist = false;
					self.fireResistance = 1.0;
					self.freezeVulnerability = 1.0;
				}
			}
		}
		// 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 GoldIndicator = Container.expand(function (value, x, y) {
	var self = Container.call(this);
	var shadowText = new Text2("+" + value, {
		size: 45,
		fill: 0x000000,
		weight: 800
	});
	shadowText.anchor.set(0.5, 0.5);
	shadowText.x = 2;
	shadowText.y = 2;
	self.addChild(shadowText);
	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 = [];
	// Add exclusive spawn and goal arrays for allies
	self.spawntwo = [];
	self.goaltwo = [];
	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
				1: Wall
				2: Spawn
				3: Goal
	*/
	// Create world-based maze layout
	self.generateMazeForWorld = function (worldNumber) {
		// Clear existing maze
		for (var i = 0; i < gridWidth; i++) {
			for (var j = 0; j < gridHeight; j++) {
				self.cells[i][j].type = 1; // Default to wall
			}
		}
		self.spawns = [];
		self.goals = [];
		// Also clear exclusive allied spawn/goal arrays
		self.spawntwo = [];
		self.goaltwo = [];
		// Always create entry area (top 5 rows) with 3-block wide path
		for (var i = 0; i < gridWidth; i++) {
			for (var j = 0; j <= 4; j++) {
				if (i >= 11 && i <= 13) {
					self.cells[i][j].type = 0; // Open path in center (3 blocks wide)
					if (j === 0) {
						self.cells[i][j].type = 2; // Spawn points
						self.spawns.push(self.cells[i][j]);
						// Invertido: ahora los goals de aliados están en la entrada
						self.goaltwo.push(self.cells[i][j]);
					}
				} else {
					self.cells[i][j].type = 1; // Walls on sides
				}
			}
		}
		// Always create exit area (bottom 5 rows) with 3-block wide path
		for (var i = 0; i < gridWidth; i++) {
			for (var j = gridHeight - 5; j < gridHeight; j++) {
				if (i >= 11 && i <= 13) {
					self.cells[i][j].type = 0; // Open path in center (3 blocks wide)
					if (j === gridHeight - 1) {
						self.cells[i][j].type = 3; // Goal points
						self.goals.push(self.cells[i][j]);
						// Invertido: ahora los spawns de aliados están en la salida
						self.spawntwo.push(self.cells[i][j]);
					}
				} else {
					self.cells[i][j].type = 1; // Walls on sides
				}
			}
		}
		// Create classic square-cornered labyrinth with generous tower placement areas
		// Simple pattern: Create a winding path with 90-degree turns and wide wall corridors
		// Path layout: Start center-top, go down, turn right, down, turn left, repeat creating a serpentine pattern
		// Main path coordinates (center of 3-block wide paths)
		// Reduced to only 2 curves for a simpler path
		var pathCenterX = 12; // Center column
		var pathPattern = [
		// Start: go down from entry
		{
			x: pathCenterX,
			startY: 5,
			endY: 12,
			type: 'vertical'
		},
		// First curve: turn left
		{
			y: 12,
			startX: pathCenterX,
			endX: 4,
			type: 'horizontal'
		},
		// Go down on left side
		{
			x: 4,
			startY: 12,
			endY: 20,
			type: 'vertical'
		},
		// Second curve: turn right to center
		{
			y: 20,
			startX: 4,
			endX: pathCenterX,
			type: 'horizontal'
		},
		// Final path to exit
		{
			x: pathCenterX,
			startY: 20,
			endY: gridHeight - 5,
			type: 'vertical'
		}];
		// Crear los segmentos del camino con 3 bloques de grosor (camino) y dejar espacio extra de 1 bloque a cada lado para asegurar zonas de 2x2 para torretas
		for (var p = 0; p < pathPattern.length; p++) {
			var segment = pathPattern[p];
			if (segment.type === 'vertical') {
				var startY = Math.min(segment.startY, segment.endY);
				var endY = Math.max(segment.startY, segment.endY);
				for (var y = startY; y <= endY; y++) {
					// Camino de 3 bloques de ancho
					for (var offset = -1; offset <= 1; offset++) {
						var pathX = segment.x + offset;
						if (pathX >= 0 && pathX < gridWidth && y >= 0 && y < gridHeight) {
							self.cells[pathX][y].type = 0;
						}
					}
					// Dejar espacio extra de 1 bloque a cada lado del camino para permitir torretas 2x2
					for (var extra = -2; extra <= 2; extra += 4) {
						var sideX = segment.x + extra;
						if (sideX >= 0 && sideX < gridWidth && y >= 0 && y < gridHeight) {
							// Solo marcar como espacio de torre si no es camino ni spawn/goal
							if (self.cells[sideX][y].type === 1) {
								// No cambiar si ya es camino/spawn/goal
								self.cells[sideX][y].type = 1; // Mantener como muro, pero dejarlo para posible torre
							}
						}
					}
					// Asegurar espacio de 2x2 para torretas a la izquierda y derecha del camino
					for (var offsetY = 0; offsetY <= 1; offsetY++) {
						for (var extra = -2; extra <= 2; extra += 4) {
							var baseX = segment.x + extra;
							var baseY = y + offsetY;
							if (baseX >= 0 && baseX + 1 < gridWidth && baseY >= 0 && baseY + 1 < gridHeight) {
								// Solo marcar como espacio de torre si ambos son muro
								if (self.cells[baseX][baseY].type === 1 && self.cells[baseX + 1][baseY].type === 1 && self.cells[baseX][baseY + 1].type === 1 && self.cells[baseX + 1][baseY + 1].type === 1) {
									// Marcar como espacio de torre (type 1) para permitir torretas 2x2
									self.cells[baseX][baseY].type = 1;
									self.cells[baseX + 1][baseY].type = 1;
									self.cells[baseX][baseY + 1].type = 1;
									self.cells[baseX + 1][baseY + 1].type = 1;
								}
							}
						}
					}
				}
			} else if (segment.type === 'horizontal') {
				var startX = Math.min(segment.startX, segment.endX);
				var endX = Math.max(segment.startX, segment.endX);
				for (var x = startX; x <= endX; x++) {
					// Camino de 3 bloques de alto
					for (var offset = -1; offset <= 1; offset++) {
						var pathY = segment.y + offset;
						if (x >= 0 && x < gridWidth && pathY >= 0 && pathY < gridHeight) {
							self.cells[x][pathY].type = 0;
						}
					}
					// Dejar espacio extra de 1 bloque arriba y abajo del camino para permitir torretas 2x2
					for (var extra = -2; extra <= 2; extra += 4) {
						var sideY = segment.y + extra;
						if (x >= 0 && x < gridWidth && sideY >= 0 && sideY < gridHeight) {
							if (self.cells[x][sideY].type === 1) {
								self.cells[x][sideY].type = 1;
							}
						}
					}
					// Asegurar espacio de 2x2 para torretas arriba y abajo del camino
					for (var offsetX = 0; offsetX <= 1; offsetX++) {
						for (var extra = -2; extra <= 2; extra += 4) {
							var baseX = x + offsetX;
							var baseY = segment.y + extra;
							if (baseX >= 0 && baseX + 1 < gridWidth && baseY >= 0 && baseY + 1 < gridHeight) {
								if (self.cells[baseX][baseY].type === 1 && self.cells[baseX + 1][baseY].type === 1 && self.cells[baseX][baseY + 1].type === 1 && self.cells[baseX + 1][baseY + 1].type === 1) {
									// Marcar como espacio de torre (type 1) para permitir torretas 2x2
									self.cells[baseX][baseY].type = 1;
									self.cells[baseX + 1][baseY].type = 1;
									self.cells[baseX][baseY + 1].type = 1;
									self.cells[baseX + 1][baseY + 1].type = 1;
								}
							}
						}
					}
				}
			}
		}
		// Asegurar conexiones suaves en las intersecciones y dejar espacio de 2x2 para torret
	};
	// Generate maze for current world
	var world = Math.ceil(currentWave / 9);
	if (world < 1) world = 1;
	if (world > 6) world = 6;
	self.generateMazeForWorld(world);
	// Apply the maze layout to cells
	for (var i = 0; i < gridWidth; i++) {
		for (var j = 0; j < gridHeight; j++) {
			var cell = self.cells[i][j];
			var cellType = cell.type; // Use the type set by generateMazeForWorld
			cell.type = cellType;
			cell.x = i;
			cell.y = 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 = [];
			// Only create debug cells for visible areas and reduce frequency
			if (j > 3 && j <= gridHeight - 4 && (i + j) % 2 === 0) {
				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];
				// Simplified pathfinding - only check cardinal directions for better performance
				var 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 with speed multiplier
			enemy.currentCellY += enemy.speed * gameSpeed;
			// Ensure enemy moves towards the center of the 3-block wide path (x=12)
			var pathCenterX = 12;
			if (enemy.currentCellX !== pathCenterX) {
				var xDiff = pathCenterX - enemy.currentCellX;
				var moveSpeed = Math.min(Math.abs(xDiff), enemy.speed * 0.5 * gameSpeed);
				enemy.currentCellX += xDiff > 0 ? moveSpeed : -moveSpeed;
			}
			// 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;
			// 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 * gameSpeed;
			enemy.currentCellY += Math.sin(angle) * enemy.speed * gameSpeed;
			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];
		}
		// Initialize stuck tracking for enemy
		if (enemy.lastPosition === undefined) {
			enemy.lastPosition = {
				x: enemy.currentCellX,
				y: enemy.currentCellY
			};
			enemy.stuckCounter = 0;
			enemy.lastMovementTime = LK.ticks;
		}
		// Check if enemy is stuck (hasn't moved significantly in a while)
		var currentPos = {
			x: enemy.currentCellX,
			y: enemy.currentCellY
		};
		var distanceMoved = Math.sqrt(Math.pow(currentPos.x - enemy.lastPosition.x, 2) + Math.pow(currentPos.y - enemy.lastPosition.y, 2));
		// If enemy hasn't moved much in the last 60 ticks (1 second), consider it stuck
		if (distanceMoved < 0.1 && LK.ticks - enemy.lastMovementTime > 60) {
			enemy.stuckCounter++;
			enemy.lastMovementTime = LK.ticks;
			// If stuck for too long, try to find alternative path
			if (enemy.stuckCounter > 3) {
				// Reset stuck counter to prevent infinite loops
				enemy.stuckCounter = 0;
				// Try to find alternative targets from current cell
				if (cell.targets && cell.targets.length > 1) {
					// Find a different target than the current one
					for (var i = 0; i < cell.targets.length; i++) {
						var alternativeTarget = cell.targets[i];
						if (alternativeTarget !== enemy.currentTarget) {
							enemy.currentTarget = alternativeTarget;
							break;
						}
					}
				} else {
					// If no alternative targets, try neighboring cells
					var neighbors = [cell.up, cell.right, cell.down, cell.left];
					var validNeighbors = [];
					for (var i = 0; i < neighbors.length; i++) {
						var neighbor = neighbors[i];
						if (neighbor && neighbor.type !== 1 && neighbor.pathId === pathId && neighbor.targets && neighbor.targets.length > 0) {
							validNeighbors.push(neighbor);
						}
					}
					if (validNeighbors.length > 0) {
						// Choose a random valid neighbor and use its target
						var randomNeighbor = validNeighbors[Math.floor(Math.random() * validNeighbors.length)];
						enemy.currentTarget = randomNeighbor.targets[0];
						// Move slightly towards the chosen neighbor to unstuck
						var neighborX = randomNeighbor.x;
						var neighborY = randomNeighbor.y;
						var unstuckAngle = Math.atan2(neighborY - enemy.currentCellY, neighborX - enemy.currentCellX);
						enemy.currentCellX += Math.cos(unstuckAngle) * enemy.speed * 0.5;
						enemy.currentCellY += Math.sin(unstuckAngle) * enemy.speed * 0.5;
					}
				}
			}
		} else if (distanceMoved >= 0.1) {
			// Enemy is moving, reset stuck tracking
			enemy.stuckCounter = 0;
			enemy.lastMovementTime = LK.ticks;
			enemy.lastPosition = {
				x: currentPos.x,
				y: currentPos.y
			};
		}
		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;
				// Reset position tracking when reaching target
				enemy.lastPosition = {
					x: enemy.currentCellX,
					y: enemy.currentCellY
				};
				return;
			}
			var angle = Math.atan2(oy, ox);
			enemy.currentCellX += Math.cos(angle) * enemy.speed * gameSpeed;
			enemy.currentCellY += Math.sin(angle) * enemy.speed * gameSpeed;
		}
		enemy.x = grid.x + enemy.currentCellX * CELL_SIZE;
		enemy.y = grid.y + enemy.currentCellY * CELL_SIZE;
	};
});
var LevelSelectionMenu = Container.expand(function (worldNumber) {
	var self = Container.call(this);
	self.worldNumber = worldNumber;
	// Position the menu at center of screen
	self.x = 2048 / 2;
	self.y = 2732 / 2;
	var menuBackground = self.attachAsset('notification', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	menuBackground.width = 1800;
	menuBackground.height = 1200;
	menuBackground.tint = 0x333333;
	menuBackground.alpha = 0.9;
	var worldNames = ["", "Forest", "Desert", "Glacier", "Village", "Inferno", "Tech Lab"];
	var titleText = new Text2('Select Level - ' + worldNames[worldNumber], {
		size: 80,
		fill: 0xFFFFFF,
		weight: 800
	});
	titleText.anchor.set(0.5, 0.5);
	titleText.y = -400;
	self.addChild(titleText);
	var worldLevels = storage.worldLevels || {
		1: 1,
		2: 1,
		3: 1,
		4: 1,
		5: 1,
		6: 1
	};
	var unlockedLevel = worldLevels[worldNumber] || 1;
	// Create level buttons in a grid
	var buttonsPerRow = 5;
	var buttonWidth = 120;
	var buttonHeight = 80;
	var buttonSpacing = 160;
	var startX = -((buttonsPerRow - 1) * buttonSpacing) / 2;
	var startY = -200;
	for (var i = 1; i <= 10; i++) {
		var levelButton = new Container();
		var levelButtonBg = levelButton.attachAsset('notification', {
			anchorX: 0.5,
			anchorY: 0.5
		});
		levelButtonBg.width = buttonWidth;
		levelButtonBg.height = buttonHeight;
		var isUnlocked = i <= unlockedLevel;
		levelButtonBg.tint = isUnlocked ? 0x4444FF : 0x666666;
		var levelButtonText = new Text2(isUnlocked ? i.toString() : "🔒", {
			size: 40,
			fill: isUnlocked ? 0xFFFFFF : 0x999999,
			weight: 800
		});
		levelButtonText.anchor.set(0.5, 0.5);
		levelButton.addChild(levelButtonText);
		// Position buttons in grid
		var row = Math.floor((i - 1) / buttonsPerRow);
		var col = (i - 1) % buttonsPerRow;
		levelButton.x = startX + col * buttonSpacing;
		levelButton.y = startY + row * 120;
		self.addChild(levelButton);
		(function (levelIndex, unlocked) {
			levelButton.down = function () {
				if (unlocked) {
					self.destroy();
					game.startWorldLevel(self.worldNumber, levelIndex);
				} else {
					var notification = game.addChild(new Notification("Complete previous level to unlock!"));
					notification.x = 2048 / 2;
					notification.y = 2732 / 2 + 200;
				}
			};
		})(i, isUnlocked);
	}
	// Back button
	var backButton = new Container();
	var backButtonBg = backButton.attachAsset('notification', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	backButtonBg.width = 200;
	backButtonBg.height = 80;
	backButtonBg.tint = 0x666666;
	var backButtonText = new Text2('Back', {
		size: 40,
		fill: 0xFFFFFF,
		weight: 800
	});
	backButtonText.anchor.set(0.5, 0.5);
	backButton.addChild(backButtonText);
	backButton.x = 0;
	backButton.y = 400;
	self.addChild(backButton);
	backButton.down = function () {
		self.destroy();
		var worldSelectionMenu = new WorldSelectionMenu();
		game.addChild(worldSelectionMenu);
	};
	return self;
});
var MainMenu = Container.expand(function () {
	var self = Container.call(this);
	// Position the menu at center of screen
	self.x = 2048 / 2;
	self.y = 2732 / 2;
	// Stop any currently playing music first
	LK.stopMusic();
	// Start main menu music
	LK.playMusic('mainMenuMusic', {
		fade: {
			start: 0,
			end: 0.8,
			duration: 1500
		}
	});
	var menuBackground = self.attachAsset('notification', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	menuBackground.width = 1800;
	menuBackground.height = 1200;
	menuBackground.tint = 0x333333;
	menuBackground.alpha = 0.9;
	var titleText = new Text2(getText('firewallDefensors'), {
		size: 100,
		fill: 0xFFFFFF,
		weight: 800
	});
	titleText.anchor.set(0.5, 0.5);
	titleText.y = -300;
	self.addChild(titleText);
	// Add version number below title
	var versionText = new Text2('v0.3', {
		size: 40,
		fill: 0xCCCCCC,
		weight: 400
	});
	versionText.anchor.set(0.5, 0.5);
	versionText.y = -220;
	self.addChild(versionText);
	// Add Beta text with Minecraft-style effects
	var betaText = new Text2('BETA', {
		size: 80,
		fill: 0xFFFF00,
		weight: 800
	});
	betaText.anchor.set(0.5, 0.5);
	betaText.x = 400;
	betaText.y = -200;
	betaText.rotation = -Math.PI / 12; // Tilt the text (15 degrees)
	betaText.alpha = 0.8;
	self.addChild(betaText);
	// Add floating animation similar to Minecraft
	var _betaFloatAnimation = function betaFloatAnimation() {
		tween(betaText, {
			y: betaText.y - 15,
			rotation: betaText.rotation + 0.05
		}, {
			duration: 2000,
			easing: tween.easeInOut,
			onFinish: function onFinish() {
				tween(betaText, {
					y: betaText.y + 15,
					rotation: betaText.rotation - 0.05
				}, {
					duration: 2000,
					easing: tween.easeInOut,
					onFinish: _betaFloatAnimation
				});
			}
		});
	};
	_betaFloatAnimation();
	// Add glow effect to beta text
	var _betaGlowAnimation = function betaGlowAnimation() {
		tween(betaText, {
			alpha: 1.0,
			scaleX: 1.1,
			scaleY: 1.1
		}, {
			duration: 1500,
			easing: tween.easeInOut,
			onFinish: function onFinish() {
				tween(betaText, {
					alpha: 0.8,
					scaleX: 1.0,
					scaleY: 1.0
				}, {
					duration: 1500,
					easing: tween.easeInOut,
					onFinish: _betaGlowAnimation
				});
			}
		});
	};
	_betaGlowAnimation();
	var startButton = new Container();
	var startButtonBg = startButton.attachAsset('notification', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	startButtonBg.width = 400;
	startButtonBg.height = 120;
	startButtonBg.tint = 0x00AA00;
	var startButtonText = new Text2(getText('startGame'), {
		size: 50,
		fill: 0xFFFFFF,
		weight: 800
	});
	startButtonText.anchor.set(0.5, 0.5);
	startButton.addChild(startButtonText);
	startButton.y = -50;
	self.addChild(startButton);
	startButton.down = function () {
		self.destroy();
		game.startGame();
	};
	// Add tutorial button
	var tutorialButton = new Container();
	var tutorialButtonBg = tutorialButton.attachAsset('notification', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	tutorialButtonBg.width = 400;
	tutorialButtonBg.height = 120;
	tutorialButtonBg.tint = 0x00AAAA;
	var tutorialButtonText = new Text2(getText('tutorial'), {
		size: 50,
		fill: 0xFFFFFF,
		weight: 800
	});
	tutorialButtonText.anchor.set(0.5, 0.5);
	tutorialButton.addChild(tutorialButtonText);
	tutorialButton.y = 80;
	self.addChild(tutorialButton);
	tutorialButton.down = function () {
		self.destroy();
		game.startTutorial();
	};
	// Add leaderboard button
	var leaderboardButton = new Container();
	var leaderboardButtonBg = leaderboardButton.attachAsset('notification', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	leaderboardButtonBg.width = 400;
	leaderboardButtonBg.height = 120;
	leaderboardButtonBg.tint = 0x4444FF;
	var leaderboardButtonText = new Text2('Leaderboard', {
		size: 50,
		fill: 0xFFFFFF,
		weight: 800
	});
	leaderboardButtonText.anchor.set(0.5, 0.5);
	leaderboardButton.addChild(leaderboardButtonText);
	leaderboardButton.y = 340;
	self.addChild(leaderboardButton);
	leaderboardButton.down = function () {
		// Defensive: try/catch in case leaderboard is not available
		try {
			if (typeof LK.showLeaderboard === "function") {
				LK.showLeaderboard();
			} else if (typeof LK.showLeaderBoard === "function") {
				LK.showLeaderBoard();
			} else {
				var notification = game.addChild(new Notification("Leaderboard not available!"));
				notification.x = 2048 / 2;
				notification.y = 2732 / 2 + 200;
			}
		} catch (e) {
			var notification = game.addChild(new Notification("Leaderboard not available!"));
			notification.x = 2048 / 2;
			notification.y = 2732 / 2 + 200;
		}
	};
	// Add language button
	var languageButton = new Container();
	var languageButtonBg = languageButton.attachAsset('notification', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	languageButtonBg.width = 400;
	languageButtonBg.height = 120;
	languageButtonBg.tint = 0xFF6600;
	var languageButtonText = new Text2(getText('language'), {
		size: 50,
		fill: 0xFFFFFF,
		weight: 800
	});
	languageButtonText.anchor.set(0.5, 0.5);
	languageButton.addChild(languageButtonText);
	languageButton.y = 210;
	self.addChild(languageButton);
	languageButton.down = function () {
		// Cycle through all four languages: English -> Spanish -> Chinese -> Portuguese -> English
		var languages = ['en', 'es', 'zh', 'pt'];
		var currentIndex = languages.indexOf(currentLanguage);
		var newIndex = (currentIndex + 1) % languages.length;
		var newLang = languages[newIndex];
		setLanguage(newLang);
		// Recreate main menu with new language
		self.destroy();
		var newMainMenu = new MainMenu();
		game.addChild(newMainMenu);
	};
	return self;
});
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 () {
		// Check if we can start next wave (10 waves max per world, all worlds)
		var worldWave = (currentWave - 1) % 10 + 1;
		if (currentWave === 0) worldWave = 0;
		// Show button during tutorial or when can start next wave
		var showForTutorial = waveIndicator && waveIndicator.gameStarted && currentWave === 0;
		var showForNextWave = waveIndicator && waveIndicator.gameStarted && worldWave < 10 && !waveInProgress && enemies.length === 0;
		var showForWorld2Plus = waveIndicator && waveIndicator.gameStarted && worldWave < 10 && !waveInProgress && enemies.length === 0;
		if (showForTutorial || showForNextWave || showForWorld2Plus) {
			self.enabled = true;
			self.visible = true;
			buttonBackground.tint = 0x0088FF;
			self.alpha = 1;
			// Update button text based on whether this is the first wave start
			if (isFirstWaveStart) {
				buttonText.setText("Start Wave");
			} else {
				buttonText.setText("Next Wave");
			}
		} else {
			self.enabled = false;
			self.visible = false;
			buttonBackground.tint = 0x888888;
			self.alpha = 0.7;
		}
	};
	self.down = function () {
		if (!self.enabled) {
			return;
		}
		if (waveIndicator.gameStarted && !waveInProgress && enemies.length === 0) {
			// Handle first wave start for selected levels
			if (isFirstWaveStart) {
				// Don't increment currentWave, just clear the flag
				isFirstWaveStart = false;
			} else {
				// Normal wave progression - increment currentWave
				if (currentWave > 0 && (currentWave - 1) % 10 + 1 < 10) {
					currentWave++; // Increment to the next wave
				} else if (currentWave === 0) {
					currentWave = 1; // Start at wave 1 if we're at wave 0 (tutorial/fresh start)
				}
			}
			// Calculate current world and level for 10-wave system
			currentWorld = Math.ceil(currentWave / 10);
			currentLevel = (currentWave - 1) % 10 + 1;
			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(currentLevel);
			var enemyCount = waveIndicator.getEnemyCount(currentLevel);
			// Update wave counter display
			updateWaveCounter();
			var notification = game.addChild(new Notification("Wave " + currentLevel + " (" + waveType + " - " + enemyCount + " enemies) activated!"));
			notification.x = 2048 / 2;
			notification.y = grid.height - 150;
			// Play wave start sound
			LK.getSound('waveStart').play();
		}
	};
	return self;
});
var Notification = Container.expand(function (message) {
	var self = Container.call(this);
	var notificationGraphics = self.attachAsset('notification', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	var notificationText = new Text2(message, {
		size: 50,
		fill: 0x000000,
		weight: 800
	});
	notificationText.anchor.set(0.5, 0.5);
	// Stretch notification background to fit text with extra padding
	var minWidth = 600; // Increased minimum width for better visibility
	var calculatedWidth = Math.max(minWidth, notificationText.width + 150);
	notificationGraphics.width = calculatedWidth;
	notificationGraphics.height = 140; // Increased height for better visibility
	self.addChild(notificationText);
	self.alpha = 1;
	var fadeOutTime = 180; // Increased display time for better readability
	// Collision avoidance system
	self.checkCollisions = function () {
		if (!self.parent) return;
		var notifications = [];
		for (var i = 0; i < self.parent.children.length; i++) {
			var child = self.parent.children[i];
			if (child instanceof Notification && child !== self && child.alpha > 0) {
				notifications.push(child);
			}
		}
		// Sort notifications by creation order (older first)
		notifications.sort(function (a, b) {
			return (a._creationTime || 0) - (b._creationTime || 0);
		});
		// Check for overlaps and adjust position
		for (var i = 0; i < notifications.length; i++) {
			var other = notifications[i];
			var dx = Math.abs(self.x - other.x);
			var dy = Math.abs(self.y - other.y);
			var minDistance = 140; // Minimum vertical distance between notifications
			if (dx < calculatedWidth * 0.7 && dy < minDistance) {
				// Move this notification up to avoid overlap
				var offset = minDistance - dy + 10;
				self.y -= offset;
				// Ensure notification stays within screen bounds
				if (self.y < 100) {
					self.y = other.y + minDistance;
				}
			}
		}
	};
	// Store creation time for sorting
	self._creationTime = LK.ticks || Date.now();
	// Initial collision check
	LK.setTimeout(function () {
		self.checkCollisions();
	}, 1);
	self.update = function () {
		// Periodic collision checking during early life
		if (fadeOutTime > 150) {
			self.checkCollisions();
		}
		if (fadeOutTime > 0) {
			fadeOutTime--;
			self.alpha = Math.min(fadeOutTime / 180 * 2, 1);
		} else {
			self.destroy();
		}
	};
	// Add smooth entrance animation with tween
	self.scaleX = 0.3;
	self.scaleY = 0.3;
	tween(self, {
		scaleX: 1.0,
		scaleY: 1.0
	}, {
		duration: 200,
		easing: tween.backOut
	});
	return self;
});
var SourceTower = Container.expand(function (towerType) {
	var self = Container.call(this);
	self.towerType = towerType || 'default';
	// Increase size of base for easier touch
	var baseGraphics = self.attachAsset('tower', {
		anchorX: 0.5,
		anchorY: 0.5,
		scaleX: 1.5,
		scaleY: 1.5
	});
	switch (self.towerType) {
		case 'rapid':
			baseGraphics.tint = 0x00AAFF;
			break;
		case 'sniper':
			baseGraphics.tint = 0xFF5500;
			break;
		case 'splash':
			baseGraphics.tint = 0x33CC00;
			break;
		case 'slow':
			baseGraphics.tint = 0x9900FF;
			break;
		case 'poison':
			baseGraphics.tint = 0x00FFAA;
			break;
		default:
			baseGraphics.tint = 0xAAAAAA;
	}
	var towerCost = getTowerCost(self.towerType);
	// Add shadow for tower type label
	var typeLabelShadow = new Text2(self.towerType.charAt(0).toUpperCase() + self.towerType.slice(1), {
		size: 50,
		fill: 0x000000,
		weight: 800
	});
	typeLabelShadow.anchor.set(0.5, 0.5);
	typeLabelShadow.x = 4;
	typeLabelShadow.y = -20 + 4;
	self.addChild(typeLabelShadow);
	// Add tower type label
	var typeLabel = new Text2(self.towerType.charAt(0).toUpperCase() + self.towerType.slice(1), {
		size: 60,
		fill: 0xFFFFFF,
		weight: 800
	});
	typeLabel.anchor.set(0.5, 0.5);
	typeLabel.y = -25; // Position above center of tower
	self.addChild(typeLabel);
	// Add cost shadow
	var costLabelShadow = new Text2(towerCost, {
		size: 50,
		fill: 0x000000,
		weight: 800
	});
	costLabelShadow.anchor.set(0.5, 0.5);
	costLabelShadow.x = 4;
	costLabelShadow.y = 24 + 12;
	self.addChild(costLabelShadow);
	// Add cost label
	var costLabel = new Text2(towerCost + ' bits', {
		size: 60,
		fill: 0xFFD700,
		weight: 800
	});
	costLabel.anchor.set(0.5, 0.5);
	costLabel.y = 25 + 12;
	self.addChild(costLabel);
	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 StorySequence = Container.expand(function (worldNumber) {
	var self = Container.call(this);
	self.worldNumber = worldNumber;
	self.currentPanel = 0;
	self.panels = [];
	self.onComplete = null;
	// Position at center of screen
	self.x = 2048 / 2;
	self.y = 2732 / 2;
	// Semi-transparent background overlay
	var overlay = self.attachAsset('notification', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	overlay.width = 2048;
	overlay.height = 2732;
	overlay.tint = 0x000000;
	overlay.alpha = 0.7;
	self.getWorldStory = function (worldNumber) {
		switch (worldNumber) {
			case 1:
				return [{
					text: getText('story1_1'),
					image: 'agumon'
				}, {
					text: getText('story1_2'),
					image: 'gabumon'
				}, {
					text: getText('story1_3'),
					image: 'tentomon'
				}];
			case 2:
				return [{
					text: getText('story2_1'),
					image: 'palmon'
				}, {
					text: getText('story2_2'),
					image: 'gomamon'
				}, {
					text: getText('story2_3'),
					image: 'patamon'
				}];
			case 3:
				return [{
					text: getText('story3_1'),
					image: null
				}, {
					text: getText('story3_2'),
					image: null
				}, {
					text: getText('story3_3'),
					image: null
				}];
			case 4:
				return [{
					text: getText('story4_1'),
					image: 'angemon'
				}, {
					text: getText('story4_2'),
					image: 'magnaangemon'
				}, {
					text: getText('story4_3'),
					image: 'seraphimon'
				}];
			case 5:
				return [{
					text: getText('story5_1'),
					image: 'greymon'
				}, {
					text: getText('story5_2'),
					image: 'metalgreymon'
				}, {
					text: getText('story5_3'),
					image: 'wargreymon'
				}];
			case 6:
				return [{
					text: getText('story6_1'),
					image: 'garurumon'
				}, {
					text: getText('story6_2'),
					image: 'weregarurumon'
				}, {
					text: getText('story6_3'),
					image: 'metalgarurumon'
				}];
			default:
				return [{
					text: getText('storyDefault_1'),
					image: 'agumon'
				}, {
					text: getText('storyDefault_2'),
					image: 'gabumon'
				}, {
					text: getText('storyDefault_3'),
					image: 'tentomon'
				}];
		}
	};
	// World-specific story content
	var storyData = self.getWorldStory(worldNumber);
	// Create panels
	for (var i = 0; i < storyData.length; i++) {
		var panel = new ComicPanel(storyData[i].image, storyData[i].text);
		panel.x = (i - 1) * 650; // Position panels side by side
		self.addChild(panel);
		self.panels.push(panel);
	}
	// Navigation indicators
	var indicatorContainer = new Container();
	indicatorContainer.y = 250;
	self.addChild(indicatorContainer);
	for (var i = 0; i < self.panels.length; i++) {
		var indicator = indicatorContainer.attachAsset('towerLevelIndicator', {
			anchorX: 0.5,
			anchorY: 0.5
		});
		indicator.width = 20;
		indicator.height = 20;
		indicator.tint = i === 0 ? 0xffffff : 0x666666;
		indicator.x = (i - (self.panels.length - 1) / 2) * 40;
	}
	// Skip button
	var skipButton = new Container();
	var skipBg = skipButton.attachAsset('notification', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	skipBg.width = 150;
	skipBg.height = 60;
	skipBg.tint = 0x666666;
	var skipText = new Text2('Skip', {
		size: 40,
		fill: 0xffffff,
		weight: 800
	});
	skipText.anchor.set(0.5, 0.5);
	skipButton.addChild(skipText);
	skipButton.x = 400;
	skipButton.y = -300;
	self.addChild(skipButton);
	skipButton.down = function () {
		self.complete();
	};
	self.showPanel = function (index) {
		if (index < 0 || index >= self.panels.length) return;
		// Hide all panels
		for (var i = 0; i < self.panels.length; i++) {
			self.panels[i].alpha = 0;
			indicatorContainer.children[i].tint = 0x666666;
		}
		// Show current panel
		self.panels[index].show();
		indicatorContainer.children[index].tint = 0xffffff;
		self.currentPanel = index;
	};
	self.nextPanel = function () {
		if (self.currentPanel < self.panels.length - 1) {
			self.showPanel(self.currentPanel + 1);
		} else {
			self.complete();
		}
	};
	self.complete = function () {
		if (self.onComplete) {
			self.onComplete();
		}
		self.destroy();
	};
	// Show first panel
	self.showPanel(0);
	// Only set up auto-advance for panels after the first one
	var autoAdvanceTimer = null;
	self.down = function () {
		if (autoAdvanceTimer) {
			LK.clearTimeout(autoAdvanceTimer);
		}
		self.nextPanel();
	};
	return self;
});
var Tower = Container.expand(function (id) {
	var self = Container.call(this);
	self.id = id || 'default';
	self.level = 1;
	self.maxLevel = 6;
	self.gridX = 0;
	self.gridY = 0;
	self.range = 3 * CELL_SIZE;
	// Tower health system
	self.maxHealth = 100;
	self.health = self.maxHealth;
	// 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 'sniper':
				// Sniper: base 5, +0.8 per level, but final upgrade gets a huge boost
				if (self.level === self.maxLevel) {
					return 12 * CELL_SIZE; // Significantly increased range for max level
				}
				return (5 + (self.level - 1) * 0.8) * CELL_SIZE;
			case 'splash':
				// Splash: base 2, +0.2 per level (max ~4 blocks at max level)
				return (2 + (self.level - 1) * 0.2) * CELL_SIZE;
			case 'rapid':
				// Rapid: base 2.5, +0.5 per level
				return (2.5 + (self.level - 1) * 0.5) * CELL_SIZE;
			case 'slow':
				// Slow: base 3.5, +0.5 per level
				return (3.5 + (self.level - 1) * 0.5) * CELL_SIZE;
			case 'poison':
				// Poison: base 3.2, +0.5 per level
				return (3.2 + (self.level - 1) * 0.5) * CELL_SIZE;
			default:
				// Default: base 3, +0.5 per level
				return (3 + (self.level - 1) * 0.5) * CELL_SIZE;
		}
	};
	self.cellsInRange = [];
	self.fireRate = 60;
	self.bulletSpeed = 5;
	self.damage = 10;
	self.lastFired = 0;
	self.targetEnemy = null;
	switch (self.id) {
		case 'agumon':
			// Fire-based attacks, burns enemies
			self.fireRate = 60;
			self.damage = 12;
			self.range = 3 * CELL_SIZE;
			self.bulletSpeed = 6;
			self.maxHealth = 120; // Tanky fire dragon
			break;
		case 'gabumon':
			// Ice-based attacks, freezes enemies
			self.fireRate = 55;
			self.damage = 10;
			self.range = 3.2 * CELL_SIZE;
			self.bulletSpeed = 6;
			self.maxHealth = 110; // Ice wolf durability
			break;
		case 'tentomon':
			// Electric paralysis attacks
			self.fireRate = 70;
			self.damage = 14;
			self.range = 3.5 * CELL_SIZE;
			self.bulletSpeed = 8;
			self.maxHealth = 90; // Electric insect, moderate health
			break;
		case 'palmon':
			// Poison attacks that spread
			self.fireRate = 65;
			self.damage = 11;
			self.range = 3 * CELL_SIZE;
			self.bulletSpeed = 5;
			self.maxHealth = 100; // Plant creature, balanced
			break;
		case 'gomamon':
			// Water/moisture attacks, weakens fire resistance
			self.fireRate = 60;
			self.damage = 9;
			self.range = 3.3 * CELL_SIZE;
			self.bulletSpeed = 5;
			self.maxHealth = 105; // Aquatic mammal, decent health
			break;
		case 'patamon':
			// Healing and support abilities
			self.fireRate = 80;
			self.damage = 8;
			self.range = 4 * CELL_SIZE; // Larger healing range
			self.bulletSpeed = 7;
			self.maxHealth = 85; // Angel creature, less physical but supportive
			break;
	}
	self.health = self.maxHealth;
	var baseGraphics = self.attachAsset('tower', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	switch (self.id) {
		case 'gabumon':
			//{93} // Blue colors for Gabumon
			baseGraphics.tint = 0x00AAFF;
			break;
		case 'tentomon':
			//{96} // Red colors for Tentomon
			baseGraphics.tint = 0xFF5500;
			break;
		case 'palmon':
			//{99} // Green colors for Palmon
			baseGraphics.tint = 0x33CC00;
			break;
		case 'gomamon':
			//{9c} // Purple colors for Gomamon
			baseGraphics.tint = 0x9900FF;
			break;
		case 'patamon':
			//{9f} // Cyan colors for Patamon
			baseGraphics.tint = 0x00FFAA;
			break;
		default:
			//{9i} // Agumon default
			baseGraphics.tint = 0xAAAAAA;
	}
	// Tower health bar
	var towerHealthBarOutline = self.attachAsset('healthBarOutline', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	var towerHealthBar = self.attachAsset('healthBar', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	towerHealthBarOutline.width = 80;
	towerHealthBarOutline.height = 8;
	towerHealthBarOutline.tint = 0x000000;
	towerHealthBar.width = 76;
	towerHealthBar.height = 6;
	towerHealthBar.tint = 0x00ff00;
	towerHealthBarOutline.y = towerHealthBar.y = -CELL_SIZE - 15;
	self.towerHealthBar = towerHealthBar;
	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 = 0x000000;
		var towerLevelIndicator = dot.attachAsset('towerLevelIndicator', {
			anchorX: 0.5,
			anchorY: 0.5
		});
		towerLevelIndicator.width = dotSize;
		towerLevelIndicator.height = dotSize;
		towerLevelIndicator.tint = 0xCCCCCC;
		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(self.id, {
		anchorX: 0.5,
		anchorY: 0.5
	});
	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 = 0xFFFFFF;
			} else {
				switch (self.id) {
					case 'rapid':
						towerLevelIndicator.tint = 0x00AAFF;
						break;
					case 'sniper':
						towerLevelIndicator.tint = 0xFF5500;
						break;
					case 'splash':
						towerLevelIndicator.tint = 0x33CC00;
						break;
					case 'slow':
						towerLevelIndicator.tint = 0x9900FF;
						break;
					case 'poison':
						towerLevelIndicator.tint = 0x00FFAA;
						break;
					default:
						towerLevelIndicator.tint = 0xAAAAAA;
				}
			}
		}
	};
	self.updateLevelIndicators();
	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++;
				// Play tower upgrade sound
				LK.getSound('towerUpgrade').play();
				// --- HEALTH UPGRADE LOGIC ---
				// Increase maxHealth and heal tower on upgrade
				var prevMaxHealth = self.maxHealth;
				// Health scaling per tower type
				switch (self.id) {
					case 'agumon':
						self.maxHealth = 120 + (self.level - 1) * 20;
						break;
					case 'gabumon':
						self.maxHealth = 110 + (self.level - 1) * 18;
						break;
					case 'tentomon':
						self.maxHealth = 90 + (self.level - 1) * 15;
						break;
					case 'palmon':
						self.maxHealth = 100 + (self.level - 1) * 18;
						break;
					case 'gomamon':
						self.maxHealth = 105 + (self.level - 1) * 17;
						break;
					case 'patamon':
						self.maxHealth = 85 + (self.level - 1) * 14;
						break;
					default:
						self.maxHealth = 100 + (self.level - 1) * 15;
						break;
				}
				// Heal tower to new max health on upgrade
				self.health = self.maxHealth;
				// --- DAMAGE/FIRERATE UPGRADE LOGIC ---
				// Tweak last upgrade so it's not overpowered
				if (self.id === 'rapid') {
					if (self.level === self.maxLevel) {
						// Last upgrade: only a moderate boost, not double
						self.fireRate = Math.max(8, 30 - self.level * 6); // was 4, now 8 min
						self.damage = 5 + self.level * 5; // was 10, now 5 per level
						self.bulletSpeed = 7 + self.level * 1.2; // was 2.4, now 1.2 per level
					} 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) {
						// Last upgrade: only a moderate boost, not double
						self.fireRate = Math.max(10, 60 - self.level * 12); // was 5, now 10 min
						self.damage = 10 + self.level * 10; // was 20, now 10 per level
						self.bulletSpeed = 5 + self.level * 1.2; // was 2.4, now 1.2 per level
					} 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();
				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
							});
						}
					});
				}
				// Check if tower reached maximum level and award bonus
				if (self.level === self.maxLevel) {
					// Award 300 bits and security points for reaching max level
					setGold(gold + 300);
					score += 300;
					// Save security points to storage
					storage.securityPoints = score;
					updateUI();
					var notification = game.addChild(new Notification("Max level reached! +300 bits & security points!"));
					notification.x = 2048 / 2;
					notification.y = grid.height - 50;
				}
				return true;
			} else {
				var notification = game.addChild(new Notification("Not enough bits 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 () {
		// Update tower health bar
		self.towerHealthBar.width = self.health / self.maxHealth * 76;
		if (self.health / self.maxHealth > 0.6) {
			self.towerHealthBar.tint = 0x00ff00; // Green
		} else if (self.health / self.maxHealth > 0.3) {
			self.towerHealthBar.tint = 0xffff00; // Yellow
		} else {
			self.towerHealthBar.tint = 0xff0000; // Red
		}
		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;
			var effectiveFireRate = gameSpeed > 1 ? Math.max(1, Math.floor(self.fireRate / gameSpeed)) : self.fireRate;
			if (LK.ticks - self.lastFired >= effectiveFireRate) {
				self.fire();
				self.lastFired = LK.ticks;
			}
		}
	};
	self.down = function (x, y, obj) {
		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.3;
		// Add evolution arrow if digivice is available and tower level is appropriate
		function canDigivolve() {
			if (self.level < 2) return false; // Need at least level 2
			var hasDigivice = false;
			if (self.level >= 2 && self.level <= 3 && storage.digiviceC) hasDigivice = true;
			if (self.level >= 4 && self.level <= 5 && storage.digiviceB) hasDigivice = true;
			if (self.level >= 6 && storage.digiviceA) hasDigivice = true;
			return hasDigivice;
		}
		if (canDigivolve()) {
			var evolutionArrow = rangeIndicator.attachAsset('arrow', {
				anchorX: 0.5,
				anchorY: 0.5
			});
			evolutionArrow.width = 60;
			evolutionArrow.height = 60;
			evolutionArrow.tint = 0xFF6600;
			evolutionArrow.x = self.getRange() + 40;
			evolutionArrow.y = -40;
			evolutionArrow.rotation = -Math.PI / 4; // Point diagonally up-right
			// Add glow effect
			evolutionArrow.alpha = 0.8;
			var _glowTween = function glowTween() {
				tween(evolutionArrow, {
					alpha: 1.0,
					scaleX: 1.2,
					scaleY: 1.2
				}, {
					duration: 500,
					easing: tween.easeInOut,
					onFinish: function onFinish() {
						tween(evolutionArrow, {
							alpha: 0.8,
							scaleX: 1.0,
							scaleY: 1.0
						}, {
							duration: 500,
							easing: tween.easeInOut,
							onFinish: _glowTween
						});
					}
				});
			};
			_glowTween();
		}
		// Check if clicked on evolution arrow
		var clickedOnEvolutionArrow = false;
		if (rangeIndicator.children.length > 1) {
			// Has evolution arrow
			var evolutionArrow = rangeIndicator.children[1];
			var arrowGlobalPos = rangeIndicator.toGlobal(evolutionArrow.position);
			var arrowX = arrowGlobalPos.x;
			var arrowY = arrowGlobalPos.y;
			if (Math.abs(x - arrowX) < 40 && Math.abs(y - arrowY) < 40) {
				clickedOnEvolutionArrow = true;
				// Trigger digivolution
				var evolutionCost = getTowerCost(self.id) * 2;
				if (gold >= evolutionCost) {
					setGold(gold - evolutionCost);
					// Apply evolution effects
					self.damage *= 1.5;
					self.fireRate = Math.max(5, Math.floor(self.fireRate * 0.8));
					var notification = game.addChild(new Notification(self.id.toUpperCase() + " DIGIVOLVED!"));
					notification.x = 2048 / 2;
					notification.y = grid.height - 50;
					// Visual evolution effect
					LK.effects.flashObject(self, 0xFF6600, 1000);
					// Remove evolution arrow after use
					rangeIndicator.removeChild(evolutionArrow);
				} else {
					var notification = game.addChild(new Notification("Not enough bits to digivolve!"));
					notification.x = 2048 / 2;
					notification.y = grid.height - 50;
				}
			}
		}
		if (!clickedOnEvolutionArrow) {
			var upgradeMenu = new UpgradeMenu(self);
			game.addChild(upgradeMenu);
			upgradeMenu.x = 2048 / 2;
			tween(upgradeMenu, {
				y: 2732 - 225
			}, {
				duration: 200,
				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 () {
		// Patamon healing ability - heal nearby towers instead of attacking
		if (self.id === 'patamon' && LK.ticks % 120 === 0) {
			// Every 2 seconds
			var healingRange = self.getRange();
			var healAmount = 5 + self.level * 2;
			var towersHealed = 0;
			for (var i = 0; i < towers.length; i++) {
				var otherTower = towers[i];
				if (otherTower !== self && otherTower.health < otherTower.maxHealth) {
					var dx = otherTower.x - self.x;
					var dy = otherTower.y - self.y;
					var distance = Math.sqrt(dx * dx + dy * dy);
					if (distance <= healingRange) {
						otherTower.health = Math.min(otherTower.maxHealth, otherTower.health + healAmount);
						otherTower.towerHealthBar.width = otherTower.health / otherTower.maxHealth * 76;
						towersHealed++;
						// Visual healing effect
						var healEffect = new EffectIndicator(otherTower.x, otherTower.y, 'heal');
						game.addChild(healEffect);
					}
				}
			}
			if (towersHealed > 0) {
				// Show healing indicator on Patamon
				var healSelfEffect = new EffectIndicator(self.x, self.y, 'heal');
				game.addChild(healSelfEffect);
			}
		}
		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 and add special effects
				bullet.type = self.id;
				bullet.sourceTowerLevel = self.level;
				// Special attack patterns for different evolutionary lines
				switch (self.id) {
					case 'agumon':
						// Fire attacks that can burn enemies
						// Burn chance: 5% at level 1, up to 50% at max level (linear)
						bullet.burnChance = Math.min(0.05 + (self.level - 1) * 0.09, 0.5);
						break;
					case 'gabumon':
						// Ice attacks that can freeze enemies
						// Freeze chance: 5% at level 1, up to 50% at max level (linear)
						bullet.freezeChance = Math.min(0.05 + (self.level - 1) * 0.09, 0.5);
						break;
					case 'tentomon':
						// Electric attacks that can paralyze multiple enemies
						// Paralyze chance: 5% at level 1, up to 55% at max level (linear)
						bullet.paralyzeChance = Math.min(0.05 + (self.level - 1) * 0.10, 0.55);
						bullet.paralyzeArea = CELL_SIZE * (1 + self.level * 0.2);
						break;
					case 'palmon':
						// Poison attacks that spread to nearby enemies
						// Poison spread chance: 5% at level 1, up to 50% at max level (linear)
						bullet.poisonSpread = true;
						bullet.poisonSpreadChance = Math.min(0.05 + (self.level - 1) * 0.09, 0.5);
						bullet.poisonRadius = CELL_SIZE * (0.8 + self.level * 0.15);
						break;
					case 'gomamon':
						// Water attacks that make enemies wet (vulnerable to freeze, resistant to burn)
						bullet.moistureEffect = true;
						bullet.moistureDuration = 180 + self.level * 30;
						break;
				}
				// Customize bullet appearance based on tower type
				switch (self.id) {
					case 'rapid':
						bullet.children[0].tint = 0x00AAFF;
						bullet.children[0].width = 20;
						bullet.children[0].height = 20;
						break;
					case 'sniper':
						bullet.children[0].tint = 0xFF5500;
						bullet.children[0].width = 15;
						bullet.children[0].height = 15;
						break;
					case 'splash':
						bullet.children[0].tint = 0x33CC00;
						bullet.children[0].width = 40;
						bullet.children[0].height = 40;
						break;
					case 'slow':
						bullet.children[0].tint = 0x9900FF;
						bullet.children[0].width = 35;
						bullet.children[0].height = 35;
						break;
					case 'poison':
						bullet.children[0].tint = 0x00FFAA;
						bullet.children[0].width = 35;
						bullet.children[0].height = 35;
						break;
				}
				game.addChild(bullet);
				bullets.push(bullet);
				self.targetEnemy.bulletsTargetingThis.push(bullet);
				// --- 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;
		// Mark cells as occupied by tower (type 1 = wall/occupied)
		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();
	};
	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;
		switch (self.towerType) {
			case 'rapid':
				previewGraphics.tint = 0x00AAFF;
				break;
			case 'sniper':
				previewGraphics.tint = 0xFF5500;
				break;
			case 'splash':
				previewGraphics.tint = 0x33CC00;
				break;
			case 'slow':
				previewGraphics.tint = 0x9900FF;
				break;
			case 'poison':
				previewGraphics.tint = 0x00FFAA;
				break;
			default:
				previewGraphics.tint = 0xAAAAAA;
		}
		if (!self.canPlace || !self.hasEnoughGold) {
			previewGraphics.tint = 0xFF0000;
		}
	};
	self.updatePlacementStatus = function () {
		var validGridPlacement = true;
		// Check if tower would be placed within valid grid bounds
		if (self.gridX < 0 || self.gridY < 0 || self.gridX + 1 >= grid.cells.length || self.gridY + 1 >= grid.cells[0].length) {
			validGridPlacement = false;
		} else {
			// Check if all 4 cells for the 2x2 tower are available (not on enemy path)
			for (var i = 0; i < 2; i++) {
				for (var j = 0; j < 2; j++) {
					var cell = grid.getCell(self.gridX + i, self.gridY + j);
					// Only allow placement on wall cells (type 1) - not on path, spawn, or goal
					if (!cell || cell.type !== 1) {
						validGridPlacement = false;
						break;
					}
				}
				if (!validGridPlacement) {
					break;
				}
			}
		}
		self.blockedByEnemy = false;
		// Remove enemy blocking detection since towers can only be placed on wall tiles
		// which enemies cannot occupy anyway
		self.blockedByEnemy = false;
		self.canPlace = validGridPlacement;
		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 TutorialSequence = Container.expand(function () {
	var self = Container.call(this);
	console.log("Starting interactive tutorial sequence");
	self.currentStep = 0;
	self.onComplete = null;
	self.tutorialActive = true;
	self.waitingForUserAction = false;
	self.stepCompleted = false;
	// Tutorial steps data
	self.tutorialSteps = [{
		textKey: 'tutorialWelcome',
		action: 'intro'
	}, {
		textKey: 'tutorialStep1',
		action: 'placeTower',
		completeTextKey: 'tutorialStep1Complete'
	}, {
		textKey: 'tutorialStep2',
		action: 'startWave',
		completeTextKey: 'tutorialStep2Complete'
	}, {
		textKey: 'tutorialStep3',
		action: 'upgradeTower',
		completeTextKey: 'tutorialStep3Complete'
	}, {
		textKey: 'tutorialCompleted',
		action: 'finish'
	}];
	// Semi-transparent background overlay
	var overlay = self.attachAsset('notification', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	overlay.width = 2048;
	overlay.height = 2732;
	overlay.tint = 0x000000;
	overlay.alpha = 0.4;
	// Instruction panel
	var instructionBg = self.attachAsset('notification', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	instructionBg.width = 1600;
	instructionBg.height = 400;
	instructionBg.tint = 0x222222;
	instructionBg.alpha = 0.95;
	instructionBg.x = 2048 / 2;
	instructionBg.y = 400;
	var instructionText = new Text2(getText(self.tutorialSteps[0].textKey), {
		size: 50,
		fill: 0xFFFFFF,
		weight: 600
	});
	instructionText.anchor.set(0.5, 0.5);
	instructionText.wordWrap = true;
	instructionText.wordWrapWidth = 1500;
	instructionText.x = 2048 / 2;
	instructionText.y = 400;
	self.addChild(instructionText);
	// Next button
	var nextButton = new Container();
	var nextBg = nextButton.attachAsset('notification', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	nextBg.width = 250;
	nextBg.height = 80;
	nextBg.tint = 0x00AA00;
	var nextText = new Text2(getText('nextStep'), {
		size: 45,
		fill: 0xFFFFFF,
		weight: 800
	});
	nextText.anchor.set(0.5, 0.5);
	nextButton.addChild(nextText);
	nextButton.x = 2048 / 2;
	nextButton.y = 580;
	self.addChild(nextButton);
	// Skip button
	var skipButton = new Container();
	var skipBg = skipButton.attachAsset('notification', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	skipBg.width = 200;
	skipBg.height = 80;
	skipBg.tint = 0x666666;
	var skipText = new Text2(getText('skipTutorial'), {
		size: 35,
		fill: 0xffffff,
		weight: 800
	});
	skipText.anchor.set(0.5, 0.5);
	skipButton.addChild(skipText);
	skipButton.x = 2048 - 150;
	skipButton.y = 150;
	self.addChild(skipButton);
	// Button handlers
	nextButton.down = function () {
		self.nextStep();
	};
	skipButton.down = function () {
		self.complete();
	};
	// Update button state
	self.updateButtonState = function () {
		if (self.waitingForUserAction && !self.stepCompleted) {
			nextBg.tint = 0x666666;
			nextButton.alpha = 0.5;
			nextText.setText('Complete Action');
		} else {
			nextBg.tint = 0x00AA00;
			nextButton.alpha = 1.0;
			nextText.setText(getText('nextStep'));
		}
	};
	self.nextStep = function () {
		// Don't advance if waiting for user action and step not completed
		if (self.waitingForUserAction && !self.stepCompleted) {
			return;
		}
		self.currentStep++;
		if (self.currentStep >= self.tutorialSteps.length) {
			self.complete();
			return;
		}
		self.waitingForUserAction = false;
		self.stepCompleted = false;
		var step = self.tutorialSteps[self.currentStep];
		instructionText.setText(getText(step.textKey));
		// Handle specific step actions
		switch (step.action) {
			case 'placeTower':
				self.waitingForUserAction = true;
				// Give enough gold for tutorial
				setGold(200);
				updateUI();
				break;
			case 'startWave':
				self.waitingForUserAction = true;
				waveIndicator.gameStarted = true;
				break;
			case 'upgradeTower':
				self.waitingForUserAction = true;
				// Give extra gold for upgrades
				setGold(gold + 200);
				updateUI();
				break;
		}
		self.updateButtonState();
	};
	// Check for step completion
	self.update = function () {
		if (!self.waitingForUserAction || self.stepCompleted) {
			return;
		}
		var step = self.tutorialSteps[self.currentStep];
		switch (step.action) {
			case 'placeTower':
				if (towers.length > 0) {
					self.stepCompleted = true;
					if (step.completeTextKey) {
						instructionText.setText(getText(step.completeTextKey));
					}
					self.updateButtonState();
				}
				break;
			case 'startWave':
				if (waveInProgress || currentWave > 0) {
					self.stepCompleted = true;
					if (step.completeTextKey) {
						instructionText.setText(getText(step.completeTextKey));
					}
					self.updateButtonState();
				}
				break;
			case 'upgradeTower':
				// Check if any tower has been upgraded or if upgrade menu was closed
				var hasUpgradedTower = false;
				for (var i = 0; i < towers.length; i++) {
					if (towers[i].level > 1) {
						hasUpgradedTower = true;
						break;
					}
				}
				// Also check if upgrade menu was opened and closed
				var upgradeMenuExists = false;
				for (var i = 0; i < game.children.length; i++) {
					if (game.children[i] instanceof UpgradeMenu) {
						upgradeMenuExists = true;
						break;
					}
				}
				// If tower was upgraded OR if menu was opened and then closed
				if (hasUpgradedTower || !upgradeMenuExists && self.menuWasOpened) {
					self.stepCompleted = true;
					if (step.completeTextKey) {
						instructionText.setText(getText(step.completeTextKey));
					}
					self.updateButtonState();
				}
				// Track if menu was opened
				if (upgradeMenuExists) {
					self.menuWasOpened = true;
				}
				break;
		}
	};
	// Initialize first step
	self.updateButtonState();
	self.complete = function () {
		self.tutorialActive = false;
		// Mark tutorial as completed globally
		tutorialCompleted = true;
		tutorialInProgress = false;
		// Stop all music first to prevent mixing
		LK.stopMusic();
		// Clear tutorial game state
		currentWave = 0;
		currentWorld = 1;
		currentLevel = 1;
		waveInProgress = false;
		waveSpawned = false;
		waveIndicator.gameStarted = false;
		gold = 80;
		lives = 20;
		score = 0;
		enemiesKilledInWave = 0;
		// Clear all game entities
		while (enemies.length > 0) {
			var enemy = enemies.pop();
			if (enemy.parent) {
				enemy.parent.removeChild(enemy);
			}
			if (enemy.shadow && enemy.shadow.parent) {
				enemy.shadow.parent.removeChild(enemy.shadow);
			}
		}
		while (towers.length > 0) {
			var tower = towers.pop();
			if (tower.parent) {
				tower.parent.removeChild(tower);
			}
		}
		while (bullets.length > 0) {
			var bullet = bullets.pop();
			if (bullet.parent) {
				bullet.parent.removeChild(bullet);
			}
		}
		while (alliedUnits.length > 0) {
			var unit = alliedUnits.pop();
			if (unit.parent) {
				unit.parent.removeChild(unit);
			}
		}
		// Clear grid state
		for (var i = 0; i < 24; i++) {
			for (var j = 0; j < 35; j++) {
				if (grid.cells[i] && grid.cells[i][j]) {
					grid.cells[i][j].towersInRange = [];
				}
			}
		}
		// Reset UI
		updateUI();
		updateWaveCounter();
		if (self.onComplete) {
			self.onComplete();
		}
		self.destroy();
		// Wait before creating main menu to ensure clean transition
		LK.setTimeout(function () {
			// Return to main menu after tutorial
			var mainMenu = new MainMenu();
			game.addChild(mainMenu);
		}, 500);
	};
	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 = 600;
	menuBackground.tint = 0x444444;
	menuBackground.alpha = 0.9;
	var towerTypeText = new Text2(self.tower.id.charAt(0).toUpperCase() + self.tower.id.slice(1) + ' Tower', {
		size: 80,
		fill: 0xFFFFFF,
		weight: 800
	});
	towerTypeText.anchor.set(0, 0);
	towerTypeText.x = -840;
	towerTypeText.y = -160;
	self.addChild(towerTypeText);
	var statsText = new Text2('Level: ' + self.tower.level + '/' + self.tower.maxLevel + '\nHealth: ' + self.tower.health + '/' + self.tower.maxHealth + '\nDamage: ' + self.tower.damage + '\nFire Rate: ' + (60 / self.tower.fireRate).toFixed(1) + '/s', {
		size: 60,
		fill: 0xFFFFFF,
		weight: 400
	});
	statsText.anchor.set(0, 0.5);
	statsText.x = -840;
	statsText.y = 50;
	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 + ' bits', {
		size: 60,
		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 + ' bits', {
		size: 60,
		fill: 0xFFFFFF,
		weight: 800
	});
	sellButtonText.anchor.set(0.5, 0.5);
	sellButton.addChild(sellButtonText);
	upgradeButton.y = -75;
	sellButton.y = 75;
	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) {
		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('Level: ' + self.tower.level + '/' + self.tower.maxLevel + '\nDamage: ' + self.tower.damage + '\nFire Rate: ' + (60 / self.tower.fireRate).toFixed(1) + '/s');
			buttonText.setText('Upgrade: ' + upgradeCost + ' bits');
			var totalInvestment = self.tower.getTotalValue ? self.tower.getTotalValue() : 0;
			var sellValue = Math.floor(totalInvestment * 0.6);
			sellButtonText.setText('Sell: +' + sellValue + ' bits');
			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) {
		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 + " bits!"));
		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);
					}
				}
			}
		}
		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();
		// Sincronizar el mapa de visualización después de vender torre
		syncVisualizationMap();
		worldRenderer.updateWorldGraphics(currentWorld, mapVisualization);
		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) {
		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 + ' bits';
		if (buttonText.text !== newText) {
			buttonText.setText(newText);
		}
	};
	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;
	// Add shadow for start text
	var startTextShadow = new Text2("Start Game", {
		size: 50,
		fill: 0x000000,
		weight: 800
	});
	startTextShadow.anchor.set(0.5, 0.5);
	startTextShadow.x = 4;
	startTextShadow.y = 4;
	startMarker.addChild(startTextShadow);
	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!");
			startTextShadow.setText("Started!");
			// Make sure shadow position remains correct after text change
			startTextShadow.x = 4;
			startTextShadow.y = 4;
			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 shadow for wave type - 30% smaller than before
		var waveTypeShadow = new Text2(waveType, {
			size: 56,
			fill: 0x000000,
			weight: 800
		});
		waveTypeShadow.anchor.set(0.5, 0.5);
		waveTypeShadow.x = 4;
		waveTypeShadow.y = 4;
		marker.addChild(waveTypeShadow);
		// Add wave type text - 30% smaller than before
		var waveTypeText = new Text2(waveType, {
			size: 56,
			fill: 0xFFFFFF,
			weight: 800
		});
		waveTypeText.anchor.set(0.5, 0.5);
		waveTypeText.y = 0;
		marker.addChild(waveTypeText);
		// Add shadow for wave number - 20% larger than before
		var waveNumShadow = new Text2((i + 1).toString(), {
			size: 48,
			fill: 0x000000,
			weight: 800
		});
		waveNumShadow.anchor.set(1.0, 1.0);
		waveNumShadow.x = blockWidth / 2 - 16 + 5;
		waveNumShadow.y = block.height / 2 - 12 + 5;
		marker.addChild(waveNumShadow);
		// Main wave number text - 20% larger than before
		var waveNum = new Text2((i + 1).toString(), {
			size: 48,
			fill: 0xFFFFFF,
			weight: 800
		});
		waveNum.anchor.set(1.0, 1.0);
		waveNum.x = blockWidth / 2 - 16;
		waveNum.y = block.height / 2 - 12;
		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;
		// Fix positioning to properly show first wave when currentWave is 0
		var displayWave = Math.max(0, currentWave);
		var moveAmount = (progress + displayWave) * 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];
			// Fix comparison to properly handle wave 0 and 1
			if (i - 1 < displayWave) {
				block.alpha = .5;
			}
		}
		self.handleWaveProgression = function () {
			if (!self.gameStarted) {
				return;
			}
			// Waves no longer advance automatically - they must be triggered manually via NextWaveButton
			// This function now only handles the initial game start
		};
		self.handleWaveProgression();
	};
	return self;
});
var WorldRenderer = Container.expand(function () {
	var self = Container.call(this);
	self.currentWorld = 1;
	self.backgroundTiles = [];
	self.pathTiles = [];
	self.wallTiles = [];
	self.sceneryElements = [];
	self.getWorldAssets = function (worldNumber) {
		switch (worldNumber) {
			case 1:
				return {
					background: 'forestBg',
					path: 'forestPath',
					wall: 'forestWall',
					scenery: 'forestScenery',
					ambient: 0x90ee90
				};
			case 2:
				return {
					background: 'desertBg',
					path: 'desertPath',
					wall: 'desertWall',
					scenery: 'desertScenery',
					ambient: 0xffd700
				};
			case 3:
				return {
					background: 'glacierBg',
					path: 'glacierPath',
					wall: 'glacierWall',
					scenery: 'glacierScenery',
					ambient: 0xe6f3ff
				};
			case 4:
				return {
					background: 'villageBg',
					path: 'villagePath',
					wall: 'villageWall',
					scenery: 'villageScenery',
					ambient: 0xf0e68c
				};
			case 5:
				return {
					background: 'infernoBg',
					path: 'infernoPath',
					wall: 'infernoWall',
					scenery: 'infernoScenery',
					ambient: 0xff6347
				};
			case 6:
				return {
					background: 'techLabBg',
					path: 'techLabPath',
					wall: 'techLabWall',
					scenery: 'techLabScenery',
					ambient: 0x87ceeb
				};
			default:
				return {
					background: 'forestBg',
					path: 'forestPath',
					wall: 'forestWall',
					scenery: 'forestScenery',
					ambient: 0x90ee90
				};
		}
	};
	self.updateWorldGraphics = function (worldNumber, gridInstance) {
		// Forzar renderizado siempre, incluso si el mundo no cambia
		self.currentWorld = worldNumber;
		var worldAssets = self.getWorldAssets(worldNumber);
		// Clear existing tiles
		while (self.backgroundTiles.length) {
			self.removeChild(self.backgroundTiles.pop());
		}
		while (self.pathTiles.length) {
			self.removeChild(self.pathTiles.pop());
		}
		while (self.wallTiles.length) {
			self.removeChild(self.wallTiles.pop());
		}
		while (self.sceneryElements.length) {
			self.removeChild(self.sceneryElements.pop());
		}
		// Create new tiles based on current world using the correct assets
		var gridWidth = 24;
		var gridHeight = 35;
		for (var i = 0; i < gridWidth; i++) {
			for (var j = 0; j < gridHeight; j++) {
				// Create background tile using world-specific assets
				var bgTile = self.attachAsset(worldAssets.background, {
					anchorX: 0,
					anchorY: 0
				});
				bgTile.x = i * CELL_SIZE;
				bgTile.y = j * CELL_SIZE;
				self.backgroundTiles.push(bgTile);
				// Add world-specific scenery elements randomly
				if (Math.random() < 0.15) {
					// 15% chance for scenery - use world-specific scenery assets
					var scenery = self.attachAsset(worldAssets.scenery, {
						anchorX: 0.5,
						anchorY: 0.5
					});
					scenery.x = i * CELL_SIZE + CELL_SIZE / 2;
					scenery.y = j * CELL_SIZE + CELL_SIZE / 2;
					// World-specific scenery shapes
					switch (worldNumber) {
						case 1:
							// Forest - trees/bushes (ellipses)
							scenery.scaleY = 1.2 + Math.random() * 0.8;
							break;
						case 2:
							// Desert - cacti/rocks (tall thin or wide)
							if (Math.random() < 0.5) {
								scenery.scaleX = 0.6;
								scenery.scaleY = 1.8; // Tall cactus
							} else {
								scenery.scaleX = 1.4;
								scenery.scaleY = 0.7; // Wide rock
							}
							break;
						case 3:
							// Glacier - ice crystals
							scenery.scaleX = 0.8 + Math.random() * 0.4;
							scenery.scaleY = 0.8 + Math.random() * 0.4;
							scenery.alpha = 0.7;
							break;
						case 4:
							// Village - small structures
							scenery.scaleX = 1.2;
							scenery.scaleY = 1.0;
							break;
						case 5:
							// Technology - machinery
							scenery.scaleX = 0.9;
							scenery.scaleY = 0.9;
							scenery.alpha = 0.8;
							break;
						case 6:
							// Hell - lava bubbles/flames
							scenery.scaleX = 1.1 + Math.random() * 0.6;
							scenery.scaleY = 1.1 + Math.random() * 0.6;
							scenery.alpha = 0.9;
							break;
					}
					self.sceneryElements.push(scenery);
				}
			}
		}
		// Overlay path and wall tiles on top of scenery
		if (gridInstance && gridInstance.cells) {
			for (var i = 0; i < Math.min(gridWidth, gridInstance.cells.length); i++) {
				for (var j = 0; j < Math.min(gridHeight, gridInstance.cells[i].length); j++) {
					var cell = gridInstance.cells[i][j];
					if (cell.type === 0 || cell.type === 2 || cell.type === 3) {
						// Path, spawn, or goal - use world-specific path assets
						var pathTile = self.attachAsset(worldAssets.path, {
							anchorX: 0,
							anchorY: 0
						});
						pathTile.x = i * CELL_SIZE;
						pathTile.y = j * CELL_SIZE;
						pathTile.alpha = 0.95;
						self.pathTiles.push(pathTile);
					} else if (cell.type === 1) {
						// Wall - use appropriate wall tile based on world
						var wallTile = self.attachAsset(worldAssets.wall, {
							anchorX: 0,
							anchorY: 0
						});
						wallTile.x = i * CELL_SIZE;
						wallTile.y = j * CELL_SIZE;
						wallTile.alpha = 0.98;
						self.wallTiles.push(wallTile);
					}
				}
			}
		}
		// Asegurar que WorldRenderer esté en el fondo, debajo de todo
		if (self.parent) {
			self.parent.addChildAt(self, 0);
		}
	};
	return self;
});
var WorldSelectionMenu = Container.expand(function () {
	var self = Container.call(this);
	// Position the menu at center of screen
	self.x = 2048 / 2;
	self.y = 2732 / 2;
	var menuBackground = self.attachAsset('notification', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	menuBackground.width = 1800;
	menuBackground.height = 1200;
	menuBackground.tint = 0x333333;
	menuBackground.alpha = 0.9;
	var titleText = new Text2('Select World', {
		size: 80,
		fill: 0xFFFFFF,
		weight: 800
	});
	titleText.anchor.set(0.5, 0.5);
	titleText.y = -400;
	self.addChild(titleText);
	var worldNames = ["Forest", "Desert", "Glacier", "Village", "Inferno", "Tech Lab"];
	var unlockedWorlds = storage.unlockedWorlds || 1;
	for (var i = 0; i < worldNames.length; i++) {
		var worldButton = new Container();
		var worldButtonBg = worldButton.attachAsset('notification', {
			anchorX: 0.5,
			anchorY: 0.5
		});
		worldButtonBg.width = 350;
		worldButtonBg.height = 80;
		var isUnlocked = i + 1 <= unlockedWorlds;
		worldButtonBg.tint = isUnlocked ? 0x4444FF : 0x666666;
		var worldButtonText = new Text2(isUnlocked ? worldNames[i] : "Locked", {
			size: 40,
			fill: isUnlocked ? 0xFFFFFF : 0x999999,
			weight: 800
		});
		worldButtonText.anchor.set(0.5, 0.5);
		worldButton.addChild(worldButtonText);
		worldButton.y = -200 + i * 100;
		self.addChild(worldButton);
		(function (worldIndex, unlocked) {
			worldButton.down = function () {
				if (unlocked) {
					self.destroy();
					var levelSelectionMenu = new LevelSelectionMenu(worldIndex + 1);
					game.addChild(levelSelectionMenu);
				} else {
					var notification = game.addChild(new Notification("Complete the previous world to unlock!"));
					notification.x = 2048 / 2;
					notification.y = 2732 / 2 + 200;
				}
			};
		})(i, isUnlocked);
	}
	return self;
});
/**** 
* Initialize Game
****/ 
var game = new LK.Game({
	backgroundColor: 0x333333
});
/**** 
* Game Code
****/ 
// Re-inicializar todos los sonidos y música para asegurar que el audio funcione correctamente
// This ensures the browser only requests microphone access, not camera.
// Only use microphone features from facekit. Do NOT call any camera-related methods.
// NOTE: We only use facekit for microphone/voice features (facekit.volume).
// We never call any camera or face detection methods, so the browser will only request microphone access.
// Voice & Summoning
// Boss & Special Events
// UI & Feedback
// Special Effects
// Combat & Action Sounds
// Music system for world progression
// Notify user about microphone usage for voice commands
// Digimon Evolution Assets for Voice Summoning
// Agumon Evolution Line (Fire/Dragon)
// Gabumon Evolution Line (Ice/Wolf)
// Tentomon Evolution Line (Electric/Insect)
// Palmon Evolution Line (Plant/Poison)
// Gomamon Evolution Line (Water)
// Patamon Evolution Line (Sacred/Angel)
// Try to import facekit but make it optional to prevent loading issues
var facekit = null;
try {
	facekit = LK.import("@upit/facekit.v1");
} catch (e) {
	console.log("Facekit not available - voice features disabled");
	// Create a mock facekit object to prevent errors
	facekit = {
		volume: 0
	};
}
console.log("This game uses microphone for voice summoning commands, not camera.");
function playWorldMusic(worldNumber, isBoss, isFinalBoss) {
	// Stop any currently playing music first to prevent mixing
	LK.stopMusic();
	// Play world-specific music with fade in effect
	var musicId;
	// Check for boss music first
	if (isFinalBoss) {
		musicId = 'finalBossMusic';
	} else if (isBoss) {
		musicId = 'bossMusic';
	} else {
		switch (worldNumber) {
			case 1:
				musicId = 'forestMusic';
				break;
			case 2:
				musicId = 'desertMusic';
				break;
			case 3:
				musicId = 'glacierMusic';
				break;
			case 4:
				musicId = 'villageMusic';
				break;
			case 5:
				musicId = 'infernoMusic';
				break;
			case 6:
				musicId = 'techLabMusic';
				break;
			default:
				musicId = 'forestMusic';
		}
	}
	console.log("Playing music for world " + worldNumber + ": " + musicId);
	LK.playMusic(musicId, {
		fade: {
			start: 0,
			end: isBoss || isFinalBoss ? 1.0 : 0.8,
			duration: 1500
		}
	});
}
// Language system
var currentLanguage = storage.language || 'en';
var translations = {
	en: {
		firewallDefensors: 'Firewall Defenders',
		startGame: 'Start Game',
		tutorial: 'Tutorial',
		language: 'Language',
		selectWorld: 'Select World',
		selectLevel: 'Select Level',
		back: 'Back',
		locked: 'Locked',
		completeLevel: 'Complete previous level to unlock!',
		completeWorld: 'Complete the previous world to unlock!',
		nextWave: 'Next Wave',
		shop: 'Shop',
		bits: 'Bits',
		systemHealth: 'System Health',
		securityScore: 'Security Score',
		wave: 'Wave',
		upgrade: 'Upgrade',
		sell: 'Sell',
		maxLevel: 'Max Level',
		digivolve: 'Digivolve',
		owned: 'OWNED',
		notEnoughBits: 'Not enough bits!',
		cannotBuild: 'Cannot build here!',
		pathBlocked: 'Tower would block the path!',
		tutorialWelcome: 'Welcome to the Tutorial!\n\nThis game uses microphone for voice commands (not camera).\nLearn to play step by step...',
		tutorialStep1: 'Step 1: Tower Placement\n\nDrag a Digimon tower from the bottom of the screen to a valid position on the battlefield.',
		tutorialStep2: 'Step 2: Starting Waves\n\nClick the "Next Wave" button to start sending enemies. Towers will attack automatically.',
		tutorialStep3: 'Step 3: Upgrading Towers\n\nClick on a placed tower and then press the "Upgrade" button to increase its power. Close the menu when done!',
		tutorialCompleted: 'Tutorial completed!\n\nNow you know how to:\n• Place towers\n• Start waves\n• Upgrade towers\n• Shout Digimon names to summon allies (requires microphone)\n\nDefend the digital world!',
		tutorialStep1Complete: 'Excellent! You placed your first tower.',
		tutorialStep2Complete: 'Well done! The wave has started.',
		tutorialStep3Complete: 'Perfect! You upgraded the tower.',
		nextStep: 'Next',
		skipTutorial: 'Skip Tutorial',
		// Story translations (EN)
		story1_1: "ALERT! Pokémon infiltrators have breached\nthe Digital Forest servers! They're attempting\nto steal Digimon data files!",
		story1_2: "These Pokémon spies are using advanced\nstealth protocols to access our core database.\nDeploy Digimon guardians immediately!",
		story1_3: "The future of the Digimon franchise\ndepends on you! Stop Pokémon from\ncorrupting our digital ecosystem!",
		story2_1: "Pokémon agents have infiltrated the Desert\nData Center! They're planting malicious code\nto corrupt our systems!",
		story2_2: "Fire-type Pokémon are overheating our\nservers while others steal precious\nDigimon evolution data!",
		story2_3: "Their coordinated attack is more sophisticated\nthan before. Pokémon want to monopolize\nthe children's entertainment industry!",
		story3_1: "Ice-type Pokémon have frozen our Glacier\nservers to slow down our defenses\nwhile they extract data!",
		story3_2: "Flying Pokémon are bypassing our security\nwalls! They're trying to reach the core\nDigimon genetic database!",
		story3_3: "Critical system temperatures detected!\nPokémon are trying to cause a complete\nserver meltdown!",
		story4_1: "Pokémon sleeper agents hidden in the Village\nNetwork have activated! They've been\ngathering intelligence for months!",
		story4_2: "Multiple Pokémon strike teams are attacking\nsimultaneously, trying to overwhelm\nour Digimon defenders!",
		story4_3: "This is corporate espionage on a massive\nscale! Pokémon Company wants to steal\nour digital creature technology!",
		story5_1: "MAXIMUM THREAT LEVEL! Elite Pokémon\nhackers have breached our most secure\nTechnology Labs!",
		story5_2: "They're using legendary Pokémon abilities\nto bypass our quantum encryption!\nOur most sensitive data is at risk!",
		story5_3: "Deploy our strongest Mega-level Digimon!\nOnly they can stop this corporate\ncyber warfare!",
		story6_1: "FINAL ASSAULT! Pokémon's master plan\nis revealed - they want to delete ALL\nDigimon data permanently!",
		story6_2: "Legendary Pokémon themselves are leading\nthis final attack on our core servers!\nThis is the ultimate battle for supremacy!",
		story6_3: "The children's hearts are at stake!\nDefeat Pokémon's invasion and save\nthe future of digital monsters forever!",
		storyDefault_1: "Pokémon infiltrators detected! Protect the Digimon database!",
		storyDefault_2: "Deploy your Digimon to stop the corporate espionage!",
		storyDefault_3: "Save the Digital World from Pokémon's takeover!"
	},
	es: {
		firewallDefensors: 'Defensores del Cortafuegos',
		startGame: 'Iniciar Juego',
		tutorial: 'Tutorial',
		language: 'Idioma',
		selectWorld: 'Seleccionar Mundo',
		selectLevel: 'Seleccionar Nivel',
		back: 'Atrás',
		locked: 'Bloqueado',
		completeLevel: '¡Completa el nivel anterior para desbloquear!',
		completeWorld: '¡Completa el mundo anterior para desbloquear!',
		nextWave: 'Siguiente Oleada',
		shop: 'Tienda',
		bits: 'Bits',
		systemHealth: 'Salud del Sistema',
		securityScore: 'Puntuación de Seguridad',
		wave: 'Oleada',
		upgrade: 'Mejorar',
		sell: 'Vender',
		maxLevel: 'Nivel Máximo',
		digivolve: 'Digievolucionar',
		owned: 'POSEÍDO',
		notEnoughBits: '¡No tienes suficientes bits!',
		cannotBuild: '¡No se puede construir aquí!',
		pathBlocked: '¡La torreta bloquearía el camino!',
		tutorialWelcome: '¡Bienvenido al Tutorial!\n\nEste juego usa el micrófono para comandos de voz (no la cámara).\nAprende a jugar paso a paso...',
		tutorialStep1: 'Paso 1: Colocación de torretas\n\nArrastra una torreta Digimon desde la parte inferior de la pantalla hasta una posición válida en el campo de batalla.',
		tutorialStep2: 'Paso 2: Iniciar oleadas\n\nHaz clic en "Siguiente Oleada" para enviar enemigos. Las torretas atacarán automáticamente.',
		tutorialStep3: 'Paso 3: Mejorar torretas\n\nHaz clic en una torreta y presiona "Mejorar" para aumentar su poder. ¡Cierra el menú al terminar!',
		tutorialCompleted: '¡Tutorial completado!\n\nAhora sabes:\n• Colocar torretas\n• Iniciar oleadas\n• Mejorar torretas\n• Gritar nombres Digimon para invocar aliados\n\n¡Defiende el mundo digital!',
		tutorialStep1Complete: 'Excelente! Has colocado tu primera torreta.',
		tutorialStep2Complete: 'Bien hecho! La oleada ha comenzado.',
		tutorialStep3Complete: 'Perfecto! Has mejorado la torreta.',
		nextStep: 'Siguiente',
		skipTutorial: 'Saltar Tutorial',
		// Story translations (ES)
		story1_1: "¡ALERTA! ¡Infiltradores Pokémon han violado\nlos servidores del Bosque Digital!\n¡Intentan robar archivos de datos Digimon!",
		story1_2: "¡Estos espías Pokémon usan protocolos avanzados\nde sigilo para acceder a nuestra base de datos central!\n¡Despliega guardianes Digimon de inmediato!",
		story1_3: "¡El futuro de la franquicia Digimon depende de ti!\n¡Detén a Pokémon antes de que corrompan\nnuestro ecosistema digital!",
		story2_1: "¡Agentes Pokémon han infiltrado el Centro de Datos del Desierto!\n¡Están plantando código malicioso\npara corromper nuestros sistemas!",
		story2_2: "¡Pokémon de tipo fuego están sobrecalentando nuestros servidores\nmientras otros roban valiosos datos\nde evolución Digimon!",
		story2_3: "¡Su ataque coordinado es más sofisticado que antes!\n¡Pokémon quiere monopolizar la industria\ndel entretenimiento infantil!",
		story3_1: "¡Pokémon de tipo hielo han congelado nuestros servidores Glaciar\npara ralentizar nuestras defensas\nmientras extraen datos!",
		story3_2: "¡Pokémon voladores están evadiendo nuestros muros de seguridad!\n¡Intentan llegar a la base genética central\nde Digimon!",
		story3_3: "¡Temperaturas críticas detectadas!\n¡Pokémon intenta provocar un colapso\ntotal del servidor!",
		story4_1: "¡Agentes durmientes Pokémon ocultos en la Red de la Aldea se han activado!\n¡Han estado recolectando inteligencia\ndurante meses!",
		story4_2: "¡Múltiples equipos de ataque Pokémon atacan simultáneamente,\nintentando sobrepasar a nuestros defensores Digimon!",
		story4_3: "¡Esto es espionaje corporativo a gran escala!\n¡La Compañía Pokémon quiere robar nuestra tecnología\nde criaturas digitales!",
		story5_1: "¡NIVEL DE AMENAZA MÁXIMO! ¡Hackers Pokémon de élite han violado\nnuestros Laboratorios de Tecnología más seguros!",
		story5_2: "¡Usan habilidades legendarias Pokémon para evadir nuestro cifrado cuántico!\n¡Nuestros datos más sensibles están en riesgo!",
		story5_3: "¡Despliega nuestros Digimon Mega más fuertes!\n¡Solo ellos pueden detener esta guerra cibernética corporativa!",
		story6_1: "¡ASALTO FINAL! ¡El plan maestro de Pokémon se revela:\nquieren borrar TODOS los datos Digimon permanentemente!",
		story6_2: "¡Pokémon legendarios lideran este ataque final a nuestros servidores centrales!\n¡Es la batalla definitiva por la supremacía!",
		story6_3: "¡El corazón de los niños está en juego!\n¡Derrota la invasión Pokémon y salva el futuro\nde los monstruos digitales para siempre!",
		storyDefault_1: "¡Infiltradores Pokémon detectados! ¡Protege la base de datos Digimon!",
		storyDefault_2: "¡Despliega tus Digimon para detener el espionaje corporativo!",
		storyDefault_3: "¡Salva el Mundo Digital del dominio de Pokémon!"
	},
	zh: {
		firewallDefensors: '防火墙守护者',
		startGame: '开始游戏',
		tutorial: '教程',
		language: '语言',
		selectWorld: '选择世界',
		selectLevel: '选择关卡',
		back: '返回',
		locked: '锁定',
		completeLevel: '完成前一关卡以解锁!',
		completeWorld: '完成前一世界以解锁!',
		nextWave: '下一波',
		shop: '商店',
		bits: '比特',
		systemHealth: '系统健康',
		securityScore: '安全分数',
		wave: '波次',
		upgrade: '升级',
		sell: '出售',
		maxLevel: '最高等级',
		digivolve: '数码进化',
		owned: '已拥有',
		notEnoughBits: '比特不足!',
		cannotBuild: '无法在此建造!',
		pathBlocked: '塔会阻挡路径!',
		tutorialWelcome: '欢迎来到教程!\n\n此游戏使用麦克风进行语音指令(不使用摄像头)。\n逐步学习游戏玩法...',
		tutorialStep1: '第1步:塔的放置\n\n从屏幕底部拖拽数码宝贝塔到战场上的有效位置。',
		tutorialStep2: '第2步:开始波次\n\n点击"下一波"按钮开始派遣敌人。塔会自动攻击。',
		tutorialStep3: '第3步:升级塔\n\n点击已放置的塔,然后按"升级"按钮增强其威力。完成后关闭菜单!',
		tutorialCompleted: '教程完成!\n\n现在您知道如何:\n• 放置塔\n• 开始波次\n• 升级塔\n• 喊出数码宝贝名字召唤盟友(需要麦克风)\n\n保卫数字世界!',
		tutorialStep1Complete: '出色!您放置了第一座塔。',
		tutorialStep2Complete: '做得好!波次已开始。',
		tutorialStep3Complete: '完美!您升级了塔。',
		nextStep: '下一步',
		skipTutorial: '跳过教程',
		// Story translations (ZH)
		story1_1: "警报!宝可梦入侵者已突破\n数字森林服务器!他们试图\n窃取数码宝贝数据文件!",
		story1_2: "这些宝可梦间谍使用先进的\n隐身协议访问我们的核心数据库。\n立即部署数码宝贝守护者!",
		story1_3: "数码宝贝系列的未来\n取决于您!阻止宝可梦\n破坏我们的数字生态系统!",
		story2_1: "宝可梦特工已渗透沙漠\n数据中心!他们正在植入恶意代码\n来破坏我们的系统!",
		story2_2: "火系宝可梦正在过热我们的\n服务器,而其他宝可梦则在窃取珍贵的\n数码宝贝进化数据!",
		story2_3: "他们的协同攻击比以前更加复杂。\n宝可梦想要垄断\n儿童娱乐产业!",
		story3_1: "冰系宝可梦已冻结我们的冰川\n服务器以减缓我们的防御\n同时提取数据!",
		story3_2: "飞行宝可梦正在绕过我们的安全\n防火墙!他们试图到达核心\n数码宝贝基因数据库!",
		story3_3: "检测到关键系统温度!\n宝可梦试图造成完全的\n服务器崩溃!",
		story4_1: "隐藏在村庄\n网络中的宝可梦潜伏特工已激活!他们\n已收集情报数月!",
		story4_2: "多个宝可梦突击队同时攻击,\n试图压垮\n我们的数码宝贝防御者!",
		story4_3: "这是大规模的企业间谍活动!\n宝可梦公司想要窃取\n我们的数字生物技术!",
		story5_1: "最高威胁级别!精英宝可梦\n黑客已突破我们最安全的\n技术实验室!",
		story5_2: "他们使用传说宝可梦的能力\n绕过我们的量子加密!\n我们最敏感的数据处于危险中!",
		story5_3: "部署我们最强的超级数码宝贝!\n只有他们能阻止这场企业\n网络战争!",
		story6_1: "最终攻击!宝可梦的主计划\n被揭露——他们想要永久删除所有\n数码宝贝数据!",
		story6_2: "传说宝可梦本身正在领导\n对我们核心服务器的最终攻击!\n这是争夺霸权的终极战斗!",
		story6_3: "孩子们的心灵处于危险中!\n击败宝可梦的入侵,拯救\n数字怪兽的未来!",
		storyDefault_1: "发现宝可梦入侵者!保护数码宝贝数据库!",
		storyDefault_2: "部署您的数码宝贝阻止企业间谍活动!",
		storyDefault_3: "从宝可梦的接管中拯救数字世界!"
	},
	pt: {
		firewallDefensors: 'Defensores do Firewall',
		startGame: 'Iniciar Jogo',
		tutorial: 'Tutorial',
		language: 'Idioma',
		selectWorld: 'Selecionar Mundo',
		selectLevel: 'Selecionar Nível',
		back: 'Voltar',
		locked: 'Bloqueado',
		completeLevel: 'Complete o nível anterior para desbloquear!',
		completeWorld: 'Complete o mundo anterior para desbloquear!',
		nextWave: 'Próxima Onda',
		shop: 'Loja',
		bits: 'Bits',
		systemHealth: 'Saúde do Sistema',
		securityScore: 'Pontuação de Segurança',
		wave: 'Onda',
		upgrade: 'Melhorar',
		sell: 'Vender',
		maxLevel: 'Nível Máximo',
		digivolve: 'Digievolução',
		owned: 'POSSUÍDO',
		notEnoughBits: 'Bits insuficientes!',
		cannotBuild: 'Não é possível construir aqui!',
		pathBlocked: 'A torre bloquearia o caminho!',
		tutorialWelcome: 'Bem-vindo ao Tutorial!\n\nEste jogo usa microfone para comandos de voz (não câmera).\nAprenda a jogar passo a passo...',
		tutorialStep1: 'Passo 1: Colocação de Torres\n\nArraste uma torre Digimon da parte inferior da tela para uma posição válida no campo de batalha.',
		tutorialStep2: 'Passo 2: Iniciando Ondas\n\nClique no botão "Próxima Onda" para começar a enviar inimigos. As torres atacarão automaticamente.',
		tutorialStep3: 'Passo 3: Melhorando Torres\n\nClique em uma torre colocada e pressione o botão "Melhorar" para aumentar seu poder. Feche o menu quando terminar!',
		tutorialCompleted: 'Tutorial concluído!\n\nAgora você sabe como:\n• Colocar torres\n• Iniciar ondas\n• Melhorar torres\n• Gritar nomes de Digimon para invocar aliados (requer microfone)\n\nDefenda o mundo digital!',
		tutorialStep1Complete: 'Excelente! Você colocou sua primeira torre.',
		tutorialStep2Complete: 'Muito bem! A onda começou.',
		tutorialStep3Complete: 'Perfeito! Você melhorou a torre.',
		nextStep: 'Próximo',
		skipTutorial: 'Pular Tutorial',
		// Story translations (PT)
		story1_1: "ALERTA! Infiltradores Pokémon violaram\nos servidores da Floresta Digital! Eles estão tentando\nroubar arquivos de dados Digimon!",
		story1_2: "Esses espiões Pokémon estão usando protocolos\navançados de furtividade para acessar nosso banco de dados central.\nImplante guardiões Digimon imediatamente!",
		story1_3: "O futuro da franquia Digimon\ndepende de você! Impeça os Pokémon de\ncorromper nosso ecossistema digital!",
		story2_1: "Agentes Pokémon infiltraram o Centro\nde Dados do Deserto! Eles estão plantando código malicioso\npara corromper nossos sistemas!",
		story2_2: "Pokémon do tipo fogo estão superaquecendo nossos\nservidores enquanto outros roubam dados preciosos\nde evolução Digimon!",
		story2_3: "Seu ataque coordenado é mais sofisticado\nque antes. Os Pokémon querem monopolizar\na indústria de entretenimento infantil!",
		story3_1: "Pokémon do tipo gelo congelaram nossos servidores\nda Geleira para retardar nossas defesas\nenquanto extraem dados!",
		story3_2: "Pokémon voadores estão contornando nossas paredes\nde segurança! Eles estão tentando alcançar o banco\nde dados genéticos central dos Digimon!",
		story3_3: "Temperaturas críticas do sistema detectadas!\nOs Pokémon estão tentando causar um\ncolapso completo do servidor!",
		story4_1: "Agentes adormecidos Pokémon escondidos na Rede\nda Vila foram ativados! Eles estiveram\ncoletando inteligência por meses!",
		story4_2: "Múltiplas equipes de ataque Pokémon estão atacando\nsimultaneamente, tentando sobrecarregar\nnossos defensores Digimon!",
		story4_3: "Esta é espionagem corporativa em escala massiva!\nA Empresa Pokémon quer roubar\nnossa tecnologia de criaturas digitais!",
		story5_1: "NÍVEL MÁXIMO DE AMEAÇA! Hackers Pokémon de elite\nviolaram nossos Laboratórios de Tecnologia\nmais seguros!",
		story5_2: "Eles estão usando habilidades de Pokémon lendários\npara contornar nossa criptografia quântica!\nNossos dados mais sensíveis estão em risco!",
		story5_3: "Implante nossos Digimon Mega mais fortes!\nSomente eles podem parar esta\nguerra cibernética corporativa!",
		story6_1: "ASSALTO FINAL! O plano mestre dos Pokémon\nfoi revelado - eles querem deletar TODOS\nos dados Digimon permanentemente!",
		story6_2: "Os próprios Pokémon lendários estão liderando\neste ataque final aos nossos servidores centrais!\nEsta é a batalha definitiva pela supremacia!",
		story6_3: "Os corações das crianças estão em jogo!\nDerrote a invasão Pokémon e salve\no futuro dos monstros digitais para sempre!",
		storyDefault_1: "Infiltradores Pokémon detectados! Proteja o banco de dados Digimon!",
		storyDefault_2: "Implante seus Digimon para parar a espionagem corporativa!",
		storyDefault_3: "Salve o Mundo Digital da tomada de controle dos Pokémon!"
	}
};
function getText(key) {
	return translations[currentLanguage][key] || translations.en[key] || key;
}
function setLanguage(lang) {
	currentLanguage = lang;
	storage.language = lang;
}
// Assets adicionales para tiles específicos por mundo
var isHidingUpgradeMenu = false;
function hideUpgradeMenu(menu) {
	if (isHidingUpgradeMenu) {
		return;
	}
	isHidingUpgradeMenu = true;
	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;
// Ensure pathId is incremented on every pathFind to keep pathfinding in sync
var enemies = [];
var towers = [];
var bullets = [];
var defenses = [];
var alliedUnits = []; // Array to track summoned allied Digimon units
var selectedTower = null;
var gold = 80; // Base starting gold, will be updated based on world selection
var lives = 20;
var score = storage.securityPoints || 0;
var currentWave = 0;
var totalWaves = 10; // 10 waves per world
var currentWorld = 1;
var currentLevel = 1;
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 enemiesKilledInWave = 0;
var coins = [];
// Allied units management
var maxAlliedUnits = 5; // Maximum number of allied units at once
var summonCooldown = 300; // 5 seconds at 60 FPS
var lastSummonTime = 0;
// Voice summoning system
var voiceSummonCooldown = {};
var voiceDetectionActive = false;
var lastVoiceDetectionTime = 0;
var voiceDetectionCooldown = 60; // 1 second between voice detections
var coinSpawnTimer = 0;
var goldText = new Text2(getText('bits') + ': ' + gold, {
	size: 60,
	fill: 0xFFD700,
	weight: 800
});
goldText.anchor.set(0.5, 0.5);
// Create health bar container
var healthBarContainer = new Container();
var healthBarBg = healthBarContainer.attachAsset('healthBarOutline', {
	anchorX: 0.5,
	anchorY: 0.5
});
healthBarBg.width = 300;
healthBarBg.height = 20;
healthBarBg.tint = 0x000000;
var healthBarFill = healthBarContainer.attachAsset('healthBar', {
	anchorX: 0.5,
	anchorY: 0.5
});
healthBarFill.width = 296;
healthBarFill.height = 16;
healthBarFill.tint = 0x00FF00;
// Add health text label
var livesText = new Text2(getText('systemHealth'), {
	size: 45,
	fill: 0xFFFFFF,
	weight: 800
});
livesText.anchor.set(0.5, 0.5);
livesText.y = -35;
healthBarContainer.addChild(livesText);
// Contenedor para la puntuación de seguridad, con texto arriba y número abajo
var scoreContainer = new Container();
var scoreLabel = new Text2(getText('securityScore'), {
	size: currentLanguage === 'es' ? 40 : 50,
	fill: 0xFF0000,
	weight: 800
});
scoreLabel.anchor.set(0.5, 1.0);
scoreLabel.y = 0;
var scoreValue = new Text2(score.toString(), {
	size: 60,
	fill: 0xFF0000,
	weight: 800
});
scoreValue.anchor.set(0.5, 0);
scoreValue.y = 8;
scoreContainer.addChild(scoreLabel);
scoreContainer.addChild(scoreValue);
var topMargin = 50;
var centerX = 2048 / 2;
var spacing = 400;
// Create speed control button
var speedButton = new Container();
var speedButtonBg = speedButton.attachAsset('notification', {
	anchorX: 0.5,
	anchorY: 0.5
});
speedButtonBg.width = 150;
speedButtonBg.height = 80;
speedButtonBg.tint = 0x00AA00;
var speedButtonText = new Text2('1x', {
	size: 50,
	fill: 0xFFFFFF,
	weight: 800
});
speedButtonText.anchor.set(0.5, 0.5);
speedButton.addChild(speedButtonText);
var gameSpeed = 1;
var speedLevels = [1, 2];
var currentSpeedIndex = 0;
speedButton.down = function () {
	currentSpeedIndex = (currentSpeedIndex + 1) % speedLevels.length;
	gameSpeed = speedLevels[currentSpeedIndex];
	speedButtonText.setText(gameSpeed + 'x');
	// Update button color based on speed
	if (gameSpeed === 1) {
		speedButtonBg.tint = 0x00AA00; // Green for normal speed
	} else {
		speedButtonBg.tint = 0xFFAA00; // Orange for 2x speed
	}
};
// --- Add semi-transparent black background to bits and security score UI ---
// Bits background
var goldBg = new Container();
var goldBgRect = goldBg.attachAsset('notification', {
	anchorX: 0.5,
	anchorY: 0.5
});
goldBgRect.width = 320;
goldBgRect.height = 90;
goldBgRect.tint = 0x000000;
goldBgRect.alpha = 0.5;
goldBg.addChild(goldText);
goldBg.x = -spacing;
goldBg.y = topMargin;
LK.gui.top.addChild(goldBg);
// Security score background
var scoreBg = new Container();
var scoreBgRect = scoreBg.attachAsset('notification', {
	anchorX: 0.5,
	anchorY: 0.5
});
scoreBgRect.width = 340;
scoreBgRect.height = 110;
scoreBgRect.tint = 0x000000;
scoreBgRect.alpha = 0.5;
scoreBg.addChild(scoreContainer);
scoreBg.x = spacing;
scoreBg.y = topMargin;
LK.gui.top.addChild(scoreBg);
LK.gui.bottom.addChild(healthBarContainer);
LK.gui.top.addChild(speedButton);
healthBarContainer.x = 0;
healthBarContainer.y = -300;
speedButton.x = 0;
speedButton.y = topMargin;
function updateUI() {
	goldText.setText(getText('bits') + ': ' + gold);
	// Actualiza el valor de la puntuación de seguridad
	if (scoreContainer && scoreContainer.children && scoreContainer.children.length > 1) {
		scoreContainer.children[1].setText(score.toString());
	}
	// Save security points to storage
	storage.securityPoints = score;
	// Update health bar
	var healthPercent = lives / 20; // Assuming max lives is 20
	healthBarFill.width = 296 * healthPercent;
	// Change color based on health
	if (healthPercent > 0.6) {
		healthBarFill.tint = 0x00FF00; // Green
	} else if (healthPercent > 0.3) {
		healthBarFill.tint = 0xFFFF00; // Yellow
	} else {
		healthBarFill.tint = 0xFF0000; // Red
	}
}
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);
// Add world renderer for background graphics
var worldRenderer = new WorldRenderer();
var backgroundLayer = new Container();
backgroundLayer.addChild(worldRenderer);
var grid = new Grid(24, 29 + 6);
grid.x = 150;
grid.y = 200 - CELL_SIZE * 4;
worldRenderer.x = grid.x;
worldRenderer.y = grid.y;
grid.pathFind();
// Crear una segunda copia del mapa específicamente para visualización
var mapVisualization = new Grid(24, 29 + 6);
mapVisualization.x = 150;
mapVisualization.y = 200 - CELL_SIZE * 4;
// Sincronizar el mapa de visualización con el mapa principal
function syncVisualizationMap() {
	for (var i = 0; i < 24; i++) {
		for (var j = 0; j < 35; j++) {
			if (grid.cells[i] && grid.cells[i][j] && mapVisualization.cells[i] && mapVisualization.cells[i][j]) {
				mapVisualization.cells[i][j].type = grid.cells[i][j].type;
			}
		}
	}
}
// Forzar actualización visual del mapa cada vez que se sincroniza
worldRenderer.updateWorldGraphics(currentWorld, mapVisualization);
// Sincronizar inicialmente
syncVisualizationMap();
// Render initial world graphics
worldRenderer.updateWorldGraphics(currentWorld, mapVisualization);
// Only render debug on game start, not every frame
if (LK.ticks === 0) {
	grid.renderDebug();
}
debugLayer.addChild(grid);
game.addChildAt(backgroundLayer, 0);
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();
	// Sincronizar el mapa de visualización después de las verificaciones
	syncVisualizationMap();
	return blocked;
}
function getTowerCost(towerType) {
	var cost = 5;
	switch (towerType) {
		case 'gabumon':
			//{ju} // Rapid fire Digimon
			cost = 15;
			break;
		case 'tentomon':
			//{jw} // Long range Digimon
			cost = 25;
			break;
		case 'palmon':
			//{jy} // Area damage Digimon
			cost = 35;
			break;
		case 'gomamon':
			//{jA} // Slowing Digimon
			cost = 45;
			break;
		case 'patamon':
			//{jC} // Poison/status Digimon
			cost = 55;
			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);
	if (gold >= towerCost) {
		var tower = new Tower(towerType || 'default');
		tower.placeOnGrid(gridX, gridY);
		towerLayer.addChild(tower);
		towers.push(tower);
		setGold(gold - towerCost);
		// Play tower placement sound
		LK.getSound('towerPlace').play();
		grid.pathFind();
		grid.renderDebug();
		// Sincronizar el mapa de visualización después de colocar torre
		syncVisualizationMap();
		worldRenderer.updateWorldGraphics(currentWorld, mapVisualization);
		return true;
	} else {
		var notification = game.addChild(new Notification("Not enough bits!"));
		notification.x = 2048 / 2;
		notification.y = grid.height - 50;
		// Play purchase fail sound
		LK.getSound('purchaseFail').play();
		return false;
	}
}
game.down = function (x, y, obj) {
	var upgradeMenuVisible = game.children.some(function (child) {
		return child instanceof UpgradeMenu;
	});
	var shopVisible = digimonShop && digimonShop.visible;
	var summonMenuVisible = digimonSummonMenu && digimonSummonMenu.visible;
	if (upgradeMenuVisible || shopVisible || summonMenuVisible) {
		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) {
	var shopVisible = digimonShop && digimonShop.visible;
	var summonMenuVisible = digimonSummonMenu && digimonSummonMenu.visible;
	if (shopVisible || summonMenuVisible) {
		return;
	}
	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 shopVisible = digimonShop && digimonShop.visible;
	var summonMenuVisible = digimonSummonMenu && digimonSummonMenu.visible;
	if (shopVisible || summonMenuVisible) {
		// Still allow dragging to be reset even when menus are open
		if (isDragging) {
			isDragging = false;
			towerPreview.visible = false;
		}
		return;
	}
	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.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();
			}
		}
	}
};
// Simple wave counter instead of full wave indicator
var waveCounterText = new Text2('Wave: 0/9', {
	size: 60,
	fill: 0xFFFFFF,
	weight: 800
});
waveCounterText.anchor.set(0.5, 0.5);
waveCounterText.x = 2048 / 2;
waveCounterText.y = 2732 - 80;
game.addChild(waveCounterText);
// Create a simple wave counter object to replace waveIndicator
var waveIndicator = {
	gameStarted: false,
	getWaveType: function getWaveType(waveNumber) {
		if (waveNumber < 1 || waveNumber > 10) return "normal";
		// Simple wave type logic for 10 waves
		var waveTypes = ['normal', 'fast', 'immune', 'flying', 'swarm', 'normal', 'fast', 'immune', 'flying', 'boss'];
		return waveTypes[waveNumber - 1];
	},
	getEnemyCount: function getEnemyCount(waveNumber) {
		if (waveNumber < 1 || waveNumber > 10) return 10;
		// Wave 10 is boss with 1 giant enemy, others have 10-30 enemies
		if (waveNumber === 10) return 1;
		if (waveNumber === 5) return 30; // Swarm wave
		return 10;
	},
	getWaveTypeName: function getWaveTypeName(waveNumber) {
		var type = this.getWaveType(waveNumber);
		var typeName = type.charAt(0).toUpperCase() + type.slice(1);
		if (waveNumber === 10) typeName = "BOSS";
		return typeName;
	}
};
// Function to update wave counter display
function updateWaveCounter() {
	var currentWorldWave;
	if (currentWave === 0) {
		currentWorldWave = 0;
	} else {
		// Calculate the wave within the current world (1-10)
		currentWorldWave = (currentWave - 1) % 10 + 1;
	}
	waveCounterText.setText(getText('wave') + ': ' + currentWorldWave + '/10');
}
var nextWaveButtonContainer = new Container();
// --- Next Wave Countdown and Early Reward System ---
var nextWaveButton = new NextWaveButton();
nextWaveButton.x = 2048 - 200;
nextWaveButton.y = 2732 - 100 + 20;
nextWaveButtonContainer.addChild(nextWaveButton);
game.addChild(nextWaveButtonContainer);
// --- 20s Countdown and Early Reward System ---
var nextWaveCountdownActive = false;
var nextWaveCountdownTimer = 0;
var nextWaveCountdownMax = 1200; // 20 seconds at 60 FPS
var nextWaveCountdownText = null;
var nextWaveEarlyReward = 0; // Bits to reward for early start (calculated dynamically)
// Create countdown text (hidden by default)
nextWaveCountdownText = new Text2('', {
	size: 48,
	fill: 0x00FFD0,
	stroke: 0xFFCCCC,
	strokeThickness: 4,
	weight: 800
});
nextWaveCountdownText.anchor.set(0, 0.5);
// Position countdown text to the left of the Next Wave button, vertically centered
nextWaveCountdownText.x = -nextWaveButton.width / 2 - 120;
nextWaveCountdownText.y = 0;
nextWaveCountdownText.visible = false;
nextWaveButtonContainer.addChild(nextWaveCountdownText);
// Helper to start the countdown
function startNextWaveCountdown() {
	nextWaveCountdownActive = true;
	nextWaveCountdownTimer = nextWaveCountdownMax;
	nextWaveCountdownText.visible = true;
	nextWaveCountdownText.setText('20');
	nextWaveEarlyReward = 0;
}
// Helper to stop the countdown
function stopNextWaveCountdown() {
	nextWaveCountdownActive = false;
	nextWaveCountdownTimer = 0;
	nextWaveCountdownText.visible = false;
}
// Patch NextWaveButton.down to handle countdown and early reward
var originalNextWaveButtonDown = nextWaveButton.down;
nextWaveButton.down = function () {
	if (!nextWaveButton.enabled) return;
	// If countdown is active, reward bits for early start
	if (nextWaveCountdownActive && nextWaveCountdownTimer > 0) {
		// Reward is proportional to time left: 1 bit per second left (minimum 1)
		var secondsLeft = Math.ceil(nextWaveCountdownTimer / 60);
		var reward = Math.max(1, secondsLeft);
		setGold(gold + reward);
		var rewardNote = game.addChild(new Notification("¡Oleada iniciada antes! +" + reward + " bits"));
		rewardNote.x = 2048 / 2;
		rewardNote.y = grid.height - 100;
		stopNextWaveCountdown();
	}
	originalNextWaveButtonDown.apply(this, arguments);
};
// Patch game.update to handle countdown logic
var originalGameUpdate = game.update;
game.update = function () {
	// Show countdown only when button is visible and enabled, and not in progress
	if (nextWaveButton.visible && nextWaveButton.enabled && !waveInProgress && enemies.length === 0 && !nextWaveCountdownActive) {
		startNextWaveCountdown();
	}
	// Update countdown if active
	if (nextWaveCountdownActive) {
		if (nextWaveCountdownTimer > 0) {
			nextWaveCountdownTimer--;
			var secondsLeft = Math.ceil(nextWaveCountdownTimer / 60);
			nextWaveCountdownText.setText(secondsLeft.toString());
			// If timer runs out, auto-start next wave (no reward)
			if (nextWaveCountdownTimer === 0) {
				stopNextWaveCountdown();
				if (nextWaveButton.enabled) {
					originalNextWaveButtonDown.apply(nextWaveButton, []);
				}
			}
		}
	} else {
		nextWaveCountdownText.visible = false;
	}
	// Call original update
	originalGameUpdate.apply(this, arguments);
};
// Add shop button
var shopButton = new Container();
var shopButtonBg = shopButton.attachAsset('notification', {
	anchorX: 0.5,
	anchorY: 0.5
});
shopButtonBg.width = 300;
shopButtonBg.height = 100;
shopButtonBg.tint = 0x4444FF;
var shopButtonText = new Text2('Shop', {
	size: 50,
	fill: 0xFFFFFF,
	weight: 800
});
shopButtonText.anchor.set(0.5, 0.5);
shopButton.addChild(shopButtonText);
shopButton.x = 200;
shopButton.y = 2732 - 100 + 20;
game.addChild(shopButton);
var digimonShop = new DigimonShop();
digimonShop.x = 2048 / 2;
game.addChild(digimonShop);
shopButton.down = function () {
	digimonShop.show();
};
// --- Add C, B, and A buttons for Digimon summon filtering ---
var summonButtonSpacing = 120;
var summonButtonStartX = shopButton.x + 220; // Place to the right of shop button
function createSummonLevelButton(label, color, filterLevel, offset) {
	var btn = new Container();
	var btnBg = btn.attachAsset('notification', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	btnBg.width = 100;
	btnBg.height = 100;
	btnBg.tint = color;
	var btnText = new Text2(label, {
		size: 60,
		fill: 0xFFFFFF,
		weight: 800
	});
	btnText.anchor.set(0.5, 0.5);
	btn.addChild(btnText);
	btn.x = summonButtonStartX + offset * summonButtonSpacing;
	btn.y = shopButton.y;
	btn.down = function () {
		// Show summon menu filtered by evolution level
		if (digimonSummonMenu && digimonSummonMenu.show) {
			digimonSummonMenu.show(filterLevel);
		}
	};
	return btn;
}
// C = Champion, B = Ultimate, A = Mega
var buttonC = createSummonLevelButton('C', 0x32CD32, 'champion', 0);
var buttonB = createSummonLevelButton('B', 0xFFD700, 'ultimate', 1);
var buttonA = createSummonLevelButton('A', 0xFF4500, 'mega', 2);
game.addChild(buttonC);
game.addChild(buttonB);
game.addChild(buttonA);
var towerTypes = ['agumon', 'gabumon', 'tentomon', 'palmon', 'gomamon', 'patamon'];
var sourceTowers = [];
var towerSpacing = 320; // 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 summon arrow button at the end of source towers
var summonArrowButton = new Container();
var arrowBg = summonArrowButton.attachAsset('notification', {
	anchorX: 0.5,
	anchorY: 0.5
});
arrowBg.width = 200;
arrowBg.height = 200;
arrowBg.tint = 0xFF6600;
var arrowGraphics = summonArrowButton.attachAsset('arrow', {
	anchorX: 0.5,
	anchorY: 0.5
});
arrowGraphics.width = 80;
arrowGraphics.height = 80;
arrowGraphics.tint = 0xFFFFFF;
arrowGraphics.rotation = Math.PI / 2; // Point upward
var summonText = new Text2('SUMMON', {
	size: 35,
	fill: 0xFFFFFF,
	weight: 800
});
summonText.anchor.set(0.5, 0.5);
summonText.y = 50;
summonArrowButton.addChild(summonText);
var summonSubText = new Text2('(No Mic)', {
	size: 25,
	fill: 0xCCCCCC,
	weight: 600
});
summonSubText.anchor.set(0.5, 0.5);
summonSubText.y = 75;
summonArrowButton.addChild(summonSubText);
summonArrowButton.x = startX + towerTypes.length * towerSpacing;
summonArrowButton.y = towerY;
towerLayer.addChild(summonArrowButton);
// Create summon menu
var digimonSummonMenu = new DigimonSummonMenu();
digimonSummonMenu.x = 2048 / 2;
game.addChild(digimonSummonMenu);
summonArrowButton.down = function () {
	digimonSummonMenu.show();
};
sourceTower = null;
enemiesToSpawn = 10;
game.update = function () {
	// Update background color based on current world
	var worldAssets = worldRenderer.getWorldAssets(currentWorld);
	if (worldAssets) {
		game.setBackgroundColor(worldAssets.ambient);
	}
	// Apply speed multiplier to frame-dependent updates
	var effectiveSpeed = gameSpeed;
	// Note: Wave progression is now manual only via NextWaveButton, no automatic timer
	if (waveInProgress) {
		if (!waveSpawned) {
			waveSpawned = true;
			enemiesKilledInWave = 0; // Reset kill counter for new wave
			// Calculate correct world and level for this wave
			var levelsPerWorld = 10;
			var newWorld = Math.ceil(currentWave / levelsPerWorld);
			var newLevel = (currentWave - 1) % levelsPerWorld + 1;
			var previousWorld = currentWorld;
			currentWorld = newWorld;
			currentLevel = newLevel;
			if (currentWorld > 6) currentWorld = 6;
			// Regenerate maze and world graphics if world changed or first wave
			if (currentWorld !== previousWorld || currentWave === 1) {
				grid.generateMazeForWorld(currentWorld);
				mapVisualization.generateMazeForWorld(currentWorld); // Regenerar también el mapa de visualización
				grid.pathFind();
				grid.renderDebug();
				// Sincronizar mapas después de regenerar
				syncVisualizationMap();
				// Update world graphics usando el mapa de visualización
				worldRenderer.updateWorldGraphics(currentWorld, mapVisualization);
				// Force pathId update for all allies
				for (var i = 0; i < alliedUnits.length; i++) {
					alliedUnits[i].currentTarget = null;
				}
				// Change music when entering new world
				playWorldMusic(currentWorld);
				// World-specific notification messages
				var worldNames = ["", "Forest", "Desert", "Glacier", "Village", "Inferno", "Tech Lab"];
				var worldNotification = game.addChild(new Notification("Welcome to Digital World " + currentWorld + ": " + worldNames[currentWorld] + "!"));
				worldNotification.x = 2048 / 2;
				worldNotification.y = grid.height - 100;
			}
			// Get wave type and enemy count from the wave indicator (based on current world/level)
			var worldWave = currentLevel;
			var waveType = waveIndicator.getWaveType(worldWave);
			var enemyCount = waveIndicator.getEnemyCount(worldWave);
			// Update wave counter display
			updateWaveCounter();
			// Check if this is a boss wave (wave 10 of each world)
			var isBossWave = worldWave === 10;
			if (isBossWave) {
				// Boss waves have just 1 giant enemy
				enemyCount = 1;
				// Get boss name based on current world
				var bossNames = ["", "SNORLAX", "RHYDON", "ARTICUNO", "MACHAMP", "GROUDON", "MEWTWO"];
				var bossName = bossNames[currentWorld] || "BOSS";
				// Check if this is the final boss (Mewtwo in world 6)
				var isFinalBoss = currentWorld === 6;
				// Play boss music
				playWorldMusic(currentWorld, true, isFinalBoss);
				// Show boss announcement with specific name
				var notification = game.addChild(new Notification("⚠️ " + bossName + " APPEARS! ⚠️"));
				notification.x = 2048 / 2;
				notification.y = grid.height - 200;
			}
			// Spawn the appropriate number of enemies
			for (var i = 0; i < enemyCount; i++) {
				var enemy = new Enemy(waveType);
				// Make wave 10 boss giant with much more health
				if (isBossWave) {
					enemy.isBoss = true;
					enemy.maxHealth *= 50; // 50x more health for boss
					enemy.health = enemy.maxHealth;
					// Make boss visually larger
					if (enemy.children[0]) {
						enemy.children[0].scaleX = 3.0;
						enemy.children[0].scaleY = 3.0;
					}
					// Make boss slower but more intimidating
					enemy.speed *= 0.5;
				}
				// 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;
				// 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;
		}
	}
	// Apply speed multiplier to enemy updates
	for (var a = enemies.length - 1; a >= 0; a--) {
		var enemy = enemies[a];
		// Update enemy with speed multiplier applied
		if (enemy.update) {
			for (var speedTick = 0; speedTick < effectiveSpeed; speedTick++) {
				enemy.update();
			}
		}
		if (enemy.health <= 0) {
			// Play enemy death sound
			if (enemy.isBoss) {
				LK.getSound('bossDeath').play();
			} else {
				LK.getSound('enemyDestroyed').play();
			}
			for (var i = 0; i < enemy.bulletsTargetingThis.length; i++) {
				var bullet = enemy.bulletsTargetingThis[i];
				bullet.targetEnemy = null;
			}
			// Track enemy kills for wave progression
			if (enemy.waveNumber === currentWave) {
				enemiesKilledInWave++;
				// Give bonus gold every 5 enemies killed
				if (enemiesKilledInWave % 5 === 0) {
					var bonusGold = enemy.isBoss ? 15 : 10;
					var bonusIndicator = new GoldIndicator(bonusGold, enemy.x, enemy.y - 50);
					game.addChild(bonusIndicator);
					setGold(gold + bonusGold);
					var notification = game.addChild(new Notification("Kill streak bonus! +" + bonusGold + " bits!"));
					notification.x = 2048 / 2;
					notification.y = grid.height - 200;
				}
			}
			// 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);
			setGold(gold + goldEarned);
			// Give more score for defeating a boss
			var scoreValue = enemy.isBoss ? 100 : 5;
			score += scoreValue;
			// Save security points to storage
			storage.securityPoints = score;
			// Add a notification for boss defeat
			if (enemy.isBoss) {
				// Get boss name based on world
				var bossNames = ["", "Snorlax", "Rhydon", "Articuno", "Machamp", "Groudon", "Mewtwo"];
				var bossName = bossNames[enemy.worldNumber] || "Boss";
				var notification = game.addChild(new Notification(bossName + " defeated! +" + goldEarned + " bits!"));
				notification.x = 2048 / 2;
				notification.y = grid.height - 150;
				// Return to normal world music after boss defeat
				LK.setTimeout(function () {
					LK.stopMusic();
					LK.setTimeout(function () {
						playWorldMusic(enemy.worldNumber, false, false);
					}, 500); // Add delay to ensure clean music transition
				}, 2000); // Wait 2 seconds before switching back to normal music
			}
			updateUI();
			// 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);
			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);
			// Calculate damage based on enemy world and type
			var damageDealt = 1; // Base damage
			var worldDamageMultiplier = 1;
			// Scale damage based on world
			switch (enemy.worldNumber) {
				case 1:
					worldDamageMultiplier = 1;
					break;
				case 2:
					worldDamageMultiplier = 1.2;
					break;
				case 3:
					worldDamageMultiplier = 1.4;
					break;
				case 4:
					worldDamageMultiplier = 1.6;
					break;
				case 5:
					worldDamageMultiplier = 1.8;
					break;
				case 6:
					worldDamageMultiplier = 2.0;
					break;
			}
			// Scale damage based on enemy type
			switch (enemy.type) {
				case 'normal':
					damageDealt = 1;
					break;
				case 'fast':
					damageDealt = 1;
					break;
				// Fast but same damage
				case 'immune':
					damageDealt = 2;
					break;
				// Tanky and hits hard
				case 'flying':
					damageDealt = 1.5;
					break;
				// Aerial advantage
				case 'swarm':
					damageDealt = 1;
					break;
				// Weak individual damage
			}
			// Boss enemies deal significantly more damage
			if (enemy.isBoss) {
				damageDealt *= 5;
			}
			// Apply world multiplier and round
			damageDealt = Math.ceil(damageDealt * worldDamageMultiplier);
			lives = Math.max(0, lives - damageDealt);
			// Play system damage sound
			LK.getSound('systemDamage').play();
			// Check for critical health warning
			if (lives <= 5 && lives > 0) {
				LK.getSound('criticalHealth').play();
			}
			updateUI();
			// Show damage indicator
			var damageIndicator = new Notification("-" + damageDealt + " System Health!");
			damageIndicator.x = 2048 / 2;
			damageIndicator.y = 2732 - 200;
			game.addChild(damageIndicator);
			if (lives <= 0) {
				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();
	}
	// Check for level completion and world progression
	var levelsPerWorld = 10;
	var completedWaves = storage.completedWaves || 0;
	if (currentWave > completedWaves) {
		storage.completedWaves = currentWave;
		// Calculate current world and level
		var currentWorldNum = Math.ceil(currentWave / levelsPerWorld);
		var currentLevelNum = (currentWave - 1) % levelsPerWorld + 1;
		// Update world levels in storage
		var worldLevels = storage.worldLevels || {
			1: 1,
			2: 1,
			3: 1,
			4: 1,
			5: 1,
			6: 1
		};
		if (currentWorldNum >= 1 && currentWorldNum <= 6) {
			worldLevels[currentWorldNum] = Math.max(worldLevels[currentWorldNum], currentLevelNum);
			storage.worldLevels = worldLevels;
		}
		// Check if we completed a world (every 10 waves)
		var worldsCompleted = Math.floor(currentWave / levelsPerWorld);
		var unlockedWorlds = storage.unlockedWorlds || 1;
		if (worldsCompleted + 1 > unlockedWorlds && worldsCompleted + 1 <= 6) {
			storage.unlockedWorlds = worldsCompleted + 1;
			var notification = game.addChild(new Notification("New world unlocked!"));
			notification.x = 2048 / 2;
			notification.y = grid.height - 100;
		}
	}
	// Spawn coins randomly on the field
	coinSpawnTimer++;
	if (coinSpawnTimer > 10800 && coins.length < 3) {
		// Spawn every 3 minutes (180 seconds * 60 FPS = 10800 ticks), max 3 coins
		coinSpawnTimer = 0;
		// Find a random walkable position
		var attempts = 0;
		var spawnX, spawnY;
		do {
			var gridX = Math.floor(Math.random() * 24);
			var gridY = Math.floor(Math.random() * 35);
			var cell = grid.getCell(gridX, gridY);
			if (cell && cell.type === 0) {
				// Spawn on path tiles
				spawnX = grid.x + gridX * CELL_SIZE + CELL_SIZE / 2;
				spawnY = grid.y + gridY * CELL_SIZE + CELL_SIZE / 2;
				break;
			}
			attempts++;
		} while (attempts < 50);
		if (attempts < 50) {
			var coinValue = 5; // Fixed 5 bits for security store currency
			var coin = new Coin(spawnX, spawnY, coinValue);
			game.addChild(coin);
			coins.push(coin);
		}
	}
	// Update existing coins
	for (var i = coins.length - 1; i >= 0; i--) {
		var coin = coins[i];
		if (coin.update) coin.update();
		if (coin.collected) {
			coins.splice(i, 1);
		}
	}
	// Update allied units with speed multiplier
	for (var i = alliedUnits.length - 1; i >= 0; i--) {
		var unit = alliedUnits[i];
		if (unit.update) {
			for (var speedTick = 0; speedTick < effectiveSpeed; speedTick++) {
				unit.update();
			}
		}
		// Remove dead or destroyed units
		if (unit.isDead || !unit.parent) {
			alliedUnits.splice(i, 1);
		}
	}
	// Voice summoning system
	handleVoiceSummoning();
	// Check for completion of current world (10 waves) or all worlds
	var worldWave = (currentWave - 1) % 10 + 1;
	if (worldWave >= 10 && enemies.length === 0 && !waveInProgress) {
		// --- PERFECT WORLD COMPLETION BONUS ---
		// Only award if player has full lives (no life lost in the world)
		// Calculate which world was just completed
		var completedWorld = Math.ceil(currentWave / 10);
		// Defensive: only apply if completedWorld is valid (1-6)
		if (completedWorld >= 1 && completedWorld <= 6) {
			// Check if player has not lost any lives during this world
			// We assume max lives is always 20 at start of world
			// Defensive: Only check for perfect if lives is 20
			if (lives === 20) {
				// Calculate perfect bonus: 300 for world 1, +100 per world
				var perfectBonus = 300 + (completedWorld - 1) * 100;
				score += perfectBonus;
				storage.securityPoints = score;
				updateUI();
				var perfectNote = game.addChild(new Notification(currentLanguage === 'es' ? "¡Mundo completado PERFECTO! +" + perfectBonus + " puntos de seguridad" : "PERFECT World! +" + perfectBonus + " security score"));
				perfectNote.x = 2048 / 2;
				perfectNote.y = grid.height - 250;
			}
		}
		// Play level complete sound
		LK.getSound('levelComplete').play();
		// Stop all music first to prevent mixing
		LK.stopMusic();
		// Clear all timeouts to prevent conflicts
		// (No direct timeout clearing method available, but stopping music helps)
		// Show win screen instead of immediately returning to main menu
		LK.showYouWin(); // This will handle the game state reset and return to main menu properly
	}
};
// Show microphone usage notification
var micNotification = game.addChild(new Notification("Microphone access may be requested for voice summoning features"));
micNotification.x = 2048 / 2;
micNotification.y = 200;
var mainMenu = new MainMenu();
game.addChild(mainMenu);
// Execute the game
console.log("Game initialized and running!");
game.startGame = function () {
	var worldSelectionMenu = new WorldSelectionMenu();
	game.addChild(worldSelectionMenu);
};
game.startWorld = function (worldNumber) {
	currentWorld = worldNumber;
	// Set gold based on world number: 80 for world 1, 100 for world 2, 120 for world 3, etc. (scaled, not additive)
	gold = 60 + worldNumber * 20;
	updateUI();
	grid.generateMazeForWorld(currentWorld);
	mapVisualization.generateMazeForWorld(currentWorld); // Regenerar también el mapa de visualización
	grid.pathFind();
	grid.renderDebug();
	// Sincronizar mapas
	syncVisualizationMap();
	worldRenderer.updateWorldGraphics(currentWorld, mapVisualization);
	// Reset currentTarget for all allies to force path recalculation
	for (var i = 0; i < alliedUnits.length; i++) {
		alliedUnits[i].currentTarget = null;
	}
	// Change to world-specific music
	playWorldMusic(worldNumber);
	// Show story sequence before starting the world
	var storySequence = new StorySequence(worldNumber);
	game.addChild(storySequence);
	storySequence.onComplete = function () {
		waveIndicator.gameStarted = true;
		// Start at the first incomplete wave for this world, or at the first wave if all are complete
		var worldLevels = storage.worldLevels || {
			1: 1,
			2: 1,
			3: 1,
			4: 1,
			5: 1,
			6: 1
		};
		var startLevel = worldLevels[currentWorld] || 1;
		currentWave = (currentWorld - 1) * 10 + startLevel;
		waveTimer = nextWaveTime;
	};
};
game.startWorldLevel = function (worldNumber, levelNumber) {
	currentWorld = worldNumber;
	// Set gold based on world number: 80 for world 1, 100 for world 2, 120 for world 3, etc. (scaled, not additive)
	gold = 60 + worldNumber * 20;
	updateUI();
	// Always use the provided levelNumber when it's given
	if (levelNumber) {
		currentLevel = levelNumber;
	} else {
		var worldLevels = storage.worldLevels || {
			1: 1,
			2: 1,
			3: 1,
			4: 1,
			5: 1,
			6: 1
		};
		currentLevel = worldLevels[worldNumber] || 1;
	}
	// Calculate currentWave based on selected level, not progress
	currentWave = (worldNumber - 1) * 10 + currentLevel;
	grid.generateMazeForWorld(currentWorld);
	mapVisualization.generateMazeForWorld(currentWorld);
	grid.pathFind();
	grid.renderDebug();
	// Sincronizar mapas
	syncVisualizationMap();
	worldRenderer.updateWorldGraphics(currentWorld, mapVisualization);
	// Reset currentTarget for all allies to force path recalculation
	for (var i = 0; i < alliedUnits.length; i++) {
		alliedUnits[i].currentTarget = null;
	}
	// Change to world-specific music
	playWorldMusic(worldNumber);
	// Show story sequence before starting the level
	var storySequence = new StorySequence(worldNumber);
	game.addChild(storySequence);
	storySequence.onComplete = function () {
		waveIndicator.gameStarted = true;
		// Set flag to indicate this is the first wave start for a selected level
		isFirstWaveStart = true;
		// Set waveTimer so the first wave button click starts at the selected level
		waveTimer = nextWaveTime;
	};
};
// Add tutorial state tracking
var tutorialCompleted = false;
var tutorialInProgress = false;
var isFirstWaveStart = false; // Flag to track if this is the first wave start for selected level
// Voice summoning system handler
// This function only uses facekit.volume (microphone input).
// No camera or face detection features are used, so only microphone permission is requested.
function handleVoiceSummoning() {
	// Only process voice commands during active gameplay
	if (!waveIndicator.gameStarted || LK.ticks - lastVoiceDetectionTime < voiceDetectionCooldown) {
		return;
	}
	// Check if facekit is available and for loud voice input (shouting level)
	if (facekit && facekit.volume > 0.7) {
		lastVoiceDetectionTime = LK.ticks;
		// Define Digimon voice commands and their requirements
		var digimonVoiceCommands = {
			// Champion level (requires Digivice C and base tower)
			'greymon': {
				baseTower: 'agumon',
				requiredDigivice: 'digiviceC',
				level: 'champion',
				cost: 150,
				description: 'Champion Fire Dragon'
			},
			'garurumon': {
				baseTower: 'gabumon',
				requiredDigivice: 'digiviceC',
				level: 'champion',
				cost: 180,
				description: 'Champion Ice Wolf'
			},
			'kabuterimon': {
				baseTower: 'tentomon',
				requiredDigivice: 'digiviceC',
				level: 'champion',
				cost: 160,
				description: 'Champion Electric Insect'
			},
			// Ultimate level (requires Digivice B and base tower)
			'metalgreymon': {
				baseTower: 'agumon',
				requiredDigivice: 'digiviceB',
				level: 'ultimate',
				cost: 400,
				description: 'Ultimate Cyborg Dragon'
			},
			'weregarurumon': {
				baseTower: 'gabumon',
				requiredDigivice: 'digiviceB',
				level: 'ultimate',
				cost: 450,
				description: 'Ultimate Beast Warrior'
			},
			'megakabuterimon': {
				baseTower: 'tentomon',
				requiredDigivice: 'digiviceB',
				level: 'ultimate',
				cost: 420,
				description: 'Ultimate Giant Insect'
			},
			// Mega level (requires Digivice A and base tower)
			'wargreymon': {
				baseTower: 'agumon',
				requiredDigivice: 'digiviceA',
				level: 'mega',
				cost: 800,
				description: 'Mega Dragon Warrior'
			}
		};
		// Check each voice command
		for (var digimonName in digimonVoiceCommands) {
			var command = digimonVoiceCommands[digimonName];
			// Check if player has the required Digivice
			if (!storage[command.requiredDigivice]) {
				continue;
			}
			// Count base towers of the required type
			var baseTowerCount = 0;
			for (var i = 0; i < towers.length; i++) {
				if (towers[i].id === command.baseTower) {
					baseTowerCount++;
				}
			}
			// Need at least one base tower to summon evolution
			if (baseTowerCount === 0) {
				continue;
			}
			// Calculate cooldown based on number of base towers
			var cooldownReduction = Math.max(1, baseTowerCount);
			var effectiveCooldown = Math.floor(summonCooldown / cooldownReduction);
			// Check if this Digimon is off cooldown
			var lastSummonTime = voiceSummonCooldown[digimonName] || 0;
			if (LK.ticks - lastSummonTime < effectiveCooldown) {
				continue;
			}
			// Check if player can afford the summon
			if (score < command.cost) {
				continue;
			}
			// Check if there's space for more allied units
			if (alliedUnits.length >= maxAlliedUnits) {
				continue;
			}
			// Voice detection successful - summon the Digimon!
			score -= command.cost;
			updateUI();
			// Create a more powerful allied unit based on evolution level
			var summonedUnit = new DigimonUnit(digimonName, getEvolutionLevel(command.level));
			// Apply evolution bonuses
			switch (command.level) {
				case 'champion':
					summonedUnit.damage *= 2;
					summonedUnit.health *= 2;
					summonedUnit.maxHealth = summonedUnit.health;
					break;
				case 'ultimate':
					summonedUnit.damage *= 3;
					summonedUnit.health *= 3;
					summonedUnit.maxHealth = summonedUnit.health;
					summonedUnit.range *= 1.5;
					break;
				case 'mega':
					summonedUnit.damage *= 5;
					summonedUnit.health *= 5;
					summonedUnit.maxHealth = summonedUnit.health;
					summonedUnit.range *= 2;
					summonedUnit.attackRate = Math.floor(summonedUnit.attackRate * 0.7);
					break;
			}
			// Add to game world
			enemyLayerTop.addChild(summonedUnit);
			alliedUnits.push(summonedUnit);
			// Set cooldown for this specific Digimon
			voiceSummonCooldown[digimonName] = LK.ticks;
			// Show success notification
			var notification = game.addChild(new Notification(digimonName.toUpperCase() + " summoned by voice!"));
			notification.x = 2048 / 2;
			notification.y = grid.height - 50;
			// Show microphone icon animation above the summoned Digimon
			var micIcon = summonedUnit.attachAsset('notification', {
				anchorX: 0.5,
				anchorY: 1.0
			});
			micIcon.width = 60;
			micIcon.height = 60;
			micIcon.x = 0;
			micIcon.y = -summonedUnit.children[0].height / 2 - 10;
			micIcon.tint = 0x00AAFF;
			micIcon.alpha = 0;
			tween(micIcon, {
				alpha: 1,
				y: micIcon.y - 20
			}, {
				duration: 200,
				easing: tween.easeOut,
				onFinish: function onFinish() {
					tween(micIcon, {
						alpha: 0,
						y: micIcon.y - 40
					}, {
						duration: 600,
						delay: 600,
						easing: tween.easeIn,
						onFinish: function onFinish() {
							micIcon.visible = false;
						}
					});
				}
			});
			// Visual and audio feedback
			LK.effects.flashScreen(0x00FF00, 500);
			// Play voice summon and digivolve sounds
			LK.getSound('voiceSummon').play();
			LK.setTimeout(function () {
				LK.getSound('digivolveSound').play();
			}, 200);
			// Only summon one Digimon per voice command
			break;
		}
	}
}
// Helper function to convert evolution level to numeric level
function getEvolutionLevel(levelName) {
	switch (levelName) {
		case 'champion':
			return 3;
		case 'ultimate':
			return 5;
		case 'mega':
			return 6;
		default:
			return 1;
	}
}
game.startTutorial = function () {
	// Clear any existing game state first
	while (enemies.length > 0) {
		var enemy = enemies.pop();
		if (enemy.parent) {
			enemy.parent.removeChild(enemy);
		}
		if (enemy.shadow && enemy.shadow.parent) {
			enemy.shadow.parent.removeChild(enemy.shadow);
		}
	}
	while (towers.length > 0) {
		var tower = towers.pop();
		if (tower.parent) {
			tower.parent.removeChild(tower);
		}
	}
	while (bullets.length > 0) {
		var bullet = bullets.pop();
		if (bullet.parent) {
			bullet.parent.removeChild(bullet);
		}
	}
	// IMPORTANT: Reset tutorial state BEFORE creating the sequence
	tutorialCompleted = false;
	tutorialInProgress = false;
	// Reset game state for tutorial
	currentWorld = 1;
	currentLevel = 1;
	currentWave = 0; // Reset to 0 so tutorial starts at wave 0/9
	waveInProgress = false;
	waveSpawned = false;
	gold = 60 + currentWorld * 20; // Tutorial uses world-based gold: 80, 100, 120, etc. (scaled amount)
	lives = 20;
	score = 0;
	enemiesKilledInWave = 0;
	// Update wave counter for tutorial - explicitly set to show 0/10
	waveCounterText.setText('Wave: 0/10');
	updateUI();
	// Generate tutorial world
	grid.generateMazeForWorld(currentWorld);
	mapVisualization.generateMazeForWorld(currentWorld);
	grid.pathFind();
	grid.renderDebug();
	syncVisualizationMap();
	worldRenderer.updateWorldGraphics(currentWorld, mapVisualization);
	// Reset currentTarget for all allies to force path recalculation
	for (var i = 0; i < alliedUnits.length; i++) {
		alliedUnits[i].currentTarget = null;
	}
	// Start tutorial with forest world music
	playWorldMusic(1);
	// Create tutorial sequence immediately after state reset
	var tutorialSequence = new TutorialSequence();
	game.addChild(tutorialSequence);
	// Set tutorial in progress AFTER creation
	tutorialInProgress = true;
	tutorialSequence.onComplete = function () {
		// Mark tutorial as completed
		tutorialCompleted = true;
		tutorialInProgress = false;
		// After tutorial, ensure game is ready to start from wave 1
		waveIndicator.gameStarted = true;
		// Tutorial complete - show next wave button
		var notification = game.addChild(new Notification("Tutorial complete! Use Next Wave button to start your first wave!"));
		notification.x = 2048 / 2;
		notification.y = grid.height - 150;
	};
}; /**** 
* Plugins
****/ 
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
	unlockedWorlds: 1,
	completedWaves: 0,
	digiviceC: false,
	digiviceB: false,
	digiviceA: false,
	worldLevels: {},
	language: "en",
	securityPoints: 0
});
/**** 
* 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 bulletGraphics = self.attachAsset('bullet', {
		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;
			if (self.targetEnemy.health <= 0) {
				self.targetEnemy.health = 0;
			} else {
				self.targetEnemy.healthBar.width = self.targetEnemy.health / self.targetEnemy.maxHealth * 70;
			}
			// 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);
				// Play splash attack sound
				LK.getSound('splashAttack').play();
				// 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);
					// Play freeze effect sound for slow attacks
					LK.getSound('freezeEffect').play();
					// 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);
					// Play poison effect sound
					LK.getSound('poisonEffect').play();
					// 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);
			}
			// Special Digimon evolutionary line effects
			if (self.type === 'agumon' && self.burnChance) {
				// Agumon line: Fire/burn effects
				if (!self.targetEnemy.isImmune && Math.random() < self.burnChance) {
					self.targetEnemy.burning = true;
					self.targetEnemy.burnDamage = self.damage * 0.15;
					self.targetEnemy.burnDuration = 240; // 4 seconds
					var burnEffect = new EffectIndicator(self.targetEnemy.x, self.targetEnemy.y, 'burn');
					game.addChild(burnEffect);
					// Play burn effect sound
					LK.getSound('burnEffect').play();
				}
			} else if (self.type === 'gabumon' && self.freezeChance) {
				// Gabumon line: Ice/freeze effects
				if (!self.targetEnemy.isImmune && Math.random() < self.freezeChance) {
					self.targetEnemy.frozen = true;
					self.targetEnemy.frozenDuration = 120; // 2 seconds
					if (!self.targetEnemy.originalSpeed) {
						self.targetEnemy.originalSpeed = self.targetEnemy.speed;
					}
					self.targetEnemy.speed = 0; // Completely frozen
					var freezeEffect = new EffectIndicator(self.targetEnemy.x, self.targetEnemy.y, 'freeze');
					game.addChild(freezeEffect);
					// Play freeze effect sound
					LK.getSound('freezeEffect').play();
				}
			} else if (self.type === 'tentomon' && self.paralyzeChance) {
				// Tentomon line: Electric paralysis affecting multiple enemies
				if (!self.targetEnemy.isImmune && Math.random() < self.paralyzeChance) {
					// Paralyze main target
					self.targetEnemy.paralyzed = true;
					self.targetEnemy.paralyzeDuration = 180; // 3 seconds
					if (!self.targetEnemy.originalSpeed) {
						self.targetEnemy.originalSpeed = self.targetEnemy.speed;
					}
					self.targetEnemy.speed *= 0.1; // Nearly stopped
					var paralyzeEffect = new EffectIndicator(self.targetEnemy.x, self.targetEnemy.y, 'paralyze');
					game.addChild(paralyzeEffect);
					// Spread paralysis to nearby enemies
					for (var i = 0; i < enemies.length; i++) {
						var nearbyEnemy = enemies[i];
						if (nearbyEnemy !== self.targetEnemy && !nearbyEnemy.isImmune) {
							var dx = nearbyEnemy.x - self.targetEnemy.x;
							var dy = nearbyEnemy.y - self.targetEnemy.y;
							var distance = Math.sqrt(dx * dx + dy * dy);
							if (distance <= self.paralyzeArea) {
								nearbyEnemy.paralyzed = true;
								nearbyEnemy.paralyzeDuration = 120; // Shorter for spread effect
								if (!nearbyEnemy.originalSpeed) {
									nearbyEnemy.originalSpeed = nearbyEnemy.speed;
								}
								nearbyEnemy.speed *= 0.3;
								var spreadEffect = new EffectIndicator(nearbyEnemy.x, nearbyEnemy.y, 'paralyze');
								game.addChild(spreadEffect);
							}
						}
					}
				}
			} else if (self.type === 'palmon' && self.poisonSpread) {
				// Palmon line: Spreading poison effects
				if (!self.targetEnemy.isImmune) {
					// Apply poison to main target
					self.targetEnemy.poisoned = true;
					self.targetEnemy.poisonDamage = self.damage * 0.25;
					self.targetEnemy.poisonDuration = 360; // 6 seconds
					var poisonEffect = new EffectIndicator(self.targetEnemy.x, self.targetEnemy.y, 'poison');
					game.addChild(poisonEffect);
					// Spread poison to nearby enemies with a chance
					for (var i = 0; i < enemies.length; i++) {
						var nearbyEnemy = enemies[i];
						if (nearbyEnemy !== self.targetEnemy && !nearbyEnemy.isImmune) {
							var dx = nearbyEnemy.x - self.targetEnemy.x;
							var dy = nearbyEnemy.y - self.targetEnemy.y;
							var distance = Math.sqrt(dx * dx + dy * dy);
							if (distance <= self.poisonRadius) {
								// Use poisonSpreadChance if defined, otherwise default to 30%
								var spreadChance = typeof self.poisonSpreadChance === "number" ? self.poisonSpreadChance : 0.3;
								if (Math.random() < spreadChance) {
									nearbyEnemy.poisoned = true;
									nearbyEnemy.poisonDamage = self.damage * 0.15; // Weaker spread poison
									nearbyEnemy.poisonDuration = 240;
									var spreadPoisonEffect = new EffectIndicator(nearbyEnemy.x, nearbyEnemy.y, 'poison');
									game.addChild(spreadPoisonEffect);
								}
							}
						}
					}
				}
			} else if (self.type === 'gomamon' && self.moistureEffect) {
				// Gomamon line: Moisture effects that affect fire/ice interactions
				if (!self.targetEnemy.isImmune) {
					self.targetEnemy.moist = true;
					self.targetEnemy.moistDuration = self.moistureDuration;
					self.targetEnemy.fireResistance = 0.5; // Reduced fire damage
					self.targetEnemy.freezeVulnerability = 1.5; // Increased freeze chance
					var moistEffect = new EffectIndicator(self.targetEnemy.x, self.targetEnemy.y, 'moist');
					game.addChild(moistEffect);
				}
			}
			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 Coin = Container.expand(function (x, y, value) {
	var self = Container.call(this);
	self.value = value || 5;
	self.x = x;
	self.y = y;
	self.collected = false;
	self.walkSpeed = 0.5;
	self.direction = Math.random() * Math.PI * 2;
	self.changeDirectionTimer = 0;
	var coinGraphics = self.attachAsset('fairy', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	coinGraphics.width = 50;
	coinGraphics.height = 55;
	coinGraphics.tint = 0xFFD700;
	// Add fluttering animation
	var _flutterAnimation = function flutterAnimation() {
		tween(coinGraphics, {
			scaleY: 1.2,
			y: -8
		}, {
			duration: 800,
			easing: tween.easeInOut,
			onFinish: function onFinish() {
				tween(coinGraphics, {
					scaleY: 1.0,
					y: 0
				}, {
					duration: 800,
					easing: tween.easeInOut,
					onFinish: _flutterAnimation
				});
			}
		});
	};
	_flutterAnimation();
	// Add gentle rotation for fairy-like movement
	var _rotateAnimation = function rotateAnimation() {
		tween(coinGraphics, {
			rotation: coinGraphics.rotation + 0.3
		}, {
			duration: 1000,
			easing: tween.easeInOut,
			onFinish: function onFinish() {
				tween(coinGraphics, {
					rotation: coinGraphics.rotation - 0.6
				}, {
					duration: 2000,
					easing: tween.easeInOut,
					onFinish: function onFinish() {
						tween(coinGraphics, {
							rotation: coinGraphics.rotation + 0.3
						}, {
							duration: 1000,
							easing: tween.easeInOut,
							onFinish: _rotateAnimation
						});
					}
				});
			}
		});
	};
	_rotateAnimation();
	// Add coin value text
	var valueText = new Text2(self.value.toString(), {
		size: 20,
		fill: 0x000000,
		weight: 800
	});
	valueText.anchor.set(0.5, 0.5);
	self.addChild(valueText);
	self.update = function () {
		if (self.collected) return;
		// Change direction occasionally
		self.changeDirectionTimer++;
		if (self.changeDirectionTimer > 120) {
			// Change direction every 2 seconds
			self.direction = Math.random() * Math.PI * 2;
			self.changeDirectionTimer = 0;
		}
		// Move in current direction with more fairy-like floating movement
		var newX = self.x + Math.cos(self.direction) * self.walkSpeed;
		var newY = self.y + Math.sin(self.direction) * self.walkSpeed * 0.7; // Slower vertical movement
		// Add slight vertical bobbing for fairy effect
		newY += Math.sin(LK.ticks * 0.05) * 0.3;
		// Keep within playable bounds
		var minX = grid.x + CELL_SIZE;
		var maxX = grid.x + grid.cells.length * CELL_SIZE - CELL_SIZE;
		var minY = grid.y + CELL_SIZE;
		var maxY = grid.y + grid.cells[0].length * CELL_SIZE - CELL_SIZE;
		if (newX < minX || newX > maxX) {
			self.direction = Math.PI - self.direction; // Bounce horizontally
		} else {
			self.x = newX;
		}
		if (newY < minY || newY > maxY) {
			self.direction = -self.direction; // Bounce vertically
		} else {
			self.y = newY;
		}
		// Check if player clicks on coin
	};
	self.down = function () {
		if (!self.collected) {
			self.collected = true;
			setGold(gold + self.value);
			// Add 10 security score points
			score += 10;
			// Save security points to storage
			storage.securityPoints = score;
			updateUI();
			// Play coin collect sound
			LK.getSound('coinCollect').play();
			var goldIndicator = new GoldIndicator(self.value, self.x, self.y);
			game.addChild(goldIndicator);
			// Remove coin from coins array
			var coinIndex = coins.indexOf(self);
			if (coinIndex !== -1) {
				coins.splice(coinIndex, 1);
			}
			self.destroy();
		}
	};
	return self;
});
var ComicPanel = Container.expand(function (imagePath, text) {
	var self = Container.call(this);
	// Panel background - larger panel
	var panelBg = self.attachAsset('notification', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	panelBg.width = 700;
	panelBg.height = 500;
	panelBg.tint = 0x222222;
	panelBg.alpha = 0.9;
	// Add border effect
	var border = self.attachAsset('notification', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	border.width = 710;
	border.height = 510;
	border.tint = 0x000000;
	border.alpha = 0.8;
	self.addChildAt(border, 0);
	// Text area - larger and repositioned
	var textBg = self.attachAsset('notification', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	textBg.width = 660;
	textBg.height = 160;
	textBg.tint = 0xffffff;
	textBg.alpha = 0.95;
	textBg.y = 170;
	self.addChild(textBg);
	// Character image if provided - repositioned
	if (imagePath) {
		// Starting from glacier world (world 3), use Digimon ally images
		var shouldUseDigimonImages = currentWorld >= 3;
		var characterImage;
		if (shouldUseDigimonImages && imagePath) {
			// Use actual Digimon asset images for glacier world and beyond
			characterImage = self.attachAsset(imagePath, {
				anchorX: 0.5,
				anchorY: 0.5
			});
		} else {
			// Use default notification asset for forest and desert worlds
			characterImage = self.attachAsset('notification', {
				anchorX: 0.5,
				anchorY: 0.5
			});
		}
		characterImage.width = 200;
		characterImage.height = 200;
		characterImage.y = -80;
		self.addChild(characterImage);
	}
	// Story text - improved sizing and positioning
	var storyText = new Text2(text, {
		size: 28,
		fill: 0xFFFFFF,
		stroke: 0x000000,
		strokeThickness: 4,
		weight: 800
	});
	storyText.anchor.set(0.5, 0.5);
	storyText.y = 170;
	storyText.wordWrap = true;
	storyText.wordWrapWidth = 600;
	storyText.maxWidth = 600;
	self.addChild(storyText);
	// Add Next button for navigation
	var nextButton = new Container();
	var nextButtonBg = nextButton.attachAsset('notification', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	nextButtonBg.width = 150;
	nextButtonBg.height = 60;
	nextButtonBg.tint = 0x00AA00;
	var nextButtonText = new Text2('Next', {
		size: 40,
		fill: 0xFFFFFF,
		stroke: 0x000000,
		strokeThickness: 2,
		weight: 800
	});
	nextButtonText.anchor.set(0.5, 0.5);
	nextButton.addChild(nextButtonText);
	nextButton.x = 0;
	nextButton.y = 280;
	self.addChild(nextButton);
	// Store reference to next button for external access
	self.nextButton = nextButton;
	// Scale animation entrance
	self.scaleX = 0.3;
	self.scaleY = 0.3;
	self.alpha = 0;
	self.show = function () {
		tween(self, {
			scaleX: 1,
			scaleY: 1,
			alpha: 1
		}, {
			duration: 400,
			easing: tween.backOut
		});
	};
	self.hide = function (callback) {
		tween(self, {
			scaleX: 0.8,
			scaleY: 0.8,
			alpha: 0
		}, {
			duration: 300,
			easing: tween.easeIn,
			onFinish: callback
		});
	};
	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 = [];
	// Removed number label to improve performance
	// Numbers were causing lag due to text rendering overhead
	self.update = function () {};
	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());
		}
	};
	self.render = function (data) {
		// Renderizado visual desactivado para evitar interferencia con tiles del mundo
		// Solo mantiene la lógica de pathfinding sin elementos visuales
		// Los tiles del mundo se renderizan a través de WorldRenderer
		// Actualizar las flechas solo si hay una torre seleccionada para debug
		if (selectedTower && data.towersInRange && data.towersInRange.indexOf(selectedTower) !== -1) {
			// Mostrar flechas solo para torres seleccionadas
			while (debugArrows.length > data.targets.length) {
				self.removeChild(debugArrows.pop());
			}
			for (var a = 0; a < data.targets.length; a++) {
				var destination = data.targets[a];
				var ox = destination.x - data.x;
				var oy = destination.y - data.y;
				var angle = Math.atan2(oy, ox);
				if (!debugArrows[a]) {
					debugArrows[a] = LK.getAsset('arrow', {
						anchorX: -.5,
						anchorY: 0.5
					});
					debugArrows[a].alpha = .5;
					self.addChildAt(debugArrows[a], 1);
				}
				debugArrows[a].rotation = angle;
			}
		} else {
			// Remover flechas si no hay torre seleccionada
			self.removeArrows();
		}
		// Hacer el gráfico de celda invisible para no interferir con tiles
		cellGraphics.alpha = 0;
	};
});
var DigimonShop = Container.expand(function () {
	var self = Container.call(this);
	self.visible = false;
	self.y = 2732;
	var shopBackground = self.attachAsset('notification', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	shopBackground.width = 2048;
	shopBackground.height = 600;
	shopBackground.tint = 0x222222;
	shopBackground.alpha = 0.95;
	var titleText = new Text2('Digimon Firewall Shop', {
		size: 80,
		fill: 0xFFFFFF,
		weight: 800
	});
	titleText.anchor.set(0.5, 0.5);
	titleText.y = -250;
	self.addChild(titleText);
	// Create shop items container
	var itemsContainer = new Container();
	itemsContainer.y = -50;
	self.addChild(itemsContainer);
	// Digivice items data
	var digiviceItems = [{
		id: 'digiviceC',
		name: 'Digivice C',
		cost: 500,
		description: 'Unlocks Champion Level'
	}, {
		id: 'digiviceB',
		name: 'Digivice B',
		cost: 2000,
		description: 'Unlocks Ultimate Level'
	}, {
		id: 'digiviceA',
		name: 'Digivice A',
		cost: 8000,
		description: 'Unlocks Mega Level'
	}];
	// Create shop item buttons
	for (var i = 0; i < digiviceItems.length; i++) {
		var item = digiviceItems[i];
		var itemButton = new Container();
		itemButton.x = -600 + i * 400;
		itemButton.y = 0;
		itemsContainer.addChild(itemButton);
		var itemBg = itemButton.attachAsset('notification', {
			anchorX: 0.5,
			anchorY: 0.5
		});
		itemBg.width = 350;
		itemBg.height = 200;
		var itemNameText = new Text2(item.name, {
			size: 50,
			fill: 0xFFFFFF,
			weight: 800
		});
		itemNameText.anchor.set(0.5, 0.5);
		itemNameText.y = -50;
		itemButton.addChild(itemNameText);
		var itemDescText = new Text2(item.description, {
			size: 35,
			fill: 0xCCCCCC,
			weight: 400
		});
		itemDescText.anchor.set(0.5, 0.5);
		itemDescText.y = -10;
		itemButton.addChild(itemDescText);
		var itemCostText = new Text2(item.cost + ' points', {
			size: 45,
			fill: 0xFFD700,
			weight: 800
		});
		itemCostText.anchor.set(0.5, 0.5);
		itemCostText.y = 40;
		itemButton.addChild(itemCostText);
		// Create purchase functionality
		(function (itemData, button, background, costText) {
			button.update = function () {
				var owned = storage[itemData.id] || false;
				var canAfford = score >= itemData.cost;
				if (owned) {
					background.tint = 0x00AA00;
					costText.setText('OWNED');
					button.alpha = 0.7;
				} else if (canAfford) {
					background.tint = 0x4444FF;
					costText.setText(itemData.cost + ' points');
					button.alpha = 1.0;
				} else {
					background.tint = 0x666666;
					costText.setText(itemData.cost + ' points');
					button.alpha = 0.5;
				}
			};
			button.down = function () {
				var owned = storage[itemData.id] || false;
				var canAfford = score >= itemData.cost;
				if (owned) {
					var notification = game.addChild(new Notification("You already own " + itemData.name + "!"));
					notification.x = 2048 / 2;
					notification.y = grid.height - 50;
				} else if (canAfford) {
					score -= itemData.cost;
					updateUI();
					storage[itemData.id] = true;
					var notification = game.addChild(new Notification("Purchased " + itemData.name + "!"));
					notification.x = 2048 / 2;
					notification.y = grid.height - 50;
				} else {
					var notification = game.addChild(new Notification("Not enough security points for " + itemData.name + "!"));
					notification.x = 2048 / 2;
					notification.y = grid.height - 50;
				}
			};
		})(item, itemButton, itemBg, itemCostText);
	}
	var closeButton = new Container();
	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 = shopBackground.width / 2 - 57;
	closeButton.y = -shopBackground.height / 2 + 57;
	self.addChild(closeButton);
	closeButton.down = function () {
		self.hide();
	};
	self.show = function () {
		self.visible = true;
		tween(self, {
			y: 2732 - 300
		}, {
			duration: 300,
			easing: tween.backOut
		});
	};
	self.hide = function () {
		tween(self, {
			y: 2732
		}, {
			duration: 200,
			easing: tween.easeIn,
			onFinish: function onFinish() {
				self.visible = false;
			}
		});
	};
	return self;
});
var DigimonSummonMenu = Container.expand(function () {
	var self = Container.call(this);
	self.visible = false;
	self.y = 2732;
	var menuBackground = self.attachAsset('notification', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	menuBackground.width = 2048;
	menuBackground.height = 600;
	menuBackground.tint = 0x222222;
	menuBackground.alpha = 0.95;
	var titleText = new Text2('Summon Digimon Allies', {
		size: 80,
		fill: 0xFFFFFF,
		weight: 800
	});
	titleText.anchor.set(0.5, 0.5);
	titleText.y = -250;
	self.addChild(titleText);
	// Create summon items container
	var itemsContainer = new Container();
	itemsContainer.y = -50;
	self.addChild(itemsContainer);
	// Available Digimon based on unlocked Digivices
	function getAvailableDigimon() {
		// Todas las líneas evolutivas principales completas
		var available = [
		// Agumon line - COMPLETE
		{
			id: 'agumon',
			name: 'Agumon',
			description: 'Rookie Fire Dragon',
			reqDigivice: null
		}, {
			id: 'greymon',
			name: 'Greymon',
			description: 'Champion Fire Dragon',
			reqDigivice: 'digiviceC',
			reqBaseTower: 'agumon'
		}, {
			id: 'metalgreymon',
			name: 'MetalGreymon',
			description: 'Ultimate Cyborg Dragon',
			reqDigivice: 'digiviceB',
			reqBaseTower: 'agumon'
		}, {
			id: 'wargreymon',
			name: 'WarGreymon',
			description: 'Mega Dragon Warrior',
			reqDigivice: 'digiviceA',
			reqBaseTower: 'agumon'
		},
		// Gabumon line - COMPLETE
		{
			id: 'gabumon',
			name: 'Gabumon',
			description: 'Rookie Ranged Wolf',
			reqDigivice: null
		}, {
			id: 'garurumon',
			name: 'Garurumon',
			description: 'Champion Ice Wolf',
			reqDigivice: 'digiviceC',
			reqBaseTower: 'gabumon'
		}, {
			id: 'weregarurumon',
			name: 'WereGarurumon',
			description: 'Ultimate Beast Warrior',
			reqDigivice: 'digiviceB',
			reqBaseTower: 'gabumon'
		}, {
			id: 'metalgarurumon',
			name: 'MetalGarurumon',
			description: 'Mega Ice Wolf',
			reqDigivice: 'digiviceA',
			reqBaseTower: 'gabumon'
		},
		// Tentomon line - COMPLETE
		{
			id: 'tentomon',
			name: 'Tentomon',
			description: 'Rookie Electric Insect',
			reqDigivice: null
		}, {
			id: 'kabuterimon',
			name: 'Kabuterimon',
			description: 'Champion Electric Insect',
			reqDigivice: 'digiviceC',
			reqBaseTower: 'tentomon'
		}, {
			id: 'megakabuterimon',
			name: 'MegaKabuterimon',
			description: 'Ultimate Giant Insect',
			reqDigivice: 'digiviceB',
			reqBaseTower: 'tentomon'
		}, {
			id: 'herculeskabuterimon',
			name: 'HerculesKabuterimon',
			description: 'Mega Hercules Insect',
			reqDigivice: 'digiviceA',
			reqBaseTower: 'tentomon'
		},
		// Palmon line - COMPLETE
		{
			id: 'palmon',
			name: 'Palmon',
			description: 'Rookie Plant Fighter',
			reqDigivice: null
		}, {
			id: 'togemon',
			name: 'Togemon',
			description: 'Champion Cactus',
			reqDigivice: 'digiviceC',
			reqBaseTower: 'palmon'
		}, {
			id: 'lillymon',
			name: 'Lillymon',
			description: 'Ultimate Flower Fairy',
			reqDigivice: 'digiviceB',
			reqBaseTower: 'palmon'
		}, {
			id: 'rosemon',
			name: 'Rosemon',
			description: 'Mega Rose Queen',
			reqDigivice: 'digiviceA',
			reqBaseTower: 'palmon'
		},
		// Gomamon line - COMPLETE
		{
			id: 'gomamon',
			name: 'Gomamon',
			description: 'Rookie Aquatic Mammal',
			reqDigivice: null
		}, {
			id: 'ikkakumon',
			name: 'Ikkakumon',
			description: 'Champion Sea Beast',
			reqDigivice: 'digiviceC',
			reqBaseTower: 'gomamon'
		}, {
			id: 'zudomon',
			name: 'Zudomon',
			description: 'Ultimate Hammer Warrior',
			reqDigivice: 'digiviceB',
			reqBaseTower: 'gomamon'
		}, {
			id: 'vikemon',
			name: 'Vikemon',
			description: 'Mega Viking Beast',
			reqDigivice: 'digiviceA',
			reqBaseTower: 'gomamon'
		},
		// Patamon line - COMPLETE
		{
			id: 'patamon',
			name: 'Patamon',
			description: 'Rookie Angel',
			reqDigivice: null
		}, {
			id: 'angemon',
			name: 'Angemon',
			description: 'Champion Angel',
			reqDigivice: 'digiviceC',
			reqBaseTower: 'patamon'
		}, {
			id: 'magnaangemon',
			name: 'MagnaAngemon',
			description: 'Ultimate Holy Angel',
			reqDigivice: 'digiviceB',
			reqBaseTower: 'patamon'
		}, {
			id: 'seraphimon',
			name: 'Seraphimon',
			description: 'Mega Seraph',
			reqDigivice: 'digiviceA',
			reqBaseTower: 'patamon'
		},
		// UlforceVeedramon - FINAL LEVEL UNLOCK
		{
			id: 'UlforceVeedramon',
			name: 'UlforceVeedramon',
			description: 'Ultimate Speed Dragon',
			reqDigivice: 'digiviceA',
			reqBaseTower: null // No base tower required
		}];
		return available;
	}
	function updateDigimonButtons() {
		// Clear existing buttons
		while (itemsContainer.children.length > 0) {
			itemsContainer.removeChild(itemsContainer.children[0]);
		}
		// Show ALL Digimon, but disable buttons if requirements not met
		var allDigimon = getAvailableDigimon();
		// If a filterLevel is set (from C, B, or A button), filter Digimon by evolution level
		var filterLevel = self._filterLevel;
		if (filterLevel) {
			// Map Digimon id to evolution level
			var digimonLevelMap = {
				koromon: "rookie",
				tsunomon: "rookie",
				greymon: "champion",
				garurumon: "champion",
				kabuterimon: "champion",
				metalgreymon: "ultimate",
				weregarurumon: "ultimate",
				megakabuterimon: "ultimate",
				wargreymon: "mega"
			};
			allDigimon = allDigimon.filter(function (d) {
				return (digimonLevelMap[d.id] || "").toLowerCase() === filterLevel.toLowerCase();
			});
		}
		// If no Digimon to show, display message
		if (allDigimon.length === 0) {
			var noDigimonText = new Text2("No Digimon available for this level", {
				size: 40,
				fill: 0xFFFFFF,
				weight: 600
			});
			noDigimonText.anchor.set(0.5, 0.5);
			itemsContainer.addChild(noDigimonText);
			return;
		}
		// Layout: 3 per row, center horizontally
		var buttonsPerRow = Math.min(3, allDigimon.length);
		var buttonSpacing = 400;
		var startX = -((buttonsPerRow - 1) * buttonSpacing) / 2;
		for (var i = 0; i < allDigimon.length; i++) {
			var digimon = allDigimon[i];
			var row = Math.floor(i / buttonsPerRow);
			var col = i % buttonsPerRow;
			var digimonButton = new Container();
			digimonButton.x = startX + col * buttonSpacing;
			digimonButton.y = row * 180;
			itemsContainer.addChild(digimonButton);
			// Use correct asset for Digimon - Complete mapping for all evolutionary lines
			var digimonAssetMap = {
				// Agumon line - COMPLETE
				agumon: 'agumon',
				greymon: 'greymon',
				metalgreymon: 'metalgreymon',
				wargreymon: 'wargreymon',
				// Gabumon line - COMPLETE
				gabumon: 'gabumon',
				garurumon: 'garurumon',
				weregarurumon: 'weregarurumon',
				metalgarurumon: 'metalgarurumon',
				// Tentomon line - COMPLETE
				tentomon: 'tentomon',
				kabuterimon: 'kabuterimon',
				megakabuterimon: 'megakabuterimon',
				herculeskabuterimon: 'herculeskabuterimon',
				// Palmon line - COMPLETE
				palmon: 'palmon',
				togemon: 'togemon',
				lillymon: 'lillymon',
				rosemon: 'rosemon',
				// Gomamon line - COMPLETE
				gomamon: 'gomamon',
				ikkakumon: 'ikkakumon',
				zudomon: 'zudomon',
				vikemon: 'vikemon',
				// Patamon line - COMPLETE
				patamon: 'patamon',
				angemon: 'angemon',
				magnaangemon: 'magnaangemon',
				seraphimon: 'seraphimon',
				// UlforceVeedramon - Final unlock
				UlforceVeedramon: 'UlforceVeedramon'
			};
			var assetId = digimonAssetMap[digimon.id] || 'agumon';
			var digimonArt = digimonButton.attachAsset(assetId, {
				anchorX: 0.5,
				anchorY: 0.5
			});
			digimonArt.width = 90;
			digimonArt.height = 90;
			digimonArt.y = -60;
			var buttonBg = digimonButton.attachAsset('notification', {
				anchorX: 0.5,
				anchorY: 0.5
			});
			buttonBg.width = 350;
			buttonBg.height = 160;
			var nameText = new Text2(digimon.name, {
				size: 45,
				fill: 0xFFFFFF,
				weight: 800
			});
			nameText.anchor.set(0.5, 0.5);
			nameText.y = -40;
			digimonButton.addChild(nameText);
			var descText = new Text2(digimon.description, {
				size: 28,
				fill: 0xCCCCCC,
				weight: 400
			});
			descText.anchor.set(0.5, 0.5);
			descText.y = -10;
			digimonButton.addChild(descText);
			var costText = new Text2('', {
				size: 35,
				fill: 0xFFD700,
				weight: 800
			});
			costText.anchor.set(0.5, 0.5);
			costText.y = 30;
			digimonButton.addChild(costText);
			// Show reason if not available
			var reasonText = null;
			(function (digimonData, button, background, costText) {
				button.update = function () {
					var hasSpace = alliedUnits.length < maxAlliedUnits;
					// Calculate cooldown based on Digivice requirement
					var requiredCooldown = summonCooldown; // Default fallback
					if (digimonData.reqDigivice === 'digiviceC') {
						requiredCooldown = 50 * 60; // 50 seconds at 60 FPS
					} else if (digimonData.reqDigivice === 'digiviceB') {
						requiredCooldown = 100 * 60; // 1 minute 40 seconds at 60 FPS
					} else if (digimonData.reqDigivice === 'digiviceA') {
						requiredCooldown = 130 * 60; // 2 minutes 10 seconds at 60 FPS
					}
					var isAvailable = !summonCooldown || LK.ticks - lastSummonTime > requiredCooldown;
					var hasDigivice = !digimonData.reqDigivice || storage[digimonData.reqDigivice];
					// Check for base tower requirement
					var hasBaseTower = true;
					if (digimonData.reqBaseTower) {
						hasBaseTower = false;
						for (var i = 0; i < towers.length; i++) {
							if (towers[i].id === digimonData.reqBaseTower) {
								hasBaseTower = true;
								break;
							}
						}
					}
					// Special case: UlforceVeedramon only unlocks in final level (wave 60/level 10 of world 6)
					var isUlforceVeedramon = digimonData.id === 'UlforceVeedramon';
					var isFinalLevel = currentWave >= 60 && currentWorld >= 6;
					if (isUlforceVeedramon && !isFinalLevel) {
						hasBaseTower = false;
					}
					// Show disabled state and reason if not available
					if (!hasBaseTower) {
						background.tint = 0x333333;
						button.alpha = 0.5;
						if (!button._reasonText) {
							button._reasonText = new Text2("Requires " + digimonData.reqBaseTower.toUpperCase() + " tower in field", {
								size: 24,
								fill: 0xFF6600,
								weight: 800
							});
							button._reasonText.anchor.set(0.5, 0.5);
							button._reasonText.y = 60;
							button.addChild(button._reasonText);
						}
						costText.setText(''); // No cost shown
					} else if (!hasDigivice) {
						background.tint = 0x333333;
						button.alpha = 0.5;
						if (!button._reasonText) {
							button._reasonText = new Text2("Requires " + (digimonData.reqDigivice === "digiviceC" ? "Digivice C" : digimonData.reqDigivice === "digiviceB" ? "Digivice B" : "Digivice A"), {
								size: 24,
								fill: 0xFF6600,
								weight: 800
							});
							button._reasonText.anchor.set(0.5, 0.5);
							button._reasonText.y = 60;
							button.addChild(button._reasonText);
						}
						costText.setText('');
					} else if (!hasSpace) {
						background.tint = 0x666666;
						button.alpha = 0.7;
						if (!button._reasonText) {
							button._reasonText = new Text2("Max allies reached", {
								size: 24,
								fill: 0xFF6600,
								weight: 800
							});
							button._reasonText.anchor.set(0.5, 0.5);
							button._reasonText.y = 60;
							button.addChild(button._reasonText);
						}
						costText.setText('');
					} else if (!isAvailable) {
						background.tint = 0x666666;
						button.alpha = 0.7;
						// Calculate cooldown based on Digivice requirement
						var requiredCooldown = summonCooldown; // Default fallback
						if (digimonData.reqDigivice === 'digiviceC') {
							requiredCooldown = 50 * 60; // 50 seconds
						} else if (digimonData.reqDigivice === 'digiviceB') {
							requiredCooldown = 100 * 60; // 1 minute 40 seconds
						} else if (digimonData.reqDigivice === 'digiviceA') {
							requiredCooldown = 130 * 60; // 2 minutes 10 seconds
						}
						var remainingCooldown = Math.ceil((requiredCooldown - (LK.ticks - lastSummonTime)) / 60);
						if (!button._reasonText) {
							button._reasonText = new Text2("Cooldown: " + remainingCooldown + "s", {
								size: 24,
								fill: 0xFF6600,
								weight: 800
							});
							button._reasonText.anchor.set(0.5, 0.5);
							button._reasonText.y = 60;
							button.addChild(button._reasonText);
						} else {
							button._reasonText.setText("Cooldown: " + remainingCooldown + "s");
						}
						costText.setText('');
					} else {
						background.tint = 0x4444FF;
						button.alpha = 1.0;
						if (button._reasonText) {
							button.removeChild(button._reasonText);
							button._reasonText = null;
						}
						costText.setText('Ready!');
					}
				};
				button.down = function () {
					var hasSpace = alliedUnits.length < maxAlliedUnits;
					// Calculate cooldown based on Digivice requirement
					var requiredCooldown = summonCooldown; // Default fallback
					if (digimonData.reqDigivice === 'digiviceC') {
						requiredCooldown = 50 * 60; // 50 seconds at 60 FPS
					} else if (digimonData.reqDigivice === 'digiviceB') {
						requiredCooldown = 100 * 60; // 1 minute 40 seconds at 60 FPS
					} else if (digimonData.reqDigivice === 'digiviceA') {
						requiredCooldown = 130 * 60; // 2 minutes 10 seconds at 60 FPS
					}
					var isAvailable = !summonCooldown || LK.ticks - lastSummonTime > requiredCooldown;
					var hasDigivice = !digimonData.reqDigivice || storage[digimonData.reqDigivice];
					// Check for base tower requirement
					var hasBaseTower = true;
					if (digimonData.reqBaseTower) {
						hasBaseTower = false;
						for (var i = 0; i < towers.length; i++) {
							if (towers[i].id === digimonData.reqBaseTower) {
								hasBaseTower = true;
								break;
							}
						}
					}
					if (!hasBaseTower) {
						var notification = game.addChild(new Notification("Requires " + digimonData.reqBaseTower.toUpperCase() + " tower in field to summon " + digimonData.name + "!"));
						notification.x = 2048 / 2;
						notification.y = grid.height - 50;
					} else if (!hasDigivice) {
						var notification = game.addChild(new Notification("Requires " + (digimonData.reqDigivice === "digiviceC" ? "Digivice C" : digimonData.reqDigivice === "digiviceB" ? "Digivice B" : "Digivice A") + " to summon " + digimonData.name + "!"));
						notification.x = 2048 / 2;
						notification.y = grid.height - 50;
					} else if (!hasSpace) {
						var notification = game.addChild(new Notification("¡Límite de aliados alcanzado!"));
						notification.x = 2048 / 2;
						notification.y = grid.height - 50;
					} else if (!isAvailable) {
						// Calculate cooldown based on Digivice requirement
						var requiredCooldown = summonCooldown; // Default fallback
						if (digimonData.reqDigivice === 'digiviceC') {
							requiredCooldown = 50 * 60; // 50 seconds
						} else if (digimonData.reqDigivice === 'digiviceB') {
							requiredCooldown = 100 * 60; // 1 minute 40 seconds
						} else if (digimonData.reqDigivice === 'digiviceA') {
							requiredCooldown = 130 * 60; // 2 minutes 10 seconds
						}
						var remainingCooldown = Math.ceil((requiredCooldown - (LK.ticks - lastSummonTime)) / 60);
						var notification = game.addChild(new Notification("Summon cooldown: " + remainingCooldown + "s"));
						notification.x = 2048 / 2;
						notification.y = grid.height - 50;
					} else {
						// Summon the Digimon (no cost, just cooldown)
						var newUnit = new DigimonUnit(digimonData.id, 1);
						// Find corresponding base tower to spawn from
						var spawnTower = null;
						if (digimonData.reqBaseTower) {
							for (var i = 0; i < towers.length; i++) {
								if (towers[i].id === digimonData.reqBaseTower) {
									spawnTower = towers[i];
									break;
								}
							}
						}
						// Set spawn position - summoned Digimon spawn from exclusive allied spawn (spawntwo) y avanzan hacia la meta de aliados (goaltwo)
						if (grid.spawntwo && grid.spawntwo.length > 0) {
							var startSpawn = grid.spawntwo[Math.floor(Math.random() * grid.spawntwo.length)];
							newUnit.cellX = startSpawn.x;
							newUnit.cellY = startSpawn.y;
							newUnit.currentCellX = startSpawn.x;
							newUnit.currentCellY = startSpawn.y;
							newUnit.x = grid.x + newUnit.currentCellX * CELL_SIZE;
							newUnit.y = grid.y + newUnit.currentCellY * CELL_SIZE;
						}
						enemyLayerTop.addChild(newUnit); // Add to top layer so they appear above enemies
						alliedUnits.push(newUnit);
						lastSummonTime = LK.ticks;
						// Add flash effect when manually summoning
						tween(newUnit, {
							alpha: 0.3,
							scaleX: 1.5,
							scaleY: 1.5
						}, {
							duration: 150,
							easing: tween.easeOut,
							onFinish: function onFinish() {
								tween(newUnit, {
									alpha: 1.0,
									scaleX: 1.0,
									scaleY: 1.0
								}, {
									duration: 200,
									easing: tween.easeInOut
								});
							}
						});
						// Play success summon sound for manual summoning
						LK.getSound('purchaseSuccess').play();
						var notification = game.addChild(new Notification(digimonData.name + " summoned!"));
						notification.x = 2048 / 2;
						notification.y = grid.height - 50;
						self.hide();
					}
				};
			})(digimon, digimonButton, buttonBg, costText);
		}
	}
	// Close button
	var closeButton = new Container();
	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;
	self.addChild(closeButton);
	closeButton.down = function () {
		self.hide();
	};
	self.show = function (filterLevel) {
		// If a filterLevel is provided, filter Digimon by evolution level
		self._filterLevel = filterLevel || null;
		updateDigimonButtons();
		self.visible = true;
		tween(self, {
			y: 2732 - 300
		}, {
			duration: 300,
			easing: tween.backOut
		});
	};
	self.hide = function () {
		self._filterLevel = null; // Reset filter when closing
		tween(self, {
			y: 2732
		}, {
			duration: 200,
			easing: tween.easeIn,
			onFinish: function onFinish() {
				self.visible = false;
			}
		});
	};
	self.update = function () {
		// Update all button states
		for (var i = 0; i < itemsContainer.children.length; i++) {
			var button = itemsContainer.children[i];
			if (button.update) button.update();
		}
	};
	return self;
});
var DigimonUnit = Container.expand(function (type, level) {
	var self = Container.call(this);
	self.type = type || 'koromon';
	self.level = level || 1;
	self.health = 100 + (self.level - 1) * 50;
	self.maxHealth = self.health;
	self.damage = (20 + (self.level - 1) * 10) * 3;
	self.speed = 2.0; // Increased speed for better movement
	self.range = CELL_SIZE * 1.5;
	self.attackType = 'melee'; // 'melee', 'ranged', 'area'
	self.lastAttacked = 0;
	self.attackRate = 90; // Frames between attacks
	self.targetEnemy = null;
	self.isDead = false;
	// --- Digimon UI contextual elements ---
	var digimonNames = {
		// Agumon line
		agumon: "Agumon",
		greymon: "Greymon",
		metalgreymon: "MetalGreymon",
		wargreymon: "WarGreymon",
		// Gabumon line
		gabumon: "Gabumon",
		garurumon: "Garurumon",
		weregarurumon: "WereGarurumon",
		metalgarurumon: "MetalGarurumon",
		// Tentomon line
		tentomon: "Tentomon",
		kabuterimon: "Kabuterimon",
		megakabuterimon: "MegaKabuterimon",
		herculeskabuterimon: "HerculesKabuterimon",
		// Palmon line
		palmon: "Palmon",
		togemon: "Togemon",
		lillymon: "Lillymon",
		rosemon: "Rosemon",
		// Gomamon line
		gomamon: "Gomamon",
		ikkakumon: "Ikkakumon",
		zudomon: "Zudomon",
		vikemon: "Vikemon",
		// Patamon line
		patamon: "Patamon",
		angemon: "Angemon",
		magnaangemon: "MagnaAngemon",
		seraphimon: "Seraphimon",
		UlforceVeedramon: "UlforceVeedramon"
	};
	var digimonLevels = {
		agumon: "Rookie",
		greymon: "Champion",
		metalgreymon: "Ultimate",
		wargreymon: "Mega",
		gabumon: "Rookie",
		garurumon: "Champion",
		weregarurumon: "Ultimate",
		metalgarurumon: "Mega",
		tentomon: "Rookie",
		kabuterimon: "Champion",
		megakabuterimon: "Ultimate",
		herculeskabuterimon: "Mega",
		palmon: "Rookie",
		togemon: "Champion",
		lillymon: "Ultimate",
		rosemon: "Mega",
		gomamon: "Rookie",
		ikkakumon: "Champion",
		zudomon: "Ultimate",
		vikemon: "Mega",
		patamon: "Rookie",
		angemon: "Champion",
		magnaangemon: "Ultimate",
		seraphimon: "Mega",
		UlforceVeedramon: "Mega"
	};
	var digimonDescriptions = {
		agumon: "Rookie Fire Dragon",
		greymon: "Champion Fire Dragon",
		metalgreymon: "Ultimate Cyborg Dragon",
		wargreymon: "Mega Dragon Warrior",
		gabumon: "Rookie Ranged Wolf",
		garurumon: "Champion Ice Wolf",
		weregarurumon: "Ultimate Beast Warrior",
		metalgarurumon: "Mega Ice Wolf",
		tentomon: "Rookie Electric Insect",
		kabuterimon: "Champion Electric Insect",
		megakabuterimon: "Ultimate Giant Insect",
		herculeskabuterimon: "Mega Insect",
		palmon: "Rookie Plant Fighter",
		togemon: "Champion Cactus",
		lillymon: "Ultimate Flower Fairy",
		rosemon: "Mega Rose Queen",
		gomamon: "Rookie Aquatic Mammal",
		ikkakumon: "Champion Sea Beast",
		zudomon: "Ultimate Hammer Warrior",
		vikemon: "Mega Viking Beast",
		patamon: "Rookie Angel",
		angemon: "Champion Angel",
		magnaangemon: "Ultimate Holy Angel",
		seraphimon: "Mega Seraph",
		UlforceVeedramon: "Ultimate Speed Dragon"
	};
	var digimonColor = {
		agumon: 0xFF8C00,
		greymon: 0xFF4500,
		metalgreymon: 0xFF6347,
		wargreymon: 0xFFD700,
		gabumon: 0xAAAAFF,
		garurumon: 0x0066FF,
		weregarurumon: 0x0066CC,
		metalgarurumon: 0x3399FF,
		tentomon: 0xFFAA00,
		kabuterimon: 0x33CC00,
		megakabuterimon: 0x228B22,
		herculeskabuterimon: 0xFFD700,
		palmon: 0x33CC00,
		togemon: 0x228B22,
		lillymon: 0xFFB6C1,
		rosemon: 0xFF69B4,
		gomamon: 0x00BFFF,
		ikkakumon: 0xB0E0E6,
		zudomon: 0x4682B4,
		vikemon: 0xAA291A,
		patamon: 0xFFD700,
		angemon: 0x87CEEB,
		magnaangemon: 0x14719f,
		seraphimon: 0x6ddca4,
		UlforceVeedramon: 0x0080FF
	};
	// Set properties based on Digimon type and level
	switch (self.type) {
		// Agumon line
		case 'agumon':
			self.attackType = 'melee';
			self.damage = (15 + (self.level - 1) * 8) * 3;
			self.range = CELL_SIZE * 1.5;
			self.health = 100 + (self.level - 1) * 40;
			self.speed = 2.0;
			self.maxHealth = 130; // Fire rookie with decent health
			break;
		case 'greymon':
			self.attackType = 'ranged';
			self.damage = 70 * 3;
			self.range = CELL_SIZE * 2.5;
			self.health = 150 + (self.level - 1) * 65;
			self.speed = 1.3;
			break;
		case 'metalgreymon':
			self.attackType = 'area';
			self.damage = 120 * 3;
			self.range = CELL_SIZE * 2.8;
			self.health = 240 + (self.level - 1) * 100;
			self.speed = 1.4;
			break;
		case 'wargreymon':
			self.attackType = 'area';
			self.damage = 180 * 3 * 7; // 7x attack multiplier for Wargreymon
			self.range = CELL_SIZE * 4.5;
			self.health = 450 + (self.level - 1) * 170;
			self.speed = 1.3;
			break;
		// Gabumon line
		case 'gabumon':
			self.attackType = 'ranged';
			self.damage = (16 + (self.level - 1) * 7) * 3;
			self.range = CELL_SIZE * 2.2;
			break;
		case 'garurumon':
			self.attackType = 'ranged';
			self.damage = 70 * 3;
			self.range = CELL_SIZE * 2.7;
			self.health = 140 + (self.level - 1) * 60;
			self.speed = 1.2;
			break;
		case 'weregarurumon':
			self.attackType = 'ranged';
			self.damage = 120 * 3;
			self.range = CELL_SIZE * 3.2;
			self.health = 220 + (self.level - 1) * 90;
			self.speed = 1.5;
			break;
		case 'metalgarurumon':
			self.attackType = 'ranged';
			self.damage = 170 * 3;
			self.range = CELL_SIZE * 3.5;
			self.health = 400 + (self.level - 1) * 150;
			self.speed = 1.2;
			break;
		// Tentomon line
		case 'tentomon':
			self.attackType = 'ranged';
			self.damage = (15 + (self.level - 1) * 7) * 3;
			self.range = CELL_SIZE * 2.0;
			break;
		case 'kabuterimon':
			self.attackType = 'area';
			self.damage = 70 * 3;
			self.health = 140 + (self.level - 1) * 60;
			self.range = CELL_SIZE * 2.2;
			break;
		case 'megakabuterimon':
			self.attackType = 'area';
			self.damage = 120 * 3;
			self.health = 230 + (self.level - 1) * 90;
			self.range = CELL_SIZE * 2.5;
			break;
		case 'herculeskabuterimon':
			self.attackType = 'area';
			self.damage = 170 * 3;
			self.health = 400 + (self.level - 1) * 150;
			self.range = CELL_SIZE * 3.0;
			break;
		// Palmon line
		case 'palmon':
			self.attackType = 'melee';
			self.damage = (14 + (self.level - 1) * 6) * 3;
			break;
		case 'togemon':
			self.attackType = 'area';
			self.damage = 70 * 3;
			self.health = 120 + (self.level - 1) * 50;
			self.range = CELL_SIZE * 1.8;
			self.maxHealth = 120; // Champion cactus with good health
			break;
		case 'lillymon':
			self.attackType = 'area';
			self.damage = 120 * 3;
			self.health = 200 + (self.level - 1) * 80;
			self.range = CELL_SIZE * 2.2;
			break;
		case 'rosemon':
			self.attackType = 'area';
			self.damage = 170 * 3;
			self.health = 350 + (self.level - 1) * 120;
			self.range = CELL_SIZE * 2.7;
			break;
		// Gomamon line
		case 'gomamon':
			self.attackType = 'melee';
			self.damage = (15 + (self.level - 1) * 7) * 3;
			break;
		case 'ikkakumon':
			self.attackType = 'area';
			self.damage = 70 * 3;
			self.health = 130 + (self.level - 1) * 55;
			self.range = CELL_SIZE * 1.8;
			self.maxHealth = 130; // Champion sea beast with high health
			break;
		case 'zudomon':
			self.attackType = 'area';
			self.damage = 120 * 3;
			self.health = 210 + (self.level - 1) * 90;
			self.range = CELL_SIZE * 2.3;
			break;
		case 'vikemon':
			self.attackType = 'area';
			self.damage = 170 * 3;
			self.health = 400 + (self.level - 1) * 150;
			self.range = CELL_SIZE * 2.8;
			break;
		// Patamon line
		case 'patamon':
			self.attackType = 'ranged';
			self.damage = (12 + (self.level - 1) * 6) * 3;
			self.range = CELL_SIZE * 2.0;
			break;
		case 'angemon':
			self.attackType = 'ranged';
			self.damage = 70 * 3;
			self.range = CELL_SIZE * 2.5;
			self.health = 120 + (self.level - 1) * 50;
			self.maxHealth = 120; // Champion angel with balanced stats
			break;
		case 'magnaangemon':
			self.attackType = 'ranged';
			self.damage = 120 * 3;
			self.range = CELL_SIZE * 3.0;
			self.health = 200 + (self.level - 1) * 80;
			break;
		case 'seraphimon':
			self.attackType = 'ranged';
			self.damage = 170 * 3;
			self.range = CELL_SIZE * 3.5;
			self.health = 400 + (self.level - 1) * 150;
			break;
		case 'UlforceVeedramon':
			self.attackType = 'area';
			self.damage = 200 * 3 * 15; // 15x attack multiplier
			self.range = CELL_SIZE * 5.0;
			self.health = 500 + (self.level - 1) * 200;
			self.speed = 3.0; // Ultra fast
			break;
	}
	self.maxHealth = self.health;
	// Use correct asset for all allied units
	var digimonAssetMap = {
		// Agumon line
		agumon: 'agumon',
		greymon: 'greymon',
		metalgreymon: 'metalgreymon',
		wargreymon: 'wargreymon',
		// Gabumon line
		gabumon: 'gabumon',
		garurumon: 'garurumon',
		weregarurumon: 'weregarurumon',
		metalgarurumon: 'metalgarurumon',
		// Tentomon line
		tentomon: 'tentomon',
		kabuterimon: 'kabuterimon',
		megakabuterimon: 'megakabuterimon',
		herculeskabuterimon: 'herculeskabuterimon',
		// Palmon line
		palmon: 'palmon',
		togemon: 'togemon',
		lillymon: 'lillymon',
		rosemon: 'rosemon',
		// Gomamon line
		gomamon: 'gomamon',
		ikkakumon: 'ikkakumon',
		zudomon: 'zudomon',
		vikemon: 'vikemon',
		// Patamon line
		patamon: 'patamon',
		angemon: 'angemon',
		magnaangemon: 'magnaangemon',
		seraphimon: 'seraphimon',
		// UlforceVeedramon
		UlforceVeedramon: 'UlforceVeedramon'
	};
	var assetId = digimonAssetMap[self.type] || 'agumon';
	var unitGraphics = self.attachAsset(assetId, {
		anchorX: 0.5,
		anchorY: 0.5
	});
	// Remove any tinting to keep original colors
	unitGraphics.tint = 0xFFFFFF;
	// Tint and scale based on type
	switch (self.type) {
		case 'greymon':
			unitGraphics.scaleX = 1.5;
			unitGraphics.scaleY = 1.5;
			break;
		case 'garurumon':
			unitGraphics.scaleX = 1.5;
			unitGraphics.scaleY = 1.5;
			break;
		case 'kabuterimon':
			unitGraphics.scaleX = 1.5;
			unitGraphics.scaleY = 1.5;
			break;
		case 'togemon':
		case 'ikkakumon':
		case 'angemon':
			unitGraphics.scaleX = 1.5;
			unitGraphics.scaleY = 1.5;
			break;
		case 'metalgreymon':
			unitGraphics.scaleX = 1.75;
			unitGraphics.scaleY = 1.75;
			break;
		case 'weregarurumon':
			unitGraphics.scaleX = 1.75;
			unitGraphics.scaleY = 1.75;
			break;
		case 'megakabuterimon':
			unitGraphics.scaleX = 1.75;
			unitGraphics.scaleY = 1.75;
			break;
		case 'lillymon':
		case 'zudomon':
		case 'magnaangemon':
			unitGraphics.scaleX = 1.75;
			unitGraphics.scaleY = 1.75;
			break;
		case 'wargreymon':
			unitGraphics.scaleX = 2.5;
			unitGraphics.scaleY = 2.5;
			break;
		case 'metalgarurumon':
		case 'herculeskabuterimon':
		case 'rosemon':
		case 'vikemon':
		case 'seraphimon':
			unitGraphics.scaleX = 2.0;
			unitGraphics.scaleY = 2.0;
			break;
		case 'UlforceVeedramon':
			unitGraphics.scaleX = 2.5;
			unitGraphics.scaleY = 2.5;
			break;
	}
	// --- Level badge (top left of unit) ---
	var badgeColors = {
		Rookie: 0x00BFFF,
		Champion: 0x32CD32,
		Ultimate: 0xFFD700,
		Mega: 0xFF4500
	};
	var levelName = digimonLevels[self.type] || "Rookie";
	var badgeColor = badgeColors[levelName] || 0x00BFFF;
	var badge = self.attachAsset('towerLevelIndicator', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	badge.width = 44;
	badge.height = 44;
	badge.x = -unitGraphics.width / 2 + 22;
	badge.y = -unitGraphics.height / 2 + 22;
	badge.tint = badgeColor;
	var badgeText = new Text2(levelName.charAt(0), {
		size: 32,
		fill: 0xffffff,
		weight: 800
	});
	badgeText.anchor.set(0.5, 0.5);
	badgeText.x = badge.x;
	badgeText.y = badge.y;
	self.addChild(badge);
	self.addChild(badgeText);
	// --- Name popup (on summon, above unit) ---
	var namePopup = new Text2(digimonNames[self.type] || self.type, {
		size: 60,
		fill: 0xffffff,
		weight: 800
	});
	namePopup.anchor.set(0.5, 1.0);
	namePopup.x = 0;
	namePopup.y = -unitGraphics.height / 2 - 30;
	namePopup.alpha = 0;
	self.addChild(namePopup);
	// Animate name popup on spawn
	tween(namePopup, {
		alpha: 1,
		y: namePopup.y - 30
	}, {
		duration: 350,
		easing: tween.easeOut,
		onFinish: function onFinish() {
			tween(namePopup, {
				alpha: 0,
				y: namePopup.y - 60
			}, {
				duration: 700,
				delay: 700,
				easing: tween.easeIn,
				onFinish: function onFinish() {
					namePopup.visible = false;
				}
			});
		}
	});
	// Play summon sound and effect for all Digimon
	if (typeof LK !== "undefined" && LK.getSound) {
		LK.getSound('voiceSummon').play();
	}
	LK.effects.flashObject(self, 0x00FF00, 400);
	// --- Tooltip on touch (shows name, level, description) ---
	var tooltip = new Container();
	tooltip.visible = false;
	var tooltipBg = tooltip.attachAsset('notification', {
		anchorX: 0.5,
		anchorY: 1.0
	});
	tooltipBg.width = 340;
	tooltipBg.height = 120;
	tooltipBg.tint = 0x222222;
	tooltipBg.alpha = 0.95;
	var tooltipName = new Text2(digimonNames[self.type] || self.type, {
		size: 38,
		fill: 0xffffff,
		weight: 800
	});
	tooltipName.anchor.set(0.5, 0);
	tooltipName.y = -50;
	var tooltipLevel = new Text2(levelName, {
		size: 28,
		fill: badgeColor,
		weight: 800
	});
	tooltipLevel.anchor.set(0.5, 0);
	tooltipLevel.y = -15;
	var tooltipDesc = new Text2(digimonDescriptions[self.type] || "", {
		size: 24,
		fill: 0xcccccc,
		weight: 400
	});
	tooltipDesc.anchor.set(0.5, 0);
	tooltipDesc.y = 15;
	tooltip.addChild(tooltipBg);
	tooltip.addChild(tooltipName);
	tooltip.addChild(tooltipLevel);
	tooltip.addChild(tooltipDesc);
	tooltip.x = 0;
	tooltip.y = -unitGraphics.height / 2 - 10;
	self.addChild(tooltip);
	// Health bar
	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 = -unitGraphics.height / 2 - 15;
	healthBarOutline.x = -healthBarOutline.width / 2;
	healthBarBG.x = healthBar.x = -healthBar.width / 2 - 0.5;
	healthBar.tint = 0x00ff00;
	healthBarBG.tint = 0xff0000;
	self.healthBar = healthBar;
	// Movement properties
	self.cellX = 0;
	self.cellY = 0;
	self.currentCellX = 0;
	self.currentCellY = 0;
	self.currentTarget = null;
	self.pathTargets = [];
	// Initialize at exclusive allied spawn positions (spawntwo)
	if (grid.spawntwo && grid.spawntwo.length > 0) {
		var startSpawn = grid.spawntwo[Math.floor(Math.random() * grid.spawntwo.length)];
		self.cellX = startSpawn.x;
		self.cellY = startSpawn.y;
		self.currentCellX = startSpawn.x;
		self.currentCellY = startSpawn.y;
		self.x = grid.x + self.currentCellX * CELL_SIZE;
		self.y = grid.y + self.currentCellY * CELL_SIZE;
	}
	// --- Touch interaction for tooltip ---
	self.down = function () {
		tooltip.visible = true;
		tooltip.alpha = 0;
		tween(tooltip, {
			alpha: 1
		}, {
			duration: 120,
			easing: tween.easeOut
		});
		// Hide after 2 seconds
		LK.setTimeout(function () {
			tween(tooltip, {
				alpha: 0
			}, {
				duration: 200,
				easing: tween.easeIn,
				onFinish: function onFinish() {
					tooltip.visible = false;
				}
			});
		}, 2000);
	};
	self.findNearbyEnemies = function () {
		var nearbyEnemies = [];
		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);
			if (distance <= self.range) {
				nearbyEnemies.push(enemy);
			}
		}
		return nearbyEnemies;
	};
	self.attack = function (targetEnemy) {
		if (LK.ticks - self.lastAttacked < self.attackRate) return;
		self.lastAttacked = LK.ticks;
		// All allied attacks are area attacks
		var nearbyEnemies = self.findNearbyEnemies();
		for (var i = 0; i < nearbyEnemies.length; i++) {
			var enemy = nearbyEnemies[i];
			enemy.health -= self.damage;
			if (enemy.health <= 0) {
				enemy.health = 0;
			} else {
				enemy.healthBar.width = enemy.health / enemy.maxHealth * 70;
			}
		}
		// Visual splash effect
		var splashEffect = new EffectIndicator(self.x, self.y, 'splash');
		game.addChild(splashEffect);
	};
	self.update = function () {
		if (self.isDead) return;
		// --- Visual feedback for status effects (UX/UI improvement) ---
		if (self.burning && LK.ticks % 45 === 0) {
			var burnFx = new EffectIndicator(self.x, self.y, 'burn');
			if (game && game.addChild) game.addChild(burnFx);
		}
		if (self.frozen && LK.ticks % 30 === 0) {
			var freezeFx = new EffectIndicator(self.x, self.y, 'freeze');
			if (game && game.addChild) game.addChild(freezeFx);
		}
		if (self.poisoned && LK.ticks % 30 === 0) {
			var poisonFx = new EffectIndicator(self.x, self.y, 'poison');
			if (game && game.addChild) game.addChild(poisonFx);
		}
		if (self.paralyzed && LK.ticks % 30 === 0) {
			var paralyzeFx = new EffectIndicator(self.x, self.y, 'paralyze');
			if (game && game.addChild) game.addChild(paralyzeFx);
		}
		if (self.moist && LK.ticks % 60 === 0) {
			var moistFx = new EffectIndicator(self.x, self.y, 'moist');
			if (game && game.addChild) game.addChild(moistFx);
		}
		if (self.healing && LK.ticks % 30 === 0) {
			var healFx = new EffectIndicator(self.x, self.y, 'heal');
			if (game && game.addChild) game.addChild(healFx);
		}
		// Update health bar
		if (self.health <= 0) {
			self.isDead = true;
			self.healthBar.width = 0;
			// Show disappearance animation (fade out and scale up)
			tween(self, {
				alpha: 0,
				scaleX: 1.5,
				scaleY: 1.5
			}, {
				duration: 600,
				easing: tween.easeIn,
				onFinish: function onFinish() {
					// Remove from allied units array and destroy after delay
					var unitIndex = alliedUnits.indexOf(self);
					if (unitIndex !== -1) alliedUnits.splice(unitIndex, 1);
					self.destroy();
				}
			});
			// Show notification of defeat
			var defeatNote = game.addChild(new Notification((digimonNames[self.type] || self.type) + " defeated!"));
			defeatNote.x = 2048 / 2;
			defeatNote.y = grid.height - 120;
			return;
		}
		self.healthBar.width = self.health / self.maxHealth * 70;
		// --- Check if reached the end of the path (exclusive allied spawn cell) and award bits ---
		// Defensive: Only check if not already dead or being removed
		if (!self.isDead) {
			// Find current cell
			var currCellX = Math.round((self.x - grid.x) / CELL_SIZE);
			var currCellY = Math.round((self.y - grid.y) / CELL_SIZE);
			currCellX = Math.max(0, Math.min(grid.cells.length - 1, currCellX));
			currCellY = Math.max(0, Math.min(grid.cells[0].length - 1, currCellY));
			var currCell = grid.getCell(currCellX, currCellY);
			// Good practice: track last cell type to detect arrival at exclusive spawn
			if (self.lastCellType === undefined) self.lastCellType = currCell ? currCell.type : null;
			// Check if this cell is in goaltwo (exclusive allied goal)
			var isInGoalTwo = false;
			if (grid.goaltwo && grid.goaltwo.length > 0 && currCell) {
				for (var i = 0; i < grid.goaltwo.length; i++) {
					if (grid.goaltwo[i] === currCell) {
						isInGoalTwo = true;
						break;
					}
				}
			}
			if (isInGoalTwo && self.lastCellType !== 3) {
				// Award 10 bits
				setGold(gold + 10);
				// Show notification
				var rewardNote = game.addChild(new Notification("+10 bits! " + (digimonNames[self.type] || self.type) + " reached the goal!"));
				rewardNote.x = self.x;
				rewardNote.y = self.y - 60;
				// Remove from alliedUnits array and destroy
				var idx = alliedUnits.indexOf(self);
				if (idx !== -1) alliedUnits.splice(idx, 1);
				// Animate disappearance
				tween(self, {
					alpha: 0,
					scaleX: 1.5,
					scaleY: 1.5
				}, {
					duration: 400,
					easing: tween.easeIn,
					onFinish: function onFinish() {
						self.destroy();
					}
				});
				// Prevent further updates
				self.isDead = true;
				return;
			}
			// Update lastCellType for next frame
			self.lastCellType = currCell ? currCell.type : null;
		}
		// Find and attack nearby enemies
		var nearbyEnemies = self.findNearbyEnemies();
		if (nearbyEnemies.length > 0) {
			// Stop and attack closest enemy
			var closestEnemy = nearbyEnemies[0];
			var closestDistance = Infinity;
			for (var i = 0; i < nearbyEnemies.length; i++) {
				var enemy = nearbyEnemies[i];
				var dx = enemy.x - self.x;
				var dy = enemy.y - self.y;
				var distance = Math.sqrt(dx * dx + dy * dy);
				if (distance < closestDistance) {
					closestDistance = distance;
					closestEnemy = enemy;
				}
			}
			self.targetEnemy = closestEnemy;
			self.attack(closestEnemy);
			// Face the enemy
			var dx = closestEnemy.x - self.x;
			var dy = closestEnemy.y - self.y;
			var angle = Math.atan2(dy, dx);
			unitGraphics.rotation = angle;
			return; // Don't move while attacking
		}
		self.targetEnemy = null;
		// --- Simple Enemy Following Logic ---
		// Find the closest enemy to follow
		var closestEnemy = null;
		var closestDistance = 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);
			if (distance < closestDistance) {
				closestDistance = distance;
				closestEnemy = enemy;
			}
		}
		// If we found an enemy, move towards it
		if (closestEnemy) {
			var dx = closestEnemy.x - self.x;
			var dy = closestEnemy.y - self.y;
			var distance = Math.sqrt(dx * dx + dy * dy);
			// Only move if not too close (to avoid clustering)
			if (distance > self.range * 0.8) {
				var angle = Math.atan2(dy, dx);
				var moveSpeed = self.speed * gameSpeed;
				self.x += Math.cos(angle) * moveSpeed;
				self.y += Math.sin(angle) * moveSpeed;
				// Face the enemy
				if (self.children && self.children[0]) {
					self.children[0].rotation = angle;
				}
			}
		} else {
			// No enemies found, move towards the center of the map
			var centerX = grid.x + grid.cells.length * CELL_SIZE / 2;
			var centerY = grid.y + grid.cells[0].length * CELL_SIZE / 2;
			var dx = centerX - self.x;
			var dy = centerY - self.y;
			var distance = Math.sqrt(dx * dx + dy * dy);
			if (distance > CELL_SIZE) {
				var angle = Math.atan2(dy, dx);
				var moveSpeed = self.speed * gameSpeed * 0.5; // Slower when no enemies
				self.x += Math.cos(angle) * moveSpeed;
				self.y += Math.sin(angle) * moveSpeed;
				// Face movement direction
				if (self.children && self.children[0]) {
					self.children[0].rotation = angle;
				}
			}
		}
		// Keep units within reasonable bounds
		var minX = grid.x + CELL_SIZE;
		var maxX = grid.x + 23 * CELL_SIZE;
		var minY = grid.y + CELL_SIZE;
		var maxY = grid.y + 34 * CELL_SIZE;
		self.x = Math.max(minX, Math.min(maxX, self.x));
		self.y = Math.max(minY, Math.min(maxY, self.y));
	};
	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 = 0x33CC00;
			effectGraphics.width = effectGraphics.height = CELL_SIZE * 1.5;
			break;
		case 'slow':
			effectGraphics.tint = 0x9900FF;
			effectGraphics.width = effectGraphics.height = CELL_SIZE;
			break;
		case 'poison':
			effectGraphics.tint = 0x00FFAA;
			effectGraphics.width = effectGraphics.height = CELL_SIZE;
			break;
		case 'sniper':
			effectGraphics.tint = 0xFF5500;
			effectGraphics.width = effectGraphics.height = CELL_SIZE;
			break;
		case 'burn':
			effectGraphics.tint = 0xFF4400;
			effectGraphics.width = effectGraphics.height = CELL_SIZE * 1.2;
			break;
		case 'freeze':
			effectGraphics.tint = 0x66CCFF;
			effectGraphics.width = effectGraphics.height = CELL_SIZE * 1.3;
			break;
		case 'paralyze':
			effectGraphics.tint = 0xFFFF00;
			effectGraphics.width = effectGraphics.height = CELL_SIZE * 1.1;
			break;
		case 'moist':
			effectGraphics.tint = 0x0099CC;
			effectGraphics.width = effectGraphics.height = CELL_SIZE * 0.9;
			break;
		case 'heal':
			effectGraphics.tint = 0x00FF88;
			effectGraphics.width = effectGraphics.height = CELL_SIZE * 1.4;
			break;
	}
	effectGraphics.alpha = 0.7;
	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;
	self.worldNumber = currentWorld; // Track which world this enemy belongs to
	// 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;
	}
	// --- ENEMY ATTACK TOWER LOGIC ---
	// Enemies will attack towers if they are adjacent or on the same cell
	self.attackTowerCooldown = 0;
	self.attackTowerRate = 60; // Try to attack a tower every 1 second
	self.update = function () {
		if (self.health <= 0) {
			self.health = 0;
			self.healthBar.width = 0;
		}
		// --- Boss Attack Patterns ---
		// Only apply to boss enemies
		if (self.isBoss) {
			// World 1 Boss: Snorlax (sleep nearby towers)
			if (self.worldNumber === 1) {
				// Snorlax sleeps towers within 2.5 cells every 6 seconds
				if (!self._snorlaxSleepTimer) self._snorlaxSleepTimer = 0;
				self._snorlaxSleepTimer++;
				if (self._snorlaxSleepTimer > 360) {
					// 6 seconds at 60 FPS
					self._snorlaxSleepTimer = 0;
					// Find all towers within 2.5 cells
					for (var i = 0; i < towers.length; i++) {
						var tower = towers[i];
						var dx = tower.x - self.x;
						var dy = tower.y - self.y;
						var dist = Math.sqrt(dx * dx + dy * dy);
						if (dist <= CELL_SIZE * 2.5) {
							// Put tower to sleep for 4 seconds (240 ticks)
							if (!tower._sleepTimer || tower._sleepTimer <= 0) {
								tower._sleepTimer = 240;
								// Visual feedback: flash blue
								LK.effects.flashObject(tower, 0x3399FF, 800);
								// Show notification
								var note = game.addChild(new Notification("Snorlax put a tower to sleep!"));
								note.x = tower.x;
								note.y = tower.y - 100;
							}
						}
					}
				}
			}
			// World 2 Boss: Rhydon (stuns towers in a line every 7s)
			if (self.worldNumber === 2) {
				if (!self._rhydonStunTimer) self._rhydonStunTimer = 0;
				self._rhydonStunTimer++;
				if (self._rhydonStunTimer > 420) {
					// 7 seconds
					self._rhydonStunTimer = 0;
					// Stun towers in a horizontal line (same y, +/- 1 cell)
					for (var i = 0; i < towers.length; i++) {
						var tower = towers[i];
						var dy = Math.abs(tower.y - self.y);
						if (dy <= CELL_SIZE * 1.2) {
							if (!tower._stunTimer || tower._stunTimer <= 0) {
								tower._stunTimer = 120; // 2 seconds
								LK.effects.flashObject(tower, 0xAAAAAA, 600);
								var note = game.addChild(new Notification("Rhydon stunned a tower!"));
								note.x = tower.x;
								note.y = tower.y - 100;
							}
						}
					}
				}
			}
			// World 3 Boss: Articuno (freezes all towers every 10s)
			if (self.worldNumber === 3) {
				if (!self._articunoFreezeTimer) self._articunoFreezeTimer = 0;
				self._articunoFreezeTimer++;
				if (self._articunoFreezeTimer > 600) {
					// 10 seconds
					self._articunoFreezeTimer = 0;
					for (var i = 0; i < towers.length; i++) {
						var tower = towers[i];
						if (!tower._freezeTimer || tower._freezeTimer <= 0) {
							tower._freezeTimer = 90; // 1.5 seconds
							LK.effects.flashObject(tower, 0x66CCFF, 500);
							var note = game.addChild(new Notification("Articuno froze a tower!"));
							note.x = tower.x;
							note.y = tower.y - 100;
						}
					}
				}
			}
			// World 4 Boss: Machamp (smashes nearest tower every 8s, heavy damage)
			if (self.worldNumber === 4) {
				if (!self._machampSmashTimer) self._machampSmashTimer = 0;
				self._machampSmashTimer++;
				if (self._machampSmashTimer > 480) {
					// 8 seconds
					self._machampSmashTimer = 0;
					// Find nearest tower
					var minDist = Infinity,
						nearest = null;
					for (var i = 0; i < towers.length; i++) {
						var tower = towers[i];
						var dx = tower.x - self.x;
						var dy = tower.y - self.y;
						var dist = Math.sqrt(dx * dx + dy * dy);
						if (dist < minDist) {
							minDist = dist;
							nearest = tower;
						}
					}
					if (nearest) {
						nearest.health = Math.max(0, nearest.health - 40);
						nearest.towerHealthBar.width = nearest.health / nearest.maxHealth * 76;
						LK.effects.flashObject(nearest, 0xFF0000, 600);
						var note = game.addChild(new Notification("Machamp SMASH! -40 HP"));
						note.x = nearest.x;
						note.y = nearest.y - 100;
					}
				}
			}
			// World 5 Boss: Groudon (Tech Lab - high-tech cyber attacks every 8s)
			if (self.worldNumber === 5) {
				if (!self._groudonCyberTimer) self._groudonCyberTimer = 0;
				self._groudonCyberTimer++;
				if (self._groudonCyberTimer > 480) {
					// 8 seconds
					self._groudonCyberTimer = 0;
					// Cyber attack: overload 3 random towers with system errors
					var availableTowers = towers.slice();
					var targetsToOverload = Math.min(3, availableTowers.length);
					for (var t = 0; t < targetsToOverload; t++) {
						if (availableTowers.length === 0) break;
						var randomIndex = Math.floor(Math.random() * availableTowers.length);
						var tower = availableTowers[randomIndex];
						availableTowers.splice(randomIndex, 1);
						if (!tower._overloadTimer || tower._overloadTimer <= 0) {
							tower._overloadTimer = 150; // 2.5 seconds disabled
							LK.effects.flashObject(tower, 0x00FFFF, 800);
							var note = game.addChild(new Notification("Groudon's volcanic eruption disabled a tower!"));
							note.x = tower.x;
							note.y = tower.y - 100;
						}
					}
				}
			}
			// World 6 Boss: Mewtwo (Inferno - FINAL BOSS with escalating psychic attacks)
			if (self.worldNumber === 6) {
				if (!self._mewtwoPhaseTimer) self._mewtwoPhaseTimer = 0;
				if (!self._mewtwoPhase) self._mewtwoPhase = 1;
				self._mewtwoPhaseTimer++;
				// Phase-based attacks based on remaining health
				var healthPercent = self.health / self.maxHealth;
				if (healthPercent > 0.66) {
					self._mewtwoPhase = 1;
				} else if (healthPercent > 0.33) {
					self._mewtwoPhase = 2;
				} else {
					self._mewtwoPhase = 3;
				}
				// Phase 1: Mind control towers (every 6s)
				if (self._mewtwoPhase === 1 && self._mewtwoPhaseTimer > 360) {
					self._mewtwoPhaseTimer = 0;
					if (towers.length > 0) {
						var idx = Math.floor(Math.random() * towers.length);
						var tower = towers[idx];
						if (!tower._mindControlTimer || tower._mindControlTimer <= 0) {
							tower._mindControlTimer = 120; // 2 seconds mind controlled
							LK.effects.flashObject(tower, 0xFF00FF, 600);
							var note = game.addChild(new Notification("Mewtwo mind-controlled a tower!"));
							note.x = tower.x;
							note.y = tower.y - 100;
						}
					}
					// Phase 2: Psychic barrier reflects bullets (every 8s)
				} else if (self._mewtwoPhase === 2 && self._mewtwoPhaseTimer > 480) {
					self._mewtwoPhaseTimer = 0;
					self._psychicBarrier = 180; // 3 seconds of bullet reflection
					LK.effects.flashObject(self, 0xFFFFFF, 1000);
					var note = game.addChild(new Notification("Mewtwo activated psychic barrier!"));
					note.x = 2048 / 2;
					note.y = grid.height - 100;
					// Phase 3: Desperation - multiple random attacks (every 4s)
				} else if (self._mewtwoPhase === 3 && self._mewtwoPhaseTimer > 240) {
					self._mewtwoPhaseTimer = 0;
					// Random between mind control, barrier, and mass disable
					var attackType = Math.floor(Math.random() * 3);
					if (attackType === 0) {
						// Mass mind control
						for (var i = 0; i < Math.min(2, towers.length); i++) {
							var tower = towers[Math.floor(Math.random() * towers.length)];
							if (!tower._mindControlTimer || tower._mindControlTimer <= 0) {
								tower._mindControlTimer = 90;
								LK.effects.flashObject(tower, 0xFF00FF, 400);
							}
						}
						var note = game.addChild(new Notification("Mewtwo's desperation attack!"));
					} else if (attackType === 1) {
						// Psychic barrier
						self._psychicBarrier = 120;
						LK.effects.flashObject(self, 0xFFFFFF, 600);
						var note = game.addChild(new Notification("Mewtwo's psychic shield!"));
					} else {
						// Mass disable
						for (var i = 0; i < Math.min(3, towers.length); i++) {
							var tower = towers[Math.floor(Math.random() * towers.length)];
							if (!tower._disableTimer || tower._disableTimer <= 0) {
								tower._disableTimer = 90;
								LK.effects.flashObject(tower, 0x666666, 400);
							}
						}
						var note = game.addChild(new Notification("Mewtwo disabled multiple towers!"));
					}
					note.x = 2048 / 2;
					note.y = grid.height - 100;
				}
			}
		}
		// --- Tower status effect handling (sleep, stun, freeze, burn, disable) ---
		// This is global for all towers, but we add it here for boss effects
		for (var i = 0; i < towers.length; i++) {
			var t = towers[i];
			// Sleep disables fire/update
			if (t._sleepTimer && t._sleepTimer > 0) {
				t._sleepTimer--;
				t.alpha = 0.5;
				t.update = function () {}; // No-op
				if (t._sleepTimer === 0) {
					t.alpha = 1;
					delete t.update;
				}
				continue;
			}
			// Stun disables fire/update
			if (t._stunTimer && t._stunTimer > 0) {
				t._stunTimer--;
				t.alpha = 0.7;
				t.update = function () {};
				if (t._stunTimer === 0) {
					t.alpha = 1;
					delete t.update;
				}
				continue;
			}
			// Freeze disables fire/update
			if (t._freezeTimer && t._freezeTimer > 0) {
				t._freezeTimer--;
				t.alpha = 0.7;
				t.update = function () {};
				if (t._freezeTimer === 0) {
					t.alpha = 1;
					delete t.update;
				}
				continue;
			}
			// Burn: take damage over time
			if (t._burnTimer && t._burnTimer > 0) {
				t._burnTimer--;
				if (LK.ticks % 30 === 0) {
					t.health = Math.max(0, t.health - 3);
					t.towerHealthBar.width = t.health / t.maxHealth * 76;
					LK.effects.flashObject(t, 0xFF4400, 200);
				}
				if (t._burnTimer === 0) {
					t.alpha = 1;
				}
			}
			// Disable: disables fire/update
			if (t._disableTimer && t._disableTimer > 0) {
				t._disableTimer--;
				t.alpha = 0.3;
				t.update = function () {};
				if (t._disableTimer === 0) {
					t.alpha = 1;
					delete t.update;
				}
				continue;
			}
		}
		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;
					var slowFx = new EffectIndicator(self.x, self.y, 'slow');
					if (game && game.addChild) game.addChild(slowFx);
				}
				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;
					var poisonFx = new EffectIndicator(self.x, self.y, 'poison');
					if (game && game.addChild) game.addChild(poisonFx);
				}
				// 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
					}
				}
			}
			// Handle burn effect (Agumon line)
			if (self.burning) {
				if (LK.ticks % 45 === 0) {
					// Every 0.75 seconds
					var burnDamage = self.burnDamage;
					// Reduce burn damage if enemy is moist
					if (self.moist && self.fireResistance) {
						burnDamage *= self.fireResistance;
					}
					self.health -= burnDamage;
					if (self.health <= 0) self.health = 0;else self.healthBar.width = self.health / self.maxHealth * 70;
					var burnFx = new EffectIndicator(self.x, self.y, 'burn');
					if (game && game.addChild) game.addChild(burnFx);
				}
				self.burnDuration--;
				if (self.burnDuration <= 0) {
					self.burning = false;
				}
			}
			// Handle freeze effect (Gabumon line)
			if (self.frozen) {
				if (LK.ticks % 30 === 0) {
					var freezeFx = new EffectIndicator(self.x, self.y, 'freeze');
					if (game && game.addChild) game.addChild(freezeFx);
				}
				self.frozenDuration--;
				if (self.frozenDuration <= 0) {
					self.frozen = false;
					if (self.originalSpeed !== undefined) {
						self.speed = self.originalSpeed;
					}
				}
			}
			// Handle paralysis effect (Tentomon line)
			if (self.paralyzed) {
				if (LK.ticks % 30 === 0) {
					var paralyzeFx = new EffectIndicator(self.x, self.y, 'paralyze');
					if (game && game.addChild) game.addChild(paralyzeFx);
				}
				self.paralyzeDuration--;
				if (self.paralyzeDuration <= 0) {
					self.paralyzed = false;
					if (self.originalSpeed !== undefined) {
						self.speed = self.originalSpeed;
					}
				}
			}
			// Handle moisture effect (Gomamon line)
			if (self.moist) {
				if (LK.ticks % 60 === 0) {
					var moistFx = new EffectIndicator(self.x, self.y, 'moist');
					if (game && game.addChild) game.addChild(moistFx);
				}
				self.moistDuration--;
				if (self.moistDuration <= 0) {
					self.moist = false;
					self.fireResistance = 1.0;
					self.freezeVulnerability = 1.0;
				}
			}
		}
		// 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;
		}
		// --- ENEMY ATTACK TOWER AND ALLIED UNIT LOGIC ---
		// Enemies will attack towers and allied units if they are adjacent or on the same cell
		if (!self.isFlying && !self.isBoss) {
			self.attackTowerCooldown--;
			if (self.attackTowerCooldown <= 0) {
				// First check for allied units to attack (priority target)
				var foundAlly = null;
				for (var i = 0; i < alliedUnits.length; i++) {
					var ally = alliedUnits[i];
					if (ally.isDead || !ally.parent) continue;
					// Check if ally is in melee range (adjacent cells)
					var dx = Math.abs(ally.currentCellX - self.cellX);
					var dy = Math.abs(ally.currentCellY - self.cellY);
					if (dx <= 1.5 && dy <= 1.5) {
						foundAlly = ally;
						break;
					}
				}
				if (foundAlly) {
					// Deal damage to the allied unit
					var baseDamage = 4 + (self.worldNumber - 1) * 20; // Base damage increases by 20 per world
					// Increase damage with world and wave
					var worldMultiplier = 1 + (self.worldNumber - 1) * 0.4;
					var waveMultiplier = 1 + currentWave * 0.05;
					var totalDamage = Math.ceil(baseDamage * worldMultiplier * waveMultiplier);
					// Check if the allied unit is directly in front of the enemy (within 0.5 cell in the direction the enemy is facing)
					var facingBonus = 1;
					if (foundAlly && self.children && self.children[0]) {
						var enemyAngle = self.children[0].rotation;
						// Calculate vector from enemy to ally
						var dx = foundAlly.x - self.x;
						var dy = foundAlly.y - self.y;
						var dist = Math.sqrt(dx * dx + dy * dy);
						if (dist > 0) {
							// Normalize direction
							var dirX = Math.cos(enemyAngle);
							var dirY = Math.sin(enemyAngle);
							// Project vector to ally onto facing direction
							var dot = (dx * dirX + dy * dirY) / dist;
							// If dot > 0.7 (roughly within 45 degrees in front), and distance is close (within 1 cell)
							if (dot > 0.7 && dist < CELL_SIZE * 1.2) {
								facingBonus = 2; // Double damage if in front
							}
						}
					}
					totalDamage *= facingBonus;
					foundAlly.health = Math.max(0, foundAlly.health - totalDamage);
					// Play enemy hit sound
					LK.getSound('enemyHit').play();
					// Show damage indicator on ally
					var damageIndicator = new Notification("-" + totalDamage + " HP!");
					damageIndicator.x = foundAlly.x;
					damageIndicator.y = foundAlly.y - 60;
					if (game && game.addChild) game.addChild(damageIndicator);
					// Visual feedback - flash ally red when hit
					LK.effects.flashObject(foundAlly, 0xFF0000, 300);
					self.attackTowerCooldown = Math.max(25, self.attackTowerRate - Math.floor(currentWave * 0.5));
				} else {
					// No allies nearby, check for towers in adjacent cells (including current cell)
					var foundTower = null;
					for (var i = 0; i < towers.length; i++) {
						var tower = towers[i];
						// Check if tower is in a 2x2 area adjacent to enemy's cell
						var dx = Math.abs(tower.gridX + 0.5 - self.cellX);
						var dy = Math.abs(tower.gridY + 0.5 - self.cellY);
						if (dx <= 1.5 && dy <= 1.5) {
							foundTower = tower;
							break;
						}
					}
					if (foundTower) {
						// Deal damage to the tower
						var baseDamage = 3 + (self.worldNumber - 1) * 20; // Base damage increases by 20 per world
						// Increase damage with world and wave
						var worldMultiplier = 1 + (self.worldNumber - 1) * 0.4;
						var waveMultiplier = 1 + currentWave * 0.05;
						var totalDamage = Math.ceil(baseDamage * worldMultiplier * waveMultiplier);
						foundTower.health = Math.max(0, foundTower.health - totalDamage);
						foundTower.towerHealthBar.width = foundTower.health / foundTower.maxHealth * 76;
						// Play system damage sound
						LK.getSound('systemDamage').play();
						// Show damage indicator
						var damageIndicator = new Notification("-" + totalDamage + " Tower Health!");
						damageIndicator.x = foundTower.x;
						damageIndicator.y = foundTower.y - 80;
						if (game && game.addChild) game.addChild(damageIndicator);
						// If tower destroyed, remove from game
						if (foundTower.health <= 0) {
							var idx = towers.indexOf(foundTower);
							if (idx !== -1) towers.splice(idx, 1);
							if (foundTower.parent) foundTower.parent.removeChild(foundTower);
						}
						self.attackTowerCooldown = Math.max(30, self.attackTowerRate - Math.floor(currentWave * 0.5)); // Faster attacks as game progresses
					} else {
						self.attackTowerCooldown = 15; // Check again soon
					}
				}
			}
		}
		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;
	};
	// Apply world-based scaling to all enemy types
	self.applyWorldScaling = function () {
		var worldMultiplier = 1;
		var speedMultiplier = 1;
		// Scale stats based on world progression
		switch (self.worldNumber) {
			case 1:
				// Forest - Base stats
				worldMultiplier = 1;
				speedMultiplier = 1;
				break;
			case 2:
				// Desert - 25% more health, 10% faster
				worldMultiplier = 1.25;
				speedMultiplier = 1.1;
				break;
			case 3:
				// Glacier - 50% more health, slightly slower
				worldMultiplier = 1.5;
				speedMultiplier = 0.95;
				break;
			case 4:
				// Village - 75% more health, 15% faster
				worldMultiplier = 1.75;
				speedMultiplier = 1.15;
				break;
			case 5:
				// Tech Lab - Double health, 20% faster
				worldMultiplier = 2.0;
				speedMultiplier = 1.2;
				break;
			case 6:
				// Inferno - Triple health, 25% faster
				worldMultiplier = 3.0;
				speedMultiplier = 1.25;
				break;
			default:
				worldMultiplier = 1;
				speedMultiplier = 1;
		}
		// Apply scaling only to non-boss enemies - bosses have fixed health values
		if (!self.isBoss) {
			self.maxHealth = Math.floor(self.maxHealth * worldMultiplier);
		}
		self.speed *= speedMultiplier;
	};
	// Apply world scaling
	self.applyWorldScaling();
	if (currentWave % 10 === 0 && currentWave > 0 && type !== 'swarm') {
		self.isBoss = true;
		// Boss enemies have 20x health and are larger
		self.maxHealth *= 20;
		// Special case: Set boss base health before scaling by world - these values are final and won't be affected by world multipliers
		if (self.worldNumber === 1) {
			self.maxHealth = 600; // Rhydon: 600 base health (no multiplier applied to maintain exact health)
		} else if (self.worldNumber === 2) {
			self.maxHealth = 600; // Rhydon: 600 base health (no multiplier applied to maintain exact health)
		} else if (self.worldNumber === 3) {
			self.maxHealth = 700; // Articuno: 700 base health (no multiplier applied to maintain exact health)
		} else if (self.worldNumber === 4) {
			self.maxHealth = 800; // Machamp: 800 base health (no multiplier applied to maintain exact health)
		} else if (self.worldNumber === 5) {
			self.maxHealth = 900; // Groudon: 900 base health (no multiplier applied to maintain exact health)
		} else if (self.worldNumber === 6) {
			self.maxHealth = 1000; // Mewtwo: 1000 base health (no multiplier applied to maintain exact health)
		}
		// Slower speed for bosses
		self.speed = self.speed * 0.7;
	}
	self.health = self.maxHealth;
	// Get appropriate asset for this virus type
	var assetId = 'virus';
	if (self.isBoss) {
		// Use world-specific boss assets
		switch (self.worldNumber) {
			case 1:
				assetId = 'boss_snorlax';
				break;
			case 2:
				assetId = 'boss_rhydon';
				break;
			case 3:
				assetId = 'boss_articuno';
				break;
			case 4:
				assetId = 'boss_machamp';
				break;
			case 5:
				assetId = 'boss_groudon';
				break;
			case 6:
				assetId = 'boss_mewtwo';
				break;
			default:
				assetId = 'virus';
		}
	} else if (self.type !== 'normal') {
		assetId = 'virus_' + self.type;
	}
	var enemyGraphics = self.attachAsset(assetId, {
		anchorX: 0.5,
		anchorY: 0.5
	});
	// Scale up boss enemies (but less since they already have larger base assets)
	if (self.isBoss) {
		enemyGraphics.scaleX = 1.2;
		enemyGraphics.scaleY = 1.2;
	}
	// Apply world-based color tinting system
	self.getWorldTint = function () {
		var baseTint = 0xFFFFFF; // Default white tint
		// Get base color based on world
		switch (self.worldNumber) {
			case 1:
				// Forest - Green tints
				baseTint = 0x90EE90;
				break;
			case 2:
				// Desert - Sand/yellow tints
				baseTint = 0xF4A460;
				break;
			case 3:
				// Glacier - Blue/ice tints
				baseTint = 0xADD8E6;
				break;
			case 4:
				// Village - Brown/earth tints
				baseTint = 0xD2B48C;
				break;
			case 5:
				// Tech Lab - Metallic/silver tints
				baseTint = 0xC0C0C0;
				break;
			case 6:
				// Inferno - Red/fire tints
				baseTint = 0xFF6B6B;
				break;
			default:
				baseTint = 0xFFFFFF;
		}
		// Modify base tint slightly based on enemy type while keeping world theme
		switch (self.type) {
			case 'fast':
				// Make it slightly more blue-ish while keeping world color
				baseTint = tween.linear(baseTint, 0x0080FF, 0.3);
				break;
			case 'immune':
				// Make it slightly more red-ish while keeping world color
				baseTint = tween.linear(baseTint, 0xFF4444, 0.3);
				break;
			case 'flying':
				// Make it brighter while keeping world color
				baseTint = Math.min(0xFFFFFF, baseTint + 0x202020);
				break;
			case 'swarm':
				// Make it slightly darker while keeping world color
				baseTint = Math.max(0x404040, baseTint - 0x202020);
				break;
		}
		return baseTint;
	};
	// Apply world-based tinting
	var worldTint = self.getWorldTint();
	enemyGraphics.tint = worldTint;
	// 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
					}
				}
			}
			// Handle burn effect (Agumon line)
			if (self.burning) {
				if (LK.ticks % 45 === 0) {
					// Every 0.75 seconds
					var burnDamage = self.burnDamage;
					// Reduce burn damage if enemy is moist
					if (self.moist && self.fireResistance) {
						burnDamage *= self.fireResistance;
					}
					self.health -= burnDamage;
					if (self.health <= 0) self.health = 0;else self.healthBar.width = self.health / self.maxHealth * 70;
				}
				self.burnDuration--;
				if (self.burnDuration <= 0) {
					self.burning = false;
				}
			}
			// Handle freeze effect (Gabumon line)
			if (self.frozen) {
				self.frozenDuration--;
				if (self.frozenDuration <= 0) {
					self.frozen = false;
					if (self.originalSpeed !== undefined) {
						self.speed = self.originalSpeed;
					}
				}
			}
			// Handle paralysis effect (Tentomon line)
			if (self.paralyzed) {
				self.paralyzeDuration--;
				if (self.paralyzeDuration <= 0) {
					self.paralyzed = false;
					if (self.originalSpeed !== undefined) {
						self.speed = self.originalSpeed;
					}
				}
			}
			// Handle moisture effect (Gomamon line)
			if (self.moist) {
				self.moistDuration--;
				if (self.moistDuration <= 0) {
					self.moist = false;
					self.fireResistance = 1.0;
					self.freezeVulnerability = 1.0;
				}
			}
		}
		// 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 GoldIndicator = Container.expand(function (value, x, y) {
	var self = Container.call(this);
	var shadowText = new Text2("+" + value, {
		size: 45,
		fill: 0x000000,
		weight: 800
	});
	shadowText.anchor.set(0.5, 0.5);
	shadowText.x = 2;
	shadowText.y = 2;
	self.addChild(shadowText);
	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 = [];
	// Add exclusive spawn and goal arrays for allies
	self.spawntwo = [];
	self.goaltwo = [];
	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
				1: Wall
				2: Spawn
				3: Goal
	*/
	// Create world-based maze layout
	self.generateMazeForWorld = function (worldNumber) {
		// Clear existing maze
		for (var i = 0; i < gridWidth; i++) {
			for (var j = 0; j < gridHeight; j++) {
				self.cells[i][j].type = 1; // Default to wall
			}
		}
		self.spawns = [];
		self.goals = [];
		// Also clear exclusive allied spawn/goal arrays
		self.spawntwo = [];
		self.goaltwo = [];
		// Always create entry area (top 5 rows) with 3-block wide path
		for (var i = 0; i < gridWidth; i++) {
			for (var j = 0; j <= 4; j++) {
				if (i >= 11 && i <= 13) {
					self.cells[i][j].type = 0; // Open path in center (3 blocks wide)
					if (j === 0) {
						self.cells[i][j].type = 2; // Spawn points
						self.spawns.push(self.cells[i][j]);
						// Invertido: ahora los goals de aliados están en la entrada
						self.goaltwo.push(self.cells[i][j]);
					}
				} else {
					self.cells[i][j].type = 1; // Walls on sides
				}
			}
		}
		// Always create exit area (bottom 5 rows) with 3-block wide path
		for (var i = 0; i < gridWidth; i++) {
			for (var j = gridHeight - 5; j < gridHeight; j++) {
				if (i >= 11 && i <= 13) {
					self.cells[i][j].type = 0; // Open path in center (3 blocks wide)
					if (j === gridHeight - 1) {
						self.cells[i][j].type = 3; // Goal points
						self.goals.push(self.cells[i][j]);
						// Invertido: ahora los spawns de aliados están en la salida
						self.spawntwo.push(self.cells[i][j]);
					}
				} else {
					self.cells[i][j].type = 1; // Walls on sides
				}
			}
		}
		// Create classic square-cornered labyrinth with generous tower placement areas
		// Simple pattern: Create a winding path with 90-degree turns and wide wall corridors
		// Path layout: Start center-top, go down, turn right, down, turn left, repeat creating a serpentine pattern
		// Main path coordinates (center of 3-block wide paths)
		// Reduced to only 2 curves for a simpler path
		var pathCenterX = 12; // Center column
		var pathPattern = [
		// Start: go down from entry
		{
			x: pathCenterX,
			startY: 5,
			endY: 12,
			type: 'vertical'
		},
		// First curve: turn left
		{
			y: 12,
			startX: pathCenterX,
			endX: 4,
			type: 'horizontal'
		},
		// Go down on left side
		{
			x: 4,
			startY: 12,
			endY: 20,
			type: 'vertical'
		},
		// Second curve: turn right to center
		{
			y: 20,
			startX: 4,
			endX: pathCenterX,
			type: 'horizontal'
		},
		// Final path to exit
		{
			x: pathCenterX,
			startY: 20,
			endY: gridHeight - 5,
			type: 'vertical'
		}];
		// Crear los segmentos del camino con 3 bloques de grosor (camino) y dejar espacio extra de 1 bloque a cada lado para asegurar zonas de 2x2 para torretas
		for (var p = 0; p < pathPattern.length; p++) {
			var segment = pathPattern[p];
			if (segment.type === 'vertical') {
				var startY = Math.min(segment.startY, segment.endY);
				var endY = Math.max(segment.startY, segment.endY);
				for (var y = startY; y <= endY; y++) {
					// Camino de 3 bloques de ancho
					for (var offset = -1; offset <= 1; offset++) {
						var pathX = segment.x + offset;
						if (pathX >= 0 && pathX < gridWidth && y >= 0 && y < gridHeight) {
							self.cells[pathX][y].type = 0;
						}
					}
					// Dejar espacio extra de 1 bloque a cada lado del camino para permitir torretas 2x2
					for (var extra = -2; extra <= 2; extra += 4) {
						var sideX = segment.x + extra;
						if (sideX >= 0 && sideX < gridWidth && y >= 0 && y < gridHeight) {
							// Solo marcar como espacio de torre si no es camino ni spawn/goal
							if (self.cells[sideX][y].type === 1) {
								// No cambiar si ya es camino/spawn/goal
								self.cells[sideX][y].type = 1; // Mantener como muro, pero dejarlo para posible torre
							}
						}
					}
					// Asegurar espacio de 2x2 para torretas a la izquierda y derecha del camino
					for (var offsetY = 0; offsetY <= 1; offsetY++) {
						for (var extra = -2; extra <= 2; extra += 4) {
							var baseX = segment.x + extra;
							var baseY = y + offsetY;
							if (baseX >= 0 && baseX + 1 < gridWidth && baseY >= 0 && baseY + 1 < gridHeight) {
								// Solo marcar como espacio de torre si ambos son muro
								if (self.cells[baseX][baseY].type === 1 && self.cells[baseX + 1][baseY].type === 1 && self.cells[baseX][baseY + 1].type === 1 && self.cells[baseX + 1][baseY + 1].type === 1) {
									// Marcar como espacio de torre (type 1) para permitir torretas 2x2
									self.cells[baseX][baseY].type = 1;
									self.cells[baseX + 1][baseY].type = 1;
									self.cells[baseX][baseY + 1].type = 1;
									self.cells[baseX + 1][baseY + 1].type = 1;
								}
							}
						}
					}
				}
			} else if (segment.type === 'horizontal') {
				var startX = Math.min(segment.startX, segment.endX);
				var endX = Math.max(segment.startX, segment.endX);
				for (var x = startX; x <= endX; x++) {
					// Camino de 3 bloques de alto
					for (var offset = -1; offset <= 1; offset++) {
						var pathY = segment.y + offset;
						if (x >= 0 && x < gridWidth && pathY >= 0 && pathY < gridHeight) {
							self.cells[x][pathY].type = 0;
						}
					}
					// Dejar espacio extra de 1 bloque arriba y abajo del camino para permitir torretas 2x2
					for (var extra = -2; extra <= 2; extra += 4) {
						var sideY = segment.y + extra;
						if (x >= 0 && x < gridWidth && sideY >= 0 && sideY < gridHeight) {
							if (self.cells[x][sideY].type === 1) {
								self.cells[x][sideY].type = 1;
							}
						}
					}
					// Asegurar espacio de 2x2 para torretas arriba y abajo del camino
					for (var offsetX = 0; offsetX <= 1; offsetX++) {
						for (var extra = -2; extra <= 2; extra += 4) {
							var baseX = x + offsetX;
							var baseY = segment.y + extra;
							if (baseX >= 0 && baseX + 1 < gridWidth && baseY >= 0 && baseY + 1 < gridHeight) {
								if (self.cells[baseX][baseY].type === 1 && self.cells[baseX + 1][baseY].type === 1 && self.cells[baseX][baseY + 1].type === 1 && self.cells[baseX + 1][baseY + 1].type === 1) {
									// Marcar como espacio de torre (type 1) para permitir torretas 2x2
									self.cells[baseX][baseY].type = 1;
									self.cells[baseX + 1][baseY].type = 1;
									self.cells[baseX][baseY + 1].type = 1;
									self.cells[baseX + 1][baseY + 1].type = 1;
								}
							}
						}
					}
				}
			}
		}
		// Asegurar conexiones suaves en las intersecciones y dejar espacio de 2x2 para torret
	};
	// Generate maze for current world
	var world = Math.ceil(currentWave / 9);
	if (world < 1) world = 1;
	if (world > 6) world = 6;
	self.generateMazeForWorld(world);
	// Apply the maze layout to cells
	for (var i = 0; i < gridWidth; i++) {
		for (var j = 0; j < gridHeight; j++) {
			var cell = self.cells[i][j];
			var cellType = cell.type; // Use the type set by generateMazeForWorld
			cell.type = cellType;
			cell.x = i;
			cell.y = 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 = [];
			// Only create debug cells for visible areas and reduce frequency
			if (j > 3 && j <= gridHeight - 4 && (i + j) % 2 === 0) {
				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];
				// Simplified pathfinding - only check cardinal directions for better performance
				var 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 with speed multiplier
			enemy.currentCellY += enemy.speed * gameSpeed;
			// Ensure enemy moves towards the center of the 3-block wide path (x=12)
			var pathCenterX = 12;
			if (enemy.currentCellX !== pathCenterX) {
				var xDiff = pathCenterX - enemy.currentCellX;
				var moveSpeed = Math.min(Math.abs(xDiff), enemy.speed * 0.5 * gameSpeed);
				enemy.currentCellX += xDiff > 0 ? moveSpeed : -moveSpeed;
			}
			// 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;
			// 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 * gameSpeed;
			enemy.currentCellY += Math.sin(angle) * enemy.speed * gameSpeed;
			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];
		}
		// Initialize stuck tracking for enemy
		if (enemy.lastPosition === undefined) {
			enemy.lastPosition = {
				x: enemy.currentCellX,
				y: enemy.currentCellY
			};
			enemy.stuckCounter = 0;
			enemy.lastMovementTime = LK.ticks;
		}
		// Check if enemy is stuck (hasn't moved significantly in a while)
		var currentPos = {
			x: enemy.currentCellX,
			y: enemy.currentCellY
		};
		var distanceMoved = Math.sqrt(Math.pow(currentPos.x - enemy.lastPosition.x, 2) + Math.pow(currentPos.y - enemy.lastPosition.y, 2));
		// If enemy hasn't moved much in the last 60 ticks (1 second), consider it stuck
		if (distanceMoved < 0.1 && LK.ticks - enemy.lastMovementTime > 60) {
			enemy.stuckCounter++;
			enemy.lastMovementTime = LK.ticks;
			// If stuck for too long, try to find alternative path
			if (enemy.stuckCounter > 3) {
				// Reset stuck counter to prevent infinite loops
				enemy.stuckCounter = 0;
				// Try to find alternative targets from current cell
				if (cell.targets && cell.targets.length > 1) {
					// Find a different target than the current one
					for (var i = 0; i < cell.targets.length; i++) {
						var alternativeTarget = cell.targets[i];
						if (alternativeTarget !== enemy.currentTarget) {
							enemy.currentTarget = alternativeTarget;
							break;
						}
					}
				} else {
					// If no alternative targets, try neighboring cells
					var neighbors = [cell.up, cell.right, cell.down, cell.left];
					var validNeighbors = [];
					for (var i = 0; i < neighbors.length; i++) {
						var neighbor = neighbors[i];
						if (neighbor && neighbor.type !== 1 && neighbor.pathId === pathId && neighbor.targets && neighbor.targets.length > 0) {
							validNeighbors.push(neighbor);
						}
					}
					if (validNeighbors.length > 0) {
						// Choose a random valid neighbor and use its target
						var randomNeighbor = validNeighbors[Math.floor(Math.random() * validNeighbors.length)];
						enemy.currentTarget = randomNeighbor.targets[0];
						// Move slightly towards the chosen neighbor to unstuck
						var neighborX = randomNeighbor.x;
						var neighborY = randomNeighbor.y;
						var unstuckAngle = Math.atan2(neighborY - enemy.currentCellY, neighborX - enemy.currentCellX);
						enemy.currentCellX += Math.cos(unstuckAngle) * enemy.speed * 0.5;
						enemy.currentCellY += Math.sin(unstuckAngle) * enemy.speed * 0.5;
					}
				}
			}
		} else if (distanceMoved >= 0.1) {
			// Enemy is moving, reset stuck tracking
			enemy.stuckCounter = 0;
			enemy.lastMovementTime = LK.ticks;
			enemy.lastPosition = {
				x: currentPos.x,
				y: currentPos.y
			};
		}
		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;
				// Reset position tracking when reaching target
				enemy.lastPosition = {
					x: enemy.currentCellX,
					y: enemy.currentCellY
				};
				return;
			}
			var angle = Math.atan2(oy, ox);
			enemy.currentCellX += Math.cos(angle) * enemy.speed * gameSpeed;
			enemy.currentCellY += Math.sin(angle) * enemy.speed * gameSpeed;
		}
		enemy.x = grid.x + enemy.currentCellX * CELL_SIZE;
		enemy.y = grid.y + enemy.currentCellY * CELL_SIZE;
	};
});
var LevelSelectionMenu = Container.expand(function (worldNumber) {
	var self = Container.call(this);
	self.worldNumber = worldNumber;
	// Position the menu at center of screen
	self.x = 2048 / 2;
	self.y = 2732 / 2;
	var menuBackground = self.attachAsset('notification', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	menuBackground.width = 1800;
	menuBackground.height = 1200;
	menuBackground.tint = 0x333333;
	menuBackground.alpha = 0.9;
	var worldNames = ["", "Forest", "Desert", "Glacier", "Village", "Inferno", "Tech Lab"];
	var titleText = new Text2('Select Level - ' + worldNames[worldNumber], {
		size: 80,
		fill: 0xFFFFFF,
		weight: 800
	});
	titleText.anchor.set(0.5, 0.5);
	titleText.y = -400;
	self.addChild(titleText);
	var worldLevels = storage.worldLevels || {
		1: 1,
		2: 1,
		3: 1,
		4: 1,
		5: 1,
		6: 1
	};
	var unlockedLevel = worldLevels[worldNumber] || 1;
	// Create level buttons in a grid
	var buttonsPerRow = 5;
	var buttonWidth = 120;
	var buttonHeight = 80;
	var buttonSpacing = 160;
	var startX = -((buttonsPerRow - 1) * buttonSpacing) / 2;
	var startY = -200;
	for (var i = 1; i <= 10; i++) {
		var levelButton = new Container();
		var levelButtonBg = levelButton.attachAsset('notification', {
			anchorX: 0.5,
			anchorY: 0.5
		});
		levelButtonBg.width = buttonWidth;
		levelButtonBg.height = buttonHeight;
		var isUnlocked = i <= unlockedLevel;
		levelButtonBg.tint = isUnlocked ? 0x4444FF : 0x666666;
		var levelButtonText = new Text2(isUnlocked ? i.toString() : "🔒", {
			size: 40,
			fill: isUnlocked ? 0xFFFFFF : 0x999999,
			weight: 800
		});
		levelButtonText.anchor.set(0.5, 0.5);
		levelButton.addChild(levelButtonText);
		// Position buttons in grid
		var row = Math.floor((i - 1) / buttonsPerRow);
		var col = (i - 1) % buttonsPerRow;
		levelButton.x = startX + col * buttonSpacing;
		levelButton.y = startY + row * 120;
		self.addChild(levelButton);
		(function (levelIndex, unlocked) {
			levelButton.down = function () {
				if (unlocked) {
					self.destroy();
					game.startWorldLevel(self.worldNumber, levelIndex);
				} else {
					var notification = game.addChild(new Notification("Complete previous level to unlock!"));
					notification.x = 2048 / 2;
					notification.y = 2732 / 2 + 200;
				}
			};
		})(i, isUnlocked);
	}
	// Back button
	var backButton = new Container();
	var backButtonBg = backButton.attachAsset('notification', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	backButtonBg.width = 200;
	backButtonBg.height = 80;
	backButtonBg.tint = 0x666666;
	var backButtonText = new Text2('Back', {
		size: 40,
		fill: 0xFFFFFF,
		weight: 800
	});
	backButtonText.anchor.set(0.5, 0.5);
	backButton.addChild(backButtonText);
	backButton.x = 0;
	backButton.y = 400;
	self.addChild(backButton);
	backButton.down = function () {
		self.destroy();
		var worldSelectionMenu = new WorldSelectionMenu();
		game.addChild(worldSelectionMenu);
	};
	return self;
});
var MainMenu = Container.expand(function () {
	var self = Container.call(this);
	// Position the menu at center of screen
	self.x = 2048 / 2;
	self.y = 2732 / 2;
	// Stop any currently playing music first
	LK.stopMusic();
	// Start main menu music
	LK.playMusic('mainMenuMusic', {
		fade: {
			start: 0,
			end: 0.8,
			duration: 1500
		}
	});
	var menuBackground = self.attachAsset('notification', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	menuBackground.width = 1800;
	menuBackground.height = 1200;
	menuBackground.tint = 0x333333;
	menuBackground.alpha = 0.9;
	var titleText = new Text2(getText('firewallDefensors'), {
		size: 100,
		fill: 0xFFFFFF,
		weight: 800
	});
	titleText.anchor.set(0.5, 0.5);
	titleText.y = -300;
	self.addChild(titleText);
	// Add version number below title
	var versionText = new Text2('v0.3', {
		size: 40,
		fill: 0xCCCCCC,
		weight: 400
	});
	versionText.anchor.set(0.5, 0.5);
	versionText.y = -220;
	self.addChild(versionText);
	// Add Beta text with Minecraft-style effects
	var betaText = new Text2('BETA', {
		size: 80,
		fill: 0xFFFF00,
		weight: 800
	});
	betaText.anchor.set(0.5, 0.5);
	betaText.x = 400;
	betaText.y = -200;
	betaText.rotation = -Math.PI / 12; // Tilt the text (15 degrees)
	betaText.alpha = 0.8;
	self.addChild(betaText);
	// Add floating animation similar to Minecraft
	var _betaFloatAnimation = function betaFloatAnimation() {
		tween(betaText, {
			y: betaText.y - 15,
			rotation: betaText.rotation + 0.05
		}, {
			duration: 2000,
			easing: tween.easeInOut,
			onFinish: function onFinish() {
				tween(betaText, {
					y: betaText.y + 15,
					rotation: betaText.rotation - 0.05
				}, {
					duration: 2000,
					easing: tween.easeInOut,
					onFinish: _betaFloatAnimation
				});
			}
		});
	};
	_betaFloatAnimation();
	// Add glow effect to beta text
	var _betaGlowAnimation = function betaGlowAnimation() {
		tween(betaText, {
			alpha: 1.0,
			scaleX: 1.1,
			scaleY: 1.1
		}, {
			duration: 1500,
			easing: tween.easeInOut,
			onFinish: function onFinish() {
				tween(betaText, {
					alpha: 0.8,
					scaleX: 1.0,
					scaleY: 1.0
				}, {
					duration: 1500,
					easing: tween.easeInOut,
					onFinish: _betaGlowAnimation
				});
			}
		});
	};
	_betaGlowAnimation();
	var startButton = new Container();
	var startButtonBg = startButton.attachAsset('notification', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	startButtonBg.width = 400;
	startButtonBg.height = 120;
	startButtonBg.tint = 0x00AA00;
	var startButtonText = new Text2(getText('startGame'), {
		size: 50,
		fill: 0xFFFFFF,
		weight: 800
	});
	startButtonText.anchor.set(0.5, 0.5);
	startButton.addChild(startButtonText);
	startButton.y = -50;
	self.addChild(startButton);
	startButton.down = function () {
		self.destroy();
		game.startGame();
	};
	// Add tutorial button
	var tutorialButton = new Container();
	var tutorialButtonBg = tutorialButton.attachAsset('notification', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	tutorialButtonBg.width = 400;
	tutorialButtonBg.height = 120;
	tutorialButtonBg.tint = 0x00AAAA;
	var tutorialButtonText = new Text2(getText('tutorial'), {
		size: 50,
		fill: 0xFFFFFF,
		weight: 800
	});
	tutorialButtonText.anchor.set(0.5, 0.5);
	tutorialButton.addChild(tutorialButtonText);
	tutorialButton.y = 80;
	self.addChild(tutorialButton);
	tutorialButton.down = function () {
		self.destroy();
		game.startTutorial();
	};
	// Add leaderboard button
	var leaderboardButton = new Container();
	var leaderboardButtonBg = leaderboardButton.attachAsset('notification', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	leaderboardButtonBg.width = 400;
	leaderboardButtonBg.height = 120;
	leaderboardButtonBg.tint = 0x4444FF;
	var leaderboardButtonText = new Text2('Leaderboard', {
		size: 50,
		fill: 0xFFFFFF,
		weight: 800
	});
	leaderboardButtonText.anchor.set(0.5, 0.5);
	leaderboardButton.addChild(leaderboardButtonText);
	leaderboardButton.y = 340;
	self.addChild(leaderboardButton);
	leaderboardButton.down = function () {
		// Defensive: try/catch in case leaderboard is not available
		try {
			if (typeof LK.showLeaderboard === "function") {
				LK.showLeaderboard();
			} else if (typeof LK.showLeaderBoard === "function") {
				LK.showLeaderBoard();
			} else {
				var notification = game.addChild(new Notification("Leaderboard not available!"));
				notification.x = 2048 / 2;
				notification.y = 2732 / 2 + 200;
			}
		} catch (e) {
			var notification = game.addChild(new Notification("Leaderboard not available!"));
			notification.x = 2048 / 2;
			notification.y = 2732 / 2 + 200;
		}
	};
	// Add language button
	var languageButton = new Container();
	var languageButtonBg = languageButton.attachAsset('notification', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	languageButtonBg.width = 400;
	languageButtonBg.height = 120;
	languageButtonBg.tint = 0xFF6600;
	var languageButtonText = new Text2(getText('language'), {
		size: 50,
		fill: 0xFFFFFF,
		weight: 800
	});
	languageButtonText.anchor.set(0.5, 0.5);
	languageButton.addChild(languageButtonText);
	languageButton.y = 210;
	self.addChild(languageButton);
	languageButton.down = function () {
		// Cycle through all four languages: English -> Spanish -> Chinese -> Portuguese -> English
		var languages = ['en', 'es', 'zh', 'pt'];
		var currentIndex = languages.indexOf(currentLanguage);
		var newIndex = (currentIndex + 1) % languages.length;
		var newLang = languages[newIndex];
		setLanguage(newLang);
		// Recreate main menu with new language
		self.destroy();
		var newMainMenu = new MainMenu();
		game.addChild(newMainMenu);
	};
	return self;
});
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 () {
		// Check if we can start next wave (10 waves max per world, all worlds)
		var worldWave = (currentWave - 1) % 10 + 1;
		if (currentWave === 0) worldWave = 0;
		// Show button during tutorial or when can start next wave
		var showForTutorial = waveIndicator && waveIndicator.gameStarted && currentWave === 0;
		var showForNextWave = waveIndicator && waveIndicator.gameStarted && worldWave < 10 && !waveInProgress && enemies.length === 0;
		var showForWorld2Plus = waveIndicator && waveIndicator.gameStarted && worldWave < 10 && !waveInProgress && enemies.length === 0;
		if (showForTutorial || showForNextWave || showForWorld2Plus) {
			self.enabled = true;
			self.visible = true;
			buttonBackground.tint = 0x0088FF;
			self.alpha = 1;
			// Update button text based on whether this is the first wave start
			if (isFirstWaveStart) {
				buttonText.setText("Start Wave");
			} else {
				buttonText.setText("Next Wave");
			}
		} else {
			self.enabled = false;
			self.visible = false;
			buttonBackground.tint = 0x888888;
			self.alpha = 0.7;
		}
	};
	self.down = function () {
		if (!self.enabled) {
			return;
		}
		if (waveIndicator.gameStarted && !waveInProgress && enemies.length === 0) {
			// Handle first wave start for selected levels
			if (isFirstWaveStart) {
				// Don't increment currentWave, just clear the flag
				isFirstWaveStart = false;
			} else {
				// Normal wave progression - increment currentWave
				if (currentWave > 0 && (currentWave - 1) % 10 + 1 < 10) {
					currentWave++; // Increment to the next wave
				} else if (currentWave === 0) {
					currentWave = 1; // Start at wave 1 if we're at wave 0 (tutorial/fresh start)
				}
			}
			// Calculate current world and level for 10-wave system
			currentWorld = Math.ceil(currentWave / 10);
			currentLevel = (currentWave - 1) % 10 + 1;
			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(currentLevel);
			var enemyCount = waveIndicator.getEnemyCount(currentLevel);
			// Update wave counter display
			updateWaveCounter();
			var notification = game.addChild(new Notification("Wave " + currentLevel + " (" + waveType + " - " + enemyCount + " enemies) activated!"));
			notification.x = 2048 / 2;
			notification.y = grid.height - 150;
			// Play wave start sound
			LK.getSound('waveStart').play();
		}
	};
	return self;
});
var Notification = Container.expand(function (message) {
	var self = Container.call(this);
	var notificationGraphics = self.attachAsset('notification', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	var notificationText = new Text2(message, {
		size: 50,
		fill: 0x000000,
		weight: 800
	});
	notificationText.anchor.set(0.5, 0.5);
	// Stretch notification background to fit text with extra padding
	var minWidth = 600; // Increased minimum width for better visibility
	var calculatedWidth = Math.max(minWidth, notificationText.width + 150);
	notificationGraphics.width = calculatedWidth;
	notificationGraphics.height = 140; // Increased height for better visibility
	self.addChild(notificationText);
	self.alpha = 1;
	var fadeOutTime = 180; // Increased display time for better readability
	// Collision avoidance system
	self.checkCollisions = function () {
		if (!self.parent) return;
		var notifications = [];
		for (var i = 0; i < self.parent.children.length; i++) {
			var child = self.parent.children[i];
			if (child instanceof Notification && child !== self && child.alpha > 0) {
				notifications.push(child);
			}
		}
		// Sort notifications by creation order (older first)
		notifications.sort(function (a, b) {
			return (a._creationTime || 0) - (b._creationTime || 0);
		});
		// Check for overlaps and adjust position
		for (var i = 0; i < notifications.length; i++) {
			var other = notifications[i];
			var dx = Math.abs(self.x - other.x);
			var dy = Math.abs(self.y - other.y);
			var minDistance = 140; // Minimum vertical distance between notifications
			if (dx < calculatedWidth * 0.7 && dy < minDistance) {
				// Move this notification up to avoid overlap
				var offset = minDistance - dy + 10;
				self.y -= offset;
				// Ensure notification stays within screen bounds
				if (self.y < 100) {
					self.y = other.y + minDistance;
				}
			}
		}
	};
	// Store creation time for sorting
	self._creationTime = LK.ticks || Date.now();
	// Initial collision check
	LK.setTimeout(function () {
		self.checkCollisions();
	}, 1);
	self.update = function () {
		// Periodic collision checking during early life
		if (fadeOutTime > 150) {
			self.checkCollisions();
		}
		if (fadeOutTime > 0) {
			fadeOutTime--;
			self.alpha = Math.min(fadeOutTime / 180 * 2, 1);
		} else {
			self.destroy();
		}
	};
	// Add smooth entrance animation with tween
	self.scaleX = 0.3;
	self.scaleY = 0.3;
	tween(self, {
		scaleX: 1.0,
		scaleY: 1.0
	}, {
		duration: 200,
		easing: tween.backOut
	});
	return self;
});
var SourceTower = Container.expand(function (towerType) {
	var self = Container.call(this);
	self.towerType = towerType || 'default';
	// Increase size of base for easier touch
	var baseGraphics = self.attachAsset('tower', {
		anchorX: 0.5,
		anchorY: 0.5,
		scaleX: 1.5,
		scaleY: 1.5
	});
	switch (self.towerType) {
		case 'rapid':
			baseGraphics.tint = 0x00AAFF;
			break;
		case 'sniper':
			baseGraphics.tint = 0xFF5500;
			break;
		case 'splash':
			baseGraphics.tint = 0x33CC00;
			break;
		case 'slow':
			baseGraphics.tint = 0x9900FF;
			break;
		case 'poison':
			baseGraphics.tint = 0x00FFAA;
			break;
		default:
			baseGraphics.tint = 0xAAAAAA;
	}
	var towerCost = getTowerCost(self.towerType);
	// Add shadow for tower type label
	var typeLabelShadow = new Text2(self.towerType.charAt(0).toUpperCase() + self.towerType.slice(1), {
		size: 50,
		fill: 0x000000,
		weight: 800
	});
	typeLabelShadow.anchor.set(0.5, 0.5);
	typeLabelShadow.x = 4;
	typeLabelShadow.y = -20 + 4;
	self.addChild(typeLabelShadow);
	// Add tower type label
	var typeLabel = new Text2(self.towerType.charAt(0).toUpperCase() + self.towerType.slice(1), {
		size: 60,
		fill: 0xFFFFFF,
		weight: 800
	});
	typeLabel.anchor.set(0.5, 0.5);
	typeLabel.y = -25; // Position above center of tower
	self.addChild(typeLabel);
	// Add cost shadow
	var costLabelShadow = new Text2(towerCost, {
		size: 50,
		fill: 0x000000,
		weight: 800
	});
	costLabelShadow.anchor.set(0.5, 0.5);
	costLabelShadow.x = 4;
	costLabelShadow.y = 24 + 12;
	self.addChild(costLabelShadow);
	// Add cost label
	var costLabel = new Text2(towerCost + ' bits', {
		size: 60,
		fill: 0xFFD700,
		weight: 800
	});
	costLabel.anchor.set(0.5, 0.5);
	costLabel.y = 25 + 12;
	self.addChild(costLabel);
	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 StorySequence = Container.expand(function (worldNumber) {
	var self = Container.call(this);
	self.worldNumber = worldNumber;
	self.currentPanel = 0;
	self.panels = [];
	self.onComplete = null;
	// Position at center of screen
	self.x = 2048 / 2;
	self.y = 2732 / 2;
	// Semi-transparent background overlay
	var overlay = self.attachAsset('notification', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	overlay.width = 2048;
	overlay.height = 2732;
	overlay.tint = 0x000000;
	overlay.alpha = 0.7;
	self.getWorldStory = function (worldNumber) {
		switch (worldNumber) {
			case 1:
				return [{
					text: getText('story1_1'),
					image: 'agumon'
				}, {
					text: getText('story1_2'),
					image: 'gabumon'
				}, {
					text: getText('story1_3'),
					image: 'tentomon'
				}];
			case 2:
				return [{
					text: getText('story2_1'),
					image: 'palmon'
				}, {
					text: getText('story2_2'),
					image: 'gomamon'
				}, {
					text: getText('story2_3'),
					image: 'patamon'
				}];
			case 3:
				return [{
					text: getText('story3_1'),
					image: null
				}, {
					text: getText('story3_2'),
					image: null
				}, {
					text: getText('story3_3'),
					image: null
				}];
			case 4:
				return [{
					text: getText('story4_1'),
					image: 'angemon'
				}, {
					text: getText('story4_2'),
					image: 'magnaangemon'
				}, {
					text: getText('story4_3'),
					image: 'seraphimon'
				}];
			case 5:
				return [{
					text: getText('story5_1'),
					image: 'greymon'
				}, {
					text: getText('story5_2'),
					image: 'metalgreymon'
				}, {
					text: getText('story5_3'),
					image: 'wargreymon'
				}];
			case 6:
				return [{
					text: getText('story6_1'),
					image: 'garurumon'
				}, {
					text: getText('story6_2'),
					image: 'weregarurumon'
				}, {
					text: getText('story6_3'),
					image: 'metalgarurumon'
				}];
			default:
				return [{
					text: getText('storyDefault_1'),
					image: 'agumon'
				}, {
					text: getText('storyDefault_2'),
					image: 'gabumon'
				}, {
					text: getText('storyDefault_3'),
					image: 'tentomon'
				}];
		}
	};
	// World-specific story content
	var storyData = self.getWorldStory(worldNumber);
	// Create panels
	for (var i = 0; i < storyData.length; i++) {
		var panel = new ComicPanel(storyData[i].image, storyData[i].text);
		panel.x = (i - 1) * 650; // Position panels side by side
		self.addChild(panel);
		self.panels.push(panel);
	}
	// Navigation indicators
	var indicatorContainer = new Container();
	indicatorContainer.y = 250;
	self.addChild(indicatorContainer);
	for (var i = 0; i < self.panels.length; i++) {
		var indicator = indicatorContainer.attachAsset('towerLevelIndicator', {
			anchorX: 0.5,
			anchorY: 0.5
		});
		indicator.width = 20;
		indicator.height = 20;
		indicator.tint = i === 0 ? 0xffffff : 0x666666;
		indicator.x = (i - (self.panels.length - 1) / 2) * 40;
	}
	// Skip button
	var skipButton = new Container();
	var skipBg = skipButton.attachAsset('notification', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	skipBg.width = 150;
	skipBg.height = 60;
	skipBg.tint = 0x666666;
	var skipText = new Text2('Skip', {
		size: 40,
		fill: 0xffffff,
		weight: 800
	});
	skipText.anchor.set(0.5, 0.5);
	skipButton.addChild(skipText);
	skipButton.x = 400;
	skipButton.y = -300;
	self.addChild(skipButton);
	skipButton.down = function () {
		self.complete();
	};
	self.showPanel = function (index) {
		if (index < 0 || index >= self.panels.length) return;
		// Hide all panels
		for (var i = 0; i < self.panels.length; i++) {
			self.panels[i].alpha = 0;
			indicatorContainer.children[i].tint = 0x666666;
		}
		// Show current panel
		self.panels[index].show();
		indicatorContainer.children[index].tint = 0xffffff;
		self.currentPanel = index;
	};
	self.nextPanel = function () {
		if (self.currentPanel < self.panels.length - 1) {
			self.showPanel(self.currentPanel + 1);
		} else {
			self.complete();
		}
	};
	self.complete = function () {
		if (self.onComplete) {
			self.onComplete();
		}
		self.destroy();
	};
	// Show first panel
	self.showPanel(0);
	// Only set up auto-advance for panels after the first one
	var autoAdvanceTimer = null;
	self.down = function () {
		if (autoAdvanceTimer) {
			LK.clearTimeout(autoAdvanceTimer);
		}
		self.nextPanel();
	};
	return self;
});
var Tower = Container.expand(function (id) {
	var self = Container.call(this);
	self.id = id || 'default';
	self.level = 1;
	self.maxLevel = 6;
	self.gridX = 0;
	self.gridY = 0;
	self.range = 3 * CELL_SIZE;
	// Tower health system
	self.maxHealth = 100;
	self.health = self.maxHealth;
	// 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 'sniper':
				// Sniper: base 5, +0.8 per level, but final upgrade gets a huge boost
				if (self.level === self.maxLevel) {
					return 12 * CELL_SIZE; // Significantly increased range for max level
				}
				return (5 + (self.level - 1) * 0.8) * CELL_SIZE;
			case 'splash':
				// Splash: base 2, +0.2 per level (max ~4 blocks at max level)
				return (2 + (self.level - 1) * 0.2) * CELL_SIZE;
			case 'rapid':
				// Rapid: base 2.5, +0.5 per level
				return (2.5 + (self.level - 1) * 0.5) * CELL_SIZE;
			case 'slow':
				// Slow: base 3.5, +0.5 per level
				return (3.5 + (self.level - 1) * 0.5) * CELL_SIZE;
			case 'poison':
				// Poison: base 3.2, +0.5 per level
				return (3.2 + (self.level - 1) * 0.5) * CELL_SIZE;
			default:
				// Default: base 3, +0.5 per level
				return (3 + (self.level - 1) * 0.5) * CELL_SIZE;
		}
	};
	self.cellsInRange = [];
	self.fireRate = 60;
	self.bulletSpeed = 5;
	self.damage = 10;
	self.lastFired = 0;
	self.targetEnemy = null;
	switch (self.id) {
		case 'agumon':
			// Fire-based attacks, burns enemies
			self.fireRate = 60;
			self.damage = 12;
			self.range = 3 * CELL_SIZE;
			self.bulletSpeed = 6;
			self.maxHealth = 120; // Tanky fire dragon
			break;
		case 'gabumon':
			// Ice-based attacks, freezes enemies
			self.fireRate = 55;
			self.damage = 10;
			self.range = 3.2 * CELL_SIZE;
			self.bulletSpeed = 6;
			self.maxHealth = 110; // Ice wolf durability
			break;
		case 'tentomon':
			// Electric paralysis attacks
			self.fireRate = 70;
			self.damage = 14;
			self.range = 3.5 * CELL_SIZE;
			self.bulletSpeed = 8;
			self.maxHealth = 90; // Electric insect, moderate health
			break;
		case 'palmon':
			// Poison attacks that spread
			self.fireRate = 65;
			self.damage = 11;
			self.range = 3 * CELL_SIZE;
			self.bulletSpeed = 5;
			self.maxHealth = 100; // Plant creature, balanced
			break;
		case 'gomamon':
			// Water/moisture attacks, weakens fire resistance
			self.fireRate = 60;
			self.damage = 9;
			self.range = 3.3 * CELL_SIZE;
			self.bulletSpeed = 5;
			self.maxHealth = 105; // Aquatic mammal, decent health
			break;
		case 'patamon':
			// Healing and support abilities
			self.fireRate = 80;
			self.damage = 8;
			self.range = 4 * CELL_SIZE; // Larger healing range
			self.bulletSpeed = 7;
			self.maxHealth = 85; // Angel creature, less physical but supportive
			break;
	}
	self.health = self.maxHealth;
	var baseGraphics = self.attachAsset('tower', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	switch (self.id) {
		case 'gabumon':
			//{93} // Blue colors for Gabumon
			baseGraphics.tint = 0x00AAFF;
			break;
		case 'tentomon':
			//{96} // Red colors for Tentomon
			baseGraphics.tint = 0xFF5500;
			break;
		case 'palmon':
			//{99} // Green colors for Palmon
			baseGraphics.tint = 0x33CC00;
			break;
		case 'gomamon':
			//{9c} // Purple colors for Gomamon
			baseGraphics.tint = 0x9900FF;
			break;
		case 'patamon':
			//{9f} // Cyan colors for Patamon
			baseGraphics.tint = 0x00FFAA;
			break;
		default:
			//{9i} // Agumon default
			baseGraphics.tint = 0xAAAAAA;
	}
	// Tower health bar
	var towerHealthBarOutline = self.attachAsset('healthBarOutline', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	var towerHealthBar = self.attachAsset('healthBar', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	towerHealthBarOutline.width = 80;
	towerHealthBarOutline.height = 8;
	towerHealthBarOutline.tint = 0x000000;
	towerHealthBar.width = 76;
	towerHealthBar.height = 6;
	towerHealthBar.tint = 0x00ff00;
	towerHealthBarOutline.y = towerHealthBar.y = -CELL_SIZE - 15;
	self.towerHealthBar = towerHealthBar;
	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 = 0x000000;
		var towerLevelIndicator = dot.attachAsset('towerLevelIndicator', {
			anchorX: 0.5,
			anchorY: 0.5
		});
		towerLevelIndicator.width = dotSize;
		towerLevelIndicator.height = dotSize;
		towerLevelIndicator.tint = 0xCCCCCC;
		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(self.id, {
		anchorX: 0.5,
		anchorY: 0.5
	});
	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 = 0xFFFFFF;
			} else {
				switch (self.id) {
					case 'rapid':
						towerLevelIndicator.tint = 0x00AAFF;
						break;
					case 'sniper':
						towerLevelIndicator.tint = 0xFF5500;
						break;
					case 'splash':
						towerLevelIndicator.tint = 0x33CC00;
						break;
					case 'slow':
						towerLevelIndicator.tint = 0x9900FF;
						break;
					case 'poison':
						towerLevelIndicator.tint = 0x00FFAA;
						break;
					default:
						towerLevelIndicator.tint = 0xAAAAAA;
				}
			}
		}
	};
	self.updateLevelIndicators();
	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++;
				// Play tower upgrade sound
				LK.getSound('towerUpgrade').play();
				// --- HEALTH UPGRADE LOGIC ---
				// Increase maxHealth and heal tower on upgrade
				var prevMaxHealth = self.maxHealth;
				// Health scaling per tower type
				switch (self.id) {
					case 'agumon':
						self.maxHealth = 120 + (self.level - 1) * 20;
						break;
					case 'gabumon':
						self.maxHealth = 110 + (self.level - 1) * 18;
						break;
					case 'tentomon':
						self.maxHealth = 90 + (self.level - 1) * 15;
						break;
					case 'palmon':
						self.maxHealth = 100 + (self.level - 1) * 18;
						break;
					case 'gomamon':
						self.maxHealth = 105 + (self.level - 1) * 17;
						break;
					case 'patamon':
						self.maxHealth = 85 + (self.level - 1) * 14;
						break;
					default:
						self.maxHealth = 100 + (self.level - 1) * 15;
						break;
				}
				// Heal tower to new max health on upgrade
				self.health = self.maxHealth;
				// --- DAMAGE/FIRERATE UPGRADE LOGIC ---
				// Tweak last upgrade so it's not overpowered
				if (self.id === 'rapid') {
					if (self.level === self.maxLevel) {
						// Last upgrade: only a moderate boost, not double
						self.fireRate = Math.max(8, 30 - self.level * 6); // was 4, now 8 min
						self.damage = 5 + self.level * 5; // was 10, now 5 per level
						self.bulletSpeed = 7 + self.level * 1.2; // was 2.4, now 1.2 per level
					} 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) {
						// Last upgrade: only a moderate boost, not double
						self.fireRate = Math.max(10, 60 - self.level * 12); // was 5, now 10 min
						self.damage = 10 + self.level * 10; // was 20, now 10 per level
						self.bulletSpeed = 5 + self.level * 1.2; // was 2.4, now 1.2 per level
					} 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();
				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
							});
						}
					});
				}
				// Check if tower reached maximum level and award bonus
				if (self.level === self.maxLevel) {
					// Award 300 bits and security points for reaching max level
					setGold(gold + 300);
					score += 300;
					// Save security points to storage
					storage.securityPoints = score;
					updateUI();
					var notification = game.addChild(new Notification("Max level reached! +300 bits & security points!"));
					notification.x = 2048 / 2;
					notification.y = grid.height - 50;
				}
				return true;
			} else {
				var notification = game.addChild(new Notification("Not enough bits 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 () {
		// Update tower health bar
		self.towerHealthBar.width = self.health / self.maxHealth * 76;
		if (self.health / self.maxHealth > 0.6) {
			self.towerHealthBar.tint = 0x00ff00; // Green
		} else if (self.health / self.maxHealth > 0.3) {
			self.towerHealthBar.tint = 0xffff00; // Yellow
		} else {
			self.towerHealthBar.tint = 0xff0000; // Red
		}
		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;
			var effectiveFireRate = gameSpeed > 1 ? Math.max(1, Math.floor(self.fireRate / gameSpeed)) : self.fireRate;
			if (LK.ticks - self.lastFired >= effectiveFireRate) {
				self.fire();
				self.lastFired = LK.ticks;
			}
		}
	};
	self.down = function (x, y, obj) {
		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.3;
		// Add evolution arrow if digivice is available and tower level is appropriate
		function canDigivolve() {
			if (self.level < 2) return false; // Need at least level 2
			var hasDigivice = false;
			if (self.level >= 2 && self.level <= 3 && storage.digiviceC) hasDigivice = true;
			if (self.level >= 4 && self.level <= 5 && storage.digiviceB) hasDigivice = true;
			if (self.level >= 6 && storage.digiviceA) hasDigivice = true;
			return hasDigivice;
		}
		if (canDigivolve()) {
			var evolutionArrow = rangeIndicator.attachAsset('arrow', {
				anchorX: 0.5,
				anchorY: 0.5
			});
			evolutionArrow.width = 60;
			evolutionArrow.height = 60;
			evolutionArrow.tint = 0xFF6600;
			evolutionArrow.x = self.getRange() + 40;
			evolutionArrow.y = -40;
			evolutionArrow.rotation = -Math.PI / 4; // Point diagonally up-right
			// Add glow effect
			evolutionArrow.alpha = 0.8;
			var _glowTween = function glowTween() {
				tween(evolutionArrow, {
					alpha: 1.0,
					scaleX: 1.2,
					scaleY: 1.2
				}, {
					duration: 500,
					easing: tween.easeInOut,
					onFinish: function onFinish() {
						tween(evolutionArrow, {
							alpha: 0.8,
							scaleX: 1.0,
							scaleY: 1.0
						}, {
							duration: 500,
							easing: tween.easeInOut,
							onFinish: _glowTween
						});
					}
				});
			};
			_glowTween();
		}
		// Check if clicked on evolution arrow
		var clickedOnEvolutionArrow = false;
		if (rangeIndicator.children.length > 1) {
			// Has evolution arrow
			var evolutionArrow = rangeIndicator.children[1];
			var arrowGlobalPos = rangeIndicator.toGlobal(evolutionArrow.position);
			var arrowX = arrowGlobalPos.x;
			var arrowY = arrowGlobalPos.y;
			if (Math.abs(x - arrowX) < 40 && Math.abs(y - arrowY) < 40) {
				clickedOnEvolutionArrow = true;
				// Trigger digivolution
				var evolutionCost = getTowerCost(self.id) * 2;
				if (gold >= evolutionCost) {
					setGold(gold - evolutionCost);
					// Apply evolution effects
					self.damage *= 1.5;
					self.fireRate = Math.max(5, Math.floor(self.fireRate * 0.8));
					var notification = game.addChild(new Notification(self.id.toUpperCase() + " DIGIVOLVED!"));
					notification.x = 2048 / 2;
					notification.y = grid.height - 50;
					// Visual evolution effect
					LK.effects.flashObject(self, 0xFF6600, 1000);
					// Remove evolution arrow after use
					rangeIndicator.removeChild(evolutionArrow);
				} else {
					var notification = game.addChild(new Notification("Not enough bits to digivolve!"));
					notification.x = 2048 / 2;
					notification.y = grid.height - 50;
				}
			}
		}
		if (!clickedOnEvolutionArrow) {
			var upgradeMenu = new UpgradeMenu(self);
			game.addChild(upgradeMenu);
			upgradeMenu.x = 2048 / 2;
			tween(upgradeMenu, {
				y: 2732 - 225
			}, {
				duration: 200,
				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 () {
		// Patamon healing ability - heal nearby towers instead of attacking
		if (self.id === 'patamon' && LK.ticks % 120 === 0) {
			// Every 2 seconds
			var healingRange = self.getRange();
			var healAmount = 5 + self.level * 2;
			var towersHealed = 0;
			for (var i = 0; i < towers.length; i++) {
				var otherTower = towers[i];
				if (otherTower !== self && otherTower.health < otherTower.maxHealth) {
					var dx = otherTower.x - self.x;
					var dy = otherTower.y - self.y;
					var distance = Math.sqrt(dx * dx + dy * dy);
					if (distance <= healingRange) {
						otherTower.health = Math.min(otherTower.maxHealth, otherTower.health + healAmount);
						otherTower.towerHealthBar.width = otherTower.health / otherTower.maxHealth * 76;
						towersHealed++;
						// Visual healing effect
						var healEffect = new EffectIndicator(otherTower.x, otherTower.y, 'heal');
						game.addChild(healEffect);
					}
				}
			}
			if (towersHealed > 0) {
				// Show healing indicator on Patamon
				var healSelfEffect = new EffectIndicator(self.x, self.y, 'heal');
				game.addChild(healSelfEffect);
			}
		}
		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 and add special effects
				bullet.type = self.id;
				bullet.sourceTowerLevel = self.level;
				// Special attack patterns for different evolutionary lines
				switch (self.id) {
					case 'agumon':
						// Fire attacks that can burn enemies
						// Burn chance: 5% at level 1, up to 50% at max level (linear)
						bullet.burnChance = Math.min(0.05 + (self.level - 1) * 0.09, 0.5);
						break;
					case 'gabumon':
						// Ice attacks that can freeze enemies
						// Freeze chance: 5% at level 1, up to 50% at max level (linear)
						bullet.freezeChance = Math.min(0.05 + (self.level - 1) * 0.09, 0.5);
						break;
					case 'tentomon':
						// Electric attacks that can paralyze multiple enemies
						// Paralyze chance: 5% at level 1, up to 55% at max level (linear)
						bullet.paralyzeChance = Math.min(0.05 + (self.level - 1) * 0.10, 0.55);
						bullet.paralyzeArea = CELL_SIZE * (1 + self.level * 0.2);
						break;
					case 'palmon':
						// Poison attacks that spread to nearby enemies
						// Poison spread chance: 5% at level 1, up to 50% at max level (linear)
						bullet.poisonSpread = true;
						bullet.poisonSpreadChance = Math.min(0.05 + (self.level - 1) * 0.09, 0.5);
						bullet.poisonRadius = CELL_SIZE * (0.8 + self.level * 0.15);
						break;
					case 'gomamon':
						// Water attacks that make enemies wet (vulnerable to freeze, resistant to burn)
						bullet.moistureEffect = true;
						bullet.moistureDuration = 180 + self.level * 30;
						break;
				}
				// Customize bullet appearance based on tower type
				switch (self.id) {
					case 'rapid':
						bullet.children[0].tint = 0x00AAFF;
						bullet.children[0].width = 20;
						bullet.children[0].height = 20;
						break;
					case 'sniper':
						bullet.children[0].tint = 0xFF5500;
						bullet.children[0].width = 15;
						bullet.children[0].height = 15;
						break;
					case 'splash':
						bullet.children[0].tint = 0x33CC00;
						bullet.children[0].width = 40;
						bullet.children[0].height = 40;
						break;
					case 'slow':
						bullet.children[0].tint = 0x9900FF;
						bullet.children[0].width = 35;
						bullet.children[0].height = 35;
						break;
					case 'poison':
						bullet.children[0].tint = 0x00FFAA;
						bullet.children[0].width = 35;
						bullet.children[0].height = 35;
						break;
				}
				game.addChild(bullet);
				bullets.push(bullet);
				self.targetEnemy.bulletsTargetingThis.push(bullet);
				// --- 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;
		// Mark cells as occupied by tower (type 1 = wall/occupied)
		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();
	};
	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;
		switch (self.towerType) {
			case 'rapid':
				previewGraphics.tint = 0x00AAFF;
				break;
			case 'sniper':
				previewGraphics.tint = 0xFF5500;
				break;
			case 'splash':
				previewGraphics.tint = 0x33CC00;
				break;
			case 'slow':
				previewGraphics.tint = 0x9900FF;
				break;
			case 'poison':
				previewGraphics.tint = 0x00FFAA;
				break;
			default:
				previewGraphics.tint = 0xAAAAAA;
		}
		if (!self.canPlace || !self.hasEnoughGold) {
			previewGraphics.tint = 0xFF0000;
		}
	};
	self.updatePlacementStatus = function () {
		var validGridPlacement = true;
		// Check if tower would be placed within valid grid bounds
		if (self.gridX < 0 || self.gridY < 0 || self.gridX + 1 >= grid.cells.length || self.gridY + 1 >= grid.cells[0].length) {
			validGridPlacement = false;
		} else {
			// Check if all 4 cells for the 2x2 tower are available (not on enemy path)
			for (var i = 0; i < 2; i++) {
				for (var j = 0; j < 2; j++) {
					var cell = grid.getCell(self.gridX + i, self.gridY + j);
					// Only allow placement on wall cells (type 1) - not on path, spawn, or goal
					if (!cell || cell.type !== 1) {
						validGridPlacement = false;
						break;
					}
				}
				if (!validGridPlacement) {
					break;
				}
			}
		}
		self.blockedByEnemy = false;
		// Remove enemy blocking detection since towers can only be placed on wall tiles
		// which enemies cannot occupy anyway
		self.blockedByEnemy = false;
		self.canPlace = validGridPlacement;
		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 TutorialSequence = Container.expand(function () {
	var self = Container.call(this);
	console.log("Starting interactive tutorial sequence");
	self.currentStep = 0;
	self.onComplete = null;
	self.tutorialActive = true;
	self.waitingForUserAction = false;
	self.stepCompleted = false;
	// Tutorial steps data
	self.tutorialSteps = [{
		textKey: 'tutorialWelcome',
		action: 'intro'
	}, {
		textKey: 'tutorialStep1',
		action: 'placeTower',
		completeTextKey: 'tutorialStep1Complete'
	}, {
		textKey: 'tutorialStep2',
		action: 'startWave',
		completeTextKey: 'tutorialStep2Complete'
	}, {
		textKey: 'tutorialStep3',
		action: 'upgradeTower',
		completeTextKey: 'tutorialStep3Complete'
	}, {
		textKey: 'tutorialCompleted',
		action: 'finish'
	}];
	// Semi-transparent background overlay
	var overlay = self.attachAsset('notification', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	overlay.width = 2048;
	overlay.height = 2732;
	overlay.tint = 0x000000;
	overlay.alpha = 0.4;
	// Instruction panel
	var instructionBg = self.attachAsset('notification', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	instructionBg.width = 1600;
	instructionBg.height = 400;
	instructionBg.tint = 0x222222;
	instructionBg.alpha = 0.95;
	instructionBg.x = 2048 / 2;
	instructionBg.y = 400;
	var instructionText = new Text2(getText(self.tutorialSteps[0].textKey), {
		size: 50,
		fill: 0xFFFFFF,
		weight: 600
	});
	instructionText.anchor.set(0.5, 0.5);
	instructionText.wordWrap = true;
	instructionText.wordWrapWidth = 1500;
	instructionText.x = 2048 / 2;
	instructionText.y = 400;
	self.addChild(instructionText);
	// Next button
	var nextButton = new Container();
	var nextBg = nextButton.attachAsset('notification', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	nextBg.width = 250;
	nextBg.height = 80;
	nextBg.tint = 0x00AA00;
	var nextText = new Text2(getText('nextStep'), {
		size: 45,
		fill: 0xFFFFFF,
		weight: 800
	});
	nextText.anchor.set(0.5, 0.5);
	nextButton.addChild(nextText);
	nextButton.x = 2048 / 2;
	nextButton.y = 580;
	self.addChild(nextButton);
	// Skip button
	var skipButton = new Container();
	var skipBg = skipButton.attachAsset('notification', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	skipBg.width = 200;
	skipBg.height = 80;
	skipBg.tint = 0x666666;
	var skipText = new Text2(getText('skipTutorial'), {
		size: 35,
		fill: 0xffffff,
		weight: 800
	});
	skipText.anchor.set(0.5, 0.5);
	skipButton.addChild(skipText);
	skipButton.x = 2048 - 150;
	skipButton.y = 150;
	self.addChild(skipButton);
	// Button handlers
	nextButton.down = function () {
		self.nextStep();
	};
	skipButton.down = function () {
		self.complete();
	};
	// Update button state
	self.updateButtonState = function () {
		if (self.waitingForUserAction && !self.stepCompleted) {
			nextBg.tint = 0x666666;
			nextButton.alpha = 0.5;
			nextText.setText('Complete Action');
		} else {
			nextBg.tint = 0x00AA00;
			nextButton.alpha = 1.0;
			nextText.setText(getText('nextStep'));
		}
	};
	self.nextStep = function () {
		// Don't advance if waiting for user action and step not completed
		if (self.waitingForUserAction && !self.stepCompleted) {
			return;
		}
		self.currentStep++;
		if (self.currentStep >= self.tutorialSteps.length) {
			self.complete();
			return;
		}
		self.waitingForUserAction = false;
		self.stepCompleted = false;
		var step = self.tutorialSteps[self.currentStep];
		instructionText.setText(getText(step.textKey));
		// Handle specific step actions
		switch (step.action) {
			case 'placeTower':
				self.waitingForUserAction = true;
				// Give enough gold for tutorial
				setGold(200);
				updateUI();
				break;
			case 'startWave':
				self.waitingForUserAction = true;
				waveIndicator.gameStarted = true;
				break;
			case 'upgradeTower':
				self.waitingForUserAction = true;
				// Give extra gold for upgrades
				setGold(gold + 200);
				updateUI();
				break;
		}
		self.updateButtonState();
	};
	// Check for step completion
	self.update = function () {
		if (!self.waitingForUserAction || self.stepCompleted) {
			return;
		}
		var step = self.tutorialSteps[self.currentStep];
		switch (step.action) {
			case 'placeTower':
				if (towers.length > 0) {
					self.stepCompleted = true;
					if (step.completeTextKey) {
						instructionText.setText(getText(step.completeTextKey));
					}
					self.updateButtonState();
				}
				break;
			case 'startWave':
				if (waveInProgress || currentWave > 0) {
					self.stepCompleted = true;
					if (step.completeTextKey) {
						instructionText.setText(getText(step.completeTextKey));
					}
					self.updateButtonState();
				}
				break;
			case 'upgradeTower':
				// Check if any tower has been upgraded or if upgrade menu was closed
				var hasUpgradedTower = false;
				for (var i = 0; i < towers.length; i++) {
					if (towers[i].level > 1) {
						hasUpgradedTower = true;
						break;
					}
				}
				// Also check if upgrade menu was opened and closed
				var upgradeMenuExists = false;
				for (var i = 0; i < game.children.length; i++) {
					if (game.children[i] instanceof UpgradeMenu) {
						upgradeMenuExists = true;
						break;
					}
				}
				// If tower was upgraded OR if menu was opened and then closed
				if (hasUpgradedTower || !upgradeMenuExists && self.menuWasOpened) {
					self.stepCompleted = true;
					if (step.completeTextKey) {
						instructionText.setText(getText(step.completeTextKey));
					}
					self.updateButtonState();
				}
				// Track if menu was opened
				if (upgradeMenuExists) {
					self.menuWasOpened = true;
				}
				break;
		}
	};
	// Initialize first step
	self.updateButtonState();
	self.complete = function () {
		self.tutorialActive = false;
		// Mark tutorial as completed globally
		tutorialCompleted = true;
		tutorialInProgress = false;
		// Stop all music first to prevent mixing
		LK.stopMusic();
		// Clear tutorial game state
		currentWave = 0;
		currentWorld = 1;
		currentLevel = 1;
		waveInProgress = false;
		waveSpawned = false;
		waveIndicator.gameStarted = false;
		gold = 80;
		lives = 20;
		score = 0;
		enemiesKilledInWave = 0;
		// Clear all game entities
		while (enemies.length > 0) {
			var enemy = enemies.pop();
			if (enemy.parent) {
				enemy.parent.removeChild(enemy);
			}
			if (enemy.shadow && enemy.shadow.parent) {
				enemy.shadow.parent.removeChild(enemy.shadow);
			}
		}
		while (towers.length > 0) {
			var tower = towers.pop();
			if (tower.parent) {
				tower.parent.removeChild(tower);
			}
		}
		while (bullets.length > 0) {
			var bullet = bullets.pop();
			if (bullet.parent) {
				bullet.parent.removeChild(bullet);
			}
		}
		while (alliedUnits.length > 0) {
			var unit = alliedUnits.pop();
			if (unit.parent) {
				unit.parent.removeChild(unit);
			}
		}
		// Clear grid state
		for (var i = 0; i < 24; i++) {
			for (var j = 0; j < 35; j++) {
				if (grid.cells[i] && grid.cells[i][j]) {
					grid.cells[i][j].towersInRange = [];
				}
			}
		}
		// Reset UI
		updateUI();
		updateWaveCounter();
		if (self.onComplete) {
			self.onComplete();
		}
		self.destroy();
		// Wait before creating main menu to ensure clean transition
		LK.setTimeout(function () {
			// Return to main menu after tutorial
			var mainMenu = new MainMenu();
			game.addChild(mainMenu);
		}, 500);
	};
	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 = 600;
	menuBackground.tint = 0x444444;
	menuBackground.alpha = 0.9;
	var towerTypeText = new Text2(self.tower.id.charAt(0).toUpperCase() + self.tower.id.slice(1) + ' Tower', {
		size: 80,
		fill: 0xFFFFFF,
		weight: 800
	});
	towerTypeText.anchor.set(0, 0);
	towerTypeText.x = -840;
	towerTypeText.y = -160;
	self.addChild(towerTypeText);
	var statsText = new Text2('Level: ' + self.tower.level + '/' + self.tower.maxLevel + '\nHealth: ' + self.tower.health + '/' + self.tower.maxHealth + '\nDamage: ' + self.tower.damage + '\nFire Rate: ' + (60 / self.tower.fireRate).toFixed(1) + '/s', {
		size: 60,
		fill: 0xFFFFFF,
		weight: 400
	});
	statsText.anchor.set(0, 0.5);
	statsText.x = -840;
	statsText.y = 50;
	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 + ' bits', {
		size: 60,
		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 + ' bits', {
		size: 60,
		fill: 0xFFFFFF,
		weight: 800
	});
	sellButtonText.anchor.set(0.5, 0.5);
	sellButton.addChild(sellButtonText);
	upgradeButton.y = -75;
	sellButton.y = 75;
	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) {
		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('Level: ' + self.tower.level + '/' + self.tower.maxLevel + '\nDamage: ' + self.tower.damage + '\nFire Rate: ' + (60 / self.tower.fireRate).toFixed(1) + '/s');
			buttonText.setText('Upgrade: ' + upgradeCost + ' bits');
			var totalInvestment = self.tower.getTotalValue ? self.tower.getTotalValue() : 0;
			var sellValue = Math.floor(totalInvestment * 0.6);
			sellButtonText.setText('Sell: +' + sellValue + ' bits');
			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) {
		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 + " bits!"));
		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);
					}
				}
			}
		}
		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();
		// Sincronizar el mapa de visualización después de vender torre
		syncVisualizationMap();
		worldRenderer.updateWorldGraphics(currentWorld, mapVisualization);
		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) {
		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 + ' bits';
		if (buttonText.text !== newText) {
			buttonText.setText(newText);
		}
	};
	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;
	// Add shadow for start text
	var startTextShadow = new Text2("Start Game", {
		size: 50,
		fill: 0x000000,
		weight: 800
	});
	startTextShadow.anchor.set(0.5, 0.5);
	startTextShadow.x = 4;
	startTextShadow.y = 4;
	startMarker.addChild(startTextShadow);
	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!");
			startTextShadow.setText("Started!");
			// Make sure shadow position remains correct after text change
			startTextShadow.x = 4;
			startTextShadow.y = 4;
			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 shadow for wave type - 30% smaller than before
		var waveTypeShadow = new Text2(waveType, {
			size: 56,
			fill: 0x000000,
			weight: 800
		});
		waveTypeShadow.anchor.set(0.5, 0.5);
		waveTypeShadow.x = 4;
		waveTypeShadow.y = 4;
		marker.addChild(waveTypeShadow);
		// Add wave type text - 30% smaller than before
		var waveTypeText = new Text2(waveType, {
			size: 56,
			fill: 0xFFFFFF,
			weight: 800
		});
		waveTypeText.anchor.set(0.5, 0.5);
		waveTypeText.y = 0;
		marker.addChild(waveTypeText);
		// Add shadow for wave number - 20% larger than before
		var waveNumShadow = new Text2((i + 1).toString(), {
			size: 48,
			fill: 0x000000,
			weight: 800
		});
		waveNumShadow.anchor.set(1.0, 1.0);
		waveNumShadow.x = blockWidth / 2 - 16 + 5;
		waveNumShadow.y = block.height / 2 - 12 + 5;
		marker.addChild(waveNumShadow);
		// Main wave number text - 20% larger than before
		var waveNum = new Text2((i + 1).toString(), {
			size: 48,
			fill: 0xFFFFFF,
			weight: 800
		});
		waveNum.anchor.set(1.0, 1.0);
		waveNum.x = blockWidth / 2 - 16;
		waveNum.y = block.height / 2 - 12;
		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;
		// Fix positioning to properly show first wave when currentWave is 0
		var displayWave = Math.max(0, currentWave);
		var moveAmount = (progress + displayWave) * 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];
			// Fix comparison to properly handle wave 0 and 1
			if (i - 1 < displayWave) {
				block.alpha = .5;
			}
		}
		self.handleWaveProgression = function () {
			if (!self.gameStarted) {
				return;
			}
			// Waves no longer advance automatically - they must be triggered manually via NextWaveButton
			// This function now only handles the initial game start
		};
		self.handleWaveProgression();
	};
	return self;
});
var WorldRenderer = Container.expand(function () {
	var self = Container.call(this);
	self.currentWorld = 1;
	self.backgroundTiles = [];
	self.pathTiles = [];
	self.wallTiles = [];
	self.sceneryElements = [];
	self.getWorldAssets = function (worldNumber) {
		switch (worldNumber) {
			case 1:
				return {
					background: 'forestBg',
					path: 'forestPath',
					wall: 'forestWall',
					scenery: 'forestScenery',
					ambient: 0x90ee90
				};
			case 2:
				return {
					background: 'desertBg',
					path: 'desertPath',
					wall: 'desertWall',
					scenery: 'desertScenery',
					ambient: 0xffd700
				};
			case 3:
				return {
					background: 'glacierBg',
					path: 'glacierPath',
					wall: 'glacierWall',
					scenery: 'glacierScenery',
					ambient: 0xe6f3ff
				};
			case 4:
				return {
					background: 'villageBg',
					path: 'villagePath',
					wall: 'villageWall',
					scenery: 'villageScenery',
					ambient: 0xf0e68c
				};
			case 5:
				return {
					background: 'infernoBg',
					path: 'infernoPath',
					wall: 'infernoWall',
					scenery: 'infernoScenery',
					ambient: 0xff6347
				};
			case 6:
				return {
					background: 'techLabBg',
					path: 'techLabPath',
					wall: 'techLabWall',
					scenery: 'techLabScenery',
					ambient: 0x87ceeb
				};
			default:
				return {
					background: 'forestBg',
					path: 'forestPath',
					wall: 'forestWall',
					scenery: 'forestScenery',
					ambient: 0x90ee90
				};
		}
	};
	self.updateWorldGraphics = function (worldNumber, gridInstance) {
		// Forzar renderizado siempre, incluso si el mundo no cambia
		self.currentWorld = worldNumber;
		var worldAssets = self.getWorldAssets(worldNumber);
		// Clear existing tiles
		while (self.backgroundTiles.length) {
			self.removeChild(self.backgroundTiles.pop());
		}
		while (self.pathTiles.length) {
			self.removeChild(self.pathTiles.pop());
		}
		while (self.wallTiles.length) {
			self.removeChild(self.wallTiles.pop());
		}
		while (self.sceneryElements.length) {
			self.removeChild(self.sceneryElements.pop());
		}
		// Create new tiles based on current world using the correct assets
		var gridWidth = 24;
		var gridHeight = 35;
		for (var i = 0; i < gridWidth; i++) {
			for (var j = 0; j < gridHeight; j++) {
				// Create background tile using world-specific assets
				var bgTile = self.attachAsset(worldAssets.background, {
					anchorX: 0,
					anchorY: 0
				});
				bgTile.x = i * CELL_SIZE;
				bgTile.y = j * CELL_SIZE;
				self.backgroundTiles.push(bgTile);
				// Add world-specific scenery elements randomly
				if (Math.random() < 0.15) {
					// 15% chance for scenery - use world-specific scenery assets
					var scenery = self.attachAsset(worldAssets.scenery, {
						anchorX: 0.5,
						anchorY: 0.5
					});
					scenery.x = i * CELL_SIZE + CELL_SIZE / 2;
					scenery.y = j * CELL_SIZE + CELL_SIZE / 2;
					// World-specific scenery shapes
					switch (worldNumber) {
						case 1:
							// Forest - trees/bushes (ellipses)
							scenery.scaleY = 1.2 + Math.random() * 0.8;
							break;
						case 2:
							// Desert - cacti/rocks (tall thin or wide)
							if (Math.random() < 0.5) {
								scenery.scaleX = 0.6;
								scenery.scaleY = 1.8; // Tall cactus
							} else {
								scenery.scaleX = 1.4;
								scenery.scaleY = 0.7; // Wide rock
							}
							break;
						case 3:
							// Glacier - ice crystals
							scenery.scaleX = 0.8 + Math.random() * 0.4;
							scenery.scaleY = 0.8 + Math.random() * 0.4;
							scenery.alpha = 0.7;
							break;
						case 4:
							// Village - small structures
							scenery.scaleX = 1.2;
							scenery.scaleY = 1.0;
							break;
						case 5:
							// Technology - machinery
							scenery.scaleX = 0.9;
							scenery.scaleY = 0.9;
							scenery.alpha = 0.8;
							break;
						case 6:
							// Hell - lava bubbles/flames
							scenery.scaleX = 1.1 + Math.random() * 0.6;
							scenery.scaleY = 1.1 + Math.random() * 0.6;
							scenery.alpha = 0.9;
							break;
					}
					self.sceneryElements.push(scenery);
				}
			}
		}
		// Overlay path and wall tiles on top of scenery
		if (gridInstance && gridInstance.cells) {
			for (var i = 0; i < Math.min(gridWidth, gridInstance.cells.length); i++) {
				for (var j = 0; j < Math.min(gridHeight, gridInstance.cells[i].length); j++) {
					var cell = gridInstance.cells[i][j];
					if (cell.type === 0 || cell.type === 2 || cell.type === 3) {
						// Path, spawn, or goal - use world-specific path assets
						var pathTile = self.attachAsset(worldAssets.path, {
							anchorX: 0,
							anchorY: 0
						});
						pathTile.x = i * CELL_SIZE;
						pathTile.y = j * CELL_SIZE;
						pathTile.alpha = 0.95;
						self.pathTiles.push(pathTile);
					} else if (cell.type === 1) {
						// Wall - use appropriate wall tile based on world
						var wallTile = self.attachAsset(worldAssets.wall, {
							anchorX: 0,
							anchorY: 0
						});
						wallTile.x = i * CELL_SIZE;
						wallTile.y = j * CELL_SIZE;
						wallTile.alpha = 0.98;
						self.wallTiles.push(wallTile);
					}
				}
			}
		}
		// Asegurar que WorldRenderer esté en el fondo, debajo de todo
		if (self.parent) {
			self.parent.addChildAt(self, 0);
		}
	};
	return self;
});
var WorldSelectionMenu = Container.expand(function () {
	var self = Container.call(this);
	// Position the menu at center of screen
	self.x = 2048 / 2;
	self.y = 2732 / 2;
	var menuBackground = self.attachAsset('notification', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	menuBackground.width = 1800;
	menuBackground.height = 1200;
	menuBackground.tint = 0x333333;
	menuBackground.alpha = 0.9;
	var titleText = new Text2('Select World', {
		size: 80,
		fill: 0xFFFFFF,
		weight: 800
	});
	titleText.anchor.set(0.5, 0.5);
	titleText.y = -400;
	self.addChild(titleText);
	var worldNames = ["Forest", "Desert", "Glacier", "Village", "Inferno", "Tech Lab"];
	var unlockedWorlds = storage.unlockedWorlds || 1;
	for (var i = 0; i < worldNames.length; i++) {
		var worldButton = new Container();
		var worldButtonBg = worldButton.attachAsset('notification', {
			anchorX: 0.5,
			anchorY: 0.5
		});
		worldButtonBg.width = 350;
		worldButtonBg.height = 80;
		var isUnlocked = i + 1 <= unlockedWorlds;
		worldButtonBg.tint = isUnlocked ? 0x4444FF : 0x666666;
		var worldButtonText = new Text2(isUnlocked ? worldNames[i] : "Locked", {
			size: 40,
			fill: isUnlocked ? 0xFFFFFF : 0x999999,
			weight: 800
		});
		worldButtonText.anchor.set(0.5, 0.5);
		worldButton.addChild(worldButtonText);
		worldButton.y = -200 + i * 100;
		self.addChild(worldButton);
		(function (worldIndex, unlocked) {
			worldButton.down = function () {
				if (unlocked) {
					self.destroy();
					var levelSelectionMenu = new LevelSelectionMenu(worldIndex + 1);
					game.addChild(levelSelectionMenu);
				} else {
					var notification = game.addChild(new Notification("Complete the previous world to unlock!"));
					notification.x = 2048 / 2;
					notification.y = 2732 / 2 + 200;
				}
			};
		})(i, isUnlocked);
	}
	return self;
});
/**** 
* Initialize Game
****/ 
var game = new LK.Game({
	backgroundColor: 0x333333
});
/**** 
* Game Code
****/ 
// Re-inicializar todos los sonidos y música para asegurar que el audio funcione correctamente
// This ensures the browser only requests microphone access, not camera.
// Only use microphone features from facekit. Do NOT call any camera-related methods.
// NOTE: We only use facekit for microphone/voice features (facekit.volume).
// We never call any camera or face detection methods, so the browser will only request microphone access.
// Voice & Summoning
// Boss & Special Events
// UI & Feedback
// Special Effects
// Combat & Action Sounds
// Music system for world progression
// Notify user about microphone usage for voice commands
// Digimon Evolution Assets for Voice Summoning
// Agumon Evolution Line (Fire/Dragon)
// Gabumon Evolution Line (Ice/Wolf)
// Tentomon Evolution Line (Electric/Insect)
// Palmon Evolution Line (Plant/Poison)
// Gomamon Evolution Line (Water)
// Patamon Evolution Line (Sacred/Angel)
// Try to import facekit but make it optional to prevent loading issues
var facekit = null;
try {
	facekit = LK.import("@upit/facekit.v1");
} catch (e) {
	console.log("Facekit not available - voice features disabled");
	// Create a mock facekit object to prevent errors
	facekit = {
		volume: 0
	};
}
console.log("This game uses microphone for voice summoning commands, not camera.");
function playWorldMusic(worldNumber, isBoss, isFinalBoss) {
	// Stop any currently playing music first to prevent mixing
	LK.stopMusic();
	// Play world-specific music with fade in effect
	var musicId;
	// Check for boss music first
	if (isFinalBoss) {
		musicId = 'finalBossMusic';
	} else if (isBoss) {
		musicId = 'bossMusic';
	} else {
		switch (worldNumber) {
			case 1:
				musicId = 'forestMusic';
				break;
			case 2:
				musicId = 'desertMusic';
				break;
			case 3:
				musicId = 'glacierMusic';
				break;
			case 4:
				musicId = 'villageMusic';
				break;
			case 5:
				musicId = 'infernoMusic';
				break;
			case 6:
				musicId = 'techLabMusic';
				break;
			default:
				musicId = 'forestMusic';
		}
	}
	console.log("Playing music for world " + worldNumber + ": " + musicId);
	LK.playMusic(musicId, {
		fade: {
			start: 0,
			end: isBoss || isFinalBoss ? 1.0 : 0.8,
			duration: 1500
		}
	});
}
// Language system
var currentLanguage = storage.language || 'en';
var translations = {
	en: {
		firewallDefensors: 'Firewall Defenders',
		startGame: 'Start Game',
		tutorial: 'Tutorial',
		language: 'Language',
		selectWorld: 'Select World',
		selectLevel: 'Select Level',
		back: 'Back',
		locked: 'Locked',
		completeLevel: 'Complete previous level to unlock!',
		completeWorld: 'Complete the previous world to unlock!',
		nextWave: 'Next Wave',
		shop: 'Shop',
		bits: 'Bits',
		systemHealth: 'System Health',
		securityScore: 'Security Score',
		wave: 'Wave',
		upgrade: 'Upgrade',
		sell: 'Sell',
		maxLevel: 'Max Level',
		digivolve: 'Digivolve',
		owned: 'OWNED',
		notEnoughBits: 'Not enough bits!',
		cannotBuild: 'Cannot build here!',
		pathBlocked: 'Tower would block the path!',
		tutorialWelcome: 'Welcome to the Tutorial!\n\nThis game uses microphone for voice commands (not camera).\nLearn to play step by step...',
		tutorialStep1: 'Step 1: Tower Placement\n\nDrag a Digimon tower from the bottom of the screen to a valid position on the battlefield.',
		tutorialStep2: 'Step 2: Starting Waves\n\nClick the "Next Wave" button to start sending enemies. Towers will attack automatically.',
		tutorialStep3: 'Step 3: Upgrading Towers\n\nClick on a placed tower and then press the "Upgrade" button to increase its power. Close the menu when done!',
		tutorialCompleted: 'Tutorial completed!\n\nNow you know how to:\n• Place towers\n• Start waves\n• Upgrade towers\n• Shout Digimon names to summon allies (requires microphone)\n\nDefend the digital world!',
		tutorialStep1Complete: 'Excellent! You placed your first tower.',
		tutorialStep2Complete: 'Well done! The wave has started.',
		tutorialStep3Complete: 'Perfect! You upgraded the tower.',
		nextStep: 'Next',
		skipTutorial: 'Skip Tutorial',
		// Story translations (EN)
		story1_1: "ALERT! Pokémon infiltrators have breached\nthe Digital Forest servers! They're attempting\nto steal Digimon data files!",
		story1_2: "These Pokémon spies are using advanced\nstealth protocols to access our core database.\nDeploy Digimon guardians immediately!",
		story1_3: "The future of the Digimon franchise\ndepends on you! Stop Pokémon from\ncorrupting our digital ecosystem!",
		story2_1: "Pokémon agents have infiltrated the Desert\nData Center! They're planting malicious code\nto corrupt our systems!",
		story2_2: "Fire-type Pokémon are overheating our\nservers while others steal precious\nDigimon evolution data!",
		story2_3: "Their coordinated attack is more sophisticated\nthan before. Pokémon want to monopolize\nthe children's entertainment industry!",
		story3_1: "Ice-type Pokémon have frozen our Glacier\nservers to slow down our defenses\nwhile they extract data!",
		story3_2: "Flying Pokémon are bypassing our security\nwalls! They're trying to reach the core\nDigimon genetic database!",
		story3_3: "Critical system temperatures detected!\nPokémon are trying to cause a complete\nserver meltdown!",
		story4_1: "Pokémon sleeper agents hidden in the Village\nNetwork have activated! They've been\ngathering intelligence for months!",
		story4_2: "Multiple Pokémon strike teams are attacking\nsimultaneously, trying to overwhelm\nour Digimon defenders!",
		story4_3: "This is corporate espionage on a massive\nscale! Pokémon Company wants to steal\nour digital creature technology!",
		story5_1: "MAXIMUM THREAT LEVEL! Elite Pokémon\nhackers have breached our most secure\nTechnology Labs!",
		story5_2: "They're using legendary Pokémon abilities\nto bypass our quantum encryption!\nOur most sensitive data is at risk!",
		story5_3: "Deploy our strongest Mega-level Digimon!\nOnly they can stop this corporate\ncyber warfare!",
		story6_1: "FINAL ASSAULT! Pokémon's master plan\nis revealed - they want to delete ALL\nDigimon data permanently!",
		story6_2: "Legendary Pokémon themselves are leading\nthis final attack on our core servers!\nThis is the ultimate battle for supremacy!",
		story6_3: "The children's hearts are at stake!\nDefeat Pokémon's invasion and save\nthe future of digital monsters forever!",
		storyDefault_1: "Pokémon infiltrators detected! Protect the Digimon database!",
		storyDefault_2: "Deploy your Digimon to stop the corporate espionage!",
		storyDefault_3: "Save the Digital World from Pokémon's takeover!"
	},
	es: {
		firewallDefensors: 'Defensores del Cortafuegos',
		startGame: 'Iniciar Juego',
		tutorial: 'Tutorial',
		language: 'Idioma',
		selectWorld: 'Seleccionar Mundo',
		selectLevel: 'Seleccionar Nivel',
		back: 'Atrás',
		locked: 'Bloqueado',
		completeLevel: '¡Completa el nivel anterior para desbloquear!',
		completeWorld: '¡Completa el mundo anterior para desbloquear!',
		nextWave: 'Siguiente Oleada',
		shop: 'Tienda',
		bits: 'Bits',
		systemHealth: 'Salud del Sistema',
		securityScore: 'Puntuación de Seguridad',
		wave: 'Oleada',
		upgrade: 'Mejorar',
		sell: 'Vender',
		maxLevel: 'Nivel Máximo',
		digivolve: 'Digievolucionar',
		owned: 'POSEÍDO',
		notEnoughBits: '¡No tienes suficientes bits!',
		cannotBuild: '¡No se puede construir aquí!',
		pathBlocked: '¡La torreta bloquearía el camino!',
		tutorialWelcome: '¡Bienvenido al Tutorial!\n\nEste juego usa el micrófono para comandos de voz (no la cámara).\nAprende a jugar paso a paso...',
		tutorialStep1: 'Paso 1: Colocación de torretas\n\nArrastra una torreta Digimon desde la parte inferior de la pantalla hasta una posición válida en el campo de batalla.',
		tutorialStep2: 'Paso 2: Iniciar oleadas\n\nHaz clic en "Siguiente Oleada" para enviar enemigos. Las torretas atacarán automáticamente.',
		tutorialStep3: 'Paso 3: Mejorar torretas\n\nHaz clic en una torreta y presiona "Mejorar" para aumentar su poder. ¡Cierra el menú al terminar!',
		tutorialCompleted: '¡Tutorial completado!\n\nAhora sabes:\n• Colocar torretas\n• Iniciar oleadas\n• Mejorar torretas\n• Gritar nombres Digimon para invocar aliados\n\n¡Defiende el mundo digital!',
		tutorialStep1Complete: 'Excelente! Has colocado tu primera torreta.',
		tutorialStep2Complete: 'Bien hecho! La oleada ha comenzado.',
		tutorialStep3Complete: 'Perfecto! Has mejorado la torreta.',
		nextStep: 'Siguiente',
		skipTutorial: 'Saltar Tutorial',
		// Story translations (ES)
		story1_1: "¡ALERTA! ¡Infiltradores Pokémon han violado\nlos servidores del Bosque Digital!\n¡Intentan robar archivos de datos Digimon!",
		story1_2: "¡Estos espías Pokémon usan protocolos avanzados\nde sigilo para acceder a nuestra base de datos central!\n¡Despliega guardianes Digimon de inmediato!",
		story1_3: "¡El futuro de la franquicia Digimon depende de ti!\n¡Detén a Pokémon antes de que corrompan\nnuestro ecosistema digital!",
		story2_1: "¡Agentes Pokémon han infiltrado el Centro de Datos del Desierto!\n¡Están plantando código malicioso\npara corromper nuestros sistemas!",
		story2_2: "¡Pokémon de tipo fuego están sobrecalentando nuestros servidores\nmientras otros roban valiosos datos\nde evolución Digimon!",
		story2_3: "¡Su ataque coordinado es más sofisticado que antes!\n¡Pokémon quiere monopolizar la industria\ndel entretenimiento infantil!",
		story3_1: "¡Pokémon de tipo hielo han congelado nuestros servidores Glaciar\npara ralentizar nuestras defensas\nmientras extraen datos!",
		story3_2: "¡Pokémon voladores están evadiendo nuestros muros de seguridad!\n¡Intentan llegar a la base genética central\nde Digimon!",
		story3_3: "¡Temperaturas críticas detectadas!\n¡Pokémon intenta provocar un colapso\ntotal del servidor!",
		story4_1: "¡Agentes durmientes Pokémon ocultos en la Red de la Aldea se han activado!\n¡Han estado recolectando inteligencia\ndurante meses!",
		story4_2: "¡Múltiples equipos de ataque Pokémon atacan simultáneamente,\nintentando sobrepasar a nuestros defensores Digimon!",
		story4_3: "¡Esto es espionaje corporativo a gran escala!\n¡La Compañía Pokémon quiere robar nuestra tecnología\nde criaturas digitales!",
		story5_1: "¡NIVEL DE AMENAZA MÁXIMO! ¡Hackers Pokémon de élite han violado\nnuestros Laboratorios de Tecnología más seguros!",
		story5_2: "¡Usan habilidades legendarias Pokémon para evadir nuestro cifrado cuántico!\n¡Nuestros datos más sensibles están en riesgo!",
		story5_3: "¡Despliega nuestros Digimon Mega más fuertes!\n¡Solo ellos pueden detener esta guerra cibernética corporativa!",
		story6_1: "¡ASALTO FINAL! ¡El plan maestro de Pokémon se revela:\nquieren borrar TODOS los datos Digimon permanentemente!",
		story6_2: "¡Pokémon legendarios lideran este ataque final a nuestros servidores centrales!\n¡Es la batalla definitiva por la supremacía!",
		story6_3: "¡El corazón de los niños está en juego!\n¡Derrota la invasión Pokémon y salva el futuro\nde los monstruos digitales para siempre!",
		storyDefault_1: "¡Infiltradores Pokémon detectados! ¡Protege la base de datos Digimon!",
		storyDefault_2: "¡Despliega tus Digimon para detener el espionaje corporativo!",
		storyDefault_3: "¡Salva el Mundo Digital del dominio de Pokémon!"
	},
	zh: {
		firewallDefensors: '防火墙守护者',
		startGame: '开始游戏',
		tutorial: '教程',
		language: '语言',
		selectWorld: '选择世界',
		selectLevel: '选择关卡',
		back: '返回',
		locked: '锁定',
		completeLevel: '完成前一关卡以解锁!',
		completeWorld: '完成前一世界以解锁!',
		nextWave: '下一波',
		shop: '商店',
		bits: '比特',
		systemHealth: '系统健康',
		securityScore: '安全分数',
		wave: '波次',
		upgrade: '升级',
		sell: '出售',
		maxLevel: '最高等级',
		digivolve: '数码进化',
		owned: '已拥有',
		notEnoughBits: '比特不足!',
		cannotBuild: '无法在此建造!',
		pathBlocked: '塔会阻挡路径!',
		tutorialWelcome: '欢迎来到教程!\n\n此游戏使用麦克风进行语音指令(不使用摄像头)。\n逐步学习游戏玩法...',
		tutorialStep1: '第1步:塔的放置\n\n从屏幕底部拖拽数码宝贝塔到战场上的有效位置。',
		tutorialStep2: '第2步:开始波次\n\n点击"下一波"按钮开始派遣敌人。塔会自动攻击。',
		tutorialStep3: '第3步:升级塔\n\n点击已放置的塔,然后按"升级"按钮增强其威力。完成后关闭菜单!',
		tutorialCompleted: '教程完成!\n\n现在您知道如何:\n• 放置塔\n• 开始波次\n• 升级塔\n• 喊出数码宝贝名字召唤盟友(需要麦克风)\n\n保卫数字世界!',
		tutorialStep1Complete: '出色!您放置了第一座塔。',
		tutorialStep2Complete: '做得好!波次已开始。',
		tutorialStep3Complete: '完美!您升级了塔。',
		nextStep: '下一步',
		skipTutorial: '跳过教程',
		// Story translations (ZH)
		story1_1: "警报!宝可梦入侵者已突破\n数字森林服务器!他们试图\n窃取数码宝贝数据文件!",
		story1_2: "这些宝可梦间谍使用先进的\n隐身协议访问我们的核心数据库。\n立即部署数码宝贝守护者!",
		story1_3: "数码宝贝系列的未来\n取决于您!阻止宝可梦\n破坏我们的数字生态系统!",
		story2_1: "宝可梦特工已渗透沙漠\n数据中心!他们正在植入恶意代码\n来破坏我们的系统!",
		story2_2: "火系宝可梦正在过热我们的\n服务器,而其他宝可梦则在窃取珍贵的\n数码宝贝进化数据!",
		story2_3: "他们的协同攻击比以前更加复杂。\n宝可梦想要垄断\n儿童娱乐产业!",
		story3_1: "冰系宝可梦已冻结我们的冰川\n服务器以减缓我们的防御\n同时提取数据!",
		story3_2: "飞行宝可梦正在绕过我们的安全\n防火墙!他们试图到达核心\n数码宝贝基因数据库!",
		story3_3: "检测到关键系统温度!\n宝可梦试图造成完全的\n服务器崩溃!",
		story4_1: "隐藏在村庄\n网络中的宝可梦潜伏特工已激活!他们\n已收集情报数月!",
		story4_2: "多个宝可梦突击队同时攻击,\n试图压垮\n我们的数码宝贝防御者!",
		story4_3: "这是大规模的企业间谍活动!\n宝可梦公司想要窃取\n我们的数字生物技术!",
		story5_1: "最高威胁级别!精英宝可梦\n黑客已突破我们最安全的\n技术实验室!",
		story5_2: "他们使用传说宝可梦的能力\n绕过我们的量子加密!\n我们最敏感的数据处于危险中!",
		story5_3: "部署我们最强的超级数码宝贝!\n只有他们能阻止这场企业\n网络战争!",
		story6_1: "最终攻击!宝可梦的主计划\n被揭露——他们想要永久删除所有\n数码宝贝数据!",
		story6_2: "传说宝可梦本身正在领导\n对我们核心服务器的最终攻击!\n这是争夺霸权的终极战斗!",
		story6_3: "孩子们的心灵处于危险中!\n击败宝可梦的入侵,拯救\n数字怪兽的未来!",
		storyDefault_1: "发现宝可梦入侵者!保护数码宝贝数据库!",
		storyDefault_2: "部署您的数码宝贝阻止企业间谍活动!",
		storyDefault_3: "从宝可梦的接管中拯救数字世界!"
	},
	pt: {
		firewallDefensors: 'Defensores do Firewall',
		startGame: 'Iniciar Jogo',
		tutorial: 'Tutorial',
		language: 'Idioma',
		selectWorld: 'Selecionar Mundo',
		selectLevel: 'Selecionar Nível',
		back: 'Voltar',
		locked: 'Bloqueado',
		completeLevel: 'Complete o nível anterior para desbloquear!',
		completeWorld: 'Complete o mundo anterior para desbloquear!',
		nextWave: 'Próxima Onda',
		shop: 'Loja',
		bits: 'Bits',
		systemHealth: 'Saúde do Sistema',
		securityScore: 'Pontuação de Segurança',
		wave: 'Onda',
		upgrade: 'Melhorar',
		sell: 'Vender',
		maxLevel: 'Nível Máximo',
		digivolve: 'Digievolução',
		owned: 'POSSUÍDO',
		notEnoughBits: 'Bits insuficientes!',
		cannotBuild: 'Não é possível construir aqui!',
		pathBlocked: 'A torre bloquearia o caminho!',
		tutorialWelcome: 'Bem-vindo ao Tutorial!\n\nEste jogo usa microfone para comandos de voz (não câmera).\nAprenda a jogar passo a passo...',
		tutorialStep1: 'Passo 1: Colocação de Torres\n\nArraste uma torre Digimon da parte inferior da tela para uma posição válida no campo de batalha.',
		tutorialStep2: 'Passo 2: Iniciando Ondas\n\nClique no botão "Próxima Onda" para começar a enviar inimigos. As torres atacarão automaticamente.',
		tutorialStep3: 'Passo 3: Melhorando Torres\n\nClique em uma torre colocada e pressione o botão "Melhorar" para aumentar seu poder. Feche o menu quando terminar!',
		tutorialCompleted: 'Tutorial concluído!\n\nAgora você sabe como:\n• Colocar torres\n• Iniciar ondas\n• Melhorar torres\n• Gritar nomes de Digimon para invocar aliados (requer microfone)\n\nDefenda o mundo digital!',
		tutorialStep1Complete: 'Excelente! Você colocou sua primeira torre.',
		tutorialStep2Complete: 'Muito bem! A onda começou.',
		tutorialStep3Complete: 'Perfeito! Você melhorou a torre.',
		nextStep: 'Próximo',
		skipTutorial: 'Pular Tutorial',
		// Story translations (PT)
		story1_1: "ALERTA! Infiltradores Pokémon violaram\nos servidores da Floresta Digital! Eles estão tentando\nroubar arquivos de dados Digimon!",
		story1_2: "Esses espiões Pokémon estão usando protocolos\navançados de furtividade para acessar nosso banco de dados central.\nImplante guardiões Digimon imediatamente!",
		story1_3: "O futuro da franquia Digimon\ndepende de você! Impeça os Pokémon de\ncorromper nosso ecossistema digital!",
		story2_1: "Agentes Pokémon infiltraram o Centro\nde Dados do Deserto! Eles estão plantando código malicioso\npara corromper nossos sistemas!",
		story2_2: "Pokémon do tipo fogo estão superaquecendo nossos\nservidores enquanto outros roubam dados preciosos\nde evolução Digimon!",
		story2_3: "Seu ataque coordenado é mais sofisticado\nque antes. Os Pokémon querem monopolizar\na indústria de entretenimento infantil!",
		story3_1: "Pokémon do tipo gelo congelaram nossos servidores\nda Geleira para retardar nossas defesas\nenquanto extraem dados!",
		story3_2: "Pokémon voadores estão contornando nossas paredes\nde segurança! Eles estão tentando alcançar o banco\nde dados genéticos central dos Digimon!",
		story3_3: "Temperaturas críticas do sistema detectadas!\nOs Pokémon estão tentando causar um\ncolapso completo do servidor!",
		story4_1: "Agentes adormecidos Pokémon escondidos na Rede\nda Vila foram ativados! Eles estiveram\ncoletando inteligência por meses!",
		story4_2: "Múltiplas equipes de ataque Pokémon estão atacando\nsimultaneamente, tentando sobrecarregar\nnossos defensores Digimon!",
		story4_3: "Esta é espionagem corporativa em escala massiva!\nA Empresa Pokémon quer roubar\nnossa tecnologia de criaturas digitais!",
		story5_1: "NÍVEL MÁXIMO DE AMEAÇA! Hackers Pokémon de elite\nviolaram nossos Laboratórios de Tecnologia\nmais seguros!",
		story5_2: "Eles estão usando habilidades de Pokémon lendários\npara contornar nossa criptografia quântica!\nNossos dados mais sensíveis estão em risco!",
		story5_3: "Implante nossos Digimon Mega mais fortes!\nSomente eles podem parar esta\nguerra cibernética corporativa!",
		story6_1: "ASSALTO FINAL! O plano mestre dos Pokémon\nfoi revelado - eles querem deletar TODOS\nos dados Digimon permanentemente!",
		story6_2: "Os próprios Pokémon lendários estão liderando\neste ataque final aos nossos servidores centrais!\nEsta é a batalha definitiva pela supremacia!",
		story6_3: "Os corações das crianças estão em jogo!\nDerrote a invasão Pokémon e salve\no futuro dos monstros digitais para sempre!",
		storyDefault_1: "Infiltradores Pokémon detectados! Proteja o banco de dados Digimon!",
		storyDefault_2: "Implante seus Digimon para parar a espionagem corporativa!",
		storyDefault_3: "Salve o Mundo Digital da tomada de controle dos Pokémon!"
	}
};
function getText(key) {
	return translations[currentLanguage][key] || translations.en[key] || key;
}
function setLanguage(lang) {
	currentLanguage = lang;
	storage.language = lang;
}
// Assets adicionales para tiles específicos por mundo
var isHidingUpgradeMenu = false;
function hideUpgradeMenu(menu) {
	if (isHidingUpgradeMenu) {
		return;
	}
	isHidingUpgradeMenu = true;
	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;
// Ensure pathId is incremented on every pathFind to keep pathfinding in sync
var enemies = [];
var towers = [];
var bullets = [];
var defenses = [];
var alliedUnits = []; // Array to track summoned allied Digimon units
var selectedTower = null;
var gold = 80; // Base starting gold, will be updated based on world selection
var lives = 20;
var score = storage.securityPoints || 0;
var currentWave = 0;
var totalWaves = 10; // 10 waves per world
var currentWorld = 1;
var currentLevel = 1;
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 enemiesKilledInWave = 0;
var coins = [];
// Allied units management
var maxAlliedUnits = 5; // Maximum number of allied units at once
var summonCooldown = 300; // 5 seconds at 60 FPS
var lastSummonTime = 0;
// Voice summoning system
var voiceSummonCooldown = {};
var voiceDetectionActive = false;
var lastVoiceDetectionTime = 0;
var voiceDetectionCooldown = 60; // 1 second between voice detections
var coinSpawnTimer = 0;
var goldText = new Text2(getText('bits') + ': ' + gold, {
	size: 60,
	fill: 0xFFD700,
	weight: 800
});
goldText.anchor.set(0.5, 0.5);
// Create health bar container
var healthBarContainer = new Container();
var healthBarBg = healthBarContainer.attachAsset('healthBarOutline', {
	anchorX: 0.5,
	anchorY: 0.5
});
healthBarBg.width = 300;
healthBarBg.height = 20;
healthBarBg.tint = 0x000000;
var healthBarFill = healthBarContainer.attachAsset('healthBar', {
	anchorX: 0.5,
	anchorY: 0.5
});
healthBarFill.width = 296;
healthBarFill.height = 16;
healthBarFill.tint = 0x00FF00;
// Add health text label
var livesText = new Text2(getText('systemHealth'), {
	size: 45,
	fill: 0xFFFFFF,
	weight: 800
});
livesText.anchor.set(0.5, 0.5);
livesText.y = -35;
healthBarContainer.addChild(livesText);
// Contenedor para la puntuación de seguridad, con texto arriba y número abajo
var scoreContainer = new Container();
var scoreLabel = new Text2(getText('securityScore'), {
	size: currentLanguage === 'es' ? 40 : 50,
	fill: 0xFF0000,
	weight: 800
});
scoreLabel.anchor.set(0.5, 1.0);
scoreLabel.y = 0;
var scoreValue = new Text2(score.toString(), {
	size: 60,
	fill: 0xFF0000,
	weight: 800
});
scoreValue.anchor.set(0.5, 0);
scoreValue.y = 8;
scoreContainer.addChild(scoreLabel);
scoreContainer.addChild(scoreValue);
var topMargin = 50;
var centerX = 2048 / 2;
var spacing = 400;
// Create speed control button
var speedButton = new Container();
var speedButtonBg = speedButton.attachAsset('notification', {
	anchorX: 0.5,
	anchorY: 0.5
});
speedButtonBg.width = 150;
speedButtonBg.height = 80;
speedButtonBg.tint = 0x00AA00;
var speedButtonText = new Text2('1x', {
	size: 50,
	fill: 0xFFFFFF,
	weight: 800
});
speedButtonText.anchor.set(0.5, 0.5);
speedButton.addChild(speedButtonText);
var gameSpeed = 1;
var speedLevels = [1, 2];
var currentSpeedIndex = 0;
speedButton.down = function () {
	currentSpeedIndex = (currentSpeedIndex + 1) % speedLevels.length;
	gameSpeed = speedLevels[currentSpeedIndex];
	speedButtonText.setText(gameSpeed + 'x');
	// Update button color based on speed
	if (gameSpeed === 1) {
		speedButtonBg.tint = 0x00AA00; // Green for normal speed
	} else {
		speedButtonBg.tint = 0xFFAA00; // Orange for 2x speed
	}
};
// --- Add semi-transparent black background to bits and security score UI ---
// Bits background
var goldBg = new Container();
var goldBgRect = goldBg.attachAsset('notification', {
	anchorX: 0.5,
	anchorY: 0.5
});
goldBgRect.width = 320;
goldBgRect.height = 90;
goldBgRect.tint = 0x000000;
goldBgRect.alpha = 0.5;
goldBg.addChild(goldText);
goldBg.x = -spacing;
goldBg.y = topMargin;
LK.gui.top.addChild(goldBg);
// Security score background
var scoreBg = new Container();
var scoreBgRect = scoreBg.attachAsset('notification', {
	anchorX: 0.5,
	anchorY: 0.5
});
scoreBgRect.width = 340;
scoreBgRect.height = 110;
scoreBgRect.tint = 0x000000;
scoreBgRect.alpha = 0.5;
scoreBg.addChild(scoreContainer);
scoreBg.x = spacing;
scoreBg.y = topMargin;
LK.gui.top.addChild(scoreBg);
LK.gui.bottom.addChild(healthBarContainer);
LK.gui.top.addChild(speedButton);
healthBarContainer.x = 0;
healthBarContainer.y = -300;
speedButton.x = 0;
speedButton.y = topMargin;
function updateUI() {
	goldText.setText(getText('bits') + ': ' + gold);
	// Actualiza el valor de la puntuación de seguridad
	if (scoreContainer && scoreContainer.children && scoreContainer.children.length > 1) {
		scoreContainer.children[1].setText(score.toString());
	}
	// Save security points to storage
	storage.securityPoints = score;
	// Update health bar
	var healthPercent = lives / 20; // Assuming max lives is 20
	healthBarFill.width = 296 * healthPercent;
	// Change color based on health
	if (healthPercent > 0.6) {
		healthBarFill.tint = 0x00FF00; // Green
	} else if (healthPercent > 0.3) {
		healthBarFill.tint = 0xFFFF00; // Yellow
	} else {
		healthBarFill.tint = 0xFF0000; // Red
	}
}
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);
// Add world renderer for background graphics
var worldRenderer = new WorldRenderer();
var backgroundLayer = new Container();
backgroundLayer.addChild(worldRenderer);
var grid = new Grid(24, 29 + 6);
grid.x = 150;
grid.y = 200 - CELL_SIZE * 4;
worldRenderer.x = grid.x;
worldRenderer.y = grid.y;
grid.pathFind();
// Crear una segunda copia del mapa específicamente para visualización
var mapVisualization = new Grid(24, 29 + 6);
mapVisualization.x = 150;
mapVisualization.y = 200 - CELL_SIZE * 4;
// Sincronizar el mapa de visualización con el mapa principal
function syncVisualizationMap() {
	for (var i = 0; i < 24; i++) {
		for (var j = 0; j < 35; j++) {
			if (grid.cells[i] && grid.cells[i][j] && mapVisualization.cells[i] && mapVisualization.cells[i][j]) {
				mapVisualization.cells[i][j].type = grid.cells[i][j].type;
			}
		}
	}
}
// Forzar actualización visual del mapa cada vez que se sincroniza
worldRenderer.updateWorldGraphics(currentWorld, mapVisualization);
// Sincronizar inicialmente
syncVisualizationMap();
// Render initial world graphics
worldRenderer.updateWorldGraphics(currentWorld, mapVisualization);
// Only render debug on game start, not every frame
if (LK.ticks === 0) {
	grid.renderDebug();
}
debugLayer.addChild(grid);
game.addChildAt(backgroundLayer, 0);
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();
	// Sincronizar el mapa de visualización después de las verificaciones
	syncVisualizationMap();
	return blocked;
}
function getTowerCost(towerType) {
	var cost = 5;
	switch (towerType) {
		case 'gabumon':
			//{ju} // Rapid fire Digimon
			cost = 15;
			break;
		case 'tentomon':
			//{jw} // Long range Digimon
			cost = 25;
			break;
		case 'palmon':
			//{jy} // Area damage Digimon
			cost = 35;
			break;
		case 'gomamon':
			//{jA} // Slowing Digimon
			cost = 45;
			break;
		case 'patamon':
			//{jC} // Poison/status Digimon
			cost = 55;
			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);
	if (gold >= towerCost) {
		var tower = new Tower(towerType || 'default');
		tower.placeOnGrid(gridX, gridY);
		towerLayer.addChild(tower);
		towers.push(tower);
		setGold(gold - towerCost);
		// Play tower placement sound
		LK.getSound('towerPlace').play();
		grid.pathFind();
		grid.renderDebug();
		// Sincronizar el mapa de visualización después de colocar torre
		syncVisualizationMap();
		worldRenderer.updateWorldGraphics(currentWorld, mapVisualization);
		return true;
	} else {
		var notification = game.addChild(new Notification("Not enough bits!"));
		notification.x = 2048 / 2;
		notification.y = grid.height - 50;
		// Play purchase fail sound
		LK.getSound('purchaseFail').play();
		return false;
	}
}
game.down = function (x, y, obj) {
	var upgradeMenuVisible = game.children.some(function (child) {
		return child instanceof UpgradeMenu;
	});
	var shopVisible = digimonShop && digimonShop.visible;
	var summonMenuVisible = digimonSummonMenu && digimonSummonMenu.visible;
	if (upgradeMenuVisible || shopVisible || summonMenuVisible) {
		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) {
	var shopVisible = digimonShop && digimonShop.visible;
	var summonMenuVisible = digimonSummonMenu && digimonSummonMenu.visible;
	if (shopVisible || summonMenuVisible) {
		return;
	}
	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 shopVisible = digimonShop && digimonShop.visible;
	var summonMenuVisible = digimonSummonMenu && digimonSummonMenu.visible;
	if (shopVisible || summonMenuVisible) {
		// Still allow dragging to be reset even when menus are open
		if (isDragging) {
			isDragging = false;
			towerPreview.visible = false;
		}
		return;
	}
	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.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();
			}
		}
	}
};
// Simple wave counter instead of full wave indicator
var waveCounterText = new Text2('Wave: 0/9', {
	size: 60,
	fill: 0xFFFFFF,
	weight: 800
});
waveCounterText.anchor.set(0.5, 0.5);
waveCounterText.x = 2048 / 2;
waveCounterText.y = 2732 - 80;
game.addChild(waveCounterText);
// Create a simple wave counter object to replace waveIndicator
var waveIndicator = {
	gameStarted: false,
	getWaveType: function getWaveType(waveNumber) {
		if (waveNumber < 1 || waveNumber > 10) return "normal";
		// Simple wave type logic for 10 waves
		var waveTypes = ['normal', 'fast', 'immune', 'flying', 'swarm', 'normal', 'fast', 'immune', 'flying', 'boss'];
		return waveTypes[waveNumber - 1];
	},
	getEnemyCount: function getEnemyCount(waveNumber) {
		if (waveNumber < 1 || waveNumber > 10) return 10;
		// Wave 10 is boss with 1 giant enemy, others have 10-30 enemies
		if (waveNumber === 10) return 1;
		if (waveNumber === 5) return 30; // Swarm wave
		return 10;
	},
	getWaveTypeName: function getWaveTypeName(waveNumber) {
		var type = this.getWaveType(waveNumber);
		var typeName = type.charAt(0).toUpperCase() + type.slice(1);
		if (waveNumber === 10) typeName = "BOSS";
		return typeName;
	}
};
// Function to update wave counter display
function updateWaveCounter() {
	var currentWorldWave;
	if (currentWave === 0) {
		currentWorldWave = 0;
	} else {
		// Calculate the wave within the current world (1-10)
		currentWorldWave = (currentWave - 1) % 10 + 1;
	}
	waveCounterText.setText(getText('wave') + ': ' + currentWorldWave + '/10');
}
var nextWaveButtonContainer = new Container();
// --- Next Wave Countdown and Early Reward System ---
var nextWaveButton = new NextWaveButton();
nextWaveButton.x = 2048 - 200;
nextWaveButton.y = 2732 - 100 + 20;
nextWaveButtonContainer.addChild(nextWaveButton);
game.addChild(nextWaveButtonContainer);
// --- 20s Countdown and Early Reward System ---
var nextWaveCountdownActive = false;
var nextWaveCountdownTimer = 0;
var nextWaveCountdownMax = 1200; // 20 seconds at 60 FPS
var nextWaveCountdownText = null;
var nextWaveEarlyReward = 0; // Bits to reward for early start (calculated dynamically)
// Create countdown text (hidden by default)
nextWaveCountdownText = new Text2('', {
	size: 48,
	fill: 0x00FFD0,
	stroke: 0xFFCCCC,
	strokeThickness: 4,
	weight: 800
});
nextWaveCountdownText.anchor.set(0, 0.5);
// Position countdown text to the left of the Next Wave button, vertically centered
nextWaveCountdownText.x = -nextWaveButton.width / 2 - 120;
nextWaveCountdownText.y = 0;
nextWaveCountdownText.visible = false;
nextWaveButtonContainer.addChild(nextWaveCountdownText);
// Helper to start the countdown
function startNextWaveCountdown() {
	nextWaveCountdownActive = true;
	nextWaveCountdownTimer = nextWaveCountdownMax;
	nextWaveCountdownText.visible = true;
	nextWaveCountdownText.setText('20');
	nextWaveEarlyReward = 0;
}
// Helper to stop the countdown
function stopNextWaveCountdown() {
	nextWaveCountdownActive = false;
	nextWaveCountdownTimer = 0;
	nextWaveCountdownText.visible = false;
}
// Patch NextWaveButton.down to handle countdown and early reward
var originalNextWaveButtonDown = nextWaveButton.down;
nextWaveButton.down = function () {
	if (!nextWaveButton.enabled) return;
	// If countdown is active, reward bits for early start
	if (nextWaveCountdownActive && nextWaveCountdownTimer > 0) {
		// Reward is proportional to time left: 1 bit per second left (minimum 1)
		var secondsLeft = Math.ceil(nextWaveCountdownTimer / 60);
		var reward = Math.max(1, secondsLeft);
		setGold(gold + reward);
		var rewardNote = game.addChild(new Notification("¡Oleada iniciada antes! +" + reward + " bits"));
		rewardNote.x = 2048 / 2;
		rewardNote.y = grid.height - 100;
		stopNextWaveCountdown();
	}
	originalNextWaveButtonDown.apply(this, arguments);
};
// Patch game.update to handle countdown logic
var originalGameUpdate = game.update;
game.update = function () {
	// Show countdown only when button is visible and enabled, and not in progress
	if (nextWaveButton.visible && nextWaveButton.enabled && !waveInProgress && enemies.length === 0 && !nextWaveCountdownActive) {
		startNextWaveCountdown();
	}
	// Update countdown if active
	if (nextWaveCountdownActive) {
		if (nextWaveCountdownTimer > 0) {
			nextWaveCountdownTimer--;
			var secondsLeft = Math.ceil(nextWaveCountdownTimer / 60);
			nextWaveCountdownText.setText(secondsLeft.toString());
			// If timer runs out, auto-start next wave (no reward)
			if (nextWaveCountdownTimer === 0) {
				stopNextWaveCountdown();
				if (nextWaveButton.enabled) {
					originalNextWaveButtonDown.apply(nextWaveButton, []);
				}
			}
		}
	} else {
		nextWaveCountdownText.visible = false;
	}
	// Call original update
	originalGameUpdate.apply(this, arguments);
};
// Add shop button
var shopButton = new Container();
var shopButtonBg = shopButton.attachAsset('notification', {
	anchorX: 0.5,
	anchorY: 0.5
});
shopButtonBg.width = 300;
shopButtonBg.height = 100;
shopButtonBg.tint = 0x4444FF;
var shopButtonText = new Text2('Shop', {
	size: 50,
	fill: 0xFFFFFF,
	weight: 800
});
shopButtonText.anchor.set(0.5, 0.5);
shopButton.addChild(shopButtonText);
shopButton.x = 200;
shopButton.y = 2732 - 100 + 20;
game.addChild(shopButton);
var digimonShop = new DigimonShop();
digimonShop.x = 2048 / 2;
game.addChild(digimonShop);
shopButton.down = function () {
	digimonShop.show();
};
// --- Add C, B, and A buttons for Digimon summon filtering ---
var summonButtonSpacing = 120;
var summonButtonStartX = shopButton.x + 220; // Place to the right of shop button
function createSummonLevelButton(label, color, filterLevel, offset) {
	var btn = new Container();
	var btnBg = btn.attachAsset('notification', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	btnBg.width = 100;
	btnBg.height = 100;
	btnBg.tint = color;
	var btnText = new Text2(label, {
		size: 60,
		fill: 0xFFFFFF,
		weight: 800
	});
	btnText.anchor.set(0.5, 0.5);
	btn.addChild(btnText);
	btn.x = summonButtonStartX + offset * summonButtonSpacing;
	btn.y = shopButton.y;
	btn.down = function () {
		// Show summon menu filtered by evolution level
		if (digimonSummonMenu && digimonSummonMenu.show) {
			digimonSummonMenu.show(filterLevel);
		}
	};
	return btn;
}
// C = Champion, B = Ultimate, A = Mega
var buttonC = createSummonLevelButton('C', 0x32CD32, 'champion', 0);
var buttonB = createSummonLevelButton('B', 0xFFD700, 'ultimate', 1);
var buttonA = createSummonLevelButton('A', 0xFF4500, 'mega', 2);
game.addChild(buttonC);
game.addChild(buttonB);
game.addChild(buttonA);
var towerTypes = ['agumon', 'gabumon', 'tentomon', 'palmon', 'gomamon', 'patamon'];
var sourceTowers = [];
var towerSpacing = 320; // 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 summon arrow button at the end of source towers
var summonArrowButton = new Container();
var arrowBg = summonArrowButton.attachAsset('notification', {
	anchorX: 0.5,
	anchorY: 0.5
});
arrowBg.width = 200;
arrowBg.height = 200;
arrowBg.tint = 0xFF6600;
var arrowGraphics = summonArrowButton.attachAsset('arrow', {
	anchorX: 0.5,
	anchorY: 0.5
});
arrowGraphics.width = 80;
arrowGraphics.height = 80;
arrowGraphics.tint = 0xFFFFFF;
arrowGraphics.rotation = Math.PI / 2; // Point upward
var summonText = new Text2('SUMMON', {
	size: 35,
	fill: 0xFFFFFF,
	weight: 800
});
summonText.anchor.set(0.5, 0.5);
summonText.y = 50;
summonArrowButton.addChild(summonText);
var summonSubText = new Text2('(No Mic)', {
	size: 25,
	fill: 0xCCCCCC,
	weight: 600
});
summonSubText.anchor.set(0.5, 0.5);
summonSubText.y = 75;
summonArrowButton.addChild(summonSubText);
summonArrowButton.x = startX + towerTypes.length * towerSpacing;
summonArrowButton.y = towerY;
towerLayer.addChild(summonArrowButton);
// Create summon menu
var digimonSummonMenu = new DigimonSummonMenu();
digimonSummonMenu.x = 2048 / 2;
game.addChild(digimonSummonMenu);
summonArrowButton.down = function () {
	digimonSummonMenu.show();
};
sourceTower = null;
enemiesToSpawn = 10;
game.update = function () {
	// Update background color based on current world
	var worldAssets = worldRenderer.getWorldAssets(currentWorld);
	if (worldAssets) {
		game.setBackgroundColor(worldAssets.ambient);
	}
	// Apply speed multiplier to frame-dependent updates
	var effectiveSpeed = gameSpeed;
	// Note: Wave progression is now manual only via NextWaveButton, no automatic timer
	if (waveInProgress) {
		if (!waveSpawned) {
			waveSpawned = true;
			enemiesKilledInWave = 0; // Reset kill counter for new wave
			// Calculate correct world and level for this wave
			var levelsPerWorld = 10;
			var newWorld = Math.ceil(currentWave / levelsPerWorld);
			var newLevel = (currentWave - 1) % levelsPerWorld + 1;
			var previousWorld = currentWorld;
			currentWorld = newWorld;
			currentLevel = newLevel;
			if (currentWorld > 6) currentWorld = 6;
			// Regenerate maze and world graphics if world changed or first wave
			if (currentWorld !== previousWorld || currentWave === 1) {
				grid.generateMazeForWorld(currentWorld);
				mapVisualization.generateMazeForWorld(currentWorld); // Regenerar también el mapa de visualización
				grid.pathFind();
				grid.renderDebug();
				// Sincronizar mapas después de regenerar
				syncVisualizationMap();
				// Update world graphics usando el mapa de visualización
				worldRenderer.updateWorldGraphics(currentWorld, mapVisualization);
				// Force pathId update for all allies
				for (var i = 0; i < alliedUnits.length; i++) {
					alliedUnits[i].currentTarget = null;
				}
				// Change music when entering new world
				playWorldMusic(currentWorld);
				// World-specific notification messages
				var worldNames = ["", "Forest", "Desert", "Glacier", "Village", "Inferno", "Tech Lab"];
				var worldNotification = game.addChild(new Notification("Welcome to Digital World " + currentWorld + ": " + worldNames[currentWorld] + "!"));
				worldNotification.x = 2048 / 2;
				worldNotification.y = grid.height - 100;
			}
			// Get wave type and enemy count from the wave indicator (based on current world/level)
			var worldWave = currentLevel;
			var waveType = waveIndicator.getWaveType(worldWave);
			var enemyCount = waveIndicator.getEnemyCount(worldWave);
			// Update wave counter display
			updateWaveCounter();
			// Check if this is a boss wave (wave 10 of each world)
			var isBossWave = worldWave === 10;
			if (isBossWave) {
				// Boss waves have just 1 giant enemy
				enemyCount = 1;
				// Get boss name based on current world
				var bossNames = ["", "SNORLAX", "RHYDON", "ARTICUNO", "MACHAMP", "GROUDON", "MEWTWO"];
				var bossName = bossNames[currentWorld] || "BOSS";
				// Check if this is the final boss (Mewtwo in world 6)
				var isFinalBoss = currentWorld === 6;
				// Play boss music
				playWorldMusic(currentWorld, true, isFinalBoss);
				// Show boss announcement with specific name
				var notification = game.addChild(new Notification("⚠️ " + bossName + " APPEARS! ⚠️"));
				notification.x = 2048 / 2;
				notification.y = grid.height - 200;
			}
			// Spawn the appropriate number of enemies
			for (var i = 0; i < enemyCount; i++) {
				var enemy = new Enemy(waveType);
				// Make wave 10 boss giant with much more health
				if (isBossWave) {
					enemy.isBoss = true;
					enemy.maxHealth *= 50; // 50x more health for boss
					enemy.health = enemy.maxHealth;
					// Make boss visually larger
					if (enemy.children[0]) {
						enemy.children[0].scaleX = 3.0;
						enemy.children[0].scaleY = 3.0;
					}
					// Make boss slower but more intimidating
					enemy.speed *= 0.5;
				}
				// 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;
				// 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;
		}
	}
	// Apply speed multiplier to enemy updates
	for (var a = enemies.length - 1; a >= 0; a--) {
		var enemy = enemies[a];
		// Update enemy with speed multiplier applied
		if (enemy.update) {
			for (var speedTick = 0; speedTick < effectiveSpeed; speedTick++) {
				enemy.update();
			}
		}
		if (enemy.health <= 0) {
			// Play enemy death sound
			if (enemy.isBoss) {
				LK.getSound('bossDeath').play();
			} else {
				LK.getSound('enemyDestroyed').play();
			}
			for (var i = 0; i < enemy.bulletsTargetingThis.length; i++) {
				var bullet = enemy.bulletsTargetingThis[i];
				bullet.targetEnemy = null;
			}
			// Track enemy kills for wave progression
			if (enemy.waveNumber === currentWave) {
				enemiesKilledInWave++;
				// Give bonus gold every 5 enemies killed
				if (enemiesKilledInWave % 5 === 0) {
					var bonusGold = enemy.isBoss ? 15 : 10;
					var bonusIndicator = new GoldIndicator(bonusGold, enemy.x, enemy.y - 50);
					game.addChild(bonusIndicator);
					setGold(gold + bonusGold);
					var notification = game.addChild(new Notification("Kill streak bonus! +" + bonusGold + " bits!"));
					notification.x = 2048 / 2;
					notification.y = grid.height - 200;
				}
			}
			// 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);
			setGold(gold + goldEarned);
			// Give more score for defeating a boss
			var scoreValue = enemy.isBoss ? 100 : 5;
			score += scoreValue;
			// Save security points to storage
			storage.securityPoints = score;
			// Add a notification for boss defeat
			if (enemy.isBoss) {
				// Get boss name based on world
				var bossNames = ["", "Snorlax", "Rhydon", "Articuno", "Machamp", "Groudon", "Mewtwo"];
				var bossName = bossNames[enemy.worldNumber] || "Boss";
				var notification = game.addChild(new Notification(bossName + " defeated! +" + goldEarned + " bits!"));
				notification.x = 2048 / 2;
				notification.y = grid.height - 150;
				// Return to normal world music after boss defeat
				LK.setTimeout(function () {
					LK.stopMusic();
					LK.setTimeout(function () {
						playWorldMusic(enemy.worldNumber, false, false);
					}, 500); // Add delay to ensure clean music transition
				}, 2000); // Wait 2 seconds before switching back to normal music
			}
			updateUI();
			// 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);
			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);
			// Calculate damage based on enemy world and type
			var damageDealt = 1; // Base damage
			var worldDamageMultiplier = 1;
			// Scale damage based on world
			switch (enemy.worldNumber) {
				case 1:
					worldDamageMultiplier = 1;
					break;
				case 2:
					worldDamageMultiplier = 1.2;
					break;
				case 3:
					worldDamageMultiplier = 1.4;
					break;
				case 4:
					worldDamageMultiplier = 1.6;
					break;
				case 5:
					worldDamageMultiplier = 1.8;
					break;
				case 6:
					worldDamageMultiplier = 2.0;
					break;
			}
			// Scale damage based on enemy type
			switch (enemy.type) {
				case 'normal':
					damageDealt = 1;
					break;
				case 'fast':
					damageDealt = 1;
					break;
				// Fast but same damage
				case 'immune':
					damageDealt = 2;
					break;
				// Tanky and hits hard
				case 'flying':
					damageDealt = 1.5;
					break;
				// Aerial advantage
				case 'swarm':
					damageDealt = 1;
					break;
				// Weak individual damage
			}
			// Boss enemies deal significantly more damage
			if (enemy.isBoss) {
				damageDealt *= 5;
			}
			// Apply world multiplier and round
			damageDealt = Math.ceil(damageDealt * worldDamageMultiplier);
			lives = Math.max(0, lives - damageDealt);
			// Play system damage sound
			LK.getSound('systemDamage').play();
			// Check for critical health warning
			if (lives <= 5 && lives > 0) {
				LK.getSound('criticalHealth').play();
			}
			updateUI();
			// Show damage indicator
			var damageIndicator = new Notification("-" + damageDealt + " System Health!");
			damageIndicator.x = 2048 / 2;
			damageIndicator.y = 2732 - 200;
			game.addChild(damageIndicator);
			if (lives <= 0) {
				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();
	}
	// Check for level completion and world progression
	var levelsPerWorld = 10;
	var completedWaves = storage.completedWaves || 0;
	if (currentWave > completedWaves) {
		storage.completedWaves = currentWave;
		// Calculate current world and level
		var currentWorldNum = Math.ceil(currentWave / levelsPerWorld);
		var currentLevelNum = (currentWave - 1) % levelsPerWorld + 1;
		// Update world levels in storage
		var worldLevels = storage.worldLevels || {
			1: 1,
			2: 1,
			3: 1,
			4: 1,
			5: 1,
			6: 1
		};
		if (currentWorldNum >= 1 && currentWorldNum <= 6) {
			worldLevels[currentWorldNum] = Math.max(worldLevels[currentWorldNum], currentLevelNum);
			storage.worldLevels = worldLevels;
		}
		// Check if we completed a world (every 10 waves)
		var worldsCompleted = Math.floor(currentWave / levelsPerWorld);
		var unlockedWorlds = storage.unlockedWorlds || 1;
		if (worldsCompleted + 1 > unlockedWorlds && worldsCompleted + 1 <= 6) {
			storage.unlockedWorlds = worldsCompleted + 1;
			var notification = game.addChild(new Notification("New world unlocked!"));
			notification.x = 2048 / 2;
			notification.y = grid.height - 100;
		}
	}
	// Spawn coins randomly on the field
	coinSpawnTimer++;
	if (coinSpawnTimer > 10800 && coins.length < 3) {
		// Spawn every 3 minutes (180 seconds * 60 FPS = 10800 ticks), max 3 coins
		coinSpawnTimer = 0;
		// Find a random walkable position
		var attempts = 0;
		var spawnX, spawnY;
		do {
			var gridX = Math.floor(Math.random() * 24);
			var gridY = Math.floor(Math.random() * 35);
			var cell = grid.getCell(gridX, gridY);
			if (cell && cell.type === 0) {
				// Spawn on path tiles
				spawnX = grid.x + gridX * CELL_SIZE + CELL_SIZE / 2;
				spawnY = grid.y + gridY * CELL_SIZE + CELL_SIZE / 2;
				break;
			}
			attempts++;
		} while (attempts < 50);
		if (attempts < 50) {
			var coinValue = 5; // Fixed 5 bits for security store currency
			var coin = new Coin(spawnX, spawnY, coinValue);
			game.addChild(coin);
			coins.push(coin);
		}
	}
	// Update existing coins
	for (var i = coins.length - 1; i >= 0; i--) {
		var coin = coins[i];
		if (coin.update) coin.update();
		if (coin.collected) {
			coins.splice(i, 1);
		}
	}
	// Update allied units with speed multiplier
	for (var i = alliedUnits.length - 1; i >= 0; i--) {
		var unit = alliedUnits[i];
		if (unit.update) {
			for (var speedTick = 0; speedTick < effectiveSpeed; speedTick++) {
				unit.update();
			}
		}
		// Remove dead or destroyed units
		if (unit.isDead || !unit.parent) {
			alliedUnits.splice(i, 1);
		}
	}
	// Voice summoning system
	handleVoiceSummoning();
	// Check for completion of current world (10 waves) or all worlds
	var worldWave = (currentWave - 1) % 10 + 1;
	if (worldWave >= 10 && enemies.length === 0 && !waveInProgress) {
		// --- PERFECT WORLD COMPLETION BONUS ---
		// Only award if player has full lives (no life lost in the world)
		// Calculate which world was just completed
		var completedWorld = Math.ceil(currentWave / 10);
		// Defensive: only apply if completedWorld is valid (1-6)
		if (completedWorld >= 1 && completedWorld <= 6) {
			// Check if player has not lost any lives during this world
			// We assume max lives is always 20 at start of world
			// Defensive: Only check for perfect if lives is 20
			if (lives === 20) {
				// Calculate perfect bonus: 300 for world 1, +100 per world
				var perfectBonus = 300 + (completedWorld - 1) * 100;
				score += perfectBonus;
				storage.securityPoints = score;
				updateUI();
				var perfectNote = game.addChild(new Notification(currentLanguage === 'es' ? "¡Mundo completado PERFECTO! +" + perfectBonus + " puntos de seguridad" : "PERFECT World! +" + perfectBonus + " security score"));
				perfectNote.x = 2048 / 2;
				perfectNote.y = grid.height - 250;
			}
		}
		// Play level complete sound
		LK.getSound('levelComplete').play();
		// Stop all music first to prevent mixing
		LK.stopMusic();
		// Clear all timeouts to prevent conflicts
		// (No direct timeout clearing method available, but stopping music helps)
		// Show win screen instead of immediately returning to main menu
		LK.showYouWin(); // This will handle the game state reset and return to main menu properly
	}
};
// Show microphone usage notification
var micNotification = game.addChild(new Notification("Microphone access may be requested for voice summoning features"));
micNotification.x = 2048 / 2;
micNotification.y = 200;
var mainMenu = new MainMenu();
game.addChild(mainMenu);
// Execute the game
console.log("Game initialized and running!");
game.startGame = function () {
	var worldSelectionMenu = new WorldSelectionMenu();
	game.addChild(worldSelectionMenu);
};
game.startWorld = function (worldNumber) {
	currentWorld = worldNumber;
	// Set gold based on world number: 80 for world 1, 100 for world 2, 120 for world 3, etc. (scaled, not additive)
	gold = 60 + worldNumber * 20;
	updateUI();
	grid.generateMazeForWorld(currentWorld);
	mapVisualization.generateMazeForWorld(currentWorld); // Regenerar también el mapa de visualización
	grid.pathFind();
	grid.renderDebug();
	// Sincronizar mapas
	syncVisualizationMap();
	worldRenderer.updateWorldGraphics(currentWorld, mapVisualization);
	// Reset currentTarget for all allies to force path recalculation
	for (var i = 0; i < alliedUnits.length; i++) {
		alliedUnits[i].currentTarget = null;
	}
	// Change to world-specific music
	playWorldMusic(worldNumber);
	// Show story sequence before starting the world
	var storySequence = new StorySequence(worldNumber);
	game.addChild(storySequence);
	storySequence.onComplete = function () {
		waveIndicator.gameStarted = true;
		// Start at the first incomplete wave for this world, or at the first wave if all are complete
		var worldLevels = storage.worldLevels || {
			1: 1,
			2: 1,
			3: 1,
			4: 1,
			5: 1,
			6: 1
		};
		var startLevel = worldLevels[currentWorld] || 1;
		currentWave = (currentWorld - 1) * 10 + startLevel;
		waveTimer = nextWaveTime;
	};
};
game.startWorldLevel = function (worldNumber, levelNumber) {
	currentWorld = worldNumber;
	// Set gold based on world number: 80 for world 1, 100 for world 2, 120 for world 3, etc. (scaled, not additive)
	gold = 60 + worldNumber * 20;
	updateUI();
	// Always use the provided levelNumber when it's given
	if (levelNumber) {
		currentLevel = levelNumber;
	} else {
		var worldLevels = storage.worldLevels || {
			1: 1,
			2: 1,
			3: 1,
			4: 1,
			5: 1,
			6: 1
		};
		currentLevel = worldLevels[worldNumber] || 1;
	}
	// Calculate currentWave based on selected level, not progress
	currentWave = (worldNumber - 1) * 10 + currentLevel;
	grid.generateMazeForWorld(currentWorld);
	mapVisualization.generateMazeForWorld(currentWorld);
	grid.pathFind();
	grid.renderDebug();
	// Sincronizar mapas
	syncVisualizationMap();
	worldRenderer.updateWorldGraphics(currentWorld, mapVisualization);
	// Reset currentTarget for all allies to force path recalculation
	for (var i = 0; i < alliedUnits.length; i++) {
		alliedUnits[i].currentTarget = null;
	}
	// Change to world-specific music
	playWorldMusic(worldNumber);
	// Show story sequence before starting the level
	var storySequence = new StorySequence(worldNumber);
	game.addChild(storySequence);
	storySequence.onComplete = function () {
		waveIndicator.gameStarted = true;
		// Set flag to indicate this is the first wave start for a selected level
		isFirstWaveStart = true;
		// Set waveTimer so the first wave button click starts at the selected level
		waveTimer = nextWaveTime;
	};
};
// Add tutorial state tracking
var tutorialCompleted = false;
var tutorialInProgress = false;
var isFirstWaveStart = false; // Flag to track if this is the first wave start for selected level
// Voice summoning system handler
// This function only uses facekit.volume (microphone input).
// No camera or face detection features are used, so only microphone permission is requested.
function handleVoiceSummoning() {
	// Only process voice commands during active gameplay
	if (!waveIndicator.gameStarted || LK.ticks - lastVoiceDetectionTime < voiceDetectionCooldown) {
		return;
	}
	// Check if facekit is available and for loud voice input (shouting level)
	if (facekit && facekit.volume > 0.7) {
		lastVoiceDetectionTime = LK.ticks;
		// Define Digimon voice commands and their requirements
		var digimonVoiceCommands = {
			// Champion level (requires Digivice C and base tower)
			'greymon': {
				baseTower: 'agumon',
				requiredDigivice: 'digiviceC',
				level: 'champion',
				cost: 150,
				description: 'Champion Fire Dragon'
			},
			'garurumon': {
				baseTower: 'gabumon',
				requiredDigivice: 'digiviceC',
				level: 'champion',
				cost: 180,
				description: 'Champion Ice Wolf'
			},
			'kabuterimon': {
				baseTower: 'tentomon',
				requiredDigivice: 'digiviceC',
				level: 'champion',
				cost: 160,
				description: 'Champion Electric Insect'
			},
			// Ultimate level (requires Digivice B and base tower)
			'metalgreymon': {
				baseTower: 'agumon',
				requiredDigivice: 'digiviceB',
				level: 'ultimate',
				cost: 400,
				description: 'Ultimate Cyborg Dragon'
			},
			'weregarurumon': {
				baseTower: 'gabumon',
				requiredDigivice: 'digiviceB',
				level: 'ultimate',
				cost: 450,
				description: 'Ultimate Beast Warrior'
			},
			'megakabuterimon': {
				baseTower: 'tentomon',
				requiredDigivice: 'digiviceB',
				level: 'ultimate',
				cost: 420,
				description: 'Ultimate Giant Insect'
			},
			// Mega level (requires Digivice A and base tower)
			'wargreymon': {
				baseTower: 'agumon',
				requiredDigivice: 'digiviceA',
				level: 'mega',
				cost: 800,
				description: 'Mega Dragon Warrior'
			}
		};
		// Check each voice command
		for (var digimonName in digimonVoiceCommands) {
			var command = digimonVoiceCommands[digimonName];
			// Check if player has the required Digivice
			if (!storage[command.requiredDigivice]) {
				continue;
			}
			// Count base towers of the required type
			var baseTowerCount = 0;
			for (var i = 0; i < towers.length; i++) {
				if (towers[i].id === command.baseTower) {
					baseTowerCount++;
				}
			}
			// Need at least one base tower to summon evolution
			if (baseTowerCount === 0) {
				continue;
			}
			// Calculate cooldown based on number of base towers
			var cooldownReduction = Math.max(1, baseTowerCount);
			var effectiveCooldown = Math.floor(summonCooldown / cooldownReduction);
			// Check if this Digimon is off cooldown
			var lastSummonTime = voiceSummonCooldown[digimonName] || 0;
			if (LK.ticks - lastSummonTime < effectiveCooldown) {
				continue;
			}
			// Check if player can afford the summon
			if (score < command.cost) {
				continue;
			}
			// Check if there's space for more allied units
			if (alliedUnits.length >= maxAlliedUnits) {
				continue;
			}
			// Voice detection successful - summon the Digimon!
			score -= command.cost;
			updateUI();
			// Create a more powerful allied unit based on evolution level
			var summonedUnit = new DigimonUnit(digimonName, getEvolutionLevel(command.level));
			// Apply evolution bonuses
			switch (command.level) {
				case 'champion':
					summonedUnit.damage *= 2;
					summonedUnit.health *= 2;
					summonedUnit.maxHealth = summonedUnit.health;
					break;
				case 'ultimate':
					summonedUnit.damage *= 3;
					summonedUnit.health *= 3;
					summonedUnit.maxHealth = summonedUnit.health;
					summonedUnit.range *= 1.5;
					break;
				case 'mega':
					summonedUnit.damage *= 5;
					summonedUnit.health *= 5;
					summonedUnit.maxHealth = summonedUnit.health;
					summonedUnit.range *= 2;
					summonedUnit.attackRate = Math.floor(summonedUnit.attackRate * 0.7);
					break;
			}
			// Add to game world
			enemyLayerTop.addChild(summonedUnit);
			alliedUnits.push(summonedUnit);
			// Set cooldown for this specific Digimon
			voiceSummonCooldown[digimonName] = LK.ticks;
			// Show success notification
			var notification = game.addChild(new Notification(digimonName.toUpperCase() + " summoned by voice!"));
			notification.x = 2048 / 2;
			notification.y = grid.height - 50;
			// Show microphone icon animation above the summoned Digimon
			var micIcon = summonedUnit.attachAsset('notification', {
				anchorX: 0.5,
				anchorY: 1.0
			});
			micIcon.width = 60;
			micIcon.height = 60;
			micIcon.x = 0;
			micIcon.y = -summonedUnit.children[0].height / 2 - 10;
			micIcon.tint = 0x00AAFF;
			micIcon.alpha = 0;
			tween(micIcon, {
				alpha: 1,
				y: micIcon.y - 20
			}, {
				duration: 200,
				easing: tween.easeOut,
				onFinish: function onFinish() {
					tween(micIcon, {
						alpha: 0,
						y: micIcon.y - 40
					}, {
						duration: 600,
						delay: 600,
						easing: tween.easeIn,
						onFinish: function onFinish() {
							micIcon.visible = false;
						}
					});
				}
			});
			// Visual and audio feedback
			LK.effects.flashScreen(0x00FF00, 500);
			// Play voice summon and digivolve sounds
			LK.getSound('voiceSummon').play();
			LK.setTimeout(function () {
				LK.getSound('digivolveSound').play();
			}, 200);
			// Only summon one Digimon per voice command
			break;
		}
	}
}
// Helper function to convert evolution level to numeric level
function getEvolutionLevel(levelName) {
	switch (levelName) {
		case 'champion':
			return 3;
		case 'ultimate':
			return 5;
		case 'mega':
			return 6;
		default:
			return 1;
	}
}
game.startTutorial = function () {
	// Clear any existing game state first
	while (enemies.length > 0) {
		var enemy = enemies.pop();
		if (enemy.parent) {
			enemy.parent.removeChild(enemy);
		}
		if (enemy.shadow && enemy.shadow.parent) {
			enemy.shadow.parent.removeChild(enemy.shadow);
		}
	}
	while (towers.length > 0) {
		var tower = towers.pop();
		if (tower.parent) {
			tower.parent.removeChild(tower);
		}
	}
	while (bullets.length > 0) {
		var bullet = bullets.pop();
		if (bullet.parent) {
			bullet.parent.removeChild(bullet);
		}
	}
	// IMPORTANT: Reset tutorial state BEFORE creating the sequence
	tutorialCompleted = false;
	tutorialInProgress = false;
	// Reset game state for tutorial
	currentWorld = 1;
	currentLevel = 1;
	currentWave = 0; // Reset to 0 so tutorial starts at wave 0/9
	waveInProgress = false;
	waveSpawned = false;
	gold = 60 + currentWorld * 20; // Tutorial uses world-based gold: 80, 100, 120, etc. (scaled amount)
	lives = 20;
	score = 0;
	enemiesKilledInWave = 0;
	// Update wave counter for tutorial - explicitly set to show 0/10
	waveCounterText.setText('Wave: 0/10');
	updateUI();
	// Generate tutorial world
	grid.generateMazeForWorld(currentWorld);
	mapVisualization.generateMazeForWorld(currentWorld);
	grid.pathFind();
	grid.renderDebug();
	syncVisualizationMap();
	worldRenderer.updateWorldGraphics(currentWorld, mapVisualization);
	// Reset currentTarget for all allies to force path recalculation
	for (var i = 0; i < alliedUnits.length; i++) {
		alliedUnits[i].currentTarget = null;
	}
	// Start tutorial with forest world music
	playWorldMusic(1);
	// Create tutorial sequence immediately after state reset
	var tutorialSequence = new TutorialSequence();
	game.addChild(tutorialSequence);
	// Set tutorial in progress AFTER creation
	tutorialInProgress = true;
	tutorialSequence.onComplete = function () {
		// Mark tutorial as completed
		tutorialCompleted = true;
		tutorialInProgress = false;
		// After tutorial, ensure game is ready to start from wave 1
		waveIndicator.gameStarted = true;
		// Tutorial complete - show next wave button
		var notification = game.addChild(new Notification("Tutorial complete! Use Next Wave button to start your first wave!"));
		notification.x = 2048 / 2;
		notification.y = grid.height - 150;
	};
};
:quality(85)/https://cdn.frvr.ai/671be23a3de176b9ade21311.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/6869bebf63775205018c17a0.png%3F3) 
 Gabumon viendo a la derecha, perspectiva isométrica. In-Game asset. 2d. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/686a0b5f397a35b4b61a24c8.png%3F3) 
 Gomamon. In-Game asset. 2d. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/686a0c5b397a35b4b61a24d4.png%3F3) 
 Agumon viendo a la derecha, perspectiva isometrica. In-Game asset. 2d. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/686a0f65397a35b4b61a24eb.png%3F3) 
 Tentomon viendo a la derecha, perspectiva isometrica. In-Game asset. 2d. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/686a0ff3397a35b4b61a24f9.png%3F3) 
 Patamon viendo a la derecha, perspectiva isometrica. In-Game asset. 2d. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/686a1072397a35b4b61a2501.png%3F3) 
 Pikachu visto desde arriba. In-Game asset. 2d. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/686a10e1397a35b4b61a250c.png%3F3) 
 Pidgeot visto desde arriba. In-Game asset. 2d. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/686a1140397a35b4b61a2518.png%3F3) 
 Beedrill visto desde arriba. In-Game asset. 2d. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/686a1195397a35b4b61a2525.png%3F3) 
 Gengar visto desde arriba. In-Game asset. 2d. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/686a15ff397a35b4b61a2544.png%3F3) 
 forestScenaryElement. In-Game asset. 2d. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/687ff0d57cc77e9275ea8909.png%3F3) 
 Cell. In-Game asset. 2d. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/687ff4a17cc77e9275ea894b.png%3F3) 
 desertbg. In-Game asset. 2d. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/687ff7c07cc77e9275ea8974.png%3F3) 
 glacierbg block. In-Game asset. 2d. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/6881b67b7d8315438bf4a0f1.png%3F3) 
 Rattata, visto desde arriba, viendo a la derecha. In-Game asset. 2d. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/6881e0c31ec60ae272fb855a.png%3F3) 
 boton con contorno mecanico. In-Game asset. 2d. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/68828defbdec89e52bf0b5d9.png%3F3) 
 Bloque de pasto verde, visto desde arriba. In-Game asset. 2d. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/68829ab6b15a1eeb0c9b5910.png%3F3) 
 Articuno visto desde arriba. In-Game asset. 2d. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/68829d130bebf4363dcfe57e.png%3F3) 
 Bloque de tierra sin césped, visto desde arriba, con efecto repetido para unirse a otros bloques, ocupar el cuadro completo de imagen In-Game asset. 2d. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/68829dfd0bebf4363dcfe593.png%3F3) 
 Snorlax, visto desde arriba. In-Game asset. 2d. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/68829fbd0bebf4363dcfe5aa.png%3F3) 
 Quitale el fondo blanco pero cuidado con las garras
:quality(85)/https://cdn.frvr.ai/6882a0670bebf4363dcfe5ba.png%3F3) 
 Machamp, visto desde arriba, bien detallado. In-Game asset. 2d. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/6882a1670bebf4363dcfe5c8.png%3F3) 
 Groudon, visto desde arriba, detalles altos. In-Game asset. 2d. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/6882a2370bebf4363dcfe5d2.png%3F3) 
 Mewtwo, visto desde arriba, altos detalles. In-Game asset. 2d. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/6882c82e0e90bd13b6f7faa2.png%3F3) 
 Greymon, visto desde arriba, detalles altos sin errores. In-Game asset. 2d. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/6882d7d7c3d1e52d5ff06cfb.png%3F3) 
 Metalgreymon, visto desde arriba, detalles altos, sin errores debe ser identico. In-Game asset. 2d. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/6882d8afc3d1e52d5ff06d08.png%3F3) 
 Wargreymon, visto desde arriba, detalles muy altos, sin errores idéntico. In-Game asset. 2d. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/6882d990c3d1e52d5ff06d1a.png%3F3) 
 Garurumon, visto desde arriba, detalles muy altos, sin errores idéntico. In-Game asset. 2d. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/6882dc2fc3d1e52d5ff06d33.png%3F3) 
 WereGarurumon, visto desde arriba, detalles muy altos, sin errores, original In-Game asset. 2d. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/6882dd91c3d1e52d5ff06d53.png%3F3) 
 Metalgarurumon, visto desde arriba, detalles muy épicos. In-Game asset. 2d. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/6882de83c3d1e52d5ff06d62.png%3F3) 
 Kabuterimon. visto desde arriba. calidad y detalles altos. In-Game asset. 2d. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/6882dfc0c3d1e52d5ff06d77.png%3F3) 
 MegaKabuterimon. visto desde arriba. calidad y detalles altos. In-Game asset. 2d. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/6882e07fc3d1e52d5ff06d89.png%3F3) 
 Herculeskabuterimon, visto desde arriba, calidad y detalles altamente épicos. In-Game asset. 2d. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/6882e2eac3d1e52d5ff06da9.png%3F3) 
 Rosemon, vista desde arriba, calidad y detalles altos, busto grande sexy, cuerpo completo. In-Game asset. 2d. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/6882e42cc3d1e52d5ff06dbe.png%3F3) 
 Togemon, visto desde arriba, calidad y detalles altamente épicos. In-Game asset. 2d. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/6882e4cac3d1e52d5ff06dc8.png%3F3) 
 Lillymon, visto desde arriba, calidad y detalles altamente épicos. In-Game asset. 2d. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/6882e597c3d1e52d5ff06dd5.png%3F3) 
 Ikkakumon, visto desde arriba, calidad y detalles altamente épicos. In-Game asset. 2d. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/6882e63dc3d1e52d5ff06de2.png%3F3) 
 Angemon, visto desde arriba, calidad y detalles altamente épicos.. In-Game asset. 2d. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/6882e6d0c3d1e52d5ff06df9.png%3F3) 
 Zudomon, visto desde arriba, calidad y detalles altamente épicos, fidelidad al concepto original In-Game asset. 2d. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/688780178cf41ec47abf8a6a.png%3F3) 
 Sin fondo
:quality(85)/https://cdn.frvr.ai/6887aa6f5e177a10309e3a36.png%3F3) 
 hada digital. In-Game asset. 2d. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/6887aafe5e177a10309e3a42.png%3F3) 
 MagnaAngemon, visto desde arriba, calidad y detalles altamente épicos.. In-Game asset. 2d. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/6888072b8900e8743999f957.png%3F3) 
 Seraphimon, visto desde arriba, calidad y detalles altamente épicos, Digimon. In-Game asset. 2d. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/68880b0d8900e8743999f98e.png%3F3) 
 Vikemon, visto desde arriba, calidad y detalles altamente épicos, Digimon. In-Game asset. 2d. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/688840f56bddd9b8de7871c4.png%3F3) 
 Hielo oscuro, ocupar toda la imagen, visto desde arriba. In-Game asset. 2d. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/68884280b1bd246557ba490a.png%3F3) 
 Hielo, ocupar toda la imagen, visto desde arriba. In-Game asset. 2d. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/6888432ab1bd246557ba4917.png%3F3) 
 Tierras con piedras, ocupar toda la imagen, visto desde arriba. In-Game asset. 2d. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/688843e7b1bd246557ba491f.png%3F3) 
 Aldeas, ocupar toda la imagen, visto desde arriba. In-Game asset. 2d. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/688844f4b1bd246557ba492a.png%3F3) 
 Alfombra roja, ocupar toda la imagen, visto desde arriba. In-Game asset. 2d. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/688845dcb1bd246557ba493d.png%3F3) 
 Metal con pequeños componentes de electrónica, ocupar toda la imagen, visto desde arriba. In-Game asset. 2d. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/68884690b1bd246557ba4947.png%3F3) 
 Magma oscura, ocupar toda la imagen, visto desde arriba. In-Game asset. 2d. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/68884772b1bd246557ba4956.png%3F3) 
 Rocas volcanicas, ocupar toda la imagen, visto desde arriba. In-Game asset. 2d. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/6888488eb1bd246557ba495e.png%3F3) 
 Arena del desierto, ocupar toda la imagen, visto desde arriba. In-Game asset. 2d. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/689b4a7ae52906fd6220b8db.png%3F3) 
 UlforceVeedramon, visto desde arriba. In-Game asset. 2d. High contrast. No shadows
mainMenuMusic
Music
forestMusic
Music
desertMusic
Music
glacierMusic
Music
villageMusic
Music
techLabMusic
Music
infernoMusic
Music
bossMusic
Music
finalBossMusic
Music
digimonAttack
Sound effect
enemyHit
Sound effect
enemyDestroyed
Sound effect
towerPlace
Sound effect
towerUpgrade
Sound effect
digivolveSound
Sound effect
splashAttack
Sound effect
poisonEffect
Sound effect
freezeEffect
Sound effect
burnEffect
Sound effect
buttonClick
Sound effect
coinCollect
Sound effect
bossWarning
Sound effect
criticalHealth
Sound effect
levelComplete
Sound effect
purchaseFail
Sound effect
purchaseSuccess
Sound effect
systemDamage
Sound effect
voiceSummon
Sound effect
waveStart
Sound effect
bossDeath
Sound effect