Acimut
Profesional de WaH
INTRODUCCIÓN innecesariamente larga
¡Que tal! ¿Me recuerdan?
Soy el quería publicar el EV-IV Display Screen de @jiangzhengwenjz para rojo fuego en español, pero el autor ha borrado el código de los repositorios de GitHub, así que luego de hablar con él también desistí de publicar su código.
Y, ¿qué pasa con esto?
Pues bien, una de las razones por las que me uní a esta comunidad fue por este recurso, me cautivó por completo. Quiero decir, he visto mods en otros juegos, así como modificaciones de mecánicas y mejoras gráficas; pero esto era algo totalmente nuevo dentro de este juego y me pareció increíble lo que podía lograr esta comunidad (
Fue así, que no tuve más remedio que estudiar un poco más su código y empezar el mío desde cero. Bueno, no desde cero luego de entender cómo funciona.
Resulta, que @jiangzhengwenjz se basó en el código de `Diploma.c` de `\pokefirered\src\` de ya hace un tiempo considerable, por lo que tuvo que arreglárselas con lo que tenía, y una de esas cosas era DevkitARM r45, incompatible con todo lo actualizado de hoy, como inyecciones en C y proyectos de decompilación.
Y ¿por qué el código de Diploma.c?
Para hacer algo coomo el EV-IV Display Screen anterior, es necesario salir del Overworld y mostrar otra pantalla que imprima los EV-IV de nuestro equipo Pokémon. Así que el mejor ejemplo que ya tenemos en el código del juego es el diploma, de hecho, buscando entre el código de Pokefirered, es el más corto para mostrar otra pantalla desde el OW e imprimir texto, con menos de 300 líneas de código.
Así que, tomando una versión más actualizada del código de `Diploma.c` de `\pokefirered\src`, me he tomado la molestia de traerles un nuevo EV-IV Display Screen, donde además de poder ver los EV – IV de tu equipo Pokémon, podrás modificarlo para que se adapte de acuerdo tus necesidades y capacidades.
Y de esto va este tutorial, más que inyectar el código a tu rom, va dirigido a cómo modificar el código para que obtengas los resultados que quieres (
Así que empecemos con ello, pero primero…
DEFINICIÓN DE TÉRMINOS:
- EV: valores de esfuerzo. Un Pokémon puede ganar puntos de esfuerzo mediante combates, ítems o cheats, desde 0 hasta un total de 255 EV en cada estadística (ps, ataque, defensa, atq.esp., def.esp. y velocidad), y la suma de los EV en todas las estadísticas no puede superar 512, a menos que uses cheats o modifiques la rom.
- IV: valores iniciales. Son los valores con los que nace un Pokémon, del 0 al 31, y NO se
puedendeben cambiar a menos que quieras un Pokémon ilegal. Estos valores determinan qué tan fuerte es tu Pokémon, así como decidir el tipo y fuerza del movimiento “poder oculto”. - BS: estadísticas base. Son los parámetros individuales de cada especie, donde se define su fortaleza. Los IV, EV, así como la personalidad juegan un papel importante aquí, ya que de acuerdo a estos varían los valores cada estadística, haciendo que Pokémon de la misma especie y nivel tengan estadísticas distintas.
- Resumen: Entre más altos sean los IV, EV y los BS de tu Pokémon, más fuerte es.
CARACTERÍSTICAS:
- Base compatible: Pokémon Fire Red 1.0 y Pokémon Rojo Fuego
- Además de los EV-IV, muestra las estadísticas base del Pokémon.
- El IV, así como el nombre de la estadística cambia de color de acuerdo a la naturaleza del Pokémon, negro si es neutro, rojo si se beneficia y azul si se perjudica.
- Muestra el Sprite del Pokémon
- También suena su grito.
- Censura la información de estadísticas de los huevos.
- Puedes cambiar el background por el fondo que quieras para tu EV-IV, la inyección la insertará automáticamente.
- Puedes cambiar las coordenadas para que se ajuste a tus gustos.
- Suma el total de las estadísticas.
- En los huevos, muestra aprox. cuántos pasos falta para la eclosión del huevo.
- Es posible portearlo a tu proyecto de decomp sin muchas complicaciones (
o eso supongo, y mientras no sea de pokeruby mejor). Pueden ver el port de Lunos a pokeemerald dando click aquí. - Huele a limón.
INSTRUCCIONES DE USO
Para inyectar el código en tu rom es necesario tener:
- Ganas y paciencia.
- Conocimientos intermedios o avanzados sobre el rom hacking (no recomendado para novatos).
- Instalado devkitARM, cualquier versión reciente sirve.
- Instalado MAKE, cualquier versión reciente sirve.
- Instalado ArmIPS, cualquier versión reciente sirve.
- Instalado pret-tools (en seguida explico cómo).
- Más paciencia.
- Una rom de pokémon fire red o rojo fuego.
- Clonado o descargado el siguiente repositorio: https://github.com/Acimut/Custom-EV-IV-Display-Screen
Además de tener instalado devkitarm y armips, ahora será necesario tener instalado las “tools” que se usan para los proyectos de decompilación de PRET, en las inyecciones que estoy realizando.
En Windows:
1. Descargar el archivo rar con los binarios compilados desde aquí.
2. Descomprimir este archivo preferiblemente en la raíz del disco del sistema, en mi caso C:\pret-tools
3. Desde cmd o ejecutar (tecla Windows + R) escriben SystemPropertiesAdvanced y dan enter.
4. Dan clic en el botón “variables de entorno…”
5. Abajo en el cuadro Variables del sistema buscamos la variable Path, le damos editar.
6. Le damos a nuevo, pegamos la ruta donde descomprimieron los archivos, en este caso C:\pret-tools y le dan a aceptar, aceptar, y aceptar.
7. Comprobar que todo salió bien, abren cmd y escriben gbagfx y dan enter, debería salir algo como:
Estos 2 binarios son los que vamos a usar en esta inyección, así que asegúrate de que funciona.
Pueden ver en el siguiente gif cómo se configura en Windows 10:
Si usas Linux, puedes compilar los binarios usando el código fuente: https://github.com/pret/pokefirered/tree/master/tools
En Windows:
1. Descargar el archivo rar con los binarios compilados desde aquí.
Aclaro que estos binarios los he compilado en Windows 10 para sistemas de 64 bits.
2. Descomprimir este archivo preferiblemente en la raíz del disco del sistema, en mi caso C:\pret-tools
3. Desde cmd o ejecutar (tecla Windows + R) escriben SystemPropertiesAdvanced y dan enter.
4. Dan clic en el botón “variables de entorno…”
5. Abajo en el cuadro Variables del sistema buscamos la variable Path, le damos editar.
6. Le damos a nuevo, pegamos la ruta donde descomprimieron los archivos, en este caso C:\pret-tools y le dan a aceptar, aceptar, y aceptar.
7. Comprobar que todo salió bien, abren cmd y escriben gbagfx y dan enter, debería salir algo como:
Usage: gbagfx INPUT_PATH OUTPUT_PATH [options…]
también pueden probar escribiendo preproc y dando enter, debería salir algo como:
Usage: preproc SRC_FILE CHARMAP_FILE
Estos 2 binarios son los que vamos a usar en esta inyección, así que asegúrate de que funciona.
Pueden ver en el siguiente gif cómo se configura en Windows 10:
Si usas Linux, puedes compilar los binarios usando el código fuente: https://github.com/pret/pokefirered/tree/master/tools
Lo siguiente es modificar el archivo makefile para cambiar el offset y la rom con la que vamos a trabajar. Buscamos las siguientes líneas:
Ya no es necesario cambiar el offset de gBaseStats.
Ponen su rom con el nombre BPRE0.gba (BPRS0.gba para rojo fuego), dentro de la carpeta raíz del proyecto.
Luego sólo es abrir la consola o terminal de turno que utilizan dentro de la carpeta del repositorio, escribir make y darle enter. Se genera una nueva carpeta llamada “build” donde se encontrará un archivo “rom.gba” (“romEsp.gba” en caso de Rojo Fuego) que tendrá el código compilado. Pueden abrir el archivo “offset.txt” y comprobar que el offset de “maineviv” corresponde a donde insertaron el código.
Para probar si funciona, en un script ponen callasm offset+1 donde insertaron el código.
export ROM_CODE := BPRE
export INSERT_INTO := 0x08f90000
- Cambiamos f90000 por un offset alienado con suficiente espacio libre (más de 0x2000 bytes libres) se puede insertar en espacio expandido de la rom cambiando el 0x08 por 0x09 al inicio del offset.
- Cambiamos BPRE por BPRS si vas a compilar en una rom rojo fuego española.
Ponen su rom con el nombre BPRE0.gba (BPRS0.gba para rojo fuego), dentro de la carpeta raíz del proyecto.
Luego sólo es abrir la consola o terminal de turno que utilizan dentro de la carpeta del repositorio, escribir make y darle enter. Se genera una nueva carpeta llamada “build” donde se encontrará un archivo “rom.gba” (“romEsp.gba” en caso de Rojo Fuego) que tendrá el código compilado. Pueden abrir el archivo “offset.txt” y comprobar que el offset de “maineviv” corresponde a donde insertaron el código.
Para probar si funciona, en un script ponen callasm offset+1 donde insertaron el código.
Código:
#dynamic 0x800000
#org @evivEjemplo
callasm 0x8F90001
end
MODIFICACIONES
Para el background deben usar uno de 256x256 píxeles, obtener el tileset en formato .png a 16 colores (o menos), y el tilemap (raw) en formato .bin
Se pueden guiar del diseño de @ACE10 para hacer sus propios bg:
Pueden usar el background por defecto que hizo @ACE10 (no olviden dar créditos).
Para generar un tileset y un tilemap, recomiendo el uso de tilemapstudio y gale, pueden descargarlos desde sus páginas oficiales:
También pueden guiarse del siguiente vídeo, en donde uso tilemapstudio y gale para crear el tileset y tilemap (recomiendo verlo en 720p o 1080p):
Para cambiar el background, sólo tienes que reemplazar el que viene por defecto en la carpeta “src/graphics”; reemplazas el bgEvIv.png por tu tileset, y bgEvIv.bin por tu tilemap, la inyección insertará automáticamente el tileset, tilemap y paleta en espacio libre justo después del código.
Se pueden guiar del diseño de @ACE10 para hacer sus propios bg:
Pueden usar el background por defecto que hizo @ACE10 (no olviden dar créditos).
Para generar un tileset y un tilemap, recomiendo el uso de tilemapstudio y gale, pueden descargarlos desde sus páginas oficiales:
También pueden guiarse del siguiente vídeo, en donde uso tilemapstudio y gale para crear el tileset y tilemap (recomiendo verlo en 720p o 1080p):
Para cambiar el background, sólo tienes que reemplazar el que viene por defecto en la carpeta “src/graphics”; reemplazas el bgEvIv.png por tu tileset, y bgEvIv.bin por tu tilemap, la inyección insertará automáticamente el tileset, tilemap y paleta en espacio libre justo después del código.
Las ventanas son espacios rectangulares que el juego usa para múltiples propósitos, el más importante es imprimir texto en pantalla, seguido de mostrar íconos como los tipos y estados.
Las ventanas están asociadas a una paleta, de la cual se tomará los colores para los textos y otros usos, así como para los íconos que aparezcan en ella. Esto explica por qué cuando cambias la paleta de los tipos cambia el color del texto, pues porque comparten la misma ventana y por ende la misma paleta.
En esta inyección de código las ventanas usan la paleta 15. Para que se hagan una idea más simple, en las siguientes imágenes pueden ver las ventanas que uso en el código:
Las ventanas de esta inyección se usan en el bg 0, así que junto con el bg 1 (donde tenemos nuestro background personalizado) y el sprite del pokémon tenemos esto:
El tamaño de la ventana la podemos configurar fácilmente a través de unos #defines en el archivo TaskEvIvInit.c, ya que de esta manera no tendrán que calcular el baseblock, y así no tendrán bugs visuales. El tamaño de estos lo mediremos en tiles de 8x8 píxeles, por lo que si ponemos 10 de ancho y 10 de alto, tendremos una ventana de 80x80 píxeles. Así, si queremos cambiar el tamaño de la ventana 1, buscamos las siguientes líneas y cambiamos los números que tienen delante de ellas (Aplica para todas las ventanas de esta inyección).
El código se ve algo así:
Para cambiar las coordenadas de las ventanas nos meteremos con la estructura de WindowTemplate, pero sólo con las siguientes líneas y deben corresponder a la ventana que quieren mover:
Esto tampoco tiene misterio, tilemapLeft indica a qué distancia se encuentra del borde izquierdo del bg0 (eje x), y tilemapTop a qué distancia del borde superior del bg0 (eje y), también medidos en tiles de 8x8 píxeles.
Las ventanas están asociadas a una paleta, de la cual se tomará los colores para los textos y otros usos, así como para los íconos que aparezcan en ella. Esto explica por qué cuando cambias la paleta de los tipos cambia el color del texto, pues porque comparten la misma ventana y por ende la misma paleta.
En esta inyección de código las ventanas usan la paleta 15. Para que se hagan una idea más simple, en las siguientes imágenes pueden ver las ventanas que uso en el código:
Las ventanas de esta inyección se usan en el bg 0, así que junto con el bg 1 (donde tenemos nuestro background personalizado) y el sprite del pokémon tenemos esto:
El tamaño de la ventana la podemos configurar fácilmente a través de unos #defines en el archivo TaskEvIvInit.c, ya que de esta manera no tendrán que calcular el baseblock, y así no tendrán bugs visuales. El tamaño de estos lo mediremos en tiles de 8x8 píxeles, por lo que si ponemos 10 de ancho y 10 de alto, tendremos una ventana de 80x80 píxeles. Así, si queremos cambiar el tamaño de la ventana 1, buscamos las siguientes líneas y cambiamos los números que tienen delante de ellas (Aplica para todas las ventanas de esta inyección).
C:
#define VENTANA1_ANCHO 17
#define VENTANA1_ALTO 11
Para cambiar las coordenadas de las ventanas nos meteremos con la estructura de WindowTemplate, pero sólo con las siguientes líneas y deben corresponder a la ventana que quieren mover:
Código:
.tilemapLeft = X,
.tilemapTop = X,
Puedes cambiar el texto que aparece simplemente abriendo el archivo Textos.c que se encuentra en la carpeta src, y modificando el texto que aparece entre comillas. Tened en cuenta los espacios usados.
Recordar que los colores del texto están sujetos a la paleta de la ventana, en este caso la paleta 15.
Para cambiar las coordenadas de los textos dentro de las ventanas, primero nos situamos en el archivo TaskEvIvInit.c
La función encargada de poner el texto en la ventana es AddTextPrinterParameterized3
Esta función es de tipo void (no retorna ningún tipo de dato), pero eso no les interesa a ustedes :v lo que quieren saber son los parámetros de entrada que recibe esta función (los que están dentro de paréntesis, separados por una coma), pues con ella controlamos la siguiente información:
Ahora veamos un ejemplo, este es la línea que imprime el nickname del Pokémon en la parte superior, encima del sprite:
Como pueden ver, a la función se le da los siguientes parámetros: Imprima en la ventana 0, tamaño de fuente 2, 0x90 píxeles desde el borde izquierdo de la ventana 0, 2 píxeles desde el borde superior de la ventana 0, color de texto gris (o lo que tenga ahí), velocidad 0, y por último, el texto que está en gStringVar4. Si quieres cambiar las coordenadas donde aparece el texto dentro de la ventana o el color, lo haces desde ahí.
¿por qué gStringVar4? Bueno, como tiene suficiente espacio lo usamos como búfer para acomodar nuestros textos de manera temporal.
El truco que usé para cambiar el color del texto para las naturalezas, fue metiéndole un código que el juego interpreta como cambio de color, que son los bytes FC 04 seguidos de 3 bytes con la configuración de color fuente, fondo, sombra; es llamado a través de una función StringAppend, donde se concatena el texto de gStringVar4 con los bytes del cambio de color, ya sea rojo (gColorRojo) si beneficia o azul (gColorAzul) por si perjudica el stat, así el texto que le sigue será de ese color. Tener en cuenta que este cambio de color sólo lo estamos aplicando en la ventana 1, donde se imprimen los stats.
Luego de pintar el color del stat beneficiado o perjudicado, es necesario volver al color que definido en AddTextPrinterParameterized3, por lo que se usa un color más: gColorDefecto. Este debería ser igual al color usado por AddTextPrinterParameterized3, que para la ventana 1 estoy usando gColorTextoNegro, por lo que si van a cambiar este deberían cambiar el de gColorDefecto. Podemos ver en seguida que el color de fuente, fondo y sombra, son el mismo en ambos:
Recordar que los colores del texto están sujetos a la paleta de la ventana, en este caso la paleta 15.
Para cambiar las coordenadas de los textos dentro de las ventanas, primero nos situamos en el archivo TaskEvIvInit.c
La función encargada de poner el texto en la ventana es AddTextPrinterParameterized3
C:
void AddTextPrinterParameterized3(u8 windowId, u8 fontId, u8 x, u8 y, const u8 * color, s8 speed, const u8 * str);
- windowId: número de la ventana a la que le enviaremos el texto.
- fontId: tamaño del texto, nosotros usaremos el 2 para que se vea grande xd.
- x: coordenada x, distancia en píxeles en el eje x (de izquierda a derecha) dentro de la ventana, repito, dentro de la ventana, donde empezará a imprimir el texto.
- y: coordenada y, distancia en píxeles en el eje y (de arriba abajo) dentro de la ventana, donde empezará a imprimir el texto.
- color: puntero a un arreglo con el número del color del fondo, de la fuente y de la sombra, en ese orden, tomados de la paleta de la ventana. El texto tendrá esta configuración de color, pero puede ser cambiada dentro del texto.
- speed: velocidad del texto. Dejarlo en 0, no es necesario cambiarlo.
- str: puntero al texto que vamos a imprimir en la ventana.
Ahora veamos un ejemplo, este es la línea que imprime el nickname del Pokémon en la parte superior, encima del sprite:
C:
AddTextPrinterParameterized3(0, 0x2, 0x90, 0x2, gColorTextoGris, 0, gStringVar4);
¿por qué gStringVar4? Bueno, como tiene suficiente espacio lo usamos como búfer para acomodar nuestros textos de manera temporal.
El truco que usé para cambiar el color del texto para las naturalezas, fue metiéndole un código que el juego interpreta como cambio de color, que son los bytes FC 04 seguidos de 3 bytes con la configuración de color fuente, fondo, sombra; es llamado a través de una función StringAppend, donde se concatena el texto de gStringVar4 con los bytes del cambio de color, ya sea rojo (gColorRojo) si beneficia o azul (gColorAzul) por si perjudica el stat, así el texto que le sigue será de ese color. Tener en cuenta que este cambio de color sólo lo estamos aplicando en la ventana 1, donde se imprimen los stats.
Luego de pintar el color del stat beneficiado o perjudicado, es necesario volver al color que definido en AddTextPrinterParameterized3, por lo que se usa un color más: gColorDefecto. Este debería ser igual al color usado por AddTextPrinterParameterized3, que para la ventana 1 estoy usando gColorTextoNegro, por lo que si van a cambiar este deberían cambiar el de gColorDefecto. Podemos ver en seguida que el color de fuente, fondo y sombra, son el mismo en ambos:
C:
// fondo fuente sombra
static const u8 gColorTextoNegro[3] = {TEXT_COLOR_TRANSPARENT, TEXT_COLOR_DARK_GREY, TEXT_COLOR_LIGHT_GREY};
// fuente fondo sombra 0xff
static const u8 gColorDefecto[] = {0xFC, 0x04, TEXT_COLOR_DARK_GREY, TEXT_COLOR_TRANSPARENT, TEXT_COLOR_LIGHT_GREY, EOS}; //negro
Cambiar las coordenadas del Sprite y la animación es lo más sencillo de todo, pues les he preparado unos #defines al inicio del código del archivo TaskEvIvInit.c, con los que podrán configurar el resultado que deseen, incluso dejé anotaciones así que no creo que sea necesarias más explicaciones:
C:
#define SPRITE_SALTO 1 //1 = ACTIVADO, 0 = DESACTIVADO, activa la animación de salto del sprite del pokémon.
#define SPRITE_DIRECCION_SALTO 1 //1 = DE DERECHA A IZQUIERDA, 0 = EN EL CENTRO -1 = DE IZQUIERDA A DERECHA, dirección a donde salta el pokémon.
#define SPRITE_DIRECCION_VISTA 0 //1 = A LA DERECHA, 0 = A LA IZQUIERDA, dirección a la que mira el sprite del pokémon.
#define PICMON_X 18 //coordenada x del sprite pokémon, se mide en tiles de x8 pixeles
#define PICMON_Y 5 //coordenada y del sprite pokémon, se mide en tiles de x8 pixeles
NOTAS Y ACLARACIONES
Si hay algo en lo que esté equivocado y deba corregir, hacérmelo saber. Somos personas y no somos perfectos.
Las definiciones de términos y la mayoría de lo que he escrito en este post representa mi opinión personal, y está sujeto a modificaciones y/o actualizaciones.
Los archivos de cabecera .h que se encuentran en “\src\include\” fueron tomados de una versión no actual de “pokefirered\include\”. Me ha dado ansiedad el reducir los archivos de cabecera para que sólo tengan información relevante del proyecto, así que solo copié y pegué los archivos que dependían unos de otros, excepto el archivo “\src\include\pokemon.h” que fue modificado para que sea compatible con las inyecciones.
La plantilla (archivos linker.ld, main.s y Makefile) para hacer la inyección C a la rom, la tomé de los repositorios de @Kaiser de Emperana te amo!
El código que he hecho está lleno de spanglihs :v y esto tiene explicación. Quien sepa que me gusta ir en contra de la corriente, se imaginará que, en mi primera clase de programación, mis compañeros de clase se reían al ver que no escribí el clásico “Hola mundo”, en vez de ello, aparecía un “Tengo hambre” en la consola. Además, como estoy trabajando sobre código que está escrito en inglés, se me facilita el nombrar funciones y variables en español, para no usar palabras reservadas y recordar rápidamente lo que he hecho desde que empecé el proyecto, no me queda mucho tiempo libre lamentablemente.
He puesto algunos comentarios luego de releer el código, en contra de la práctica de programación limpia, para que la parte que he hecho sea un poco más fácil de entender y modificar. Sin embargo, si tienen dudas sobre el código no duden en preguntar, apenas tenga tiempo con gusto les aclaro sus dudas.
Si quieres adaptar el código a tu proyecto de decomp, no más es preparar el background y cambiar algunas cosas del código para que se ajusten a tu proyecto. No he hecho la prueba aún, pero en teoría no debería haber problemas.
Ha sido probado personalmente sobre:
- Pokémon Fire Red 1.0
- armips assembler v0.11 [Source]
- devkitARM54r
- GNU Make 4.3
- GNU Make 4.2.1
- Cygwin64
- WSL (ubuntu 20.04)
- Windows 10 Pro
CRÉDITOS:
@ACE10 por los gráficos, ideas y sugerencias.
@Tohru
@Drakymino
@Tio_Terry por reportar bugs
@Frander_Pikapro por reportar bugs
@Kaiser de Emperana viendo tus tutos y parte de tu código me decidí a empezar también a hacer mis propias modificaciones.
@Samu por aclararme una duda sobre la estructura de la BgTemplate aquí.
@jiangzhengwenjz por ser la inspiración de este pequeño proyecto.
Al equipo de PRET, sin ellos no sería tan fácil esto. Enlace a pokefirered.
Última edición: