diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 4c60758184..c2d5e894ab 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -4828,8 +4828,6 @@ BattleScript_PartyHealEnd:: BattleScript_EffectTripleKick:: attackcanceler accuracycheck BattleScript_PrintMoveMissed, ACC_CURR_MOVE - attackstring - ppreduce jumpifmove MOVE_TRIPLE_AXEL BS_TripleAxel addbyte sTRIPLE_KICK_POWER, 10 @ triple kick gets +10 power goto BattleScript_HitFromAtkString @@ -8751,10 +8749,7 @@ BattleScript_IntimidateLoop: jumpiftargetally BattleScript_IntimidateLoopIncrement jumpifabsent BS_TARGET, BattleScript_IntimidateLoopIncrement jumpifstatus2 BS_TARGET, STATUS2_SUBSTITUTE, BattleScript_IntimidateLoopIncrement - jumpifholdeffect BS_TARGET, HOLD_EFFECT_CLEAR_AMULET, BattleScript_IntimidatePrevented_Item - jumpifability BS_TARGET, ABILITY_CLEAR_BODY, BattleScript_IntimidatePrevented jumpifability BS_TARGET, ABILITY_HYPER_CUTTER, BattleScript_IntimidatePrevented - jumpifability BS_TARGET, ABILITY_WHITE_SMOKE, BattleScript_IntimidatePrevented .if B_UPDATED_INTIMIDATE >= GEN_8 jumpifability BS_TARGET, ABILITY_INNER_FOCUS, BattleScript_IntimidatePrevented jumpifability BS_TARGET, ABILITY_SCRAPPY, BattleScript_IntimidatePrevented @@ -8795,7 +8790,6 @@ BattleScript_IntimidateContrary_WontIncrease: BattleScript_IntimidatePrevented: call BattleScript_AbilityPopUp pause B_WAIT_TIME_LONG -BattleScript_IntimidatePrevented_Item: setbyte gBattleCommunication STAT_ATK stattextbuffer BS_TARGET printstring STRINGID_STATWASNOTLOWERED @@ -9123,6 +9117,12 @@ BattleScript_AbilityNoStatLoss:: waitmessage B_WAIT_TIME_LONG return +BattleScript_ItemNoStatLoss:: + pause B_WAIT_TIME_SHORT + printstring STRINGID_STATWASNOTLOWERED + waitmessage B_WAIT_TIME_LONG + return + BattleScript_BRNPrevention:: pause B_WAIT_TIME_SHORT printfromtable gBRNPreventionStringIds @@ -9569,6 +9569,7 @@ BattleScript_IgnoresWhileAsleep:: BattleScript_IgnoresAndUsesRandomMove:: printstring STRINGID_PKMNIGNOREDORDERS waitmessage B_WAIT_TIME_LONG + setbyte sMOVE_EFFECT, 0 jumptocalledmove FALSE BattleScript_MoveUsedLoafingAround:: diff --git a/include/battle_scripts.h b/include/battle_scripts.h index 3bb4ed4124..8996abd719 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -168,6 +168,7 @@ extern const u8 BattleScript_MonMadeMoveUseless[]; extern const u8 BattleScript_FlashFireBoost_PPLoss[]; extern const u8 BattleScript_FlashFireBoost[]; extern const u8 BattleScript_AbilityNoStatLoss[]; +extern const u8 BattleScript_ItemNoStatLoss[]; extern const u8 BattleScript_BRNPrevention[]; extern const u8 BattleScript_PRLZPrevention[]; extern const u8 BattleScript_PSNPrevention[]; diff --git a/src/battle_anim.c b/src/battle_anim.c index f051fc7fa8..5691e21e2a 100644 --- a/src/battle_anim.c +++ b/src/battle_anim.c @@ -457,7 +457,7 @@ static u8 GetBattleAnimMoveTargets(u8 battlerArgIndex, u8 *targets) if (IsBattlerAlive(BATTLE_PARTNER(BATTLE_OPPOSITE(targets[0])))) { - targets[2] = BATTLE_PARTNER(BATTLE_OPPOSITE(targets[0])); + targets[2] = BATTLE_PARTNER(BATTLE_OPPOSITE(targets[0])); numTargets++; } break; @@ -525,12 +525,13 @@ static void Cmd_createsprite(void) subpriority = GetSubpriorityForMoveAnim(argVar); - CreateSpriteAndAnimate( - template, + if (CreateSpriteAndAnimate(template, GetBattlerSpriteCoord(gBattleAnimTarget, BATTLER_COORD_X_2), GetBattlerSpriteCoord(gBattleAnimTarget, BATTLER_COORD_Y_PIC_OFFSET), - subpriority); - gAnimVisualTaskCount++; + subpriority) != MAX_SPRITES) // Don't increment the task count if the sprite couldn't be created(i.e. there are too many created sprites atm). + { + gAnimVisualTaskCount++; + } } static void CreateSpriteOnTargets(const struct SpriteTemplate *template, u8 argVar, u8 battlerArgIndex, u8 argsCount, bool32 overwriteAnimTgt) @@ -555,12 +556,13 @@ static void CreateSpriteOnTargets(const struct SpriteTemplate *template, u8 argV if (overwriteAnimTgt) gBattleAnimArgs[battlerArgIndex] = targets[i]; - CreateSpriteAndAnimate( - template, + if (CreateSpriteAndAnimate(template, GetBattlerSpriteCoord(targets[i], BATTLER_COORD_X_2), GetBattlerSpriteCoord(targets[i], BATTLER_COORD_Y_PIC_OFFSET), - subpriority); - gAnimVisualTaskCount++; + subpriority) != MAX_SPRITES) // Don't increment the task count if the sprite couldn't be created(i.e. there are too many created sprites atm). + { + gAnimVisualTaskCount++; + } } } diff --git a/src/battle_interface.c b/src/battle_interface.c index 35f4413a89..f2ca798db8 100644 --- a/src/battle_interface.c +++ b/src/battle_interface.c @@ -196,8 +196,8 @@ static void SpriteCB_StatusSummaryBalls_OnSwitchout(struct Sprite *); static void SpriteCb_MegaTrigger(struct Sprite *); static void MegaIndicator_SetVisibilities(u32 healthboxId, bool32 invisible); static void MegaIndicator_UpdateLevel(u32 healthboxId, u32 level); -static void MegaIndicator_CreateSprites(u32 battlerId, u32 healthboxSpriteId); -static void MegaIndicator_UpdateOamPriorities(u32 healthboxId, u32 oamPriority); +static void MegaIndicator_CreateSprite(u32 battlerId, u32 healthboxSpriteId); +static void MegaIndicator_UpdateOamPriority(u32 healthboxId, u32 oamPriority); static void SpriteCb_MegaIndicator(struct Sprite *); static u8 GetStatusIconForBattlerId(u8, u8); @@ -683,7 +683,7 @@ static const struct SpriteTemplate sSpriteTemplate_MegaTrigger = // data fields for healthboxMain // oam.affineParam holds healthboxRight spriteId -#define hMain_MegaIndicatorIds data[3] // Mega, Alpha, Omega as u8 in data[3], data[3] + 1, data[4] +#define hMain_MegaIndicatorId data[3] #define hMain_HealthBarSpriteId data[5] #define hMain_Battler data[6] #define hMain_Data7 data[7] @@ -798,8 +798,8 @@ u8 CreateBattlerHealthboxSprites(u8 battlerId) healthBarSpritePtr->hBar_Data6 = data6; healthBarSpritePtr->invisible = TRUE; - // Create mega indicator sprites. - MegaIndicator_CreateSprites(battlerId, healthboxLeftSpriteId); + // Create mega indicator sprite. + MegaIndicator_CreateSprite(battlerId, healthboxLeftSpriteId); gBattleStruct->ballSpriteIds[0] = MAX_SPRITES; gBattleStruct->ballSpriteIds[1] = MAX_SPRITES; @@ -934,7 +934,7 @@ void UpdateOamPriorityInAllHealthboxes(u8 priority, bool32 hideHPBoxes) gSprites[healthboxRightSpriteId].oam.priority = priority; gSprites[healthbarSpriteId].oam.priority = priority; - MegaIndicator_UpdateOamPriorities(healthboxLeftSpriteId, priority); + MegaIndicator_UpdateOamPriority(healthboxLeftSpriteId, priority); #if B_HIDE_HEALTHBOX_IN_ANIMS if (hideHPBoxes && IsBattlerAlive(i)) @@ -1509,77 +1509,72 @@ void MegaIndicator_LoadSpritesGfx(void) LoadSpritePalettes(sMegaIndicator_SpritePalettes); } -static bool32 MegaIndicator_ShouldBeInvisible(u32 battlerId, u32 indicatorType) +static bool32 MegaIndicator_ShouldBeInvisible(u32 battlerId, struct Sprite *sprite) { u32 side = GetBattlerSide(battlerId); - if (indicatorType == INDICATOR_MEGA) - { - if (IsBattlerMegaEvolved(battlerId)) - return FALSE; - } - else - { - if (indicatorType == INDICATOR_ALPHA && gBattleMons[battlerId].species != SPECIES_KYOGRE_PRIMAL) - return TRUE; - else if (indicatorType == INDICATOR_OMEGA && gBattleMons[battlerId].species != SPECIES_GROUDON_PRIMAL) - return TRUE; - if (IsBattlerPrimalReverted(battlerId)) - return FALSE; - } - return TRUE; + bool32 megaEvolved = IsBattlerMegaEvolved(battlerId); + bool32 primalReverted = IsBattlerPrimalReverted(battlerId); + + if (!megaEvolved && !primalReverted) + return TRUE; + + if (megaEvolved) + sprite->tType = INDICATOR_MEGA; + else if (primalReverted && gBattleMons[battlerId].species == SPECIES_KYOGRE_PRIMAL) + sprite->tType = INDICATOR_ALPHA; + else if (primalReverted && gBattleMons[battlerId].species == SPECIES_GROUDON_PRIMAL) + sprite->tType = INDICATOR_OMEGA; + + sprite->oam.tileNum = GetSpriteTileStartByTag(sMegaIndicatorTags[sprite->tType][0]); + sprite->oam.paletteNum = IndexOfSpritePaletteTag(sMegaIndicatorTags[sprite->tType][1]); + return FALSE; } -static u8 *MegaIndicator_GetSpriteIds(u32 healthboxSpriteId) +static u8 *MegaIndicator_GetSpriteId(u32 healthboxSpriteId) { - u8 *spriteIds = (u8 *)(&gSprites[healthboxSpriteId].hMain_MegaIndicatorIds); - return spriteIds; + u8 *spriteId = (u8 *)(&gSprites[healthboxSpriteId].hMain_MegaIndicatorId); + return spriteId; } void MegaIndicator_SetVisibilities(u32 healthboxId, bool32 invisible) { - u32 i; - u8 *spriteIds = MegaIndicator_GetSpriteIds(healthboxId); + u8 *spriteId = MegaIndicator_GetSpriteId(healthboxId); u32 battlerId = gSprites[healthboxId].hMain_Battler; if (GetSafariZoneFlag()) return; - for (i = 0; i < INDICATOR_COUNT; i++) - { - if (invisible == TRUE) - gSprites[spriteIds[i]].invisible = TRUE; - else // Try visible. - gSprites[spriteIds[i]].invisible = MegaIndicator_ShouldBeInvisible(battlerId, i); - } + if (invisible == TRUE) + gSprites[*spriteId].invisible = TRUE; + else // Try visible. + gSprites[*spriteId].invisible = MegaIndicator_ShouldBeInvisible(battlerId, &gSprites[*spriteId]); } -static void MegaIndicator_UpdateOamPriorities(u32 healthboxId, u32 oamPriority) +static void MegaIndicator_UpdateOamPriority(u32 healthboxId, u32 oamPriority) { - u32 i; - u8 *spriteIds = MegaIndicator_GetSpriteIds(healthboxId); - for (i = 0; i < INDICATOR_COUNT; i++) - gSprites[spriteIds[i]].oam.priority = oamPriority; + u8 *spriteId = MegaIndicator_GetSpriteId(healthboxId); + gSprites[*spriteId].oam.priority = oamPriority; } static void MegaIndicator_UpdateLevel(u32 healthboxId, u32 level) { u32 i; s16 xDelta = 0; - u8 *spriteIds = MegaIndicator_GetSpriteIds(healthboxId); + u8 *spriteId = MegaIndicator_GetSpriteId(healthboxId); if (level >= 100) xDelta -= 4; else if (level < 10) xDelta += 5; - for (i = 0; i < INDICATOR_COUNT; i++) - gSprites[spriteIds[i]].tLevelXDelta = xDelta; + gSprites[*spriteId].tLevelXDelta = xDelta; } -static void MegaIndicator_CreateSprites(u32 battlerId, u32 healthboxSpriteId) +static void MegaIndicator_CreateSprite(u32 battlerId, u32 healthboxSpriteId) { - u32 position, i, level; - u8 *spriteIds; + struct SpriteTemplate sprTemplate; + u32 position, level; + u8 *spriteId; s16 xHealthbox = 0, y = 0; s32 x = 0; @@ -1589,18 +1584,15 @@ static void MegaIndicator_CreateSprites(u32 battlerId, u32 healthboxSpriteId) x = sIndicatorPositions[position][0]; y += sIndicatorPositions[position][1]; - spriteIds = MegaIndicator_GetSpriteIds(healthboxSpriteId); - for (i = 0; i < INDICATOR_COUNT; i++) - { - struct SpriteTemplate sprTemplate = sSpriteTemplate_MegaIndicator; - sprTemplate.tileTag = sMegaIndicatorTags[i][0]; - sprTemplate.paletteTag = sMegaIndicatorTags[i][1]; - spriteIds[i] = CreateSpriteAtEnd(&sprTemplate, 0, y, 0); - gSprites[spriteIds[i]].tType = i; - gSprites[spriteIds[i]].tBattler = battlerId; - gSprites[spriteIds[i]].tPosX = x; - gSprites[spriteIds[i]].invisible = TRUE; - } + spriteId = MegaIndicator_GetSpriteId(healthboxSpriteId); + sprTemplate = sSpriteTemplate_MegaIndicator; + sprTemplate.tileTag = sMegaIndicatorTags[INDICATOR_MEGA][0]; + sprTemplate.paletteTag = sMegaIndicatorTags[INDICATOR_MEGA][1]; + *spriteId = CreateSpriteAtEnd(&sprTemplate, 0, y, 0); + gSprites[*spriteId].tType = INDICATOR_MEGA; + gSprites[*spriteId].tBattler = battlerId; + gSprites[*spriteId].tPosX = x; + gSprites[*spriteId].invisible = TRUE; } static void SpriteCb_MegaIndicator(struct Sprite *sprite) diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 0faeef28d6..6bbe3d90b2 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -11955,10 +11955,17 @@ static u32 ChangeStatBuffs(s8 statValue, u32 statId, u32 flags, const u8 *BS_ptr { BattleScriptPush(BS_ptr); gBattleScripting.battler = gActiveBattler; - gBattlerAbility = gActiveBattler; - gBattlescriptCurrInstr = BattleScript_AbilityNoStatLoss; - gLastUsedAbility = activeBattlerAbility; - RecordAbilityBattle(gActiveBattler, gLastUsedAbility); + if (GetBattlerHoldEffect(gActiveBattler, TRUE) == HOLD_EFFECT_CLEAR_AMULET) + { + gBattlescriptCurrInstr = BattleScript_ItemNoStatLoss; + } + else + { + gBattlerAbility = gActiveBattler; + gBattlescriptCurrInstr = BattleScript_AbilityNoStatLoss; + gLastUsedAbility = activeBattlerAbility; + RecordAbilityBattle(gActiveBattler, gLastUsedAbility); + } gSpecialStatuses[gActiveBattler].statLowered = TRUE; } } @@ -12098,15 +12105,15 @@ static u32 ChangeStatBuffs(s8 statValue, u32 statId, u32 flags, const u8 *BS_ptr { gBattleCommunication[MULTISTRING_CHOOSER] = (gBattlerTarget == gActiveBattler); gProtectStructs[gActiveBattler].statRaised = TRUE; - + // check mirror herb for (index = 0; index < gBattlersCount; index++) { if (GetBattlerSide(index) == GetBattlerSide(gActiveBattler)) - continue; // Only triggers on opposing side + continue; // Only triggers on opposing side if (GetBattlerHoldEffect(index, TRUE) == HOLD_EFFECT_MIRROR_HERB && gBattleMons[index].statStages[statId] < MAX_STAT_STAGE) - { + { gProtectStructs[index].eatMirrorHerb = 1; gTotemBoosts[index].stats |= (1 << (statId - 1)); // -1 to start at atk gTotemBoosts[index].statChanges[statId - 1] = statValue; @@ -16300,7 +16307,7 @@ void BS_CheckParentalBondCounter(void) void BS_GetBattlerSide(void) { NATIVE_ARGS(u8 battler); - gBattleCommunication[0] = GetBattlerSide(GetBattlerForBattleScript(cmd->battler)); + gBattleCommunication[0] = GetBattlerSide(GetBattlerForBattleScript(cmd->battler)); gBattlescriptCurrInstr = cmd->nextInstr; } @@ -16326,7 +16333,7 @@ void BS_TrySymbiosis(void) gBattlescriptCurrInstr = BattleScript_SymbiosisActivates; return; } - + gBattlescriptCurrInstr = cmd->nextInstr; } diff --git a/src/debug.c b/src/debug.c index 7eda7d973e..43ebb01a82 100644 --- a/src/debug.c +++ b/src/debug.c @@ -750,6 +750,7 @@ static void DebugAction_DestroyExtraWindow(u8 taskId) ClearStdWindowAndFrame(gTasks[taskId].data[2], TRUE); RemoveWindow(gTasks[taskId].data[2]); + DestroyListMenuTask(gTasks[taskId].data[0], NULL, NULL); DestroyTask(taskId); ScriptContext_Enable(); UnfreezeObjectEvents(); diff --git a/test/ability_clear_body.c b/test/ability_clear_body.c new file mode 100644 index 0000000000..11789aaf60 --- /dev/null +++ b/test/ability_clear_body.c @@ -0,0 +1,38 @@ +#include "global.h" +#include "test_battle.h" + +SINGLE_BATTLE_TEST("Clear Body prevents intimidate") +{ + s16 turnOneHit; + s16 turnTwoHit; + + GIVEN { + PLAYER(SPECIES_EKANS) { Ability(ABILITY_SHED_SKIN); }; + PLAYER(SPECIES_EKANS) { Ability(ABILITY_INTIMIDATE); }; + OPPONENT(SPECIES_BELDUM) { Ability(ABILITY_CLEAR_BODY); }; + } WHEN { + TURN { MOVE(opponent, MOVE_TACKLE); } + TURN { SWITCH(player, 1); MOVE(opponent, MOVE_TACKLE); } + + } SCENE { + HP_BAR(player, captureDamage: &turnOneHit); + ABILITY_POPUP(player, ABILITY_INTIMIDATE); + NONE_OF { ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); } + ABILITY_POPUP(opponent, ABILITY_CLEAR_BODY); + MESSAGE("Foe Beldum's Clear Body prevents stat loss!"); + HP_BAR(player, captureDamage: &turnTwoHit); + } THEN { + EXPECT_EQ(turnOneHit, turnTwoHit); + } +} + +TO_DO_BATTLE_TEST("Clear Body prevents stat stage reduction from moves"); // Growl, Leer, Confide, Fake Tears, Scary Face, Sweet Scent, Sand Attack (Attack, Defense, Sp. Attack, Sp. Defense, Speed, Evasion, Accuracy +TO_DO_BATTLE_TEST("Clear Body prevents Sticky Web"); +TO_DO_BATTLE_TEST("Clear Body doesn't prevent stat stage reduction from moves used by the user"); // e.g. Superpower +TO_DO_BATTLE_TEST("Clear Body doesn't prevent Speed reduction from Iron Ball"); +TO_DO_BATTLE_TEST("Clear Body doesn't prevent Speed reduction from paralysis"); +TO_DO_BATTLE_TEST("Clear Body doesn't prevent Attack reduction from burn"); +TO_DO_BATTLE_TEST("Clear Body doesn't prevent receiving negative stat changes from Baton Pass"); +TO_DO_BATTLE_TEST("Clear Body doesn't prevent Topsy-Turvy"); +TO_DO_BATTLE_TEST("Clear Body doesn't prevent Spectral Thief from resetting positive stat changes"); +TO_DO_BATTLE_TEST("Clear Body is ignored by Mold Breaker"); diff --git a/test/ability_full_metal_body.c b/test/ability_full_metal_body.c new file mode 100644 index 0000000000..c47ce9d4dc --- /dev/null +++ b/test/ability_full_metal_body.c @@ -0,0 +1,38 @@ +#include "global.h" +#include "test_battle.h" + +SINGLE_BATTLE_TEST("Full Metal Body prevents intimidate") +{ + s16 turnOneHit; + s16 turnTwoHit; + + GIVEN { + PLAYER(SPECIES_EKANS) { Ability(ABILITY_SHED_SKIN); }; + PLAYER(SPECIES_EKANS) { Ability(ABILITY_INTIMIDATE); }; + OPPONENT(SPECIES_SOLGALEO) { Ability(ABILITY_FULL_METAL_BODY); }; + } WHEN { + TURN { MOVE(opponent, MOVE_TACKLE); } + TURN { SWITCH(player, 1); MOVE(opponent, MOVE_TACKLE); } + + } SCENE { + HP_BAR(player, captureDamage: &turnOneHit); + ABILITY_POPUP(player, ABILITY_INTIMIDATE); + NONE_OF { ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); } + ABILITY_POPUP(opponent, ABILITY_FULL_METAL_BODY); + MESSAGE("Foe Solgaleo's Full Metal Body prevents stat loss!"); + HP_BAR(player, captureDamage: &turnTwoHit); + } THEN { + EXPECT_EQ(turnOneHit, turnTwoHit); + } +} + +TO_DO_BATTLE_TEST("Full Metal Body prevents stat stage reduction from moves"); // Growl, Leer, Confide, Fake Tears, Scary Face, Sweet Scent, Sand Attack (Attack, Defense, Sp. Attack, Sp. Defense, Speed, Evasion, Accuracy +TO_DO_BATTLE_TEST("Full Metal Body prevents Sticky Web"); +TO_DO_BATTLE_TEST("Full Metal Body doesn't prevent stat stage reduction from moves used by the user"); // e.g. Superpower +TO_DO_BATTLE_TEST("Full Metal Body doesn't prevent Speed reduction from Iron Ball"); +TO_DO_BATTLE_TEST("Full Metal Body doesn't prevent Speed reduction from paralysis"); +TO_DO_BATTLE_TEST("Full Metal Body doesn't prevent Attack reduction from burn"); +TO_DO_BATTLE_TEST("Full Metal Body doesn't prevent receiving negative stat changes from Baton Pass"); +TO_DO_BATTLE_TEST("Full Metal Body doesn't prevent Topsy-Turvy"); +TO_DO_BATTLE_TEST("Full Metal Body doesn't prevent Spectral Thief from resetting positive stat changes"); +TO_DO_BATTLE_TEST("Full Metal Body is ignored by Mold Breaker"); diff --git a/test/ability_hyper_cutter.c b/test/ability_hyper_cutter.c new file mode 100644 index 0000000000..67dac4c66e --- /dev/null +++ b/test/ability_hyper_cutter.c @@ -0,0 +1,35 @@ +#include "global.h" +#include "test_battle.h" + +SINGLE_BATTLE_TEST("Hyper Cutter prevents intimidate") +{ + s16 turnOneHit; + s16 turnTwoHit; + + GIVEN { + PLAYER(SPECIES_EKANS) { Ability(ABILITY_SHED_SKIN); }; + PLAYER(SPECIES_EKANS) { Ability(ABILITY_INTIMIDATE); }; + OPPONENT(SPECIES_KRABBY) { Ability(ABILITY_HYPER_CUTTER); }; + } WHEN { + TURN { MOVE(opponent, MOVE_TACKLE); } + TURN { SWITCH(player, 1); MOVE(opponent, MOVE_TACKLE); } + + } SCENE { + HP_BAR(player, captureDamage: &turnOneHit); + ABILITY_POPUP(player, ABILITY_INTIMIDATE); + NONE_OF { ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); } + ABILITY_POPUP(opponent, ABILITY_HYPER_CUTTER); + MESSAGE("Foe Krabby's Attack was not lowered!"); + HP_BAR(player, captureDamage: &turnTwoHit); + } THEN { + EXPECT_EQ(turnOneHit, turnTwoHit); + } +} + +TO_DO_BATTLE_TEST("Hyper Cutter prevents Attack stage reduction from moves"); // Growl +TO_DO_BATTLE_TEST("Hyper Cutter doesn't prevent Attack stage reduction from moves used by the user"); // e.g. Superpower +TO_DO_BATTLE_TEST("Hyper Cutter doesn't prevent Attack reduction from burn"); +TO_DO_BATTLE_TEST("Hyper Cutter doesn't prevent receiving negative Attack stage changes from Baton Pass"); +TO_DO_BATTLE_TEST("Hyper Cutter doesn't prevent Topsy-Turvy"); +TO_DO_BATTLE_TEST("Hyper Cutter doesn't prevent Spectral Thief from resetting positive Attack stage changes"); +TO_DO_BATTLE_TEST("Hyper Cutter is ignored by Mold Breaker"); diff --git a/test/ability_inner_focus.c b/test/ability_inner_focus.c new file mode 100644 index 0000000000..5b713475dc --- /dev/null +++ b/test/ability_inner_focus.c @@ -0,0 +1,57 @@ +#include "global.h" +#include "test_battle.h" + +SINGLE_BATTLE_TEST("Inner Focus prevents intimidate") +{ + s16 turnOneHit; + s16 turnTwoHit; + + GIVEN { + ASSUME(B_UPDATED_INTIMIDATE >= GEN_8); + PLAYER(SPECIES_EKANS) { Ability(ABILITY_SHED_SKIN); }; + PLAYER(SPECIES_EKANS) { Ability(ABILITY_INTIMIDATE); }; + OPPONENT(SPECIES_ZUBAT) { Ability(ABILITY_INNER_FOCUS); }; + } WHEN { + TURN { MOVE(opponent, MOVE_TACKLE); } + TURN { SWITCH(player, 1); MOVE(opponent, MOVE_TACKLE); } + + } SCENE { + HP_BAR(player, captureDamage: &turnOneHit); + ABILITY_POPUP(player, ABILITY_INTIMIDATE); + NONE_OF { ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); } + ABILITY_POPUP(opponent, ABILITY_INNER_FOCUS); + MESSAGE("Foe Zubat's Attack was not lowered!"); + HP_BAR(player, captureDamage: &turnTwoHit); + } THEN { + EXPECT_EQ(turnOneHit, turnTwoHit); + } +} + +SINGLE_BATTLE_TEST("Inner Focus prevents flinching") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_ZUBAT) { Ability(ABILITY_INNER_FOCUS); }; + } WHEN { + TURN { MOVE(player, MOVE_FAKE_OUT); + MOVE(opponent, MOVE_TACKLE); + } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_FAKE_OUT, player); + NONE_OF { MESSAGE("Foe Zubat flinched!"); } + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent); + } +} + +SINGLE_BATTLE_TEST("Inner Focus is ignored by Mold Breaker") +{ + GIVEN { + PLAYER(SPECIES_PINSIR) { Ability(ABILITY_MOLD_BREAKER); }; + OPPONENT(SPECIES_ZUBAT) { Ability(ABILITY_INNER_FOCUS); }; + } WHEN { + TURN { MOVE(player, MOVE_FAKE_OUT); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_FAKE_OUT, player); + MESSAGE("Foe Zubat flinched!"); + } +} diff --git a/test/ability_own_tempo.c b/test/ability_own_tempo.c new file mode 100644 index 0000000000..2440ac7798 --- /dev/null +++ b/test/ability_own_tempo.c @@ -0,0 +1,119 @@ +#include "global.h" +#include "test_battle.h" + +SINGLE_BATTLE_TEST("Own Tempo prevents intimidate") +{ + s16 turnOneHit; + s16 turnTwoHit; + + GIVEN { + ASSUME(B_UPDATED_INTIMIDATE >= GEN_8); + PLAYER(SPECIES_EKANS) { Ability(ABILITY_SHED_SKIN); }; + PLAYER(SPECIES_EKANS) { Ability(ABILITY_INTIMIDATE); }; + OPPONENT(SPECIES_SLOWPOKE) { Ability(ABILITY_OWN_TEMPO); }; + } WHEN { + TURN { MOVE(opponent, MOVE_TACKLE); } + TURN { SWITCH(player, 1); MOVE(opponent, MOVE_TACKLE); } + + } SCENE { + HP_BAR(player, captureDamage: &turnOneHit); + ABILITY_POPUP(player, ABILITY_INTIMIDATE); + NONE_OF { ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); } + ABILITY_POPUP(opponent, ABILITY_OWN_TEMPO); + MESSAGE("Foe Slowpoke's Attack was not lowered!"); + HP_BAR(player, captureDamage: &turnTwoHit); + } THEN { + EXPECT_EQ(turnOneHit, turnTwoHit); + } +} + +SINGLE_BATTLE_TEST("Own Tempo prevents confusion from moves by the opponent") +{ + GIVEN { + ASSUME(gBattleMoves[MOVE_CONFUSE_RAY].effect == EFFECT_CONFUSE); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_SLOWPOKE) { Ability(ABILITY_OWN_TEMPO); }; + } WHEN { + TURN { MOVE(player, MOVE_CONFUSE_RAY); } + } SCENE { + ABILITY_POPUP(opponent, ABILITY_OWN_TEMPO); + MESSAGE("Foe Slowpoke's Own Tempo prevents confusion!"); + } +} + +SINGLE_BATTLE_TEST("Own Tempo prevents confusion from moves by the user") +{ + GIVEN { + ASSUME(gBattleMoves[MOVE_PETAL_DANCE].effect == EFFECT_RAMPAGE); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_SLOWPOKE) { Ability(ABILITY_OWN_TEMPO); }; + } WHEN { + TURN { MOVE(opponent, MOVE_PETAL_DANCE); } + TURN { MOVE(opponent, MOVE_PETAL_DANCE); } + TURN { MOVE(opponent, MOVE_PETAL_DANCE); } + TURN { MOVE(opponent, MOVE_PETAL_DANCE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_PETAL_DANCE, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_PETAL_DANCE, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_PETAL_DANCE, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_PETAL_DANCE, opponent); + NONE_OF { MESSAGE("Foe Slowpoke became confused due to fatigue!"); } + } +} + +SINGLE_BATTLE_TEST("Own Tempo cures confusion obtained from an opponent with Mold Breaker") +{ + KNOWN_FAILING; + GIVEN { + ASSUME(gBattleMoves[MOVE_CONFUSE_RAY].effect == EFFECT_CONFUSE); + PLAYER(SPECIES_PINSIR) { Ability(ABILITY_MOLD_BREAKER); }; + OPPONENT(SPECIES_SLOWPOKE) { Ability(ABILITY_OWN_TEMPO); }; + } WHEN { + TURN { MOVE(player, MOVE_CONFUSE_RAY); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_CONFUSE_RAY, player); + MESSAGE("Foe Slowpoke became confused!"); + NONE_OF { + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_CONFUSION, opponent); + } + ABILITY_POPUP(opponent, ABILITY_OWN_TEMPO); + MESSAGE("Foe Slowpoke's Own Tempo cured its confusion problem!"); + } +} + +SINGLE_BATTLE_TEST("Own Tempo cures confusion if it's obtained via Skill Swap") +{ + GIVEN { + ASSUME(gBattleMoves[MOVE_CONFUSE_RAY].effect == EFFECT_CONFUSE); + ASSUME(gBattleMoves[MOVE_SKILL_SWAP].effect == EFFECT_SKILL_SWAP); + PLAYER(SPECIES_SLOWPOKE) { Ability(ABILITY_OWN_TEMPO); }; + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_CONFUSE_RAY); } + TURN { MOVE(player, MOVE_SKILL_SWAP); + MOVE(opponent, MOVE_TACKLE); + } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_CONFUSE_RAY, player); + MESSAGE("Foe Wobbuffet became confused!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SKILL_SWAP, player); + ABILITY_POPUP(opponent, ABILITY_OWN_TEMPO); + MESSAGE("Foe Wobbuffet's Own Tempo cured its confusion problem!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent); + } +} + +SINGLE_BATTLE_TEST("Own Tempo prevents confusion from items") +{ + GIVEN { + ASSUME(gItems[ITEM_BERSERK_GENE].holdEffect == HOLD_EFFECT_BERSERK_GENE); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_SLOWPOKE) { Ability(ABILITY_OWN_TEMPO); Item(ITEM_BERSERK_GENE); }; + } WHEN { + TURN { MOVE(opponent, MOVE_TACKLE); } + } SCENE { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent); + ABILITY_POPUP(opponent, ABILITY_OWN_TEMPO); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent); + } +} diff --git a/test/ability_pastel_veil.c b/test/ability_pastel_veil.c index ddc0806198..363c018c10 100644 --- a/test/ability_pastel_veil.c +++ b/test/ability_pastel_veil.c @@ -33,18 +33,20 @@ DOUBLE_BATTLE_TEST("Pastel Veil prevents Poison Sting poison on partner") SINGLE_BATTLE_TEST("Pastel Veil immediately cures Mold Breaker poison") { + KNOWN_FAILING; GIVEN { ASSUME(gBattleMoves[MOVE_TOXIC].effect == EFFECT_TOXIC); PLAYER(SPECIES_PINSIR) { Ability(ABILITY_MOLD_BREAKER); } OPPONENT(SPECIES_PONYTA_GALARIAN) { Ability(ABILITY_PASTEL_VEIL); } } WHEN { - TURN { MOVE(player, MOVE_TOXIC); } + TURN { MOVE(player, MOVE_TOXIC); MOVE(opponent, MOVE_TACKLE); } } SCENE { ANIMATION(ANIM_TYPE_MOVE, MOVE_TOXIC, player); STATUS_ICON(opponent, badPoison: TRUE); ABILITY_POPUP(opponent, ABILITY_PASTEL_VEIL); MESSAGE("Foe Ponyta's Pastel Veil cured its poison problem!"); STATUS_ICON(opponent, none: TRUE); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent); } } diff --git a/test/ability_scrappy.c b/test/ability_scrappy.c new file mode 100644 index 0000000000..b4f83afcf8 --- /dev/null +++ b/test/ability_scrappy.c @@ -0,0 +1,66 @@ +#include "global.h" +#include "test_battle.h" + +SINGLE_BATTLE_TEST("Scrappy prevents intimidate") +{ + s16 turnOneHit; + s16 turnTwoHit; + + GIVEN { + ASSUME(B_UPDATED_INTIMIDATE >= GEN_8); + PLAYER(SPECIES_EKANS) { Ability(ABILITY_SHED_SKIN); }; + PLAYER(SPECIES_EKANS) { Ability(ABILITY_INTIMIDATE); }; + OPPONENT(SPECIES_KANGASKHAN) { Ability(ABILITY_SCRAPPY); }; + } WHEN { + TURN { MOVE(opponent, MOVE_TACKLE); } + TURN { SWITCH(player, 1); MOVE(opponent, MOVE_TACKLE); } + + } SCENE { + HP_BAR(player, captureDamage: &turnOneHit); + ABILITY_POPUP(player, ABILITY_INTIMIDATE); + NONE_OF { ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); } + ABILITY_POPUP(opponent, ABILITY_SCRAPPY); + MESSAGE("Foe Kangaskhan's Attack was not lowered!"); + HP_BAR(player, captureDamage: &turnTwoHit); + } THEN { + EXPECT_EQ(turnOneHit, turnTwoHit); + } +} + +SINGLE_BATTLE_TEST("Scrappy allows to hit Ghost-type Pokémon with Normal- and Fighting-type moves") +{ + u32 move; + PARAMETRIZE { move = MOVE_TACKLE; } + PARAMETRIZE { move = MOVE_KARATE_CHOP; } + + GIVEN { + PLAYER(SPECIES_KANGASKHAN) { Ability(ABILITY_SCRAPPY); }; + OPPONENT(SPECIES_GASTLY); + } WHEN { + TURN { MOVE(player, move); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, move, player); + HP_BAR(opponent); + } +} + +SINGLE_BATTLE_TEST("Scrappy doesn't bypass a Ghost-type's Wonder Guard") +{ + u32 move; + PARAMETRIZE { move = MOVE_TACKLE; } + PARAMETRIZE { move = MOVE_KARATE_CHOP; } + + GIVEN { + PLAYER(SPECIES_KANGASKHAN) { Ability(ABILITY_SCRAPPY); }; + OPPONENT(SPECIES_SHEDINJA) { Ability(ABILITY_WONDER_GUARD); }; + } WHEN { + TURN { MOVE(player, move); } + } SCENE { + NONE_OF { + ANIMATION(ANIM_TYPE_MOVE, move, player); + HP_BAR(opponent); + } + ABILITY_POPUP(opponent, ABILITY_WONDER_GUARD); + MESSAGE("Foe Shedinja avoided damage with Wonder Guard!"); + } +} diff --git a/test/ability_white_smoke.c b/test/ability_white_smoke.c new file mode 100644 index 0000000000..9689aee6c2 --- /dev/null +++ b/test/ability_white_smoke.c @@ -0,0 +1,39 @@ +#include "global.h" +#include "test_battle.h" + +SINGLE_BATTLE_TEST("White Smoke prevents intimidate") +{ + s16 turnOneHit; + s16 turnTwoHit; + + GIVEN { + PLAYER(SPECIES_EKANS) { Ability(ABILITY_SHED_SKIN); }; + PLAYER(SPECIES_EKANS) { Ability(ABILITY_INTIMIDATE); }; + OPPONENT(SPECIES_TORKOAL) { Ability(ABILITY_WHITE_SMOKE); }; + } WHEN { + TURN { MOVE(opponent, MOVE_TACKLE); } + TURN { SWITCH(player, 1); MOVE(opponent, MOVE_TACKLE); } + + } SCENE { + HP_BAR(player, captureDamage: &turnOneHit); + ABILITY_POPUP(player, ABILITY_INTIMIDATE); + NONE_OF { ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); } + ABILITY_POPUP(opponent, ABILITY_WHITE_SMOKE); + MESSAGE("Foe Torkoal's White Smoke prevents stat loss!"); + HP_BAR(player, captureDamage: &turnTwoHit); + } THEN { + EXPECT_EQ(turnOneHit, turnTwoHit); + } +} + + +TO_DO_BATTLE_TEST("White Smoke prevents stat stage reduction from moves"); // Growl, Leer, Confide, Fake Tears, Scary Face, Sweet Scent, Sand Attack (Attack, Defense, Sp. Attack, Sp. Defense, Speed, Evasion, Accuracy +TO_DO_BATTLE_TEST("White Smoke prevents Sticky Web"); +TO_DO_BATTLE_TEST("White Smoke doesn't prevent stat stage reduction from moves used by the user"); // e.g. Superpower +TO_DO_BATTLE_TEST("White Smoke doesn't prevent Speed reduction from Iron Ball"); +TO_DO_BATTLE_TEST("White Smoke doesn't prevent Speed reduction from paralysis"); +TO_DO_BATTLE_TEST("White Smoke doesn't prevent Attack reduction from burn"); +TO_DO_BATTLE_TEST("White Smoke doesn't prevent receiving negative stat changes from Baton Pass"); +TO_DO_BATTLE_TEST("White Smoke doesn't prevent Topsy-Turvy"); +TO_DO_BATTLE_TEST("White Smoke doesn't prevent Spectral Thief from resetting positive stat changes"); +TO_DO_BATTLE_TEST("White Smoke is ignored by Mold Breaker"); diff --git a/test/hold_effect_clear_amulet.c b/test/hold_effect_clear_amulet.c new file mode 100644 index 0000000000..83c887b2d4 --- /dev/null +++ b/test/hold_effect_clear_amulet.c @@ -0,0 +1,111 @@ +#include "global.h" +#include "test_battle.h" + +ASSUMPTIONS +{ + ASSUME(gItems[ITEM_CLEAR_AMULET].holdEffect == HOLD_EFFECT_CLEAR_AMULET); +} + +SINGLE_BATTLE_TEST("Clear Amulet prevents Intimidate") +{ + s16 turnOneHit; + s16 turnTwoHit; + + GIVEN { + PLAYER(SPECIES_EKANS) { Ability(ABILITY_SHED_SKIN); }; + PLAYER(SPECIES_EKANS) { Ability(ABILITY_INTIMIDATE); }; + OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_CLEAR_AMULET); }; + } WHEN { + TURN { MOVE(opponent, MOVE_TACKLE); } + TURN { SWITCH(player, 1); MOVE(opponent, MOVE_TACKLE); } + } SCENE { + HP_BAR(player, captureDamage: &turnOneHit); + ABILITY_POPUP(player, ABILITY_INTIMIDATE); + NONE_OF { ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); } + MESSAGE("Foe Wobbuffet's Attack was not lowered!"); + HP_BAR(player, captureDamage: &turnTwoHit); + } THEN { + EXPECT_EQ(turnOneHit, turnTwoHit); + } +} + +SINGLE_BATTLE_TEST("Clear Amulet prevents stat reducing effects") +{ + u32 move; + + PARAMETRIZE { move = MOVE_GROWL; } + PARAMETRIZE { move = MOVE_LEER; } + PARAMETRIZE { move = MOVE_CONFIDE; } + PARAMETRIZE { move = MOVE_FAKE_TEARS; } + PARAMETRIZE { move = MOVE_SCARY_FACE; } + PARAMETRIZE { move = MOVE_SWEET_SCENT; } + PARAMETRIZE { move = MOVE_SAND_ATTACK; } + + GIVEN { + ASSUME(gBattleMoves[MOVE_GROWL].effect == EFFECT_ATTACK_DOWN); + ASSUME(gBattleMoves[MOVE_LEER].effect == EFFECT_DEFENSE_DOWN); + ASSUME(gBattleMoves[MOVE_CONFIDE].effect == EFFECT_SPECIAL_ATTACK_DOWN); + ASSUME(gBattleMoves[MOVE_FAKE_TEARS].effect == EFFECT_SPECIAL_DEFENSE_DOWN_2); + ASSUME(gBattleMoves[MOVE_SCARY_FACE].effect == EFFECT_SPEED_DOWN_2); + ASSUME(B_UPDATED_MOVE_DATA >= GEN_6); + ASSUME(gBattleMoves[MOVE_SWEET_SCENT].effect == EFFECT_EVASION_DOWN_2); + ASSUME(gBattleMoves[MOVE_SAND_ATTACK].effect == EFFECT_ACCURACY_DOWN); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_CLEAR_AMULET); }; + } WHEN { + TURN { MOVE(player, move); } + } SCENE { + NONE_OF { ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); } + switch (move) + { + case MOVE_GROWL: + MESSAGE("Foe Wobbuffet's Attack was not lowered!"); + break; + case MOVE_LEER: + MESSAGE("Foe Wobbuffet's Defense was not lowered!"); + break; + case MOVE_CONFIDE: + MESSAGE("Foe Wobbuffet's Sp. Atk was not lowered!"); + break; + case MOVE_FAKE_TEARS: + MESSAGE("Foe Wobbuffet's Sp. Def was not lowered!"); + break; + case MOVE_SCARY_FACE: + MESSAGE("Foe Wobbuffet's Speed was not lowered!"); + break; + case MOVE_SWEET_SCENT: + MESSAGE("Foe Wobbuffet's evasiveness was not lowered!"); + break; + case MOVE_SAND_ATTACK: + MESSAGE("Foe Wobbuffet's accuracy was not lowered!"); + break; + } + } +} + +SINGLE_BATTLE_TEST("Clear Amulet prevents secondary effects that reduce stats") +{ + u32 move; + + PARAMETRIZE { move = MOVE_AURORA_BEAM; } + PARAMETRIZE { move = MOVE_ROCK_SMASH; } + PARAMETRIZE { move = MOVE_SNARL; } + PARAMETRIZE { move = MOVE_PSYCHIC; } + PARAMETRIZE { move = MOVE_BUBBLE_BEAM; } + PARAMETRIZE { move = MOVE_MUD_SLAP; } + + GIVEN { + ASSUME(gBattleMoves[MOVE_AURORA_BEAM].effect == EFFECT_ATTACK_DOWN_HIT); + ASSUME(gBattleMoves[MOVE_ROCK_SMASH].effect == EFFECT_DEFENSE_DOWN_HIT); + ASSUME(gBattleMoves[MOVE_SNARL].effect == EFFECT_SPECIAL_ATTACK_DOWN_HIT); + ASSUME(gBattleMoves[MOVE_PSYCHIC].effect == EFFECT_SPECIAL_DEFENSE_DOWN_HIT); + ASSUME(gBattleMoves[MOVE_BUBBLE_BEAM].effect == EFFECT_SPEED_DOWN_HIT); + ASSUME(gBattleMoves[MOVE_MUD_SLAP].effect == EFFECT_ACCURACY_DOWN_HIT); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_CLEAR_AMULET); }; + } WHEN { + TURN { MOVE(player, MOVE_ROCK_SMASH); } + } SCENE { + NONE_OF { ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); } + } +}