diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc index 1f99e53e4e..c384571776 100644 --- a/asm/macros/battle_script.inc +++ b/asm/macros/battle_script.inc @@ -1099,7 +1099,7 @@ .byte 0xca .endm - .macro setcharge battler:req + .macro unused_0xcb battler:req .byte 0xcb .byte \battler .endm diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 765f2f5b77..48324bd149 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -2291,7 +2291,7 @@ BattleScript_TryTailwindAbilitiesLoop_WindRider: BattleScript_TryTailwindAbilitiesLoop_WindPower: call BattleScript_AbilityPopUp - setcharge BS_TARGET + setvolatile BS_TARGET, VOLATILE_CHARGE_TIMER, 2 printstring STRINGID_BEINGHITCHARGEDPKMNWITHPOWER waitmessage B_WAIT_TIME_LONG goto BattleScript_TryTailwindAbilitiesLoop_Increment @@ -4022,7 +4022,7 @@ BattleScript_EffectFollowMe:: BattleScript_EffectCharge:: attackcanceler - setcharge BS_ATTACKER + setvolatile BS_ATTACKER, VOLATILE_CHARGE_TIMER, 2 attackanimation waitanimation .if B_CHARGE_SPDEF_RAISE >= GEN_5 @@ -5339,7 +5339,7 @@ BattleScript_AngerShellRet: BattleScript_WindPowerActivates:: call BattleScript_AbilityPopUp - setcharge BS_TARGET + setvolatile BS_TARGET, VOLATILE_CHARGE_TIMER, 1 printstring STRINGID_BEINGHITCHARGEDPKMNWITHPOWER waitmessage B_WAIT_TIME_LONG return diff --git a/include/battle.h b/include/battle.h index 92018ea8db..36fec03bdd 100755 --- a/include/battle.h +++ b/include/battle.h @@ -102,8 +102,7 @@ struct DisableStruct u8 battlerWithSureHit; u8 isFirstTurn; u8 mimickedMoves:4; - u8 chargeTimer:4; - u8 rechargeTimer; + u8 rechargeTimer:4; u8 autotomizeCount; u16 slowStartTimer; u16 embargoTimer; diff --git a/include/battle_util.h b/include/battle_util.h index e6855af5b5..15a156dac4 100644 --- a/include/battle_util.h +++ b/include/battle_util.h @@ -407,7 +407,6 @@ bool32 IsPursuitTargetSet(void); void ClearPursuitValuesIfSet(u32 battler); void ClearPursuitValues(void); bool32 HasWeatherEffect(void); -bool32 IsAnyTargetAffected(u32 battlerAtk); bool32 IsFutureSightAttackerInParty(u32 battlerAtk, u32 battlerDef, u32 move); bool32 HadMoreThanHalfHpNowDoesnt(u32 battler); void ChooseStatBoostAnimation(u32 battler); diff --git a/include/constants/battle.h b/include/constants/battle.h index fe8ff2b0a7..8dacb7a60a 100644 --- a/include/constants/battle.h +++ b/include/constants/battle.h @@ -199,7 +199,7 @@ enum VolatileFlags F(VOLATILE_LOCK_ON, lockOn, (u32, 2), V_BATON_PASSABLE) \ F(VOLATILE_PERISH_SONG, perishSong, (u32, 1), V_BATON_PASSABLE) \ F(VOLATILE_MINIMIZE, minimize, (u32, 1)) \ - F(VOLATILE_CHARGE, charge, (u32, 1)) \ + F(VOLATILE_CHARGE_TIMER, chargeTimer, (u32, 2)) \ F(VOLATILE_ROOT, root, (u32, 1), V_BATON_PASSABLE) \ F(VOLATILE_YAWN, yawn, (u32, 2)) \ F(VOLATILE_IMPRISON, imprison, (u32, 1)) \ diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index 5db2732d4d..5fc7e3fcbf 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -1447,7 +1447,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) } break; case EFFECT_CHARGE: - if (gBattleMons[battlerAtk].volatiles.charge) + if (gBattleMons[battlerAtk].volatiles.chargeTimer > 0) ADJUST_SCORE(-20); else if (!HasMoveWithType(battlerAtk, TYPE_ELECTRIC)) ADJUST_SCORE(-10); diff --git a/src/battle_debug.c b/src/battle_debug.c index 23e8a74ff6..836329d660 100644 --- a/src/battle_debug.c +++ b/src/battle_debug.c @@ -378,7 +378,7 @@ static const struct ListMenuItem sVolatileStatusListItems[] = {COMPOUND_STRING("Lock On"), VOLATILE_LOCK_ON}, {COMPOUND_STRING("Perish Song"), VOLATILE_PERISH_SONG}, {COMPOUND_STRING("Minimize"), VOLATILE_MINIMIZE}, - {COMPOUND_STRING("Charge"), VOLATILE_CHARGE}, + {COMPOUND_STRING("Charge"), VOLATILE_CHARGE_TIMER}, {COMPOUND_STRING("Root"), VOLATILE_ROOT}, {COMPOUND_STRING("Yawn"), VOLATILE_YAWN}, {COMPOUND_STRING("Imprison"), VOLATILE_IMPRISON}, diff --git a/src/battle_end_turn.c b/src/battle_end_turn.c index d6d8601383..b4d71c2cf4 100644 --- a/src/battle_end_turn.c +++ b/src/battle_end_turn.c @@ -63,8 +63,8 @@ static bool32 HandleEndTurnVarious(u32 battler) if (gBattleMons[i].volatiles.lockOn > 0) gBattleMons[i].volatiles.lockOn--; - if (gDisableStructs[i].chargeTimer > 0 && --gDisableStructs[i].chargeTimer == 0) - gBattleMons[i].volatiles.charge = FALSE; + if (B_CHARGE < GEN_9 && gBattleMons[i].volatiles.chargeTimer > 0) + gBattleMons[i].volatiles.chargeTimer--; if (gDisableStructs[i].laserFocusTimer > 0 && --gDisableStructs[i].laserFocusTimer == 0) gBattleMons[i].volatiles.laserFocus = FALSE; diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index adea3fb925..6caa81050f 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -336,6 +336,8 @@ static void AccuracyCheck(bool32 recalcDragonDarts, const u8 *nextInstr, const u static void ResetValuesForCalledMove(void); static bool32 TrySymbiosis(u32 battler, u32 itemId, bool32 moveEnd); static bool32 CanAbilityShieldActivateForBattler(u32 battler); +static void TryClearChargeVolatile(u32 moveType); +static bool32 IsAnyTargetAffected(void); static void Cmd_attackcanceler(void); static void Cmd_accuracycheck(void); @@ -540,7 +542,7 @@ static void Cmd_unused_0xC7(void); static void Cmd_unused_c8(void); static void Cmd_trymemento(void); static void Cmd_setforcedtarget(void); -static void Cmd_setcharge(void); +static void Cmd_unused_0xcb(void); static void Cmd_unused_0xCC(void); static void Cmd_curestatuswithmove(void); static void Cmd_settorment(void); @@ -799,7 +801,7 @@ void (*const gBattleScriptingCommandsTable[])(void) = Cmd_unused_c8, //0xC8 Cmd_trymemento, //0xC9 Cmd_setforcedtarget, //0xCA - Cmd_setcharge, //0xCB + Cmd_unused_0xcb, //0xCB Cmd_unused_0xCC, //0xCC Cmd_curestatuswithmove, //0xCD Cmd_settorment, //0xCE @@ -1017,6 +1019,33 @@ bool32 IsMoveNotAllowedInSkyBattles(u32 move) return (gBattleStruct->isSkyBattle && IsMoveSkyBattleBanned(gCurrentMove)); } +static void TryClearChargeVolatile(u32 moveType) +{ + if (B_CHARGE < GEN_9) // Prior to gen9, charge is cleared during the end turn + return; + + if (gBattleMons[gBattlerAttacker].volatiles.chargeTimer == 2) // Has been set this turn by move + gBattleMons[gBattlerAttacker].volatiles.chargeTimer--; + else if (moveType == TYPE_ELECTRIC && gBattleMons[gBattlerAttacker].volatiles.chargeTimer == 1) + gBattleMons[gBattlerAttacker].volatiles.chargeTimer = 0; +} + +static bool32 IsAnyTargetAffected(void) +{ + if (gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE) + return FALSE; + + for (u32 battler = 0; battler < gBattlersCount; battler++) + { + if (battler == gBattlerAttacker) + continue; + + if (!(gBattleStruct->moveResultFlags[battler] & MOVE_RESULT_NO_EFFECT)) + return TRUE; + } + return FALSE; +} + u32 NumAffectedSpreadMoveTargets(void) { u32 targetCount = 0; @@ -7025,9 +7054,7 @@ static void Cmd_moveend(void) gBattleScripting.moveendState++; break; case MOVEEND_SAME_MOVE_TURNS: - if (gCurrentMove != gLastResultingMoves[gBattlerAttacker] - || gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_NO_EFFECT - || gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE) + if (gCurrentMove != gLastResultingMoves[gBattlerAttacker] || !IsAnyTargetAffected()) gBattleStruct->metronomeItemCounter[gBattlerAttacker] = 0; else if (gCurrentMove == gLastResultingMoves[gBattlerAttacker] && gSpecialStatuses[gBattlerAttacker].parentalBondState != PARENTAL_BOND_1ST_HIT) gBattleStruct->metronomeItemCounter[gBattlerAttacker]++; @@ -7049,12 +7076,10 @@ static void Cmd_moveend(void) && gBattleMons[gBattlerAttacker].volatiles.lockConfusionTurns != 1) // And won't end this turn CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_IGNORE); // Cancel it + TryClearChargeVolatile(moveType); ValidateSavedBattlerCounts(); gProtectStructs[gBattlerAttacker].shellTrap = FALSE; gBattleStruct->battlerState[gBattlerAttacker].ateBoost = FALSE; - gSpecialStatuses[gBattlerAttacker].gemBoost = FALSE; - gSpecialStatuses[gBattlerTarget].berryReduced = FALSE; - gSpecialStatuses[gBattlerTarget].distortedTypeMatchups = FALSE; gBattleScripting.moveEffect = MOVE_EFFECT_NONE; gBattleStruct->swapDamageCategory = FALSE; gBattleStruct->categoryOverride = FALSE; @@ -7070,8 +7095,6 @@ static void Cmd_moveend(void) gBattleStruct->pledgeMove = FALSE; if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_Z_MOVE) 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 (gBattleMons[gBattlerAttacker].volatiles.destinyBond > 0) gBattleMons[gBattlerAttacker].volatiles.destinyBond--; if (moveEffect == EFFECT_ECHOED_VOICE && !(gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE)) @@ -12591,17 +12614,8 @@ static void Cmd_setforcedtarget(void) gBattlescriptCurrInstr = cmd->nextInstr; } -static void Cmd_setcharge(void) +static void Cmd_unused_0xcb(void) { - CMD_ARGS(u8 battler); - - u8 battler = GetBattlerForBattleScript(cmd->battler); - gBattleMons[battler].volatiles.charge = TRUE; - if (B_CHARGE < GEN_9) - gDisableStructs[battler].chargeTimer = 2; - else - gDisableStructs[battler].chargeTimer = 0; - gBattlescriptCurrInstr = cmd->nextInstr; } static void Cmd_unused_0xCC(void) diff --git a/src/battle_tv.c b/src/battle_tv.c index 9444e4dea3..a56862aa26 100644 --- a/src/battle_tv.c +++ b/src/battle_tv.c @@ -603,7 +603,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 (gBattleMons[gBattlerAttacker].volatiles.charge) + if (gBattleMons[gBattlerAttacker].volatiles.chargeTimer > 0) AddMovePoints(PTS_ELECTRIC, move, moveSlot, 0); if (move == MOVE_WISH) diff --git a/src/battle_util.c b/src/battle_util.c index 5507c19497..dfb1b8a35e 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -7137,7 +7137,7 @@ static inline u32 CalcMoveBasePowerAfterModifiers(struct DamageContext *ctx) if (gSpecialStatuses[battlerAtk].gemBoost) modifier = uq4_12_multiply(modifier, uq4_12_add(UQ_4_12(1.0), PercentToUQ4_12(gSpecialStatuses[battlerAtk].gemParam))); - if (gBattleMons[battlerAtk].volatiles.charge && moveType == TYPE_ELECTRIC) + if (moveType == TYPE_ELECTRIC && gBattleMons[battlerAtk].volatiles.chargeTimer > 0) modifier = uq4_12_multiply(modifier, UQ_4_12(2.0)); if (GetMoveEffect(ctx->chosenMove) == EFFECT_ME_FIRST) modifier = uq4_12_multiply(modifier, UQ_4_12(1.5)); diff --git a/test/battle/move_effect/charge.c b/test/battle/move_effect/charge.c index 8883529223..48b77d8bdc 100644 --- a/test/battle/move_effect/charge.c +++ b/test/battle/move_effect/charge.c @@ -131,7 +131,7 @@ SINGLE_BATTLE_TEST("Charge's effect is removed regardless if the next move is El } } -SINGLE_BATTLE_TEST("Charge will not expire if it flinches twice in a row") +SINGLE_BATTLE_TEST("Charge will expire if user flinches while using an electric move") { s16 damage[2]; GIVEN { @@ -151,9 +151,6 @@ SINGLE_BATTLE_TEST("Charge will not expire if it flinches twice in a row") ANIMATION(ANIM_TYPE_MOVE, MOVE_THUNDERBOLT, player); HP_BAR(opponent, captureDamage: &damage[1]); } THEN { - if (B_CHARGE < GEN_9) - EXPECT_EQ(damage[0], damage[1]); - else - EXPECT_MUL_EQ(damage[0], Q_4_12(2.0), damage[1]); + EXPECT_EQ(damage[0], damage[1]); } }