Immunity abilities trigger on turn 0 (leads) (#7814)

This commit is contained in:
spindrift64
2025-09-30 19:07:23 +02:00
committed by GitHub
parent 768b87696c
commit 2231b2fdca
8 changed files with 146 additions and 91 deletions

View File

@@ -8285,6 +8285,13 @@ BattleScript_AbilityCuredStatus::
updatestatusicon BS_SCRIPTING
return
BattleScript_AbilityCuredStatusEnd3::
call BattleScript_AbilityPopUp
printstring STRINGID_PKMNSXCUREDITSYPROBLEM
waitmessage B_WAIT_TIME_LONG
updatestatusicon BS_SCRIPTING
end3
BattleScript_BattlerShookOffTaunt::
call BattleScript_AbilityPopUp
printstring STRINGID_PKMNSHOOKOFFTHETAUNT

View File

@@ -202,6 +202,7 @@ extern const u8 BattleScript_AbilityStatusEffect[];
extern const u8 BattleScript_SynchronizeActivates[];
extern const u8 BattleScript_NoItemSteal[];
extern const u8 BattleScript_AbilityCuredStatus[];
extern const u8 BattleScript_AbilityCuredStatusEnd3[];
extern const u8 BattleScript_IgnoresWhileAsleep[];
extern const u8 BattleScript_IgnoresAndUsesRandomMove[];
extern const u8 BattleScript_MoveUsedLoafingAround[];

View File

@@ -55,6 +55,7 @@ enum {
ABILITYEFFECT_SWITCH_IN_WEATHER,
ABILITYEFFECT_OPPORTUNIST,
ABILITYEFFECT_SWITCH_IN_STATUSES,
ABILITYEFFECT_ON_SWITCHIN_IMMUNITIES,
};
// For the first argument of ItemBattleEffects, to deteremine which block of item effects to try
@@ -307,6 +308,7 @@ struct Pokemon *GetIllusionMonPtr(u32 battler);
void ClearIllusionMon(u32 battler);
u32 GetIllusionMonPartyId(struct Pokemon *party, struct Pokemon *mon, struct Pokemon *partnerMon, u32 battler);
bool32 SetIllusionMon(struct Pokemon *mon, u32 battler);
u32 TryImmunityAbilityHealStatus(u32 battler, u32 caseID);
bool32 ShouldGetStatBadgeBoost(u16 flagId, u32 battler);
enum DamageCategory GetBattleMoveCategory(u32 move);
void SetDynamicMoveCategory(u32 battlerAtk, u32 battlerDef, u32 move);

View File

@@ -3870,6 +3870,8 @@ static void TryDoEventsBeforeFirstTurn(void)
return;
if (TryClearIllusion(i, ABILITYEFFECT_ON_SWITCHIN))
return;
if (AbilityBattleEffects(ABILITYEFFECT_ON_SWITCHIN_IMMUNITIES, i, 0, 0, 0) != 0)
return;
}
gBattleStruct->switchInBattlerCounter = 0;
gBattleStruct->eventsBeforeFirstTurnState++;

View File

@@ -6121,9 +6121,12 @@ static void Cmd_moveend(void)
gBattleScripting.moveendState++;
break;
case MOVEEND_STATUS_IMMUNITY_ABILITIES: // status immunities
if (AbilityBattleEffects(ABILITYEFFECT_IMMUNITY, 0, 0, 0, 0))
effect = TRUE; // it loops through all battlers, so we increment after its done with all battlers
else
for (u16 battler = 0; battler < gBattlersCount; battler++)
{
if (AbilityBattleEffects(ABILITYEFFECT_IMMUNITY, battler, 0, 0, 0))
effect = TRUE;
}
if(!effect)
gBattleScripting.moveendState++;
break;
case MOVEEND_SYNCHRONIZE_ATTACKER: // attacker synchronize
@@ -17250,6 +17253,7 @@ void BS_SwitchinAbilities(void)
AbilityBattleEffects(ABILITYEFFECT_NEUTRALIZINGGAS, battler, 0, 0, 0);
AbilityBattleEffects(ABILITYEFFECT_ON_SWITCHIN, battler, 0, 0, 0);
AbilityBattleEffects(ABILITYEFFECT_OPPORTUNIST, battler, 0, 0, 0);
AbilityBattleEffects(ABILITYEFFECT_IMMUNITY, battler, 0, 0, 0);
if (gBattleWeather & B_WEATHER_ANY && HasWeatherEffect())
AbilityBattleEffects(ABILITYEFFECT_ON_WEATHER, battler, 0, 0, 0);

View File

@@ -5092,94 +5092,12 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
}
break;
case ABILITYEFFECT_IMMUNITY:
for (battler = 0; battler < gBattlersCount; battler++)
{
switch (GetBattlerAbilityIgnoreMoldBreaker(battler))
{
case ABILITY_IMMUNITY:
case ABILITY_PASTEL_VEIL:
if (gBattleMons[battler].status1 & (STATUS1_POISON | STATUS1_TOXIC_POISON | STATUS1_TOXIC_COUNTER))
{
StringCopy(gBattleTextBuff1, gStatusConditionString_PoisonJpn);
effect = 1;
}
break;
case ABILITY_OWN_TEMPO:
if (gBattleMons[battler].volatiles.confusionTurns > 0)
{
StringCopy(gBattleTextBuff1, gStatusConditionString_ConfusionJpn);
effect = 2;
}
break;
case ABILITY_LIMBER:
if (gBattleMons[battler].status1 & STATUS1_PARALYSIS)
{
StringCopy(gBattleTextBuff1, gStatusConditionString_ParalysisJpn);
effect = 1;
}
break;
case ABILITY_INSOMNIA:
case ABILITY_VITAL_SPIRIT:
if (gBattleMons[battler].status1 & STATUS1_SLEEP)
{
TryDeactivateSleepClause(GetBattlerSide(battler), gBattlerPartyIndexes[battler]);
gBattleMons[battler].volatiles.nightmare = FALSE;
StringCopy(gBattleTextBuff1, gStatusConditionString_SleepJpn);
effect = 1;
}
break;
case ABILITY_WATER_VEIL:
case ABILITY_WATER_BUBBLE:
case ABILITY_THERMAL_EXCHANGE:
if (gBattleMons[battler].status1 & STATUS1_BURN)
{
StringCopy(gBattleTextBuff1, gStatusConditionString_BurnJpn);
effect = 1;
}
break;
case ABILITY_MAGMA_ARMOR:
if (gBattleMons[battler].status1 & (STATUS1_FREEZE | STATUS1_FROSTBITE))
{
StringCopy(gBattleTextBuff1, gStatusConditionString_IceJpn);
effect = 1;
}
break;
case ABILITY_OBLIVIOUS:
if (gBattleMons[battler].volatiles.infatuation)
effect = 3;
else if (gDisableStructs[battler].tauntTimer != 0)
effect = 4;
break;
}
if (effect != 0)
{
switch (effect)
{
case 1: // status cleared
gBattleMons[battler].status1 = 0;
BattleScriptCall(BattleScript_AbilityCuredStatus);
break;
case 2: // get rid of confusion
RemoveConfusionStatus(battler);
BattleScriptCall(BattleScript_AbilityCuredStatus);
break;
case 3: // get rid of infatuation
gBattleMons[battler].volatiles.infatuation = 0;
BattleScriptCall(BattleScript_BattlerGotOverItsInfatuation);
break;
case 4: // get rid of taunt
gDisableStructs[battler].tauntTimer = 0;
BattleScriptCall(BattleScript_BattlerShookOffTaunt);
break;
}
gBattleScripting.battler = gBattlerAbility = battler;
BtlController_EmitSetMonData(battler, B_COMM_TO_CONTROLLER, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[battler].status1);
MarkBattlerForControllerExec(battler);
return effect;
}
}
effect = TryImmunityAbilityHealStatus(battler, caseID);
if (effect)
return effect;
break;
case ABILITYEFFECT_ON_SWITCHIN_IMMUNITIES:
effect = TryImmunityAbilityHealStatus(battler, caseID);
break;
case ABILITYEFFECT_SYNCHRONIZE:
if (gLastUsedAbility == ABILITY_SYNCHRONIZE && gBattleStruct->synchronizeMoveEffect != MOVE_EFFECT_NONE)
@@ -10404,6 +10322,110 @@ bool32 SetIllusionMon(struct Pokemon *mon, u32 battler)
return FALSE;
}
u32 TryImmunityAbilityHealStatus(u32 battler, u32 caseID)
{
u32 effect = 0;
switch (GetBattlerAbilityIgnoreMoldBreaker(battler))
{
case ABILITY_IMMUNITY:
case ABILITY_PASTEL_VEIL:
if (gBattleMons[battler].status1 & (STATUS1_POISON | STATUS1_TOXIC_POISON | STATUS1_TOXIC_COUNTER))
{
StringCopy(gBattleTextBuff1, gStatusConditionString_PoisonJpn);
effect = 1;
}
break;
case ABILITY_OWN_TEMPO:
if (gBattleMons[battler].volatiles.confusionTurns > 0)
{
StringCopy(gBattleTextBuff1, gStatusConditionString_ConfusionJpn);
effect = 2;
}
break;
case ABILITY_LIMBER:
if (gBattleMons[battler].status1 & STATUS1_PARALYSIS)
{
StringCopy(gBattleTextBuff1, gStatusConditionString_ParalysisJpn);
effect = 1;
}
break;
case ABILITY_INSOMNIA:
case ABILITY_VITAL_SPIRIT:
if (gBattleMons[battler].status1 & STATUS1_SLEEP)
{
TryDeactivateSleepClause(GetBattlerSide(battler), gBattlerPartyIndexes[battler]);
gBattleMons[battler].volatiles.nightmare = FALSE;
StringCopy(gBattleTextBuff1, gStatusConditionString_SleepJpn);
effect = 1;
}
break;
case ABILITY_WATER_VEIL:
case ABILITY_WATER_BUBBLE:
case ABILITY_THERMAL_EXCHANGE:
if (gBattleMons[battler].status1 & STATUS1_BURN)
{
StringCopy(gBattleTextBuff1, gStatusConditionString_BurnJpn);
effect = 1;
}
break;
case ABILITY_MAGMA_ARMOR:
if (gBattleMons[battler].status1 & (STATUS1_FREEZE | STATUS1_FROSTBITE))
{
StringCopy(gBattleTextBuff1, gStatusConditionString_IceJpn);
effect = 1;
}
break;
case ABILITY_OBLIVIOUS:
if (gBattleMons[battler].volatiles.infatuation)
effect = 3;
else if (gDisableStructs[battler].tauntTimer != 0)
effect = 4;
break;
}
if (effect != 0)
{
switch (effect)
{
case 1: // status cleared
gBattleMons[battler].status1 = 0;
if(caseID == ABILITYEFFECT_ON_SWITCHIN_IMMUNITIES)
BattleScriptExecute(BattleScript_AbilityCuredStatusEnd3);
else
BattleScriptCall(BattleScript_AbilityCuredStatus);
break;
case 2: // get rid of confusion
RemoveConfusionStatus(battler);
if(caseID == ABILITYEFFECT_ON_SWITCHIN_IMMUNITIES)
BattleScriptExecute(BattleScript_AbilityCuredStatusEnd3);
else
BattleScriptCall(BattleScript_AbilityCuredStatus);
break;
case 3: // get rid of infatuation
gBattleMons[battler].volatiles.infatuation = 0;
if(caseID == ABILITYEFFECT_ON_SWITCHIN_IMMUNITIES)
BattleScriptExecute(BattleScript_AbilityCuredStatusEnd3);
else
BattleScriptCall(BattleScript_AbilityCuredStatus);
break;
case 4: // get rid of taunt
gDisableStructs[battler].tauntTimer = 0;
if(caseID == ABILITYEFFECT_ON_SWITCHIN_IMMUNITIES)
BattleScriptExecute(BattleScript_AbilityCuredStatusEnd3);
else
BattleScriptCall(BattleScript_AbilityCuredStatus);
break;
}
gBattleScripting.battler = gBattlerAbility = battler;
BtlController_EmitSetMonData(battler, B_COMM_TO_CONTROLLER, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[battler].status1);
MarkBattlerForControllerExec(battler);
return effect;
}
return 0;
}
bool32 ShouldGetStatBadgeBoost(u16 badgeFlag, u32 battler)
{
if (B_BADGE_BOOST == GEN_3 && badgeFlag != 0)

View File

@@ -63,3 +63,19 @@ SINGLE_BATTLE_TEST("Immunity doesn't prevent Pokémon from being poisoned by Tox
NOT HP_BAR(player);
}
}
SINGLE_BATTLE_TEST("Immunity cures existing poison on turn 0")
{
GIVEN {
PLAYER(SPECIES_ZANGOOSE) {
Ability(ABILITY_IMMUNITY);
Status1(STATUS1_POISON);
}
OPPONENT(SPECIES_WOBBUFFET);
} SCENE {
ABILITY_POPUP(player, ABILITY_IMMUNITY);
TURN { MOVE(player, MOVE_SPLASH); }
} THEN {
EXPECT_EQ(player->status1, STATUS1_NONE);
}
}

View File

@@ -1075,6 +1075,7 @@ SINGLE_BATTLE_TEST("Sleep Clause: Sleep clause is deactivated when a sleeping mo
u32 ability;
PARAMETRIZE { ability = ABILITY_VITAL_SPIRIT; }
PARAMETRIZE { ability = ABILITY_INSOMNIA; }
GIVEN {
FLAG_SET(B_FLAG_SLEEP_CLAUSE);
ASSUME(GetMoveEffect(MOVE_SPORE) == EFFECT_NON_VOLATILE_STATUS);