diff --git a/.all-contributorsrc b/.all-contributorsrc
index 8ae6daf62c..3d356bbc97 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -642,6 +642,24 @@
"contributions": [
"code"
]
+ },
+ {
+ "login": "LogicalLlama",
+ "name": "LogicalLlama",
+ "avatar_url": "https://avatars.githubusercontent.com/u/248230900?v=4",
+ "profile": "https://github.com/LogicalLlama",
+ "contributions": [
+ "bug"
+ ]
+ },
+ {
+ "login": "KnightGallade",
+ "name": "KnightGallade",
+ "avatar_url": "https://avatars.githubusercontent.com/u/189022270?v=4",
+ "profile": "https://github.com/KnightGallade",
+ "contributions": [
+ "bug"
+ ]
}
],
"contributorsPerLine": 7,
diff --git a/CREDITS.md b/CREDITS.md
index 7d9e48d70e..133208fa95 100644
--- a/CREDITS.md
+++ b/CREDITS.md
@@ -92,6 +92,8 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
 SabataLunar 🎨 |
 PacFire 🎨 |
 ChrispyChris27 💻 |
+  LogicalLlama 🐛 |
+  KnightGallade 🐛 |
diff --git a/data/battle_anim_scripts.s b/data/battle_anim_scripts.s
index 78f45c1c61..2f3de93af4 100644
--- a/data/battle_anim_scripts.s
+++ b/data/battle_anim_scripts.s
@@ -25680,7 +25680,7 @@ SnoreEffect:
playsewithpan SE_M_SNORE, SOUND_PAN_ATTACKER
createvisualtask AnimTask_ScaleMonAndRestore, 5, -7, -7, 7, ANIM_ATTACKER, 1
createvisualtask AnimTask_ShakeMon2, 2, ANIM_TARGET, 4, 0, 7, 1
- shake_mon_or_platform velocity=6, shake_timer=1, shake_duration=14, type=0, battler_selector=0
+ shake_mon_or_platform velocity=6, shake_timer=1, shake_duration=14, type=0
createsprite gSnoreZSpriteTemplate, ANIM_ATTACKER, 2, 0, 0, -42, -38, 24, 0, 0
createsprite gSnoreZSpriteTemplate, ANIM_ATTACKER, 2, 0, 0, 0, -42, 24, 0, 0
createsprite gSnoreZSpriteTemplate, ANIM_ATTACKER, 2, 0, 0, 42, -38, 24, 0, 0
diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s
index 25262692d7..8a24a0043b 100644
--- a/data/battle_scripts_1.s
+++ b/data/battle_scripts_1.s
@@ -1652,6 +1652,7 @@ BattleScript_ToxicThreadTryPsn::
BattleScript_EffectVenomDrench::
attackcanceler
+ jumpifsubstituteblocks BattleScript_ButItFailed
jumpifstatus BS_TARGET, STATUS1_PSN_ANY, BattleScript_EffectVenomDrenchCanBeUsed
goto BattleScript_ButItFailed
BattleScript_EffectVenomDrenchCanBeUsed:
@@ -2302,6 +2303,7 @@ BattleScript_EffectMagicRoom::
BattleScript_EffectAquaRing::
attackcanceler
+ jumpifvolatile BS_ATTACKER, VOLATILE_AQUA_RING, BattleScript_ButItFailed
setvolatile BS_ATTACKER, VOLATILE_AQUA_RING
attackanimation
waitanimation
@@ -4318,6 +4320,7 @@ BattleScript_EffectWaterSport::
BattleScript_EffectTickle::
attackcanceler
+ jumpifsubstituteblocks BattleScript_ButItFailed
jumpifstat BS_TARGET, CMP_GREATER_THAN, STAT_ATK, MIN_STAT_STAGE, BattleScript_TickleDoMoveAnim
jumpifstat BS_TARGET, CMP_EQUAL, STAT_DEF, MIN_STAT_STAGE, BattleScript_CantLowerMultipleStats
BattleScript_TickleDoMoveAnim::
@@ -5320,9 +5323,7 @@ BattleScript_GulpMissileNoDmgGorging:
handleformchange BS_TARGET, 0
playanimation BS_TARGET, B_ANIM_FORM_CHANGE
waitanimation
- swapattackerwithtarget
- seteffectprimary BS_ATTACKER, BS_TARGET, MOVE_EFFECT_PARALYSIS
- swapattackerwithtarget
+ seteffectprimary BS_TARGET, BS_ATTACKER, MOVE_EFFECT_PARALYSIS
return
BattleScript_GulpMissileNoSecondEffectGorging:
handleformchange BS_TARGET, 0
@@ -5352,7 +5353,7 @@ BattleScript_GulpMissileNoDmgGulping:
printfromtable gStatDownStringIds
waitmessage B_WAIT_TIME_LONG
BattleScript_GulpMissileGulpingEnd:
- swapattackerwithtarget @ restore the battlers, just in case
+ swapattackerwithtarget
return
BattleScript_GulpMissileNoSecondEffectGulping:
handleformchange BS_TARGET, 0
diff --git a/include/battle.h b/include/battle.h
index 6c528866cf..3861aa8339 100755
--- a/include/battle.h
+++ b/include/battle.h
@@ -61,6 +61,7 @@ struct DisableStruct
{
u32 transformedMonPersonality;
bool8 transformedMonShininess;
+ u16 transformedMonSpecies;
u16 disabledMove;
u16 encoredMove;
u8 protectUses:4;
diff --git a/include/config/battle.h b/include/config/battle.h
index 965174d579..16b4ac9b64 100644
--- a/include/config/battle.h
+++ b/include/config/battle.h
@@ -115,11 +115,16 @@
// Additionally, in gen8+ the Healing Wish's effect will be stored until the user switches into a statused or hurt mon.
#define B_DEFOG_EFFECT_CLEARING GEN_LATEST // In Gen5+, Defog does not lower Evasion of target behind Subsitute. In Gen6+, Defog clears Spikes, Toxic Spikes, Stealth Rock and Sticky Web from both sides. In Gen8+, Defog also clears active Terrain.
#define B_STOCKPILE_RAISES_DEFS GEN_LATEST // In Gen4+, Stockpile also raises Defense and Sp. Defense stats. Once Spit Up / Swallow is used, these stat changes are lost.
+#define B_TRANSFORM_SEMI_INV_FAIL GEN_LATEST // In Gen2+, Transform fails if the target is semi-invulnerable.
+#define B_TRANSFORM_TARGET_FAIL GEN_LATEST // In Gen2+, Transform fails if the target is already transformed.
+#define B_TRANSFORM_USER_FAIL GEN_LATEST // In Gen5+, Transform fails if the user is already transformed.
+#define B_TRANSFORM_SUBSTITUTE_FAIL GEN_LATEST // In Gen5+, Transform fails if the target is behind a Substitute.
#define B_TRANSFORM_SHINY GEN_LATEST // In Gen4+, Transform will copy the shiny state of the opponent instead of maintaining its own shiny state.
#define B_TRANSFORM_FORM_CHANGES GEN_LATEST // In Gen5+, Transformed Pokemon cannot change forms.
#define B_WIDE_GUARD GEN_LATEST // In Gen5 only, Wide Guard has a chance to fail if used consecutively.
#define B_QUICK_GUARD GEN_LATEST // In Gen5 only, Quick Guard has a chance to fail if used consecutively.
#define B_IMPRISON GEN_LATEST // In Gen5+, Imprison doesn't fail if opposing pokemon don't have any moves the user knows.
+#define B_TAUNT_ME_FIRST GEN_LATEST // In Gen5+, Taunt does not block Me First.
#define B_ALLY_SWITCH_FAIL_CHANCE GEN_LATEST // In Gen9+, using Ally Switch consecutively decreases the chance of success for each consecutive use.
#define B_SKETCH_BANS GEN_LATEST // In Gen9+, Sketch is unable to copy more moves than in previous generations.
#define B_KNOCK_OFF_REMOVAL GEN_LATEST // In Gen5+, Knock Off removes the foe's item instead of rendering it unusable.
diff --git a/include/constants/form_change_types.h b/include/constants/form_change_types.h
index f43f3bddc6..1f9a58cd94 100644
--- a/include/constants/form_change_types.h
+++ b/include/constants/form_change_types.h
@@ -137,6 +137,8 @@ enum FormChanges
FORM_CHANGE_OVERWORLD_WEATHER,
// Form change that activates when the Pokémon is deposited into the PC or Daycare.
FORM_CHANGE_DEPOSIT,
+ // Form change for Minior, which appears unchanged when encountered in the wild
+ FORM_CHANGE_BEGIN_WILD_ENCOUNTER,
};
#endif // GUARD_CONSTANTS_FORM_CHANGE_TYPES_H
diff --git a/include/constants/generational_changes.h b/include/constants/generational_changes.h
index 0a5331c342..8a8a31e830 100644
--- a/include/constants/generational_changes.h
+++ b/include/constants/generational_changes.h
@@ -106,11 +106,16 @@
F(B_HEALING_WISH_SWITCH, healingWishSwitch, (u32, GEN_COUNT - 1)) \
F(B_DEFOG_EFFECT_CLEARING, defogEffectClearing, (u32, GEN_COUNT - 1)) \
F(B_STOCKPILE_RAISES_DEFS, stockpileRaisesDefs, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \
+ F(B_TRANSFORM_SEMI_INV_FAIL, transformSemiInvFail, (u32, GEN_COUNT - 1)) \
+ F(B_TRANSFORM_TARGET_FAIL, transformTargetFail, (u32, GEN_COUNT - 1)) \
+ F(B_TRANSFORM_USER_FAIL, transformUserFail, (u32, GEN_COUNT - 1)) \
+ F(B_TRANSFORM_SUBSTITUTE_FAIL, transformSubstituteFail, (u32, GEN_COUNT - 1)) \
F(B_TRANSFORM_SHINY, transformShiny, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \
- F(B_TRANSFORM_FORM_CHANGES, transformFormChanges, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \
+ F(B_TRANSFORM_FORM_CHANGES, transformFormChanges, (u32, GEN_COUNT - 1)) \
F(B_WIDE_GUARD, wideGuard, (u32, GEN_COUNT - 1)) \
F(B_QUICK_GUARD, quickGuard, (u32, GEN_COUNT - 1)) \
F(B_IMPRISON, imprison, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \
+ F(B_TAUNT_ME_FIRST, tauntMeFirst, (u32, GEN_COUNT - 1)) \
F(B_ALLY_SWITCH_FAIL_CHANCE, allySwitchFailChance, (u32, GEN_COUNT - 1)) \
F(B_SKETCH_BANS, sketchBans, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \
F(B_KNOCK_OFF_REMOVAL, knockOffRemoval, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \
diff --git a/include/field_effect_helpers.h b/include/field_effect_helpers.h
index 65dce3dfda..7f772c0c6c 100644
--- a/include/field_effect_helpers.h
+++ b/include/field_effect_helpers.h
@@ -43,4 +43,6 @@ void UpdateSparkleFieldEffect(struct Sprite *sprite);
void SetSpriteInvisible(u8 spriteId);
void ShowWarpArrowSprite(u8 spriteId, u8 direction, s16 x, s16 y);
+u32 FldEff_TallGrass(void);
+
#endif //GUARD_FIELD_EFFECT_HELPERS_H
diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c
index 68cf333b57..bf6768f71c 100644
--- a/src/battle_ai_main.c
+++ b/src/battle_ai_main.c
@@ -5694,8 +5694,11 @@ static s32 AI_CalcAdditionalEffectScore(u32 battlerAtk, u32 battlerDef, u32 move
u32 i;
u32 additionalEffectCount = GetMoveAdditionalEffectCount(move);
- if (IsSheerForceAffected(move, aiData->abilities[battlerAtk]))
+ if (IsSheerForceAffected(move, aiData->abilities[battlerAtk])
+ && !(GetMoveEffect(move) == EFFECT_ORDER_UP && gBattleStruct->battlerState[battlerAtk].commanderSpecies != SPECIES_NONE))
+ {
return score;
+ }
// check move additional effects that are likely to happen
for (i = 0; i < additionalEffectCount; i++)
diff --git a/src/battle_ai_switch_items.c b/src/battle_ai_switch_items.c
index f5d105d04c..ad1023ac15 100644
--- a/src/battle_ai_switch_items.c
+++ b/src/battle_ai_switch_items.c
@@ -480,7 +480,6 @@ static bool32 FindMonThatAbsorbsOpponentsMove(u32 battler)
u32 opposingBattler = GetOppositeBattler(battler);
u32 incomingMove = GetIncomingMove(battler, opposingBattler, gAiLogicData);
enum Type incomingType = CheckDynamicMoveType(GetBattlerMon(opposingBattler), incomingMove, opposingBattler, MON_IN_BATTLE);
- bool32 isOpposingBattlerChargingOrInvulnerable = !BreaksThroughSemiInvulnerablity(opposingBattler, incomingMove) || IsTwoTurnNotSemiInvulnerableMove(opposingBattler, incomingMove);
s32 i, j;
if (!(gAiThinkingStruct->aiFlags[battler] & AI_FLAG_SMART_SWITCHING))
@@ -530,42 +529,42 @@ static bool32 FindMonThatAbsorbsOpponentsMove(u32 battler)
{
absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_FLASH_FIRE;
}
- if (incomingType == TYPE_WATER || (isOpposingBattlerChargingOrInvulnerable && incomingType == TYPE_WATER))
+ if (incomingType == TYPE_WATER)
{
absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_WATER_ABSORB;
absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_DRY_SKIN;
if (GetConfig(B_REDIRECT_ABILITY_IMMUNITY) >= GEN_5)
absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_STORM_DRAIN;
}
- if (incomingType == TYPE_ELECTRIC || (isOpposingBattlerChargingOrInvulnerable && incomingType == TYPE_ELECTRIC))
+ if (incomingType == TYPE_ELECTRIC)
{
absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_VOLT_ABSORB;
absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_MOTOR_DRIVE;
if (GetConfig(B_REDIRECT_ABILITY_IMMUNITY) >= GEN_5)
absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_LIGHTNING_ROD;
}
- if (incomingType == TYPE_GRASS || (isOpposingBattlerChargingOrInvulnerable && incomingType == TYPE_GRASS))
+ if (incomingType == TYPE_GRASS)
{
absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_SAP_SIPPER;
}
- if (incomingType == TYPE_GROUND || (isOpposingBattlerChargingOrInvulnerable && incomingType == TYPE_GROUND))
+ if (incomingType == TYPE_GROUND)
{
absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_EARTH_EATER;
absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_LEVITATE;
}
- if (IsSoundMove(incomingMove) || (isOpposingBattlerChargingOrInvulnerable && IsSoundMove(incomingMove)))
+ if (IsSoundMove(incomingMove))
{
absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_SOUNDPROOF;
}
- if (IsBallisticMove(incomingMove) || (isOpposingBattlerChargingOrInvulnerable && IsBallisticMove(incomingMove)))
+ if (IsBallisticMove(incomingMove))
{
absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_BULLETPROOF;
}
- if (IsWindMove(incomingMove) || (isOpposingBattlerChargingOrInvulnerable && IsWindMove(incomingMove)))
+ if (IsWindMove(incomingMove))
{
absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_WIND_RIDER;
}
- if (IsPowderMove(incomingMove) || (isOpposingBattlerChargingOrInvulnerable && IsPowderMove(incomingMove)))
+ if (IsPowderMove(incomingMove))
{
if (GetConfig(B_POWDER_OVERCOAT) >= GEN_6)
absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_OVERCOAT;
@@ -617,14 +616,23 @@ static bool32 ShouldSwitchIfOpponentChargingOrInvulnerable(u32 battler)
{
u32 opposingBattler = GetOppositeBattler(battler);
u32 incomingMove = GetIncomingMove(battler, opposingBattler, gAiLogicData);
-
- bool32 isOpposingBattlerChargingOrInvulnerable = !BreaksThroughSemiInvulnerablity(opposingBattler, incomingMove) || IsTwoTurnNotSemiInvulnerableMove(opposingBattler, incomingMove);
+ enum BattleMoveEffects effect = GetMoveEffect(incomingMove);
if (IsDoubleBattle() || !(gAiThinkingStruct->aiFlags[battler] & AI_FLAG_SMART_SWITCHING))
return FALSE;
+ // Two-turn attacks that charge without entering semi-invulnerable state (e.g. Solar Beam).
+ // First turn of Fly/Dive/Bounce/Sky Drop: move is selected this turn but user is not yet semi-invulnerable.
+ // Opponent is already semi-invulnerable.
+ if (!(IsTwoTurnNotSemiInvulnerableMove(opposingBattler, incomingMove)
+ || ((effect == EFFECT_SEMI_INVULNERABLE || effect == EFFECT_SKY_DROP) && !IsSemiInvulnerable(opposingBattler, CHECK_ALL))
+ || IsSemiInvulnerable(opposingBattler, CHECK_ALL)))
+ {
+ return FALSE;
+ }
+
// In a world with a unified ShouldSwitch function, also want to check whether we already win 1v1 and if we do don't switch; not worth doubling the HasBadOdds computation for now
- if (isOpposingBattlerChargingOrInvulnerable && gAiLogicData->mostSuitableMonId[battler] != PARTY_SIZE && RandomPercentage(RNG_AI_SWITCH_FREE_TURN, GetSwitchChance(SHOULD_SWITCH_FREE_TURN)))
+ if (gAiLogicData->mostSuitableMonId[battler] != PARTY_SIZE && RandomPercentage(RNG_AI_SWITCH_FREE_TURN, GetSwitchChance(SHOULD_SWITCH_FREE_TURN)))
return SetSwitchinAndSwitch(battler, PARTY_SIZE);
return FALSE;
@@ -654,7 +662,7 @@ static bool32 ShouldSwitchIfTrapperInParty(u32 battler)
for (i = firstId; i < lastId; i++)
{
if (IsAceMon(battler, i))
- return FALSE;
+ continue;
monAbility = GetMonAbility(&party[i]);
diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c
index 83a93bb60c..1b55026ac8 100644
--- a/src/battle_ai_util.c
+++ b/src/battle_ai_util.c
@@ -54,7 +54,7 @@ static bool32 AI_IsDoubleSpreadMove(u32 battlerAtk, u32 move)
if (moveTargetType == MOVE_TARGET_BOTH && battlerAtk == BATTLE_PARTNER(battlerDef))
continue;
- if (IsBattlerAlive(battlerDef) && !IsSemiInvulnerable(battlerDef, move))
+ if (IsBattlerAlive(battlerDef) && (!IsSemiInvulnerable(battlerDef, CHECK_ALL) || BreaksThroughSemiInvulnerablity(battlerDef, move)))
numOfTargets++;
}
@@ -1034,8 +1034,11 @@ static bool32 AI_IsMoveEffectInPlus(u32 battlerAtk, u32 battlerDef, u32 move, s3
enum Ability abilityDef = gAiLogicData->abilities[battlerDef];
enum Ability abilityAtk = gAiLogicData->abilities[battlerAtk];
- if (IsSheerForceAffected(move, abilityAtk))
+ if (IsSheerForceAffected(move, abilityAtk)
+ && !(GetMoveEffect(move) == EFFECT_ORDER_UP && gBattleStruct->battlerState[battlerAtk].commanderSpecies != SPECIES_NONE))
+ {
return FALSE;
+ }
switch (GetMoveEffect(move))
{
diff --git a/src/battle_controller_player_partner.c b/src/battle_controller_player_partner.c
index 37af6497df..738b6e4c38 100644
--- a/src/battle_controller_player_partner.c
+++ b/src/battle_controller_player_partner.c
@@ -206,13 +206,7 @@ static void PlayerPartnerHandleDrawTrainerPic(u32 battler)
enum DifficultyLevel difficulty = GetBattlePartnerDifficultyLevel(gPartnerTrainerId);
- if (IsMultibattleTest())
- {
- trainerPicId = TRAINER_BACK_PIC_STEVEN;
- xPos = 90;
- yPos = (8 - gTrainerBacksprites[trainerPicId].coordinates.size) * 4 + 80;
- }
- else if (gPartnerTrainerId > TRAINER_PARTNER(PARTNER_NONE))
+ if (gPartnerTrainerId > TRAINER_PARTNER(PARTNER_NONE))
{
trainerPicId = gBattlePartners[difficulty][gPartnerTrainerId - TRAINER_PARTNER(PARTNER_NONE)].trainerBackPic;
xPos = 90;
diff --git a/src/battle_controller_recorded_partner.c b/src/battle_controller_recorded_partner.c
index 4a59002020..dddf32982b 100644
--- a/src/battle_controller_recorded_partner.c
+++ b/src/battle_controller_recorded_partner.c
@@ -203,11 +203,32 @@ static void RecordedPartnerHandleDrawTrainerPic(u32 battler)
s16 xPos, yPos;
u32 trainerPicId;
- trainerPicId = TRAINER_BACK_PIC_STEVEN;
- xPos = 90;
- yPos = (8 - gTrainerBacksprites[trainerPicId].coordinates.size) * 4 + 80;
+ enum DifficultyLevel difficulty = GetBattlePartnerDifficultyLevel(gPartnerTrainerId);
- isFrontPic = FALSE;
+ if (gPartnerTrainerId > TRAINER_PARTNER(PARTNER_NONE))
+ {
+ trainerPicId = gBattlePartners[difficulty][gPartnerTrainerId - TRAINER_PARTNER(PARTNER_NONE)].trainerBackPic;
+ xPos = 90;
+ yPos = (8 - gTrainerBacksprites[trainerPicId].coordinates.size) * 4 + 80;
+ }
+ else if (IsAiVsAiBattle())
+ {
+ trainerPicId = GetTrainerPicFromId(gPartnerTrainerId);
+ xPos = 60;
+ yPos = 80;
+ }
+ else
+ {
+ trainerPicId = GetFrontierTrainerFrontSpriteId(gPartnerTrainerId);
+ xPos = 32;
+ yPos = 80;
+ }
+
+ // Use back pic only if the partner Steven or is custom.
+ if (gPartnerTrainerId > TRAINER_PARTNER(PARTNER_NONE))
+ isFrontPic = FALSE;
+ else
+ isFrontPic = TRUE;
BtlController_HandleDrawTrainerPic(battler, trainerPicId, isFrontPic, xPos, yPos, -1);
}
@@ -246,9 +267,9 @@ static void RecordedPartnerHandleIntroTrainerBallThrow(u32 battler)
enum DifficultyLevel difficulty = GetBattlePartnerDifficultyLevel(gPartnerTrainerId);
if (gPartnerTrainerId > TRAINER_PARTNER(PARTNER_NONE))
- trainerPal = gTrainerBacksprites[gBattlePartners[difficulty][gPartnerTrainerId - TRAINER_PARTNER(PARTNER_NONE)].trainerPic].palette.data;
+ trainerPal = gTrainerBacksprites[gBattlePartners[difficulty][gPartnerTrainerId - TRAINER_PARTNER(PARTNER_NONE)].trainerBackPic].palette.data;
else if (IsAiVsAiBattle())
- trainerPal = gTrainerSprites[GetTrainerPicFromId(gPartnerTrainerId)].palette.data;
+ trainerPal = gTrainerSprites[GetTrainerBackPicFromId(gPartnerTrainerId)].palette.data;
else
trainerPal = gTrainerSprites[GetFrontierTrainerFrontSpriteId(gPartnerTrainerId)].palette.data; // 2 vs 2 multi battle in Battle Frontier, load front sprite and pal.
diff --git a/src/battle_controllers.c b/src/battle_controllers.c
index dd8abcf213..819fc10f30 100644
--- a/src/battle_controllers.c
+++ b/src/battle_controllers.c
@@ -2397,8 +2397,7 @@ void BtlController_HandleDrawTrainerPic(u32 battler, u32 trainerPicId, bool32 is
if ((gBattleTypeFlags & BATTLE_TYPE_SAFARI) && GetBattlerPosition(battler) == B_POSITION_PLAYER_LEFT)
gBattlerSpriteIds[battler] = gBattleStruct->trainerSlideSpriteIds[battler];
- // Aiming for palette slots 8 and 9 for Player and PlayerPartner to prevent Trainer Slides causing mons to change colour
- gSprites[gBattleStruct->trainerSlideSpriteIds[battler]].oam.paletteNum = (8 + battler/2);
+ gSprites[gBattleStruct->trainerSlideSpriteIds[battler]].oam.paletteNum = battler;
}
gSprites[gBattleStruct->trainerSlideSpriteIds[battler]].x2 = DISPLAY_WIDTH;
gSprites[gBattleStruct->trainerSlideSpriteIds[battler]].sSpeedX = -2;
@@ -2423,8 +2422,7 @@ void BtlController_HandleTrainerSlide(u32 battler, u32 trainerPicId)
30);
if ((gBattleTypeFlags & BATTLE_TYPE_SAFARI) && GetBattlerPosition(battler) == B_POSITION_PLAYER_LEFT)
gBattlerSpriteIds[battler] = gBattleStruct->trainerSlideSpriteIds[battler];
- // Aiming for palette slots 8 and 9 for Player and PlayerPartner to prevent Trainer Slides causing mons to change colour
- gSprites[gBattleStruct->trainerSlideSpriteIds[battler]].oam.paletteNum = (8 + battler/2);
+ gSprites[gBattleStruct->trainerSlideSpriteIds[battler]].oam.paletteNum = battler;
gSprites[gBattleStruct->trainerSlideSpriteIds[battler]].x2 = -96;
gSprites[gBattleStruct->trainerSlideSpriteIds[battler]].sSpeedX = 2;
}
@@ -2772,7 +2770,7 @@ void BtlController_HandleIntroTrainerBallThrow(u32 battler, u16 tagTrainerPal, c
paletteNum = AllocSpritePalette(tagTrainerPal);
LoadPalette(trainerPal, OBJ_PLTT_ID(paletteNum), PLTT_SIZE_4BPP);
- gSprites[gBattleStruct->trainerSlideSpriteIds[battler]].oam.paletteNum = (8 + battler/2);
+ gSprites[gBattleStruct->trainerSlideSpriteIds[battler]].oam.paletteNum = paletteNum;
}
else
{
diff --git a/src/battle_gfx_sfx_util.c b/src/battle_gfx_sfx_util.c
index 44f75a78d0..74587b3648 100644
--- a/src/battle_gfx_sfx_util.c
+++ b/src/battle_gfx_sfx_util.c
@@ -700,9 +700,8 @@ void DecompressTrainerBackPic(u16 backPicId, u8 battler)
{
u8 position = GetBattlerPosition(battler);
CopyTrainerBackspriteFramesToDest(backPicId, gMonSpritesGfxPtr->spritesGfx[position]);
- // Aiming for palette slots 8 and 9 for Player and PlayerPartner to prevent Trainer Slides causing mons to change colour
LoadPalette(gTrainerBacksprites[backPicId].palette.data,
- OBJ_PLTT_ID(8 + battler/2), PLTT_SIZE_4BPP);
+ OBJ_PLTT_ID(battler), PLTT_SIZE_4BPP);
}
void FreeTrainerFrontPicPalette(u16 frontPicId)
diff --git a/src/battle_main.c b/src/battle_main.c
index d23e46797c..afbefb2cdf 100644
--- a/src/battle_main.c
+++ b/src/battle_main.c
@@ -582,7 +582,11 @@ static void CB2_InitBattleInternal(void)
TryFormChange(i, B_SIDE_PLAYER, FORM_CHANGE_BEGIN_BATTLE);
TryFormChange(i, B_SIDE_OPPONENT, FORM_CHANGE_BEGIN_BATTLE);
}
-
+ if (!(gBattleTypeFlags & BATTLE_TYPE_TRAINER))
+ {
+ TryFormChange(0, B_SIDE_OPPONENT, FORM_CHANGE_BEGIN_WILD_ENCOUNTER);
+ TryFormChange(1, B_SIDE_OPPONENT, FORM_CHANGE_BEGIN_WILD_ENCOUNTER);// Only tries to change the first two opposing slots, assuming these are the only ones occupied in a wild battle.
+ }
if (TESTING)
{
gPlayerPartyCount = CalculatePartyCount(gPlayerParty);
@@ -3368,6 +3372,9 @@ const u8* FaintClearSetData(u32 battler)
if (gBattleStruct->battlerState[battler].commanderSpecies != SPECIES_NONE)
{
u32 partner = BATTLE_PARTNER(battler);
+ // Clear commander state immediately so a replacement doesn't inherit it.
+ gBattleStruct->battlerState[battler].commanderSpecies = SPECIES_NONE;
+ gBattleMons[partner].volatiles.semiInvulnerable = STATE_NONE;
if (IsBattlerAlive(partner))
{
BtlController_EmitSpriteInvisibility(partner, B_COMM_TO_CONTROLLER, FALSE);
@@ -3887,6 +3894,7 @@ static void TryDoEventsBeforeFirstTurn(void)
while (gBattleStruct->switchInBattlerCounter < gBattlersCount) // From fastest to slowest
{
u32 battler = gBattlerByTurnOrder[gBattleStruct->switchInBattlerCounter++];
+ gBattlerAttacker = battler;
if (ItemBattleEffects(battler, 0, GetBattlerHoldEffect(battler), IsOnSwitchInFirstTurnActivation))
return;
}
diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c
index 9d4f679208..de8d68cb51 100644
--- a/src/battle_script_commands.c
+++ b/src/battle_script_commands.c
@@ -1136,6 +1136,19 @@ static inline bool32 IsBattlerUsingBeakBlast(u32 battler)
return !HasBattlerActedThisTurn(battler);
}
+static inline bool32 IsInstructBannedChargingMove(u32 battler)
+{
+ enum BattleMoveEffects moveEffect;
+
+ if (gChosenActionByBattler[battler] != B_ACTION_USE_MOVE || HasBattlerActedThisTurn(battler))
+ return FALSE;
+
+ moveEffect = GetMoveEffect(gChosenMoveByBattler[battler]);
+ return moveEffect == EFFECT_FOCUS_PUNCH
+ || moveEffect == EFFECT_BEAK_BLAST
+ || moveEffect == EFFECT_SHELL_TRAP;
+}
+
static void Cmd_attackcanceler(void)
{
CMD_ARGS();
@@ -3095,7 +3108,9 @@ void SetMoveEffect(u32 battler, u32 effectBattler, enum MoveEffect moveEffect, c
case MOVE_EFFECT_TOXIC:
case MOVE_EFFECT_FROSTBITE:
if (IsSafeguardProtected(gBattlerAttacker, gEffectBattler, GetBattlerAbility(gBattlerAttacker)) && !primary)
+ {
gBattlescriptCurrInstr = battleScript;
+ }
else if (CanSetNonVolatileStatus(
gBattlerAttacker,
gEffectBattler,
@@ -3103,7 +3118,13 @@ void SetMoveEffect(u32 battler, u32 effectBattler, enum MoveEffect moveEffect, c
battlerAbility,
moveEffect,
CHECK_TRIGGER))
+ {
SetNonVolatileStatus(gEffectBattler, moveEffect, battleScript, TRIGGER_ON_MOVE);
+ }
+ else
+ {
+ gBattlescriptCurrInstr = battleScript;
+ }
break;
case MOVE_EFFECT_CONFUSION:
if (!CanBeConfused(gEffectBattler)
@@ -3493,8 +3514,11 @@ void SetMoveEffect(u32 battler, u32 effectBattler, enum MoveEffect moveEffect, c
}
break;
case MOVE_EFFECT_THROAT_CHOP:
- gDisableStructs[gEffectBattler].throatChopTimer = 2;
- gBattlescriptCurrInstr = battleScript;
+ if (gDisableStructs[gEffectBattler].throatChopTimer == 0)
+ {
+ gDisableStructs[gEffectBattler].throatChopTimer = 2;
+ gBattlescriptCurrInstr = battleScript;
+ }
break;
case MOVE_EFFECT_INCINERATE:
if (((gBattleMons[gEffectBattler].item >= FIRST_BERRY_INDEX && gBattleMons[gEffectBattler].item <= LAST_BERRY_INDEX)
@@ -4246,7 +4270,8 @@ static void Cmd_seteffectprimary(void)
u32 battler = GetBattlerForBattleScript(cmd->battler);
u32 effectBattler = GetBattlerForBattleScript(cmd->effectBattler);
- SetMoveEffect(battler, effectBattler, gBattleScripting.moveEffect, cmd->nextInstr, EFFECT_PRIMARY);
+ gBattlescriptCurrInstr = cmd->nextInstr;
+ SetMoveEffect(battler, effectBattler, gBattleScripting.moveEffect, gBattlescriptCurrInstr, EFFECT_PRIMARY);
}
static void Cmd_seteffectsecondary(void)
@@ -4255,7 +4280,8 @@ static void Cmd_seteffectsecondary(void)
u32 battler = GetBattlerForBattleScript(cmd->battler);
u32 effectBattler = GetBattlerForBattleScript(cmd->effectBattler);
- SetMoveEffect(battler, effectBattler, gBattleScripting.moveEffect, cmd->nextInstr, EFFECT_PRIMARY);
+ gBattlescriptCurrInstr = cmd->nextInstr;
+ SetMoveEffect(battler, effectBattler, gBattleScripting.moveEffect, gBattlescriptCurrInstr, NO_FLAGS);
}
static void Cmd_clearvolatile(void)
@@ -5512,6 +5538,7 @@ static void PlayAnimation(u32 battler, u8 animId, const u16 *argPtr, const u8 *n
|| animId == B_ANIM_FORM_CHANGE
|| animId == B_ANIM_SUBSTITUTE_FADE
|| animId == B_ANIM_PRIMAL_REVERSION
+ || animId == B_ANIM_POWER_CONSTRUCT
|| animId == B_ANIM_ULTRA_BURST
|| animId == B_ANIM_TERA_CHARGE
|| animId == B_ANIM_TERA_ACTIVATE)
@@ -5622,7 +5649,7 @@ static inline bool32 CanEjectButtonTrigger(u32 battlerAtk, u32 battlerDef, enum
&& battlerAtk != battlerDef
&& IsBattlerTurnDamaged(battlerDef)
&& IsBattlerAlive(battlerDef)
- && CountUsablePartyMons(battlerDef) > 0
+ && CanBattlerSwitch(battlerDef)
&& !(moveEffect == EFFECT_HIT_SWITCH_TARGET && CanBattlerSwitch(battlerAtk)))
return TRUE;
@@ -5634,7 +5661,7 @@ static inline bool32 CanEjectPackTrigger(u32 battlerAtk, u32 battlerDef, enum Ba
if (gDisableStructs[battlerDef].tryEjectPack
&& GetBattlerHoldEffect(battlerDef) == HOLD_EFFECT_EJECT_PACK
&& IsBattlerAlive(battlerDef)
- && CountUsablePartyMons(battlerDef) > 0
+ && CanBattlerSwitch(battlerDef)
&& !gProtectStructs[battlerDef].disableEjectPack
&& !(moveEffect == EFFECT_HIT_SWITCH_TARGET && CanBattlerSwitch(battlerAtk))
&& !(moveEffect == EFFECT_PARTING_SHOT && CanBattlerSwitch(battlerAtk)))
@@ -11265,10 +11292,11 @@ static void Cmd_transformdataexecution(void)
gChosenMove = MOVE_UNAVAILABLE;
gBattlescriptCurrInstr = cmd->nextInstr;
- if (gBattleMons[gBattlerTarget].volatiles.transformed
- || DoesSubstituteBlockMove(gBattlerAttacker, gBattlerTarget, gCurrentMove)
- || gBattleStruct->illusion[gBattlerTarget].state == ILLUSION_ON
- || IsSemiInvulnerable(gBattlerTarget, EXCLUDE_COMMANDER))
+ if ((GetConfig(B_TRANSFORM_SEMI_INV_FAIL) >= GEN_2 && IsSemiInvulnerable(gBattlerTarget, EXCLUDE_COMMANDER))
+ || (GetConfig(B_TRANSFORM_TARGET_FAIL) >= GEN_2 && gBattleMons[gBattlerTarget].volatiles.transformed)
+ || (GetConfig(B_TRANSFORM_USER_FAIL) >= GEN_5 && gBattleMons[gBattlerAttacker].volatiles.transformed)
+ || (GetConfig(B_TRANSFORM_SUBSTITUTE_FAIL) >= GEN_5 && DoesSubstituteBlockMove(gBattlerAttacker, gBattlerTarget, gCurrentMove))
+ || gBattleStruct->illusion[gBattlerTarget].state == ILLUSION_ON)
{
gBattleStruct->moveResultFlags[gBattlerTarget] |= MOVE_RESULT_FAILED;
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TRANSFORM_FAILED;
@@ -11282,6 +11310,7 @@ static void Cmd_transformdataexecution(void)
gBattleMons[gBattlerAttacker].volatiles.transformed = TRUE;
gDisableStructs[gBattlerAttacker].disabledMove = MOVE_NONE;
gDisableStructs[gBattlerAttacker].disableTimer = 0;
+ gDisableStructs[gBattlerAttacker].transformedMonSpecies = gBattleMons[gBattlerAttacker].species;
gDisableStructs[gBattlerAttacker].transformedMonPersonality = gBattleMons[gBattlerTarget].personality;
if (B_TRANSFORM_SHINY >= GEN_4)
gDisableStructs[gBattlerAttacker].transformedMonShininess = gBattleMons[gBattlerTarget].isShiny;
@@ -12119,13 +12148,17 @@ static void Cmd_presentdamagecalculation(void)
{
gBattlescriptCurrInstr = BattleScript_HitFromCritCalc;
}
+ else if (gBattlerTarget == BATTLE_PARTNER(gBattlerAttacker) && GetBattlerAbility(gBattlerTarget) == ABILITY_TELEPATHY)
+ {
+ gBattlescriptCurrInstr = BattleScript_MoveMissedPause;
+ }
else if (gBattleMons[gBattlerTarget].maxHP == gBattleMons[gBattlerTarget].hp)
{
gBattlescriptCurrInstr = BattleScript_AlreadyAtFullHp;
}
else
{
- gBattleStruct->moveResultFlags[gBattlerTarget] &= ~MOVE_RESULT_DOESNT_AFFECT_FOE;
+ gBattleStruct->moveResultFlags[gBattlerTarget] &= ~(MOVE_RESULT_MISSED | MOVE_RESULT_DOESNT_AFFECT_FOE);
gBattlescriptCurrInstr = BattleScript_PresentHealTarget;
}
}
@@ -16529,6 +16562,12 @@ void BS_JumpIfAbilityCantBeReactivated(void)
u32 battler = GetBattlerForBattleScript(cmd->battler);
u32 ability = gBattleMons[battler].ability;
+ if (GetBattlerHoldEffectIgnoreAbility(battler) == HOLD_EFFECT_ABILITY_SHIELD)
+ {
+ gBattlescriptCurrInstr = cmd->jumpInstr;
+ return;
+ }
+
switch (ability)
{
case ABILITY_IMPOSTER:
@@ -17379,6 +17418,9 @@ void BS_TryInstruct(void)
u16 move = gLastPrintedMoves[gBattlerTarget];
if (move == MOVE_NONE || move == MOVE_UNAVAILABLE || MoveHasAdditionalEffectSelf(move, MOVE_EFFECT_RECHARGE)
|| IsMoveInstructBanned(move)
+ || IsInstructBannedChargingMove(gBattlerTarget)
+ || gBattleMons[gBattlerTarget].volatiles.bideTurns != 0
+ || gBattleMons[gBattlerTarget].volatiles.semiInvulnerable == STATE_SKY_DROP
|| gBattleMoveEffects[GetMoveEffect(move)].twoTurnEffect
|| (GetActiveGimmick(gBattlerTarget) == GIMMICK_DYNAMAX)
|| IsZMove(move)
diff --git a/src/battle_setup.c b/src/battle_setup.c
index 9b8a669255..59cb31a515 100644
--- a/src/battle_setup.c
+++ b/src/battle_setup.c
@@ -1321,6 +1321,13 @@ static void CB2_EndTrainerBattle(void)
DowngradeBadPoison();
SetMainCallback2(CB2_ReturnToFieldContinueScriptPlayMapMusic);
}
+ else if (DidPlayerForfeitNormalTrainerBattle())
+ {
+ if (FlagGet(B_FLAG_NO_WHITEOUT) || CurrentBattlePyramidLocation() != PYRAMID_LOCATION_NONE || InTrainerHillChallenge())
+ SetMainCallback2(CB2_ReturnToFieldContinueScriptPlayMapMusic);
+ else
+ SetMainCallback2(CB2_WhiteOut);
+ }
else if (IsPlayerDefeated(gBattleOutcome) == TRUE)
{
if (CurrentBattlePyramidLocation() != PYRAMID_LOCATION_NONE || InTrainerHillChallenge() || (!NoAliveMonsForPlayer()) || FlagGet(B_FLAG_NO_WHITEOUT))
@@ -1328,10 +1335,6 @@ static void CB2_EndTrainerBattle(void)
else
SetMainCallback2(CB2_WhiteOut);
}
- else if (DidPlayerForfeitNormalTrainerBattle())
- {
- SetMainCallback2(CB2_WhiteOut);
- }
else
{
SetMainCallback2(CB2_ReturnToFieldContinueScriptPlayMapMusic);
diff --git a/src/battle_terastal.c b/src/battle_terastal.c
index f0e720c93e..320e236662 100644
--- a/src/battle_terastal.c
+++ b/src/battle_terastal.c
@@ -163,15 +163,19 @@ uq4_12_t GetTeraMultiplier(struct DamageContext *ctx)
else
return UQ_4_12(2.0);
}
- // Base or Tera type only.
- else if ((ctx->moveType == teraType && !IS_BATTLER_OF_BASE_TYPE(ctx->battlerAtk, ctx->moveType))
- || (ctx->moveType != teraType && IS_BATTLER_OF_BASE_TYPE(ctx->battlerAtk, ctx->moveType)))
+ // Tera type only (Adaptability applies).
+ else if (ctx->moveType == teraType && !IS_BATTLER_OF_BASE_TYPE(ctx->battlerAtk, ctx->moveType))
{
if (ctx->abilityAtk == ABILITY_ADAPTABILITY)
return UQ_4_12(2.0);
else
return UQ_4_12(1.5);
}
+ // Base type only (Adaptability does not apply while Terastallized).
+ else if (ctx->moveType != teraType && IS_BATTLER_OF_BASE_TYPE(ctx->battlerAtk, ctx->moveType))
+ {
+ return UQ_4_12(1.5);
+ }
// Neither base or Tera type.
else
{
diff --git a/src/battle_util.c b/src/battle_util.c
index e352173577..ee3ae241f7 100644
--- a/src/battle_util.c
+++ b/src/battle_util.c
@@ -1474,7 +1474,10 @@ u32 TrySetCantSelectMoveBattleScript(u32 battler)
}
}
- if (GetActiveGimmick(battler) != GIMMICK_Z_MOVE && gDisableStructs[battler].tauntTimer != 0 && IsBattleMoveStatus(move))
+ if (GetActiveGimmick(battler) != GIMMICK_Z_MOVE
+ && gDisableStructs[battler].tauntTimer != 0
+ && IsBattleMoveStatus(move)
+ && (GetConfig(B_TAUNT_ME_FIRST) < GEN_5 || moveEffect != EFFECT_ME_FIRST))
{
if ((GetActiveGimmick(battler) == GIMMICK_DYNAMAX))
gCurrentMove = MOVE_MAX_GUARD;
@@ -1709,7 +1712,10 @@ u32 CheckMoveLimitations(u32 battler, u8 unusableMoves, u16 check)
else if (check & MOVE_LIMITATION_TORMENTED && move == gLastMoves[battler] && gBattleMons[battler].volatiles.torment == TRUE)
unusableMoves |= 1u << i;
// Taunt
- else if (check & MOVE_LIMITATION_TAUNT && gDisableStructs[battler].tauntTimer && IsBattleMoveStatus(move))
+ else if (check & MOVE_LIMITATION_TAUNT
+ && gDisableStructs[battler].tauntTimer
+ && IsBattleMoveStatus(move)
+ && (GetConfig(B_TAUNT_ME_FIRST) < GEN_5 || moveEffect != EFFECT_ME_FIRST))
unusableMoves |= 1u << i;
// Imprison
else if (check & MOVE_LIMITATION_IMPRISON && GetImprisonedMovesCount(battler, move))
@@ -2249,7 +2255,12 @@ static enum MoveCanceler CancelerVolatileBlocked(struct BattleContext *ctx)
static enum MoveCanceler CancelerTaunted(struct BattleContext *ctx)
{
- if (GetActiveGimmick(ctx->battlerAtk) != GIMMICK_Z_MOVE && gDisableStructs[ctx->battlerAtk].tauntTimer && IsBattleMoveStatus(ctx->currentMove))
+ enum BattleMoveEffects moveEffect = GetMoveEffect(ctx->currentMove);
+
+ if (GetActiveGimmick(ctx->battlerAtk) != GIMMICK_Z_MOVE
+ && gDisableStructs[ctx->battlerAtk].tauntTimer
+ && IsBattleMoveStatus(ctx->currentMove)
+ && (GetConfig(B_TAUNT_ME_FIRST) < GEN_5 || moveEffect != EFFECT_ME_FIRST))
{
gProtectStructs[ctx->battlerAtk].unableToUseMove = TRUE;
CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK);
@@ -5407,7 +5418,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab
if (IsBattlerAlive(gBattlerTarget)
&& !gProtectStructs[gBattlerAttacker].confusionSelfDmg
&& CanBePoisoned(gBattlerAttacker, gBattlerTarget, gLastUsedAbility, GetBattlerAbility(gBattlerTarget))
- && !CanBattlerAvoidContactEffects(gBattlerAttacker, gBattlerTarget, GetBattlerAbility(gBattlerAttacker), GetBattlerHoldEffect(gBattlerAttacker), move)
+ && IsMoveMakingContact(gBattlerAttacker, gBattlerTarget, GetBattlerAbility(gBattlerAttacker), GetBattlerHoldEffect(gBattlerAttacker), move)
&& IsBattlerTurnDamaged(gBattlerTarget) // Need to actually hit the target
&& RandomPercentage(RNG_POISON_TOUCH, 30))
{
@@ -8134,8 +8145,13 @@ static inline u32 CalcDefenseStat(struct DamageContext *ctx)
modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(2.0));
break;
case HOLD_EFFECT_EVIOLITE:
- if (CanEvolve(gBattleMons[battlerDef].species))
- modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(1.5));
+ {
+ u16 species = gBattleMons[battlerDef].species;
+ if (gBattleMons[battlerDef].volatiles.transformed && gDisableStructs[battlerDef].transformedMonSpecies != SPECIES_NONE)
+ species = gDisableStructs[battlerDef].transformedMonSpecies;
+ if (CanEvolve(species))
+ modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(1.5));
+ }
break;
case HOLD_EFFECT_ASSAULT_VEST:
if (!usesDefStat)
@@ -8372,12 +8388,12 @@ static inline uq4_12_t GetDefenderAbilitiesModifier(struct DamageContext *ctx)
}
break;
case ABILITY_FLUFFY:
- if (ctx->moveType == TYPE_FIRE && !IsMoveMakingContact(ctx->battlerAtk, ctx->battlerDef, ABILITY_NONE, ctx->holdEffectAtk, ctx->move))
+ if (ctx->moveType == TYPE_FIRE && !IsMoveMakingContact(ctx->battlerAtk, ctx->battlerDef, ctx->abilityAtk, ctx->holdEffectAtk, ctx->move))
{
modifier = UQ_4_12(2.0);
recordAbility = TRUE;
}
- if (ctx->moveType != TYPE_FIRE && IsMoveMakingContact(ctx->battlerAtk, ctx->battlerDef, ABILITY_NONE, ctx->holdEffectAtk, ctx->move))
+ if (ctx->moveType != TYPE_FIRE && IsMoveMakingContact(ctx->battlerAtk, ctx->battlerDef, ctx->abilityAtk, ctx->holdEffectAtk, ctx->move))
{
modifier = UQ_4_12(0.5);
recordAbility = TRUE;
@@ -9342,7 +9358,7 @@ static bool32 CanBattlerFormChange(u32 battler, enum FormChanges method)
{
// Can't change form if transformed.
if (gBattleMons[battler].volatiles.transformed
- && B_TRANSFORM_FORM_CHANGES >= GEN_5)
+ && GetConfig(B_TRANSFORM_FORM_CHANGES) >= GEN_5)
return FALSE;
switch (method)
@@ -10680,7 +10696,7 @@ bool32 TrySwitchInEjectPack(enum EjectPackTiming timing)
if (gDisableStructs[i].tryEjectPack
&& GetBattlerHoldEffect(i) == HOLD_EFFECT_EJECT_PACK
&& IsBattlerAlive(i)
- && CountUsablePartyMons(i) > 0)
+ && CanBattlerSwitch(i))
{
ejectPackBattlers |= 1u << i;
numEjectPackBattlers++;
@@ -11204,11 +11220,21 @@ static u32 GetAssistMove(void)
u32 move = MOVE_NONE;
s32 chooseableMovesNo = 0;
struct Pokemon *party;
+ u8 battlerByPartyId[PARTY_SIZE];
u16 *validMoves = Alloc(sizeof(u16) * PARTY_SIZE * MAX_MON_MOVES);
if (validMoves != NULL)
{
party = GetBattlerParty(gBattlerAttacker);
+ for (u32 i = 0; i < PARTY_SIZE; i++)
+ battlerByPartyId[i] = MAX_BATTLERS_COUNT;
+ for (u32 battler = 0; battler < gBattlersCount; battler++)
+ {
+ if (GetBattlerSide(battler) != GetBattlerSide(gBattlerAttacker))
+ continue;
+ if (gBattlerPartyIndexes[battler] < PARTY_SIZE)
+ battlerByPartyId[gBattlerPartyIndexes[battler]] = battler;
+ }
for (u32 monId = 0; monId < PARTY_SIZE; monId++)
{
@@ -11221,7 +11247,12 @@ static u32 GetAssistMove(void)
for (u32 moveId = 0; moveId < MAX_MON_MOVES; moveId++)
{
- u16 move = GetMonData(&party[monId], MON_DATA_MOVE1 + moveId);
+ u16 move;
+
+ if (battlerByPartyId[monId] != MAX_BATTLERS_COUNT)
+ move = gBattleMons[battlerByPartyId[monId]].moves[moveId];
+ else
+ move = GetMonData(&party[monId], MON_DATA_MOVE1 + moveId);
if (IsMoveAssistBanned(move))
continue;
diff --git a/src/data/moves_info.h b/src/data/moves_info.h
index de2912aa08..e48ed6d7a1 100644
--- a/src/data/moves_info.h
+++ b/src/data/moves_info.h
@@ -436,10 +436,10 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] =
.accuracy = B_UPDATED_MOVE_DATA >= GEN_3 ? 100 : 75,
#if B_UPDATED_MOVE_DATA >= GEN_4
.criticalHitStage = 1,
- #elif B_UPDATED_MOVE_DATA == GEN_3
- .criticalHitStage = 0,
- #else
+ #elif B_UPDATED_MOVE_DATA == GEN_2
.criticalHitStage = 2,
+ #else
+ .criticalHitStage = 0,
#endif
.pp = 10,
.target = MOVE_TARGET_BOTH,
@@ -471,6 +471,8 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] =
.priority = 0,
.category = DAMAGE_CATEGORY_STATUS,
.zMove = { .effect = Z_EFFECT_RESET_STATS },
+ .ignoresProtect = TRUE,
+ .mirrorMoveBanned = TRUE,
.danceMove = TRUE,
.snatchAffected = TRUE,
.contestEffect = CONTEST_EFFECT_IMPROVE_CONDITION_PREVENT_NERVOUSNESS,
@@ -3184,6 +3186,8 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] =
.priority = 0,
.category = DAMAGE_CATEGORY_STATUS,
.zMove = { .effect = Z_EFFECT_ATK_UP_2 },
+ .ignoresProtect = TRUE,
+ .mirrorMoveBanned = TRUE,
.mimicBanned = TRUE,
.metronomeBanned = B_UPDATED_MOVE_FLAGS >= GEN_4,
.copycatBanned = TRUE,
@@ -4519,7 +4523,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] =
.priority = 0,
.category = DAMAGE_CATEGORY_STATUS,
.zMove = { .effect = Z_EFFECT_DEF_UP_1 },
- .ignoresProtect = (B_UPDATED_MOVE_FLAGS >= GEN_6) || (B_UPDATED_MOVE_FLAGS < GEN_3),
+ .ignoresProtect = B_UPDATED_MOVE_FLAGS < GEN_3,
.magicCoatAffected = TRUE,
.contestEffect = CONTEST_EFFECT_MAKE_FOLLOWING_MONS_NERVOUS,
.contestCategory = CONTEST_CATEGORY_SMART,
@@ -4696,7 +4700,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] =
.priority = 0,
.category = DAMAGE_CATEGORY_STATUS,
.zMove = { .effect = Z_EFFECT_RECOVER_HP },
- .ignoresProtect = B_UPDATED_MOVE_FLAGS >= GEN_3,
+ .ignoresProtect = TRUE,
.ignoresSubstitute = B_UPDATED_MOVE_FLAGS >= GEN_5,
.mirrorMoveBanned = TRUE,
.contestEffect = CONTEST_EFFECT_BETTER_IF_SAME_TYPE,
@@ -4849,6 +4853,8 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] =
.category = DAMAGE_CATEGORY_STATUS,
.argument = { .protectMethod = PROTECT_NORMAL },
.zMove = { .effect = Z_EFFECT_RESET_STATS },
+ .ignoresProtect = TRUE,
+ .mirrorMoveBanned = TRUE,
.metronomeBanned = TRUE,
.copycatBanned = TRUE,
.assistBanned = TRUE,
@@ -7065,6 +7071,8 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] =
.target = MOVE_TARGET_DEPENDS,
.priority = 0,
.category = DAMAGE_CATEGORY_STATUS,
+ .ignoresProtect = TRUE,
+ .mirrorMoveBanned = TRUE,
.metronomeBanned = B_UPDATED_MOVE_FLAGS >= GEN_5,
.copycatBanned = TRUE,
.sleepTalkBanned = TRUE,
@@ -7633,6 +7641,8 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] =
.priority = 4,
.category = DAMAGE_CATEGORY_STATUS,
.zMove = { .effect = Z_EFFECT_SPD_UP_2 },
+ .ignoresProtect = TRUE,
+ .mirrorMoveBanned = TRUE,
.ignoresSubstitute = TRUE,
.forcePressure = TRUE,
.metronomeBanned = TRUE,
diff --git a/src/data/pokemon/form_change_tables.h b/src/data/pokemon/form_change_tables.h
index 6926e20ef9..9e1eaaa6f7 100644
--- a/src/data/pokemon/form_change_tables.h
+++ b/src/data/pokemon/form_change_tables.h
@@ -1316,10 +1316,10 @@ static const struct FormChange sSilvallyFormChangeTable[] =
{FORM_CHANGE_TERMINATOR},
};
#endif //P_FAMILY_TYPE_NULL
-
#if P_FAMILY_MINIOR
static const struct FormChange sMiniorRedFormChangeTable[] =
{
+ {FORM_CHANGE_BEGIN_WILD_ENCOUNTER, SPECIES_MINIOR_METEOR_RED},
{FORM_CHANGE_BEGIN_BATTLE, SPECIES_MINIOR_CORE_RED},
{FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_METEOR_RED, ABILITY_SHIELDS_DOWN, HP_HIGHER_THAN, 50},
{FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_CORE_RED, ABILITY_SHIELDS_DOWN, HP_LOWER_EQ_THAN, 50},
@@ -1330,6 +1330,7 @@ static const struct FormChange sMiniorRedFormChangeTable[] =
};
static const struct FormChange sMiniorBlueFormChangeTable[] =
{
+ {FORM_CHANGE_BEGIN_WILD_ENCOUNTER, SPECIES_MINIOR_METEOR_BLUE},
{FORM_CHANGE_BEGIN_BATTLE, SPECIES_MINIOR_CORE_BLUE},
{FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_METEOR_BLUE, ABILITY_SHIELDS_DOWN, HP_HIGHER_THAN, 50},
{FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_CORE_BLUE, ABILITY_SHIELDS_DOWN, HP_LOWER_EQ_THAN, 50},
@@ -1340,6 +1341,7 @@ static const struct FormChange sMiniorBlueFormChangeTable[] =
};
static const struct FormChange sMiniorGreenFormChangeTable[] =
{
+ {FORM_CHANGE_BEGIN_WILD_ENCOUNTER, SPECIES_MINIOR_METEOR_GREEN},
{FORM_CHANGE_BEGIN_BATTLE, SPECIES_MINIOR_CORE_GREEN},
{FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_METEOR_GREEN, ABILITY_SHIELDS_DOWN, HP_HIGHER_THAN, 50},
{FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_CORE_GREEN, ABILITY_SHIELDS_DOWN, HP_LOWER_EQ_THAN, 50},
@@ -1350,6 +1352,7 @@ static const struct FormChange sMiniorGreenFormChangeTable[] =
};
static const struct FormChange sMiniorIndigoFormChangeTable[] =
{
+ {FORM_CHANGE_BEGIN_WILD_ENCOUNTER, SPECIES_MINIOR_METEOR_INDIGO},
{FORM_CHANGE_BEGIN_BATTLE, SPECIES_MINIOR_CORE_INDIGO},
{FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_METEOR_INDIGO, ABILITY_SHIELDS_DOWN, HP_HIGHER_THAN, 50},
{FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_CORE_INDIGO, ABILITY_SHIELDS_DOWN, HP_LOWER_EQ_THAN, 50},
@@ -1360,6 +1363,7 @@ static const struct FormChange sMiniorIndigoFormChangeTable[] =
};
static const struct FormChange sMiniorOrangeFormChangeTable[] =
{
+ {FORM_CHANGE_BEGIN_WILD_ENCOUNTER, SPECIES_MINIOR_METEOR_ORANGE},
{FORM_CHANGE_BEGIN_BATTLE, SPECIES_MINIOR_CORE_ORANGE},
{FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_METEOR_ORANGE, ABILITY_SHIELDS_DOWN, HP_HIGHER_THAN, 50},
{FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_CORE_ORANGE, ABILITY_SHIELDS_DOWN, HP_LOWER_EQ_THAN, 50},
@@ -1370,6 +1374,7 @@ static const struct FormChange sMiniorOrangeFormChangeTable[] =
};
static const struct FormChange sMiniorVioletFormChangeTable[] =
{
+ {FORM_CHANGE_BEGIN_WILD_ENCOUNTER, SPECIES_MINIOR_METEOR_VIOLET},
{FORM_CHANGE_BEGIN_BATTLE, SPECIES_MINIOR_CORE_VIOLET},
{FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_METEOR_VIOLET, ABILITY_SHIELDS_DOWN, HP_HIGHER_THAN, 50},
{FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_CORE_VIOLET, ABILITY_SHIELDS_DOWN, HP_LOWER_EQ_THAN, 50},
@@ -1379,6 +1384,7 @@ static const struct FormChange sMiniorVioletFormChangeTable[] =
{FORM_CHANGE_TERMINATOR},
};
static const struct FormChange sMiniorYellowFormChangeTable[] = {
+ {FORM_CHANGE_BEGIN_WILD_ENCOUNTER, SPECIES_MINIOR_METEOR_YELLOW},
{FORM_CHANGE_BEGIN_BATTLE, SPECIES_MINIOR_CORE_YELLOW},
{FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_METEOR_YELLOW, ABILITY_SHIELDS_DOWN, HP_HIGHER_THAN, 50},
{FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_CORE_YELLOW, ABILITY_SHIELDS_DOWN, HP_LOWER_EQ_THAN, 50},
diff --git a/src/debug.c b/src/debug.c
index 3f1a53c5b9..333ec4bf20 100644
--- a/src/debug.c
+++ b/src/debug.c
@@ -2425,10 +2425,11 @@ static void DebugAction_Give_Pokemon_SelectShiny(u8 taskId)
}
}
-static void Debug_Display_Ability(enum Ability abilityId, u32 digit, u8 windowId)//(u32 natureId, u32 digit, u8 windowId)
+static void Debug_Display_Ability(u32 abilityNum, u32 digit, u8 windowId)//(u32 natureId, u32 digit, u8 windowId)
{
+ enum Ability abilityId = GetAbilityBySpecies(sDebugMonData->species, abilityNum);
StringCopy(gStringVar2, gText_DigitIndicator[digit]);
- ConvertIntToDecimalStringN(gStringVar3, abilityId, STR_CONV_MODE_LEADING_ZEROS, 2);
+ ConvertIntToDecimalStringN(gStringVar3, abilityNum, STR_CONV_MODE_LEFT_ALIGN, 2);
StringCopyPadded(gStringVar3, gStringVar3, CHAR_SPACE, 15);
u8 *end = StringCopy(gStringVar1, gAbilitiesInfo[abilityId].name);
WrapFontIdToFit(gStringVar1, end, DEBUG_MENU_FONT, WindowWidthPx(windowId));
@@ -2464,8 +2465,7 @@ static void DebugAction_Give_Pokemon_SelectNature(u8 taskId)
gTasks[taskId].tInput = 0;
gTasks[taskId].tDigit = 0;
- enum Ability abilityId = GetAbilityBySpecies(sDebugMonData->species, 0);
- Debug_Display_Ability(abilityId, gTasks[taskId].tDigit, gTasks[taskId].tSubWindowId);
+ Debug_Display_Ability(0, gTasks[taskId].tDigit, gTasks[taskId].tSubWindowId);
gTasks[taskId].func = DebugAction_Give_Pokemon_SelectAbility;
}
@@ -2489,8 +2489,7 @@ static void Debug_Display_TeraType(u32 typeId, u32 digit, u8 windowId)
static void DebugAction_Give_Pokemon_SelectAbility(u8 taskId)
{
- u8 abilityCount = NUM_ABILITY_SLOTS - 1; //-1 for proper iteration
- u8 i = 0;
+ s32 abilityNum = -1;
if (JOY_NEW(DPAD_ANY))
{
@@ -2498,28 +2497,31 @@ static void DebugAction_Give_Pokemon_SelectAbility(u8 taskId)
if (JOY_NEW(DPAD_UP))
{
- gTasks[taskId].tInput += sPowersOfTen[gTasks[taskId].tDigit];
- if (gTasks[taskId].tInput > abilityCount)
- gTasks[taskId].tInput = abilityCount;
+ abilityNum = gTasks[taskId].tInput + 1;
+ while (GetSpeciesAbility(sDebugMonData->species, abilityNum) == ABILITY_NONE && abilityNum < NUM_ABILITY_SLOTS)
+ {
+ abilityNum++;
+ }
}
if (JOY_NEW(DPAD_DOWN))
{
- gTasks[taskId].tInput -= sPowersOfTen[gTasks[taskId].tDigit];
- if (gTasks[taskId].tInput < 0)
- gTasks[taskId].tInput = 0;
+ abilityNum = gTasks[taskId].tInput - 1;
+ while (GetSpeciesAbility(sDebugMonData->species, abilityNum) == ABILITY_NONE && abilityNum >= 0)
+ {
+ abilityNum--;
+ }
}
- while (GetAbilityBySpecies(sDebugMonData->species, gTasks[taskId].tInput - i) == ABILITY_NONE && gTasks[taskId].tInput - i < NUM_ABILITY_SLOTS)
+ if (abilityNum >= 0 && abilityNum < NUM_ABILITY_SLOTS)
{
- i++;
+ gTasks[taskId].tInput = abilityNum;
+ Debug_Display_Ability(abilityNum, gTasks[taskId].tDigit, gTasks[taskId].tSubWindowId);
}
- enum Ability abilityId = GetAbilityBySpecies(sDebugMonData->species, gTasks[taskId].tInput - i);
- Debug_Display_Ability(abilityId, gTasks[taskId].tDigit, gTasks[taskId].tSubWindowId);
}
if (JOY_NEW(A_BUTTON))
{
- sDebugMonData->abilityNum = gTasks[taskId].tInput - i;
+ sDebugMonData->abilityNum = gTasks[taskId].tInput;
gTasks[taskId].tInput = 0;
gTasks[taskId].tDigit = 0;
diff --git a/src/fake_rtc.c b/src/fake_rtc.c
index 2024065b7d..c6b68d9105 100644
--- a/src/fake_rtc.c
+++ b/src/fake_rtc.c
@@ -50,6 +50,9 @@ void FakeRtc_TickTimeForward(void)
void FakeRtc_AdvanceTimeBy(u32 days, u32 hours, u32 minutes, u32 seconds)
{
+ if (!OW_USE_FAKE_RTC)
+ return;
+
struct DateTime dateTime;
struct SiiRtcInfo *rtc = FakeRtc_GetCurrentTime();
@@ -63,6 +66,9 @@ void FakeRtc_AdvanceTimeBy(u32 days, u32 hours, u32 minutes, u32 seconds)
void FakeRtc_ForwardTimeTo(u32 hour, u32 minute, u32 second)
{
+ if (!OW_USE_FAKE_RTC)
+ return;
+
Script_PauseFakeRtc();
struct Time diff, target;
struct SiiRtcInfo *fakeRtc = FakeRtc_GetCurrentTime();
diff --git a/src/field_effect.c b/src/field_effect.c
index 9127e6b0ea..14194c8ac2 100644
--- a/src/field_effect.c
+++ b/src/field_effect.c
@@ -803,14 +803,22 @@ void FieldEffectScript_LoadTiles(u8 **script)
(*script) += 4;
}
+static bool32 ShouldFieldEffectBeFogBlended(u8 *script)
+{
+ u32 ptr = FieldEffectScript_ReadWord(&script);
+ if (ptr == (u32)FldEff_TallGrass)
+ return FALSE;
+ return TRUE;
+}
+
void FieldEffectScript_LoadFadedPalette(u8 **script)
{
struct SpritePalette *palette = (struct SpritePalette *)FieldEffectScript_ReadWord(script);
u32 paletteSlot = LoadSpritePalette(palette);
(*script) += 4;
SetPaletteColorMapType(paletteSlot + 16, T1_READ_8(*script));
- UpdateSpritePaletteWithWeather(paletteSlot, TRUE);
(*script)++;
+ UpdateSpritePaletteWithWeather(paletteSlot, ShouldFieldEffectBeFogBlended(*script));
}
void FieldEffectScript_LoadPalette(u8 **script)
diff --git a/src/frontier_util.c b/src/frontier_util.c
index 8d1a3845b7..ea2feb5444 100644
--- a/src/frontier_util.c
+++ b/src/frontier_util.c
@@ -138,7 +138,7 @@ const struct FrontierBrain gFrontierBrainInfo[NUM_FRONTIER_FACILITIES] =
COMPOUND_STRING("我巨蛋超级巨星\n可不是浪得虚名!") //Gold
},
.battledBit = {1 << 2, 1 << 3},
- .streakAppearances = {1, 2, 5, 0},
+ .streakAppearances = {4, 9, 5, 0},
},
[FRONTIER_FACILITY_PALACE] =
{
diff --git a/src/pokemon.c b/src/pokemon.c
index 416265b768..869125b46f 100644
--- a/src/pokemon.c
+++ b/src/pokemon.c
@@ -6970,6 +6970,7 @@ u32 GetFormChangeTargetSpeciesBoxMon(struct BoxPokemon *boxMon, enum FormChanges
case FORM_CHANGE_DEPOSIT:
case FORM_CHANGE_FAINT:
case FORM_CHANGE_DAYS_PASSED:
+ case FORM_CHANGE_BEGIN_WILD_ENCOUNTER:
targetSpecies = formChanges[i].targetSpecies;
break;
case FORM_CHANGE_STATUS:
diff --git a/src/reshow_battle_screen.c b/src/reshow_battle_screen.c
index d56f910dda..fa8e1037fd 100644
--- a/src/reshow_battle_screen.c
+++ b/src/reshow_battle_screen.c
@@ -324,7 +324,7 @@ void CreateBattlerSprite(u32 battler)
gBattlerSpriteIds[battler] = CreateSprite(&gMultiuseSpriteTemplate, 0x50,
(8 - gTrainerBacksprites[gSaveBlock2Ptr->playerGender].coordinates.size) * 4 + 80,
GetBattlerSpriteSubpriority(0));
- gSprites[gBattlerSpriteIds[battler]].oam.paletteNum = (8 + battler / 2);
+ gSprites[gBattlerSpriteIds[battler]].oam.paletteNum = battler;
gSprites[gBattlerSpriteIds[battler]].callback = SpriteCallbackDummy;
gSprites[gBattlerSpriteIds[battler]].data[0] = battler;
}
@@ -334,7 +334,7 @@ void CreateBattlerSprite(u32 battler)
gBattlerSpriteIds[battler] = CreateSprite(&gMultiuseSpriteTemplate, 0x50,
(8 - gTrainerBacksprites[TRAINER_BACK_PIC_WALLY].coordinates.size) * 4 + 80,
GetBattlerSpriteSubpriority(0));
- gSprites[gBattlerSpriteIds[battler]].oam.paletteNum = (8 + battler / 2);
+ gSprites[gBattlerSpriteIds[battler]].oam.paletteNum = battler;
gSprites[gBattlerSpriteIds[battler]].callback = SpriteCallbackDummy;
gSprites[gBattlerSpriteIds[battler]].data[0] = battler;
}
diff --git a/src/rtc.c b/src/rtc.c
index ace7bfb0c4..adab416446 100644
--- a/src/rtc.c
+++ b/src/rtc.c
@@ -349,7 +349,8 @@ void RtcCalcLocalTimeOffset(s32 days, s32 hours, s32 minutes, s32 seconds)
gLocalTime.hours = hours;
gLocalTime.minutes = minutes;
gLocalTime.seconds = seconds;
- FakeRtc_ManuallySetTime(gLocalTime.days, gLocalTime.hours, gLocalTime.minutes, seconds);
+ if (!OW_USE_FAKE_RTC)
+ FakeRtc_ManuallySetTime(gLocalTime.days, gLocalTime.hours, gLocalTime.minutes, seconds);
RtcGetInfo(&sRtc);
RtcCalcTimeDifference(&sRtc, &gSaveBlock2Ptr->localTimeOffset, &gLocalTime);
}
diff --git a/src/scrcmd.c b/src/scrcmd.c
index e05190c18d..7983f54b43 100644
--- a/src/scrcmd.c
+++ b/src/scrcmd.c
@@ -3239,6 +3239,9 @@ bool8 ScrCmd_fwdtime(struct ScriptContext *ctx)
bool8 ScrCmd_fwdweekday(struct ScriptContext *ctx)
{
+ if (!OW_USE_FAKE_RTC)
+ return FALSE;
+
struct SiiRtcInfo *rtc = FakeRtc_GetCurrentTime();
u32 weekdayTarget = ScriptReadWord(ctx);
diff --git a/test/battle/ability/adaptability.c b/test/battle/ability/adaptability.c
index 876dac212c..ba54590cd2 100644
--- a/test/battle/ability/adaptability.c
+++ b/test/battle/ability/adaptability.c
@@ -61,4 +61,39 @@ SINGLE_BATTLE_TEST("(TERA) Terastallizing into the same type with Adaptability g
}
}
-TO_DO_BATTLE_TEST("Adaptability does not affect Stellar-type moves");
+SINGLE_BATTLE_TEST("(TERA) Adaptability does not increase non-Tera base STAB beyond 1.5x", s16 damage)
+{
+ u32 move;
+ PARAMETRIZE { move = MOVE_GUST; }
+ PARAMETRIZE { move = MOVE_WATER_GUN; }
+ GIVEN {
+ PLAYER(SPECIES_CRAWDAUNT) { Ability(ABILITY_ADAPTABILITY); TeraType(TYPE_NORMAL); }
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { MOVE(player, move, gimmick: GIMMICK_TERA); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, move, player);
+ HP_BAR(opponent, captureDamage: &results[i].damage);
+ } FINALLY {
+ // With Adaptability, non-Tera base type should still be 1.5x STAB (not 2.0x).
+ EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), results[1].damage);
+ }
+}
+
+SINGLE_BATTLE_TEST("(TERA) Adaptability does not affect Stellar-type moves", s16 damage)
+{
+ u32 ability;
+ PARAMETRIZE { ability = ABILITY_HYPER_CUTTER; }
+ PARAMETRIZE { ability = ABILITY_ADAPTABILITY; }
+ GIVEN {
+ PLAYER(SPECIES_CRAWDAUNT) { Ability(ability); TeraType(TYPE_STELLAR); }
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { MOVE(player, MOVE_TERA_BLAST, gimmick: GIMMICK_TERA); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_TERA_BLAST, player);
+ HP_BAR(opponent, captureDamage: &results[i].damage);
+ } FINALLY {
+ EXPECT_EQ(results[0].damage, results[1].damage);
+ }
+}
diff --git a/test/battle/ability/commander.c b/test/battle/ability/commander.c
index e33c4e1521..93a8021877 100644
--- a/test/battle/ability/commander.c
+++ b/test/battle/ability/commander.c
@@ -380,6 +380,7 @@ DOUBLE_BATTLE_TEST("Commander Tatsugiri does not attack if Dondozo faints the sa
DOUBLE_BATTLE_TEST("Commander Tatsugiri does not get hit by Dragon Darts when a commanded Dondozo faints")
{
GIVEN {
+ KNOWN_FAILING;
ASSUME(GetMoveEffect(MOVE_DRAGON_DARTS) == EFFECT_DRAGON_DARTS);
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_DONDOZO) { HP(1); }
@@ -474,3 +475,35 @@ DOUBLE_BATTLE_TEST("Commander will not activate if partner Dondozo is about to s
NOT ABILITY_POPUP(playerRight, ABILITY_COMMANDER);
}
}
+
+DOUBLE_BATTLE_TEST("Commander clears when Dondozo is replaced and Tatsugiri can be hit")
+{
+ GIVEN {
+ ASSUME(GetMoveEffect(MOVE_VOLT_SWITCH) == EFFECT_HIT_ESCAPE);
+ PLAYER(SPECIES_DONDOZO) { HP(1); Speed(1); }
+ PLAYER(SPECIES_TATSUGIRI) { Ability(ABILITY_COMMANDER); MaxHP(400); HP(400); Speed(2); }
+ PLAYER(SPECIES_SEADRA) { Speed(3); }
+ OPPONENT(SPECIES_VENUSAUR) { Speed(5); }
+ OPPONENT(SPECIES_LUXRAY) { Speed(6); }
+ OPPONENT(SPECIES_BUTTERFREE) { Speed(4); }
+ } WHEN {
+ TURN {
+ MOVE(opponentLeft, MOVE_SEED_BOMB, target: playerRight);
+ MOVE(opponentRight, MOVE_VOLT_SWITCH, target: playerLeft);
+ SEND_OUT(opponentRight, 2);
+ SEND_OUT(playerLeft, 2);
+ }
+ TURN {
+ MOVE(opponentRight, MOVE_BUG_BUZZ, target: playerRight);
+ }
+ } SCENE {
+ ABILITY_POPUP(playerRight, ABILITY_COMMANDER);
+ MESSAGE("Tatsugiri was swallowed by Dondozo and became Dondozo's commander!");
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_VOLT_SWITCH, opponentRight);
+ MESSAGE("Dondozo fainted!");
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_SEED_BOMB, opponentLeft);
+ HP_BAR(playerRight);
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_BUG_BUZZ, opponentRight);
+ HP_BAR(playerRight);
+ }
+}
diff --git a/test/battle/ability/dazzling.c b/test/battle/ability/dazzling.c
index f54f024452..26279fa360 100644
--- a/test/battle/ability/dazzling.c
+++ b/test/battle/ability/dazzling.c
@@ -163,8 +163,8 @@ SINGLE_BATTLE_TEST("Dazzling, Queenly Majesty and Armor Tail do not block Teatim
GIVEN {
ASSUME(GetMoveEffect(MOVE_TEATIME) == EFFECT_TEATIME);
ASSUME(GetItemHoldEffect(ITEM_ORAN_BERRY) == HOLD_EFFECT_RESTORE_HP);
- PLAYER(SPECIES_MURKROW) { Ability(ABILITY_PRANKSTER); Item(ITEM_ORAN_BERRY); HP(1); MaxHP(100); }
- OPPONENT(species) { Ability(ability); Item(ITEM_ORAN_BERRY); HP(1); MaxHP(100); }
+ PLAYER(SPECIES_MURKROW) { Ability(ABILITY_PRANKSTER); Item(ITEM_ORAN_BERRY); HP(60); MaxHP(100); }
+ OPPONENT(species) { Ability(ability); Item(ITEM_ORAN_BERRY); HP(60); MaxHP(100); }
} WHEN {
TURN { MOVE(player, MOVE_TEATIME); }
} SCENE {
diff --git a/test/battle/ability/fluffy.c b/test/battle/ability/fluffy.c
index 68afbd8993..964e7d9b06 100644
--- a/test/battle/ability/fluffy.c
+++ b/test/battle/ability/fluffy.c
@@ -70,6 +70,7 @@ SINGLE_BATTLE_TEST("Fluffy halves damage taken from moves that make direct conta
PARAMETRIZE { ability = ABILITY_KLUTZ; }
PARAMETRIZE { ability = ABILITY_FLUFFY; }
GIVEN {
+ ASSUME(MoveMakesContact(MOVE_THUNDER_PUNCH));
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_PROTECTIVE_PADS); }
OPPONENT(SPECIES_STUFFUL) { Ability(ability); }
} WHEN {
@@ -88,6 +89,8 @@ SINGLE_BATTLE_TEST("Fluffy does not halve damage taken from moves that make dire
PARAMETRIZE { ability = ABILITY_KLUTZ; }
PARAMETRIZE { ability = ABILITY_FLUFFY; }
GIVEN {
+ ASSUME(MoveMakesContact(MOVE_THUNDER_PUNCH));
+ ASSUME(IsPunchingMove(MOVE_THUNDER_PUNCH));
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_PUNCHING_GLOVE); }
OPPONENT(SPECIES_STUFFUL) { Ability(ability); }
} WHEN {
@@ -99,3 +102,22 @@ SINGLE_BATTLE_TEST("Fluffy does not halve damage taken from moves that make dire
EXPECT_EQ(results[0].damage, results[1].damage);
}
}
+
+SINGLE_BATTLE_TEST("Fluffy does not halve damage taken from moves that make direct contact but are ignored by Long Reach", s16 damage)
+{
+ enum Ability ability;
+ PARAMETRIZE { ability = ABILITY_KLUTZ; }
+ PARAMETRIZE { ability = ABILITY_FLUFFY; }
+ GIVEN {
+ ASSUME(MoveMakesContact(MOVE_THUNDER_PUNCH));
+ PLAYER(SPECIES_ROWLET) { Ability(ABILITY_LONG_REACH); }
+ OPPONENT(SPECIES_STUFFUL) { Ability(ability); }
+ } WHEN {
+ TURN { MOVE(player, MOVE_THUNDER_PUNCH); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_THUNDER_PUNCH, player);
+ HP_BAR(opponent, captureDamage: &results[i].damage);
+ } FINALLY {
+ EXPECT_EQ(results[0].damage, results[1].damage);
+ }
+}
diff --git a/test/battle/ability/gulp_missile.c b/test/battle/ability/gulp_missile.c
index 5a3266ff17..89c6d904db 100644
--- a/test/battle/ability/gulp_missile.c
+++ b/test/battle/ability/gulp_missile.c
@@ -201,3 +201,30 @@ SINGLE_BATTLE_TEST("Gulp Missile triggered by explosion doesn't freeze the game"
TURN { MOVE(opponent, MOVE_SURF); MOVE(player, MOVE_EXPLOSION); }
}
}
+
+SINGLE_BATTLE_TEST("(Gulp Missile) Cramorant in Gorging damages an electric type without paralysing")
+{
+ GIVEN {
+ PLAYER(SPECIES_CRAMORANT) { HP(120); MaxHP(250); Ability(ABILITY_GULP_MISSILE); }
+ OPPONENT(SPECIES_EELEKTROSS);
+ } WHEN {
+ TURN { MOVE(player, MOVE_SURF); MOVE(opponent, MOVE_SCRATCH); }
+ TURN { MOVE(player, MOVE_SCRATCH); MOVE(opponent, MOVE_SCRATCH); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_SURF, player);
+ HP_BAR(opponent);
+ ABILITY_POPUP(player, ABILITY_GULP_MISSILE);
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, opponent);
+ HP_BAR(player);
+ ABILITY_POPUP(player, ABILITY_GULP_MISSILE);
+ HP_BAR(opponent);
+ NONE_OF {
+ ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PRZ, opponent);
+ STATUS_ICON(opponent, paralysis: TRUE);
+ }
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, player);
+ HP_BAR(opponent);
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, opponent);
+ HP_BAR(player);
+ }
+}
diff --git a/test/battle/ability/hadron_engine.c b/test/battle/ability/hadron_engine.c
index e6324c0c51..0de50c94ec 100644
--- a/test/battle/ability/hadron_engine.c
+++ b/test/battle/ability/hadron_engine.c
@@ -1,4 +1,45 @@
#include "global.h"
#include "test/battle.h"
-TO_DO_BATTLE_TEST("TODO: Write Hadron Engine (Ability) test titles")
+SINGLE_BATTLE_TEST("Hadron Engine creates Electric Terrain when entering the battle")
+{
+ GIVEN {
+ PLAYER(SPECIES_MIRAIDON) { Ability(ABILITY_HADRON_ENGINE); }
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN {}
+ } SCENE {
+ ABILITY_POPUP(player, ABILITY_HADRON_ENGINE);
+ MESSAGE("An electric current ran across the battlefield!");
+ }
+}
+
+SINGLE_BATTLE_TEST("Hadron Engine boosts the Pokemon's Special Attack on Electric Terrain even if not grounded", s16 damage)
+{
+ bool32 overrideTerrain, airBalloon;
+
+ PARAMETRIZE { airBalloon = FALSE; overrideTerrain = TRUE; }
+ PARAMETRIZE { airBalloon = FALSE; overrideTerrain = FALSE; }
+ PARAMETRIZE { airBalloon = TRUE; overrideTerrain = TRUE; }
+ PARAMETRIZE { airBalloon = TRUE; overrideTerrain = FALSE; }
+
+ GIVEN {
+ ASSUME(gItemsInfo[ITEM_AIR_BALLOON].holdEffect == HOLD_EFFECT_AIR_BALLOON);
+ ASSUME(GetMoveEffect(MOVE_GRASSY_TERRAIN) == EFFECT_GRASSY_TERRAIN);
+ ASSUME(GetMoveCategory(MOVE_POWER_GEM) == DAMAGE_CATEGORY_SPECIAL);
+ PLAYER(SPECIES_MIRAIDON) { Ability(ABILITY_HADRON_ENGINE); Moves(MOVE_POWER_GEM, MOVE_CELEBRATE); Item(airBalloon ? ITEM_AIR_BALLOON : ITEM_NONE); Speed(1); }
+ OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_GRASSY_TERRAIN, MOVE_CELEBRATE); Speed(2); }
+ } WHEN {
+ if (overrideTerrain)
+ TURN { MOVE(opponent, MOVE_GRASSY_TERRAIN); }
+ TURN { MOVE(player, MOVE_POWER_GEM); }
+ } SCENE {
+ if (overrideTerrain)
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_GRASSY_TERRAIN, opponent);
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_POWER_GEM, player);
+ HP_BAR(opponent, captureDamage: &results[i].damage);
+ } FINALLY {
+ EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.3333), results[1].damage);
+ EXPECT_MUL_EQ(results[2].damage, Q_4_12(1.3333), results[3].damage);
+ }
+}
diff --git a/test/battle/ability/heavy_metal.c b/test/battle/ability/heavy_metal.c
index baaa039b19..2257b5d068 100644
--- a/test/battle/ability/heavy_metal.c
+++ b/test/battle/ability/heavy_metal.c
@@ -1,4 +1,4 @@
#include "global.h"
#include "test/battle.h"
-TO_DO_BATTLE_TEST("TODO: Write Heavy Metal (Ability) test titles")
+// Tests for Heavy Metal are handled in test/battle/ability/light_metal.c
diff --git a/test/battle/ability/light_metal.c b/test/battle/ability/light_metal.c
index 8ad4a6a4b5..d09204c25d 100644
--- a/test/battle/ability/light_metal.c
+++ b/test/battle/ability/light_metal.c
@@ -1,4 +1,30 @@
#include "global.h"
#include "test/battle.h"
-TO_DO_BATTLE_TEST("TODO: Write Light Metal (Ability) test titles")
+SINGLE_BATTLE_TEST("Light Metal and Heavy Metal affect the power of Low Kick", s16 damage)
+{
+ enum Ability ability;
+ PARAMETRIZE { ability = ABILITY_LIGHT_METAL; } // 10.0 - 24.9 kg (40 power)
+ PARAMETRIZE { ability = ABILITY_STALWART; } // 25.0 - 49.9 kg (60 power)
+ PARAMETRIZE { ability = ABILITY_HEAVY_METAL; } // 50.0 - 99.9 kg (80 power)
+
+ GIVEN {
+ ASSUME(GetMoveEffect(MOVE_SOAK) == EFFECT_SOAK);
+ ASSUME(GetMoveArgType(MOVE_SOAK) == TYPE_WATER);
+ ASSUME(GetSpeciesWeight(SPECIES_DURALUDON) == 400);
+ PLAYER(SPECIES_WOBBUFFET);
+ OPPONENT(SPECIES_DURALUDON) { Ability(ability); }
+ } WHEN {
+ TURN { MOVE(player, MOVE_SOAK); } // To remove super-effectiveness, as it was messing with calculations.
+ TURN { MOVE(player, MOVE_LOW_KICK); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_LOW_KICK, player);
+ HP_BAR(opponent, captureDamage: &results[i].damage);
+ } THEN {
+ // Calc 20 power increase, with the first iteration being 40 power
+ if (i != 0)
+ EXPECT_MUL_EQ(results[0].damage, Q_4_12((i * 0.5) + 1), results[i].damage);
+ }
+}
+
+TO_DO_BATTLE_TEST("Light Metal and Heavy Metal don't affect Heavy Ball's multiplier")
diff --git a/test/battle/ability/pickpocket.c b/test/battle/ability/pickpocket.c
index e6b92a6e8e..559a8e2a21 100644
--- a/test/battle/ability/pickpocket.c
+++ b/test/battle/ability/pickpocket.c
@@ -310,3 +310,32 @@ SINGLE_BATTLE_TEST("Pickpocket does not prevent King's Rock or Razor Fang flinch
EXPECT(player->item == ITEM_NONE);
}
}
+
+SINGLE_BATTLE_TEST("Pickpocket activates when user has Protective Pads, but not with Punching Glove or Long Reach")
+{
+ u32 item, ability;
+
+ PARAMETRIZE { item = ITEM_PROTECTIVE_PADS; ability = ABILITY_OVERGROW; }
+ PARAMETRIZE { item = ITEM_PUNCHING_GLOVE; ability = ABILITY_OVERGROW; }
+ PARAMETRIZE { item = ITEM_NONE; ability = ABILITY_LONG_REACH; }
+
+ GIVEN {
+ ASSUME(MoveMakesContact(MOVE_MACH_PUNCH));
+ ASSUME(IsPunchingMove(MOVE_MACH_PUNCH));
+ ASSUME(GetItemHoldEffect(ITEM_PROTECTIVE_PADS) == HOLD_EFFECT_PROTECTIVE_PADS);
+ ASSUME(GetItemHoldEffect(ITEM_PUNCHING_GLOVE) == HOLD_EFFECT_PUNCHING_GLOVE);
+ ASSUME(GetItemHoldEffect(ITEM_FOCUS_SASH) == HOLD_EFFECT_FOCUS_SASH);
+ PLAYER(SPECIES_DECIDUEYE) { Ability(ability); Item(item); }
+ OPPONENT(SPECIES_SNEASEL) { Ability(ABILITY_PICKPOCKET); Item(ITEM_FOCUS_SASH); }
+ } WHEN {
+ TURN { MOVE(player, MOVE_MACH_PUNCH); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_MACH_PUNCH, player);
+
+ if (item == ITEM_PROTECTIVE_PADS) {
+ ABILITY_POPUP(opponent, ABILITY_PICKPOCKET);
+ } else {
+ NOT ABILITY_POPUP(opponent, ABILITY_PICKPOCKET);
+ }
+ }
+}
diff --git a/test/battle/ability/poison_touch.c b/test/battle/ability/poison_touch.c
index e3775d7427..b0c8c73329 100644
--- a/test/battle/ability/poison_touch.c
+++ b/test/battle/ability/poison_touch.c
@@ -75,3 +75,38 @@ SINGLE_BATTLE_TEST("Poison Touch applies between multi-hit move hits")
STATUS_ICON(opponent, poison: TRUE);
}
}
+
+SINGLE_BATTLE_TEST("Poison Touch activates when user has Protective Pads, but not with Punching Glove")
+{
+ u32 item;
+
+ PARAMETRIZE { item = ITEM_PROTECTIVE_PADS; }
+ PARAMETRIZE { item = ITEM_PUNCHING_GLOVE; }
+
+ GIVEN {
+ ASSUME(MoveMakesContact(MOVE_MACH_PUNCH));
+ ASSUME(IsPunchingMove(MOVE_MACH_PUNCH));
+ ASSUME(GetItemHoldEffect(ITEM_PROTECTIVE_PADS) == HOLD_EFFECT_PROTECTIVE_PADS);
+ ASSUME(GetItemHoldEffect(ITEM_PUNCHING_GLOVE) == HOLD_EFFECT_PUNCHING_GLOVE);
+ PLAYER(SPECIES_GRIMER) { Ability(ABILITY_POISON_TOUCH); Item(item); }
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { MOVE(player, MOVE_MACH_PUNCH); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_MACH_PUNCH, player);
+
+ if (item != ITEM_PUNCHING_GLOVE) {
+ ABILITY_POPUP(player, ABILITY_POISON_TOUCH);
+ ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, opponent);
+ MESSAGE("The opposing Wobbuffet was poisoned by Grimer's Poison Touch!");
+ STATUS_ICON(opponent, poison: TRUE);
+ } else {
+ NONE_OF {
+ ABILITY_POPUP(player, ABILITY_POISON_TOUCH);
+ ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, opponent);
+ MESSAGE("The opposing Wobbuffet was poisoned by Grimer's Poison Touch!");
+ STATUS_ICON(opponent, poison: TRUE);
+ }
+ }
+ }
+}
diff --git a/test/battle/ability/shields_down.c b/test/battle/ability/shields_down.c
index d16aa240da..ca1ade2c37 100644
--- a/test/battle/ability/shields_down.c
+++ b/test/battle/ability/shields_down.c
@@ -75,3 +75,39 @@ SINGLE_BATTLE_TEST("Shields Down protects Minior Meteor from status conditions")
EXPECT(opponent->status1 & STATUS1_BURN);
}
}
+
+WILD_BATTLE_TEST("Wild Minior appear in Meteor form without transforming")// To be replaced with WILD_DOUBLE_BATTLE_TEST when that is made possible.
+{
+ GIVEN {
+ PLAYER(SPECIES_MINIOR_CORE) { Ability(ABILITY_SHIELDS_DOWN); }
+ OPPONENT(SPECIES_MINIOR_CORE) { Ability(ABILITY_SHIELDS_DOWN); }
+ } WHEN {
+ TURN {}
+ } SCENE {
+ ABILITY_POPUP(player, ABILITY_SHIELDS_DOWN);
+ ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE, player);
+ NONE_OF {
+ ABILITY_POPUP(opponent, ABILITY_SHIELDS_DOWN);
+ ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE, opponent);
+ }
+ } THEN {
+ EXPECT_EQ(opponent->species, SPECIES_MINIOR_METEOR);
+ EXPECT_EQ(player->species, SPECIES_MINIOR_METEOR);
+ }
+}
+
+SINGLE_BATTLE_TEST("Trainers' Minior appear in Core form")
+{
+ GIVEN {
+ PLAYER(SPECIES_WOBBUFFET)
+ OPPONENT(SPECIES_MINIOR_METEOR) { Ability(ABILITY_SHIELDS_DOWN); }
+ } WHEN {
+ TURN {}
+ } SCENE {
+ ABILITY_POPUP(opponent, ABILITY_SHIELDS_DOWN);
+ ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE, opponent);
+ } THEN {
+ EXPECT_EQ(opponent->species, SPECIES_MINIOR_METEOR);
+ }
+}
+
diff --git a/test/battle/ability/tough_claws.c b/test/battle/ability/tough_claws.c
index 4e6f4ecf8b..f2dd887fe4 100644
--- a/test/battle/ability/tough_claws.c
+++ b/test/battle/ability/tough_claws.c
@@ -2,3 +2,27 @@
#include "test/battle.h"
TO_DO_BATTLE_TEST("TODO: Write Tough Claws (Ability) test titles")
+
+SINGLE_BATTLE_TEST("Tough Claws boosts contact moves when user has Protective Pads, but not with Punching Glove", s16 damage)
+{
+ u32 item;
+
+ PARAMETRIZE { item = ITEM_PROTECTIVE_PADS; }
+ PARAMETRIZE { item = ITEM_PUNCHING_GLOVE; }
+
+ GIVEN {
+ ASSUME(MoveMakesContact(MOVE_MACH_PUNCH));
+ ASSUME(IsPunchingMove(MOVE_MACH_PUNCH));
+ ASSUME(GetItemHoldEffect(ITEM_PROTECTIVE_PADS) == HOLD_EFFECT_PROTECTIVE_PADS);
+ ASSUME(GetItemHoldEffect(ITEM_PUNCHING_GLOVE) == HOLD_EFFECT_PUNCHING_GLOVE);
+ PLAYER(SPECIES_BARBARACLE) { Ability(ABILITY_TOUGH_CLAWS); Item(item); }
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { MOVE(player, MOVE_MACH_PUNCH); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_MACH_PUNCH, player);
+ HP_BAR(opponent, captureDamage: &results[i].damage);
+ } FINALLY {
+ EXPECT_MUL_EQ(results[1].damage, UQ_4_12(1.18), results[0].damage); // 1.3 / 1.1 ~= 1.18
+ }
+}
diff --git a/test/battle/ability/unseen_fist.c b/test/battle/ability/unseen_fist.c
index 046ef7d2fb..da5880c042 100644
--- a/test/battle/ability/unseen_fist.c
+++ b/test/battle/ability/unseen_fist.c
@@ -1,4 +1,35 @@
#include "global.h"
#include "test/battle.h"
+ASSUMPTIONS
+{
+ ASSUME(MoveMakesContact(MOVE_SCRATCH));
+ ASSUME(GetMoveEffect(MOVE_PROTECT) == EFFECT_PROTECT);
+}
+
TO_DO_BATTLE_TEST("TODO: Write Unseen Fist (Ability) test titles")
+
+SINGLE_BATTLE_TEST("Unseen Fist ignores Protect when user has Protective Pads, but not with Punching Glove", s16 damage)
+{
+ u32 item;
+
+ PARAMETRIZE { item = ITEM_PROTECTIVE_PADS; }
+ PARAMETRIZE { item = ITEM_PUNCHING_GLOVE; }
+
+ GIVEN {
+ ASSUME(MoveMakesContact(MOVE_MACH_PUNCH));
+ ASSUME(IsPunchingMove(MOVE_MACH_PUNCH));
+ ASSUME(GetItemHoldEffect(ITEM_PROTECTIVE_PADS) == HOLD_EFFECT_PROTECTIVE_PADS);
+ ASSUME(GetItemHoldEffect(ITEM_PUNCHING_GLOVE) == HOLD_EFFECT_PUNCHING_GLOVE);
+ PLAYER(SPECIES_URSHIFU) { Ability(ABILITY_UNSEEN_FIST); Item(item); }
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { MOVE(opponent, MOVE_PROTECT); MOVE(player, MOVE_MACH_PUNCH); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_PROTECT, opponent);
+ if (item != ITEM_PUNCHING_GLOVE)
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_MACH_PUNCH, player);
+ else
+ NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_MACH_PUNCH, player);
+ }
+}
diff --git a/test/battle/ai/ai_switching.c b/test/battle/ai/ai_switching.c
index 7d7cf7561f..af67f4d051 100644
--- a/test/battle/ai/ai_switching.c
+++ b/test/battle/ai/ai_switching.c
@@ -1030,6 +1030,21 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will switch out if player's m
}
}
+AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will switch out on turn 1 if it predicts a semi-invulnerable move and it has a good switchin")
+{
+ PASSES_RANDOMLY(PREDICT_MOVE_CHANCE, 100, RNG_AI_PREDICT_MOVE);
+ PASSES_RANDOMLY(SHOULD_SWITCH_FREE_TURN_PERCENTAGE, 100, RNG_AI_SWITCH_FREE_TURN);
+ GIVEN {
+ ASSUME(GetMoveType(MOVE_DIVE) == TYPE_WATER);
+ AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_SWITCHING | AI_FLAG_OMNISCIENT | AI_FLAG_PREDICT_MOVE);
+ PLAYER(SPECIES_LUVDISC) { Level(1); Moves(MOVE_DIVE); }
+ OPPONENT(SPECIES_ZIGZAGOON) { Moves(MOVE_SCRATCH); }
+ OPPONENT(SPECIES_PIKACHU) { Moves(MOVE_THUNDERBOLT); }
+ } WHEN {
+ TURN { MOVE(player, MOVE_DIVE); EXPECT_SWITCH(opponent, 1); }
+ }
+}
+
AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will switch out if it has an absorber but current mon has SE move 33% of the time")
{
PASSES_RANDOMLY(33, 100, RNG_AI_SWITCH_ABSORBING_STAY_IN);
@@ -1742,9 +1757,10 @@ AI_DOUBLE_BATTLE_TEST("AI will not choose to switch out Dondozo with Commander T
PLAYER(SPECIES_ZIGZAGOON) { Moves(MOVE_CELEBRATE); }
PLAYER(SPECIES_ZIGZAGOON) { Moves (MOVE_CELEBRATE); }
} WHEN {
- TURN { MOVE(playerLeft, MOVE_CELEBRATE); MOVE(playerRight, MOVE_PERISH_SONG); }
- TURN { MOVE(playerLeft, MOVE_CELEBRATE); MOVE(playerRight, MOVE_CELEBRATE); }
- TURN { SWITCH(playerLeft, 2); SWITCH(playerRight, 3); }
- TURN { MOVE(playerLeft, MOVE_CELEBRATE); MOVE(playerRight, MOVE_CELEBRATE); EXPECT_MOVE(opponentLeft, MOVE_WATER_GUN); }
+ // Commander Tatsugiri cannot act while swallowed, so skip its turn explicitly.
+ TURN { MOVE(playerLeft, MOVE_CELEBRATE); MOVE(playerRight, MOVE_PERISH_SONG); SKIP_TURN(opponentRight); }
+ TURN { MOVE(playerLeft, MOVE_CELEBRATE); MOVE(playerRight, MOVE_CELEBRATE); SKIP_TURN(opponentRight); }
+ TURN { SWITCH(playerLeft, 2); SWITCH(playerRight, 3); SKIP_TURN(opponentRight); }
+ TURN { MOVE(playerLeft, MOVE_CELEBRATE); MOVE(playerRight, MOVE_CELEBRATE); EXPECT_MOVE(opponentLeft, MOVE_WATER_GUN); SKIP_TURN(opponentRight); }
}
}
diff --git a/test/battle/hold_effect/ability_shield.c b/test/battle/hold_effect/ability_shield.c
index 6e5a9884b8..e0798c9cfd 100644
--- a/test/battle/hold_effect/ability_shield.c
+++ b/test/battle/hold_effect/ability_shield.c
@@ -35,6 +35,38 @@ SINGLE_BATTLE_TEST("Ability Shield protects against Neutralizing Gas")
}
}
+DOUBLE_BATTLE_TEST("Ability Shield prevents Intimidate from reactivating after Neutralizing Gas ends")
+{
+ GIVEN {
+ PLAYER(SPECIES_WOBBUFFET) { Speed(5); }
+ PLAYER(SPECIES_WYNAUT) { Speed(4); }
+ OPPONENT(SPECIES_KOFFING) { Ability(ABILITY_NEUTRALIZING_GAS); HP(1); Speed(1); }
+ OPPONENT(SPECIES_GYARADOS) { Ability(ABILITY_INTIMIDATE); Item(ITEM_ABILITY_SHIELD); Speed(3); }
+ } WHEN {
+ TURN { MOVE(playerLeft, MOVE_SCRATCH, target: opponentLeft); }
+ } SCENE {
+ ABILITY_POPUP(opponentLeft, ABILITY_NEUTRALIZING_GAS);
+ MESSAGE("Neutralizing gas filled the area!");
+ ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponentRight);
+ MESSAGE("The opposing Gyarados's Ability is protected by the effects of its Ability Shield!");
+ ABILITY_POPUP(opponentRight, ABILITY_INTIMIDATE);
+ ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft);
+ ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight);
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, playerLeft);
+ HP_BAR(opponentLeft);
+ MESSAGE("The effects of the neutralizing gas wore off!");
+ NONE_OF {
+ ABILITY_POPUP(opponentRight, ABILITY_INTIMIDATE);
+ ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft);
+ ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight);
+ }
+ MESSAGE("The opposing Koffing fainted!");
+ } THEN {
+ EXPECT_EQ(playerLeft->statStages[STAT_ATK], DEFAULT_STAT_STAGE - 1);
+ EXPECT_EQ(playerRight->statStages[STAT_ATK], DEFAULT_STAT_STAGE - 1);
+ }
+}
+
SINGLE_BATTLE_TEST("Ability Shield protects against Mold Breaker (no message)")
{
u32 item;
diff --git a/test/battle/hold_effect/eviolite.c b/test/battle/hold_effect/eviolite.c
index 1aa0622e43..5387d50929 100644
--- a/test/battle/hold_effect/eviolite.c
+++ b/test/battle/hold_effect/eviolite.c
@@ -1,4 +1,82 @@
#include "global.h"
#include "test/battle.h"
-TO_DO_BATTLE_TEST("TODO: Write Eviolite (Hold Effect) test titles")
+ASSUMPTIONS
+{
+ ASSUME(gItemsInfo[ITEM_EVIOLITE].holdEffect == HOLD_EFFECT_EVIOLITE);
+}
+
+SINGLE_BATTLE_TEST("Eviolite boosts Defense and Sp. Def for unevolved Pokemon", s16 damage)
+{
+ u16 move;
+ u32 item;
+
+ PARAMETRIZE { move = MOVE_SCRATCH; item = ITEM_EVIOLITE; }
+ PARAMETRIZE { move = MOVE_SCRATCH; item = ITEM_NONE; }
+ PARAMETRIZE { move = MOVE_WATER_GUN; item = ITEM_EVIOLITE; }
+ PARAMETRIZE { move = MOVE_WATER_GUN; item = ITEM_NONE; }
+
+ GIVEN {
+ ASSUME(GetMoveCategory(MOVE_SCRATCH) == DAMAGE_CATEGORY_PHYSICAL);
+ ASSUME(GetMoveCategory(MOVE_WATER_GUN) == DAMAGE_CATEGORY_SPECIAL);
+ PLAYER(SPECIES_PIKACHU) { Item(item); }
+ OPPONENT(SPECIES_MAGIKARP) { Moves(MOVE_SCRATCH, MOVE_WATER_GUN); }
+ } WHEN {
+ TURN { MOVE(opponent, move); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, move, opponent);
+ HP_BAR(player, captureDamage: &results[i].damage);
+ } FINALLY {
+ EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), results[1].damage);
+ EXPECT_MUL_EQ(results[2].damage, Q_4_12(1.5), results[3].damage);
+ }
+}
+
+SINGLE_BATTLE_TEST("Eviolite does not boost Defense or Sp. Def for evolved Pokemon", s16 damage)
+{
+ u16 move;
+ u32 item;
+
+ PARAMETRIZE { move = MOVE_SCRATCH; item = ITEM_EVIOLITE; }
+ PARAMETRIZE { move = MOVE_SCRATCH; item = ITEM_NONE; }
+ PARAMETRIZE { move = MOVE_WATER_GUN; item = ITEM_EVIOLITE; }
+ PARAMETRIZE { move = MOVE_WATER_GUN; item = ITEM_NONE; }
+
+ GIVEN {
+ ASSUME(GetMoveCategory(MOVE_SCRATCH) == DAMAGE_CATEGORY_PHYSICAL);
+ ASSUME(GetMoveCategory(MOVE_WATER_GUN) == DAMAGE_CATEGORY_SPECIAL);
+ PLAYER(SPECIES_RAICHU) { Item(item); }
+ OPPONENT(SPECIES_MAGIKARP) { Moves(MOVE_SCRATCH, MOVE_WATER_GUN); }
+ } WHEN {
+ TURN { MOVE(opponent, move); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, move, opponent);
+ HP_BAR(player, captureDamage: &results[i].damage);
+ } FINALLY {
+ EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.0), results[1].damage);
+ EXPECT_MUL_EQ(results[2].damage, Q_4_12(1.0), results[3].damage);
+ }
+}
+
+SINGLE_BATTLE_TEST("Eviolite uses original species after Transform", s16 damage)
+{
+ u32 item;
+
+ PARAMETRIZE { item = ITEM_EVIOLITE; }
+ PARAMETRIZE { item = ITEM_NONE; }
+
+ GIVEN {
+ ASSUME(GetMoveEffect(MOVE_TRANSFORM) == EFFECT_TRANSFORM);
+ PLAYER(SPECIES_PIKACHU) { Item(item); Moves(MOVE_TRANSFORM, MOVE_CELEBRATE); }
+ OPPONENT(SPECIES_GYARADOS) { Moves(MOVE_SCRATCH, MOVE_CELEBRATE); }
+ } WHEN {
+ TURN { MOVE(player, MOVE_TRANSFORM); MOVE(opponent, MOVE_CELEBRATE); }
+ TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_SCRATCH); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_TRANSFORM, player);
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, opponent);
+ HP_BAR(player, captureDamage: &results[i].damage);
+ } FINALLY {
+ EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), results[1].damage);
+ }
+}
diff --git a/test/battle/hold_effect/float_stone.c b/test/battle/hold_effect/float_stone.c
index f677c41369..88221a7122 100644
--- a/test/battle/hold_effect/float_stone.c
+++ b/test/battle/hold_effect/float_stone.c
@@ -1,4 +1,32 @@
#include "global.h"
#include "test/battle.h"
-TO_DO_BATTLE_TEST("TODO: Write Float Stone (Hold Effect) test titles")
+ASSUMPTIONS
+{
+ ASSUME(GetItemHoldEffect(ITEM_FLOAT_STONE) == HOLD_EFFECT_FLOAT_STONE);
+}
+
+SINGLE_BATTLE_TEST("Float Stone halves the holder's weight", s16 damage)
+{
+ u32 item;
+ PARAMETRIZE { item = ITEM_FLOAT_STONE; } // 10.0 - 24.9 kg (40 power)
+ PARAMETRIZE { item = ITEM_NONE; } // 25.0 - 49.9 kg (60 power)
+
+ GIVEN {
+ ASSUME(GetMoveEffect(MOVE_SOAK) == EFFECT_SOAK);
+ ASSUME(GetMoveArgType(MOVE_SOAK) == TYPE_WATER);
+ ASSUME(GetSpeciesWeight(SPECIES_DURALUDON) == 400);
+ PLAYER(SPECIES_WOBBUFFET);
+ OPPONENT(SPECIES_DURALUDON) { Ability(ABILITY_STALWART); Item(item); }
+ } WHEN {
+ TURN { MOVE(player, MOVE_SOAK); } // To remove super-effectiveness, as it was messing with calculations.
+ TURN { MOVE(player, MOVE_LOW_KICK); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_LOW_KICK, player);
+ HP_BAR(opponent, captureDamage: &results[i].damage);
+ } FINALLY {
+ EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), results[1].damage);
+ }
+}
+
+TO_DO_BATTLE_TEST("Float Stone doesn't affect Heavy Ball's multiplier")
diff --git a/test/battle/hold_effect/restore_hp.c b/test/battle/hold_effect/restore_hp.c
index 2441e92449..9304296c1b 100644
--- a/test/battle/hold_effect/restore_hp.c
+++ b/test/battle/hold_effect/restore_hp.c
@@ -82,3 +82,16 @@ SINGLE_BATTLE_TEST("Sitrus Berry restores HP immediately after Leech Seed damage
HP_BAR(player);
}
}
+
+SINGLE_BATTLE_TEST("Healing berry animates on the correct battler at battle start")
+{
+ GIVEN {
+ PLAYER(SPECIES_WOBBUFFET);
+ OPPONENT(SPECIES_WOBBUFFET) { HP(1); MaxHP(400); Item(ITEM_ORAN_BERRY); }
+ } WHEN {
+ TURN { }
+ } SCENE {
+ NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player);
+ ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent);
+ }
+}
diff --git a/test/battle/move_effect/ally_switch.c b/test/battle/move_effect/ally_switch.c
index a82816ef24..798aa63354 100644
--- a/test/battle/move_effect/ally_switch.c
+++ b/test/battle/move_effect/ally_switch.c
@@ -92,24 +92,24 @@ DOUBLE_BATTLE_TEST("Ally Switch does not redirect the target of Snipe Shot")
DOUBLE_BATTLE_TEST("Ally Switch does not redirect moves done by Pokémon with Stalwart and Propeller Tail")
{
+ u16 species;
enum Ability ability;
- PARAMETRIZE { ability = ABILITY_STALWART; }
- PARAMETRIZE { ability = ABILITY_PROPELLER_TAIL; }
- PARAMETRIZE { ability = ABILITY_TELEPATHY; }
+ PARAMETRIZE { species = SPECIES_DURALUDON; ability = ABILITY_STALWART; }
+ PARAMETRIZE { species = SPECIES_ARROKUDA; ability = ABILITY_PROPELLER_TAIL; }
+ PARAMETRIZE { species = SPECIES_RALTS; ability = ABILITY_TELEPATHY; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET); // Wobb is playerLeft, but it'll be Wynaut after Ally Switch
PLAYER(SPECIES_WYNAUT);
- OPPONENT(SPECIES_KADABRA) { Ability(ability); }
+ OPPONENT(species) { Ability(ability); }
OPPONENT(SPECIES_ABRA);
} WHEN {
- TURN { MOVE(playerLeft, MOVE_ALLY_SWITCH); MOVE(opponentLeft, MOVE_SCRATCH, target:playerRight); } // Kadabra targets playerRight Wynaut.
+ TURN { MOVE(playerLeft, MOVE_ALLY_SWITCH); MOVE(opponentLeft, MOVE_SCRATCH, target:playerRight); } // Opponent targets playerRight Wynaut.
} SCENE {
MESSAGE("Wobbuffet used Ally Switch!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_ALLY_SWITCH, playerLeft);
MESSAGE("Wobbuffet and Wynaut switched places!");
- MESSAGE("The opposing Kadabra used Scratch!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, opponentLeft);
HP_BAR((ability == ABILITY_STALWART || ability == ABILITY_PROPELLER_TAIL) ? playerLeft : playerRight);
}
@@ -218,6 +218,7 @@ DOUBLE_BATTLE_TEST("Ally Switch increases the Protect-like moves counter (Gen9+)
DOUBLE_BATTLE_TEST("Ally Switch works if ally used two-turn move like Dig")
{
GIVEN {
+ ASSUME(gBattleMoveEffects[GetMoveEffect(MOVE_DIG)].twoTurnEffect);
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_WOBBUFFET);
@@ -313,9 +314,8 @@ DOUBLE_BATTLE_TEST("Ally switch swaps opposing sky drop targets if partner is be
DOUBLE_BATTLE_TEST("Ally Switch swaps Illusion data")
{
GIVEN {
- ASSUME(GetMoveEffect(MOVE_ALLY_SWITCH) == EFFECT_ALLY_SWITCH);
PLAYER(SPECIES_HOOPA);
- PLAYER(SPECIES_ZOROARK);
+ PLAYER(SPECIES_ZOROARK) {Ability(ABILITY_ILLUSION); }
PLAYER(SPECIES_MAMOSWINE); // the third member here is required for zoroark
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
@@ -329,6 +329,7 @@ DOUBLE_BATTLE_TEST("Ally Switch swaps Illusion data")
DOUBLE_BATTLE_TEST("Ally switch updates last used moves for Mimic")
{
GIVEN {
+ ASSUME(GetMoveEffect(MOVE_MIMIC) == EFFECT_MIMIC);
PLAYER(SPECIES_XATU) { Speed(100); }
PLAYER(SPECIES_RIOLU) { Speed(150); }
OPPONENT(SPECIES_FEAROW) { Speed(20); }
@@ -348,9 +349,10 @@ DOUBLE_BATTLE_TEST("Ally switch updates last used moves for Mimic")
}
}
-DOUBLE_BATTLE_TEST("Ally Switch does not update leech seed battler")
+DOUBLE_BATTLE_TEST("Ally Switch does not update leech seed position")
{
GIVEN {
+ ASSUME(GetMoveEffect(MOVE_LEECH_SEED) == EFFECT_LEECH_SEED);
PLAYER(SPECIES_WYNAUT);
PLAYER(SPECIES_SOLOSIS);
OPPONENT(SPECIES_BULBASAUR) { HP(50); MaxHP(100); }
@@ -379,6 +381,114 @@ DOUBLE_BATTLE_TEST("Ally Switch does not update leech seed battler")
}
}
+DOUBLE_BATTLE_TEST("Ally Switch does not update Future Sight target position")
+{
+ GIVEN {
+ ASSUME(GetMoveEffect(MOVE_FUTURE_SIGHT) == EFFECT_FUTURE_SIGHT);
+ PLAYER(SPECIES_WOBBUFFET);
+ PLAYER(SPECIES_WYNAUT);
+ OPPONENT(SPECIES_ABRA);
+ OPPONENT(SPECIES_RALTS);
+ } WHEN {
+ TURN { MOVE(opponentLeft, MOVE_FUTURE_SIGHT, target: playerLeft); }
+ TURN { MOVE(playerLeft, MOVE_ALLY_SWITCH); }
+ TURN { }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_FUTURE_SIGHT, opponentLeft);
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_ALLY_SWITCH, playerLeft);
+ MESSAGE("Wynaut took the Future Sight attack!");
+ HP_BAR(playerLeft);
+ NOT HP_BAR(playerRight);
+ }
+}
+
+DOUBLE_BATTLE_TEST("Ally Switch does not update Future Sight target position when attacker side switches")
+{
+ GIVEN {
+ ASSUME(GetMoveEffect(MOVE_FUTURE_SIGHT) == EFFECT_FUTURE_SIGHT);
+ PLAYER(SPECIES_WOBBUFFET);
+ PLAYER(SPECIES_WYNAUT);
+ OPPONENT(SPECIES_ABRA);
+ OPPONENT(SPECIES_RALTS);
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { MOVE(opponentLeft, MOVE_FUTURE_SIGHT, target: playerLeft); }
+ TURN { SWITCH(opponentLeft, 2); MOVE(opponentRight, MOVE_ALLY_SWITCH); }
+ TURN { }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_FUTURE_SIGHT, opponentLeft);
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_ALLY_SWITCH, opponentRight);
+ MESSAGE("Wobbuffet took the Future Sight attack!");
+ HP_BAR(playerLeft);
+ NOT HP_BAR(playerRight);
+ }
+}
+
+DOUBLE_BATTLE_TEST("Ally Switch does not update Wish recovery position")
+{
+ GIVEN {
+ ASSUME(GetMoveEffect(MOVE_WISH) == EFFECT_WISH);
+ PLAYER(SPECIES_WOBBUFFET) { HP(50); MaxHP(100); }
+ PLAYER(SPECIES_WYNAUT) { HP(20); MaxHP(100); }
+ OPPONENT(SPECIES_WOBBUFFET);
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { MOVE(playerLeft, MOVE_WISH); }
+ TURN { MOVE(playerLeft, MOVE_ALLY_SWITCH); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_WISH, playerLeft);
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_ALLY_SWITCH, playerLeft);
+ HP_BAR(playerLeft);
+ NOT HP_BAR(playerRight);
+ } THEN {
+ EXPECT_EQ(playerLeft->hp, 70);
+ EXPECT_EQ(playerRight->hp, 50);
+ }
+}
+
+DOUBLE_BATTLE_TEST("Ally Switch does not update Healing Wish/Lunar Dance recovery position")
+{
+ u16 move = MOVE_NONE;
+ struct BattlePokemon *switchTarget = NULL;
+
+ PARAMETRIZE { move = MOVE_HEALING_WISH; switchTarget = playerLeft; }
+ PARAMETRIZE { move = MOVE_HEALING_WISH; switchTarget = playerRight; }
+ PARAMETRIZE { move = MOVE_LUNAR_DANCE; switchTarget = playerLeft; }
+ PARAMETRIZE { move = MOVE_LUNAR_DANCE; switchTarget = playerRight; }
+
+ GIVEN {
+ WITH_CONFIG(B_HEALING_WISH_SWITCH, GEN_8);
+ ASSUME(GetMoveEffect(MOVE_HEALING_WISH) == EFFECT_HEALING_WISH);
+ ASSUME(GetMoveEffect(MOVE_LUNAR_DANCE) == EFFECT_LUNAR_DANCE);
+ PLAYER(SPECIES_GARDEVOIR);
+ PLAYER(SPECIES_ABRA);
+ PLAYER(SPECIES_WOBBUFFET) { HP(100); MaxHP(100); }
+ PLAYER(SPECIES_WYNAUT) { HP(50); MaxHP(80); Status1(STATUS1_PARALYSIS); }
+ OPPONENT(SPECIES_WOBBUFFET);
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { MOVE(playerLeft, move); SEND_OUT(playerLeft, 2); }
+ TURN { MOVE(playerRight, MOVE_ALLY_SWITCH); }
+ TURN { SWITCH(switchTarget, 3); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, move, playerLeft);
+ if (switchTarget == playerLeft) {
+ HP_BAR(playerLeft, hp: 80);
+ STATUS_ICON(playerLeft, none: TRUE);
+ } else {
+ NOT HP_BAR(playerRight);
+ }
+ } THEN {
+ if (switchTarget == playerLeft) {
+ EXPECT_EQ(playerLeft->hp, 80);
+ EXPECT_EQ(playerLeft->status1, STATUS1_NONE);
+ } else {
+ EXPECT_EQ(playerRight->hp, 50);
+ EXPECT_EQ(playerRight->status1, STATUS1_PARALYSIS);
+ }
+ }
+}
+
DOUBLE_BATTLE_TEST("Ally Switch updates attract battler")
{
GIVEN {
diff --git a/test/battle/move_effect/aqua_ring.c b/test/battle/move_effect/aqua_ring.c
index d137a35276..e800f76c6c 100644
--- a/test/battle/move_effect/aqua_ring.c
+++ b/test/battle/move_effect/aqua_ring.c
@@ -1,6 +1,27 @@
#include "global.h"
#include "test/battle.h"
+ASSUMPTIONS
+{
+ ASSUME(GetMoveEffect(MOVE_AQUA_RING) == EFFECT_AQUA_RING);
+}
+
+SINGLE_BATTLE_TEST("Aqua Ring fails if already active")
+{
+ GIVEN {
+ PLAYER(SPECIES_WOBBUFFET);
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { MOVE(player, MOVE_AQUA_RING); }
+ TURN { MOVE(player, MOVE_AQUA_RING); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_AQUA_RING, player);
+ MESSAGE("Wobbuffet surrounded itself with a veil of water!");
+ NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_AQUA_RING, player);
+ MESSAGE("But it failed!");
+ }
+}
+
SINGLE_BATTLE_TEST("Aqua Ring recovers 1/16th HP at end of turn")
{
GIVEN {
@@ -15,9 +36,30 @@ SINGLE_BATTLE_TEST("Aqua Ring recovers 1/16th HP at end of turn")
}
}
+SINGLE_BATTLE_TEST("Aqua Ring restores 30% more HP when holding Big Root")
+{
+ u32 item;
+ u16 expectedHp;
+ PARAMETRIZE { item = ITEM_NONE; expectedHp = 58; }
+ PARAMETRIZE { item = ITEM_BIG_ROOT; expectedHp = 60; }
+
+ GIVEN {
+ ASSUME(gItemsInfo[ITEM_BIG_ROOT].holdEffect == HOLD_EFFECT_BIG_ROOT);
+ PLAYER(SPECIES_WOBBUFFET) { HP(50); MaxHP(128); Item(item); }
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { MOVE(player, MOVE_AQUA_RING); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_AQUA_RING, player);
+ } THEN {
+ EXPECT_EQ(player->hp, expectedHp);
+ }
+}
+
SINGLE_BATTLE_TEST("Aqua Ring can be used under Heal Block but will not heal the user")
{
GIVEN {
+ ASSUME(GetMoveEffect(MOVE_HEAL_BLOCK) == EFFECT_HEAL_BLOCK);
PLAYER(SPECIES_WOBBUFFET) { HP(50); MaxHP(128); Speed(50); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(100); }
} WHEN {
@@ -29,4 +71,21 @@ SINGLE_BATTLE_TEST("Aqua Ring can be used under Heal Block but will not heal the
}
}
-TO_DO_BATTLE_TEST("Baton Pass passes Aqua Ring's effect");
+SINGLE_BATTLE_TEST("Aqua Ring's effect is passed by Baton Pass")
+{
+ GIVEN {
+ ASSUME(GetMoveEffect(MOVE_BATON_PASS) == EFFECT_BATON_PASS);
+ PLAYER(SPECIES_WOBBUFFET);
+ PLAYER(SPECIES_WYNAUT) { HP(50); MaxHP(128); }
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { MOVE(player, MOVE_AQUA_RING); }
+ TURN { MOVE(player, MOVE_BATON_PASS); SEND_OUT(player, 1); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_AQUA_RING, player);
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_BATON_PASS, player);
+ SEND_IN_MESSAGE("Wynaut");
+ } THEN {
+ EXPECT(player->hp == 58);
+ }
+}
diff --git a/test/battle/move_effect/assist.c b/test/battle/move_effect/assist.c
index 3a380ef125..6f236af983 100644
--- a/test/battle/move_effect/assist.c
+++ b/test/battle/move_effect/assist.c
@@ -12,7 +12,6 @@ TO_DO_BATTLE_TEST("Assist can call moves with no PP left");
TO_DO_BATTLE_TEST("Assist can call moves from a fainted party member");
TO_DO_BATTLE_TEST("Assist can call moves that are blocked to its partners"); // Eg. double battle parter blocked by Disable
TO_DO_BATTLE_TEST("Assist can only call the original moves of a Transformed partner (Gen4 only)");
-TO_DO_BATTLE_TEST("Assist can only call the current moves of a Transformed partner (Gen5+)");
TO_DO_BATTLE_TEST("Assist cannot call a Mimicked move (Gen4 only)");
TO_DO_BATTLE_TEST("Assist can call a Mimicked move but not the original Mimic (Gen5+)");
TO_DO_BATTLE_TEST("Assist can call moves in unhatched Eggs (Gen5 only)");
@@ -57,3 +56,25 @@ SINGLE_BATTLE_TEST("Assisted move triggers correct weakness berry")
ANIMATION(ANIM_TYPE_MOVE, MOVE_SURF, player);
}
}
+
+DOUBLE_BATTLE_TEST("Assist can only call the current moves of a Transformed partner (Gen5+)")
+{
+ GIVEN {
+ ASSUME(GetMoveEffect(MOVE_TRANSFORM) == EFFECT_TRANSFORM);
+ PLAYER(SPECIES_WOBBUFFET) { Speed(3); Moves(MOVE_ASSIST); }
+ PLAYER(SPECIES_DITTO) { Speed(4); Moves(MOVE_TRANSFORM); }
+ OPPONENT(SPECIES_WOBBUFFET) { Speed(2); Moves(MOVE_SCRATCH); }
+ OPPONENT(SPECIES_WOBBUFFET) { Speed(1); }
+ } WHEN {
+ TURN {
+ MOVE(playerRight, MOVE_TRANSFORM, target: opponentLeft);
+ MOVE(playerLeft, MOVE_ASSIST);
+ MOVE(opponentLeft, MOVE_SCRATCH, target: playerRight);
+ }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_TRANSFORM, playerRight);
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_ASSIST, playerLeft);
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, playerLeft);
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, opponentLeft);
+ }
+}
diff --git a/test/battle/move_effect/autotomize.c b/test/battle/move_effect/autotomize.c
index 79a71ecbc7..e749ea1cbc 100644
--- a/test/battle/move_effect/autotomize.c
+++ b/test/battle/move_effect/autotomize.c
@@ -1,12 +1,45 @@
#include "global.h"
#include "test/battle.h"
-TO_DO_BATTLE_TEST("Autotomize increases Speed by 2 stages");
-TO_DO_BATTLE_TEST("Autotomize decreases weight by 100kg (220 lbs.)");
-TO_DO_BATTLE_TEST("Autotomize can be used multiple times to decrease weight each time");
+TO_DO_BATTLE_TEST("Autotomize increases Speed by 2 stages")
+
+SINGLE_BATTLE_TEST("Autotomize decreases weight by 100kg (220 lbs.) each time it's used")
+{
+ s16 damage[3];
+
+ GIVEN {
+ ASSUME(GetSpeciesWeight(SPECIES_METANG) == 2025);
+ PLAYER(SPECIES_WOBBUFFET);
+ OPPONENT(SPECIES_METANG);
+ } WHEN {
+ TURN { MOVE(player, MOVE_LOW_KICK); }
+ TURN { MOVE(opponent, MOVE_AUTOTOMIZE); MOVE(player, MOVE_LOW_KICK); }
+ TURN { MOVE(opponent, MOVE_AUTOTOMIZE); MOVE(player, MOVE_LOW_KICK); }
+ } SCENE {
+ // 200.0 kg or more (120 power)
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_LOW_KICK, player);
+ HP_BAR(opponent, captureDamage: &damage[0]);
+
+ // 100.0 - 199.9 kg (100 power)
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_AUTOTOMIZE, opponent);
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_LOW_KICK, player);
+ HP_BAR(opponent, captureDamage: &damage[1]);
+
+ // 0.1 - 9.9 kg (20 power)
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_AUTOTOMIZE, opponent);
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_LOW_KICK, player);
+ HP_BAR(opponent, captureDamage: &damage[2]);
+ } THEN {
+ EXPECT_MUL_EQ(damage[2], Q_4_12(6.0), damage[0]);
+ EXPECT_MUL_EQ(damage[2], Q_4_12(5.0), damage[0]);
+ }
+}
+
+
TO_DO_BATTLE_TEST("Autotomize cannot decrease weight below 0.1kg (0.2 lbs)");
TO_DO_BATTLE_TEST("Autotomize's weight reduction cannot be Baton Passed");
TO_DO_BATTLE_TEST("Autotomize's weight reduction cannot be removed by Haze");
TO_DO_BATTLE_TEST("Autotomize's weight reduction is reset upon form change (Gen6+)");
TO_DO_BATTLE_TEST("Autotomize's weight reduction is reset upon switch");
TO_DO_BATTLE_TEST("Autotomize's weight reduction is reset upon fainting");
+TO_DO_BATTLE_TEST("Autotomize doesn't affect Heavy Ball's multiplier")
diff --git a/test/battle/move_effect/beak_blast.c b/test/battle/move_effect/beak_blast.c
index 4cbea8a596..5a9d43df71 100644
--- a/test/battle/move_effect/beak_blast.c
+++ b/test/battle/move_effect/beak_blast.c
@@ -144,6 +144,7 @@ SINGLE_BATTLE_TEST("Beak Blast doesn't burn fire types")
{
GIVEN {
ASSUME(gSpeciesInfo[SPECIES_ARCANINE].types[0] == TYPE_FIRE || gSpeciesInfo[SPECIES_ARCANINE].types[1] == TYPE_FIRE);
+ ASSUME(MoveMakesContact(MOVE_SCRATCH));
PLAYER(SPECIES_ARCANINE);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
@@ -159,6 +160,7 @@ SINGLE_BATTLE_TEST("Beak Blast doesn't burn after being used")
{
GIVEN {
ASSUME(GetMovePriority(MOVE_COUNTER) < GetMovePriority(MOVE_BEAK_BLAST));
+ ASSUME(MoveMakesContact(MOVE_COUNTER));
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
@@ -169,44 +171,29 @@ SINGLE_BATTLE_TEST("Beak Blast doesn't burn after being used")
}
}
-DOUBLE_BATTLE_TEST("Beak Blast doesn't burn if the target is protected")
+DOUBLE_BATTLE_TEST("Beak Blast doesn't burn if the target is protected by Mat Block")
{
- u32 move;
-
- PARAMETRIZE { move = MOVE_SPIKY_SHIELD; }
- PARAMETRIZE { move = MOVE_BANEFUL_BUNKER; }
- PARAMETRIZE { move = MOVE_BURNING_BULWARK; }
- PARAMETRIZE { move = MOVE_SILK_TRAP; }
-
GIVEN {
- ASSUME(GetMoveEffect(move) == EFFECT_PROTECT);
- ASSUME(GetMoveEffect(MOVE_INSTRUCT) == EFFECT_INSTRUCT);
- ASSUME(GetMovePriority(MOVE_BEAK_BLAST) > GetMovePriority(MOVE_TRICK_ROOM));
+ ASSUME(GetMoveEffect(MOVE_MAT_BLOCK) == EFFECT_MAT_BLOCK);
+ ASSUME(GetMoveProtectMethod(MOVE_MAT_BLOCK) == PROTECT_MAT_BLOCK);
+ ASSUME(MoveMakesContact(MOVE_POUND));
PLAYER(SPECIES_WOBBUFFET) { Speed(1); }
PLAYER(SPECIES_WYNAUT) { Speed(2); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(5); }
OPPONENT(SPECIES_WYNAUT) { Speed(10); }
} WHEN {
- TURN { MOVE(opponentLeft, move); }
- TURN { MOVE(opponentRight, MOVE_INSTRUCT, target: opponentLeft, WITH_RNG(RNG_PROTECT_FAIL, 0));
- MOVE(opponentLeft, MOVE_BEAK_BLAST, target: playerLeft);
- MOVE(playerRight, MOVE_TRICK_ROOM);
- MOVE(playerLeft, MOVE_POUND, target: opponentLeft); }
+ TURN {
+ MOVE(opponentRight, MOVE_MAT_BLOCK);
+ MOVE(opponentLeft, MOVE_BEAK_BLAST, target: playerLeft);
+ MOVE(playerLeft, MOVE_POUND, target: opponentLeft);
+ }
} SCENE {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_BEAK_BLAST_SETUP, opponentLeft);
- ANIMATION(ANIM_TYPE_MOVE, MOVE_INSTRUCT, opponentRight);
- ANIMATION(ANIM_TYPE_MOVE, move, opponentLeft);
- NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_POUND, playerLeft);
- if (move == MOVE_SPIKY_SHIELD) {
- HP_BAR(playerLeft);
- } else if (move == MOVE_BANEFUL_BUNKER) {
- STATUS_ICON(playerLeft, STATUS1_POISON);
- } else if (move == MOVE_BURNING_BULWARK) {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_MAT_BLOCK, opponentRight);
+ NONE_OF {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_POUND, playerLeft);
STATUS_ICON(playerLeft, STATUS1_BURN);
- } else if (move == MOVE_SILK_TRAP) {
- ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft);
}
- NOT STATUS_ICON(playerLeft, STATUS1_BURN);
}
}
@@ -216,6 +203,7 @@ DOUBLE_BATTLE_TEST("Beak Blast doesn't burn if the target is protected by Quick
ASSUME(GetMoveEffect(MOVE_QUICK_GUARD) == EFFECT_PROTECT);
ASSUME(GetMoveProtectMethod(MOVE_QUICK_GUARD) == PROTECT_QUICK_GUARD);
ASSUME(GetMovePriority(MOVE_QUICK_ATTACK) > 0);
+ ASSUME(MoveMakesContact(MOVE_QUICK_ATTACK));
PLAYER(SPECIES_WOBBUFFET) { Speed(1); }
PLAYER(SPECIES_WYNAUT) { Speed(2); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(5); }
diff --git a/test/battle/move_effect/ingrain.c b/test/battle/move_effect/ingrain.c
index 19213f10d9..2d08a67744 100644
--- a/test/battle/move_effect/ingrain.c
+++ b/test/battle/move_effect/ingrain.c
@@ -1,4 +1,130 @@
#include "global.h"
#include "test/battle.h"
-TO_DO_BATTLE_TEST("TODO: Write Ingrain (Move Effect) test titles")
+ASSUMPTIONS
+{
+ ASSUME(GetMoveEffect(MOVE_INGRAIN) == EFFECT_INGRAIN);
+}
+
+SINGLE_BATTLE_TEST("Ingrain fails if already rooted")
+{
+ GIVEN {
+ PLAYER(SPECIES_WOBBUFFET);
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { MOVE(player, MOVE_INGRAIN); }
+ TURN { MOVE(player, MOVE_INGRAIN); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_INGRAIN, player);
+ MESSAGE("Wobbuffet planted its roots!");
+ NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_INGRAIN, player);
+ MESSAGE("But it failed!");
+ }
+}
+
+SINGLE_BATTLE_TEST("Ingrain restores 1/16th HP at the end of turn")
+{
+ GIVEN {
+ PLAYER(SPECIES_WOBBUFFET) { HP(50); MaxHP(128); }
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { MOVE(player, MOVE_INGRAIN); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_INGRAIN, player);
+ } THEN {
+ EXPECT_EQ(player->hp, 58);
+ }
+}
+
+SINGLE_BATTLE_TEST("Ingrain restores 30% more HP when holding Big Root")
+{
+ u32 item;
+ u16 expectedHp;
+ PARAMETRIZE { item = ITEM_NONE; expectedHp = 58; }
+ PARAMETRIZE { item = ITEM_BIG_ROOT; expectedHp = 60; }
+
+ GIVEN {
+ ASSUME(gItemsInfo[ITEM_BIG_ROOT].holdEffect == HOLD_EFFECT_BIG_ROOT);
+ PLAYER(SPECIES_WOBBUFFET) { HP(50); MaxHP(128); Item(item); }
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { MOVE(player, MOVE_INGRAIN); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_INGRAIN, player);
+ } THEN {
+ EXPECT_EQ(player->hp, expectedHp);
+ }
+}
+
+SINGLE_BATTLE_TEST("Ingrain can be used under Heal Block but will not heal the user")
+{
+ GIVEN {
+ ASSUME(GetMoveEffect(MOVE_HEAL_BLOCK) == EFFECT_HEAL_BLOCK);
+ PLAYER(SPECIES_WOBBUFFET) { HP(50); MaxHP(128); Speed(50); }
+ OPPONENT(SPECIES_WOBBUFFET) { Speed(100); }
+ } WHEN {
+ TURN { MOVE(opponent, MOVE_HEAL_BLOCK); MOVE(player, MOVE_INGRAIN); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_INGRAIN, player);
+ } THEN {
+ EXPECT_EQ(player->hp, 50);
+ }
+}
+
+SINGLE_BATTLE_TEST("Ingrain prevents regular switching out")
+{
+ GIVEN {
+ PLAYER(SPECIES_WOBBUFFET);
+ PLAYER(SPECIES_WYNAUT);
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { MOVE(player, MOVE_INGRAIN); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_INGRAIN, player);
+ } THEN {
+ u32 battler = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT);
+ EXPECT_EQ(CanBattlerEscape(battler), FALSE);
+ }
+}
+
+SINGLE_BATTLE_TEST("Ingrain does not prevent switching out with Flip Turn")
+{
+ GIVEN {
+ ASSUME(GetMoveEffect(MOVE_FLIP_TURN) == EFFECT_HIT_ESCAPE);
+ PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_INGRAIN, MOVE_FLIP_TURN); }
+ PLAYER(SPECIES_WYNAUT);
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { MOVE(player, MOVE_INGRAIN); }
+ TURN { MOVE(player, MOVE_FLIP_TURN); SEND_OUT(player, 1); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_INGRAIN, player);
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_FLIP_TURN, player);
+ HP_BAR(opponent);
+ SEND_IN_MESSAGE("Wynaut");
+ } THEN {
+ EXPECT_EQ(player->species, SPECIES_WYNAUT);
+ }
+}
+
+SINGLE_BATTLE_TEST("Ingrain's effect is passed by Baton Pass")
+{
+ GIVEN {
+ ASSUME(GetMoveEffect(MOVE_BATON_PASS) == EFFECT_BATON_PASS);
+ PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_INGRAIN, MOVE_BATON_PASS); }
+ PLAYER(SPECIES_WYNAUT) { HP(50); MaxHP(128); }
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { MOVE(player, MOVE_INGRAIN); }
+ TURN { MOVE(player, MOVE_BATON_PASS); SEND_OUT(player, 1); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_INGRAIN, player);
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_BATON_PASS, player);
+ SEND_IN_MESSAGE("Wynaut");
+ } THEN {
+ EXPECT_EQ(player->species, SPECIES_WYNAUT);
+ EXPECT_EQ(player->hp, 58);
+ }
+}
+
+TO_DO_BATTLE_TEST("Red Card and forced switch moves (Roar/Whirlwind) cannot force out a rooted Pokémon");
diff --git a/test/battle/move_effect/instruct.c b/test/battle/move_effect/instruct.c
index b0bd715ddd..9bb4eab9de 100644
--- a/test/battle/move_effect/instruct.c
+++ b/test/battle/move_effect/instruct.c
@@ -52,6 +52,57 @@ DOUBLE_BATTLE_TEST("Instruct fails if move is banned by Instruct")
}
}
+TO_DO_BATTLE_TEST("Instruct fails if target is in the middle of Bide");
+
+DOUBLE_BATTLE_TEST("Instruct fails if target is preparing Focus Punch, Beak Blast or Shell Trap")
+{
+ u32 move, Anim;
+ PARAMETRIZE { move = MOVE_FOCUS_PUNCH; Anim = B_ANIM_FOCUS_PUNCH_SETUP; }
+ PARAMETRIZE { move = MOVE_BEAK_BLAST; Anim = B_ANIM_BEAK_BLAST_SETUP; }
+ PARAMETRIZE { move = MOVE_SHELL_TRAP; Anim = B_ANIM_SHELL_TRAP_SETUP; }
+
+ GIVEN {
+ ASSUME(GetMoveEffect(MOVE_FOCUS_PUNCH) == EFFECT_FOCUS_PUNCH);
+ ASSUME(GetMoveEffect(MOVE_BEAK_BLAST) == EFFECT_BEAK_BLAST);
+ ASSUME(GetMoveEffect(MOVE_SHELL_TRAP) == EFFECT_SHELL_TRAP);
+ PLAYER(SPECIES_WOBBUFFET) { Speed(4); Moves(MOVE_INSTRUCT, MOVE_CELEBRATE); }
+ PLAYER(SPECIES_WOBBUFFET) { Speed(3); Moves(MOVE_POUND, move); }
+ OPPONENT(SPECIES_WOBBUFFET) { Speed(2); }
+ OPPONENT(SPECIES_WOBBUFFET) { Speed(1); }
+ } WHEN {
+ TURN { MOVE(playerRight, MOVE_POUND, target: opponentLeft); }
+ TURN {
+ if (move == MOVE_SHELL_TRAP)
+ MOVE(playerRight, move);
+ else
+ MOVE(playerRight, move, target: opponentLeft);
+ MOVE(playerLeft, MOVE_INSTRUCT, target: playerRight);
+ }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_POUND, playerRight);
+ ANIMATION(ANIM_TYPE_GENERAL, Anim, playerRight);
+ NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_INSTRUCT, playerLeft);
+ }
+}
+
+DOUBLE_BATTLE_TEST("Instruct fails if target is picked up by Sky Drop even if one of the battlers has No Guard")
+{
+ GIVEN {
+ ASSUME(GetMoveEffect(MOVE_SKY_DROP) == EFFECT_SKY_DROP);
+ PLAYER(SPECIES_WOBBUFFET) { Speed(3); Moves(MOVE_INSTRUCT, MOVE_CELEBRATE); }
+ PLAYER(SPECIES_MACHAMP) { Speed(2); Ability(ABILITY_NO_GUARD); Moves(MOVE_SCRATCH, MOVE_CELEBRATE); }
+ OPPONENT(SPECIES_WOBBUFFET) { Speed(4); Moves(MOVE_SKY_DROP, MOVE_CELEBRATE); }
+ OPPONENT(SPECIES_WOBBUFFET) { Speed(1); }
+ } WHEN {
+ TURN { MOVE(opponentLeft, MOVE_CELEBRATE); MOVE(playerRight, MOVE_SCRATCH, target: opponentLeft); }
+ TURN { MOVE(opponentLeft, MOVE_SKY_DROP, target: playerRight); MOVE(playerLeft, MOVE_INSTRUCT, target: playerRight); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, playerRight);
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_SKY_DROP, opponentLeft);
+ NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_INSTRUCT, playerLeft);
+ }
+}
+
DOUBLE_BATTLE_TEST("Instruct-called move targets the target of the move picked on its last use")
{
GIVEN {
diff --git a/test/battle/move_effect/low_kick.c b/test/battle/move_effect/low_kick.c
index c68b152e2d..d88996aba6 100644
--- a/test/battle/move_effect/low_kick.c
+++ b/test/battle/move_effect/low_kick.c
@@ -1,4 +1,29 @@
#include "global.h"
#include "test/battle.h"
-TO_DO_BATTLE_TEST("TODO: Write Low Kick (Move Effect) test titles")
+SINGLE_BATTLE_TEST("Low Kick's damage varies based on the target's weight", s16 damage)
+{
+ u32 species, weight;
+
+ PARAMETRIZE { species = SPECIES_CUBONE; weight = 65; } // 0.1 - 9.9 kg (20 power)
+ PARAMETRIZE { species = SPECIES_SANDSHREW; weight = 120; } // 10.0 - 24.9 kg (40 power)
+ PARAMETRIZE { species = SPECIES_MAROWAK; weight = 450; } // 25.0 - 49.9 kg (60 power)
+ PARAMETRIZE { species = SPECIES_SANDACONDA; weight = 655; } // 50.0 - 99.9 kg (80 power)
+ PARAMETRIZE { species = SPECIES_DONPHAN; weight = 1200; } // 100.0 - 199.9 kg (100 power)
+ PARAMETRIZE { species = SPECIES_HIPPOWDON; weight = 3000; } // 200.0 kg or more (120 power)
+
+ GIVEN {
+ ASSUME(GetSpeciesWeight(species) == weight);
+ PLAYER(SPECIES_WOBBUFFET);
+ OPPONENT(species) { Defense(170); } // Cubone's Defense, the lowest one in hopes of avoid distorting the results.
+ } WHEN {
+ TURN { MOVE(player, MOVE_LOW_KICK); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_LOW_KICK, player);
+ HP_BAR(opponent, captureDamage: &results[i].damage);
+ } THEN {
+ // Since Low Kick increases by 20 each tier, multiply by tier number to compare with the first tier.
+ if (i != 0)
+ EXPECT_MUL_EQ(results[0].damage, Q_4_12(i + 1), results[i].damage);
+ }
+}
diff --git a/test/battle/move_effect/me_first.c b/test/battle/move_effect/me_first.c
index 8ccaa324ef..48f8d179be 100644
--- a/test/battle/move_effect/me_first.c
+++ b/test/battle/move_effect/me_first.c
@@ -82,6 +82,37 @@ SINGLE_BATTLE_TEST("Me First can be selected if users holds Assault Vest")
}
}
+SINGLE_BATTLE_TEST("Me First can be selected under Taunt in Gen5+")
+{
+ u32 gen = 0;
+
+ PARAMETRIZE { gen = GEN_4; }
+ PARAMETRIZE { gen = GEN_5; }
+
+ GIVEN {
+ WITH_CONFIG(B_TAUNT_ME_FIRST, gen);
+ PLAYER(SPECIES_WOBBUFFET) { Speed(100); Moves(MOVE_ME_FIRST, MOVE_TACKLE); }
+ OPPONENT(SPECIES_WOBBUFFET) { Speed(50); Moves(MOVE_TAUNT, MOVE_TACKLE); }
+ } WHEN {
+ TURN { MOVE(player, MOVE_TACKLE); MOVE(opponent, MOVE_TAUNT); }
+ if (gen >= GEN_5) {
+ TURN { MOVE(player, MOVE_ME_FIRST); MOVE(opponent, MOVE_TACKLE); }
+ } else {
+ TURN {
+ MOVE(player, MOVE_ME_FIRST, allowed: FALSE);
+ MOVE(player, MOVE_TACKLE);
+ MOVE(opponent, MOVE_TACKLE);
+ }
+ }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_TAUNT, opponent);
+ if (gen >= GEN_5)
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_ME_FIRST, player);
+ else
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player);
+ }
+}
+
SINGLE_BATTLE_TEST("Me First deducts power points from itself, not the copied move")
{
ASSUME(GetMovePP(MOVE_ME_FIRST) == 20);
diff --git a/test/battle/move_effect/present.c b/test/battle/move_effect/present.c
index bd21f13de4..224e85bf8f 100644
--- a/test/battle/move_effect/present.c
+++ b/test/battle/move_effect/present.c
@@ -1,4 +1,63 @@
#include "global.h"
#include "test/battle.h"
-TO_DO_BATTLE_TEST("TODO: Write Present (Move Effect) test titles")
+ASSUMPTIONS
+{
+ ASSUME(GetMoveEffect(MOVE_PRESENT) == EFFECT_PRESENT);
+}
+
+SINGLE_BATTLE_TEST("Present healing through Wonder Guard is still considered to have affected the target")
+{
+ GIVEN {
+ ASSUME(GetMoveEffect(MOVE_MIRROR_MOVE) == EFFECT_MIRROR_MOVE);
+ PLAYER(SPECIES_WOBBUFFET);
+ OPPONENT(SPECIES_SHEDINJA) { Ability(ABILITY_WONDER_GUARD); HP(1); MaxHP(100); }
+ } WHEN {
+ TURN { MOVE(player, MOVE_PRESENT, WITH_RNG(RNG_PRESENT, 254)); }
+ TURN { MOVE(opponent, MOVE_MIRROR_MOVE, WITH_RNG(RNG_PRESENT, 1)); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_PRESENT, player);
+ HP_BAR(opponent);
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_MIRROR_MOVE, opponent);
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_PRESENT, opponent);
+ HP_BAR(player);
+ }
+}
+
+DOUBLE_BATTLE_TEST("Present healing is blocked by Telepathy on an ally target")
+{
+ GIVEN {
+ PLAYER(SPECIES_WOBBUFFET);
+ PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_TELEPATHY); HP(50); MaxHP(100); }
+ OPPONENT(SPECIES_WOBBUFFET);
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { MOVE(playerLeft, MOVE_PRESENT, target: playerRight, WITH_RNG(RNG_PRESENT, 254)); }
+ } SCENE {
+ NONE_OF {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_PRESENT, playerLeft);
+ HP_BAR(playerRight);
+ }
+ } THEN {
+ EXPECT_EQ(playerRight->hp, 50);
+ }
+}
+
+SINGLE_BATTLE_TEST("Present with Parental Bond hits twice when damaging, but only once when healing")
+{
+ GIVEN {
+ ASSUME(GetSpeciesAbility(SPECIES_KANGASKHAN_MEGA, 0) == ABILITY_PARENTAL_BOND);
+ PLAYER(SPECIES_KANGASKHAN) { Item(ITEM_KANGASKHANITE); }
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { MOVE(player, MOVE_PRESENT, gimmick: GIMMICK_MEGA, WITH_RNG(RNG_PRESENT, 1)); }
+ TURN { MOVE(player, MOVE_PRESENT, WITH_RNG(RNG_PRESENT, 254)); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_PRESENT, player);
+ HP_BAR(opponent);
+ HP_BAR(opponent);
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_PRESENT, player);
+ HP_BAR(opponent);
+ NOT HP_BAR(opponent);
+ }
+}
diff --git a/test/battle/move_effect/psyblade.c b/test/battle/move_effect/psyblade.c
index 3a5778d1f4..edc079fb84 100644
--- a/test/battle/move_effect/psyblade.c
+++ b/test/battle/move_effect/psyblade.c
@@ -1,4 +1,37 @@
#include "global.h"
#include "test/battle.h"
-TO_DO_BATTLE_TEST("TODO: Write Psyblade (Move Effect) test titles")
+ASSUMPTIONS
+{
+ ASSUME(GetMoveEffect(MOVE_PSYBLADE) == EFFECT_PSYBLADE);
+}
+
+SINGLE_BATTLE_TEST("Psyblade's power increases by 50% on Electric Terrain even if the user is not grounded", s16 damage)
+{
+ bool32 terrain, airBalloon;
+
+ PARAMETRIZE { terrain = FALSE; airBalloon = FALSE; }
+ PARAMETRIZE { terrain = TRUE; airBalloon = FALSE; }
+ PARAMETRIZE { terrain = FALSE; airBalloon = TRUE; }
+ PARAMETRIZE { terrain = TRUE; airBalloon = TRUE; }
+
+ GIVEN {
+ ASSUME(gItemsInfo[ITEM_AIR_BALLOON].holdEffect == HOLD_EFFECT_AIR_BALLOON);
+ ASSUME(GetMoveEffect(MOVE_ELECTRIC_TERRAIN) == EFFECT_ELECTRIC_TERRAIN);
+ ASSUME(GetMoveEffect(MOVE_PSYBLADE) == EFFECT_PSYBLADE);
+ PLAYER(SPECIES_SLOWKING) { Moves(MOVE_ELECTRIC_TERRAIN, MOVE_PSYBLADE); Item(airBalloon ? ITEM_AIR_BALLOON : ITEM_NONE); }
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ if (terrain)
+ TURN { MOVE(player, MOVE_ELECTRIC_TERRAIN); }
+ TURN { MOVE(player, MOVE_PSYBLADE); }
+ } SCENE {
+ if (terrain)
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_ELECTRIC_TERRAIN, player);
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_PSYBLADE, player);
+ HP_BAR(opponent, captureDamage: &results[i].damage);
+ } FINALLY {
+ EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), results[1].damage);
+ EXPECT_MUL_EQ(results[2].damage, Q_4_12(1.5), results[3].damage);
+ }
+}
diff --git a/test/battle/move_effect/sky_drop.c b/test/battle/move_effect/sky_drop.c
index b3bc6739af..29cb431bb4 100644
--- a/test/battle/move_effect/sky_drop.c
+++ b/test/battle/move_effect/sky_drop.c
@@ -68,7 +68,7 @@ DOUBLE_BATTLE_TEST("Sky Drop is cancelled if Gravity activated")
}
}
-SINGLE_BATTLE_TEST("Sky Drop fails on heavy targets")
+SINGLE_BATTLE_TEST("Sky Drop fails on targets heavier or equal than 200kg")
{
GIVEN {
ASSUME(gSpeciesInfo[SPECIES_METAGROSS].weight >= 2000);
diff --git a/test/battle/move_effect/tickle.c b/test/battle/move_effect/tickle.c
index 3a878868fd..8513340f4b 100644
--- a/test/battle/move_effect/tickle.c
+++ b/test/battle/move_effect/tickle.c
@@ -21,3 +21,20 @@ SINGLE_BATTLE_TEST("Tickle reduces the target's Attack and Defense by 1 stage ea
EXPECT_EQ(opponent->statStages[STAT_DEF], DEFAULT_STAT_STAGE - 1);
}
}
+
+SINGLE_BATTLE_TEST("Tickle is blocked by Substitute (Gen4+)")
+{
+ GIVEN {
+ PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_TICKLE, MOVE_CELEBRATE); Speed(5); }
+ OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_SUBSTITUTE, MOVE_CELEBRATE); Speed(10); }
+ } WHEN {
+ TURN { MOVE(opponent, MOVE_SUBSTITUTE); MOVE(player, MOVE_CELEBRATE); }
+ TURN { MOVE(player, MOVE_TICKLE); MOVE(opponent, MOVE_CELEBRATE); }
+ } SCENE {
+ NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_TICKLE, player);
+ MESSAGE("But it failed!");
+ } THEN {
+ EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE);
+ EXPECT_EQ(opponent->statStages[STAT_DEF], DEFAULT_STAT_STAGE);
+ }
+}
diff --git a/test/battle/move_effect/transform.c b/test/battle/move_effect/transform.c
index de98de70a6..d82728c184 100644
--- a/test/battle/move_effect/transform.c
+++ b/test/battle/move_effect/transform.c
@@ -1,7 +1,135 @@
#include "global.h"
#include "test/battle.h"
-TO_DO_BATTLE_TEST("TODO: Write Transform (Move Effect) test titles")
+ASSUMPTIONS
+{
+ ASSUME(GetMoveEffect(MOVE_TRANSFORM) == EFFECT_TRANSFORM);
+}
+
+SINGLE_BATTLE_TEST("Transform fails on semi-invulnerable target in Gen2+")
+{
+ u32 genConfig;
+ bool32 expectFail;
+
+ PARAMETRIZE { genConfig = GEN_1; expectFail = FALSE; }
+ PARAMETRIZE { genConfig = GEN_2; expectFail = TRUE; }
+
+ GIVEN {
+ WITH_CONFIG(B_TRANSFORM_SEMI_INV_FAIL, genConfig);
+ PLAYER(SPECIES_WOBBUFFET) { Speed(50); Moves(MOVE_DIG); }
+ OPPONENT(SPECIES_DITTO) { Speed(10); Moves(MOVE_TRANSFORM); }
+ } WHEN {
+ TURN { MOVE(player, MOVE_DIG); MOVE(opponent, MOVE_TRANSFORM); }
+ } SCENE {
+ if (expectFail)
+ MESSAGE("But it failed!");
+ else
+ MESSAGE("The opposing Ditto transformed into Wobbuffet!");
+ }
+}
+
+SINGLE_BATTLE_TEST("Transform fails on transformed target in Gen2+")
+{
+ u32 genConfig;
+ bool32 expectFail;
+
+ PARAMETRIZE { genConfig = GEN_1; expectFail = FALSE; }
+ PARAMETRIZE { genConfig = GEN_2; expectFail = TRUE; }
+
+ GIVEN {
+ WITH_CONFIG(B_TRANSFORM_TARGET_FAIL, genConfig);
+ PLAYER(SPECIES_DITTO) { Speed(50); Moves(MOVE_TRANSFORM, MOVE_CELEBRATE); }
+ OPPONENT(SPECIES_WOBBUFFET) { Speed(10); Moves(MOVE_TRANSFORM, MOVE_CELEBRATE); }
+ } WHEN {
+ TURN { MOVE(player, MOVE_TRANSFORM); MOVE(opponent, MOVE_CELEBRATE); }
+ TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_TRANSFORM); }
+ } SCENE {
+ MESSAGE("Ditto transformed into Wobbuffet!");
+ if (expectFail)
+ MESSAGE("But it failed!");
+ else
+ MESSAGE("The opposing Wobbuffet transformed into Wobbuffet!");
+ }
+}
+
+SINGLE_BATTLE_TEST("Transform fails when the user is already transformed in Gen5+")
+{
+ u32 genConfig;
+ bool32 expectFail;
+
+ PARAMETRIZE { genConfig = GEN_4; expectFail = FALSE; }
+ PARAMETRIZE { genConfig = GEN_5; expectFail = TRUE; }
+
+ GIVEN {
+ WITH_CONFIG(B_TRANSFORM_USER_FAIL, genConfig);
+ PLAYER(SPECIES_WOBBUFFET) { Speed(50); Moves(MOVE_TRANSFORM, MOVE_CELEBRATE); }
+ OPPONENT(SPECIES_DITTO) { Speed(10); Moves(MOVE_TRANSFORM, MOVE_CELEBRATE); }
+ } WHEN {
+ TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_TRANSFORM); }
+ TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_TRANSFORM); }
+ } SCENE {
+ MESSAGE("The opposing Ditto transformed into Wobbuffet!");
+ if (expectFail)
+ MESSAGE("But it failed!");
+ else
+ MESSAGE("The opposing Ditto transformed into Wobbuffet!");
+ }
+}
+
+SINGLE_BATTLE_TEST("Transform fails on target behind substitute in Gen5+")
+{
+ u32 genConfig;
+ bool32 expectFail;
+
+ PARAMETRIZE { genConfig = GEN_4; expectFail = FALSE; }
+ PARAMETRIZE { genConfig = GEN_5; expectFail = TRUE; }
+
+ GIVEN {
+ WITH_CONFIG(B_TRANSFORM_SUBSTITUTE_FAIL, genConfig);
+ PLAYER(SPECIES_WOBBUFFET) { Speed(50); Moves(MOVE_SUBSTITUTE); }
+ OPPONENT(SPECIES_DITTO) { Speed(10); Moves(MOVE_TRANSFORM); }
+ } WHEN {
+ TURN { MOVE(player, MOVE_SUBSTITUTE); MOVE(opponent, MOVE_TRANSFORM); }
+ } SCENE {
+ if (expectFail)
+ MESSAGE("But it failed!");
+ else
+ MESSAGE("The opposing Ditto transformed into Wobbuffet!");
+ }
+}
+
+SINGLE_BATTLE_TEST("Transformed Pokemon cannot change forms in Gen5+")
+{
+ u32 genConfig;
+ bool32 expectFormChange;
+
+ PARAMETRIZE { genConfig = GEN_4; expectFormChange = TRUE; }
+ PARAMETRIZE { genConfig = GEN_5; expectFormChange = FALSE; }
+
+ GIVEN {
+ WITH_CONFIG(B_TRANSFORM_FORM_CHANGES, genConfig);
+ PLAYER(SPECIES_AEGISLASH) { Moves(MOVE_TACKLE, MOVE_CELEBRATE); }
+ OPPONENT(SPECIES_DITTO) { Moves(MOVE_TACKLE, MOVE_TRANSFORM); }
+ } WHEN {
+ TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_TRANSFORM); }
+ TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_TACKLE); }
+ } SCENE {
+ if (expectFormChange) {
+ ABILITY_POPUP(opponent, ABILITY_STANCE_CHANGE);
+ ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE, opponent);
+ } else {
+ NONE_OF {
+ ABILITY_POPUP(opponent, ABILITY_STANCE_CHANGE);
+ ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE, opponent);
+ }
+ }
+ } THEN {
+ if (expectFormChange)
+ EXPECT_EQ(opponent->species, SPECIES_AEGISLASH_BLADE);
+ else
+ EXPECT_EQ(opponent->species, SPECIES_AEGISLASH);
+ }
+}
SINGLE_BATTLE_TEST("(TERA) Transform does not copy the target's Tera Type, and if the user is Terastallized it keeps its own Tera Type")
{
@@ -42,3 +170,5 @@ SINGLE_BATTLE_TEST("Transform returns the user to normal at the end of the battl
EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_SPECIES), SPECIES_DITTO);
}
}
+
+TO_DO_BATTLE_TEST("TODO: Write Transform (Move Effect) test titles")
diff --git a/test/battle/move_effect/venom_drench.c b/test/battle/move_effect/venom_drench.c
index 00dac65858..aa09061f22 100644
--- a/test/battle/move_effect/venom_drench.c
+++ b/test/battle/move_effect/venom_drench.c
@@ -1,4 +1,42 @@
#include "global.h"
#include "test/battle.h"
-TO_DO_BATTLE_TEST("TODO: Write Venom Drench (Move Effect) test titles")
+ASSUMPTIONS
+{
+ ASSUME(GetMoveEffect(MOVE_VENOM_DRENCH) == EFFECT_VENOM_DRENCH);
+}
+
+SINGLE_BATTLE_TEST("Venom Drench lowers stats of a poisoned target")
+{
+ GIVEN {
+ PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_VENOM_DRENCH); }
+ OPPONENT(SPECIES_WOBBUFFET) { Status1(STATUS1_POISON); }
+ } WHEN {
+ TURN { MOVE(player, MOVE_VENOM_DRENCH); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_VENOM_DRENCH, player);
+ } THEN {
+ EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE - 1);
+ EXPECT_EQ(opponent->statStages[STAT_SPATK], DEFAULT_STAT_STAGE - 1);
+ EXPECT_EQ(opponent->statStages[STAT_SPEED], DEFAULT_STAT_STAGE - 1);
+ }
+}
+
+SINGLE_BATTLE_TEST("Venom Drench is blocked by Substitute")
+{
+ GIVEN {
+ ASSUME(GetMoveEffect(MOVE_SUBSTITUTE) == EFFECT_SUBSTITUTE);
+ PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_VENOM_DRENCH, MOVE_CELEBRATE); }
+ OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_SUBSTITUTE, MOVE_CELEBRATE); Status1(STATUS1_POISON); }
+ } WHEN {
+ TURN { MOVE(opponent, MOVE_SUBSTITUTE); MOVE(player, MOVE_CELEBRATE); }
+ TURN { MOVE(opponent, MOVE_CELEBRATE); MOVE(player, MOVE_VENOM_DRENCH); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_SUBSTITUTE, opponent);
+ NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_VENOM_DRENCH, player);
+ } THEN {
+ EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE);
+ EXPECT_EQ(opponent->statStages[STAT_SPATK], DEFAULT_STAT_STAGE);
+ EXPECT_EQ(opponent->statStages[STAT_SPEED], DEFAULT_STAT_STAGE);
+ }
+}
diff --git a/test/battle/move_effect_secondary/throat_chop.c b/test/battle/move_effect_secondary/throat_chop.c
index 3d6438a4ba..27eb27c304 100644
--- a/test/battle/move_effect_secondary/throat_chop.c
+++ b/test/battle/move_effect_secondary/throat_chop.c
@@ -27,8 +27,9 @@ SINGLE_BATTLE_TEST("Throat Chop prevents the usage of sound moves")
SINGLE_BATTLE_TEST("Throat Chop prevents sound base moves for 2 turns")
{
GIVEN {
+ ASSUME(IsSoundMove(MOVE_HYPER_VOICE));
PLAYER(SPECIES_WOBBUFFET);
- OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_HYPER_VOICE, MOVE_ALLURING_VOICE, MOVE_OVERDRIVE, MOVE_ROUND); }
+ OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_HYPER_VOICE); }
} WHEN {
TURN { MOVE(opponent, MOVE_HYPER_VOICE); MOVE(player, MOVE_THROAT_CHOP); }
TURN { FORCED_MOVE(opponent); }
@@ -45,3 +46,22 @@ SINGLE_BATTLE_TEST("Throat Chop prevents sound base moves for 2 turns")
ANIMATION(ANIM_TYPE_MOVE, MOVE_HYPER_VOICE, opponent);
}
}
+
+SINGLE_BATTLE_TEST("Throat Chop usage when target is already prevented from using sound moves doesn't reset timer")
+{
+ GIVEN {
+ ASSUME(IsSoundMove(MOVE_HYPER_VOICE));
+ PLAYER(SPECIES_WOBBUFFET);
+ OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_HYPER_VOICE); }
+ } WHEN {
+ TURN { MOVE(opponent, MOVE_HYPER_VOICE); MOVE(player, MOVE_THROAT_CHOP); }
+ TURN { FORCED_MOVE(opponent); MOVE(player, MOVE_THROAT_CHOP); }
+ TURN { MOVE(opponent, MOVE_HYPER_VOICE); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_HYPER_VOICE, opponent);
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_THROAT_CHOP, player);
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_STRUGGLE, opponent);
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_THROAT_CHOP, player);
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_HYPER_VOICE, opponent);
+ }
+}