Registrarse

[ASM] [FR] Posicionamiento del Menu Dinámico

MetalKaktus

A la luz del quinto día, mira al este
Miembro insignia
Intro

Hey, hace como un millón de años que no me paso por el foro pero hoy os traigo un tuto nuevo. A raíz de una duda de un usuario que me habló por Discord preguntandome si es posible cambiar el menu de posición cuando sale en pantalla. Me puse a investigar un poquito en los ficheros de pokefirered las funciones que se encargaban de gestionar todo el tema del menu (posición, paleta, items...) y acabe encontrando la solución a la duda que me preguntó. En este tutorial os explicaré por un lado todo el proceso que he seguido (buscar en los ficheros fuentes, hacer pruebas con el debugger, cambios en la rom, rutinas ASM...) por si a alguien le interesa. Por otro lado dejaré los cambios que hay que hacer a la ROM para aquellos que no les importe como lo he conseguido y solo quieren implementarlo.

Info Previa

Voy a dividir el tutorial en dos partes. Por un lado enseñaré como se puede hacer el cambio de posición del menu de forma estática e inalterable y por otro lado mostraré como hacer este cambio de forma dinámica y poder cambiarlo dentro del juego mediante el uso de variables. Para que veais que no soy un vendehumos y un mentiroso os dejo una captura con los resultados.
s1.png

(No es que sea un bug que no esté el prota, es que lo tapa el menu, que veo venir a los haters)

Posicionamiento del Menu Estático [Desarrollo]
Lo primero que tenemos que hacer es tratar de encontrar alguna función relacionada con la gestión del menu. De ahí podemos ir tirando de funciones que la llamen o de funciones que llama. Yo uso el VSCode para editar los ficheros del pokefirered. Por lo tanto abrimos el directorio del pokefirered.
s1.png


Nos vamos al directorio /scr y nos encontramos con un fichero llamado "start_menu.c". Entramos en él y vemos los prototipos de funciones que se definen dentro del fichero. Las primeras funciones son las que nos interesan.
Código:
static void SetUpStartMenu_Link(void);
static void SetUpStartMenu_UnionRoom(void);
static void SetUpStartMenu_SafariZone(void);
static void SetUpStartMenu_NormalField(void);
Al parecer esas funciones se encargan de hacer el set up del menu en diferentes situaciones. Yo me voy a centrar en la última de ellas, que es la que se encarga del menu normal (cuando no estamos en un safari o en sitios raros como los otros de arriba). Siendo sincero ni me he molestado en mirar si en los otros casos que no sean el normal usan las mismas subfunciones para el posicionamiento del menu o no, pero en cualquier caso si a alguien le interesa que lo mire o que me pregunte. Lo dicho, vamos a la función que nos interesa y nos encontramos con esto:

Código:
static void SetUpStartMenu_NormalField(void)
{
    if (FlagGet(FLAG_SYS_POKEDEX_GET) == TRUE)
        AppendToStartMenuItems(STARTMENU_POKEDEX);
    if (FlagGet(FLAG_SYS_POKEMON_GET) == TRUE)
        AppendToStartMenuItems(STARTMENU_POKEMON);
    AppendToStartMenuItems(STARTMENU_BAG);
    AppendToStartMenuItems(STARTMENU_PLAYER);
    AppendToStartMenuItems(STARTMENU_SAVE);
    AppendToStartMenuItems(STARTMENU_OPTION);
    AppendToStartMenuItems(STARTMENU_EXIT);
}
Parece que se encarga de ir añadiendo los items que salen en el menu (los dos primeros en función si hemos recibido la pokedex o si tenemos algún pokemon). Si vamos a la función que llama reiteradas veces nos encontramos esto.
Código:
static void AppendToStartMenuItems(u8 newEntry)
{
    AppendToList(sStartMenuOrder, &sNumStartMenuItems, newEntry);
}
Y si vamos a la función que llama:

Código:
void AppendToList(u8 *list, u8 *cursor, u8 newEntry)
{
    list[*cursor] = newEntry;
    (*cursor)++;
}
Al parecer la función que hemos visto al principio cuando nos hemos puesto a buscar solo sirve para actualizar una variable que otra función usa para mostrar los items en pantalla. Por lo tanto tendremos que buscar donde se llama a la función "static void SetUpStartMenu_NormalField(void)". Nos encontraremos la siguiente función.
Código:
static void SetUpStartMenu(void)
{
    sNumStartMenuItems = 0;
    if (IsUpdateLinkStateCBActive() == TRUE)
        SetUpStartMenu_Link();
    else if (InUnionRoom() == TRUE)
        SetUpStartMenu_UnionRoom();
    else if (GetSafariZoneFlag() == TRUE)
        SetUpStartMenu_SafariZone();
    else
        SetUpStartMenu_NormalField();
}
Hacemos lo propio con la función "static void SetUpStartMenu(void)". Nos encontraremos esto:

Código:
static s8 DoDrawStartMenu(void)
{
    switch (sDrawStartMenuState[0])
    {
    case 0:
        sDrawStartMenuState[0]++;
        break;
    case 1:
        SetUpStartMenu();
        sDrawStartMenuState[0]++;
        break;
    case 2:
        LoadStdWindowFrameGfx();
        DrawStdWindowFrame(CreateStartMenuWindow(sNumStartMenuItems), FALSE);
        sDrawStartMenuState[0]++;
        break;
    case 3:
        if (GetSafariZoneFlag())
            DrawSafariZoneStatsWindow();
        sDrawStartMenuState[0]++;
        break;
    case 4:
        if (PrintStartMenuItems(&sDrawStartMenuState[1], 2) == TRUE)
            sDrawStartMenuState[0]++;
        break;
    case 5:
        sStartMenuCursorPos = Menu_InitCursor(GetStartMenuWindowId(), 2, 0, 0, 15, sNumStartMenuItems, sStartMenuCursorPos);
        if (!MenuHelpers_LinkSomething() && InUnionRoom() != TRUE && gSaveBlock2Ptr->optionsButtonMode == OPTIONS_BUTTON_MODE_HELP)
        {
            DrawHelpMessageWindowWithText(sStartMenuDescPointers[sStartMenuOrder[sStartMenuCursorPos]]);
        }
        CopyWindowToVram(GetStartMenuWindowId(), COPYWIN_MAP);
        return TRUE;
    }
    return FALSE;
}
Esto ya es más interesante. vemos un swtich-case que simplemente va ejecutando en cascada todos los cases. Vemos que primero ejecuta la función de set up que hemos estado investigando, que basicamente añadia items a la lista a mostrar. El case 2 es más interesante, la función "CreateStartMenuWindow" parece más interesante. Vamos a la propia función(en new_menu_helpers.c) y nos encontramos con esto:

Código:
u8 CreateStartMenuWindow(u8 height)
{
    if (sStartMenuWindowId == 0xFF)
    {
        struct WindowTemplate template = SetWindowTemplateFields(0, 0x8, 1, 7, height * 2 - 1, DLG_WINDOW_PALETTE_NUM, 0x13D);
        sStartMenuWindowId = AddWindow(&template);
        PutWindowTilemap(sStartMenuWindowId);
    }
    return sStartMenuWindowId;
}
Que a su vez nos encotnramos con la función "SetWindowTemplateFields" que parece que tiene los parámetros que buscamos. Finalmente vemos la declaración de dicha función:
Código:
struct WindowTemplate SetWindowTemplateFields(u8 bg, u8 left, u8 top, u8 width, u8 height, u8 paletteNum, u16 baseBlock);
Bingo, los nombre de los parámetros parecen encajar con lo que estamos buscando. Según los nombre me atrevería a decir que significan:

  • u8 bg -> background
  • u8 left -> posición con respecto al borde izquierdo de la pantalla
  • u8 top -> posición con respecto al borde superior de la pantalla
  • u8 width -> ancho del box del menu
  • u8 height -> alto del box del menu
  • u8 paletteNum -> paleta del menu
  • u16 baseBlock -> ni **** idea
Para saber donde se encuentra la llamada a la función en el fichero binario de la ROM vamos a cambiar los parámetros y compararla con una ROM sin modificar. Voy a cambiar de:

Código:
struct WindowTemplate template = SetWindowTemplateFields(0, 0x16, 1, 7, height * 2 - 1, DLG_WINDOW_PALETTE_NUM, 0x13D);
a esto:

Código:
struct WindowTemplate template = SetWindowTemplateFields(0, 0x8, 4, 7, height * 2 - 1, DLG_WINDOW_PALETTE_NUM, 0x13D);
Compilamos el proyecto y cuando acabe probamos a ver que tal han quedado los cambios. El resultado ha sido el siguiente:

pokefirered-3.png


Ahora ya sabemos que se puede editar la posición del menu en la pantalla. Vamos a comparar esta ROM con una original para ver cuales son los bytes que hay que cambiar.

comp.png


Posición en eje horizontal: 0xf790c (valor de 0x0 a 0x16)
Posición en eje vertical : 0xf790e (valor de 1 a algún número, no he hecho pruebas, pero si quereis cambiarlo hacedlo a prueba y error)
Posicionamiento del Menu Estático
Posición en eje horizontal: 0xf790c (valor de 0x0 a 0x16)
Posición en eje vertical : 0xf790e (valor de 1 a algún número, no he hecho pruebas, pero si quereis cambiarlo hacedlo a prueba y error)


Como ejemplo os dejo una captura con valores de 0x12 para el primer byte y 0x3 para el segundo byte:

pokefirered-3.png


En cuanto al posicionamiento del menu dinámico estoy trabajando en ello todavía, hay que hacer una rutina ASM y demás cosas. Ya lo actualizaré hoy o mañana.

Por ahora eso es todo :)
 

Adjuntos

Checo_XP

La vida es una eXPeriencia
Ahora al menos ya se que es posible hacerlo, solo me falta aprender ASM y lo hare.
 

Shiny_Miner

Rocker Coder
Intro

Hey, hace como un millón de años que no me paso por el foro pero hoy os traigo un tuto nuevo. A raíz de una duda de un usuario que me habló por Discord preguntandome si es posible cambiar el menu de posición cuando sale en pantalla. Me puse a investigar un poquito en los ficheros de pokefirered las funciones que se encargaban de gestionar todo el tema del menu (posición, paleta, items...) y acabe encontrando la solución a la duda que me preguntó. En este tutorial os explicaré por un lado todo el proceso que he seguido (buscar en los ficheros fuentes, hacer pruebas con el debugger, cambios en la rom, rutinas ASM...) por si a alguien le interesa. Por otro lado dejaré los cambios que hay que hacer a la ROM para aquellos que no les importe como lo he conseguido y solo quieren implementarlo.

Info Previa

Voy a dividir el tutorial en dos partes. Por un lado enseñaré como se puede hacer el cambio de posición del menu de forma estática e inalterable y por otro lado mostraré como hacer este cambio de forma dinámica y poder cambiarlo dentro del juego mediante el uso de variables. Para que veais que no soy un vendehumos y un mentiroso os dejo una captura con los resultados.
Ver el archivo adjunto 10589
(No es que sea un bug que no esté el prota, es que lo tapa el menu, que veo venir a los haters)

Posicionamiento del Menu Estático [Desarrollo]
Lo primero que tenemos que hacer es tratar de encontrar alguna función relacionada con la gestión del menu. De ahí podemos ir tirando de funciones que la llamen o de funciones que llama. Yo uso el VSCode para editar los ficheros del pokefirered. Por lo tanto abrimos el directorio del pokefirered.
Ver el archivo adjunto 10591

Nos vamos al directorio /scr y nos encontramos con un fichero llamado "start_menu.c". Entramos en él y vemos los prototipos de funciones que se definen dentro del fichero. Las primeras funciones son las que nos interesan.
Código:
static void SetUpStartMenu_Link(void);
static void SetUpStartMenu_UnionRoom(void);
static void SetUpStartMenu_SafariZone(void);
static void SetUpStartMenu_NormalField(void);
Al parecer esas funciones se encargan de hacer el set up del menu en diferentes situaciones. Yo me voy a centrar en la última de ellas, que es la que se encarga del menu normal (cuando no estamos en un safari o en sitios raros como los otros de arriba). Siendo sincero ni me he molestado en mirar si en los otros casos que no sean el normal usan las mismas subfunciones para el posicionamiento del menu o no, pero en cualquier caso si a alguien le interesa que lo mire o que me pregunte. Lo dicho, vamos a la función que nos interesa y nos encontramos con esto:

Código:
static void SetUpStartMenu_NormalField(void)
{
    if (FlagGet(FLAG_SYS_POKEDEX_GET) == TRUE)
        AppendToStartMenuItems(STARTMENU_POKEDEX);
    if (FlagGet(FLAG_SYS_POKEMON_GET) == TRUE)
        AppendToStartMenuItems(STARTMENU_POKEMON);
    AppendToStartMenuItems(STARTMENU_BAG);
    AppendToStartMenuItems(STARTMENU_PLAYER);
    AppendToStartMenuItems(STARTMENU_SAVE);
    AppendToStartMenuItems(STARTMENU_OPTION);
    AppendToStartMenuItems(STARTMENU_EXIT);
}
Parece que se encarga de ir añadiendo los items que salen en el menu (los dos primeros en función si hemos recibido la pokedex o si tenemos algún pokemon). Si vamos a la función que llama reiteradas veces nos encontramos esto.
Código:
static void AppendToStartMenuItems(u8 newEntry)
{
    AppendToList(sStartMenuOrder, &sNumStartMenuItems, newEntry);
}
Y si vamos a la función que llama:

Código:
void AppendToList(u8 *list, u8 *cursor, u8 newEntry)
{
    list[*cursor] = newEntry;
    (*cursor)++;
}
Al parecer la función que hemos visto al principio cuando nos hemos puesto a buscar solo sirve para actualizar una variable que otra función usa para mostrar los items en pantalla. Por lo tanto tendremos que buscar donde se llama a la función "static void SetUpStartMenu_NormalField(void)". Nos encontraremos la siguiente función.
Código:
static void SetUpStartMenu(void)
{
    sNumStartMenuItems = 0;
    if (IsUpdateLinkStateCBActive() == TRUE)
        SetUpStartMenu_Link();
    else if (InUnionRoom() == TRUE)
        SetUpStartMenu_UnionRoom();
    else if (GetSafariZoneFlag() == TRUE)
        SetUpStartMenu_SafariZone();
    else
        SetUpStartMenu_NormalField();
}
Hacemos lo propio con la función "static void SetUpStartMenu(void)". Nos encontraremos esto:

Código:
static s8 DoDrawStartMenu(void)
{
    switch (sDrawStartMenuState[0])
    {
    case 0:
        sDrawStartMenuState[0]++;
        break;
    case 1:
        SetUpStartMenu();
        sDrawStartMenuState[0]++;
        break;
    case 2:
        LoadStdWindowFrameGfx();
        DrawStdWindowFrame(CreateStartMenuWindow(sNumStartMenuItems), FALSE);
        sDrawStartMenuState[0]++;
        break;
    case 3:
        if (GetSafariZoneFlag())
            DrawSafariZoneStatsWindow();
        sDrawStartMenuState[0]++;
        break;
    case 4:
        if (PrintStartMenuItems(&sDrawStartMenuState[1], 2) == TRUE)
            sDrawStartMenuState[0]++;
        break;
    case 5:
        sStartMenuCursorPos = Menu_InitCursor(GetStartMenuWindowId(), 2, 0, 0, 15, sNumStartMenuItems, sStartMenuCursorPos);
        if (!MenuHelpers_LinkSomething() && InUnionRoom() != TRUE && gSaveBlock2Ptr->optionsButtonMode == OPTIONS_BUTTON_MODE_HELP)
        {
            DrawHelpMessageWindowWithText(sStartMenuDescPointers[sStartMenuOrder[sStartMenuCursorPos]]);
        }
        CopyWindowToVram(GetStartMenuWindowId(), COPYWIN_MAP);
        return TRUE;
    }
    return FALSE;
}
Esto ya es más interesante. vemos un swtich-case que simplemente va ejecutando en cascada todos los cases. Vemos que primero ejecuta la función de set up que hemos estado investigando, que basicamente añadia items a la lista a mostrar. El case 2 es más interesante, la función "CreateStartMenuWindow" parece más interesante. Vamos a la propia función(en new_menu_helpers.c) y nos encontramos con esto:

Código:
u8 CreateStartMenuWindow(u8 height)
{
    if (sStartMenuWindowId == 0xFF)
    {
        struct WindowTemplate template = SetWindowTemplateFields(0, 0x8, 1, 7, height * 2 - 1, DLG_WINDOW_PALETTE_NUM, 0x13D);
        sStartMenuWindowId = AddWindow(&template);
        PutWindowTilemap(sStartMenuWindowId);
    }
    return sStartMenuWindowId;
}
Que a su vez nos encotnramos con la función "SetWindowTemplateFields" que parece que tiene los parámetros que buscamos. Finalmente vemos la declaración de dicha función:
Código:
struct WindowTemplate SetWindowTemplateFields(u8 bg, u8 left, u8 top, u8 width, u8 height, u8 paletteNum, u16 baseBlock);
Bingo, los nombre de los parámetros parecen encajar con lo que estamos buscando. Según los nombre me atrevería a decir que significan:

  • u8 bg -> background
  • u8 left -> posición con respecto al borde izquierdo de la pantalla
  • u8 top -> posición con respecto al borde superior de la pantalla
  • u8 width -> ancho del box del menu
  • u8 height -> alto del box del menu
  • u8 paletteNum -> paleta del menu
  • u16 baseBlock -> ni **** idea
Para saber donde se encuentra la llamada a la función en el fichero binario de la ROM vamos a cambiar los parámetros y compararla con una ROM sin modificar. Voy a cambiar de:

Código:
struct WindowTemplate template = SetWindowTemplateFields(0, 0x16, 1, 7, height * 2 - 1, DLG_WINDOW_PALETTE_NUM, 0x13D);
a esto:

Código:
struct WindowTemplate template = SetWindowTemplateFields(0, 0x8, 4, 7, height * 2 - 1, DLG_WINDOW_PALETTE_NUM, 0x13D);
Compilamos el proyecto y cuando acabe probamos a ver que tal han quedado los cambios. El resultado ha sido el siguiente:

Ver el archivo adjunto 10602

Ahora ya sabemos que se puede editar la posición del menu en la pantalla. Vamos a comparar esta ROM con una original para ver cuales son los bytes que hay que cambiar.

Ver el archivo adjunto 10603

Posición en eje horizontal: 0xf790c (valor de 0x0 a 0x16)
Posición en eje vertical : 0xf790e (valor de 1 a algún número, no he hecho pruebas, pero si quereis cambiarlo hacedlo a prueba y error)
Posicionamiento del Menu Estático
Posición en eje horizontal: 0xf790c (valor de 0x0 a 0x16)
Posición en eje vertical : 0xf790e (valor de 1 a algún número, no he hecho pruebas, pero si quereis cambiarlo hacedlo a prueba y error)


Como ejemplo os dejo una captura con valores de 0x12 para el primer byte y 0x3 para el segundo byte:

Ver el archivo adjunto 10602

En cuanto al posicionamiento del menu dinámico estoy trabajando en ello todavía, hay que hacer una rutina ASM y demás cosas. Ya lo actualizaré hoy o mañana.

Por ahora eso es todo :)
genial contribution! BRo
 
Arriba