Multiple bugfixes (mostly Dancer/called moves) (#8401)

This commit is contained in:
PhallenTree 2025-12-01 19:12:11 +00:00 committed by GitHub
parent 81192c9146
commit 1a6d03344a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 70 additions and 27 deletions

View File

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

View File

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

View File

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

View File

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

View File

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