Merge branch '_RHH/upcoming' into _RHH/pr/upcoming/lighting-expansion-v2

This commit is contained in:
Eduardo Quezada 2025-02-20 19:46:02 -03:00
commit 8c57172f2a
23 changed files with 623 additions and 192 deletions

View File

@ -640,7 +640,7 @@ BattleScript_AffectionBasedStatus_HealFreezeString:
printstring STRINGID_ATTACKERMELTEDTHEICE printstring STRINGID_ATTACKERMELTEDTHEICE
goto BattleScript_AffectionBasedStatusHeal_Continue goto BattleScript_AffectionBasedStatusHeal_Continue
BattleScript_AffectionBasedStatus_HealFrostbiteString: BattleScript_AffectionBasedStatus_HealFrostbiteString:
printstring STRINGID_ATTACKERMELTEDTHEICE printstring STRINGID_ATTACKERHEALEDITSFROSTBITE
BattleScript_AffectionBasedStatusHeal_Continue: BattleScript_AffectionBasedStatusHeal_Continue:
waitmessage B_WAIT_TIME_LONG waitmessage B_WAIT_TIME_LONG
clearstatus BS_ATTACKER clearstatus BS_ATTACKER
@ -1563,6 +1563,7 @@ BattleScript_RototillerMoveTargetEnd:
moveendto MOVEEND_NEXT_TARGET moveendto MOVEEND_NEXT_TARGET
addbyte gBattlerTarget, 1 addbyte gBattlerTarget, 1
jumpifbytenotequal gBattlerTarget, gBattlersCount, BattleScript_RototillerLoop jumpifbytenotequal gBattlerTarget, gBattlersCount, BattleScript_RototillerLoop
restoretarget
end end
BattleScript_RototillerCantRaiseMultipleStats: BattleScript_RototillerCantRaiseMultipleStats:
@ -1743,8 +1744,6 @@ BattleScript_EffectCopycat::
trycopycat BattleScript_CopycatFail trycopycat BattleScript_CopycatFail
attackanimation attackanimation
waitanimation waitanimation
setbyte sB_ANIM_TURN, 0
setbyte sB_ANIM_TARGETS_HIT, 0
jumptocalledmove TRUE jumptocalledmove TRUE
BattleScript_CopycatFail: BattleScript_CopycatFail:
ppreduce ppreduce
@ -1762,8 +1761,6 @@ BattleScript_EffectInstruct::
copybyte gBattlerTarget, gEffectBattler copybyte gBattlerTarget, gEffectBattler
printstring STRINGID_USEDINSTRUCTEDMOVE printstring STRINGID_USEDINSTRUCTEDMOVE
waitmessage B_WAIT_TIME_LONG waitmessage B_WAIT_TIME_LONG
setbyte sB_ANIM_TURN, 0
setbyte sB_ANIM_TARGETS_HIT, 0
jumptocalledmove TRUE jumptocalledmove TRUE
BattleScript_EffectAutotomize:: BattleScript_EffectAutotomize::
@ -2187,8 +2184,6 @@ BattleScript_EffectMeFirst::
trymefirst BattleScript_FailedFromPpReduce trymefirst BattleScript_FailedFromPpReduce
attackanimation attackanimation
waitanimation waitanimation
setbyte sB_ANIM_TURN, 0
setbyte sB_ANIM_TARGETS_HIT, 0
jumptocalledmove TRUE jumptocalledmove TRUE
BattleScript_EffectAttackSpAttackUp:: BattleScript_EffectAttackSpAttackUp::
@ -3657,12 +3652,12 @@ BattleScript_EffectParalyze::
jumpifmovehadnoeffect BattleScript_ButItFailed jumpifmovehadnoeffect BattleScript_ButItFailed
jumpifstatus BS_TARGET, STATUS1_PARALYSIS, BattleScript_AlreadyParalyzed jumpifstatus BS_TARGET, STATUS1_PARALYSIS, BattleScript_AlreadyParalyzed
jumpifelectricabilityaffected BS_TARGET, ABILITY_VOLT_ABSORB, BattleScript_VoltAbsorbHeal jumpifelectricabilityaffected BS_TARGET, ABILITY_VOLT_ABSORB, BattleScript_VoltAbsorbHeal
clearmoveresultflags MOVE_RESULT_SUPER_EFFECTIVE | MOVE_RESULT_NOT_VERY_EFFECTIVE
tryparalyzetype BS_ATTACKER, BS_TARGET, BattleScript_NotAffected tryparalyzetype BS_ATTACKER, BS_TARGET, BattleScript_NotAffected
jumpifstatus BS_TARGET, STATUS1_ANY, BattleScript_ButItFailed jumpifstatus BS_TARGET, STATUS1_ANY, BattleScript_ButItFailed
jumpifterrainaffected BS_TARGET, STATUS_FIELD_MISTY_TERRAIN, BattleScript_MistyTerrainPrevents jumpifterrainaffected BS_TARGET, STATUS_FIELD_MISTY_TERRAIN, BattleScript_MistyTerrainPrevents
accuracycheck BattleScript_ButItFailed, ACC_CURR_MOVE accuracycheck BattleScript_ButItFailed, ACC_CURR_MOVE
jumpifsafeguard BattleScript_SafeguardProtected jumpifsafeguard BattleScript_SafeguardProtected
clearmoveresultflags MOVE_RESULT_SUPER_EFFECTIVE | MOVE_RESULT_NOT_VERY_EFFECTIVE
attackanimation attackanimation
waitanimation waitanimation
seteffectprimary MOVE_EFFECT_PARALYSIS seteffectprimary MOVE_EFFECT_PARALYSIS
@ -3843,8 +3838,6 @@ BattleScript_EffectMetronome::
pause B_WAIT_TIME_SHORT pause B_WAIT_TIME_SHORT
attackanimation attackanimation
waitanimation waitanimation
setbyte sB_ANIM_TURN, 0
setbyte sB_ANIM_TARGETS_HIT, 0
metronome metronome
BattleScript_EffectLeechSeed:: BattleScript_EffectLeechSeed::
@ -4051,8 +4044,6 @@ BattleScript_SleepTalkIsAsleep::
BattleScript_SleepTalkUsingMove:: BattleScript_SleepTalkUsingMove::
attackanimation attackanimation
waitanimation waitanimation
setbyte sB_ANIM_TURN, 0
setbyte sB_ANIM_TARGETS_HIT, 0
jumptocalledmove TRUE jumptocalledmove TRUE
BattleScript_EffectDestinyBond:: BattleScript_EffectDestinyBond::
@ -4167,11 +4158,15 @@ BattleScript_CurseTrySpeed::
setbyte sB_ANIM_TURN, 1 setbyte sB_ANIM_TURN, 1
attackanimation attackanimation
waitanimation waitanimation
setbyte sSTAT_ANIM_PLAYED, FALSE
playstatchangeanimation BS_ATTACKER, BIT_SPEED, STAT_CHANGE_NEGATIVE
setstatchanger STAT_SPEED, 1, TRUE setstatchanger STAT_SPEED, 1, TRUE
statbuffchange MOVE_EFFECT_AFFECTS_USER | STAT_CHANGE_ALLOW_PTR, BattleScript_CurseTryAttack statbuffchange MOVE_EFFECT_AFFECTS_USER | STAT_CHANGE_ALLOW_PTR, BattleScript_CurseTryAttack
printfromtable gStatDownStringIds printfromtable gStatDownStringIds
waitmessage B_WAIT_TIME_LONG waitmessage B_WAIT_TIME_LONG
BattleScript_CurseTryAttack:: BattleScript_CurseTryAttack::
setbyte sSTAT_ANIM_PLAYED, FALSE
playstatchangeanimation BS_ATTACKER, BIT_ATK | BIT_DEF, 0
setstatchanger STAT_ATK, 1, FALSE setstatchanger STAT_ATK, 1, FALSE
statbuffchange MOVE_EFFECT_AFFECTS_USER | STAT_CHANGE_ALLOW_PTR, BattleScript_CurseTryDefense statbuffchange MOVE_EFFECT_AFFECTS_USER | STAT_CHANGE_ALLOW_PTR, BattleScript_CurseTryDefense
printfromtable gStatUpStringIds printfromtable gStatUpStringIds
@ -5070,8 +5065,6 @@ BattleScript_EffectAssist::
assistattackselect BattleScript_FailedFromPpReduce assistattackselect BattleScript_FailedFromPpReduce
attackanimation attackanimation
waitanimation waitanimation
setbyte sB_ANIM_TURN, 0
setbyte sB_ANIM_TARGETS_HIT, 0
jumptocalledmove TRUE jumptocalledmove TRUE
BattleScript_EffectIngrain:: BattleScript_EffectIngrain::
@ -5112,6 +5105,7 @@ BattleScript_EffectBrickBreak::
accuracycheck BattleScript_PrintMoveMissed, ACC_CURR_MOVE accuracycheck BattleScript_PrintMoveMissed, ACC_CURR_MOVE
attackstring attackstring
ppreduce ppreduce
typecalc
removelightscreenreflect removelightscreenreflect
critcalc critcalc
damagecalc damagecalc
@ -8651,8 +8645,6 @@ BattleScript_BattleBondActivatesOnMoveEndAttacker::
BattleScript_DancerActivates:: BattleScript_DancerActivates::
call BattleScript_AbilityPopUp call BattleScript_AbilityPopUp
waitmessage B_WAIT_TIME_SHORT waitmessage B_WAIT_TIME_SHORT
setbyte sB_ANIM_TURN, 0
setbyte sB_ANIM_TARGETS_HIT, 0
orword gHitMarker, HITMARKER_ALLOW_NO_PP orword gHitMarker, HITMARKER_ALLOW_NO_PP
jumptocalledmove TRUE jumptocalledmove TRUE

View File

@ -554,6 +554,10 @@ Causes the test to fail if a and b compare incorrectly, e.g.
EXPECT_EQ(results[0].damage, Q_4_12(1.5), results[1].damage); EXPECT_EQ(results[0].damage, Q_4_12(1.5), results[1].damage);
``` ```
### `FORCE_MOVE_ANIM`
`FORCE_MOVE_ANIM(TRUE)`
Forces the moves in the current test to do their animations in headless mode. Useful for debugging animations.
## Overworld Command Reference ## Overworld Command Reference
### `OVERWORLD_SCRIPT` ### `OVERWORLD_SCRIPT`

View File

@ -14,6 +14,6 @@ JASC-PAL
96 168 120 96 168 120
64 136 96 64 136 96
232 232 248 232 232 248
152 40 64 39 44 149
0 0 0 0 0 0
0 0 0 0 0 0

View File

@ -835,6 +835,7 @@ struct BattleStruct
u8 padding3:2; u8 padding3:2;
struct MessageStatus slideMessageStatus; struct MessageStatus slideMessageStatus;
u8 trainerSlideSpriteIds[MAX_BATTLERS_COUNT]; u8 trainerSlideSpriteIds[MAX_BATTLERS_COUNT];
u8 embodyAspectBoost[NUM_BATTLE_SIDES];
}; };
// The palaceFlags member of struct BattleStruct contains 1 flag per move to indicate which moves the AI should consider, // The palaceFlags member of struct BattleStruct contains 1 flag per move to indicate which moves the AI should consider,

View File

@ -172,6 +172,7 @@ enum SleepClauseBlock
void HandleAction_ThrowBall(void); void HandleAction_ThrowBall(void);
bool32 IsAffectedByFollowMe(u32 battlerAtk, u32 defSide, u32 move); bool32 IsAffectedByFollowMe(u32 battlerAtk, u32 defSide, u32 move);
bool32 HandleMoveTargetRedirection(void);
void HandleAction_UseMove(void); void HandleAction_UseMove(void);
void HandleAction_Switch(void); void HandleAction_Switch(void);
void HandleAction_UseItem(void); void HandleAction_UseItem(void);

View File

@ -533,6 +533,20 @@ static inline s32 SetFixedMoveBasePower(u32 battlerAtk, u32 move)
return fixedBasePower; return fixedBasePower;
} }
static inline void AI_StoreBattlerTypes(u32 battlerAtk, u32 *types)
{
types[0] = gBattleMons[battlerAtk].types[0];
types[1] = gBattleMons[battlerAtk].types[1];
types[2] = gBattleMons[battlerAtk].types[2];
}
static inline void AI_RestoreBattlerTypes(u32 battlerAtk, u32 *types)
{
gBattleMons[battlerAtk].types[0] = types[0];
gBattleMons[battlerAtk].types[1] = types[1];
gBattleMons[battlerAtk].types[2] = types[2];
}
static inline void CalcDynamicMoveDamage(struct DamageCalculationData *damageCalcData, s32 *expectedDamage, s32 *minimumDamage, u32 holdEffectAtk, u32 abilityAtk) static inline void CalcDynamicMoveDamage(struct DamageCalculationData *damageCalcData, s32 *expectedDamage, s32 *minimumDamage, u32 holdEffectAtk, u32 abilityAtk)
{ {
u32 move = damageCalcData->move; u32 move = damageCalcData->move;
@ -658,6 +672,9 @@ struct SimulatedDamage AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u
{ {
s32 critChanceIndex, fixedBasePower; s32 critChanceIndex, fixedBasePower;
u32 types[3];
AI_StoreBattlerTypes(battlerAtk, types);
ProteanTryChangeType(battlerAtk, aiData->abilities[battlerAtk], move, moveType); ProteanTryChangeType(battlerAtk, aiData->abilities[battlerAtk], move, moveType);
fixedBasePower = SetFixedMoveBasePower(battlerAtk, move); fixedBasePower = SetFixedMoveBasePower(battlerAtk, move);
@ -735,6 +752,8 @@ struct SimulatedDamage AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u
aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerAtk],
aiData->abilities[battlerAtk]); aiData->abilities[battlerAtk]);
} }
AI_RestoreBattlerTypes(battlerAtk, types);
} }
else else
{ {
@ -1068,8 +1087,15 @@ s32 AI_WhoStrikesFirst(u32 battlerAI, u32 battler, u32 moveConsidered)
u32 abilityAI = AI_DATA->abilities[battlerAI]; u32 abilityAI = AI_DATA->abilities[battlerAI];
u32 abilityPlayer = AI_DATA->abilities[battler]; u32 abilityPlayer = AI_DATA->abilities[battler];
if (GetBattleMovePriority(battlerAI, moveConsidered) > 0) u32 predictedMove = AI_DATA->lastUsedMove[battler]; // TODO update for move prediction
s8 aiPriority = GetBattleMovePriority(battlerAI, moveConsidered);
s8 playerPriority = GetBattleMovePriority(battler, predictedMove);
if (aiPriority > playerPriority)
return AI_IS_FASTER; return AI_IS_FASTER;
else if (aiPriority < playerPriority)
return AI_IS_SLOWER;
speedBattlerAI = GetBattlerTotalSpeedStatArgs(battlerAI, abilityAI, holdEffectAI); speedBattlerAI = GetBattlerTotalSpeedStatArgs(battlerAI, abilityAI, holdEffectAI);
speedBattler = GetBattlerTotalSpeedStatArgs(battler, abilityPlayer, holdEffectPlayer); speedBattler = GetBattlerTotalSpeedStatArgs(battler, abilityPlayer, holdEffectPlayer);

View File

@ -344,6 +344,7 @@ static bool8 CanBurnHitThaw(u16 move);
static u32 GetNextTarget(u32 moveTarget, bool32 excludeCurrent); static u32 GetNextTarget(u32 moveTarget, bool32 excludeCurrent);
static void TryUpdateEvolutionTracker(u32 evolutionMethod, u32 upAmount, u16 usedMove); static void TryUpdateEvolutionTracker(u32 evolutionMethod, u32 upAmount, u16 usedMove);
static void AccuracyCheck(bool32 recalcDragonDarts, const u8 *nextInstr, const u8 *failInstr, u16 move); 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_attackcanceler(void);
static void Cmd_accuracycheck(void); static void Cmd_accuracycheck(void);
@ -8746,6 +8747,18 @@ static void Cmd_hidepartystatussummary(void)
gBattlescriptCurrInstr = cmd->nextInstr; 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) static void Cmd_jumptocalledmove(void)
{ {
CMD_ARGS(bool8 notChosenMove); CMD_ARGS(bool8 notChosenMove);
@ -8755,6 +8768,8 @@ static void Cmd_jumptocalledmove(void)
else else
gChosenMove = gCurrentMove = gCalledMove; gChosenMove = gCurrentMove = gCalledMove;
ResetValuesForCalledMove();
gBattlescriptCurrInstr = GetMoveBattleScript(gCurrentMove); gBattlescriptCurrInstr = GetMoveBattleScript(gCurrentMove);
} }
@ -10811,10 +10826,8 @@ static void Cmd_various(void)
gBattlescriptCurrInstr = cmd->failInstr; gBattlescriptCurrInstr = cmd->failInstr;
else else
{ {
SetTypeBeforeUsingMove(gCalledMove, gBattlerTarget);
gEffectBattler = gBattleStruct->lastMoveTarget[gBattlerTarget]; gEffectBattler = gBattleStruct->lastMoveTarget[gBattlerTarget];
gHitMarker &= ~HITMARKER_ATTACKSTRING_PRINTED; gHitMarker &= ~HITMARKER_ATTACKSTRING_PRINTED;
gBattleStruct->atkCancellerTracker = 0;
PREPARE_MON_NICK_WITH_PREFIX_BUFFER(gBattleTextBuff1, battler, gBattlerPartyIndexes[battler]); PREPARE_MON_NICK_WITH_PREFIX_BUFFER(gBattleTextBuff1, battler, gBattlerPartyIndexes[battler]);
gBattlescriptCurrInstr = cmd->nextInstr; gBattlescriptCurrInstr = cmd->nextInstr;
} }
@ -11804,8 +11817,8 @@ static void SetMoveForMirrorMove(u32 move)
gCurrentMove = move; gCurrentMove = move;
} }
SetAtkCancellerForCalledMove();
gBattlerTarget = GetBattleMoveTarget(gCurrentMove, NO_TARGET_OVERRIDE); gBattlerTarget = GetBattleMoveTarget(gCurrentMove, NO_TARGET_OVERRIDE);
ResetValuesForCalledMove();
gBattlescriptCurrInstr = GetMoveBattleScript(gCurrentMove); gBattlescriptCurrInstr = GetMoveBattleScript(gCurrentMove);
} }
@ -13333,10 +13346,10 @@ static void Cmd_metronome(void)
#endif #endif
gCurrentMove = RandomUniformExcept(RNG_METRONOME, 1, moveCount - 1, InvalidMetronomeMove); gCurrentMove = RandomUniformExcept(RNG_METRONOME, 1, moveCount - 1, InvalidMetronomeMove);
SetAtkCancellerForCalledMove();
PrepareStringBattle(STRINGID_WAGGLINGAFINGER, gBattlerAttacker); PrepareStringBattle(STRINGID_WAGGLINGAFINGER, gBattlerAttacker);
gBattlescriptCurrInstr = GetMoveBattleScript(gCurrentMove); gBattlescriptCurrInstr = GetMoveBattleScript(gCurrentMove);
gBattlerTarget = GetBattleMoveTarget(gCurrentMove, NO_TARGET_OVERRIDE); gBattlerTarget = GetBattleMoveTarget(gCurrentMove, NO_TARGET_OVERRIDE);
ResetValuesForCalledMove();
} }
static void Cmd_dmgtolevel(void) static void Cmd_dmgtolevel(void)
@ -18046,14 +18059,10 @@ void BS_JumpIfBlockedBySoundproof(void)
void BS_SetMagicCoatTarget(void) void BS_SetMagicCoatTarget(void)
{ {
NATIVE_ARGS(); NATIVE_ARGS();
u32 side;
gBattleStruct->attackerBeforeBounce = gBattleScripting.battler = gBattlerAttacker; gBattleStruct->attackerBeforeBounce = gBattleScripting.battler = gBattlerAttacker;
gBattlerAttacker = gBattlerTarget; gBattlerAttacker = gBattlerTarget;
side = BATTLE_OPPOSITE(GetBattlerSide(gBattlerAttacker)); gBattlerTarget = gBattleStruct->attackerBeforeBounce;
if (IsAffectedByFollowMe(gBattlerAttacker, side, gCurrentMove)) HandleMoveTargetRedirection();
gBattlerTarget = gSideTimers[side].followmeTarget;
else
gBattlerTarget = gBattleStruct->attackerBeforeBounce;
gBattlescriptCurrInstr = cmd->nextInstr; gBattlescriptCurrInstr = cmd->nextInstr;
} }

View File

@ -234,11 +234,69 @@ bool32 IsAffectedByFollowMe(u32 battlerAtk, u32 defSide, u32 move)
return TRUE; return TRUE;
} }
bool32 HandleMoveTargetRedirection(void)
{
u32 redirectorOrderNum = MAX_BATTLERS_COUNT;
u16 moveTarget = GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove);
u32 moveType = GetBattleMoveType(gCurrentMove);
u32 moveEffect = GetMoveEffect(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 TRUE;
}
else if (IsDoubleBattle()
&& gSideTimers[side].followmeTimer == 0
&& (!IsBattleMoveStatus(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
&& moveEffect != EFFECT_SNIPE_SHOT
&& moveEffect != EFFECT_PLEDGE
&& GetBattlerAbility(gBattlerAttacker) != ABILITY_PROPELLER_TAIL
&& GetBattlerAbility(gBattlerAttacker) != ABILITY_STALWART)
{
redirectorOrderNum = GetBattlerTurnOrderNum(battler);
}
}
if (redirectorOrderNum != MAX_BATTLERS_COUNT)
{
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 // Functions
void HandleAction_UseMove(void) void HandleAction_UseMove(void)
{ {
u32 battler, i, side, moveType, ability, var = MAX_BATTLERS_COUNT; u32 i;
u16 moveTarget;
gBattlerAttacker = gBattlerByTurnOrder[gCurrentTurnActionNumber]; gBattlerAttacker = gBattlerByTurnOrder[gCurrentTurnActionNumber];
if (gBattleStruct->battlerState[gBattlerAttacker].absentBattlerFlags if (gBattleStruct->battlerState[gBattlerAttacker].absentBattlerFlags
@ -308,7 +366,6 @@ void HandleAction_UseMove(void)
// Set dynamic move type. // Set dynamic move type.
SetTypeBeforeUsingMove(gChosenMove, gBattlerAttacker); SetTypeBeforeUsingMove(gChosenMove, gBattlerAttacker);
moveType = GetBattleMoveType(gCurrentMove);
// check Z-Move used // check Z-Move used
if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_Z_MOVE && !IsBattleMoveStatus(gCurrentMove) && !IsZMove(gCurrentMove)) if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_Z_MOVE && !IsBattleMoveStatus(gCurrentMove) && !IsZMove(gCurrentMove))
@ -323,117 +380,44 @@ void HandleAction_UseMove(void)
gCurrentMove = gChosenMove = GetMaxMove(gBattlerAttacker, gCurrentMove); gCurrentMove = gChosenMove = GetMaxMove(gBattlerAttacker, gCurrentMove);
} }
moveTarget = GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove); if (!HandleMoveTargetRedirection())
u32 moveEffect = GetMoveEffect(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))
{ {
gBattleStruct->moveTarget[gBattlerAttacker] = gBattlerTarget = gSideTimers[side].followmeTarget; // follow me moxie fix u32 moveTarget = GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove);
} if (IsDoubleBattle() && moveTarget & MOVE_TARGET_RANDOM)
else if (IsDoubleBattle()
&& gSideTimers[side].followmeTimer == 0
&& !gBattleStruct->battlerState[gBattleStruct->moveTarget[gBattlerAttacker]].pursuitTarget
&& (!IsBattleMoveStatus(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++)
{ {
ability = GetBattlerAbility(battler); gBattlerTarget = SetRandomTarget(gBattlerAttacker);
if ((B_REDIRECT_ABILITY_ALLIES >= GEN_4 || !IsBattlerAlly(gBattlerAttacker, battler)) if (gAbsentBattlerFlags & (1u << gBattlerTarget)
&& battler != gBattlerAttacker && GetBattlerSide(gBattlerAttacker) != GetBattlerSide(gBattlerTarget))
&& gBattleStruct->moveTarget[gBattlerAttacker] != battler
&& ((ability == ABILITY_LIGHTNING_ROD && moveType == TYPE_ELECTRIC)
|| (ability == ABILITY_STORM_DRAIN && moveType == TYPE_WATER))
&& GetBattlerTurnOrderNum(battler) < var
&& moveEffect != EFFECT_SNIPE_SHOT
&& moveEffect != 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 = GetPartnerBattler(gBattlerTarget); gBattlerTarget = GetPartnerBattler(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 else
{ {
u16 battlerAbility; gBattlerTarget = *(gBattleStruct->moveTarget + gBattlerAttacker);
battler = gBattlerByTurnOrder[var]; if (!IsBattlerAlive(gBattlerTarget)
battlerAbility = GetBattlerAbility(battler); && moveTarget != MOVE_TARGET_OPPONENTS_FIELD
&& (GetBattlerSide(gBattlerAttacker) != GetBattlerSide(gBattlerTarget)))
RecordAbilityBattle(battler, gBattleMons[battler].ability); {
if (battlerAbility == ABILITY_LIGHTNING_ROD && gCurrentMove != MOVE_TEATIME) gBattlerTarget = GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(gBattlerTarget)));
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 = GetPartnerBattler(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 = GetPartnerBattler(gBattlerTarget);
} }
} }
@ -3215,6 +3199,8 @@ static void CancellerAsleep(u32 *effect)
gBattleMons[gBattlerAttacker].status1 -= toSub; gBattleMons[gBattlerAttacker].status1 -= toSub;
if (gBattleMons[gBattlerAttacker].status1 & STATUS1_SLEEP) if (gBattleMons[gBattlerAttacker].status1 & STATUS1_SLEEP)
{ {
u32 moveEffect = GetMoveEffect(gChosenMove);
if (moveEffect != EFFECT_SNORE && moveEffect != EFFECT_SLEEP_TALK)
if (gChosenMove != MOVE_SNORE && gChosenMove != MOVE_SLEEP_TALK) if (gChosenMove != MOVE_SNORE && gChosenMove != MOVE_SLEEP_TALK)
{ {
gBattlescriptCurrInstr = BattleScript_MoveUsedIsAsleep; gBattlescriptCurrInstr = BattleScript_MoveUsedIsAsleep;
@ -3551,7 +3537,7 @@ static void CancellerThaw(u32 *effect)
static void CancellerStanceChangeTwo(u32 *effect) static void CancellerStanceChangeTwo(u32 *effect)
{ {
if (B_STANCE_CHANGE_FAIL >= GEN_7 && TryFormChangeBeforeMove()) if (B_STANCE_CHANGE_FAIL >= GEN_7 && !gBattleStruct->isAtkCancelerForCalledMove && TryFormChangeBeforeMove())
*effect = 1; *effect = 1;
} }
@ -5306,7 +5292,8 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
case ABILITY_EMBODY_ASPECT_HEARTHFLAME_MASK: case ABILITY_EMBODY_ASPECT_HEARTHFLAME_MASK:
case ABILITY_EMBODY_ASPECT_WELLSPRING_MASK: case ABILITY_EMBODY_ASPECT_WELLSPRING_MASK:
case ABILITY_EMBODY_ASPECT_CORNERSTONE_MASK: case ABILITY_EMBODY_ASPECT_CORNERSTONE_MASK:
if (!gSpecialStatuses[battler].switchInAbilityDone) if (!gSpecialStatuses[battler].switchInAbilityDone
&& !(gBattleStruct->embodyAspectBoost[GetBattlerSide(battler)] & (1u << gBattlerPartyIndexes[battler])))
{ {
u32 stat; u32 stat;
@ -5325,6 +5312,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
gBattleScripting.savedBattler = gBattlerAttacker; gBattleScripting.savedBattler = gBattlerAttacker;
gBattlerAttacker = battler; gBattlerAttacker = battler;
gSpecialStatuses[battler].switchInAbilityDone = TRUE; gSpecialStatuses[battler].switchInAbilityDone = TRUE;
gBattleStruct->embodyAspectBoost[GetBattlerSide(battler)] |= 1u << gBattlerPartyIndexes[battler];
SET_STATCHANGER(stat, 1, FALSE); SET_STATCHANGER(stat, 1, FALSE);
BattleScriptPushCursorAndCallback(BattleScript_BattlerAbilityStatRaiseOnSwitchIn); BattleScriptPushCursorAndCallback(BattleScript_BattlerAbilityStatRaiseOnSwitchIn);
effect++; effect++;
@ -6297,7 +6285,6 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
// Set bit and save Dancer mon's original target // Set bit and save Dancer mon's original target
gSpecialStatuses[battler].dancerUsedMove = TRUE; gSpecialStatuses[battler].dancerUsedMove = TRUE;
gSpecialStatuses[battler].dancerOriginalTarget = gBattleStruct->moveTarget[battler] | 0x4; gSpecialStatuses[battler].dancerOriginalTarget = gBattleStruct->moveTarget[battler] | 0x4;
gBattleStruct->atkCancellerTracker = 0;
gBattlerAttacker = gBattlerAbility = battler; gBattlerAttacker = gBattlerAbility = battler;
gCalledMove = gCurrentMove; gCalledMove = gCurrentMove;
@ -6306,7 +6293,6 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
// Edge case for dance moves that hit multiply targets // Edge case for dance moves that hit multiply targets
gHitMarker &= ~HITMARKER_NO_ATTACKSTRING; gHitMarker &= ~HITMARKER_NO_ATTACKSTRING;
SetTypeBeforeUsingMove(gCalledMove, battler);
// Make sure that the target isn't an ally - if it is, target the original user // Make sure that the target isn't an ally - if it is, target the original user
if (IsBattlerAlly(gBattlerTarget, gBattlerAttacker)) if (IsBattlerAlly(gBattlerTarget, gBattlerAttacker))

View File

@ -105,7 +105,7 @@ static const u8 sFeintDescription[] = _(
"An attack that hits foes\n" "An attack that hits foes\n"
"using moves like Protect."); "using moves like Protect.");
const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] =
{ {
[MOVE_NONE] = [MOVE_NONE] =
{ {

View File

@ -3439,7 +3439,7 @@ static void DebugAction_Give_Pokemon_ComplexCreateMon(u8 taskId) //https://githu
//Moves //Moves
for (i = 0; i < MAX_MON_MOVES; i++) for (i = 0; i < MAX_MON_MOVES; i++)
{ {
if (moves[i] == MOVE_NONE || moves[i] == 0xFF || moves[i] >= MOVES_COUNT) if (moves[i] == MOVE_NONE || moves[i] >= MOVES_COUNT)
continue; continue;
SetMonMoveSlot(&mon, moves[i], i); SetMonMoveSlot(&mon, moves[i], i);

View File

@ -38,8 +38,6 @@ static void Task_DrawFieldMessage(u8 taskId)
LoadMessageBoxAndBorderGfx(); LoadMessageBoxAndBorderGfx();
task->tState++; task->tState++;
break; break;
task->tState++;
break;
case 1: case 1:
DrawDialogueFrame(0, TRUE); DrawDialogueFrame(0, TRUE);
task->tState++; task->tState++;

View File

@ -6074,15 +6074,8 @@ const u8 *GetTrainerPartnerName(void)
{ {
if (gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER) if (gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER)
{ {
if (gPartnerTrainerId == TRAINER_PARTNER(PARTNER_STEVEN)) GetFrontierTrainerName(gStringVar1, gPartnerTrainerId);
{ return gStringVar1;
return GetTrainerNameFromId(PARTNER_STEVEN);
}
else
{
GetFrontierTrainerName(gStringVar1, gPartnerTrainerId);
return gStringVar1;
}
} }
else else
{ {

View File

@ -57,3 +57,26 @@ SINGLE_BATTLE_TEST("Embody Aspect activates when it's no longer effected by Neut
MESSAGE("The opposing Ogerpon's Embody Aspect raised its Speed!"); MESSAGE("The opposing Ogerpon's Embody Aspect raised its Speed!");
} }
} }
SINGLE_BATTLE_TEST("Embody Aspect raises Speed only once per battle")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_OGERPON_TEAL_TERA) { Ability(ABILITY_EMBODY_ASPECT_TEAL_MASK); }
OPPONENT(SPECIES_WYNAUT);
} WHEN {
TURN { SWITCH(opponent, 1); }
TURN { SWITCH(opponent, 0); }
} SCENE {
ABILITY_POPUP(opponent, ABILITY_EMBODY_ASPECT_TEAL_MASK);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
MESSAGE("The opposing Ogerpon's Embody Aspect raised its Speed!");
NONE_OF {
ABILITY_POPUP(opponent, ABILITY_EMBODY_ASPECT_TEAL_MASK);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
MESSAGE("The opposing Ogerpon's Embody Aspect raised its Speed!");
}
} THEN {
EXPECT_EQ(opponent->statStages[STAT_SPEED], DEFAULT_STAT_STAGE);
}
}

View File

@ -831,3 +831,17 @@ AI_SINGLE_BATTLE_TEST("AI stays choice locked into moves in spite of the player'
TURN { EXPECT_MOVE(opponent, aiMove); } TURN { EXPECT_MOVE(opponent, aiMove); }
} }
} }
AI_SINGLE_BATTLE_TEST("AI won't use Sucker Punch if it expects a move of the same priority bracket and the opponent is faster")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_QUICK_ATTACK].priority == 1);
ASSUME(gMovesInfo[MOVE_SUCKER_PUNCH].priority == 1);
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
PLAYER(SPECIES_WOBBUFFET) { Speed(300); Moves(MOVE_QUICK_ATTACK); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(100); Moves(MOVE_SUCKER_PUNCH, MOVE_TACKLE); }
} WHEN {
TURN { MOVE(player, MOVE_QUICK_ATTACK); EXPECT_MOVE(opponent, MOVE_SUCKER_PUNCH); }
TURN { MOVE(player, MOVE_QUICK_ATTACK); EXPECT_MOVE(opponent, MOVE_TACKLE); }
}
}

View File

@ -4,119 +4,139 @@
ASSUMPTIONS ASSUMPTIONS
{ {
ASSUME(GetMoveEffect(MOVE_BRICK_BREAK) == EFFECT_BRICK_BREAK); ASSUME(GetMoveEffect(MOVE_BRICK_BREAK) == EFFECT_BRICK_BREAK);
ASSUME(GetMoveEffect(MOVE_PSYCHIC_FANGS) == EFFECT_BRICK_BREAK);
ASSUME(GetMoveEffect(MOVE_SNOWSCAPE) == EFFECT_SNOWSCAPE); ASSUME(GetMoveEffect(MOVE_SNOWSCAPE) == EFFECT_SNOWSCAPE);
ASSUME(GetMoveEffect(MOVE_LIGHT_SCREEN) == EFFECT_LIGHT_SCREEN); ASSUME(GetMoveEffect(MOVE_LIGHT_SCREEN) == EFFECT_LIGHT_SCREEN);
ASSUME(GetMoveEffect(MOVE_REFLECT) == EFFECT_REFLECT); ASSUME(GetMoveEffect(MOVE_REFLECT) == EFFECT_REFLECT);
ASSUME(GetMoveEffect(MOVE_AURORA_VEIL) == EFFECT_AURORA_VEIL); ASSUME(GetMoveEffect(MOVE_AURORA_VEIL) == EFFECT_AURORA_VEIL);
} }
SINGLE_BATTLE_TEST("Brick Break removes Light Screen, Reflect and Aurora Veil from the target's side of the field") SINGLE_BATTLE_TEST("Brick Break and Psychic Fangs remove Light Screen, Reflect and Aurora Veil from the target's side of the field")
{ {
u16 move; u32 move;
u32 breakingMove;
PARAMETRIZE { move = MOVE_LIGHT_SCREEN; } PARAMETRIZE { move = MOVE_LIGHT_SCREEN; breakingMove = MOVE_BRICK_BREAK; }
PARAMETRIZE { move = MOVE_REFLECT; } PARAMETRIZE { move = MOVE_REFLECT; breakingMove = MOVE_BRICK_BREAK; }
PARAMETRIZE { move = MOVE_AURORA_VEIL; } PARAMETRIZE { move = MOVE_AURORA_VEIL; breakingMove = MOVE_BRICK_BREAK; }
PARAMETRIZE { move = MOVE_LIGHT_SCREEN; breakingMove = MOVE_PSYCHIC_FANGS; }
PARAMETRIZE { move = MOVE_REFLECT; breakingMove = MOVE_PSYCHIC_FANGS; }
PARAMETRIZE { move = MOVE_AURORA_VEIL; breakingMove = MOVE_PSYCHIC_FANGS; }
GIVEN { GIVEN {
PLAYER(SPECIES_WOBBUFFET); PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET);
} WHEN { } WHEN {
TURN { MOVE(player, MOVE_SNOWSCAPE); } TURN { MOVE(player, MOVE_SNOWSCAPE); }
TURN { MOVE(opponent, move); MOVE(player, MOVE_BRICK_BREAK); } TURN { MOVE(opponent, move); MOVE(player, breakingMove); }
} SCENE { } SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SNOWSCAPE, player); ANIMATION(ANIM_TYPE_MOVE, MOVE_SNOWSCAPE, player);
ANIMATION(ANIM_TYPE_MOVE, move, opponent); ANIMATION(ANIM_TYPE_MOVE, move, opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_BRICK_BREAK, player); ANIMATION(ANIM_TYPE_MOVE, breakingMove, player);
MESSAGE("The wall shattered!"); MESSAGE("The wall shattered!");
HP_BAR(opponent); HP_BAR(opponent);
} }
} }
SINGLE_BATTLE_TEST("Brick Break doesn't remove Light Screen, Reflect and Aurora Veil if the target is immune") SINGLE_BATTLE_TEST("Brick Break and Psychic Fangs don't remove Light Screen, Reflect and Aurora Veil if the target is immune")
{ {
u16 move; u32 move;
u32 breakingMove;
PARAMETRIZE { move = MOVE_LIGHT_SCREEN; } PARAMETRIZE { move = MOVE_LIGHT_SCREEN; breakingMove = MOVE_BRICK_BREAK; }
PARAMETRIZE { move = MOVE_REFLECT; } PARAMETRIZE { move = MOVE_REFLECT; breakingMove = MOVE_BRICK_BREAK; }
PARAMETRIZE { move = MOVE_AURORA_VEIL; } PARAMETRIZE { move = MOVE_AURORA_VEIL; breakingMove = MOVE_BRICK_BREAK; }
PARAMETRIZE { move = MOVE_LIGHT_SCREEN; breakingMove = MOVE_PSYCHIC_FANGS; }
PARAMETRIZE { move = MOVE_REFLECT; breakingMove = MOVE_PSYCHIC_FANGS; }
PARAMETRIZE { move = MOVE_AURORA_VEIL; breakingMove = MOVE_PSYCHIC_FANGS; }
KNOWN_FAILING;
GIVEN { GIVEN {
PLAYER(SPECIES_WOBBUFFET); PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_GASTLY); OPPONENT(SPECIES_SABLEYE);
} WHEN { } WHEN {
TURN { MOVE(player, MOVE_SNOWSCAPE); } TURN { MOVE(player, MOVE_SNOWSCAPE); }
TURN { MOVE(opponent, move); MOVE(player, MOVE_BRICK_BREAK); } TURN { MOVE(opponent, move); MOVE(player, breakingMove); }
} SCENE { } SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SNOWSCAPE, player); ANIMATION(ANIM_TYPE_MOVE, MOVE_SNOWSCAPE, player);
ANIMATION(ANIM_TYPE_MOVE, move, opponent); ANIMATION(ANIM_TYPE_MOVE, move, opponent);
NONE_OF { NONE_OF {
ANIMATION(ANIM_TYPE_MOVE, MOVE_BRICK_BREAK, player); ANIMATION(ANIM_TYPE_MOVE, breakingMove, player);
MESSAGE("The wall shattered!"); MESSAGE("The wall shattered!");
HP_BAR(opponent); HP_BAR(opponent);
} }
} }
} }
SINGLE_BATTLE_TEST("Brick Break doesn't remove Light Screen, Reflect and Aurora Veil if the target Protected") SINGLE_BATTLE_TEST("Brick Break and Psychic Fangs don't remove Light Screen, Reflect and Aurora Veil if the target Protected")
{ {
u16 move; u32 move;
u32 breakingMove;
PARAMETRIZE { move = MOVE_LIGHT_SCREEN; } PARAMETRIZE { move = MOVE_LIGHT_SCREEN; breakingMove = MOVE_BRICK_BREAK; }
PARAMETRIZE { move = MOVE_REFLECT; } PARAMETRIZE { move = MOVE_REFLECT; breakingMove = MOVE_BRICK_BREAK; }
PARAMETRIZE { move = MOVE_AURORA_VEIL; } PARAMETRIZE { move = MOVE_AURORA_VEIL; breakingMove = MOVE_BRICK_BREAK; }
PARAMETRIZE { move = MOVE_LIGHT_SCREEN; breakingMove = MOVE_PSYCHIC_FANGS; }
PARAMETRIZE { move = MOVE_REFLECT; breakingMove = MOVE_PSYCHIC_FANGS; }
PARAMETRIZE { move = MOVE_AURORA_VEIL; breakingMove = MOVE_PSYCHIC_FANGS; }
GIVEN { GIVEN {
PLAYER(SPECIES_WOBBUFFET); PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET);
} WHEN { } WHEN {
TURN { MOVE(player, MOVE_SNOWSCAPE); MOVE(opponent, move); } TURN { MOVE(player, MOVE_SNOWSCAPE); MOVE(opponent, move); }
TURN { MOVE(player, MOVE_BRICK_BREAK); MOVE(opponent, MOVE_PROTECT); } TURN { MOVE(player, breakingMove); MOVE(opponent, MOVE_PROTECT); }
} SCENE { } SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SNOWSCAPE, player); ANIMATION(ANIM_TYPE_MOVE, MOVE_SNOWSCAPE, player);
ANIMATION(ANIM_TYPE_MOVE, move, opponent); ANIMATION(ANIM_TYPE_MOVE, move, opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_PROTECT, opponent); ANIMATION(ANIM_TYPE_MOVE, MOVE_PROTECT, opponent);
NONE_OF { NONE_OF {
ANIMATION(ANIM_TYPE_MOVE, MOVE_BRICK_BREAK, player); ANIMATION(ANIM_TYPE_MOVE, breakingMove, player);
MESSAGE("The wall shattered!"); MESSAGE("The wall shattered!");
HP_BAR(opponent); HP_BAR(opponent);
} }
} }
} }
SINGLE_BATTLE_TEST("Brick Break doesn't remove Light Screen, Reflect and Aurora Veil if it misses") SINGLE_BATTLE_TEST("Brick Break and Psychic Fangs don't remove Light Screen, Reflect and Aurora Veil if it misses")
{ {
u16 move; u32 move;
u32 breakingMove;
PARAMETRIZE { move = MOVE_LIGHT_SCREEN; } PARAMETRIZE { move = MOVE_LIGHT_SCREEN; breakingMove = MOVE_BRICK_BREAK; }
PARAMETRIZE { move = MOVE_REFLECT; } PARAMETRIZE { move = MOVE_REFLECT; breakingMove = MOVE_BRICK_BREAK; }
PARAMETRIZE { move = MOVE_AURORA_VEIL; } PARAMETRIZE { move = MOVE_AURORA_VEIL; breakingMove = MOVE_BRICK_BREAK; }
PARAMETRIZE { move = MOVE_LIGHT_SCREEN; breakingMove = MOVE_PSYCHIC_FANGS; }
PARAMETRIZE { move = MOVE_REFLECT; breakingMove = MOVE_PSYCHIC_FANGS; }
PARAMETRIZE { move = MOVE_AURORA_VEIL; breakingMove = MOVE_PSYCHIC_FANGS; }
GIVEN { GIVEN {
PLAYER(SPECIES_WOBBUFFET); PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_BRIGHT_POWDER); } OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_BRIGHT_POWDER); }
} WHEN { } WHEN {
TURN { MOVE(player, MOVE_SNOWSCAPE); MOVE(opponent, move); } TURN { MOVE(player, MOVE_SNOWSCAPE); MOVE(opponent, move); }
TURN { MOVE(player, MOVE_BRICK_BREAK, hit: FALSE); } TURN { MOVE(player, breakingMove, hit: FALSE); }
} SCENE { } SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SNOWSCAPE, player); ANIMATION(ANIM_TYPE_MOVE, MOVE_SNOWSCAPE, player);
ANIMATION(ANIM_TYPE_MOVE, move, opponent); ANIMATION(ANIM_TYPE_MOVE, move, opponent);
NONE_OF { NONE_OF {
ANIMATION(ANIM_TYPE_MOVE, MOVE_BRICK_BREAK, player); ANIMATION(ANIM_TYPE_MOVE, breakingMove, player);
MESSAGE("The wall shattered!"); MESSAGE("The wall shattered!");
HP_BAR(opponent); HP_BAR(opponent);
} }
} }
} }
DOUBLE_BATTLE_TEST("Brick Break can remove Light Screen, Reflect and Aurora Veil on users side") DOUBLE_BATTLE_TEST("Brick Break and Psychic Fangs can remove Light Screen, Reflect and Aurora Veil on users side")
{ {
u16 move; u32 move;
u32 breakingMove;
PARAMETRIZE { move = MOVE_LIGHT_SCREEN; } PARAMETRIZE { move = MOVE_LIGHT_SCREEN; breakingMove = MOVE_BRICK_BREAK; }
PARAMETRIZE { move = MOVE_REFLECT; } PARAMETRIZE { move = MOVE_REFLECT; breakingMove = MOVE_BRICK_BREAK; }
PARAMETRIZE { move = MOVE_AURORA_VEIL; } PARAMETRIZE { move = MOVE_AURORA_VEIL; breakingMove = MOVE_BRICK_BREAK; }
PARAMETRIZE { move = MOVE_LIGHT_SCREEN; breakingMove = MOVE_PSYCHIC_FANGS; }
PARAMETRIZE { move = MOVE_REFLECT; breakingMove = MOVE_PSYCHIC_FANGS; }
PARAMETRIZE { move = MOVE_AURORA_VEIL; breakingMove = MOVE_PSYCHIC_FANGS; }
GIVEN { GIVEN {
PLAYER(SPECIES_WOBBUFFET); PLAYER(SPECIES_WOBBUFFET);
@ -127,12 +147,12 @@ DOUBLE_BATTLE_TEST("Brick Break can remove Light Screen, Reflect and Aurora Veil
TURN { TURN {
MOVE(opponentLeft, MOVE_SNOWSCAPE); MOVE(opponentLeft, MOVE_SNOWSCAPE);
MOVE(playerLeft, move); MOVE(playerLeft, move);
MOVE(playerRight, MOVE_BRICK_BREAK, target: playerLeft); MOVE(playerRight, breakingMove, target: playerLeft);
} }
} SCENE { } SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SNOWSCAPE, opponentLeft); ANIMATION(ANIM_TYPE_MOVE, MOVE_SNOWSCAPE, opponentLeft);
ANIMATION(ANIM_TYPE_MOVE, move, playerLeft); ANIMATION(ANIM_TYPE_MOVE, move, playerLeft);
ANIMATION(ANIM_TYPE_MOVE, MOVE_BRICK_BREAK, playerRight); ANIMATION(ANIM_TYPE_MOVE, breakingMove, playerRight);
MESSAGE("The wall shattered!"); MESSAGE("The wall shattered!");
HP_BAR(playerLeft); HP_BAR(playerLeft);
} }

View File

@ -15,7 +15,9 @@ SINGLE_BATTLE_TEST("Curse lowers Speed, raises Attack, and raises Defense when u
TURN { MOVE(player, MOVE_CURSE); } TURN { MOVE(player, MOVE_CURSE); }
} SCENE { } SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_CURSE, player); ANIMATION(ANIM_TYPE_MOVE, MOVE_CURSE, player);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
MESSAGE("Wobbuffet's Speed fell!"); MESSAGE("Wobbuffet's Speed fell!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
MESSAGE("Wobbuffet's Attack rose!"); MESSAGE("Wobbuffet's Attack rose!");
MESSAGE("Wobbuffet's Defense rose!"); MESSAGE("Wobbuffet's Defense rose!");
} }

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 { GIVEN {
PLAYER(SPECIES_WOBBUFFET); PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WYNAUT); PLAYER(SPECIES_WYNAUT);
@ -226,7 +229,7 @@ DOUBLE_BATTLE_TEST("Instructed move will be absorbed by Lightning Rod if it turn
OPPONENT(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET);
} WHEN { } WHEN {
TURN { TURN {
MOVE(playerLeft, MOVE_TACKLE, target: opponentLeft); MOVE(playerLeft, MOVE_TACKLE, target: moveTarget);
MOVE(opponentLeft, MOVE_PLASMA_FISTS, target: playerLeft); MOVE(opponentLeft, MOVE_PLASMA_FISTS, target: playerLeft);
MOVE(playerRight, MOVE_INSTRUCT, target: playerLeft); MOVE(playerRight, MOVE_INSTRUCT, target: playerLeft);
MOVE(opponentRight, MOVE_CELEBRATE); 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); 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

@ -41,7 +41,6 @@ SINGLE_BATTLE_TEST("Raging Bull doesn't remove Light Screen, Reflect and Aurora
PARAMETRIZE { move = MOVE_REFLECT; } PARAMETRIZE { move = MOVE_REFLECT; }
PARAMETRIZE { move = MOVE_AURORA_VEIL; } PARAMETRIZE { move = MOVE_AURORA_VEIL; }
KNOWN_FAILING;
GIVEN { GIVEN {
PLAYER(SPECIES_WOBBUFFET); PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_GASTLY); OPPONENT(SPECIES_GASTLY);

View File

@ -0,0 +1,98 @@
#include "global.h"
#include "test/battle.h"
ASSUMPTIONS
{
ASSUME(gMovesInfo[MOVE_ROTOTILLER].effect == EFFECT_ROTOTILLER);
}
DOUBLE_BATTLE_TEST("Rototiller boosts Attack and Special Attack of all Grass types on the field")
{
GIVEN {
ASSUME(gSpeciesInfo[SPECIES_TANGELA].types[0] == TYPE_GRASS);
ASSUME(gSpeciesInfo[SPECIES_SNIVY].types[0] == TYPE_GRASS);
PLAYER(SPECIES_TANGELA);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_SNIVY);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(playerRight, MOVE_ROTOTILLER); MOVE(playerLeft, MOVE_CELEBRATE); MOVE(opponentLeft, MOVE_CELEBRATE); MOVE(opponentRight, MOVE_CELEBRATE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_ROTOTILLER, playerRight);
NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft);
NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentRight);
} THEN {
EXPECT_EQ(playerLeft->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 1);
EXPECT_EQ(playerLeft->statStages[STAT_SPATK], DEFAULT_STAT_STAGE + 1);
EXPECT_EQ(opponentLeft->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 1);
EXPECT_EQ(opponentLeft->statStages[STAT_SPATK], DEFAULT_STAT_STAGE + 1);
EXPECT_EQ(playerRight->statStages[STAT_ATK], DEFAULT_STAT_STAGE);
EXPECT_EQ(playerRight->statStages[STAT_SPATK], DEFAULT_STAT_STAGE);
EXPECT_EQ(opponentRight->statStages[STAT_ATK], DEFAULT_STAT_STAGE);
EXPECT_EQ(opponentRight->statStages[STAT_SPATK], DEFAULT_STAT_STAGE);
}
}
SINGLE_BATTLE_TEST("Rototiller fails if there are no valid targets")
{
GIVEN {
ASSUME(gSpeciesInfo[SPECIES_WOBBUFFET].types[0] != TYPE_GRASS);
ASSUME(gSpeciesInfo[SPECIES_WOBBUFFET].types[1] != TYPE_GRASS);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_ROTOTILLER); }
} SCENE {
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_ROTOTILLER, player);
MESSAGE("Wobbuffet used Rototiller!");
MESSAGE("But it failed!");
}
}
SINGLE_BATTLE_TEST("Rototiller doesn't affect pokemon that are semi-invulnerable")
{
GIVEN {
ASSUME(gSpeciesInfo[SPECIES_TANGELA].types[0] == TYPE_GRASS);
ASSUME(gMovesInfo[MOVE_DIG].effect == EFFECT_SEMI_INVULNERABLE);
PLAYER(SPECIES_TANGELA);
OPPONENT(SPECIES_TANGELA);
} WHEN {
TURN { MOVE(opponent, MOVE_DIG); MOVE(player, MOVE_ROTOTILLER); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_DIG, opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_ROTOTILLER, player);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
MESSAGE("It won't have any effect on the opposing Tangela!");
} THEN {
EXPECT_EQ(player->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 1);
EXPECT_EQ(player->statStages[STAT_SPATK], DEFAULT_STAT_STAGE + 1);
EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE);
EXPECT_EQ(opponent->statStages[STAT_SPATK], DEFAULT_STAT_STAGE);
}
}
SINGLE_BATTLE_TEST("Rototiller fails if the only valid target is semi-invulnerable")
{
GIVEN {
ASSUME(gSpeciesInfo[SPECIES_TANGELA].types[0] == TYPE_GRASS);
ASSUME(gSpeciesInfo[SPECIES_WOBBUFFET].types[0] != TYPE_GRASS);
ASSUME(gSpeciesInfo[SPECIES_WOBBUFFET].types[1] != TYPE_GRASS);
ASSUME(gMovesInfo[MOVE_DIG].effect == EFFECT_SEMI_INVULNERABLE);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_TANGELA);
} WHEN {
TURN { MOVE(opponent, MOVE_DIG); MOVE(player, MOVE_ROTOTILLER); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_DIG, opponent);
MESSAGE("Wobbuffet used Rototiller!");
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_ROTOTILLER, player);
MESSAGE("But it failed!");
} THEN {
EXPECT_EQ(player->statStages[STAT_ATK], DEFAULT_STAT_STAGE);
EXPECT_EQ(player->statStages[STAT_SPATK], DEFAULT_STAT_STAGE);
EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE);
EXPECT_EQ(opponent->statStages[STAT_SPATK], DEFAULT_STAT_STAGE);
}
}

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

View File

@ -62,3 +62,18 @@ AI_SINGLE_BATTLE_TEST("AI avoids Thunder Wave when it can not paralyse target")
TURN { SCORE_EQ(opponent, MOVE_CELEBRATE, MOVE_THUNDER_WAVE); } // Both get -10 TURN { SCORE_EQ(opponent, MOVE_CELEBRATE, MOVE_THUNDER_WAVE); } // Both get -10
} }
} }
SINGLE_BATTLE_TEST("Thunder Wave doesn't affect Electric types in Gen6+")
{
GIVEN {
ASSUME(gSpeciesInfo[SPECIES_PIKACHU].types[0] == TYPE_ELECTRIC);
ASSUME(B_PARALYZE_ELECTRIC >= GEN_6);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_PIKACHU);
} WHEN {
TURN { MOVE(player, MOVE_THUNDER_WAVE); }
} SCENE {
MESSAGE("Wobbuffet used Thunder Wave!");
MESSAGE("It doesn't affect the opposing Pikachu…");
}
}