diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc index d30d084a66..4f831b427a 100644 --- a/asm/macros/battle_script.inc +++ b/asm/macros/battle_script.inc @@ -1007,7 +1007,7 @@ .4byte \jumpInstr .endm - .macro unused_bb + .macro tryhealingitem .byte 0xbb .endm @@ -1140,9 +1140,8 @@ .4byte \failInstr .endm - .macro trywish turnNumber:req, failInstr:req + .macro trywish failInstr:req .byte 0xd4 - .byte \turnNumber .4byte \failInstr .endm diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index f5f1d948a2..20cdc51cef 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -414,12 +414,12 @@ BattleScript_MoveEffectSaltCure:: return BattleScript_SaltCureExtraDamage:: - playanimation BS_TARGET, B_ANIM_SALT_CURE_DAMAGE, NULL + playanimation BS_ATTACKER, B_ANIM_SALT_CURE_DAMAGE, NULL waitanimation call BattleScript_HurtTarget_NoString printstring STRINGID_TARGETISHURTBYSALTCURE waitmessage B_WAIT_TIME_LONG - tryfaintmon BS_TARGET + tryfaintmon BS_ATTACKER end2 BattleScript_HurtTarget_NoString: @@ -4977,7 +4977,7 @@ BattleScript_EffectWish:: attackcanceler attackstring ppreduce - trywish 0, BattleScript_ButItFailed + trywish BattleScript_ButItFailed attackanimation waitanimation goto BattleScript_MoveEnd @@ -5770,13 +5770,13 @@ BattleScript_FogEnded_Ret:: return BattleScript_IceBodyHeal:: - call BattleScript_AbilityPopUpScripting - playanimation BS_SCRIPTING, B_ANIM_SIMPLE_HEAL - healthbarupdate BS_SCRIPTING - datahpupdate BS_SCRIPTING + call BattleScript_AbilityPopUp + playanimation BS_ATTACKER, B_ANIM_SIMPLE_HEAL + healthbarupdate BS_ATTACKER + datahpupdate BS_ATTACKER printstring STRINGID_ICEBODYHPGAIN waitmessage B_WAIT_TIME_LONG - end2 + end3 BattleScript_OverworldStatusStarts:: printfromtable gStartingStatusStringIds @@ -5837,12 +5837,14 @@ BattleScript_WonderRoomEnds:: BattleScript_MagicRoomEnds:: printstring STRINGID_MAGICROOMENDS waitmessage B_WAIT_TIME_LONG + setbyte gBattlerTarget, 0 +BattleScript_MagicRoomHealingItemsLoop: + copyarraywithindex gBattlerAttacker, gBattlerByTurnOrder, gBattlerTarget, 1 + tryhealingitem + addbyte gBattlerTarget, 1 + jumpifbytenotequal gBattlerTarget, gBattlersCount, BattleScript_MagicRoomHealingItemsLoop end2 -BattleScript_GrassyTerrainEnds:: - call BattleScript_GrassyTerrainHeals_Ret - goto BattleScript_TerrainEnds - BattleScript_TerrainEnds_Ret:: printfromtable gTerrainStringIds waitmessage B_WAIT_TIME_LONG @@ -6608,7 +6610,6 @@ BattleScript_SelectingNotAllowedCurrentMoveInPalace:: goto BattleScript_SelectingUnusableMoveInPalace BattleScript_WishComesTrue:: - trywish 1, BattleScript_WishButFullHp playanimation BS_TARGET, B_ANIM_WISH_HEAL printstring STRINGID_PKMNWISHCAMETRUE waitmessage B_WAIT_TIME_LONG @@ -6927,7 +6928,7 @@ BattleScript_CudChewActivates:: pause B_WAIT_TIME_SHORTEST call BattleScript_AbilityPopUp setbyte sBERRY_OVERRIDE, 1 @ override the requirements for eating berries - consumeberry BS_SCRIPTING, FALSE + consumeberry BS_ATTACKER, FALSE setbyte sBERRY_OVERRIDE, 0 end3 @@ -7079,6 +7080,7 @@ BattleScript_DoTurnDmg: datahpupdate BS_ATTACKER tryfaintmon BS_ATTACKER checkteamslost BattleScript_DoTurnDmgEnd + tryhealingitem BattleScript_DoTurnDmgEnd: end2 @@ -7326,6 +7328,7 @@ BattleScript_YawnEnd: BattleScript_EmbargoEndTurn:: printstring STRINGID_EMBARGOENDS waitmessage B_WAIT_TIME_LONG + tryhealingitem end2 BattleScript_TelekinesisEndTurn:: @@ -7544,6 +7547,37 @@ BattleScript_EmergencyExitWild:: finishaction return +BattleScript_EmergencyExitEnd2:: + pause 5 + call BattleScript_AbilityPopUp + pause B_WAIT_TIME_LONG + playanimation BS_ATTACKER, B_ANIM_SLIDE_OFFSCREEN + waitanimation + openpartyscreen BS_ATTACKER, BattleScript_EmergencyExitRetEnd2 + switchoutabilities BS_ATTACKER + waitstate + switchhandleorder BS_ATTACKER, 2 + returntoball BS_TARGET, FALSE + getswitchedmondata BS_ATTACKER + switchindataupdate BS_ATTACKER + hpthresholds BS_ATTACKER + printstring STRINGID_SWITCHINMON + switchinanim BS_ATTACKER, FALSE, TRUE + waitstate + switchineffects BS_ATTACKER +BattleScript_EmergencyExitRetEnd2: + end2 + +BattleScript_EmergencyExitWildEnd2:: + pause 5 + call BattleScript_AbilityPopUp + pause B_WAIT_TIME_LONG + playanimation BS_ATTACKER, B_ANIM_SLIDE_OFFSCREEN + waitanimation + setoutcomeonteleport BS_ATTACKER + finishaction + end2 + BattleScript_TraceActivates:: pause B_WAIT_TIME_SHORT call BattleScript_AbilityPopUpScripting @@ -7586,6 +7620,7 @@ BattleScript_PickupActivates:: call BattleScript_AbilityPopUp printstring STRINGID_XFOUNDONEY waitmessage B_WAIT_TIME_LONG + tryhealingitem BattleScript_PickupActivatesEnd: end3 @@ -7595,6 +7630,7 @@ BattleScript_HarvestActivates:: call BattleScript_AbilityPopUp printstring STRINGID_HARVESTBERRY waitmessage B_WAIT_TIME_LONG + tryhealingitem BattleScript_HarvestActivatesEnd: end3 @@ -8229,25 +8265,13 @@ BattleScript_MoveUsedPsychicTerrainPrevents:: goto BattleScript_MoveEnd BattleScript_GrassyTerrainHeals:: - call BattleScript_GrassyTerrainHeals_Ret - end2 - -BattleScript_GrassyTerrainHeals_Ret:: - setbyte gBattleCommunication, 0 -BattleScript_GrassyTerrainLoop: - copyarraywithindex gBattlerAttacker, gBattlerByTurnOrder, gBattleCommunication, 1 - checkgrassyterrainheal BS_ATTACKER, BattleScript_GrassyTerrainLoopIncrement printstring STRINGID_GRASSYTERRAINHEALS waitmessage B_WAIT_TIME_LONG orword gHitMarker, HITMARKER_IGNORE_BIDE | HITMARKER_IGNORE_SUBSTITUTE | HITMARKER_PASSIVE_DAMAGE healthbarupdate BS_ATTACKER datahpupdate BS_ATTACKER -BattleScript_GrassyTerrainLoopIncrement:: - addbyte gBattleCommunication, 1 - jumpifbytenotequal gBattleCommunication, gBattlersCount, BattleScript_GrassyTerrainLoop bicword gHitMarker, HITMARKER_IGNORE_BIDE | HITMARKER_IGNORE_SUBSTITUTE | HITMARKER_PASSIVE_DAMAGE -BattleScript_GrassyTerrainHealEnd: - return + end2 BattleScript_AbilityNoSpecificStatLoss:: pause B_WAIT_TIME_SHORT diff --git a/include/battle.h b/include/battle.h index 1996ee0757..c6596b3c84 100644 --- a/include/battle.h +++ b/include/battle.h @@ -85,12 +85,12 @@ struct DisableStruct s8 stockpileBeforeSpDef; u8 substituteHP; u8 encoredMovePos; - u8 disableTimer:4; - u8 encoreTimer:4; - u8 perishSongTimer:4; - u8 rolloutTimer:4; - u8 rolloutTimerStartValue:4; - u8 tauntTimer:4; + u16 disableTimer; + u16 encoreTimer; + u16 perishSongTimer; + u16 rolloutTimer; + u16 rolloutTimerStartValue; + u16 tauntTimer; u8 furyCutterCounter; u8 battlerPreventingEscape; u8 battlerWithSureHit; @@ -99,16 +99,16 @@ struct DisableStruct u8 chargeTimer:4; u8 rechargeTimer; u8 autotomizeCount; - u8 slowStartTimer; - u8 embargoTimer; - u8 magnetRiseTimer; - u8 telekinesisTimer; - u8 healBlockTimer; - u8 laserFocusTimer; - u8 throatChopTimer; + u16 slowStartTimer; + u16 embargoTimer; + u16 magnetRiseTimer; + u16 telekinesisTimer; + u16 healBlockTimer; + u16 laserFocusTimer; + u16 throatChopTimer; u8 wrapTurns; - u8 syrupBombTimer; - u8 tormentTimer:4; // used for G-Max Meltdown + u16 syrupBombTimer; + u16 tormentTimer; // used for G-Max Meltdown u8 usedMoves:4; u8 truantCounter:1; u8 truantSwitchInHack:1; @@ -270,7 +270,7 @@ struct FieldTimer struct WishFutureKnock { - u8 futureSightCounter[MAX_BATTLERS_COUNT]; + u16 futureSightCounter[MAX_BATTLERS_COUNT]; u8 futureSightBattlerIndex[MAX_BATTLERS_COUNT]; u8 futureSightPartyIndex[MAX_BATTLERS_COUNT]; u16 futureSightMove[MAX_BATTLERS_COUNT]; @@ -557,7 +557,7 @@ struct ZMoveData struct DynamaxData { - u8 dynamaxTurns[MAX_BATTLERS_COUNT]; + u16 dynamaxTurns[MAX_BATTLERS_COUNT]; u16 baseMoves[MAX_BATTLERS_COUNT]; // base move of Max Move u16 lastUsedBaseMove; }; @@ -634,9 +634,9 @@ struct BattlerState struct BattleStruct { struct BattlerState battlerState[MAX_BATTLERS_COUNT]; - u8 turnEffectsTracker; + u8 eventBlockCounter; u8 turnEffectsBattlerId; - u8 turnCountersTracker; + u8 endTurnEventsCounter; u16 wrappedMove[MAX_BATTLERS_COUNT]; u16 moveTarget[MAX_BATTLERS_COUNT]; u32 expShareExpValue; @@ -707,8 +707,6 @@ struct BattleStruct struct LinkBattlerHeader linkBattlerHeader; struct BattleVideo battleVideo; } multiBuffer; - u8 wishPerishSongState; - u8 wishPerishSongBattlerId; u8 startingStatus:6; // status to apply at battle start. defined in constants/battle.h u8 startingStatusDone:1; u8 terrainDone:1; @@ -718,7 +716,7 @@ struct BattleStruct u8 friskedAbility:1; // If identifies two mons, show the ability pop-up only once. u8 fickleBeamBoosted:1; u8 poisonPuppeteerConfusion:1; - u8 startingStatusTimer; + u16 startingStatusTimer; u8 atkCancellerTracker; struct BattleTvMovePoints tvMovePoints; struct BattleTv tv; diff --git a/include/battle_end_turn.h b/include/battle_end_turn.h new file mode 100644 index 0000000000..468544cf2b --- /dev/null +++ b/include/battle_end_turn.h @@ -0,0 +1,7 @@ +#ifndef GUARD_BATTLE_END_TURN +#define GUARD_BATTLE_END_TURN + +u32 DoEndTurnEffects(void); + +#endif // GUARD_BATTLE_END_TURN + diff --git a/include/battle_scripts.h b/include/battle_scripts.h index d67fa47be4..770f21b7a9 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -101,6 +101,7 @@ extern const u8 BattleScript_SelectingTormentedMoveInPalace[]; extern const u8 BattleScript_SelectingNotAllowedMoveTaunt[]; extern const u8 BattleScript_MoveUsedIsTaunted[]; extern const u8 BattleScript_SelectingNotAllowedMoveTauntInPalace[]; +extern const u8 BattleScript_WishButFullHp[]; extern const u8 BattleScript_WishComesTrue[]; extern const u8 BattleScript_IngrainTurnHeal[]; extern const u8 BattleScript_AtkDefDown[]; @@ -269,7 +270,6 @@ extern const u8 BattleScript_WonderRoomEnds[]; extern const u8 BattleScript_MagicRoomEnds[]; extern const u8 BattleScript_TerrainEnds[]; extern const u8 BattleScript_TerrainEnds_Ret[]; -extern const u8 BattleScript_GrassyTerrainEnds[]; extern const u8 BattleScript_MudSportEnds[]; extern const u8 BattleScript_WaterSportEnds[]; extern const u8 BattleScript_SturdiedMsg[]; @@ -385,6 +385,8 @@ extern const u8 BattleScript_FriskMsgWithPopup[]; extern const u8 BattleScript_MoodyActivates[]; extern const u8 BattleScript_EmergencyExit[]; extern const u8 BattleScript_EmergencyExitWild[]; +extern const u8 BattleScript_EmergencyExitEnd2[]; +extern const u8 BattleScript_EmergencyExitWildEnd2[]; extern const u8 BattleScript_CheekPouchActivates[]; extern const u8 BattleScript_TotemVar[]; extern const u8 BattleScript_TotemFlaredToLife[]; diff --git a/include/battle_util.h b/include/battle_util.h index b67a36b591..9748beee4e 100644 --- a/include/battle_util.h +++ b/include/battle_util.h @@ -68,6 +68,7 @@ enum ItemCaseId ITEMEFFECT_ON_SWITCH_IN, ITEMEFFECT_ON_SWITCH_IN_FIRST_TURN, ITEMEFFECT_NORMAL, + ITEMEFFECT_TRY_HEALING, ITEMEFFECT_MOVE_END, ITEMEFFECT_KINGSROCK, ITEMEFFECT_TARGET, @@ -172,6 +173,8 @@ enum SleepClauseBlock }; void HandleAction_ThrowBall(void); +u32 GetCurrentBattleWeather(void); +bool32 EndOrContinueWeather(void); bool32 IsAffectedByFollowMe(u32 battlerAtk, u32 defSide, u32 move); bool32 HandleMoveTargetRedirection(void); void HandleAction_UseMove(void); @@ -205,11 +208,9 @@ u32 TrySetCantSelectMoveBattleScript(u32 battler); u8 CheckMoveLimitations(u32 battler, u8 unusableMoves, u16 check); bool32 AreAllMovesUnusable(u32 battler); u8 GetImprisonedMovesCount(u32 battler, u16 move); -u8 DoFieldEndTurnEffects(void); s32 GetDrainedBigRootHp(u32 battler, s32 hp); bool32 IsMagicGuardProtected(u32 battler, u32 ability); -u8 DoBattlerEndTurnEffects(void); -bool32 HandleWishPerishSongOnTurnEnd(void); +u32 DoEndTurnEffects(void); bool32 HandleFaintedMonActions(void); void TryClearRageAndFuryCutter(void); u32 AtkCanceller_MoveSuccessOrder(void); @@ -327,6 +328,7 @@ bool32 CanBeConfused(u32 battler); bool32 IsBattlerTerrainAffected(u32 battler, u32 terrainFlag); u32 GetBattlerAffectionHearts(u32 battler); void TryToRevertMimicryAndFlags(void); +bool32 BattleArenaTurnEnd(void); u32 CountBattlerStatIncreases(u32 battler, bool32 countEvasionAcc); bool32 ChangeTypeBasedOnTerrain(u32 battler); void RemoveConfusionStatus(u32 battler); diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index 8ec95411f5..1c6698820e 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -1040,7 +1040,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) // the following checks apply to any target (including user) // throat chop check - if (gDisableStructs[battlerAtk].throatChopTimer && IsSoundMove(move)) + if (gDisableStructs[battlerAtk].throatChopTimer > gBattleTurnCounter && IsSoundMove(move)) return 0; // Can't even select move at all // heal block check if (gStatuses3[battlerAtk] & STATUS3_HEAL_BLOCK && IsHealBlockPreventingMove(battlerAtk, move)) @@ -2447,7 +2447,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) break; case EFFECT_EMBARGO: if (!IsBattlerItemEnabled(battlerAtk) - || gDisableStructs[battlerDef].embargoTimer != 0 + || gStatuses3[battlerDef] & STATUS3_EMBARGO || PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove)) ADJUST_SCORE(-10); break; @@ -2465,7 +2465,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) ADJUST_SCORE(-10); break; case EFFECT_HEAL_BLOCK: - if (gDisableStructs[battlerDef].healBlockTimer != 0 + if (gStatuses3[battlerDef] & STATUS3_HEAL_BLOCK || PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove)) ADJUST_SCORE(-10); break; diff --git a/src/battle_dynamax.c b/src/battle_dynamax.c index fecadd887d..007b5b20ee 100644 --- a/src/battle_dynamax.c +++ b/src/battle_dynamax.c @@ -178,7 +178,7 @@ void ActivateDynamax(u32 battler) // Set appropriate use flags. SetActiveGimmick(battler, GIMMICK_DYNAMAX); SetGimmickAsActivated(battler, GIMMICK_DYNAMAX); - gBattleStruct->dynamax.dynamaxTurns[battler] = DYNAMAX_TURNS_COUNT; + gBattleStruct->dynamax.dynamaxTurns[battler] = gBattleTurnCounter + DYNAMAX_TURNS_COUNT; // Substitute is removed upon Dynamaxing. gBattleMons[battler].status2 &= ~STATUS2_SUBSTITUTE; @@ -209,7 +209,6 @@ void UndoDynamax(u32 battler) // Makes sure there are no Dynamax flags set, including on switch / faint. SetActiveGimmick(battler, GIMMICK_NONE); - gBattleStruct->dynamax.dynamaxTurns[battler] = 0; // Undo form change if needed. if (IsGigantamaxed(battler)) diff --git a/src/battle_end_turn.c b/src/battle_end_turn.c new file mode 100644 index 0000000000..531c5a778f --- /dev/null +++ b/src/battle_end_turn.c @@ -0,0 +1,1596 @@ +#include "global.h" +#include "battle.h" +#include "battle_util.h" +#include "battle_controllers.h" +#include "battle_ai_util.h" +#include "battle_gimmick.h" +#include "battle_scripts.h" +#include "constants/battle.h" +#include "constants/battle_string_ids.h" +#include "constants/hold_effects.h" +#include "constants/abilities.h" +#include "constants/items.h" +#include "constants/moves.h" + +// General End Turn Effects based on research from smogon from vanilla games: +// https://www.smogon.com/forums/threads/sword-shield-battle-mechanics-research.3655528/page-64#post-9244179 +enum EndTurnResolutionOrder +{ + ENDTURN_ORDER, + ENDTURN_VARIOUS, + ENDTURN_WEATHER, + ENDTURN_WEATHER_DAMAGE, + ENDTURN_EMERGENCY_EXIT_1, + ENDTURN_AFFECTION, + ENDTURN_FUTURE_SIGHT, + ENDTURN_WISH, + ENDTURN_FIRST_EVENT_BLOCK, + ENDTURN_EMERGENCY_EXIT_2, + ENDTURN_AQUA_RING, + ENDTURN_INGRAIN, + ENDTURN_LEECH_SEED, + ENDTURN_POISON, + ENDTURN_BURN, + ENDTURN_FROSTBITE, + ENDTURN_NIGHTMARE, + ENDTURN_CURSE, + ENDTURN_WRAP, + ENDTURN_SALT_CURE, + ENDTURN_OCTOLOCK, + ENDTURN_SYRUP_BOMB, + ENDTURN_TAUNT, + ENDTURN_TORMENT, + ENDTURN_ENCORE, + ENDTURN_DISABLE, + ENDTURN_MAGNET_RISE, + ENDTURN_TELEKINESIS, + ENDTURN_HEAL_BLOCK, + ENDTURN_EMBARGO, + ENDTURN_YAWN, + ENDTURN_PERISH_SONG, + ENDTURN_ROOST, + ENDTURN_EMERGENCY_EXIT_3, + ENDTURN_SECOND_EVENT_BLOCK, + ENDTURN_TRICK_ROOM, + ENDTURN_GRAVITY, + ENDTURN_WATER_SPORT, + ENDTURN_MUD_SPORT, + ENDTURN_WONDER_ROOM, + ENDTURN_MAGIC_ROOM, + ENDTURN_TERRAIN, + ENDTURN_THIRD_EVENT_BLOCK, + ENDTURN_EMERGENCY_EXIT_4, + ENDTURN_ABILITIES, + ENDTURN_FOURTH_EVENT_BLOCK, + ENDTURN_DYNAMAX, + ENDTURN_COUNT, +}; + +// Block that handles effects for each individual battler on the field (eg residual damage) +enum FirstEventBlock +{ + FIRST_EVENT_BLOCK_GMAX_MOVE_RESIDUAL, // Needs to be split + FIRST_EVENT_BLOCK_SEA_OF_FIRE_DAMAGE, + FIRST_EVENT_BLOCK_THRASH, // Thrash isn't handled here in vanilla but for now it is that best place for it. + FIRST_EVENT_BLOCK_GRASSY_TERRAIN_HEAL, + FIRST_EVENT_BLOCK_ABILITIES, + FIRST_EVENT_BLOCK_HEAL_ITEMS, +}; + +// Block that tries to remove side statuses +enum SecondEventBlock +{ + SECOND_EVENT_BLOCK_REFLECT, + SECOND_EVENT_BLOCK_LIGHT_SCREEN, + SECOND_EVENT_BLOCK_SAFEGUARD, + SECOND_EVENT_BLOCK_MIST, + SECOND_EVENT_BLOCK_TAILWIND, + SECOND_EVENT_BLOCK_LUCKY_CHANT, + SECOND_EVENT_BLOCK_RAINBOW, + SECOND_EVENT_BLOCK_SEA_OF_FIRE, + SECOND_EVENT_BLOCK_SWAMP, + SECOND_EVENT_BLOCK_AURORA_VEIL, +}; + +// Block that handles Uproar, items and non-form changing abilities +enum ThirdEventBlock +{ + THIRD_EVENT_BLOCK_UPROAR, + THIRD_EVENT_BLOCK_ABILITIES, + THIRD_EVENT_BLOCK_ITEMS, +}; + +// Form changing abilities and Eject Pack +enum FourthEventBlock +{ + FOURTH_EVENT_BLOCK_HUNGER_SWITCH, + FOURTH_EVENT_BLOCK_EJECT_PACK, +}; + +static inline bool32 IsBattlerProtectedByMagicGuard(u32 battler, u32 ability) +{ + if (ability != ABILITY_MAGIC_GUARD) + return FALSE; + + RecordAbilityBattle(battler, ability); + return TRUE; +} + +static bool32 HandleEndTurnOrder(u32 battler) +{ + bool32 effect = FALSE; + + gBattleTurnCounter++; + gBattleStruct->endTurnEventsCounter++; + + u32 i, j; + for (i = 0; i < gBattlersCount; i++) + { + gBattlerByTurnOrder[i] = i; + } + for (i = 0; i < gBattlersCount - 1; i++) + { + for (j = i + 1; j < gBattlersCount; j++) + { + if (GetWhichBattlerFaster(gBattlerByTurnOrder[i], gBattlerByTurnOrder[j], FALSE) == -1) + SwapTurnOrder(i, j); + } + } + + return effect; +} + +static bool32 HandleEndTurnVarious(u32 battler) +{ + u32 i; + bool32 effect = FALSE; + + gBattleStruct->endTurnEventsCounter++; + + if (gFieldStatuses & STATUS_FIELD_FAIRY_LOCK && gFieldTimers.fairyLockTimer == gBattleTurnCounter) + gFieldStatuses &= ~STATUS_FIELD_FAIRY_LOCK; + + for (i = 0; i < NUM_BATTLE_SIDES; i++) + { + if (gSideStatuses[i] & SIDE_STATUS_DAMAGE_NON_TYPES && gSideTimers[i].damageNonTypesTimer == gBattleTurnCounter) + gSideStatuses[i] &= ~SIDE_STATUS_DAMAGE_NON_TYPES; + } + + for (i = 0; i < gBattlersCount; i++) + { + if (gStatuses3[i] & STATUS3_ALWAYS_HITS) + gStatuses3[i] -= STATUS3_ALWAYS_HITS_TURN(1); + + if (gDisableStructs[i].chargeTimer && --gDisableStructs[i].chargeTimer == 0) + gStatuses3[i] &= ~STATUS3_CHARGED_UP; + + if (gStatuses3[i] & STATUS3_LASER_FOCUS && gDisableStructs[i].laserFocusTimer == gBattleTurnCounter) + gStatuses3[i] &= ~STATUS3_LASER_FOCUS; + + gBattleStruct->hpBefore[i] = gBattleMons[i].hp; + } + + return effect; +} + +static bool32 HandleEndTurnWeather(u32 battler) +{ + gBattleStruct->endTurnEventsCounter++; + return EndOrContinueWeather(); +} + +static bool32 HandleEndTurnWeatherDamage(u32 battler) +{ + bool32 effect = FALSE; + + u32 ability = GetBattlerAbility(battler); + u32 currBattleWeather = GetCurrentBattleWeather(); + + if (currBattleWeather == 0xFF) + { + // If there is no weather on the field, no need to check other battlers so go to next state + gBattleStruct->turnEffectsBattlerId = 0; + gBattleStruct->endTurnEventsCounter++; + return effect; + } + + gBattleStruct->turnEffectsBattlerId++; + + if (!IsBattlerAlive(battler) || !HasWeatherEffect()) + return effect; + + + switch (currBattleWeather) + { + case BATTLE_WEATHER_FOG: + case BATTLE_WEATHER_STRONG_WINDS: + break; + case BATTLE_WEATHER_RAIN: + case BATTLE_WEATHER_RAIN_PRIMAL: + case BATTLE_WEATHER_RAIN_DOWNPOUR: + if (ability == ABILITY_DRY_SKIN || ability == ABILITY_RAIN_DISH) + { + if (AbilityBattleEffects(ABILITYEFFECT_ENDTURN, battler, ability, 0, MOVE_NONE)) + effect = TRUE; + } + break; + case BATTLE_WEATHER_SUN: + case BATTLE_WEATHER_SUN_PRIMAL: + if (ability == ABILITY_DRY_SKIN || ability == ABILITY_SOLAR_POWER) + { + if (AbilityBattleEffects(ABILITYEFFECT_ENDTURN, battler, ability, 0, MOVE_NONE)) + effect = TRUE; + } + break; + case BATTLE_WEATHER_SANDSTORM: + if (ability != ABILITY_SAND_VEIL + && ability != ABILITY_SAND_FORCE + && ability != ABILITY_SAND_RUSH + && ability != ABILITY_OVERCOAT + && !IS_BATTLER_ANY_TYPE(gBattlerAttacker, TYPE_ROCK, TYPE_GROUND, TYPE_STEEL) + && !(gStatuses3[gBattlerAttacker] & (STATUS3_UNDERGROUND | STATUS3_UNDERWATER)) + && GetBattlerHoldEffect(gBattlerAttacker, TRUE) != HOLD_EFFECT_SAFETY_GOGGLES + && !IsBattlerProtectedByMagicGuard(battler, ability)) + { + gBattleStruct->moveDamage[battler] = GetNonDynamaxMaxHP(battler) / 16; + if (gBattleStruct->moveDamage[battler] == 0) + gBattleStruct->moveDamage[battler] = 1; + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SANDSTORM; + BattleScriptExecute(BattleScript_DamagingWeather); + effect = TRUE; + } + break; + case BATTLE_WEATHER_HAIL: + case BATTLE_WEATHER_SNOW: + if (ability == ABILITY_ICE_BODY) + { + if (AbilityBattleEffects(ABILITYEFFECT_ENDTURN, battler, ability, 0, MOVE_NONE)) + effect = TRUE; + } + else if (currBattleWeather == BATTLE_WEATHER_HAIL) + { + if (ability != ABILITY_SNOW_CLOAK + && ability != ABILITY_OVERCOAT + && !IS_BATTLER_OF_TYPE(battler, TYPE_ICE) + && !(gStatuses3[battler] & (STATUS3_UNDERGROUND | STATUS3_UNDERWATER)) + && GetBattlerHoldEffect(battler, TRUE) != HOLD_EFFECT_SAFETY_GOGGLES + && !IsBattlerProtectedByMagicGuard(battler, ability)) + { + gBattleStruct->moveDamage[battler] = GetNonDynamaxMaxHP(battler) / 16; + if (gBattleStruct->moveDamage[battler] == 0) + gBattleStruct->moveDamage[battler] = 1; + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_HAIL; + BattleScriptExecute(BattleScript_DamagingWeather); + effect = TRUE; + } + } + break; + } + + return effect; +} + +static bool32 HandleEndTurnEmergencyExit(u32 battler) +{ + bool32 effect = FALSE; + u32 ability = GetBattlerAbility(battler); + + gBattleStruct->turnEffectsBattlerId++; + + if (ability == ABILITY_EMERGENCY_EXIT || ability == ABILITY_WIMP_OUT) + { + u32 cutoff = gBattleMons[battler].maxHP / 2; + bool32 HadMoreThanHalfHpNowDoesnt = gBattleStruct->hpBefore[battler] > cutoff && gBattleMons[battler].hp <= cutoff; + + if (HadMoreThanHalfHpNowDoesnt + && IsBattlerAlive(battler) + && (CanBattlerSwitch(battler) || !(gBattleTypeFlags & BATTLE_TYPE_TRAINER)) + && !(gBattleTypeFlags & BATTLE_TYPE_ARENA) + && CountUsablePartyMons(battler) > 0 + && !(gStatuses3[battler] & STATUS3_SKY_DROPPED)) // Not currently held by Sky Drop + { + gBattlerAbility = battler; + gLastUsedAbility = ability; + + if (gBattleTypeFlags & BATTLE_TYPE_TRAINER || GetBattlerSide(battler) == B_SIDE_PLAYER) + BattleScriptExecute(BattleScript_EmergencyExitEnd2); + else + BattleScriptExecute(BattleScript_EmergencyExitWildEnd2); + + effect = TRUE; + } + } + + return effect; +} + +static bool32 HandleEndTurnAffection(u32 battler) +{ + bool32 effect = FALSE; + + gBattleStruct->turnEffectsBattlerId++; + + if (!B_AFFECTION_MECHANICS + || !IsBattlerAlive(battler) + || GetBattlerSide(battler) != B_SIDE_PLAYER) + return effect; + + if (GetBattlerAffectionHearts(gBattlerAttacker) >= AFFECTION_FOUR_HEARTS && (Random() % 100 < 20)) + { + gBattleCommunication[MULTISTRING_CHOOSER] = 1; + BattleScriptExecute(BattleScript_AffectionBasedStatusHeal); + effect = TRUE; + } + + return effect; +} + +// Note: Technically Future Sight, Doom Desire and Wish need a queue but +// I think we should accept this slight inconsistency so custom moves don't have to touch this code +static bool32 HandleEndTurnFutureSight(u32 battler) +{ + bool32 effect = FALSE; + + gBattleStruct->turnEffectsBattlerId++; + + if (gWishFutureKnock.futureSightCounter[battler] == gBattleTurnCounter) + { + struct Pokemon *party; + + if (gWishFutureKnock.futureSightCounter[battler] == gBattleTurnCounter + && gWishFutureKnock.futureSightCounter[BATTLE_PARTNER(battler)] <= gBattleTurnCounter) + { + gSideStatuses[GetBattlerSide(battler)] &= ~SIDE_STATUS_FUTUREATTACK; + } + + if (!IsBattlerAlive(battler)) + return effect; + + if (gWishFutureKnock.futureSightMove[battler] == MOVE_FUTURE_SIGHT) + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_FUTURE_SIGHT; + else + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_DOOM_DESIRE; + + PREPARE_MOVE_BUFFER(gBattleTextBuff1, gWishFutureKnock.futureSightMove[battler]); + + gBattlerTarget = battler; + gBattlerAttacker = gWishFutureKnock.futureSightBattlerIndex[battler]; + gCurrentMove = gWishFutureKnock.futureSightMove[battler]; + + party = GetSideParty(GetBattlerSide(gBattlerAttacker)); + if (&party[gWishFutureKnock.futureSightPartyIndex[gBattlerTarget]] == &party[gBattlerPartyIndexes[gBattlerAttacker]]) + SetTypeBeforeUsingMove(gCurrentMove, gBattlerAttacker); + + BattleScriptExecute(BattleScript_MonTookFutureAttack); + effect = TRUE; + } + + return effect; +} + +static bool32 HandleEndTurnWish(u32 battler) +{ + bool32 effect = FALSE; + + gBattleStruct->turnEffectsBattlerId++; + + if (gWishFutureKnock.wishCounter[battler] == gBattleTurnCounter && IsBattlerAlive(battler)) + { + gBattlerTarget = battler; + PREPARE_MON_NICK_WITH_PREFIX_BUFFER(gBattleTextBuff1, battler, gWishFutureKnock.wishPartyId[battler]) + if (B_WISH_HP_SOURCE >= GEN_5) + { + if (GetBattlerSide(battler) == B_SIDE_PLAYER) + gBattleStruct->moveDamage[battler] = max(1, GetMonData(&gPlayerParty[gWishFutureKnock.wishPartyId[battler]], MON_DATA_MAX_HP) / 2); + else + gBattleStruct->moveDamage[battler] = max(1, GetMonData(&gEnemyParty[gWishFutureKnock.wishPartyId[battler]], MON_DATA_MAX_HP) / 2); + } + else + { + gBattleStruct->moveDamage[battler] = max(1, GetNonDynamaxMaxHP(battler) / 2); + } + + gBattleStruct->moveDamage[battler] *= -1; + if (gBattleMons[battler].hp == gBattleMons[battler].maxHP) + BattleScriptExecute(BattleScript_WishButFullHp); + else + BattleScriptExecute(BattleScript_WishComesTrue); + + effect = TRUE; + } + + return effect; +} + +static bool32 HandleEndTurnFirstEventBlock(u32 battler) +{ + bool32 effect = FALSE; + + u32 side; + + switch (gBattleStruct->eventBlockCounter) + { + case FIRST_EVENT_BLOCK_GMAX_MOVE_RESIDUAL: // TODO: Has to be split into 3 statuses and needs a queue + side = GetBattlerSide(battler); + if (gSideStatuses[side] & SIDE_STATUS_DAMAGE_NON_TYPES) + { + if (IsBattlerAlive(battler) + && !IS_BATTLER_OF_TYPE(battler, gSideTimers[side].damageNonTypesType) + && !IsBattlerProtectedByMagicGuard(battler, GetBattlerAbility(battler))) + { + gBattlerAttacker = battler; + gBattleStruct->moveDamage[battler] = GetNonDynamaxMaxHP(battler) / 6; + ChooseDamageNonTypesString(gSideTimers[side].damageNonTypesType); + BattleScriptExecute(BattleScript_DamageNonTypesContinues); + effect = TRUE; + } + } + gBattleStruct->eventBlockCounter++; + break; + case FIRST_EVENT_BLOCK_SEA_OF_FIRE_DAMAGE: + if (gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_SEA_OF_FIRE && IsBattlerAlive(battler)) + { + gBattlerAttacker = battler; + gBattleStruct->moveDamage[battler] = GetNonDynamaxMaxHP(battler) / 8; + BtlController_EmitStatusAnimation(battler, BUFFER_A, FALSE, STATUS1_BURN); + MarkBattlerForControllerExec(battler); + BattleScriptExecute(BattleScript_HurtByTheSeaOfFire); + effect = TRUE; + } + gBattleStruct->eventBlockCounter++; + break; + case FIRST_EVENT_BLOCK_THRASH: + if (gBattleMons[battler].status2 & STATUS2_LOCK_CONFUSE && !(gStatuses3[battler] & STATUS3_SKY_DROPPED)) + { + gBattleMons[battler].status2 -= STATUS2_LOCK_CONFUSE_TURN(1); + if (WasUnableToUseMove(battler)) + { + CancelMultiTurnMoves(battler); + } + else if (!(gBattleMons[battler].status2 & STATUS2_LOCK_CONFUSE) && (gBattleMons[battler].status2 & STATUS2_MULTIPLETURNS)) + { + gBattleMons[battler].status2 &= ~STATUS2_MULTIPLETURNS; + if (!(gBattleMons[battler].status2 & STATUS2_CONFUSION)) + { + gBattleScripting.moveEffect = MOVE_EFFECT_CONFUSION | MOVE_EFFECT_AFFECTS_USER; + SetMoveEffect(TRUE, FALSE); + if (gBattleMons[battler].status2 & STATUS2_CONFUSION) + BattleScriptExecute(BattleScript_ThrashConfuses); + effect = TRUE; + } + } + } + gBattleStruct->eventBlockCounter++; + break; + case FIRST_EVENT_BLOCK_GRASSY_TERRAIN_HEAL: + if (gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN && IsBattlerAlive(battler) && !IsBattlerAtMaxHp(battler)) + { + gBattlerAttacker = battler; + gBattleStruct->moveDamage[battler] = -(GetNonDynamaxMaxHP(battler) / 16); + BattleScriptExecute(BattleScript_GrassyTerrainHeals); + effect = TRUE; + } + gBattleStruct->eventBlockCounter++; + break; + case FIRST_EVENT_BLOCK_ABILITIES: + { + u32 ability = GetBattlerAbility(battler); + switch (ability) + { + case ABILITY_HEALER: + case ABILITY_HYDRATION: + case ABILITY_SHED_SKIN: + if (AbilityBattleEffects(ABILITYEFFECT_ENDTURN, battler, ability, 0, MOVE_NONE)) + effect = TRUE; + break; + } + gBattleStruct->eventBlockCounter++; + break; + } + case FIRST_EVENT_BLOCK_HEAL_ITEMS: + { + u32 holdEffect = GetBattlerHoldEffect(battler, TRUE); + switch (holdEffect) + { + case HOLD_EFFECT_LEFTOVERS: + case HOLD_EFFECT_BLACK_SLUDGE: + if (ItemBattleEffects(ITEMEFFECT_NORMAL, battler, FALSE)) + effect = TRUE; + break; + } + gBattleStruct->eventBlockCounter = 0; + gBattleStruct->turnEffectsBattlerId++; + break; + } + } + + return effect; +} + +static bool32 HandleEndTurnAquaRing(u32 battler) +{ + bool32 effect = FALSE; + + gBattleStruct->turnEffectsBattlerId++; + + if (gStatuses3[battler] & STATUS3_AQUA_RING + && !(gStatuses3[battler] & STATUS3_HEAL_BLOCK) + && !IsBattlerAtMaxHp(battler) + && IsBattlerAlive(battler)) + { + gBattleStruct->moveDamage[battler] = GetDrainedBigRootHp(battler, GetNonDynamaxMaxHP(battler) / 16); + BattleScriptExecute(BattleScript_AquaRingHeal); + effect = TRUE; + } + + return effect; +} + +static bool32 HandleEndTurnIngrain(u32 battler) +{ + bool32 effect = FALSE; + + gBattleStruct->turnEffectsBattlerId++; + + if (gStatuses3[battler] & STATUS3_ROOTED + && !(gStatuses3[battler] & STATUS3_HEAL_BLOCK) + && !IsBattlerAtMaxHp(battler) + && IsBattlerAlive(battler)) + { + gBattleStruct->moveDamage[battler] = GetDrainedBigRootHp(battler, GetNonDynamaxMaxHP(battler) / 16); + BattleScriptExecute(BattleScript_IngrainTurnHeal); + effect = TRUE; + } + + return effect; +} + +static bool32 HandleEndTurnLeechSeed(u32 battler) +{ + bool32 effect = FALSE; + + gBattleStruct->turnEffectsBattlerId++; + + if (gStatuses3[battler] & STATUS3_LEECHSEED + && IsBattlerAlive(gStatuses3[battler] & STATUS3_LEECHSEED_BATTLER) + && IsBattlerAlive(battler) + && !IsBattlerProtectedByMagicGuard(battler, GetBattlerAbility(battler))) + { + gBattlerTarget = gStatuses3[battler] & STATUS3_LEECHSEED_BATTLER; // Notice gBattlerTarget is actually the HP receiver. + gBattleScripting.animArg1 = gBattlerTarget; + gBattleScripting.animArg2 = gBattlerAttacker; + gBattleStruct->moveDamage[gBattlerAttacker] = max(1, GetNonDynamaxMaxHP(battler) / 8); + gBattleStruct->moveDamage[gBattlerTarget] = GetDrainedBigRootHp(gBattlerAttacker, gBattleStruct->moveDamage[gBattlerAttacker]); + gHitMarker |= HITMARKER_IGNORE_SUBSTITUTE | HITMARKER_PASSIVE_DAMAGE; + if (GetBattlerAbility(battler) == ABILITY_LIQUID_OOZE) + { + gBattleStruct->moveDamage[gBattlerTarget] = gBattleStruct->moveDamage[gBattlerTarget] * -1; + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_LEECH_SEED_OOZE; + BattleScriptExecute(BattleScript_LeechSeedTurnDrainLiquidOoze); + } + else if (gStatuses3[gBattlerTarget] & STATUS3_HEAL_BLOCK) + { + BattleScriptExecute(BattleScript_LeechSeedTurnDrainHealBlock); + } + else + { + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_LEECH_SEED_DRAIN; + BattleScriptExecute(BattleScript_LeechSeedTurnDrainRecovery); + } + effect = TRUE; + } + + return effect; +} + +static bool32 HandleEndTurnPoison(u32 battler) +{ + bool32 effect = FALSE; + + u32 ability = GetBattlerAbility(battler); + + gBattleStruct->turnEffectsBattlerId++; + + if ((gBattleMons[battler].status1 & STATUS1_POISON || gBattleMons[battler].status1 & STATUS1_TOXIC_POISON) + && IsBattlerAlive(battler) + && !IsBattlerProtectedByMagicGuard(battler, ability)) + { + if (ability == ABILITY_POISON_HEAL) + { + if (!IsBattlerAtMaxHp(battler) && !(gStatuses3[battler] & STATUS3_HEAL_BLOCK)) + { + gBattleStruct->moveDamage[battler] = GetNonDynamaxMaxHP(battler) / 8; + if (gBattleStruct->moveDamage[battler] == 0) + gBattleStruct->moveDamage[battler] = 1; + gBattleStruct->moveDamage[battler] *= -1; + BattleScriptExecute(BattleScript_PoisonHealActivates); + effect = TRUE; + } + } + else if (gBattleMons[battler].status1 & STATUS1_TOXIC_POISON) + { + gBattleStruct->moveDamage[battler] = GetNonDynamaxMaxHP(battler) / 16; + if (gBattleStruct->moveDamage[battler] == 0) + gBattleStruct->moveDamage[battler] = 1; + if ((gBattleMons[battler].status1 & STATUS1_TOXIC_COUNTER) != STATUS1_TOXIC_TURN(15)) // not 16 turns + gBattleMons[battler].status1 += STATUS1_TOXIC_TURN(1); + gBattleStruct->moveDamage[battler] *= (gBattleMons[battler].status1 & STATUS1_TOXIC_COUNTER) >> 8; + BattleScriptExecute(BattleScript_PoisonTurnDmg); + effect = TRUE; + } + else + { + gBattleStruct->moveDamage[battler] = GetNonDynamaxMaxHP(battler) / 8; + if (gBattleStruct->moveDamage[battler] == 0) + gBattleStruct->moveDamage[battler] = 1; + BattleScriptExecute(BattleScript_PoisonTurnDmg); + effect = TRUE; + } + } + + return effect; +} + +static bool32 HandleEndTurnBurn(u32 battler) +{ + bool32 effect = FALSE; + + u32 ability = GetBattlerAbility(battler); + + gBattleStruct->turnEffectsBattlerId++; + + if (gBattleMons[battler].status1 & STATUS1_BURN + && IsBattlerAlive(battler) + && !IsBattlerProtectedByMagicGuard(battler, ability)) + { + gBattleStruct->moveDamage[battler] = GetNonDynamaxMaxHP(battler) / (B_BURN_DAMAGE >= GEN_7 ? 16 : 8); + if (ability == ABILITY_HEATPROOF) + { + if (gBattleStruct->moveDamage[battler] > (gBattleStruct->moveDamage[battler] / 2) + 1) // Record ability if the burn takes less damage than it normally would. + RecordAbilityBattle(battler, ABILITY_HEATPROOF); + gBattleStruct->moveDamage[battler] /= 2; + } + if (gBattleStruct->moveDamage[battler] == 0) + gBattleStruct->moveDamage[battler] = 1; + BattleScriptExecute(BattleScript_BurnTurnDmg); + effect = TRUE; + } + + return effect; +} + +static bool32 HandleEndTurnFrostbite(u32 battler) +{ + bool32 effect = FALSE; + + gBattleStruct->turnEffectsBattlerId++; + + if (gBattleMons[battler].status1 & STATUS1_FROSTBITE + && IsBattlerAlive(battler) + && !IsBattlerProtectedByMagicGuard(battler, GetBattlerAbility(battler))) + { + gBattleStruct->moveDamage[battler] = GetNonDynamaxMaxHP(battler) / (B_BURN_DAMAGE >= GEN_7 ? 16 : 8); + if (gBattleStruct->moveDamage[battler] == 0) + gBattleStruct->moveDamage[battler] = 1; + BattleScriptExecute(BattleScript_FrostbiteTurnDmg); + effect = TRUE; + } + + return effect; +} + +static bool32 HandleEndTurnNightmare(u32 battler) +{ + bool32 effect = FALSE; + + gBattleStruct->turnEffectsBattlerId++; + + if (gBattleMons[battler].status2 & STATUS2_NIGHTMARE + && IsBattlerAlive(battler) + && !IsBattlerProtectedByMagicGuard(battler, GetBattlerAbility(battler))) + { + if (gBattleMons[battler].status1 & STATUS1_SLEEP) + { + gBattleStruct->moveDamage[battler] = GetNonDynamaxMaxHP(battler) / 4; + if (gBattleStruct->moveDamage[battler] == 0) + gBattleStruct->moveDamage[battler] = 1; + BattleScriptExecute(BattleScript_NightmareTurnDmg); + effect = TRUE; + } + else + { + gBattleMons[battler].status2 &= ~STATUS2_NIGHTMARE; + } + } + + return effect; +} + +static bool32 HandleEndTurnCurse(u32 battler) +{ + bool32 effect = FALSE; + + gBattleStruct->turnEffectsBattlerId++; + + if (gBattleMons[battler].status2 & STATUS2_CURSED + && IsBattlerAlive(battler) + && !IsBattlerProtectedByMagicGuard(battler, GetBattlerAbility(battler))) + { + gBattleStruct->moveDamage[battler] = GetNonDynamaxMaxHP(battler) / 4; + if (gBattleStruct->moveDamage[battler] == 0) + gBattleStruct->moveDamage[battler] = 1; + BattleScriptExecute(BattleScript_CurseTurnDmg); + effect = TRUE; + } + + return effect; +} + +static bool32 HandleEndTurnWrap(u32 battler) +{ + bool32 effect = FALSE; + + gBattleStruct->turnEffectsBattlerId++; + + if (gBattleMons[battler].status2 & STATUS2_WRAPPED && IsBattlerAlive(battler)) + { + if (--gDisableStructs[battler].wrapTurns != 0 && !IsBattlerProtectedByMagicGuard(battler, GetBattlerAbility(battler))) + { + gBattleScripting.animArg1 = gBattleStruct->wrappedMove[battler]; + gBattleScripting.animArg2 = gBattleStruct->wrappedMove[battler] >> 8; + PREPARE_MOVE_BUFFER(gBattleTextBuff1, gBattleStruct->wrappedMove[battler]); + BattleScriptExecute(BattleScript_WrapTurnDmg); + if (GetBattlerHoldEffect(gBattleStruct->wrappedBy[battler], TRUE) == HOLD_EFFECT_BINDING_BAND) + gBattleStruct->moveDamage[battler] = GetNonDynamaxMaxHP(battler) / (B_BINDING_DAMAGE >= GEN_6 ? 6 : 8); + else + gBattleStruct->moveDamage[battler] = GetNonDynamaxMaxHP(battler) / (B_BINDING_DAMAGE >= GEN_6 ? 8 : 16); + + if (gBattleStruct->moveDamage[battler] == 0) + gBattleStruct->moveDamage[battler] = 1; + } + else // broke free + { + gBattleMons[battler].status2 &= ~STATUS2_WRAPPED; + PREPARE_MOVE_BUFFER(gBattleTextBuff1, gBattleStruct->wrappedMove[battler]); + BattleScriptExecute(BattleScript_WrapEnds); + } + effect = TRUE; + } + + return effect; +} + +static bool32 HandleEndTurnSaltCure(u32 battler) +{ + bool32 effect = FALSE; + + gBattleStruct->turnEffectsBattlerId++; + + if (gStatuses4[battler] & STATUS4_SALT_CURE + && IsBattlerAlive(battler) + && !IsBattlerProtectedByMagicGuard(battler, GetBattlerAbility(battler))) + { + if (IS_BATTLER_ANY_TYPE(battler, TYPE_STEEL, TYPE_WATER)) + gBattleStruct->moveDamage[battler] = gBattleMons[battler].maxHP / 4; + else + gBattleStruct->moveDamage[battler] = gBattleMons[battler].maxHP / 8; + if (gBattleStruct->moveDamage[battler] == 0) + gBattleStruct->moveDamage[battler] = 1; + PREPARE_MOVE_BUFFER(gBattleTextBuff1, MOVE_SALT_CURE); + BattleScriptExecute(BattleScript_SaltCureExtraDamage); + effect = TRUE; + } + + return effect; +} + +static bool32 HandleEndTurnOctolock(u32 battler) +{ + bool32 effect = FALSE; + + gBattleStruct->turnEffectsBattlerId++; + + if (gDisableStructs[battler].octolock) + { + gBattlerTarget = battler; + gBattlerAttacker = gDisableStructs[battler].battlerPreventingEscape; + BattleScriptExecute(BattleScript_OctolockEndTurn); + effect = TRUE; + } + + return effect; +} + +static bool32 HandleEndTurnSyrupBomb(u32 battler) +{ + bool32 effect = FALSE; + + gBattleStruct->turnEffectsBattlerId++; + + if ((gStatuses4[battler] & STATUS4_SYRUP_BOMB) && (IsBattlerAlive(battler))) + { + if (gDisableStructs[battler].syrupBombTimer > 0 && --gDisableStructs[battler].syrupBombTimer == 0) + gStatuses4[battler] &= ~STATUS4_SYRUP_BOMB; + PREPARE_MOVE_BUFFER(gBattleTextBuff1, MOVE_SYRUP_BOMB); + gBattlescriptCurrInstr = BattleScript_SyrupBombEndTurn; + BattleScriptExecute(gBattlescriptCurrInstr); + effect = TRUE; + } + + return effect; +} + +static bool32 HandleEndTurnTaunt(u32 battler) +{ + bool32 effect = FALSE; + + gBattleStruct->turnEffectsBattlerId++; + + if (gDisableStructs[battler].tauntTimer && --gDisableStructs[battler].tauntTimer == 0) + { + BattleScriptExecute(BattleScript_BufferEndTurn); + PREPARE_MOVE_BUFFER(gBattleTextBuff1, MOVE_TAUNT); + effect = TRUE; + } + + return effect; +} + +static bool32 HandleEndTurnTorment(u32 battler) +{ + bool32 effect = FALSE; + + gBattleStruct->turnEffectsBattlerId++; + + if (gDisableStructs[battler].tormentTimer == gBattleTurnCounter) + { + gBattleMons[battler].status2 &= ~STATUS2_TORMENT; + BattleScriptExecute(BattleScript_TormentEnds); + effect = TRUE; + } + + return effect; +} + +static bool32 HandleEndTurnEncore(u32 battler) +{ + bool32 effect = FALSE; + + gBattleStruct->turnEffectsBattlerId++; + + if (gDisableStructs[battler].encoreTimer != 0) + { + if (gBattleMons[battler].moves[gDisableStructs[battler].encoredMovePos] != gDisableStructs[battler].encoredMove) // Pokémon does not have the encored move anymore + { + gDisableStructs[battler].encoredMove = 0; + gDisableStructs[battler].encoreTimer = 0; + } + else if (--gDisableStructs[battler].encoreTimer == 0 + || gBattleMons[battler].pp[gDisableStructs[battler].encoredMovePos] == 0) + { + gDisableStructs[battler].encoredMove = 0; + gDisableStructs[battler].encoreTimer = 0; + BattleScriptExecute(BattleScript_EncoredNoMore); + effect = TRUE; + } + } + + return effect; +} + +static bool32 HandleEndTurnDisable(u32 battler) +{ + bool32 effect = FALSE; + + u32 moveIndex = 0; + gBattleStruct->turnEffectsBattlerId++; + + if (gDisableStructs[battler].disableTimer != 0) + { + for (moveIndex = 0; moveIndex < MAX_MON_MOVES; moveIndex++) + { + if (gDisableStructs[battler].disabledMove == gBattleMons[battler].moves[moveIndex]) + break; + } + if (moveIndex == MAX_MON_MOVES) // Pokémon does not have the disabled move anymore + { + gDisableStructs[battler].disabledMove = 0; + gDisableStructs[battler].disableTimer = 0; + } + else if (--gDisableStructs[battler].disableTimer == 0) // disable ends + { + gDisableStructs[battler].disabledMove = 0; + BattleScriptExecute(BattleScript_DisabledNoMore); + effect = TRUE; + } + } + + return effect; +} + +static bool32 HandleEndTurnMagnetRise(u32 battler) +{ + bool32 effect = FALSE; + + gBattleStruct->turnEffectsBattlerId++; + + if (gStatuses3[battler] & STATUS3_MAGNET_RISE && gDisableStructs[battler].magnetRiseTimer == gBattleTurnCounter) + { + gStatuses3[battler] &= ~STATUS3_MAGNET_RISE; + BattleScriptExecute(BattleScript_BufferEndTurn); + PREPARE_STRING_BUFFER(gBattleTextBuff1, STRINGID_ELECTROMAGNETISM); + effect = TRUE; + } + + return effect; +} + +static bool32 HandleEndTurnTelekinesis(u32 battler) +{ + bool32 effect = FALSE; + + gBattleStruct->turnEffectsBattlerId++; + + if (gStatuses3[battler] & STATUS3_TELEKINESIS && gDisableStructs[battler].telekinesisTimer == gBattleTurnCounter) + { + gStatuses3[battler] &= ~STATUS3_TELEKINESIS; + BattleScriptExecute(BattleScript_TelekinesisEndTurn); + effect = TRUE; + } + + return effect; +} + +static bool32 HandleEndTurnHealBlock(u32 battler) +{ + bool32 effect = FALSE; + + gBattleStruct->turnEffectsBattlerId++; + + if (gStatuses3[battler] & STATUS3_HEAL_BLOCK && gDisableStructs[battler].healBlockTimer == gBattleTurnCounter) + { + gStatuses3[battler] &= ~STATUS3_HEAL_BLOCK; + BattleScriptExecute(BattleScript_BufferEndTurn); + PREPARE_MOVE_BUFFER(gBattleTextBuff1, MOVE_HEAL_BLOCK); + effect = TRUE; + } + + return effect; +} + +static bool32 HandleEndTurnEmbargo(u32 battler) +{ + bool32 effect = FALSE; + + gBattleStruct->turnEffectsBattlerId++; + + if (gStatuses3[battler] & STATUS3_EMBARGO && gDisableStructs[battler].embargoTimer == gBattleTurnCounter) + { + gStatuses3[battler] &= ~STATUS3_EMBARGO; + BattleScriptExecute(BattleScript_EmbargoEndTurn); + effect = TRUE; + } + + return effect; +} + +static bool32 HandleEndTurnYawn(u32 battler) +{ + bool32 effect = FALSE; + + u32 ability = GetBattlerAbility(battler); + + gBattleStruct->turnEffectsBattlerId++; + + if (gStatuses3[battler] & STATUS3_YAWN) + { + gStatuses3[battler] -= STATUS3_YAWN_TURN(1); + if (!(gStatuses3[battler] & STATUS3_YAWN) && !(gBattleMons[battler].status1 & STATUS1_ANY) + && ability != ABILITY_VITAL_SPIRIT + && ability != ABILITY_INSOMNIA + && !UproarWakeUpCheck(battler) + && !IsLeafGuardProtected(battler, ability)) + { + CancelMultiTurnMoves(battler); + gEffectBattler = gBattlerTarget = battler; + if (IsBattlerTerrainAffected(battler, STATUS_FIELD_ELECTRIC_TERRAIN)) + { + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAINPREVENTS_ELECTRIC; + BattleScriptExecute(BattleScript_TerrainPreventsEnd2); + } + else if (IsBattlerTerrainAffected(battler, STATUS_FIELD_MISTY_TERRAIN)) + { + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAINPREVENTS_MISTY; + BattleScriptExecute(BattleScript_TerrainPreventsEnd2); + } + else if (IsSleepClauseActiveForSide(GetBattlerSide(battler))) + { + BattleScriptExecute(BattleScript_SleepClausePreventsEnd); + } + else + { + if (B_SLEEP_TURNS >= GEN_5) + gBattleMons[battler].status1 |= ((Random() % 3) + 2); + else + gBattleMons[battler].status1 |= ((Random() % 4) + 3); + + TryActivateSleepClause(battler, gBattlerPartyIndexes[battler]); + BtlController_EmitSetMonData(battler, BUFFER_A, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[battler].status1); + MarkBattlerForControllerExec(battler); + BattleScriptExecute(BattleScript_YawnMakesAsleep); + } + effect = TRUE; + } + } + + return effect; +} + +static bool32 HandleEndTurnPerishSong(u32 battler) +{ + bool32 effect = FALSE; + + gBattleStruct->turnEffectsBattlerId++; + + if (IsBattlerAlive(battler) && gStatuses3[battler] & STATUS3_PERISH_SONG) + { + PREPARE_BYTE_NUMBER_BUFFER(gBattleTextBuff1, 1, gDisableStructs[battler].perishSongTimer); + if (gDisableStructs[battler].perishSongTimer == 0) + { + gStatuses3[battler] &= ~STATUS3_PERISH_SONG; + gBattleStruct->moveDamage[battler] = gBattleMons[battler].hp; + BattleScriptExecute(BattleScript_PerishSongTakesLife); + } + else + { + gDisableStructs[battler].perishSongTimer--; + BattleScriptExecute(BattleScript_PerishSongCountGoesDown); + } + effect = TRUE; + } + + return effect; +} + +static bool32 HandleEndTurnRoost(u32 battler) +{ + bool32 effect = FALSE; + + gBattleStruct->turnEffectsBattlerId++; + + if (gDisableStructs[battler].roostActive) + gDisableStructs[battler].roostActive = FALSE; + + return effect; +} + +static bool32 HandleEndTurnSecondEventBlock(u32 battler) +{ + bool32 effect = FALSE; + + u32 side = gBattleStruct->turnSideTracker; + + switch (gBattleStruct->eventBlockCounter) + { + case SECOND_EVENT_BLOCK_REFLECT: + if (gSideStatuses[side] & SIDE_STATUS_REFLECT && gSideTimers[side].reflectTimer == gBattleTurnCounter) + { + gBattlerAttacker = gSideTimers[side].reflectBattlerId; + gSideStatuses[side] &= ~SIDE_STATUS_REFLECT; + BattleScriptExecute(BattleScript_SideStatusWoreOff); + gBattleCommunication[MULTISTRING_CHOOSER] = side; + PREPARE_MOVE_BUFFER(gBattleTextBuff1, MOVE_REFLECT); + effect = TRUE; + } + gBattleStruct->eventBlockCounter++; + break; + case SECOND_EVENT_BLOCK_LIGHT_SCREEN: + if (gSideStatuses[side] & SIDE_STATUS_LIGHTSCREEN && gSideTimers[side].lightscreenTimer == gBattleTurnCounter) + { + gBattlerAttacker = gSideTimers[side].lightscreenBattlerId; + gSideStatuses[side] &= ~SIDE_STATUS_LIGHTSCREEN; + BattleScriptExecute(BattleScript_SideStatusWoreOff); + gBattleCommunication[MULTISTRING_CHOOSER] = side; + PREPARE_MOVE_BUFFER(gBattleTextBuff1, MOVE_LIGHT_SCREEN); + effect = TRUE; + } + gBattleStruct->eventBlockCounter++; + break; + case SECOND_EVENT_BLOCK_SAFEGUARD: + if (gSideStatuses[side] & SIDE_STATUS_SAFEGUARD && gSideTimers[side].safeguardTimer == gBattleTurnCounter) + { + gBattlerAttacker = gSideTimers[side].safeguardBattlerId; + gSideStatuses[side] &= ~SIDE_STATUS_SAFEGUARD; + BattleScriptExecute(BattleScript_SafeguardEnds); + effect = TRUE; + } + gBattleStruct->eventBlockCounter++; + break; + case SECOND_EVENT_BLOCK_MIST: + if (gSideTimers[side].mistTimer != 0 && gSideTimers[side].mistTimer == gBattleTurnCounter) + { + gBattlerAttacker = gSideTimers[side].mistBattlerId; + gSideStatuses[side] &= ~SIDE_STATUS_MIST; + BattleScriptExecute(BattleScript_SideStatusWoreOff); + gBattleCommunication[MULTISTRING_CHOOSER] = side; + PREPARE_MOVE_BUFFER(gBattleTextBuff1, MOVE_MIST); + effect = TRUE; + } + gBattleStruct->eventBlockCounter++; + break; + case SECOND_EVENT_BLOCK_TAILWIND: + if (gSideStatuses[side] & SIDE_STATUS_TAILWIND && gSideTimers[side].tailwindTimer == gBattleTurnCounter) + { + gBattlerAttacker = gSideTimers[side].tailwindBattlerId; + gSideStatuses[side] &= ~SIDE_STATUS_TAILWIND; + BattleScriptExecute(BattleScript_TailwindEnds); + effect = TRUE; + } + gBattleStruct->eventBlockCounter++; + break; + case SECOND_EVENT_BLOCK_LUCKY_CHANT: + if (gSideStatuses[side] & SIDE_STATUS_LUCKY_CHANT && gSideTimers[side].luckyChantTimer == gBattleTurnCounter) + { + gBattlerAttacker = gSideTimers[side].luckyChantBattlerId; + gSideStatuses[side] &= ~SIDE_STATUS_LUCKY_CHANT; + BattleScriptExecute(BattleScript_LuckyChantEnds); + effect = TRUE; + } + gBattleStruct->eventBlockCounter++; + break; + case SECOND_EVENT_BLOCK_RAINBOW: + if (gSideStatuses[side] & SIDE_STATUS_RAINBOW) + { + for (gBattlerAttacker = 0; gBattlerAttacker < gBattlersCount; gBattlerAttacker++) + { + if (GetBattlerSide(gBattlerAttacker) == side) + break; + } + + if (gSideTimers[side].rainbowTimer == gBattleTurnCounter) + { + gSideStatuses[side] &= ~SIDE_STATUS_RAINBOW; + BattleScriptExecute(BattleScript_TheRainbowDisappeared); + effect = TRUE; + } + } + gBattleStruct->eventBlockCounter++; + break; + case SECOND_EVENT_BLOCK_SEA_OF_FIRE: + if (gSideStatuses[side] & SIDE_STATUS_SEA_OF_FIRE) + { + for (gBattlerAttacker = 0; gBattlerAttacker < gBattlersCount; gBattlerAttacker++) + { + if (GetBattlerSide(gBattlerAttacker) == side) + break; + } + + if (gSideTimers[side].seaOfFireTimer == gBattleTurnCounter) + { + gSideStatuses[side] &= ~SIDE_STATUS_SEA_OF_FIRE; + BattleScriptExecute(BattleScript_TheSeaOfFireDisappeared); + effect = TRUE; + } + } + gBattleStruct->eventBlockCounter++; + break; + case SECOND_EVENT_BLOCK_SWAMP: + if (gSideStatuses[side] & SIDE_STATUS_SWAMP) + { + for (gBattlerAttacker = 0; gBattlerAttacker < gBattlersCount; gBattlerAttacker++) + { + if (GetBattlerSide(gBattlerAttacker) == side) + break; + } + + if (gSideTimers[side].swampTimer == gBattleTurnCounter) + { + gSideStatuses[side] &= ~SIDE_STATUS_SWAMP; + BattleScriptExecute(BattleScript_TheSwampDisappeared); + effect = TRUE; + } + } + gBattleStruct->eventBlockCounter++; + break; + case SECOND_EVENT_BLOCK_AURORA_VEIL: + if (gSideStatuses[side] & SIDE_STATUS_AURORA_VEIL && gSideTimers[side].auroraVeilTimer == gBattleTurnCounter) + { + gBattlerAttacker = gSideTimers[side].auroraVeilBattlerId; + gSideStatuses[side] &= ~SIDE_STATUS_AURORA_VEIL; + BattleScriptExecute(BattleScript_SideStatusWoreOff); + gBattleCommunication[MULTISTRING_CHOOSER] = side; + PREPARE_MOVE_BUFFER(gBattleTextBuff1, MOVE_AURORA_VEIL); + effect = TRUE; + } + gBattleStruct->turnSideTracker++; + gBattleStruct->eventBlockCounter = 0; + break; + } + + return effect; +} + +static bool32 HandleEndTurnTrickRoom(u32 battler) +{ + bool32 effect = FALSE; + + gBattleStruct->endTurnEventsCounter++; + + if (gFieldStatuses & STATUS_FIELD_TRICK_ROOM && gFieldTimers.trickRoomTimer == gBattleTurnCounter) + { + gFieldStatuses &= ~STATUS_FIELD_TRICK_ROOM; + BattleScriptExecute(BattleScript_TrickRoomEnds); + effect = TRUE; + } + + return effect; +} + +static bool32 HandleEndTurnGravity(u32 battler) +{ + bool32 effect = FALSE; + + gBattleStruct->endTurnEventsCounter++; + + if (gFieldStatuses & STATUS_FIELD_GRAVITY && gFieldTimers.gravityTimer == gBattleTurnCounter) + { + gFieldStatuses &= ~STATUS_FIELD_GRAVITY; + BattleScriptExecute(BattleScript_GravityEnds); + effect = TRUE; + } + + return effect; +} + +static bool32 HandleEndTurnWaterSport(u32 battler) +{ + bool32 effect = FALSE; + + gBattleStruct->endTurnEventsCounter++; + + if (gFieldStatuses & STATUS_FIELD_WATERSPORT && gFieldTimers.waterSportTimer == gBattleTurnCounter) + { + gFieldStatuses &= ~STATUS_FIELD_WATERSPORT; + BattleScriptExecute(BattleScript_WaterSportEnds); + effect = TRUE; + } + + return effect; +} + +static bool32 HandleEndTurnMudSport(u32 battler) +{ + bool32 effect = FALSE; + + gBattleStruct->endTurnEventsCounter++; + + if (gFieldStatuses & STATUS_FIELD_MUDSPORT && gFieldTimers.mudSportTimer == gBattleTurnCounter) + { + gFieldStatuses &= ~STATUS_FIELD_MUDSPORT; + BattleScriptExecute(BattleScript_MudSportEnds); + effect = TRUE; + } + + return effect; +} + +static bool32 HandleEndTurnWonderRoom(u32 battler) +{ + bool32 effect = FALSE; + + gBattleStruct->endTurnEventsCounter++; + + if (gFieldStatuses & STATUS_FIELD_WONDER_ROOM && gFieldTimers.wonderRoomTimer == gBattleTurnCounter) + { + gFieldStatuses &= ~STATUS_FIELD_WONDER_ROOM; + BattleScriptExecute(BattleScript_WonderRoomEnds); + effect = TRUE; + } + + return effect; +} + +static bool32 HandleEndTurnMagicRoom(u32 battler) +{ + bool32 effect = FALSE; + + gBattleStruct->endTurnEventsCounter++; + + if (gFieldStatuses & STATUS_FIELD_MAGIC_ROOM && gFieldTimers.magicRoomTimer == gBattleTurnCounter) + { + gFieldStatuses &= ~STATUS_FIELD_MAGIC_ROOM; + BattleScriptExecute(BattleScript_MagicRoomEnds); + effect = TRUE; + } + + return effect; +} + +static bool32 EndTurnTerrain(u32 terrainFlag, u32 stringTableId) +{ + if (gFieldStatuses & terrainFlag && gFieldTimers.terrainTimer == gBattleTurnCounter) + { + gFieldStatuses &= ~terrainFlag; + TryToRevertMimicryAndFlags(); + gBattleCommunication[MULTISTRING_CHOOSER] = stringTableId; + BattleScriptExecute(BattleScript_TerrainEnds); + return TRUE; + } + + return FALSE; +} + +static bool32 HandleEndTurnTerrain(u32 battler) +{ + bool32 effect = FALSE; + + gBattleStruct->endTurnEventsCounter++; + + if (gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN) + effect = EndTurnTerrain(STATUS_FIELD_ELECTRIC_TERRAIN, B_MSG_TERRAIN_END_ELECTRIC); + else if (gFieldStatuses & STATUS_FIELD_MISTY_TERRAIN) + effect = EndTurnTerrain(STATUS_FIELD_MISTY_TERRAIN, B_MSG_TERRAIN_END_MISTY); + else if (gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN) + effect = EndTurnTerrain(STATUS_FIELD_GRASSY_TERRAIN, B_MSG_TERRAIN_END_GRASSY); + else if (gFieldStatuses & STATUS_FIELD_PSYCHIC_TERRAIN) + effect = EndTurnTerrain(STATUS_FIELD_PSYCHIC_TERRAIN, B_MSG_TERRAIN_END_PSYCHIC); + + return effect; +} + +static bool32 HandleEndTurnThirdEventBlock(u32 battler) +{ + bool32 effect = FALSE; + + if (!IsBattlerAlive(battler)) + { + gBattleStruct->turnEffectsBattlerId++; + return effect; + } + + switch (gBattleStruct->eventBlockCounter) + { + case THIRD_EVENT_BLOCK_UPROAR: + if (gBattleMons[battler].status2 & STATUS2_UPROAR) + { + for (gBattlerAttacker = 0; gBattlerAttacker < gBattlersCount; gBattlerAttacker++) + { + if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_SLEEP) + && GetBattlerAbility(gBattlerAttacker) != ABILITY_SOUNDPROOF) + { + gBattleMons[gBattlerAttacker].status1 &= ~STATUS1_SLEEP; + gBattleMons[gBattlerAttacker].status2 &= ~STATUS2_NIGHTMARE; + gBattleCommunication[MULTISTRING_CHOOSER] = 1; + BattleScriptExecute(BattleScript_MonWokeUpInUproar); + BtlController_EmitSetMonData(gBattlerAttacker, BUFFER_A, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[gBattlerAttacker].status1); + MarkBattlerForControllerExec(gBattlerAttacker); + break; + } + } + if (gBattlerAttacker != gBattlersCount) + { + break; + } + else + { + gBattlerAttacker = battler; + gBattleMons[battler].status2 -= STATUS2_UPROAR_TURN(1); // uproar timer goes down + if (WasUnableToUseMove(battler)) + { + CancelMultiTurnMoves(battler); + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_UPROAR_ENDS; + } + else if (gBattleMons[battler].status2 & STATUS2_UPROAR) + { + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_UPROAR_CONTINUES; + gBattleMons[battler].status2 |= STATUS2_MULTIPLETURNS; + } + else + { + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_UPROAR_ENDS; + CancelMultiTurnMoves(battler); + } + BattleScriptExecute(BattleScript_PrintUproarOverTurns); + effect = TRUE; + } + } + gBattleStruct->eventBlockCounter++; + break; + case THIRD_EVENT_BLOCK_ABILITIES: + { + u32 ability = GetBattlerAbility(battler); + switch (ability) + { + case ABILITY_TRUANT: // Not fully accurate but it has to be handled somehow. TODO: Find a better way. + case ABILITY_CUD_CHEW: + case ABILITY_SLOW_START: + case ABILITY_BAD_DREAMS: + case ABILITY_BALL_FETCH: + case ABILITY_HARVEST: + case ABILITY_MOODY: + case ABILITY_PICKUP: + case ABILITY_SPEED_BOOST: + if (AbilityBattleEffects(ABILITYEFFECT_ENDTURN, battler, ability, 0, MOVE_NONE)) + effect = TRUE; + break; + } + gBattleStruct->eventBlockCounter++; + break; + } + case THIRD_EVENT_BLOCK_ITEMS: + { + u32 holdEffect = GetBattlerHoldEffect(battler, TRUE); + switch (holdEffect) + { + case HOLD_EFFECT_FLAME_ORB: + case HOLD_EFFECT_STICKY_BARB: + case HOLD_EFFECT_TOXIC_ORB: + if (ItemBattleEffects(ITEMEFFECT_ORBS, battler, FALSE)) + effect = TRUE; + break; + case HOLD_EFFECT_RESTORE_STATS: + if (ItemBattleEffects(ITEMEFFECT_NORMAL, battler, FALSE)) + effect = TRUE; + break; + } + gBattleStruct->eventBlockCounter = 0; + gBattleStruct->turnEffectsBattlerId++; + break; + } + } + + return effect; +} + +static bool32 HandleEndTurnAbilities(u32 battler) +{ + bool32 effect = FALSE; + + u32 ability = GetBattlerAbility(battler); + + gBattleStruct->turnEffectsBattlerId++; + + switch (ability) + { + case ABILITY_POWER_CONSTRUCT: + case ABILITY_SCHOOLING: + case ABILITY_SHIELDS_DOWN: + case ABILITY_ZEN_MODE: + if (AbilityBattleEffects(ABILITYEFFECT_ENDTURN, battler, ability, 0, MOVE_NONE)) + effect = TRUE; + } + + return effect; +} + +static bool32 HandleEndTurnFourthEventBlock(u32 battler) +{ + bool32 effect = FALSE; + + switch (gBattleStruct->eventBlockCounter) + { + case FOURTH_EVENT_BLOCK_HUNGER_SWITCH: + { + u32 ability = GetBattlerAbility(battler); + if (ability == ABILITY_HUNGER_SWITCH) + { + if (AbilityBattleEffects(ABILITYEFFECT_ENDTURN, battler, ability, 0, MOVE_NONE)) + effect = TRUE; + } + gBattleStruct->eventBlockCounter++; + break; + } + case FOURTH_EVENT_BLOCK_EJECT_PACK: + { + u32 holdEffect = GetBattlerHoldEffect(battler, TRUE); + if (holdEffect == HOLD_EFFECT_EJECT_PACK) + { + if (ItemBattleEffects(ITEMEFFECT_NORMAL, battler, FALSE)) + effect = TRUE; + } + gBattleStruct->eventBlockCounter = 0; + gBattleStruct->turnEffectsBattlerId++; + break; + } + } + + return effect; +} + +static bool32 HandleEndTurnDynamax(u32 battler) +{ + bool32 effect = FALSE; + + gBattleStruct->turnEffectsBattlerId++; + + if (GetActiveGimmick(battler) == GIMMICK_DYNAMAX && gBattleStruct->dynamax.dynamaxTurns[battler] == gBattleTurnCounter) + { + gBattleScripting.battler = battler; + UndoDynamax(battler); + BattleScriptExecute(BattleScript_DynamaxEnds); + effect = TRUE; + } + + return effect; +} + +static bool32 (*const sEndTurnEffectHandlers[])(u32 battler) = +{ + [ENDTURN_ORDER] = HandleEndTurnOrder, + [ENDTURN_VARIOUS] = HandleEndTurnVarious, + [ENDTURN_WEATHER] = HandleEndTurnWeather, + [ENDTURN_WEATHER_DAMAGE] = HandleEndTurnWeatherDamage, + [ENDTURN_EMERGENCY_EXIT_1] = HandleEndTurnEmergencyExit, + [ENDTURN_AFFECTION] = HandleEndTurnAffection, + [ENDTURN_FUTURE_SIGHT] = HandleEndTurnFutureSight, + [ENDTURN_WISH] = HandleEndTurnWish, + [ENDTURN_FIRST_EVENT_BLOCK] = HandleEndTurnFirstEventBlock, + [ENDTURN_EMERGENCY_EXIT_2] = HandleEndTurnEmergencyExit, + [ENDTURN_AQUA_RING] = HandleEndTurnAquaRing, + [ENDTURN_INGRAIN] = HandleEndTurnIngrain, + [ENDTURN_LEECH_SEED] = HandleEndTurnLeechSeed, + [ENDTURN_POISON] = HandleEndTurnPoison, + [ENDTURN_BURN] = HandleEndTurnBurn, + [ENDTURN_FROSTBITE] = HandleEndTurnFrostbite, + [ENDTURN_NIGHTMARE] = HandleEndTurnNightmare, + [ENDTURN_CURSE] = HandleEndTurnCurse, + [ENDTURN_WRAP] = HandleEndTurnWrap, + [ENDTURN_SALT_CURE] = HandleEndTurnSaltCure, + [ENDTURN_OCTOLOCK] = HandleEndTurnOctolock, + [ENDTURN_SYRUP_BOMB] = HandleEndTurnSyrupBomb, + [ENDTURN_TAUNT] = HandleEndTurnTaunt, + [ENDTURN_TORMENT] = HandleEndTurnTorment, + [ENDTURN_ENCORE] = HandleEndTurnEncore, + [ENDTURN_DISABLE] = HandleEndTurnDisable, + [ENDTURN_MAGNET_RISE] = HandleEndTurnMagnetRise, + [ENDTURN_TELEKINESIS] = HandleEndTurnTelekinesis, + [ENDTURN_HEAL_BLOCK] = HandleEndTurnHealBlock, + [ENDTURN_EMBARGO] = HandleEndTurnEmbargo, + [ENDTURN_YAWN] = HandleEndTurnYawn, + [ENDTURN_PERISH_SONG] = HandleEndTurnPerishSong, + [ENDTURN_ROOST] = HandleEndTurnRoost, + [ENDTURN_EMERGENCY_EXIT_3] = HandleEndTurnEmergencyExit, + [ENDTURN_SECOND_EVENT_BLOCK] = HandleEndTurnSecondEventBlock, + [ENDTURN_TRICK_ROOM] = HandleEndTurnTrickRoom, + [ENDTURN_GRAVITY] = HandleEndTurnGravity, + [ENDTURN_WATER_SPORT] = HandleEndTurnWaterSport, + [ENDTURN_MUD_SPORT] = HandleEndTurnMudSport, + [ENDTURN_WONDER_ROOM] = HandleEndTurnWonderRoom, + [ENDTURN_MAGIC_ROOM] = HandleEndTurnMagicRoom, + [ENDTURN_TERRAIN] = HandleEndTurnTerrain, + [ENDTURN_THIRD_EVENT_BLOCK] = HandleEndTurnThirdEventBlock, + [ENDTURN_EMERGENCY_EXIT_4] = HandleEndTurnEmergencyExit, + [ENDTURN_ABILITIES] = HandleEndTurnAbilities, + [ENDTURN_FOURTH_EVENT_BLOCK] = HandleEndTurnFourthEventBlock, + [ENDTURN_DYNAMAX] = HandleEndTurnDynamax, +}; + +u32 DoEndTurnEffects(void) +{ + u32 battler = MAX_BATTLERS_COUNT; + gHitMarker |= (HITMARKER_GRUDGE | HITMARKER_IGNORE_BIDE); + + for (;;) + { + // If either turnEffectsBattlerId or turnSideTracker are at max count, reest values and go to the next state + if (gBattleStruct->turnEffectsBattlerId == gBattlersCount || gBattleStruct->turnSideTracker == NUM_BATTLE_SIDES) + { + gBattleStruct->turnEffectsBattlerId = 0; + gBattleStruct->turnSideTracker = 0; + gBattleStruct->eventBlockCounter = 0; + gBattleStruct->endTurnEventsCounter++; + } + + // Jump out if possible after endTurnEventsCounter was increased in the above code block + if (gBattleStruct->endTurnEventsCounter == ENDTURN_COUNT) + { + gHitMarker &= ~(HITMARKER_GRUDGE | HITMARKER_IGNORE_BIDE); + return FALSE; + } + + battler = gBattlerAttacker = gBattlerByTurnOrder[gBattleStruct->turnEffectsBattlerId]; + + if (sEndTurnEffectHandlers[gBattleStruct->endTurnEventsCounter](battler)) + return TRUE; + } +} diff --git a/src/battle_main.c b/src/battle_main.c index bbb164332b..6d7a5334b7 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -5,6 +5,7 @@ #include "battle_ai_util.h" #include "battle_arena.h" #include "battle_controllers.h" +#include "battle_end_turn.h" #include "battle_interface.h" #include "battle_main.h" #include "battle_message.h" @@ -3878,13 +3879,11 @@ static void TryDoEventsBeforeFirstTurn(void) gBattleStruct->appearedInBattle |= 1u << gBattlerPartyIndexes[i]; } - *(&gBattleStruct->turnEffectsTracker) = 0; + *(&gBattleStruct->eventBlockCounter) = 0; *(&gBattleStruct->turnEffectsBattlerId) = 0; - *(&gBattleStruct->wishPerishSongState) = 0; - *(&gBattleStruct->wishPerishSongBattlerId) = 0; gBattleScripting.moveendState = 0; gBattleStruct->faintedActionsState = 0; - gBattleStruct->turnCountersTracker = 0; + gBattleStruct->endTurnEventsCounter = 0; memset(gQueuedStatBoosts, 0, sizeof(gQueuedStatBoosts)); SetShellSideArmCategory(); @@ -3918,11 +3917,9 @@ static void HandleEndTurn_ContinueBattle(void) if ((gBattleMons[i].status1 & STATUS1_SLEEP) && (gBattleMons[i].status2 & STATUS2_MULTIPLETURNS)) CancelMultiTurnMoves(i); } - gBattleStruct->turnEffectsTracker = 0; + gBattleStruct->eventBlockCounter = 0; gBattleStruct->turnEffectsBattlerId = 0; - gBattleStruct->wishPerishSongState = 0; - gBattleStruct->wishPerishSongBattlerId = 0; - gBattleStruct->turnCountersTracker = 0; + gBattleStruct->endTurnEventsCounter = 0; } } @@ -3933,18 +3930,14 @@ void BattleTurnPassed(void) gBattleStruct->speedTieBreaks = RandomUniform(RNG_SPEED_TIE, 0, Factorial(MAX_BATTLERS_COUNT) - 1); TurnValuesCleanUp(TRUE); - if (gBattleOutcome == 0) - { - if (DoFieldEndTurnEffects()) - return; - if (DoBattlerEndTurnEffects()) - return; - if (HandleWishPerishSongOnTurnEnd()) - return; - } + if (gBattleOutcome == 0 && DoEndTurnEffects()) + return; + if (BattleArenaTurnEnd()) + return; if (HandleFaintedMonActions()) return; + gBattleStruct->faintedActionsState = 0; TurnValuesCleanUp(FALSE); @@ -3979,6 +3972,8 @@ void BattleTurnPassed(void) gBattleStruct->battlerState[i].absent = (gAbsentBattlerFlags & (1u << i) ? TRUE : FALSE); gBattleStruct->monToSwitchIntoId[i] = PARTY_SIZE; gStatuses4[i] &= ~STATUS4_ELECTRIFIED; + gBattleMons[i].status2 &= ~STATUS2_FLINCHED; + gBattleMons[i].status2 &= ~STATUS2_POWDER; } for (i = 0; i < NUM_BATTLE_SIDES; i++) @@ -3987,6 +3982,8 @@ void BattleTurnPassed(void) gSideTimers[i].retaliateTimer--; } + gFieldStatuses &= ~STATUS_FIELD_ION_DELUGE; + BattlePutTextOnWindow(gText_EmptyString3, B_WIN_MSG); AssignUsableGimmicks(); SetShellSideArmCategory(); diff --git a/src/battle_message.c b/src/battle_message.c index 11f2519bc4..9f1b85514d 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -962,7 +962,8 @@ const u16 gTerrainPreventsStringIds[] = const u16 gHealingWishStringIds[] = { - STRINGID_HEALINGWISHCAMETRUE, STRINGID_LUNARDANCECAMETRUE + STRINGID_HEALINGWISHCAMETRUE, + STRINGID_LUNARDANCECAMETRUE }; const u16 gDmgHazardsStringIds[] = diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 4dd66975b6..3256b472a4 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -533,7 +533,7 @@ static void Cmd_presentdamagecalculation(void); static void Cmd_setsafeguard(void); static void Cmd_magnitudedamagecalculation(void); static void Cmd_jumpifnopursuitswitchdmg(void); -static void Cmd_unused_bb(void); +static void Cmd_tryhealingitem(void); static void Cmd_halvehp(void); static void Cmd_copyfoestats(void); static void Cmd_rapidspinfree(void); @@ -792,7 +792,7 @@ void (* const gBattleScriptingCommandsTable[])(void) = Cmd_setsafeguard, //0xB8 Cmd_magnitudedamagecalculation, //0xB9 Cmd_jumpifnopursuitswitchdmg, //0xBA - Cmd_unused_bb, //0xBB + Cmd_tryhealingitem, //0xBB Cmd_halvehp, //0xBC Cmd_copyfoestats, //0xBD Cmd_rapidspinfree, //0xBE @@ -3987,7 +3987,7 @@ void SetMoveEffect(bool32 primary, bool32 certain) } break; case MOVE_EFFECT_THROAT_CHOP: - gDisableStructs[gEffectBattler].throatChopTimer = 2; + gDisableStructs[gEffectBattler].throatChopTimer = gBattleTurnCounter + 2; gBattlescriptCurrInstr++; break; case MOVE_EFFECT_INCINERATE: @@ -4194,7 +4194,7 @@ void SetMoveEffect(bool32 primary, bool32 certain) else if (!(gStatuses3[gEffectBattler] & STATUS3_HEAL_BLOCK)) { gStatuses3[gEffectBattler] |= STATUS3_HEAL_BLOCK; - gDisableStructs[gEffectBattler].healBlockTimer = 2; + gDisableStructs[gEffectBattler].healBlockTimer = gBattleTurnCounter + 2; BattleScriptPush(gBattlescriptCurrInstr + 1); gBattlescriptCurrInstr = BattleScript_EffectPsychicNoise; } @@ -7691,7 +7691,7 @@ static void Cmd_sethealblock(void) else { gStatuses3[gBattlerTarget] |= STATUS3_HEAL_BLOCK; - gDisableStructs[gBattlerTarget].healBlockTimer = 5; + gDisableStructs[gBattlerTarget].healBlockTimer = gBattleTurnCounter + 5; gBattlescriptCurrInstr = cmd->nextInstr; } } @@ -8345,6 +8345,7 @@ static bool32 DoSwitchInEffectsForBattler(u32 battler) else if ((gBattleStruct->battlerState[battler].storedHealingWish || gBattleStruct->battlerState[battler].storedLunarDance) && (gBattleMons[battler].hp != gBattleMons[battler].maxHP || gBattleMons[battler].status1 != 0 || B_HEALING_WISH_SWITCH < GEN_8)) { + gBattlerAttacker = battler; if (gBattleStruct->battlerState[battler].storedHealingWish) { BattleScriptPushCursor(); @@ -14304,7 +14305,7 @@ static void Cmd_setembargo(void) else { gStatuses3[gBattlerTarget] |= STATUS3_EMBARGO; - gDisableStructs[gBattlerTarget].embargoTimer = 5; + gDisableStructs[gBattlerTarget].embargoTimer = gBattleTurnCounter + 5; gBattlescriptCurrInstr = cmd->nextInstr; } } @@ -14480,8 +14481,18 @@ static void Cmd_jumpifnopursuitswitchdmg(void) } } -static void Cmd_unused_bb(void) +static void Cmd_tryhealingitem(void) { + CMD_ARGS(); + + if (gItemsInfo[gBattleMons[gBattlerAttacker].item].pocket == POCKET_BERRIES + || GetBattlerHoldEffect(gBattlerAttacker, TRUE) == HOLD_EFFECT_RESTORE_HP) // Edge case for Berry Juice + { + if (ItemBattleEffects(ITEMEFFECT_TRY_HEALING, gBattlerAttacker, FALSE)) + return; + } + + gBattlescriptCurrInstr = cmd->nextInstr; } // Belly Drum, Fillet Away @@ -14935,7 +14946,6 @@ static void Cmd_settorment(void) else { gBattleMons[gBattlerTarget].status2 |= STATUS2_TORMENT; - gDisableStructs[gBattlerTarget].tormentTimer = PERMANENT_TORMENT; // permanent gBattlescriptCurrInstr = cmd->nextInstr; } } @@ -15143,36 +15153,17 @@ static void Cmd_trycopyability(void) static void Cmd_trywish(void) { - CMD_ARGS(u8 turnNumber, const u8 *failInstr); + CMD_ARGS(const u8 *failInstr); - switch (cmd->turnNumber) + if (gWishFutureKnock.wishCounter[gBattlerAttacker] <= gBattleTurnCounter) { - case 0: // use wish - if (gWishFutureKnock.wishCounter[gBattlerAttacker] <= gBattleTurnCounter) - { - gWishFutureKnock.wishCounter[gBattlerAttacker] = gBattleTurnCounter + 2; - gWishFutureKnock.wishPartyId[gBattlerAttacker] = gBattlerPartyIndexes[gBattlerAttacker]; - gBattlescriptCurrInstr = cmd->nextInstr; - } - else - { - gBattlescriptCurrInstr = cmd->failInstr; - } - break; - case 1: // heal effect - PREPARE_MON_NICK_WITH_PREFIX_BUFFER(gBattleTextBuff1, gBattlerTarget, gWishFutureKnock.wishPartyId[gBattlerTarget]) - if (B_WISH_HP_SOURCE >= GEN_5) - gBattleStruct->moveDamage[gBattlerTarget] = max(1, GetMonData(&GetBattlerParty(gBattlerTarget)[gWishFutureKnock.wishPartyId[gBattlerTarget]], MON_DATA_MAX_HP) / 2); - else - gBattleStruct->moveDamage[gBattlerTarget] = max(1, GetNonDynamaxMaxHP(gBattlerAttacker) / 2); - - gBattleStruct->moveDamage[gBattlerTarget] *= -1; - if (gBattleMons[gBattlerTarget].hp == gBattleMons[gBattlerTarget].maxHP) - gBattlescriptCurrInstr = cmd->failInstr; - else - gBattlescriptCurrInstr = cmd->nextInstr; - - break; + gWishFutureKnock.wishCounter[gBattlerAttacker] = gBattleTurnCounter + 2; + gWishFutureKnock.wishPartyId[gBattlerAttacker] = gBattlerPartyIndexes[gBattlerAttacker]; + gBattlescriptCurrInstr = cmd->nextInstr; + } + else + { + gBattlescriptCurrInstr = cmd->failInstr; } } @@ -15405,9 +15396,9 @@ static void Cmd_setuserstatus3(void) { gStatuses3[gBattlerAttacker] |= flags; if (flags & STATUS3_MAGNET_RISE) - gDisableStructs[gBattlerAttacker].magnetRiseTimer = 5; + gDisableStructs[gBattlerAttacker].magnetRiseTimer = gBattleTurnCounter + 5; if (flags & STATUS3_LASER_FOCUS) - gDisableStructs[gBattlerAttacker].laserFocusTimer = 2; + gDisableStructs[gBattlerAttacker].laserFocusTimer = gBattleTurnCounter + 2; gBattlescriptCurrInstr = cmd->nextInstr; } } @@ -16647,7 +16638,7 @@ static void Cmd_settelekinesis(void) else { gStatuses3[gBattlerTarget] |= STATUS3_TELEKINESIS; - gDisableStructs[gBattlerTarget].telekinesisTimer = 3; + gDisableStructs[gBattlerTarget].telekinesisTimer = gBattleTurnCounter + 3; gBattlescriptCurrInstr = cmd->nextInstr; } } @@ -18748,7 +18739,7 @@ void BS_TrySetTorment(void) && !IsAbilityOnSide(gBattlerTarget, ABILITY_AROMA_VEIL)) { gBattleMons[gBattlerTarget].status2 |= STATUS2_TORMENT; - gDisableStructs[gBattlerTarget].tormentTimer = 3; // 3 turns excluding current turn + gDisableStructs[gBattlerTarget].tormentTimer = gBattleTurnCounter + 3; // 3 turns excluding current turn gBattleCommunication[MULTISTRING_CHOOSER] = 3; gEffectBattler = gBattlerTarget; gBattlescriptCurrInstr = cmd->nextInstr; diff --git a/src/battle_util.c b/src/battle_util.c index 59ec8fcfe2..8b5d32afef 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -100,7 +100,6 @@ static const u8 sPkblToEscapeFactor[][3] = { static const u8 sGoNearCounterToCatchFactor[] = {4, 3, 2, 1}; static const u8 sGoNearCounterToEscapeFactor[] = {4, 4, 4, 4}; - struct BattleWeatherInfo { u16 flag; @@ -203,6 +202,49 @@ static const struct BattleWeatherInfo sBattleWeatherInfo[BATTLE_WEATHER_COUNT] = }, }; +u32 GetCurrentBattleWeather(void) +{ + u32 currBattleWeather = 0xFF; + + for (u32 weather = 0; weather < ARRAY_COUNT(sBattleWeatherInfo); weather++) + { + if (gBattleWeather & sBattleWeatherInfo[weather].flag) + { + currBattleWeather = weather; + break; + } + } + + return currBattleWeather; +} + +bool32 EndOrContinueWeather(void) +{ + u32 currBattleWeather = GetCurrentBattleWeather(); + + if (currBattleWeather == 0xFF) + return FALSE; + + if (gWishFutureKnock.weatherDuration > 0 && --gWishFutureKnock.weatherDuration == 0) + { + gBattleWeather = B_WEATHER_NONE; + for (u32 battler = 0; battler < gBattlersCount; battler++) + gDisableStructs[battler].weatherAbilityDone = FALSE; + gBattleCommunication[MULTISTRING_CHOOSER] = sBattleWeatherInfo[currBattleWeather].endMessage; + BattleScriptExecute(BattleScript_WeatherFaded); + return TRUE; + } + else + { + gBattleCommunication[MULTISTRING_CHOOSER] = sBattleWeatherInfo[currBattleWeather].continuesMessage; + gBattleScripting.animArg1 = sBattleWeatherInfo[currBattleWeather].animation; + BattleScriptExecute(BattleScript_WeatherContinues); + return TRUE; + } + + return FALSE; +} + static u32 CalcBeatUpPower(void) { u32 basePower; @@ -1019,7 +1061,7 @@ const u8* CancelMultiTurnMoves(u32 battler) // If target was sky dropped in the middle of Outrage/Thrash/Petal Dance, // confuse them upon release and display "confused by fatigue" message & animation. // Don't do this if this CancelMultiTurnMoves is caused by falling asleep via Yawn. - if (gBattleMons[otherSkyDropper].status2 & STATUS2_LOCK_CONFUSE && gBattleStruct->turnEffectsTracker != 24) + if (gBattleMons[otherSkyDropper].status2 & STATUS2_LOCK_CONFUSE && gBattleStruct->eventBlockCounter != 24) { gBattleMons[otherSkyDropper].status2 &= ~(STATUS2_LOCK_CONFUSE); @@ -1292,7 +1334,7 @@ u32 TrySetCantSelectMoveBattleScript(u32 battler) } } - if (DYNAMAX_BYPASS_CHECK && GetActiveGimmick(battler) != GIMMICK_Z_MOVE && gDisableStructs[battler].throatChopTimer != 0 && IsSoundMove(move)) + if (DYNAMAX_BYPASS_CHECK && GetActiveGimmick(battler) != GIMMICK_Z_MOVE && gDisableStructs[battler].throatChopTimer > gBattleTurnCounter && IsSoundMove(move)) { gCurrentMove = move; if (gBattleTypeFlags & BATTLE_TYPE_PALACE) @@ -1532,7 +1574,7 @@ u8 CheckMoveLimitations(u32 battler, u8 unusableMoves, u16 check) else if (check & MOVE_LIMITATION_BELCH && IsBelchPreventingMove(battler, move)) unusableMoves |= 1u << i; // Throat Chop - else if (check & MOVE_LIMITATION_THROAT_CHOP && gDisableStructs[battler].throatChopTimer && IsSoundMove(move)) + else if (check & MOVE_LIMITATION_THROAT_CHOP && gDisableStructs[battler].throatChopTimer > gBattleTurnCounter && IsSoundMove(move)) unusableMoves |= 1u << i; // Stuff Cheeks else if (check & MOVE_LIMITATION_STUFF_CHEEKS && moveEffect == EFFECT_STUFF_CHEEKS && ItemId_GetPocket(gBattleMons[battler].item) != POCKET_BERRIES) @@ -1620,587 +1662,27 @@ void TryToRevertMimicryAndFlags(void) } } -enum +bool32 BattleArenaTurnEnd(void) { - ENDTURN_ORDER, - ENDTURN_REFLECT, - ENDTURN_LIGHT_SCREEN, - ENDTURN_AURORA_VEIL, - ENDTURN_MIST, - ENDTURN_LUCKY_CHANT, - ENDTURN_SAFEGUARD, - ENDTURN_TAILWIND, - ENDTURN_WISH, - ENDTURN_WEATHER, - ENDTURN_DAMAGE_NON_TYPES, - ENDTURN_GRAVITY, - ENDTURN_WATER_SPORT, - ENDTURN_MUD_SPORT, - ENDTURN_TRICK_ROOM, - ENDTURN_WONDER_ROOM, - ENDTURN_MAGIC_ROOM, - ENDTURN_ELECTRIC_TERRAIN, - ENDTURN_MISTY_TERRAIN, - ENDTURN_GRASSY_TERRAIN, - ENDTURN_PSYCHIC_TERRAIN, - ENDTURN_ION_DELUGE, - ENDTURN_FAIRY_LOCK, - ENDTURN_STATUS_HEAL, - ENDTURN_RAINBOW, - ENDTURN_SEA_OF_FIRE, - ENDTURN_SWAMP, - ENDTURN_FIELD_COUNT, -}; + gHitMarker |= (HITMARKER_GRUDGE | HITMARKER_IGNORE_BIDE); -static bool32 EndTurnTerrain(u32 terrainFlag, u32 stringTableId) -{ - if (gFieldStatuses & terrainFlag) + if ((gBattleTypeFlags & BATTLE_TYPE_ARENA) + && gBattleStruct->arenaTurnCounter == 2 + && IsBattlerAlive(B_POSITION_PLAYER_LEFT) && IsBattlerAlive(B_POSITION_OPPONENT_LEFT)) { - if (terrainFlag & STATUS_FIELD_GRASSY_TERRAIN) - BattleScriptExecute(BattleScript_GrassyTerrainHeals); - if (gFieldTimers.terrainTimer == gBattleTurnCounter) - { - gFieldStatuses &= ~terrainFlag; - TryToRevertMimicryAndFlags(); - gBattleCommunication[MULTISTRING_CHOOSER] = stringTableId; - if (terrainFlag & STATUS_FIELD_GRASSY_TERRAIN) - BattleScriptExecute(BattleScript_GrassyTerrainEnds); - else - BattleScriptExecute(BattleScript_TerrainEnds); - return TRUE; - } + for (u32 battler = 0; battler < 2; battler++) + CancelMultiTurnMoves(battler); + + gBattlescriptCurrInstr = BattleScript_ArenaDoJudgment; + BattleScriptExecute(BattleScript_ArenaDoJudgment); + return TRUE; } + + gHitMarker &= ~(HITMARKER_GRUDGE | HITMARKER_IGNORE_BIDE); + return FALSE; } -static bool32 TryEndTurnWeather(void) -{ - u32 i = 0; - u32 effect = 0; - u32 currBattleWeather = 0xFF; - - for (i = 0; i < ARRAY_COUNT(sBattleWeatherInfo); i++) - { - if (gBattleWeather & sBattleWeatherInfo[i].flag) - { - currBattleWeather = i; - break; - } - } - - if (currBattleWeather == 0xFF) - return effect; - - if (gWishFutureKnock.weatherDuration > 0 && --gWishFutureKnock.weatherDuration == 0) - { - gBattleWeather = B_WEATHER_NONE; - for (i = 0; i < gBattlersCount; i++) - gDisableStructs[i].weatherAbilityDone = FALSE; - gBattleCommunication[MULTISTRING_CHOOSER] = sBattleWeatherInfo[currBattleWeather].endMessage; - BattleScriptExecute(BattleScript_WeatherFaded); - effect++; - } - else - { - gBattleCommunication[MULTISTRING_CHOOSER] = sBattleWeatherInfo[currBattleWeather].continuesMessage; - gBattleScripting.animArg1 = sBattleWeatherInfo[currBattleWeather].animation; - BattleScriptExecute(BattleScript_WeatherContinues); - effect++; - } - - return effect; -} - -u8 DoFieldEndTurnEffects(void) -{ - u8 effect = 0; - - for (gBattlerAttacker = 0; gBattlerAttacker < gBattlersCount && gAbsentBattlerFlags & (1u << gBattlerAttacker); gBattlerAttacker++) - { - } - for (gBattlerTarget = 0; gBattlerTarget < gBattlersCount && gAbsentBattlerFlags & (1u << gBattlerTarget); gBattlerTarget++) - { - } - - do - { - s32 i; - u32 side; - - switch (gBattleStruct->turnCountersTracker) - { - case ENDTURN_ORDER: - for (i = 0; i < gBattlersCount - 1; i++) - { - s32 j; - for (j = i + 1; j < gBattlersCount; j++) - { - if (!(gProtectStructs[i].quash && gProtectStructs[j].quash) - && GetWhichBattlerFaster(gBattlerByTurnOrder[i], gBattlerByTurnOrder[j], FALSE) == -1) - SwapTurnOrder(i, j); - } - } - gBattleTurnCounter++; - gBattleStruct->turnCountersTracker++; - gBattleStruct->turnSideTracker = 0; - // fall through - case ENDTURN_REFLECT: - while (gBattleStruct->turnSideTracker < 2) - { - side = gBattleStruct->turnSideTracker; - gBattlerAttacker = gSideTimers[side].reflectBattlerId; - if (gSideStatuses[side] & SIDE_STATUS_REFLECT) - { - if (gSideTimers[side].reflectTimer == gBattleTurnCounter) - { - gSideStatuses[side] &= ~SIDE_STATUS_REFLECT; - BattleScriptExecute(BattleScript_SideStatusWoreOff); - PREPARE_MOVE_BUFFER(gBattleTextBuff1, MOVE_REFLECT); - effect++; - } - } - gBattleStruct->turnSideTracker++; - if (effect != 0) - break; - } - if (effect == 0) - { - gBattleStruct->turnCountersTracker++; - gBattleStruct->turnSideTracker = 0; - } - break; - case ENDTURN_LIGHT_SCREEN: - while (gBattleStruct->turnSideTracker < 2) - { - side = gBattleStruct->turnSideTracker; - gBattlerAttacker = gSideTimers[side].lightscreenBattlerId; - if (gSideStatuses[side] & SIDE_STATUS_LIGHTSCREEN) - { - if (gSideTimers[side].lightscreenTimer == gBattleTurnCounter) - { - gSideStatuses[side] &= ~SIDE_STATUS_LIGHTSCREEN; - BattleScriptExecute(BattleScript_SideStatusWoreOff); - gBattleCommunication[MULTISTRING_CHOOSER] = side; - PREPARE_MOVE_BUFFER(gBattleTextBuff1, MOVE_LIGHT_SCREEN); - effect++; - } - } - gBattleStruct->turnSideTracker++; - if (effect != 0) - break; - } - if (effect == 0) - { - gBattleStruct->turnCountersTracker++; - gBattleStruct->turnSideTracker = 0; - } - break; - case ENDTURN_AURORA_VEIL: - while (gBattleStruct->turnSideTracker < 2) - { - side = gBattleStruct->turnSideTracker; - gBattlerAttacker = gSideTimers[side].auroraVeilBattlerId; - if (gSideStatuses[side] & SIDE_STATUS_AURORA_VEIL) - { - if (gSideTimers[side].auroraVeilTimer == gBattleTurnCounter) - { - gSideStatuses[side] &= ~SIDE_STATUS_AURORA_VEIL; - BattleScriptExecute(BattleScript_SideStatusWoreOff); - gBattleCommunication[MULTISTRING_CHOOSER] = side; - PREPARE_MOVE_BUFFER(gBattleTextBuff1, MOVE_AURORA_VEIL); - effect++; - } - } - gBattleStruct->turnSideTracker++; - if (effect != 0) - break; - } - if (!effect) - { - gBattleStruct->turnCountersTracker++; - gBattleStruct->turnSideTracker = 0; - } - break; - case ENDTURN_MIST: - while (gBattleStruct->turnSideTracker < 2) - { - side = gBattleStruct->turnSideTracker; - gBattlerAttacker = gSideTimers[side].mistBattlerId; - if (gSideTimers[side].mistTimer == gBattleTurnCounter) - { - gSideStatuses[side] &= ~SIDE_STATUS_MIST; - BattleScriptExecute(BattleScript_SideStatusWoreOff); - gBattleCommunication[MULTISTRING_CHOOSER] = side; - PREPARE_MOVE_BUFFER(gBattleTextBuff1, MOVE_MIST); - effect++; - } - gBattleStruct->turnSideTracker++; - if (effect != 0) - break; - } - if (effect == 0) - { - gBattleStruct->turnCountersTracker++; - gBattleStruct->turnSideTracker = 0; - } - break; - case ENDTURN_SAFEGUARD: - while (gBattleStruct->turnSideTracker < 2) - { - side = gBattleStruct->turnSideTracker; - gBattlerAttacker = gSideTimers[side].safeguardBattlerId; - if (gSideStatuses[side] & SIDE_STATUS_SAFEGUARD) - { - if (gSideTimers[side].safeguardTimer == gBattleTurnCounter) - { - gSideStatuses[side] &= ~SIDE_STATUS_SAFEGUARD; - BattleScriptExecute(BattleScript_SafeguardEnds); - effect++; - } - } - gBattleStruct->turnSideTracker++; - if (effect != 0) - break; - } - if (effect == 0) - { - gBattleStruct->turnCountersTracker++; - gBattleStruct->turnSideTracker = 0; - } - break; - case ENDTURN_LUCKY_CHANT: - while (gBattleStruct->turnSideTracker < 2) - { - side = gBattleStruct->turnSideTracker; - gBattlerAttacker = gSideTimers[side].luckyChantBattlerId; - if (gSideStatuses[side] & SIDE_STATUS_LUCKY_CHANT) - { - if (gSideTimers[side].luckyChantTimer == gBattleTurnCounter) - { - gSideStatuses[side] &= ~SIDE_STATUS_LUCKY_CHANT; - BattleScriptExecute(BattleScript_LuckyChantEnds); - effect++; - } - } - gBattleStruct->turnSideTracker++; - if (effect != 0) - break; - } - if (!effect) - { - gBattleStruct->turnCountersTracker++; - gBattleStruct->turnSideTracker = 0; - } - break; - case ENDTURN_TAILWIND: - while (gBattleStruct->turnSideTracker < 2) - { - side = gBattleStruct->turnSideTracker; - gBattlerAttacker = gSideTimers[side].tailwindBattlerId; - if (gSideStatuses[side] & SIDE_STATUS_TAILWIND) - { - if (gSideTimers[side].tailwindTimer == gBattleTurnCounter) - { - gSideStatuses[side] &= ~SIDE_STATUS_TAILWIND; - BattleScriptExecute(BattleScript_TailwindEnds); - effect++; - } - } - gBattleStruct->turnSideTracker++; - if (effect != 0) - break; - } - if (!effect) - { - gBattleStruct->turnCountersTracker++; - gBattleStruct->turnSideTracker = 0; - } - break; - case ENDTURN_WISH: - while (gBattleStruct->turnSideTracker < gBattlersCount) - { - u32 battler = gBattlerByTurnOrder[gBattleStruct->turnSideTracker]; - if (gWishFutureKnock.wishCounter[battler] == gBattleTurnCounter && IsBattlerAlive(battler)) - { - gBattlerTarget = battler; - BattleScriptExecute(BattleScript_WishComesTrue); - effect++; - } - gBattleStruct->turnSideTracker++; - if (effect != 0) - break; - } - if (effect == 0) - { - gBattleStruct->turnCountersTracker++; - gBattleStruct->turnSideTracker = 0; - } - break; - case ENDTURN_WEATHER: - effect = TryEndTurnWeather(); - gBattleStruct->turnCountersTracker++; - break; - case ENDTURN_DAMAGE_NON_TYPES: - while (gBattleStruct->turnSideTracker < 2) - { - side = gBattleStruct->turnSideTracker; - if (gSideStatuses[side] & SIDE_STATUS_DAMAGE_NON_TYPES && gSideTimers[side].damageNonTypesTimer == gBattleTurnCounter) - { - // There is no in-game message when this side status ends. - gSideStatuses[side] &= ~SIDE_STATUS_DAMAGE_NON_TYPES; - effect++; - } - gBattleStruct->turnSideTracker++; - if (effect != 0) - break; - } - if (!effect) - { - gBattleStruct->turnCountersTracker++; - gBattleStruct->turnSideTracker = 0; - } - break; - case ENDTURN_TRICK_ROOM: - if (gFieldStatuses & STATUS_FIELD_TRICK_ROOM && gFieldTimers.trickRoomTimer == gBattleTurnCounter) - { - gFieldStatuses &= ~STATUS_FIELD_TRICK_ROOM; - BattleScriptExecute(BattleScript_TrickRoomEnds); - effect++; - } - gBattleStruct->turnCountersTracker++; - break; - case ENDTURN_WONDER_ROOM: - if (gFieldStatuses & STATUS_FIELD_WONDER_ROOM && gFieldTimers.wonderRoomTimer == gBattleTurnCounter) - { - gFieldStatuses &= ~STATUS_FIELD_WONDER_ROOM; - BattleScriptExecute(BattleScript_WonderRoomEnds); - effect++; - } - gBattleStruct->turnCountersTracker++; - break; - case ENDTURN_MAGIC_ROOM: - if (gFieldStatuses & STATUS_FIELD_MAGIC_ROOM && gFieldTimers.magicRoomTimer == gBattleTurnCounter) - { - gFieldStatuses &= ~STATUS_FIELD_MAGIC_ROOM; - BattleScriptExecute(BattleScript_MagicRoomEnds); - effect++; - } - gBattleStruct->turnCountersTracker++; - break; - case ENDTURN_ELECTRIC_TERRAIN: - effect = EndTurnTerrain(STATUS_FIELD_ELECTRIC_TERRAIN, B_MSG_TERRAIN_END_ELECTRIC); - gBattleStruct->turnCountersTracker++; - break; - case ENDTURN_MISTY_TERRAIN: - effect = EndTurnTerrain(STATUS_FIELD_MISTY_TERRAIN, B_MSG_TERRAIN_END_MISTY); - gBattleStruct->turnCountersTracker++; - break; - case ENDTURN_GRASSY_TERRAIN: - effect = EndTurnTerrain(STATUS_FIELD_GRASSY_TERRAIN, B_MSG_TERRAIN_END_GRASSY); - gBattleStruct->turnCountersTracker++; - break; - case ENDTURN_PSYCHIC_TERRAIN: - effect = EndTurnTerrain(STATUS_FIELD_PSYCHIC_TERRAIN, B_MSG_TERRAIN_END_PSYCHIC); - gBattleStruct->turnCountersTracker++; - break; - case ENDTURN_WATER_SPORT: - if (gFieldStatuses & STATUS_FIELD_WATERSPORT && gFieldTimers.waterSportTimer == gBattleTurnCounter) - { - gFieldStatuses &= ~STATUS_FIELD_WATERSPORT; - BattleScriptExecute(BattleScript_WaterSportEnds); - effect++; - } - gBattleStruct->turnCountersTracker++; - break; - case ENDTURN_MUD_SPORT: - if (gFieldStatuses & STATUS_FIELD_MUDSPORT && gFieldTimers.mudSportTimer == gBattleTurnCounter) - { - gFieldStatuses &= ~STATUS_FIELD_MUDSPORT; - BattleScriptExecute(BattleScript_MudSportEnds); - effect++; - } - gBattleStruct->turnCountersTracker++; - break; - case ENDTURN_GRAVITY: - if (gFieldStatuses & STATUS_FIELD_GRAVITY && gFieldTimers.gravityTimer == gBattleTurnCounter) - { - gFieldStatuses &= ~STATUS_FIELD_GRAVITY; - BattleScriptExecute(BattleScript_GravityEnds); - effect++; - } - gBattleStruct->turnCountersTracker++; - break; - case ENDTURN_ION_DELUGE: - gFieldStatuses &= ~STATUS_FIELD_ION_DELUGE; - gBattleStruct->turnCountersTracker++; - break; - case ENDTURN_FAIRY_LOCK: - if (gFieldStatuses & STATUS_FIELD_FAIRY_LOCK && gFieldTimers.fairyLockTimer == gBattleTurnCounter) - { - gFieldStatuses &= ~STATUS_FIELD_FAIRY_LOCK; - } - gBattleStruct->turnCountersTracker++; - break; - case ENDTURN_STATUS_HEAL: - for (gBattlerAttacker = 0; gBattlerAttacker < gBattlersCount; gBattlerAttacker++) - { - if (B_AFFECTION_MECHANICS == TRUE - && GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER - && GetBattlerAffectionHearts(gBattlerAttacker) >= AFFECTION_FOUR_HEARTS - && (Random() % 100 < 20)) - { - gBattleCommunication[MULTISTRING_CHOOSER] = 1; - BattleScriptExecute(BattleScript_AffectionBasedStatusHeal); - break; - } - } - gBattleStruct->turnCountersTracker++; - break; - case ENDTURN_RAINBOW: - while (gBattleStruct->turnSideTracker < 2) - { - side = gBattleStruct->turnSideTracker; - if (gSideStatuses[side] & SIDE_STATUS_RAINBOW) - { - for (gBattlerAttacker = 0; gBattlerAttacker < gBattlersCount; gBattlerAttacker++) - { - if (GetBattlerSide(gBattlerAttacker) == side) - break; - } - - if (gSideTimers[side].rainbowTimer == gBattleTurnCounter) - { - gSideStatuses[side] &= ~SIDE_STATUS_RAINBOW; - BattleScriptExecute(BattleScript_TheRainbowDisappeared); - effect++; - } - } - gBattleStruct->turnSideTracker++; - if (effect != 0) - break; - } - if (!effect) - { - gBattleStruct->turnCountersTracker++; - gBattleStruct->turnSideTracker = 0; - } - break; - case ENDTURN_SEA_OF_FIRE: - while (gBattleStruct->turnSideTracker < 2) - { - side = gBattleStruct->turnSideTracker; - - if (gSideStatuses[side] & SIDE_STATUS_SEA_OF_FIRE) - { - for (gBattlerAttacker = 0; gBattlerAttacker < gBattlersCount; gBattlerAttacker++) - { - if (GetBattlerSide(gBattlerAttacker) == side) - break; - } - - if (gSideTimers[side].seaOfFireTimer == gBattleTurnCounter) - { - gSideStatuses[side] &= ~SIDE_STATUS_SEA_OF_FIRE; - BattleScriptExecute(BattleScript_TheSeaOfFireDisappeared); - effect++; - } - } - gBattleStruct->turnSideTracker++; - if (effect != 0) - break; - } - if (!effect) - { - gBattleStruct->turnCountersTracker++; - gBattleStruct->turnSideTracker = 0; - } - break; - case ENDTURN_SWAMP: - while (gBattleStruct->turnSideTracker < 2) - { - side = gBattleStruct->turnSideTracker; - - if (gSideStatuses[side] & SIDE_STATUS_SWAMP) - { - for (gBattlerAttacker = 0; gBattlerAttacker < gBattlersCount; gBattlerAttacker++) - { - if (GetBattlerSide(gBattlerAttacker) == side) - break; - } - - if (gSideTimers[side].swampTimer == gBattleTurnCounter) - { - gSideStatuses[side] &= ~SIDE_STATUS_SWAMP; - BattleScriptExecute(BattleScript_TheSwampDisappeared); - effect++; - } - } - gBattleStruct->turnSideTracker++; - if (effect != 0) - break; - } - if (!effect) - { - gBattleStruct->turnCountersTracker++; - gBattleStruct->turnSideTracker = 0; - } - break; - case ENDTURN_FIELD_COUNT: - effect++; - break; - } - } while (effect == 0); - - return (gBattleMainFunc != BattleTurnPassed); -} - -enum -{ - ENDTURN_WEATHER_DAMAGE, - ENDTURN_INGRAIN, - ENDTURN_AQUA_RING, - ENDTURN_ABILITIES, - ENDTURN_ITEMS1, - ENDTURN_LEECH_SEED, - ENDTURN_POISON, - ENDTURN_BAD_POISON, - ENDTURN_BURN, - ENDTURN_FROSTBITE, - ENDTURN_NIGHTMARES, - ENDTURN_CURSE, - ENDTURN_WRAP, - ENDTURN_OCTOLOCK, - ENDTURN_UPROAR, - ENDTURN_THRASH, - ENDTURN_FLINCH, - ENDTURN_DISABLE, - ENDTURN_ENCORE, - ENDTURN_MAGNET_RISE, - ENDTURN_TELEKINESIS, - ENDTURN_HEALBLOCK, - ENDTURN_EMBARGO, - ENDTURN_LOCK_ON, - ENDTURN_LASER_FOCUS, - ENDTURN_CHARGE, - ENDTURN_TAUNT, - ENDTURN_YAWN, - ENDTURN_ITEMS2, - ENDTURN_ORBS, - ENDTURN_ROOST, - ENDTURN_POWDER, - ENDTURN_THROAT_CHOP, - ENDTURN_SLOW_START, - ENDTURN_CUD_CHEW, - ENDTURN_TORMENT, // supposedly this goes after Taunt, before Encore, but Encore is first right now? - ENDTURN_SALT_CURE, - ENDTURN_SYRUP_BOMB, - ENDTURN_DYNAMAX, - ENDTURN_GMAX_MOVE_RESIDUAL_DAMAGE, - ENDTURN_SEA_OF_FIRE_DAMAGE, - ENDTURN_ITEMS3, - ENDTURN_BATTLER_COUNT -}; - // Ingrain, Leech Seed, Strength Sap and Aqua Ring s32 GetDrainedBigRootHp(u32 battler, s32 hp) { @@ -2222,799 +1704,7 @@ bool32 IsMagicGuardProtected(u32 battler, u32 ability) return TRUE; } -u8 DoBattlerEndTurnEffects(void) -{ - u32 battler, ability, i, effect = 0; - - gHitMarker |= (HITMARKER_GRUDGE | HITMARKER_IGNORE_BIDE); - while (gBattleStruct->turnEffectsBattlerId < gBattlersCount && gBattleStruct->turnEffectsTracker <= ENDTURN_BATTLER_COUNT) - { - battler = gBattlerAttacker = gBattlerByTurnOrder[gBattleStruct->turnEffectsBattlerId]; - if (gAbsentBattlerFlags & (1u << battler)) - { - gBattleStruct->turnEffectsBattlerId++; - continue; - } - - ability = GetBattlerAbility(battler); - switch (gBattleStruct->turnEffectsTracker) - { - case ENDTURN_WEATHER_DAMAGE: - ability = GetBattlerAbility(battler); - if (!IsBattlerAlive(battler) || !HasWeatherEffect() || ability == ABILITY_MAGIC_GUARD) - { - gBattleStruct->turnEffectsTracker++; - break; - } - else if (gBattleWeather & B_WEATHER_SANDSTORM - && ability != ABILITY_SAND_VEIL - && ability != ABILITY_SAND_FORCE - && ability != ABILITY_SAND_RUSH - && ability != ABILITY_OVERCOAT - && !IS_BATTLER_ANY_TYPE(gBattlerAttacker, TYPE_ROCK, TYPE_GROUND, TYPE_STEEL) - && !(gStatuses3[gBattlerAttacker] & (STATUS3_UNDERGROUND | STATUS3_UNDERWATER)) - && GetBattlerHoldEffect(gBattlerAttacker, TRUE) != HOLD_EFFECT_SAFETY_GOGGLES) - { - gBattleScripting.battler = battler; - gBattleStruct->moveDamage[battler] = GetNonDynamaxMaxHP(battler) / 16; - if (gBattleStruct->moveDamage[battler] == 0) - gBattleStruct->moveDamage[battler] = 1; - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SANDSTORM; - BattleScriptExecute(BattleScript_DamagingWeather); - effect++; - } - else if (gBattleWeather & (B_WEATHER_HAIL | B_WEATHER_SNOW) - && ability == ABILITY_ICE_BODY - && !(gStatuses3[battler] & (STATUS3_UNDERGROUND | STATUS3_UNDERWATER)) - && !IsBattlerAtMaxHp(battler) - && !(gStatuses3[battler] & STATUS3_HEAL_BLOCK)) - { - gBattleScripting.battler = battler; - gBattleStruct->moveDamage[battler] = -1 * max(1, GetNonDynamaxMaxHP(battler) / 16); - BattleScriptExecute(BattleScript_IceBodyHeal); - effect++; - } - else if (gBattleWeather & B_WEATHER_HAIL - && !IS_BATTLER_OF_TYPE(battler, TYPE_ICE) - && ability != ABILITY_SNOW_CLOAK - && ability != ABILITY_OVERCOAT - && ability != ABILITY_ICE_BODY - && !(gStatuses3[battler] & (STATUS3_UNDERGROUND | STATUS3_UNDERWATER)) - && GetBattlerHoldEffect(battler, TRUE) != HOLD_EFFECT_SAFETY_GOGGLES) - { - gBattleScripting.battler = battler; - gBattleStruct->moveDamage[battler] = GetNonDynamaxMaxHP(battler) / 16; - if (gBattleStruct->moveDamage[battler] == 0) - gBattleStruct->moveDamage[battler] = 1; - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_HAIL; - BattleScriptExecute(BattleScript_DamagingWeather); - effect++; - } - gBattleStruct->turnEffectsTracker++; - break; - case ENDTURN_INGRAIN: // ingrain - if ((gStatuses3[battler] & STATUS3_ROOTED) - && !IsBattlerAtMaxHp(battler) - && !(gStatuses3[battler] & STATUS3_HEAL_BLOCK) - && IsBattlerAlive(battler)) - { - gBattleStruct->moveDamage[battler] = GetDrainedBigRootHp(battler, GetNonDynamaxMaxHP(battler) / 16); - BattleScriptExecute(BattleScript_IngrainTurnHeal); - effect++; - } - gBattleStruct->turnEffectsTracker++; - break; - case ENDTURN_AQUA_RING: // aqua ring - if ((gStatuses3[battler] & STATUS3_AQUA_RING) - && !IsBattlerAtMaxHp(battler) - && !(gStatuses3[battler] & STATUS3_HEAL_BLOCK) - && IsBattlerAlive(battler)) - { - gBattleStruct->moveDamage[battler] = GetDrainedBigRootHp(battler, GetNonDynamaxMaxHP(battler) / 16); - BattleScriptExecute(BattleScript_AquaRingHeal); - effect++; - } - gBattleStruct->turnEffectsTracker++; - break; - case ENDTURN_ABILITIES: // end turn abilities - if (AbilityBattleEffects(ABILITYEFFECT_ENDTURN, battler, 0, 0, 0)) - effect++; - gBattleStruct->turnEffectsTracker++; - break; - case ENDTURN_ITEMS1: // item effects - if (ItemBattleEffects(ITEMEFFECT_NORMAL, battler, FALSE)) - effect++; - gBattleStruct->turnEffectsTracker++; - break; - case ENDTURN_ITEMS2: // item effects again - if (ItemBattleEffects(ITEMEFFECT_NORMAL, battler, TRUE)) - effect++; - gBattleStruct->turnEffectsTracker++; - break; - case ENDTURN_ITEMS3: // berry effects - if (gItemsInfo[gBattleMons[battler].item].pocket == POCKET_BERRIES - || GetBattlerHoldEffect(battler, TRUE) == HOLD_EFFECT_RESTORE_HP) // Edge case for Berry Juice - { - if (ItemBattleEffects(ITEMEFFECT_NORMAL, battler, FALSE)) - effect++; - } - gBattleStruct->turnEffectsTracker++; - break; - case ENDTURN_ORBS: - if (IsBattlerAlive(battler) && ItemBattleEffects(ITEMEFFECT_ORBS, battler, FALSE)) - effect++; - gBattleStruct->turnEffectsTracker++; - break; - case ENDTURN_LEECH_SEED: // leech seed - if ((gStatuses3[battler] & STATUS3_LEECHSEED) - && IsBattlerAlive(gStatuses3[battler] & STATUS3_LEECHSEED_BATTLER) - && IsBattlerAlive(battler) - && !IsMagicGuardProtected(battler, ability)) - { - gBattlerTarget = gStatuses3[battler] & STATUS3_LEECHSEED_BATTLER; // Notice gBattlerTarget is actually the HP receiver. - gBattlerAttacker = battler; - gBattleScripting.animArg1 = gBattlerTarget; - gBattleScripting.animArg2 = gBattlerAttacker; - gBattleStruct->moveDamage[gBattlerAttacker] = max(1, GetNonDynamaxMaxHP(battler) / 8); - gBattleStruct->moveDamage[gBattlerTarget] = GetDrainedBigRootHp(gBattlerAttacker, gBattleStruct->moveDamage[gBattlerAttacker]); - gHitMarker |= HITMARKER_IGNORE_SUBSTITUTE | HITMARKER_PASSIVE_DAMAGE; - if (GetBattlerAbility(battler) == ABILITY_LIQUID_OOZE) - { - gBattleStruct->moveDamage[gBattlerTarget] = gBattleStruct->moveDamage[gBattlerTarget] * -1; - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_LEECH_SEED_OOZE; - BattleScriptExecute(BattleScript_LeechSeedTurnDrainLiquidOoze); - } - else if (gStatuses3[gBattlerTarget] & STATUS3_HEAL_BLOCK) - { - BattleScriptExecute(BattleScript_LeechSeedTurnDrainHealBlock); - } - else - { - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_LEECH_SEED_DRAIN; - BattleScriptExecute(BattleScript_LeechSeedTurnDrainRecovery); - } - effect++; - } - gBattleStruct->turnEffectsTracker++; - break; - case ENDTURN_POISON: // poison - if ((gBattleMons[battler].status1 & STATUS1_POISON) - && IsBattlerAlive(battler) - && !IsMagicGuardProtected(battler, ability)) - { - if (ability == ABILITY_POISON_HEAL) - { - if (!IsBattlerAtMaxHp(battler) && !(gStatuses3[battler] & STATUS3_HEAL_BLOCK)) - { - gBattleStruct->moveDamage[battler] = GetNonDynamaxMaxHP(battler) / 8; - if (gBattleStruct->moveDamage[battler] == 0) - gBattleStruct->moveDamage[battler] = 1; - gBattleStruct->moveDamage[battler] *= -1; - BattleScriptExecute(BattleScript_PoisonHealActivates); - effect++; - } - } - else - { - gBattleStruct->moveDamage[battler] = GetNonDynamaxMaxHP(battler) / 8; - if (gBattleStruct->moveDamage[battler] == 0) - gBattleStruct->moveDamage[battler] = 1; - BattleScriptExecute(BattleScript_PoisonTurnDmg); - effect++; - } - } - gBattleStruct->turnEffectsTracker++; - break; - case ENDTURN_BAD_POISON: // toxic poison - if ((gBattleMons[battler].status1 & STATUS1_TOXIC_POISON) - && IsBattlerAlive(battler) - && !IsMagicGuardProtected(battler, ability)) - { - if (ability == ABILITY_POISON_HEAL) - { - if (!IsBattlerAtMaxHp(battler) && !(gStatuses3[battler] & STATUS3_HEAL_BLOCK)) - { - gBattleStruct->moveDamage[battler] = GetNonDynamaxMaxHP(battler) / 8; - if (gBattleStruct->moveDamage[battler] == 0) - gBattleStruct->moveDamage[battler] = 1; - gBattleStruct->moveDamage[battler] *= -1; - BattleScriptExecute(BattleScript_PoisonHealActivates); - effect++; - } - } - else - { - gBattleStruct->moveDamage[battler] = GetNonDynamaxMaxHP(battler) / 16; - if (gBattleStruct->moveDamage[battler] == 0) - gBattleStruct->moveDamage[battler] = 1; - if ((gBattleMons[battler].status1 & STATUS1_TOXIC_COUNTER) != STATUS1_TOXIC_TURN(15)) // not 16 turns - gBattleMons[battler].status1 += STATUS1_TOXIC_TURN(1); - gBattleStruct->moveDamage[battler] *= (gBattleMons[battler].status1 & STATUS1_TOXIC_COUNTER) >> 8; - BattleScriptExecute(BattleScript_PoisonTurnDmg); - effect++; - } - } - gBattleStruct->turnEffectsTracker++; - break; - case ENDTURN_BURN: // burn - if ((gBattleMons[battler].status1 & STATUS1_BURN) - && IsBattlerAlive(battler) - && !IsMagicGuardProtected(battler, ability)) - { - gBattleStruct->moveDamage[battler] = GetNonDynamaxMaxHP(battler) / (B_BURN_DAMAGE >= GEN_7 ? 16 : 8); - if (ability == ABILITY_HEATPROOF) - { - if (gBattleStruct->moveDamage[battler] > (gBattleStruct->moveDamage[battler] / 2) + 1) // Record ability if the burn takes less damage than it normally would. - RecordAbilityBattle(battler, ABILITY_HEATPROOF); - gBattleStruct->moveDamage[battler] /= 2; - } - if (gBattleStruct->moveDamage[battler] == 0) - gBattleStruct->moveDamage[battler] = 1; - BattleScriptExecute(BattleScript_BurnTurnDmg); - effect++; - } - gBattleStruct->turnEffectsTracker++; - break; - case ENDTURN_FROSTBITE: // burn - if ((gBattleMons[battler].status1 & STATUS1_FROSTBITE) - && IsBattlerAlive(battler) - && !IsMagicGuardProtected(battler, ability)) - { - gBattleStruct->moveDamage[battler] = GetNonDynamaxMaxHP(battler) / (B_BURN_DAMAGE >= GEN_7 ? 16 : 8); - if (gBattleStruct->moveDamage[battler] == 0) - gBattleStruct->moveDamage[battler] = 1; - BattleScriptExecute(BattleScript_FrostbiteTurnDmg); - effect++; - } - gBattleStruct->turnEffectsTracker++; - break; - case ENDTURN_NIGHTMARES: // spooky nightmares - if ((gBattleMons[battler].status2 & STATUS2_NIGHTMARE) - && IsBattlerAlive(battler) - && !IsMagicGuardProtected(battler, ability)) - { - // R/S does not perform this sleep check, which causes the nightmare effect to - // persist even after the affected Pokémon has been awakened by Shed Skin. - if (gBattleMons[battler].status1 & STATUS1_SLEEP) - { - gBattleStruct->moveDamage[battler] = GetNonDynamaxMaxHP(battler) / 4; - if (gBattleStruct->moveDamage[battler] == 0) - gBattleStruct->moveDamage[battler] = 1; - BattleScriptExecute(BattleScript_NightmareTurnDmg); - effect++; - } - else - { - gBattleMons[battler].status2 &= ~STATUS2_NIGHTMARE; - } - } - gBattleStruct->turnEffectsTracker++; - break; - case ENDTURN_CURSE: // curse - if ((gBattleMons[battler].status2 & STATUS2_CURSED) - && IsBattlerAlive(battler) - && !IsMagicGuardProtected(battler, ability)) - { - gBattleStruct->moveDamage[battler] = GetNonDynamaxMaxHP(battler) / 4; - if (gBattleStruct->moveDamage[battler] == 0) - gBattleStruct->moveDamage[battler] = 1; - BattleScriptExecute(BattleScript_CurseTurnDmg); - effect++; - } - gBattleStruct->turnEffectsTracker++; - break; - case ENDTURN_WRAP: // wrap - if ((gBattleMons[battler].status2 & STATUS2_WRAPPED) && IsBattlerAlive(battler)) - { - if (--gDisableStructs[battler].wrapTurns != 0) // damaged by wrap - { - if (IsMagicGuardProtected(battler, ability)) - { - gBattleStruct->turnEffectsTracker++; - break; - } - - gBattleScripting.animArg1 = gBattleStruct->wrappedMove[battler]; - gBattleScripting.animArg2 = gBattleStruct->wrappedMove[battler] >> 8; - PREPARE_MOVE_BUFFER(gBattleTextBuff1, gBattleStruct->wrappedMove[battler]); - gBattlescriptCurrInstr = BattleScript_WrapTurnDmg; - if (GetBattlerHoldEffect(gBattleStruct->wrappedBy[battler], TRUE) == HOLD_EFFECT_BINDING_BAND) - gBattleStruct->moveDamage[battler] = GetNonDynamaxMaxHP(battler) / (B_BINDING_DAMAGE >= GEN_6 ? 6 : 8); - else - gBattleStruct->moveDamage[battler] = GetNonDynamaxMaxHP(battler) / (B_BINDING_DAMAGE >= GEN_6 ? 8 : 16); - - if (gBattleStruct->moveDamage[battler] == 0) - gBattleStruct->moveDamage[battler] = 1; - } - else // broke free - { - gBattleMons[battler].status2 &= ~STATUS2_WRAPPED; - PREPARE_MOVE_BUFFER(gBattleTextBuff1, gBattleStruct->wrappedMove[battler]); - gBattlescriptCurrInstr = BattleScript_WrapEnds; - } - BattleScriptExecute(gBattlescriptCurrInstr); - effect++; - } - gBattleStruct->turnEffectsTracker++; - break; - case ENDTURN_OCTOLOCK: - if (gDisableStructs[battler].octolock) - { - gBattlerAttacker = gDisableStructs[battler].battlerPreventingEscape; - gBattlerTarget = battler; - BattleScriptExecute(BattleScript_OctolockEndTurn); - effect++; - } - gBattleStruct->turnEffectsTracker++; - break; - case ENDTURN_UPROAR: // uproar - if (gBattleMons[battler].status2 & STATUS2_UPROAR) - { - for (gBattlerAttacker = 0; gBattlerAttacker < gBattlersCount; gBattlerAttacker++) - { - if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_SLEEP) - && (B_UPROAR_IGNORE_SOUNDPROOF >= GEN_5 || GetBattlerAbility(gBattlerAttacker) != ABILITY_SOUNDPROOF)) - { - gBattleMons[gBattlerAttacker].status1 &= ~STATUS1_SLEEP; - gBattleMons[gBattlerAttacker].status2 &= ~STATUS2_NIGHTMARE; - gBattleCommunication[MULTISTRING_CHOOSER] = 1; - BattleScriptExecute(BattleScript_MonWokeUpInUproar); - BtlController_EmitSetMonData(gBattlerAttacker, BUFFER_A, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[gBattlerAttacker].status1); - MarkBattlerForControllerExec(gBattlerAttacker); - break; - } - } - if (gBattlerAttacker != gBattlersCount) - { - effect = 2; // a Pokémon was awaken - break; - } - else - { - gBattlerAttacker = battler; - gBattleMons[battler].status2 -= STATUS2_UPROAR_TURN(1); // uproar timer goes down - if (WasUnableToUseMove(battler)) - { - CancelMultiTurnMoves(battler); - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_UPROAR_ENDS; - } - else if (gBattleMons[battler].status2 & STATUS2_UPROAR) - { - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_UPROAR_CONTINUES; - gBattleMons[battler].status2 |= STATUS2_MULTIPLETURNS; - } - else - { - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_UPROAR_ENDS; - CancelMultiTurnMoves(battler); - } - BattleScriptExecute(BattleScript_PrintUproarOverTurns); - effect = 1; - } - } - if (effect != 2) - gBattleStruct->turnEffectsTracker++; - break; - case ENDTURN_THRASH: // thrash - // Don't decrement STATUS2_LOCK_CONFUSE if the target is held by Sky Drop - if (gBattleMons[battler].status2 & STATUS2_LOCK_CONFUSE && !(gStatuses3[battler] & STATUS3_SKY_DROPPED)) - { - gBattleMons[battler].status2 -= STATUS2_LOCK_CONFUSE_TURN(1); - if (WasUnableToUseMove(battler)) - CancelMultiTurnMoves(battler); - else if (!(gBattleMons[battler].status2 & STATUS2_LOCK_CONFUSE) - && (gBattleMons[battler].status2 & STATUS2_MULTIPLETURNS)) - { - gBattleMons[battler].status2 &= ~STATUS2_MULTIPLETURNS; - if (!(gBattleMons[battler].status2 & STATUS2_CONFUSION)) - { - gBattleScripting.moveEffect = MOVE_EFFECT_CONFUSION | MOVE_EFFECT_AFFECTS_USER; - SetMoveEffect(TRUE, FALSE); - if (gBattleMons[battler].status2 & STATUS2_CONFUSION) - BattleScriptExecute(BattleScript_ThrashConfuses); - effect++; - } - } - } - gBattleStruct->turnEffectsTracker++; - break; - case ENDTURN_FLINCH: // reset flinch - gBattleMons[battler].status2 &= ~STATUS2_FLINCHED; - gBattleStruct->turnEffectsTracker++; - break; - case ENDTURN_DISABLE: // disable - if (gDisableStructs[battler].disableTimer != 0) - { - for (i = 0; i < MAX_MON_MOVES; i++) - { - if (gDisableStructs[battler].disabledMove == gBattleMons[battler].moves[i]) - break; - } - if (i == MAX_MON_MOVES) // Pokémon does not have the disabled move anymore - { - gDisableStructs[battler].disabledMove = 0; - gDisableStructs[battler].disableTimer = 0; - } - else if (--gDisableStructs[battler].disableTimer == 0) // disable ends - { - gDisableStructs[battler].disabledMove = 0; - BattleScriptExecute(BattleScript_DisabledNoMore); - effect++; - } - } - gBattleStruct->turnEffectsTracker++; - break; - case ENDTURN_ENCORE: // encore - if (gDisableStructs[battler].encoreTimer != 0) - { - if (gBattleMons[battler].moves[gDisableStructs[battler].encoredMovePos] != gDisableStructs[battler].encoredMove) // Pokémon does not have the encored move anymore - { - gDisableStructs[battler].encoredMove = 0; - gDisableStructs[battler].encoreTimer = 0; - } - else if (--gDisableStructs[battler].encoreTimer == 0 - || gBattleMons[battler].pp[gDisableStructs[battler].encoredMovePos] == 0) - { - gDisableStructs[battler].encoredMove = 0; - gDisableStructs[battler].encoreTimer = 0; - BattleScriptExecute(BattleScript_EncoredNoMore); - effect++; - } - } - gBattleStruct->turnEffectsTracker++; - break; - case ENDTURN_LOCK_ON: // lock-on decrement - if (gStatuses3[battler] & STATUS3_ALWAYS_HITS) - gStatuses3[battler] -= STATUS3_ALWAYS_HITS_TURN(1); - gBattleStruct->turnEffectsTracker++; - break; - case ENDTURN_CHARGE: // charge - if (gDisableStructs[battler].chargeTimer > 0 && --gDisableStructs[battler].chargeTimer == 0) - gStatuses3[battler] &= ~STATUS3_CHARGED_UP; - gBattleStruct->turnEffectsTracker++; - break; - case ENDTURN_TAUNT: // taunt - if (gDisableStructs[battler].tauntTimer && --gDisableStructs[battler].tauntTimer == 0) - { - BattleScriptExecute(BattleScript_BufferEndTurn); - PREPARE_MOVE_BUFFER(gBattleTextBuff1, MOVE_TAUNT); - effect++; - } - gBattleStruct->turnEffectsTracker++; - break; - case ENDTURN_YAWN: // yawn - if (gStatuses3[battler] & STATUS3_YAWN) - { - u16 battlerAbility = GetBattlerAbility(battler); - gStatuses3[battler] -= STATUS3_YAWN_TURN(1); - if (!(gStatuses3[battler] & STATUS3_YAWN) && !(gBattleMons[battler].status1 & STATUS1_ANY) - && battlerAbility != ABILITY_VITAL_SPIRIT - && battlerAbility != ABILITY_INSOMNIA && !UproarWakeUpCheck(battler) - && !IsLeafGuardProtected(battler, battlerAbility)) - { - CancelMultiTurnMoves(battler); - gEffectBattler = gBattlerTarget = battler; - if (IsBattlerTerrainAffected(battler, STATUS_FIELD_ELECTRIC_TERRAIN)) - { - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAINPREVENTS_ELECTRIC; - BattleScriptExecute(BattleScript_TerrainPreventsEnd2); - } - else if (IsBattlerTerrainAffected(battler, STATUS_FIELD_MISTY_TERRAIN)) - { - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAINPREVENTS_MISTY; - BattleScriptExecute(BattleScript_TerrainPreventsEnd2); - } - else if (IsSleepClauseActiveForSide(GetBattlerSide(battler))) - { - BattleScriptExecute(BattleScript_SleepClausePreventsEnd); - } - else - { - if (B_SLEEP_TURNS >= GEN_5) - gBattleMons[battler].status1 |= ((Random() % 3) + 2); - else - gBattleMons[battler].status1 |= ((Random() % 4) + 3); - - TryActivateSleepClause(battler, gBattlerPartyIndexes[battler]); - BtlController_EmitSetMonData(battler, BUFFER_A, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[battler].status1); - MarkBattlerForControllerExec(battler); - BattleScriptExecute(BattleScript_YawnMakesAsleep); - } - effect++; - } - } - gBattleStruct->turnEffectsTracker++; - break; - case ENDTURN_LASER_FOCUS: - if (gStatuses3[battler] & STATUS3_LASER_FOCUS) - { - if (gDisableStructs[battler].laserFocusTimer == 0 || --gDisableStructs[battler].laserFocusTimer == 0) - gStatuses3[battler] &= ~STATUS3_LASER_FOCUS; - } - gBattleStruct->turnEffectsTracker++; - break; - case ENDTURN_EMBARGO: - if (gStatuses3[battler] & STATUS3_EMBARGO) - { - if (gDisableStructs[battler].embargoTimer == 0 || --gDisableStructs[battler].embargoTimer == 0) - { - gStatuses3[battler] &= ~STATUS3_EMBARGO; - BattleScriptExecute(BattleScript_EmbargoEndTurn); - effect++; - } - } - gBattleStruct->turnEffectsTracker++; - break; - case ENDTURN_MAGNET_RISE: - if (gStatuses3[battler] & STATUS3_MAGNET_RISE) - { - if (gDisableStructs[battler].magnetRiseTimer == 0 || --gDisableStructs[battler].magnetRiseTimer == 0) - { - gStatuses3[battler] &= ~STATUS3_MAGNET_RISE; - BattleScriptExecute(BattleScript_BufferEndTurn); - PREPARE_STRING_BUFFER(gBattleTextBuff1, STRINGID_ELECTROMAGNETISM); - effect++; - } - } - gBattleStruct->turnEffectsTracker++; - break; - case ENDTURN_TELEKINESIS: - if (gStatuses3[battler] & STATUS3_TELEKINESIS) - { - if (gDisableStructs[battler].telekinesisTimer == 0 || --gDisableStructs[battler].telekinesisTimer == 0) - { - gStatuses3[battler] &= ~STATUS3_TELEKINESIS; - BattleScriptExecute(BattleScript_TelekinesisEndTurn); - effect++; - } - } - gBattleStruct->turnEffectsTracker++; - break; - case ENDTURN_HEALBLOCK: - if (gStatuses3[battler] & STATUS3_HEAL_BLOCK) - { - if (gDisableStructs[battler].healBlockTimer == 0 || --gDisableStructs[battler].healBlockTimer == 0) - { - gStatuses3[battler] &= ~STATUS3_HEAL_BLOCK; - BattleScriptExecute(BattleScript_BufferEndTurn); - PREPARE_MOVE_BUFFER(gBattleTextBuff1, MOVE_HEAL_BLOCK); - effect++; - } - } - gBattleStruct->turnEffectsTracker++; - break; - case ENDTURN_ROOST: // Return flying type. - if (gDisableStructs[battler].roostActive) - gDisableStructs[battler].roostActive = FALSE; - gBattleStruct->turnEffectsTracker++; - break; - case ENDTURN_POWDER: - gBattleMons[battler].status2 &= ~STATUS2_POWDER; - gBattleStruct->turnEffectsTracker++; - break; - case ENDTURN_THROAT_CHOP: - if (gDisableStructs[battler].throatChopTimer && --gDisableStructs[battler].throatChopTimer == 0) - { - BattleScriptExecute(BattleScript_ThroatChopEndTurn); - effect++; - } - gBattleStruct->turnEffectsTracker++; - break; - case ENDTURN_SLOW_START: - if (gDisableStructs[battler].slowStartTimer - && --gDisableStructs[battler].slowStartTimer == 0 - && ability == ABILITY_SLOW_START) - { - BattleScriptExecute(BattleScript_SlowStartEnds); - effect++; - } - gBattleStruct->turnEffectsTracker++; - break; - case ENDTURN_CUD_CHEW: - if (GetBattlerAbility(battler) == ABILITY_CUD_CHEW && !gDisableStructs[battler].cudChew && ItemId_GetPocket(GetUsedHeldItem(battler)) == POCKET_BERRIES) - gDisableStructs[battler].cudChew = TRUE; - gBattleStruct->turnEffectsTracker++; - break; - case ENDTURN_SALT_CURE: - if (gStatuses4[battler] & STATUS4_SALT_CURE - && IsBattlerAlive(battler) - && !IsMagicGuardProtected(battler, ability)) - { - gBattlerTarget = battler; - if (IS_BATTLER_ANY_TYPE(battler, TYPE_STEEL, TYPE_WATER)) - gBattleStruct->moveDamage[battler] = gBattleMons[battler].maxHP / 4; - else - gBattleStruct->moveDamage[battler] = gBattleMons[battler].maxHP / 8; - if (gBattleStruct->moveDamage[battler] == 0) - gBattleStruct->moveDamage[battler] = 1; - PREPARE_MOVE_BUFFER(gBattleTextBuff1, MOVE_SALT_CURE); - BattleScriptExecute(BattleScript_SaltCureExtraDamage); - effect++; - } - gBattleStruct->turnEffectsTracker++; - break; - case ENDTURN_SYRUP_BOMB: - if ((gStatuses4[battler] & STATUS4_SYRUP_BOMB) && (IsBattlerAlive(battler))) - { - if (gDisableStructs[battler].syrupBombTimer > 0 && --gDisableStructs[battler].syrupBombTimer == 0) - gStatuses4[battler] &= ~STATUS4_SYRUP_BOMB; - gBattlerTarget = battler; - PREPARE_MOVE_BUFFER(gBattleTextBuff1, MOVE_SYRUP_BOMB); - gBattlescriptCurrInstr = BattleScript_SyrupBombEndTurn; - BattleScriptExecute(gBattlescriptCurrInstr); - effect++; - } - gBattleStruct->turnEffectsTracker++; - break; - case ENDTURN_TORMENT: - if (gDisableStructs[battler].tormentTimer != PERMANENT_TORMENT - && --gDisableStructs[battler].tormentTimer == 0) - { - gBattleMons[battler].status2 &= ~STATUS2_TORMENT; - BattleScriptExecute(BattleScript_TormentEnds); - effect++; - } - gBattleStruct->turnEffectsTracker++; - break; - case ENDTURN_DYNAMAX: - if (GetActiveGimmick(battler) == GIMMICK_DYNAMAX - && --gBattleStruct->dynamax.dynamaxTurns[battler] == 0) - { - gBattleScripting.battler = battler; - UndoDynamax(battler); - BattleScriptExecute(BattleScript_DynamaxEnds); - effect++; - } - gBattleStruct->turnEffectsTracker++; - break; - case ENDTURN_GMAX_MOVE_RESIDUAL_DAMAGE: - { - u32 side = GetBattlerSide(gBattlerAttacker); - if (gSideStatuses[side] & SIDE_STATUS_DAMAGE_NON_TYPES - && !IS_BATTLER_OF_TYPE(gBattlerAttacker, gSideTimers[side].damageNonTypesType) - && IsBattlerAlive(gBattlerAttacker) - && GetBattlerAbility(gBattlerAttacker) != ABILITY_MAGIC_GUARD) - { - gBattleScripting.battler = battler; - gBattleStruct->moveDamage[battler] = GetNonDynamaxMaxHP(battler) / 6; - ChooseDamageNonTypesString(gSideTimers[side].damageNonTypesType); - BattleScriptExecute(BattleScript_DamageNonTypesContinues); - effect++; - } - gBattleStruct->turnEffectsTracker++; - break; - } - case ENDTURN_SEA_OF_FIRE_DAMAGE: - if (IsBattlerAlive(battler) && gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_SEA_OF_FIRE) - { - gBattleStruct->moveDamage[battler] = GetNonDynamaxMaxHP(battler) / 8; - BtlController_EmitStatusAnimation(battler, BUFFER_A, FALSE, STATUS1_BURN); - MarkBattlerForControllerExec(battler); - BattleScriptExecute(BattleScript_HurtByTheSeaOfFire); - effect++; - } - gBattleStruct->turnEffectsTracker++; - break; - case ENDTURN_BATTLER_COUNT: // done - gBattleStruct->turnEffectsTracker = 0; - gBattleStruct->turnEffectsBattlerId++; - break; - } - - if (effect != 0) - return effect; - - } - gHitMarker &= ~(HITMARKER_GRUDGE | HITMARKER_IGNORE_BIDE); - return 0; -} - -bool32 HandleWishPerishSongOnTurnEnd(void) -{ - u32 battler; - - gHitMarker |= (HITMARKER_GRUDGE | HITMARKER_IGNORE_BIDE); - switch (gBattleStruct->wishPerishSongState) - { - case 0: - while (gBattleStruct->wishPerishSongBattlerId < gBattlersCount) - { - battler = gBattleStruct->wishPerishSongBattlerId; - - gBattleStruct->wishPerishSongBattlerId++; - - if (gWishFutureKnock.futureSightCounter[battler] == gBattleTurnCounter && !(gAbsentBattlerFlags & (1u << battler))) - { - struct Pokemon *party; - - if (gWishFutureKnock.futureSightCounter[battler] == gBattleTurnCounter - && gWishFutureKnock.futureSightCounter[BATTLE_PARTNER(battler)] <= gBattleTurnCounter) - { - gSideStatuses[GetBattlerSide(battler)] &= ~SIDE_STATUS_FUTUREATTACK; - } - - if (!IsBattlerAlive(battler)) - continue; - - if (gWishFutureKnock.futureSightMove[battler] == MOVE_FUTURE_SIGHT) - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_FUTURE_SIGHT; - else - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_DOOM_DESIRE; - - PREPARE_MOVE_BUFFER(gBattleTextBuff1, gWishFutureKnock.futureSightMove[battler]); - - gBattlerTarget = battler; - gBattlerAttacker = gWishFutureKnock.futureSightBattlerIndex[battler]; - gCurrentMove = gWishFutureKnock.futureSightMove[battler]; - - party = GetSideParty(GetBattlerSide(gBattlerAttacker)); - if (&party[gWishFutureKnock.futureSightPartyIndex[gBattlerTarget]] == &party[gBattlerPartyIndexes[gBattlerAttacker]]) - SetTypeBeforeUsingMove(gCurrentMove, gBattlerAttacker); - - BattleScriptExecute(BattleScript_MonTookFutureAttack); - - return TRUE; - } - } - gBattleStruct->wishPerishSongState = 1; - gBattleStruct->wishPerishSongBattlerId = 0; - // fall through - case 1: - while (gBattleStruct->wishPerishSongBattlerId < gBattlersCount) - { - battler = gBattlerAttacker = gBattlerByTurnOrder[gBattleStruct->wishPerishSongBattlerId]; - if (gAbsentBattlerFlags & (1u << battler)) - { - gBattleStruct->wishPerishSongBattlerId++; - continue; - } - gBattleStruct->wishPerishSongBattlerId++; - if (gStatuses3[battler] & STATUS3_PERISH_SONG) - { - PREPARE_BYTE_NUMBER_BUFFER(gBattleTextBuff1, 1, gDisableStructs[battler].perishSongTimer); - if (gDisableStructs[battler].perishSongTimer == 0) - { - gStatuses3[battler] &= ~STATUS3_PERISH_SONG; - gBattleStruct->moveDamage[battler] = gBattleMons[battler].hp; - gBattlescriptCurrInstr = BattleScript_PerishSongTakesLife; - } - else - { - gDisableStructs[battler].perishSongTimer--; - gBattlescriptCurrInstr = BattleScript_PerishSongCountGoesDown; - } - BattleScriptExecute(gBattlescriptCurrInstr); - return TRUE; - } - } - gBattleStruct->wishPerishSongState = 2; - gBattleStruct->wishPerishSongBattlerId = 0; - // fall through - case 2: - if ((gBattleTypeFlags & BATTLE_TYPE_ARENA) - && gBattleStruct->arenaTurnCounter == 2 - && IsBattlerAlive(B_POSITION_PLAYER_LEFT) && IsBattlerAlive(B_POSITION_OPPONENT_LEFT)) - { - s32 i; - - for (i = 0; i < 2; i++) - CancelMultiTurnMoves(i); - - gBattlescriptCurrInstr = BattleScript_ArenaDoJudgment; - BattleScriptExecute(BattleScript_ArenaDoJudgment); - gBattleStruct->wishPerishSongState++; - return TRUE; - } - break; - } - - gHitMarker &= ~(HITMARKER_GRUDGE | HITMARKER_IGNORE_BIDE); - - return FALSE; -} - #define FAINTED_ACTIONS_MAX_CASE 7 - bool32 HandleFaintedMonActions(void) { if (gBattleTypeFlags & BATTLE_TYPE_SAFARI) @@ -3404,7 +2094,7 @@ static void CancellerGravity(u32 *effect) static void CancellerThroatChop(u32 *effect) { - if (GetActiveGimmick(gBattlerAttacker) != GIMMICK_Z_MOVE && gDisableStructs[gBattlerAttacker].throatChopTimer && IsSoundMove(gCurrentMove)) + if (GetActiveGimmick(gBattlerAttacker) != GIMMICK_Z_MOVE && gDisableStructs[gBattlerAttacker].throatChopTimer > gBattleTurnCounter && IsSoundMove(gCurrentMove)) { gProtectStructs[gBattlerAttacker].usedThroatChopPreventedMove = TRUE; CancelMultiTurnMoves(gBattlerAttacker); @@ -4538,6 +3228,8 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 if (special) gLastUsedAbility = special; + else if (ability) + gLastUsedAbility = ability; else gLastUsedAbility = GetBattlerAbility(battler); @@ -4848,7 +3540,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 case ABILITY_SLOW_START: if (!gSpecialStatuses[battler].switchInAbilityDone) { - gDisableStructs[battler].slowStartTimer = 5; + gDisableStructs[battler].slowStartTimer = gBattleTurnCounter + 5; gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SWITCHIN_SLOWSTART; gSpecialStatuses[battler].switchInAbilityDone = TRUE; BattleScriptPushCursorAndCallback(BattleScript_SwitchInAbilityMsg); @@ -5442,6 +4134,19 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 effect++; } break; + case ABILITY_ICE_BODY: + if (!IsBattlerAtMaxHp(battler) + && !(gStatuses3[battler] & (STATUS3_UNDERGROUND | STATUS3_UNDERWATER)) + && !(gStatuses3[battler] & STATUS3_HEAL_BLOCK)) + { + BattleScriptPushCursorAndCallback(BattleScript_IceBodyHeal); + gBattleStruct->moveDamage[battler] = GetNonDynamaxMaxHP(battler) / 16; + if (gBattleStruct->moveDamage[battler] == 0) + gBattleStruct->moveDamage[battler] = 1; + gBattleStruct->moveDamage[battler] *= -1; + effect++; + } + break; case ABILITY_DRY_SKIN: if (IsBattlerWeatherAffected(battler, B_WEATHER_SUN)) goto SOLAR_POWER_HP_DROP; @@ -5546,6 +4251,13 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 case ABILITY_TRUANT: gDisableStructs[gBattlerAttacker].truantCounter ^= 1; break; + case ABILITY_SLOW_START: + if (gDisableStructs[battler].slowStartTimer == gBattleTurnCounter) + { + BattleScriptExecute(BattleScript_SlowStartEnds); + effect++; + } + break; case ABILITY_BAD_DREAMS: BattleScriptPushCursorAndCallback(BattleScript_BadDreamsActivates); effect++; @@ -5617,6 +4329,10 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 BattleScriptPushCursorAndCallback(BattleScript_CudChewActivates); effect++; } + else if (!gDisableStructs[battler].cudChew && ItemId_GetPocket(GetUsedHeldItem(battler)) == POCKET_BERRIES) + { + gDisableStructs[battler].cudChew = TRUE; + } break; } } @@ -7888,6 +6604,7 @@ u32 ItemBattleEffects(enum ItemCaseId caseID, u32 battler, bool32 moveTurn) } break; case ITEMEFFECT_NORMAL: + case ITEMEFFECT_TRY_HEALING: if (gBattleMons[battler].hp) { switch (battlerHoldEffect) @@ -9766,7 +8483,7 @@ static inline u32 CalcAttackStat(struct DamageCalculationData *damageCalcData, u modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(2.0)); break; case ABILITY_SLOW_START: - if (gDisableStructs[battlerAtk].slowStartTimer != 0) + if (gDisableStructs[battlerAtk].slowStartTimer > gBattleTurnCounter) modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(0.5)); break; case ABILITY_SOLAR_POWER: @@ -11569,7 +10286,7 @@ bool32 CanFling(u32 battler) if (item == ITEM_NONE || (B_KLUTZ_FLING_INTERACTION >= GEN_5 && GetBattlerAbility(battler) == ABILITY_KLUTZ) || gFieldStatuses & STATUS_FIELD_MAGIC_ROOM - || gDisableStructs[battler].embargoTimer != 0 + || gStatuses3[battler] & STATUS3_EMBARGO || GetFlingPowerFromItemId(item) == 0 || !CanBattlerGetOrLoseItem(battler, item)) return FALSE; diff --git a/test/battle/ability/emergency_exit.c b/test/battle/ability/emergency_exit.c index 0dbecb95e0..5027d929ec 100644 --- a/test/battle/ability/emergency_exit.c +++ b/test/battle/ability/emergency_exit.c @@ -66,3 +66,45 @@ DOUBLE_BATTLE_TEST("Only the fastest Wimp Out (Emergency Exit) user switches out ABILITY_POPUP(opponentRight, ABILITY_WIMP_OUT); } } + +SINGLE_BATTLE_TEST("Emergency Exit activates when taking residual damage and falling under 50% max-hp - Burn") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_GOLISOPOD) { Ability(ABILITY_EMERGENCY_EXIT); MaxHP(263); HP(134); Status1(STATUS1_BURN); }; + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { SEND_OUT(opponent, 1); } + } SCENE { + HP_BAR(opponent); + ABILITY_POPUP(opponent, ABILITY_EMERGENCY_EXIT); + } +} + +SINGLE_BATTLE_TEST("Emergency Exit activates when taking residual damage and falling under 50% max-hp - Weather") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_GOLISOPOD) { Ability(ABILITY_EMERGENCY_EXIT); MaxHP(263); HP(134); }; + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_SANDSTORM); SEND_OUT(opponent, 1); } + } SCENE { + HP_BAR(opponent); + ABILITY_POPUP(opponent, ABILITY_EMERGENCY_EXIT); + } +} + +SINGLE_BATTLE_TEST("Emergency Exit activates when taking residual damage and falling under 50% max-hp - Salt Cure") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_GOLISOPOD) { Ability(ABILITY_EMERGENCY_EXIT); MaxHP(263); HP(160); }; + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_SALT_CURE); SEND_OUT(opponent, 1); } + } SCENE { + HP_BAR(opponent); + ABILITY_POPUP(opponent, ABILITY_EMERGENCY_EXIT); + } +} diff --git a/test/battle/ability/healer.c b/test/battle/ability/healer.c index 9626783ec5..6a113f407b 100644 --- a/test/battle/ability/healer.c +++ b/test/battle/ability/healer.c @@ -26,8 +26,6 @@ DOUBLE_BATTLE_TEST("Healer cures adjacent ally's status condition 30% of the tim DOUBLE_BATTLE_TEST("Healer cures status condition before burn or poison damage is dealt") { - KNOWN_FAILING; // According to Bulbapedia, Healer should trigger before status damage and Wobbuffet should live - // Source: https://bulbapedia.bulbagarden.net/wiki/Healer_(Ability)#Effect u16 status; PARAMETRIZE { status = STATUS1_POISON; } PARAMETRIZE { status = STATUS1_BURN; } @@ -42,10 +40,8 @@ DOUBLE_BATTLE_TEST("Healer cures status condition before burn or poison damage i } WHEN { TURN {} } SCENE { - NOT { - MESSAGE("The opposing Wobbuffet fainted!"); - } - MESSAGE("The opposing Chansey's Healer cured Foe Wobbuffet's problem!"); + NOT MESSAGE("The opposing Wobbuffet fainted!"); + MESSAGE("The opposing Chansey's Healer cured the opposing Wobbuffet's problem!"); } } diff --git a/test/battle/end_turn_effects.c b/test/battle/end_turn_effects.c new file mode 100644 index 0000000000..51aa1e79bf --- /dev/null +++ b/test/battle/end_turn_effects.c @@ -0,0 +1,31 @@ +#include "global.h" +#include "test/battle.h" + +DOUBLE_BATTLE_TEST("End Turn Effects: First Event Block is executed correctly") +{ + s16 healed; + s16 damage; + + GIVEN { + PLAYER(SPECIES_WYNAUT) { HP(100); Speed(1); } + PLAYER(SPECIES_EKANS) { HP(100); Ability(ABILITY_SHED_SKIN); Status1(STATUS1_BURN); Speed(2); } + OPPONENT(SPECIES_WYNAUT) { HP(100); Item(ITEM_LEFTOVERS); Speed(3); } + OPPONENT(SPECIES_WOBBUFFET) { HP(100); Item(ITEM_BLACK_SLUDGE); Speed(4); } + } WHEN { + TURN { MOVE(playerLeft, MOVE_GRASSY_TERRAIN); } + } SCENE { + MESSAGE("The opposing Wobbuffet is healed by the grassy terrain!"); + HP_BAR(opponentRight, captureDamage: &healed); + HP_BAR(opponentRight, captureDamage: &damage); + MESSAGE("The opposing Wobbuffet was hurt by the Black Sludge!"); + MESSAGE("The opposing Wynaut is healed by the grassy terrain!"); + MESSAGE("The opposing Wynaut restored a little HP using its Leftovers!"); + MESSAGE("Ekans is healed by the grassy terrain!"); + MESSAGE("Ekans's Shed Skin cured its burn problem!"); + MESSAGE("Wynaut is healed by the grassy terrain!"); + } THEN { + EXPECT_GT(0, healed); + EXPECT_GT(damage, 0); + } +} + diff --git a/test/battle/move_effect/court_change.c b/test/battle/move_effect/court_change.c index 817ab134ca..41381df18c 100644 --- a/test/battle/move_effect/court_change.c +++ b/test/battle/move_effect/court_change.c @@ -107,11 +107,11 @@ DOUBLE_BATTLE_TEST("Court Change used by the player swaps Mist, Safeguard, Auror MESSAGE("Wynaut used Court Change!"); MESSAGE("Wynaut swapped the battle effects affecting each side of the field!"); // The effects now end for the player side. - MESSAGE("Your team's Mist wore off!"); MESSAGE("Your team is no longer protected by Safeguard!"); + MESSAGE("Your team's Mist wore off!"); MESSAGE("Your team's Reflect wore off!"); - MESSAGE("Your team's Aurora Veil wore off!"); MESSAGE("Your team's Tailwind petered out!"); + MESSAGE("Your team's Aurora Veil wore off!"); MESSAGE("Your team's Light Screen wore off!"); } } @@ -144,11 +144,12 @@ DOUBLE_BATTLE_TEST("Court Change used by the opponent swaps Mist, Safeguard, Aur MESSAGE("The opposing Wynaut used Court Change!"); MESSAGE("The opposing Wynaut swapped the battle effects affecting each side of the field!"); // The effects now end for the player side. - MESSAGE("The opposing team's Mist wore off!"); + MESSAGE("The snow stopped."); MESSAGE("The opposing team is no longer protected by Safeguard!"); + MESSAGE("The opposing team's Mist wore off!"); MESSAGE("The opposing team's Reflect wore off!"); - MESSAGE("The opposing team's Aurora Veil wore off!"); MESSAGE("The opposing team's Tailwind petered out!"); + MESSAGE("The opposing team's Aurora Veil wore off!"); MESSAGE("The opposing team's Light Screen wore off!"); } } diff --git a/test/battle/move_effect/grassy_terrain.c b/test/battle/move_effect/grassy_terrain.c index 90e878b6dd..8b1895928b 100644 --- a/test/battle/move_effect/grassy_terrain.c +++ b/test/battle/move_effect/grassy_terrain.c @@ -8,9 +8,20 @@ SINGLE_BATTLE_TEST("Grassy Terrain recovers 1/16th HP at end of turn") OPPONENT(SPECIES_WOBBUFFET) { MaxHP(100); HP(1); }; } WHEN { TURN { MOVE(player, MOVE_GRASSY_TERRAIN); } + TURN {} } SCENE { s32 maxHPPlayer = GetMonData(&PLAYER_PARTY[0], MON_DATA_MAX_HP); s32 maxHPOpponent = GetMonData(&OPPONENT_PARTY[0], MON_DATA_MAX_HP); + + ANIMATION(ANIM_TYPE_MOVE, MOVE_GRASSY_TERRAIN, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponent); + MESSAGE("Wobbuffet is healed by the grassy terrain!"); + HP_BAR(player, damage: -maxHPPlayer / 16); + MESSAGE("The opposing Wobbuffet is healed by the grassy terrain!"); + HP_BAR(opponent, damage: -maxHPOpponent / 16); + + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponent); MESSAGE("Wobbuffet is healed by the grassy terrain!"); HP_BAR(player, damage: -maxHPPlayer / 16); MESSAGE("The opposing Wobbuffet is healed by the grassy terrain!"); diff --git a/test/battle/move_effect/leech_seed.c b/test/battle/move_effect/leech_seed.c index d363e98009..56da50a12b 100644 --- a/test/battle/move_effect/leech_seed.c +++ b/test/battle/move_effect/leech_seed.c @@ -58,6 +58,36 @@ SINGLE_BATTLE_TEST("Leech Seed recovery is prevented by Heal Block") } } +DOUBLE_BATTLE_TEST("Leech Seed will drain HP based on speed of the drained mon") +{ + GIVEN { + PLAYER(SPECIES_WYNAUT) { Speed(1); } + PLAYER(SPECIES_WOBBUFFET) { Speed(2); } + OPPONENT(SPECIES_WYNAUT) { Speed(3); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(4); } + } WHEN { + TURN { + MOVE(playerLeft, MOVE_LEECH_SEED, target: opponentLeft); + MOVE(playerRight, MOVE_LEECH_SEED, target: opponentRight); + MOVE(opponentLeft, MOVE_LEECH_SEED, target: playerLeft); + MOVE(opponentRight, MOVE_LEECH_SEED, target: playerRight); + } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_LEECH_SEED, opponentRight); + ANIMATION(ANIM_TYPE_MOVE, MOVE_LEECH_SEED, opponentLeft); + ANIMATION(ANIM_TYPE_MOVE, MOVE_LEECH_SEED, playerRight); + ANIMATION(ANIM_TYPE_MOVE, MOVE_LEECH_SEED, playerLeft); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_LEECH_SEED_DRAIN, opponentRight); + HP_BAR(opponentRight); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_LEECH_SEED_DRAIN, opponentLeft); + HP_BAR(opponentLeft); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_LEECH_SEED_DRAIN, playerRight); + HP_BAR(playerRight); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_LEECH_SEED_DRAIN, playerLeft); + HP_BAR(playerLeft); + } +} + TO_DO_BATTLE_TEST("Leech Seed doesn't affect already seeded targets") TO_DO_BATTLE_TEST("Leech Seed's effect is paused until a new battler replaces the original user's position") // Faint, can't be replaced, then revived. TO_DO_BATTLE_TEST("Leech Seed's effect pause still prevents it from being seeded again") diff --git a/test/battle/move_effect/magic_room.c b/test/battle/move_effect/magic_room.c new file mode 100644 index 0000000000..b1be6ac5bc --- /dev/null +++ b/test/battle/move_effect/magic_room.c @@ -0,0 +1,37 @@ +#include "global.h" +#include "test/battle.h" + +ASSUMPTIONS +{ + ASSUME(GetMoveEffect(MOVE_MAGIC_ROOM) == EFFECT_MAGIC_ROOM); +} + +DOUBLE_BATTLE_TEST("Magic Room prevents item hold effects") +{ + GIVEN { + ASSUME(gItemsInfo[ITEM_BERRY_JUICE].holdEffect == HOLD_EFFECT_RESTORE_HP); + ASSUME(GetMoveEffect(MOVE_DRAGON_RAGE) == EFFECT_FIXED_DAMAGE_ARG); + ASSUME(GetMoveFixedDamage(MOVE_DRAGON_RAGE) == 40); + + PLAYER(SPECIES_WOBBUFFET) { MaxHP(100); HP(60); Item(ITEM_BERRY_JUICE); } + PLAYER(SPECIES_WOBBUFFET) { MaxHP(100); HP(60); Item(ITEM_BERRY_JUICE); } + OPPONENT(SPECIES_WOBBUFFET) { MaxHP(100); HP(60); Item(ITEM_BERRY_JUICE); } + OPPONENT(SPECIES_WOBBUFFET) { MaxHP(100); HP(60); Item(ITEM_BERRY_JUICE); } + } WHEN { + TURN { MOVE(playerLeft, MOVE_MAGIC_ROOM); } + TURN { + MOVE(playerLeft, MOVE_DRAGON_RAGE, target: opponentLeft); + MOVE(opponentLeft, MOVE_DRAGON_RAGE, target: playerLeft); + MOVE(playerRight, MOVE_DRAGON_RAGE, target: opponentRight); + MOVE(opponentRight, MOVE_DRAGON_RAGE, target: playerRight); + } + TURN { MOVE(playerLeft, MOVE_MAGIC_ROOM); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_MAGIC_ROOM, playerLeft); + ANIMATION(ANIM_TYPE_MOVE, MOVE_MAGIC_ROOM, playerLeft); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerLeft); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponentLeft); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerRight); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponentRight); + } +} diff --git a/test/battle/move_effect/wish.c b/test/battle/move_effect/wish.c new file mode 100644 index 0000000000..c1e5459f02 --- /dev/null +++ b/test/battle/move_effect/wish.c @@ -0,0 +1,25 @@ +#include "global.h" +#include "test/battle.h" + +ASSUMPTIONS +{ + ASSUME(GetMoveEffect(MOVE_WISH) == EFFECT_WISH); +} + +SINGLE_BATTLE_TEST("Wish restores 50% of max player HP") +{ + GIVEN { + PLAYER(SPECIES_WYNAUT) { HP(50); MaxHP(100); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_WISH); } + TURN { } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_WISH, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, player); + MESSAGE("Wynaut's wish came true!"); + HP_BAR(player, hp: 100); + MESSAGE("Wynaut's HP was restored."); + } +} + diff --git a/test/battle/move_effect_secondary/throat_chop.c b/test/battle/move_effect_secondary/throat_chop.c index e640948c95..c2724a0f38 100644 --- a/test/battle/move_effect_secondary/throat_chop.c +++ b/test/battle/move_effect_secondary/throat_chop.c @@ -24,22 +24,24 @@ SINGLE_BATTLE_TEST("Throat Chop prevents the usage of sound moves") } } -SINGLE_BATTLE_TEST("Throat Chop won't work through a substitute") +SINGLE_BATTLE_TEST("Throat Chop prevents sound base moves for 2 turns") { GIVEN { - PLAYER(SPECIES_INCINEROAR) { Speed(100); }; - OPPONENT(SPECIES_WOBBUFFET) { Speed(50); }; + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_HYPER_VOICE, MOVE_ALLURING_VOICE, MOVE_OVERDRIVE, MOVE_ROUND); } } WHEN { - TURN { MOVE(opponent, MOVE_SUBSTITUTE); } - TURN { MOVE(player, MOVE_THROAT_CHOP); MOVE(opponent, MOVE_HYPER_VOICE); } - TURN {} + TURN { MOVE(opponent, MOVE_HYPER_VOICE); MOVE(player, MOVE_THROAT_CHOP); } + TURN { FORCED_MOVE(opponent); } + TURN { MOVE(opponent, MOVE_HYPER_VOICE); MOVE(player, MOVE_THROAT_CHOP); } + TURN { FORCED_MOVE(opponent); } + TURN { MOVE(opponent, MOVE_HYPER_VOICE); } } SCENE { - ANIMATION(ANIM_TYPE_MOVE, MOVE_SUBSTITUTE, opponent); - HP_BAR(opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_HYPER_VOICE, opponent); ANIMATION(ANIM_TYPE_MOVE, MOVE_THROAT_CHOP, player); - NONE_OF { - MESSAGE("The effects of Throat Chop prevent the opposing Wobbuffet from using certain moves!"); - } + ANIMATION(ANIM_TYPE_MOVE, MOVE_STRUGGLE, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_HYPER_VOICE, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_THROAT_CHOP, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_STRUGGLE, opponent); ANIMATION(ANIM_TYPE_MOVE, MOVE_HYPER_VOICE, opponent); } }