From acc735591d6d180c9b4f635bf99e0232aa67e0d0 Mon Sep 17 00:00:00 2001 From: Eduardo Quezada Date: Sat, 27 Jul 2024 20:47:23 -0400 Subject: [PATCH] Follower form change using form change table --- include/constants/form_change_types.h | 4 + src/battle_util.c | 176 ++++++++++++-------------- src/data/pokemon/form_change_tables.h | 7 + src/event_object_movement.c | 42 ++---- src/pokemon.c | 13 +- 5 files changed, 113 insertions(+), 129 deletions(-) diff --git a/include/constants/form_change_types.h b/include/constants/form_change_types.h index 74bc16cf2f5c..9603a8d3129f 100644 --- a/include/constants/form_change_types.h +++ b/include/constants/form_change_types.h @@ -134,4 +134,8 @@ // param1: amount of days #define FORM_CHANGE_DAYS_PASSED 23 +// Form change that activates when overworld weather changes. +// param1: weather to check. +#define FORM_CHANGE_OVERWORLD_WEATHER 24 + #endif // GUARD_CONSTANTS_FORM_CHANGE_TYPES_H diff --git a/src/battle_util.c b/src/battle_util.c index 8523471ee541..dba6af56cf43 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -10612,20 +10612,17 @@ 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: - 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: + if (formChanges[i].param1 == heldItemId) + return TRUE; + break; } } return FALSE; @@ -10756,93 +10753,88 @@ u16 GetBattleFormChangeTargetSpecies(u32 battler, u16 method) u16 species = gBattleMons[battler].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)) - { - // 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; - } - } - 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)) + 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) { - targetSpecies = formChanges[i].targetSpecies; + 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_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; } + 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; } } } diff --git a/src/data/pokemon/form_change_tables.h b/src/data/pokemon/form_change_tables.h index 52f888a55b32..118d726390e2 100644 --- a/src/data/pokemon/form_change_tables.h +++ b/src/data/pokemon/form_change_tables.h @@ -410,6 +410,13 @@ static const struct FormChange sGlalieFormChangeTable[] = { #if P_FAMILY_CASTFORM static const struct FormChange sCastformFormChangeTable[] = { + {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}, #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}, diff --git a/src/event_object_movement.c b/src/event_object_movement.c index e064a5c5ed38..bc3ef7daa292 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -2018,36 +2018,22 @@ static void RefreshFollowerGraphics(struct ObjectEvent *objEvent) } } -static u16 GetOverworldWeatherSpecies(u32 species) +static u16 GetOverworldWeatherSpecies(u16 species) { + u32 i; u32 weather = GetCurrentWeather(); - u32 baseSpecies = GET_BASE_SPECIES_ID(species); + const struct FormChange *formChanges = GetSpeciesFormChanges(species); - if (baseSpecies == SPECIES_CASTFORM) - { - switch (GetCurrentWeather()) - { - 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; - } - return SPECIES_CASTFORM_NORMAL; - } - else if (baseSpecies == SPECIES_CHERRIM) + for (i = 0; formChanges != NULL && formChanges[i].method != FORM_CHANGE_TERMINATOR; i++) { - switch (GetCurrentWeather()) + // Unlike other form change checks, we don't do the "species != formChanges[i].targetSpecies" check + if (formChanges[i].method == FORM_CHANGE_OVERWORLD_WEATHER) { - case WEATHER_SUNNY_CLOUDS: - case WEATHER_DROUGHT: - return SPECIES_CHERRIM_SUNSHINE; + 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_CHERRIM_OVERCAST; } return species; } @@ -2064,14 +2050,12 @@ static bool8 GetMonInfo(struct Pokemon *mon, u16 *species, u8 *form, u8 *shiny) } *species = GetMonData(mon, MON_DATA_SPECIES); *shiny = IsMonShiny(mon); - switch (GET_BASE_SPECIES_ID(*species)) + switch (*species) { case SPECIES_UNOWN: *form = GET_UNOWN_LETTER(mon->box.personality); break; - // form is based on overworld weather - case SPECIES_CASTFORM: - case SPECIES_CHERRIM: + default: *species = GetOverworldWeatherSpecies(*species); break; } @@ -5228,7 +5212,7 @@ static bool32 TryStartFollowerTransformEffect(struct ObjectEvent *objectEvent, s u32 multi; u32 baseSpecies = GET_BASE_SPECIES_ID(OW_SPECIES(objectEvent)); if (OW_FOLLOWERS_WEATHER_FORMS - && (baseSpecies == SPECIES_CASTFORM || baseSpecies == SPECIES_CHERRIM) + && DoesSpeciesHaveFormChangeMethod(OW_SPECIES(objectEvent), FORM_CHANGE_BATTLE_WEATHER) && OW_SPECIES(objectEvent) != (multi = GetOverworldWeatherSpecies(OW_SPECIES(objectEvent)))) { sprite->data[7] = TRANSFORM_TYPE_WEATHER << 8; diff --git a/src/pokemon.c b/src/pokemon.c index befede6fd115..cbc22bfd4dbe 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -4401,7 +4401,7 @@ static 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; @@ -6579,7 +6579,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) { @@ -6594,13 +6594,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;