User prompt
No se nota ningun cambio incluso al ya estar introducidos los assets
User prompt
Lo que se puede hacer es crear una duplica de el mapa donde ahí si vea los assets
User prompt
Puedes hacer todo eso porfavor
User prompt
Remplaza todo la forma en como funciona el mapa por una que sea compatible con nuestros assets
User prompt
Muy bien Ava ahora vamos con el conflicto principal de todo este trayecto por que no se aplica el tile set apropiado con los assets que ya creamos
User prompt
A lo mejor queda mejor si le dejamos solo 2 curvas
User prompt
Oye ava sabes por que no funciona
User prompt
Sigue igual
User prompt
Puedes porfis
User prompt
Si por favor
User prompt
Muy bien así lo quiero pero necesito espacio para poner a los digimon por los caminos que pasan por el medio en donde solo hay un bloque de espacio para los Digimon y no es suficiente para ponerlo como torreta ahí
User prompt
Esta peor aun, puedes dejar el mapa como estaba antes y dejar espacio suciente para los digimon
User prompt
Hiciste lo contrario eliminaste el poco espacio que habia para los digimon
User prompt
Podemos añadir un botón arriba para acelerar el tiempo y abrir algo de espacio por los caminos del medio para colocar torretas
User prompt
Podemos añadir los objetos de tienda permanentes es decir seran 3 por los momentos Digivice C, B y A estos desbloquearan las evoluciones de menor a mayor etapa el boton de digievolucionar solo se muestra cuando una torreta ya tiene un nivel mejorado ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
Muy bien así es como queria el mapa pero puedes abrir un poquito de espacio por el medio para que quepan las torretas
User prompt
Si eso me sirve
User prompt
Esta muy desorneda lo quiero con esquinas cudradas
User prompt
No esta nada mal pero los enemigos tienen un camino recto para pasar yo lo queria con varias curvas y vueltas hasta llegar de esa manera tienen más oportunidades las torretas
User prompt
Los fallos que tenemos son que el laberinto nunca llega al nucleo Digimon, no hay suficiente espacio para colocar a los Digimon y además lanza un mensaje de que estan bloqueando el camino cuando no es cierto
User prompt
El camino debe tener 3 bloques de grosor y por ultimo sigo sin entender el mapa también las oleadas avanzan solas y no deberia ser así
User prompt
Mejor dale forma en serpiente en cuadrado con forma de laberinto clasico y remplaza todo el mapa que comience a usar los assets, también has que los Diigmon se puedan colocar en cualquier parte menos en el camino de los enemigos
User prompt
Muy bien hagamos cambios en el mapa borra el como funciona todo el mapa excepto el arrastrar la torre y colocarla todos los mapas tendran la misma posición de laberinto en zig zag de 3 bloque como
User prompt
Hagamos más cambios mejoremos los cuadro de dialogos para que se vean mucho mejor y no se salgan de la caja de dialogos también puedes borrar eso de que no permite construir torres de digimon.
User prompt
Muy bien arreglemos varios cosas lo primero es que la salud del sistema estara abajo, lo segundo cambia el titulo del juego a Firewall Defensors, todos los mapas estaran con forma de serpiente laberintica.
/**** 
* 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
});
/**** 
* 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);
				// Splash damage to nearby enemies
				var splashRadius = CELL_SIZE * 1.5;
				for (var i = 0; i < enemies.length; i++) {
					var otherEnemy = enemies[i];
					if (otherEnemy !== self.targetEnemy) {
						var splashDx = otherEnemy.x - self.targetEnemy.x;
						var splashDy = otherEnemy.y - self.targetEnemy.y;
						var splashDistance = Math.sqrt(splashDx * splashDx + splashDy * splashDy);
						if (splashDistance <= splashRadius) {
							// Apply splash damage (50% of original damage)
							otherEnemy.health -= self.damage * 0.5;
							if (otherEnemy.health <= 0) {
								otherEnemy.health = 0;
							} else {
								otherEnemy.healthBar.width = otherEnemy.health / otherEnemy.maxHealth * 70;
							}
						}
					}
				}
			} else if (self.type === 'slow') {
				// Prevent slow effect on immune enemies
				if (!self.targetEnemy.isImmune) {
					// Create visual slow effect
					var slowEffect = new EffectIndicator(self.targetEnemy.x, self.targetEnemy.y, 'slow');
					game.addChild(slowEffect);
					// Apply slow effect
					// Make slow percentage scale with tower level (default 50%, up to 80% at max level)
					var slowPct = 0.5;
					if (self.sourceTowerLevel !== undefined) {
						// Scale: 50% at level 1, 60% at 2, 65% at 3, 70% at 4, 75% at 5, 80% at 6
						var slowLevels = [0.5, 0.6, 0.65, 0.7, 0.75, 0.8];
						var idx = Math.max(0, Math.min(5, self.sourceTowerLevel - 1));
						slowPct = slowLevels[idx];
					}
					if (!self.targetEnemy.slowed) {
						self.targetEnemy.originalSpeed = self.targetEnemy.speed;
						self.targetEnemy.speed *= 1 - slowPct; // Slow by X%
						self.targetEnemy.slowed = true;
						self.targetEnemy.slowDuration = 180; // 3 seconds at 60 FPS
					} else {
						self.targetEnemy.slowDuration = 180; // Reset duration
					}
				}
			} else if (self.type === 'poison') {
				// Prevent poison effect on immune enemies
				if (!self.targetEnemy.isImmune) {
					// Create visual poison effect
					var poisonEffect = new EffectIndicator(self.targetEnemy.x, self.targetEnemy.y, 'poison');
					game.addChild(poisonEffect);
					// Apply poison effect
					self.targetEnemy.poisoned = true;
					self.targetEnemy.poisonDamage = self.damage * 0.2; // 20% of original damage per tick
					self.targetEnemy.poisonDuration = 300; // 5 seconds at 60 FPS
				}
			} else if (self.type === 'sniper') {
				// Create visual critical hit effect for sniper
				var sniperEffect = new EffectIndicator(self.targetEnemy.x, self.targetEnemy.y, 'sniper');
				game.addChild(sniperEffect);
			}
			self.destroy();
		} else {
			var angle = Math.atan2(dy, dx);
			self.x += Math.cos(angle) * self.speed;
			self.y += Math.sin(angle) * self.speed;
		}
	};
	return self;
});
var 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) {
		var characterImage = self.attachAsset(imagePath, {
			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: 32,
		fill: 0x000000,
		weight: 600
	});
	storyText.anchor.set(0.5, 0.5);
	storyText.y = 170;
	storyText.wordWrap = true;
	storyText.wordWrapWidth = 620;
	storyText.maxWidth = 620;
	self.addChild(storyText);
	// 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) {
		switch (data.type) {
			case 0:
			case 2:
				{
					if (data.pathId != pathId) {
						self.removeArrows();
						// Removed number label setText to improve performance
						cellGraphics.tint = 0x880000;
						return;
					}
					// Keep visual tinting but remove number label updates
					var tint = Math.floor(data.score / maxScore * 0x88);
					var towerInRangeHighlight = false;
					if (selectedTower && data.towersInRange && data.towersInRange.indexOf(selectedTower) !== -1) {
						towerInRangeHighlight = true;
						cellGraphics.tint = 0x0088ff;
					} else {
						cellGraphics.tint = 0x88 - tint << 8 | tint;
					}
					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;
					}
					break;
				}
			case 1:
				{
					self.removeArrows();
					cellGraphics.tint = 0xaaaaaa;
					// Removed number label visibility setting for performance
					break;
				}
			case 3:
				{
					self.removeArrows();
					cellGraphics.tint = 0x008800;
					// Removed number label visibility setting for performance
					break;
				}
		}
		// Removed numberLabel.setText to improve performance - text rendering was causing lag
	};
});
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 + ' bits', {
			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 = gold >= itemData.cost;
				if (owned) {
					background.tint = 0x00AA00;
					costText.setText('OWNED');
					button.alpha = 0.7;
				} else if (canAfford) {
					background.tint = 0x4444FF;
					costText.setText(itemData.cost + ' bits');
					button.alpha = 1.0;
				} else {
					background.tint = 0x666666;
					costText.setText(itemData.cost + ' bits');
					button.alpha = 0.5;
				}
			};
			button.down = function () {
				var owned = storage[itemData.id] || false;
				var canAfford = gold >= 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) {
					setGold(gold - itemData.cost);
					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 bits 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;
});
// 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;
	}
	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;
	// Check if this is a boss wave
	// Check if this is a boss wave
	// Apply different stats based on enemy type
	switch (self.type) {
		case 'fast':
			self.speed *= 2; // Twice as fast
			self.maxHealth = 100;
			break;
		case 'immune':
			self.isImmune = true;
			self.maxHealth = 80;
			break;
		case 'flying':
			self.isFlying = true;
			self.maxHealth = 80;
			break;
		case 'swarm':
			self.maxHealth = 50; // Weaker enemies
			break;
		case 'normal':
		default:
			// Normal enemy uses default values
			break;
	}
	if (currentWave % 10 === 0 && currentWave > 0 && type !== 'swarm') {
		self.isBoss = true;
		// Boss enemies have 20x health and are larger
		self.maxHealth *= 20;
		// Slower speed for bosses
		self.speed = self.speed * 0.7;
	}
	self.health = self.maxHealth;
	// Get appropriate asset for this virus type
	var assetId = 'virus';
	if (self.type !== 'normal') {
		assetId = 'virus_' + self.type;
	}
	var enemyGraphics = self.attachAsset(assetId, {
		anchorX: 0.5,
		anchorY: 0.5
	});
	// Scale up boss enemies
	if (self.isBoss) {
		enemyGraphics.scaleX = 1.8;
		enemyGraphics.scaleY = 1.8;
	}
	// Fall back to regular enemy asset if specific type asset not found
	// Apply tint to differentiate enemy types
	/*switch (self.type) {
		case 'fast':
			enemyGraphics.tint = 0x00AAFF; // Blue for fast enemies
			break;
		case 'immune':
			enemyGraphics.tint = 0xAA0000; // Red for immune enemies
			break;
		case 'flying':
			enemyGraphics.tint = 0xFFFF00; // Yellow for flying enemies
			break;
		case 'swarm':
			enemyGraphics.tint = 0xFF00FF; // Pink for swarm enemies
			break;
	}*/
	// Create shadow for flying enemies
	if (self.isFlying) {
		// Create a shadow container that will be added to the shadow layer
		self.shadow = new Container();
		// Clone the enemy graphics for the shadow
		var shadowGraphics = self.shadow.attachAsset(assetId || 'enemy', {
			anchorX: 0.5,
			anchorY: 0.5
		});
		// Apply shadow effect
		shadowGraphics.tint = 0x000000; // Black shadow
		shadowGraphics.alpha = 0.4; // Semi-transparent
		// If this is a boss, scale up the shadow to match
		if (self.isBoss) {
			shadowGraphics.scaleX = 1.8;
			shadowGraphics.scaleY = 1.8;
		}
		// Position shadow slightly offset
		self.shadow.x = 20; // Offset right
		self.shadow.y = 20; // Offset down
		// Ensure shadow has the same rotation as the enemy
		shadowGraphics.rotation = enemyGraphics.rotation;
	}
	var healthBarOutline = self.attachAsset('healthBarOutline', {
		anchorX: 0,
		anchorY: 0.5
	});
	var healthBarBG = self.attachAsset('healthBar', {
		anchorX: 0,
		anchorY: 0.5
	});
	var healthBar = self.attachAsset('healthBar', {
		anchorX: 0,
		anchorY: 0.5
	});
	healthBarBG.y = healthBarOutline.y = healthBar.y = -enemyGraphics.height / 2 - 10;
	healthBarOutline.x = -healthBarOutline.width / 2;
	healthBarBG.x = healthBar.x = -healthBar.width / 2 - .5;
	healthBar.tint = 0x00ff00;
	healthBarBG.tint = 0xff0000;
	self.healthBar = healthBar;
	self.update = function () {
		if (self.health <= 0) {
			self.health = 0;
			self.healthBar.width = 0;
		}
		// Handle slow effect
		if (self.isImmune) {
			// Immune enemies cannot be slowed or poisoned, clear any such effects
			self.slowed = false;
			self.slowEffect = false;
			self.poisoned = false;
			self.poisonEffect = false;
			// Reset speed to original if needed
			if (self.originalSpeed !== undefined) {
				self.speed = self.originalSpeed;
			}
		} else {
			// Handle slow effect
			if (self.slowed) {
				// Visual indication of slowed status
				if (!self.slowEffect) {
					self.slowEffect = true;
				}
				self.slowDuration--;
				if (self.slowDuration <= 0) {
					self.speed = self.originalSpeed;
					self.slowed = false;
					self.slowEffect = false;
					// Only reset tint if not poisoned
					if (!self.poisoned) {
						enemyGraphics.tint = 0xFFFFFF; // Reset tint
					}
				}
			}
			// Handle poison effect
			if (self.poisoned) {
				// Visual indication of poisoned status
				if (!self.poisonEffect) {
					self.poisonEffect = true;
				}
				// Apply poison damage every 30 frames (twice per second)
				if (LK.ticks % 30 === 0) {
					self.health -= self.poisonDamage;
					if (self.health <= 0) {
						self.health = 0;
					}
					self.healthBar.width = self.health / self.maxHealth * 70;
				}
				self.poisonDuration--;
				if (self.poisonDuration <= 0) {
					self.poisoned = false;
					self.poisonEffect = false;
					// Only reset tint if not slowed
					if (!self.slowed) {
						enemyGraphics.tint = 0xFFFFFF; // Reset tint
					}
				}
			}
		}
		// Set tint based on effect status
		if (self.isImmune) {
			enemyGraphics.tint = 0xFFFFFF;
		} else if (self.poisoned && self.slowed) {
			// Combine poison (0x00FFAA) and slow (0x9900FF) colors
			// Simple average: R: (0+153)/2=76, G: (255+0)/2=127, B: (170+255)/2=212
			enemyGraphics.tint = 0x4C7FD4;
		} else if (self.poisoned) {
			enemyGraphics.tint = 0x00FFAA;
		} else if (self.slowed) {
			enemyGraphics.tint = 0x9900FF;
		} else {
			enemyGraphics.tint = 0xFFFFFF;
		}
		if (self.currentTarget) {
			var ox = self.currentTarget.x - self.currentCellX;
			var oy = self.currentTarget.y - self.currentCellY;
			if (ox !== 0 || oy !== 0) {
				var angle = Math.atan2(oy, ox);
				if (enemyGraphics.targetRotation === undefined) {
					enemyGraphics.targetRotation = angle;
					enemyGraphics.rotation = angle;
				} else {
					if (Math.abs(angle - enemyGraphics.targetRotation) > 0.05) {
						tween.stop(enemyGraphics, {
							rotation: true
						});
						// Calculate the shortest angle to rotate
						var currentRotation = enemyGraphics.rotation;
						var angleDiff = angle - currentRotation;
						// Normalize angle difference to -PI to PI range for shortest path
						while (angleDiff > Math.PI) {
							angleDiff -= Math.PI * 2;
						}
						while (angleDiff < -Math.PI) {
							angleDiff += Math.PI * 2;
						}
						enemyGraphics.targetRotation = angle;
						tween(enemyGraphics, {
							rotation: currentRotation + angleDiff
						}, {
							duration: 250,
							easing: tween.easeOut
						});
					}
				}
			}
		}
		healthBarOutline.y = healthBarBG.y = healthBar.y = -enemyGraphics.height / 2 - 10;
	};
	return self;
});
var 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 = [];
	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 = [];
		// 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]);
					}
				} 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]);
					}
				} 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
			enemy.currentCellY += enemy.speed;
			// Rotate enemy graphic to face downward (PI/2 radians = 90 degrees)
			var angle = Math.PI / 2;
			if (enemy.children[0] && enemy.children[0].targetRotation === undefined) {
				enemy.children[0].targetRotation = angle;
				enemy.children[0].rotation = angle;
			} else if (enemy.children[0]) {
				if (Math.abs(angle - enemy.children[0].targetRotation) > 0.05) {
					tween.stop(enemy.children[0], {
						rotation: true
					});
					// Calculate the shortest angle to rotate
					var currentRotation = enemy.children[0].rotation;
					var angleDiff = angle - currentRotation;
					// Normalize angle difference to -PI to PI range for shortest path
					while (angleDiff > Math.PI) {
						angleDiff -= Math.PI * 2;
					}
					while (angleDiff < -Math.PI) {
						angleDiff += Math.PI * 2;
					}
					// Set target rotation and animate to it
					enemy.children[0].targetRotation = angle;
					tween(enemy.children[0], {
						rotation: currentRotation + angleDiff
					}, {
						duration: 250,
						easing: tween.easeOut
					});
				}
			}
			// Update enemy's position
			enemy.x = grid.x + enemy.currentCellX * CELL_SIZE;
			enemy.y = grid.y + enemy.currentCellY * CELL_SIZE;
			// If enemy has now reached the entry area, update cell coordinates
			if (enemy.currentCellY >= 4) {
				enemy.cellX = Math.round(enemy.currentCellX);
				enemy.cellY = Math.round(enemy.currentCellY);
			}
			return false;
		}
		// After reaching entry area, handle flying enemies differently
		if (enemy.isFlying) {
			// Flying enemies head straight to the closest goal
			if (!enemy.flyingTarget) {
				// Set flying target to the closest goal
				enemy.flyingTarget = self.goals[0];
				// Find closest goal if there are multiple
				if (self.goals.length > 1) {
					var closestDist = Infinity;
					for (var i = 0; i < self.goals.length; i++) {
						var goal = self.goals[i];
						var dx = goal.x - enemy.cellX;
						var dy = goal.y - enemy.cellY;
						var dist = dx * dx + dy * dy;
						if (dist < closestDist) {
							closestDist = dist;
							enemy.flyingTarget = goal;
						}
					}
				}
			}
			// Move directly toward the goal
			var ox = enemy.flyingTarget.x - enemy.currentCellX;
			var oy = enemy.flyingTarget.y - enemy.currentCellY;
			var dist = Math.sqrt(ox * ox + oy * oy);
			if (dist < enemy.speed) {
				// Reached the goal
				return true;
			}
			var angle = Math.atan2(oy, ox);
			// Rotate enemy graphic to match movement direction
			if (enemy.children[0] && enemy.children[0].targetRotation === undefined) {
				enemy.children[0].targetRotation = angle;
				enemy.children[0].rotation = angle;
			} else if (enemy.children[0]) {
				if (Math.abs(angle - enemy.children[0].targetRotation) > 0.05) {
					tween.stop(enemy.children[0], {
						rotation: true
					});
					// Calculate the shortest angle to rotate
					var currentRotation = enemy.children[0].rotation;
					var angleDiff = angle - currentRotation;
					// Normalize angle difference to -PI to PI range for shortest path
					while (angleDiff > Math.PI) {
						angleDiff -= Math.PI * 2;
					}
					while (angleDiff < -Math.PI) {
						angleDiff += Math.PI * 2;
					}
					// Set target rotation and animate to it
					enemy.children[0].targetRotation = angle;
					tween(enemy.children[0], {
						rotation: currentRotation + angleDiff
					}, {
						duration: 250,
						easing: tween.easeOut
					});
				}
			}
			// Update the cell position to track where the flying enemy is
			enemy.cellX = Math.round(enemy.currentCellX);
			enemy.cellY = Math.round(enemy.currentCellY);
			enemy.currentCellX += Math.cos(angle) * enemy.speed;
			enemy.currentCellY += Math.sin(angle) * enemy.speed;
			enemy.x = grid.x + enemy.currentCellX * CELL_SIZE;
			enemy.y = grid.y + enemy.currentCellY * CELL_SIZE;
			// Update shadow position if this is a flying enemy
			return false;
		}
		// Handle normal pathfinding enemies
		if (!enemy.currentTarget) {
			enemy.currentTarget = cell.targets[0];
		}
		// 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;
			enemy.currentCellY += Math.sin(angle) * enemy.speed;
		}
		enemy.x = grid.x + enemy.currentCellX * CELL_SIZE;
		enemy.y = grid.y + enemy.currentCellY * CELL_SIZE;
	};
});
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;
	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('Firewall Defensors', {
		size: 100,
		fill: 0xFFFFFF,
		weight: 800
	});
	titleText.anchor.set(0.5, 0.5);
	titleText.y = -300;
	self.addChild(titleText);
	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('Start Game', {
		size: 50,
		fill: 0xFFFFFF,
		weight: 800
	});
	startButtonText.anchor.set(0.5, 0.5);
	startButton.addChild(startButtonText);
	startButton.y = 100;
	self.addChild(startButton);
	startButton.down = function () {
		self.destroy();
		game.startGame();
	};
	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 () {
		if (waveIndicator && waveIndicator.gameStarted && currentWave < totalWaves && !waveInProgress && enemies.length === 0) {
			self.enabled = true;
			self.visible = true;
			buttonBackground.tint = 0x0088FF;
			self.alpha = 1;
		} else {
			self.enabled = false;
			self.visible = false;
			buttonBackground.tint = 0x888888;
			self.alpha = 0.7;
		}
	};
	self.down = function () {
		if (!self.enabled) {
			return;
		}
		if (waveIndicator.gameStarted && currentWave < totalWaves && !waveInProgress && enemies.length === 0) {
			currentWave++; // Increment to the next wave directly
			// Calculate current world and level
			currentWorld = Math.ceil(currentWave / 9);
			currentLevel = (currentWave - 1) % 9 + 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(currentWave);
			var enemyCount = waveIndicator.getEnemyCount(currentWave);
			var notification = game.addChild(new Notification("World " + currentWorld + " Level " + currentLevel + " (" + waveType + " - " + enemyCount + " enemies) activated!"));
			notification.x = 2048 / 2;
			notification.y = grid.height - 150;
		}
	};
	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);
	notificationGraphics.width = notificationText.width + 30;
	self.addChild(notificationText);
	self.alpha = 1;
	var fadeOutTime = 120;
	self.update = function () {
		if (fadeOutTime > 0) {
			fadeOutTime--;
			self.alpha = Math.min(fadeOutTime / 120 * 2, 1);
		} else {
			self.destroy();
		}
	};
	return self;
});
var SourceTower = Container.expand(function (towerType) {
	var self = Container.call(this);
	self.towerType = towerType || 'default';
	// Increase size of base for easier touch
	var baseGraphics = self.attachAsset('tower', {
		anchorX: 0.5,
		anchorY: 0.5,
		scaleX: 1.3,
		scaleY: 1.3
	});
	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: 50,
		fill: 0xFFFFFF,
		weight: 800
	});
	typeLabel.anchor.set(0.5, 0.5);
	typeLabel.y = -20; // 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: 50,
		fill: 0xFFD700,
		weight: 800
	});
	costLabel.anchor.set(0.5, 0.5);
	costLabel.y = 20 + 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: "ALERT! Pokémon infiltrators have breached the Digital Forest servers! They're attempting to steal Digimon data files!",
					image: 'agumon'
				}, {
					text: "These Pokémon spies are using advanced stealth protocols to access our core database. Deploy Digimon guardians immediately!",
					image: 'gabumon'
				}, {
					text: "The future of the Digimon franchise depends on you! Stop Pokémon from corrupting our digital ecosystem!",
					image: 'tentomon'
				}];
			case 2:
				return [{
					text: "Pokémon agents have infiltrated the Desert Data Center! They're planting malicious code to corrupt our systems!",
					image: 'palmon'
				}, {
					text: "Fire-type Pokémon are overheating our servers while others steal precious Digimon evolution data!",
					image: 'gomamon'
				}, {
					text: "Their coordinated attack is more sophisticated than before. Pokémon want to monopolize the children's entertainment industry!",
					image: 'patamon'
				}];
			case 3:
				return [{
					text: "Ice-type Pokémon have frozen our Glacier servers to slow down our defenses while they extract data!",
					image: null
				}, {
					text: "Flying Pokémon are bypassing our security walls! They're trying to reach the core Digimon genetic database!",
					image: null
				}, {
					text: "Critical system temperatures detected! Pokémon are trying to cause a complete server meltdown!",
					image: null
				}];
			case 4:
				return [{
					text: "Pokémon sleeper agents hidden in the Village Network have activated! They've been gathering intelligence for months!",
					image: null
				}, {
					text: "Multiple Pokémon strike teams are attacking simultaneously, trying to overwhelm our Digimon defenders!",
					image: null
				}, {
					text: "This is corporate espionage on a massive scale! Pokémon Company wants to steal our digital creature technology!",
					image: null
				}];
			case 5:
				return [{
					text: "MAXIMUM THREAT LEVEL! Elite Pokémon hackers have breached our most secure Technology Labs!",
					image: null
				}, {
					text: "They're using legendary Pokémon abilities to bypass our quantum encryption! Our most sensitive data is at risk!",
					image: null
				}, {
					text: "Deploy our strongest Mega-level Digimon! Only they can stop this corporate cyber warfare!",
					image: null
				}];
			case 6:
				return [{
					text: "FINAL ASSAULT! Pokémon's master plan is revealed - they want to delete ALL Digimon data permanently!",
					image: null
				}, {
					text: "Legendary Pokémon themselves are leading this final attack on our core servers! This is the ultimate battle for supremacy!",
					image: null
				}, {
					text: "The children's hearts are at stake! Defeat Pokémon's invasion and save the future of digital monsters forever!",
					image: null
				}];
			default:
				return [{
					text: "Pokémon infiltrators detected! Protect the Digimon database!",
					image: null
				}, {
					text: "Deploy your Digimon to stop the corporate espionage!",
					image: null
				}, {
					text: "Save the Digital World from Pokémon's takeover!",
					image: null
				}];
		}
	};
	// 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);
	// Auto-advance after 4 seconds or click to advance
	var autoAdvanceTimer = LK.setTimeout(function () {
		self.nextPanel();
	}, 4000);
	self.down = function () {
		LK.clearTimeout(autoAdvanceTimer);
		autoAdvanceTimer = LK.setTimeout(function () {
			self.nextPanel();
		}, 4000);
		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;
	// 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 'gabumon':
			//{8L} // Rapid fire Digimon
			self.fireRate = 30;
			self.damage = 5;
			self.range = 2.5 * CELL_SIZE;
			self.bulletSpeed = 7;
			break;
		case 'tentomon':
			//{8N} // Long range Digimon
			self.fireRate = 90;
			self.damage = 25;
			self.range = 5 * CELL_SIZE;
			self.bulletSpeed = 25;
			break;
		case 'palmon':
			//{8P} // Area damage Digimon
			self.fireRate = 75;
			self.damage = 15;
			self.range = 2 * CELL_SIZE;
			self.bulletSpeed = 4;
			break;
		case 'gomamon':
			//{8R} // Slowing Digimon
			self.fireRate = 50;
			self.damage = 8;
			self.range = 3.5 * CELL_SIZE;
			self.bulletSpeed = 5;
			break;
		case 'patamon':
			//{8U} // Poison/status Digimon
			self.fireRate = 70;
			self.damage = 12;
			self.range = 3.2 * CELL_SIZE;
			self.bulletSpeed = 5;
			break;
	}
	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;
	}
	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++;
				// No need to update self.range here; getRange() is now the source of truth
				// Apply tower-specific upgrades based on type
				if (self.id === 'rapid') {
					if (self.level === self.maxLevel) {
						// Extra powerful last upgrade (double the effect)
						self.fireRate = Math.max(4, 30 - self.level * 9); // double the effect
						self.damage = 5 + self.level * 10; // double the effect
						self.bulletSpeed = 7 + self.level * 2.4; // double the effect
					} else {
						self.fireRate = Math.max(15, 30 - self.level * 3); // Fast tower gets faster with upgrades
						self.damage = 5 + self.level * 3;
						self.bulletSpeed = 7 + self.level * 0.7;
					}
				} else {
					if (self.level === self.maxLevel) {
						// Extra powerful last upgrade for all other towers (double the effect)
						self.fireRate = Math.max(5, 60 - self.level * 24); // double the effect
						self.damage = 10 + self.level * 20; // double the effect
						self.bulletSpeed = 5 + self.level * 2.4; // double the effect
					} else {
						self.fireRate = Math.max(20, 60 - self.level * 8);
						self.damage = 10 + self.level * 5;
						self.bulletSpeed = 5 + self.level * 0.5;
					}
				}
				self.refreshCellsInRange();
				self.updateLevelIndicators();
				if (self.level > 1) {
					var levelDot = levelIndicators[self.level - 1].children[1];
					tween(levelDot, {
						scaleX: 1.5,
						scaleY: 1.5
					}, {
						duration: 300,
						easing: tween.elasticOut,
						onFinish: function onFinish() {
							tween(levelDot, {
								scaleX: 1,
								scaleY: 1
							}, {
								duration: 200,
								easing: tween.easeOut
							});
						}
					});
				}
				return true;
			} else {
				var notification = game.addChild(new Notification("Not enough 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 () {
		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;
		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 () {
		if (self.targetEnemy) {
			var potentialDamage = 0;
			for (var i = 0; i < self.targetEnemy.bulletsTargetingThis.length; i++) {
				potentialDamage += self.targetEnemy.bulletsTargetingThis[i].damage;
			}
			if (self.targetEnemy.health > potentialDamage) {
				var bulletX = self.x + Math.cos(gunContainer.rotation) * 40;
				var bulletY = self.y + Math.sin(gunContainer.rotation) * 40;
				var bullet = new Bullet(bulletX, bulletY, self.targetEnemy, self.damage, self.bulletSpeed);
				// Set bullet type based on tower type
				bullet.type = self.id;
				// For slow tower, pass level for scaling slow effect
				if (self.id === 'slow') {
					bullet.sourceTowerLevel = self.level;
				}
				// 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 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 + '\nDamage: ' + self.tower.damage + '\nFire Rate: ' + (60 / self.tower.fireRate).toFixed(1) + '/s', {
		size: 70,
		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);
	// Create digivolve button
	var digivolveButton = new Container();
	buttonsContainer.addChild(digivolveButton);
	var digivolveButtonBackground = digivolveButton.attachAsset('notification', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	digivolveButtonBackground.width = 500;
	digivolveButtonBackground.height = 150;
	var digivolveButtonText = new Text2('Digivolve', {
		size: 60,
		fill: 0xFFFFFF,
		weight: 800
	});
	digivolveButtonText.anchor.set(0.5, 0.5);
	digivolveButton.addChild(digivolveButtonText);
	// Check if digivolution is available
	function canDigivolve() {
		if (self.tower.level < 2) return false; // Need at least level 2
		var hasDigivice = false;
		if (self.tower.level >= 2 && self.tower.level <= 3 && storage.digiviceC) hasDigivice = true;
		if (self.tower.level >= 4 && self.tower.level <= 5 && storage.digiviceB) hasDigivice = true;
		if (self.tower.level >= 6 && storage.digiviceA) hasDigivice = true;
		return hasDigivice;
	}
	digivolveButton.update = function () {
		var canEvolve = canDigivolve();
		digivolveButton.visible = canEvolve;
		if (canEvolve) {
			digivolveButtonBackground.tint = 0xFF6600;
		} else {
			digivolveButtonBackground.tint = 0x666666;
		}
	};
	digivolveButton.down = function () {
		if (canDigivolve()) {
			var evolutionCost = getTowerCost(self.tower.id) * 2;
			if (gold >= evolutionCost) {
				setGold(gold - evolutionCost);
				// Apply evolution effects
				self.tower.damage *= 1.5;
				self.tower.fireRate = Math.max(5, Math.floor(self.tower.fireRate * 0.8));
				var notification = game.addChild(new Notification(self.tower.id + " digivolved!"));
				notification.x = 2048 / 2;
				notification.y = grid.height - 50;
				// Update stats display
				statsText.setText('Level: ' + self.tower.level + '/' + self.tower.maxLevel + '\nDamage: ' + self.tower.damage + '\nFire Rate: ' + (60 / self.tower.fireRate).toFixed(1) + '/s');
			} else {
				var notification = game.addChild(new Notification("Not enough bits to digivolve!"));
				notification.x = 2048 / 2;
				notification.y = grid.height - 50;
			}
		}
	};
	upgradeButton.y = -125;
	digivolveButton.y = 0;
	sellButton.y = 125;
	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();
		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;
		var moveAmount = (progress + currentWave) * blockWidth;
		for (var i = 0; i < self.waveMarkers.length; i++) {
			var marker = self.waveMarkers[i];
			marker.x = -moveAmount + i * blockWidth;
		}
		self.positionIndicator.x = 0;
		for (var i = 0; i < totalWaves + 1; i++) {
			var marker = self.waveMarkers[i];
			if (i === 0) {
				continue;
			}
			var block = marker.children[0];
			if (i - 1 < currentWave) {
				block.alpha = .5;
			}
		}
		self.handleWaveProgression = function () {
			if (!self.gameStarted) {
				return;
			}
			// 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: 'forestBackgroundTile',
					path: 'forestPathTile',
					wall: 'forestWallTile',
					scenery: 'forestSceneryElement',
					ambient: 0x90ee90
				};
			case 2:
				return {
					background: 'desertBackgroundTile',
					path: 'desertPathTile',
					wall: 'desertWallTile',
					scenery: 'desertSceneryElement',
					ambient: 0xffd700
				};
			case 3:
				return {
					background: 'glacierBackgroundTile',
					path: 'glacierPathTile',
					wall: 'glacierWallTile',
					scenery: 'glacierSceneryElement',
					ambient: 0xe6f3ff
				};
			case 4:
				return {
					background: 'villageBackgroundTile',
					path: 'villagePathTile',
					wall: 'villageWallTile',
					scenery: 'villageSceneryElement',
					ambient: 0xf0e68c
				};
			case 5:
				return {
					background: 'techLabBackgroundTile',
					path: 'techLabPathTile',
					wall: 'techLabWallTile',
					scenery: 'techLabSceneryElement',
					ambient: 0x87ceeb
				};
			case 6:
				return {
					background: 'infernoBackgroundTile',
					path: 'infernoPathTile',
					wall: 'infernoWallTile',
					scenery: 'infernoSceneryElement',
					ambient: 0xff6347
				};
			default:
				return {
					background: 'forestBackgroundTile',
					path: 'forestPathTile',
					wall: 'forestWallTile',
					scenery: 'forestSceneryElement',
					ambient: 0x90ee90
				};
		}
	};
	self.updateWorldGraphics = function (worldNumber, gridInstance) {
		if (self.currentWorld === worldNumber) {
			return; // Already rendered this world
		}
		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);
					}
				}
			}
		}
	};
	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", "Tech Lab", "Inferno"];
	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();
					game.startWorld(worldIndex + 1);
				} 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
****/ 
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;
var enemies = [];
var towers = [];
var bullets = [];
var defenses = [];
var selectedTower = null;
var gold = 80;
var lives = 20;
var score = 0;
var currentWave = 0;
var totalWaves = 54; // 6 worlds × 9 levels = 54 total waves
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 goldText = new Text2('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('System Health', {
	size: 45,
	fill: 0xFFFFFF,
	weight: 800
});
livesText.anchor.set(0.5, 0.5);
livesText.y = -35;
healthBarContainer.addChild(livesText);
var scoreText = new Text2('Security Score: ' + score, {
	size: 60,
	fill: 0xFF0000,
	weight: 800
});
scoreText.anchor.set(0.5, 0.5);
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, 4];
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 if (gameSpeed === 2) {
		speedButtonBg.tint = 0xFFAA00; // Orange for 2x speed
	} else {
		speedButtonBg.tint = 0xFF0000; // Red for 4x speed
	}
};
LK.gui.top.addChild(goldText);
LK.gui.bottom.addChild(healthBarContainer);
LK.gui.top.addChild(scoreText);
LK.gui.top.addChild(speedButton);
healthBarContainer.x = 0;
healthBarContainer.y = -50;
goldText.x = -spacing;
goldText.y = topMargin;
scoreText.x = spacing;
scoreText.y = topMargin;
speedButton.x = 0;
speedButton.y = topMargin;
function updateUI() {
	goldText.setText('Bits: ' + gold);
	scoreText.setText('Security Score: ' + 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();
// Render initial world graphics
worldRenderer.updateWorldGraphics(1, grid);
// Only render debug on game start, not every frame
if (LK.ticks === 0) {
	grid.renderDebug();
}
debugLayer.addChild(grid);
game.addChild(backgroundLayer);
game.addChild(debugLayer);
game.addChild(towerLayer);
game.addChild(enemyLayer);
var offset = 0;
var towerPreview = new TowerPreview();
game.addChild(towerPreview);
towerPreview.visible = false;
var isDragging = false;
function wouldBlockPath(gridX, gridY) {
	var cells = [];
	for (var i = 0; i < 2; i++) {
		for (var j = 0; j < 2; j++) {
			var cell = grid.getCell(gridX + i, gridY + j);
			if (cell) {
				cells.push({
					cell: cell,
					originalType: cell.type
				});
				cell.type = 1;
			}
		}
	}
	var blocked = grid.pathFind();
	for (var i = 0; i < cells.length; i++) {
		cells[i].cell.type = cells[i].originalType;
	}
	grid.pathFind();
	grid.renderDebug();
	return blocked;
}
function getTowerCost(towerType) {
	var cost = 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);
		grid.pathFind();
		grid.renderDebug();
		return true;
	} else {
		var notification = game.addChild(new Notification("Not enough bits!"));
		notification.x = 2048 / 2;
		notification.y = grid.height - 50;
		return false;
	}
}
game.down = function (x, y, obj) {
	var upgradeMenuVisible = game.children.some(function (child) {
		return child instanceof UpgradeMenu;
	});
	if (upgradeMenuVisible) {
		return;
	}
	for (var i = 0; i < sourceTowers.length; i++) {
		var tower = sourceTowers[i];
		if (x >= tower.x - tower.width / 2 && x <= tower.x + tower.width / 2 && y >= tower.y - tower.height / 2 && y <= tower.y + tower.height / 2) {
			towerPreview.visible = true;
			isDragging = true;
			towerPreview.towerType = tower.towerType;
			towerPreview.updateAppearance();
			// Apply the same offset as in move handler to ensure consistency when starting drag
			towerPreview.snapToGrid(x, y - CELL_SIZE * 1.5);
			break;
		}
	}
};
game.move = function (x, y, obj) {
	if (isDragging) {
		// Shift the y position upward by 1.5 tiles to show preview above finger
		towerPreview.snapToGrid(x, y - CELL_SIZE * 1.5);
	}
};
game.up = function (x, y, obj) {
	var clickedOnTower = false;
	for (var i = 0; i < towers.length; i++) {
		var tower = towers[i];
		var towerLeft = tower.x - tower.width / 2;
		var towerRight = tower.x + tower.width / 2;
		var towerTop = tower.y - tower.height / 2;
		var towerBottom = tower.y + tower.height / 2;
		if (x >= towerLeft && x <= towerRight && y >= towerTop && y <= towerBottom) {
			clickedOnTower = true;
			break;
		}
	}
	var upgradeMenus = game.children.filter(function (child) {
		return child instanceof UpgradeMenu;
	});
	if (upgradeMenus.length > 0 && !isDragging && !clickedOnTower) {
		var clickedOnMenu = false;
		for (var i = 0; i < upgradeMenus.length; i++) {
			var menu = upgradeMenus[i];
			var menuWidth = 2048;
			var menuHeight = 450;
			var menuLeft = menu.x - menuWidth / 2;
			var menuRight = menu.x + menuWidth / 2;
			var menuTop = menu.y - menuHeight / 2;
			var menuBottom = menu.y + menuHeight / 2;
			if (x >= menuLeft && x <= menuRight && y >= menuTop && y <= menuBottom) {
				clickedOnMenu = true;
				break;
			}
		}
		if (!clickedOnMenu) {
			for (var i = 0; i < upgradeMenus.length; i++) {
				var menu = upgradeMenus[i];
				hideUpgradeMenu(menu);
			}
			for (var i = game.children.length - 1; i >= 0; i--) {
				if (game.children[i].isTowerRange) {
					game.removeChild(game.children[i]);
				}
			}
			selectedTower = null;
			grid.renderDebug();
		}
	}
	if (isDragging) {
		isDragging = false;
		if (towerPreview.canPlace) {
			if (!wouldBlockPath(towerPreview.gridX, towerPreview.gridY)) {
				placeTower(towerPreview.gridX, towerPreview.gridY, towerPreview.towerType);
			} else {
				var notification = game.addChild(new Notification("Tower would block the path!"));
				notification.x = 2048 / 2;
				notification.y = grid.height - 50;
			}
		} else if (towerPreview.visible) {
			var notification = game.addChild(new Notification("Cannot build here!"));
			notification.x = 2048 / 2;
			notification.y = grid.height - 50;
		}
		towerPreview.visible = false;
		if (isDragging) {
			var upgradeMenus = game.children.filter(function (child) {
				return child instanceof UpgradeMenu;
			});
			for (var i = 0; i < upgradeMenus.length; i++) {
				upgradeMenus[i].destroy();
			}
		}
	}
};
var waveIndicator = new WaveIndicator();
waveIndicator.x = 2048 / 2;
waveIndicator.y = 2732 - 80;
game.addChild(waveIndicator);
var nextWaveButtonContainer = new Container();
var nextWaveButton = new NextWaveButton();
nextWaveButton.x = 2048 - 200;
nextWaveButton.y = 2732 - 100 + 20;
nextWaveButtonContainer.addChild(nextWaveButton);
game.addChild(nextWaveButtonContainer);
// Add 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();
};
var towerTypes = ['agumon', 'gabumon', 'tentomon', 'palmon', 'gomamon', 'patamon'];
var sourceTowers = [];
var towerSpacing = 300; // Increase spacing for larger towers
var startX = 2048 / 2 - towerTypes.length * towerSpacing / 2 + towerSpacing / 2;
var towerY = 2732 - CELL_SIZE * 3 - 90;
for (var i = 0; i < towerTypes.length; i++) {
	var tower = new SourceTower(towerTypes[i]);
	tower.x = startX + i * towerSpacing;
	tower.y = towerY;
	towerLayer.addChild(tower);
	sourceTowers.push(tower);
}
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;
			// Check if we entered a new world and regenerate maze
			var newWorld = Math.ceil(currentWave / 9);
			if (newWorld > currentWorld || currentWave === 1) {
				currentWorld = newWorld;
				if (currentWorld > 6) currentWorld = 6;
				grid.generateMazeForWorld(currentWorld);
				grid.pathFind();
				grid.renderDebug();
				// Update world graphics
				worldRenderer.updateWorldGraphics(currentWorld, grid);
				// World-specific notification messages
				var worldNames = ["", "Forest", "Desert", "Glacier", "Village", "Tech Lab", "Inferno"];
				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
			var waveType = waveIndicator.getWaveType(currentWave);
			var enemyCount = waveIndicator.getEnemyCount(currentWave);
			// Check if this is a boss wave
			var isBossWave = currentWave % 10 === 0 && currentWave > 0;
			if (isBossWave && waveType !== 'swarm') {
				// Boss waves have just 1 enemy regardless of what the wave indicator says
				enemyCount = 1;
				// Show boss announcement
				var notification = game.addChild(new Notification("⚠️ BOSS WAVE! ⚠️"));
				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);
				// Add enemy to the appropriate layer based on type
				if (enemy.isFlying) {
					// Add flying enemy to the top layer
					enemyLayerTop.addChild(enemy);
					// If it's a flying enemy, add its shadow to the middle layer
					if (enemy.shadow) {
						enemyLayerMiddle.addChild(enemy.shadow);
					}
				} else {
					// Add normal/ground enemies to the bottom layer
					enemyLayerBottom.addChild(enemy);
				}
				// Scale difficulty with wave number but don't apply to boss
				// as bosses already have their health multiplier
				// Use exponential scaling for health
				var healthMultiplier = Math.pow(1.12, currentWave); // ~20% increase per wave
				enemy.maxHealth = Math.round(enemy.maxHealth * healthMultiplier);
				enemy.health = enemy.maxHealth;
				// Increment speed slightly with wave number
				//enemy.speed = enemy.speed + currentWave * 0.002;
				// All enemy types now spawn in the middle 6 tiles at the top spacing
				var gridWidth = 24;
				var midPoint = Math.floor(gridWidth / 2); // 12
				// Find a column that isn't occupied by another enemy that's not yet in view
				var availableColumns = [];
				for (var col = midPoint - 3; col < midPoint + 3; col++) {
					var columnOccupied = false;
					// Check if any enemy is already in this column but not yet in view
					for (var e = 0; e < enemies.length; e++) {
						if (enemies[e].cellX === col && enemies[e].currentCellY < 4) {
							columnOccupied = true;
							break;
						}
					}
					if (!columnOccupied) {
						availableColumns.push(col);
					}
				}
				// If all columns are occupied, use original random method
				var spawnX;
				if (availableColumns.length > 0) {
					// Choose a random unoccupied column
					spawnX = availableColumns[Math.floor(Math.random() * availableColumns.length)];
				} else {
					// Fallback to random if all columns are occupied
					spawnX = midPoint - 3 + Math.floor(Math.random() * 6); // x from 9 to 14
				}
				var spawnY = -1 - Math.random() * 5; // Random distance above the grid for spreading
				enemy.cellX = spawnX;
				enemy.cellY = 5; // Position after entry
				enemy.currentCellX = spawnX;
				enemy.currentCellY = spawnY;
				enemy.waveNumber = currentWave;
				enemies.push(enemy);
			}
		}
		var currentWaveEnemiesRemaining = false;
		for (var i = 0; i < enemies.length; i++) {
			if (enemies[i].waveNumber === currentWave) {
				currentWaveEnemiesRemaining = true;
				break;
			}
		}
		if (waveSpawned && !currentWaveEnemiesRemaining) {
			waveInProgress = false;
			waveSpawned = false;
		}
	}
	// Apply speed multiplier to enemy updates
	for (var a = enemies.length - 1; a >= 0; a--) {
		var enemy = enemies[a];
		// Apply speed multiplier to enemy movement
		if (effectiveSpeed > 1) {
			for (var speedTick = 0; speedTick < effectiveSpeed; speedTick++) {
				if (enemy.update) enemy.update();
			}
		}
		if (enemy.health <= 0) {
			for (var i = 0; i < enemy.bulletsTargetingThis.length; i++) {
				var bullet = enemy.bulletsTargetingThis[i];
				bullet.targetEnemy = null;
			}
			// 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;
			// Add a notification for boss defeat
			if (enemy.isBoss) {
				var notification = game.addChild(new Notification("Boss defeated! +" + goldEarned + " bits!"));
				notification.x = 2048 / 2;
				notification.y = grid.height - 150;
			}
			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);
			lives = Math.max(0, lives - 1);
			updateUI();
			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 world completion and unlock next world
	var wavesPerWorld = 9;
	var completedWaves = storage.completedWaves || 0;
	if (currentWave > completedWaves) {
		storage.completedWaves = currentWave;
		// Check if we completed a world (every 9 waves)
		var worldsCompleted = Math.floor(currentWave / wavesPerWorld);
		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;
		}
	}
	if (currentWave >= totalWaves && enemies.length === 0 && !waveInProgress) {
		LK.showYouWin();
	}
};
var mainMenu = new MainMenu();
game.addChild(mainMenu);
game.startGame = function () {
	var worldSelectionMenu = new WorldSelectionMenu();
	game.addChild(worldSelectionMenu);
};
game.startWorld = function (worldNumber) {
	currentWorld = worldNumber;
	grid.generateMazeForWorld(currentWorld);
	grid.pathFind();
	grid.renderDebug();
	worldRenderer.updateWorldGraphics(currentWorld, grid);
	// Show story sequence before starting the world
	var storySequence = new StorySequence(worldNumber);
	game.addChild(storySequence);
	storySequence.onComplete = function () {
		waveIndicator.gameStarted = true;
		currentWave = (currentWorld - 1) * 9;
		waveTimer = nextWaveTime;
	};
}; ===================================================================
--- original.js
+++ change.js
@@ -2949,94 +2949,64 @@
 	self.backgroundTiles = [];
 	self.pathTiles = [];
 	self.wallTiles = [];
 	self.sceneryElements = [];
-	self.getWorldColors = function (worldNumber) {
+	self.getWorldAssets = function (worldNumber) {
 		switch (worldNumber) {
 			case 1:
-				// Forest
 				return {
-					background: 0x228b22,
-					// Forest green
-					path: 0x8b4513,
-					// Saddle brown (dirt path)
-					wall: 0x2f4f2f,
-					// Dark forest green
-					scenery: 0x32cd32,
-					// Lime green (bushes/trees)
-					ambient: 0x90ee90 // Light green tint
+					background: 'forestBackgroundTile',
+					path: 'forestPathTile',
+					wall: 'forestWallTile',
+					scenery: 'forestSceneryElement',
+					ambient: 0x90ee90
 				};
 			case 2:
-				// Desert
 				return {
-					background: 0xf4a460,
-					// Sandy brown
-					path: 0xdaa520,
-					// Goldenrod (sand path)
-					wall: 0xcd853f,
-					// Peru (rock formations)
-					scenery: 0xdaa520,
-					// Goldenrod (cacti/rocks)
-					ambient: 0xffd700 // Gold tint
+					background: 'desertBackgroundTile',
+					path: 'desertPathTile',
+					wall: 'desertWallTile',
+					scenery: 'desertSceneryElement',
+					ambient: 0xffd700
 				};
 			case 3:
-				// Glacier
 				return {
-					background: 0xb0e0e6,
-					// Powder blue
-					path: 0xe0ffff,
-					// Light cyan (ice path)
-					wall: 0x4682b4,
-					// Steel blue (ice walls)
-					scenery: 0xffffff,
-					// White (snow/ice crystals)
-					ambient: 0xe6f3ff // Light blue tint
+					background: 'glacierBackgroundTile',
+					path: 'glacierPathTile',
+					wall: 'glacierWallTile',
+					scenery: 'glacierSceneryElement',
+					ambient: 0xe6f3ff
 				};
 			case 4:
-				// Village
 				return {
-					background: 0x9acd32,
-					// Yellow green
-					path: 0x696969,
-					// Dim gray (stone path)
-					wall: 0x8b4513,
-					// Saddle brown (wooden buildings)
-					scenery: 0xd2691e,
-					// Chocolate (roofs/structures)
-					ambient: 0xf0e68c // Khaki tint
+					background: 'villageBackgroundTile',
+					path: 'villagePathTile',
+					wall: 'villageWallTile',
+					scenery: 'villageSceneryElement',
+					ambient: 0xf0e68c
 				};
 			case 5:
-				// Technology
 				return {
-					background: 0x2f2f2f,
-					// Dark gray
-					path: 0x708090,
-					// Slate gray (metal corridors)
-					wall: 0x4169e1,
-					// Royal blue (tech panels)
-					scenery: 0x00ced1,
-					// Dark turquoise (machinery)
-					ambient: 0x87ceeb // Sky blue tint
+					background: 'techLabBackgroundTile',
+					path: 'techLabPathTile',
+					wall: 'techLabWallTile',
+					scenery: 'techLabSceneryElement',
+					ambient: 0x87ceeb
 				};
 			case 6:
-				// Hell
 				return {
-					background: 0x8b0000,
-					// Dark red
-					path: 0x660000,
-					// Dark red (lava cracks)
-					wall: 0x000000,
-					// Black (obsidian)
-					scenery: 0xff4500,
-					// Orange red (lava/fire)
-					ambient: 0xff6347 // Tomato red tint
+					background: 'infernoBackgroundTile',
+					path: 'infernoPathTile',
+					wall: 'infernoWallTile',
+					scenery: 'infernoSceneryElement',
+					ambient: 0xff6347
 				};
 			default:
 				return {
-					background: 0x228b22,
-					path: 0x8b4513,
-					wall: 0x2f4f2f,
-					scenery: 0x32cd32,
+					background: 'forestBackgroundTile',
+					path: 'forestPathTile',
+					wall: 'forestWallTile',
+					scenery: 'forestSceneryElement',
 					ambient: 0x90ee90
 				};
 		}
 	};
@@ -3044,9 +3014,9 @@
 		if (self.currentWorld === worldNumber) {
 			return; // Already rendered this world
 		}
 		self.currentWorld = worldNumber;
-		var colors = self.getWorldColors(worldNumber);
+		var worldAssets = self.getWorldAssets(worldNumber);
 		// Clear existing tiles
 		while (self.backgroundTiles.length) {
 			self.removeChild(self.backgroundTiles.pop());
 		}
@@ -3058,36 +3028,15 @@
 		}
 		while (self.sceneryElements.length) {
 			self.removeChild(self.sceneryElements.pop());
 		}
-		// Create new tiles based on current world using custom image assets
+		// 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 image assets
-				var bgAssetName = 'forestBackgroundTile'; // Default
-				switch (worldNumber) {
-					case 1:
-						bgAssetName = 'forestBackgroundTile';
-						break;
-					case 2:
-						bgAssetName = 'desertBackgroundTile';
-						break;
-					case 3:
-						bgAssetName = 'glacierBackgroundTile';
-						break;
-					case 4:
-						bgAssetName = 'villageBackgroundTile';
-						break;
-					case 5:
-						bgAssetName = 'techLabBackgroundTile';
-						break;
-					case 6:
-						bgAssetName = 'infernoBackgroundTile';
-						break;
-				}
-				var bgTile = self.attachAsset(bgAssetName, {
+				// Create background tile using world-specific assets
+				var bgTile = self.attachAsset(worldAssets.background, {
 					anchorX: 0,
 					anchorY: 0
 				});
 				bgTile.x = i * CELL_SIZE;
@@ -3095,30 +3044,9 @@
 				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 sceneryAssetName = 'forestSceneryElement'; // Default
-					switch (worldNumber) {
-						case 1:
-							sceneryAssetName = 'forestSceneryElement';
-							break;
-						case 2:
-							sceneryAssetName = 'desertSceneryElement';
-							break;
-						case 3:
-							sceneryAssetName = 'glacierSceneryElement';
-							break;
-						case 4:
-							sceneryAssetName = 'villageSceneryElement';
-							break;
-						case 5:
-							sceneryAssetName = 'techLabSceneryElement';
-							break;
-						case 6:
-							sceneryAssetName = 'infernoSceneryElement';
-							break;
-					}
-					var scenery = self.attachAsset(sceneryAssetName, {
+					var scenery = self.attachAsset(worldAssets.scenery, {
 						anchorX: 0.5,
 						anchorY: 0.5
 					});
 					scenery.x = i * CELL_SIZE + CELL_SIZE / 2;
@@ -3173,70 +3101,24 @@
 				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 pathAssetName = 'forestPathTile'; // Default
-						switch (worldNumber) {
-							case 1:
-								pathAssetName = 'forestPathTile';
-								break;
-							case 2:
-								pathAssetName = 'desertPathTile';
-								break;
-							case 3:
-								pathAssetName = 'glacierPathTile';
-								break;
-							case 4:
-								pathAssetName = 'villagePathTile';
-								break;
-							case 5:
-								pathAssetName = 'techLabPathTile';
-								break;
-							case 6:
-								pathAssetName = 'infernoPathTile';
-								break;
-						}
-						var pathTile = self.attachAsset(pathAssetName, {
+						var pathTile = self.attachAsset(worldAssets.path, {
 							anchorX: 0,
 							anchorY: 0
 						});
 						pathTile.x = i * CELL_SIZE;
 						pathTile.y = j * CELL_SIZE;
-						// Optionally tint path tiles for extra world flavor
-						// pathTile.tint = colors.path;
 						pathTile.alpha = 0.95;
 						self.pathTiles.push(pathTile);
 					} else if (cell.type === 1) {
 						// Wall - use appropriate wall tile based on world
-						var wallAssetName = 'forestWallTile';
-						switch (worldNumber) {
-							case 1:
-								wallAssetName = 'forestWallTile';
-								break;
-							case 2:
-								wallAssetName = 'desertWallTile';
-								break;
-							case 3:
-								wallAssetName = 'glacierWallTile';
-								break;
-							case 4:
-								wallAssetName = 'villageWallTile';
-								break;
-							case 5:
-								wallAssetName = 'techLabWallTile';
-								break;
-							case 6:
-								wallAssetName = 'infernoWallTile';
-								break;
-						}
-						var wallTile = self.attachAsset(wallAssetName, {
+						var wallTile = self.attachAsset(worldAssets.wall, {
 							anchorX: 0,
 							anchorY: 0
 						});
 						wallTile.x = i * CELL_SIZE;
 						wallTile.y = j * CELL_SIZE;
-						// Optionally tint wall tiles for extra world flavor
-						// wallTile.tint = colors.wall;
 						wallTile.alpha = 0.98;
 						self.wallTiles.push(wallTile);
 					}
 				}
@@ -3707,11 +3589,11 @@
 sourceTower = null;
 enemiesToSpawn = 10;
 game.update = function () {
 	// Update background color based on current world
-	var worldColors = worldRenderer.getWorldColors(currentWorld);
-	if (worldColors) {
-		game.setBackgroundColor(worldColors.ambient);
+	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
: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