10/11/25 Master to upcoming merge

This commit is contained in:
AlexOn1ine 2025-11-01 11:54:17 +01:00
commit d4e7e7ad36
18 changed files with 271 additions and 117 deletions

View File

@ -189,8 +189,13 @@
.4byte \jumpInstr
.endm
.macro unused_0x21
.macro jumpifstatignorecontrary battler:req, comparison:req, stat:req, value:req, jumpInstr:req
.byte 0x21
.byte \battler
.byte \comparison
.byte \stat
.byte \value
.4byte \jumpInstr
.endm
.macro jumpbasedontype battler:req, type:req, jumpIfType:req, jumpInstr:req

View File

@ -3685,7 +3685,7 @@ BattleScript_BlockedByPrimalWeatherRet::
BattleScript_EffectBellyDrum::
attackcanceler
jumpifstat BS_ATTACKER, CMP_EQUAL, STAT_ATK, MAX_STAT_STAGE, BattleScript_ButItFailed
jumpifstatignorecontrary BS_ATTACKER, CMP_EQUAL, STAT_ATK, MAX_STAT_STAGE, BattleScript_ButItFailed
halvehp BattleScript_ButItFailed
attackanimation
waitanimation
@ -7440,11 +7440,13 @@ BattleScript_CuteCharmActivates::
return
BattleScript_GooeyActivates::
statbuffchange BS_ATTACKER, STAT_CHANGE_ONLY_CHECKING, BattleScript_GooeyActivatesRet
waitstate
call BattleScript_AbilityPopUp
swapattackerwithtarget @ for defiant, mirror armor
seteffectsecondary BS_ATTACKER, BS_TARGET, MOVE_EFFECT_SPD_MINUS_1
swapattackerwithtarget
BattleScript_GooeyActivatesRet:
return
BattleScript_AbilityStatusEffect::

View File

@ -315,6 +315,7 @@ bool32 IsBattlerMegaEvolved(u32 battler);
bool32 IsBattlerPrimalReverted(u32 battler);
bool32 IsBattlerUltraBursted(u32 battler);
u16 GetBattleFormChangeTargetSpecies(u32 battler, enum FormChanges method);
bool32 TryRevertPartyMonFormChange(u32 partyIndex);
bool32 TryBattleFormChange(u32 battler, enum FormChanges method);
bool32 DoBattlersShareType(u32 battler1, u32 battler2);
bool32 CanBattlerGetOrLoseItem(u32 battler, u16 itemId);
@ -346,7 +347,7 @@ void TrySaveExchangedItem(u32 battler, u16 stolenItem);
bool32 IsPartnerMonFromSameTrainer(u32 battler);
bool32 IsBattlerAffectedByHazards(u32 battler, bool32 toxicSpikes);
void SortBattlersBySpeed(u8 *battlers, bool32 slowToFast);
bool32 CompareStat(u32 battler, enum Stat statId, u8 cmpTo, u8 cmpKind);
bool32 CompareStat(u32 battler, enum Stat statId, u8 cmpTo, u8 cmpKind, enum Ability ability);
bool32 BlocksPrankster(u16 move, u32 battlerPrankster, u32 battlerDef, bool32 checkTarget);
bool32 PickupHasValidTarget(u32 battler);
bool32 CantPickupItem(u32 battler);

View File

@ -461,6 +461,7 @@ void HandleInputChooseTarget(u32 battler)
gBattleStruct->zmove.viewing = TRUE;
ReloadMoveNames(battler);
}
TryToAddMoveInfoWindow();
DoBounceEffect(battler, BOUNCE_HEALTHBOX, 7, 1);
DoBounceEffect(battler, BOUNCE_MON, 7, 1);
EndBounceEffect(gMultiUsePlayerCursor, BOUNCE_HEALTHBOX);

View File

@ -77,7 +77,7 @@ enum ItemEffect TryBoosterEnergy(u32 battler, enum Ability ability, ActivationTi
static enum ItemEffect TryRoomService(u32 battler, ActivationTiming timing)
{
if (gFieldStatuses & STATUS_FIELD_TRICK_ROOM && CompareStat(battler, STAT_SPEED, MIN_STAT_STAGE, CMP_GREATER_THAN))
if (gFieldStatuses & STATUS_FIELD_TRICK_ROOM && CompareStat(battler, STAT_SPEED, MIN_STAT_STAGE, CMP_GREATER_THAN, GetBattlerAbility(battler)))
{
gEffectBattler = gBattleScripting.battler = battler;
SET_STATCHANGER(STAT_SPEED, 1, TRUE);
@ -97,7 +97,7 @@ static enum ItemEffect TryRoomService(u32 battler, ActivationTiming timing)
enum ItemEffect TryHandleSeed(u32 battler, u32 terrainFlag, enum Stat statId, ActivationTiming timing)
{
if (gFieldStatuses & terrainFlag && CompareStat(battler, statId, MAX_STAT_STAGE, CMP_LESS_THAN))
if (gFieldStatuses & terrainFlag && CompareStat(battler, statId, MAX_STAT_STAGE, CMP_LESS_THAN, GetBattlerAbility(battler)))
{
gEffectBattler = gBattleScripting.battler = battler;
SET_STATCHANGER(statId, 1, FALSE);
@ -423,7 +423,7 @@ static enum ItemEffect TryBlunderPolicy(u32 battlerAtk)
if (gBattleStruct->blunderPolicy
&& IsBattlerAlive(battlerAtk)
&& CompareStat(battlerAtk, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN))
&& CompareStat(battlerAtk, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN, GetBattlerAbility(battlerAtk)))
{
gBattleStruct->blunderPolicy = FALSE;
SET_STATCHANGER(STAT_SPEED, 2, FALSE);
@ -502,7 +502,7 @@ static enum ItemEffect TryThroatSpray(u32 battlerAtk)
&& gMultiHitCounter == 0
&& IsBattlerAlive(battlerAtk)
&& IsAnyTargetTurnDamaged(battlerAtk)
&& CompareStat(battlerAtk, STAT_SPATK, MAX_STAT_STAGE, CMP_LESS_THAN)
&& CompareStat(battlerAtk, STAT_SPATK, MAX_STAT_STAGE, CMP_LESS_THAN, GetBattlerAbility(battlerAtk))
&& !NoAliveMonsForEitherParty()) // Don't activate if battle will end
{
SET_STATCHANGER(STAT_SPATK, 1, FALSE);
@ -517,7 +517,7 @@ static enum ItemEffect DamagedStatBoostBerryEffect(u32 battlerDef, u32 battlerAt
{
enum ItemEffect effect = ITEM_NO_EFFECT;
if (!IsBattlerAlive(battlerDef) || !CompareStat(battlerDef, statId, MAX_STAT_STAGE, CMP_LESS_THAN))
if (!IsBattlerAlive(battlerDef) || !CompareStat(battlerDef, statId, MAX_STAT_STAGE, CMP_LESS_THAN, GetBattlerAbility(battlerDef)))
return effect;
if (gBattleScripting.overrideBerryRequirements
@ -970,7 +970,7 @@ static enum ItemEffect StatRaiseBerry(u32 battler, u32 itemId, enum Stat statId,
enum ItemEffect effect = ITEM_NO_EFFECT;
enum Ability ability = GetBattlerAbility(battler);
if (CompareStat(battler, statId, MAX_STAT_STAGE, CMP_LESS_THAN)
if (CompareStat(battler, statId, MAX_STAT_STAGE, CMP_LESS_THAN, ability)
&& HasEnoughHpToEatBerry(battler, ability, GetItemHoldEffectParam(itemId), itemId))
{
gEffectBattler = gBattleScripting.battler = battler;
@ -1011,17 +1011,17 @@ static enum ItemEffect RandomStatRaiseBerry(u32 battler, u32 itemId, ActivationT
{
enum ItemEffect effect = ITEM_NO_EFFECT;
enum Stat stat;
enum Ability ability = GetBattlerAbility(battler);
for (stat = STAT_ATK; stat < NUM_STATS; stat++)
{
if (CompareStat(battler, stat, MAX_STAT_STAGE, CMP_LESS_THAN))
if (CompareStat(battler, stat, MAX_STAT_STAGE, CMP_LESS_THAN, ability))
break;
}
if (stat == NUM_STATS)
return effect;
enum Ability ability = GetBattlerAbility(battler);
if (HasEnoughHpToEatBerry(battler, ability, GetItemHoldEffectParam(itemId), itemId))
{
u32 savedAttacker = gBattlerAttacker;

View File

@ -5642,19 +5642,8 @@ static void HandleEndTurn_FinishBattle(void)
for (i = 0; i < PARTY_SIZE; i++)
{
bool8 changedForm = FALSE;
// Appeared in battle and didn't faint
if (gBattleStruct->partyState[B_SIDE_PLAYER][i].sentOut && GetMonData(&gPlayerParty[i], MON_DATA_HP, NULL) != 0)
changedForm = TryFormChange(i, B_SIDE_PLAYER, FORM_CHANGE_END_BATTLE_ENVIRONMENT);
if (!changedForm)
changedForm = TryFormChange(i, B_SIDE_PLAYER, FORM_CHANGE_END_BATTLE);
// Clear original species field
gBattleStruct->partyState[B_SIDE_PLAYER][i].changedSpecies = SPECIES_NONE;
gBattleStruct->partyState[B_SIDE_OPPONENT][i].changedSpecies = SPECIES_NONE;
bool32 changedForm = TryRevertPartyMonFormChange(i);
// Recalculate the stats of every party member before the end
if (!changedForm && B_RECALCULATE_STATS >= GEN_5)
CalculateMonStats(&gPlayerParty[i]);

View File

@ -370,7 +370,7 @@ static void Cmd_jumpifvolatile(void);
static void Cmd_jumpifability(void);
static void Cmd_jumpifsideaffecting(void);
static void Cmd_jumpifstat(void);
static void Cmd_unused_0x21(void);
static void Cmd_jumpifstatignorecontrary(void);
static void Cmd_jumpbasedontype(void);
static void Cmd_getexp(void);
static void Cmd_checkteamslost(void);
@ -629,7 +629,7 @@ void (*const gBattleScriptingCommandsTable[])(void) =
Cmd_jumpifability, //0x1E
Cmd_jumpifsideaffecting, //0x1F
Cmd_jumpifstat, //0x20
Cmd_unused_0x21, //0x21
Cmd_jumpifstatignorecontrary, //0x21
Cmd_jumpbasedontype, //0x22
Cmd_getexp, //0x23
Cmd_checkteamslost, //0x24
@ -4384,7 +4384,7 @@ static void Cmd_jumpifstat(void)
u8 value = cmd->value;
u8 comparison = cmd->comparison;
ret = CompareStat(battler, stat, value, comparison);
ret = CompareStat(battler, stat, value, comparison, GetBattlerAbility(battler));
if (ret)
gBattlescriptCurrInstr = cmd->jumpInstr;
@ -4392,8 +4392,22 @@ static void Cmd_jumpifstat(void)
gBattlescriptCurrInstr = cmd->nextInstr;
}
static void Cmd_unused_0x21(void)
static void Cmd_jumpifstatignorecontrary(void)
{
CMD_ARGS(u8 battler, u8 comparison, u8 stat, u8 value, const u8 *jumpInstr);
bool32 ret = 0;
u8 battler = GetBattlerForBattleScript(cmd->battler);
u8 stat = cmd->stat;
u8 value = cmd->value;
u8 comparison = cmd->comparison;
ret = CompareStat(battler, stat, value, comparison, ABILITY_NONE);
if (ret)
gBattlescriptCurrInstr = cmd->jumpInstr;
else
gBattlescriptCurrInstr = cmd->nextInstr;
}
static void Cmd_jumpbasedontype(void)
@ -5572,7 +5586,7 @@ static bool32 HandleMoveEndAbilityBlock(u32 battlerAtk, u32 battlerDef, u32 move
else if (abilityAtk == ABILITY_GRIM_NEIGH || abilityAtk == ABILITY_AS_ONE_SHADOW_RIDER)
stat = STAT_SPATK;
if (numMonsFainted && CompareStat(battlerAtk, stat, MAX_STAT_STAGE, CMP_LESS_THAN))
if (numMonsFainted && CompareStat(battlerAtk, stat, MAX_STAT_STAGE, CMP_LESS_THAN, abilityAtk))
{
gLastUsedAbility = abilityAtk;
if (abilityAtk == ABILITY_AS_ONE_ICE_RIDER)
@ -5612,17 +5626,17 @@ static bool32 HandleMoveEndAbilityBlock(u32 battlerAtk, u32 battlerDef, u32 move
else
{
u32 numStatBuffs = 0;
if (CompareStat(battlerAtk, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN))
if (CompareStat(battlerAtk, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN, abilityAtk))
{
gBattleScripting.animArg1 = GET_STAT_BUFF_ID(STAT_ATK) + STAT_ANIM_PLUS1;
numStatBuffs++;
}
if (CompareStat(battlerAtk, STAT_SPATK, MAX_STAT_STAGE, CMP_LESS_THAN))
if (CompareStat(battlerAtk, STAT_SPATK, MAX_STAT_STAGE, CMP_LESS_THAN, abilityAtk))
{
gBattleScripting.animArg1 = GET_STAT_BUFF_ID(STAT_SPATK) + STAT_ANIM_PLUS1;
numStatBuffs++;
}
if (CompareStat(battlerAtk, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN))
if (CompareStat(battlerAtk, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN, abilityAtk))
{
gBattleScripting.animArg1 = GET_STAT_BUFF_ID(STAT_SPEED) + STAT_ANIM_PLUS1;
numStatBuffs++;
@ -5853,7 +5867,7 @@ static bool32 HandleMoveEndMoveBlock(u32 moveEffect)
&& !IsBattlerAlive(gBattlerTarget)
&& IsBattlerTurnDamaged(gBattlerTarget)
&& !NoAliveMonsForEitherParty()
&& CompareStat(gBattlerAttacker, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN))
&& CompareStat(gBattlerAttacker, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN, GetBattlerAbility(gBattlerAttacker)))
{
SET_STATCHANGER(STAT_ATK, GetGenConfig(GEN_CONFIG_FELL_STINGER_STAT_RAISE) >= GEN_7 ? 3 : 2, FALSE);
PREPARE_STAT_BUFFER(gBattleTextBuff1, STAT_ATK);
@ -6118,7 +6132,7 @@ static void Cmd_moveend(void)
&& !IsBattlerAlly(gBattlerAttacker, gBattlerTarget)
&& IsBattlerTurnDamaged(gBattlerTarget)
&& !IsBattleMoveStatus(gCurrentMove)
&& CompareStat(gBattlerTarget, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN))
&& CompareStat(gBattlerTarget, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN, GetBattlerAbility(gBattlerTarget)))
{
SET_STATCHANGER(STAT_ATK, 1, FALSE);
BattleScriptCall(BattleScript_RageIsBuilding);
@ -11912,7 +11926,7 @@ static void Cmd_jumpifconfusedandstatmaxed(void)
CMD_ARGS(u8 stat, const u8 *jumpInstr);
if (gBattleMons[gBattlerTarget].volatiles.confusionTurns > 0
&& !CompareStat(gBattlerTarget, cmd->stat, MAX_STAT_STAGE, CMP_LESS_THAN))
&& !CompareStat(gBattlerTarget, cmd->stat, MAX_STAT_STAGE, CMP_LESS_THAN, GetBattlerAbility(gBattlerTarget)))
gBattlescriptCurrInstr = cmd->jumpInstr; // Fails if we're confused AND stat cannot be raised
else
gBattlescriptCurrInstr = cmd->nextInstr;
@ -13892,6 +13906,8 @@ static void Cmd_givecaughtmon(void)
}
else
{
//Before sending to PC, we revert battle form
TryRevertPartyMonFormChange(gSelectedMonPartyId);
// Mon chosen, try to put it in the PC
if (CopyMonToPC(&gPlayerParty[gSelectedMonPartyId]) == MON_GIVEN_TO_PC)
{
@ -15690,7 +15706,7 @@ void BS_CanTarShotWork(void)
NATIVE_ARGS(const u8 *failInstr);
// Tar Shot will fail if it's already been used on the target or if its speed can't be lowered further
if (!gDisableStructs[gBattlerTarget].tarShot
&& CompareStat(gBattlerTarget, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN))
&& CompareStat(gBattlerTarget, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN, GetBattlerAbility(gBattlerTarget)))
gBattlescriptCurrInstr = cmd->nextInstr;
else
gBattlescriptCurrInstr = cmd->failInstr;
@ -16705,7 +16721,7 @@ void BS_TryAcupressure(void)
u32 bits = 0;
for (enum Stat stat = STAT_ATK; stat < NUM_BATTLE_STATS; stat++)
{
if (CompareStat(gBattlerTarget, stat, MAX_STAT_STAGE, CMP_LESS_THAN))
if (CompareStat(gBattlerTarget, stat, MAX_STAT_STAGE, CMP_LESS_THAN, GetBattlerAbility(gBattlerTarget)))
bits |= 1u << stat;
}
if (bits)
@ -17104,10 +17120,11 @@ void BS_TryActivateSoulheart(void)
while (gBattleStruct->soulheartBattlerId < gBattlersCount)
{
gBattleScripting.battler = gBattleStruct->soulheartBattlerId++;
if (GetBattlerAbility(gBattleScripting.battler) == ABILITY_SOUL_HEART
u32 ability = GetBattlerAbility(gBattleScripting.battler);
if (ability == ABILITY_SOUL_HEART
&& IsBattlerAlive(gBattleScripting.battler)
&& !NoAliveMonsForEitherParty()
&& CompareStat(gBattleScripting.battler, STAT_SPATK, MAX_STAT_STAGE, CMP_LESS_THAN))
&& CompareStat(gBattleScripting.battler, STAT_SPATK, MAX_STAT_STAGE, CMP_LESS_THAN, ability))
{
SET_STATCHANGER(STAT_SPATK, 1, FALSE);
PREPARE_STAT_BUFFER(gBattleTextBuff1, STAT_SPATK);
@ -17922,11 +17939,12 @@ void BS_SetAttackerToStickyWebUser(void)
void BS_CutOneThirdHpAndRaiseStats(void)
{
NATIVE_ARGS(const u8 *failInstr);
bool32 atLeastOneStatBoosted = FALSE;
u32 ability = GetBattlerAbility(gBattlerAttacker);
for (u32 stat = 1; stat < NUM_STATS; stat++)
{
if (CompareStat(gBattlerAttacker, stat, MAX_STAT_STAGE, CMP_LESS_THAN))
if (CompareStat(gBattlerAttacker, stat, MAX_STAT_STAGE, CMP_LESS_THAN, ability))
{
atLeastOneStatBoosted = TRUE;
break;

View File

@ -66,6 +66,7 @@ static u32 GetFlingPowerFromItemId(u32 itemId);
static void SetRandomMultiHitCounter();
static bool32 IsNonVolatileStatusBlocked(u32 battlerDef, enum Ability abilityDef, enum Ability abilityAffected, const u8 *battleScript, enum FunctionCallOption option);
static bool32 CanSleepDueToSleepClause(u32 battlerAtk, u32 battlerDef, enum FunctionCallOption option);
static bool32 IsOpposingSideEmpty(u32 battler);
// Submoves
static u32 GetMirrorMoveMove(void);
@ -1237,7 +1238,7 @@ void PrepareStringBattle(enum StringID stringId, u32 battler)
case STRINGID_PKMNCUTSATTACKWITH:
if (GetGenConfig(GEN_CONFIG_UPDATED_INTIMIDATE) >= GEN_8
&& targetAbility == ABILITY_RATTLED
&& CompareStat(gBattlerTarget, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN))
&& CompareStat(gBattlerTarget, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN, targetAbility))
{
gBattlerAbility = gBattlerTarget;
BattleScriptCall(BattleScript_AbilityRaisesDefenderStat);
@ -1763,13 +1764,13 @@ u32 GetBattlerAffectionHearts(u32 battler)
// gBattlerAttacker is the battler that's trying to raise their stats and due to limitations of RandomUniformExcept, cannot be an argument
bool32 MoodyCantRaiseStat(u32 stat)
{
return CompareStat(gBattlerAttacker, stat, MAX_STAT_STAGE, CMP_EQUAL);
return CompareStat(gBattlerAttacker, stat, MAX_STAT_STAGE, CMP_EQUAL, GetBattlerAbility(gBattlerAttacker));
}
// gBattlerAttacker is the battler that's trying to lower their stats and due to limitations of RandomUniformExcept, cannot be an argument
bool32 MoodyCantLowerStat(u32 stat)
{
return stat == GET_STAT_BUFF_ID(gBattleScripting.statChanger) || CompareStat(gBattlerAttacker, stat, MIN_STAT_STAGE, CMP_EQUAL);
return stat == GET_STAT_BUFF_ID(gBattleScripting.statChanger) || CompareStat(gBattlerAttacker, stat, MIN_STAT_STAGE, CMP_EQUAL, GetBattlerAbility(gBattlerAttacker));
}
void TryToRevertMimicryAndFlags(void)
@ -3540,7 +3541,7 @@ bool32 CanAbilityAbsorbMove(u32 battlerAtk, u32 battlerDef, enum Ability ability
break;
case MOVE_ABSORBED_BY_STAT_INCREASE_ABILITY:
gBattleStruct->pledgeMove = FALSE;
if (!CompareStat(battlerDef, statId, MAX_STAT_STAGE, CMP_LESS_THAN))
if (!CompareStat(battlerDef, statId, MAX_STAT_STAGE, CMP_LESS_THAN, abilityDef))
{
battleScript = BattleScript_MonMadeMoveUseless;
}
@ -4119,7 +4120,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab
gSpecialStatuses[battler].switchInAbilityDone = TRUE;
if (CompareStat(battler, statId, MAX_STAT_STAGE, CMP_LESS_THAN))
if (CompareStat(battler, statId, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility))
{
SET_STATCHANGER(statId, 1, FALSE);
SaveBattlerAttacker(gBattlerAttacker);
@ -4272,7 +4273,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab
}
break;
case ABILITY_INTIMIDATE:
if (!gSpecialStatuses[battler].switchInAbilityDone)
if (!gSpecialStatuses[battler].switchInAbilityDone && !IsOpposingSideEmpty(battler))
{
SaveBattlerAttacker(gBattlerAttacker);
gBattlerAttacker = battler;
@ -4284,7 +4285,8 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab
break;
case ABILITY_SUPERSWEET_SYRUP:
if (!gSpecialStatuses[battler].switchInAbilityDone
&& !GetBattlerPartyState(battler)->supersweetSyrup)
&& !GetBattlerPartyState(battler)->supersweetSyrup
&& !IsOpposingSideEmpty(battler))
{
SaveBattlerAttacker(gBattlerAttacker);
gBattlerAttacker = battler;
@ -4331,7 +4333,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab
if (GetGenConfig(GEN_INTREPID_SWORD) == GEN_9)
GetBattlerPartyState(battler)->intrepidSwordBoost = TRUE;
gSpecialStatuses[battler].switchInAbilityDone = TRUE;
if (CompareStat(battler, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN))
if (CompareStat(battler, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility))
{
SET_STATCHANGER(STAT_ATK, 1, FALSE);
BattleScriptPushCursorAndCallback(BattleScript_BattlerAbilityStatRaiseOnSwitchIn);
@ -4346,7 +4348,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab
if (GetGenConfig(GEN_DAUNTLESS_SHIELD) == GEN_9)
GetBattlerPartyState(battler)->dauntlessShieldBoost = TRUE;
gSpecialStatuses[battler].switchInAbilityDone = TRUE;
if (CompareStat(battler, STAT_DEF, MAX_STAT_STAGE, CMP_LESS_THAN))
if (CompareStat(battler, STAT_DEF, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility))
{
SET_STATCHANGER(STAT_DEF, 1, FALSE);
BattleScriptPushCursorAndCallback(BattleScript_BattlerAbilityStatRaiseOnSwitchIn);
@ -4356,7 +4358,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab
break;
case ABILITY_WIND_RIDER:
if (!gSpecialStatuses[battler].switchInAbilityDone
&& CompareStat(battler, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN)
&& CompareStat(battler, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility)
&& gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_TAILWIND)
{
gSpecialStatuses[battler].switchInAbilityDone = TRUE;
@ -4496,7 +4498,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab
else //ABILITY_EMBODY_ASPECT_TEAL_MASK
stat = STAT_SPEED;
if (CompareStat(battler, stat, MAX_STAT_STAGE, CMP_EQUAL))
if (CompareStat(battler, stat, MAX_STAT_STAGE, CMP_EQUAL, gLastUsedAbility))
break;
gSpecialStatuses[battler].switchInAbilityDone = TRUE;
@ -4646,7 +4648,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab
}
break;
case ABILITY_SPEED_BOOST:
if (CompareStat(battler, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN) && gDisableStructs[battler].isFirstTurn != 2)
if (CompareStat(battler, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility) && gDisableStructs[battler].isFirstTurn != 2)
{
SaveBattlerAttacker(gBattlerAttacker);
SET_STATCHANGER(STAT_SPEED, 1, FALSE);
@ -4663,9 +4665,9 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab
for (i = STAT_ATK; i < statsNum; i++)
{
if (CompareStat(battler, i, MIN_STAT_STAGE, CMP_GREATER_THAN))
if (CompareStat(battler, i, MIN_STAT_STAGE, CMP_GREATER_THAN, gLastUsedAbility))
validToLower |= 1u << i;
if (CompareStat(battler, i, MAX_STAT_STAGE, CMP_LESS_THAN))
if (CompareStat(battler, i, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility))
validToRaise |= 1u << i;
}
@ -4807,7 +4809,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab
if (IsBattlerTurnDamaged(battler)
&& IsBattlerAlive(battler)
&& HadMoreThanHalfHpNowDoesnt(battler)
&& CompareStat(battler, STAT_SPATK, MAX_STAT_STAGE, CMP_LESS_THAN))
&& CompareStat(battler, STAT_SPATK, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility))
{
gEffectBattler = gBattlerAbility = battler;
SET_STATCHANGER(STAT_SPATK, 1, FALSE);
@ -4836,7 +4838,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab
if (IsBattlerTurnDamaged(battler)
&& IsBattlerAlive(battler)
&& moveType == TYPE_DARK
&& CompareStat(battler, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN))
&& CompareStat(battler, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility))
{
gEffectBattler = gBattlerAbility = battler;
SET_STATCHANGER(STAT_ATK, 1, FALSE);
@ -4848,7 +4850,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab
if (IsBattlerTurnDamaged(battler)
&& IsBattlerAlive(battler)
&& (moveType == TYPE_DARK || moveType == TYPE_BUG || moveType == TYPE_GHOST)
&& CompareStat(battler, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN))
&& CompareStat(battler, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility))
{
gEffectBattler = gBattlerAbility = battler;
SET_STATCHANGER(STAT_SPEED, 1, FALSE);
@ -4860,7 +4862,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab
if (IsBattlerTurnDamaged(battler)
&& IsBattlerAlive(battler)
&& moveType == TYPE_WATER
&& CompareStat(battler, STAT_DEF, MAX_STAT_STAGE, CMP_LESS_THAN))
&& CompareStat(battler, STAT_DEF, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility))
{
gEffectBattler = gBattlerAbility = battler;
SET_STATCHANGER(STAT_DEF, 2, FALSE);
@ -4872,7 +4874,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab
if (gBattlerAttacker != gBattlerTarget
&& IsBattlerTurnDamaged(gBattlerTarget)
&& IsBattlerAlive(battler)
&& CompareStat(battler, STAT_DEF, MAX_STAT_STAGE, CMP_LESS_THAN))
&& CompareStat(battler, STAT_DEF, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility))
{
gEffectBattler = gBattlerAbility = battler;
SET_STATCHANGER(STAT_DEF, 1, FALSE);
@ -4884,8 +4886,8 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab
if (IsBattlerTurnDamaged(battler)
&& IsBattlerAlive(battler)
&& IsBattleMovePhysical(gCurrentMove)
&& (CompareStat(battler, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN) // Don't activate if both Speed and Defense cannot be raised.
|| CompareStat(battler, STAT_DEF, MIN_STAT_STAGE, CMP_GREATER_THAN)))
&& (CompareStat(battler, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility) // Don't activate if both Speed and Defense cannot be raised.
|| CompareStat(battler, STAT_DEF, MIN_STAT_STAGE, CMP_GREATER_THAN, gLastUsedAbility)))
{
if (GetMoveEffect(gCurrentMove) == EFFECT_HIT_ESCAPE && CanBattlerSwitch(gBattlerAttacker))
gProtectStructs[battler].disableEjectPack = TRUE; // Set flag for target
@ -4964,7 +4966,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab
if (gSpecialStatuses[battler].criticalHit
&& IsBattlerTurnDamaged(battler)
&& IsBattlerAlive(battler)
&& CompareStat(battler, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN))
&& CompareStat(battler, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility))
{
SET_STATCHANGER(STAT_ATK, MAX_STAT_STAGE - gBattleMons[battler].statStages[STAT_ATK], FALSE);
BattleScriptCall(BattleScript_TargetsStatWasMaxedOut);
@ -4974,7 +4976,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab
case ABILITY_GOOEY:
case ABILITY_TANGLING_HAIR:
if (IsBattlerAlive(gBattlerAttacker)
&& (CompareStat(gBattlerAttacker, STAT_SPEED, MIN_STAT_STAGE, CMP_GREATER_THAN) || GetBattlerAbility(gBattlerAttacker) == ABILITY_MIRROR_ARMOR)
&& (CompareStat(gBattlerAttacker, STAT_SPEED, MIN_STAT_STAGE, CMP_GREATER_THAN, gLastUsedAbility) || GetBattlerAbility(gBattlerAttacker) == ABILITY_MIRROR_ARMOR)
&& !gProtectStructs[gBattlerAttacker].confusionSelfDmg
&& IsBattlerTurnDamaged(gBattlerTarget)
&& !CanBattlerAvoidContactEffects(gBattlerAttacker, gBattlerTarget, GetBattlerAbility(gBattlerAttacker), GetBattlerHoldEffect(gBattlerAttacker), move))
@ -5178,7 +5180,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab
case ABILITY_STEAM_ENGINE:
if (IsBattlerTurnDamaged(gBattlerTarget)
&& IsBattlerAlive(battler)
&& CompareStat(battler, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN)
&& CompareStat(battler, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility)
&& (moveType == TYPE_FIRE || moveType == TYPE_WATER))
{
gEffectBattler = gBattlerAbility = battler;
@ -5260,7 +5262,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab
case ABILITY_THERMAL_EXCHANGE:
if (IsBattlerTurnDamaged(gBattlerTarget)
&& IsBattlerAlive(gBattlerTarget)
&& CompareStat(gBattlerTarget, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN)
&& CompareStat(gBattlerTarget, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility)
&& moveType == TYPE_FIRE)
{
gEffectBattler = gBattlerAbility = gBattlerTarget;
@ -9052,17 +9054,46 @@ bool32 CanBattlerFormChange(u32 battler, enum FormChanges method)
if (gBattleMons[battler].volatiles.transformed
&& B_TRANSFORM_FORM_CHANGES >= GEN_5)
return FALSE;
// Mega Evolved and Ultra Bursted Pokémon should always revert to normal upon fainting or ending the battle.
if ((IsBattlerMegaEvolved(battler) || IsBattlerUltraBursted(battler) || IsBattlerInTeraForm(battler)) && (method == FORM_CHANGE_FAINT || method == FORM_CHANGE_END_BATTLE))
return TRUE;
else if (IsBattlerPrimalReverted(battler) && (method == FORM_CHANGE_END_BATTLE))
return TRUE;
// Gigantamaxed Pokemon should revert upon fainting, switching, or ending the battle.
else if (IsGigantamaxed(battler) && (method == FORM_CHANGE_FAINT || method == FORM_CHANGE_BATTLE_SWITCH || method == FORM_CHANGE_END_BATTLE))
return TRUE;
switch (method)
{
case FORM_CHANGE_END_BATTLE:
if (IsBattlerPrimalReverted(battler))
return TRUE;
// Fallthrough
case FORM_CHANGE_FAINT:
if (IsBattlerMegaEvolved(battler) || IsBattlerUltraBursted(battler) || IsBattlerInTeraForm(battler) || IsGigantamaxed(battler))
return TRUE;
break;
case FORM_CHANGE_BATTLE_SWITCH:
if (IsGigantamaxed(battler))
return TRUE;
else if (GetActiveGimmick(battler) == GIMMICK_TERA && GetBattlerAbility(battler) == ABILITY_HUNGER_SWITCH)
return FALSE;
break;
default:
break;
}
return DoesSpeciesHaveFormChangeMethod(gBattleMons[battler].species, method);
}
bool32 TryRevertPartyMonFormChange(u32 partyIndex)
{
bool32 changedForm = FALSE;
// Appeared in battle and didn't faint
if (gBattleStruct->partyState[B_SIDE_PLAYER][partyIndex].sentOut && GetMonData(&gPlayerParty[partyIndex], MON_DATA_HP, NULL) != 0)
changedForm = TryFormChange(partyIndex, B_SIDE_PLAYER, FORM_CHANGE_END_BATTLE_ENVIRONMENT);
if (!changedForm)
changedForm = TryFormChange(partyIndex, B_SIDE_PLAYER, FORM_CHANGE_END_BATTLE);
// Clear original species field
gBattleStruct->partyState[B_SIDE_PLAYER][partyIndex].changedSpecies = SPECIES_NONE;
return changedForm;
}
bool32 TryBattleFormChange(u32 battler, enum FormChanges method)
{
u32 monId = gBattlerPartyIndexes[battler];
@ -9076,7 +9107,7 @@ bool32 TryBattleFormChange(u32 battler, enum FormChanges method)
targetSpecies = GetBattleFormChangeTargetSpecies(battler, method);
if (targetSpecies == currentSpecies)
targetSpecies = GetFormChangeTargetSpecies(&party[monId], method, 0);
if (targetSpecies != currentSpecies)
if (targetSpecies != currentSpecies && targetSpecies != SPECIES_NONE)
{
// Saves the original species on the first form change.
@ -9093,17 +9124,22 @@ bool32 TryBattleFormChange(u32 battler, enum FormChanges method)
{
bool32 restoreSpecies = FALSE;
// Mega Evolved and Ultra Bursted Pokémon should always revert to normal upon fainting or ending the battle, so no need to add it to the form change tables.
if ((IsBattlerMegaEvolved(battler) || IsBattlerUltraBursted(battler) || IsBattlerInTeraForm(battler)) && (method == FORM_CHANGE_FAINT || method == FORM_CHANGE_END_BATTLE))
restoreSpecies = TRUE;
// Unlike Megas, Primal Reversion isn't canceled on fainting.
else if (IsBattlerPrimalReverted(battler) && (method == FORM_CHANGE_END_BATTLE))
restoreSpecies = TRUE;
// Gigantamax Pokemon have their forms reverted after fainting, switching, or ending the battle.
else if (IsGigantamaxed(battler) && (method == FORM_CHANGE_FAINT || method == FORM_CHANGE_BATTLE_SWITCH || method == FORM_CHANGE_END_BATTLE))
switch (method)
{
case FORM_CHANGE_END_BATTLE:
restoreSpecies = TRUE;
break;
case FORM_CHANGE_FAINT:
if (IsBattlerMegaEvolved(battler) || IsBattlerUltraBursted(battler) || IsBattlerInTeraForm(battler) || IsGigantamaxed(battler))
restoreSpecies = TRUE;
break;
case FORM_CHANGE_BATTLE_SWITCH:
if (IsGigantamaxed(battler))
restoreSpecies = TRUE;
break;
default:
break;
}
if (restoreSpecies)
{
@ -9674,14 +9710,14 @@ bool32 TestIfSheerForceAffected(u32 battler, u16 move)
}
// This function is the body of "jumpifstat", but can be used dynamically in a function
bool32 CompareStat(u32 battler, enum Stat statId, u8 cmpTo, u8 cmpKind)
bool32 CompareStat(u32 battler, enum Stat statId, u8 cmpTo, u8 cmpKind, enum Ability ability)
{
bool32 ret = FALSE;
u8 statValue = gBattleMons[battler].statStages[statId];
// Because this command is used as a way of checking if a stat can be lowered/raised,
// we need to do some modification at run-time.
if (GetBattlerAbility(battler) == ABILITY_CONTRARY)
if (ability == ABILITY_CONTRARY)
{
if (cmpKind == CMP_GREATER_THAN)
cmpKind = CMP_LESS_THAN;
@ -10772,6 +10808,21 @@ bool32 HasPartnerTrainer(u32 battler)
return FALSE;
}
static bool32 IsOpposingSideEmpty(u32 battler)
{
u32 oppositeBattler = BATTLE_OPPOSITE(battler);
if (IsBattlerAlive(oppositeBattler))
return FALSE;
if (!IsDoubleBattle())
return TRUE;
if (IsBattlerAlive(BATTLE_PARTNER(oppositeBattler)))
return FALSE;
return TRUE;
}
static u32 GetMirrorMoveMove(void)
{
s32 i, validMovesCount;

View File

@ -128,7 +128,7 @@ static void SetUpItemUseCallback(u8 taskId)
type = gTasks[taskId].tEnigmaBerryType - 1;
else
type = GetItemType(gSpecialVar_ItemId) - 1;
if (gTasks[taskId].tUsingRegisteredKeyItem && type == (ITEM_USE_PARTY_MENU - 1))
{
FadeScreen(FADE_TO_BLACK, 0);
@ -1306,15 +1306,18 @@ bool32 CannotUseItemsInBattle(u16 itemId, struct Pokemon *mon)
}
break;
case EFFECT_ITEM_INCREASE_ALL_STATS:
{
u32 ability = GetBattlerAbility(gBattlerInMenuId);
for (i = STAT_ATK; i < NUM_STATS; i++)
{
if (CompareStat(gBattlerInMenuId, i, MAX_STAT_STAGE, CMP_EQUAL))
if (CompareStat(gBattlerInMenuId, i, MAX_STAT_STAGE, CMP_EQUAL, ability))
{
cannotUse = TRUE;
break;
}
}
break;
}
case EFFECT_ITEM_RESTORE_HP:
if (hp == 0 || hp == GetMonData(mon, MON_DATA_MAX_HP))
cannotUse = TRUE;

View File

@ -1511,7 +1511,7 @@ static void HandleChooseMonSelection(u8 taskId, s8 *slotPtr)
}
case PARTY_ACTION_SEND_MON_TO_BOX:
{
u8 partyId = GetPartyIdFromBattleSlot((u8)*slotPtr);
u8 partyId = (u8)*slotPtr;
if (partyId == 0 || ((gBattleTypeFlags & BATTLE_TYPE_DOUBLE) && partyId == 1))
{
// Can't select if mon is currently on the field
@ -1538,7 +1538,7 @@ static void HandleChooseMonSelection(u8 taskId, s8 *slotPtr)
else
{
PlaySE(SE_SELECT);
gSelectedMonPartyId = partyId;
gSelectedMonPartyId = GetPartyIdFromBattleSlot(partyId);
Task_ClosePartyMenu(taskId);
}
break;

View File

@ -399,6 +399,7 @@ bool8 CheckForTrainersWantingBattle(void)
if (numTrainers == 0xFF) // non-trainerbattle script
{
u32 objectEventId = gApproachingTrainers[gNoOfApproachingTrainers - 1].objectEventId;
gApproachingTrainers[gNoOfApproachingTrainers - 1].trainerScriptPtr = GetObjectEventScriptPointerByObjectEventId(objectEventId);
gSelectedObjectEvent = objectEventId;
gSpecialVar_LastTalked = gObjectEvents[objectEventId].localId;
ScriptContext_SetupScript(EventScript_ObjectApproachPlayer);

View File

@ -53,7 +53,6 @@ SINGLE_BATTLE_TEST("Hunger Switch does not switch Morpeko's form when Terastalli
SINGLE_BATTLE_TEST("Hunger Switch does not switch Morpeko's form after switching out while Terastallized")
{
KNOWN_FAILING; // #7062
GIVEN {
ASSUME(GetMoveEffect(MOVE_ROAR) == EFFECT_ROAR);
PLAYER(SPECIES_MORPEKO) { Ability(ABILITY_HUNGER_SWITCH); TeraType(TYPE_NORMAL); }

View File

@ -240,6 +240,7 @@ DOUBLE_BATTLE_TEST("Intimidate is not going to trigger if a mon switches out thr
ANIMATION(ANIM_TYPE_MOVE, MOVE_HEALING_WISH, opponentRight);
ANIMATION(ANIM_TYPE_MOVE, MOVE_U_TURN, playerLeft);
HP_BAR(opponentLeft);
NOT ABILITY_POPUP(playerLeft, ABILITY_INTIMIDATE);
MESSAGE("2 sent out Treecko!");
MESSAGE("2 sent out Torchic!");
NOT ABILITY_POPUP(playerLeft, ABILITY_INTIMIDATE);
@ -264,7 +265,7 @@ SINGLE_BATTLE_TEST("Intimidate activates when it's no longer effected by Neutral
}
}
SINGLE_BATTLE_TEST("Intimidate activates when it's no longer affected by Neutralizing Gas - switching moves")
DOUBLE_BATTLE_TEST("Intimidate activates when it's no longer affected by Neutralizing Gas - switching moves")
{
u32 move;
PARAMETRIZE { move = MOVE_U_TURN; }
@ -276,19 +277,24 @@ SINGLE_BATTLE_TEST("Intimidate activates when it's no longer affected by Neutral
ASSUME(GetMoveEffect(MOVE_BATON_PASS) == EFFECT_BATON_PASS);
PLAYER(SPECIES_WEEZING) { Ability(ABILITY_NEUTRALIZING_GAS); }
PLAYER(SPECIES_WOBBUFFET) { HP(1); }
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_ARBOK) { Ability(ABILITY_INTIMIDATE); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, move); SEND_OUT(player, 1); }
TURN {
if (move == MOVE_U_TURN)
MOVE(playerLeft, move, target: opponentLeft);
else
MOVE(playerLeft, move);
SEND_OUT(playerLeft, 2);
}
} SCENE {
ABILITY_POPUP(player, ABILITY_NEUTRALIZING_GAS);
ABILITY_POPUP(playerLeft, ABILITY_NEUTRALIZING_GAS);
MESSAGE("Neutralizing gas filled the area!");
ANIMATION(ANIM_TYPE_MOVE, move, player);
ANIMATION(ANIM_TYPE_MOVE, move, playerLeft);
MESSAGE("The effects of the neutralizing gas wore off!");
ABILITY_POPUP(opponent, ABILITY_INTIMIDATE);
ABILITY_POPUP(opponentLeft, ABILITY_INTIMIDATE);
SEND_IN_MESSAGE("Wobbuffet");
} THEN {
if (move == MOVE_HEALING_WISH)
EXPECT_EQ(player->hp, player->maxHP);
}
}
@ -330,23 +336,25 @@ SINGLE_BATTLE_TEST("Intimidate activates when it's no longer affected by Neutral
}
}
SINGLE_BATTLE_TEST("Intimidate activates when it's no longer affected by Neutralizing Gas - fainted")
DOUBLE_BATTLE_TEST("Intimidate activates when it's no longer affected by Neutralizing Gas - fainted")
{
GIVEN {
ASSUME(GetMoveEffect(MOVE_FELL_STINGER) == EFFECT_FELL_STINGER);
PLAYER(SPECIES_WEEZING) { Ability(ABILITY_NEUTRALIZING_GAS); HP(1); }
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_ARBOK) { Ability(ABILITY_INTIMIDATE); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_FELL_STINGER); SEND_OUT(player, 1); }
TURN { MOVE(opponentLeft, MOVE_FELL_STINGER, target: playerLeft); SEND_OUT(playerLeft, 2); }
} SCENE {
ABILITY_POPUP(player, ABILITY_NEUTRALIZING_GAS);
ABILITY_POPUP(playerLeft, ABILITY_NEUTRALIZING_GAS);
MESSAGE("Neutralizing gas filled the area!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_FELL_STINGER, opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_FELL_STINGER, opponentLeft);
MESSAGE("The effects of the neutralizing gas wore off!");
ABILITY_POPUP(opponent, ABILITY_INTIMIDATE);
ABILITY_POPUP(opponentLeft, ABILITY_INTIMIDATE);
MESSAGE("Weezing fainted!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft);
SEND_IN_MESSAGE("Wobbuffet");
}
}

View File

@ -2,3 +2,56 @@
#include "test/battle.h"
TO_DO_BATTLE_TEST("TODO: Write Power Construct (Ability) test titles")
SINGLE_BATTLE_TEST("Power Construct switches Zygarde's form when HP is below half")
{
u16 baseSpecies;
PARAMETRIZE { baseSpecies = SPECIES_ZYGARDE_10_POWER_CONSTRUCT; }
PARAMETRIZE { baseSpecies = SPECIES_ZYGARDE_50_POWER_CONSTRUCT; }
GIVEN {
PLAYER(baseSpecies)
{
Ability(ABILITY_POWER_CONSTRUCT);
HP((GetMonData(&PLAYER_PARTY[0], MON_DATA_MAX_HP) / 2) + 1);
}
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_SCRATCH); MOVE(player, MOVE_CELEBRATE); }
} SCENE {
MESSAGE("You sense the presence of many!");
ABILITY_POPUP(player, ABILITY_POWER_CONSTRUCT);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_POWER_CONSTRUCT, player);
} THEN {
EXPECT_EQ(player->species, SPECIES_ZYGARDE_COMPLETE);
}
}
WILD_BATTLE_TEST("Power Construct Zygarde reverts to its original form upon catching")
{
u16 baseSpecies;
PARAMETRIZE { baseSpecies = SPECIES_ZYGARDE_10_POWER_CONSTRUCT; }
PARAMETRIZE { baseSpecies = SPECIES_ZYGARDE_50_POWER_CONSTRUCT; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(baseSpecies)
{
Ability(ABILITY_POWER_CONSTRUCT);
HP((GetMonData(&OPPONENT_PARTY[0], MON_DATA_MAX_HP) / 2) + 1);
}
} WHEN {
TURN { MOVE(player, MOVE_SCRATCH); }
TURN { USE_ITEM(player, ITEM_MASTER_BALL); }
} SCENE {
// Turn 1
MESSAGE("You sense the presence of many!");
ABILITY_POPUP(opponent, ABILITY_POWER_CONSTRUCT);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_POWER_CONSTRUCT, opponent);
// Turn 2
ANIMATION(ANIM_TYPE_SPECIAL, B_ANIM_BALL_THROW, player);
} THEN {
EXPECT_EQ(GetMonData(&gPlayerParty[1], MON_DATA_SPECIES), baseSpecies);
}
}

View File

@ -8,7 +8,6 @@ ASSUMPTIONS
ASSUME(MoveMakesContact(MOVE_SCRATCH) == TRUE);
}
SINGLE_BATTLE_TEST("Tangling Hair drops opposing mon's speed if ability user got hit by a contact move")
{
u32 move;
@ -85,3 +84,16 @@ SINGLE_BATTLE_TEST("Tangling Hair does not activate on confusion damage")
}
}
}
SINGLE_BATTLE_TEST("Tangling Hair does not trigger on Clear Body")
{
GIVEN {
PLAYER(SPECIES_DUGTRIO) { Ability(ABILITY_TANGLING_HAIR); }
OPPONENT(SPECIES_BELDUM) { Ability(ABILITY_CLEAR_BODY); };
} WHEN {
TURN { MOVE(opponent, MOVE_SCRATCH); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, opponent);
NOT ABILITY_POPUP(player, ABILITY_TANGLING_HAIR);
}
}

View File

@ -1,9 +1,24 @@
#include "global.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Aegislash reverts to Shield Form upon fainting")
SINGLE_BATTLE_TEST("Aegislash reverts to Shield Form upon fainting (start as Shield)")
{
GIVEN {
PLAYER(SPECIES_AEGISLASH_SHIELD) { HP(1); }
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_SCRATCH); MOVE(opponent, MOVE_GUST); SEND_OUT(player, 1); }
} SCENE {
MESSAGE("The opposing Wobbuffet used Gust!");
MESSAGE("Aegislash fainted!");
} THEN {
EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_SPECIES), SPECIES_AEGISLASH_SHIELD);
}
}
SINGLE_BATTLE_TEST("Aegislash reverts to Shield Form upon fainting (start as Blade)")
{
KNOWN_FAILING;
GIVEN {
PLAYER(SPECIES_AEGISLASH_BLADE) { HP(1); }
PLAYER(SPECIES_WOBBUFFET);
@ -14,7 +29,7 @@ SINGLE_BATTLE_TEST("Aegislash reverts to Shield Form upon fainting")
MESSAGE("The opposing Wobbuffet used Gust!");
MESSAGE("Aegislash fainted!");
} THEN {
EXPECT_EQ(GetMonData(&PLAYER_PARTY[0], MON_DATA_SPECIES), SPECIES_AEGISLASH_SHIELD);
EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_SPECIES), SPECIES_AEGISLASH_SHIELD);
}
}

View File

@ -159,7 +159,6 @@ SINGLE_BATTLE_TEST("Belly Drum maximizes the user's Attack stat, even when below
SINGLE_BATTLE_TEST("Belly Drum fails if the user's Attack is already at +6, even with Contrary")
{
KNOWN_FAILING;
GIVEN {
ASSUME(GetMoveEffect(MOVE_CHARM) == EFFECT_ATTACK_DOWN_2);
PLAYER(SPECIES_SERPERIOR) { Ability(ABILITY_CONTRARY); }
@ -190,7 +189,6 @@ SINGLE_BATTLE_TEST("Belly Drum fails if the user's Attack is already at +6, even
SINGLE_BATTLE_TEST("Belly Drum deducts HP if the user has Contrary and is at -6")
{
KNOWN_FAILING;
GIVEN {
ASSUME(GetMoveEffect(MOVE_SWORDS_DANCE) == EFFECT_ATTACK_UP_2);
PLAYER(SPECIES_SERPERIOR) { Ability(ABILITY_CONTRARY); }
@ -215,6 +213,6 @@ SINGLE_BATTLE_TEST("Belly Drum deducts HP if the user has Contrary and is at -6"
ANIMATION(ANIM_TYPE_MOVE, MOVE_BELLY_DRUM, player);
s32 maxHP = GetMonData(&PLAYER_PARTY[0], MON_DATA_MAX_HP);
HP_BAR(player, hp: maxHP / 2);
MESSAGE("Wobbuffet cut its own HP and maximized its Attack!");
MESSAGE("Serperior cut its own HP and maximized its Attack!");
}
}

View File

@ -113,7 +113,7 @@ SINGLE_BATTLE_TEST("Hit Escape: U-turn switches the user out after Ice Face acti
}
}
SINGLE_BATTLE_TEST("Hit Escape: Held items are consumed immediately after a mon switched in by U-turn and Intimidate activates after it: player side")
SINGLE_BATTLE_TEST("Hit Escape: Held items are consumed immediately after a mon switched in by U-turn: player side")
{
GIVEN {
PLAYER(SPECIES_TAPU_KOKO) { Ability(ABILITY_ELECTRIC_SURGE); };
@ -126,7 +126,6 @@ SINGLE_BATTLE_TEST("Hit Escape: Held items are consumed immediately after a mon
ABILITY_POPUP(player, ABILITY_ELECTRIC_SURGE);
ANIMATION(ANIM_TYPE_MOVE, MOVE_U_TURN, player);
HP_BAR(opponent);
ABILITY_POPUP(player, ABILITY_INTIMIDATE);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
MESSAGE("2 sent out Wynaut!");
@ -136,7 +135,7 @@ SINGLE_BATTLE_TEST("Hit Escape: Held items are consumed immediately after a mon
}
}
SINGLE_BATTLE_TEST("Hit Escape: Held items are consumed immediately after a mon switched in by U-turn and Intimidate activates after it: opposing side")
SINGLE_BATTLE_TEST("Hit Escape: Held items are consumed immediately after a mon switched in by U-turn: opposing side")
{
GIVEN {
PLAYER(SPECIES_TAPU_KOKO) { Ability(ABILITY_ELECTRIC_SURGE); };
@ -149,7 +148,6 @@ SINGLE_BATTLE_TEST("Hit Escape: Held items are consumed immediately after a mon
ABILITY_POPUP(player, ABILITY_ELECTRIC_SURGE);
ANIMATION(ANIM_TYPE_MOVE, MOVE_U_TURN, player);
HP_BAR(opponent);
ABILITY_POPUP(player, ABILITY_INTIMIDATE);
MESSAGE("2 sent out Wynaut!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);