/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
// Game starts in menu state
var MenuButton = Container.expand(function (text, onClickCallback) {
var self = Container.call(this);
// Create button background
var buttonBg = self.attachAsset('notePad', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3.0,
scaleY: 2.4
});
buttonBg.tint = 0xffffff;
// Create button text
var buttonText = new Text2(text, {
size: 120,
fill: 0x000000
});
buttonText.anchor.set(0.5, 0.5);
self.addChild(buttonText);
self.onClickCallback = onClickCallback;
self.down = function (x, y, obj) {
buttonBg.tint = 0xcccccc;
if (self.onClickCallback) {
self.onClickCallback();
}
};
self.up = function (x, y, obj) {
buttonBg.tint = 0xffffff;
};
return self;
});
var Note = Container.expand(function (lane, isRedMode) {
var self = Container.call(this);
var noteGraphics = self.attachAsset('note', {
anchorX: 0.5,
anchorY: 0.5
});
self.lane = lane; // 0 = left, 1 = center, 2 = right
self.speed = Note.prototype.speed || 8;
self.hasBeenHit = false;
self.isRedMode = isRedMode || false;
self.startX = 0; // Track starting X position for swipe detection
self.hasStartedDrag = false;
// Make red notes visually distinct
if (self.isRedMode) {
noteGraphics.tint = 0xff0000; // Red color
}
self.update = function () {
self.y += self.speed;
};
self.down = function (x, y, obj) {
if (!self.hasBeenHit) {
if (self.isRedMode) {
// For red notes, track starting position for swipe detection
self.startX = x;
self.hasStartedDrag = true;
} else {
// Normal note behavior
self.hasBeenHit = true;
gameScore += 5;
gameCombo += 1;
// Combo system logic
combo += 1;
if (combo >= 10 && !feverMode) {
feverMode = true;
LK.effects.flashScreen(0xFFD700, 200);
game.setBackgroundColor(0xFFD700); // Gold
LK.setTimeout(function () {
feverMode = false;
game.setBackgroundColor(0x1a1a2e);
combo = 0;
}, 5000);
}
comboDisplayText.setText("Perfect!");
comboDisplayText.visible = true;
LK.setTimeout(function () {
comboDisplayText.visible = false;
}, 500);
// Create explosion effect
createExplosionEffect(self.x, self.y);
// Change background color effect
var colors = [0xff6b6b, 0x4ecdc4, 0x45b7d1, 0x96ceb4, 0xfeca57, 0xff9ff3, 0x54a0ff];
var randomColor = colors[Math.floor(Math.random() * colors.length)];
game.setBackgroundColor(randomColor);
// Visual effect
tween(self, {
alpha: 0,
scaleX: 2,
scaleY: 2
}, {
duration: 200,
onFinish: function onFinish() {
removeNote(self);
}
});
LK.getSound('success').play();
updateUI();
}
}
};
self.up = function (x, y, obj) {
if (self.isRedMode && self.hasStartedDrag && !self.hasBeenHit) {
// Check if user swiped left (moved at least 100 pixels to the left)
var swipeDistance = self.startX - x;
if (swipeDistance > 100) {
// Successful left swipe on red note
self.hasBeenHit = true;
gameScore += 10; // Red notes give 10 points
gameCombo += 1;
// Combo system logic
combo += 1;
if (combo >= 10 && !feverMode) {
feverMode = true;
LK.effects.flashScreen(0xFFD700, 200);
game.setBackgroundColor(0xFFD700); // Gold
LK.setTimeout(function () {
feverMode = false;
game.setBackgroundColor(0x1a1a2e);
combo = 0;
}, 5000);
}
comboDisplayText.setText("Perfect!");
comboDisplayText.visible = true;
LK.setTimeout(function () {
comboDisplayText.visible = false;
}, 500);
// Create explosion effect
createExplosionEffect(self.x, self.y);
// Change background color effect
var colors = [0xff6b6b, 0x4ecdc4, 0x45b7d1, 0x96ceb4, 0xfeca57, 0xff9ff3, 0x54a0ff];
var randomColor = colors[Math.floor(Math.random() * colors.length)];
game.setBackgroundColor(randomColor);
// Visual effect
tween(self, {
alpha: 0,
scaleX: 2,
scaleY: 2
}, {
duration: 200,
onFinish: function onFinish() {
removeNote(self);
}
});
LK.getSound('success').play();
updateUI();
}
}
self.hasStartedDrag = false;
};
return self;
});
var NotePad = Container.expand(function (lane) {
var self = Container.call(this);
var padGraphics = self.attachAsset('notePad', {
anchorX: 0.5,
anchorY: 0.5
});
var perfectZone = self.attachAsset('perfectZone', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.3
});
self.lane = lane;
self.isPressed = false;
self.down = function (x, y, obj) {
self.isPressed = true;
checkNoteHit(self.lane);
};
self.up = function (x, y, obj) {
self.isPressed = false;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a1a2e
});
/****
* Game Code
****/
// Create background
var background = LK.getAsset('Background', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
});
game.addChild(background);
var notes = [];
var gameScore = 0;
var gameCombo = 0;
var spawnTimer = 0;
var spawnInterval = 45; // 0.75 seconds at 60fps
var perfectZoneY = 2400;
var perfectTolerance = 100;
var noteCounter = 0; // Track total notes spawned for red mode
var combo = 0;
var feverMode = false;
var comboDisplayText;
var scoreDisplayText;
// Game state
var gameState = 'menu'; // 'menu', 'difficulty', or 'playing'
var selectedDifficulty = 'normal';
var mainMenu = new Container();
var difficultyMenu = new Container();
var howToPlayMenu = new Container();
var gameContainer = new Container();
// Main Menu Elements
var titleText = new Text2('Rhythm Tap Challenge', {
size: 80,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 800;
mainMenu.addChild(titleText);
var startButton = new MenuButton('START', function () {
showDifficultyMenu();
});
startButton.x = 1024;
startButton.y = 1200;
mainMenu.addChild(startButton);
var howToPlayButton = new MenuButton('HOW TO PLAY', function () {
showHowToPlayMenu();
});
// Enlarge the green area behind the How To Play button
howToPlayButton.children[0].scaleX = 5.0;
howToPlayButton.children[0].scaleY = 4.0;
howToPlayButton.x = 1024;
howToPlayButton.y = 1500;
mainMenu.addChild(howToPlayButton);
// Difficulty Menu Elements
var difficultyTitle = new Text2('Select Difficulty', {
size: 70,
fill: 0xFFFFFF
});
difficultyTitle.anchor.set(0.5, 0.5);
difficultyTitle.x = 1024;
difficultyTitle.y = 800;
difficultyMenu.addChild(difficultyTitle);
var easyButton = new MenuButton('EASY', function () {
selectedDifficulty = 'easy';
startGame();
});
easyButton.x = 1024;
easyButton.y = 1000;
difficultyMenu.addChild(easyButton);
var normalButton = new MenuButton('NORMAL', function () {
selectedDifficulty = 'normal';
startGame();
});
normalButton.x = 1024;
normalButton.y = 1150;
difficultyMenu.addChild(normalButton);
var hardButton = new MenuButton('HARD', function () {
selectedDifficulty = 'hard';
startGame();
});
hardButton.x = 1024;
hardButton.y = 1300;
difficultyMenu.addChild(hardButton);
var backButton = new MenuButton('BACK', function () {
showMainMenu();
});
backButton.x = 1024;
backButton.y = 1500;
difficultyMenu.addChild(backButton);
// How To Play Menu Elements
var howToPlayTitle = new Text2('How To Play', {
size: 70,
fill: 0xFFFFFF
});
howToPlayTitle.anchor.set(0.5, 0.5);
howToPlayTitle.x = 1024;
howToPlayTitle.y = 600;
howToPlayMenu.addChild(howToPlayTitle);
var instructionsText1 = new Text2('Notes fall from 3 different points', {
size: 50,
fill: 0xFFFFFF
});
instructionsText1.anchor.set(0.5, 0.5);
instructionsText1.x = 1024;
instructionsText1.y = 900;
howToPlayMenu.addChild(instructionsText1);
var instructionsText2 = new Text2('from top to bottom.', {
size: 50,
fill: 0xFFFFFF
});
instructionsText2.anchor.set(0.5, 0.5);
instructionsText2.x = 1024;
instructionsText2.y = 970;
howToPlayMenu.addChild(instructionsText2);
var instructionsText3 = new Text2('Touch the notes before they disappear', {
size: 50,
fill: 0xFFFFFF
});
instructionsText3.anchor.set(0.5, 0.5);
instructionsText3.x = 1024;
instructionsText3.y = 1040;
howToPlayMenu.addChild(instructionsText3);
var instructionsText4 = new Text2('and earn 5 points.', {
size: 50,
fill: 0xFFFFFF
});
instructionsText4.anchor.set(0.5, 0.5);
instructionsText4.x = 1024;
instructionsText4.y = 1110;
howToPlayMenu.addChild(instructionsText4);
var instructionsText5 = new Text2('If you cannot touch them in time,', {
size: 50,
fill: 0xFFFFFF
});
instructionsText5.anchor.set(0.5, 0.5);
instructionsText5.x = 1024;
instructionsText5.y = 1180;
howToPlayMenu.addChild(instructionsText5);
var instructionsText6 = new Text2('your 5 points will decrease and', {
size: 50,
fill: 0xFFFFFF
});
instructionsText6.anchor.set(0.5, 0.5);
instructionsText6.x = 1024;
instructionsText6.y = 1250;
howToPlayMenu.addChild(instructionsText6);
var instructionsText7 = new Text2('the game will end at -50 points', {
size: 50,
fill: 0xFFFFFF
});
instructionsText7.anchor.set(0.5, 0.5);
instructionsText7.x = 1024;
instructionsText7.y = 1320;
howToPlayMenu.addChild(instructionsText7);
var instructionsText8 = new Text2('We must move the red note that comes every 25 notes', {
size: 50,
fill: 0xFFFFFF
});
instructionsText8.anchor.set(0.5, 0.5);
instructionsText8.x = 1024;
instructionsText8.y = 1390;
howToPlayMenu.addChild(instructionsText8);
var instructionsText9 = new Text2('to the left. If we move it, we gain 20 points.', {
size: 50,
fill: 0xFFFFFF
});
instructionsText9.anchor.set(0.5, 0.5);
instructionsText9.x = 1024;
instructionsText9.y = 1460;
howToPlayMenu.addChild(instructionsText9);
var instructionsText10 = new Text2('If we cannot, our 20 points will be deducted', {
size: 50,
fill: 0xFFFFFF
});
instructionsText10.anchor.set(0.5, 0.5);
instructionsText10.x = 1024;
instructionsText10.y = 1530;
howToPlayMenu.addChild(instructionsText10);
var backFromHowToPlayButton = new MenuButton('BACK', function () {
showMainMenu();
});
backFromHowToPlayButton.x = 1024;
backFromHowToPlayButton.y = 1650;
howToPlayMenu.addChild(backFromHowToPlayButton);
game.addChild(mainMenu);
game.addChild(difficultyMenu);
game.addChild(howToPlayMenu);
difficultyMenu.visible = false;
howToPlayMenu.visible = false;
// UI Elements
var scoreText = new Text2('0', {
size: 60,
fill: 0xFFFFFF
});
scoreText.anchor.set(0.5, 0);
scoreText.x = 0;
scoreText.y = 50;
LK.gui.top.addChild(scoreText);
var comboText = new Text2('Combo: 0', {
size: 50,
fill: 0xFFFF00
});
comboText.anchor.set(1, 0);
comboText.x = -50;
comboText.y = 130;
LK.gui.topRight.addChild(comboText);
var highScoreText = new Text2('High Score: ' + (storage.highScore || 0), {
size: 40,
fill: 0xFFFFFF
});
highScoreText.anchor.set(1, 0);
highScoreText.x = -50;
highScoreText.y = 200;
LK.gui.topRight.addChild(highScoreText);
// Hide UI initially
scoreText.visible = false;
comboText.visible = false;
highScoreText.visible = false;
// Create restart button for game screen
var restartButton = new MenuButton('↺', function () {
// Reset game state
gameScore = 0;
gameCombo = 0;
spawnTimer = 0;
noteCounter = 0; // Reset note counter for red mode
// Clear all notes
for (var i = notes.length - 1; i >= 0; i--) {
removeNote(notes[i]);
}
updateUI();
});
restartButton.x = 924;
restartButton.y = 50;
restartButton.scaleX = 0.42;
restartButton.scaleY = 0.42;
gameContainer.addChild(restartButton);
// Create return to main menu button for game screen
var returnMenuButton = new MenuButton('🏠', function () {
// Stop game music and start menu music
LK.stopMusic();
LK.playMusic('menumusic', {
loop: true
});
gameState = 'menu';
gameContainer.visible = false;
scoreText.visible = false;
comboText.visible = false;
highScoreText.visible = false;
// Clear all notes
for (var i = notes.length - 1; i >= 0; i--) {
removeNote(notes[i]);
}
// Reset game variables
gameScore = 0;
gameCombo = 0;
spawnTimer = 0;
noteCounter = 0; // Reset note counter for red mode
updateUI();
// Show main menu
showMainMenu();
});
returnMenuButton.x = 1124;
returnMenuButton.y = 50;
returnMenuButton.scaleX = 0.42;
returnMenuButton.scaleY = 0.42;
gameContainer.addChild(returnMenuButton);
// Create note pads
var padPositions = [512, 1024, 1536]; // Left, center, right positions
game.addChild(gameContainer);
gameContainer.visible = false;
// Create intro text
var introText = new Text2('lighten the darkness with music', {
size: 80,
fill: 0xFFFFFF
});
introText.anchor.set(0.5, 0.5);
introText.x = 1024;
introText.y = 1366;
introText.alpha = 1;
game.addChild(introText);
// Create black overlay for screen blackout effect
var blackOverlay = LK.getAsset('notePad', {
anchorX: 0,
anchorY: 0,
scaleX: 10,
scaleY: 35,
x: 0,
y: 0
});
blackOverlay.tint = 0x000000;
blackOverlay.alpha = 0.8;
game.addChild(blackOverlay);
// Initialize combo and score display text
comboDisplayText = new Text2("", {
size: 64,
fill: 0xFFFFFF
});
comboDisplayText.anchor.set(0.5, 0.5);
comboDisplayText.x = 360;
comboDisplayText.y = 300;
comboDisplayText.visible = false;
game.addChild(comboDisplayText);
scoreDisplayText = new Text2("", {
size: 24,
fill: 0xFFFFFF
});
scoreDisplayText.anchor.set(0.5, 0.5);
scoreDisplayText.x = 360;
scoreDisplayText.y = 50;
game.addChild(scoreDisplayText);
// Show intro text with fade in effect and black screen - starting 4.5 seconds earlier
LK.setTimeout(function () {
// Hold for 2 seconds then fade out
LK.setTimeout(function () {
tween(introText, {
alpha: 0
}, {
duration: 1500,
onFinish: function onFinish() {
game.removeChild(introText);
}
});
// Fade black overlay out
tween(blackOverlay, {
alpha: 0
}, {
duration: 1500,
onFinish: function onFinish() {
game.removeChild(blackOverlay);
}
});
}, 2000);
}, 0); // Start 4.5 seconds earlier (reduced from 500ms to 0ms)
// Start menu music
LK.playMusic('menumusic', {
loop: true
});
function showDifficultyMenu() {
gameState = 'difficulty';
mainMenu.visible = false;
difficultyMenu.visible = true;
}
function showHowToPlayMenu() {
gameState = 'howtoplay';
mainMenu.visible = false;
howToPlayMenu.visible = true;
}
function showMainMenu() {
gameState = 'menu';
mainMenu.visible = true;
difficultyMenu.visible = false;
howToPlayMenu.visible = false;
}
function startGame() {
gameState = 'playing';
mainMenu.visible = false;
difficultyMenu.visible = false;
gameContainer.visible = true;
scoreText.visible = true;
comboText.visible = true;
highScoreText.visible = true;
// Reset game state
gameScore = 0;
gameCombo = 0;
spawnTimer = 0;
noteCounter = 0; // Reset note counter for red mode
// Set note speed based on difficulty
var noteSpeed = 8; // normal speed
if (selectedDifficulty === 'easy') {
noteSpeed = 12; // 1.5 times faster than normal (8 * 1.5)
} else if (selectedDifficulty === 'normal') {
noteSpeed = 28; // 3.5 times faster than normal (8 * 3.5)
} else if (selectedDifficulty === 'hard') {
noteSpeed = 32; // 4 times faster than normal (8 * 4)
}
// Update all note pads with new speed
Note.prototype.speed = noteSpeed;
// Clear any existing notes
for (var i = notes.length - 1; i >= 0; i--) {
removeNote(notes[i]);
}
// Stop menu music and start game music
LK.stopMusic();
LK.playMusic('bgmusic', {
loop: true
});
updateUI();
// Update high score display for selected difficulty
var highScoreKey = 'highScore_' + selectedDifficulty;
highScoreText.setText('Best (' + selectedDifficulty.toUpperCase() + '): ' + (storage[highScoreKey] || 0));
}
function spawnNote() {
var randomLane = Math.floor(Math.random() * 3);
noteCounter++;
// Create red note every 25 notes
var isRedMode = noteCounter % 25 === 0;
var note = new Note(randomLane, isRedMode);
note.x = padPositions[randomLane];
note.y = -50;
notes.push(note);
gameContainer.addChild(note);
}
function checkNoteHit(lane) {
var hitNote = null;
var bestDistance = Infinity;
// Find the closest note in the perfect zone for this lane
for (var i = 0; i < notes.length; i++) {
var note = notes[i];
if (note.lane === lane && !note.hasBeenHit) {
var distance = Math.abs(note.y - perfectZoneY);
if (distance < perfectTolerance && distance < bestDistance) {
hitNote = note;
bestDistance = distance;
}
}
}
if (hitNote) {
// Perfect hit
hitNote.hasBeenHit = true;
gameScore += 5;
gameCombo += 1;
// Create explosion effect
createExplosionEffect(hitNote.x, hitNote.y);
// Change background color effect
var colors = [0xff6b6b, 0x4ecdc4, 0x45b7d1, 0x96ceb4, 0xfeca57, 0xff9ff3, 0x54a0ff];
var randomColor = colors[Math.floor(Math.random() * colors.length)];
game.setBackgroundColor(randomColor);
// Visual effect
tween(hitNote, {
alpha: 0,
scaleX: 2,
scaleY: 2
}, {
duration: 200,
onFinish: function onFinish() {
removeNote(hitNote);
}
});
LK.getSound('success').play();
updateUI();
} else {
// Wrong timing
gameScore -= 5;
gameCombo = 0;
LK.effects.flashScreen(0xff0000, 200);
updateUI();
}
}
function createExplosionEffect(x, y) {
// Create multiple explosion particles
for (var i = 0; i < 8; i++) {
var particle = LK.getAsset('note', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3,
scaleY: 0.3
});
particle.x = x;
particle.y = y;
particle.tint = 0xFFFF00; // Yellow explosion color
gameContainer.addChild(particle);
// Random direction for each particle
var angle = i / 8 * Math.PI * 2;
var speed = 100 + Math.random() * 50;
var targetX = x + Math.cos(angle) * speed;
var targetY = y + Math.sin(angle) * speed;
// Animate particle outward and fade
tween(particle, {
x: targetX,
y: targetY,
alpha: 0,
scaleX: 0.1,
scaleY: 0.1
}, {
duration: 400,
easing: tween.easeOut,
onFinish: function onFinish() {
gameContainer.removeChild(particle);
}
});
}
}
function showComboEffect(x, y) {
var comboEffect = new Text2('COMBO!', {
size: 80,
fill: 0xFFFF00
});
comboEffect.anchor.set(0.5, 0.5);
comboEffect.x = x;
comboEffect.y = y;
gameContainer.addChild(comboEffect);
tween(comboEffect, {
y: y - 100,
alpha: 0,
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
gameContainer.removeChild(comboEffect);
}
});
}
function removeNote(note) {
var index = notes.indexOf(note);
if (index > -1) {
notes.splice(index, 1);
gameContainer.removeChild(note);
}
}
function updateUI() {
scoreText.setText(gameScore);
comboText.setText('Combo: ' + gameCombo);
LK.setScore(gameScore);
// Update high score if current score is higher
var highScoreKey = 'highScore_' + selectedDifficulty;
var currentHighScore = storage[highScoreKey] || 0;
if (gameScore > currentHighScore) {
storage[highScoreKey] = gameScore;
}
highScoreText.setText('Best (' + selectedDifficulty.toUpperCase() + '): ' + (storage[highScoreKey] || 0));
// Check for game over at -50 points
if (gameScore <= -50) {
LK.showGameOver();
}
}
// Add pause menu functionality
var pauseMenu = new Container();
var pauseTitle = new Text2('Game Paused', {
size: 70,
fill: 0xFFFFFF
});
pauseTitle.anchor.set(0.5, 0.5);
pauseTitle.x = 1024;
pauseTitle.y = 1000;
pauseMenu.addChild(pauseTitle);
var resumeButton = new MenuButton('RESUME', function () {
pauseMenu.visible = false;
});
resumeButton.x = 1024;
resumeButton.y = 1200;
pauseMenu.addChild(resumeButton);
var mainMenuButton = new MenuButton('MAIN MENU', function () {
// Stop game music and start menu music
LK.stopMusic();
LK.playMusic('menumusic', {
loop: true
});
gameState = 'menu';
pauseMenu.visible = false;
gameContainer.visible = false;
scoreText.visible = false;
comboText.visible = false;
highScoreText.visible = false;
// Clear all notes
for (var i = notes.length - 1; i >= 0; i--) {
removeNote(notes[i]);
}
// Reset game variables
gameScore = 0;
gameCombo = 0;
spawnTimer = 0;
noteCounter = 0; // Reset note counter for red mode
updateUI();
// Show main menu
showMainMenu();
});
mainMenuButton.x = 1024;
mainMenuButton.y = 1350;
pauseMenu.addChild(mainMenuButton);
game.addChild(pauseMenu);
pauseMenu.visible = false;
game.update = function () {
if (gameState !== 'playing') {
return;
}
// Spawn notes
spawnTimer++;
if (spawnTimer >= spawnInterval) {
spawnNote();
spawnTimer = 0;
}
// Update and check notes
for (var i = notes.length - 1; i >= 0; i--) {
var note = notes[i];
// Check if note reached bottom (missed)
if (note.y > 2800 && !note.hasBeenHit) {
if (note.isRedMode) {
gameScore -= 20; // Red notes penalty is -20 points
} else {
gameScore -= 5; // Normal notes penalty is -5 points
}
gameCombo = 0;
// Combo system reset on miss
combo = 0;
comboDisplayText.setText("Miss!");
comboDisplayText.visible = true;
LK.setTimeout(function () {
comboDisplayText.visible = false;
}, 500);
LK.getSound('fail').play();
updateUI();
removeNote(note);
}
}
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
// Game starts in menu state
var MenuButton = Container.expand(function (text, onClickCallback) {
var self = Container.call(this);
// Create button background
var buttonBg = self.attachAsset('notePad', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3.0,
scaleY: 2.4
});
buttonBg.tint = 0xffffff;
// Create button text
var buttonText = new Text2(text, {
size: 120,
fill: 0x000000
});
buttonText.anchor.set(0.5, 0.5);
self.addChild(buttonText);
self.onClickCallback = onClickCallback;
self.down = function (x, y, obj) {
buttonBg.tint = 0xcccccc;
if (self.onClickCallback) {
self.onClickCallback();
}
};
self.up = function (x, y, obj) {
buttonBg.tint = 0xffffff;
};
return self;
});
var Note = Container.expand(function (lane, isRedMode) {
var self = Container.call(this);
var noteGraphics = self.attachAsset('note', {
anchorX: 0.5,
anchorY: 0.5
});
self.lane = lane; // 0 = left, 1 = center, 2 = right
self.speed = Note.prototype.speed || 8;
self.hasBeenHit = false;
self.isRedMode = isRedMode || false;
self.startX = 0; // Track starting X position for swipe detection
self.hasStartedDrag = false;
// Make red notes visually distinct
if (self.isRedMode) {
noteGraphics.tint = 0xff0000; // Red color
}
self.update = function () {
self.y += self.speed;
};
self.down = function (x, y, obj) {
if (!self.hasBeenHit) {
if (self.isRedMode) {
// For red notes, track starting position for swipe detection
self.startX = x;
self.hasStartedDrag = true;
} else {
// Normal note behavior
self.hasBeenHit = true;
gameScore += 5;
gameCombo += 1;
// Combo system logic
combo += 1;
if (combo >= 10 && !feverMode) {
feverMode = true;
LK.effects.flashScreen(0xFFD700, 200);
game.setBackgroundColor(0xFFD700); // Gold
LK.setTimeout(function () {
feverMode = false;
game.setBackgroundColor(0x1a1a2e);
combo = 0;
}, 5000);
}
comboDisplayText.setText("Perfect!");
comboDisplayText.visible = true;
LK.setTimeout(function () {
comboDisplayText.visible = false;
}, 500);
// Create explosion effect
createExplosionEffect(self.x, self.y);
// Change background color effect
var colors = [0xff6b6b, 0x4ecdc4, 0x45b7d1, 0x96ceb4, 0xfeca57, 0xff9ff3, 0x54a0ff];
var randomColor = colors[Math.floor(Math.random() * colors.length)];
game.setBackgroundColor(randomColor);
// Visual effect
tween(self, {
alpha: 0,
scaleX: 2,
scaleY: 2
}, {
duration: 200,
onFinish: function onFinish() {
removeNote(self);
}
});
LK.getSound('success').play();
updateUI();
}
}
};
self.up = function (x, y, obj) {
if (self.isRedMode && self.hasStartedDrag && !self.hasBeenHit) {
// Check if user swiped left (moved at least 100 pixels to the left)
var swipeDistance = self.startX - x;
if (swipeDistance > 100) {
// Successful left swipe on red note
self.hasBeenHit = true;
gameScore += 10; // Red notes give 10 points
gameCombo += 1;
// Combo system logic
combo += 1;
if (combo >= 10 && !feverMode) {
feverMode = true;
LK.effects.flashScreen(0xFFD700, 200);
game.setBackgroundColor(0xFFD700); // Gold
LK.setTimeout(function () {
feverMode = false;
game.setBackgroundColor(0x1a1a2e);
combo = 0;
}, 5000);
}
comboDisplayText.setText("Perfect!");
comboDisplayText.visible = true;
LK.setTimeout(function () {
comboDisplayText.visible = false;
}, 500);
// Create explosion effect
createExplosionEffect(self.x, self.y);
// Change background color effect
var colors = [0xff6b6b, 0x4ecdc4, 0x45b7d1, 0x96ceb4, 0xfeca57, 0xff9ff3, 0x54a0ff];
var randomColor = colors[Math.floor(Math.random() * colors.length)];
game.setBackgroundColor(randomColor);
// Visual effect
tween(self, {
alpha: 0,
scaleX: 2,
scaleY: 2
}, {
duration: 200,
onFinish: function onFinish() {
removeNote(self);
}
});
LK.getSound('success').play();
updateUI();
}
}
self.hasStartedDrag = false;
};
return self;
});
var NotePad = Container.expand(function (lane) {
var self = Container.call(this);
var padGraphics = self.attachAsset('notePad', {
anchorX: 0.5,
anchorY: 0.5
});
var perfectZone = self.attachAsset('perfectZone', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.3
});
self.lane = lane;
self.isPressed = false;
self.down = function (x, y, obj) {
self.isPressed = true;
checkNoteHit(self.lane);
};
self.up = function (x, y, obj) {
self.isPressed = false;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a1a2e
});
/****
* Game Code
****/
// Create background
var background = LK.getAsset('Background', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
});
game.addChild(background);
var notes = [];
var gameScore = 0;
var gameCombo = 0;
var spawnTimer = 0;
var spawnInterval = 45; // 0.75 seconds at 60fps
var perfectZoneY = 2400;
var perfectTolerance = 100;
var noteCounter = 0; // Track total notes spawned for red mode
var combo = 0;
var feverMode = false;
var comboDisplayText;
var scoreDisplayText;
// Game state
var gameState = 'menu'; // 'menu', 'difficulty', or 'playing'
var selectedDifficulty = 'normal';
var mainMenu = new Container();
var difficultyMenu = new Container();
var howToPlayMenu = new Container();
var gameContainer = new Container();
// Main Menu Elements
var titleText = new Text2('Rhythm Tap Challenge', {
size: 80,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 800;
mainMenu.addChild(titleText);
var startButton = new MenuButton('START', function () {
showDifficultyMenu();
});
startButton.x = 1024;
startButton.y = 1200;
mainMenu.addChild(startButton);
var howToPlayButton = new MenuButton('HOW TO PLAY', function () {
showHowToPlayMenu();
});
// Enlarge the green area behind the How To Play button
howToPlayButton.children[0].scaleX = 5.0;
howToPlayButton.children[0].scaleY = 4.0;
howToPlayButton.x = 1024;
howToPlayButton.y = 1500;
mainMenu.addChild(howToPlayButton);
// Difficulty Menu Elements
var difficultyTitle = new Text2('Select Difficulty', {
size: 70,
fill: 0xFFFFFF
});
difficultyTitle.anchor.set(0.5, 0.5);
difficultyTitle.x = 1024;
difficultyTitle.y = 800;
difficultyMenu.addChild(difficultyTitle);
var easyButton = new MenuButton('EASY', function () {
selectedDifficulty = 'easy';
startGame();
});
easyButton.x = 1024;
easyButton.y = 1000;
difficultyMenu.addChild(easyButton);
var normalButton = new MenuButton('NORMAL', function () {
selectedDifficulty = 'normal';
startGame();
});
normalButton.x = 1024;
normalButton.y = 1150;
difficultyMenu.addChild(normalButton);
var hardButton = new MenuButton('HARD', function () {
selectedDifficulty = 'hard';
startGame();
});
hardButton.x = 1024;
hardButton.y = 1300;
difficultyMenu.addChild(hardButton);
var backButton = new MenuButton('BACK', function () {
showMainMenu();
});
backButton.x = 1024;
backButton.y = 1500;
difficultyMenu.addChild(backButton);
// How To Play Menu Elements
var howToPlayTitle = new Text2('How To Play', {
size: 70,
fill: 0xFFFFFF
});
howToPlayTitle.anchor.set(0.5, 0.5);
howToPlayTitle.x = 1024;
howToPlayTitle.y = 600;
howToPlayMenu.addChild(howToPlayTitle);
var instructionsText1 = new Text2('Notes fall from 3 different points', {
size: 50,
fill: 0xFFFFFF
});
instructionsText1.anchor.set(0.5, 0.5);
instructionsText1.x = 1024;
instructionsText1.y = 900;
howToPlayMenu.addChild(instructionsText1);
var instructionsText2 = new Text2('from top to bottom.', {
size: 50,
fill: 0xFFFFFF
});
instructionsText2.anchor.set(0.5, 0.5);
instructionsText2.x = 1024;
instructionsText2.y = 970;
howToPlayMenu.addChild(instructionsText2);
var instructionsText3 = new Text2('Touch the notes before they disappear', {
size: 50,
fill: 0xFFFFFF
});
instructionsText3.anchor.set(0.5, 0.5);
instructionsText3.x = 1024;
instructionsText3.y = 1040;
howToPlayMenu.addChild(instructionsText3);
var instructionsText4 = new Text2('and earn 5 points.', {
size: 50,
fill: 0xFFFFFF
});
instructionsText4.anchor.set(0.5, 0.5);
instructionsText4.x = 1024;
instructionsText4.y = 1110;
howToPlayMenu.addChild(instructionsText4);
var instructionsText5 = new Text2('If you cannot touch them in time,', {
size: 50,
fill: 0xFFFFFF
});
instructionsText5.anchor.set(0.5, 0.5);
instructionsText5.x = 1024;
instructionsText5.y = 1180;
howToPlayMenu.addChild(instructionsText5);
var instructionsText6 = new Text2('your 5 points will decrease and', {
size: 50,
fill: 0xFFFFFF
});
instructionsText6.anchor.set(0.5, 0.5);
instructionsText6.x = 1024;
instructionsText6.y = 1250;
howToPlayMenu.addChild(instructionsText6);
var instructionsText7 = new Text2('the game will end at -50 points', {
size: 50,
fill: 0xFFFFFF
});
instructionsText7.anchor.set(0.5, 0.5);
instructionsText7.x = 1024;
instructionsText7.y = 1320;
howToPlayMenu.addChild(instructionsText7);
var instructionsText8 = new Text2('We must move the red note that comes every 25 notes', {
size: 50,
fill: 0xFFFFFF
});
instructionsText8.anchor.set(0.5, 0.5);
instructionsText8.x = 1024;
instructionsText8.y = 1390;
howToPlayMenu.addChild(instructionsText8);
var instructionsText9 = new Text2('to the left. If we move it, we gain 20 points.', {
size: 50,
fill: 0xFFFFFF
});
instructionsText9.anchor.set(0.5, 0.5);
instructionsText9.x = 1024;
instructionsText9.y = 1460;
howToPlayMenu.addChild(instructionsText9);
var instructionsText10 = new Text2('If we cannot, our 20 points will be deducted', {
size: 50,
fill: 0xFFFFFF
});
instructionsText10.anchor.set(0.5, 0.5);
instructionsText10.x = 1024;
instructionsText10.y = 1530;
howToPlayMenu.addChild(instructionsText10);
var backFromHowToPlayButton = new MenuButton('BACK', function () {
showMainMenu();
});
backFromHowToPlayButton.x = 1024;
backFromHowToPlayButton.y = 1650;
howToPlayMenu.addChild(backFromHowToPlayButton);
game.addChild(mainMenu);
game.addChild(difficultyMenu);
game.addChild(howToPlayMenu);
difficultyMenu.visible = false;
howToPlayMenu.visible = false;
// UI Elements
var scoreText = new Text2('0', {
size: 60,
fill: 0xFFFFFF
});
scoreText.anchor.set(0.5, 0);
scoreText.x = 0;
scoreText.y = 50;
LK.gui.top.addChild(scoreText);
var comboText = new Text2('Combo: 0', {
size: 50,
fill: 0xFFFF00
});
comboText.anchor.set(1, 0);
comboText.x = -50;
comboText.y = 130;
LK.gui.topRight.addChild(comboText);
var highScoreText = new Text2('High Score: ' + (storage.highScore || 0), {
size: 40,
fill: 0xFFFFFF
});
highScoreText.anchor.set(1, 0);
highScoreText.x = -50;
highScoreText.y = 200;
LK.gui.topRight.addChild(highScoreText);
// Hide UI initially
scoreText.visible = false;
comboText.visible = false;
highScoreText.visible = false;
// Create restart button for game screen
var restartButton = new MenuButton('↺', function () {
// Reset game state
gameScore = 0;
gameCombo = 0;
spawnTimer = 0;
noteCounter = 0; // Reset note counter for red mode
// Clear all notes
for (var i = notes.length - 1; i >= 0; i--) {
removeNote(notes[i]);
}
updateUI();
});
restartButton.x = 924;
restartButton.y = 50;
restartButton.scaleX = 0.42;
restartButton.scaleY = 0.42;
gameContainer.addChild(restartButton);
// Create return to main menu button for game screen
var returnMenuButton = new MenuButton('🏠', function () {
// Stop game music and start menu music
LK.stopMusic();
LK.playMusic('menumusic', {
loop: true
});
gameState = 'menu';
gameContainer.visible = false;
scoreText.visible = false;
comboText.visible = false;
highScoreText.visible = false;
// Clear all notes
for (var i = notes.length - 1; i >= 0; i--) {
removeNote(notes[i]);
}
// Reset game variables
gameScore = 0;
gameCombo = 0;
spawnTimer = 0;
noteCounter = 0; // Reset note counter for red mode
updateUI();
// Show main menu
showMainMenu();
});
returnMenuButton.x = 1124;
returnMenuButton.y = 50;
returnMenuButton.scaleX = 0.42;
returnMenuButton.scaleY = 0.42;
gameContainer.addChild(returnMenuButton);
// Create note pads
var padPositions = [512, 1024, 1536]; // Left, center, right positions
game.addChild(gameContainer);
gameContainer.visible = false;
// Create intro text
var introText = new Text2('lighten the darkness with music', {
size: 80,
fill: 0xFFFFFF
});
introText.anchor.set(0.5, 0.5);
introText.x = 1024;
introText.y = 1366;
introText.alpha = 1;
game.addChild(introText);
// Create black overlay for screen blackout effect
var blackOverlay = LK.getAsset('notePad', {
anchorX: 0,
anchorY: 0,
scaleX: 10,
scaleY: 35,
x: 0,
y: 0
});
blackOverlay.tint = 0x000000;
blackOverlay.alpha = 0.8;
game.addChild(blackOverlay);
// Initialize combo and score display text
comboDisplayText = new Text2("", {
size: 64,
fill: 0xFFFFFF
});
comboDisplayText.anchor.set(0.5, 0.5);
comboDisplayText.x = 360;
comboDisplayText.y = 300;
comboDisplayText.visible = false;
game.addChild(comboDisplayText);
scoreDisplayText = new Text2("", {
size: 24,
fill: 0xFFFFFF
});
scoreDisplayText.anchor.set(0.5, 0.5);
scoreDisplayText.x = 360;
scoreDisplayText.y = 50;
game.addChild(scoreDisplayText);
// Show intro text with fade in effect and black screen - starting 4.5 seconds earlier
LK.setTimeout(function () {
// Hold for 2 seconds then fade out
LK.setTimeout(function () {
tween(introText, {
alpha: 0
}, {
duration: 1500,
onFinish: function onFinish() {
game.removeChild(introText);
}
});
// Fade black overlay out
tween(blackOverlay, {
alpha: 0
}, {
duration: 1500,
onFinish: function onFinish() {
game.removeChild(blackOverlay);
}
});
}, 2000);
}, 0); // Start 4.5 seconds earlier (reduced from 500ms to 0ms)
// Start menu music
LK.playMusic('menumusic', {
loop: true
});
function showDifficultyMenu() {
gameState = 'difficulty';
mainMenu.visible = false;
difficultyMenu.visible = true;
}
function showHowToPlayMenu() {
gameState = 'howtoplay';
mainMenu.visible = false;
howToPlayMenu.visible = true;
}
function showMainMenu() {
gameState = 'menu';
mainMenu.visible = true;
difficultyMenu.visible = false;
howToPlayMenu.visible = false;
}
function startGame() {
gameState = 'playing';
mainMenu.visible = false;
difficultyMenu.visible = false;
gameContainer.visible = true;
scoreText.visible = true;
comboText.visible = true;
highScoreText.visible = true;
// Reset game state
gameScore = 0;
gameCombo = 0;
spawnTimer = 0;
noteCounter = 0; // Reset note counter for red mode
// Set note speed based on difficulty
var noteSpeed = 8; // normal speed
if (selectedDifficulty === 'easy') {
noteSpeed = 12; // 1.5 times faster than normal (8 * 1.5)
} else if (selectedDifficulty === 'normal') {
noteSpeed = 28; // 3.5 times faster than normal (8 * 3.5)
} else if (selectedDifficulty === 'hard') {
noteSpeed = 32; // 4 times faster than normal (8 * 4)
}
// Update all note pads with new speed
Note.prototype.speed = noteSpeed;
// Clear any existing notes
for (var i = notes.length - 1; i >= 0; i--) {
removeNote(notes[i]);
}
// Stop menu music and start game music
LK.stopMusic();
LK.playMusic('bgmusic', {
loop: true
});
updateUI();
// Update high score display for selected difficulty
var highScoreKey = 'highScore_' + selectedDifficulty;
highScoreText.setText('Best (' + selectedDifficulty.toUpperCase() + '): ' + (storage[highScoreKey] || 0));
}
function spawnNote() {
var randomLane = Math.floor(Math.random() * 3);
noteCounter++;
// Create red note every 25 notes
var isRedMode = noteCounter % 25 === 0;
var note = new Note(randomLane, isRedMode);
note.x = padPositions[randomLane];
note.y = -50;
notes.push(note);
gameContainer.addChild(note);
}
function checkNoteHit(lane) {
var hitNote = null;
var bestDistance = Infinity;
// Find the closest note in the perfect zone for this lane
for (var i = 0; i < notes.length; i++) {
var note = notes[i];
if (note.lane === lane && !note.hasBeenHit) {
var distance = Math.abs(note.y - perfectZoneY);
if (distance < perfectTolerance && distance < bestDistance) {
hitNote = note;
bestDistance = distance;
}
}
}
if (hitNote) {
// Perfect hit
hitNote.hasBeenHit = true;
gameScore += 5;
gameCombo += 1;
// Create explosion effect
createExplosionEffect(hitNote.x, hitNote.y);
// Change background color effect
var colors = [0xff6b6b, 0x4ecdc4, 0x45b7d1, 0x96ceb4, 0xfeca57, 0xff9ff3, 0x54a0ff];
var randomColor = colors[Math.floor(Math.random() * colors.length)];
game.setBackgroundColor(randomColor);
// Visual effect
tween(hitNote, {
alpha: 0,
scaleX: 2,
scaleY: 2
}, {
duration: 200,
onFinish: function onFinish() {
removeNote(hitNote);
}
});
LK.getSound('success').play();
updateUI();
} else {
// Wrong timing
gameScore -= 5;
gameCombo = 0;
LK.effects.flashScreen(0xff0000, 200);
updateUI();
}
}
function createExplosionEffect(x, y) {
// Create multiple explosion particles
for (var i = 0; i < 8; i++) {
var particle = LK.getAsset('note', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3,
scaleY: 0.3
});
particle.x = x;
particle.y = y;
particle.tint = 0xFFFF00; // Yellow explosion color
gameContainer.addChild(particle);
// Random direction for each particle
var angle = i / 8 * Math.PI * 2;
var speed = 100 + Math.random() * 50;
var targetX = x + Math.cos(angle) * speed;
var targetY = y + Math.sin(angle) * speed;
// Animate particle outward and fade
tween(particle, {
x: targetX,
y: targetY,
alpha: 0,
scaleX: 0.1,
scaleY: 0.1
}, {
duration: 400,
easing: tween.easeOut,
onFinish: function onFinish() {
gameContainer.removeChild(particle);
}
});
}
}
function showComboEffect(x, y) {
var comboEffect = new Text2('COMBO!', {
size: 80,
fill: 0xFFFF00
});
comboEffect.anchor.set(0.5, 0.5);
comboEffect.x = x;
comboEffect.y = y;
gameContainer.addChild(comboEffect);
tween(comboEffect, {
y: y - 100,
alpha: 0,
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
gameContainer.removeChild(comboEffect);
}
});
}
function removeNote(note) {
var index = notes.indexOf(note);
if (index > -1) {
notes.splice(index, 1);
gameContainer.removeChild(note);
}
}
function updateUI() {
scoreText.setText(gameScore);
comboText.setText('Combo: ' + gameCombo);
LK.setScore(gameScore);
// Update high score if current score is higher
var highScoreKey = 'highScore_' + selectedDifficulty;
var currentHighScore = storage[highScoreKey] || 0;
if (gameScore > currentHighScore) {
storage[highScoreKey] = gameScore;
}
highScoreText.setText('Best (' + selectedDifficulty.toUpperCase() + '): ' + (storage[highScoreKey] || 0));
// Check for game over at -50 points
if (gameScore <= -50) {
LK.showGameOver();
}
}
// Add pause menu functionality
var pauseMenu = new Container();
var pauseTitle = new Text2('Game Paused', {
size: 70,
fill: 0xFFFFFF
});
pauseTitle.anchor.set(0.5, 0.5);
pauseTitle.x = 1024;
pauseTitle.y = 1000;
pauseMenu.addChild(pauseTitle);
var resumeButton = new MenuButton('RESUME', function () {
pauseMenu.visible = false;
});
resumeButton.x = 1024;
resumeButton.y = 1200;
pauseMenu.addChild(resumeButton);
var mainMenuButton = new MenuButton('MAIN MENU', function () {
// Stop game music and start menu music
LK.stopMusic();
LK.playMusic('menumusic', {
loop: true
});
gameState = 'menu';
pauseMenu.visible = false;
gameContainer.visible = false;
scoreText.visible = false;
comboText.visible = false;
highScoreText.visible = false;
// Clear all notes
for (var i = notes.length - 1; i >= 0; i--) {
removeNote(notes[i]);
}
// Reset game variables
gameScore = 0;
gameCombo = 0;
spawnTimer = 0;
noteCounter = 0; // Reset note counter for red mode
updateUI();
// Show main menu
showMainMenu();
});
mainMenuButton.x = 1024;
mainMenuButton.y = 1350;
pauseMenu.addChild(mainMenuButton);
game.addChild(pauseMenu);
pauseMenu.visible = false;
game.update = function () {
if (gameState !== 'playing') {
return;
}
// Spawn notes
spawnTimer++;
if (spawnTimer >= spawnInterval) {
spawnNote();
spawnTimer = 0;
}
// Update and check notes
for (var i = notes.length - 1; i >= 0; i--) {
var note = notes[i];
// Check if note reached bottom (missed)
if (note.y > 2800 && !note.hasBeenHit) {
if (note.isRedMode) {
gameScore -= 20; // Red notes penalty is -20 points
} else {
gameScore -= 5; // Normal notes penalty is -5 points
}
gameCombo = 0;
// Combo system reset on miss
combo = 0;
comboDisplayText.setText("Miss!");
comboDisplayText.visible = true;
LK.setTimeout(function () {
comboDisplayText.visible = false;
}, 500);
LK.getSound('fail').play();
updateUI();
removeNote(note);
}
}
};