diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc index fa2b3591d9..9de7fa763a 100644 --- a/asm/macros/battle_script.inc +++ b/asm/macros/battle_script.inc @@ -189,8 +189,13 @@ .4byte \jumpInstr .endm - .macro unused_0x21 + .macro jumpifstatignorecontrary battler:req, comparison:req, stat:req, value:req, jumpInstr:req .byte 0x21 + .byte \battler + .byte \comparison + .byte \stat + .byte \value + .4byte \jumpInstr .endm .macro jumpbasedontype battler:req, type:req, jumpIfType:req, jumpInstr:req diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 3db6ab87ff..5ab5bb81d1 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -3685,7 +3685,7 @@ BattleScript_BlockedByPrimalWeatherRet:: BattleScript_EffectBellyDrum:: attackcanceler - jumpifstat BS_ATTACKER, CMP_EQUAL, STAT_ATK, MAX_STAT_STAGE, BattleScript_ButItFailed + jumpifstatignorecontrary BS_ATTACKER, CMP_EQUAL, STAT_ATK, MAX_STAT_STAGE, BattleScript_ButItFailed halvehp BattleScript_ButItFailed attackanimation waitanimation @@ -7440,11 +7440,13 @@ BattleScript_CuteCharmActivates:: return BattleScript_GooeyActivates:: + statbuffchange BS_ATTACKER, STAT_CHANGE_ONLY_CHECKING, BattleScript_GooeyActivatesRet waitstate call BattleScript_AbilityPopUp swapattackerwithtarget @ for defiant, mirror armor seteffectsecondary BS_ATTACKER, BS_TARGET, MOVE_EFFECT_SPD_MINUS_1 swapattackerwithtarget +BattleScript_GooeyActivatesRet: return BattleScript_AbilityStatusEffect:: diff --git a/include/battle_util.h b/include/battle_util.h index f6d92b6b11..421757333e 100644 --- a/include/battle_util.h +++ b/include/battle_util.h @@ -315,6 +315,7 @@ bool32 IsBattlerMegaEvolved(u32 battler); bool32 IsBattlerPrimalReverted(u32 battler); bool32 IsBattlerUltraBursted(u32 battler); u16 GetBattleFormChangeTargetSpecies(u32 battler, enum FormChanges method); +bool32 TryRevertPartyMonFormChange(u32 partyIndex); bool32 TryBattleFormChange(u32 battler, enum FormChanges method); bool32 DoBattlersShareType(u32 battler1, u32 battler2); bool32 CanBattlerGetOrLoseItem(u32 battler, u16 itemId); @@ -346,7 +347,7 @@ void TrySaveExchangedItem(u32 battler, u16 stolenItem); bool32 IsPartnerMonFromSameTrainer(u32 battler); bool32 IsBattlerAffectedByHazards(u32 battler, bool32 toxicSpikes); void SortBattlersBySpeed(u8 *battlers, bool32 slowToFast); -bool32 CompareStat(u32 battler, enum Stat statId, u8 cmpTo, u8 cmpKind); +bool32 CompareStat(u32 battler, enum Stat statId, u8 cmpTo, u8 cmpKind, enum Ability ability); bool32 BlocksPrankster(u16 move, u32 battlerPrankster, u32 battlerDef, bool32 checkTarget); bool32 PickupHasValidTarget(u32 battler); bool32 CantPickupItem(u32 battler); diff --git a/src/battle_controller_player.c b/src/battle_controller_player.c index d3076417f4..4dde40e29a 100644 --- a/src/battle_controller_player.c +++ b/src/battle_controller_player.c @@ -461,6 +461,7 @@ void HandleInputChooseTarget(u32 battler) gBattleStruct->zmove.viewing = TRUE; ReloadMoveNames(battler); } + TryToAddMoveInfoWindow(); DoBounceEffect(battler, BOUNCE_HEALTHBOX, 7, 1); DoBounceEffect(battler, BOUNCE_MON, 7, 1); EndBounceEffect(gMultiUsePlayerCursor, BOUNCE_HEALTHBOX); diff --git a/src/battle_hold_effects.c b/src/battle_hold_effects.c index 1b91e0f4ac..ec47a3f8d9 100644 --- a/src/battle_hold_effects.c +++ b/src/battle_hold_effects.c @@ -77,7 +77,7 @@ enum ItemEffect TryBoosterEnergy(u32 battler, enum Ability ability, ActivationTi static enum ItemEffect TryRoomService(u32 battler, ActivationTiming timing) { - if (gFieldStatuses & STATUS_FIELD_TRICK_ROOM && CompareStat(battler, STAT_SPEED, MIN_STAT_STAGE, CMP_GREATER_THAN)) + if (gFieldStatuses & STATUS_FIELD_TRICK_ROOM && CompareStat(battler, STAT_SPEED, MIN_STAT_STAGE, CMP_GREATER_THAN, GetBattlerAbility(battler))) { gEffectBattler = gBattleScripting.battler = battler; SET_STATCHANGER(STAT_SPEED, 1, TRUE); @@ -97,7 +97,7 @@ static enum ItemEffect TryRoomService(u32 battler, ActivationTiming timing) enum ItemEffect TryHandleSeed(u32 battler, u32 terrainFlag, enum Stat statId, ActivationTiming timing) { - if (gFieldStatuses & terrainFlag && CompareStat(battler, statId, MAX_STAT_STAGE, CMP_LESS_THAN)) + if (gFieldStatuses & terrainFlag && CompareStat(battler, statId, MAX_STAT_STAGE, CMP_LESS_THAN, GetBattlerAbility(battler))) { gEffectBattler = gBattleScripting.battler = battler; SET_STATCHANGER(statId, 1, FALSE); @@ -423,7 +423,7 @@ static enum ItemEffect TryBlunderPolicy(u32 battlerAtk) if (gBattleStruct->blunderPolicy && IsBattlerAlive(battlerAtk) - && CompareStat(battlerAtk, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN)) + && CompareStat(battlerAtk, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN, GetBattlerAbility(battlerAtk))) { gBattleStruct->blunderPolicy = FALSE; SET_STATCHANGER(STAT_SPEED, 2, FALSE); @@ -502,7 +502,7 @@ static enum ItemEffect TryThroatSpray(u32 battlerAtk) && gMultiHitCounter == 0 && IsBattlerAlive(battlerAtk) && IsAnyTargetTurnDamaged(battlerAtk) - && CompareStat(battlerAtk, STAT_SPATK, MAX_STAT_STAGE, CMP_LESS_THAN) + && CompareStat(battlerAtk, STAT_SPATK, MAX_STAT_STAGE, CMP_LESS_THAN, GetBattlerAbility(battlerAtk)) && !NoAliveMonsForEitherParty()) // Don't activate if battle will end { SET_STATCHANGER(STAT_SPATK, 1, FALSE); @@ -517,7 +517,7 @@ static enum ItemEffect DamagedStatBoostBerryEffect(u32 battlerDef, u32 battlerAt { enum ItemEffect effect = ITEM_NO_EFFECT; - if (!IsBattlerAlive(battlerDef) || !CompareStat(battlerDef, statId, MAX_STAT_STAGE, CMP_LESS_THAN)) + if (!IsBattlerAlive(battlerDef) || !CompareStat(battlerDef, statId, MAX_STAT_STAGE, CMP_LESS_THAN, GetBattlerAbility(battlerDef))) return effect; if (gBattleScripting.overrideBerryRequirements @@ -970,7 +970,7 @@ static enum ItemEffect StatRaiseBerry(u32 battler, u32 itemId, enum Stat statId, enum ItemEffect effect = ITEM_NO_EFFECT; enum Ability ability = GetBattlerAbility(battler); - if (CompareStat(battler, statId, MAX_STAT_STAGE, CMP_LESS_THAN) + if (CompareStat(battler, statId, MAX_STAT_STAGE, CMP_LESS_THAN, ability) && HasEnoughHpToEatBerry(battler, ability, GetItemHoldEffectParam(itemId), itemId)) { gEffectBattler = gBattleScripting.battler = battler; @@ -1011,17 +1011,17 @@ static enum ItemEffect RandomStatRaiseBerry(u32 battler, u32 itemId, ActivationT { enum ItemEffect effect = ITEM_NO_EFFECT; enum Stat stat; + enum Ability ability = GetBattlerAbility(battler); for (stat = STAT_ATK; stat < NUM_STATS; stat++) { - if (CompareStat(battler, stat, MAX_STAT_STAGE, CMP_LESS_THAN)) + if (CompareStat(battler, stat, MAX_STAT_STAGE, CMP_LESS_THAN, ability)) break; } if (stat == NUM_STATS) return effect; - enum Ability ability = GetBattlerAbility(battler); if (HasEnoughHpToEatBerry(battler, ability, GetItemHoldEffectParam(itemId), itemId)) { u32 savedAttacker = gBattlerAttacker; diff --git a/src/battle_main.c b/src/battle_main.c index d0ad83fc51..7a7f638add 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -5642,19 +5642,8 @@ static void HandleEndTurn_FinishBattle(void) for (i = 0; i < PARTY_SIZE; i++) { - bool8 changedForm = FALSE; - - // Appeared in battle and didn't faint - if (gBattleStruct->partyState[B_SIDE_PLAYER][i].sentOut && GetMonData(&gPlayerParty[i], MON_DATA_HP, NULL) != 0) - changedForm = TryFormChange(i, B_SIDE_PLAYER, FORM_CHANGE_END_BATTLE_ENVIRONMENT); - - if (!changedForm) - changedForm = TryFormChange(i, B_SIDE_PLAYER, FORM_CHANGE_END_BATTLE); - - // Clear original species field - gBattleStruct->partyState[B_SIDE_PLAYER][i].changedSpecies = SPECIES_NONE; - gBattleStruct->partyState[B_SIDE_OPPONENT][i].changedSpecies = SPECIES_NONE; - + bool32 changedForm = TryRevertPartyMonFormChange(i); + // Recalculate the stats of every party member before the end if (!changedForm && B_RECALCULATE_STATS >= GEN_5) CalculateMonStats(&gPlayerParty[i]); diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index db58230974..6b428ded15 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -370,7 +370,7 @@ static void Cmd_jumpifvolatile(void); static void Cmd_jumpifability(void); static void Cmd_jumpifsideaffecting(void); static void Cmd_jumpifstat(void); -static void Cmd_unused_0x21(void); +static void Cmd_jumpifstatignorecontrary(void); static void Cmd_jumpbasedontype(void); static void Cmd_getexp(void); static void Cmd_checkteamslost(void); @@ -629,7 +629,7 @@ void (*const gBattleScriptingCommandsTable[])(void) = Cmd_jumpifability, //0x1E Cmd_jumpifsideaffecting, //0x1F Cmd_jumpifstat, //0x20 - Cmd_unused_0x21, //0x21 + Cmd_jumpifstatignorecontrary, //0x21 Cmd_jumpbasedontype, //0x22 Cmd_getexp, //0x23 Cmd_checkteamslost, //0x24 @@ -4384,7 +4384,7 @@ static void Cmd_jumpifstat(void) u8 value = cmd->value; u8 comparison = cmd->comparison; - ret = CompareStat(battler, stat, value, comparison); + ret = CompareStat(battler, stat, value, comparison, GetBattlerAbility(battler)); if (ret) gBattlescriptCurrInstr = cmd->jumpInstr; @@ -4392,8 +4392,22 @@ static void Cmd_jumpifstat(void) gBattlescriptCurrInstr = cmd->nextInstr; } -static void Cmd_unused_0x21(void) +static void Cmd_jumpifstatignorecontrary(void) { + CMD_ARGS(u8 battler, u8 comparison, u8 stat, u8 value, const u8 *jumpInstr); + + bool32 ret = 0; + u8 battler = GetBattlerForBattleScript(cmd->battler); + u8 stat = cmd->stat; + u8 value = cmd->value; + u8 comparison = cmd->comparison; + + ret = CompareStat(battler, stat, value, comparison, ABILITY_NONE); + + if (ret) + gBattlescriptCurrInstr = cmd->jumpInstr; + else + gBattlescriptCurrInstr = cmd->nextInstr; } static void Cmd_jumpbasedontype(void) @@ -5572,7 +5586,7 @@ static bool32 HandleMoveEndAbilityBlock(u32 battlerAtk, u32 battlerDef, u32 move else if (abilityAtk == ABILITY_GRIM_NEIGH || abilityAtk == ABILITY_AS_ONE_SHADOW_RIDER) stat = STAT_SPATK; - if (numMonsFainted && CompareStat(battlerAtk, stat, MAX_STAT_STAGE, CMP_LESS_THAN)) + if (numMonsFainted && CompareStat(battlerAtk, stat, MAX_STAT_STAGE, CMP_LESS_THAN, abilityAtk)) { gLastUsedAbility = abilityAtk; if (abilityAtk == ABILITY_AS_ONE_ICE_RIDER) @@ -5612,17 +5626,17 @@ static bool32 HandleMoveEndAbilityBlock(u32 battlerAtk, u32 battlerDef, u32 move else { u32 numStatBuffs = 0; - if (CompareStat(battlerAtk, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN)) + if (CompareStat(battlerAtk, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN, abilityAtk)) { gBattleScripting.animArg1 = GET_STAT_BUFF_ID(STAT_ATK) + STAT_ANIM_PLUS1; numStatBuffs++; } - if (CompareStat(battlerAtk, STAT_SPATK, MAX_STAT_STAGE, CMP_LESS_THAN)) + if (CompareStat(battlerAtk, STAT_SPATK, MAX_STAT_STAGE, CMP_LESS_THAN, abilityAtk)) { gBattleScripting.animArg1 = GET_STAT_BUFF_ID(STAT_SPATK) + STAT_ANIM_PLUS1; numStatBuffs++; } - if (CompareStat(battlerAtk, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN)) + if (CompareStat(battlerAtk, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN, abilityAtk)) { gBattleScripting.animArg1 = GET_STAT_BUFF_ID(STAT_SPEED) + STAT_ANIM_PLUS1; numStatBuffs++; @@ -5853,7 +5867,7 @@ static bool32 HandleMoveEndMoveBlock(u32 moveEffect) && !IsBattlerAlive(gBattlerTarget) && IsBattlerTurnDamaged(gBattlerTarget) && !NoAliveMonsForEitherParty() - && CompareStat(gBattlerAttacker, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN)) + && CompareStat(gBattlerAttacker, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN, GetBattlerAbility(gBattlerAttacker))) { SET_STATCHANGER(STAT_ATK, GetGenConfig(GEN_CONFIG_FELL_STINGER_STAT_RAISE) >= GEN_7 ? 3 : 2, FALSE); PREPARE_STAT_BUFFER(gBattleTextBuff1, STAT_ATK); @@ -6118,7 +6132,7 @@ static void Cmd_moveend(void) && !IsBattlerAlly(gBattlerAttacker, gBattlerTarget) && IsBattlerTurnDamaged(gBattlerTarget) && !IsBattleMoveStatus(gCurrentMove) - && CompareStat(gBattlerTarget, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN)) + && CompareStat(gBattlerTarget, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN, GetBattlerAbility(gBattlerTarget))) { SET_STATCHANGER(STAT_ATK, 1, FALSE); BattleScriptCall(BattleScript_RageIsBuilding); @@ -11912,7 +11926,7 @@ static void Cmd_jumpifconfusedandstatmaxed(void) CMD_ARGS(u8 stat, const u8 *jumpInstr); if (gBattleMons[gBattlerTarget].volatiles.confusionTurns > 0 - && !CompareStat(gBattlerTarget, cmd->stat, MAX_STAT_STAGE, CMP_LESS_THAN)) + && !CompareStat(gBattlerTarget, cmd->stat, MAX_STAT_STAGE, CMP_LESS_THAN, GetBattlerAbility(gBattlerTarget))) gBattlescriptCurrInstr = cmd->jumpInstr; // Fails if we're confused AND stat cannot be raised else gBattlescriptCurrInstr = cmd->nextInstr; @@ -13892,6 +13906,8 @@ static void Cmd_givecaughtmon(void) } else { + //Before sending to PC, we revert battle form + TryRevertPartyMonFormChange(gSelectedMonPartyId); // Mon chosen, try to put it in the PC if (CopyMonToPC(&gPlayerParty[gSelectedMonPartyId]) == MON_GIVEN_TO_PC) { @@ -15690,7 +15706,7 @@ void BS_CanTarShotWork(void) NATIVE_ARGS(const u8 *failInstr); // Tar Shot will fail if it's already been used on the target or if its speed can't be lowered further if (!gDisableStructs[gBattlerTarget].tarShot - && CompareStat(gBattlerTarget, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN)) + && CompareStat(gBattlerTarget, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN, GetBattlerAbility(gBattlerTarget))) gBattlescriptCurrInstr = cmd->nextInstr; else gBattlescriptCurrInstr = cmd->failInstr; @@ -16705,7 +16721,7 @@ void BS_TryAcupressure(void) u32 bits = 0; for (enum Stat stat = STAT_ATK; stat < NUM_BATTLE_STATS; stat++) { - if (CompareStat(gBattlerTarget, stat, MAX_STAT_STAGE, CMP_LESS_THAN)) + if (CompareStat(gBattlerTarget, stat, MAX_STAT_STAGE, CMP_LESS_THAN, GetBattlerAbility(gBattlerTarget))) bits |= 1u << stat; } if (bits) @@ -17104,10 +17120,11 @@ void BS_TryActivateSoulheart(void) while (gBattleStruct->soulheartBattlerId < gBattlersCount) { gBattleScripting.battler = gBattleStruct->soulheartBattlerId++; - if (GetBattlerAbility(gBattleScripting.battler) == ABILITY_SOUL_HEART + u32 ability = GetBattlerAbility(gBattleScripting.battler); + if (ability == ABILITY_SOUL_HEART && IsBattlerAlive(gBattleScripting.battler) && !NoAliveMonsForEitherParty() - && CompareStat(gBattleScripting.battler, STAT_SPATK, MAX_STAT_STAGE, CMP_LESS_THAN)) + && CompareStat(gBattleScripting.battler, STAT_SPATK, MAX_STAT_STAGE, CMP_LESS_THAN, ability)) { SET_STATCHANGER(STAT_SPATK, 1, FALSE); PREPARE_STAT_BUFFER(gBattleTextBuff1, STAT_SPATK); @@ -17922,11 +17939,12 @@ void BS_SetAttackerToStickyWebUser(void) void BS_CutOneThirdHpAndRaiseStats(void) { NATIVE_ARGS(const u8 *failInstr); - bool32 atLeastOneStatBoosted = FALSE; + u32 ability = GetBattlerAbility(gBattlerAttacker); + for (u32 stat = 1; stat < NUM_STATS; stat++) { - if (CompareStat(gBattlerAttacker, stat, MAX_STAT_STAGE, CMP_LESS_THAN)) + if (CompareStat(gBattlerAttacker, stat, MAX_STAT_STAGE, CMP_LESS_THAN, ability)) { atLeastOneStatBoosted = TRUE; break; diff --git a/src/battle_util.c b/src/battle_util.c index 4460e72732..fb23ab400d 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -66,6 +66,7 @@ static u32 GetFlingPowerFromItemId(u32 itemId); static void SetRandomMultiHitCounter(); static bool32 IsNonVolatileStatusBlocked(u32 battlerDef, enum Ability abilityDef, enum Ability abilityAffected, const u8 *battleScript, enum FunctionCallOption option); static bool32 CanSleepDueToSleepClause(u32 battlerAtk, u32 battlerDef, enum FunctionCallOption option); +static bool32 IsOpposingSideEmpty(u32 battler); // Submoves static u32 GetMirrorMoveMove(void); @@ -1237,7 +1238,7 @@ void PrepareStringBattle(enum StringID stringId, u32 battler) case STRINGID_PKMNCUTSATTACKWITH: if (GetGenConfig(GEN_CONFIG_UPDATED_INTIMIDATE) >= GEN_8 && targetAbility == ABILITY_RATTLED - && CompareStat(gBattlerTarget, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN)) + && CompareStat(gBattlerTarget, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN, targetAbility)) { gBattlerAbility = gBattlerTarget; BattleScriptCall(BattleScript_AbilityRaisesDefenderStat); @@ -1763,13 +1764,13 @@ u32 GetBattlerAffectionHearts(u32 battler) // gBattlerAttacker is the battler that's trying to raise their stats and due to limitations of RandomUniformExcept, cannot be an argument bool32 MoodyCantRaiseStat(u32 stat) { - return CompareStat(gBattlerAttacker, stat, MAX_STAT_STAGE, CMP_EQUAL); + return CompareStat(gBattlerAttacker, stat, MAX_STAT_STAGE, CMP_EQUAL, GetBattlerAbility(gBattlerAttacker)); } // gBattlerAttacker is the battler that's trying to lower their stats and due to limitations of RandomUniformExcept, cannot be an argument bool32 MoodyCantLowerStat(u32 stat) { - return stat == GET_STAT_BUFF_ID(gBattleScripting.statChanger) || CompareStat(gBattlerAttacker, stat, MIN_STAT_STAGE, CMP_EQUAL); + return stat == GET_STAT_BUFF_ID(gBattleScripting.statChanger) || CompareStat(gBattlerAttacker, stat, MIN_STAT_STAGE, CMP_EQUAL, GetBattlerAbility(gBattlerAttacker)); } void TryToRevertMimicryAndFlags(void) @@ -3540,7 +3541,7 @@ bool32 CanAbilityAbsorbMove(u32 battlerAtk, u32 battlerDef, enum Ability ability break; case MOVE_ABSORBED_BY_STAT_INCREASE_ABILITY: gBattleStruct->pledgeMove = FALSE; - if (!CompareStat(battlerDef, statId, MAX_STAT_STAGE, CMP_LESS_THAN)) + if (!CompareStat(battlerDef, statId, MAX_STAT_STAGE, CMP_LESS_THAN, abilityDef)) { battleScript = BattleScript_MonMadeMoveUseless; } @@ -4119,7 +4120,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab gSpecialStatuses[battler].switchInAbilityDone = TRUE; - if (CompareStat(battler, statId, MAX_STAT_STAGE, CMP_LESS_THAN)) + if (CompareStat(battler, statId, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility)) { SET_STATCHANGER(statId, 1, FALSE); SaveBattlerAttacker(gBattlerAttacker); @@ -4272,7 +4273,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab } break; case ABILITY_INTIMIDATE: - if (!gSpecialStatuses[battler].switchInAbilityDone) + if (!gSpecialStatuses[battler].switchInAbilityDone && !IsOpposingSideEmpty(battler)) { SaveBattlerAttacker(gBattlerAttacker); gBattlerAttacker = battler; @@ -4284,7 +4285,8 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab break; case ABILITY_SUPERSWEET_SYRUP: if (!gSpecialStatuses[battler].switchInAbilityDone - && !GetBattlerPartyState(battler)->supersweetSyrup) + && !GetBattlerPartyState(battler)->supersweetSyrup + && !IsOpposingSideEmpty(battler)) { SaveBattlerAttacker(gBattlerAttacker); gBattlerAttacker = battler; @@ -4331,7 +4333,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab if (GetGenConfig(GEN_INTREPID_SWORD) == GEN_9) GetBattlerPartyState(battler)->intrepidSwordBoost = TRUE; gSpecialStatuses[battler].switchInAbilityDone = TRUE; - if (CompareStat(battler, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN)) + if (CompareStat(battler, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility)) { SET_STATCHANGER(STAT_ATK, 1, FALSE); BattleScriptPushCursorAndCallback(BattleScript_BattlerAbilityStatRaiseOnSwitchIn); @@ -4346,7 +4348,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab if (GetGenConfig(GEN_DAUNTLESS_SHIELD) == GEN_9) GetBattlerPartyState(battler)->dauntlessShieldBoost = TRUE; gSpecialStatuses[battler].switchInAbilityDone = TRUE; - if (CompareStat(battler, STAT_DEF, MAX_STAT_STAGE, CMP_LESS_THAN)) + if (CompareStat(battler, STAT_DEF, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility)) { SET_STATCHANGER(STAT_DEF, 1, FALSE); BattleScriptPushCursorAndCallback(BattleScript_BattlerAbilityStatRaiseOnSwitchIn); @@ -4356,7 +4358,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab break; case ABILITY_WIND_RIDER: if (!gSpecialStatuses[battler].switchInAbilityDone - && CompareStat(battler, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN) + && CompareStat(battler, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility) && gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_TAILWIND) { gSpecialStatuses[battler].switchInAbilityDone = TRUE; @@ -4496,7 +4498,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab else //ABILITY_EMBODY_ASPECT_TEAL_MASK stat = STAT_SPEED; - if (CompareStat(battler, stat, MAX_STAT_STAGE, CMP_EQUAL)) + if (CompareStat(battler, stat, MAX_STAT_STAGE, CMP_EQUAL, gLastUsedAbility)) break; gSpecialStatuses[battler].switchInAbilityDone = TRUE; @@ -4646,7 +4648,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab } break; case ABILITY_SPEED_BOOST: - if (CompareStat(battler, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN) && gDisableStructs[battler].isFirstTurn != 2) + if (CompareStat(battler, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility) && gDisableStructs[battler].isFirstTurn != 2) { SaveBattlerAttacker(gBattlerAttacker); SET_STATCHANGER(STAT_SPEED, 1, FALSE); @@ -4663,9 +4665,9 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab for (i = STAT_ATK; i < statsNum; i++) { - if (CompareStat(battler, i, MIN_STAT_STAGE, CMP_GREATER_THAN)) + if (CompareStat(battler, i, MIN_STAT_STAGE, CMP_GREATER_THAN, gLastUsedAbility)) validToLower |= 1u << i; - if (CompareStat(battler, i, MAX_STAT_STAGE, CMP_LESS_THAN)) + if (CompareStat(battler, i, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility)) validToRaise |= 1u << i; } @@ -4807,7 +4809,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab if (IsBattlerTurnDamaged(battler) && IsBattlerAlive(battler) && HadMoreThanHalfHpNowDoesnt(battler) - && CompareStat(battler, STAT_SPATK, MAX_STAT_STAGE, CMP_LESS_THAN)) + && CompareStat(battler, STAT_SPATK, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility)) { gEffectBattler = gBattlerAbility = battler; SET_STATCHANGER(STAT_SPATK, 1, FALSE); @@ -4836,7 +4838,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab if (IsBattlerTurnDamaged(battler) && IsBattlerAlive(battler) && moveType == TYPE_DARK - && CompareStat(battler, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN)) + && CompareStat(battler, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility)) { gEffectBattler = gBattlerAbility = battler; SET_STATCHANGER(STAT_ATK, 1, FALSE); @@ -4848,7 +4850,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab if (IsBattlerTurnDamaged(battler) && IsBattlerAlive(battler) && (moveType == TYPE_DARK || moveType == TYPE_BUG || moveType == TYPE_GHOST) - && CompareStat(battler, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN)) + && CompareStat(battler, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility)) { gEffectBattler = gBattlerAbility = battler; SET_STATCHANGER(STAT_SPEED, 1, FALSE); @@ -4860,7 +4862,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab if (IsBattlerTurnDamaged(battler) && IsBattlerAlive(battler) && moveType == TYPE_WATER - && CompareStat(battler, STAT_DEF, MAX_STAT_STAGE, CMP_LESS_THAN)) + && CompareStat(battler, STAT_DEF, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility)) { gEffectBattler = gBattlerAbility = battler; SET_STATCHANGER(STAT_DEF, 2, FALSE); @@ -4872,7 +4874,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab if (gBattlerAttacker != gBattlerTarget && IsBattlerTurnDamaged(gBattlerTarget) && IsBattlerAlive(battler) - && CompareStat(battler, STAT_DEF, MAX_STAT_STAGE, CMP_LESS_THAN)) + && CompareStat(battler, STAT_DEF, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility)) { gEffectBattler = gBattlerAbility = battler; SET_STATCHANGER(STAT_DEF, 1, FALSE); @@ -4884,8 +4886,8 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab if (IsBattlerTurnDamaged(battler) && IsBattlerAlive(battler) && IsBattleMovePhysical(gCurrentMove) - && (CompareStat(battler, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN) // Don't activate if both Speed and Defense cannot be raised. - || CompareStat(battler, STAT_DEF, MIN_STAT_STAGE, CMP_GREATER_THAN))) + && (CompareStat(battler, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility) // Don't activate if both Speed and Defense cannot be raised. + || CompareStat(battler, STAT_DEF, MIN_STAT_STAGE, CMP_GREATER_THAN, gLastUsedAbility))) { if (GetMoveEffect(gCurrentMove) == EFFECT_HIT_ESCAPE && CanBattlerSwitch(gBattlerAttacker)) gProtectStructs[battler].disableEjectPack = TRUE; // Set flag for target @@ -4964,7 +4966,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab if (gSpecialStatuses[battler].criticalHit && IsBattlerTurnDamaged(battler) && IsBattlerAlive(battler) - && CompareStat(battler, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN)) + && CompareStat(battler, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility)) { SET_STATCHANGER(STAT_ATK, MAX_STAT_STAGE - gBattleMons[battler].statStages[STAT_ATK], FALSE); BattleScriptCall(BattleScript_TargetsStatWasMaxedOut); @@ -4974,7 +4976,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab case ABILITY_GOOEY: case ABILITY_TANGLING_HAIR: if (IsBattlerAlive(gBattlerAttacker) - && (CompareStat(gBattlerAttacker, STAT_SPEED, MIN_STAT_STAGE, CMP_GREATER_THAN) || GetBattlerAbility(gBattlerAttacker) == ABILITY_MIRROR_ARMOR) + && (CompareStat(gBattlerAttacker, STAT_SPEED, MIN_STAT_STAGE, CMP_GREATER_THAN, gLastUsedAbility) || GetBattlerAbility(gBattlerAttacker) == ABILITY_MIRROR_ARMOR) && !gProtectStructs[gBattlerAttacker].confusionSelfDmg && IsBattlerTurnDamaged(gBattlerTarget) && !CanBattlerAvoidContactEffects(gBattlerAttacker, gBattlerTarget, GetBattlerAbility(gBattlerAttacker), GetBattlerHoldEffect(gBattlerAttacker), move)) @@ -5178,7 +5180,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab case ABILITY_STEAM_ENGINE: if (IsBattlerTurnDamaged(gBattlerTarget) && IsBattlerAlive(battler) - && CompareStat(battler, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN) + && CompareStat(battler, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility) && (moveType == TYPE_FIRE || moveType == TYPE_WATER)) { gEffectBattler = gBattlerAbility = battler; @@ -5260,7 +5262,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab case ABILITY_THERMAL_EXCHANGE: if (IsBattlerTurnDamaged(gBattlerTarget) && IsBattlerAlive(gBattlerTarget) - && CompareStat(gBattlerTarget, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN) + && CompareStat(gBattlerTarget, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility) && moveType == TYPE_FIRE) { gEffectBattler = gBattlerAbility = gBattlerTarget; @@ -9052,17 +9054,46 @@ bool32 CanBattlerFormChange(u32 battler, enum FormChanges method) if (gBattleMons[battler].volatiles.transformed && B_TRANSFORM_FORM_CHANGES >= GEN_5) return FALSE; - // Mega Evolved and Ultra Bursted Pokémon should always revert to normal upon fainting or ending the battle. - if ((IsBattlerMegaEvolved(battler) || IsBattlerUltraBursted(battler) || IsBattlerInTeraForm(battler)) && (method == FORM_CHANGE_FAINT || method == FORM_CHANGE_END_BATTLE)) - return TRUE; - else if (IsBattlerPrimalReverted(battler) && (method == FORM_CHANGE_END_BATTLE)) - return TRUE; - // Gigantamaxed Pokemon should revert upon fainting, switching, or ending the battle. - else if (IsGigantamaxed(battler) && (method == FORM_CHANGE_FAINT || method == FORM_CHANGE_BATTLE_SWITCH || method == FORM_CHANGE_END_BATTLE)) - return TRUE; + + switch (method) + { + case FORM_CHANGE_END_BATTLE: + if (IsBattlerPrimalReverted(battler)) + return TRUE; + // Fallthrough + case FORM_CHANGE_FAINT: + if (IsBattlerMegaEvolved(battler) || IsBattlerUltraBursted(battler) || IsBattlerInTeraForm(battler) || IsGigantamaxed(battler)) + return TRUE; + break; + case FORM_CHANGE_BATTLE_SWITCH: + if (IsGigantamaxed(battler)) + return TRUE; + else if (GetActiveGimmick(battler) == GIMMICK_TERA && GetBattlerAbility(battler) == ABILITY_HUNGER_SWITCH) + return FALSE; + break; + default: + break; + } return DoesSpeciesHaveFormChangeMethod(gBattleMons[battler].species, method); } +bool32 TryRevertPartyMonFormChange(u32 partyIndex) +{ + bool32 changedForm = FALSE; + + // Appeared in battle and didn't faint + if (gBattleStruct->partyState[B_SIDE_PLAYER][partyIndex].sentOut && GetMonData(&gPlayerParty[partyIndex], MON_DATA_HP, NULL) != 0) + changedForm = TryFormChange(partyIndex, B_SIDE_PLAYER, FORM_CHANGE_END_BATTLE_ENVIRONMENT); + + if (!changedForm) + changedForm = TryFormChange(partyIndex, B_SIDE_PLAYER, FORM_CHANGE_END_BATTLE); + + // Clear original species field + gBattleStruct->partyState[B_SIDE_PLAYER][partyIndex].changedSpecies = SPECIES_NONE; + + return changedForm; +} + bool32 TryBattleFormChange(u32 battler, enum FormChanges method) { u32 monId = gBattlerPartyIndexes[battler]; @@ -9076,7 +9107,7 @@ bool32 TryBattleFormChange(u32 battler, enum FormChanges method) targetSpecies = GetBattleFormChangeTargetSpecies(battler, method); if (targetSpecies == currentSpecies) targetSpecies = GetFormChangeTargetSpecies(&party[monId], method, 0); - if (targetSpecies != currentSpecies) + if (targetSpecies != currentSpecies && targetSpecies != SPECIES_NONE) { // Saves the original species on the first form change. @@ -9093,17 +9124,22 @@ bool32 TryBattleFormChange(u32 battler, enum FormChanges method) { bool32 restoreSpecies = FALSE; - // Mega Evolved and Ultra Bursted Pokémon should always revert to normal upon fainting or ending the battle, so no need to add it to the form change tables. - if ((IsBattlerMegaEvolved(battler) || IsBattlerUltraBursted(battler) || IsBattlerInTeraForm(battler)) && (method == FORM_CHANGE_FAINT || method == FORM_CHANGE_END_BATTLE)) - restoreSpecies = TRUE; - - // Unlike Megas, Primal Reversion isn't canceled on fainting. - else if (IsBattlerPrimalReverted(battler) && (method == FORM_CHANGE_END_BATTLE)) - restoreSpecies = TRUE; - - // Gigantamax Pokemon have their forms reverted after fainting, switching, or ending the battle. - else if (IsGigantamaxed(battler) && (method == FORM_CHANGE_FAINT || method == FORM_CHANGE_BATTLE_SWITCH || method == FORM_CHANGE_END_BATTLE)) + switch (method) + { + case FORM_CHANGE_END_BATTLE: restoreSpecies = TRUE; + break; + case FORM_CHANGE_FAINT: + if (IsBattlerMegaEvolved(battler) || IsBattlerUltraBursted(battler) || IsBattlerInTeraForm(battler) || IsGigantamaxed(battler)) + restoreSpecies = TRUE; + break; + case FORM_CHANGE_BATTLE_SWITCH: + if (IsGigantamaxed(battler)) + restoreSpecies = TRUE; + break; + default: + break; + } if (restoreSpecies) { @@ -9674,14 +9710,14 @@ bool32 TestIfSheerForceAffected(u32 battler, u16 move) } // This function is the body of "jumpifstat", but can be used dynamically in a function -bool32 CompareStat(u32 battler, enum Stat statId, u8 cmpTo, u8 cmpKind) +bool32 CompareStat(u32 battler, enum Stat statId, u8 cmpTo, u8 cmpKind, enum Ability ability) { bool32 ret = FALSE; u8 statValue = gBattleMons[battler].statStages[statId]; // Because this command is used as a way of checking if a stat can be lowered/raised, // we need to do some modification at run-time. - if (GetBattlerAbility(battler) == ABILITY_CONTRARY) + if (ability == ABILITY_CONTRARY) { if (cmpKind == CMP_GREATER_THAN) cmpKind = CMP_LESS_THAN; @@ -10772,6 +10808,21 @@ bool32 HasPartnerTrainer(u32 battler) return FALSE; } +static bool32 IsOpposingSideEmpty(u32 battler) +{ + u32 oppositeBattler = BATTLE_OPPOSITE(battler); + + if (IsBattlerAlive(oppositeBattler)) + return FALSE; + + if (!IsDoubleBattle()) + return TRUE; + + if (IsBattlerAlive(BATTLE_PARTNER(oppositeBattler))) + return FALSE; + return TRUE; +} + static u32 GetMirrorMoveMove(void) { s32 i, validMovesCount; diff --git a/src/item_use.c b/src/item_use.c index aa34f7b1ec..a667f8b4a6 100644 --- a/src/item_use.c +++ b/src/item_use.c @@ -128,7 +128,7 @@ static void SetUpItemUseCallback(u8 taskId) type = gTasks[taskId].tEnigmaBerryType - 1; else type = GetItemType(gSpecialVar_ItemId) - 1; - + if (gTasks[taskId].tUsingRegisteredKeyItem && type == (ITEM_USE_PARTY_MENU - 1)) { FadeScreen(FADE_TO_BLACK, 0); @@ -1306,15 +1306,18 @@ bool32 CannotUseItemsInBattle(u16 itemId, struct Pokemon *mon) } break; case EFFECT_ITEM_INCREASE_ALL_STATS: + { + u32 ability = GetBattlerAbility(gBattlerInMenuId); for (i = STAT_ATK; i < NUM_STATS; i++) { - if (CompareStat(gBattlerInMenuId, i, MAX_STAT_STAGE, CMP_EQUAL)) + if (CompareStat(gBattlerInMenuId, i, MAX_STAT_STAGE, CMP_EQUAL, ability)) { cannotUse = TRUE; break; } } break; + } case EFFECT_ITEM_RESTORE_HP: if (hp == 0 || hp == GetMonData(mon, MON_DATA_MAX_HP)) cannotUse = TRUE; diff --git a/src/party_menu.c b/src/party_menu.c index 8a9532eaea..891066dd41 100644 --- a/src/party_menu.c +++ b/src/party_menu.c @@ -1511,7 +1511,7 @@ static void HandleChooseMonSelection(u8 taskId, s8 *slotPtr) } case PARTY_ACTION_SEND_MON_TO_BOX: { - u8 partyId = GetPartyIdFromBattleSlot((u8)*slotPtr); + u8 partyId = (u8)*slotPtr; if (partyId == 0 || ((gBattleTypeFlags & BATTLE_TYPE_DOUBLE) && partyId == 1)) { // Can't select if mon is currently on the field @@ -1538,7 +1538,7 @@ static void HandleChooseMonSelection(u8 taskId, s8 *slotPtr) else { PlaySE(SE_SELECT); - gSelectedMonPartyId = partyId; + gSelectedMonPartyId = GetPartyIdFromBattleSlot(partyId); Task_ClosePartyMenu(taskId); } break; diff --git a/src/trainer_see.c b/src/trainer_see.c index 7dbc55181f..07240e2be7 100644 --- a/src/trainer_see.c +++ b/src/trainer_see.c @@ -399,6 +399,7 @@ bool8 CheckForTrainersWantingBattle(void) if (numTrainers == 0xFF) // non-trainerbattle script { u32 objectEventId = gApproachingTrainers[gNoOfApproachingTrainers - 1].objectEventId; + gApproachingTrainers[gNoOfApproachingTrainers - 1].trainerScriptPtr = GetObjectEventScriptPointerByObjectEventId(objectEventId); gSelectedObjectEvent = objectEventId; gSpecialVar_LastTalked = gObjectEvents[objectEventId].localId; ScriptContext_SetupScript(EventScript_ObjectApproachPlayer); diff --git a/test/battle/ability/hunger_switch.c b/test/battle/ability/hunger_switch.c index 1892de4424..469cf02d3c 100644 --- a/test/battle/ability/hunger_switch.c +++ b/test/battle/ability/hunger_switch.c @@ -53,7 +53,6 @@ SINGLE_BATTLE_TEST("Hunger Switch does not switch Morpeko's form when Terastalli SINGLE_BATTLE_TEST("Hunger Switch does not switch Morpeko's form after switching out while Terastallized") { - KNOWN_FAILING; // #7062 GIVEN { ASSUME(GetMoveEffect(MOVE_ROAR) == EFFECT_ROAR); PLAYER(SPECIES_MORPEKO) { Ability(ABILITY_HUNGER_SWITCH); TeraType(TYPE_NORMAL); } diff --git a/test/battle/ability/intimidate.c b/test/battle/ability/intimidate.c index bdd521d782..0606938d45 100644 --- a/test/battle/ability/intimidate.c +++ b/test/battle/ability/intimidate.c @@ -240,6 +240,7 @@ DOUBLE_BATTLE_TEST("Intimidate is not going to trigger if a mon switches out thr ANIMATION(ANIM_TYPE_MOVE, MOVE_HEALING_WISH, opponentRight); ANIMATION(ANIM_TYPE_MOVE, MOVE_U_TURN, playerLeft); HP_BAR(opponentLeft); + NOT ABILITY_POPUP(playerLeft, ABILITY_INTIMIDATE); MESSAGE("2 sent out Treecko!"); MESSAGE("2 sent out Torchic!"); NOT ABILITY_POPUP(playerLeft, ABILITY_INTIMIDATE); @@ -264,7 +265,7 @@ SINGLE_BATTLE_TEST("Intimidate activates when it's no longer effected by Neutral } } -SINGLE_BATTLE_TEST("Intimidate activates when it's no longer affected by Neutralizing Gas - switching moves") +DOUBLE_BATTLE_TEST("Intimidate activates when it's no longer affected by Neutralizing Gas - switching moves") { u32 move; PARAMETRIZE { move = MOVE_U_TURN; } @@ -276,19 +277,24 @@ SINGLE_BATTLE_TEST("Intimidate activates when it's no longer affected by Neutral ASSUME(GetMoveEffect(MOVE_BATON_PASS) == EFFECT_BATON_PASS); PLAYER(SPECIES_WEEZING) { Ability(ABILITY_NEUTRALIZING_GAS); } PLAYER(SPECIES_WOBBUFFET) { HP(1); } + PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_ARBOK) { Ability(ABILITY_INTIMIDATE); } + OPPONENT(SPECIES_WOBBUFFET); } WHEN { - TURN { MOVE(player, move); SEND_OUT(player, 1); } + TURN { + if (move == MOVE_U_TURN) + MOVE(playerLeft, move, target: opponentLeft); + else + MOVE(playerLeft, move); + SEND_OUT(playerLeft, 2); + } } SCENE { - ABILITY_POPUP(player, ABILITY_NEUTRALIZING_GAS); + ABILITY_POPUP(playerLeft, ABILITY_NEUTRALIZING_GAS); MESSAGE("Neutralizing gas filled the area!"); - ANIMATION(ANIM_TYPE_MOVE, move, player); + ANIMATION(ANIM_TYPE_MOVE, move, playerLeft); MESSAGE("The effects of the neutralizing gas wore off!"); - ABILITY_POPUP(opponent, ABILITY_INTIMIDATE); + ABILITY_POPUP(opponentLeft, ABILITY_INTIMIDATE); SEND_IN_MESSAGE("Wobbuffet"); - } THEN { - if (move == MOVE_HEALING_WISH) - EXPECT_EQ(player->hp, player->maxHP); } } @@ -330,23 +336,25 @@ SINGLE_BATTLE_TEST("Intimidate activates when it's no longer affected by Neutral } } -SINGLE_BATTLE_TEST("Intimidate activates when it's no longer affected by Neutralizing Gas - fainted") +DOUBLE_BATTLE_TEST("Intimidate activates when it's no longer affected by Neutralizing Gas - fainted") { GIVEN { ASSUME(GetMoveEffect(MOVE_FELL_STINGER) == EFFECT_FELL_STINGER); PLAYER(SPECIES_WEEZING) { Ability(ABILITY_NEUTRALIZING_GAS); HP(1); } PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_ARBOK) { Ability(ABILITY_INTIMIDATE); } + OPPONENT(SPECIES_WOBBUFFET); } WHEN { - TURN { MOVE(opponent, MOVE_FELL_STINGER); SEND_OUT(player, 1); } + TURN { MOVE(opponentLeft, MOVE_FELL_STINGER, target: playerLeft); SEND_OUT(playerLeft, 2); } } SCENE { - ABILITY_POPUP(player, ABILITY_NEUTRALIZING_GAS); + ABILITY_POPUP(playerLeft, ABILITY_NEUTRALIZING_GAS); MESSAGE("Neutralizing gas filled the area!"); - ANIMATION(ANIM_TYPE_MOVE, MOVE_FELL_STINGER, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_FELL_STINGER, opponentLeft); MESSAGE("The effects of the neutralizing gas wore off!"); - ABILITY_POPUP(opponent, ABILITY_INTIMIDATE); + ABILITY_POPUP(opponentLeft, ABILITY_INTIMIDATE); MESSAGE("Weezing fainted!"); - ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft); SEND_IN_MESSAGE("Wobbuffet"); } } diff --git a/test/battle/ability/power_construct.c b/test/battle/ability/power_construct.c index 6a8ca9db5f..07d52c1767 100644 --- a/test/battle/ability/power_construct.c +++ b/test/battle/ability/power_construct.c @@ -2,3 +2,56 @@ #include "test/battle.h" TO_DO_BATTLE_TEST("TODO: Write Power Construct (Ability) test titles") + +SINGLE_BATTLE_TEST("Power Construct switches Zygarde's form when HP is below half") +{ + u16 baseSpecies; + PARAMETRIZE { baseSpecies = SPECIES_ZYGARDE_10_POWER_CONSTRUCT; } + PARAMETRIZE { baseSpecies = SPECIES_ZYGARDE_50_POWER_CONSTRUCT; } + + GIVEN { + PLAYER(baseSpecies) + { + Ability(ABILITY_POWER_CONSTRUCT); + HP((GetMonData(&PLAYER_PARTY[0], MON_DATA_MAX_HP) / 2) + 1); + } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_SCRATCH); MOVE(player, MOVE_CELEBRATE); } + } SCENE { + MESSAGE("You sense the presence of many!"); + ABILITY_POPUP(player, ABILITY_POWER_CONSTRUCT); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_POWER_CONSTRUCT, player); + } THEN { + EXPECT_EQ(player->species, SPECIES_ZYGARDE_COMPLETE); + } +} + +WILD_BATTLE_TEST("Power Construct Zygarde reverts to its original form upon catching") +{ + u16 baseSpecies; + PARAMETRIZE { baseSpecies = SPECIES_ZYGARDE_10_POWER_CONSTRUCT; } + PARAMETRIZE { baseSpecies = SPECIES_ZYGARDE_50_POWER_CONSTRUCT; } + + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(baseSpecies) + { + Ability(ABILITY_POWER_CONSTRUCT); + HP((GetMonData(&OPPONENT_PARTY[0], MON_DATA_MAX_HP) / 2) + 1); + } + } WHEN { + TURN { MOVE(player, MOVE_SCRATCH); } + TURN { USE_ITEM(player, ITEM_MASTER_BALL); } + } SCENE { + // Turn 1 + MESSAGE("You sense the presence of many!"); + ABILITY_POPUP(opponent, ABILITY_POWER_CONSTRUCT); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_POWER_CONSTRUCT, opponent); + + // Turn 2 + ANIMATION(ANIM_TYPE_SPECIAL, B_ANIM_BALL_THROW, player); + } THEN { + EXPECT_EQ(GetMonData(&gPlayerParty[1], MON_DATA_SPECIES), baseSpecies); + } +} diff --git a/test/battle/ability/tangling_hair.c b/test/battle/ability/tangling_hair.c index 13a0f27cb0..849c83df64 100644 --- a/test/battle/ability/tangling_hair.c +++ b/test/battle/ability/tangling_hair.c @@ -8,7 +8,6 @@ ASSUMPTIONS ASSUME(MoveMakesContact(MOVE_SCRATCH) == TRUE); } - SINGLE_BATTLE_TEST("Tangling Hair drops opposing mon's speed if ability user got hit by a contact move") { u32 move; @@ -85,3 +84,16 @@ SINGLE_BATTLE_TEST("Tangling Hair does not activate on confusion damage") } } } + +SINGLE_BATTLE_TEST("Tangling Hair does not trigger on Clear Body") +{ + GIVEN { + PLAYER(SPECIES_DUGTRIO) { Ability(ABILITY_TANGLING_HAIR); } + OPPONENT(SPECIES_BELDUM) { Ability(ABILITY_CLEAR_BODY); }; + } WHEN { + TURN { MOVE(opponent, MOVE_SCRATCH); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, opponent); + NOT ABILITY_POPUP(player, ABILITY_TANGLING_HAIR); + } +} diff --git a/test/battle/form_change/faint.c b/test/battle/form_change/faint.c index 8fc95cdc65..316ecca0bf 100644 --- a/test/battle/form_change/faint.c +++ b/test/battle/form_change/faint.c @@ -1,9 +1,24 @@ #include "global.h" #include "test/battle.h" -SINGLE_BATTLE_TEST("Aegislash reverts to Shield Form upon fainting") +SINGLE_BATTLE_TEST("Aegislash reverts to Shield Form upon fainting (start as Shield)") +{ + GIVEN { + PLAYER(SPECIES_AEGISLASH_SHIELD) { HP(1); } + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_SCRATCH); MOVE(opponent, MOVE_GUST); SEND_OUT(player, 1); } + } SCENE { + MESSAGE("The opposing Wobbuffet used Gust!"); + MESSAGE("Aegislash fainted!"); + } THEN { + EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_SPECIES), SPECIES_AEGISLASH_SHIELD); + } +} + +SINGLE_BATTLE_TEST("Aegislash reverts to Shield Form upon fainting (start as Blade)") { - KNOWN_FAILING; GIVEN { PLAYER(SPECIES_AEGISLASH_BLADE) { HP(1); } PLAYER(SPECIES_WOBBUFFET); @@ -14,7 +29,7 @@ SINGLE_BATTLE_TEST("Aegislash reverts to Shield Form upon fainting") MESSAGE("The opposing Wobbuffet used Gust!"); MESSAGE("Aegislash fainted!"); } THEN { - EXPECT_EQ(GetMonData(&PLAYER_PARTY[0], MON_DATA_SPECIES), SPECIES_AEGISLASH_SHIELD); + EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_SPECIES), SPECIES_AEGISLASH_SHIELD); } } diff --git a/test/battle/move_effect/belly_drum.c b/test/battle/move_effect/belly_drum.c index 20024f5b91..194ee5b9d0 100644 --- a/test/battle/move_effect/belly_drum.c +++ b/test/battle/move_effect/belly_drum.c @@ -159,7 +159,6 @@ SINGLE_BATTLE_TEST("Belly Drum maximizes the user's Attack stat, even when below SINGLE_BATTLE_TEST("Belly Drum fails if the user's Attack is already at +6, even with Contrary") { - KNOWN_FAILING; GIVEN { ASSUME(GetMoveEffect(MOVE_CHARM) == EFFECT_ATTACK_DOWN_2); PLAYER(SPECIES_SERPERIOR) { Ability(ABILITY_CONTRARY); } @@ -190,7 +189,6 @@ SINGLE_BATTLE_TEST("Belly Drum fails if the user's Attack is already at +6, even SINGLE_BATTLE_TEST("Belly Drum deducts HP if the user has Contrary and is at -6") { - KNOWN_FAILING; GIVEN { ASSUME(GetMoveEffect(MOVE_SWORDS_DANCE) == EFFECT_ATTACK_UP_2); PLAYER(SPECIES_SERPERIOR) { Ability(ABILITY_CONTRARY); } @@ -215,6 +213,6 @@ SINGLE_BATTLE_TEST("Belly Drum deducts HP if the user has Contrary and is at -6" ANIMATION(ANIM_TYPE_MOVE, MOVE_BELLY_DRUM, player); s32 maxHP = GetMonData(&PLAYER_PARTY[0], MON_DATA_MAX_HP); HP_BAR(player, hp: maxHP / 2); - MESSAGE("Wobbuffet cut its own HP and maximized its Attack!"); + MESSAGE("Serperior cut its own HP and maximized its Attack!"); } } diff --git a/test/battle/move_effect/hit_escape.c b/test/battle/move_effect/hit_escape.c index 0ced64d7e5..f587d078a0 100644 --- a/test/battle/move_effect/hit_escape.c +++ b/test/battle/move_effect/hit_escape.c @@ -113,7 +113,7 @@ SINGLE_BATTLE_TEST("Hit Escape: U-turn switches the user out after Ice Face acti } } -SINGLE_BATTLE_TEST("Hit Escape: Held items are consumed immediately after a mon switched in by U-turn and Intimidate activates after it: player side") +SINGLE_BATTLE_TEST("Hit Escape: Held items are consumed immediately after a mon switched in by U-turn: player side") { GIVEN { PLAYER(SPECIES_TAPU_KOKO) { Ability(ABILITY_ELECTRIC_SURGE); }; @@ -126,7 +126,6 @@ SINGLE_BATTLE_TEST("Hit Escape: Held items are consumed immediately after a mon ABILITY_POPUP(player, ABILITY_ELECTRIC_SURGE); ANIMATION(ANIM_TYPE_MOVE, MOVE_U_TURN, player); HP_BAR(opponent); - ABILITY_POPUP(player, ABILITY_INTIMIDATE); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); MESSAGE("2 sent out Wynaut!"); @@ -136,7 +135,7 @@ SINGLE_BATTLE_TEST("Hit Escape: Held items are consumed immediately after a mon } } -SINGLE_BATTLE_TEST("Hit Escape: Held items are consumed immediately after a mon switched in by U-turn and Intimidate activates after it: opposing side") +SINGLE_BATTLE_TEST("Hit Escape: Held items are consumed immediately after a mon switched in by U-turn: opposing side") { GIVEN { PLAYER(SPECIES_TAPU_KOKO) { Ability(ABILITY_ELECTRIC_SURGE); }; @@ -149,7 +148,6 @@ SINGLE_BATTLE_TEST("Hit Escape: Held items are consumed immediately after a mon ABILITY_POPUP(player, ABILITY_ELECTRIC_SURGE); ANIMATION(ANIM_TYPE_MOVE, MOVE_U_TURN, player); HP_BAR(opponent); - ABILITY_POPUP(player, ABILITY_INTIMIDATE); MESSAGE("2 sent out Wynaut!"); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);