Merge branch 'master' of https://github.com/rh-hideout/pokeemerald-expansion into rh-hideout-master

This commit is contained in:
RoamerX 2026-01-27 12:26:20 +08:00
commit e94050f53c
85 changed files with 1473 additions and 371 deletions

View File

@ -615,6 +615,24 @@
"contributions": [
"code"
]
},
{
"login": "SabataLunar",
"name": "SabataLunar",
"avatar_url": "https://avatars.githubusercontent.com/u/26584469?v=4",
"profile": "https://github.com/SabataLunar",
"contributions": [
"design"
]
},
{
"login": "PacFire",
"name": "PacFire",
"avatar_url": "https://avatars.githubusercontent.com/u/108960850?v=4",
"profile": "https://github.com/PacFire",
"contributions": [
"design"
]
}
],
"contributorsPerLine": 7,

View File

@ -88,6 +88,10 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
<td align="center" valign="top" width="14.28%"><a href="http://hufford.io"><img src="https://avatars.githubusercontent.com/u/8021794?v=4?s=100" width="100px;" alt="Josh Hufford"/><br /><sub><b>Josh Hufford</b></sub></a><br /><a href="https://github.com/rh-hideout/pokeemerald-expansion/commits?author=ostomachion" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Kasenn"><img src="https://avatars.githubusercontent.com/u/115586266?v=4?s=100" width="100px;" alt="Kasenn"/><br /><sub><b>Kasenn</b></sub></a><br /><a href="https://github.com/rh-hideout/pokeemerald-expansion/commits?author=Kasenn" title="Code">💻</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/SabataLunar"><img src="https://avatars.githubusercontent.com/u/26584469?v=4?s=100" width="100px;" alt="SabataLunar"/><br /><sub><b>SabataLunar</b></sub></a><br /><a href="#design-SabataLunar" title="Design">🎨</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/PacFire"><img src="https://avatars.githubusercontent.com/u/108960850?v=4?s=100" width="100px;" alt="PacFire"/><br /><sub><b>PacFire</b></sub></a><br /><a href="#design-PacFire" title="Design">🎨</a></td>
</tr>
</tbody>
<tfoot>
<tr>

View File

@ -1171,24 +1171,59 @@ BattleScript_EffectSpectralThiefFromDamage:
BattleScript_EffectPartingShot::
attackcanceler
jumpifstat BS_TARGET, CMP_GREATER_THAN, STAT_ATK, MIN_STAT_STAGE, BattleScript_EffectPartingShotTryAtk
jumpifstat BS_TARGET, CMP_EQUAL, STAT_SPATK, MIN_STAT_STAGE, BattleScript_CantLowerMultipleStats
jumpifstat BS_TARGET, CMP_EQUAL, STAT_SPATK, MIN_STAT_STAGE, BattleScript_EffectPartingShotCantLowerMultipleStats
BattleScript_EffectPartingShotTryAtk:
accuracycheck BattleScript_MoveMissedPause, ACC_CURR_MOVE
attackanimation
waitanimation
setbyte sB_ANIM_TARGETS_HIT, 0
setstatchanger STAT_ATK, 1, TRUE
statbuffchange BS_TARGET, STAT_CHANGE_ALLOW_PTR, BattleScript_EffectPartingShotTrySpAtk, BIT_SPATK
printfromtable gStatDownStringIds
waitmessage B_WAIT_TIME_LONG
call BattleScript_EffectPartingShotMaybePrintStat
BattleScript_EffectPartingShotTrySpAtk:
setstatchanger STAT_SPATK, 1, TRUE
statbuffchange BS_TARGET, STAT_CHANGE_ALLOW_PTR, BattleScript_EffectPartingShotSwitch
printfromtable gStatDownStringIds
waitmessage B_WAIT_TIME_LONG
statbuffchange BS_TARGET, STAT_CHANGE_ALLOW_PTR, BattleScript_EffectPartingShotMaybeSwitch
call BattleScript_EffectPartingShotMaybePrintStat
BattleScript_EffectPartingShotMaybeSwitch:
jumpifgenconfiglowerthan CONFIG_PARTING_SHOT_SWITCH, GEN_7, BattleScript_EffectPartingShotSwitch
jumpifbyte CMP_NOT_EQUAL, sB_ANIM_TARGETS_HIT, 0, BattleScript_EffectPartingShotSwitch
goto BattleScript_MoveEnd
BattleScript_EffectPartingShotSwitch:
moveendall
goto BattleScript_MoveSwitchPursuitEnd
BattleScript_EffectPartingShotCantLowerMultipleStats:
pause B_WAIT_TIME_SHORT
setmoveresultflags MOVE_RESULT_FAILED
call BattleScript_EffectPartingShotPrintWontDecrease
setbyte sB_ANIM_TARGETS_HIT, 0
goto BattleScript_EffectPartingShotMaybeSwitch
BattleScript_EffectPartingShotMaybePrintStat:
jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, B_MSG_ATTACKER_STAT_CHANGED, BattleScript_EffectPartingShotPrintStat
jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, B_MSG_DEFENDER_STAT_CHANGED, BattleScript_EffectPartingShotPrintStat
return
BattleScript_EffectPartingShotPrintStat:
setbyte sB_ANIM_TARGETS_HIT, 1
printfromtable gStatDownStringIds
waitmessage B_WAIT_TIME_LONG
return
BattleScript_EffectPartingShotPrintWontDecrease:
jumpifability BS_TARGET, ABILITY_CONTRARY, BattleScript_EffectPartingShotPrintWontDecreaseContrary
printstring STRINGID_STATSWONTDECREASE2
waitmessage B_WAIT_TIME_LONG
return
BattleScript_EffectPartingShotPrintWontDecreaseContrary:
swapattackerwithtarget
printstring STRINGID_STATSWONTDECREASE2
waitmessage B_WAIT_TIME_LONG
swapattackerwithtarget
return
BattleScript_EffectPowder::
attackcanceler
accuracycheck BattleScript_MoveMissedPause, NO_ACC_CALC_CHECK_LOCK_ON
@ -1224,6 +1259,7 @@ BattleScript_EffectAromaticMistWontGoHigher:
BattleScript_EffectMagneticFlux::
attackcanceler
savetarget
setbyte gBattleCommunication, 0
BattleScript_EffectMagneticFluxStart:
jumpifability BS_TARGET, ABILITY_MINUS, BattleScript_EffectMagneticFluxCheckStats
@ -1252,13 +1288,16 @@ BattleScript_EffectMagneticFluxTrySpDef:
waitmessage B_WAIT_TIME_LONG
BattleScript_EffectMagneticFluxLoop:
jumpifbytenotequal gBattlerTarget, gBattlerAttacker, BattleScript_EffectMagneticFluxEnd
jumpifnoally BS_ATTACKER, BattleScript_EffectMagneticFluxEnd
setallytonexttarget BattleScript_EffectMagneticFluxStart
BattleScript_EffectMagneticFluxEnd:
restoretarget
jumpifbyte CMP_NOT_EQUAL, gBattleCommunication, 0, BattleScript_MoveEnd
goto BattleScript_ButItFailed
BattleScript_EffectGearUp::
attackcanceler
savetarget
setbyte gBattleCommunication, 0
BattleScript_EffectGearUpStart:
jumpifability BS_TARGET, ABILITY_MINUS, BattleScript_EffectGearUpCheckStats
@ -1287,8 +1326,10 @@ BattleScript_EffectGearUpTrySpAtk:
waitmessage B_WAIT_TIME_LONG
BattleScript_EffectGearUpLoop:
jumpifbytenotequal gBattlerTarget, gBattlerAttacker, BattleScript_EffectGearUpEnd
jumpifnoally BS_ATTACKER, BattleScript_EffectGearUpEnd
setallytonexttarget BattleScript_EffectGearUpStart
BattleScript_EffectGearUpEnd:
restoretarget
jumpifbyte CMP_NOT_EQUAL, gBattleCommunication, 0, BattleScript_MoveEnd
goto BattleScript_ButItFailed
@ -2316,6 +2357,9 @@ BattleScript_TryTailwindAbilitiesLoop_WindPower:
BattleScript_EffectMiracleEye::
attackcanceler
accuracycheck BattleScript_MoveMissedPause, ACC_CURR_MOVE
jumpifgenconfiglowerthan CONFIG_MIRACLE_EYE_FAIL, GEN_5, BattleScript_MiracleEyeSet
jumpifvolatile BS_TARGET, VOLATILE_MIRACLE_EYE, BattleScript_ButItFailed
BattleScript_MiracleEyeSet:
setvolatile BS_TARGET, VOLATILE_MIRACLE_EYE
goto BattleScript_IdentifiedFoe
@ -3438,7 +3482,11 @@ BattleScript_EffectSpikes::
BattleScript_EffectForesight::
attackcanceler
accuracycheck BattleScript_ButItFailed, NO_ACC_CALC_CHECK_LOCK_ON
jumpifgenconfiglowerthan CONFIG_FORESIGHT_FAIL, GEN_3, BattleScript_ForesightFailCheck
jumpifgenconfiglowerthan CONFIG_FORESIGHT_FAIL, GEN_5, BattleScript_ForesightSet
BattleScript_ForesightFailCheck:
jumpifvolatile BS_TARGET, VOLATILE_FORESIGHT, BattleScript_ButItFailed
BattleScript_ForesightSet:
setvolatile BS_TARGET, VOLATILE_FORESIGHT
BattleScript_IdentifiedFoe:
attackanimation

View File

@ -34,9 +34,9 @@ will NOT be merged until after the next Minor Release.
---
## What is a "Big Feature"?
* If the original owner of the PR thinks a feature should be labeled a Big Feature, it is, no questions asked
* If a reviewer thinks a PR is a Big Feature, then it is
* If the two disagree, it can be discussed in a PR thread, and can ultimately be resolved with a Maintainer vote.
* If any maintainer thinks a PR is a Big Feature, then it is, no question asked
* If there is disagreement, it can be discussed in a PR thread, and can ultimately be resolved with a Maintainer vote.
* If you believe your PR should have this feature, please let a maintainer know.
### How To Identify a Big Feature
* **Big diffs**: It's easy for something to go unnoticed in review when it's a tiny part of a massive diff.

View File

@ -27,26 +27,6 @@
#include "random.h" // for rng_value_t
#include "trainer_slide.h"
// Helper for accessing command arguments and advancing gBattlescriptCurrInstr.
//
// For example accuracycheck is defined as:
//
// .macro accuracycheck failInstr:req, move:req
// .byte 0x1
// .4byte \failInstr
// .2byte \move
// .endm
//
// Which corresponds to:
//
// CMD_ARGS(const u8 *failInstr, u16 move);
//
// The arguments can be accessed as cmd->failInstr and cmd->move.
// gBattlescriptCurrInstr = cmd->nextInstr; advances to the next instruction.
#define CMD_ARGS(...) const struct __attribute__((packed)) { u8 opcode; RECURSIVELY(R_FOR_EACH(APPEND_SEMICOLON, __VA_ARGS__)) const u8 nextInstr[0]; } *const cmd UNUSED = (const void *)gBattlescriptCurrInstr
#define VARIOUS_ARGS(...) CMD_ARGS(u8 battler, u8 id, ##__VA_ARGS__)
#define NATIVE_ARGS(...) CMD_ARGS(void (*func)(void), ##__VA_ARGS__)
// Used to exclude moves learned temporarily by Transform or Mimic
#define MOVE_IS_PERMANENT(battler, moveSlot) \
(!(gBattleMons[battler].volatiles.transformed) \
@ -1230,7 +1210,7 @@ static inline struct PartyState *GetBattlerPartyState(u32 battler)
static inline bool32 IsDoubleBattle(void)
{
return (gBattleTypeFlags & BATTLE_TYPE_MORE_THAN_TWO_BATTLERS);
return !!(gBattleTypeFlags & BATTLE_TYPE_MORE_THAN_TWO_BATTLERS);
}
static inline bool32 IsSpreadMove(u32 moveTarget)

View File

@ -96,6 +96,7 @@
#define B_CAN_SPITE_FAIL GEN_LATEST // In Gen4+, Spite can no longer fail if the foe's last move only has 1 remaining PP.
#define B_CRASH_IF_TARGET_IMMUNE GEN_LATEST // In Gen4+, moves with crash damage will crash if the user attacks a target that is immune due to their typing.
#define B_MEMENTO_FAIL GEN_LATEST // In Gen4+, Memento no longer fails if the target already has -6 Attack and Special Attack. Additionally, in Gen5+, it fails if there is no target, or if the target is protected or behind a Substitute.
#define B_PARTING_SHOT_SWITCH GEN_LATEST // In Gen7+, the user won't switch out if Parting Shot fails to lower the target's stats.
#define B_GLARE_GHOST GEN_LATEST // In Gen4+, Glare can hit Ghost-type Pokémon normally.
#define B_SKILL_SWAP GEN_LATEST // In Gen4+, Skill Swap triggers switch-in abilities after use.
#define B_BRICK_BREAK GEN_LATEST // In Gen4+, you can destroy your own side's screens. In Gen 5+, screens are not removed if the target is immune.
@ -127,6 +128,8 @@
#define B_AFTER_YOU_TURN_ORDER GEN_LATEST // In Gen8+, After You doesn't fail if the turn order wouldn't change after use.
#define B_QUASH_TURN_ORDER GEN_LATEST // In Gen8+, Quash-affected battlers move according to speed order. Before Gen8, Quash-affected battlers move in the order they were affected by Quash.
#define B_DESTINY_BOND_FAIL GEN_LATEST // In Gen7+, Destiny Bond fails if used repeatedly.
#define B_FORESIGHT_FAIL GEN_LATEST // In Gen2 and Gen5+, Foresight fails if used against a target already under its effect.
#define B_MIRACLE_EYE_FAIL GEN_LATEST // In Gen5+, Miracle Eye fails if used against a target already under its effect.
#define B_PURSUIT_TARGET GEN_LATEST // In Gen4+, Pursuit attacks a switching opponent even if they weren't targeting them. Before Gen4, Pursuit only attacks a switching opponent that it originally targeted.
#define B_SKIP_RECHARGE GEN_LATEST // In Gen1, recharging moves such as Hyper Beam skip the recharge if the target gets KO'd
#define B_ENCORE_TARGET GEN_LATEST // In Gen5+, encored moves are allowed to choose a target

View File

@ -89,6 +89,7 @@
F(CAN_SPITE_FAIL, canSpiteFail, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \
F(CRASH_IF_TARGET_IMMUNE, crashIfTargetImmune, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \
F(MEMENTO_FAIL, mementoFail, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \
F(PARTING_SHOT_SWITCH, partingShotSwitch, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \
F(GLARE_GHOST, glareGhost, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \
F(SKILL_SWAP, skillSwap, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \
F(BRICK_BREAK, brickBreak, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \
@ -118,6 +119,8 @@
F(AFTER_YOU_TURN_ORDER, afterYouTurnOrder, (u32, GEN_COUNT - 1)) \
F(QUASH_TURN_ORDER, quashTurnOrder, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \
F(DESTINY_BOND_FAIL, destinyBondFail, (u32, GEN_COUNT - 1)) \
F(FORESIGHT_FAIL, foresightFail, (u32, GEN_COUNT - 1)) \
F(MIRACLE_EYE_FAIL, miracleEyeFail, (u32, GEN_COUNT - 1)) \
F(PURSUIT_TARGET, pursuitTarget, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \
F(SKIP_RECHARGE, skipRecharge, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \
F(ENCORE_TARGET, encoreTarget, (u32, GEN_COUNT - 1)) \

View File

@ -63,4 +63,11 @@ enum {
bool32 CanThrowBall(void);
bool32 CannotUseItemsInBattle(u16 itemId, struct Pokemon *mon);
enum ItemTMHMOrEvolutionStone
{
ITEM_IS_OTHER,
ITEM_IS_TM_HM,
ITEM_IS_EVOLUTION_STONE,
};
#endif // GUARD_ITEM_USE_H

View File

@ -5500,9 +5500,9 @@ static s32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move, stru
tailwindScore += 1;
if (speed <= foe2Speed && (speed * 2) > foe2Speed)
tailwindScore += 1;
if (partnerSpeed <= foe1Speed && (speed * 2) > foe1Speed)
if (partnerSpeed <= foe1Speed && (partnerSpeed * 2) > foe1Speed)
tailwindScore += 1;
if (partnerSpeed <= foe1Speed && (speed * 2) > foe1Speed)
if (partnerSpeed <= foe2Speed && (partnerSpeed * 2) > foe2Speed)
tailwindScore += 1;
if (tailwindScore > 0)

View File

@ -1960,40 +1960,45 @@ bool32 IsAllyProtectingFromMove(u32 battlerAtk, u32 attackerMove, u32 allyMove)
{
return FALSE;
}
else
enum ProtectMethod protectMethod = GetMoveProtectMethod(allyMove);
switch (protectMethod)
{
enum ProtectMethod protectMethod = GetMoveProtectMethod(allyMove);
if (protectMethod == PROTECT_QUICK_GUARD)
case PROTECT_CRAFTY_SHIELD:
if (!IsBattleMoveStatus(attackerMove))
{
u32 priority = GetBattleMovePriority(battlerAtk, gAiLogicData->abilities[battlerAtk], attackerMove);
return (priority > 0);
return FALSE;
}
if (IsBattleMoveStatus(attackerMove))
else if (GetMoveEffect(attackerMove) == EFFECT_HOLD_HANDS)
{
switch (protectMethod)
{
case PROTECT_NORMAL:
case PROTECT_CRAFTY_SHIELD:
case PROTECT_MAX_GUARD:
case PROTECT_WIDE_GUARD:
return TRUE;
default:
return FALSE;
}
return TRUE;
}
else
{
switch (protectMethod)
{
case PROTECT_CRAFTY_SHIELD:
return FALSE;
default:
return TRUE;
}
u32 moveTarget = GetBattlerMoveTargetType(battlerAtk, attackerMove);
return (GetBattlerSide(battlerAtk) != GetBattlerSide(BATTLE_PARTNER(battlerAtk))
&& moveTarget != MOVE_TARGET_OPPONENTS_FIELD
&& moveTarget != MOVE_TARGET_ALL_BATTLERS);
}
case PROTECT_WIDE_GUARD:
return IsSpreadMove(GetBattlerMoveTargetType(battlerAtk, attackerMove));
case PROTECT_NORMAL:
case PROTECT_SPIKY_SHIELD:
case PROTECT_MAX_GUARD:
case PROTECT_BANEFUL_BUNKER:
case PROTECT_BURNING_BULWARK:
return TRUE;
case PROTECT_OBSTRUCT:
case PROTECT_SILK_TRAP:
case PROTECT_KINGS_SHIELD:
return !IsBattleMoveStatus(attackerMove);
case PROTECT_QUICK_GUARD:
return (GetChosenMovePriority(battlerAtk, gAiLogicData->abilities[battlerAtk]) > 0);
case PROTECT_MAT_BLOCK:
return !IsBattleMoveStatus(attackerMove);
default:
return FALSE;
}
}

View File

@ -457,43 +457,3 @@ void ChooseDamageNonTypesString(enum Type type)
break;
}
}
// Updates Dynamax HP multipliers and healthboxes.
void BS_UpdateDynamax(void)
{
NATIVE_ARGS();
u32 battler = gBattleScripting.battler;
struct Pokemon *mon = GetBattlerMon(battler);
if (!IsGigantamaxed(battler)) // RecalcBattlerStats will get called on form change.
RecalcBattlerStats(battler, mon, GetActiveGimmick(battler) == GIMMICK_DYNAMAX);
UpdateHealthboxAttribute(gHealthboxSpriteIds[battler], mon, HEALTHBOX_ALL);
gBattlescriptCurrInstr = cmd->nextInstr;
}
// Goes to the jump instruction if the target is Dynamaxed.
void BS_JumpIfDynamaxed(void)
{
NATIVE_ARGS(const u8 *jumpInstr);
if ((GetActiveGimmick(gBattlerTarget) == GIMMICK_DYNAMAX))
gBattlescriptCurrInstr = cmd->jumpInstr;
else
gBattlescriptCurrInstr = cmd->nextInstr;
}
void BS_UndoDynamax(void)
{
NATIVE_ARGS(u8 battler);
u32 battler = GetBattlerForBattleScript(cmd->battler);
if (GetActiveGimmick(battler) == GIMMICK_DYNAMAX)
{
UndoDynamax(battler);
gBattleScripting.battler = battler;
BattleScriptCall(BattleScript_DynamaxEnds_Ret);
return;
}
gBattlescriptCurrInstr = cmd->nextInstr;
}

View File

@ -72,6 +72,26 @@
#include "load_save.h"
#include "test/test_runner_battle.h"
// Helper for accessing command arguments and advancing gBattlescriptCurrInstr.
//
// For example accuracycheck is defined as:
//
// .macro accuracycheck failInstr:req, move:req
// .byte 0x1
// .4byte \failInstr
// .2byte \move
// .endm
//
// Which corresponds to:
//
// CMD_ARGS(const u8 *failInstr, u16 move);
//
// The arguments can be accessed as cmd->failInstr and cmd->move.
// gBattlescriptCurrInstr = cmd->nextInstr; advances to the next instruction.
#define CMD_ARGS(...) const struct __attribute__((packed)) { u8 opcode; RECURSIVELY(R_FOR_EACH(APPEND_SEMICOLON, __VA_ARGS__)) const u8 nextInstr[0]; } *const cmd UNUSED = (const void *)gBattlescriptCurrInstr
#define VARIOUS_ARGS(...) CMD_ARGS(u8 battler, u8 id, ##__VA_ARGS__)
#define NATIVE_ARGS(...) CMD_ARGS(void (*func)(void), ##__VA_ARGS__)
// table to avoid ugly powing on gba (courtesy of doesnt)
// this returns (i^2.5)/4
// the quarters cancel so no need to re-quadruple them in actual calculation
@ -3451,7 +3471,7 @@ void SetMoveEffect(u32 battler, u32 effectBattler, enum MoveEffect moveEffect, c
if (i)
{
BattleScriptPush(battleScript);
if (gCurrentMove == MOVE_HYPERSPACE_FURY)
if (GetMoveEffect(gCurrentMove) == EFFECT_HYPERSPACE_FURY)
gBattlescriptCurrInstr = BattleScript_HyperspaceFuryRemoveProtect;
else
gBattlescriptCurrInstr = BattleScript_MoveEffectFeint;
@ -6153,7 +6173,7 @@ static void Cmd_moveend(void)
// Not strictly a protect effect, but works the same way
if (IsBattlerUsingBeakBlast(gBattlerTarget)
&& CanBeBurned(gBattlerAttacker, gBattlerAttacker, GetBattlerAbility(gBattlerAttacker))
&& !(gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_NO_EFFECT))
&& IsBattlerTurnDamaged(gBattlerTarget))
{
gProtectStructs[gBattlerAttacker].touchedProtectLike = FALSE;
gBattleMons[gBattlerAttacker].status1 = STATUS1_BURN;
@ -9733,7 +9753,8 @@ static void Cmd_setprotectlike(void)
if ((sProtectSuccessRates[gDisableStructs[gBattlerAttacker].protectUses] >= RandomUniform(RNG_PROTECT_FAIL, 0, USHRT_MAX) && notLastTurn)
|| (protectMethod == PROTECT_WIDE_GUARD && GetConfig(CONFIG_WIDE_GUARD) >= GEN_6)
|| (protectMethod == PROTECT_QUICK_GUARD && GetConfig(CONFIG_QUICK_GUARD) >= GEN_6))
|| (protectMethod == PROTECT_QUICK_GUARD && GetConfig(CONFIG_QUICK_GUARD) >= GEN_6)
|| (protectMethod == PROTECT_CRAFTY_SHIELD))
{
if (GetMoveEffect(gCurrentMove) == EFFECT_ENDURE)
{
@ -10375,6 +10396,8 @@ static u32 ChangeStatBuffs(u32 battler, s8 statValue, enum Stat statId, union St
}
else if (battlerAbility == ABILITY_MIRROR_ARMOR && !flags.mirrorArmored && gBattlerAttacker != gBattlerTarget && battler == gBattlerTarget)
{
if (GetMoveEffect(gCurrentMove) == EFFECT_PARTING_SHOT)
gBattleScripting.animTargetsHit = 1;
if (flags.allowPtr)
{
SET_STATCHANGER(statId, GET_STAT_BUFF_VALUE(statValue) | STAT_BUFF_NEGATIVE, TRUE);
@ -18081,3 +18104,43 @@ void BS_TryAbsorbToxicSpikesOnFaint(void)
gBattlescriptCurrInstr = cmd->nextInstr;
}
// Updates Dynamax HP multipliers and healthboxes.
void BS_UpdateDynamax(void)
{
NATIVE_ARGS();
u32 battler = gBattleScripting.battler;
struct Pokemon *mon = GetBattlerMon(battler);
if (!IsGigantamaxed(battler)) // RecalcBattlerStats will get called on form change.
RecalcBattlerStats(battler, mon, GetActiveGimmick(battler) == GIMMICK_DYNAMAX);
UpdateHealthboxAttribute(gHealthboxSpriteIds[battler], mon, HEALTHBOX_ALL);
gBattlescriptCurrInstr = cmd->nextInstr;
}
// Goes to the jump instruction if the target is Dynamaxed.
void BS_JumpIfDynamaxed(void)
{
NATIVE_ARGS(const u8 *jumpInstr);
if ((GetActiveGimmick(gBattlerTarget) == GIMMICK_DYNAMAX))
gBattlescriptCurrInstr = cmd->jumpInstr;
else
gBattlescriptCurrInstr = cmd->nextInstr;
}
void BS_UndoDynamax(void)
{
NATIVE_ARGS(u8 battler);
u32 battler = GetBattlerForBattleScript(cmd->battler);
if (GetActiveGimmick(battler) == GIMMICK_DYNAMAX)
{
UndoDynamax(battler);
gBattleScripting.battler = battler;
BattleScriptCall(BattleScript_DynamaxEnds_Ret);
return;
}
gBattlescriptCurrInstr = cmd->nextInstr;
}

View File

@ -6635,6 +6635,26 @@ static inline bool32 IsSideProtected(u32 battler, enum ProtectMethod method)
|| gProtectStructs[BATTLE_PARTNER(battler)].protected == method;
}
static bool32 IsCraftyShieldProtected(u32 battlerAtk, u32 battlerDef, u32 move)
{
if (!IsBattleMoveStatus(move))
return FALSE;
if (!IsSideProtected(battlerDef, PROTECT_CRAFTY_SHIELD))
return FALSE;
if (GetMoveEffect(move) == EFFECT_HOLD_HANDS)
return TRUE;
u32 moveTarget = GetBattlerMoveTargetType(battlerAtk, move);
if (!IsBattlerAlly(battlerAtk, battlerDef)
&& moveTarget != MOVE_TARGET_OPPONENTS_FIELD
&& moveTarget != MOVE_TARGET_ALL_BATTLERS)
return TRUE;
return FALSE;
}
bool32 IsBattlerProtected(u32 battlerAtk, u32 battlerDef, u32 move)
{
if (gProtectStructs[battlerDef].protected == PROTECT_NONE
@ -6652,9 +6672,7 @@ bool32 IsBattlerProtected(u32 battlerAtk, u32 battlerDef, u32 move)
bool32 isProtected = FALSE;
if (IsSideProtected(battlerDef, PROTECT_CRAFTY_SHIELD)
&& IsBattleMoveStatus(move)
&& GetMoveEffect(move) != EFFECT_COACHING)
if (IsCraftyShieldProtected(battlerAtk, battlerDef, move))
isProtected = TRUE;
else if (MoveIgnoresProtect(move))
isProtected = FALSE;

View File

@ -8,15 +8,15 @@ const u16 gBattleEnvironmentPalette_Building[] = INCBIN_U16("graphics/battle_env
const u16 gBattleEnvironmentPalette_Kyogre[] = INCBIN_U16("graphics/battle_environment/water/kyogre.gbapal");
const u16 gBattleEnvironmentPalette_Groudon[] = INCBIN_U16("graphics/battle_environment/cave/groudon.gbapal");
const u16 gBattleEnvironmentPalette_BuildingGym[] = INCBIN_U16("graphics/battle_environment/building/palette2.gbapal");
const u16 gBattleEnvironmentPalette_BuildingLeader[] = INCBIN_U16("graphics/battle_environment/building/palette3.gbapal");
const u16 gBattleEnvironmentPalette_StadiumAqua[] = INCBIN_U16("graphics/battle_environment/stadium/palette1.gbapal");
const u16 gBattleEnvironmentPalette_StadiumMagma[] = INCBIN_U16("graphics/battle_environment/stadium/palette2.gbapal");
const u16 gBattleEnvironmentPalette_StadiumSidney[] = INCBIN_U16("graphics/battle_environment/stadium/palette3.gbapal");
const u16 gBattleEnvironmentPalette_StadiumPhoebe[] = INCBIN_U16("graphics/battle_environment/stadium/palette4.gbapal");
const u16 gBattleEnvironmentPalette_StadiumGlacia[] = INCBIN_U16("graphics/battle_environment/stadium/palette5.gbapal");
const u16 gBattleEnvironmentPalette_StadiumDrake[] = INCBIN_U16("graphics/battle_environment/stadium/palette6.gbapal");
const u16 gBattleEnvironmentPalette_StadiumWallace[] = INCBIN_U16("graphics/battle_environment/stadium/palette7.gbapal");
const u16 gBattleEnvironmentPalette_BuildingGym[] = INCBIN_U16("graphics/battle_environment/building/gym.gbapal");
const u16 gBattleEnvironmentPalette_BuildingLeader[] = INCBIN_U16("graphics/battle_environment/building/leader.gbapal");
const u16 gBattleEnvironmentPalette_StadiumAqua[] = INCBIN_U16("graphics/battle_environment/stadium/aqua.gbapal");
const u16 gBattleEnvironmentPalette_StadiumMagma[] = INCBIN_U16("graphics/battle_environment/stadium/magma.gbapal");
const u16 gBattleEnvironmentPalette_StadiumSidney[] = INCBIN_U16("graphics/battle_environment/stadium/sidney.gbapal");
const u16 gBattleEnvironmentPalette_StadiumPhoebe[] = INCBIN_U16("graphics/battle_environment/stadium/phoebe.gbapal");
const u16 gBattleEnvironmentPalette_StadiumGlacia[] = INCBIN_U16("graphics/battle_environment/stadium/glacia.gbapal");
const u16 gBattleEnvironmentPalette_StadiumDrake[] = INCBIN_U16("graphics/battle_environment/stadium/drake.gbapal");
const u16 gBattleEnvironmentPalette_StadiumWallace[] = INCBIN_U16("graphics/battle_environment/stadium/wallace.gbapal");
const u16 gBattleEnvironmentPalette_Rayquaza[] = INCBIN_U16("graphics/battle_environment/sky/palette.gbapal");
const u32 gBattleEnvironmentAnimTiles_TallGrass[] = INCBIN_U32("graphics/battle_environment/tall_grass/anim_tiles.4bpp.smol");

View File

@ -21,7 +21,7 @@ const u16 gItemIconPalette_UltraBall[] = INCBIN_U16("graphics/items/icon_palette
const u32 gItemIcon_MasterBall[] = INCBIN_U32("graphics/items/icons/master_ball.4bpp.smol");
const u16 gItemIconPalette_MasterBall[] = INCBIN_U16("graphics/items/icon_palettes/master_ball.gbapal");
const u32 gItemIcon_PremierBall[] = INCBIN_U16("graphics/items/icons/premier_ball.4bpp.smol");
const u32 gItemIcon_PremierBall[] = INCBIN_U32("graphics/items/icons/premier_ball.4bpp.smol");
const u16 gItemIconPalette_PremierBall[] = INCBIN_U16("graphics/items/icon_palettes/premier_ball.gbapal");
const u32 gItemIcon_HealBall[] = INCBIN_U32("graphics/items/icons/heal_ball.4bpp.smol");

View File

@ -19386,7 +19386,7 @@ const u32 gObjectEventPic_Substitute[] = INCBIN_COMP("graphics/pokemon/question_
const u16 gMonPalette_VivillonIcySnow[] = INCBIN_U16("graphics/pokemon/vivillon/normal.gbapal");
const u32 gMonBackPic_VivillonIcySnow[] = INCBIN_U32("graphics/pokemon/vivillon/back.4bpp.smol");
const u16 gMonShinyPalette_VivillonIcySnow[] = INCBIN_U16("graphics/pokemon/vivillon/shiny.gbapal");
const u8 gMonIcon_VivillonIcySnow[] = INCBIN_U8("graphics/pokemon/vivillon/meadow/icon.4bpp");
const u8 gMonIcon_VivillonIcySnow[] = INCBIN_U8("graphics/pokemon/vivillon/icon.4bpp");
#if P_FOOTPRINTS
const u8 gMonFootprint_Vivillon[] = INCBIN_U8("graphics/pokemon/vivillon/footprint.1bpp");
#endif //P_FOOTPRINTS

View File

@ -15818,7 +15818,9 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] =
.metronomeBanned = TRUE,
.sketchBanned = (B_SKETCH_BANS >= GEN_9),
.additionalEffects = ADDITIONAL_EFFECTS({
// Feint move effect handled in script as it goes before animation
.moveEffect = MOVE_EFFECT_FEINT, // TODO: Is this supposed to happen before the attack animation?
},
{
.moveEffect = MOVE_EFFECT_DEF_MINUS_1,
.self = TRUE,
}),

View File

@ -277,7 +277,11 @@ static const u8 sRSAvatarGfxIds[GENDER_COUNT] =
[FEMALE] = OBJ_EVENT_GFX_LINK_RS_MAY
};
static const u8 sPlayerAvatarGfxToStateFlag[GENDER_COUNT][5][2] =
static const struct __attribute__((packed))
{
u8 graphicsId;
u8 playerFlag;
} sPlayerAvatarGfxToStateFlag[GENDER_COUNT][5] =
{
[MALE] =
{
@ -1577,8 +1581,8 @@ static u8 GetPlayerAvatarStateTransitionByGraphicsId(u16 graphicsId, u8 gender)
for (i = 0; i < ARRAY_COUNT(sPlayerAvatarGfxToStateFlag[0]); i++)
{
if (sPlayerAvatarGfxToStateFlag[gender][i][0] == graphicsId)
return sPlayerAvatarGfxToStateFlag[gender][i][1];
if (sPlayerAvatarGfxToStateFlag[gender][i].graphicsId == graphicsId)
return sPlayerAvatarGfxToStateFlag[gender][i].playerFlag;
}
return PLAYER_AVATAR_FLAG_ON_FOOT;
}
@ -1590,8 +1594,8 @@ u16 GetPlayerAvatarGraphicsIdByCurrentState(void)
for (i = 0; i < ARRAY_COUNT(sPlayerAvatarGfxToStateFlag[0]); i++)
{
if (sPlayerAvatarGfxToStateFlag[gPlayerAvatar.gender][i][1] & flags)
return sPlayerAvatarGfxToStateFlag[gPlayerAvatar.gender][i][0];
if (sPlayerAvatarGfxToStateFlag[gPlayerAvatar.gender][i].playerFlag & flags)
return sPlayerAvatarGfxToStateFlag[gPlayerAvatar.gender][i].graphicsId;
}
return 0;
}

View File

@ -2612,8 +2612,6 @@ void CreateFrontierBrainPokemon(void)
friendship = 0;
}
SetMonData(&gEnemyParty[monPartyId], MON_DATA_FRIENDSHIP, &friendship);
j = FALSE;
SetMonData(&gPlayerParty[MULTI_PARTY_SIZE + i], MON_DATA_IS_SHINY, &j);
CalculateMonStats(&gEnemyParty[monPartyId]);
monPartyId++;
}

View File

@ -222,11 +222,11 @@ static void Task_CloseCantUseKeyItemMessage(u8 taskId)
u8 CheckIfItemIsTMHMOrEvolutionStone(u16 itemId)
{
if (GetItemFieldFunc(itemId) == ItemUseOutOfBattle_TMHM)
return 1;
return ITEM_IS_TM_HM;
else if (GetItemFieldFunc(itemId) == ItemUseOutOfBattle_EvolutionStone)
return 2;
return ITEM_IS_EVOLUTION_STONE;
else
return 0;
return ITEM_IS_OTHER;
}
// Mail in the bag menu can't have a message but it can be checked (view the mail background, no message)

View File

@ -2620,5 +2620,3 @@ static const struct SpritePalette sSpritePalettes[] =
{gNamingScreenMenu_Pal[4], PALTAG_OK_BUTTON},
{}
};

View File

@ -1152,10 +1152,10 @@ static bool8 DisplayPartyPokemonDataForMoveTutorOrEvolutionItem(u8 slot)
{
default:
return FALSE;
case 1: // TM/HM
case ITEM_IS_TM_HM: // TM/HM
DisplayPartyPokemonDataToTeachMove(slot, ItemIdToBattleMoveId(item));
break;
case 2: // Evolution stone
case ITEM_IS_EVOLUTION_STONE: // Evolution stone
if (!GetMonData(currentPokemon, MON_DATA_IS_EGG) && GetEvolutionTargetSpecies(currentPokemon, EVO_MODE_ITEM_CHECK, item, NULL, NULL, CHECK_EVO) != SPECIES_NONE)
return FALSE;
DisplayPartyPokemonDescriptionData(slot, PARTYBOX_DESC_NO_USE);

View File

@ -566,4 +566,3 @@ static s32 GetTypeIconBounceMovement(s32 originalY, u32 position)
struct Sprite *healthbox = &gSprites[gHealthboxSpriteIds[GetBattlerAtPosition(position)]];
return originalY + healthbox->y2;
}

View File

@ -780,27 +780,33 @@ AI_DOUBLE_BATTLE_TEST("AI does not use Helping Hand on Good as Gold ally")
}
}
AI_DOUBLE_BATTLE_TEST("AI uses Tailwind")
AI_DOUBLE_BATTLE_TEST("AI uses Tailwind based on speed matchups")
{
u32 speed1, speed2, speed3, speed4;
bool32 expectTailwind;
PARAMETRIZE { speed1 = 20; speed2 = 20; speed3 = 20; speed4 = 20; }
PARAMETRIZE { speed1 = 20; speed2 = 20; speed3 = 5; speed4 = 5; }
PARAMETRIZE { speed1 = 20; speed2 = 20; speed3 = 15; speed4 = 15; }
PARAMETRIZE { speed1 = 1; speed2 = 1; speed3 = 5; speed4 = 5; }
PARAMETRIZE { speed1 = 1; speed2 = 20; speed3 = 15; speed4 = 15; }
PARAMETRIZE { speed1 = 1; speed2 = 20; speed3 = 20; speed4 = 15; }
// All four comparisons qualify -> tailwindScore = 5
PARAMETRIZE { speed1 = 20; speed2 = 20; speed3 = 20; speed4 = 20; expectTailwind = TRUE; }
// Only the attacker flips one foe matchup -> tailwindScore = 2
PARAMETRIZE { speed1 = 20; speed2 = 40; speed3 = 20; speed4 = 50; expectTailwind = TRUE; }
// Only the partner flips one foe matchup -> tailwindScore = 2
PARAMETRIZE { speed1 = 10; speed2 = 29; speed3 = 50; speed4 = 15; expectTailwind = TRUE; }
// Too slow: even after doubling, still slower than both foes -> tailwindScore = 0.
PARAMETRIZE { speed1 = 40; speed2 = 40; speed3 = 10; speed4 = 10; expectTailwind = FALSE; }
// Already faster: Tailwind doesn't improve matchups -> tailwindScore = 0.
PARAMETRIZE { speed1 = 5; speed2 = 5; speed3 = 10; speed4 = 10; expectTailwind = FALSE; }
// Boundary: speed*2 == foe speed does not count -> tailwindScore = 0.
PARAMETRIZE { speed1 = 20; speed2 = 20; speed3 = 10; speed4 = 30; expectTailwind = FALSE; }
GIVEN {
ASSUME(GetMoveEffect(MOVE_AFTER_YOU) == EFFECT_AFTER_YOU);
ASSUME(GetMoveEffect(MOVE_TRICK_ROOM) == EFFECT_TRICK_ROOM);
ASSUME(GetMoveEffect(MOVE_TAILWIND) == EFFECT_TAILWIND);
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY | AI_FLAG_DOUBLE_BATTLE);
PLAYER(SPECIES_WOBBUFFET) { Speed(speed1); }
PLAYER(SPECIES_WOBBUFFET) { Speed(speed2); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(speed3); Moves(MOVE_TAILWIND, MOVE_HEADBUTT); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(speed4); Moves(MOVE_TAILWIND, MOVE_HEADBUTT); }
} WHEN {
if (speed3 > 10)
if (expectTailwind)
TURN { EXPECT_MOVE(opponentLeft, MOVE_TAILWIND); }
else
TURN { NOT_EXPECT_MOVE(opponentLeft, MOVE_TAILWIND); }
@ -874,3 +880,29 @@ AI_DOUBLE_BATTLE_TEST("AI prefers to Fake Out the opponent vulnerable to flinchi
TURN { EXPECT_MOVE(opponentLeft, MOVE_FAKE_OUT, target:playerRight); }
}
}
AI_DOUBLE_BATTLE_TEST("AI uses Gear Up")
{
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT);
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_POUND, MOVE_CELEBRATE); }
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_POUND, MOVE_CELEBRATE); }
OPPONENT(SPECIES_KLINKLANG) { Ability(ABILITY_PLUS); Moves(MOVE_GEAR_UP, MOVE_WATER_GUN, MOVE_POUND); }
OPPONENT(SPECIES_KLINKLANG) { Ability(ABILITY_PLUS); Moves(MOVE_GEAR_UP, MOVE_WATER_GUN, MOVE_POUND); }
} WHEN {
TURN { EXPECT_MOVE(opponentLeft, MOVE_GEAR_UP); }
}
}
AI_DOUBLE_BATTLE_TEST("AI uses Magnetic Flux")
{
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT);
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_POUND, MOVE_CELEBRATE); }
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_POUND, MOVE_CELEBRATE); }
OPPONENT(SPECIES_KLINK) { Ability(ABILITY_PLUS); Moves(MOVE_MAGNETIC_FLUX, MOVE_POUND); }
OPPONENT(SPECIES_KLINK) { Ability(ABILITY_PLUS); Moves(MOVE_MAGNETIC_FLUX, MOVE_POUND); }
} WHEN {
TURN { EXPECT_MOVE(opponentLeft, MOVE_MAGNETIC_FLUX); }
}
}

View File

@ -87,7 +87,6 @@ SINGLE_BATTLE_TEST("Beak Blast burns only when contact moves are used")
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, move); MOVE(player, MOVE_BEAK_BLAST); }
TURN {}
} SCENE {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_BEAK_BLAST_SETUP, player);
MESSAGE("Wobbuffet started heating up its beak!");
@ -112,6 +111,35 @@ SINGLE_BATTLE_TEST("Beak Blast burns only when contact moves are used")
}
}
SINGLE_BATTLE_TEST("Beak Blast doesn't burn when charging a two turn move")
{
u32 move;
PARAMETRIZE { move = MOVE_BOUNCE; }
PARAMETRIZE { move = MOVE_DIG; }
GIVEN {
ASSUME(MoveMakesContact(MOVE_BOUNCE));
ASSUME(MoveMakesContact(MOVE_DIG));
ASSUME(gBattleMoveEffects[GetMoveEffect(MOVE_BOUNCE)].twoTurnEffect);
ASSUME(gBattleMoveEffects[GetMoveEffect(MOVE_DIG)].twoTurnEffect);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, move); MOVE(player, MOVE_BEAK_BLAST); }
} SCENE {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_BEAK_BLAST_SETUP, player);
MESSAGE("Wobbuffet started heating up its beak!");
ANIMATION(ANIM_TYPE_MOVE, move, opponent);
NONE_OF {
HP_BAR(player);
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_BRN, opponent);
MESSAGE("The opposing Wobbuffet was burned!");
STATUS_ICON(opponent, burn: TRUE);
}
}
}
SINGLE_BATTLE_TEST("Beak Blast doesn't burn fire types")
{
GIVEN {

View File

@ -1,14 +1,123 @@
#include "global.h"
#include "test/battle.h"
TO_DO_BATTLE_TEST("Foresight removes Ghost's type immunity to Normal and Fighting types")
ASSUMPTIONS
{
ASSUME(GetMoveEffect(MOVE_FORESIGHT) == EFFECT_FORESIGHT);
}
SINGLE_BATTLE_TEST("Foresight removes Ghost's type immunity to Normal and Fighting types")
{
GIVEN {
ASSUME(GetMoveType(MOVE_SCRATCH) == TYPE_NORMAL);
ASSUME(GetMoveType(MOVE_LOW_KICK) == TYPE_FIGHTING);
ASSUME(GetSpeciesType(SPECIES_GENGAR, 0) == TYPE_GHOST);
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_FORESIGHT, MOVE_SCRATCH, MOVE_LOW_KICK); }
OPPONENT(SPECIES_GENGAR) { Moves(MOVE_SPLASH); }
} WHEN {
TURN { MOVE(player, MOVE_FORESIGHT); MOVE(opponent, MOVE_SPLASH); }
TURN { MOVE(player, MOVE_SCRATCH); MOVE(opponent, MOVE_SPLASH); }
TURN { MOVE(player, MOVE_LOW_KICK); MOVE(opponent, MOVE_SPLASH); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_FORESIGHT, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, player);
HP_BAR(opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_LOW_KICK, player);
HP_BAR(opponent);
}
}
SINGLE_BATTLE_TEST("Foresight always hits unless the target is semi-invulnerable")
{
bool32 semiInvulnerable = FALSE;
PARAMETRIZE { semiInvulnerable = FALSE; }
PARAMETRIZE { semiInvulnerable = TRUE; }
GIVEN {
ASSUME(GetMoveEffect(MOVE_DOUBLE_TEAM) == EFFECT_EVASION_UP);
ASSUME(GetMoveEffect(MOVE_FLY) == EFFECT_SEMI_INVULNERABLE);
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_FORESIGHT, MOVE_SPLASH); Speed(10); }
OPPONENT(SPECIES_SQUAWKABILLY) { Moves(MOVE_DOUBLE_TEAM, MOVE_FLY); Speed(20); }
} WHEN {
if (semiInvulnerable)
TURN { MOVE(player, MOVE_FORESIGHT); MOVE(opponent, MOVE_FLY); }
else
TURN { MOVE(player, MOVE_FORESIGHT); MOVE(opponent, MOVE_DOUBLE_TEAM); }
if (semiInvulnerable)
TURN { MOVE(player, MOVE_SPLASH); SKIP_TURN(opponent); }
} SCENE {
if (semiInvulnerable) {
MESSAGE("The opposing Squawkabilly avoided the attack!");
} else {
ANIMATION(ANIM_TYPE_MOVE, MOVE_DOUBLE_TEAM, opponent);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_FORESIGHT, player);
}
}
}
SINGLE_BATTLE_TEST("Foresight causes moves against the target to ignore positive evasion stat stages")
{
PASSES_RANDOMLY(100, 100, RNG_ACCURACY);
GIVEN {
ASSUME(GetMoveEffect(MOVE_DOUBLE_TEAM) == EFFECT_EVASION_UP);
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_FORESIGHT, MOVE_SCRATCH); Speed(10); }
OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_DOUBLE_TEAM, MOVE_SPLASH); Speed(20); }
} WHEN {
TURN { MOVE(player, MOVE_FORESIGHT); MOVE(opponent, MOVE_DOUBLE_TEAM); }
TURN { MOVE(player, MOVE_SCRATCH); MOVE(opponent, MOVE_SPLASH); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_DOUBLE_TEAM, opponent);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_FORESIGHT, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, player);
HP_BAR(opponent);
}
}
SINGLE_BATTLE_TEST("Foresight fails if the target is already under its effect (Gen 2 and Gen5+)")
{
u32 genConfig = GEN_2;
PARAMETRIZE { genConfig = GEN_2; }
PARAMETRIZE { genConfig = GEN_5; }
GIVEN {
WITH_CONFIG(CONFIG_FORESIGHT_FAIL, genConfig);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_FORESIGHT); }
TURN { MOVE(player, MOVE_FORESIGHT); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_FORESIGHT, player);
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_FORESIGHT, player);
MESSAGE("But it failed!");
}
}
SINGLE_BATTLE_TEST("Foresight doesn't fail if the target is already under its effect (Gen 3-4)")
{
u32 genConfig = GEN_3;
PARAMETRIZE { genConfig = GEN_3; }
PARAMETRIZE { genConfig = GEN_4; }
GIVEN {
WITH_CONFIG(CONFIG_FORESIGHT_FAIL, genConfig);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_FORESIGHT); }
TURN { MOVE(player, MOVE_FORESIGHT); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_FORESIGHT, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_FORESIGHT, player);
NOT MESSAGE("But it failed!");
}
}
TO_DO_BATTLE_TEST("Foresight causes accuracy/evasion stat changes only between the user/target when the user's accuracy stage is less than the target's evasion stage (Gen 2)")
TO_DO_BATTLE_TEST("Foresight causes all moves against the target to ignore evasion stat changes (Gen 3)")
TO_DO_BATTLE_TEST("Foresight causes all moves against the target to ignore only positive evasion stat changes (Gen 4+)") // Eg. Doesn't ignore Sweet Scent
TO_DO_BATTLE_TEST("Foresight doesn't cause moves used against the target to always hit (Gen 2-3)")
TO_DO_BATTLE_TEST("Foresight causes moves used against the target to always hit (Gen 4+)")
TO_DO_BATTLE_TEST("Foresight does not make moves hit semi-invulnerable targets")
TO_DO_BATTLE_TEST("Foresight fails if the target is already under its effect (Gen 2 and Gen5+)")
TO_DO_BATTLE_TEST("Foresight doesn't fail if the target is already under its effect (Gen 3-4)")
TO_DO_BATTLE_TEST("Baton Pass passes Foresight's effect (Gen 2)");
TO_DO_BATTLE_TEST("Baton Pass doesn't pass Foresight's effect (Gen 3+)");

View File

@ -1,17 +1,47 @@
#include "global.h"
#include "test/battle.h"
TO_DO_BATTLE_TEST("Gear Up increases the Attack and Sp. Attack of the user and allies if they have Plus or Minus")
ASSUMPTIONS
{
ASSUME(GetMoveEffect(MOVE_GEAR_UP) == EFFECT_GEAR_UP);
}
AI_DOUBLE_BATTLE_TEST("AI uses Gear Up")
SINGLE_BATTLE_TEST("Gear Up raises Attack and Sp. Attack of the user with Plus/Minus in singles")
{
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT);
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_POUND, MOVE_CELEBRATE); }
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_POUND, MOVE_CELEBRATE); }
OPPONENT(SPECIES_KLINKLANG) { Ability(ABILITY_PLUS); Moves(MOVE_GEAR_UP, MOVE_WATER_GUN, MOVE_POUND); }
OPPONENT(SPECIES_KLINKLANG) { Ability(ABILITY_PLUS); Moves(MOVE_GEAR_UP, MOVE_WATER_GUN, MOVE_POUND); }
PLAYER(SPECIES_PLUSLE) { Ability(ABILITY_PLUS); }
OPPONENT(SPECIES_MINUN) { Ability(ABILITY_MINUS); }
} WHEN {
TURN { EXPECT_MOVE(opponentLeft, MOVE_GEAR_UP); }
TURN { MOVE(player, MOVE_GEAR_UP); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_GEAR_UP, player);
} 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);
}
}
DOUBLE_BATTLE_TEST("Gear Up raises Attack and Sp. Attack of all Plus/Minus allies in doubles")
{
GIVEN {
PLAYER(SPECIES_PLUSLE) { Ability(ABILITY_PLUS); }
PLAYER(SPECIES_MINUN) { Ability(ABILITY_MINUS); }
OPPONENT(SPECIES_PLUSLE) { Ability(ABILITY_PLUS); }
OPPONENT(SPECIES_MINUN) { Ability(ABILITY_MINUS); }
} WHEN {
TURN { MOVE(playerLeft, MOVE_GEAR_UP); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_GEAR_UP, playerLeft);
} THEN {
EXPECT_EQ(playerLeft->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 1);
EXPECT_EQ(playerLeft->statStages[STAT_SPATK], DEFAULT_STAT_STAGE + 1);
EXPECT_EQ(playerRight->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 1);
EXPECT_EQ(playerRight->statStages[STAT_SPATK], DEFAULT_STAT_STAGE + 1);
EXPECT_EQ(opponentLeft->statStages[STAT_ATK], DEFAULT_STAT_STAGE);
EXPECT_EQ(opponentLeft->statStages[STAT_SPATK], DEFAULT_STAT_STAGE);
EXPECT_EQ(opponentRight->statStages[STAT_ATK], DEFAULT_STAT_STAGE);
EXPECT_EQ(opponentRight->statStages[STAT_SPATK], DEFAULT_STAT_STAGE);
}
}

View File

@ -1,4 +0,0 @@
#include "global.h"
#include "test/battle.h"
TO_DO_BATTLE_TEST("TODO: Write Hyperspace Fury (Move Effect) test titles")

View File

@ -1,17 +1,47 @@
#include "global.h"
#include "test/battle.h"
TO_DO_BATTLE_TEST("TODO: Write Magnetic Flux (Move Effect) test titles")
ASSUMPTIONS
{
ASSUME(GetMoveEffect(MOVE_MAGNETIC_FLUX) == EFFECT_MAGNETIC_FLUX);
}
AI_DOUBLE_BATTLE_TEST("AI uses Magnetic Flux")
SINGLE_BATTLE_TEST("Magnetic Flux raises Defense and Sp. Defense of the user with Plus/Minus in singles")
{
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT);
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_POUND, MOVE_CELEBRATE); }
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_POUND, MOVE_CELEBRATE); }
OPPONENT(SPECIES_KLINK) { Ability(ABILITY_PLUS); Moves(MOVE_MAGNETIC_FLUX, MOVE_POUND); }
OPPONENT(SPECIES_KLINK) { Ability(ABILITY_PLUS); Moves(MOVE_MAGNETIC_FLUX, MOVE_POUND); }
PLAYER(SPECIES_PLUSLE) { Ability(ABILITY_PLUS); }
OPPONENT(SPECIES_MINUN) { Ability(ABILITY_MINUS); }
} WHEN {
TURN { EXPECT_MOVE(opponentLeft, MOVE_MAGNETIC_FLUX); }
TURN { MOVE(player, MOVE_MAGNETIC_FLUX); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_MAGNETIC_FLUX, player);
} THEN {
EXPECT_EQ(player->statStages[STAT_DEF], DEFAULT_STAT_STAGE + 1);
EXPECT_EQ(player->statStages[STAT_SPDEF], DEFAULT_STAT_STAGE + 1);
EXPECT_EQ(opponent->statStages[STAT_DEF], DEFAULT_STAT_STAGE);
EXPECT_EQ(opponent->statStages[STAT_SPDEF], DEFAULT_STAT_STAGE);
}
}
DOUBLE_BATTLE_TEST("Magnetic Flux raises Defense and Sp. Defense of all Plus/Minus allies in doubles")
{
GIVEN {
PLAYER(SPECIES_PLUSLE) { Ability(ABILITY_PLUS); }
PLAYER(SPECIES_MINUN) { Ability(ABILITY_MINUS); }
OPPONENT(SPECIES_PLUSLE) { Ability(ABILITY_PLUS); }
OPPONENT(SPECIES_MINUN) { Ability(ABILITY_MINUS); }
} WHEN {
TURN { MOVE(playerLeft, MOVE_MAGNETIC_FLUX); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_MAGNETIC_FLUX, playerLeft);
} THEN {
EXPECT_EQ(playerLeft->statStages[STAT_DEF], DEFAULT_STAT_STAGE + 1);
EXPECT_EQ(playerLeft->statStages[STAT_SPDEF], DEFAULT_STAT_STAGE + 1);
EXPECT_EQ(playerRight->statStages[STAT_DEF], DEFAULT_STAT_STAGE + 1);
EXPECT_EQ(playerRight->statStages[STAT_SPDEF], DEFAULT_STAT_STAGE + 1);
EXPECT_EQ(opponentLeft->statStages[STAT_DEF], DEFAULT_STAT_STAGE);
EXPECT_EQ(opponentLeft->statStages[STAT_SPDEF], DEFAULT_STAT_STAGE);
EXPECT_EQ(opponentRight->statStages[STAT_DEF], DEFAULT_STAT_STAGE);
EXPECT_EQ(opponentRight->statStages[STAT_SPDEF], DEFAULT_STAT_STAGE);
}
}

View File

@ -1,4 +1,106 @@
#include "global.h"
#include "test/battle.h"
TO_DO_BATTLE_TEST("TODO: Write Miracle Eye (Move Effect) test titles")
ASSUMPTIONS
{
ASSUME(GetMoveEffect(MOVE_MIRACLE_EYE) == EFFECT_MIRACLE_EYE);
}
SINGLE_BATTLE_TEST("Miracle Eye removes Dark-type immunity to Psychic-type moves")
{
GIVEN {
ASSUME(GetMoveType(MOVE_PSYCHIC) == TYPE_PSYCHIC);
ASSUME(GetSpeciesType(SPECIES_UMBREON, 0) == TYPE_DARK);
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_MIRACLE_EYE, MOVE_PSYCHIC); }
OPPONENT(SPECIES_UMBREON) { Moves(MOVE_SPLASH); }
} WHEN {
TURN { MOVE(player, MOVE_PSYCHIC); MOVE(opponent, MOVE_SPLASH); }
TURN { MOVE(player, MOVE_MIRACLE_EYE); MOVE(opponent, MOVE_SPLASH); }
TURN { MOVE(player, MOVE_PSYCHIC); MOVE(opponent, MOVE_SPLASH); }
} SCENE {
NOT HP_BAR(opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_MIRACLE_EYE, player);
HP_BAR(opponent);
}
}
SINGLE_BATTLE_TEST("Miracle Eye always hits unless the target is semi-invulnerable")
{
bool32 semiInvulnerable = FALSE;
PARAMETRIZE { semiInvulnerable = FALSE; }
PARAMETRIZE { semiInvulnerable = TRUE; }
GIVEN {
ASSUME(GetMoveEffect(MOVE_DOUBLE_TEAM) == EFFECT_EVASION_UP);
ASSUME(GetMoveEffect(MOVE_FLY) == EFFECT_SEMI_INVULNERABLE);
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_MIRACLE_EYE, MOVE_SPLASH); Speed(10); }
OPPONENT(SPECIES_SQUAWKABILLY) { Moves(MOVE_DOUBLE_TEAM, MOVE_FLY); Speed(20); }
} WHEN {
if (semiInvulnerable)
TURN { MOVE(player, MOVE_MIRACLE_EYE); MOVE(opponent, MOVE_FLY); }
else
TURN { MOVE(player, MOVE_MIRACLE_EYE); MOVE(opponent, MOVE_DOUBLE_TEAM); }
if (semiInvulnerable)
TURN { MOVE(player, MOVE_SPLASH); SKIP_TURN(opponent); }
} SCENE {
if (semiInvulnerable) {
MESSAGE("Wobbuffet's attack missed!");
} else {
ANIMATION(ANIM_TYPE_MOVE, MOVE_DOUBLE_TEAM, opponent);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_MIRACLE_EYE, player);
}
}
}
SINGLE_BATTLE_TEST("Miracle Eye causes moves against the target to ignore positive evasion stat stages")
{
PASSES_RANDOMLY(100, 100, RNG_ACCURACY);
GIVEN {
ASSUME(GetMoveEffect(MOVE_DOUBLE_TEAM) == EFFECT_EVASION_UP);
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_MIRACLE_EYE, MOVE_SCRATCH); Speed(10); }
OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_DOUBLE_TEAM, MOVE_SPLASH); Speed(20); }
} WHEN {
TURN { MOVE(player, MOVE_MIRACLE_EYE); MOVE(opponent, MOVE_DOUBLE_TEAM); }
TURN { MOVE(player, MOVE_SCRATCH); MOVE(opponent, MOVE_SPLASH); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_DOUBLE_TEAM, opponent);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_MIRACLE_EYE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, player);
HP_BAR(opponent);
}
}
SINGLE_BATTLE_TEST("Miracle Eye fails if the target is already affected by Miracle Eye (Gen5+)")
{
GIVEN {
WITH_CONFIG(CONFIG_MIRACLE_EYE_FAIL, GEN_5);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_MIRACLE_EYE); }
TURN { MOVE(player, MOVE_MIRACLE_EYE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_MIRACLE_EYE, player);
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_MIRACLE_EYE, player);
MESSAGE("But it failed!");
}
}
SINGLE_BATTLE_TEST("Miracle Eye does not fail if the target is already affected by Miracle Eye (Gen4)")
{
GIVEN {
WITH_CONFIG(CONFIG_MIRACLE_EYE_FAIL, GEN_4);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_MIRACLE_EYE); }
TURN { MOVE(player, MOVE_MIRACLE_EYE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_MIRACLE_EYE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_MIRACLE_EYE, player);
NOT MESSAGE("But it failed!");
}
}

View File

@ -1,4 +1,385 @@
#include "global.h"
#include "test/battle.h"
TO_DO_BATTLE_TEST("TODO: Write Parting Shot (Move Effect) test titles")
ASSUMPTIONS
{
ASSUME(GetMoveEffect(MOVE_PARTING_SHOT) == EFFECT_PARTING_SHOT);
}
SINGLE_BATTLE_TEST("Parting Shot: Passes Substitute and switches the user out")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_PARTING_SHOT, MOVE_CELEBRATE); }
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_SUBSTITUTE, MOVE_CELEBRATE); }
} WHEN {
TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_SUBSTITUTE); }
TURN { MOVE(player, MOVE_PARTING_SHOT); MOVE(opponent, MOVE_CELEBRATE); SEND_OUT(player, 1); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_PARTING_SHOT, player);
SEND_IN_MESSAGE("Wynaut");
} THEN {
EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE - 1);
EXPECT_EQ(opponent->statStages[STAT_SPATK], DEFAULT_STAT_STAGE - 1);
EXPECT_EQ(player->species, SPECIES_WYNAUT);
}
}
SINGLE_BATTLE_TEST("Parting Shot: Soundproof and Good as Gold block Parting Shot")
{
u16 species, ability;
PARAMETRIZE { species = SPECIES_EXPLOUD; ability = ABILITY_SOUNDPROOF; }
PARAMETRIZE { species = SPECIES_GHOLDENGO; ability = ABILITY_GOOD_AS_GOLD; }
GIVEN {
ASSUME(IsSoundMove(MOVE_PARTING_SHOT));
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_PARTING_SHOT); }
PLAYER(SPECIES_WYNAUT);
OPPONENT(species) { Ability(ability); Moves(MOVE_CELEBRATE); }
} WHEN {
TURN { MOVE(player, MOVE_PARTING_SHOT); MOVE(opponent, MOVE_CELEBRATE); }
} SCENE {
ABILITY_POPUP(opponent, ability);
if (ability == ABILITY_SOUNDPROOF)
MESSAGE("The opposing Exploud's Soundproof blocks Parting Shot!");
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_PARTING_SHOT, player);
} THEN {
EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE);
EXPECT_EQ(opponent->statStages[STAT_SPATK], DEFAULT_STAT_STAGE);
EXPECT_EQ(player->species, SPECIES_WOBBUFFET);
}
}
SINGLE_BATTLE_TEST("Parting Shot: Hyper Cutter blocks Attack drop but still switches")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_PARTING_SHOT); }
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_KRABBY) { Ability(ABILITY_HYPER_CUTTER); }
} WHEN {
TURN { MOVE(player, MOVE_PARTING_SHOT); SEND_OUT(player, 1); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_PARTING_SHOT, player);
SEND_IN_MESSAGE("Wynaut");
} THEN {
EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE);
EXPECT_EQ(opponent->statStages[STAT_SPATK], DEFAULT_STAT_STAGE - 1);
EXPECT_EQ(player->species, SPECIES_WYNAUT);
}
}
SINGLE_BATTLE_TEST("Parting Shot: Magic Coat bounces it and switches the target out")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_PARTING_SHOT); }
OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_MAGIC_COAT); }
OPPONENT(SPECIES_WYNAUT);
} WHEN {
TURN { MOVE(opponent, MOVE_MAGIC_COAT); MOVE(player, MOVE_PARTING_SHOT); SEND_OUT(opponent, 1); }
} THEN {
EXPECT_EQ(player->statStages[STAT_ATK], DEFAULT_STAT_STAGE - 1);
EXPECT_EQ(player->statStages[STAT_SPATK], DEFAULT_STAT_STAGE - 1);
EXPECT_EQ(opponent->species, SPECIES_WYNAUT);
}
}
SINGLE_BATTLE_TEST("Parting Shot: Magic Bounce bounces it and switches the target out")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_PARTING_SHOT); }
OPPONENT(SPECIES_ESPEON) { Ability(ABILITY_MAGIC_BOUNCE); }
OPPONENT(SPECIES_WYNAUT);
} WHEN {
TURN { MOVE(player, MOVE_PARTING_SHOT); SEND_OUT(opponent, 1); }
} SCENE {
ABILITY_POPUP(opponent, ABILITY_MAGIC_BOUNCE);
} THEN {
EXPECT_EQ(player->statStages[STAT_ATK], DEFAULT_STAT_STAGE - 1);
EXPECT_EQ(player->statStages[STAT_SPATK], DEFAULT_STAT_STAGE - 1);
EXPECT_EQ(opponent->species, SPECIES_WYNAUT);
}
}
SINGLE_BATTLE_TEST("Parting Shot: Mirror Armor switches the user even if reflected drops fail")
{
u16 species, ability, item;
PARAMETRIZE { species = SPECIES_METAGROSS; ability = ABILITY_CLEAR_BODY; item = ITEM_NONE; }
PARAMETRIZE { species = SPECIES_TORKOAL; ability = ABILITY_WHITE_SMOKE; item = ITEM_NONE; }
PARAMETRIZE { species = SPECIES_SOLGALEO; ability = ABILITY_FULL_METAL_BODY; item = ITEM_NONE; }
PARAMETRIZE { species = SPECIES_LUCARIO; ability = ABILITY_INNER_FOCUS; item = ITEM_CLEAR_AMULET; }
GIVEN {
ASSUME(gItemsInfo[ITEM_CLEAR_AMULET].holdEffect == HOLD_EFFECT_CLEAR_AMULET);
PLAYER(species) { Ability(ability); Item(item); Moves(MOVE_PARTING_SHOT); }
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_CORVIKNIGHT) { Ability(ABILITY_MIRROR_ARMOR); }
} WHEN {
TURN { MOVE(player, MOVE_PARTING_SHOT); SEND_OUT(player, 1); }
} SCENE {
ABILITY_POPUP(opponent, ABILITY_MIRROR_ARMOR);
if (ability == ABILITY_CLEAR_BODY) {
ABILITY_POPUP(player, ABILITY_CLEAR_BODY);
MESSAGE("Metagross's Clear Body prevents stat loss!");
} else if (ability == ABILITY_WHITE_SMOKE) {
ABILITY_POPUP(player, ABILITY_WHITE_SMOKE);
MESSAGE("Torkoal's White Smoke prevents stat loss!");
} else if (ability == ABILITY_FULL_METAL_BODY) {
ABILITY_POPUP(player, ABILITY_FULL_METAL_BODY);
MESSAGE("Solgaleo's Full Metal Body prevents stat loss!");
} else if (item == ITEM_CLEAR_AMULET) {
MESSAGE("The effects of the Clear Amulet held by Lucario prevents its stats from being lowered!");
}
} THEN {
EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE);
EXPECT_EQ(opponent->statStages[STAT_SPATK], DEFAULT_STAT_STAGE);
EXPECT_EQ(player->species, SPECIES_WYNAUT);
}
}
SINGLE_BATTLE_TEST("Parting Shot: Mirror Armor switches even if reflected stats are at minimum")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_PARTING_SHOT, MOVE_SHELL_SMASH, MOVE_CELEBRATE); }
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_CORVIKNIGHT) { Ability(ABILITY_MIRROR_ARMOR); Moves(MOVE_TOPSY_TURVY, MOVE_CELEBRATE); }
} WHEN {
TURN { MOVE(player, MOVE_SHELL_SMASH); MOVE(opponent, MOVE_CELEBRATE); }
TURN { MOVE(player, MOVE_SHELL_SMASH); MOVE(opponent, MOVE_CELEBRATE); }
TURN { MOVE(player, MOVE_SHELL_SMASH); MOVE(opponent, MOVE_CELEBRATE); }
TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_TOPSY_TURVY); }
TURN { MOVE(player, MOVE_PARTING_SHOT); SEND_OUT(player, 1); }
} SCENE {
ABILITY_POPUP(opponent, ABILITY_MIRROR_ARMOR);
} THEN {
EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE);
EXPECT_EQ(opponent->statStages[STAT_SPATK], DEFAULT_STAT_STAGE);
EXPECT_EQ(player->species, SPECIES_WYNAUT);
}
}
SINGLE_BATTLE_TEST("Parting Shot: Does not switch if both stats are at minimum (Gen7+)")
{
GIVEN {
WITH_CONFIG(CONFIG_PARTING_SHOT_SWITCH, GEN_7);
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_PARTING_SHOT, MOVE_TOPSY_TURVY, MOVE_CELEBRATE); }
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_OMASTAR) { Moves(MOVE_SHELL_SMASH, MOVE_CELEBRATE); }
} WHEN {
TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_SHELL_SMASH); }
TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_SHELL_SMASH); }
TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_SHELL_SMASH); }
TURN { MOVE(player, MOVE_TOPSY_TURVY); MOVE(opponent, MOVE_CELEBRATE); }
TURN { MOVE(player, MOVE_PARTING_SHOT); MOVE(opponent, MOVE_CELEBRATE); }
} SCENE {
MESSAGE("The opposing Omastar's stats won't go any lower!");
} THEN {
EXPECT_EQ(opponent->statStages[STAT_ATK], MIN_STAT_STAGE);
EXPECT_EQ(opponent->statStages[STAT_SPATK], MIN_STAT_STAGE);
EXPECT_EQ(player->species, SPECIES_WOBBUFFET);
}
}
SINGLE_BATTLE_TEST("Parting Shot: Does not switch if Contrary is at maximum stats (Gen7+)")
{
GIVEN {
WITH_CONFIG(CONFIG_PARTING_SHOT_SWITCH, GEN_7);
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_PARTING_SHOT, MOVE_TOPSY_TURVY, MOVE_CELEBRATE); }
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_INKAY) { Ability(ABILITY_CONTRARY); Moves(MOVE_SHELL_SMASH, MOVE_CELEBRATE); }
} WHEN {
TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_SHELL_SMASH); }
TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_SHELL_SMASH); }
TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_SHELL_SMASH); }
TURN { MOVE(player, MOVE_TOPSY_TURVY); MOVE(opponent, MOVE_CELEBRATE); }
TURN { MOVE(player, MOVE_PARTING_SHOT); MOVE(opponent, MOVE_CELEBRATE); }
} SCENE {
MESSAGE("The opposing Inkay's stats won't go any higher!");
} THEN {
EXPECT_EQ(opponent->statStages[STAT_ATK], MAX_STAT_STAGE);
EXPECT_EQ(opponent->statStages[STAT_SPATK], MAX_STAT_STAGE);
EXPECT_EQ(player->species, SPECIES_WOBBUFFET);
}
}
SINGLE_BATTLE_TEST("Parting Shot: Stat drop prevention by abilities/items does not switch (Gen7+)")
{
u16 species, ability, item;
PARAMETRIZE { species = SPECIES_METAGROSS; ability = ABILITY_CLEAR_BODY; item = ITEM_NONE; }
PARAMETRIZE { species = SPECIES_TORKOAL; ability = ABILITY_WHITE_SMOKE; item = ITEM_NONE; }
PARAMETRIZE { species = SPECIES_SOLGALEO; ability = ABILITY_FULL_METAL_BODY; item = ITEM_NONE; }
PARAMETRIZE { species = SPECIES_LUCARIO; ability = ABILITY_INNER_FOCUS; item = ITEM_CLEAR_AMULET; }
GIVEN {
WITH_CONFIG(CONFIG_PARTING_SHOT_SWITCH, GEN_7);
ASSUME(gItemsInfo[ITEM_CLEAR_AMULET].holdEffect == HOLD_EFFECT_CLEAR_AMULET);
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_PARTING_SHOT); }
PLAYER(SPECIES_WYNAUT);
OPPONENT(species) { Ability(ability); Item(item); }
} WHEN {
TURN { MOVE(player, MOVE_PARTING_SHOT); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_PARTING_SHOT, player);
} THEN {
EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE);
EXPECT_EQ(opponent->statStages[STAT_SPATK], DEFAULT_STAT_STAGE);
EXPECT_EQ(player->species, SPECIES_WOBBUFFET);
}
}
SINGLE_BATTLE_TEST("Parting Shot: Mist prevents stat drops and does not switch (Gen7+)")
{
GIVEN {
WITH_CONFIG(CONFIG_PARTING_SHOT_SWITCH, GEN_7);
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_PARTING_SHOT, MOVE_CELEBRATE); }
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_MIST, MOVE_CELEBRATE); }
} WHEN {
TURN { MOVE(opponent, MOVE_MIST); MOVE(player, MOVE_CELEBRATE); }
TURN { MOVE(opponent, MOVE_CELEBRATE); MOVE(player, MOVE_PARTING_SHOT); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_PARTING_SHOT, player);
} THEN {
EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE);
EXPECT_EQ(opponent->statStages[STAT_SPATK], DEFAULT_STAT_STAGE);
EXPECT_EQ(player->species, SPECIES_WOBBUFFET);
}
}
DOUBLE_BATTLE_TEST("Parting Shot: Flower Veil prevents stat drops and does not switch (Gen7+)")
{
GIVEN {
ASSUME(GetSpeciesType(SPECIES_BULBASAUR, 0) == TYPE_GRASS);
WITH_CONFIG(CONFIG_PARTING_SHOT_SWITCH, GEN_7);
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_PARTING_SHOT); }
PLAYER(SPECIES_WYNAUT);
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_BULBASAUR);
OPPONENT(SPECIES_COMFEY) { Ability(ABILITY_FLOWER_VEIL); }
} WHEN {
TURN { MOVE(playerLeft, MOVE_PARTING_SHOT, target: opponentLeft); MOVE(playerRight, MOVE_CELEBRATE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_PARTING_SHOT, playerLeft);
} THEN {
EXPECT_EQ(opponentLeft->statStages[STAT_ATK], DEFAULT_STAT_STAGE);
EXPECT_EQ(opponentLeft->statStages[STAT_SPATK], DEFAULT_STAT_STAGE);
EXPECT_EQ(playerLeft->species, SPECIES_WOBBUFFET);
}
}
SINGLE_BATTLE_TEST("Parting Shot: Switches if both stats are at minimum (Gen6)")
{
GIVEN {
WITH_CONFIG(CONFIG_PARTING_SHOT_SWITCH, GEN_6);
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_PARTING_SHOT, MOVE_TOPSY_TURVY, MOVE_CELEBRATE); }
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_OMASTAR) { Moves(MOVE_SHELL_SMASH, MOVE_CELEBRATE); }
} WHEN {
TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_SHELL_SMASH); }
TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_SHELL_SMASH); }
TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_SHELL_SMASH); }
TURN { MOVE(player, MOVE_TOPSY_TURVY); MOVE(opponent, MOVE_CELEBRATE); }
TURN { MOVE(player, MOVE_PARTING_SHOT); MOVE(opponent, MOVE_CELEBRATE); SEND_OUT(player, 1); }
} SCENE {
MESSAGE("The opposing Omastar's stats won't go any lower!");
SEND_IN_MESSAGE("Wynaut");
} THEN {
EXPECT_EQ(opponent->statStages[STAT_ATK], MIN_STAT_STAGE);
EXPECT_EQ(opponent->statStages[STAT_SPATK], MIN_STAT_STAGE);
EXPECT_EQ(player->species, SPECIES_WYNAUT);
}
}
SINGLE_BATTLE_TEST("Parting Shot: Switches if Contrary is at maximum stats (Gen6)")
{
GIVEN {
WITH_CONFIG(CONFIG_PARTING_SHOT_SWITCH, GEN_6);
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_PARTING_SHOT, MOVE_TOPSY_TURVY, MOVE_CELEBRATE); }
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_INKAY) { Ability(ABILITY_CONTRARY); Moves(MOVE_SHELL_SMASH, MOVE_CELEBRATE); }
} WHEN {
TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_SHELL_SMASH); }
TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_SHELL_SMASH); }
TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_SHELL_SMASH); }
TURN { MOVE(player, MOVE_TOPSY_TURVY); MOVE(opponent, MOVE_CELEBRATE); }
TURN { MOVE(player, MOVE_PARTING_SHOT); MOVE(opponent, MOVE_CELEBRATE); SEND_OUT(player, 1); }
} SCENE {
MESSAGE("The opposing Inkay's stats won't go any higher!");
SEND_IN_MESSAGE("Wynaut");
} THEN {
EXPECT_EQ(opponent->statStages[STAT_ATK], MAX_STAT_STAGE);
EXPECT_EQ(opponent->statStages[STAT_SPATK], MAX_STAT_STAGE);
EXPECT_EQ(player->species, SPECIES_WYNAUT);
}
}
SINGLE_BATTLE_TEST("Parting Shot: Stat drop prevention by abilities/items switches (Gen6)")
{
u16 species, ability, item;
PARAMETRIZE { species = SPECIES_METAGROSS; ability = ABILITY_CLEAR_BODY; item = ITEM_NONE; }
PARAMETRIZE { species = SPECIES_TORKOAL; ability = ABILITY_WHITE_SMOKE; item = ITEM_NONE; }
PARAMETRIZE { species = SPECIES_SOLGALEO; ability = ABILITY_FULL_METAL_BODY; item = ITEM_NONE; }
PARAMETRIZE { species = SPECIES_LUCARIO; ability = ABILITY_INNER_FOCUS; item = ITEM_CLEAR_AMULET; }
GIVEN {
WITH_CONFIG(CONFIG_PARTING_SHOT_SWITCH, GEN_6);
ASSUME(gItemsInfo[ITEM_CLEAR_AMULET].holdEffect == HOLD_EFFECT_CLEAR_AMULET);
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_PARTING_SHOT); }
PLAYER(SPECIES_WYNAUT);
OPPONENT(species) { Ability(ability); Item(item); }
} WHEN {
TURN { MOVE(player, MOVE_PARTING_SHOT); SEND_OUT(player, 1); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_PARTING_SHOT, player);
SEND_IN_MESSAGE("Wynaut");
} THEN {
EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE);
EXPECT_EQ(opponent->statStages[STAT_SPATK], DEFAULT_STAT_STAGE);
EXPECT_EQ(player->species, SPECIES_WYNAUT);
}
}
SINGLE_BATTLE_TEST("Parting Shot: Mist prevents stat drops and switches (Gen6)")
{
GIVEN {
WITH_CONFIG(CONFIG_PARTING_SHOT_SWITCH, GEN_6);
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_PARTING_SHOT, MOVE_CELEBRATE); }
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_MIST, MOVE_CELEBRATE); }
} WHEN {
TURN { MOVE(opponent, MOVE_MIST); MOVE(player, MOVE_CELEBRATE); }
TURN { MOVE(opponent, MOVE_CELEBRATE); MOVE(player, MOVE_PARTING_SHOT); SEND_OUT(player, 1); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_PARTING_SHOT, player);
SEND_IN_MESSAGE("Wynaut");
} THEN {
EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE);
EXPECT_EQ(opponent->statStages[STAT_SPATK], DEFAULT_STAT_STAGE);
EXPECT_EQ(player->species, SPECIES_WYNAUT);
}
}
DOUBLE_BATTLE_TEST("Parting Shot: Flower Veil prevents stat drops and switches (Gen6)")
{
GIVEN {
WITH_CONFIG(CONFIG_PARTING_SHOT_SWITCH, GEN_6);
ASSUME(GetSpeciesType(SPECIES_BULBASAUR, 0) == TYPE_GRASS);
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_PARTING_SHOT); }
PLAYER(SPECIES_WYNAUT);
PLAYER(SPECIES_PIKACHU);
OPPONENT(SPECIES_BULBASAUR);
OPPONENT(SPECIES_COMFEY) { Ability(ABILITY_FLOWER_VEIL); }
} WHEN {
TURN { MOVE(playerLeft, MOVE_PARTING_SHOT, target: opponentLeft); MOVE(playerRight, MOVE_CELEBRATE); SEND_OUT(playerLeft, 2); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_PARTING_SHOT, playerLeft);
SEND_IN_MESSAGE("Pikachu");
} THEN {
EXPECT_EQ(opponentLeft->statStages[STAT_ATK], DEFAULT_STAT_STAGE);
EXPECT_EQ(opponentLeft->statStages[STAT_SPATK], DEFAULT_STAT_STAGE);
EXPECT_EQ(playerLeft->species, SPECIES_PIKACHU);
}
}

View File

@ -125,6 +125,36 @@ SINGLE_BATTLE_TEST("Protect: King's Shield, Silk Trap and Obstruct protect from
}
}
SINGLE_BATTLE_TEST("Protect: King's Shield, Silk Trap and Obstruct don't lower stats when charging a two turn move")
{
u32 move, protectMove;
PARAMETRIZE { move = MOVE_BOUNCE; protectMove = MOVE_KINGS_SHIELD; }
PARAMETRIZE { move = MOVE_DIG; protectMove = MOVE_KINGS_SHIELD; }
PARAMETRIZE { move = MOVE_BOUNCE; protectMove = MOVE_SILK_TRAP; }
PARAMETRIZE { move = MOVE_DIG; protectMove = MOVE_SILK_TRAP; }
PARAMETRIZE { move = MOVE_BOUNCE; protectMove = MOVE_OBSTRUCT; }
PARAMETRIZE { move = MOVE_DIG; protectMove = MOVE_OBSTRUCT; }
GIVEN {
ASSUME(MoveMakesContact(MOVE_BOUNCE));
ASSUME(MoveMakesContact(MOVE_DIG));
ASSUME(gBattleMoveEffects[GetMoveEffect(MOVE_BOUNCE)].twoTurnEffect);
ASSUME(gBattleMoveEffects[GetMoveEffect(MOVE_DIG)].twoTurnEffect);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, protectMove); MOVE(opponent, move); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, protectMove, player);
ANIMATION(ANIM_TYPE_MOVE, move, opponent);
NONE_OF {
HP_BAR(player);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
}
}
}
SINGLE_BATTLE_TEST("Protect: Spiky Shield does 1/8 dmg of max hp of attackers making contact and may faint them")
{
u16 usedMove = MOVE_NONE;
@ -162,6 +192,32 @@ SINGLE_BATTLE_TEST("Protect: Spiky Shield does 1/8 dmg of max hp of attackers ma
}
}
SINGLE_BATTLE_TEST("Protect: Spiky Shield doesn't hurt attacker when charging a two turn move")
{
u32 move;
PARAMETRIZE { move = MOVE_BOUNCE; }
PARAMETRIZE { move = MOVE_DIG; }
GIVEN {
ASSUME(MoveMakesContact(MOVE_BOUNCE));
ASSUME(MoveMakesContact(MOVE_DIG));
ASSUME(gBattleMoveEffects[GetMoveEffect(MOVE_BOUNCE)].twoTurnEffect);
ASSUME(gBattleMoveEffects[GetMoveEffect(MOVE_DIG)].twoTurnEffect);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_SPIKY_SHIELD); MOVE(opponent, move); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SPIKY_SHIELD, player);
ANIMATION(ANIM_TYPE_MOVE, move, opponent);
NONE_OF {
HP_BAR(player);
HP_BAR(opponent);
}
}
}
SINGLE_BATTLE_TEST("Protect: Baneful Bunker poisons Pokémon for moves making contact")
{
u16 usedMove = MOVE_NONE;
@ -214,6 +270,32 @@ SINGLE_BATTLE_TEST("Protect: Baneful Bunker can't poison Pokémon if they are al
}
}
SINGLE_BATTLE_TEST("Protect: Baneful Bunker doesn't poison attacker when charging a two turn move")
{
u32 move;
PARAMETRIZE { move = MOVE_BOUNCE; }
PARAMETRIZE { move = MOVE_DIG; }
GIVEN {
ASSUME(MoveMakesContact(MOVE_BOUNCE));
ASSUME(MoveMakesContact(MOVE_DIG));
ASSUME(gBattleMoveEffects[GetMoveEffect(MOVE_BOUNCE)].twoTurnEffect);
ASSUME(gBattleMoveEffects[GetMoveEffect(MOVE_DIG)].twoTurnEffect);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_BANEFUL_BUNKER); MOVE(opponent, move); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_BANEFUL_BUNKER, player);
ANIMATION(ANIM_TYPE_MOVE, move, opponent);
NONE_OF {
HP_BAR(player);
STATUS_ICON(opponent, STATUS1_POISON);
}
}
}
SINGLE_BATTLE_TEST("Protect: Burning Bulwark burns Pokémon for moves making contact")
{
u16 usedMove = MOVE_NONE;
@ -266,6 +348,32 @@ SINGLE_BATTLE_TEST("Protect: Burning Bulwark can't burn Pokémon if they are alr
}
}
SINGLE_BATTLE_TEST("Protect: Burning Bulwark doesn't burn attacker when charging a two turn move")
{
u32 move;
PARAMETRIZE { move = MOVE_BOUNCE; }
PARAMETRIZE { move = MOVE_DIG; }
GIVEN {
ASSUME(MoveMakesContact(MOVE_BOUNCE));
ASSUME(MoveMakesContact(MOVE_DIG));
ASSUME(gBattleMoveEffects[GetMoveEffect(MOVE_BOUNCE)].twoTurnEffect);
ASSUME(gBattleMoveEffects[GetMoveEffect(MOVE_DIG)].twoTurnEffect);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_BURNING_BULWARK); MOVE(opponent, move); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_BURNING_BULWARK, player);
ANIMATION(ANIM_TYPE_MOVE, move, opponent);
NONE_OF {
HP_BAR(player);
STATUS_ICON(opponent, STATUS1_BURN);
}
}
}
SINGLE_BATTLE_TEST("Protect: Recoil damage is not applied if target was protected")
{
u32 j, k;
@ -528,7 +636,7 @@ DOUBLE_BATTLE_TEST("Protect: Quick Guard can not fail on consecutive turns (Gen6
}
}
DOUBLE_BATTLE_TEST("Protect: Crafty Shield protects self and ally from status moves")
DOUBLE_BATTLE_TEST("Crafty Shield protects self and ally from opposing status moves")
{
u16 move = MOVE_NONE;
struct BattlePokemon *targetOpponent = NULL;
@ -569,6 +677,72 @@ DOUBLE_BATTLE_TEST("Protect: Crafty Shield protects self and ally from status mo
}
}
DOUBLE_BATTLE_TEST("Crafty Shield does not protect against status moves used on the user's side")
{
u32 move;
PARAMETRIZE { move = MOVE_AROMATHERAPY; }
PARAMETRIZE { move = MOVE_ACUPRESSURE; }
GIVEN {
ASSUME(GetMoveEffect(MOVE_AROMATHERAPY) == EFFECT_HEAL_BELL);
ASSUME(GetMoveEffect(MOVE_ACUPRESSURE) == EFFECT_ACUPRESSURE);
PLAYER(SPECIES_WOBBUFFET) { Speed(5); }
PLAYER(SPECIES_WOBBUFFET) { Speed(5); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(10); }
OPPONENT(SPECIES_WYNAUT) { Speed(5); Status1(STATUS1_BURN); }
} WHEN {
TURN {
MOVE(opponentLeft, MOVE_CRAFTY_SHIELD);
if (move == MOVE_ACUPRESSURE)
MOVE(opponentRight, move, target: opponentLeft);
else
MOVE(opponentRight, move);
}
TURN {}
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_CRAFTY_SHIELD, opponentLeft);
if (move == MOVE_ACUPRESSURE) {
ANIMATION(ANIM_TYPE_MOVE, MOVE_ACUPRESSURE, opponentRight);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft);
} else {
ANIMATION(ANIM_TYPE_MOVE, MOVE_AROMATHERAPY, opponentRight);
STATUS_ICON(opponentRight, none: TRUE);
}
}
}
DOUBLE_BATTLE_TEST("Crafty Shield does not protect against entry hazard moves")
{
u32 move;
PARAMETRIZE { move = MOVE_SPIKES; }
PARAMETRIZE { move = MOVE_STEALTH_ROCK; }
PARAMETRIZE { move = MOVE_TOXIC_SPIKES; }
PARAMETRIZE { move = MOVE_STICKY_WEB; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponentLeft, MOVE_CRAFTY_SHIELD); MOVE(playerLeft, move, target: opponentLeft); }
TURN {}
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_CRAFTY_SHIELD, opponentLeft);
if (move == MOVE_SPIKES) {
MESSAGE("Spikes were scattered on the ground all around the opposing team!");
} else if (move == MOVE_TOXIC_SPIKES) {
MESSAGE("Poison spikes were scattered on the ground all around the opposing team!");
} else if (move == MOVE_STEALTH_ROCK) {
MESSAGE("Pointed stones float in the air around the opposing team!");
} else {
MESSAGE("A sticky web has been laid out on the ground around the opposing team!");
}
}
}
SINGLE_BATTLE_TEST("Protect: Protect does not block Confide or Decorate")
{
u32 move;
@ -618,6 +792,11 @@ DOUBLE_BATTLE_TEST("Crafty Shield protects self and ally from Confide and Decora
DOUBLE_BATTLE_TEST("Crafty Shield does not protect against moves that target all battlers")
{
u32 move;
PARAMETRIZE { move = MOVE_FLOWER_SHIELD; }
PARAMETRIZE { move = MOVE_PERISH_SONG; }
GIVEN {
ASSUME(GetSpeciesType(SPECIES_TANGELA, 0) == TYPE_GRASS);
ASSUME(GetSpeciesType(SPECIES_TANGROWTH, 0) == TYPE_GRASS);
@ -628,21 +807,29 @@ DOUBLE_BATTLE_TEST("Crafty Shield does not protect against moves that target all
OPPONENT(SPECIES_SUNKERN);
OPPONENT(SPECIES_SUNFLORA);
} WHEN {
TURN { MOVE(opponentLeft, MOVE_CRAFTY_SHIELD); MOVE(opponentRight, MOVE_CELEBRATE); MOVE(playerLeft, MOVE_FLOWER_SHIELD); MOVE(playerRight, MOVE_CELEBRATE); }
TURN { MOVE(opponentLeft, MOVE_CRAFTY_SHIELD); MOVE(opponentRight, MOVE_CELEBRATE); MOVE(playerLeft, move); MOVE(playerRight, MOVE_CELEBRATE); }
} SCENE {
MESSAGE("Tangela used Flower Shield!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_FLOWER_SHIELD, playerLeft);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft);
MESSAGE("Tangela's Defense rose!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_FLOWER_SHIELD, playerLeft);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft);
MESSAGE("The opposing Sunkern's Defense rose!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_FLOWER_SHIELD, playerLeft);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight);
MESSAGE("Tangrowth's Defense rose!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_FLOWER_SHIELD, playerLeft);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentRight);
MESSAGE("The opposing Sunflora's Defense rose!");
if (move == MOVE_FLOWER_SHIELD) {
MESSAGE("Tangela used Flower Shield!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_FLOWER_SHIELD, playerLeft);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft);
MESSAGE("Tangela's Defense rose!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_FLOWER_SHIELD, playerLeft);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft);
MESSAGE("The opposing Sunkern's Defense rose!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_FLOWER_SHIELD, playerLeft);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight);
MESSAGE("Tangrowth's Defense rose!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_FLOWER_SHIELD, playerLeft);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentRight);
MESSAGE("The opposing Sunflora's Defense rose!");
} else {
ANIMATION(ANIM_TYPE_MOVE, MOVE_PERISH_SONG, playerLeft);
NONE_OF {
MESSAGE("The opposing Sunkern protected itself!");
MESSAGE("The opposing Sunflora protected itself!");
}
}
}
}

View File

@ -0,0 +1,97 @@
#include "global.h"
#include "test/battle.h"
ASSUMPTIONS
{
ASSUME(GetMoveEffect(MOVE_HYPERSPACE_FURY) == EFFECT_HYPERSPACE_FURY);
ASSUME(MoveHasAdditionalEffect(MOVE_HYPERSPACE_FURY, MOVE_EFFECT_FEINT));
ASSUME(MoveHasAdditionalEffectSelf(MOVE_HYPERSPACE_FURY, MOVE_EFFECT_DEF_MINUS_1));
ASSUME(GetMoveEffect(MOVE_PROTECT) == EFFECT_PROTECT);
}
SINGLE_BATTLE_TEST("Hyperspace Fury fails if used by a Pokémon other than Hoopa Unbound")
{
u32 species;
PARAMETRIZE { species = SPECIES_WOBBUFFET; }
PARAMETRIZE { species = SPECIES_HOOPA_CONFINED; }
PARAMETRIZE { species = SPECIES_HOOPA_UNBOUND; }
GIVEN {
PLAYER(species);
OPPONENT(SPECIES_REGIROCK);
} WHEN {
TURN { MOVE(player, MOVE_HYPERSPACE_FURY); }
} SCENE {
switch (species)
{
case SPECIES_HOOPA_UNBOUND:
ANIMATION(ANIM_TYPE_MOVE, MOVE_HYPERSPACE_FURY, player);
break;
case SPECIES_HOOPA_CONFINED:
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_HYPERSPACE_FURY, player);
MESSAGE("But Hoopa can't use it the way it is now!");
break;
case SPECIES_WOBBUFFET:
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_HYPERSPACE_FURY, player);
MESSAGE("But Wobbuffet can't use the move!");
break;
default:
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_HYPERSPACE_FURY, player);
break;
}
}
}
DOUBLE_BATTLE_TEST("Hyperspace Fury hits the target through Protect and breaks it")
{
GIVEN {
PLAYER(SPECIES_HOOPA_UNBOUND);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_REGIROCK);
OPPONENT(SPECIES_WYNAUT);
} WHEN {
TURN { MOVE(opponentLeft, MOVE_PROTECT); MOVE(playerLeft, MOVE_HYPERSPACE_FURY, target: opponentLeft); MOVE(playerRight, MOVE_SCRATCH, target: opponentLeft); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_PROTECT, opponentLeft);
ANIMATION(ANIM_TYPE_MOVE, MOVE_HYPERSPACE_FURY, playerLeft);
HP_BAR(opponentLeft);
ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, playerRight);
HP_BAR(opponentLeft);
}
}
SINGLE_BATTLE_TEST("Hyperspace Fury lowers the user's Defense by 1 stage after hitting the target")
{
GIVEN {
PLAYER(SPECIES_HOOPA_UNBOUND);
OPPONENT(SPECIES_REGIROCK);
} WHEN {
TURN { MOVE(player, MOVE_HYPERSPACE_FURY); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_HYPERSPACE_FURY, player);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
} THEN {
EXPECT_EQ(player->statStages[STAT_DEF], DEFAULT_STAT_STAGE - 1);
}
}
DOUBLE_BATTLE_TEST("Hyperspace Fury breaks protection and lowers the user's Defense by 1 stage")
{
GIVEN {
PLAYER(SPECIES_HOOPA_UNBOUND);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_REGIROCK);
OPPONENT(SPECIES_WYNAUT);
} WHEN {
TURN { MOVE(opponentLeft, MOVE_PROTECT); MOVE(playerLeft, MOVE_HYPERSPACE_FURY, target: opponentLeft); MOVE(playerRight, MOVE_SCRATCH, target: opponentLeft); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_PROTECT, opponentLeft);
ANIMATION(ANIM_TYPE_MOVE, MOVE_HYPERSPACE_FURY, playerLeft);
HP_BAR(opponentLeft);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft);
ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, playerRight);
HP_BAR(opponentLeft);
} THEN {
EXPECT_EQ(playerLeft->statStages[STAT_DEF], DEFAULT_STAT_STAGE - 1);
}
}