Code edit (1 edits merged)
Please save this source code
User prompt
cambia el titulo por el nombre del juego
User prompt
agrega a la pantalla de victoria para que se vea la puntuación
User prompt
Please fix the bug: 'TypeError: storage.get is not a function' in or related to this line: 'var prevRecord = storage.get(recordKey);' Line Number: 534
User prompt
HAz que al finalizar el juego muestre el record
User prompt
Please fix the bug: 'storage.get is not a function' in or related to this line: 'var bestScore = storage.get('bestScore') || 0;' Line Number: 362
User prompt
haz que en el menu inicial debajo del boton de tutorial salga el mejor records
User prompt
Please fix the bug: 'storage.getItem is not a function' in or related to this line: 'var bestScore = storage.getItem('bestScore') || 0;' Line Number: 363
User prompt
Please fix the bug: 'storage.get is not a function' in or related to this line: 'var bestScore = storage.get('bestScore') || 0;' Line Number: 363
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ var Note = Container.expand(function () { var self = Container.call(this); var hitZone = self.attachAsset('hit', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.6, scaleY: 1.6 }); hitZone.alpha = 0.4; var hitAccuracy = self.attachAsset('hit', { anchorX: 0.5, anchorY: 0.5, scaleX: 2.5, scaleY: 2.5 }); hitAccuracy.alpha = 0.25; var hitAccuracyTargetScale = 1.6; var hitAccuracyStartScale = 3.4; hitAccuracy.scaleX = hitAccuracyStartScale; hitAccuracy.scaleY = hitAccuracyStartScale; tween(hitAccuracy, { scaleX: hitAccuracyTargetScale, scaleY: hitAccuracyTargetScale }, { duration: 1000 / speed }); var noteAsset = self.attachAsset('note', { anchorX: 0.5, anchorY: 0.5 }); self.numberText = new Text2('1', { size: 150, fill: 0x000000 }); self.numberText.anchor.set(0.5, 0.5); self.addChild(self.numberText); self.setNumber = function (num) { self.numberText.setText(num.toString()); }; var margin = 100; self.x = margin + Math.random() * (2048 - 2 * margin); self.y = margin + Math.random() * (2732 - 2 * margin); var MARVELOUS_WINDOW = 30; var PERFECT_WINDOW = 70; var GOOD_WINDOW = 120; var BAD_WINDOW = 180; var FAIL_WINDOW = 250; self.spawnTime = Date.now(); self.hitZoneTime = self.spawnTime + 1000 / speed; function showFeedback(result, msValue) { var colorMap = { marvelous: 0x00fffc, perfect: 0x00ff00, good: 0xffff00, bad: 0xffa500, fail: 0xff0000 }; var color = colorMap[result] || 0xffffff; var msText = ""; if (typeof msValue === "number") { var msAbs = Math.abs(Math.round(msValue)); msText = " / " + (msValue > 0 ? "-" : "") + msAbs + "ms"; } var feedback = new Text2(result.toUpperCase() + msText, { size: 120, fill: "#" + ("000000" + color.toString(16)).slice(-6) }); feedback.anchor.set(0.5, 0); feedback.x = 2048 / 2; feedback.y = 120; game.addChild(feedback); tween(feedback, { alpha: 0, y: feedback.y - 60 }, { duration: 500 }); LK.setTimeout(function () { feedback.destroy(); }, 500); } self.down = function (x, y, obj) { // Only allow if this note is the first in activeNotes if (activeNotes.length === 0 || activeNotes[0] !== self) { // Not the first note, ignore press return; } var now = Date.now(); var msDiff = now - self.hitZoneTime; var absMs = Math.abs(msDiff); var result = "fail"; var points = 0; if (absMs <= MARVELOUS_WINDOW) { result = "marvelous"; marvelousCount++; points = 350; } else if (absMs <= PERFECT_WINDOW) { result = "perfect"; perfectCount++; points = 300; } else if (absMs <= GOOD_WINDOW) { result = "good"; goodCount++; points = 100; } else if (absMs <= BAD_WINDOW) { result = "bad"; badCount++; points = 50; } else if (absMs <= FAIL_WINDOW) { result = "fail"; failCount++; points = -100; } if (now < self.spawnTime + 0.2 * (self.hitZoneTime - self.spawnTime)) { result = "fail"; failCount++; points = -100; } score += points; updateAccuracyDisplay(); LK.getSound('hitSound').play(); showFeedback(result, msDiff); removeNoteFromActive(self); self.destroy(); }; self.update = function () { var now = Date.now(); if (now - self.hitZoneTime > FAIL_WINDOW) { failCount++; score -= 100; updateAccuracyDisplay(); showFeedback("fail", undefined); removeNoteFromActive(self); self.destroy(); } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000000 }); /**** * Game Code ****/ // Niveles: lista de niveles con id, nombre, canción y puntuación máxima var niveles = [{ id: 0, nombre: "Tutorial", cancion: "Tutorial", puntuacionMax: 0, audioOffset: 2405, noteOffset: 0 } // Puedes agregar más niveles aquí ]; var ms; var time = 0; var gameStartTime; var gameStart; var timerText; var speed = 1.3; var note; var life = 100; var combo = 0; var score = 0; var scoreText = null; var marvelousCount = 0; var perfectCount = 0; var goodCount = 0; var badCount = 0; var failCount = 0; var noteCounter = 0; var activeNotes = []; // === Audio and Note Offsets === var audioOffset = 0; var noteOffset = 0; var accuracyDisplayConfig = [{ label: 'Marvelous', color: 0x00FFFC, yOffset: -300, type: 'marvelous' }, { label: 'Perfect', color: 0x00FF00, yOffset: -240, type: 'perfect' }, { label: 'Good', color: 0xFFFF00, yOffset: -180, type: 'good' }, { label: 'Bad', color: 0xFFA500, yOffset: -120, type: 'bad' }, { label: 'Fail', color: 0xFF0000, yOffset: -60, type: 'fail' }]; var accuracyTexts = {}; function createAccuracyText(label, color, yOffset) { var txt = new Text2(label + ': 0', { size: 40, fill: color }); txt.anchor.set(1, 0); txt.x = -20; txt.y = yOffset; LK.gui.bottomRight.addChild(txt); return txt; } // Reusable function to create notes at specific times var scheduledNotes = []; function addNote(nivel, timeString, x, y) { // Convert time string format "M:SS.CC" or "MM:SS.CC" to milliseconds var parts = timeString.split(':'); var minutes = parseInt(parts[0]); var secondsParts = parts[1].split('.'); var seconds = parseInt(secondsParts[0]); var centiseconds = parseInt(secondsParts[1]); var totalMs = minutes * 60 * 1000 + seconds * 1000 + centiseconds * 10; scheduledNotes.push({ nivel: nivel, time: totalMs, created: false, x: x, y: y }); } function createNoteAtPosition(x, y) { var newNote = new Note(); // Square: width = 2048, so square is 2048x2048 centered vertically var squareSize = 2048; var squareTop = (2732 - squareSize) / 2; // Si x o y no están definidos, usa aleatorio en el cuadrado central newNote.x = x === undefined || x === null || x === 0 ? Math.random() * 2048 : x; newNote.y = y === undefined || y === null || y === 0 ? squareTop + Math.random() * squareSize : y; noteCounter++; newNote.orderNumber = noteCounter; newNote.setNumber(activeNotes.length + 1); activeNotes.push(newNote); // Set alpha and interactivity: only first note is fully opaque and interactive if (activeNotes.length === 1) { newNote.alpha = 1; newNote.interactive = true; } else { newNote.alpha = 0.8; newNote.interactive = false; } game.addChild(newNote); return newNote; } function updateNoteNumbers() { for (var i = 0; i < activeNotes.length; i++) { activeNotes[i].setNumber(i + 1); // Only the first note is fully opaque and interactive if (i === 0) { activeNotes[i].alpha = 1; activeNotes[i].interactive = true; } else { activeNotes[i].alpha = 0.8; activeNotes[i].interactive = false; } } } function removeNoteFromActive(note) { var index = activeNotes.indexOf(note); if (index > -1) { activeNotes.splice(index, 1); updateNoteNumbers(); } } accuracyDisplayConfig.forEach(function (cfg) { accuracyTexts[cfg.type] = createAccuracyText(cfg.label, cfg.color, cfg.yOffset); accuracyTexts[cfg.type].visible = false; // Hide by default in menu }); function updateAccuracyDisplay() { accuracyTexts.marvelous.setText('Marvelous: ' + marvelousCount); accuracyTexts.perfect.setText('Perfect: ' + perfectCount); accuracyTexts.good.setText('Good: ' + goodCount); accuracyTexts.bad.setText('Bad: ' + badCount); accuracyTexts.fail.setText('Fail: ' + failCount); } function showAccuracyTexts(visible) { for (var key in accuracyTexts) { if (accuracyTexts.hasOwnProperty(key)) { accuracyTexts[key].visible = visible; } } } /* * Initializations */ // --- MENU STATE --- var menuContainer = new Container(); var menuTitle = new Text2("RHYTHM GAME", { size: 120, fill: 0xFFFFFF }); menuTitle.anchor.set(0.5, 0); menuTitle.x = 2048 / 2; menuTitle.y = 200; menuContainer.addChild(menuTitle); var levelButtons = []; var buttonStartY = 500; var buttonSpacing = 180; // Plugin for persistent storage for (var i = 0; i < niveles.length; i++) { (function (idx) { var nivel = niveles[idx]; var btn = LK.getAsset('button', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: buttonStartY + idx * buttonSpacing + 300, scaleX: 3.5, scaleY: 3.5 }); var btnText = new Text2(nivel.nombre, { size: 60, fill: 0x000000 }); btnText.anchor.set(0.5, 0.5); btnText.x = btn.x; btnText.y = btn.y; btn.interactive = true; btn.down = function (x, y, obj) { startGameWithLevel(idx); }; menuContainer.addChild(btn); menuContainer.addChild(btnText); levelButtons.push(btn); })(i); } // Show best record below the tutorial button var bestScore = storage.getItem('bestScore') || 0; var bestScoreText = new Text2("Mejor récord: " + bestScore, { size: 54, fill: 0xFFD700 }); bestScoreText.anchor.set(0.5, 0); bestScoreText.x = 2048 / 2; bestScoreText.y = buttonStartY + 300 + 120; // 120px below the tutorial button menuContainer.addChild(bestScoreText); game.addChild(menuContainer); // --- GAME STATE --- var gameStarted = false; var musicStartTime = null; var musicDuration = null; function startGameWithLevel(levelIdx) { // Hide menu menuContainer.visible = false; gameStarted = true; // Reset state gameStartTime = Date.now(); marvelousCount = 0; perfectCount = 0; goodCount = 0; badCount = 0; failCount = 0; noteCounter = 0; activeNotes = []; score = 0; combo = 0; // On game end, update best record if needed var prevShowYouWin = LK.showYouWin; LK.showYouWin = function () { // Only update if score is higher than bestScore var bestScore = storage.getItem('bestScore') || 0; if (score > bestScore) { storage.setItem('bestScore', score); if (typeof bestScoreText !== "undefined" && bestScoreText.setText) { bestScoreText.setText("Mejor récord: " + score); } } prevShowYouWin(); }; for (var k = 0; k < scheduledNotes.length; k++) { scheduledNotes[k].created = false; } updateAccuracyDisplay(); showAccuracyTexts(true); // Show timer timerText.visible = true; scoreText.visible = true; scoreText.setText('Score: 0'); // Start the music for the selected level var nivel = niveles[levelIdx]; // Set per-level offsets if (nivel) { audioOffset = typeof nivel.audioOffset === "number" ? nivel.audioOffset : 0; noteOffset = typeof nivel.noteOffset === "number" ? nivel.noteOffset : 0; } if (nivel && nivel.cancion) { if (audioOffset > 0) { // Delay music start by audioOffset milliseconds LK.setTimeout(function () { LK.playMusic(nivel.cancion, { loop: false }); musicStartTime = Date.now(); }, audioOffset); } else { // Start music immediately (negative offset handled as 0) LK.playMusic(nivel.cancion, { loop: false }); musicStartTime = Date.now(); } // Set music duration (Tutorial is approximately 20 seconds) if (nivel.cancion === "Tutorial") { musicDuration = 20000; // 20 seconds in milliseconds } } // Start the timer (already handled by game.update using gameStartTime) } // Timer text (hidden until game starts) timerText = new Text2('0:00.00', { size: 60, fill: 0xFFFFFF }); timerText.anchor.set(0.5, 0); timerText.x = 0; timerText.y = 50; LK.gui.top.addChild(timerText); timerText.visible = false; // Score text (top right) scoreText = new Text2('Score: 0', { size: 60, fill: 0xFFFFFF }); scoreText.anchor.set(1, 0); scoreText.x = -20; scoreText.y = 50; LK.gui.topRight.addChild(scoreText); scoreText.visible = false; // Add notes for Tutorial (nivel 0) as example // 5 addNote(0, "0:01.65", 0, 0); addNote(0, "0:02.00", 200, 600); addNote(0, "0:02.30", 500, 700); addNote(0, "0:02.60", 800, 800); addNote(0, "0:02.90", 1100, 900); addNote(0, "0:03.20", 1400, 1000); addNote(0, "0:03.65", 0, 0); addNote(0, "0:04.00", 200, 600); addNote(0, "0:04.30", 500, 700); addNote(0, "0:04.60", 800, 800); addNote(0, "0:04.90", 1100, 900); addNote(0, "0:05.20", 1400, 1000); addNote(0, "0:05.65", 0, 0); addNote(0, "0:06.00", 200, 600); addNote(0, "0:06.30", 500, 700); addNote(0, "0:06.60", 800, 800); addNote(0, "0:06.90", 1100, 900); addNote(0, "0:07.20", 1400, 1000); addNote(0, "0:07.65", 0, 0); addNote(0, "0:08.00", 200, 600); addNote(0, "0:08.30", 500, 700); addNote(0, "0:08.60", 800, 800); addNote(0, "0:08.90", 1100, 900); addNote(0, "0:09.20", 1400, 1000); addNote(0, "0:09.65", 0, 0); addNote(0, "0:10.00", 200, 600); addNote(0, "0:10.30", 500, 700); addNote(0, "0:10.60", 800, 800); addNote(0, "0:10.90", 1100, 900); addNote(0, "0:11.20", 1400, 1000); addNote(0, "0:11.65", 0, 0); addNote(0, "0:12.00", 200, 600); addNote(0, "0:12.30", 500, 700); addNote(0, "0:12.60", 800, 800); addNote(0, "0:12.90", 1100, 900); addNote(0, "0:13.20", 1400, 1000); addNote(0, "0:13.65", 0, 0); addNote(0, "0:14.00", 200, 600); addNote(0, "0:14.30", 500, 700); addNote(0, "0:14.60", 800, 800); addNote(0, "0:14.90", 1100, 900); addNote(0, "0:15.20", 1400, 1000); addNote(0, "0:15.65", 0, 0); addNote(0, "0:16.00", 200, 600); addNote(0, "0:16.30", 500, 700); addNote(0, "0:16.60", 800, 800); addNote(0, "0:16.90", 1100, 900); addNote(0, "0:17.20", 1400, 1000); addNote(0, "0:17.65", 0, 0); addNote(0, "0:18.00", 200, 600); addNote(0, "0:18.30", 500, 700); addNote(0, "0:18.60", 800, 800); addNote(0, "0:18.90", 1100, 900); addNote(0, "0:19.20", 1400, 1000); addNote(0, "0:20.30", 500, 700); addNote(0, "0:20.60", 800, 800); addNote(0, "0:20.90", 1100, 900); addNote(0, "0:21.00", 1400, 1000); addNote(0, "0:21.20", 1350, 600); game.update = function () { if (!gameStarted) { // Menu is active, do not run game logic return; } var currentTime = Date.now(); var elapsedMs = currentTime - gameStartTime; // Apply noteOffset to note timing var adjustedElapsedMs = elapsedMs + noteOffset; var totalSeconds = Math.floor(elapsedMs / 1000); var minutes = Math.floor(totalSeconds / 60); var seconds = totalSeconds % 60; var centiseconds = Math.floor(elapsedMs % 1000 / 10); var timeString = minutes + ':' + (seconds < 10 ? '0' : '') + seconds + '.' + (centiseconds < 10 ? '0' : '') + centiseconds; timerText.setText(timeString); // Check scheduled notes for (var i = 0; i < scheduledNotes.length; i++) { var scheduled = scheduledNotes[i]; if (!scheduled.created && adjustedElapsedMs >= scheduled.time) { createNoteAtPosition(scheduled.x, scheduled.y); scheduled.created = true; } } // Update score text if (scoreText) { scoreText.setText('Score: ' + score); } // Check if music has ended if (musicStartTime && musicDuration && currentTime - musicStartTime >= musicDuration) { // Music has ended, show victory LK.showYouWin(); musicStartTime = null; // Reset to prevent multiple calls } };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Note = Container.expand(function () {
var self = Container.call(this);
var hitZone = self.attachAsset('hit', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.6,
scaleY: 1.6
});
hitZone.alpha = 0.4;
var hitAccuracy = self.attachAsset('hit', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.5,
scaleY: 2.5
});
hitAccuracy.alpha = 0.25;
var hitAccuracyTargetScale = 1.6;
var hitAccuracyStartScale = 3.4;
hitAccuracy.scaleX = hitAccuracyStartScale;
hitAccuracy.scaleY = hitAccuracyStartScale;
tween(hitAccuracy, {
scaleX: hitAccuracyTargetScale,
scaleY: hitAccuracyTargetScale
}, {
duration: 1000 / speed
});
var noteAsset = self.attachAsset('note', {
anchorX: 0.5,
anchorY: 0.5
});
self.numberText = new Text2('1', {
size: 150,
fill: 0x000000
});
self.numberText.anchor.set(0.5, 0.5);
self.addChild(self.numberText);
self.setNumber = function (num) {
self.numberText.setText(num.toString());
};
var margin = 100;
self.x = margin + Math.random() * (2048 - 2 * margin);
self.y = margin + Math.random() * (2732 - 2 * margin);
var MARVELOUS_WINDOW = 30;
var PERFECT_WINDOW = 70;
var GOOD_WINDOW = 120;
var BAD_WINDOW = 180;
var FAIL_WINDOW = 250;
self.spawnTime = Date.now();
self.hitZoneTime = self.spawnTime + 1000 / speed;
function showFeedback(result, msValue) {
var colorMap = {
marvelous: 0x00fffc,
perfect: 0x00ff00,
good: 0xffff00,
bad: 0xffa500,
fail: 0xff0000
};
var color = colorMap[result] || 0xffffff;
var msText = "";
if (typeof msValue === "number") {
var msAbs = Math.abs(Math.round(msValue));
msText = " / " + (msValue > 0 ? "-" : "") + msAbs + "ms";
}
var feedback = new Text2(result.toUpperCase() + msText, {
size: 120,
fill: "#" + ("000000" + color.toString(16)).slice(-6)
});
feedback.anchor.set(0.5, 0);
feedback.x = 2048 / 2;
feedback.y = 120;
game.addChild(feedback);
tween(feedback, {
alpha: 0,
y: feedback.y - 60
}, {
duration: 500
});
LK.setTimeout(function () {
feedback.destroy();
}, 500);
}
self.down = function (x, y, obj) {
// Only allow if this note is the first in activeNotes
if (activeNotes.length === 0 || activeNotes[0] !== self) {
// Not the first note, ignore press
return;
}
var now = Date.now();
var msDiff = now - self.hitZoneTime;
var absMs = Math.abs(msDiff);
var result = "fail";
var points = 0;
if (absMs <= MARVELOUS_WINDOW) {
result = "marvelous";
marvelousCount++;
points = 350;
} else if (absMs <= PERFECT_WINDOW) {
result = "perfect";
perfectCount++;
points = 300;
} else if (absMs <= GOOD_WINDOW) {
result = "good";
goodCount++;
points = 100;
} else if (absMs <= BAD_WINDOW) {
result = "bad";
badCount++;
points = 50;
} else if (absMs <= FAIL_WINDOW) {
result = "fail";
failCount++;
points = -100;
}
if (now < self.spawnTime + 0.2 * (self.hitZoneTime - self.spawnTime)) {
result = "fail";
failCount++;
points = -100;
}
score += points;
updateAccuracyDisplay();
LK.getSound('hitSound').play();
showFeedback(result, msDiff);
removeNoteFromActive(self);
self.destroy();
};
self.update = function () {
var now = Date.now();
if (now - self.hitZoneTime > FAIL_WINDOW) {
failCount++;
score -= 100;
updateAccuracyDisplay();
showFeedback("fail", undefined);
removeNoteFromActive(self);
self.destroy();
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Niveles: lista de niveles con id, nombre, canción y puntuación máxima
var niveles = [{
id: 0,
nombre: "Tutorial",
cancion: "Tutorial",
puntuacionMax: 0,
audioOffset: 2405,
noteOffset: 0
}
// Puedes agregar más niveles aquí
];
var ms;
var time = 0;
var gameStartTime;
var gameStart;
var timerText;
var speed = 1.3;
var note;
var life = 100;
var combo = 0;
var score = 0;
var scoreText = null;
var marvelousCount = 0;
var perfectCount = 0;
var goodCount = 0;
var badCount = 0;
var failCount = 0;
var noteCounter = 0;
var activeNotes = [];
// === Audio and Note Offsets ===
var audioOffset = 0;
var noteOffset = 0;
var accuracyDisplayConfig = [{
label: 'Marvelous',
color: 0x00FFFC,
yOffset: -300,
type: 'marvelous'
}, {
label: 'Perfect',
color: 0x00FF00,
yOffset: -240,
type: 'perfect'
}, {
label: 'Good',
color: 0xFFFF00,
yOffset: -180,
type: 'good'
}, {
label: 'Bad',
color: 0xFFA500,
yOffset: -120,
type: 'bad'
}, {
label: 'Fail',
color: 0xFF0000,
yOffset: -60,
type: 'fail'
}];
var accuracyTexts = {};
function createAccuracyText(label, color, yOffset) {
var txt = new Text2(label + ': 0', {
size: 40,
fill: color
});
txt.anchor.set(1, 0);
txt.x = -20;
txt.y = yOffset;
LK.gui.bottomRight.addChild(txt);
return txt;
}
// Reusable function to create notes at specific times
var scheduledNotes = [];
function addNote(nivel, timeString, x, y) {
// Convert time string format "M:SS.CC" or "MM:SS.CC" to milliseconds
var parts = timeString.split(':');
var minutes = parseInt(parts[0]);
var secondsParts = parts[1].split('.');
var seconds = parseInt(secondsParts[0]);
var centiseconds = parseInt(secondsParts[1]);
var totalMs = minutes * 60 * 1000 + seconds * 1000 + centiseconds * 10;
scheduledNotes.push({
nivel: nivel,
time: totalMs,
created: false,
x: x,
y: y
});
}
function createNoteAtPosition(x, y) {
var newNote = new Note();
// Square: width = 2048, so square is 2048x2048 centered vertically
var squareSize = 2048;
var squareTop = (2732 - squareSize) / 2;
// Si x o y no están definidos, usa aleatorio en el cuadrado central
newNote.x = x === undefined || x === null || x === 0 ? Math.random() * 2048 : x;
newNote.y = y === undefined || y === null || y === 0 ? squareTop + Math.random() * squareSize : y;
noteCounter++;
newNote.orderNumber = noteCounter;
newNote.setNumber(activeNotes.length + 1);
activeNotes.push(newNote);
// Set alpha and interactivity: only first note is fully opaque and interactive
if (activeNotes.length === 1) {
newNote.alpha = 1;
newNote.interactive = true;
} else {
newNote.alpha = 0.8;
newNote.interactive = false;
}
game.addChild(newNote);
return newNote;
}
function updateNoteNumbers() {
for (var i = 0; i < activeNotes.length; i++) {
activeNotes[i].setNumber(i + 1);
// Only the first note is fully opaque and interactive
if (i === 0) {
activeNotes[i].alpha = 1;
activeNotes[i].interactive = true;
} else {
activeNotes[i].alpha = 0.8;
activeNotes[i].interactive = false;
}
}
}
function removeNoteFromActive(note) {
var index = activeNotes.indexOf(note);
if (index > -1) {
activeNotes.splice(index, 1);
updateNoteNumbers();
}
}
accuracyDisplayConfig.forEach(function (cfg) {
accuracyTexts[cfg.type] = createAccuracyText(cfg.label, cfg.color, cfg.yOffset);
accuracyTexts[cfg.type].visible = false; // Hide by default in menu
});
function updateAccuracyDisplay() {
accuracyTexts.marvelous.setText('Marvelous: ' + marvelousCount);
accuracyTexts.perfect.setText('Perfect: ' + perfectCount);
accuracyTexts.good.setText('Good: ' + goodCount);
accuracyTexts.bad.setText('Bad: ' + badCount);
accuracyTexts.fail.setText('Fail: ' + failCount);
}
function showAccuracyTexts(visible) {
for (var key in accuracyTexts) {
if (accuracyTexts.hasOwnProperty(key)) {
accuracyTexts[key].visible = visible;
}
}
}
/*
* Initializations
*/
// --- MENU STATE ---
var menuContainer = new Container();
var menuTitle = new Text2("RHYTHM GAME", {
size: 120,
fill: 0xFFFFFF
});
menuTitle.anchor.set(0.5, 0);
menuTitle.x = 2048 / 2;
menuTitle.y = 200;
menuContainer.addChild(menuTitle);
var levelButtons = [];
var buttonStartY = 500;
var buttonSpacing = 180;
// Plugin for persistent storage
for (var i = 0; i < niveles.length; i++) {
(function (idx) {
var nivel = niveles[idx];
var btn = LK.getAsset('button', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: buttonStartY + idx * buttonSpacing + 300,
scaleX: 3.5,
scaleY: 3.5
});
var btnText = new Text2(nivel.nombre, {
size: 60,
fill: 0x000000
});
btnText.anchor.set(0.5, 0.5);
btnText.x = btn.x;
btnText.y = btn.y;
btn.interactive = true;
btn.down = function (x, y, obj) {
startGameWithLevel(idx);
};
menuContainer.addChild(btn);
menuContainer.addChild(btnText);
levelButtons.push(btn);
})(i);
}
// Show best record below the tutorial button
var bestScore = storage.getItem('bestScore') || 0;
var bestScoreText = new Text2("Mejor récord: " + bestScore, {
size: 54,
fill: 0xFFD700
});
bestScoreText.anchor.set(0.5, 0);
bestScoreText.x = 2048 / 2;
bestScoreText.y = buttonStartY + 300 + 120; // 120px below the tutorial button
menuContainer.addChild(bestScoreText);
game.addChild(menuContainer);
// --- GAME STATE ---
var gameStarted = false;
var musicStartTime = null;
var musicDuration = null;
function startGameWithLevel(levelIdx) {
// Hide menu
menuContainer.visible = false;
gameStarted = true;
// Reset state
gameStartTime = Date.now();
marvelousCount = 0;
perfectCount = 0;
goodCount = 0;
badCount = 0;
failCount = 0;
noteCounter = 0;
activeNotes = [];
score = 0;
combo = 0;
// On game end, update best record if needed
var prevShowYouWin = LK.showYouWin;
LK.showYouWin = function () {
// Only update if score is higher than bestScore
var bestScore = storage.getItem('bestScore') || 0;
if (score > bestScore) {
storage.setItem('bestScore', score);
if (typeof bestScoreText !== "undefined" && bestScoreText.setText) {
bestScoreText.setText("Mejor récord: " + score);
}
}
prevShowYouWin();
};
for (var k = 0; k < scheduledNotes.length; k++) {
scheduledNotes[k].created = false;
}
updateAccuracyDisplay();
showAccuracyTexts(true);
// Show timer
timerText.visible = true;
scoreText.visible = true;
scoreText.setText('Score: 0');
// Start the music for the selected level
var nivel = niveles[levelIdx];
// Set per-level offsets
if (nivel) {
audioOffset = typeof nivel.audioOffset === "number" ? nivel.audioOffset : 0;
noteOffset = typeof nivel.noteOffset === "number" ? nivel.noteOffset : 0;
}
if (nivel && nivel.cancion) {
if (audioOffset > 0) {
// Delay music start by audioOffset milliseconds
LK.setTimeout(function () {
LK.playMusic(nivel.cancion, {
loop: false
});
musicStartTime = Date.now();
}, audioOffset);
} else {
// Start music immediately (negative offset handled as 0)
LK.playMusic(nivel.cancion, {
loop: false
});
musicStartTime = Date.now();
}
// Set music duration (Tutorial is approximately 20 seconds)
if (nivel.cancion === "Tutorial") {
musicDuration = 20000; // 20 seconds in milliseconds
}
}
// Start the timer (already handled by game.update using gameStartTime)
}
// Timer text (hidden until game starts)
timerText = new Text2('0:00.00', {
size: 60,
fill: 0xFFFFFF
});
timerText.anchor.set(0.5, 0);
timerText.x = 0;
timerText.y = 50;
LK.gui.top.addChild(timerText);
timerText.visible = false;
// Score text (top right)
scoreText = new Text2('Score: 0', {
size: 60,
fill: 0xFFFFFF
});
scoreText.anchor.set(1, 0);
scoreText.x = -20;
scoreText.y = 50;
LK.gui.topRight.addChild(scoreText);
scoreText.visible = false;
// Add notes for Tutorial (nivel 0) as example
// 5
addNote(0, "0:01.65", 0, 0);
addNote(0, "0:02.00", 200, 600);
addNote(0, "0:02.30", 500, 700);
addNote(0, "0:02.60", 800, 800);
addNote(0, "0:02.90", 1100, 900);
addNote(0, "0:03.20", 1400, 1000);
addNote(0, "0:03.65", 0, 0);
addNote(0, "0:04.00", 200, 600);
addNote(0, "0:04.30", 500, 700);
addNote(0, "0:04.60", 800, 800);
addNote(0, "0:04.90", 1100, 900);
addNote(0, "0:05.20", 1400, 1000);
addNote(0, "0:05.65", 0, 0);
addNote(0, "0:06.00", 200, 600);
addNote(0, "0:06.30", 500, 700);
addNote(0, "0:06.60", 800, 800);
addNote(0, "0:06.90", 1100, 900);
addNote(0, "0:07.20", 1400, 1000);
addNote(0, "0:07.65", 0, 0);
addNote(0, "0:08.00", 200, 600);
addNote(0, "0:08.30", 500, 700);
addNote(0, "0:08.60", 800, 800);
addNote(0, "0:08.90", 1100, 900);
addNote(0, "0:09.20", 1400, 1000);
addNote(0, "0:09.65", 0, 0);
addNote(0, "0:10.00", 200, 600);
addNote(0, "0:10.30", 500, 700);
addNote(0, "0:10.60", 800, 800);
addNote(0, "0:10.90", 1100, 900);
addNote(0, "0:11.20", 1400, 1000);
addNote(0, "0:11.65", 0, 0);
addNote(0, "0:12.00", 200, 600);
addNote(0, "0:12.30", 500, 700);
addNote(0, "0:12.60", 800, 800);
addNote(0, "0:12.90", 1100, 900);
addNote(0, "0:13.20", 1400, 1000);
addNote(0, "0:13.65", 0, 0);
addNote(0, "0:14.00", 200, 600);
addNote(0, "0:14.30", 500, 700);
addNote(0, "0:14.60", 800, 800);
addNote(0, "0:14.90", 1100, 900);
addNote(0, "0:15.20", 1400, 1000);
addNote(0, "0:15.65", 0, 0);
addNote(0, "0:16.00", 200, 600);
addNote(0, "0:16.30", 500, 700);
addNote(0, "0:16.60", 800, 800);
addNote(0, "0:16.90", 1100, 900);
addNote(0, "0:17.20", 1400, 1000);
addNote(0, "0:17.65", 0, 0);
addNote(0, "0:18.00", 200, 600);
addNote(0, "0:18.30", 500, 700);
addNote(0, "0:18.60", 800, 800);
addNote(0, "0:18.90", 1100, 900);
addNote(0, "0:19.20", 1400, 1000);
addNote(0, "0:20.30", 500, 700);
addNote(0, "0:20.60", 800, 800);
addNote(0, "0:20.90", 1100, 900);
addNote(0, "0:21.00", 1400, 1000);
addNote(0, "0:21.20", 1350, 600);
game.update = function () {
if (!gameStarted) {
// Menu is active, do not run game logic
return;
}
var currentTime = Date.now();
var elapsedMs = currentTime - gameStartTime;
// Apply noteOffset to note timing
var adjustedElapsedMs = elapsedMs + noteOffset;
var totalSeconds = Math.floor(elapsedMs / 1000);
var minutes = Math.floor(totalSeconds / 60);
var seconds = totalSeconds % 60;
var centiseconds = Math.floor(elapsedMs % 1000 / 10);
var timeString = minutes + ':' + (seconds < 10 ? '0' : '') + seconds + '.' + (centiseconds < 10 ? '0' : '') + centiseconds;
timerText.setText(timeString);
// Check scheduled notes
for (var i = 0; i < scheduledNotes.length; i++) {
var scheduled = scheduledNotes[i];
if (!scheduled.created && adjustedElapsedMs >= scheduled.time) {
createNoteAtPosition(scheduled.x, scheduled.y);
scheduled.created = true;
}
}
// Update score text
if (scoreText) {
scoreText.setText('Score: ' + score);
}
// Check if music has ended
if (musicStartTime && musicDuration && currentTime - musicStartTime >= musicDuration) {
// Music has ended, show victory
LK.showYouWin();
musicStartTime = null; // Reset to prevent multiple calls
}
};