Expanded Pokémon Follower transformation functionality (#5048)

This commit is contained in:
Eduardo Quezada 2025-01-18 19:19:06 -03:00 committed by GitHub
parent 243900cdbb
commit cd1482bbaa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 247 additions and 172 deletions

View File

@ -55,8 +55,10 @@
#define OW_MON_WANDER_WALK TRUE // If true, OW pokemon with MOVEMENT_TYPE_WANDER will walk-in-place in between steps.
// Follower Pokémon
#define OW_FOLLOWERS_ENABLED FALSE // Enables follower Pokémon, HGSS style. Requires OW_POKEMON_OBJECT_EVENTS. Note that additional scripting may be required for them to be fully supported!
#define OW_FOLLOWERS_BOBBING TRUE // If true, follower pokemon will bob up and down during their idle & walking animations
#define OW_FOLLOWERS_POKEBALLS TRUE // Followers will emerge from the pokeball they are stored in, instead of a normal pokeball
#define OW_FOLLOWERS_BOBBING TRUE // If TRUE, follower Pokémon will bob up and down during their idle & walking animations
#define OW_FOLLOWERS_POKEBALLS TRUE // If TRUE, follower Pokémon will emerge from the Poké Ball they are stored in, instead of a normal Poké Ball
#define OW_FOLLOWERS_WEATHER_FORMS FALSE // If TRUE, Castform and Cherrim gain FORM_CHANGE_OVERWORLD_WEATHER, which will make them transform in the overworld based on the weather.
#define OW_FOLLOWERS_COPY_WILD_PKMN FALSE // If TRUE, follower Pokémon that know Transform or have Illusion/Imposter will copy wild Pokémon at random.
// Out-of-battle Ability effects
#define OW_SYNCHRONIZE_NATURE GEN_LATEST // In Gen8+, if a Pokémon with Synchronize leads the party, wild Pokémon will always have their same Nature as opposed to the 50% chance in previous games. Gift Pokémon excluded.

View File

@ -144,4 +144,11 @@
// param2: ability to check, optional
#define FORM_CHANGE_BATTLE_BEFORE_MOVE_CATEGORY 25
// Form change that activates when overworld weather changes.
// param1: weather to check.
#define FORM_CHANGE_OVERWORLD_WEATHER 26
// Form change that activates when the Pokémon is deposited into the PC or Daycare.
#define FORM_CHANGE_DEPOSIT 27
#endif // GUARD_CONSTANTS_FORM_CHANGE_TYPES_H

View File

@ -148,6 +148,7 @@ void ClearObjectEventMovement(struct ObjectEvent *objectEvent, struct Sprite *sp
void ObjectEventClearHeldMovement(struct ObjectEvent *);
void ObjectEventClearHeldMovementIfActive(struct ObjectEvent *);
struct Pokemon *GetFirstLiveMon(void);
u16 GetOverworldWeatherSpecies(u16 species);
void UpdateFollowingPokemon(void);
void RemoveFollowingPokemon(void);
struct ObjectEvent *GetFollowerObject(void);

View File

@ -10925,21 +10925,18 @@ bool32 DoesSpeciesUseHoldItemToChangeForm(u16 species, u16 heldItemId)
u32 i;
const struct FormChange *formChanges = GetSpeciesFormChanges(species);
if (formChanges != NULL)
for (i = 0; formChanges != NULL && formChanges[i].method != FORM_CHANGE_TERMINATOR; i++)
{
for (i = 0; formChanges[i].method != FORM_CHANGE_TERMINATOR; i++)
switch (formChanges[i].method)
{
switch (formChanges[i].method)
{
case FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM:
case FORM_CHANGE_BATTLE_PRIMAL_REVERSION:
case FORM_CHANGE_BATTLE_ULTRA_BURST:
case FORM_CHANGE_ITEM_HOLD:
case FORM_CHANGE_BEGIN_BATTLE:
if (formChanges[i].param1 == heldItemId)
return TRUE;
break;
}
case FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM:
case FORM_CHANGE_BATTLE_PRIMAL_REVERSION:
case FORM_CHANGE_BATTLE_ULTRA_BURST:
case FORM_CHANGE_ITEM_HOLD:
case FORM_CHANGE_BEGIN_BATTLE:
if (formChanges[i].param1 == heldItemId)
return TRUE;
break;
}
}
return FALSE;
@ -11070,103 +11067,98 @@ u16 GetBattleFormChangeTargetSpecies(u32 battler, u16 method)
u32 targetSpecies = species;
const struct FormChange *formChanges = GetSpeciesFormChanges(species);
struct Pokemon *mon = &GetBattlerParty(battler)[gBattlerPartyIndexes[battler]];
u16 heldItem;
u16 heldItem = gBattleMons[battler].item;
if (formChanges != NULL)
for (i = 0; formChanges != NULL && formChanges[i].method != FORM_CHANGE_TERMINATOR; i++)
{
heldItem = gBattleMons[battler].item;
for (i = 0; formChanges[i].method != FORM_CHANGE_TERMINATOR; i++)
if (method == formChanges[i].method && species != formChanges[i].targetSpecies)
{
if (method == formChanges[i].method && species != formChanges[i].targetSpecies)
switch (method)
{
switch (method)
case FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM:
case FORM_CHANGE_BATTLE_PRIMAL_REVERSION:
case FORM_CHANGE_BATTLE_ULTRA_BURST:
if (heldItem == formChanges[i].param1)
targetSpecies = formChanges[i].targetSpecies;
break;
case FORM_CHANGE_BATTLE_MEGA_EVOLUTION_MOVE:
if (gBattleMons[battler].moves[0] == formChanges[i].param1
|| gBattleMons[battler].moves[1] == formChanges[i].param1
|| gBattleMons[battler].moves[2] == formChanges[i].param1
|| gBattleMons[battler].moves[3] == formChanges[i].param1)
targetSpecies = formChanges[i].targetSpecies;
break;
case FORM_CHANGE_BATTLE_SWITCH:
if (formChanges[i].param1 == GetBattlerAbility(battler) || formChanges[i].param1 == ABILITY_NONE)
targetSpecies = formChanges[i].targetSpecies;
break;
case FORM_CHANGE_BATTLE_HP_PERCENT:
if (formChanges[i].param1 == GetBattlerAbility(battler))
{
case FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM:
case FORM_CHANGE_BATTLE_PRIMAL_REVERSION:
case FORM_CHANGE_BATTLE_ULTRA_BURST:
if (heldItem == formChanges[i].param1)
targetSpecies = formChanges[i].targetSpecies;
break;
case FORM_CHANGE_BATTLE_MEGA_EVOLUTION_MOVE:
if (gBattleMons[battler].moves[0] == formChanges[i].param1
|| gBattleMons[battler].moves[1] == formChanges[i].param1
|| gBattleMons[battler].moves[2] == formChanges[i].param1
|| gBattleMons[battler].moves[3] == formChanges[i].param1)
targetSpecies = formChanges[i].targetSpecies;
break;
case FORM_CHANGE_BATTLE_SWITCH:
if (formChanges[i].param1 == GetBattlerAbility(battler) || formChanges[i].param1 == ABILITY_NONE)
targetSpecies = formChanges[i].targetSpecies;
break;
case FORM_CHANGE_BATTLE_HP_PERCENT:
if (formChanges[i].param1 == GetBattlerAbility(battler))
// We multiply by 100 to make sure that integer division doesn't mess with the health check.
u32 hpCheck = gBattleMons[battler].hp * 100 * 100 / gBattleMons[battler].maxHP;
switch(formChanges[i].param2)
{
// We multiply by 100 to make sure that integer division doesn't mess with the health check.
u32 hpCheck = gBattleMons[battler].hp * 100 * 100 / gBattleMons[battler].maxHP;
switch(formChanges[i].param2)
{
case HP_HIGHER_THAN:
if (hpCheck > formChanges[i].param3 * 100)
targetSpecies = formChanges[i].targetSpecies;
break;
case HP_LOWER_EQ_THAN:
if (hpCheck <= formChanges[i].param3 * 100)
targetSpecies = formChanges[i].targetSpecies;
break;
}
case HP_HIGHER_THAN:
if (hpCheck > formChanges[i].param3 * 100)
targetSpecies = formChanges[i].targetSpecies;
break;
case HP_LOWER_EQ_THAN:
if (hpCheck <= formChanges[i].param3 * 100)
targetSpecies = formChanges[i].targetSpecies;
break;
}
break;
case FORM_CHANGE_BATTLE_GIGANTAMAX:
if (GetMonData(mon, MON_DATA_GIGANTAMAX_FACTOR))
targetSpecies = formChanges[i].targetSpecies;
break;
case FORM_CHANGE_BATTLE_WEATHER:
// Check if there is a required ability and if the battler's ability does not match it
// or is suppressed. If so, revert to the no weather form.
if (formChanges[i].param2
&& GetBattlerAbility(battler) != formChanges[i].param2
&& formChanges[i].param1 == B_WEATHER_NONE)
{
targetSpecies = formChanges[i].targetSpecies;
}
// We need to revert the weather form if the field is under Air Lock, too.
else if (!WEATHER_HAS_EFFECT && formChanges[i].param1 == B_WEATHER_NONE)
{
targetSpecies = formChanges[i].targetSpecies;
}
// Otherwise, just check for a match between the weather and the form change table.
// Added a check for whether the weather is in effect to prevent end-of-turn soft locks with Cloud Nine / Air Lock
else if (((gBattleWeather & formChanges[i].param1) && WEATHER_HAS_EFFECT)
|| (gBattleWeather == B_WEATHER_NONE && formChanges[i].param1 == B_WEATHER_NONE))
{
targetSpecies = formChanges[i].targetSpecies;
}
break;
case FORM_CHANGE_BATTLE_TURN_END:
case FORM_CHANGE_HIT_BY_MOVE:
if (formChanges[i].param1 == GetBattlerAbility(battler))
targetSpecies = formChanges[i].targetSpecies;
break;
case FORM_CHANGE_STATUS:
if (gBattleMons[battler].status1 & formChanges[i].param1)
targetSpecies = formChanges[i].targetSpecies;
break;
case FORM_CHANGE_BATTLE_TERASTALLIZATION:
if (GetBattlerTeraType(battler) == formChanges[i].param1)
targetSpecies = formChanges[i].targetSpecies;
break;
case FORM_CHANGE_BATTLE_BEFORE_MOVE:
if (formChanges[i].param1 == gCurrentMove
&& (formChanges[i].param2 == ABILITY_NONE || formChanges[i].param2 == GetBattlerAbility(battler)))
targetSpecies = formChanges[i].targetSpecies;
break;
case FORM_CHANGE_BATTLE_BEFORE_MOVE_CATEGORY:
if (formChanges[i].param1 == GetBattleMoveCategory(gCurrentMove)
&& (formChanges[i].param2 == ABILITY_NONE || formChanges[i].param2 == GetBattlerAbility(battler)))
targetSpecies = formChanges[i].targetSpecies;
break;
}
break;
case FORM_CHANGE_BATTLE_GIGANTAMAX:
if (GetMonData(mon, MON_DATA_GIGANTAMAX_FACTOR))
targetSpecies = formChanges[i].targetSpecies;
break;
case FORM_CHANGE_BATTLE_WEATHER:
// Check if there is a required ability and if the battler's ability does not match it
// or is suppressed. If so, revert to the no weather form.
if (formChanges[i].param2
&& GetBattlerAbility(battler) != formChanges[i].param2
&& formChanges[i].param1 == B_WEATHER_NONE)
{
targetSpecies = formChanges[i].targetSpecies;
}
// We need to revert the weather form if the field is under Air Lock, too.
else if (!WEATHER_HAS_EFFECT && formChanges[i].param1 == B_WEATHER_NONE)
{
targetSpecies = formChanges[i].targetSpecies;
}
// Otherwise, just check for a match between the weather and the form change table.
// Added a check for whether the weather is in effect to prevent end-of-turn soft locks with Cloud Nine / Air Lock
else if (((gBattleWeather & formChanges[i].param1) && WEATHER_HAS_EFFECT)
|| (gBattleWeather == B_WEATHER_NONE && formChanges[i].param1 == B_WEATHER_NONE))
{
targetSpecies = formChanges[i].targetSpecies;
}
break;
case FORM_CHANGE_BATTLE_TURN_END:
case FORM_CHANGE_HIT_BY_MOVE:
if (formChanges[i].param1 == GetBattlerAbility(battler))
targetSpecies = formChanges[i].targetSpecies;
break;
case FORM_CHANGE_STATUS:
if (gBattleMons[battler].status1 & formChanges[i].param1)
targetSpecies = formChanges[i].targetSpecies;
break;
case FORM_CHANGE_BATTLE_TERASTALLIZATION:
if (GetBattlerTeraType(battler) == formChanges[i].param1)
targetSpecies = formChanges[i].targetSpecies;
break;
case FORM_CHANGE_BATTLE_BEFORE_MOVE:
if (formChanges[i].param1 == gCurrentMove
&& (formChanges[i].param2 == ABILITY_NONE || formChanges[i].param2 == GetBattlerAbility(battler)))
targetSpecies = formChanges[i].targetSpecies;
break;
case FORM_CHANGE_BATTLE_BEFORE_MOVE_CATEGORY:
if (formChanges[i].param1 == GetBattleMoveCategory(gCurrentMove)
&& (formChanges[i].param2 == ABILITY_NONE || formChanges[i].param2 == GetBattlerAbility(battler)))
targetSpecies = formChanges[i].targetSpecies;
break;
}
}
}

View File

@ -410,22 +410,33 @@ static const struct FormChange sGlalieFormChangeTable[] = {
#if P_FAMILY_CASTFORM
static const struct FormChange sCastformFormChangeTable[] = {
#if OW_FOLLOWERS_WEATHER_FORMS == TRUE
{FORM_CHANGE_OVERWORLD_WEATHER, SPECIES_CASTFORM_SUNNY, WEATHER_SUNNY_CLOUDS},
{FORM_CHANGE_OVERWORLD_WEATHER, SPECIES_CASTFORM_SUNNY, WEATHER_DROUGHT},
{FORM_CHANGE_OVERWORLD_WEATHER, SPECIES_CASTFORM_RAINY, WEATHER_RAIN},
{FORM_CHANGE_OVERWORLD_WEATHER, SPECIES_CASTFORM_RAINY, WEATHER_RAIN_THUNDERSTORM},
{FORM_CHANGE_OVERWORLD_WEATHER, SPECIES_CASTFORM_RAINY, WEATHER_DOWNPOUR},
{FORM_CHANGE_OVERWORLD_WEATHER, SPECIES_CASTFORM_SNOWY, WEATHER_SNOW},
{FORM_CHANGE_OVERWORLD_WEATHER, SPECIES_CASTFORM_NORMAL, WEATHER_NONE},
{FORM_CHANGE_BEGIN_BATTLE, SPECIES_CASTFORM_NORMAL}, // To prevent Battles from being affected.
{FORM_CHANGE_DEPOSIT, SPECIES_CASTFORM_NORMAL}, // To prevent depositing weather forms into the PC.
#endif //OW_FOLLOWERS_WEATHER_FORMS
#if B_WEATHER_FORMS >= GEN_5
{FORM_CHANGE_BATTLE_WEATHER, SPECIES_CASTFORM_SUNNY, B_WEATHER_SUN, ABILITY_FORECAST},
{FORM_CHANGE_BATTLE_WEATHER, SPECIES_CASTFORM_RAINY, B_WEATHER_RAIN, ABILITY_FORECAST},
{FORM_CHANGE_BATTLE_WEATHER, SPECIES_CASTFORM_SNOWY, B_WEATHER_HAIL | B_WEATHER_SNOW, ABILITY_FORECAST},
{FORM_CHANGE_BATTLE_WEATHER, SPECIES_CASTFORM_NORMAL, ~(B_WEATHER_SUN | B_WEATHER_RAIN | B_WEATHER_HAIL | B_WEATHER_SNOW), ABILITY_FORECAST},
{FORM_CHANGE_BATTLE_WEATHER, SPECIES_CASTFORM_NORMAL, B_WEATHER_NONE, ABILITY_FORECAST},
{FORM_CHANGE_BATTLE_WEATHER, SPECIES_CASTFORM_SUNNY, B_WEATHER_SUN, ABILITY_FORECAST},
{FORM_CHANGE_BATTLE_WEATHER, SPECIES_CASTFORM_RAINY, B_WEATHER_RAIN, ABILITY_FORECAST},
{FORM_CHANGE_BATTLE_WEATHER, SPECIES_CASTFORM_SNOWY, B_WEATHER_HAIL | B_WEATHER_SNOW, ABILITY_FORECAST},
{FORM_CHANGE_BATTLE_WEATHER, SPECIES_CASTFORM_NORMAL, ~(B_WEATHER_SUN | B_WEATHER_RAIN | B_WEATHER_HAIL | B_WEATHER_SNOW), ABILITY_FORECAST},
{FORM_CHANGE_BATTLE_WEATHER, SPECIES_CASTFORM_NORMAL, B_WEATHER_NONE, ABILITY_FORECAST},
#else
{FORM_CHANGE_BATTLE_WEATHER, SPECIES_CASTFORM_SUNNY, B_WEATHER_SUN},
{FORM_CHANGE_BATTLE_WEATHER, SPECIES_CASTFORM_RAINY, B_WEATHER_RAIN},
{FORM_CHANGE_BATTLE_WEATHER, SPECIES_CASTFORM_SNOWY, B_WEATHER_HAIL | B_WEATHER_SNOW},
{FORM_CHANGE_BATTLE_WEATHER, SPECIES_CASTFORM_NORMAL, ~(B_WEATHER_SUN | B_WEATHER_RAIN | B_WEATHER_HAIL | B_WEATHER_SNOW)},
{FORM_CHANGE_BATTLE_WEATHER, SPECIES_CASTFORM_NORMAL, B_WEATHER_NONE},
{FORM_CHANGE_BATTLE_WEATHER, SPECIES_CASTFORM_SUNNY, B_WEATHER_SUN},
{FORM_CHANGE_BATTLE_WEATHER, SPECIES_CASTFORM_RAINY, B_WEATHER_RAIN},
{FORM_CHANGE_BATTLE_WEATHER, SPECIES_CASTFORM_SNOWY, B_WEATHER_HAIL | B_WEATHER_SNOW},
{FORM_CHANGE_BATTLE_WEATHER, SPECIES_CASTFORM_NORMAL, ~(B_WEATHER_SUN | B_WEATHER_RAIN | B_WEATHER_HAIL | B_WEATHER_SNOW)},
{FORM_CHANGE_BATTLE_WEATHER, SPECIES_CASTFORM_NORMAL, B_WEATHER_NONE},
#endif
{FORM_CHANGE_BATTLE_SWITCH, SPECIES_CASTFORM_NORMAL},
{FORM_CHANGE_FAINT, SPECIES_CASTFORM_NORMAL},
{FORM_CHANGE_END_BATTLE, SPECIES_CASTFORM_NORMAL},
{FORM_CHANGE_BATTLE_SWITCH, SPECIES_CASTFORM_NORMAL},
{FORM_CHANGE_FAINT, SPECIES_CASTFORM_NORMAL},
{FORM_CHANGE_END_BATTLE, SPECIES_CASTFORM_NORMAL},
{FORM_CHANGE_TERMINATOR},
};
#endif //P_FAMILY_CASTFORM
@ -531,18 +542,24 @@ static const struct FormChange sBurmyFormChangeTable[] = {
#if P_FAMILY_CHERUBI
static const struct FormChange sCherrimFormChangeTable[] = {
#if OW_FOLLOWERS_WEATHER_FORMS == TRUE
{FORM_CHANGE_OVERWORLD_WEATHER, SPECIES_CHERRIM_SUNSHINE, WEATHER_DROUGHT},
{FORM_CHANGE_OVERWORLD_WEATHER, SPECIES_CHERRIM_OVERCAST, WEATHER_NONE},
{FORM_CHANGE_BEGIN_BATTLE, SPECIES_CHERRIM_OVERCAST}, // To prevent Battles from being affected.
{FORM_CHANGE_DEPOSIT, SPECIES_CHERRIM_OVERCAST}, // To prevent depositing weather forms into the PC.
#endif //OW_FOLLOWERS_WEATHER_FORMS
#if B_WEATHER_FORMS >= GEN_5
{FORM_CHANGE_BATTLE_WEATHER, SPECIES_CHERRIM_SUNSHINE, B_WEATHER_SUN, ABILITY_FLOWER_GIFT},
{FORM_CHANGE_BATTLE_WEATHER, SPECIES_CHERRIM_OVERCAST, ~B_WEATHER_SUN, ABILITY_FLOWER_GIFT},
{FORM_CHANGE_BATTLE_WEATHER, SPECIES_CHERRIM_OVERCAST, B_WEATHER_NONE, ABILITY_FLOWER_GIFT},
{FORM_CHANGE_BATTLE_WEATHER, SPECIES_CHERRIM_SUNSHINE, B_WEATHER_SUN, ABILITY_FLOWER_GIFT},
{FORM_CHANGE_BATTLE_WEATHER, SPECIES_CHERRIM_OVERCAST, ~B_WEATHER_SUN, ABILITY_FLOWER_GIFT},
{FORM_CHANGE_BATTLE_WEATHER, SPECIES_CHERRIM_OVERCAST, B_WEATHER_NONE, ABILITY_FLOWER_GIFT},
#else
{FORM_CHANGE_BATTLE_WEATHER, SPECIES_CHERRIM_SUNSHINE, B_WEATHER_SUN},
{FORM_CHANGE_BATTLE_WEATHER, SPECIES_CHERRIM_OVERCAST, ~B_WEATHER_SUN},
{FORM_CHANGE_BATTLE_WEATHER, SPECIES_CHERRIM_OVERCAST, B_WEATHER_NONE},
{FORM_CHANGE_BATTLE_WEATHER, SPECIES_CHERRIM_SUNSHINE, B_WEATHER_SUN},
{FORM_CHANGE_BATTLE_WEATHER, SPECIES_CHERRIM_OVERCAST, ~B_WEATHER_SUN},
{FORM_CHANGE_BATTLE_WEATHER, SPECIES_CHERRIM_OVERCAST, B_WEATHER_NONE},
#endif
{FORM_CHANGE_BATTLE_SWITCH, SPECIES_CHERRIM_OVERCAST},
{FORM_CHANGE_FAINT, SPECIES_CHERRIM_OVERCAST},
{FORM_CHANGE_END_BATTLE, SPECIES_CHERRIM_OVERCAST},
{FORM_CHANGE_BATTLE_SWITCH, SPECIES_CHERRIM_OVERCAST},
{FORM_CHANGE_FAINT, SPECIES_CHERRIM_OVERCAST},
{FORM_CHANGE_END_BATTLE, SPECIES_CHERRIM_OVERCAST},
{FORM_CHANGE_TERMINATOR},
};
#endif //P_FAMILY_CHERUBI

View File

@ -259,6 +259,13 @@ static void StorePokemonInDaycare(struct Pokemon *mon, struct DaycareMon *daycar
TakeMailFromMon(mon);
}
u32 newSpecies = GetFormChangeTargetSpecies(mon, FORM_CHANGE_DEPOSIT, 0);
if (newSpecies != GetMonData(mon, MON_DATA_SPECIES))
{
SetMonData(mon, MON_DATA_SPECIES, &newSpecies);
CalculateMonStats(mon);
}
daycareMon->mon = mon->box;
daycareMon->steps = 0;
ZeroMonData(mon);

View File

@ -25,6 +25,7 @@
#include "metatile_behavior.h"
#include "overworld.h"
#include "palette.h"
#include "party_menu.h"
#include "pokemon.h"
#include "pokeball.h"
#include "random.h"
@ -2154,21 +2155,24 @@ static void RefreshFollowerGraphics(struct ObjectEvent *objEvent)
}
}
static u16 GetOverworldCastformSpecies(void)
u16 GetOverworldWeatherSpecies(u16 species)
{
switch (GetCurrentWeather())
u32 i;
u32 weather = GetCurrentWeather();
const struct FormChange *formChanges = GetSpeciesFormChanges(species);
for (i = 0; formChanges != NULL && formChanges[i].method != FORM_CHANGE_TERMINATOR; i++)
{
case WEATHER_SUNNY_CLOUDS:
case WEATHER_DROUGHT:
return SPECIES_CASTFORM_SUNNY;
case WEATHER_RAIN:
case WEATHER_RAIN_THUNDERSTORM:
case WEATHER_DOWNPOUR:
return SPECIES_CASTFORM_RAINY;
case WEATHER_SNOW:
return SPECIES_CASTFORM_SNOWY;
// Unlike other form change checks, we don't do the "species != formChanges[i].targetSpecies" check
if (formChanges[i].method == FORM_CHANGE_OVERWORLD_WEATHER)
{
if (formChanges[i].param1 == weather)
return formChanges[i].targetSpecies;
else if (formChanges[i].param1 == WEATHER_NONE) // Set the default form for weather not defined in form change table
species = formChanges[i].targetSpecies;
}
}
return SPECIES_CASTFORM_NORMAL;
return species;
}
static bool8 GetMonInfo(struct Pokemon *mon, u16 *species, u8 *form, u8 *shiny)
@ -2189,8 +2193,8 @@ static bool8 GetMonInfo(struct Pokemon *mon, u16 *species, u8 *form, u8 *shiny)
case SPECIES_UNOWN:
*form = GET_UNOWN_LETTER(mon->box.personality);
break;
case SPECIES_CASTFORM: // form is based on overworld weather
*species = GetOverworldCastformSpecies();
default:
*species = GetOverworldWeatherSpecies(*species);
break;
}
return TRUE;
@ -5337,14 +5341,20 @@ static bool32 EndFollowerTransformEffect(struct ObjectEvent *objectEvent, struct
static bool32 TryStartFollowerTransformEffect(struct ObjectEvent *objectEvent, struct Sprite *sprite)
{
u32 multi;
if (GET_BASE_SPECIES_ID(OW_SPECIES(objectEvent)) == SPECIES_CASTFORM
&& OW_SPECIES(objectEvent) != (multi = GetOverworldCastformSpecies()))
struct Pokemon *mon;
u32 ability;
if (DoesSpeciesHaveFormChangeMethod(OW_SPECIES(objectEvent), FORM_CHANGE_OVERWORLD_WEATHER)
&& OW_SPECIES(objectEvent) != (multi = GetOverworldWeatherSpecies(OW_SPECIES(objectEvent))))
{
sprite->data[7] = TRANSFORM_TYPE_WEATHER << 8;
PlaySE(SE_M_MINIMIZE);
return TRUE;
}
else if ((Random() & 0xFFFF) < 18 && GetLocalWildMon(FALSE)
&& (OW_SPECIES(objectEvent) == SPECIES_MEW || OW_SPECIES(objectEvent) == SPECIES_DITTO))
if (OW_FOLLOWERS_COPY_WILD_PKMN
&& (MonKnowsMove(mon = GetFirstLiveMon(), MOVE_TRANSFORM)
|| (ability = GetMonAbility(mon)) == ABILITY_IMPOSTER || ability == ABILITY_ILLUSION)
&& (Random() & 0xFFFF) < 18 && GetLocalWildMon(FALSE))
{
sprite->data[7] = TRANSFORM_TYPE_RANDOM_WILD << 8;
PlaySE(SE_M_MINIMIZE);
@ -5378,7 +5388,7 @@ static bool8 UpdateFollowerTransformEffect(struct ObjectEvent *objectEvent, stru
break;
case TRANSFORM_TYPE_WEATHER:
multi = objectEvent->graphicsId;
objectEvent->graphicsId = GetOverworldCastformSpecies();
objectEvent->graphicsId = GetOverworldWeatherSpecies(OW_SPECIES(objectEvent));
if (!objectEvent->graphicsId)
{
objectEvent->graphicsId = multi;

View File

@ -246,11 +246,28 @@ void SetNextWeather(u8 weather)
gWeatherPtr->finishStep = 0;
}
static void UpdateWeatherForms(void)
{
s32 i;
for (i = 0; i < PARTY_SIZE; i++)
{
struct Pokemon *mon = &gPlayerParty[i];
u16 species = GetMonData(mon, MON_DATA_SPECIES);
u16 targetSpecies = GetOverworldWeatherSpecies(species);
if (species != targetSpecies)
{
SetMonData(mon, MON_DATA_SPECIES, &targetSpecies);
CalculateMonStats(mon);
}
}
}
void SetCurrentAndNextWeather(u8 weather)
{
PlayRainStoppingSoundEffect();
gWeatherPtr->currWeather = weather;
gWeatherPtr->nextWeather = weather;
UpdateWeatherForms();
}
void SetCurrentAndNextWeatherNoDelay(u8 weather)
@ -260,6 +277,7 @@ void SetCurrentAndNextWeatherNoDelay(u8 weather)
gWeatherPtr->nextWeather = weather;
// Overrides the normal delay during screen fading.
gWeatherPtr->readyForInit = TRUE;
UpdateWeatherForms();
}
static void Task_WeatherInit(u8 taskId)
@ -287,6 +305,7 @@ static void Task_WeatherMain(u8 taskId)
gWeatherPtr->palProcessingState = WEATHER_PAL_STATE_CHANGING_WEATHER;
gWeatherPtr->currWeather = gWeatherPtr->nextWeather;
gWeatherPtr->weatherChangeComplete = TRUE;
UpdateWeatherForms();
}
}
else

View File

@ -4425,7 +4425,7 @@ u32 GetGMaxTargetSpecies(u32 species)
{
const struct FormChange *formChanges = GetSpeciesFormChanges(species);
u32 i;
for (i = 0; formChanges[i].method != FORM_CHANGE_TERMINATOR; i++)
for (i = 0; formChanges != NULL && formChanges[i].method != FORM_CHANGE_TERMINATOR; i++)
{
if (formChanges[i].method == FORM_CHANGE_BATTLE_GIGANTAMAX)
return formChanges[i].targetSpecies;
@ -6588,6 +6588,7 @@ u32 GetFormChangeTargetSpeciesBoxMon(struct BoxPokemon *boxMon, u16 method, u32
targetSpecies = formChanges[i].targetSpecies;
break;
case FORM_CHANGE_WITHDRAW:
case FORM_CHANGE_DEPOSIT:
case FORM_CHANGE_FAINT:
case FORM_CHANGE_DAYS_PASSED:
targetSpecies = formChanges[i].targetSpecies;
@ -6623,7 +6624,7 @@ void TrySetDayLimitToFormChange(struct Pokemon *mon)
u16 species = GetMonData(mon, MON_DATA_SPECIES, NULL);
const struct FormChange *formChanges = GetSpeciesFormChanges(species);
for (i = 0; formChanges[i].method != FORM_CHANGE_TERMINATOR; i++)
for (i = 0; formChanges != NULL && formChanges[i].method != FORM_CHANGE_TERMINATOR; i++)
{
if (formChanges[i].method == FORM_CHANGE_DAYS_PASSED && species != formChanges[i].targetSpecies)
{
@ -6638,13 +6639,10 @@ bool32 DoesSpeciesHaveFormChangeMethod(u16 species, u16 method)
u32 i;
const struct FormChange *formChanges = GetSpeciesFormChanges(species);
if (formChanges != NULL)
for (i = 0; formChanges != NULL && formChanges[i].method != FORM_CHANGE_TERMINATOR; i++)
{
for (i = 0; formChanges[i].method != FORM_CHANGE_TERMINATOR; i++)
{
if (method == formChanges[i].method && species != formChanges[i].targetSpecies)
return TRUE;
}
if (method == formChanges[i].method && species != formChanges[i].targetSpecies)
return TRUE;
}
return FALSE;

View File

@ -550,6 +550,7 @@ EWRAM_DATA static u8 sMovingMonOrigBoxId = 0;
EWRAM_DATA static u8 sMovingMonOrigBoxPos = 0;
EWRAM_DATA static bool8 sAutoActionOn = 0;
EWRAM_DATA static bool8 sJustOpenedBag = 0;
EWRAM_DATA static bool8 sRefreshDisplayMonGfx = FALSE;
// Main tasks
static void Task_InitPokeStorage(u8);
@ -844,7 +845,7 @@ static void TilemapUtil_DrawPrev(u8);
static void TilemapUtil_Draw(u8);
// Form changing
void SetMonFormPSS(struct BoxPokemon *boxMon);
void SetMonFormPSS(struct BoxPokemon *boxMon, u32 method);
void UpdateSpeciesSpritePSS(struct BoxPokemon *boxmon);
static const u8 gText_JustOnePkmn[] = _("There is just one POKéMON with you.");
@ -3773,12 +3774,14 @@ static void GiveChosenBagItem(void)
{
struct Pokemon *mon = &gPlayerParty[pos];
SetMonData(&gPlayerParty[pos], MON_DATA_HELD_ITEM, &itemId);
SetMonFormPSS(&mon->box);
SetMonFormPSS(&mon->box, FORM_CHANGE_ITEM_HOLD);
UpdateSpeciesSpritePSS(&mon->box);
}
else
{
SetCurrentBoxMonData(pos, MON_DATA_HELD_ITEM, &itemId);
SetMonFormPSS(&gPokemonStoragePtr->boxes[StorageGetCurrentBox()][pos]);
SetMonFormPSS(&gPokemonStoragePtr->boxes[StorageGetCurrentBox()][pos], FORM_CHANGE_ITEM_HOLD);
UpdateSpeciesSpritePSS(&gPokemonStoragePtr->boxes[StorageGetCurrentBox()][pos]);
}
RemoveBagItem(itemId, 1);
@ -6346,11 +6349,14 @@ static void PlaceMon(void)
case CURSOR_AREA_IN_PARTY:
SetPlacedMonData(TOTAL_BOXES_COUNT, sCursorPosition);
SetPlacedMonSprite(TOTAL_BOXES_COUNT, sCursorPosition);
struct Pokemon *mon = &gPlayerParty[sCursorPosition];
UpdateSpeciesSpritePSS(&mon->box);
break;
case CURSOR_AREA_IN_BOX:
boxId = StorageGetCurrentBox();
SetPlacedMonData(boxId, sCursorPosition);
SetPlacedMonSprite(boxId, sCursorPosition);
UpdateSpeciesSpritePSS(&gPokemonStoragePtr->boxes[boxId][sCursorPosition]);
break;
default:
return;
@ -6390,12 +6396,15 @@ static void SetPlacedMonData(u8 boxId, u8 position)
if (boxId == TOTAL_BOXES_COUNT)
{
gPlayerParty[position] = sStorage->movingMon;
if (&gPlayerParty[position] == GetFirstLiveMon())
struct Pokemon *mon = &gPlayerParty[position];
if (mon == GetFirstLiveMon())
gFollowerSteps = 0;
SetMonFormPSS(&mon->box, FORM_CHANGE_WITHDRAW);
}
else
{
SetBoxMonAt(boxId, position, &sStorage->movingMon.box);
SetMonFormPSS(&gPokemonStoragePtr->boxes[boxId][position], FORM_CHANGE_DEPOSIT);
}
}
@ -6900,13 +6909,13 @@ static void ReshowDisplayMon(void)
TryRefreshDisplayMon();
}
void SetMonFormPSS(struct BoxPokemon *boxMon)
void SetMonFormPSS(struct BoxPokemon *boxMon, u32 method)
{
u16 targetSpecies = GetFormChangeTargetSpeciesBoxMon(boxMon, FORM_CHANGE_ITEM_HOLD, 0);
u16 targetSpecies = GetFormChangeTargetSpeciesBoxMon(boxMon, method, 0);
if (targetSpecies != GetBoxMonData(boxMon, MON_DATA_SPECIES, NULL))
{
SetBoxMonData(boxMon, MON_DATA_SPECIES, &targetSpecies);
UpdateSpeciesSpritePSS(boxMon);
sRefreshDisplayMonGfx = TRUE;
}
}
@ -8885,14 +8894,16 @@ static void TakeItemFromMon(u8 cursorArea, u8 cursorPos)
{
SetCurrentBoxMonData(cursorPos, MON_DATA_HELD_ITEM, &itemId);
SetBoxMonIconObjMode(cursorPos, ST_OAM_OBJ_BLEND);
SetMonFormPSS(&gPokemonStoragePtr->boxes[StorageGetCurrentBox()][cursorPos]);
SetMonFormPSS(&gPokemonStoragePtr->boxes[StorageGetCurrentBox()][cursorPos], FORM_CHANGE_ITEM_HOLD);
UpdateSpeciesSpritePSS(&gPokemonStoragePtr->boxes[StorageGetCurrentBox()][cursorPos]);
}
else
{
struct Pokemon *mon = &gPlayerParty[cursorPos];
SetMonData(&gPlayerParty[cursorPos], MON_DATA_HELD_ITEM, &itemId);
SetPartyMonIconObjMode(cursorPos, ST_OAM_OBJ_BLEND);
SetMonFormPSS(&mon->box);
SetMonFormPSS(&mon->box, FORM_CHANGE_ITEM_HOLD);
UpdateSpeciesSpritePSS(&mon->box);
}
sStorage->movingItemId = sStorage->displayMonItemId;
@ -8927,7 +8938,8 @@ static void SwapItemsWithMon(u8 cursorArea, u8 cursorPos)
itemId = GetCurrentBoxMonData(cursorPos, MON_DATA_HELD_ITEM);
SetCurrentBoxMonData(cursorPos, MON_DATA_HELD_ITEM, &sStorage->movingItemId);
sStorage->movingItemId = itemId;
SetMonFormPSS(&gPokemonStoragePtr->boxes[StorageGetCurrentBox()][cursorPos]);
SetMonFormPSS(&gPokemonStoragePtr->boxes[StorageGetCurrentBox()][cursorPos], FORM_CHANGE_ITEM_HOLD);
UpdateSpeciesSpritePSS(&gPokemonStoragePtr->boxes[StorageGetCurrentBox()][cursorPos]);
}
else
{
@ -8935,7 +8947,8 @@ static void SwapItemsWithMon(u8 cursorArea, u8 cursorPos)
itemId = GetMonData(&gPlayerParty[cursorPos], MON_DATA_HELD_ITEM);
SetMonData(&gPlayerParty[cursorPos], MON_DATA_HELD_ITEM, &sStorage->movingItemId);
sStorage->movingItemId = itemId;
SetMonFormPSS(&mon->box);
SetMonFormPSS(&mon->box, FORM_CHANGE_ITEM_HOLD);
UpdateSpeciesSpritePSS(&mon->box);
}
id = GetItemIconIdxByPosition(CURSOR_AREA_IN_HAND, 0);
@ -8957,14 +8970,16 @@ static void GiveItemToMon(u8 cursorArea, u8 cursorPos)
{
SetCurrentBoxMonData(cursorPos, MON_DATA_HELD_ITEM, &sStorage->movingItemId);
SetBoxMonIconObjMode(cursorPos, ST_OAM_OBJ_NORMAL);
SetMonFormPSS(&gPokemonStoragePtr->boxes[StorageGetCurrentBox()][cursorPos]);
SetMonFormPSS(&gPokemonStoragePtr->boxes[StorageGetCurrentBox()][cursorPos], FORM_CHANGE_ITEM_HOLD);
UpdateSpeciesSpritePSS(&gPokemonStoragePtr->boxes[StorageGetCurrentBox()][cursorPos]);
}
else
{
struct Pokemon *mon = &gPlayerParty[cursorPos];
SetMonData(&gPlayerParty[cursorPos], MON_DATA_HELD_ITEM, &sStorage->movingItemId);
SetPartyMonIconObjMode(cursorPos, ST_OAM_OBJ_NORMAL);
SetMonFormPSS(&mon->box);
SetMonFormPSS(&mon->box, FORM_CHANGE_ITEM_HOLD);
UpdateSpeciesSpritePSS(&mon->box);
}
}
@ -8984,14 +8999,16 @@ static void MoveItemFromMonToBag(u8 cursorArea, u8 cursorPos)
{
SetCurrentBoxMonData(cursorPos, MON_DATA_HELD_ITEM, &itemId);
SetBoxMonIconObjMode(cursorPos, ST_OAM_OBJ_BLEND);
SetMonFormPSS(&gPokemonStoragePtr->boxes[StorageGetCurrentBox()][cursorPos]);
SetMonFormPSS(&gPokemonStoragePtr->boxes[StorageGetCurrentBox()][cursorPos], FORM_CHANGE_ITEM_HOLD);
UpdateSpeciesSpritePSS(&gPokemonStoragePtr->boxes[StorageGetCurrentBox()][cursorPos]);
}
else
{
struct Pokemon *mon = &gPlayerParty[cursorPos];
SetMonData(&gPlayerParty[cursorPos], MON_DATA_HELD_ITEM, &itemId);
SetPartyMonIconObjMode(cursorPos, ST_OAM_OBJ_BLEND);
SetMonFormPSS(&mon->box);
SetMonFormPSS(&mon->box, FORM_CHANGE_ITEM_HOLD);
UpdateSpeciesSpritePSS(&mon->box);
}
}
@ -10051,8 +10068,12 @@ void UpdateSpeciesSpritePSS(struct BoxPokemon *boxMon)
sStorage->displayMonPalette = GetMonSpritePalFromSpeciesAndPersonality(species, isShiny, pid);
if (!sJustOpenedBag)
{
LoadDisplayMonGfx(species, pid);
StartDisplayMonMosaicEffect();
if (sRefreshDisplayMonGfx)
{
LoadDisplayMonGfx(species, pid);
StartDisplayMonMosaicEffect();
sRefreshDisplayMonGfx = FALSE;
}
// Recreate icon sprite
if (sInPartyMenu)
@ -10062,9 +10083,10 @@ void UpdateSpeciesSpritePSS(struct BoxPokemon *boxMon)
}
else
{
DestroyBoxMonIcon(sStorage->boxMonsSprites[sCursorPosition]);
DestroyBoxMonIconAtPosition(sCursorPosition);
CreateBoxMonIconAtPos(sCursorPosition);
SetBoxMonIconObjMode(sCursorPosition, GetBoxMonData(boxMon, MON_DATA_HELD_ITEM) == ITEM_NONE);
if (sStorage->boxOption == OPTION_MOVE_ITEMS)
SetBoxMonIconObjMode(sCursorPosition, (GetBoxMonData(boxMon, MON_DATA_HELD_ITEM) == ITEM_NONE ? ST_OAM_OBJ_NORMAL : ST_OAM_OBJ_BLEND));
}
}
sJustOpenedBag = FALSE;