From 1a6d03344a38a17a26500ff85690500c3977a325 Mon Sep 17 00:00:00 2001 From: PhallenTree <168426989+PhallenTree@users.noreply.github.com> Date: Mon, 1 Dec 2025 19:12:11 +0000 Subject: [PATCH] Multiple bugfixes (mostly Dancer/called moves) (#8401) --- data/battle_scripts_1.s | 10 ++++++++-- src/battle_script_commands.c | 19 ++++++------------- src/battle_util.c | 26 ++++++++++++++------------ test/battle/ability/dancer.c | 21 +++++++++++++++++++++ test/battle/move_effect/sleep_talk.c | 21 +++++++++++++++++++++ 5 files changed, 70 insertions(+), 27 deletions(-) diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 8754ed053c..bb3f23437c 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -868,8 +868,8 @@ BattleScript_StuffCheeksEnd: BattleScript_EffectDecorate:: attackcanceler accuracycheck BattleScript_MoveMissedPause, ACC_CURR_MOVE - jumpifstat BS_TARGET, CMP_NOT_EQUAL, STAT_ATK, 12, BattleScript_DecorateBoost - jumpifstat BS_TARGET, CMP_NOT_EQUAL, STAT_SPATK, 12, BattleScript_DecorateBoost + jumpifstat BS_TARGET, CMP_NOT_EQUAL, STAT_ATK, MAX_STAT_STAGE, BattleScript_DecorateBoost + jumpifstat BS_TARGET, CMP_NOT_EQUAL, STAT_SPATK, MAX_STAT_STAGE, BattleScript_DecorateBoost goto BattleScript_ButItFailed BattleScript_DecorateBoost: attackanimation @@ -1040,6 +1040,7 @@ BattleScript_EffectStrengthSap:: statbuffchange BS_TARGET, STAT_CHANGE_ALLOW_PTR, BattleScript_MoveEnd printfromtable gStatDownStringIds waitmessage B_WAIT_TIME_LONG + setmoveresultflags MOVE_RESULT_MISSED @ TODO: Is this even necessary? goto BattleScript_MoveEnd BattleScript_StrengthSapTryLower: getstatvalue STAT_ATK @@ -1218,6 +1219,7 @@ BattleScript_EffectAromaticMistWontGoHigher: pause B_WAIT_TIME_SHORTEST printstring STRINGID_TARGETSTATWONTGOHIGHER waitmessage B_WAIT_TIME_LONG + setmoveresultflags MOVE_RESULT_MISSED @ TODO: Is this even necessary? goto BattleScript_EffectAromaticMistEnd BattleScript_EffectMagneticFlux:: @@ -1501,6 +1503,7 @@ BattleScript_DefogWorks: jumpifbyte CMP_LESS_THAN, cMULTISTRING_CHOOSER, B_MSG_STAT_WONT_CHANGE, BattleScript_DefogDoAnim jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, B_MSG_STAT_CHANGE_EMPTY, BattleScript_DefogTryHazardsWithAnim pause B_WAIT_TIME_SHORT + setmoveresultflags MOVE_RESULT_MISSED @ TODO: Is this even necessary? goto BattleScript_DefogPrintString BattleScript_DefogDoAnim:: attackanimation @@ -2608,6 +2611,7 @@ BattleScript_EffectStatUpAfterAtkCanceler:: statbuffchange BS_ATTACKER, STAT_CHANGE_ALLOW_PTR | STAT_CHANGE_ONLY_CHECKING, BattleScript_StatUpEnd jumpifbyte CMP_NOT_EQUAL, cMULTISTRING_CHOOSER, B_MSG_STAT_WONT_CHANGE, BattleScript_StatUpAttackAnim pause B_WAIT_TIME_SHORT + setmoveresultflags MOVE_RESULT_MISSED @ TODO: Is this even necessary? goto BattleScript_StatUpPrintString BattleScript_StatUpAttackAnim:: attackanimation @@ -2661,6 +2665,7 @@ BattleScript_EffectStatDownFromStatBuffChange: jumpifbyte CMP_LESS_THAN, cMULTISTRING_CHOOSER, B_MSG_STAT_WONT_CHANGE, BattleScript_StatDownDoAnim jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, B_MSG_STAT_CHANGE_EMPTY, BattleScript_StatDownEnd pause B_WAIT_TIME_SHORT + setmoveresultflags MOVE_RESULT_MISSED @ TODO: Is this even necessary? goto BattleScript_StatDownPrintString BattleScript_StatDownDoAnim:: attackanimation @@ -6773,6 +6778,7 @@ BattleScript_DeltaStreamActivates:: printstring STRINGID_MYSTERIOUSAIRCURRENT waitstate playanimation BS_ATTACKER, B_ANIM_STRONG_WINDS + call BattleScript_ActivateWeatherAbilities end3 BattleScript_ProtosynthesisActivates:: diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 99847ba3f3..aeaaa5a754 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -1032,9 +1032,6 @@ static void TryClearChargeVolatile(u32 moveType) static bool32 IsAnyTargetAffected(void) { - if (gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE) - return FALSE; - for (u32 battler = 0; battler < gBattlersCount; battler++) { if (battler == gBattlerAttacker) @@ -1234,8 +1231,6 @@ static void Cmd_attackcanceler(void) { gBattleStruct->bouncedMoveIsUsed = TRUE; // Edge case for bouncing a powder move against a grass type pokemon. - - ClearDamageCalcResults(); gEffectBattler = gBattlerTarget; if (BlocksPrankster(gCurrentMove, gBattlerTarget, gBattlerAttacker, TRUE)) { @@ -1268,7 +1263,6 @@ static void Cmd_attackcanceler(void) if (gBattleStruct->bouncedMoveIsUsed) { - ClearDamageCalcResults(); BattleScriptCall(BattleScript_MagicBounce); gBattlerAbility = battler; return; @@ -7046,7 +7040,7 @@ static void Cmd_moveend(void) gBattleScripting.moveendState++; break; case MOVEEND_SAME_MOVE_TURNS: - if (gCurrentMove != gLastResultingMoves[gBattlerAttacker] || !IsAnyTargetAffected()) + if (gCurrentMove != gLastResultingMoves[gBattlerAttacker] || (gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE) || !IsAnyTargetAffected()) gBattleStruct->metronomeItemCounter[gBattlerAttacker] = 0; else if (gCurrentMove == gLastResultingMoves[gBattlerAttacker] && gSpecialStatuses[gBattlerAttacker].parentalBondState != PARENTAL_BOND_1ST_HIT) gBattleStruct->metronomeItemCounter[gBattlerAttacker]++; @@ -7073,7 +7067,6 @@ static void Cmd_moveend(void) gProtectStructs[gBattlerAttacker].shellTrap = FALSE; gBattleStruct->battlerState[gBattlerAttacker].ateBoost = FALSE; gBattleScripting.moveEffect = MOVE_EFFECT_NONE; - gBattleStruct->moldBreakerActive = FALSE; gBattleStruct->swapDamageCategory = FALSE; gBattleStruct->categoryOverride = FALSE; gBattleStruct->additionalEffectsCounter = 0; @@ -7132,7 +7125,7 @@ static void Cmd_moveend(void) } } - if (!(gBattleStruct->moveResultFlags[gBattlerTarget] & (MOVE_RESULT_FAILED | MOVE_RESULT_DOESNT_AFFECT_FOE) + if (!(!IsAnyTargetAffected() || (gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE && !hasDancerTriggered) || (!gSpecialStatuses[gBattlerAttacker].dancerUsedMove && gBattleStruct->bouncedMoveIsUsed))) { // Dance move succeeds @@ -10457,7 +10450,6 @@ static u32 ChangeStatBuffs(u32 battler, s8 statValue, enum Stat statId, union St { if (!flags.allowPtr) return STAT_CHANGE_DIDNT_WORK; - gBattleStruct->moveResultFlags[gBattlerTarget] |= MOVE_RESULT_MISSED; return STAT_CHANGE_WORKED; } @@ -15737,9 +15729,9 @@ void BS_TryTarShot(void) void BS_CanTarShotWork(void) { NATIVE_ARGS(const u8 *failInstr); - // Tar Shot will fail if it's already been used on the target or if its speed can't be lowered further - if (!gDisableStructs[gBattlerTarget].tarShot - && CompareStat(gBattlerTarget, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN, GetBattlerAbility(gBattlerTarget))) + // Tar Shot fails if the target can't be made weaker to fire and it's speed can't be lowered further + if (!(gDisableStructs[gBattlerTarget].tarShot || GetActiveGimmick(gBattlerTarget) == GIMMICK_TERA) + || CompareStat(gBattlerTarget, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN, GetBattlerAbility(gBattlerTarget))) gBattlescriptCurrInstr = cmd->nextInstr; else gBattlescriptCurrInstr = cmd->failInstr; @@ -15769,6 +15761,7 @@ void BS_SetMagicCoatTarget(void) gBattlerAttacker = gBattlerTarget; gBattlerTarget = gBattleStruct->attackerBeforeBounce; HandleMoveTargetRedirection(); + ClearDamageCalcResults(); gBattlescriptCurrInstr = cmd->nextInstr; } diff --git a/src/battle_util.c b/src/battle_util.c index b5b10ebcd7..33328d8532 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -461,11 +461,6 @@ void HandleAction_UseMove(void) return; } - gBattleStruct->eventState.atkCanceler = 0; - ClearDamageCalcResults(); - gMultiHitCounter = 0; - gBattleScripting.savedDmg = 0; - gBattleCommunication[MISS_TYPE] = 0; gCurrMovePos = gChosenMovePos = gBattleStruct->chosenMovePositions[gBattlerAttacker]; // choose move @@ -533,8 +528,10 @@ void HandleAction_UseMove(void) gCurrentMove = gChosenMove = GetMaxMove(gBattlerAttacker, gCurrentMove); } - if (IsMoldBreakerTypeAbility(gBattlerAttacker, GetBattlerAbility(gBattlerAttacker)) || MoveIgnoresTargetAbility(gCurrentMove)) - gBattleStruct->moldBreakerActive = TRUE; + gBattleStruct->eventState.atkCanceler = 0; + ClearDamageCalcResults(); + gMultiHitCounter = 0; + gBattleCommunication[MISS_TYPE] = 0; moveTarget = GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove); @@ -960,14 +957,13 @@ void HandleAction_ActionFinished(void) memset(&gSpecialStatuses, 0, sizeof(gSpecialStatuses)); gHitMarker &= ~(HITMARKER_OBEYS); - ClearDamageCalcResults(); gCurrentMove = MOVE_NONE; + ClearDamageCalcResults(); gBattleScripting.animTurn = 0; gBattleScripting.animTargetsHit = 0; gBattleStruct->dynamicMoveType = 0; gBattleStruct->bouncedMoveIsUsed = FALSE; gBattleStruct->snatchedMoveIsUsed = FALSE; - gBattleStruct->moldBreakerActive = FALSE; gBattleScripting.moveendState = 0; gBattleCommunication[3] = 0; gBattleCommunication[4] = 0; @@ -2589,6 +2585,7 @@ static enum MoveCanceler CancelerPPDeduction(struct BattleContext *ctx) // Possibly better to just move type setting and redirection to attackcanceler as a new case at this point SetTypeBeforeUsingMove(ctx->currentMove, ctx->battlerAtk); HandleMoveTargetRedirection(); + ClearDamageCalcResults(); gBattlescriptCurrInstr = GetMoveBattleScript(ctx->currentMove); return MOVE_STEP_BREAK; } @@ -5277,7 +5274,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab if (!IsAbilityAndRecord(gBattlerAttacker, GetBattlerAbility(gBattlerAttacker), ABILITY_MAGIC_GUARD)) SetPassiveDamageAmount(gBattlerAttacker, GetNonDynamaxMaxHP(gBattlerAttacker) / 4); - switch(gBattleMons[gBattlerTarget].species) + switch (gBattleMons[gBattlerTarget].species) { case SPECIES_CRAMORANT_GORGING: TryBattleFormChange(battler, FORM_CHANGE_HIT_BY_MOVE); @@ -10408,8 +10405,8 @@ void ClearDamageCalcResults(void) { gBattleStruct->moveDamage[battler] = 0; gBattleStruct->critChance[battler] = 0; - gBattleStruct->moveResultFlags[battler] = CAN_DAMAGE; - gBattleStruct->noResultString[battler] = 0; + gBattleStruct->moveResultFlags[battler] = 0; + gBattleStruct->noResultString[battler] = CAN_DAMAGE; gBattleStruct->missStringId[battler] = 0; gSpecialStatuses[battler].criticalHit = FALSE; } @@ -10419,6 +10416,11 @@ void ClearDamageCalcResults(void) gBattleStruct->calculatedSpreadMoveAccuracy = FALSE; gBattleStruct->printedStrongWindsWeakenedAttack = FALSE; gBattleStruct->numSpreadTargets = 0; + gBattleScripting.savedDmg = 0; + if (gCurrentMove != MOVE_NONE) + gBattleStruct->moldBreakerActive = IsMoldBreakerTypeAbility(gBattlerAttacker, GetBattlerAbility(gBattlerAttacker)) || MoveIgnoresTargetAbility(gCurrentMove); + else + gBattleStruct->moldBreakerActive = FALSE; } bool32 DoesDestinyBondFail(u32 battler) diff --git a/test/battle/ability/dancer.c b/test/battle/ability/dancer.c index b456d9d79a..037def9699 100644 --- a/test/battle/ability/dancer.c +++ b/test/battle/ability/dancer.c @@ -146,6 +146,27 @@ SINGLE_BATTLE_TEST("Dancer-called attacks have their type updated") } } +SINGLE_BATTLE_TEST("Dancer-called attacks do not trigger Life Orb if target is immune") +{ + GIVEN { + ASSUME(IsDanceMove(MOVE_REVELATION_DANCE)); + ASSUME(GetMoveEffect(MOVE_REVELATION_DANCE) == EFFECT_REVELATION_DANCE); + ASSUME(GetMoveEffect(MOVE_ROOST) == EFFECT_ROOST); + ASSUME(GetItemHoldEffect(ITEM_LIFE_ORB) == HOLD_EFFECT_LIFE_ORB); + ASSUME(GetSpeciesType(SPECIES_ORICORIO_POM_POM, 0) == TYPE_ELECTRIC || GetSpeciesType(SPECIES_ORICORIO_POM_POM, 1) == TYPE_ELECTRIC); + PLAYER(SPECIES_RAICHU) { Ability(ABILITY_LIGHTNING_ROD); } + OPPONENT(SPECIES_ORICORIO_POM_POM) { Ability(ABILITY_DANCER); Item(ITEM_LIFE_ORB); } + } WHEN { + TURN { MOVE(opponent, MOVE_ROOST); MOVE(player, MOVE_REVELATION_DANCE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_REVELATION_DANCE, player); + ABILITY_POPUP(opponent, ABILITY_DANCER); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_REVELATION_DANCE, opponent); + ABILITY_POPUP(player, ABILITY_LIGHTNING_ROD); + NOT HP_BAR(opponent); + } +} + DOUBLE_BATTLE_TEST("Dancer doesn't trigger on a snatched move") { GIVEN { diff --git a/test/battle/move_effect/sleep_talk.c b/test/battle/move_effect/sleep_talk.c index 2a2443c7ee..ace40150c5 100644 --- a/test/battle/move_effect/sleep_talk.c +++ b/test/battle/move_effect/sleep_talk.c @@ -147,6 +147,27 @@ DOUBLE_BATTLE_TEST("Sleep Talk calls move and that move may be redirected by Sto } } +SINGLE_BATTLE_TEST("Sleep Talk calls move and that move correctly ignores ability if it should") +{ + u32 species; + enum Ability ability; + + PARAMETRIZE { species = SPECIES_KINGLER; ability = ABILITY_SHELL_ARMOR; } + PARAMETRIZE { species = SPECIES_ARMALDO; ability = ABILITY_BATTLE_ARMOR; } + + GIVEN { + ASSUME(MoveIgnoresTargetAbility(MOVE_SUNSTEEL_STRIKE)); + PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_SLEEP); Moves(MOVE_SLEEP_TALK, MOVE_SUNSTEEL_STRIKE); } + OPPONENT(species) { Ability(ability); } + } WHEN { + TURN { MOVE(player, MOVE_SLEEP_TALK, criticalHit: TRUE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SLEEP_TALK, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SUNSTEEL_STRIKE, player); + MESSAGE("A critical hit!"); + } +} + SINGLE_BATTLE_TEST("Sleep Talk deducts power points from itself, not the called move") { ASSUME(GetMovePP(MOVE_SLEEP_TALK) == 10);