From 4528bbe01d14421a8dc60e6ed2395034383406f7 Mon Sep 17 00:00:00 2001 From: hedara90 <90hedara@gmail.com> Date: Thu, 17 Apr 2025 15:42:25 +0200 Subject: [PATCH 01/17] Fix VRAM load for Black Hole Eclipse (#6635) Co-authored-by: Hedara --- data/battle_anim_scripts.s | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/data/battle_anim_scripts.s b/data/battle_anim_scripts.s index 09913a9e6f..edd3ea89c2 100644 --- a/data/battle_anim_scripts.s +++ b/data/battle_anim_scripts.s @@ -31487,13 +31487,15 @@ gBattleAnimMove_BlackHoleEclipse:: delay 0x8 createsprite gBlackHoleEclipseHoleShrinkSpriteTemplate, ANIM_ATTACKER, 2, 0x0, 0x0, ANIM_TARGET, 0x0 waitforvisualfinish + unloadspritegfx ANIM_TAG_THIN_RING + unloadspritegfx ANIM_TAG_ICE_CHUNK loadspritegfx ANIM_TAG_SPARKLE_4 @detect createsprite gTargetTwinkleSpriteTemplate, ANIM_ATTACKER, 13, 0x0, 0x0, ANIM_TARGET @detect star delay 0x2 + unloadspritegfx ANIM_TAG_VERTICAL_HEX @red unloadspritegfx ANIM_TAG_SHADOW_BALL - unloadspritegfx ANIM_TAG_THIN_RING - unloadspritegfx ANIM_TAG_ICE_CHUNK unloadspritegfx ANIM_TAG_BLACK_BALL_2 + unloadspritegfx ANIM_TAG_FOCUS_ENERGY loadspritegfx ANIM_TAG_EXPLOSION_2 call BlackHoleEclipseExplosion createvisualtask AnimTask_BlendBattleAnimPal, 0xa, (F_PAL_BG | F_PAL_BATTLERS_2), 0x1, 0x0, 0x10, 0x7fff @ bg to white pal From 2d1b2f28c4f5865edd5a2df5cabc039c8429099d Mon Sep 17 00:00:00 2001 From: AlexOn1ine Date: Thu, 17 Apr 2025 19:46:16 +0200 Subject: [PATCH 02/17] Fixes Gulp Missile freezing the game if when attacker fainted --- src/battle_script_commands.c | 1 + src/battle_util.c | 1 + test/battle/ability/gulp_missile.c | 10 ++++++++++ 3 files changed, 12 insertions(+) diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index ee367133dd..dc71bc1a25 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -17993,6 +17993,7 @@ void BS_TryActivateGulpMissile(void) if (!(gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_NO_EFFECT) && !gProtectStructs[gBattlerAttacker].confusionSelfDmg + && IsBattlerAlive(gBattlerAttacker) && IsBattlerTurnDamaged(gBattlerTarget) && gBattleMons[gBattlerTarget].species != SPECIES_CRAMORANT && GetBattlerAbility(gBattlerTarget) == ABILITY_GULP_MISSILE) diff --git a/src/battle_util.c b/src/battle_util.c index 1597674c17..7ae8a63205 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -6092,6 +6092,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 && !gProtectStructs[gBattlerAttacker].confusionSelfDmg && IsBattlerTurnDamaged(gBattlerTarget) && IsBattlerAlive(battler) + && IsBattlerAlive(gBattlerAttacker) && gBattleMons[gBattlerTarget].species != SPECIES_CRAMORANT) { if (GetBattlerAbility(gBattlerAttacker) != ABILITY_MAGIC_GUARD) diff --git a/test/battle/ability/gulp_missile.c b/test/battle/ability/gulp_missile.c index 72e826b252..76d66c4fb8 100644 --- a/test/battle/ability/gulp_missile.c +++ b/test/battle/ability/gulp_missile.c @@ -190,3 +190,13 @@ SINGLE_BATTLE_TEST("(Gulp Missile) Transformed Cramorant Gulping lowers defense HP_BAR(opponent); } } + +SINGLE_BATTLE_TEST("Gulp Missile triggered by explosion doesn't freeze the game") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_CRAMORANT); + } WHEN { + TURN { MOVE(opponent, MOVE_SURF); MOVE(player, MOVE_EXPLOSION); } + } +} From 4e2c60ece7749c93103e84c7a3875d59222455ac Mon Sep 17 00:00:00 2001 From: AlexOn1ine Date: Thu, 17 Apr 2025 19:56:08 +0200 Subject: [PATCH 03/17] remove wrong check --- src/battle_util.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/battle_util.c b/src/battle_util.c index 7ae8a63205..39a25b564b 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -6091,7 +6091,6 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 if (!(gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_NO_EFFECT) && !gProtectStructs[gBattlerAttacker].confusionSelfDmg && IsBattlerTurnDamaged(gBattlerTarget) - && IsBattlerAlive(battler) && IsBattlerAlive(gBattlerAttacker) && gBattleMons[gBattlerTarget].species != SPECIES_CRAMORANT) { From 031fd4d363ea51b912704193d1548577c560b111 Mon Sep 17 00:00:00 2001 From: hedara90 <90hedara@gmail.com> Date: Fri, 18 Apr 2025 22:06:22 +0200 Subject: [PATCH 04/17] Fixed Color Change test referring to the wrong mon (#6644) Co-authored-by: Hedara --- test/battle/ability/color_change.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/battle/ability/color_change.c b/test/battle/ability/color_change.c index 668cb01b62..cc0a3e2472 100644 --- a/test/battle/ability/color_change.c +++ b/test/battle/ability/color_change.c @@ -47,7 +47,7 @@ SINGLE_BATTLE_TEST("Color Change does not change the type of a dual-type Pokemon ANIMATION(ANIM_TYPE_MOVE, MOVE_PSYCHO_CUT, player); NONE_OF { ABILITY_POPUP(opponent, ABILITY_COLOR_CHANGE); - MESSAGE("The opposing Xatu's Color Change made it the Psychic type!"); + MESSAGE("The opposing Slowbro's Color Change made it the Psychic type!"); } } } From 2a03f881d41b6cee1e311031cfbfa9e7055711cf Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Fri, 18 Apr 2025 22:07:18 +0200 Subject: [PATCH 05/17] Fixes Big Root Leech Seed heal amount (#6638) --- src/battle_util.c | 2 +- test/battle/hold_effect/big_root.c | 8 +++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/battle_util.c b/src/battle_util.c index 39a25b564b..defe6e31d0 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -2351,7 +2351,7 @@ u8 DoBattlerEndTurnEffects(void) gBattleScripting.animArg1 = gBattlerTarget; gBattleScripting.animArg2 = gBattlerAttacker; gBattleStruct->moveDamage[gBattlerAttacker] = max(1, GetNonDynamaxMaxHP(battler) / 8); - gBattleStruct->moveDamage[gBattlerTarget] = GetDrainedBigRootHp(gBattlerAttacker, gBattleStruct->moveDamage[gBattlerAttacker]); + gBattleStruct->moveDamage[gBattlerTarget] = GetDrainedBigRootHp(gBattlerTarget, gBattleStruct->moveDamage[gBattlerAttacker]); gHitMarker |= HITMARKER_IGNORE_SUBSTITUTE | HITMARKER_PASSIVE_DAMAGE; if (GetBattlerAbility(battler) == ABILITY_LIQUID_OOZE) { diff --git a/test/battle/hold_effect/big_root.c b/test/battle/hold_effect/big_root.c index 0b3d9b4af6..41dd09adc0 100644 --- a/test/battle/hold_effect/big_root.c +++ b/test/battle/hold_effect/big_root.c @@ -30,8 +30,6 @@ SINGLE_BATTLE_TEST("Big Root increases healing from absorbing moves", s16 damage SINGLE_BATTLE_TEST("Big Root increases the damage restored from Leech Seed, Ingrain and Aqua Ring", s16 heal, s16 damage) { - KNOWN_FAILING; - u32 item; u32 move; @@ -53,9 +51,9 @@ SINGLE_BATTLE_TEST("Big Root increases the damage restored from Leech Seed, Ingr HP_BAR(player, captureDamage: &results[i].heal); } FINALLY { EXPECT_EQ(results[0].damage, results[1].damage); // Damage is unaffected - EXPECT_MUL_EQ(results[1].heal, Q_4_12(5234 / 4096), results[0].heal); - EXPECT_MUL_EQ(results[3].heal, Q_4_12(5234 / 4096), results[2].heal); - EXPECT_MUL_EQ(results[5].heal, Q_4_12(5234 / 4096), results[4].heal); + EXPECT_MUL_EQ(results[0].heal, Q_4_12(1.3), results[1].heal); + EXPECT_MUL_EQ(results[2].heal, Q_4_12(1.3), results[3].heal); + EXPECT_MUL_EQ(results[4].heal, Q_4_12(1.3), results[5].heal); } } From 37f72bb2374f06bc4339d81275aac6ea0ad5d2f5 Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Fri, 18 Apr 2025 22:21:57 +0200 Subject: [PATCH 06/17] Fix Mirror Armor and Obstruct interaction (#6630)a --- src/battle_script_commands.c | 9 ++++++--- test/battle/ability/mirror_armor.c | 15 +++++++++++++++ 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index dc71bc1a25..014cef63a0 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -3698,8 +3698,8 @@ void SetMoveEffect(bool32 primary, bool32 certain) flags = MOVE_EFFECT_AFFECTS_USER | MOVE_EFFECT_CERTAIN; else flags = 0; - if (mirrorArmorReflected) - flags |= (STAT_CHANGE_ALLOW_PTR * !affectsUser); + if (mirrorArmorReflected && !affectsUser) + flags |= STAT_CHANGE_ALLOW_PTR; else flags |= STAT_CHANGE_UPDATE_MOVE_EFFECT; @@ -3753,9 +3753,12 @@ void SetMoveEffect(bool32 primary, bool32 certain) flags = 0; if (mirrorArmorReflected && !affectsUser) flags |= STAT_CHANGE_ALLOW_PTR; + else + flags |= STAT_CHANGE_UPDATE_MOVE_EFFECT; + if (ChangeStatBuffs(SET_STAT_BUFF_VALUE(2) | STAT_BUFF_NEGATIVE, gBattleScripting.moveEffect - MOVE_EFFECT_ATK_MINUS_2 + 1, - flags | STAT_CHANGE_UPDATE_MOVE_EFFECT, gBattlescriptCurrInstr + 1) == STAT_CHANGE_DIDNT_WORK) + flags, gBattlescriptCurrInstr + 1) == STAT_CHANGE_DIDNT_WORK) { if (!mirrorArmorReflected) gBattlescriptCurrInstr++; diff --git a/test/battle/ability/mirror_armor.c b/test/battle/ability/mirror_armor.c index 5aa2b55ef3..0918682d76 100644 --- a/test/battle/ability/mirror_armor.c +++ b/test/battle/ability/mirror_armor.c @@ -211,3 +211,18 @@ SINGLE_BATTLE_TEST("Mirror Armor reflects Tangling Hair speed drop") ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); } } + +SINGLE_BATTLE_TEST("Mirror Armor reflects Obstruct defense drop") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_CORVIKNIGHT) { Ability(ABILITY_MIRROR_ARMOR); } + } WHEN { + TURN { MOVE(player, MOVE_OBSTRUCT); MOVE(opponent, MOVE_TACKLE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_OBSTRUCT, player); + NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); + ABILITY_POPUP(opponent, ABILITY_MIRROR_ARMOR); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + } +} From 6b09480912a5bc1d090a4c45ee05d7cfc10c6889 Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Fri, 18 Apr 2025 22:57:04 +0200 Subject: [PATCH 07/17] Fixes some restore issues (#6629) --- data/battle_scripts_1.s | 3 +-- include/battle.h | 1 - src/battle_script_commands.c | 21 ++++++--------------- src/battle_util.c | 4 ++-- 4 files changed, 9 insertions(+), 20 deletions(-) diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index e8224b25fa..4f17840393 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -6010,7 +6010,6 @@ BattleScript_RoarSuccessSwitch_Ret: swapattackerwithtarget @ continuation of RedCardActivates restoretarget restoreattacker - restoresavedmove setbyte sSWITCH_CASE, B_SWITCH_NORMAL return @@ -9492,7 +9491,6 @@ BattleScript_RedCardActivationNoSwitch:: removeitem BS_SCRIPTING restoretarget restoreattacker - restoresavedmove return BattleScript_RedCardActivates:: @@ -9515,6 +9513,7 @@ BattleScript_RedCardIngrainContinue: waitmessage B_WAIT_TIME_LONG removeitem BS_SCRIPTING restoretarget + restoreattacker return BattleScript_RedCardSuctionCups: printstring STRINGID_PKMNANCHORSITSELFWITH diff --git a/include/battle.h b/include/battle.h index 3e4907050b..823e4813e9 100644 --- a/include/battle.h +++ b/include/battle.h @@ -834,7 +834,6 @@ struct BattleStruct u8 usedMicleBerry; struct MessageStatus slideMessageStatus; u8 trainerSlideSpriteIds[MAX_BATTLERS_COUNT]; - u16 savedMove; // backup current move for mid-turn switching, e.g. Red Card u16 opponentMonCanTera:6; u16 opponentMonCanDynamax:6; u16 padding:4; diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 014cef63a0..4837fb970e 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -7199,7 +7199,6 @@ static void Cmd_moveend(void) gLastUsedItem = gBattleMons[battler].item; SaveBattlerTarget(battler); // save battler with red card SaveBattlerAttacker(gBattlerAttacker); - gBattleStruct->savedMove = gCurrentMove; gBattleScripting.battler = battler; gEffectBattler = gBattlerAttacker; if (moveEffect == EFFECT_HIT_ESCAPE) @@ -7414,7 +7413,11 @@ static void Cmd_moveend(void) gBattleScripting.moveendState++; break; case MOVEEND_DANCER: // Special case because it's so annoying - if (IsDanceMove(gCurrentMove) && !gBattleStruct->snatchedMoveIsUsed) + if (gCurrentMove == MOVE_NONE) + originallyUsedMove = gChosenMove; // Fallback to chosen move in case attacker is switched out in the middle of an attack resolution (eg red card) + else + originallyUsedMove = gCurrentMove; + if (IsDanceMove(originallyUsedMove) && !gBattleStruct->snatchedMoveIsUsed) { u32 battler, nextDancer = 0; bool32 hasDancerTriggered = FALSE; @@ -7448,7 +7451,7 @@ static void Cmd_moveend(void) nextDancer = battler | 0x4; } } - if (nextDancer && AbilityBattleEffects(ABILITYEFFECT_MOVE_END_OTHER, nextDancer & 0x3, 0, 0, 0)) + if (nextDancer && AbilityBattleEffects(ABILITYEFFECT_MOVE_END_OTHER, nextDancer & 0x3, 0, 0, originallyUsedMove)) effect = TRUE; } } @@ -18779,15 +18782,3 @@ void BS_SetSteelsurge(void) gBattlescriptCurrInstr = cmd->nextInstr; } } - -void BS_RestoreSavedMove(void) -{ - NATIVE_ARGS(); - - if (gBattleStruct->savedMove == MOVE_NONE) - DebugPrintfLevel(MGBA_LOG_WARN, "restoresavedmove was called with no move saved!"); - - gCurrentMove = gBattleStruct->savedMove; - gBattleStruct->savedMove = MOVE_NONE; - gBattlescriptCurrInstr = cmd->nextInstr; -} diff --git a/src/battle_util.c b/src/battle_util.c index defe6e31d0..0a95f4e59c 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -6271,7 +6271,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 { case ABILITY_DANCER: if (IsBattlerAlive(battler) - && IsDanceMove(gCurrentMove) + && IsDanceMove(move) && !gSpecialStatuses[battler].dancerUsedMove && gBattlerAttacker != battler) { @@ -6279,7 +6279,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 gSpecialStatuses[battler].dancerUsedMove = TRUE; gSpecialStatuses[battler].dancerOriginalTarget = gBattleStruct->moveTarget[battler] | 0x4; gBattlerAttacker = gBattlerAbility = battler; - gCalledMove = gCurrentMove; + gCalledMove = move; // Set the target to the original target of the mon that first used a Dance move gBattlerTarget = gBattleScripting.savedBattler & 0x3; From 17b992bec5da89580c542d25682ae421c287065f Mon Sep 17 00:00:00 2001 From: hedara90 <90hedara@gmail.com> Date: Fri, 18 Apr 2025 23:16:46 +0200 Subject: [PATCH 08/17] Fixes some moves trying to load too much VRAM (#6633) Co-authored-by: Hedara --- data/battle_anim_scripts.s | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/data/battle_anim_scripts.s b/data/battle_anim_scripts.s index edd3ea89c2..39bb3a42ed 100644 --- a/data/battle_anim_scripts.s +++ b/data/battle_anim_scripts.s @@ -12326,8 +12326,6 @@ gBattleAnimMove_NaturesMadness:: loadspritegfx ANIM_TAG_SPARKLE_2 @stars PinkStarsTemplate loadspritegfx ANIM_TAG_PINK_PETAL @pink PinkRingTemplate, PinkStarsTemplate loadspritegfx ANIM_TAG_ICE_CHUNK @blue green CrystalsTemplate - loadspritegfx ANIM_TAG_CIRCLE_OF_LIGHT @charge - loadspritegfx ANIM_TAG_TEAL_ALERT @charge particles monbg ANIM_ATTACKER setalpha 14, 8 delay 0x1 @@ -12374,6 +12372,8 @@ gBattleAnimMove_NaturesMadness:: createsprite gNaturesMadnessGrayRingTemplate, ANIM_ATTACKER, 40, 0x0, 0x0, 0x1, 0x0 playsewithpan SE_M_HEAL_BELL, SOUND_PAN_ATTACKER waitforvisualfinish + unloadspritegfx ANIM_TAG_ECLIPSING_ORB + loadspritegfx ANIM_TAG_SPARKLE_2 playsewithpan SE_M_EXPLOSION, SOUND_PAN_ATTACKER createsprite gNaturesMadnessPinkRingTemplate, ANIM_ATTACKER, 3, 0x0, 0x0, 0x1, 0x0, 0x1F, 0x8 createsprite gNaturesMadnessPinkStarsTemplate, ANIM_ATTACKER, 2, 0xa, 0xa, 0x19, 0x1 @@ -16168,7 +16168,6 @@ SandsearStormFireSpin: gBattleAnimMove_LunarBlessing:: loadspritegfx ANIM_TAG_BLUE_STAR loadspritegfx ANIM_TAG_MOON - loadspritegfx ANIM_TAG_SPARKLE_2 loadspritegfx ANIM_TAG_GUARD_RING loadspritegfx ANIM_TAG_SMALL_EMBER @Yellow colour for ring monbg ANIM_ATK_PARTNER From d342b373f086d470af2648a1a841934ecaa2ade1 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Sat, 19 Apr 2025 07:15:13 -0500 Subject: [PATCH 09/17] Fix no eligible mons when only the partner wins in multi battle (#6626) --- src/battle_script_commands.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 4837fb970e..4e090e3257 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -5344,6 +5344,7 @@ bool32 NoAliveMonsForPlayer(void) u32 i; u32 maxI = PARTY_SIZE; u32 HP_count = 0; + u32 ineligibleMonsCount = 0; if (B_MULTI_BATTLE_WHITEOUT < GEN_4 && gBattleTypeFlags & (BATTLE_TYPE_MULTI | BATTLE_TYPE_INGAME_PARTNER)) maxI = MULTI_PARTY_SIZE; @@ -5356,6 +5357,28 @@ bool32 NoAliveMonsForPlayer(void) { HP_count += GetMonData(&gPlayerParty[i], MON_DATA_HP); } + + // Get the number of fainted mons or eggs (not empty slots) in the first three party slots. + if (i < 3 && ((GetMonData(&gPlayerParty[i], MON_DATA_SPECIES) && !GetMonData(&gPlayerParty[i], MON_DATA_HP)) + || GetMonData(&gPlayerParty[i], MON_DATA_IS_EGG))) + ineligibleMonsCount++; + } + + // Get the number of inelligible slots in the saved player party. + if (B_MULTI_BATTLE_WHITEOUT > GEN_3 && gBattleTypeFlags & (BATTLE_TYPE_MULTI | BATTLE_TYPE_INGAME_PARTNER) + && !(gBattleTypeFlags & BATTLE_TYPE_ARENA)) + { + for (i = 0; i < PARTY_SIZE; i++) + { + if (!GetMonData(&gSaveBlock1Ptr->playerParty[i], MON_DATA_SPECIES) + || !GetMonData(&gSaveBlock1Ptr->playerParty[i], MON_DATA_HP) + || GetMonData(&gSaveBlock1Ptr->playerParty[i], MON_DATA_IS_EGG)) + ineligibleMonsCount++; + } + + // If the total number of ineligible mons is 6 or more, lose the battle. + if (ineligibleMonsCount >= 6) + return TRUE; } return (HP_count == 0); From bd64ab599b09798612661e1e00e11f31eda260b9 Mon Sep 17 00:00:00 2001 From: PhallenTree <168426989+PhallenTree@users.noreply.github.com> Date: Sat, 19 Apr 2025 19:24:00 +0100 Subject: [PATCH 10/17] Fixes Ally Switch cancelling partner's moves targeting that side of the field (#6647) --- src/battle_anim_effects_1.c | 10 +++++++--- src/battle_util.c | 20 +++++++++++++++----- test/battle/move_effect/acupressure.c | 26 +++++++++++++++++++++++--- test/battle/move_effect/ally_switch.c | 18 ++++++++++++++++++ 4 files changed, 63 insertions(+), 11 deletions(-) diff --git a/src/battle_anim_effects_1.c b/src/battle_anim_effects_1.c index 8e1e39d629..96e0aad14a 100644 --- a/src/battle_anim_effects_1.c +++ b/src/battle_anim_effects_1.c @@ -6914,11 +6914,11 @@ static void AnimTask_AllySwitchDataSwap(u8 taskId) SWAP(gMoveSelectionCursor[battlerAtk], gMoveSelectionCursor[battlerPartner], temp); // Swap turn order, so that all the battlers take action SWAP(gChosenActionByBattler[battlerAtk], gChosenActionByBattler[battlerPartner], temp); - for (i = 0; i < MAX_BATTLERS_COUNT; i++) + for (i = 0; i < gBattlersCount; i++) { if (gBattlerByTurnOrder[i] == battlerAtk || gBattlerByTurnOrder[i] == battlerPartner) { - for (j = i + 1; j < MAX_BATTLERS_COUNT; j++) + for (j = i + 1; j < gBattlersCount; j++) { if (gBattlerByTurnOrder[j] == battlerAtk || gBattlerByTurnOrder[j] == battlerPartner) break; @@ -6938,9 +6938,13 @@ static void AnimTask_AllySwitchDataSwap(u8 taskId) TrySwapWishBattlerIds(battlerAtk, battlerPartner); // For Snipe Shot and abilities Stalwart/Propeller Tail - keep the original target. - for (i = 0; i < MAX_BATTLERS_COUNT; i++) + for (i = 0; i < gBattlersCount; i++) { u16 ability = GetBattlerAbility(i); + // if not targeting a slot that got switched, continue + if (!IsBattlerAlly(gBattleStruct->moveTarget[i], battlerAtk)) + continue; + if (gChosenMoveByBattler[i] == MOVE_SNIPE_SHOT || ability == ABILITY_PROPELLER_TAIL || ability == ABILITY_STALWART) gBattleStruct->moveTarget[i] ^= BIT_FLANK; } diff --git a/src/battle_util.c b/src/battle_util.c index 0a95f4e59c..d38135dbb2 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -309,7 +309,7 @@ bool32 HandleMoveTargetRedirection(void) // Functions void HandleAction_UseMove(void) { - u32 i; + u32 i, moveTarget; gBattlerAttacker = gBattlerByTurnOrder[gCurrentTurnActionNumber]; if (gBattleStruct->battlerState[gBattlerAttacker].absentBattlerFlags @@ -393,9 +393,10 @@ void HandleAction_UseMove(void) gCurrentMove = gChosenMove = GetMaxMove(gBattlerAttacker, gCurrentMove); } + moveTarget = GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove); + if (!HandleMoveTargetRedirection()) { - u32 moveTarget = GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove); if (IsDoubleBattle() && moveTarget & MOVE_TARGET_RANDOM) { gBattlerTarget = SetRandomTarget(gBattlerAttacker); @@ -407,7 +408,7 @@ void HandleAction_UseMove(void) } else if (moveTarget == MOVE_TARGET_ALLY) { - if (IsBattlerAlive(BATTLE_PARTNER(gBattlerAttacker))) + if (IsBattlerAlive(BATTLE_PARTNER(gBattlerAttacker)) && !gProtectStructs[BATTLE_PARTNER(gBattlerAttacker)].usedAllySwitch) gBattlerTarget = BATTLE_PARTNER(gBattlerAttacker); else gBattlerTarget = gBattlerAttacker; @@ -422,6 +423,10 @@ void HandleAction_UseMove(void) break; } } + else if (moveTarget == MOVE_TARGET_USER) + { + gBattlerTarget = gBattlerAttacker; + } else { gBattlerTarget = *(gBattleStruct->moveTarget + gBattlerAttacker); @@ -455,8 +460,13 @@ void HandleAction_UseMove(void) } } - if ((IsBattlerAlly(gBattlerAttacker, gBattlerTarget)) - && (!IsBattlerAlive(gBattlerTarget) || gProtectStructs[BATTLE_PARTNER(gBattlerAttacker)].usedAllySwitch)) + if (IsBattlerAlly(gBattlerAttacker, gBattlerTarget) && !IsBattlerAlive(gBattlerTarget)) + { + gBattlescriptCurrInstr = BattleScript_FailedFromAtkCanceler; + } + // If originally targetting an ally but now targetting user due to Ally Switch + else if (moveTarget & MOVE_TARGET_ALLY && gBattlerAttacker == gBattlerTarget + && gProtectStructs[BATTLE_PARTNER(gBattlerAttacker)].usedAllySwitch) { gBattlescriptCurrInstr = BattleScript_FailedFromAtkCanceler; } diff --git a/test/battle/move_effect/acupressure.c b/test/battle/move_effect/acupressure.c index d59a3faee3..02be60725d 100644 --- a/test/battle/move_effect/acupressure.c +++ b/test/battle/move_effect/acupressure.c @@ -16,20 +16,40 @@ DOUBLE_BATTLE_TEST("Acupressure fails on the user if it targeted its ally but sw OPPONENT(SPECIES_KADABRA); OPPONENT(SPECIES_ABRA); } WHEN { - TURN { MOVE(playerLeft, MOVE_ALLY_SWITCH); MOVE(playerRight, MOVE_ACUPRESSURE, target:playerLeft); } + TURN { MOVE(playerLeft, MOVE_ALLY_SWITCH); MOVE(playerRight, MOVE_ACUPRESSURE, target: playerLeft); } } SCENE { MESSAGE("Wobbuffet used Ally Switch!"); ANIMATION(ANIM_TYPE_MOVE, MOVE_ALLY_SWITCH, playerLeft); MESSAGE("Wobbuffet and Wynaut switched places!"); - + MESSAGE("Wynaut used Acupressure!"); MESSAGE("But it failed!"); NONE_OF { - ANIMATION(ANIM_TYPE_MOVE, MOVE_ACUPRESSURE); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ACUPRESSURE, playerLeft); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft); } } } +DOUBLE_BATTLE_TEST("Acupressure works on the ally if it targeted itself but switched positions via Ally Switch") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_KADABRA); + OPPONENT(SPECIES_ABRA); + } WHEN { + TURN { MOVE(playerLeft, MOVE_ALLY_SWITCH); MOVE(playerRight, MOVE_ACUPRESSURE, target: playerRight); } + } SCENE { + MESSAGE("Wobbuffet used Ally Switch!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ALLY_SWITCH, playerLeft); + MESSAGE("Wobbuffet and Wynaut switched places!"); + MESSAGE("Wynaut used Acupressure!"); + NOT MESSAGE("But it failed!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ACUPRESSURE, playerLeft); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight); + } +} + TO_DO_BATTLE_TEST("Acupressure works on the user if its side is protected by Crafty Shield"); TO_DO_BATTLE_TEST("Acupressure fails on the ally if its side is protected by Crafty Shield"); diff --git a/test/battle/move_effect/ally_switch.c b/test/battle/move_effect/ally_switch.c index 520de60106..ba2ac1a053 100644 --- a/test/battle/move_effect/ally_switch.c +++ b/test/battle/move_effect/ally_switch.c @@ -167,6 +167,24 @@ DOUBLE_BATTLE_TEST("Ally Switch - move fails if the target was ally which change } } +DOUBLE_BATTLE_TEST("Ally Switch doesn't make self-targeting status moves fail") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_HARDEN].target == MOVE_TARGET_USER); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { MOVE(playerLeft, MOVE_ALLY_SWITCH); MOVE(playerRight, MOVE_HARDEN); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_ALLY_SWITCH, playerLeft); + ANIMATION(ANIM_TYPE_MOVE, MOVE_HARDEN, playerLeft); + } THEN { + EXPECT_EQ(playerLeft->statStages[STAT_DEF], DEFAULT_STAT_STAGE + 1); + } +} + DOUBLE_BATTLE_TEST("Ally Switch increases the Protect-like moves counter") { GIVEN { From c650199909e2cd7bd47654cdd4f16c453ac9dca6 Mon Sep 17 00:00:00 2001 From: Eduardo Quezada Date: Sun, 20 Apr 2025 16:36:27 -0400 Subject: [PATCH 11/17] Added missing `gMovesInfo` encapsulation (#6656) --- include/move.h | 27 ++++++++++++++---------- src/apprentice.c | 2 +- src/battle_ai_util.c | 4 ++-- src/battle_script_commands.c | 6 +++--- test/battle/ability/color_change.c | 4 ++-- test/battle/ability/intimidate.c | 2 +- test/battle/ability/neutralizing_gas.c | 10 ++++----- test/battle/ability/thermal_exchange.c | 10 ++++----- test/battle/ai/ai.c | 4 ++-- test/battle/hold_effect/shell_bell.c | 2 +- test/battle/move_effect/dream_eater.c | 10 ++++----- test/battle/move_effect/follow_me.c | 4 ++-- test/battle/move_effect/instruct.c | 10 ++++----- test/battle/move_effect/laser_focus.c | 2 +- test/battle/move_effect/recoil_if_miss.c | 2 +- test/battle/move_effect/rototiller.c | 6 +++--- test/battle/move_effect/sleep_talk.c | 8 +++---- test/battle/move_effect/snore.c | 4 ++-- test/battle/move_effect/spectral_thief.c | 8 +++---- test/battle/move_flags/recoil.c | 2 +- test/test_runner_battle.c | 18 ++++++++-------- 21 files changed, 75 insertions(+), 70 deletions(-) diff --git a/include/move.h b/include/move.h index 5537840703..743889850e 100644 --- a/include/move.h +++ b/include/move.h @@ -137,7 +137,7 @@ struct MoveInfo const u8 *battleAnimScript; }; -extern const struct MoveInfo gMovesInfo[]; +extern const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL]; extern const u8 gNotDoneYetDescription[]; extern const struct BattleMoveEffect gBattleMoveEffects[]; @@ -154,19 +154,19 @@ static inline const u8 *GetMoveName(u32 moveId) return gMovesInfo[SanitizeMoveId(moveId)].name; } -static inline const u8 *GetMoveDescription(u32 moveId) -{ - moveId = SanitizeMoveId(moveId); - if (gMovesInfo[moveId].effect == EFFECT_PLACEHOLDER) - return gNotDoneYetDescription; - return gMovesInfo[moveId].description; -} - static inline u32 GetMoveEffect(u32 moveId) { return gMovesInfo[SanitizeMoveId(moveId)].effect; } +static inline const u8 *GetMoveDescription(u32 moveId) +{ + moveId = SanitizeMoveId(moveId); + if (GetMoveEffect(moveId) == EFFECT_PLACEHOLDER) + return gNotDoneYetDescription; + return gMovesInfo[moveId].description; +} + static inline u32 GetMoveType(u32 moveId) { return gMovesInfo[SanitizeMoveId(moveId)].type; @@ -437,6 +437,11 @@ static inline bool32 IsMoveSketchBanned(u32 moveId) return gMovesInfo[SanitizeMoveId(moveId)].sketchBanned; } +static inline bool32 IsValidApprenticeMove(u32 moveId) +{ + return gMovesInfo[SanitizeMoveId(moveId)].validApprenticeMove; +} + static inline u32 GetMoveTwoTurnAttackStringId(u32 moveId) { return gMovesInfo[SanitizeMoveId(moveId)].argument.twoTurnAttack.stringId; @@ -529,12 +534,12 @@ static inline const u8 *GetMoveAnimationScript(u32 moveId) static inline const u8 *GetMoveBattleScript(u32 moveId) { moveId = SanitizeMoveId(moveId); - if (gBattleMoveEffects[gMovesInfo[moveId].effect].battleScript == NULL) + if (gBattleMoveEffects[GetMoveEffect(moveId)].battleScript == NULL) { DebugPrintfLevel(MGBA_LOG_WARN, "No effect for moveId=%u", moveId); return gBattleMoveEffects[EFFECT_PLACEHOLDER].battleScript; } - return gBattleMoveEffects[gMovesInfo[moveId].effect].battleScript; + return gBattleMoveEffects[GetMoveEffect(moveId)].battleScript; } #endif // GUARD_MOVES_H diff --git a/src/apprentice.c b/src/apprentice.c index 7769dde6e3..7dcca7b28b 100644 --- a/src/apprentice.c +++ b/src/apprentice.c @@ -411,7 +411,7 @@ static u16 GetRandomAlternateMove(u8 monId) if (TrySetMove(monId, moveId)) { - if (gMovesInfo[moveId].validApprenticeMove) + if (IsValidApprenticeMove(moveId)) break; i++; } diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index ca94655476..6478950a85 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -1465,7 +1465,7 @@ bool32 IsConfusionMoveEffect(u32 moveEffect) bool32 IsHazardMove(u32 move) { // Hazard setting moves like Stealth Rock, Spikes, etc. - u32 i, moveEffect = gMovesInfo[move].effect; + u32 i, moveEffect = GetMoveEffect(move); switch (moveEffect) { case EFFECT_SPIKES: @@ -1491,7 +1491,7 @@ bool32 IsHazardMove(u32 move) bool32 IsHazardClearingMove(u32 move) { // Hazard clearing effects like Rapid Spin, Tidy Up, etc. - u32 i, moveEffect = gMovesInfo[move].effect; + u32 i, moveEffect = GetMoveEffect(move); switch (moveEffect) { case EFFECT_RAPID_SPIN: diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 4e090e3257..8b7e2702fd 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -1198,7 +1198,7 @@ static void Cmd_attackcanceler(void) if (AbilityBattleEffects(ABILITYEFFECT_MOVES_BLOCK, gBattlerTarget, 0, 0, 0)) return; - if (gMovesInfo[gCurrentMove].effect == EFFECT_PARALYZE && AbilityBattleEffects(ABILITYEFFECT_ABSORBING, gBattlerTarget, 0, 0, gCurrentMove)) + if (GetMoveEffect(gCurrentMove) == EFFECT_PARALYZE && AbilityBattleEffects(ABILITYEFFECT_ABSORBING, gBattlerTarget, 0, 0, gCurrentMove)) return; if (IsMovePowderBlocked(gBattlerAttacker, gBattlerTarget, gCurrentMove)) return; @@ -2423,7 +2423,7 @@ static void Cmd_attackanimation(void) } // handle special move animations - if (gMovesInfo[gCurrentMove].effect == EFFECT_EXPANDING_FORCE && moveTarget & MOVE_TARGET_BOTH && CountAliveMonsInBattle(BATTLE_ALIVE_SIDE, BATTLE_OPPOSITE(gBattlerAttacker) > 1)) + if (GetMoveEffect(gCurrentMove) == EFFECT_EXPANDING_FORCE && moveTarget & MOVE_TARGET_BOTH && CountAliveMonsInBattle(BATTLE_ALIVE_SIDE, BATTLE_OPPOSITE(gBattlerAttacker) > 1)) gBattleScripting.animTurn = 1; if (!(moveResultFlags & MOVE_RESULT_NO_EFFECT)) @@ -6475,7 +6475,7 @@ static void Cmd_moveend(void) gBattleScripting.moveendState++; break; } - else if (gMovesInfo[gCurrentMove].effect == EFFECT_RECOIL_IF_MISS + else if (GetMoveEffect(gCurrentMove) == EFFECT_RECOIL_IF_MISS && (!IsBattlerTurnDamaged(gBattlerTarget) || gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_NO_EFFECT) && !gBattleStruct->noTargetPresent && IsBattlerAlive(gBattlerAttacker)) diff --git a/test/battle/ability/color_change.c b/test/battle/ability/color_change.c index cc0a3e2472..42932a7d58 100644 --- a/test/battle/ability/color_change.c +++ b/test/battle/ability/color_change.c @@ -5,7 +5,7 @@ SINGLE_BATTLE_TEST("Color Change changes the type of a Pokemon being hit by a mo { GIVEN { ASSUME(gSpeciesInfo[SPECIES_KECLEON].types[0] != TYPE_PSYCHIC && gSpeciesInfo[SPECIES_KECLEON].types[1] != TYPE_PSYCHIC); - ASSUME(gMovesInfo[MOVE_PSYWAVE].type == TYPE_PSYCHIC); + ASSUME(GetMoveType(MOVE_PSYWAVE) == TYPE_PSYCHIC); PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_KECLEON) { Ability(ABILITY_COLOR_CHANGE); } } WHEN { @@ -21,7 +21,7 @@ SINGLE_BATTLE_TEST("Color Change does not change the type when hit by a move tha { GIVEN { ASSUME(gSpeciesInfo[SPECIES_KECLEON].types[0] == TYPE_NORMAL || gSpeciesInfo[SPECIES_KECLEON].types[1] == TYPE_NORMAL); - ASSUME(gMovesInfo[MOVE_TACKLE].type == TYPE_NORMAL); + ASSUME(GetMoveType(MOVE_TACKLE) == TYPE_NORMAL); PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_KECLEON) { Ability(ABILITY_COLOR_CHANGE); } } WHEN { diff --git a/test/battle/ability/intimidate.c b/test/battle/ability/intimidate.c index 61b53f42e1..dd1f761dee 100644 --- a/test/battle/ability/intimidate.c +++ b/test/battle/ability/intimidate.c @@ -379,7 +379,7 @@ DOUBLE_BATTLE_TEST("Intimidate will correctly decrease the attack of the second SINGLE_BATTLE_TEST("Intimdate does not lose timing after mega evolution and switch out by a hit escape move") { GIVEN { - ASSUME(gMovesInfo[MOVE_U_TURN].effect == EFFECT_HIT_ESCAPE); + ASSUME(GetMoveEffect(MOVE_U_TURN) == EFFECT_HIT_ESCAPE); PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_MANECTRIC) { Item(ITEM_MANECTITE); } OPPONENT(SPECIES_ARBOK) { Ability(ABILITY_INTIMIDATE); } diff --git a/test/battle/ability/neutralizing_gas.c b/test/battle/ability/neutralizing_gas.c index ef83483d32..ae11c458f7 100644 --- a/test/battle/ability/neutralizing_gas.c +++ b/test/battle/ability/neutralizing_gas.c @@ -51,7 +51,7 @@ DOUBLE_BATTLE_TEST("Neutralizing Gas prevents ally's switch-in ability from acti DOUBLE_BATTLE_TEST("Neutralizing Gas ignores all battlers' ability effects") { GIVEN { - ASSUME(gMovesInfo[MOVE_SURF].target == MOVE_TARGET_FOES_AND_ALLY); + ASSUME(GetMoveTarget(MOVE_SURF) == MOVE_TARGET_FOES_AND_ALLY); PLAYER(SPECIES_WEEZING) { Ability(ABILITY_NEUTRALIZING_GAS); } PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_TELEPATHY); } OPPONENT(SPECIES_LANTURN) { Ability(ABILITY_WATER_ABSORB); } @@ -87,7 +87,7 @@ SINGLE_BATTLE_TEST("Neutralizing Gas ignores multipliers from attacker's ability PARAMETRIZE { ability = ABILITY_NEUTRALIZING_GAS; } PARAMETRIZE { ability = ABILITY_LEVITATE; } GIVEN { - ASSUME(gMovesInfo[MOVE_TACKLE].category == DAMAGE_CATEGORY_PHYSICAL); + ASSUME(GetMoveCategory(MOVE_TACKLE) == DAMAGE_CATEGORY_PHYSICAL); PLAYER(SPECIES_WEEZING) { Ability(ability); } OPPONENT(SPECIES_AZUMARILL) { Ability(ABILITY_HUGE_POWER); } } WHEN { @@ -106,8 +106,8 @@ SINGLE_BATTLE_TEST("Neutralizing Gas ignores multipliers from target's ability", PARAMETRIZE { ability = ABILITY_NEUTRALIZING_GAS; } PARAMETRIZE { ability = ABILITY_LEVITATE; } GIVEN { - ASSUME(gMovesInfo[MOVE_TACKLE].makesContact == TRUE); - ASSUME(gMovesInfo[MOVE_TACKLE].type == TYPE_NORMAL); + ASSUME(MoveMakesContact(MOVE_TACKLE) == TRUE); + ASSUME(GetMoveType(MOVE_TACKLE) == TYPE_NORMAL); PLAYER(SPECIES_WEEZING) { Ability(ability); } OPPONENT(SPECIES_BEWEAR) { Ability(ABILITY_FLUFFY); } } WHEN { @@ -146,7 +146,7 @@ DOUBLE_BATTLE_TEST("Neutralizing Gas ignores multipliers from ally's ability", s PARAMETRIZE { ability = ABILITY_NEUTRALIZING_GAS; } PARAMETRIZE { ability = ABILITY_LEVITATE; } GIVEN { - ASSUME(gMovesInfo[MOVE_TACKLE].category == DAMAGE_CATEGORY_PHYSICAL); + ASSUME(GetMoveCategory(MOVE_TACKLE) == DAMAGE_CATEGORY_PHYSICAL); PLAYER(SPECIES_WEEZING) { Ability(ability); } PLAYER(SPECIES_WO_CHIEN) { Ability(ABILITY_TABLETS_OF_RUIN); } OPPONENT(SPECIES_WOBBUFFET) { Ability(ABILITY_TELEPATHY); } diff --git a/test/battle/ability/thermal_exchange.c b/test/battle/ability/thermal_exchange.c index 57f4256d7a..7484fa207c 100644 --- a/test/battle/ability/thermal_exchange.c +++ b/test/battle/ability/thermal_exchange.c @@ -4,7 +4,7 @@ SINGLE_BATTLE_TEST("Thermal Exchange makes Will-O-Wisp fail") { GIVEN { - ASSUME(gMovesInfo[MOVE_WILL_O_WISP].effect == EFFECT_WILL_O_WISP); + ASSUME(GetMoveEffect(MOVE_WILL_O_WISP) == EFFECT_WILL_O_WISP); PLAYER(SPECIES_BAXCALIBUR) { Ability(ABILITY_THERMAL_EXCHANGE); } OPPONENT(SPECIES_WOBBUFFET); } WHEN { @@ -37,8 +37,8 @@ SINGLE_BATTLE_TEST("Thermal Exchange prevents the user from getting burned when SINGLE_BATTLE_TEST("Thermal Exchange cures burns when acquired") { GIVEN { - ASSUME(gMovesInfo[MOVE_WILL_O_WISP].effect == EFFECT_WILL_O_WISP); - ASSUME(gMovesInfo[MOVE_SKILL_SWAP].effect == EFFECT_SKILL_SWAP); + ASSUME(GetMoveEffect(MOVE_WILL_O_WISP) == EFFECT_WILL_O_WISP); + ASSUME(GetMoveEffect(MOVE_SKILL_SWAP) == EFFECT_SKILL_SWAP); PLAYER(SPECIES_BAXCALIBUR) { Ability(ABILITY_THERMAL_EXCHANGE); } OPPONENT(SPECIES_WOBBUFFET); } WHEN { @@ -56,7 +56,7 @@ SINGLE_BATTLE_TEST("Thermal Exchange cures burns when acquired") SINGLE_BATTLE_TEST("Thermal Exchange burn prevention can be bypassed with Mold Breaker but is cured after") { GIVEN { - ASSUME(gMovesInfo[MOVE_WILL_O_WISP].effect == EFFECT_WILL_O_WISP); + ASSUME(GetMoveEffect(MOVE_WILL_O_WISP) == EFFECT_WILL_O_WISP); PLAYER(SPECIES_BAXCALIBUR) { Ability(ABILITY_THERMAL_EXCHANGE); } OPPONENT(SPECIES_RAMPARDOS) { Ability(ABILITY_MOLD_BREAKER); } } WHEN { @@ -73,7 +73,7 @@ SINGLE_BATTLE_TEST("Thermal Exchange burn prevention can be bypassed with Mold B SINGLE_BATTLE_TEST("Thermal Exchange boosts attack if hit by a damaging fire type move") { GIVEN { - ASSUME(gMovesInfo[MOVE_EMBER].type == TYPE_FIRE); + ASSUME(GetMoveType(MOVE_EMBER) == TYPE_FIRE); PLAYER(SPECIES_BAXCALIBUR) { Ability(ABILITY_THERMAL_EXCHANGE); } OPPONENT(SPECIES_WOBBUFFET); } WHEN { diff --git a/test/battle/ai/ai.c b/test/battle/ai/ai.c index d11f146fc1..6f56e608b3 100644 --- a/test/battle/ai/ai.c +++ b/test/battle/ai/ai.c @@ -835,8 +835,8 @@ AI_SINGLE_BATTLE_TEST("AI stays choice locked into moves in spite of the player' AI_SINGLE_BATTLE_TEST("AI won't use Sucker Punch if it expects a move of the same priority bracket and the opponent is faster") { GIVEN { - ASSUME(gMovesInfo[MOVE_QUICK_ATTACK].priority == 1); - ASSUME(gMovesInfo[MOVE_SUCKER_PUNCH].priority == 1); + ASSUME(GetMovePriority(MOVE_QUICK_ATTACK) == 1); + ASSUME(GetMovePriority(MOVE_SUCKER_PUNCH) == 1); AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT); PLAYER(SPECIES_WOBBUFFET) { Speed(300); Moves(MOVE_QUICK_ATTACK); } OPPONENT(SPECIES_WOBBUFFET) { Speed(100); Moves(MOVE_SUCKER_PUNCH, MOVE_TACKLE); } diff --git a/test/battle/hold_effect/shell_bell.c b/test/battle/hold_effect/shell_bell.c index f669c55081..8e91011865 100644 --- a/test/battle/hold_effect/shell_bell.c +++ b/test/battle/hold_effect/shell_bell.c @@ -208,7 +208,7 @@ DOUBLE_BATTLE_TEST("Shell Bell heals accumulated damage for spread moves") const u16 maxHp = 200; const u16 initHp = 1; GIVEN { - ASSUME(gMovesInfo[MOVE_DISCHARGE].target == MOVE_TARGET_FOES_AND_ALLY); + ASSUME(GetMoveTarget(MOVE_DISCHARGE) == MOVE_TARGET_FOES_AND_ALLY); PLAYER(SPECIES_ARIADOS) { MaxHP(maxHp); HP(initHp); Item(ITEM_SHELL_BELL); } PLAYER(SPECIES_WOBBUFFET) {} OPPONENT(SPECIES_GYARADOS) {} diff --git a/test/battle/move_effect/dream_eater.c b/test/battle/move_effect/dream_eater.c index 67b2ed5af5..caa365a17a 100644 --- a/test/battle/move_effect/dream_eater.c +++ b/test/battle/move_effect/dream_eater.c @@ -77,9 +77,9 @@ SINGLE_BATTLE_TEST("Dream Eater works on targets with Comatose") SINGLE_BATTLE_TEST("Dream Eater fails if the target is behind a Substitute (Gen 1-4)") { GIVEN { - ASSUME(gMovesInfo[MOVE_YAWN].effect == EFFECT_YAWN); - ASSUME(gMovesInfo[MOVE_SUBSTITUTE].effect == EFFECT_SUBSTITUTE); - ASSUME(!gMovesInfo[MOVE_DREAM_EATER].ignoresSubstitute); + ASSUME(GetMoveEffect(MOVE_YAWN) == EFFECT_YAWN); + ASSUME(GetMoveEffect(MOVE_SUBSTITUTE) == EFFECT_SUBSTITUTE); + ASSUME(!MoveIgnoresSubstitute(MOVE_DREAM_EATER)); PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET); } WHEN { @@ -97,8 +97,8 @@ SINGLE_BATTLE_TEST("Dream Eater works if the target is behind a Substitute (Gen s16 damage; s16 healed; GIVEN { - ASSUME(gMovesInfo[MOVE_YAWN].effect == EFFECT_YAWN); - ASSUME(gMovesInfo[MOVE_SUBSTITUTE].effect == EFFECT_SUBSTITUTE); + ASSUME(GetMoveEffect(MOVE_YAWN) == EFFECT_YAWN); + ASSUME(GetMoveEffect(MOVE_SUBSTITUTE) == EFFECT_SUBSTITUTE); PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET) { HP(1); } } WHEN { diff --git a/test/battle/move_effect/follow_me.c b/test/battle/move_effect/follow_me.c index fe7b96207d..f24629f141 100644 --- a/test/battle/move_effect/follow_me.c +++ b/test/battle/move_effect/follow_me.c @@ -3,8 +3,8 @@ ASSUMPTIONS { - ASSUME(gMovesInfo[MOVE_FOLLOW_ME].effect == EFFECT_FOLLOW_ME); - ASSUME(gMovesInfo[MOVE_SPOTLIGHT].effect == EFFECT_FOLLOW_ME); + ASSUME(GetMoveEffect(MOVE_FOLLOW_ME) == EFFECT_FOLLOW_ME); + ASSUME(GetMoveEffect(MOVE_SPOTLIGHT) == EFFECT_FOLLOW_ME); } DOUBLE_BATTLE_TEST("Follow Me redirects single target moves used by opponents to user") diff --git a/test/battle/move_effect/instruct.c b/test/battle/move_effect/instruct.c index 85f56d4c4b..e61540b9c5 100644 --- a/test/battle/move_effect/instruct.c +++ b/test/battle/move_effect/instruct.c @@ -249,8 +249,8 @@ DOUBLE_BATTLE_TEST("Instructed move will be redirected by Follow Me after instru PARAMETRIZE { moveTarget = opponentLeft; } PARAMETRIZE { moveTarget = opponentRight; } GIVEN { - ASSUME(gMovesInfo[MOVE_FOLLOW_ME].effect == EFFECT_FOLLOW_ME); - ASSUME(gMovesInfo[MOVE_SKILL_SWAP].effect == EFFECT_SKILL_SWAP); + ASSUME(GetMoveEffect(MOVE_FOLLOW_ME) == EFFECT_FOLLOW_ME); + ASSUME(GetMoveEffect(MOVE_SKILL_SWAP) == EFFECT_SKILL_SWAP); PLAYER(SPECIES_DURALUDON) { Ability(ABILITY_STALWART); } PLAYER(SPECIES_DURALUDON) { Ability(ABILITY_STALWART); } OPPONENT(SPECIES_WOBBUFFET); @@ -279,9 +279,9 @@ DOUBLE_BATTLE_TEST("Instructed move will be redirected by Rage Powder after inst PARAMETRIZE { moveTarget = opponentLeft; } PARAMETRIZE { moveTarget = opponentRight; } GIVEN { - ASSUME(gMovesInfo[MOVE_RAGE_POWDER].effect == EFFECT_FOLLOW_ME); - ASSUME(gMovesInfo[MOVE_RAGE_POWDER].powderMove == TRUE); - ASSUME(gMovesInfo[MOVE_SOAK].effect == EFFECT_SOAK); + ASSUME(GetMoveEffect(MOVE_RAGE_POWDER) == EFFECT_FOLLOW_ME); + ASSUME(IsPowderMove(MOVE_RAGE_POWDER) == TRUE); + ASSUME(GetMoveEffect(MOVE_SOAK) == EFFECT_SOAK); PLAYER(SPECIES_TREECKO); PLAYER(SPECIES_SCEPTILE); OPPONENT(SPECIES_WOBBUFFET); diff --git a/test/battle/move_effect/laser_focus.c b/test/battle/move_effect/laser_focus.c index c486a3cbee..17922d3231 100644 --- a/test/battle/move_effect/laser_focus.c +++ b/test/battle/move_effect/laser_focus.c @@ -3,7 +3,7 @@ ASSUMPTIONS { - ASSUME(gMovesInfo[MOVE_LASER_FOCUS].effect == EFFECT_LASER_FOCUS); + ASSUME(GetMoveEffect(MOVE_LASER_FOCUS) == EFFECT_LASER_FOCUS); } SINGLE_BATTLE_TEST("Laser Focus causes the user's move used on the next turn to result in a Critical Hit") diff --git a/test/battle/move_effect/recoil_if_miss.c b/test/battle/move_effect/recoil_if_miss.c index 0b65eb8541..a5dbfef178 100644 --- a/test/battle/move_effect/recoil_if_miss.c +++ b/test/battle/move_effect/recoil_if_miss.c @@ -102,7 +102,7 @@ SINGLE_BATTLE_TEST("Recoil if miss: Jump Kick's recoil happens after Spiky Shiel SINGLE_BATTLE_TEST("Recoil if miss: Jump Kick recoil happens after Spiky Shield damage") { GIVEN { - ASSUME(!gMovesInfo[MOVE_JUMP_KICK].ignoresProtect); + ASSUME(!MoveIgnoresProtect(MOVE_JUMP_KICK)); PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET); } WHEN { diff --git a/test/battle/move_effect/rototiller.c b/test/battle/move_effect/rototiller.c index 32ae7da75f..34b62a2666 100644 --- a/test/battle/move_effect/rototiller.c +++ b/test/battle/move_effect/rototiller.c @@ -3,7 +3,7 @@ ASSUMPTIONS { - ASSUME(gMovesInfo[MOVE_ROTOTILLER].effect == EFFECT_ROTOTILLER); + ASSUME(GetMoveEffect(MOVE_ROTOTILLER) == EFFECT_ROTOTILLER); } DOUBLE_BATTLE_TEST("Rototiller boosts Attack and Special Attack of all Grass types on the field") @@ -55,7 +55,7 @@ SINGLE_BATTLE_TEST("Rototiller doesn't affect pokemon that are semi-invulnerable { GIVEN { ASSUME(gSpeciesInfo[SPECIES_TANGELA].types[0] == TYPE_GRASS); - ASSUME(gMovesInfo[MOVE_DIG].effect == EFFECT_SEMI_INVULNERABLE); + ASSUME(GetMoveEffect(MOVE_DIG) == EFFECT_SEMI_INVULNERABLE); PLAYER(SPECIES_TANGELA); OPPONENT(SPECIES_TANGELA); } WHEN { @@ -79,7 +79,7 @@ SINGLE_BATTLE_TEST("Rototiller fails if the only valid target is semi-invulnerab ASSUME(gSpeciesInfo[SPECIES_TANGELA].types[0] == TYPE_GRASS); ASSUME(gSpeciesInfo[SPECIES_WOBBUFFET].types[0] != TYPE_GRASS); ASSUME(gSpeciesInfo[SPECIES_WOBBUFFET].types[1] != TYPE_GRASS); - ASSUME(gMovesInfo[MOVE_DIG].effect == EFFECT_SEMI_INVULNERABLE); + ASSUME(GetMoveEffect(MOVE_DIG) == EFFECT_SEMI_INVULNERABLE); PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_TANGELA); } WHEN { diff --git a/test/battle/move_effect/sleep_talk.c b/test/battle/move_effect/sleep_talk.c index b00f89e582..fd246f47aa 100644 --- a/test/battle/move_effect/sleep_talk.c +++ b/test/battle/move_effect/sleep_talk.c @@ -94,8 +94,8 @@ SINGLE_BATTLE_TEST("Sleep Talk can use moves while choiced into Sleep Talk") SINGLE_BATTLE_TEST("Sleep Talk fails if user is taunted") { GIVEN { - ASSUME(gMovesInfo[MOVE_TAUNT].effect == EFFECT_TAUNT); - ASSUME(gMovesInfo[MOVE_SLEEP_TALK].category == DAMAGE_CATEGORY_STATUS); + ASSUME(GetMoveEffect(MOVE_TAUNT) == EFFECT_TAUNT); + ASSUME(GetMoveCategory(MOVE_SLEEP_TALK) == DAMAGE_CATEGORY_STATUS); PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_SLEEP); Moves(MOVE_SLEEP_TALK, MOVE_TACKLE, MOVE_FLY, MOVE_DIG); } OPPONENT(SPECIES_WOBBUFFET); } WHEN { @@ -113,7 +113,7 @@ DOUBLE_BATTLE_TEST("Sleep Talk calls move and that move may be redirected by Lig { PASSES_RANDOMLY(1, 2, RNG_RANDOM_TARGET); GIVEN { - ASSUME(gMovesInfo[MOVE_SPARK].type == TYPE_ELECTRIC); + ASSUME(GetMoveType(MOVE_SPARK) == TYPE_ELECTRIC); PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_SLEEP); Moves(MOVE_SLEEP_TALK, MOVE_SPARK, MOVE_FLY, MOVE_DIG); } PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET); @@ -132,7 +132,7 @@ DOUBLE_BATTLE_TEST("Sleep Talk calls move and that move may be redirected by Sto { PASSES_RANDOMLY(1, 2, RNG_RANDOM_TARGET); GIVEN { - ASSUME(gMovesInfo[MOVE_WATER_GUN].type == TYPE_WATER); + ASSUME(GetMoveType(MOVE_WATER_GUN) == TYPE_WATER); PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_SLEEP); Moves(MOVE_SLEEP_TALK, MOVE_WATER_GUN, MOVE_FLY, MOVE_DIG); } PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET); diff --git a/test/battle/move_effect/snore.c b/test/battle/move_effect/snore.c index d83e35b16c..410406a65b 100644 --- a/test/battle/move_effect/snore.c +++ b/test/battle/move_effect/snore.c @@ -3,7 +3,7 @@ ASSUMPTIONS { - ASSUME(gMovesInfo[MOVE_SNORE].effect == EFFECT_SNORE); + ASSUME(GetMoveEffect(MOVE_SNORE) == EFFECT_SNORE); } SINGLE_BATTLE_TEST("Snore fails if not asleep") @@ -47,7 +47,7 @@ SINGLE_BATTLE_TEST("Snore fails if user is throat chopped") { GIVEN { ASSUME(MoveHasAdditionalEffect(MOVE_THROAT_CHOP, MOVE_EFFECT_THROAT_CHOP)); - ASSUME(gMovesInfo[MOVE_SNORE].soundMove == TRUE); + ASSUME(IsSoundMove(MOVE_SNORE) == TRUE); PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_SLEEP); } OPPONENT(SPECIES_WOBBUFFET); } WHEN { diff --git a/test/battle/move_effect/spectral_thief.c b/test/battle/move_effect/spectral_thief.c index 7bb5d5a536..885df9f5e3 100644 --- a/test/battle/move_effect/spectral_thief.c +++ b/test/battle/move_effect/spectral_thief.c @@ -8,8 +8,8 @@ SINGLE_BATTLE_TEST("Spectral Thief steals opponents boost before attacking", s16 PARAMETRIZE { move = MOVE_SWORDS_DANCE; } GIVEN { - ASSUME(gMovesInfo[MOVE_SWORDS_DANCE].effect == EFFECT_ATTACK_UP_2); - ASSUME(gMovesInfo[MOVE_SPECTRAL_THIEF].effect == EFFECT_SPECTRAL_THIEF); + ASSUME(GetMoveEffect(MOVE_SWORDS_DANCE) == EFFECT_ATTACK_UP_2); + ASSUME(GetMoveEffect(MOVE_SPECTRAL_THIEF) == EFFECT_SPECTRAL_THIEF); PLAYER(SPECIES_REGIROCK); OPPONENT(SPECIES_WOBBUFFET); } WHEN { @@ -32,8 +32,8 @@ SINGLE_BATTLE_TEST("Spectral Thief steals opponents boost before attacking", s16 SINGLE_BATTLE_TEST("Spectral Thief can't steal opponent's boost if target is immune") { GIVEN { - ASSUME(gMovesInfo[MOVE_SWORDS_DANCE].effect == EFFECT_ATTACK_UP_2); - ASSUME(gMovesInfo[MOVE_SPECTRAL_THIEF].effect == EFFECT_SPECTRAL_THIEF); + ASSUME(GetMoveEffect(MOVE_SWORDS_DANCE) == EFFECT_ATTACK_UP_2); + ASSUME(GetMoveEffect(MOVE_SPECTRAL_THIEF) == EFFECT_SPECTRAL_THIEF); PLAYER(SPECIES_MEOWTH); OPPONENT(SPECIES_WOBBUFFET); } WHEN { diff --git a/test/battle/move_flags/recoil.c b/test/battle/move_flags/recoil.c index 5a8f3be576..0293a07a80 100644 --- a/test/battle/move_flags/recoil.c +++ b/test/battle/move_flags/recoil.c @@ -87,7 +87,7 @@ SINGLE_BATTLE_TEST("Flare Blitz deals 33% of recoil damage to the user and can b SINGLE_BATTLE_TEST("Flare Blitz is absorbed by Flash Fire and no recoil damage is dealt") { GIVEN { - ASSUME(gMovesInfo[MOVE_FLARE_BLITZ].recoil > 0); + ASSUME(GetMoveRecoil(MOVE_FLARE_BLITZ) > 0); PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_VULPIX) { Ability(ABILITY_FLASH_FIRE); }; } WHEN { diff --git a/test/test_runner_battle.c b/test/test_runner_battle.c index 399cb8a852..806dcf1956 100644 --- a/test/test_runner_battle.c +++ b/test/test_runner_battle.c @@ -2034,16 +2034,16 @@ s32 MoveGetTarget(s32 battlerId, u32 moveId, struct MoveContext *ctx, u32 source } else { - const struct MoveInfo *move = &gMovesInfo[moveId]; - if (move->target == MOVE_TARGET_RANDOM - || move->target == MOVE_TARGET_BOTH - || move->target == MOVE_TARGET_DEPENDS - || move->target == MOVE_TARGET_FOES_AND_ALLY - || move->target == MOVE_TARGET_OPPONENTS_FIELD) + u32 moveTarget = GetMoveTarget(moveId); + if (moveTarget == MOVE_TARGET_RANDOM + || moveTarget == MOVE_TARGET_BOTH + || moveTarget == MOVE_TARGET_DEPENDS + || moveTarget == MOVE_TARGET_FOES_AND_ALLY + || moveTarget == MOVE_TARGET_OPPONENTS_FIELD) { target = BATTLE_OPPOSITE(battlerId); } - else if (move->target == MOVE_TARGET_SELECTED || move->target == MOVE_TARGET_OPPONENT) + else if (moveTarget == MOVE_TARGET_SELECTED || moveTarget == MOVE_TARGET_OPPONENT) { // In AI Doubles not specified target allows any target for EXPECT_MOVE. if (GetBattleTest()->type != BATTLE_TEST_AI_DOUBLES) @@ -2053,11 +2053,11 @@ s32 MoveGetTarget(s32 battlerId, u32 moveId, struct MoveContext *ctx, u32 source target = BATTLE_OPPOSITE(battlerId); } - else if (move->target == MOVE_TARGET_USER || move->target == MOVE_TARGET_ALL_BATTLERS) + else if (moveTarget == MOVE_TARGET_USER || moveTarget == MOVE_TARGET_ALL_BATTLERS) { target = battlerId; } - else if (move->target == MOVE_TARGET_ALLY) + else if (moveTarget == MOVE_TARGET_ALLY) { target = BATTLE_PARTNER(battlerId); } From 5c02abb101324b734280d8b84e3939788a892c47 Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Sun, 20 Apr 2025 22:36:50 +0200 Subject: [PATCH 12/17] Test for Freeze vs Scald thaw (#6658) Co-authored-by: Bassoonian --- test/battle/status1/freeze.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/test/battle/status1/freeze.c b/test/battle/status1/freeze.c index f363ab9e35..12104438ac 100644 --- a/test/battle/status1/freeze.c +++ b/test/battle/status1/freeze.c @@ -61,3 +61,20 @@ SINGLE_BATTLE_TEST("Freeze isn't thawed if opponent is asleep during thawing att } } } + +SINGLE_BATTLE_TEST("Freeze isn't thawed if opponent is asleep during thawing attack when using Scald") +{ + PASSES_RANDOMLY(80, 100, RNG_FROZEN); + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_FREEZE); } + OPPONENT(SPECIES_WOBBUFFET) { Status1(STATUS1_SLEEP); } + } WHEN { + TURN { MOVE(opponent, MOVE_SCALD); MOVE(player, MOVE_CELEBRATE); } + } SCENE { + NONE_OF { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SCALD, opponent); + MESSAGE("Wobbuffet thawed out!"); + STATUS_ICON(player, none: TRUE); + } + } +} From fedbce510b30239099b25cb966ebfcab3d5dd568 Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Sun, 20 Apr 2025 22:49:18 +0200 Subject: [PATCH 13/17] Fixes commander activation on a fainted mon (#6632) Co-authored-by: Bassoonian --- src/battle_script_commands.c | 1 + src/battle_util.c | 2 ++ test/battle/ability/commander.c | 34 +++++++++++++++++++++++++++++++++ 3 files changed, 37 insertions(+) diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 8b7e2702fd..0c45e291ed 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -6708,6 +6708,7 @@ static void Cmd_moveend(void) && IsBattlerTurnDamaged(gBattlerTarget) && IsBattlerAlive(gBattlerTarget) && IsBattlerAlive(gBattlerAttacker) + && !(gStatuses3[BATTLE_PARTNER(gBattlerTarget)] & STATUS3_COMMANDER) && gSpecialStatuses[gBattlerAttacker].parentalBondState != PARENTAL_BOND_1ST_HIT) { u32 targetAbility = GetBattlerAbility(gBattlerTarget); diff --git a/src/battle_util.c b/src/battle_util.c index d38135dbb2..359c88e2b4 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -5363,6 +5363,8 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 case ABILITY_COMMANDER: partner = BATTLE_PARTNER(battler); if (!gSpecialStatuses[battler].switchInAbilityDone + && IsBattlerAlive(partner) + && IsBattlerAlive(battler) && gBattleStruct->commanderActive[partner] == SPECIES_NONE && gBattleMons[partner].species == SPECIES_DONDOZO && GET_BASE_SPECIES_ID(GetMonData(GetPartyBattlerData(battler), MON_DATA_SPECIES)) == SPECIES_TATSUGIRI) diff --git a/test/battle/ability/commander.c b/test/battle/ability/commander.c index d4b31f23b8..a50eba7b8c 100644 --- a/test/battle/ability/commander.c +++ b/test/battle/ability/commander.c @@ -421,3 +421,37 @@ DOUBLE_BATTLE_TEST("Commander Tatsugiri does not get hit by Dragon Darts when co NOT HP_BAR(playerLeft); } } + +DOUBLE_BATTLE_TEST("Commander will not activate if Dondozo fainted right before Tatsugiri came in") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_DONDOZO) { HP(1); } + PLAYER(SPECIES_TATSUGIRI) { Ability(ABILITY_COMMANDER); } + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponentRight, MOVE_TACKLE, target: playerRight); MOVE(playerLeft, MOVE_SHED_TAIL); SEND_OUT(playerLeft, 2); SEND_OUT(playerRight, 3); } + } SCENE { + NOT ABILITY_POPUP(playerLeft, ABILITY_COMMANDER); + } +} + +DOUBLE_BATTLE_TEST("Commander prevent Dondozo from switch out by Dragon Tail") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_DRAGON_TAIL) == EFFECT_HIT_SWITCH_TARGET); + PLAYER(SPECIES_DONDOZO); + PLAYER(SPECIES_TATSUGIRI) { Ability(ABILITY_COMMANDER); } + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponentLeft, MOVE_DRAGON_TAIL, target: playerLeft); } + } SCENE { + ABILITY_POPUP(playerRight, ABILITY_COMMANDER); + ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_TAIL, opponentLeft); + NOT MESSAGE("Wobbuffet was dragged out!"); + } +} From 86969f991962b847940c43d34c71f51d479d3958 Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Sun, 20 Apr 2025 22:49:52 +0200 Subject: [PATCH 14/17] Remove HitmarkerSwapAttackerTarget (#6627) Co-authored-by: Bassoonian --- data/battle_scripts_1.s | 1 - include/constants/battle.h | 8 ++++---- src/battle_script_commands.c | 15 +++++---------- src/battle_util.c | 5 ++--- 4 files changed, 11 insertions(+), 18 deletions(-) diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 4f17840393..9a44235816 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -7102,7 +7102,6 @@ BattleScript_MoveUsedIsAsleep:: goto BattleScript_MoveEnd BattleScript_MoveUsedWokeUp:: - bicword gHitMarker, HITMARKER_WAKE_UP_CLEAR printfromtable gWokeUpStringIds waitmessage B_WAIT_TIME_LONG updatestatusicon BS_ATTACKER diff --git a/include/constants/battle.h b/include/constants/battle.h index ce1cf0e948..de7bbdfe92 100644 --- a/include/constants/battle.h +++ b/include/constants/battle.h @@ -196,7 +196,7 @@ #define STATUS4_SYRUP_BOMB (1 << 5) #define STATUS4_GLAIVE_RUSH (1 << 6) -#define HITMARKER_WAKE_UP_CLEAR (1 << 4) // Cleared when waking up. Never set or checked. +#define HITMARKER_UNUSED_1 (1 << 4) #define HITMARKER_IGNORE_BIDE (1 << 5) #define HITMARKER_DESTINYBOND (1 << 6) #define HITMARKER_NO_ANIMATIONS (1 << 7) // set from battleSceneOff. Never changed during battle @@ -204,16 +204,16 @@ #define HITMARKER_NO_ATTACKSTRING (1 << 9) #define HITMARKER_ATTACKSTRING_PRINTED (1 << 10) #define HITMARKER_NO_PPDEDUCT (1 << 11) -#define HITMARKER_SWAP_ATTACKER_TARGET (1 << 12) +#define HITMARKER_UNUSED_2 (1 << 12) #define HITMARKER_STATUS_ABILITY_EFFECT (1 << 13) #define HITMARKER_SYNCHRONISE_EFFECT (1 << 14) #define HITMARKER_RUN (1 << 15) #define HITMARKER_IGNORE_DISGUISE (1 << 16) #define HITMARKER_DISABLE_ANIMATION (1 << 17) // disable animations during battle scripts, e.g. for Bug Bite -// 3 free spots because of change in handling of UNDERGROUND/UNDERWATER/ON AIR +#define HITMARKER_UNUSED_3 (1 << 18) #define HITMARKER_UNABLE_TO_USE_MOVE (1 << 19) #define HITMARKER_PASSIVE_DAMAGE (1 << 20) -#define HITMARKER_DISOBEDIENT_MOVE (1 << 21) +#define HITMARKER_UNUSED_4 (1 << 21) #define HITMARKER_PLAYER_FAINTED (1 << 22) #define HITMARKER_ALLOW_NO_PP (1 << 23) #define HITMARKER_GRUDGE (1 << 24) diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 0c45e291ed..069ffb1737 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -6842,12 +6842,13 @@ static void Cmd_moveend(void) } } - if (gHitMarker & HITMARKER_SWAP_ATTACKER_TARGET) + // After swapattackerwithtarget is used for snatch the correct battlers have to be restored so data is stored correctly + if (gBattleStruct->snatchedMoveIsUsed) { - u8 temp; + u32 temp; SWAP(gBattlerAttacker, gBattlerTarget, temp); - gHitMarker &= ~HITMARKER_SWAP_ATTACKER_TARGET; } + if (!gSpecialStatuses[gBattlerAttacker].dancerUsedMove) { gDisableStructs[gBattlerAttacker].usedMoves |= 1u << gCurrMovePos; @@ -8827,14 +8828,8 @@ static void Cmd_swapattackerwithtarget(void) { CMD_ARGS(); - u8 temp; + u32 temp; SWAP(gBattlerAttacker, gBattlerTarget, temp); - - if (gHitMarker & HITMARKER_SWAP_ATTACKER_TARGET) - gHitMarker &= ~HITMARKER_SWAP_ATTACKER_TARGET; - else - gHitMarker |= HITMARKER_SWAP_ATTACKER_TARGET; - gBattlescriptCurrInstr = cmd->nextInstr; } diff --git a/src/battle_util.c b/src/battle_util.c index 359c88e2b4..05f5b83602 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -787,7 +787,7 @@ void HandleAction_NothingIsFainted(void) gCurrentActionFuncId = gActionsByTurnOrder[gCurrentTurnActionNumber]; gHitMarker &= ~(HITMARKER_DESTINYBOND | HITMARKER_IGNORE_SUBSTITUTE | HITMARKER_ATTACKSTRING_PRINTED | HITMARKER_NO_PPDEDUCT | HITMARKER_STATUS_ABILITY_EFFECT | HITMARKER_PASSIVE_DAMAGE - | HITMARKER_OBEYS | HITMARKER_WAKE_UP_CLEAR | HITMARKER_SYNCHRONISE_EFFECT + | HITMARKER_OBEYS | HITMARKER_SYNCHRONISE_EFFECT | HITMARKER_CHARGING | HITMARKER_NEVER_SET); } @@ -801,7 +801,7 @@ void HandleAction_ActionFinished(void) SpecialStatusesClear(); gHitMarker &= ~(HITMARKER_DESTINYBOND | HITMARKER_IGNORE_SUBSTITUTE | HITMARKER_ATTACKSTRING_PRINTED | HITMARKER_NO_PPDEDUCT | HITMARKER_STATUS_ABILITY_EFFECT | HITMARKER_PASSIVE_DAMAGE - | HITMARKER_OBEYS | HITMARKER_WAKE_UP_CLEAR | HITMARKER_SYNCHRONISE_EFFECT + | HITMARKER_OBEYS | HITMARKER_SYNCHRONISE_EFFECT | HITMARKER_CHARGING | HITMARKER_NEVER_SET | HITMARKER_IGNORE_DISGUISE); // check if Stellar type boost should be used up @@ -3309,7 +3309,6 @@ static void CancellerObedience(u32 *effect) SetAtkCancellerForCalledMove(); gBattlescriptCurrInstr = BattleScript_IgnoresAndUsesRandomMove; gBattlerTarget = GetBattleMoveTarget(gCalledMove, NO_TARGET_OVERRIDE); - gHitMarker |= HITMARKER_DISOBEDIENT_MOVE; gHitMarker |= HITMARKER_OBEYS; break; } From 0069ec1f0c1c096caa09ccc2aa12a366bb067b8f Mon Sep 17 00:00:00 2001 From: cawtds <38510667+cawtds@users.noreply.github.com> Date: Mon, 21 Apr 2025 11:01:01 +0200 Subject: [PATCH 15/17] Fix captivate-oblivious interaction and related test (#6653) --- asm/macros/battle_script.inc | 2 +- data/battle_scripts_1.s | 10 ++++++---- src/battle_script_commands.c | 22 ++++++++++++++++------ test/battle/ability/oblivious.c | 2 +- 4 files changed, 24 insertions(+), 12 deletions(-) diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc index e386c389c9..2af5c1fcc8 100644 --- a/asm/macros/battle_script.inc +++ b/asm/macros/battle_script.inc @@ -1331,7 +1331,7 @@ .byte \stat .endm - .macro jumpifoppositegenders jumpInstr:req + .macro jumpifcaptivateaffected jumpInstr:req .byte 0xfc .4byte \jumpInstr .endm diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 9a44235816..5631934ed1 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -2743,11 +2743,13 @@ BattleScript_EffectRoost:: BattleScript_EffectCaptivate:: setstatchanger STAT_SPATK, 2, TRUE attackcanceler - jumpifsubstituteblocks BattleScript_FailedFromAtkString - jumpifoppositegenders BattleScript_CaptivateCheckAcc - goto BattleScript_FailedFromAtkString + attackstring + ppreduce + jumpifsubstituteblocks BattleScript_ButItFailed + jumpifcaptivateaffected BattleScript_CaptivateCheckAcc + goto BattleScript_ButItFailed BattleScript_CaptivateCheckAcc: - accuracycheck BattleScript_PrintMoveMissed, ACC_CURR_MOVE + accuracycheck BattleScript_MoveMissedPause, ACC_CURR_MOVE goto BattleScript_StatDownFromAttackString BattleScript_EffectHealBlock:: diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 069ffb1737..f743879fc6 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -599,7 +599,7 @@ static void Cmd_trainerslideout(void); static void Cmd_settelekinesis(void); static void Cmd_swapstatstages(void); static void Cmd_averagestats(void); -static void Cmd_jumpifoppositegenders(void); +static void Cmd_jumpifcaptivateaffected(void); static void Cmd_unused(void); static void Cmd_tryworryseed(void); static void Cmd_callnative(void); @@ -836,8 +836,8 @@ void (* const gBattleScriptingCommandsTable[])(void) = Cmd_jumpifhasnohp, //0xE3 Cmd_jumpifnotcurrentmoveargtype, //0xE4 Cmd_pickup, //0xE5 - Cmd_unused_0xE6, //0xE6 - Cmd_unused_0xE7, //0xE7 + Cmd_unused_0xE6, //0xE6 + Cmd_unused_0xE7, //0xE7 Cmd_settypebasedhalvers, //0xE8 Cmd_jumpifsubstituteblocks, //0xE9 Cmd_tryrecycleitem, //0xEA @@ -858,7 +858,7 @@ void (* const gBattleScriptingCommandsTable[])(void) = Cmd_settelekinesis, //0xF9 Cmd_swapstatstages, //0xFA Cmd_averagestats, //0xFB - Cmd_jumpifoppositegenders, //0xFC + Cmd_jumpifcaptivateaffected, //0xFC Cmd_unused, //0xFD Cmd_tryworryseed, //0xFE Cmd_callnative, //0xFF @@ -16665,14 +16665,24 @@ static void Cmd_averagestats(void) gBattlescriptCurrInstr = cmd->nextInstr; } -static void Cmd_jumpifoppositegenders(void) +static void Cmd_jumpifcaptivateaffected(void) { CMD_ARGS(const u8 *jumpInstr); - if (AreBattlersOfOppositeGender(gBattlerAttacker, gBattlerTarget)) + if (GetBattlerAbility(gBattlerTarget) == ABILITY_OBLIVIOUS) + { + gBattlescriptCurrInstr = BattleScript_NotAffectedAbilityPopUp; + gLastUsedAbility = ABILITY_OBLIVIOUS; + RecordAbilityBattle(gBattlerTarget, ABILITY_OBLIVIOUS); + } + else if (AreBattlersOfOppositeGender(gBattlerAttacker, gBattlerTarget)) + { gBattlescriptCurrInstr = cmd->jumpInstr; + } else + { gBattlescriptCurrInstr = cmd->nextInstr; + } } static void Cmd_unused(void) diff --git a/test/battle/ability/oblivious.c b/test/battle/ability/oblivious.c index 3ac979a271..a787ec6578 100644 --- a/test/battle/ability/oblivious.c +++ b/test/battle/ability/oblivious.c @@ -23,7 +23,7 @@ SINGLE_BATTLE_TEST("Oblivious prevents Captivate") PLAYER(SPECIES_SLOWPOKE) { Ability(ABILITY_OBLIVIOUS); Gender(MON_MALE); } OPPONENT(SPECIES_WOBBUFFET) { Gender(MON_FEMALE); } } WHEN { - TURN { MOVE(opponent, MOVE_ATTRACT); } + TURN { MOVE(opponent, MOVE_CAPTIVATE); } } SCENE { ABILITY_POPUP(player, ABILITY_OBLIVIOUS); NONE_OF { ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); } From c4edfd29887ba1769b0780a5434b86db5f608706 Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Mon, 21 Apr 2025 13:53:04 +0200 Subject: [PATCH 16/17] Fix gBattlerTarget OOB for Gravity and Perish Song (#6662) --- data/battle_scripts_1.s | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 5631934ed1..2c82112b1a 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -2713,6 +2713,7 @@ BattleScript_EffectGravity:: attackstring ppreduce setgravity BattleScript_ButItFailed + savetarget attackanimation waitanimation BattleScript_EffectGravitySuccess:: @@ -2730,6 +2731,7 @@ BattleScript_GravityLoopDrop: BattleScript_GravityLoopEnd: moveendcase MOVEEND_TARGET_VISIBLE jumpifnexttargetvalid BattleScript_GravityLoop + restoretarget goto BattleScript_MoveEnd BattleScript_EffectRoost:: @@ -4213,6 +4215,7 @@ BattleScript_EffectPerishSong:: attackstring ppreduce trysetperishsong BattleScript_ButItFailed + savetarget attackanimation waitanimation printstring STRINGID_FAINTINTHREE @@ -4224,6 +4227,7 @@ BattleScript_PerishSongLoop:: BattleScript_PerishSongLoopIncrement:: addbyte gBattlerTarget, 1 jumpifbytenotequal gBattlerTarget, gBattlersCount, BattleScript_PerishSongLoop + restoretarget goto BattleScript_MoveEnd BattleScript_PerishSongBlocked:: From 4e7fa359ef2fc54c46cb6ea1ec8aca5a18143937 Mon Sep 17 00:00:00 2001 From: Eduardo Quezada Date: Mon, 21 Apr 2025 08:48:49 -0400 Subject: [PATCH 17/17] Fixed LGPE move test fail with `B_UPDATED_MOVE_DATA` to `GEN_7` (#6657) --- test/battle/move_effect_secondary/haze.c | 5 ++++- test/battle/move_effect_secondary/leech_seed.c | 5 ++++- test/battle/move_effect_secondary/light_screen.c | 5 ++++- test/battle/move_effect_secondary/reflect.c | 5 ++++- 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/test/battle/move_effect_secondary/haze.c b/test/battle/move_effect_secondary/haze.c index c3831f0768..c6a0f2cab1 100644 --- a/test/battle/move_effect_secondary/haze.c +++ b/test/battle/move_effect_secondary/haze.c @@ -16,7 +16,10 @@ SINGLE_BATTLE_TEST("Freeze Frost restores stat changes when it was succesful") PLAYER(SPECIES_WYNAUT); OPPONENT(SPECIES_WOBBUFFET); } WHEN { - TURN { MOVE(player, MOVE_FREEZY_FROST, hit: moveSuccess); } + TURN { + MOVE(opponent, MOVE_SAND_ATTACK); + MOVE(player, MOVE_FREEZY_FROST, hit: moveSuccess); + } } SCENE { if (moveSuccess == TRUE) { diff --git a/test/battle/move_effect_secondary/leech_seed.c b/test/battle/move_effect_secondary/leech_seed.c index c5a8db57cc..b7eef45ed4 100644 --- a/test/battle/move_effect_secondary/leech_seed.c +++ b/test/battle/move_effect_secondary/leech_seed.c @@ -26,7 +26,10 @@ SINGLE_BATTLE_TEST("Sappy Seed is not going to seed the target if it fails") PLAYER(SPECIES_WYNAUT); OPPONENT(SPECIES_WOBBUFFET); } WHEN { - TURN { MOVE(player, MOVE_SAPPY_SEED, hit: FALSE); } + TURN { + MOVE(opponent, MOVE_SAND_ATTACK); + MOVE(player, MOVE_SAPPY_SEED, hit: FALSE); + } } SCENE { NONE_OF { ANIMATION(ANIM_TYPE_MOVE, MOVE_SAPPY_SEED, player); diff --git a/test/battle/move_effect_secondary/light_screen.c b/test/battle/move_effect_secondary/light_screen.c index 244e469893..e834e6055f 100644 --- a/test/battle/move_effect_secondary/light_screen.c +++ b/test/battle/move_effect_secondary/light_screen.c @@ -16,7 +16,10 @@ SINGLE_BATTLE_TEST("Glitzy Glow sets up Light Screen when it was succesful") PLAYER(SPECIES_WYNAUT); OPPONENT(SPECIES_WOBBUFFET); } WHEN { - TURN { MOVE(player, MOVE_GLITZY_GLOW, hit: moveSuccess); } + TURN { + MOVE(opponent, MOVE_SAND_ATTACK); + MOVE(player, MOVE_GLITZY_GLOW, hit: moveSuccess); + } } SCENE { if (moveSuccess == TRUE) { diff --git a/test/battle/move_effect_secondary/reflect.c b/test/battle/move_effect_secondary/reflect.c index 6a0dda06d8..0b1dbf7fd3 100644 --- a/test/battle/move_effect_secondary/reflect.c +++ b/test/battle/move_effect_secondary/reflect.c @@ -16,7 +16,10 @@ SINGLE_BATTLE_TEST("Baddy Bad sets up Reflect when it was succesful") PLAYER(SPECIES_WYNAUT); OPPONENT(SPECIES_WOBBUFFET); } WHEN { - TURN { MOVE(player, MOVE_BADDY_BAD, hit: moveSuccess); } + TURN { + MOVE(opponent, MOVE_SAND_ATTACK); + MOVE(player, MOVE_BADDY_BAD, hit: moveSuccess); + } } SCENE { if (moveSuccess == TRUE) {