Adds move Spicy Extract (#4211)

* Adds move Spicy Extract

* remove uneeded line

* anim, new tests, acc change

* Clear Amulet, Contrary interaction

* ai

* correction

* simplify script a bit

* clean up

* Spicy Extract script overhaul

* merge rhh/upcoming

* alignment

* AI changes/additions

* add Foul Play check

* Remove useless ai checks

* remove wrong test

* review issues
This commit is contained in:
Alex 2024-05-16 11:03:50 +02:00 committed by GitHub
parent 7d84cd62ef
commit faf61e62fb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 367 additions and 9 deletions

View file

@ -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::

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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,

View file

@ -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))

View file

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

View file

@ -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);

View file

@ -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,

View file

@ -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,

View file

@ -5689,4 +5689,4 @@ const struct SpeciesInfo gSpeciesInfoGen4[] =
#ifdef __INTELLISENSE__
};
#endif
#endif

View file

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