updated Cmd_accuracycheck

This commit is contained in:
cawtds 2024-04-29 23:07:11 +02:00
parent a1b3e83aac
commit bb55f18b33
9 changed files with 361 additions and 127 deletions

View File

@ -1441,7 +1441,7 @@ BattleScript_EffectMeanLook::
attackcanceler
attackstring
ppreduce
accuracycheck BattleScript_ButItFailed, NO_ACC_CALC
accuracycheck BattleScript_ButItFailed, NO_ACC_CALC_CHECK_LOCK_ON
jumpifstatus2 BS_TARGET, STATUS2_ESCAPE_PREVENTION, BattleScript_ButItFailed
jumpifstatus2 BS_TARGET, STATUS2_SUBSTITUTE, BattleScript_ButItFailed
attackanimation

View File

@ -585,6 +585,8 @@ struct BattleStruct
u8 supremeOverlordCounter[MAX_BATTLERS_COUNT];
struct Illusion illusion[MAX_BATTLERS_COUNT];
u8 trainerSlideFirstSTABMoveMsgState:2;
u8 blunderPolicy:1; // should blunder policy activate
u8 skyDropTargets[MAX_BATTLERS_COUNT]; // For Sky Drop, to account for if multiple Pokemon use Sky Drop in a double battle.
}; // size == 0x200 bytes
extern struct BattleStruct *gBattleStruct;

View File

@ -7,6 +7,12 @@
#define WINDOW_CLEAR (1 << 0)
#define WINDOW_BG1 (1 << 7)
struct StatFractions
{
u8 dividend;
u8 divisor;
};
void AI_CalcDmg(u8 attacker, u8 defender);
u8 TypeCalc(u16 move, u8 attacker, u8 defender);
u8 AI_TypeCalc(u16 move, u16 targetSpecies, u16 targetAbility);
@ -21,7 +27,9 @@ bool8 UproarWakeUpCheck(u8 battlerId);
u8 GetCatchingBattler(void);
bool32 IsMoveAffectedByParentalBond(u32 move, u32 battler);
u32 GetHighestStatId(u32 battlerId);
u32 GetTotalAccuracy(u32 battlerAtk, u32 battlerDef, u32 move, u32 atkAbility, u32 defAbility, u32 atkHoldEffect, u32 defHoldEffect);
extern void (* const gBattleScriptingCommandsTable[])(void);
extern const struct StatFractions gAccuracyStageRatios[];
#endif // GUARD_BATTLE_SCRIPT_COMMANDS_H

View File

@ -133,13 +133,15 @@ void HandleAction_RunBattleScript(void);
u8 GetMoveTarget(u16 move, u8 setTarget);
u8 IsMonDisobedient(void);
// new
bool32 IsNeutralizingGasOnField(void);
bool32 IsMyceliumMightOnField(void);
bool32 IsMoldBreakerTypeAbility(u32 ability);
u32 GetBattlerAbility(u32 battler);
u32 IsAbilityOnSide(u32 battler, u32 ability);
u32 IsAbilityOnOpposingSide(u32 battler, u32 ability);
u32 IsAbilityOnField(u32 ability);
u32 IsAbilityOnFieldExcept(u32 battler, u32 ability);
u32 IsAbilityPreventingEscape(u32 battler);
u32 GetBattlerHoldEffect(u32 battler, bool32 checkNegating);
bool32 IsBattlerAlive(u32 battler);
bool32 CompareStat(u32 battler, u8 statId, u8 cmpTo, u8 cmpKind);
u32 CalcSecondaryEffectChance(u32 battler, u32 battlerAbility, const struct AdditionalEffect *additionalEffect);
@ -175,6 +177,10 @@ u32 GetIllusionMonSpecies(u32 battler);
struct Pokemon *GetIllusionMonPtr(u32 battler);
void ClearIllusionMon(u32 battler);
bool32 SetIllusionMon(struct Pokemon *mon, u32 battler);
u32 GetBattlerAffectionHearts(u32 battler);
u32 GetBattlerHoldEffect(u32 battler, bool32 checkNegating);
u32 GetBattlerHoldEffectIgnoreAbility(u32 battler, bool32 checkNegating);
u32 GetBattlerHoldEffectInternal(u32 battler, bool32 checkNegating, bool32 checkAbility);
// battle_ai_util.h
bool32 IsHealingMove(u32 move);

View File

@ -136,11 +136,16 @@
#define FRIENDSHIP_EVENT_FAINT_OUTSIDE_BATTLE 8
#define FRIENDSHIP_EVENT_FAINT_LARGE 9
#if P_UPDATED_FRIENDSHIP >= GEN_8
#define STANDARD_FRIENDSHIP 50
#else
#define STANDARD_FRIENDSHIP 70
#endif
// Constants for GetBattlerAffectionHearts (based on friendship value)
#define AFFECTION_NO_HEARTS 0 // 0-79 friendship
#define AFFECTION_ONE_HEART 1 // 80-129 friendship
#define AFFECTION_TWO_HEARTS 2 // 130-179 friendship
#define AFFECTION_THREE_HEARTS 3 // 180-219 friendship
#define AFFECTION_FOUR_HEARTS 4 // 220-254 friendship
#define AFFECTION_FIVE_HEARTS 5 // Max friendship
// Friendship value that the majority of species use.
#define STANDARD_FRIENDSHIP ((P_UPDATED_FRIENDSHIP >= GEN_8) ? 50 : 70)
#define MAX_FRIENDSHIP 255
#define MAX_SHEEN 255

View File

@ -657,7 +657,7 @@ void SetBattleMonMoveSlot(struct BattlePokemon *mon, u16 move, u8 slot);
u16 MonTryLearningNewMove(struct Pokemon *mon, bool8 firstMove);
void DeleteFirstMoveAndGiveMoveToMon(struct Pokemon *mon, u16 move);
s32 CalculateBaseDamageOld(struct BattlePokemon *attacker, struct BattlePokemon *defender, u32 move, u32 sideStatus, u16 powerOverride, u8 typeOverride, u8 battlerIdAtk, u8 battlerIdDef);
u32 GetMonAffectionHearts(struct Pokemon *pokemon);
u8 CountAliveMonsInBattle(u8 caseId, u32 battler);
u8 GetDefaultMoveTarget(u8 battlerId);

View File

@ -604,13 +604,7 @@ void (* const gBattleScriptingCommandsTable[])(void) =
Cmd_callnative, //0xFF
};
struct StatFractions
{
u8 dividend;
u8 divisor;
};
static const struct StatFractions sAccuracyStageRatios[] =
const struct StatFractions gAccuracyStageRatios[] =
{
{ 33, 100}, // -6
{ 36, 100}, // -5
@ -1156,22 +1150,23 @@ static void Cmd_attackcanceler(void)
}
}
static void JumpIfMoveFailed(u8 adder, u16 move)
static bool32 JumpIfMoveFailed(u8 adder, u16 move)
{
const u8 *BS_ptr = gBattlescriptCurrInstr + adder;
if (gMoveResultFlags & MOVE_RESULT_NO_EFFECT)
{
gLastLandedMoves[gBattlerTarget] = 0;
gLastHitByType[gBattlerTarget] = 0;
BS_ptr = T1_READ_PTR(gBattlescriptCurrInstr + 1);
gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1);
return TRUE;
}
else
{
TrySetDestinyBondToHappen();
if (AbilityBattleEffects(ABILITYEFFECT_ABSORBING, gBattlerTarget, 0, 0, move))
return;
return TRUE;
}
gBattlescriptCurrInstr = BS_ptr;
gBattlescriptCurrInstr += adder;
return FALSE;
}
static void Cmd_jumpifaffectedbyprotect(void)
@ -1191,7 +1186,7 @@ static void Cmd_jumpifaffectedbyprotect(void)
static bool8 JumpIfMoveAffectedByProtect(u16 move)
{
bool8 affected = FALSE;
if (DEFENDER_IS_PROTECTED)
if (IsBattlerProtected(gBattlerTarget, move))
{
gMoveResultFlags |= MOVE_RESULT_MISSED;
JumpIfMoveFailed(7, move);
@ -1201,43 +1196,81 @@ static bool8 JumpIfMoveAffectedByProtect(u16 move)
return affected;
}
static bool8 AccuracyCalcHelper(u16 move)
static bool32 AccuracyCalcHelper(u16 move)
{
if (gStatuses3[gBattlerTarget] & STATUS3_ALWAYS_HITS && gDisableStructs[gBattlerTarget].battlerWithSureHit == gBattlerAttacker)
if ((gStatuses3[gBattlerTarget] & STATUS3_ALWAYS_HITS && gDisableStructs[gBattlerTarget].battlerWithSureHit == gBattlerAttacker)
|| (B_TOXIC_NEVER_MISS >= GEN_6 && gMovesInfo[move].effect == EFFECT_TOXIC && IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_POISON))
|| gStatuses4[gBattlerTarget] & STATUS4_GLAIVE_RUSH)
{
JumpIfMoveFailed(7, move);
return TRUE;
}
// If the attacker has the ability No Guard and they aren't targeting a Pokemon involved in a Sky Drop with the move Sky Drop, move hits.
else if (GetBattlerAbility(gBattlerAttacker) == ABILITY_NO_GUARD && (move != MOVE_SKY_DROP || gBattleStruct->skyDropTargets[gBattlerTarget] == 0xFF))
{
if (!JumpIfMoveFailed(7, move))
RecordAbilityBattle(gBattlerAttacker, ABILITY_NO_GUARD);
return TRUE;
}
// If the target has the ability No Guard and they aren't involved in a Sky Drop or the current move isn't Sky Drop, move hits.
else if (GetBattlerAbility(gBattlerTarget) == ABILITY_NO_GUARD && (move != MOVE_SKY_DROP || gBattleStruct->skyDropTargets[gBattlerTarget] == 0xFF))
{
if (!JumpIfMoveFailed(7, move))
RecordAbilityBattle(gBattlerTarget, ABILITY_NO_GUARD);
return TRUE;
}
// If the target is under the effects of Telekinesis, and the move isn't a OH-KO move, move hits.
else if (gStatuses3[gBattlerTarget] & STATUS3_TELEKINESIS
&& !(gStatuses3[gBattlerTarget] & STATUS3_SEMI_INVULNERABLE)
&& gMovesInfo[move].effect != EFFECT_OHKO)
{
JumpIfMoveFailed(7, move);
return TRUE;
}
if (!(gHitMarker & HITMARKER_IGNORE_ON_AIR) && gStatuses3[gBattlerTarget] & STATUS3_ON_AIR)
// TODO: Z-Moves
// if (gBattleStruct->zmove.active && !(gStatuses3[gBattlerTarget] & STATUS3_SEMI_INVULNERABLE))
// {
// JumpIfMoveFailed(7, move);
// return TRUE;
// }
if ((gStatuses3[gBattlerTarget] & STATUS3_PHANTOM_FORCE)
|| ((gStatuses3[gBattlerTarget] & STATUS3_ON_AIR) && !(gMovesInfo[move].damagesAirborne || gMovesInfo[move].damagesAirborneDoubleDamage))
|| ((gStatuses3[gBattlerTarget] & STATUS3_UNDERGROUND) && !gMovesInfo[move].damagesUnderground)
|| ((gStatuses3[gBattlerTarget] & STATUS3_UNDERWATER) && !gMovesInfo[move].damagesUnderwater))
{
gMoveResultFlags |= MOVE_RESULT_MISSED;
JumpIfMoveFailed(7, move);
return TRUE;
}
gHitMarker &= ~HITMARKER_IGNORE_ON_AIR;
if (!(gHitMarker & HITMARKER_IGNORE_UNDERGROUND) && gStatuses3[gBattlerTarget] & STATUS3_UNDERGROUND)
if (WEATHER_HAS_EFFECT)
{
if ((gMovesInfo[move].effect == EFFECT_THUNDER || gMovesInfo[move].effect == EFFECT_RAIN_ALWAYS_HIT)
&& IsBattlerWeatherAffected(gBattlerTarget, B_WEATHER_RAIN))
{
// thunder/hurricane/genie moves ignore acc checks in rain unless target is holding utility umbrella
JumpIfMoveFailed(7, move);
return TRUE;
}
else if ((gBattleWeather & (B_WEATHER_HAIL | B_WEATHER_SNOW)) && gMovesInfo[move].effect == EFFECT_BLIZZARD)
{
// Blizzard ignores acc checks in Hail in Gen4+
JumpIfMoveFailed(7, move);
return TRUE;
}
}
if (B_MINIMIZE_DMG_ACC >= GEN_6
&& (gStatuses3[gBattlerTarget] & STATUS3_MINIMIZED)
&& gMovesInfo[move].minimizeDoubleDamage)
{
gMoveResultFlags |= MOVE_RESULT_MISSED;
JumpIfMoveFailed(7, move);
return TRUE;
}
gHitMarker &= ~HITMARKER_IGNORE_UNDERGROUND;
if (!(gHitMarker & HITMARKER_IGNORE_UNDERWATER) && gStatuses3[gBattlerTarget] & STATUS3_UNDERWATER)
{
gMoveResultFlags |= MOVE_RESULT_MISSED;
JumpIfMoveFailed(7, move);
return TRUE;
}
gHitMarker &= ~HITMARKER_IGNORE_UNDERWATER;
if ((WEATHER_HAS_EFFECT && (gBattleWeather & B_WEATHER_RAIN) && gMovesInfo[move].effect == EFFECT_THUNDER)
|| (gMovesInfo[move].effect == EFFECT_ALWAYS_HIT || gMovesInfo[move].effect == EFFECT_VITAL_THROW))
if (gMovesInfo[move].accuracy == 0)
{
JumpIfMoveFailed(7, move);
return TRUE;
@ -1246,10 +1279,137 @@ static bool8 AccuracyCalcHelper(u16 move)
return FALSE;
}
u32 GetTotalAccuracy(u32 battlerAtk, u32 battlerDef, u32 move, u32 atkAbility, u32 defAbility, u32 atkHoldEffect, u32 defHoldEffect)
{
u32 calc, moveAcc;
s8 buff, accStage, evasionStage;
u32 atkParam = GetBattlerHoldEffectParam(battlerAtk);
u32 defParam = GetBattlerHoldEffectParam(battlerDef);
u32 atkAlly = BATTLE_PARTNER(battlerAtk);
u32 atkAllyAbility = GetBattlerAbility(atkAlly);
gPotentialItemEffectBattler = battlerDef;
accStage = gBattleMons[battlerAtk].statStages[STAT_ACC];
evasionStage = gBattleMons[battlerDef].statStages[STAT_EVASION];
if (atkAbility == ABILITY_UNAWARE || atkAbility == ABILITY_KEEN_EYE || atkAbility == ABILITY_MINDS_EYE
|| (B_ILLUMINATE_EFFECT >= GEN_9 && atkAbility == ABILITY_ILLUMINATE))
evasionStage = DEFAULT_STAT_STAGE;
if (gMovesInfo[move].ignoresTargetDefenseEvasionStages)
evasionStage = DEFAULT_STAT_STAGE;
if (defAbility == ABILITY_UNAWARE)
accStage = DEFAULT_STAT_STAGE;
if (gBattleMons[battlerDef].status2 & STATUS2_FORESIGHT || gStatuses3[battlerDef] & STATUS3_MIRACLE_EYED)
buff = accStage;
else
buff = accStage + DEFAULT_STAT_STAGE - evasionStage;
if (buff < MIN_STAT_STAGE)
buff = MIN_STAT_STAGE;
if (buff > MAX_STAT_STAGE)
buff = MAX_STAT_STAGE;
moveAcc = gMovesInfo[move].accuracy;
// Check Thunder and Hurricane on sunny weather.
if (IsBattlerWeatherAffected(battlerDef, B_WEATHER_SUN) && gMovesInfo[move].effect == EFFECT_THUNDER)
moveAcc = 50;
// Check Wonder Skin.
if (defAbility == ABILITY_WONDER_SKIN && IS_MOVE_STATUS(move) && moveAcc > 50)
moveAcc = 50;
calc = gAccuracyStageRatios[buff].dividend * moveAcc;
calc /= gAccuracyStageRatios[buff].divisor;
// Attacker's ability
switch (atkAbility)
{
case ABILITY_COMPOUND_EYES:
calc = (calc * 130) / 100; // 1.3 compound eyes boost
break;
case ABILITY_VICTORY_STAR:
calc = (calc * 110) / 100; // 1.1 victory star boost
break;
case ABILITY_HUSTLE:
if (IS_MOVE_PHYSICAL(move))
calc = (calc * 80) / 100; // 1.2 hustle loss
break;
}
// Target's ability
switch (defAbility)
{
case ABILITY_SAND_VEIL:
if (WEATHER_HAS_EFFECT && gBattleWeather & B_WEATHER_SANDSTORM)
calc = (calc * 80) / 100; // 1.2 sand veil loss
break;
case ABILITY_SNOW_CLOAK:
if (WEATHER_HAS_EFFECT && (gBattleWeather & (B_WEATHER_HAIL | B_WEATHER_SNOW)))
calc = (calc * 80) / 100; // 1.2 snow cloak loss
break;
case ABILITY_TANGLED_FEET:
if (gBattleMons[battlerDef].status2 & STATUS2_CONFUSION)
calc = (calc * 50) / 100; // 1.5 tangled feet loss
break;
}
// Attacker's ally's ability
switch (atkAllyAbility)
{
case ABILITY_VICTORY_STAR:
if (IsBattlerAlive(atkAlly))
calc = (calc * 110) / 100; // 1.1 ally's victory star boost
break;
}
// Attacker's hold effect
switch (atkHoldEffect)
{
case HOLD_EFFECT_WIDE_LENS:
calc = (calc * (100 + atkParam)) / 100;
break;
case HOLD_EFFECT_ZOOM_LENS:
if (GetBattlerTurnOrderNum(battlerAtk) > GetBattlerTurnOrderNum(battlerDef))
calc = (calc * (100 + atkParam)) / 100;
break;
}
// Target's hold effect
switch (defHoldEffect)
{
case HOLD_EFFECT_EVASION_UP:
calc = (calc * (100 - defParam)) / 100;
break;
}
if (gProtectStructs[battlerAtk].usedMicleBerry)
{
gProtectStructs[battlerAtk].usedMicleBerry = FALSE;
if (atkAbility == ABILITY_RIPEN)
calc = (calc * 140) / 100; // ripen gives 40% acc boost
else
calc = (calc * 120) / 100; // 20% acc boost
}
if (gFieldStatuses & STATUS_FIELD_GRAVITY)
calc = (calc * 5) / 3; // 1.66 Gravity acc boost
if (B_AFFECTION_MECHANICS == TRUE && GetBattlerAffectionHearts(battlerDef) == AFFECTION_FIVE_HEARTS)
calc = (calc * 90) / 100;
return calc;
}
static void Cmd_accuracycheck(void)
{
u16 move = T2_READ_16(gBattlescriptCurrInstr + 5);
CMD_ARGS(const u8 *failInstr, u16 move);
u32 type, move = cmd->move;
u32 moveTarget = GetBattlerMoveTargetType(gBattlerAttacker, move);
u32 abilityAtk = GetBattlerAbility(gBattlerAttacker);
u32 abilityDef = GetBattlerAbility(gBattlerTarget);
u32 holdEffectAtk = GetBattlerHoldEffect(gBattlerAttacker, TRUE);
// pokefirered specific
if ((gBattleTypeFlags & BATTLE_TYPE_FIRST_BATTLE
&& !BtlCtrl_OakOldMan_TestState2Flag(1)
&& gMovesInfo[move].power != 0
@ -1263,89 +1423,61 @@ static void Cmd_accuracycheck(void)
JumpIfMoveFailed(7, move);
return;
}
if (move == NO_ACC_CALC || move == NO_ACC_CALC_CHECK_LOCK_ON)
if (move == ACC_CURR_MOVE)
move = gCurrentMove;
if (move == NO_ACC_CALC_CHECK_LOCK_ON)
{
if (gStatuses3[gBattlerTarget] & STATUS3_ALWAYS_HITS && move == NO_ACC_CALC_CHECK_LOCK_ON && gDisableStructs[gBattlerTarget].battlerWithSureHit == gBattlerAttacker)
gBattlescriptCurrInstr += 7;
else if (gStatuses3[gBattlerTarget] & (STATUS3_ON_AIR | STATUS3_UNDERGROUND | STATUS3_UNDERWATER))
gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1);
else if (!JumpIfMoveAffectedByProtect(0))
gBattlescriptCurrInstr += 7;
if (gStatuses3[gBattlerTarget] & STATUS3_ALWAYS_HITS && gDisableStructs[gBattlerTarget].battlerWithSureHit == gBattlerAttacker)
gBattlescriptCurrInstr = cmd->nextInstr;
else if (gStatuses3[gBattlerTarget] & (STATUS3_SEMI_INVULNERABLE))
gBattlescriptCurrInstr = cmd->failInstr;
else if (!JumpIfMoveAffectedByProtect(gCurrentMove))
gBattlescriptCurrInstr = cmd->nextInstr;
}
else if (gSpecialStatuses[gBattlerAttacker].parentalBondState == PARENTAL_BOND_2ND_HIT
|| (gSpecialStatuses[gBattlerAttacker].multiHitOn
&& (abilityAtk == ABILITY_SKILL_LINK || holdEffectAtk == HOLD_EFFECT_LOADED_DICE
|| !(gMovesInfo[move].effect == EFFECT_TRIPLE_KICK || gMovesInfo[move].effect == EFFECT_POPULATION_BOMB))))
{
// No acc checks for second hit of Parental Bond or multi hit moves, except Triple Kick/Triple Axel/Population Bomb
gBattlescriptCurrInstr = cmd->nextInstr;
}
else
{
u8 type, moveAcc, holdEffect, param;
s8 buff;
u16 calc;
if (move == ACC_CURR_MOVE)
move = gCurrentMove;
u32 accuracy;
GET_MOVE_TYPE(move, type);
if (JumpIfMoveAffectedByProtect(move))
return;
if (AccuracyCalcHelper(move))
return;
if (gBattleMons[gBattlerTarget].status2 & STATUS2_FORESIGHT)
{
u8 acc = gBattleMons[gBattlerAttacker].statStages[STAT_ACC];
buff = acc;
}
else
{
u8 acc = gBattleMons[gBattlerAttacker].statStages[STAT_ACC];
buff = acc + DEFAULT_STAT_STAGE - gBattleMons[gBattlerTarget].statStages[STAT_EVASION];
}
accuracy = GetTotalAccuracy(
gBattlerAttacker,
gBattlerTarget,
move,
abilityAtk,
abilityDef,
holdEffectAtk,
GetBattlerHoldEffect(gBattlerTarget, TRUE)
);
if (buff < MIN_STAT_STAGE)
buff = MIN_STAT_STAGE;
if (buff > MAX_STAT_STAGE)
buff = MAX_STAT_STAGE;
moveAcc = gMovesInfo[move].accuracy;
// check Thunder on sunny weather
if (WEATHER_HAS_EFFECT && gBattleWeather & B_WEATHER_SUN && gMovesInfo[move].effect == EFFECT_THUNDER)
moveAcc = 50;
calc = sAccuracyStageRatios[buff].dividend * moveAcc;
calc /= sAccuracyStageRatios[buff].divisor;
if (gBattleMons[gBattlerAttacker].ability == ABILITY_COMPOUND_EYES)
calc = (calc * 130) / 100; // 1.3 compound eyes boost
if (WEATHER_HAS_EFFECT && gBattleMons[gBattlerTarget].ability == ABILITY_SAND_VEIL && gBattleWeather & B_WEATHER_SANDSTORM)
calc = (calc * 80) / 100; // 1.2 sand veil loss
if (gBattleMons[gBattlerAttacker].ability == ABILITY_HUSTLE && IS_TYPE_PHYSICAL(type))
calc = (calc * 80) / 100; // 1.2 hustle loss
if (gBattleMons[gBattlerTarget].item == ITEM_ENIGMA_BERRY)
{
holdEffect = gEnigmaBerries[gBattlerTarget].holdEffect;
param = gEnigmaBerries[gBattlerTarget].holdEffectParam;
}
else
{
holdEffect = ItemId_GetHoldEffect(gBattleMons[gBattlerTarget].item);
param = ItemId_GetHoldEffectParam(gBattleMons[gBattlerTarget].item);
}
gPotentialItemEffectBattler = gBattlerTarget;
if (holdEffect == HOLD_EFFECT_EVASION_UP)
calc = (calc * (100 - param)) / 100;
// final calculation
if ((Random() % 100 + 1) > calc)
if (!RandomPercentage(RNG_ACCURACY, accuracy))
{
gMoveResultFlags |= MOVE_RESULT_MISSED;
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE
&& (gMovesInfo[move].target == MOVE_TARGET_BOTH || gMovesInfo[move].target == MOVE_TARGET_FOES_AND_ALLY))
if (holdEffectAtk == HOLD_EFFECT_BLUNDER_POLICY)
gBattleStruct->blunderPolicy = TRUE; // Only activates from missing through acc/evasion checks
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE &&
(moveTarget == MOVE_TARGET_BOTH || moveTarget == MOVE_TARGET_FOES_AND_ALLY))
gBattleCommunication[MISS_TYPE] = B_MSG_AVOIDED_ATK;
else
gBattleCommunication[MISS_TYPE] = B_MSG_MISSED;
CheckWonderGuardAndLevitate();
if (gMovesInfo[move].power)
CalcTypeEffectivenessMultiplier(move, type, gBattlerAttacker, gBattlerTarget, abilityDef, TRUE);
}
JumpIfMoveFailed(7, move);
}

View File

@ -3566,44 +3566,90 @@ static bool32 IsBattlerModernFatefulEncounter(u8 battlerId)
return GetMonData(&gPlayerParty[gBattlerPartyIndexes[battlerId]], MON_DATA_MODERN_FATEFUL_ENCOUNTER, NULL);
}
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;
}
bool32 IsMyceliumMightOnField(void)
{
u32 i;
for (i = 0; i < gBattlersCount; i++)
{
if (IsBattlerAlive(i) && gBattleMons[i].ability == ABILITY_MYCELIUM_MIGHT && IS_MOVE_STATUS(gCurrentMove))
return TRUE;
}
return FALSE;
}
bool32 IsMoldBreakerTypeAbility(u32 ability)
{
return (ability == ABILITY_MOLD_BREAKER || ability == ABILITY_TERAVOLT || ability == ABILITY_TURBOBLAZE);
}
u32 GetBattlerAbility(u32 battler)
{
// if (gStatuses3[battler] & STATUS3_GASTRO_ACID)
// return ABILITY_NONE;
if (gAbilitiesInfo[gBattleMons[battler].ability].cantBeSuppressed)
return gBattleMons[battler].ability;
// if (IsNeutralizingGasOnField() && !IsNeutralizingGasBannedAbility(gBattleMons[battler].ability))
// return ABILITY_NONE;
if (gStatuses3[battler] & STATUS3_GASTRO_ACID)
return ABILITY_NONE;
// if (IsMyceliumMightOnField())
// return ABILITY_NONE;
if (IsNeutralizingGasOnField()
&& gBattleMons[battler].ability != ABILITY_NEUTRALIZING_GAS
&& GetBattlerHoldEffectIgnoreAbility(battler, TRUE) != HOLD_EFFECT_ABILITY_SHIELD)
return ABILITY_NONE;
// if (((IsMoldBreakerTypeAbility(gBattleMons[gBattlerAttacker].ability)
// && !(gStatuses3[gBattlerAttacker] & STATUS3_GASTRO_ACID))
// || gBattleMoves[gCurrentMove].ignoresTargetAbility)
// && sAbilitiesAffectedByMoldBreaker[gBattleMons[battler].ability]
// && gBattlerByTurnOrder[gCurrentTurnActionNumber] == gBattlerAttacker
// && gActionsByTurnOrder[gBattlerByTurnOrder[gBattlerAttacker]] == B_ACTION_USE_MOVE
// && gCurrentTurnActionNumber < gBattlersCount)
// return ABILITY_NONE;
if (IsMyceliumMightOnField())
return ABILITY_NONE;
if (((IsMoldBreakerTypeAbility(gBattleMons[gBattlerAttacker].ability)
&& !(gStatuses3[gBattlerAttacker] & STATUS3_GASTRO_ACID))
|| gMovesInfo[gCurrentMove].ignoresTargetAbility)
&& gAbilitiesInfo[gBattleMons[battler].ability].breakable
&& gBattlerByTurnOrder[gCurrentTurnActionNumber] == gBattlerAttacker
&& gActionsByTurnOrder[gBattlerByTurnOrder[gBattlerAttacker]] == B_ACTION_USE_MOVE
&& gCurrentTurnActionNumber < gBattlersCount)
return ABILITY_NONE;
return gBattleMons[battler].ability;
}
u32 GetBattlerHoldEffect(u32 battler, bool32 checkNegating)
{
return GetBattlerHoldEffectInternal(battler, checkNegating, TRUE);
}
u32 GetBattlerHoldEffectIgnoreAbility(u32 battler, bool32 checkNegating)
{
return GetBattlerHoldEffectInternal(battler, checkNegating, FALSE);
}
u32 GetBattlerHoldEffectInternal(u32 battler, bool32 checkNegating, bool32 checkAbility)
{
if (checkNegating)
{
// if (gStatuses3[battler] & STATUS3_EMBARGO)
// return HOLD_EFFECT_NONE;
// if (gFieldStatuses & STATUS_FIELD_MAGIC_ROOM)
// return HOLD_EFFECT_NONE;
if (GetBattlerAbility(battler) == ABILITY_KLUTZ)
if (gStatuses3[battler] & STATUS3_EMBARGO)
return HOLD_EFFECT_NONE;
if (gFieldStatuses & STATUS_FIELD_MAGIC_ROOM)
return HOLD_EFFECT_NONE;
if (checkAbility && GetBattlerAbility(battler) == ABILITY_KLUTZ)
return HOLD_EFFECT_NONE;
}
gPotentialItemEffectBattler = battler;
if (gBattleMons[battler].item == ITEM_ENIGMA_BERRY)
if (gBattleMons[battler].item == ITEM_ENIGMA_BERRY_E_READER)
return gEnigmaBerries[battler].holdEffect;
else
return ItemId_GetHoldEffect(gBattleMons[battler].item);
@ -6116,6 +6162,23 @@ bool32 SetIllusionMon(struct Pokemon *mon, u32 battler)
return FALSE;
}
u32 GetBattlerAffectionHearts(u32 battler)
{
u8 side = GetBattlerSide(battler);
struct Pokemon *party = GetSideParty(side);
u16 species = GetMonData(&party[gBattlerPartyIndexes[battler]], MON_DATA_SPECIES);
if (side != B_SIDE_PLAYER)
return AFFECTION_NO_HEARTS;
else if (gSpeciesInfo[species].isMegaEvolution
|| (gBattleTypeFlags & (BATTLE_TYPE_EREADER_TRAINER
| BATTLE_TYPE_BATTLE_TOWER
| BATTLE_TYPE_LINK)))
return AFFECTION_NO_HEARTS;
return GetMonAffectionHearts(&party[gBattlerPartyIndexes[battler]]);
}
// battle_ai_util.c

View File

@ -6166,3 +6166,21 @@ u16 GetFormSpeciesId(u16 speciesId, u8 formId)
else
return speciesId;
}
u32 GetMonAffectionHearts(struct Pokemon *pokemon)
{
u32 friendship = GetMonData(pokemon, MON_DATA_FRIENDSHIP, NULL);
if (friendship == MAX_FRIENDSHIP)
return AFFECTION_FIVE_HEARTS;
if (friendship >= 220)
return AFFECTION_FOUR_HEARTS;
if (friendship >= 180)
return AFFECTION_THREE_HEARTS;
if (friendship >= 130)
return AFFECTION_TWO_HEARTS;
if (friendship >= 80)
return AFFECTION_ONE_HEART;
return AFFECTION_NO_HEARTS;
}