/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var Note = Container.expand(function (type, lane) { var self = Container.call(this); self.type = type || 'regular'; // 'regular' or 'hold' self.lane = lane || 0; self.speed = 8; self.hit = false; self.missed = false; self.holdPressed = false; self.holdDuration = 0; self.maxHoldDuration = 0; self.colorIndex = Math.floor(Math.random() * 5) + 1; // 1-5 for different colors self.lastY = self.y; // Musical note symbols var symbols = ['1', '2', '3', '4', '5', '6', '7', '8', '§', '∆', '×', '÷', 'π', '√', '✓', '>', '<']; self.symbol = symbols[(self.colorIndex - 1) % symbols.length]; // Create note graphics based on type if (self.type === 'hold') { self.noteGraphics = self.attachAsset('noteHold' + self.colorIndex, { anchorX: 0.5, anchorY: 0.5 }); self.maxHoldDuration = 60; // 1 second at 60fps } else { self.noteGraphics = self.attachAsset('noteRegular' + self.colorIndex, { anchorX: 0.5, anchorY: 0.5 }); } // Add text symbol self.symbolText = new Text2(self.symbol, { size: 60, fill: 0xffffff }); self.symbolText.anchor.set(0.5, 0.5); self.addChild(self.symbolText); // Position based on lane self.x = 256 + self.lane * 384; // 4 lanes across screen self.y = -100; self.lastY = self.y; self.update = function () { self.lastY = self.y; self.y += self.speed; // Add pulsing glow effect var pulse = Math.sin(LK.ticks * 0.2) * 0.2 + 0.8; self.noteGraphics.alpha = pulse; self.alpha = pulse; // Check if note is in hit zone var hitZoneY = 2732 - 300; var distanceFromHitZone = Math.abs(self.y - hitZoneY); // Miss detection - transition from inside to outside hit zone if (self.lastY <= hitZoneY + 100 && self.y > hitZoneY + 100 && !self.hit && !self.missed) { self.missed = true; combo = 0; accuracy.total++; accuracy.missed++; LK.getSound('miss').play(); } // Handle hold notes if (self.type === 'hold' && self.hit && self.holdPressed) { self.holdDuration++; // Change opacity while holding self.noteGraphics.alpha = 0.5; } }; return self; }); var ParticleEffect = Container.expand(function (x, y, color) { var self = Container.call(this); self.x = x; self.y = y; self.lifetime = 30; self.age = 0; self.particle = self.attachAsset('particle', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.8, scaleY: 0.8 }); // Tint with passed color or random neon color if (color) { self.particle.tint = color; } else { var colors = [0x7d4dff, 0xb366ff, 0xff65d7, 0xff8fe6, 0x65b3ff]; self.particle.tint = colors[Math.floor(Math.random() * colors.length)]; } self.velocityX = (Math.random() - 0.5) * 15; self.velocityY = (Math.random() - 0.5) * 15 - 8; self.update = function () { self.age++; self.x += self.velocityX; self.y += self.velocityY; self.velocityY += 0.3; // Gravity effect var alpha = 1 - self.age / self.lifetime; self.particle.alpha = alpha; var scale = 0.8 + (1 - alpha) * 0.5; self.particle.scaleX = scale; self.particle.scaleY = scale; if (self.age >= self.lifetime) { self.destroy(); for (var i = particles.length - 1; i >= 0; i--) { if (particles[i] === self) { particles.splice(i, 1); break; } } } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x0a0a1a }); /**** * Game Code ****/ // Neon-style note shapes with various colors // Game variables var notes = []; var particles = []; var combo = 0; var score = 0; var accuracy = { perfect: 0, good: 0, missed: 0, total: 0 }; var gameStarted = false; var noteSpawnTimer = 0; var activeTouches = {}; var hitZoneLastAlpha = 0.2; var selectedSong = 'random'; var gameMode = 'menu'; // 'menu', 'playing' // Song data var songs = { random: { name: "Random Notes", bpm: 120, spawnRate: 40, difficulty: "Easy", music: "randomMusic" }, neonBeats: { name: "Neon Beats", bpm: 140, spawnRate: 30, difficulty: "Medium", music: "neonBeatsMusic", notes: [{ time: 1000, lane: 0, type: 'regular' }, { time: 1200, lane: 1, type: 'regular' }, { time: 1400, lane: 2, type: 'hold', duration: 60 }, { time: 1600, lane: 3, type: 'regular' }, { time: 1800, lane: 0, type: 'regular' }, { time: 2000, lane: 1, type: 'regular' }, { time: 2200, lane: 2, type: 'regular' }, { time: 2400, lane: 3, type: 'hold', duration: 80 }, { time: 2600, lane: 0, type: 'regular' }, { time: 2800, lane: 1, type: 'regular' }] }, synthWave: { name: "Synth Wave", bpm: 160, spawnRate: 25, difficulty: "Hard", music: "synthWaveMusic", notes: [{ time: 500, lane: 0, type: 'regular' }, { time: 700, lane: 1, type: 'regular' }, { time: 900, lane: 2, type: 'regular' }, { time: 1100, lane: 3, type: 'regular' }, { time: 1300, lane: 0, type: 'hold', duration: 40 }, { time: 1300, lane: 2, type: 'hold', duration: 40 }, { time: 1500, lane: 1, type: 'regular' }, { time: 1700, lane: 3, type: 'regular' }, { time: 1900, lane: 0, type: 'regular' }, { time: 2100, lane: 1, type: 'regular' }, { time: 2300, lane: 2, type: 'regular' }, { time: 2500, lane: 3, type: 'regular' }] }, cyberpunk: { name: "Cyberpunk City", bpm: 180, spawnRate: 20, difficulty: "Expert", music: "cyberpunkMusic", notes: [{ time: 400, lane: 0, type: 'regular' }, { time: 600, lane: 1, type: 'regular' }, { time: 800, lane: 2, type: 'regular' }, { time: 1000, lane: 3, type: 'regular' }, { time: 1200, lane: 0, type: 'hold', duration: 30 }, { time: 1200, lane: 1, type: 'hold', duration: 30 }, { time: 1400, lane: 2, type: 'regular' }, { time: 1600, lane: 3, type: 'regular' }, { time: 1800, lane: 0, type: 'regular' }, { time: 2000, lane: 1, type: 'regular' }, { time: 2200, lane: 2, type: 'hold', duration: 60 }, { time: 2400, lane: 3, type: 'regular' }] } }; var songStartTime = 0; var songNoteIndex = 0; var menuVisible = true; // Create hit zone with neon glow effect var hitZone = game.addChild(LK.getAsset('hitZone', { anchorX: 0.5, anchorY: 0.5 })); hitZone.x = 2048 / 2; hitZone.y = 2732 - 300; hitZone.alpha = 0.2; // Create lane markers with neon styling for (var i = 0; i < 4; i++) { var laneMarker = game.addChild(LK.getAsset('trail', { anchorX: 0.5, anchorY: 0.5, scaleY: 30 })); laneMarker.x = 256 + i * 384; laneMarker.y = 2732 / 2; laneMarker.alpha = 0.15; } // Create 4 clickable squares at the bottom for each lane var laneButtons = []; for (var i = 0; i < 4; i++) { var laneButton = game.addChild(LK.getAsset('noteRegular' + (i + 1), { anchorX: 0.5, anchorY: 0.5, scaleX: 2.0, scaleY: 2.0 })); laneButton.x = 256 + i * 384; laneButton.y = 2732 - 300; // Position higher up from bottom laneButton.alpha = 0.6; laneButton.lane = i; laneButtons.push(laneButton); } // UI Elements with neon colors var scoreText = new Text2('Score: 0', { size: 60, fill: 0x9D65FF }); scoreText.anchor.set(0.5, 0); LK.gui.top.addChild(scoreText); var comboText = new Text2('Combo: 0', { size: 50, fill: 0xFF65D7 }); comboText.anchor.set(1, 0); LK.gui.topRight.addChild(comboText); var accuracyText = new Text2('Accuracy: 100%', { size: 40, fill: 0x65B3FF }); accuracyText.anchor.set(0, 0); LK.gui.topLeft.addChild(accuracyText); accuracyText.x = 120; // Offset from menu button // Create song selection menu var menuContainer = new Container(); game.addChild(menuContainer); // Menu background var menuBg = LK.getAsset('hitZone', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.0, scaleY: 8.0 }); menuBg.x = 2048 / 2; menuBg.y = 2732 / 2; menuBg.alpha = 0.8; menuContainer.addChild(menuBg); // Menu title var menuTitle = new Text2('Choose Your Song', { size: 80, fill: 0xFF65D7 }); menuTitle.anchor.set(0.5, 0.5); menuTitle.x = 2048 / 2; menuTitle.y = 800; menuContainer.addChild(menuTitle); // Song selection buttons var songButtons = []; var songNames = Object.keys(songs); var buttonSpacing = 300; var startY = 1200; for (var i = 0; i < songNames.length; i++) { var songKey = songNames[i]; var song = songs[songKey]; // Button background var buttonBg = LK.getAsset('noteRegular' + (i % 4 + 1), { anchorX: 0.5, anchorY: 0.5, scaleX: 4.0, scaleY: 1.5 }); buttonBg.x = 2048 / 2; buttonBg.y = startY + i * buttonSpacing; buttonBg.alpha = 0.7; buttonBg.songKey = songKey; menuContainer.addChild(buttonBg); // Button text var buttonText = new Text2(song.name, { size: 50, fill: 0xFFFFFF }); buttonText.anchor.set(0.5, 0.5); buttonText.x = buttonBg.x; buttonText.y = buttonBg.y - 20; menuContainer.addChild(buttonText); // Difficulty and BPM text var infoText = new Text2(song.difficulty + ' • ' + song.bpm + ' BPM', { size: 30, fill: 0x65B3FF }); infoText.anchor.set(0.5, 0.5); infoText.x = buttonBg.x; infoText.y = buttonBg.y + 30; menuContainer.addChild(infoText); songButtons.push({ bg: buttonBg, text: buttonText, info: infoText, songKey: songKey }); } function spawnNote(forcedLane, forcedType, forcedDuration) { var lane = forcedLane !== undefined ? forcedLane : Math.floor(Math.random() * 4); var noteType = forcedType || (Math.random() < 0.2 ? 'hold' : 'regular'); var note = new Note(noteType, lane); if (forcedDuration && noteType === 'hold') { note.maxHoldDuration = forcedDuration; } notes.push(note); game.addChild(note); } function updateScore(hitType) { var basePoints = 100; var multiplier = Math.max(1, Math.floor(combo / 10) + 1); if (hitType === 'perfect') { score += basePoints * 2 * multiplier; accuracy.perfect++; } else if (hitType === 'good') { score += basePoints * multiplier; accuracy.good++; } accuracy.total++; LK.setScore(score); scoreText.setText('Score: ' + score); comboText.setText('Combo: ' + combo); var accuracyPercent = Math.round((accuracy.perfect + accuracy.good) / accuracy.total * 100); accuracyText.setText('Accuracy: ' + accuracyPercent + '%'); // Check for game over condition when accuracy drops below 40% if (accuracy.total >= 10 && accuracyPercent < 40) { game.setBackgroundColor(0x800080); // Change background to purple LK.showGameOver(); } } function createParticleEffect(x, y, color) { for (var i = 0; i < 8; i++) { var particle = new ParticleEffect(x, y, color); particles.push(particle); game.addChild(particle); } } function getLaneFromX(x) { if (x < 448) { return 0; } if (x < 832) { return 1; } if (x < 1216) { return 2; } return 3; } function startSelectedSong() { menuVisible = false; menuContainer.visible = false; gameMode = 'playing'; songStartTime = LK.ticks; songNoteIndex = 0; // Reset game state score = 0; combo = 0; accuracy = { perfect: 0, good: 0, missed: 0, total: 0 }; // Clear existing notes for (var i = notes.length - 1; i >= 0; i--) { notes[i].destroy(); notes.splice(i, 1); } updateScore('good'); // Updates displays LK.setScore(0); scoreText.setText('Score: 0'); comboText.setText('Combo: 0'); accuracyText.setText('Accuracy: 100%'); // Play specific music for selected song var currentSong = songs[selectedSong]; if (currentSong.music) { LK.playMusic(currentSong.music); } else { LK.playMusic('bgmusic'); } } function showMenu() { menuVisible = true; menuContainer.visible = true; gameMode = 'menu'; gameStarted = false; } game.down = function (x, y, obj) { // Handle menu interactions if (menuVisible) { for (var i = 0; i < songButtons.length; i++) { var button = songButtons[i]; var buttonBounds = { left: button.bg.x - button.bg.width * button.bg.scaleX / 2, right: button.bg.x + button.bg.width * button.bg.scaleX / 2, top: button.bg.y - button.bg.height * button.bg.scaleY / 2, bottom: button.bg.y + button.bg.height * button.bg.scaleY / 2 }; if (x >= buttonBounds.left && x <= buttonBounds.right && y >= buttonBounds.top && y <= buttonBounds.bottom) { // Button clicked selectedSong = button.songKey; startSelectedSong(); return; } } return; } var lane = getLaneFromX(x); var hitZoneY = 2732 - 300; var hitFound = false; // Add visual feedback to lane button if (laneButtons[lane]) { laneButtons[lane].alpha = 1.0; tween(laneButtons[lane], { scaleX: 2.4, scaleY: 2.4 }, { duration: 100 }); } // Find the closest note in this lane var closestNote = null; var closestDistance = Infinity; for (var i = 0; i < notes.length; i++) { var note = notes[i]; if (note.lane === lane && !note.hit && !note.missed) { var distance = Math.abs(note.y - hitZoneY); if (distance < closestDistance) { closestDistance = distance; closestNote = note; } } } if (closestNote && closestDistance < 100) { closestNote.hit = true; hitFound = true; if (closestNote.type === 'hold') { closestNote.holdPressed = true; activeTouches[lane] = true; } // Determine hit accuracy var hitType = 'good'; var noteColor = [0x7d4dff, 0xb366ff, 0xff65d7, 0xff8fe6, 0x65b3ff][closestNote.colorIndex - 1]; if (closestDistance < 40) { hitType = 'perfect'; combo++; LK.getSound('hitPerfect').play(); createParticleEffect(closestNote.x, closestNote.y, noteColor); // Flash screen effect for perfect hits LK.effects.flashScreen(noteColor, 200); } else { combo++; LK.getSound('hitGood').play(); createParticleEffect(closestNote.x, closestNote.y, noteColor); } updateScore(hitType); // Flash hit zone with note color tween(hitZone, { alpha: 0.6 }, { duration: 100, onFinish: function onFinish() { tween(hitZone, { alpha: 0.2 }, { duration: 300 }); } }); } if (!hitFound) { combo = 0; comboText.setText('Combo: 0'); } }; game.up = function (x, y, obj) { var lane = getLaneFromX(x); activeTouches[lane] = false; // Reset visual feedback for lane button if (laneButtons[lane]) { laneButtons[lane].alpha = 0.6; tween(laneButtons[lane], { scaleX: 2.0, scaleY: 2.0 }, { duration: 150 }); } // Release hold notes in this lane for (var i = 0; i < notes.length; i++) { var note = notes[i]; if (note.lane === lane && note.type === 'hold' && note.holdPressed) { note.holdPressed = false; // Score based on hold duration if (note.holdDuration >= note.maxHoldDuration * 0.8) { combo++; updateScore('perfect'); } else if (note.holdDuration >= note.maxHoldDuration * 0.5) { updateScore('good'); } else { combo = 0; } } } }; game.update = function () { // Handle menu mode if (menuVisible) { // Add pulsing effect to menu buttons for (var i = 0; i < songButtons.length; i++) { var button = songButtons[i]; var pulse = Math.sin(LK.ticks * 0.1 + i * 0.3) * 0.1 + 0.7; button.bg.alpha = pulse; } return; } // Game started flag if (!gameStarted) { gameStarted = true; } // Add pulsing glow effect to hit zone var hitZonePulse = Math.sin(LK.ticks * 0.15) * 0.1 + 0.2; hitZone.alpha = hitZonePulse; // Add subtle pulsing effect to lane buttons for (var i = 0; i < laneButtons.length; i++) { if (!activeTouches[i]) { var buttonPulse = Math.sin(LK.ticks * 0.1 + i * 0.5) * 0.1 + 0.6; laneButtons[i].alpha = buttonPulse; } } // Handle song-specific note spawning if (selectedSong === 'random') { // Spawn notes randomly noteSpawnTimer++; if (noteSpawnTimer >= 40) { spawnNote(); noteSpawnTimer = 0; } } else { // Spawn notes based on song pattern var currentSong = songs[selectedSong]; if (currentSong.notes && songNoteIndex < currentSong.notes.length) { var currentTime = (LK.ticks - songStartTime) * (1000 / 60); // Convert ticks to milliseconds var nextNote = currentSong.notes[songNoteIndex]; if (currentTime >= nextNote.time) { spawnNote(nextNote.lane, nextNote.type, nextNote.duration); songNoteIndex++; } } // End song when all notes are played and cleared if (songNoteIndex >= currentSong.notes.length && notes.length === 0) { LK.setTimeout(function () { showMenu(); }, 2000); } } // Clean up off-screen notes for (var i = notes.length - 1; i >= 0; i--) { var note = notes[i]; // Remove notes that are off screen or completed if (note.y > 2800 || note.hit && note.type === 'regular' || note.hit && note.type === 'hold' && !note.holdPressed && note.holdDuration > 0) { note.destroy(); notes.splice(i, 1); } } // Update hold note states for (var lane = 0; lane < 4; lane++) { if (!activeTouches[lane]) { for (var j = 0; j < notes.length; j++) { var note = notes[j]; if (note.lane === lane && note.type === 'hold' && note.holdPressed) { note.holdPressed = false; } } } } // Add combo glow effect if (combo > 10) { var comboColors = [0xff65d7, 0x9d65ff, 0x65b3ff, 0xff8fe6]; var colorIndex = Math.floor(LK.ticks / 10 % comboColors.length); comboText.fill = comboColors[colorIndex]; } // End game condition (for demo purposes, end after reaching high score) if (score >= 50000) { LK.showYouWin(); } };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Note = Container.expand(function (type, lane) {
var self = Container.call(this);
self.type = type || 'regular'; // 'regular' or 'hold'
self.lane = lane || 0;
self.speed = 8;
self.hit = false;
self.missed = false;
self.holdPressed = false;
self.holdDuration = 0;
self.maxHoldDuration = 0;
self.colorIndex = Math.floor(Math.random() * 5) + 1; // 1-5 for different colors
self.lastY = self.y;
// Musical note symbols
var symbols = ['1', '2', '3', '4', '5', '6', '7', '8', '§', '∆', '×', '÷', 'π', '√', '✓', '>', '<'];
self.symbol = symbols[(self.colorIndex - 1) % symbols.length];
// Create note graphics based on type
if (self.type === 'hold') {
self.noteGraphics = self.attachAsset('noteHold' + self.colorIndex, {
anchorX: 0.5,
anchorY: 0.5
});
self.maxHoldDuration = 60; // 1 second at 60fps
} else {
self.noteGraphics = self.attachAsset('noteRegular' + self.colorIndex, {
anchorX: 0.5,
anchorY: 0.5
});
}
// Add text symbol
self.symbolText = new Text2(self.symbol, {
size: 60,
fill: 0xffffff
});
self.symbolText.anchor.set(0.5, 0.5);
self.addChild(self.symbolText);
// Position based on lane
self.x = 256 + self.lane * 384; // 4 lanes across screen
self.y = -100;
self.lastY = self.y;
self.update = function () {
self.lastY = self.y;
self.y += self.speed;
// Add pulsing glow effect
var pulse = Math.sin(LK.ticks * 0.2) * 0.2 + 0.8;
self.noteGraphics.alpha = pulse;
self.alpha = pulse;
// Check if note is in hit zone
var hitZoneY = 2732 - 300;
var distanceFromHitZone = Math.abs(self.y - hitZoneY);
// Miss detection - transition from inside to outside hit zone
if (self.lastY <= hitZoneY + 100 && self.y > hitZoneY + 100 && !self.hit && !self.missed) {
self.missed = true;
combo = 0;
accuracy.total++;
accuracy.missed++;
LK.getSound('miss').play();
}
// Handle hold notes
if (self.type === 'hold' && self.hit && self.holdPressed) {
self.holdDuration++;
// Change opacity while holding
self.noteGraphics.alpha = 0.5;
}
};
return self;
});
var ParticleEffect = Container.expand(function (x, y, color) {
var self = Container.call(this);
self.x = x;
self.y = y;
self.lifetime = 30;
self.age = 0;
self.particle = self.attachAsset('particle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8
});
// Tint with passed color or random neon color
if (color) {
self.particle.tint = color;
} else {
var colors = [0x7d4dff, 0xb366ff, 0xff65d7, 0xff8fe6, 0x65b3ff];
self.particle.tint = colors[Math.floor(Math.random() * colors.length)];
}
self.velocityX = (Math.random() - 0.5) * 15;
self.velocityY = (Math.random() - 0.5) * 15 - 8;
self.update = function () {
self.age++;
self.x += self.velocityX;
self.y += self.velocityY;
self.velocityY += 0.3; // Gravity effect
var alpha = 1 - self.age / self.lifetime;
self.particle.alpha = alpha;
var scale = 0.8 + (1 - alpha) * 0.5;
self.particle.scaleX = scale;
self.particle.scaleY = scale;
if (self.age >= self.lifetime) {
self.destroy();
for (var i = particles.length - 1; i >= 0; i--) {
if (particles[i] === self) {
particles.splice(i, 1);
break;
}
}
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x0a0a1a
});
/****
* Game Code
****/
// Neon-style note shapes with various colors
// Game variables
var notes = [];
var particles = [];
var combo = 0;
var score = 0;
var accuracy = {
perfect: 0,
good: 0,
missed: 0,
total: 0
};
var gameStarted = false;
var noteSpawnTimer = 0;
var activeTouches = {};
var hitZoneLastAlpha = 0.2;
var selectedSong = 'random';
var gameMode = 'menu'; // 'menu', 'playing'
// Song data
var songs = {
random: {
name: "Random Notes",
bpm: 120,
spawnRate: 40,
difficulty: "Easy",
music: "randomMusic"
},
neonBeats: {
name: "Neon Beats",
bpm: 140,
spawnRate: 30,
difficulty: "Medium",
music: "neonBeatsMusic",
notes: [{
time: 1000,
lane: 0,
type: 'regular'
}, {
time: 1200,
lane: 1,
type: 'regular'
}, {
time: 1400,
lane: 2,
type: 'hold',
duration: 60
}, {
time: 1600,
lane: 3,
type: 'regular'
}, {
time: 1800,
lane: 0,
type: 'regular'
}, {
time: 2000,
lane: 1,
type: 'regular'
}, {
time: 2200,
lane: 2,
type: 'regular'
}, {
time: 2400,
lane: 3,
type: 'hold',
duration: 80
}, {
time: 2600,
lane: 0,
type: 'regular'
}, {
time: 2800,
lane: 1,
type: 'regular'
}]
},
synthWave: {
name: "Synth Wave",
bpm: 160,
spawnRate: 25,
difficulty: "Hard",
music: "synthWaveMusic",
notes: [{
time: 500,
lane: 0,
type: 'regular'
}, {
time: 700,
lane: 1,
type: 'regular'
}, {
time: 900,
lane: 2,
type: 'regular'
}, {
time: 1100,
lane: 3,
type: 'regular'
}, {
time: 1300,
lane: 0,
type: 'hold',
duration: 40
}, {
time: 1300,
lane: 2,
type: 'hold',
duration: 40
}, {
time: 1500,
lane: 1,
type: 'regular'
}, {
time: 1700,
lane: 3,
type: 'regular'
}, {
time: 1900,
lane: 0,
type: 'regular'
}, {
time: 2100,
lane: 1,
type: 'regular'
}, {
time: 2300,
lane: 2,
type: 'regular'
}, {
time: 2500,
lane: 3,
type: 'regular'
}]
},
cyberpunk: {
name: "Cyberpunk City",
bpm: 180,
spawnRate: 20,
difficulty: "Expert",
music: "cyberpunkMusic",
notes: [{
time: 400,
lane: 0,
type: 'regular'
}, {
time: 600,
lane: 1,
type: 'regular'
}, {
time: 800,
lane: 2,
type: 'regular'
}, {
time: 1000,
lane: 3,
type: 'regular'
}, {
time: 1200,
lane: 0,
type: 'hold',
duration: 30
}, {
time: 1200,
lane: 1,
type: 'hold',
duration: 30
}, {
time: 1400,
lane: 2,
type: 'regular'
}, {
time: 1600,
lane: 3,
type: 'regular'
}, {
time: 1800,
lane: 0,
type: 'regular'
}, {
time: 2000,
lane: 1,
type: 'regular'
}, {
time: 2200,
lane: 2,
type: 'hold',
duration: 60
}, {
time: 2400,
lane: 3,
type: 'regular'
}]
}
};
var songStartTime = 0;
var songNoteIndex = 0;
var menuVisible = true;
// Create hit zone with neon glow effect
var hitZone = game.addChild(LK.getAsset('hitZone', {
anchorX: 0.5,
anchorY: 0.5
}));
hitZone.x = 2048 / 2;
hitZone.y = 2732 - 300;
hitZone.alpha = 0.2;
// Create lane markers with neon styling
for (var i = 0; i < 4; i++) {
var laneMarker = game.addChild(LK.getAsset('trail', {
anchorX: 0.5,
anchorY: 0.5,
scaleY: 30
}));
laneMarker.x = 256 + i * 384;
laneMarker.y = 2732 / 2;
laneMarker.alpha = 0.15;
}
// Create 4 clickable squares at the bottom for each lane
var laneButtons = [];
for (var i = 0; i < 4; i++) {
var laneButton = game.addChild(LK.getAsset('noteRegular' + (i + 1), {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.0,
scaleY: 2.0
}));
laneButton.x = 256 + i * 384;
laneButton.y = 2732 - 300; // Position higher up from bottom
laneButton.alpha = 0.6;
laneButton.lane = i;
laneButtons.push(laneButton);
}
// UI Elements with neon colors
var scoreText = new Text2('Score: 0', {
size: 60,
fill: 0x9D65FF
});
scoreText.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreText);
var comboText = new Text2('Combo: 0', {
size: 50,
fill: 0xFF65D7
});
comboText.anchor.set(1, 0);
LK.gui.topRight.addChild(comboText);
var accuracyText = new Text2('Accuracy: 100%', {
size: 40,
fill: 0x65B3FF
});
accuracyText.anchor.set(0, 0);
LK.gui.topLeft.addChild(accuracyText);
accuracyText.x = 120; // Offset from menu button
// Create song selection menu
var menuContainer = new Container();
game.addChild(menuContainer);
// Menu background
var menuBg = LK.getAsset('hitZone', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.0,
scaleY: 8.0
});
menuBg.x = 2048 / 2;
menuBg.y = 2732 / 2;
menuBg.alpha = 0.8;
menuContainer.addChild(menuBg);
// Menu title
var menuTitle = new Text2('Choose Your Song', {
size: 80,
fill: 0xFF65D7
});
menuTitle.anchor.set(0.5, 0.5);
menuTitle.x = 2048 / 2;
menuTitle.y = 800;
menuContainer.addChild(menuTitle);
// Song selection buttons
var songButtons = [];
var songNames = Object.keys(songs);
var buttonSpacing = 300;
var startY = 1200;
for (var i = 0; i < songNames.length; i++) {
var songKey = songNames[i];
var song = songs[songKey];
// Button background
var buttonBg = LK.getAsset('noteRegular' + (i % 4 + 1), {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 4.0,
scaleY: 1.5
});
buttonBg.x = 2048 / 2;
buttonBg.y = startY + i * buttonSpacing;
buttonBg.alpha = 0.7;
buttonBg.songKey = songKey;
menuContainer.addChild(buttonBg);
// Button text
var buttonText = new Text2(song.name, {
size: 50,
fill: 0xFFFFFF
});
buttonText.anchor.set(0.5, 0.5);
buttonText.x = buttonBg.x;
buttonText.y = buttonBg.y - 20;
menuContainer.addChild(buttonText);
// Difficulty and BPM text
var infoText = new Text2(song.difficulty + ' • ' + song.bpm + ' BPM', {
size: 30,
fill: 0x65B3FF
});
infoText.anchor.set(0.5, 0.5);
infoText.x = buttonBg.x;
infoText.y = buttonBg.y + 30;
menuContainer.addChild(infoText);
songButtons.push({
bg: buttonBg,
text: buttonText,
info: infoText,
songKey: songKey
});
}
function spawnNote(forcedLane, forcedType, forcedDuration) {
var lane = forcedLane !== undefined ? forcedLane : Math.floor(Math.random() * 4);
var noteType = forcedType || (Math.random() < 0.2 ? 'hold' : 'regular');
var note = new Note(noteType, lane);
if (forcedDuration && noteType === 'hold') {
note.maxHoldDuration = forcedDuration;
}
notes.push(note);
game.addChild(note);
}
function updateScore(hitType) {
var basePoints = 100;
var multiplier = Math.max(1, Math.floor(combo / 10) + 1);
if (hitType === 'perfect') {
score += basePoints * 2 * multiplier;
accuracy.perfect++;
} else if (hitType === 'good') {
score += basePoints * multiplier;
accuracy.good++;
}
accuracy.total++;
LK.setScore(score);
scoreText.setText('Score: ' + score);
comboText.setText('Combo: ' + combo);
var accuracyPercent = Math.round((accuracy.perfect + accuracy.good) / accuracy.total * 100);
accuracyText.setText('Accuracy: ' + accuracyPercent + '%');
// Check for game over condition when accuracy drops below 40%
if (accuracy.total >= 10 && accuracyPercent < 40) {
game.setBackgroundColor(0x800080); // Change background to purple
LK.showGameOver();
}
}
function createParticleEffect(x, y, color) {
for (var i = 0; i < 8; i++) {
var particle = new ParticleEffect(x, y, color);
particles.push(particle);
game.addChild(particle);
}
}
function getLaneFromX(x) {
if (x < 448) {
return 0;
}
if (x < 832) {
return 1;
}
if (x < 1216) {
return 2;
}
return 3;
}
function startSelectedSong() {
menuVisible = false;
menuContainer.visible = false;
gameMode = 'playing';
songStartTime = LK.ticks;
songNoteIndex = 0;
// Reset game state
score = 0;
combo = 0;
accuracy = {
perfect: 0,
good: 0,
missed: 0,
total: 0
};
// Clear existing notes
for (var i = notes.length - 1; i >= 0; i--) {
notes[i].destroy();
notes.splice(i, 1);
}
updateScore('good'); // Updates displays
LK.setScore(0);
scoreText.setText('Score: 0');
comboText.setText('Combo: 0');
accuracyText.setText('Accuracy: 100%');
// Play specific music for selected song
var currentSong = songs[selectedSong];
if (currentSong.music) {
LK.playMusic(currentSong.music);
} else {
LK.playMusic('bgmusic');
}
}
function showMenu() {
menuVisible = true;
menuContainer.visible = true;
gameMode = 'menu';
gameStarted = false;
}
game.down = function (x, y, obj) {
// Handle menu interactions
if (menuVisible) {
for (var i = 0; i < songButtons.length; i++) {
var button = songButtons[i];
var buttonBounds = {
left: button.bg.x - button.bg.width * button.bg.scaleX / 2,
right: button.bg.x + button.bg.width * button.bg.scaleX / 2,
top: button.bg.y - button.bg.height * button.bg.scaleY / 2,
bottom: button.bg.y + button.bg.height * button.bg.scaleY / 2
};
if (x >= buttonBounds.left && x <= buttonBounds.right && y >= buttonBounds.top && y <= buttonBounds.bottom) {
// Button clicked
selectedSong = button.songKey;
startSelectedSong();
return;
}
}
return;
}
var lane = getLaneFromX(x);
var hitZoneY = 2732 - 300;
var hitFound = false;
// Add visual feedback to lane button
if (laneButtons[lane]) {
laneButtons[lane].alpha = 1.0;
tween(laneButtons[lane], {
scaleX: 2.4,
scaleY: 2.4
}, {
duration: 100
});
}
// Find the closest note in this lane
var closestNote = null;
var closestDistance = Infinity;
for (var i = 0; i < notes.length; i++) {
var note = notes[i];
if (note.lane === lane && !note.hit && !note.missed) {
var distance = Math.abs(note.y - hitZoneY);
if (distance < closestDistance) {
closestDistance = distance;
closestNote = note;
}
}
}
if (closestNote && closestDistance < 100) {
closestNote.hit = true;
hitFound = true;
if (closestNote.type === 'hold') {
closestNote.holdPressed = true;
activeTouches[lane] = true;
}
// Determine hit accuracy
var hitType = 'good';
var noteColor = [0x7d4dff, 0xb366ff, 0xff65d7, 0xff8fe6, 0x65b3ff][closestNote.colorIndex - 1];
if (closestDistance < 40) {
hitType = 'perfect';
combo++;
LK.getSound('hitPerfect').play();
createParticleEffect(closestNote.x, closestNote.y, noteColor);
// Flash screen effect for perfect hits
LK.effects.flashScreen(noteColor, 200);
} else {
combo++;
LK.getSound('hitGood').play();
createParticleEffect(closestNote.x, closestNote.y, noteColor);
}
updateScore(hitType);
// Flash hit zone with note color
tween(hitZone, {
alpha: 0.6
}, {
duration: 100,
onFinish: function onFinish() {
tween(hitZone, {
alpha: 0.2
}, {
duration: 300
});
}
});
}
if (!hitFound) {
combo = 0;
comboText.setText('Combo: 0');
}
};
game.up = function (x, y, obj) {
var lane = getLaneFromX(x);
activeTouches[lane] = false;
// Reset visual feedback for lane button
if (laneButtons[lane]) {
laneButtons[lane].alpha = 0.6;
tween(laneButtons[lane], {
scaleX: 2.0,
scaleY: 2.0
}, {
duration: 150
});
}
// Release hold notes in this lane
for (var i = 0; i < notes.length; i++) {
var note = notes[i];
if (note.lane === lane && note.type === 'hold' && note.holdPressed) {
note.holdPressed = false;
// Score based on hold duration
if (note.holdDuration >= note.maxHoldDuration * 0.8) {
combo++;
updateScore('perfect');
} else if (note.holdDuration >= note.maxHoldDuration * 0.5) {
updateScore('good');
} else {
combo = 0;
}
}
}
};
game.update = function () {
// Handle menu mode
if (menuVisible) {
// Add pulsing effect to menu buttons
for (var i = 0; i < songButtons.length; i++) {
var button = songButtons[i];
var pulse = Math.sin(LK.ticks * 0.1 + i * 0.3) * 0.1 + 0.7;
button.bg.alpha = pulse;
}
return;
}
// Game started flag
if (!gameStarted) {
gameStarted = true;
}
// Add pulsing glow effect to hit zone
var hitZonePulse = Math.sin(LK.ticks * 0.15) * 0.1 + 0.2;
hitZone.alpha = hitZonePulse;
// Add subtle pulsing effect to lane buttons
for (var i = 0; i < laneButtons.length; i++) {
if (!activeTouches[i]) {
var buttonPulse = Math.sin(LK.ticks * 0.1 + i * 0.5) * 0.1 + 0.6;
laneButtons[i].alpha = buttonPulse;
}
}
// Handle song-specific note spawning
if (selectedSong === 'random') {
// Spawn notes randomly
noteSpawnTimer++;
if (noteSpawnTimer >= 40) {
spawnNote();
noteSpawnTimer = 0;
}
} else {
// Spawn notes based on song pattern
var currentSong = songs[selectedSong];
if (currentSong.notes && songNoteIndex < currentSong.notes.length) {
var currentTime = (LK.ticks - songStartTime) * (1000 / 60); // Convert ticks to milliseconds
var nextNote = currentSong.notes[songNoteIndex];
if (currentTime >= nextNote.time) {
spawnNote(nextNote.lane, nextNote.type, nextNote.duration);
songNoteIndex++;
}
}
// End song when all notes are played and cleared
if (songNoteIndex >= currentSong.notes.length && notes.length === 0) {
LK.setTimeout(function () {
showMenu();
}, 2000);
}
}
// Clean up off-screen notes
for (var i = notes.length - 1; i >= 0; i--) {
var note = notes[i];
// Remove notes that are off screen or completed
if (note.y > 2800 || note.hit && note.type === 'regular' || note.hit && note.type === 'hold' && !note.holdPressed && note.holdDuration > 0) {
note.destroy();
notes.splice(i, 1);
}
}
// Update hold note states
for (var lane = 0; lane < 4; lane++) {
if (!activeTouches[lane]) {
for (var j = 0; j < notes.length; j++) {
var note = notes[j];
if (note.lane === lane && note.type === 'hold' && note.holdPressed) {
note.holdPressed = false;
}
}
}
}
// Add combo glow effect
if (combo > 10) {
var comboColors = [0xff65d7, 0x9d65ff, 0x65b3ff, 0xff8fe6];
var colorIndex = Math.floor(LK.ticks / 10 % comboColors.length);
comboText.fill = comboColors[colorIndex];
}
// End game condition (for demo purposes, end after reaching high score)
if (score >= 50000) {
LK.showYouWin();
}
};