Registrarse

[pokeemerald] Mostrar la hora al desplegar el menú

BLAx501!

A veces comento en temas :3
Miembro de honor
Usuario de Platino
Si me llegan a decir hace dos años que volvería a hacer un tutorial con algo relacionado con el ROMHacking, no me lo habría creido... Pero como no me lo dijo nadie, aquí estamos. No diré de vuelta en acción porque sería adelantarme demasiado, pero sí al menos con curiosidad por estas descompilaciones de los juegos de tercera generación. En mi caso, la que más me ha llamado la atención es, sin duda alguna, la descompilación de Pokémon Esmeralda puesto que se trata de la base más avanzada dentro de la tercera generación, a la cual podemos aplicarle ahora un sinfín de cambios de manera mucho más directa y sencilla.

Sin más preámbulo, vamos con el tutorial:


Mostrar la hora al desplegar el menú

El objetivo del tutorial es, como su nombre indica, conseguir mostrar en el menú de pausa, una cajita extra con la hora actual, basándonos en el RTC y no en el sistema de hora interno del juego. Esto por sí sólo no es más que replicar en la pantalla un dato, pero con algo de creatividad y ampliando el código un poco, puede utilizarse también para crear eventos dependientes de la hora del día a la que se juegue (porque el sistema está enlazado a la hora del dispositivo con el que juguemos, y no la interna del propio juego).

Finalmente, obtendremos algo como esto:


Para ello tendremos que modificar los siguientes archivos:

  • rtc.h que se encuentra en la carpeta include
  • rtc.c que se encuentra en la carpeta src
  • start_menu.c que se encuentra en la carpeta src

rtc.h: En este archivo es donde menos cosas tenemos que tocar. Únicamente es necesario añadir la definición de las funciones que crearemos dentro de rtc.c. Así que, al final del archivo, antes de la línea donde pone:
Código:
[PLAIN]#endif // GUARD_RTC_UTIL_H[/PLAIN]
Colocamos lo siguiente:

Código:
[PLAIN]
u8 Rtc_GetCurrentHour(void);
u8 Rtc_GetCurrentMinute(void);
void FormatDecimalTimeWOSeconds(u8 *dest, u8 hour, u8 minute);
[/PLAIN]
Personalmente, recomiendo que añadáis un comentario para indicar la fecha en que habéis añadido dicho cambio a la rom para evitar posibles problemas a futuro.

Con eso terminamos de editar este fichero.​

rtc.c: En este archivo tenemos que crear las funciones que hemos definido en su fichero de cabecera, así que, de nuevo, al final del fichero escribimos lo siguiente

Código:
[PLAIN]
u8 Rtc_GetCurrentHour(void){ // Toma el valor de la hora actual del RTC
    RtcGetInfo(&sRtc);	
	if(sRtc.hour>25){
		return sRtc.hour-12;
	}
	else if(sRtc.hour>9){
		return sRtc.hour-6;
	}
	
	return sRtc.hour;
}

u8 Rtc_GetCurrentMinute(void){ // Toma el valor del minuto actual del RTC
    RtcGetInfo(&sRtc);	
	if(sRtc.minute>73){
		return sRtc.minute-30;
	}
	else if(sRtc.minute>57){
		return sRtc.minute-24;
	}
	else if(sRtc.minute>41){
		return sRtc.minute-18;
	}
	else if(sRtc.minute>25){
		return sRtc.minute-12;
	}
	else if(sRtc.minute>9){
		return sRtc.minute-6;
	}
	
	return sRtc.minute;
}

void FormatDecimalTimeWOSeconds(u8 *dest, u8 hour, u8 minute) // Función para obtener los datos de hora y minutos del RTC
{
    dest = ConvertIntToDecimalStringN(dest, hour, STR_CONV_MODE_LEADING_ZEROS, 2);
    *dest++ = CHAR_COLON;
    dest = ConvertIntToDecimalStringN(dest, minute, STR_CONV_MODE_LEADING_ZEROS, 2);
    *dest = EOS;
}
[/PLAIN]
Las dos primeras funciones toman el valor en crudo del RTC y lo convierten a un número legible. Si os preguntáis por la chapuza de los ifs, es para corregir el dato que nos propociona el RTC, porque realiza una operación muy rara.

El valor proporcionado por el RTC en horas y minutos viene dado por una fórmula un tanto extraña según he podido comprobar en varias pruebas:

Siendo X1X2 los dígitos de decenas y unidades en las dos cifras, horas y minutos, el valor que devuelve el RTC es 6*X1+(X1+X2).

Así, cuando eran las 20:12 en la vida real, para el RTC eran las 32:18; una hora un poco rara, vaya.

La tercera función es una copia modificada de una función presente en el código del juego que permite colocar en un puntero a u8 (es decir, el formato de los 4 buffers de texto) los datos de horas y minutos en formato legible, separados por dos puntos.

start_menu.c: Y llegamos al meollo del asunto. En este fichero es donde más modificaciones hay que hacer, y no es para menos, pues es donde se realizan las llamadas a las funciones creadas antes y se trabajan los gráficos del menú de pausa.

En primer lugar vamos a hacer un include de rtc.h para poder llamar a las funciones creadas en esos ficheros desde este, así que al comienzo del documento, donde están todos los include añadimos el nuestro tal que así:

Código:
[PLAIN]
#include "rtc.h"
[/PLAIN]
A continuación vamos a realizar dos definiciones en el fichero. Primero tenemos que crear la estructura de la ventana extra que vamos a crear, así que buscamos en el fichero la siguiente línea:

Código:
[PLAIN]
static const struct WindowTemplate sSafariBallsWindowTemplate = {0, 1, 1, 9, 4, 0xF, 8};
[/PLAIN]
Y justo debajo de ella pegamos lo siguiente:

Código:
[PLAIN]
static const struct WindowTemplate sStartMenuWindowTemplate = {0, 1, 1, 4, 2, 0xF, 8}; // Parámetros de la ventana extra
[/PLAIN]
Lo segundo que tenemos que definir es es la función que crearemos a continuación. Esta función estará definida en el ámbito de este fichero y nada más, por lo que realizamos la definición en el propio fichero de código y no es su cabecera como hicimos antes en los ficheros del rtc. Así que buscamos el trozo de código donde están definidas todas las funciones que se usan en este fichero (podemos buscar por // Local functions y las localizaremos rápido) y al final de todas colocamos lo siguiente:

Código:
[PLAIN]
static void ShowStartMenuExtraWindow(void);
[/PLAIN]
Ahora crearemos la función estática, así que, como antes, nos vamos al final del fichero (o a algún sitio que reconozcáis fácilmente) y colocamos lo siguiente:

Código:
[PLAIN]
static void ShowStartMenuExtraWindow(void) // Función que carga una ventana auxiliar en el menú de pausa.
{	
    sSafariBallsWindowId = AddWindow(&sStartMenuWindowTemplate);
    PutWindowTilemap(sSafariBallsWindowId);
    NewMenuHelpers_DrawStdWindowFrame(sSafariBallsWindowId, FALSE);
	FormatDecimalTimeWOSeconds(gStringVar4, Rtc_GetCurrentHour(), Rtc_GetCurrentMinute());                                     
    AddTextPrinterParameterized(sSafariBallsWindowId, 1, gStringVar4, 0, 1, 0xFF, NULL); 
    CopyWindowToVram(sSafariBallsWindowId, 2);
}
[/PLAIN]
Esta función es la encargada de crear la ventana auxiliar, tomar los datos del RTC mediante las funciones que creamos antes, formatearlo para que se pueda leer y finalmente añadirlo a la ventana que hemos creado.

Para acabar, tenemos que colocar en alguna parte del código esta función que hemos creado. Buscaremos en el fichero la siguiente función:

Código:
[PLAIN]
static void BuildNormalStartMenu(void)
[/PLAIN]
Y añadimos al final de la función, antes de la llave que la cierra una llamada a la función creada antes:

Código:
[PLAIN]
ShowStartMenuExtraWindow();
[/PLAIN]
Pero con esto no basta ya que si lo dejásemos aquí al cerrar el menú de pausa no se quitaría la pequeña cajita que hemos añadido. Por lo que navegaremos hasta la siguiente función:

Código:
[PLAIN]
static void RemoveExtraStartMenuWindows(void)
[/PLAIN]
Y para poder borrar nuestra caja tendremos que añadir el siguiente fragmento de código modificando el existente para que quede así:

Código:
[PLAIN]
static void RemoveExtraStartMenuWindows(void) //Modificado el 23/1/2019
{
    if (GetSafariZoneFlag())
    {
        sub_8198070(sSafariBallsWindowId, FALSE);
        CopyWindowToVram(sSafariBallsWindowId, 2);
        RemoveWindow(sSafariBallsWindowId);
    }else if (InBattlePyramid()) //Antes eran dos if separados
    {
        sub_8198070(sBattlePyramidFloorWindowId, FALSE);
        RemoveWindow(sBattlePyramidFloorWindowId);
    }
	else{ //Borra de la pantalla la venta auxiliar de la hora
        sub_8198070(sSafariBallsWindowId, FALSE);
        RemoveWindow(sSafariBallsWindowId);	
		
	}
}
[/PLAIN]

Y con esa última modificación ya habríamos terminado. Aunque me ha llevado un buen rato sacarlo, el principal problema ha sido comprender cómo funciona más o menos el RTC (no el reloj del juego) para poder mostrar los datos de forma correcta.

Espero que os sirva de ayuda y os anime un poquito a trabajar con pokeemerald :awesome:

Un saludo, BLAx501!


P.D. Agradecimientos a @Sayer301! y a @Dani_SR_17 que han estado conmigo por Discord y me han ayudado a entender la fórmula que usaba el RTC para sacar valores tan raros en las horas y minutos.
 

KleinStudio

Un plato es un plato
Miembro del equipo
Webmaster
Respuesta: [pokeemerald] Mostrar la hora al desplegar el menú

Cada nuevo tutorial de pokeruby/emerald es un gran aporte.
Puede ser bastante útil si pretendemos tener eventos basados en el tiempo como la Cueva Cardumen o simplemente la evolución de Eevee.
Espero ver más cosillas así, da gusto volver a ver un tutorial tuyo ;)
 

Samu

Miembro insignia
Miembro insignia
Respuesta: [pokeemerald] Mostrar la hora al desplegar el menú

Ya te lo he comentado por privado antes, pero te he resuelto el 'misterio' del extraño formato del sRtc xDD.

Por algún motivo el RTC almacena los datos en memoria utilizando los numero en hexadecimal como si se tratase de un sistema decimal, de ahí que en 24 horas tengas 36 horas. [0x24 = 36].

Si quieres dejar el código 'más elegante' sin todos los if/else puedes usar esto.

Código:
u8 Rtc_GetCurrentHour(void) // Returns current hour from RTC data
{     
    RtcGetInfo(&sRtc);	
	return sRtc.hour - (sRtc.hour/0x10) * 6;
}

u8 Rtc_GetCurrentMinute(void){ // Returns current minute from RTC data
    
    RtcGetInfo(&sRtc);
    return sRtc.minute - (sRtc.minute/0x10) * 6;
}

u8 Rtc_GetCurrentMonth(void)
{
    RtcGetInfo(&sRtc);
    return sRtc.month < 10 ? sRtc.month : sRtc.month - 6;
}

u8 Rtc_GetCurrentDay(void)
{
    RtcGetInfo(&sRtc);
    return sRtc.day - (sRtc.day/0x10) * 6;
}
Suerte!
 

zaux

Usuario mítico
Respuesta: [pokeemerald] Mostrar la hora al desplegar el menú

Que gran aporte!
De verdad todos estos tutoriales que están sacando hacen pensar en trabajar en estás plataformas, felicitaciones, muy buen trabajo
 

BLAx501!

A veces comento en temas :3
Miembro de honor
Usuario de Platino
Respuesta: [pokeemerald] Mostrar la hora al desplegar el menú

Ya te lo he comentado por privado antes, pero te he resuelto el 'misterio' del extraño formato del sRtc xDD.

Por algún motivo el RTC almacena los datos en memoria utilizando los numero en hexadecimal como si se tratase de un sistema decimal, de ahí que en 24 horas tengas 36 horas. [0x24 = 36].

Si quieres dejar el código 'más elegante' sin todos los if/else puedes usar esto.

Código:
u8 Rtc_GetCurrentHour(void) // Returns current hour from RTC data
{     
    RtcGetInfo(&sRtc);	
	return sRtc.hour - (sRtc.hour/0x10) * 6;
}

u8 Rtc_GetCurrentMinute(void){ // Returns current minute from RTC data
    
    RtcGetInfo(&sRtc);
    return sRtc.minute - (sRtc.minute/0x10) * 6;
}

u8 Rtc_GetCurrentMonth(void)
{
    RtcGetInfo(&sRtc);
    return sRtc.month < 10 ? sRtc.month : sRtc.month - 6;
}

u8 Rtc_GetCurrentDay(void)
{
    RtcGetInfo(&sRtc);
    return sRtc.day - (sRtc.day/0x10) * 6;
}
Suerte!
Perfecto, mil gracias. Cuando tenga tiempo (después exámenes así que aún queda xD) lo actualizaré :awesome:
 

GaboExtreme

El Ayudante Infinito
Respuesta: [pokeemerald] Mostrar la hora al desplegar el menú

Excelente de verdad es un gran aporte. Se podría hacer en FR?
 

Jaizu

Usuario mítico
Hola, para la gente que quiera usar esto con el repo actual pues no va a funcionar.
En donde dice
Código:
static void ShowStartMenuExtraWindow(void) // Función que carga una ventana auxiliar en el menú de pausa.
{   
    sSafariBallsWindowId = AddWindow(&sStartMenuWindowTemplate);
    PutWindowTilemap(sSafariBallsWindowId);
    NewMenuHelpers_DrawStdWindowFrame(sSafariBallsWindowId, FALSE);
    FormatDecimalTimeWOSeconds(gStringVar4, Rtc_GetCurrentHour(), Rtc_GetCurrentMinute());                                     
    AddTextPrinterParameterized(sSafariBallsWindowId, 1, gStringVar4, 0, 1, 0xFF, NULL);
    CopyWindowToVram(sSafariBallsWindowId, 2);
}
En vez de eso ponéis
Código:
static void ShowStartMenuExtraWindow(void) // Función que carga una ventana auxiliar en el menú de pausa.
{   
    sSafariBallsWindowId = AddWindow(&sStartMenuWindowTemplate);
    PutWindowTilemap(sSafariBallsWindowId);
    DrawStdWindowFrame(sSafariBallsWindowId, FALSE);
    FormatDecimalTimeWOSeconds(gStringVar4, Rtc_GetCurrentHour(), Rtc_GetCurrentMinute());                                     
    AddTextPrinterParameterized(sSafariBallsWindowId, 1, gStringVar4, 0, 1, 0xFF, NULL);
    CopyWindowToVram(sSafariBallsWindowId, 2);
}
Y en donde pone
Código:
static void RemoveExtraStartMenuWindows(void) //Modificado el 23/1/2019
{
    if (GetSafariZoneFlag())
    {
        sub_8198070(sSafariBallsWindowId, FALSE);
        CopyWindowToVram(sSafariBallsWindowId, 2);
        RemoveWindow(sSafariBallsWindowId);
    }else if (InBattlePyramid()) //Antes eran dos if separados
    {
        sub_8198070(sBattlePyramidFloorWindowId, FALSE);
        RemoveWindow(sBattlePyramidFloorWindowId);
    }
    else{ //Borra de la pantalla la venta auxiliar de la hora
        sub_8198070(sSafariBallsWindowId, FALSE);
        RemoveWindow(sSafariBallsWindowId);   
        
    }
}
En vez de eso ponéis
Código:
static void RemoveExtraStartMenuWindows(void) //Modificado el 23/1/2019
{
    if (GetSafariZoneFlag())
    {
        ClearStdWindowAndFrameToTransparent(sSafariBallsWindowId, FALSE);
        CopyWindowToVram(sSafariBallsWindowId, 2);
        RemoveWindow(sSafariBallsWindowId);
    }else if (InBattlePyramid()) //Antes eran dos if separados
    {
        ClearStdWindowAndFrameToTransparent(sBattlePyramidFloorWindowId, FALSE);
        RemoveWindow(sBattlePyramidFloorWindowId);
    }
    else{ //Borra de la pantalla la venta auxiliar de la hora
        ClearStdWindowAndFrameToTransparent(sSafariBallsWindowId, FALSE);
        RemoveWindow(sSafariBallsWindowId);   
        
    }
}
 
Arriba