/**** 
* Plugins
****/ 
var tween = LK.import("@upit/tween.v1");
/**** 
* Classes
****/ 
// Enemy (hazard)
var Enemy = Container.expand(function () {
	var self = Container.call(this);
	var asset = self.attachAsset('enemy', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	self.width = asset.width;
	self.height = asset.height;
	self.moveDir = Math.random() < 0.5 ? 1 : -1;
	self.moveSpeed = 3 + Math.random() * 3;
	self.range = 400 + Math.random() * 200;
	self.baseX = 0;
	self.t = 0;
	self.init = function () {
		self.baseX = self.x;
		self.t = Math.random() * 1000;
	};
	self.update = function () {
		self.t += self.moveSpeed;
		self.x = self.baseX + Math.sin(self.t / 180) * self.range;
	};
	return self;
});
// Jumper (player)
var Jumper = Container.expand(function () {
	var self = Container.call(this);
	var asset = self.attachAsset('jumper', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	self.width = asset.width;
	self.height = asset.height;
	self.vx = 0;
	self.vy = 0;
	self.jetpackTime = 0;
	self.jetpackActive = false;
	self.springBoost = false;
	self.springTimer = 0;
	self.update = function () {
		// Jetpack
		if (self.jetpackActive) {
			self.vy = -38;
			self.jetpackTime--;
			if (self.jetpackTime <= 0) {
				self.jetpackActive = false;
			}
		}
		// Spring
		if (self.springBoost) {
			self.vy = -48;
			self.springBoost = false;
			self.springTimer = 10;
		}
		if (self.springTimer > 0) {
			self.springTimer--;
		}
		// Gravity
		if (!self.jetpackActive) {
			self.vy += 2.2;
			if (self.vy > 60) self.vy = 60;
		}
		// Move
		self.x += self.vx;
		self.y += self.vy;
		// Wrap horizontally
		if (self.x < -self.width / 2) self.x = 2048 + self.width / 2;
		if (self.x > 2048 + self.width / 2) self.x = -self.width / 2;
	};
	self.jump = function (power) {
		self.vy = -power;
	};
	self.activateJetpack = function () {
		self.jetpackActive = true;
		self.jetpackTime = 90;
	};
	self.activateSpring = function () {
		self.springBoost = true;
	};
	return self;
});
// Platform types: 'normal', 'break', 'move'
var Platform = Container.expand(function () {
	var self = Container.call(this);
	self.type = 'normal';
	self.broken = false;
	self.moveDir = 1; // 1:right, -1:left
	self.moveSpeed = 4 + Math.random() * 4;
	self.range = 300 + Math.random() * 300;
	self.baseX = 0;
	self.t = 0;
	self.asset = null;
	self.init = function (type) {
		self.type = type;
		if (type === 'normal') {
			self.asset = self.attachAsset('platform', {
				anchorX: 0.5,
				anchorY: 0.5
			});
		} else if (type === 'break') {
			self.asset = self.attachAsset('platform_break', {
				anchorX: 0.5,
				anchorY: 0.5
			});
		} else if (type === 'move') {
			self.asset = self.attachAsset('platform_move', {
				anchorX: 0.5,
				anchorY: 0.5
			});
		}
		self.width = self.asset.width;
		self.height = self.asset.height;
		self.baseX = self.x;
		self.t = Math.random() * 1000;
	};
	self.update = function () {
		if (self.type === 'move') {
			self.t += self.moveSpeed;
			self.x = self.baseX + Math.sin(self.t / 180) * self.range;
		}
	};
	self.breakPlatform = function () {
		if (self.type === 'break' && !self.broken) {
			self.broken = true;
			tween(self.asset, {
				alpha: 0
			}, {
				duration: 300,
				onFinish: function onFinish() {
					self.visible = false;
				}
			});
		}
	};
	return self;
});
// PowerUp: type = 'spring' or 'jetpack'
var PowerUp = Container.expand(function () {
	var self = Container.call(this);
	self.type = 'spring';
	self.active = true;
	self.asset = null;
	self.init = function (type) {
		self.type = type;
		if (type === 'spring') {
			self.asset = self.attachAsset('spring', {
				anchorX: 0.5,
				anchorY: 0.5
			});
		} else if (type === 'jetpack') {
			self.asset = self.attachAsset('jetpack', {
				anchorX: 0.5,
				anchorY: 0.5
			});
		}
		self.width = self.asset.width;
		self.height = self.asset.height;
	};
	self.collect = function () {
		self.active = false;
		tween(self.asset, {
			alpha: 0
		}, {
			duration: 200,
			onFinish: function onFinish() {
				self.visible = false;
			}
		});
	};
	return self;
});
/**** 
* Initialize Game
****/ 
var game = new LK.Game({
	backgroundColor: 0x87ceeb // Sky blue
});
/**** 
* Game Code
****/ 
// Game constants
// Character: a springy jumper
// Platform: normal
// Platform: breakable
// Platform: moving
// Hazard: enemy
// Power-up: spring
// Power-up: jetpack
var GAME_WIDTH = 2048;
var GAME_HEIGHT = 2732;
var PLATFORM_MIN_Y = 200;
var PLATFORM_MAX_Y = 400;
var PLATFORM_WIDTH = 300;
var PLATFORM_HEIGHT = 40;
var JUMPER_START_X = GAME_WIDTH / 2;
var JUMPER_START_Y = GAME_HEIGHT - 400;
var CAMERA_OFFSET = 900; // How far from bottom before camera scrolls
// Game state
var jumper;
var platforms = [];
var enemies = [];
var powerups = [];
var maxY = 0; // Highest Y reached (lowest value, since y decreases as you go up)
var cameraY = 0;
var dragNode = null;
var lastDownX = 0;
var lastDownY = 0;
var scoreTxt;
var gameOver = false;
// Score display
scoreTxt = new Text2('0', {
	size: 120,
	fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Helper: create a platform at (x, y) of given type
function createPlatform(x, y, type) {
	var plat = new Platform();
	plat.x = x;
	plat.y = y;
	plat.init(type);
	platforms.push(plat);
	game.addChild(plat);
	return plat;
}
// Helper: create an enemy at (x, y)
function createEnemy(x, y) {
	var enemy = new Enemy();
	enemy.x = x;
	enemy.y = y;
	enemy.init();
	enemies.push(enemy);
	game.addChild(enemy);
	return enemy;
}
// Helper: create a powerup at (x, y) of given type
function createPowerUp(x, y, type) {
	var pu = new PowerUp();
	pu.x = x;
	pu.y = y;
	pu.init(type);
	powerups.push(pu);
	game.addChild(pu);
	return pu;
}
// Reset game state
function resetGame() {
	// Remove all platforms, enemies, powerups
	for (var i = 0; i < platforms.length; ++i) platforms[i].destroy();
	for (var i = 0; i < enemies.length; ++i) enemies[i].destroy();
	for (var i = 0; i < powerups.length; ++i) powerups[i].destroy();
	platforms = [];
	enemies = [];
	powerups = [];
	// Create initial platform directly under jumper
	createPlatform(JUMPER_START_X, JUMPER_START_Y + 80, 'normal');
	// Create initial platforms
	var y = JUMPER_START_Y + 200;
	while (y > -2000) {
		var x = 200 + Math.random() * (GAME_WIDTH - 400);
		var type = 'normal';
		if (Math.random() < 0.12 && y < JUMPER_START_Y - 600) type = 'break';else if (Math.random() < 0.15 && y < JUMPER_START_Y - 800) type = 'move';
		createPlatform(x, y, type);
		y -= PLATFORM_MIN_Y + Math.random() * (PLATFORM_MAX_Y - PLATFORM_MIN_Y);
	}
	// Add some enemies and powerups
	for (var i = 0; i < 6; ++i) {
		var px = 200 + Math.random() * (GAME_WIDTH - 400);
		var py = JUMPER_START_Y - 800 - Math.random() * 2000;
		createEnemy(px, py);
	}
	for (var i = 0; i < 4; ++i) {
		var px = 200 + Math.random() * (GAME_WIDTH - 400);
		var py = JUMPER_START_Y - 600 - Math.random() * 2000;
		var type = Math.random() < 0.5 ? 'spring' : 'jetpack';
		createPowerUp(px, py, type);
	}
	// Create jumper
	jumper = new Jumper();
	jumper.x = JUMPER_START_X;
	jumper.y = JUMPER_START_Y;
	jumper.vx = 0;
	jumper.vy = 0;
	game.addChild(jumper);
	// Reset camera
	cameraY = 0;
	maxY = jumper.y;
	// Reset score
	LK.setScore(0);
	scoreTxt.setText('0');
	gameOver = false;
}
// Camera scroll
function updateCamera() {
	// Camera follows jumper upward, but not downward
	var targetY = jumper.y - CAMERA_OFFSET;
	if (targetY < cameraY) {
		cameraY = targetY;
	}
	// Move all objects relative to camera
	for (var i = 0; i < platforms.length; ++i) platforms[i].yRel = platforms[i].y - cameraY;
	for (var i = 0; i < enemies.length; ++i) enemies[i].yRel = enemies[i].y - cameraY;
	for (var i = 0; i < powerups.length; ++i) powerups[i].yRel = powerups[i].y - cameraY;
	jumper.yRel = jumper.y - cameraY;
	// Actually set y for display
	for (var i = 0; i < platforms.length; ++i) platforms[i].position.y = platforms[i].yRel;
	for (var i = 0; i < enemies.length; ++i) enemies[i].position.y = enemies[i].yRel;
	for (var i = 0; i < powerups.length; ++i) powerups[i].position.y = powerups[i].yRel;
	jumper.position.y = jumper.yRel;
}
// Generate new platforms as needed
function generatePlatforms() {
	// Find highest platform
	var minY = 999999;
	for (var i = 0; i < platforms.length; ++i) {
		if (platforms[i].y < minY) minY = platforms[i].y;
	}
	// If highest platform is above screen, add more
	while (minY > cameraY - 800) {
		var x = 200 + Math.random() * (GAME_WIDTH - 400);
		var y = minY - (PLATFORM_MIN_Y + Math.random() * (PLATFORM_MAX_Y - PLATFORM_MIN_Y));
		var type = 'normal';
		if (Math.random() < 0.13) type = 'break';else if (Math.random() < 0.17) type = 'move';
		createPlatform(x, y, type);
		// Maybe add enemy or powerup
		if (Math.random() < 0.08) createEnemy(x, y - 100);
		if (Math.random() < 0.07) createPowerUp(x, y - 60, Math.random() < 0.5 ? 'spring' : 'jetpack');
		minY = y;
	}
}
// Remove offscreen objects
function cleanupObjects() {
	// Platforms
	for (var i = platforms.length - 1; i >= 0; --i) {
		if (platforms[i].yRel > GAME_HEIGHT + 200) {
			platforms[i].destroy();
			platforms.splice(i, 1);
		}
	}
	// Enemies
	for (var i = enemies.length - 1; i >= 0; --i) {
		if (enemies[i].yRel > GAME_HEIGHT + 200) {
			enemies[i].destroy();
			enemies.splice(i, 1);
		}
	}
	// Powerups
	for (var i = powerups.length - 1; i >= 0; --i) {
		if (powerups[i].yRel > GAME_HEIGHT + 200 || !powerups[i].active) {
			powerups[i].destroy();
			powerups.splice(i, 1);
		}
	}
}
// Platform collision
function checkPlatformCollision() {
	if (jumper.vy > 0) {
		// Only when falling
		for (var i = 0; i < platforms.length; ++i) {
			var plat = platforms[i];
			if (!plat.visible || plat.broken) continue;
			// Simple AABB
			var px = plat.x,
				py = plat.yRel;
			var pw = plat.width,
				ph = plat.height;
			var jx = jumper.x,
				jy = jumper.yRel;
			var jw = jumper.width,
				jh = jumper.height;
			if (jy + jh / 2 > py - ph / 2 && jy + jh / 2 < py + ph / 2) {
				if (jx + jw / 2 > px - pw / 2 && jx - jw / 2 < px + pw / 2) {
					// Landed on platform
					if (plat.type === 'break') {
						plat.breakPlatform();
						return;
					}
					jumper.jump(38);
					// Score: 1 per platform landed
					LK.setScore(LK.getScore() + 1);
					scoreTxt.setText(LK.getScore());
					return;
				}
			}
		}
	}
}
// Powerup collision
function checkPowerUpCollision() {
	for (var i = 0; i < powerups.length; ++i) {
		var pu = powerups[i];
		if (!pu.active) continue;
		var px = pu.x,
			py = pu.yRel;
		var pw = pu.width,
			ph = pu.height;
		var jx = jumper.x,
			jy = jumper.yRel;
		var jw = jumper.width,
			jh = jumper.height;
		if (Math.abs(jx - px) < (jw + pw) / 2 && Math.abs(jy - py) < (jh + ph) / 2) {
			// Collect
			pu.collect();
			if (pu.type === 'spring') {
				jumper.activateSpring();
			} else if (pu.type === 'jetpack') {
				jumper.activateJetpack();
			}
		}
	}
}
// Enemy collision
function checkEnemyCollision() {
	for (var i = 0; i < enemies.length; ++i) {
		var en = enemies[i];
		var ex = en.x,
			ey = en.yRel;
		var ew = en.width,
			eh = en.height;
		var jx = jumper.x,
			jy = jumper.yRel;
		var jw = jumper.width,
			jh = jumper.height;
		if (Math.abs(jx - ex) < (jw + ew) / 2 - 10 && Math.abs(jy - ey) < (jh + eh) / 2 - 10) {
			// Collided
			LK.effects.flashScreen(0xff0000, 800);
			gameOver = true;
			LK.showGameOver();
			return;
		}
	}
}
// Game over if fall off screen
function checkFall() {
	if (jumper.yRel > GAME_HEIGHT + 100) {
		LK.effects.flashScreen(0xff0000, 800);
		gameOver = true;
		LK.showGameOver();
	}
}
// Touch controls: drag left/right
game.down = function (x, y, obj) {
	if (gameOver) return;
	dragNode = jumper;
	lastDownX = x;
	lastDownY = y;
};
game.move = function (x, y, obj) {
	if (gameOver) return;
	if (dragNode) {
		var dx = x - lastDownX;
		jumper.x += dx;
		lastDownX = x;
		lastDownY = y;
	}
};
game.up = function (x, y, obj) {
	dragNode = null;
};
// Main update loop
game.update = function () {
	if (gameOver) return;
	jumper.update();
	for (var i = 0; i < platforms.length; ++i) platforms[i].update();
	for (var i = 0; i < enemies.length; ++i) enemies[i].update();
	// Camera
	updateCamera();
	// Generate new platforms
	generatePlatforms();
	// Remove offscreen
	cleanupObjects();
	// Collisions
	checkPlatformCollision();
	checkPowerUpCollision();
	checkEnemyCollision();
	checkFall();
	// Update maxY and score
	if (jumper.y < maxY) {
		maxY = jumper.y;
		var sc = Math.floor((JUMPER_START_Y - maxY) / 10);
		if (sc > LK.getScore()) {
			LK.setScore(sc);
			scoreTxt.setText(sc);
		}
	}
};
// Start game
resetGame(); /**** 
* Plugins
****/ 
var tween = LK.import("@upit/tween.v1");
/**** 
* Classes
****/ 
// Enemy (hazard)
var Enemy = Container.expand(function () {
	var self = Container.call(this);
	var asset = self.attachAsset('enemy', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	self.width = asset.width;
	self.height = asset.height;
	self.moveDir = Math.random() < 0.5 ? 1 : -1;
	self.moveSpeed = 3 + Math.random() * 3;
	self.range = 400 + Math.random() * 200;
	self.baseX = 0;
	self.t = 0;
	self.init = function () {
		self.baseX = self.x;
		self.t = Math.random() * 1000;
	};
	self.update = function () {
		self.t += self.moveSpeed;
		self.x = self.baseX + Math.sin(self.t / 180) * self.range;
	};
	return self;
});
// Jumper (player)
var Jumper = Container.expand(function () {
	var self = Container.call(this);
	var asset = self.attachAsset('jumper', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	self.width = asset.width;
	self.height = asset.height;
	self.vx = 0;
	self.vy = 0;
	self.jetpackTime = 0;
	self.jetpackActive = false;
	self.springBoost = false;
	self.springTimer = 0;
	self.update = function () {
		// Jetpack
		if (self.jetpackActive) {
			self.vy = -38;
			self.jetpackTime--;
			if (self.jetpackTime <= 0) {
				self.jetpackActive = false;
			}
		}
		// Spring
		if (self.springBoost) {
			self.vy = -48;
			self.springBoost = false;
			self.springTimer = 10;
		}
		if (self.springTimer > 0) {
			self.springTimer--;
		}
		// Gravity
		if (!self.jetpackActive) {
			self.vy += 2.2;
			if (self.vy > 60) self.vy = 60;
		}
		// Move
		self.x += self.vx;
		self.y += self.vy;
		// Wrap horizontally
		if (self.x < -self.width / 2) self.x = 2048 + self.width / 2;
		if (self.x > 2048 + self.width / 2) self.x = -self.width / 2;
	};
	self.jump = function (power) {
		self.vy = -power;
	};
	self.activateJetpack = function () {
		self.jetpackActive = true;
		self.jetpackTime = 90;
	};
	self.activateSpring = function () {
		self.springBoost = true;
	};
	return self;
});
// Platform types: 'normal', 'break', 'move'
var Platform = Container.expand(function () {
	var self = Container.call(this);
	self.type = 'normal';
	self.broken = false;
	self.moveDir = 1; // 1:right, -1:left
	self.moveSpeed = 4 + Math.random() * 4;
	self.range = 300 + Math.random() * 300;
	self.baseX = 0;
	self.t = 0;
	self.asset = null;
	self.init = function (type) {
		self.type = type;
		if (type === 'normal') {
			self.asset = self.attachAsset('platform', {
				anchorX: 0.5,
				anchorY: 0.5
			});
		} else if (type === 'break') {
			self.asset = self.attachAsset('platform_break', {
				anchorX: 0.5,
				anchorY: 0.5
			});
		} else if (type === 'move') {
			self.asset = self.attachAsset('platform_move', {
				anchorX: 0.5,
				anchorY: 0.5
			});
		}
		self.width = self.asset.width;
		self.height = self.asset.height;
		self.baseX = self.x;
		self.t = Math.random() * 1000;
	};
	self.update = function () {
		if (self.type === 'move') {
			self.t += self.moveSpeed;
			self.x = self.baseX + Math.sin(self.t / 180) * self.range;
		}
	};
	self.breakPlatform = function () {
		if (self.type === 'break' && !self.broken) {
			self.broken = true;
			tween(self.asset, {
				alpha: 0
			}, {
				duration: 300,
				onFinish: function onFinish() {
					self.visible = false;
				}
			});
		}
	};
	return self;
});
// PowerUp: type = 'spring' or 'jetpack'
var PowerUp = Container.expand(function () {
	var self = Container.call(this);
	self.type = 'spring';
	self.active = true;
	self.asset = null;
	self.init = function (type) {
		self.type = type;
		if (type === 'spring') {
			self.asset = self.attachAsset('spring', {
				anchorX: 0.5,
				anchorY: 0.5
			});
		} else if (type === 'jetpack') {
			self.asset = self.attachAsset('jetpack', {
				anchorX: 0.5,
				anchorY: 0.5
			});
		}
		self.width = self.asset.width;
		self.height = self.asset.height;
	};
	self.collect = function () {
		self.active = false;
		tween(self.asset, {
			alpha: 0
		}, {
			duration: 200,
			onFinish: function onFinish() {
				self.visible = false;
			}
		});
	};
	return self;
});
/**** 
* Initialize Game
****/ 
var game = new LK.Game({
	backgroundColor: 0x87ceeb // Sky blue
});
/**** 
* Game Code
****/ 
// Game constants
// Character: a springy jumper
// Platform: normal
// Platform: breakable
// Platform: moving
// Hazard: enemy
// Power-up: spring
// Power-up: jetpack
var GAME_WIDTH = 2048;
var GAME_HEIGHT = 2732;
var PLATFORM_MIN_Y = 200;
var PLATFORM_MAX_Y = 400;
var PLATFORM_WIDTH = 300;
var PLATFORM_HEIGHT = 40;
var JUMPER_START_X = GAME_WIDTH / 2;
var JUMPER_START_Y = GAME_HEIGHT - 400;
var CAMERA_OFFSET = 900; // How far from bottom before camera scrolls
// Game state
var jumper;
var platforms = [];
var enemies = [];
var powerups = [];
var maxY = 0; // Highest Y reached (lowest value, since y decreases as you go up)
var cameraY = 0;
var dragNode = null;
var lastDownX = 0;
var lastDownY = 0;
var scoreTxt;
var gameOver = false;
// Score display
scoreTxt = new Text2('0', {
	size: 120,
	fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Helper: create a platform at (x, y) of given type
function createPlatform(x, y, type) {
	var plat = new Platform();
	plat.x = x;
	plat.y = y;
	plat.init(type);
	platforms.push(plat);
	game.addChild(plat);
	return plat;
}
// Helper: create an enemy at (x, y)
function createEnemy(x, y) {
	var enemy = new Enemy();
	enemy.x = x;
	enemy.y = y;
	enemy.init();
	enemies.push(enemy);
	game.addChild(enemy);
	return enemy;
}
// Helper: create a powerup at (x, y) of given type
function createPowerUp(x, y, type) {
	var pu = new PowerUp();
	pu.x = x;
	pu.y = y;
	pu.init(type);
	powerups.push(pu);
	game.addChild(pu);
	return pu;
}
// Reset game state
function resetGame() {
	// Remove all platforms, enemies, powerups
	for (var i = 0; i < platforms.length; ++i) platforms[i].destroy();
	for (var i = 0; i < enemies.length; ++i) enemies[i].destroy();
	for (var i = 0; i < powerups.length; ++i) powerups[i].destroy();
	platforms = [];
	enemies = [];
	powerups = [];
	// Create initial platform directly under jumper
	createPlatform(JUMPER_START_X, JUMPER_START_Y + 80, 'normal');
	// Create initial platforms
	var y = JUMPER_START_Y + 200;
	while (y > -2000) {
		var x = 200 + Math.random() * (GAME_WIDTH - 400);
		var type = 'normal';
		if (Math.random() < 0.12 && y < JUMPER_START_Y - 600) type = 'break';else if (Math.random() < 0.15 && y < JUMPER_START_Y - 800) type = 'move';
		createPlatform(x, y, type);
		y -= PLATFORM_MIN_Y + Math.random() * (PLATFORM_MAX_Y - PLATFORM_MIN_Y);
	}
	// Add some enemies and powerups
	for (var i = 0; i < 6; ++i) {
		var px = 200 + Math.random() * (GAME_WIDTH - 400);
		var py = JUMPER_START_Y - 800 - Math.random() * 2000;
		createEnemy(px, py);
	}
	for (var i = 0; i < 4; ++i) {
		var px = 200 + Math.random() * (GAME_WIDTH - 400);
		var py = JUMPER_START_Y - 600 - Math.random() * 2000;
		var type = Math.random() < 0.5 ? 'spring' : 'jetpack';
		createPowerUp(px, py, type);
	}
	// Create jumper
	jumper = new Jumper();
	jumper.x = JUMPER_START_X;
	jumper.y = JUMPER_START_Y;
	jumper.vx = 0;
	jumper.vy = 0;
	game.addChild(jumper);
	// Reset camera
	cameraY = 0;
	maxY = jumper.y;
	// Reset score
	LK.setScore(0);
	scoreTxt.setText('0');
	gameOver = false;
}
// Camera scroll
function updateCamera() {
	// Camera follows jumper upward, but not downward
	var targetY = jumper.y - CAMERA_OFFSET;
	if (targetY < cameraY) {
		cameraY = targetY;
	}
	// Move all objects relative to camera
	for (var i = 0; i < platforms.length; ++i) platforms[i].yRel = platforms[i].y - cameraY;
	for (var i = 0; i < enemies.length; ++i) enemies[i].yRel = enemies[i].y - cameraY;
	for (var i = 0; i < powerups.length; ++i) powerups[i].yRel = powerups[i].y - cameraY;
	jumper.yRel = jumper.y - cameraY;
	// Actually set y for display
	for (var i = 0; i < platforms.length; ++i) platforms[i].position.y = platforms[i].yRel;
	for (var i = 0; i < enemies.length; ++i) enemies[i].position.y = enemies[i].yRel;
	for (var i = 0; i < powerups.length; ++i) powerups[i].position.y = powerups[i].yRel;
	jumper.position.y = jumper.yRel;
}
// Generate new platforms as needed
function generatePlatforms() {
	// Find highest platform
	var minY = 999999;
	for (var i = 0; i < platforms.length; ++i) {
		if (platforms[i].y < minY) minY = platforms[i].y;
	}
	// If highest platform is above screen, add more
	while (minY > cameraY - 800) {
		var x = 200 + Math.random() * (GAME_WIDTH - 400);
		var y = minY - (PLATFORM_MIN_Y + Math.random() * (PLATFORM_MAX_Y - PLATFORM_MIN_Y));
		var type = 'normal';
		if (Math.random() < 0.13) type = 'break';else if (Math.random() < 0.17) type = 'move';
		createPlatform(x, y, type);
		// Maybe add enemy or powerup
		if (Math.random() < 0.08) createEnemy(x, y - 100);
		if (Math.random() < 0.07) createPowerUp(x, y - 60, Math.random() < 0.5 ? 'spring' : 'jetpack');
		minY = y;
	}
}
// Remove offscreen objects
function cleanupObjects() {
	// Platforms
	for (var i = platforms.length - 1; i >= 0; --i) {
		if (platforms[i].yRel > GAME_HEIGHT + 200) {
			platforms[i].destroy();
			platforms.splice(i, 1);
		}
	}
	// Enemies
	for (var i = enemies.length - 1; i >= 0; --i) {
		if (enemies[i].yRel > GAME_HEIGHT + 200) {
			enemies[i].destroy();
			enemies.splice(i, 1);
		}
	}
	// Powerups
	for (var i = powerups.length - 1; i >= 0; --i) {
		if (powerups[i].yRel > GAME_HEIGHT + 200 || !powerups[i].active) {
			powerups[i].destroy();
			powerups.splice(i, 1);
		}
	}
}
// Platform collision
function checkPlatformCollision() {
	if (jumper.vy > 0) {
		// Only when falling
		for (var i = 0; i < platforms.length; ++i) {
			var plat = platforms[i];
			if (!plat.visible || plat.broken) continue;
			// Simple AABB
			var px = plat.x,
				py = plat.yRel;
			var pw = plat.width,
				ph = plat.height;
			var jx = jumper.x,
				jy = jumper.yRel;
			var jw = jumper.width,
				jh = jumper.height;
			if (jy + jh / 2 > py - ph / 2 && jy + jh / 2 < py + ph / 2) {
				if (jx + jw / 2 > px - pw / 2 && jx - jw / 2 < px + pw / 2) {
					// Landed on platform
					if (plat.type === 'break') {
						plat.breakPlatform();
						return;
					}
					jumper.jump(38);
					// Score: 1 per platform landed
					LK.setScore(LK.getScore() + 1);
					scoreTxt.setText(LK.getScore());
					return;
				}
			}
		}
	}
}
// Powerup collision
function checkPowerUpCollision() {
	for (var i = 0; i < powerups.length; ++i) {
		var pu = powerups[i];
		if (!pu.active) continue;
		var px = pu.x,
			py = pu.yRel;
		var pw = pu.width,
			ph = pu.height;
		var jx = jumper.x,
			jy = jumper.yRel;
		var jw = jumper.width,
			jh = jumper.height;
		if (Math.abs(jx - px) < (jw + pw) / 2 && Math.abs(jy - py) < (jh + ph) / 2) {
			// Collect
			pu.collect();
			if (pu.type === 'spring') {
				jumper.activateSpring();
			} else if (pu.type === 'jetpack') {
				jumper.activateJetpack();
			}
		}
	}
}
// Enemy collision
function checkEnemyCollision() {
	for (var i = 0; i < enemies.length; ++i) {
		var en = enemies[i];
		var ex = en.x,
			ey = en.yRel;
		var ew = en.width,
			eh = en.height;
		var jx = jumper.x,
			jy = jumper.yRel;
		var jw = jumper.width,
			jh = jumper.height;
		if (Math.abs(jx - ex) < (jw + ew) / 2 - 10 && Math.abs(jy - ey) < (jh + eh) / 2 - 10) {
			// Collided
			LK.effects.flashScreen(0xff0000, 800);
			gameOver = true;
			LK.showGameOver();
			return;
		}
	}
}
// Game over if fall off screen
function checkFall() {
	if (jumper.yRel > GAME_HEIGHT + 100) {
		LK.effects.flashScreen(0xff0000, 800);
		gameOver = true;
		LK.showGameOver();
	}
}
// Touch controls: drag left/right
game.down = function (x, y, obj) {
	if (gameOver) return;
	dragNode = jumper;
	lastDownX = x;
	lastDownY = y;
};
game.move = function (x, y, obj) {
	if (gameOver) return;
	if (dragNode) {
		var dx = x - lastDownX;
		jumper.x += dx;
		lastDownX = x;
		lastDownY = y;
	}
};
game.up = function (x, y, obj) {
	dragNode = null;
};
// Main update loop
game.update = function () {
	if (gameOver) return;
	jumper.update();
	for (var i = 0; i < platforms.length; ++i) platforms[i].update();
	for (var i = 0; i < enemies.length; ++i) enemies[i].update();
	// Camera
	updateCamera();
	// Generate new platforms
	generatePlatforms();
	// Remove offscreen
	cleanupObjects();
	// Collisions
	checkPlatformCollision();
	checkPowerUpCollision();
	checkEnemyCollision();
	checkFall();
	// Update maxY and score
	if (jumper.y < maxY) {
		maxY = jumper.y;
		var sc = Math.floor((JUMPER_START_Y - maxY) / 10);
		if (sc > LK.getScore()) {
			LK.setScore(sc);
			scoreTxt.setText(sc);
		}
	}
};
// Start game
resetGame();
:quality(85)/https://cdn.frvr.ai/682bb404a5a5017f6a058ad2.png%3F3) 
 Character. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
:quality(85)/https://cdn.frvr.ai/682bb459a5a5017f6a058af2.png%3F3) 
 Enemy. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
:quality(85)/https://cdn.frvr.ai/682bb4b2a5a5017f6a058b18.png%3F3) 
 A jetpack. In-Game asset. 2d. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/682bb532a5a5017f6a058b46.png%3F3) 
 A spring.. In-Game asset. 2d. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/682bb571a5a5017f6a058b52.png%3F3) 
 Platform. In-Game asset. 2d. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/682bb5e7a5a5017f6a058b6f.png%3F3) 
 Platform break.. In-Game asset. 2d. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/682bb672a5a5017f6a058b7e.png%3F3) 
 Paltform move.. In-Game asset. 2d. High contrast. No shadows