/**** 
* Plugins
****/ 
var tween = LK.import("@upit/tween.v1");
/**** 
* Classes
****/ 
// --- Bullet Class ---
var Bullet = Container.expand(function () {
	var self = Container.call(this);
	// Attach bullet asset (white ellipse)
	var bulletAsset = self.attachAsset('bullet', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	// Bullet speed (pixels per frame)
	self.speed = 160;
	// Direction in radians (set on creation)
	self.direction = 0;
	// Damage dealt by this bullet
	self.damage = 1;
	// Defensive: track last position for possible future use
	self.lastX = self.x;
	self.lastY = self.y;
	// Update method called every tick
	self.update = function () {
		self.lastX = self.x;
		self.lastY = self.y;
		self.x += Math.cos(self.direction) * self.speed;
		self.y += Math.sin(self.direction) * self.speed;
	};
	return self;
});
// --- Enemy Class ---
var Enemy = Container.expand(function () {
	var self = Container.call(this);
	// Attach enemy asset (pink ellipse), scaled up 2x
	var enemyAsset = self.attachAsset('enemyCircle', {
		anchorX: 0.5,
		anchorY: 0.5,
		scaleX: 2,
		scaleY: 2
	});
	// Enemy properties
	self.radius = 60 + Math.random() * 40; // for spawn distance
	self.speed = 1.15 + Math.random() * 0.55; // even faster approach
	self.maxHp = 3; // always 3 HP
	self.hp = self.maxHp;
	// Direction towards center (set on spawn)
	self.direction = 0;
	// Health bar (white background, green foreground), both rectangles, above the enemy
	self.hpBarBg = self.addChild(LK.getAsset('hpbar_bg', {
		anchorX: 0.5,
		anchorY: 1.0,
		y: -140 // higher above the enemy
	}));
	self.hpBar = self.addChild(LK.getAsset('hpbar_fg', {
		anchorX: 0.5,
		anchorY: 1.0,
		y: -140 // match background bar position
	}));
	// Defensive: track last position for possible future use
	self.lastX = self.x;
	self.lastY = self.y;
	// Update health bar width
	self.updateHpBar = function () {
		// Use asset width for scaling, so health bar always matches background
		self.hpBar.width = self.hpBarBg.width * (self.hp / self.maxHp);
		self.hpBar.x = self.hpBarBg.x - (self.hpBarBg.width - self.hpBar.width) / 2;
	};
	// Update method called every tick
	self.update = function () {
		self.lastX = self.x;
		self.lastY = self.y;
		self.x += Math.cos(self.direction) * self.speed;
		self.y += Math.sin(self.direction) * self.speed;
	};
	// Take damage
	self.takeDamage = function (amount) {
		self.hp -= amount;
		if (self.hp < 0) self.hp = 0;
		self.updateHpBar();
	};
	// On death
	self.die = function () {
		// Flash effect
		LK.effects.flashObject(self, 0xffffff, 200);
		// Particle explosion (intense red, gravity-free)
		// More and bigger particles, even more red!
		for (var i = 0; i < 38; i++) {
			var p = new Container();
			var asset = p.attachAsset('bullet', {
				anchorX: 0.5,
				anchorY: 0.5,
				// Make particles much bigger overall
				scaleX: 1.1 + Math.random() * 0.8,
				scaleY: 1.1 + Math.random() * 0.8,
				// Use a deeper, more saturated red
				color: 0xff0000,
				alpha: 0.82 + Math.random() * 0.18
			});
			p.x = self.x;
			p.y = self.y;
			var angle = Math.random() * Math.PI * 2;
			var speed = 22 + Math.random() * 20;
			var life = 26 + Math.floor(Math.random() * 18);
			p.update = function (a, s, l) {
				var vx = Math.cos(a) * s;
				var vy = Math.sin(a) * s;
				var ticks = 0;
				return function () {
					this.x += vx;
					this.y += vy;
					vx *= 0.88;
					vy *= 0.88;
					this.alpha *= 0.93;
					ticks++;
					if (ticks > l) {
						this.destroy();
					}
				};
			}(angle, speed, life);
			game.addChild(p);
			// Add to update loop
			if (!game._enemyParticles) game._enemyParticles = [];
			game._enemyParticles.push(p);
		}
	};
	// Initialize health bar
	self.updateHpBar();
	return self;
});
// --- HealthBox Class ---
var HealthBox = Container.expand(function () {
	var self = Container.call(this);
	// Attach health asset (red box), scaled up 1.5x
	var asset = self.attachAsset('healthBox', {
		anchorX: 0.5,
		anchorY: 0.5,
		scaleX: 1.5,
		scaleY: 1.5
	});
	self.radius = 60 + Math.random() * 40;
	self.speed = 0.32 + Math.random() * 0.16; // even faster movement
	self.maxHp = 1;
	self.hp = self.maxHp;
	self.direction = 0;
	// Defensive: track last position
	self.lastX = self.x;
	self.lastY = self.y;
	// Update method
	self.update = function () {
		self.lastX = self.x;
		self.lastY = self.y;
		self.x += Math.cos(self.direction) * self.speed;
		self.y += Math.sin(self.direction) * self.speed;
	};
	// Take damage
	self.takeDamage = function (amount) {
		self.hp -= amount;
		if (self.hp < 0) self.hp = 0;
	};
	// On death
	self.die = function () {
		LK.effects.flashObject(self, 0xffffff, 200);
		// Particle effect (white explosion)
		for (var i = 0; i < 18; i++) {
			var p = new Container();
			var asset = p.attachAsset('bullet', {
				anchorX: 0.5,
				anchorY: 0.5,
				scaleX: 0.9 + Math.random() * 0.7,
				scaleY: 0.9 + Math.random() * 0.7,
				color: 0xffffff,
				alpha: 0.8 + Math.random() * 0.15
			});
			p.x = self.x;
			p.y = self.y;
			var angle = Math.random() * Math.PI * 2;
			var speed = 12 + Math.random() * 10;
			var life = 16 + Math.floor(Math.random() * 10);
			p.update = function (a, s, l) {
				var vx = Math.cos(a) * s;
				var vy = Math.sin(a) * s;
				var ticks = 0;
				return function () {
					this.x += vx;
					this.y += vy;
					vx *= 0.88;
					vy *= 0.88;
					this.alpha *= 0.91;
					ticks++;
					if (ticks > l) {
						this.destroy();
					}
				};
			}(angle, speed, life);
			game.addChild(p);
			if (!game._enemyParticles) game._enemyParticles = [];
			game._enemyParticles.push(p);
		}
	};
	return self;
});
// --- Player Class ---
var Player = Container.expand(function () {
	var self = Container.call(this);
	// Attach player asset (blue ellipse), scaled up 2x
	var playerAsset = self.attachAsset('character', {
		anchorX: 0.5,
		anchorY: 0.5,
		scaleX: 2,
		scaleY: 2
	});
	// Player properties
	self.radius = 0; // always at center
	self.rotation = 0; // radians
	self.maxHp = 5;
	self.hp = self.maxHp;
	// Health bar (red background, green foreground) - always on top, not rotating
	self.hpBarBg = LK.getAsset('hpbar_bg', {
		anchorX: 0.5,
		anchorY: 0.5,
		y: -140
	});
	self.hpBar = LK.getAsset('hpbar_fg', {
		anchorX: 0.5,
		anchorY: 0.5,
		y: -140
	});
	// Defensive: track last position for possible future use
	self.lastX = self.x;
	self.lastY = self.y;
	// Update health bar width
	self.updateHpBar = function () {
		// Clamp hp to [0, maxHp]
		if (self.hp < 0) self.hp = 0;
		if (self.hp > self.maxHp) self.hp = self.maxHp;
		// Use asset width for scaling, so health bar always matches background
		// Health bar shrinks from left to right (left edge fixed)
		var targetWidth = self.hpBarBg.width * (self.hp / self.maxHp);
		if (targetWidth < 0) targetWidth = 0;
		// Smoothly animate width decrease (if needed)
		if (typeof self.hpBar.width === "undefined") self.hpBar.width = self.hpBarBg.width;
		if (self.hpBar.width > targetWidth) {
			self.hpBar.width -= Math.max(6, (self.hpBar.width - targetWidth) * 0.18);
			if (self.hpBar.width < targetWidth) self.hpBar.width = targetWidth;
		} else {
			self.hpBar.width = targetWidth;
		}
		// Left edge fixed: set x so left edge stays at bg left
		self.hpBar.x = self.hpBarBg.x - self.hpBarBg.width / 2 + self.hpBar.width / 2;
		// Add a white border to the health bar background for better visibility
		if (!self.hpBarBg._borderAdded) {
			var border = LK.getAsset('hpbar_bg', {
				anchorX: 0.5,
				anchorY: 0.5,
				y: self.hpBarBg.y,
				width: self.hpBarBg.width + 8,
				height: self.hpBarBg.height + 8,
				color: 0xffffff,
				alpha: 0.25
			});
			border.x = self.hpBarBg.x;
			border.y = self.hpBarBg.y;
			if (self.hpBarBg.parent) self.hpBarBg.parent.addChild(border);
			self.hpBarBg._borderAdded = true;
		}
	};
	// Take damage
	self.takeDamage = function (amount) {
		self.hp -= amount;
		if (self.hp < 0) self.hp = 0;
		if (self.hp > self.maxHp) self.hp = self.maxHp;
		self.updateHpBar();
	};
	// On death
	self.die = function () {
		LK.effects.flashObject(self, 0xff0000, 500);
	};
	// Initialize health bar
	self.updateHpBar();
	return self;
});
// --- Shield Layer Class ---
var ShieldLayer = Container.expand(function () {
	var self = Container.call(this);
	// Properties
	self.radius = 0; // distance from player center
	self.maxHp = 3;
	self.hp = self.maxHp;
	self.index = 0; // 0=innermost, 1=outer
	self.active = true;
	// Visual: support shield1, shield2, and shield3 assets
	var shieldAssetNames = ['shield1', 'shield2', 'shield3'];
	var assetName = shieldAssetNames[self.index] || 'shield1';
	self.shieldAsset = self.attachAsset(assetName, {
		anchorX: 0.5,
		anchorY: 0.5,
		scaleX: 4.2,
		scaleY: 4.2,
		alpha: 0.25
	});
	// Health bar above shield
	self.hpBarBg = self.addChild(LK.getAsset('hpbar_bg', {
		anchorX: 0.5,
		anchorY: 0.5,
		y: -self.radius - 120
	}));
	self.hpBar = self.addChild(LK.getAsset('hpbar_fg', {
		anchorX: 0.5,
		anchorY: 0.5,
		y: -self.radius - 120
	}));
	// Defensive: track last position for possible future use
	self.lastX = self.x;
	self.lastY = self.y;
	// Update health bar width
	self.updateHpBar = function () {
		// Use asset width for scaling, so health bar always matches background
		self.hpBar.width = self.hpBarBg.width * (self.hp / self.maxHp);
		self.hpBar.x = self.hpBarBg.x - (self.hpBarBg.width - self.hpBar.width) / 2;
		self.hpBar.visible = self.active;
		self.hpBarBg.visible = self.active;
	};
	// Take damage
	self.takeDamage = function (amount) {
		if (!self.active) return;
		self.hp -= amount;
		if (self.hp < 0) self.hp = 0;
		self.updateHpBar();
		if (self.hp <= 0) {
			// Play shield explosion sound
			LK.getSound('shieldExplode').play();
			self.active = false;
			self.visible = false;
			self.hpBar.visible = false;
			self.hpBarBg.visible = false;
		}
	};
	// Reset shield
	self.reset = function () {
		self.hp = self.maxHp;
		self.active = true;
		self.visible = true;
		self.updateHpBar();
	};
	// Initialize
	self.updateHpBar();
	return self;
});
/**** 
* Initialize Game
****/ 
var game = new LK.Game({
	backgroundColor: 0x181818
});
/**** 
* Game Code
****/ 
// Health bar assets for bg/fg
// --- Background Image ---
var backgroundImg = LK.getAsset('background', {
	anchorX: 0.5,
	anchorY: 0.5,
	x: 2048 / 2,
	y: 2732 / 2,
	scaleX: 2048 / 4096,
	scaleY: 2732 / 4096
});
game.addChild(backgroundImg);
// --- Global Variables --- 
var player;
var enemies = [];
var bullets = [];
var spawnTimer = 0;
var fireCooldown = 0;
var dragActive = false;
var lastTouchAngle = 0;
// --- Score Display ---
var scoreTxt = new Text2('0', {
	size: 120,
	fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// --- Helper Line for Fire Direction (dotted, in fire direction, aligned with bullet) ---
var helperLineDots = [];
var helperLineAlpha = 0.7;
var helperLineWidth = 18;
var helperLineDotSpacing = 48; // px between dots
// Calculate max possible length from player to farthest screen edge
function getHelperLineLength(angle) {
	// Player is always at center
	var cx = 2048 / 2;
	var cy = 2732 / 2;
	// Find intersection with screen edge in direction of angle
	var dx = Math.cos(angle);
	var dy = Math.sin(angle);
	// Calculate t for each edge, pick the smallest positive t
	var tVals = [];
	if (dx !== 0) {
		var t1 = (0 - cx) / dx;
		var t2 = (2048 - cx) / dx;
		tVals.push(t1, t2);
	}
	if (dy !== 0) {
		var t3 = (0 - cy) / dy;
		var t4 = (2732 - cy) / dy;
		tVals.push(t3, t4);
	}
	// Only consider positive t (forward direction)
	var maxLen = 0;
	for (var i = 0; i < tVals.length; i++) {
		if (tVals[i] > 0) {
			var px = cx + dx * tVals[i];
			var py = cy + dy * tVals[i];
			// Check if point is on screen
			if (px >= 0 && px <= 2048 && py >= 0 && py <= 2732) {
				var dist = Math.sqrt((px - cx) * (px - cx) + (py - cy) * (py - cy));
				if (dist > maxLen) maxLen = dist;
			}
		}
	}
	// Clamp to a minimum length
	if (maxLen < 200) maxLen = 200;
	return maxLen;
}
// Pre-create enough dots for the longest possible line (diagonal)
var maxPossibleLength = Math.sqrt(2048 * 2048 + 2732 * 2732);
var helperLineDotCount = Math.ceil(maxPossibleLength / helperLineDotSpacing);
for (var i = 1; i <= helperLineDotCount; i++) {
	var dot = new Container();
	var asset = dot.attachAsset('helperDot', {
		anchorX: 0.5,
		anchorY: 0.5,
		scaleX: helperLineWidth / 32,
		scaleY: helperLineWidth / 32,
		alpha: helperLineAlpha
	});
	dot.visible = false;
	game.addChild(dot);
	helperLineDots.push(dot);
}
// --- Player Initialization ---
player = new Player();
player.x = 2048 / 2;
player.y = 2732 / 2;
// Always keep player sprite visually fixed (no rotation)
player.rotation = 0;
game.addChild(player);
// Add player health bar to game (not as child of player, so it doesn't rotate)
game.addChild(player.hpBarBg);
game.addChild(player.hpBar);
// Position health bar above player
player.hpBarBg.x = player.x;
player.hpBarBg.y = player.y - 170;
player.hpBar.x = player.x;
player.hpBar.y = player.y - 170;
// --- Shield Layers Initialization ---
// Add three shield layers: shield1 (innermost), shield2 (2x), shield3 (3x)
var shieldLayers = [];
var shieldRadii = [110, 220, 330]; // shield1, shield2, shield3 (further out)
var shieldScales = [4.2, 8.4, 12.6]; // shield2 is 2x, shield3 is 3x larger than shield1
var shieldAssetNames = ['shield1', 'shield2', 'shield3'];
for (var i = 0; i < 3; i++) {
	var shield = new ShieldLayer();
	shield.index = i;
	shield.radius = shieldRadii[i];
	// Place shield at player center
	shield.x = 2048 / 2;
	shield.y = 2732 / 2;
	// Set shield visual size
	shield.shieldAsset.destroy(); // Remove default asset
	shield.shieldAsset = shield.attachAsset(shieldAssetNames[i], {
		anchorX: 0.5,
		anchorY: 0.5,
		scaleX: shieldScales[i],
		scaleY: shieldScales[i],
		alpha: 0.25 + 0.15 * i
	});
	// Position health bar above shield
	if (i === 1) {
		// shield2: place its health bar above shield2 visual, and above shield1/player health bar
		// Calculate shield2 visual height (scaled)
		var shield2VisualHeight = LK.getAsset('shield2', {
			anchorX: 0.5,
			anchorY: 0.5
		}).height * shieldScales[1];
		// Place health bar just above shield2 visual
		shield.hpBarBg.y = -shield2VisualHeight / 2 - 60;
		shield.hpBar.y = -shield2VisualHeight / 2 - 60;
	} else if (i === 2) {
		// shield3: place its health bar above shield3 visual, and above shield2
		var shield3VisualHeight = LK.getAsset('shield3', {
			anchorX: 0.5,
			anchorY: 0.5
		}).height * shieldScales[2];
		shield.hpBarBg.y = -shield3VisualHeight / 2 - 60;
		shield.hpBar.y = -shield3VisualHeight / 2 - 60;
	} else {
		// shield1: default position
		shield.hpBarBg.y = -shield.radius - 120;
		shield.hpBar.y = -shield.radius - 120;
	}
	// Add to game and to shieldLayers array
	game.addChild(shield);
	shieldLayers.push(shield);
}
// --- Helper: Get angle from center to (x, y) ---
function getAngleToCenter(x, y) {
	var cx = 2048 / 2;
	var cy = 2732 / 2;
	return Math.atan2(y - cy, x - cx);
}
// --- Helper: Fire Bullet ---
function fireBullet(angle) {
	if (fireCooldown > 0) return;
	var bullet = new Bullet();
	bullet.x = player.x;
	bullet.y = player.y;
	bullet.direction = angle;
	// Defensive: initialize lastX/lastY
	bullet.lastX = bullet.x;
	bullet.lastY = bullet.y;
	bullets.push(bullet);
	game.addChild(bullet);
	fireCooldown = 4; // frames (was 10, now fires much more frequently)
	// Play shoot sound
	LK.getSound('shoot').play();
}
// --- Handle Touch/Drag to Set Fire Angle (Player sprite does NOT rotate) ---
game.down = function (x, y, obj) {
	dragActive = true;
	// Store angle from player center to touch point
	var angle = Math.atan2(y - player.y, x - player.x);
	lastTouchAngle = angle;
};
game.move = function (x, y, obj) {
	if (dragActive) {
		// Update angle from player center to current touch point
		var angle = Math.atan2(y - player.y, x - player.x);
		lastTouchAngle = angle;
	}
};
game.up = function (x, y, obj) {
	dragActive = false;
	// Fire bullet in the direction of last drag/touch (from center to release point)
	fireBullet(lastTouchAngle);
};
// --- Enemy Spawning ---
function spawnEnemy() {
	var angle = Math.random() * Math.PI * 2;
	var distance = 1100 + Math.random() * 400;
	var ex = 2048 / 2 + Math.cos(angle) * distance;
	var ey = 2732 / 2 + Math.sin(angle) * distance;
	// 1 in 5 chance to spawn a health box instead of enemy (increased frequency)
	if (Math.random() < 0.2) {
		var healthBox = new HealthBox();
		healthBox.x = ex;
		healthBox.y = ey;
		healthBox.direction = Math.atan2(2732 / 2 - ey, 2048 / 2 - ex);
		healthBox.lastX = healthBox.x;
		healthBox.lastY = healthBox.y;
		enemies.push(healthBox);
		game.addChild(healthBox);
	} else {
		var enemy = new Enemy();
		enemy.x = ex;
		enemy.y = ey;
		// Set direction towards player center
		enemy.direction = Math.atan2(2732 / 2 - ey, 2048 / 2 - ex);
		// Scale enemy speed with score: base speed + (score * 0.08), capped at 7x base
		var score = LK.getScore();
		var speedScale = 1 + Math.min(score * 0.08, 6); // up to 7x base speed
		enemy.speed *= speedScale;
		// Defensive: initialize lastX/lastY
		enemy.lastX = enemy.x;
		enemy.lastY = enemy.y;
		enemies.push(enemy);
		game.addChild(enemy);
	}
}
// --- Game Update Loop ---
game.update = function () {
	// --- Fire cooldown ---
	if (fireCooldown > 0) fireCooldown--;
	// --- Update Helper Line (dotted, in fire direction, aligned with bullet) ---
	var cx = player.x;
	var cy = player.y;
	var lineLen = getHelperLineLength(lastTouchAngle);
	var dotCount = Math.floor(lineLen / helperLineDotSpacing);
	for (var i = 0; i < helperLineDots.length; i++) {
		var dot = helperLineDots[i];
		if (i < dotCount) {
			var dist = (i + 1) * helperLineDotSpacing;
			dot.x = cx + Math.cos(lastTouchAngle) * dist;
			dot.y = cy + Math.sin(lastTouchAngle) * dist;
			dot.rotation = lastTouchAngle;
			dot.visible = true;
		} else {
			dot.visible = false;
		}
	}
	// Keep player health bar above player (not rotating)
	player.hpBarBg.x = player.x;
	player.hpBarBg.y = player.y - 170;
	player.hpBar.x = player.x;
	player.hpBar.y = player.y - 170;
	player.updateHpBar();
	// --- Enemy spawn timer ---
	spawnTimer--;
	if (spawnTimer <= 0) {
		spawnEnemy();
		// Make enemies and health boxes spawn less frequently (every 2.5-3.5s)
		spawnTimer = 150 + Math.floor(Math.random() * 60); // spawn every 2.5-3.5s
	}
	// --- Update Bullets ---
	for (var i = bullets.length - 1; i >= 0; i--) {
		var bullet = bullets[i];
		// Defensive: initialize lastX/lastY if not set
		if (typeof bullet.lastX === "undefined") bullet.lastX = bullet.x;
		if (typeof bullet.lastY === "undefined") bullet.lastY = bullet.y;
		bullet.update();
		// Remove if out of bounds or destroyed
		if (bullet.x < -200 || bullet.x > 2048 + 200 || bullet.y < -200 || bullet.y > 2732 + 200 || bullet.destroyed) {
			if (!bullet.destroyed) bullet.destroy();
			bullets.splice(i, 1);
			continue;
		}
		// Check collision with enemies
		var hit = false;
		for (var j = enemies.length - 1; j >= 0; j--) {
			var enemy = enemies[j];
			// Defensive: initialize lastX/lastY if not set
			if (typeof enemy.lastX === "undefined") enemy.lastX = enemy.x;
			if (typeof enemy.lastY === "undefined") enemy.lastY = enemy.y;
			if (enemy.destroyed) continue;
			if (bullet.intersects(enemy)) {
				// HealthBox dies in 1 hit, Enemy dies at 0 hp
				var isHealthBox = typeof enemy.maxHp !== "undefined" && enemy.maxHp === 1;
				if (isHealthBox) {
					// HealthBox: always die in one hit, regardless of hp
					enemy.hp = 0;
				} else {
					enemy.takeDamage(bullet.damage);
				}
				if (enemy.hp <= 0) {
					var isHealthBox = typeof enemy.maxHp !== "undefined" && enemy.maxHp === 1;
					if (isHealthBox) {
						// HealthBox: fill player health to max
						player.hp = player.maxHp;
						player.updateHpBar();
						// Find the outermost active shield (highest index, active)
						var outermostActive = -1;
						for (var si = shieldLayers.length - 1; si >= 0; si--) {
							if (shieldLayers[si].active) {
								outermostActive = si;
								break;
							}
						}
						// If all shields are inactive, add the next shield (lowest inactive)
						if (outermostActive === -1) {
							for (var si = 0; si < shieldLayers.length; si++) {
								if (!shieldLayers[si].active) {
									shieldLayers[si].reset();
									LK.effects.flashObject(shieldLayers[si], 0x44ff44, 400);
									LK.getSound('shieldHit').play();
									break;
								}
							}
						} else {
							// If outermost shield is not full, restore its HP
							var shield = shieldLayers[outermostActive];
							if (shield.hp < shield.maxHp) {
								shield.hp = shield.maxHp;
								shield.updateHpBar();
								LK.effects.flashObject(shield, 0x44ff44, 400);
								LK.getSound('shieldHit').play();
							} else {
								// If outermost shield is full, try to add the next shield (if exists and inactive)
								if (outermostActive + 1 < shieldLayers.length && !shieldLayers[outermostActive + 1].active) {
									shieldLayers[outermostActive + 1].reset();
									LK.effects.flashObject(shieldLayers[outermostActive + 1], 0x44ff44, 400);
									LK.getSound('shieldHit').play();
								} else {
									// All shields are active and full, just play effect on outermost
									LK.effects.flashObject(shield, 0x44ff44, 400);
									LK.getSound('shieldHit').play();
								}
							}
						}
					} else {
						LK.getSound('enemyDie').play();
					}
					enemy.die();
					if (!enemy.destroyed) enemy.destroy();
					enemies.splice(j, 1);
					if (!(typeof enemy.maxHp !== "undefined" && enemy.maxHp === 1)) {
						LK.setScore(LK.getScore() + 1);
						scoreTxt.setText(LK.getScore());
					}
				}
				if (!bullet.destroyed) bullet.destroy();
				bullets.splice(i, 1);
				hit = true;
				break;
			}
		}
		if (hit) continue;
	}
	// Remove destroyed bullets (defensive, in case any remain)
	for (var i = bullets.length - 1; i >= 0; i--) {
		if (bullets[i].destroyed) bullets.splice(i, 1);
	}
	// --- Update Enemies ---
	for (var i = enemies.length - 1; i >= 0; i--) {
		var enemy = enemies[i];
		// Defensive: initialize lastX/lastY if not set
		if (typeof enemy.lastX === "undefined") enemy.lastX = enemy.x;
		if (typeof enemy.lastY === "undefined") enemy.lastY = enemy.y;
		if (enemy.destroyed) {
			enemies.splice(i, 1);
			continue;
		}
		enemy.update();
		// Check collision with shields (outermost to innermost)
		var blocked = false;
		for (var s = shieldLayers.length - 1; s >= 0; s--) {
			var shield = shieldLayers[s];
			if (shield.active) {
				// Defensive: initialize lastX/lastY if not set
				if (typeof shield.lastX === "undefined") shield.lastX = shield.x;
				if (typeof shield.lastY === "undefined") shield.lastY = shield.y;
				// Check distance from player center to enemy center
				var dx = enemy.x - player.x;
				var dy = enemy.y - player.y;
				var dist = Math.sqrt(dx * dx + dy * dy);
				// Use actual shield visual size for collision (shieldAsset is ellipse, scaleX/scaleY applied)
				// Defensive: recalculate radius using current scale for each shield asset
				var shieldAssetNames = ['shield1', 'shield2', 'shield3'];
				var assetName = shieldAssetNames[shield.index] || 'shield1';
				var baseRadius = LK.getAsset(assetName, {
					anchorX: 0.5,
					anchorY: 0.5
				}).width / 2;
				var shieldVisualRadius = baseRadius * shield.shieldAsset.scaleX;
				var enemyVisualRadius = (enemy.width > enemy.height ? enemy.width : enemy.height) / 2;
				if (dist < shieldVisualRadius + enemyVisualRadius) {
					// Collided with shield
					var isHealthBox = typeof enemy.maxHp !== "undefined" && enemy.maxHp === 1;
					if (isHealthBox) {
						// HealthBox: just disappears, does not affect shield, no revive or restore
						enemy.die();
						if (!enemy.destroyed) enemy.destroy();
						enemies.splice(i, 1);
						blocked = true;
						break;
					} else {
						// Regular enemy: damage shield
						shield.takeDamage(1);
						LK.getSound('shieldHit').play();
						enemy.die();
						if (!enemy.destroyed) enemy.destroy();
						enemies.splice(i, 1);
						// Increase score when enemy dies by hitting shield
						LK.setScore(LK.getScore() + 1);
						scoreTxt.setText(LK.getScore());
						// Flash shield
						LK.effects.flashObject(shield, 0x44aaff, 200);
						blocked = true;
						break;
					}
				}
			}
		}
		if (blocked) continue;
		// If not blocked, check collision with player
		if (enemy.intersects(player)) {
			var isHealthBox = typeof enemy.maxHp !== "undefined" && enemy.maxHp === 1;
			if (isHealthBox) {
				// HealthBox: just disappears, does not affect player health
				enemy.die();
				if (!enemy.destroyed) enemy.destroy();
				enemies.splice(i, 1);
				// Do not change player health or flash screen
			} else {
				player.takeDamage(1);
				if (player.hp < 0) player.hp = 0;
				if (player.hp > player.maxHp) player.hp = player.maxHp;
				LK.getSound('enemyDie').play();
				enemy.die();
				if (!enemy.destroyed) enemy.destroy();
				enemies.splice(i, 1);
				// Flash screen
				LK.effects.flashScreen(0xff0000, 400);
				// Game over if player dead
				if (player.hp <= 0) {
					player.die();
					LK.showGameOver();
					return;
				}
			}
		}
	}
	// Remove destroyed enemies (defensive, in case any remain)
	for (var i = enemies.length - 1; i >= 0; i--) {
		if (enemies[i].destroyed) enemies.splice(i, 1);
	}
	;
	// --- Update and cleanup enemy death particles ---
	if (game._enemyParticles) {
		for (var i = game._enemyParticles.length - 1; i >= 0; i--) {
			var p = game._enemyParticles[i];
			// Defensive: initialize lastX/lastY if not set
			if (typeof p.lastX === "undefined") p.lastX = p.x;
			if (typeof p.lastY === "undefined") p.lastY = p.y;
			if (p.destroyed) {
				game._enemyParticles.splice(i, 1);
				continue;
			}
			if (typeof p.update === "function") p.update();
		}
		// Defensive: remove any remaining destroyed particles
		for (var i = game._enemyParticles.length - 1; i >= 0; i--) {
			if (game._enemyParticles[i].destroyed) game._enemyParticles.splice(i, 1);
		}
	}
};
// --- Score Initialization ---
LK.setScore(0);
scoreTxt.setText(LK.getScore()); /**** 
* Plugins
****/ 
var tween = LK.import("@upit/tween.v1");
/**** 
* Classes
****/ 
// --- Bullet Class ---
var Bullet = Container.expand(function () {
	var self = Container.call(this);
	// Attach bullet asset (white ellipse)
	var bulletAsset = self.attachAsset('bullet', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	// Bullet speed (pixels per frame)
	self.speed = 160;
	// Direction in radians (set on creation)
	self.direction = 0;
	// Damage dealt by this bullet
	self.damage = 1;
	// Defensive: track last position for possible future use
	self.lastX = self.x;
	self.lastY = self.y;
	// Update method called every tick
	self.update = function () {
		self.lastX = self.x;
		self.lastY = self.y;
		self.x += Math.cos(self.direction) * self.speed;
		self.y += Math.sin(self.direction) * self.speed;
	};
	return self;
});
// --- Enemy Class ---
var Enemy = Container.expand(function () {
	var self = Container.call(this);
	// Attach enemy asset (pink ellipse), scaled up 2x
	var enemyAsset = self.attachAsset('enemyCircle', {
		anchorX: 0.5,
		anchorY: 0.5,
		scaleX: 2,
		scaleY: 2
	});
	// Enemy properties
	self.radius = 60 + Math.random() * 40; // for spawn distance
	self.speed = 1.15 + Math.random() * 0.55; // even faster approach
	self.maxHp = 3; // always 3 HP
	self.hp = self.maxHp;
	// Direction towards center (set on spawn)
	self.direction = 0;
	// Health bar (white background, green foreground), both rectangles, above the enemy
	self.hpBarBg = self.addChild(LK.getAsset('hpbar_bg', {
		anchorX: 0.5,
		anchorY: 1.0,
		y: -140 // higher above the enemy
	}));
	self.hpBar = self.addChild(LK.getAsset('hpbar_fg', {
		anchorX: 0.5,
		anchorY: 1.0,
		y: -140 // match background bar position
	}));
	// Defensive: track last position for possible future use
	self.lastX = self.x;
	self.lastY = self.y;
	// Update health bar width
	self.updateHpBar = function () {
		// Use asset width for scaling, so health bar always matches background
		self.hpBar.width = self.hpBarBg.width * (self.hp / self.maxHp);
		self.hpBar.x = self.hpBarBg.x - (self.hpBarBg.width - self.hpBar.width) / 2;
	};
	// Update method called every tick
	self.update = function () {
		self.lastX = self.x;
		self.lastY = self.y;
		self.x += Math.cos(self.direction) * self.speed;
		self.y += Math.sin(self.direction) * self.speed;
	};
	// Take damage
	self.takeDamage = function (amount) {
		self.hp -= amount;
		if (self.hp < 0) self.hp = 0;
		self.updateHpBar();
	};
	// On death
	self.die = function () {
		// Flash effect
		LK.effects.flashObject(self, 0xffffff, 200);
		// Particle explosion (intense red, gravity-free)
		// More and bigger particles, even more red!
		for (var i = 0; i < 38; i++) {
			var p = new Container();
			var asset = p.attachAsset('bullet', {
				anchorX: 0.5,
				anchorY: 0.5,
				// Make particles much bigger overall
				scaleX: 1.1 + Math.random() * 0.8,
				scaleY: 1.1 + Math.random() * 0.8,
				// Use a deeper, more saturated red
				color: 0xff0000,
				alpha: 0.82 + Math.random() * 0.18
			});
			p.x = self.x;
			p.y = self.y;
			var angle = Math.random() * Math.PI * 2;
			var speed = 22 + Math.random() * 20;
			var life = 26 + Math.floor(Math.random() * 18);
			p.update = function (a, s, l) {
				var vx = Math.cos(a) * s;
				var vy = Math.sin(a) * s;
				var ticks = 0;
				return function () {
					this.x += vx;
					this.y += vy;
					vx *= 0.88;
					vy *= 0.88;
					this.alpha *= 0.93;
					ticks++;
					if (ticks > l) {
						this.destroy();
					}
				};
			}(angle, speed, life);
			game.addChild(p);
			// Add to update loop
			if (!game._enemyParticles) game._enemyParticles = [];
			game._enemyParticles.push(p);
		}
	};
	// Initialize health bar
	self.updateHpBar();
	return self;
});
// --- HealthBox Class ---
var HealthBox = Container.expand(function () {
	var self = Container.call(this);
	// Attach health asset (red box), scaled up 1.5x
	var asset = self.attachAsset('healthBox', {
		anchorX: 0.5,
		anchorY: 0.5,
		scaleX: 1.5,
		scaleY: 1.5
	});
	self.radius = 60 + Math.random() * 40;
	self.speed = 0.32 + Math.random() * 0.16; // even faster movement
	self.maxHp = 1;
	self.hp = self.maxHp;
	self.direction = 0;
	// Defensive: track last position
	self.lastX = self.x;
	self.lastY = self.y;
	// Update method
	self.update = function () {
		self.lastX = self.x;
		self.lastY = self.y;
		self.x += Math.cos(self.direction) * self.speed;
		self.y += Math.sin(self.direction) * self.speed;
	};
	// Take damage
	self.takeDamage = function (amount) {
		self.hp -= amount;
		if (self.hp < 0) self.hp = 0;
	};
	// On death
	self.die = function () {
		LK.effects.flashObject(self, 0xffffff, 200);
		// Particle effect (white explosion)
		for (var i = 0; i < 18; i++) {
			var p = new Container();
			var asset = p.attachAsset('bullet', {
				anchorX: 0.5,
				anchorY: 0.5,
				scaleX: 0.9 + Math.random() * 0.7,
				scaleY: 0.9 + Math.random() * 0.7,
				color: 0xffffff,
				alpha: 0.8 + Math.random() * 0.15
			});
			p.x = self.x;
			p.y = self.y;
			var angle = Math.random() * Math.PI * 2;
			var speed = 12 + Math.random() * 10;
			var life = 16 + Math.floor(Math.random() * 10);
			p.update = function (a, s, l) {
				var vx = Math.cos(a) * s;
				var vy = Math.sin(a) * s;
				var ticks = 0;
				return function () {
					this.x += vx;
					this.y += vy;
					vx *= 0.88;
					vy *= 0.88;
					this.alpha *= 0.91;
					ticks++;
					if (ticks > l) {
						this.destroy();
					}
				};
			}(angle, speed, life);
			game.addChild(p);
			if (!game._enemyParticles) game._enemyParticles = [];
			game._enemyParticles.push(p);
		}
	};
	return self;
});
// --- Player Class ---
var Player = Container.expand(function () {
	var self = Container.call(this);
	// Attach player asset (blue ellipse), scaled up 2x
	var playerAsset = self.attachAsset('character', {
		anchorX: 0.5,
		anchorY: 0.5,
		scaleX: 2,
		scaleY: 2
	});
	// Player properties
	self.radius = 0; // always at center
	self.rotation = 0; // radians
	self.maxHp = 5;
	self.hp = self.maxHp;
	// Health bar (red background, green foreground) - always on top, not rotating
	self.hpBarBg = LK.getAsset('hpbar_bg', {
		anchorX: 0.5,
		anchorY: 0.5,
		y: -140
	});
	self.hpBar = LK.getAsset('hpbar_fg', {
		anchorX: 0.5,
		anchorY: 0.5,
		y: -140
	});
	// Defensive: track last position for possible future use
	self.lastX = self.x;
	self.lastY = self.y;
	// Update health bar width
	self.updateHpBar = function () {
		// Clamp hp to [0, maxHp]
		if (self.hp < 0) self.hp = 0;
		if (self.hp > self.maxHp) self.hp = self.maxHp;
		// Use asset width for scaling, so health bar always matches background
		// Health bar shrinks from left to right (left edge fixed)
		var targetWidth = self.hpBarBg.width * (self.hp / self.maxHp);
		if (targetWidth < 0) targetWidth = 0;
		// Smoothly animate width decrease (if needed)
		if (typeof self.hpBar.width === "undefined") self.hpBar.width = self.hpBarBg.width;
		if (self.hpBar.width > targetWidth) {
			self.hpBar.width -= Math.max(6, (self.hpBar.width - targetWidth) * 0.18);
			if (self.hpBar.width < targetWidth) self.hpBar.width = targetWidth;
		} else {
			self.hpBar.width = targetWidth;
		}
		// Left edge fixed: set x so left edge stays at bg left
		self.hpBar.x = self.hpBarBg.x - self.hpBarBg.width / 2 + self.hpBar.width / 2;
		// Add a white border to the health bar background for better visibility
		if (!self.hpBarBg._borderAdded) {
			var border = LK.getAsset('hpbar_bg', {
				anchorX: 0.5,
				anchorY: 0.5,
				y: self.hpBarBg.y,
				width: self.hpBarBg.width + 8,
				height: self.hpBarBg.height + 8,
				color: 0xffffff,
				alpha: 0.25
			});
			border.x = self.hpBarBg.x;
			border.y = self.hpBarBg.y;
			if (self.hpBarBg.parent) self.hpBarBg.parent.addChild(border);
			self.hpBarBg._borderAdded = true;
		}
	};
	// Take damage
	self.takeDamage = function (amount) {
		self.hp -= amount;
		if (self.hp < 0) self.hp = 0;
		if (self.hp > self.maxHp) self.hp = self.maxHp;
		self.updateHpBar();
	};
	// On death
	self.die = function () {
		LK.effects.flashObject(self, 0xff0000, 500);
	};
	// Initialize health bar
	self.updateHpBar();
	return self;
});
// --- Shield Layer Class ---
var ShieldLayer = Container.expand(function () {
	var self = Container.call(this);
	// Properties
	self.radius = 0; // distance from player center
	self.maxHp = 3;
	self.hp = self.maxHp;
	self.index = 0; // 0=innermost, 1=outer
	self.active = true;
	// Visual: support shield1, shield2, and shield3 assets
	var shieldAssetNames = ['shield1', 'shield2', 'shield3'];
	var assetName = shieldAssetNames[self.index] || 'shield1';
	self.shieldAsset = self.attachAsset(assetName, {
		anchorX: 0.5,
		anchorY: 0.5,
		scaleX: 4.2,
		scaleY: 4.2,
		alpha: 0.25
	});
	// Health bar above shield
	self.hpBarBg = self.addChild(LK.getAsset('hpbar_bg', {
		anchorX: 0.5,
		anchorY: 0.5,
		y: -self.radius - 120
	}));
	self.hpBar = self.addChild(LK.getAsset('hpbar_fg', {
		anchorX: 0.5,
		anchorY: 0.5,
		y: -self.radius - 120
	}));
	// Defensive: track last position for possible future use
	self.lastX = self.x;
	self.lastY = self.y;
	// Update health bar width
	self.updateHpBar = function () {
		// Use asset width for scaling, so health bar always matches background
		self.hpBar.width = self.hpBarBg.width * (self.hp / self.maxHp);
		self.hpBar.x = self.hpBarBg.x - (self.hpBarBg.width - self.hpBar.width) / 2;
		self.hpBar.visible = self.active;
		self.hpBarBg.visible = self.active;
	};
	// Take damage
	self.takeDamage = function (amount) {
		if (!self.active) return;
		self.hp -= amount;
		if (self.hp < 0) self.hp = 0;
		self.updateHpBar();
		if (self.hp <= 0) {
			// Play shield explosion sound
			LK.getSound('shieldExplode').play();
			self.active = false;
			self.visible = false;
			self.hpBar.visible = false;
			self.hpBarBg.visible = false;
		}
	};
	// Reset shield
	self.reset = function () {
		self.hp = self.maxHp;
		self.active = true;
		self.visible = true;
		self.updateHpBar();
	};
	// Initialize
	self.updateHpBar();
	return self;
});
/**** 
* Initialize Game
****/ 
var game = new LK.Game({
	backgroundColor: 0x181818
});
/**** 
* Game Code
****/ 
// Health bar assets for bg/fg
// --- Background Image ---
var backgroundImg = LK.getAsset('background', {
	anchorX: 0.5,
	anchorY: 0.5,
	x: 2048 / 2,
	y: 2732 / 2,
	scaleX: 2048 / 4096,
	scaleY: 2732 / 4096
});
game.addChild(backgroundImg);
// --- Global Variables --- 
var player;
var enemies = [];
var bullets = [];
var spawnTimer = 0;
var fireCooldown = 0;
var dragActive = false;
var lastTouchAngle = 0;
// --- Score Display ---
var scoreTxt = new Text2('0', {
	size: 120,
	fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// --- Helper Line for Fire Direction (dotted, in fire direction, aligned with bullet) ---
var helperLineDots = [];
var helperLineAlpha = 0.7;
var helperLineWidth = 18;
var helperLineDotSpacing = 48; // px between dots
// Calculate max possible length from player to farthest screen edge
function getHelperLineLength(angle) {
	// Player is always at center
	var cx = 2048 / 2;
	var cy = 2732 / 2;
	// Find intersection with screen edge in direction of angle
	var dx = Math.cos(angle);
	var dy = Math.sin(angle);
	// Calculate t for each edge, pick the smallest positive t
	var tVals = [];
	if (dx !== 0) {
		var t1 = (0 - cx) / dx;
		var t2 = (2048 - cx) / dx;
		tVals.push(t1, t2);
	}
	if (dy !== 0) {
		var t3 = (0 - cy) / dy;
		var t4 = (2732 - cy) / dy;
		tVals.push(t3, t4);
	}
	// Only consider positive t (forward direction)
	var maxLen = 0;
	for (var i = 0; i < tVals.length; i++) {
		if (tVals[i] > 0) {
			var px = cx + dx * tVals[i];
			var py = cy + dy * tVals[i];
			// Check if point is on screen
			if (px >= 0 && px <= 2048 && py >= 0 && py <= 2732) {
				var dist = Math.sqrt((px - cx) * (px - cx) + (py - cy) * (py - cy));
				if (dist > maxLen) maxLen = dist;
			}
		}
	}
	// Clamp to a minimum length
	if (maxLen < 200) maxLen = 200;
	return maxLen;
}
// Pre-create enough dots for the longest possible line (diagonal)
var maxPossibleLength = Math.sqrt(2048 * 2048 + 2732 * 2732);
var helperLineDotCount = Math.ceil(maxPossibleLength / helperLineDotSpacing);
for (var i = 1; i <= helperLineDotCount; i++) {
	var dot = new Container();
	var asset = dot.attachAsset('helperDot', {
		anchorX: 0.5,
		anchorY: 0.5,
		scaleX: helperLineWidth / 32,
		scaleY: helperLineWidth / 32,
		alpha: helperLineAlpha
	});
	dot.visible = false;
	game.addChild(dot);
	helperLineDots.push(dot);
}
// --- Player Initialization ---
player = new Player();
player.x = 2048 / 2;
player.y = 2732 / 2;
// Always keep player sprite visually fixed (no rotation)
player.rotation = 0;
game.addChild(player);
// Add player health bar to game (not as child of player, so it doesn't rotate)
game.addChild(player.hpBarBg);
game.addChild(player.hpBar);
// Position health bar above player
player.hpBarBg.x = player.x;
player.hpBarBg.y = player.y - 170;
player.hpBar.x = player.x;
player.hpBar.y = player.y - 170;
// --- Shield Layers Initialization ---
// Add three shield layers: shield1 (innermost), shield2 (2x), shield3 (3x)
var shieldLayers = [];
var shieldRadii = [110, 220, 330]; // shield1, shield2, shield3 (further out)
var shieldScales = [4.2, 8.4, 12.6]; // shield2 is 2x, shield3 is 3x larger than shield1
var shieldAssetNames = ['shield1', 'shield2', 'shield3'];
for (var i = 0; i < 3; i++) {
	var shield = new ShieldLayer();
	shield.index = i;
	shield.radius = shieldRadii[i];
	// Place shield at player center
	shield.x = 2048 / 2;
	shield.y = 2732 / 2;
	// Set shield visual size
	shield.shieldAsset.destroy(); // Remove default asset
	shield.shieldAsset = shield.attachAsset(shieldAssetNames[i], {
		anchorX: 0.5,
		anchorY: 0.5,
		scaleX: shieldScales[i],
		scaleY: shieldScales[i],
		alpha: 0.25 + 0.15 * i
	});
	// Position health bar above shield
	if (i === 1) {
		// shield2: place its health bar above shield2 visual, and above shield1/player health bar
		// Calculate shield2 visual height (scaled)
		var shield2VisualHeight = LK.getAsset('shield2', {
			anchorX: 0.5,
			anchorY: 0.5
		}).height * shieldScales[1];
		// Place health bar just above shield2 visual
		shield.hpBarBg.y = -shield2VisualHeight / 2 - 60;
		shield.hpBar.y = -shield2VisualHeight / 2 - 60;
	} else if (i === 2) {
		// shield3: place its health bar above shield3 visual, and above shield2
		var shield3VisualHeight = LK.getAsset('shield3', {
			anchorX: 0.5,
			anchorY: 0.5
		}).height * shieldScales[2];
		shield.hpBarBg.y = -shield3VisualHeight / 2 - 60;
		shield.hpBar.y = -shield3VisualHeight / 2 - 60;
	} else {
		// shield1: default position
		shield.hpBarBg.y = -shield.radius - 120;
		shield.hpBar.y = -shield.radius - 120;
	}
	// Add to game and to shieldLayers array
	game.addChild(shield);
	shieldLayers.push(shield);
}
// --- Helper: Get angle from center to (x, y) ---
function getAngleToCenter(x, y) {
	var cx = 2048 / 2;
	var cy = 2732 / 2;
	return Math.atan2(y - cy, x - cx);
}
// --- Helper: Fire Bullet ---
function fireBullet(angle) {
	if (fireCooldown > 0) return;
	var bullet = new Bullet();
	bullet.x = player.x;
	bullet.y = player.y;
	bullet.direction = angle;
	// Defensive: initialize lastX/lastY
	bullet.lastX = bullet.x;
	bullet.lastY = bullet.y;
	bullets.push(bullet);
	game.addChild(bullet);
	fireCooldown = 4; // frames (was 10, now fires much more frequently)
	// Play shoot sound
	LK.getSound('shoot').play();
}
// --- Handle Touch/Drag to Set Fire Angle (Player sprite does NOT rotate) ---
game.down = function (x, y, obj) {
	dragActive = true;
	// Store angle from player center to touch point
	var angle = Math.atan2(y - player.y, x - player.x);
	lastTouchAngle = angle;
};
game.move = function (x, y, obj) {
	if (dragActive) {
		// Update angle from player center to current touch point
		var angle = Math.atan2(y - player.y, x - player.x);
		lastTouchAngle = angle;
	}
};
game.up = function (x, y, obj) {
	dragActive = false;
	// Fire bullet in the direction of last drag/touch (from center to release point)
	fireBullet(lastTouchAngle);
};
// --- Enemy Spawning ---
function spawnEnemy() {
	var angle = Math.random() * Math.PI * 2;
	var distance = 1100 + Math.random() * 400;
	var ex = 2048 / 2 + Math.cos(angle) * distance;
	var ey = 2732 / 2 + Math.sin(angle) * distance;
	// 1 in 5 chance to spawn a health box instead of enemy (increased frequency)
	if (Math.random() < 0.2) {
		var healthBox = new HealthBox();
		healthBox.x = ex;
		healthBox.y = ey;
		healthBox.direction = Math.atan2(2732 / 2 - ey, 2048 / 2 - ex);
		healthBox.lastX = healthBox.x;
		healthBox.lastY = healthBox.y;
		enemies.push(healthBox);
		game.addChild(healthBox);
	} else {
		var enemy = new Enemy();
		enemy.x = ex;
		enemy.y = ey;
		// Set direction towards player center
		enemy.direction = Math.atan2(2732 / 2 - ey, 2048 / 2 - ex);
		// Scale enemy speed with score: base speed + (score * 0.08), capped at 7x base
		var score = LK.getScore();
		var speedScale = 1 + Math.min(score * 0.08, 6); // up to 7x base speed
		enemy.speed *= speedScale;
		// Defensive: initialize lastX/lastY
		enemy.lastX = enemy.x;
		enemy.lastY = enemy.y;
		enemies.push(enemy);
		game.addChild(enemy);
	}
}
// --- Game Update Loop ---
game.update = function () {
	// --- Fire cooldown ---
	if (fireCooldown > 0) fireCooldown--;
	// --- Update Helper Line (dotted, in fire direction, aligned with bullet) ---
	var cx = player.x;
	var cy = player.y;
	var lineLen = getHelperLineLength(lastTouchAngle);
	var dotCount = Math.floor(lineLen / helperLineDotSpacing);
	for (var i = 0; i < helperLineDots.length; i++) {
		var dot = helperLineDots[i];
		if (i < dotCount) {
			var dist = (i + 1) * helperLineDotSpacing;
			dot.x = cx + Math.cos(lastTouchAngle) * dist;
			dot.y = cy + Math.sin(lastTouchAngle) * dist;
			dot.rotation = lastTouchAngle;
			dot.visible = true;
		} else {
			dot.visible = false;
		}
	}
	// Keep player health bar above player (not rotating)
	player.hpBarBg.x = player.x;
	player.hpBarBg.y = player.y - 170;
	player.hpBar.x = player.x;
	player.hpBar.y = player.y - 170;
	player.updateHpBar();
	// --- Enemy spawn timer ---
	spawnTimer--;
	if (spawnTimer <= 0) {
		spawnEnemy();
		// Make enemies and health boxes spawn less frequently (every 2.5-3.5s)
		spawnTimer = 150 + Math.floor(Math.random() * 60); // spawn every 2.5-3.5s
	}
	// --- Update Bullets ---
	for (var i = bullets.length - 1; i >= 0; i--) {
		var bullet = bullets[i];
		// Defensive: initialize lastX/lastY if not set
		if (typeof bullet.lastX === "undefined") bullet.lastX = bullet.x;
		if (typeof bullet.lastY === "undefined") bullet.lastY = bullet.y;
		bullet.update();
		// Remove if out of bounds or destroyed
		if (bullet.x < -200 || bullet.x > 2048 + 200 || bullet.y < -200 || bullet.y > 2732 + 200 || bullet.destroyed) {
			if (!bullet.destroyed) bullet.destroy();
			bullets.splice(i, 1);
			continue;
		}
		// Check collision with enemies
		var hit = false;
		for (var j = enemies.length - 1; j >= 0; j--) {
			var enemy = enemies[j];
			// Defensive: initialize lastX/lastY if not set
			if (typeof enemy.lastX === "undefined") enemy.lastX = enemy.x;
			if (typeof enemy.lastY === "undefined") enemy.lastY = enemy.y;
			if (enemy.destroyed) continue;
			if (bullet.intersects(enemy)) {
				// HealthBox dies in 1 hit, Enemy dies at 0 hp
				var isHealthBox = typeof enemy.maxHp !== "undefined" && enemy.maxHp === 1;
				if (isHealthBox) {
					// HealthBox: always die in one hit, regardless of hp
					enemy.hp = 0;
				} else {
					enemy.takeDamage(bullet.damage);
				}
				if (enemy.hp <= 0) {
					var isHealthBox = typeof enemy.maxHp !== "undefined" && enemy.maxHp === 1;
					if (isHealthBox) {
						// HealthBox: fill player health to max
						player.hp = player.maxHp;
						player.updateHpBar();
						// Find the outermost active shield (highest index, active)
						var outermostActive = -1;
						for (var si = shieldLayers.length - 1; si >= 0; si--) {
							if (shieldLayers[si].active) {
								outermostActive = si;
								break;
							}
						}
						// If all shields are inactive, add the next shield (lowest inactive)
						if (outermostActive === -1) {
							for (var si = 0; si < shieldLayers.length; si++) {
								if (!shieldLayers[si].active) {
									shieldLayers[si].reset();
									LK.effects.flashObject(shieldLayers[si], 0x44ff44, 400);
									LK.getSound('shieldHit').play();
									break;
								}
							}
						} else {
							// If outermost shield is not full, restore its HP
							var shield = shieldLayers[outermostActive];
							if (shield.hp < shield.maxHp) {
								shield.hp = shield.maxHp;
								shield.updateHpBar();
								LK.effects.flashObject(shield, 0x44ff44, 400);
								LK.getSound('shieldHit').play();
							} else {
								// If outermost shield is full, try to add the next shield (if exists and inactive)
								if (outermostActive + 1 < shieldLayers.length && !shieldLayers[outermostActive + 1].active) {
									shieldLayers[outermostActive + 1].reset();
									LK.effects.flashObject(shieldLayers[outermostActive + 1], 0x44ff44, 400);
									LK.getSound('shieldHit').play();
								} else {
									// All shields are active and full, just play effect on outermost
									LK.effects.flashObject(shield, 0x44ff44, 400);
									LK.getSound('shieldHit').play();
								}
							}
						}
					} else {
						LK.getSound('enemyDie').play();
					}
					enemy.die();
					if (!enemy.destroyed) enemy.destroy();
					enemies.splice(j, 1);
					if (!(typeof enemy.maxHp !== "undefined" && enemy.maxHp === 1)) {
						LK.setScore(LK.getScore() + 1);
						scoreTxt.setText(LK.getScore());
					}
				}
				if (!bullet.destroyed) bullet.destroy();
				bullets.splice(i, 1);
				hit = true;
				break;
			}
		}
		if (hit) continue;
	}
	// Remove destroyed bullets (defensive, in case any remain)
	for (var i = bullets.length - 1; i >= 0; i--) {
		if (bullets[i].destroyed) bullets.splice(i, 1);
	}
	// --- Update Enemies ---
	for (var i = enemies.length - 1; i >= 0; i--) {
		var enemy = enemies[i];
		// Defensive: initialize lastX/lastY if not set
		if (typeof enemy.lastX === "undefined") enemy.lastX = enemy.x;
		if (typeof enemy.lastY === "undefined") enemy.lastY = enemy.y;
		if (enemy.destroyed) {
			enemies.splice(i, 1);
			continue;
		}
		enemy.update();
		// Check collision with shields (outermost to innermost)
		var blocked = false;
		for (var s = shieldLayers.length - 1; s >= 0; s--) {
			var shield = shieldLayers[s];
			if (shield.active) {
				// Defensive: initialize lastX/lastY if not set
				if (typeof shield.lastX === "undefined") shield.lastX = shield.x;
				if (typeof shield.lastY === "undefined") shield.lastY = shield.y;
				// Check distance from player center to enemy center
				var dx = enemy.x - player.x;
				var dy = enemy.y - player.y;
				var dist = Math.sqrt(dx * dx + dy * dy);
				// Use actual shield visual size for collision (shieldAsset is ellipse, scaleX/scaleY applied)
				// Defensive: recalculate radius using current scale for each shield asset
				var shieldAssetNames = ['shield1', 'shield2', 'shield3'];
				var assetName = shieldAssetNames[shield.index] || 'shield1';
				var baseRadius = LK.getAsset(assetName, {
					anchorX: 0.5,
					anchorY: 0.5
				}).width / 2;
				var shieldVisualRadius = baseRadius * shield.shieldAsset.scaleX;
				var enemyVisualRadius = (enemy.width > enemy.height ? enemy.width : enemy.height) / 2;
				if (dist < shieldVisualRadius + enemyVisualRadius) {
					// Collided with shield
					var isHealthBox = typeof enemy.maxHp !== "undefined" && enemy.maxHp === 1;
					if (isHealthBox) {
						// HealthBox: just disappears, does not affect shield, no revive or restore
						enemy.die();
						if (!enemy.destroyed) enemy.destroy();
						enemies.splice(i, 1);
						blocked = true;
						break;
					} else {
						// Regular enemy: damage shield
						shield.takeDamage(1);
						LK.getSound('shieldHit').play();
						enemy.die();
						if (!enemy.destroyed) enemy.destroy();
						enemies.splice(i, 1);
						// Increase score when enemy dies by hitting shield
						LK.setScore(LK.getScore() + 1);
						scoreTxt.setText(LK.getScore());
						// Flash shield
						LK.effects.flashObject(shield, 0x44aaff, 200);
						blocked = true;
						break;
					}
				}
			}
		}
		if (blocked) continue;
		// If not blocked, check collision with player
		if (enemy.intersects(player)) {
			var isHealthBox = typeof enemy.maxHp !== "undefined" && enemy.maxHp === 1;
			if (isHealthBox) {
				// HealthBox: just disappears, does not affect player health
				enemy.die();
				if (!enemy.destroyed) enemy.destroy();
				enemies.splice(i, 1);
				// Do not change player health or flash screen
			} else {
				player.takeDamage(1);
				if (player.hp < 0) player.hp = 0;
				if (player.hp > player.maxHp) player.hp = player.maxHp;
				LK.getSound('enemyDie').play();
				enemy.die();
				if (!enemy.destroyed) enemy.destroy();
				enemies.splice(i, 1);
				// Flash screen
				LK.effects.flashScreen(0xff0000, 400);
				// Game over if player dead
				if (player.hp <= 0) {
					player.die();
					LK.showGameOver();
					return;
				}
			}
		}
	}
	// Remove destroyed enemies (defensive, in case any remain)
	for (var i = enemies.length - 1; i >= 0; i--) {
		if (enemies[i].destroyed) enemies.splice(i, 1);
	}
	;
	// --- Update and cleanup enemy death particles ---
	if (game._enemyParticles) {
		for (var i = game._enemyParticles.length - 1; i >= 0; i--) {
			var p = game._enemyParticles[i];
			// Defensive: initialize lastX/lastY if not set
			if (typeof p.lastX === "undefined") p.lastX = p.x;
			if (typeof p.lastY === "undefined") p.lastY = p.y;
			if (p.destroyed) {
				game._enemyParticles.splice(i, 1);
				continue;
			}
			if (typeof p.update === "function") p.update();
		}
		// Defensive: remove any remaining destroyed particles
		for (var i = game._enemyParticles.length - 1; i >= 0; i--) {
			if (game._enemyParticles[i].destroyed) game._enemyParticles.splice(i, 1);
		}
	}
};
// --- Score Initialization ---
LK.setScore(0);
scoreTxt.setText(LK.getScore());