Disturbo
Decomper
¡Hola hola!
Como dice el título, haciendo los cambios que veréis a continuación podréis asignar un gráfico diferente a cada tipo de MSGBOX, e incluso diferentes gráficos dentro de cada MSGBOX(inception). Para esta demostración añadiremos la separación a aquellos del tipo MSGBOX_SIGN, pero el mismo procedimiento puede ser usado para crear otras. Para que sepáis donde coloco las cosas en cada archivo, en cada bloque de código colocaré algunas de las lineas anteriores. Empecemos.
¡Ahora ya tenemos todo necesario para modificar los comandos de scripting!
¡Ya está todo implementado! Solo quedan dos cosas por hacer: definir los tipos y los subtipos de MSGBOX y hacer que se carguen las t-boxes que queramos para cada uno. Sin más dilación, continuemos.
Whew, ¡eso es todo! Quién podría imaginar que un tutorial sobre separar text boxes se podría alargar tanto. Bueno, espero que os sea útil, estos días subiré una repo a Github con los cambios para que no os tiréis la vida copiando esto.
~Disturbo
Como dice el título, haciendo los cambios que veréis a continuación podréis asignar un gráfico diferente a cada tipo de MSGBOX, e incluso diferentes gráficos dentro de cada MSGBOX
Lo primero es añadir la nueva t-box. Para ello, vamos a intentar mantener el formato que utiliza pokeemerald.
Las declaraciones de variables relacionadas con las t-boxes se realizan en "graphics/text_window/". En nuestro, y a forma de prueba, cargaremos el gráfico "sign_message_box.png"
Ahora debemos añadir las referencias a la imagen y la paleta (la cual debe ser de máximo 16 colores). En include/graphics.h:
Y en src/graphics.c:
También debemos indicar que queremos cargar esta paleta como parte de las que se muestran en mensajes, asique vamos a src/text_window.c:
Para cada nueva t-box debemos repetir este proceso.
Las declaraciones de variables relacionadas con las t-boxes se realizan en "graphics/text_window/". En nuestro, y a forma de prueba, cargaremos el gráfico "sign_message_box.png"
Ahora debemos añadir las referencias a la imagen y la paleta (la cual debe ser de máximo 16 colores). En include/graphics.h:
C:
extern const u32 gMessageBox_Gfx[];
extern const u16 gMessageBox_Pal[];
//Debemos insertar estas dos líneas
extern const u32 gSignMessageBox_Gfx[];
extern const u16 gSignMessageBox_Pal[];
C:
const u16 gMessageBox_Pal[] = INCBIN_U16("graphics/text_window/message_box.gbapal");
const u8 gMessageBox_Gfx[] = INCBIN_U8("graphics/text_window/message_box.4bpp");
//Debemos insertar estas dos líneas
const u16 gSignMessageBox_Pal[] = INCBIN_U16("graphics/text_window/sign_message_box.gbapal");
const u8 gSignMessageBox_Gfx[] = INCBIN_U8("graphics/text_window/sign_message_box.4bpp");
C:
//Simplemente copiamos y pegamos lo que asignamos a "gSignMessageBox_Pal" dentro del método
static const u16 sUnknown_0851017C[][16] =
{
INCBIN_U16("graphics/text_window/message_box.gbapal"),
INCBIN_U16("graphics/text_window/sign_message_box.gbapal"), //Tal que así
INCBIN_U16("graphics/text_window/text_pal1.gbapal"),
INCBIN_U16("graphics/text_window/text_pal2.gbapal"),
INCBIN_U16("graphics/text_window/text_pal3.gbapal"),
INCBIN_U16("graphics/text_window/text_pal4.gbapal")
};
Lo primero que necesitamos hacer es ir a sym_ewram.txt y añadir la siguiente línea al final:
Necesitamos tener una variable que almacene el tipo de t-box que estamos usando y otra para el subtipo. De nuevo, las añadiremos en src/text_window.c:
Ahora tenemos que añadir los métodos que nos permitirán por un lado asignar valores a ellas y cargar la t-box corresponiente. Para ello, al final de src/text_window.c:
Y para poder acceder a ellos desde otros archivos, vamos a include/text_window.h y al final añadimos:
Código:
.include "src/text_window.o"
C:
//Bajo todos los includes
static EWRAM_DATA u8 sMsgBoxType = MSGBOX_DEFAULT;
static EWRAM_DATA u8 sMsgBoxSubType = MSGBOX_SUB_DEFAULT;
//Aquí también añadiremos la siguiente línea, que nos será útil más adelante
void LoadMsgBox(const u32 **graphic, const u16 **palette);
C:
//MBSP
//Set the current type and subtype of t-box
void SetMsgBox(u8 type, u8 subtype)
{
sMsgBoxType = type;
sMsgBoxSubType = subtype;
}
//Load t-box graphics depending on type and subtype
//Este método es un ejemplo. De lo que pongáis aquí depende que aspecto tendrá cada tipo y subtipo de t-box
void LoadMsgBox(const u32 **graphic, const u16 **palette)
{
switch(sMsgBoxType)
{
case 3: //Los carteles tienen asociado el 3. Esto será explicado más adelante en el tutorial.
if(sMsgBoxSubType == 1)
{
*graphic = gSignMessageBox_Gfx;
*palette = gSignMessageBox_Pal;
}
else
{
*graphic = gMessageBox_Gfx;
*palette = gMessageBox_Pal;
}
break;
default:
*graphic = gMessageBox_Gfx;
*palette = gMessageBox_Pal;
break;
}
//Esto reestablece el tipo y subtipo cargados a los default.
SetMsgBox(4, 0);
}
C:
//MBSP
void SetMsgBox(u8 type, u8 subtype);
void LoadMsgBox(const u32 **graphic, const u16 **palette);
Ya hemos implementado la diferenciación de t-boxes, pero ahora tenemos que hacer que se tenga en cuenta al cargar los gráficos. Para ello vamos a src/text_window.c:
C:
//Tenemos que reemplazar
void LoadMessageBoxGfx(u8 windowId, u16 destOffset, u8 palOffset)
{
LoadBgTiles(GetWindowAttribute(windowId, WINDOW_BG), gMessageBox_Gfx, 0x1C0, destOffset);
LoadPalette(gMessageBox_Pal, palOffset, 0x20);
}
//con lo siguiente
void LoadMessageBoxGfx(u8 windowId, u16 destOffset, u8 palOffset)
{
//MBSP
//Call LoadMsgBox to loas proper graphics and palette
const u32 *currentMessageBox_Gfx;
const u16 *currentMessageBox_Pal;
LoadMsgBox(¤tMessageBox_Gfx, ¤tMessageBox_Pal);
LoadBgTiles(GetWindowAttribute(windowId, WINDOW_BG), currentMessageBox_Gfx, 0x1C0, destOffset);
LoadPalette(currentMessageBox_Pal, palOffset, 0x20);
}
¡Ahora ya tenemos todo necesario para modificar los comandos de scripting!
En lugar de hardcodear todo, vamos a optar por modificar los métodos habituales de carga de t-boxes. Para ello, primero vamos a ams/macros/event.inc.
Aquí vamos a hacer dos cambios, que podéis ver en el siguiente bloque de código:
Con estos cambios añadimos definimos los nuevos argumentos que necesitan los comandos "msgbox" y "setmessagebox" para funcionar.
Tranquilos, no hace falta editar todos los "msgbox" del juego ya que el último valor es opcional, por lo que su estructura pasaría a ser una de estas tres (¡las tres son válidas!):
Aquí vamos a hacer dos cambios, que podéis ver en el siguiente bloque de código:
C:
//Buscamos "msgbox" y reemplazamos esto
.macro msgbox text:req, type=MSGBOX_DEFAULT
loadword 0, \text
callstd \type
.endm
//Con esto
MSGBOX_SUB_DEFAULT = 0
.macro msgbox text:req, type=MSGBOX_DEFAULT, subtype=MSGBOX_SUB_DEFAULT
loadword 0, \text
loadbyte 1, \type
loadbyte 2, \subtype
callstd \type
.endm
//Más atrás, buscamos "signmsg" y reemplazamos esto
.macro signmsg
//Con esto
.macro setmessagebox type:req, subtype:req
Tranquilos, no hace falta editar todos los "msgbox" del juego ya que el último valor es opcional, por lo que su estructura pasaría a ser una de estas tres (¡las tres son válidas!):
Código:
msgbox <texto>
msgbox <texto>, <tipo de text box>
msgbox <texto>, <tipo de text box>, <subtipo de text box>
Nos dirigimos a data/script_cmd_table.inc y hacemos este cambio:
C:
//Cambiamos este bloque:
.4byte ScrCmd_bufferboxname
.4byte ScrCmd_nop1
.4byte ScrCmd_nop1
.4byte ScrCmd_nop1
.4byte ScrCmd_nop1
.4byte ScrCmd_nop1
.4byte ScrCmd_nop1
.4byte ScrCmd_setmonobedient
//Por el siguiente:
.4byte ScrCmd_bufferboxname
.4byte ScrCmd_nop1
.4byte ScrCmd_nop1
.4byte ScrCmd_nop1
.4byte ScrCmd_setmessagebox
.4byte ScrCmd_nop1
.4byte ScrCmd_nop1
.4byte ScrCmd_setmonobedient
El juego ya reconoce el comando, ¡pero de poco nos sirve si no hace nada!
Para hacer que haga algo, nos dirigimos a src/scrcmd.c y añadimos esto en cualquier parte del archivo:
Para hacer que haga algo, nos dirigimos a src/scrcmd.c y añadimos esto en cualquier parte del archivo:
C:
bool8 ScrCmd_setmessagebox(struct ScriptContext * ctx)
{
u8 type = ctx->data[1];
u8 subtype = ctx->data[2];
SetMsgBox(type, subtype);
return FALSE;
}
Ahora queremos que el juego sepa, a partir del comando que ponemos en el script, el tipo y el subtipo del cartel. Para ello vamos a data/scripts/std_msgbox.inc.
En este archivo tenemos que reemplazar lo siguiente:
Esta parte es la que hace que cada vez que nosotros llamemos a msgbox en un script se establezca el tipo y el subtipo de cartel
Aprovecho para decir que hay text boxes, como la de los entrenadores cuando te retan, que no se cargan usando el comando msgbox. Para que esto funcione en esos casos, hay que buscar el método llamado (habitualmente ShowFieldMessage) y añadir ANTES del método un SetMsgBox(type, sybtype) con los tipos deseados. Cabe también destacar que se debe hacer "#include script.h" en caso de que el archivo donde esté el ShowFieldMessage no lo incluya ya.
En este archivo tenemos que reemplazar lo siguiente:
C:
//Para cada aparición de
message 0x0
//Debemos reemplazarla con lo siguiente
setmessagebox 0x1 0x2
message 0x0
Aprovecho para decir que hay text boxes, como la de los entrenadores cuando te retan, que no se cargan usando el comando msgbox. Para que esto funcione en esos casos, hay que buscar el método llamado (habitualmente ShowFieldMessage) y añadir ANTES del método un SetMsgBox(type, sybtype) con los tipos deseados. Cabe también destacar que se debe hacer "#include script.h" en caso de que el archivo donde esté el ShowFieldMessage no lo incluya ya.
¡Ya está todo implementado! Solo quedan dos cosas por hacer: definir los tipos y los subtipos de MSGBOX y hacer que se carguen las t-boxes que queramos para cada uno. Sin más dilación, continuemos.
Como hemos visto antes en el spoiler "Implementar el método que marcará el tipo de t-box a usar", usamos números para identificar los tipos de t-box. Resulta muy incómodo trabajar con valores numéricos que para el ojo del público no tienen ningún sentido, por lo que vamos a emplear "defines" y otros métodos similares.
Hay dos lugares en los que debemos definir los valores a un número, empecemos con el más sencillo, scr/text_window.c.
Por un lado necesitamos definir los tipos de t-boxes, que ya vienen dados por el juego. Debemos poner bajo los "#include":
Pero como también queremos trabajar con nombres y no con números, añadimos también los subtipos:
Añadimos un define por cada subtipo que queramos.
Desafortunadamente esos no son todos los sitios donde debemos añadir cosas. Ahora volvemos a ams/macros/event.inc.
Aquí tenemos que añadir los subtipos que acabamos de definir. Para ello, buscamos "msgbox" y encontramos el siguiente código y colocamos las definiciones:
Igual que antes, añadimos uno por cada línea que queramos. Estos son muy importantes pues son los que el juego utiliza para entender el comando "msgbox"
Hay dos lugares en los que debemos definir los valores a un número, empecemos con el más sencillo, scr/text_window.c.
Por un lado necesitamos definir los tipos de t-boxes, que ya vienen dados por el juego. Debemos poner bajo los "#include":
C:
#define MSGBOX_NPC 2
#define MSGBOX_SIGN 3
#define MSGBOX_DEFAULT 4
#define MSGBOX_YESNO 5
#define MSGBOX_AUTOCLOSE 6
#define MSGBOX_GETPOINTS 9
C:
#define MSGBOX_SUB_DEFAULT 0
#define MSGBOX_SUB_COOLSIGN 1
Desafortunadamente esos no son todos los sitios donde debemos añadir cosas. Ahora volvemos a ams/macros/event.inc.
Aquí tenemos que añadir los subtipos que acabamos de definir. Para ello, buscamos "msgbox" y encontramos el siguiente código y colocamos las definiciones:
C:
@ Message box types
MSGBOX_NPC = 2
MSGBOX_SIGN = 3
MSGBOX_DEFAULT = 4
MSGBOX_YESNO = 5
MSGBOX_AUTOCLOSE = 6
MSGBOX_GETPOINTS = 9
YES = 1
NO = 0
//Esto es lo que añadimos
MSGBOX_SUB_DEFAULT = 0
MSGBOX_SUB_COOLSIGN = 1
.macro msgbox text:req, type=MSGBOX_DEFAULT, subtype=MSGBOX_SUB_DEFAULT
Venga, que ya es casi lo último. Solo queda personalizar el método "LoadMsgBox" en src/script.c para acomodar tus tipos y subtipos. Recordemos la del ejemplo:
Podemos distinguir tres partes:
Pero bueno, ahora que hemos definido las cosas por nombre, vamos a ponerlo bonito:
¿Ahora mejor? Bien. Si quisiesemos dar una textbox diferente a otro tipo de MSGBOX, bastaría con añadir un caso para esta. Igual sucede con los subtipos, como se puede ver este ejemplo.
Aquí creamos una nueva separación para el tipo de MSGBOX NPC sin subtipos, y un nuevo subtipo para MSGBOX_SIGN.
C:
void LoadMsgBox(const u32 **graphic, const u16 **palette)
{
switch(sMsgBoxType)
{
case 3:
if(sMsgBoxSubType == 1)
{
*graphic = gSignMessageBox_Gfx;
*palette = gSignMessageBox_Pal;
}
else
{
*graphic = gMessageBox_Gfx;
*palette = gMessageBox_Pal;
}
default:
*graphic = gMessageBox_Gfx;
*palette = gMessageBox_Pal;
break;
}
SetMsgBox(4, 0);
}
- El switch: Cargar el caso correspondiente a sMsgBoxType.
- Los ifs de cada caso: También podrían ser un switch, y cargan el gráfico y paleta correspondiente.
- El default: Si no hay ningún casó con el valor de sMsgBoxType, esto se "activa"
Pero bueno, ahora que hemos definido las cosas por nombre, vamos a ponerlo bonito:
C:
void LoadMsgBox(const u32 **graphic, const u16 **palette)
{
switch(sMsgBoxType)
{
case MSGBOX_SIGN:
if(sMsgBoxSubType == MSGBOX_SUB_COOLSIGN)
{
*graphic = gSignMessageBox_Gfx;
*palette = gSignMessageBox_Pal;
}
else
{
*graphic = gMessageBox_Gfx;
*palette = gMessageBox_Pal;
}
break;
default:
*graphic = gMessageBox_Gfx;
*palette = gMessageBox_Pal;
break;
}
SetMsgBox(MSGBOX_DEFAULT, MSGBOX_SUB_DEFAULT);
}
C:
//Suponiendo que hemos cargado los gráficos y paletas gNPCMessageBox y gWoodSignMessageBox
//Suponiendo también que creamos el define para el subtipo MSGBOX_SUB_WOODSIGN en los dos lugares requeridos
void LoadMsgBox(const u32 **graphic, const u16 **palette)
{
switch(sMsgBoxType)
{
case MSGBOX_SIGN:
if(sMsgBoxSubType == MSGBOX_SUB_COOLSIGN)
{
*graphic = gSignMessageBox_Gfx;
*palette = gSignMessageBox_Pal;
}
else if(sMsgBoxSubType == MSGBOX_SUB_WOODSIGN)
{
*graphic = gWoodSignMessageBox_Gfx;
*palette = gWoodSignMessageBox_Pal;
}
else
{
*graphic = gMessageBox_Gfx;
*palette = gMessageBox_Pal;
}
break;
case MSGBOX_NPC:
*graphic = gNPCMessageBox_Gfx;
*palette = gNPCMessageBox_Pal;
break;
default:
*graphic = gMessageBox_Gfx;
*palette = gMessageBox_Pal;
break;
}
SetMsgBox(MSGBOX_DEFAULT, MSGBOX_SUB_DEFAULT);
}
Esta sección será la más breve, puesto que cambian muy poquitas cosas. Tenemos dos opciones:
- msgbox <texto>, <tipo>: Si el tipo de MSGBOX no tiene subtipos, utiliza este
- msgbox <texto>, <tipo>, <subtipo>: Si el tipo de MSGBOX tiene subtipos, utiliza este, indicando el subtipo al dinal
Whew, ¡eso es todo! Quién podría imaginar que un tutorial sobre separar text boxes se podría alargar tanto. Bueno, espero que os sea útil, estos días subiré una repo a Github con los cambios para que no os tiréis la vida copiando esto.
~Disturbo
Última edición: