/**** 
* Plugins
****/ 
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
var facekit = LK.import("@upit/facekit.v1");
/**** 
* Classes
****/ 
var NoteIndicator = Container.expand(function () {
	var self = Container.call(this);
	var indicatorGraphics = self.attachAsset('noteIndicator', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	self.setNote = function (noteIndex) {
		// Position the indicator at its proper y location
		self.y = getYPositionForNote(noteIndex);
	};
	return self;
});
var Obstacle = Container.expand(function () {
	var self = Container.call(this);
	// Main obstacle body
	var obstacleGraphics = self.attachAsset('obstacle', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	// Opening in the obstacle
	self.opening = new Container();
	self.addChild(self.opening);
	var openingGraphics = self.opening.attachAsset('opening', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	self.speed = 5;
	self.active = true;
	self.passed = false;
	self.setOpeningPosition = function (y) {
		self.opening.y = y;
	};
	self.update = function () {
		if (!self.active) {
			return;
		}
		self.x -= self.speed;
		// Check if obstacle is off screen
		if (self.x < -obstacleGraphics.width / 2) {
			self.active = false;
		}
	};
	return self;
});
var Player = Container.expand(function () {
	var self = Container.call(this);
	var playerGraphics = self.attachAsset('player', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	self.targetY = 0;
	self.velocity = 0;
	self.gravity = 0.5;
	self.damping = 0.9;
	self.update = function () {
		// Move towards target position with physics
		var distance = self.targetY - self.y;
		self.velocity += distance * 0.08;
		self.velocity *= self.damping;
		self.y += self.velocity;
		// Keep player within game bounds
		if (self.y < 100) {
			self.y = 100;
			self.velocity = 0;
		} else if (self.y > 2732 - 100) {
			self.y = 2732 - 100;
			self.velocity = 0;
		}
	};
	return self;
});
/**** 
* Initialize Game
****/ 
var game = new LK.Game({
	backgroundColor: 0x121E40
});
/**** 
* Game Code
****/ 
// Game constants
var GAME_WIDTH = 2048;
var GAME_HEIGHT = 2732;
var NOTE_NAMES = ["Do", "Re", "Mi", "Fa", "Sol", "La", "Si", "Do"];
var NOTE_FREQUENCIES = [262, 294, 330, 349, 392, 440, 494, 523]; // C4 to C5 frequencies in Hz
var FREQUENCY_TOLERANCE = 15; // Hz tolerance for detecting notes
// Game variables
var player;
var obstacles = [];
var noteIndicators = [];
var currentNote = -1;
var score = 0;
var started = false;
var obstacleTimer;
var difficultyTimer;
var difficulty = 1;
var obstacleSpeed = 5;
var obstacleInterval = 2000;
// Score display
var scoreTxt = new Text2('Score: 0', {
	size: 70,
	fill: 0xFFFFFF
});
scoreTxt.anchor.set(1, 0);
LK.gui.topRight.addChild(scoreTxt);
// Note display
var noteTxt = new Text2('Sing a note!', {
	size: 70,
	fill: 0xFFFFFF
});
noteTxt.anchor.set(0, 0);
LK.gui.topLeft.addChild(noteTxt);
noteTxt.x = 100; // Move away from top left corner
// Instructions display
var instructionsTxt = new Text2('Sing notes (Do, Re, Mi...) to move up and down.\nAvoid obstacles by positioning correctly.', {
	size: 50,
	fill: 0xFFFFFF
});
instructionsTxt.anchor.set(0.5, 0);
instructionsTxt.y = 100;
LK.gui.top.addChild(instructionsTxt);
// Start button
var startBtn = new Container();
var startBtnBg = startBtn.attachAsset('player', {
	anchorX: 0.5,
	anchorY: 0.5,
	scaleX: 5,
	scaleY: 2
});
startBtnBg.tint = 0x00ff00;
var startBtnTxt = new Text2('START', {
	size: 80,
	fill: 0xFFFFFF
});
startBtnTxt.anchor.set(0.5, 0.5);
startBtn.addChild(startBtnTxt);
startBtn.x = GAME_WIDTH / 2;
startBtn.y = GAME_HEIGHT / 2;
game.addChild(startBtn);
// Create note indicators on the side
function createNoteIndicators() {
	for (var i = 0; i < 8; i++) {
		var indicator = new NoteIndicator();
		indicator.x = 50;
		indicator.setNote(i);
		indicator.alpha = 0.5;
		game.addChild(indicator);
		noteIndicators.push(indicator);
	}
}
// Get Y position based on note index (0-7 for Do to high Do)
function getYPositionForNote(noteIndex) {
	// Map note index to Y position (higher notes = higher position)
	// Reserve space at top and bottom
	var usableHeight = GAME_HEIGHT - 400;
	return GAME_HEIGHT - 200 - noteIndex * (usableHeight / 7);
}
// Handle voice input and map to notes
function handleVoiceInput() {
	if (facekit.pitch > 0 && facekit.volume > 0.1) {
		// Find closest note match
		var detectedNote = -1;
		var closestDistance = Infinity;
		for (var i = 0; i < NOTE_FREQUENCIES.length; i++) {
			var distance = Math.abs(facekit.pitch - NOTE_FREQUENCIES[i]);
			if (distance < closestDistance && distance < FREQUENCY_TOLERANCE) {
				closestDistance = distance;
				detectedNote = i;
			}
		}
		if (detectedNote !== -1) {
			player.targetY = getYPositionForNote(detectedNote);
			// Update current note display if changed
			if (currentNote !== detectedNote) {
				currentNote = detectedNote;
				noteTxt.setText(NOTE_NAMES[currentNote]);
				// Update indicators
				for (var j = 0; j < noteIndicators.length; j++) {
					noteIndicators[j].alpha = j === currentNote ? 1.0 : 0.5;
					if (j === currentNote) {
						tween(noteIndicators[j], {
							scaleX: 1.5,
							scaleY: 1.5
						}, {
							duration: 200,
							onFinish: function onFinish() {
								if (currentNote !== -1) {
									tween(noteIndicators[currentNote], {
										scaleX: 1,
										scaleY: 1
									}, {
										duration: 200
									});
								}
							}
						});
					}
				}
			}
		}
	} else {
		// No sound detected
		currentNote = -1;
		noteTxt.setText("Sing a note!");
	}
}
// Create a new obstacle
function createObstacle() {
	var obstacle = new Obstacle();
	obstacle.x = GAME_WIDTH + 200;
	obstacle.y = GAME_HEIGHT / 2;
	obstacle.speed = obstacleSpeed;
	// Place opening at a random note position
	var noteIndex = Math.floor(Math.random() * 8);
	obstacle.setOpeningPosition(getYPositionForNote(noteIndex) - GAME_HEIGHT / 2);
	game.addChild(obstacle);
	obstacles.push(obstacle);
	return obstacle;
}
// Initialize the game
function initGame() {
	// Create player
	player = new Player();
	player.x = 400;
	player.y = GAME_HEIGHT / 2;
	game.addChild(player);
	// Create note indicators
	createNoteIndicators();
	// Start with an initial obstacle
	createObstacle();
	// Start background music
	LK.playMusic('bgmusic');
	// Hide start button
	game.removeChild(startBtn);
	// Hide instructions after a delay
	LK.setTimeout(function () {
		tween(instructionsTxt, {
			alpha: 0
		}, {
			duration: 1000
		});
	}, 5000);
	// Set up obstacle spawning
	obstacleTimer = LK.setInterval(function () {
		createObstacle();
	}, obstacleInterval);
	// Increase difficulty over time
	difficultyTimer = LK.setInterval(function () {
		difficulty += 0.5;
		obstacleSpeed = Math.min(15, 5 + difficulty);
		obstacleInterval = Math.max(800, 2000 - difficulty * 200);
		// Update existing obstacles
		for (var i = 0; i < obstacles.length; i++) {
			if (obstacles[i].active) {
				obstacles[i].speed = obstacleSpeed;
			}
		}
		// Clear and restart obstacle timer with new interval
		LK.clearInterval(obstacleTimer);
		obstacleTimer = LK.setInterval(function () {
			createObstacle();
		}, obstacleInterval);
	}, 10000);
	started = true;
}
// Handle start button click
startBtn.down = function () {
	initGame();
};
// Check for collision between player and obstacles
function checkCollisions() {
	for (var i = 0; i < obstacles.length; i++) {
		var obstacle = obstacles[i];
		// Skip inactive obstacles
		if (!obstacle.active) {
			continue;
		}
		// Check if player passed an obstacle for the first time
		if (!obstacle.passed && player.x > obstacle.x) {
			obstacle.passed = true;
			score += 1;
			scoreTxt.setText("Score: " + score);
			LK.setScore(score);
			LK.getSound('pass').play();
		}
		// Check for collision with the obstacle body (but not the opening)
		if (player.intersects(obstacle) && !player.intersects(obstacle.opening)) {
			// Player hit an obstacle
			LK.getSound('hit').play();
			LK.effects.flashScreen(0xff0000, 500);
			LK.showGameOver();
			return true;
		}
	}
	return false;
}
// Clean up inactive obstacles
function cleanupObstacles() {
	for (var i = obstacles.length - 1; i >= 0; i--) {
		if (!obstacles[i].active) {
			obstacles[i].destroy();
			obstacles.splice(i, 1);
		}
	}
}
// Main game update loop
game.update = function () {
	if (!started) {
		return;
	}
	// Handle voice input
	handleVoiceInput();
	// Update player
	player.update();
	// Update obstacles
	for (var i = 0; i < obstacles.length; i++) {
		obstacles[i].update();
	}
	// Check for collisions
	if (checkCollisions()) {
		// Game over handling
		return;
	}
	// Clean up off-screen obstacles
	cleanupObstacles();
}; /**** 
* Plugins
****/ 
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
var facekit = LK.import("@upit/facekit.v1");
/**** 
* Classes
****/ 
var NoteIndicator = Container.expand(function () {
	var self = Container.call(this);
	var indicatorGraphics = self.attachAsset('noteIndicator', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	self.setNote = function (noteIndex) {
		// Position the indicator at its proper y location
		self.y = getYPositionForNote(noteIndex);
	};
	return self;
});
var Obstacle = Container.expand(function () {
	var self = Container.call(this);
	// Main obstacle body
	var obstacleGraphics = self.attachAsset('obstacle', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	// Opening in the obstacle
	self.opening = new Container();
	self.addChild(self.opening);
	var openingGraphics = self.opening.attachAsset('opening', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	self.speed = 5;
	self.active = true;
	self.passed = false;
	self.setOpeningPosition = function (y) {
		self.opening.y = y;
	};
	self.update = function () {
		if (!self.active) {
			return;
		}
		self.x -= self.speed;
		// Check if obstacle is off screen
		if (self.x < -obstacleGraphics.width / 2) {
			self.active = false;
		}
	};
	return self;
});
var Player = Container.expand(function () {
	var self = Container.call(this);
	var playerGraphics = self.attachAsset('player', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	self.targetY = 0;
	self.velocity = 0;
	self.gravity = 0.5;
	self.damping = 0.9;
	self.update = function () {
		// Move towards target position with physics
		var distance = self.targetY - self.y;
		self.velocity += distance * 0.08;
		self.velocity *= self.damping;
		self.y += self.velocity;
		// Keep player within game bounds
		if (self.y < 100) {
			self.y = 100;
			self.velocity = 0;
		} else if (self.y > 2732 - 100) {
			self.y = 2732 - 100;
			self.velocity = 0;
		}
	};
	return self;
});
/**** 
* Initialize Game
****/ 
var game = new LK.Game({
	backgroundColor: 0x121E40
});
/**** 
* Game Code
****/ 
// Game constants
var GAME_WIDTH = 2048;
var GAME_HEIGHT = 2732;
var NOTE_NAMES = ["Do", "Re", "Mi", "Fa", "Sol", "La", "Si", "Do"];
var NOTE_FREQUENCIES = [262, 294, 330, 349, 392, 440, 494, 523]; // C4 to C5 frequencies in Hz
var FREQUENCY_TOLERANCE = 15; // Hz tolerance for detecting notes
// Game variables
var player;
var obstacles = [];
var noteIndicators = [];
var currentNote = -1;
var score = 0;
var started = false;
var obstacleTimer;
var difficultyTimer;
var difficulty = 1;
var obstacleSpeed = 5;
var obstacleInterval = 2000;
// Score display
var scoreTxt = new Text2('Score: 0', {
	size: 70,
	fill: 0xFFFFFF
});
scoreTxt.anchor.set(1, 0);
LK.gui.topRight.addChild(scoreTxt);
// Note display
var noteTxt = new Text2('Sing a note!', {
	size: 70,
	fill: 0xFFFFFF
});
noteTxt.anchor.set(0, 0);
LK.gui.topLeft.addChild(noteTxt);
noteTxt.x = 100; // Move away from top left corner
// Instructions display
var instructionsTxt = new Text2('Sing notes (Do, Re, Mi...) to move up and down.\nAvoid obstacles by positioning correctly.', {
	size: 50,
	fill: 0xFFFFFF
});
instructionsTxt.anchor.set(0.5, 0);
instructionsTxt.y = 100;
LK.gui.top.addChild(instructionsTxt);
// Start button
var startBtn = new Container();
var startBtnBg = startBtn.attachAsset('player', {
	anchorX: 0.5,
	anchorY: 0.5,
	scaleX: 5,
	scaleY: 2
});
startBtnBg.tint = 0x00ff00;
var startBtnTxt = new Text2('START', {
	size: 80,
	fill: 0xFFFFFF
});
startBtnTxt.anchor.set(0.5, 0.5);
startBtn.addChild(startBtnTxt);
startBtn.x = GAME_WIDTH / 2;
startBtn.y = GAME_HEIGHT / 2;
game.addChild(startBtn);
// Create note indicators on the side
function createNoteIndicators() {
	for (var i = 0; i < 8; i++) {
		var indicator = new NoteIndicator();
		indicator.x = 50;
		indicator.setNote(i);
		indicator.alpha = 0.5;
		game.addChild(indicator);
		noteIndicators.push(indicator);
	}
}
// Get Y position based on note index (0-7 for Do to high Do)
function getYPositionForNote(noteIndex) {
	// Map note index to Y position (higher notes = higher position)
	// Reserve space at top and bottom
	var usableHeight = GAME_HEIGHT - 400;
	return GAME_HEIGHT - 200 - noteIndex * (usableHeight / 7);
}
// Handle voice input and map to notes
function handleVoiceInput() {
	if (facekit.pitch > 0 && facekit.volume > 0.1) {
		// Find closest note match
		var detectedNote = -1;
		var closestDistance = Infinity;
		for (var i = 0; i < NOTE_FREQUENCIES.length; i++) {
			var distance = Math.abs(facekit.pitch - NOTE_FREQUENCIES[i]);
			if (distance < closestDistance && distance < FREQUENCY_TOLERANCE) {
				closestDistance = distance;
				detectedNote = i;
			}
		}
		if (detectedNote !== -1) {
			player.targetY = getYPositionForNote(detectedNote);
			// Update current note display if changed
			if (currentNote !== detectedNote) {
				currentNote = detectedNote;
				noteTxt.setText(NOTE_NAMES[currentNote]);
				// Update indicators
				for (var j = 0; j < noteIndicators.length; j++) {
					noteIndicators[j].alpha = j === currentNote ? 1.0 : 0.5;
					if (j === currentNote) {
						tween(noteIndicators[j], {
							scaleX: 1.5,
							scaleY: 1.5
						}, {
							duration: 200,
							onFinish: function onFinish() {
								if (currentNote !== -1) {
									tween(noteIndicators[currentNote], {
										scaleX: 1,
										scaleY: 1
									}, {
										duration: 200
									});
								}
							}
						});
					}
				}
			}
		}
	} else {
		// No sound detected
		currentNote = -1;
		noteTxt.setText("Sing a note!");
	}
}
// Create a new obstacle
function createObstacle() {
	var obstacle = new Obstacle();
	obstacle.x = GAME_WIDTH + 200;
	obstacle.y = GAME_HEIGHT / 2;
	obstacle.speed = obstacleSpeed;
	// Place opening at a random note position
	var noteIndex = Math.floor(Math.random() * 8);
	obstacle.setOpeningPosition(getYPositionForNote(noteIndex) - GAME_HEIGHT / 2);
	game.addChild(obstacle);
	obstacles.push(obstacle);
	return obstacle;
}
// Initialize the game
function initGame() {
	// Create player
	player = new Player();
	player.x = 400;
	player.y = GAME_HEIGHT / 2;
	game.addChild(player);
	// Create note indicators
	createNoteIndicators();
	// Start with an initial obstacle
	createObstacle();
	// Start background music
	LK.playMusic('bgmusic');
	// Hide start button
	game.removeChild(startBtn);
	// Hide instructions after a delay
	LK.setTimeout(function () {
		tween(instructionsTxt, {
			alpha: 0
		}, {
			duration: 1000
		});
	}, 5000);
	// Set up obstacle spawning
	obstacleTimer = LK.setInterval(function () {
		createObstacle();
	}, obstacleInterval);
	// Increase difficulty over time
	difficultyTimer = LK.setInterval(function () {
		difficulty += 0.5;
		obstacleSpeed = Math.min(15, 5 + difficulty);
		obstacleInterval = Math.max(800, 2000 - difficulty * 200);
		// Update existing obstacles
		for (var i = 0; i < obstacles.length; i++) {
			if (obstacles[i].active) {
				obstacles[i].speed = obstacleSpeed;
			}
		}
		// Clear and restart obstacle timer with new interval
		LK.clearInterval(obstacleTimer);
		obstacleTimer = LK.setInterval(function () {
			createObstacle();
		}, obstacleInterval);
	}, 10000);
	started = true;
}
// Handle start button click
startBtn.down = function () {
	initGame();
};
// Check for collision between player and obstacles
function checkCollisions() {
	for (var i = 0; i < obstacles.length; i++) {
		var obstacle = obstacles[i];
		// Skip inactive obstacles
		if (!obstacle.active) {
			continue;
		}
		// Check if player passed an obstacle for the first time
		if (!obstacle.passed && player.x > obstacle.x) {
			obstacle.passed = true;
			score += 1;
			scoreTxt.setText("Score: " + score);
			LK.setScore(score);
			LK.getSound('pass').play();
		}
		// Check for collision with the obstacle body (but not the opening)
		if (player.intersects(obstacle) && !player.intersects(obstacle.opening)) {
			// Player hit an obstacle
			LK.getSound('hit').play();
			LK.effects.flashScreen(0xff0000, 500);
			LK.showGameOver();
			return true;
		}
	}
	return false;
}
// Clean up inactive obstacles
function cleanupObstacles() {
	for (var i = obstacles.length - 1; i >= 0; i--) {
		if (!obstacles[i].active) {
			obstacles[i].destroy();
			obstacles.splice(i, 1);
		}
	}
}
// Main game update loop
game.update = function () {
	if (!started) {
		return;
	}
	// Handle voice input
	handleVoiceInput();
	// Update player
	player.update();
	// Update obstacles
	for (var i = 0; i < obstacles.length; i++) {
		obstacles[i].update();
	}
	// Check for collisions
	if (checkCollisions()) {
		// Game over handling
		return;
	}
	// Clean up off-screen obstacles
	cleanupObstacles();
};