diff --git a/include/battle.h b/include/battle.h index 8fe4fa8cce..ba3752717a 100644 --- a/include/battle.h +++ b/include/battle.h @@ -722,7 +722,7 @@ struct BattleStruct struct Illusion illusion[MAX_BATTLERS_COUNT]; u8 soulheartBattlerId; u8 friskedBattler; // Frisk needs to identify 2 battlers in double battles. - u8 sameMoveTurns[MAX_BATTLERS_COUNT]; // For Metronome, number of times the same moves has been SUCCESFULLY used. + u8 metronomeItemCounter[MAX_BATTLERS_COUNT]; // For Metronome, number of times the same moves has been SUCCESFULLY used. u8 quickClawBattlerId; struct LostItem itemLost[NUM_BATTLE_SIDES][PARTY_SIZE]; // Pokemon that had items consumed or stolen (two bytes per party member per side) u8 blunderPolicy:1; // should blunder policy activate @@ -782,7 +782,8 @@ struct BattleStruct u8 hazardsQueue[NUM_BATTLE_SIDES][HAZARDS_MAX_COUNT]; u8 numHazards[NUM_BATTLE_SIDES]; u8 hazardsCounter:4; // Counter for applying hazard on switch in - u8 padding2:4; + u8 incrementEchoedVoice:1; + u8 echoedVoiceCounter:3; }; struct AiBattleData diff --git a/src/battle_end_turn.c b/src/battle_end_turn.c index 73ffa12023..da7607d9c4 100644 --- a/src/battle_end_turn.c +++ b/src/battle_end_turn.c @@ -168,6 +168,17 @@ static bool32 HandleEndTurnVarious(u32 battler) gBattleStruct->hpBefore[i] = gBattleMons[i].hp; } + if (gBattleStruct->incrementEchoedVoice) + { + if (gBattleStruct->echoedVoiceCounter < 4) + gBattleStruct->echoedVoiceCounter++; + gBattleStruct->incrementEchoedVoice = FALSE; + } + else + { + gBattleStruct->echoedVoiceCounter = 0; + } + return effect; } diff --git a/src/battle_main.c b/src/battle_main.c index 26755eda97..ee593da2f1 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -3227,7 +3227,7 @@ void SwitchInClearSetData(u32 battler, struct Volatiles *volatilesCopy) gLastHitBy[battler] = 0xFF; gBattleStruct->lastTakenMove[battler] = 0; - gBattleStruct->sameMoveTurns[battler] = 0; + gBattleStruct->metronomeItemCounter[battler] = 0; gBattleStruct->lastTakenMoveFrom[battler][0] = 0; gBattleStruct->lastTakenMoveFrom[battler][1] = 0; gBattleStruct->lastTakenMoveFrom[battler][2] = 0; @@ -3348,7 +3348,7 @@ const u8* FaintClearSetData(u32 battler) gLastHitBy[battler] = 0xFF; gBattleStruct->choicedMove[battler] = MOVE_NONE; - gBattleStruct->sameMoveTurns[battler] = 0; + gBattleStruct->metronomeItemCounter[battler] = 0; gBattleStruct->lastTakenMove[battler] = MOVE_NONE; gBattleStruct->lastTakenMoveFrom[battler][0] = 0; gBattleStruct->lastTakenMoveFrom[battler][1] = 0; diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index d64a2d953f..e5bd66a0ac 100755 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -1541,7 +1541,7 @@ static void Cmd_ppreduce(void) // For item Metronome, echoed voice if (gCurrentMove != gLastResultingMoves[gBattlerAttacker] || WasUnableToUseMove(gBattlerAttacker)) - gBattleStruct->sameMoveTurns[gBattlerAttacker] = 0; + gBattleStruct->metronomeItemCounter[gBattlerAttacker] = 0; if (gBattleMons[gBattlerAttacker].pp[gCurrMovePos] > ppToDeduct) gBattleMons[gBattlerAttacker].pp[gCurrMovePos] -= ppToDeduct; @@ -6893,9 +6893,9 @@ static void Cmd_moveend(void) if (gCurrentMove != gLastResultingMoves[gBattlerAttacker] || gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_NO_EFFECT || gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE) - gBattleStruct->sameMoveTurns[gBattlerAttacker] = 0; + gBattleStruct->metronomeItemCounter[gBattlerAttacker] = 0; else if (gCurrentMove == gLastResultingMoves[gBattlerAttacker] && gSpecialStatuses[gBattlerAttacker].parentalBondState != PARENTAL_BOND_1ST_HIT) - gBattleStruct->sameMoveTurns[gBattlerAttacker]++; + gBattleStruct->metronomeItemCounter[gBattlerAttacker]++; gBattleScripting.moveendState++; break; case MOVEEND_CLEAR_BITS: // Clear/Set bits for things like using a move for all targets and all hits. @@ -6949,6 +6949,8 @@ static void Cmd_moveend(void) SetActiveGimmick(gBattlerAttacker, GIMMICK_NONE); if (B_CHARGE >= GEN_9 && moveType == TYPE_ELECTRIC && (IsBattlerTurnDamaged(gBattlerTarget) || gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_NO_EFFECT)) gBattleMons[gBattlerAttacker].volatiles.charge = FALSE; + if (moveEffect == EFFECT_ECHOED_VOICE && !(gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE)) + gBattleStruct->incrementEchoedVoice = TRUE; // check if Stellar type boost should be used up if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_TERA && GetBattlerTeraType(gBattlerAttacker) == TYPE_STELLAR diff --git a/src/battle_util.c b/src/battle_util.c index b0206f98bd..cd1c435474 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -8074,10 +8074,10 @@ static inline u32 CalcMoveBasePower(struct DamageContext *ctx) break; } case EFFECT_ECHOED_VOICE: - // gBattleStruct->sameMoveTurns incremented in ppreduce - if (gBattleStruct->sameMoveTurns[battlerAtk] != 0 && GetMoveEffect(gLastResultingMoves[battlerAtk]) == EFFECT_ECHOED_VOICE) + // gBattleStruct->echoedVoiceCounter incremented in EndTurnVarious called by DoEndTurnEffects + if (gBattleStruct->echoedVoiceCounter != 0) { - basePower += (basePower * gBattleStruct->sameMoveTurns[battlerAtk]); + basePower += (basePower * gBattleStruct->echoedVoiceCounter); if (basePower > 200) basePower = 200; } @@ -9207,7 +9207,7 @@ static inline uq4_12_t GetAttackerItemsModifier(u32 battlerAtk, uq4_12_t typeEff { case HOLD_EFFECT_METRONOME: metronomeBoostBase = PercentToUQ4_12(GetBattlerHoldEffectParam(battlerAtk)); - metronomeTurns = min(gBattleStruct->sameMoveTurns[battlerAtk], 5); + metronomeTurns = min(gBattleStruct->metronomeItemCounter[battlerAtk], 5); // according to bulbapedia this is the "correct" way to calculate the metronome boost // due to the limited domain of damage numbers it will never really matter whether this is off by one return uq4_12_add(UQ_4_12(1.0), metronomeBoostBase * metronomeTurns); diff --git a/test/battle/move_effect/echoed_voice.c b/test/battle/move_effect/echoed_voice.c index 3c36270454..29a2b6e487 100644 --- a/test/battle/move_effect/echoed_voice.c +++ b/test/battle/move_effect/echoed_voice.c @@ -1,7 +1,169 @@ #include "global.h" #include "test/battle.h" -TO_DO_BATTLE_TEST("Echoed Voice's power is multiplied for every consecutive turn used, capped at 5"); -TO_DO_BATTLE_TEST("Echoed Voice's power is reset when using a different move"); -TO_DO_BATTLE_TEST("Echoed Voice's power is increased even if it misses"); -TO_DO_BATTLE_TEST("Echoed Voice's power is increased even if it's blocked by Protect"); +ASSUMPTIONS +{ + ASSUME(GetMoveEffect(MOVE_ECHOED_VOICE) == EFFECT_ECHOED_VOICE); +} + +SINGLE_BATTLE_TEST("Echoed Voice's power is multiplied for every consecutive turn used, capped at 5") +{ + s16 damage[6]; + + GIVEN { + ASSUME(GetMoveEffect(MOVE_SOFT_BOILED) == EFFECT_SOFTBOILED); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_BLISSEY); + } WHEN { + TURN { MOVE(player, MOVE_ECHOED_VOICE); } + TURN { MOVE(player, MOVE_ECHOED_VOICE); } + TURN { MOVE(player, MOVE_ECHOED_VOICE); MOVE(opponent, MOVE_SOFT_BOILED); } + TURN { MOVE(player, MOVE_ECHOED_VOICE); } + TURN { MOVE(player, MOVE_ECHOED_VOICE); MOVE(opponent, MOVE_SOFT_BOILED); } + TURN { MOVE(player, MOVE_ECHOED_VOICE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, player); + HP_BAR(opponent, captureDamage: &damage[0]); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, player); + HP_BAR(opponent, captureDamage: &damage[1]); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, player); + HP_BAR(opponent, captureDamage: &damage[2]); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, player); + HP_BAR(opponent, captureDamage: &damage[3]); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, player); + HP_BAR(opponent, captureDamage: &damage[4]); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, player); + HP_BAR(opponent, captureDamage: &damage[5]); + } THEN { + EXPECT_MUL_EQ(damage[0], UQ_4_12(2.0), damage[1]); + EXPECT_MUL_EQ(damage[0], UQ_4_12(3.0), damage[2]); + EXPECT_MUL_EQ(damage[0], UQ_4_12(4.0), damage[3]); + EXPECT_MUL_EQ(damage[0], UQ_4_12(5.0), damage[4]); + EXPECT_EQ(damage[4], damage[5]); + } +} + +SINGLE_BATTLE_TEST("Echoed Voice's power increases even if used by another battler") +{ + s16 damage[2]; + + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_ECHOED_VOICE); } + TURN { MOVE(player, MOVE_ECHOED_VOICE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, opponent); + HP_BAR(player, captureDamage: &damage[0]); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, player); + HP_BAR(opponent, captureDamage: &damage[1]); + } THEN { + EXPECT_MUL_EQ(damage[0], UQ_4_12(2.0), damage[1]); + } +} + +SINGLE_BATTLE_TEST("Echoed Voice's power does not change until the end of the turn") +{ + s16 damage[3]; + + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_ECHOED_VOICE); MOVE(opponent, MOVE_ECHOED_VOICE); } + TURN { MOVE(player, MOVE_ECHOED_VOICE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, player); + HP_BAR(opponent, captureDamage: &damage[0]); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, opponent); + HP_BAR(player, captureDamage: &damage[1]); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, player); + HP_BAR(opponent, captureDamage: &damage[2]); + } THEN { + EXPECT_EQ(damage[0], damage[1]); + EXPECT_MUL_EQ(damage[0], UQ_4_12(2.0), damage[2]); + } +} + +SINGLE_BATTLE_TEST("Echoed Voice's power increase is reset when no battler uses it successfully during a turn") +{ + s16 damage[3]; + + GIVEN { + ASSUME(MoveHasAdditionalEffect(MOVE_BITE, MOVE_EFFECT_FLINCH)); + PLAYER(SPECIES_WOBBUFFET) { Speed(5); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(10); } + } WHEN { + TURN { MOVE(opponent, MOVE_ECHOED_VOICE); } + TURN { MOVE(player, MOVE_ECHOED_VOICE); } + TURN { MOVE(opponent, MOVE_BITE); MOVE(player, MOVE_ECHOED_VOICE); } + TURN { MOVE(player, MOVE_ECHOED_VOICE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, opponent); + HP_BAR(player, captureDamage: &damage[0]); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, player); + HP_BAR(opponent, captureDamage: &damage[1]); + ANIMATION(ANIM_TYPE_MOVE, MOVE_BITE, opponent); + MESSAGE("Wobbuffet flinched and couldn't move!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, player); + HP_BAR(opponent, captureDamage: &damage[2]); + } THEN { + EXPECT_EQ(damage[0], damage[2]); + EXPECT_NE(damage[1], damage[2]); + } +} + +SINGLE_BATTLE_TEST("Echoed Voice's power is increased even if it misses") +{ + s16 damage[3]; + + GIVEN { + ASSUME(GetMoveEffect(MOVE_SAND_ATTACK) == EFFECT_ACCURACY_DOWN); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_ECHOED_VOICE); } + TURN { MOVE(player, MOVE_ECHOED_VOICE); MOVE(opponent, MOVE_SAND_ATTACK); } + TURN { MOVE(player, MOVE_ECHOED_VOICE, hit: FALSE); } + TURN { MOVE(player, MOVE_ECHOED_VOICE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, player); + HP_BAR(opponent, captureDamage: &damage[0]); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, player); + HP_BAR(opponent, captureDamage: &damage[1]); + MESSAGE("Wobbuffet's attack missed!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, player); + HP_BAR(opponent, captureDamage: &damage[2]); + } THEN { + EXPECT_MUL_EQ(damage[0], UQ_4_12(2.0), damage[1]); + EXPECT_MUL_EQ(damage[0], UQ_4_12(4.0), damage[2]); + } +} + +SINGLE_BATTLE_TEST("Echoed Voice's power is increased even if it's blocked by Protect") +{ + s16 damage[3]; + + GIVEN { + ASSUME(GetMoveEffect(MOVE_PROTECT) == EFFECT_PROTECT); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_ECHOED_VOICE); } + TURN { MOVE(player, MOVE_ECHOED_VOICE); } + TURN { MOVE(player, MOVE_ECHOED_VOICE); MOVE(opponent, MOVE_PROTECT); } + TURN { MOVE(player, MOVE_ECHOED_VOICE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, player); + HP_BAR(opponent, captureDamage: &damage[0]); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, player); + HP_BAR(opponent, captureDamage: &damage[1]); + ANIMATION(ANIM_TYPE_MOVE, MOVE_PROTECT, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, player); + HP_BAR(opponent, captureDamage: &damage[2]); + } THEN { + EXPECT_MUL_EQ(damage[0], UQ_4_12(2.0), damage[1]); + EXPECT_MUL_EQ(damage[0], UQ_4_12(4.0), damage[2]); + } +}