diff --git a/include/battle_ai_switch_items.h b/include/battle_ai_switch_items.h index b52e792612e9..3c70132afe8f 100644 --- a/include/battle_ai_switch_items.h +++ b/include/battle_ai_switch_items.h @@ -5,5 +5,6 @@ void GetAIPartyIndexes(u32 battlerId, s32 *firstId, s32 *lastId); void AI_TrySwitchOrUseItem(u32 battler); u32 GetMostSuitableMonToSwitchInto(u32 battler, bool32 switchAfterMonKOd); bool32 ShouldSwitch(u32 battler, bool32 emitResult); +bool32 IsMonGrounded(u16 heldItemEffect, u32 ability, u8 type1, u8 type2); #endif // GUARD_BATTLE_AI_SWITCH_ITEMS_H diff --git a/include/config/pokemon.h b/include/config/pokemon.h index 7bf9450cbbbe..89e72cec17f5 100644 --- a/include/config/pokemon.h +++ b/include/config/pokemon.h @@ -51,6 +51,7 @@ #define P_TWO_FRAME_FRONT_SPRITES TRUE // In Pokémon Emerald, Pokémon front sprites always consist of two frames. This config can revert it to only use the first frame, as is the case in the other Gen 3 games. #define P_ONLY_OBTAINABLE_SHINIES FALSE // If TRUE, Pokémon encountered in the Battle Pyramid won't be shiny. #define P_NO_SHINIES_WITHOUT_POKEBALLS FALSE // If TRUE, Pokémon encountered when the player is out of Poké Balls won't be shiny +#define P_SHOW_DYNAMIC_TYPES FALSE // If TRUE, moves with dynamic type changes will be reflected as their current type in battle/summary screens. // Learnset helper toggles #define P_LEARNSET_HELPER_TEACHABLE TRUE // If TRUE, teachable_learnsets.h will be populated by tools/learnset_helpers/teachable.py using the included JSON files based on available TMs and tutors. diff --git a/include/pokemon.h b/include/pokemon.h index a67241fb145e..4be40addbf91 100644 --- a/include/pokemon.h +++ b/include/pokemon.h @@ -882,5 +882,7 @@ const u8 *GetMoveName(u16 moveId); const u8 *GetMoveAnimationScript(u16 moveId); void UpdateDaysPassedSinceFormChange(u16 days); void TrySetDayLimitToFormChange(struct Pokemon *mon); +u8 CheckDynamicMoveType(struct Pokemon *mon, u32 move, u32 battler); +u8 CalculateHiddenPowerType(struct Pokemon *mon); #endif // GUARD_POKEMON_H diff --git a/src/battle_ai_switch_items.c b/src/battle_ai_switch_items.c index a0a44cb98d46..d85cf28d3702 100644 --- a/src/battle_ai_switch_items.c +++ b/src/battle_ai_switch_items.c @@ -1318,7 +1318,7 @@ static u32 GetBestMonDmg(struct Pokemon *party, int firstId, int lastId, u8 inva return bestMonId; } -static bool32 IsMonGrounded(u16 heldItemEffect, u32 ability, u8 type1, u8 type2) +bool32 IsMonGrounded(u16 heldItemEffect, u32 ability, u8 type1, u8 type2) { // List that makes mon not grounded if (type1 == TYPE_FLYING || type2 == TYPE_FLYING || ability == ABILITY_LEVITATE diff --git a/src/battle_controller_player.c b/src/battle_controller_player.c index 6dcea9eb98c1..f49a8f95c701 100644 --- a/src/battle_controller_player.c +++ b/src/battle_controller_player.c @@ -1716,15 +1716,14 @@ static void MoveSelectionDisplayMoveType(u32 battler) u8 type; u32 speciesId; struct ChooseMoveStruct *moveInfo = (struct ChooseMoveStruct *)(&gBattleResources->bufferA[battler][4]); - txtPtr = StringCopy(gDisplayedStringBattle, gText_MoveInterfaceType); - type = gMovesInfo[moveInfo->moves[gMoveSelectionCursor[battler]]].type; if (moveInfo->moves[gMoveSelectionCursor[battler]] == MOVE_TERA_BLAST) { if (IsGimmickSelected(battler, GIMMICK_TERA) || GetActiveGimmick(battler) == GIMMICK_TERA) type = GetBattlerTeraType(battler); + end = StringCopy(txtPtr, gTypesInfo[type].name); } else if (moveInfo->moves[gMoveSelectionCursor[battler]] == MOVE_IVY_CUDGEL) { @@ -1734,21 +1733,33 @@ static void MoveSelectionDisplayMoveType(u32 battler) || speciesId == SPECIES_OGERPON_HEARTHFLAME_MASK || speciesId == SPECIES_OGERPON_HEARTHFLAME_MASK_TERA || speciesId == SPECIES_OGERPON_CORNERSTONE_MASK || speciesId == SPECIES_OGERPON_CORNERSTONE_MASK_TERA) type = gBattleMons[battler].type2; + end = StringCopy(txtPtr, gTypesInfo[type].name); } // Max Guard is a Normal-type move else if (gMovesInfo[moveInfo->moves[gMoveSelectionCursor[battler]]].category == DAMAGE_CATEGORY_STATUS && (GetActiveGimmick(battler) == GIMMICK_DYNAMAX || IsGimmickSelected(battler, GIMMICK_DYNAMAX))) { type = TYPE_NORMAL; + end = StringCopy(txtPtr, gTypesInfo[type].name); } else if (moveInfo->moves[gMoveSelectionCursor[battler]] == MOVE_TERA_STARSTORM) { if (gBattleMons[battler].species == SPECIES_TERAPAGOS_STELLAR || (IsGimmickSelected(battler, GIMMICK_TERA) && gBattleMons[battler].species == SPECIES_TERAPAGOS_TERASTAL)) type = TYPE_STELLAR; + end = StringCopy(txtPtr, gTypesInfo[type].name); + } + else if (P_SHOW_DYNAMIC_TYPES) + { + struct Pokemon *mon = &gPlayerParty[gBattlerPartyIndexes[battler]]; + type = CheckDynamicMoveType(mon, moveInfo->moves[gMoveSelectionCursor[battler]], battler); + end = StringCopy(txtPtr, gTypesInfo[type].name); + } + else + { + end = StringCopy(txtPtr, gTypesInfo[type].name); } - end = StringCopy(txtPtr, gTypesInfo[type].name); PrependFontIdToFit(txtPtr, end, FONT_NORMAL, WindowWidthPx(B_WIN_MOVE_TYPE) - 25); BattlePutTextOnWindow(gDisplayedStringBattle, B_WIN_MOVE_TYPE); } diff --git a/src/pokemon.c b/src/pokemon.c index c8d52ddb9d0d..1807b675df6c 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -2,6 +2,7 @@ #include "malloc.h" #include "apprentice.h" #include "battle.h" +#include "battle_ai_switch_items.h" #include "battle_anim.h" #include "battle_controllers.h" #include "battle_message.h" @@ -6941,3 +6942,149 @@ void UpdateDaysPassedSinceFormChange(u16 days) } } } + +u8 CheckDynamicMoveType(struct Pokemon *mon, u32 move, u32 battler) +{ + u32 type = gMovesInfo[move].type; + u32 species = GetMonData(mon, MON_DATA_SPECIES); + u32 heldItem = GetMonData(mon, MON_DATA_HELD_ITEM, 0); + u32 heldItemEffect = ItemId_GetHoldEffect(heldItem); + u32 ability = GetMonAbility(mon); + u32 type1 = gSpeciesInfo[species].types[0]; + u32 type2 = gSpeciesInfo[species].types[1]; + + if (move == MOVE_IVY_CUDGEL + && (species == SPECIES_OGERPON_WELLSPRING_MASK || species == SPECIES_OGERPON_WELLSPRING_MASK_TERA + || species == SPECIES_OGERPON_HEARTHFLAME_MASK || species == SPECIES_OGERPON_HEARTHFLAME_MASK_TERA + || species == SPECIES_OGERPON_CORNERSTONE_MASK || species == SPECIES_OGERPON_CORNERSTONE_MASK_TERA)) + { + type = type2; + } + else if (move == MOVE_STRUGGLE) + { + return TYPE_NORMAL; + } + else if (move == MOVE_TERA_BLAST && GetActiveGimmick(battler) == GIMMICK_TERA && gBattleMons[battler].species == species) + { + return GetMonData(mon, MON_DATA_TERA_TYPE); + } + else if (move == MOVE_TERA_STARSTORM && species == SPECIES_TERAPAGOS_STELLAR) + { + return TYPE_STELLAR; + } + else if (move == MOVE_HIDDEN_POWER) + { + return CalculateHiddenPowerType(mon); + } + else if (move == MOVE_AURA_WHEEL && species == SPECIES_MORPEKO_HANGRY) + { + type = TYPE_DARK; + } + else if (gMovesInfo[move].effect == EFFECT_CHANGE_TYPE_ON_ITEM) + { + if (heldItemEffect == gMovesInfo[move].argument) + return ItemId_GetSecondaryId(heldItem); + else + return TYPE_NORMAL; + } + else if (move == MOVE_NATURAL_GIFT) + { + if (ItemId_GetPocket(heldItem) == POCKET_BERRIES) + return gNaturalGiftTable[ITEM_TO_BERRY(heldItem)].type; + else + return TYPE_NORMAL; + } + else if (move == MOVE_RAGING_BULL + && (species == SPECIES_TAUROS_PALDEAN_COMBAT_BREED + || species == SPECIES_TAUROS_PALDEAN_BLAZE_BREED + || species == SPECIES_TAUROS_PALDEAN_AQUA_BREED)) + { + return type2; + } + else if (move == MOVE_REVELATION_DANCE) + { + if (gBattleMons[battler].species != species && type1 != TYPE_MYSTERY) + type = type1; + else if (gBattleMons[battler].species != species && type2 != TYPE_MYSTERY) + type = type2; + else if (GetBattlerTeraType(battler) != TYPE_STELLAR && (GetActiveGimmick(battler) == GIMMICK_TERA || IsGimmickSelected(battler, GIMMICK_TERA))) + type = GetMonData(mon, MON_DATA_TERA_TYPE); + else if (gBattleMons[battler].type1 != TYPE_MYSTERY) + type = gBattleMons[battler].type1; + else if (gBattleMons[battler].type2 != TYPE_MYSTERY) + type = gBattleMons[battler].type2; + else if (gBattleMons[battler].type3 != TYPE_MYSTERY) + type = gBattleMons[battler].type3; + } + else if (gMovesInfo[move].effect == EFFECT_TERRAIN_PULSE + && ((IsMonGrounded(heldItemEffect, ability, type1, type2) && gBattleMons[battler].species != species) + || (IsBattlerTerrainAffected(battler, STATUS_FIELD_TERRAIN_ANY) && gBattleMons[battler].species == species))) + { + if (gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN) + return TYPE_ELECTRIC; + else if (gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN) + return TYPE_GRASS; + else if (gFieldStatuses & STATUS_FIELD_MISTY_TERRAIN) + return TYPE_FAIRY; + else if (gFieldStatuses & STATUS_FIELD_PSYCHIC_TERRAIN) + return TYPE_PSYCHIC; + else //failsafe + type = TYPE_NORMAL; + } + + if (gMovesInfo[move].effect == EFFECT_WEATHER_BALL && WEATHER_HAS_EFFECT) + { + if (gBattleWeather & B_WEATHER_RAIN && heldItemEffect != HOLD_EFFECT_UTILITY_UMBRELLA) + return TYPE_WATER; + else if (gBattleWeather & B_WEATHER_SANDSTORM) + return TYPE_ROCK; + else if (gBattleWeather & B_WEATHER_SUN && heldItemEffect != HOLD_EFFECT_UTILITY_UMBRELLA) + return TYPE_FIRE; + else if (gBattleWeather & (B_WEATHER_SNOW | B_WEATHER_HAIL)) + return TYPE_ICE; + else + return TYPE_NORMAL; + } + + if (ability == ABILITY_NORMALIZE && gMovesInfo[move].type != TYPE_NORMAL && GetActiveGimmick(battler) != GIMMICK_Z_MOVE) + type = TYPE_NORMAL; + + if ((gFieldStatuses & STATUS_FIELD_ION_DELUGE && type == TYPE_NORMAL) || gStatuses4[battler] & STATUS4_ELECTRIFIED) + type = TYPE_ELECTRIC; + + if (gMovesInfo[move].type == TYPE_NORMAL && gMovesInfo[move].category != DAMAGE_CATEGORY_STATUS) + { + switch (ability) + { + case ABILITY_PIXILATE: return TYPE_FAIRY; + case ABILITY_REFRIGERATE: return TYPE_ICE; + case ABILITY_AERILATE: return TYPE_FLYING; + case ABILITY_GALVANIZE: return TYPE_ELECTRIC; + default: break; + } + } + + if (ability == ABILITY_LIQUID_VOICE && gMovesInfo[move].soundMove == TRUE) + return TYPE_WATER; + + return type; +} + +u8 CalculateHiddenPowerType(struct Pokemon *mon) +{ + u32 typehp; + u32 type; + u8 typeBits = ((GetMonData(mon, MON_DATA_HP_IV) & 1) << 0) + | ((GetMonData(mon, MON_DATA_ATK_IV) & 1) << 1) + | ((GetMonData(mon, MON_DATA_DEF_IV) & 1) << 2) + | ((GetMonData(mon, MON_DATA_SPEED_IV) & 1) << 3) + | ((GetMonData(mon, MON_DATA_SPATK_IV) & 1) << 4) + | ((GetMonData(mon, MON_DATA_SPDEF_IV) & 1) << 5); + + type = (15 * typeBits) / 63 + 2; + if (type >= TYPE_MYSTERY) + type++; + type |= 0xC0; + typehp = type & 0x3F; + return typehp; +} diff --git a/src/pokemon_summary_screen.c b/src/pokemon_summary_screen.c index e81ef0b0669f..a1ebc2b18818 100644 --- a/src/pokemon_summary_screen.c +++ b/src/pokemon_summary_screen.c @@ -3957,16 +3957,29 @@ static void SetMonTypeIcons(void) static void SetMoveTypeIcons(void) { - u8 i; + u32 i; + u32 type; struct PokeSummary *summary = &sMonSummaryScreen->summary; + struct Pokemon *mon = &sMonSummaryScreen->currentMon; + for (i = 0; i < MAX_MON_MOVES; i++) { if (summary->moves[i] != MOVE_NONE) { - SetTypeSpritePosAndPal(gMovesInfo[summary->moves[i]].type, 85, 32 + (i * 16), i + SPRITE_ARR_ID_TYPE); + if (P_SHOW_DYNAMIC_TYPES) + { + type = CheckDynamicMoveType(mon, summary->moves[i], 0); + SetTypeSpritePosAndPal(type, 85, 32 + (i * 16), i + SPRITE_ARR_ID_TYPE); + } + else + { + SetTypeSpritePosAndPal(gMovesInfo[summary->moves[i]].type, 85, 32 + (i * 16), i + SPRITE_ARR_ID_TYPE); + } } else + { SetSpriteInvisibility(i + SPRITE_ARR_ID_TYPE, TRUE); + } } } @@ -3985,6 +3998,12 @@ static void SetContestMoveTypeIcons(void) static void SetNewMoveTypeIcon(void) { + u32 type = gMovesInfo[sMonSummaryScreen->newMove].type; + struct Pokemon *mon = &sMonSummaryScreen->currentMon; + + if (P_SHOW_DYNAMIC_TYPES) + type = CheckDynamicMoveType(mon, sMonSummaryScreen->newMove, 0); + if (sMonSummaryScreen->newMove == MOVE_NONE) { SetSpriteInvisibility(SPRITE_ARR_ID_TYPE + 4, TRUE); @@ -3992,9 +4011,13 @@ static void SetNewMoveTypeIcon(void) else { if (sMonSummaryScreen->currPageIndex == PSS_PAGE_BATTLE_MOVES) - SetTypeSpritePosAndPal(gMovesInfo[sMonSummaryScreen->newMove].type, 85, 96, SPRITE_ARR_ID_TYPE + 4); + { + SetTypeSpritePosAndPal(type, 85, 96, SPRITE_ARR_ID_TYPE + 4); + } else + { SetTypeSpritePosAndPal(NUMBER_OF_MON_TYPES + gMovesInfo[sMonSummaryScreen->newMove].contestCategory, 85, 96, SPRITE_ARR_ID_TYPE + 4); + } } }