lyrics.js 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. class LyricsSystem {
  2. constructor() {
  3. this.lyrics = [];
  4. this.currentLyricIndex = -1;
  5. this.showLyrics = true; // Mostrar letras por defecto
  6. this.font = null;
  7. this.songDuration = 189; // 3:09 en segundos
  8. this.isGameOver = false;
  9. this.gameOverScreen = false;
  10. this.fadeOpacity = 1.0;
  11. this.fadeDirection = 1; // 1 para aparecer, -1 para desaparecer
  12. // Offset de tiempo para ajustar sincronización (en segundos)
  13. // Valor positivo = letras aparecen ANTES
  14. // Valor negativo = letras aparecen DESPUÉS
  15. this.timeOffset = 0.2;
  16. // Configuración de texto
  17. this.maxLineWidth = 280; // Ancho máximo de línea en píxeles (original)
  18. this.lineHeight = 18; // Altura entre líneas (original)
  19. // Cargar la fuente personalizada
  20. this.loadCustomFont();
  21. // Parsear las letras desde el archivo
  22. this.parseLyrics();
  23. }
  24. loadCustomFont() {
  25. this.font = new FontFace('CustomFont', 'url(assets/font.ttf)');
  26. this.font.load().then(() => {
  27. document.fonts.add(this.font);
  28. }).catch(err => {
  29. console.warn('No se pudo cargar la fuente personalizada:', err);
  30. });
  31. }
  32. parseLyrics() {
  33. // Letras en romaji usando los tiempos exactos del archivo
  34. const lyricsData = [
  35. { time: 2.8, text: "mogumogu" },
  36. { time: 6.4, text: "shinra banshou no akusenkutou mo mayoneizpu kaketara daitai oishiku naru?" },
  37. { time: 13.3, text: "kimi ga naite mo onaka wa suku yo shouka dekinakatta \"gomen gomen\"" },
  38. { time: 19.8, text: "sui mo amai mo katte ni tabete gomeiwaku okakeshite imasu" },
  39. { time: 26.7, text: "pakuchii na okite hen na aji no juusu kokoro wo mu ni shite nomikomimasu" },
  40. { time: 33.3, text: "aa konton jouhou kata no resutoran de usoppachi no menyuu ni ocha koboshite" },
  41. { time: 39.8, text: "kimi to mogumogu (mogumogu) mogumogu (mogumogu)" },
  42. { time: 43, text: "juugeki-sen no mannaka de mogumogu (ugya~)" },
  43. { time: 46, text: "chimi mouryou harapeko no mure manpuku ni naru mirai wo negatteiru yo" },
  44. { time: 53, text: "mogumogu (mogumogu) mogumogu (mogumogu) sekai ga owaru mae ni" },
  45. { time: 59, text: "mogumogu yamii maji kami yamuyamu ari no manma" },
  46. { time: 66, text: "mogumogu yamii Magic Coming konoyo wo ajiwaun da umauma" },
  47. { time: 77, text: "" },
  48. { time: 80, text: "riron busou no mirufiiyu sando (sando)" },
  49. { time: 83, text: "tokumei kibou no ourora sousu (sousu)" },
  50. { time: 87, text: "oozappa na ajitsuke oome ni mite yo oishiku nattara \"ok ok.\"" },
  51. { time: 93, text: "ii mon warui mon ippai tabete yogoreta kuchimoto nuguimasu" },
  52. { time: 100, text: "onigiri piza keiki gouka na furu kousu wana wo utagai ki wo tsukemasu" },
  53. { time: 106, text: "aa mousou ryuugen higo no baikingu de dokuiri to zeppin ryouri yoriwakete" },
  54. { time: 113, text: "kimi to mogumogu (mogumogu) mogumogu (mogumogu)" },
  55. { time: 117, text: "desugeimu no kyoushitsu de mogumogu (ugya~)" },
  56. { time: 120, text: "chimi mouryou harapeko no mure naka no ii kimi to ikinokoretara ii na" },
  57. { time: 127, text: "mogumogu (mogumogu) mogumogu (mogumogu) suu nen-go mo kono basho de" },
  58. { time: 133, text: "mogumogu yamii maji kami kon'ya no bangohan wa" },
  59. { time: 139, text: "mogumogu yamii Magic Coming tsugi wa omae no ban da" },
  60. { time: 146, text: "mogumogu yamii maji kami yamuyamu ari no manma" },
  61. { time: 152, text: "mogumogu yamii Magic Coming konoyo wo ajiwaun da umauma" },
  62. { time: 160, text: "" },
  63. { time: 162, text: "mogumogu mogumogu mogumogu mogumogu mogumogu mogumogu mogumogu yamii" },
  64. { time: 168, text: "mogumogu mogumogu mogumogu mogumogu mogumogu mogumogu mogumogu yamii" },
  65. { time: 175, text: "mogumogu mogumogu mogumogu mogumogu mogumogu mogumogu mogumogu yamii" },
  66. { time: 181, text: "mogumogu mogumogu mogumogu mogumogu"},
  67. { time: 187, text: "yamii yamii" }
  68. ];
  69. this.lyrics = lyricsData;
  70. }
  71. update(currentTime) {
  72. // Aplicar offset de tiempo
  73. const adjustedTime = currentTime + this.timeOffset;
  74. // Verificar si la canción ha terminado
  75. if (adjustedTime >= this.songDuration && !this.isGameOver) {
  76. this.isGameOver = true;
  77. this.gameOverScreen = true;
  78. this.showLyrics = false; // Ocultar letras cuando termine la canción
  79. return;
  80. }
  81. // Actualizar letra actual basada en el tiempo ajustado
  82. this.updateCurrentLyric(adjustedTime);
  83. }
  84. updateCurrentLyric(currentTime) {
  85. let newIndex = -1;
  86. for (let i = 0; i < this.lyrics.length; i++) {
  87. if (currentTime >= this.lyrics[i].time) {
  88. newIndex = i;
  89. } else {
  90. break;
  91. }
  92. }
  93. if (newIndex !== this.currentLyricIndex) {
  94. this.currentLyricIndex = newIndex;
  95. }
  96. }
  97. draw(ctx, canvas) {
  98. if (!this.showLyrics && this.fadeOpacity <= 0) return;
  99. // Actualizar opacidad para transición suave
  100. if (this.showLyrics && this.fadeOpacity < 1.0) {
  101. this.fadeOpacity += 0.05;
  102. } else if (!this.showLyrics && this.fadeOpacity > 0) {
  103. this.fadeOpacity -= 0.05;
  104. }
  105. // Dibujar letra actual
  106. if (this.currentLyricIndex >= 0 && this.currentLyricIndex < this.lyrics.length) {
  107. const currentLyric = this.lyrics[this.currentLyricIndex];
  108. // Configurar fuente (original)
  109. ctx.font = '14px CustomFont, Arial, sans-serif';
  110. ctx.fillStyle = `rgba(255, 255, 255, ${this.fadeOpacity})`;
  111. ctx.textAlign = 'center';
  112. // Dividir texto en líneas
  113. const lines = this.wrapText(currentLyric.text, ctx, this.maxLineWidth);
  114. // Calcular posición Y inicial (original for 320x240)
  115. const totalHeight = lines.length * this.lineHeight;
  116. const startY = canvas.height - 180 - (totalHeight - this.lineHeight);
  117. // Dibujar cada línea
  118. lines.forEach((line, index) => {
  119. const x = canvas.width / 2;
  120. const y = startY + (index * this.lineHeight);
  121. ctx.strokeText(line, x, y);
  122. ctx.fillText(line, x, y);
  123. });
  124. // Resetear sombra
  125. ctx.shadowColor = 'transparent';
  126. ctx.shadowBlur = 0;
  127. ctx.shadowOffsetX = 0;
  128. ctx.shadowOffsetY = 0;
  129. }
  130. }
  131. toggleLyrics() {
  132. this.showLyrics = !this.showLyrics;
  133. this.fadeDirection = this.showLyrics ? 1 : -1;
  134. }
  135. // Método para dividir texto en líneas
  136. wrapText(text, ctx, maxWidth) {
  137. const words = text.split(' ');
  138. const lines = [];
  139. let currentLine = words[0];
  140. for (let i = 1; i < words.length; i++) {
  141. const word = words[i];
  142. const width = ctx.measureText(currentLine + ' ' + word).width;
  143. if (width < maxWidth) {
  144. currentLine += ' ' + word;
  145. } else {
  146. lines.push(currentLine);
  147. currentLine = word;
  148. }
  149. }
  150. lines.push(currentLine);
  151. return lines;
  152. }
  153. // Método para desarrollo: simular fin de canción
  154. simulateGameOver() {
  155. this.isGameOver = true;
  156. this.gameOverScreen = true;
  157. }
  158. // Método para reiniciar el juego
  159. reset() {
  160. this.currentLyricIndex = -1;
  161. this.isGameOver = false;
  162. this.gameOverScreen = false;
  163. this.showLyrics = true;
  164. this.fadeOpacity = 1.0;
  165. this.timeOffset = 0; // Resetear offset
  166. }
  167. }