Added missing redirect abilities Gen 3-4 config (#4920)

* Added missing redirect abilities Gen 3-4 config

* Combined 3 Battle Script Natives into 1

* Cleaner FindMonThatAbsorbsOpponentsMove
This commit is contained in:
Eduardo Quezada 2024-07-07 03:52:59 -04:00 committed by GitHub
parent 60c7588532
commit de609a2f5b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 207 additions and 63 deletions

View File

@ -1507,18 +1507,10 @@
.4byte \jumpInstr
.endm
.macro jumpifrodaffected battler:req, jumpInstr:req
callnative BS_JumpIfRod
.4byte \jumpInstr
.endm
.macro jumpifabsorbaffected battler:req, jumpInstr:req
callnative BS_JumpIfAbsorb
.4byte \jumpInstr
.endm
.macro jumpifmotoraffected battler:req, jumpInstr:req
callnative BS_JumpIfMotor
.macro jumpifelectricabilityaffected battler:req, ability:req, jumpInstr:req
callnative BS_JumpIfElectricAbilityAffected
.byte \battler
.2byte \ability
.4byte \jumpInstr
.endm

View File

@ -432,9 +432,9 @@ BattleScript_EffectTeatime::
waitanimation
setbyte gBattlerTarget, 0
BattleScript_TeatimeLoop:
jumpifrodaffected BS_TARGET, BattleScript_Teatimerod
jumpifabsorbaffected BS_TARGET, BattleScript_Teatimesorb
jumpifmotoraffected BS_TARGET, BattleScript_Teatimemotor
jumpifelectricabilityaffected BS_TARGET, ABILITY_LIGHTNING_ROD, BattleScript_Teatimerod
jumpifelectricabilityaffected BS_TARGET, ABILITY_VOLT_ABSORB, BattleScript_Teatimesorb
jumpifelectricabilityaffected BS_TARGET, ABILITY_MOTOR_DRIVE, BattleScript_Teatimemotor
jumpifteainvulnerable BS_TARGET, BattleScript_Teatimevul @ in semi-invulnerable state OR held item is not a Berry
orword gHitMarker, HITMARKER_DISABLE_ANIMATION | HITMARKER_IGNORE_SUBSTITUTE | HITMARKER_IGNORE_DISGUISE
setbyte sBERRY_OVERRIDE, TRUE @ override the requirements for eating berries
@ -3609,7 +3609,7 @@ BattleScript_EffectParalyze::
BattleScript_BattleScript_EffectParalyzeNoTypeCalc:
jumpifmovehadnoeffect BattleScript_ButItFailed
jumpifstatus BS_TARGET, STATUS1_PARALYSIS, BattleScript_AlreadyParalyzed
jumpifabsorbaffected BS_TARGET, BattleScript_VoltAbsorbHeal
jumpifelectricabilityaffected BS_TARGET, ABILITY_VOLT_ABSORB, BattleScript_VoltAbsorbHeal
tryparalyzetype BS_ATTACKER, BS_TARGET, BattleScript_NotAffected
jumpifstatus BS_TARGET, STATUS1_ANY, BattleScript_ButItFailed
jumpifterrainaffected BS_TARGET, STATUS_FIELD_MISTY_TERRAIN, BattleScript_MistyTerrainPrevents

View File

@ -135,6 +135,7 @@
#define B_WEATHER_FORMS GEN_LATEST // In Gen5+, Castform and Cherrim revert to their base form upon losing their respective ability. Cherrim needs Flower Gift to swap forms.
#define B_SYMBIOSIS_GEMS GEN_LATEST // In Gen7+, Symbiosis passes an item after a gem-boosted attack. Previously, items are passed before the gem-boosted attack hits, making the item effect apply.
#define B_ABSORBING_ABILITY_STRING GEN_LATEST // In Gen5+, the abilities that absorb moves of a certain type use a generic string for stat increases and decreases.
#define B_REDIRECT_ABILITY_IMMUNITY GEN_LATEST // In Gen5+, Pokémon with Lightning Rod/Storm Drain become immune to Electric/Water-type moves and increase their Sp. Attack by 1 stage on top of the redirecting effect.
#define B_LEAF_GUARD_PREVENTS_REST GEN_LATEST // In Gen5+, Leaf Guard prevents the use of Rest in harsh sunlight.
#define B_SNOW_WARNING GEN_LATEST // In Gen9+, Snow Warning will summon snow instead of hail.
#define B_TRANSISTOR_BOOST GEN_LATEST // In Gen9+, Transistor will only boost Electric-type moves by 1.3x as opposed to 1.5x.

View File

@ -943,12 +943,21 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
&& IsNonVolatileStatusMoveEffect(moveEffect))
RETURN_SCORE_MINUS(10);
break;
case ABILITY_VOLT_ABSORB:
case ABILITY_LIGHTNING_ROD:
if (B_REDIRECT_ABILITY_IMMUNITY < GEN_5)
break;
// Fallthrough
case ABILITY_MOTOR_DRIVE:
case ABILITY_VOLT_ABSORB:
if (moveType == TYPE_ELECTRIC)
RETURN_SCORE_MINUS(20);
break;
case ABILITY_STORM_DRAIN:
if (B_REDIRECT_ABILITY_IMMUNITY < GEN_5)
break;
// Fallthrough
case ABILITY_WATER_ABSORB:
case ABILITY_DRY_SKIN:
if (moveType == TYPE_WATER)
RETURN_SCORE_MINUS(20);
break;
@ -956,6 +965,10 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
if (moveType == TYPE_FIRE)
RETURN_SCORE_MINUS(20);
break;
case ABILITY_EARTH_EATER:
if (moveType == TYPE_GROUND)
RETURN_SCORE_MINUS(20);
break;
} // def ability checks
// target partner ability checks & not attacking partner
@ -2820,7 +2833,8 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
}
break;
case ABILITY_LIGHTNING_ROD:
if (moveType == TYPE_ELECTRIC
if (B_REDIRECT_ABILITY_IMMUNITY >= GEN_5
&& moveType == TYPE_ELECTRIC
&& HasMoveWithCategory(battlerAtkPartner, DAMAGE_CATEGORY_SPECIAL)
&& BattlerStatCanRise(battlerAtkPartner, atkPartnerAbility, STAT_SPATK))
{
@ -2836,7 +2850,8 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
}
break; // handled in AI_HPAware
case ABILITY_STORM_DRAIN:
if (moveType == TYPE_WATER
if (B_REDIRECT_ABILITY_IMMUNITY >= GEN_5
&& moveType == TYPE_WATER
&& HasMoveWithCategory(battlerAtkPartner, DAMAGE_CATEGORY_SPECIAL)
&& BattlerStatCanRise(battlerAtkPartner, atkPartnerAbility, STAT_SPATK))
{
@ -4266,7 +4281,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
case EFFECT_ION_DELUGE:
if ((aiData->abilities[battlerAtk] == ABILITY_VOLT_ABSORB
|| aiData->abilities[battlerAtk] == ABILITY_MOTOR_DRIVE
|| aiData->abilities[battlerAtk] == ABILITY_LIGHTNING_ROD)
|| (B_REDIRECT_ABILITY_IMMUNITY >= GEN_5 && aiData->abilities[battlerAtk] == ABILITY_LIGHTNING_ROD))
&& gMovesInfo[predictedMove].type == TYPE_NORMAL)
ADJUST_SCORE(DECENT_EFFECT);
break;
@ -4325,7 +4340,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
if (predictedMove != MOVE_NONE
&& (aiData->abilities[battlerAtk] == ABILITY_VOLT_ABSORB
|| aiData->abilities[battlerAtk] == ABILITY_MOTOR_DRIVE
|| aiData->abilities[battlerAtk] == ABILITY_LIGHTNING_ROD))
|| (B_REDIRECT_ABILITY_IMMUNITY >= GEN_5 && aiData->abilities[battlerAtk] == ABILITY_LIGHTNING_ROD)))
{
ADJUST_SCORE(DECENT_EFFECT);
}

View File

@ -334,32 +334,29 @@ static bool32 FindMonThatAbsorbsOpponentsMove(u32 battler, bool32 emitResult)
// Create an array of possible absorb abilities so the AI considers all of them
if (gMovesInfo[gLastLandedMoves[battler]].type == TYPE_FIRE)
{
absorbingTypeAbilities[0] = ABILITY_FLASH_FIRE;
numAbsorbingAbilities = 1;
absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_FLASH_FIRE;
}
else if (gMovesInfo[gLastLandedMoves[battler]].type == TYPE_WATER)
{
absorbingTypeAbilities[0] = ABILITY_WATER_ABSORB;
absorbingTypeAbilities[1] = ABILITY_STORM_DRAIN;
absorbingTypeAbilities[2] = ABILITY_DRY_SKIN;
numAbsorbingAbilities = 3;
absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_WATER_ABSORB;
absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_DRY_SKIN;
if (B_REDIRECT_ABILITY_IMMUNITY >= GEN_5)
absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_STORM_DRAIN;
}
else if (gMovesInfo[gLastLandedMoves[battler]].type == TYPE_ELECTRIC)
{
absorbingTypeAbilities[0] = ABILITY_VOLT_ABSORB;
absorbingTypeAbilities[1] = ABILITY_MOTOR_DRIVE;
absorbingTypeAbilities[2] = ABILITY_LIGHTNING_ROD;
numAbsorbingAbilities = 3;
absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_VOLT_ABSORB;
absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_MOTOR_DRIVE;
if (B_REDIRECT_ABILITY_IMMUNITY >= GEN_5)
absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_LIGHTNING_ROD;
}
else if (gMovesInfo[gLastLandedMoves[battler]].type == TYPE_GRASS)
{
absorbingTypeAbilities[0] = ABILITY_SAP_SIPPER;
numAbsorbingAbilities = 1;
absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_SAP_SIPPER;
}
else if (gMovesInfo[gLastLandedMoves[battler]].type == TYPE_GROUND)
{
absorbingTypeAbilities[0] = ABILITY_EARTH_EATER;
numAbsorbingAbilities = 1;
absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_EARTH_EATER;
}
else
{

View File

@ -385,15 +385,21 @@ bool32 IsDamageMoveUnusable(u32 move, u32 battlerAtk, u32 battlerDef)
switch (battlerDefAbility)
{
case ABILITY_LIGHTNING_ROD:
if (B_REDIRECT_ABILITY_IMMUNITY < GEN_5)
break;
// Fallthrough
case ABILITY_VOLT_ABSORB:
case ABILITY_MOTOR_DRIVE:
case ABILITY_LIGHTNING_ROD:
if (moveType == TYPE_ELECTRIC)
return TRUE;
break;
case ABILITY_STORM_DRAIN:
if (B_REDIRECT_ABILITY_IMMUNITY < GEN_5)
break;
// Fallthrough
case ABILITY_WATER_ABSORB:
case ABILITY_DRY_SKIN:
case ABILITY_STORM_DRAIN:
if (moveType == TYPE_WATER)
return TRUE;
break;

View File

@ -8699,7 +8699,7 @@ static bool32 IsRototillerAffected(u32 battler)
return TRUE;
}
static bool32 IsElectricAbilityAffected(u32 ability)
static bool32 IsElectricAbilityAffected(u32 battler, u32 ability)
{
u32 moveType;
@ -8710,7 +8710,9 @@ static bool32 IsElectricAbilityAffected(u32 ability)
else
moveType = gMovesInfo[gCurrentMove].type;
if (moveType == TYPE_ELECTRIC && GetBattlerAbility(gBattlerTarget) == ability)
if (moveType == TYPE_ELECTRIC
&& (ability != ABILITY_LIGHTNING_ROD || B_REDIRECT_ABILITY_IMMUNITY >= GEN_5)
&& GetBattlerAbility(battler) == ability)
return TRUE;
else
return FALSE;
@ -16346,28 +16348,12 @@ void BS_JumpIfEmergencyExited(void)
gBattlescriptCurrInstr = cmd->nextInstr;
}
void BS_JumpIfRod(void)
void BS_JumpIfElectricAbilityAffected(void)
{
NATIVE_ARGS(const u8 *jumpInstr);
if (IsElectricAbilityAffected(ABILITY_LIGHTNING_ROD))
gBattlescriptCurrInstr = cmd->jumpInstr;
else
gBattlescriptCurrInstr = cmd->nextInstr;
}
NATIVE_ARGS(u8 battler, u16 ability, const u8 *jumpInstr);
u32 battler = GetBattlerForBattleScript(cmd->battler);
void BS_JumpIfAbsorb(void)
{
NATIVE_ARGS(const u8 *jumpInstr);
if (IsElectricAbilityAffected(ABILITY_VOLT_ABSORB))
gBattlescriptCurrInstr = cmd->jumpInstr;
else
gBattlescriptCurrInstr = cmd->nextInstr;
}
void BS_JumpIfMotor(void)
{
NATIVE_ARGS(const u8 *jumpInstr);
if (IsElectricAbilityAffected(ABILITY_MOTOR_DRIVE))
if (IsElectricAbilityAffected(battler, cmd->ability))
gBattlescriptCurrInstr = cmd->jumpInstr;
else
gBattlescriptCurrInstr = cmd->nextInstr;

View File

@ -5080,11 +5080,11 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
effect = 2, statId = STAT_SPEED;
break;
case ABILITY_LIGHTNING_ROD:
if (moveType == TYPE_ELECTRIC && gMovesInfo[move].target != MOVE_TARGET_ALL_BATTLERS)
if (B_REDIRECT_ABILITY_IMMUNITY >= GEN_5 && moveType == TYPE_ELECTRIC && gMovesInfo[move].target != MOVE_TARGET_ALL_BATTLERS)
effect = 2, statId = STAT_SPATK;
break;
case ABILITY_STORM_DRAIN:
if (moveType == TYPE_WATER)
if (B_REDIRECT_ABILITY_IMMUNITY >= GEN_5 && moveType == TYPE_WATER)
effect = 2, statId = STAT_SPATK;
break;
case ABILITY_SAP_SIPPER:

View File

@ -0,0 +1,73 @@
#include "global.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Lightning Rod absorbs Electric-type moves and increases the Sp. Attack [Gen5+]")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_THUNDERBOLT].type == TYPE_ELECTRIC);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_RAICHU) { Ability(ABILITY_LIGHTNING_ROD); }
} WHEN {
TURN { MOVE(player, MOVE_THUNDERBOLT); MOVE(opponent, MOVE_CELEBRATE); }
} SCENE {
if (B_REDIRECT_ABILITY_IMMUNITY >= GEN_5) {
NONE_OF {
ANIMATION(ANIM_TYPE_MOVE, MOVE_THUNDERBOLT, player);
HP_BAR(opponent);
};
ABILITY_POPUP(opponent, ABILITY_LIGHTNING_ROD);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
MESSAGE("Foe Raichu's Sp. Atk rose!");
} else {
NONE_OF {
ABILITY_POPUP(opponent, ABILITY_LIGHTNING_ROD);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
MESSAGE("Foe Raichu's Sp. Atk rose!");
};
ANIMATION(ANIM_TYPE_MOVE, MOVE_THUNDERBOLT, player);
HP_BAR(opponent);
}
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponent);
}
}
DOUBLE_BATTLE_TEST("Lightning Rod forces single-target Electric-type moves to target the Pokémon with this Ability.")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_THUNDERBOLT].type == TYPE_ELECTRIC);
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_RAICHU) { Ability(ABILITY_LIGHTNING_ROD); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN {
MOVE(playerLeft, MOVE_THUNDERBOLT, target: opponentRight);
MOVE(playerRight, MOVE_THUNDERBOLT, target: opponentRight);
MOVE(opponentLeft, MOVE_CELEBRATE);
MOVE(opponentRight, MOVE_CELEBRATE);
}
} SCENE {
if (B_REDIRECT_ABILITY_IMMUNITY >= GEN_5) {
NONE_OF {
HP_BAR(opponentLeft);
HP_BAR(opponentRight);
};
ABILITY_POPUP(opponentLeft, ABILITY_LIGHTNING_ROD);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft);
MESSAGE("Foe Raichu's Sp. Atk rose!");
ABILITY_POPUP(opponentLeft, ABILITY_LIGHTNING_ROD);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft);
MESSAGE("Foe Raichu's Sp. Atk rose!");
} else {
NONE_OF {
HP_BAR(opponentRight);
};
ANIMATION(ANIM_TYPE_MOVE, MOVE_THUNDERBOLT, playerLeft);
HP_BAR(opponentLeft);
ANIMATION(ANIM_TYPE_MOVE, MOVE_THUNDERBOLT, playerRight);
HP_BAR(opponentLeft);
}
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponentLeft);
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponentRight);
}
}

View File

@ -0,0 +1,73 @@
#include "global.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Storm Drain absorbs Water-type moves and increases the Sp. Attack [Gen5+]")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_WATER_GUN].type == TYPE_WATER);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_GASTRODON_EAST_SEA) { Ability(ABILITY_STORM_DRAIN); }
} WHEN {
TURN { MOVE(player, MOVE_WATER_GUN); MOVE(opponent, MOVE_CELEBRATE); }
} SCENE {
if (B_REDIRECT_ABILITY_IMMUNITY >= GEN_5) {
NONE_OF {
ANIMATION(ANIM_TYPE_MOVE, MOVE_WATER_GUN, player);
HP_BAR(opponent);
};
ABILITY_POPUP(opponent, ABILITY_STORM_DRAIN);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
MESSAGE("Foe Gastrodon's Sp. Atk rose!");
} else {
NONE_OF {
ABILITY_POPUP(opponent, ABILITY_STORM_DRAIN);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
MESSAGE("Foe Gastrodon's Sp. Atk rose!");
};
ANIMATION(ANIM_TYPE_MOVE, MOVE_WATER_GUN, player);
HP_BAR(opponent);
}
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponent);
}
}
DOUBLE_BATTLE_TEST("Storm Drain forces single-target Water-type moves to target the Pokémon with this Ability.")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_WATER_GUN].type == TYPE_WATER);
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_GASTRODON_EAST_SEA) { Ability(ABILITY_STORM_DRAIN); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN {
MOVE(playerLeft, MOVE_WATER_GUN, target: opponentRight);
MOVE(playerRight, MOVE_WATER_GUN, target: opponentRight);
MOVE(opponentLeft, MOVE_CELEBRATE);
MOVE(opponentRight, MOVE_CELEBRATE);
}
} SCENE {
if (B_REDIRECT_ABILITY_IMMUNITY >= GEN_5) {
NONE_OF {
HP_BAR(opponentLeft);
HP_BAR(opponentRight);
};
ABILITY_POPUP(opponentLeft, ABILITY_STORM_DRAIN);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft);
MESSAGE("Foe Gastrodon's Sp. Atk rose!");
ABILITY_POPUP(opponentLeft, ABILITY_STORM_DRAIN);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft);
MESSAGE("Foe Gastrodon's Sp. Atk rose!");
} else {
NONE_OF {
HP_BAR(opponentRight);
};
ANIMATION(ANIM_TYPE_MOVE, MOVE_WATER_GUN, playerLeft);
HP_BAR(opponentLeft);
ANIMATION(ANIM_TYPE_MOVE, MOVE_WATER_GUN, playerRight);
HP_BAR(opponentLeft);
}
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponentLeft);
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponentRight);
}
}

View File

@ -41,7 +41,7 @@ WILD_BATTLE_TEST("Ion Deluge works the same way as always when used by a mon wit
NONE_OF {
ABILITY_POPUP(opponent, ability);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
MESSAGE("Wild Zebstrika's Sp.Atk rose!");
MESSAGE("Wild Zebstrika's Sp. Atk rose!");
MESSAGE("Wild Zebstrika's Speed rose!");
}
MESSAGE("A deluge of ions showers the battlefield!");

View File

@ -67,7 +67,8 @@ SINGLE_BATTLE_TEST("Dire Claw cannot poison/paralyze/cause to fall asleep pokemo
u8 statusAnim;
u16 species, ability;
u32 rng;
PARAMETRIZE { statusAnim = B_ANIM_STATUS_PRZ; rng = MOVE_EFFECT_PARALYSIS; species = SPECIES_RAICHU; ability = ABILITY_LIGHTNING_ROD; }
if (B_REDIRECT_ABILITY_IMMUNITY >= GEN_5)
PARAMETRIZE { statusAnim = B_ANIM_STATUS_PRZ; rng = MOVE_EFFECT_PARALYSIS; species = SPECIES_RAICHU; ability = ABILITY_LIGHTNING_ROD; }
PARAMETRIZE { statusAnim = B_ANIM_STATUS_PRZ; rng = MOVE_EFFECT_PARALYSIS; species = SPECIES_JOLTEON; ability = ABILITY_VOLT_ABSORB; }
PARAMETRIZE { statusAnim = B_ANIM_STATUS_PRZ; rng = MOVE_EFFECT_PARALYSIS; species = SPECIES_ELECTIVIRE; ability = ABILITY_MOTOR_DRIVE; }
PARAMETRIZE { statusAnim = B_ANIM_STATUS_PSN; rng = MOVE_EFFECT_POISON; species = SPECIES_ZANGOOSE; ability = ABILITY_IMMUNITY; }