diff --git a/data/battle_anim_scripts.s b/data/battle_anim_scripts.s index e162989ac3..13982a59f9 100644 --- a/data/battle_anim_scripts.s +++ b/data/battle_anim_scripts.s @@ -17848,10 +17848,54 @@ ElectroShotUnleash: blendoff end +Move_SPICY_EXTRACT:: + loadspritegfx ANIM_TAG_SMALL_EMBER + loadspritegfx ANIM_TAG_POISON_BUBBLE + loadspritegfx ANIM_TAG_SMALL_BUBBLES + createvisualtask AnimTask_BlendParticle, 5, ANIM_TAG_POISON_BUBBLE, 0, 12, 12, 0x061D @Orange + call SludgeBombProjectile + call SludgeBombProjectile + call SludgeBombProjectile + call SludgeBombProjectile + call SludgeBombProjectile + call SludgeBombProjectile + call SludgeBombProjectile + call SludgeBombProjectile + call SludgeBombProjectile + call SludgeBombProjectile + createvisualtask AnimTask_ShakeMon2, 5, ANIM_TARGET, 3, 0, 15, 1 + createvisualtask AnimTask_BlendBattleAnimPal, 10, F_PAL_TARGET, 3, 0, 9, RGB_RED + createsprite gSludgeBombHitParticleSpriteTemplate, ANIM_TARGET, 2, 42, 27, 20 + createsprite gSludgeBombHitParticleSpriteTemplate, ANIM_TARGET, 2, -27, 44, 20 + createsprite gSludgeBombHitParticleSpriteTemplate, ANIM_TARGET, 2, 39, -28, 20 + createsprite gSludgeBombHitParticleSpriteTemplate, ANIM_TARGET, 2, -42, -42, 20 + playsewithpan SE_M_FLAME_WHEEL, SOUND_PAN_TARGET + delay 5 + createsprite gSludgeBombHitParticleSpriteTemplate, ANIM_TARGET, 2, 0, 40, 20 + createsprite gSludgeBombHitParticleSpriteTemplate, ANIM_TARGET, 2, -8, -44, 20 + createsprite gSludgeBombHitParticleSpriteTemplate, ANIM_TARGET, 2, -46, -28, 20 + createsprite gSludgeBombHitParticleSpriteTemplate, ANIM_TARGET, 2, 46, 9, 20 + playsewithpan SE_M_FLAME_WHEEL, SOUND_PAN_TARGET + delay 5 + createsprite gSludgeBombHitParticleSpriteTemplate, ANIM_TARGET, 2, 42, 0, 20 + createsprite gSludgeBombHitParticleSpriteTemplate, ANIM_TARGET, 2, -43, -12, 20 + createsprite gSludgeBombHitParticleSpriteTemplate, ANIM_TARGET, 2, 16, -46, 20 + createsprite gSludgeBombHitParticleSpriteTemplate, ANIM_TARGET, 2, -16, 44, 20 + playsewithpan SE_M_FLAME_WHEEL, SOUND_PAN_TARGET + call LetsSnuggleForeverTears + delay 0x8 + call LetsSnuggleForeverTears + delay 0x8 + call LetsSnuggleForeverTears + waitsound + waitforvisualfinish + createvisualtask AnimTask_BlendBattleAnimPal, 10, F_PAL_TARGET, 3, 9, 0, RGB_RED + waitforvisualfinish + end + Move_TERA_BLAST:: Move_AXE_KICK:: Move_ORDER_UP:: -Move_SPICY_EXTRACT:: Move_SPIN_OUT:: Move_POPULATION_BOMB:: Move_GLAIVE_RUSH:: diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index ddbd8ec62b..394ebd8e17 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -52,6 +52,43 @@ BattleScript_LowerAtkSpAtkTrySpAtk:: BattleScript_LowerAtkSpAtkEnd: return +BattleScript_EffectSpicyExtract:: + attackcanceler + jumpifsubstituteblocks BattleScript_ButItFailed + accuracycheck BattleScript_PrintMoveMissed, ACC_CURR_MOVE + jumpifstat BS_TARGET, CMP_LESS_THAN, STAT_ATK, MAX_STAT_STAGE BattleScript_SpicyExtract_CheckShouldSkipAttackAnim + jumpifstat BS_TARGET, CMP_GREATER_THAN, STAT_DEF, MIN_STAT_STAGE, BattleScript_SpicyExtract_CheckShouldSkipAttackAnim + goto BattleScript_ButItFailed +BattleScript_SpicyExtract_CheckShouldSkipAttackAnim: + jumpifbyte CMP_NOT_EQUAL, gBattleCommunication, 0, BattleScript_SpicyExtract_RaiseAtk + attackstring + ppreduce + bicword gHitMarker, HITMARKER_NO_ATTACKSTRING | HITMARKER_NO_PPDEDUCT + goto BattleScript_SpicyExtract_SkipAttackAnim +BattleScript_SpicyExtract_RaiseAtk: + attackstring + ppreduce + attackanimation + waitanimation +BattleScript_SpicyExtract_SkipAttackAnim: + setbyte sSTAT_ANIM_PLAYED, FALSE + playstatchangeanimation BS_TARGET, BIT_ATK, STAT_CHANGE_BY_TWO + setstatchanger STAT_ATK, 2, FALSE + statbuffchange STAT_CHANGE_ALLOW_PTR, BattleScript_EffectSpicyExtractDefenseDown + jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, B_MSG_STAT_WONT_INCREASE, BattleScript_EffectSpicyExtractDefenseDown + printfromtable gStatUpStringIds + waitmessage B_WAIT_TIME_LONG +BattleScript_EffectSpicyExtractDefenseDown: + setbyte sSTAT_ANIM_PLAYED, FALSE + playstatchangeanimation BS_TARGET, BIT_DEF, STAT_CHANGE_NEGATIVE | STAT_CHANGE_BY_TWO + setstatchanger STAT_DEF, 2, TRUE + statbuffchange STAT_CHANGE_ALLOW_PTR, BattleScript_EffectSpicyExtract_End + jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, B_MSG_STAT_WONT_DECREASE, BattleScript_EffectSpicyExtract_End + printfromtable gStatDownStringIds + waitmessage B_WAIT_TIME_LONG +BattleScript_EffectSpicyExtract_End: + goto BattleScript_MoveEnd + BattleScript_EffectTidyUp:: attackcanceler attackstring @@ -3132,6 +3169,7 @@ BattleScript_EffectStatDown: BattleScript_StatDownFromAttackString: attackstring ppreduce +BattleScript_EffectStatDownFromStatBuffChange: statbuffchange STAT_CHANGE_ALLOW_PTR, BattleScript_StatDownEnd jumpifbyte CMP_LESS_THAN, cMULTISTRING_CHOOSER, B_MSG_STAT_WONT_DECREASE, BattleScript_StatDownDoAnim jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, B_MSG_STAT_FELL_EMPTY, BattleScript_StatDownEnd @@ -3142,6 +3180,7 @@ BattleScript_StatDownDoAnim:: waitanimation setgraphicalstatchangevalues playanimation BS_TARGET, B_ANIM_STATS_CHANGE, sB_ANIM_ARG1 + bicword gHitMarker, HITMARKER_DISABLE_ANIMATION BattleScript_StatDownPrintString:: printfromtable gStatDownStringIds waitmessage B_WAIT_TIME_LONG diff --git a/include/battle_ai_util.h b/include/battle_ai_util.h index bd2b8fb920..03fa1e5f31 100644 --- a/include/battle_ai_util.h +++ b/include/battle_ai_util.h @@ -193,5 +193,6 @@ s32 AI_TryToClearStats(u32 battlerAtk, u32 battlerDef, bool32 isDoubleBattle); bool32 AI_ShouldCopyStatChanges(u32 battlerAtk, u32 battlerDef); bool32 AI_ShouldSetUpHazards(u32 battlerAtk, u32 battlerDef, struct AiLogicData *aiData); void IncreaseTidyUpScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score); +bool32 AI_ShouldSpicyExtract(u32 battlerAtk, u32 battlerAtkPartner, u32 move, struct AiLogicData *aiData); #endif //GUARD_BATTLE_AI_UTIL_H diff --git a/include/battle_scripts.h b/include/battle_scripts.h index 2e27b5e935..39ff811af9 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -179,6 +179,7 @@ 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_ItemNoStatLossSpicyExtract[]; extern const u8 BattleScript_BRNPrevention[]; extern const u8 BattleScript_PRLZPrevention[]; extern const u8 BattleScript_PSNPrevention[]; @@ -836,5 +837,6 @@ extern const u8 BattleScript_EffectFilletAway[]; extern const u8 BattleScript_EffectShedTail[]; extern const u8 BattleScript_EffectUpperHand[]; extern const u8 BattleScript_EffectTidyUp[]; +extern const u8 BattleScript_EffectSpicyExtract[]; #endif // GUARD_BATTLE_SCRIPTS_H diff --git a/include/constants/battle_move_effects.h b/include/constants/battle_move_effects.h index a86471b5d9..04ddb6d881 100644 --- a/include/constants/battle_move_effects.h +++ b/include/constants/battle_move_effects.h @@ -350,6 +350,7 @@ enum { EFFECT_DRAGON_CHEER, EFFECT_LAST_RESPECTS, EFFECT_TIDY_UP, + EFFECT_SPICY_EXTRACT, EFFECT_TERA_BLAST, EFFECT_TERA_STARSTORM, NUM_BATTLE_MOVE_EFFECTS, diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index ffd99c7cbd..3489cdfcbf 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -439,7 +439,7 @@ static void SetBattlerAiMovesData(struct AiLogicData *aiData, u32 battlerAtk, u3 weather = AI_GetWeather(aiData); SetBattlerData(battlerAtk); - + // Simulate dmg for both ai controlled mons and for player controlled mons. for (battlerDef = 0; battlerDef < battlersCount; battlerDef++) { @@ -1367,9 +1367,6 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) break; case EFFECT_EVASION_DOWN: case EFFECT_EVASION_DOWN_2: - if (!ShouldLowerStat(battlerDef, aiData->abilities[battlerDef], STAT_EVASION)) - ADJUST_SCORE(-10); - break; case EFFECT_TICKLE: if (!ShouldLowerStat(battlerDef, aiData->abilities[battlerDef], STAT_ATK)) ADJUST_SCORE(-10); @@ -2639,6 +2636,14 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) && !BattlerStatCanRise(battlerAtk, aiData->abilities[battlerAtk], STAT_SPDEF)) ADJUST_SCORE(-10); break; + case EFFECT_SPICY_EXTRACT: + if (battlerAtk != BATTLE_PARTNER(battlerDef) + && (HasMoveWithCategory(battlerDef, DAMAGE_CATEGORY_PHYSICAL) + || aiData->abilities[battlerDef] == ABILITY_CLEAR_BODY + || aiData->abilities[battlerDef] == ABILITY_GOOD_AS_GOLD + || aiData->holdEffects[battlerDef] == HOLD_EFFECT_CLEAR_AMULET)) + ADJUST_SCORE(-10); + break; case EFFECT_UPPER_HAND: if (predictedMove == MOVE_NONE || IS_MOVE_STATUS(predictedMove) || AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_SLOWER || GetMovePriority(battlerDef, move) < 1 || GetMovePriority(battlerDef, move) > 3) // Opponent going first or not using priority move ADJUST_SCORE(-10); @@ -2769,6 +2774,7 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) || gMovesInfo[aiData->partnerMove].criticalHitStage > 0 || HasMoveWithCriticalHitChance(battlerAtkPartner)) ADJUST_SCORE(GOOD_EFFECT); + break; } // our effect relative to partner // consider global move effects @@ -2922,6 +2928,12 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) { switch (effect) { + case EFFECT_SPICY_EXTRACT: + if (AI_ShouldSpicyExtract(battlerAtk, battlerAtkPartner, move, aiData)) + { + RETURN_SCORE_PLUS(GOOD_EFFECT); + } + break; case EFFECT_PURIFY: if (gBattleMons[battlerAtkPartner].status1 & STATUS1_ANY) { @@ -3383,6 +3395,9 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move) if (gBattleMons[battlerDef].statStages[STAT_EVASION] < 7 || aiData->abilities[battlerAtk] == ABILITY_NO_GUARD) ADJUST_SCORE(-2); break; + case EFFECT_SPICY_EXTRACT: + // TODO: Make IncreaseStatDownScore function, just like IncreaseStatUpScore + break; case EFFECT_BIDE: if (aiData->hpPercents[battlerAtk] < 90) ADJUST_SCORE(-2); // Should be either removed or turned into increasing score @@ -3404,7 +3419,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move) score += AI_TryToClearStats(battlerAtk, battlerDef, isDoubleBattle); break; case EFFECT_ROAR: - if ((gMovesInfo[move].soundMove && aiData->abilities[battlerDef] == ABILITY_SOUNDPROOF) + if ((gMovesInfo[move].soundMove && aiData->abilities[battlerDef] == ABILITY_SOUNDPROOF) || aiData->abilities[battlerDef] == ABILITY_SUCTION_CUPS) break; else if (IsDynamaxed(battlerDef)) diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index a3b4553607..d752e1f6a2 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -3819,3 +3819,42 @@ void IncreaseTidyUpScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score) if (gStatuses3[battlerDef] & STATUS3_LEECHSEED) ADJUST_SCORE_PTR(-2); } + +bool32 AI_ShouldSpicyExtract(u32 battlerAtk, u32 battlerAtkPartner, u32 move, struct AiLogicData *aiData) +{ + u32 preventsStatLoss; + u32 partnerAbility; + u32 partnerHoldEffect = aiData->holdEffects[battlerAtkPartner]; + + if (DoesBattlerIgnoreAbilityChecks(aiData->abilities[battlerAtk], move)) + partnerAbility = ABILITY_NONE; + else + partnerAbility = aiData->abilities[battlerAtkPartner]; + + if (gBattleMons[battlerAtkPartner].statStages[STAT_ATK] == MAX_STAT_STAGE + || partnerAbility == ABILITY_CONTRARY + || partnerAbility == ABILITY_GOOD_AS_GOLD + || HasMoveEffect(BATTLE_OPPOSITE(battlerAtk), EFFECT_FOUL_PLAY) + || HasMoveEffect(BATTLE_OPPOSITE(battlerAtkPartner), EFFECT_FOUL_PLAY)) + return FALSE; + + preventsStatLoss = (partnerAbility == ABILITY_CLEAR_BODY + || partnerAbility == ABILITY_FULL_METAL_BODY + || partnerAbility == ABILITY_WHITE_SMOKE + || partnerHoldEffect == HOLD_EFFECT_CLEAR_AMULET); + + switch (gMovesInfo[aiData->partnerMove].effect) + { + case EFFECT_DEFENSE_UP: + case EFFECT_DEFENSE_UP_2: + case EFFECT_DEFENSE_UP_3: + case EFFECT_BULK_UP: + case EFFECT_STOCKPILE: + if (!preventsStatLoss) + return FALSE; + } + + return (preventsStatLoss + && AI_STRIKES_FIRST(battlerAtk, battlerAtkPartner, TRUE) + && HasMoveWithCategory(battlerAtkPartner, DAMAGE_CATEGORY_PHYSICAL)); +} diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 85e18efb93..f7a58fae02 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -11647,17 +11647,18 @@ static u32 ChangeStatBuffs(s8 statValue, u32 statId, u32 flags, const u8 *BS_ptr } else { - BattleScriptPush(BS_ptr); gBattleScripting.battler = battler; if (battlerHoldEffect == HOLD_EFFECT_CLEAR_AMULET) { gLastUsedItem = gBattleMons[battler].item; + BattleScriptPush(BS_ptr); gBattlescriptCurrInstr = BattleScript_ItemNoStatLoss; RecordItemEffectBattle(battler, HOLD_EFFECT_CLEAR_AMULET); } else { gBattlerAbility = battler; + BattleScriptPush(BS_ptr); gBattlescriptCurrInstr = BattleScript_AbilityNoStatLoss; gLastUsedAbility = battlerAbility; RecordAbilityBattle(battler, gLastUsedAbility); diff --git a/src/data/battle_move_effects.h b/src/data/battle_move_effects.h index 966d4d0dea..1f474206cc 100644 --- a/src/data/battle_move_effects.h +++ b/src/data/battle_move_effects.h @@ -2231,6 +2231,13 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] = .encourageEncore = TRUE, }, + [EFFECT_SPICY_EXTRACT] = + { + .battleScript = BattleScript_EffectSpicyExtract, + .battleTvScore = 0, // TODO: Assign points + .encourageEncore = TRUE, + }, + [EFFECT_TERA_BLAST] = { .battleScript = BattleScript_EffectPhotonGeyser, diff --git a/src/data/moves_info.h b/src/data/moves_info.h index 4e4f32fbac..36bd149ece 100644 --- a/src/data/moves_info.h +++ b/src/data/moves_info.h @@ -18577,7 +18577,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .description = COMPOUND_STRING( "Sharply ups target's Attack,\n" "harshly lowers its Defense."), - .effect = EFFECT_PLACEHOLDER, // EFFECT_SPICY_EXTRACT + .effect = EFFECT_SPICY_EXTRACT, .power = 0, .type = TYPE_GRASS, .accuracy = 0, diff --git a/src/data/pokemon/species_info/gen_4_families.h b/src/data/pokemon/species_info/gen_4_families.h index b8f54203f2..a2e4bd3ddd 100644 --- a/src/data/pokemon/species_info/gen_4_families.h +++ b/src/data/pokemon/species_info/gen_4_families.h @@ -5689,4 +5689,4 @@ const struct SpeciesInfo gSpeciesInfoGen4[] = #ifdef __INTELLISENSE__ }; -#endif \ No newline at end of file +#endif diff --git a/test/battle/move_effect/spicy_extract.c b/test/battle/move_effect/spicy_extract.c new file mode 100644 index 0000000000..3642bf936c --- /dev/null +++ b/test/battle/move_effect/spicy_extract.c @@ -0,0 +1,209 @@ +#include "global.h" +#include "test/battle.h" + +ASSUMPTIONS +{ + ASSUME(gMovesInfo[MOVE_SPICY_EXTRACT].effect == EFFECT_SPICY_EXTRACT); +} + +SINGLE_BATTLE_TEST("Spicy Extract raises target's Attack by 2 stages and lowers target's Defense by 2 stages") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_SPICY_EXTRACT); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SPICY_EXTRACT, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); + MESSAGE("Foe Wobbuffet's Attack sharply rose!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); + MESSAGE("Foe Wobbuffet's Defense harshly fell!"); + } THEN { + EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 2); + EXPECT_EQ(opponent->statStages[STAT_DEF], DEFAULT_STAT_STAGE - 2); + } +} + +SINGLE_BATTLE_TEST("Spicy Extract is prevented by target's ability if it's Attack stat is maxed out") +{ + u16 ability; + + PARAMETRIZE { ability = ABILITY_CLEAR_BODY; } + PARAMETRIZE { ability = ABILITY_LIGHT_METAL; } + + GIVEN { + ASSUME(gMovesInfo[MOVE_SWORDS_DANCE].effect == EFFECT_ATTACK_UP_2); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_BELDUM) { Ability(ability); } + } WHEN { + TURN { MOVE(opponent, MOVE_SWORDS_DANCE); } + TURN { MOVE(opponent, MOVE_SWORDS_DANCE); } + TURN { MOVE(opponent, MOVE_SWORDS_DANCE); MOVE(player, MOVE_SPICY_EXTRACT); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SWORDS_DANCE, opponent); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SWORDS_DANCE, opponent); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SWORDS_DANCE, opponent); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); + MESSAGE("Wobbuffet used Spicy Extract!"); + if (ability == ABILITY_CLEAR_BODY) { + ABILITY_POPUP(opponent, ABILITY_CLEAR_BODY); + MESSAGE("Foe Beldum's Clear Body prevents stat loss!"); + NONE_OF { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SPICY_EXTRACT, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); + } + } else { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SPICY_EXTRACT, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); + } + } +} + +SINGLE_BATTLE_TEST("Spicy Extract Defense loss is prevented by Big Pecks") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_PIDGEY) { Ability(ABILITY_BIG_PECKS); } + } WHEN { + TURN { MOVE(player, MOVE_SPICY_EXTRACT); } + } SCENE { + MESSAGE("Wobbuffet used Spicy Extract!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SPICY_EXTRACT, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); + MESSAGE("Foe Pidgey's Attack sharply rose!"); + NONE_OF { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); + MESSAGE("Foe Wobbuffet's Defense harshly fell!"); + } + ABILITY_POPUP(opponent, ABILITY_BIG_PECKS); + MESSAGE("Foe Pidgey's Big Pecks prevents Defense loss!"); + } +} + +SINGLE_BATTLE_TEST("Spicy Extract bypasses accuracy checks") +{ + PASSES_RANDOMLY(100, 100, RNG_ACCURACY); + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_BRIGHTPOWDER); } + } WHEN { + TURN { MOVE(player, MOVE_SPICY_EXTRACT); } + } SCENE { + MESSAGE("Wobbuffet used Spicy Extract!"); + NOT MESSAGE("Wobbuffet's attack missed!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SPICY_EXTRACT, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); + MESSAGE("Foe Wobbuffet's Attack sharply rose!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); + MESSAGE("Foe Wobbuffet's Defense harshly fell!"); + } +} + +SINGLE_BATTLE_TEST("Spicy Extract will fail if target is in a semi-invulnerability state") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_BRIGHTPOWDER); } + } WHEN { + TURN { MOVE(opponent, MOVE_DIVE); MOVE(player, MOVE_SPICY_EXTRACT); } + } SCENE { + MESSAGE("Foe Wobbuffet used Dive!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_DIVE, opponent); + MESSAGE("Wobbuffet used Spicy Extract!"); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_SPICY_EXTRACT, player); + MESSAGE("Wobbuffet's attack missed!"); + } +} + +SINGLE_BATTLE_TEST("Spicy Extract stat changes will be inverted by Contrary") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_SNIVY) { Ability(ABILITY_CONTRARY); } + } WHEN { + TURN { MOVE(player, MOVE_SPICY_EXTRACT); } + } SCENE { + MESSAGE("Wobbuffet used Spicy Extract!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SPICY_EXTRACT, player); + + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); + MESSAGE("Foe Snivy's Attack harshly fell!"); + + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); + MESSAGE("Foe Snivy's Defense sharply rose!"); + } THEN { + EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE - 2); + EXPECT_EQ(opponent->statStages[STAT_DEF], DEFAULT_STAT_STAGE + 2); + } +} + +SINGLE_BATTLE_TEST("Spicy Extract against Clear Amulet and Contrary raises Defense only") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_SNIVY) { Ability(ABILITY_CONTRARY); Item(ITEM_CLEAR_AMULET); } + } WHEN { + TURN { MOVE(player, MOVE_SPICY_EXTRACT); } + } SCENE { + MESSAGE("Wobbuffet used Spicy Extract!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SPICY_EXTRACT, player); + NONE_OF { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); + MESSAGE("Foe Snivy's Attack harshly fell!"); + } + MESSAGE("Foe Snivy's Clear Amulet prevents its stats from being lowered!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); + MESSAGE("Foe Snivy's Defense sharply rose!"); + } THEN { + EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE); + EXPECT_EQ(opponent->statStages[STAT_DEF], DEFAULT_STAT_STAGE + 2); + } +} + +AI_DOUBLE_BATTLE_TEST("Spicy Extract user will use it if partner holds Clear Amulet and a physical move") +{ + u32 move; + + PARAMETRIZE { move = MOVE_TACKLE; } + PARAMETRIZE { move = MOVE_SWIFT;} + + AI_LOG; + GIVEN { + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT); + PLAYER(SPECIES_WOBBUFFET) { Speed(10); } + PLAYER(SPECIES_WOBBUFFET) { Speed(10); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(20); Item(ITEM_CLEAR_AMULET); Moves(move); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(40); Moves(MOVE_TACKLE, MOVE_SPICY_EXTRACT); } + } WHEN { + TURN { + if (move == MOVE_TACKLE) + EXPECT_MOVE(opponentRight, MOVE_SPICY_EXTRACT); + else + EXPECT_MOVE(opponentRight, MOVE_TACKLE); + } + } +} + +AI_DOUBLE_BATTLE_TEST("Spicy Extract user will not choose the move if it does not benefit partner") +{ + u32 species; + u32 ability; + + PARAMETRIZE { species = SPECIES_GHOLDENGO; ability = ABILITY_GOOD_AS_GOLD; } + PARAMETRIZE { species = SPECIES_SNIVY; ability = ABILITY_CONTRARY; } + + GIVEN { + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT); + PLAYER(SPECIES_WOBBUFFET) { Speed(10); } + PLAYER(SPECIES_WOBBUFFET) { Speed(10); } + OPPONENT(species) { Speed(20); Ability(ability); Moves(MOVE_TACKLE); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(40); Moves(MOVE_TACKLE, MOVE_SPICY_EXTRACT); } + } WHEN { + TURN { + EXPECT_MOVE(opponentRight, MOVE_TACKLE); + } + } +}