From 782c559a20e873ee0926eca3328ea23d28645c6b Mon Sep 17 00:00:00 2001 From: PhallenTree <168426989+PhallenTree@users.noreply.github.com> Date: Sun, 26 Oct 2025 21:41:16 +0000 Subject: [PATCH] Fixes Ruin field statuses negation conditions + upcoming cleanup (#8042) --- data/battle_scripts_1.s | 4 +- include/battle_scripts.h | 4 +- src/battle_ai_main.c | 4 +- src/battle_ai_util.c | 3 +- src/battle_hold_effects.c | 12 +-- src/battle_script_commands.c | 22 +++--- src/battle_util.c | 12 ++- test/battle/ability/beads_of_ruin.c | 116 +++++++++++++++++++++++++++- test/battle/ability/sword_of_ruin.c | 115 +++++++++++++++++++++++++++ 9 files changed, 265 insertions(+), 27 deletions(-) diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 25624a8ecd..f7edad9db4 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -7723,7 +7723,7 @@ BattleScript_ItemHealHP_End2:: call BattleScript_ItemHealHP_Ret end2 -BattleScript_AirBaloonMsgIn:: +BattleScript_AirBalloonMsgIn:: printstring STRINGID_AIRBALLOONFLOAT waitmessage B_WAIT_TIME_LONG end3 @@ -7733,7 +7733,7 @@ BattleScript_AirBalloonMsgInRet:: waitmessage B_WAIT_TIME_LONG return -BattleScript_AirBaloonMsgPop:: +BattleScript_AirBalloonMsgPop:: printstring STRINGID_AIRBALLOONPOP waitmessage B_WAIT_TIME_LONG removeitem BS_TARGET diff --git a/include/battle_scripts.h b/include/battle_scripts.h index d64d9812aa..1031f1dcbf 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -345,9 +345,9 @@ extern const u8 BattleScript_WeaknessPolicy[]; extern const u8 BattleScript_TargetItemStatRaise[]; extern const u8 BattleScript_RockyHelmetActivates[]; extern const u8 BattleScript_ItemHurtEnd2[]; -extern const u8 BattleScript_AirBaloonMsgIn[]; +extern const u8 BattleScript_AirBalloonMsgIn[]; extern const u8 BattleScript_AirBalloonMsgInRet[]; -extern const u8 BattleScript_AirBaloonMsgPop[]; +extern const u8 BattleScript_AirBalloonMsgPop[]; extern const u8 BattleScript_ItemHurtRet[]; extern const u8 BattleScript_ToxicOrb[]; extern const u8 BattleScript_FlameOrb[]; diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index 80f2f46abc..94b4a10a51 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -279,7 +279,7 @@ void BattleAI_SetupFlags(void) } else // Assign ai flags for player for prediction { - u64 aiFlags = GetAiFlags(TRAINER_BATTLE_PARAM.opponentA, B_POSITION_OPPONENT_LEFT) + u64 aiFlags = GetAiFlags(TRAINER_BATTLE_PARAM.opponentA, B_POSITION_OPPONENT_LEFT) | GetAiFlags(TRAINER_BATTLE_PARAM.opponentB, B_POSITION_OPPONENT_RIGHT); gAiThinkingStruct->aiFlags[B_POSITION_PLAYER_RIGHT] = aiFlags; gAiThinkingStruct->aiFlags[B_POSITION_PLAYER_LEFT] = aiFlags; @@ -2277,7 +2277,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) } break; case PROTECT_WIDE_GUARD: - if(!(GetBattlerMoveTargetType(battlerAtk, predictedMove) & (MOVE_TARGET_FOES_AND_ALLY | MOVE_TARGET_BOTH))) + if (!(GetBattlerMoveTargetType(battlerAtk, predictedMove) & (MOVE_TARGET_FOES_AND_ALLY | MOVE_TARGET_BOTH))) { ADJUST_SCORE(-10); decreased = TRUE; diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index 6049a13842..bab5d76706 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -5308,7 +5308,8 @@ void DecideTerastal(u32 battler) #define takenWithTera altCalcs->takenWithTera #define takenWithoutTera gAiLogicData->simulatedDmg[opposingBattler][battler] -enum AIConsiderGimmick ShouldTeraFromCalcs(u32 battler, u32 opposingBattler, struct AltTeraCalcs *altCalcs) { +enum AIConsiderGimmick ShouldTeraFromCalcs(u32 battler, u32 opposingBattler, struct AltTeraCalcs *altCalcs) +{ struct Pokemon* party = GetBattlerParty(battler); // Check how many pokemon we have that could tera diff --git a/src/battle_hold_effects.c b/src/battle_hold_effects.c index 4f4d65b056..7dacc04223 100644 --- a/src/battle_hold_effects.c +++ b/src/battle_hold_effects.c @@ -231,7 +231,7 @@ static enum ItemEffect TryKingsRock(u32 battlerAtk, u32 battlerDef, u32 item) return effect; } -static enum ItemEffect TryAirBallon(u32 battler, ActivationTiming timing) +static enum ItemEffect TryAirBalloon(u32 battler, ActivationTiming timing) { enum ItemEffect effect = ITEM_NO_EFFECT; @@ -239,7 +239,7 @@ static enum ItemEffect TryAirBallon(u32 battler, ActivationTiming timing) { if (IsBattlerTurnDamaged(battler)) { - BattleScriptCall(BattleScript_AirBaloonMsgPop); + BattleScriptCall(BattleScript_AirBalloonMsgPop); effect = ITEM_EFFECT_OTHER; } } @@ -247,7 +247,7 @@ static enum ItemEffect TryAirBallon(u32 battler, ActivationTiming timing) { gSpecialStatuses[battler].switchInItemDone = TRUE; if (timing == IsOnSwitchInFirstTurnActivation) - BattleScriptPushCursorAndCallback(BattleScript_AirBaloonMsgIn); + BattleScriptPushCursorAndCallback(BattleScript_AirBalloonMsgIn); else BattleScriptCall(BattleScript_AirBalloonMsgInRet); RecordItemEffectBattle(battler, HOLD_EFFECT_AIR_BALLOON); @@ -497,7 +497,7 @@ static enum ItemEffect TryMentalHerb(u32 battler) return effect; } -static enum ItemEffect TryThroatSray(u32 battlerAtk) +static enum ItemEffect TryThroatSpray(u32 battlerAtk) { enum ItemEffect effect = ITEM_NO_EFFECT; @@ -1110,7 +1110,7 @@ enum ItemEffect ItemBattleEffects(u32 itemBattler, u32 battler, enum HoldEffect effect = TryKingsRock(itemBattler, battler, item); break; case HOLD_EFFECT_AIR_BALLOON: - effect = TryAirBallon(itemBattler, timing); + effect = TryAirBalloon(itemBattler, timing); break; case HOLD_EFFECT_ROCKY_HELMET: effect = TryRockyHelmet(itemBattler, battler); @@ -1146,7 +1146,7 @@ enum ItemEffect ItemBattleEffects(u32 itemBattler, u32 battler, enum HoldEffect effect = TryMentalHerb(itemBattler); break; case HOLD_EFFECT_THROAT_SPRAY: - effect = TryThroatSray(itemBattler); + effect = TryThroatSpray(itemBattler); break; case HOLD_EFFECT_KEE_BERRY: // consume and boost defense if used physical move effect = DamagedStatBoostBerryEffect(itemBattler, battler, STAT_DEF, DAMAGE_CATEGORY_PHYSICAL); diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 62604d43f5..80e0139f4a 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -6124,7 +6124,7 @@ static void Cmd_moveend(void) if (AbilityBattleEffects(ABILITYEFFECT_IMMUNITY, battler, 0, 0, 0)) effect = TRUE; } - if(!effect) + if (!effect) gBattleScripting.moveendState++; break; case MOVEEND_SYNCHRONIZE_ATTACKER: // attacker synchronize @@ -9129,16 +9129,18 @@ static void Cmd_useitemonopponent(void) bool32 CanUseLastResort(u8 battler) { - u32 knownMovesCount = 0, usedMovesCount = 0; - - for (u32 i = 0; i < MAX_MON_MOVES; i++) + u32 moveIndex; + for (moveIndex = 0; moveIndex < MAX_MON_MOVES; moveIndex++) { - if (gBattleMons[battler].moves[i] != MOVE_NONE) - knownMovesCount++; - if (GetMoveEffect(gBattleMons[battler].moves[i]) != EFFECT_LAST_RESORT && gDisableStructs[battler].usedMoves & (1u << i)) // Increment used move count for all moves except current Last Resort. - usedMovesCount++; + u32 move = gBattleMons[battler].moves[moveIndex]; + // Assumes that an empty slot cannot have a non-empty slot after it + if (move == MOVE_NONE) + break; + // If not Last Resort and has not been used, can't use Last Resort + if (GetMoveEffect(move) != EFFECT_LAST_RESORT && !(gDisableStructs[battler].usedMoves & (1u << moveIndex))) + return FALSE; } - return (knownMovesCount >= 2 && usedMovesCount >= knownMovesCount - 1); + return moveIndex >= 2; // At least two usable moves that are either Last Resort or have been used } static void RemoveAllWeather(void) @@ -17075,7 +17077,7 @@ void BS_TryActivateReceiver(void) u32 partnerAbility = GetBattlerAbility(gBattlerAbility); if (IsBattlerAlive(gBattlerAbility) && (partnerAbility == ABILITY_RECEIVER || partnerAbility == ABILITY_POWER_OF_ALCHEMY) - && GetBattlerHoldEffect(battler) != HOLD_EFFECT_ABILITY_SHIELD + && GetBattlerHoldEffectIgnoreAbility(battler) != HOLD_EFFECT_ABILITY_SHIELD && !gAbilitiesInfo[gBattleMons[battler].ability].cantBeCopied) { gBattleStruct->tracedAbility[gBattlerAbility] = gBattleMons[battler].ability; // re-using the variable for trace diff --git a/src/battle_util.c b/src/battle_util.c index 72be8c97fe..c7c6f49e58 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -7421,11 +7421,17 @@ static inline u32 CalcMoveBasePowerAfterModifiers(struct DamageContext *ctx) static bool32 IsRuinStatusActive(u32 fieldEffect) { - if (IsNeutralizingGasOnField()) // Neutralizing Gas still blocks Ruin field statuses - return FALSE; - + bool32 isNeutralizingGasOnField = IsNeutralizingGasOnField(); for (u32 battler = 0; battler < gBattlersCount; battler++) { + // Mold Breaker doesn't ignore Ruin field status but Gastro Acid and Neutralizing Gas do + if (gBattleMons[battler].volatiles.gastroAcid) + continue; + if (GetBattlerHoldEffectIgnoreAbility(battler) != HOLD_EFFECT_ABILITY_SHIELD + && isNeutralizingGasOnField + && gBattleMons[battler].ability != ABILITY_NEUTRALIZING_GAS) + continue; + if (GetBattlerVolatile(battler, fieldEffect)) return TRUE; } diff --git a/test/battle/ability/beads_of_ruin.c b/test/battle/ability/beads_of_ruin.c index 37240d2d68..86172459ab 100644 --- a/test/battle/ability/beads_of_ruin.c +++ b/test/battle/ability/beads_of_ruin.c @@ -4,6 +4,7 @@ ASSUMPTIONS { ASSUME(GetMoveCategory(MOVE_WATER_GUN) == DAMAGE_CATEGORY_SPECIAL); + ASSUME(GetMoveCategory(MOVE_ROUND) == DAMAGE_CATEGORY_SPECIAL); ASSUME(GetMoveEffect(MOVE_ROLE_PLAY) == EFFECT_ROLE_PLAY); } @@ -74,4 +75,117 @@ SINGLE_BATTLE_TEST("Beads of Ruin's message displays correctly after all battler } } -TO_DO_BATTLE_TEST("Beads of Ruin reduce Defense if Wonder Room is active"); +DOUBLE_BATTLE_TEST("Beads of Ruin increases damage taken by physical moves in Wonder Room", s16 damage) +{ + bool32 useWonderRoom; + u32 move; + + PARAMETRIZE { useWonderRoom = FALSE; move = MOVE_SCRATCH; } + PARAMETRIZE { useWonderRoom = FALSE; move = MOVE_ROUND; } + PARAMETRIZE { useWonderRoom = TRUE; move = MOVE_SCRATCH; } + PARAMETRIZE { useWonderRoom = TRUE; move = MOVE_ROUND; } + + GIVEN { + ASSUME(GetMoveEffect(MOVE_WONDER_ROOM) == EFFECT_WONDER_ROOM); + ASSUME(GetMoveCategory(MOVE_SCRATCH) == DAMAGE_CATEGORY_PHYSICAL); + ASSUME(GetMoveEffect(MOVE_ROUND) != EFFECT_PSYSHOCK); + PLAYER(SPECIES_CHI_YU) { Ability(ABILITY_BEADS_OF_RUIN); } + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + if (useWonderRoom) + TURN { MOVE(opponentLeft, MOVE_WONDER_ROOM); MOVE(playerRight, move, target: opponentLeft); } + else + TURN { MOVE(playerRight, move, target: opponentLeft); } + } SCENE { + ABILITY_POPUP(playerLeft, ABILITY_BEADS_OF_RUIN); + MESSAGE("Chi-Yu's Beads of Ruin weakened the Sp. Def of all surrounding Pokémon!"); + ANIMATION(ANIM_TYPE_MOVE, move, playerRight); + HP_BAR(opponentLeft, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_GT(results[2].damage, results[0].damage); // In Wonder Room, physical move deals more damage + EXPECT_LT(results[3].damage, results[1].damage); // In Wonder Room, special move deals less damage + } +} + +SINGLE_BATTLE_TEST("Beads of Ruin doesn't activate when dragged out by Mold Breaker attacker") +{ + u32 ability; + + PARAMETRIZE { ability = ABILITY_MOLD_BREAKER; } + PARAMETRIZE { ability = ABILITY_SAND_RUSH; } + + GIVEN { + ASSUME(GetMoveEffect(MOVE_DRAGON_TAIL) == EFFECT_HIT_SWITCH_TARGET); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_CHI_YU) { Ability(ABILITY_BEADS_OF_RUIN); } + OPPONENT(SPECIES_EXCADRILL) { Ability(ability); } + } WHEN { + TURN { MOVE(opponent, MOVE_DRAGON_TAIL); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_TAIL, opponent); + if (ability == ABILITY_MOLD_BREAKER) + { + NONE_OF { + ABILITY_POPUP(player, ABILITY_BEADS_OF_RUIN); + MESSAGE("Chi-Yu's Beads of Ruin weakened the Sp. Def of all surrounding Pokémon!"); + } + } + else + { + ABILITY_POPUP(player, ABILITY_BEADS_OF_RUIN); + MESSAGE("Chi-Yu's Beads of Ruin weakened the Sp. Def of all surrounding Pokémon!"); + } + } +} + +DOUBLE_BATTLE_TEST("Beads of Ruin's Sp. Def reduction is not ignored by Mold Breaker", s16 damage) +{ + u32 ability; + + PARAMETRIZE { ability = ABILITY_MOLD_BREAKER; } + PARAMETRIZE { ability = ABILITY_SAND_RUSH; } + + GIVEN { + PLAYER(SPECIES_CHI_YU) { Ability(ABILITY_BEADS_OF_RUIN); } + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_EXCADRILL) { Ability(ability); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponentLeft, MOVE_ROUND, target: playerRight); } + } SCENE { + ABILITY_POPUP(playerLeft, ABILITY_BEADS_OF_RUIN); + MESSAGE("Chi-Yu's Beads of Ruin weakened the Sp. Def of all surrounding Pokémon!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ROUND, opponentLeft); + HP_BAR(playerRight, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_EQ(results[0].damage, results[1].damage); + } +} + +DOUBLE_BATTLE_TEST("Beads of Ruin's Sp. Def reduction is ignored by Gastro Acid", s16 damage) +{ + u32 move; + + PARAMETRIZE { move = MOVE_GASTRO_ACID; } + PARAMETRIZE { move = MOVE_CELEBRATE; } + + GIVEN { + ASSUME(GetMoveEffect(MOVE_GASTRO_ACID) == EFFECT_GASTRO_ACID); + PLAYER(SPECIES_CHI_YU) { Ability(ABILITY_BEADS_OF_RUIN); } + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponentRight, move, target: playerLeft); MOVE(opponentLeft, MOVE_ROUND, target: playerRight); } + } SCENE { + ABILITY_POPUP(playerLeft, ABILITY_BEADS_OF_RUIN); + MESSAGE("Chi-Yu's Beads of Ruin weakened the Sp. Def of all surrounding Pokémon!"); + ANIMATION(ANIM_TYPE_MOVE, move, opponentRight); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ROUND, opponentLeft); + HP_BAR(playerRight, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_LT(results[0].damage, results[1].damage); + } +} diff --git a/test/battle/ability/sword_of_ruin.c b/test/battle/ability/sword_of_ruin.c index 395cd3fd68..a891f7d5a7 100644 --- a/test/battle/ability/sword_of_ruin.c +++ b/test/battle/ability/sword_of_ruin.c @@ -73,3 +73,118 @@ SINGLE_BATTLE_TEST("Sword of Ruin's message displays correctly after all battler MESSAGE("The opposing Chien-Pao's Sword of Ruin weakened the Defense of all surrounding Pokémon!"); } } + +DOUBLE_BATTLE_TEST("Sword of Ruin increases damage taken by special moves in Wonder Room", s16 damage) +{ + bool32 useWonderRoom; + u32 move; + + PARAMETRIZE { useWonderRoom = FALSE; move = MOVE_SCRATCH; } + PARAMETRIZE { useWonderRoom = FALSE; move = MOVE_ROUND; } + PARAMETRIZE { useWonderRoom = TRUE; move = MOVE_SCRATCH; } + PARAMETRIZE { useWonderRoom = TRUE; move = MOVE_ROUND; } + + GIVEN { + ASSUME(GetMoveEffect(MOVE_WONDER_ROOM) == EFFECT_WONDER_ROOM); + ASSUME(GetMoveCategory(MOVE_ROUND) == DAMAGE_CATEGORY_SPECIAL); + ASSUME(GetMoveEffect(MOVE_ROUND) != EFFECT_PSYSHOCK); + PLAYER(SPECIES_CHIEN_PAO) { Ability(ABILITY_SWORD_OF_RUIN); } + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + if (useWonderRoom) + TURN { MOVE(opponentLeft, MOVE_WONDER_ROOM); MOVE(playerRight, move, target: opponentLeft); } + else + TURN { MOVE(playerRight, move, target: opponentLeft); } + } SCENE { + ABILITY_POPUP(playerLeft, ABILITY_SWORD_OF_RUIN); + MESSAGE("Chien-Pao's Sword of Ruin weakened the Defense of all surrounding Pokémon!"); + ANIMATION(ANIM_TYPE_MOVE, move, playerRight); + HP_BAR(opponentLeft, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_LT(results[2].damage, results[0].damage); // In Wonder Room, physical move deals less damage + EXPECT_GT(results[3].damage, results[1].damage); // In Wonder Room, special move deals more damage + } +} + +SINGLE_BATTLE_TEST("Sword of Ruin doesn't activate when dragged out by Mold Breaker attacker") +{ + u32 ability; + + PARAMETRIZE { ability = ABILITY_MOLD_BREAKER; } + PARAMETRIZE { ability = ABILITY_SAND_RUSH; } + + GIVEN { + ASSUME(GetMoveEffect(MOVE_DRAGON_TAIL) == EFFECT_HIT_SWITCH_TARGET); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_CHIEN_PAO) { Ability(ABILITY_SWORD_OF_RUIN); } + OPPONENT(SPECIES_EXCADRILL) { Ability(ability); } + } WHEN { + TURN { MOVE(opponent, MOVE_DRAGON_TAIL); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_TAIL, opponent); + if (ability == ABILITY_MOLD_BREAKER) + { + NONE_OF { + ABILITY_POPUP(player, ABILITY_SWORD_OF_RUIN); + MESSAGE("Chien-Pao's Sword of Ruin weakened the Defense of all surrounding Pokémon!"); + } + } + else + { + ABILITY_POPUP(player, ABILITY_SWORD_OF_RUIN); + MESSAGE("Chien-Pao's Sword of Ruin weakened the Defense of all surrounding Pokémon!"); + } + } +} + +DOUBLE_BATTLE_TEST("Sword of Ruin's Defense reduction is not ignored by Mold Breaker", s16 damage) +{ + u32 ability; + + PARAMETRIZE { ability = ABILITY_MOLD_BREAKER; } + PARAMETRIZE { ability = ABILITY_SAND_RUSH; } + + GIVEN { + PLAYER(SPECIES_CHIEN_PAO) { Ability(ABILITY_SWORD_OF_RUIN); } + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_EXCADRILL) { Ability(ability); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponentLeft, MOVE_SCRATCH, target: playerRight); } + } SCENE { + ABILITY_POPUP(playerLeft, ABILITY_SWORD_OF_RUIN); + MESSAGE("Chien-Pao's Sword of Ruin weakened the Defense of all surrounding Pokémon!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, opponentLeft); + HP_BAR(playerRight, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_EQ(results[0].damage, results[1].damage); + } +} + +DOUBLE_BATTLE_TEST("Sword of Ruin's Defense reduction is ignored by Gastro Acid", s16 damage) +{ + u32 move; + + PARAMETRIZE { move = MOVE_GASTRO_ACID; } + PARAMETRIZE { move = MOVE_CELEBRATE; } + + GIVEN { + ASSUME(GetMoveEffect(MOVE_GASTRO_ACID) == EFFECT_GASTRO_ACID); + PLAYER(SPECIES_CHIEN_PAO) { Ability(ABILITY_SWORD_OF_RUIN); } + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponentRight, move, target: playerLeft); MOVE(opponentLeft, MOVE_SCRATCH, target: playerRight); } + } SCENE { + ABILITY_POPUP(playerLeft, ABILITY_SWORD_OF_RUIN); + MESSAGE("Chien-Pao's Sword of Ruin weakened the Defense of all surrounding Pokémon!"); + ANIMATION(ANIM_TYPE_MOVE, move, opponentRight); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, opponentLeft); + HP_BAR(playerRight, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_LT(results[0].damage, results[1].damage); + } +}