/**** 
* Plugins
****/ 
var tween = LK.import("@upit/tween.v1");
/**** 
* Classes
****/ 
var Column = Container.expand(function () {
	var self = Container.call(this);
	var columnGraphics = self.attachAsset('column', {
		anchorX: 0.5,
		anchorY: 0,
		alpha: 0.3
	});
	var scoreZone = self.attachAsset('scoreZone', {
		anchorX: 0.5,
		anchorY: 0.5,
		y: 2732 - 200,
		alpha: 0.4
	});
	self.scoreZoneY = 2732 - 200;
	return self;
});
var Tile = Container.expand(function () {
	var self = Container.call(this);
	var tileGraphics = self.attachAsset('tile', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	self.speed = 6;
	self.columnIndex = 0;
	self.scored = false;
	self.missed = false;
	self.update = function () {
		self.y += self.speed;
	};
	return self;
});
/**** 
* Initialize Game
****/ 
var game = new LK.Game({
	backgroundColor: 0x1a1a2e
});
/**** 
* Game Code
****/ 
var columns = [];
var tiles = [];
var score = 0;
var gameStarted = false;
var musicDuration = 22000; // 22 seconds
var gameStartTime = 0;
var perfectZone = 50;
var goodZone = 100;
var pianoTones = ['noteA3', 'noteC4', 'noteE4', 'noteG4'];
var columnDelays = [0, 50, 100, 150]; // Stagger effects by 50ms per column
var lastBeatTime = 0;
var beatCount = 0;
var baseAnimationIntensity = 1.0;
var comboCount = 0;
var comboCounter = 0;
// Create background overlay for rhythmic effects
var backgroundOverlay = LK.getAsset('backgroundOverlay', {
	anchorX: 0.5,
	anchorY: 0.5,
	alpha: 0.05,
	x: 2048 / 2,
	y: 2732 / 2
});
game.addChild(backgroundOverlay);
// Create back gray overlay for beat animation
var backgrayoverlay = LK.getAsset('backgrayoverlay', {
	anchorX: 0.5,
	anchorY: 0.5,
	alpha: 0,
	x: 2048 / 2,
	y: 2732 / 2
});
game.addChild(backgrayoverlay);
// Create pulse flash overlay at center, behind falling tiles
var pulseFlashFull = LK.getAsset('pulseFlash1', {
	anchorX: 0.5,
	anchorY: 0.5,
	alpha: 0,
	scaleX: 2048 / 100,
	// Scale to screen width (2048px / 100px asset)
	scaleY: 2732 / 100,
	// Scale to screen height (2732px / 100px asset)
	x: 2048 / 2,
	y: 2732 / 2
});
game.addChild(pulseFlashFull);
// Create UI
var scoreTxt = new Text2('Score: 0', {
	size: 120,
	fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Create combo display
var comboDisplay = new Text2('', {
	size: 60,
	fill: 0xFFD700,
	fontWeight: 'bold'
});
comboDisplay.anchor.set(1, 0);
comboDisplay.visible = false;
LK.gui.topRight.addChild(comboDisplay);
// Position relative to topRight anchor
comboDisplay.x = -80;
comboDisplay.y = 40;
// Create columns
var columnWidth = 2048 / 4;
for (var i = 0; i < 4; i++) {
	var column = new Column();
	column.x = i * columnWidth + columnWidth / 2;
	column.y = 0;
	columns.push(column);
	game.addChild(column);
}
// Multiple predefined melody patterns for variety
var melodyPatterns = [[1, 1, 2, 2, 3, 3, 2, 2, 1],
// Pattern A: C4, C4, E4, E4, G4, G4, E4, E4, C4
[3, 2, 1, 1, 0, 0, 1, 2],
// Pattern B: G4, E4, C4, C4, A3, A3, C4, E4
[0, 2, 1, 3, 0, 2, 1, 3],
// Pattern C: A3, E4, C4, G4, A3, E4, C4, G4
[3, 1, 3, 1, 2, 2, 0, 0] // Pattern D: G4, C4, G4, C4, E4, E4, A3, A3
];
var currentPatternIndex = 0;
var melodyPattern = melodyPatterns[currentPatternIndex];
var spawnPattern = [];
var beatInterval = 500; // 500ms per beat for steady rhythm
var patternRepeatDuration = melodyPattern.length * beatInterval; // 9 beats = 4500ms per pattern
// Generate spawn pattern by repeating the melody pattern throughout the game duration
var currentTime = 500; // Start after 500ms
var patternPosition = 0;
while (currentTime < musicDuration) {
	spawnPattern.push({
		time: currentTime,
		columns: [melodyPattern[patternPosition]]
	});
	currentTime += beatInterval;
	patternPosition = (patternPosition + 1) % melodyPattern.length;
}
var patternIndex = 0;
function spawnTile(columnIndex) {
	var tile = new Tile();
	tile.columnIndex = columnIndex;
	tile.x = columns[columnIndex].x;
	tile.y = -60;
	tiles.push(tile);
	game.addChild(tile);
}
function calculateScore(distance) {
	if (distance <= perfectZone) {
		return 100;
	} else if (distance <= goodZone) {
		return 50;
	}
	return 0;
}
function updateScore(points) {
	score += points;
	scoreTxt.setText('Score: ' + score);
}
function triggerBeatAnimation() {
	beatCount++;
	// Calculate animation intensity based on score milestones
	var scoreMultiplier = 1.0;
	if (score >= 5000) {
		scoreMultiplier = 2.0;
	} else if (score >= 2500) {
		scoreMultiplier = 1.7;
	} else if (score >= 1000) {
		scoreMultiplier = 1.4;
	} else if (score >= 500) {
		scoreMultiplier = 1.2;
	}
	var animationIntensity = baseAnimationIntensity * scoreMultiplier;
	// Background pulse - scale and brightness
	var pulseScale = 1.0 + 0.02 * animationIntensity; // 2% scale increase max
	var pulseAlpha = 0.05 + 0.01 * animationIntensity; // 1% brightness increase max
	tween(backgroundOverlay, {
		scaleX: pulseScale,
		scaleY: pulseScale,
		alpha: pulseAlpha
	}, {
		duration: 100,
		easing: tween.easeOut,
		onFinish: function onFinish() {
			tween(backgroundOverlay, {
				scaleX: 1.0,
				scaleY: 1.0,
				alpha: 0.05
			}, {
				duration: 200,
				easing: tween.easeIn
			});
		}
	});
	// Pulse flash animation - fade in then out on every beat
	tween(pulseFlashFull, {
		alpha: 0.35
	}, {
		duration: 0,
		onFinish: function onFinish() {
			tween(pulseFlashFull, {
				alpha: 0
			}, {
				duration: 300,
				easing: tween.easeOut
			});
		}
	});
	// Subtle rotation effect that increases with score
	var rotationAmount = beatCount % 8 * 0.01 * animationIntensity; // Very subtle rotation
	tween(backgroundOverlay, {
		rotation: rotationAmount
	}, {
		duration: 300,
		easing: tween.easeInOut
	});
	// Back gray overlay beat animation - fade in to 0.35 then out over 300ms
	tween(backgrayoverlay, {
		alpha: 0.35
	}, {
		duration: 0,
		onFinish: function onFinish() {
			tween(backgrayoverlay, {
				alpha: 0
			}, {
				duration: 300,
				easing: tween.easeOut
			});
		}
	});
}
game.down = function (x, y, obj) {
	if (!gameStarted) {
		gameStarted = true;
		gameStartTime = LK.ticks * (1000 / 60);
		LK.playMusic('gamesound3_backing', {
			loop: true
		});
		return;
	}
	// Determine which column was tapped
	var columnIndex = Math.floor(x / columnWidth);
	if (columnIndex < 0) {
		columnIndex = 0;
	}
	if (columnIndex > 3) {
		columnIndex = 3;
	}
	var hitTile = null;
	var bestDistance = Infinity;
	// Find the closest tile in the tapped column within scoring range
	for (var i = 0; i < tiles.length; i++) {
		var tile = tiles[i];
		if (tile.columnIndex === columnIndex && !tile.scored && !tile.missed) {
			var scoreZoneY = columns[columnIndex].scoreZoneY;
			var distance = Math.abs(tile.y - scoreZoneY);
			if (distance <= goodZone && distance < bestDistance) {
				bestDistance = distance;
				hitTile = tile;
			}
		}
	}
	if (hitTile) {
		hitTile.scored = true;
		var points = calculateScore(bestDistance);
		updateScore(points);
		// Increment combo count
		comboCount++;
		comboCounter++;
		// Update combo display
		if (comboCounter < 2) {
			comboDisplay.visible = false;
		} else {
			comboDisplay.visible = true;
			comboDisplay.setText('Combo x' + comboCounter);
			// Check for color change and scaling on multiples of 10
			if (comboCounter % 10 === 0 && comboCounter > 0) {
				// Change color to bright red temporarily
				comboDisplay.tint = 0xFF4444;
				// Scale animation - larger scale for more dramatic effect
				tween(comboDisplay, {
					scaleX: 1.5,
					scaleY: 1.5
				}, {
					duration: 200,
					easing: tween.easeOut,
					onFinish: function onFinish() {
						tween(comboDisplay, {
							scaleX: 1.0,
							scaleY: 1.0
						}, {
							duration: 400,
							easing: tween.easeInOut
						});
					}
				});
				// Reset color after 600ms
				LK.setTimeout(function () {
					comboDisplay.tint = 0xFFD700;
				}, 600);
			}
		}
		// Check for 10-hit combo streak
		if (comboCount % 10 === 0) {
			// Spawn 'comboGlow' behind tiles with opacity 0.5 and fade it out over 400ms
			var comboGlow = LK.getAsset('comboGlow', {
				anchorX: 0.5,
				anchorY: 0.5,
				alpha: 0.5,
				x: 2048 / 2,
				y: 2732 / 2
			});
			game.addChild(comboGlow);
			// Fade out comboGlow over 400ms
			tween(comboGlow, {
				alpha: 0
			}, {
				duration: 400,
				easing: tween.easeOut,
				onFinish: function onFinish() {
					comboGlow.destroy();
				}
			});
			// Spawn 'comboText' at (screen center X, 100px from top)
			var comboText = new Text2('Combo x' + comboCount + '!', {
				size: 200,
				fill: 0xFFD700
			});
			comboText.anchor.set(0.5, 0.5);
			comboText.x = 2048 / 2;
			comboText.y = 280; // 100px from top
			comboText.alpha = 0;
			comboText.scaleX = 0.8;
			comboText.scaleY = 0.8;
			game.addChild(comboText);
			// Fade in over 150ms with scaling popping effect
			tween(comboText, {
				alpha: 1,
				scaleX: 1.2,
				scaleY: 1.2
			}, {
				duration: 150,
				easing: tween.easeOut,
				onFinish: function onFinish() {
					// Hold for 400ms, then fade out over 300ms
					LK.setTimeout(function () {
						tween(comboText, {
							alpha: 0,
							scaleX: 1.0,
							scaleY: 1.0
						}, {
							duration: 300,
							easing: tween.easeOut,
							onFinish: function onFinish() {
								comboText.destroy();
							}
						});
					}, 400);
				}
			});
		}
		// Create color burst effect with bright, saturated colors for each column
		var burstColors = [0x9932CC, 0x0000FF, 0xFFFF00, 0xFF0000]; // Purple, Blue, Yellow, Red
		var burstColor = burstColors[columnIndex];
		var columnDelay = columnDelays[columnIndex];
		// Create column light-up effect
		LK.setTimeout(function () {
			var columnOverlay = LK.getAsset('columnOverlay', {
				anchorX: 0.5,
				anchorY: 0,
				tint: burstColor,
				alpha: 0.4,
				x: columns[columnIndex].x,
				y: 0
			});
			game.addChild(columnOverlay);
			// Animate the column overlay with pulse effect
			tween(columnOverlay, {
				alpha: 0
			}, {
				duration: 350,
				easing: tween.easeOut,
				onFinish: function onFinish() {
					columnOverlay.destroy();
				}
			});
		}, columnDelay);
		// Spawn 'burst1' at the tile's position - limited to 1.5x tile size with vivid colors
		LK.setTimeout(function () {
			var burst1 = LK.getAsset('brust1', {
				anchorX: 0.5,
				anchorY: 0.5,
				scaleX: 0.6,
				scaleY: 0.6,
				tint: burstColor,
				alpha: 0.9,
				x: hitTile.x,
				y: hitTile.y
			});
			game.addChild(burst1);
			tween(burst1, {
				scaleX: 1.8,
				// Limited to 1.5x tile size (400px * 1.5 = 600px, so scale ~1.8 for 100px asset)
				scaleY: 1.8,
				alpha: 0
			}, {
				duration: 350,
				easing: tween.easeOut,
				onFinish: function onFinish() {
					burst1.destroy();
				}
			});
		}, columnDelay);
		// Overlay 'glowring1' with matching column color and radial gradient effect
		LK.setTimeout(function () {
			var glowRing = LK.getAsset('glowring1', {
				anchorX: 0.5,
				anchorY: 0.5,
				scaleX: 0.8,
				scaleY: 0.8,
				tint: burstColor,
				alpha: 0.8,
				x: hitTile.x,
				y: hitTile.y
			});
			game.addChild(glowRing);
			tween(glowRing, {
				scaleX: 1.8,
				// Limited to 1.5x tile size
				scaleY: 1.8,
				alpha: 0
			}, {
				duration: 380,
				easing: tween.easeInOut,
				onFinish: function onFinish() {
					glowRing.destroy();
				}
			});
		}, columnDelay + 25);
		// Create spark line animation near the tile
		LK.setTimeout(function () {
			for (var s = 0; s < 4; s++) {
				var sparkLine = LK.getAsset('sparkline', {
					anchorX: 0.5,
					anchorY: 0.5,
					tint: burstColor,
					alpha: 0.9,
					x: hitTile.x + (Math.random() - 0.5) * 100,
					y: hitTile.y + (Math.random() - 0.5) * 100,
					rotation: Math.random() * Math.PI * 2
				});
				game.addChild(sparkLine);
				tween(sparkLine, {
					scaleX: 2,
					scaleY: 0.2,
					alpha: 0,
					rotation: sparkLine.rotation + Math.PI
				}, {
					duration: 200,
					easing: tween.easeOut,
					onFinish: function onFinish() {
						sparkLine.destroy();
					}
				});
			}
		}, columnDelay + 50);
		// Emit 5-6 small 'trail1' particles radiating outward with small movement radius
		LK.setTimeout(function () {
			var numTrails = 5 + Math.floor(Math.random() * 2); // 5-6 particles
			for (var t = 0; t < numTrails; t++) {
				var trail = LK.getAsset('trail1', {
					anchorX: 0.5,
					anchorY: 0.5,
					scaleX: 0.3,
					scaleY: 0.3,
					tint: burstColor,
					alpha: 0.8,
					x: hitTile.x,
					y: hitTile.y
				});
				game.addChild(trail);
				var angle = t / numTrails * Math.PI * 2 + Math.random() * 0.3;
				var distance = 40 + Math.random() * 40; // Small movement radius within 80px
				var targetX = hitTile.x + Math.cos(angle) * distance;
				var targetY = hitTile.y + Math.sin(angle) * distance;
				tween(trail, {
					x: targetX,
					y: targetY,
					alpha: 0,
					scaleX: 0.1,
					scaleY: 0.1
				}, {
					duration: 300,
					easing: tween.easeOut,
					onFinish: function onFinish() {
						trail.destroy();
					}
				});
			}
		}, columnDelay + 75);
		// Spawn columnFlash overlay at tile position with upward then downward animation
		var columnFlashAssets = ['columnFlash1', 'columnFlash2', 'columnFlash3', 'columnFlash4'];
		var originalY = hitTile.y;
		var columnFlash = LK.getAsset(columnFlashAssets[columnIndex], {
			anchorX: 0.5,
			anchorY: 0.5,
			alpha: 1.0,
			x: hitTile.x,
			y: originalY
		});
		game.addChild(columnFlash);
		// Animate upward movement over 150ms with ease-out
		tween(columnFlash, {
			y: originalY - 100
		}, {
			duration: 150,
			easing: tween.easeOut,
			onFinish: function onFinish() {
				// Animate downward movement back to original position over 150ms with ease-in
				tween(columnFlash, {
					y: originalY
				}, {
					duration: 150,
					easing: tween.easeIn,
					onFinish: function onFinish() {
						columnFlash.destroy();
					}
				});
			}
		});
		// Fade out overlay over full 300ms duration
		tween(columnFlash, {
			alpha: 0
		}, {
			duration: 300,
			easing: tween.easeOut
		});
		// Visual feedback for scoring
		if (points === 100) {
			LK.effects.flashObject(hitTile, 0x00ff00, 200);
		} else if (points === 50) {
			LK.effects.flashObject(hitTile, 0xffff00, 200);
		}
		LK.getSound(pianoTones[columnIndex]).play();
		// Remove tile
		hitTile.destroy();
		for (var j = tiles.length - 1; j >= 0; j--) {
			if (tiles[j] === hitTile) {
				tiles.splice(j, 1);
				break;
			}
		}
	}
};
game.update = function () {
	if (!gameStarted) {
		return;
	}
	var currentTime = LK.ticks * (1000 / 60) - gameStartTime;
	// Beat detection for background animation
	var currentBeatTime = Math.floor(currentTime / beatInterval) * beatInterval;
	if (currentBeatTime !== lastBeatTime && currentTime >= 500) {
		lastBeatTime = currentBeatTime;
		triggerBeatAnimation();
	}
	// Spawn tiles according to endless repeating pattern with pattern cycling and controlled randomness
	var patternTime = currentTime % patternRepeatDuration; // Loop the pattern timing
	var shouldSpawn = false;
	// Check if we need to switch to the next pattern
	var patternCycleTime = Math.floor(currentTime / patternRepeatDuration);
	var newPatternIndex = patternCycleTime % melodyPatterns.length;
	if (newPatternIndex !== currentPatternIndex) {
		currentPatternIndex = newPatternIndex;
		melodyPattern = melodyPatterns[currentPatternIndex];
	}
	for (var p = 0; p < melodyPattern.length; p++) {
		var spawnTime = p * beatInterval + 500; // Add initial 500ms offset
		if (Math.abs(patternTime - spawnTime) < 16) {
			// 16ms tolerance for 60fps
			var targetColumn = melodyPattern[p];
			var finalColumn = targetColumn;
			// Add controlled randomness - 20% chance to vary the column
			if (Math.random() < 0.2) {
				// Define harmonic variations for each base column
				var harmonicVariations = [[0, 1],
				// A3 can vary to C4
				[1, 2],
				// C4 can vary to E4  
				[2, 3, 1],
				// E4 can vary to G4 or back to C4
				[3, 2] // G4 can vary to E4
				];
				var variations = harmonicVariations[targetColumn];
				if (variations && variations.length > 0) {
					finalColumn = variations[Math.floor(Math.random() * variations.length)];
				}
			}
			spawnTile(finalColumn);
			shouldSpawn = true;
			break;
		}
	}
	// Update tiles and check for misses
	for (var i = tiles.length - 1; i >= 0; i--) {
		var tile = tiles[i];
		// Check if tile passed the scoring zone without being hit
		if (!tile.scored && !tile.missed && tile.y > columns[tile.columnIndex].scoreZoneY + goodZone) {
			tile.missed = true;
			comboCount = 0; // Reset combo on miss
			comboCounter = 0; // Reset combo counter on miss
			// Update combo display visibility and reset properties
			comboDisplay.visible = false;
			comboDisplay.tint = 0xFFD700;
			comboDisplay.scaleX = 1.0;
			comboDisplay.scaleY = 1.0;
			LK.effects.flashObject(tile, 0xff0000, 300);
			LK.getSound('miss').play();
		}
		// Remove tiles that are off screen
		if (tile.y > 2732 + 100) {
			tile.destroy();
			tiles.splice(i, 1);
		}
	}
}; /**** 
* Plugins
****/ 
var tween = LK.import("@upit/tween.v1");
/**** 
* Classes
****/ 
var Column = Container.expand(function () {
	var self = Container.call(this);
	var columnGraphics = self.attachAsset('column', {
		anchorX: 0.5,
		anchorY: 0,
		alpha: 0.3
	});
	var scoreZone = self.attachAsset('scoreZone', {
		anchorX: 0.5,
		anchorY: 0.5,
		y: 2732 - 200,
		alpha: 0.4
	});
	self.scoreZoneY = 2732 - 200;
	return self;
});
var Tile = Container.expand(function () {
	var self = Container.call(this);
	var tileGraphics = self.attachAsset('tile', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	self.speed = 6;
	self.columnIndex = 0;
	self.scored = false;
	self.missed = false;
	self.update = function () {
		self.y += self.speed;
	};
	return self;
});
/**** 
* Initialize Game
****/ 
var game = new LK.Game({
	backgroundColor: 0x1a1a2e
});
/**** 
* Game Code
****/ 
var columns = [];
var tiles = [];
var score = 0;
var gameStarted = false;
var musicDuration = 22000; // 22 seconds
var gameStartTime = 0;
var perfectZone = 50;
var goodZone = 100;
var pianoTones = ['noteA3', 'noteC4', 'noteE4', 'noteG4'];
var columnDelays = [0, 50, 100, 150]; // Stagger effects by 50ms per column
var lastBeatTime = 0;
var beatCount = 0;
var baseAnimationIntensity = 1.0;
var comboCount = 0;
var comboCounter = 0;
// Create background overlay for rhythmic effects
var backgroundOverlay = LK.getAsset('backgroundOverlay', {
	anchorX: 0.5,
	anchorY: 0.5,
	alpha: 0.05,
	x: 2048 / 2,
	y: 2732 / 2
});
game.addChild(backgroundOverlay);
// Create back gray overlay for beat animation
var backgrayoverlay = LK.getAsset('backgrayoverlay', {
	anchorX: 0.5,
	anchorY: 0.5,
	alpha: 0,
	x: 2048 / 2,
	y: 2732 / 2
});
game.addChild(backgrayoverlay);
// Create pulse flash overlay at center, behind falling tiles
var pulseFlashFull = LK.getAsset('pulseFlash1', {
	anchorX: 0.5,
	anchorY: 0.5,
	alpha: 0,
	scaleX: 2048 / 100,
	// Scale to screen width (2048px / 100px asset)
	scaleY: 2732 / 100,
	// Scale to screen height (2732px / 100px asset)
	x: 2048 / 2,
	y: 2732 / 2
});
game.addChild(pulseFlashFull);
// Create UI
var scoreTxt = new Text2('Score: 0', {
	size: 120,
	fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Create combo display
var comboDisplay = new Text2('', {
	size: 60,
	fill: 0xFFD700,
	fontWeight: 'bold'
});
comboDisplay.anchor.set(1, 0);
comboDisplay.visible = false;
LK.gui.topRight.addChild(comboDisplay);
// Position relative to topRight anchor
comboDisplay.x = -80;
comboDisplay.y = 40;
// Create columns
var columnWidth = 2048 / 4;
for (var i = 0; i < 4; i++) {
	var column = new Column();
	column.x = i * columnWidth + columnWidth / 2;
	column.y = 0;
	columns.push(column);
	game.addChild(column);
}
// Multiple predefined melody patterns for variety
var melodyPatterns = [[1, 1, 2, 2, 3, 3, 2, 2, 1],
// Pattern A: C4, C4, E4, E4, G4, G4, E4, E4, C4
[3, 2, 1, 1, 0, 0, 1, 2],
// Pattern B: G4, E4, C4, C4, A3, A3, C4, E4
[0, 2, 1, 3, 0, 2, 1, 3],
// Pattern C: A3, E4, C4, G4, A3, E4, C4, G4
[3, 1, 3, 1, 2, 2, 0, 0] // Pattern D: G4, C4, G4, C4, E4, E4, A3, A3
];
var currentPatternIndex = 0;
var melodyPattern = melodyPatterns[currentPatternIndex];
var spawnPattern = [];
var beatInterval = 500; // 500ms per beat for steady rhythm
var patternRepeatDuration = melodyPattern.length * beatInterval; // 9 beats = 4500ms per pattern
// Generate spawn pattern by repeating the melody pattern throughout the game duration
var currentTime = 500; // Start after 500ms
var patternPosition = 0;
while (currentTime < musicDuration) {
	spawnPattern.push({
		time: currentTime,
		columns: [melodyPattern[patternPosition]]
	});
	currentTime += beatInterval;
	patternPosition = (patternPosition + 1) % melodyPattern.length;
}
var patternIndex = 0;
function spawnTile(columnIndex) {
	var tile = new Tile();
	tile.columnIndex = columnIndex;
	tile.x = columns[columnIndex].x;
	tile.y = -60;
	tiles.push(tile);
	game.addChild(tile);
}
function calculateScore(distance) {
	if (distance <= perfectZone) {
		return 100;
	} else if (distance <= goodZone) {
		return 50;
	}
	return 0;
}
function updateScore(points) {
	score += points;
	scoreTxt.setText('Score: ' + score);
}
function triggerBeatAnimation() {
	beatCount++;
	// Calculate animation intensity based on score milestones
	var scoreMultiplier = 1.0;
	if (score >= 5000) {
		scoreMultiplier = 2.0;
	} else if (score >= 2500) {
		scoreMultiplier = 1.7;
	} else if (score >= 1000) {
		scoreMultiplier = 1.4;
	} else if (score >= 500) {
		scoreMultiplier = 1.2;
	}
	var animationIntensity = baseAnimationIntensity * scoreMultiplier;
	// Background pulse - scale and brightness
	var pulseScale = 1.0 + 0.02 * animationIntensity; // 2% scale increase max
	var pulseAlpha = 0.05 + 0.01 * animationIntensity; // 1% brightness increase max
	tween(backgroundOverlay, {
		scaleX: pulseScale,
		scaleY: pulseScale,
		alpha: pulseAlpha
	}, {
		duration: 100,
		easing: tween.easeOut,
		onFinish: function onFinish() {
			tween(backgroundOverlay, {
				scaleX: 1.0,
				scaleY: 1.0,
				alpha: 0.05
			}, {
				duration: 200,
				easing: tween.easeIn
			});
		}
	});
	// Pulse flash animation - fade in then out on every beat
	tween(pulseFlashFull, {
		alpha: 0.35
	}, {
		duration: 0,
		onFinish: function onFinish() {
			tween(pulseFlashFull, {
				alpha: 0
			}, {
				duration: 300,
				easing: tween.easeOut
			});
		}
	});
	// Subtle rotation effect that increases with score
	var rotationAmount = beatCount % 8 * 0.01 * animationIntensity; // Very subtle rotation
	tween(backgroundOverlay, {
		rotation: rotationAmount
	}, {
		duration: 300,
		easing: tween.easeInOut
	});
	// Back gray overlay beat animation - fade in to 0.35 then out over 300ms
	tween(backgrayoverlay, {
		alpha: 0.35
	}, {
		duration: 0,
		onFinish: function onFinish() {
			tween(backgrayoverlay, {
				alpha: 0
			}, {
				duration: 300,
				easing: tween.easeOut
			});
		}
	});
}
game.down = function (x, y, obj) {
	if (!gameStarted) {
		gameStarted = true;
		gameStartTime = LK.ticks * (1000 / 60);
		LK.playMusic('gamesound3_backing', {
			loop: true
		});
		return;
	}
	// Determine which column was tapped
	var columnIndex = Math.floor(x / columnWidth);
	if (columnIndex < 0) {
		columnIndex = 0;
	}
	if (columnIndex > 3) {
		columnIndex = 3;
	}
	var hitTile = null;
	var bestDistance = Infinity;
	// Find the closest tile in the tapped column within scoring range
	for (var i = 0; i < tiles.length; i++) {
		var tile = tiles[i];
		if (tile.columnIndex === columnIndex && !tile.scored && !tile.missed) {
			var scoreZoneY = columns[columnIndex].scoreZoneY;
			var distance = Math.abs(tile.y - scoreZoneY);
			if (distance <= goodZone && distance < bestDistance) {
				bestDistance = distance;
				hitTile = tile;
			}
		}
	}
	if (hitTile) {
		hitTile.scored = true;
		var points = calculateScore(bestDistance);
		updateScore(points);
		// Increment combo count
		comboCount++;
		comboCounter++;
		// Update combo display
		if (comboCounter < 2) {
			comboDisplay.visible = false;
		} else {
			comboDisplay.visible = true;
			comboDisplay.setText('Combo x' + comboCounter);
			// Check for color change and scaling on multiples of 10
			if (comboCounter % 10 === 0 && comboCounter > 0) {
				// Change color to bright red temporarily
				comboDisplay.tint = 0xFF4444;
				// Scale animation - larger scale for more dramatic effect
				tween(comboDisplay, {
					scaleX: 1.5,
					scaleY: 1.5
				}, {
					duration: 200,
					easing: tween.easeOut,
					onFinish: function onFinish() {
						tween(comboDisplay, {
							scaleX: 1.0,
							scaleY: 1.0
						}, {
							duration: 400,
							easing: tween.easeInOut
						});
					}
				});
				// Reset color after 600ms
				LK.setTimeout(function () {
					comboDisplay.tint = 0xFFD700;
				}, 600);
			}
		}
		// Check for 10-hit combo streak
		if (comboCount % 10 === 0) {
			// Spawn 'comboGlow' behind tiles with opacity 0.5 and fade it out over 400ms
			var comboGlow = LK.getAsset('comboGlow', {
				anchorX: 0.5,
				anchorY: 0.5,
				alpha: 0.5,
				x: 2048 / 2,
				y: 2732 / 2
			});
			game.addChild(comboGlow);
			// Fade out comboGlow over 400ms
			tween(comboGlow, {
				alpha: 0
			}, {
				duration: 400,
				easing: tween.easeOut,
				onFinish: function onFinish() {
					comboGlow.destroy();
				}
			});
			// Spawn 'comboText' at (screen center X, 100px from top)
			var comboText = new Text2('Combo x' + comboCount + '!', {
				size: 200,
				fill: 0xFFD700
			});
			comboText.anchor.set(0.5, 0.5);
			comboText.x = 2048 / 2;
			comboText.y = 280; // 100px from top
			comboText.alpha = 0;
			comboText.scaleX = 0.8;
			comboText.scaleY = 0.8;
			game.addChild(comboText);
			// Fade in over 150ms with scaling popping effect
			tween(comboText, {
				alpha: 1,
				scaleX: 1.2,
				scaleY: 1.2
			}, {
				duration: 150,
				easing: tween.easeOut,
				onFinish: function onFinish() {
					// Hold for 400ms, then fade out over 300ms
					LK.setTimeout(function () {
						tween(comboText, {
							alpha: 0,
							scaleX: 1.0,
							scaleY: 1.0
						}, {
							duration: 300,
							easing: tween.easeOut,
							onFinish: function onFinish() {
								comboText.destroy();
							}
						});
					}, 400);
				}
			});
		}
		// Create color burst effect with bright, saturated colors for each column
		var burstColors = [0x9932CC, 0x0000FF, 0xFFFF00, 0xFF0000]; // Purple, Blue, Yellow, Red
		var burstColor = burstColors[columnIndex];
		var columnDelay = columnDelays[columnIndex];
		// Create column light-up effect
		LK.setTimeout(function () {
			var columnOverlay = LK.getAsset('columnOverlay', {
				anchorX: 0.5,
				anchorY: 0,
				tint: burstColor,
				alpha: 0.4,
				x: columns[columnIndex].x,
				y: 0
			});
			game.addChild(columnOverlay);
			// Animate the column overlay with pulse effect
			tween(columnOverlay, {
				alpha: 0
			}, {
				duration: 350,
				easing: tween.easeOut,
				onFinish: function onFinish() {
					columnOverlay.destroy();
				}
			});
		}, columnDelay);
		// Spawn 'burst1' at the tile's position - limited to 1.5x tile size with vivid colors
		LK.setTimeout(function () {
			var burst1 = LK.getAsset('brust1', {
				anchorX: 0.5,
				anchorY: 0.5,
				scaleX: 0.6,
				scaleY: 0.6,
				tint: burstColor,
				alpha: 0.9,
				x: hitTile.x,
				y: hitTile.y
			});
			game.addChild(burst1);
			tween(burst1, {
				scaleX: 1.8,
				// Limited to 1.5x tile size (400px * 1.5 = 600px, so scale ~1.8 for 100px asset)
				scaleY: 1.8,
				alpha: 0
			}, {
				duration: 350,
				easing: tween.easeOut,
				onFinish: function onFinish() {
					burst1.destroy();
				}
			});
		}, columnDelay);
		// Overlay 'glowring1' with matching column color and radial gradient effect
		LK.setTimeout(function () {
			var glowRing = LK.getAsset('glowring1', {
				anchorX: 0.5,
				anchorY: 0.5,
				scaleX: 0.8,
				scaleY: 0.8,
				tint: burstColor,
				alpha: 0.8,
				x: hitTile.x,
				y: hitTile.y
			});
			game.addChild(glowRing);
			tween(glowRing, {
				scaleX: 1.8,
				// Limited to 1.5x tile size
				scaleY: 1.8,
				alpha: 0
			}, {
				duration: 380,
				easing: tween.easeInOut,
				onFinish: function onFinish() {
					glowRing.destroy();
				}
			});
		}, columnDelay + 25);
		// Create spark line animation near the tile
		LK.setTimeout(function () {
			for (var s = 0; s < 4; s++) {
				var sparkLine = LK.getAsset('sparkline', {
					anchorX: 0.5,
					anchorY: 0.5,
					tint: burstColor,
					alpha: 0.9,
					x: hitTile.x + (Math.random() - 0.5) * 100,
					y: hitTile.y + (Math.random() - 0.5) * 100,
					rotation: Math.random() * Math.PI * 2
				});
				game.addChild(sparkLine);
				tween(sparkLine, {
					scaleX: 2,
					scaleY: 0.2,
					alpha: 0,
					rotation: sparkLine.rotation + Math.PI
				}, {
					duration: 200,
					easing: tween.easeOut,
					onFinish: function onFinish() {
						sparkLine.destroy();
					}
				});
			}
		}, columnDelay + 50);
		// Emit 5-6 small 'trail1' particles radiating outward with small movement radius
		LK.setTimeout(function () {
			var numTrails = 5 + Math.floor(Math.random() * 2); // 5-6 particles
			for (var t = 0; t < numTrails; t++) {
				var trail = LK.getAsset('trail1', {
					anchorX: 0.5,
					anchorY: 0.5,
					scaleX: 0.3,
					scaleY: 0.3,
					tint: burstColor,
					alpha: 0.8,
					x: hitTile.x,
					y: hitTile.y
				});
				game.addChild(trail);
				var angle = t / numTrails * Math.PI * 2 + Math.random() * 0.3;
				var distance = 40 + Math.random() * 40; // Small movement radius within 80px
				var targetX = hitTile.x + Math.cos(angle) * distance;
				var targetY = hitTile.y + Math.sin(angle) * distance;
				tween(trail, {
					x: targetX,
					y: targetY,
					alpha: 0,
					scaleX: 0.1,
					scaleY: 0.1
				}, {
					duration: 300,
					easing: tween.easeOut,
					onFinish: function onFinish() {
						trail.destroy();
					}
				});
			}
		}, columnDelay + 75);
		// Spawn columnFlash overlay at tile position with upward then downward animation
		var columnFlashAssets = ['columnFlash1', 'columnFlash2', 'columnFlash3', 'columnFlash4'];
		var originalY = hitTile.y;
		var columnFlash = LK.getAsset(columnFlashAssets[columnIndex], {
			anchorX: 0.5,
			anchorY: 0.5,
			alpha: 1.0,
			x: hitTile.x,
			y: originalY
		});
		game.addChild(columnFlash);
		// Animate upward movement over 150ms with ease-out
		tween(columnFlash, {
			y: originalY - 100
		}, {
			duration: 150,
			easing: tween.easeOut,
			onFinish: function onFinish() {
				// Animate downward movement back to original position over 150ms with ease-in
				tween(columnFlash, {
					y: originalY
				}, {
					duration: 150,
					easing: tween.easeIn,
					onFinish: function onFinish() {
						columnFlash.destroy();
					}
				});
			}
		});
		// Fade out overlay over full 300ms duration
		tween(columnFlash, {
			alpha: 0
		}, {
			duration: 300,
			easing: tween.easeOut
		});
		// Visual feedback for scoring
		if (points === 100) {
			LK.effects.flashObject(hitTile, 0x00ff00, 200);
		} else if (points === 50) {
			LK.effects.flashObject(hitTile, 0xffff00, 200);
		}
		LK.getSound(pianoTones[columnIndex]).play();
		// Remove tile
		hitTile.destroy();
		for (var j = tiles.length - 1; j >= 0; j--) {
			if (tiles[j] === hitTile) {
				tiles.splice(j, 1);
				break;
			}
		}
	}
};
game.update = function () {
	if (!gameStarted) {
		return;
	}
	var currentTime = LK.ticks * (1000 / 60) - gameStartTime;
	// Beat detection for background animation
	var currentBeatTime = Math.floor(currentTime / beatInterval) * beatInterval;
	if (currentBeatTime !== lastBeatTime && currentTime >= 500) {
		lastBeatTime = currentBeatTime;
		triggerBeatAnimation();
	}
	// Spawn tiles according to endless repeating pattern with pattern cycling and controlled randomness
	var patternTime = currentTime % patternRepeatDuration; // Loop the pattern timing
	var shouldSpawn = false;
	// Check if we need to switch to the next pattern
	var patternCycleTime = Math.floor(currentTime / patternRepeatDuration);
	var newPatternIndex = patternCycleTime % melodyPatterns.length;
	if (newPatternIndex !== currentPatternIndex) {
		currentPatternIndex = newPatternIndex;
		melodyPattern = melodyPatterns[currentPatternIndex];
	}
	for (var p = 0; p < melodyPattern.length; p++) {
		var spawnTime = p * beatInterval + 500; // Add initial 500ms offset
		if (Math.abs(patternTime - spawnTime) < 16) {
			// 16ms tolerance for 60fps
			var targetColumn = melodyPattern[p];
			var finalColumn = targetColumn;
			// Add controlled randomness - 20% chance to vary the column
			if (Math.random() < 0.2) {
				// Define harmonic variations for each base column
				var harmonicVariations = [[0, 1],
				// A3 can vary to C4
				[1, 2],
				// C4 can vary to E4  
				[2, 3, 1],
				// E4 can vary to G4 or back to C4
				[3, 2] // G4 can vary to E4
				];
				var variations = harmonicVariations[targetColumn];
				if (variations && variations.length > 0) {
					finalColumn = variations[Math.floor(Math.random() * variations.length)];
				}
			}
			spawnTile(finalColumn);
			shouldSpawn = true;
			break;
		}
	}
	// Update tiles and check for misses
	for (var i = tiles.length - 1; i >= 0; i--) {
		var tile = tiles[i];
		// Check if tile passed the scoring zone without being hit
		if (!tile.scored && !tile.missed && tile.y > columns[tile.columnIndex].scoreZoneY + goodZone) {
			tile.missed = true;
			comboCount = 0; // Reset combo on miss
			comboCounter = 0; // Reset combo counter on miss
			// Update combo display visibility and reset properties
			comboDisplay.visible = false;
			comboDisplay.tint = 0xFFD700;
			comboDisplay.scaleX = 1.0;
			comboDisplay.scaleY = 1.0;
			LK.effects.flashObject(tile, 0xff0000, 300);
			LK.getSound('miss').play();
		}
		// Remove tiles that are off screen
		if (tile.y > 2732 + 100) {
			tile.destroy();
			tiles.splice(i, 1);
		}
	}
};