27/04/25 Master to upcoming merge (#6701)

This commit is contained in:
hedara90 2025-04-27 18:01:50 +02:00 committed by GitHub
commit 5dfbdf56da
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
26 changed files with 871 additions and 89 deletions

View File

@ -1366,6 +1366,10 @@
callnative BS_RestoreAttacker
.endm
.macro jumpifintimidateabilityprevented
callnative BS_JumpIfIntimidateAbilityPrevented
.endm
.macro metalburstdamagecalculator failInstr:req
callnative BS_CalcMetalBurstDmg
.4byte \failInstr
@ -1382,6 +1386,10 @@
.byte \battler
.endm
.macro tryintimidatejectpack
callnative BS_TryIntimidatEjectpack
.endm
.macro allyswitchswapbattlers
callnative BS_AllySwitchSwapBattler
.endm

View File

@ -3197,7 +3197,7 @@ BattleScript_StatDownEnd::
BattleScript_MirrorArmorReflect::
pause B_WAIT_TIME_SHORT
call BattleScript_AbilityPopUp
jumpifsubstituteblocks BattleScript_AbilityNoSpecificStatLoss
jumpifstatus2 BS_ATTACKER, STATUS2_SUBSTITUTE, BattleScript_MirrorArmorDoesntAffect
BattleScript_MirrorArmorReflectStatLoss:
statbuffchange MOVE_EFFECT_AFFECTS_USER | STAT_CHANGE_MIRROR_ARMOR | STAT_CHANGE_NOT_PROTECT_AFFECTED | STAT_CHANGE_ALLOW_PTR, BattleScript_MirrorArmorReflectEnd
jumpifbyte CMP_LESS_THAN, cMULTISTRING_CHOOSER, B_MSG_STAT_WONT_DECREASE, BattleScript_MirrorArmorReflectAnim
@ -3211,6 +3211,13 @@ BattleScript_MirrorArmorReflectPrintString:
BattleScript_MirrorArmorReflectEnd:
return
BattleScript_MirrorArmorDoesntAffect:
swapattackerwithtarget
printstring STRINGID_ITDOESNTAFFECT
waitmessage B_WAIT_TIME_LONG
swapattackerwithtarget
return
BattleScript_MirrorArmorReflectWontFall:
copybyte gBattlerTarget, gBattlerAttacker @ STRINGID_STATSWONTDECREASE uses target
goto BattleScript_MirrorArmorReflectPrintString
@ -7719,13 +7726,7 @@ BattleScript_IntimidateLoop:
jumpiftargetally BattleScript_IntimidateLoopIncrement
jumpifabsent BS_TARGET, BattleScript_IntimidateLoopIncrement
jumpifstatus2 BS_TARGET, STATUS2_SUBSTITUTE, BattleScript_IntimidateLoopIncrement
.if B_UPDATED_INTIMIDATE >= GEN_8 @These abilties specifically prevent just intimidate, without blocking stat decreases
jumpifability BS_TARGET, ABILITY_INNER_FOCUS, BattleScript_IntimidatePrevented
jumpifability BS_TARGET, ABILITY_SCRAPPY, BattleScript_IntimidatePrevented
jumpifability BS_TARGET, ABILITY_OWN_TEMPO, BattleScript_IntimidatePrevented
jumpifability BS_TARGET, ABILITY_OBLIVIOUS, BattleScript_IntimidatePrevented
.endif
jumpifability BS_TARGET, ABILITY_GUARD_DOG, BattleScript_IntimidateInReverse
jumpifintimidateabilityprevented
BattleScript_IntimidateEffect:
copybyte sBATTLER, gBattlerAttacker
setstatchanger STAT_ATK, 1, TRUE
@ -7750,9 +7751,10 @@ BattleScript_IntimidateLoopIncrement:
destroyabilitypopup
restoretarget
pause B_WAIT_TIME_MED
tryintimidatejectpack
end3
BattleScript_IntimidatePrevented:
BattleScript_IntimidatePrevented::
copybyte sBATTLER, gBattlerTarget
call BattleScript_AbilityPopUp
printstring STRINGID_PKMNPREVENTSSTATLOSSWITH
@ -7772,7 +7774,7 @@ BattleScript_IntimidateContrary_WontIncrease:
printstring STRINGID_TARGETSTATWONTGOHIGHER
goto BattleScript_IntimidateEffect_WaitString
BattleScript_IntimidateInReverse:
BattleScript_IntimidateInReverse::
copybyte sBATTLER, gBattlerTarget
call BattleScript_AbilityPopUpTarget
pause B_WAIT_TIME_SHORT
@ -9527,7 +9529,7 @@ BattleScript_EjectButtonActivates::
printstring STRINGID_EJECTBUTTONACTIVATE
waitmessage B_WAIT_TIME_LONG
removeitem BS_SCRIPTING
undodynamax BS_SCRIPTING
undodynamax BS_SCRIPTING
makeinvisible BS_SCRIPTING
openpartyscreen BS_SCRIPTING, BattleScript_EjectButtonEnd
copybyte sSAVED_BATTLER, sBATTLER

View File

@ -174,7 +174,8 @@ struct ProtectStruct
u16 eatMirrorHerb:1;
u16 activateOpportunist:2; // 2 - to copy stats. 1 - stats copied (do not repeat). 0 - no stats to copy
u16 usedAllySwitch:1;
u16 padding:5;
u16 lashOutAffected:1;
u16 padding:4;
// End of 16-bit bitfield
u16 physicalDmg;
u16 specialDmg;
@ -1272,4 +1273,3 @@ static inline bool32 IsBattlerInvalidForSpreadMove(u32 battlerAtk, u32 battlerDe
}
#endif // GUARD_BATTLE_H

View File

@ -171,6 +171,8 @@ extern const u8 BattleScript_RainDishActivates[];
extern const u8 BattleScript_SandstreamActivates[];
extern const u8 BattleScript_ShedSkinActivates[];
extern const u8 BattleScript_IntimidateActivates[];
extern const u8 BattleScript_IntimidatePrevented[];
extern const u8 BattleScript_IntimidateInReverse[];
extern const u8 BattleScript_DroughtActivates[];
extern const u8 BattleScript_TookAttack[];
extern const u8 BattleScript_SturdyPreventsOHKO[];

View File

@ -368,6 +368,7 @@ void ClearPursuitValuesIfSet(u32 battler);
void ClearPursuitValues(void);
bool32 HasWeatherEffect(void);
u32 RestoreWhiteHerbStats(u32 battler);
bool32 IsFutureSightAttackerInParty(u32 battlerAtk, u32 battlerDef);
bool32 HadMoreThanHalfHpNowDoesnt(u32 battler);
#endif // GUARD_BATTLE_UTIL_H

View File

@ -335,8 +335,6 @@ static bool32 HandleEndTurnFutureSight(u32 battler)
if (gWishFutureKnock.futureSightCounter[battler] == gBattleTurnCounter)
{
struct Pokemon *party;
if (gWishFutureKnock.futureSightCounter[battler] == gBattleTurnCounter
&& gWishFutureKnock.futureSightCounter[BATTLE_PARTNER(battler)] <= gBattleTurnCounter)
{
@ -357,8 +355,7 @@ static bool32 HandleEndTurnFutureSight(u32 battler)
gBattlerAttacker = gWishFutureKnock.futureSightBattlerIndex[battler];
gCurrentMove = gWishFutureKnock.futureSightMove[battler];
party = GetSideParty(GetBattlerSide(gBattlerAttacker));
if (&party[gWishFutureKnock.futureSightPartyIndex[gBattlerTarget]] == &party[gBattlerPartyIndexes[gBattlerAttacker]])
if (!IsFutureSightAttackerInParty(gBattlerAttacker, gBattlerTarget))
SetTypeBeforeUsingMove(gCurrentMove, gBattlerAttacker);
BattleScriptExecute(BattleScript_MonTookFutureAttack);

View File

@ -3746,6 +3746,7 @@ static void TryDoEventsBeforeFirstTurn(void)
{
for (i = 0; i < gBattlersCount; i++)
{
gBattleStruct->monToSwitchIntoId[i] = PARTY_SIZE; // Included here because switches can happen before during set ups (eg. eject pack)
struct Pokemon *party = GetBattlerParty(i);
struct Pokemon *mon = &party[gBattlerPartyIndexes[i]];
if (!IsBattlerAlive(i) || gBattleMons[i].species == SPECIES_NONE || GetMonData(mon, MON_DATA_IS_EGG))
@ -4758,6 +4759,8 @@ u32 GetBattlerTotalSpeedStatArgs(u32 battler, u32 ability, enum ItemHoldEffect h
speed = (GetHighestStatId(battler) == STAT_SPEED) ? (speed * 150) / 100 : speed;
else if (ability == ABILITY_QUARK_DRIVE && !(gBattleMons[battler].status2 & STATUS2_TRANSFORMED) && (gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN || gDisableStructs[battler].boosterEnergyActivates))
speed = (GetHighestStatId(battler) == STAT_SPEED) ? (speed * 150) / 100 : speed;
else if (ability == ABILITY_UNBURDEN && gDisableStructs[battler].unburdenActive)
speed *= 2;
// stat stages
speed *= gStatStageRatios[gBattleMons[battler].statStages[STAT_SPEED]][0];
@ -4784,8 +4787,6 @@ u32 GetBattlerTotalSpeedStatArgs(u32 battler, u32 ability, enum ItemHoldEffect h
// various effects
if (gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_TAILWIND)
speed *= 2;
if (gDisableStructs[battler].unburdenActive)
speed *= 2;
// paralysis drop
if (gBattleMons[battler].status1 & STATUS1_PARALYSIS && ability != ABILITY_QUICK_FEET)
@ -4854,7 +4855,6 @@ s32 GetBattleMovePriority(u32 battler, u32 ability, u32 move)
return priority;
}
// Function for AI with variables provided as arguments to speed the computation time
s32 GetWhichBattlerFasterArgs(u32 battler1, u32 battler2, bool32 ignoreChosenMoves, u32 ability1, u32 ability2,
enum ItemHoldEffect holdEffectBattler1, enum ItemHoldEffect holdEffectBattler2, u32 speedBattler1, u32 speedBattler2, s32 priority1, s32 priority2)
{
@ -5140,6 +5140,7 @@ static void TurnValuesCleanUp(bool8 var0)
gSpecialStatuses[i].parentalBondState = PARENTAL_BOND_OFF;
gBattleStruct->battlerState[i].usedEjectItem = FALSE;
gProtectStructs[i].lashOutAffected = FALSE;
}
gSideTimers[B_SIDE_PLAYER].followmeTimer = 0;

View File

@ -4665,7 +4665,6 @@ void SetMoveEffect(bool32 primary, bool32 certain)
static bool32 CanApplyAdditionalEffect(const struct AdditionalEffect *additionalEffect)
{
// Self-targeting move effects only apply after the last mon has been hit
if (additionalEffect->self
&& NumAffectedSpreadMoveTargets() > 1
&& GetNextTarget(GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove), TRUE) != MAX_BATTLERS_COUNT)
@ -6374,7 +6373,6 @@ static bool32 HandleMoveEndAbilityBlock(u32 battlerAtk, u32 battlerDef, u32 move
gEffectBattler = battlerDef;
BattleScriptPushCursor();
gBattlescriptCurrInstr = BattleScript_MagicianActivates;
gSpecialStatuses[battlerAtk].preventLifeOrbDamage = TRUE;
effect = TRUE;
}
break;
@ -7356,6 +7354,9 @@ static void Cmd_moveend(void)
if (numEjectPackBattlers > 1)
SortBattlersBySpeed(battlers, FALSE);
for (i = 0; i < gBattlersCount; i++)
gProtectStructs[i].statFell = FALSE; // restore for every possible eject pack battler
for (i = 0; i < gBattlersCount; i++)
{
u32 battler = battlers[i];
@ -7370,7 +7371,6 @@ static void Cmd_moveend(void)
BattleScriptPushCursor();
gBattlescriptCurrInstr = BattleScript_EjectPackActivates;
AI_DATA->ejectPackSwitch = TRUE;
gProtectStructs[battler].statFell = FALSE;
break; // Only the fastest Eject item activates
}
}
@ -7617,7 +7617,6 @@ static void Cmd_moveend(void)
gProtectStructs[gBattlerAttacker].shellTrap = FALSE;
gBattleStruct->ateBoost[gBattlerAttacker] = FALSE;
gSpecialStatuses[gBattlerAttacker].gemBoost = FALSE;
gSpecialStatuses[gBattlerAttacker].preventLifeOrbDamage = 0;
gSpecialStatuses[gBattlerTarget].berryReduced = FALSE;
gSpecialStatuses[gBattlerTarget].distortedTypeMatchups = FALSE;
gBattleScripting.moveEffect = 0;
@ -8392,6 +8391,7 @@ static bool32 DoSwitchInEffectsForBattler(u32 battler)
{
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SWITCHIN_NEUTRALIZING_GAS;
gSpecialStatuses[battler].announceNeutralizingGas = TRUE;
gDisableStructs[battler].neutralizingGas = TRUE;
gBattlerAbility = battler;
BattleScriptPushCursor();
gBattlescriptCurrInstr = BattleScript_SwitchInAbilityMsgRet;
@ -8456,13 +8456,7 @@ static bool32 DoSwitchInEffectsForBattler(u32 battler)
else if (IsBattlerAffectedByHazards(battler, TRUE))
{
i = GetBattlerAbility(battler);
if (!(gBattleMons[battler].status1 & STATUS1_ANY)
&& !IS_BATTLER_OF_TYPE(battler, TYPE_STEEL)
&& i != ABILITY_IMMUNITY
&& i != ABILITY_PURIFYING_SALT
&& !IsAbilityOnSide(battler, ABILITY_PASTEL_VEIL)
&& !(gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_SAFEGUARD)
&& !(gFieldStatuses & STATUS_FIELD_MISTY_TERRAIN))
if (CanBePoisoned(gBattlerAttacker, battler, i))
{
if (gSideTimers[GetBattlerSide(battler)].toxicSpikesAmount >= 2)
gBattleMons[battler].status1 |= STATUS1_TOXIC_POISON;
@ -12664,7 +12658,8 @@ static u32 ChangeStatBuffs(s8 statValue, u32 statId, u32 flags, const u8 *BS_ptr
}
else
{
gProtectStructs[battler].statFell = TRUE; // Eject pack, lash out
gProtectStructs[battler].statFell = TRUE;
gProtectStructs[battler].lashOutAffected = TRUE;
gBattleCommunication[MULTISTRING_CHOOSER] = (gBattlerTarget == battler); // B_MSG_ATTACKER_STAT_FELL or B_MSG_DEFENDER_STAT_FELL
}
}
@ -18853,6 +18848,90 @@ void BS_SetSteelsurge(void)
}
}
void BS_JumpIfIntimidateAbilityPrevented(void)
{
NATIVE_ARGS();
u32 hasAbility = FALSE;
u32 ability = GetBattlerAbility(gBattlerTarget);
switch (ability)
{
case ABILITY_INNER_FOCUS:
case ABILITY_SCRAPPY:
case ABILITY_OWN_TEMPO:
case ABILITY_OBLIVIOUS:
if (B_UPDATED_INTIMIDATE >= GEN_8)
{
hasAbility = TRUE;
gBattlescriptCurrInstr = BattleScript_IntimidatePrevented;
}
else
{
gBattlescriptCurrInstr = cmd->nextInstr;
}
break;
case ABILITY_GUARD_DOG:
hasAbility = TRUE;
gBattlescriptCurrInstr = BattleScript_IntimidateInReverse;
break;
default:
gBattlescriptCurrInstr = cmd->nextInstr;
break;
}
if (hasAbility)
{
gLastUsedAbility = ability;
gBattlerAbility = gBattlerTarget;
RecordAbilityBattle(gBattlerTarget, gLastUsedAbility);
}
}
void BS_TryIntimidatEjectpack(void)
{
NATIVE_ARGS();
u32 affectedBattler = 0xFF;
u32 battler = BATTLE_OPPOSITE(gBattlerAttacker);
u32 partnerBattler = BATTLE_PARTNER(battler);
bool32 ejectPackBattler = CanEjectPackTrigger(gBattlerAttacker, battler, MOVE_NONE);
bool32 ejectPackPartnerBattler = CanEjectPackTrigger(gBattlerAttacker, partnerBattler, MOVE_NONE);
if (ejectPackBattler && ejectPackPartnerBattler)
{
u32 battlerSpeed = GetBattlerTotalSpeedStat(battler);
u32 partnerbattlerSpeed = GetBattlerTotalSpeedStat(partnerBattler);
if (battlerSpeed >= partnerbattlerSpeed)
affectedBattler = battler;
else
affectedBattler = partnerBattler;
}
else if (ejectPackBattler)
{
affectedBattler = battler;
}
else if (ejectPackPartnerBattler)
{
affectedBattler = partnerBattler;
}
gBattlescriptCurrInstr = cmd->nextInstr;
if (affectedBattler != 0xFF)
{
gProtectStructs[battler].statFell = FALSE;
gProtectStructs[partnerBattler].statFell = FALSE;
AI_DATA->ejectPackSwitch = TRUE;
gBattleScripting.battler = affectedBattler;
gLastUsedItem = gBattleMons[affectedBattler].item;
RecordItemEffectBattle(affectedBattler, HOLD_EFFECT_EJECT_PACK);
BattleScriptPushCursor();
gBattlescriptCurrInstr = BattleScript_EjectPackActivate_Ret;
}
}
void BS_JumpIfCanGigantamax(void)
{
NATIVE_ARGS(u8 battler, const u8 *jumpInstr);

View File

@ -4599,12 +4599,9 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
&& IsBattlerAlive(gBattlerAttacker))
{
// Prevent Innards Out effect if Future Sight user is currently not on field
if (GetMoveEffect(gCurrentMove) == EFFECT_FUTURE_SIGHT)
{
if (gWishFutureKnock.futureSightPartyIndex[gBattlerTarget] != gBattlerPartyIndexes[gBattlerAttacker]
&& gWishFutureKnock.futureSightPartyIndex[gBattlerTarget] != BATTLE_PARTNER(gBattlerPartyIndexes[gBattlerAttacker]))
break;
}
if (GetMoveEffect(gCurrentMove) == EFFECT_FUTURE_SIGHT
&& IsFutureSightAttackerInParty(gBattlerAttacker, gBattlerTarget))
break;
gBattleScripting.battler = gBattlerTarget;
gBattleStruct->moveDamage[gBattlerAttacker] = gBattleStruct->moveDamage[gBattlerTarget];
@ -5961,6 +5958,9 @@ static u32 ItemHealHp(u32 battler, u32 itemId, enum ItemCaseId caseID, bool32 pe
static bool32 UnnerveOn(u32 battler, u32 itemId)
{
if (gBattleScripting.overrideBerryRequirements > 0) // Berries that aren't eaten naturally ignore unnerve
return FALSE;
if (ItemId_GetPocket(itemId) == POCKET_BERRIES && IsUnnerveAbilityOnOpposingSide(battler))
return TRUE;
return FALSE;
@ -6934,7 +6934,7 @@ u32 ItemBattleEffects(enum ItemCaseId caseID, u32 battler, bool32 moveTurn)
&& (IsBattlerTurnDamaged(gBattlerTarget) || gBattleStruct->moveDamage[gBattlerTarget]) // Needs the second check in case of Substitute
&& GetBattlerAbility(gBattlerAttacker) != ABILITY_MAGIC_GUARD
&& !gProtectStructs[gBattlerAttacker].confusionSelfDmg
&& !gSpecialStatuses[gBattlerAttacker].preventLifeOrbDamage)
&& !IsFutureSightAttackerInParty(gBattlerAttacker, gBattlerTarget))
{
gBattleStruct->moveDamage[gBattlerAttacker] = GetNonDynamaxMaxHP(gBattlerAttacker) / 10;
if (gBattleStruct->moveDamage[gBattlerAttacker] == 0)
@ -7190,9 +7190,6 @@ u32 ItemBattleEffects(enum ItemCaseId caseID, u32 battler, bool32 moveTurn)
gBattlescriptCurrInstr = BattleScript_WhiteHerbRet;
}
break;
case HOLD_EFFECT_EJECT_PACK:
effect = TryEjectPack(battler, ITEMEFFECT_ON_SWITCH_IN);
break;
default:
break;
}
@ -8007,7 +8004,7 @@ static inline u32 CalcMoveBasePower(struct DamageCalculationData *damageCalcData
basePower *= 2;
break;
case EFFECT_LASH_OUT:
if (gProtectStructs[battlerAtk].statFell)
if (gProtectStructs[battlerAtk].lashOutAffected)
basePower *= 2;
break;
case EFFECT_EXPLOSION:
@ -9361,8 +9358,6 @@ static inline s32 DoFutureSightAttackDamageCalcVars(struct DamageCalculationData
if (dmg == 0)
dmg = 1;
gSpecialStatuses[battlerAtk].preventLifeOrbDamage = TRUE;
return dmg;
}
@ -9390,14 +9385,11 @@ static u32 GetWeather(void)
return gBattleWeather;
}
static inline bool32 IsFutureSightAttackerInParty(struct DamageCalculationData *damageCalcData)
bool32 IsFutureSightAttackerInParty(u32 battlerAtk, u32 battlerDef)
{
if (GetMoveEffect(damageCalcData->move) != EFFECT_FUTURE_SIGHT)
return FALSE;
struct Pokemon *party = GetSideParty(GetBattlerSide(damageCalcData->battlerAtk));
return &party[gWishFutureKnock.futureSightPartyIndex[damageCalcData->battlerDef]]
!= &party[gBattlerPartyIndexes[damageCalcData->battlerAtk]];
struct Pokemon *party = GetSideParty(GetBattlerSide(battlerAtk));
return &party[gWishFutureKnock.futureSightPartyIndex[battlerDef]] != &party[gBattlerPartyIndexes[battlerAtk]]
&& &party[gWishFutureKnock.futureSightPartyIndex[battlerDef]] != &party[BATTLE_PARTNER(gBattlerPartyIndexes[battlerAtk])];
}
s32 CalculateMoveDamage(struct DamageCalculationData *damageCalcData, u32 fixedBasePower)
@ -9409,7 +9401,8 @@ s32 CalculateMoveDamage(struct DamageCalculationData *damageCalcData, u32 fixedB
GetBattlerAbility(damageCalcData->battlerDef),
damageCalcData->updateFlags);
if (IsFutureSightAttackerInParty(damageCalcData))
if (GetMoveEffect(damageCalcData->move) == EFFECT_FUTURE_SIGHT
&& IsFutureSightAttackerInParty(damageCalcData->battlerAtk, damageCalcData->battlerDef))
return DoFutureSightAttackDamageCalc(damageCalcData, typeEffectivenessMultiplier, GetWeather());
return DoMoveDamageCalc(damageCalcData, fixedBasePower, typeEffectivenessMultiplier, GetWeather());

View File

@ -1850,13 +1850,10 @@ u8 CreateObjectGraphicsSpriteWithTag(u16 graphicsId, void (*callback)(struct Spr
const struct ObjectEventGraphicsInfo *graphicsInfo = GetObjectEventGraphicsInfo(graphicsId);
struct Sprite *sprite;
u8 spriteId;
bool32 isShiny = graphicsId & OBJ_EVENT_MON_SHINY;
spriteTemplate = Alloc(sizeof(struct SpriteTemplate));
CopyObjectGraphicsInfoToSpriteTemplate(graphicsId, callback, spriteTemplate, &subspriteTables);
if (isShiny)
graphicsId -= SPECIES_SHINY_TAG;
if (OW_GFX_COMPRESS)
{

View File

@ -1,7 +1,25 @@
#include "global.h"
#include "test/battle.h"
TO_DO_BATTLE_TEST("Analytic increases the power of moves by 30% if it's the last one that uses its move");
SINGLE_BATTLE_TEST("Analytic increases the power of moves by 30% if it's the last one that uses its move", s16 damage)
{
u32 speed;
PARAMETRIZE { speed = 3; }
PARAMETRIZE { speed = 1; }
GIVEN {
PLAYER(SPECIES_MAGNEMITE) { Ability(ABILITY_ANALYTIC); Speed(speed); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(2); }
} WHEN {
TURN { MOVE(player, MOVE_TACKLE); }
} SCENE {
HP_BAR(opponent, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.3), results[1].damage);
}
}
TO_DO_BATTLE_TEST("Analytic takes into account modifications to speeed an priority (Gen 5-8)"); //Eg. Paralysis, Power Weight, Stall
TO_DO_BATTLE_TEST("Analytic does not take into account modifications to speeed an priority (Gen 8)"); //Eg. Paralysis, Power Weight, Stall
TO_DO_BATTLE_TEST("Analytic takes into account the turn order of what fainted Pokémon would've moved");

View File

@ -55,3 +55,35 @@ SINGLE_BATTLE_TEST("Comatose may be suppressed if pokemon transformed into a pok
else if (move == MOVE_SLEEP_POWDER) { STATUS_ICON(opponent, sleep: TRUE); }
}
}
SINGLE_BATTLE_TEST("Comatose pokemon doesn't get poisoned by Toxic Spikes on switch-in")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_KOMALA) { Ability(ABILITY_COMATOSE); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_TOXIC_SPIKES); }
TURN { SWITCH(player, 1); }
} SCENE {
NOT STATUS_ICON(player, STATUS1_POISON);
ABILITY_POPUP(player, ABILITY_COMATOSE);
NOT HP_BAR(player);
}
}
SINGLE_BATTLE_TEST("Comatose pokemon don't get poisoned by Toxic Spikes on switch-in if forced in by phazing with Mold Breaker")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_KOMALA) { Ability(ABILITY_COMATOSE); }
OPPONENT(SPECIES_PINSIR) { Ability(ABILITY_MOLD_BREAKER); }
} WHEN {
TURN { MOVE(opponent, MOVE_TOXIC_SPIKES); }
TURN { MOVE(opponent, MOVE_DRAGON_TAIL); }
} SCENE {
NOT STATUS_ICON(player, STATUS1_POISON);
ABILITY_POPUP(player, ABILITY_COMATOSE);
NOT HP_BAR(player);
}
}

View File

@ -45,3 +45,20 @@ SINGLE_BATTLE_TEST("Immunity prevents Toxic Spikes poison")
NOT STATUS_ICON(opponent, poison: TRUE);
}
}
SINGLE_BATTLE_TEST("Immunity doesn't prevent pokemon from being poisoned by Toxic Spikes on switch-in if forced in by phazing with Mold Breaker, but it cures it immediately")
{
GIVEN {
ASSUME(GetMoveEffect(MOVE_DRAGON_TAIL) == EFFECT_HIT_SWITCH_TARGET);
ASSUME(GetMoveEffect(MOVE_TOXIC_SPIKES) == EFFECT_TOXIC_SPIKES);
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_SNORLAX) { Ability(ABILITY_IMMUNITY); }
OPPONENT(SPECIES_PINSIR) { Ability(ABILITY_MOLD_BREAKER); }
} WHEN {
TURN { MOVE(opponent, MOVE_TOXIC_SPIKES); }
TURN { MOVE(opponent, MOVE_DRAGON_TAIL); }
} SCENE {
STATUS_ICON(player, STATUS1_POISON);
NOT HP_BAR(player);
}
}

View File

@ -104,7 +104,7 @@ DOUBLE_BATTLE_TEST("Intimidate doesn't activate on an empty field in a double ba
}
}
SINGLE_BATTLE_TEST("Intimidate and Eject Button force the opponent to Attack")
SINGLE_BATTLE_TEST("Intimidate and Eject Button don't force the opponent to Attack")
{
GIVEN {
ASSUME(gItemsInfo[ITEM_EJECT_BUTTON].holdEffect == HOLD_EFFECT_EJECT_BUTTON);
@ -365,6 +365,7 @@ DOUBLE_BATTLE_TEST("Intimidate will correctly decrease the attack of the second
} SCENE {
ABILITY_POPUP(opponentLeft, ABILITY_INTIMIDATE);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerLeft);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerLeft);
ABILITY_POPUP(playerLeft, ABILITY_PROTOSYNTHESIS);
@ -372,7 +373,6 @@ DOUBLE_BATTLE_TEST("Intimidate will correctly decrease the attack of the second
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentRight);
}
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight);
}
}
@ -390,3 +390,48 @@ SINGLE_BATTLE_TEST("Intimdate does not lose timing after mega evolution and swit
ABILITY_POPUP(opponent, ABILITY_INTIMIDATE);
}
}
DOUBLE_BATTLE_TEST("Intimidate drop down both opposing atk before eject pack has the chance to activate")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_EJECT_PACK); }
PLAYER(SPECIES_WYNAUT);
PLAYER(SPECIES_EKANS) { Ability(ABILITY_INTIMIDATE); }
OPPONENT(SPECIES_WYNAUT);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_EKANS) { Ability(ABILITY_INTIMIDATE); }
} WHEN {
TURN { SWITCH(opponentLeft, 2); SEND_OUT(playerLeft, 2); }
} SCENE {
ABILITY_POPUP(opponentLeft, ABILITY_INTIMIDATE);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerLeft);
ABILITY_POPUP(playerLeft, ABILITY_INTIMIDATE);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentRight);
}
}
DOUBLE_BATTLE_TEST("Intimidate will not miss timing for competitive")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_EJECT_PACK); }
PLAYER(SPECIES_MILOTIC) { Ability(ABILITY_COMPETITIVE); }
PLAYER(SPECIES_EKANS) { Ability(ABILITY_INTIMIDATE); }
OPPONENT(SPECIES_WYNAUT);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_EKANS) { Ability(ABILITY_INTIMIDATE); }
} WHEN {
TURN { SWITCH(opponentLeft, 2); SEND_OUT(playerLeft, 2); }
} SCENE {
ABILITY_POPUP(opponentLeft, ABILITY_INTIMIDATE);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight);
ABILITY_POPUP(playerRight, ABILITY_COMPETITIVE);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerLeft);
ABILITY_POPUP(playerLeft, ABILITY_INTIMIDATE);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentRight);
}
}

View File

@ -1,7 +1,7 @@
#include "global.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Magician does not get self-damage recoil after stealing Life Orb")
SINGLE_BATTLE_TEST("Magician gets self-damage recoil after stealing Life Orb")
{
GIVEN {
ASSUME(gItemsInfo[ITEM_LIFE_ORB].holdEffect == HOLD_EFFECT_LIFE_ORB);
@ -16,10 +16,8 @@ SINGLE_BATTLE_TEST("Magician does not get self-damage recoil after stealing Life
MESSAGE("Delphox used Tackle!");
ABILITY_POPUP(player, ABILITY_MAGICIAN);
MESSAGE("Delphox stole the opposing Wobbuffet's Life Orb!");
NONE_OF {
HP_BAR(player);
MESSAGE("Delphox was hurt by the Life Orb!");
}
HP_BAR(player);
MESSAGE("Delphox was hurt by the Life Orb!");
// 2nd turn - Life Orb recoil happens now
MESSAGE("Delphox used Tackle!");
HP_BAR(player);

View File

@ -102,10 +102,8 @@ SINGLE_BATTLE_TEST("Mirror Armor lowers the Attack of Pokemon with Intimidate")
}
}
// Unsure whether this should or should not fail, as Showdown has conflicting information. Needs testing in gen8 games.
SINGLE_BATTLE_TEST("Mirror Armor doesn't lower the stats of an attacking Pokemon behind Substitute")
{
KNOWN_FAILING;
GIVEN {
PLAYER(SPECIES_CORVIKNIGHT) { Ability(ABILITY_MIRROR_ARMOR); }
OPPONENT(SPECIES_WYNAUT);

View File

@ -100,3 +100,20 @@ SINGLE_BATTLE_TEST("Purifying Salt user can't be poisoned by Toxic Spikes")
EXPECT_EQ(player->status1, STATUS1_NONE);
}
}
SINGLE_BATTLE_TEST("Purifying Salt doesn't prevent pokemon from being poisoned by Toxic Spikes on switch-in if forced in by phazing with Mold Breaker")
{
GIVEN {
ASSUME(GetMoveEffect(MOVE_DRAGON_TAIL) == EFFECT_HIT_SWITCH_TARGET);
ASSUME(GetMoveEffect(MOVE_TOXIC_SPIKES) == EFFECT_TOXIC_SPIKES);
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_GARGANACL) { Ability(ABILITY_PURIFYING_SALT); }
OPPONENT(SPECIES_PINSIR) { Ability(ABILITY_MOLD_BREAKER); }
} WHEN {
TURN { MOVE(opponent, MOVE_TOXIC_SPIKES); }
TURN { MOVE(opponent, MOVE_DRAGON_TAIL); }
} SCENE {
STATUS_ICON(player, STATUS1_POISON);
HP_BAR(player);
}
}

View File

@ -84,7 +84,6 @@ SINGLE_BATTLE_TEST("Teraform Zero cannot be copied")
DOUBLE_BATTLE_TEST("Teraform Zero shouldn't cause Neutralizing Gas to show it's popup when trying to activate")
{
KNOWN_FAILING; // #5010
GIVEN {
PLAYER(SPECIES_TERAPAGOS_TERASTAL);
PLAYER(SPECIES_ABSOL) {Ability(ABILITY_PRESSURE); }
@ -95,8 +94,8 @@ DOUBLE_BATTLE_TEST("Teraform Zero shouldn't cause Neutralizing Gas to show it's
TURN { SWITCH(playerRight, 2); MOVE(playerLeft, MOVE_CELEBRATE, gimmick: GIMMICK_TERA); }
} SCENE {
MESSAGE("Terapagos is storing energy!");
MESSAGE("Terapagos terastalized into the Stellar type!");
MESSAGE("Terapagos terastallized into the Stellar type!");
NOT ABILITY_POPUP(playerRight, ABILITY_NEUTRALIZING_GAS);
MESSAGE("Terapagos used Celebreate!");
MESSAGE("Terapagos used Celebrate!");
}
}

View File

@ -0,0 +1,101 @@
#include "global.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Unburden doubles speed once user uses item")
{
GIVEN {
ASSUME(ItemId_GetHoldEffect(ITEM_GRASSY_SEED) == HOLD_EFFECT_SEEDS);
ASSUME(GetMoveEffect(MOVE_U_TURN) == EFFECT_HIT_ESCAPE);
PLAYER(SPECIES_DRIFBLIM) { Ability(ABILITY_UNBURDEN); Item(ITEM_GRASSY_SEED); Speed(5); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(7); }
OPPONENT(SPECIES_RILLABOOM) { Speed(7); Ability(ABILITY_GRASSY_SURGE); }
} WHEN {
TURN { MOVE(opponent, MOVE_U_TURN); SEND_OUT(opponent, 1); }
TURN { }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_U_TURN, opponent);
ABILITY_POPUP(opponent, ABILITY_GRASSY_SURGE);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, player);
// Turn 2, doubled speed
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponent);
}
}
SINGLE_BATTLE_TEST("Unburden doubles speed once user gets their item knocked off")
{
GIVEN {
ASSUME(MoveHasAdditionalEffect(MOVE_KNOCK_OFF, MOVE_EFFECT_KNOCK_OFF));
PLAYER(SPECIES_DRIFBLIM) { Ability(ABILITY_UNBURDEN); Item(ITEM_POTION); Speed(5); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(7); }
} WHEN {
TURN { MOVE(opponent, MOVE_KNOCK_OFF); }
TURN { }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_KNOCK_OFF, opponent);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ITEM_KNOCKOFF, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, player);
// Turn 2, doubled speed
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponent);
}
}
SINGLE_BATTLE_TEST("Unburden doesn't activate when item is consumed in Neutralizing Gas")
{
GIVEN {
ASSUME(GetMoveEffect(MOVE_U_TURN) == EFFECT_HIT_ESCAPE);
ASSUME(MoveHasAdditionalEffect(MOVE_KNOCK_OFF, MOVE_EFFECT_KNOCK_OFF));
PLAYER(SPECIES_DRIFBLIM) { Ability(ABILITY_UNBURDEN); Item(ITEM_POTION); Speed(5); }
OPPONENT(SPECIES_WEEZING) { Speed(7); Ability(ABILITY_NEUTRALIZING_GAS); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(7); }
} WHEN {
TURN { MOVE(opponent, MOVE_KNOCK_OFF); }
TURN { MOVE(opponent, MOVE_U_TURN); SEND_OUT(opponent, 1); }
TURN { }
} SCENE {
ABILITY_POPUP(opponent, ABILITY_NEUTRALIZING_GAS);
ANIMATION(ANIM_TYPE_MOVE, MOVE_KNOCK_OFF, opponent);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ITEM_KNOCKOFF, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, player);
// Turn 2, no speed increase
ANIMATION(ANIM_TYPE_MOVE, MOVE_U_TURN, opponent);
MESSAGE("The effects of the neutralizing gas wore off!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, player);
// Turn 3, no speed increase
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, player);
}
}
SINGLE_BATTLE_TEST("Unburden doubling speed effect is ignored by Neutralizing Gas")
{
GIVEN {
ASSUME(GetMoveEffect(MOVE_U_TURN) == EFFECT_HIT_ESCAPE);
ASSUME(MoveHasAdditionalEffect(MOVE_KNOCK_OFF, MOVE_EFFECT_KNOCK_OFF));
PLAYER(SPECIES_DRIFBLIM) { Ability(ABILITY_UNBURDEN); Item(ITEM_POTION); Speed(5); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(7); }
OPPONENT(SPECIES_WEEZING) { Speed(7); Ability(ABILITY_NEUTRALIZING_GAS); }
} WHEN {
TURN { MOVE(opponent, MOVE_KNOCK_OFF); }
TURN { MOVE(opponent, MOVE_U_TURN); SEND_OUT(opponent, 1); }
TURN { MOVE(opponent, MOVE_U_TURN); SEND_OUT(opponent, 0); }
TURN { }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_KNOCK_OFF, opponent);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ITEM_KNOCKOFF, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, player);
// Turn 2, doubled speed
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_U_TURN, opponent);
ABILITY_POPUP(opponent, ABILITY_NEUTRALIZING_GAS);
// Turn 3, no speed increase
ANIMATION(ANIM_TYPE_MOVE, MOVE_U_TURN, opponent);
MESSAGE("The effects of the neutralizing gas wore off!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, player);
// Turn 4, doubled speed
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponent);
}
}

View File

@ -160,3 +160,74 @@ SINGLE_BATTLE_TEST("Eject Pack will miss timing to switch out user if Eject Butt
EXPECT(opponent->species == SPECIES_WYNAUT);
}
}
DOUBLE_BATTLE_TEST("Eject Pack: Only the fastest Eject Pack will activate after intimidate")
{
u32 speed;
PARAMETRIZE { speed = 1; }
PARAMETRIZE { speed = 11; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Speed(10); Item(ITEM_EJECT_PACK); }
PLAYER(SPECIES_WYNAUT) { Speed(speed); Item(ITEM_EJECT_PACK); }
PLAYER(SPECIES_WOBBUFFET) { Speed(3); }
OPPONENT(SPECIES_WYNAUT) { Speed(4); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(5); }
OPPONENT(SPECIES_EKANS) { Speed(6); Ability(ABILITY_INTIMIDATE); }
} WHEN {
TURN {
SWITCH(opponentLeft, 2);
if (speed == 11)
SEND_OUT(playerRight, 2);
else
SEND_OUT(playerLeft, 2);
}
} SCENE {
ABILITY_POPUP(opponentLeft, ABILITY_INTIMIDATE);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight);
if (speed == 11) {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerRight);
NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerLeft);
} else {
NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerRight);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerLeft);
}
}
}
DOUBLE_BATTLE_TEST("Eject Pack: Only the fastest Eject Pack will activate after a move stat drop")
{
u32 speed;
PARAMETRIZE { speed = 1; }
PARAMETRIZE { speed = 11; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Speed(10); Item(ITEM_EJECT_PACK); }
PLAYER(SPECIES_WYNAUT) { Speed(speed); Item(ITEM_EJECT_PACK); }
PLAYER(SPECIES_WOBBUFFET) { Speed(3); }
OPPONENT(SPECIES_WYNAUT) { Speed(4); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(5); }
} WHEN {
TURN {
MOVE(opponentLeft, MOVE_BUBBLE);
if (speed == 11)
SEND_OUT(playerRight, 2);
else
SEND_OUT(playerLeft, 2);
}
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_BUBBLE, opponentLeft);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight);
if (speed == 11) {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerRight);
NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerLeft);
} else {
NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerRight);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerLeft);
}
}
}

View File

@ -1,6 +1,32 @@
#include "global.h"
#include "test/battle.h"
TO_DO_BATTLE_TEST("Aqua Ring recovers 1/16th HP at end of turn");
TO_DO_BATTLE_TEST("Aqua Ring can be used under Heal Block but will not heal the user");
SINGLE_BATTLE_TEST("Aqua Ring recovers 1/16th HP at end of turn")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { HP(50); MaxHP(128); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_AQUA_RING); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_AQUA_RING, player);
} THEN {
EXPECT(player->hp == 58);
}
}
SINGLE_BATTLE_TEST("Aqua Ring can be used under Heal Block but will not heal the user")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { HP(50); MaxHP(128); Speed(50); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(100); }
} WHEN {
TURN { MOVE(opponent, MOVE_HEAL_BLOCK); MOVE(player, MOVE_AQUA_RING); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_AQUA_RING, player);
} THEN {
EXPECT(player->hp == 50);
}
}
TO_DO_BATTLE_TEST("Baton Pass passes Aqua Ring's effect");

View File

@ -1,7 +1,105 @@
#include "global.h"
#include "test/battle.h"
TO_DO_BATTLE_TEST("Attract causes the target to become infatuated with the user if they have opposite genders");
TO_DO_BATTLE_TEST("Attract ignores type immunity");
TO_DO_BATTLE_TEST("Attract bypasses Substitute");
TO_DO_BATTLE_TEST("Attract fails if the target is already infatuated");
ASSUMPTIONS
{
ASSUME(GetMoveEffect(MOVE_ATTRACT) == EFFECT_ATTRACT);
ASSUME(gSpeciesInfo[SPECIES_NIDOKING].genderRatio == MON_MALE);
ASSUME(gSpeciesInfo[SPECIES_NIDOQUEEN].genderRatio == MON_FEMALE);
}
SINGLE_BATTLE_TEST("Attract causes the target to become infatuated with the user if they have opposite genders")
{
GIVEN {
PLAYER(SPECIES_NIDOQUEEN);
OPPONENT(SPECIES_NIDOKING);
} WHEN {
TURN { MOVE(player, MOVE_ATTRACT); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_ATTRACT, player);
MESSAGE("The opposing Nidoking fell in love!");
} THEN {
EXPECT(opponent->status2 & STATUS2_INFATUATION);
}
}
SINGLE_BATTLE_TEST("Attract ignores type immunity")
{
GIVEN {
ASSUME(GetMoveType(MOVE_ATTRACT) == TYPE_NORMAL);
PLAYER(SPECIES_NIDOQUEEN);
OPPONENT(SPECIES_MISDREAVUS) { Gender(MON_MALE); }
} WHEN {
TURN { MOVE(player, MOVE_ATTRACT); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_ATTRACT, player);
MESSAGE("The opposing Misdreavus fell in love!");
} THEN {
EXPECT(opponent->status2 & STATUS2_INFATUATION);
}
}
SINGLE_BATTLE_TEST("Attract bypasses Substitute")
{
GIVEN {
PLAYER(SPECIES_NIDOQUEEN) { Speed(90); }
OPPONENT(SPECIES_NIDOKING) { Speed(100); }
} WHEN {
TURN { MOVE(opponent, MOVE_SUBSTITUTE); }
TURN { MOVE(player, MOVE_ATTRACT); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_ATTRACT, player);
MESSAGE("The opposing Nidoking fell in love!");
} THEN {
EXPECT(opponent->status2 & STATUS2_INFATUATION);
}
}
SINGLE_BATTLE_TEST("Attract fails if the target is already infatuated")
{
GIVEN {
PLAYER(SPECIES_NIDOQUEEN);
OPPONENT(SPECIES_NIDOKING);
} WHEN {
TURN { MOVE(player, MOVE_ATTRACT); }
TURN { MOVE(player, MOVE_ATTRACT); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_ATTRACT, player);
MESSAGE("The opposing Nidoking fell in love!");
MESSAGE("Nidoqueen used Attract!");
MESSAGE("But it failed!");
} THEN {
EXPECT(opponent->status2 & STATUS2_INFATUATION);
}
}
SINGLE_BATTLE_TEST("Attract fails when used on a Pokémon of the same gender")
{
GIVEN {
PLAYER(SPECIES_NIDOQUEEN);
OPPONENT(SPECIES_NIDOQUEEN);
} WHEN {
TURN { MOVE(player, MOVE_ATTRACT); }
} SCENE {
MESSAGE("Nidoqueen used Attract!");
MESSAGE("But it failed!");
} THEN {
EXPECT(!(opponent->status2 & STATUS2_INFATUATION));
}
}
SINGLE_BATTLE_TEST("Attract fails when used on a genderless Pokémon")
{
GIVEN {
ASSUME(gSpeciesInfo[SPECIES_STARMIE].genderRatio == MON_GENDERLESS);
PLAYER(SPECIES_NIDOQUEEN);
OPPONENT(SPECIES_STARMIE);
} WHEN {
TURN { MOVE(player, MOVE_ATTRACT); }
} SCENE {
MESSAGE("Nidoqueen used Attract!");
MESSAGE("But it failed!");
} THEN {
EXPECT(!(opponent->status2 & STATUS2_INFATUATION));
}
}

View File

@ -1,10 +1,130 @@
#include "global.h"
#include "test/battle.h"
TO_DO_BATTLE_TEST("Bestow transfers its held item to the target");
TO_DO_BATTLE_TEST("Bestow fails if the user has no held item");
TO_DO_BATTLE_TEST("Bestow fails if the target already has a held item");
TO_DO_BATTLE_TEST("Bestow fails if the target is behind a Substitute");
TO_DO_BATTLE_TEST("Bestow fails if the user is holding Mail");
TO_DO_BATTLE_TEST("Bestow fails if the user's held item changes its form");
TO_DO_BATTLE_TEST("Bestow fails if the user's held item is a Z-Crystal");
SINGLE_BATTLE_TEST("Bestow transfers its held item to the target")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_SITRUS_BERRY); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_BESTOW); }
} THEN {
EXPECT(player->item == ITEM_NONE);
EXPECT(opponent->item == ITEM_SITRUS_BERRY);
}
}
SINGLE_BATTLE_TEST("Bestow fails if the user has no held item")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_BESTOW); }
} SCENE {
MESSAGE("But it failed!");
} THEN {
EXPECT(player->item == ITEM_NONE);
EXPECT(opponent->item == ITEM_NONE);
}
}
SINGLE_BATTLE_TEST("Bestow fails if the target already has a held item")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_SITRUS_BERRY); }
OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_LUM_BERRY); }
} WHEN {
TURN { MOVE(player, MOVE_BESTOW); }
} SCENE {
MESSAGE("But it failed!");
} THEN {
EXPECT(player->item == ITEM_SITRUS_BERRY);
EXPECT(opponent->item == ITEM_LUM_BERRY);
}
}
#include "mail.h"
SINGLE_BATTLE_TEST("Bestow fails if the user is holding Mail")
{
KNOWN_FAILING;
GIVEN {
ASSUME(ItemIsMail(ITEM_ORANGE_MAIL));
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_ORANGE_MAIL); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_BESTOW); }
} SCENE {
MESSAGE("But it failed!");
} THEN {
EXPECT(player->item == ITEM_ORANGE_MAIL);
EXPECT(opponent->item == ITEM_NONE);
}
}
SINGLE_BATTLE_TEST("Bestow fails if the user's held item is a Mega Stone")
{
GIVEN {
PLAYER(SPECIES_BLAZIKEN) { Item(ITEM_BLAZIKENITE); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_BESTOW); }
} SCENE {
MESSAGE("But it failed!");
} THEN {
EXPECT(player->item == ITEM_BLAZIKENITE);
EXPECT(opponent->item == ITEM_NONE);
}
}
SINGLE_BATTLE_TEST("Bestow fails if the user's held item is a Z-Crystal")
{
GIVEN {
ASSUME(ItemId_GetHoldEffect(ITEM_FIGHTINIUM_Z) == HOLD_EFFECT_Z_CRYSTAL);
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_FIGHTINIUM_Z); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_BESTOW); }
} SCENE {
MESSAGE("But it failed!");
} THEN {
EXPECT(player->item == ITEM_FIGHTINIUM_Z);
EXPECT(opponent->item == ITEM_NONE);
}
}
SINGLE_BATTLE_TEST("Bestow fails if the target is behind a Substitute")
{
KNOWN_FAILING;
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_SITRUS_BERRY); Speed(50); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(100); }
} WHEN {
TURN { MOVE(opponent, MOVE_SUBSTITUTE); MOVE(player, MOVE_BESTOW); }
} SCENE {
MESSAGE("But it failed!");
} THEN {
EXPECT(player->item == ITEM_SITRUS_BERRY);
EXPECT(opponent->item == ITEM_NONE);
}
}
SINGLE_BATTLE_TEST("Bestow fails if the user's held item changes its form")
{
KNOWN_FAILING;
GIVEN {
PLAYER(SPECIES_GIRATINA_ORIGIN) { Item(ITEM_GRISEOUS_ORB); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_BESTOW); }
} SCENE {
MESSAGE("But it failed!");
} THEN {
EXPECT(player->item == ITEM_GRISEOUS_ORB);
EXPECT(opponent->item == ITEM_NONE);
}
}

View File

@ -1,7 +1,112 @@
#include "global.h"
#include "test/battle.h"
TO_DO_BATTLE_TEST("Captivate decreases the target's Sp. Attack if they're opposite gender from the user");
TO_DO_BATTLE_TEST("Captivate fails if the target and user share gender");
TO_DO_BATTLE_TEST("Captivate fails if the target is genderless");
TO_DO_BATTLE_TEST("Captivate fails if the user is genderless");
ASSUMPTIONS
{
ASSUME(GetMoveEffect(MOVE_CAPTIVATE) == EFFECT_CAPTIVATE);
ASSUME(gSpeciesInfo[SPECIES_NIDOKING].genderRatio == MON_MALE);
ASSUME(gSpeciesInfo[SPECIES_NIDOQUEEN].genderRatio == MON_FEMALE);
ASSUME(gSpeciesInfo[SPECIES_STARMIE].genderRatio == MON_GENDERLESS);
}
SINGLE_BATTLE_TEST("Captivate decreases the target's Sp. Attack if they're opposite gender from the user")
{
GIVEN {
PLAYER(SPECIES_NIDOQUEEN);
OPPONENT(SPECIES_NIDOKING);
} WHEN {
TURN { MOVE(player, MOVE_CAPTIVATE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_CAPTIVATE, player);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
MESSAGE("The opposing Nidoking's Sp. Atk harshly fell!");
} THEN {
EXPECT(opponent->statStages[STAT_SPATK] == 4);
}
}
SINGLE_BATTLE_TEST("Captivate fails if the target and user share gender")
{
GIVEN {
PLAYER(SPECIES_NIDOKING);
OPPONENT(SPECIES_NIDOKING);
} WHEN {
TURN { MOVE(player, MOVE_CAPTIVATE); }
} SCENE {
MESSAGE("But it failed!");
} THEN {
EXPECT(opponent->statStages[STAT_SPATK] == 6);
}
}
SINGLE_BATTLE_TEST("Captivate fails if the target is genderless")
{
GIVEN {
PLAYER(SPECIES_NIDOQUEEN);
OPPONENT(SPECIES_STARMIE);
} WHEN {
TURN { MOVE(player, MOVE_CAPTIVATE); }
} SCENE {
MESSAGE("But it failed!");
} THEN {
EXPECT(opponent->statStages[STAT_SPATK] == 6);
}
}
SINGLE_BATTLE_TEST("Captivate fails if the user is genderless")
{
GIVEN {
PLAYER(SPECIES_STARMIE);
OPPONENT(SPECIES_NIDOQUEEN);
} WHEN {
TURN { MOVE(player, MOVE_CAPTIVATE); }
} SCENE {
MESSAGE("But it failed!");
} THEN {
EXPECT(opponent->statStages[STAT_SPATK] == 6);
}
}
SINGLE_BATTLE_TEST("Captivate fails if both the user and the opponent are genderless")
{
GIVEN {
PLAYER(SPECIES_STARMIE);
OPPONENT(SPECIES_STARMIE);
} WHEN {
TURN { MOVE(player, MOVE_CAPTIVATE); }
} SCENE {
MESSAGE("But it failed!");
} THEN {
EXPECT(opponent->statStages[STAT_SPATK] == 6);
}
}
SINGLE_BATTLE_TEST("Attract fails when used by a genderless Pokémon")
{
GIVEN {
PLAYER(SPECIES_STARMIE);
OPPONENT(SPECIES_NIDOQUEEN);
} WHEN {
TURN { MOVE(player, MOVE_ATTRACT); }
} SCENE {
MESSAGE("Starmie used Attract!");
MESSAGE("But it failed!");
} THEN {
EXPECT(!(opponent->status2 & STATUS2_INFATUATION));
}
}
SINGLE_BATTLE_TEST("Attract fails if both the user and the target are genderless")
{
GIVEN {
PLAYER(SPECIES_STARMIE);
OPPONENT(SPECIES_STARMIE);
} WHEN {
TURN { MOVE(player, MOVE_ATTRACT); }
} SCENE {
MESSAGE("Starmie used Attract!");
MESSAGE("But it failed!");
} THEN {
EXPECT(!(opponent->status2 & STATUS2_INFATUATION));
}
}

View File

@ -14,6 +14,48 @@ AI_DOUBLE_BATTLE_TEST("AI prefers Entrainment'ing good abilities onto partner wi
}
}
TO_DO_BATTLE_TEST("Entrainment changes the target's Ability to match the user's");
TO_DO_BATTLE_TEST("Entrainment fails if the user's ability has cantBeCopied flag");
TO_DO_BATTLE_TEST("Entrainment fails if the targets's ability has cantBeOverwritten flag");
SINGLE_BATTLE_TEST("Entrainment changes the target's Ability to match the user's")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_TELEPATHY); }
OPPONENT(SPECIES_WOBBUFFET) { Ability(ABILITY_SHADOW_TAG); }
} WHEN {
TURN { MOVE(player, MOVE_ENTRAINMENT); }
} THEN {
EXPECT(opponent->ability == ABILITY_TELEPATHY);
}
}
SINGLE_BATTLE_TEST("Entrainment fails if the user's ability has cantBeCopied flag")
{
GIVEN {
ASSUME(gAbilitiesInfo[ABILITY_MULTITYPE].cantBeCopied);
PLAYER(SPECIES_ARCEUS) { Ability(ABILITY_MULTITYPE); }
OPPONENT(SPECIES_WOBBUFFET) { Ability(ABILITY_SHADOW_TAG); }
} WHEN {
TURN { MOVE(player, MOVE_ENTRAINMENT); }
} SCENE {
MESSAGE("But it failed!");
} THEN {
EXPECT(player->ability == ABILITY_MULTITYPE);
EXPECT(opponent->ability == ABILITY_SHADOW_TAG);
}
}
SINGLE_BATTLE_TEST("Entrainment fails if the target's ability has cantBeOverwritten flag")
{
GIVEN {
ASSUME(gAbilitiesInfo[ABILITY_MULTITYPE].cantBeOverwritten);
PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_TELEPATHY); }
OPPONENT(SPECIES_ARCEUS) { Ability(ABILITY_MULTITYPE); }
} WHEN {
TURN { MOVE(player, MOVE_ENTRAINMENT); }
} SCENE {
MESSAGE("But it failed!");
} THEN {
EXPECT(player->ability == ABILITY_TELEPATHY);
EXPECT(opponent->ability == ABILITY_MULTITYPE);
}
}
TO_DO_BATTLE_TEST("Entrainment fails on Dynamaxed Pokémon");

View File

@ -133,3 +133,18 @@ SINGLE_BATTLE_TEST("Tanga Berry activates before Bug Bite")
EXPECT_EQ(player->item, ITEM_NONE);
}
}
SINGLE_BATTLE_TEST("Bug Bite ignores Unnerve")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { HP(1); }
OPPONENT(SPECIES_TYRANITAR) { Ability(ABILITY_UNNERVE); Item(ITEM_ORAN_BERRY); }
} WHEN {
TURN { MOVE(player, MOVE_BUG_BITE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_BUG_BITE, player);
HP_BAR(player);
} THEN {
EXPECT_EQ(opponent->item, ITEM_NONE);
}
}