User prompt
En la línea de diálogo donde Satoshi dice: "¡Lo prometo! Seré el alumno más aplicado que hayas visto.", actualmente aparece el sprite "characterSprite", lo cual es incorrecto. Reemplázalo por "characterSprite3", que es el único que debe mostrarse en esa parte. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Corrige el error en la escena donde Satoshi dice: "¡Lo prometo! Seré el alumno más aplicado que hayas visto." En ese momento solo debe mostrarse el sprite "characterSprite3", no "characterSprite".
User prompt
Corrige el error en la escena donde Satoshi dice: "¡Lo prometo! Seré el alumno más aplicado que hayas visto." En ese momento solo debe mostrarse el sprite "characterSprite3", no "characterSprite2".
User prompt
"characterSprite" aparece al inicio del juego por error. Elimina su aparición inicial y asegúrate de que no se muestre hasta que sea invocado correctamente más adelante.
User prompt
Animación general de aparición/cambio entre sprites: Cada vez que se muestre o se reemplace un sprite del retrato (por ejemplo, cuando cambia de una expresión a otra): Animación base de transición (entre sprites): Usa un fade-out + pequeño desplazamiento hacia abajo (4-6px) y luego fade-in + regreso a posición original. Esto da la sensación de que el personaje "baja la cabeza" brevemente entre emociones. Duración: entre 250ms y 400ms. Interpolación: ease-out para salida, ease-in para entrada. --- ✳️ Animaciones específicas según sprite: 🔻 "characterSprite" (Vergüenza / timidez): Tipo de animación: Temblor tímido + parpadeo de rubor. Efecto visual: Tras aparecer, aplica un ligero "temblor vertical" (como un micro-shake hacia abajo y arriba, muy leve, 2px máx., 2-3 veces). Luego, haz que el retrato emita un destello rojo muy tenue o un parpadeo de rubor en las mejillas (o en una capa glow externa si no hay mejillas). Duración total: 700ms - 1s. Objetivo: Reflejar vergüenza, incomodidad y ternura. --- 🔷 "characterSprite2" (Pensando / duda): Tipo de animación: Bostezo leve + parpadeo lento. Efecto visual: Haz que el retrato tenga una animación tipo zoom-out muy leve (~98%) seguido de zoom-in (~102%), como si el personaje se alejara mentalmente para pensar. Añade un parpadeo ocular lento si aplica (si no hay ojos, simplemente baja la opacidad brevemente y vuelve a subir, imitando un "parpadeo reflexivo"). Duración total: 1.2s. Objetivo: Reflejar introspección o una pausa reflexiva. --- 🟢 "characterSprite3" (Entusiasmo / emoción): Tipo de animación: Rebote y brillo. Efecto visual: Al aparecer, el retrato hace un "bounce" suave hacia arriba, como un pequeño salto con estiramiento vertical (escala Y 105% → 95% → 100%). Después del bounce, añade un brillo blanco rápido y tenue que recorra el retrato diagonalmente (de arriba a la izquierda a abajo a la derecha). Duración total: 800ms. Objetivo: Comunicar energía, alegría o determinación. --- 🧩 Complemento opcional: Si el personaje cambia de sprite y no se reemplaza inmediatamente por otro (por ejemplo, si se oculta el retrato al acabar la escena o cambiar de tema), puedes añadir: Animación de desaparición (sin reemplazo): Fade-out + desenfoque (blur suave). O un shrink-out (se encoge hasta desaparecer al centro). Duración: 300ms. Con esto marcas que "se retiró" su expresión del diálogo. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
🎨 Propuesta de animaciones para el icono del personaje principal sobre el diálogo Este icono representa al protagonista, ubicado encima del cuadro de diálogo (dialogBox). No es un personaje jugable, sino un retrato que expresa sus emociones cambiando de sprite. Actualmente se utilizan tres sprites: "characterSprite" → Expresión de vergüenza "characterSprite2" → Expresión de reflexión o duda "characterSprite3" → Expresión de entusiasmo A continuación se detallan las animaciones recomendadas para cada sprite según su carga emocional, así como las animaciones de reemplazo entre sprites y de desaparición total cuando el personaje ya no está en escena. --- 🧩 Animaciones individuales por sprite "characterSprite" — Vergüenza / timidez Animaciones sugeridas: Temblor vertical muy leve (idle): simula nerviosismo o inseguridad. Parpadeo ocasional: si se dispone de una versión con ojos cerrados, se puede alternar brevemente cada 4–6 segundos. Entrada con deslizamiento suave desde abajo a la izquierda + fade-in: enfatiza una presencia tímida y contenida. --- "characterSprite2" — Reflexión / indecisión Animaciones sugeridas: Efecto de “respiración” (zoom-in-out leve y lento): simula introspección. Transición con desenfoque suave (blur-in): crea un efecto mental de concentración. Cambio desde otro sprite con crossfade: transiciones suaves para no romper la contemplación. --- "characterSprite3" — Entusiasmo / emoción positiva intensa Animaciones sugeridas: Zoom con rebote al aparecer: transmite energía y emoción viva. Glow o resplandor breve: acentúa el entusiasmo. Shake horizontal sutil al aparecer: refleja vibración emocional. --- 🔄 Animación para reemplazo de un sprite por otro Cuando un sprite es reemplazado por otro sprite (por ejemplo, de "characterSprite" a "characterSprite2"), se recomienda lo siguiente: Animación recomendada: Desvanecimiento cruzado (crossfade) entre ambos sprites. Deslizamiento cruzado: Sprite saliente: opacity 1 → 0 y translateX: -10px Sprite entrante: opacity 0 → 1 y translateX: 10px → 0 Opcional: Añadir un flash blanco de 1–2 frames si el cambio emocional es muy intenso. --- 🧨 Animación de desaparición cuando el sprite no es reemplazado por ninguno Cuando el retrato debe desaparecer sin ser reemplazado por otro sprite, se sugiere: Animación recomendada: Desvanecimiento completo (opacity: 1 → 0) Contracción vertical leve (scaleY: 1 → 0.85) Desplazamiento descendente sutil (translateY: 0 → 6px) Esto simula una retirada emocional elegante y silenciosa. --- 🚫 Bloqueo de interacción durante las animaciones Durante cualquier animación (entrada, cambio o desaparición de sprite), se recomienda desactivar temporalmente la interacción con el diálogo o botones hasta que la animación haya finalizado, para evitar errores visuales o interrupciones. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
🎨 Sugerencias para la animación del ícono del personaje principal (retrato sobre el dialogBox) Este ícono representa al personaje principal y cambia según su expresión emocional durante el diálogo. Aunque no es un personaje jugable, cumple una función expresiva importante y mejora la inmersión del jugador. Los sprites disponibles actualmente son: characterSprite → expresión de vergüenza characterSprite2 → expresión de pensamiento / decisión characterSprite3 → expresión de entusiasmo --- 🧠 Animaciones sugeridas por expresión 🔻 characterSprite (Vergüenza) Objetivo: Transmitir timidez o incomodidad. Animaciones sugeridas: Parpadeo lento + bajada leve de cabeza. Brillo tenue y pulsante en las mejillas. Microtemblor vertical casi imperceptible. 🔷 characterSprite2 (Pensativo / Decisión) Objetivo: Mostrar duda o reflexión. Animaciones sugeridas: Movimiento ocular suave, mirando de lado a lado. Efecto de “pulso” leve del retrato (aura o brillo). Balanceo suave lateral, lento. 🔶 characterSprite3 (Entusiasmo) Objetivo: Reflejar energía o emoción positiva. Animaciones sugeridas: Mini salto hacia arriba con rebote. Efecto de “pop” o resplandor breve en los bordes. Sacudida horizontal rápida pero ligera. --- 🔄 Transiciones generales entre sprites (cuando uno reemplaza a otro) Cuando un sprite es reemplazado por otro, se sugiere una animación de transición fluida para que el cambio no sea brusco. Algunas opciones: Fundido cruzado (crossfade) entre el sprite saliente y el nuevo. Desenfoque temporal + entrada nítida del nuevo sprite. Desplazamiento lateral + entrada opuesta del nuevo (como si uno saliera hacia la izquierda y el otro entrara por la derecha). Flash blanco muy tenue entre uno y otro para enfatizar un cambio emocional rápido. --- ❌ Animación de desaparición (cuando el sprite se oculta y no es reemplazado por otro) Cuando el sprite desaparece sin ser reemplazado, se recomienda una animación distinta que refleje que el ícono ha salido de escena: Desvanecimiento con escala descendente: el retrato se desvanece mientras se encoge levemente. Zoom out + opacidad decreciente. Desenfoque progresivo hasta desvanecerse completamente. Fade out con ligera vibración final (opcional, para enfatizar impacto emocional). Esto ayuda a reforzar que el personaje ya no está participando activamente en ese momento del diálogo o que su presencia emocional se ha retirado. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
🧩 Petición para implementar animaciones en el icono de retrato del personaje principal Quiero implementar animaciones sutiles y expresivas en el ícono que aparece encima del dialogBox, el cual representa el rostro del personaje principal. Este ícono no es jugable, y su propósito es mostrar la expresión actual del personaje, según la escena. Los sprites disponibles por el momento son: "characterSprite" → Expresión de vergüenza. "characterSprite2" → Expresión de estar pensando antes de tomar una decisión. "characterSprite3" → Expresión de entusiasmo. --- 🎭 Animaciones específicas para cada sprite 🔹 characterSprite (Vergüenza) Parpadeo lento y ocasional. Leve bajada de cabeza y mirada evasiva hacia abajo/lateral. Sonrojo sutil con entrada/salida en fade. (Opcional) Ligero temblor vertical de 1px por unos segundos. 🔹 characterSprite2 (Pensativo) Movimiento ocular lateral suave, como si analizara. Fruncimiento de cejas con entrada en fade. Movimiento ascendente leve de la cabeza seguido de regreso al centro. (Opcional) Líneas flotantes tenues de pensamiento estilo manga. 🔹 characterSprite3 (Entusiasmo) Brillo (sparkle) suave que aparece y desaparece cada cierto tiempo. Entrada con efecto rebote + ligera rotación. Zoom inicial de 105% → 100% al aparecer. (Opcional) Pequeña sacudida lateral alegre al aparecer. --- 🔄 Animaciones generales de transición entre sprites Cuando un sprite es reemplazado por otro, aplicar la siguiente transición: Desvanecimiento cruzado (crossfade) de 0.4 segundos. Leve rebote de escala (1.05 → 1.0) al mostrar el nuevo sprite. Desenfoque breve (blur-out del sprite anterior → blur-in del nuevo). Desplazamiento vertical sutil (entrada desde 5px arriba). --- ❌ Animación de salida sin reemplazo Cuando el sprite simplemente debe desaparecer sin que aparezca otro en su lugar: Aplicar una animación de desvanecimiento con leve escalado inverso (100% → 95%) y caída de opacidad hasta 0 en 0.4 segundos. (Opcional) Añadir una animación de deslizamiento descendente y fade-out, como si el personaje se "retirara" visualmente. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
🎨 Propuesta de animaciones para el icono del personaje principal sobre el diálogo Este icono representa al protagonista, ubicado encima del cuadro de diálogo (dialogBox). No es un personaje jugable, sino un retrato que expresa sus emociones cambiando de sprite. Actualmente se utilizan tres sprites: "characterSprite" → Expresión de vergüenza "characterSprite2" → Expresión de reflexión o duda "characterSprite3" → Expresión de entusiasmo A continuación se detallan las animaciones recomendadas para cada sprite según su carga emocional, así como las animaciones de reemplazo entre sprites y de desaparición total cuando el personaje ya no está en escena. --- 🧩 Animaciones individuales por sprite "characterSprite" — Vergüenza / timidez Animaciones sugeridas: Temblor vertical muy leve (idle): simula nerviosismo o inseguridad. Parpadeo ocasional: si se dispone de una versión con ojos cerrados, se puede alternar brevemente cada 4–6 segundos. Entrada con deslizamiento suave desde abajo a la izquierda + fade-in: enfatiza una presencia tímida y contenida. --- "characterSprite2" — Reflexión / indecisión Animaciones sugeridas: Efecto de “respiración” (zoom-in-out leve y lento): simula introspección. Transición con desenfoque suave (blur-in): crea un efecto mental de concentración. Cambio desde otro sprite con crossfade: transiciones suaves para no romper la contemplación. --- "characterSprite3" — Entusiasmo / emoción positiva intensa Animaciones sugeridas: Zoom con rebote al aparecer: transmite energía y emoción viva. Glow o resplandor breve: acentúa el entusiasmo. Shake horizontal sutil al aparecer: refleja vibración emocional. --- 🔄 Animación para reemplazo de un sprite por otro Cuando un sprite es reemplazado por otro sprite (por ejemplo, de "characterSprite" a "characterSprite2"), se recomienda lo siguiente: Animación recomendada: Desvanecimiento cruzado (crossfade) entre ambos sprites. Deslizamiento cruzado: Sprite saliente: opacity 1 → 0 y translateX: -10px Sprite entrante: opacity 0 → 1 y translateX: 10px → 0 Opcional: Añadir un flash blanco de 1–2 frames si el cambio emocional es muy intenso. --- 🧨 Animación de desaparición cuando el sprite no es reemplazado por ninguno Cuando el retrato debe desaparecer sin ser reemplazado por otro sprite, se sugiere: Animación recomendada: Desvanecimiento completo (opacity: 1 → 0) Contracción vertical leve (scaleY: 1 → 0.85) Desplazamiento descendente sutil (translateY: 0 → 6px) Esto simula una retirada emocional elegante y silenciosa. --- 🚫 Bloqueo de interacción durante las animaciones Durante cualquier animación (entrada, cambio o desaparición de sprite), se recomienda desactivar temporalmente la interacción con el diálogo o botones hasta que la animación haya finalizado, para evitar errores visuales o interrupciones. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Solicito que los botones choiceButton y choiceButtonWallpaper también detengan cualquier voz en reproducción al ser seleccionados.
User prompt
Por favor, añade la funcionalidad al botón "Siguiente" para que, además de avanzar el texto, también interrumpa cualquier línea de voz que esté en reproducción. Esto evitará la reproducción simultánea de múltiples audios cuando el jugador avance rápidamente.
User prompt
1. Prioridad absoluta: Los botones no deben ser interactivos hasta que la animación haya finalizado por completo Antes de cualquier otra cosa, por favor asegúrate de lo siguiente de forma estricta: > Durante toda la animación de aparición, los choiceButton y el choiceButtonWallpaper no deben ser tocables, clicables, seleccionables, ni deben reaccionar a ningún tipo de entrada. Es decir, no pueden ser interactivos hasta que la animación haya terminado totalmente en todos los botones. Esto es muy importante. Imagina que los botones están "dormidos" o "apagados" mientras aparecen. El jugador no debe poder tocarlos, activarlos ni hacer nada con ellos mientras se están moviendo o apareciendo en pantalla. Debe cumplirse lo siguiente: Su propiedad de interacción debe estar completamente desactivada (interactable: false, pointer-events: none, etc.). No deben tener ningún evento activo de hover, click, ni visualmente parecer tocables. Si se usa un sistema de navegación con teclado o gamepad, deben estar excluidos del foco mientras están animando. Además, para reforzar esta condición y evitar errores de toque accidental, debe colocarse una pared invisible (overlay transparente) sobre los choiceButton y choiceButtonWallpaper durante la animación. Esta pared debe: Cubrir toda la superficie ocupada por los botones. Seguir su movimiento (si se animan en desplazamiento). Desactivarse automáticamente en cuanto termine completamente la animación de todos los botones. En caso de no poder sincronizarse con el final exacto, mantenerse activa al menos 1 minuto y 30 segundos. --- 2. Animación de aparición (tipo 7): Slide In desde izquierda y derecha de forma escalonada Esta es la animación visual que deben tener los choiceButton y el choiceButtonWallpaper al aparecer. Efecto buscado: Los botones deben deslizarse lateralmente desde fuera de la pantalla (unos desde la izquierda, otros desde la derecha), y hacerlo de forma escalonada, uno tras otro con un leve retardo. Detalles técnicos: Estado inicial: opacity: 0 transform: translateX(-100px) o translateX(+100px) según el lado desde el que aparezca scale: 0.95 pointer-events: none Animación: Transición de translateX(±100px) → 0px opacity: 0 → 1 scale: 0.95 → 1.0 easing: easeOutCubic o easeOutQuint duration: 0.4 segundos por botón Stagger (retraso escalonado): Primer botón: delay: 0.0s Segundo botón: delay: 0.1s Tercer botón: delay: 0.2s Cuarto botón (si existe): delay: 0.3s > Importante: la interactividad no debe habilitarse hasta que el último botón haya terminado completamente su animación. Esto se puede lograr con un evento de onAnimationComplete o temporizador con margen de seguridad (por ejemplo, esperar 0.7 segundos si hay 3 botones). --- 3. Animación de desaparición (combinada visualmente con la aparición) Para que la animación de salida sea coherente con la entrada lateral tipo 7, se recomienda una animación de desaparición tipo Fade Out + Slide Down. Efecto buscado: Los botones se desvanecen mientras se deslizan suavemente hacia abajo, dando la sensación de que "caen" o "se disuelven" hacia fuera del campo de visión. Detalles técnicos: opacity: 1 → 0 transform: translateY(0px) → translateY(40px) scale: 1.0 → 0.95 duration: 0.3 segundos easing: easeInCubic o easeInOut Se puede aplicar simultáneamente a todos los botones (no se requiere stagger aquí). Una vez terminada esta animación: Los botones deben eliminarse completamente del DOM o escena (o desactivarse si es más eficiente). Su interactividad debe volver a estar en estado false de inmediato. --- Conclusión Para evitar cualquier malentendido, lo más importante es: Bloquear toda interacción durante la animación de aparición. Asegurar la aparición escalonada lateral. Combinar con una desaparición fluida descendente. Este comportamiento debe aplicarse tanto a los choiceButton como al choiceButtonWallpaper al mismo tiempo. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
1. Prioridad absoluta: Los botones no deben ser interactivos hasta que la animación haya finalizado por completo Antes de cualquier otra cosa, por favor asegúrate de lo siguiente de forma estricta: > Durante toda la animación de aparición, los choiceButton y el choiceButtonWallpaper no deben ser tocables, clicables, seleccionables, ni deben reaccionar a ningún tipo de entrada. Es decir, no pueden ser interactivos hasta que la animación haya terminado totalmente en todos los botones. Esto es muy importante. Imagina que los botones están "dormidos" o "apagados" mientras aparecen. El jugador no debe poder tocarlos, activarlos ni hacer nada con ellos mientras se están moviendo o apareciendo en pantalla. Debe cumplirse lo siguiente: Su propiedad de interacción debe estar completamente desactivada (interactable: false, pointer-events: none, etc.). No deben tener ningún evento activo de hover, click, ni visualmente parecer tocables. Si se usa un sistema de navegación con teclado o gamepad, deben estar excluidos del foco mientras están animando. Además, para reforzar esta condición y evitar errores de toque accidental, debe colocarse una pared invisible (overlay transparente) sobre los choiceButton y choiceButtonWallpaper durante la animación. Esta pared debe: Cubrir toda la superficie ocupada por los botones. Seguir su movimiento (si se animan en desplazamiento). Desactivarse automáticamente en cuanto termine completamente la animación de todos los botones. En caso de no poder sincronizarse con el final exacto, mantenerse activa al menos 1 minuto y 30 segundos. --- 2. Animación de aparición (tipo 7): Slide In desde izquierda y derecha de forma escalonada Esta es la animación visual que deben tener los choiceButton y el choiceButtonWallpaper al aparecer. Efecto buscado: Los botones deben deslizarse lateralmente desde fuera de la pantalla (unos desde la izquierda, otros desde la derecha), y hacerlo de forma escalonada, uno tras otro con un leve retardo. Detalles técnicos: Estado inicial: opacity: 0 transform: translateX(-100px) o translateX(+100px) según el lado desde el que aparezca scale: 0.95 pointer-events: none Animación: Transición de translateX(±100px) → 0px opacity: 0 → 1 scale: 0.95 → 1.0 easing: easeOutCubic o easeOutQuint duration: 0.4 segundos por botón Stagger (retraso escalonado): Primer botón: delay: 0.0s Segundo botón: delay: 0.1s Tercer botón: delay: 0.2s Cuarto botón (si existe): delay: 0.3s > Importante: la interactividad no debe habilitarse hasta que el último botón haya terminado completamente su animación. Esto se puede lograr con un evento de onAnimationComplete o temporizador con margen de seguridad (por ejemplo, esperar 0.7 segundos si hay 3 botones). --- 3. Animación de desaparición (combinada visualmente con la aparición) Para que la animación de salida sea coherente con la entrada lateral tipo 7, se recomienda una animación de desaparición tipo Fade Out + Slide Down. Efecto buscado: Los botones se desvanecen mientras se deslizan suavemente hacia abajo, dando la sensación de que "caen" o "se disuelven" hacia fuera del campo de visión. Detalles técnicos: opacity: 1 → 0 transform: translateY(0px) → translateY(40px) scale: 1.0 → 0.95 duration: 0.3 segundos easing: easeInCubic o easeInOut Se puede aplicar simultáneamente a todos los botones (no se requiere stagger aquí). Una vez terminada esta animación: Los botones deben eliminarse completamente del DOM o escena (o desactivarse si es más eficiente). Su interactividad debe volver a estar en estado false de inmediato. --- Conclusión Para evitar cualquier malentendido, lo más importante es: Bloquear toda interacción durante la animación de aparición. Asegurar la aparición escalonada lateral. Combinar con una desaparición fluida descendente. Este comportamiento debe aplicarse tanto a los choiceButton como al choiceButtonWallpaper al mismo tiempo. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Solicitud de implementación: Animación de aparición y desaparición de choiceButton y choiceButtonWallpaper 1. Instrucción enfática: Los botones NO deben ser interactivos hasta que la animación haya finalizado completamente Este es un requerimiento absolutamente obligatorio: Los botones choiceButton y el fondo choiceButtonWallpaper no deben ser interactivos, clicables, ni sensibles al tacto en ningún momento mientras su animación de aparición esté en curso. Esto debe cumplirse de forma estricta, sin excepciones ni márgenes de error. Durante toda la duración de la animación: Deben estar desactivados lógicamente: no se deben registrar clics, toques, ni ningún tipo de entrada del usuario. Deben estar desactivados visualmente: sin hover, cambios de color ni animaciones adicionales que sugieran que pueden ser tocados. La propiedad de interacción debe estar completamente bloqueada (interactable: false, pointer-events: none, o equivalente en el motor que se esté utilizando). Para asegurarlo, debe colocarse encima una pared invisible (overlay) que cubra tanto los choiceButton como el choiceButtonWallpaper. Esta pared: Se posiciona por encima de los botones. Los sigue visualmente durante su desplazamiento si se mueven. Se elimina automáticamente una vez que haya finalizado la animación completa de todos los elementos. Si no puede sincronizarse con el fin real de la animación, debe mantenerse al menos 1 minuto y 30 segundos como medida de protección. No debe ser posible tocar un solo botón antes de que la animación haya terminado. Si eso sucede, es un error crítico. --- 2. Animación de aparición (Slide In Left / Right escalonado) La animación de aparición debe tener un estilo suave, fluido y natural. Los botones deben entrar desde los lados, uno por uno, con un ligero retardo entre ellos. Detalles técnicos de la animación: Movimiento principal: deslizamiento horizontal desde fuera de pantalla hasta su posición final. Dirección: Los botones alternan entre deslizarse desde la izquierda y desde la derecha. translateX: -100px o translateX: +100px dependiendo del lado. Estado inicial de cada botón: opacity: 0 (invisible) transform: translateX(±100px) scale: 0.95 (ligeramente más pequeño) interactable: false Durante la animación: translateX → 0 opacity: 1 scale: 1.0 easing: easeOutCubic o easeOutQuint (curva de aceleración suave) Duración por botón: 0.4 segundos Retraso escalonado (stagger): Primer botón: delay: 0.0s Segundo botón: delay: 0.1s Tercer botón: delay: 0.2s (Y así sucesivamente) Tiempo total estimado para 3 botones: 0.6 segundos Importante: Solo se deben habilitar los botones para ser tocados después de que el último botón haya terminado completamente su animación. Esto puede controlarse por un evento de onAnimationEnd o esperar un margen de seguridad de 0.7 segundos. --- 3. Animación de desaparición (complementaria y coherente con la aparición) La animación de desaparición debe tener un estilo visual que combine con la aparición lateral. Como los botones entran desde los lados, su salida debe tener una sensación ligera, como si se disiparan hacia abajo. Detalles técnicos de la animación de desaparición: Movimiento: deslizamiento hacia abajo con desvanecimiento translateY: 0 → 40px opacity: 1 → 0 scale: 1.0 → 0.95 (ligero encogimiento) Duración: 0.3 segundos easing: easeInCubic o easeInOut para una salida suave y rápida Esta animación puede ejecutarse en todos los botones simultáneamente, sin necesidad de escalonamiento. Una vez finalizada esta animación: Los botones y el fondo deben ser eliminados o desactivados completamente. No deben quedar residuos visuales ni activos interactivos en pantalla. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Solicitud para implementación de animación y lógica de choiceButton y choiceButtonWallpaper --- 1. Requisito crítico: Los botones no deben ser interactivos hasta que termine completamente la animación Antes que cualquier otra cosa, asegúrate de que los choiceButton y el choiceButtonWallpaper no sean interactivos ni tocables de ninguna manera mientras la animación de aparición esté en curso. Esto significa que: No deben aceptar toques, clics, enfoque, comandos de teclado, ni disparar eventos de ningún tipo durante la animación. No deben cambiar de apariencia visual (ni highlight, ni hover, ni efectos de selección). Su propiedad de interacción debe estar completamente deshabilitada (interactable = false, pointer-events: none, o equivalente). Cualquier intento de interacción debe ser bloqueado completamente hasta que la animación haya finalizado en todos los botones. Como refuerzo adicional, se debe crear una pared invisible o capa de bloqueo que: Esté posicionada encima de los choiceButton y choiceButtonWallpaper. Siga a los botones si estos se mueven durante la animación. Dure como mínimo 1 minuto y 30 segundos si no se tiene un evento preciso de finalización de animación. Se elimine inmediatamente después de que la animación haya terminado. No se debe permitir ninguna interacción antes del final de la animación, ni siquiera un solo fotograma antes. --- 2. Animación de aparición — Tipo 7: Slide In Left / Right con retraso escalonado Los botones deben aparecer con una animación lateral suave y secuencial. La animación debe aplicarse tanto a los choiceButton como al choiceButtonWallpaper. Propiedades de animación para cada botón: Estado inicial: opacity: 0 transform: translateX(-100px) si entra desde la izquierda transform: translateX(100px) si entra desde la derecha pointer-events: none Animación: Transición de opacity: 0 → 1 Transición de translateX: ±100px → 0px Opcional: scale: 0.95 → 1.0 para dar peso visual Easing: easeOutCubic o easeOutQuint Duración por botón: 0.4 segundos Aparición escalonada (staggered): Primer botón: delay = 0.0s Segundo botón: delay = 0.1s Tercer botón: delay = 0.2s Y así sucesivamente para botones adicionales Tiempo total estimado para 3 botones: Último botón empieza a los 0.2s y termina a los 0.6s. No debe habilitarse la interacción antes de los 0.7s. Se recomienda activar la interactividad únicamente con un evento que confirme el fin de la última animación, no con temporizador. --- 3. Animación de desaparición — Fade Out + Slide Down Para que combine visualmente con la aparición lateral, la animación de desaparición debe transmitir ligereza y movimiento descendente. Recomendación: que los botones "caigan" suavemente y se desvanezcan. Propiedades de la animación de desaparición: opacity: 1 → 0 translateY: 0px → 40px scale: 1.0 → 0.95 (opcional) Duración: 0.3 segundos Easing: easeInCubic o easeInOut La desaparición puede ejecutarse de forma simultánea en todos los botones (sin stagger). Importante: al finalizar esta animación, los elementos deben ser eliminados de la pantalla y su interactividad debe desactivarse de inmediato. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
⚠️ Regla obligatoria: Bajo ninguna circunstancia se debe permitir que el jugador interactúe con los choiceButton ni con el choiceButtonWallpaper hasta que su animación de aparición haya terminado por completo. Esto significa que: No pueden recibir toques, clics, comandos ni enfoque de ningún tipo. No deben reaccionar al cursor, a un dedo en pantalla, ni cambiar de estado visual (hover, highlight, selección, etc.). No deben emitir sonido, respuesta visual o lógica. No deben ser interactivos ni siquiera parcialmente durante la animación. ✔️ La interacción debe activarse solo después de que la animación haya finalizado completamente, tanto visual como internamente. Cualquier posibilidad de tocar o activar los botones antes de ese punto exacto es un error crítico y debe ser evitado. --- 🧱 Refuerzo adicional – Pared invisible de protección > Para evitar errores accidentales en la activación, se debe colocar una pared invisible o capa de bloqueo: Que cubra completamente los choiceButton y el choiceButtonWallpaper. Que siga su movimiento exactamente, incluso si se mueven en pantalla durante la animación. Que se mantenga activa durante 1 minuto y 30 segundos si no hay forma de sincronizarla con el final real de la animación. Esta capa debe bloquear toda interacción física y lógica. --- 🎞️📐 SEGUNDA PARTE – Descripción técnica detallada de la animación (Slide In Left / Right) > La animación de aparición con deslizamiento lateral (Slide In Left / Right) debe aplicarse tanto a los choiceButton como al choiceButtonWallpaper. A continuación se detalla cómo debe ejecutarse: 🔁 Tipo de animación: > Slide In Left / Right con aparición secuencial (staggered) 📊 Propiedades visuales por botón: Inicio (antes de animar): opacity: 0 (completamente invisible) transform: translateX(-100px) si entra desde la izquierda transform: translateX(100px) si entra desde la derecha pointer-events: none Durante la animación (por botón individual): Transición de opacity: 0 → 1 Transición de translateX: ±100px → 0px Transición de scale: 0.95 → 1.0 (opcional, para sensación de peso) Easing: ease-out o easeOutCubic Duración por botón: 0.4 segundos Retraso entre botones (stagger): Botón 1: delay = 0.0s Botón 2: delay = 0.1s Botón 3: delay = 0.2s Y así sucesivamente si hay más Total estimado de tiempo hasta que termina la animación completa (3 botones): Último botón inicia a los 0.2s, y dura 0.4s Tiempo total: 0.6 segundos Aun así, el sistema no debe habilitar interacción hasta que se confirme que la animación del último botón ha terminado. --- ✅ Condición lógica para permitir interacción IF (animación_completada == true) AND (pared_invisible_terminó == true) THEN (activar_interacción == true) ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Haz que la pared invisible cubra toda la pantalla para que la precisión sea máxima. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Se me ocurrió una solución: al mostrar los choiceButton, agrega una pared invisible que se mantenga encima de ellos y del choiceButtonWallpaper, y que los siga mientras se animan. Esta capa debe permanecer durante 1 minuto y 30 segundos, funcionando como una barrera protectora para evitar cualquier toque accidental. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
No se desactivaron los botones, corrige eso. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Durante la animación de aparición con deslizamiento lateral de los choiceButton y choiceButtonWallpaper, se desactivan todos los botones del juego por un total de 1 minuto y 30 segundos, incluyendo los propios botones de elección. Esta medida garantiza que los elementos no puedan ser tocados accidentalmente antes de que la animación haya finalizado por completo. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
El problema persiste: Toma medidas drasticas. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
El problema persiste: no debe poder tocarse nada en pantalla hasta que haya finalizado completamente la animación de aparición lateral (Slide In Left / Right). ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
⚠️ Corregir: los choiceButton y choiceButtonWallpaper no deben ser interactivos durante la animación de entrada lateral (Slide In Left / Right). Esperar a que toda la animación finalice antes de permitir cualquier selección. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Durante la animación de aparición con deslizamiento lateral (Slide In Left / Right), tanto choiceButton como choiceButtonWallpaper deben tener sus propiedades de interacción desactivadas (interactable = false, disabled = true, etc.). La interacción solo debe activarse cuando se complete la animación del último botón. Esto garantiza que no se pueda hacer clic accidentalmente en un botón o fondo antes de que todo haya aparecido visualmente. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Corregir: los choiceButton no deben ser interactivos hasta que finalice la animación de entrada. Esperar a que todos los botones terminen de aparecer antes de habilitar la interacción. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ var Character = Container.expand(function () { var self = Container.call(this); var sprite = self.attachAsset('characterSprite', { anchorX: 0.5, anchorY: 1, x: 280, y: 2732 - 700 }); var sprite2 = self.attachAsset('characterSprite2', { anchorX: 0.5, anchorY: 1, x: 280, y: 2732 - 700 }); sprite2.visible = false; var sprite3 = self.attachAsset('characterSprite3', { anchorX: 0.5, anchorY: 1, x: 280, y: 2732 - 700 }); sprite3.visible = false; self.show = function (isDecisionMoment, isMotivated) { sprite.tint = 0xffffff; // Always use white/no tint if (isMotivated) { sprite.visible = false; sprite2.visible = false; sprite3.visible = true; } else if (isDecisionMoment) { sprite.visible = false; sprite2.visible = true; sprite3.visible = false; } else { sprite.visible = true; sprite2.visible = false; sprite3.visible = false; } self.visible = true; }; self.hide = function () { self.visible = false; }; return self; }); var ChoiceSystem = Container.expand(function () { var self = Container.call(this); var choices = []; self.showChoices = function (choiceOptions) { self.clearChoices(); // Start animation state choiceAnimationActive = true; choicesInteractionEnabled = false; // Check if this is the first scene with crossroads choices if (currentScene === 0 && choiceOptions.length === 2 && choiceOptions[0].text === 'Contarle todo a mamá' && choiceOptions[1].text === 'Mantenerlo en secreto') { // Play crossroads song for this specific choice moment LK.stopMusic(); LK.playMusic('theCrossroadsSong'); } // Check if this is the school scene with three route choices if (currentScene === 2 && choiceOptions.length === 3 && choiceOptions[0].text === 'Acercarme a Aiko' && choiceOptions[1].text === 'Hablar con Yui' && choiceOptions[2].text === 'Seguir a Hikari') { // Play crossroads song for this specific choice moment LK.stopMusic(); LK.playMusic('theCrossroadsSong'); } // Check if this is Aiko route tutoring choice moment if (currentScene === 3 && choiceOptions.length === 2 && choiceOptions[0].text === 'Pedirle que sea mi tutora' && choiceOptions[1].text === 'Sugerir estudiar juntos como amigos') { // Play crossroads song for this specific choice moment LK.stopMusic(); LK.playMusic('theCrossroadsSong'); } // Update character sprite for decision moment if Satoshi is visible if (selectedRoute && character.visible) { character.show(true); } // Create invisible wall that covers all choice buttons invisibleWall = self.attachAsset('choiceButton', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 900 + (choiceOptions.length - 1) * 200, // Center on all buttons scaleX: 1.2, scaleY: choiceOptions.length + 0.5, // Cover all buttons with margin alpha: 0, // Completely invisible tint: 0x000000 }); // Track animation completion count var animationsCompleted = 0; var totalAnimations = choiceOptions.length; for (var i = 0; i < choiceOptions.length; i++) { var choice = choiceOptions[i]; // Choice button wallpaper - positioned and centered with choiceButton within 2048x2048 scene area var choiceButtonWallpaper = self.attachAsset('choiceButtonWallpaper', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 900 + i * 400, alpha: 0, // Start invisible scaleX: 0.95 // Start slightly smaller }); var choiceBtn = self.attachAsset('choiceButton', { anchorX: 0.5, anchorY: 0.5, x: 1024 - 100, //{Y} // Start 100px to the left for slide-in effect y: 900 + i * 400, alpha: 0, // Start invisible scaleX: 0.95 // Start slightly smaller }); var choiceText = new Text2(choice.text, { size: 85, fill: 0x000000, wordWrap: true, wordWrapWidth: 750 }); choiceText.anchor.set(0.5, 0.5); choiceText.x = choiceBtn.x; choiceText.y = choiceBtn.y; choiceText.alpha = 0; // Start invisible choiceText.scaleX = 0.95; // Start slightly smaller self.addChild(choiceText); choiceBtn.choiceData = choice; choiceBtn.down = function () { // Only allow interaction if animation is complete and enabled if (choicesInteractionEnabled && !choiceAnimationActive) { game.makeChoice(this.choiceData); } }; // Store original positions for animation choiceBtn.targetX = 1024; choiceButtonWallpaper.targetX = 1024; choiceText.targetX = 1024; choices.push({ btn: choiceBtn, text: choiceText, wallpaper: choiceButtonWallpaper }); // Animate each button with staggered delay (100ms apart) var animationDelay = i * 100; // Animate wallpaper tween(choiceButtonWallpaper, { x: choiceButtonWallpaper.targetX, alpha: 1, scaleX: 1.0 }, { duration: 400, delay: animationDelay, easing: tween.easeOut }); // Animate button tween(choiceBtn, { x: choiceBtn.targetX, alpha: 1, scaleX: 1.0 }, { duration: 400, delay: animationDelay, easing: tween.easeOut }); // Animate text with completion tracking tween(choiceText, { x: choiceText.targetX, alpha: 1, scaleX: 1.0 }, { duration: 400, delay: animationDelay, easing: tween.easeOut, onFinish: function onFinish() { animationsCompleted++; // Check if all animations are complete if (animationsCompleted >= totalAnimations) { // Animation complete - enable interaction choiceAnimationActive = false; choicesInteractionEnabled = true; // Remove invisible wall if (invisibleWall) { invisibleWall.destroy(); invisibleWall = null; } } } }); } self.visible = true; }; self.clearChoices = function () { // Reset animation state choiceAnimationActive = false; choicesInteractionEnabled = false; // Remove invisible wall if it exists if (invisibleWall) { invisibleWall.destroy(); invisibleWall = null; } for (var i = 0; i < choices.length; i++) { choices[i].btn.destroy(); choices[i].text.destroy(); if (choices[i].wallpaper) { choices[i].wallpaper.destroy(); } } choices = []; self.visible = false; }; return self; }); var DialogSystem = Container.expand(function () { var self = Container.call(this); // Dialog box var dialogBox = self.attachAsset('dialogBox', { anchorX: 0.5, anchorY: 1, x: 1024, y: 2732 - 200 }); // Variables for text acceleration var isDialogPressed = false; var normalTypewriterDelay = 50; var fastTypewriterDelay = 10; // Name box wallpaper - positioned and centered with nameBox var nameBoxWallpaper = self.attachAsset('nameBoxWallpaper', { anchorX: 0.5, anchorY: 0.5, x: 250, // Centered with nameBox x position (50) + nameBox width/2 (200) y: 2732 - 590 // Centered with nameBox y position }); // Name box var nameBox = self.attachAsset('nameBox', { anchorX: 0, anchorY: 1, x: 50, y: 2732 - 550 }); // Texts var nameText = new Text2('', { size: 80, fill: 0x000000 }); nameText.anchor.set(0.5, 0.5); nameText.x = nameBox.x + 200; nameText.y = nameBox.y - 40; self.addChild(nameText); var dialogText = new Text2('', { size: 90, fill: 0x000000, wordWrap: true, wordWrapWidth: 1400 }); dialogText.anchor.set(0, 0); dialogText.x = dialogBox.x - 900; dialogText.y = dialogBox.y - 280; self.addChild(dialogText); // Next button var nextBtn = self.attachAsset('nextButton', { anchorX: 0.5, anchorY: 0.5, x: 1750, y: 2732 - 350 }); var nextText = new Text2('', { size: 85, fill: 0x000000 }); nextText.anchor.set(0.5, 0.5); nextText.x = nextBtn.x; nextText.y = nextBtn.y; self.addChild(nextText); self.showDialog = function (speaker, text) { nameText.setText(speaker); // Clear previous text and hide next button during typing dialogText.setText(''); nextBtn.visible = false; nextText.visible = false; nextBtn.alpha = 0; nextText.alpha = 0; nextBtn.interactive = false; // Show nameBoxWallpaper and nameBox together nameBoxWallpaper.visible = true; nameBox.visible = true; self.visible = true; // Reset acceleration state isDialogPressed = false; // Typewriter effect variables var currentCharIndex = 0; var typewriterDelay = normalTypewriterDelay; // Function to add next character function addNextCharacter() { if (currentCharIndex < text.length) { dialogText.setText(text.substring(0, currentCharIndex + 1)); currentCharIndex++; // Use current delay based on press state typewriterDelay = isDialogPressed ? fastTypewriterDelay : normalTypewriterDelay; // Schedule next character LK.setTimeout(addNextCharacter, typewriterDelay); } else { // Typing complete - show next button unless GAME OVER if (speaker === 'GAME OVER') { nextBtn.visible = false; nextText.visible = false; } else { nextBtn.visible = true; nextText.visible = true; // Entry animation: scale from 0.0x to 1.0x nextBtn.scaleX = 0; nextBtn.scaleY = 0; nextText.scaleX = 0; nextText.scaleY = 0; tween(nextBtn, { scaleX: 1, scaleY: 1, alpha: 1 }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { nextBtn.interactive = true; // Start bounce animation after entry self.startBounceAnimation(); } }); tween(nextText, { scaleX: 1, scaleY: 1, alpha: 1 }, { duration: 300, easing: tween.easeOut }); } } } // Start typewriter effect addNextCharacter(); }; self.hide = function () { // Hide nameBoxWallpaper and nameBox together nameBoxWallpaper.visible = false; nameBox.visible = false; self.visible = false; }; // Dialog box press handlers for text acceleration dialogBox.down = function () { isDialogPressed = true; }; dialogBox.up = function () { isDialogPressed = false; }; nextBtn.down = function () { // Stop bounce animation when pressed self.stopBounceAnimation(); // Disable button interaction immediately nextBtn.interactive = false; // Exit animation: scale from 1.0x to 0.0x tween(nextBtn, { scaleX: 0, scaleY: 0 }, { duration: 200, easing: tween.easeIn, onFinish: function onFinish() { nextBtn.visible = false; // Execute original button logic after animation if (game.currentChoices.length > 0) { game.showChoices(); } else { game.nextDialog(); } } }); tween(nextText, { scaleX: 0, scaleY: 0 }, { duration: 200, easing: tween.easeIn, onFinish: function onFinish() { nextText.visible = false; } }); }; // Bounce animation for next button var bounceTimer = null; var originalNextBtnX = 1750; self.startBounceAnimation = function () { // Clear any existing bounce timer if (bounceTimer) { LK.clearInterval(bounceTimer); } // Start bounce animation every 4 seconds bounceTimer = LK.setInterval(function () { if (!nextBtn.visible || !nextBtn.interactive) return; // First bounce: +8 px tween(nextBtn, { x: originalNextBtnX + 8 }, { duration: 75, easing: tween.easeInOut, onFinish: function onFinish() { // Return to original position tween(nextBtn, { x: originalNextBtnX }, { duration: 75, easing: tween.easeInOut, onFinish: function onFinish() { // Second bounce: +16 px tween(nextBtn, { x: originalNextBtnX + 16 }, { duration: 75, easing: tween.easeInOut, onFinish: function onFinish() { // Return to original position tween(nextBtn, { x: originalNextBtnX }, { duration: 75, easing: tween.easeInOut }); } }); } }); } }); }, 4000); }; self.stopBounceAnimation = function () { if (bounceTimer) { LK.clearInterval(bounceTimer); bounceTimer = null; } }; return self; }); var MathEvaluation = Container.expand(function () { var self = Container.call(this); var currentInputIndex = 0; var answers = ['', '', '', '']; var problems = [{ question: '8 + 5 = ', answer: '13' }, { question: '12 - 4 = ', answer: '8' }, { question: '3 × 6 = ', answer: '18' }, { question: '16 ÷ 4 = ', answer: '4' }]; var inputFields = []; var questionTexts = []; // Background var mathBackground = self.attachAsset('mathEvaluationBackground', { x: 0, y: 0 }); // Grid lines for notebook effect for (var i = 0; i < 50; i++) { var gridLine = self.attachAsset('gridLine', { x: 0, y: i * 50, scaleX: 40 }); } // Aiko character var aikoSprite = self.attachAsset('aikoEvaluation', { anchorX: 0.5, anchorY: 1, x: 1700, y: 1200, scaleX: 0.8, scaleY: 0.8 }); // Title var titleText = new Text2('Evaluación Matemática', { size: 120, fill: 0x000000 }); titleText.anchor.set(0.5, 0); titleText.x = 1024; titleText.y = 200; self.addChild(titleText); // Create problems and input fields for (var i = 0; i < problems.length; i++) { var questionText = new Text2(i + 1 + '. ' + problems[i].question, { size: 100, fill: 0x000000 }); questionText.anchor.set(0, 0.5); questionText.x = 300; questionText.y = 500 + i * 200; self.addChild(questionText); questionTexts.push(questionText); var inputField = self.attachAsset('mathEvaluationInput', { anchorX: 0.5, anchorY: 0.5, x: 900, y: 500 + i * 200 }); var inputText = new Text2('', { size: 80, fill: 0x000000 }); inputText.anchor.set(0.5, 0.5); inputText.x = inputField.x; inputText.y = inputField.y; self.addChild(inputText); inputField.inputText = inputText; inputField.problemIndex = i; inputField.down = function () { currentInputIndex = this.problemIndex; }; inputFields.push({ field: inputField, text: inputText }); } // Number buttons var numberButtons = []; for (var i = 0; i <= 9; i++) { var numBtn = self.attachAsset('mathEvaluationButton', { anchorX: 0.5, anchorY: 0.5, x: 200 + i % 5 * 120, y: 1600 + Math.floor(i / 5) * 100 }); var numText = new Text2(i.toString(), { size: 60, fill: 0xffffff }); numText.anchor.set(0.5, 0.5); numText.x = numBtn.x; numText.y = numBtn.y; self.addChild(numText); numBtn.number = i; numBtn.down = function () { if (answers[currentInputIndex].length < 3) { answers[currentInputIndex] += this.number.toString(); inputFields[currentInputIndex].text.setText(answers[currentInputIndex]); } }; numberButtons.push(numBtn); } // Clear button var clearBtn = self.attachAsset('mathEvaluationButton', { anchorX: 0.5, anchorY: 0.5, x: 800, y: 1600 }); var clearText = new Text2('Borrar', { size: 50, fill: 0xffffff }); clearText.anchor.set(0.5, 0.5); clearText.x = clearBtn.x; clearText.y = clearBtn.y; self.addChild(clearText); clearBtn.down = function () { answers[currentInputIndex] = ''; inputFields[currentInputIndex].text.setText(''); }; // Submit button var submitBtn = self.attachAsset('mathEvaluationButton', { anchorX: 0.5, anchorY: 0.5, x: 1000, y: 1600, scaleX: 1.5 }); var submitText = new Text2('Listo', { size: 60, fill: 0xffffff }); submitText.anchor.set(0.5, 0.5); submitText.x = submitBtn.x; submitText.y = submitBtn.y; self.addChild(submitText); submitBtn.down = function () { var allCorrect = true; for (var i = 0; i < problems.length; i++) { if (answers[i] !== problems[i].answer) { allCorrect = false; break; } } if (allCorrect) { game.startRomanticScene(); } else { // Handle incorrect answers - show failure scene game.startMathEvaluationFailure(); } }; return self; }); var RomanticScene = Container.expand(function () { var self = Container.call(this); var currentRomanticDialog = 0; var romanticDialogs = [{ speaker: 'Aiko', text: '...¿Todas correctas? Hmm... No está mal, para alguien como vos.' }, { speaker: 'Aiko', text: 'Vamos.', action: 'transition' }, { speaker: 'Aiko', text: '¿Sabés algo? No esperaba sentirme así después de enseñarle a alguien como vos.' }, { speaker: 'Aiko', text: 'Pero... hay algo que me gusta de tu esfuerzo. ...Y de tus ojos tontos también.' }, { speaker: 'Aiko', text: 'Eso fue por aprobar mi prueba.', action: 'kiss' }]; // Classroom at sunset background var sunsetBackground = self.attachAsset('aulaAtardecer', { x: 0, y: 0 }); // Aiko character - hidden by default since this is not math evaluation var aikoSprite = self.attachAsset('aikoEvaluation', { anchorX: 0.5, anchorY: 1, x: 1400, y: 2200, visible: false }); self.showRomanticDialog = function () { if (currentRomanticDialog >= romanticDialogs.length) { self.showFinalChoices(); return; } var dialog = romanticDialogs[currentRomanticDialog]; dialogSystem.showDialog(dialog.speaker, dialog.text); // Play aikoAudioLine5 when Aiko says the specific dialog about all correct answers if (dialog.speaker === 'Aiko' && dialog.text === '...¿Todas correctas? Hmm... No está mal, para alguien como vos.') { LK.getSound('aikoAudioLine5').play(); } // Play aikoAudioLine6 when Aiko says "Vamos." if (dialog.speaker === 'Aiko' && dialog.text === 'Vamos.') { LK.getSound('audioAikoLine6').play(); } // Play aikoAudioLine7 when Aiko says the specific dialog about unexpected feelings if (dialog.speaker === 'Aiko' && dialog.text === '¿Sabés algo? No esperaba sentirme así después de enseñarle a alguien como vos.') { LK.getSound('aikoAudioLine7').play(); } // Play aikoAudioLine8 when Aiko says dialog about effort and silly eyes if (dialog.speaker === 'Aiko' && dialog.text === 'Pero... hay algo que me gusta de tu esfuerzo. ...Y de tus ojos tontos también.') { LK.getSound('aikoAudioLine8').play(); } // Play kiss sound effect followed by aikoAudioLine9 when Aiko says kiss dialog if (dialog.speaker === 'Aiko' && dialog.text === 'Eso fue por aprobar mi prueba.') { var kissSound = LK.getSound('Kissonthecheek'); kissSound.play(); // Wait for kiss sound to finish completely before playing voice LK.setTimeout(function () { LK.getSound('aikoAudioLine9').play(); }, 3000); // Further increased timing to create clear separation between sounds } if (dialog.action === 'transition') { // Create cleaning room background with fade transition var cleaningBackground = self.attachAsset('cuartoLimpiezaBackground', { x: 0, y: 0, alpha: 0 }); // Send background to back self.setChildIndex(cleaningBackground, 0); // Fade out sunset background and fade in cleaning background simultaneously tween(sunsetBackground, { alpha: 0 }, { duration: 800, easing: tween.easeInOut, onFinish: function onFinish() { sunsetBackground.destroy(); } }); tween(cleaningBackground, { alpha: 1 }, { duration: 800, easing: tween.easeInOut }); // Keep aikoEvaluation hidden during romantic scene self.setChildIndex(aikoSprite, self.children.length - 1); // Ensure dialog system and choice system are visible and in front if (dialogSystem) { game.setChildIndex(dialogSystem, game.children.length - 1); dialogSystem.visible = true; } if (choiceSystem) { game.setChildIndex(choiceSystem, game.children.length - 1); choiceSystem.visible = true; } currentRomanticDialog++; } else if (dialog.text === '¿Sabés algo? No esperaba sentirme así después de enseñarle a alguien como vos.') { // Change background to school corridor for this specific dialog with fade transition var corridorBackground = self.attachAsset('schoolcorridor', { x: 0, y: 0, alpha: 0 }); // Send background to back self.setChildIndex(corridorBackground, 0); // Fade out current background and fade in corridor background if (self.children.length > 1) { var oldBackground = self.children[1]; // Get the previous background (index 1 since corridor is now at 0) if (oldBackground && oldBackground.destroy) { tween(oldBackground, { alpha: 0 }, { duration: 800, easing: tween.easeInOut, onFinish: function onFinish() { oldBackground.destroy(); } }); } } tween(corridorBackground, { alpha: 1 }, { duration: 800, easing: tween.easeInOut }); // Keep aikoEvaluation hidden during romantic scene self.setChildIndex(aikoSprite, self.children.length - 1); // Ensure dialog system and choice system are visible and in front if (dialogSystem) { game.setChildIndex(dialogSystem, game.children.length - 1); dialogSystem.visible = true; } if (choiceSystem) { game.setChildIndex(choiceSystem, game.children.length - 1); choiceSystem.visible = true; } currentRomanticDialog++; } else if (dialog.text === 'Eso fue por aprobar mi prueba.') { // Change background to AikoKiss for this specific dialog with fade transition // Store reference to current background before creating new one var oldBackground = null; if (self.children.length > 0) { oldBackground = self.children[0]; } var kissBackground = self.attachAsset('AikoKiss', { x: 0, y: 0, alpha: 0 }); // Send background to back self.setChildIndex(kissBackground, 0); // Fade out current background and fade in kiss background if (oldBackground && oldBackground.destroy) { tween(oldBackground, { alpha: 0 }, { duration: 800, easing: tween.easeInOut, onFinish: function onFinish() { oldBackground.destroy(); } }); } tween(kissBackground, { alpha: 1 }, { duration: 800, easing: tween.easeInOut }); // Show kiss effect LK.effects.flashScreen(0xff69b4, 500); var kissText = new Text2('Satoshi ha sido besado por primera vez.', { size: 80, fill: 0xff1493 }); kissText.anchor.set(0.5, 0.5); kissText.x = 1024; kissText.y = 1366; self.addChild(kissText); currentRomanticDialog++; } else if (dialog.action === 'kiss') { // Show kiss effect LK.effects.flashScreen(0xff69b4, 500); var kissText = new Text2('Satoshi ha sido besado por primera vez.', { size: 80, fill: 0xff1493 }); kissText.anchor.set(0.5, 0.5); kissText.x = 1024; kissText.y = 1366; self.addChild(kissText); currentRomanticDialog++; } else { currentRomanticDialog++; } }; self.showFinalChoices = function () { dialogSystem.hide(); // Play crossroads music for the final romantic choice moment LK.stopMusic(); LK.playMusic('theCrossroadsSong'); var finalChoices = [{ text: 'Aiko... quiero más que un beso.', type: 'romantic' }, { text: 'No... no puedo hacer esto contigo.', type: 'reject' }]; choiceSystem.showChoices(finalChoices); }; return self; }); var TitleScreen = Container.expand(function () { var self = Container.call(this); // Background var spaceBackground = self.attachAsset('titleSpaceBackground', { x: 0, y: 0 }); // Stars array for twinkling effect var stars = []; for (var i = 0; i < 50; i++) { var star = self.attachAsset('titleStar', { anchorX: 0.5, anchorY: 0.5, x: Math.random() * 2048, y: Math.random() * 2732 }); stars.push(star); } // Hyperstar setup var hyperStar = self.attachAsset('titleHyperStar', { anchorX: 0.5, anchorY: 0.5, x: Math.random() * 2048, y: Math.random() * 2732, alpha: 0 }); // Logo setup (starts tiny) var logo = self.attachAsset('titleScreenLogo', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 800, scaleX: 0.1, scaleY: 0.1 }); // Start button (starts off screen) var startButton = self.attachAsset('titleStartButton', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: -500 }); // Earth for transition var earth = self.attachAsset('titleEarth', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 2000, scaleX: 0.3, scaleY: 0.3 }); // Cat setup var catSprites = ['titleCatFloat', 'titleCatJump', 'titleCatMeow', 'titleCatPlay', 'titleCatSleep', 'titleCatEat']; var currentCatSprite = 0; var catTouchCount = 0; var catAngry = false; var catRemoved = false; var cat = self.attachAsset('titleCatFloat', { anchorX: 0.5, anchorY: 0.5, x: 300, y: 1200, scaleX: 1.5, scaleY: 1.5 }); var catVelocityX = 1; var catVelocityY = 0.5; // Cat spaceship (hidden initially) var catSpaceship = self.attachAsset('titleCatSpaceship', { anchorX: 0.5, anchorY: 0.5, x: -300, y: cat.y, scaleX: 1.2, scaleY: 1.2, visible: false }); // Timers var starTwinkleTimer = 0; var hyperStarTimer = 0; var catAnimationTimer = 0; var hyperStarActive = false; // Initialize animations self.initAnimations = function () { // Logo grow animation tween(logo, { scaleX: 1.0, scaleY: 1.0 }, { duration: 2000, easing: tween.elasticOut }); // Start button bounce in tween(startButton, { y: 1800 }, { duration: 1500, easing: tween.bounceOut }); }; self.update = function () { if (catRemoved) return; // Star twinkling starTwinkleTimer++; if (starTwinkleTimer % 60 === 0) { var randomStar = stars[Math.floor(Math.random() * stars.length)]; tween(randomStar, { alpha: 0.3 }, { duration: 300, onFinish: function onFinish() { tween(randomStar, { alpha: 1 }, { duration: 300 }); } }); } // Additional star twinkling for more frequent blinking if (starTwinkleTimer % 30 === 0) { var randomStar2 = stars[Math.floor(Math.random() * stars.length)]; tween(randomStar2, { alpha: 0.1 }, { duration: 200, onFinish: function onFinish() { tween(randomStar2, { alpha: 1 }, { duration: 400 }); } }); } // Continuous gentle twinkling for all stars if (starTwinkleTimer % 15 === 0) { for (var i = 0; i < stars.length; i++) { if (Math.random() < 0.3) { // 30% chance for each star var star = stars[i]; tween(star, { alpha: 0.5 + Math.random() * 0.5 }, { duration: 500 + Math.random() * 1000 }); } } } // Hyperstar movement hyperStarTimer++; if (hyperStarTimer % 900 === 0) { // Every 15 seconds if (!hyperStarActive) { hyperStarActive = true; hyperStar.x = Math.random() * 2048; hyperStar.y = Math.random() * 2732; tween(hyperStar, { alpha: 1 }, { duration: 500, onFinish: function onFinish() { var targetX = Math.random() * 2048; var targetY = Math.random() * 2732; tween(hyperStar, { x: targetX, y: targetY }, { duration: 2000, easing: tween.linear, onFinish: function onFinish() { tween(hyperStar, { alpha: 0 }, { duration: 500, onFinish: function onFinish() { hyperStarActive = false; } }); } }); } }); } } // Cat floating movement if (!catAngry) { cat.x += catVelocityX; cat.y += catVelocityY; // Bounce off edges if (cat.x <= 100 || cat.x >= 1948) { catVelocityX *= -1; } if (cat.y <= 100 || cat.y >= 2632) { catVelocityY *= -1; } // Random sprite change catAnimationTimer++; if (catAnimationTimer % 180 === 0) { currentCatSprite = (currentCatSprite + 1) % catSprites.length; cat.destroy(); cat = self.attachAsset(catSprites[currentCatSprite], { anchorX: 0.5, anchorY: 0.5, x: cat.x, y: cat.y, scaleX: 1.5, scaleY: 1.5 }); // Re-add touch handler cat.down = function () { if (catRemoved) return; catTouchCount++; if (catTouchCount >= 10 && !catAngry) { self.makeCatAngry(); } }; } } }; self.makeCatAngry = function () { catAngry = true; cat.destroy(); cat = self.attachAsset('titleCatAngry', { anchorX: 0.5, anchorY: 0.5, x: cat.x, y: cat.y, scaleX: 1.5, scaleY: 1.5 }); // After 1 second, show spaceship LK.setTimeout(function () { catSpaceship.visible = true; catSpaceship.y = cat.y; // Move spaceship to cat tween(catSpaceship, { x: cat.x - 100 }, { duration: 2000, easing: tween.linear, onFinish: function onFinish() { // Move cat and spaceship to Earth tween(cat, { x: earth.x, y: earth.y, scaleX: 0.3, scaleY: 0.3 }, { duration: 3000, easing: tween.easeInOut }); tween(catSpaceship, { x: earth.x, y: earth.y, scaleX: 0.3, scaleY: 0.3 }, { duration: 3000, easing: tween.easeInOut, onFinish: function onFinish() { cat.visible = false; catSpaceship.visible = false; catRemoved = true; } }); } }); }, 1000); }; // Start button handler startButton.down = function () { // Zoom to Earth transition tween(earth, { scaleX: 50, scaleY: 50 }, { duration: 2000, easing: tween.easeInOut, onFinish: function onFinish() { game.startMainGame(); } }); // Zoom cat and move it in random direction if not removed if (!catRemoved) { // Random direction: 0-7 (8 directions: edges and corners) var direction = Math.floor(Math.random() * 8); var targetX, targetY; // Calculate target position based on direction switch (direction) { case 0: // Left edge targetX = -300; targetY = cat.y; break; case 1: // Right edge targetX = 2348; targetY = cat.y; break; case 2: // Top edge targetX = cat.x; targetY = -300; break; case 3: // Bottom edge targetX = cat.x; targetY = 3032; break; case 4: // Top-left corner targetX = -300; targetY = -300; break; case 5: // Top-right corner targetX = 2348; targetY = -300; break; case 6: // Bottom-left corner targetX = -300; targetY = 3032; break; case 7: // Bottom-right corner targetX = 2348; targetY = 3032; break; } // Apply much faster zoom and movement to cat tween(cat, { scaleX: 100, scaleY: 100, x: targetX, y: targetY }, { duration: 500, easing: tween.easeInOut, onFinish: function onFinish() { cat.visible = false; } }); } }; // Cat touch handler cat.down = function () { if (catRemoved) return; catTouchCount++; if (catTouchCount >= 10 && !catAngry) { self.makeCatAngry(); } }; return self; }); var WelcomeScreen = Container.expand(function () { var self = Container.call(this); var playerName = ''; var isCapitalized = false; var keyboardMode = 'letters'; // 'letters', 'symbols1', 'symbols2' // Background var background = self.attachAsset('titleSpaceBackground', { x: 0, y: 0 }); // Welcome text var welcomeText = new Text2('¡BIENVENIDO!', { size: 140, fill: 0xffffff }); welcomeText.anchor.set(0.5, 0.5); welcomeText.x = 1024; welcomeText.y = 500; self.addChild(welcomeText); // Question text var questionText = new Text2('¿Cuál es tu nombre?', { size: 100, fill: 0xffffff }); questionText.anchor.set(0.5, 0.5); questionText.x = 1024; questionText.y = 650; self.addChild(questionText); // Name display box var nameBox = self.attachAsset('inputField', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 800 }); var nameDisplayText = new Text2('', { size: 80, fill: 0x000000 }); nameDisplayText.anchor.set(0.5, 0.5); nameDisplayText.x = nameBox.x; nameDisplayText.y = nameBox.y; self.addChild(nameDisplayText); // Keyboard container var keyboard = new Container(); self.addChild(keyboard); var keys = []; // Letter layouts var letterLayout = [['!#1', 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P'], ['A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', 'Ñ'], ['SHIFT', 'Z', 'X', 'C', 'V', 'B', 'N', 'M', '←'], ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0'], [',', 'SPACE', '.', 'ENTER']]; var symbols1Layout = [['ABC', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0'], ['@', '#', '$', '%', '&', '*', '-', '+', '(', ')'], ['!', '"', "'", ':', ';', '/', '¿', '?', '¡', '=', '←'], ['SPACE', '.', '1/2', 'ENTER']]; var symbols2Layout = [['ABC', '[', ']', '{', '}', '<', '>', '^', '_', '`', '~'], ['|', '\\', '°', '¬', '·', '•', '¶', '§', '×', '÷'], ['€', '£', '¥', '¢', '©', '®', '™', '♡', '♥', '←'], ['SPACE', ',', '2/2', 'ENTER']]; function createKeyboard() { // Clear existing keys for (var i = 0; i < keys.length; i++) { keys[i].btn.destroy(); keys[i].text.destroy(); } keys = []; var layout = keyboardMode === 'letters' ? letterLayout : keyboardMode === 'symbols1' ? symbols1Layout : symbols2Layout; var startY = 1200; var keyWidth = 160; var keyHeight = 120; var keySpacing = 10; for (var row = 0; row < layout.length; row++) { var rowKeys = layout[row]; var totalRowWidth = rowKeys.length * keyWidth + (rowKeys.length - 1) * keySpacing; var startX = (2048 - totalRowWidth) / 2; for (var col = 0; col < rowKeys.length; col++) { var keyText = rowKeys[col]; var keyBtn = keyboard.attachAsset('mathEvaluationButton', { anchorX: 0.5, anchorY: 0.5, x: startX + col * (keyWidth + keySpacing) + keyWidth / 2, y: startY + row * (keyHeight + keySpacing) }); // Adjust special key widths if (keyText === 'SPACE') { keyBtn.scaleX = 3; } else if (keyText === 'SHIFT' || keyText === 'ENTER' || keyText === 'ABC' || keyText === '!#1' || keyText.includes('/') || keyText === '←') { keyBtn.scaleX = 1.2; } var displayText = keyText; if (keyText === 'SPACE') displayText = ' '; if (keyText === '←') displayText = '←'; if (keyText === 'SHIFT') displayText = '↑'; // Show lowercase letters when not in uppercase mode (letters only) if (keyboardMode === 'letters' && keyText.length === 1 && keyText.match(/[A-ZÑ]/)) { if (!isCapitalized && playerName.length > 0) { displayText = keyText.toLowerCase(); } else { displayText = keyText; } } var keyLabel = new Text2(displayText, { size: keyText === 'SPACE' ? 40 : 45, fill: 0xffffff }); keyLabel.anchor.set(0.5, 0.5); keyLabel.x = keyBtn.x; keyLabel.y = keyBtn.y; keyboard.addChild(keyLabel); keyBtn.keyValue = keyText; keyBtn.down = function () { handleKeyPress(this.keyValue); }; keys.push({ btn: keyBtn, text: keyLabel }); } } } function handleKeyPress(key) { if (key === 'ENTER') { if (playerName.length > 0) { storage.playerName = playerName; self.startTitleScreen(); } } else if (key === '←') { if (playerName.length > 0) { playerName = playerName.substring(0, playerName.length - 1); nameDisplayText.setText(playerName); // Reset to uppercase if name becomes empty if (playerName.length === 0) { isCapitalized = false; if (keyboardMode === 'letters') { createKeyboard(); } } } } else if (key === 'SHIFT') { isCapitalized = !isCapitalized; createKeyboard(); } else if (key === 'ABC' || key === '!#1') { if (keyboardMode === 'letters') { keyboardMode = 'symbols1'; } else { keyboardMode = 'letters'; } createKeyboard(); } else if (key === '1/2') { console.log('Switching to symbols2 mode'); keyboardMode = 'symbols2'; createKeyboard(); } else if (key === '2/2') { console.log('Switching back to symbols1 mode'); keyboardMode = 'symbols1'; createKeyboard(); } else if (key === 'SPACE') { if (playerName.length < 20) { playerName += ' '; nameDisplayText.setText(playerName); } } else { // Regular character input if (playerName.length < 20) { var charToAdd = key; if (keyboardMode === 'letters' && key.length === 1 && key.match(/[A-ZÑ]/)) { if (playerName.length === 0 || isCapitalized) { charToAdd = key.toUpperCase(); // Auto-switch to lowercase after first character if (playerName.length === 0) { isCapitalized = false; LK.setTimeout(function () { createKeyboard(); }, 100); } } else { charToAdd = key.toLowerCase(); } } playerName += charToAdd; nameDisplayText.setText(playerName); } } } self.startTitleScreen = function () { // Transition to title screen self.visible = false; game.showTitleScreen(); }; // Initialize with entrance animation self.initAnimations = function () { // Start everything very large (too close to see) welcomeText.scaleX = 20; welcomeText.scaleY = 20; welcomeText.alpha = 0; questionText.scaleX = 15; questionText.scaleY = 15; questionText.alpha = 0; nameBox.scaleX = 10; nameBox.scaleY = 10; nameBox.alpha = 0; nameDisplayText.scaleX = 10; nameDisplayText.scaleY = 10; nameDisplayText.alpha = 0; keyboard.scaleX = 8; keyboard.scaleY = 8; keyboard.alpha = 0; // Animate elements into view with depth effect tween(welcomeText, { scaleX: 1, scaleY: 1, alpha: 1 }, { duration: 1000, easing: tween.easeOut }); LK.setTimeout(function () { tween(questionText, { scaleX: 1, scaleY: 1, alpha: 1 }, { duration: 800, easing: tween.easeOut }); }, 300); LK.setTimeout(function () { tween(nameBox, { scaleX: 1, scaleY: 1, alpha: 1 }, { duration: 600, easing: tween.easeOut }); tween(nameDisplayText, { scaleX: 1, scaleY: 1, alpha: 1 }, { duration: 600, easing: tween.easeOut }); }, 600); LK.setTimeout(function () { // Position keyboard initially below screen keyboard.y = 2732 + 600; // Start below screen keyboard.scaleX = 1; keyboard.scaleY = 1; keyboard.alpha = 1; // Create keyboard before animation starts createKeyboard(); // Animate keyboard sliding up from bottom tween(keyboard, { y: 0 }, { duration: 800, easing: tween.easeOut }); }, 600); // Start at same time as inputField }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000000 }); /**** * Game Code ****/ // Game state // Fondos var currentScene = 0; var currentDialog = 0; var currentChoices = []; var relationshipPoints = { aiko: 0, yui: 0, hikari: 0, respect: 0, peace: 0, academic: 0 }; var selectedRoute = null; var currentBackground = null; var mathEvaluation = null; var romanticScene = null; var isInMathEvaluation = false; var isInRomanticScene = false; var gameStarted = false; // Choice animation state tracking var choiceAnimationActive = false; var choicesInteractionEnabled = false; var invisibleWall = null; // DialogBox wallpaper - renders behind all backgrounds and dialogBox var dialogBoxWallpaper = game.addChild(LK.getAsset('dialogBoxWallpaper', { x: 0, y: 2048, // Position at bottom area where dialog appears alpha: 1 })); // Send wallpaper to very back (behind all other elements) game.setChildIndex(dialogBoxWallpaper, 0); // Welcome screen (shown first) var welcomeScreen = game.addChild(new WelcomeScreen()); welcomeScreen.initAnimations(); // Title screen (initially hidden) var titleScreen = game.addChild(new TitleScreen()); titleScreen.visible = false; // Do not play title screen music during welcome screen // UI Systems (initially hidden) var dialogSystem = game.addChild(new DialogSystem()); var choiceSystem = game.addChild(new ChoiceSystem()); var character = game.addChild(new Character()); // Hide game elements initially dialogSystem.visible = false; choiceSystem.visible = false; character.visible = false; // Story data var storyScenes = [{ background: 'habitacion', dialogs: [{ speaker: 'Satoshi', text: '¡Ya es mi último año de secundaria! Necesito encontrar novia antes de graduarme.' }, { speaker: 'Satoshi', text: 'Pero primero... mejor hablo con mamá sobre mis planes.' }], choices: [{ text: 'Contarle todo a mamá', effects: { respect: 2, peace: -1 } }, { text: 'Mantenerlo en secreto', effects: { peace: 2, respect: -1 } }] }, { background: 'calle', dialogs: [{ speaker: 'Satoshi', text: 'Camino a la escuela... Hoy conoceré a las chicas que podrían cambiar mi vida.' }] }, { background: 'escuela', dialogs: [{ speaker: 'Satoshi', text: 'Aquí está la escuela. Veo tres chicas que me llaman la atención...' }, { speaker: 'Satoshi', text: 'Aiko, la delegada seria... Yui, mi amiga de la infancia... y Hikari, la misteriosa.' }], choices: [{ text: 'Acercarme a Aiko', route: 'aiko' }, { text: 'Hablar con Yui', route: 'yui' }, { text: 'Seguir a Hikari', route: 'hikari' }] }]; // Route-specific scenes var routeScenes = { aiko: [{ background: 'aula', dialogs: [{ speaker: 'Aiko', text: 'Satoshi-kun, necesitas ser más responsable con tus estudios.' }, { speaker: 'Satoshi', text: 'Tienes razón, Aiko. ¿Me ayudarías a estudiar?' }], choices: [{ text: 'Pedirle que sea mi tutora', effects: { aiko: 3 } }, { text: 'Sugerir estudiar juntos como amigos', effects: { aiko: 1 } }] }, { background: 'Aikosurprised', dialogs: [{ speaker: 'Aiko', text: '...¿Yo? ¿tu tutora?' }, { speaker: 'Aiko', text: 'Está bien. Pero no quiero distracciones. Si no cumplís, se acaba el trato.', backgroundChange: 'aula' }, { speaker: 'Satoshi', text: '¡Lo prometo! Seré el alumno más aplicado que hayas visto.' }] }, { background: 'aula', dialogs: [{ speaker: 'Aiko', text: 'Antes de seguir con estas sesiones, quiero comprobar cuánto sabés.', action: 'startMathEval', backgroundChange: 'Aikosmiling' }, { speaker: 'Aiko', text: 'Nada complicado. Empecemos.' }] }], yui: [{ background: 'patio', dialogs: [{ speaker: 'Yui', text: '¡Satoshi! Como en los viejos tiempos, ¿verdad?' }, { speaker: 'Satoshi', text: 'Sí, Yui... pero ahora te veo diferente.' }], choices: [{ text: 'Confesarle mis sentimientos', effects: { yui: 3 } }, { text: 'Mantener la amistad por ahora', effects: { yui: 1 } }] }], hikari: [{ background: 'aula2', dialogs: [{ speaker: 'Hikari', text: 'Te has dado cuenta de mí... interesante.' }, { speaker: 'Satoshi', text: 'Hay algo especial en ti, Hikari.' }], choices: [{ text: 'Preguntarle sobre su misterio', effects: { hikari: 3 } }, { text: 'Invitarla a caminar', effects: { hikari: 2 } }] }] }; function changeBackground(backgroundType) { // If there's no current background, just create the new one immediately if (!currentBackground) { currentBackground = game.addChild(LK.getAsset(backgroundType, { x: 0, y: 0 })); // Send background to back but after dialogBoxWallpaper game.setChildIndex(currentBackground, 1); // Store background type for future comparison currentBackground.backgroundType = backgroundType; return; } // Check if the new background is the same as the current one if (currentBackground.backgroundType === backgroundType) { // Same background - no transition needed return; } // Create new background with alpha 0 (invisible) var newBackground = game.addChild(LK.getAsset(backgroundType, { x: 0, y: 0, alpha: 0 })); // Send new background to back but after dialogBoxWallpaper game.setChildIndex(newBackground, 1); // Store background type for future comparison newBackground.backgroundType = backgroundType; // Store reference to old background for destruction var oldBackground = currentBackground; currentBackground = newBackground; // Fade out old background and fade in new background simultaneously tween(oldBackground, { alpha: 0 }, { duration: 800, easing: tween.easeInOut, onFinish: function onFinish() { oldBackground.destroy(); } }); tween(newBackground, { alpha: 1 }, { duration: 800, easing: tween.easeInOut }); } function showCurrentDialog() { var scene = getCurrentScene(); if (!scene || !scene.dialogs || currentDialog >= scene.dialogs.length) { if (scene && scene.choices) { currentChoices = scene.choices; choiceSystem.showChoices(scene.choices); dialogSystem.hide(); } else { nextScene(); } return; } var dialog = scene.dialogs[currentDialog]; dialogSystem.showDialog(dialog.speaker, dialog.text); // Play aikoAudioLine1 when Aiko says the specific dialog about being responsible if (dialog.speaker === 'Aiko' && dialog.text === 'Satoshi-kun, necesitas ser más responsable con tus estudios.') { LK.getSound('aikoAudioLine1').play(); } // Play aikoAudioLine2 when Aiko says the tutoring dialog if (dialog.speaker === 'Aiko' && dialog.text === '...¿Yo? ¿tu tutora?') { LK.getSound('aikoAudioLine2').play(); } // Play aikoAudioLine3 when Aiko says the deal dialog if (dialog.speaker === 'Aiko' && dialog.text === 'Está bien. Pero no quiero distracciones. Si no cumplís, se acaba el trato.') { LK.getSound('aikoAudioLine3').play(); } // Play aikoAudioLine4 when Aiko says the evaluation dialog if (dialog.speaker === 'Aiko' && dialog.text === 'Antes de seguir con estas sesiones, quiero comprobar cuánto sabés.') { LK.getSound('aikoAudioLine4').play(); } // Play aikoAudioLine5 when Aiko says the specific dialog about all correct answers if (dialog.speaker === 'Aiko' && dialog.text === '...¿Todas correctas? Hmm... No está mal, para alguien como vos.') { LK.getSound('aikoAudioLine5').play(); } // Handle background changes within dialog if (dialog.backgroundChange) { changeBackground(dialog.backgroundChange); } // Before route selection: hide characterSprite, show full Satoshi // After route selection: show characterSprite for Satoshi, hide for others if (!selectedRoute) { // Before route selection - always hide characterSprite character.hide(); } else { // After route selection - show characterSprite for Satoshi, hide for others if (dialog.speaker === 'Satoshi') { // Check if this is the specific promise dialog in Aiko route var isMotivatedMoment = selectedRoute === 'aiko' && currentScene === 4 && currentDialog === 2 && dialog.text === '¡Lo prometo! Seré el alumno más aplicado que hayas visto.'; character.show(false, isMotivatedMoment); } else { character.hide(); } } choiceSystem.clearChoices(); } function getCurrentScene() { if (selectedRoute && routeScenes[selectedRoute]) { var routeIndex = currentScene - storyScenes.length; if (routeIndex >= 0 && routeIndex < routeScenes[selectedRoute].length) { return routeScenes[selectedRoute][routeIndex]; } } if (currentScene < storyScenes.length) { return storyScenes[currentScene]; } return null; } function nextScene() { currentScene++; currentDialog = 0; currentChoices = []; // No auto-save - game resets on refresh var scene = getCurrentScene(); if (scene) { if (scene.background) { changeBackground(scene.background); } showCurrentDialog(); } else { showEnding(); } } function showEnding() { var maxPoints = 0; var winner = null; if (selectedRoute) { maxPoints = relationshipPoints[selectedRoute]; winner = selectedRoute; } var endingText = ''; if (maxPoints >= 3) { endingText = '¡Felicidades! Has encontrado el amor verdadero con ' + (winner === 'aiko' ? 'Aiko' : winner === 'yui' ? 'Yui' : 'Hikari') + '!'; LK.showYouWin(); } else { endingText = 'Aunque no encontraste el amor esta vez, aprendiste mucho sobre ti mismo.'; dialogSystem.showDialog('Narrador', endingText); } } game.nextDialog = function () { // If we're in romantic scene, continue with romantic dialog if (isInRomanticScene && romanticScene) { romanticScene.showRomanticDialog(); return; } var scene = getCurrentScene(); var dialog = scene && scene.dialogs ? scene.dialogs[currentDialog] : null; if (dialog && dialog.action === 'startMathEval' && !isInMathEvaluation) { game.startMathEvaluation(); return; } currentDialog++; // No auto-save for dialog progress showCurrentDialog(); }; game.startMathEvaluation = function () { isInMathEvaluation = true; dialogSystem.hide(); character.hide(); if (currentBackground) { currentBackground.destroy(); } // Play evaluation song during math test LK.playMusic('evaluationSong'); mathEvaluation = game.addChild(new MathEvaluation()); }; game.startMathEvaluationFailure = function () { isInMathEvaluation = false; if (mathEvaluation) { mathEvaluation.destroy(); mathEvaluation = null; } if (currentBackground) { currentBackground.destroy(); } // Stop music when player fails evaluation LK.stopMusic(); // Change to cold, grey classroom changeBackground('aula'); character.hide(); // Show Aiko's disappointed reaction with specific dialogs var failureDialogs = [{ speaker: 'Aiko', text: '...¿En serio?' }, { speaker: 'Aiko', text: 'Pensé que podrías con algo tan simple. Pero está claro que puse expectativas demasiado altas en vos.' }, { speaker: 'Aiko', text: 'Mejor andá a pedirle ayuda a tu mamá. Quizás con dibujos entiendas.' }, { speaker: 'GAME OVER', text: '¡Por eso eres virgen!' }]; var failureDialogIndex = 0; function showFailureDialog() { if (failureDialogIndex < failureDialogs.length) { var dialog = failureDialogs[failureDialogIndex]; dialogSystem.showDialog(dialog.speaker, dialog.text); failureDialogIndex++; if (dialog.speaker === 'GAME OVER') { LK.effects.flashScreen(0xff0000, 1000); // Play game over sound effect when narrator says the virgin line LK.getSound('gameOverEffect').play(); } } } // Override nextDialog temporarily for failure sequence var originalNextDialog = game.nextDialog; game.nextDialog = function () { showFailureDialog(); if (failureDialogIndex >= failureDialogs.length) { // Restore original nextDialog game.nextDialog = originalNextDialog; } }; showFailureDialog(); }; game.startRomanticScene = function () { isInMathEvaluation = false; isInRomanticScene = true; if (mathEvaluation) { mathEvaluation.destroy(); mathEvaluation = null; } if (currentBackground) { currentBackground.destroy(); } // Stop evaluation music and play romantic song when player passes evaluation LK.stopMusic(); LK.playMusic('romanticSong'); romanticScene = game.addChild(new RomanticScene()); romanticScene.showRomanticDialog(); }; game.makeChoice = function (choice) { // Handle romantic scene choices if (isInRomanticScene && choice.type) { choiceSystem.clearChoices(); if (choice.type === 'romantic') { // Resume romantic music for positive choice LK.stopMusic(); LK.playMusic('romanticSong'); // Show positive romantic outcome if (romanticScene) { romanticScene.destroy(); } // Change background to AikoPoseOjou for this specific dialog if (currentBackground) { currentBackground.destroy(); } currentBackground = game.addChild(LK.getAsset('AikoPoseOjou', { x: 0, y: 0 })); // Send background to back but after dialogBoxWallpaper game.setChildIndex(currentBackground, 1); relationshipPoints.aiko += 2; dialogSystem.showDialog('Aiko', '¿Más...? Sos más valiente de lo que pensaba. Pero antes de seguir... prométeme que vas a seguir estudiando conmigo.'); // Play aikoAudioLine10 when Aiko says the romantic choice dialog LK.getSound('aikoAudioLine10').play(); // Override nextDialog to handle Yui's route transition var originalNextDialog = game.nextDialog; game.nextDialog = function () { // Restore original nextDialog game.nextDialog = originalNextDialog; // Start Yui's hidden route game.startYuiHiddenRoute(); }; } else if (choice.type === 'reject') { // Stop music completely for rejection to emphasize gameOverEffect LK.stopMusic(); // Show game over if (romanticScene) { romanticScene.destroy(); } LK.effects.flashScreen(0xff0000, 1000); dialogSystem.showDialog('GAME OVER', '¡Por eso eres virgen!'); // Play game over sound effect with clear musical space LK.getSound('gameOverEffect').play(); } return; } // Check if this is the first scene crossroads choice and restore music if (currentScene === 0 && (choice.text === 'Contarle todo a mamá' || choice.text === 'Mantenerlo en secreto')) { // Resume the beginning song after making the crossroads choice LK.stopMusic(); LK.playMusic('theBeginningSong'); } // Check if this is the school scene route choice and play school song if (currentScene === 2 && (choice.text === 'Acercarme a Aiko' || choice.text === 'Hablar con Yui' || choice.text === 'Seguir a Hikari')) { // Play school song after making route choice LK.stopMusic(); LK.playMusic('theSchoolSong'); } // Check if this is Aiko tutoring choice and resume school song if (currentScene === 3 && (choice.text === 'Pedirle que sea mi tutora' || choice.text === 'Sugerir estudiar juntos como amigos')) { // Resume the school song after making the tutoring choice LK.stopMusic(); LK.playMusic('theSchoolSong'); } // Reset character sprite to normal state if (selectedRoute && character.visible) { character.show(false); } if (choice.effects) { for (var key in choice.effects) { relationshipPoints[key] += choice.effects[key]; } } // Special handling for Aiko route choices if (selectedRoute === 'aiko' && currentScene === 3) { if (choice.text === 'Pedirle que sea mi tutora') { relationshipPoints.academic += 1; } else if (choice.text === 'Sugerir estudiar juntos como amigos') { // Trigger immediate game over dialogSystem.hide(); choiceSystem.clearChoices(); LK.effects.flashScreen(0xff0000, 1000); dialogSystem.showDialog('GAME OVER', '¡Por eso eres virgen!'); return; } } if (choice.route) { selectedRoute = choice.route; } choiceSystem.clearChoices(); currentChoices = []; nextScene(); }; game.showChoices = function () { if (currentChoices.length > 0) { choiceSystem.showChoices(currentChoices); dialogSystem.hide(); } }; game.showTitleScreen = function () { // Hide welcome screen welcomeScreen.visible = false; // Play title screen music without looping when showing title screen LK.playMusic('titleScreenSong', { loop: false }); // Show and initialize title screen titleScreen.visible = true; titleScreen.initAnimations(); }; game.startMainGame = function () { if (gameStarted) return; gameStarted = true; // Stop title screen music LK.stopMusic(); // Start the beginning song for Satoshi's room scene LK.playMusic('theBeginningSong'); // Hide title screen titleScreen.visible = false; // Show game elements dialogSystem.visible = true; choiceSystem.visible = true; character.visible = true; // Restart button text removed - no color changes needed // Initialize game changeBackground('habitacion'); showCurrentDialog(); }; // Don't auto-start the game - wait for title screen // Restart button in top-right corner var restartButton = LK.gui.topRight.addChild(LK.getAsset('restartButton', { anchorX: 1, anchorY: 0, x: -50, y: 20, scaleX: 0.6, scaleY: 0.6 })); // Restart button text removed - sprite is sufficient restartButton.down = function () { // Stop all music immediately for complete reset LK.stopMusic(); // Clear all saved data delete storage.currentScene; delete storage.currentDialog; delete storage.relationshipPoints; delete storage.selectedRoute; delete storage.playerName; // Reset game state variables currentScene = 0; currentDialog = 0; currentChoices = []; relationshipPoints = { aiko: 0, yui: 0, hikari: 0, respect: 0, peace: 0, academic: 0 }; selectedRoute = null; isInMathEvaluation = false; isInRomanticScene = false; gameStarted = false; // Reset animation state choiceAnimationActive = false; choicesInteractionEnabled = false; invisibleWall = null; // Reset the nextDialog function to original state game.nextDialog = function () { // If we're in romantic scene, continue with romantic dialog if (isInRomanticScene && romanticScene) { romanticScene.showRomanticDialog(); return; } var scene = getCurrentScene(); var dialog = scene && scene.dialogs ? scene.dialogs[currentDialog] : null; if (dialog && dialog.action === 'startMathEval' && !isInMathEvaluation) { game.startMathEvaluation(); return; } currentDialog++; // No auto-save for dialog progress showCurrentDialog(); }; // Destroy any active special scenes and all screen elements if (mathEvaluation) { mathEvaluation.destroy(); mathEvaluation = null; } if (romanticScene) { romanticScene.destroy(); romanticScene = null; } if (currentBackground) { currentBackground.destroy(); currentBackground = null; } if (currentYuiBackground) { currentYuiBackground.destroy(); currentYuiBackground = null; } // Clear all UI elements and systems choiceSystem.clearChoices(); if (choiceSystem) { choiceSystem.destroy(); choiceSystem = null; } if (character) { character.destroy(); character = null; } if (dialogSystem) { dialogSystem.destroy(); dialogSystem = null; } // Destroy and clear all screen elements if (titleScreen) { titleScreen.destroy(); titleScreen = null; } if (welcomeScreen) { welcomeScreen.destroy(); welcomeScreen = null; } // Remove all children from game to clear the screen completely while (game.children.length > 0) { var child = game.children[0]; if (child && child.destroy) { child.destroy(); } else { game.removeChild(child); } } // Recreate dialogBoxWallpaper first (always at bottom) dialogBoxWallpaper = game.addChild(LK.getAsset('dialogBoxWallpaper', { x: 0, y: 2048, alpha: 1 })); game.setChildIndex(dialogBoxWallpaper, 0); // Recreate all systems from scratch dialogSystem = game.addChild(new DialogSystem()); choiceSystem = game.addChild(new ChoiceSystem()); character = game.addChild(new Character()); titleScreen = game.addChild(new TitleScreen()); welcomeScreen = game.addChild(new WelcomeScreen()); // Hide all game elements - start fresh at title screen dialogSystem.visible = false; choiceSystem.visible = false; character.visible = false; welcomeScreen.visible = false; // Show title screen and initialize it titleScreen.visible = true; titleScreen.initAnimations(); // Play title screen music for fresh start LK.playMusic('titleScreenSong'); }; // Yui's hidden route scenes var yuiHiddenRoute = [{ background: 'schoolcorridor', dialogs: [{ speaker: 'Narrador', text: 'En un rincón del pasillo, entre casilleros, una figura permanece inmóvil. Sus labios no se mueven. Pero su mirada... lo dice todo.' }] }, { background: 'schoolcorridor', dialogs: [{ speaker: 'Yui', text: '¿Así que ahora sonríes así por ella...?', isThought: true }] }, { background: 'schoolcorridor', dialogs: [{ speaker: 'Yui', text: 'Intenta quitármelo... y sufrirás las graves consecuencias.', isThought: true }] }, { background: 'schoolcorridor', dialogs: [{ speaker: 'Yui', text: 'Si yo no puedo tenerlo...', isThought: true }] }, { background: 'schoolcorridor', dialogs: [{ speaker: 'Yui', text: '...nadie más lo tendrá.', isThought: true }] }, { background: 'schoolcorridor', dialogs: [{ speaker: 'Yui', text: 'Satoshi, te conocí cuando éramos niños...', isThought: true }] }, { background: 'schoolcorridor', dialogs: [{ speaker: 'Yui', text: 'Jugábamos a las carreras, a las escondidas... Y a los novios...', isThought: true }] }, { background: 'schoolcorridor', dialogs: [{ speaker: 'Yui', text: 'Pero jamás pensé... que lo olvidarías.', isThought: true }] }, { background: 'schoolcorridor', dialogs: [{ speaker: 'Narrador', text: 'El amor de la infancia... no siempre se desvanece. A veces... se pudre.' }] }]; var currentYuiBackground = null; var yuiBackgroundFilters = [{ brightness: 1.0, contrast: 1.0, saturation: 1.0, tint: 0xffffff }, // Warm afternoon light { brightness: 0.8, contrast: 1.2, saturation: 0.9, tint: 0xf0f0f0 }, // Longer shadows { brightness: 0.7, contrast: 1.4, saturation: 0.8, tint: 0xff9999 }, // Reddish tint { brightness: 0.6, contrast: 1.1, saturation: 0.4, tint: 0xcccccc }, // Desaturated { brightness: 0.4, contrast: 1.6, saturation: 0.2, tint: 0x999999 }, // Dark and cold { brightness: 0.5, contrast: 1.3, saturation: 0.6, tint: 0xddaa88 }, // Sepia tones { brightness: 0.3, contrast: 1.8, saturation: 0.3, tint: 0x888888 }, // Unstable lighting { brightness: 0.2, contrast: 2.0, saturation: 0.1, tint: 0x666666 } // Almost black and white ]; function changeYuiBackground(backgroundType) { // If there's no current Yui background, just create the new one immediately if (!currentYuiBackground) { currentYuiBackground = game.addChild(LK.getAsset(backgroundType, { x: 0, y: 0 })); // Send background to back but after dialogBoxWallpaper game.setChildIndex(currentYuiBackground, 1); // Store background type for future comparison currentYuiBackground.backgroundType = backgroundType; return; } // Check if the new background is the same as the current one if (currentYuiBackground.backgroundType === backgroundType) { // Same background - no transition needed return; } // Create new background with alpha 0 (invisible) var newYuiBackground = game.addChild(LK.getAsset(backgroundType, { x: 0, y: 0, alpha: 0 })); // Send new background to back but after dialogBoxWallpaper game.setChildIndex(newYuiBackground, 1); // Store background type for future comparison newYuiBackground.backgroundType = backgroundType; // Store reference to old background for destruction var oldYuiBackground = currentYuiBackground; currentYuiBackground = newYuiBackground; // Fade out old background and fade in new background simultaneously tween(oldYuiBackground, { alpha: 0 }, { duration: 800, easing: tween.easeInOut, onFinish: function onFinish() { oldYuiBackground.destroy(); } }); tween(newYuiBackground, { alpha: 1 }, { duration: 800, easing: tween.easeInOut }); } game.startYuiHiddenRoute = function () { // Stop romantic music LK.stopMusic(); // Clear any existing scene if (romanticScene) { romanticScene.destroy(); romanticScene = null; } // Store reference to current background before creating new one var oldBackground = null; if (currentBackground) { oldBackground = currentBackground; currentBackground = null; } isInRomanticScene = false; character.hide(); // Create theLocker background with fade transition var newLockerBackground = game.addChild(LK.getAsset('theLocker', { x: 0, y: 0, alpha: 0 })); // Send background to back but after dialogBoxWallpaper game.setChildIndex(newLockerBackground, 1); // Update current background reference currentYuiBackground = newLockerBackground; // Fade out old background and fade in new background simultaneously if (oldBackground && oldBackground.destroy) { tween(oldBackground, { alpha: 0 }, { duration: 800, easing: tween.easeInOut, onFinish: function onFinish() { oldBackground.destroy(); } }); } tween(newLockerBackground, { alpha: 1 }, { duration: 800, easing: tween.easeInOut }); // Start Yui's route var yuiSceneIndex = 0; function showYuiScene() { if (yuiSceneIndex >= yuiHiddenRoute.length) { // End of Yui's route - show final message dialogSystem.showDialog('Narrador', 'Inicio de la ruta de Yui desbloqueado'); return; } var scene = yuiHiddenRoute[yuiSceneIndex]; var dialog = scene.dialogs[0]; dialogSystem.showDialog(dialog.speaker, dialog.text); // Play YandereSong when Narrator says the opening line for Yui's hidden route if (dialog.speaker === 'Narrador' && dialog.text === 'En un rincón del pasillo, entre casilleros, una figura permanece inmóvil. Sus labios no se mueven. Pero su mirada... lo dice todo.') { LK.playMusic('yandereSong'); } // Play yuiAudioLine1 when Yui says her first thought dialog if (dialog.speaker === 'Yui' && dialog.text === '¿Así que ahora sonríes así por ella...?') { LK.getSound('yuiAudioLine1').play(); // Change background to theLockerOfYui for this specific scene changeYuiBackground('theLockerOfYui'); } // Play yuiAudioLine2 when Yui says her threatening thought dialog if (dialog.speaker === 'Yui' && dialog.text === 'Intenta quitármelo... y sufrirás las graves consecuencias.') { LK.getSound('yuiAudioLine2').play(); // Change background to threatofYui for this specific scene changeYuiBackground('threatofYui'); } // Play yuiAudioLine3 when Yui says her fourth thought dialog if (dialog.speaker === 'Yui' && dialog.text === 'Si yo no puedo tenerlo...') { LK.getSound('yuiAudioLine3').play(); // Change background to threatofYui for this specific scene changeYuiBackground('threatofYui'); } // Play yuiAudioLine4 when Yui says her fifth thought dialog if (dialog.speaker === 'Yui' && dialog.text === '...nadie más lo tendrá.') { LK.getSound('yuiAudioLine4').play(); // Change background to crazyInTheLocker for this specific scene changeYuiBackground('crazyInTheLocker'); } // Play yuiAudioLine5 when Yui says her sixth thought dialog if (dialog.speaker === 'Yui' && dialog.text === 'Satoshi, te conocí cuando éramos niños...') { LK.getSound('yuiAudioLine5').play(); // Change background to theLoveOfChildren for this specific scene changeYuiBackground('theLoveOfChildren'); } // Play yuiAudioLine6 when Yui says her seventh thought dialog if (dialog.speaker === 'Yui' && dialog.text === 'Jugábamos a las carreras, a las escondidas... Y a los novios...') { LK.getSound('yuiAudioLine6').play(); // Change background to theLoveOfChildren for this specific scene changeYuiBackground('theLoveOfChildren'); } // Play yuiAudioLine7 when Yui says her eighth thought dialog if (dialog.speaker === 'Yui' && dialog.text === 'Pero jamás pensé... que lo olvidarías.') { LK.getSound('yuiAudioLine7').play(); // Change background to cryingInTheLocker for this specific scene changeYuiBackground('cryingInTheLocker'); } // Change background to cryingInTheLocker when Narrator says the final line about childhood love rotting if (dialog.speaker === 'Narrador' && dialog.text === 'El amor de la infancia... no siempre se desvanece. A veces... se pudre.') { changeYuiBackground('cryingInTheLocker'); } yuiSceneIndex++; } // Override nextDialog for Yui's route var originalNextDialog = game.nextDialog; game.nextDialog = function () { showYuiScene(); // Restore original nextDialog after Yui's route ends if (yuiSceneIndex >= yuiHiddenRoute.length) { game.nextDialog = originalNextDialog; } }; // Start Yui's route showYuiScene(); }; // Add game state to global for debugging game.currentChoices = currentChoices; // Admin skip button for instant stage bypass var adminSkipButton = LK.gui.topLeft.addChild(LK.getAsset('titleButton', { anchorX: 0, anchorY: 0, x: 120, // Avoid top-left 100x100 px reserved area y: 20, scaleX: 0.4, scaleY: 0.4, alpha: 0.3 // Semi-transparent to indicate admin function })); var skipText = new Text2('SKIP', { size: 60, fill: 0xffffff }); skipText.anchor.set(0.5, 0.5); skipText.x = adminSkipButton.x + adminSkipButton.width * 0.2; // Center on button skipText.y = adminSkipButton.y + adminSkipButton.height * 0.2; LK.gui.topLeft.addChild(skipText); adminSkipButton.down = function () { // Skip current stage based on game state if (!gameStarted) { // Skip title screen game.startMainGame(); } else if (isInMathEvaluation) { // Skip math evaluation with success game.startRomanticScene(); } else if (isInRomanticScene) { // Skip to Yui's hidden route game.startYuiHiddenRoute(); } else if (currentChoices.length > 0) { // Auto-select first choice to continue if (currentChoices[0]) { game.makeChoice(currentChoices[0]); } } else { // Skip current dialog/scene if (currentScene < storyScenes.length || selectedRoute && routeScenes[selectedRoute] && currentScene - storyScenes.length < routeScenes[selectedRoute].length) { // Skip to end of current scene var scene = getCurrentScene(); if (scene && scene.dialogs) { currentDialog = scene.dialogs.length - 1; } game.nextDialog(); } else { // Skip to ending showEnding(); } } };
===================================================================
--- original.js
+++ change.js
@@ -55,8 +55,11 @@
var self = Container.call(this);
var choices = [];
self.showChoices = function (choiceOptions) {
self.clearChoices();
+ // Start animation state
+ choiceAnimationActive = true;
+ choicesInteractionEnabled = false;
// Check if this is the first scene with crossroads choices
if (currentScene === 0 && choiceOptions.length === 2 && choiceOptions[0].text === 'Contarle todo a mamá' && choiceOptions[1].text === 'Mantenerlo en secreto') {
// Play crossroads song for this specific choice moment
LK.stopMusic();
@@ -77,30 +80,25 @@
// Update character sprite for decision moment if Satoshi is visible
if (selectedRoute && character.visible) {
character.show(true);
}
- // Track animation completion for interactivity control
- var animationsCompleted = 0;
- var totalAnimations = choiceOptions.length;
- // Create invisible wall barrier covering full screen for maximum protection
- if (choiceProtectionWall) {
- choiceProtectionWall.destroy();
- }
- choiceProtectionWall = game.addChild(LK.getAsset('choiceButton', {
+ // Create invisible wall that covers all choice buttons
+ invisibleWall = self.attachAsset('choiceButton', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
- y: 1366,
- scaleX: 20.0,
- // Cover full screen width (2048px)
- scaleY: 27.0,
- // Cover full screen height (2732px)
+ y: 900 + (choiceOptions.length - 1) * 200,
+ // Center on all buttons
+ scaleX: 1.2,
+ scaleY: choiceOptions.length + 0.5,
+ // Cover all buttons with margin
alpha: 0,
// Completely invisible
- interactive: true
- }));
- // Position wall above all other elements
- game.setChildIndex(choiceProtectionWall, game.children.length - 1);
+ tint: 0x000000
+ });
+ // Track animation completion count
+ var animationsCompleted = 0;
+ var totalAnimations = choiceOptions.length;
for (var i = 0; i < choiceOptions.length; i++) {
var choice = choiceOptions[i];
// Choice button wallpaper - positioned and centered with choiceButton within 2048x2048 scene area
var choiceButtonWallpaper = self.attachAsset('choiceButtonWallpaper', {
@@ -108,19 +106,20 @@
anchorY: 0.5,
x: 1024,
y: 900 + i * 400,
alpha: 0,
- scaleX: 0.95,
- scaleY: 0.95
+ // Start invisible
+ scaleX: 0.95 // Start slightly smaller
});
var choiceBtn = self.attachAsset('choiceButton', {
anchorX: 0.5,
anchorY: 0.5,
- x: 1024,
+ x: 1024 - 100,
+ //{Y} // Start 100px to the left for slide-in effect
y: 900 + i * 400,
alpha: 0,
- scaleX: 0.95,
- scaleY: 0.95
+ // Start invisible
+ scaleX: 0.95 // Start slightly smaller
});
var choiceText = new Text2(choice.text, {
size: 85,
fill: 0x000000,
@@ -129,157 +128,94 @@
});
choiceText.anchor.set(0.5, 0.5);
choiceText.x = choiceBtn.x;
choiceText.y = choiceBtn.y;
- choiceText.alpha = 0;
- choiceText.scaleX = 0.95;
- choiceText.scaleY = 0.95;
+ choiceText.alpha = 0; // Start invisible
+ choiceText.scaleX = 0.95; // Start slightly smaller
self.addChild(choiceText);
- // Store final positions and calculate slide-in start positions
- var finalX = 1024;
- var finalY = 900 + i * 400;
- var slideFromLeft = i % 2 === 0; // Alternate: even index from left, odd from right
- var startX = slideFromLeft ? -500 : 2548; // Start off-screen on respective sides
- // Set initial positions for slide-in animation
- choiceButtonWallpaper.x = startX;
- choiceBtn.x = startX;
- choiceText.x = startX;
- // Disable interactivity initially for both button and wallpaper
- choiceBtn.interactive = false;
- choiceButtonWallpaper.interactive = false;
choiceBtn.choiceData = choice;
choiceBtn.down = function () {
- game.makeChoice(this.choiceData);
+ // Only allow interaction if animation is complete and enabled
+ if (choicesInteractionEnabled && !choiceAnimationActive) {
+ game.makeChoice(this.choiceData);
+ }
};
- // Animate wallpaper entrance
+ // Store original positions for animation
+ choiceBtn.targetX = 1024;
+ choiceButtonWallpaper.targetX = 1024;
+ choiceText.targetX = 1024;
+ choices.push({
+ btn: choiceBtn,
+ text: choiceText,
+ wallpaper: choiceButtonWallpaper
+ });
+ // Animate each button with staggered delay (100ms apart)
+ var animationDelay = i * 100;
+ // Animate wallpaper
tween(choiceButtonWallpaper, {
- x: finalX,
+ x: choiceButtonWallpaper.targetX,
alpha: 1,
- scaleX: 1.0,
- scaleY: 1.0
+ scaleX: 1.0
}, {
duration: 400,
- delay: i * 100,
+ delay: animationDelay,
easing: tween.easeOut
});
- // Animate button entrance
+ // Animate button
tween(choiceBtn, {
- x: finalX,
+ x: choiceBtn.targetX,
alpha: 1,
- scaleX: 1.0,
- scaleY: 1.0
+ scaleX: 1.0
}, {
duration: 400,
- delay: i * 100,
+ delay: animationDelay,
+ easing: tween.easeOut
+ });
+ // Animate text with completion tracking
+ tween(choiceText, {
+ x: choiceText.targetX,
+ alpha: 1,
+ scaleX: 1.0
+ }, {
+ duration: 400,
+ delay: animationDelay,
easing: tween.easeOut,
onFinish: function onFinish() {
animationsCompleted++;
- // Enable interactivity only after all animations complete
+ // Check if all animations are complete
if (animationsCompleted >= totalAnimations) {
- for (var j = 0; j < choices.length; j++) {
- choices[j].btn.interactive = true;
- choices[j].wallpaper.interactive = true;
+ // Animation complete - enable interaction
+ choiceAnimationActive = false;
+ choicesInteractionEnabled = true;
+ // Remove invisible wall
+ if (invisibleWall) {
+ invisibleWall.destroy();
+ invisibleWall = null;
}
- // Start 1.5 minute protection timer
- LK.setTimeout(function () {
- if (choiceProtectionWall && choiceProtectionWall.destroy) {
- choiceProtectionWall.destroy();
- choiceProtectionWall = null;
- }
- }, 90000); // 1 minute 30 seconds = 90,000 milliseconds
}
}
});
- // Animate text entrance
- tween(choiceText, {
- x: finalX,
- alpha: 1,
- scaleX: 1.0,
- scaleY: 1.0
- }, {
- duration: 400,
- delay: i * 100,
- easing: tween.easeOut
- });
- choices.push({
- btn: choiceBtn,
- text: choiceText,
- wallpaper: choiceButtonWallpaper
- });
}
self.visible = true;
};
self.clearChoices = function () {
- if (choices.length === 0) {
- self.visible = false;
- return;
+ // Reset animation state
+ choiceAnimationActive = false;
+ choicesInteractionEnabled = false;
+ // Remove invisible wall if it exists
+ if (invisibleWall) {
+ invisibleWall.destroy();
+ invisibleWall = null;
}
- // Animate choices sliding out before destroying them
- var animationsToComplete = choices.length * 3; // 3 elements per choice (btn, text, wallpaper)
- var animationsCompleted = 0;
for (var i = 0; i < choices.length; i++) {
- var choice = choices[i];
- // Disable interactivity immediately for both button and wallpaper
- choice.btn.interactive = false;
- choice.wallpaper.interactive = false;
- // Remove protection wall immediately when clearing choices
- if (choiceProtectionWall && choiceProtectionWall.destroy) {
- choiceProtectionWall.destroy();
- choiceProtectionWall = null;
+ choices[i].btn.destroy();
+ choices[i].text.destroy();
+ if (choices[i].wallpaper) {
+ choices[i].wallpaper.destroy();
}
- // Determine slide-out direction (same as slide-in but reversed)
- var slideToLeft = i % 2 === 0; // Even index slides left, odd slides right
- var targetX = slideToLeft ? -500 : 2548;
- // Animate button slide-out
- tween(choice.btn, {
- x: targetX,
- alpha: 0
- }, {
- duration: 300,
- easing: tween.easeIn,
- onFinish: function onFinish() {
- animationsCompleted++;
- if (animationsCompleted >= animationsToComplete) {
- // Destroy all elements after animations complete
- for (var j = 0; j < choices.length; j++) {
- choices[j].btn.destroy();
- choices[j].text.destroy();
- if (choices[j].wallpaper) {
- choices[j].wallpaper.destroy();
- }
- }
- choices = [];
- self.visible = false;
- }
- }
- });
- // Animate text slide-out
- tween(choice.text, {
- x: targetX,
- alpha: 0
- }, {
- duration: 300,
- easing: tween.easeIn,
- onFinish: function onFinish() {
- animationsCompleted++;
- }
- });
- // Animate wallpaper slide-out
- if (choice.wallpaper) {
- tween(choice.wallpaper, {
- x: targetX,
- alpha: 0
- }, {
- duration: 300,
- easing: tween.easeIn,
- onFinish: function onFinish() {
- animationsCompleted++;
- }
- });
- } else {
- animationsCompleted++; // No wallpaper to animate
- }
}
+ choices = [];
+ self.visible = false;
};
return self;
});
var DialogSystem = Container.expand(function () {
@@ -1516,10 +1452,10 @@
/****
* Game Code
****/
-// Fondos
// Game state
+// Fondos
var currentScene = 0;
var currentDialog = 0;
var currentChoices = [];
var relationshipPoints = {
@@ -1536,8 +1472,12 @@
var romanticScene = null;
var isInMathEvaluation = false;
var isInRomanticScene = false;
var gameStarted = false;
+// Choice animation state tracking
+var choiceAnimationActive = false;
+var choicesInteractionEnabled = false;
+var invisibleWall = null;
// DialogBox wallpaper - renders behind all backgrounds and dialogBox
var dialogBoxWallpaper = game.addChild(LK.getAsset('dialogBoxWallpaper', {
x: 0,
y: 2048,
@@ -1556,10 +1496,8 @@
// UI Systems (initially hidden)
var dialogSystem = game.addChild(new DialogSystem());
var choiceSystem = game.addChild(new ChoiceSystem());
var character = game.addChild(new Character());
-// Invisible wall barrier for choice button protection
-var choiceProtectionWall = null;
// Hide game elements initially
dialogSystem.visible = false;
choiceSystem.visible = false;
character.visible = false;
@@ -2108,8 +2046,12 @@
selectedRoute = null;
isInMathEvaluation = false;
isInRomanticScene = false;
gameStarted = false;
+ // Reset animation state
+ choiceAnimationActive = false;
+ choicesInteractionEnabled = false;
+ invisibleWall = null;
// Reset the nextDialog function to original state
game.nextDialog = function () {
// If we're in romantic scene, continue with romantic dialog
if (isInRomanticScene && romanticScene) {
A cute 15-year-old red-haired boy with intense red eyes, wearing a traditional Japanese school uniform (gakuran). He's running down the street with a serious and confident expression. Despite his determined look, his appearance remains sweet and endearing. His gaze is focused straight ahead, with the wind gently lifting his hair and school jacket. The scene is illustrated in Pixel Art style.. In-Game asset. 2d. High contrast. No shadows
A cute 15-year-old red-haired boy with red eyes, wearing a traditional Japanese school uniform (gakuran). He has a big closed-mouth smile, shining eyes, and clenched fists held forward, as if encouraging himself. Behind him, other Japanese students can be seen. The scene is illustrated in Pixel Art style.. In-Game asset. 2d. High contrast. No shadows
A cute anime-style girl in pixel art, with an emo aesthetic and striking purple eyes. She wears a classic Japanese school uniform (sailor fuku) and is often shown with a slightly grumpy expression, reflecting a bit of a tsundere personality. She stands confidently with her hands on her hips and a slight tilt to her posture. Behind her is a typical Japanese classroom, enhancing the school-themed atmosphere.. In-Game asset. 2d. High contrast. No shadows
She’s a cute anime girl with a free-spirited and rebellious vibe. Her hair is pink, and she’s wearing a traditional Japanese school uniform (sailor fuku). She sports dark sunglasses and holds a skateboard in one hand while pointing with the other. Her face lights up with a big smile and mischievous pink eyes that match her hair. In the background, there's a Japanese classroom, and the entire artwork is done in Pixel Art style.. In-Game asset. 2d. High contrast. No shadows
Stella is a sweet and very kawaii 15-year-old anime girl with short, soft blue-gray hair (HEX: #b0b9d8) and black eyes. She wears a traditional Japanese school uniform called a sailor fuku. Stella is extremely shy—she blushes easily, avoids eye contact, and often covers herself when she feels nervous. In this Pixel Art scene, she’s gently touching the tips of her index fingers together, a classic gesture of shyness seen in anime. Behind her is the courtyard of a Japanese middle school, completing the charming school setting.. In-Game asset. 2d. High contrast. No shadows
A kawaii anime boy with intense red eyes, each containing shining stars. He raises his fist forward with determination, wearing a wide, open smile full of enthusiasm. Sparkles and glimmers float around him, symbolizing hope. The entire scene is rendered in Pixel Art style.
A kawaii anime boy with red eyes and a serious expression stands with his eyes closed, resting a hand under his chin in a thoughtful pose. He’s wearing a traditional Japanese school uniform called a gakuran. Suddenly, a lightbulb appears above his head—the classic symbol of a brilliant idea. He opens his eyes with determination, a confident smile forming on his face. It's Pixel Art.
Remove the sparkles from the background.
Pure white. 2d
A cute anime-style girl in pixel art, with an emo aesthetic, striking purple eyes, and a classic Japanese school uniform (sailor fuku), is studying with furrowed brows and a confident smile. She sits next to a shy red-haired boy with red eyes, also in pixel art style. Their desks are pushed together, and on the boy’s desk lies an open notebook, which he nervously stares at while holding a graphite pencil. The girl looks at him with a mix of sternness and confidence, convinced he won’t pass the exam. The background depicts a typical Japanese classroom.
A cute anime-style girl rendered in pixel art, with an emo aesthetic and striking purple eyes, is wearing a high school cheerleader uniform and holding pom-poms in both hands. With her arms and legs extended in a dynamic pose, she winks playfully while cheering. The background is an American football field.
A cute anime-style girl depicted in pixel art, with an emo aesthetic and striking purple eyes. She's wearing a classic Japanese school uniform (sailor fuku). She has a shy expression, with blushing cheeks and an averted gaze. Her left hand is playing with her hair, while her right hand rests on her chest. In the background, there's a typical anime-style classroom.
In pixel art style, a cute anime-style girl with an emo aesthetic, striking purple eyes, and a Japanese school uniform (sailor fuku) suddenly pulls the hand of a shy red-haired boy with red eyes, who is wearing a gakuran and sitting in front of a rectangular desk. She smiles brightly with her eyes closed as she yanks him toward her, pulling him out of his seat. The boy looks confused and nervous, flustered by the sudden physical contact. In the background, a typical Japanese classroom can be seen.
The camera focuses only on their hands. The girl, wearing a traditional Japanese sailor fuku uniform, firmly grabs the hand of the boy, who is dressed in a classic gakuran. She pulls him forward with energy and excitement. His hand follows with slight tension, expressing surprise. Nothing else is shown—just the gesture: a meaningful connection captured in the simple act of their hands intertwined. Behind them stretches a Japanese school hallway, all rendered in a charming Pixel Art style.
A cute anime-style girl with an emo aesthetic, striking purple eyes, and dressed in a traditional Japanese school uniform (sailor fuku), suddenly gives an unexpected kiss on the lips to a sweet red-haired boy with deep crimson eyes, wearing a classic gakuran school uniform. As she kisses him, her cheeks are deeply flushed, while he, also blushing, wears a look of shock and disbelief — not only was the kiss completely unprompted, but it’s also the very first time anyone has ever kissed him. Behind them stretches a Japanese school hallway, all rendered in a charming Pixel Art style.
A charming anime-style girl rendered in delightful Pixel Art, featuring an emo aesthetic, striking purple eyes, and a classic Japanese school uniform (sailor fuku). She confidently strikes the iconic “Ojou-sama pose”: Her left hand is elegantly lifted near her face, fingers gracefully splayed like a fan, perfectly framing her smug expression. Her face bears a tilted, self-assured smile, the kind that practically echoes a soft, aristocratic “Ohoho~” laugh. One eyebrow arches subtly, radiating playful confidence and a touch of superiority. Her right hand rests casually near her waist, loosely clenched into a relaxed fist — an anchor for her graceful yet dominant posture. Behind her stretches the polished hallway of a Japanese school, complete with sliding doors and sunlit windows, completing the scene with a nostalgic, everyday charm — all captured in the cozy, detailed charm of Pixel Art.
A cute gray chibi-style anime kitten, dressed in an astronaut suit and drifting through the universe, rendered in Pixel Art.
A cute gray kitten, drawn in an adorable chibi anime style and wearing an astronaut suit, floats in outer space with its little paws outstretched as if reaching out for a hug. It meows sweetly, and the entire scene is illustrated in charming Pixel Art style.
A cute chibi-style anime kitten, dressed in an astronaut suit and drifting through the universe, rendered in Pixel Art.
White star of the universe in Pixel Art style
Planet Earth of the universe in Pixel Art style
Create a title screen logo with the following text: "Corazón de Estudiante - Novela Visual" Behind the text, the Andromeda Galaxy (M31) stretches across the background, creating a dreamy, cosmic atmosphere that evokes mystery and romance.
A cute kitten, illustrated in an adorable chibi anime style and wearing an astronaut suit, is playfully tangled in a ball of yarn, floating through space with overwhelming joy. Its eyes are shut tight in pure happiness, and its movements are full of energetic delight.
Three-quarter view pixel art jetpack
Transparent, colorless glass button that says "Empezar" (Spanish for "Start"), featuring an adventure video game font and Pixel Art style.
Advertencia que dice: "Sin usar"
A realistic yellow star from the universe, in pixel art style.
Pure white
A cute, red-haired anime boy, 15 years old, with red eyes. He's in pajamas, looking sleepy, rubbing one eye with his hand. He's in his otaku-style bedroom. The scene is illustrated in Pixel Art style.
Blue button with two yellow chevrons in a row, pixel art style.
A green circular button featuring a fuchsia-colored refresh icon, designed in Pixel Art style.
Stella is a 15-year-old anime girl—sweet and incredibly kawaii—with short, soft hair in a bluish-gray tone (HEX: #b0b9d8) and deep black eyes. She wears a traditional Japanese school uniform, the classic sailor-style fuku. In her right hand, she holds a butcher knife. Her expression is serious, and her eyes, now glowing with an intense yandere red, reflect jealousy and obsession. Dark under-eye circles emphasize her unsettling gaze. She is inside a high school locker in a Japanese school. Her right hand is slightly raised, gripping the knife, while her left hand gently touches the blade with her index finger, mimicking the motion of a clock hand—adding to the eerie atmosphere. To emphasize the feeling of being inside the locker, soft light shines through the ventilation slits, casting dramatic shadows across her face. The entire scene is rendered in detailed Pixel Art style, enhancing both the cuteness and the unsettling atmosphere of the character and setting.
Yui y un niño anime, ambos de 5 o 6 años, corren por un parque soleado en una carrera infantil. Yui tiene el cabello corto, suave, azul grisáceo (HEX: #b0b9d8) y ojos negros. Lleva un vestido lavanda claro con volantes blancos y zapatillas blancas con detalles rosa pastel. Sonríe tímidamente con mejillas sonrojadas mientras corre con pasos pequeños. El niño tiene cabello pelirrojo alborotado y ojos rojos intensos. Lleva una camiseta amarilla brillante con una estrella blanca, pantalones cortos azul claro y zapatillas rojas. Corre con energía, sonriendo ampliamente, mirando a Yui con entusiasmo. La escena está en Pixel Art, con césped verde, árboles y pétalos flotando. Ambos se ven felices, reflejando un momento puro de infancia.
Yui is a cute 15-year-old anime girl with big black eyes and short, soft blue-gray hair (HEX: #b0b9d8). She wears a traditional Japanese school uniform called a sailor fuku. In this Pixel Art scene, Yui looks clearly upset—she has very noticeable dark circles under her eyes, her eyebrows are tense, and her teeth are pressed together in frustration. Her arms are relaxed and down at her sides, showing no physical aggression, just quiet tension. She’s inside a school locker, looking out through a metal grate with a serious, focused expression. The entire scene is viewed from inside the locker, with soft light coming through the vents, casting gentle shadows across her face and creating a slightly tense but family-friendly atmosphere.
Pixel art. A red-haired 15-year-old anime-style boy with red eyes and a girl with purple eyes in Japanese school uniforms, both with large, expressive eyes and oversized irises to look super kawaii, are talking cheerfully while looking into each other’s eyes and gesturing with their hands. They are standing very close to a row of school lockers. In the center locker, a pair of glowing red eyes peer through the slats. The eyes belong to a hidden yandere—obsessed, crazy, and jealous—whose face and body remain completely obscured in the darkness inside the locker. The mood is lighthearted between the boy and girl, contrasting with the eerie presence watching them from the shadows.
Stella, a 15-year-old anime girl with short, soft hair in a bluish-gray tone (HEX: #b0b9d8), and large black eyes with oversized irises (super kawaii style), glowing with intense yandere red. She wears a traditional Japanese school uniform (classic sailor fuku). She is inside a Japanese school locker, with soft light coming through the ventilation slits, casting dramatic shadows on her face. Her eyebrows are furrowed in extreme anger, but she has a wide, creepy smile, like someone completely insane. Dark under-eye circles highlight her unhinged expression. Her right hand holds a butcher knife slightly raised, while her left hand gently touches the blade with her index finger, mimicking a clock hand motion. Visual style: highly detailed Pixel Art.
Yui is a cute 15-year-old anime girl with big black eyes and short, soft blue-gray hair (HEX: #b0b9d8). She wears a traditional Japanese school uniform called a sailor fuku. In this Pixel Art scene, Yui is clearly devastated—she’s hunched over in a slouched, defeated posture, crying intensely. Many visible tears stream down her cheeks, slipping past her fingers as she covers her face with both hands. Her entire body language screams sadness; it’s raw, heavy, and impossible to ignore. She’s inside a school locker, and although she’s looking outward through the metal grate, her face is mostly hidden behind her trembling hands. The entire scene is viewed from inside the locker, with soft light coming through the vents, casting delicate shadows across her curled-up figure and highlighting the depth of her heartbreak. The atmosphere is heavy, emotional, and quietly heartbreaking—yet still family-friendly in tone.
Diseña exclusivamente un cuadro de diálogo de color blanco de 2048 x 684 píxeles y esquinas en ángulo recto, evocando la estética romántica y juvenil de una novela ligera escolar, todo en estilo pixel art. No debe incluir ninguna otra funcionalidad. Detrás hay un fondo de color verde croma.
Ilustración en Pixel Art de un Name Box blanco, característico de una novela visual romántica, con detalles en colores pastel. El cuadro no contiene texto.
Ilustración en Pixel Art de un Name Box blanco, característico de una novela visual romántica, con detalles en colores pastel. El cuadro no contiene texto.
LoveTime
Music
Music1
Music
titleScreenSong
Music
theBeginningSong
Music
theCrossroadsSong
Music
theSchoolSong
Music
romanticSong
Music
youWinSong
Music
gameOverSong
Music
gameOverEffect
Sound effect
evaluationSong
Music
aikoAudioLine2
Sound effect
aikoAudioLine1
Sound effect
aikoAudioLine3
Sound effect
aikoAudioLine4
Sound effect
aikoAudioLine5
Sound effect
audioAikoLine6
Sound effect
aikoAudioLine7
Sound effect
aikoAudioLine8
Sound effect
Kissonthecheek
Sound effect
aikoAudioLine9
Sound effect
aikoAudioLine10
Sound effect
yuiAudioLine1
Sound effect
yuiAudioLine2
Sound effect
yuiAudioLine3
Sound effect
yuiAudioLine4
Sound effect
yuiAudioLine5
Sound effect
yuiAudioLine6
Sound effect
yuiAudioLine7
Sound effect
yandereSong
Music