diff --git a/include/config/overworld.h b/include/config/overworld.h index 5a6cb1e955..5b774ec558 100644 --- a/include/config/overworld.h +++ b/include/config/overworld.h @@ -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. diff --git a/include/constants/form_change_types.h b/include/constants/form_change_types.h index 8af9740cad..8cf2e37e1b 100644 --- a/include/constants/form_change_types.h +++ b/include/constants/form_change_types.h @@ -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 diff --git a/include/event_object_movement.h b/include/event_object_movement.h index f6aa18280f..ce30769f17 100644 --- a/include/event_object_movement.h +++ b/include/event_object_movement.h @@ -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); diff --git a/src/battle_util.c b/src/battle_util.c index fc916a76c4..d9b742d023 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -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; } } } diff --git a/src/data/pokemon/form_change_tables.h b/src/data/pokemon/form_change_tables.h index 340bdb1fa6..1cab4fc833 100644 --- a/src/data/pokemon/form_change_tables.h +++ b/src/data/pokemon/form_change_tables.h @@ -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 diff --git a/src/daycare.c b/src/daycare.c index 26b86ec1e2..6b6b84ac58 100644 --- a/src/daycare.c +++ b/src/daycare.c @@ -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); diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 581cd94620..80423aa316 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -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; diff --git a/src/field_weather.c b/src/field_weather.c index 7d3c4caf4f..ae69289d8a 100644 --- a/src/field_weather.c +++ b/src/field_weather.c @@ -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 diff --git a/src/pokemon.c b/src/pokemon.c index efb4d91abb..fcbde3ba4a 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -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; diff --git a/src/pokemon_storage_system.c b/src/pokemon_storage_system.c index 81dff0f435..9714293931 100644 --- a/src/pokemon_storage_system.c +++ b/src/pokemon_storage_system.c @@ -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;