Code edit (5 edits merged)
Please save this source code
User prompt
Rename Notification asset Notification1
User prompt
Make the respawning of the girl and the message on the tablet only one time not continuously. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Respawn the girl1 from the right side to the left side then start its animation with girl2 and girl3, and after the message in the tablet is finished and tablet is removed remove the girl to start the playing. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Make the tablet bigger, use the size in the asset and do more lines to fit inside the tablet. change text color to ff0080
User prompt
Add Tablet asset to the game, respawn it from the middle of the screen after respawning the girl and let it in the center. add text animation with electronic characters in the tablet spacing from sides by 100px "Hi!I'm the high commander: We shall begin a war with the enemies, nothing will stand in our ways, we will destroy them and we prove our supremacy, we will not rest until victory is ours!". ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Respawn it one time and hold its position to the left side of the screen and with big size i made a size already 500x500px!
User prompt
Add Girl1 asset respawn it from the right side to the left side of the screen and make the animation with it like this Girl1 is the original Girl frame always back to it show frame Girl2 then back to Girl1, Girl3 then back to Girl1 to look like she's speaking. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
move it a bit to the right edge of the screen
User prompt
Add the settingbutton in the game after game start to configure the setting when in gameplay make its position above the waves blocks indicators on the right beside the right edge of the screen.
User prompt
Rest the position of the lightbutton to be inside the lightbar from inside beside its left edge.
User prompt
Reduce spacing between light icon and light bar to be 50px
User prompt
Make the limit distance for the lightbutton is between edges of the bar horizontal from the inside.
User prompt
Make the limit distance for the lightbutton is between edges of the bar horizontal from the inside.
User prompt
Make the light setting up by 100px
User prompt
Do some space between Lighticon and the bar
User prompt
Create Light setting with lightIcon and button and a bar assets in settingbackground to increase or reduce light of the game screen ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Create an asset for it so i can change its image
User prompt
Make the close button for the settingbackground with an asset ↪💡 Consider importing and using the following plugins: @upit/tween.v1
Code edit (1 edits merged)
Please save this source code
User prompt
Respawn ship1 and ship2 randomly each 20sec and ship3 each 40sec
User prompt
Make the ship1 and ship2 respawn each 25sec and ship3 each 30sec
User prompt
Make the ship1 and ship2 respawn each 15sec and ship3 each 20sec
User prompt
fix the dragging animation the button is not moving!
User prompt
make hold on the button to drag and stop it when i release it, don't let it move a way auto when i click on it
/**** 
* Plugins
****/ 
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/**** 
* Classes
****/ 
var Bullet = Container.expand(function (startX, startY, targetEnemy, damage, speed) {
	var self = Container.call(this);
	self.targetEnemy = targetEnemy;
	self.damage = damage || 10;
	self.speed = speed || 5;
	self.x = startX;
	self.y = startY;
	var bulletGraphics = self.attachAsset('bullet', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	self.update = function () {
		if (!self.targetEnemy || !self.targetEnemy.parent) {
			self.destroy();
			return;
		}
		// Check if bullet has exceeded its source tower's range (for all tower types)
		if (self.sourceTower) {
			var towerDx = self.x - self.sourceTower.x;
			var towerDy = self.y - self.sourceTower.y;
			var distanceFromTower = Math.sqrt(towerDx * towerDx + towerDy * towerDy);
			if (distanceFromTower > self.sourceTower.getRange()) {
				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 with resistance scaling
			var actualDamage = self.damage;
			if (self.targetEnemy.damageResistance) {
				actualDamage = self.damage * self.targetEnemy.damageResistance;
			}
			self.targetEnemy.health -= actualDamage;
			if (self.targetEnemy.health <= 0) {
				self.targetEnemy.health = 0;
			} else {
				// Update enemy health bar using stored original width
				var healthPercentage = self.targetEnemy.health / self.targetEnemy.maxHealth;
				self.targetEnemy.healthBar.width = self.targetEnemy.healthBarOriginalWidth * healthPercentage;
			}
			// Apply special effects based on bullet type
			if (self.type === 'cannon') {
				// Create fire explosion effect for cannon bullets
				var explosionEffect = new Container();
				explosionEffect.x = self.targetEnemy.x;
				explosionEffect.y = self.targetEnemy.y;
				game.addChild(explosionEffect);
				// Create multiple fire particles for the explosion
				for (var fireParticle = 0; fireParticle < 8; fireParticle++) {
					var particle = explosionEffect.attachAsset('rangeCircle', {
						anchorX: 0.5,
						anchorY: 0.5
					});
					particle.width = particle.height = 20 + Math.random() * 30;
					particle.tint = fireParticle % 2 === 0 ? 0xFF4500 : 0xFF8C00; // Orange-red fire colors
					particle.alpha = 0.8;
					particle.blendMode = 1; // Add blend mode for fire effect
					particle.x = (Math.random() - 0.5) * 40;
					particle.y = (Math.random() - 0.5) * 40;
					particle.scaleX = 0.1;
					particle.scaleY = 0.1;
					// Animate each particle exploding outward
					tween(particle, {
						scaleX: 1.5,
						scaleY: 1.5,
						x: particle.x + (Math.random() - 0.5) * 60,
						y: particle.y + (Math.random() - 0.5) * 60,
						alpha: 0
					}, {
						duration: 300 + Math.random() * 200,
						easing: tween.easeOut
					});
				}
				// Create central explosion flash
				var centralFlash = explosionEffect.attachAsset('rangeCircle', {
					anchorX: 0.5,
					anchorY: 0.5
				});
				centralFlash.width = centralFlash.height = 80;
				centralFlash.tint = 0xFFFFAA; // Bright yellow-white center
				centralFlash.alpha = 1;
				centralFlash.blendMode = 1;
				centralFlash.scaleX = 0.1;
				centralFlash.scaleY = 0.1;
				// Animate central flash
				tween(centralFlash, {
					scaleX: 2,
					scaleY: 2,
					alpha: 0
				}, {
					duration: 400,
					easing: tween.easeOut,
					onFinish: function onFinish() {
						explosionEffect.destroy();
					}
				});
			} else if (self.type === 'Rocket') {
				// Create visual splash effect
				var splashEffect = new EffectIndicator(self.targetEnemy.x, self.targetEnemy.y, 'splash');
				game.addChild(splashEffect);
				// Splash damage to nearby enemies - 200x200 area means 100 pixel radius
				var splashRadius = 100;
				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 {
								// Update enemy health bar using stored original width
								var healthPercentage = otherEnemy.health / otherEnemy.maxHealth;
								otherEnemy.healthBar.width = otherEnemy.healthBarOriginalWidth * healthPercentage;
							}
						}
					}
				}
			} else if (self.type === 'gumbomb') {
				// Prevent gum effect on immune enemies
				if (!self.targetEnemy.isImmune) {
					// Create visual gumbomb explosion effect
					var gumEffect = new EffectIndicator(self.targetEnemy.x, self.targetEnemy.y, 'gumbomb');
					game.addChild(gumEffect);
					// Apply gum stuck effect - enemies become completely immobilized
					if (!self.targetEnemy.gumStuck) {
						self.targetEnemy.originalSpeed = self.targetEnemy.speed;
						self.targetEnemy.speed = 0; // Completely stuck
						self.targetEnemy.gumStuck = true;
						self.targetEnemy.gumDuration = 240; // 4 seconds at 60 FPS
						// Create purple gum effect beneath the enemy
						var gumStuckGraphics = LK.getAsset('gumStuckEffect', {
							anchorX: 0.5,
							anchorY: 0.5
						});
						gumStuckGraphics.x = self.targetEnemy.x;
						gumStuckGraphics.y = self.targetEnemy.y + self.targetEnemy.children[0].height / 2;
						gumStuckGraphics.alpha = 0;
						gumStuckGraphics.scaleX = 0.1;
						gumStuckGraphics.scaleY = 0.1;
						self.targetEnemy.gumStuckEffect = gumStuckGraphics;
						enemyLayerBottom.addChildAt(gumStuckGraphics, 0);
						// Explosion animation - scale up and fade in quickly
						tween(gumStuckGraphics, {
							alpha: 0.7,
							scaleX: 1.2,
							scaleY: 1.2
						}, {
							duration: 200,
							easing: tween.easeOut,
							onFinish: function onFinish() {
								// Settle to normal size
								tween(gumStuckGraphics, {
									scaleX: 1,
									scaleY: 1
								}, {
									duration: 300,
									easing: tween.elasticOut
								});
								// Set up tween to destroy gum area after 4 seconds
								tween(gumStuckGraphics, {
									alpha: 0
								}, {
									duration: 4000,
									onFinish: function onFinish() {
										if (gumStuckGraphics && gumStuckGraphics.parent) {
											gumStuckGraphics.parent.removeChild(gumStuckGraphics);
										}
									}
								});
							}
						});
					} else {
						self.targetEnemy.gumDuration = 240; // Reset duration to 4 seconds
					}
				}
			} else if (self.type === 'Toxin') {
				// Prevent toxin effect on immune enemies
				if (!self.targetEnemy.isImmune) {
					// Create ToxinBomb explosion effect using Toxin asset
					var explosionEffect = new Container();
					explosionEffect.x = self.targetEnemy.x;
					explosionEffect.y = self.targetEnemy.y;
					game.addChild(explosionEffect);
					// Create the main toxin explosion using Toxin asset
					var toxinExplosion = explosionEffect.attachAsset('Toxin', {
						anchorX: 0.5,
						anchorY: 0.5
					});
					toxinExplosion.width = toxinExplosion.height = 150;
					toxinExplosion.tint = 0x00FFAA; // Green toxin color
					toxinExplosion.alpha = 0.9;
					toxinExplosion.scaleX = 0.1;
					toxinExplosion.scaleY = 0.1;
					// Animate the toxin explosion expanding and fading
					tween(toxinExplosion, {
						scaleX: 1.8,
						scaleY: 1.8,
						alpha: 0.3
					}, {
						duration: 400,
						easing: tween.easeOut
					});
					// Create additional smaller toxin particles around the main explosion
					for (var toxinParticle = 0; toxinParticle < 6; toxinParticle++) {
						var particle = explosionEffect.attachAsset('Toxin', {
							anchorX: 0.5,
							anchorY: 0.5
						});
						particle.width = particle.height = 40 + Math.random() * 30;
						particle.tint = 0x00FFAA; // Consistent green toxin color
						particle.alpha = 0.7;
						particle.x = (Math.random() - 0.5) * 80;
						particle.y = (Math.random() - 0.5) * 80;
						particle.scaleX = 0.1;
						particle.scaleY = 0.1;
						// Animate each particle expanding outward
						tween(particle, {
							scaleX: 1.2,
							scaleY: 1.2,
							x: particle.x + (Math.random() - 0.5) * 100,
							y: particle.y + (Math.random() - 0.5) * 100,
							alpha: 0
						}, {
							duration: 500 + Math.random() * 200,
							easing: tween.easeOut
						});
					}
					// Destroy the explosion effect after animation completes
					tween(explosionEffect, {
						alpha: 1
					}, {
						duration: 600,
						onFinish: function onFinish() {
							explosionEffect.destroy();
						}
					});
					// Apply toxin 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
				}
			}
			// Flame tower does not use bullets, so destroy any 'flame' type bullets immediately
			if (self.type === 'flame') {
				self.destroy();
			} else {
				self.destroy();
			}
		} else {
			var angle = Math.atan2(dy, dx);
			// Rotate bullet to face movement direction
			if (self.children[0]) {
				// Check if this is a RifleBullet which needs rotation offset
				var rotationOffset = 0;
				if (self.type === 'rifle') {
					rotationOffset = Math.PI / 2; // Compensate for upward-pointing asset
				}
				var targetAngle = angle + rotationOffset;
				if (self.children[0].targetRotation === undefined) {
					self.children[0].targetRotation = targetAngle;
					self.children[0].rotation = targetAngle;
				} else {
					if (Math.abs(targetAngle - self.children[0].targetRotation) > 0.05) {
						tween.stop(self.children[0], {
							rotation: true
						});
						// Calculate the shortest angle to rotate
						var currentRotation = self.children[0].rotation;
						var angleDiff = targetAngle - 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;
						}
						self.children[0].targetRotation = targetAngle;
						tween(self.children[0], {
							rotation: currentRotation + angleDiff
						}, {
							duration: 100,
							easing: tween.easeOut
						});
					}
				}
			}
			self.x += Math.cos(angle) * self.speed;
			self.y += Math.sin(angle) * self.speed;
		}
	};
	return self;
});
var CrystalIndicator = 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 crystalText = new Text2("+" + value, {
		size: 45,
		fill: 0xFF69B4,
		weight: 800
	});
	crystalText.anchor.set(0.5, 0.5);
	self.addChild(crystalText);
	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 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 = [];
	// Numbers removed - using animated orange lines only
	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();
						cellGraphics.tint = 0x880000;
						return;
					}
					var towerInRangeHighlight = false;
					if (selectedTower && data.towersInRange && data.towersInRange.indexOf(selectedTower) !== -1) {
						towerInRangeHighlight = true;
						cellGraphics.tint = 0xe14de9; // pink color with transparency
						cellGraphics.alpha = 0.5; // Almost max transparent
					} else {
						cellGraphics.tint = 0x4db3e9; // Sky blue color with transparency
						cellGraphics.alpha = 0.5; // Almost max transparent
					}
					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;
						// Change lines pointing up to point down
						if (oy < 0) {
							oy = -oy; // Flip the y direction for upward pointing lines
						}
						var angle = Math.atan2(oy, ox);
						if (!debugArrows[a]) {
							// Create arrow container
							debugArrows[a] = new Container();
							// Create arrow shaft using shape asset
							var arrowShaft = LK.getAsset('cell', {
								anchorX: 0,
								anchorY: 0.5
							});
							arrowShaft.width = 40;
							arrowShaft.height = 8;
							arrowShaft.tint = 0xff0080;
							debugArrows[a].addChild(arrowShaft);
							// Create arrow head using shape asset
							var arrowHead = LK.getAsset('cell', {
								anchorX: 0,
								anchorY: 0.5
							});
							arrowHead.width = 20;
							arrowHead.height = 16;
							arrowHead.tint = 0xff0080;
							arrowHead.x = 35; // Position at end of shaft
							arrowHead.y = 0;
							debugArrows[a].addChild(arrowHead);
							self.addChildAt(debugArrows[a], 1);
						}
						// Fix rotation calculation - ensure correct direction mapping
						debugArrows[a].rotation = angle;
						// Position the line at center of cell for better visibility
						debugArrows[a].x = 0;
						debugArrows[a].y = 0;
						// Animate the violet arrow with flowing effect
						var arrow = debugArrows[a];
						arrow.animationPhase = (arrow.animationPhase || 0) + 0.1;
						// Create flowing effect by animating scale - maintain consistent base scale
						var flowScale = 1.0 + 0.3 * Math.sin(arrow.animationPhase * 1.2);
						arrow.scaleX = flowScale;
						// Reset scale Y to ensure proper aspect ratio
						arrow.scaleY = 1.0;
					}
					break;
				}
			case 1:
				{
					self.removeArrows();
					// Replace the cell with DebugWall image asset
					if (cellGraphics) {
						self.removeChild(cellGraphics);
					}
					cellGraphics = self.attachAsset('DebugWall', {
						anchorX: 0.5,
						anchorY: 0.5
					});
					cellGraphics.alpha = 0.9; // Make wall more visible
					break;
				}
			case 3:
				{
					self.removeArrows();
					cellGraphics.tint = 0xff0080; // pink
					cellGraphics.alpha = 5;
					break;
				}
		}
	};
});
// 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 'gumbomb':
			effectGraphics.tint = 0x9900FF;
			effectGraphics.width = effectGraphics.height = CELL_SIZE * 2; // Larger explosion effect
			break;
		case 'toxin':
			effectGraphics.tint = 0x00FFAA;
			effectGraphics.width = effectGraphics.height = CELL_SIZE;
			break;
		case 'flame':
			effectGraphics.tint = 0xFF8800; // Mix of yellow, orange, red
			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 - increased health
	switch (self.type) {
		case 'fast':
			self.speed *= 2; // Twice as fast
			self.maxHealth = 45; // Increased from 30
			break;
		case 'immune':
			self.isImmune = true;
			self.maxHealth = 40; // Increased from 25
			break;
		case 'flying':
			self.isFlying = true;
			self.maxHealth = 40; // Increased from 25
			break;
		case 'swarm':
			self.maxHealth = 25; // Increased from 15
			break;
		case 'normal':
		default:
			self.maxHealth = 35; // Increased from 20
			break;
	}
	if (currentWave % 10 === 0 && currentWave > 0 && type !== 'swarm') {
		self.isBoss = true;
		// Boss enemies have 8x health and are larger
		self.maxHealth *= 8;
		// Slightly slower speed for bosses but not as much
		self.speed = self.speed * 0.8;
	}
	// Apply wave-based difficulty scaling every 5 waves
	var difficultyWaves = Math.floor((currentWave - 1) / 5);
	if (difficultyWaves > 0) {
		// Increase health by 75% every 5 waves (was 50%)
		var healthMultiplier = 1 + difficultyWaves * 0.75;
		self.maxHealth = Math.round(self.maxHealth * healthMultiplier);
		// Increase speed by 30% every 5 waves (was 20%)
		var speedMultiplier = 1 + difficultyWaves * 0.3;
		self.speed = self.speed * speedMultiplier;
		// Add damage resistance - enemies take 10% less damage every 5 waves
		self.damageResistance = 1 - Math.min(0.5, difficultyWaves * 0.1); // Cap at 50% resistance
	}
	self.health = self.maxHealth;
	// Get appropriate asset for this enemy type
	var assetId = 'enemy';
	if (self.type !== 'normal') {
		assetId = 'enemy_' + self.type;
	}
	// For flying enemies, use different assets based on wave progression
	if (self.type === 'flying') {
		// Use different flying enemy assets based on wave number
		var waveGroup = Math.floor((currentWave - 1) / 10); // Every 10 waves change flying enemy type
		var flyingAssets = ['Firo', 'Eye', 'Electro'];
		var assetIndex = waveGroup % flyingAssets.length;
		assetId = flyingAssets[assetIndex];
	} else {
		// For ground enemies, use different Cyborg assets based on wave number
		var waveGroup = Math.floor((currentWave - 1) / 10); // Every 10 waves change ground enemy type
		var groundAssets = ['Cyborg', 'Cyborg2', 'Cyborg3'];
		var assetIndex = waveGroup % groundAssets.length;
		if (self.type === 'normal') {
			assetId = groundAssets[assetIndex];
		} else if (self.type === 'fast') {
			// For fast enemies, use Robot1, Spider1, and Spider2 assets based on wave progression
			var waveGroup = Math.floor((currentWave - 1) / 10); // Every 10 waves change fast enemy type
			var fastAssets = ['Robot1', 'Spider1', 'Spider2'];
			var assetIndex = waveGroup % fastAssets.length;
			assetId = fastAssets[assetIndex];
		} else if (self.type === 'immune') {
			// For immune enemies, use Big_Robot1, Big_Robot2, and Big_Robot3 assets based on wave progression
			var waveGroup = Math.floor((currentWave - 1) / 10); // Every 10 waves change immune enemy type
			var immuneAssets = ['Big_Robot1', 'Big_Robot2', 'Big_Robot3'];
			var assetIndex = waveGroup % immuneAssets.length;
			assetId = immuneAssets[assetIndex];
		} else if (self.type === 'swarm') {
			// For swarm enemies, use CyberSnake1, CyberSnake2, and CyberSnake3 assets based on wave progression
			var waveGroup = Math.floor((currentWave - 1) / 10); // Every 10 waves change swarm enemy type
			var swarmAssets = ['CyberSnake1', 'CyberSnake2', 'CyberSnake3'];
			var assetIndex = waveGroup % swarmAssets.length;
			assetId = swarmAssets[assetIndex];
		} else {
			// For other non-normal ground enemies, still use the original prefixed system but could be enhanced later
			assetId = 'enemy_' + self.type;
		}
	}
	var enemyGraphics = self.attachAsset(assetId, {
		anchorX: 0.5,
		anchorY: 0.5
	});
	// Scale up boss enemies
	if (self.isBoss) {
		enemyGraphics.scaleX = 1.8;
		enemyGraphics.scaleY = 1.8;
	}
	// Fall back to regular enemy asset if specific type asset not found
	// Apply tint to differentiate enemy types
	/*switch (self.type) {
		case 'fast':
			enemyGraphics.tint = 0x00AAFF; // Blue for fast enemies
			break;
		case 'immune':
			enemyGraphics.tint = 0xAA0000; // Red for immune enemies
			break;
		case 'flying':
			enemyGraphics.tint = 0xFFFF00; // Yellow for flying enemies
			break;
		case 'swarm':
			enemyGraphics.tint = 0xFF00FF; // Pink for swarm enemies
			break;
	}*/
	// Create shadow for flying enemies
	if (self.isFlying) {
		// Create a shadow container that will be added to the shadow layer
		self.shadow = new Container();
		// Clone the enemy graphics for the shadow - use the same asset as the main enemy
		var shadowAssetId = assetId;
		// For flying enemies, make sure we use the correct asset for the shadow
		if (self.type === 'flying') {
			var waveGroup = Math.floor((currentWave - 1) / 10);
			var flyingAssets = ['Firo', 'Eye', 'Electro'];
			var assetIndex = waveGroup % flyingAssets.length;
			shadowAssetId = flyingAssets[assetIndex];
		}
		var shadowGraphics = self.shadow.attachAsset(shadowAssetId, {
			anchorX: 0.5,
			anchorY: 0.5
		});
		// Apply shadow effect
		shadowGraphics.tint = 0x000000; // Black shadow
		shadowGraphics.alpha = 0.3; // 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('EnemyHealthBarOutline', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	var healthBar = self.attachAsset('Enemyhealthbar', {
		anchorX: 0,
		anchorY: 0.5
	});
	healthBarOutline.y = healthBar.y = -enemyGraphics.height / 2 - 10;
	healthBar.x = -healthBar.width / 2;
	self.healthBar = healthBar;
	self.healthBarOriginalWidth = healthBar.width; // Store original width
	self.update = function () {
		if (self.health <= 0) {
			self.health = 0;
			self.healthBar.width = 0;
		} else {
			// Update health bar width based on current health
			var healthPercentage = self.health / self.maxHealth;
			self.healthBar.width = self.healthBarOriginalWidth * healthPercentage;
		}
		// Handle slow effect
		if (self.isImmune) {
			// Immune enemies cannot be slowed, gum stuck, or poisoned, clear any such effects
			self.slowed = false;
			self.slowEffect = false;
			self.gumStuck = false;
			if (self.gumStuckEffect && self.gumStuckEffect.parent) {
				game.removeChild(self.gumStuckEffect);
			}
			self.gumStuckEffect = null;
			self.poisoned = false;
			self.poisonEffect = false;
			// Reset speed to original if needed
			if (self.originalSpeed !== undefined) {
				self.speed = self.originalSpeed;
			}
		} else {
			// Handle gum stuck effect
			if (self.gumStuck) {
				// Visual indication of gum stuck status
				if (!self.gumStuckEffect) {
					self.gumStuckEffect = true;
				}
				self.gumDuration--;
				if (self.gumDuration <= 0) {
					self.speed = self.originalSpeed;
					self.gumStuck = false;
					self.gumStuckEffect = false;
					// Remove gum effect visual with fade out animation
					if (self.gumStuckEffect && self.gumStuckEffect.parent) {
						tween(self.gumStuckEffect, {
							alpha: 0,
							scaleX: 1.5,
							scaleY: 1.5
						}, {
							duration: 300,
							easing: tween.easeOut,
							onFinish: function onFinish() {
								if (self.gumStuckEffect && self.gumStuckEffect.parent) {
									game.removeChild(self.gumStuckEffect);
								}
								self.gumStuckEffect = null;
							}
						});
					} else {
						self.gumStuckEffect = null;
					}
					// Only reset tint if not poisoned
					if (!self.poisoned) {
						enemyGraphics.tint = 0xFFFFFF; // Reset tint
					}
				} else {
					// Update gum effect position to follow enemy
					if (self.gumStuckEffect && self.gumStuckEffect.parent) {
						self.gumStuckEffect.x = self.x;
						self.gumStuckEffect.y = self.y + enemyGraphics.height / 2;
					}
				}
			}
			// 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) {
					var actualPoisonDamage = self.poisonDamage;
					if (self.damageResistance) {
						actualPoisonDamage = self.poisonDamage * self.damageResistance;
					}
					self.health -= actualPoisonDamage;
					if (self.health <= 0) {
						self.health = 0;
					}
					// Update enemy health bar using stored original width
					var healthPercentage = self.health / self.maxHealth;
					self.healthBar.width = self.healthBarOriginalWidth * healthPercentage;
				}
				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 || self.gumStuck)) {
			// Combine poison (0x00FFAA) and slow/gum (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 || self.gumStuck) {
			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 = healthBar.y = -enemyGraphics.height / 2 - 10;
	};
	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
	*/
	for (var i = 0; i < gridWidth; i++) {
		for (var j = 0; j < gridHeight; j++) {
			var cell = self.cells[i][j];
			var cellType = i === 0 || i === gridWidth - 1 || j <= 4 || j >= gridHeight - 4 ? 1 : 0;
			if (i > 11 - 3 && i <= 11 + 3) {
				if (j === 0) {
					cellType = 2;
					self.spawns.push(cell);
				} else if (j <= 4) {
					cellType = 0;
				} else if (j === gridHeight - 1) {
					cellType = 3;
					self.goals.push(cell);
				} else if (j >= gridHeight - 4) {
					cellType = 0;
				}
			}
			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 = [];
			if (j > 3 && j <= gridHeight - 4) {
				var debugCell = new DebugCell();
				self.addChild(debugCell);
				debugCell.cell = cell;
				debugCell.x = i * CELL_SIZE;
				debugCell.y = j * CELL_SIZE;
				cell.debugCell = debugCell;
			}
		}
	}
	self.getCell = function (x, y) {
		return self.cells[x] && self.cells[x][y];
	};
	self.pathFind = function () {
		var before = new Date().getTime();
		var toProcess = self.goals.concat([]);
		maxScore = 0;
		pathId += 1;
		for (var a = 0; a < toProcess.length; a++) {
			toProcess[a].pathId = pathId;
		}
		function processNode(node, targetValue, targetNode) {
			if (node && node.type != 1) {
				if (node.pathId < pathId || targetValue < node.score) {
					node.targets = [targetNode];
				} else if (node.pathId == pathId && targetValue == node.score) {
					node.targets.push(targetNode);
				}
				if (node.pathId < pathId || targetValue < node.score) {
					node.score = targetValue;
					if (node.pathId != pathId) {
						toProcess.push(node);
					}
					node.pathId = pathId;
					if (targetValue > maxScore) {
						maxScore = targetValue;
					}
				}
			}
		}
		while (toProcess.length) {
			var nodes = toProcess;
			toProcess = [];
			for (var a = 0; a < nodes.length; a++) {
				var node = nodes[a];
				var targetScore = node.score + 14142;
				if (node.up && node.left && node.up.type != 1 && node.left.type != 1) {
					processNode(node.upLeft, targetScore, node);
				}
				if (node.up && node.right && node.up.type != 1 && node.right.type != 1) {
					processNode(node.upRight, targetScore, node);
				}
				if (node.down && node.right && node.down.type != 1 && node.right.type != 1) {
					processNode(node.downRight, targetScore, node);
				}
				if (node.down && node.left && node.down.type != 1 && node.left.type != 1) {
					processNode(node.downLeft, targetScore, node);
				}
				targetScore = node.score + 10000;
				processNode(node.up, targetScore, node);
				processNode(node.right, targetScore, node);
				processNode(node.down, targetScore, node);
				processNode(node.left, targetScore, node);
			}
		}
		for (var a = 0; a < self.spawns.length; a++) {
			if (self.spawns[a].pathId != pathId) {
				console.warn("Spawn blocked");
				return true;
			}
		}
		for (var a = 0; a < enemies.length; a++) {
			var enemy = enemies[a];
			// Skip enemies that haven't entered the viewable area yet
			if (enemy.currentCellY < 4) {
				continue;
			}
			// Skip flying enemies from path check as they can fly over obstacles
			if (enemy.isFlying) {
				continue;
			}
			var target = self.getCell(enemy.cellX, enemy.cellY);
			if (enemy.currentTarget) {
				if (enemy.currentTarget.pathId != pathId) {
					if (!target || target.pathId != pathId) {
						console.warn("Enemy blocked 1 ");
						return true;
					}
				}
			} else if (!target || target.pathId != pathId) {
				console.warn("Enemy blocked 2");
				return true;
			}
		}
		console.log("Speed", new Date().getTime() - before);
	};
	self.renderDebug = function () {
		for (var i = 0; i < gridWidth; i++) {
			for (var j = 0; j < gridHeight; j++) {
				var debugCell = self.cells[i][j].debugCell;
				if (debugCell) {
					debugCell.render(self.cells[i][j]);
				}
			}
		}
	};
	self.updateEnemy = function (enemy) {
		var cell = grid.getCell(enemy.cellX, enemy.cellY);
		if (cell.type == 3) {
			return true;
		}
		if (enemy.isFlying && enemy.shadow) {
			enemy.shadow.x = enemy.x + 20; // Match enemy x-position + offset
			enemy.shadow.y = enemy.y + 20; // Match enemy y-position + offset
			// Match shadow rotation with enemy rotation
			if (enemy.children[0] && enemy.shadow.children[0]) {
				enemy.shadow.children[0].rotation = enemy.children[0].rotation;
			}
		}
		// Check if the enemy has reached the entry area (y position is at least 5)
		var hasReachedEntryArea = enemy.currentCellY >= 4;
		// If enemy hasn't reached the entry area yet, just move down vertically
		if (!hasReachedEntryArea) {
			// Move directly downward
			enemy.currentCellY += enemy.speed;
			// Rotate enemy graphic to face downward (PI/2 radians = 90 degrees)
			var angle = Math.PI / 2;
			if (enemy.children[0] && enemy.children[0].targetRotation === undefined) {
				enemy.children[0].targetRotation = angle;
				enemy.children[0].rotation = angle;
			} else if (enemy.children[0]) {
				if (Math.abs(angle - enemy.children[0].targetRotation) > 0.05) {
					tween.stop(enemy.children[0], {
						rotation: true
					});
					// Calculate the shortest angle to rotate
					var currentRotation = enemy.children[0].rotation;
					var angleDiff = angle - currentRotation;
					// Normalize angle difference to -PI to PI range for shortest path
					while (angleDiff > Math.PI) {
						angleDiff -= Math.PI * 2;
					}
					while (angleDiff < -Math.PI) {
						angleDiff += Math.PI * 2;
					}
					// Set target rotation and animate to it
					enemy.children[0].targetRotation = angle;
					tween(enemy.children[0], {
						rotation: currentRotation + angleDiff
					}, {
						duration: 250,
						easing: tween.easeOut
					});
				}
			}
			// Update enemy's position
			enemy.x = grid.x + enemy.currentCellX * CELL_SIZE;
			enemy.y = grid.y + enemy.currentCellY * CELL_SIZE;
			// If enemy has now reached the entry area, update cell coordinates
			if (enemy.currentCellY >= 4) {
				enemy.cellX = Math.round(enemy.currentCellX);
				enemy.cellY = Math.round(enemy.currentCellY);
			}
			return false;
		}
		// After reaching entry area, handle flying enemies differently
		if (enemy.isFlying) {
			// Flying enemies head straight to the closest goal
			if (!enemy.flyingTarget) {
				// Set flying target to the closest goal
				enemy.flyingTarget = self.goals[0];
				// Find closest goal if there are multiple
				if (self.goals.length > 1) {
					var closestDist = Infinity;
					for (var i = 0; i < self.goals.length; i++) {
						var goal = self.goals[i];
						var dx = goal.x - enemy.cellX;
						var dy = goal.y - enemy.cellY;
						var dist = dx * dx + dy * dy;
						if (dist < closestDist) {
							closestDist = dist;
							enemy.flyingTarget = goal;
						}
					}
				}
			}
			// Move directly toward the goal
			var ox = enemy.flyingTarget.x - enemy.currentCellX;
			var oy = enemy.flyingTarget.y - enemy.currentCellY;
			var dist = Math.sqrt(ox * ox + oy * oy);
			if (dist < enemy.speed) {
				// Reached the goal
				return true;
			}
			var angle = Math.atan2(oy, ox);
			// Rotate enemy graphic to match movement direction
			if (enemy.children[0] && enemy.children[0].targetRotation === undefined) {
				enemy.children[0].targetRotation = angle;
				enemy.children[0].rotation = angle;
			} else if (enemy.children[0]) {
				if (Math.abs(angle - enemy.children[0].targetRotation) > 0.05) {
					tween.stop(enemy.children[0], {
						rotation: true
					});
					// Calculate the shortest angle to rotate
					var currentRotation = enemy.children[0].rotation;
					var angleDiff = angle - currentRotation;
					// Normalize angle difference to -PI to PI range for shortest path
					while (angleDiff > Math.PI) {
						angleDiff -= Math.PI * 2;
					}
					while (angleDiff < -Math.PI) {
						angleDiff += Math.PI * 2;
					}
					// Set target rotation and animate to it
					enemy.children[0].targetRotation = angle;
					tween(enemy.children[0], {
						rotation: currentRotation + angleDiff
					}, {
						duration: 250,
						easing: tween.easeOut
					});
				}
			}
			// Update the cell position to track where the flying enemy is
			enemy.cellX = Math.round(enemy.currentCellX);
			enemy.cellY = Math.round(enemy.currentCellY);
			enemy.currentCellX += Math.cos(angle) * enemy.speed;
			enemy.currentCellY += Math.sin(angle) * enemy.speed;
			enemy.x = grid.x + enemy.currentCellX * CELL_SIZE;
			enemy.y = grid.y + enemy.currentCellY * CELL_SIZE;
			// Update shadow position if this is a flying enemy
			return false;
		}
		// Handle normal pathfinding enemies
		if (!enemy.currentTarget) {
			enemy.currentTarget = cell.targets[0];
		}
		if (enemy.currentTarget) {
			if (cell.score < enemy.currentTarget.score) {
				enemy.currentTarget = cell;
			}
			var ox = enemy.currentTarget.x - enemy.currentCellX;
			var oy = enemy.currentTarget.y - enemy.currentCellY;
			var dist = Math.sqrt(ox * ox + oy * oy);
			if (dist < enemy.speed) {
				enemy.cellX = Math.round(enemy.currentCellX);
				enemy.cellY = Math.round(enemy.currentCellY);
				enemy.currentTarget = undefined;
				return;
			}
			var angle = Math.atan2(oy, ox);
			enemy.currentCellX += Math.cos(angle) * enemy.speed;
			enemy.currentCellY += Math.sin(angle) * enemy.speed;
		}
		enemy.x = grid.x + enemy.currentCellX * CELL_SIZE;
		enemy.y = grid.y + enemy.currentCellY * CELL_SIZE;
	};
});
var NextWaveButton = Container.expand(function () {
	var self = Container.call(this);
	var buttonBackground = self.attachAsset('notification', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	buttonBackground.width = 300;
	buttonBackground.height = 100;
	buttonBackground.tint = 0x0088FF;
	var buttonText = new Text2("Next Wave", {
		size: 50,
		fill: 0xFFFFFF,
		weight: 800
	});
	buttonText.anchor.set(0.5, 0.5);
	self.addChild(buttonText);
	self.enabled = false;
	self.visible = false;
	self.update = function () {
		if (waveIndicator && waveIndicator.gameStarted && currentWave < totalWaves) {
			self.enabled = true;
			self.visible = true;
			buttonBackground.tint = 0x0088FF;
			self.alpha = 1;
		} else {
			self.enabled = false;
			self.visible = false;
			buttonBackground.tint = 0x888888;
			self.alpha = 0.7;
		}
	};
	self.down = function () {
		if (!self.enabled) {
			return;
		}
		if (waveIndicator.gameStarted && currentWave < totalWaves) {
			currentWave++; // Increment to the next wave directly
			waveTimer = 0; // Reset wave timer
			waveInProgress = true;
			waveSpawned = false;
			// Get the type of the current wave (which is now the next wave)
			var waveType = waveIndicator.getWaveTypeName(currentWave);
			var enemyCount = waveIndicator.getEnemyCount(currentWave);
			var notification = game.addChild(new Notification("Wave " + currentWave + " (" + waveType + " - " + enemyCount + " enemies) activated!"));
			notification.x = 2048 / 2;
			notification.y = 2732 / 2;
		}
	};
	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: 65,
		fill: 0xff0080,
		weight: 800
	});
	notificationText.anchor.set(0.5, 0.5);
	notificationGraphics.width = notificationText.width + 60;
	notificationGraphics.height = notificationText.height + 40;
	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 Ship = Container.expand(function (type) {
	var self = Container.call(this);
	self.type = type || 1; // Ship type 1, 2, or 3
	self.speed = 2 + Math.random() * 2; // Random speed between 2-4
	self.direction = Math.random() < 0.5 ? 1 : -1; // 1 for left to right, -1 for right to left
	// Select ship asset based on type
	var shipAsset = 'Ship' + self.type;
	var shipGraphics = self.attachAsset(shipAsset, {
		anchorX: 0.5,
		anchorY: 0.5
	});
	// Play ship sound based on ship type
	var shipSound = 'Ship' + self.type + 'sound';
	LK.getSound(shipSound).play();
	// Flip ship if moving right to left
	if (self.direction === -1) {
		shipGraphics.scaleX = -1;
	}
	// Calculate game field boundaries
	var gridLeft = 150; // grid.x value
	var gridWidth = 24 * 76; // 24 cells * CELL_SIZE
	var gridRight = gridLeft + gridWidth; // 150 + 1824 = 1974
	// Calculate vertical boundaries based on grid position
	var gridTop = 200 - 76 * 4; // grid.y value (200 - CELL_SIZE * 4)
	var gridHeight = (29 + 6) * 76; // (29 + 6) cells * CELL_SIZE
	var gridBottom = gridTop + gridHeight;
	// Random Y position within the game field boundaries
	self.y = gridTop + Math.random() * gridHeight;
	// Set starting X position based on direction, spawning from field edges
	if (self.direction === 1) {
		// Moving left to right, start from left edge of field
		self.x = gridLeft - shipGraphics.width / 2;
	} else {
		// Moving right to left, start from right edge of field
		self.x = gridRight + shipGraphics.width / 2;
	}
	self.update = function () {
		self.x += self.speed * self.direction;
		// Check if ship has moved off the opposite field edge
		if (self.direction === 1 && self.x > gridRight + shipGraphics.width) {
			self.destroy();
		} else if (self.direction === -1 && self.x < gridLeft - shipGraphics.width) {
			self.destroy();
		}
		// Initialize sound timer if not set
		if (self.soundTimer === undefined) {
			self.soundTimer = 0;
		}
		// Increment sound timer
		self.soundTimer++;
		// Play ship sound every 120 frames (2 seconds at 60 FPS)
		if (self.soundTimer % 120 === 0) {
			var shipSound = 'Ship' + self.type + 'sound';
			LK.getSound(shipSound).play();
		}
	};
	return self;
});
var SourceTower = Container.expand(function (towerType) {
	var self = Container.call(this);
	self.towerType = towerType || 'cannon';
	// Use specific tower asset based on type
	var towerAssetId = 'CannonTower'; // default
	switch (self.towerType) {
		case 'rifle':
			towerAssetId = 'RifleTower';
			break;
		case 'flame':
			towerAssetId = 'FlameTower';
			break;
		case 'Rocket':
			towerAssetId = 'RocketTower';
			break;
		case 'gumbomb':
			towerAssetId = 'GumBombTower';
			break;
		case 'Toxin':
			towerAssetId = 'ToxinTower';
			break;
		case 'cannon':
		default:
			towerAssetId = 'CannonTower';
			break;
	}
	// Increase size of base for easier touch
	var baseGraphics = self.attachAsset(towerAssetId, {
		anchorX: 0.5,
		anchorY: 0.5,
		scaleX: 1.3,
		scaleY: 1.3
	});
	// No need to tint as we're using specific tower assets
	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 displayName = self.towerType === 'rifle' ? 'Rifle' : self.towerType === 'Toxin' ? 'Toxinbomb' : self.towerType.charAt(0).toUpperCase() + self.towerType.slice(1);
	// Set color based on tower type
	var towerColor = 0xFFFFFF; // default white
	switch (self.towerType) {
		case 'cannon':
			towerColor = 0xFF0000; // Red
			break;
		case 'rifle':
			towerColor = 0x00FF00; // Green
			break;
		case 'flame':
			towerColor = 0x800080; // Purple
			break;
		case 'Rocket':
			towerColor = 0x000000; // Black
			break;
		case 'gumbomb':
			towerColor = 0x0000FF; // Blue
			break;
		case 'Toxin':
			towerColor = 0xFFFF00; // Yellow
			break;
	}
	var typeLabel = new Text2(displayName, {
		size: 50,
		fill: towerColor,
		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, {
		size: 50,
		fill: 0x87CEEB,
		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 = crystals >= getTowerCost(self.towerType);
		// Set opacity based on affordability
		self.alpha = canAfford ? 1 : 0.5;
	};
	return self;
});
var Tower = Container.expand(function (id) {
	var self = Container.call(this);
	self.id = id || 'cannon';
	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 'flame':
				// Flame: 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 'Rocket':
				// Rocket: base range higher than sniper, +0.8 per level
				if (self.level === self.maxLevel) {
					return 14 * CELL_SIZE; // Higher than sniper's max range
				}
				return (6 + (self.level - 1) * 0.8) * CELL_SIZE;
			case 'rifle':
				// Rifle: base 3 (default), +0.5 per level
				return (3 + (self.level - 1) * 0.5) * CELL_SIZE;
			case 'gumbomb':
				// GumBomb: base 3.5, +0.5 per level
				return (3.5 + (self.level - 1) * 0.5) * CELL_SIZE;
			case 'Toxin':
				// Toxin: base 3.2, +0.5 per level
				return (3.2 + (self.level - 1) * 0.5) * CELL_SIZE;
			case 'cannon':
				// Cannon: base 3, +0.5 per level
				return (3 + (self.level - 1) * 0.5) * CELL_SIZE;
				break;
			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 = 7;
	self.lastFired = 0;
	self.targetEnemy = null;
	self.flameEffect = null; // Track active flame effect for flame tower
	self.flameAnimation = null; // Track flame animation visual
	switch (self.id) {
		case 'rifle':
			self.fireRate = Math.floor(60 / 3.5); // 3.5 shots per second = 60/3.5 = 17 frames
			self.damage = 5;
			self.range = 3 * CELL_SIZE; // Default tower range
			self.bulletSpeed = 7;
			break;
		case 'flame':
			self.fireRate = 120; // 0.5 shots per second
			self.damage = 9;
			self.range = 5 * CELL_SIZE;
			self.bulletSpeed = 25;
			break;
		case 'Rocket':
			self.fireRate = Math.floor(60 / 1.0); // 1.0 shots per second = 60 frames
			self.damage = 13;
			self.range = 2 * CELL_SIZE;
			self.bulletSpeed = 8;
			break;
		case 'gumbomb':
			self.fireRate = 50;
			self.damage = 3;
			self.range = 3.5 * CELL_SIZE;
			self.bulletSpeed = 5;
			break;
		case 'Toxin':
			self.fireRate = 70;
			self.damage = 18;
			self.range = 3.2 * CELL_SIZE;
			self.bulletSpeed = 5;
			break;
	}
	// Use specific tower asset based on type
	var towerAssetId = 'CannonTower'; // default
	switch (self.id) {
		case 'rifle':
			towerAssetId = 'RifleTower';
			break;
		case 'flame':
			towerAssetId = 'FlameTower';
			break;
		case 'Rocket':
			towerAssetId = 'RocketTower';
			break;
		case 'gumbomb':
			towerAssetId = 'GumBombTower';
			break;
		case 'Toxin':
			towerAssetId = 'ToxinTower';
			break;
		case 'cannon':
		default:
			towerAssetId = 'CannonTower';
			break;
	}
	var baseGraphics = self.attachAsset(towerAssetId, {
		anchorX: 0.5,
		anchorY: 0.5
	});
	// No need to tint as we're using specific tower assets
	var levelIndicators = [];
	var maxDots = self.maxLevel;
	var dotSpacing = baseGraphics.width / (maxDots + 1);
	var dotSize = 30;
	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 = dotSpacing * (i + 1) - baseGraphics.width / 2;
		dot.y = -CELL_SIZE * 1.2;
		self.addChild(dot);
		levelIndicators.push(dot);
	}
	var gunContainer = new Container();
	self.addChild(gunContainer);
	var weaponAssetId = 'Weapon1'; // default
	switch (self.id) {
		case 'rifle':
			weaponAssetId = 'Weapon2';
			break;
		case 'flame':
			weaponAssetId = 'Weapon3';
			break;
		case 'Rocket':
			weaponAssetId = 'Weapon4';
			break;
		case 'gumbomb':
			weaponAssetId = 'Weapon5';
			break;
		case 'Toxin':
			weaponAssetId = 'Weapon6';
			break;
		case 'cannon':
		default:
			weaponAssetId = 'Weapon1';
			break;
	}
	var gunGraphics = gunContainer.attachAsset(weaponAssetId, {
		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 = 0xff6600;
			} else {
				switch (self.id) {
					case 'rifle':
						towerLevelIndicator.tint = 0xffffff;
						break;
					case 'flame':
						towerLevelIndicator.tint = 0xffffff;
						break;
					case 'Rocket':
						towerLevelIndicator.tint = 0xffffff;
						break;
					case 'gumbomb':
						towerLevelIndicator.tint = 0xffffff;
						break;
					case 'Toxin':
						towerLevelIndicator.tint = 0xffffff;
						break;
					default:
						towerLevelIndicator.tint = 0xffffff;
				}
			}
		}
	};
	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 (crystals >= upgradeCost) {
				setCrystals(crystals - 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 === 'rifle') {
					if (self.level === self.maxLevel) {
						// Extra powerful last upgrade (double the effect)
						self.fireRate = Math.max(4, 12 - self.level * 3); // double the effect, starting from 12
						self.damage = 8 + self.level * 8; // double the effect, starting from 8
						self.bulletSpeed = 7 + self.level * 2.4; // double the effect
					} else {
						self.fireRate = Math.max(6, 12 - self.level * 1); // Rifle gets faster with upgrades, starting from 12
						self.damage = 8 + self.level * 2; // Starting from 8 damage
						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 crystals 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) {
			if (self.flameAnimation) {
				self.flameAnimation.destroy();
				self.flameAnimation = null;
			}
			if (self.flameEffect) {
				self.flameEffect.destroy();
				self.flameEffect = null;
			}
		}
		if (self.targetEnemy) {
			var dx = self.targetEnemy.x - self.x;
			var dy = self.targetEnemy.y - self.y;
			var angle = Math.atan2(dy, dx);
			// Smooth gun rotation using tween
			if (gunContainer.targetRotation === undefined) {
				gunContainer.targetRotation = angle;
				gunContainer.rotation = angle;
			} else {
				if (Math.abs(angle - gunContainer.targetRotation) > 0.05) {
					tween.stop(gunContainer, {
						rotation: true
					});
					// Calculate the shortest angle to rotate
					var currentRotation = gunContainer.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;
					}
					gunContainer.targetRotation = angle;
					tween(gunContainer, {
						rotation: currentRotation + angleDiff
					}, {
						duration: 200,
						easing: tween.easeOut
					});
				}
			}
			// Flame tower has special continuous firing logic
			if (self.id === 'flame') {
				// Continuously apply damage if an enemy is targeted
				if (self.targetEnemy && self.isInRange(self.targetEnemy)) {
					// Apply damage every frame while the enemy is in range and alive
					if (self.targetEnemy.health > 0) {
						var actualDamage = self.damage / 60; // Damage per second (damage/60 FPS)
						if (self.targetEnemy.damageResistance) {
							actualDamage = actualDamage * self.targetEnemy.damageResistance;
						}
						self.targetEnemy.health -= actualDamage;
						// Ensure health doesn't go below zero
						if (self.targetEnemy.health <= 0) {
							self.targetEnemy.health = 0;
						} else {
							// Update enemy health bar using stored original width
							var healthPercentage = self.targetEnemy.health / self.targetEnemy.maxHealth;
							self.targetEnemy.healthBar.width = self.targetEnemy.healthBarOriginalWidth * healthPercentage;
						}
						// --- Flame Visuals ---
						var dx = self.targetEnemy.x - self.x;
						var dy = self.targetEnemy.y - self.y;
						var distance = Math.sqrt(dx * dx + dy * dy);
						// Create flame animation from the gun if not already active
						if (!self.flameAnimation) {
							self.flameAnimation = new Container();
							var flameGraphics = self.flameAnimation.attachAsset('Flame', {
								anchorX: 0,
								// Anchor at the base (left) of the flame
								anchorY: 0.5 // Anchor in the middle vertically
							});
							// The Flame asset needs no rotation as it should extend horizontally from the gun
							flameGraphics.rotation = 0;
							flameGraphics.tint = 0xFF8800;
							gunContainer.addChild(self.flameAnimation);
							// Position at the front edge of the gun barrel
							self.flameAnimation.x = gunGraphics.width / 2 + 20;
							// Set initial scale for appear animation
							flameGraphics.scale.set(0, 0.6);
							// Initialize flame sound timer
							self.flameSoundTimer = 0;
						}
						// Play flame sound repeatedly while burning
						if (!self.flameSoundTimer) {
							self.flameSoundTimer = 0;
						}
						self.flameSoundTimer++;
						// Play flame sound every 30 frames (0.5 seconds at 60 FPS) while actively burning
						if (self.flameSoundTimer % 30 === 0) {
							LK.getSound('Flamesound').play();
						}
						// Update the flame animation every frame to stretch and flicker
						if (self.flameAnimation && self.flameAnimation.children[0]) {
							var flameGraphics = self.flameAnimation.children[0];
							// The flame asset is 300px long (originally height)
							var flameAssetLength = 300;
							var targetScaleX = distance / flameAssetLength;
							// Animate the flame length
							flameGraphics.scale.x += (targetScaleX - flameGraphics.scale.x) * 0.3;
							// Use a sinusoidal flicker for a more natural look on the width
							var flicker = Math.sin(LK.ticks * 0.5) * 0.1;
							flameGraphics.scale.y = 0.6 + flicker;
						}
						// Show visual effect on the enemy
						if (!self.flameEffect) {
							self.flameEffect = new EffectIndicator(self.targetEnemy.x, self.targetEnemy.y, 'flame');
							game.addChild(self.flameEffect);
						}
						// Update the position of the flame effect to match the target enemy
						self.flameEffect.x = self.targetEnemy.x;
						self.flameEffect.y = self.targetEnemy.y;
					} else {
						// Target is dead, destroy flame animation and effect
						if (self.flameAnimation) {
							self.flameAnimation.destroy();
							self.flameAnimation = null;
						}
						if (self.flameEffect) {
							self.flameEffect.destroy();
							self.flameEffect = null;
						}
					}
				} else {
					// No target enemy in range, destroy flame animation and effect
					if (self.flameAnimation) {
						self.flameAnimation.destroy();
						self.flameAnimation = null;
					}
					if (self.flameEffect) {
						self.flameEffect.destroy();
						self.flameEffect = null;
					}
				}
			} else {
				// Standard tower firing logic (single bullets)
				if (self.id === 'flame') {
					// Manage flame visibility based on fire rate cooldown
					var flameGraphics = self.flameAnimation ? self.flameAnimation.children[0] : null;
					if (flameGraphics) {
						if (LK.ticks - self.lastFired >= self.fireRate * 0.6) {
							// Show flame for 60% of fire rate
							flameGraphics.visible = true;
						} else {
							flameGraphics.visible = false;
						}
					}
					if (LK.ticks - self.lastFired >= self.fireRate) {
						self.fire();
						self.lastFired = LK.ticks;
					}
				} else {
					if (LK.ticks - self.lastFired >= self.fireRate) {
						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;
				// Store reference to source tower for range checking (all tower types)
				bullet.sourceTower = self;
				// For Rocket towers, use Rocket asset
				if (self.id === 'Rocket') {
					// Replace bullet asset with Rocket asset
					bullet.removeChild(bullet.children[0]);
					var rocketGraphics = bullet.attachAsset('Rocket', {
						anchorX: 0.5,
						anchorY: 0.5
					});
					rocketGraphics.tint = 0x00FF00;
					rocketGraphics.width = 80;
					rocketGraphics.height = 70;
				}
				// For gumbomb tower, pass level for scaling gum effect
				if (self.id === 'gumbomb') {
					bullet.sourceTowerLevel = self.level;
				}
				// Customize bullet appearance based on tower type
				switch (self.id) {
					case 'rifle':
						// Replace bullet asset with RifleBullet asset
						bullet.removeChild(bullet.children[0]);
						var rifleGraphics = bullet.attachAsset('RifleBullet', {
							anchorX: 0.5,
							anchorY: 0.5
						});
						rifleGraphics.tint = 0xFF8000;
						rifleGraphics.width = 5;
						rifleGraphics.height = 10;
						break;
					// Remove flame bullet type creation as flame tower now applies continuous damage
					case 'Rocket':
						// Rocket bullets now use Rocket asset, styling already applied above
						break;
					case 'gumbomb':
						// Replace bullet asset with GumBomb asset
						bullet.removeChild(bullet.children[0]);
						var gumbombGraphics = bullet.attachAsset('gumbomb', {
							anchorX: 0.5,
							anchorY: 0.5
						});
						gumbombGraphics.tint = 0x9900FF;
						gumbombGraphics.width = 35;
						gumbombGraphics.height = 35;
						break;
					case 'Toxin':
						// Replace bullet asset with ToxinBomb asset
						bullet.removeChild(bullet.children[0]);
						var toxinGraphics = bullet.attachAsset('ToxinBomb', {
							anchorX: 0.5,
							anchorY: 0.5
						});
						toxinGraphics.tint = 0xFF00AA;
						toxinGraphics.width = 35;
						toxinGraphics.height = 35;
						break;
				}
				game.addChild(bullet);
				bullets.push(bullet);
				self.targetEnemy.bulletsTargetingThis.push(bullet);
				// Play sound for tower shots
				switch (self.id) {
					case 'cannon':
						LK.getSound('Bulletsound').play();
						break;
					case 'rifle':
						LK.getSound('Riflebulletsound').play();
						break;
					case 'Rocket':
						LK.getSound('Rocketsound').play();
						break;
					case 'gumbomb':
						LK.getSound('Gumbombsound').play();
						break;
					case 'Toxin':
						LK.getSound('Toxinbombsound').play();
						break;
				}
				// --- Fire recoil effect for gunContainer ---
				// Stop any ongoing recoil tweens before starting a new one
				tween.stop(gunContainer, {
					x: true,
					y: true,
					scaleX: true,
					scaleY: true
				});
				// Always use the original resting position for recoil, never accumulate offset
				if (gunContainer._restX === undefined) {
					gunContainer._restX = 0;
				}
				if (gunContainer._restY === undefined) {
					gunContainer._restY = 0;
				}
				if (gunContainer._restScaleX === undefined) {
					gunContainer._restScaleX = 1;
				}
				if (gunContainer._restScaleY === undefined) {
					gunContainer._restScaleY = 1;
				}
				// Reset to resting position before animating (in case of interrupted tweens)
				gunContainer.x = gunContainer._restX;
				gunContainer.y = gunContainer._restY;
				gunContainer.scaleX = gunContainer._restScaleX;
				gunContainer.scaleY = gunContainer._restScaleY;
				// Calculate recoil offset (recoil back along the gun's rotation)
				var recoilDistance = 8;
				var recoilX = -Math.cos(gunContainer.rotation) * recoilDistance;
				var recoilY = -Math.sin(gunContainer.rotation) * recoilDistance;
				// Animate recoil back from the resting position
				tween(gunContainer, {
					x: gunContainer._restX + recoilX,
					y: gunContainer._restY + recoilY
				}, {
					duration: 60,
					easing: tween.cubicOut,
					onFinish: function onFinish() {
						// Animate return to original position/scale
						tween(gunContainer, {
							x: gunContainer._restX,
							y: gunContainer._restY
						}, {
							duration: 90,
							easing: tween.cubicIn
						});
					}
				});
			}
		}
	};
	self.placeOnGrid = function (gridX, gridY) {
		self.gridX = gridX;
		self.gridY = gridY;
		self.x = grid.x + gridX * CELL_SIZE + CELL_SIZE / 2;
		self.y = grid.y + gridY * CELL_SIZE + CELL_SIZE / 2;
		for (var i = 0; i < 2; i++) {
			for (var j = 0; j < 2; j++) {
				var cell = grid.getCell(gridX + i, gridY + j);
				if (cell) {
					cell.type = 1;
				}
			}
		}
		self.refreshCellsInRange();
		// Play placement sound for cannon tower
		if (self.id === 'cannon') {
			LK.getSound('placmentsound1').play();
		} else if (self.id === 'rifle') {
			LK.getSound('placmentsound2').play();
		} else if (self.id === 'flame') {
			LK.getSound('placmentsound3').play();
		} else if (self.id === 'Rocket') {
			LK.getSound('placmentsound4').play();
		} else if (self.id === 'gumbomb') {
			LK.getSound('placmentsound5').play();
		} else if (self.id === 'Toxin') {
			LK.getSound('placmentsound6').play();
		}
	};
	return self;
});
var TowerPreview = Container.expand(function () {
	var self = Container.call(this);
	var towerRange = 3;
	var rangeInPixels = towerRange * CELL_SIZE;
	self.towerType = 'cannon';
	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 = crystals >= 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 'rifle':
				previewGraphics.tint = 0xFFFFFF;
				break;
			case 'flame':
				previewGraphics.tint = 0xFFFFFF;
				break;
			case 'Rocket':
				previewGraphics.tint = 0xFFFFFF;
				break;
			case 'gumbomb':
				previewGraphics.tint = 0xFFFFFF;
				break;
			case 'Toxin':
				previewGraphics.tint = 0xFFFFFF;
				break;
			case 'cannon':
				previewGraphics.tint = 0xFFFFFF;
				break;
			default:
				previewGraphics.tint = 0xFFFFFF;
		}
		if (!self.canPlace || !self.hasEnoughGold) {
			previewGraphics.tint = 0xffffff;
		}
	};
	self.updatePlacementStatus = function () {
		var validGridPlacement = true;
		if (self.gridY <= 4 || self.gridY + 1 >= grid.cells[0].length - 4) {
			validGridPlacement = false;
		} else {
			for (var i = 0; i < 2; i++) {
				for (var j = 0; j < 2; j++) {
					var cell = grid.getCell(self.gridX + i, self.gridY + j);
					if (!cell || cell.type !== 0) {
						validGridPlacement = false;
						break;
					}
				}
				if (!validGridPlacement) {
					break;
				}
			}
		}
		self.blockedByEnemy = false;
		if (validGridPlacement) {
			for (var i = 0; i < enemies.length; i++) {
				var enemy = enemies[i];
				if (enemy.currentCellY < 4) {
					continue;
				}
				// Only check non-flying enemies, flying enemies can pass over towers
				if (!enemy.isFlying) {
					if (enemy.cellX >= self.gridX && enemy.cellX < self.gridX + 2 && enemy.cellY >= self.gridY && enemy.cellY < self.gridY + 2) {
						self.blockedByEnemy = true;
						break;
					}
					if (enemy.currentTarget) {
						var targetX = enemy.currentTarget.x;
						var targetY = enemy.currentTarget.y;
						if (targetX >= self.gridX && targetX < self.gridX + 2 && targetY >= self.gridY && targetY < self.gridY + 2) {
							self.blockedByEnemy = true;
							break;
						}
					}
				}
			}
		}
		self.canPlace = validGridPlacement && !self.blockedByEnemy;
		self.hasEnoughGold = crystals >= getTowerCost(self.towerType);
		self.updateAppearance();
	};
	self.checkPlacement = function () {
		self.updatePlacementStatus();
	};
	self.snapToGrid = function (x, y) {
		var gridPosX = x - grid.x;
		var gridPosY = y - grid.y;
		self.gridX = Math.floor(gridPosX / CELL_SIZE);
		self.gridY = Math.floor(gridPosY / CELL_SIZE);
		self.x = grid.x + self.gridX * CELL_SIZE + CELL_SIZE / 2;
		self.y = grid.y + self.gridY * CELL_SIZE + CELL_SIZE / 2;
		self.checkPlacement();
	};
	return self;
});
var UpgradeMenu = Container.expand(function (tower) {
	var self = Container.call(this);
	self.tower = tower;
	self.y = 2732 + 225;
	var menuBackground = self.attachAsset('notification', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	menuBackground.width = 2048;
	menuBackground.height = 500;
	menuBackground.tint = 0x444444;
	menuBackground.alpha = 0.9;
	var displayName = self.tower.id === 'rifle' ? 'Rifle' : self.tower.id === 'Toxin' ? 'Toxinbomb' : self.tower.id.charAt(0).toUpperCase() + self.tower.id.slice(1);
	var towerTypeText = new Text2(displayName + ' 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 : crystals >= upgradeCost ? 0x00AA00 : 0x888888;
	var buttonText = new Text2(isMaxLevel ? 'Max Level' : 'Upgrade: ' + upgradeCost + ' crystal', {
		size: 60,
		fill: 0x87CEEB,
		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 + ' crystal', {
		size: 60,
		fill: 0x87CEEB,
		weight: 800
	});
	sellButtonText.anchor.set(0.5, 0.5);
	sellButton.addChild(sellButtonText);
	upgradeButton.y = -85;
	sellButton.y = 85;
	var closeButton = new Container();
	self.addChild(closeButton);
	var closeBackground = closeButton.attachAsset('notification', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	closeBackground.width = 90;
	closeBackground.height = 90;
	closeBackground.tint = 0xAA0000;
	var closeText = new Text2('X', {
		size: 68,
		fill: 0xFFFFFF,
		weight: 800
	});
	closeText.anchor.set(0.5, 0.5);
	closeButton.addChild(closeText);
	closeButton.x = menuBackground.width / 2 - 57;
	closeButton.y = -menuBackground.height / 2 + 57;
	upgradeButton.down = function (x, y, obj) {
		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 + ' crystal');
			var totalInvestment = self.tower.getTotalValue ? self.tower.getTotalValue() : 0;
			var sellValue = Math.floor(totalInvestment * 0.6);
			sellButtonText.setText('Sell: +' + sellValue + ' crystal');
			if (self.tower.level >= self.tower.maxLevel) {
				buttonBackground.tint = 0xffffff;
				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);
		setCrystals(crystals + sellValue);
		var notification = game.addChild(new Notification("Tower sold for " + sellValue + " crystal!"));
		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 = 0xffffff;
			}
			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 = crystals >= currentUpgradeCost;
		buttonBackground.tint = canAfford ? 0x00ffb3 : 0x888888;
		var newText = 'Upgrade: ' + currentUpgradeCost + ' crystal';
		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 = 0x9900ff;
	// 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 = 2732 / 2;
		}
	};
	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 = 0xffffff;
			waveType = "Easy";
			enemyType = "normal";
			enemyCount = 10;
		} else if (i === 1) {
			block.tint = 0xffffff;
			waveType = "Fast";
			enemyType = "fast";
			enemyCount = 10;
		} else if (i === 2) {
			block.tint = 0xffffff;
			waveType = "Unstoppable";
			enemyType = "immune";
			enemyCount = 10;
		} else if (i === 3) {
			block.tint = 0xffffff;
			waveType = "Air";
			enemyType = "flying";
			enemyCount = 10;
		} else if (i === 4) {
			block.tint = 0xffffff;
			waveType = "Group";
			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 Air";
				block.tint = 0xffffff;
			} else {
				enemyType = bossTypes[bossTypeIndex % bossTypes.length];
				switch (enemyType) {
					case 'normal':
						block.tint = 0xffffff;
						waveType = "Boss Easy";
						break;
					case 'fast':
						block.tint = 0xffffff;
						waveType = "Boss Fast";
						break;
					case 'immune':
						block.tint = 0xffffff;
						waveType = "Boss Unstoppable";
						break;
					case 'flying':
						block.tint = 0xffffff;
						waveType = "Boss Air";
						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 = 0xffffff;
					break;
				case 'fast':
					block.tint = 0xffffff;
					break;
				case 'immune':
					block.tint = 0xffffff;
					break;
				case 'flying':
					block.tint = 0xffffff;
					break;
				default:
					block.tint = 0xffffff;
					break;
			}
		} else if ((i + 1) % 5 === 0) {
			// Every 5th non-boss wave is fast
			block.tint = 0xffffff;
			waveType = "Fast";
			enemyType = "fast";
			enemyCount = 10;
		} else if ((i + 1) % 4 === 0) {
			// Every 4th non-boss wave is immune
			block.tint = 0xffffff;
			waveType = "Unstoppable";
			enemyType = "immune";
			enemyCount = 10;
		} else if ((i + 1) % 7 === 0) {
			// Every 7th non-boss wave is flying
			block.tint = 0xffffff;
			waveType = "Air";
			enemyType = "flying";
			enemyCount = 10;
		} else if ((i + 1) % 3 === 0) {
			// Every 3rd non-boss wave is swarm
			block.tint = 0xffffff;
			waveType = "Group";
			enemyType = "swarm";
			enemyCount = 30;
		} else {
			block.tint = 0xffffff;
			waveType = "Easy";
			enemyType = "normal";
			enemyCount = 10;
		}
		// --- End new unified wave logic ---
		// Add tween animation to transition block color from red to white
		tween(block, {
			tint: 0xFFFFFF
		}, {
			duration: 2000,
			easing: tween.easeInOut
		});
		// 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;
		self.waveDisplayNames = self.waveDisplayNames || [];
		self.waveDisplayNames[i] = waveType;
		// 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) {
		if (waveNumber < 1 || waveNumber > totalWaves) {
			return "Normal";
		}
		// Return the stored display name
		return self.waveDisplayNames[waveNumber - 1] || "Normal";
	};
	self.positionIndicator = new Container();
	var indicator = self.positionIndicator.attachAsset('towerLevelIndicator', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	indicator.width = blockWidth - 10;
	indicator.height = 16;
	indicator.tint = 0xffad0e;
	indicator.y = -65;
	var indicator2 = self.positionIndicator.attachAsset('towerLevelIndicator', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	indicator2.width = blockWidth - 10;
	indicator2.height = 16;
	indicator2.tint = 0xffad0e;
	indicator2.y = 65;
	var leftWall = self.positionIndicator.attachAsset('towerLevelIndicator', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	leftWall.width = 16;
	leftWall.height = 146;
	leftWall.tint = 0xffad0e;
	leftWall.x = -(blockWidth - 16) / 2;
	var rightWall = self.positionIndicator.attachAsset('towerLevelIndicator', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	rightWall.width = 16;
	rightWall.height = 146;
	rightWall.tint = 0xffad0e;
	rightWall.x = (blockWidth - 16) / 2;
	self.addChild(self.positionIndicator);
	self.update = function () {
		var progress = waveTimer / nextWaveTime;
		var moveAmount = (progress + currentWave) * blockWidth;
		for (var i = 0; i < self.waveMarkers.length; i++) {
			var marker = self.waveMarkers[i];
			marker.x = -moveAmount + i * blockWidth;
		}
		self.positionIndicator.x = 0;
		for (var i = 0; i < totalWaves + 1; i++) {
			var marker = self.waveMarkers[i];
			if (i === 0) {
				continue;
			}
			var block = marker.children[0];
			if (i - 1 < currentWave) {
				block.alpha = .5;
			}
		}
		self.handleWaveProgression = function () {
			if (!self.gameStarted) {
				return;
			}
			if (currentWave < totalWaves) {
				waveTimer++;
				if (waveTimer >= nextWaveTime) {
					waveTimer = 0;
					currentWave++;
					waveInProgress = true;
					waveSpawned = false;
					if (currentWave != 1) {
						var waveType = self.getWaveTypeName(currentWave);
						var enemyCount = self.getEnemyCount(currentWave);
						var notification = game.addChild(new Notification("Wave " + currentWave + " (" + waveType + " - " + enemyCount + " enemies) incoming!"));
						notification.x = 2048 / 2;
						notification.y = 2732 / 2;
					}
				}
			}
		};
		self.handleWaveProgression();
	};
	return self;
});
/**** 
* Initialize Game
****/ 
var game = new LK.Game({
	backgroundColor: 0x333333
});
/**** 
* Game Code
****/ 
// Game state variables
var gameStarted = false;
var showingIntro = true;
var showingScores = false;
var scoreBackground = null;
var scoreMenu = null;
var showingSettings = false;
var settingBackground = null;
var settingMenu = null;
var brightnessOverlay = null;
var isDraggingLightButton = false;
// High scores management
function getHighScores() {
	return storage.highScores || [];
}
function addHighScore(score) {
	var highScores = getHighScores();
	highScores.push(score);
	// Sort in descending order
	highScores.sort(function (a, b) {
		return b - a;
	});
	// Keep only top 10
	if (highScores.length > 10) {
		highScores = highScores.slice(0, 10);
	}
	storage.highScores = highScores;
	return highScores;
}
function showSettings() {
	if (showingSettings) {
		return;
	}
	showingSettings = true;
	settingBackground = game.attachAsset('SettingBackground', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	var startX = 2048 + settingBackground.width / 2;
	var targetX = 2048 / 2;
	settingBackground.x = startX;
	settingBackground.y = 2732 / 2;
	game.addChild(settingBackground);
	settingMenu = new Container();
	settingMenu.x = startX;
	settingMenu.y = 2732 / 2;
	game.addChild(settingMenu);
	var closeButton = new Container();
	closeButton.attachAsset('CloseButton', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	closeButton.y = settingBackground.height / 2 - 150;
	settingMenu.addChild(closeButton);
	closeButton.down = function () {
		hideSettings();
	};
	var lightIcon = settingMenu.attachAsset('LightIcon', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	lightIcon.x = -settingBackground.width / 2 + 200;
	lightIcon.y = -300;
	var lightBar = settingMenu.attachAsset('LightBar', {
		anchorX: 0,
		anchorY: 0.5
	});
	lightBar.name = "lightBar";
	lightBar.x = lightIcon.x + 150;
	lightBar.y = -300;
	var lightButton = settingMenu.attachAsset('LightButton', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	lightButton.name = "lightButton";
	lightButton.y = -300;
	// Set initial position based on current brightness
	if (brightnessOverlay) {
		var percentage = brightnessOverlay.alpha / 0.7; // Assuming max alpha is 0.7 for dimming
		lightButton.x = lightBar.x + lightBar.width * percentage;
	} else {
		lightButton.x = lightBar.x;
	}
	lightButton.down = function () {
		isDraggingLightButton = true;
	};
	tween(settingBackground, {
		x: targetX
	}, {
		duration: 500,
		easing: tween.easeOut
	});
	tween(settingMenu, {
		x: targetX
	}, {
		duration: 500,
		easing: tween.easeOut
	});
}
function hideSettings() {
	if (!showingSettings) {
		return;
	}
	showingSettings = false;
	var targetX = 2048 + 1600 / 2;
	if (settingBackground) {
		tween(settingBackground, {
			x: targetX
		}, {
			duration: 500,
			easing: tween.easeIn,
			onFinish: function onFinish() {
				if (settingBackground) {
					game.removeChild(settingBackground);
					settingBackground = null;
				}
			}
		});
	}
	if (settingMenu) {
		tween(settingMenu, {
			x: targetX
		}, {
			duration: 500,
			easing: tween.easeIn,
			onFinish: function onFinish() {
				if (settingMenu) {
					game.removeChild(settingMenu);
					settingMenu = null;
				}
			}
		});
	}
}
function showHighScores() {
	if (showingScores) {
		return;
	}
	showingScores = true;
	// Create score background
	scoreBackground = game.attachAsset('Scorebackground', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	scoreBackground.x = 2048 / 2;
	scoreBackground.y = 2732 / 2;
	game.addChild(scoreBackground);
	// Create score menu container
	scoreMenu = new Container();
	scoreMenu.x = 2048 / 2;
	scoreMenu.y = 2732 / 2;
	game.addChild(scoreMenu);
	// Add title
	var titleShadow = new Text2("HIGH SCORES", {
		size: 80,
		fill: 0x000000,
		weight: 800
	});
	titleShadow.anchor.set(0.5, 0.5);
	titleShadow.x = 4;
	titleShadow.y = -400 + 4;
	scoreMenu.addChild(titleShadow);
	var title = new Text2("HIGH SCORES", {
		size: 80,
		fill: 0xFFD700,
		weight: 800
	});
	title.anchor.set(0.5, 0.5);
	title.y = -400;
	scoreMenu.addChild(title);
	// Get and display high scores
	var highScores = getHighScores();
	for (var i = 0; i < 10; i++) {
		var rank = i + 1;
		var scoreValue = i < highScores.length ? highScores[i] : 0;
		var yPos = -280 + i * 60;
		// Score rank and value shadow
		var scoreShadow = new Text2(rank + ". " + scoreValue, {
			size: 60,
			fill: 0x000000,
			weight: 800
		});
		scoreShadow.anchor.set(0.5, 0.5);
		scoreShadow.x = 4;
		scoreShadow.y = yPos + 4;
		scoreMenu.addChild(scoreShadow);
		// Score rank and value
		var scoreColor = i < 3 ? 0xFFD700 : 0xFFFFFF; // Gold for top 3
		var scoreText = new Text2(rank + ". " + scoreValue, {
			size: 60,
			fill: scoreColor,
			weight: 800
		});
		scoreText.anchor.set(0.5, 0.5);
		scoreText.y = yPos;
		scoreMenu.addChild(scoreText);
	}
	// Add close button
	var closeButton = new Container();
	var closeButtonBg = closeButton.attachAsset('ScoreButton', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	closeButtonBg.width = 300;
	closeButtonBg.height = 100;
	closeButtonBg.tint = 0x4db3e9;
	var closeButtonShadow = new Text2("CLOSE", {
		size: 50,
		fill: 0x000000,
		weight: 800
	});
	closeButtonShadow.anchor.set(0.5, 0.5);
	closeButtonShadow.x = 4;
	closeButtonShadow.y = 4;
	closeButton.addChild(closeButtonShadow);
	var closeButtonText = new Text2("CLOSE", {
		size: 50,
		fill: 0xFFFFFF,
		weight: 800
	});
	closeButtonText.anchor.set(0.5, 0.5);
	closeButton.addChild(closeButtonText);
	closeButton.y = 450;
	scoreMenu.addChild(closeButton);
	// Close button handler
	closeButton.down = function () {
		hideHighScores();
	};
}
function hideHighScores() {
	if (!showingScores) {
		return;
	}
	showingScores = false;
	if (scoreBackground) {
		game.removeChild(scoreBackground);
		scoreBackground = null;
	}
	if (scoreMenu) {
		game.removeChild(scoreMenu);
		scoreMenu = null;
	}
}
// Add intro background image to cover the entire game area
var introBackground = game.attachAsset('Introbackground', {
	anchorX: 0,
	anchorY: 0,
	width: 2048,
	height: 2732
});
// Position background at top-left corner
introBackground.x = 0;
introBackground.y = 0;
// Send background to the back
game.addChildAt(introBackground, 0);
// Play intro music immediately when intro starts
LK.playMusic('Intromusic');
// Add StartButton on the left side
var startButton = game.attachAsset('StartButton', {
	anchorX: 0.5,
	anchorY: 0.5
});
startButton.x = 724;
startButton.y = 2732 - 200 - 200; // 200px from bottom
game.addChild(startButton);
// Add ScoreButton
var scoreButton = game.attachAsset('ScoreButton', {
	anchorX: 0.5,
	anchorY: 0.5
});
scoreButton.x = 1124;
scoreButton.y = 2732 - 200 - 200; // 200px from bottom
game.addChild(scoreButton);
// Add SettingButton on the right side
var settingButton = game.attachAsset('SettingButton', {
	anchorX: 0.5,
	anchorY: 0.5
});
settingButton.x = 1424;
settingButton.y = 2732 - 200 - 200; // 200px from bottom
game.addChild(settingButton);
// Add game background image (initially hidden)
var gameBackground = game.attachAsset('gamebackground', {
	anchorX: 0,
	anchorY: 0,
	width: 2048,
	height: 2732
});
gameBackground.x = 0;
gameBackground.y = 0;
gameBackground.visible = false;
game.addChildAt(gameBackground, 0);
// Function to start the game
function startGame() {
	showingIntro = false;
	gameStarted = true;
	// Hide intro elements
	introBackground.visible = false;
	startButton.visible = false;
	scoreButton.visible = false;
	settingButton.visible = false;
	// Show game elements
	gameBackground.visible = true;
	debugLayer.visible = true;
	towerLayer.visible = true;
	enemyLayer.visible = true;
	crystalIcon.visible = true;
	crystalText.visible = true;
	healthBarContainer.visible = true;
	scoreText.visible = true;
	waveIndicator.visible = true;
	nextWaveButtonContainer.visible = true;
	towerPreview.visible = false;
	// Stop intro music and play game music only if music is on
	LK.stopMusic();
	if (musicOn) {
		LK.playMusic('Gamemusic1');
	}
}
// Add click handlers for buttons
startButton.down = function () {
	startGame();
};
scoreButton.down = function () {
	showHighScores();
};
settingButton.down = function () {
	if (showingIntro && !showingScores) {
		showSettings();
	}
};
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 crystals = 100;
var lives = 100;
var score = 0;
var currentWave = 0;
var lastWave = 0;
var totalWaves = 50;
var waveTimer = 0;
var waveInProgress = false;
var waveSpawned = false;
var nextWaveTime = 12000 / 2;
var sourceTower = null;
var enemiesToSpawn = 10; // Default number of enemies per wave
var ships = [];
var lastShip3Spawn = 0;
var ship3SpawnInterval = 2400; // 40 seconds at 60 FPS
var crystalText = new Text2('Crystal: ' + crystals, {
	size: 60,
	fill: 0xff0080,
	weight: 800
});
crystalText.anchor.set(0.5, 0.9);
var healthBarBG = LK.getAsset('healthBarOutline', {
	anchorX: 0.5,
	anchorY: 1
});
var healthBarFill = LK.getAsset('gameHealthBar', {
	anchorX: 0,
	anchorY: 1.1
});
healthBarFill.x = -healthBarBG.width / 2 + (healthBarBG.width - healthBarFill.width) / 2;
healthBarFill.originalWidth = healthBarFill.width;
var healthText = new Text2('100/100', {
	size: 50,
	fill: 0x4db3e9,
	weight: 800
});
healthText.anchor.set(0.5, 0.9);
var healthBarContainer = new Container();
healthBarContainer.addChild(healthBarBG);
healthBarContainer.addChild(healthBarFill);
healthBarContainer.addChild(healthText);
var scoreText = new Text2('Score: ' + score, {
	size: 60,
	fill: 0x87CEEB,
	weight: 800
});
scoreText.anchor.set(0.5, 0.9);
var topMargin = 50;
var centerX = 2048 / 2;
var spacing = 400;
var crystalIcon = LK.getAsset('Crystal', {
	anchorX: 1.4,
	anchorY: 0.9,
	scaleX: 0.4,
	scaleY: 0.6
});
LK.gui.top.addChild(crystalIcon);
LK.gui.top.addChild(crystalText);
LK.gui.top.addChild(healthBarContainer);
LK.gui.top.addChild(scoreText);
// Hide UI elements during intro
crystalIcon.visible = false;
crystalText.visible = false;
healthBarContainer.visible = false;
scoreText.visible = false;
healthBarContainer.x = 0;
healthBarContainer.y = topMargin;
crystalText.x = -spacing;
crystalText.y = topMargin;
crystalIcon.x = -spacing - 180;
crystalIcon.y = topMargin;
scoreText.x = spacing;
scoreText.y = topMargin;
// Add mute button to top right corner with music state tracking
var musicOn = true; // Track current music state
var muteButton = LK.getAsset('Mutebutton1', {
	anchorX: 1.0,
	anchorY: -0.1,
	width: 100,
	height: 100
});
muteButton.x = 2048 - 50; // 50px from right edge
muteButton.y = 0; // 0px from top edge
game.addChild(muteButton);
// Add mute button click handler
muteButton.down = function () {
	musicOn = !musicOn; // Toggle music state
	if (musicOn) {
		// Music is now on - show Mutebutton1 and resume music
		game.removeChild(muteButton);
		muteButton = LK.getAsset('Mutebutton1', {
			anchorX: 1.0,
			anchorY: -0.1,
			width: 100,
			height: 100
		});
		muteButton.x = 2048 - 50;
		muteButton.y = 0;
		game.addChild(muteButton);
		// Animate button appearance
		muteButton.alpha = 0;
		tween(muteButton, {
			alpha: 1
		}, {
			duration: 200,
			easing: tween.easeOut
		});
		// Resume appropriate music based on game state
		if (showingIntro) {
			LK.playMusic('Intromusic');
		} else if (gameStarted) {
			// Resume game music based on current wave
			if (currentWave === 10) {
				LK.playMusic('Bossmusic1');
			} else if (currentWave >= 11 && currentWave < 20) {
				LK.playMusic('Gamemusic2');
			} else if (currentWave === 20) {
				LK.playMusic('Bossmusic2');
			} else if (currentWave >= 21 && currentWave < 30) {
				LK.playMusic('gamemusic3');
			} else if (currentWave === 30) {
				LK.playMusic('Bossmusic3');
			} else if (currentWave >= 31 && currentWave < 40) {
				LK.playMusic('Gamemusic4');
			} else if (currentWave === 40) {
				LK.playMusic('Bossmusic4');
			} else if (currentWave >= 41 && currentWave < 50) {
				LK.playMusic('Gamemusic5');
			} else if (currentWave === 50) {
				LK.playMusic('Bossmusic5');
			} else {
				LK.playMusic('Gamemusic1');
			}
		}
	} else {
		// Music is now off - show Mutebutton2 and stop music
		game.removeChild(muteButton);
		muteButton = LK.getAsset('Mutebutton2', {
			anchorX: 1.0,
			anchorY: -0.1,
			width: 100,
			height: 100
		});
		muteButton.x = 2048 - 50;
		muteButton.y = 0;
		game.addChild(muteButton);
		// Animate button appearance
		muteButton.alpha = 0;
		tween(muteButton, {
			alpha: 1
		}, {
			duration: 200,
			easing: tween.easeOut
		});
		// Stop all music
		LK.stopMusic();
	}
	// Re-assign the click handler to the new button
	muteButton.down = arguments.callee;
};
function updateUI() {
	crystalText.setText('Crystal: ' + crystals);
	healthText.setText(lives + '/100');
	// Update health bar fill width based on current lives (assuming max 100 lives)
	var healthPercentage = lives / 100;
	healthBarFill.width = healthBarFill.originalWidth * healthPercentage;
	scoreText.setText('Score: ' + score);
}
function setCrystals(value) {
	crystals = value;
	updateUI();
}
var debugLayer = new Container();
var towerLayer = new Container();
// Create three separate layers for enemy hierarchy
var enemyLayerBottom = new Container(); // For normal enemies
var enemyLayerMiddle = new Container(); // For shadows
var enemyLayerTop = new Container(); // For flying enemies
var enemyLayer = new Container(); // Main container to hold all enemy layers
// Add layers in correct order (bottom first, then middle for shadows, then top)
enemyLayer.addChild(enemyLayerBottom);
enemyLayer.addChild(enemyLayerMiddle);
enemyLayer.addChild(enemyLayerTop);
var grid = new Grid(24, 29 + 6);
grid.x = 150;
grid.y = 200 - CELL_SIZE * 4;
grid.pathFind();
grid.renderDebug();
debugLayer.addChild(grid);
game.addChild(debugLayer);
game.addChild(towerLayer);
game.addChild(enemyLayer);
brightnessOverlay = LK.getAsset('cell', {
	width: 2048,
	height: 2732
});
brightnessOverlay.tint = 0x000000;
brightnessOverlay.alpha = 0;
brightnessOverlay.x = 0;
brightnessOverlay.y = 0;
game.addChild(brightnessOverlay);
// Hide game elements during intro
debugLayer.visible = false;
towerLayer.visible = false;
enemyLayer.visible = false;
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 = 10;
	switch (towerType) {
		case 'rifle':
			cost = 20;
			break;
		case 'flame':
			cost = 60;
			break;
		case 'Rocket':
			cost = 80;
			break;
		case 'gumbomb':
			cost = 30;
			break;
		case 'Toxin':
			cost = 100;
			break;
	}
	return cost;
}
function getTowerSellValue(totalValue) {
	return waveIndicator && waveIndicator.gameStarted ? Math.floor(totalValue * 0.8) : totalValue;
}
function placeTower(gridX, gridY, towerType) {
	var towerCost = getTowerCost(towerType);
	if (crystals >= towerCost) {
		var tower = new Tower(towerType || 'cannon');
		tower.placeOnGrid(gridX, gridY);
		towerLayer.addChild(tower);
		towers.push(tower);
		setCrystals(crystals - towerCost);
		grid.pathFind();
		grid.renderDebug();
		return true;
	} else {
		var notification = game.addChild(new Notification("Not enough crystals!"));
		notification.x = 2048 / 2;
		notification.y = grid.height - 50;
		return false;
	}
}
game.down = function (x, y, obj) {
	// Handle high scores menu during intro
	if (showingIntro && showingScores) {
		// Check if click is outside score menu area
		var menuLeft = 2048 / 2 - 800;
		var menuRight = 2048 / 2 + 800;
		var menuTop = 2732 / 2 - 1100;
		var menuBottom = 2732 / 2 + 1100;
		if (x < menuLeft || x > menuRight || y < menuTop || y > menuBottom) {
			hideHighScores();
		}
		return;
	}
	// Don't process game input during intro
	if (showingIntro || !gameStarted) {
		return;
	}
	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 (isDraggingLightButton) {
		if (settingMenu) {
			var lightBar = null;
			var lightButton = null;
			for (var i = 0; i < settingMenu.children.length; i++) {
				if (settingMenu.children[i].name === "lightBar") lightBar = settingMenu.children[i];
				if (settingMenu.children[i].name === "lightButton") lightButton = settingMenu.children[i];
			}
			if (lightBar && lightButton) {
				var localPos = settingMenu.toLocal({
					x: x,
					y: y
				});
				var minX = lightBar.x + lightButton.width / 2;
				var maxX = lightBar.x + lightBar.width - lightButton.width / 2;
				var newX = Math.max(minX, Math.min(maxX, localPos.x));
				lightButton.x = newX;
				var trackWidth = maxX - minX;
				var percentage = trackWidth > 0 ? (newX - minX) / trackWidth : 0;
				if (brightnessOverlay) {
					brightnessOverlay.alpha = percentage * 0.7; // Max dim alpha 0.7
				}
			}
		}
		return;
	}
	// Don't process game input during intro
	if (showingIntro || !gameStarted) {
		return;
	}
	if (isDragging) {
		// Shift the y position upward by 1.5 tiles to show preview above finger
		towerPreview.snapToGrid(x, y - CELL_SIZE * 1.5);
	}
};
game.up = function (x, y, obj) {
	if (isDraggingLightButton) {
		isDraggingLightButton = false;
		return;
	}
	// Don't process game input during intro
	if (showingIntro || !gameStarted) {
		return;
	}
	var clickedOnTower = false;
	for (var i = 0; i < towers.length; i++) {
		var tower = towers[i];
		var towerLeft = tower.x - tower.width / 2;
		var towerRight = tower.x + tower.width / 2;
		var towerTop = tower.y - tower.height / 2;
		var towerBottom = tower.y + tower.height / 2;
		if (x >= towerLeft && x <= towerRight && y >= towerTop && y <= towerBottom) {
			clickedOnTower = true;
			break;
		}
	}
	var upgradeMenus = game.children.filter(function (child) {
		return child instanceof UpgradeMenu;
	});
	if (upgradeMenus.length > 0 && !isDragging && !clickedOnTower) {
		var clickedOnMenu = false;
		for (var i = 0; i < upgradeMenus.length; i++) {
			var menu = upgradeMenus[i];
			var menuWidth = 2048;
			var menuHeight = 450;
			var menuLeft = menu.x - menuWidth / 2;
			var menuRight = menu.x + menuWidth / 2;
			var menuTop = menu.y - menuHeight / 2;
			var menuBottom = menu.y + menuHeight / 2;
			if (x >= menuLeft && x <= menuRight && y >= menuTop && y <= menuBottom) {
				clickedOnMenu = true;
				break;
			}
		}
		if (!clickedOnMenu) {
			for (var i = 0; i < upgradeMenus.length; i++) {
				var menu = upgradeMenus[i];
				hideUpgradeMenu(menu);
			}
			for (var i = game.children.length - 1; i >= 0; i--) {
				if (game.children[i].isTowerRange) {
					game.removeChild(game.children[i]);
				}
			}
			selectedTower = null;
			grid.renderDebug();
		}
	}
	if (isDragging) {
		isDragging = false;
		if (towerPreview.canPlace) {
			if (!wouldBlockPath(towerPreview.gridX, towerPreview.gridY)) {
				placeTower(towerPreview.gridX, towerPreview.gridY, towerPreview.towerType);
			} else {
				var notification = game.addChild(new Notification("Tower would block the path!"));
				notification.x = 2048 / 2;
				notification.y = grid.height - 50;
			}
		} else if (towerPreview.blockedByEnemy) {
			var notification = game.addChild(new Notification("Cannot build: Enemy in the way!"));
			notification.x = 2048 / 2;
			notification.y = grid.height - 50;
		} else if (towerPreview.visible) {
			var notification = game.addChild(new Notification("Cannot build here!"));
			notification.x = 2048 / 2;
			notification.y = grid.height - 50;
		}
		towerPreview.visible = false;
		if (isDragging) {
			var upgradeMenus = game.children.filter(function (child) {
				return child instanceof UpgradeMenu;
			});
			for (var i = 0; i < upgradeMenus.length; i++) {
				upgradeMenus[i].destroy();
			}
		}
	}
};
var waveIndicator = new WaveIndicator();
waveIndicator.x = 2048 / 2;
waveIndicator.y = 2732 - 80;
waveIndicator.visible = false;
game.addChild(waveIndicator);
var nextWaveButtonContainer = new Container();
var nextWaveButton = new NextWaveButton();
nextWaveButton.x = 2048 - 200;
nextWaveButton.y = 2732 - 100 + 20;
nextWaveButtonContainer.addChild(nextWaveButton);
nextWaveButtonContainer.visible = false;
game.addChild(nextWaveButtonContainer);
var towerTypes = ['cannon', 'rifle', 'flame', 'Rocket', 'gumbomb', 'Toxin'];
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 () {
	// Only run game logic if the game has started
	if (!gameStarted || showingIntro) {
		return;
	}
	if (currentWave !== lastWave) {
		if (musicOn) {
			// Only play music if music is enabled
			if (currentWave === 10) {
				LK.playMusic('Bossmusic1');
			} else if (currentWave === 11) {
				LK.playMusic('Gamemusic2');
			} else if (currentWave === 20) {
				LK.playMusic('Bossmusic2');
			} else if (currentWave === 21) {
				LK.playMusic('gamemusic3');
			} else if (currentWave === 30) {
				LK.playMusic('Bossmusic3');
			} else if (currentWave === 31) {
				LK.playMusic('Gamemusic4');
			} else if (currentWave === 40) {
				LK.playMusic('Bossmusic4');
			} else if (currentWave === 41) {
				LK.playMusic('Gamemusic5');
			} else if (currentWave === 50) {
				LK.playMusic('Bossmusic5');
			}
		}
		lastWave = currentWave;
	}
	if (waveInProgress) {
		if (!waveSpawned) {
			waveSpawned = true;
			// Get wave type and enemy count from the wave indicator
			var waveType = waveIndicator.getWaveType(currentWave);
			var enemyCount = waveIndicator.getEnemyCount(currentWave);
			// Check if this is a boss wave
			var isBossWave = currentWave % 10 === 0 && currentWave > 0;
			if (isBossWave && waveType !== 'swarm') {
				// Boss waves have just 1 enemy regardless of what the wave indicator says
				enemyCount = 1;
				// Show boss announcement
				var notification = game.addChild(new Notification("⚠️ BOSS WAVE! ⚠️"));
				notification.x = 2048 / 2;
				notification.y = 2732 / 2;
			}
			// 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);
				}
				// Enemy scaling is now handled in Enemy constructor based on currentWave
				// This ensures consistent scaling every 5 waves for all enemy types
				// All enemy types now spawn in the middle 6 tiles at the top spacing
				var gridWidth = 24;
				var midPoint = Math.floor(gridWidth / 2); // 12
				// Find a column that isn't occupied by another enemy that's not yet in view
				var availableColumns = [];
				for (var col = midPoint - 3; col < midPoint + 3; col++) {
					var columnOccupied = false;
					// Check if any enemy is already in this column but not yet in view
					for (var e = 0; e < enemies.length; e++) {
						if (enemies[e].cellX === col && enemies[e].currentCellY < 4) {
							columnOccupied = true;
							break;
						}
					}
					if (!columnOccupied) {
						availableColumns.push(col);
					}
				}
				// If all columns are occupied, use original random method
				var spawnX;
				if (availableColumns.length > 0) {
					// Choose a random unoccupied column
					spawnX = availableColumns[Math.floor(Math.random() * availableColumns.length)];
				} else {
					// Fallback to random if all columns are occupied
					spawnX = midPoint - 3 + Math.floor(Math.random() * 6); // x from 9 to 14
				}
				var spawnY = -1 - Math.random() * 5; // Random distance above the grid for spreading
				enemy.cellX = spawnX;
				enemy.cellY = 5; // Position after entry
				enemy.currentCellX = spawnX;
				enemy.currentCellY = spawnY;
				enemy.waveNumber = currentWave;
				enemies.push(enemy);
			}
		}
		var currentWaveEnemiesRemaining = false;
		for (var i = 0; i < enemies.length; i++) {
			if (enemies[i].waveNumber === currentWave) {
				currentWaveEnemiesRemaining = true;
				break;
			}
		}
		if (waveSpawned && !currentWaveEnemiesRemaining) {
			waveInProgress = false;
			waveSpawned = false;
		}
	}
	for (var a = enemies.length - 1; a >= 0; a--) {
		var enemy = enemies[a];
		if (enemy.health <= 0) {
			for (var i = 0; i < enemy.bulletsTargetingThis.length; i++) {
				var bullet = enemy.bulletsTargetingThis[i];
				bullet.targetEnemy = null;
			}
			// Boss enemies give more crystals and score - increased rewards
			var crystalsEarned = enemy.isBoss ? Math.floor(25 + (enemy.waveNumber - 1) * 1.5) : Math.floor(3 + (enemy.waveNumber - 1) * 0.4);
			var crystalIndicator = new CrystalIndicator(crystalsEarned, enemy.x, enemy.y);
			game.addChild(crystalIndicator);
			setCrystals(crystals + crystalsEarned);
			// 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! +" + crystalsEarned + " crystal!"));
				notification.x = 2048 / 2;
				notification.y = 2732 / 2;
			}
			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) {
				// Save high score before showing game over
				addHighScore(score);
				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();
	}
	if (currentWave >= totalWaves && enemies.length === 0 && !waveInProgress) {
		// Save high score before showing you win
		addHighScore(score);
		LK.showYouWin();
	}
	// Ship spawning logic
	// Spawn ship1 or ship2 randomly, on average every 20 seconds
	if (Math.random() < 1 / 1200) {
		// 1200 ticks = 20 seconds at 60 FPS
		// Randomly select ship type (1 or 2)
		var shipType = Math.floor(Math.random() * 2) + 1;
		var ship = new Ship(shipType);
		// Add ship to game at a lower layer so it appears behind game elements
		game.addChildAt(ship, 1); // Just above background
		ships.push(ship);
	}
	// Spawn ship3 every 40 seconds
	if (LK.ticks - lastShip3Spawn >= ship3SpawnInterval) {
		lastShip3Spawn = LK.ticks;
		var ship = new Ship(3);
		// Add ship to game at a lower layer so it appears behind game elements
		game.addChildAt(ship, 1); // Just above background
		ships.push(ship);
	}
	// Update and cleanup ships
	for (var i = ships.length - 1; i >= 0; i--) {
		if (!ships[i].parent) {
			ships.splice(i, 1);
		}
	}
}; ===================================================================
--- original.js
+++ change.js
@@ -2961,9 +2961,9 @@
 		anchorX: 0,
 		anchorY: 0.5
 	});
 	lightBar.name = "lightBar";
-	lightBar.x = lightIcon.x + 200;
+	lightBar.x = lightIcon.x + 150;
 	lightBar.y = -300;
 	var lightButton = settingMenu.attachAsset('LightButton', {
 		anchorX: 0.5,
 		anchorY: 0.5
 
 Pack top square of towers facing the screen to use it as a grid. future. seen from above. HD colors.
 Pack top square of tower facing the screen to use it as a grid. future. seen from above. HD colors.
 
 
 
 
 
 
 
 
 
 
 
 different cyber flying enemy from the front side facing camera. HD colors. separated In-Game asset. 2d. High contrast. No shadows
 different cyber electro flying enemy from the front side facing camera. HD colors. separated In-Game asset. 2d. High contrast. No shadows
 different cyborg electro enemy from the front side facing camera. HD colors. separated In-Game asset. 2d. High contrast. No shadows
 
 
 different cyber small enemy from the front side facing camera. HD colors. separated In-Game asset. 2d. High contrast. No shadows
 different cyber spider small enemy from the front side facing camera. HD colors. separated In-Game asset. 2d. High contrast. No shadows
 different cyber big Robot enemy from the front side facing camera. HD colors. separated In-Game asset. 2d. High contrast. No shadows
 different big cyber snake Robot enemy from the front side facing camera. HD colors. separated In-Game asset. 2d. High contrast. No shadows
 Fullscreen modern App Store landscape banner, 16:9, high definition, HD colors. for a future game of tower defense with cyberpunk and abstract style titled "Cyber Towers" without description "Defend the exit way in final path from different enemies Robots, cyborgs, cyber robot snakes, cyber spiders, flying cyber robots, by placing towers each tower have different weapon then the others, cannon, rifle, flame, rocket, gum bomb, toxin bomb. don't let them pass through the path!". with text on the middle of the banner "Cyber Towers"!
 
 
 
 
 
 
 Police cyber flying Ship assets without 'police' text, Cyberpunk, Abstract, Futuer, HD colors, Different colors. Horizontal. seen from a side. In-Game asset. 2d. High contrast. No shadows
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 Ability icon 'healing' . cyberpunk. abstract. Futuer. different colors. HD colors
 Ability icon 'weakness curse' . cyberpunk. abstract. Futuer. different colors. HD colors
 Ability icon 'Lighting storm' . cyberpunk. abstract. Futuer. different colors. HD colors
 Ability icon 'Dust storm' . cyberpunk. abstract. Futuer. different colors. HD colors
 Ability icon 'Water flood' . cyberpunk. abstract. Futuer. different colors. HD colors
 Ability icon 'Fire ring' . cyberpunk. abstract. Futuer. different colors. HD colors
 Ability icon 'Ice freeze' . cyberpunk. abstract. Futuer. different colors. HD colors
 Ability icon 'Shield Boost' . cyberpunk. abstract. Futuer. different colors. HD colors
 Ability icon 'Ultimate Storm' . cyberpunk. abstract. Futuer. different colors. HD colors
 
 placmentsound1
Sound effect
placmentsound2
Sound effect
placmentsound3
Sound effect
placmentsound4
Sound effect
placmentsound5
Sound effect
placmentsound6
Sound effect
Intromusic
Music
Bulletsound
Sound effect
Riflebulletsound
Sound effect
Flamesound
Sound effect
Rocketsound
Sound effect
Gumbombsound
Sound effect
Gamemusic2
Music
gamemusic3
Music
Gamemusic4
Music
Gamemusic5
Music
Bossmusic1
Music
Bossmusic2
Music
Bossmusic3
Music
Bossmusic4
Music
Bossmusic5
Music
Ship1sound
Sound effect
Ship2sound
Sound effect
Ship3sound
Sound effect
Gamemusic1
Music
Toxinbombsound
Sound effect
Closebuttonsound1
Sound effect
Useabilitysound1
Sound effect
Abilitybuttonsound1
Sound effect
Startbuttonsound1
Sound effect
Bigrobots_damage_sound1
Sound effect
Cybersnake_damage_sound1
Sound effect
Cyborg_Damage_sound1
Sound effect
Electro_Damage_sound1
Sound effect
Eye_Damage_sound1
Sound effect
Firo_Damage_sound1
Sound effect
Robot_Damage_sound1
Sound effect