/**** 
* Plugins
****/ 
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
	highScore: 0
});
/**** 
* Classes
****/ 
// Note class for falling notes
var Note = Container.expand(function () {
	var self = Container.call(this);
	// Lane index (0-3)
	self.lane = 0;
	self.hit = false;
	self.missed = false;
	self.speed = 0; // pixels per tick
	self.time = 0; // time (in ticks) when note should reach the target
	self.spawned = false;
	// Attach correct note asset based on lane
	self.setLane = function (laneIdx) {
		self.lane = laneIdx;
		var assetId = 'note' + (laneIdx + 1);
		var noteAsset = self.attachAsset(assetId, {
			anchorX: 0.5,
			anchorY: 0.5,
			width: NOTE_WIDTH,
			height: NOTE_HEIGHT
		});
	};
	// Called every tick
	self.update = function () {
		if (!self.spawned) return;
		self.y += self.speed;
	};
	// Called when note is hit
	self.onHit = function () {
		if (self.hit || self.missed) return;
		self.hit = true;
		// Animate note
		tween(self, {
			alpha: 0,
			scaleX: 1.5,
			scaleY: 1.5
		}, {
			duration: 150,
			easing: tween.easeOut,
			onFinish: function onFinish() {
				self.destroy();
			}
		});
	};
	// Called when note is missed
	self.onMiss = function () {
		if (self.hit || self.missed) return;
		self.missed = true;
		LK.getSound('miss').play({
			fade: {
				start: 1,
				end: 0,
				duration: 1000
			}
		});
		tween(self, {
			alpha: 0
		}, {
			duration: 200,
			onFinish: function onFinish() {
				self.destroy();
			}
		});
	};
	return self;
});
// People class for animated people at lane edges
var People = Container.expand(function () {
	var self = Container.call(this);
	self.isJumping = false;
	self.baseY = 0;
	// Attach a body (ellipse, larger, lower color)
	var body = self.attachAsset('centerCircle', {
		anchorX: 0.5,
		anchorY: 1,
		width: 100,
		height: 180,
		y: 0
	});
	// Arms removed: only body remains
	// Save baseY for jump animation
	self.baseY = self.y;
	// Animate jump
	self.jump = function () {
		if (self.isJumping) return;
		self.isJumping = true;
		var jumpHeight = 120;
		var jumpDuration = 180;
		var originalY = self.y;
		// --- Screen shake effect when people jump ---
		if (typeof game !== "undefined" && typeof tween !== "undefined") {
			// Only shake if not already shaking
			if (!game._isShaking) {
				game._isShaking = true;
				var originalGameX = game.x || 0;
				var originalGameY = game.y || 0;
				// Determine if all people are jumping at once for stronger shake
				var allJumping = false;
				if (typeof peopleLeft !== "undefined" && typeof peopleRight !== "undefined") {
					var allLeftJumping = true;
					for (var i = 0; i < peopleLeft.length; i++) {
						if (!peopleLeft[i].isJumping) {
							allLeftJumping = false;
							break;
						}
					}
					var allRightJumping = true;
					for (var i = 0; i < peopleRight.length; i++) {
						if (!peopleRight[i].isJumping) {
							allRightJumping = false;
							break;
						}
					}
					allJumping = allLeftJumping && allRightJumping;
				}
				var shakeAmount = allJumping ? 120 : 40; // much stronger shake if all people are jumping
				var shakeDuration = allJumping ? 260 : 180;
				// Shake out
				tween(game, {
					x: originalGameX + (Math.random() - 0.5) * shakeAmount,
					y: originalGameY + (Math.random() - 0.5) * shakeAmount
				}, {
					duration: shakeDuration,
					onFinish: function onFinish() {
						// Shake back
						tween(game, {
							x: originalGameX,
							y: originalGameY
						}, {
							duration: shakeDuration,
							onFinish: function onFinish() {
								game._isShaking = false;
							}
						});
					}
				});
			}
		}
		tween(self, {
			y: originalY - jumpHeight
		}, {
			duration: jumpDuration,
			easing: tween.easeOut,
			onFinish: function onFinish() {
				tween(self, {
					y: originalY
				}, {
					duration: jumpDuration,
					easing: tween.easeIn,
					onFinish: function onFinish() {
						self.isJumping = false;
					}
				});
			}
		});
	};
	return self;
});
/**** 
* Initialize Game
****/ 
var game = new LK.Game({
	backgroundColor: 0x181818
});
/**** 
* Game Code
****/ 
// Notes are destroyed by tapping them directly before they reach the white target area at the bottom
// 4 note lanes, each with a different color for clarity
// --- UI Elements ---
var NUM_LANES = 4;
var LANE_WIDTH = 200;
var LANE_SPACING = 40;
var NOTE_WIDTH = 340; // Wider notes
var NOTE_HEIGHT = 340; // Taller notes (stretches upward)
var TARGET_HEIGHT = 60; // White target area is now just a visual "danger" zone, reduced height for smaller targets
var LANE_HEIGHT = 2200;
var GAME_WIDTH = 2048;
var GAME_HEIGHT = 2732;
var LANE_TOTAL_WIDTH = NUM_LANES * LANE_WIDTH + (NUM_LANES - 1) * LANE_SPACING;
var LANE_START_X = (GAME_WIDTH - LANE_TOTAL_WIDTH) / 2 + LANE_WIDTH / 2;
var TARGET_Y = GAME_HEIGHT - 420; // Target zone Y position (moved higher)
// --- Game State ---
var notes = []; // All active notes
var noteIndex = 0; // Index of next note to spawn
var songTicks = 0; // Ticks since song start
var score = 0;
var combo = 0;
var maxCombo = 0;
var misses = 0;
var maxMisses = 10;
var songEnded = false;
var songStarted = false;
var lastTick = 0;
// Note speed multiplier for gradual speed up
var noteSpeedMultiplier = 1.0;
// --- UI Elements ---
var scoreTxt = new Text2('0', {
	size: 120,
	fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// High Score UI
// --- Per-mode high score keys ---
function getCurrentModeKey() {
	if (typeof extremeMode !== "undefined" && extremeMode) return "extreme";
	if (typeof hardMode !== "undefined" && hardMode) return "hard";
	return "easy";
}
function getHighScoreKey() {
	return "highScore_" + getCurrentModeKey();
}
function getHighScore() {
	var key = getHighScoreKey();
	return typeof storage[key] !== "undefined" ? storage[key] : 0;
}
function setHighScore(val) {
	var key = getHighScoreKey();
	storage[key] = val;
}
var highScore = getHighScore();
var highScoreTxt = new Text2('High Score: ' + highScore, {
	size: 60,
	fill: 0xFFD700
});
// Move high score to top-right, with some margin from the edge
highScoreTxt.anchor.set(1, 0); // right aligned, top
highScoreTxt.x = LK.gui.width - 40; // 40px margin from right
highScoreTxt.y = 30; // 30px from top
LK.gui.top.addChild(highScoreTxt);
var comboTxt = new Text2('', {
	size: 70,
	fill: 0xFFE066
});
comboTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(comboTxt);
comboTxt.y = 130;
// --- Mode Labels ---
var hardMode = typeof hardMode !== "undefined" ? hardMode : false;
var extremeMode = typeof extremeMode !== "undefined" ? extremeMode : false;
var hardModeLabel = null;
var extremeModeLabel = null;
if (extremeMode) {
	extremeModeLabel = new Text2('EXTREME MODE', {
		size: 80,
		fill: 0xFF00FF,
		font: "'GillSans-Bold',Impact,'Arial Black',Tahoma"
	});
	extremeModeLabel.anchor.set(0.5, 0);
	extremeModeLabel.x = GAME_WIDTH / 2;
	extremeModeLabel.y = 40;
	LK.gui.top.addChild(extremeModeLabel);
} else if (hardMode) {
	hardModeLabel = new Text2('HARD MODE', {
		size: 80,
		fill: 0xFF2222,
		font: "'GillSans-Bold',Impact,'Arial Black',Tahoma"
	});
	hardModeLabel.anchor.set(0.5, 0);
	hardModeLabel.x = GAME_WIDTH / 2;
	hardModeLabel.y = 40;
	LK.gui.top.addChild(hardModeLabel);
}
// Removed red 'misses' text from the top GUI. Misses are now only shown in white below each target.
// --- Add Background ---
var background = LK.getAsset('background', {
	anchorX: 0,
	anchorY: 0,
	x: 0,
	y: 0,
	width: GAME_WIDTH,
	height: GAME_HEIGHT
});
game.addChild(background);
// --- Lanes and Targets ---
var lanes = [];
var targets = [];
var peopleLeft = [];
var peopleRight = [];
var targetMissTexts = []; // Miss counters for each target
for (var i = 0; i < NUM_LANES; i++) {
	// Lane background - made transparent to remove black stripes
	var laneX = LANE_START_X + i * (LANE_WIDTH + LANE_SPACING);
	var lane = LK.getAsset('lane', {
		anchorX: 0.5,
		anchorY: 0,
		x: laneX,
		y: 0,
		width: LANE_WIDTH,
		height: LANE_HEIGHT,
		alpha: 0
	});
	game.addChild(lane);
	lanes.push(lane);
	// Visually improved target: larger, more distinct, with a colored border effect
	var target = LK.getAsset('target', {
		anchorX: 0.5,
		anchorY: 0.5,
		x: laneX,
		y: TARGET_Y + 40,
		//{1R} // Move target further back (higher y = lower on screen)
		width: LANE_WIDTH + 40,
		// Make target slightly wider for better visuals
		height: TARGET_HEIGHT + 40 // Make target slightly taller for better visuals
	});
	game.addChild(target);
	// Add a second, inner target for a "bullseye" effect
	var innerTarget = LK.getAsset('target', {
		anchorX: 0.5,
		anchorY: 0.5,
		x: laneX,
		y: TARGET_Y + 40,
		//{1W} // Move inner target further back as well
		width: LANE_WIDTH - 30,
		height: TARGET_HEIGHT - 20
	});
	game.addChild(innerTarget);
	// (removed: misses counter above target, now shown on the target itself)
	// Add a misses counter directly ON the target (replaces label text with misses count)
	var targetOnText = new Text2('0', {
		size: 54,
		fill: 0x3399FF // Blue (not black)
	});
	targetOnText.anchor.set(0.5, 0.5);
	targetOnText.x = laneX;
	targetOnText.y = TARGET_Y + 40;
	game.addChild(targetOnText);
	// Store this as the main misses counter for this target (for updating)
	targetMissTexts[i] = targetOnText;
	// Store both for later effects
	targets.push({
		outer: target,
		inner: innerTarget
	});
}
// Place people only outside the lanes, never over the road/lane area
// --- Randomized, non-overlapping, not-in-center people placement ---
peopleLeft = [];
peopleRight = [];
var NUM_PEOPLE_PER_EDGE = 16; // Increased people per side for more crowd
var PERSON_RADIUS = 90; // Half of body width, for spacing
var OUTER_X_OFFSET = 120; // How far outside the lane edge to place people
// Allow people to go all the way to the screen edge, not just near the lanes
var FAR_LEFT_X_MIN = 60;
var FAR_LEFT_X_MAX = getLaneX(0) - LANE_WIDTH / 2 - OUTER_X_OFFSET - 10;
var FAR_RIGHT_X_MIN = getLaneX(NUM_LANES - 1) + LANE_WIDTH / 2 + OUTER_X_OFFSET + 10;
var FAR_RIGHT_X_MAX = GAME_WIDTH - 60;
var Y_MIN = 200; // Don't go too high
var Y_MAX = TARGET_Y - 60; // Don't go too low
var CENTER_EXCLUSION_X = GAME_WIDTH / 2 - 320; // Exclude a wide center band
var CENTER_EXCLUSION_WIDTH = 640; // Center band width to avoid
var MAX_ATTEMPTS = 40; // Max tries to find a non-overlapping spot
function isFarEnough(x, y, arr) {
	for (var i = 0; i < arr.length; i++) {
		var dx = x - arr[i].x;
		var dy = y - arr[i].y;
		if (Math.sqrt(dx * dx + dy * dy) < PERSON_RADIUS * 2.1) return false;
	}
	return true;
}
function isNotCenter(x) {
	return !(x > CENTER_EXCLUSION_X && x < CENTER_EXCLUSION_X + CENTER_EXCLUSION_WIDTH);
}
// Left edge: randomize y, keep x always outside leftmost lane, avoid center, avoid overlap
for (var i = 0; i < NUM_PEOPLE_PER_EDGE; i++) {
	var leftPerson = new People();
	var attempts = 0;
	var px, py;
	do {
		py = Y_MIN + Math.random() * (Y_MAX - Y_MIN);
		// Randomize x across the full left empty area
		px = FAR_LEFT_X_MIN + Math.random() * (FAR_LEFT_X_MAX - FAR_LEFT_X_MIN);
		attempts++;
	} while ((!isFarEnough(px, py, peopleLeft) || !isNotCenter(px)) && attempts < MAX_ATTEMPTS);
	leftPerson.x = px;
	leftPerson.y = py;
	game.addChild(leftPerson);
	peopleLeft.push(leftPerson);
}
// Right edge: randomize y, keep x always outside rightmost lane, avoid center, avoid overlap
for (var i = 0; i < NUM_PEOPLE_PER_EDGE; i++) {
	var rightPerson = new People();
	var attempts = 0;
	var px, py;
	do {
		py = Y_MIN + Math.random() * (Y_MAX - Y_MIN);
		// Randomize x across the full right empty area
		px = FAR_RIGHT_X_MIN + Math.random() * (FAR_RIGHT_X_MAX - FAR_RIGHT_X_MIN);
		attempts++;
	} while ((!isFarEnough(px, py, peopleRight) || !isNotCenter(px)) && attempts < MAX_ATTEMPTS);
	rightPerson.x = px;
	rightPerson.y = py;
	game.addChild(rightPerson);
	peopleRight.push(rightPerson);
}
// --- Song Data (Random Infinite Notes) ---
// Each note: {lane: 0-3, time: tick when note should reach target}
// We'll generate notes on the fly, at random lanes and random intervals
var bpm = 60;
var ticksPerBeat = 60 * 60 / bpm; // 60fps
var NOTE_TRAVEL_TICKS = 180; // 3 seconds at 60fps
// Infinite random note generator state
var nextNoteTick = 60; // When the next note should appear (in songTicks)
function getRandomLane() {
	return Math.floor(Math.random() * NUM_LANES);
}
function getRandomInterval() {
	// In extreme mode, spawn notes extremely frequently (interval is 0.12x to 0.35x of ticksPerBeat)
	if (typeof extremeMode !== "undefined" && extremeMode) {
		return Math.floor(ticksPerBeat * (0.12 + Math.random() * 0.23));
	}
	// In hard mode, spawn notes more frequently (interval is 0.3x to 1.0x of ticksPerBeat)
	if (typeof hardMode !== "undefined" && hardMode) {
		return Math.floor(ticksPerBeat * (0.3 + Math.random() * 0.7));
	}
	// Random interval between notes: 0.5x to 1.5x of ticksPerBeat
	return Math.floor(ticksPerBeat * (0.5 + Math.random()));
}
// --- Helper Functions ---
function getLaneX(laneIdx) {
	return LANE_START_X + laneIdx * (LANE_WIDTH + LANE_SPACING);
}
// --- Game Logic ---
// Start song/music
function startSong() {
	if (songStarted) return;
	songStarted = true;
	LK.playMusic('song1');
	songTicks = 0;
	noteIndex = 0;
	score = 0;
	combo = 0;
	maxCombo = 0;
	misses = 0;
	songEnded = false;
	scoreTxt.setText('0');
	comboTxt.setText('');
	highScore = getHighScore();
	highScoreTxt.setText('High Score: ' + highScore);
	notes.length = 0;
	// Reset per-target misses counters (on the target itself)
	if (typeof targets !== "undefined" && typeof targetMissTexts !== "undefined") {
		for (var i = 0; i < targetMissTexts.length; i++) {
			if (targetMissTexts[i]) {
				targetMissTexts[i].setText('0');
				targetMissTexts[i].setStyle({
					fill: 0x3399FF
				});
			}
			if (targets[i]) targets[i].missCount = 0;
		}
	}
	// Reset nextNoteTick and noteSpeedMultiplier to ensure smooth start
	nextNoteTick = 60;
	if (typeof extremeMode !== "undefined" && extremeMode) {
		noteSpeedMultiplier = 1.5; // Slower than before for extreme mode (spawn rate unchanged)
		// Remove any previous mode labels
		if (typeof hardModeLabel !== "undefined" && hardModeLabel && hardModeLabel.parent) {
			hardModeLabel.destroy();
		}
		if (typeof extremeModeLabel !== "undefined" && extremeModeLabel && extremeModeLabel.parent) {
			extremeModeLabel.destroy();
		}
		extremeModeLabel = new Text2('EXTREME MODE', {
			size: 80,
			fill: 0xFF00FF,
			font: "'GillSans-Bold',Impact,'Arial Black',Tahoma"
		});
		extremeModeLabel.anchor.set(0.5, 0);
		extremeModeLabel.x = GAME_WIDTH / 2;
		extremeModeLabel.y = 40;
		LK.gui.top.addChild(extremeModeLabel);
	} else if (typeof hardMode !== "undefined" && hardMode) {
		noteSpeedMultiplier = 2.0; // Much faster for hard mode
		// Show Hard Mode label in gameplay
		if (typeof hardModeLabel !== "undefined" && hardModeLabel && hardModeLabel.parent) {
			hardModeLabel.destroy();
		}
		if (typeof extremeModeLabel !== "undefined" && extremeModeLabel && extremeModeLabel.parent) {
			extremeModeLabel.destroy();
		}
		hardModeLabel = new Text2('HARD MODE', {
			size: 80,
			fill: 0xFF2222,
			font: "'GillSans-Bold',Impact,'Arial Black',Tahoma"
		});
		hardModeLabel.anchor.set(0.5, 0);
		hardModeLabel.x = GAME_WIDTH / 2;
		hardModeLabel.y = 40;
		LK.gui.top.addChild(hardModeLabel);
	} else if (typeof easyMode !== "undefined" && easyMode) {
		noteSpeedMultiplier = 1.0;
		// Show Easy Mode label in gameplay
		if (typeof hardModeLabel !== "undefined" && hardModeLabel && hardModeLabel.parent) {
			hardModeLabel.destroy();
		}
		if (typeof extremeModeLabel !== "undefined" && extremeModeLabel && extremeModeLabel.parent) {
			extremeModeLabel.destroy();
		}
		if (typeof easyModeLabel !== "undefined" && easyModeLabel && easyModeLabel.parent) {
			easyModeLabel.destroy();
		}
		easyModeLabel = new Text2('EASY MODE', {
			size: 80,
			fill: 0x44FF44,
			font: "'GillSans-Bold',Impact,'Arial Black',Tahoma"
		});
		easyModeLabel.anchor.set(0.5, 0);
		easyModeLabel.x = GAME_WIDTH / 2;
		easyModeLabel.y = 40;
		LK.gui.top.addChild(easyModeLabel);
	} else {
		noteSpeedMultiplier = 1.0;
		if (typeof hardModeLabel !== "undefined" && hardModeLabel && hardModeLabel.parent) {
			hardModeLabel.destroy();
		}
		if (typeof extremeModeLabel !== "undefined" && extremeModeLabel && extremeModeLabel.parent) {
			extremeModeLabel.destroy();
		}
	}
}
// End song/game
function endSong(win) {
	if (songEnded) return;
	songEnded = true;
	LK.stopMusic();
	// No win or game over, just stop music and mark as ended
}
// --- Input Handling ---
game.down = function (x, y, obj) {
	if (menuActive) return; // Prevent gameplay input until menu is dismissed
	// Only allow input if song is running
	if (!songStarted || songEnded) return;
	// Pause people movement for 2 seconds (120 ticks) after a note is hit
	if (typeof game._peopleMovePausedUntil === "undefined") game._peopleMovePausedUntil = 0;
	game._peopleMovePausedUntil = songTicks + 120;
	// Check if tap is on any note (from topmost to bottom)
	var hit = false;
	var _loop = function _loop() {
			note = notes[i];
			if (note.hit || note.missed) return 0; // continue
			// Get note bounds
			noteLeft = note.x - NOTE_WIDTH / 2;
			noteRight = note.x + NOTE_WIDTH / 2;
			noteTop = note.y - NOTE_HEIGHT / 2;
			noteBottom = note.y + NOTE_HEIGHT / 2;
			if (x >= noteLeft && x <= noteRight && y >= noteTop && y <= noteBottom) {
				// Hit!
				note.onHit();
				hit = true;
				score += 100;
				combo += 1;
				if (combo > maxCombo) maxCombo = combo;
				scoreTxt.setText(score + '');
				comboTxt.setText(combo > 1 ? combo + ' Combo!' : '');
				// High score logic
				if (score > highScore) {
					highScore = score;
					setHighScore(highScore);
					highScoreTxt.setText('High Score: ' + highScore);
				}
				LK.getSound('tap').play();
				// Make a random subset of people jump when any note is hit!
				// Every 10 combo, make all people jump and apply a stronger shake
				if (typeof peopleLeft !== "undefined" && typeof peopleRight !== "undefined") {
					if (combo > 0 && combo % 50 === 0) {
						// --- MASSIVE SHAKE for every 50 combo ---
						// All people jump!
						for (var iAll = 0; iAll < peopleLeft.length; iAll++) {
							if (peopleLeft[iAll]) peopleLeft[iAll].jump();
						}
						for (var iAll = 0; iAll < peopleRight.length; iAll++) {
							if (peopleRight[iAll]) peopleRight[iAll].jump();
						}
						// Massive shake: override game.x/y with a huge shake
						if (typeof game !== "undefined" && typeof tween !== "undefined") {
							if (!game._isShaking) {
								game._isShaking = true;
								var originalGameX = game.x || 0;
								var originalGameY = game.y || 0;
								var shakeAmount = 420; // extremely strong shake
								var shakeDuration = 420;
								tween(game, {
									x: originalGameX + (Math.random() - 0.5) * shakeAmount,
									y: originalGameY + (Math.random() - 0.5) * shakeAmount
								}, {
									duration: shakeDuration,
									onFinish: function onFinish() {
										tween(game, {
											x: originalGameX,
											y: originalGameY
										}, {
											duration: shakeDuration,
											onFinish: function onFinish() {
												game._isShaking = false;
											}
										});
									}
								});
							}
						}
					} else if (combo > 0 && combo % 10 === 0) {
						// All people jump!
						for (var iAll = 0; iAll < peopleLeft.length; iAll++) {
							if (peopleLeft[iAll]) peopleLeft[iAll].jump();
						}
						for (var iAll = 0; iAll < peopleRight.length; iAll++) {
							if (peopleRight[iAll]) peopleRight[iAll].jump();
						}
						// Stronger shake: override game.x/y with a bigger shake
						if (typeof game !== "undefined" && typeof tween !== "undefined") {
							if (!game._isShaking) {
								game._isShaking = true;
								var originalGameX = game.x || 0;
								var originalGameY = game.y || 0;
								var shakeAmount = 60; // much stronger shake
								var shakeDuration = 120;
								tween(game, {
									x: originalGameX + (Math.random() - 0.5) * shakeAmount,
									y: originalGameY + (Math.random() - 0.5) * shakeAmount
								}, {
									duration: shakeDuration,
									onFinish: function onFinish() {
										tween(game, {
											x: originalGameX,
											y: originalGameY
										}, {
											duration: shakeDuration,
											onFinish: function onFinish() {
												game._isShaking = false;
											}
										});
									}
								});
							}
						}
					} else {
						// Helper to get unique random indices
						var getRandomIndices = function getRandomIndices(arrLen, count) {
							var indices = [];
							var used = [];
							while (indices.length < count && indices.length < arrLen) {
								var idx = Math.floor(Math.random() * arrLen);
								if (!used[idx]) {
									indices.push(idx);
									used[idx] = true;
								}
							}
							return indices;
						};
						// How many people to jump per side? 1-3 random per side
						leftJumpCount = 1 + Math.floor(Math.random() * 3);
						rightJumpCount = 1 + Math.floor(Math.random() * 3);
						leftIndices = getRandomIndices(peopleLeft.length, leftJumpCount);
						rightIndices = getRandomIndices(peopleRight.length, rightJumpCount);
						for (j = 0; j < leftIndices.length; j++) {
							idx = leftIndices[j];
							if (peopleLeft[idx]) peopleLeft[idx].jump();
						}
						for (j = 0; j < rightIndices.length; j++) {
							idx = rightIndices[j];
							if (peopleRight[idx]) peopleRight[idx].jump();
						}
					}
				}
				// Show floating feedback text based on combo
				feedbackText = '';
				if (combo >= 30) {
					feedbackText = 'PERFECT!';
				} else if (combo >= 15) {
					feedbackText = 'GREAT!';
				} else if (combo >= 5) {
					feedbackText = 'GOOD!';
				}
				if (feedbackText) {
					fbTxt = new Text2(feedbackText, {
						size: 120,
						fill: 0xFFD700,
						font: "'GillSans-Bold',Impact,'Arial Black',Tahoma"
					});
					fbTxt.anchor.set(0.5, 0.5);
					fbTxt.x = GAME_WIDTH / 2;
					fbTxt.y = GAME_HEIGHT / 2 - 200;
					fbTxt.alpha = 1;
					game.addChild(fbTxt);
					tween(fbTxt, {
						y: fbTxt.y - 120,
						alpha: 0
					}, {
						duration: 700,
						easing: tween.easeOut,
						onFinish: function onFinish() {
							fbTxt.destroy();
						}
					});
				}
				return 1; // break
			}
		},
		note,
		noteLeft,
		noteRight,
		noteTop,
		noteBottom,
		leftJumpCount,
		rightJumpCount,
		leftIndices,
		rightIndices,
		j,
		idx,
		j,
		idx,
		feedbackText,
		fbTxt,
		_ret;
	for (var i = notes.length - 1; i >= 0; i--) {
		_ret = _loop();
		if (_ret === 0) continue;
		if (_ret === 1) break;
	}
	if (!hit) {
		// Missed tap (no note hit)
		combo = 0;
		comboTxt.setText('');
		// No misses for tap misses, no flash
	}
};
// --- Main Game Loop ---
game.update = function () {
	if (!songStarted || songEnded) return;
	songTicks += 1;
	// Gradually increase noteSpeedMultiplier (very slow ramp, e.g. +0.0002 per tick)
	// In normal mode, do NOT increase speed faster after 5000 points
	if (noteSpeedMultiplier < 2.0) {
		noteSpeedMultiplier += 0.0002;
		if (noteSpeedMultiplier > 2.0) noteSpeedMultiplier = 2.0;
	}
	// Spawn notes as needed (random, infinite)
	// After 5000 points, do not increase note spawn count; only speed increases
	var notesToSpawn = 1;
	// No change to notesToSpawn after 5000 points
	while (songTicks >= nextNoteTick - NOTE_TRAVEL_TICKS) {
		for (var spawnIdx = 0; spawnIdx < notesToSpawn; spawnIdx++) {
			var lane = getRandomLane();
			var noteTime = nextNoteTick;
			var note = new Note();
			note.setLane(lane);
			note.x = getLaneX(lane);
			note.y = -NOTE_HEIGHT / 2;
			note.speed = (TARGET_Y + NOTE_HEIGHT / 2) / NOTE_TRAVEL_TICKS * noteSpeedMultiplier;
			note.time = noteTime;
			note.spawned = true;
			notes.push(note);
			game.addChild(note);
		}
		// Schedule next note
		nextNoteTick += getRandomInterval();
	}
	// Update notes
	for (var i = notes.length - 1; i >= 0; i--) {
		var note = notes[i];
		note.update();
		// If note reached target zone and not hit, mark as missed
		if (!note.hit && !note.missed && note.y >= TARGET_Y + TARGET_HEIGHT / 2) {
			note.onMiss();
			combo = 0;
			misses += 1;
			comboTxt.setText('');
			// Increment and show per-target misses in white above each target
			if (!targets[note.lane].missCount) targets[note.lane].missCount = 0;
			targets[note.lane].missCount += 1;
			if (targetMissTexts && targetMissTexts[note.lane]) {
				targetMissTexts[note.lane].setText(targets[note.lane].missCount + '');
				targetMissTexts[note.lane].setStyle({
					fill: 0x3399FF
				});
			}
			LK.getSound('miss').play({
				fade: {
					start: 1,
					end: 0,
					duration: 1000
				}
			});
			LK.effects.flashObject(targets[note.lane].outer, 0xff0000, 200);
			LK.effects.flashObject(targets[note.lane].inner, 0xff6666, 200);
			if (misses >= maxMisses) {
				// No game over, just keep going
			}
			// Check if any target's miss count exceeds 10, trigger game over
			for (var t = 0; t < targets.length; t++) {
				if (targets[t].missCount && targets[t].missCount > 10) {
					LK.showGameOver();
					return;
				}
			}
		}
		// Remove destroyed notes
		if (note.destroyed) {
			notes.splice(i, 1);
		}
	}
	// --- Move people to new random positions every 2 seconds ---
	if (typeof game._lastPeopleMoveTick === "undefined") game._lastPeopleMoveTick = 0;
	if (typeof game._peopleMovePausedUntil === "undefined") game._peopleMovePausedUntil = 0;
	// Only move people if not in the 2s pause after a note hit
	if (songTicks - game._lastPeopleMoveTick > 120 && songTicks >= game._peopleMovePausedUntil) {
		// Helper to move a person to a new random position (non-overlapping, not center)
		var movePerson = function movePerson(person, arr, isLeft) {
			var attempts = 0;
			var px, py;
			do {
				py = Y_MIN + Math.random() * (Y_MAX - Y_MIN);
				if (isLeft) {
					// Move anywhere in the left empty area
					px = FAR_LEFT_X_MIN + Math.random() * (FAR_LEFT_X_MAX - FAR_LEFT_X_MIN);
				} else {
					// Move anywhere in the right empty area
					px = FAR_RIGHT_X_MIN + Math.random() * (FAR_RIGHT_X_MAX - FAR_RIGHT_X_MIN);
				}
				attempts++;
			} while ((!isFarEnough(px, py, arr) || !isNotCenter(px)) && attempts < MAX_ATTEMPTS);
			// Animate to new position (move very slowly, never teleport)
			tween(person, {
				x: px,
				y: py
			}, {
				duration: 3200,
				// much slower movement (3.2 seconds)
				easing: tween.easeInOut
			});
			person.baseY = py;
		}; // Move left people
		// every 2 seconds at 60fps
		game._lastPeopleMoveTick = songTicks;
		for (var i = 0; i < peopleLeft.length; i++) {
			movePerson(peopleLeft[i], peopleLeft, true);
		}
		// Move right people
		for (var i = 0; i < peopleRight.length; i++) {
			movePerson(peopleRight[i], peopleRight, false);
		}
	}
	// No win condition, keep game running
};
// --- Start Menu Overlay ---
var startMenuOverlay = new Container();
var overlayBg = LK.getAsset('lane', {
	anchorX: 0.5,
	anchorY: 0.5,
	x: GAME_WIDTH / 2,
	y: GAME_HEIGHT / 2,
	width: GAME_WIDTH,
	height: GAME_HEIGHT,
	color: 0x000000
});
overlayBg.alpha = 0.85;
startMenuOverlay.addChild(overlayBg);
var titleText = new Text2('BEAT TAPPER', {
	size: 120,
	fill: 0xFFD700,
	font: "'GillSans-Bold',Impact,'Arial Black',Tahoma"
});
titleText.anchor.set(0.5, 0.5);
titleText.x = GAME_WIDTH / 2;
titleText.y = GAME_HEIGHT / 2 - 400;
startMenuOverlay.addChild(titleText);
var tapToStartText = new Text2('Tap to Start', {
	size: 90,
	fill: 0xFFFFFF
});
tapToStartText.anchor.set(0.5, 0.5);
tapToStartText.x = GAME_WIDTH / 2;
// Move even higher above instructions (was -60, now -120 for more space above instructions)
tapToStartText.y = GAME_HEIGHT / 2 - 120;
// Create a box behind the text
var tapBoxPaddingX = 60;
var tapBoxPaddingY = 30;
var tapBox = LK.getAsset('lane', {
	anchorX: 0.5,
	anchorY: 0.5,
	x: tapToStartText.x,
	y: tapToStartText.y,
	width: tapToStartText.width + tapBoxPaddingX,
	height: tapToStartText.height + tapBoxPaddingY,
	color: 0xFFFFFF
});
tapBox.alpha = 0.18;
startMenuOverlay.addChild(tapBox);
startMenuOverlay.addChild(tapToStartText);
var instructionsText = new Text2("How to Play:\n\n" + "• Tap the falling notes when they reach the targets.\n" + "• Don't let too many notes pass the targets!\n" + "• Each lane shows your misses. If any lane gets more than 10 misses, it's game over.\n" + "• Try to get the highest combo and score!", {
	size: 44,
	fill: 0xFFFFFF,
	align: "center"
});
instructionsText.anchor.set(0.5, 0.5);
instructionsText.x = GAME_WIDTH / 2;
instructionsText.y = GAME_HEIGHT / 2 + 120;
startMenuOverlay.addChild(instructionsText);
var highScoreMenuText = new Text2('High Score: ' + getHighScore(), {
	size: 70,
	fill: 0xFFD700,
	align: "center"
});
highScoreMenuText.anchor.set(0.5, 0.5);
highScoreMenuText.x = GAME_WIDTH / 2;
highScoreMenuText.y = GAME_HEIGHT / 2 + 320;
startMenuOverlay.addChild(highScoreMenuText);
// --- Easy Mode Button ---
var easyModeBtn = new Text2('Easy Mode', {
	size: 80,
	fill: 0x44FF44,
	font: "'GillSans-Bold',Impact,'Arial Black',Tahoma"
});
easyModeBtn.anchor.set(0.5, 0.5);
easyModeBtn.x = GAME_WIDTH / 2;
easyModeBtn.y = GAME_HEIGHT / 2 + 480;
easyModeBtn.alpha = 0.92;
easyModeBtn._isEasyModeBtn = true;
startMenuOverlay.addChild(easyModeBtn);
// --- Hard Mode Button ---
var hardModeBtn = new Text2('Hard Mode', {
	size: 80,
	fill: 0xFF2222,
	font: "'GillSans-Bold',Impact,'Arial Black',Tahoma"
});
hardModeBtn.anchor.set(0.5, 0.5);
hardModeBtn.x = GAME_WIDTH / 2;
hardModeBtn.y = GAME_HEIGHT / 2 + 620;
hardModeBtn.alpha = 0.92;
hardModeBtn._isHardModeBtn = true;
startMenuOverlay.addChild(hardModeBtn);
// --- Extreme Mode Button ---
var extremeModeBtn = new Text2('Extreme Mode', {
	size: 80,
	fill: 0xFF00FF,
	font: "'GillSans-Bold',Impact,'Arial Black',Tahoma"
});
extremeModeBtn.anchor.set(0.5, 0.5);
extremeModeBtn.x = GAME_WIDTH / 2;
extremeModeBtn.y = GAME_HEIGHT / 2 + 760;
extremeModeBtn.alpha = 0.92;
extremeModeBtn._isExtremeModeBtn = true;
startMenuOverlay.addChild(extremeModeBtn);
game.addChild(startMenuOverlay);
var menuActive = true;
var hardMode = false;
var extremeMode = false;
var easyMode = true; // Default to easy mode
game.down = function (x, y, obj) {
	if (menuActive) {
		// Check if tap is on Easy Mode button
		var easyBtnLeft = easyModeBtn.x - easyModeBtn.width / 2;
		var easyBtnRight = easyModeBtn.x + easyModeBtn.width / 2;
		var easyBtnTop = easyModeBtn.y - easyModeBtn.height / 2;
		var easyBtnBottom = easyModeBtn.y + easyModeBtn.height / 2;
		if (x >= easyBtnLeft && x <= easyBtnRight && y >= easyBtnTop && y <= easyBtnBottom) {
			// Toggle easy mode ON and visually indicate
			easyMode = true;
			hardMode = false;
			extremeMode = false;
			easyModeBtn.setText('Easy Mode: ON');
			easyModeBtn.fill = 0x66FF66;
			easyModeBtn.alpha = 1;
			hardModeBtn.setText('Hard Mode');
			hardModeBtn.fill = 0xFF2222;
			hardModeBtn.alpha = 0.92;
			extremeModeBtn.setText('Extreme Mode');
			extremeModeBtn.fill = 0xFF00FF;
			extremeModeBtn.alpha = 0.92;
			// Update high score in menu overlay
			if (typeof highScoreMenuText !== "undefined") {
				highScoreMenuText.setText('High Score: ' + getHighScore());
			}
			// Optionally, flash or animate
			tween(easyModeBtn, {
				scaleX: 1.2,
				scaleY: 1.2
			}, {
				duration: 120,
				yoyo: true,
				repeat: 1,
				onFinish: function onFinish() {
					easyModeBtn.scaleX = 1;
					easyModeBtn.scaleY = 1;
				}
			});
			return;
		}
		// Check if tap is on Hard Mode button
		var btnLeft = hardModeBtn.x - hardModeBtn.width / 2;
		var btnRight = hardModeBtn.x + hardModeBtn.width / 2;
		var btnTop = hardModeBtn.y - hardModeBtn.height / 2;
		var btnBottom = hardModeBtn.y + hardModeBtn.height / 2;
		if (x >= btnLeft && x <= btnRight && y >= btnTop && y <= btnBottom) {
			// Toggle hard mode ON and visually indicate
			hardMode = true;
			easyMode = false;
			extremeMode = false;
			hardModeBtn.setText('Hard Mode: ON');
			hardModeBtn.fill = 0xFF4444;
			hardModeBtn.alpha = 1;
			easyModeBtn.setText('Easy Mode');
			easyModeBtn.fill = 0x44FF44;
			easyModeBtn.alpha = 0.92;
			extremeModeBtn.setText('Extreme Mode');
			extremeModeBtn.fill = 0xFF00FF;
			extremeModeBtn.alpha = 0.92;
			// Update high score in menu overlay
			if (typeof highScoreMenuText !== "undefined") {
				highScoreMenuText.setText('High Score: ' + getHighScore());
			}
			// Optionally, flash or animate
			tween(hardModeBtn, {
				scaleX: 1.2,
				scaleY: 1.2
			}, {
				duration: 120,
				yoyo: true,
				repeat: 1,
				onFinish: function onFinish() {
					hardModeBtn.scaleX = 1;
					hardModeBtn.scaleY = 1;
				}
			});
			return;
		}
		// Check if tap is on Extreme Mode button
		var ebtnLeft = extremeModeBtn.x - extremeModeBtn.width / 2;
		var ebtnRight = extremeModeBtn.x + extremeModeBtn.width / 2;
		var ebtnTop = extremeModeBtn.y - extremeModeBtn.height / 2;
		var ebtnBottom = extremeModeBtn.y + extremeModeBtn.height / 2;
		if (x >= ebtnLeft && x <= ebtnRight && y >= ebtnTop && y <= ebtnBottom) {
			// Toggle extreme mode ON and visually indicate
			extremeMode = true;
			hardMode = false;
			easyMode = false;
			extremeModeBtn.setText('Extreme Mode: ON');
			extremeModeBtn.fill = 0xFF66FF;
			extremeModeBtn.alpha = 1;
			hardModeBtn.setText('Hard Mode');
			hardModeBtn.fill = 0xFF2222;
			hardModeBtn.alpha = 0.92;
			easyModeBtn.setText('Easy Mode');
			easyModeBtn.fill = 0x44FF44;
			easyModeBtn.alpha = 0.92;
			// Update high score in menu overlay
			if (typeof highScoreMenuText !== "undefined") {
				highScoreMenuText.setText('High Score: ' + getHighScore());
			}
			// Optionally, flash or animate
			tween(extremeModeBtn, {
				scaleX: 1.2,
				scaleY: 1.2
			}, {
				duration: 120,
				yoyo: true,
				repeat: 1,
				onFinish: function onFinish() {
					extremeModeBtn.scaleX = 1;
					extremeModeBtn.scaleY = 1;
				}
			});
			return;
		}
		menuActive = false;
		startMenuOverlay.destroy();
		startSong();
		// Restore original input handler for gameplay
		game.down = function (x, y, obj) {
			// Only allow input if song is running
			if (!songStarted || songEnded) return;
			// Pause people movement for 2 seconds (120 ticks) after a note is hit
			if (typeof game._peopleMovePausedUntil === "undefined") game._peopleMovePausedUntil = 0;
			game._peopleMovePausedUntil = songTicks + 120;
			// Check if tap is on any note (from topmost to bottom)
			var hit = false;
			var _loop = function _loop() {
					note = notes[i];
					if (note.hit || note.missed) return 0; // continue
					// Get note bounds
					noteLeft = note.x - NOTE_WIDTH / 2;
					noteRight = note.x + NOTE_WIDTH / 2;
					noteTop = note.y - NOTE_HEIGHT / 2;
					noteBottom = note.y + NOTE_HEIGHT / 2;
					if (x >= noteLeft && x <= noteRight && y >= noteTop && y <= noteBottom) {
						// Hit!
						note.onHit();
						hit = true;
						score += 100;
						combo += 1;
						if (combo > maxCombo) maxCombo = combo;
						scoreTxt.setText(score + '');
						comboTxt.setText(combo > 1 ? combo + ' Combo!' : '');
						// High score logic
						if (score > highScore) {
							highScore = score;
							setHighScore(highScore);
							highScoreTxt.setText('High Score: ' + highScore);
						}
						LK.getSound('tap').play();
						// Make a random subset of people jump when any note is hit!
						// Every 10 combo, make all people jump and apply a stronger shake
						if (typeof peopleLeft !== "undefined" && typeof peopleRight !== "undefined") {
							if (combo > 0 && combo % 50 === 0) {
								// --- MASSIVE SHAKE for every 50 combo ---
								// All people jump!
								for (var iAll = 0; iAll < peopleLeft.length; iAll++) {
									if (peopleLeft[iAll]) peopleLeft[iAll].jump();
								}
								for (var iAll = 0; iAll < peopleRight.length; iAll++) {
									if (peopleRight[iAll]) peopleRight[iAll].jump();
								}
								// Massive shake: override game.x/y with a huge shake
								if (typeof game !== "undefined" && typeof tween !== "undefined") {
									if (!game._isShaking) {
										game._isShaking = true;
										var originalGameX = game.x || 0;
										var originalGameY = game.y || 0;
										var shakeAmount = 420; // extremely strong shake
										var shakeDuration = 420;
										tween(game, {
											x: originalGameX + (Math.random() - 0.5) * shakeAmount,
											y: originalGameY + (Math.random() - 0.5) * shakeAmount
										}, {
											duration: shakeDuration,
											onFinish: function onFinish() {
												tween(game, {
													x: originalGameX,
													y: originalGameY
												}, {
													duration: shakeDuration,
													onFinish: function onFinish() {
														game._isShaking = false;
													}
												});
											}
										});
									}
								}
							} else if (combo > 0 && combo % 10 === 0) {
								// All people jump!
								for (var iAll = 0; iAll < peopleLeft.length; iAll++) {
									if (peopleLeft[iAll]) peopleLeft[iAll].jump();
								}
								for (var iAll = 0; iAll < peopleRight.length; iAll++) {
									if (peopleRight[iAll]) peopleRight[iAll].jump();
								}
								// Stronger shake: override game.x/y with a bigger shake
								if (typeof game !== "undefined" && typeof tween !== "undefined") {
									if (!game._isShaking) {
										game._isShaking = true;
										var originalGameX = game.x || 0;
										var originalGameY = game.y || 0;
										var shakeAmount = 60; // much stronger shake
										var shakeDuration = 120;
										tween(game, {
											x: originalGameX + (Math.random() - 0.5) * shakeAmount,
											y: originalGameY + (Math.random() - 0.5) * shakeAmount
										}, {
											duration: shakeDuration,
											onFinish: function onFinish() {
												tween(game, {
													x: originalGameX,
													y: originalGameY
												}, {
													duration: shakeDuration,
													onFinish: function onFinish() {
														game._isShaking = false;
													}
												});
											}
										});
									}
								}
							} else {
								// Helper to get unique random indices
								var getRandomIndices = function getRandomIndices(arrLen, count) {
									var indices = [];
									var used = [];
									while (indices.length < count && indices.length < arrLen) {
										var idx = Math.floor(Math.random() * arrLen);
										if (!used[idx]) {
											indices.push(idx);
											used[idx] = true;
										}
									}
									return indices;
								};
								// How many people to jump per side? 1-3 random per side
								leftJumpCount = 1 + Math.floor(Math.random() * 3);
								rightJumpCount = 1 + Math.floor(Math.random() * 3);
								leftIndices = getRandomIndices(peopleLeft.length, leftJumpCount);
								rightIndices = getRandomIndices(peopleRight.length, rightJumpCount);
								for (j = 0; j < leftIndices.length; j++) {
									idx = leftIndices[j];
									if (peopleLeft[idx]) peopleLeft[idx].jump();
								}
								for (j = 0; j < rightIndices.length; j++) {
									idx = rightIndices[j];
									if (peopleRight[idx]) peopleRight[idx].jump();
								}
							}
						}
						// Show floating feedback text based on combo
						feedbackText = '';
						if (combo >= 30) {
							feedbackText = 'PERFECT!';
						} else if (combo >= 15) {
							feedbackText = 'GREAT!';
						} else if (combo >= 5) {
							feedbackText = 'GOOD!';
						}
						if (feedbackText) {
							fbTxt = new Text2(feedbackText, {
								size: 120,
								fill: 0xFFD700,
								font: "'GillSans-Bold',Impact,'Arial Black',Tahoma"
							});
							fbTxt.anchor.set(0.5, 0.5);
							fbTxt.x = GAME_WIDTH / 2;
							fbTxt.y = GAME_HEIGHT / 2 - 200;
							fbTxt.alpha = 1;
							game.addChild(fbTxt);
							tween(fbTxt, {
								y: fbTxt.y - 120,
								alpha: 0
							}, {
								duration: 700,
								easing: tween.easeOut,
								onFinish: function onFinish() {
									fbTxt.destroy();
								}
							});
						}
						return 1; // break
					}
				},
				note,
				noteLeft,
				noteRight,
				noteTop,
				noteBottom,
				leftJumpCount,
				rightJumpCount,
				leftIndices,
				rightIndices,
				j,
				idx,
				j,
				idx,
				feedbackText,
				fbTxt,
				_ret;
			for (var i = notes.length - 1; i >= 0; i--) {
				_ret = _loop();
				if (_ret === 0) continue;
				if (_ret === 1) break;
			}
			if (!hit) {
				// Missed tap (no note hit)
				combo = 0;
				comboTxt.setText('');
				// No misses for tap misses, no flash
			}
		};
	}
};
// Do not start the song immediately; wait for user tap
// startSong();; ===================================================================
--- original.js
+++ change.js
@@ -176,11 +176,11 @@
 
 /**** 
 * Game Code
 ****/ 
-// --- UI Elements ---
-// 4 note lanes, each with a different color for clarity
 // Notes are destroyed by tapping them directly before they reach the white target area at the bottom
+// 4 note lanes, each with a different color for clarity
+// --- UI Elements ---
 var NUM_LANES = 4;
 var LANE_WIDTH = 200;
 var LANE_SPACING = 40;
 var NOTE_WIDTH = 340; // Wider notes
@@ -291,17 +291,18 @@
 var peopleLeft = [];
 var peopleRight = [];
 var targetMissTexts = []; // Miss counters for each target
 for (var i = 0; i < NUM_LANES; i++) {
-	// Lane background
+	// Lane background - made transparent to remove black stripes
 	var laneX = LANE_START_X + i * (LANE_WIDTH + LANE_SPACING);
 	var lane = LK.getAsset('lane', {
 		anchorX: 0.5,
 		anchorY: 0,
 		x: laneX,
 		y: 0,
 		width: LANE_WIDTH,
-		height: LANE_HEIGHT
+		height: LANE_HEIGHT,
+		alpha: 0
 	});
 	game.addChild(lane);
 	lanes.push(lane);
 	// Visually improved target: larger, more distinct, with a colored border effect