Fixes Echoed Voice base power increase depending on attacker's use of the move (#7997)

This commit is contained in:
PhallenTree 2025-10-22 12:39:24 +01:00 committed by GitHub
parent 2d73286777
commit 2416bfb53b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 191 additions and 15 deletions

View File

@ -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

View File

@ -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;
}

View File

@ -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;

View File

@ -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

View File

@ -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);

View File

@ -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]);
}
}