Fixes Instructed moves looking at the wrong turn order number (#7788)

This commit is contained in:
PhallenTree 2025-09-24 18:11:45 +01:00 committed by GitHub
parent b244740f52
commit 58b9642cb1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 78 additions and 23 deletions

View File

@ -44,7 +44,8 @@ s32 CalcCritChanceStage(u32 battlerAtk, u32 battlerDef, u32 move, bool32 recordA
s32 CalcCritChanceStageGen1(u32 battlerAtk, u32 battlerDef, u32 move, bool32 recordAbility, u32 abilityAtk, u32 abilityDef, enum ItemHoldEffect holdEffectAtk);
s32 GetCritHitOdds(s32 critChanceIndex);
u32 GetTotalAccuracy(u32 battlerAtk, u32 battlerDef, u32 move, u32 atkAbility, u32 defAbility, u32 atkHoldEffect, u32 defHoldEffect);
u8 GetBattlerTurnOrderNum(u8 battler);
bool32 HasBattlerActedThisTurn(u32 battler);
u32 GetBattlerTurnOrderNum(u32 battler);
bool32 NoAliveMonsForBattlerSide(u32 battler);
bool32 NoAliveMonsForPlayer(void);
bool32 NoAliveMonsForEitherParty(void);

View File

@ -2804,9 +2804,20 @@ static void Cmd_printselectionstringfromtable(void)
}
}
u8 GetBattlerTurnOrderNum(u8 battler)
bool32 HasBattlerActedThisTurn(u32 battler)
{
s32 i;
u32 i;
for (i = 0; i < gCurrentTurnActionNumber; i++)
{
if (gBattlerByTurnOrder[i] == battler)
return TRUE;
}
return FALSE;
}
u32 GetBattlerTurnOrderNum(u32 battler)
{
u32 i;
for (i = 0; i < gBattlersCount; i++)
{
if (gBattlerByTurnOrder[i] == battler)
@ -3091,8 +3102,8 @@ void SetMoveEffect(u32 battler, u32 effectBattler, bool32 primary, bool32 certai
{
gBattlescriptCurrInstr++;
}
else if (GetBattlerTurnOrderNum(gEffectBattler) > gCurrentTurnActionNumber
&& !(GetActiveGimmick(gEffectBattler) == GIMMICK_DYNAMAX))
else if (!HasBattlerActedThisTurn(gEffectBattler)
&& GetActiveGimmick(gEffectBattler) != GIMMICK_DYNAMAX)
{
gBattleMons[gEffectBattler].volatiles.flinched = TRUE;
gBattlescriptCurrInstr++;
@ -3425,7 +3436,7 @@ void SetMoveEffect(u32 battler, u32 effectBattler, bool32 primary, bool32 certai
}
break;
case MOVE_EFFECT_CORE_ENFORCER:
if (GetBattlerTurnOrderNum(gBattlerAttacker) > GetBattlerTurnOrderNum(gBattlerTarget)
if (HasBattlerActedThisTurn(gBattlerTarget)
&& !NoAliveMonsForEitherParty())
{
BattleScriptPush(gBattlescriptCurrInstr + 1);
@ -9455,10 +9466,12 @@ static bool32 ChangeOrderTargetAfterAttacker(void)
u32 i;
u8 data[MAX_BATTLERS_COUNT];
u8 actionsData[MAX_BATTLERS_COUNT];
u32 attackerTurnOrderNum = GetBattlerTurnOrderNum(gBattlerAttacker);
u32 targetTurnOrderNum = GetBattlerTurnOrderNum(gBattlerTarget);
if (GetBattlerTurnOrderNum(gBattlerAttacker) > GetBattlerTurnOrderNum(gBattlerTarget))
if (attackerTurnOrderNum > targetTurnOrderNum)
return FALSE;
if (GetBattlerTurnOrderNum(gBattlerAttacker) + 1 == GetBattlerTurnOrderNum(gBattlerTarget))
if (attackerTurnOrderNum + 1 == targetTurnOrderNum)
return GetGenConfig(GEN_CONFIG_AFTER_YOU_TURN_ORDER) >= GEN_8;
for (i = 0; i < MAX_BATTLERS_COUNT; i++)
@ -9466,14 +9479,14 @@ static bool32 ChangeOrderTargetAfterAttacker(void)
data[i] = gBattlerByTurnOrder[i];
actionsData[i] = gActionsByTurnOrder[i];
}
if (GetBattlerTurnOrderNum(gBattlerAttacker) == 0 && GetBattlerTurnOrderNum(gBattlerTarget) == 2)
if (attackerTurnOrderNum == 0 && targetTurnOrderNum == 2)
{
gBattlerByTurnOrder[1] = gBattlerTarget;
gActionsByTurnOrder[1] = actionsData[2];
gBattlerByTurnOrder[2] = data[1];
gActionsByTurnOrder[2] = actionsData[1];
}
else if (GetBattlerTurnOrderNum(gBattlerAttacker) == 0 && GetBattlerTurnOrderNum(gBattlerTarget) == 3)
else if (attackerTurnOrderNum == 0 && targetTurnOrderNum == 3)
{
gBattlerByTurnOrder[1] = gBattlerTarget;
gActionsByTurnOrder[1] = actionsData[3];
@ -9482,7 +9495,7 @@ static bool32 ChangeOrderTargetAfterAttacker(void)
gBattlerByTurnOrder[3] = data[2];
gActionsByTurnOrder[3] = actionsData[2];
}
else // Attacker == 1, Target == 3
else // attackerTurnOrderNum == 1, targetTurnOrderNum == 3
{
gBattlerByTurnOrder[2] = gBattlerTarget;
gActionsByTurnOrder[2] = actionsData[3];
@ -11436,7 +11449,7 @@ static void Cmd_trysetencore(void)
gDisableStructs[gBattlerTarget].encoredMove = gBattleMons[gBattlerTarget].moves[i];
gDisableStructs[gBattlerTarget].encoredMovePos = i;
// Encore always lasts 3 turns, but we need to account for a scenario where Encore changes the move during the same turn.
if (GetBattlerTurnOrderNum(gBattlerAttacker) > GetBattlerTurnOrderNum(gBattlerTarget))
if (HasBattlerActedThisTurn(gBattlerTarget))
gDisableStructs[gBattlerTarget].encoreTimer = 4;
else
gDisableStructs[gBattlerTarget].encoreTimer = 3;
@ -12742,7 +12755,7 @@ static void Cmd_settaunt(void)
if (B_TAUNT_TURNS >= GEN_5)
{
turns = 4;
if (GetBattlerTurnOrderNum(gBattlerTarget) > GetBattlerTurnOrderNum(gBattlerAttacker))
if (!HasBattlerActedThisTurn(gBattlerTarget))
turns--; // If the target hasn't yet moved this turn, Taunt lasts for only three turns (source: Bulbapedia)
}
else if (B_TAUNT_TURNS >= GEN_4)
@ -12771,7 +12784,7 @@ static void Cmd_trysethelpinghand(void)
if (IsDoubleBattle()
&& !(gAbsentBattlerFlags & (1u << gBattlerTarget))
&& gCurrentTurnActionNumber < GetBattlerTurnOrderNum(gBattlerTarget))
&& !HasBattlerActedThisTurn(gBattlerTarget))
{
gProtectStructs[gBattlerTarget].helpingHand++;
gBattlescriptCurrInstr = cmd->nextInstr;
@ -15401,7 +15414,7 @@ void BS_SetPledge(void)
else if ((gChosenActionByBattler[partner] == B_ACTION_USE_MOVE)
&& IsDoubleBattle()
&& IsBattlerAlive(partner)
&& GetBattlerTurnOrderNum(gBattlerAttacker) < GetBattlerTurnOrderNum(partner)
&& !HasBattlerActedThisTurn(partner)
&& !(gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE)
&& gCurrentMove != partnerMove
&& GetMoveEffect(partnerMove) == EFFECT_PLEDGE)
@ -15600,7 +15613,7 @@ void BS_TryUpperHand(void)
u32 abilityDef = GetBattlerAbility(gBattlerTarget);
u32 prio = GetChosenMovePriority(gBattlerTarget, abilityDef);
if (GetBattlerTurnOrderNum(gBattlerAttacker) > GetBattlerTurnOrderNum(gBattlerTarget)
if (HasBattlerActedThisTurn(gBattlerTarget)
|| gChosenMoveByBattler[gBattlerTarget] == MOVE_NONE
|| IsBattleMoveStatus(gChosenMoveByBattler[gBattlerTarget])
|| prio < 1
@ -15817,7 +15830,7 @@ void BS_TryQuash(void)
u32 i, j;
// It's true if foe is faster, has a bigger priority, or switches
if (GetBattlerTurnOrderNum(gBattlerAttacker) > GetBattlerTurnOrderNum(gBattlerTarget))
if (HasBattlerActedThisTurn(gBattlerTarget))
{
gBattlescriptCurrInstr = cmd->failInstr;
return;
@ -17346,7 +17359,7 @@ void BS_SuckerPunchCheck(void)
NATIVE_ARGS(const u8 *failInstr);
if (gProtectStructs[gBattlerTarget].protected == PROTECT_OBSTRUCT)
gBattlescriptCurrInstr = cmd->failInstr;
else if (GetBattlerTurnOrderNum(gBattlerAttacker) > GetBattlerTurnOrderNum(gBattlerTarget))
else if (HasBattlerActedThisTurn(gBattlerTarget))
gBattlescriptCurrInstr = cmd->failInstr;
else if (IsBattleMoveStatus(gBattleMons[gBattlerTarget].moves[gBattleStruct->chosenMovePositions[gBattlerTarget]]) && !gProtectStructs[gBattlerTarget].noValidMoves)
gBattlescriptCurrInstr = cmd->failInstr;
@ -17433,7 +17446,7 @@ void BS_TryMeFirst(void)
NATIVE_ARGS(const u8 *failInstr);
u16 move = gBattleMons[gBattlerTarget].moves[gBattleStruct->chosenMovePositions[gBattlerTarget]];
if (IsBattleMoveStatus(move) || IsMoveMeFirstBanned(move)
|| GetBattlerTurnOrderNum(gBattlerAttacker) > GetBattlerTurnOrderNum(gBattlerTarget))
|| HasBattlerActedThisTurn(gBattlerTarget))
{
gBattlescriptCurrInstr = cmd->failInstr;
}
@ -17457,7 +17470,7 @@ void BS_TryMeFirst(void)
void BS_TryElectrify(void)
{
NATIVE_ARGS(const u8 *failInstr);
if (GetBattlerTurnOrderNum(gBattlerAttacker) > GetBattlerTurnOrderNum(gBattlerTarget))
if (HasBattlerActedThisTurn(gBattlerTarget))
{
gBattlescriptCurrInstr = cmd->failInstr;
}

View File

@ -8169,12 +8169,12 @@ static inline u32 CalcMoveBasePower(struct DamageContext *ctx)
}
break;
case EFFECT_PAYBACK:
if (GetBattlerTurnOrderNum(battlerAtk) > GetBattlerTurnOrderNum(battlerDef)
if (HasBattlerActedThisTurn(battlerDef)
&& (B_PAYBACK_SWITCH_BOOST < GEN_5 || gDisableStructs[battlerDef].isFirstTurn != 2))
basePower *= 2;
break;
case EFFECT_BOLT_BEAK:
if (GetBattlerTurnOrderNum(battlerAtk) < GetBattlerTurnOrderNum(battlerDef)
if (!HasBattlerActedThisTurn(battlerDef)
|| gDisableStructs[battlerDef].isFirstTurn == 2)
basePower *= 2;
break;
@ -11818,7 +11818,7 @@ u32 GetTotalAccuracy(u32 battlerAtk, u32 battlerDef, u32 move, u32 atkAbility, u
calc = (calc * (100 + atkParam)) / 100;
break;
case HOLD_EFFECT_ZOOM_LENS:
if (GetBattlerTurnOrderNum(battlerAtk) > GetBattlerTurnOrderNum(battlerDef))
if (HasBattlerActedThisTurn(battlerDef))
calc = (calc * (100 + atkParam)) / 100;
break;
}

View File

@ -51,3 +51,22 @@ SINGLE_BATTLE_TEST("Sucker Punch doesn't hit targets that has already moved")
}
}
}
DOUBLE_BATTLE_TEST("Sucker Punch fails if the target has attempted to act even if previously successful")
{
GIVEN {
ASSUME(GetMoveEffect(MOVE_INSTRUCT) == EFFECT_INSTRUCT);
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponentLeft, MOVE_SCRATCH, target: playerLeft); MOVE(playerLeft, MOVE_SUCKER_PUNCH, target: opponentLeft); MOVE(playerRight, MOVE_INSTRUCT, target: playerLeft); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SUCKER_PUNCH, playerLeft);
HP_BAR(opponentLeft);
ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, opponentLeft);
ANIMATION(ANIM_TYPE_MOVE, MOVE_INSTRUCT, playerRight);
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_SUCKER_PUNCH, playerLeft);
}
}

View File

@ -132,3 +132,25 @@ AI_SINGLE_BATTLE_TEST("AI won't use Upper Hand unless it has seen a priority mov
TURN { MOVE(player, move); EXPECT_MOVE(opponent, move == MOVE_QUICK_ATTACK ? MOVE_UPPER_HAND : MOVE_KARATE_CHOP); }
}
}
DOUBLE_BATTLE_TEST("Upper Hand fails if the target has attempted to act even if previously successful")
{
GIVEN {
ASSUME(GetMoveCategory(MOVE_EXTREME_SPEED) == DAMAGE_CATEGORY_PHYSICAL);
ASSUME(GetMovePriority(MOVE_EXTREME_SPEED) == 2);
ASSUME(GetMoveEffect(MOVE_INSTRUCT) == EFFECT_INSTRUCT);
PLAYER(SPECIES_MIENSHAO);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponentLeft, MOVE_EXTREME_SPEED, target: playerLeft); MOVE(playerLeft, MOVE_UPPER_HAND, target: opponentLeft); MOVE(playerRight, MOVE_INSTRUCT, target: playerLeft); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_UPPER_HAND, playerLeft);
HP_BAR(opponentLeft);
MESSAGE("The opposing Wobbuffet flinched and couldn't move!");
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_EXTREME_SPEED, opponentLeft);
ANIMATION(ANIM_TYPE_MOVE, MOVE_INSTRUCT, playerRight);
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_UPPER_HAND, playerLeft);
}
}