Skip to content

Commit

Permalink
Adds some Snatch interactions, fixes for Dragon Darts, Trace, Primal …
Browse files Browse the repository at this point in the history
…Reversion, Protosynthesis/Quark Drive (#5430)

* Fixes Electrified Dragon Darts sometimes targeting battlers with absorbing abilities (Volt Absorb, Motor Drive)

* Add Snatch interactions with Dancer, Swallow

* Trace fix + cleanup

* Simplify Quash

* Fixes multiple mons with Primal Reversion causing only one Primal Reversion, add tests

* Fix Booster Energy Ability Popup

* Accidentally removed healing from Swallow

* More Trace cleanup
  • Loading branch information
PhallenTree authored Sep 24, 2024
1 parent 5508658 commit e67d5a2
Show file tree
Hide file tree
Showing 10 changed files with 313 additions and 55 deletions.
12 changes: 4 additions & 8 deletions data/battle_scripts_1.s
Original file line number Diff line number Diff line change
Expand Up @@ -6961,12 +6961,12 @@ BattleScript_WishMegaEvolution::

BattleScript_PrimalReversion::
call BattleScript_PrimalReversionRet
end2
end3

BattleScript_PrimalReversionRestoreAttacker::
call BattleScript_PrimalReversionRet
copybyte gBattlerAttacker, sSAVED_BATTLER
end2
end3

BattleScript_PrimalReversionRet::
flushtextbox
Expand Down Expand Up @@ -7675,15 +7675,11 @@ BattleScript_EmergencyExitWildNoPopUp::

BattleScript_TraceActivates::
pause B_WAIT_TIME_SHORT
call BattleScript_AbilityPopUp
call BattleScript_AbilityPopUpScripting
printstring STRINGID_PKMNTRACED
waitmessage B_WAIT_TIME_LONG
settracedability BS_SCRIPTING
switchinabilities BS_SCRIPTING
return

BattleScript_TraceActivatesEnd3::
call BattleScript_TraceActivates
end3

BattleScript_ReceiverActivates::
Expand Down Expand Up @@ -10011,7 +10007,7 @@ BattleScript_BerserkGeneRet_End:

BattleScript_BoosterEnergyEnd2::
playanimation BS_SCRIPTING, B_ANIM_HELD_ITEM_EFFECT, sB_ANIM_ARG1
call BattleScript_AbilityPopUpTarget
call BattleScript_AbilityPopUpScripting
printstring STRINGID_BOOSTERENERGYACTIVATES
waitmessage B_WAIT_TIME_MED
printstring STRINGID_STATWASHEIGHTENED
Expand Down
3 changes: 2 additions & 1 deletion include/battle.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ struct ResourceFlags
#define RESOURCE_FLAG_ROOST 0x2
#define RESOURCE_FLAG_UNBURDEN 0x4
#define RESOURCE_FLAG_UNUSED 0x8
#define RESOURCE_FLAG_TRACED 0x10
#define RESOURCE_FLAG_UNUSED_2 0x10
#define RESOURCE_FLAG_EMERGENCY_EXIT 0x20
#define RESOURCE_FLAG_NEUTRALIZING_GAS 0x40
#define RESOURCE_FLAG_ICE_FACE 0x80
Expand Down Expand Up @@ -751,6 +751,7 @@ struct BattleStruct
u8 blunderPolicy:1; // should blunder policy activate
u8 swapDamageCategory:1; // Photon Geyser, Shell Side Arm, Light That Burns the Sky
u8 bouncedMoveIsUsed:1;
u8 snatchedMoveIsUsed:1;
u8 descriptionSubmenu:1; // For Move Description window in move selection screen
u8 ackBallUseBtn:1; // Used for the last used ball feature
u8 ballSwapped:1; // Used for the last used ball feature
Expand Down
1 change: 0 additions & 1 deletion include/battle_scripts.h
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,6 @@ extern const u8 BattleScript_ItemSteal[];
extern const u8 BattleScript_DrizzleActivates[];
extern const u8 BattleScript_SpeedBoostActivates[];
extern const u8 BattleScript_TraceActivates[];
extern const u8 BattleScript_TraceActivatesEnd3[];
extern const u8 BattleScript_RainDishActivates[];
extern const u8 BattleScript_SandstreamActivates[];
extern const u8 BattleScript_ShedSkinActivates[];
Expand Down
2 changes: 1 addition & 1 deletion include/battle_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ bool32 MoveHasAdditionalEffectSelf(u32 move, u32 moveEffect);
bool32 MoveHasAdditionalEffectSelfArg(u32 move, u32 moveEffect, u32 argument);
bool32 MoveHasChargeTurnAdditionalEffect(u32 move);
bool32 CanTargetPartner(u32 battlerAtk, u32 battlerDef);
bool32 TargetFullyImmuneToCurrMove(u32 BattlerAtk, u32 battlerDef);
bool32 TargetFullyImmuneToCurrMove(u32 battlerAtk, u32 battlerDef);

bool32 CanBeSlept(u32 battler, u32 ability);
bool32 CanBePoisoned(u32 battlerAtk, u32 battlerDef, u32 defAbility);
Expand Down
52 changes: 25 additions & 27 deletions src/battle_script_commands.c
Original file line number Diff line number Diff line change
Expand Up @@ -1401,6 +1401,7 @@ static void Cmd_attackcanceler(void)
if ((gProtectStructs[gBattlerByTurnOrder[i]].stealMove) && gMovesInfo[gCurrentMove].snatchAffected)
{
gProtectStructs[gBattlerByTurnOrder[i]].stealMove = FALSE;
gBattleStruct->snatchedMoveIsUsed = TRUE;
gBattleScripting.battler = gBattlerByTurnOrder[i];
BattleScriptPushCursor();
gBattlescriptCurrInstr = BattleScript_SnatchedMove;
Expand Down Expand Up @@ -6287,7 +6288,7 @@ static void Cmd_moveend(void)
gBattleScripting.moveendState++;
break;
case MOVEEND_DANCER: // Special case because it's so annoying
if (gMovesInfo[gCurrentMove].danceMove)
if (gMovesInfo[gCurrentMove].danceMove && !gBattleStruct->snatchedMoveIsUsed)
{
u32 battler, nextDancer = 0;
bool32 hasDancerTriggered = FALSE;
Expand Down Expand Up @@ -6431,6 +6432,7 @@ static void Cmd_moveend(void)
gBattleStruct->swapDamageCategory = FALSE;
gBattleStruct->categoryOverride = FALSE;
gBattleStruct->bouncedMoveIsUsed = FALSE;
gBattleStruct->snatchedMoveIsUsed = FALSE;
gBattleStruct->enduredDamage = 0;
gBattleStruct->additionalEffectsCounter = 0;
gBattleStruct->poisonPuppeteerConfusion = FALSE;
Expand Down Expand Up @@ -11537,7 +11539,7 @@ static void Cmd_stockpiletohpheal(void)

const u8 *failInstr = cmd->failInstr;

if (gDisableStructs[gBattlerAttacker].stockpileCounter == 0)
if (gDisableStructs[gBattlerAttacker].stockpileCounter == 0 && !gBattleStruct->snatchedMoveIsUsed)
{
gBattlescriptCurrInstr = failInstr;
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SWALLOW_FAILED;
Expand All @@ -11553,14 +11555,22 @@ static void Cmd_stockpiletohpheal(void)
}
else
{
gBattleMoveDamage = GetNonDynamaxMaxHP(gBattlerAttacker) / (1 << (3 - gDisableStructs[gBattlerAttacker].stockpileCounter));
if (gDisableStructs[gBattlerAttacker].stockpileCounter > 0)
{
gBattleMoveDamage = GetNonDynamaxMaxHP(gBattlerAttacker) / (1 << (3 - gDisableStructs[gBattlerAttacker].stockpileCounter));
gBattleScripting.animTurn = gDisableStructs[gBattlerAttacker].stockpileCounter;
gBattleStruct->moveEffect2 = MOVE_EFFECT_STOCKPILE_WORE_OFF;
}
else // Snatched move
{
gBattleMoveDamage = GetNonDynamaxMaxHP(gBattlerAttacker) / 4;
gBattleScripting.animTurn = 1;
}

if (gBattleMoveDamage == 0)
gBattleMoveDamage = 1;
gBattleMoveDamage *= -1;

gBattleScripting.animTurn = gDisableStructs[gBattlerAttacker].stockpileCounter;
gBattleStruct->moveEffect2 = MOVE_EFFECT_STOCKPILE_WORE_OFF;

gBattlescriptCurrInstr = cmd->nextInstr;
gBattlerTarget = gBattlerAttacker;
}
Expand Down Expand Up @@ -17122,30 +17132,18 @@ void BS_TryQuash(void)

// If the above condition is not true, it means we are faster than the foe, so we can set the quash bit
gProtectStructs[gBattlerTarget].quash = TRUE;

if (B_QUASH_TURN_ORDER < GEN_8)

// this implementation assumes turn order is correct when using Quash
i = GetBattlerTurnOrderNum(gBattlerTarget);
for (j = i + 1; j < gBattlersCount; j++)
{
// Gen 7- config makes target go last so that the order of quash targets is kept for the correct turn order
j = GetBattlerTurnOrderNum(gBattlerTarget);
for (i = j + 1; i < gBattlersCount; i++)
{
// Gen 8+ config alters Turn Order of the target according to speed, dynamic speed should handle the rest
if (B_QUASH_TURN_ORDER < GEN_8 || GetWhichBattlerFaster(gBattlerByTurnOrder[i], gBattlerByTurnOrder[j], FALSE) == -1)
SwapTurnOrder(i, j);
j++;
}
}
else
{
// Gen 8+ config only alters Turn Order of battlers affected by Quash, dynamic speed should handle the rest
for (i = gCurrentTurnActionNumber + 1; i < gBattlersCount - 1; i++)
{
for (j = i + 1; j < gBattlersCount; j++)
{
u32 battler1 = gBattlerByTurnOrder[i], battler2 = gBattlerByTurnOrder[j];
if ((gProtectStructs[battler1].quash || gProtectStructs[battler2].quash)
&& GetWhichBattlerFaster(battler1, battler2, FALSE) == -1)
SwapTurnOrder(i, j);
}
}
else
break;
i++;
}
gBattlescriptCurrInstr = cmd->nextInstr;
}
Expand Down
29 changes: 12 additions & 17 deletions src/battle_util.c
Original file line number Diff line number Diff line change
Expand Up @@ -4368,13 +4368,10 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32

if (gSpecialStatuses[battler].switchInAbilityDone)
break;
if (gBattleResources->flags->flags[battler] & RESOURCE_FLAG_TRACED)
break;

side = (BATTLE_OPPOSITE(GetBattlerPosition(battler))) & BIT_SIDE;
target1 = GetBattlerAtPosition(side);
target2 = GetBattlerAtPosition(side + BIT_FLANK);
gSpecialStatuses[battler].switchInAbilityDone = TRUE;
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
{
if (!gAbilitiesInfo[gBattleMons[target1].ability].cantBeTraced && gBattleMons[target1].hp != 0
Expand All @@ -4393,11 +4390,10 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32

if (effect != 0)
{
BattleScriptPushCursorAndCallback(BattleScript_TraceActivatesEnd3);
gBattleResources->flags->flags[battler] &= ~RESOURCE_FLAG_TRACED;
BattleScriptPushCursorAndCallback(BattleScript_TraceActivates);
gBattleStruct->tracedAbility[battler] = gLastUsedAbility = gBattleMons[chosenTarget].ability;
RecordAbilityBattle(chosenTarget, gLastUsedAbility); // Record the opposing battler has this ability
battler = gBattlerAbility = gBattleScripting.battler = battler;
gBattlerAbility = battler;

PREPARE_MON_NICK_WITH_PREFIX_BUFFER(gBattleTextBuff1, chosenTarget, gBattlerPartyIndexes[chosenTarget])
PREPARE_ABILITY_BUFFER(gBattleTextBuff2, gLastUsedAbility)
Expand Down Expand Up @@ -5214,7 +5210,6 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
break;
case ABILITY_GOOD_AS_GOLD:
if (IS_MOVE_STATUS(gCurrentMove)
&& !(moveTarget & MOVE_TARGET_USER)
&& !(moveTarget & MOVE_TARGET_OPPONENTS_FIELD)
&& !(moveTarget & MOVE_TARGET_ALL_BATTLERS))
effect = 3;
Expand Down Expand Up @@ -6388,14 +6383,14 @@ bool32 TryPrimalReversion(u32 battler)
{
if (gBattlerAttacker == battler)
{
BattleScriptExecute(BattleScript_PrimalReversion);
BattleScriptPushCursorAndCallback(BattleScript_PrimalReversion);
}
else
{
// edge case for scenarios like a switch-in after activated eject button
gBattleScripting.savedBattler = gBattlerAttacker;
gBattlerAttacker = battler;
BattleScriptExecute(BattleScript_PrimalReversionRestoreAttacker);
BattleScriptPushCursorAndCallback(BattleScript_PrimalReversionRestoreAttacker);
}
return TRUE;
}
Expand Down Expand Up @@ -8706,7 +8701,7 @@ u32 CountBattlerStatIncreases(u32 battler, bool32 countEvasionAcc)

u32 GetMoveTargetCount(u32 move, u32 battlerAtk, u32 battlerDef)
{
switch (GetBattlerMoveTargetType(gBattlerAttacker, move))
switch (GetBattlerMoveTargetType(battlerAtk, move))
{
case MOVE_TARGET_BOTH:
return !(gAbsentBattlerFlags & gBitTable[battlerDef])
Expand Down Expand Up @@ -11816,19 +11811,19 @@ bool32 CanTargetPartner(u32 battlerAtk, u32 battlerDef)
&& battlerDef != BATTLE_PARTNER(battlerAtk));
}

static inline bool32 DoesCurrentTargetHaveAbilityImmunity(void)
static inline bool32 DoesBattlerHaveAbilityImmunity(u32 battlerDef)
{
return (AbilityBattleEffects(ABILITYEFFECT_WOULD_BLOCK, gBattlerTarget, 0, 0, 0)
|| AbilityBattleEffects(ABILITYEFFECT_WOULD_ABSORB, gBattlerTarget, 0, 0, 0));
return (AbilityBattleEffects(ABILITYEFFECT_WOULD_BLOCK, battlerDef, 0, 0, 0)
|| AbilityBattleEffects(ABILITYEFFECT_WOULD_ABSORB, battlerDef, 0, 0, 0));
}

bool32 TargetFullyImmuneToCurrMove(u32 BattlerAtk, u32 battlerDef)
bool32 TargetFullyImmuneToCurrMove(u32 battlerAtk, u32 battlerDef)
{
u32 moveType = 0;
GET_MOVE_TYPE(gCurrentMove, moveType);

return ((CalcTypeEffectivenessMultiplier(gCurrentMove, moveType, BattlerAtk, battlerDef, GetBattlerAbility(battlerDef), FALSE) == UQ_4_12(0.0))
|| IsBattlerProtected(BattlerAtk, battlerDef, gCurrentMove)
return ((CalcTypeEffectivenessMultiplier(gCurrentMove, moveType, battlerAtk, battlerDef, GetBattlerAbility(battlerDef), FALSE) == UQ_4_12(0.0))
|| IsBattlerProtected(battlerAtk, battlerDef, gCurrentMove)
|| IsSemiInvulnerable(battlerDef, gCurrentMove)
|| DoesCurrentTargetHaveAbilityImmunity());
|| DoesBattlerHaveAbilityImmunity(battlerDef));
}
79 changes: 79 additions & 0 deletions test/battle/ability/dancer.c
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,85 @@ SINGLE_BATTLE_TEST("Dancer-called attacks have their type updated")
}
}

DOUBLE_BATTLE_TEST("Dancer doesn't trigger on a snatched move")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_DRAGON_DANCE].danceMove == TRUE);
ASSUME(gMovesInfo[MOVE_SNATCH].effect == EFFECT_SNATCH);
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_ORICORIO);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponentRight, MOVE_SNATCH); MOVE(playerRight, MOVE_DRAGON_DANCE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SNATCH, opponentRight);
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_DANCE, playerRight);
ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_DANCE, opponentRight);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentRight);
NONE_OF {
ABILITY_POPUP(opponentLeft, ABILITY_DANCER);
ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_DANCE, opponentLeft);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft);
}
}
}

DOUBLE_BATTLE_TEST("Dancer triggers on Instructed dance moves")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_DRAGON_DANCE].danceMove == TRUE);
ASSUME(gMovesInfo[MOVE_DRAGON_DANCE].instructBanned == FALSE);
ASSUME(gMovesInfo[MOVE_INSTRUCT].effect == EFFECT_INSTRUCT);
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_ORICORIO);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(playerRight, MOVE_DRAGON_DANCE); MOVE(playerLeft, MOVE_INSTRUCT, target: playerRight); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_DANCE, playerRight);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight);
ABILITY_POPUP(opponentLeft, ABILITY_DANCER);
ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_DANCE, opponentLeft);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft);
ANIMATION(ANIM_TYPE_MOVE, MOVE_INSTRUCT, playerLeft);
ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_DANCE, playerRight);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight);
ABILITY_POPUP(opponentLeft, ABILITY_DANCER);
ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_DANCE, opponentLeft);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft);
}
}

DOUBLE_BATTLE_TEST("Dancer-called move doesn't update move to be Instructed")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_DRAGON_DANCE].danceMove == TRUE);
ASSUME(gMovesInfo[MOVE_TACKLE].instructBanned == FALSE);
ASSUME(gMovesInfo[MOVE_INSTRUCT].effect == EFFECT_INSTRUCT);
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_ORICORIO);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponentLeft, MOVE_TACKLE, target: playerLeft); MOVE(playerRight, MOVE_DRAGON_DANCE); MOVE(opponentRight, MOVE_INSTRUCT, target: opponentLeft); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponentLeft);
ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_DANCE, playerRight);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight);
ABILITY_POPUP(opponentLeft, ABILITY_DANCER);
ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_DANCE, opponentLeft);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft);
ANIMATION(ANIM_TYPE_MOVE, MOVE_INSTRUCT, opponentRight);
NONE_OF {
ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_DANCE, opponentLeft);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft);
}
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponentLeft);
}
}

DOUBLE_BATTLE_TEST("Dancer doesn't call a move that didn't execute due to Powder")
{
GIVEN {
Expand Down
15 changes: 15 additions & 0 deletions test/battle/ability/trace.c
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,21 @@ SINGLE_BATTLE_TEST("Trace will copy an opponent's ability whenever it has the ch
}
}


SINGLE_BATTLE_TEST("Trace copies opponent's Intimidate and triggers it immediately")
{
GIVEN {
PLAYER(SPECIES_RALTS) { Ability(ABILITY_TRACE); }
OPPONENT(SPECIES_MASQUERAIN) { Ability(ABILITY_INTIMIDATE); }
} WHEN {
TURN { }
} SCENE {
ABILITY_POPUP(player, ABILITY_TRACE);
ABILITY_POPUP(player, ABILITY_INTIMIDATE);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
}
}

DOUBLE_BATTLE_TEST("Trace respects the turn order")
{
GIVEN {
Expand Down
Loading

0 comments on commit e67d5a2

Please sign in to comment.