Fixes Called moves ignoring redirection (#6267)

This commit is contained in:
PhallenTree 2025-02-14 12:47:02 +00:00 committed by GitHub
parent 53727aa23d
commit b1c597495b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 391 additions and 135 deletions

View File

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

View File

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

View File

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

View File

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

View 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);
}
}

View File

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

View File

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

View 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);
}
}