/**** 
* Plugins
****/ 
var tween = LK.import("@upit/tween.v1");
/**** 
* Classes
****/ 
// Bullet class
var Bullet = Container.expand(function () {
	var self = Container.call(this);
	self.target = null;
	self.speed = 18;
	self.damage = 1;
	self.type = 1;
	self.active = true;
	self.shooter = null;
	self.setType = function (type) {
		self.type = type;
		var assetId = 'bullet' + type;
		self.attachAsset(assetId, {
			anchorX: 0.5,
			anchorY: 0.5
		});
	};
	self.update = function () {
		if (!self.active || !self.target || self.target.dead) {
			self.destroy();
			return;
		}
		// Move toward target
		var dx = self.target.x - self.x;
		var dy = self.target.y - self.y;
		var dist = Math.sqrt(dx * dx + dy * dy);
		if (dist < 30) {
			// Hit
			self.target.takeDamage(self.damage);
			self.destroy();
			return;
		}
		var step = self.speed / (dist || 1);
		self.x += dx * step;
		self.y += dy * step;
	};
	return self;
});
// Soldier class
var Soldier = Container.expand(function () {
	var self = Container.call(this);
	self.type = 1;
	self.fireRate = 60; // ticks between shots
	self.damage = 1;
	self.range = 350;
	self.cooldown = 0;
	self.level = 1;
	self.wallSpot = null;
	self.setType = function (type) {
		self.type = type;
		// Remove all previous children (visuals)
		while (self.children && self.children.length > 0) {
			self.removeChild(self.children[0]);
		}
		var assetId = 'soldier' + type;
		self.attachAsset(assetId, {
			anchorX: 0.5,
			anchorY: 0.5
		});
		// Set stats
		if (type === 1) {
			self.fireRate = 60;
			self.damage = 1;
			self.range = 350;
			self.dualHit = false;
			self.money = 3;
		}
		if (type === 2) {
			self.fireRate = 40;
			self.damage = 1;
			self.range = 300;
			self.dualHit = false;
			self.money = 10;
		}
		if (type === 3) {
			self.fireRate = 90;
			self.damage = 3;
			self.range = 320;
			self.dualHit = false;
			self.money = 20;
		}
		if (type === 4) {
			self.fireRate = 60;
			self.damage = 8;
			self.range = 400;
			self.dualHit = false; // 4th upgrade: single hit
			self.money = 50;
		}
	};
	self.update = function () {
		if (self.cooldown > 0) {
			self.cooldown--;
			return;
		}
		// Find nearest zombie in range
		var nearest = null;
		var minDist = 99999;
		for (var i = 0; i < zombies.length; ++i) {
			var z = zombies[i];
			if (z.dead) continue;
			var dx = z.x - self.x;
			var dy = z.y - self.y;
			var d = Math.sqrt(dx * dx + dy * dy);
			if (d < self.range && d < minDist) {
				minDist = d;
				nearest = z;
			}
		}
		if (nearest) {
			// Shoot
			if (self.type === 4 && !self.dualHit) {
				// 4th upgrade: single hit, 8 dmg
				var bullet = new Bullet();
				bullet.setType(self.type);
				bullet.x = self.x;
				bullet.y = self.y;
				bullet.target = nearest;
				bullet.damage = self.damage;
				bullet.shooter = self;
				bullets.push(bullet);
				game.addChild(bullet);
				self.cooldown = self.fireRate;
			} else if (self.type === 4 && self.dualHit) {
				// (legacy, should not occur, but keep for safety)
				for (var i = 0; i < 2; ++i) {
					var bullet = new Bullet();
					bullet.setType(self.type);
					bullet.x = self.x + (i === 0 ? -30 : 30);
					bullet.y = self.y;
					bullet.target = nearest;
					bullet.damage = self.damage;
					bullet.shooter = self;
					bullets.push(bullet);
					game.addChild(bullet);
				}
				self.cooldown = self.fireRate;
			} else {
				var bullet = new Bullet();
				bullet.setType(self.type);
				bullet.x = self.x;
				bullet.y = self.y;
				bullet.target = nearest;
				bullet.damage = self.damage;
				bullet.shooter = self;
				bullets.push(bullet);
				game.addChild(bullet);
				self.cooldown = self.fireRate;
			}
		}
	};
	return self;
});
// Zombie class
var Zombie = Container.expand(function () {
	var self = Container.call(this);
	self.isRed = false; // Will be set after construction if needed
	var sprite = self.attachAsset('zombie', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	// Health bar background
	var hpBarBg = LK.getAsset('pathnode', {
		anchorX: 0.5,
		anchorY: 0.5,
		width: 60 * 1.5,
		height: 14 * 1.5,
		y: -60,
		alpha: 0.25,
		color: 0x000000
	});
	self.setRed = function () {
		if (!self.isRed) {
			self.isRed = true;
			// Remove old sprite
			if (sprite && sprite.parent === self) self.removeChild(sprite);
			// Attach red zombie asset
			var redSprite = self.attachAsset('zombieRed', {
				anchorX: 0.5,
				anchorY: 0.5
			});
		}
	};
	// Add yellow zombie support
	self.isYellow = false;
	self.setYellow = function () {
		if (!self.isYellow) {
			self.isYellow = true;
			// Remove old sprite
			if (sprite && sprite.parent === self) self.removeChild(sprite);
			// Attach yellow zombie asset
			var yellowSprite = self.attachAsset('zombieYellow', {
				anchorX: 0.5,
				anchorY: 0.5
			});
		}
	};
	self.addChild(hpBarBg);
	// Health bar foreground
	var hpBar = LK.getAsset('pathnode', {
		anchorX: 0.5,
		anchorY: 0.5,
		width: 56 * 1.5,
		height: 10 * 1.5,
		y: -60,
		color: 0x44ff44,
		// green
		alpha: 0.85
	});
	self.addChild(hpBar);
	self.speed = 1.2;
	self.maxHp = 5;
	self.hp = 5;
	self.reward = 1;
	self.pathIndex = 0;
	self.progress = 0; // 0-1 between path[pathIndex] and path[pathIndex+1]
	self.targetX = 0;
	self.targetY = 0;
	self.dead = false;
	self.setStats = function (hp, speed, reward) {
		self.maxHp = hp;
		self.hp = hp;
		self.speed = speed;
		self.reward = reward;
		// Update health bar on spawn
		if (hpBar) {
			hpBar.width = 56 * 1.5;
		}
	};
	self.setPathIndex = function (idx) {
		self.pathIndex = idx;
		self.progress = 0;
	};
	self.update = function () {
		if (self.dead) return;
		// Move along path
		if (self.pathIndex >= path.length - 1) return;
		var from = path[self.pathIndex];
		var to = path[self.pathIndex + 1];
		var dx = to.x - from.x;
		var dy = to.y - from.y;
		var dist = Math.sqrt(dx * dx + dy * dy);
		if (dist === 0) dist = 1;
		var step = self.speed / dist;
		self.progress += step;
		if (self.progress >= 1) {
			self.pathIndex++;
			self.progress = 0;
			if (self.pathIndex >= path.length - 1) {
				// At base
				self.x = to.x;
				self.y = to.y;
				self.reachedBase = true;
				return;
			}
		}
		var from = path[self.pathIndex];
		var to = path[self.pathIndex + 1];
		self.x = from.x + (to.x - from.x) * self.progress;
		self.y = from.y + (to.y - from.y) * self.progress;
		// Update health bar position and width
		if (hpBar && hpBarBg) {
			hpBarBg.x = 0;
			hpBarBg.y = -60;
			hpBar.x = 0;
			hpBar.y = -60;
			var ratio = Math.max(0, Math.min(1, self.hp / self.maxHp));
			hpBar.width = 56 * 1.5 * ratio;
			hpBar.color = 0x44ff44; // always green
		}
	};
	self.takeDamage = function (dmg) {
		self.hp -= dmg;
		if (self.hp <= 0 && !self.dead) {
			self.dead = true;
			// Animate fade out
			tween(self, {
				alpha: 0
			}, {
				duration: 200,
				onFinish: function onFinish() {
					self.destroy();
				}
			});
		}
	};
	return self;
});
/**** 
* Initialize Game
****/ 
var game = new LK.Game({
	backgroundColor: 0x000000
});
/**** 
* Game Code
****/ 
// Intro screen overlay
var introOverlay = new Container();
introOverlay.visible = true;
// Fullscreen black background
var introBg = LK.getAsset('pathnode', {
	anchorX: 0.5,
	anchorY: 0.5,
	x: 2048 / 2,
	y: 2732 / 2,
	width: 2048,
	height: 2732,
	color: 0x000000,
	alpha: 1
});
introOverlay.addChild(introBg);
// (Instructions removed for fully black intro screen)
// "Welcome" text centered at top
var welcomeTxt = new Text2('Welcome', {
	size: 220,
	fill: 0xffffff
});
welcomeTxt.anchor.set(0.5, 0);
welcomeTxt.x = 2048 / 2;
welcomeTxt.y = 400;
introOverlay.addChild(welcomeTxt);
// Start button centered at bottom
var startBtn = new Text2('Start', {
	size: 140,
	fill: 0xffffff
});
startBtn.anchor.set(0.5, 0.5);
startBtn.x = 2048 / 2;
startBtn.y = 2732 - 400;
introOverlay.addChild(startBtn);
// Start button handler
startBtn.down = function (x, y, obj) {
	introOverlay.visible = false;
};
// Add overlay to game
game.addChild(introOverlay);
// Path and wall spot colors
// Path definition (nodes spaced further apart horizontally for a wider and longer path)
var path = [{
	x: 500,
	y: 600
},
// Start
{
	x: 500,
	y: 1200
}, {
	x: 500,
	y: 1800
},
// Extend downward
{
	x: 500,
	y: 2300
},
// Curve right
{
	x: 900,
	y: 2300
}, {
	x: 1300,
	y: 2300
},
// Curve up
{
	x: 1300,
	y: 1800
}, {
	x: 1300,
	y: 1200
},
// Curve right again
{
	x: 1750,
	y: 1200
}, {
	x: 1750,
	y: 1800
},
// Curve up to base
{
	x: 1750,
	y: 2300
}, {
	x: 1750,
	y: 2732 - 200 // near bottom edge, but not out of bounds
}];
// Wall spot positions (10 unit placement positions, adjacent to the new, longer path)
var wallSpots = [{
	x: path[1].x + 120,
	y: path[1].y
}, {
	x: path[2].x + 120,
	y: path[2].y
}, {
	x: path[3].x + 120,
	y: path[3].y
}, {
	x: path[4].x,
	y: path[4].y - 120
}, {
	x: path[5].x,
	y: path[5].y - 120
}, {
	x: path[6].x - 120,
	y: path[6].y
}, {
	x: path[7].x - 120,
	y: path[7].y
}, {
	x: path[8].x - 120,
	y: path[8].y
}, {
	x: path[9].x,
	y: path[9].y + 120
}, {
	x: path[10].x,
	y: path[10].y + 120
}];
// Wall spot state
var wallSpotStates = [];
for (var i = 0; i < wallSpots.length; ++i) wallSpotStates[i] = null;
// Game state
var zombies = [];
var bullets = [];
var soldiers = [];
var coins = 10;
var baseHp = 10;
var wave = 1;
var spawnTimer = 0;
var spawnIdx = 0;
var zombiesToSpawn = 0;
var waveInProgress = false;
var baseNode = null;
var selectedSoldierType = 1;
var placingSoldier = false;
var placingSoldierIdx = -1;
var gameOver = false;
// GUI
var coinTxt = new Text2('Coins: 10', {
	size: 80,
	fill: 0xFFE680
});
coinTxt.anchor.set(0, 0);
LK.gui.top.addChild(coinTxt);
var baseHpTxt = new Text2('Base: 10', {
	size: 80,
	fill: 0xFFAAAA
});
baseHpTxt.anchor.set(1, 0);
LK.gui.topRight.addChild(baseHpTxt);
var waveTxt = new Text2('Wave: 1', {
	size: 80,
	fill: 0xAAFFAA
});
waveTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(waveTxt);
var soldierBtns = [];
var soldierCosts = [3, 10, 20, 50];
var soldierNames = ['Basic', 'Rapid', 'Heavy', 'Red Arm'];
var soldierBtnY = 0;
for (var i = 0; i < 4; ++i) {
	var btn = new Text2(soldierNames[i] + "\n" + soldierCosts[i] + "c", {
		size: 60,
		fill: "#fff"
	});
	btn.anchor.set(0.5, 0);
	btn.x = 300 + i * 300;
	btn.y = 0;
	LK.gui.bottom.addChild(btn);
	soldierBtns.push(btn);
}
// Only 4 buttons shown, 4 upgrades available
// Draw path (as faint nodes)
for (var i = 0; i < path.length; ++i) {
	var node = LK.getAsset('pathnode', {
		anchorX: 0.5,
		anchorY: 0.5,
		x: path[i].x,
		y: path[i].y,
		alpha: 0.18,
		color: 0x8B5A2B
	});
	game.addChild(node);
	// Connect lines (not possible, so just nodes)
}
// Draw wall spots
var wallSpotNodes = [];
for (var i = 0; i < wallSpots.length; ++i) {
	var ws = LK.getAsset('wallspot', {
		anchorX: 0.5,
		anchorY: 0.5,
		x: wallSpots[i].x,
		y: wallSpots[i].y,
		alpha: 0.5,
		color: 0x0000ff // blue
	});
	ws.idx = i;
	wallSpotNodes.push(ws);
	game.addChild(ws);
}
// Draw base
baseNode = LK.getAsset('base', {
	anchorX: 0.5,
	anchorY: 0.5,
	x: path[path.length - 1].x,
	y: path[path.length - 1].y
});
game.addChild(baseNode);
// Handle soldier button selection
for (var i = 0; i < soldierBtns.length; ++i) {
	(function (idx) {
		soldierBtns[idx].down = function (x, y, obj) {
			selectedSoldierType = idx + 1;
			for (var j = 0; j < soldierBtns.length; ++j) {
				soldierBtns[j].setStyle({
					fill: j === idx ? "#ffff00" : "#fff"
				});
			}
		};
	})(i);
}
soldierBtns[0].setStyle({
	fill: 0xFFFF00
});
// Place soldier on wall spot
for (var i = 0; i < wallSpotNodes.length; ++i) {
	(function (idx) {
		wallSpotNodes[idx].down = function (x, y, obj) {
			if (gameOver) return;
			if (wallSpotStates[idx] === null) {
				// Place soldier if empty
				var cost = soldierCosts[selectedSoldierType - 1];
				if (coins < cost) {
					// Flash red
					tween(wallSpotNodes[idx], {
						tint: 0xff0000
					}, {
						duration: 200,
						onFinish: function onFinish() {
							tween(wallSpotNodes[idx], {
								tint: 0x888888
							}, {
								duration: 200
							});
						}
					});
					return;
				}
				coins -= cost;
				coinTxt.setText('Coins: ' + coins);
				// Place soldier
				var s = new Soldier();
				s.setType(selectedSoldierType);
				s.x = wallSpots[idx].x;
				s.y = wallSpots[idx].y;
				s.wallSpot = idx;
				wallSpotStates[idx] = s;
				soldiers.push(s);
				game.addChild(s);
				// Add upgrade text overlay
				var upgradeTxt = new Text2('Upgrade', {
					size: 40,
					fill: 0x00FF00
				});
				upgradeTxt.anchor.set(0.5, 1);
				upgradeTxt.x = s.x;
				upgradeTxt.y = s.y - 60;
				upgradeTxt.visible = false;
				s.upgradeTxt = upgradeTxt;
				game.addChild(upgradeTxt);
				// Add tap handler for upgrade
				s.down = function (x, y, obj) {
					if (gameOver) return;
					// Show upgrade text if not max level
					if (s.level < 4) {
						s.upgradeTxt.visible = true;
						// Hide after 1s
						LK.setTimeout(function () {
							if (s.upgradeTxt) s.upgradeTxt.visible = false;
						}, 1000);
					}
					// If already at max, do nothing
				};
				// Add tap handler for upgrade text
				upgradeTxt.down = function (x, y, obj) {
					if (gameOver) return;
					if (s.level >= 4) return;
					// Upgrade cost: Level 2 = 10, Level 3 = 20, Level 4 = 50
					var upgradeCost = 0;
					if (s.level === 1) upgradeCost = 10;else if (s.level === 2) upgradeCost = 20;else if (s.level === 3) upgradeCost = 50;
					if (coins < upgradeCost) {
						// Flash red
						tween(upgradeTxt, {
							fill: 0xff0000
						}, {
							duration: 200,
							onFinish: function onFinish() {
								tween(upgradeTxt, {
									fill: 0x00ff00
								}, {
									duration: 200
								});
							}
						});
						return;
					}
					coins -= upgradeCost;
					coinTxt.setText('Coins: ' + coins);
					s.level += 1;
					// Change type and visuals on upgrade
					if (s.level === 2) {
						s.setType(2);
					}
					if (s.level === 3) {
						s.setType(3);
					}
					if (s.level === 4) {
						s.setType(4);
					}
					// Show upgraded text
					upgradeTxt.setText('Upgraded!');
					upgradeTxt.fill = "#ffff00";
					LK.setTimeout(function () {
						if (upgradeTxt) {
							upgradeTxt.setText('Upgrade');
							upgradeTxt.fill = "#00ff00";
							upgradeTxt.visible = false;
						}
					}, 1000);
				};
			} else {
				// If already a soldier, tap to show upgrade
				var s = wallSpotStates[idx];
				if (s && s.upgradeTxt && s.level < 5) {
					s.upgradeTxt.visible = true;
					LK.setTimeout(function () {
						if (s.upgradeTxt) s.upgradeTxt.visible = false;
					}, 1000);
				}
			}
		};
	})(i);
}
// Start first wave
// Red zombie spawn plan: wave 10-20, 1 red per wave; wave 11, 2 reds; otherwise, 1 red per wave 1-10
var redZombiePlan = {}; // wave number -> array of 0/1 (1=red)
function setupRedZombiePlan() {
	// For waves 1-9: 1 red per wave
	for (var w = 1; w <= 9; ++w) {
		var arr = [];
		var totalZ = 5 + w * 2;
		for (var i = 0; i < totalZ; ++i) arr.push(0);
		arr[0] = 1; // Always first zombie is red (could randomize if desired)
		// Shuffle so red is not always first
		for (var i = arr.length - 1; i > 0; --i) {
			var j = Math.floor(Math.random() * (i + 1));
			var tmp = arr[i];
			arr[i] = arr[j];
			arr[j] = tmp;
		}
		redZombiePlan[w] = arr;
	}
	// Wave 10: 1 red
	var arr10 = [];
	var totalZ10 = 5 + 10 * 2;
	for (var i = 0; i < totalZ10; ++i) arr10.push(0);
	arr10[0] = 1;
	for (var i = arr10.length - 1; i > 0; --i) {
		var j = Math.floor(Math.random() * (i + 1));
		var tmp = arr10[i];
		arr10[i] = arr10[j];
		arr10[j] = tmp;
	}
	redZombiePlan[10] = arr10;
	// Wave 11: 2 reds
	var arr11 = [];
	var totalZ11 = 5 + 11 * 2;
	for (var i = 0; i < totalZ11; ++i) arr11.push(0);
	arr11[0] = 1;
	arr11[1] = 1;
	for (var i = arr11.length - 1; i > 0; --i) {
		var j = Math.floor(Math.random() * (i + 1));
		var tmp = arr11[i];
		arr11[i] = arr11[j];
		arr11[j] = tmp;
	}
	redZombiePlan[11] = arr11;
	// Waves 12-20: 1 red per wave
	for (var w = 12; w <= 20; ++w) {
		var arr = [];
		var totalZ = 5 + w * 2;
		for (var i = 0; i < totalZ; ++i) arr.push(0);
		arr[0] = 1;
		for (var i = arr.length - 1; i > 0; --i) {
			var j = Math.floor(Math.random() * (i + 1));
			var tmp = arr[i];
			arr[i] = arr[j];
			arr[j] = tmp;
		}
		redZombiePlan[w] = arr;
	}
}
setupRedZombiePlan();
function startWave() {
	waveInProgress = true;
	spawnIdx = 0;
	zombiesToSpawn = 5 + wave * 2;
	spawnTimer = 0;
	waveTxt.setText('Wave: ' + wave);
}
startWave();
// Spawn zombie
function spawnZombie() {
	var z = new Zombie();
	// Stats scale with wave
	var hp = 5; // Green zombie health is always 5
	var speed = 1.2 + 0.08 * wave;
	var reward = 1 + Math.floor(wave / 2);
	// Red zombie logic
	var makeRed = false;
	var makeYellow = false;
	if (wave >= 15 && wave <= 20) {
		// Only red zombies between wave 15 and 20
		makeRed = true;
	} else if (wave >= 1 && wave <= 20 && redZombiePlan[wave]) {
		if (spawnIdx < redZombiePlan[wave].length && redZombiePlan[wave][spawnIdx] === 1) {
			makeRed = true;
		}
	} else if (wave > 20 && wave < 31) {
		// Between wave 21 and 30, spawn a yellow zombie at a random index per wave
		// Only one yellow zombie per wave, at a random spawnIdx
		if (typeof spawnZombie.yellowIdx === "undefined" || spawnZombie.yellowWave !== wave) {
			// Pick a random index for yellow zombie for this wave
			spawnZombie.yellowIdx = 20 + Math.floor(Math.random() * Math.max(1, 5 + wave * 2 - 20));
			spawnZombie.yellowWave = wave;
		}
		if (spawnIdx === spawnZombie.yellowIdx) {
			makeYellow = true;
		}
		makeRed = false;
	} else if (wave > 30) {
		// After wave 30, only green zombies
		makeRed = false;
		makeYellow = false;
	}
	if (makeRed) {
		z.setRed();
		hp = 10; // Red zombie health is always 10
		reward = reward * 2;
	}
	if (makeYellow) {
		z.setYellow();
		hp = 20; // Yellow zombie health is 20
		reward = reward * 3;
	}
	z.setStats(hp, speed, reward);
	z.setPathIndex(0);
	z.x = path[0].x;
	z.y = path[0].y;
	zombies.push(z);
	game.addChild(z);
}
// Main game update
game.update = function () {
	if (gameOver) return;
	// Spawn zombies for wave
	if (waveInProgress && spawnIdx < zombiesToSpawn) {
		if (spawnTimer <= 0) {
			spawnZombie();
			spawnIdx++;
			spawnTimer = 40 - Math.min(wave * 2, 30); // Faster spawns later
		} else {
			spawnTimer--;
		}
	}
	// Update zombies
	for (var i = zombies.length - 1; i >= 0; --i) {
		var z = zombies[i];
		z.update();
		if (z.dead) {
			// Reward coins
			if (z.isRed) {
				coins += 2;
			} else {
				coins += 1;
			}
			coinTxt.setText('Coins: ' + coins);
			zombies.splice(i, 1);
			continue;
		}
		if (z.pathIndex >= path.length - 1 && !z.dead) {
			// Reached base
			baseHp--;
			baseHpTxt.setText('Base: ' + baseHp);
			LK.effects.flashObject(baseNode, 0xff0000, 400);
			z.dead = true;
			z.destroy();
			zombies.splice(i, 1);
			if (baseHp <= 0) {
				gameOver = true;
				LK.effects.flashScreen(0xff0000, 1200);
				LK.showGameOver();
				return;
			}
		}
	}
	// Update bullets
	for (var i = bullets.length - 1; i >= 0; --i) {
		var b = bullets[i];
		b.update();
		if (!b.parent) {
			bullets.splice(i, 1);
		}
	}
	// Update soldiers
	for (var i = 0; i < soldiers.length; ++i) {
		soldiers[i].update();
	}
	// Check if wave is over
	if (waveInProgress && spawnIdx >= zombiesToSpawn && zombies.length === 0) {
		waveInProgress = false;
		// Next wave after short delay
		LK.setTimeout(function () {
			wave++;
			startWave();
		}, 1200);
	}
};
// Allow dragging soldiers to swap positions (optional, MVP skips this)
// Prevent placing elements in top left 100x100 (already handled by GUI placement)
// No background, music, or sound per requirements
// No pause, leaderboard, or game over handling (LK does this) /**** 
* Plugins
****/ 
var tween = LK.import("@upit/tween.v1");
/**** 
* Classes
****/ 
// Bullet class
var Bullet = Container.expand(function () {
	var self = Container.call(this);
	self.target = null;
	self.speed = 18;
	self.damage = 1;
	self.type = 1;
	self.active = true;
	self.shooter = null;
	self.setType = function (type) {
		self.type = type;
		var assetId = 'bullet' + type;
		self.attachAsset(assetId, {
			anchorX: 0.5,
			anchorY: 0.5
		});
	};
	self.update = function () {
		if (!self.active || !self.target || self.target.dead) {
			self.destroy();
			return;
		}
		// Move toward target
		var dx = self.target.x - self.x;
		var dy = self.target.y - self.y;
		var dist = Math.sqrt(dx * dx + dy * dy);
		if (dist < 30) {
			// Hit
			self.target.takeDamage(self.damage);
			self.destroy();
			return;
		}
		var step = self.speed / (dist || 1);
		self.x += dx * step;
		self.y += dy * step;
	};
	return self;
});
// Soldier class
var Soldier = Container.expand(function () {
	var self = Container.call(this);
	self.type = 1;
	self.fireRate = 60; // ticks between shots
	self.damage = 1;
	self.range = 350;
	self.cooldown = 0;
	self.level = 1;
	self.wallSpot = null;
	self.setType = function (type) {
		self.type = type;
		// Remove all previous children (visuals)
		while (self.children && self.children.length > 0) {
			self.removeChild(self.children[0]);
		}
		var assetId = 'soldier' + type;
		self.attachAsset(assetId, {
			anchorX: 0.5,
			anchorY: 0.5
		});
		// Set stats
		if (type === 1) {
			self.fireRate = 60;
			self.damage = 1;
			self.range = 350;
			self.dualHit = false;
			self.money = 3;
		}
		if (type === 2) {
			self.fireRate = 40;
			self.damage = 1;
			self.range = 300;
			self.dualHit = false;
			self.money = 10;
		}
		if (type === 3) {
			self.fireRate = 90;
			self.damage = 3;
			self.range = 320;
			self.dualHit = false;
			self.money = 20;
		}
		if (type === 4) {
			self.fireRate = 60;
			self.damage = 8;
			self.range = 400;
			self.dualHit = false; // 4th upgrade: single hit
			self.money = 50;
		}
	};
	self.update = function () {
		if (self.cooldown > 0) {
			self.cooldown--;
			return;
		}
		// Find nearest zombie in range
		var nearest = null;
		var minDist = 99999;
		for (var i = 0; i < zombies.length; ++i) {
			var z = zombies[i];
			if (z.dead) continue;
			var dx = z.x - self.x;
			var dy = z.y - self.y;
			var d = Math.sqrt(dx * dx + dy * dy);
			if (d < self.range && d < minDist) {
				minDist = d;
				nearest = z;
			}
		}
		if (nearest) {
			// Shoot
			if (self.type === 4 && !self.dualHit) {
				// 4th upgrade: single hit, 8 dmg
				var bullet = new Bullet();
				bullet.setType(self.type);
				bullet.x = self.x;
				bullet.y = self.y;
				bullet.target = nearest;
				bullet.damage = self.damage;
				bullet.shooter = self;
				bullets.push(bullet);
				game.addChild(bullet);
				self.cooldown = self.fireRate;
			} else if (self.type === 4 && self.dualHit) {
				// (legacy, should not occur, but keep for safety)
				for (var i = 0; i < 2; ++i) {
					var bullet = new Bullet();
					bullet.setType(self.type);
					bullet.x = self.x + (i === 0 ? -30 : 30);
					bullet.y = self.y;
					bullet.target = nearest;
					bullet.damage = self.damage;
					bullet.shooter = self;
					bullets.push(bullet);
					game.addChild(bullet);
				}
				self.cooldown = self.fireRate;
			} else {
				var bullet = new Bullet();
				bullet.setType(self.type);
				bullet.x = self.x;
				bullet.y = self.y;
				bullet.target = nearest;
				bullet.damage = self.damage;
				bullet.shooter = self;
				bullets.push(bullet);
				game.addChild(bullet);
				self.cooldown = self.fireRate;
			}
		}
	};
	return self;
});
// Zombie class
var Zombie = Container.expand(function () {
	var self = Container.call(this);
	self.isRed = false; // Will be set after construction if needed
	var sprite = self.attachAsset('zombie', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	// Health bar background
	var hpBarBg = LK.getAsset('pathnode', {
		anchorX: 0.5,
		anchorY: 0.5,
		width: 60 * 1.5,
		height: 14 * 1.5,
		y: -60,
		alpha: 0.25,
		color: 0x000000
	});
	self.setRed = function () {
		if (!self.isRed) {
			self.isRed = true;
			// Remove old sprite
			if (sprite && sprite.parent === self) self.removeChild(sprite);
			// Attach red zombie asset
			var redSprite = self.attachAsset('zombieRed', {
				anchorX: 0.5,
				anchorY: 0.5
			});
		}
	};
	// Add yellow zombie support
	self.isYellow = false;
	self.setYellow = function () {
		if (!self.isYellow) {
			self.isYellow = true;
			// Remove old sprite
			if (sprite && sprite.parent === self) self.removeChild(sprite);
			// Attach yellow zombie asset
			var yellowSprite = self.attachAsset('zombieYellow', {
				anchorX: 0.5,
				anchorY: 0.5
			});
		}
	};
	self.addChild(hpBarBg);
	// Health bar foreground
	var hpBar = LK.getAsset('pathnode', {
		anchorX: 0.5,
		anchorY: 0.5,
		width: 56 * 1.5,
		height: 10 * 1.5,
		y: -60,
		color: 0x44ff44,
		// green
		alpha: 0.85
	});
	self.addChild(hpBar);
	self.speed = 1.2;
	self.maxHp = 5;
	self.hp = 5;
	self.reward = 1;
	self.pathIndex = 0;
	self.progress = 0; // 0-1 between path[pathIndex] and path[pathIndex+1]
	self.targetX = 0;
	self.targetY = 0;
	self.dead = false;
	self.setStats = function (hp, speed, reward) {
		self.maxHp = hp;
		self.hp = hp;
		self.speed = speed;
		self.reward = reward;
		// Update health bar on spawn
		if (hpBar) {
			hpBar.width = 56 * 1.5;
		}
	};
	self.setPathIndex = function (idx) {
		self.pathIndex = idx;
		self.progress = 0;
	};
	self.update = function () {
		if (self.dead) return;
		// Move along path
		if (self.pathIndex >= path.length - 1) return;
		var from = path[self.pathIndex];
		var to = path[self.pathIndex + 1];
		var dx = to.x - from.x;
		var dy = to.y - from.y;
		var dist = Math.sqrt(dx * dx + dy * dy);
		if (dist === 0) dist = 1;
		var step = self.speed / dist;
		self.progress += step;
		if (self.progress >= 1) {
			self.pathIndex++;
			self.progress = 0;
			if (self.pathIndex >= path.length - 1) {
				// At base
				self.x = to.x;
				self.y = to.y;
				self.reachedBase = true;
				return;
			}
		}
		var from = path[self.pathIndex];
		var to = path[self.pathIndex + 1];
		self.x = from.x + (to.x - from.x) * self.progress;
		self.y = from.y + (to.y - from.y) * self.progress;
		// Update health bar position and width
		if (hpBar && hpBarBg) {
			hpBarBg.x = 0;
			hpBarBg.y = -60;
			hpBar.x = 0;
			hpBar.y = -60;
			var ratio = Math.max(0, Math.min(1, self.hp / self.maxHp));
			hpBar.width = 56 * 1.5 * ratio;
			hpBar.color = 0x44ff44; // always green
		}
	};
	self.takeDamage = function (dmg) {
		self.hp -= dmg;
		if (self.hp <= 0 && !self.dead) {
			self.dead = true;
			// Animate fade out
			tween(self, {
				alpha: 0
			}, {
				duration: 200,
				onFinish: function onFinish() {
					self.destroy();
				}
			});
		}
	};
	return self;
});
/**** 
* Initialize Game
****/ 
var game = new LK.Game({
	backgroundColor: 0x000000
});
/**** 
* Game Code
****/ 
// Intro screen overlay
var introOverlay = new Container();
introOverlay.visible = true;
// Fullscreen black background
var introBg = LK.getAsset('pathnode', {
	anchorX: 0.5,
	anchorY: 0.5,
	x: 2048 / 2,
	y: 2732 / 2,
	width: 2048,
	height: 2732,
	color: 0x000000,
	alpha: 1
});
introOverlay.addChild(introBg);
// (Instructions removed for fully black intro screen)
// "Welcome" text centered at top
var welcomeTxt = new Text2('Welcome', {
	size: 220,
	fill: 0xffffff
});
welcomeTxt.anchor.set(0.5, 0);
welcomeTxt.x = 2048 / 2;
welcomeTxt.y = 400;
introOverlay.addChild(welcomeTxt);
// Start button centered at bottom
var startBtn = new Text2('Start', {
	size: 140,
	fill: 0xffffff
});
startBtn.anchor.set(0.5, 0.5);
startBtn.x = 2048 / 2;
startBtn.y = 2732 - 400;
introOverlay.addChild(startBtn);
// Start button handler
startBtn.down = function (x, y, obj) {
	introOverlay.visible = false;
};
// Add overlay to game
game.addChild(introOverlay);
// Path and wall spot colors
// Path definition (nodes spaced further apart horizontally for a wider and longer path)
var path = [{
	x: 500,
	y: 600
},
// Start
{
	x: 500,
	y: 1200
}, {
	x: 500,
	y: 1800
},
// Extend downward
{
	x: 500,
	y: 2300
},
// Curve right
{
	x: 900,
	y: 2300
}, {
	x: 1300,
	y: 2300
},
// Curve up
{
	x: 1300,
	y: 1800
}, {
	x: 1300,
	y: 1200
},
// Curve right again
{
	x: 1750,
	y: 1200
}, {
	x: 1750,
	y: 1800
},
// Curve up to base
{
	x: 1750,
	y: 2300
}, {
	x: 1750,
	y: 2732 - 200 // near bottom edge, but not out of bounds
}];
// Wall spot positions (10 unit placement positions, adjacent to the new, longer path)
var wallSpots = [{
	x: path[1].x + 120,
	y: path[1].y
}, {
	x: path[2].x + 120,
	y: path[2].y
}, {
	x: path[3].x + 120,
	y: path[3].y
}, {
	x: path[4].x,
	y: path[4].y - 120
}, {
	x: path[5].x,
	y: path[5].y - 120
}, {
	x: path[6].x - 120,
	y: path[6].y
}, {
	x: path[7].x - 120,
	y: path[7].y
}, {
	x: path[8].x - 120,
	y: path[8].y
}, {
	x: path[9].x,
	y: path[9].y + 120
}, {
	x: path[10].x,
	y: path[10].y + 120
}];
// Wall spot state
var wallSpotStates = [];
for (var i = 0; i < wallSpots.length; ++i) wallSpotStates[i] = null;
// Game state
var zombies = [];
var bullets = [];
var soldiers = [];
var coins = 10;
var baseHp = 10;
var wave = 1;
var spawnTimer = 0;
var spawnIdx = 0;
var zombiesToSpawn = 0;
var waveInProgress = false;
var baseNode = null;
var selectedSoldierType = 1;
var placingSoldier = false;
var placingSoldierIdx = -1;
var gameOver = false;
// GUI
var coinTxt = new Text2('Coins: 10', {
	size: 80,
	fill: 0xFFE680
});
coinTxt.anchor.set(0, 0);
LK.gui.top.addChild(coinTxt);
var baseHpTxt = new Text2('Base: 10', {
	size: 80,
	fill: 0xFFAAAA
});
baseHpTxt.anchor.set(1, 0);
LK.gui.topRight.addChild(baseHpTxt);
var waveTxt = new Text2('Wave: 1', {
	size: 80,
	fill: 0xAAFFAA
});
waveTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(waveTxt);
var soldierBtns = [];
var soldierCosts = [3, 10, 20, 50];
var soldierNames = ['Basic', 'Rapid', 'Heavy', 'Red Arm'];
var soldierBtnY = 0;
for (var i = 0; i < 4; ++i) {
	var btn = new Text2(soldierNames[i] + "\n" + soldierCosts[i] + "c", {
		size: 60,
		fill: "#fff"
	});
	btn.anchor.set(0.5, 0);
	btn.x = 300 + i * 300;
	btn.y = 0;
	LK.gui.bottom.addChild(btn);
	soldierBtns.push(btn);
}
// Only 4 buttons shown, 4 upgrades available
// Draw path (as faint nodes)
for (var i = 0; i < path.length; ++i) {
	var node = LK.getAsset('pathnode', {
		anchorX: 0.5,
		anchorY: 0.5,
		x: path[i].x,
		y: path[i].y,
		alpha: 0.18,
		color: 0x8B5A2B
	});
	game.addChild(node);
	// Connect lines (not possible, so just nodes)
}
// Draw wall spots
var wallSpotNodes = [];
for (var i = 0; i < wallSpots.length; ++i) {
	var ws = LK.getAsset('wallspot', {
		anchorX: 0.5,
		anchorY: 0.5,
		x: wallSpots[i].x,
		y: wallSpots[i].y,
		alpha: 0.5,
		color: 0x0000ff // blue
	});
	ws.idx = i;
	wallSpotNodes.push(ws);
	game.addChild(ws);
}
// Draw base
baseNode = LK.getAsset('base', {
	anchorX: 0.5,
	anchorY: 0.5,
	x: path[path.length - 1].x,
	y: path[path.length - 1].y
});
game.addChild(baseNode);
// Handle soldier button selection
for (var i = 0; i < soldierBtns.length; ++i) {
	(function (idx) {
		soldierBtns[idx].down = function (x, y, obj) {
			selectedSoldierType = idx + 1;
			for (var j = 0; j < soldierBtns.length; ++j) {
				soldierBtns[j].setStyle({
					fill: j === idx ? "#ffff00" : "#fff"
				});
			}
		};
	})(i);
}
soldierBtns[0].setStyle({
	fill: 0xFFFF00
});
// Place soldier on wall spot
for (var i = 0; i < wallSpotNodes.length; ++i) {
	(function (idx) {
		wallSpotNodes[idx].down = function (x, y, obj) {
			if (gameOver) return;
			if (wallSpotStates[idx] === null) {
				// Place soldier if empty
				var cost = soldierCosts[selectedSoldierType - 1];
				if (coins < cost) {
					// Flash red
					tween(wallSpotNodes[idx], {
						tint: 0xff0000
					}, {
						duration: 200,
						onFinish: function onFinish() {
							tween(wallSpotNodes[idx], {
								tint: 0x888888
							}, {
								duration: 200
							});
						}
					});
					return;
				}
				coins -= cost;
				coinTxt.setText('Coins: ' + coins);
				// Place soldier
				var s = new Soldier();
				s.setType(selectedSoldierType);
				s.x = wallSpots[idx].x;
				s.y = wallSpots[idx].y;
				s.wallSpot = idx;
				wallSpotStates[idx] = s;
				soldiers.push(s);
				game.addChild(s);
				// Add upgrade text overlay
				var upgradeTxt = new Text2('Upgrade', {
					size: 40,
					fill: 0x00FF00
				});
				upgradeTxt.anchor.set(0.5, 1);
				upgradeTxt.x = s.x;
				upgradeTxt.y = s.y - 60;
				upgradeTxt.visible = false;
				s.upgradeTxt = upgradeTxt;
				game.addChild(upgradeTxt);
				// Add tap handler for upgrade
				s.down = function (x, y, obj) {
					if (gameOver) return;
					// Show upgrade text if not max level
					if (s.level < 4) {
						s.upgradeTxt.visible = true;
						// Hide after 1s
						LK.setTimeout(function () {
							if (s.upgradeTxt) s.upgradeTxt.visible = false;
						}, 1000);
					}
					// If already at max, do nothing
				};
				// Add tap handler for upgrade text
				upgradeTxt.down = function (x, y, obj) {
					if (gameOver) return;
					if (s.level >= 4) return;
					// Upgrade cost: Level 2 = 10, Level 3 = 20, Level 4 = 50
					var upgradeCost = 0;
					if (s.level === 1) upgradeCost = 10;else if (s.level === 2) upgradeCost = 20;else if (s.level === 3) upgradeCost = 50;
					if (coins < upgradeCost) {
						// Flash red
						tween(upgradeTxt, {
							fill: 0xff0000
						}, {
							duration: 200,
							onFinish: function onFinish() {
								tween(upgradeTxt, {
									fill: 0x00ff00
								}, {
									duration: 200
								});
							}
						});
						return;
					}
					coins -= upgradeCost;
					coinTxt.setText('Coins: ' + coins);
					s.level += 1;
					// Change type and visuals on upgrade
					if (s.level === 2) {
						s.setType(2);
					}
					if (s.level === 3) {
						s.setType(3);
					}
					if (s.level === 4) {
						s.setType(4);
					}
					// Show upgraded text
					upgradeTxt.setText('Upgraded!');
					upgradeTxt.fill = "#ffff00";
					LK.setTimeout(function () {
						if (upgradeTxt) {
							upgradeTxt.setText('Upgrade');
							upgradeTxt.fill = "#00ff00";
							upgradeTxt.visible = false;
						}
					}, 1000);
				};
			} else {
				// If already a soldier, tap to show upgrade
				var s = wallSpotStates[idx];
				if (s && s.upgradeTxt && s.level < 5) {
					s.upgradeTxt.visible = true;
					LK.setTimeout(function () {
						if (s.upgradeTxt) s.upgradeTxt.visible = false;
					}, 1000);
				}
			}
		};
	})(i);
}
// Start first wave
// Red zombie spawn plan: wave 10-20, 1 red per wave; wave 11, 2 reds; otherwise, 1 red per wave 1-10
var redZombiePlan = {}; // wave number -> array of 0/1 (1=red)
function setupRedZombiePlan() {
	// For waves 1-9: 1 red per wave
	for (var w = 1; w <= 9; ++w) {
		var arr = [];
		var totalZ = 5 + w * 2;
		for (var i = 0; i < totalZ; ++i) arr.push(0);
		arr[0] = 1; // Always first zombie is red (could randomize if desired)
		// Shuffle so red is not always first
		for (var i = arr.length - 1; i > 0; --i) {
			var j = Math.floor(Math.random() * (i + 1));
			var tmp = arr[i];
			arr[i] = arr[j];
			arr[j] = tmp;
		}
		redZombiePlan[w] = arr;
	}
	// Wave 10: 1 red
	var arr10 = [];
	var totalZ10 = 5 + 10 * 2;
	for (var i = 0; i < totalZ10; ++i) arr10.push(0);
	arr10[0] = 1;
	for (var i = arr10.length - 1; i > 0; --i) {
		var j = Math.floor(Math.random() * (i + 1));
		var tmp = arr10[i];
		arr10[i] = arr10[j];
		arr10[j] = tmp;
	}
	redZombiePlan[10] = arr10;
	// Wave 11: 2 reds
	var arr11 = [];
	var totalZ11 = 5 + 11 * 2;
	for (var i = 0; i < totalZ11; ++i) arr11.push(0);
	arr11[0] = 1;
	arr11[1] = 1;
	for (var i = arr11.length - 1; i > 0; --i) {
		var j = Math.floor(Math.random() * (i + 1));
		var tmp = arr11[i];
		arr11[i] = arr11[j];
		arr11[j] = tmp;
	}
	redZombiePlan[11] = arr11;
	// Waves 12-20: 1 red per wave
	for (var w = 12; w <= 20; ++w) {
		var arr = [];
		var totalZ = 5 + w * 2;
		for (var i = 0; i < totalZ; ++i) arr.push(0);
		arr[0] = 1;
		for (var i = arr.length - 1; i > 0; --i) {
			var j = Math.floor(Math.random() * (i + 1));
			var tmp = arr[i];
			arr[i] = arr[j];
			arr[j] = tmp;
		}
		redZombiePlan[w] = arr;
	}
}
setupRedZombiePlan();
function startWave() {
	waveInProgress = true;
	spawnIdx = 0;
	zombiesToSpawn = 5 + wave * 2;
	spawnTimer = 0;
	waveTxt.setText('Wave: ' + wave);
}
startWave();
// Spawn zombie
function spawnZombie() {
	var z = new Zombie();
	// Stats scale with wave
	var hp = 5; // Green zombie health is always 5
	var speed = 1.2 + 0.08 * wave;
	var reward = 1 + Math.floor(wave / 2);
	// Red zombie logic
	var makeRed = false;
	var makeYellow = false;
	if (wave >= 15 && wave <= 20) {
		// Only red zombies between wave 15 and 20
		makeRed = true;
	} else if (wave >= 1 && wave <= 20 && redZombiePlan[wave]) {
		if (spawnIdx < redZombiePlan[wave].length && redZombiePlan[wave][spawnIdx] === 1) {
			makeRed = true;
		}
	} else if (wave > 20 && wave < 31) {
		// Between wave 21 and 30, spawn a yellow zombie at a random index per wave
		// Only one yellow zombie per wave, at a random spawnIdx
		if (typeof spawnZombie.yellowIdx === "undefined" || spawnZombie.yellowWave !== wave) {
			// Pick a random index for yellow zombie for this wave
			spawnZombie.yellowIdx = 20 + Math.floor(Math.random() * Math.max(1, 5 + wave * 2 - 20));
			spawnZombie.yellowWave = wave;
		}
		if (spawnIdx === spawnZombie.yellowIdx) {
			makeYellow = true;
		}
		makeRed = false;
	} else if (wave > 30) {
		// After wave 30, only green zombies
		makeRed = false;
		makeYellow = false;
	}
	if (makeRed) {
		z.setRed();
		hp = 10; // Red zombie health is always 10
		reward = reward * 2;
	}
	if (makeYellow) {
		z.setYellow();
		hp = 20; // Yellow zombie health is 20
		reward = reward * 3;
	}
	z.setStats(hp, speed, reward);
	z.setPathIndex(0);
	z.x = path[0].x;
	z.y = path[0].y;
	zombies.push(z);
	game.addChild(z);
}
// Main game update
game.update = function () {
	if (gameOver) return;
	// Spawn zombies for wave
	if (waveInProgress && spawnIdx < zombiesToSpawn) {
		if (spawnTimer <= 0) {
			spawnZombie();
			spawnIdx++;
			spawnTimer = 40 - Math.min(wave * 2, 30); // Faster spawns later
		} else {
			spawnTimer--;
		}
	}
	// Update zombies
	for (var i = zombies.length - 1; i >= 0; --i) {
		var z = zombies[i];
		z.update();
		if (z.dead) {
			// Reward coins
			if (z.isRed) {
				coins += 2;
			} else {
				coins += 1;
			}
			coinTxt.setText('Coins: ' + coins);
			zombies.splice(i, 1);
			continue;
		}
		if (z.pathIndex >= path.length - 1 && !z.dead) {
			// Reached base
			baseHp--;
			baseHpTxt.setText('Base: ' + baseHp);
			LK.effects.flashObject(baseNode, 0xff0000, 400);
			z.dead = true;
			z.destroy();
			zombies.splice(i, 1);
			if (baseHp <= 0) {
				gameOver = true;
				LK.effects.flashScreen(0xff0000, 1200);
				LK.showGameOver();
				return;
			}
		}
	}
	// Update bullets
	for (var i = bullets.length - 1; i >= 0; --i) {
		var b = bullets[i];
		b.update();
		if (!b.parent) {
			bullets.splice(i, 1);
		}
	}
	// Update soldiers
	for (var i = 0; i < soldiers.length; ++i) {
		soldiers[i].update();
	}
	// Check if wave is over
	if (waveInProgress && spawnIdx >= zombiesToSpawn && zombies.length === 0) {
		waveInProgress = false;
		// Next wave after short delay
		LK.setTimeout(function () {
			wave++;
			startWave();
		}, 1200);
	}
};
// Allow dragging soldiers to swap positions (optional, MVP skips this)
// Prevent placing elements in top left 100x100 (already handled by GUI placement)
// No background, music, or sound per requirements
// No pause, leaderboard, or game over handling (LK does this)
:quality(85)/https://cdn.frvr.ai/68371e9fc9a05b7098dbc92c.png%3F3) 
 Soilder. In-Game asset. 2d. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/68371f18c9a05b7098dbc939.png%3F3) 
 one-gun soldier. In-Game asset. 2d. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/68371f64c9a05b7098dbc944.png%3F3) 
 soldier with 2 kate ak47 in his hand. In-Game asset. 2d. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/68371fb7c9a05b7098dbc94e.png%3F3) 
 soldier with rifle. In-Game asset. 2d. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/68371ff8c9a05b7098dbc961.png%3F3) 
 Zombie. In-Game asset. 2d. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/68371ffac9a05b7098dbc965.png%3F3) 
 Zombie. In-Game asset. 2d. High contrast. No shadows