From 98dbf3a575b7f7c93e334f12fd3a57144602aba5 Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Tue, 27 Aug 2024 20:01:51 +0200 Subject: [PATCH] Fixes Charged up status (#5274) Charge changed to last until the damage boost is consumed instead of next turn, previous behavior accessible with `B_CHARGE` --- include/battle.h | 8 +- include/config/battle.h | 1 + src/battle_script_commands.c | 9 +- src/battle_tv.c | 2 +- src/battle_util.c | 4 +- test/battle/move_effect/charge.c | 170 +++++++++++++++++++++++++++++-- 6 files changed, 179 insertions(+), 15 deletions(-) diff --git a/include/battle.h b/include/battle.h index 4db357fca6..1729c76b3d 100644 --- a/include/battle.h +++ b/include/battle.h @@ -115,17 +115,15 @@ struct DisableStruct u8 disableTimer:4; u8 encoreTimer:4; u8 perishSongTimer:4; - u8 furyCutterCounter; u8 rolloutTimer:4; u8 rolloutTimerStartValue:4; - u8 chargeTimer:4; u8 tauntTimer:4; + u8 furyCutterCounter; u8 battlerPreventingEscape; u8 battlerWithSureHit; u8 isFirstTurn; - u8 truantCounter:1; - u8 truantSwitchInHack:1; u8 mimickedMoves:4; + u8 chargeTimer:4; u8 rechargeTimer; u8 autotomizeCount; u8 slowStartTimer; @@ -138,6 +136,8 @@ struct DisableStruct u8 wrapTurns; u8 tormentTimer:4; // used for G-Max Meltdown u8 usedMoves:4; + u8 truantCounter:1; + u8 truantSwitchInHack:1; u8 noRetreat:1; u8 tarShot:1; u8 octolock:1; diff --git a/include/config/battle.h b/include/config/battle.h index 1cb4a4a6ce..55091fb331 100644 --- a/include/config/battle.h +++ b/include/config/battle.h @@ -121,6 +121,7 @@ #define B_SKETCH_BANS GEN_LATEST // In Gen9+, Sketch is unable to copy more moves than in previous generations. #define B_KNOCK_OFF_REMOVAL GEN_LATEST // In Gen5+, Knock Off removes the foe's item instead of rendering it unusable. #define B_HEAL_BELL_SOUNDPROOF GEN_LATEST // In Gen5, Heal Bell affects all mons with Soundproof. In Gen6-8 it affects inactive mons, but not battlers. In Gen9 it always affects the user. +#define B_CHARGE GEN_LATEST // In Gen8-, Charge status is lost regardless of the typing of the next move. // Ability settings #define B_EXPANDED_ABILITY_NAMES TRUE // If TRUE, ability names are increased from 12 characters to 16 characters. diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index c4f589a818..9e1af78714 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -6427,9 +6427,11 @@ static void Cmd_moveend(void) gBattleStruct->additionalEffectsCounter = 0; gBattleStruct->poisonPuppeteerConfusion = FALSE; gBattleStruct->fickleBeamBoosted = FALSE; + gBattleStruct->distortedTypeMatchups = 0; if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_Z_MOVE) SetActiveGimmick(gBattlerAttacker, GIMMICK_NONE); - gBattleStruct->distortedTypeMatchups = 0; + if (B_CHARGE <= GEN_8 || moveType == TYPE_ELECTRIC) + gStatuses3[gBattlerAttacker] &= ~(STATUS3_CHARGED_UP); memset(gQueuedStatBoosts, 0, sizeof(gQueuedStatBoosts)); gBattleScripting.moveendState++; break; @@ -14012,7 +14014,10 @@ static void Cmd_setcharge(void) u8 battler = GetBattlerForBattleScript(cmd->battler); gStatuses3[battler] |= STATUS3_CHARGED_UP; - gDisableStructs[battler].chargeTimer = 2; + if (B_CHARGE < GEN_9) + gDisableStructs[battler].chargeTimer = 2; + else + gDisableStructs[battler].chargeTimer = 0; gBattlescriptCurrInstr++; gBattlescriptCurrInstr = cmd->nextInstr; } diff --git a/src/battle_tv.c b/src/battle_tv.c index bba14054c9..24d573d0bf 100644 --- a/src/battle_tv.c +++ b/src/battle_tv.c @@ -775,7 +775,7 @@ void BattleTv_SetDataBasedOnMove(u16 move, u16 weatherFlags, struct DisableStruc tvPtr->side[atkSide].usedMoveSlot = moveSlot; AddMovePoints(PTS_MOVE_EFFECT, moveSlot, move, 0); AddPointsBasedOnWeather(weatherFlags, move, moveSlot); - if (disableStructPtr->chargeTimer != 0) + if (gStatuses3[gBattlerAttacker] & STATUS3_CHARGED_UP) AddMovePoints(PTS_ELECTRIC, move, moveSlot, 0); if (move == MOVE_WISH) diff --git a/src/battle_util.c b/src/battle_util.c index 6170ad0e48..c6199453ea 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -2303,8 +2303,8 @@ enum ENDTURN_HEALBLOCK, ENDTURN_EMBARGO, ENDTURN_LOCK_ON, - ENDTURN_CHARGE, ENDTURN_LASER_FOCUS, + ENDTURN_CHARGE, ENDTURN_TAUNT, ENDTURN_YAWN, ENDTURN_ITEMS2, @@ -2767,7 +2767,7 @@ u8 DoBattlerEndTurnEffects(void) gBattleStruct->turnEffectsTracker++; break; case ENDTURN_CHARGE: // charge - if (gDisableStructs[battler].chargeTimer && --gDisableStructs[battler].chargeTimer == 0) + if (gDisableStructs[battler].chargeTimer > 0 && --gDisableStructs[battler].chargeTimer == 0) gStatuses3[battler] &= ~STATUS3_CHARGED_UP; gBattleStruct->turnEffectsTracker++; break; diff --git a/test/battle/move_effect/charge.c b/test/battle/move_effect/charge.c index e0a782b15f..f95a137993 100644 --- a/test/battle/move_effect/charge.c +++ b/test/battle/move_effect/charge.c @@ -1,9 +1,167 @@ #include "global.h" #include "test/battle.h" -TO_DO_BATTLE_TEST("Charge doubles the damage of the next Electric move of the user"); -TO_DO_BATTLE_TEST("Charge's effect is removed regardless if the next move is Electric or not (Gen 3-8)"); -TO_DO_BATTLE_TEST("Charge's effect is kept until the user uses an Electric move (Gen 9+)"); -TO_DO_BATTLE_TEST("Charge's effect is removed if the user fails using an Electric move (Gen 9+)"); -TO_DO_BATTLE_TEST("Charge's effect does not stack with Electromorphosis"); -TO_DO_BATTLE_TEST("Charge's effect does not stack with Wind Power"); +ASSUMPTIONS +{ + ASSUME(gMovesInfo[MOVE_THUNDERBOLT].power != 0); + ASSUME(gMovesInfo[MOVE_THUNDERBOLT].type == TYPE_ELECTRIC); +} + +SINGLE_BATTLE_TEST("Charge doubles the damage of the next Electric move of the user") +{ + s16 normalDamage = 0; + s16 chargedUpDamage = 0; + + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_THUNDERBOLT); } + TURN { MOVE(player, MOVE_CHARGE); } + TURN { MOVE(player, MOVE_THUNDERBOLT); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_THUNDERBOLT, player); + HP_BAR(opponent, captureDamage: &normalDamage); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CHARGE, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_THUNDERBOLT, player); + HP_BAR(opponent, captureDamage: &chargedUpDamage); + } THEN { + EXPECT_MUL_EQ(normalDamage, Q_4_12(2.0), chargedUpDamage); + } +} + +SINGLE_BATTLE_TEST("Charge's effect is kept until the user uses an Electric move (Gen 9+)") +{ + s16 normalDamage = 0; + s16 chargedUpDamage = 0; + + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_THUNDERBOLT); } + TURN { MOVE(player, MOVE_CHARGE); } + TURN { MOVE(player, MOVE_TACKLE); } + TURN { MOVE(player, MOVE_THUNDERBOLT); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_THUNDERBOLT, player); + HP_BAR(opponent, captureDamage: &normalDamage); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CHARGE, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_THUNDERBOLT, player); + HP_BAR(opponent, captureDamage: &chargedUpDamage); + } THEN { + EXPECT_MUL_EQ(normalDamage, Q_4_12(2.0), chargedUpDamage); + } +} + +SINGLE_BATTLE_TEST("Charge's effect is removed if the user fails using an Electric move (Gen 9+)") +{ + s16 damage[2]; + + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_THUNDER); } + TURN { MOVE(player, MOVE_CHARGE); } + TURN { MOVE(player, MOVE_THUNDER, hit: FALSE); } + TURN { MOVE(player, MOVE_THUNDER); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_THUNDER, player); + HP_BAR(opponent, captureDamage: &damage[0]); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CHARGE, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_THUNDER, player); + HP_BAR(opponent, captureDamage: &damage[1]); + } THEN { + EXPECT_EQ(damage[0], damage[1]); + } +} + +SINGLE_BATTLE_TEST("Charge's effect does not stack with Electromorphosis or Wind Power") +{ + u32 species, ability; + s16 normalDamage = 0; + s16 chargedUpDamage = 0; + + PARAMETRIZE { species = SPECIES_WATTREL; ability = ABILITY_WIND_POWER; } + PARAMETRIZE { species = SPECIES_TADBULB; ability = ABILITY_ELECTROMORPHOSIS; } + + GIVEN { + ASSUME(gMovesInfo[MOVE_AIR_CUTTER].windMove == TRUE); + PLAYER(species) { Ability(ability); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_THUNDERBOLT); } + TURN { MOVE(player, MOVE_CHARGE); MOVE(opponent, MOVE_AIR_CUTTER); } + TURN { MOVE(player, MOVE_THUNDERBOLT); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_THUNDERBOLT, player); + HP_BAR(opponent, captureDamage: &normalDamage); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CHARGE, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_AIR_CUTTER, opponent); + ABILITY_POPUP(player, ability); + ANIMATION(ANIM_TYPE_MOVE, MOVE_THUNDERBOLT, player); + HP_BAR(opponent, captureDamage: &chargedUpDamage); + } THEN { + EXPECT_MUL_EQ(normalDamage, Q_4_12(2.0), chargedUpDamage); + } +} + +SINGLE_BATTLE_TEST("Charge's effect is removed regardless if the next move is Electric or not (Gen 3-8)") +{ + s16 normalDamage = 0; + s16 chargedUpDamage = 0; + + GIVEN { + ASSUME(gMovesInfo[MOVE_TACKLE].type != TYPE_ELECTRIC); + ASSUME(gMovesInfo[MOVE_TACKLE].power != 0); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_THUNDERBOLT); } + TURN { MOVE(player, MOVE_CHARGE); } + TURN { MOVE(player, MOVE_TACKLE); } + TURN { MOVE(player, MOVE_THUNDERBOLT); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_THUNDERBOLT, player); + HP_BAR(opponent, captureDamage: &normalDamage); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CHARGE, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_THUNDERBOLT, player); + HP_BAR(opponent, captureDamage: &chargedUpDamage); + } THEN { + if (B_CHARGE < GEN_9) + EXPECT_EQ(normalDamage, chargedUpDamage); + else + EXPECT_MUL_EQ(normalDamage, Q_4_12(2.0), chargedUpDamage); + } +} + +SINGLE_BATTLE_TEST("Charge will not expire if it flinches twice in a row") +{ + s16 normalDamage = 0; + s16 chargedUpDamage = 0; + + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_LUM_BERRY); } + } WHEN { + TURN { MOVE(player, MOVE_THUNDERBOLT); } + TURN { MOVE(player, MOVE_CHARGE); } + TURN { MOVE(opponent, MOVE_IRON_HEAD); MOVE(player, MOVE_THUNDERBOLT); } + TURN { MOVE(opponent, MOVE_IRON_HEAD); MOVE(player, MOVE_THUNDERBOLT); } + TURN { MOVE(player, MOVE_THUNDERBOLT); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_THUNDERBOLT, player); + HP_BAR(opponent, captureDamage: &normalDamage); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CHARGE, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_THUNDERBOLT, player); + HP_BAR(opponent, captureDamage: &chargedUpDamage); + } THEN { + if (B_CHARGE < GEN_9) + EXPECT_EQ(normalDamage, chargedUpDamage); + else + EXPECT_MUL_EQ(normalDamage, Q_4_12(2.0), chargedUpDamage); + } +}