Move end clear bits clean up (#8354)

Co-authored-by: PhallenTree <168426989+PhallenTree@users.noreply.github.com>
This commit is contained in:
Alex 2025-11-27 17:42:16 +01:00 committed by GitHub
parent 9fde188033
commit 2d628aca0e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 48 additions and 39 deletions

View File

@ -1099,7 +1099,7 @@
.byte 0xca
.endm
.macro setcharge battler:req
.macro unused_0xcb battler:req
.byte 0xcb
.byte \battler
.endm

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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