Fix various stuff not working when hitting substitutes (#9487)
Some checks are pending
CI / build (push) Waiting to run
CI / docs_validate (push) Waiting to run
CI / allcontributors (push) Waiting to run
Docs / deploy (push) Waiting to run

Co-authored-by: Hedara <hedara90@gmail.com>
This commit is contained in:
hedara90 2026-03-10 18:33:58 +01:00 committed by GitHub
parent df81e254ab
commit 44c132a752
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 407 additions and 77 deletions

View File

@ -1067,9 +1067,15 @@ static inline bool32 IsBattlerAlive(enum BattlerId battler)
return TRUE;
}
static inline bool32 IsBattlerTurnDamaged(enum BattlerId battler)
enum SubCheck
{
return gSpecialStatuses[battler].damagedByAttack;
EXCLUDING_SUBSTITUTES,
INCLUDING_SUBSTITUTES
};
static inline bool32 IsBattlerTurnDamaged(enum BattlerId battler, enum SubCheck subCheck)
{
return gSpecialStatuses[battler].damagedByAttack || ((subCheck == INCLUDING_SUBSTITUTES) && gBattleStruct->moveDamage[battler] > 0);
}
static inline bool32 IsBattlerAtMaxHp(enum BattlerId battler)

View File

@ -189,7 +189,7 @@ static enum ItemEffect TryKingsRock(enum BattlerId battlerAtk, enum BattlerId ba
enum ItemEffect effect = ITEM_NO_EFFECT;
if (!IsBattlerAlive(battlerDef)
|| !IsBattlerTurnDamaged(battlerDef)
|| !IsBattlerTurnDamaged(battlerDef, EXCLUDING_SUBSTITUTES)
|| MoveIgnoresKingsRock(gCurrentMove)
|| MoveHasAdditionalEffect(gCurrentMove, MOVE_EFFECT_FLINCH))
return effect;
@ -216,7 +216,7 @@ static enum ItemEffect TryAirBalloon(enum BattlerId battler, ActivationTiming ti
if (timing == IsOnTargetHitActivation)
{
if (IsBattlerTurnDamaged(battler))
if (IsBattlerTurnDamaged(battler, EXCLUDING_SUBSTITUTES))
{
BattleScriptCall(BattleScript_AirBalloonMsgPop);
effect = ITEM_EFFECT_OTHER;
@ -237,7 +237,7 @@ static enum ItemEffect TryRockyHelmet(enum BattlerId battlerDef, enum BattlerId
enum ItemEffect effect = ITEM_NO_EFFECT;
enum Ability ability = GetBattlerAbility(battlerAtk);
if (IsBattlerTurnDamaged(battlerDef)
if (IsBattlerTurnDamaged(battlerDef, EXCLUDING_SUBSTITUTES)
&& IsBattlerAlive(battlerAtk)
&& !CanBattlerAvoidContactEffects(battlerAtk, battlerDef, ability, GetBattlerHoldEffect(battlerAtk), gCurrentMove)
&& !IsAbilityAndRecord(battlerAtk, ability, ABILITY_MAGIC_GUARD))
@ -256,7 +256,7 @@ static enum ItemEffect TryWeaknessPolicy(enum BattlerId battlerDef)
enum ItemEffect effect = ITEM_NO_EFFECT;
if (IsBattlerAlive(battlerDef)
&& IsBattlerTurnDamaged(battlerDef)
&& IsBattlerTurnDamaged(battlerDef, EXCLUDING_SUBSTITUTES)
&& gBattleStruct->moveResultFlags[battlerDef] & MOVE_RESULT_SUPER_EFFECTIVE)
{
BattleScriptCall(BattleScript_WeaknessPolicy);
@ -271,7 +271,7 @@ static enum ItemEffect TrySnowball(enum BattlerId battlerDef)
enum ItemEffect effect = ITEM_NO_EFFECT;
if (IsBattlerAlive(battlerDef)
&& IsBattlerTurnDamaged(battlerDef)
&& IsBattlerTurnDamaged(battlerDef, EXCLUDING_SUBSTITUTES)
&& GetBattleMoveType(gCurrentMove) == TYPE_ICE)
{
BattleScriptCall(BattleScript_TargetItemStatRaise);
@ -287,7 +287,7 @@ static enum ItemEffect TryLuminousMoss(enum BattlerId battlerDef)
enum ItemEffect effect = ITEM_NO_EFFECT;
if (IsBattlerAlive(battlerDef)
&& IsBattlerTurnDamaged(battlerDef)
&& IsBattlerTurnDamaged(battlerDef, EXCLUDING_SUBSTITUTES)
&& GetBattleMoveType(gCurrentMove) == TYPE_WATER)
{
BattleScriptCall(BattleScript_TargetItemStatRaise);
@ -303,7 +303,7 @@ static enum ItemEffect TryCellBattery(enum BattlerId battlerDef)
enum ItemEffect effect = ITEM_NO_EFFECT;
if (IsBattlerAlive(battlerDef)
&& IsBattlerTurnDamaged(battlerDef)
&& IsBattlerTurnDamaged(battlerDef, EXCLUDING_SUBSTITUTES)
&& GetBattleMoveType(gCurrentMove) == TYPE_ELECTRIC)
{
BattleScriptCall(BattleScript_TargetItemStatRaise);
@ -319,7 +319,7 @@ static enum ItemEffect TryAbsorbBulb(enum BattlerId battlerDef)
enum ItemEffect effect = ITEM_NO_EFFECT;
if (IsBattlerAlive(battlerDef)
&& IsBattlerTurnDamaged(battlerDef)
&& IsBattlerTurnDamaged(battlerDef, EXCLUDING_SUBSTITUTES)
&& GetBattleMoveType(gCurrentMove) == TYPE_WATER)
{
effect = ITEM_STATS_CHANGE;
@ -335,7 +335,7 @@ static enum ItemEffect TryJabocaBerry(enum BattlerId battlerDef, enum BattlerId
enum ItemEffect effect = ITEM_NO_EFFECT;
if (IsBattlerAlive(battlerAtk)
&& IsBattlerTurnDamaged(battlerDef)
&& IsBattlerTurnDamaged(battlerDef, EXCLUDING_SUBSTITUTES)
&& !DoesSubstituteBlockMove(battlerAtk, battlerDef, gCurrentMove)
&& IsBattleMovePhysical(gCurrentMove)
&& !IsAbilityAndRecord(battlerAtk, GetBattlerAbility(battlerAtk), ABILITY_MAGIC_GUARD))
@ -357,7 +357,7 @@ static enum ItemEffect TryRowapBerry(enum BattlerId battlerDef, enum BattlerId b
enum ItemEffect effect = ITEM_NO_EFFECT;
if (IsBattlerAlive(battlerAtk)
&& IsBattlerTurnDamaged(battlerDef)
&& IsBattlerTurnDamaged(battlerDef, EXCLUDING_SUBSTITUTES)
&& !DoesSubstituteBlockMove(battlerAtk, battlerDef, gCurrentMove)
&& IsBattleMoveSpecial(gCurrentMove)
&& !IsAbilityAndRecord(battlerAtk, GetBattlerAbility(battlerAtk), ABILITY_MAGIC_GUARD))
@ -380,7 +380,7 @@ static enum ItemEffect TrySetEnigmaBerry(enum BattlerId battlerDef, enum Battler
if (IsBattlerAlive(battlerDef)
&& !DoesSubstituteBlockMove(battlerAtk, battlerDef, gCurrentMove)
&& ((IsBattlerTurnDamaged(battlerDef) && gBattleStruct->moveResultFlags[battlerDef] & MOVE_RESULT_SUPER_EFFECTIVE) || gBattleScripting.overrideBerryRequirements)
&& ((IsBattlerTurnDamaged(battlerDef, EXCLUDING_SUBSTITUTES) && gBattleStruct->moveResultFlags[battlerDef] & MOVE_RESULT_SUPER_EFFECTIVE) || gBattleScripting.overrideBerryRequirements)
&& !(gBattleScripting.overrideBerryRequirements && gBattleMons[battlerDef].hp == gBattleMons[battlerDef].maxHP)
&& !(B_HEAL_BLOCKING >= GEN_5 && gBattleMons[battlerDef].volatiles.healBlock))
{
@ -502,7 +502,7 @@ static enum ItemEffect DamagedStatBoostBerryEffect(enum BattlerId battlerDef, en
if (gBattleScripting.overrideBerryRequirements
|| (!DoesSubstituteBlockMove(battlerAtk, battlerDef, gCurrentMove)
&& GetBattleMoveCategory(gCurrentMove) == category
&& IsBattlerTurnDamaged(battlerDef)))
&& IsBattlerTurnDamaged(battlerDef, EXCLUDING_SUBSTITUTES)))
{
if (GetBattlerAbility(battlerDef) == ABILITY_RIPEN)
SET_STATCHANGER(statId, 2, FALSE);
@ -562,7 +562,7 @@ static enum ItemEffect TryStickyBarbOnTargetHit(enum BattlerId battlerDef, enum
{
enum ItemEffect effect = ITEM_NO_EFFECT;
if (IsBattlerTurnDamaged(battlerDef)
if (IsBattlerTurnDamaged(battlerDef, EXCLUDING_SUBSTITUTES)
&& !CanBattlerAvoidContactEffects(battlerAtk, battlerDef, GetBattlerAbility(battlerAtk), GetBattlerHoldEffect(battlerAtk), gCurrentMove)
&& !DoesSubstituteBlockMove(battlerAtk, battlerDef, gCurrentMove)
&& IsBattlerAlive(battlerAtk)

View File

@ -2141,7 +2141,7 @@ static enum MoveEndResult MoveEndProtectLikeEffect(void)
// Not strictly a protect effect, but works the same way
if (IsBattlerUsingBeakBlast(gBattlerTarget)
&& IsBattlerTurnDamaged(gBattlerTarget)
&& IsBattlerTurnDamaged(gBattlerTarget, EXCLUDING_SUBSTITUTES)
&& CanBeBurned(gBattlerAttacker, gBattlerAttacker, GetBattlerAbility(gBattlerAttacker)))
{
gBattleMons[gBattlerAttacker].status1 = STATUS1_BURN;
@ -2208,7 +2208,7 @@ static enum MoveEndResult MoveEndAbsorb(void)
case EFFECT_ABSORB:
case EFFECT_DREAM_EATER:
if (gBattleStruct->moveDamage[gBattlerTarget] > 0
&& IsBattlerTurnDamaged(gBattlerTarget)
&& IsBattlerTurnDamaged(gBattlerTarget, INCLUDING_SUBSTITUTES)
&& IsBattlerAlive(gBattlerAttacker))
{
s32 healAmount = (gBattleStruct->moveDamage[gBattlerTarget] * GetMoveAbsorbPercentage(gCurrentMove) / 100);
@ -2248,7 +2248,7 @@ static enum MoveEndResult MoveEndRage(void)
&& IsBattlerAlive(gBattlerTarget)
&& gBattlerAttacker != gBattlerTarget
&& !IsBattlerAlly(gBattlerAttacker, gBattlerTarget)
&& IsBattlerTurnDamaged(gBattlerTarget)
&& IsBattlerTurnDamaged(gBattlerTarget, EXCLUDING_SUBSTITUTES)
&& !IsBattleMoveStatus(gCurrentMove)
&& CompareStat(gBattlerTarget, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN, GetBattlerAbility(gBattlerTarget)))
{
@ -2520,7 +2520,7 @@ static enum MoveEndResult MoveEndFaintBlock(void)
break;
case FAINT_BLOCK_TRY_DESTINY_BOND: // Checked before FAINT_BLOCK_FAINT_TARGET but occurs after since volatiles are cleared on faint
if (gBattleMons[gBattlerTarget].volatiles.destinyBond
&& IsBattlerTurnDamaged(gBattlerTarget)
&& IsBattlerTurnDamaged(gBattlerTarget, EXCLUDING_SUBSTITUTES)
&& IsBattlerAlive(gBattlerAttacker)
&& GetActiveGimmick(gBattlerAttacker) != GIMMICK_DYNAMAX
&& !IsBattlerAlly(gBattlerAttacker, gBattlerTarget))
@ -2531,7 +2531,7 @@ static enum MoveEndResult MoveEndFaintBlock(void)
break;
case FAINT_BLOCK_TRY_GRUDGE: // Checked before FAINT_BLOCK_FAINT_TARGET but occurs after since volatiles are cleared on faint
if (gBattleMons[gBattlerTarget].volatiles.grudge
&& IsBattlerTurnDamaged(gBattlerTarget)
&& IsBattlerTurnDamaged(gBattlerTarget, EXCLUDING_SUBSTITUTES)
&& IsBattlerAlive(gBattlerAttacker)
&& !IsBattlerAlly(gBattlerAttacker, gBattlerTarget)
&& !IsZMove(gCurrentMove)
@ -2882,7 +2882,7 @@ static enum MoveEndResult MoveEndDefrost(void)
continue;
if (!(gBattleMons[battler].status1 & STATUS1_ICY_ANY)
|| !IsBattlerTurnDamaged(battler)
|| !IsBattlerTurnDamaged(battler, EXCLUDING_SUBSTITUTES)
|| !IsBattlerAlive(battler))
continue;
@ -2947,7 +2947,7 @@ static enum MoveEndResult MoveEndMoveBlock(void)
if (gBattleMons[gBattlerTarget].item != ITEM_NONE
&& IsBattlerAlive(gBattlerAttacker)
&& !(B_KNOCK_OFF_REMOVAL >= GEN_5 && side == B_SIDE_PLAYER && !(gBattleTypeFlags & BATTLE_TYPE_TRAINER))
&& IsBattlerTurnDamaged(gBattlerTarget)
&& IsBattlerTurnDamaged(gBattlerTarget, EXCLUDING_SUBSTITUTES)
&& !DoesSubstituteBlockMove(gBattlerAttacker, gBattlerTarget, gCurrentMove)
&& CanBattlerGetOrLoseItem(gBattlerTarget, gBattlerAttacker, gBattleMons[gBattlerTarget].item)
&& !NoAliveMonsForEitherParty())
@ -2986,7 +2986,7 @@ static enum MoveEndResult MoveEndMoveBlock(void)
}
break;
case EFFECT_STEAL_ITEM:
if (!IsBattlerTurnDamaged(gBattlerTarget)
if (!IsBattlerTurnDamaged(gBattlerTarget, EXCLUDING_SUBSTITUTES)
|| gBattleMons[gBattlerAttacker].item != ITEM_NONE
|| gBattleMons[gBattlerTarget].item == ITEM_NONE
|| !IsBattlerAlive(gBattlerAttacker)
@ -3016,7 +3016,7 @@ static enum MoveEndResult MoveEndMoveBlock(void)
}
break;
case EFFECT_HIT_SWITCH_TARGET:
if (IsBattlerTurnDamaged(gBattlerTarget)
if (IsBattlerTurnDamaged(gBattlerTarget, EXCLUDING_SUBSTITUTES)
&& IsBattlerAlive(gBattlerTarget)
&& IsBattlerAlive(gBattlerAttacker)
&& gBattleMons[BATTLE_PARTNER(gBattlerTarget)].volatiles.semiInvulnerable != STATE_COMMANDER)
@ -3047,7 +3047,7 @@ static enum MoveEndResult MoveEndMoveBlock(void)
break;
case EFFECT_SMACK_DOWN:
if (!IsBattlerGrounded(gBattlerTarget, GetBattlerAbility(gBattlerTarget), GetBattlerHoldEffect(gBattlerTarget))
&& IsBattlerTurnDamaged(gBattlerTarget)
&& IsBattlerTurnDamaged(gBattlerTarget, EXCLUDING_SUBSTITUTES)
&& IsBattlerAlive(gBattlerTarget)
&& !DoesSubstituteBlockMove(gBattlerAttacker, gBattlerTarget, gCurrentMove))
{
@ -3094,7 +3094,7 @@ static enum MoveEndResult MoveEndMoveBlock(void)
}
break;
case EFFECT_RECOIL:
if (IsBattlerTurnDamaged(gBattlerTarget) && IsBattlerAlive(gBattlerAttacker) && gBattleStruct->moveDamage[gBattlerTarget] > 0)
if (IsBattlerTurnDamaged(gBattlerTarget, INCLUDING_SUBSTITUTES) && IsBattlerAlive(gBattlerAttacker) && gBattleStruct->moveDamage[gBattlerTarget] > 0)
{
enum Ability ability = GetBattlerAbility(gBattlerAttacker);
if (IsAbilityAndRecord(gBattlerAttacker, ability, ABILITY_ROCK_HEAD)
@ -3108,7 +3108,7 @@ static enum MoveEndResult MoveEndMoveBlock(void)
}
break;
case EFFECT_CHLOROBLAST:
if (IsBattlerTurnDamaged(gBattlerTarget) && IsBattlerAlive(gBattlerAttacker))
if (IsBattlerTurnDamaged(gBattlerTarget, INCLUDING_SUBSTITUTES) && IsBattlerAlive(gBattlerAttacker))
{
enum Ability ability = GetBattlerAbility(gBattlerAttacker);
if (IsAbilityAndRecord(gBattlerAttacker, ability, ABILITY_ROCK_HEAD)
@ -3123,7 +3123,7 @@ static enum MoveEndResult MoveEndMoveBlock(void)
}
break;
case EFFECT_RAPID_SPIN:
if (IsBattlerTurnDamaged(gBattlerTarget) && IsBattlerAlive(gBattlerAttacker))
if (IsBattlerTurnDamaged(gBattlerTarget, INCLUDING_SUBSTITUTES) && IsBattlerAlive(gBattlerAttacker))
{
BattleScriptCall(BattleScript_RapidSpinAway);
result = MOVEEND_RESULT_RUN_SCRIPT;
@ -3132,7 +3132,7 @@ static enum MoveEndResult MoveEndMoveBlock(void)
case EFFECT_FELL_STINGER:
if (IsBattlerAlive(gBattlerAttacker)
&& !IsBattlerAlive(gBattlerTarget)
&& IsBattlerTurnDamaged(gBattlerTarget)
&& IsBattlerTurnDamaged(gBattlerTarget, EXCLUDING_SUBSTITUTES)
&& !NoAliveMonsForEitherParty()
&& CompareStat(gBattlerAttacker, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN, GetBattlerAbility(gBattlerAttacker)))
{
@ -3145,7 +3145,7 @@ static enum MoveEndResult MoveEndMoveBlock(void)
break;
case EFFECT_STONE_AXE:
if (!IsHazardOnSide(side, HAZARDS_STEALTH_ROCK)
&& IsBattlerTurnDamaged(gBattlerTarget)
&& IsBattlerTurnDamaged(gBattlerTarget, INCLUDING_SUBSTITUTES)
&& IsBattlerAlive(gBattlerAttacker))
{
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_POINTEDSTONESFLOAT;
@ -3156,7 +3156,7 @@ static enum MoveEndResult MoveEndMoveBlock(void)
break;
case EFFECT_CEASELESS_EDGE:
if (gSideTimers[side].spikesAmount < 3
&& IsBattlerTurnDamaged(gBattlerTarget)
&& IsBattlerTurnDamaged(gBattlerTarget, INCLUDING_SUBSTITUTES)
&& IsBattlerAlive(gBattlerAttacker))
{
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SPIKESSCATTERED;
@ -3216,7 +3216,7 @@ static enum MoveEndResult MoveEndShellTrap(void)
// Set ShellTrap to activate after the attacker's turn if target was hit by a physical move.
if (GetMoveEffect(gChosenMoveByBattler[battlerDef]) == EFFECT_SHELL_TRAP
&& IsBattleMovePhysical(gCurrentMove)
&& IsBattlerTurnDamaged(battlerDef)
&& IsBattlerTurnDamaged(battlerDef, EXCLUDING_SUBSTITUTES)
&& gProtectStructs[battlerDef].physicalBattlerId == gBattlerAttacker)
{
gProtectStructs[battlerDef].shellTrap = TRUE;
@ -3265,7 +3265,7 @@ static enum MoveEndResult MoveEndKeeMarangaHpThresholdItemTarget(void)
static bool32 TryRedCard(enum BattlerId battlerAtk, enum BattlerId redCardBattler, enum Move move)
{
if (!IsBattlerAlive(redCardBattler)
|| !IsBattlerTurnDamaged(redCardBattler)
|| !IsBattlerTurnDamaged(redCardBattler, EXCLUDING_SUBSTITUTES)
|| DoesSubstituteBlockMove(battlerAtk, redCardBattler, move)
|| !CanBattlerSwitch(battlerAtk))
return FALSE;
@ -3287,7 +3287,7 @@ static bool32 TryRedCard(enum BattlerId battlerAtk, enum BattlerId redCardBattle
static bool32 TryEjectButton(enum BattlerId battlerAtk, u32 ejectButtonBattler)
{
if (!IsBattlerTurnDamaged(ejectButtonBattler)
if (!IsBattlerTurnDamaged(ejectButtonBattler, EXCLUDING_SUBSTITUTES)
|| !IsBattlerAlive(ejectButtonBattler)
|| !CanBattlerSwitch(ejectButtonBattler))
return FALSE;
@ -3434,7 +3434,7 @@ static enum MoveEndResult MoveEndHitEscape(void)
if (GetMoveEffect(gCurrentMove) == EFFECT_HIT_ESCAPE
&& !gBattleStruct->unableToUseMove
&& IsBattlerTurnDamaged(gBattlerTarget)
&& IsBattlerTurnDamaged(gBattlerTarget, INCLUDING_SUBSTITUTES)
&& IsBattlerAlive(gBattlerAttacker)
&& !NoAliveMonsForBattlerSide(gBattlerTarget))
{
@ -3463,7 +3463,7 @@ static enum MoveEndResult MoveEndPickpocket(void)
&& !IsBattlerUnaffectedByMove(battlerDef)
&& GetBattlerAbility(battlerDef) == ABILITY_PICKPOCKET
&& IsMoveMakingContact(gBattlerAttacker, battlerDef, GetBattlerAbility(gBattlerAttacker), GetBattlerHoldEffect(gBattlerAttacker), gCurrentMove)
&& IsBattlerTurnDamaged(battlerDef)
&& IsBattlerTurnDamaged(battlerDef, EXCLUDING_SUBSTITUTES)
&& !DoesSubstituteBlockMove(gBattlerAttacker, battlerDef, gCurrentMove)
&& IsBattlerAlive(battlerDef)
&& gBattleMons[battlerDef].item == ITEM_NONE
@ -3560,7 +3560,7 @@ static enum MoveEndResult MoveEndThirdMoveBlock(void)
switch (moveEffect)
{
case EFFECT_STEEL_ROLLER:
if (gFieldStatuses & STATUS_FIELD_TERRAIN_ANY && IsBattlerTurnDamaged(gBattlerTarget))
if (gFieldStatuses & STATUS_FIELD_TERRAIN_ANY && IsBattlerTurnDamaged(gBattlerTarget, INCLUDING_SUBSTITUTES))
{
BattleScriptCall(BattleScript_RemoveTerrain);
result = MOVEEND_RESULT_RUN_SCRIPT;
@ -3570,7 +3570,7 @@ static enum MoveEndResult MoveEndThirdMoveBlock(void)
if (gFieldStatuses & STATUS_FIELD_TERRAIN_ANY
&& gLastPrintedMoves[gBattlerAttacker] == gCurrentMove
&& IsBattlerAlive(gBattlerAttacker)
&& IsBattlerTurnDamaged(gBattlerTarget))
&& IsBattlerTurnDamaged(gBattlerTarget, INCLUDING_SUBSTITUTES))
{
BattleScriptCall(BattleScript_RemoveTerrain);
result = MOVEEND_RESULT_RUN_SCRIPT;

View File

@ -2423,6 +2423,26 @@ static inline bool32 IgnoreTargetingForMoveEffect(enum MoveEffect moveEffect) //
}
}
static bool32 DoesSubstituteBlockMoveEffectOnTarget(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum MoveEffect moveEffect)
{
if (battlerAtk == battlerDef)
return FALSE;
if (moveEffect != MOVE_EFFECT_BUG_BITE && IgnoreTargetingForMoveEffect(moveEffect))
return FALSE;
if (moveEffect == MOVE_EFFECT_CORE_ENFORCER)
return FALSE;
if (moveEffect == MOVE_EFFECT_BREAK_SCREEN)
return FALSE;
if (DoesSubstituteBlockMove(battlerAtk, battlerDef, gCurrentMove))
return TRUE;
return FALSE;
}
void SetMoveEffect(enum BattlerId battlerAtk, enum BattlerId effectBattler, enum MoveEffect moveEffect, const u8 *battleScript, enum SetMoveEffectFlags effectFlags)
{
enum Ability abilities[MAX_BATTLERS_COUNT] = {ABILITY_NONE};
@ -2455,7 +2475,7 @@ void SetMoveEffect(enum BattlerId battlerAtk, enum BattlerId effectBattler, enum
moveEffect = MOVE_EFFECT_NONE;
else if (!IsBattlerAlive(gEffectBattler) && !IgnoreTargetingForMoveEffect(moveEffect))
moveEffect = MOVE_EFFECT_NONE;
else if (DoesSubstituteBlockMove(gBattlerAttacker, gEffectBattler, gCurrentMove) && !affectsUser)
else if (DoesSubstituteBlockMoveEffectOnTarget(gBattlerAttacker, gEffectBattler, moveEffect))
moveEffect = MOVE_EFFECT_NONE;
gBattleScripting.moveEffect = moveEffect; // ChangeStatBuffs still needs the global moveEffect
@ -2824,7 +2844,7 @@ void SetMoveEffect(enum BattlerId battlerAtk, enum BattlerId effectBattler, enum
if (gBattleMons[gEffectBattler].statStages[i] != DEFAULT_STAT_STAGE)
break;
}
if (IsBattlerTurnDamaged(gEffectBattler) && i != NUM_BATTLE_STATS)
if (IsBattlerTurnDamaged(gEffectBattler, EXCLUDING_SUBSTITUTES) && i != NUM_BATTLE_STATS)
{
for (i = 0; i < NUM_BATTLE_STATS; i++)
gBattleMons[gEffectBattler].statStages[i] = DEFAULT_STAT_STAGE;
@ -3714,7 +3734,7 @@ static void SetToxicChainPriority(void)
if (abilityAtk == ABILITY_TOXIC_CHAIN
&& IsBattlerAlive(gBattlerTarget)
&& CanBePoisoned(gBattlerAttacker, gBattlerTarget, abilityAtk, GetBattlerAbility(gBattlerTarget))
&& IsBattlerTurnDamaged(gBattlerTarget)
&& IsBattlerTurnDamaged(gBattlerTarget, EXCLUDING_SUBSTITUTES)
&& RandomWeighted(RNG_TOXIC_CHAIN, 7, 3))
gBattleStruct->toxicChainPriority = TRUE;
}

View File

@ -2313,7 +2313,7 @@ u32 NumFaintedBattlersByAttacker(enum BattlerId battlerAtk)
if (battler == battlerAtk)
continue;
if (IsBattlerTurnDamaged(battler) && !IsBattlerAlive(battler))
if (IsBattlerTurnDamaged(battler, EXCLUDING_SUBSTITUTES) && !IsBattlerAlive(battler))
numMonsFainted++;
}
@ -3835,7 +3835,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, enum BattlerId battler, enum
switch (gLastUsedAbility)
{
case ABILITY_COLOR_CHANGE:
if (IsBattlerTurnDamaged(battler)
if (IsBattlerTurnDamaged(battler, EXCLUDING_SUBSTITUTES)
&& IsBattlerAlive(battler)
&& !IS_BATTLER_OF_TYPE(battler, moveType)
&& move != MOVE_STRUGGLE
@ -3850,7 +3850,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, enum BattlerId battler, enum
}
break;
case ABILITY_BERSERK:
if (IsBattlerTurnDamaged(battler)
if (IsBattlerTurnDamaged(battler, EXCLUDING_SUBSTITUTES)
&& IsBattlerAlive(battler)
&& HadMoreThanHalfHpNowDoesnt(battler)
&& CompareStat(battler, STAT_SPATK, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility))
@ -3862,7 +3862,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, enum BattlerId battler, enum
}
break;
case ABILITY_ANGER_SHELL:
if (IsBattlerTurnDamaged(battler)
if (IsBattlerTurnDamaged(battler, EXCLUDING_SUBSTITUTES)
&& IsBattlerAlive(battler)
&& HadMoreThanHalfHpNowDoesnt(battler))
{
@ -3879,7 +3879,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, enum BattlerId battler, enum
switch (gLastUsedAbility)
{
case ABILITY_JUSTIFIED:
if (IsBattlerTurnDamaged(battler)
if (IsBattlerTurnDamaged(battler, EXCLUDING_SUBSTITUTES)
&& IsBattlerAlive(battler)
&& moveType == TYPE_DARK
&& CompareStat(battler, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility))
@ -3891,7 +3891,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, enum BattlerId battler, enum
}
break;
case ABILITY_RATTLED:
if (IsBattlerTurnDamaged(battler)
if (IsBattlerTurnDamaged(battler, EXCLUDING_SUBSTITUTES)
&& IsBattlerAlive(battler)
&& (moveType == TYPE_DARK || moveType == TYPE_BUG || moveType == TYPE_GHOST)
&& CompareStat(battler, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility))
@ -3903,7 +3903,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, enum BattlerId battler, enum
}
break;
case ABILITY_WATER_COMPACTION:
if (IsBattlerTurnDamaged(battler)
if (IsBattlerTurnDamaged(battler, EXCLUDING_SUBSTITUTES)
&& IsBattlerAlive(battler)
&& moveType == TYPE_WATER
&& CompareStat(battler, STAT_DEF, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility))
@ -3916,7 +3916,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, enum BattlerId battler, enum
break;
case ABILITY_STAMINA:
if (gBattlerAttacker != gBattlerTarget
&& IsBattlerTurnDamaged(gBattlerTarget)
&& IsBattlerTurnDamaged(gBattlerTarget, EXCLUDING_SUBSTITUTES)
&& IsBattlerAlive(battler)
&& CompareStat(battler, STAT_DEF, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility))
{
@ -3927,7 +3927,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, enum BattlerId battler, enum
}
break;
case ABILITY_WEAK_ARMOR:
if (IsBattlerTurnDamaged(battler)
if (IsBattlerTurnDamaged(battler, EXCLUDING_SUBSTITUTES)
&& IsBattlerAlive(battler)
&& IsBattleMovePhysical(gCurrentMove)
&& (CompareStat(battler, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility) // Don't activate if both Speed and Defense cannot be raised.
@ -3941,7 +3941,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, enum BattlerId battler, enum
}
break;
case ABILITY_CURSED_BODY:
if (IsBattlerTurnDamaged(gBattlerTarget)
if (IsBattlerTurnDamaged(gBattlerTarget, EXCLUDING_SUBSTITUTES)
&& gBattleMons[gBattlerAttacker].volatiles.disabledMove == MOVE_NONE
&& IsBattlerAlive(gBattlerAttacker)
&& !IsAbilityOnSide(gBattlerAttacker, ABILITY_AROMA_VEIL)
@ -3958,7 +3958,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, enum BattlerId battler, enum
case ABILITY_LINGERING_AROMA:
case ABILITY_MUMMY:
if (IsBattlerAlive(gBattlerAttacker)
&& IsBattlerTurnDamaged(gBattlerTarget)
&& IsBattlerTurnDamaged(gBattlerTarget, EXCLUDING_SUBSTITUTES)
&& !CanBattlerAvoidContactEffects(gBattlerAttacker, gBattlerTarget, GetBattlerAbility(gBattlerAttacker), GetBattlerHoldEffect(gBattlerAttacker), move)
&& gBattleMons[gBattlerAttacker].volatiles.overwrittenAbility != GetBattlerAbility(gBattlerTarget)
&& gBattleMons[gBattlerAttacker].ability != ABILITY_MUMMY
@ -3981,7 +3981,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, enum BattlerId battler, enum
break;
case ABILITY_WANDERING_SPIRIT:
if (IsBattlerAlive(gBattlerAttacker)
&& IsBattlerTurnDamaged(gBattlerTarget)
&& IsBattlerTurnDamaged(gBattlerTarget, EXCLUDING_SUBSTITUTES)
&& !CanBattlerAvoidContactEffects(gBattlerAttacker, gBattlerTarget, GetBattlerAbility(gBattlerAttacker), GetBattlerHoldEffect(gBattlerAttacker), move)
&& !(GetActiveGimmick(gBattlerTarget) == GIMMICK_DYNAMAX)
&& !gAbilitiesInfo[gBattleMons[gBattlerAttacker].ability].cantBeSwapped)
@ -4008,7 +4008,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, enum BattlerId battler, enum
break;
case ABILITY_ANGER_POINT:
if (gSpecialStatuses[battler].criticalHit
&& IsBattlerTurnDamaged(battler)
&& IsBattlerTurnDamaged(battler, EXCLUDING_SUBSTITUTES)
&& IsBattlerAlive(battler)
&& CompareStat(battler, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility))
{
@ -4022,7 +4022,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, enum BattlerId battler, enum
if (IsBattlerAlive(gBattlerAttacker)
&& (CompareStat(gBattlerAttacker, STAT_SPEED, MIN_STAT_STAGE, CMP_GREATER_THAN, gLastUsedAbility) || GetBattlerAbility(gBattlerAttacker) == ABILITY_MIRROR_ARMOR)
&& !gBattleStruct->unableToUseMove
&& IsBattlerTurnDamaged(gBattlerTarget)
&& IsBattlerTurnDamaged(gBattlerTarget, EXCLUDING_SUBSTITUTES)
&& !CanBattlerAvoidContactEffects(gBattlerAttacker, gBattlerTarget, GetBattlerAbility(gBattlerAttacker), GetBattlerHoldEffect(gBattlerAttacker), move))
{
SET_STATCHANGER(STAT_SPEED, 1, TRUE);
@ -4035,7 +4035,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, enum BattlerId battler, enum
case ABILITY_IRON_BARBS:
if (IsBattlerAlive(gBattlerAttacker)
&& !gBattleStruct->unableToUseMove
&& IsBattlerTurnDamaged(gBattlerTarget)
&& IsBattlerTurnDamaged(gBattlerTarget, EXCLUDING_SUBSTITUTES)
&& !CanBattlerAvoidContactEffects(gBattlerAttacker, gBattlerTarget, GetBattlerAbility(gBattlerAttacker), GetBattlerHoldEffect(gBattlerAttacker), move))
{
SetPassiveDamageAmount(gBattlerAttacker, GetNonDynamaxMaxHP(gBattlerAttacker) / (B_ROUGH_SKIN_DMG >= GEN_4 ? 8 : 16));
@ -4108,7 +4108,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, enum BattlerId battler, enum
if (i < sleep
&& IsBattlerAlive(gBattlerAttacker)
&& !gBattleStruct->unableToUseMove
&& IsBattlerTurnDamaged(gBattlerTarget)
&& IsBattlerTurnDamaged(gBattlerTarget, EXCLUDING_SUBSTITUTES)
&& CanBeSlept(gBattlerTarget, gBattlerAttacker, abilityAtk, NOT_BLOCKED_BY_SLEEP_CLAUSE)
&& !CanBattlerAvoidContactEffects(gBattlerAttacker, gBattlerTarget, abilityAtk, holdEffectAtk, move))
{
@ -4132,7 +4132,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, enum BattlerId battler, enum
enum Ability abilityAtk = GetBattlerAbility(gBattlerAttacker);
if (IsBattlerAlive(gBattlerAttacker)
&& !gBattleStruct->unableToUseMove
&& IsBattlerTurnDamaged(gBattlerTarget)
&& IsBattlerTurnDamaged(gBattlerTarget, EXCLUDING_SUBSTITUTES)
&& CanBePoisoned(gBattlerTarget, gBattlerAttacker, gLastUsedAbility, abilityAtk)
&& !CanBattlerAvoidContactEffects(gBattlerAttacker, gBattlerTarget, abilityAtk, GetBattlerHoldEffect(gBattlerAttacker), move))
{
@ -4154,7 +4154,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, enum BattlerId battler, enum
enum Ability abilityAtk = GetBattlerAbility(gBattlerAttacker);
if (IsBattlerAlive(gBattlerAttacker)
&& !gBattleStruct->unableToUseMove
&& IsBattlerTurnDamaged(gBattlerTarget)
&& IsBattlerTurnDamaged(gBattlerTarget, EXCLUDING_SUBSTITUTES)
&& CanBeParalyzed(gBattlerTarget, gBattlerAttacker, abilityAtk)
&& !CanBattlerAvoidContactEffects(gBattlerAttacker, gBattlerTarget, abilityAtk, GetBattlerHoldEffect(gBattlerAttacker), move))
{
@ -4172,7 +4172,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, enum BattlerId battler, enum
if (IsBattlerAlive(gBattlerAttacker)
&& !gBattleStruct->unableToUseMove
&& !CanBattlerAvoidContactEffects(gBattlerAttacker, gBattlerTarget, GetBattlerAbility(gBattlerAttacker), GetBattlerHoldEffect(gBattlerAttacker), move)
&& IsBattlerTurnDamaged(gBattlerTarget)
&& IsBattlerTurnDamaged(gBattlerTarget, EXCLUDING_SUBSTITUTES)
&& CanBeBurned(gBattlerTarget, gBattlerAttacker, GetBattlerAbility(gBattlerAttacker))
&& (GetConfig(B_ABILITY_TRIGGER_CHANCE) >= GEN_4 ? RandomPercentage(RNG_FLAME_BODY, 30) : RandomChance(RNG_FLAME_BODY, 1, 3)))
{
@ -4187,7 +4187,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, enum BattlerId battler, enum
case ABILITY_CUTE_CHARM:
if (IsBattlerAlive(gBattlerAttacker)
&& !gBattleStruct->unableToUseMove
&& IsBattlerTurnDamaged(gBattlerTarget)
&& IsBattlerTurnDamaged(gBattlerTarget, EXCLUDING_SUBSTITUTES)
&& IsBattlerAlive(gBattlerTarget)
&& (GetConfig(B_ABILITY_TRIGGER_CHANCE) >= GEN_4 ? RandomPercentage(RNG_CUTE_CHARM, 30) : RandomChance(RNG_CUTE_CHARM, 1, 3))
&& !(gBattleMons[gBattlerAttacker].volatiles.infatuation)
@ -4202,7 +4202,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, enum BattlerId battler, enum
}
break;
case ABILITY_ILLUSION:
if (gBattleStruct->illusion[gBattlerTarget].state == ILLUSION_ON && IsBattlerTurnDamaged(gBattlerTarget))
if (gBattleStruct->illusion[gBattlerTarget].state == ILLUSION_ON && IsBattlerTurnDamaged(gBattlerTarget, EXCLUDING_SUBSTITUTES))
{
gBattleScripting.battler = gBattlerTarget;
BattleScriptCall(BattleScript_IllusionOff);
@ -4210,7 +4210,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, enum BattlerId battler, enum
}
break;
case ABILITY_COTTON_DOWN:
if (IsBattlerTurnDamaged(gBattlerTarget))
if (IsBattlerTurnDamaged(gBattlerTarget, EXCLUDING_SUBSTITUTES))
{
gEffectBattler = gBattlerTarget;
// Will ability popup activate if there is only one target?
@ -4219,7 +4219,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, enum BattlerId battler, enum
}
break;
case ABILITY_STEAM_ENGINE:
if (IsBattlerTurnDamaged(gBattlerTarget)
if (IsBattlerTurnDamaged(gBattlerTarget, EXCLUDING_SUBSTITUTES)
&& IsBattlerAlive(battler)
&& CompareStat(battler, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility)
&& (moveType == TYPE_FIRE || moveType == TYPE_WATER))
@ -4232,7 +4232,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, enum BattlerId battler, enum
break;
case ABILITY_SAND_SPIT:
if (!gBattleStruct->unableToUseMove
&& IsBattlerTurnDamaged(gBattlerTarget)
&& IsBattlerTurnDamaged(gBattlerTarget, EXCLUDING_SUBSTITUTES)
&& !(gBattleWeather & B_WEATHER_SANDSTORM && HasWeatherEffect()))
{
if (gBattleWeather & B_WEATHER_PRIMAL_ANY && HasWeatherEffect())
@ -4250,7 +4250,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, enum BattlerId battler, enum
break;
case ABILITY_PERISH_BODY:
if (!gBattleStruct->unableToUseMove
&& IsBattlerTurnDamaged(gBattlerTarget)
&& IsBattlerTurnDamaged(gBattlerTarget, EXCLUDING_SUBSTITUTES)
&& IsBattlerAlive(battler)
&& !CanBattlerAvoidContactEffects(gBattlerAttacker, gBattlerTarget, GetBattlerAbility(gBattlerAttacker), GetBattlerHoldEffect(gBattlerAttacker), move)
&& !gBattleMons[gBattlerAttacker].volatiles.perishSong)
@ -4268,7 +4268,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, enum BattlerId battler, enum
break;
case ABILITY_SEED_SOWER:
if (!gBattleStruct->unableToUseMove
&& IsBattlerTurnDamaged(gBattlerTarget)
&& IsBattlerTurnDamaged(gBattlerTarget, EXCLUDING_SUBSTITUTES)
&& IsBattlerAlive(gBattlerTarget)
&& TryChangeBattleTerrain(gBattlerTarget, STATUS_FIELD_GRASSY_TERRAIN))
{
@ -4277,7 +4277,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, enum BattlerId battler, enum
}
break;
case ABILITY_THERMAL_EXCHANGE:
if (IsBattlerTurnDamaged(gBattlerTarget)
if (IsBattlerTurnDamaged(gBattlerTarget, EXCLUDING_SUBSTITUTES)
&& IsBattlerAlive(gBattlerTarget)
&& CompareStat(gBattlerTarget, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility)
&& moveType == TYPE_FIRE)
@ -4294,7 +4294,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, enum BattlerId battler, enum
// fall through
case ABILITY_ELECTROMORPHOSIS:
if (!gBattleStruct->unableToUseMove
&& IsBattlerTurnDamaged(gBattlerTarget)
&& IsBattlerTurnDamaged(gBattlerTarget, EXCLUDING_SUBSTITUTES)
&& IsBattlerAlive(gBattlerTarget))
{
BattleScriptCall(BattleScript_WindPowerActivates);
@ -4305,7 +4305,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, enum BattlerId battler, enum
if (!gBattleStruct->isSkyBattle
&& !gBattleStruct->unableToUseMove
&& IsBattleMovePhysical(gCurrentMove)
&& IsBattlerTurnDamaged(gBattlerTarget)
&& IsBattlerTurnDamaged(gBattlerTarget, EXCLUDING_SUBSTITUTES)
&& (gSideTimers[GetBattlerSide(gBattlerAttacker)].toxicSpikesAmount != 2))
{
SaveBattlerTarget(gBattlerTarget);
@ -4328,7 +4328,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, enum BattlerId battler, enum
&& !gBattleStruct->unableToUseMove
&& CanBePoisoned(gBattlerAttacker, gBattlerTarget, gLastUsedAbility, GetBattlerAbility(gBattlerTarget))
&& IsMoveMakingContact(gBattlerAttacker, gBattlerTarget, GetBattlerAbility(gBattlerAttacker), GetBattlerHoldEffect(gBattlerAttacker), move)
&& IsBattlerTurnDamaged(gBattlerTarget) // Need to actually hit the target
&& IsBattlerTurnDamaged(gBattlerTarget, EXCLUDING_SUBSTITUTES) // Need to actually hit the target
&& RandomPercentage(RNG_POISON_TOUCH, 30))
{
gEffectBattler = gBattlerTarget;
@ -4355,7 +4355,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, enum BattlerId battler, enum
if (IsBattlerAlive(gBattlerTarget)
&& !gBattleStruct->unableToUseMove
&& RandomChance(RNG_STENCH, 1, 10)
&& IsBattlerTurnDamaged(gBattlerTarget)
&& IsBattlerTurnDamaged(gBattlerTarget, EXCLUDING_SUBSTITUTES)
&& !MoveHasAdditionalEffect(gCurrentMove, MOVE_EFFECT_FLINCH))
{
SetMoveEffect(gBattlerAttacker, gBattlerTarget, MOVE_EFFECT_FLINCH, gBattlescriptCurrInstr, EFFECT_PRIMARY);
@ -4382,7 +4382,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, enum BattlerId battler, enum
speciesForm = gBattleMons[gBattlerTarget].species;
if (gBattleStruct->unableToUseMove
|| !IsBattlerTurnDamaged(gBattlerTarget)
|| !IsBattlerTurnDamaged(gBattlerTarget, EXCLUDING_SUBSTITUTES)
|| !TryBattleFormChange(gBattlerTarget, FORM_CHANGE_BATTLE_HIT_BY_MOVE_CATEGORY, ability))
break;
@ -4472,7 +4472,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, enum BattlerId battler, enum
{
if (gBattleMons[battlerDef].item != ITEM_NONE
&& battlerDef != battler
&& IsBattlerTurnDamaged(battlerDef)
&& IsBattlerTurnDamaged(battlerDef, EXCLUDING_SUBSTITUTES)
&& CanStealItem(battler, battlerDef, gBattleMons[battlerDef].item)
&& !GetBattlerPartyState(battlerDef)->isKnockedOff
&& !DoesSubstituteBlockMove(battler, battlerDef, move)
@ -10008,7 +10008,7 @@ bool32 HasWeatherEffect(void)
void UpdateStallMons(void)
{
if (IsBattlerTurnDamaged(gBattlerTarget) || GetMoveCategory(gCurrentMove) == DAMAGE_CATEGORY_STATUS)
if (IsBattlerTurnDamaged(gBattlerTarget, INCLUDING_SUBSTITUTES) || GetMoveCategory(gCurrentMove) == DAMAGE_CATEGORY_STATUS)
return;
struct BattleContext ctx = {0};
@ -10702,7 +10702,7 @@ bool32 IsAnyTargetTurnDamaged(enum BattlerId battlerAtk)
{
if (battlerDef == battlerAtk)
continue;
if (IsBattlerTurnDamaged(battlerDef))
if (IsBattlerTurnDamaged(battlerDef, INCLUDING_SUBSTITUTES))
return TRUE;
}
return FALSE;

View File

@ -598,6 +598,24 @@ SINGLE_BATTLE_TEST("(Z-MOVE) Genesis Supernova sets up psychic terrain")
}
}
SINGLE_BATTLE_TEST("(Z-MOVE) Genesis Supernova sets up psychic terrain when the target is behind a Substitute")
{
GIVEN {
ASSUME(MoveHasAdditionalEffect(MOVE_GENESIS_SUPERNOVA, MOVE_EFFECT_PSYCHIC_TERRAIN));
PLAYER(SPECIES_MEW) { Item(ITEM_MEWNIUM_Z); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_SUBSTITUTE); MOVE(player, MOVE_PSYCHIC, gimmick: GIMMICK_Z_MOVE); }
TURN { MOVE(player, MOVE_QUICK_ATTACK); }
} SCENE {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ZMOVE_ACTIVATE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_GENESIS_SUPERNOVA, player);
SUB_HIT(opponent);
NOT { ANIMATION(ANIM_TYPE_MOVE, MOVE_QUICK_ATTACK, player); }
MESSAGE("The opposing Wobbuffet is protected by the Psychic Terrain!");
}
}
SINGLE_BATTLE_TEST("(Z-MOVE) Splintered Stormshards removes terrain")
{
GIVEN {
@ -616,6 +634,25 @@ SINGLE_BATTLE_TEST("(Z-MOVE) Splintered Stormshards removes terrain")
}
}
SINGLE_BATTLE_TEST("(Z-MOVE) Splintered Stormshards removes terrain when the target is behind a Substitute")
{
GIVEN {
ASSUME(GetMoveEffect(MOVE_SPLINTERED_STORMSHARDS) == EFFECT_ICE_SPINNER);
PLAYER(SPECIES_LYCANROC_DUSK) { Item(ITEM_LYCANIUM_Z); }
OPPONENT(SPECIES_TAPU_LELE) { Ability(ABILITY_PSYCHIC_SURGE); HP(1000); MaxHP(1000); }
} WHEN {
TURN { MOVE(opponent, MOVE_SUBSTITUTE); MOVE(player, MOVE_STONE_EDGE, gimmick: GIMMICK_Z_MOVE); }
TURN { MOVE(player, MOVE_QUICK_ATTACK); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SUBSTITUTE, opponent);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ZMOVE_ACTIVATE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_SPLINTERED_STORMSHARDS, player);
SUB_HIT(opponent);
MESSAGE("The weirdness disappeared from the battlefield!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_QUICK_ATTACK, player);
}
}
SINGLE_BATTLE_TEST("(Z-MOVE) Clangorous Soulblaze boosts all the user's stats by one stage")
{
GIVEN {

View File

@ -104,7 +104,24 @@ SINGLE_BATTLE_TEST("Absorb does not drain any HP if user flinched")
}
}
TO_DO_BATTLE_TEST("Absorb recovers 50% of the damage dealt to a Substitute");
SINGLE_BATTLE_TEST("Absorb recovers 50% of the damage dealt to a Substitute")
{
u16 damage;
s16 healing;
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) { HP(1); }
} WHEN {
TURN { MOVE(player, MOVE_SUBSTITUTE); MOVE(opponent, MOVE_ABSORB); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SUBSTITUTE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_ABSORB, opponent);
SUB_HIT(player, captureDamage: &damage);
HP_BAR(opponent, captureDamage: &healing);
} THEN {
EXPECT_MUL_EQ(damage, Q_4_12(-0.5), healing);
}
}
SINGLE_BATTLE_TEST("Absorb does not drain any HP if user does 0 damage")
{

View File

@ -94,3 +94,20 @@ SINGLE_BATTLE_TEST("Ceaseless Edge does not set up hazards if target was not hit
}
}
}
SINGLE_BATTLE_TEST("Ceaseless Edge will set up rocks if the target is behind a Substitute")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_SUBSTITUTE); MOVE(opponent, MOVE_CEASELESS_EDGE); }
TURN { SWITCH(player, 1); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SUBSTITUTE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_CEASELESS_EDGE, opponent);
SUB_HIT(player);
HP_BAR(player);
}
}

View File

@ -156,3 +156,18 @@ SINGLE_BATTLE_TEST("Chloroblast is not affected by Reckless", s16 damage)
EXPECT_EQ(results[0].damage, results[1].damage);
}
}
SINGLE_BATTLE_TEST("Chloroblast has recoil if the target is behind a Substitute")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) { HP(400); MaxHP(400); }
} WHEN {
TURN { MOVE(player, MOVE_SUBSTITUTE); MOVE(opponent, MOVE_CHLOROBLAST); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SUBSTITUTE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_CHLOROBLAST, opponent);
SUB_HIT(player);
HP_BAR(opponent, damage: 200);
}
}

View File

@ -0,0 +1,54 @@
#include "global.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Core Enforcer suppresses the ability of targets that have already acted")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WEEZING) { Ability(ABILITY_LEVITATE); }
} WHEN {
TURN { MOVE(opponent, MOVE_CELEBRATE); MOVE(player, MOVE_CORE_ENFORCER); }
TURN { MOVE(player, MOVE_EARTHQUAKE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_CORE_ENFORCER, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_EARTHQUAKE, player);
HP_BAR(opponent);
}
}
SINGLE_BATTLE_TEST("Core Enforcer doesn't suppresses the ability of targets that haven't acted")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WEEZING) { Ability(ABILITY_LEVITATE); }
} WHEN {
TURN { MOVE(player, MOVE_CORE_ENFORCER); MOVE(opponent, MOVE_CELEBRATE); }
TURN { MOVE(player, MOVE_EARTHQUAKE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_CORE_ENFORCER, player);
HP_BAR(opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponent);
NONE_OF {
ANIMATION(ANIM_TYPE_MOVE, MOVE_EARTHQUAKE, player);
HP_BAR(opponent);
}
}
}
SINGLE_BATTLE_TEST("Core Enforcer suppresses the ability of targets that have already acted that are behind Substitutes")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WEEZING) { Ability(ABILITY_LEVITATE); }
} WHEN {
TURN { MOVE(opponent, MOVE_SUBSTITUTE); MOVE(player, MOVE_CORE_ENFORCER); }
TURN { MOVE(player, MOVE_EARTHQUAKE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SUBSTITUTE, opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_CORE_ENFORCER, player);
SUB_HIT(opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_EARTHQUAKE, player);
SUB_HIT(opponent);
}
}

View File

@ -207,3 +207,17 @@ SINGLE_BATTLE_TEST("Hit Escape: U-turn will fail to switch if the user faints")
HP_BAR(opponent);
}
}
SINGLE_BATTLE_TEST("Hit Escape: U-turn will switch if the target is behind a Substitute")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WYNAUT);
} WHEN {
TURN { MOVE(player, MOVE_SUBSTITUTE); MOVE(opponent, MOVE_U_TURN); SEND_OUT(opponent, 1); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SUBSTITUTE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_U_TURN, opponent);
}
}

View File

@ -125,3 +125,20 @@ AI_SINGLE_BATTLE_TEST("Ice Spinner can be chosen by AI regardless if there is a
}
}
}
SINGLE_BATTLE_TEST("Ice Spinner will remove terrain if target is behind a Substitute")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_GRASSY_TERRAIN); }
TURN { MOVE(player, MOVE_SUBSTITUTE); MOVE(opponent, MOVE_ICE_SPINNER); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_GRASSY_TERRAIN, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_SUBSTITUTE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_ICE_SPINNER, opponent);
SUB_HIT(player);
NOT HP_BAR(player);
}
}

View File

@ -147,3 +147,18 @@ SINGLE_BATTLE_TEST("Steel Beam is not blocked by Damp")
}
}
}
SINGLE_BATTLE_TEST("Steel Beam inflicts recoil if it hits a Substitute")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { HP(400); MaxHP(400); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_SUBSTITUTE); MOVE(player, MOVE_STEEL_BEAM); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SUBSTITUTE, opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_STEEL_BEAM, player);
SUB_HIT(opponent);
HP_BAR(player, damage: 200);
}
}

View File

@ -120,3 +120,27 @@ SINGLE_BATTLE_TEST("Rapid Spin doesn't blow away Wrap, hazards or raise Speed wh
}
}
}
SINGLE_BATTLE_TEST("Rapid Spin and Mortal Spin will remove hazards if the target is behind a Substitute")
{
enum Move move;
PARAMETRIZE { move = MOVE_RAPID_SPIN; }
PARAMETRIZE { move = MOVE_MORTAL_SPIN; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WYNAUT);
} WHEN {
TURN { MOVE(player, MOVE_STEALTH_ROCK); }
TURN { MOVE(player, MOVE_SUBSTITUTE); MOVE(opponent, move); }
TURN { SWITCH(opponent, 1); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_STEALTH_ROCK, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_SUBSTITUTE, player);
ANIMATION(ANIM_TYPE_MOVE, move, opponent);
SUB_HIT(player);
NOT HP_BAR(opponent);
}
}

View File

@ -73,3 +73,19 @@ AI_SINGLE_BATTLE_TEST("Steel Roller wont be chosen by AI if there is no terrain
}
}
}
SINGLE_BATTLE_TEST("Steel Roller will remove terrain if target is behind a Substitute")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_GRASSY_TERRAIN); }
TURN { MOVE(player, MOVE_SUBSTITUTE); MOVE(opponent, MOVE_STEEL_ROLLER); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_GRASSY_TERRAIN, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_SUBSTITUTE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_STEEL_ROLLER, opponent);
NOT HP_BAR(player);
}
}

View File

@ -92,3 +92,20 @@ SINGLE_BATTLE_TEST("Stone Axe fails to set up hazards if user faints")
NOT MESSAGE("Pointed stones float in the air around the opposing team!");
}
}
SINGLE_BATTLE_TEST("Stone Axe will set up rocks if the target is behind a Substitute")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_SUBSTITUTE); MOVE(opponent, MOVE_STONE_AXE); }
TURN { SWITCH(player, 1); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SUBSTITUTE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_STONE_AXE, opponent);
SUB_HIT(player);
HP_BAR(player);
}
}

View File

@ -159,3 +159,25 @@ DOUBLE_BATTLE_TEST("Brick Break and Psychic Fangs can remove Light Screen, Refle
}
}
SINGLE_BATTLE_TEST("Brick Break and Psychic Fangs can remove screens when the target is behind a Substitute")
{
enum Move move;
PARAMETRIZE { move = MOVE_BRICK_BREAK; }
PARAMETRIZE { move = MOVE_PSYCHIC_FANGS; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_REFLECT); }
TURN { MOVE(player, MOVE_SUBSTITUTE); MOVE(opponent, move); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_REFLECT, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_SUBSTITUTE, player);
ANIMATION(ANIM_TYPE_MOVE, move, opponent);
MESSAGE("The wall shattered!");
SUB_HIT(player);
}
}

View File

@ -116,3 +116,22 @@ SINGLE_BATTLE_TEST("Plasma Fists turns normal type dynamax-moves into electric t
MESSAGE("It's super effective!");
}
}
SINGLE_BATTLE_TEST("Plasma Fists turns normal moves into electric moves even if it hits a substitute")
{
GIVEN {
PLAYER(SPECIES_JOLTEON) { Ability(ABILITY_VOLT_ABSORB); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_SUBSTITUTE); }
TURN { MOVE(player, MOVE_PLASMA_FISTS); MOVE(opponent, MOVE_SCRATCH); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SUBSTITUTE, opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_PLASMA_FISTS, player);
SUB_HIT(opponent);
NONE_OF {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, opponent);
HP_BAR(player);
}
}
}

View File

@ -137,3 +137,23 @@ SINGLE_BATTLE_TEST("Recoil: No recoil is taken if the move is blocked by Disguis
EXPECT_EQ(player->hp, player->maxHP);
}
}
SINGLE_BATTLE_TEST("Recoil: Hitting substitutes inflicts recoil")
{
u16 damage;
s16 recoil;
GIVEN {
ASSUME(GetMoveRecoil(MOVE_TAKE_DOWN) == 25);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_SUBSTITUTE); MOVE(opponent, MOVE_TAKE_DOWN); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SUBSTITUTE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_TAKE_DOWN, opponent);
SUB_HIT(player, captureDamage: &damage);
HP_BAR(opponent, captureDamage: &recoil);
} THEN {
EXPECT_MUL_EQ(damage, Q_4_12(0.25), recoil);
}
}