diff --git a/asm/macros/battle_frontier/battle_tower.inc b/asm/macros/battle_frontier/battle_tower.inc index 49e72e7446..b3c86955dd 100644 --- a/asm/macros/battle_frontier/battle_tower.inc +++ b/asm/macros/battle_frontier/battle_tower.inc @@ -98,7 +98,7 @@ waitstate .endm - .macro multi_do type:req, partnerId:req, partnerPicId:req + .macro multi_do type:req, partnerId:req special ReducePlayerPartyToSelectedMons setvar VAR_0x8004, FRONTIER_UTIL_FUNC_SET_DATA setvar VAR_0x8005, FRONTIER_DATA_SELECTED_MON_ORDER @@ -106,7 +106,6 @@ setvar VAR_0x8004, SPECIAL_BATTLE_MULTI setvar VAR_0x8005, \type | MULTI_BATTLE_CHOOSE_MONS setvar VAR_0x8006, \partnerId - setvar VAR_0x8007, \partnerPicId special DoSpecialTrainerBattle waitstate setvar VAR_0x8004, FRONTIER_UTIL_FUNC_SAVE_PARTY @@ -114,30 +113,29 @@ special LoadPlayerParty .endm - .macro multi_2_vs_2 trainer1Id:req, trainer1LoseText:req, trainer2Id:req, trainer2LoseText:req, partnerId:req, partnerPicId:req + .macro multi_2_vs_2 trainer1Id:req, trainer1LoseText:req, trainer2Id:req, trainer2LoseText:req, partnerId:req special SavePlayerParty trainerbattle TRAINER_BATTLE_SET_TRAINER_A, \trainer1Id, 0, NULL, \trainer1LoseText @ set first trainer mons trainerbattle TRAINER_BATTLE_SET_TRAINER_B, \trainer2Id, 0, NULL, \trainer2LoseText @ set second trainer mons - multi_do MULTI_BATTLE_2_VS_2, \partnerId, \partnerPicId + multi_do MULTI_BATTLE_2_VS_2, \partnerId .endm - .macro multi_2_vs_1 trainer1Id:req, trainer1LoseText:req, partnerId:req, partnerPicId:req + .macro multi_2_vs_1 trainer1Id:req, trainer1LoseText:req, partnerId:req special SavePlayerParty trainerbattle TRAINER_BATTLE_SET_TRAINER_A, \trainer1Id, 0, NULL, \trainer1LoseText @ set first trainer mons - multi_do MULTI_BATTLE_2_VS_1, \partnerId, \partnerPicId + multi_do MULTI_BATTLE_2_VS_1, \partnerId .endm @ Wild mons need to be assigned to gEnemyParty 0 and 3 slots, other slots need to be cleared out. - .macro multi_wild partnerId:req, partnerPicId:req + .macro multi_wild partnerId:req special SavePlayerParty - multi_do MULTI_BATTLE_2_VS_WILD, \partnerId, \partnerPicId + multi_do MULTI_BATTLE_2_VS_WILD, \partnerId .endm - .macro multi_do_fixed type:req, partnerId:req, partnerPicId:req + .macro multi_do_fixed type:req, partnerId:req setvar VAR_0x8004, SPECIAL_BATTLE_MULTI setvar VAR_0x8005, \type setvar VAR_0x8006, \partnerId - setvar VAR_0x8007, \partnerPicId special DoSpecialTrainerBattle waitstate setvar VAR_0x8004, FRONTIER_UTIL_FUNC_SAVE_PARTY @@ -145,21 +143,21 @@ special LoadPlayerParty .endm - .macro multi_fixed_2_vs_2 trainer1Id:req, trainer1LoseText:req, trainer2Id:req, trainer2LoseText:req, partnerId:req, partnerPicId:req + .macro multi_fixed_2_vs_2 trainer1Id:req, trainer1LoseText:req, trainer2Id:req, trainer2LoseText:req, partnerId:req special SavePlayerParty trainerbattle TRAINER_BATTLE_SET_TRAINER_A, \trainer1Id, 0, NULL, \trainer1LoseText @ set first trainer mons trainerbattle TRAINER_BATTLE_SET_TRAINER_B, \trainer2Id, 0, NULL, \trainer2LoseText @ set second trainer mons - multi_do_fixed MULTI_BATTLE_2_VS_2, \partnerId, \partnerPicId + multi_do_fixed MULTI_BATTLE_2_VS_2, \partnerId .endm - .macro multi_fixed_2_vs_1 trainer1Id:req, trainer1LoseText:req, partnerId:req, partnerPicId:req + .macro multi_fixed_2_vs_1 trainer1Id:req, trainer1LoseText:req, partnerId:req special SavePlayerParty trainerbattle TRAINER_BATTLE_SET_TRAINER_A, \trainer1Id, 0, NULL, \trainer1LoseText @ set first trainer mons - multi_do_fixed MULTI_BATTLE_2_VS_1, \partnerId, \partnerPicId + multi_do_fixed MULTI_BATTLE_2_VS_1, \partnerId .endm @ Wild mons need to be assigned to gEnemyParty 0 and 3 slots, other slots need to be cleared out. - .macro multi_fixed_wild partnerId:req, partnerPicId:req + .macro multi_fixed_wild partnerId:req special SavePlayerParty - multi_do_fixed MULTI_BATTLE_2_VS_WILD, \partnerId, \partnerPicId + multi_do_fixed MULTI_BATTLE_2_VS_WILD, \partnerId .endm diff --git a/data/maps/MossdeepCity_SpaceCenter_2F/scripts.inc b/data/maps/MossdeepCity_SpaceCenter_2F/scripts.inc index a4fcc0237c..4ec82003b5 100644 --- a/data/maps/MossdeepCity_SpaceCenter_2F/scripts.inc +++ b/data/maps/MossdeepCity_SpaceCenter_2F/scripts.inc @@ -257,7 +257,7 @@ MossdeepCity_SpaceCenter_2F_EventScript_ChoosePartyForMultiBattle:: goto MossdeepCity_SpaceCenter_2F_EventScript_ReadyForBattlePrompt MossdeepCity_SpaceCenter_2F_EventScript_DoStevenMultiBattle:: - multi_2_vs_2 TRAINER_MAXIE_MOSSDEEP, MossdeepCity_SpaceCenter_2F_Text_JustWantToExpandLand, TRAINER_TABITHA_MOSSDEEP, MossdeepCity_SpaceCenter_Text_TabithaDefeat, PARTNER_STEVEN, TRAINER_BACK_PIC_STEVEN + multi_2_vs_2 TRAINER_MAXIE_MOSSDEEP, MossdeepCity_SpaceCenter_2F_Text_JustWantToExpandLand, TRAINER_TABITHA_MOSSDEEP, MossdeepCity_SpaceCenter_Text_TabithaDefeat, PARTNER_STEVEN switch VAR_RESULT case 1, MossdeepCity_SpaceCenter_2F_EventScript_DefeatedMaxieTabitha fadescreen FADE_TO_BLACK diff --git a/include/battle.h b/include/battle.h index 7b08c25b09..3e07784adb 100644 --- a/include/battle.h +++ b/include/battle.h @@ -1133,7 +1133,6 @@ extern u16 gMoveToLearn; extern u32 gFieldStatuses; extern struct FieldTimer gFieldTimers; extern u8 gBattlerAbility; -extern u16 gPartnerSpriteId; extern struct QueuedStatBoost gQueuedStatBoosts[MAX_BATTLERS_COUNT]; extern const struct BattleMoveEffect gBattleMoveEffects[]; diff --git a/include/battle_script_commands.h b/include/battle_script_commands.h index dc9a5c9290..fe9c7f1e64 100644 --- a/include/battle_script_commands.h +++ b/include/battle_script_commands.h @@ -24,7 +24,7 @@ struct PickupItem s32 CalcCritChanceStageArgs(u32 battlerAtk, u32 battlerDef, u32 move, bool32 recordAbility, u32 abilityAtk, u32 abilityDef, u32 holdEffectAtk); s32 CalcCritChanceStage(u32 battlerAtk, u32 battlerDef, u32 move, bool32 recordAbility); -s32 GetCritHitChance(s32 critChanceIndex); +s32 GetCritHitOdds(s32 critChanceIndex); u32 GetTotalAccuracy(u32 battlerAtk, u32 battlerDef, u32 move, u32 atkAbility, u32 defAbility, u32 atkHoldEffect, u32 defHoldEffect); u8 GetBattlerTurnOrderNum(u8 battlerId); bool32 NoAliveMonsForPlayer(void); diff --git a/include/battle_util.h b/include/battle_util.h index 7861966a7b..543cfdaecc 100644 --- a/include/battle_util.h +++ b/include/battle_util.h @@ -256,7 +256,6 @@ bool32 CanBeConfused(u32 battler); bool32 IsBattlerTerrainAffected(u32 battler, u32 terrainFlag); u32 GetBattlerAffectionHearts(u32 battler); u32 CountBattlerStatIncreases(u32 battler, bool32 countEvasionAcc); -bool32 IsMyceliumMightOnField(void); bool32 ChangeTypeBasedOnTerrain(u32 battler); void RemoveConfusionStatus(u32 battler); u8 GetBattlerGender(u32 battler); diff --git a/include/constants/form_change_types.h b/include/constants/form_change_types.h index b371c84067..ac1f53bb51 100644 --- a/include/constants/form_change_types.h +++ b/include/constants/form_change_types.h @@ -24,6 +24,8 @@ // param2: time of day to check, optional. // - DAY if Form change that activates in the daytime. // - NIGHT if Form change that activates at nighttime. +// - 0 if irrelevant, but param3 is necessary. +// param3: illegal statuses to have, optional. #define FORM_CHANGE_ITEM_USE 2 // TODO: Form change that activates when the Pokémon learns or forgets the move. diff --git a/include/daycare.h b/include/daycare.h index 2747a66850..4d5b470f8b 100644 --- a/include/daycare.h +++ b/include/daycare.h @@ -32,7 +32,7 @@ void SetDaycareCompatibilityString(void); bool8 NameHasGenderSymbol(const u8 *name, u8 genderRatio); void ShowDaycareLevelMenu(void); void ChooseSendDaycareMon(void); -u8 GetEggMovesSpecies(u16 species, u16 *eggMoves); +u8 GetEggMovesBySpecies(u16 species, u16 *eggMoves); bool8 SpeciesCanLearnEggMove(u16 species, u16 move); #endif // GUARD_DAYCARE_H diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index 162649d4b1..478b2e70fe 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -560,20 +560,33 @@ s32 AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u8 *typeEffectivenes aiData->abilities[battlerAtk], aiData->abilities[battlerDef]); critChanceIndex = CalcCritChanceStageArgs(battlerAtk, battlerDef, move, FALSE, aiData->abilities[battlerAtk], aiData->abilities[battlerDef], aiData->holdEffects[battlerAtk]); - if (critChanceIndex > 1) // Consider crit damage only if a move has at least +1 crit chance + if (critChanceIndex > 1) // Consider crit damage only if a move has at least +2 crit chance { s32 critDmg = CalculateMoveDamageVars(move, battlerAtk, battlerDef, moveType, fixedBasePower, effectivenessMultiplier, weather, TRUE, aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef], aiData->abilities[battlerAtk], aiData->abilities[battlerDef]); - u32 critChance = GetCritHitChance(critChanceIndex); - // With critChance getting closer to 1, dmg gets closer to critDmg. + u32 critOdds = GetCritHitOdds(critChanceIndex); + // With critOdds getting closer to 1, dmg gets closer to critDmg. if (dmgRoll == DMG_ROLL_DEFAULT) - dmg = DmgRoll((critDmg + normalDmg * (critChance - 1)) / (critChance)); + dmg = DmgRoll((critDmg + normalDmg * (critOdds - 1)) / (critOdds)); else if (dmgRoll == DMG_ROLL_HIGHEST) - dmg = HighestRollDmg((critDmg + normalDmg * (critChance - 1)) / (critChance)); + dmg = HighestRollDmg((critDmg + normalDmg * (critOdds - 1)) / (critOdds)); else - dmg = LowestRollDmg((critDmg + normalDmg * (critChance - 1)) / (critChance)); // Default to lowest roll + dmg = LowestRollDmg((critDmg + normalDmg * (critOdds - 1)) / (critOdds)); // Default to lowest roll + } + else if (critChanceIndex == -2) // Guaranteed critical + { + s32 critDmg = CalculateMoveDamageVars(move, battlerAtk, battlerDef, moveType, fixedBasePower, + effectivenessMultiplier, weather, TRUE, + aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef], + aiData->abilities[battlerAtk], aiData->abilities[battlerDef]); + if (dmgRoll == DMG_ROLL_DEFAULT) + dmg = DmgRoll(critDmg); + else if (dmgRoll == DMG_ROLL_HIGHEST) + dmg = HighestRollDmg(critDmg); + else + dmg = LowestRollDmg(critDmg); // Default to lowest roll } else { diff --git a/src/battle_controller_player_partner.c b/src/battle_controller_player_partner.c index 5c3fd41762..b817adf097 100644 --- a/src/battle_controller_player_partner.c +++ b/src/battle_controller_player_partner.c @@ -435,7 +435,7 @@ static void PlayerPartnerHandleIntroTrainerBallThrow(u32 battler) const u32 *trainerPal; if (gPartnerTrainerId > TRAINER_PARTNER(PARTNER_NONE)) - trainerPal = gTrainerBacksprites[gPartnerSpriteId].palette.data; + trainerPal = gTrainerBacksprites[gBattlePartners[gPartnerTrainerId - TRAINER_PARTNER(PARTNER_NONE)].trainerPic].palette.data; else if (IsAiVsAiBattle()) trainerPal = gTrainerSprites[GetTrainerPicFromId(gPartnerTrainerId)].palette.data; else diff --git a/src/battle_main.c b/src/battle_main.c index 8c2d5b7de7..3168b95b3a 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -221,7 +221,6 @@ EWRAM_DATA u16 gMoveToLearn = 0; EWRAM_DATA u32 gFieldStatuses = 0; EWRAM_DATA struct FieldTimer gFieldTimers = {0}; EWRAM_DATA u8 gBattlerAbility = 0; -EWRAM_DATA u16 gPartnerSpriteId = 0; EWRAM_DATA struct QueuedStatBoost gQueuedStatBoosts[MAX_BATTLERS_COUNT] = {0}; EWRAM_DATA bool8 gHasFetchedBall = FALSE; EWRAM_DATA u8 gLastUsedBall = 0; diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index ceb4220523..b73e05d45f 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -1857,11 +1857,11 @@ static void Cmd_ppreduce(void) // The chance is 1/N for each stage. #if B_CRIT_CHANCE >= GEN_7 - static const u8 sCriticalHitChance[] = {24, 8, 2, 1, 1}; + static const u8 sCriticalHitOdds[] = {24, 8, 2, 1, 1}; #elif B_CRIT_CHANCE == GEN_6 - static const u8 sCriticalHitChance[] = {16, 8, 2, 1, 1}; + static const u8 sCriticalHitOdds[] = {16, 8, 2, 1, 1}; #else - static const u8 sCriticalHitChance[] = {16, 8, 4, 3, 2}; // Gens 2,3,4,5 + static const u8 sCriticalHitOdds[] = {16, 8, 4, 3, 2}; // Gens 2,3,4,5 #endif // B_CRIT_CHANCE #define BENEFITS_FROM_LEEK(battler, holdEffect)((holdEffect == HOLD_EFFECT_LEEK) && (GET_BASE_SPECIES_ID(gBattleMons[battler].species) == SPECIES_FARFETCHD || gBattleMons[battler].species == SPECIES_SIRFETCHD)) @@ -1869,8 +1869,7 @@ s32 CalcCritChanceStageArgs(u32 battlerAtk, u32 battlerDef, u32 move, bool32 rec { s32 critChance = 0; - if (gSideStatuses[battlerDef] & SIDE_STATUS_LUCKY_CHANT - || abilityDef == ABILITY_BATTLE_ARMOR || abilityDef == ABILITY_SHELL_ARMOR) + if (gSideStatuses[battlerDef] & SIDE_STATUS_LUCKY_CHANT) { critChance = -1; } @@ -1892,12 +1891,21 @@ s32 CalcCritChanceStageArgs(u32 battlerAtk, u32 battlerDef, u32 move, bool32 rec + (abilityAtk == ABILITY_SUPER_LUCK) + gBattleStruct->bonusCritStages[gBattlerAttacker]; - // Record ability only if move had at least +3 chance to get a crit - if (critChance >= 3 && recordAbility && (abilityDef == ABILITY_BATTLE_ARMOR || abilityDef == ABILITY_SHELL_ARMOR)) - RecordAbilityBattle(battlerDef, abilityDef); + if (critChance >= ARRAY_COUNT(sCriticalHitOdds)) + critChance = ARRAY_COUNT(sCriticalHitOdds) - 1; + } - if (critChance >= ARRAY_COUNT(sCriticalHitChance)) - critChance = ARRAY_COUNT(sCriticalHitChance) - 1; + if (critChance != -1 && (abilityDef == ABILITY_BATTLE_ARMOR || abilityDef == ABILITY_SHELL_ARMOR)) + { + // Record ability only if move had 100% chance to get a crit + if (recordAbility) + { + if (critChance == -2) + RecordAbilityBattle(battlerDef, abilityDef); + else if (sCriticalHitOdds[critChance] == 1) + RecordAbilityBattle(battlerDef, abilityDef); + } + critChance = -1; } return critChance; @@ -1912,12 +1920,12 @@ s32 CalcCritChanceStage(u32 battlerAtk, u32 battlerDef, u32 move, bool32 recordA } #undef BENEFITS_FROM_LEEK -s32 GetCritHitChance(s32 critChanceIndex) +s32 GetCritHitOdds(s32 critChanceIndex) { if (critChanceIndex < 0) return -1; else - return sCriticalHitChance[critChanceIndex]; + return sCriticalHitOdds[critChanceIndex]; } static void Cmd_critcalc(void) @@ -1935,7 +1943,7 @@ static void Cmd_critcalc(void) else if (critChance == -2) gIsCriticalHit = TRUE; else - gIsCriticalHit = RandomWeighted(RNG_CRITICAL_HIT, sCriticalHitChance[critChance] - 1, 1); + gIsCriticalHit = RandomWeighted(RNG_CRITICAL_HIT, sCriticalHitOdds[critChance] - 1, 1); // Counter for EVO_CRITICAL_HITS. partySlot = gBattlerPartyIndexes[gBattlerAttacker]; diff --git a/src/battle_tower.c b/src/battle_tower.c index c5b4fcbef1..2fa78c7aa6 100644 --- a/src/battle_tower.c +++ b/src/battle_tower.c @@ -2125,7 +2125,6 @@ void DoSpecialTrainerBattle(void) gBattleTypeFlags = BATTLE_TYPE_TRAINER | BATTLE_TYPE_DOUBLE | BATTLE_TYPE_TWO_OPPONENTS | BATTLE_TYPE_MULTI | BATTLE_TYPE_INGAME_PARTNER; } - gPartnerSpriteId = VarGet(gSpecialVar_0x8007); gPartnerTrainerId = VarGet(gSpecialVar_0x8006) + TRAINER_PARTNER(PARTNER_NONE); FillPartnerParty(gPartnerTrainerId); CreateTask(Task_StartBattleAfterTransition, 1); diff --git a/src/battle_util.c b/src/battle_util.c index 5d40b4508b..845ff657d3 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -3571,7 +3571,7 @@ u8 AtkCanceller_UnableToUseMove(u32 moveType) case CANCELLER_MULTIHIT_MOVES: if (gMovesInfo[gCurrentMove].effect == EFFECT_MULTI_HIT) { - u16 ability = gBattleMons[gBattlerAttacker].ability; + u32 ability = GetBattlerAbility(gBattlerAttacker); if (ability == ABILITY_SKILL_LINK) { @@ -3993,14 +3993,12 @@ static inline uq4_12_t GetSupremeOverlordModifier(u32 battler) return UQ_4_12(1.0) + (UQ_4_12(0.1) * gBattleStruct->supremeOverlordCounter[battler]); } -static inline bool32 HadMoreThanHalfHpNowHasLess(u32 battler) +static inline bool32 HadMoreThanHalfHpNowDoesnt(u32 battler) { u32 cutoff = gBattleMons[battler].maxHP / 2; - if (gBattleMons[battler].maxHP % 2 == 1) - cutoff++; // Had more than half of hp before, now has less - return (gBattleStruct->hpBefore[battler] >= cutoff - && gBattleMons[battler].hp < cutoff); + return (gBattleStruct->hpBefore[battler] > cutoff + && gBattleMons[battler].hp <= cutoff); } u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 moveArg) @@ -5288,7 +5286,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && TARGET_TURN_DAMAGED && IsBattlerAlive(battler) - && HadMoreThanHalfHpNowHasLess(battler) + && HadMoreThanHalfHpNowDoesnt(battler) && (gMultiHitCounter == 0 || gMultiHitCounter == 1) && !(TestIfSheerForceAffected(gBattlerAttacker, gCurrentMove)) && CompareStat(battler, STAT_SPATK, MAX_STAT_STAGE, CMP_LESS_THAN)) @@ -5306,7 +5304,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 && TARGET_TURN_DAMAGED && IsBattlerAlive(battler) // Had more than half of hp before, now has less - && HadMoreThanHalfHpNowHasLess(battler) + && HadMoreThanHalfHpNowDoesnt(battler) && (gMultiHitCounter == 0 || gMultiHitCounter == 1) && !(TestIfSheerForceAffected(gBattlerAttacker, gCurrentMove)) && (CanBattlerSwitch(battler) || !(gBattleTypeFlags & BATTLE_TYPE_TRAINER)) @@ -5747,7 +5745,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 && TARGET_TURN_DAMAGED && (gMultiHitCounter == 0 || gMultiHitCounter == 1) // Activates after all hits from a multi-hit move. && IsBattlerAlive(gBattlerTarget) - && HadMoreThanHalfHpNowHasLess(gBattlerTarget) + && HadMoreThanHalfHpNowDoesnt(gBattlerTarget) && !(TestIfSheerForceAffected(gBattlerAttacker, gCurrentMove))) { gBattlerAttacker = gBattlerTarget; @@ -6235,26 +6233,16 @@ bool32 IsNeutralizingGasOnField(void) return FALSE; } -bool32 IsMyceliumMightOnField(void) -{ - u32 i; - - for (i = 0; i < gBattlersCount; i++) - { - if (IsBattlerAlive(i) && gBattleMons[i].ability == ABILITY_MYCELIUM_MIGHT && IS_MOVE_STATUS(gCurrentMove)) - return TRUE; - } - - return FALSE; -} - bool32 IsMoldBreakerTypeAbility(u32 ability) { - return (ability == ABILITY_MOLD_BREAKER || ability == ABILITY_TERAVOLT || ability == ABILITY_TURBOBLAZE); + return (ability == ABILITY_MOLD_BREAKER || ability == ABILITY_TERAVOLT || ability == ABILITY_TURBOBLAZE + || (ability == ABILITY_MYCELIUM_MIGHT && IS_MOVE_STATUS(gCurrentMove))); } u32 GetBattlerAbility(u32 battler) { + bool32 noAbilityShield = GetBattlerHoldEffectIgnoreAbility(battler, TRUE) != HOLD_EFFECT_ABILITY_SHIELD; + if (gAbilitiesInfo[gBattleMons[battler].ability].cantBeSuppressed) return gBattleMons[battler].ability; @@ -6263,16 +6251,14 @@ u32 GetBattlerAbility(u32 battler) if (IsNeutralizingGasOnField() && gBattleMons[battler].ability != ABILITY_NEUTRALIZING_GAS - && GetBattlerHoldEffectIgnoreAbility(battler, TRUE) != HOLD_EFFECT_ABILITY_SHIELD) - return ABILITY_NONE; - - if (IsMyceliumMightOnField()) + && noAbilityShield) return ABILITY_NONE; if (((IsMoldBreakerTypeAbility(gBattleMons[gBattlerAttacker].ability) && !(gStatuses3[gBattlerAttacker] & STATUS3_GASTRO_ACID)) || gMovesInfo[gCurrentMove].ignoresTargetAbility) && gAbilitiesInfo[gBattleMons[battler].ability].breakable + && noAbilityShield && gBattlerByTurnOrder[gCurrentTurnActionNumber] == gBattlerAttacker && gActionsByTurnOrder[gBattlerByTurnOrder[gBattlerAttacker]] == B_ACTION_USE_MOVE && gCurrentTurnActionNumber < gBattlersCount) @@ -10160,8 +10146,6 @@ static inline void MulByTypeEffectiveness(uq4_12_t *modifier, u32 move, u32 move mod = UQ_4_12(2.0); if (moveType == TYPE_GROUND && defType == TYPE_FLYING && IsBattlerGrounded(battlerDef) && mod == UQ_4_12(0.0)) mod = UQ_4_12(1.0); - if (moveType == TYPE_FIRE && gDisableStructs[battlerDef].tarShot) - mod = UQ_4_12(2.0); if (moveType == TYPE_STELLAR && IsTerastallized(battlerDef)) mod = UQ_4_12(2.0); @@ -10227,6 +10211,8 @@ static inline uq4_12_t CalcTypeEffectivenessMultiplierInternal(u32 move, u32 mov if (GetBattlerType(battlerDef, 2, FALSE) != TYPE_MYSTERY && GetBattlerType(battlerDef, 2, FALSE) != GetBattlerType(battlerDef, 1, FALSE) && GetBattlerType(battlerDef, 2, FALSE) != GetBattlerType(battlerDef, 0, FALSE)) MulByTypeEffectiveness(&modifier, move, moveType, battlerDef, GetBattlerType(battlerDef, 2, FALSE), battlerAtk, recordAbilities); + if (moveType == TYPE_FIRE && gDisableStructs[battlerDef].tarShot) + modifier = uq4_12_multiply(modifier, UQ_4_12(2.0)); if (recordAbilities && (illusionSpecies = GetIllusionMonSpecies(battlerDef))) TryNoticeIllusionInTypeEffectiveness(move, moveType, battlerAtk, battlerDef, modifier, illusionSpecies); @@ -11359,7 +11345,7 @@ void RemoveConfusionStatus(u32 battler) static bool32 CanBeInfinitelyConfused(u32 battler) { - if (gBattleMons[battler].ability == ABILITY_OWN_TEMPO + if (GetBattlerAbility(battler) == ABILITY_OWN_TEMPO || IsBattlerTerrainAffected(battler, STATUS_FIELD_MISTY_TERRAIN) || gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_SAFEGUARD) { diff --git a/src/data/items.h b/src/data/items.h index 24a884a0dd..daf1f9b957 100644 --- a/src/data/items.h +++ b/src/data/items.h @@ -642,7 +642,7 @@ const struct Item gItemsInfo[] = [ITEM_HYPER_POTION] = { .name = _("Hyper Potion"), - .price = (I_PRICE >= GEN_2 || I_PRICE <= GEN_6) ? 1200 : 1500, + .price = (I_PRICE >= GEN_2 && I_PRICE <= GEN_6) ? 1200 : 1500, .holdEffectParam = 120, .description = COMPOUND_STRING( "Restores the HP of\n" diff --git a/src/data/pokemon/form_change_tables.h b/src/data/pokemon/form_change_tables.h index 85ff50940a..78de99f140 100644 --- a/src/data/pokemon/form_change_tables.h +++ b/src/data/pokemon/form_change_tables.h @@ -624,7 +624,7 @@ static const struct FormChange sGiratinaFormChangeTable[] = { #if P_FAMILY_SHAYMIN static const struct FormChange sShayminFormChangeTable[] = { - {FORM_CHANGE_ITEM_USE, SPECIES_SHAYMIN_SKY, ITEM_GRACIDEA, DAY}, + {FORM_CHANGE_ITEM_USE, SPECIES_SHAYMIN_SKY, ITEM_GRACIDEA, DAY, STATUS1_FREEZE | STATUS1_FROSTBITE}, {FORM_CHANGE_WITHDRAW, SPECIES_SHAYMIN_LAND}, {FORM_CHANGE_TIME_OF_DAY, SPECIES_SHAYMIN_LAND, NIGHT}, {FORM_CHANGE_STATUS, SPECIES_SHAYMIN_LAND, STATUS1_FREEZE | STATUS1_FROSTBITE}, diff --git a/src/daycare.c b/src/daycare.c index 02bd889b94..7efe8612c4 100644 --- a/src/daycare.c +++ b/src/daycare.c @@ -34,6 +34,7 @@ static void SetInitialEggData(struct Pokemon *mon, u16 species, struct DayCare * static void DaycarePrintMonInfo(u8 windowId, u32 daycareSlotId, u8 y); static u8 ModifyBreedingScoreForOvalCharm(u8 score); static u8 GetEggMoves(struct Pokemon *pokemon, u16 *eggMoves); +static u16 GetEggSpecies(u16 species); // RAM buffers used to assist with BuildEggMoveset() EWRAM_DATA static u16 sHatchedEggLevelUpMoves[EGG_LVL_UP_MOVES_ARRAY_COUNT] = {0}; @@ -84,6 +85,24 @@ static const struct ListMenuTemplate sDaycareListMenuLevelTemplate = .cursorKind = CURSOR_BLACK_ARROW }; +static const struct { + u16 currSpecies; + u16 item; + u16 babySpecies; +} sIncenseBabyTable[] = +{ + // Regular offspring, Item, Incense Offspring + { SPECIES_WOBBUFFET, ITEM_LAX_INCENSE, SPECIES_WYNAUT }, + { SPECIES_MARILL, ITEM_SEA_INCENSE, SPECIES_AZURILL }, + { SPECIES_SNORLAX, ITEM_FULL_INCENSE, SPECIES_MUNCHLAX }, + { SPECIES_CHANSEY, ITEM_LUCK_INCENSE, SPECIES_HAPPINY }, + { SPECIES_MR_MIME, ITEM_ODD_INCENSE, SPECIES_MIME_JR }, + { SPECIES_CHIMECHO, ITEM_PURE_INCENSE, SPECIES_CHINGLING }, + { SPECIES_SUDOWOODO, ITEM_ROCK_INCENSE, SPECIES_BONSLY }, + { SPECIES_ROSELIA, ITEM_ROSE_INCENSE, SPECIES_BUDEW }, + { SPECIES_MANTINE, ITEM_WAVE_INCENSE, SPECIES_MANTYKE }, +}; + static const u8 *const sCompatibilityMessages[] = { gDaycareText_GetAlongVeryWell, @@ -172,26 +191,42 @@ static void TransferEggMoves(void) { u32 i, j, k, l; u16 numEggMoves; - struct Pokemon mon; for (i = 0; i < DAYCARE_MON_COUNT; i++) { + u16 moveLearnerSpecies = GetBoxMonData(&gSaveBlock1Ptr->daycare.mons[i].mon, MON_DATA_SPECIES); + u16 eggSpecies = GetEggSpecies(moveLearnerSpecies); + if (!GetBoxMonData(&gSaveBlock1Ptr->daycare.mons[i].mon, MON_DATA_SANITY_HAS_SPECIES)) continue; - BoxMonToMon(&gSaveBlock1Ptr->daycare.mons[i].mon, &mon); + // Prevent non-baby species from learning incense baby egg moves + if (P_INCENSE_BREEDING < GEN_9 && eggSpecies != moveLearnerSpecies) + { + for (j = 0; j < ARRAY_COUNT(sIncenseBabyTable); j++) + { + if (sIncenseBabyTable[j].babySpecies == eggSpecies) + { + eggSpecies = sIncenseBabyTable[j].currSpecies; + break; + } + } + } + ClearHatchedEggMoves(); - numEggMoves = GetEggMoves(&mon, sHatchedEggEggMoves); + numEggMoves = GetEggMovesBySpecies(eggSpecies, sHatchedEggEggMoves); for (j = 0; j < numEggMoves; j++) { // Go through other Daycare mons for (k = 0; k < DAYCARE_MON_COUNT; k++) { + u16 moveTeacherSpecies = GetBoxMonData(&gSaveBlock1Ptr->daycare.mons[k].mon, MON_DATA_SPECIES); + if (k == i || !GetBoxMonData(&gSaveBlock1Ptr->daycare.mons[k].mon, MON_DATA_SANITY_HAS_SPECIES)) continue; // Check if you can inherit from them - if (GetBoxMonData(&gSaveBlock1Ptr->daycare.mons[k].mon, MON_DATA_SPECIES) != GetBoxMonData(&gSaveBlock1Ptr->daycare.mons[i].mon, MON_DATA_SPECIES) + if (GET_BASE_SPECIES_ID(moveTeacherSpecies) != GET_BASE_SPECIES_ID(moveLearnerSpecies) && (P_EGG_MOVE_TRANSFER < GEN_9 || GetBoxMonData(&gSaveBlock1Ptr->daycare.mons[i].mon, MON_DATA_HELD_ITEM) != ITEM_MIRROR_HERB) ) continue; @@ -757,7 +792,7 @@ static u8 GetEggMoves(struct Pokemon *pokemon, u16 *eggMoves) return numEggMoves; } -u8 GetEggMovesSpecies(u16 species, u16 *eggMoves) +u8 GetEggMovesBySpecies(u16 species, u16 *eggMoves) { u16 numEggMoves; const u16 *eggMoveLearnset; @@ -916,26 +951,6 @@ void RejectEggFromDayCare(void) RemoveEggFromDayCare(&gSaveBlock1Ptr->daycare); } - -static const struct { - u16 currSpecies; - u16 item; - u16 babySpecies; -} sIncenseBabyTable[] = -{ - // Regular offspring, Item, Incense Offspring - { SPECIES_WOBBUFFET, ITEM_LAX_INCENSE, SPECIES_WYNAUT }, - { SPECIES_MARILL, ITEM_SEA_INCENSE, SPECIES_AZURILL }, - { SPECIES_SNORLAX, ITEM_FULL_INCENSE, SPECIES_MUNCHLAX }, - { SPECIES_CHANSEY, ITEM_LUCK_INCENSE, SPECIES_HAPPINY }, - { SPECIES_MR_MIME, ITEM_ODD_INCENSE, SPECIES_MIME_JR }, - { SPECIES_CHIMECHO, ITEM_PURE_INCENSE, SPECIES_CHINGLING }, - { SPECIES_SUDOWOODO, ITEM_ROCK_INCENSE, SPECIES_BONSLY }, - { SPECIES_ROSELIA, ITEM_ROSE_INCENSE, SPECIES_BUDEW }, - { SPECIES_MANTINE, ITEM_WAVE_INCENSE, SPECIES_MANTYKE }, -}; - -#if P_INCENSE_BREEDING < GEN_9 static void AlterEggSpeciesWithIncenseItem(u16 *species, struct DayCare *daycare) { u32 i; @@ -952,7 +967,6 @@ static void AlterEggSpeciesWithIncenseItem(u16 *species, struct DayCare *daycare } } } -#endif static const struct { u16 offspring; @@ -1057,9 +1071,8 @@ static void _GiveEggFromDaycare(struct DayCare *daycare) bool8 isEgg; species = DetermineEggSpeciesAndParentSlots(daycare, parentSlots); -#if P_INCENSE_BREEDING < GEN_9 - AlterEggSpeciesWithIncenseItem(&species, daycare); -#endif + if (P_INCENSE_BREEDING < GEN_9) + AlterEggSpeciesWithIncenseItem(&species, daycare); SetInitialEggData(&egg, species, daycare); InheritIVs(&egg, daycare); InheritPokeball(&egg, &daycare->mons[parentSlots[1]].mon, &daycare->mons[parentSlots[0]].mon); diff --git a/src/pokedex_plus_hgss.c b/src/pokedex_plus_hgss.c index 22cbd5ba14..3599fb1ccb 100644 --- a/src/pokedex_plus_hgss.c +++ b/src/pokedex_plus_hgss.c @@ -5077,7 +5077,7 @@ static bool8 CalculateMoves(void) species = GetFormSpeciesId(species, 0); //Calculate amount of Egg and LevelUp moves - numEggMoves = GetEggMovesSpecies(species, statsMovesEgg); + numEggMoves = GetEggMovesBySpecies(species, statsMovesEgg); numLevelUpMoves = GetLevelUpMovesBySpecies(species, statsMovesLevelUp); //Egg moves diff --git a/src/pokemon.c b/src/pokemon.c index a706c95786..0d1512dae8 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -6471,20 +6471,24 @@ u16 GetFormChangeTargetSpeciesBoxMon(struct BoxPokemon *boxMon, u16 method, u32 case FORM_CHANGE_ITEM_USE: if (arg == formChanges[i].param1) { + bool32 pass = TRUE; switch (formChanges[i].param2) { case DAY: - if (GetTimeOfDay() != TIME_NIGHT) - targetSpecies = formChanges[i].targetSpecies; + if (GetTimeOfDay() == TIME_NIGHT) + pass = FALSE; break; case NIGHT: - if (GetTimeOfDay() == TIME_NIGHT) - targetSpecies = formChanges[i].targetSpecies; - break; - default: - targetSpecies = formChanges[i].targetSpecies; + if (GetTimeOfDay() != TIME_NIGHT) + pass = FALSE; break; } + + if (formChanges[i].param3 != STATUS1_NONE && GetBoxMonData(boxMon, MON_DATA_STATUS, NULL) & formChanges[i].param3) + pass = FALSE; + + if (pass) + targetSpecies = formChanges[i].targetSpecies; } break; case FORM_CHANGE_ITEM_USE_MULTICHOICE: diff --git a/test/battle/ability/anger_shell.c b/test/battle/ability/anger_shell.c index 3591c4077f..f0d11d7576 100644 --- a/test/battle/ability/anger_shell.c +++ b/test/battle/ability/anger_shell.c @@ -6,6 +6,7 @@ SINGLE_BATTLE_TEST("Anger Shell activates only if the target had more than 50% o bool32 activates = FALSE; u16 maxHp = 500, hp = 0; + PARAMETRIZE { hp = 250; activates = FALSE; } PARAMETRIZE { hp = 249; activates = FALSE; } PARAMETRIZE { hp = 100; activates = FALSE; } PARAMETRIZE { hp = 50; activates = FALSE; } @@ -41,7 +42,7 @@ SINGLE_BATTLE_TEST("Anger Shell lowers Def/Sp.Def by 1 and raises Atk/Sp.Atk/Spd u16 maxHp = 500; GIVEN { ASSUME(gMovesInfo[MOVE_TACKLE].power != 0); - PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_ANGER_SHELL); MaxHP(maxHp); HP(maxHp / 2 + 1); } + PLAYER(SPECIES_KLAWF) { Ability(ABILITY_ANGER_SHELL); MaxHP(maxHp); HP(maxHp / 2 + 1); } OPPONENT(SPECIES_WOBBUFFET); } WHEN { TURN { MOVE(opponent, MOVE_TACKLE); } @@ -49,15 +50,15 @@ SINGLE_BATTLE_TEST("Anger Shell lowers Def/Sp.Def by 1 and raises Atk/Sp.Atk/Spd ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent); ABILITY_POPUP(player, ABILITY_ANGER_SHELL); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); - MESSAGE("Wobbuffet's Defense fell!"); + MESSAGE("Klawf's Defense fell!"); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); - MESSAGE("Wobbuffet's Sp. Def fell!"); + MESSAGE("Klawf's Sp. Def fell!"); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); - MESSAGE("Wobbuffet's Attack rose!"); + MESSAGE("Klawf's Attack rose!"); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); - MESSAGE("Wobbuffet's Sp. Atk rose!"); + MESSAGE("Klawf's Sp. Atk rose!"); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); - MESSAGE("Wobbuffet's Speed rose!"); + MESSAGE("Klawf's Speed rose!"); } THEN { EXPECT_EQ(player->statStages[STAT_DEF], DEFAULT_STAT_STAGE - 1); EXPECT_EQ(player->statStages[STAT_SPDEF], DEFAULT_STAT_STAGE - 1); @@ -73,13 +74,12 @@ SINGLE_BATTLE_TEST("Anger Shell activates after all hits from a multi-hit move") u16 maxHp = 500; GIVEN { ASSUME(gMovesInfo[MOVE_DOUBLE_SLAP].effect == EFFECT_MULTI_HIT); - PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_ANGER_SHELL); MaxHP(maxHp); HP(maxHp / 2 + 1); } + PLAYER(SPECIES_KLAWF) { Ability(ABILITY_ANGER_SHELL); MaxHP(maxHp); HP(maxHp / 2 + 1); } OPPONENT(SPECIES_SHELLDER) { Ability(ABILITY_SKILL_LINK); } // Always hits 5 times. } WHEN { TURN { MOVE(opponent, MOVE_DOUBLE_SLAP); } } SCENE { - for (j = 0; j < 4; j++) - { + for (j = 0; j < 4; j++) { ANIMATION(ANIM_TYPE_MOVE, MOVE_DOUBLE_SLAP, opponent); NOT ABILITY_POPUP(player, ABILITY_ANGER_SHELL); } diff --git a/test/battle/ability/berserk.c b/test/battle/ability/berserk.c new file mode 100644 index 0000000000..3bf269e1ee --- /dev/null +++ b/test/battle/ability/berserk.c @@ -0,0 +1,75 @@ +#include "global.h" +#include "test/battle.h" + +SINGLE_BATTLE_TEST("Berserk activates only if the target had more than 50% of its hp") +{ + bool32 activates = FALSE; + u16 maxHp = 500, hp = 0; + + PARAMETRIZE { hp = 250; activates = FALSE; } + PARAMETRIZE { hp = 249; activates = FALSE; } + PARAMETRIZE { hp = 100; activates = FALSE; } + PARAMETRIZE { hp = 50; activates = FALSE; } + PARAMETRIZE { hp = 251; activates = TRUE; } + PARAMETRIZE { hp = 254; activates = TRUE; } + + GIVEN { + ASSUME(gMovesInfo[MOVE_TACKLE].power != 0); + PLAYER(SPECIES_DRAMPA) { Ability(ABILITY_BERSERK); MaxHP(maxHp); HP(hp); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_TACKLE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent); + if (activates) { + ABILITY_POPUP(player, ABILITY_BERSERK); + } else { + NOT ABILITY_POPUP(player, ABILITY_BERSERK); + } + } THEN { + if (activates) { + EXPECT_EQ(player->statStages[STAT_SPATK], DEFAULT_STAT_STAGE + 1); + } + } +} + +SINGLE_BATTLE_TEST("Berserk raises Sp.Atk by 1") +{ + u16 maxHp = 500; + GIVEN { + ASSUME(gMovesInfo[MOVE_TACKLE].power != 0); + PLAYER(SPECIES_DRAMPA) { Ability(ABILITY_BERSERK); MaxHP(maxHp); HP(maxHp / 2 + 1); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_TACKLE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent); + ABILITY_POPUP(player, ABILITY_BERSERK); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + MESSAGE("Drampa's Sp. Atk rose!"); + } THEN { + EXPECT_EQ(player->statStages[STAT_SPATK], DEFAULT_STAT_STAGE + 1); + } +} + +SINGLE_BATTLE_TEST("Berserk activates after all hits from a multi-hit move") +{ + u32 j; + u16 maxHp = 500; + GIVEN { + ASSUME(gMovesInfo[MOVE_DOUBLE_SLAP].effect == EFFECT_MULTI_HIT); + PLAYER(SPECIES_DRAMPA) { Ability(ABILITY_BERSERK); MaxHP(maxHp); HP(maxHp / 2 + 1); } + OPPONENT(SPECIES_SHELLDER) { Ability(ABILITY_SKILL_LINK); } // Always hits 5 times. + } WHEN { + TURN { MOVE(opponent, MOVE_DOUBLE_SLAP); } + } SCENE { + for (j = 0; j < 4; j++) { + ANIMATION(ANIM_TYPE_MOVE, MOVE_DOUBLE_SLAP, opponent); + NOT ABILITY_POPUP(player, ABILITY_BERSERK); + } + ANIMATION(ANIM_TYPE_MOVE, MOVE_DOUBLE_SLAP, opponent); + ABILITY_POPUP(player, ABILITY_BERSERK); + } THEN { + EXPECT_EQ(player->statStages[STAT_SPATK], DEFAULT_STAT_STAGE + 1); + } +} diff --git a/test/battle/ability/clear_body.c b/test/battle/ability/clear_body.c index 7f167c9fe7..5f0efd265e 100644 --- a/test/battle/ability/clear_body.c +++ b/test/battle/ability/clear_body.c @@ -17,7 +17,9 @@ SINGLE_BATTLE_TEST("Clear Body prevents intimidate") } SCENE { HP_BAR(player, captureDamage: &turnOneHit); ABILITY_POPUP(player, ABILITY_INTIMIDATE); - NONE_OF { ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); } + NONE_OF { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); + } ABILITY_POPUP(opponent, ABILITY_CLEAR_BODY); MESSAGE("Foe Beldum's Clear Body prevents stat loss!"); HP_BAR(player, captureDamage: &turnTwoHit); @@ -26,13 +28,251 @@ SINGLE_BATTLE_TEST("Clear Body prevents intimidate") } } -TO_DO_BATTLE_TEST("Clear Body prevents stat stage reduction from moves"); // Growl, Leer, Confide, Fake Tears, Scary Face, Sweet Scent, Sand Attack (Attack, Defense, Sp. Attack, Sp. Defense, Speed, Evasion, Accuracy -TO_DO_BATTLE_TEST("Clear Body prevents Sticky Web"); -TO_DO_BATTLE_TEST("Clear Body doesn't prevent stat stage reduction from moves used by the user"); // e.g. Superpower -TO_DO_BATTLE_TEST("Clear Body doesn't prevent Speed reduction from Iron Ball"); -TO_DO_BATTLE_TEST("Clear Body doesn't prevent Speed reduction from paralysis"); -TO_DO_BATTLE_TEST("Clear Body doesn't prevent Attack reduction from burn"); -TO_DO_BATTLE_TEST("Clear Body doesn't prevent receiving negative stat changes from Baton Pass"); -TO_DO_BATTLE_TEST("Clear Body doesn't prevent Topsy-Turvy"); -TO_DO_BATTLE_TEST("Clear Body doesn't prevent Spectral Thief from resetting positive stat changes"); -TO_DO_BATTLE_TEST("Clear Body is ignored by Mold Breaker"); +SINGLE_BATTLE_TEST("Clear Body prevents stat stage reduction from moves") +{ + u16 move; + PARAMETRIZE{ move = MOVE_GROWL; } + PARAMETRIZE{ move = MOVE_LEER; } + PARAMETRIZE{ move = MOVE_CONFIDE; } + PARAMETRIZE{ move = MOVE_FAKE_TEARS; } + PARAMETRIZE{ move = MOVE_SCARY_FACE; } + PARAMETRIZE{ move = MOVE_SWEET_SCENT; } + PARAMETRIZE{ move = MOVE_SAND_ATTACK; } + + GIVEN { + ASSUME(gMovesInfo[MOVE_GROWL].effect == EFFECT_ATTACK_DOWN); + ASSUME(gMovesInfo[MOVE_LEER].effect == EFFECT_DEFENSE_DOWN); + ASSUME(gMovesInfo[MOVE_CONFIDE].effect == EFFECT_SPECIAL_ATTACK_DOWN); + ASSUME(gMovesInfo[MOVE_FAKE_TEARS].effect == EFFECT_SPECIAL_DEFENSE_DOWN_2); + ASSUME(gMovesInfo[MOVE_SCARY_FACE].effect == EFFECT_SPEED_DOWN_2); + ASSUME(gMovesInfo[MOVE_SWEET_SCENT].effect == (B_UPDATED_MOVE_DATA >= GEN_6 ? EFFECT_EVASION_DOWN_2 : EFFECT_EVASION_DOWN)); + ASSUME(gMovesInfo[MOVE_SAND_ATTACK].effect == EFFECT_ACCURACY_DOWN); + PLAYER(SPECIES_WOBBUFFET) + OPPONENT(SPECIES_BELDUM) { Ability(ABILITY_CLEAR_BODY); } + } WHEN { + TURN { MOVE(player, move); } + } SCENE { + NONE_OF { + ANIMATION(ANIM_TYPE_MOVE, move, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); + } + ABILITY_POPUP(opponent, ABILITY_CLEAR_BODY); + MESSAGE("Foe Beldum's Clear Body prevents stat loss!"); + } +} + +SINGLE_BATTLE_TEST("Clear Body prevents Sticky Web") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_STICKY_WEB].effect == EFFECT_STICKY_WEB); + PLAYER(SPECIES_WOBBUFFET) + OPPONENT(SPECIES_WOBBUFFET) + OPPONENT(SPECIES_BELDUM) { Ability(ABILITY_CLEAR_BODY); } + } WHEN { + TURN { MOVE(player, MOVE_STICKY_WEB); } + TURN { SWITCH(opponent, 1); } + } SCENE { + NONE_OF { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); + } + ABILITY_POPUP(opponent, ABILITY_CLEAR_BODY); + MESSAGE("Foe Beldum's Clear Body prevents stat loss!"); + } +} + +SINGLE_BATTLE_TEST("Clear Body doesn't prevent stat stage reduction from moves used by the user") +{ + GIVEN { + ASSUME(MoveHasAdditionalEffectSelf(MOVE_SUPERPOWER, MOVE_EFFECT_ATK_DEF_DOWN) == TRUE); + PLAYER(SPECIES_WOBBUFFET) + OPPONENT(SPECIES_BELDUM) { Ability(ABILITY_CLEAR_BODY); } + } WHEN { + TURN { MOVE(opponent, MOVE_SUPERPOWER); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SUPERPOWER, opponent); + NONE_OF { + ABILITY_POPUP(opponent, ABILITY_CLEAR_BODY); + MESSAGE("Foe Beldum's Clear Body prevents stat loss!"); + } + } +} + +SINGLE_BATTLE_TEST("Mold Breaker, Teravolt, and Turboblaze ignore Clear Body") +{ + u32 j, k; + u16 ability = ABILITY_NONE; + u16 move = ABILITY_NONE; + static const u16 breakerAbilities[] = { + ABILITY_MOLD_BREAKER, + ABILITY_TERAVOLT, + ABILITY_TURBOBLAZE, + }; + static const u16 statReductionMoves[] = { + MOVE_GROWL, + MOVE_LEER, + MOVE_CONFIDE, + MOVE_FAKE_TEARS, + MOVE_SCARY_FACE, + MOVE_SWEET_SCENT, + MOVE_SAND_ATTACK, + }; + + for (j = 0; j < ARRAY_COUNT(statReductionMoves); j++) + { + for (k = 0; k < ARRAY_COUNT(breakerAbilities); k++) + { + PARAMETRIZE{ move = statReductionMoves[j]; ability = breakerAbilities[k]; } + } + } + + GIVEN { + ASSUME(gMovesInfo[MOVE_GROWL].effect == EFFECT_ATTACK_DOWN); + ASSUME(gMovesInfo[MOVE_LEER].effect == EFFECT_DEFENSE_DOWN); + ASSUME(gMovesInfo[MOVE_CONFIDE].effect == EFFECT_SPECIAL_ATTACK_DOWN); + ASSUME(gMovesInfo[MOVE_FAKE_TEARS].effect == EFFECT_SPECIAL_DEFENSE_DOWN_2); + ASSUME(gMovesInfo[MOVE_SCARY_FACE].effect == EFFECT_SPEED_DOWN_2); + ASSUME(gMovesInfo[MOVE_SWEET_SCENT].effect == (B_UPDATED_MOVE_DATA >= GEN_6 ? EFFECT_EVASION_DOWN_2 : EFFECT_EVASION_DOWN)); + ASSUME(gMovesInfo[MOVE_SAND_ATTACK].effect == EFFECT_ACCURACY_DOWN); + PLAYER(SPECIES_WOBBUFFET) { Ability(ability); } + OPPONENT(SPECIES_BELDUM) { Ability(ABILITY_CLEAR_BODY); } + } WHEN { + TURN { MOVE(player, move); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, move, player); + NONE_OF { + ABILITY_POPUP(opponent, ABILITY_CLEAR_BODY); + MESSAGE("Foe Beldum's Clear Body prevents stat loss!"); + } + } +} + +SINGLE_BATTLE_TEST("Clear Body doesn't prevent Speed reduction from Iron Ball") +{ + u16 heldItem; + PARAMETRIZE{ heldItem = ITEM_NONE; } + PARAMETRIZE{ heldItem = ITEM_IRON_BALL; } + GIVEN { + ASSUME(gItemsInfo[ITEM_IRON_BALL].holdEffect == HOLD_EFFECT_IRON_BALL); + PLAYER(SPECIES_WOBBUFFET) { Speed(4); } + OPPONENT(SPECIES_BELDUM) { Speed(6); Ability(ABILITY_CLEAR_BODY); Item(heldItem); } + } WHEN { + TURN { } + } SCENE { + NOT ABILITY_POPUP(opponent, ABILITY_CLEAR_BODY); + if (heldItem == ITEM_IRON_BALL) { + MESSAGE("Wobbuffet used Celebrate!"); + MESSAGE("Foe Beldum used Celebrate!"); + } else { + MESSAGE("Foe Beldum used Celebrate!"); + MESSAGE("Wobbuffet used Celebrate!"); + } + } +} + +SINGLE_BATTLE_TEST("Clear Body doesn't prevent Speed reduction from paralysis") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Speed(4); } + OPPONENT(SPECIES_BELDUM) { Speed(6); Ability(ABILITY_CLEAR_BODY); } + } WHEN { + TURN { MOVE(player, MOVE_THUNDER_WAVE); } + TURN { MOVE(player, MOVE_THUNDER_WAVE); } + } SCENE { + MESSAGE("Foe Beldum used Celebrate!"); + MESSAGE("Wobbuffet used Thunder Wave!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_THUNDER_WAVE, player); + NOT ABILITY_POPUP(opponent, ABILITY_CLEAR_BODY); + MESSAGE("Wobbuffet used Thunder Wave!"); + ONE_OF { + MESSAGE("Foe Beldum used Celebrate!"); + MESSAGE("Foe Beldum is paralyzed! It can't move!"); + } + } +} + +SINGLE_BATTLE_TEST("Clear Body doesn't prevent Attack reduction from burn", s16 damage) +{ + bool32 burned; + PARAMETRIZE{ burned = FALSE; } + PARAMETRIZE{ burned = TRUE; } + GIVEN { + ASSUME(gMovesInfo[MOVE_TACKLE].category == DAMAGE_CATEGORY_PHYSICAL); + PLAYER(SPECIES_WOBBUFFET) + OPPONENT(SPECIES_BELDUM) { Ability(ABILITY_CLEAR_BODY); if (burned) Status1(STATUS1_BURN); } + } WHEN { + TURN { MOVE(opponent, MOVE_TACKLE); } + } SCENE { + NOT ABILITY_POPUP(opponent, ABILITY_CLEAR_BODY); + HP_BAR(player, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage); + } +} + +SINGLE_BATTLE_TEST("Clear Body doesn't prevent receiving negative stat changes from Baton Pass") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_SCARY_FACE].effect == EFFECT_SPEED_DOWN_2); + ASSUME(gMovesInfo[MOVE_BATON_PASS].effect == EFFECT_BATON_PASS); + PLAYER(SPECIES_WOBBUFFET) { Speed(4); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(3); } + OPPONENT(SPECIES_BELDUM) { Speed(6); Ability(ABILITY_CLEAR_BODY); } + } WHEN { + TURN { MOVE(player, MOVE_SCARY_FACE); MOVE(opponent, MOVE_BATON_PASS); SEND_OUT(opponent, 1); } + TURN { MOVE(player, MOVE_SCARY_FACE); } + } SCENE { + MESSAGE("Wobbuffet used Scary Face!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SCARY_FACE, player); + ABILITY_POPUP(opponent, ABILITY_CLEAR_BODY); + MESSAGE("Foe Beldum used Celebrate!"); + } +} + +SINGLE_BATTLE_TEST("Clear Body doesn't prevent Topsy-Turvy") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_TOPSY_TURVY].effect == EFFECT_TOPSY_TURVY); + ASSUME(gMovesInfo[MOVE_SCARY_FACE].effect == EFFECT_SPEED_DOWN_2); + ASSUME(gMovesInfo[MOVE_BATON_PASS].effect == EFFECT_BATON_PASS); + PLAYER(SPECIES_WOBBUFFET) { Speed(4); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(3); } + OPPONENT(SPECIES_BELDUM) { Speed(6); Ability(ABILITY_CLEAR_BODY); } + } WHEN { + TURN { MOVE(player, MOVE_SCARY_FACE); MOVE(opponent, MOVE_BATON_PASS); SEND_OUT(opponent, 1); } + TURN { MOVE(player, MOVE_TOPSY_TURVY); } + TURN { MOVE(player, MOVE_SCARY_FACE); } + } SCENE { + MESSAGE("Wobbuffet used Topsy-Turvy!"); + NOT ABILITY_POPUP(opponent, ABILITY_CLEAR_BODY); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TOPSY_TURVY, player); + MESSAGE("Foe Beldum used Celebrate!"); + MESSAGE("Foe Beldum used Celebrate!"); + MESSAGE("Wobbuffet used Scary Face!"); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_SCARY_FACE, player); + ABILITY_POPUP(opponent, ABILITY_CLEAR_BODY); + } +} + +SINGLE_BATTLE_TEST("Clear Body doesn't prevent Spectral Thief from resetting positive stat changes") +{ + GIVEN { + ASSUME(MoveHasAdditionalEffect(MOVE_SPECTRAL_THIEF, MOVE_EFFECT_SPECTRAL_THIEF) == TRUE); + ASSUME(gMovesInfo[MOVE_AGILITY].effect == EFFECT_SPEED_UP_2); + PLAYER(SPECIES_WOBBUFFET) { Speed(4); } + OPPONENT(SPECIES_METANG) { Speed(5); Ability(ABILITY_CLEAR_BODY); } + } WHEN { + TURN{ MOVE(opponent, MOVE_AGILITY); } + TURN{ MOVE(player, MOVE_SPECTRAL_THIEF); } + TURN{ } + } SCENE { + MESSAGE("Foe Metang used Agility!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_AGILITY, opponent); + MESSAGE("Wobbuffet used Celebrate!"); + MESSAGE("Foe Metang used Celebrate!"); + MESSAGE("Wobbuffet used Spectral Thief!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SPECTRAL_THIEF, player); + NOT ABILITY_POPUP(opponent, ABILITY_CLEAR_BODY); + MESSAGE("Wobbuffet used Celebrate!"); + MESSAGE("Foe Metang used Celebrate!"); + } +} diff --git a/test/battle/ai.c b/test/battle/ai.c index 2d804446d5..cd79236b81 100644 --- a/test/battle/ai.c +++ b/test/battle/ai.c @@ -691,3 +691,26 @@ AI_DOUBLE_BATTLE_TEST("AI will the see a corresponding absorbing ability on part TURN { EXPECT_MOVE(opponentLeft, MOVE_TACKLE); } } } + +AI_SINGLE_BATTLE_TEST("AI calculates guaranteed criticals and detects critical immunity") +{ + u32 ability; + PARAMETRIZE { ability = ABILITY_SWIFT_SWIM; } + PARAMETRIZE { ability = ABILITY_SHELL_ARMOR; } + + GIVEN { + ASSUME(gMovesInfo[MOVE_STORM_THROW].alwaysCriticalHit); + ASSUME(gMovesInfo[MOVE_STORM_THROW].power == 60); + ASSUME(gMovesInfo[MOVE_BRICK_BREAK].power == 75); + ASSUME(gMovesInfo[MOVE_STORM_THROW].type == gMovesInfo[MOVE_BRICK_BREAK].type); + ASSUME(gMovesInfo[MOVE_STORM_THROW].category == gMovesInfo[MOVE_BRICK_BREAK].category); + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT); + PLAYER(SPECIES_OMASTAR) { Ability(ability); } + OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_STORM_THROW, MOVE_BRICK_BREAK); } + } WHEN { + if (ability == ABILITY_SHELL_ARMOR) + TURN { EXPECT_MOVE(opponent, MOVE_BRICK_BREAK); } + else + TURN { EXPECT_MOVE(opponent, MOVE_STORM_THROW); } + } +} diff --git a/test/battle/hold_effect/ability_shield.c b/test/battle/hold_effect/ability_shield.c index c84397abfa..ee84b2c5e0 100644 --- a/test/battle/hold_effect/ability_shield.c +++ b/test/battle/hold_effect/ability_shield.c @@ -32,3 +32,75 @@ SINGLE_BATTLE_TEST("Ability Shield prevents Neutralizing Gas") } } } + +SINGLE_BATTLE_TEST("Ability Shield protects against Mold Breaker") +{ + u32 item; + + PARAMETRIZE { item = ITEM_ABILITY_SHIELD; } + PARAMETRIZE { item = ITEM_NONE; } + + GIVEN { + PLAYER(SPECIES_SHEDINJA) { Ability(ABILITY_WONDER_GUARD); Item(item); } + OPPONENT(SPECIES_TINKATON) { Ability(ABILITY_MOLD_BREAKER); } + } WHEN { + TURN { MOVE(opponent, MOVE_GIGATON_HAMMER); } + } SCENE { + if (item == ITEM_ABILITY_SHIELD) { + NONE_OF { + MESSAGE("Shedinja fainted!"); + } + } else { + MESSAGE("Shedinja fainted!"); + } + } +} + +SINGLE_BATTLE_TEST("Ability Shield protects against Mycelium Might") +{ + u32 item; + + PARAMETRIZE { item = ITEM_ABILITY_SHIELD; } + PARAMETRIZE { item = ITEM_NONE; } + + GIVEN { + PLAYER(SPECIES_VIGOROTH) { Ability(ABILITY_VITAL_SPIRIT); Item(item); } + OPPONENT(SPECIES_TOEDSCOOL) { Ability(ABILITY_MYCELIUM_MIGHT); } + } WHEN { + TURN { MOVE(opponent, MOVE_SPORE); MOVE(player, MOVE_SPORE); } + } SCENE { + + if (item == ITEM_ABILITY_SHIELD) { + NONE_OF { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SPORE, opponent); + STATUS_ICON(player, sleep: TRUE); + } + } else { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SPORE, opponent); + STATUS_ICON(player, sleep: TRUE); + } + } +} + +SINGLE_BATTLE_TEST("Ability Shield protects against Sunsteel Strike") +{ + u32 item; + + PARAMETRIZE { item = ITEM_ABILITY_SHIELD; } + PARAMETRIZE { item = ITEM_NONE; } + + GIVEN { + PLAYER(SPECIES_SHEDINJA) { Ability(ABILITY_WONDER_GUARD); Item(item); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_SUNSTEEL_STRIKE); } + } SCENE { + if (item == ITEM_ABILITY_SHIELD) { + NONE_OF { + MESSAGE("Shedinja fainted!"); + } + } else { + MESSAGE("Shedinja fainted!"); + } + } +} diff --git a/test/battle/hold_effect/clear_amulet.c b/test/battle/hold_effect/clear_amulet.c index 9500419540..f2059a0285 100644 --- a/test/battle/hold_effect/clear_amulet.c +++ b/test/battle/hold_effect/clear_amulet.c @@ -47,8 +47,7 @@ SINGLE_BATTLE_TEST("Clear Amulet prevents stat reducing effects") ASSUME(gMovesInfo[MOVE_CONFIDE].effect == EFFECT_SPECIAL_ATTACK_DOWN); ASSUME(gMovesInfo[MOVE_FAKE_TEARS].effect == EFFECT_SPECIAL_DEFENSE_DOWN_2); ASSUME(gMovesInfo[MOVE_SCARY_FACE].effect == EFFECT_SPEED_DOWN_2); - ASSUME(B_UPDATED_MOVE_DATA >= GEN_6); - ASSUME(gMovesInfo[MOVE_SWEET_SCENT].effect == EFFECT_EVASION_DOWN_2); + ASSUME(gMovesInfo[MOVE_SWEET_SCENT].effect == (B_UPDATED_MOVE_DATA >= GEN_6 ? EFFECT_EVASION_DOWN_2 : EFFECT_EVASION_DOWN)); ASSUME(gMovesInfo[MOVE_SAND_ATTACK].effect == EFFECT_ACCURACY_DOWN); PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_CLEAR_AMULET); }; diff --git a/test/battle/move_effect/smack_down.c b/test/battle/move_effect/smack_down.c index 54bf6885af..d68cb75ec3 100644 --- a/test/battle/move_effect/smack_down.c +++ b/test/battle/move_effect/smack_down.c @@ -3,7 +3,7 @@ ASSUMPTIONS { - ASSUME(gMovesInfo[MOVE_SMACK_DOWN].additionalEffects->moveEffect == MOVE_EFFECT_SMACK_DOWN); + ASSUME(MoveHasAdditionalEffect(MOVE_SMACK_DOWN, MOVE_EFFECT_SMACK_DOWN) == TRUE); } SINGLE_BATTLE_TEST("Smack Down does not ground mons behind substitutes") diff --git a/test/battle/move_effect/smelling_salts.c b/test/battle/move_effect/smelling_salts.c index 09168f8a1e..311024a153 100644 --- a/test/battle/move_effect/smelling_salts.c +++ b/test/battle/move_effect/smelling_salts.c @@ -3,7 +3,7 @@ ASSUMPTIONS { - ASSUME(gMovesInfo[MOVE_SMELLING_SALTS].additionalEffects->moveEffect == MOVE_EFFECT_REMOVE_STATUS); + ASSUME(MoveHasAdditionalEffect(MOVE_SMELLING_SALTS, MOVE_EFFECT_REMOVE_STATUS) == TRUE); ASSUME(gMovesInfo[MOVE_SMELLING_SALTS].argument == STATUS1_PARALYSIS); } diff --git a/test/battle/move_effect/sparkling_aria.c b/test/battle/move_effect/sparkling_aria.c index 1bf7de2df4..0cbfdbc3e9 100644 --- a/test/battle/move_effect/sparkling_aria.c +++ b/test/battle/move_effect/sparkling_aria.c @@ -3,7 +3,7 @@ ASSUMPTIONS { - ASSUME(gMovesInfo[MOVE_SPARKLING_ARIA].additionalEffects->moveEffect == MOVE_EFFECT_REMOVE_STATUS); + ASSUME(MoveHasAdditionalEffect(MOVE_SPARKLING_ARIA, MOVE_EFFECT_REMOVE_STATUS) == TRUE); ASSUME(gMovesInfo[MOVE_SPARKLING_ARIA].argument == STATUS1_BURN); ASSUME(gMovesInfo[MOVE_SPARKLING_ARIA].soundMove == TRUE); } diff --git a/test/battle/move_effect/tar_shot.c b/test/battle/move_effect/tar_shot.c new file mode 100644 index 0000000000..61aca1bec1 --- /dev/null +++ b/test/battle/move_effect/tar_shot.c @@ -0,0 +1,44 @@ +#include "global.h" +#include "test/battle.h" + +ASSUMPTIONS +{ + ASSUME(gMovesInfo[MOVE_TAR_SHOT].effect == EFFECT_TAR_SHOT); +} + +SINGLE_BATTLE_TEST("Tar Shot doubles the effectiveness of Fire-type moves used on the target") +{ + s16 damage[2]; + u32 species; + + PARAMETRIZE { species = SPECIES_WOBBUFFET; } + PARAMETRIZE { species = SPECIES_OMASTAR; } // Dual type with double resists + + ASSUME(gSpeciesInfo[SPECIES_WOBBUFFET].types[0] == TYPE_PSYCHIC); + ASSUME(gSpeciesInfo[SPECIES_WOBBUFFET].types[1] == TYPE_PSYCHIC); + ASSUME(gSpeciesInfo[SPECIES_OMASTAR].types[0] == TYPE_ROCK); + ASSUME(gSpeciesInfo[SPECIES_OMASTAR].types[1] == TYPE_WATER); + ASSUME(gMovesInfo[MOVE_EMBER].type == TYPE_FIRE); + + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(species); + } WHEN { + TURN { MOVE(player, MOVE_EMBER); } + TURN { MOVE(player, MOVE_TAR_SHOT); } + TURN { MOVE(player, MOVE_EMBER); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_EMBER, player); + HP_BAR(opponent, captureDamage: &damage[0]); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TAR_SHOT, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_EMBER, player); + HP_BAR(opponent, captureDamage: &damage[1]); + if (species != SPECIES_OMASTAR) + MESSAGE("It's super effective!"); + else + MESSAGE("It's not very effective…"); + } THEN { + EXPECT_MUL_EQ(damage[0], Q_4_12(2.0), damage[1]); + } +} + diff --git a/test/battle/move_effect/thousand_arrows.c b/test/battle/move_effect/thousand_arrows.c index 174085bc2a..8deea06fbe 100644 --- a/test/battle/move_effect/thousand_arrows.c +++ b/test/battle/move_effect/thousand_arrows.c @@ -3,7 +3,7 @@ ASSUMPTIONS { - ASSUME(gMovesInfo[MOVE_THOUSAND_ARROWS].additionalEffects->moveEffect == MOVE_EFFECT_SMACK_DOWN); + ASSUME(MoveHasAdditionalEffect(MOVE_THOUSAND_ARROWS, MOVE_EFFECT_SMACK_DOWN) == TRUE); ASSUME(gMovesInfo[MOVE_THOUSAND_ARROWS].ignoreTypeIfFlyingAndUngrounded == TRUE); } diff --git a/test/battle/move_effect/wake_up_slap.c b/test/battle/move_effect/wake_up_slap.c index 98bcf95b20..2c9dd7ed24 100644 --- a/test/battle/move_effect/wake_up_slap.c +++ b/test/battle/move_effect/wake_up_slap.c @@ -3,7 +3,7 @@ ASSUMPTIONS { - ASSUME(gMovesInfo[MOVE_WAKE_UP_SLAP].additionalEffects->moveEffect == MOVE_EFFECT_REMOVE_STATUS); + ASSUME(MoveHasAdditionalEffect(MOVE_WAKE_UP_SLAP, MOVE_EFFECT_REMOVE_STATUS) == TRUE); ASSUME(gMovesInfo[MOVE_WAKE_UP_SLAP].argument == STATUS1_SLEEP); }