mirror of
https://github.com/Ninjdai1/pokeemerald.git
synced 2025-01-13 07:03:40 +01:00
Merge branch 'battle_engine' of github.com:rh-hideout/pokeemerald-expansion into battle_engine_sync
This commit is contained in:
commit
7369f60c06
@ -1884,6 +1884,10 @@
|
||||
.2byte \species
|
||||
.4byte \ptr
|
||||
.endm
|
||||
|
||||
.macro tryendneutralizinggas battler:req
|
||||
various \battler, VARIOUS_TRY_END_NEUTRALIZING_GAS
|
||||
.endm
|
||||
|
||||
.macro trytoapplymimicry battler:req, ptr:req
|
||||
various \battler, VARIOUS_TRY_TO_APPLY_MIMICRY
|
||||
|
@ -2187,6 +2187,7 @@ BattleScript_EffectSimpleBeam:
|
||||
trytoclearprimalweather
|
||||
printstring STRINGID_EMPTYSTRING3
|
||||
waitmessage 1
|
||||
tryendneutralizinggas BS_TARGET
|
||||
goto BattleScript_MoveEnd
|
||||
|
||||
BattleScript_EffectSuckerPunch:
|
||||
@ -2409,6 +2410,7 @@ BattleScript_EffectGastroAcid:
|
||||
trytoclearprimalweather
|
||||
printstring STRINGID_EMPTYSTRING3
|
||||
waitmessage 1
|
||||
tryendneutralizinggas BS_TARGET
|
||||
goto BattleScript_MoveEnd
|
||||
|
||||
BattleScript_EffectToxicSpikes:
|
||||
@ -8383,6 +8385,12 @@ BattleScript_SwitchInAbilityMsg::
|
||||
waitmessage B_WAIT_TIME_LONG
|
||||
end3
|
||||
|
||||
BattleScript_SwitchInAbilityMsgRet::
|
||||
call BattleScript_AbilityPopUp
|
||||
printfromtable gSwitchInAbilityStringIds
|
||||
waitmessage B_WAIT_TIME_LONG
|
||||
return
|
||||
|
||||
BattleScript_ActivateAsOne::
|
||||
call BattleScript_AbilityPopUp
|
||||
printfromtable gSwitchInAbilityStringIds
|
||||
@ -9237,3 +9245,19 @@ BattleScript_PastelVeilLoopIncrement:
|
||||
goto BattleScript_PastelVeilEnd
|
||||
BattleScript_PastelVeilEnd:
|
||||
end3
|
||||
|
||||
sByteFour:
|
||||
.byte MAX_BATTLERS_COUNT
|
||||
|
||||
BattleScript_NeutralizingGasExits::
|
||||
savetarget
|
||||
pause B_WAIT_TIME_SHORT
|
||||
printstring STRINGID_NEUTRALIZINGGASOVER
|
||||
waitmessage B_WAIT_TIME_LONG
|
||||
setbyte gBattlerTarget, 0
|
||||
BattleScript_NeutralizingGasExitsLoop:
|
||||
switchinabilities BS_TARGET
|
||||
addbyte gBattlerTarget, 1
|
||||
jumpifbytenotequal gBattlerTarget, sByteFour, BattleScript_NeutralizingGasExitsLoop @ SOMEHOW, comparing to gBattlersCount is problematic.
|
||||
restoretarget
|
||||
return
|
||||
|
@ -51,12 +51,13 @@ struct ResourceFlags
|
||||
u32 flags[4];
|
||||
};
|
||||
|
||||
#define RESOURCE_FLAG_FLASH_FIRE 0x1
|
||||
#define RESOURCE_FLAG_ROOST 0x2
|
||||
#define RESOURCE_FLAG_UNBURDEN 0x4
|
||||
#define RESOURCE_FLAG_INTIMIDATED 0x8
|
||||
#define RESOURCE_FLAG_TRACED 0x10
|
||||
#define RESOURCE_FLAG_EMERGENCY_EXIT 0x20
|
||||
#define RESOURCE_FLAG_FLASH_FIRE 0x1
|
||||
#define RESOURCE_FLAG_ROOST 0x2
|
||||
#define RESOURCE_FLAG_UNBURDEN 0x4
|
||||
#define RESOURCE_FLAG_INTIMIDATED 0x8
|
||||
#define RESOURCE_FLAG_TRACED 0x10
|
||||
#define RESOURCE_FLAG_EMERGENCY_EXIT 0x20
|
||||
#define RESOURCE_FLAG_NEUTRALIZING_GAS 0x40
|
||||
|
||||
struct DisableStruct
|
||||
{
|
||||
@ -173,6 +174,8 @@ struct SpecialStatus
|
||||
u8 damagedMons:4; // Mons that have been damaged directly by using a move, includes substitute.
|
||||
u8 dancerUsedMove:1;
|
||||
u8 dancerOriginalTarget:3;
|
||||
u8 announceNeutralizingGas:1; // See Cmd_switchineffects
|
||||
u8 neutralizingGasRemoved:1; // See VARIOUS_TRY_END_NEUTRALIZING_GAS
|
||||
s32 dmg;
|
||||
s32 physicalDmg;
|
||||
s32 specialDmg;
|
||||
@ -225,7 +228,7 @@ struct WishFutureKnock
|
||||
u8 futureSightAttacker[MAX_BATTLERS_COUNT];
|
||||
u16 futureSightMove[MAX_BATTLERS_COUNT];
|
||||
u8 wishCounter[MAX_BATTLERS_COUNT];
|
||||
u8 wishMonId[MAX_BATTLERS_COUNT];
|
||||
u8 wishPartyId[MAX_BATTLERS_COUNT];
|
||||
u8 weatherDuration;
|
||||
u8 knockedOffMons[2]; // Each battler is represented by a bit. The array entry is dependent on the battler's side.
|
||||
};
|
||||
|
@ -1,9 +1,9 @@
|
||||
#ifndef GUARD_BATTLE_AI_UTIL_H
|
||||
#define GUARD_BATTLE_AI_UTIL_H
|
||||
|
||||
// for IsAiFaster
|
||||
#define AI_CHECK_FASTER 0 // if_user_faster
|
||||
#define AI_CHECK_SLOWER 1 // if_target_faster
|
||||
// for AI_WhoStrikesFirst
|
||||
#define AI_IS_FASTER 0
|
||||
#define AI_IS_SLOWER 1
|
||||
|
||||
#define FOE(battler) ((battler ^ BIT_SIDE) & BIT_SIDE)
|
||||
|
||||
@ -21,12 +21,13 @@ void SaveBattlerData(u8 battlerId);
|
||||
void SetBattlerData(u8 battlerId);
|
||||
void RestoreBattlerData(u8 battlerId);
|
||||
|
||||
bool32 WillAIStrikeFirst(void);
|
||||
u32 GetTotalBaseStat(u32 species);
|
||||
bool32 IsTruantMonVulnerable(u32 battlerAI, u32 opposingBattler);
|
||||
bool32 AtMaxHp(u8 battler);
|
||||
u32 GetHealthPercentage(u8 battler);
|
||||
bool32 IsBattlerTrapped(u8 battler, bool8 switching);
|
||||
bool32 IsAiFaster(u8 battler);
|
||||
u8 AI_WhoStrikesFirst(u8 battlerAI, u8 battler2);
|
||||
bool32 CanTargetFaintAi(u8 battlerDef, u8 battlerAtk);
|
||||
bool32 CanMoveFaintBattler(u16 move, u8 battlerDef, u8 battlerAtk, u8 nHits);
|
||||
bool32 CanTargetFaintAiWithMod(u8 battlerDef, u8 battlerAtk, s32 hpMod, s32 dmgMod);
|
||||
@ -52,10 +53,13 @@ bool32 ShouldRecover(u8 battlerAtk, u8 battlerDef, u16 move, u8 healPercent);
|
||||
bool32 ShouldSetScreen(u8 battlerAtk, u8 battlerDef, u16 moveEffect);
|
||||
bool32 ShouldPivot(u8 battlerAtk, u8 battlerDef, u16 defAbility, u16 move, u8 moveIndex);
|
||||
bool32 IsRecycleEncouragedItem(u16 item);
|
||||
bool32 ShouldRestoreHpBerry(u8 battlerAtk, u16 item);
|
||||
bool32 IsStatBoostingBerry(u16 item);
|
||||
bool32 CanKnockOffItem(u8 battler, u16 item);
|
||||
bool32 IsAbilityOfRating(u16 ability, s8 rating);
|
||||
s8 GetAbilityRating(u16 ability);
|
||||
bool32 AI_IsAbilityOnSide(u32 battlerId, u32 ability);
|
||||
bool32 AI_MoveMakesContact(u32 ability, u32 holdEffect, u16 move);
|
||||
|
||||
// stat stage checks
|
||||
bool32 AnyStatIsRaised(u8 battlerId);
|
||||
|
@ -261,6 +261,7 @@ extern const u8 BattleScript_AttackerAbilityStatRaiseEnd3[];
|
||||
extern const u8 BattleScript_PoisonHealActivates[];
|
||||
extern const u8 BattleScript_BadDreamsActivates[];
|
||||
extern const u8 BattleScript_SwitchInAbilityMsg[];
|
||||
extern const u8 BattleScript_SwitchInAbilityMsgRet[];
|
||||
extern const u8 BattleScript_ToxicSpikesPoisoned[];
|
||||
extern const u8 BattleScript_ToxicSpikesAbsorbed[];
|
||||
extern const u8 BattleScript_StickyWebOnSwitchIn[];
|
||||
@ -413,5 +414,6 @@ extern const u8 BattleScript_AttackerFormChangeEnd3NoPopup[];
|
||||
extern const u8 BattleScript_AttackerFormChangeMoveEffect[];
|
||||
extern const u8 BattleScript_BothCanNoLongerEscape[];
|
||||
extern const u8 BattleScript_OctolockEndTurn[];
|
||||
extern const u8 BattleScript_NeutralizingGasExits[];
|
||||
|
||||
#endif // GUARD_BATTLE_SCRIPTS_H
|
||||
|
@ -24,6 +24,7 @@
|
||||
#define ABILITYEFFECT_TRACE1 12
|
||||
#define ABILITYEFFECT_TRACE2 13
|
||||
#define ABILITYEFFECT_MOVE_END_OTHER 14
|
||||
#define ABILITYEFFECT_NEUTRALIZINGGAS 15
|
||||
#define ABILITYEFFECT_SWITCH_IN_TERRAIN 254
|
||||
#define ABILITYEFFECT_SWITCH_IN_WEATHER 255
|
||||
|
||||
|
@ -51,6 +51,7 @@
|
||||
#define SPECIES_MELOETTA_PIROUETTE 10019
|
||||
#define SPECIES_MORPEKO 0
|
||||
#define SPECIES_MORPEKO_HANGRY 10020
|
||||
#define SPECIES_SIRFETCHD 10021
|
||||
#endif
|
||||
|
||||
// Items with peculiar battle effects.
|
||||
@ -91,6 +92,9 @@
|
||||
#define GEN_8 5
|
||||
#endif
|
||||
|
||||
// Mega Evolution settings
|
||||
#define B_MEGA_EVO_TURN_ORDER GEN_7 // In Gen7, a Pokémon's Speed after Mega Evolution is used to determine turn order, not its Speed before.
|
||||
|
||||
// Calculation settings
|
||||
#define B_CRIT_CHANCE GEN_7 // Chances of a critical hit landing. See CalcCritChanceStage.
|
||||
#define B_CRIT_MULTIPLIER GEN_7 // In Gen6+, critical hits multiply damage by 1.5 instead of 2.
|
||||
@ -114,6 +118,7 @@
|
||||
#define B_PAYBACK_SWITCH_BOOST GEN_7 // In Gen5+, if the opponent switches out, Payback's damage will no longer be doubled.
|
||||
#define B_HIDDEN_POWER_DMG GEN_7 // In Gen6+, Hidden Power's base power was set to always be 60. Before, it was determined by the mon's IVs.
|
||||
#define B_ROUGH_SKIN_DMG GEN_7 // In Gen4+, Rough Skin contact damage is 1/8th of max HP instead of 1/16th. This will also affect Iron Barbs.
|
||||
#define B_KNOCK_OFF_DMG GEN_8 // In Gen6+, Knock Off deals 50% more damage when knocking off an item
|
||||
|
||||
// Type settings
|
||||
#define B_GHOSTS_ESCAPE GEN_7 // In Gen6+, abilities like Shadow Tag or moves like Mean Look fail on Ghost-type Pokémon. They can also escape any Wild Battle.
|
||||
@ -154,6 +159,7 @@
|
||||
#define B_SKILL_SWAP GEN_7 // In Gen4+, Skill Swap triggers switch-in abilities after use.
|
||||
#define B_BRICK_BREAK GEN_7 // In Gen4+, you can destroy your own side's screens. In Gen 5+, screens are not removed if the target is immune.
|
||||
#define B_WISH_HP_SOURCE GEN_7 // In Gen5+, Wish heals half of the user's max HP instead of the target's.
|
||||
#define B_RAMPAGE_CANCELLING GEN_7 // In Gen5+, a failed Thrash, etc, will cancel except on its last turn.
|
||||
|
||||
// Ability settings
|
||||
#define B_ABILITY_WEATHER GEN_7 // In Gen6+, ability-induced weather lasts 5 turns. Before, it lasted until the battle ended or until it was changed by a move or a different weather-affecting ability.
|
||||
@ -184,6 +190,7 @@
|
||||
#define B_HEAVY_BALL_MODIFIER GEN_7 // In Gen7+, Heavy Ball's ranges change. See Cmd_handleballthrow.
|
||||
#define B_DREAM_BALL_MODIFIER GEN_8 // In Gen8, Dream Ball's catch multiplier is x4 when the target is asleep or has the ability Comatose.
|
||||
#define B_SERENE_GRACE_BOOST GEN_7 // In Gen5+, Serene Grace boosts the added flinch chance of King's Rock and Razor Fang.
|
||||
#define B_LEEK_ALWAYS_CRIT GEN_7 // In Gen6+, if a Farfetch'd or Sirfetch'd holding a Leek use a move with increased Critical Hit ratio, it will always result in a Critical Hit.
|
||||
|
||||
// Flag settings
|
||||
// To use the following features in scripting, replace the 0s with the flag ID you're assigning it to.
|
||||
|
@ -224,6 +224,7 @@
|
||||
#define VARIOUS_CHECK_POLTERGEIST 134
|
||||
#define VARIOUS_SET_OCTOLOCK 135
|
||||
#define VARIOUS_CUT_1_3_HP_RAISE_STATS 136
|
||||
#define VARIOUS_TRY_END_NEUTRALIZING_GAS 137
|
||||
|
||||
// Cmd_manipulatedamage
|
||||
#define DMG_CHANGE_SIGN 0
|
||||
|
@ -607,8 +607,10 @@
|
||||
#define STRINGID_PKMNBECAMEWEAKERTOFIRE 604
|
||||
#define STRINGID_ABOUTTOUSEPOLTERGEIST 605
|
||||
#define STRINGID_CANTESCAPEBECAUSEOFCURRENTMOVE 606
|
||||
#define STRINGID_NEUTRALIZINGGASENTERS 607
|
||||
#define STRINGID_NEUTRALIZINGGASOVER 608
|
||||
|
||||
#define BATTLESTRINGS_COUNT 607
|
||||
#define BATTLESTRINGS_COUNT 609
|
||||
|
||||
// The below IDs are all indexes into battle message tables,
|
||||
// used to determine which of a set of messages to print.
|
||||
@ -844,6 +846,7 @@
|
||||
#define B_MSG_SWITCHIN_ASONE 13
|
||||
#define B_MSG_SWITCHIN_CURIOUS_MEDICINE 14
|
||||
#define B_MSG_SWITCHIN_PASTEL_VEIL 15
|
||||
#define B_MSG_SWITCHIN_NEUTRALIZING_GAS 16
|
||||
|
||||
// gMentalHerbCureStringIds
|
||||
#define B_MSG_MENTALHERBCURE_INFATUATION 0
|
||||
|
@ -62,12 +62,12 @@
|
||||
#define HOLD_EFFECT_FIRE_POWER 58
|
||||
#define HOLD_EFFECT_DRAGON_POWER 59
|
||||
#define HOLD_EFFECT_NORMAL_POWER 60
|
||||
#define HOLD_EFFECT_UP_GRADE 61
|
||||
#define HOLD_EFFECT_UPGRADE 61
|
||||
#define HOLD_EFFECT_SHELL_BELL 62
|
||||
#define HOLD_EFFECT_LUCKY_PUNCH 63
|
||||
#define HOLD_EFFECT_METAL_POWDER 64
|
||||
#define HOLD_EFFECT_THICK_CLUB 65
|
||||
#define HOLD_EFFECT_STICK 66
|
||||
#define HOLD_EFFECT_LEEK 66
|
||||
|
||||
// Gen4 hold effects.
|
||||
#define HOLD_EFFECT_CHOICE_SCARF 67
|
||||
|
@ -278,6 +278,7 @@ static u8 ChooseMoveOrAction_Singles(void)
|
||||
return AI_CHOICE_WATCH;
|
||||
|
||||
gActiveBattler = sBattler_AI;
|
||||
|
||||
// If can switch.
|
||||
if (CountUsablePartyMons(sBattler_AI) > 0
|
||||
&& !IsAbilityPreventingEscape(sBattler_AI)
|
||||
@ -318,7 +319,7 @@ static u8 ChooseMoveOrAction_Singles(void)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
numOfBestMoves = 1;
|
||||
currentMoveArray[0] = AI_THINKING_STRUCT->score[0];
|
||||
consideredMoveArray[0] = 0;
|
||||
@ -559,7 +560,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
}
|
||||
|
||||
// check off screen
|
||||
if (IsSemiInvulnerable(battlerDef, move) && moveEffect != EFFECT_SEMI_INVULNERABLE && GetWhoStrikesFirst(battlerAtk, battlerDef, TRUE) != 1)
|
||||
if (IsSemiInvulnerable(battlerDef, move) && moveEffect != EFFECT_SEMI_INVULNERABLE && AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_FASTER)
|
||||
RETURN_SCORE_MINUS(20); // if target off screen and we go first, don't use move
|
||||
|
||||
// check if negates type
|
||||
@ -1258,7 +1259,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
&& (B_MENTAL_HERB >= GEN_5 && AI_DATA->defHoldEffect != HOLD_EFFECT_MENTAL_HERB)
|
||||
&& !PartnerHasSameMoveEffectWithoutTarget(AI_DATA->battlerAtkPartner, move, AI_DATA->partnerMove))
|
||||
{
|
||||
if (GetWhoStrikesFirst(battlerAtk, battlerDef, TRUE) == 0) // attacker should go first
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_FASTER) // Attacker should go first
|
||||
{
|
||||
if (gLastMoves[battlerDef] == MOVE_NONE || gLastMoves[battlerDef] == 0xFFFF)
|
||||
score -= 10; // no anticipated move to disable
|
||||
@ -1278,7 +1279,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
&& (B_MENTAL_HERB >= GEN_5 && AI_DATA->defHoldEffect != HOLD_EFFECT_MENTAL_HERB)
|
||||
&& !DoesPartnerHaveSameMoveEffect(AI_DATA->battlerAtkPartner, battlerDef, move, AI_DATA->partnerMove))
|
||||
{
|
||||
if (GetWhoStrikesFirst(battlerAtk, battlerDef, TRUE) == 0) // attacker should go first
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_FASTER) // Attacker should go first
|
||||
{
|
||||
if (gLastMoves[battlerDef] == MOVE_NONE || gLastMoves[battlerDef] == 0xFFFF)
|
||||
score -= 10; // no anticipated move to encore
|
||||
@ -1392,22 +1393,22 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
}
|
||||
break;
|
||||
case EFFECT_SANDSTORM:
|
||||
if (gBattleWeather & B_WEATHER_SANDSTORM //TODO | B_WEATHER_PRIMAL_ANY)
|
||||
if (gBattleWeather & (B_WEATHER_SANDSTORM | B_WEATHER_PRIMAL_ANY)
|
||||
|| PartnerMoveEffectIsWeather(AI_DATA->battlerAtkPartner, AI_DATA->partnerMove))
|
||||
score -= 8;
|
||||
break;
|
||||
case EFFECT_SUNNY_DAY:
|
||||
if (gBattleWeather & B_WEATHER_SUN //TODO | B_WEATHER_PRIMAL_ANY)
|
||||
if (gBattleWeather & (B_WEATHER_SUN | B_WEATHER_PRIMAL_ANY)
|
||||
|| PartnerMoveEffectIsWeather(AI_DATA->battlerAtkPartner, AI_DATA->partnerMove))
|
||||
score -= 8;
|
||||
break;
|
||||
case EFFECT_RAIN_DANCE:
|
||||
if (gBattleWeather & B_WEATHER_RAIN //TODO | B_WEATHER_PRIMAL_ANY)
|
||||
if (gBattleWeather & (B_WEATHER_RAIN | B_WEATHER_PRIMAL_ANY)
|
||||
|| PartnerMoveEffectIsWeather(AI_DATA->battlerAtkPartner, AI_DATA->partnerMove))
|
||||
score -= 8;
|
||||
break;
|
||||
case EFFECT_HAIL:
|
||||
if (gBattleWeather & B_WEATHER_HAIL //TODO | B_WEATHER_PRIMAL_ANY)
|
||||
if (gBattleWeather & (B_WEATHER_HAIL | B_WEATHER_PRIMAL_ANY)
|
||||
|| PartnerMoveEffectIsWeather(AI_DATA->battlerAtkPartner, AI_DATA->partnerMove))
|
||||
score -= 8;
|
||||
break;
|
||||
@ -1719,7 +1720,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
break;
|
||||
case EFFECT_SPITE:
|
||||
case EFFECT_MIMIC:
|
||||
if (GetWhoStrikesFirst(battlerAtk, battlerDef, TRUE) == 0) // attacker should go first
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_FASTER) // Attacker should go first
|
||||
{
|
||||
if (gLastMoves[battlerDef] == MOVE_NONE
|
||||
|| gLastMoves[battlerDef] == 0xFFFF)
|
||||
@ -1876,7 +1877,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
if (isDoubleBattle)
|
||||
{
|
||||
if (IsHazardMoveEffect(gBattleMoves[AI_DATA->partnerMove].effect) // partner is going to set up hazards
|
||||
&& GetWhoStrikesFirst(AI_DATA->battlerAtkPartner, battlerAtk, FALSE)) // partner is going to set up before the potential Defog
|
||||
&& AI_WhoStrikesFirst(AI_DATA->battlerAtkPartner, battlerAtk) == AI_IS_FASTER) // partner is going to set up before the potential Defog
|
||||
{
|
||||
score -= 10;
|
||||
break; // Don't use Defog if partner is going to set up hazards
|
||||
@ -1914,7 +1915,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
break;
|
||||
case EFFECT_SEMI_INVULNERABLE:
|
||||
if (predictedMove != MOVE_NONE
|
||||
&& GetWhoStrikesFirst(battlerAtk, battlerDef, TRUE) == 1
|
||||
&& AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_SLOWER
|
||||
&& gBattleMoves[predictedMove].effect == EFFECT_SEMI_INVULNERABLE)
|
||||
score -= 10; // Don't Fly/dig/etc if opponent is going to fly/dig/etc after you
|
||||
|
||||
@ -2095,7 +2096,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
case EFFECT_ME_FIRST:
|
||||
if (predictedMove != MOVE_NONE)
|
||||
{
|
||||
if (GetWhoStrikesFirst(battlerAtk, battlerDef, TRUE) == 1)
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_SLOWER)
|
||||
score -= 10; // Target is predicted to go first, Me First will fail
|
||||
else
|
||||
return AI_CheckBadMove(battlerAtk, battlerDef, predictedMove, score);
|
||||
@ -2282,7 +2283,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
}
|
||||
break;
|
||||
case EFFECT_ELECTRIFY:
|
||||
if (GetWhoStrikesFirst(battlerAtk, battlerDef, TRUE) == 0
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_FASTER
|
||||
//|| GetMoveTypeSpecial(battlerDef, predictedMove) == TYPE_ELECTRIC // Move will already be electric type
|
||||
|| PartnerMoveIsSameAsAttacker(AI_DATA->battlerAtkPartner, battlerDef, move, AI_DATA->partnerMove))
|
||||
score -= 10;
|
||||
@ -2311,7 +2312,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
case EFFECT_INSTRUCT:
|
||||
{
|
||||
u16 instructedMove;
|
||||
if (GetWhoStrikesFirst(battlerAtk, battlerDef, TRUE) == 1)
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_SLOWER)
|
||||
instructedMove = predictedMove;
|
||||
else
|
||||
instructedMove = gLastMoves[battlerDef];
|
||||
@ -2320,7 +2321,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
|| IsInstructBannedMove(instructedMove)
|
||||
|| MoveRequiresRecharging(instructedMove)
|
||||
|| MoveCallsOtherMove(instructedMove)
|
||||
#ifdef ITEM_Z_RING
|
||||
#ifdef ITEM_Z_POWER_RING
|
||||
//|| (IsZMove(instructedMove))
|
||||
#endif
|
||||
|| (gLockedMoves[battlerDef] != 0 && gLockedMoves[battlerDef] != 0xFFFF)
|
||||
@ -2351,21 +2352,21 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
break;
|
||||
case EFFECT_QUASH:
|
||||
if (!isDoubleBattle
|
||||
|| GetWhoStrikesFirst(battlerAtk, battlerDef, TRUE) == 1
|
||||
|| AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_SLOWER
|
||||
|| PartnerMoveIsSameAsAttacker(AI_DATA->battlerAtkPartner, battlerDef, move, AI_DATA->partnerMove))
|
||||
score -= 10;
|
||||
break;
|
||||
case EFFECT_AFTER_YOU:
|
||||
if (!IsTargetingPartner(battlerAtk, battlerDef)
|
||||
|| !isDoubleBattle
|
||||
|| GetWhoStrikesFirst(battlerAtk, battlerDef, TRUE) == 1
|
||||
|| AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_SLOWER
|
||||
|| PartnerMoveIsSameAsAttacker(AI_DATA->battlerAtkPartner, battlerDef, move, AI_DATA->partnerMove))
|
||||
score -= 10;
|
||||
break;
|
||||
case EFFECT_SUCKER_PUNCH:
|
||||
if (predictedMove != MOVE_NONE)
|
||||
{
|
||||
if (IS_MOVE_STATUS(predictedMove) || GetWhoStrikesFirst(battlerAtk, battlerDef, TRUE) == 1) // opponent going first
|
||||
if (IS_MOVE_STATUS(predictedMove) || AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_SLOWER) // Opponent going first
|
||||
score -= 10;
|
||||
}
|
||||
break;
|
||||
@ -2413,11 +2414,11 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
score--;
|
||||
break;
|
||||
case EFFECT_VITAL_THROW:
|
||||
if (IsAiFaster(AI_CHECK_FASTER) && GetHealthPercentage(battlerAtk) < 40)
|
||||
if (WillAIStrikeFirst() && GetHealthPercentage(battlerAtk) < 40)
|
||||
score--; // don't want to move last
|
||||
break;
|
||||
case EFFECT_FLAIL:
|
||||
if (GetWhoStrikesFirst(battlerAtk, battlerDef, TRUE) == 1 // opponent should go first
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_SLOWER // Opponent should go first
|
||||
|| GetHealthPercentage(battlerAtk) > 50)
|
||||
score -= 4;
|
||||
break;
|
||||
@ -2468,7 +2469,7 @@ static s16 AI_TryToFaint(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
if (CanIndexMoveFaintTarget(battlerAtk, battlerDef, AI_THINKING_STRUCT->movesetIndex, 0) && gBattleMoves[move].effect != EFFECT_EXPLOSION)
|
||||
{
|
||||
// this move can faint the target
|
||||
if (GetWhoStrikesFirst(battlerAtk, battlerDef, TRUE) == 0 || GetMovePriority(battlerAtk, move) > 0)
|
||||
if (!WillAIStrikeFirst() || GetMovePriority(battlerAtk, move) > 0)
|
||||
score += 4; // we go first or we're using priority move
|
||||
else
|
||||
score += 2;
|
||||
@ -2516,7 +2517,7 @@ static s16 AI_TryToFaint(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
}
|
||||
|
||||
//AI_TryToFaint_CheckIfDanger
|
||||
if (!IsAiFaster(AI_CHECK_FASTER) && CanTargetFaintAi(battlerDef, battlerAtk))
|
||||
if (!WillAIStrikeFirst() && CanTargetFaintAi(battlerDef, battlerAtk))
|
||||
{ // AI_TryToFaint_Danger
|
||||
if (GetMoveDamageResult(move) != MOVE_POWER_BEST)
|
||||
score--;
|
||||
@ -2566,7 +2567,7 @@ static s16 AI_DoubleBattle(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
// Ally decided to use Frost Breath on us. we must have Anger Point as our ability
|
||||
if (AI_DATA->atkAbility == ABILITY_ANGER_POINT)
|
||||
{
|
||||
if (GetWhoStrikesFirst(battlerAtk, battlerAtkPartner, TRUE) == 1) // partner moving first
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerAtkPartner) == AI_IS_SLOWER) // Partner moving first
|
||||
{
|
||||
// discourage raising our attack since it's about to be maxed out
|
||||
if (IsAttackBoostMoveEffect(effect))
|
||||
@ -2833,22 +2834,22 @@ static s16 AI_DoubleBattle(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
case EFFECT_INSTRUCT:
|
||||
{
|
||||
u16 instructedMove;
|
||||
if (GetWhoStrikesFirst(battlerAtk, battlerAtkPartner, TRUE) == 0)
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerAtkPartner) == AI_IS_FASTER)
|
||||
instructedMove = AI_DATA->partnerMove;
|
||||
else
|
||||
instructedMove = gLastMoves[battlerAtkPartner];
|
||||
|
||||
if (instructedMove != MOVE_NONE
|
||||
&& !IS_MOVE_STATUS(instructedMove)
|
||||
&& gBattleMoves[instructedMove].target & (MOVE_TARGET_BOTH | MOVE_TARGET_FOES_AND_ALLY)) //Use instruct on multi-target moves
|
||||
&& gBattleMoves[instructedMove].target & (MOVE_TARGET_BOTH | MOVE_TARGET_FOES_AND_ALLY)) // Use instruct on multi-target moves
|
||||
{
|
||||
RETURN_SCORE_PLUS(1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EFFECT_AFTER_YOU:
|
||||
if (GetWhoStrikesFirst(battlerAtkPartner, FOE(battlerAtkPartner), TRUE) == 1 // opponent mon 1 goes before partner
|
||||
|| GetWhoStrikesFirst(battlerAtkPartner, BATTLE_PARTNER(FOE(battlerAtkPartner)), TRUE) == 1) // opponent mon 2 goes before partner
|
||||
if (AI_WhoStrikesFirst(battlerAtkPartner, FOE(battlerAtkPartner) == AI_IS_SLOWER) // Opponent mon 1 goes before partner
|
||||
|| AI_WhoStrikesFirst(battlerAtkPartner, BATTLE_PARTNER(FOE(battlerAtkPartner)) == AI_IS_SLOWER)) // Opponent mon 2 goes before partner
|
||||
{
|
||||
if (gBattleMoves[AI_DATA->partnerMove].effect == EFFECT_COUNTER || gBattleMoves[AI_DATA->partnerMove].effect == EFFECT_MIRROR_COAT)
|
||||
break; // These moves need to go last
|
||||
@ -2874,7 +2875,9 @@ static s16 AI_DoubleBattle(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
case EFFECT_EARTHQUAKE:
|
||||
case EFFECT_MAGNITUDE:
|
||||
if (!IsBattlerGrounded(battlerAtkPartner)
|
||||
|| (GetWhoStrikesFirst(battlerAtk, battlerAtkPartner, TRUE) == 1 && IsUngroundingEffect(gBattleMoves[AI_DATA->partnerMove].effect)))
|
||||
|| (IsBattlerGrounded(battlerAtkPartner)
|
||||
&& AI_WhoStrikesFirst(battlerAtk, battlerAtkPartner) == AI_IS_SLOWER
|
||||
&& IsUngroundingEffect(gBattleMoves[AI_DATA->partnerMove].effect)))
|
||||
score += 2;
|
||||
else if (IS_BATTLER_OF_TYPE(battlerAtkPartner, TYPE_FIRE)
|
||||
|| IS_BATTLER_OF_TYPE(battlerAtkPartner, TYPE_ELECTRIC)
|
||||
@ -2925,9 +2928,9 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
// check already dead
|
||||
if (!IsBattlerIncapacitated(battlerDef, AI_DATA->defAbility)
|
||||
&& CanTargetFaintAi(battlerAtk, battlerDef)
|
||||
&& GetWhoStrikesFirst(battlerAtk, battlerDef, TRUE) == 1) // opponent should go first
|
||||
&& AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_SLOWER) // Opponent should go first
|
||||
{
|
||||
if (atkPriority > 0)
|
||||
if (atkPriority > 0)
|
||||
score++;
|
||||
else
|
||||
score--;
|
||||
@ -2969,7 +2972,11 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
{
|
||||
case ABILITY_MOXIE:
|
||||
case ABILITY_BEAST_BOOST:
|
||||
if (GetWhoStrikesFirst(battlerAtk, battlerDef, TRUE) == 0) // attacker should go first
|
||||
case ABILITY_CHILLING_NEIGH:
|
||||
case ABILITY_GRIM_NEIGH:
|
||||
case ABILITY_AS_ONE_ICE_RIDER:
|
||||
case ABILITY_AS_ONE_SHADOW_RIDER:
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_FASTER) // Attacker should go first
|
||||
{
|
||||
if (CanIndexMoveFaintTarget(battlerAtk, battlerDef, AI_THINKING_STRUCT->movesetIndex, 0))
|
||||
score += 8; // prioritize killing target for stat boost
|
||||
@ -2980,7 +2987,6 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
// move effect checks
|
||||
switch (moveEffect)
|
||||
{
|
||||
|
||||
case EFFECT_HIT:
|
||||
break;
|
||||
case EFFECT_SLEEP:
|
||||
@ -3042,7 +3048,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
break;
|
||||
case EFFECT_SPEED_UP:
|
||||
case EFFECT_SPEED_UP_2:
|
||||
if (IsAiFaster(AI_CHECK_SLOWER))
|
||||
if (!WillAIStrikeFirst())
|
||||
{
|
||||
if (!AI_RandLessThan(70))
|
||||
score += 3;
|
||||
@ -3140,7 +3146,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
break;
|
||||
case EFFECT_SPEED_DOWN:
|
||||
case EFFECT_SPEED_DOWN_2:
|
||||
if (IsAiFaster(AI_CHECK_FASTER))
|
||||
if (WillAIStrikeFirst())
|
||||
score -= 3;
|
||||
else if (!AI_RandLessThan(70))
|
||||
score += 2;
|
||||
@ -3243,6 +3249,10 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
case EFFECT_MULTI_HIT:
|
||||
case EFFECT_DOUBLE_HIT:
|
||||
case EFFECT_TRIPLE_KICK:
|
||||
if (AI_MoveMakesContact(AI_DATA->atkAbility, AI_DATA->atkHoldEffect, move)
|
||||
&& AI_DATA->atkAbility != ABILITY_MAGIC_GUARD
|
||||
&& AI_DATA->defHoldEffect == HOLD_EFFECT_ROCKY_HELMET)
|
||||
score -= 2;
|
||||
break;
|
||||
case EFFECT_CONVERSION:
|
||||
if (!IS_BATTLER_OF_TYPE(battlerAtk, gBattleMoves[gBattleMons[battlerAtk].moves[0]].type))
|
||||
@ -3381,7 +3391,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
score += 2;
|
||||
break;
|
||||
case EFFECT_SPEED_DOWN_HIT:
|
||||
if (IsAiFaster(AI_CHECK_FASTER))
|
||||
if (WillAIStrikeFirst())
|
||||
score -= 2;
|
||||
else if (!AI_RandLessThan(70))
|
||||
score++;
|
||||
@ -3413,7 +3423,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
score++;
|
||||
break;
|
||||
case EFFECT_MIMIC:
|
||||
if (GetWhoStrikesFirst(battlerAtk, battlerDef, TRUE) == 0)
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_FASTER)
|
||||
{
|
||||
if (gLastMoves[battlerDef] != MOVE_NONE && gLastMoves[battlerDef] != 0xFFFF)
|
||||
return AI_CheckViability(battlerAtk, battlerDef, gLastMoves[battlerDef], score);
|
||||
@ -3472,7 +3482,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
if (gDisableStructs[battlerDef].disableTimer == 0
|
||||
&& (B_MENTAL_HERB >= GEN_5 && AI_DATA->defHoldEffect != HOLD_EFFECT_MENTAL_HERB)) // mental herb
|
||||
{
|
||||
if (GetWhoStrikesFirst(battlerAtk, battlerDef, TRUE) == 0) // AI goes first
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_FASTER) // AI goes first
|
||||
{
|
||||
if (gLastMoves[battlerDef] != MOVE_NONE
|
||||
&& gLastMoves[battlerDef] != 0xFFFF)
|
||||
@ -3522,11 +3532,11 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
score++;
|
||||
break;
|
||||
case EFFECT_SPEED_UP_HIT:
|
||||
if (AI_DATA->atkAbility == ABILITY_SERENE_GRACE && AI_DATA->defAbility != ABILITY_CONTRARY && IsAiFaster(AI_CHECK_SLOWER))
|
||||
if (AI_DATA->atkAbility == ABILITY_SERENE_GRACE && AI_DATA->defAbility != ABILITY_CONTRARY && !WillAIStrikeFirst())
|
||||
score += 3;
|
||||
break;
|
||||
case EFFECT_DESTINY_BOND:
|
||||
if (GetWhoStrikesFirst(battlerAtk, battlerDef, TRUE) == 0 && CanTargetFaintAi(battlerDef, battlerAtk))
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_FASTER && CanTargetFaintAi(battlerDef, battlerAtk))
|
||||
score += 3;
|
||||
break;
|
||||
case EFFECT_SPITE:
|
||||
@ -3774,7 +3784,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
&& AI_DATA->atkAbility != ABILITY_CONTRARY
|
||||
&& CanIndexMoveFaintTarget(battlerAtk, battlerDef, AI_THINKING_STRUCT->movesetIndex, 0))
|
||||
{
|
||||
if (GetWhoStrikesFirst(battlerAtk, battlerDef, TRUE) == 0) // Attacker goes first
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_FASTER) // Attacker goes first
|
||||
score += 9;
|
||||
else
|
||||
score += 3;
|
||||
@ -3819,7 +3829,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
score++;
|
||||
if (predictedMove != MOVE_NONE && !isDoubleBattle)
|
||||
{
|
||||
if (GetWhoStrikesFirst(battlerAtk, battlerDef, TRUE) == 0) // Attacker goes first
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_FASTER) // Attacker goes first
|
||||
{
|
||||
if (gBattleMoves[predictedMove].effect == EFFECT_EXPLOSION
|
||||
|| gBattleMoves[predictedMove].effect == EFFECT_PROTECT)
|
||||
@ -3886,7 +3896,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
break;
|
||||
case EFFECT_ATTRACT:
|
||||
if (!isDoubleBattle && BattlerWillFaintFromSecondaryDamage(battlerDef, AI_DATA->defAbility)
|
||||
&& GetWhoStrikesFirst(battlerAtk, battlerDef, TRUE) == 1) // Target goes first
|
||||
&& AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_SLOWER) // Target goes first
|
||||
break; // Don't use if the attract won't have a change to activate
|
||||
|
||||
if (gBattleMons[battlerDef].status1 & STATUS1_ANY
|
||||
@ -3931,7 +3941,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
if (isDoubleBattle)
|
||||
{
|
||||
if (IsHazardMoveEffect(gBattleMoves[AI_DATA->partnerMove].effect) // Partner is going to set up hazards
|
||||
&& GetWhoStrikesFirst(battlerAtk, AI_DATA->battlerAtkPartner, TRUE) == 1) // Partner going first
|
||||
&& AI_WhoStrikesFirst(battlerAtk, AI_DATA->battlerAtkPartner) == AI_IS_SLOWER) // Partner going first
|
||||
break; // Don't use Defog if partner is going to set up hazards
|
||||
}
|
||||
|
||||
@ -4101,6 +4111,18 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
score++;
|
||||
if (IsRecycleEncouragedItem(GetUsedHeldItem(battlerAtk)))
|
||||
score++;
|
||||
if (AI_DATA->atkAbility == ABILITY_RIPEN)
|
||||
{
|
||||
u16 item = GetUsedHeldItem(battlerAtk);
|
||||
u16 toHeal = (ItemId_GetHoldEffectParam(item) == 10) ? 10 : gBattleMons[battlerAtk].maxHP / ItemId_GetHoldEffectParam(item);
|
||||
|
||||
if (IsStatBoostingBerry(item) && atkHpPercent > 60)
|
||||
score++;
|
||||
else if (ShouldRestoreHpBerry(battlerAtk, item) && !CanAIFaintTarget(battlerAtk, battlerDef, 0)
|
||||
&& ((GetWhoStrikesFirst(battlerAtk, battlerDef, TRUE) == 0 && CanTargetFaintAiWithMod(battlerDef, battlerAtk, 0, 0))
|
||||
|| !CanTargetFaintAiWithMod(battlerDef, battlerAtk, toHeal, 0)))
|
||||
score++; // Recycle healing berry if we can't otherwise faint the target and the target wont kill us after we activate the berry
|
||||
}
|
||||
break;
|
||||
case EFFECT_BRICK_BREAK:
|
||||
if (gSideStatuses[GetBattlerSide(battlerDef)] & SIDE_STATUS_REFLECT)
|
||||
@ -4408,13 +4430,13 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
score++;
|
||||
break;
|
||||
case EFFECT_THROAT_CHOP:
|
||||
if (predictedMove != MOVE_NONE && TestMoveFlags(predictedMove, FLAG_SOUND) && GetWhoStrikesFirst(battlerAtk, battlerDef, TRUE) == 0)
|
||||
if (predictedMove != MOVE_NONE && TestMoveFlags(predictedMove, FLAG_SOUND) && AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_FASTER)
|
||||
score += 3; // Ai goes first and predicts the target will use a sound move
|
||||
else if (TestMoveFlagsInMoveset(battlerDef, FLAG_SOUND))
|
||||
score += 3;
|
||||
break;
|
||||
case EFFECT_HEAL_BLOCK:
|
||||
if (GetWhoStrikesFirst(battlerAtk, battlerDef, TRUE) == 0 && predictedMove != MOVE_NONE && IsHealingMoveEffect(gBattleMoves[predictedMove].effect))
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_FASTER && predictedMove != MOVE_NONE && IsHealingMoveEffect(gBattleMoves[predictedMove].effect))
|
||||
score += 3; // Try to cancel healing move
|
||||
else if (HasHealingEffect(battlerDef) || AI_DATA->defHoldEffect == HOLD_EFFECT_LEFTOVERS
|
||||
|| (AI_DATA->defHoldEffect == HOLD_EFFECT_BLACK_SLUDGE && IS_BATTLER_OF_TYPE(battlerDef, TYPE_POISON)))
|
||||
@ -4450,7 +4472,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
break;
|
||||
case EFFECT_QUASH:
|
||||
if (isDoubleBattle
|
||||
&& GetWhoStrikesFirst(AI_DATA->battlerAtkPartner, battlerDef, TRUE) == 1) // Attacker partner wouldn't go before target
|
||||
&& AI_WhoStrikesFirst(AI_DATA->battlerAtkPartner, battlerDef) == AI_IS_SLOWER) // Attacker partner wouldn't go before target
|
||||
score++;
|
||||
break;
|
||||
case EFFECT_TAILWIND:
|
||||
@ -4472,8 +4494,8 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
if (IsBattlerGrounded(battlerAtk) && HasDamagingMoveOfType(battlerDef, TYPE_ELECTRIC)
|
||||
&& !(AI_GetTypeEffectiveness(MOVE_EARTHQUAKE, battlerDef, battlerAtk) == AI_EFFECTIVENESS_x0)) // Doesn't resist ground move
|
||||
{
|
||||
if (GetWhoStrikesFirst(battlerAtk, battlerDef, TRUE) == 0) // Attacker goes first
|
||||
{
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_FASTER) // Attacker goes first
|
||||
{
|
||||
if (gBattleMoves[predictedMove].type == TYPE_GROUND)
|
||||
score += 3; // Cause the enemy's move to fail
|
||||
break;
|
||||
@ -4487,7 +4509,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
}
|
||||
break;
|
||||
case EFFECT_CAMOUFLAGE:
|
||||
if (predictedMove != MOVE_NONE && GetWhoStrikesFirst(battlerAtk, battlerDef, TRUE) == 0 // Attacker goes first
|
||||
if (predictedMove != MOVE_NONE && AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_FASTER // Attacker goes first
|
||||
&& !IS_MOVE_STATUS(move) && AI_GetTypeEffectiveness(predictedMove, battlerDef, battlerAtk) != AI_EFFECTIVENESS_x0)
|
||||
score++;
|
||||
break;
|
||||
@ -4530,7 +4552,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
}
|
||||
break;
|
||||
case EFFECT_FLAIL:
|
||||
if (GetWhoStrikesFirst(battlerAtk, battlerDef, TRUE) == 0) // Ai goes first
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_FASTER) // Ai goes first
|
||||
{
|
||||
if (GetHealthPercentage(battlerAtk) < 20)
|
||||
score++;
|
||||
@ -4572,7 +4594,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
score += 2;
|
||||
break;
|
||||
case EFFECT_ENDEAVOR:
|
||||
if (GetWhoStrikesFirst(battlerAtk, battlerDef, TRUE) == 1) // Opponent faster
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_SLOWER) // Opponent faster
|
||||
{
|
||||
if (GetHealthPercentage(battlerAtk) < 40)
|
||||
score++;
|
||||
@ -4603,7 +4625,7 @@ static s16 AI_SetupFirstTurn(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
return score;
|
||||
|
||||
if (AI_THINKING_STRUCT->aiFlags & AI_FLAG_SMART_SWITCHING
|
||||
&& GetWhoStrikesFirst(battlerAtk, battlerDef, TRUE) == 1
|
||||
&& AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_SLOWER
|
||||
&& CanTargetFaintAi(battlerDef, battlerAtk)
|
||||
&& GetMovePriority(battlerAtk, move) == 0)
|
||||
{
|
||||
|
@ -83,7 +83,7 @@ static bool8 ShouldSwitchIfWonderGuard(void)
|
||||
|
||||
opposingPosition = BATTLE_OPPOSITE(GetBattlerPosition(gActiveBattler));
|
||||
|
||||
if (gBattleMons[GetBattlerAtPosition(opposingPosition)].ability != ABILITY_WONDER_GUARD)
|
||||
if (GetBattlerAbility(GetBattlerAtPosition(opposingPosition)) != ABILITY_WONDER_GUARD)
|
||||
return FALSE;
|
||||
|
||||
// Check if Pokemon has a super effective move.
|
||||
@ -177,7 +177,7 @@ static bool8 FindMonThatAbsorbsOpponentsMove(void)
|
||||
else
|
||||
return FALSE;
|
||||
|
||||
if (gBattleMons[gActiveBattler].ability == absorbingTypeAbility)
|
||||
if (AI_GetAbility(gActiveBattler) == absorbingTypeAbility)
|
||||
return FALSE;
|
||||
|
||||
GetAIPartyIndexes(gActiveBattler, &firstId, &lastId);
|
||||
@ -229,7 +229,7 @@ static bool8 ShouldSwitchIfNaturalCure(void)
|
||||
{
|
||||
if (!(gBattleMons[gActiveBattler].status1 & STATUS1_SLEEP))
|
||||
return FALSE;
|
||||
if (gBattleMons[gActiveBattler].ability != ABILITY_NATURAL_CURE)
|
||||
if (AI_GetAbility(gActiveBattler) != ABILITY_NATURAL_CURE)
|
||||
return FALSE;
|
||||
if (gBattleMons[gActiveBattler].hp < gBattleMons[gActiveBattler].maxHP / 2)
|
||||
return FALSE;
|
||||
@ -437,6 +437,7 @@ bool32 ShouldSwitch(void)
|
||||
return FALSE;
|
||||
|
||||
availableToSwitch = 0;
|
||||
|
||||
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
|
||||
{
|
||||
battlerIn1 = gActiveBattler;
|
||||
@ -852,7 +853,8 @@ static bool8 ShouldUseItem(void)
|
||||
break;
|
||||
if (gBattleMons[gActiveBattler].hp == 0)
|
||||
break;
|
||||
if (gBattleMons[gActiveBattler].hp < gBattleMons[gActiveBattler].maxHP / 4 || gBattleMons[gActiveBattler].maxHP - gBattleMons[gActiveBattler].hp > itemEffects[paramOffset])
|
||||
if (gBattleMons[gActiveBattler].hp < gBattleMons[gActiveBattler].maxHP / 4
|
||||
|| gBattleMons[gActiveBattler].maxHP - gBattleMons[gActiveBattler].hp > itemEffects[paramOffset])
|
||||
shouldUse = TRUE;
|
||||
break;
|
||||
case AI_ITEM_CURE_CONDITION:
|
||||
|
@ -439,6 +439,11 @@ static const u16 sOtherMoveCallingMoves[] =
|
||||
};
|
||||
|
||||
// Functions
|
||||
bool32 WillAIStrikeFirst(void)
|
||||
{
|
||||
return (AI_WhoStrikesFirst(sBattler_AI, gBattlerTarget) == AI_IS_FASTER);
|
||||
}
|
||||
|
||||
bool32 AI_RandLessThan(u8 val)
|
||||
{
|
||||
if ((Random() % 0xFF) < val)
|
||||
@ -632,7 +637,7 @@ bool32 IsTruantMonVulnerable(u32 battlerAI, u32 opposingBattler)
|
||||
u32 move = gBattleResources->battleHistory->usedMoves[opposingBattler][i];
|
||||
if (gBattleMoves[move].effect == EFFECT_PROTECT && move != MOVE_ENDURE)
|
||||
return TRUE;
|
||||
if (gBattleMoves[move].effect == EFFECT_SEMI_INVULNERABLE && GetWhoStrikesFirst(battlerAI, opposingBattler, TRUE) == 1)
|
||||
if (gBattleMoves[move].effect == EFFECT_SEMI_INVULNERABLE && AI_WhoStrikesFirst(battlerAI, opposingBattler) == AI_IS_SLOWER)
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
@ -981,48 +986,43 @@ u8 AI_GetMoveEffectiveness(u16 move, u8 battlerAtk, u8 battlerDef)
|
||||
return damageVar;
|
||||
}
|
||||
|
||||
// AI_CHECK_FASTER: is user(ai) faster
|
||||
// AI_CHECK_SLOWER: is target faster
|
||||
bool32 IsAiFaster(u8 battler)
|
||||
/* Checks to see if AI will move ahead of another battler
|
||||
* Output:
|
||||
* AI_IS_FASTER: is user(ai) faster
|
||||
* AI_IS_SLOWER: is target faster
|
||||
*/
|
||||
u8 AI_WhoStrikesFirst(u8 battlerAI, u8 battler2)
|
||||
{
|
||||
u32 fasterAI = 0, fasterPlayer = 0, i;
|
||||
s8 prioAI, prioPlayer;
|
||||
s8 prioAI = 0;
|
||||
s8 prioPlayer = 0;
|
||||
|
||||
// Check move priorities first.
|
||||
prioAI = GetMovePriority(sBattler_AI, AI_THINKING_STRUCT->moveConsidered);
|
||||
SaveBattlerData(gBattlerTarget);
|
||||
SetBattlerData(gBattlerTarget);
|
||||
prioAI = GetMovePriority(battlerAI, AI_THINKING_STRUCT->moveConsidered);
|
||||
for (i = 0; i < MAX_MON_MOVES; i++)
|
||||
{
|
||||
if (gBattleMons[gBattlerTarget].moves[i] == 0 || gBattleMons[gBattlerTarget].moves[i] == 0xFFFF)
|
||||
if (gBattleMons[battler2].moves[i] == 0 || gBattleMons[battler2].moves[i] == 0xFFFF)
|
||||
continue;
|
||||
|
||||
prioPlayer = GetMovePriority(gBattlerTarget, gBattleMons[gBattlerTarget].moves[i]);
|
||||
prioPlayer = GetMovePriority(battler2, gBattleMons[battler2].moves[i]);
|
||||
if (prioAI > prioPlayer)
|
||||
fasterAI++;
|
||||
else if (prioPlayer > prioAI)
|
||||
fasterPlayer++;
|
||||
}
|
||||
RestoreBattlerData(gBattlerTarget);
|
||||
|
||||
if (fasterAI > fasterPlayer)
|
||||
{
|
||||
if (battler == 0) // is user (ai) faster
|
||||
return TRUE;
|
||||
else
|
||||
return FALSE;
|
||||
return AI_IS_FASTER;
|
||||
}
|
||||
else if (fasterAI < fasterPlayer)
|
||||
{
|
||||
if (battler == 1) // is target (player) faster
|
||||
return TRUE;
|
||||
else
|
||||
return FALSE;
|
||||
return AI_IS_SLOWER;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Priorities are the same(at least comparing to moves the AI is aware of), decide by speed.
|
||||
if (GetWhoStrikesFirst(sBattler_AI, gBattlerTarget, TRUE) == battler)
|
||||
if (GetWhoStrikesFirst(battlerAI, battler2, TRUE) == 0)
|
||||
return TRUE;
|
||||
else
|
||||
return FALSE;
|
||||
@ -1090,12 +1090,16 @@ bool32 CanTargetFaintAiWithMod(u8 battlerDef, u8 battlerAtk, s32 hpMod, s32 dmgM
|
||||
{
|
||||
u32 i;
|
||||
u32 unusable = CheckMoveLimitations(battlerDef, 0, 0xFF);
|
||||
s32 dmg;
|
||||
u16 *moves = gBattleResources->battleHistory->usedMoves[battlerDef];
|
||||
u32 hpCheck = gBattleMons[battlerAtk].hp + hpMod;
|
||||
|
||||
if (hpCheck > gBattleMons[battlerAtk].maxHP)
|
||||
hpCheck = gBattleMons[battlerAtk].maxHP;
|
||||
|
||||
for (i = 0; i < MAX_MON_MOVES; i++)
|
||||
{
|
||||
u32 dmg = AI_CalcDamage(moves[i], battlerDef, battlerAtk);
|
||||
u32 hpCheck = gBattleMons[battlerAtk].hp + hpMod;
|
||||
dmg = AI_THINKING_STRUCT->simulatedDmg[battlerAtk][battlerDef][i];
|
||||
if (dmgMod)
|
||||
dmg *= dmgMod;
|
||||
|
||||
@ -1121,31 +1125,35 @@ bool32 AI_IsAbilityOnSide(u32 battlerId, u32 ability)
|
||||
// does NOT include ability suppression checks
|
||||
s32 AI_GetAbility(u32 battlerId)
|
||||
{
|
||||
u32 knownAbility = GetBattlerAbility(battlerId);
|
||||
|
||||
// The AI knows its own ability.
|
||||
if (IsBattlerAIControlled(battlerId))
|
||||
return gBattleMons[battlerId].ability;
|
||||
return knownAbility;
|
||||
|
||||
// Check neutralizing gas, gastro acid
|
||||
if (knownAbility == ABILITY_NONE)
|
||||
return knownAbility;
|
||||
|
||||
if (BATTLE_HISTORY->abilities[battlerId] != ABILITY_NONE)
|
||||
return BATTLE_HISTORY->abilities[battlerId];
|
||||
|
||||
// Abilities that prevent fleeing.
|
||||
if (gBattleMons[battlerId].ability == ABILITY_SHADOW_TAG
|
||||
|| gBattleMons[battlerId].ability == ABILITY_MAGNET_PULL
|
||||
|| gBattleMons[battlerId].ability == ABILITY_ARENA_TRAP)
|
||||
return gBattleMons[battlerId].ability;
|
||||
// Abilities that prevent fleeing - treat as always known
|
||||
if (knownAbility == ABILITY_SHADOW_TAG || knownAbility == ABILITY_MAGNET_PULL || knownAbility == ABILITY_ARENA_TRAP)
|
||||
return knownAbility;
|
||||
|
||||
// Else, guess the ability
|
||||
if (gBaseStats[gBattleMons[battlerId].species].abilities[0] != ABILITY_NONE)
|
||||
{
|
||||
if (gBaseStats[gBattleMons[battlerId].species].abilities[1] != ABILITY_NONE)
|
||||
u16 abilityGuess = ABILITY_NONE;
|
||||
while (abilityGuess == ABILITY_NONE)
|
||||
{
|
||||
// AI has no knowledge of opponent, so it guesses which ability.
|
||||
return gBaseStats[gBattleMons[battlerId].species].abilities[Random() & 1];
|
||||
}
|
||||
else
|
||||
{
|
||||
return gBaseStats[gBattleMons[battlerId].species].abilities[0]; // It's definitely ability 1.
|
||||
abilityGuess = gBaseStats[gBattleMons[battlerId].species].abilities[Random() % NUM_ABILITY_SLOTS];
|
||||
}
|
||||
|
||||
return abilityGuess;
|
||||
}
|
||||
|
||||
return ABILITY_NONE; // Unknown.
|
||||
}
|
||||
|
||||
@ -1703,7 +1711,7 @@ u32 CountNegativeStatStages(u8 battlerId)
|
||||
|
||||
bool32 ShouldLowerAttack(u8 battlerAtk, u8 battlerDef, u16 defAbility)
|
||||
{
|
||||
if (IsAiFaster(AI_CHECK_FASTER) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
|
||||
if (WillAIStrikeFirst() && (AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
|
||||
return FALSE; // Don't bother lowering stats if can kill enemy.
|
||||
|
||||
if (gBattleMons[battlerDef].statStages[STAT_ATK] > 4
|
||||
@ -1719,7 +1727,7 @@ bool32 ShouldLowerAttack(u8 battlerAtk, u8 battlerDef, u16 defAbility)
|
||||
|
||||
bool32 ShouldLowerDefense(u8 battlerAtk, u8 battlerDef, u16 defAbility)
|
||||
{
|
||||
if (IsAiFaster(AI_CHECK_FASTER) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
|
||||
if (WillAIStrikeFirst() && (AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
|
||||
return FALSE; // Don't bother lowering stats if can kill enemy.
|
||||
|
||||
if (gBattleMons[battlerDef].statStages[STAT_DEF] > 4
|
||||
@ -1735,10 +1743,10 @@ bool32 ShouldLowerDefense(u8 battlerAtk, u8 battlerDef, u16 defAbility)
|
||||
|
||||
bool32 ShouldLowerSpeed(u8 battlerAtk, u8 battlerDef, u16 defAbility)
|
||||
{
|
||||
if (IsAiFaster(AI_CHECK_FASTER) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
|
||||
if (WillAIStrikeFirst() && (AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
|
||||
return FALSE; // Don't bother lowering stats if can kill enemy.
|
||||
|
||||
if (IsAiFaster(AI_CHECK_SLOWER)
|
||||
if (!WillAIStrikeFirst()
|
||||
&& defAbility != ABILITY_CONTRARY
|
||||
&& defAbility != ABILITY_CLEAR_BODY
|
||||
&& defAbility != ABILITY_FULL_METAL_BODY
|
||||
@ -1749,7 +1757,7 @@ bool32 ShouldLowerSpeed(u8 battlerAtk, u8 battlerDef, u16 defAbility)
|
||||
|
||||
bool32 ShouldLowerSpAtk(u8 battlerAtk, u8 battlerDef, u16 defAbility)
|
||||
{
|
||||
if (IsAiFaster(AI_CHECK_FASTER) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
|
||||
if (WillAIStrikeFirst() && (AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
|
||||
return FALSE; // Don't bother lowering stats if can kill enemy.
|
||||
|
||||
if (gBattleMons[battlerDef].statStages[STAT_SPATK] > 4
|
||||
@ -1764,7 +1772,7 @@ bool32 ShouldLowerSpAtk(u8 battlerAtk, u8 battlerDef, u16 defAbility)
|
||||
|
||||
bool32 ShouldLowerSpDef(u8 battlerAtk, u8 battlerDef, u16 defAbility)
|
||||
{
|
||||
if (IsAiFaster(AI_CHECK_FASTER) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
|
||||
if (WillAIStrikeFirst() && (AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
|
||||
return FALSE; // Don't bother lowering stats if can kill enemy.
|
||||
|
||||
if (gBattleMons[battlerDef].statStages[STAT_SPDEF] > 4
|
||||
@ -1779,7 +1787,7 @@ bool32 ShouldLowerSpDef(u8 battlerAtk, u8 battlerDef, u16 defAbility)
|
||||
|
||||
bool32 ShouldLowerAccuracy(u8 battlerAtk, u8 battlerDef, u16 defAbility)
|
||||
{
|
||||
if (IsAiFaster(AI_CHECK_FASTER) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
|
||||
if (WillAIStrikeFirst() && (AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
|
||||
return FALSE; // Don't bother lowering stats if can kill enemy.
|
||||
|
||||
if (defAbility != ABILITY_CONTRARY
|
||||
@ -1793,7 +1801,7 @@ bool32 ShouldLowerAccuracy(u8 battlerAtk, u8 battlerDef, u16 defAbility)
|
||||
|
||||
bool32 ShouldLowerEvasion(u8 battlerAtk, u8 battlerDef, u16 defAbility)
|
||||
{
|
||||
if (IsAiFaster(AI_CHECK_FASTER) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
|
||||
if (WillAIStrikeFirst() && (AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
|
||||
return FALSE; // Don't bother lowering stats if can kill enemy.
|
||||
|
||||
if (gBattleMons[battlerDef].statStages[STAT_EVASION] > DEFAULT_STAT_STAGE
|
||||
@ -2408,9 +2416,19 @@ static bool32 AnyUsefulStatIsRaised(u8 battler)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
struct Pokemon *GetPartyBattlerPartyData(u8 battlerId, u8 switchBattler)
|
||||
{
|
||||
struct Pokemon *mon;
|
||||
if (GetBattlerSide(battlerId) == B_SIDE_PLAYER)
|
||||
mon = &gPlayerParty[switchBattler];
|
||||
else
|
||||
mon = &gEnemyParty[switchBattler];
|
||||
return mon;
|
||||
}
|
||||
|
||||
static bool32 PartyBattlerShouldAvoidHazards(u8 currBattler, u8 switchBattler)
|
||||
{
|
||||
struct Pokemon *mon = GetBattlerPartyData(switchBattler);
|
||||
struct Pokemon *mon = GetPartyBattlerPartyData(currBattler, switchBattler);
|
||||
u16 ability = GetMonAbility(mon); // we know our own party data
|
||||
u16 holdEffect = GetBattlerHoldEffect(GetMonData(mon, MON_DATA_HELD_ITEM), TRUE);
|
||||
u32 flags = gSideStatuses[GetBattlerSide(currBattler)] & (SIDE_STATUS_SPIKES | SIDE_STATUS_STEALTH_ROCK | SIDE_STATUS_STICKY_WEB | SIDE_STATUS_TOXIC_SPIKES);
|
||||
@ -2457,7 +2475,7 @@ bool32 ShouldPivot(u8 battlerAtk, u8 battlerDef, u16 defAbility, u16 move, u8 mo
|
||||
/*if (IsPredictedToSwitch(battlerDef, battlerAtk) && !hasStatBoost)
|
||||
return PIVOT; // Try pivoting so you can switch to a better matchup to counter your new opponent*/
|
||||
|
||||
if (GetWhoStrikesFirst(battlerAtk, battlerDef, TRUE) == 0) // Attacker goes first
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_FASTER) // Attacker goes first
|
||||
{
|
||||
if (!CanAIFaintTarget(battlerAtk, battlerDef, 0)) // Can't KO foe otherwise
|
||||
{
|
||||
@ -2817,7 +2835,7 @@ u32 ShouldTryToFlinch(u8 battlerAtk, u8 battlerDef, u16 atkAbility, u16 defAbili
|
||||
{
|
||||
if (defAbility == ABILITY_INNER_FOCUS
|
||||
|| DoesSubstituteBlockMove(battlerAtk, battlerDef, move)
|
||||
|| GetWhoStrikesFirst(battlerAtk, battlerDef, TRUE) == 1) // opponent goes first
|
||||
|| AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_SLOWER) // Opponent goes first
|
||||
{
|
||||
return 0; // don't try to flinch
|
||||
}
|
||||
@ -2941,7 +2959,7 @@ bool32 ShouldUseRecoilMove(u8 battlerAtk, u8 battlerDef, u32 recoilDmg, u8 moveI
|
||||
|
||||
bool32 ShouldAbsorb(u8 battlerAtk, u8 battlerDef, u16 move, s32 damage)
|
||||
{
|
||||
if (move == 0xFFFF || GetWhoStrikesFirst(battlerAtk, gBattlerTarget, TRUE) == 0)
|
||||
if (move == 0xFFFF || AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_FASTER)
|
||||
{
|
||||
// using item or user goes first
|
||||
u8 healPercent = (gBattleMoves[move].argument == 0) ? 50 : gBattleMoves[move].argument;
|
||||
@ -2968,7 +2986,7 @@ bool32 ShouldAbsorb(u8 battlerAtk, u8 battlerDef, u16 move, s32 damage)
|
||||
|
||||
bool32 ShouldRecover(u8 battlerAtk, u8 battlerDef, u16 move, u8 healPercent)
|
||||
{
|
||||
if (move == 0xFFFF || GetWhoStrikesFirst(battlerAtk, gBattlerTarget, TRUE) == 0)
|
||||
if (move == 0xFFFF || AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_FASTER)
|
||||
{
|
||||
// using item or user going first
|
||||
s32 damage = AI_THINKING_STRUCT->simulatedDmg[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex];
|
||||
@ -3368,6 +3386,47 @@ static const u16 sRecycleEncouragedItems[] =
|
||||
// TODO expand this
|
||||
};
|
||||
|
||||
// Its assumed that the berry is strategically given, so no need to check benefits of the berry
|
||||
bool32 IsStatBoostingBerry(u16 item)
|
||||
{
|
||||
switch (item)
|
||||
{
|
||||
case ITEM_LIECHI_BERRY:
|
||||
case ITEM_GANLON_BERRY:
|
||||
case ITEM_SALAC_BERRY:
|
||||
case ITEM_PETAYA_BERRY:
|
||||
case ITEM_APICOT_BERRY:
|
||||
//case ITEM_LANSAT_BERRY:
|
||||
case ITEM_STARF_BERRY:
|
||||
#ifdef ITEM_EXPANSION
|
||||
case ITEM_MICLE_BERRY:
|
||||
#endif
|
||||
return TRUE;
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
bool32 ShouldRestoreHpBerry(u8 battlerAtk, u16 item)
|
||||
{
|
||||
switch (item)
|
||||
{
|
||||
case ITEM_ORAN_BERRY:
|
||||
if (gBattleMons[battlerAtk].maxHP <= 50)
|
||||
return TRUE; // Only worth it in the early game
|
||||
return FALSE;
|
||||
case ITEM_SITRUS_BERRY:
|
||||
case ITEM_FIGY_BERRY:
|
||||
case ITEM_WIKI_BERRY:
|
||||
case ITEM_MAGO_BERRY:
|
||||
case ITEM_AGUAV_BERRY:
|
||||
case ITEM_IAPAPA_BERRY:
|
||||
return TRUE;
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
bool32 IsRecycleEncouragedItem(u16 item)
|
||||
{
|
||||
u32 i;
|
||||
@ -3389,6 +3448,9 @@ void IncreaseStatUpScore(u8 battlerAtk, u8 battlerDef, u8 statId, s16 *score)
|
||||
|
||||
if (GetHealthPercentage(battlerAtk) < 80 && AI_RandLessThan(128))
|
||||
return;
|
||||
|
||||
if ((AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
|
||||
return; // Damaging moves would get a score boost from AI_TryToFaint or PreferStrongestMove so we don't consider them here
|
||||
|
||||
switch (statId)
|
||||
{
|
||||
@ -3414,7 +3476,7 @@ void IncreaseStatUpScore(u8 battlerAtk, u8 battlerDef, u8 statId, s16 *score)
|
||||
}
|
||||
break;
|
||||
case STAT_SPEED:
|
||||
if (IsAiFaster(AI_CHECK_SLOWER))
|
||||
if (!WillAIStrikeFirst())
|
||||
{
|
||||
if (gBattleMons[battlerAtk].statStages[STAT_SPEED] < STAT_UP_2_STAGE)
|
||||
*score += 2;
|
||||
@ -3461,6 +3523,9 @@ void IncreaseStatUpScore(u8 battlerAtk, u8 battlerDef, u8 statId, s16 *score)
|
||||
|
||||
void IncreasePoisonScore(u8 battlerAtk, u8 battlerDef, u16 move, s16 *score)
|
||||
{
|
||||
if ((AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
|
||||
return;
|
||||
|
||||
if (AI_CanPoison(battlerAtk, battlerDef, AI_DATA->defAbility, move, AI_DATA->partnerMove) && GetHealthPercentage(battlerDef) > 20)
|
||||
{
|
||||
if (!HasDamagingMove(battlerDef))
|
||||
@ -3481,6 +3546,9 @@ void IncreasePoisonScore(u8 battlerAtk, u8 battlerDef, u16 move, s16 *score)
|
||||
|
||||
void IncreaseBurnScore(u8 battlerAtk, u8 battlerDef, u16 move, s16 *score)
|
||||
{
|
||||
if ((AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
|
||||
return;
|
||||
|
||||
if (AI_CanBurn(battlerAtk, battlerDef, AI_DATA->defAbility, AI_DATA->battlerAtkPartner, move, AI_DATA->partnerMove))
|
||||
{
|
||||
(*score)++; // burning is good
|
||||
@ -3497,6 +3565,9 @@ void IncreaseBurnScore(u8 battlerAtk, u8 battlerDef, u16 move, s16 *score)
|
||||
|
||||
void IncreaseParalyzeScore(u8 battlerAtk, u8 battlerDef, u16 move, s16 *score)
|
||||
{
|
||||
if ((AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
|
||||
return;
|
||||
|
||||
if (AI_CanParalyze(battlerAtk, battlerDef, AI_DATA->defAbility, move, AI_DATA->partnerMove))
|
||||
{
|
||||
u8 atkSpeed = GetBattlerTotalSpeedStat(battlerAtk);
|
||||
@ -3515,6 +3586,9 @@ void IncreaseParalyzeScore(u8 battlerAtk, u8 battlerDef, u16 move, s16 *score)
|
||||
|
||||
void IncreaseSleepScore(u8 battlerAtk, u8 battlerDef, u16 move, s16 *score)
|
||||
{
|
||||
if ((AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
|
||||
return;
|
||||
|
||||
if (AI_CanPutToSleep(battlerAtk, battlerDef, AI_DATA->defAbility, move, AI_DATA->partnerMove))
|
||||
*score += 2;
|
||||
else
|
||||
@ -3530,6 +3604,9 @@ void IncreaseSleepScore(u8 battlerAtk, u8 battlerDef, u16 move, s16 *score)
|
||||
|
||||
void IncreaseConfusionScore(u8 battlerAtk, u8 battlerDef, u16 move, s16 *score)
|
||||
{
|
||||
if ((AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
|
||||
return;
|
||||
|
||||
if (AI_CanConfuse(battlerAtk, battlerDef, AI_DATA->defAbility, AI_DATA->battlerAtkPartner, move, AI_DATA->partnerMove)
|
||||
&& AI_DATA->defHoldEffect != HOLD_EFFECT_CURE_CONFUSION
|
||||
&& AI_DATA->defHoldEffect != HOLD_EFFECT_CURE_STATUS)
|
||||
@ -3542,3 +3619,12 @@ void IncreaseConfusionScore(u8 battlerAtk, u8 battlerDef, u16 move, s16 *score)
|
||||
*score += 2;
|
||||
}
|
||||
}
|
||||
|
||||
bool32 AI_MoveMakesContact(u32 ability, u32 holdEffect, u16 move)
|
||||
{
|
||||
if (TestMoveFlags(move, FLAG_MAKES_CONTACT)
|
||||
&& ability != ABILITY_LONG_REACH
|
||||
&& holdEffect != HOLD_EFFECT_PROTECTIVE_PADS)
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
@ -1542,7 +1542,7 @@ static void OpponentHandlePrintSelectionString(void)
|
||||
|
||||
static void OpponentHandleChooseAction(void)
|
||||
{
|
||||
AI_TrySwitchOrUseItem();
|
||||
AI_TrySwitchOrUseItem(); // TODO consider move choice first
|
||||
OpponentBufferExecCompleted();
|
||||
}
|
||||
|
||||
@ -1591,7 +1591,9 @@ static void OpponentHandleChooseMove(void)
|
||||
if (gAbsentBattlerFlags & gBitTable[gBattlerTarget])
|
||||
gBattlerTarget = GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT);
|
||||
}
|
||||
if (CanMegaEvolve(gActiveBattler)) // If opponent can mega evolve, do it.
|
||||
|
||||
// If opponent can mega evolve, do it.
|
||||
if (CanMegaEvolve(gActiveBattler))
|
||||
BtlController_EmitTwoReturnValues(BUFFER_B, 10, (chosenMoveId) | (RET_MEGA_EVOLUTION) | (gBattlerTarget << 8));
|
||||
else
|
||||
BtlController_EmitTwoReturnValues(BUFFER_B, 10, (chosenMoveId) | (gBattlerTarget << 8));
|
||||
@ -1599,7 +1601,7 @@ static void OpponentHandleChooseMove(void)
|
||||
}
|
||||
OpponentBufferExecCompleted();
|
||||
}
|
||||
else
|
||||
else // Wild pokemon - use random move
|
||||
{
|
||||
u16 move;
|
||||
do
|
||||
|
@ -1534,10 +1534,12 @@ static void PlayerPartnerHandleChooseMove(void)
|
||||
gBattlerTarget = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT);
|
||||
}
|
||||
|
||||
if (CanMegaEvolve(gActiveBattler)) // If partner can mega evolve, do it.
|
||||
// If partner can mega evolve, do it.
|
||||
if (CanMegaEvolve(gActiveBattler))
|
||||
BtlController_EmitTwoReturnValues(BUFFER_B, 10, (chosenMoveId) | (RET_MEGA_EVOLUTION) | (gBattlerTarget << 8));
|
||||
else
|
||||
BtlController_EmitTwoReturnValues(BUFFER_B, 10, (chosenMoveId) | (gBattlerTarget << 8));
|
||||
|
||||
PlayerPartnerBufferExecCompleted();
|
||||
}
|
||||
|
||||
|
@ -1924,12 +1924,20 @@ static const u8 sText_HoldEffectPsychicPower[] = _("Psychic Power");
|
||||
static const u8 sText_HoldEffectFirePower[] = _("Fire Power");
|
||||
static const u8 sText_HoldEffectDragonPower[] = _("Dragon Power");
|
||||
static const u8 sText_HoldEffectNormalPower[] = _("Normal Power");
|
||||
static const u8 sText_HoldEffectUpGrade[] = _("Up Grade");
|
||||
#ifdef ITEM_EXPANSION
|
||||
static const u8 sText_HoldEffectUpgrade[] = _("Upgrade");
|
||||
#else
|
||||
static const u8 sText_HoldEffectUpgrade[] = _("Up Grade");
|
||||
#endif
|
||||
static const u8 sText_HoldEffectShellBell[] = _("Shell Bell");
|
||||
static const u8 sText_HoldEffectLuckyPunch[] = _("Lucky Punch");
|
||||
static const u8 sText_HoldEffectMetalPowder[] = _("Metal Powder");
|
||||
static const u8 sText_HoldEffectThickClub[] = _("Thick Club");
|
||||
static const u8 sText_HoldEffectStick[] = _("Stick");
|
||||
#ifdef ITEM_EXPANSION
|
||||
static const u8 sText_HoldEffectLeek[] = _("Leek");
|
||||
#else
|
||||
static const u8 sText_HoldEffectLeek[] = _("Stick");
|
||||
#endif
|
||||
static const u8 sText_HoldEffectChoiceScarf[] = _("Choice Scarf");
|
||||
static const u8 sText_HoldEffectChoiceSpecs[] = _("Choice Specs");
|
||||
static const u8 sText_HoldEffectDampRock[] = _("Damp Rock");
|
||||
@ -2064,12 +2072,12 @@ static const u8 *const sHoldEffectNames[] =
|
||||
[HOLD_EFFECT_FIRE_POWER] = sText_HoldEffectFirePower,
|
||||
[HOLD_EFFECT_DRAGON_POWER] = sText_HoldEffectDragonPower,
|
||||
[HOLD_EFFECT_NORMAL_POWER] = sText_HoldEffectNormalPower,
|
||||
[HOLD_EFFECT_UP_GRADE] = sText_HoldEffectUpGrade,
|
||||
[HOLD_EFFECT_UPGRADE] = sText_HoldEffectUpgrade,
|
||||
[HOLD_EFFECT_SHELL_BELL] = sText_HoldEffectShellBell,
|
||||
[HOLD_EFFECT_LUCKY_PUNCH] = sText_HoldEffectLuckyPunch,
|
||||
[HOLD_EFFECT_METAL_POWDER] = sText_HoldEffectMetalPowder,
|
||||
[HOLD_EFFECT_THICK_CLUB] = sText_HoldEffectThickClub,
|
||||
[HOLD_EFFECT_STICK] = sText_HoldEffectStick,
|
||||
[HOLD_EFFECT_LEEK] = sText_HoldEffectLeek,
|
||||
[HOLD_EFFECT_CHOICE_SCARF] = sText_HoldEffectChoiceScarf,
|
||||
[HOLD_EFFECT_CHOICE_SPECS] = sText_HoldEffectChoiceSpecs,
|
||||
[HOLD_EFFECT_DAMP_ROCK] = sText_HoldEffectDampRock,
|
||||
|
@ -99,6 +99,7 @@ static void RunTurnActionsFunctions(void);
|
||||
static void SetActionsAndBattlersTurnOrder(void);
|
||||
static void UpdateBattlerPartyOrdersOnSwitch(void);
|
||||
static bool8 AllAtActionConfirmed(void);
|
||||
static void TryChangeTurnOrder(void);
|
||||
static void CheckFocusPunch_ClearVarsBeforeTurnStarts(void);
|
||||
static void CheckMegaEvolutionBeforeTurn(void);
|
||||
static void CheckQuickClaw_CustapBerryActivation(void);
|
||||
@ -3603,6 +3604,10 @@ static void TryDoEventsBeforeFirstTurn(void)
|
||||
}
|
||||
}
|
||||
|
||||
// Check neutralizing gas
|
||||
if (AbilityBattleEffects(ABILITYEFFECT_NEUTRALIZINGGAS, 0, 0, 0, 0) != 0)
|
||||
return;
|
||||
|
||||
// Check all switch in abilities happening from the fastest mon to slowest.
|
||||
while (gBattleStruct->switchInAbilitiesCounter < gBattlersCount)
|
||||
{
|
||||
@ -4732,6 +4737,32 @@ static void CheckMegaEvolutionBeforeTurn(void)
|
||||
}
|
||||
}
|
||||
|
||||
#if B_MEGA_EVO_TURN_ORDER <= GEN_6
|
||||
gBattleMainFunc = CheckFocusPunch_ClearVarsBeforeTurnStarts;
|
||||
gBattleStruct->focusPunchBattlerId = 0;
|
||||
#else
|
||||
gBattleMainFunc = TryChangeTurnOrder; // This will just do nothing if no mon has mega evolved
|
||||
#endif
|
||||
}
|
||||
|
||||
// In gen7, priority and speed are recalculated during the turn in which a pokemon mega evolves
|
||||
static void TryChangeTurnOrder(void)
|
||||
{
|
||||
s32 i, j;
|
||||
for (i = 0; i < gBattlersCount - 1; i++)
|
||||
{
|
||||
for (j = i + 1; j < gBattlersCount; j++)
|
||||
{
|
||||
u8 battler1 = gBattlerByTurnOrder[i];
|
||||
u8 battler2 = gBattlerByTurnOrder[j];
|
||||
if (gActionsByTurnOrder[i] == B_ACTION_USE_MOVE
|
||||
&& gActionsByTurnOrder[j] == B_ACTION_USE_MOVE)
|
||||
{
|
||||
if (GetWhoStrikesFirst(battler1, battler2, FALSE))
|
||||
SwapTurnOrder(i, j);
|
||||
}
|
||||
}
|
||||
}
|
||||
gBattleMainFunc = CheckFocusPunch_ClearVarsBeforeTurnStarts;
|
||||
gBattleStruct->focusPunchBattlerId = 0;
|
||||
}
|
||||
|
@ -729,9 +729,13 @@ static const u8 sText_CantEscapeDueToUsedMove[] = _("{B_ATK_NAME_WITH_PREFIX} ca
|
||||
static const u8 sText_PkmnBecameWeakerToFire[] = _("{B_DEF_NAME_WITH_PREFIX} became\nweaker to fire!");
|
||||
static const u8 sText_PkmnAboutToBeAttackedByItsItem[] = _("{B_DEF_NAME_WITH_PREFIX} is about\nto be attacked by its {B_BUFF1}!");
|
||||
static const u8 sText_CantEscapeBecauseOfCurrentMove[] = _("{B_DEF_NAME_WITH_PREFIX} can no longer escape\nbecause of {B_CURRENT_MOVE}!");
|
||||
static const u8 sText_NeutralizingGasEnters[] = _("Neutralizing Gas filled the area!");
|
||||
static const u8 sText_NeutralizingGasOver[] = _("The effects of Neutralizing\nGas wore off!");
|
||||
|
||||
const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT] =
|
||||
{
|
||||
[STRINGID_NEUTRALIZINGGASOVER - 12] = sText_NeutralizingGasOver,
|
||||
[STRINGID_NEUTRALIZINGGASENTERS - 12] = sText_NeutralizingGasEnters,
|
||||
[STRINGID_BATTLERTYPECHANGEDTO - 12] = sText_BattlerTypeChangedTo,
|
||||
[STRINGID_PASTELVEILENTERS - 12] = sText_PastelVeilEnters,
|
||||
[STRINGID_PASTELVEILPROTECTED -12] = sText_PastelVeilProtected,
|
||||
@ -1388,6 +1392,7 @@ const u16 gSwitchInAbilityStringIds[] =
|
||||
[B_MSG_SWITCHIN_ASONE] = STRINGID_ASONEENTERS,
|
||||
[B_MSG_SWITCHIN_CURIOUS_MEDICINE] = STRINGID_CURIOUSMEDICINEENTERS,
|
||||
[B_MSG_SWITCHIN_PASTEL_VEIL] = STRINGID_PASTELVEILENTERS,
|
||||
[B_MSG_SWITCHIN_NEUTRALIZING_GAS] = STRINGID_NEUTRALIZINGGASENTERS,
|
||||
};
|
||||
|
||||
const u16 gMissStringIds[] =
|
||||
|
@ -1870,11 +1870,13 @@ static void Cmd_ppreduce(void)
|
||||
static const u8 sCriticalHitChance[] = {16, 8, 4, 3, 2}; // Gens 2,3,4,5
|
||||
#endif // B_CRIT_CHANCE
|
||||
|
||||
#define BENEFITS_FROM_LEEK(battler, holdEffect)((holdEffect == HOLD_EFFECT_LEEK) && (GET_BASE_SPECIES_ID(gBattleMons[battler].species) == SPECIES_FARFETCHD || gBattleMons[battler].species == SPECIES_SIRFETCHD))
|
||||
s32 CalcCritChanceStage(u8 battlerAtk, u8 battlerDef, u32 move, bool32 recordAbility)
|
||||
{
|
||||
s32 critChance = 0;
|
||||
u32 abilityAtk = GetBattlerAbility(gBattlerAttacker);
|
||||
u32 abilityDef = GetBattlerAbility(gBattlerTarget);
|
||||
u32 holdEffectAtk = GetBattlerHoldEffect(battlerAtk, TRUE);
|
||||
|
||||
if (gSideStatuses[battlerDef] & SIDE_STATUS_LUCKY_CHANT
|
||||
|| gStatuses3[gBattlerAttacker] & STATUS3_CANT_SCORE_A_CRIT)
|
||||
@ -1890,19 +1892,21 @@ s32 CalcCritChanceStage(u8 battlerAtk, u8 battlerDef, u32 move, bool32 recordAbi
|
||||
else if (gStatuses3[battlerAtk] & STATUS3_LASER_FOCUS
|
||||
|| gBattleMoves[move].effect == EFFECT_ALWAYS_CRIT
|
||||
|| (abilityAtk == ABILITY_MERCILESS && gBattleMons[battlerDef].status1 & STATUS1_PSN_ANY)
|
||||
|| move == MOVE_SURGING_STRIKES)
|
||||
|| move == MOVE_SURGING_STRIKES
|
||||
#if B_LEEK_ALWAYS_CRIT >= GEN_6
|
||||
|| ((gBattleMoves[gCurrentMove].flags & FLAG_HIGH_CRIT) && BENEFITS_FROM_LEEK(battlerAtk, holdEffectAtk))
|
||||
#endif
|
||||
)
|
||||
{
|
||||
critChance = -2;
|
||||
}
|
||||
else
|
||||
{
|
||||
u32 holdEffectAtk = GetBattlerHoldEffect(battlerAtk, TRUE);
|
||||
|
||||
critChance = 2 * ((gBattleMons[gBattlerAttacker].status2 & STATUS2_FOCUS_ENERGY) != 0)
|
||||
+ ((gBattleMoves[gCurrentMove].flags & FLAG_HIGH_CRIT) != 0)
|
||||
+ (holdEffectAtk == HOLD_EFFECT_SCOPE_LENS)
|
||||
+ 2 * (holdEffectAtk == HOLD_EFFECT_LUCKY_PUNCH && gBattleMons[gBattlerAttacker].species == SPECIES_CHANSEY)
|
||||
+ 2 * (holdEffectAtk == HOLD_EFFECT_STICK && gBattleMons[gBattlerAttacker].species == SPECIES_FARFETCHD)
|
||||
+ 2 * BENEFITS_FROM_LEEK(battlerAtk, holdEffectAtk)
|
||||
+ (abilityAtk == ABILITY_SUPER_LUCK);
|
||||
|
||||
if (critChance >= ARRAY_COUNT(sCriticalHitChance))
|
||||
@ -1911,6 +1915,7 @@ s32 CalcCritChanceStage(u8 battlerAtk, u8 battlerDef, u32 move, bool32 recordAbi
|
||||
|
||||
return critChance;
|
||||
}
|
||||
#undef BENEFITS_FROM_LEEK
|
||||
|
||||
s8 GetInverseCritChance(u8 battlerAtk, u8 battlerDef, u32 move)
|
||||
{
|
||||
@ -3174,7 +3179,11 @@ void SetMoveEffect(bool32 primary, u32 certain)
|
||||
RecordAbilityBattle(gBattlerTarget, gLastUsedAbility);
|
||||
}
|
||||
else if (gBattleMons[gBattlerAttacker].item != 0
|
||||
#ifdef ITEM_EXPANSION
|
||||
|| gBattleMons[gBattlerTarget].item == ITEM_ENIGMA_BERRY_E_READER
|
||||
#else
|
||||
|| gBattleMons[gBattlerTarget].item == ITEM_ENIGMA_BERRY
|
||||
#endif
|
||||
|| gBattleMons[gBattlerTarget].item == 0)
|
||||
{
|
||||
gBattlescriptCurrInstr++;
|
||||
@ -5470,6 +5479,14 @@ static void Cmd_moveend(void)
|
||||
*(gBattleStruct->moveTarget + gBattlerAttacker) = gSpecialStatuses[gBattlerAttacker].instructedChosenTarget & 0x3;
|
||||
if (gSpecialStatuses[gBattlerAttacker].dancerOriginalTarget)
|
||||
*(gBattleStruct->moveTarget + gBattlerAttacker) = gSpecialStatuses[gBattlerAttacker].dancerOriginalTarget & 0x3;
|
||||
|
||||
#if B_RAMPAGE_CANCELLING >= GEN_5
|
||||
if (gBattleMoves[gCurrentMove].effect == EFFECT_RAMPAGE // If we're rampaging
|
||||
&& (gMoveResultFlags & MOVE_RESULT_NO_EFFECT) // And it is unusable
|
||||
&& (gBattleMons[gBattlerAttacker].status2 & STATUS2_LOCK_CONFUSE) != STATUS2_LOCK_CONFUSE_TURN(1)) // And won't end this turn
|
||||
CancelMultiTurnMoves(gBattlerAttacker); // Cancel it
|
||||
#endif
|
||||
|
||||
gProtectStructs[gBattlerAttacker].usesBouncedMove = FALSE;
|
||||
gProtectStructs[gBattlerAttacker].targetAffected = FALSE;
|
||||
gBattleStruct->ateBoost[gBattlerAttacker] = 0;
|
||||
@ -6174,7 +6191,16 @@ static void Cmd_switchineffects(void)
|
||||
gHitMarker &= ~HITMARKER_FAINTED(gActiveBattler);
|
||||
gSpecialStatuses[gActiveBattler].faintedHasReplacement = FALSE;
|
||||
|
||||
if (!(gSideStatuses[GetBattlerSide(gActiveBattler)] & SIDE_STATUS_SPIKES_DAMAGED)
|
||||
// Neutralizing Gas announces itself before hazards
|
||||
if (gBattleMons[gActiveBattler].ability == ABILITY_NEUTRALIZING_GAS && gSpecialStatuses[gActiveBattler].announceNeutralizingGas == 0)
|
||||
{
|
||||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SWITCHIN_NEUTRALIZING_GAS;
|
||||
gSpecialStatuses[gActiveBattler].announceNeutralizingGas = TRUE;
|
||||
gBattlerAbility = gActiveBattler;
|
||||
BattleScriptPushCursor();
|
||||
gBattlescriptCurrInstr = BattleScript_SwitchInAbilityMsgRet;
|
||||
}
|
||||
else if (!(gSideStatuses[GetBattlerSide(gActiveBattler)] & SIDE_STATUS_SPIKES_DAMAGED)
|
||||
&& (gSideStatuses[GetBattlerSide(gActiveBattler)] & SIDE_STATUS_SPIKES)
|
||||
&& GetBattlerAbility(gActiveBattler) != ABILITY_MAGIC_GUARD
|
||||
&& IsBattlerAffectedByHazards(gActiveBattler, FALSE)
|
||||
@ -6248,7 +6274,7 @@ static void Cmd_switchineffects(void)
|
||||
{
|
||||
// There is a hack here to ensure the truant counter will be 0 when the battler's next turn starts.
|
||||
// The truant counter is not updated in the case where a mon switches in after a lost judgement in the battle arena.
|
||||
if (gBattleMons[gActiveBattler].ability == ABILITY_TRUANT
|
||||
if (GetBattlerAbility(gActiveBattler) == ABILITY_TRUANT
|
||||
&& gCurrentActionFuncId != B_ACTION_USE_MOVE
|
||||
&& !gDisableStructs[gActiveBattler].truantSwitchInHack)
|
||||
gDisableStructs[gActiveBattler].truantCounter = 1;
|
||||
@ -7446,7 +7472,7 @@ u32 IsLeafGuardProtected(u32 battler)
|
||||
|
||||
bool32 IsShieldsDownProtected(u32 battler)
|
||||
{
|
||||
return (gBattleMons[battler].ability == ABILITY_SHIELDS_DOWN
|
||||
return (GetBattlerAbility(battler) == ABILITY_SHIELDS_DOWN
|
||||
&& GetFormIdFromFormSpeciesId(gBattleMons[battler].species) < GetFormIdFromFormSpeciesId(SPECIES_MINIOR_CORE_RED)); // Minior is not in core form
|
||||
}
|
||||
|
||||
@ -7914,6 +7940,7 @@ static void Cmd_various(void)
|
||||
break;
|
||||
case VARIOUS_SWITCHIN_ABILITIES:
|
||||
gBattlescriptCurrInstr += 3;
|
||||
AbilityBattleEffects(ABILITYEFFECT_NEUTRALIZINGGAS, gActiveBattler, 0, 0, 0);
|
||||
AbilityBattleEffects(ABILITYEFFECT_ON_SWITCHIN, gActiveBattler, 0, 0, 0);
|
||||
AbilityBattleEffects(ABILITYEFFECT_INTIMIDATE2, gActiveBattler, 0, 0, 0);
|
||||
AbilityBattleEffects(ABILITYEFFECT_TRACE2, gActiveBattler, 0, 0, 0);
|
||||
@ -8094,6 +8121,9 @@ static void Cmd_various(void)
|
||||
}
|
||||
else
|
||||
{
|
||||
if (gBattleMons[gBattlerTarget].ability == ABILITY_NEUTRALIZING_GAS)
|
||||
gSpecialStatuses[gBattlerTarget].neutralizingGasRemoved = TRUE;
|
||||
|
||||
gBattleMons[gBattlerTarget].ability = ABILITY_SIMPLE;
|
||||
gBattlescriptCurrInstr += 7;
|
||||
}
|
||||
@ -8840,7 +8870,7 @@ static void Cmd_various(void)
|
||||
effect = TryHandleSeed(gActiveBattler, STATUS_FIELD_PSYCHIC_TERRAIN, STAT_SPDEF, item, FALSE);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if (effect)
|
||||
return;
|
||||
}
|
||||
@ -8970,11 +9000,11 @@ static void Cmd_various(void)
|
||||
|
||||
for (i = 0; i < gBattlersCount; i++)
|
||||
{
|
||||
if (((GetBattlerAbility(i) == ABILITY_DESOLATE_LAND && gBattleWeather & B_WEATHER_SUN_PRIMAL)
|
||||
|| (GetBattlerAbility(i) == ABILITY_PRIMORDIAL_SEA && gBattleWeather & B_WEATHER_RAIN_PRIMAL)
|
||||
|| (GetBattlerAbility(i) == ABILITY_DELTA_STREAM && gBattleWeather & B_WEATHER_STRONG_WINDS))
|
||||
&& IsBattlerAlive(i)
|
||||
&& !(gStatuses3[i] & STATUS3_GASTRO_ACID))
|
||||
u32 ability = GetBattlerAbility(i);
|
||||
if (((ability == ABILITY_DESOLATE_LAND && gBattleWeather & B_WEATHER_SUN_PRIMAL)
|
||||
|| (ability == ABILITY_PRIMORDIAL_SEA && gBattleWeather & B_WEATHER_RAIN_PRIMAL)
|
||||
|| (ability == ABILITY_DELTA_STREAM && gBattleWeather & B_WEATHER_STRONG_WINDS))
|
||||
&& IsBattlerAlive(i))
|
||||
shouldNotClear = TRUE;
|
||||
}
|
||||
if (gBattleWeather & B_WEATHER_SUN_PRIMAL && !shouldNotClear)
|
||||
@ -8997,6 +9027,15 @@ static void Cmd_various(void)
|
||||
}
|
||||
break;
|
||||
}
|
||||
case VARIOUS_TRY_END_NEUTRALIZING_GAS:
|
||||
if (gSpecialStatuses[gActiveBattler].neutralizingGasRemoved)
|
||||
{
|
||||
gSpecialStatuses[gActiveBattler].neutralizingGasRemoved = FALSE;
|
||||
BattleScriptPush(gBattlescriptCurrInstr + 3);
|
||||
gBattlescriptCurrInstr = BattleScript_NeutralizingGasExits;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case VARIOUS_GET_ROTOTILLER_TARGETS:
|
||||
// Gets the battlers to be affected by rototiller. If there are none, print 'But it failed!'
|
||||
{
|
||||
@ -11413,11 +11452,11 @@ static void Cmd_healpartystatus(void)
|
||||
u16 ability;
|
||||
|
||||
if (gBattlerPartyIndexes[gBattlerAttacker] == i)
|
||||
ability = gBattleMons[gBattlerAttacker].ability;
|
||||
ability = GetBattlerAbility(gBattlerAttacker);
|
||||
else if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE
|
||||
&& gBattlerPartyIndexes[gActiveBattler] == i
|
||||
&& !(gAbsentBattlerFlags & gBitTable[gActiveBattler]))
|
||||
ability = gBattleMons[gActiveBattler].ability;
|
||||
ability = GetBattlerAbility(gActiveBattler);
|
||||
else
|
||||
ability = GetAbilityBySpecies(species, abilityNum);
|
||||
|
||||
@ -12320,7 +12359,7 @@ static void Cmd_trywish(void)
|
||||
if (gWishFutureKnock.wishCounter[gBattlerAttacker] == 0)
|
||||
{
|
||||
gWishFutureKnock.wishCounter[gBattlerAttacker] = 2;
|
||||
gWishFutureKnock.wishMonId[gBattlerAttacker] = gBattlerPartyIndexes[gBattlerAttacker];
|
||||
gWishFutureKnock.wishPartyId[gBattlerAttacker] = gBattlerPartyIndexes[gBattlerAttacker];
|
||||
gBattlescriptCurrInstr += 6;
|
||||
}
|
||||
else
|
||||
@ -12329,9 +12368,12 @@ static void Cmd_trywish(void)
|
||||
}
|
||||
break;
|
||||
case 1: // heal effect
|
||||
PREPARE_MON_NICK_WITH_PREFIX_BUFFER(gBattleTextBuff1, gBattlerTarget, gWishFutureKnock.wishMonId[gBattlerTarget])
|
||||
PREPARE_MON_NICK_WITH_PREFIX_BUFFER(gBattleTextBuff1, gBattlerTarget, gWishFutureKnock.wishPartyId[gBattlerTarget])
|
||||
#if B_WISH_HP_SOURCE >= GEN_5
|
||||
gBattleMoveDamage = max(1, gBattleMons[gWishFutureKnock.wishMonId[gBattlerTarget]].maxHP / 2);
|
||||
if (GetBattlerSide(gBattlerTarget) == B_SIDE_PLAYER)
|
||||
gBattleMoveDamage = max(1, GetMonData(&gPlayerParty[gWishFutureKnock.wishPartyId[gBattlerTarget]], MON_DATA_MAX_HP) / 2);
|
||||
else
|
||||
gBattleMoveDamage = max(1, GetMonData(&gEnemyParty[gWishFutureKnock.wishPartyId[gBattlerTarget]], MON_DATA_MAX_HP) / 2);
|
||||
#else
|
||||
gBattleMoveDamage = max(1, gBattleMons[gBattlerTarget].maxHP / 2);
|
||||
#endif
|
||||
@ -12369,6 +12411,9 @@ static void Cmd_setgastroacid(void)
|
||||
}
|
||||
else
|
||||
{
|
||||
if (gBattleMons[gBattlerTarget].ability == ABILITY_NEUTRALIZING_GAS)
|
||||
gSpecialStatuses[gBattlerTarget].neutralizingGasRemoved = TRUE;
|
||||
|
||||
gStatuses3[gBattlerTarget] |= STATUS3_GASTRO_ACID;
|
||||
gBattlescriptCurrInstr += 5;
|
||||
}
|
||||
@ -12649,31 +12694,39 @@ static void Cmd_trygetintimidatetarget(void)
|
||||
static void Cmd_switchoutabilities(void)
|
||||
{
|
||||
gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]);
|
||||
|
||||
switch (GetBattlerAbility(gActiveBattler))
|
||||
if (gBattleMons[gActiveBattler].ability == ABILITY_NEUTRALIZING_GAS)
|
||||
{
|
||||
case ABILITY_NATURAL_CURE:
|
||||
gBattleMons[gActiveBattler].status1 = 0;
|
||||
BtlController_EmitSetMonData(BUFFER_A, REQUEST_STATUS_BATTLE,
|
||||
gBitTable[*(gBattleStruct->battlerPartyIndexes + gActiveBattler)],
|
||||
sizeof(gBattleMons[gActiveBattler].status1),
|
||||
&gBattleMons[gActiveBattler].status1);
|
||||
MarkBattlerForControllerExec(gActiveBattler);
|
||||
break;
|
||||
case ABILITY_REGENERATOR:
|
||||
gBattleMoveDamage = gBattleMons[gActiveBattler].maxHP / 3;
|
||||
gBattleMoveDamage += gBattleMons[gActiveBattler].hp;
|
||||
if (gBattleMoveDamage > gBattleMons[gActiveBattler].maxHP)
|
||||
gBattleMoveDamage = gBattleMons[gActiveBattler].maxHP;
|
||||
BtlController_EmitSetMonData(BUFFER_A, REQUEST_HP_BATTLE,
|
||||
gBitTable[*(gBattleStruct->battlerPartyIndexes + gActiveBattler)],
|
||||
sizeof(gBattleMoveDamage),
|
||||
&gBattleMoveDamage);
|
||||
MarkBattlerForControllerExec(gActiveBattler);
|
||||
break;
|
||||
gBattleMons[gActiveBattler].ability = ABILITY_NONE;
|
||||
BattleScriptPush(gBattlescriptCurrInstr);
|
||||
gBattlescriptCurrInstr = BattleScript_NeutralizingGasExits;
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (GetBattlerAbility(gActiveBattler))
|
||||
{
|
||||
case ABILITY_NATURAL_CURE:
|
||||
gBattleMons[gActiveBattler].status1 = 0;
|
||||
BtlController_EmitSetMonData(BUFFER_A, REQUEST_STATUS_BATTLE,
|
||||
gBitTable[*(gBattleStruct->battlerPartyIndexes + gActiveBattler)],
|
||||
sizeof(gBattleMons[gActiveBattler].status1),
|
||||
&gBattleMons[gActiveBattler].status1);
|
||||
MarkBattlerForControllerExec(gActiveBattler);
|
||||
break;
|
||||
case ABILITY_REGENERATOR:
|
||||
gBattleMoveDamage = gBattleMons[gActiveBattler].maxHP / 3;
|
||||
gBattleMoveDamage += gBattleMons[gActiveBattler].hp;
|
||||
if (gBattleMoveDamage > gBattleMons[gActiveBattler].maxHP)
|
||||
gBattleMoveDamage = gBattleMons[gActiveBattler].maxHP;
|
||||
BtlController_EmitSetMonData(BUFFER_A, REQUEST_HP_BATTLE,
|
||||
gBitTable[*(gBattleStruct->battlerPartyIndexes + gActiveBattler)],
|
||||
sizeof(gBattleMoveDamage),
|
||||
&gBattleMoveDamage);
|
||||
MarkBattlerForControllerExec(gActiveBattler);
|
||||
break;
|
||||
}
|
||||
|
||||
gBattlescriptCurrInstr += 2;
|
||||
gBattlescriptCurrInstr += 2;
|
||||
}
|
||||
}
|
||||
|
||||
static void Cmd_jumpifhasnohp(void)
|
||||
@ -13098,7 +13151,8 @@ static void Cmd_removelightscreenreflect(void) // brick break
|
||||
bool32 failed;
|
||||
|
||||
#if B_BRICK_BREAK >= GEN_4
|
||||
side = GetBattlerSide(gBattlerAttacker);
|
||||
// From Gen 4 onwards, Brick Break can remove screens on the user's side if used on an ally
|
||||
side = GetBattlerSide(gBattlerTarget);
|
||||
#else
|
||||
side = GetBattlerSide(gBattlerAttacker) ^ BIT_SIDE;
|
||||
#endif
|
||||
@ -13169,7 +13223,7 @@ static void Cmd_handleballthrow(void)
|
||||
u8 catchRate;
|
||||
|
||||
gLastThrownBall = gLastUsedItem;
|
||||
if (gLastUsedItem == ITEM_SAFARI_BALL)
|
||||
if (gBattleTypeFlags & BATTLE_TYPE_SAFARI)
|
||||
catchRate = gBattleStruct->safariCatchFactor * 1275 / 100;
|
||||
else
|
||||
catchRate = gBaseStats[gBattleMons[gBattlerTarget].species].catchRate;
|
||||
|
@ -317,8 +317,8 @@ void HandleAction_UseMove(void)
|
||||
else if ((gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
|
||||
&& gSideTimers[side].followmeTimer == 0
|
||||
&& (gBattleMoves[gCurrentMove].power != 0 || gBattleMoves[gCurrentMove].target != MOVE_TARGET_USER)
|
||||
&& ((gBattleMons[*(gBattleStruct->moveTarget + gBattlerAttacker)].ability != ABILITY_LIGHTNING_ROD && moveType == TYPE_ELECTRIC)
|
||||
|| (gBattleMons[*(gBattleStruct->moveTarget + gBattlerAttacker)].ability != ABILITY_STORM_DRAIN && moveType == TYPE_WATER)))
|
||||
&& ((GetBattlerAbility(*(gBattleStruct->moveTarget + gBattlerAttacker)) != ABILITY_LIGHTNING_ROD && moveType == TYPE_ELECTRIC)
|
||||
|| (GetBattlerAbility(*(gBattleStruct->moveTarget + gBattlerAttacker)) != ABILITY_STORM_DRAIN && moveType == TYPE_WATER)))
|
||||
{
|
||||
side = GetBattlerSide(gBattlerAttacker);
|
||||
for (gActiveBattler = 0; gActiveBattler < gBattlersCount; gActiveBattler++)
|
||||
@ -387,9 +387,9 @@ void HandleAction_UseMove(void)
|
||||
{
|
||||
gActiveBattler = gBattlerByTurnOrder[var];
|
||||
RecordAbilityBattle(gActiveBattler, gBattleMons[gActiveBattler].ability);
|
||||
if (gBattleMons[gActiveBattler].ability == ABILITY_LIGHTNING_ROD)
|
||||
if (GetBattlerAbility(gActiveBattler) == ABILITY_LIGHTNING_ROD)
|
||||
gSpecialStatuses[gActiveBattler].lightningRodRedirected = TRUE;
|
||||
else if (gBattleMons[gActiveBattler].ability == ABILITY_STORM_DRAIN)
|
||||
else if (GetBattlerAbility(gActiveBattler) == ABILITY_STORM_DRAIN)
|
||||
gSpecialStatuses[gActiveBattler].stormDrainRedirected = TRUE;
|
||||
gBattlerTarget = gActiveBattler;
|
||||
}
|
||||
@ -627,7 +627,7 @@ bool8 TryRunFromBattle(u8 battler)
|
||||
effect++;
|
||||
}
|
||||
#endif
|
||||
else if (gBattleMons[battler].ability == ABILITY_RUN_AWAY)
|
||||
else if (GetBattlerAbility(battler) == ABILITY_RUN_AWAY)
|
||||
{
|
||||
if (InBattlePyramid())
|
||||
{
|
||||
@ -2736,7 +2736,7 @@ u8 DoBattlerEndTurnEffects(void)
|
||||
for (gBattlerAttacker = 0; gBattlerAttacker < gBattlersCount; gBattlerAttacker++)
|
||||
{
|
||||
if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_SLEEP)
|
||||
&& gBattleMons[gBattlerAttacker].ability != ABILITY_SOUNDPROOF)
|
||||
&& GetBattlerAbility(gBattlerAttacker) != ABILITY_SOUNDPROOF)
|
||||
{
|
||||
gBattleMons[gBattlerAttacker].status1 &= ~STATUS1_SLEEP;
|
||||
gBattleMons[gBattlerAttacker].status2 &= ~STATUS2_NIGHTMARE;
|
||||
@ -2804,6 +2804,7 @@ u8 DoBattlerEndTurnEffects(void)
|
||||
case ENDTURN_FLINCH: // reset flinch
|
||||
gBattleMons[gActiveBattler].status2 &= ~STATUS2_FLINCHED;
|
||||
gBattleStruct->turnEffectsTracker++;
|
||||
break;
|
||||
case ENDTURN_DISABLE: // disable
|
||||
if (gDisableStructs[gActiveBattler].disableTimer != 0)
|
||||
{
|
||||
@ -3320,7 +3321,7 @@ u8 AtkCanceller_UnableToUseMove(void)
|
||||
gBattleStruct->atkCancellerTracker++;
|
||||
break;
|
||||
case CANCELLER_TRUANT: // truant
|
||||
if (gBattleMons[gBattlerAttacker].ability == ABILITY_TRUANT && gDisableStructs[gBattlerAttacker].truantCounter)
|
||||
if (GetBattlerAbility(gBattlerAttacker) == ABILITY_TRUANT && gDisableStructs[gBattlerAttacker].truantCounter)
|
||||
{
|
||||
CancelMultiTurnMoves(gBattlerAttacker);
|
||||
gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE;
|
||||
@ -3761,7 +3762,7 @@ u8 TryWeatherFormChange(u8 battler)
|
||||
|
||||
if (gBattleMons[battler].species == SPECIES_CASTFORM)
|
||||
{
|
||||
if (gBattleMons[battler].ability != ABILITY_FORECAST || gBattleMons[battler].hp == 0)
|
||||
if (GetBattlerAbility(battler) != ABILITY_FORECAST || gBattleMons[battler].hp == 0)
|
||||
{
|
||||
ret = 0;
|
||||
}
|
||||
@ -3797,11 +3798,11 @@ u8 TryWeatherFormChange(u8 battler)
|
||||
}
|
||||
else if (gBattleMons[battler].species == SPECIES_CHERRIM)
|
||||
{
|
||||
if (gBattleMons[battler].ability != ABILITY_FLOWER_GIFT || gBattleMons[battler].hp == 0)
|
||||
if (GetBattlerAbility(battler) != ABILITY_FLOWER_GIFT || gBattleMons[battler].hp == 0)
|
||||
ret = 0;
|
||||
else if (gBattleMonForms[battler] == 0 && weatherEffect && holdEffect != HOLD_EFFECT_UTILITY_UMBRELLA && gBattleWeather & B_WEATHER_SUN)
|
||||
ret = 2;
|
||||
else if (gBattleMonForms[battler] != 0 && (!weatherEffect || holdEffect != HOLD_EFFECT_UTILITY_UMBRELLA || !(gBattleWeather & B_WEATHER_SUN)))
|
||||
else if (gBattleMonForms[battler] != 0 && (!weatherEffect || holdEffect == HOLD_EFFECT_UTILITY_UMBRELLA || !(gBattleWeather & B_WEATHER_SUN)))
|
||||
ret = 1;
|
||||
}
|
||||
|
||||
@ -3888,7 +3889,7 @@ static bool32 ShouldChangeFormHpBased(u32 battler)
|
||||
|
||||
for (i = 0; i < ARRAY_COUNT(forms); i++)
|
||||
{
|
||||
if (gBattleMons[battler].ability == forms[i][0])
|
||||
if (GetBattlerAbility(battler) == forms[i][0])
|
||||
{
|
||||
if (gBattleMons[battler].species == forms[i][2]
|
||||
&& gBattleMons[battler].hp > gBattleMons[battler].maxHP / forms[i][3])
|
||||
@ -5383,7 +5384,7 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move
|
||||
case ABILITYEFFECT_IMMUNITY: // 5
|
||||
for (battler = 0; battler < gBattlersCount; battler++)
|
||||
{
|
||||
switch (gBattleMons[battler].ability)
|
||||
switch (GetBattlerAbility(battler))
|
||||
{
|
||||
case ABILITY_IMMUNITY:
|
||||
if (gBattleMons[battler].status1 & (STATUS1_POISON | STATUS1_TOXIC_POISON | STATUS1_TOXIC_COUNTER))
|
||||
@ -5473,7 +5474,7 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move
|
||||
case ABILITYEFFECT_FORECAST: // 6
|
||||
for (battler = 0; battler < gBattlersCount; battler++)
|
||||
{
|
||||
if (gBattleMons[battler].ability == ABILITY_FORECAST || gBattleMons[battler].ability == ABILITY_FLOWER_GIFT)
|
||||
if (GetBattlerAbility(battler) == ABILITY_FORECAST || GetBattlerAbility(battler) == ABILITY_FLOWER_GIFT)
|
||||
{
|
||||
effect = TryWeatherFormChange(battler);
|
||||
if (effect)
|
||||
@ -5534,7 +5535,7 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move
|
||||
case ABILITYEFFECT_INTIMIDATE2:
|
||||
for (i = 0; i < gBattlersCount; i++)
|
||||
{
|
||||
if (gBattleMons[i].ability == ABILITY_INTIMIDATE && gBattleResources->flags->flags[i] & RESOURCE_FLAG_INTIMIDATED)
|
||||
if (GetBattlerAbility(i) == ABILITY_INTIMIDATE && gBattleResources->flags->flags[i] & RESOURCE_FLAG_INTIMIDATED)
|
||||
{
|
||||
gLastUsedAbility = ABILITY_INTIMIDATE;
|
||||
gBattleResources->flags->flags[i] &= ~RESOURCE_FLAG_INTIMIDATED;
|
||||
@ -5601,6 +5602,23 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ABILITYEFFECT_NEUTRALIZINGGAS:
|
||||
// Prints message only. separate from ABILITYEFFECT_ON_SWITCHIN bc activates before entry hazards
|
||||
for (i = 0; i < gBattlersCount; i++)
|
||||
{
|
||||
if (gBattleMons[i].ability == ABILITY_NEUTRALIZING_GAS && !(gBattleResources->flags->flags[i] & RESOURCE_FLAG_NEUTRALIZING_GAS))
|
||||
{
|
||||
gBattleResources->flags->flags[i] |= RESOURCE_FLAG_NEUTRALIZING_GAS;
|
||||
gBattlerAbility = i;
|
||||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SWITCHIN_NEUTRALIZING_GAS;
|
||||
BattleScriptPushCursorAndCallback(BattleScript_SwitchInAbilityMsg);
|
||||
effect++;
|
||||
}
|
||||
|
||||
if (effect)
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (effect && gLastUsedAbility != 0xFF)
|
||||
@ -5611,11 +5629,51 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move
|
||||
return effect;
|
||||
}
|
||||
|
||||
bool32 IsNeutralizingGasBannedAbility(u32 ability)
|
||||
{
|
||||
switch (ability)
|
||||
{
|
||||
case ABILITY_MULTITYPE:
|
||||
case ABILITY_ZEN_MODE:
|
||||
case ABILITY_STANCE_CHANGE:
|
||||
case ABILITY_POWER_CONSTRUCT:
|
||||
case ABILITY_SCHOOLING:
|
||||
case ABILITY_RKS_SYSTEM:
|
||||
case ABILITY_SHIELDS_DOWN:
|
||||
case ABILITY_COMATOSE:
|
||||
case ABILITY_DISGUISE:
|
||||
case ABILITY_GULP_MISSILE:
|
||||
case ABILITY_ICE_FACE:
|
||||
case ABILITY_AS_ONE_ICE_RIDER:
|
||||
case ABILITY_AS_ONE_SHADOW_RIDER:
|
||||
return TRUE;
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
bool32 IsNeutralizingGasOnField(void)
|
||||
{
|
||||
u32 i;
|
||||
|
||||
for (i = 0; i < gBattlersCount; i++)
|
||||
{
|
||||
if (IsBattlerAlive(i) && gBattleMons[i].ability == ABILITY_NEUTRALIZING_GAS && !(gStatuses3[i] & STATUS3_GASTRO_ACID))
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
u32 GetBattlerAbility(u8 battlerId)
|
||||
{
|
||||
if (gStatuses3[battlerId] & STATUS3_GASTRO_ACID)
|
||||
return ABILITY_NONE;
|
||||
else if ((((gBattleMons[gBattlerAttacker].ability == ABILITY_MOLD_BREAKER
|
||||
|
||||
if (IsNeutralizingGasOnField() && !IsNeutralizingGasBannedAbility(gBattleMons[battlerId].ability))
|
||||
return ABILITY_NONE;
|
||||
|
||||
if ((((gBattleMons[gBattlerAttacker].ability == ABILITY_MOLD_BREAKER
|
||||
|| gBattleMons[gBattlerAttacker].ability == ABILITY_TERAVOLT
|
||||
|| gBattleMons[gBattlerAttacker].ability == ABILITY_TURBOBLAZE)
|
||||
&& !(gStatuses3[gBattlerAttacker] & STATUS3_GASTRO_ACID))
|
||||
@ -5625,8 +5683,8 @@ u32 GetBattlerAbility(u8 battlerId)
|
||||
&& gActionsByTurnOrder[gBattlerByTurnOrder[gBattlerAttacker]] == B_ACTION_USE_MOVE
|
||||
&& gCurrentTurnActionNumber < gBattlersCount)
|
||||
return ABILITY_NONE;
|
||||
else
|
||||
return gBattleMons[battlerId].ability;
|
||||
|
||||
return gBattleMons[battlerId].ability;
|
||||
}
|
||||
|
||||
u32 IsAbilityOnSide(u32 battlerId, u32 ability)
|
||||
@ -5678,7 +5736,7 @@ u32 IsAbilityPreventingEscape(u32 battlerId)
|
||||
return 0;
|
||||
#endif
|
||||
#if B_SHADOW_TAG_ESCAPE >= GEN_4
|
||||
if ((id = IsAbilityOnOpposingSide(battlerId, ABILITY_SHADOW_TAG)) && gBattleMons[battlerId].ability != ABILITY_SHADOW_TAG)
|
||||
if ((id = IsAbilityOnOpposingSide(battlerId, ABILITY_SHADOW_TAG)) && GetBattlerAbility(battlerId) != ABILITY_SHADOW_TAG)
|
||||
#else
|
||||
if (id = IsAbilityOnOpposingSide(battlerId, ABILITY_SHADOW_TAG))
|
||||
#endif
|
||||
@ -7226,7 +7284,7 @@ u32 GetMoveTarget(u16 move, u8 setTarget)
|
||||
targetBattler = SetRandomTarget(gBattlerAttacker);
|
||||
if (gBattleMoves[move].type == TYPE_ELECTRIC
|
||||
&& IsAbilityOnOpposingSide(gBattlerAttacker, ABILITY_LIGHTNING_ROD)
|
||||
&& gBattleMons[targetBattler].ability != ABILITY_LIGHTNING_ROD)
|
||||
&& GetBattlerAbility(targetBattler) != ABILITY_LIGHTNING_ROD)
|
||||
{
|
||||
targetBattler ^= BIT_FLANK;
|
||||
RecordAbilityBattle(targetBattler, gBattleMons[targetBattler].ability);
|
||||
@ -7234,7 +7292,7 @@ u32 GetMoveTarget(u16 move, u8 setTarget)
|
||||
}
|
||||
else if (gBattleMoves[move].type == TYPE_WATER
|
||||
&& IsAbilityOnOpposingSide(gBattlerAttacker, ABILITY_STORM_DRAIN)
|
||||
&& gBattleMons[targetBattler].ability != ABILITY_STORM_DRAIN)
|
||||
&& GetBattlerAbility(targetBattler) != ABILITY_STORM_DRAIN)
|
||||
{
|
||||
targetBattler ^= BIT_FLANK;
|
||||
RecordAbilityBattle(targetBattler, gBattleMons[targetBattler].ability);
|
||||
@ -7412,7 +7470,7 @@ u32 GetBattlerHoldEffect(u8 battlerId, bool32 checkNegating)
|
||||
return HOLD_EFFECT_NONE;
|
||||
if (gFieldStatuses & STATUS_FIELD_MAGIC_ROOM)
|
||||
return HOLD_EFFECT_NONE;
|
||||
if (gBattleMons[battlerId].ability == ABILITY_KLUTZ && !(gStatuses3[battlerId] & STATUS3_GASTRO_ACID))
|
||||
if (GetBattlerAbility(battlerId) == ABILITY_KLUTZ)
|
||||
return HOLD_EFFECT_NONE;
|
||||
}
|
||||
|
||||
@ -7467,8 +7525,13 @@ bool32 IsBattlerProtected(u8 battlerId, u16 move)
|
||||
else if (gProtectStructs[battlerId].protected)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!(gBattleMoves[move].flags & FLAG_PROTECT_AFFECTED))
|
||||
|
||||
// Protective Pads doesn't stop Unseen Fist from bypassing Protect effects, so IsMoveMakingContact() isn't used here.
|
||||
// This means extra logic is needed to handle Shell Side Arm.
|
||||
if (GetBattlerAbility(gBattlerAttacker) == ABILITY_UNSEEN_FIST
|
||||
&& (gBattleMoves[move].flags & FLAG_MAKES_CONTACT || (gBattleMoves[move].effect == EFFECT_SHELL_SIDE_ARM && gSwapDamageCategory)))
|
||||
return FALSE;
|
||||
else if (!(gBattleMoves[move].flags & FLAG_PROTECT_AFFECTED))
|
||||
return FALSE;
|
||||
else if (gBattleMoves[move].effect == MOVE_EFFECT_FEINT)
|
||||
return FALSE;
|
||||
@ -8081,7 +8144,7 @@ static u32 CalcMoveBasePowerAfterModifiers(u16 move, u8 battlerAtk, u8 battlerDe
|
||||
if (IsAbilityOnField(ABILITY_AURA_BREAK))
|
||||
MulModifier(&modifier, UQ_4_12(0.75));
|
||||
else
|
||||
MulModifier(&modifier, UQ_4_12(1.25));
|
||||
MulModifier(&modifier, UQ_4_12(1.33));
|
||||
}
|
||||
|
||||
// attacker partner's abilities
|
||||
@ -8242,8 +8305,11 @@ static u32 CalcMoveBasePowerAfterModifiers(u16 move, u8 battlerAtk, u8 battlerDe
|
||||
MulModifier(&modifier, UQ_4_12(0.5));
|
||||
break;
|
||||
case EFFECT_KNOCK_OFF:
|
||||
if (gBattleMons[battlerDef].item != ITEM_NONE && GetBattlerAbility(battlerDef) != ABILITY_STICKY_HOLD)
|
||||
#if B_KNOCK_OFF_DMG >= GEN_6
|
||||
if (gBattleMons[battlerDef].item != ITEM_NONE
|
||||
&& CanBattlerGetOrLoseItem(battlerDef, gBattleMons[battlerDef].item))
|
||||
MulModifier(&modifier, UQ_4_12(1.5));
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
@ -9055,9 +9121,9 @@ bool32 CanMegaEvolve(u8 battlerId)
|
||||
struct MegaEvolutionData *mega = &(((struct ChooseMoveStruct*)(&gBattleResources->bufferA[gActiveBattler][4]))->mega);
|
||||
|
||||
#ifdef ITEM_EXPANSION
|
||||
// Check if Player has a Mega Bracelet
|
||||
// Check if Player has a Mega Ring
|
||||
if ((GetBattlerPosition(battlerId) == B_POSITION_PLAYER_LEFT || (!(gBattleTypeFlags & BATTLE_TYPE_MULTI) && GetBattlerPosition(battlerId) == B_POSITION_PLAYER_RIGHT))
|
||||
&& !CheckBagHasItem(ITEM_MEGA_BRACELET, 1))
|
||||
&& !CheckBagHasItem(ITEM_MEGA_RING, 1))
|
||||
return FALSE;
|
||||
#endif
|
||||
|
||||
|
@ -142,7 +142,7 @@ u32 BattlePalace_TryEscapeStatus(u8 battlerId)
|
||||
{
|
||||
u32 toSub;
|
||||
|
||||
if (gBattleMons[battlerId].ability == ABILITY_EARLY_BIRD)
|
||||
if (GetBattlerAbility(battlerId) == ABILITY_EARLY_BIRD)
|
||||
toSub = 2;
|
||||
else
|
||||
toSub = 1;
|
||||
|
@ -2680,7 +2680,7 @@ const struct Item gItems[] =
|
||||
.name = _("UP-GRADE"),
|
||||
.itemId = ITEM_UP_GRADE,
|
||||
.price = 2100,
|
||||
.holdEffect = HOLD_EFFECT_UP_GRADE,
|
||||
.holdEffect = HOLD_EFFECT_UPGRADE,
|
||||
.description = sUpGradeDesc,
|
||||
.pocket = POCKET_ITEMS,
|
||||
.type = ITEM_USE_BAG_MENU,
|
||||
@ -2767,7 +2767,7 @@ const struct Item gItems[] =
|
||||
.name = _("STICK"),
|
||||
.itemId = ITEM_STICK,
|
||||
.price = 200,
|
||||
.holdEffect = HOLD_EFFECT_STICK,
|
||||
.holdEffect = HOLD_EFFECT_LEEK,
|
||||
.description = sStickDesc,
|
||||
.pocket = POCKET_ITEMS,
|
||||
.type = ITEM_USE_BAG_MENU,
|
||||
|
Loading…
x
Reference in New Issue
Block a user