Registrarse

[pokeruby - pokeemerald] Invertir o bloquear movimiento mediante flag

Laquin

Usuario mítico
¡Hola!

En este tutorial aprenderemos, por una parte, a invertir el movimiento del personaje mientras camina, corre o va en bicicleta mediante una flag que podrá ser activada o desactivada en un script, como es común; y, por la otra, a bloquear sus movimientos, también mediante flags. De esta manera, podremos hacer que, al intentar moverse a la derecha, el jugador se vaya hacia la izquierda, y viceversa; o que, al intentar ir hacia arriba, directamente no se pueda.

Si bien es muy parecido para ambos, el tutorial lo haré por separado para pokeruby y pokeemerald, porque sí que existen algunas diferencias entre los dos. Cada uno, a su vez, estará dividido en cuatro partes:
  1. Creando la flag.
  2. Código para invertir el movimiento.
  3. Código para bloquear el movimiento.
  4. El código final, con todo incluido.
Copiar y pegar el código final es suficiente en la mayoría de los casos, aunque tendréis que tener cuidado, como explicaré, con el número de la flag, para que no se repita con una que tengáis en uso. Aun así, os recomiendo encarecidamente que leáis el tutorial completo, para entender lo que estáis haciendo poniendo ese código. La idea es aprender, y leyendo las explicaciones se aprende bastante más que haciendo un copiapega.

Primero, vamos a crear la flag. Las flags se definen en el archivo include/constants/flags.h. Como podréis observar, estas están ordenadas según su uso. Como ejemplos, las primeras 0x1F flags son flags temporales, o las que van de la 0x258 a la 0x2B9 se utilizan para los ítems ocultos.
Para lo que nosotros vamos a hacer, nos interesan los SYSTEM FLAGS, que están al final. El último system flag es FLAG_LANDMARK_BERRY_MASTERS_HOUSE, en la posición 0x863, por lo que nosotros crearemos uno a continuación, de la siguiente manera:
Código:
#define NombreDelFlag (SYSTEM_FLAGS + 0x64)
SYSTEM_FLAGS equivale a 0x800, que es donde empiezan este tipo de flags. Es lo mismo poner (SYSTEM_FLAGS + 0x64) que poner 0x864.
El nombre que yo he elegido para la flag es FLAG_INVERTED_MOVEMENT. Podéis poner el nombre que queráis, pero es recomendable que sea un nombre lo más descriptivo posible. Asimismo, es buena idea empezar el nombre con FLAG_, que es como empiezan todas las demás flags.
Si la posición (SYSTEM_FLAGS + 0x64) la tenéis ocupada, por ejemplo, porque añadisteis la flag para invocar Pokémon shinys, entonces podéis, sin problema, sustituir el 0x64 por 0x65. Lo que importa es que la flag que utilicemos esté libre.
Una vez hayamos creado la flag, haremos que, cuando el jugador pulse una tecla, el juego compruebe si la flag está activada o no, y, en caso de estarlo, que el movimiento sea invertido.
El código que nos interesa se encuentra en src/field_control_avatar.c, más concretamente en la línea 168:
Código:
if (heldKeys & DPAD_UP)
    input->dpadDirection = DIR_NORTH;
else if (heldKeys & DPAD_DOWN)
    input->dpadDirection = DIR_SOUTH;
else if (heldKeys & DPAD_LEFT)
    input->dpadDirection = DIR_WEST;
else if (heldKeys & DPAD_RIGHT)
    input->dpadDirection = DIR_EAST;
DIR_SOUTH, DIR_SOUTH, DIR_WEST y DIR_EAST indican valores de 1 al 4 respectivamente. Dependiendo del valor que le demos a input->dpadDirection, el jugador se moverá en una dirección o en otra. Entonces, le daremos unos valores u otros dependiendo de si la flag está activada o no:
(Ambos códigos son lo mismo, solo que el extendido está escrito en más líneas que el compacto).
Código:
if (heldKeys & DPAD_UP){
    if(FlagGet(FLAG_INVERTED_MOVEMENT)) input->dpadDirection = DIR_SOUTH;
    else input->dpadDirection = DIR_NORTH;
} else if (heldKeys & DPAD_DOWN){
    if(FlagGet(FLAG_INVERTED_MOVEMENT)) input->dpadDirection = DIR_NORTH;
    else input->dpadDirection = DIR_SOUTH;
} else if (heldKeys & DPAD_LEFT){
    if(FlagGet(FLAG_INVERTED_MOVEMENT)) input->dpadDirection = DIR_EAST;
    else input->dpadDirection = DIR_WEST;
} else if (heldKeys & DPAD_RIGHT){
    if(FlagGet(FLAG_INVERTED_MOVEMENT)) input->dpadDirection = DIR_WEST;
    else input->dpadDirection = DIR_EAST;
}
Código:
if (heldKeys & DPAD_UP){
    if(FlagGet(FLAG_INVERTED_MOVEMENT))
        input->dpadDirection = DIR_SOUTH;
    else
        input->dpadDirection = DIR_NORTH;
} else if (heldKeys & DPAD_DOWN){
    if(FlagGet(FLAG_INVERTED_MOVEMENT))
        input->dpadDirection = DIR_NORTH;
    else
        input->dpadDirection = DIR_SOUTH;
} else if (heldKeys & DPAD_LEFT){
    if(FlagGet(FLAG_INVERTED_MOVEMENT))
        input->dpadDirection = DIR_EAST;
    else
        input->dpadDirection = DIR_WEST;
} else if (heldKeys & DPAD_RIGHT){
    if(FlagGet(FLAG_INVERTED_MOVEMENT))
        input->dpadDirection = DIR_WEST;
    else
        input->dpadDirection = DIR_EAST;
}
FlagGet(nombreDeFlag) nos devuelve un valor de TRUE ('verdadero') o FALSE ('falso'); es decir, nos dice si la flag está activada o no. Si la flag es verdadera, o, lo que es lo mismo, si está activada, al pulsar el botón DPAD_UP (el botón de arriba), el jugador irá hacia DIR_SOUTH ('dirección sur'); si no está activada, entonces irá hacia DIR_NORTH ('dirección norte'). Sustituyendo el código original por este último, lo habremos hecho con todos los botones.
La lógica para esto es similar a la que utilizamos para la inversión del movimiento. Crearemos cuatro flags, una para cada dirección:
Código:
#define FLAG_BLOCKED_DOWN (SYSTEM_FLAGS + 0x64)
#define FLAG_BLOCKED_UP (SYSTEM_FLAGS + 0x65)
#define FLAG_BLOCKED_LEFT (SYSTEM_FLAGS + 0x66)
#define FLAG_BLOCKED_RIGHT (SYSTEM_FLAGS + 0x67)
Repito: Tanto los nombres como los números de la derecha pueden ser distintos, siempre y cuando las direcciones no estén siendo utilizados por otra flag y el nombre no esté en uso.
Una vez hemos creado las flags, las aplicaremos en el código del movimiento del jugador:
Código:
if (heldKeys & DPAD_UP && !FlagGet(FLAG_BLOCKED_UP))
    input->dpadDirection = DIR_NORTH;
else if (heldKeys & DPAD_DOWN && !FlagGet(FLAG_BLOCKED_DOWN))
    input->dpadDirection = DIR_SOUTH;
else if (heldKeys & DPAD_LEFT && !FlagGet(FLAG_BLOCKED_LEFT))
    input->dpadDirection = DIR_WEST;
else if (heldKeys & DPAD_RIGHT && !FlagGet(FLAG_BLOCKED_RIGHT))
    input->dpadDirection = DIR_EAST;
Nos interesan dos operadores nuevos:
  • && es una puerta lógica AND; es decir, si ambas condiciones se cumplen, entonces ocurrirá algo.
  • ! es una puerta lógica NOT; es decir, que si la flag está activada, entonces nos devolverá FALSE, y si está desactivada, nos devolverá TRUE —lo contrario, vamos—.
Con esto, podemos entender lo que hemos hecho en el código: Si pulsamos la tecla hacia arriba Y (&&) la flag del bloqueo NO (!) está activada, entonces el jugador se moverá para arriba. ¿Y si está activada? Entonces toda la condición será falsa, y el jugador no se va a mover.
Juntando ambos sistemas, nos quedaría este código:
(En include/constants/flags.h):
Código:
#define FLAG_INVERTED_MOVEMENT (SYSTEM_FLAGS + 0x64)
#define FLAG_BLOCKED_DOWN (SYSTEM_FLAGS + 0x65)
#define FLAG_BLOCKED_UP (SYSTEM_FLAGS + 0x66)
#define FLAG_BLOCKED_LEFT (SYSTEM_FLAGS + 0x67)
#define FLAG_BLOCKED_RIGHT (SYSTEM_FLAGS + 0x68)
(En src/field_control_avatar.c):
Código:
if (heldKeys & DPAD_UP && !FlagGet(FLAG_BLOCKED_UP)){
    if(FlagGet(FLAG_INVERTED_MOVEMENT))
        input->dpadDirection = DIR_SOUTH;
    else
        input->dpadDirection = DIR_NORTH;
} else if (heldKeys & DPAD_DOWN && !FlagGet(FLAG_BLOCKED_DOWN)){
    if(FlagGet(FLAG_INVERTED_MOVEMENT))
        input->dpadDirection = DIR_NORTH;
    else
        input->dpadDirection = DIR_SOUTH;
} else if (heldKeys & DPAD_LEFT && !FlagGet(FLAG_BLOCKED_LEFT)){
    if(FlagGet(FLAG_INVERTED_MOVEMENT))
        input->dpadDirection = DIR_EAST;
    else
        input->dpadDirection = DIR_WEST;
} else if (heldKeys & DPAD_RIGHT && !FlagGet(FLAG_BLOCKED_RIGHT)){
    if(FlagGet(FLAG_INVERTED_MOVEMENT))
        input->dpadDirection = DIR_WEST;
    else
        input->dpadDirection = DIR_EAST;
}
De esta manera, podremos invertir o bloquear independientemente cualquier movimiento del jugador, cuando nosotros queramos.
A diferencia de como es en pokeruby, en pokeemerald no crearemos la flag, sino que modificaremos una ya existente. Tranquilos, no vamos a perder ninguna que ya estuviera en uso. En su lugar, cogeremos las unused flags ('flags sin usar'). Como el nombre bien dice, estas flags no se usan en ninguna parte del juego; no sirven para nada así como están xD Por ende, les podemos cambiar el nombre, y utilizarlas para lo que a nosotros nos venga en gana.
Las flags están definidas en el archivo include/constants/flags.h. Como podréis observar, estas están ordenadas según su uso. Como ejemplos, las primeras 0x1F flags son flags temporales, o las que van de la 0x1E4 a la 0x1F3 se utilizan para los regalos misteriosos.
Para lo que nosotros vamos a hacer, nos interesan, como he dicho antes los Unused flags, que están en muchos sitios xD Pero cogeremos los del principio, para qué complicarnos. A partir de de 0x20 tenemos unas cuantas flags totalmente libres. Usaremos, por ejemplo, la primera de este grupo:
Código:
#define FLAG_UNUSED_0x020 0x20
Lo cambiaremos por:
Código:
#define FLAG_INVERTED_MOVEMENT 0x20
Podemos poner esta línea en cualquier sitio, siempre y cuando el número que usemos y el nombre que le pongamos estén libres.
El nombre que yo he elegido para la flag es FLAG_INVERTED_MOVEMENT. Podéis poner el nombre que queráis, pero es recomendable que sea un nombre lo más descriptivo posible. Asimismo, es buena idea empezar el nombre con FLAG_, que es como empiezan todas las demás flags.Si la flag 0x20 la tenéis ocupada, verbigracia, porque añadisteis el repelente permanente mediante flag, simplemente podéis coger otra flag, y punto. Lo que importa es que la flag que utilicemos esté libre.
Una vez hayamos creado la flag, haremos que, cuando el jugador pulse una tecla, el juego compruebe si la flag está activada o no, y, en caso de estarlo, que el movimiento sea invertido.
El código que nos interesa se encuentra en src/field_control_avatar.c, más concretamente en la línea 123:
Código:
if (heldKeys & DPAD_UP)
    input->dpadDirection = DIR_NORTH;
else if (heldKeys & DPAD_DOWN)
    input->dpadDirection = DIR_SOUTH;
else if (heldKeys & DPAD_LEFT)
    input->dpadDirection = DIR_WEST;
else if (heldKeys & DPAD_RIGHT)
    input->dpadDirection = DIR_EAST;
DIR_SOUTH, DIR_SOUTH, DIR_WEST y DIR_EAST indican direcciones. Dependiendo de la dirección que asignemos a input->dpadDirection, el jugador se moverá en una dirección o en otra. Entonces, le asignaremos una dirección u otra dependiendo de si la flag está activada o no:
(Ambos códigos son lo mismo, solo que el extendido está escrito en más líneas que el compacto).
Código:
if (heldKeys & DPAD_UP){
    if(FlagGet(FLAG_INVERTED_MOVEMENT)) input->dpadDirection = DIR_SOUTH;
    else input->dpadDirection = DIR_NORTH;
} else if (heldKeys & DPAD_DOWN){
    if(FlagGet(FLAG_INVERTED_MOVEMENT)) input->dpadDirection = DIR_NORTH;
    else input->dpadDirection = DIR_SOUTH;
} else if (heldKeys & DPAD_LEFT){
    if(FlagGet(FLAG_INVERTED_MOVEMENT)) input->dpadDirection = DIR_EAST;
    else input->dpadDirection = DIR_WEST;
} else if (heldKeys & DPAD_RIGHT){
    if(FlagGet(FLAG_INVERTED_MOVEMENT)) input->dpadDirection = DIR_WEST;
    else input->dpadDirection = DIR_EAST;
}
Código:
if (heldKeys & DPAD_UP){
    if(FlagGet(FLAG_INVERTED_MOVEMENT))
        input->dpadDirection = DIR_SOUTH;
    else
        input->dpadDirection = DIR_NORTH;
} else if (heldKeys & DPAD_DOWN){
    if(FlagGet(FLAG_INVERTED_MOVEMENT))
        input->dpadDirection = DIR_NORTH;
    else
        input->dpadDirection = DIR_SOUTH;
} else if (heldKeys & DPAD_LEFT){
    if(FlagGet(FLAG_INVERTED_MOVEMENT))
        input->dpadDirection = DIR_EAST;
    else
        input->dpadDirection = DIR_WEST;
} else if (heldKeys & DPAD_RIGHT){
    if(FlagGet(FLAG_INVERTED_MOVEMENT))
        input->dpadDirection = DIR_WEST;
    else
        input->dpadDirection = DIR_EAST;
}
FlagGet(nombreDeFlag) nos devuelve un valor de TRUE ('verdadero') o FALSE ('falso'). Si la flag es verdadera; es decir, si está activada, al pulsar el botón DPAD_UP (el botón de arriba), el jugador irá hacia DIR_SOUTH ('dirección sur'); si no está activada, entonces irá hacia DIR_NORTH ('dirección norte'). Sustituyendo el código original por este último, lo habremos hecho con todos los botones.
La lógica para esto es similar a la que utilizamos para la inversión del movimiento. Crearemos cuatro flags, una para cada dirección:
Código:
#define FLAG_BLOCKED_DOWN 0x20
#define FLAG_BLOCKED_UP 0x21
#define FLAG_BLOCKED_LEFT 0x22
#define FLAG_BLOCKED_RIGHT 0x23
Repito: Tanto los nombres como los números de la derecha pueden ser distintos, siempre y cuando las direcciones no estén siendo utilizados por otra flag y el nombre no esté en uso.
Una vez hemos creado las flags, las aplicaremos en el código del movimiento del jugador:
Código:
if (heldKeys & DPAD_UP && !FlagGet(FLAG_BLOCKED_UP))
    input->dpadDirection = DIR_NORTH;
else if (heldKeys & DPAD_DOWN && !FlagGet(FLAG_BLOCKED_DOWN))
    input->dpadDirection = DIR_SOUTH;
else if (heldKeys & DPAD_LEFT && !FlagGet(FLAG_BLOCKED_LEFT))
    input->dpadDirection = DIR_WEST;
else if (heldKeys & DPAD_RIGHT && !FlagGet(FLAG_BLOCKED_RIGHT))
    input->dpadDirection = DIR_EAST;
Nos interesan dos operadores nuevos:
  • && es una puerta lógica AND; es decir, si ambas condiciones se cumplen, entonces ocurrirá algo.
  • ! es una puerta lógica NOT; es decir, que si la flag está activada, entonces nos devolverá FALSE, y si está desactivada, nos devolverá TRUE —lo contrario, vamos—.
Con esto, podemos entender lo que hemos hecho en el código: Si pulsamos la tecla hacia arriba Y (&&) la flag del bloqueo NO (!) está activada, entonces el jugador se moverá para arriba. ¿Y si está activada? Entonces toda la condición será falsa, y el jugador no se va a mover.
Juntando ambos sistemas, nos quedaría este código:
(En include/constants/flags.h):
Código:
#define FLAG_INVERTED_MOVEMENT 0x20
#define FLAG_BLOCKED_DOWN 0x21
#define FLAG_BLOCKED_UP 0x22
#define FLAG_BLOCKED_LEFT 0x23
#define FLAG_BLOCKED_RIGHT 0x24
(En src/field_control_avatar.c):
Código:
if (heldKeys & DPAD_UP && !FlagGet(FLAG_BLOCKED_UP)){
    if(FlagGet(FLAG_INVERTED_MOVEMENT))
        input->dpadDirection = DIR_SOUTH;
    else
        input->dpadDirection = DIR_NORTH;
} else if (heldKeys & DPAD_DOWN && !FlagGet(FLAG_BLOCKED_DOWN)){
    if(FlagGet(FLAG_INVERTED_MOVEMENT))
        input->dpadDirection = DIR_NORTH;
    else
        input->dpadDirection = DIR_SOUTH;
} else if (heldKeys & DPAD_LEFT && !FlagGet(FLAG_BLOCKED_LEFT)){
    if(FlagGet(FLAG_INVERTED_MOVEMENT))
        input->dpadDirection = DIR_EAST;
    else
        input->dpadDirection = DIR_WEST;
} else if (heldKeys & DPAD_RIGHT && !FlagGet(FLAG_BLOCKED_RIGHT)){
    if(FlagGet(FLAG_INVERTED_MOVEMENT))
        input->dpadDirection = DIR_WEST;
    else
        input->dpadDirection = DIR_EAST;
}
De esta manera, podremos invertir o bloquear independientemente cualquier movimiento del jugador, cuando nosotros queramos.
Por último, para activar y desactivar las flags en los scripts, podéis echar un vistazo a este tutorial: FLAGS (Tutorial de scripting).

¡Saludos! :D
 
Última edición:
Arriba