Diría que el título es bastante explicativo, he hecho un par de comandos que permiten cambiar el npc que muestra el avatar del jugador. Creo que aún no había ninguno público y supongo que os será de utilidad a varios para hacer lo que os apetezca en vuestros scripts. En fin, vamos al grano.
Los comandos se llaman 'setdisguise' y 'cleardisguise'. El primero activa el sistema y nos permite indicar que npc queremos cargar, así como restringir alguna de sus acciones permitidas. Los parámetros opcionales pueden ser indicados o no. Su valor por defecto es 0 (desactivado).
El segundo comando desactiva el sistema
Es importante aclarar, que para hacer el cambio de avatar efectivo, es necesario recargar el mapa de alguna manera; por ejemplo con un warp.
Los ids de cada uino de los npc podéis verlos desde el porymap o en el fichero 'include/constants/event_objects.h'.
INSTALACIÓN
Para la instalación tenéis dos opciones:
Los comandos se llaman 'setdisguise' y 'cleardisguise'. El primero activa el sistema y nos permite indicar que npc queremos cargar, así como restringir alguna de sus acciones permitidas. Los parámetros opcionales pueden ser indicados o no. Su valor por defecto es 0 (desactivado).
Código:
setdisguise [id], {canrun}, {canridebike}, {cansurf}, {canfish}, {canirrigate}
id - es el id del npc que queréis cargar como avatar. (obligatorio)
canrun - Si vale 0 impide correr, si vale 1 permite correr. (opcional)
canridebike - Si vale 0 impide correr en bici, si vale 1 lo permite. (opcional)
cansurf - Si vale 0 impide hacer surf, si vale 1 lo permite. (opcional)
canfish - Si vale 0 impide pescar, si vale 1 lo permite. (opcional)
canirrigate - Si vale 0 impide regar, si vale 1 lo permite. (opcional)
El segundo comando desactiva el sistema
Código:
cleardisguise
Es importante aclarar, que para hacer el cambio de avatar efectivo, es necesario recargar el mapa de alguna manera; por ejemplo con un warp.
Los ids de cada uino de los npc podéis verlos desde el porymap o en el fichero 'include/constants/event_objects.h'.
INSTALACIÓN
Para la instalación tenéis dos opciones:
- Bajar la rama avatar-disguise-cmd de mi fork de pokeemerald e incluirla en vuestro proyecto con un merge.
- Modificar el código a mano siguiendo las indicaciones que se encuentran más adelante o copiando los cambios de este commit.
Antes de continuar, volver a insistir en que podéis mirar los cambios que hay en el código de forma bastante más sencilla desde este commit de GitHub.
Hay que hacer cambios en los ficheros listados a continuación.
Hay que hacer cambios en los ficheros listados a continuación.
En primer lugar vamos a ir a 'include/global.h' y vamos a añadir el siguiente struct, en mi caso lo voy a colocar encima de 'struct PyramidBag' (lo único realmente importante es que esté antes de 'struct SaveBlock2').
A continuación, dentro de este mismo fichero buscamos 'struct SaveBlock2' y añadimos 'struct PlayerDisguise playerDisguise;' al final. Quedará algo así:
Código:
struct PlayerDisguise
{
u8 enabled:1;
u8 canRun:1;
u8 canSurf:1;
u8 canRideBike:1;
u8 canFish:1;
u8 canIrrigate:1;
u16 npcId;
};
A continuación, dentro de este mismo fichero buscamos 'struct SaveBlock2' y añadimos 'struct PlayerDisguise playerDisguise;' al final. Quedará algo así:
Código:
.
.
.
/*0x21C*/ struct RankingHall1P hallRecords1P[HALL_FACILITIES_COUNT][2][3]; // From record mixing.
/*0x57C*/ struct RankingHall2P hallRecords2P[2][3]; // From record mixing.
/*0x624*/ u16 contestLinkResults[CONTEST_CATEGORIES_COUNT][CONTESTANT_COUNT];
/*0x64C*/ struct BattleFrontier frontier;
/*0x64F*/ struct PlayerDisguise playerDisguise;
}; // sizeof=0xF2C
El siguiente fichero que debemos modificar es 'asm/macros/event.inc'. añadiremos nuestros dos comandos (setdisguise y cleardisguise) justo después de '.macro bufferitemnameplural'.
Código:
.macro bufferitemnameplural out:req, item:req, quantity:req
.byte 0xe2
.byte \out
.2byte \item
.2byte \quantity
.endm @El código original termina aquí
@El código a copiar comienza aquí
.macro setdisguise id:req, canrun=0, canridebike=0, cansurf=0, canfish=0, canirrigate=0
.byte 0xe3
.2byte \id
.byte \canrun
.byte \canridebike
.byte \cansurf
.byte \canfish
.byte \canirrigate
.endm
.macro cleardisguise
.byte 0xe4
.endm
A continuación tenemos que añadir dos funciones a la tabla de 'data/script_cmd_table.inc', justo debajo de '.4byte ScrCmd_bufferitemnameplural'.
Código:
.4byte SrcCmd_setdisguise @ 0xe3
.4byte SrcCmd_cleardisguise @ 0xe4
Después de haber añadido las funciones a la tabla, nos toca declararlas dentro del fichero 'src/scrcmd.c'. Pegamos lo siguiente al final del fichero.
Código:
bool8 SrcCmd_setdisguise(struct ScriptContext *ctx)
{
u16 id = ScriptReadHalfword(ctx);
bool8 canRun = ScriptReadByte(ctx);
bool8 canRideBike = ScriptReadByte(ctx);
bool8 canSurf = ScriptReadByte(ctx);
bool8 canFish = ScriptReadByte(ctx);
bool8 canIrrigate = ScriptReadByte(ctx);
gSaveBlock2Ptr->playerDisguise.enabled = TRUE;
gSaveBlock2Ptr->playerDisguise.npcId = id;
gSaveBlock2Ptr->playerDisguise.canRun = canRun;
gSaveBlock2Ptr->playerDisguise.canRideBike = canRideBike;
gSaveBlock2Ptr->playerDisguise.canSurf = canSurf;
gSaveBlock2Ptr->playerDisguise.canFish = canFish;
gSaveBlock2Ptr->playerDisguise.canIrrigate = canIrrigate;
return FALSE;
}
bool8 SrcCmd_cleardisguise(struct ScriptContext* ctx)
{
gSaveBlock2Ptr->playerDisguise.enabled = FALSE;
return FALSE;
}
Este cambio es opcional, pero recomendado. En el fichero 'src/event_object_movement.c' dentro de la función 'static void SetPlayerAvatarObjectEventIdAndObjectId (u8 objectEventId, u8 spriteId)' cambiamos la siguiente línea:
por esto:
Código:
gPlayerAvatar.gender = GetPlayerAvatarGenderByGraphicsId(gObjectEvents[objectEventId].graphicsId);
Código:
gPlayerAvatar.gender = gSaveBlock2Ptr->playerGender;
Dentro del fichero 'field_player_avatar.c' tendremos que realizar varios cambios. En primer lugar buscamos la función 'void InitPlayerAvatar(s16 x, s16 y, u8 direction, u8 gender)' y susituimos la línea
por esto:
A continuación buscamos la función 'u8 GetPlayerAvatarGraphicsIdByStateId(u8 state)' y sustituimos su contenido con lo siguiente:
De forma similar, buscamos la función 'u8 GetPlayerAvatarGraphicsIdByStateIdAndGender(u8 state, u8 gender)' y sustituimos su contenido.
Ahora buscamos la función 'static void PlayerNotOnBikeMoving(u8 direction, u16 heldKeys)' y pegamos el siguiente código justo encima.
Por último, dentro de esa misma función 'PlayerNotOnBikeMoving' buscamos la siguiente línea:
Y la sustituimos por esto:
Código:
playerObjEventTemplate.graphicsId = GetPlayerAvatarGraphicsIdByStateIdAndGender(PLAYER_AVATAR_STATE_NORMAL, gender);
Código:
if (gSaveBlock2Ptr->playerDisguise.enabled)
playerObjEventTemplate.graphicsId = gSaveBlock2Ptr->playerDisguise.npcId;
else
playerObjEventTemplate.graphicsId = GetPlayerAvatarGraphicsIdByStateIdAndGender(PLAYER_AVATAR_STATE_NORMAL, gender);
A continuación buscamos la función 'u8 GetPlayerAvatarGraphicsIdByStateId(u8 state)' y sustituimos su contenido con lo siguiente:
Código:
if (gSaveBlock2Ptr->playerDisguise.enabled)
return gSaveBlock2Ptr->playerDisguise.npcId;
else
return GetPlayerAvatarGraphicsIdByStateIdAndGender(state, gPlayerAvatar.gender);
De forma similar, buscamos la función 'u8 GetPlayerAvatarGraphicsIdByStateIdAndGender(u8 state, u8 gender)' y sustituimos su contenido.
Código:
if (gSaveBlock2Ptr->playerDisguise.enabled)
return gSaveBlock2Ptr->playerDisguise.npcId;
return sPlayerAvatarGfxIds[state][gender];
Ahora buscamos la función 'static void PlayerNotOnBikeMoving(u8 direction, u16 heldKeys)' y pegamos el siguiente código justo encima.
Código:
static bool8 CanDisguiseRun()
{
if (gSaveBlock2Ptr->playerDisguise.enabled && !gSaveBlock2Ptr->playerDisguise.canRun)
return FALSE;
return TRUE;
}
Por último, dentro de esa misma función 'PlayerNotOnBikeMoving' buscamos la siguiente línea:
Código:
&& IsRunningDisallowed(gObjectEvents[gPlayerAvatar.objectEventId].currentMetatileBehavior) == 0)
Código:
&& IsRunningDisallowed(gObjectEvents[gPlayerAvatar.objectEventId].currentMetatileBehavior) == 0 && CanDisguiseRun())
Dentro del fichero 'src/field_control_avatar.c' buscamos la función 'GetInteractedWaterScript' y justo encima añadimos lo siguiente:
Después de esto cambiamos las siguientes líneas dentro de la función 'GetInteractedWaterScript'.
Se cambia por
Se cambia por
Código:
static bool8 CanDisguiseSurf()
{
if (gSaveBlock2Ptr->playerDisguise.enabled && !gSaveBlock2Ptr->playerDisguise.canSurf)
return FALSE;
return TRUE;
}
Después de esto cambiamos las siguientes líneas dentro de la función 'GetInteractedWaterScript'.
Código:
if (FlagGet(FLAG_BADGE05_GET) == TRUE && PartyHasMonWithSurf() == TRUE && IsPlayerFacingSurfableFishableWater() == TRUE)
Código:
if (FlagGet(FLAG_BADGE05_GET) && PartyHasMonWithSurf() && IsPlayerFacingSurfableFishableWater() && CanDisguiseSurf())
Código:
if (FlagGet(FLAG_BADGE08_GET) == TRUE && IsPlayerSurfingNorth() == TRUE)
Código:
if (FlagGet(FLAG_BADGE08_GET) && IsPlayerSurfingNorth() && CanDisguiseSurf())
Ya estamos a punto de terminar YAY..... solo nos quedan hacer los cambios necesarios del fichero 'src/item_use.c'.
Buscamos la función 'ItemUseOutOfBattle_Bike' y encima de la misma añadimos:
Ahora, dentro de la propia función 'ItemUseOutOfBattle_Bike' buscamos la línea
Y la sustituimos por
Buscamos la función 'void ItemUseOutOfBattle_Rod(u8 taskId)' y encima colocamos lo siguiente:
Dentro de la propia función 'ItemUseOutOfBattle_Rod' sustituimos la línea
por
Buscamos la función 'void ItemUseOutOfBattle_WailmerPail(u8 taskId)' y justo encima añadimos
Por último, dentro de la propia función 'ItemUseOutOfBattle_WailmerPail' tendremos que sustituir la línea
por
y la siguiente línea
por
Buscamos la función 'ItemUseOutOfBattle_Bike' y encima de la misma añadimos:
Código:
static bool8 CanDisguiseRideBike()
{
if (gSaveBlock2Ptr->playerDisguise.enabled && !gSaveBlock2Ptr->playerDisguise.canRideBike)
return FALSE;
return TRUE;
}
Ahora, dentro de la propia función 'ItemUseOutOfBattle_Bike' buscamos la línea
Código:
if (Overworld_IsBikingAllowed() == TRUE && IsBikingDisallowedByPlayer() == 0)
Código:
if (Overworld_IsBikingAllowed() == TRUE && IsBikingDisallowedByPlayer() == 0 && CanDisguiseRideBike())
Buscamos la función 'void ItemUseOutOfBattle_Rod(u8 taskId)' y encima colocamos lo siguiente:
Código:
static bool8 CanDisguiseFish()
{
if (gSaveBlock2Ptr->playerDisguise.enabled && !gSaveBlock2Ptr->playerDisguise.canFish)
return FALSE;
return TRUE;
}
Dentro de la propia función 'ItemUseOutOfBattle_Rod' sustituimos la línea
Código:
if (CanFish() == TRUE)
Código:
if (CanFish() && CanDisguiseFish())
Buscamos la función 'void ItemUseOutOfBattle_WailmerPail(u8 taskId)' y justo encima añadimos
Código:
static bool8 CanDisguiseIrrigate()
{
if (gSaveBlock2Ptr->playerDisguise.enabled && !gSaveBlock2Ptr->playerDisguise.canIrrigate)
return FALSE;
return TRUE;
}
Por último, dentro de la propia función 'ItemUseOutOfBattle_WailmerPail' tendremos que sustituir la línea
Código:
if (TryToWaterSudowoodo() == TRUE)
Código:
if (TryToWaterSudowoodo() && CanDisguiseIrrigate())
y la siguiente línea
Código:
else if (TryToWaterBerryTree() == TRUE)
Código:
else if (TryToWaterBerryTree() && CanDisguiseIrrigate())