/**** 
* Plugins
****/ 
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/**** 
* Classes
****/ 
// Collectible class: moves downward, destroys itself off screen
var Collectible = Container.expand(function () {
	var self = Container.call(this);
	var colAsset = self.attachAsset('collectible', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	self.radius = colAsset.width / 2;
	self.speed = 12; // Will be set by game
	self.update = function () {
		self.y += self.speed;
	};
	return self;
});
// Fireball projectile: moves upward, destroys itself off screen or on hit
var Fireball = Container.expand(function () {
	var self = Container.call(this);
	var asset = self.attachAsset('skin_gold', {
		anchorX: 0.5,
		anchorY: 0.5,
		width: 60,
		height: 60
	});
	self.radius = asset.width / 2;
	self.speed = -36; // Fast upward
	self.update = function () {
		self.y += self.speed;
	};
	return self;
});
// FireballPowerup: when collected, enables fireball shooting for a duration
var FireballPowerup = Container.expand(function () {
	var self = Container.call(this);
	var asset = self.attachAsset('fireball_powerup', {
		anchorX: 0.5,
		anchorY: 0.5,
		width: 100,
		height: 100
	});
	self.radius = asset.width / 2;
	self.speed = 12;
	self.update = function () {
		self.y += self.speed;
	};
	return self;
});
// Obstacle class: moves downward, destroys itself off screen
var Obstacle = Container.expand(function () {
	var self = Container.call(this);
	var obsAsset = self.attachAsset('obstacle', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	self.width = obsAsset.width;
	self.height = obsAsset.height;
	self.speed = 12; // Will be set by game
	self.update = function () {
		self.y += self.speed;
	};
	return self;
});
// Player class: follows face position, grows when mouth is open
var Player = Container.expand(function () {
	var self = Container.call(this);
	var playerAsset = self.attachAsset('player', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	self.radius = playerAsset.width / 2;
	// For collision, always use self.x, self.y, and self.radius
	// Animate mouth open/close (scale up/down)
	self.setMouthOpen = function (open) {
		var targetScale = open ? 1.25 : 1.0;
		tween.stop(self, {
			scaleX: true,
			scaleY: true
		});
		tween(self, {
			scaleX: targetScale,
			scaleY: targetScale
		}, {
			duration: 180,
			easing: tween.easeOut
		});
	};
	// For update, nothing needed (position is set by game)
	self.update = function () {};
	return self;
});
/**** 
* Initialize Game
****/ 
var game = new LK.Game({
	backgroundColor: 0x3a7eea
});
/**** 
* Game Code
****/ 
// Slider assets
// Collectible: Green circle
// Obstacle: Red rectangles
// Character: The player avatar, a bright ellipse
// Game area
var GAME_W = 2048;
var GAME_H = 2732;
// Player setup
var player = new Player();
player.x = GAME_W / 2;
player.y = GAME_H * 0.8;
player.scaleX = 1.0;
player.scaleY = 1.0;
game.addChild(player);
// Apply selected skin if not default
if (typeof selectedSkin !== 'undefined' && selectedSkin !== 'player') {
	player.removeChildren();
	var newAsset = player.attachAsset(selectedSkin, {
		anchorX: 0.5,
		anchorY: 0.5
	});
	player.radius = newAsset.width / 2;
}
// Score display
var scoreTxt = new Text2('0', {
	size: 120,
	fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Arrays for obstacles, collectibles, fireballs, and powerups
var obstacles = [];
var collectibles = [];
var fireballs = [];
var fireballPowerups = [];
// Fireball powerup state
var fireballActive = false;
var fireballTimer = 0;
var fireballShootTicks = 0;
// Game state
var score = 0;
var ticks = 0;
var gameSpeed = 12; // Initial speed
var spawnInterval = 60; // Frames between spawns (1 sec at 60fps)
var collectibleInterval = 180; // Frames between collectibles
// For collision detection
function circleRectIntersect(cx, cy, cr, rx, ry, rw, rh) {
	// Find closest point to circle within rectangle
	var closestX = Math.max(rx - rw / 2, Math.min(cx, rx + rw / 2));
	var closestY = Math.max(ry - rh / 2, Math.min(cy, ry + rh / 2));
	var dx = cx - closestX;
	var dy = cy - closestY;
	return dx * dx + dy * dy < cr * cr;
}
function circleCircleIntersect(x1, y1, r1, x2, y2, r2) {
	var dx = x1 - x2;
	var dy = y1 - y2;
	var distSq = dx * dx + dy * dy;
	var rSum = r1 + r2;
	return distSq < rSum * rSum;
}
// Slider control variables
var sliderBar = null;
var sliderKnob = null;
var sliderDragging = false;
var sliderBarWidth = GAME_W; // Make the slider bar as wide as the game area
var sliderBarHeight = 32;
var sliderKnobRadius = 70;
var sliderBarY = GAME_H - 220; // Place slider above bottom, visible on all screens
// Create slider bar and knob using dedicated assets
sliderBar = LK.getAsset('slider_bar', {
	anchorX: 0.5,
	anchorY: 0.5,
	x: GAME_W / 2,
	y: sliderBarY
});
sliderKnob = LK.getAsset('slider_knob', {
	anchorX: 0.5,
	anchorY: 0.5,
	x: GAME_W / 2,
	y: sliderBarY
});
sliderKnob.width = sliderKnobRadius * 2;
sliderKnob.height = sliderKnobRadius * 2;
game.addChild(sliderBar);
game.addChild(sliderKnob);
// Helper to clamp knob position
function clampSliderKnobX(x) {
	// Allow knob to reach the very left and right edges of the bar
	var minX = sliderBar.x - sliderBarWidth / 2 + sliderKnobRadius;
	var maxX = sliderBar.x + sliderBarWidth / 2 - sliderKnobRadius;
	// If the knob is wider than the bar, clamp to center
	if (minX > maxX) {
		return sliderBar.x;
	}
	return Math.max(minX, Math.min(maxX, x));
}
// Set initial knob position
sliderKnob.x = GAME_W / 2;
// Update player position from slider
function updatePlayerFromSlider() {
	// Map knob X to player X, allowing player to reach the very left and right edges
	var minX = player.radius;
	var maxX = GAME_W - player.radius;
	var knobMinX = sliderBar.x - sliderBarWidth / 2 + sliderKnobRadius;
	var knobMaxX = sliderBar.x + sliderBarWidth / 2 - sliderKnobRadius;
	// If the knob is wider than the bar, set t to 0.5 (center)
	var t = knobMaxX === knobMinX ? 0.5 : (sliderKnob.x - knobMinX) / (knobMaxX - knobMinX);
	player.x = minX + t * (maxX - minX);
	// Player Y is fixed
	player.y = GAME_H * 0.8;
}
// Touch/mouse events for slider
game.down = function (x, y, obj) {
	// Only start drag if touch is near knob
	var dx = x - sliderKnob.x;
	var dy = y - sliderKnob.y;
	if (dx * dx + dy * dy <= sliderKnobRadius * sliderKnobRadius * 1.2) {
		sliderDragging = true;
		// Move knob immediately
		sliderKnob.x = clampSliderKnobX(x);
		updatePlayerFromSlider();
	}
};
game.move = function (x, y, obj) {
	if (sliderDragging) {
		sliderKnob.x = clampSliderKnobX(x);
		updatePlayerFromSlider();
	}
};
game.up = function (x, y, obj) {
	sliderDragging = false;
};
// Remove facekit control entirely
// Spawning
function spawnObstacle() {
	var obs = new Obstacle();
	// Random X, avoid edges
	var margin = 80;
	obs.x = margin + Math.random() * (GAME_W - 2 * margin);
	obs.y = -obs.height / 2;
	obs.speed = gameSpeed;
	obstacles.push(obs);
	game.addChild(obs);
}
function spawnCollectible() {
	var col = new Collectible();
	var margin = 80;
	col.x = margin + Math.random() * (GAME_W - 2 * margin);
	col.y = -col.radius;
	col.speed = gameSpeed;
	collectibles.push(col);
	game.addChild(col);
}
// Fireball powerup spawner (rare)
function spawnFireballPowerup() {
	var p = new FireballPowerup();
	var margin = 80;
	p.x = margin + Math.random() * (GAME_W - 2 * margin);
	p.y = -p.radius;
	p.speed = gameSpeed;
	fireballPowerups.push(p);
	game.addChild(p);
}
// Difficulty scaling
function updateDifficulty() {
	// Every 7 points, increase speed and spawn rate (harder)
	var level = Math.floor(score / 7);
	gameSpeed = 14 + level * 2.5; // Start faster, scale faster
	spawnInterval = Math.max(18, 48 - level * 5); // Min ~0.3s, faster spawn
}
// Main update loop
game.update = function () {
	ticks++;
	// Slider control
	updatePlayerFromSlider();
	// --- Fireball Powerup Spawning (rare, every 10 seconds) ---
	if (ticks % (60 * 10) === 0) {
		if (Math.random() < 0.7) {
			// 70% chance every 10s
			spawnFireballPowerup();
		}
	}
	// Spawn obstacles
	if (ticks % spawnInterval === 0) {
		spawnObstacle();
	}
	// Spawn collectibles
	if (ticks % collectibleInterval === 0) {
		spawnCollectible();
	}
	// --- Fireball Powerup Update & Collision ---
	for (var fpi = fireballPowerups.length - 1; fpi >= 0; fpi--) {
		var p = fireballPowerups[fpi];
		p.speed = gameSpeed;
		p.update();
		// Collect powerup
		if (circleCircleIntersect(player.x, player.y, player.radius * player.scaleX, p.x, p.y, p.radius)) {
			fireballActive = true;
			// 5-10 seconds random duration
			fireballTimer = 60 * (5 + Math.floor(Math.random() * 6));
			fireballShootTicks = 0;
			LK.effects.flashObject(player, 0xffa500, 400);
			p.destroy();
			fireballPowerups.splice(fpi, 1);
			continue;
		}
		// Off screen
		if (p.y - p.radius > GAME_H + 100) {
			p.destroy();
			fireballPowerups.splice(fpi, 1);
		}
	}
	// --- Fireball Powerup Timer & Shooting ---
	if (fireballActive) {
		fireballTimer--;
		fireballShootTicks++;
		// Shoot fireball every 18 frames (~3.3/sec, less projectiles)
		if (fireballShootTicks % 18 === 0) {
			var fb = new Fireball();
			fb.x = player.x;
			fb.y = player.y - player.radius * player.scaleY - 30;
			fireballs.push(fb);
			game.addChild(fb);
		}
		if (fireballTimer <= 0) {
			fireballActive = false;
		}
	}
	// --- Fireball Update & Collision with Obstacles ---
	for (var fbi = fireballs.length - 1; fbi >= 0; fbi--) {
		var fb = fireballs[fbi];
		fb.update();
		var destroyed = false;
		// Remove if off screen
		if (fb.y + fb.radius < -100) {
			fb.destroy();
			fireballs.splice(fbi, 1);
			continue;
		}
		// Check collision with obstacles
		for (var oi = obstacles.length - 1; oi >= 0; oi--) {
			var obs = obstacles[oi];
			if (circleRectIntersect(fb.x, fb.y, fb.radius, obs.x, obs.y, obs.width, obs.height)) {
				// Destroy both fireball and obstacle
				obs.destroy();
				obstacles.splice(oi, 1);
				fb.destroy();
				fireballs.splice(fbi, 1);
				destroyed = true;
				break;
			}
		}
		if (destroyed) continue;
	}
	// Update obstacles
	for (var i = obstacles.length - 1; i >= 0; i--) {
		var obs = obstacles[i];
		obs.speed = gameSpeed;
		obs.update();
		// Collision with player
		if (circleRectIntersect(player.x, player.y, player.radius * player.scaleX, obs.x, obs.y, obs.width, obs.height)) {
			// Flash screen, game over
			LK.effects.flashScreen(0xff0000, 800);
			LK.showGameOver();
			return;
		}
		// Off screen
		if (obs.y - obs.height / 2 > GAME_H + 100) {
			obs.destroy();
			obstacles.splice(i, 1);
		}
	}
	// Update collectibles
	for (var j = collectibles.length - 1; j >= 0; j--) {
		var col = collectibles[j];
		col.speed = gameSpeed;
		col.update();
		// Collect
		if (circleCircleIntersect(player.x, player.y, player.radius * player.scaleX, col.x, col.y, col.radius)) {
			score += 1;
			LK.setScore(score);
			scoreTxt.setText(score);
			updateDifficulty();
			// Update allTimePoints in storage
			if (!storage.allTimePoints) storage.allTimePoints = 0;
			storage.allTimePoints = storage.allTimePoints + 1;
			// Flash green
			LK.effects.flashObject(player, 0x7ed957, 300);
			col.destroy();
			collectibles.splice(j, 1);
			// Win condition
			if (score >= 50) {
				LK.showYouWin();
				return;
			}
		}
		// Off screen
		if (col.y - col.radius > GAME_H + 100) {
			col.destroy();
			collectibles.splice(j, 1);
		}
	}
};
// Initial score
LK.setScore(0);
scoreTxt.setText(0);
// Place score text at top center, avoid top left 100x100
scoreTxt.y = 0;
scoreTxt.x = 0;
// Skin shop feature removed
// No drag/move events needed; all control is via facekit
// End of MVP /**** 
* Plugins
****/ 
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/**** 
* Classes
****/ 
// Collectible class: moves downward, destroys itself off screen
var Collectible = Container.expand(function () {
	var self = Container.call(this);
	var colAsset = self.attachAsset('collectible', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	self.radius = colAsset.width / 2;
	self.speed = 12; // Will be set by game
	self.update = function () {
		self.y += self.speed;
	};
	return self;
});
// Fireball projectile: moves upward, destroys itself off screen or on hit
var Fireball = Container.expand(function () {
	var self = Container.call(this);
	var asset = self.attachAsset('skin_gold', {
		anchorX: 0.5,
		anchorY: 0.5,
		width: 60,
		height: 60
	});
	self.radius = asset.width / 2;
	self.speed = -36; // Fast upward
	self.update = function () {
		self.y += self.speed;
	};
	return self;
});
// FireballPowerup: when collected, enables fireball shooting for a duration
var FireballPowerup = Container.expand(function () {
	var self = Container.call(this);
	var asset = self.attachAsset('fireball_powerup', {
		anchorX: 0.5,
		anchorY: 0.5,
		width: 100,
		height: 100
	});
	self.radius = asset.width / 2;
	self.speed = 12;
	self.update = function () {
		self.y += self.speed;
	};
	return self;
});
// Obstacle class: moves downward, destroys itself off screen
var Obstacle = Container.expand(function () {
	var self = Container.call(this);
	var obsAsset = self.attachAsset('obstacle', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	self.width = obsAsset.width;
	self.height = obsAsset.height;
	self.speed = 12; // Will be set by game
	self.update = function () {
		self.y += self.speed;
	};
	return self;
});
// Player class: follows face position, grows when mouth is open
var Player = Container.expand(function () {
	var self = Container.call(this);
	var playerAsset = self.attachAsset('player', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	self.radius = playerAsset.width / 2;
	// For collision, always use self.x, self.y, and self.radius
	// Animate mouth open/close (scale up/down)
	self.setMouthOpen = function (open) {
		var targetScale = open ? 1.25 : 1.0;
		tween.stop(self, {
			scaleX: true,
			scaleY: true
		});
		tween(self, {
			scaleX: targetScale,
			scaleY: targetScale
		}, {
			duration: 180,
			easing: tween.easeOut
		});
	};
	// For update, nothing needed (position is set by game)
	self.update = function () {};
	return self;
});
/**** 
* Initialize Game
****/ 
var game = new LK.Game({
	backgroundColor: 0x3a7eea
});
/**** 
* Game Code
****/ 
// Slider assets
// Collectible: Green circle
// Obstacle: Red rectangles
// Character: The player avatar, a bright ellipse
// Game area
var GAME_W = 2048;
var GAME_H = 2732;
// Player setup
var player = new Player();
player.x = GAME_W / 2;
player.y = GAME_H * 0.8;
player.scaleX = 1.0;
player.scaleY = 1.0;
game.addChild(player);
// Apply selected skin if not default
if (typeof selectedSkin !== 'undefined' && selectedSkin !== 'player') {
	player.removeChildren();
	var newAsset = player.attachAsset(selectedSkin, {
		anchorX: 0.5,
		anchorY: 0.5
	});
	player.radius = newAsset.width / 2;
}
// Score display
var scoreTxt = new Text2('0', {
	size: 120,
	fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Arrays for obstacles, collectibles, fireballs, and powerups
var obstacles = [];
var collectibles = [];
var fireballs = [];
var fireballPowerups = [];
// Fireball powerup state
var fireballActive = false;
var fireballTimer = 0;
var fireballShootTicks = 0;
// Game state
var score = 0;
var ticks = 0;
var gameSpeed = 12; // Initial speed
var spawnInterval = 60; // Frames between spawns (1 sec at 60fps)
var collectibleInterval = 180; // Frames between collectibles
// For collision detection
function circleRectIntersect(cx, cy, cr, rx, ry, rw, rh) {
	// Find closest point to circle within rectangle
	var closestX = Math.max(rx - rw / 2, Math.min(cx, rx + rw / 2));
	var closestY = Math.max(ry - rh / 2, Math.min(cy, ry + rh / 2));
	var dx = cx - closestX;
	var dy = cy - closestY;
	return dx * dx + dy * dy < cr * cr;
}
function circleCircleIntersect(x1, y1, r1, x2, y2, r2) {
	var dx = x1 - x2;
	var dy = y1 - y2;
	var distSq = dx * dx + dy * dy;
	var rSum = r1 + r2;
	return distSq < rSum * rSum;
}
// Slider control variables
var sliderBar = null;
var sliderKnob = null;
var sliderDragging = false;
var sliderBarWidth = GAME_W; // Make the slider bar as wide as the game area
var sliderBarHeight = 32;
var sliderKnobRadius = 70;
var sliderBarY = GAME_H - 220; // Place slider above bottom, visible on all screens
// Create slider bar and knob using dedicated assets
sliderBar = LK.getAsset('slider_bar', {
	anchorX: 0.5,
	anchorY: 0.5,
	x: GAME_W / 2,
	y: sliderBarY
});
sliderKnob = LK.getAsset('slider_knob', {
	anchorX: 0.5,
	anchorY: 0.5,
	x: GAME_W / 2,
	y: sliderBarY
});
sliderKnob.width = sliderKnobRadius * 2;
sliderKnob.height = sliderKnobRadius * 2;
game.addChild(sliderBar);
game.addChild(sliderKnob);
// Helper to clamp knob position
function clampSliderKnobX(x) {
	// Allow knob to reach the very left and right edges of the bar
	var minX = sliderBar.x - sliderBarWidth / 2 + sliderKnobRadius;
	var maxX = sliderBar.x + sliderBarWidth / 2 - sliderKnobRadius;
	// If the knob is wider than the bar, clamp to center
	if (minX > maxX) {
		return sliderBar.x;
	}
	return Math.max(minX, Math.min(maxX, x));
}
// Set initial knob position
sliderKnob.x = GAME_W / 2;
// Update player position from slider
function updatePlayerFromSlider() {
	// Map knob X to player X, allowing player to reach the very left and right edges
	var minX = player.radius;
	var maxX = GAME_W - player.radius;
	var knobMinX = sliderBar.x - sliderBarWidth / 2 + sliderKnobRadius;
	var knobMaxX = sliderBar.x + sliderBarWidth / 2 - sliderKnobRadius;
	// If the knob is wider than the bar, set t to 0.5 (center)
	var t = knobMaxX === knobMinX ? 0.5 : (sliderKnob.x - knobMinX) / (knobMaxX - knobMinX);
	player.x = minX + t * (maxX - minX);
	// Player Y is fixed
	player.y = GAME_H * 0.8;
}
// Touch/mouse events for slider
game.down = function (x, y, obj) {
	// Only start drag if touch is near knob
	var dx = x - sliderKnob.x;
	var dy = y - sliderKnob.y;
	if (dx * dx + dy * dy <= sliderKnobRadius * sliderKnobRadius * 1.2) {
		sliderDragging = true;
		// Move knob immediately
		sliderKnob.x = clampSliderKnobX(x);
		updatePlayerFromSlider();
	}
};
game.move = function (x, y, obj) {
	if (sliderDragging) {
		sliderKnob.x = clampSliderKnobX(x);
		updatePlayerFromSlider();
	}
};
game.up = function (x, y, obj) {
	sliderDragging = false;
};
// Remove facekit control entirely
// Spawning
function spawnObstacle() {
	var obs = new Obstacle();
	// Random X, avoid edges
	var margin = 80;
	obs.x = margin + Math.random() * (GAME_W - 2 * margin);
	obs.y = -obs.height / 2;
	obs.speed = gameSpeed;
	obstacles.push(obs);
	game.addChild(obs);
}
function spawnCollectible() {
	var col = new Collectible();
	var margin = 80;
	col.x = margin + Math.random() * (GAME_W - 2 * margin);
	col.y = -col.radius;
	col.speed = gameSpeed;
	collectibles.push(col);
	game.addChild(col);
}
// Fireball powerup spawner (rare)
function spawnFireballPowerup() {
	var p = new FireballPowerup();
	var margin = 80;
	p.x = margin + Math.random() * (GAME_W - 2 * margin);
	p.y = -p.radius;
	p.speed = gameSpeed;
	fireballPowerups.push(p);
	game.addChild(p);
}
// Difficulty scaling
function updateDifficulty() {
	// Every 7 points, increase speed and spawn rate (harder)
	var level = Math.floor(score / 7);
	gameSpeed = 14 + level * 2.5; // Start faster, scale faster
	spawnInterval = Math.max(18, 48 - level * 5); // Min ~0.3s, faster spawn
}
// Main update loop
game.update = function () {
	ticks++;
	// Slider control
	updatePlayerFromSlider();
	// --- Fireball Powerup Spawning (rare, every 10 seconds) ---
	if (ticks % (60 * 10) === 0) {
		if (Math.random() < 0.7) {
			// 70% chance every 10s
			spawnFireballPowerup();
		}
	}
	// Spawn obstacles
	if (ticks % spawnInterval === 0) {
		spawnObstacle();
	}
	// Spawn collectibles
	if (ticks % collectibleInterval === 0) {
		spawnCollectible();
	}
	// --- Fireball Powerup Update & Collision ---
	for (var fpi = fireballPowerups.length - 1; fpi >= 0; fpi--) {
		var p = fireballPowerups[fpi];
		p.speed = gameSpeed;
		p.update();
		// Collect powerup
		if (circleCircleIntersect(player.x, player.y, player.radius * player.scaleX, p.x, p.y, p.radius)) {
			fireballActive = true;
			// 5-10 seconds random duration
			fireballTimer = 60 * (5 + Math.floor(Math.random() * 6));
			fireballShootTicks = 0;
			LK.effects.flashObject(player, 0xffa500, 400);
			p.destroy();
			fireballPowerups.splice(fpi, 1);
			continue;
		}
		// Off screen
		if (p.y - p.radius > GAME_H + 100) {
			p.destroy();
			fireballPowerups.splice(fpi, 1);
		}
	}
	// --- Fireball Powerup Timer & Shooting ---
	if (fireballActive) {
		fireballTimer--;
		fireballShootTicks++;
		// Shoot fireball every 18 frames (~3.3/sec, less projectiles)
		if (fireballShootTicks % 18 === 0) {
			var fb = new Fireball();
			fb.x = player.x;
			fb.y = player.y - player.radius * player.scaleY - 30;
			fireballs.push(fb);
			game.addChild(fb);
		}
		if (fireballTimer <= 0) {
			fireballActive = false;
		}
	}
	// --- Fireball Update & Collision with Obstacles ---
	for (var fbi = fireballs.length - 1; fbi >= 0; fbi--) {
		var fb = fireballs[fbi];
		fb.update();
		var destroyed = false;
		// Remove if off screen
		if (fb.y + fb.radius < -100) {
			fb.destroy();
			fireballs.splice(fbi, 1);
			continue;
		}
		// Check collision with obstacles
		for (var oi = obstacles.length - 1; oi >= 0; oi--) {
			var obs = obstacles[oi];
			if (circleRectIntersect(fb.x, fb.y, fb.radius, obs.x, obs.y, obs.width, obs.height)) {
				// Destroy both fireball and obstacle
				obs.destroy();
				obstacles.splice(oi, 1);
				fb.destroy();
				fireballs.splice(fbi, 1);
				destroyed = true;
				break;
			}
		}
		if (destroyed) continue;
	}
	// Update obstacles
	for (var i = obstacles.length - 1; i >= 0; i--) {
		var obs = obstacles[i];
		obs.speed = gameSpeed;
		obs.update();
		// Collision with player
		if (circleRectIntersect(player.x, player.y, player.radius * player.scaleX, obs.x, obs.y, obs.width, obs.height)) {
			// Flash screen, game over
			LK.effects.flashScreen(0xff0000, 800);
			LK.showGameOver();
			return;
		}
		// Off screen
		if (obs.y - obs.height / 2 > GAME_H + 100) {
			obs.destroy();
			obstacles.splice(i, 1);
		}
	}
	// Update collectibles
	for (var j = collectibles.length - 1; j >= 0; j--) {
		var col = collectibles[j];
		col.speed = gameSpeed;
		col.update();
		// Collect
		if (circleCircleIntersect(player.x, player.y, player.radius * player.scaleX, col.x, col.y, col.radius)) {
			score += 1;
			LK.setScore(score);
			scoreTxt.setText(score);
			updateDifficulty();
			// Update allTimePoints in storage
			if (!storage.allTimePoints) storage.allTimePoints = 0;
			storage.allTimePoints = storage.allTimePoints + 1;
			// Flash green
			LK.effects.flashObject(player, 0x7ed957, 300);
			col.destroy();
			collectibles.splice(j, 1);
			// Win condition
			if (score >= 50) {
				LK.showYouWin();
				return;
			}
		}
		// Off screen
		if (col.y - col.radius > GAME_H + 100) {
			col.destroy();
			collectibles.splice(j, 1);
		}
	}
};
// Initial score
LK.setScore(0);
scoreTxt.setText(0);
// Place score text at top center, avoid top left 100x100
scoreTxt.y = 0;
scoreTxt.x = 0;
// Skin shop feature removed
// No drag/move events needed; all control is via facekit
// End of MVP