diff --git a/include/battle_util.h b/include/battle_util.h index 120c0b7e9..4f263e8da 100644 --- a/include/battle_util.h +++ b/include/battle_util.h @@ -144,14 +144,12 @@ u16 CalcTypeEffectivenessMultiplier(u16 move, u8 moveType, u8 battlerAtk, u8 bat u16 CalcPartyMonTypeEffectivenessMultiplier(u16 move, u16 speciesDef, u16 abilityDef); u16 GetTypeModifier(u8 atkType, u8 defType); s32 GetStealthHazardDamage(u8 hazardType, u8 battlerId); -u16 GetMegaEvolutionSpecies(u16 preEvoSpecies, u16 heldItemId); -u16 GetPrimalReversionSpecies(u16 preSpecies, u16 heldItemId); -u16 GetWishMegaEvolutionSpecies(u16 preEvoSpecies, u16 moveId1, u16 moveId2, u16 moveId3, u16 moveId4); +bool32 DoesSpeciesUseHoldItemToChangeForm(u16 species, u16 heldItemId); bool32 CanMegaEvolve(u8 battlerId); -void UndoMegaEvolution(u32 monId); bool32 IsBattlerMegaEvolved(u8 battlerId); bool32 IsBattlerPrimalReverted(u8 battlerId); -void TryBattleFormChange(u8 battlerId, u16 method); +u16 GetBattleFormChangeTargetSpecies(u8 battlerId, u16 method); +bool32 TryBattleFormChange(u8 battlerId, u16 method); bool32 DoBattlersShareType(u32 battler1, u32 battler2); bool32 CanBattlerGetOrLoseItem(u8 battlerId, u16 itemId); struct Pokemon *GetIllusionMonPtr(u32 battlerId); diff --git a/include/constants/pokemon.h b/include/constants/pokemon.h index f31fa8d6c..31b7e72fb 100644 --- a/include/constants/pokemon.h +++ b/include/constants/pokemon.h @@ -323,9 +323,14 @@ #define FORM_CHANGE_BATTLE_BEGIN 6 #define FORM_CHANGE_BATTLE_END 7 #define FORM_CHANGE_BATTLE_SWITCH 8 -#define FORM_CHANGE_MEGA_EVOLUTION_ITEM 9 -#define FORM_CHANGE_MEGA_EVOLUTION_MOVE 10 -#define FORM_CHANGE_PRIMAL_REVERSION 11 +#define FORM_CHANGE_BATTLE_HP_PERCENT 9 +#define FORM_CHANGE_MEGA_EVOLUTION_ITEM 10 +#define FORM_CHANGE_MEGA_EVOLUTION_MOVE 11 +#define FORM_CHANGE_PRIMAL_REVERSION 12 + +// FORM_CHANGE_BATTLE_HP_PERCENT param2 arguments +#define HP_HIGHER_THAN 1 +#define HP_LOWER_EQ_THAN 2 #define MON_PIC_WIDTH 64 #define MON_PIC_HEIGHT 64 diff --git a/include/pokemon.h b/include/pokemon.h index 10b132f48..f7f5ea6de 100644 --- a/include/pokemon.h +++ b/include/pokemon.h @@ -562,7 +562,8 @@ u8 *MonSpritesGfxManager_GetSpritePtr(u8 managerId, u8 spriteNum); u16 GetFormSpeciesId(u16 speciesId, u8 formId); u8 GetFormIdFromFormSpeciesId(u16 formSpeciesId); u16 GetFormChangeTargetSpecies(struct Pokemon *mon, u16 method, u32 arg); -u16 GetFormChangeTargetSpeciesBoxMon(struct BoxPokemon *mon, u16 method, u32 arg); +u16 GetFormChangeTargetSpeciesBoxMon(struct BoxPokemon *boxMon, u16 method, u32 arg); +bool32 DoesSpeciesHaveFormChangeMethod(u16 species, u16 method); u16 MonTryLearningNewMoveEvolution(struct Pokemon *mon, bool8 firstMove); bool32 ShouldShowFemaleDifferences(u16 species, u32 personality); bool32 TryFormChange(u32 monId, u32 side, u16 method); diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index b1154e793..0998d0a48 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -3043,12 +3043,8 @@ static bool32 IsPinchBerryItemEffect(u16 holdEffect) case HOLD_EFFECT_SP_DEFENSE_UP: case HOLD_EFFECT_CRITICAL_UP: case HOLD_EFFECT_RANDOM_STAT_UP: - #ifdef HOLD_EFFECT_CUSTAP_BERRY case HOLD_EFFECT_CUSTAP_BERRY: - #endif - #ifdef HOLD_EFFECT_MICLE_BERRY case HOLD_EFFECT_MICLE_BERRY: - #endif return TRUE; } diff --git a/src/battle_main.c b/src/battle_main.c index df8e24551..907fb1aa4 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -3248,8 +3248,6 @@ void FaintClearSetData(void) Ai_UpdateFaintData(gActiveBattler); TryBattleFormChange(gActiveBattler, FORM_CHANGE_FAINT); - if (GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER) - UndoMegaEvolution(gBattlerPartyIndexes[gActiveBattler]); gBattleStruct->overwrittenAbilities[gActiveBattler] = ABILITY_NONE; @@ -3683,7 +3681,7 @@ static void TryDoEventsBeforeFirstTurn(void) for (i = 0; i < gBattlersCount; i++) { if (GetBattlerHoldEffect(i, TRUE) == HOLD_EFFECT_PRIMAL_ORB - && GetPrimalReversionSpecies(gBattleMons[i].species, gBattleMons[i].item) != SPECIES_NONE) + && GetBattleFormChangeTargetSpecies(i, FORM_CHANGE_PRIMAL_REVERSION) != SPECIES_NONE) { gBattlerAttacker = i; BattleScriptExecute(BattleScript_PrimalReversion); @@ -5200,16 +5198,15 @@ static void HandleEndTurn_FinishBattle(void) #endif for (i = 0; i < PARTY_SIZE; i++) { - UndoMegaEvolution(i); - TryFormChange(i, B_SIDE_PLAYER, FORM_CHANGE_BATTLE_END); + bool32 changedForm = TryFormChange(i, B_SIDE_PLAYER, FORM_CHANGE_BATTLE_END); DoBurmyFormChange(i); + // Clear original species field + gBattleStruct->changedSpecies[i] = SPECIES_NONE; + #if B_RECALCULATE_STATS >= GEN_5 // Recalculate the stats of every party member before the end - if (GetMonData(&gPlayerParty[i], MON_DATA_SPECIES2) != SPECIES_NONE - && GetMonData(&gPlayerParty[i], MON_DATA_SPECIES2) != SPECIES_EGG) - { + if (!changedForm) CalculateMonStats(&gPlayerParty[i]); - } #endif } // Clear battle mon species to avoid a bug on the next battle that causes diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index d2de985a7..5ea145c05 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -8800,10 +8800,10 @@ static void Cmd_various(void) if (gBattlescriptCurrInstr[3] == 0) { //Checks regular Mega Evolution - u16 megaSpecies = GetMegaEvolutionSpecies(gBattleMons[gActiveBattler].species, gBattleMons[gActiveBattler].item); + u16 megaSpecies = GetBattleFormChangeTargetSpecies(gActiveBattler, FORM_CHANGE_MEGA_EVOLUTION_ITEM); //Checks Wish Mega Evolution if (megaSpecies == SPECIES_NONE) - megaSpecies = GetWishMegaEvolutionSpecies(gBattleMons[gActiveBattler].species, gBattleMons[gActiveBattler].moves[0], gBattleMons[gActiveBattler].moves[1], gBattleMons[gActiveBattler].moves[2], gBattleMons[gActiveBattler].moves[3]); + megaSpecies = GetBattleFormChangeTargetSpecies(gActiveBattler, FORM_CHANGE_MEGA_EVOLUTION_MOVE); gBattleMons[gActiveBattler].species = megaSpecies; PREPARE_SPECIES_BUFFER(gBattleTextBuff1, gBattleMons[gActiveBattler].species); @@ -8836,7 +8836,7 @@ static void Cmd_various(void) // Change species. if (gBattlescriptCurrInstr[3] == 0) { - gBattleMons[gActiveBattler].species = GetPrimalReversionSpecies(gBattleMons[gActiveBattler].species, gBattleMons[gActiveBattler].item); + gBattleMons[gActiveBattler].species = GetBattleFormChangeTargetSpecies(gActiveBattler, FORM_CHANGE_PRIMAL_REVERSION); PREPARE_SPECIES_BUFFER(gBattleTextBuff1, gBattleMons[gActiveBattler].species); BtlController_EmitSetMonData(BUFFER_A, REQUEST_SPECIES_BATTLE, gBitTable[gBattlerPartyIndexes[gActiveBattler]], sizeof(gBattleMons[gActiveBattler].species), &gBattleMons[gActiveBattler].species); @@ -9561,7 +9561,7 @@ static void Cmd_various(void) return; case VARIOUS_JUMP_IF_CANT_REVERT_TO_PRIMAL: { - if (GetPrimalReversionSpecies(gBattleMons[gActiveBattler].species, gBattleMons[gActiveBattler].item) == SPECIES_NONE) + if (GetBattleFormChangeTargetSpecies(gActiveBattler, FORM_CHANGE_PRIMAL_REVERSION) == SPECIES_NONE) gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 3); else gBattlescriptCurrInstr += 7; diff --git a/src/battle_util.c b/src/battle_util.c index 544eef1fd..27b8cf945 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -4170,51 +4170,6 @@ static bool32 TryChangeBattleTerrain(u32 battler, u32 statusFlag, u8 *timer) return FALSE; } -static bool32 ShouldChangeFormHpBased(u32 battler) -{ - // Ability, form >, form <=, hp divided - static const u16 forms[][4] = - { - {ABILITY_ZEN_MODE, SPECIES_DARMANITAN, SPECIES_DARMANITAN_ZEN_MODE, 2}, - {ABILITY_SHIELDS_DOWN, SPECIES_MINIOR, SPECIES_MINIOR_CORE_RED, 2}, - {ABILITY_SHIELDS_DOWN, SPECIES_MINIOR_METEOR_BLUE, SPECIES_MINIOR_CORE_BLUE, 2}, - {ABILITY_SHIELDS_DOWN, SPECIES_MINIOR_METEOR_GREEN, SPECIES_MINIOR_CORE_GREEN, 2}, - {ABILITY_SHIELDS_DOWN, SPECIES_MINIOR_METEOR_INDIGO, SPECIES_MINIOR_CORE_INDIGO, 2}, - {ABILITY_SHIELDS_DOWN, SPECIES_MINIOR_METEOR_ORANGE, SPECIES_MINIOR_CORE_ORANGE, 2}, - {ABILITY_SHIELDS_DOWN, SPECIES_MINIOR_METEOR_VIOLET, SPECIES_MINIOR_CORE_VIOLET, 2}, - {ABILITY_SHIELDS_DOWN, SPECIES_MINIOR_METEOR_YELLOW, SPECIES_MINIOR_CORE_YELLOW, 2}, - {ABILITY_SCHOOLING, SPECIES_WISHIWASHI_SCHOOL, SPECIES_WISHIWASHI, 4}, - {ABILITY_GULP_MISSILE, SPECIES_CRAMORANT, SPECIES_CRAMORANT_GORGING, 2}, - {ABILITY_GULP_MISSILE, SPECIES_CRAMORANT, SPECIES_CRAMORANT_GULPING, 1}, - {ABILITY_ZEN_MODE, SPECIES_DARMANITAN_GALARIAN, SPECIES_DARMANITAN_ZEN_MODE_GALARIAN, 2}, - }; - u32 i; - u16 battlerAbility = GetBattlerAbility(battler); - - if (gBattleMons[battler].status2 & STATUS2_TRANSFORMED) - return FALSE; - - for (i = 0; i < ARRAY_COUNT(forms); i++) - { - if (battlerAbility == forms[i][0]) - { - if (gBattleMons[battler].species == forms[i][2] - && gBattleMons[battler].hp > gBattleMons[battler].maxHP / forms[i][3]) - { - gBattleMons[battler].species = forms[i][1]; - return TRUE; - } - if (gBattleMons[battler].species == forms[i][1] - && gBattleMons[battler].hp <= gBattleMons[battler].maxHP / forms[i][3]) - { - gBattleMons[battler].species = forms[i][2]; - return TRUE; - } - } - } - return FALSE; -} - static u8 ForewarnChooseMove(u32 battler) { struct Forewarn { @@ -4738,7 +4693,7 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move // Fallthrough case ABILITY_ZEN_MODE: case ABILITY_SHIELDS_DOWN: - if (ShouldChangeFormHpBased(battler)) + if (TryBattleFormChange(battler, FORM_CHANGE_BATTLE_HP_PERCENT)) { BattleScriptPushCursorAndCallback(BattleScript_AttackerFormChangeEnd3); effect++; @@ -4959,15 +4914,9 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move // Fallthrough case ABILITY_ZEN_MODE: case ABILITY_SHIELDS_DOWN: - if ((effect = ShouldChangeFormHpBased(battler))) - BattleScriptPushCursorAndCallback(BattleScript_AttackerFormChangeEnd3); - break; case ABILITY_POWER_CONSTRUCT: - if ((gBattleMons[battler].species == SPECIES_ZYGARDE || gBattleMons[battler].species == SPECIES_ZYGARDE_10) - && gBattleMons[battler].hp <= gBattleMons[battler].maxHP / 2) + if (TryBattleFormChange(battler, FORM_CHANGE_BATTLE_HP_PERCENT)) { - gBattleStruct->changedSpecies[gBattlerPartyIndexes[battler]] = gBattleMons[battler].species; - gBattleMons[battler].species = SPECIES_ZYGARDE_COMPLETE; BattleScriptPushCursorAndCallback(BattleScript_AttackerFormChangeEnd3); effect++; } @@ -5614,7 +5563,6 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move { if (gBattleMons[gBattlerTarget].species == SPECIES_CRAMORANT_GORGING) { - gBattleStruct->changedSpecies[gBattlerPartyIndexes[gBattlerTarget]] = gBattleMons[gBattlerTarget].species; gBattleMons[gBattlerTarget].species = SPECIES_CRAMORANT; if (GetBattlerAbility(gBattlerAttacker) != ABILITY_MAGIC_GUARD) { @@ -5628,7 +5576,6 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move } else if (gBattleMons[gBattlerTarget].species == SPECIES_CRAMORANT_GULPING) { - gBattleStruct->changedSpecies[gBattlerPartyIndexes[gBattlerTarget]] = gBattleMons[gBattlerTarget].species; gBattleMons[gBattlerTarget].species = SPECIES_CRAMORANT; if (GetBattlerAbility(gBattlerAttacker) != ABILITY_MAGIC_GUARD) { @@ -5681,7 +5628,7 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move break; case ABILITY_GULP_MISSILE: if (((gCurrentMove == MOVE_SURF && TARGET_TURN_DAMAGED) || gStatuses3[gBattlerAttacker] & STATUS3_UNDERWATER) - && (effect = ShouldChangeFormHpBased(gBattlerAttacker))) + && TryBattleFormChange(gBattlerAttacker, FORM_CHANGE_BATTLE_HP_PERCENT)) { BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_AttackerFormChange; @@ -9552,59 +9499,27 @@ bool32 IsPartnerMonFromSameTrainer(u8 battlerId) return TRUE; } -u16 GetMegaEvolutionSpecies(u16 preEvoSpecies, u16 heldItemId) +bool32 DoesSpeciesUseHoldItemToChangeForm(u16 species, u16 heldItemId) { u32 i; - const struct FormChange *formChanges = gFormChangeTablePointers[preEvoSpecies]; + const struct FormChange *formChanges = gFormChangeTablePointers[species]; if (formChanges != NULL) { for (i = 0; formChanges[i].method != FORM_CHANGE_END; i++) { - if ((formChanges[i].method == FORM_CHANGE_MEGA_EVOLUTION_ITEM - && formChanges[i].param1 == heldItemId) - && formChanges[i].targetSpecies != preEvoSpecies) - return formChanges[i].targetSpecies; + switch (formChanges[i].method) + { + case FORM_CHANGE_MEGA_EVOLUTION_ITEM: + case FORM_CHANGE_PRIMAL_REVERSION: + case FORM_CHANGE_ITEM_HOLD: + if (formChanges[i].param1 == heldItemId) + return TRUE; + break; + } } } - return SPECIES_NONE; -} - -u16 GetPrimalReversionSpecies(u16 preSpecies, u16 heldItemId) -{ - u32 i; - const struct FormChange *formChanges = gFormChangeTablePointers[preSpecies]; - - if (formChanges != NULL) - { - for (i = 0; formChanges[i].method != FORM_CHANGE_END; i++) - { - if (formChanges[i].method == FORM_CHANGE_PRIMAL_REVERSION - && formChanges[i].param1 == heldItemId - && formChanges[i].targetSpecies != preSpecies) - return formChanges[i].targetSpecies; - } - } - return SPECIES_NONE; -} - -u16 GetWishMegaEvolutionSpecies(u16 preEvoSpecies, u16 moveId1, u16 moveId2, u16 moveId3, u16 moveId4) -{ - u32 i, param; - const struct FormChange *formChanges = gFormChangeTablePointers[preEvoSpecies]; - - if (formChanges != NULL) - { - for (i = 0; formChanges[i].method != FORM_CHANGE_END; i++) - { - param = formChanges[i].param1; - if ((formChanges[i].method == FORM_CHANGE_MEGA_EVOLUTION_MOVE - && (param == moveId1 || param == moveId2 || param == moveId3 || param == moveId4)) - && formChanges[i].targetSpecies != preEvoSpecies) - return formChanges[i].targetSpecies; - } - } - return SPECIES_NONE; + return FALSE; } bool32 CanMegaEvolve(u8 battlerId) @@ -9647,7 +9562,7 @@ bool32 CanMegaEvolve(u8 battlerId) itemId = GetMonData(mon, MON_DATA_HELD_ITEM); // Check if there is an entry in the evolution table for regular Mega Evolution. - if (GetMegaEvolutionSpecies(species, itemId) != SPECIES_NONE) + if (GetBattleFormChangeTargetSpecies(battlerId, FORM_CHANGE_MEGA_EVOLUTION_ITEM) != SPECIES_NONE) { #if B_ENABLE_DEBUG == TRUE if (B_ENABLE_DEBUG && gBattleStruct->debugHoldEffects[battlerId]) @@ -9668,7 +9583,7 @@ bool32 CanMegaEvolve(u8 battlerId) } // Check if there is an entry in the evolution table for Wish Mega Evolution. - if (GetWishMegaEvolutionSpecies(species, GetMonData(mon, MON_DATA_MOVE1), GetMonData(mon, MON_DATA_MOVE2), GetMonData(mon, MON_DATA_MOVE3), GetMonData(mon, MON_DATA_MOVE4))) + if (GetBattleFormChangeTargetSpecies(battlerId, FORM_CHANGE_MEGA_EVOLUTION_MOVE) != SPECIES_NONE) { gBattleStruct->mega.isWishMegaEvo = TRUE; return TRUE; @@ -9678,17 +9593,6 @@ bool32 CanMegaEvolve(u8 battlerId) return FALSE; } -void UndoMegaEvolution(u32 monId) -{ - // While not exactly a mega evolution, Zygarde follows the same rules. - if (GetMonData(&gPlayerParty[monId], MON_DATA_SPECIES, NULL) == SPECIES_ZYGARDE_COMPLETE) - { - SetMonData(&gPlayerParty[monId], MON_DATA_SPECIES, &gBattleStruct->changedSpecies[monId]); - gBattleStruct->changedSpecies[monId] = 0; - CalculateMonStats(&gPlayerParty[monId]); - } -} - bool32 IsBattlerMegaEvolved(u8 battlerId) { return (gBaseStats[gBattleMons[battlerId].species].flags & SPECIES_FLAG_MEGA_EVOLUTION); @@ -9699,17 +9603,121 @@ bool32 IsBattlerPrimalReverted(u8 battlerId) return (gBaseStats[gBattleMons[battlerId].species].flags & SPECIES_FLAG_PRIMAL_REVERSION); } -void TryBattleFormChange(u8 battlerId, u16 method) +// Returns SPECIES_NONE if no form change is possible +u16 GetBattleFormChangeTargetSpecies(u8 battlerId, u16 method) +{ + u32 i, j; + u16 targetSpecies = SPECIES_NONE; + u16 species = gBattleMons[battlerId].species; + const struct FormChange *formChanges = gFormChangeTablePointers[species]; + u16 heldItem; + u32 ability; + + if (formChanges != NULL) + { + heldItem = gBattleMons[battlerId].item; + ability = GetBattlerAbility(battlerId); + + for (i = 0; formChanges[i].method != FORM_CHANGE_END; i++) + { + if (method == formChanges[i].method && species != formChanges[i].targetSpecies) + { + switch (method) + { + case FORM_CHANGE_MEGA_EVOLUTION_ITEM: + case FORM_CHANGE_PRIMAL_REVERSION: + if (heldItem == formChanges[i].param1) + targetSpecies = formChanges[i].targetSpecies; + break; + case FORM_CHANGE_MEGA_EVOLUTION_MOVE: + if (gBattleMons[battlerId].moves[0] == formChanges[i].param1 + || gBattleMons[battlerId].moves[1] == formChanges[i].param1 + || gBattleMons[battlerId].moves[2] == formChanges[i].param1 + || gBattleMons[battlerId].moves[3] == formChanges[i].param1) + targetSpecies = formChanges[i].targetSpecies; + break; + case FORM_CHANGE_BATTLE_SWITCH: + targetSpecies = formChanges[i].targetSpecies; + break; + case FORM_CHANGE_BATTLE_HP_PERCENT: + { + if (formChanges[i].param1 == GetBattlerAbility(battlerId)) + { + // We multiply by 100 to make sure that integer division doesn't mess with the health check. + u32 hpCheck = gBattleMons[battlerId].hp * 100 * 100 / gBattleMons[battlerId].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; + } + } + } + } + } + + return targetSpecies; +} + +bool32 TryBattleFormChange(u8 battlerId, u16 method) { u8 monId = gBattlerPartyIndexes[battlerId]; u8 side = GET_BATTLER_SIDE(battlerId); struct Pokemon *party = (side == B_SIDE_PLAYER) ? gPlayerParty : gEnemyParty; + u16 targetSpecies; - if (TryFormChange(monId, side, method)) + // Can't change form if transformed. + if (gBattleMons[battlerId].status2 & STATUS2_TRANSFORMED) + return FALSE; + + if (!DoesSpeciesHaveFormChangeMethod(gBattleMons[battlerId].species, method)) + return FALSE; + + targetSpecies = GetBattleFormChangeTargetSpecies(battlerId, method); + if (targetSpecies != SPECIES_NONE) { - CopyMonLevelAndBaseStatsToBattleMon(battlerId, &party[monId]); - CopyMonAbilityAndTypesToBattleMon(battlerId, &party[monId]); + // Saves the original species on the first form change. + if (gBattleStruct->changedSpecies[monId] == SPECIES_NONE) + gBattleStruct->changedSpecies[monId] = gBattleMons[battlerId].species; + + TryToSetBattleFormChangeMoves(&party[monId], method); + SetMonData(&party[monId], MON_DATA_SPECIES, &targetSpecies); + gBattleMons[battlerId].species = targetSpecies; + RecalcBattlerStats(battlerId, &party[monId]); + return TRUE; } + + targetSpecies = GetFormChangeTargetSpecies(&party[monId], method, 0); + if (targetSpecies != SPECIES_NONE) + { + // Saves the original species on the first form change. + if (gBattleStruct->changedSpecies[monId] == SPECIES_NONE) + gBattleStruct->changedSpecies[monId] = targetSpecies; + + TryToSetBattleFormChangeMoves(&party[monId], method); + SetMonData(&party[monId], MON_DATA_SPECIES, &targetSpecies); + gBattleMons[battlerId].species = targetSpecies; + RecalcBattlerStats(battlerId, &party[monId]); + return TRUE; + } + else if ((method == FORM_CHANGE_BATTLE_END || method == FORM_CHANGE_FAINT) + && gBattleStruct->changedSpecies[monId] != SPECIES_NONE) + { + TryToSetBattleFormChangeMoves(&party[monId], method); + SetMonData(&party[monId], MON_DATA_SPECIES, &gBattleStruct->changedSpecies[monId]); + RecalcBattlerStats(battlerId, &party[monId]); + return TRUE; + } + + return FALSE; } bool32 DoBattlersShareType(u32 battler1, u32 battler2) @@ -9740,24 +9748,10 @@ bool32 CanBattlerGetOrLoseItem(u8 battlerId, u16 itemId) // Mail can be stolen now if (itemId == ITEM_ENIGMA_BERRY_E_READER) return FALSE; - // Primal Reversion inducing items cannot be lost if pokemon's base species can undergo primal reversion with it. - else if (holdEffect == HOLD_EFFECT_PRIMAL_ORB && (GetPrimalReversionSpecies(GET_BASE_SPECIES_ID(species), itemId) != SPECIES_NONE)) + else if (DoesSpeciesUseHoldItemToChangeForm(species, itemId)) return FALSE; - // Mega stone cannot be lost if pokemon's base species can mega evolve with it. - else if (holdEffect == HOLD_EFFECT_MEGA_STONE && (GetMegaEvolutionSpecies(GET_BASE_SPECIES_ID(species), itemId) != SPECIES_NONE)) - return FALSE; - else if (GET_BASE_SPECIES_ID(species) == SPECIES_GIRATINA && itemId == ITEM_GRISEOUS_ORB) - return FALSE; - else if (GET_BASE_SPECIES_ID(species) == SPECIES_GENESECT && holdEffect == HOLD_EFFECT_DRIVE) - return FALSE; - else if (GET_BASE_SPECIES_ID(species) == SPECIES_SILVALLY && holdEffect == HOLD_EFFECT_MEMORY) - return FALSE; - else if (GET_BASE_SPECIES_ID(species) == SPECIES_ARCEUS && holdEffect == HOLD_EFFECT_PLATE) - return FALSE; -#ifdef HOLD_EFFECT_Z_CRYSTAL else if (holdEffect == HOLD_EFFECT_Z_CRYSTAL) return FALSE; -#endif else return TRUE; } diff --git a/src/data/pokemon/form_change_table_pointers.h b/src/data/pokemon/form_change_table_pointers.h index d69ced5bf..06d5825a2 100644 --- a/src/data/pokemon/form_change_table_pointers.h +++ b/src/data/pokemon/form_change_table_pointers.h @@ -148,6 +148,9 @@ const struct FormChange *const gFormChangeTablePointers[NUM_SPECIES] = [SPECIES_AEGISLASH_BLADE] = sAegislashFormChangeTable, [SPECIES_XERNEAS] = sXerneasFormChangeTable, [SPECIES_XERNEAS_ACTIVE] = sXerneasFormChangeTable, + [SPECIES_ZYGARDE_10_POWER_CONSTRUCT] = sZygardePowerConstructFormChangeTable, + [SPECIES_ZYGARDE_50_POWER_CONSTRUCT] = sZygardePowerConstructFormChangeTable, + [SPECIES_ZYGARDE_COMPLETE] = sZygardePowerConstructFormChangeTable, [SPECIES_DIANCIE] = sDiancieFormChangeTable, [SPECIES_DIANCIE_MEGA] = sDiancieFormChangeTable, [SPECIES_HOOPA] = sHoopaFormChangeTable, diff --git a/src/data/pokemon/form_change_tables.h b/src/data/pokemon/form_change_tables.h index 9c0b2d866..1df1d7581 100644 --- a/src/data/pokemon/form_change_tables.h +++ b/src/data/pokemon/form_change_tables.h @@ -24,6 +24,8 @@ FORM_CHANGE_WITHDRAW: FORM_CHANGE_FAINT: Form change activates when the Pokémon faints, either in battle or in the overworld by poison. + If species is not specified and it's on the player's side, it will try to use the value + saved in gBattleStruct->changedSpecies from a previous form change. No parameters. FORM_CHANGE_BATTLE_BEGIN: @@ -33,7 +35,8 @@ FORM_CHANGE_BATTLE_BEGIN: param3 = a new move to replace it with, optional FORM_CHANGE_BATTLE_END: - Form change activates at the end of a battle + Form change activates at the end of a battle. If species is not specified and it's on the player's side, + it will try to use the value saved in gBattleStruct->changedSpecies from a previous form change. param1 = item to hold, optional param2 = a move that will be replaced, optional param3 = a new move to replace it with, optional @@ -42,10 +45,27 @@ FORM_CHANGE_BATTLE_SWITCH: Form change activates when the Pokémon is switched out in battle. No parameters. +FORM_CHANGE_BATTLE_HP_PERCENT: + Form change activates when the Pokémon's HP % passes a certain threshold. + param1 = Ability to check. + param2 = HP_HIGHER_THAN if the form triggers when the current HP is higher than the specified threshold. + HP_LOWER_EQ_THAN if the form triggers when the current HP is lower or equal than the specified threshold. + param3 = HP percentage threshold. + +FORM_CHANGE_MEGA_EVOLUTION_ITEM: + Form change activates when the mon has the defined item. If it's on the player's side, it also requires + ITEM_MEGA_RING in the user's bag and for the player to choose to enable it. + param1 = item to hold. + +FORM_CHANGE_MEGA_EVOLUTION_MOVE: + Form change activates when the mon has the defined move. If it's on the player's side, it also requires + ITEM_MEGA_RING in the user's bag and for the player to choose to enable it. + param1 = move to have. + FORM_CHANGE_PRIMAL_REVERSION: - Form change activates when entering battle with the specified item. If the item is a Red Orb, + Form change activates automatically when entering battle with the specified item. If the item is a Red Orb, it uses the Omega Symbol for the animation and icon. Otherwise, it defaults to Alpha. - param1 = item to hold, required. + param1 = item to hold. */ // FORM_CHANGE_MOVE param2 Arguments @@ -440,16 +460,18 @@ static const struct FormChange sAudinoFormChangeTable[] = { }; static const struct FormChange sDarmanitanFormChangeTable[] = { - {FORM_CHANGE_BATTLE_SWITCH, SPECIES_DARMANITAN}, - {FORM_CHANGE_FAINT, SPECIES_DARMANITAN}, - {FORM_CHANGE_BATTLE_END, SPECIES_DARMANITAN}, + {FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_DARMANITAN, ABILITY_ZEN_MODE, HP_HIGHER_THAN, 50}, + {FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_DARMANITAN_ZEN_MODE, ABILITY_ZEN_MODE, HP_LOWER_EQ_THAN, 50}, + {FORM_CHANGE_FAINT, SPECIES_DARMANITAN}, + {FORM_CHANGE_BATTLE_END, SPECIES_DARMANITAN}, {FORM_CHANGE_END}, }; static const struct FormChange sDarmanitanGalarianFormChangeTable[] = { - {FORM_CHANGE_BATTLE_SWITCH, SPECIES_DARMANITAN_GALARIAN}, - {FORM_CHANGE_FAINT, SPECIES_DARMANITAN_GALARIAN}, - {FORM_CHANGE_BATTLE_END, SPECIES_DARMANITAN_GALARIAN}, + {FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_DARMANITAN_GALARIAN, ABILITY_ZEN_MODE, HP_HIGHER_THAN, 50}, + {FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_DARMANITAN_ZEN_MODE_GALARIAN, ABILITY_ZEN_MODE, HP_LOWER_EQ_THAN, 50}, + {FORM_CHANGE_FAINT, SPECIES_DARMANITAN_GALARIAN}, + {FORM_CHANGE_BATTLE_END, SPECIES_DARMANITAN_GALARIAN}, {FORM_CHANGE_END}, }; @@ -513,6 +535,13 @@ static const struct FormChange sXerneasFormChangeTable[] = { {FORM_CHANGE_END}, }; +static const struct FormChange sZygardePowerConstructFormChangeTable[] = { + {FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_ZYGARDE_COMPLETE, ABILITY_POWER_CONSTRUCT, HP_LOWER_EQ_THAN, 50}, + {FORM_CHANGE_FAINT}, + {FORM_CHANGE_BATTLE_END}, + {FORM_CHANGE_END}, +}; + static const struct FormChange sDiancieFormChangeTable[] = { {FORM_CHANGE_MEGA_EVOLUTION_ITEM, SPECIES_DIANCIE_MEGA, ITEM_DIANCITE}, {FORM_CHANGE_FAINT, SPECIES_DIANCIE}, @@ -536,9 +565,11 @@ static const struct FormChange sOricorioFormChangeTable[] = { {FORM_CHANGE_END}, }; static const struct FormChange sWishiwashiFormChangeTable[] = { - {FORM_CHANGE_BATTLE_SWITCH, SPECIES_WISHIWASHI}, - {FORM_CHANGE_FAINT, SPECIES_WISHIWASHI}, - {FORM_CHANGE_BATTLE_END, SPECIES_WISHIWASHI}, + {FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_WISHIWASHI_SCHOOL, ABILITY_SCHOOLING, HP_HIGHER_THAN, 25}, + {FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_WISHIWASHI, ABILITY_SCHOOLING, HP_LOWER_EQ_THAN, 25}, + {FORM_CHANGE_BATTLE_SWITCH, SPECIES_WISHIWASHI}, + {FORM_CHANGE_FAINT, SPECIES_WISHIWASHI}, + {FORM_CHANGE_BATTLE_END, SPECIES_WISHIWASHI}, {FORM_CHANGE_END}, }; @@ -571,55 +602,70 @@ static const struct FormChange sMimikyuFormChangeTable[] = { }; static const struct FormChange sMiniorRedFormChangeTable[] = { - {FORM_CHANGE_BATTLE_SWITCH, SPECIES_MINIOR_CORE_RED}, - {FORM_CHANGE_FAINT, SPECIES_MINIOR_CORE_RED}, - {FORM_CHANGE_BATTLE_END, SPECIES_MINIOR_CORE_RED}, + {FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR, ABILITY_SHIELDS_DOWN, HP_HIGHER_THAN, 50}, + {FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_CORE_RED, ABILITY_SHIELDS_DOWN, HP_LOWER_EQ_THAN, 50}, + {FORM_CHANGE_BATTLE_SWITCH, SPECIES_MINIOR_CORE_RED}, + {FORM_CHANGE_FAINT, SPECIES_MINIOR_CORE_RED}, + {FORM_CHANGE_BATTLE_END, SPECIES_MINIOR_CORE_RED}, {FORM_CHANGE_END}, }; static const struct FormChange sMiniorBlueFormChangeTable[] = { - {FORM_CHANGE_BATTLE_SWITCH, SPECIES_MINIOR_CORE_BLUE}, - {FORM_CHANGE_FAINT, SPECIES_MINIOR_CORE_BLUE}, - {FORM_CHANGE_BATTLE_END, SPECIES_MINIOR_CORE_BLUE}, + {FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_METEOR_BLUE, ABILITY_SHIELDS_DOWN, HP_HIGHER_THAN, 50}, + {FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_CORE_BLUE, ABILITY_SHIELDS_DOWN, HP_LOWER_EQ_THAN, 50}, + {FORM_CHANGE_BATTLE_SWITCH, SPECIES_MINIOR_CORE_BLUE}, + {FORM_CHANGE_FAINT, SPECIES_MINIOR_CORE_BLUE}, + {FORM_CHANGE_BATTLE_END, SPECIES_MINIOR_CORE_BLUE}, {FORM_CHANGE_END}, }; static const struct FormChange sMiniorGreenFormChangeTable[] = { - {FORM_CHANGE_BATTLE_SWITCH, SPECIES_MINIOR_CORE_GREEN}, - {FORM_CHANGE_FAINT, SPECIES_MINIOR_CORE_GREEN}, - {FORM_CHANGE_BATTLE_END, SPECIES_MINIOR_CORE_GREEN}, + {FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_METEOR_GREEN, ABILITY_SHIELDS_DOWN, HP_HIGHER_THAN, 50}, + {FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_CORE_GREEN, ABILITY_SHIELDS_DOWN, HP_LOWER_EQ_THAN, 50}, + {FORM_CHANGE_BATTLE_SWITCH, SPECIES_MINIOR_CORE_GREEN}, + {FORM_CHANGE_FAINT, SPECIES_MINIOR_CORE_GREEN}, + {FORM_CHANGE_BATTLE_END, SPECIES_MINIOR_CORE_GREEN}, {FORM_CHANGE_END}, }; static const struct FormChange sMiniorIndigoFormChangeTable[] = { - {FORM_CHANGE_BATTLE_SWITCH, SPECIES_MINIOR_CORE_INDIGO}, - {FORM_CHANGE_FAINT, SPECIES_MINIOR_CORE_INDIGO}, - {FORM_CHANGE_BATTLE_END, SPECIES_MINIOR_CORE_INDIGO}, + {FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_METEOR_INDIGO, ABILITY_SHIELDS_DOWN, HP_HIGHER_THAN, 50}, + {FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_CORE_INDIGO, ABILITY_SHIELDS_DOWN, HP_LOWER_EQ_THAN, 50}, + {FORM_CHANGE_BATTLE_SWITCH, SPECIES_MINIOR_CORE_INDIGO}, + {FORM_CHANGE_FAINT, SPECIES_MINIOR_CORE_INDIGO}, + {FORM_CHANGE_BATTLE_END, SPECIES_MINIOR_CORE_INDIGO}, {FORM_CHANGE_END}, }; static const struct FormChange sMiniorOrangeFormChangeTable[] = { - {FORM_CHANGE_BATTLE_SWITCH, SPECIES_MINIOR_CORE_ORANGE}, - {FORM_CHANGE_FAINT, SPECIES_MINIOR_CORE_ORANGE}, - {FORM_CHANGE_BATTLE_END, SPECIES_MINIOR_CORE_ORANGE}, + {FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_METEOR_ORANGE, ABILITY_SHIELDS_DOWN, HP_HIGHER_THAN, 50}, + {FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_CORE_ORANGE, ABILITY_SHIELDS_DOWN, HP_LOWER_EQ_THAN, 50}, + {FORM_CHANGE_BATTLE_SWITCH, SPECIES_MINIOR_CORE_ORANGE}, + {FORM_CHANGE_FAINT, SPECIES_MINIOR_CORE_ORANGE}, + {FORM_CHANGE_BATTLE_END, SPECIES_MINIOR_CORE_ORANGE}, {FORM_CHANGE_END}, }; static const struct FormChange sMiniorVioletFormChangeTable[] = { - {FORM_CHANGE_BATTLE_SWITCH, SPECIES_MINIOR_CORE_VIOLET}, - {FORM_CHANGE_FAINT, SPECIES_MINIOR_CORE_VIOLET}, - {FORM_CHANGE_BATTLE_END, SPECIES_MINIOR_CORE_VIOLET}, + {FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_METEOR_VIOLET, ABILITY_SHIELDS_DOWN, HP_HIGHER_THAN, 50}, + {FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_CORE_VIOLET, ABILITY_SHIELDS_DOWN, HP_LOWER_EQ_THAN, 50}, + {FORM_CHANGE_BATTLE_SWITCH, SPECIES_MINIOR_CORE_VIOLET}, + {FORM_CHANGE_FAINT, SPECIES_MINIOR_CORE_VIOLET}, + {FORM_CHANGE_BATTLE_END, SPECIES_MINIOR_CORE_VIOLET}, {FORM_CHANGE_END}, }; static const struct FormChange sMiniorYellowFormChangeTable[] = { - {FORM_CHANGE_BATTLE_SWITCH, SPECIES_MINIOR_CORE_YELLOW}, - {FORM_CHANGE_FAINT, SPECIES_MINIOR_CORE_YELLOW}, - {FORM_CHANGE_BATTLE_END, SPECIES_MINIOR_CORE_YELLOW}, + {FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_METEOR_YELLOW, ABILITY_SHIELDS_DOWN, HP_HIGHER_THAN, 50}, + {FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_CORE_YELLOW, ABILITY_SHIELDS_DOWN, HP_LOWER_EQ_THAN, 50}, + {FORM_CHANGE_BATTLE_SWITCH, SPECIES_MINIOR_CORE_YELLOW}, + {FORM_CHANGE_FAINT, SPECIES_MINIOR_CORE_YELLOW}, + {FORM_CHANGE_BATTLE_END, SPECIES_MINIOR_CORE_YELLOW}, {FORM_CHANGE_END}, }; - #endif #if P_GEN_8_POKEMON == TRUE static const struct FormChange sCramorantFormChangeTable[] = { - {FORM_CHANGE_BATTLE_SWITCH, SPECIES_CRAMORANT}, - {FORM_CHANGE_FAINT, SPECIES_CRAMORANT}, - {FORM_CHANGE_BATTLE_END, SPECIES_CRAMORANT}, + {FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_CRAMORANT_GULPING, ABILITY_GULP_MISSILE, HP_HIGHER_THAN, 50}, + {FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_CRAMORANT_GORGING, ABILITY_GULP_MISSILE, HP_LOWER_EQ_THAN, 50}, + {FORM_CHANGE_BATTLE_SWITCH, SPECIES_CRAMORANT}, + {FORM_CHANGE_FAINT, SPECIES_CRAMORANT}, + {FORM_CHANGE_BATTLE_END, SPECIES_CRAMORANT}, {FORM_CHANGE_END}, }; diff --git a/src/daycare.c b/src/daycare.c index fc44e200e..82280077b 100644 --- a/src/daycare.c +++ b/src/daycare.c @@ -256,7 +256,8 @@ static u16 TakeSelectedPokemonFromDaycare(struct DaycareMon *daycareMon) BoxMonToMon(&daycareMon->mon, &pokemon); newSpecies = GetFormChangeTargetSpecies(&pokemon, FORM_CHANGE_WITHDRAW, 0); - if (newSpecies != SPECIES_NONE) { + if (newSpecies != SPECIES_NONE) + { SetMonData(&pokemon, MON_DATA_SPECIES, &newSpecies); CalculateMonStats(&pokemon); species = newSpecies; diff --git a/src/pokemon.c b/src/pokemon.c index d4f14b5b9..0fc69a35f 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -3999,13 +3999,14 @@ void CalculateMonStats(struct Pokemon *mon) { if (currentHP == 0 && oldMaxHP == 0) currentHP = newMaxHP; - else if (currentHP != 0) { - // BUG: currentHP is unintentionally able to become <= 0 after the instruction below. This causes the pomeg berry glitch. - currentHP += newMaxHP - oldMaxHP; - #ifdef BUGFIX + else if (currentHP != 0) + { + if (newMaxHP > oldMaxHP) + currentHP += newMaxHP - oldMaxHP; if (currentHP <= 0) currentHP = 1; - #endif + if (currentHP > newMaxHP) + currentHP = newMaxHP; } else return; @@ -8397,13 +8398,8 @@ u16 GetFormChangeTargetSpeciesBoxMon(struct BoxPokemon *boxMon, u16 method, u32 case FORM_CHANGE_BATTLE_END: if (heldItem == formChanges[i].param1 || formChanges[i].param1 == ITEM_NONE) targetSpecies = formChanges[i].targetSpecies; - break; - case FORM_CHANGE_PRIMAL_REVERSION: - if (arg == formChanges[i].param1) - targetSpecies = formChanges[i].targetSpecies; - break; + break; case FORM_CHANGE_WITHDRAW: - case FORM_CHANGE_BATTLE_SWITCH: case FORM_CHANGE_FAINT: targetSpecies = formChanges[i].targetSpecies; break; @@ -8415,6 +8411,23 @@ u16 GetFormChangeTargetSpeciesBoxMon(struct BoxPokemon *boxMon, u16 method, u32 return targetSpecies; } +bool32 DoesSpeciesHaveFormChangeMethod(u16 species, u16 method) +{ + u32 i, j; + const struct FormChange *formChanges = gFormChangeTablePointers[species]; + + if (formChanges != NULL) + { + for (i = 0; formChanges[i].method != FORM_CHANGE_END; i++) + { + if (method == formChanges[i].method && species != formChanges[i].targetSpecies) + return TRUE; + } + } + + return FALSE; +} + u16 MonTryLearningNewMoveEvolution(struct Pokemon *mon, bool8 firstMove) { u16 species = GetMonData(mon, MON_DATA_SPECIES, NULL); @@ -8495,12 +8508,21 @@ bool32 ShouldShowFemaleDifferences(u16 species, u32 personality) return (gBaseStats[species].flags & SPECIES_FLAG_GENDER_DIFFERENCE) && GetGenderFromSpeciesAndPersonality(species, personality) == MON_FEMALE; } +// Returns species that it transformed into. If it didn't, returns SPECIES_NONE. bool32 TryFormChange(u32 monId, u32 side, u16 method) { - u32 targetSpecies; struct Pokemon *party = (side == B_SIDE_PLAYER) ? gPlayerParty : gEnemyParty; + u16 targetSpecies; + + if (GetMonData(&party[monId], MON_DATA_SPECIES2, 0) == SPECIES_NONE + || GetMonData(&party[monId], MON_DATA_SPECIES2, 0) == SPECIES_EGG) + return FALSE; targetSpecies = GetFormChangeTargetSpecies(&party[monId], method, 0); + + if (targetSpecies == SPECIES_NONE) + targetSpecies = gBattleStruct->changedSpecies[monId]; + if (targetSpecies != SPECIES_NONE) { TryToSetBattleFormChangeMoves(&party[monId], method); @@ -8508,6 +8530,7 @@ bool32 TryFormChange(u32 monId, u32 side, u16 method) CalculateMonStats(&party[monId]); return TRUE; } + return FALSE; }