User prompt
start ve endless assetlerini yeniden tanımla
User prompt
endless ve start buttonlarının asset idlerini güncelle
Code edit (1 edits merged)
Please save this source code
User prompt
I would love that the sound playing when you tap would blend better into the music. Like dance music and the sound is a vynil spin or something like that.
User prompt
devamiicin screeninde 3snden sonra ekrana basıldığında ana menüye atsın.
User prompt
Fix pause button functionality on game screen by properly implementing the click handler
User prompt
chapter 2 enddeki pause butona tıklandığında pausebg asseti, restart butonu ve main menü butonu gelsin
User prompt
chapter 2 enddeki pause button çalışmıyor
User prompt
ready info assetini 25px yukarı çıkar
User prompt
blurred assetini 50px yukarı çıkar
User prompt
devamiicin assetinin göründüğü ekranda pause buttonu da olsun
User prompt
tutorial bpm display text force 60 BPM
User prompt
song name text 8pxupward
User prompt
song name text 15px upward
User prompt
oyundaki bpm value ve şarkı ismi yazılarını 30px aşağı indir
User prompt
oyundaki bpm value ve şarkı ismi yazılarını 75px yukarı çıkar
User prompt
gameover assetini yeniden boyutlandır ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
oyundaki tüm metinleri tekrardan yüksek çözünürlükte görünecek şekilde düzenle. boyutları sabit kalsın
User prompt
creditstab yeniden boyutlandır ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
supporttab yeniden boyutlandır ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
0.3 yap
User prompt
settings menüsündeki settings assetini yeniden boyutlandır ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
0.18 yap
/**** 
* Plugins
****/ 
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
	bpm: 100,
	soundVolume: 100,
	selectedKickSound: "kick",
	selectedHihatSound: "hihat",
	selectedSnareClapSound: "snareclap",
	selectedPercussionSound: "percussion",
	tutorialCompleted: false,
	currentLevel: 1
});
/**** 
* Classes
****/ 
// default bpm 100
// Beat marker class (moving dot)
var BeatMarker = Container.expand(function () {
	var self = Container.call(this);
	var marker = self.attachAsset('beatMarker', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	self.active = true; // Whether this beat is still hittable
	self.hit = false; // Whether this beat was hit
	self.assetType = 'circle'; // Varsayılan asset tipi
	// Trail effect properties
	self.lastTrailTime = 0;
	self.trailInterval = 15; // Her 15ms'de parçacık oluştur
	self.updateTrail = function () {
		var now = Date.now();
		if (now - this.lastTrailTime > this.trailInterval) {
			var colors = BEAT_COLORS[this.assetType] || BEAT_COLORS.circle;
			// Beat'in hareket yönünü belirle (sağ tarafta mı?)
			var isRightSide = this.x > HIT_ZONE_X;
			var directionMultiplier = isRightSide ? 1 : -1;
			// --- Trail yayılımını BPM'e göre ayarla ---
			var params = getWaveParams(bpm);
			var spreadX = params.waveAmplitude * 0.2; // Dalga yüksekliğine orantılı
			var spreadY = params.waveAmplitude * 0.2;
			for (var i = 0; i < 10; i++) {
				// Ana parçacık
				var mainParticle = createTrailParticle(this.x + (Math.random() - 0.5) * spreadX, this.y + (Math.random() - 0.5) * spreadY, colors.mainTrail);
				mainParticle.velocityX = (Math.random() * 2 + 1) * directionMultiplier;
				mainParticle.velocityY = (Math.random() - 0.5) * 3;
				// Yan parçacık 1
				var subParticle1 = createTrailParticle(this.x + (Math.random() - 0.5) * (spreadX * 1.5), this.y + (Math.random() - 0.5) * (spreadY * 1.5), colors.subTrail1);
				subParticle1.width = 4;
				subParticle1.height = 4;
				subParticle1.velocityX = (Math.random() * 1.5 + 0.5) * directionMultiplier;
				subParticle1.velocityY = (Math.random() - 0.5) * 2;
				// Yan parçacık 2
				var subParticle2 = createTrailParticle(this.x + (Math.random() - 0.5) * (spreadX * 1.25), this.y + (Math.random() - 0.5) * (spreadY * 1.25), colors.subTrail2);
				subParticle2.width = 3;
				subParticle2.height = 3;
				subParticle2.velocityX = (Math.random() * 1 + 0.5) * directionMultiplier;
				subParticle2.velocityY = (Math.random() - 0.5) * 1.5;
				// Parçacıkları oyun ekranına ekle
				[mainParticle, subParticle1, subParticle2].forEach(function (p) {
					p.fadeSpeed = 0.03;
					p.gravity = 0.1;
					p.drag = 0.98;
					gamescreen.addChild(p);
					trailParticles.push(p);
				});
			}
			this.lastTrailTime = now;
		}
	};
	// For feedback animation
	self.showFeedback = function (type) {
		var feedbackId = type === 'good' ? 'goodCircle' : 'missCircle';
		var feedback = LK.getAsset(feedbackId, {
			anchorX: 0.5,
			anchorY: 0.5,
			x: self.x,
			y: self.y,
			alpha: 0.7,
			scaleX: 1,
			scaleY: 1
		});
		game.addChild(feedback);
		tween(feedback, {
			alpha: 0,
			scaleX: 1.5,
			scaleY: 1.5
		}, {
			duration: 400,
			easing: tween.easeOut,
			onFinish: function onFinish() {
				feedback.destroy();
			}
		});
	};
	return self;
});
/**** 
* Initialize Game
****/ 
var game = new LK.Game({
	backgroundColor: 0x000000 // black background
});
/**** 
* Game Code
****/ 
// --- BPM-specific music assets (kickless versions) ---
// Music (looping, short demo)
// Sound for kick
// Good feedback
// Miss feedback
// Beat marker
// Timeline bar
// Bass drum (kick) hit circle when active
// Bass drum (kick) hit circle
// --- Story Chapters Configuration ---
var STORY_CHAPTERS = [{
	bpm: 60,
	musicId: '60BPMLowKey',
	songName: 'LowKey'
}, {
	bpm: 90,
	musicId: '90BPMDigitalPulse',
	songName: 'DigitalPulse'
}];
// --- Game constants ---
var TIMELINE_WIDTH = 2048;
var TIMELINE_HEIGHT = 25;
var TIMELINE_Y = 2000; // Y position of timeline bar
var TIMELINE_X = 0;
var HIT_ZONE_X = 2048 / 2; // Center of screen
var HIT_ZONE_Y = TIMELINE_Y + TIMELINE_HEIGHT / 2;
var HIT_WINDOW = 120; // px distance for perfect hit
// Beat speed remains constant - only spacing between beats changes based on BPM
function getBeatSpeed() {
	return 400; // Constant speed of 400 px/s regardless of BPM
}
var INITIAL_BPM = 100; // Starting BPM
var BEATS_PER_MEASURE = 4;
var BEAT_INTERVAL = 60 / INITIAL_BPM; // seconds per beat
var DIFFICULTY_STEP = 0.15; // How much to increase BPM per level
var MAX_MISSES = 5;
// --- Game state ---
var beats = []; // Active BeatMarker objects
var beatPattern = []; // Array of {time, type}
var nextBeatIdx = 0; // Next beat to spawn
var songTime = 0; // Elapsed time in seconds
var lastTickTime = Date.now();
var score = 0;
var misses = 0;
var ignoredMisses = 0; // Track first 3 misses to ignore
var combo = 0;
var tutorialCompleted = false;
var currentLevel = 1;
// Reset storage values to ensure game always starts from beginning on reload
storage.tutorialCompleted = false;
storage.currentLevel = 1;
var isTutorial = false;
var bpm = typeof storage.bpm === "number" ? storage.bpm : INITIAL_BPM;
var soundVolume = typeof storage.soundVolume === "number" ? storage.soundVolume : 100;
var selectedKickSound = typeof storage.selectedKickSound === "string" ? storage.selectedKickSound : 'kick';
var selectedHihatSound = typeof storage.selectedHihatSound === "string" ? storage.selectedHihatSound : 'hihat';
var selectedSnareClapSound = typeof storage.selectedSnareClapSound === "string" ? storage.selectedSnareClapSound : 'snareclap';
var selectedPercussionSound = typeof storage.selectedPercussionSound === "string" ? storage.selectedPercussionSound : 'percussion';
// Function to update all sound volumes based on soundVolume setting
function updateAllSoundVolumes() {
	var volumeMultiplier = soundVolume / 100;
	// Update kick sound volume
	var kickSound = LK.getSound(selectedKickSound);
	if (kickSound && kickSound.setVolume) {
		kickSound.setVolume(1 * volumeMultiplier);
	}
	// Update hihat sound volume
	var hihatSound = LK.getSound(selectedHihatSound);
	if (hihatSound && hihatSound.setVolume) {
		hihatSound.setVolume(0.5 * volumeMultiplier);
	}
	// Update snare/clap sound volume
	var snareClapSound = LK.getSound(selectedSnareClapSound);
	if (snareClapSound && snareClapSound.setVolume) {
		snareClapSound.setVolume(0.6 * volumeMultiplier);
	}
	// Update percussion sound volume
	var percussionSound = LK.getSound(selectedPercussionSound);
	if (percussionSound && percussionSound.setVolume) {
		percussionSound.setVolume(0.6 * volumeMultiplier);
	}
	// Update miss sound volume
	var missSound = LK.getSound('miss');
	if (missSound && missSound.setVolume) {
		missSound.setVolume(0.6 * volumeMultiplier);
	}
}
var beatInterval = 60 / bpm;
var gameActive = true;
var feedbackTimeout = null;
// --- UI elements ---
// --- Heart/Life UI ---
// (Initialization moved after scoreTxt is defined)
var MAX_LIVES = 3;
var lives = MAX_LIVES;
var heartIcons = [];
var heartSpacing = 40;
var heartshields = 3; // Number of heartshields remaining
// Heart/life UI will be initialized after scoreTxt is defined
// --- Gameplay Screen Container ---
var gamescreen = new Container();
gamescreen.visible = false;
game.addChild(gamescreen);
// Add isyeri background image, anchored top-left, covering the whole game area
var isyeriBg = LK.getAsset('isyeri', {
	anchorX: 0,
	anchorY: 0,
	x: 0,
	y: 0,
	width: 2048,
	height: 2732
});
gamescreen.addChild(isyeriBg);
var timelineBar = LK.getAsset('timelineBar', {
	anchorX: 0,
	anchorY: 0.5,
	x: TIMELINE_X,
	y: TIMELINE_Y + TIMELINE_HEIGHT / 2
});
gamescreen.addChild(timelineBar);
// Hit zone (kick target)
var kickTarget = LK.getAsset('kickTarget', {
	anchorX: 0.5,
	anchorY: 0.5,
	x: HIT_ZONE_X,
	y: HIT_ZONE_Y
});
gamescreen.addChild(kickTarget);
// Score text - pixelated effect using small size and scale
var scoreTxt = new Text2('0', {
	size: 60,
	fill: 0xFFFFFF
});
scoreTxt.scale.set(2, 2); // Scale up 2x to create more pixelated effect
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Combo text
var comboTxt = new Text2('', {
	size: 36,
	fill: 0x00FF00
});
comboTxt.scale.set(2, 2);
comboTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(comboTxt);
comboTxt.y = 120;
// BPM display text - 135px below combo text (moved 30px down)
var bpmDisplayTxt = new Text2(bpm + ' ' + 'BPM', {
	size: 36,
	fill: 0xef69b9
});
bpmDisplayTxt.scale.set(2, 2);
bpmDisplayTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(bpmDisplayTxt);
bpmDisplayTxt.y = comboTxt.y + 135;
// Song name display text - below BPM display
var songNameTxt = new Text2('', {
	size: 36,
	fill: 0xffffff
});
songNameTxt.scale.set(2, 2);
songNameTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(songNameTxt);
songNameTxt.y = bpmDisplayTxt.y + 82;
// Pause button in top right corner (avoiding top-left platform menu)
var pauseBtn = LK.getAsset('pause', {
	anchorX: 1,
	anchorY: 0,
	x: 2048 - 50,
	y: 220,
	scaleX: 1.6,
	scaleY: 1.6
});
gamescreen.addChild(pauseBtn);
// No miss text, only hearts for lives display
// --- Heart/Life UI --- (moved here to ensure scoreTxt is defined)
// Calculate heartY based on scoreTxt position and height
var heartY = scoreTxt.y + 50; // Score text'in 50px altında
var heartWidth = 150;
var totalHeartsWidth = MAX_LIVES * heartWidth + (MAX_LIVES - 1) * heartSpacing;
var heartStartX = 2048 - 100 - totalHeartsWidth; // Sağdan 100px boşluk bırak
// Remove any existing heart icons from their parents before recreating
for (var i = 0; i < heartIcons.length; i++) {
	if (heartIcons[i] && heartIcons[i].parent) {
		heartIcons[i].parent.removeChild(heartIcons[i]);
	}
}
heartIcons = [];
for (var i = 0; i < MAX_LIVES; i++) {
	var heartshield = LK.getAsset('heartshield', {
		anchorX: 0,
		anchorY: 0,
		x: heartStartX + i * (heartWidth + heartSpacing),
		y: heartY
	});
	gamescreen.addChild(heartshield);
	heartIcons.push(heartshield);
}
// --- Beat pattern generation ---
function generateBeatPattern(bpm, measures) {
	// Simple: 4/4, kick on every beat, can add more complex patterns later
	var pattern = [];
	var interval = 60 / bpm; // seconds per beat based on current BPM
	var totalBeats = 100000; // Target 100,000 total beats
	for (var beatIndex = 0; beatIndex < totalBeats; beatIndex++) {
		pattern.push({
			time: beatIndex * interval,
			// Each beat spaced by BPM interval
			type: 'kick'
		});
	}
	return pattern;
}
// --- Start game ---
function startGame(options) {
	options = options || {};
	var mode = options.mode || "story";
	// Reset state
	for (var i = 0; i < beats.length; i++) {
		beats[i].destroy();
	}
	beats = [];
	if (mode === "story") {
		// Story modunda tutorial kontrolü
		gameMode = "story";
		var chapterIdx = typeof currentLevel === 'number' ? currentLevel - 1 : 0;
		var chapter = STORY_CHAPTERS[chapterIdx] || STORY_CHAPTERS[0];
		isTutorial = chapterIdx === 0 && !tutorialCompleted;
		bpm = chapter.bpm;
		var storyMusicId = chapter.musicId;
		var storySongName = chapter.songName;
		beatInterval = 60 / bpm;
		beatPattern = generateBeatPattern(bpm);
		nextBeatIdx = 0;
		songTime = 0;
		lastTickTime = Date.now();
		score = 0;
		misses = 0;
		ignoredMisses = 0;
		combo = 0;
		perfectCombo = 0;
		lives = MAX_LIVES;
		heartshields = 3;
		lastBpmUpgradeScore = 0;
		// Remove any beat at the kick target at game start
		for (var i = beats.length - 1; i >= 0; i--) {
			if (typeof beats[i].x !== "undefined" && Math.abs(beats[i].x - HIT_ZONE_X) < 2 || typeof beats[i].x !== "undefined" && beats[i].x >= HIT_ZONE_X - HIT_WINDOW) {
				beats[i].destroy();
				beats.splice(i, 1);
			}
		}
		if (beats.length > 0 && typeof beats[0].x !== "undefined" && Math.abs(beats[0].x - HIT_ZONE_X) < 2) {
			beats[0].destroy();
			beats.splice(0, 1);
		}
		updateHeartsUI(lives, heartshields);
		var heartWidth = 150;
		var totalHeartsWidth = MAX_LIVES * heartWidth + (MAX_LIVES - 1) * heartSpacing;
		var heartStartX = 2048 - 100 - totalHeartsWidth;
		var heartY = scoreTxt.y + 50;
		for (var i = 0; i < heartIcons.length; i++) {
			if (heartIcons[i] && heartIcons[i].parent) {
				heartIcons[i].parent.removeChild(heartIcons[i]);
			}
		}
		heartIcons = [];
		heartshields = MAX_LIVES;
		lives = MAX_LIVES;
		for (var i = 0; i < MAX_LIVES; i++) {
			var heartAsset = LK.getAsset('heartshield', {
				anchorX: 0,
				anchorY: 0,
				x: heartStartX + i * (heartWidth + heartSpacing),
				y: heartY
			});
			gamescreen.addChild(heartAsset);
			heartIcons.push(heartAsset);
		}
		gameActive = true;
		gamePaused = false;
		scoreTxt.setText('0');
		comboTxt.setText('');
		kickTarget.visible = true;
		LK.setScore(0);
		applyGlowToVioletElements();
		updateAllSoundVolumes();
		tween({}, {}, {
			duration: 3000,
			onFinish: function onFinish() {
				if (gameActive && heartshields > 0) {
					heartshields = 0;
					updateHeartsUI(lives, heartshields);
				}
			}
		});
		// --- Story modunda sabit müzik ve şarkı adı ---
		var musicId = storyMusicId;
		var volumeMultiplier = soundVolume / 100;
		var musicVolume = isTutorial ? 0.5 : 1;
		LK.playMusic(musicId, {
			volume: musicVolume * volumeMultiplier,
			loop: true
		});
		if (bpmDisplayTxt) {
			bpmDisplayTxt.setText(bpm + ' BPM');
		}
		if (songNameTxt) {
			songNameTxt.setText(storySongName);
		}
		// UI gösterimi
		scoreTxt.visible = true;
		comboTxt.visible = true;
		bpmDisplayTxt.visible = true;
		songNameTxt.visible = true;
		gamescreen.visible = true;
		mainMenuContainer.visible = false;
		updateDebugText();
		return;
	} else if (mode === "endless") {
		gameMode = "endless";
		isTutorial = false;
		bpm = typeof storage.bpm === "number" ? storage.bpm : INITIAL_BPM;
		beatInterval = 60 / bpm;
		beatPattern = generateBeatPattern(bpm);
		nextBeatIdx = 0;
		songTime = 0;
		lastTickTime = Date.now();
		score = 0;
		misses = 0;
		ignoredMisses = 0;
		combo = 0;
		perfectCombo = 0;
		lives = MAX_LIVES;
		heartshields = 3;
		lastBpmUpgradeScore = 0;
		// Remove any beat at the kick target at game start
		for (var i = beats.length - 1; i >= 0; i--) {
			if (typeof beats[i].x !== "undefined" && Math.abs(beats[i].x - HIT_ZONE_X) < 2 || typeof beats[i].x !== "undefined" && beats[i].x >= HIT_ZONE_X - HIT_WINDOW) {
				beats[i].destroy();
				beats.splice(i, 1);
			}
		}
		if (beats.length > 0 && typeof beats[0].x !== "undefined" && Math.abs(beats[0].x - HIT_ZONE_X) < 2) {
			beats[0].destroy();
			beats.splice(0, 1);
		}
		updateHeartsUI(lives, heartshields);
		var heartWidth = 150;
		var totalHeartsWidth = MAX_LIVES * heartWidth + (MAX_LIVES - 1) * heartSpacing;
		var heartStartX = 2048 - 100 - totalHeartsWidth;
		var heartY = scoreTxt.y + 50;
		for (var i = 0; i < heartIcons.length; i++) {
			if (heartIcons[i] && heartIcons[i].parent) {
				heartIcons[i].parent.removeChild(heartIcons[i]);
			}
		}
		heartIcons = [];
		heartshields = MAX_LIVES;
		lives = MAX_LIVES;
		for (var i = 0; i < MAX_LIVES; i++) {
			var heartAsset = LK.getAsset('heartshield', {
				anchorX: 0,
				anchorY: 0,
				x: heartStartX + i * (heartWidth + heartSpacing),
				y: heartY
			});
			gamescreen.addChild(heartAsset);
			heartIcons.push(heartAsset);
		}
		gameActive = true;
		gamePaused = false;
		scoreTxt.setText('0');
		comboTxt.setText('');
		kickTarget.visible = true;
		LK.setScore(0);
		applyGlowToVioletElements();
		updateAllSoundVolumes();
		tween({}, {}, {
			duration: 3000,
			onFinish: function onFinish() {
				if (gameActive && heartshields > 0) {
					heartshields = 0;
					updateHeartsUI(lives, heartshields);
				}
			}
		});
		// --- Play correct BPM music (kickless) ---
		var musicId = '100BPMSynthWave';
		if (bpm === 60) {
			musicId = '60BPMLowKey';
		} else if (bpm === 40) {
			musicId = '40BPMDeepVibes';
		} else if (bpm === 45) {
			musicId = '45BPMChillWave';
		} else if (bpm === 50) {
			musicId = '50BPMSlowMotion';
		} else if (bpm === 55) {
			musicId = '55BPMDreamscape';
		} else if (bpm === 65) {
			musicId = '65BPMRelaxed';
		} else if (bpm === 70) {
			musicId = '70BPMCalmVibes';
		} else if (bpm === 75) {
			musicId = '75BPMElectricDreams';
		} else if (bpm === 80) {
			musicId = '80BPMNeonNights';
		} else if (bpm === 85) {
			musicId = '85BPMCyberFlow';
		} else if (bpm === 90) {
			musicId = '90BPMDigitalPulse';
		} else if (bpm === 95) {
			musicId = '95BPMFutureBass';
		} else if (bpm === 100) {
			musicId = '100BPMSynthWave';
		} else if (bpm === 105) {
			musicId = '105BPMHyperDrive';
		} else if (bpm === 110) {
			musicId = '110BPMTechnoRush';
		} else if (bpm === 115) {
			musicId = '115BPMHighEnergy';
		} else if (bpm === 120) {
			musicId = '120BPMMaximum';
		}
		var volumeMultiplier = soundVolume / 100;
		var musicVolume = 1;
		LK.playMusic(musicId, {
			volume: musicVolume * volumeMultiplier,
			loop: true
		});
		if (bpmDisplayTxt) {
			bpmDisplayTxt.setText(bpm + ' BPM');
		}
		if (songNameTxt) {
			var songName = musicId.replace(/^\d+BPM/, '');
			songNameTxt.setText(songName);
		}
		// --- BPM'e göre waveform'u güncelle ---
		drawWaveformLK();
		// UI gösterimi
		scoreTxt.visible = true;
		comboTxt.visible = true;
		bpmDisplayTxt.visible = true;
		songNameTxt.visible = true;
		gamescreen.visible = true;
		mainMenuContainer.visible = false;
		updateDebugText();
		return;
	}
	beatInterval = 60 / bpm;
	beatPattern = generateBeatPattern(bpm);
	nextBeatIdx = 0;
	songTime = 0;
	lastTickTime = Date.now();
	score = 0;
	misses = 0;
	ignoredMisses = 0;
	combo = 0;
	perfectCombo = 0;
	lives = MAX_LIVES;
	heartshields = 3;
	lastBpmUpgradeScore = 0;
	// Remove any beat at the kick target at game start
	for (var i = beats.length - 1; i >= 0; i--) {
		if (typeof beats[i].x !== "undefined" && Math.abs(beats[i].x - HIT_ZONE_X) < 2 || typeof beats[i].x !== "undefined" && beats[i].x >= HIT_ZONE_X - HIT_WINDOW) {
			beats[i].destroy();
			beats.splice(i, 1);
		}
	}
	if (beats.length > 0 && typeof beats[0].x !== "undefined" && Math.abs(beats[0].x - HIT_ZONE_X) < 2) {
		beats[0].destroy();
		beats.splice(0, 1);
	}
	updateHeartsUI(lives, heartshields);
	var heartWidth = 150;
	var totalHeartsWidth = MAX_LIVES * heartWidth + (MAX_LIVES - 1) * heartSpacing;
	var heartStartX = 2048 - 100 - totalHeartsWidth;
	var heartY = scoreTxt.y + 50;
	for (var i = 0; i < heartIcons.length; i++) {
		if (heartIcons[i] && heartIcons[i].parent) {
			heartIcons[i].parent.removeChild(heartIcons[i]);
		}
	}
	heartIcons = [];
	heartshields = MAX_LIVES;
	lives = MAX_LIVES;
	for (var i = 0; i < MAX_LIVES; i++) {
		var heartAsset = LK.getAsset('heartshield', {
			anchorX: 0,
			anchorY: 0,
			x: heartStartX + i * (heartWidth + heartSpacing),
			y: heartY
		});
		gamescreen.addChild(heartAsset);
		heartIcons.push(heartAsset);
	}
	gameActive = true;
	gamePaused = false;
	scoreTxt.setText('0');
	comboTxt.setText('');
	kickTarget.visible = true;
	LK.setScore(0);
	applyGlowToVioletElements();
	updateAllSoundVolumes();
	tween({}, {}, {
		duration: 3000,
		onFinish: function onFinish() {
			if (gameActive && heartshields > 0) {
				heartshields = 0;
				updateHeartsUI(lives, heartshields);
			}
		}
	});
	// --- Play correct BPM music (kickless) ---
	var musicId = '100BPMSynthWave';
	if (bpm === 60) {
		musicId = '60BPMLowKey';
	} else if (bpm === 40) {
		musicId = '40BPMDeepVibes';
	} else if (bpm === 45) {
		musicId = '45BPMChillWave';
	} else if (bpm === 50) {
		musicId = '50BPMSlowMotion';
	} else if (bpm === 55) {
		musicId = '55BPMDreamscape';
	} else if (bpm === 65) {
		musicId = '65BPMRelaxed';
	} else if (bpm === 70) {
		musicId = '70BPMCalmVibes';
	} else if (bpm === 75) {
		musicId = '75BPMElectricDreams';
	} else if (bpm === 80) {
		musicId = '80BPMNeonNights';
	} else if (bpm === 85) {
		musicId = '85BPMCyberFlow';
	} else if (bpm === 90) {
		musicId = '90BPMDigitalPulse';
	} else if (bpm === 95) {
		musicId = '95BPMFutureBass';
	} else if (bpm === 100) {
		musicId = '100BPMSynthWave';
	} else if (bpm === 105) {
		musicId = '105BPMHyperDrive';
	} else if (bpm === 110) {
		musicId = '110BPMTechnoRush';
	} else if (bpm === 115) {
		musicId = '115BPMHighEnergy';
	} else if (bpm === 120) {
		musicId = '120BPMMaximum';
	}
	var volumeMultiplier = soundVolume / 100;
	var musicVolume = 1;
	LK.playMusic(musicId, {
		volume: musicVolume * volumeMultiplier,
		loop: true
	});
	if (bpmDisplayTxt) {
		bpmDisplayTxt.setText(bpm + ' BPM');
	}
	if (songNameTxt) {
		var songName = musicId.replace(/^\d+BPM/, '');
		songNameTxt.setText(songName);
	}
	// --- BPM'e göre waveform'u güncelle ---
	drawWaveformLK();
	// UI gösterimi
	scoreTxt.visible = true;
	comboTxt.visible = true;
	bpmDisplayTxt.visible = true;
	songNameTxt.visible = true;
	gamescreen.visible = true;
	mainMenuContainer.visible = false;
}
// --- Main Menu Implementation ---
// Add main menu background image, anchored top-left, covering the whole game area
var mainMenuContainer = new Container();
mainMenuContainer.visible = true;
var mainMenuBg = LK.getAsset('mainmanubg', {
	anchorX: 0,
	anchorY: 0,
	x: 0,
	y: 0,
	width: 2048,
	height: 2732
});
mainMenuContainer.addChild(mainMenuBg);
// Title - DJ Asset
var djAsset = LK.getAsset('DJ', {
	anchorX: 0.5,
	anchorY: 0.5,
	x: 2048 / 2,
	y: 315,
	scaleX: 0.2,
	scaleY: 0.2
});
mainMenuContainer.addChild(djAsset);
// Calculate button positions with 5px spacing
var buttonStartY = 1000;
var buttonHeight = 289; // Approximate height with scale 5
var buttonSpacing = 5;
// Start Button
var startBtn = LK.getAsset('startbutton', {
	anchorX: 0.5,
	anchorY: 0.5,
	x: 2048 / 2,
	y: buttonStartY,
	scaleX: 0.18,
	scaleY: 0.18
});
mainMenuContainer.addChild(startBtn);
// Settings Button
var settingsBtn = LK.getAsset('settingsbutton', {
	anchorX: 0.5,
	anchorY: 0.5,
	x: 2048 / 2,
	y: buttonStartY + 4 * (buttonHeight + buttonSpacing),
	scaleX: 0.18,
	scaleY: 0.18
});
mainMenuContainer.addChild(settingsBtn);
// Credits Button
var creditsBtn = LK.getAsset('creditsbutton', {
	anchorX: 0.5,
	anchorY: 0.5,
	x: 2048 / 2,
	y: buttonStartY + 2 * (buttonHeight + buttonSpacing),
	scaleX: 0.18,
	scaleY: 0.18
});
mainMenuContainer.addChild(creditsBtn);
// Support Us Button
var supportBtn = LK.getAsset('supportbutton', {
	anchorX: 0.5,
	anchorY: 0.5,
	x: 2048 / 2,
	y: buttonStartY + 3 * (buttonHeight + buttonSpacing),
	scaleX: 0.18,
	scaleY: 0.18
});
mainMenuContainer.addChild(supportBtn);
// Overlay for credits/settings/support
var settingsmenu = new Container();
settingsmenu.visible = false;
game.addChild(settingsmenu);
var creditsmenu = new Container();
creditsmenu.visible = false;
game.addChild(creditsmenu);
var supportmenu = new Container();
supportmenu.visible = false;
game.addChild(supportmenu);
// Helper to hide all overlays
function hideAllMenus() {
	settingsmenu.visible = false;
	creditsmenu.visible = false;
	supportmenu.visible = false;
}
// Show credits menu
function showCreditsMenu() {
	hideAllMenus();
	creditsmenu.removeChildren();
	var bg = LK.getAsset('creditsbg', {
		anchorX: 0,
		anchorY: 0,
		x: 0,
		y: 0,
		width: 2048,
		height: 2732
	});
	creditsmenu.addChild(bg);
	var creditsTab = LK.getAsset('creditstab', {
		anchorX: 0.5,
		anchorY: 0.5,
		x: 2048 / 2,
		y: 2732 / 2,
		scaleX: 0.5,
		scaleY: 0.5
	});
	creditsmenu.addChild(creditsTab);
	// Close button (bottom right)
	var closeBtn = LK.getAsset('closebutton', {
		anchorX: 1,
		anchorY: 1,
		x: 2048 - 60,
		y: 2732 - 60,
		scaleX: 3,
		scaleY: 3
	});
	creditsmenu.addChild(closeBtn);
	closeBtn.down = function (x, y, obj) {
		// Play button click sound
		LK.getSound('buttonclick').play();
		// Click animation - scale inward then outward
		tween(closeBtn, {
			scaleX: 0.9,
			scaleY: 0.9
		}, {
			duration: 80,
			easing: tween.easeIn,
			onFinish: function onFinish() {
				tween(closeBtn, {
					scaleX: 1,
					scaleY: 1
				}, {
					duration: 120,
					easing: tween.easeOut
				});
			}
		});
		creditsmenu.visible = false;
		mainMenuContainer.visible = true;
	};
	creditsmenu.visible = true;
	// Remove tap-to-close on background
	creditsmenu.down = undefined;
}
// Show support menu
function showSupportMenu() {
	hideAllMenus();
	supportmenu.removeChildren();
	var bg = LK.getAsset('supportbg', {
		anchorX: 0,
		anchorY: 0,
		x: 0,
		y: 0,
		width: 2048,
		height: 2732
	});
	supportmenu.addChild(bg);
	var supportTab = LK.getAsset('supporttab', {
		anchorX: 0.5,
		anchorY: 0.5,
		x: 2048 / 2,
		y: 2732 / 2,
		scaleX: 0.5,
		scaleY: 0.5
	});
	supportmenu.addChild(supportTab);
	// Close button (bottom right)
	var closeBtn = LK.getAsset('closebutton', {
		anchorX: 1,
		anchorY: 1,
		x: 2048 - 60,
		y: 2732 - 60,
		scaleX: 3,
		scaleY: 3
	});
	supportmenu.addChild(closeBtn);
	closeBtn.down = function (x, y, obj) {
		// Play button click sound
		LK.getSound('buttonclick').play();
		// Click animation - scale inward then outward
		tween(closeBtn, {
			scaleX: 0.9,
			scaleY: 0.9
		}, {
			duration: 80,
			easing: tween.easeIn,
			onFinish: function onFinish() {
				tween(closeBtn, {
					scaleX: 1,
					scaleY: 1
				}, {
					duration: 120,
					easing: tween.easeOut
				});
			}
		});
		supportmenu.visible = false;
		mainMenuContainer.visible = true;
	};
	supportmenu.visible = true;
	// Remove tap-to-close on background
	supportmenu.down = undefined;
	// Play thanks sound after 2 seconds
	LK.setTimeout(function () {
		LK.getSound('thanks').play();
	}, 2000);
}
// Show settings menu
function showSettingsMenu() {
	hideAllMenus();
	settingsmenu.removeChildren();
	var bg = LK.getAsset('settingsbg', {
		anchorX: 0,
		anchorY: 0,
		x: 0,
		y: 0,
		width: 2048,
		height: 2732
	});
	settingsmenu.addChild(bg);
	var settingsAsset = LK.getAsset('settingsbutton', {
		anchorX: 0.5,
		anchorY: 0.5,
		x: 2048 / 2,
		y: 700,
		scaleX: 0.3,
		scaleY: 0.3
	});
	settingsmenu.addChild(settingsAsset);
	// --- BPM CIRCLE UI ---
	// Container for the whole BPM control
	var bpmCircleContainer = new Container();
	bpmCircleContainer.x = 2048 / 2 - 300;
	bpmCircleContainer.y = 1100;
	settingsmenu.addChild(bpmCircleContainer);
	// --- KICK SOUND SELECTION UI ---
	// Container for kick sound selection
	var kickSoundContainer = new Container();
	kickSoundContainer.x = 2048 / 2;
	kickSoundContainer.y = 1400;
	settingsmenu.addChild(kickSoundContainer);
	// Kick sound label
	var kickSoundLabelTxt = new Text2("KICK SOUND", {
		size: 24,
		fill: 0xffffff
	});
	kickSoundLabelTxt.scale.set(2, 2);
	kickSoundLabelTxt.anchor.set(0.5, 0.5);
	kickSoundLabelTxt.x = 0;
	kickSoundLabelTxt.y = -120;
	kickSoundContainer.addChild(kickSoundLabelTxt);
	// Add settingsbar background behind kick buttons
	var kickSettingsBar = LK.getAsset('settingsbar', {
		anchorX: 0.5,
		anchorY: 0.5,
		x: 0,
		y: 0,
		scaleX: 1.2,
		scaleY: 1.2,
		alpha: 0.8
	});
	kickSoundContainer.addChild(kickSettingsBar);
	// Create kick sound buttons
	var kickSounds = ['kick', 'kick2', 'kick3', 'kick4'];
	var kickButtons = [];
	var buttonSpacing = 220;
	var startX = -330;
	for (var i = 0; i < kickSounds.length; i++) {
		var kickButton = LK.getAsset('purplecircle', {
			anchorX: 0.5,
			anchorY: 0.5,
			x: startX + i * buttonSpacing,
			y: 0,
			scaleX: 0.8,
			scaleY: 0.8,
			alpha: selectedKickSound === kickSounds[i] ? 1 : 0.5
		});
		kickSoundContainer.addChild(kickButton);
		// Add number text to button
		var numberTxt = new Text2(i + 1 + "", {
			size: 36,
			fill: 0xffffff
		});
		numberTxt.scale.set(2, 2);
		numberTxt.anchor.set(0.5, 0.5);
		numberTxt.x = startX + i * buttonSpacing;
		numberTxt.y = 0;
		kickSoundContainer.addChild(numberTxt);
		kickButton.kickSoundId = kickSounds[i];
		kickButton.buttonIndex = i;
		kickButtons.push(kickButton);
		// Button click handler
		kickButton.down = function (x, y, obj) {
			// If this is already selected kick sound and any other sound is selected, deselect all kick sounds
			if (selectedKickSound === this.kickSoundId && (selectedHihatSound !== '' || selectedSnareClapSound !== '' || selectedPercussionSound !== '')) {
				// Play the selected kick sound
				LK.getSound(this.kickSoundId).play();
				// Deselect all kick sounds
				selectedKickSound = '';
				storage.selectedKickSound = selectedKickSound;
				// Update button appearances - all kick buttons become inactive
				for (var j = 0; j < kickButtons.length; j++) {
					kickButtons[j].alpha = 0.5;
				}
				// Animate button
				tween(this, {
					scaleX: 0.9,
					scaleY: 0.9
				}, {
					duration: 80,
					easing: tween.easeIn,
					onFinish: function () {
						tween(this, {
							scaleX: 0.8,
							scaleY: 0.8
						}, {
							duration: 120,
							easing: tween.easeOut
						});
					}.bind(this)
				});
				return;
			}
			// If this is already selected and it's the only one selected, don't allow deselection
			if (selectedKickSound === this.kickSoundId && selectedHihatSound === '' && selectedSnareClapSound === '' && selectedPercussionSound === '') {
				// Play the selected kick sound
				LK.getSound(this.kickSoundId).play();
				return; // Don't deselect if it would leave no sound selected
			}
			// Play the selected kick sound
			LK.getSound(this.kickSoundId).play();
			// Update selected kick sound
			selectedKickSound = this.kickSoundId;
			storage.selectedKickSound = selectedKickSound;
			// Update button appearances
			for (var j = 0; j < kickButtons.length; j++) {
				kickButtons[j].alpha = j === this.buttonIndex ? 1 : 0.5;
			}
			// Animate button
			tween(this, {
				scaleX: 0.9,
				scaleY: 0.9
			}, {
				duration: 80,
				easing: tween.easeIn,
				onFinish: function () {
					tween(this, {
						scaleX: 0.8,
						scaleY: 0.8
					}, {
						duration: 120,
						easing: tween.easeOut
					});
				}.bind(this)
			});
		}.bind(kickButton);
	}
	// --- HIHAT SOUND SELECTION UI ---
	// Container for hihat sound selection
	var hihatSoundContainer = new Container();
	hihatSoundContainer.x = 2048 / 2;
	hihatSoundContainer.y = 1700;
	settingsmenu.addChild(hihatSoundContainer);
	// Hihat sound label
	var hihatSoundLabelTxt = new Text2("HI HATS", {
		size: 24,
		fill: 0xffffff
	});
	hihatSoundLabelTxt.scale.set(2, 2);
	hihatSoundLabelTxt.anchor.set(0.5, 0.5);
	hihatSoundLabelTxt.x = 0;
	hihatSoundLabelTxt.y = -120;
	hihatSoundContainer.addChild(hihatSoundLabelTxt);
	// Add settingsbar background behind hihat buttons
	var hihatSettingsBar = LK.getAsset('settingsbar', {
		anchorX: 0.5,
		anchorY: 0.5,
		x: 0,
		y: 0,
		scaleX: 1.2,
		scaleY: 1.2,
		alpha: 0.8
	});
	hihatSoundContainer.addChild(hihatSettingsBar);
	// Create hihat sound buttons
	var hihatSounds = ['hihat', 'hihat2', 'hihat3', 'hihat4'];
	var hihatButtons = [];
	var hihatButtonSpacing = 220;
	var hihatStartX = -330;
	for (var i = 0; i < hihatSounds.length; i++) {
		var hihatButton = LK.getAsset('purplecircle', {
			anchorX: 0.5,
			anchorY: 0.5,
			x: hihatStartX + i * hihatButtonSpacing,
			y: 0,
			scaleX: 0.8,
			scaleY: 0.8,
			alpha: selectedHihatSound === hihatSounds[i] ? 1 : 0.5
		});
		hihatSoundContainer.addChild(hihatButton);
		// Add number text to button
		var numberTxt = new Text2(i + 1 + "", {
			size: 36,
			fill: 0xffffff
		});
		numberTxt.scale.set(2, 2);
		numberTxt.anchor.set(0.5, 0.5);
		numberTxt.x = hihatStartX + i * hihatButtonSpacing;
		numberTxt.y = 0;
		hihatSoundContainer.addChild(numberTxt);
		hihatButton.hihatSoundId = hihatSounds[i];
		hihatButton.buttonIndex = i;
		hihatButtons.push(hihatButton);
		// Button click handler
		hihatButton.down = function (x, y, obj) {
			// If this is already selected hihat sound and any other sound is selected, deselect all hihat sounds
			if (selectedHihatSound === this.hihatSoundId && (selectedKickSound !== '' || selectedSnareClapSound !== '' || selectedPercussionSound !== '')) {
				// Play the selected hihat sound
				LK.getSound(this.hihatSoundId).play();
				// Deselect all hihat sounds
				selectedHihatSound = '';
				storage.selectedHihatSound = selectedHihatSound;
				// Update button appearances - all hihat buttons become inactive
				for (var j = 0; j < hihatButtons.length; j++) {
					hihatButtons[j].alpha = 0.5;
				}
				// Animate button
				tween(this, {
					scaleX: 0.9,
					scaleY: 0.9
				}, {
					duration: 80,
					easing: tween.easeIn,
					onFinish: function () {
						tween(this, {
							scaleX: 0.8,
							scaleY: 0.8
						}, {
							duration: 120,
							easing: tween.easeOut
						});
					}.bind(this)
				});
				return;
			}
			// If this is already selected and it's the only one selected, don't allow deselection
			if (selectedHihatSound === this.hihatSoundId && selectedKickSound === '' && selectedSnareClapSound === '' && selectedPercussionSound === '') {
				// Play the selected hihat sound
				LK.getSound(this.hihatSoundId).play();
				return; // Don't deselect if it would leave no sound selected
			}
			// Play the selected hihat sound
			LK.getSound(this.hihatSoundId).play();
			// Update selected hihat sound
			selectedHihatSound = this.hihatSoundId;
			storage.selectedHihatSound = selectedHihatSound;
			// Update button appearances
			for (var j = 0; j < hihatButtons.length; j++) {
				hihatButtons[j].alpha = j === this.buttonIndex ? 1 : 0.5;
			}
			// Animate button
			tween(this, {
				scaleX: 0.9,
				scaleY: 0.9
			}, {
				duration: 80,
				easing: tween.easeIn,
				onFinish: function () {
					tween(this, {
						scaleX: 0.8,
						scaleY: 0.8
					}, {
						duration: 120,
						easing: tween.easeOut
					});
				}.bind(this)
			});
		}.bind(hihatButton);
	}
	// --- SNARE/CLAP SOUND SELECTION UI ---
	// Container for snare/clap sound selection
	var snareClapSoundContainer = new Container();
	snareClapSoundContainer.x = 2048 / 2;
	snareClapSoundContainer.y = 2000;
	settingsmenu.addChild(snareClapSoundContainer);
	// Snare/clap sound label
	var snareClapSoundLabelTxt = new Text2("SNARE/CLAP", {
		size: 24,
		fill: 0xffffff
	});
	snareClapSoundLabelTxt.scale.set(2, 2);
	snareClapSoundLabelTxt.anchor.set(0.5, 0.5);
	snareClapSoundLabelTxt.x = 0;
	snareClapSoundLabelTxt.y = -120;
	snareClapSoundContainer.addChild(snareClapSoundLabelTxt);
	// Add settingsbar background behind snare/clap buttons
	var snareClapSettingsBar = LK.getAsset('settingsbar', {
		anchorX: 0.5,
		anchorY: 0.5,
		x: 0,
		y: 0,
		scaleX: 1.2,
		scaleY: 1.2,
		alpha: 0.8
	});
	snareClapSoundContainer.addChild(snareClapSettingsBar);
	// Create snare/clap sound buttons
	var snareClapSounds = ['snareclap', 'snareclap2', 'snareclap3', 'snareclap4'];
	var snareClapButtons = [];
	var snareClapButtonSpacing = 220;
	var snareClapStartX = -330;
	for (var i = 0; i < snareClapSounds.length; i++) {
		var snareClapButton = LK.getAsset('purplecircle', {
			anchorX: 0.5,
			anchorY: 0.5,
			x: snareClapStartX + i * snareClapButtonSpacing,
			y: 0,
			scaleX: 0.8,
			scaleY: 0.8,
			alpha: selectedSnareClapSound === snareClapSounds[i] ? 1 : 0.5
		});
		snareClapSoundContainer.addChild(snareClapButton);
		// Add number text to button
		var numberTxt = new Text2(i + 1 + "", {
			size: 36,
			fill: 0xffffff
		});
		numberTxt.scale.set(2, 2);
		numberTxt.anchor.set(0.5, 0.5);
		numberTxt.x = snareClapStartX + i * snareClapButtonSpacing;
		numberTxt.y = 0;
		snareClapSoundContainer.addChild(numberTxt);
		snareClapButton.snareClapSoundId = snareClapSounds[i];
		snareClapButton.buttonIndex = i;
		snareClapButtons.push(snareClapButton);
		// Button click handler
		snareClapButton.down = function (x, y, obj) {
			// If this is already selected snare/clap sound and any other sound is selected, deselect all snare/clap sounds
			if (selectedSnareClapSound === this.snareClapSoundId && (selectedKickSound !== '' || selectedHihatSound !== '' || selectedPercussionSound !== '')) {
				// Play the selected snare/clap sound
				LK.getSound(this.snareClapSoundId).play();
				// Deselect all snare/clap sounds
				selectedSnareClapSound = '';
				storage.selectedSnareClapSound = selectedSnareClapSound;
				// Update button appearances - all snare/clap buttons become inactive
				for (var j = 0; j < snareClapButtons.length; j++) {
					snareClapButtons[j].alpha = 0.5;
				}
				// Animate button
				tween(this, {
					scaleX: 0.9,
					scaleY: 0.9
				}, {
					duration: 80,
					easing: tween.easeIn,
					onFinish: function () {
						tween(this, {
							scaleX: 0.8,
							scaleY: 0.8
						}, {
							duration: 120,
							easing: tween.easeOut
						});
					}.bind(this)
				});
				return;
			}
			// If this is already selected and it's the only one selected, don't allow deselection
			if (selectedSnareClapSound === this.snareClapSoundId && selectedKickSound === '' && selectedHihatSound === '' && selectedPercussionSound === '') {
				// Play the selected snare/clap sound
				LK.getSound(this.snareClapSoundId).play();
				return; // Don't deselect if it would leave no sound selected
			}
			// Play the selected snare/clap sound
			LK.getSound(this.snareClapSoundId).play();
			// Update selected snare/clap sound
			selectedSnareClapSound = this.snareClapSoundId;
			storage.selectedSnareClapSound = selectedSnareClapSound;
			// Update button appearances
			for (var j = 0; j < snareClapButtons.length; j++) {
				snareClapButtons[j].alpha = j === this.buttonIndex ? 1 : 0.5;
			}
			// Animate button
			tween(this, {
				scaleX: 0.9,
				scaleY: 0.9
			}, {
				duration: 80,
				easing: tween.easeIn,
				onFinish: function () {
					tween(this, {
						scaleX: 0.8,
						scaleY: 0.8
					}, {
						duration: 120,
						easing: tween.easeOut
					});
				}.bind(this)
			});
		}.bind(snareClapButton);
	}
	// --- PERCUSSION SOUND SELECTION UI ---
	// Container for percussion sound selection
	var percussionSoundContainer = new Container();
	percussionSoundContainer.x = 2048 / 2;
	percussionSoundContainer.y = 2300;
	settingsmenu.addChild(percussionSoundContainer);
	// Percussion sound label
	var percussionSoundLabelTxt = new Text2("PERCUSSION", {
		size: 24,
		fill: 0xffffff
	});
	percussionSoundLabelTxt.scale.set(2, 2);
	percussionSoundLabelTxt.anchor.set(0.5, 0.5);
	percussionSoundLabelTxt.x = 0;
	percussionSoundLabelTxt.y = -120;
	percussionSoundContainer.addChild(percussionSoundLabelTxt);
	// Add settingsbar background behind percussion buttons
	var percussionSettingsBar = LK.getAsset('settingsbar', {
		anchorX: 0.5,
		anchorY: 0.5,
		x: 0,
		y: 0,
		scaleX: 1.2,
		scaleY: 1.2,
		alpha: 0.8
	});
	percussionSoundContainer.addChild(percussionSettingsBar);
	// Create percussion sound buttons
	var percussionSounds = ['percussion', 'percussion2', 'percussion3', 'percussion4'];
	var percussionButtons = [];
	var percussionButtonSpacing = 220;
	var percussionStartX = -330;
	for (var i = 0; i < percussionSounds.length; i++) {
		var percussionButton = LK.getAsset('purplecircle', {
			anchorX: 0.5,
			anchorY: 0.5,
			x: percussionStartX + i * percussionButtonSpacing,
			y: 0,
			scaleX: 0.8,
			scaleY: 0.8,
			alpha: selectedPercussionSound === percussionSounds[i] ? 1 : 0.5
		});
		percussionSoundContainer.addChild(percussionButton);
		// Add number text to button
		var numberTxt = new Text2(i + 1 + "", {
			size: 36,
			fill: 0xffffff
		});
		numberTxt.scale.set(2, 2);
		numberTxt.anchor.set(0.5, 0.5);
		numberTxt.x = percussionStartX + i * percussionButtonSpacing;
		numberTxt.y = 0;
		percussionSoundContainer.addChild(numberTxt);
		percussionButton.percussionSoundId = percussionSounds[i];
		percussionButton.buttonIndex = i;
		percussionButtons.push(percussionButton);
		// Button click handler
		percussionButton.down = function (x, y, obj) {
			// If this is already selected percussion sound and any other sound is selected, deselect all percussion sounds
			if (selectedPercussionSound === this.percussionSoundId && (selectedKickSound !== '' || selectedHihatSound !== '' || selectedSnareClapSound !== '')) {
				// Play the selected percussion sound
				LK.getSound(this.percussionSoundId).play();
				// Deselect all percussion sounds
				selectedPercussionSound = '';
				storage.selectedPercussionSound = selectedPercussionSound;
				// Update button appearances - all percussion buttons become inactive
				for (var j = 0; j < percussionButtons.length; j++) {
					percussionButtons[j].alpha = 0.5;
				}
				// Animate button
				tween(this, {
					scaleX: 0.9,
					scaleY: 0.9
				}, {
					duration: 80,
					easing: tween.easeIn,
					onFinish: function () {
						tween(this, {
							scaleX: 0.8,
							scaleY: 0.8
						}, {
							duration: 120,
							easing: tween.easeOut
						});
					}.bind(this)
				});
				return;
			}
			// If this is already selected and it's the only one selected, don't allow deselection
			if (selectedPercussionSound === this.percussionSoundId && selectedKickSound === '' && selectedHihatSound === '' && selectedSnareClapSound === '') {
				// Play the selected percussion sound
				LK.getSound(this.percussionSoundId).play();
				return; // Don't deselect if it would leave no sound selected
			}
			// Play the selected percussion sound
			LK.getSound(this.percussionSoundId).play();
			// Update selected percussion sound
			selectedPercussionSound = this.percussionSoundId;
			storage.selectedPercussionSound = selectedPercussionSound;
			// Update button appearances
			for (var j = 0; j < percussionButtons.length; j++) {
				percussionButtons[j].alpha = j === this.buttonIndex ? 1 : 0.5;
			}
			// Animate button
			tween(this, {
				scaleX: 0.9,
				scaleY: 0.9
			}, {
				duration: 80,
				easing: tween.easeIn,
				onFinish: function () {
					tween(this, {
						scaleX: 0.8,
						scaleY: 0.8
					}, {
						duration: 120,
						easing: tween.easeOut
					});
				}.bind(this)
			});
		}.bind(percussionButton);
	}
	// --- SOUND VOLUME CIRCLE UI ---
	// Container for the whole sound volume control
	var volumeCircleContainer = new Container();
	volumeCircleContainer.x = 2048 / 2 + 300;
	volumeCircleContainer.y = 1100;
	settingsmenu.addChild(volumeCircleContainer);
	// Circle background
	var bpmCircleBg = LK.getAsset('purplecircle', {
		anchorX: 0.5,
		anchorY: 0.5,
		x: 0,
		y: 0,
		scaleX: 1.1,
		scaleY: 1.1,
		alpha: 0.85
	});
	bpmCircleContainer.addChild(bpmCircleBg);
	// BPM value text
	var bpmValueTxt = new Text2(bpm + "", {
		size: 54,
		fill: 0xffffff
	});
	bpmValueTxt.scale.set(2, 2);
	bpmValueTxt.anchor.set(0.5, 0.5);
	bpmValueTxt.x = 0;
	bpmValueTxt.y = 0;
	bpmCircleContainer.addChild(bpmValueTxt);
	// BPM label text
	var bpmLabelTxt = new Text2("BPM", {
		size: 24,
		fill: 0xffffff
	});
	bpmLabelTxt.scale.set(2, 2);
	bpmLabelTxt.anchor.set(0.5, 0.5);
	bpmLabelTxt.x = 0;
	bpmLabelTxt.y = 90;
	bpmCircleContainer.addChild(bpmLabelTxt);
	// Volume circle background
	var volumeCircleBg = LK.getAsset('purplecircle', {
		anchorX: 0.5,
		anchorY: 0.5,
		x: 0,
		y: 0,
		scaleX: 1.1,
		scaleY: 1.1,
		alpha: 0.85
	});
	volumeCircleContainer.addChild(volumeCircleBg);
	// Volume value text
	var volumeValueTxt = new Text2((typeof storage.soundVolume === "number" ? storage.soundVolume : 100) + "", {
		size: 54,
		fill: 0xffffff
	});
	volumeValueTxt.scale.set(2, 2);
	volumeValueTxt.anchor.set(0.5, 0.5);
	volumeValueTxt.x = 0;
	volumeValueTxt.y = 0;
	volumeCircleContainer.addChild(volumeValueTxt);
	// Volume label text
	var volumeLabelTxt = new Text2("SOUND", {
		size: 24,
		fill: 0xffffff
	});
	volumeLabelTxt.scale.set(2, 2);
	volumeLabelTxt.anchor.set(0.5, 0.5);
	volumeLabelTxt.x = 0;
	volumeLabelTxt.y = 90;
	volumeCircleContainer.addChild(volumeLabelTxt);
	// --- Drag-to-change BPM logic ---
	var dragActive = false;
	var dragStartY = 0;
	var dragStartBpm = 0;
	var lastDragStep = 0;
	var BPM_MIN = 40;
	var BPM_MAX = 120;
	var BPM_STEP = 5;
	var PIXELS_PER_STEP = 20;
	// Touch start (down) on circle
	bpmCircleContainer.down = function (x, y, obj) {
		dragActive = true;
		dragStartY = y;
		dragStartBpm = bpm;
		lastDragStep = 0;
		// Animate circle slightly for feedback
		tween(bpmCircleBg, {
			scaleX: 1.18,
			scaleY: 1.18
		}, {
			duration: 100,
			yoyo: true,
			repeat: 1
		});
	};
	// Touch move on circle
	bpmCircleContainer.move = function (x, y, obj) {
		if (!dragActive) {
			return;
		}
		var dy = dragStartY - y; // Up is positive
		var step = Math.floor(dy / PIXELS_PER_STEP);
		if (step !== lastDragStep) {
			var calculatedBpm = dragStartBpm + step * BPM_STEP;
			if (calculatedBpm < BPM_MIN) {
				calculatedBpm = BPM_MIN;
			}
			if (calculatedBpm > BPM_MAX) {
				calculatedBpm = BPM_MAX;
			}
			if (bpm !== calculatedBpm) {
				bpm = calculatedBpm;
				bpmValueTxt.setText(bpm + "");
				beatInterval = 60 / bpm; // Recalculate beat interval for new BPM
				// Regenerate beat pattern with new BPM intervals
				beatPattern = generateBeatPattern(bpm);
				nextBeatIdx = 0; // Reset beat spawning
				storage.bpm = bpm; // persist bpm to storage
				// Play scratch sound when BPM changes during settings
				LK.getSound('scratch').play();
				if (bpmDisplayTxt) {
					// Show the actual BPM of the currently playing song, not the settings BPM
					var currentPlayingBpm = bpm; // This is the actual BPM being used for gameplay
					bpmDisplayTxt.setText(currentPlayingBpm + ' BPM');
				}
				drawWaveformLK(); // waveform'u anlık güncelle
				// --- İYİLEŞTİRME: Eğer endless modda oyun aktifse, BPM değişikliğini oyuna uygula ---
				if (gameMode === 'endless' && gameActive && !gamePaused) {
					// BPM'i ve beatInterval'ı güncelle
					beatInterval = 60 / bpm;
					beatPattern = generateBeatPattern(bpm);
					nextBeatIdx = 0;
					if (bpmDisplayTxt) {
						bpmDisplayTxt.setText(bpm + ' BPM');
					}
					drawWaveformLK();
					// Müzik de BPM'e göre değişsin
					var musicId = '100BPMSynthWave';
					if (bpm === 60) {
						musicId = '60BPMLowKey';
					} else if (bpm === 40) {
						musicId = '40BPMDeepVibes';
					} else if (bpm === 45) {
						musicId = '45BPMChillWave';
					} else if (bpm === 50) {
						musicId = '50BPMSlowMotion';
					} else if (bpm === 55) {
						musicId = '55BPMDreamscape';
					} else if (bpm === 65) {
						musicId = '65BPMRelaxed';
					} else if (bpm === 70) {
						musicId = '70BPMCalmVibes';
					} else if (bpm === 75) {
						musicId = '75BPMElectricDreams';
					} else if (bpm === 80) {
						musicId = '80BPMNeonNights';
					} else if (bpm === 85) {
						musicId = '85BPMCyberFlow';
					} else if (bpm === 90) {
						musicId = '90BPMDigitalPulse';
					} else if (bpm === 95) {
						musicId = '95BPMFutureBass';
					} else if (bpm === 100) {
						musicId = '100BPMSynthWave';
					} else if (bpm === 105) {
						musicId = '105BPMHyperDrive';
					} else if (bpm === 110) {
						musicId = '110BPMTechnoRush';
					} else if (bpm === 115) {
						musicId = '115BPMHighEnergy';
					} else if (bpm === 120) {
						musicId = '120BPMMaximum';
					}
					var volumeMultiplier = soundVolume / 100;
					LK.stopMusic();
					LK.playMusic(musicId, {
						volume: 1 * volumeMultiplier,
						loop: true
					});
					if (songNameTxt) {
						var songName = musicId.replace(/^\d+BPM/, '');
						songNameTxt.setText(songName);
					}
				}
				updateDebugText();
			}
			lastDragStep = step;
		}
	};
	// Touch end (up) on circle
	bpmCircleContainer.up = function (x, y, obj) {
		dragActive = false;
		// Animate back to normal scale
		tween(bpmCircleBg, {
			scaleX: 1.1,
			scaleY: 1.1
		}, {
			duration: 100
		});
	};
	// --- Volume drag logic ---
	var volumeDragActive = false;
	var volumeDragStartY = 0;
	var volumeDragStartValue = 0;
	var volumeLastDragStep = 0;
	var VOLUME_MIN = 0;
	var VOLUME_MAX = 100;
	var VOLUME_STEP = 5;
	var VOLUME_PIXELS_PER_STEP = 20;
	// Touch start (down) on volume circle
	volumeCircleContainer.down = function (x, y, obj) {
		volumeDragActive = true;
		volumeDragStartY = y;
		volumeDragStartValue = soundVolume;
		volumeLastDragStep = 0;
		// Animate circle slightly for feedback
		tween(volumeCircleBg, {
			scaleX: 1.18,
			scaleY: 1.18
		}, {
			duration: 100,
			yoyo: true,
			repeat: 1
		});
	};
	// Touch move on volume circle
	volumeCircleContainer.move = function (x, y, obj) {
		if (!volumeDragActive) {
			return;
		}
		var dy = volumeDragStartY - y; // Up is positive
		var step = Math.floor(dy / VOLUME_PIXELS_PER_STEP);
		if (step !== volumeLastDragStep) {
			var newVolume = volumeDragStartValue + step * VOLUME_STEP;
			if (newVolume < VOLUME_MIN) {
				newVolume = VOLUME_MIN;
			}
			if (newVolume > VOLUME_MAX) {
				newVolume = VOLUME_MAX;
			}
			if (newVolume !== soundVolume) {
				soundVolume = newVolume;
				volumeValueTxt.setText(soundVolume + "");
				storage.soundVolume = soundVolume; // persist volume to storage
				updateAllSoundVolumes(); // Update all sound volumes immediately
				// Update current playing music volume immediately
				var currentVolumeMultiplier = soundVolume / 100;
				LK.playMusic('menumusic', {
					volume: 0.8 * currentVolumeMultiplier,
					loop: true
				});
			}
			volumeLastDragStep = step;
		}
	};
	// Touch end (up) on volume circle
	volumeCircleContainer.up = function (x, y, obj) {
		volumeDragActive = false;
		// Animate back to normal scale
		tween(volumeCircleBg, {
			scaleX: 1.1,
			scaleY: 1.1
		}, {
			duration: 100
		});
	};
	// Close button (bottom right)
	var closeBtn = LK.getAsset('closebutton', {
		anchorX: 1,
		anchorY: 1,
		x: 2048 - 60,
		y: 2732 - 60,
		scaleX: 3,
		scaleY: 3
	});
	settingsmenu.addChild(closeBtn);
	closeBtn.down = function (x, y, obj) {
		// Play button click sound
		LK.getSound('buttonclick').play();
		// Click animation - scale inward then outward
		tween(closeBtn, {
			scaleX: 0.9,
			scaleY: 0.9
		}, {
			duration: 80,
			easing: tween.easeIn,
			onFinish: function onFinish() {
				tween(closeBtn, {
					scaleX: 1,
					scaleY: 1
				}, {
					duration: 120,
					easing: tween.easeOut
				});
			}
		});
		settingsmenu.visible = false;
		mainMenuContainer.visible = true;
	};
	// Remove tap-to-close on background
	settingsmenu.down = undefined;
	settingsmenu.visible = true;
}
// Button event handlers
// Countdown logic
var countdownContainer = new Container();
countdownContainer.visible = false;
game.addChild(countdownContainer);
// Pre-game ready screen container
var readyContainer = new Container();
readyContainer.visible = false;
game.addChild(readyContainer);
function showStoryIntro(onComplete) {
	storyContainer.visible = true;
	storyContainer.removeChildren();
	// Dark background covering full screen
	var storyBg = LK.getAsset('defeatbg', {
		anchorX: 0,
		anchorY: 0,
		x: 0,
		y: 0,
		width: 2048,
		height: 2732,
		tint: 0x000000
	});
	storyContainer.addChild(storyBg);
	// Play chapter1music during storytelling
	var volumeMultiplier = soundVolume / 100;
	LK.playMusic('chapter1music', {
		volume: 0.2 * volumeMultiplier,
		loop: true
	});
	// Skip button in bottom right corner
	var skipBtn = new Text2("SKIP", {
		size: 36,
		fill: 0xffffff
	});
	skipBtn.scale.set(2, 2);
	skipBtn.anchor.set(1, 1);
	skipBtn.x = 2048 - 50;
	skipBtn.y = 2732 - 50;
	storyContainer.addChild(skipBtn);
	// Skip button click handler
	skipBtn.down = function (x, y, obj) {
		LK.getSound('buttonclick').play();
		// Click animation
		tween(skipBtn, {
			scaleX: 3.6,
			scaleY: 3.6
		}, {
			duration: 80,
			easing: tween.easeIn,
			onFinish: function onFinish() {
				tween(skipBtn, {
					scaleX: 4,
					scaleY: 4
				}, {
					duration: 120,
					easing: tween.easeOut,
					onFinish: function onFinish() {
						// Keep storyContainer visible to continue story narration
						// storyContainer.visible = false;
						// storyContainer.removeChildren();
						if (typeof onComplete === "function") {
							onComplete();
						}
					}
				});
			}
		});
	};
	// Chapter 1 asset with fade in effect
	var chapter1Asset = LK.getAsset('Chapter1', {
		anchorX: 0.5,
		anchorY: 0.5,
		x: 2048 / 2,
		y: 2732 / 2 - 100,
		alpha: 0,
		scaleX: 0.4,
		scaleY: 0.4
	});
	storyContainer.addChild(chapter1Asset);
	// Fade in effect for chapter1 asset with 0.5 second delay
	tween(chapter1Asset, {
		alpha: 1
	}, {
		duration: 2000,
		delay: 500,
		easing: tween.easeOut
	});
	// Echoes of the Past asset with fade in effect
	var echoesofthepastAsset = LK.getAsset('echoesofthepast', {
		anchorX: 0.5,
		anchorY: 0.5,
		x: 2048 / 2,
		y: 2732 / 2 + 40,
		alpha: 0,
		scaleX: 0.3,
		scaleY: 0.3
	});
	storyContainer.addChild(echoesofthepastAsset);
	// Fade in effect for echoesofthepast asset with 0.8 second delay
	tween(echoesofthepastAsset, {
		alpha: 1
	}, {
		duration: 2000,
		delay: 800,
		easing: tween.easeOut
	});
	// Calculate sound duration (end - start) and fade out text when sound ends
	var soundDuration = (0.976 - 0.05) * 1000; // Convert to milliseconds
	var fadeoutDuration = 200; // 0.2 second fadeout
	// Play echoesofthepast sound synchronized with asset fade-in timing
	LK.setTimeout(function () {
		var echoesSound = LK.getSound('echoesofthepast');
		echoesSound.play();
		// Start fadeout 0.2 seconds before sound ends
		LK.setTimeout(function () {
			tween(echoesSound, {
				volume: 0
			}, {
				duration: fadeoutDuration,
				easing: tween.easeOut
			});
		}, soundDuration - fadeoutDuration);
	}, 500); // Start sound when chapter1 asset starts fading in for perfect sync
	// Remove assets 0.8 seconds after sound finishes (extended by 0.4 seconds)
	LK.setTimeout(function () {
		chapter1Asset.destroy();
		echoesofthepastAsset.destroy();
		// Add white stroke background for ch1pan1 asset
		var ch1pan1Stroke = LK.getAsset('ch1pan1', {
			anchorX: 0.5,
			anchorY: 0.5,
			x: 2048 / 2,
			y: 2732 / 2,
			rotation: 15 * Math.PI / 180,
			// Convert 15 degrees to radians
			alpha: 0,
			scaleX: 0.87,
			scaleY: 0.87,
			tint: 0xffffff
		});
		storyContainer.addChild(ch1pan1Stroke);
		// Add ch1pan1 asset to center screen with 15-degree rotation, starting from left side
		var ch1pan1Asset = LK.getAsset('ch1pan1', {
			anchorX: 0.5,
			anchorY: 0.5,
			x: -400,
			y: 2732 / 2,
			rotation: 15 * Math.PI / 180,
			// Convert 15 degrees to radians
			alpha: 0,
			scaleX: 0.85,
			scaleY: 0.85
		});
		storyContainer.addChild(ch1pan1Asset);
		// Fade in the stroke background first
		tween(ch1pan1Stroke, {
			alpha: 1
		}, {
			duration: 1000,
			easing: tween.easeOut
		});
		// Play paperswosh sound when ch1pan1 starts sliding in
		LK.getSound('paperswosh').play();
		// Fade in and slide ch1pan1 asset from left to center with sliding motion effect
		tween(ch1pan1Asset, {
			alpha: 1,
			x: 2048 / 2
		}, {
			duration: 1000,
			easing: tween.easeOut
		});
		// Track current panel being shown
		var currentPanel = 1;
		// Add touch handler to advance story
		storyContainer.down = function (x, y, obj) {
			// Check if chapter1 asset still exists - if so, don't show panel assets
			var chapter1Exists = false;
			for (var i = 0; i < storyContainer.children.length; i++) {
				var child = storyContainer.children[i];
				if (child && child.texture && child.texture.baseTexture && child.texture.baseTexture.source && child.texture.baseTexture.source.src && child.texture.baseTexture.source.src.indexOf('Chapter1') !== -1) {
					chapter1Exists = true;
					break;
				}
			}
			// If chapter1 asset still exists, don't proceed with panel assets
			if (chapter1Exists) {
				return;
			}
			if (currentPanel === 1) {
				// Add ch1pan2 asset starting from right side with 2 degree left rotation
				var ch1pan2Asset = LK.getAsset('ch1pan2', {
					anchorX: 0.5,
					anchorY: 0.5,
					x: 2048 + 400,
					// Start from right side off-screen
					y: 2732 / 2,
					rotation: -2 * Math.PI / 180,
					// 2 degrees to the left (negative)
					alpha: 0,
					scaleX: 0.85,
					scaleY: 0.85
				});
				storyContainer.addChild(ch1pan2Asset);
				// Play paperswosh sound when ch1pan2 starts sliding in
				LK.getSound('paperswosh').play();
				// Fade in and slide ch1pan2 asset from right to left
				tween(ch1pan2Asset, {
					alpha: 1,
					x: 2048 / 2 // Move to center
				}, {
					duration: 1000,
					easing: tween.easeOut
				});
				currentPanel = 2;
			} else if (currentPanel === 2) {
				// Add ch1pan3 with random rotation (left or right)
				var randomRotation = (Math.random() < 0.5 ? -1 : 1) * (Math.random() * 5 + 2); // Random 2-7 degrees left or right
				var slideFromRight = Math.random() < 0.5; // Randomly choose slide direction
				var ch1pan3Asset = LK.getAsset('ch1pan3', {
					anchorX: 0.5,
					anchorY: 0.5,
					x: slideFromRight ? 2048 + 400 : -400,
					y: 2732 / 2,
					rotation: randomRotation * Math.PI / 180,
					alpha: 0,
					scaleX: 0.85,
					scaleY: 0.85
				});
				storyContainer.addChild(ch1pan3Asset);
				// Play paperswosh sound when ch1pan3 starts sliding in
				LK.getSound('paperswosh').play();
				// Fade in and slide ch1pan3 asset to center
				tween(ch1pan3Asset, {
					alpha: 1,
					x: 2048 / 2
				}, {
					duration: 1000,
					easing: tween.easeOut
				});
				currentPanel = 3;
			} else if (currentPanel === 3) {
				// Add ch1pan4 with random rotation (left or right)
				var randomRotation = (Math.random() < 0.5 ? -1 : 1) * (Math.random() * 5 + 2); // Random 2-7 degrees left or right
				var slideFromRight = Math.random() < 0.5; // Randomly choose slide direction
				var ch1pan4Asset = LK.getAsset('ch1pan4', {
					anchorX: 0.5,
					anchorY: 0.5,
					x: slideFromRight ? 2048 + 400 : -400,
					y: 2732 / 2,
					rotation: randomRotation * Math.PI / 180,
					alpha: 0,
					scaleX: 0.85,
					scaleY: 0.85
				});
				storyContainer.addChild(ch1pan4Asset);
				// Play paperswosh sound when ch1pan4 starts sliding in
				LK.getSound('paperswosh').play();
				// Fade in and slide ch1pan4 asset to center
				tween(ch1pan4Asset, {
					alpha: 1,
					x: 2048 / 2
				}, {
					duration: 1000,
					easing: tween.easeOut
				});
				currentPanel = 4;
			} else if (currentPanel === 4) {
				// Add ch1pan5 with random rotation (left or right)
				var randomRotation = (Math.random() < 0.5 ? -1 : 1) * (Math.random() * 5 + 2); // Random 2-7 degrees left or right
				var slideFromRight = Math.random() < 0.5; // Randomly choose slide direction
				var ch1pan5Asset = LK.getAsset('ch1pan5', {
					anchorX: 0.5,
					anchorY: 0.5,
					x: slideFromRight ? 2048 + 400 : -400,
					y: 2732 / 2,
					rotation: randomRotation * Math.PI / 180,
					alpha: 0,
					scaleX: 0.85,
					scaleY: 0.85
				});
				storyContainer.addChild(ch1pan5Asset);
				// Play paperswosh sound when ch1pan5 starts sliding in
				LK.getSound('paperswosh').play();
				// Fade in and slide ch1pan5 asset to center
				tween(ch1pan5Asset, {
					alpha: 1,
					x: 2048 / 2
				}, {
					duration: 1000,
					easing: tween.easeOut
				});
				currentPanel = 5;
			} else if (currentPanel === 5) {
				// Fade out all panel assets when touched after ch1pan5
				var allPanelAssets = [];
				// Collect all panel assets from storyContainer
				for (var i = 0; i < storyContainer.children.length; i++) {
					var child = storyContainer.children[i];
					// Check if it's a panel asset by checking if it has panel-like properties
					if (child && child.anchor && child.x !== undefined && child.y !== undefined && child.alpha !== undefined) {
						// Skip background and skip button
						if (child !== storyBg && child !== skipBtn) {
							allPanelAssets.push(child);
						}
					}
				}
				// Fade out all panel assets simultaneously
				for (var i = 0; i < allPanelAssets.length; i++) {
					tween(allPanelAssets[i], {
						alpha: 0
					}, {
						duration: 200,
						easing: tween.easeOut,
						onFinish: function () {
							// Destroy the asset after fadeout
							if (this.parent) {
								this.parent.removeChild(this);
							}
							this.destroy();
						}.bind(allPanelAssets[i])
					});
				}
				// After all panels are faded out, fade out the black background and transition to tutorial
				tween(storyBg, {
					alpha: 0
				}, {
					duration: 200,
					delay: 700,
					easing: tween.easeOut,
					onFinish: function onFinish() {
						// Remove skip button
						if (skipBtn && skipBtn.parent) {
							skipBtn.parent.removeChild(skipBtn);
						}
						skipBtn.destroy();
						// Hide story container and show tutorial/game screen
						storyContainer.visible = false;
						storyContainer.removeChildren();
						// Call the onComplete callback to proceed to tutorial
						if (typeof onComplete === "function") {
							onComplete();
						}
					}
				});
				// Remove touch handler after fadeout starts
				storyContainer.down = undefined;
				currentPanel = 6;
			}
		};
		// Keep storyContainer visible to continue story narration
		// storyContainer.visible = false;
		// storyContainer.removeChildren();
		if (typeof onComplete === "function") {
			onComplete();
		}
		ch1pan1Stroke.destroy();
	}, 500 + soundDuration + 800); // Remove assets 0.8 seconds after sound ends
	// For now, skip button is the only way to proceed
	// Skip button can still be used to proceed early
}
function showReadyScreen(onReady) {
	gamescreen.visible = true;
	readyContainer.visible = true;
	readyContainer.removeChildren();
	var arrowTap = LK.getAsset('arrowtap', {
		anchorX: 0.5,
		anchorY: 0.5,
		x: HIT_ZONE_X - 150,
		y: HIT_ZONE_Y + 250,
		scaleX: 3,
		scaleY: 3
	});
	readyContainer.addChild(arrowTap);
	// Show ready button
	var readyBtn = LK.getAsset('readybutton', {
		anchorX: 0.5,
		anchorY: 0.5,
		x: 2048 / 2,
		y: 2732 / 2 + 350,
		scaleX: 6,
		scaleY: 6
	});
	readyContainer.addChild(readyBtn);
	// Add readyinfo asset on top of readybutton
	var readyInfo = LK.getAsset('readyinfo', {
		anchorX: 0.5,
		anchorY: 0.5,
		x: 2048 / 2,
		y: 2732 / 2 - 400,
		scaleX: 1.1,
		scaleY: 1.1
	});
	readyContainer.addChild(readyInfo);
	// Track if black screen is gone
	var blackScreenGone = false;
	// Check if black screen has been removed by listening for story completion
	var _checkStoryComplete = function checkStoryComplete() {
		// If storyContainer is not visible, black screen has been removed
		if (!storyContainer.visible) {
			blackScreenGone = true;
			readyBtn.alpha = 1; // Enable ready button
		} else {
			// Schedule another check
			LK.setTimeout(_checkStoryComplete, 100);
		}
	};
	// Start checking for story completion
	LK.setTimeout(_checkStoryComplete, 100);
	// Initially disable ready button
	readyBtn.alpha = 0.5;
	// Ready button click handler
	readyBtn.down = function (x, y, obj) {
		// Don't allow button press if black screen is still visible
		if (!blackScreenGone) {
			return;
		}
		// Play button click sound
		LK.getSound('buttonclick').play();
		// Click animation
		tween(readyBtn, {
			scaleX: 7.2,
			scaleY: 7.2
		}, {
			duration: 80,
			easing: tween.easeIn,
			onFinish: function onFinish() {
				tween(readyBtn, {
					scaleX: 8,
					scaleY: 8
				}, {
					duration: 120,
					easing: tween.easeOut,
					onFinish: function onFinish() {
						readyContainer.visible = false;
						readyContainer.removeChildren();
						if (typeof onReady === "function") {
							onReady();
						}
					}
				});
			}
		});
	};
}
function showCountdown(onFinish) {
	gamescreen.visible = true;
	countdownContainer.visible = true;
	countdownContainer.removeChildren();
	var numbers = ["3", "2", "1"];
	var idx = 0;
	function showNext() {
		if (idx >= numbers.length) {
			countdownContainer.visible = false;
			countdownContainer.removeChildren();
			// Always show gamescreen after countdown (fix blackscreen bug)
			gamescreen.visible = true;
			if (typeof onFinish === "function") {
				onFinish();
			}
			return;
		}
		countdownContainer.removeChildren();
		var txt = new Text2(numbers[idx], {
			size: 600,
			fill: 0xffd700
		});
		txt.scale.set(160, 160);
		txt.anchor.set(0.5, 0.5);
		txt.x = 2048 / 2;
		txt.y = 1200;
		txt.alpha = 0;
		txt.scale.x = 0.6;
		txt.scale.y = 0.6;
		countdownContainer.addChild(txt);
		// Animate in
		tween(txt, {
			alpha: 1,
			scaleX: 1,
			scaleY: 1
		}, {
			duration: 250,
			easing: tween.cubicOut,
			onFinish: function onFinish() {
				// Hold, then animate out
				tween(txt, {
					alpha: 0,
					scaleX: 1.5,
					scaleY: 1.5
				}, {
					duration: 350,
					delay: 400,
					easing: tween.cubicIn,
					onFinish: function onFinish() {
						idx++;
						showNext();
					}
				});
			}
		});
	}
	idx = 0;
	showNext();
}
startBtn.down = function (x, y, obj) {
	// Play button click sound
	LK.getSound('buttonclick').play();
	// Stop menu music immediately
	LK.stopMusic();
	// Click animation - scale inward then outward
	tween(startBtn, {
		scaleX: 0.162,
		scaleY: 0.162
	}, {
		duration: 80,
		easing: tween.easeIn,
		onFinish: function onFinish() {
			tween(startBtn, {
				scaleX: 0.18,
				scaleY: 0.18
			}, {
				duration: 120,
				easing: tween.easeOut,
				onFinish: function onFinish() {
					mainMenuContainer.visible = false;
					showStoryIntro(function () {
						showReadyScreen(function () {
							showCountdown(function () {
								startGame({
									mode: "story"
								});
							});
						});
					});
				}
			});
		}
	});
};
settingsBtn.down = function (x, y, obj) {
	// Play button click sound
	LK.getSound('buttonclick').play();
	// Click animation - scale inward then outward
	tween(settingsBtn, {
		scaleX: 0.162,
		scaleY: 0.162
	}, {
		duration: 80,
		easing: tween.easeIn,
		onFinish: function onFinish() {
			tween(settingsBtn, {
				scaleX: 0.18,
				scaleY: 0.18
			}, {
				duration: 120,
				easing: tween.easeOut,
				onFinish: function onFinish() {
					mainMenuContainer.visible = false;
					showSettingsMenu();
				}
			});
		}
	});
};
creditsBtn.down = function (x, y, obj) {
	// Play button click sound
	LK.getSound('buttonclick').play();
	// Click animation - scale inward then outward
	tween(creditsBtn, {
		scaleX: 0.162,
		scaleY: 0.162
	}, {
		duration: 80,
		easing: tween.easeIn,
		onFinish: function onFinish() {
			tween(creditsBtn, {
				scaleX: 0.18,
				scaleY: 0.18
			}, {
				duration: 120,
				easing: tween.easeOut,
				onFinish: function onFinish() {
					mainMenuContainer.visible = false;
					showCreditsMenu();
				}
			});
		}
	});
};
supportBtn.down = function (x, y, obj) {
	// Play button click sound
	LK.getSound('buttonclick').play();
	// Click animation - scale inward then outward
	tween(supportBtn, {
		scaleX: 0.162,
		scaleY: 0.162
	}, {
		duration: 80,
		easing: tween.easeIn,
		onFinish: function onFinish() {
			tween(supportBtn, {
				scaleX: 0.18,
				scaleY: 0.18
			}, {
				duration: 120,
				easing: tween.easeOut,
				onFinish: function onFinish() {
					mainMenuContainer.visible = false;
					showSupportMenu();
				}
			});
		}
	});
};
// Music mute button in bottom corner of main menu
var musicMuted = typeof storage.musicMuted === "boolean" ? storage.musicMuted : false;
var musicMuteBtn = LK.getAsset('purplecircle', {
	anchorX: 0.5,
	anchorY: 0.5,
	x: 2048 - 150,
	y: 2732 - 150,
	scaleX: 0.8,
	scaleY: 0.8,
	alpha: musicMuted ? 0.5 : 1
});
mainMenuContainer.addChild(musicMuteBtn);
// Music icon text on button
var musicIconTxt = new Text2(musicMuted ? "M" : "♪", {
	size: 48,
	fill: 0xffffff
});
musicIconTxt.scale.set(2, 2);
musicIconTxt.anchor.set(0.5, 0.5);
musicIconTxt.x = 2048 - 150;
musicIconTxt.y = 2732 - 150;
mainMenuContainer.addChild(musicIconTxt);
// Music mute button handler
musicMuteBtn.down = function (x, y, obj) {
	// Play button click sound
	LK.getSound('buttonclick').play();
	// Toggle mute state
	musicMuted = !musicMuted;
	storage.musicMuted = musicMuted;
	// Update button appearance
	musicMuteBtn.alpha = musicMuted ? 0.5 : 1;
	musicIconTxt.setText(musicMuted ? "M" : "♪");
	// Handle music playback
	if (musicMuted) {
		LK.stopMusic();
	} else {
		var volumeMultiplier = soundVolume / 100;
		LK.playMusic('menumusic', {
			volume: 0.8 * volumeMultiplier,
			loop: true
		});
	}
	// Click animation
	tween(musicMuteBtn, {
		scaleX: 0.72,
		scaleY: 0.72
	}, {
		duration: 80,
		easing: tween.easeIn,
		onFinish: function onFinish() {
			tween(musicMuteBtn, {
				scaleX: 0.8,
				scaleY: 0.8
			}, {
				duration: 120,
				easing: tween.easeOut
			});
		}
	});
};
// Story intro screen container
var storyContainer = new Container();
storyContainer.visible = false;
game.addChild(storyContainer);
// Add version number to bottom left corner of main menu
var versionTxt = new Text2('v1.01', {
	size: 24,
	fill: 0xffffff
});
versionTxt.scale.set(1.5, 1.5);
versionTxt.anchor.set(0, 1);
versionTxt.x = 50;
versionTxt.y = 2732 - 50;
mainMenuContainer.addChild(versionTxt);
// Add menu to game
game.addChild(mainMenuContainer);
// menus are already added above
// Hide all gameplay UI until game starts
scoreTxt.visible = false;
comboTxt.visible = false;
bpmDisplayTxt.visible = false;
songNameTxt.visible = false;
// missTxt removed, only hearts for lives
// timelineBar and kickTarget are now children of gamescreen, so their visibility is managed by gamescreen
// Patch startGame to show gameplay UI and gamescreen
var _originalStartGame = startGame;
startGame = function startGame(options) {
	scoreTxt.visible = true;
	comboTxt.visible = true;
	bpmDisplayTxt.visible = true;
	songNameTxt.visible = true;
	if (bpmDisplayTxt) {
		var currentPlayingBpm = isTutorial ? 60 : bpm;
		bpmDisplayTxt.setText(currentPlayingBpm + ' BPM');
	}
	gamescreen.visible = true;
	mainMenuContainer.visible = false;
	_originalStartGame(options);
};
// On menu, hide gameplay UI and gamescreen
mainMenuContainer.visible = true;
scoreTxt.visible = false;
comboTxt.visible = false;
bpmDisplayTxt.visible = false;
songNameTxt.visible = false;
gamescreen.visible = false;
// Play menu music when in main menu (only if not muted)
if (!musicMuted) {
	var volumeMultiplier = soundVolume / 100;
	LK.playMusic('menumusic', {
		volume: 0.8 * volumeMultiplier,
		loop: true
	});
}
// Apply current sound volume settings to all sounds
updateAllSoundVolumes();
// Pause button functionality
pauseBtn.down = function (x, y, obj) {
	if (!gameActive || !gamescreen.visible) {
		return;
	}
	// Play button click sound
	LK.getSound('buttonclick').play();
	// Pause the game
	gamePaused = true;
	showPauseMenu();
	// No animation - button scale remains fixed at 1.6
};
// Show pause menu function
function showPauseMenu() {
	pauseContainer.visible = true;
	pauseContainer.removeChildren();
	// Hide BPM and song name in pause menu
	bpmDisplayTxt.visible = false;
	songNameTxt.visible = false;
	// Pause menu background
	var pauseMenuBg = LK.getAsset('pausemenu', {
		anchorX: 0,
		anchorY: 0,
		x: 0,
		y: 0,
		width: 2048,
		height: 2732
	});
	pauseContainer.addChild(pauseMenuBg);
	// Button positions
	var pauseButtonStartY = 1200;
	var pauseButtonHeight = 289;
	var pauseButtonSpacing = 20;
	// Resume button
	var resumeBtn = LK.getAsset('resumebutton', {
		anchorX: 0.5,
		anchorY: 0.5,
		x: 2048 / 2,
		y: pauseButtonStartY,
		scaleX: 5,
		scaleY: 5
	});
	pauseContainer.addChild(resumeBtn);
	// Restart button
	var restartBtn = LK.getAsset('restartbutton', {
		anchorX: 0.5,
		anchorY: 0.5,
		x: 2048 / 2,
		y: pauseButtonStartY + pauseButtonHeight + pauseButtonSpacing,
		scaleX: 5,
		scaleY: 5
	});
	pauseContainer.addChild(restartBtn);
	// Main menu button
	var pauseMainMenuBtn = LK.getAsset('mainmenu', {
		anchorX: 0.5,
		anchorY: 0.5,
		x: 2048 / 2,
		y: pauseButtonStartY + 2 * (pauseButtonHeight + pauseButtonSpacing),
		scaleX: 5,
		scaleY: 5
	});
	pauseContainer.addChild(pauseMainMenuBtn);
	// Resume button handler
	resumeBtn.down = function (x, y, obj) {
		LK.getSound('buttonclick').play();
		tween(resumeBtn, {
			scaleX: 4.5,
			scaleY: 4.5
		}, {
			duration: 80,
			easing: tween.easeIn,
			onFinish: function onFinish() {
				tween(resumeBtn, {
					scaleX: 5,
					scaleY: 5
				}, {
					duration: 120,
					easing: tween.easeOut,
					onFinish: function onFinish() {
						resumeGame();
					}
				});
			}
		});
	};
	// Restart button handler
	restartBtn.down = function (x, y, obj) {
		LK.getSound('buttonclick').play();
		tween(restartBtn, {
			scaleX: 4.5,
			scaleY: 4.5
		}, {
			duration: 80,
			easing: tween.easeIn,
			onFinish: function onFinish() {
				tween(restartBtn, {
					scaleX: 5,
					scaleY: 5
				}, {
					duration: 120,
					easing: tween.easeOut,
					onFinish: function onFinish() {
						pauseContainer.visible = false;
						gamePaused = false;
						gameActive = false; // Stop game temporarily during restart
						maxCombo = 0;
						perfectCount = 0;
						maxPerfectCombo = 0;
						// Reset lives and hearts before showing ready screen
						lives = MAX_LIVES;
						heartshields = 3; // Reset heartshields
						// Continue from current level - if tutorial not completed, restart tutorial
						if (!tutorialCompleted) {
							currentLevel = 1;
							storage.currentLevel = currentLevel;
						}
						// Remove all existing heart icons
						for (var i = 0; i < heartIcons.length; i++) {
							if (heartIcons[i] && heartIcons[i].parent) {
								heartIcons[i].parent.removeChild(heartIcons[i]);
							}
						}
						// Recreate all heart icons as full heartshields
						heartIcons = [];
						var heartY = scoreTxt.y + 50;
						var heartWidth = 150;
						var totalHeartsWidth = MAX_LIVES * heartWidth + (MAX_LIVES - 1) * heartSpacing;
						var heartStartX = 2048 - 100 - totalHeartsWidth;
						for (var i = 0; i < MAX_LIVES; i++) {
							var heartAsset = LK.getAsset('heartshield', {
								anchorX: 0,
								anchorY: 0,
								x: heartStartX + i * (heartWidth + heartSpacing),
								y: heartY
							});
							gamescreen.addChild(heartAsset);
							heartIcons.push(heartAsset);
						}
						showReadyScreen(function () {
							showCountdown(function () {
								startGame();
							});
						});
					}
				});
			}
		});
	};
	// Pause main menu button handler
	pauseMainMenuBtn.down = function (x, y, obj) {
		LK.getSound('buttonclick').play();
		tween(pauseMainMenuBtn, {
			scaleX: 4.5,
			scaleY: 4.5
		}, {
			duration: 80,
			easing: tween.easeIn,
			onFinish: function onFinish() {
				tween(pauseMainMenuBtn, {
					scaleX: 5,
					scaleY: 5
				}, {
					duration: 120,
					easing: tween.easeOut,
					onFinish: function onFinish() {
						pauseContainer.visible = false;
						gamePaused = false;
						gameActive = false;
						maxCombo = 0;
						perfectCount = 0;
						maxPerfectCombo = 0;
						mainMenuContainer.visible = true;
						scoreTxt.visible = false;
						comboTxt.visible = false;
						bpmDisplayTxt.visible = false;
						songNameTxt.visible = false;
						gamescreen.visible = false;
						// Stop current music and play menu music (only if not muted)
						LK.stopMusic();
						if (!musicMuted) {
							LK.setTimeout(function () {
								var volumeMultiplier = soundVolume / 100;
								LK.playMusic('menumusic', {
									volume: 0.8 * volumeMultiplier,
									loop: true
								});
							}, 100);
						}
					}
				});
			}
		});
	};
}
// Resume game function
function resumeGame() {
	gamePaused = false;
	pauseContainer.visible = false;
	// Show BPM and song name when resuming
	bpmDisplayTxt.visible = true;
	songNameTxt.visible = true;
	lastTickTime = Date.now(); // Reset time to prevent time jump
}
// --- Beat spawning ---
function spawnBeat(beat) {
	var marker = new BeatMarker();
	marker.x = 0;
	marker.y = HIT_ZONE_Y;
	marker.beatTime = beat.time;
	marker.type = beat.type;
	marker.active = true;
	marker.hit = false;
	marker.lastX = marker.x;
	marker.hasReachedHitZone = false;
	// Trail sistemi ekle
	marker.lastTrailTime = Date.now();
	marker.trailInterval = 15;
	marker.assetType = marker.type;
	marker.updateTrail = function () {
		var now = Date.now();
		if (now - this.lastTrailTime > this.trailInterval) {
			var colors = BEAT_COLORS[this.type] || BEAT_COLORS.circle;
			// Beat'in hareket yönünü belirle (sağ tarafta mı?)
			var isRightSide = this.x > HIT_ZONE_X;
			var directionMultiplier = isRightSide ? 1 : -1;
			for (var i = 0; i < 10; i++) {
				// Ana parçacık
				var mainParticle = createTrailParticle(this.x + (Math.random() - 0.5) * 20, this.y + (Math.random() - 0.5) * 20, colors.mainTrail);
				mainParticle.velocityX = (Math.random() * 2 + 1) * directionMultiplier;
				mainParticle.velocityY = (Math.random() - 0.5) * 3;
				// Yan parçacık 1
				var subParticle1 = createTrailParticle(this.x + (Math.random() - 0.5) * 30, this.y + (Math.random() - 0.5) * 30, colors.subTrail1);
				subParticle1.width = 4;
				subParticle1.height = 4;
				subParticle1.velocityX = (Math.random() * 1.5 + 0.5) * directionMultiplier;
				subParticle1.velocityY = (Math.random() - 0.5) * 2;
				// Yan parçacık 2
				var subParticle2 = createTrailParticle(this.x + (Math.random() - 0.5) * 25, this.y + (Math.random() - 0.5) * 25, colors.subTrail2);
				subParticle2.width = 3;
				subParticle2.height = 3;
				subParticle2.velocityX = (Math.random() * 1 + 0.5) * directionMultiplier;
				subParticle2.velocityY = (Math.random() - 0.5) * 1.5;
				// Parçacıkları oyun ekranına ekle
				[mainParticle, subParticle1, subParticle2].forEach(function (p) {
					p.fadeSpeed = 0.03;
					p.gravity = 0.1;
					p.drag = 0.98;
					gamescreen.addChild(p);
					trailParticles.push(p);
				});
			}
			this.lastTrailTime = now;
		}
	};
	// Add glow effect to the beat marker
	addGlowEffect(marker, 0x8a2be2, 0.4);
	beats.push(marker);
	gamescreen.addChild(marker);
}
// --- Game update ---
game.update = function () {
	if (!gameActive || gamePaused) {
		return;
	}
	// Time management
	var now = Date.now();
	var dt = (now - lastTickTime) / 1000;
	lastTickTime = now;
	songTime += dt;
	// Prevent beat spawning until countdown is finished and gameplay is visible
	if (!gamescreen.visible || countdownContainer.visible) {
		return;
	}
	// Only allow beat spawning after countdown is finished
	if (!countdownContainer.visible) {
		// Spawn new beats with constant speed and BPM-based timing
		var currentBeatSpeed = getBeatSpeed(); // Always 400 px/s
		// Calculate travel time from left edge (x=0) to hit zone
		var travelTime = HIT_ZONE_X / currentBeatSpeed; // Time needed to travel from x=0 to hit zone
		// Spawn beats at the right time so they arrive at hit zone exactly when needed
		while (nextBeatIdx < beatPattern.length && beatPattern[nextBeatIdx].time - songTime <= travelTime) {
			spawnBeat(beatPattern[nextBeatIdx]);
			nextBeatIdx++;
		}
	}
	// Tutorial completion check - 5k points for chapter 1
	if (isTutorial && score >= 5000 && !tutorialCompleted) {
		tutorialCompleted = true;
		storage.tutorialCompleted = true;
		currentLevel = 2;
		storage.currentLevel = currentLevel;
		// Show tutorial completion message
		var tutorialCompleteTxt = new Text2('CHAPTER 1 COMPLETE!', {
			size: 72,
			fill: 0x00ff00
		});
		tutorialCompleteTxt.scale.set(2, 2);
		tutorialCompleteTxt.anchor.set(0.5, 0.5);
		tutorialCompleteTxt.x = 2048 / 2;
		tutorialCompleteTxt.y = 1200;
		game.addChild(tutorialCompleteTxt);
		tween(tutorialCompleteTxt, {
			alpha: 0,
			y: tutorialCompleteTxt.y - 100
		}, {
			duration: 2000,
			easing: tween.easeOut,
			onFinish: function onFinish() {
				tutorialCompleteTxt.destroy();
			}
		});
	}
	// Check if player reached 5k points - pause game and show black screen (only in story mode)
	if (score >= 5000 && gameActive && gameMode === "story") {
		// Pause the game
		gameActive = false;
		gamePaused = true;
		// Stop current music
		LK.stopMusic();
		// Hide all game UI elements
		scoreTxt.visible = false;
		comboTxt.visible = false;
		bpmDisplayTxt.visible = false;
		songNameTxt.visible = false;
		gamescreen.visible = false;
		// Create ch1panF screen overlay
		var blackScreen = LK.getAsset('ch1panF', {
			anchorX: 0,
			anchorY: 0,
			x: 0,
			y: 0,
			width: 2048,
			height: 2732,
			alpha: 0
		});
		game.addChild(blackScreen);
		// Play 60BPM music when ch1panF appears
		var volumeMultiplier = soundVolume / 100;
		LK.playMusic('60BPMLowKey', {
			volume: 1 * volumeMultiplier,
			loop: true
		});
		// Fade in ch1panF screen
		tween(blackScreen, {
			alpha: 1
		}, {
			duration: 500,
			easing: tween.easeOut
		});
		// Add ch1panFnarbox asset 1 second after ch1panF appears
		LK.setTimeout(function () {
			var ch1panFnarbox = LK.getAsset('ch1panFnarbox', {
				anchorX: 1,
				anchorY: 1,
				x: 2048 - 50,
				y: 2732 - 50,
				alpha: 0
			});
			game.addChild(ch1panFnarbox);
			// Fade in the narbox
			tween(ch1panFnarbox, {
				alpha: 1
			}, {
				duration: 500,
				easing: tween.easeOut
			});
			// Play ch1voice1 and ch1voice2 randomly within 3 seconds after narbox appears
			var voice1Delay = Math.random() * 3000; // Random delay between 0-3 seconds
			var voice2Delay = Math.random() * 3000; // Random delay between 0-3 seconds
			LK.setTimeout(function () {
				LK.getSound('ch1voice1').play();
			}, voice1Delay);
			LK.setTimeout(function () {
				LK.getSound('ch1voice2').play();
			}, voice2Delay);
			// After 3 seconds, fade out music and assets, return to black screen
			LK.setTimeout(function () {
				// Fade out the music
				LK.playMusic('60BPMLowKey', {
					fade: {
						start: 1,
						end: 0,
						duration: 1000
					}
				});
				// Fade out ch1panF asset
				tween(blackScreen, {
					alpha: 0
				}, {
					duration: 1000,
					easing: tween.easeOut
				});
				// Fade out ch1panFnarbox asset
				tween(ch1panFnarbox, {
					alpha: 0
				}, {
					duration: 1000,
					easing: tween.easeOut,
					onFinish: function onFinish() {
						// Remove assets after fadeout
						blackScreen.destroy();
						ch1panFnarbox.destroy();
						// Stop the music completely
						LK.stopMusic();
						// After fadeout, add Chapter 2 assets and sound
						LK.setTimeout(function () {
							// Set background to black
							game.setBackgroundColor(0x000000);
							// Add Chapter 2 asset with fade in effect
							var chapter2Asset = LK.getAsset('Chapter2', {
								anchorX: 0.5,
								anchorY: 0.5,
								x: 2048 / 2,
								y: 2732 / 2 - 100,
								alpha: 0,
								scaleX: 0.4,
								scaleY: 0.4
							});
							game.addChild(chapter2Asset);
							// Add nightshift asset with fade in effect
							var nightshiftAsset = LK.getAsset('nightshift', {
								anchorX: 0.5,
								anchorY: 0.5,
								x: 2048 / 2,
								y: 2732 / 2 + 80,
								// 20px yukarı çıkarıldı
								alpha: 0,
								scaleX: 0.25,
								scaleY: 0.25
							});
							game.addChild(nightshiftAsset);
							// Fade in chapter2 asset
							tween(chapter2Asset, {
								alpha: 1
							}, {
								duration: 2000,
								easing: tween.easeOut
							});
							// Fade in nightshift asset
							tween(nightshiftAsset, {
								alpha: 1
							}, {
								duration: 2000,
								delay: 500,
								easing: tween.easeOut
							});
							// Play nightshift sound when assets appear
							var nightshiftSound = LK.getSound('nightshift');
							nightshiftSound.play();
							// Get nightshift sound duration from asset definition (start:0, end:1, so use full duration)
							var nightshiftSoundAsset = LK.getSound('nightshift');
							var nightshiftDuration = 0;
							// Defensive: try to get duration from sound asset, fallback to 3000ms if not available
							if (nightshiftSoundAsset && nightshiftSoundAsset.duration) {
								nightshiftDuration = nightshiftSoundAsset.duration * 1000;
							} else {
								nightshiftDuration = 3000;
							}
							// Remove chapter2 and nightshift assets when sound finishes
							LK.setTimeout(function () {
								// Remove chapter2 asset
								if (chapter2Asset && chapter2Asset.parent) {
									chapter2Asset.parent.removeChild(chapter2Asset);
									chapter2Asset.destroy();
								}
								// Remove nightshift asset
								if (nightshiftAsset && nightshiftAsset.parent) {
									nightshiftAsset.parent.removeChild(nightshiftAsset);
									nightshiftAsset.destroy();
								}
								// Play chapter2music after assets are removed
								var volumeMultiplier = soundVolume / 100;
								LK.playMusic('chapter2music', {
									volume: 0.3 * volumeMultiplier,
									loop: true
								});
								// Add ch2pan1-5 assets when chapter2music starts, but only show ch2pan1 at first, and require tap to advance each panel
								var ch2panAssets = [];
								var ch2PanNames = ['ch2pan1', 'ch2pan2', 'ch2pan3', 'ch2pan4', 'ch2pan5'];
								var ch2CurrentPanel = 0;
								var ch2PanelActive = false;
								var ch2PanelContainer = new Container();
								game.addChild(ch2PanelContainer);
								// Helper to show a panel by index
								function showCh2Panel(panelIdx) {
									// Show each panel (ch2pan1 through ch2pan5) before starting chapter 2
									if (panelIdx < ch2PanNames.length) {
										// Create and show the current panel with sliding animation
										var slideFromRight = Math.random() < 0.5; // Randomly choose slide direction
										var startX = slideFromRight ? 2048 + 400 : -400; // Start from off-screen
										var panelAsset = LK.getAsset(ch2PanNames[panelIdx], {
											anchorX: 0.5,
											anchorY: 0.5,
											x: startX,
											y: 2732 / 2,
											alpha: 0,
											scaleX: 0.85,
											scaleY: 0.85,
											rotation: (Math.random() < 0.5 ? -1 : 1) * (Math.random() * 5 + 2) * Math.PI / 180
										});
										ch2PanelContainer.addChild(panelAsset);
										ch2panAssets.push(panelAsset);
										// Play paperswosh sound when panel appears
										LK.getSound('paperswosh').play();
										// Fade in and slide the panel to center
										tween(panelAsset, {
											alpha: 1,
											x: 2048 / 2 // Slide to center
										}, {
											duration: 1000,
											easing: tween.easeOut
										});
										ch2PanelActive = true;
										return;
									}
									// Only start chapter 2 after all panels have been shown
									if (panelIdx >= ch2PanNames.length) {
										var startChapter2 = function startChapter2() {
											// Set chapter 2 state
											isTutorial = false;
											bpm = 90;
											beatInterval = 60 / bpm;
											beatPattern = generateBeatPattern(bpm);
											nextBeatIdx = 0;
											songTime = 0;
											lastTickTime = Date.now();
											score = 0;
											misses = 0;
											ignoredMisses = 0;
											combo = 0;
											perfectCombo = 0;
											lives = MAX_LIVES;
											heartshields = 3;
											lastBpmUpgradeScore = 0;
											// Set UI for gameplay
											scoreTxt.setText('0');
											comboTxt.setText('');
											bpmDisplayTxt.setText('90 BPM');
											if (songNameTxt) {
												songNameTxt.setText('DigitalPulse');
											}
											// Show gameplay UI
											scoreTxt.visible = true;
											comboTxt.visible = true;
											bpmDisplayTxt.visible = true;
											songNameTxt.visible = true;
											gamescreen.visible = true;
											mainMenuContainer.visible = false;
											// Play 90BPM music
											var volumeMultiplier = soundVolume / 100;
											LK.playMusic('90BPMDigitalPulse', {
												volume: 1 * volumeMultiplier,
												loop: true
											});
											// Start countdown, then start game
											showCountdown(function () {
												// Patch win condition for chapter 2: 10K points
												// --- CHAPTER 2 GAMEPLAY: 90BPM, beats move at 90BPM, win at 10K points ---
												game.update = function () {
													if (!gameActive || gamePaused) {
														return;
													}
													// Time management
													var now = Date.now();
													var dt = (now - lastTickTime) / 1000;
													lastTickTime = now;
													songTime += dt;
													// Prevent beat spawning until countdown is finished and gameplay is visible
													if (!gamescreen.visible || countdownContainer.visible) {
														return;
													}
													// Only allow beat spawning after countdown is finished
													if (!countdownContainer.visible) {
														// --- 90BPM: spawn beats at 90BPM ---
														var currentBeatSpeed = getBeatSpeed(); // Always 400 px/s
														var travelTime = HIT_ZONE_X / currentBeatSpeed;
														// Spawn beats at the right time so they arrive at hit zone exactly when needed
														while (nextBeatIdx < beatPattern.length && beatPattern[nextBeatIdx].time - songTime <= travelTime) {
															spawnBeat(beatPattern[nextBeatIdx]);
															nextBeatIdx++;
														}
													}
													// CH2: 10K puan - show blackscreen
													if (score >= 10000 && gameActive) {
														gameActive = false;
														gamePaused = true;
														// Stop current music
														LK.stopMusic();
														// Hide all game UI elements
														scoreTxt.visible = false;
														comboTxt.visible = false;
														bpmDisplayTxt.visible = false;
														songNameTxt.visible = false;
														gamescreen.visible = false;
														// Create black screen overlay
														var blackScreen = LK.getAsset('ch2panF', {
															anchorX: 0,
															anchorY: 0,
															x: 0,
															y: 0,
															width: 2048,
															height: 2732,
															alpha: 0,
															tint: 0x000000 // Make it black
														});
														game.addChild(blackScreen);
														// Add devamiicin asset on black screen
														var blackScreenAsset = LK.getAsset('devamiicin', {
															anchorX: 0.5,
															anchorY: 0.5,
															x: 2048 / 2,
															y: 2732 / 2,
															alpha: 0,
															scaleX: 0.5,
															scaleY: 0.5
														});
														game.addChild(blackScreenAsset);
														// Add pause button to devamiicin screen
														var devamiicınPauseBtn = LK.getAsset('pause', {
															anchorX: 1,
															anchorY: 0,
															x: 2048 - 50,
															y: 220,
															scaleX: 1.6,
															scaleY: 1.6,
															alpha: 0
														});
														game.addChild(devamiicınPauseBtn);
														// Fade in pause button with devamiicin asset
														tween(devamiicınPauseBtn, {
															alpha: 1
														}, {
															duration: 1000,
															delay: 500,
															easing: tween.easeOut
														});
														// Pause button click handler for devamiicin screen
														devamiicınPauseBtn.down = function (x, y, obj) {
															// Play button click sound
															LK.getSound('buttonclick').play();
															// Show pause menu
															gamePaused = true;
															showPauseMenu();
														};
														// Fade in black screen
														tween(blackScreen, {
															alpha: 1
														}, {
															duration: 500,
															easing: tween.easeOut
														});
														// Fade in devamiicin asset after black screen appears
														tween(blackScreenAsset, {
															alpha: 1
														}, {
															duration: 1000,
															delay: 500,
															easing: tween.easeOut
														});
														// Add tap-to-return functionality after 3 seconds
														var devamiicınTapEnabled = false;
														LK.setTimeout(function () {
															devamiicınTapEnabled = true;
														}, 3000);
														// Touch handler for devamiicin screen
														blackScreen.down = function (x, y, obj) {
															if (devamiicınTapEnabled) {
																// Play button click sound
																LK.getSound('buttonclick').play();
																// Return to main menu
																LK.stopMusic();
																gameActive = false;
																gamePaused = false;
																// Clean up devamiicin screen elements
																if (blackScreen && blackScreen.parent) {
																	blackScreen.parent.removeChild(blackScreen);
																	blackScreen.destroy();
																}
																if (blackScreenAsset && blackScreenAsset.parent) {
																	blackScreenAsset.parent.removeChild(blackScreenAsset);
																	blackScreenAsset.destroy();
																}
																if (devamiicınPauseBtn && devamiicınPauseBtn.parent) {
																	devamiicınPauseBtn.parent.removeChild(devamiicınPauseBtn);
																	devamiicınPauseBtn.destroy();
																}
																// Show main menu
																mainMenuContainer.visible = true;
																scoreTxt.visible = false;
																comboTxt.visible = false;
																bpmDisplayTxt.visible = false;
																songNameTxt.visible = false;
																gamescreen.visible = false;
																// Play menu music (only if not muted)
																if (!musicMuted) {
																	LK.setTimeout(function () {
																		var volumeMultiplier = soundVolume / 100;
																		LK.playMusic('menumusic', {
																			volume: 0.8 * volumeMultiplier,
																			loop: true
																		});
																	}, 100);
																}
															}
														};
														return;
													}
													// Move beats and check for misses (copy from original game.update)
													for (var i = beats.length - 1; i >= 0; i--) {
														var beat = beats[i];
														var currentBeatSpeed = getBeatSpeed();
														var elapsedTime = songTime - (beat.beatTime - HIT_ZONE_X / currentBeatSpeed);
														beat.x = elapsedTime * currentBeatSpeed;
														// Dalga boyunu daha belirgin yapmak için waveLength'i küçült
														var waveLength = 80;
														var phase = -HIT_ZONE_X / waveLength;
														beat.y = HIT_ZONE_Y + Math.sin(beat.x / waveLength + phase) * 500;
														if (beat.x < -400) {
															beat.x = -400;
														}
														if (beat.x > TIMELINE_X + TIMELINE_WIDTH + 400) {
															beat.x = TIMELINE_X + TIMELINE_WIDTH + 400;
														}
														if (kickTarget && kickTarget.parent) {
															var kickTargetIndex = kickTarget.parent.getChildIndex(kickTarget);
															var beatIndex = beat.parent.getChildIndex(beat);
															if (beatIndex > kickTargetIndex) {
																beat.parent.setChildIndex(beat, kickTargetIndex);
															}
														}
														if (typeof beat.lastX === "undefined") {
															beat.lastX = beat.x;
														}
														// Only check for miss if the beat has passed the hit zone (not before)
														// Also, do not count as miss if the beat has not yet reached the hit zone at least once
														if (!beat.hit && beat.active && typeof beat.hasReachedHitZone !== "undefined" && beat.hasReachedHitZone && beat.lastX <= HIT_ZONE_X + HIT_WINDOW && beat.x > HIT_ZONE_X + HIT_WINDOW) {
															// Missed
															beat.active = false;
															misses++;
															// Replace the beat marker asset with missCircle for missed beats
															var missedAsset = LK.getAsset('missCircle', {
																anchorX: 0.5,
																anchorY: 0.5,
																x: beat.x,
																y: beat.y,
																alpha: 0.8,
																width: 60,
																height: 59.77
															});
															// Replace the beat's current asset with missCircle
															if (beat.parent) {
																// Store parent reference before removing beat
																var beatParent = beat.parent;
																// Remove the old beat marker from its parent
																beatParent.removeChild(beat);
																// Add the missed asset to the stored parent reference
																beatParent.addChild(missedAsset);
																// Update the beat reference to point to the new asset
																beats[i] = missedAsset;
																// Copy over important properties
																missedAsset.beatTime = beat.beatTime;
																missedAsset.type = beat.type;
																missedAsset.active = false;
																missedAsset.hit = false;
																missedAsset.lastX = beat.x;
																missedAsset.hasReachedHitZone = true;
																missedAsset.isMissed = true;
																// Destroy the original beat asset
																beat.destroy();
															}
															// Handle heartshield system
															if (heartshields > 0) {
																// Convert leftmost heartshield to heart (heartshields are lost left to right)
																var shieldIndex = 3 - heartshields;
																if (heartIcons[shieldIndex]) {
																	var parent = heartIcons[shieldIndex].parent;
																	if (parent) {
																		parent.removeChild(heartIcons[shieldIndex]);
																	}
																	heartIcons[shieldIndex] = LK.getAsset('heart', {
																		anchorX: 0,
																		anchorY: 0,
																		x: heartStartX + shieldIndex * (heartWidth + heartSpacing),
																		y: heartY
																	});
																	gamescreen.addChild(heartIcons[shieldIndex]);
																}
																heartshields--; // Lose one heartshield
																// No miss feedback, sound, or other penalties for heartshield hits
															} else {
																// All heartshields are gone, now show miss effects and lose hearts
																beat.showFeedback('miss');
																combo = 0;
																perfectCombo = 0;
																comboTxt.setText('');
																LK.effects.flashObject(kickTarget, 0xff0000, 200);
																LK.getSound('miss').play();
																// Show MISS feedback text at hit zone
																var missText = new Text2('MISS', {
																	size: 60,
																	fill: 0xff0000
																});
																missText.scale.set(2, 2);
																missText.anchor.set(0.5, 0.5);
																missText.x = HIT_ZONE_X;
																missText.y = HIT_ZONE_Y - 180;
																game.addChild(missText);
																tween(missText, {
																	alpha: 0,
																	y: missText.y - 100
																}, {
																	duration: 600,
																	easing: tween.easeOut,
																	onFinish: function onFinish() {
																		missText.destroy();
																	}
																});
																// Lose a heart and update heart icon (hearts are lost left to right)
																if (lives > 0) {
																	var lostIndex = MAX_LIVES - lives;
																	if (heartIcons[lostIndex]) {
																		var parent = heartIcons[lostIndex].parent;
																		if (parent) {
																			parent.removeChild(heartIcons[lostIndex]);
																		}
																		heartIcons[lostIndex] = LK.getAsset('lostheart', {
																			anchorX: 0,
																			anchorY: 0,
																			x: heartStartX + lostIndex * (heartWidth + heartSpacing),
																			y: heartY
																		});
																		gamescreen.addChild(heartIcons[lostIndex]);
																	}
																	lives--;
																}
																if (lives <= 0) {
																	gameOver();
																}
															}
														}
														// Track if beat has reached the hit zone at least once
														if (typeof beat.hasReachedHitZone === "undefined") {
															beat.hasReachedHitZone = false;
														}
														if (!beat.hasReachedHitZone && beat.x >= HIT_ZONE_X - HIT_WINDOW) {
															beat.hasReachedHitZone = true;
														}
														// Remove if off screen
														if (beat.x > TIMELINE_X + TIMELINE_WIDTH + 100) {
															beat.destroy();
															beats.splice(i, 1);
														}
														// Update lastX for next frame
														beat.lastX = beat.x;
													}
												};
												// Start game state
												gameActive = true;
												gamePaused = false;
												// Defensive: clear beats array
												for (var i = 0; i < beats.length; i++) {
													beats[i].destroy();
												}
												beats = [];
												// Remove any beat at the kick target
												for (var i = beats.length - 1; i >= 0; i--) {
													if (typeof beats[i].x !== "undefined" && Math.abs(beats[i].x - HIT_ZONE_X) < 2 || typeof beats[i].x !== "undefined" && beats[i].x >= HIT_ZONE_X - HIT_WINDOW) {
														beats[i].destroy();
														beats.splice(i, 1);
													}
												}
												// Reset hearts
												for (var i = 0; i < heartIcons.length; i++) {
													if (heartIcons[i] && heartIcons[i].parent) {
														heartIcons[i].parent.removeChild(heartIcons[i]);
													}
												}
												heartIcons = [];
												heartshields = MAX_LIVES;
												lives = MAX_LIVES;
												for (var i = 0; i < MAX_LIVES; i++) {
													var heartAsset = LK.getAsset('heartshield', {
														anchorX: 0,
														anchorY: 0,
														x: heartStartX + i * (heartWidth + heartSpacing),
														y: heartY
													});
													gamescreen.addChild(heartAsset);
													heartIcons.push(heartAsset);
												}
												// Set timeout to remove heartshields after 3 seconds in chapter 2
												tween({}, {}, {
													duration: 3000,
													onFinish: function onFinish() {
														if (gameActive && heartshields > 0) {
															heartshields = 0;
															updateHeartsUI(lives, heartshields);
														}
													}
												});
											});
										}; // Remove tap handler
										// Remove all ch2pan panels and container
										for (var i = 0; i < ch2panAssets.length; i++) {
											if (ch2panAssets[i] && ch2panAssets[i].parent) {
												ch2panAssets[i].parent.removeChild(ch2panAssets[i]);
												ch2panAssets[i].destroy();
											}
										}
										if (ch2PanelContainer && ch2PanelContainer.parent) {
											ch2PanelContainer.parent.removeChild(ch2PanelContainer);
										}
										// Show "Get ready to feel the beat." message before starting chapter 2
										var readyMessage = new Text2('Get ready to feel the beat.', {
											size: 60,
											fill: 0xffffff
										});
										readyMessage.scale.set(2, 2);
										readyMessage.anchor.set(0.5, 0.5);
										readyMessage.x = 2048 / 2;
										readyMessage.y = 2732 / 2;
										readyMessage.alpha = 0;
										game.addChild(readyMessage);
										// Fade in the message
										tween(readyMessage, {
											alpha: 1
										}, {
											duration: 1000,
											easing: tween.easeOut,
											onFinish: function onFinish() {
												// Hold for 2 seconds then fade out
												tween(readyMessage, {
													alpha: 0
												}, {
													duration: 1000,
													delay: 2000,
													easing: tween.easeOut,
													onFinish: function onFinish() {
														readyMessage.destroy();
														// Start chapter 2 after message disappears
														startChapter2();
													}
												});
											}
										});
										game.down = originalGameDown;
										ch2PanelActive = false;
										return;
									}
									// If not first panel, do nothing (skip all ch2pan panels)
									ch2PanelActive = false;
								}
								// Save original game.down so we can restore it after panels
								var originalGameDown = game.down;
								// Start with ch2pan1
								ch2CurrentPanel = 0;
								showCh2Panel(ch2CurrentPanel);
								// Override game.down to advance panels on tap anywhere
								game.down = function (x, y, obj) {
									if (!ch2PanelActive) {
										if (typeof originalGameDown === "function") {
											originalGameDown(x, y, obj);
										}
										return;
									}
									// If we've shown all panels, start chapter 2
									if (ch2CurrentPanel >= ch2PanNames.length - 1) {
										ch2PanelActive = false;
										// Fade out all panels simultaneously
										var fadeCount = ch2panAssets.length;
										var faded = 0;
										for (var i = 0; i < ch2panAssets.length; i++) {
											if (ch2panAssets[i] && ch2panAssets[i].parent) {
												tween(ch2panAssets[i], {
													alpha: 0
												}, {
													duration: 400,
													easing: tween.easeOut,
													onFinish: function () {
														if (this.parent) {
															this.parent.removeChild(this);
														}
														this.destroy();
														faded++;
														// After all panels faded, start chapter 2
														if (faded === fadeCount) {
															if (ch2PanelContainer && ch2PanelContainer.parent) {
																ch2PanelContainer.parent.removeChild(ch2PanelContainer);
															}
															// Start chapter 2 with showCh2Panel function that handles the actual chapter 2 logic
															showCh2Panel(ch2PanNames.length); // This will trigger chapter 2 start
														}
													}.bind(ch2panAssets[i])
												});
											}
										}
										return;
									}
									// Advance to next panel
									ch2CurrentPanel++;
									showCh2Panel(ch2CurrentPanel);
								};
							}, nightshiftDuration);
						}, 1000);
					}
				});
			}, 3000);
		}, 1000);
	}
	// Check for BPM progression every 10,000 points (only for non-tutorial)
	if (!isTutorial && score >= 10000 && score - lastBpmUpgradeScore >= 10000) {
		if (bpm < 120) {
			// Don't increase BPM beyond 120
			lastBpmUpgradeScore = score;
			var oldBpm = bpm;
			bpm = Math.min(120, bpm + 5); // Increase by 5 BPM, cap at 120
			if (bpm !== oldBpm) {
				// Update BPM display to show current playing song BPM
				if (bpmDisplayTxt) {
					// Show the actual BPM of the currently playing song, not the settings BPM
					var currentPlayingBpm = bpm; // This is the actual BPM being used for gameplay
					bpmDisplayTxt.setText(currentPlayingBpm + ' BPM');
				}
				// Update beat interval for new beats
				beatInterval = 60 / bpm;
				// Get new music ID
				var newMusicId = '100BPMSynthWave'; // Default fallback
				if (bpm === 40) {
					newMusicId = '40BPMDeepVibes';
				} else if (bpm === 45) {
					newMusicId = '45BPMChillWave';
				} else if (bpm === 50) {
					newMusicId = '50BPMSlowMotion';
				} else if (bpm === 55) {
					newMusicId = '55BPMDreamscape';
				} else if (bpm === 60) {
					newMusicId = '60BPMLowKey';
				} else if (bpm === 65) {
					newMusicId = '65BPMRelaxed';
				} else if (bpm === 70) {
					newMusicId = '70BPMCalmVibes';
				} else if (bpm === 75) {
					newMusicId = '75BPMElectricDreams';
				} else if (bpm === 80) {
					newMusicId = '80BPMNeonNights';
				} else if (bpm === 85) {
					newMusicId = '85BPMCyberFlow';
				} else if (bpm === 90) {
					newMusicId = '90BPMDigitalPulse';
				} else if (bpm === 95) {
					newMusicId = '95BPMFutureBass';
				} else if (bpm === 100) {
					newMusicId = '100BPMSynthWave';
				} else if (bpm === 105) {
					newMusicId = '105BPMHyperDrive';
				} else if (bpm === 110) {
					newMusicId = '110BPMTechnoRush';
				} else if (bpm === 115) {
					newMusicId = '115BPMHighEnergy';
				} else if (bpm === 120) {
					newMusicId = '120BPMMaximum';
				}
				// Fade out current music and fade in new music
				var volumeMultiplier = soundVolume / 100;
				LK.stopMusic();
				LK.playMusic(newMusicId, {
					volume: 0,
					loop: true,
					fade: {
						start: 0,
						end: 1 * volumeMultiplier,
						duration: 1000
					}
				});
				// Play scratch sound when BPM changes
				LK.getSound('scratch').play();
				// Update song name display
				if (songNameTxt) {
					var songName = newMusicId.replace(/^\d+BPM/, '');
					songNameTxt.setText(songName);
				}
				// --- BPM değiştiğinde waveform'u güncelle ---
				drawWaveformLK();
				updateDebugText();
			}
		}
	}
	// Move beats and check for misses
	for (var i = beats.length - 1; i >= 0; i--) {
		var beat = beats[i];
		// Move beats at constant speed from left to right
		var currentBeatSpeed = getBeatSpeed(); // Always 400 px/s
		// Calculate elapsed time since beat was spawned
		var elapsedTime = songTime - (beat.beatTime - HIT_ZONE_X / currentBeatSpeed);
		// Move beat from left edge at constant speed
		beat.x = elapsedTime * currentBeatSpeed;
		// --- YENİ: Beat yörüngesi waveform ile senkron olsun ---
		var params = getWaveParams(bpm);
		var waveLength = params.waveLength;
		var waveAmplitude = params.waveAmplitude;
		var phase = -HIT_ZONE_X / waveLength;
		beat.y = HIT_ZONE_Y + Math.sin(beat.x / waveLength + phase) * waveAmplitude;
		// ... existing code ...
		// Ensure beat stays within reasonable bounds
		if (beat.x < -400) {
			beat.x = -400;
		}
		if (beat.x > TIMELINE_X + TIMELINE_WIDTH + 400) {
			beat.x = TIMELINE_X + TIMELINE_WIDTH + 400;
		}
		// Ensure beatMarker always passes below kickTarget
		if (kickTarget && kickTarget.parent) {
			var kickTargetIndex = kickTarget.parent.getChildIndex(kickTarget);
			var beatIndex = beat.parent.getChildIndex(beat);
			if (beatIndex > kickTargetIndex) {
				beat.parent.setChildIndex(beat, kickTargetIndex);
			}
		}
		// Track lastX for miss detection
		if (typeof beat.lastX === "undefined") {
			beat.lastX = beat.x;
		}
		// Only check for miss if the beat has passed the hit zone (not before)
		// Also, do not count as miss if the beat has not yet reached the hit zone at least once
		if (!beat.hit && beat.active && typeof beat.hasReachedHitZone !== "undefined" && beat.hasReachedHitZone && beat.lastX <= HIT_ZONE_X + HIT_WINDOW && beat.x > HIT_ZONE_X + HIT_WINDOW) {
			// Missed
			beat.active = false;
			misses++;
			// Replace the beat marker asset with missCircle for missed beats
			var missedAsset = LK.getAsset('missCircle', {
				anchorX: 0.5,
				anchorY: 0.5,
				x: beat.x,
				// Start from current beat position
				y: beat.y,
				alpha: 0.8,
				width: 60,
				height: 59.77
			});
			// Replace the beat's current asset with missCircle
			if (beat.parent) {
				// Store parent reference before removing beat
				var beatParent = beat.parent;
				// Remove the old beat marker from its parent
				beatParent.removeChild(beat);
				// Add the missed asset to the stored parent reference
				beatParent.addChild(missedAsset);
				// Update the beat reference to point to the new asset
				beats[i] = missedAsset;
				// Copy over important properties
				missedAsset.beatTime = beat.beatTime;
				missedAsset.type = beat.type;
				missedAsset.active = false;
				missedAsset.hit = false;
				missedAsset.lastX = beat.x; // Use current position
				missedAsset.hasReachedHitZone = true;
				missedAsset.isMissed = true; // Mark as missed for special handling
				// Add empty updateTrail method to prevent errors
				missedAsset.updateTrail = function () {};
				// Destroy the original beat asset
				beat.destroy();
			}
			// Handle heartshield system
			if (heartshields > 0) {
				// Convert leftmost heartshield to heart (heartshields are lost left to right)
				var shieldIndex = 3 - heartshields; // Get the leftmost heartshield index
				if (heartIcons[shieldIndex]) {
					var parent = heartIcons[shieldIndex].parent;
					if (parent) {
						parent.removeChild(heartIcons[shieldIndex]);
					}
					heartIcons[shieldIndex] = LK.getAsset('heart', {
						anchorX: 0,
						anchorY: 0,
						x: heartStartX + shieldIndex * (heartWidth + heartSpacing),
						y: heartY
					});
					gamescreen.addChild(heartIcons[shieldIndex]);
				}
				heartshields--; // Lose one heartshield
				// No miss feedback, sound, or other penalties for heartshield hits
			} else {
				// All heartshields are gone, now show miss effects and lose hearts
				beat.showFeedback('miss');
				combo = 0;
				perfectCombo = 0; // Reset perfect combo on miss
				// missTxt removed, only hearts for lives
				comboTxt.setText('');
				LK.effects.flashObject(kickTarget, 0xff0000, 200);
				// Play miss sound
				LK.getSound('miss').play();
				// Show MISS feedback text at hit zone
				var missText = new Text2('MISS', {
					size: 60,
					fill: 0xff0000
				});
				missText.scale.set(2, 2);
				missText.anchor.set(0.5, 0.5);
				missText.x = HIT_ZONE_X;
				missText.y = HIT_ZONE_Y - 180;
				game.addChild(missText);
				tween(missText, {
					alpha: 0,
					y: missText.y - 100
				}, {
					duration: 600,
					easing: tween.easeOut,
					onFinish: function onFinish() {
						missText.destroy();
					}
				});
				// Lose a heart and update heart icon (hearts are lost left to right)
				if (lives > 0) {
					var lostIndex = MAX_LIVES - lives; // soldan sağa kaybetmek için
					if (heartIcons[lostIndex]) {
						var parent = heartIcons[lostIndex].parent;
						if (parent) {
							parent.removeChild(heartIcons[lostIndex]);
						}
						heartIcons[lostIndex] = LK.getAsset('lostheart', {
							anchorX: 0,
							anchorY: 0,
							x: heartStartX + lostIndex * (heartWidth + heartSpacing),
							y: heartY
						});
						gamescreen.addChild(heartIcons[lostIndex]);
					}
					lives--;
				}
				if (lives <= 0) {
					gameOver();
				}
			}
		}
		// Track if beat has reached the hit zone at least once
		if (typeof beat.hasReachedHitZone === "undefined") {
			beat.hasReachedHitZone = false;
		}
		if (!beat.hasReachedHitZone && beat.x >= HIT_ZONE_X - HIT_WINDOW) {
			beat.hasReachedHitZone = true;
		}
		// Remove if off screen
		if (beat.x > TIMELINE_X + TIMELINE_WIDTH + 100) {
			beat.destroy();
			beats.splice(i, 1);
		}
		// Update lastX for next frame
		beat.lastX = beat.x;
	}
};
// --- Input handling ---
var isTouching = false;
game.down = function (x, y, obj) {
	if (!gameActive || gamePaused) {
		return;
	}
	// Don't register hits during ready screen
	if (readyContainer.visible) {
		return;
	}
	// Don't register hits when gamescreen is not visible (e.g., in main menu)
	if (!gamescreen.visible) {
		return;
	}
	// Only register if touch is in hit zone
	var dx = x - HIT_ZONE_X;
	var dy = y - HIT_ZONE_Y;
	var dist = Math.sqrt(dx * dx + dy * dy);
	if (dist < 120) {
		isTouching = true;
		// Animate kick target
		kickTarget.destroy();
		kickTarget = LK.getAsset('kickActive', {
			anchorX: 0.5,
			anchorY: 0.5,
			x: HIT_ZONE_X,
			y: HIT_ZONE_Y
		});
		gamescreen.addChild(kickTarget);
		// Check for beat hit
		var hit = false;
		for (var i = 0; i < beats.length; i++) {
			var beat = beats[i];
			if (beat.active && !beat.hit && Math.abs(beat.x - HIT_ZONE_X) < HIT_WINDOW) {
				// Calculate timing accuracy
				var distFromCenter = Math.abs(beat.x - HIT_ZONE_X);
				var feedbackType = '';
				var feedbackText = '';
				var feedbackColor = 0xffffff;
				var scoreAdd = 0;
				if (distFromCenter < 20) {
					feedbackType = 'perfect';
					feedbackText = 'PERFECT!';
					feedbackColor = 0xffe600;
					scoreAdd = 200;
					perfectCount++; // Increment perfect count
					perfectCombo++; // Increment consecutive perfect hits
					if (perfectCombo > maxPerfectCombo) {
						maxPerfectCombo = perfectCombo; // Track maximum consecutive perfect hits
					}
					// Intense screen shake for perfect hits, increasing with combo
					var perfectShakeIntensity = Math.min(15, 5 + perfectCombo * 0.5); // Max intensity of 15
					var perfectShakeDuration = Math.min(400, 200 + perfectCombo * 10); // Max duration of 400ms
					shakeScreen(perfectShakeIntensity, perfectShakeDuration);
					// Perfect vuruş için parlak mor flash
					var flashAlpha = Math.min(0.4, 0.2 + perfectCombo * 0.01); // Combo arttıkça daha belirgin
					flashScreen(0x8a2be2, flashAlpha, perfectShakeDuration);
				} else if (distFromCenter < 40) {
					feedbackType = 'awesome';
					feedbackText = 'AWESOME!';
					feedbackColor = 0x00e6ff;
					scoreAdd = 150;
					perfectCombo = 0; // Reset perfect combo for non-perfect hit
					shakeScreen(4, 200); // Mild shake for awesome hits
					flashScreen(0x00e6ff, 0.15, 100); // Mavi flash efekti
				} else if (distFromCenter < 70) {
					feedbackType = 'good';
					feedbackText = 'GOOD';
					feedbackColor = 0x00ff00;
					scoreAdd = 100;
					perfectCombo = 0; // Reset perfect combo for non-perfect hit
					shakeScreen(3, 160); // Very mild shake for good hits
					flashScreen(0x00ff00, 0.1, 80); // Yeşil flash efekti
				} else {
					feedbackType = 'ok';
					feedbackText = 'OK';
					feedbackColor = 0xcccccc;
					scoreAdd = 50;
					perfectCombo = 0; // Reset perfect combo for non-perfect hit
					shakeScreen(2, 120); // Minimal shake for ok hits
					flashScreen(0xcccccc, 0.05, 60); // Hafif gri flash efekti
				}
				beat.hit = true;
				beat.active = false;
				hit = true;
				score += scoreAdd;
				combo += 1;
				// Trail renklerini güncelle
				updateBeatTrailOnHit(beat, feedbackType);
				if (combo > maxCombo) {
					maxCombo = combo; // Track maximum combo achieved
				}
				LK.setScore(score);
				scoreTxt.setText(score);
				comboTxt.setText('Combo: ' + combo);
				// Show feedback text at hit zone
				var feedbackTxt = new Text2(feedbackText, {
					size: 60,
					fill: feedbackColor
				});
				feedbackTxt.scale.set(2, 2);
				feedbackTxt.anchor.set(0.5, 0.5);
				feedbackTxt.x = HIT_ZONE_X;
				feedbackTxt.y = HIT_ZONE_Y - 180;
				game.addChild(feedbackTxt);
				// Show combo multiplier for perfect hits
				if (feedbackType === 'perfect' && perfectCombo > 1) {
					var comboMultiplierTxt = new Text2('x' + perfectCombo, {
						size: 21,
						fill: 0xff8c00
					});
					comboMultiplierTxt.scale.set(4, 4);
					comboMultiplierTxt.anchor.set(0, 0.5);
					comboMultiplierTxt.x = HIT_ZONE_X + 195;
					comboMultiplierTxt.y = HIT_ZONE_Y - 220;
					game.addChild(comboMultiplierTxt);
					tween(comboMultiplierTxt, {
						alpha: 0,
						y: comboMultiplierTxt.y - 100
					}, {
						duration: 600,
						easing: tween.easeOut,
						onFinish: function onFinish() {
							comboMultiplierTxt.destroy();
						}
					});
				}
				tween(feedbackTxt, {
					alpha: 0,
					y: feedbackTxt.y - 100
				}, {
					duration: 600,
					easing: tween.easeOut,
					onFinish: function onFinish() {
						feedbackTxt.destroy();
					}
				});
				beat.showFeedback('good');
				// Play sounds based on selection - all selected sounds
				if (selectedKickSound !== '') {
					LK.getSound(selectedKickSound).play();
				}
				if (selectedHihatSound !== '') {
					LK.getSound(selectedHihatSound).play();
				}
				if (selectedSnareClapSound !== '') {
					LK.getSound(selectedSnareClapSound).play();
				}
				if (selectedPercussionSound !== '') {
					LK.getSound(selectedPercussionSound).play();
				}
				LK.effects.flashObject(kickTarget, feedbackColor, 200);
				// Replace the beat's current asset based on timing quality while maintaining position and movement
				var newAssetId;
				var tintColor = 0xffffff;
				if (feedbackType === 'perfect') {
					// Perfect hits: perfectcircle asset
					newAssetId = 'perfectcircle';
					tintColor = 0xffffff;
				} else if (feedbackType === 'awesome' || feedbackType === 'good' || feedbackType === 'ok') {
					// Good/awesome/ok hits: green kickActive
					newAssetId = 'kickActive';
					tintColor = 0x00ff00;
				}
				// Replace the beat's visual asset while maintaining its position and movement
				if (beat.parent) {
					// Store parent and current position
					var beatParent = beat.parent;
					var currentX = beat.x;
					var currentY = beat.y;
					// Remove the old beat marker from its parent
					beatParent.removeChild(beat);
					// Create new asset with current position
					var newBeatAsset = LK.getAsset(newAssetId, {
						anchorX: 0.5,
						anchorY: 0.5,
						x: currentX,
						y: currentY,
						tint: tintColor,
						width: 60,
						height: 59.77
					});
					// Add the new asset to the stored parent reference
					beatParent.addChild(newBeatAsset);
					// Update the beat reference to point to the new asset
					beats[i] = newBeatAsset;
					// Copy over important properties
					newBeatAsset.beatTime = beat.beatTime;
					newBeatAsset.type = beat.type;
					newBeatAsset.active = false; // Mark as inactive since it was hit
					newBeatAsset.hit = true;
					newBeatAsset.assetType = newAssetId; // Trail renkleri için asset tipini güncelle
					newBeatAsset.lastX = currentX;
					newBeatAsset.hasReachedHitZone = true;
					// Trail özelliklerini aktar
					newBeatAsset.lastTrailTime = Date.now();
					newBeatAsset.trailInterval = 15;
					newBeatAsset.updateTrail = beat.updateTrail;
					updateBeatTrailOnHit(newBeatAsset, feedbackType);
					// Destroy the original beat asset
					beat.destroy();
				}
				// BPM remains constant at settings value - no difficulty increase during gameplay
				break;
			}
		}
		if (!hit) {
			// Missed (tapped at wrong time)
			misses++;
			combo = 0;
			perfectCombo = 0; // Reset perfect combo on wrong time
			// missTxt removed, only hearts for lives
			comboTxt.setText('');
			// Play sounds based on selection - all selected sounds
			if (selectedKickSound !== '') {
				LK.getSound(selectedKickSound).play();
			}
			if (selectedHihatSound !== '') {
				LK.getSound(selectedHihatSound).play();
			}
			if (selectedSnareClapSound !== '') {
				LK.getSound(selectedSnareClapSound).play();
			}
			if (selectedPercussionSound !== '') {
				LK.getSound(selectedPercussionSound).play();
			}
			LK.effects.flashObject(kickTarget, 0xff0000, 200);
			// Show WRONG TIME feedback text at hit zone
			var wrongTimeText = new Text2('WRONG TIME', {
				size: 60,
				fill: 0xff4500
			});
			wrongTimeText.scale.set(2, 2);
			wrongTimeText.anchor.set(0.5, 0.5);
			wrongTimeText.x = HIT_ZONE_X;
			wrongTimeText.y = HIT_ZONE_Y - 180;
			game.addChild(wrongTimeText);
			tween(wrongTimeText, {
				alpha: 0,
				y: wrongTimeText.y - 100
			}, {
				duration: 600,
				easing: tween.easeOut,
				onFinish: function onFinish() {
					wrongTimeText.destroy();
				}
			});
			// Only lose a life if countdown is not visible
			if (!countdownContainer.visible) {
				// Handle heartshield system for wrong time hits
				if (heartshields > 0) {
					// Convert leftmost heartshield to heart (heartshields are lost left to right)
					var shieldIndex = 3 - heartshields; // Get the leftmost heartshield index
					if (heartIcons[shieldIndex]) {
						var parent = heartIcons[shieldIndex].parent;
						if (parent) {
							parent.removeChild(heartIcons[shieldIndex]);
						}
						heartIcons[shieldIndex] = LK.getAsset('heart', {
							anchorX: 0,
							anchorY: 0,
							x: heartStartX + shieldIndex * (heartWidth + heartSpacing),
							y: heartY
						});
						gamescreen.addChild(heartIcons[shieldIndex]);
					}
					heartshields--; // Lose one heartshield
					// No miss feedback, sound, or other penalties for heartshield hits
				} else {
					// All heartshields are gone, now lose hearts
					// Lose a life and update heart icon
					if (lives > 0) {
						var lostIndex = MAX_LIVES - lives; // soldan sağa kaybetmek için
						if (heartIcons[lostIndex]) {
							var parent = heartIcons[lostIndex].parent;
							if (parent) {
								parent.removeChild(heartIcons[lostIndex]);
							}
							heartIcons[lostIndex] = LK.getAsset('lostheart', {
								anchorX: 0,
								anchorY: 0,
								x: heartStartX + lostIndex * (heartWidth + heartSpacing),
								y: heartY
							});
							gamescreen.addChild(heartIcons[lostIndex]);
						}
						lives--;
					}
					if (lives <= 0) {
						gameOver();
					}
				}
			}
		}
	}
};
game.up = function (x, y, obj) {
	isTouching = false;
	// Restore kick target
	kickTarget.destroy();
	kickTarget = LK.getAsset('kickTarget', {
		anchorX: 0.5,
		anchorY: 0.5,
		x: HIT_ZONE_X,
		y: HIT_ZONE_Y
	});
	gamescreen.addChild(kickTarget);
};
// --- Game over ---
function gameOver() {
	gameActive = false;
	LK.effects.flashScreen(0xff0000, 800);
	showDefeatScreen();
	LK.stopMusic();
}
// --- Glow Effect for Purple Elements ---
function addGlowEffect(element, glowColor, intensity) {
	if (!element || !element.visible) {
		return;
	}
	// Create glow animation using tint and alpha
	var originalTint = element.tint || 0xffffff;
	var glowTint = glowColor || 0xaa00ff; // Default purple glow
	var glowIntensity = intensity || 0.3;
	// Pulsing glow effect
	function startGlow() {
		tween(element, {
			alpha: 1 - glowIntensity
		}, {
			duration: 800,
			easing: tween.easeInOut,
			yoyo: true,
			repeat: -1 // Infinite repeat
		});
	}
	startGlow();
}
// Apply glow to purple elements
function applyGlowToVioletElements() {
	// Apply glow to kick target (blue/violet color)
	if (kickTarget) {
		addGlowEffect(kickTarget, 0x8a2be2, 0.2);
	}
	// Apply glow to main menu buttons (purple/violet elements)
	if (startBtn) {
		addGlowEffect(startBtn, 0x8a2be2, 0.15);
	}
	if (settingsBtn) {
		addGlowEffect(settingsBtn, 0x8a2be2, 0.15);
	}
	if (creditsBtn) {
		addGlowEffect(creditsBtn, 0x8a2be2, 0.15);
	}
	if (supportBtn) {
		addGlowEffect(supportBtn, 0x8a2be2, 0.15);
	}
	// Apply glow to BPM circle container if it exists
	if (typeof bpmCircleContainer !== 'undefined' && bpmCircleContainer) {
		addGlowEffect(bpmCircleContainer, 0x8a2be2, 0.2);
	}
}
// Initialize glow effects
applyGlowToVioletElements();
// --- Win condition ---
function checkWin() {
	if (score >= 5000) {
		gameActive = false;
		LK.effects.flashScreen(0x00ff00, 800);
		LK.showYouWin();
		LK.stopMusic();
	}
}
// --- Defeat Screen Implementation ---
var defeatContainer = new Container();
defeatContainer.visible = false;
game.addChild(defeatContainer);
var maxCombo = 0; // Track maximum combo achieved
var perfectCount = 0; // Track number of perfect hits
var perfectCombo = 0; // Track current consecutive perfect hits
var maxPerfectCombo = 0; // Track maximum consecutive perfect hits
var lastBpmUpgradeScore = 0; // Track last score where BPM was upgraded
var gamePaused = false; // Track if game is paused
var pauseContainer = new Container();
pauseContainer.visible = false;
game.addChild(pauseContainer);
function showDefeatScreen() {
	defeatContainer.visible = true;
	defeatContainer.removeChildren();
	gamescreen.visible = false;
	bpmDisplayTxt.visible = false;
	songNameTxt.visible = false;
	// Add defeat background using defeatbg asset
	var defeatBg = LK.getAsset('defeatbg', {
		anchorX: 0,
		anchorY: 0,
		x: 0,
		y: 0,
		width: 2048,
		height: 2732
	});
	defeatContainer.addChild(defeatBg);
	// Add blurred background behind statistics
	var blurredBg = LK.getAsset('blurred', {
		anchorX: 0.5,
		anchorY: 0.5,
		x: 2048 / 2,
		y: 1185,
		scaleX: 1.35,
		scaleY: 1.35,
		alpha: 1
	});
	defeatContainer.addChild(blurredBg);
	// Game Over title using gameover asset
	var gameOverAsset = LK.getAsset('gameover', {
		anchorX: 0.5,
		anchorY: 0.5,
		x: 2048 / 2,
		y: 600,
		scaleX: 0.5,
		scaleY: 0.5
	});
	defeatContainer.addChild(gameOverAsset);
	// Statistics arranged vertically with 5px spacing
	var statsStartY = 990;
	var statsSpacing = 5;
	var currentY = statsStartY;
	// Score display
	var finalScoreTxt = new Text2("Score: " + score, {
		size: 54,
		fill: 0xffffff
	});
	finalScoreTxt.scale.set(2, 2);
	finalScoreTxt.anchor.set(0, 0.5);
	finalScoreTxt.x = 2048 / 2 - 550;
	finalScoreTxt.y = currentY;
	defeatContainer.addChild(finalScoreTxt);
	currentY += finalScoreTxt.height + statsSpacing;
	// Max combo display
	var maxComboTxt = new Text2("Max Combo: " + maxCombo, {
		size: 54,
		fill: 0x00ff00
	});
	maxComboTxt.scale.set(2, 2);
	maxComboTxt.anchor.set(0, 0.5);
	maxComboTxt.x = 2048 / 2 - 550;
	maxComboTxt.y = currentY;
	defeatContainer.addChild(maxComboTxt);
	currentY += maxComboTxt.height + statsSpacing;
	// Perfect count display
	var perfectCountTxt = new Text2("Perfect: " + perfectCount, {
		size: 54,
		fill: 0xffe600
	});
	perfectCountTxt.scale.set(2, 2);
	perfectCountTxt.anchor.set(0, 0.5);
	perfectCountTxt.x = 2048 / 2 - 550;
	perfectCountTxt.y = currentY;
	defeatContainer.addChild(perfectCountTxt);
	currentY += perfectCountTxt.height + statsSpacing;
	// Max perfect combo display (orange color)
	var maxPerfectComboTxt = new Text2("Max Perfect Combo: " + maxPerfectCombo, {
		size: 54,
		fill: 0xff8c00
	});
	maxPerfectComboTxt.scale.set(2, 2);
	maxPerfectComboTxt.anchor.set(0, 0.5);
	maxPerfectComboTxt.x = 2048 / 2 - 550;
	maxPerfectComboTxt.y = currentY;
	defeatContainer.addChild(maxPerfectComboTxt);
	// Calculate button positions
	var defeatButtonStartY = 1700;
	var defeatButtonHeight = 289;
	var defeatButtonSpacing = 20;
	// Tekrar Oyna (Play Again) Button
	var playAgainBtn = LK.getAsset('tryagain', {
		anchorX: 0.5,
		anchorY: 0.5,
		x: 2048 / 2,
		y: defeatButtonStartY,
		scaleX: 5,
		scaleY: 5
	});
	defeatContainer.addChild(playAgainBtn);
	// Ana Menü (Main Menu) Button
	var mainMenuBtn = LK.getAsset('mainmenu', {
		anchorX: 0.5,
		anchorY: 0.5,
		x: 2048 / 2,
		y: defeatButtonStartY + defeatButtonHeight + defeatButtonSpacing,
		scaleX: 5,
		scaleY: 5
	});
	defeatContainer.addChild(mainMenuBtn);
	// Button event handlers
	playAgainBtn.down = function (x, y, obj) {
		// Play button click sound
		LK.getSound('buttonclick').play();
		// Click animation
		tween(playAgainBtn, {
			scaleX: 4.5,
			scaleY: 4.5
		}, {
			duration: 80,
			easing: tween.easeIn,
			onFinish: function onFinish() {
				tween(playAgainBtn, {
					scaleX: 5,
					scaleY: 5
				}, {
					duration: 120,
					easing: tween.easeOut,
					onFinish: function onFinish() {
						defeatContainer.visible = false;
						maxCombo = 0; // Reset max combo
						perfectCount = 0; // Reset perfect count
						maxPerfectCombo = 0; // Reset max perfect combo
						// Continue from current level - if tutorial not completed, restart tutorial
						if (!tutorialCompleted) {
							currentLevel = 1;
							storage.currentLevel = currentLevel;
						}
						showReadyScreen(function () {
							showCountdown(function () {
								startGame();
							});
						});
					}
				});
			}
		});
	};
	mainMenuBtn.down = function (x, y, obj) {
		// Play button click sound
		LK.getSound('buttonclick').play();
		// Click animation
		tween(mainMenuBtn, {
			scaleX: 4.5,
			scaleY: 4.5
		}, {
			duration: 80,
			easing: tween.easeIn,
			onFinish: function onFinish() {
				tween(mainMenuBtn, {
					scaleX: 5,
					scaleY: 5
				}, {
					duration: 120,
					easing: tween.easeOut,
					onFinish: function onFinish() {
						defeatContainer.visible = false;
						maxCombo = 0; // Reset max combo
						perfectCount = 0; // Reset perfect count
						maxPerfectCombo = 0; // Reset max perfect combo
						mainMenuContainer.visible = true;
						scoreTxt.visible = false;
						comboTxt.visible = false;
						bpmDisplayTxt.visible = false;
						songNameTxt.visible = false;
						gamescreen.visible = false;
						// Stop current music and play menu music with a small delay to ensure proper playback (only if not muted)
						LK.stopMusic();
						if (!musicMuted) {
							LK.setTimeout(function () {
								var volumeMultiplier = soundVolume / 100;
								LK.playMusic('menumusic', {
									volume: 0.8 * volumeMultiplier,
									loop: true
								});
							}, 100);
						}
					}
				});
			}
		});
	};
}
// --- Restart on game over ---
LK.on('gameover', function () {
	showDefeatScreen();
});
LK.on('youwin', function () {
	startGame();
});
// Kalp ve heartshield UI güncelleme fonksiyonu
function updateHeartsUI(lives, heartshields) {
	// Remove all existing heart icons
	for (var i = 0; i < heartIcons.length; i++) {
		if (heartIcons[i] && heartIcons[i].parent) {
			heartIcons[i].parent.removeChild(heartIcons[i]);
		}
	}
	heartIcons = [];
	var heartWidth = 150;
	var totalHeartsWidth = MAX_LIVES * heartWidth + (MAX_LIVES - 1) * heartSpacing;
	var heartStartX = 2048 - 100 - totalHeartsWidth;
	var heartY = scoreTxt.y + 50;
	for (var i = 0; i < MAX_LIVES; i++) {
		var assetId;
		if (i < 3 - heartshields) {
			// Heartshield kaybedilmiş, kalp veya lostheart göster
			if (i < MAX_LIVES - lives) {
				assetId = 'lostheart';
			} else {
				assetId = 'heart';
			}
		} else {
			assetId = 'heartshield';
		}
		var heartAsset = LK.getAsset(assetId, {
			anchorX: 0,
			anchorY: 0,
			x: heartStartX + i * (heartWidth + heartSpacing),
			y: heartY
		});
		gamescreen.addChild(heartAsset);
		heartIcons.push(heartAsset);
	}
}
// Beat asset değiştirme fonksiyonu
function replaceBeatAsset(beat, newAssetId, options) {
	if (!beat.parent) {
		return null;
	}
	var beatParent = beat.parent;
	var currentX = beat.x;
	var currentY = beat.y;
	beatParent.removeChild(beat);
	var assetOptions = {
		anchorX: 0.5,
		anchorY: 0.5,
		x: currentX,
		y: currentY,
		width: 60,
		height: 59.77
	};
	if (options) {
		for (var key in options) {
			assetOptions[key] = options[key];
		}
	}
	var newBeatAsset = LK.getAsset(newAssetId, {});
	beatParent.addChild(newBeatAsset);
	// Copy over important properties
	newBeatAsset.beatTime = beat.beatTime;
	newBeatAsset.type = beat.type;
	newBeatAsset.active = false;
	newBeatAsset.hit = true;
	newBeatAsset.lastX = currentX;
	newBeatAsset.hasReachedHitZone = true;
	// Trail sistemi özelliklerini kopyala ve başlat
	newBeatAsset.lastTrailTime = Date.now();
	newBeatAsset.trailInterval = 15;
	newBeatAsset.assetType = newAssetId;
	// Trail güncelleme metodunu ekle
	newBeatAsset.updateTrail = function () {
		var now = Date.now();
		if (now - this.lastTrailTime > this.trailInterval) {
			var colors = BEAT_COLORS[this.assetType] || BEAT_COLORS.circle;
			// Beat'in hareket yönünü belirle (sağ tarafta mı?)
			var isRightSide = this.x > HIT_ZONE_X;
			var directionMultiplier = isRightSide ? 1 : -1;
			// Parçacık sayısını ve yayılımı ayarla
			var particleCount = 10;
			var spreadX = 20;
			var spreadY = 20;
			for (var i = 0; i < particleCount; i++) {
				// Ana parçacık
				var mainParticle = createTrailParticle(this.x + (Math.random() - 0.5) * spreadX, this.y + (Math.random() - 0.5) * spreadY, colors.mainTrail);
				mainParticle.velocityX = (Math.random() * 2 + 1) * directionMultiplier;
				mainParticle.velocityY = (Math.random() - 0.5) * 3;
				// Yan parçacık 1
				var subParticle1 = createTrailParticle(this.x + (Math.random() - 0.5) * (spreadX * 1.5), this.y + (Math.random() - 0.5) * (spreadY * 1.5), colors.subTrail1);
				subParticle1.width = 4;
				subParticle1.height = 4;
				subParticle1.velocityX = (Math.random() * 1.5 + 0.5) * directionMultiplier;
				subParticle1.velocityY = (Math.random() - 0.5) * 2;
				// Yan parçacık 2
				var subParticle2 = createTrailParticle(this.x + (Math.random() - 0.5) * (spreadX * 1.25), this.y + (Math.random() - 0.5) * (spreadY * 1.25), colors.subTrail2);
				subParticle2.width = 3;
				subParticle2.height = 3;
				subParticle2.velocityX = (Math.random() * 1 + 0.5) * directionMultiplier;
				subParticle2.velocityY = (Math.random() - 0.5) * 1.5;
				// Parçacıkları oyun ekranına ekle ve özelliklerini ayarla
				[mainParticle, subParticle1, subParticle2].forEach(function (p) {
					p.fadeSpeed = 0.03;
					p.gravity = 0.1;
					p.drag = 0.98;
					gamescreen.addChild(p);
					trailParticles.push(p);
				});
			}
			this.lastTrailTime = now;
		}
	};
	// Trail sistemini aktif et
	if (!beats.includes(newBeatAsset)) {
		beats.push(newBeatAsset);
	}
	// Pulse efektini ekle
	newBeatAsset.pulseStartTime = Date.now();
	newBeatAsset.baseScale = newBeatAsset.scale.x;
	newBeatAsset.updateBeatPulse = function () {
		updateBeatPulse(this);
	};
	beat.destroy();
	return newBeatAsset;
}
// Sidechain efekti: Kick çalarken müziği kısa süreli kısmak için
function playKickWithSidechain() {
	// Kick çal
	if (selectedKickSound !== '') {
		LK.getSound(selectedKickSound).play();
	}
	// Müzik volume'unu kısa süreli azalt
	var music = LK.getMusic && LK.getMusic();
	if (music && music.setVolume) {
		var originalVolume = music.volume || 1;
		music.setVolume(originalVolume * 0.7);
		setTimeout(function () {
			music.setVolume(originalVolume);
		}, 120); // 120ms sonra eski volume
	}
}
// === Waveform (Sinüs Yörüngesi) Çizgisi ===
var waveformContainer = new Container();
gamescreen.addChild(waveformContainer);
function drawWaveformLK() {
	waveformContainer.removeChildren();
	var step = 10;
	var params = getWaveParams(bpm);
	var waveLength = params.waveLength;
	var waveAmplitude = params.waveAmplitude;
	var phase = -HIT_ZONE_X / waveLength;
	var prev = null;
	for (var x = 0; x <= 2048; x += step) {
		var y = HIT_ZONE_Y + Math.sin(x / waveLength + phase) * waveAmplitude;
		if (prev) {
			// Mor glow çizgisi (kalın, yarı saydam)
			var glowLine = LK.getAsset('settingsbar', {
				anchorX: 0,
				anchorY: 0.5,
				x: prev.x,
				y: prev.y,
				width: step,
				height: 16,
				alpha: 0.3,
				tint: 0xaa00ff
			});
			waveformContainer.addChild(glowLine);
			// Beyaz çizgi (ince, tam opak)
			var whiteLine = LK.getAsset('settingsbar', {
				anchorX: 0,
				anchorY: 0.5,
				x: prev.x,
				y: prev.y,
				width: step,
				height: 4,
				alpha: 1,
				tint: 0xffffff
			});
			waveformContainer.addChild(whiteLine);
		}
		prev = {
			x: x,
			y: y
		};
	}
}
// Function to get wave parameters based on BPM
function getWaveParams(bpm) {
	bpm = Math.max(40, Math.min(120, bpm));
	var minAmp = 80,
		maxAmp = 500;
	var minLength = 80,
		maxLength = 120;
	var t = (bpm - 40) / (120 - 40);
	var waveAmplitude = minAmp + (maxAmp - minAmp) * t;
	var waveLength = minLength + (maxLength - minLength) * t;
	return {
		waveLength: waveLength,
		waveAmplitude: waveAmplitude
	};
}
// Oyun başında ve BPM değiştiğinde çağır:
drawWaveformLK();
// Screen shake function
function shakeScreen(intensity, duration) {
	// Maksimum shake süresi sınırı (ör. 600ms)
	duration = Math.min(duration, 600);
	var originalX = gamescreen.x;
	var originalY = gamescreen.y;
	var originalBpmX = bpmDisplayTxt.x;
	var originalBpmY = bpmDisplayTxt.y;
	var originalSongX = songNameTxt.x;
	var originalSongY = songNameTxt.y;
	var shakeCount = 0;
	var maxShakes = Math.floor(duration / 16.67); // 60fps
	function shake() {
		if (shakeCount >= maxShakes) {
			// Son sönümleme: pozisyonu yavaşça sıfırla
			tween(gamescreen, {
				x: originalX,
				y: originalY
			}, {
				duration: 120,
				easing: tween.easeOut
			});
			tween(bpmDisplayTxt, {
				x: originalBpmX,
				y: originalBpmY
			}, {
				duration: 120,
				easing: tween.easeOut
			});
			tween(songNameTxt, {
				x: originalSongX,
				y: originalSongY
			}, {
				duration: 120,
				easing: tween.easeOut
			});
			return;
		}
		// Sönümlü intensity: sona yaklaştıkça azalır
		var progress = shakeCount / maxShakes;
		var currentIntensity = intensity * (1 - progress * 0.7); // Sonlara doğru %70 azalır
		var randomX = (Math.random() - 0.5) * 2 * currentIntensity;
		var randomY = (Math.random() - 0.5) * 2 * currentIntensity;
		tween(gamescreen, {
			x: originalX + randomX,
			y: originalY + randomY
		}, {
			duration: 16.67,
			easing: tween.easeOut,
			onFinish: function onFinish() {
				shakeCount++;
				shake();
			}
		});
		// Textler de sallansın
		bpmDisplayTxt.x = originalBpmX + randomX;
		bpmDisplayTxt.y = originalBpmY + randomY;
		songNameTxt.x = originalSongX + randomX;
		songNameTxt.y = originalSongY + randomY;
	}
	shake();
}
// Flash overlay for screen darkening
function createFlashOverlay() {
	if (!window.flashOverlay) {
		window.flashOverlay = LK.getAsset('ch2panF', {
			anchorX: 0,
			anchorY: 0,
			x: 0,
			y: 0,
			width: 2048,
			height: 2732,
			alpha: 0,
			tint: 0x000000
		});
		game.addChild(window.flashOverlay);
	}
	return window.flashOverlay;
}
function flashScreen(color, alpha, duration) {
	var overlay = createFlashOverlay();
	overlay.tint = color;
	// Hızlı flash efekti
	tween(overlay, {
		alpha: alpha
	}, {
		duration: duration * 0.2,
		// Yükselme süresi
		easing: tween.easeOut,
		onFinish: function onFinish() {
			// Yavaşça kaybolma
			tween(overlay, {
				alpha: 0
			}, {
				duration: duration * 0.8,
				// Kaybolma süresi
				easing: tween.easeIn
			});
		}
	});
}
// Trail particle system
function createTrailParticle(x, y, color) {
	var particle = LK.getAsset('circle', {
		anchorX: 0.5,
		anchorY: 0.5,
		x: x,
		y: y,
		width: 6,
		// Daha küçük parçacıklar
		height: 6,
		alpha: 0.5,
		// Biraz daha saydam
		tint: color
	});
	// Fizik özellikleri
	particle.velocityX = 0;
	particle.velocityY = 0;
	particle.drag = 0.95; // Sürtünme
	particle.gravity = 0.2; // Yerçekimi
	particle.life = 1.0; // Yaşam süresi (1.0'dan 0'a)
	particle.fadeSpeed = 0.02; // Her frame'de azalacak yaşam miktarı
	return particle;
}
function updateTrailParticle(particle) {
	// Fizik güncellemesi
	particle.velocityY += particle.gravity;
	particle.velocityX *= particle.drag;
	particle.velocityY *= particle.drag;
	particle.x += particle.velocityX;
	particle.y += particle.velocityY;
	// Yaşam süresi ve opaklık güncellemesi
	particle.life -= particle.fadeSpeed;
	particle.alpha = particle.life * 0.6;
	return particle.life > 0;
}
var trailParticles = [];
function updateTrailSystem() {
	// Tüm parçacıkları güncelle ve ölenleri kaldır
	for (var i = trailParticles.length - 1; i >= 0; i--) {
		var particle = trailParticles[i];
		if (!updateTrailParticle(particle)) {
			gamescreen.removeChild(particle);
			trailParticles.splice(i, 1);
		}
	}
}
// Game update fonksiyonuna trail güncellemesini ekle
var originalGameUpdate = game.update;
game.update = function () {
	originalGameUpdate.call(this);
	// Trail sistemini güncelle
	updateTrailSystem();
	// Tüm beatlerin trail'lerini güncelle
	for (var i = 0; i < beats.length; i++) {
		var beat = beats[i];
		// Hem aktif hem de hit olmuş beatlerin trail'lerini güncelle
		if (beat.active && !beat.hit || beat.hit && beat.x > HIT_ZONE_X) {
			if (typeof beat.updateTrail === 'function') {
				beat.updateTrail();
			}
			if (typeof beat.updateBeatPulse === 'function') {
				beat.updateBeatPulse();
			} else {
				updateBeatPulse(beat);
			}
		}
	}
};
// Beat renk konfigürasyonları
var BEAT_COLORS = {
	'perfectcircle': {
		mainTrail: 0xFFD700,
		// Altın sarısı
		subTrail1: 0xFFA500,
		// Turuncu
		subTrail2: 0xFFFF00 // Sarı
	},
	'kickActive': {
		mainTrail: 0x00ff00,
		// Yeşil
		subTrail1: 0x98fb98,
		// Açık yeşil
		subTrail2: 0x32cd32 // Lime yeşil
	},
	'circle': {
		mainTrail: 0x8a2be2,
		// Mor
		subTrail1: 0x00e6ff,
		// Mavi
		subTrail2: 0xff1493 // Pembe
	},
	'miss': {
		mainTrail: 0xFF0000,
		// Kırmızı
		subTrail1: 0xFF4444,
		// Açık kırmızı
		subTrail2: 0xCC0000 // Koyu kırmızı
	},
	'perfect': {
		mainTrail: 0xFFD700,
		// Altın sarısı
		subTrail1: 0xFFA500,
		// Turuncu
		subTrail2: 0xFFFF00 // Sarı
	},
	'awesome': {
		mainTrail: 0x00FFFF,
		// Cyan
		subTrail1: 0x40E0D0,
		// Turkuaz
		subTrail2: 0x00CED1 // Koyu turkuaz
	},
	'good': {
		mainTrail: 0x32CD32,
		// Lime yeşil
		subTrail1: 0x90EE90,
		// Açık yeşil
		subTrail2: 0x228B22 // Orman yeşili
	},
	'ok': {
		mainTrail: 0x4169E1,
		// Royal mavi
		subTrail1: 0x1E90FF,
		// Dodger mavi
		subTrail2: 0x0000CD // Orta koyu mavi
	}
};
// BPM'e göre pulse efekti
function updateBeatPulse(beat) {
	if (!beat.pulseStartTime) {
		beat.pulseStartTime = Date.now();
		beat.baseScale = beat.scale.x;
	}
	var bpmInterval = 60000 / bpm; // BPM'i milisaniyeye çevir
	var timeSinceStart = Date.now() - beat.pulseStartTime;
	var pulsePhase = timeSinceStart % bpmInterval / bpmInterval;
	// Sinüs dalgası ile pulse efekti
	var pulseScale = 0.15; // Pulse büyüklüğü
	var scaleFactor = 1 + Math.sin(pulsePhase * Math.PI * 2) * pulseScale;
	beat.scale.x = beat.baseScale * scaleFactor;
	beat.scale.y = beat.baseScale * scaleFactor;
}
// Trail renklerini güncelleme fonksiyonu
function updateTrailsColor(beat, newColorType, customColors) {
	if (!beat || !beat.updateTrail) {
		return null;
	}
	// Yeni renk tipini kaydet
	beat.trailColorType = newColorType;
	// Özel renkler verilmişse onları kullan, yoksa BEAT_COLORS'dan al
	var colors;
	if (customColors) {
		colors = {
			mainTrail: customColors.mainTrail || 0xFFFFFF,
			subTrail1: customColors.subTrail1 || 0xCCCCCC,
			subTrail2: customColors.subTrail2 || 0x999999
		};
	} else {
		colors = BEAT_COLORS[newColorType] || BEAT_COLORS.circle;
	}
	// Trail güncelleme fonksiyonunu yeni renklerle güncelle
	beat.updateTrail = function () {
		var now = Date.now();
		if (now - this.lastTrailTime > this.trailInterval) {
			// Beat'in hareket yönünü belirle (sağ tarafta mı?)
			var isRightSide = this.x > HIT_ZONE_X;
			var directionMultiplier = isRightSide ? 1 : -1;
			// Parçacık sayısını ve yayılımı ayarla
			var particleCount = 10;
			var spreadX = 20;
			var spreadY = 20;
			for (var i = 0; i < particleCount; i++) {
				// Ana parçacık
				var mainParticle = createTrailParticle(this.x + (Math.random() - 0.5) * spreadX, this.y + (Math.random() - 0.5) * spreadY, colors.mainTrail);
				mainParticle.velocityX = (Math.random() * 2 + 1) * directionMultiplier;
				mainParticle.velocityY = (Math.random() - 0.5) * 3;
				// Yan parçacık 1
				var subParticle1 = createTrailParticle(this.x + (Math.random() - 0.5) * (spreadX * 1.5), this.y + (Math.random() - 0.5) * (spreadY * 1.5), colors.subTrail1);
				subParticle1.width = 4;
				subParticle1.height = 4;
				subParticle1.velocityX = (Math.random() * 1.5 + 0.5) * directionMultiplier;
				subParticle1.velocityY = (Math.random() - 0.5) * 2;
				// Yan parçacık 2
				var subParticle2 = createTrailParticle(this.x + (Math.random() - 0.5) * (spreadX * 1.25), this.y + (Math.random() - 0.5) * (spreadY * 1.25), colors.subTrail2);
				subParticle2.width = 3;
				subParticle2.height = 3;
				subParticle2.velocityX = (Math.random() * 1 + 0.5) * directionMultiplier;
				subParticle2.velocityY = (Math.random() - 0.5) * 1.5;
				// Parçacıkları oyun ekranına ekle ve özelliklerini ayarla
				[mainParticle, subParticle1, subParticle2].forEach(function (p) {
					p.fadeSpeed = 0.03;
					p.gravity = 0.1;
					p.drag = 0.98;
					gamescreen.addChild(p);
					trailParticles.push(p);
				});
			}
			this.lastTrailTime = now;
		}
	};
	return beat;
}
function updateBeatTrailOnHit(beat, hitType) {
	if (!beat) {
		return;
	}
	// Hit türüne göre renk tipini belirle
	var colorType;
	switch (hitType.toLowerCase()) {
		case 'perfect':
			colorType = 'perfect';
			break;
		case 'awesome':
			colorType = 'awesome';
			break;
		case 'good':
			colorType = 'good';
			break;
		case 'ok':
			colorType = 'ok';
			break;
		case 'miss':
			colorType = 'miss';
			break;
		default:
			colorType = 'circle';
	}
	// Trail renklerini güncelle
	updateTrailsColor(beat, colorType);
}
// === Oyun Modu Yönetimi ===
var gameMode = "story"; // "story", "endless", "tutorial"
// === Menü Fonksiyonları ===
function showMainMenu() {
	mainMenuContainer.visible = true;
	scoreTxt.visible = false;
	comboTxt.visible = false;
	bpmDisplayTxt.visible = false;
	songNameTxt.visible = false;
	gamescreen.visible = false;
	defeatContainer.visible = false;
	pauseContainer.visible = false;
	settingsmenu.visible = false;
	creditsmenu.visible = false;
	supportmenu.visible = false;
}
// ... existing code ...
// === Ana Menü Butonları ===
// Start (Story) Button
startBtn.down = function (x, y, obj) {
	LK.getSound('buttonclick').play();
	LK.stopMusic();
	tween(startBtn, {
		scaleX: 0.162,
		scaleY: 0.162
	}, {
		duration: 80,
		easing: tween.easeIn,
		onFinish: function onFinish() {
			tween(startBtn, {
				scaleX: 0.18,
				scaleY: 0.18
			}, {
				duration: 120,
				easing: tween.easeOut,
				onFinish: function onFinish() {
					mainMenuContainer.visible = false;
					gameMode = "story";
					showStoryIntro(function () {
						showReadyScreen(function () {
							showCountdown(function () {
								startGame({
									mode: "story"
								});
							});
						});
					});
				}
			});
		}
	});
};
// Endless Mode Button
var endlessBtn = LK.getAsset('endlessbutton', {
	anchorX: 0.5,
	anchorY: 0.5,
	x: 2048 / 2,
	y: buttonStartY + buttonHeight + buttonSpacing,
	scaleX: 0.18,
	scaleY: 0.18
});
mainMenuContainer.addChild(endlessBtn);
endlessBtn.down = function (x, y, obj) {
	LK.getSound('buttonclick').play();
	LK.stopMusic();
	tween(endlessBtn, {
		scaleX: 0.162,
		scaleY: 0.162
	}, {
		duration: 80,
		easing: tween.easeIn,
		onFinish: function onFinish() {
			tween(endlessBtn, {
				scaleX: 0.18,
				scaleY: 0.18
			}, {
				duration: 120,
				easing: tween.easeOut,
				onFinish: function onFinish() {
					mainMenuContainer.visible = false;
					gameMode = "endless";
					showReadyScreen(function () {
						showCountdown(function () {
							startGame({
								mode: "endless"
							});
						});
					});
				}
			});
		}
	});
};
// ... existing code ...
// === startGame Fonksiyonunu Parametreli Yap ===
// ... existing code ...
// defeat ve ana menüye dönünce gameMode'u sıfırla
function resetGameMode() {
	gameMode = "story";
}
// defeat ekranı ve ana menü butonlarında resetGameMode çağır
// ... existing code ...
// === DEBUG: Oyun Modu ve BPM Göstergesi ===
// Ekranın sol üst köşesine debugText ekle
var debugText = new Text2('', {
	size: 24,
	fill: 0xff00ff
});
debugText.anchor.set(0, 0);
debugText.x = 20;
debugText.y = 20;
LK.gui.top.addChild(debugText);
function updateDebugText() {
	debugText.setText('MODE: ' + gameMode + ' | BPM: ' + bpm);
}
// startGame fonksiyonunun başında ve mod değiştirirken çağır
// ...
// === Trail Particle Pool Sistemi ===
var PARTICLE_POOL_SIZE = 500;
var particlePool = [];
for (var i = 0; i < PARTICLE_POOL_SIZE; i++) {
	var p = createTrailParticle(0, 0, 0xffffff);
	p.visible = false;
	p.alpha = 0;
	particlePool.push(p);
	gamescreen.addChild(p);
}
// Pool'dan particle al
function getPooledParticle(x, y, color, size) {
	for (var i = 0; i < particlePool.length; i++) {
		var p = particlePool[i];
		if (!p.visible || p.life <= 0) {
			p.x = x;
			p.y = y;
			p.tint = color;
			p.life = 1.0;
			p.alpha = 0.5;
			p.visible = true;
			p.width = 1;
			p.height = 1;
			p.velocityX = 0;
			p.velocityY = 0;
			p.fadeSpeed = 0.12;
			return p;
		}
	}
	return null;
}
// createTrailParticle fonksiyonunu sadece pool için kullan
// updateTrailParticle fonksiyonunu optimize et ===================================================================
--- original.js
+++ change.js
@@ -107,18 +107,18 @@
 
 /**** 
 * Game Code
 ****/ 
-// --- Story Chapters Configuration ---
-// Bass drum (kick) hit circle
-// Bass drum (kick) hit circle when active
-// Timeline bar
-// Beat marker
-// Miss feedback
-// Good feedback
-// Sound for kick
-// Music (looping, short demo)
 // --- BPM-specific music assets (kickless versions) ---
+// Music (looping, short demo)
+// Sound for kick
+// Good feedback
+// Miss feedback
+// Beat marker
+// Timeline bar
+// Bass drum (kick) hit circle when active
+// Bass drum (kick) hit circle
+// --- Story Chapters Configuration ---
 var STORY_CHAPTERS = [{
 	bpm: 60,
 	musicId: '60BPMLowKey',
 	songName: 'LowKey'
@@ -706,9 +706,9 @@
 var buttonStartY = 1000;
 var buttonHeight = 289; // Approximate height with scale 5
 var buttonSpacing = 5;
 // Start Button
-var startBtn = LK.getAsset('start', {
+var startBtn = LK.getAsset('startbutton', {
 	anchorX: 0.5,
 	anchorY: 0.5,
 	x: 2048 / 2,
 	y: buttonStartY,
@@ -4849,9 +4849,9 @@
 		}
 	});
 };
 // Endless Mode Button
-var endlessBtn = LK.getAsset('endless', {
+var endlessBtn = LK.getAsset('endlessbutton', {
 	anchorX: 0.5,
 	anchorY: 0.5,
 	x: 2048 / 2,
 	y: buttonStartY + buttonHeight + buttonSpacing,
 yellow ball pixel-art. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
 A minimalist horizontal pixel art timeline bar, 2048x40 resolution, designed for a lo-fi rhythm pixel game. The bar is sleek and thin, with subtle purple and violet tones, no numbers or markers. The style is consistent with a DJ-themed interface — soft glowing edges, pixel-perfect precision, and no background (transparent). The bar should feel modern yet retro, fitting into a dreamy neon-lit rhythm game UI.. In-Game asset. 2d. High contrast. No shadows
 orange
 
 
 Realistic comic book style, 2048x2048. Split-frame design: left half shows Noah mid-performance dissolving into shadows; right half shows a crumpled newspaper with the headline "DJ NOAH GONE?". Desaturated colors, somber mood. Add a transparent narrator box at the top-center with the text: "Then, silence." in comic font.. In-Game asset. 2d. High contrast. No shadows
 Add white to the edges for a photo paper look
 Add white to the edges for a photo paper look.
 Add white to the edges for a photo paper look.
 Add white to the edges for a photo paper look.
 A cinematic comic book-style vertical panel, 2048x2732. Inside a moody, dimly lit underground bar, a lone DJ (Noah) stands behind a glowing DJ booth, facing forward but with his head slightly bowed. Sparse silhouettes of a few customers sit at shadowy tables, blurred by distance and smoke. Purple and blue neon lights gently illuminate the space. The atmosphere feels still, as if something important just began. A faint spotlight outlines Noah's figure. This scene conveys calm before momentum, subtle drama and emotional depth. No text or narration in the image.. In-Game asset. 2d. High contrast. No shadows
 A rectangular comic-style narrator box placed near the bottom of a vertical panel (2048x2732). Dark purple background with rounded corners, white border. Inside, centered white text in clean comic font reads: "The lights may be dim, but the spark is back. Tonight, Noah spins not just music — but a second chance.". In-Game asset. 2d. High contrast. No shadows
 
 
 A comic panel styled like a square photo print with a clean white border. Realistic comic art. Scene: A rainy city street at night, neon signs reflecting on wet pavement, Noah stands under a flickering bar sign, hood up, looking uncertain. Narrator box at the top reads: “The rain didn’t stop him — not tonight.”. In-Game asset. 2d. High contrast. No shadows
 yazıyı güncelle "The place smelled of sweat, smoke, and forgotten dreams."
 A comic panel styled like a square photo print with a clean white border. Realistic comic art. Scene: Close-up of a middle-aged woman with bold makeup, cigarette in hand, leaning over the bar. Her name tag reads “Manager.” Narrator box at the top reads: “She didn’t ask questions — just handed him the schedule.”. In-Game asset. 2d. High contrast. No shadows
 A comic panel styled like a square photo print with a clean white border. Realistic comic art. Scene: A dusty DJ booth, some lights flickering, the equipment old but intact. Noah places his hand on the mixer with a hint of reverence. Narrator box at the top reads: “It wasn’t much — but it was a start.”. In-Game asset. 2d. High contrast. No shadows
 A comic panel styled like a square photo print with a clean white border. Realistic comic art. Scene: Over-the-shoulder view of Noah at the booth, the crowd in the bar small but curious. One hand on the vinyl, lights begin to flicker rhythmically. Narrator box at the top reads: “And just like that, the night began to breathe.”. In-Game asset. 2d. High contrast. No shadows
 
 A 2048x2732 vertical background illustration in a semi-realistic comic book style. The scene depicts a moody DJ booth inside a small urban apartment at night, overlooking a vibrant neon-lit city through large glass windows. The DJ setup is stylish but lived-in — with vinyl records, scattered sticky notes, a glowing laptop screen, and faint light reflections on the desk. Outside, rain trickles down the window, and distant buildings flicker with purples, blues, and pinks. The entire environment feels reflective, lo-fi, and intimate — as if the city hums with a quiet rhythm. No characters, no text — just a story-rich, immersive menu backdrop.. In-Game asset. 2d. High contrast. No shadows
 Write SETTINGS.
 write CREDITS.
 write SUPPORT US.
 A bold, comic-style game title reading 'DJ NOAH' in large uppercase letters. The text features a deep electric purple gradient with sharp highlights and bold white outlines, styled like a dramatic comic book title splash. Subtle halftone textures and a radial glow enhance its dynamic look. Behind the text, abstract comic-style sound wave elements stretch outward in purple and magenta hues. The composition is punchy, modern, and dramatic — designed as a central UI graphic with transparent background for in-game use. 2D, high contrast, no drop shadow.. In-Game asset. 2d. High contrast. No shadows
 A 2048x2732 vertical background illustration in a semi-realistic comic book style. The scene shows a quiet corner of the same small urban apartment, viewed from a different angle than the main menu. This part of the room features a vintage sound mixing rack, ambient LED lights softly glowing in purple and blue tones, and a corkboard on the wall filled with pinned notes, small polaroid photos, and scribbled music ideas. An open sketchbook with waveform doodles lies on a side table, and worn-out headphones rest beside it. A faint rain still falls outside the partially visible window, casting reflections on the floor. The overall mood remains lo-fi, introspective, and musically charged. No characters, no text — just a lived-in, creative space for the settings menu backdrop.. In-Game asset. 2d. High contrast. No shadows
 A 2048x2732 vertical background illustration in a semi-realistic comic book style. The setting reveals a nostalgic, dimly lit corner of the same urban apartment from the previous scenes. This part of the room showcases a personal wall collage filled with printed photographs, old event posters, and handwritten notes — all pinned with care and glowing softly under a small warm desk lamp. A wooden shelf holds vinyl covers, retro magazines, and a framed photo of a live DJ performance. On a nearby desk, a coffee mug steams next to an analog tape recorder, gently lit by the reflection of neon city lights from outside the window. The atmosphere is rich with memories, inspiration, and creative legacy. No characters, no text — just a sentimental space for the credits menu backdrop.. In-Game asset. 2d. High contrast. No shadows
 A 2048x2732 vertical background illustration in a semi-realistic comic book style. The scene takes place in another corner of the same urban apartment — this time showing an open space near a shelf filled with music memorabilia, fan-made items, and a pinboard with stickers like “Support Local DJs”, “Upvote Love”, and pixel-style hearts. A tablet on the desk is open to a game page with a “Leave a review” banner. There’s a soft spotlight on a coffee mug with “Thank you” written on it and glowing music notes floating around it. The city lights outside still shine in purples and blues, but this part of the room feels more hopeful and connected. The vibe is warm, welcoming, and subtly celebratory. No characters or text. In-game menu background, consistent with main menu themes.. In-Game asset. 2d. High contrast. No shadows
 tahta texture
 turuncu-kahverengi tonları
 semi-realistic comic book style, moody neon lighting, lo-fi urban atmosphere, rich textures, cinematic framing, purple and blue tones, soft gradients, ambient reflections
 semi-realistic comic book style, moody neon lighting, lo-fi urban atmosphere, rich textures, cinematic framing, purple and blue tones, soft gradients, ambient reflections
 semi-realistic comic book style, moody neon lighting, lo-fi urban atmosphere, rich textures, cinematic framing, purple and blue tones, soft gradients, ambient reflections
 semi-realistic comic book style, moody neon lighting, lo-fi urban atmosphere, rich textures, cinematic framing, purple and blue tones, soft gradients, ambient reflections
 kalkan mavi tonlarında olsun
 semi-realistic comic book style, moody neon lighting, lo-fi urban atmosphere, rich textures, cinematic framing, purple and blue tones, soft gradients, ambient reflections
 semi-realistic comic book style, moody neon lighting, lo-fi urban atmosphere, rich textures, cinematic framing, purple and blue tones, soft gradients, ambient reflections
 semi-realistic comic book style, moody neon lighting, lo-fi urban atmosphere, rich textures, cinematic framing, purple and blue tones, soft gradients, ambient reflections
 gri
 
 semi-realistic comic book style, moody neon lighting, lo-fi urban atmosphere, rich textures, cinematic framing, purple and blue tones, soft gradients, ambient reflections
 write MAIN MENU
 semi-realistic comic book style, moody neon lighting, lo-fi urban atmosphere, rich textures, cinematic framing, purple and blue tones, soft gradients, ambient reflections
 semi-realistic comic book style, moody neon lighting, lo-fi urban atmosphere, rich textures, cinematic framing, purple and blue tones, soft gradients, ambient reflections
 semi-realistic comic book style, moody neon lighting, lo-fi urban atmosphere, rich textures, cinematic framing, purple and blue tones, soft gradients, ambient reflections
 semi-realistic comic book style, lo-fi urban atmosphere, rich textures, cinematic framing, purple and blue tones, soft gradients
 semi-realistic comic book style, moody neon lighting, lo-fi urban atmosphere, rich textures, purple and blue tones, soft gradients
 semi-realistic comic book style, moody neon lighting, lo-fi urban atmosphere, rich textures, cinematic framing, purple and blue tones, soft gradients, ambient reflections
 semi-realistic comic book style, moody neon lighting, lo-fi urban atmosphere, rich textures, cinematic framing, purple and blue tones, soft gradients, ambient reflections
 semi-realistic comic book style, moody neon lighting, lo-fi urban atmosphere, rich textures, cinematic framing, purple and blue tones, soft gradients, ambient reflections
 write RESUME
 write RESTART
 kick
Sound effect
miss
Sound effect
menumusic
Music
75BPMElectricDreams
Music
80BPMNeonNights
Music
85BPMCyberFlow
Music
90BPMDigitalPulse
Music
95BPMFutureBass
Music
100BPMSynthWave
Music
40BPMDeepVibes
Music
45BPMChillWave
Music
50BPMSlowMotion
Music
55BPMDreamscape
Music
60BPMLowKey
Music
65BPMRelaxed
Music
70BPMCalmVibes
Music
105BPMHyperDrive
Music
110BPMTechnoRush
Music
115BPMHighEnergy
Music
120BPMMaximum
Music
scratch
Sound effect
buttonclick
Sound effect
kick2
Sound effect
kick3
Sound effect
kick4
Sound effect
hihat
Sound effect
hihat2
Sound effect
hihat3
Sound effect
hihat4
Sound effect
snareclap
Sound effect
snareclap2
Sound effect
snareclap3
Sound effect
snareclap4
Sound effect
percussion
Sound effect
percussion2
Sound effect
percussion3
Sound effect
percussion4
Sound effect
thanks
Sound effect
echoesofthepast
Sound effect
chapter1music
Music
paperswosh
Sound effect
ch1voice1
Sound effect
ch1voice2
Sound effect
nightshift
Sound effect
chapter2music
Music