mirror of
https://github.com/pret/pokeemerald.git
synced 2026-06-02 22:05:46 -05:00
Fixes Called moves ignoring redirection (#6267)
This commit is contained in:
parent
53727aa23d
commit
b1c597495b
|
|
@ -1744,8 +1744,6 @@ BattleScript_EffectCopycat::
|
|||
trycopycat BattleScript_CopycatFail
|
||||
attackanimation
|
||||
waitanimation
|
||||
setbyte sB_ANIM_TURN, 0
|
||||
setbyte sB_ANIM_TARGETS_HIT, 0
|
||||
jumptocalledmove TRUE
|
||||
BattleScript_CopycatFail:
|
||||
ppreduce
|
||||
|
|
@ -1763,8 +1761,6 @@ BattleScript_EffectInstruct::
|
|||
copybyte gBattlerTarget, gEffectBattler
|
||||
printstring STRINGID_USEDINSTRUCTEDMOVE
|
||||
waitmessage B_WAIT_TIME_LONG
|
||||
setbyte sB_ANIM_TURN, 0
|
||||
setbyte sB_ANIM_TARGETS_HIT, 0
|
||||
jumptocalledmove TRUE
|
||||
|
||||
BattleScript_EffectAutotomize::
|
||||
|
|
@ -2188,8 +2184,6 @@ BattleScript_EffectMeFirst::
|
|||
trymefirst BattleScript_FailedFromPpReduce
|
||||
attackanimation
|
||||
waitanimation
|
||||
setbyte sB_ANIM_TURN, 0
|
||||
setbyte sB_ANIM_TARGETS_HIT, 0
|
||||
jumptocalledmove TRUE
|
||||
|
||||
BattleScript_EffectAttackSpAttackUp::
|
||||
|
|
@ -3842,8 +3836,6 @@ BattleScript_EffectMetronome::
|
|||
pause B_WAIT_TIME_SHORT
|
||||
attackanimation
|
||||
waitanimation
|
||||
setbyte sB_ANIM_TURN, 0
|
||||
setbyte sB_ANIM_TARGETS_HIT, 0
|
||||
metronome
|
||||
|
||||
BattleScript_EffectLeechSeed::
|
||||
|
|
@ -4038,8 +4030,6 @@ BattleScript_SleepTalkIsAsleep::
|
|||
BattleScript_SleepTalkUsingMove::
|
||||
attackanimation
|
||||
waitanimation
|
||||
setbyte sB_ANIM_TURN, 0
|
||||
setbyte sB_ANIM_TARGETS_HIT, 0
|
||||
jumptocalledmove TRUE
|
||||
|
||||
BattleScript_EffectDestinyBond::
|
||||
|
|
@ -5055,8 +5045,6 @@ BattleScript_EffectAssist::
|
|||
assistattackselect BattleScript_FailedFromPpReduce
|
||||
attackanimation
|
||||
waitanimation
|
||||
setbyte sB_ANIM_TURN, 0
|
||||
setbyte sB_ANIM_TARGETS_HIT, 0
|
||||
jumptocalledmove TRUE
|
||||
|
||||
BattleScript_EffectIngrain::
|
||||
|
|
@ -8709,8 +8697,6 @@ BattleScript_BattleBondActivatesOnMoveEndAttacker::
|
|||
BattleScript_DancerActivates::
|
||||
call BattleScript_AbilityPopUp
|
||||
waitmessage B_WAIT_TIME_SHORT
|
||||
setbyte sB_ANIM_TURN, 0
|
||||
setbyte sB_ANIM_TARGETS_HIT, 0
|
||||
orword gHitMarker, HITMARKER_ALLOW_NO_PP
|
||||
jumptocalledmove TRUE
|
||||
|
||||
|
|
|
|||
|
|
@ -166,6 +166,7 @@ STATIC_ASSERT(sizeof(struct DamageCalculationData) <= 4, StructExceedsFourBytes)
|
|||
|
||||
void HandleAction_ThrowBall(void);
|
||||
bool32 IsAffectedByFollowMe(u32 battlerAtk, u32 defSide, u32 move);
|
||||
bool32 HandleMoveTargetRedirection(void);
|
||||
void HandleAction_UseMove(void);
|
||||
void HandleAction_Switch(void);
|
||||
void HandleAction_UseItem(void);
|
||||
|
|
|
|||
|
|
@ -336,6 +336,7 @@ static bool8 CanBurnHitThaw(u16 move);
|
|||
static u32 GetNextTarget(u32 moveTarget, bool32 excludeCurrent);
|
||||
static void TryUpdateEvolutionTracker(u32 evolutionMethod, u32 upAmount, u16 usedMove);
|
||||
static void AccuracyCheck(bool32 recalcDragonDarts, const u8 *nextInstr, const u8 *failInstr, u16 move);
|
||||
static void ResetValuesForCalledMove(void);
|
||||
|
||||
static void Cmd_attackcanceler(void);
|
||||
static void Cmd_accuracycheck(void);
|
||||
|
|
@ -8133,6 +8134,18 @@ static void Cmd_hidepartystatussummary(void)
|
|||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||||
}
|
||||
|
||||
static void ResetValuesForCalledMove(void)
|
||||
{
|
||||
if (gBattlerByTurnOrder[gCurrentTurnActionNumber] != gBattlerAttacker)
|
||||
gBattleStruct->atkCancellerTracker = 0;
|
||||
else
|
||||
SetAtkCancellerForCalledMove();
|
||||
gBattleScripting.animTurn = 0;
|
||||
gBattleScripting.animTargetsHit = 0;
|
||||
SetTypeBeforeUsingMove(gCurrentMove, gBattlerAttacker);
|
||||
HandleMoveTargetRedirection();
|
||||
}
|
||||
|
||||
static void Cmd_jumptocalledmove(void)
|
||||
{
|
||||
CMD_ARGS(bool8 notChosenMove);
|
||||
|
|
@ -8142,6 +8155,8 @@ static void Cmd_jumptocalledmove(void)
|
|||
else
|
||||
gChosenMove = gCurrentMove = gCalledMove;
|
||||
|
||||
ResetValuesForCalledMove();
|
||||
|
||||
gBattlescriptCurrInstr = GET_MOVE_BATTLESCRIPT(gCurrentMove);
|
||||
}
|
||||
|
||||
|
|
@ -10193,10 +10208,8 @@ static void Cmd_various(void)
|
|||
gBattlescriptCurrInstr = cmd->failInstr;
|
||||
else
|
||||
{
|
||||
SetTypeBeforeUsingMove(gCalledMove, gBattlerTarget);
|
||||
gEffectBattler = gBattleStruct->lastMoveTarget[gBattlerTarget];
|
||||
gHitMarker &= ~HITMARKER_ATTACKSTRING_PRINTED;
|
||||
gBattleStruct->atkCancellerTracker = 0;
|
||||
PREPARE_MON_NICK_WITH_PREFIX_BUFFER(gBattleTextBuff1, battler, gBattlerPartyIndexes[battler]);
|
||||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||||
}
|
||||
|
|
@ -11212,8 +11225,8 @@ static void SetMoveForMirrorMove(u32 move)
|
|||
gCurrentMove = move;
|
||||
}
|
||||
|
||||
SetAtkCancellerForCalledMove();
|
||||
gBattlerTarget = GetMoveTarget(gCurrentMove, NO_TARGET_OVERRIDE);
|
||||
ResetValuesForCalledMove();
|
||||
gBattlescriptCurrInstr = GET_MOVE_BATTLESCRIPT(gCurrentMove);
|
||||
}
|
||||
|
||||
|
|
@ -12765,10 +12778,10 @@ static void Cmd_metronome(void)
|
|||
#endif
|
||||
|
||||
gCurrentMove = RandomUniformExcept(RNG_METRONOME, 1, moveCount - 1, InvalidMetronomeMove);
|
||||
SetAtkCancellerForCalledMove();
|
||||
PrepareStringBattle(STRINGID_WAGGLINGAFINGER, gBattlerAttacker);
|
||||
gBattlescriptCurrInstr = GET_MOVE_BATTLESCRIPT(gCurrentMove);
|
||||
gBattlerTarget = GetMoveTarget(gCurrentMove, NO_TARGET_OVERRIDE);
|
||||
ResetValuesForCalledMove();
|
||||
}
|
||||
|
||||
static void Cmd_dmgtolevel(void)
|
||||
|
|
@ -17342,14 +17355,10 @@ void BS_JumpIfBlockedBySoundproof(void)
|
|||
void BS_SetMagicCoatTarget(void)
|
||||
{
|
||||
NATIVE_ARGS();
|
||||
u32 side;
|
||||
gBattleStruct->attackerBeforeBounce = gBattleScripting.battler = gBattlerAttacker;
|
||||
gBattlerAttacker = gBattlerTarget;
|
||||
side = BATTLE_OPPOSITE(GetBattlerSide(gBattlerAttacker));
|
||||
if (IsAffectedByFollowMe(gBattlerAttacker, side, gCurrentMove))
|
||||
gBattlerTarget = gSideTimers[side].followmeTarget;
|
||||
else
|
||||
gBattlerTarget = gBattleStruct->attackerBeforeBounce;
|
||||
gBattlerTarget = gBattleStruct->attackerBeforeBounce;
|
||||
HandleMoveTargetRedirection();
|
||||
|
||||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -124,11 +124,94 @@ bool32 IsAffectedByFollowMe(u32 battlerAtk, u32 defSide, u32 move)
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
bool32 HandleMoveTargetRedirection(void)
|
||||
{
|
||||
u32 redirectorOrderNum = MAX_BATTLERS_COUNT;
|
||||
u16 moveTarget = GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove);
|
||||
u32 moveType = GetMoveType(gCurrentMove);
|
||||
u32 side = BATTLE_OPPOSITE(GetBattlerSide(gBattlerAttacker));
|
||||
u32 ability = GetBattlerAbility(gBattleStruct->moveTarget[gBattlerAttacker]);
|
||||
|
||||
if (IsAffectedByFollowMe(gBattlerAttacker, side, gCurrentMove)
|
||||
&& moveTarget == MOVE_TARGET_SELECTED
|
||||
&& GetBattlerSide(gBattlerAttacker) != GetBattlerSide(gSideTimers[side].followmeTarget))
|
||||
{
|
||||
gBattleStruct->moveTarget[gBattlerAttacker] = gBattlerTarget = gSideTimers[side].followmeTarget; // follow me moxie fix
|
||||
return FALSE;
|
||||
}
|
||||
else if (IsDoubleBattle()
|
||||
&& gSideTimers[side].followmeTimer == 0
|
||||
&& (!IS_MOVE_STATUS(gCurrentMove) || (moveTarget != MOVE_TARGET_USER && moveTarget != MOVE_TARGET_ALL_BATTLERS))
|
||||
&& ((ability != ABILITY_LIGHTNING_ROD && moveType == TYPE_ELECTRIC)
|
||||
|| (ability != ABILITY_STORM_DRAIN && moveType == TYPE_WATER)))
|
||||
{
|
||||
// Find first battler that redirects the move (in turn order)
|
||||
u32 battler;
|
||||
for (battler = 0; battler < gBattlersCount; battler++)
|
||||
{
|
||||
ability = GetBattlerAbility(battler);
|
||||
if ((B_REDIRECT_ABILITY_ALLIES >= GEN_4 || !IsAlly(gBattlerAttacker, battler))
|
||||
&& battler != gBattlerAttacker
|
||||
&& gBattleStruct->moveTarget[gBattlerAttacker] != battler
|
||||
&& ((ability == ABILITY_LIGHTNING_ROD && moveType == TYPE_ELECTRIC)
|
||||
|| (ability == ABILITY_STORM_DRAIN && moveType == TYPE_WATER))
|
||||
&& GetBattlerTurnOrderNum(battler) < redirectorOrderNum
|
||||
&& gMovesInfo[gCurrentMove].effect != EFFECT_SNIPE_SHOT
|
||||
&& gMovesInfo[gCurrentMove].effect != EFFECT_PLEDGE
|
||||
&& GetBattlerAbility(gBattlerAttacker) != ABILITY_PROPELLER_TAIL
|
||||
&& GetBattlerAbility(gBattlerAttacker) != ABILITY_STALWART)
|
||||
{
|
||||
redirectorOrderNum = GetBattlerTurnOrderNum(battler);
|
||||
}
|
||||
}
|
||||
if (redirectorOrderNum == MAX_BATTLERS_COUNT)
|
||||
{
|
||||
if (moveTarget & MOVE_TARGET_RANDOM)
|
||||
{
|
||||
gBattlerTarget = SetRandomTarget(gBattlerAttacker);
|
||||
}
|
||||
else if (moveTarget & MOVE_TARGET_FOES_AND_ALLY)
|
||||
{
|
||||
for (gBattlerTarget = 0; gBattlerTarget < gBattlersCount; gBattlerTarget++)
|
||||
{
|
||||
if (gBattlerTarget == gBattlerAttacker)
|
||||
continue;
|
||||
if (IsBattlerAlive(gBattlerTarget))
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
gBattlerTarget = *(gBattleStruct->moveTarget + gBattlerAttacker);
|
||||
}
|
||||
|
||||
if (!IsBattlerAlive(gBattlerTarget) && GetBattlerSide(gBattlerAttacker) != GetBattlerSide(gBattlerTarget))
|
||||
{
|
||||
gBattlerTarget = GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(gBattlerTarget)));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
u16 battlerAbility;
|
||||
battler = gBattlerByTurnOrder[redirectorOrderNum];
|
||||
battlerAbility = GetBattlerAbility(battler);
|
||||
|
||||
RecordAbilityBattle(battler, gBattleMons[battler].ability);
|
||||
if (battlerAbility == ABILITY_LIGHTNING_ROD && gCurrentMove != MOVE_TEATIME)
|
||||
gSpecialStatuses[battler].lightningRodRedirected = TRUE;
|
||||
else if (battlerAbility == ABILITY_STORM_DRAIN)
|
||||
gSpecialStatuses[battler].stormDrainRedirected = TRUE;
|
||||
gBattlerTarget = battler;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Functions
|
||||
void HandleAction_UseMove(void)
|
||||
{
|
||||
u32 battler, i, side, moveType, ability, var = MAX_BATTLERS_COUNT;
|
||||
u16 moveTarget;
|
||||
u32 i;
|
||||
|
||||
gBattlerAttacker = gBattlerByTurnOrder[gCurrentTurnActionNumber];
|
||||
if (gBattleStruct->absentBattlerFlags & (1u << gBattlerAttacker)
|
||||
|
|
@ -199,7 +282,6 @@ void HandleAction_UseMove(void)
|
|||
|
||||
// Set dynamic move type.
|
||||
SetTypeBeforeUsingMove(gChosenMove, gBattlerAttacker);
|
||||
moveType = GetMoveType(gCurrentMove);
|
||||
|
||||
// check Z-Move used
|
||||
if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_Z_MOVE && !IS_MOVE_STATUS(gCurrentMove) && !IsZMove(gCurrentMove))
|
||||
|
|
@ -214,115 +296,44 @@ void HandleAction_UseMove(void)
|
|||
gCurrentMove = gChosenMove = GetMaxMove(gBattlerAttacker, gCurrentMove);
|
||||
}
|
||||
|
||||
moveTarget = GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove);
|
||||
|
||||
// choose target
|
||||
side = BATTLE_OPPOSITE(GetBattlerSide(gBattlerAttacker));
|
||||
ability = GetBattlerAbility(gBattleStruct->moveTarget[gBattlerAttacker]);
|
||||
if (IsAffectedByFollowMe(gBattlerAttacker, side, gCurrentMove)
|
||||
&& moveTarget == MOVE_TARGET_SELECTED
|
||||
&& GetBattlerSide(gBattlerAttacker) != GetBattlerSide(gSideTimers[side].followmeTarget))
|
||||
if (!HandleMoveTargetRedirection())
|
||||
{
|
||||
gBattleStruct->moveTarget[gBattlerAttacker] = gBattlerTarget = gSideTimers[side].followmeTarget; // follow me moxie fix
|
||||
}
|
||||
else if (IsDoubleBattle()
|
||||
&& gSideTimers[side].followmeTimer == 0
|
||||
&& (!IS_MOVE_STATUS(gCurrentMove) || (moveTarget != MOVE_TARGET_USER && moveTarget != MOVE_TARGET_ALL_BATTLERS))
|
||||
&& ((ability != ABILITY_LIGHTNING_ROD && moveType == TYPE_ELECTRIC)
|
||||
|| (ability != ABILITY_STORM_DRAIN && moveType == TYPE_WATER)))
|
||||
{
|
||||
// Find first battler that redirects the move (in turn order)
|
||||
for (battler = 0; battler < gBattlersCount; battler++)
|
||||
u32 moveTarget = GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove);
|
||||
if (IsDoubleBattle() && moveTarget & MOVE_TARGET_RANDOM)
|
||||
{
|
||||
ability = GetBattlerAbility(battler);
|
||||
if ((B_REDIRECT_ABILITY_ALLIES >= GEN_4 || !IsAlly(gBattlerAttacker, battler))
|
||||
&& battler != gBattlerAttacker
|
||||
&& gBattleStruct->moveTarget[gBattlerAttacker] != battler
|
||||
&& ((ability == ABILITY_LIGHTNING_ROD && moveType == TYPE_ELECTRIC)
|
||||
|| (ability == ABILITY_STORM_DRAIN && moveType == TYPE_WATER))
|
||||
&& GetBattlerTurnOrderNum(battler) < var
|
||||
&& gMovesInfo[gCurrentMove].effect != EFFECT_SNIPE_SHOT
|
||||
&& gMovesInfo[gCurrentMove].effect != EFFECT_PLEDGE
|
||||
&& GetBattlerAbility(gBattlerAttacker) != ABILITY_PROPELLER_TAIL
|
||||
&& GetBattlerAbility(gBattlerAttacker) != ABILITY_STALWART)
|
||||
{
|
||||
var = GetBattlerTurnOrderNum(battler);
|
||||
}
|
||||
}
|
||||
if (var == MAX_BATTLERS_COUNT)
|
||||
{
|
||||
if (moveTarget & MOVE_TARGET_RANDOM)
|
||||
{
|
||||
gBattlerTarget = SetRandomTarget(gBattlerAttacker);
|
||||
}
|
||||
else if (moveTarget & MOVE_TARGET_FOES_AND_ALLY)
|
||||
{
|
||||
for (gBattlerTarget = 0; gBattlerTarget < gBattlersCount; gBattlerTarget++)
|
||||
{
|
||||
if (gBattlerTarget == gBattlerAttacker)
|
||||
continue;
|
||||
if (IsBattlerAlive(gBattlerTarget))
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
gBattlerTarget = *(gBattleStruct->moveTarget + gBattlerAttacker);
|
||||
}
|
||||
|
||||
if (!IsBattlerAlive(gBattlerTarget) && GetBattlerSide(gBattlerAttacker) != GetBattlerSide(gBattlerTarget))
|
||||
gBattlerTarget = SetRandomTarget(gBattlerAttacker);
|
||||
if (gAbsentBattlerFlags & (1u << gBattlerTarget)
|
||||
&& GetBattlerSide(gBattlerAttacker) != GetBattlerSide(gBattlerTarget))
|
||||
{
|
||||
gBattlerTarget = GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(gBattlerTarget)));
|
||||
}
|
||||
}
|
||||
else if (moveTarget == MOVE_TARGET_ALLY)
|
||||
{
|
||||
if (IsBattlerAlive(BATTLE_PARTNER(gBattlerAttacker)))
|
||||
gBattlerTarget = BATTLE_PARTNER(gBattlerAttacker);
|
||||
else
|
||||
gBattlerTarget = gBattlerAttacker;
|
||||
}
|
||||
else if (IsDoubleBattle() && moveTarget == MOVE_TARGET_FOES_AND_ALLY)
|
||||
{
|
||||
for (gBattlerTarget = 0; gBattlerTarget < gBattlersCount; gBattlerTarget++)
|
||||
{
|
||||
if (gBattlerTarget == gBattlerAttacker)
|
||||
continue;
|
||||
if (IsBattlerAlive(gBattlerTarget))
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
u16 battlerAbility;
|
||||
battler = gBattlerByTurnOrder[var];
|
||||
battlerAbility = GetBattlerAbility(battler);
|
||||
|
||||
RecordAbilityBattle(battler, gBattleMons[battler].ability);
|
||||
if (battlerAbility == ABILITY_LIGHTNING_ROD && gCurrentMove != MOVE_TEATIME)
|
||||
gSpecialStatuses[battler].lightningRodRedirected = TRUE;
|
||||
else if (battlerAbility == ABILITY_STORM_DRAIN)
|
||||
gSpecialStatuses[battler].stormDrainRedirected = TRUE;
|
||||
gBattlerTarget = battler;
|
||||
}
|
||||
}
|
||||
else if (IsDoubleBattle() && moveTarget & MOVE_TARGET_RANDOM)
|
||||
{
|
||||
gBattlerTarget = SetRandomTarget(gBattlerAttacker);
|
||||
if (gAbsentBattlerFlags & (1u << gBattlerTarget)
|
||||
&& GetBattlerSide(gBattlerAttacker) != GetBattlerSide(gBattlerTarget))
|
||||
{
|
||||
gBattlerTarget = GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(gBattlerTarget)));
|
||||
}
|
||||
}
|
||||
else if (moveTarget == MOVE_TARGET_ALLY)
|
||||
{
|
||||
if (IsBattlerAlive(BATTLE_PARTNER(gBattlerAttacker)))
|
||||
gBattlerTarget = BATTLE_PARTNER(gBattlerAttacker);
|
||||
else
|
||||
gBattlerTarget = gBattlerAttacker;
|
||||
}
|
||||
else if (IsDoubleBattle() && moveTarget == MOVE_TARGET_FOES_AND_ALLY)
|
||||
{
|
||||
for (gBattlerTarget = 0; gBattlerTarget < gBattlersCount; gBattlerTarget++)
|
||||
{
|
||||
if (gBattlerTarget == gBattlerAttacker)
|
||||
continue;
|
||||
if (IsBattlerAlive(gBattlerTarget))
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
gBattlerTarget = *(gBattleStruct->moveTarget + gBattlerAttacker);
|
||||
if (!IsBattlerAlive(gBattlerTarget)
|
||||
&& moveTarget != MOVE_TARGET_OPPONENTS_FIELD
|
||||
&& (GetBattlerSide(gBattlerAttacker) != GetBattlerSide(gBattlerTarget)))
|
||||
{
|
||||
gBattlerTarget = GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(gBattlerTarget)));
|
||||
gBattlerTarget = *(gBattleStruct->moveTarget + gBattlerAttacker);
|
||||
if (!IsBattlerAlive(gBattlerTarget)
|
||||
&& moveTarget != MOVE_TARGET_OPPONENTS_FIELD
|
||||
&& (GetBattlerSide(gBattlerAttacker) != GetBattlerSide(gBattlerTarget)))
|
||||
{
|
||||
gBattlerTarget = GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(gBattlerTarget)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -3275,7 +3286,8 @@ u8 AtkCanceller_UnableToUseMove(u32 moveType)
|
|||
gBattleMons[gBattlerAttacker].status1 -= toSub;
|
||||
if (gBattleMons[gBattlerAttacker].status1 & STATUS1_SLEEP)
|
||||
{
|
||||
if (gChosenMove != MOVE_SNORE && gChosenMove != MOVE_SLEEP_TALK)
|
||||
u32 moveEffect = gMovesInfo[gChosenMove].effect;
|
||||
if (moveEffect != EFFECT_SNORE && moveEffect != EFFECT_SLEEP_TALK)
|
||||
{
|
||||
gBattlescriptCurrInstr = BattleScript_MoveUsedIsAsleep;
|
||||
gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE;
|
||||
|
|
@ -3591,7 +3603,7 @@ u8 AtkCanceller_UnableToUseMove(u32 moveType)
|
|||
gBattleStruct->atkCancellerTracker++;
|
||||
break;
|
||||
case CANCELLER_STANCE_CHANGE_2:
|
||||
if (B_STANCE_CHANGE_FAIL >= GEN_7 && TryFormChangeBeforeMove())
|
||||
if (B_STANCE_CHANGE_FAIL >= GEN_7 && !gBattleStruct->isAtkCancelerForCalledMove && TryFormChangeBeforeMove())
|
||||
effect = 1;
|
||||
gBattleStruct->atkCancellerTracker++;
|
||||
break;
|
||||
|
|
@ -6222,7 +6234,6 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
|
|||
// Set bit and save Dancer mon's original target
|
||||
gSpecialStatuses[battler].dancerUsedMove = TRUE;
|
||||
gSpecialStatuses[battler].dancerOriginalTarget = *(gBattleStruct->moveTarget + battler) | 0x4;
|
||||
gBattleStruct->atkCancellerTracker = 0;
|
||||
gBattlerAttacker = gBattlerAbility = battler;
|
||||
gCalledMove = gCurrentMove;
|
||||
|
||||
|
|
@ -6231,7 +6242,6 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
|
|||
|
||||
// Edge case for dance moves that hit multiply targets
|
||||
gHitMarker &= ~HITMARKER_NO_ATTACKSTRING;
|
||||
SetTypeBeforeUsingMove(gCalledMove, battler);
|
||||
|
||||
// Make sure that the target isn't an ally - if it is, target the original user
|
||||
if (GetBattlerSide(gBattlerTarget) == GetBattlerSide(gBattlerAttacker))
|
||||
|
|
|
|||
71
test/battle/move_effect/follow_me.c
Normal file
71
test/battle/move_effect/follow_me.c
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
#include "global.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
||||
ASSUME(gMovesInfo[MOVE_FOLLOW_ME].effect == EFFECT_FOLLOW_ME);
|
||||
ASSUME(gMovesInfo[MOVE_SPOTLIGHT].effect == EFFECT_FOLLOW_ME);
|
||||
}
|
||||
|
||||
DOUBLE_BATTLE_TEST("Follow Me redirects single target moves used by opponents to user")
|
||||
{
|
||||
struct BattlePokemon *moveUser = NULL;
|
||||
struct BattlePokemon *partner = NULL;
|
||||
PARAMETRIZE { moveUser = opponentLeft; partner = opponentRight; }
|
||||
PARAMETRIZE { moveUser = opponentRight; partner = opponentLeft; }
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
PLAYER(SPECIES_WYNAUT);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WYNAUT);
|
||||
} WHEN {
|
||||
TURN { MOVE(playerLeft, MOVE_TACKLE, target: moveUser);
|
||||
MOVE(playerRight, MOVE_TACKLE, target: partner);
|
||||
MOVE(moveUser, MOVE_FOLLOW_ME);
|
||||
MOVE(partner, MOVE_TACKLE, target: playerLeft); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_FOLLOW_ME, moveUser);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, playerLeft);
|
||||
HP_BAR(moveUser);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, playerRight);
|
||||
HP_BAR(moveUser);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, partner);
|
||||
HP_BAR(playerLeft);
|
||||
}
|
||||
}
|
||||
|
||||
DOUBLE_BATTLE_TEST("Spotlight redirects single target moves used by the opposing side to Spotlight's target")
|
||||
{
|
||||
struct BattlePokemon *moveTarget = NULL;
|
||||
PARAMETRIZE { moveTarget = playerRight; }
|
||||
PARAMETRIZE { moveTarget = opponentLeft; }
|
||||
PARAMETRIZE { moveTarget = opponentRight; }
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
PLAYER(SPECIES_WYNAUT);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WYNAUT);
|
||||
} WHEN {
|
||||
TURN { MOVE(playerLeft, MOVE_SPOTLIGHT, target: moveTarget);
|
||||
MOVE(playerRight, MOVE_TACKLE, target: opponentRight);
|
||||
MOVE(opponentLeft, MOVE_TACKLE, target: playerLeft);
|
||||
MOVE(opponentRight, MOVE_TACKLE, target: playerLeft); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SPOTLIGHT, playerLeft);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, playerRight);
|
||||
if (moveTarget != playerRight)
|
||||
HP_BAR(moveTarget);
|
||||
else
|
||||
HP_BAR(opponentRight);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponentLeft);
|
||||
if (moveTarget == playerRight)
|
||||
HP_BAR(moveTarget);
|
||||
else
|
||||
HP_BAR(playerLeft);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponentRight);
|
||||
if (moveTarget == playerRight)
|
||||
HP_BAR(moveTarget);
|
||||
else
|
||||
HP_BAR(playerLeft);
|
||||
}
|
||||
}
|
||||
|
|
@ -217,8 +217,11 @@ DOUBLE_BATTLE_TEST("Instruct-called moves keep their priority")
|
|||
}
|
||||
}
|
||||
|
||||
DOUBLE_BATTLE_TEST("Instructed move will be absorbed by Lightning Rod if it turns into an Electric Type move")
|
||||
DOUBLE_BATTLE_TEST("Instructed move will be redirected and absorbed by Lightning Rod if it turns into an Electric Type move")
|
||||
{
|
||||
struct BattlePokemon *moveTarget = NULL;
|
||||
PARAMETRIZE { moveTarget = opponentLeft; }
|
||||
PARAMETRIZE { moveTarget = opponentRight; }
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
PLAYER(SPECIES_WYNAUT);
|
||||
|
|
@ -226,7 +229,7 @@ DOUBLE_BATTLE_TEST("Instructed move will be absorbed by Lightning Rod if it turn
|
|||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN {
|
||||
MOVE(playerLeft, MOVE_TACKLE, target: opponentLeft);
|
||||
MOVE(playerLeft, MOVE_TACKLE, target: moveTarget);
|
||||
MOVE(opponentLeft, MOVE_PLASMA_FISTS, target: playerLeft);
|
||||
MOVE(playerRight, MOVE_INSTRUCT, target: playerLeft);
|
||||
MOVE(opponentRight, MOVE_CELEBRATE);
|
||||
|
|
@ -239,3 +242,65 @@ DOUBLE_BATTLE_TEST("Instructed move will be absorbed by Lightning Rod if it turn
|
|||
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, playerLeft);
|
||||
}
|
||||
}
|
||||
|
||||
DOUBLE_BATTLE_TEST("Instructed move will be redirected by Follow Me after instructed target loses Stalwart")
|
||||
{
|
||||
struct BattlePokemon *moveTarget = NULL;
|
||||
PARAMETRIZE { moveTarget = opponentLeft; }
|
||||
PARAMETRIZE { moveTarget = opponentRight; }
|
||||
GIVEN {
|
||||
ASSUME(gMovesInfo[MOVE_FOLLOW_ME].effect == EFFECT_FOLLOW_ME);
|
||||
ASSUME(gMovesInfo[MOVE_SKILL_SWAP].effect == EFFECT_SKILL_SWAP);
|
||||
PLAYER(SPECIES_DURALUDON) { Ability(ABILITY_STALWART); }
|
||||
PLAYER(SPECIES_DURALUDON) { Ability(ABILITY_STALWART); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WYNAUT);
|
||||
} WHEN {
|
||||
TURN {
|
||||
MOVE(playerLeft, MOVE_TACKLE, target: moveTarget);
|
||||
MOVE(opponentLeft, MOVE_FOLLOW_ME);
|
||||
MOVE(opponentRight, MOVE_SKILL_SWAP, target: playerLeft);
|
||||
MOVE(playerRight, MOVE_INSTRUCT, target: playerLeft);
|
||||
}
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_FOLLOW_ME, opponentLeft);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, playerLeft);
|
||||
HP_BAR(moveTarget);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SKILL_SWAP, opponentRight);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_INSTRUCT, playerRight);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, playerLeft);
|
||||
HP_BAR(opponentLeft);
|
||||
}
|
||||
}
|
||||
|
||||
DOUBLE_BATTLE_TEST("Instructed move will be redirected by Rage Powder after instructed target loses Grass typing")
|
||||
{
|
||||
struct BattlePokemon *moveTarget = NULL;
|
||||
PARAMETRIZE { moveTarget = opponentLeft; }
|
||||
PARAMETRIZE { moveTarget = opponentRight; }
|
||||
GIVEN {
|
||||
ASSUME(gMovesInfo[MOVE_RAGE_POWDER].effect == EFFECT_FOLLOW_ME);
|
||||
ASSUME(gMovesInfo[MOVE_RAGE_POWDER].powderMove == TRUE);
|
||||
ASSUME(gMovesInfo[MOVE_SOAK].effect == EFFECT_SOAK);
|
||||
PLAYER(SPECIES_TREECKO);
|
||||
PLAYER(SPECIES_SCEPTILE);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WYNAUT);
|
||||
} WHEN {
|
||||
TURN {
|
||||
MOVE(playerLeft, MOVE_TACKLE, target: moveTarget);
|
||||
MOVE(opponentLeft, MOVE_RAGE_POWDER);
|
||||
MOVE(opponentRight, MOVE_SOAK, target: playerLeft);
|
||||
MOVE(playerRight, MOVE_INSTRUCT, target: playerLeft);
|
||||
}
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_RAGE_POWDER, opponentLeft);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, playerLeft);
|
||||
HP_BAR(moveTarget);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SOAK, opponentRight);
|
||||
MESSAGE("Treecko transformed into the Water type!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_INSTRUCT, playerRight);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, playerLeft);
|
||||
HP_BAR(opponentLeft);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,7 +32,6 @@ SINGLE_BATTLE_TEST("Sleep Talk fails if not asleep")
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
SINGLE_BATTLE_TEST("Sleep Talk works if user has Comatose")
|
||||
{
|
||||
|
||||
|
|
@ -91,3 +90,59 @@ SINGLE_BATTLE_TEST("Sleep Talk can use moves while choiced into Sleep Talk")
|
|||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Sleep Talk fails if user is taunted")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(gMovesInfo[MOVE_TAUNT].effect == EFFECT_TAUNT);
|
||||
ASSUME(gMovesInfo[MOVE_SLEEP_TALK].category == DAMAGE_CATEGORY_STATUS);
|
||||
PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_SLEEP); Moves(MOVE_SLEEP_TALK, MOVE_TACKLE, MOVE_FLY, MOVE_DIG); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_TAUNT); MOVE(player, MOVE_SLEEP_TALK); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TAUNT, opponent);
|
||||
NONE_OF {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SLEEP_TALK, player);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DOUBLE_BATTLE_TEST("Sleep Talk calls move and that move may be redirected by Lightning Rod")
|
||||
{
|
||||
PASSES_RANDOMLY(1, 2, RNG_RANDOM_TARGET);
|
||||
GIVEN {
|
||||
ASSUME(gMovesInfo[MOVE_SPARK].type == TYPE_ELECTRIC);
|
||||
PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_SLEEP); Moves(MOVE_SLEEP_TALK, MOVE_SPARK, MOVE_FLY, MOVE_DIG); }
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_RAICHU) { Ability(ABILITY_LIGHTNING_ROD); }
|
||||
} WHEN {
|
||||
TURN { MOVE(playerLeft, MOVE_SLEEP_TALK); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SLEEP_TALK, playerLeft);
|
||||
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_SPARK, playerLeft);
|
||||
MESSAGE("The opposing Raichu's Lightning Rod took the attack!");
|
||||
ABILITY_POPUP(opponentRight, ABILITY_LIGHTNING_ROD);
|
||||
}
|
||||
}
|
||||
|
||||
DOUBLE_BATTLE_TEST("Sleep Talk calls move and that move may be redirected by Storm Drain")
|
||||
{
|
||||
PASSES_RANDOMLY(1, 2, RNG_RANDOM_TARGET);
|
||||
GIVEN {
|
||||
ASSUME(gMovesInfo[MOVE_WATER_GUN].type == TYPE_WATER);
|
||||
PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_SLEEP); Moves(MOVE_SLEEP_TALK, MOVE_WATER_GUN, MOVE_FLY, MOVE_DIG); }
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_GASTRODON) { Ability(ABILITY_STORM_DRAIN); }
|
||||
} WHEN {
|
||||
TURN { MOVE(playerLeft, MOVE_SLEEP_TALK); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SLEEP_TALK, playerLeft);
|
||||
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_WATER_GUN, playerLeft);
|
||||
MESSAGE("The opposing Gastrodon's Storm Drain took the attack!");
|
||||
ABILITY_POPUP(opponentRight, ABILITY_STORM_DRAIN);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
59
test/battle/move_effect/snore.c
Normal file
59
test/battle/move_effect/snore.c
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
#include "global.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
||||
ASSUME(gMovesInfo[MOVE_SNORE].effect == EFFECT_SNORE);
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Snore fails if not asleep")
|
||||
{
|
||||
u32 status;
|
||||
PARAMETRIZE { status = STATUS1_SLEEP; }
|
||||
PARAMETRIZE { status = STATUS1_NONE; }
|
||||
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { Status1(status); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_SNORE); }
|
||||
} SCENE {
|
||||
if (status == STATUS1_SLEEP) {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SNORE, player);
|
||||
NOT MESSAGE("But it failed!");
|
||||
}
|
||||
else {
|
||||
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_SNORE, player);
|
||||
MESSAGE("But it failed!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Snore works if user has Comatose")
|
||||
{
|
||||
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_KOMALA);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_SNORE); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SNORE, player);
|
||||
NOT MESSAGE("But it failed!");
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Snore fails if user is throat chopped")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(MoveHasAdditionalEffect(MOVE_THROAT_CHOP, MOVE_EFFECT_THROAT_CHOP));
|
||||
ASSUME(gMovesInfo[MOVE_SNORE].soundMove == TRUE);
|
||||
PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_SLEEP); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_THROAT_CHOP); MOVE(player, MOVE_SNORE); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_THROAT_CHOP, opponent);
|
||||
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_SNORE, player);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user