Disturbo
Decomper
¡Hola hola otra vez!
Otro tuto más para las decomps, recién salidito de cocina. Esta vez, como podéis ver en el título, ¡es para añadir nuevas emociones (de 16x16)! Habrá que hacer cambios a bastantes archivos, pero serán cortitos y de copia y pega básicamente. Los nombres de los métodos creados son orientativos, vosotros podéis darles los que queráis. Se darán referencias sobre donde colocar el código en caso de que sea necesario. Empecemos.
Definir la nueva emoción
Tenemos que crear una referencia a esta emoción, para lo que vamos a include/constants/field_effects.h y encontramos lo siguiente:
Simplemente debemos añadir un nuevo define al antes del "#endif", tal que
Haremos esto para cada emoción que queramos añadir ¡Es importante que su número sea uno más que el último!
Métodos para la carga de gráficos
Ahora vamos a hacer todo lo necesario para que el juego pueda mostrar los gráficos de la nueva emoción. Primero definimos la localización del gráfico (de la paleta no ya que usa la del jugador). Vamos a src/trainer_see.c y en el siguiente bloque de código:
Añadiremos la referencia a nuestra imagen, situada en "graphics/misc/" y llamada "emotion_test.png", tal que así:
Después le damos una estructura para que el juego reconozca su tamaño y gráfico. En el mismo archivo, buscamos:
Y justo debajo añadimos nuestro método:
Y por último le damos otra estructura con el resto de la información que necesita para cargar el gráfico. Buscamos:
Y debajo de esto añadimos el método para nuestra emoción:
Por último, creamos un método que más adelante nos permita cargar el gráfico a la memoria. Bucamos:
Y añadimos justo después:
También tenemos que añadir este último método al "include", para lo que vamos a include/trainer_see.h y buscamos:
Y debajo añadimos el FldEff_ que acabamos de crear:
Repetid estos pasos para cada emoción que queráis añadir.
Empezar la integración en los comanos de scripting
Ahora crearemos un método que nos será útil más adelante. Para ello haremos dos cambios en data/field_effect_scripts.s. Para el primero, buscamos:
Y añadimos al final:
Y ahora al final del archivo añadimos:
De nuevo, repetimos estos pasos si queremos añadir más emociones.
Crear un nuevo tipo de movimiento
Para el juego, las emociones son equivalentes a dar un paso o saltar un borde, es decir, están consideradas como movimiento, y por ello también vamos a definirlas como tal. Vamos a include/constants/event_object_movement.h y encontramos esto:
Y antes del "#define MOVEMENT_ACTION_STEP_END 0xFE", añadimos lo siguiente:
Igual que con el "define" de la primera parte del tutorial, su valor debe ser uno mayor que el último, pero esta vez contando en hexadecimal.
Darle funcionalidad al nuevo movimiento
Ya hemos definido nuestra emoción como un movimiento, pero ahora necesitamos que haga algo, para lo que vamos a src/event_object_movement.c y buscamos lo siguiente:
Y añadimos después de esto:
Y os escucho decir "Oye pues ya, ¿no?". Pobres ilusos, ¡queda lo mas mejor! ^^
Ahora tenemos que añadir este nuevo método al "include", pero esta vez de manera especial. En total, ¡3 veces! Vamos a src/data/object_events/movement_action_func_tables.h para empezar la fiesta.
Encontramos este bloque de código:
Y al final añadimos nuestra nueva emoción:
Encontramos otro bloquecito de nada de código:
Y ¡oh, sorpresa! añadimos nuestra nueva emoción al final:
Y también añadimos al final del archivo lo siguiente:
Finalmente, encontramos otro tocho más:
Para sopresa de nadie, añadimos al final nuestra emoción:
Enhorabuena, habéis terminado los includes. Ahora continuemos.
Continuando la implementación de scripting
Esta vez vamos a crear un comando (el cuál no será usado por el jugador) que sea un puente con el método de carga de gráficos. Esta parte es sencilla, solo tenemos que ir a asm/macros/movement.inc
Y añadimos antes de "enum_start 0xfe":
Incorporar el nuevo tipo de movimiento al comando "applymovement"
Ahora haremos un nuevo "comando" que hará de argumento en el comando "applymovement" y que llamará al "emote_" que acabamos de crear. Vamos a y añadimos en cualquier parte del archivo:
¡Y con eso hemos terminado! Solo queda resaltar la importancia de que el número del "FLDEFF_" coincida con la posición del "gFieldEffectScript", algo que, si habéis seguido bien el tutorial, no debería ser un problema.
Para utilizar la emoción que acabamos de añadir en un script: applymovement <id del npc> <nombre del movimiento>
En mi caso, "<nombre del movimiento>" es "Common_Movement_Test".
Espero que hayais econtrado útil el tutorial, y si os lo habéis leído hasta aquí y no utilizais ya decomp, ¡a que esperáis! Pasaos por el canal #decompilación en Discord.
~Disturbo
Otro tuto más para las decomps, recién salidito de cocina. Esta vez, como podéis ver en el título, ¡es para añadir nuevas emociones (de 16x16)! Habrá que hacer cambios a bastantes archivos, pero serán cortitos y de copia y pega básicamente. Los nombres de los métodos creados son orientativos, vosotros podéis darles los que queráis. Se darán referencias sobre donde colocar el código en caso de que sea necesario. Empecemos.
Definir la nueva emoción
Tenemos que crear una referencia a esta emoción, para lo que vamos a include/constants/field_effects.h y encontramos lo siguiente:
C:
#ifndef GUARD_FIELD_EFFECT_CONSTANTS_H
#define GUARD_FIELD_EFFECT_CONSTANTS_H
#define FLDEFF_EXCLAMATION_MARK_ICON 0
#define FLDEFF_USE_CUT_ON_GRASS 1
#define FLDEFF_USE_CUT_ON_TREE 2
#define FLDEFF_SHADOW 3
#define FLDEFF_TALL_GRASS 4
#define FLDEFF_RIPPLE 5
#define FLDEFF_FIELD_MOVE_SHOW_MON 6
#define FLDEFF_ASH 7
#define FLDEFF_SURF_BLOB 8
#define FLDEFF_USE_SURF 9
#define FLDEFF_DUST 10
// ...
// Muchos "defines" más
// ...
#define FLDEFF_USE_TOMB_PUZZLE_EFFECT 60
#define FLDEFF_PCTURN_ON 61
#define FLDEFF_HALL_OF_FAME_RECORD 62
#define FLDEFF_USE_TELEPORT 63
#define FLDEFF_RAYQUAZA 64
#define FLDEFF_65 65
#define FLDEFF_MOVE_DEOXYS_ROCK 66
#endif
C:
#define FLDEFF_65 65
#define FLDEFF_MOVE_DEOXYS_ROCK 66
#define FLDEFF_TEST_ICON 67
Métodos para la carga de gráficos
Ahora vamos a hacer todo lo necesario para que el juego pueda mostrar los gráficos de la nueva emoción. Primero definimos la localización del gráfico (de la paleta no ya que usa la del jugador). Vamos a src/trainer_see.c y en el siguiente bloque de código:
C:
static const u8 sEmotion_ExclamationMarkGfx[] = INCBIN_U8("graphics/misc/emotion_exclamation.4bpp");
static const u8 sEmotion_QuestionMarkGfx[] = INCBIN_U8("graphics/misc/emotion_question.4bpp");
static const u8 sEmotion_HeartGfx[] = INCBIN_U8("graphics/misc/emotion_heart.4bpp");
C:
static const u8 sEmotion_HeartGfx[] = INCBIN_U8("graphics/misc/emotion_heart.4bpp");
static const u8 sEmotion_TestGfx[] = INCBIN_U8("graphics/misc/emotion_test.4bpp");
Después le damos una estructura para que el juego reconozca su tamaño y gráfico. En el mismo archivo, buscamos:
C:
static const struct SpriteFrameImage sSpriteImageTable_HeartIcon[] =
{
{
.data = sEmotion_HeartGfx,
.size = 0x80
}
};
C:
static const struct SpriteFrameImage sSpriteImageTable_TestIcon[] =
{
{
.data = sEmotion_TestGfx,
.size = 0x80
}
};
Y por último le damos otra estructura con el resto de la información que necesita para cargar el gráfico. Buscamos:
C:
static const struct SpriteTemplate sSpriteTemplate_HeartIcon =
{
.tileTag = 0xffff,
.paletteTag = 0x1004,
.oam = &sOamData_Icons,
.anims = sSpriteAnimTable_Icons,
.images = sSpriteImageTable_HeartIcon,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCB_TrainerIcons
};
C:
static const struct SpriteTemplate sSpriteTemplate_TestIcon =
{
.tileTag = 0xffff,
.paletteTag = 0x1004,
.oam = &sOamData_Icons,
.anims = sSpriteAnimTable_Icons,
.images = sSpriteImageTable_TestIcon,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCB_TrainerIcons
};
Por último, creamos un método que más adelante nos permita cargar el gráfico a la memoria. Bucamos:
C:
u8 FldEff_HeartIcon(void)
{
u8 spriteId = CreateSpriteAtEnd(&sSpriteTemplate_HeartIcon, 0, 0, 0x52);
if (spriteId != MAX_SPRITES)
{
struct Sprite *sprite = &gSprites[spriteId];
SetIconSpriteData(sprite, FLDEFF_HEART_ICON, 0);
sprite->oam.paletteNum = 2;
}
return 0;
}
C:
u8 FldEff_TestIcon(void)
{
u8 spriteId = CreateSpriteAtEnd(&sSpriteTemplate_TestIcon, 0, 0, 0x52);
if (spriteId != MAX_SPRITES)
{
struct Sprite *sprite = &gSprites[spriteId];
// Daos cuenta de que teneis que reemplazar FLDEFF_TEST_ICON con el define que habéis creado antes
SetIconSpriteData(sprite, FLDEFF_TEST_ICON, 0);
sprite->oam.paletteNum = 0;
}
return 0;
}
También tenemos que añadir este último método al "include", para lo que vamos a include/trainer_see.h y buscamos:
C:
u8 FldEff_ExclamationMarkIcon(void);
u8 FldEff_QuestionMarkIcon(void);
u8 FldEff_HeartIcon(void);
C:
u8 FldEff_ExclamationMarkIcon(void);
u8 FldEff_QuestionMarkIcon(void);
u8 FldEff_HeartIcon(void);
u8 FldEff_TestIcon(void);
Empezar la integración en los comanos de scripting
Ahora crearemos un método que nos será útil más adelante. Para ello haremos dos cambios en data/field_effect_scripts.s. Para el primero, buscamos:
C:
gFieldEffectScriptPointers:: @ 82DB9D4
.4byte gFieldEffectScript_ExclamationMarkIcon1
.4byte gFieldEffectScript_UseCutOnTallGrass
.4byte gFieldEffectScript_UseCutOnTree
.4byte gFieldEffectScript_Shadow
@ Muchos ".4bytes" por aquí
.4byte gFieldEffectScript_Rayquaza
.4byte gFieldEffectScript_Unknown65
.4byte gFieldEffectScript_MoveDeoxysRock
C:
.4byte gFieldEffectScript_Rayquaza
.4byte gFieldEffectScript_Unknown65
.4byte gFieldEffectScript_MoveDeoxysRock
.4byte gFieldEffectScript_TestIcon
C:
gFieldEffectScript_TestIcon:: @ 82DBC56
field_eff_loadfadedpal_callnative gFieldEffectObjectPaletteInfo0, FldEff_TestIcon
field_eff_end
Crear un nuevo tipo de movimiento
Para el juego, las emociones son equivalentes a dar un paso o saltar un borde, es decir, están consideradas como movimiento, y por ello también vamos a definirlas como tal. Vamos a include/constants/event_object_movement.h y encontramos esto:
C:
#ifndef GUARD_CONSTANTS_EVENT_OBJECT_MOVEMENT_H
#define GUARD_CONSTANTS_EVENT_OBJECT_MOVEMENT_H
#define MOVEMENT_TYPE_NONE 0x0
#define MOVEMENT_TYPE_LOOK_AROUND 0x1
#define MOVEMENT_TYPE_WANDER_AROUND 0x2
#define MOVEMENT_TYPE_WANDER_UP_AND_DOWN 0x3
#define MOVEMENT_TYPE_WANDER_DOWN_AND_UP 0x4
#define MOVEMENT_TYPE_WANDER_LEFT_AND_RIGHT 0x5
#define MOVEMENT_TYPE_WANDER_RIGHT_AND_LEFT 0x6
#define MOVEMENT_TYPE_FACE_UP 0x7
#define MOVEMENT_TYPE_FACE_DOWN 0x8
#define MOVEMENT_TYPE_FACE_LEFT 0x9
#define MOVEMENT_TYPE_FACE_RIGHT 0xA
#define MOVEMENT_TYPE_PLAYER 0xB
#define MOVEMENT_TYPE_BERRY_TREE_GROWTH 0xC
#define MOVEMENT_TYPE_FACE_DOWN_AND_UP 0xD
#define MOVEMENT_TYPE_FACE_LEFT_AND_RIGHT 0xE
#define MOVEMENT_TYPE_FACE_UP_AND_LEFT 0xF
//Muchos más "defines" en este espacio
#define MOVEMENT_ACTION_FREE_AND_UNLOCK_ANIM 0x95
#define MOVEMENT_ACTION_WALK_LEFT_AFFINE 0x96
#define MOVEMENT_ACTION_WALK_RIGHT_AFFINE 0x97
#define MOVEMENT_ACTION_LEVITATE 0x98
#define MOVEMENT_ACTION_STOP_LEVITATE 0x99
#define MOVEMENT_ACTION_DESTROY_EXTRA_TASK_IF_AT_TOP 0x9A
#define MOVEMENT_ACTION_FIGURE_8 0x9B
#define MOVEMENT_ACTION_FLY_UP 0x9C
#define MOVEMENT_ACTION_FLY_DOWN 0x9D
#define MOVEMENT_ACTION_STEP_END 0xFE
#endif // GUARD_CONSTANTS_EVENT_OBJECT_MOVEMENT_H
C:
#define MOVEMENT_ACTION_FIGURE_8 0x9B
#define MOVEMENT_ACTION_FLY_UP 0x9C
#define MOVEMENT_ACTION_FLY_DOWN 0x9D
//Por supuesto le damos el nombre que queramos
#define MOVEMENT_ACTION_EMOTE_TEST 0x9E
#define MOVEMENT_ACTION_STEP_END 0xFE
Darle funcionalidad al nuevo movimiento
Ya hemos definido nuestra emoción como un movimiento, pero ahora necesitamos que haga algo, para lo que vamos a src/event_object_movement.c y buscamos lo siguiente:
C:
bool8 MovementAction_EmoteHeart_Step0(struct ObjectEvent *objectEvent, struct Sprite *sprite)
{
ObjectEventGetLocalIdAndMap(objectEvent, &gFieldEffectArguments[0], &gFieldEffectArguments[1], &gFieldEffectArguments[2]);
FieldEffectStart(FLDEFF_HEART_ICON);
sprite->data[2] = 1;
return TRUE;
}
C:
bool8 MovementAction_EmoteTest_Step0(struct ObjectEvent *objectEvent, struct Sprite *sprite)
{
ObjectEventGetLocalIdAndMap(objectEvent, &gFieldEffectArguments[0], &gFieldEffectArguments[1], &gFieldEffectArguments[2]);
// De nuevo, reemplazad FLDEFF_TEST_ICON con el define que habéis creado al principio
FieldEffectStart(FLDEFF_TEST_ICON);
sprite->data[2] = 1;
return TRUE;
}
Ahora tenemos que añadir este nuevo método al "include", pero esta vez de manera especial. En total, ¡3 veces! Vamos a src/data/object_events/movement_action_func_tables.h para empezar la fiesta.
Encontramos este bloque de código:
C:
u8 MovementAction_FaceDown_Step0(struct ObjectEvent *, struct Sprite *);
u8 MovementAction_FaceDown_Step0(struct ObjectEvent *, struct Sprite *);
u8 MovementAction_PauseSpriteAnim(struct ObjectEvent *, struct Sprite *);
u8 MovementAction_FaceUp_Step0(struct ObjectEvent *, struct Sprite *);
u8 MovementAction_FaceLeft_Step0(struct ObjectEvent *, struct Sprite *);
u8 MovementAction_FaceRight_Step0(struct ObjectEvent *, struct Sprite *);
// Una montonera de "u8"
u8 MovementAction_FlyUp_Step0(struct ObjectEvent *, struct Sprite *);
u8 MovementAction_FlyUp_Step1(struct ObjectEvent *, struct Sprite *);
u8 MovementAction_Fly_Finish(struct ObjectEvent *, struct Sprite *);
u8 MovementAction_FlyDown_Step0(struct ObjectEvent *, struct Sprite *);
u8 MovementAction_FlyDown_Step1(struct ObjectEvent *, struct Sprite *);
C:
u8 MovementAction_FlyDown_Step0(struct ObjectEvent *, struct Sprite *);
u8 MovementAction_FlyDown_Step1(struct ObjectEvent *, struct Sprite *);
u8 MovementAction_EmoteTest_Step0(struct ObjectEvent *, struct Sprite *);
C:
u8 (*const gMovementActionFuncs_FaceDown[])(struct ObjectEvent *, struct Sprite *);
u8 (*const gMovementActionFuncs_FaceUp[])(struct ObjectEvent *, struct Sprite *);
u8 (*const gMovementActionFuncs_FaceLeft[])(struct ObjectEvent *, struct Sprite *);
u8 (*const gMovementActionFuncs_FaceRight[])(struct ObjectEvent *, struct Sprite *);
//Otro montón de "u8"
u8 (*const gMovementActionFuncs_Figure8[])(struct ObjectEvent *, struct Sprite *);
u8 (*const gMovementActionFuncs_FlyUp[])(struct ObjectEvent *, struct Sprite *);
u8 (*const gMovementActionFuncs_FlyDown[])(struct ObjectEvent *, struct Sprite *);
C:
u8 (*const gMovementActionFuncs_FlyUp[])(struct ObjectEvent *, struct Sprite *);
u8 (*const gMovementActionFuncs_FlyDown[])(struct ObjectEvent *, struct Sprite *);
u8 (*const gMovementActionFuncs_EmoteTest[])(struct ObjectEvent *, struct Sprite *);
C:
u8 (*const gMovementActionFuncs_EmoteTest[])(struct ObjectEvent *, struct Sprite *) = {
MovementAction_EmoteTest_Step0,
MovementAction_Finish,
};
Finalmente, encontramos otro tocho más:
C:
u8 (*const *const gMovementActionFuncs[])(struct ObjectEvent *, struct Sprite *) = {
[MOVEMENT_ACTION_FACE_DOWN] = gMovementActionFuncs_FaceDown,
[MOVEMENT_ACTION_FACE_UP] = gMovementActionFuncs_FaceUp,
[MOVEMENT_ACTION_FACE_LEFT] = gMovementActionFuncs_FaceLeft,
[MOVEMENT_ACTION_FACE_RIGHT] = gMovementActionFuncs_FaceRight,
//Unas cuantas asignaciones
[MOVEMENT_ACTION_FIGURE_8] = gMovementActionFuncs_Figure8,
[MOVEMENT_ACTION_FLY_UP] = gMovementActionFuncs_FlyUp,
[MOVEMENT_ACTION_FLY_DOWN] = gMovementActionFuncs_FlyDown,
};
C:
[MOVEMENT_ACTION_FLY_UP] = gMovementActionFuncs_FlyUp,
[MOVEMENT_ACTION_FLY_DOWN] = gMovementActionFuncs_FlyDown,
[MOVEMENT_ACTION_EMOTE_TEST] = gMovementActionFuncs_EmoteTest,
Continuando la implementación de scripting
Esta vez vamos a crear un comando (el cuál no será usado por el jugador) que sea un puente con el método de carga de gráficos. Esta parte es sencilla, solo tenemos que ir a asm/macros/movement.inc
C:
.macro create_movement_action name
enum _\name
.macro \name
.byte _\name
.endm
.endm
enum_start
create_movement_action face_down
create_movement_action face_up
create_movement_action face_left
create_movement_action face_right
create_movement_action walk_slow_down
create_movement_action walk_slow_up
create_movement_action walk_slow_left
create_movement_action walk_slow_right
@ Un montón de "create_movement_action"
create_movement_action figure_8
create_movement_action fly_up
create_movement_action fly_down
enum_start 0xfe
create_movement_action step_end
C:
create_movement_action figure_8
create_movement_action fly_up
create_movement_action fly_down
create_movement_action emote_test
enum_start 0xfe
create_movement_action step_end
Incorporar el nuevo tipo de movimiento al comando "applymovement"
Ahora haremos un nuevo "comando" que hará de argumento en el comando "applymovement" y que llamará al "emote_" que acabamos de crear. Vamos a y añadimos en cualquier parte del archivo:
C:
Common_Movement_Test:
//Reemplazamos esto con el "emote" que creamos antes
emote_test
step_end
¡Y con eso hemos terminado! Solo queda resaltar la importancia de que el número del "FLDEFF_" coincida con la posición del "gFieldEffectScript", algo que, si habéis seguido bien el tutorial, no debería ser un problema.
Para utilizar la emoción que acabamos de añadir en un script: applymovement <id del npc> <nombre del movimiento>
En mi caso, "<nombre del movimiento>" es "Common_Movement_Test".
Espero que hayais econtrado útil el tutorial, y si os lo habéis leído hasta aquí y no utilizais ya decomp, ¡a que esperáis! Pasaos por el canal #decompilación en Discord.
~Disturbo
Última edición: