diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index a00654d6c7..53ed13f9b6 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -8399,7 +8399,7 @@ BattleScript_BattlerAbilityStatRaiseOnSwitchIn:: BattleScript_ScriptingAbilityStatRaise:: copybyte gBattlerAbility, sBATTLER call BattleScript_AbilityPopUp - copybyte sSAVED_DMG, gBattlerAttacker + saveattacker copybyte gBattlerAttacker, sBATTLER statbuffchange MOVE_EFFECT_AFFECTS_USER | STAT_CHANGE_NOT_PROTECT_AFFECTED | MOVE_EFFECT_CERTAIN, NULL setgraphicalstatchangevalues @@ -8407,7 +8407,7 @@ BattleScript_ScriptingAbilityStatRaise:: waitanimation printstring STRINGID_ATTACKERABILITYSTATRAISE waitmessage B_WAIT_TIME_LONG - copybyte gBattlerAttacker, sSAVED_DMG + restoreattacker return BattleScript_WeakArmorActivates:: diff --git a/data/battle_scripts_2.s b/data/battle_scripts_2.s index 572d076fab..27e3706e3f 100644 --- a/data/battle_scripts_2.s +++ b/data/battle_scripts_2.s @@ -265,13 +265,12 @@ BattleScript_ActionWallyThrow: end2 BattleScript_TrainerASlideMsgRet:: - handletrainerslidemsg BS_SCRIPTING, 0 trainerslidein BS_OPPONENT1 - handletrainerslidemsg BS_SCRIPTING, 1 + handletrainerslidemsg BS_SCRIPTING, PRINT_SLIDE_MESSAGE waitstate trainerslideout BS_OPPONENT1 waitstate - handletrainerslidemsg BS_SCRIPTING, 2 + handletrainerslidemsg BS_SCRIPTING, RESTORE_BATTLER_SLIDE_CONTROL return BattleScript_TrainerASlideMsgEnd2:: @@ -279,13 +278,12 @@ BattleScript_TrainerASlideMsgEnd2:: end2 BattleScript_TrainerBSlideMsgRet:: - handletrainerslidemsg BS_SCRIPTING, 0 trainerslidein BS_OPPONENT2 - handletrainerslidemsg BS_SCRIPTING, 1 + handletrainerslidemsg BS_SCRIPTING, PRINT_SLIDE_MESSAGE waitstate trainerslideout BS_OPPONENT2 waitstate - handletrainerslidemsg BS_SCRIPTING, 2 + handletrainerslidemsg BS_SCRIPTING, RESTORE_BATTLER_SLIDE_CONTROL return BattleScript_TrainerBSlideMsgEnd2:: diff --git a/include/battle.h b/include/battle.h index 4620112194..8874857efb 100644 --- a/include/battle.h +++ b/include/battle.h @@ -736,19 +736,19 @@ struct BattleStruct u8 debugBattler; u8 magnitudeBasePower; u8 presentBasePower; - u8 roostTypes[MAX_BATTLERS_COUNT][2]; + u8 roostTypes[MAX_BATTLERS_COUNT][NUM_BATTLE_SIDES]; u8 savedBattlerTarget[5]; u8 savedBattlerAttacker[5]; u8 savedTargetCount:4; u8 savedAttackerCount:4; bool8 ateBoost[MAX_BATTLERS_COUNT]; - u8 abilityPopUpSpriteIds[MAX_BATTLERS_COUNT][2]; // two per battler + u8 abilityPopUpSpriteIds[MAX_BATTLERS_COUNT][NUM_BATTLE_SIDES]; // two per battler struct ZMoveData zmove; struct DynamaxData dynamax; struct BattleGimmickData gimmick; const u8 *trainerSlideMsg; enum BattleIntroStates introState:8; - u8 ateBerry[2]; // array id determined by side, each party pokemon as bit + u8 ateBerry[NUM_BATTLE_SIDES]; // array id determined by side, each party pokemon as bit u8 stolenStats[NUM_BATTLE_STATS]; // hp byte is used for which stats to raise, other inform about by how many stages u8 lastMoveTarget[MAX_BATTLERS_COUNT]; // The last target on which each mon used a move, for the sake of Instruct u16 tracedAbility[MAX_BATTLERS_COUNT]; @@ -804,13 +804,14 @@ struct BattleStruct u32 stellarBoostFlags[NUM_BATTLE_SIDES]; // stored as a bitfield of flags for all types for each side u8 monCausingSleepClause[NUM_BATTLE_SIDES]; // Stores which pokemon on a given side is causing Sleep Clause to be active as the mon's index in the party u8 additionalEffectsCounter:4; // A counter for the additionalEffects applied by the current move in Cmd_setadditionaleffects + s16 savedcheekPouchDamage; // Cheek Pouch can happen in the middle of an attack execution so we need to store the current dmg u8 cheekPouchActivated:1; u8 padding2:3; u8 pursuitStoredSwitch; // Stored id for the Pursuit target's switch s32 battlerExpReward; u16 prevTurnSpecies[MAX_BATTLERS_COUNT]; // Stores species the AI has in play at start of turn - s32 moveDamage[MAX_BATTLERS_COUNT]; - s32 critChance[MAX_BATTLERS_COUNT]; + s16 moveDamage[MAX_BATTLERS_COUNT]; + s16 critChance[MAX_BATTLERS_COUNT]; u16 moveResultFlags[MAX_BATTLERS_COUNT]; u8 missStringId[MAX_BATTLERS_COUNT]; u8 noResultString[MAX_BATTLERS_COUNT]; @@ -821,7 +822,6 @@ struct BattleStruct u8 numSpreadTargets:2; u8 bypassMoldBreakerChecks:1; // for ABILITYEFFECT_IMMUNITY u8 noTargetPresent:1; - u8 usedEjectItem; u8 usedMicleBerry; struct MessageStatus slideMessageStatus; u8 trainerSlideSpriteIds[MAX_BATTLERS_COUNT]; diff --git a/include/constants/battle.h b/include/constants/battle.h index 3bc59e8773..86226013d5 100644 --- a/include/constants/battle.h +++ b/include/constants/battle.h @@ -605,4 +605,10 @@ enum StartingStatus STARTING_STATUS_SWAMP_OPPONENT, }; +enum SlideMsgStates +{ + PRINT_SLIDE_MESSAGE, + RESTORE_BATTLER_SLIDE_CONTROL, +}; + #endif // GUARD_CONSTANTS_BATTLE_H diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 3704eb1b01..03680aa5fc 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -1677,6 +1677,7 @@ static void AccuracyCheck(bool32 recalcDragonDarts, const u8 *nextInstr, const u gBattleStruct->blunderPolicy = TRUE; // Only activates from missing through acc/evasion checks if (effect == EFFECT_DRAGON_DARTS + && !IsAffectedByFollowMe(gBattlerAttacker, GetBattlerSide(battlerDef), gCurrentMove) && !recalcDragonDarts // So we don't jump back and forth between targets && CanTargetPartner(gBattlerAttacker, battlerDef) && !TargetFullyImmuneToCurrMove(gBattlerAttacker, BATTLE_PARTNER(battlerDef))) @@ -6496,8 +6497,7 @@ static void Cmd_moveend(void) else if (moveRecoil > 0 && !(gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_NO_EFFECT) && IsBattlerAlive(gBattlerAttacker) - && IsBattlerTurnDamaged(gBattlerTarget) - && gBattleScripting.savedDmg != 0) // Some checks may be redundant alongside this one + && IsBattlerTurnDamaged(gBattlerTarget)) { gBattleStruct->moveDamage[gBattlerAttacker] = max(1, gBattleScripting.savedDmg * max(1, moveRecoil) / 100); BattleScriptPushCursor(); @@ -7006,6 +7006,7 @@ static void Cmd_moveend(void) else { if (moveEffect == EFFECT_DRAGON_DARTS + && !IsAffectedByFollowMe(gBattlerAttacker, GetBattlerSide(gBattlerTarget), gCurrentMove) && !(gBattleStruct->moveResultFlags[BATTLE_PARTNER(gBattlerTarget)] & MOVE_RESULT_MISSED) // didn't miss the other target && CanTargetPartner(gBattlerAttacker, gBattlerTarget) && !TargetFullyImmuneToCurrMove(gBattlerAttacker, BATTLE_PARTNER(gBattlerTarget))) @@ -7157,6 +7158,10 @@ static void Cmd_moveend(void) if (!(ejectPackBattlers & 1u << battler)) continue; + // Hit escape moves activate before Eject Pack for user + if (moveEffect == EFFECT_HIT_ESCAPE && gBattlerAttacker == battler) + continue; + gBattleScripting.battler = battler; gLastUsedItem = gBattleMons[battler].item; @@ -9048,7 +9053,7 @@ static bool32 TryCheekPouch(u32 battler, u32 itemId) && !IsBattlerAtMaxHp(battler)) { gBattleStruct->cheekPouchActivated = TRUE; - gBattleScripting.savedDmg = gBattleStruct->moveDamage[battler]; + gBattleStruct->savedcheekPouchDamage = gBattleStruct->moveDamage[battler]; gBattleStruct->moveDamage[battler] = GetNonDynamaxMaxHP(battler) / 3; if (gBattleStruct->moveDamage[battler] == 0) gBattleStruct->moveDamage[battler] = 1; @@ -9066,7 +9071,7 @@ static void TryRestoreDamageAfterCheeckPouch(u32 battler) { if (gBattleStruct->cheekPouchActivated) { - gBattleStruct->moveDamage[battler] = gBattleScripting.savedDmg; + gBattleStruct->moveDamage[battler] = gBattleStruct->savedcheekPouchDamage; gBattleStruct->cheekPouchActivated = FALSE; } } @@ -10602,6 +10607,7 @@ static void Cmd_various(void) if ((battlerAbility == ABILITY_MOXIE || battlerAbility == ABILITY_CHILLING_NEIGH || battlerAbility == ABILITY_AS_ONE_ICE_RIDER) + && IsBattlerAlive(battler) && HasAttackerFaintedTarget() && !NoAliveMonsForEitherParty() && CompareStat(gBattlerAttacker, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN)) @@ -10625,6 +10631,7 @@ static void Cmd_various(void) if ((battlerAbility == ABILITY_GRIM_NEIGH || battlerAbility == ABILITY_AS_ONE_SHADOW_RIDER) + && IsBattlerAlive(battler) && HasAttackerFaintedTarget() && !NoAliveMonsForEitherParty() && CompareStat(gBattlerAttacker, STAT_SPATK, MAX_STAT_STAGE, CMP_LESS_THAN)) @@ -10663,7 +10670,8 @@ static void Cmd_various(void) VARIOUS_ARGS(); i = GetHighestStatId(battler); - if (GetBattlerAbility(battler) == ABILITY_BEAST_BOOST + if (IsBattlerAlive(battler) + && GetBattlerAbility(battler) == ABILITY_BEAST_BOOST && HasAttackerFaintedTarget() && !NoAliveMonsForEitherParty() && CompareStat(gBattlerAttacker, i, MAX_STAT_STAGE, CMP_LESS_THAN)) @@ -10701,6 +10709,7 @@ static void Cmd_various(void) { VARIOUS_ARGS(); if (GetMoveEffect(gCurrentMove) == EFFECT_FELL_STINGER + && IsBattlerAlive(gBattlerAttacker) && HasAttackerFaintedTarget() && !NoAliveMonsForEitherParty() && CompareStat(gBattlerAttacker, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN)) @@ -11119,21 +11128,13 @@ static void Cmd_various(void) case VARIOUS_HANDLE_TRAINER_SLIDE_MSG: { VARIOUS_ARGS(u8 case_); - if (cmd->case_ == 0) - { - // Save sprite IDs, because trainer slide in will overwrite gBattlerSpriteIds variable. - gBattleScripting.savedDmg = (gBattlerSpriteIds[battler] & 0xFF) | (gBattlerSpriteIds[BATTLE_PARTNER(battler)] << 8); - } - else if (cmd->case_ == 1) + if (cmd->case_ == PRINT_SLIDE_MESSAGE) { BtlController_EmitPrintString(battler, BUFFER_A, STRINGID_TRAINERSLIDE); MarkBattlerForControllerExec(battler); } - else + else if (cmd->case_ == RESTORE_BATTLER_SLIDE_CONTROL) { - gBattlerSpriteIds[BATTLE_PARTNER(battler)] = gBattleScripting.savedDmg >> 8; - gBattlerSpriteIds[battler] = gBattleScripting.savedDmg & 0xFF; - gBattleScripting.savedDmg = 0; if (IsBattlerAlive(battler)) { SetBattlerShadowSpriteCallback(battler, gBattleMons[battler].species); diff --git a/src/battle_util.c b/src/battle_util.c index 72d203609e..9bf82b0670 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -227,15 +227,28 @@ static bool32 ShouldTeraShellDistortTypeMatchups(u32 move, u32 battlerDef, u32 a return FALSE; } +static inline bool32 IsDragonDartsSecondHit(u32 effect) +{ + if (effect != EFFECT_DRAGON_DARTS) + return FALSE; + + if (gMultiHitCounter == 1) + return TRUE; + + return FALSE; +} + bool32 IsAffectedByFollowMe(u32 battlerAtk, u32 defSide, u32 move) { u32 ability = GetBattlerAbility(battlerAtk); u32 effect = GetMoveEffect(move); if (gSideTimers[defSide].followmeTimer == 0 - || !IsBattlerAlive(gSideTimers[defSide].followmeTarget) - || effect == EFFECT_SNIPE_SHOT || effect == EFFECT_SKY_DROP - || ability == ABILITY_PROPELLER_TAIL || ability == ABILITY_STALWART) + || (!IsBattlerAlive(gSideTimers[defSide].followmeTarget) && !IsDragonDartsSecondHit(effect)) + || effect == EFFECT_SNIPE_SHOT + || effect == EFFECT_SKY_DROP + || ability == ABILITY_PROPELLER_TAIL + || ability == ABILITY_STALWART) return FALSE; if (effect == EFFECT_PURSUIT && IsPursuitTargetSet()) @@ -3582,6 +3595,9 @@ static void CancellerWeatherPrimal(u32 *effect) } if (*effect == 1) { + gBattleScripting.moveEffect = 0; + gProtectStructs[gBattlerAttacker].chargingTurn = FALSE; + CancelMultiTurnMoves(gBattlerAttacker); gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_PrimalWeatherBlocksMove; @@ -3696,6 +3712,7 @@ static void CancellerMultihitMoves(u32 *effect) gMultiHitCounter = GetMoveStrikeCount(gCurrentMove); if (GetMoveEffect(gCurrentMove) == EFFECT_DRAGON_DARTS + && !IsAffectedByFollowMe(gBattlerAttacker, GetBattlerSide(gBattlerTarget), gCurrentMove) && CanTargetPartner(gBattlerAttacker, gBattlerTarget) && TargetFullyImmuneToCurrMove(gBattlerAttacker, gBattlerTarget)) gBattlerTarget = BATTLE_PARTNER(gBattlerTarget); @@ -7355,6 +7372,7 @@ u32 RestoreWhiteHerbStats(u32 battler) } if (effect != 0) { + gLastUsedItem = gBattleMons[battler].item; gBattleScripting.battler = battler; gPotentialItemEffectBattler = battler; } @@ -7679,10 +7697,7 @@ u32 ItemBattleEffects(enum ItemCaseId caseID, u32 battler, bool32 moveTurn) case HOLD_EFFECT_RESTORE_STATS: effect = RestoreWhiteHerbStats(battler); if (effect != 0) - { - gBattlerAttacker = battler; BattleScriptExecute(BattleScript_WhiteHerbEnd2); - } break; case HOLD_EFFECT_CONFUSE_SPICY: if (B_BERRIES_INSTANT >= GEN_4) @@ -7898,10 +7913,7 @@ u32 ItemBattleEffects(enum ItemCaseId caseID, u32 battler, bool32 moveTurn) case HOLD_EFFECT_RESTORE_STATS: effect = RestoreWhiteHerbStats(battler); if (effect != 0) - { - gBattlerAttacker = battler; BattleScriptExecute(BattleScript_WhiteHerbEnd2); - } break; case HOLD_EFFECT_BLACK_SLUDGE: if (IS_BATTLER_OF_TYPE(battler, TYPE_POISON)) @@ -8190,7 +8202,8 @@ u32 ItemBattleEffects(enum ItemCaseId caseID, u32 battler, bool32 moveTurn) break; case HOLD_EFFECT_LIFE_ORB: if (IsBattlerAlive(gBattlerAttacker) - && (IsBattlerTurnDamaged(gBattlerTarget) || gBattleStruct->moveDamage[gBattlerTarget]) // Needs the second check in case of Substitute + && !IsBattleMoveStatus(gCurrentMove) + && (IsBattlerTurnDamaged(gBattlerTarget) || !(gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_NO_EFFECT)) // Needs the second check in case of Substitute && !(TestIfSheerForceAffected(gBattlerAttacker, gCurrentMove)) && GetBattlerAbility(gBattlerAttacker) != ABILITY_MAGIC_GUARD && !gProtectStructs[gBattlerAttacker].confusionSelfDmg @@ -8311,7 +8324,7 @@ u32 ItemBattleEffects(enum ItemCaseId caseID, u32 battler, bool32 moveTurn) effect = TrySetEnigmaBerry(battler); break; case HOLD_EFFECT_JABOCA_BERRY: // consume and damage attacker if used physical move - if (IsBattlerAlive(battler) + if (IsBattlerAlive(gBattlerAttacker) && IsBattlerTurnDamaged(gBattlerTarget) && !DoesSubstituteBlockMove(gBattlerAttacker, battler, gCurrentMove) && IsBattleMovePhysical(gCurrentMove) @@ -8331,7 +8344,7 @@ u32 ItemBattleEffects(enum ItemCaseId caseID, u32 battler, bool32 moveTurn) } break; case HOLD_EFFECT_ROWAP_BERRY: // consume and damage attacker if used special move - if (IsBattlerAlive(battler) + if (IsBattlerAlive(gBattlerAttacker) && IsBattlerTurnDamaged(gBattlerTarget) && !DoesSubstituteBlockMove(gBattlerAttacker, battler, gCurrentMove) && IsBattleMoveSpecial(gCurrentMove) diff --git a/src/data/pokemon/egg_moves.h b/src/data/pokemon/egg_moves.h index 23c233de58..392d2a0549 100644 --- a/src/data/pokemon/egg_moves.h +++ b/src/data/pokemon/egg_moves.h @@ -4278,6 +4278,13 @@ static const u16 sBasculinEggMoveLearnset[] = { MOVE_HEAD_SMASH, MOVE_UNAVAILABLE, }; +#if P_HISUIAN_FORMS +static const u16 sBasculinWhiteStripedEggMoveLearnset[] = { + MOVE_ENDEAVOR, + MOVE_LAST_RESPECTS, + MOVE_UNAVAILABLE, +}; +#endif //P_HISUIAN_FORMS #endif //P_FAMILY_BASCULIN #if P_FAMILY_SANDILE diff --git a/src/data/pokemon/species_info/gen_5_families.h b/src/data/pokemon/species_info/gen_5_families.h index 7662b7ced9..27b7a61d05 100644 --- a/src/data/pokemon/species_info/gen_5_families.h +++ b/src/data/pokemon/species_info/gen_5_families.h @@ -4377,6 +4377,7 @@ const struct SpeciesInfo gSpeciesInfoGen5[] = ) .levelUpLearnset = sBasculinWhiteStripedLevelUpLearnset, .teachableLearnset = sBasculinWhiteStripedTeachableLearnset, + .eggMoveLearnset = sBasculinWhiteStripedEggMoveLearnset, .formSpeciesIdTable = sBasculinFormSpeciesIdTable, .evolutions = EVOLUTION({EVO_RECOIL_DAMAGE_MALE, 294, SPECIES_BASCULEGION_M}, {EVO_RECOIL_DAMAGE_FEMALE, 294, SPECIES_BASCULEGION_F}), diff --git a/test/battle/ability/beast_boost.c b/test/battle/ability/beast_boost.c index 8ce0c55b9e..9b07c51f1d 100644 --- a/test/battle/ability/beast_boost.c +++ b/test/battle/ability/beast_boost.c @@ -36,3 +36,23 @@ SINGLE_BATTLE_TEST("Beast Boost boosts the most proficient stat when knocking ou } } } + +SINGLE_BATTLE_TEST("Beast Boost doesn't trigger if user is fainted") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_DESTINY_BOND) == EFFECT_DESTINY_BOND); + ASSUME(GetMovePower(MOVE_SCRATCH) > 0); + PLAYER(SPECIES_WOBBUFFET) { HP(1); } + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_KARTANA) { Ability(ABILITY_BEAST_BOOST); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_DESTINY_BOND); MOVE(opponent, MOVE_SCRATCH); SEND_OUT(player, 1); SEND_OUT(opponent, 1); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_DESTINY_BOND, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, opponent); + NOT ABILITY_POPUP(opponent, ABILITY_BEAST_BOOST); + SEND_IN_MESSAGE("Wynaut"); + MESSAGE("2 sent out Wobbuffet!"); + } +} diff --git a/test/battle/ability/desolate_land.c b/test/battle/ability/desolate_land.c index 5bf1358503..e058b7b514 100644 --- a/test/battle/ability/desolate_land.c +++ b/test/battle/ability/desolate_land.c @@ -65,3 +65,21 @@ SINGLE_BATTLE_TEST("Desolate Land does not block a move if pokemon is asleep and MESSAGE("The opposing Wobbuffet is fast asleep."); } } + +SINGLE_BATTLE_TEST("Desolate Land will not create a softlock when move in semi invulnerable position is blocked") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_GROUDON) { Item(ITEM_RED_ORB); } + } WHEN { + TURN { MOVE(player, MOVE_DIVE); } + TURN { SWITCH(opponent, 1); SKIP_TURN(player); } + TURN { MOVE(player, MOVE_CELEBRATE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_DIVE, player); + ABILITY_POPUP(opponent, ABILITY_DESOLATE_LAND); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_DIVE, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, player); + } +} diff --git a/test/battle/hold_effect/jaboca_berry.c b/test/battle/hold_effect/jaboca_berry.c index 2eb10df77a..cdb5cbfacd 100644 --- a/test/battle/hold_effect/jaboca_berry.c +++ b/test/battle/hold_effect/jaboca_berry.c @@ -58,3 +58,18 @@ SINGLE_BATTLE_TEST("Jaboca Berry tirggers before Bug Bite can steal it") NOT MESSAGE("Wynaut stole and ate the opposing Wobbuffet's Jaboca Berry!"); } } + +SINGLE_BATTLE_TEST("Jaboca Berry is triggered even if berry user dies") +{ + GIVEN { + ASSUME(GetMoveCategory(MOVE_SWIFT) == DAMAGE_CATEGORY_SPECIAL); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { HP(1); Item(ITEM_JABOCA_BERRY); } + } WHEN { + TURN { MOVE(player, MOVE_TACKLE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player); + HP_BAR(player); + MESSAGE("Wobbuffet was hurt by the opposing Wobbuffet's Jaboca Berry!"); + } +} diff --git a/test/battle/hold_effect/life_orb.c b/test/battle/hold_effect/life_orb.c index e69de29bb2..f27ce29d03 100644 --- a/test/battle/hold_effect/life_orb.c +++ b/test/battle/hold_effect/life_orb.c @@ -0,0 +1,33 @@ +#include "global.h" +#include "test/battle.h" + +SINGLE_BATTLE_TEST("Life Orb activates if it hits a Substitute") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_LIFE_ORB); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_SUBSTITUTE); MOVE(player, MOVE_TACKLE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SUBSTITUTE, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player); + HP_BAR(player); + MESSAGE("Wobbuffet was hurt by the Life Orb!"); + } +} + +SINGLE_BATTLE_TEST("Life Orb does not activate if using a status move") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_LIFE_ORB); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_GROWL); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_GROWL, player); + NONE_OF { + HP_BAR(player); + MESSAGE("Wobbuffet was hurt by the Life Orb!"); + } + } +} diff --git a/test/battle/hold_effect/restore_stats.c b/test/battle/hold_effect/restore_stats.c index e7f020910f..f0f7bc5908 100644 --- a/test/battle/hold_effect/restore_stats.c +++ b/test/battle/hold_effect/restore_stats.c @@ -217,3 +217,21 @@ SINGLE_BATTLE_TEST("White Herb has correct interactions with Intimidate triggere } } } + +DOUBLE_BATTLE_TEST("White Herb is correctly displayed") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WYNAUT) { Item(ITEM_WHITE_HERB); } + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { MOVE(playerRight, MOVE_SUPERPOWER, target: opponentRight); } + } SCENE { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerRight); + MESSAGE("Wynaut returned its stats to normal using its White Herb!"); + } THEN { + EXPECT(playerLeft->item == ITEM_NONE); + EXPECT(playerLeft->statStages[STAT_DEF] = DEFAULT_STAT_STAGE); + } +} diff --git a/test/battle/hold_effect/rowap_berry.c b/test/battle/hold_effect/rowap_berry.c index 0604fefcb7..93ac49f608 100644 --- a/test/battle/hold_effect/rowap_berry.c +++ b/test/battle/hold_effect/rowap_berry.c @@ -57,3 +57,18 @@ SINGLE_BATTLE_TEST("Rowap Berry is not triggered by a physical move") } } } + +SINGLE_BATTLE_TEST("Rowap Berry is triggered even if berry user dies") +{ + GIVEN { + ASSUME(GetMoveCategory(MOVE_TACKLE) == DAMAGE_CATEGORY_PHYSICAL); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { HP(1); Item(ITEM_ROWAP_BERRY); } + } WHEN { + TURN { MOVE(player, MOVE_SWIFT); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SWIFT, player); + HP_BAR(player); + MESSAGE("Wobbuffet was hurt by the opposing Wobbuffet's Rowap Berry!"); + } +} diff --git a/test/battle/move_effect/dragon_darts.c b/test/battle/move_effect/dragon_darts.c index 9e178f67c8..1d94968a09 100644 --- a/test/battle/move_effect/dragon_darts.c +++ b/test/battle/move_effect/dragon_darts.c @@ -76,6 +76,7 @@ DOUBLE_BATTLE_TEST("Dragon Darts strikes an opponent twice if the other one is F PARAMETRIZE { chosenTarget = opponentRight; finalTarget = opponentRight; speciesLeft = SPECIES_CLEFAIRY; speciesRight = SPECIES_WOBBUFFET; } PARAMETRIZE { chosenTarget = opponentLeft; finalTarget = opponentLeft; speciesLeft = SPECIES_WOBBUFFET; speciesRight = SPECIES_CLEFAIRY; } PARAMETRIZE { chosenTarget = opponentRight; finalTarget = opponentLeft; speciesLeft = SPECIES_WOBBUFFET; speciesRight = SPECIES_CLEFAIRY; } + GIVEN { ASSUME(gSpeciesInfo[SPECIES_CLEFAIRY].types[0] == TYPE_FAIRY || gSpeciesInfo[SPECIES_CLEFAIRY].types[1] == TYPE_FAIRY); PLAYER(SPECIES_WOBBUFFET); @@ -252,3 +253,61 @@ DOUBLE_BATTLE_TEST("Dragon Darts strikes right ally twice if one strike misses") MESSAGE("The Pokémon was hit 2 time(s)!"); } } + +DOUBLE_BATTLE_TEST("Dragon Darts strikes will be both redirected to Follow Me user") +{ + GIVEN { + ASSUME(gSpeciesInfo[SPECIES_CLEFAIRY].types[0] == TYPE_FAIRY || gSpeciesInfo[SPECIES_CLEFAIRY].types[1] == TYPE_FAIRY); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponentRight, MOVE_FOLLOW_ME); MOVE(playerLeft, MOVE_DRAGON_DARTS, target: opponentLeft); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_DARTS, playerLeft); + HP_BAR(opponentRight); + ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_DARTS, playerLeft); + HP_BAR(opponentRight); + MESSAGE("The Pokémon was hit 2 time(s)!"); + } +} + +DOUBLE_BATTLE_TEST("Dragon Darts fails to strike any target if under a fairy type follow me user") +{ + GIVEN { + ASSUME(gSpeciesInfo[SPECIES_CLEFAIRY].types[0] == TYPE_FAIRY || gSpeciesInfo[SPECIES_CLEFAIRY].types[1] == TYPE_FAIRY); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_CLEFAIRY); + } WHEN { + TURN { MOVE(opponentRight, MOVE_FOLLOW_ME); MOVE(playerLeft, MOVE_DRAGON_DARTS, target: opponentLeft); } + } SCENE { + NONE_OF { + ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_DARTS, playerLeft); + ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_DARTS, playerLeft); + MESSAGE("The Pokémon was hit 2 time(s)!"); + } + } +} + +DOUBLE_BATTLE_TEST("Dragon Darts fails to strike the second target if first target fainted and follow me was active") +{ + GIVEN { + ASSUME(gSpeciesInfo[SPECIES_CLEFAIRY].types[0] == TYPE_FAIRY || gSpeciesInfo[SPECIES_CLEFAIRY].types[1] == TYPE_FAIRY); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { HP(1); } + } WHEN { + TURN { MOVE(opponentRight, MOVE_FOLLOW_ME); MOVE(playerLeft, MOVE_DRAGON_DARTS, target: opponentLeft); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_DARTS, playerLeft); + HP_BAR(opponentRight); + NONE_OF { + ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_DARTS, playerLeft); + MESSAGE("The Pokémon was hit 2 time(s)!"); + } + } +} diff --git a/test/battle/move_effect/hit_escape.c b/test/battle/move_effect/hit_escape.c index 83280f9f50..d0ef3d40f3 100644 --- a/test/battle/move_effect/hit_escape.c +++ b/test/battle/move_effect/hit_escape.c @@ -179,3 +179,19 @@ SINGLE_BATTLE_TEST("Hit Escape: Electric Seed boost is received by the right pok EXPECT_EQ(player->statStages[STAT_DEF], DEFAULT_STAT_STAGE + 1); } } + +SINGLE_BATTLE_TEST("Hit Escape: U-turn triggers before Eject Pack") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_EJECT_PACK); }; + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_GOODRA_HISUI) { Ability(ABILITY_GOOEY); }; + } WHEN { + TURN { MOVE(player, MOVE_U_TURN); SEND_OUT(player, 1); } + } SCENE { + NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_U_TURN, player); + HP_BAR(opponent); + SEND_IN_MESSAGE("Wynaut"); + } +} diff --git a/tools/mapjson/json11.cpp b/tools/mapjson/json11.cpp index 3ac5f392a0..fcacdbeb39 100644 --- a/tools/mapjson/json11.cpp +++ b/tools/mapjson/json11.cpp @@ -23,6 +23,7 @@ #include #include #include +#include namespace json11 {