Code edit (2 edits merged)
Please save this source code
User prompt
agrega a note un aro externo llamado hit zone separado a unos pixeles de el con una transparencia de 0.6
Code edit (1 edits merged)
Please save this source code
User prompt
agrega a note un aro externo llamado hit zone separado a unos pixeles de el con una transparencia de 0.6
Code edit (3 edits merged)
Please save this source code
User prompt
Crea un objeto llamado note que aparece en alguna posición aleatoria (x,y) de la pantalla
User prompt
Crea un objeto llamado note que aparece en alguna posición aleatoria (x,y) de la pantalla (No agrueges ningun script aun)
Code edit (1 edits merged)
Please save this source code
User prompt
Beat Tapper
Initial prompt
Music Game
/**** * Plugins ****/ var tween = LK.import("@upit/tween.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("Parizuzzo", { 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; 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); } 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; 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");
/****
* 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("Parizuzzo", {
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;
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);
}
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;
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
}
};