From 138a8f90c6d2463c0db7263579e22e64fecd1b0e Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Fri, 6 Mar 2026 16:15:14 +0100 Subject: [PATCH] Minor dancer clean up/consolidation (#9417) Co-authored-by: PhallenTree <168426989+PhallenTree@users.noreply.github.com> --- include/battle.h | 15 +++--- include/battle_util.h | 2 +- src/battle_move_resolution.c | 39 ++-------------- src/battle_script_commands.c | 2 +- src/battle_util.c | 90 +++++++++++++++++++++++------------- 5 files changed, 73 insertions(+), 75 deletions(-) diff --git a/include/battle.h b/include/battle.h index 65ee3ed0f4..54b367f5a0 100644 --- a/include/battle.h +++ b/include/battle.h @@ -101,7 +101,12 @@ struct ProtectStruct // Cleared at the start of HandleAction_ActionFinished struct SpecialStatus { - u8 changedStatsBattlerId; // Battler that was responsible for the latest stat change. Can be self. + u8 changedStatsBattlerId:3; // Battler that was responsible for the latest stat change. Can be self. + u8 neutralizingGasRemoved:1; + u8 berryReduced:1; + u8 mindBlownRecoil:1; + u8 padding:2; + // End of byte u8 statLowered:1; u8 abilityRedirected:1; u8 restoredBattlerSprite: 1; @@ -111,12 +116,6 @@ struct SpecialStatus u8 dancerUsedMove:1; u8 criticalHit:1; // End of byte - u8 instructedChosenTarget:3; - u8 neutralizingGasRemoved:1; - u8 berryReduced:1; - u8 mindBlownRecoil:1; - u8 padding2:2; - // End of byte u8 gemParam:7; u8 gemBoost:1; // End of byte @@ -124,7 +123,7 @@ struct SpecialStatus u8 multiHitOn:1; u8 distortedTypeMatchups:1; u8 teraShellAbilityDone:1; - u8 dancerOriginalTarget:3; + u8 backUpTarget:3; // End of byte }; diff --git a/include/battle_util.h b/include/battle_util.h index 347a942ecf..acf5c088f8 100644 --- a/include/battle_util.h +++ b/include/battle_util.h @@ -49,7 +49,7 @@ enum AbilityEffect ABILITYEFFECT_SYNCHRONIZE, ABILITYEFFECT_ATK_SYNCHRONIZE, ABILITYEFFECT_FORM_CHANGE_ON_HIT, - ABILITYEFFECT_MOVE_END_OTHER, + ABILITYEFFECT_DANCER, ABILITYEFFECT_MOVE_END_FOES_FAINTED, // Moxie-like abilities / Battle Bond / Magician // On Switch in diff --git a/src/battle_move_resolution.c b/src/battle_move_resolution.c index 8bdae7d919..c15e01ff99 100644 --- a/src/battle_move_resolution.c +++ b/src/battle_move_resolution.c @@ -1142,11 +1142,11 @@ static enum CancelerResult CancelerMoveFailure(struct BattleContext *ctx) battleScript = BattleScript_ButItFailed; break; case EFFECT_FIRST_TURN_ONLY: - if (!gBattleStruct->battlerState[ctx->battlerAtk].isFirstTurn || gSpecialStatuses[ctx->battlerAtk].instructedChosenTarget) + if (!gBattleStruct->battlerState[ctx->battlerAtk].isFirstTurn || gSpecialStatuses[ctx->battlerAtk].backUpTarget) battleScript = BattleScript_ButItFailed; break; case EFFECT_MAT_BLOCK: - if (!gBattleStruct->battlerState[ctx->battlerAtk].isFirstTurn || gSpecialStatuses[ctx->battlerAtk].instructedChosenTarget) + if (!gBattleStruct->battlerState[ctx->battlerAtk].isFirstTurn || gSpecialStatuses[ctx->battlerAtk].backUpTarget) battleScript = BattleScript_ButItFailed; break; case EFFECT_FOLLOW_ME: @@ -3689,10 +3689,8 @@ static enum MoveEndResult MoveEndClearBits(void) if (ShouldSetStompingTantrumTimer()) gBattleStruct->battlerState[gBattlerAttacker].stompingTantrumTimer = 2; - if (gSpecialStatuses[gBattlerAttacker].instructedChosenTarget) - gBattleStruct->moveTarget[gBattlerAttacker] = gSpecialStatuses[gBattlerAttacker].instructedChosenTarget & 0x3; - if (gSpecialStatuses[gBattlerAttacker].dancerOriginalTarget) - gBattleStruct->moveTarget[gBattlerAttacker] = gSpecialStatuses[gBattlerAttacker].dancerOriginalTarget & 0x3; + if (gSpecialStatuses[gBattlerAttacker].backUpTarget) + gBattleStruct->moveTarget[gBattlerAttacker] = gSpecialStatuses[gBattlerAttacker].backUpTarget - 1; // If the Pokémon needs to keep track of move usage for its evolutions, do it if (originallyUsedMove != MOVE_NONE) @@ -3758,35 +3756,8 @@ static enum MoveEndResult MoveEndClearBits(void) static enum MoveEndResult MoveEndDancer(void) { enum MoveEndResult result = MOVEEND_RESULT_CONTINUE; - bool32 anyDancerQueued = FALSE; - enum BattlerId nextDancer = 0; - for (enum BattlerId battler = 0; battler < gBattlersCount; battler++) - { - if (gBattleMons[battler].volatiles.activateDancer && !gSpecialStatuses[battler].dancerUsedMove) - { - if (!anyDancerQueued || (gBattleMons[battler].speed < gBattleMons[nextDancer].speed)) - nextDancer = battler; - anyDancerQueued = TRUE; - } - } - - if (!anyDancerQueued) - { - gBattleScripting.moveendState++; - return result; - } - - // Dance move succeeds - // Set target for other Dancer mons; set bit so that mon cannot activate Dancer off of its own move - if (!gSpecialStatuses[gBattlerAttacker].dancerUsedMove) - { - gBattleScripting.savedBattler = gBattlerTarget | 0x4; - gBattleScripting.savedBattler |= (gBattlerAttacker << 4); - gSpecialStatuses[gBattlerAttacker].dancerUsedMove = TRUE; - } - - if (AbilityBattleEffects(ABILITYEFFECT_MOVE_END_OTHER, nextDancer, ABILITY_DANCER, gCurrentMove, TRUE)) + if (AbilityBattleEffects(ABILITYEFFECT_DANCER, gBattlerAttacker, ABILITY_DANCER, gCurrentMove, TRUE)) result = MOVEEND_RESULT_RUN_SCRIPT; gBattleScripting.moveendState++; diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index ddd627ec82..fd3864e0b0 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -14175,7 +14175,7 @@ void BS_TryInstruct(void) } else { - gSpecialStatuses[gBattlerTarget].instructedChosenTarget = gBattleStruct->moveTarget[gBattlerTarget] | 0x4; + gSpecialStatuses[gBattlerTarget].backUpTarget = gBattleStruct->moveTarget[gBattlerTarget] + 1; gCalledMove = move; u32 moveIndex; bool32 foundMove = FALSE; diff --git a/src/battle_util.c b/src/battle_util.c index f542398c56..c46606bd52 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -3049,6 +3049,62 @@ static bool32 IsRestrictedAbility(enum BattlerId battler, enum Ability ability) || GetSpeciesAbility(gBattleMons[battler].species, 2) == ability; } +static bool32 TryDancer(void) +{ + bool32 anyDancerQueued = FALSE; + enum BattlerId dancerBattler = MAX_BATTLERS_COUNT; + + if (!IsDanceMove(gCurrentMove)) + return FALSE; + + for (enum BattlerId battler = 0; battler < gBattlersCount; battler++) + { + if (gBattleMons[battler].volatiles.activateDancer && !gSpecialStatuses[battler].dancerUsedMove) + { + if (!anyDancerQueued || (gBattleMons[battler].speed < gBattleMons[dancerBattler].speed)) + dancerBattler = battler; + anyDancerQueued = TRUE; + } + } + + if (!anyDancerQueued) + return FALSE; + + // Dance move succeeds + // Set target for other Dancer mons; set bit so that mon cannot activate Dancer off of its own move + if (!gSpecialStatuses[gBattlerAttacker].dancerUsedMove) + { + gBattleScripting.savedBattler = gBattlerTarget | 0x4; + gBattleScripting.savedBattler |= (gBattlerAttacker << 4); + gSpecialStatuses[gBattlerAttacker].dancerUsedMove = TRUE; + } + + if (IsBattlerAlive(dancerBattler) + && !gSpecialStatuses[dancerBattler].dancerUsedMove + && gBattlerAttacker != dancerBattler) + { + gSpecialStatuses[dancerBattler].dancerUsedMove = TRUE; + gSpecialStatuses[dancerBattler].backUpTarget = gBattleStruct->moveTarget[dancerBattler] + 1; + gBattleMons[dancerBattler].volatiles.activateDancer = FALSE; + gBattlerAttacker = gBattlerAbility = dancerBattler; + gCalledMove = gCurrentMove; + gLastUsedAbility = ABILITY_DANCER; + RecordAbilityBattle(gBattlerAttacker, ABILITY_DANCER); + + // Set the target to the original target of the mon that first used a Dance move + gBattlerTarget = gBattleScripting.savedBattler & 0x3; + + // Make sure that the target isn't an ally - if it is, target the original user + if (IsBattlerAlly(gBattlerTarget, gBattlerAttacker)) + gBattlerTarget = (gBattleScripting.savedBattler & 0xF0) >> 4; + + BattleScriptExecute(BattleScript_DancerActivates); + return TRUE; + } + + return FALSE; +} + u32 AbilityBattleEffects(enum AbilityEffect caseID, enum BattlerId battler, enum Ability ability, enum Move move, bool32 shouldAbilityTrigger) { u32 effect = 0; @@ -4424,36 +4480,8 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, enum BattlerId battler, enum break; } break; - case ABILITYEFFECT_MOVE_END_OTHER: // Abilities that activate on *another* battler's moveend: Dancer, Soul-Heart, Receiver, Symbiosis - switch (ability) - { - case ABILITY_DANCER: - if (IsBattlerAlive(battler) - && IsDanceMove(move) - && !gSpecialStatuses[battler].dancerUsedMove - && gBattlerAttacker != battler) - { - // Set bit and save Dancer mon's original target - gSpecialStatuses[battler].dancerUsedMove = TRUE; - gSpecialStatuses[battler].dancerOriginalTarget = gBattleStruct->moveTarget[battler] | 0x4; - gBattleMons[battler].volatiles.activateDancer = FALSE; - gBattlerAttacker = gBattlerAbility = battler; - gCalledMove = move; - - // Set the target to the original target of the mon that first used a Dance move - gBattlerTarget = gBattleScripting.savedBattler & 0x3; - - // Make sure that the target isn't an ally - if it is, target the original user - if (IsBattlerAlly(gBattlerTarget, gBattlerAttacker)) - gBattlerTarget = (gBattleScripting.savedBattler & 0xF0) >> 4; - BattleScriptExecute(BattleScript_DancerActivates); - effect++; - } - break; - default: - break; - } - break; + case ABILITYEFFECT_DANCER: + return TryDancer(); case ABILITYEFFECT_MOVE_END_FOES_FAINTED: switch (ability) { @@ -5027,7 +5055,7 @@ u32 IsAbilityPreventingEscape(enum BattlerId battler) { if (battler == battlerDef || IsBattlerAlly(battler, battlerDef)) continue; - + if (!IsBattlerAlive(battlerDef)) continue;