Remove more instances of hard-coded Move IDs (#7056)
This commit is contained in:
parent
21f0eb1583
commit
d795de9e9d
@ -8,6 +8,7 @@ enum BattleMoveEffects
|
||||
EFFECT_NON_VOLATILE_STATUS,
|
||||
EFFECT_ABSORB,
|
||||
EFFECT_EXPLOSION,
|
||||
EFFECT_MISTY_EXPLOSION, // Same as EFFECT_EXPLOSION but it's boosted on Misty Terrain
|
||||
EFFECT_DREAM_EATER,
|
||||
EFFECT_MIRROR_MOVE,
|
||||
EFFECT_ATTACK_UP,
|
||||
@ -34,6 +35,7 @@ enum BattleMoveEffects
|
||||
EFFECT_LIGHT_SCREEN,
|
||||
EFFECT_REST,
|
||||
EFFECT_OHKO,
|
||||
EFFECT_SHEER_COLD, // Same as EFFECT_OHKO but Ice-types are immune to it and has decreased accuracy for non Ice-type users.
|
||||
EFFECT_FUSION_COMBO,
|
||||
EFFECT_FIXED_PERCENT_DAMAGE,
|
||||
EFFECT_FIXED_HP_DAMAGE,
|
||||
|
||||
@ -494,7 +494,7 @@ static inline u32 GetMoveTwoTurnAttackWeather(u32 moveId)
|
||||
return gMovesInfo[SanitizeMoveId(moveId)].argument.twoTurnAttack.status;
|
||||
}
|
||||
|
||||
static inline u32 GetMoveProtectMethod(u32 moveId)
|
||||
static inline enum ProtectMethod GetMoveProtectMethod(u32 moveId)
|
||||
{
|
||||
return gMovesInfo[SanitizeMoveId(moveId)].argument.protectMethod;
|
||||
}
|
||||
|
||||
@ -1085,6 +1085,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
case EFFECT_LEVEL_DAMAGE:
|
||||
case EFFECT_PSYWAVE:
|
||||
case EFFECT_OHKO:
|
||||
case EFFECT_SHEER_COLD:
|
||||
case EFFECT_BIDE:
|
||||
case EFFECT_FIXED_PERCENT_DAMAGE:
|
||||
case EFFECT_ENDEAVOR:
|
||||
@ -1293,6 +1294,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
default:
|
||||
break; // check move damage
|
||||
case EFFECT_EXPLOSION:
|
||||
case EFFECT_MISTY_EXPLOSION:
|
||||
if (!(gAiThinkingStruct->aiFlags[battlerAtk] & AI_FLAG_WILL_SUICIDE))
|
||||
ADJUST_SCORE(-2);
|
||||
|
||||
@ -1675,9 +1677,11 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
|| !(weather & (B_WEATHER_HAIL | B_WEATHER_SNOW)))
|
||||
ADJUST_SCORE(-10);
|
||||
break;
|
||||
case EFFECT_OHKO:
|
||||
if (B_SHEER_COLD_IMMUNITY >= GEN_7 && move == MOVE_SHEER_COLD && IS_BATTLER_OF_TYPE(battlerDef, TYPE_ICE))
|
||||
case EFFECT_SHEER_COLD:
|
||||
if (B_SHEER_COLD_IMMUNITY >= GEN_7 && IS_BATTLER_OF_TYPE(battlerDef, TYPE_ICE))
|
||||
RETURN_SCORE_MINUS(20);
|
||||
// fallthrough
|
||||
case EFFECT_OHKO:
|
||||
if (!ShouldTryOHKO(battlerAtk, battlerDef, aiData->abilities[battlerAtk], aiData->abilities[battlerDef], move))
|
||||
ADJUST_SCORE(-10);
|
||||
else if (GetActiveGimmick(battlerDef) == GIMMICK_DYNAMAX)
|
||||
@ -2194,24 +2198,27 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
case EFFECT_PROTECT:
|
||||
{
|
||||
bool32 decreased = FALSE;
|
||||
switch (move)
|
||||
enum ProtectMethod protectMethod = GetMoveProtectMethod(move);
|
||||
switch (protectMethod)
|
||||
{
|
||||
case MOVE_QUICK_GUARD:
|
||||
case MOVE_WIDE_GUARD:
|
||||
case MOVE_CRAFTY_SHIELD:
|
||||
case PROTECT_QUICK_GUARD:
|
||||
case PROTECT_WIDE_GUARD:
|
||||
case PROTECT_CRAFTY_SHIELD:
|
||||
if (!isDoubleBattle)
|
||||
{
|
||||
ADJUST_SCORE(-10);
|
||||
decreased = TRUE;
|
||||
}
|
||||
break;
|
||||
case MOVE_MAT_BLOCK:
|
||||
case PROTECT_MAT_BLOCK:
|
||||
if (!gDisableStructs[battlerAtk].isFirstTurn)
|
||||
{
|
||||
ADJUST_SCORE(-10);
|
||||
decreased = TRUE;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
} // move check
|
||||
|
||||
if (decreased)
|
||||
@ -2222,9 +2229,9 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
break;
|
||||
}
|
||||
|
||||
if (move != MOVE_QUICK_GUARD
|
||||
&& move != MOVE_WIDE_GUARD
|
||||
&& move != MOVE_CRAFTY_SHIELD) //These moves have infinite usage
|
||||
if (protectMethod != PROTECT_QUICK_GUARD
|
||||
&& protectMethod != PROTECT_WIDE_GUARD
|
||||
&& protectMethod != PROTECT_CRAFTY_SHIELD) //These moves have infinite usage
|
||||
{
|
||||
if (GetBattlerSecondaryDamage(battlerAtk) >= gBattleMons[battlerAtk].hp
|
||||
&& !(IsMoxieTypeAbility(aiData->abilities[battlerDef])))
|
||||
@ -2704,7 +2711,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
break;
|
||||
case EFFECT_INSTRUCT:
|
||||
{
|
||||
u16 instructedMove;
|
||||
u32 instructedMove;
|
||||
if (AI_IsSlower(battlerAtk, battlerDef, move))
|
||||
instructedMove = predictedMove;
|
||||
else
|
||||
@ -2729,15 +2736,16 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
}
|
||||
else
|
||||
{
|
||||
enum BattleMoveEffects instructedEffect = GetMoveEffect(instructedMove);
|
||||
if (GetBattlerMoveTargetType(battlerDef, instructedMove) & (MOVE_TARGET_SELECTED
|
||||
| MOVE_TARGET_DEPENDS
|
||||
| MOVE_TARGET_RANDOM
|
||||
| MOVE_TARGET_BOTH
|
||||
| MOVE_TARGET_FOES_AND_ALLY
|
||||
| MOVE_TARGET_OPPONENTS_FIELD)
|
||||
&& instructedMove != MOVE_MIND_BLOWN && instructedMove != MOVE_STEEL_BEAM)
|
||||
&& instructedEffect != EFFECT_MIND_BLOWN && instructedEffect != EFFECT_MAX_HP_50_RECOIL)
|
||||
ADJUST_SCORE(-10); //Don't force the enemy to attack you again unless it can kill itself with Mind Blown
|
||||
else if (instructedMove != MOVE_MIND_BLOWN)
|
||||
else if (instructedEffect != EFFECT_MIND_BLOWN)
|
||||
ADJUST_SCORE(-5); //Do something better
|
||||
}
|
||||
}
|
||||
@ -2932,7 +2940,9 @@ static s32 AI_TryToFaint(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
if (IsBattleMoveStatus(move))
|
||||
return score; // status moves aren't accounted here
|
||||
|
||||
if (CanIndexMoveFaintTarget(battlerAtk, battlerDef, movesetIndex, AI_ATTACKING) && GetMoveEffect(move) != EFFECT_EXPLOSION)
|
||||
enum BattleMoveEffects effect = GetMoveEffect(move);
|
||||
if (CanIndexMoveFaintTarget(battlerAtk, battlerDef, movesetIndex, AI_ATTACKING)
|
||||
&& effect != EFFECT_EXPLOSION && effect != EFFECT_MISTY_EXPLOSION)
|
||||
{
|
||||
if (CanIndexMoveGuaranteeFaintTarget(battlerAtk, battlerDef, movesetIndex))
|
||||
ADJUST_SCORE(1); // Bonus point if the KO is guaranteed
|
||||
@ -3108,6 +3118,7 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
switch (effect)
|
||||
{
|
||||
case EFFECT_EXPLOSION:
|
||||
case EFFECT_MISTY_EXPLOSION:
|
||||
if (gAiThinkingStruct->aiFlags[battlerAtk] & (AI_FLAG_RISKY | AI_FLAG_WILL_SUICIDE))
|
||||
{
|
||||
RETURN_SCORE_PLUS(10);
|
||||
@ -3860,6 +3871,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
|
||||
ADJUST_SCORE(DECENT_EFFECT);
|
||||
break;
|
||||
case EFFECT_EXPLOSION:
|
||||
case EFFECT_MISTY_EXPLOSION:
|
||||
case EFFECT_MEMENTO:
|
||||
if (gAiThinkingStruct->aiFlags[battlerAtk] & AI_FLAG_WILL_SUICIDE && gBattleMons[battlerDef].statStages[STAT_EVASION] < 7)
|
||||
{
|
||||
@ -4109,6 +4121,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
|
||||
}
|
||||
break;
|
||||
case EFFECT_OHKO:
|
||||
case EFFECT_SHEER_COLD:
|
||||
if (GetActiveGimmick(battlerDef) == GIMMICK_DYNAMAX)
|
||||
break;
|
||||
else if (gStatuses3[battlerAtk] & STATUS3_ALWAYS_HITS)
|
||||
@ -4234,7 +4247,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
|
||||
ADJUST_SCORE(BEST_EFFECT);
|
||||
break;
|
||||
case EFFECT_LOCK_ON:
|
||||
if (HasMoveWithEffect(battlerAtk, EFFECT_OHKO))
|
||||
if (HasMoveWithEffect(battlerAtk, EFFECT_OHKO) || HasMoveWithEffect(battlerAtk, EFFECT_SHEER_COLD))
|
||||
ADJUST_SCORE(GOOD_EFFECT);
|
||||
else if (HasMoveWithLowAccuracy(battlerAtk, battlerDef, 85, TRUE, aiData->abilities[battlerAtk], aiData->abilities[battlerDef], aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef]))
|
||||
ADJUST_SCORE(GOOD_EFFECT);
|
||||
@ -4270,13 +4283,14 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
|
||||
case EFFECT_PROTECT:
|
||||
if (predictedMove == 0xFFFF)
|
||||
predictedMove = MOVE_NONE;
|
||||
switch (move)
|
||||
enum ProtectMethod protectMethod = GetMoveProtectMethod(move);
|
||||
switch (protectMethod)
|
||||
{
|
||||
case MOVE_QUICK_GUARD:
|
||||
case PROTECT_QUICK_GUARD:
|
||||
if (predictedMove != MOVE_NONE && GetMovePriority(predictedMove) > 0)
|
||||
ProtectChecks(battlerAtk, battlerDef, move, predictedMove, &score);
|
||||
break;
|
||||
case MOVE_WIDE_GUARD:
|
||||
case PROTECT_WIDE_GUARD:
|
||||
if (predictedMove != MOVE_NONE && GetBattlerMoveTargetType(battlerDef, predictedMove) & (MOVE_TARGET_FOES_AND_ALLY | MOVE_TARGET_BOTH))
|
||||
{
|
||||
ProtectChecks(battlerAtk, battlerDef, move, predictedMove, &score);
|
||||
@ -4287,17 +4301,17 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
|
||||
ProtectChecks(battlerAtk, battlerDef, move, predictedMove, &score);
|
||||
}
|
||||
break;
|
||||
case MOVE_CRAFTY_SHIELD:
|
||||
case PROTECT_CRAFTY_SHIELD:
|
||||
if (predictedMove != MOVE_NONE && IsBattleMoveStatus(predictedMove) && !(GetBattlerMoveTargetType(battlerDef, predictedMove) & MOVE_TARGET_USER))
|
||||
ProtectChecks(battlerAtk, battlerDef, move, predictedMove, &score);
|
||||
break;
|
||||
|
||||
case MOVE_MAT_BLOCK:
|
||||
case PROTECT_MAT_BLOCK:
|
||||
if (gDisableStructs[battlerAtk].isFirstTurn && predictedMove != MOVE_NONE
|
||||
&& !IsBattleMoveStatus(predictedMove) && !(GetBattlerMoveTargetType(battlerDef, predictedMove) & MOVE_TARGET_USER))
|
||||
ProtectChecks(battlerAtk, battlerDef, move, predictedMove, &score);
|
||||
break;
|
||||
case MOVE_KINGS_SHIELD:
|
||||
case PROTECT_KINGS_SHIELD:
|
||||
if (aiData->abilities[battlerAtk] == ABILITY_STANCE_CHANGE //Special logic for Aegislash
|
||||
&& gBattleMons[battlerAtk].species == SPECIES_AEGISLASH_BLADE
|
||||
&& !IsBattlerIncapacitated(battlerDef, aiData->abilities[battlerDef]))
|
||||
@ -4444,7 +4458,9 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
|
||||
{
|
||||
enum BattleMoveEffects predictedEffect = GetMoveEffect(predictedMove);
|
||||
if ((AI_IsFaster(battlerAtk, battlerDef, move))
|
||||
&& (predictedEffect == EFFECT_EXPLOSION || predictedEffect == EFFECT_PROTECT))
|
||||
&& (predictedEffect == EFFECT_EXPLOSION
|
||||
|| predictedEffect == EFFECT_MISTY_EXPLOSION
|
||||
|| predictedEffect == EFFECT_PROTECT))
|
||||
ADJUST_SCORE(GOOD_EFFECT);
|
||||
else if (predictedEffect == EFFECT_SEMI_INVULNERABLE && !(gStatuses3[battlerDef] & STATUS3_SEMI_INVULNERABLE))
|
||||
ADJUST_SCORE(GOOD_EFFECT);
|
||||
@ -5512,6 +5528,7 @@ static s32 AI_Risky(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
ADJUST_SCORE(STRONG_RISKY_EFFECT);
|
||||
break;
|
||||
case EFFECT_EXPLOSION:
|
||||
case EFFECT_MISTY_EXPLOSION:
|
||||
ADJUST_SCORE(STRONG_RISKY_EFFECT);
|
||||
break;
|
||||
|
||||
@ -5534,6 +5551,7 @@ static s32 AI_Risky(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
case EFFECT_FLATTER:
|
||||
case EFFECT_ATTRACT:
|
||||
case EFFECT_OHKO:
|
||||
case EFFECT_SHEER_COLD:
|
||||
ADJUST_SCORE(AVERAGE_RISKY_EFFECT);
|
||||
break;
|
||||
case EFFECT_HIT:
|
||||
@ -5610,7 +5628,7 @@ static s32 AI_PreferBatonPass(u32 battlerAtk, u32 battlerDef, u32 move, s32 scor
|
||||
ADJUST_SCORE(DECENT_EFFECT);
|
||||
break;
|
||||
case EFFECT_PROTECT:
|
||||
if (gLastMoves[battlerAtk] == MOVE_PROTECT || gLastMoves[battlerAtk] == MOVE_DETECT)
|
||||
if (GetProtectType(GetMoveProtectMethod(gLastMoves[battlerAtk])) == PROTECT_TYPE_SINGLE)
|
||||
ADJUST_SCORE(-2);
|
||||
else
|
||||
ADJUST_SCORE(DECENT_EFFECT);
|
||||
@ -5664,6 +5682,7 @@ static s32 AI_HPAware(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
switch (effect)
|
||||
{
|
||||
case EFFECT_EXPLOSION:
|
||||
case EFFECT_MISTY_EXPLOSION:
|
||||
case EFFECT_RESTORE_HP:
|
||||
case EFFECT_REST:
|
||||
case EFFECT_DESTINY_BOND:
|
||||
@ -5692,6 +5711,7 @@ static s32 AI_HPAware(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
switch (effect)
|
||||
{
|
||||
case EFFECT_EXPLOSION:
|
||||
case EFFECT_MISTY_EXPLOSION:
|
||||
case EFFECT_BIDE:
|
||||
case EFFECT_CONVERSION:
|
||||
case EFFECT_LIGHT_SCREEN:
|
||||
|
||||
@ -209,7 +209,7 @@ static bool32 ShouldSwitchIfHasBadOdds(u32 battler)
|
||||
// Check if mon has an "important" status move
|
||||
if (aiMoveEffect == EFFECT_REFLECT || aiMoveEffect == EFFECT_LIGHT_SCREEN
|
||||
|| aiMoveEffect == EFFECT_SPIKES || aiMoveEffect == EFFECT_TOXIC_SPIKES || aiMoveEffect == EFFECT_STEALTH_ROCK || aiMoveEffect == EFFECT_STICKY_WEB || aiMoveEffect == EFFECT_LEECH_SEED
|
||||
|| aiMoveEffect == EFFECT_EXPLOSION
|
||||
|| aiMoveEffect == EFFECT_EXPLOSION || aiMoveEffect == EFFECT_MISTY_EXPLOSION
|
||||
|| nonVolatileStatus == MOVE_EFFECT_SLEEP
|
||||
|| nonVolatileStatus == MOVE_EFFECT_TOXIC
|
||||
|| nonVolatileStatus == MOVE_EFFECT_PARALYSIS
|
||||
@ -2118,7 +2118,9 @@ static u32 GetBestMonIntegrated(struct Pokemon *party, int firstId, int lastId,
|
||||
}
|
||||
|
||||
// If a self destruction move doesn't OHKO, don't factor it into revenge killing
|
||||
if (GetMoveEffect(aiMove) == EFFECT_EXPLOSION && damageDealt < playerMonHP)
|
||||
enum BattleMoveEffects aiEffect = GetMoveEffect(aiMove);
|
||||
if ((aiEffect == EFFECT_EXPLOSION || aiEffect == EFFECT_MISTY_EXPLOSION)
|
||||
&& damageDealt < playerMonHP)
|
||||
continue;
|
||||
|
||||
// Check that mon isn't one shot and set best damage mon
|
||||
|
||||
@ -561,6 +561,7 @@ bool32 IsDamageMoveUnusable(u32 battlerAtk, u32 battlerDef, u32 move, u32 moveTy
|
||||
return TRUE;
|
||||
break;
|
||||
case EFFECT_EXPLOSION:
|
||||
case EFFECT_MISTY_EXPLOSION:
|
||||
case EFFECT_MIND_BLOWN:
|
||||
if (battlerDefAbility == ABILITY_DAMP || partnerDefAbility == ABILITY_DAMP)
|
||||
return TRUE;
|
||||
@ -1022,6 +1023,7 @@ static bool32 AI_IsMoveEffectInMinus(u32 battlerAtk, u32 battlerDef, u32 move, s
|
||||
case EFFECT_MAX_HP_50_RECOIL:
|
||||
case EFFECT_MIND_BLOWN:
|
||||
case EFFECT_EXPLOSION:
|
||||
case EFFECT_MISTY_EXPLOSION:
|
||||
case EFFECT_FINAL_GAMBIT:
|
||||
return TRUE;
|
||||
case EFFECT_RECOIL:
|
||||
@ -1777,7 +1779,7 @@ bool32 ShouldTryOHKO(u32 battlerAtk, u32 battlerDef, u32 atkAbility, u32 defAbil
|
||||
else // test the odds
|
||||
{
|
||||
u32 odds = accuracy + (gBattleMons[battlerAtk].level - gBattleMons[battlerDef].level);
|
||||
if (B_SHEER_COLD_ACC >= GEN_7 && move == MOVE_SHEER_COLD && !IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_ICE))
|
||||
if (B_SHEER_COLD_ACC >= GEN_7 && GetMoveEffect(move) == EFFECT_SHEER_COLD && !IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_ICE))
|
||||
odds -= 10;
|
||||
if (Random() % 100 + 1 < odds && gBattleMons[battlerAtk].level >= gBattleMons[battlerDef].level)
|
||||
return TRUE;
|
||||
@ -4478,7 +4480,7 @@ bool32 ShouldUseZMove(u32 battlerAtk, u32 battlerDef, u32 chosenMove)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void SetAIUsingGimmick(u32 battler, enum AIConsiderGimmick use)
|
||||
void SetAIUsingGimmick(u32 battler, enum AIConsiderGimmick use)
|
||||
{
|
||||
if (use == USE_GIMMICK)
|
||||
gAiBattleData->aiUsingGimmick |= (1<<battler);
|
||||
@ -4486,7 +4488,7 @@ void SetAIUsingGimmick(u32 battler, enum AIConsiderGimmick use)
|
||||
gAiBattleData->aiUsingGimmick &= ~(1<<battler);
|
||||
}
|
||||
|
||||
bool32 IsAIUsingGimmick(u32 battler)
|
||||
bool32 IsAIUsingGimmick(u32 battler)
|
||||
{
|
||||
return (gAiBattleData->aiUsingGimmick & (1<<battler)) != 0;
|
||||
}
|
||||
@ -4500,17 +4502,17 @@ enum AIConsiderGimmick ShouldTeraFromCalcs(u32 battler, u32 opposingBattler, str
|
||||
|
||||
void DecideTerastal(u32 battler)
|
||||
{
|
||||
if (gBattleStruct->gimmick.usableGimmick[battler] != GIMMICK_TERA)
|
||||
if (gBattleStruct->gimmick.usableGimmick[battler] != GIMMICK_TERA)
|
||||
return;
|
||||
|
||||
if (!(gAiThinkingStruct->aiFlags[battler] & AI_FLAG_SMART_TERA))
|
||||
|
||||
if (!(gAiThinkingStruct->aiFlags[battler] & AI_FLAG_SMART_TERA))
|
||||
return;
|
||||
|
||||
|
||||
// TODO: Currently only single battles are considered.
|
||||
if (IsDoubleBattle())
|
||||
return;
|
||||
|
||||
// TODO: A lot of these checks are most effective for an omnicient ai.
|
||||
if (IsDoubleBattle())
|
||||
return;
|
||||
|
||||
// TODO: A lot of these checks are most effective for an omnicient ai.
|
||||
// If we don't have enough information about the opponent's moves, consider simpler checks based on type effectivness.
|
||||
|
||||
u32 opposingBattler = GetOppositeBattler(battler);
|
||||
@ -4528,20 +4530,20 @@ void DecideTerastal(u32 battler)
|
||||
|
||||
uq4_12_t effectiveness;
|
||||
|
||||
for (int i = 0; i < MAX_MON_MOVES; i++)
|
||||
for (int i = 0; i < MAX_MON_MOVES; i++)
|
||||
{
|
||||
if (!IsMoveUnusable(i, aiMoves[i], gAiLogicData->moveLimitations[battler]) && !IsBattleMoveStatus(aiMoves[i]))
|
||||
if (!IsMoveUnusable(i, aiMoves[i], gAiLogicData->moveLimitations[battler]) && !IsBattleMoveStatus(aiMoves[i]))
|
||||
altCalcs.dealtWithoutTera[i] = AI_CalcDamage(aiMoves[i], battler, opposingBattler, &effectiveness, NO_GIMMICK, NO_GIMMICK, AI_GetWeather());
|
||||
else
|
||||
else
|
||||
altCalcs.dealtWithoutTera[i] = noDmg;
|
||||
|
||||
|
||||
if (!IsMoveUnusable(i, oppMoves[i], gAiLogicData->moveLimitations[opposingBattler]) && !IsBattleMoveStatus(oppMoves[i]))
|
||||
|
||||
if (!IsMoveUnusable(i, oppMoves[i], gAiLogicData->moveLimitations[opposingBattler]) && !IsBattleMoveStatus(oppMoves[i]))
|
||||
{
|
||||
altCalcs.takenWithTera[i] = AI_CalcDamage(oppMoves[i], opposingBattler, battler, &effectiveness, USE_GIMMICK, USE_GIMMICK, AI_GetWeather());
|
||||
effectivenessTakenWithTera[i] = effectiveness;
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
altCalcs.takenWithTera[i] = noDmg;
|
||||
effectivenessTakenWithTera[i] = Q_4_12(0.0);
|
||||
@ -4552,19 +4554,19 @@ void DecideTerastal(u32 battler)
|
||||
enum AIConsiderGimmick res = ShouldTeraFromCalcs(battler, opposingBattler, &altCalcs);
|
||||
|
||||
|
||||
if (res == USE_GIMMICK)
|
||||
if (res == USE_GIMMICK)
|
||||
{
|
||||
// Damage calcs for damage received assumed we wouldn't tera. Adjust that so that further AI decisions are more accurate.
|
||||
for (int i = 0; i < MAX_MON_MOVES; i++)
|
||||
for (int i = 0; i < MAX_MON_MOVES; i++)
|
||||
{
|
||||
gAiLogicData->simulatedDmg[opposingBattler][battler][i] = altCalcs.takenWithTera[i];
|
||||
gAiLogicData->effectiveness[opposingBattler][battler][i] = effectivenessTakenWithTera[i];
|
||||
}
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
// Damage calcs for damage dealt assumed we would tera. Adjust that so that further AI decisions are more accurate.
|
||||
for (int i = 0; i < MAX_MON_MOVES; i++)
|
||||
// Damage calcs for damage dealt assumed we would tera. Adjust that so that further AI decisions are more accurate.
|
||||
for (int i = 0; i < MAX_MON_MOVES; i++)
|
||||
gAiLogicData->simulatedDmg[battler][opposingBattler][i] = altCalcs.dealtWithoutTera[i];
|
||||
}
|
||||
|
||||
@ -4582,13 +4584,13 @@ enum AIConsiderGimmick ShouldTeraFromCalcs(u32 battler, u32 opposingBattler, str
|
||||
struct Pokemon* party = GetBattlerParty(battler);
|
||||
|
||||
// Check how many pokemon we have that could tera
|
||||
int numPossibleTera = 0;
|
||||
for (int i = 0; i < PARTY_SIZE; i++)
|
||||
int numPossibleTera = 0;
|
||||
for (int i = 0; i < PARTY_SIZE; i++)
|
||||
{
|
||||
if (GetMonData(&party[i], MON_DATA_HP) != 0
|
||||
&& GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG) != SPECIES_NONE
|
||||
&& GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG) != SPECIES_EGG
|
||||
&& GetMonData(&party[i], MON_DATA_TERA_TYPE) > 0)
|
||||
&& GetMonData(&party[i], MON_DATA_TERA_TYPE) > 0)
|
||||
numPossibleTera++;
|
||||
}
|
||||
|
||||
@ -4602,44 +4604,44 @@ enum AIConsiderGimmick ShouldTeraFromCalcs(u32 battler, u32 opposingBattler, str
|
||||
bool32 hasKoWithout = FALSE;
|
||||
u16 killingMove = MOVE_NONE;
|
||||
|
||||
for (int i = 0; i < MAX_MON_MOVES; i++)
|
||||
for (int i = 0; i < MAX_MON_MOVES; i++)
|
||||
{
|
||||
if (dealtWithTera[i].median >= oppHp)
|
||||
if (dealtWithTera[i].median >= oppHp)
|
||||
{
|
||||
u16 move = aiMoves[i];
|
||||
if (killingMove == MOVE_NONE || GetBattleMovePriority(battler, gAiLogicData->abilities[battler], move) > GetBattleMovePriority(battler, gAiLogicData->abilities[battler], killingMove))
|
||||
killingMove = move;
|
||||
}
|
||||
if (dealtWithoutTera[i].median >= oppHp)
|
||||
}
|
||||
if (dealtWithoutTera[i].median >= oppHp)
|
||||
hasKoWithout = TRUE;
|
||||
}
|
||||
|
||||
bool32 enablesKo = (killingMove != MOVE_NONE) && !hasKoWithout;
|
||||
|
||||
// Check whether tera saves us from a KO
|
||||
bool32 savedFromKo = FALSE;
|
||||
bool32 savedFromKo = FALSE;
|
||||
bool32 getsKodRegardlessBySingleMove = FALSE;
|
||||
|
||||
for (int i = 0; i < MAX_MON_MOVES; i++)
|
||||
for (int i = 0; i < MAX_MON_MOVES; i++)
|
||||
{
|
||||
if (takenWithoutTera[i].maximum >= aiHp && takenWithTera[i].maximum >= aiHp)
|
||||
if (takenWithoutTera[i].maximum >= aiHp && takenWithTera[i].maximum >= aiHp)
|
||||
getsKodRegardlessBySingleMove = TRUE;
|
||||
|
||||
if (takenWithoutTera[i].maximum >= aiHp && takenWithTera[i].maximum < aiHp)
|
||||
if (takenWithoutTera[i].maximum >= aiHp && takenWithTera[i].maximum < aiHp)
|
||||
savedFromKo = TRUE;
|
||||
}
|
||||
|
||||
if (getsKodRegardlessBySingleMove)
|
||||
savedFromKo = FALSE;
|
||||
|
||||
// Check whether opponent can punish tera by ko'ing
|
||||
// Check whether opponent can punish tera by ko'ing
|
||||
u16 hardPunishingMove = MOVE_NONE;
|
||||
for (int i = 0; i < MAX_MON_MOVES; i++)
|
||||
for (int i = 0; i < MAX_MON_MOVES; i++)
|
||||
{
|
||||
if (takenWithTera[i].maximum >= aiHp)
|
||||
if (takenWithTera[i].maximum >= aiHp)
|
||||
{
|
||||
u16 move = oppMoves[i];
|
||||
if (hardPunishingMove == MOVE_NONE || GetBattleMovePriority(opposingBattler, gAiLogicData->abilities[opposingBattler], move) > GetBattleMovePriority(opposingBattler, gAiLogicData->abilities[opposingBattler], hardPunishingMove))
|
||||
if (hardPunishingMove == MOVE_NONE || GetBattleMovePriority(opposingBattler, gAiLogicData->abilities[opposingBattler], move) > GetBattleMovePriority(opposingBattler, gAiLogicData->abilities[opposingBattler], hardPunishingMove))
|
||||
hardPunishingMove = move;
|
||||
}
|
||||
}
|
||||
@ -4648,32 +4650,32 @@ enum AIConsiderGimmick ShouldTeraFromCalcs(u32 battler, u32 opposingBattler, str
|
||||
// (e.g. a weakness becomes a resistance, a 4x weakness becomes neutral, etc)
|
||||
bool32 takesBigHit = FALSE;
|
||||
bool32 savedFromAllBigHits = TRUE;
|
||||
for (int i = 0; i < MAX_MON_MOVES; i++)
|
||||
for (int i = 0; i < MAX_MON_MOVES; i++)
|
||||
{
|
||||
if (takenWithoutTera[i].median > aiHp/2)
|
||||
if (takenWithoutTera[i].median > aiHp/2)
|
||||
{
|
||||
takesBigHit = TRUE;
|
||||
if (takenWithTera[i].median > aiHp/4)
|
||||
takesBigHit = TRUE;
|
||||
if (takenWithTera[i].median > aiHp/4)
|
||||
savedFromAllBigHits = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for any benefit whatsoever. Only used for the last possible mon that could tera.
|
||||
bool32 anyOffensiveBenefit = FALSE;
|
||||
for (int i = 0; i < MAX_MON_MOVES; i++)
|
||||
for (int i = 0; i < MAX_MON_MOVES; i++)
|
||||
{
|
||||
if (dealtWithTera[i].median > dealtWithoutTera[i].median)
|
||||
if (dealtWithTera[i].median > dealtWithoutTera[i].median)
|
||||
anyOffensiveBenefit = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
bool32 anyDefensiveBenefit = FALSE;
|
||||
bool32 anyDefensiveDrawback = FALSE;
|
||||
for (int i = 0; i < MAX_MON_MOVES; i++)
|
||||
bool32 anyDefensiveDrawback = FALSE;
|
||||
for (int i = 0; i < MAX_MON_MOVES; i++)
|
||||
{
|
||||
if (takenWithTera[i].median < takenWithoutTera[i].median)
|
||||
if (takenWithTera[i].median < takenWithoutTera[i].median)
|
||||
anyDefensiveBenefit = TRUE;
|
||||
|
||||
if (takenWithTera[i].median > takenWithoutTera[i].median)
|
||||
if (takenWithTera[i].median > takenWithoutTera[i].median)
|
||||
anyDefensiveDrawback = TRUE;
|
||||
}
|
||||
|
||||
@ -4681,55 +4683,55 @@ enum AIConsiderGimmick ShouldTeraFromCalcs(u32 battler, u32 opposingBattler, str
|
||||
// This is done after all loops to minimize the possibility of a timing attack in which the player could
|
||||
// determine whether the AI will tera based on the time taken to select a move.
|
||||
|
||||
if (enablesKo)
|
||||
if (enablesKo)
|
||||
{
|
||||
if (hardPunishingMove == MOVE_NONE)
|
||||
if (hardPunishingMove == MOVE_NONE)
|
||||
{
|
||||
return USE_GIMMICK;
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
// will we go first?
|
||||
if (AI_WhoStrikesFirst(battler, opposingBattler, killingMove) == AI_IS_FASTER && GetBattleMovePriority(battler, gAiLogicData->abilities[battler], killingMove) >= GetBattleMovePriority(opposingBattler, gAiLogicData->abilities[opposingBattler], hardPunishingMove))
|
||||
if (AI_WhoStrikesFirst(battler, opposingBattler, killingMove) == AI_IS_FASTER && GetBattleMovePriority(battler, gAiLogicData->abilities[battler], killingMove) >= GetBattleMovePriority(opposingBattler, gAiLogicData->abilities[opposingBattler], hardPunishingMove))
|
||||
return USE_GIMMICK;
|
||||
}
|
||||
}
|
||||
|
||||
// Decide to conserve tera based on number of possible later oppotunities
|
||||
u16 conserveTeraChance = AI_CONSERVE_TERA_CHANCE_PER_MON * (numPossibleTera-1);
|
||||
if (RandomPercentage(RNG_AI_CONSERVE_TERA, conserveTeraChance))
|
||||
if (RandomPercentage(RNG_AI_CONSERVE_TERA, conserveTeraChance))
|
||||
return NO_GIMMICK;
|
||||
|
||||
if (savedFromKo)
|
||||
if (savedFromKo)
|
||||
{
|
||||
if (hardPunishingMove == MOVE_NONE)
|
||||
if (hardPunishingMove == MOVE_NONE)
|
||||
{
|
||||
return USE_GIMMICK;
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
// If tera saves us from a ko from one move, but enables a ko otherwise, randomly predict
|
||||
// savesFromKo being true ensures opponent doesn't have a ko if we don't tera
|
||||
if (Random() % 100 < AI_TERA_PREDICT_CHANCE)
|
||||
if (Random() % 100 < AI_TERA_PREDICT_CHANCE)
|
||||
return USE_GIMMICK;
|
||||
}
|
||||
}
|
||||
|
||||
if (hardPunishingMove != MOVE_NONE)
|
||||
if (hardPunishingMove != MOVE_NONE)
|
||||
return NO_GIMMICK;
|
||||
|
||||
if (takesBigHit && savedFromAllBigHits)
|
||||
if (takesBigHit && savedFromAllBigHits)
|
||||
return USE_GIMMICK;
|
||||
|
||||
// No strongly compelling reason to tera. Conserve it if possible.
|
||||
if (numPossibleTera > 1)
|
||||
// No strongly compelling reason to tera. Conserve it if possible.
|
||||
if (numPossibleTera > 1)
|
||||
return NO_GIMMICK;
|
||||
|
||||
if (anyOffensiveBenefit || (anyDefensiveBenefit && !anyDefensiveDrawback))
|
||||
if (anyOffensiveBenefit || (anyDefensiveBenefit && !anyDefensiveDrawback))
|
||||
return USE_GIMMICK;
|
||||
|
||||
// TODO: Effects other than direct damage are not yet considered. For example, may want to tera poison to avoid a Toxic.
|
||||
|
||||
|
||||
|
||||
return NO_GIMMICK;
|
||||
}
|
||||
|
||||
@ -3938,6 +3938,7 @@ static bool32 IsDomeRiskyMoveEffect(enum BattleMoveEffects effect)
|
||||
switch(effect)
|
||||
{
|
||||
case EFFECT_EXPLOSION:
|
||||
case EFFECT_MISTY_EXPLOSION:
|
||||
case EFFECT_SPITE:
|
||||
case EFFECT_DESTINY_BOND:
|
||||
case EFFECT_PERISH_SONG:
|
||||
@ -3954,7 +3955,8 @@ static bool32 IsDomeLuckyMove(u32 move)
|
||||
switch(GetMoveEffect(move))
|
||||
{
|
||||
case EFFECT_COUNTER:
|
||||
case EFFECT_OHKO: // Technically redundant because of the above accuracy check
|
||||
case EFFECT_OHKO:
|
||||
case EFFECT_SHEER_COLD:
|
||||
case EFFECT_METRONOME:
|
||||
case EFFECT_MIRROR_MOVE:
|
||||
case EFFECT_SKETCH:
|
||||
@ -5125,11 +5127,13 @@ static u16 GetWinningMove(int winnerTournamentId, int loserTournamentId, u8 roun
|
||||
moves[i * MAX_MON_MOVES + j] = gFacilityTrainerMons[DOME_MONS[winnerTournamentId][i]].moves[j];
|
||||
|
||||
movePower = GetMovePower(moves[i * MAX_MON_MOVES + j]);
|
||||
enum BattleMoveEffects effect = GetMoveEffect(moves[i * MAX_MON_MOVES + j]);
|
||||
if (IsBattleMoveStatus(moves[i * MAX_MON_MOVES + j]))
|
||||
movePower = 40;
|
||||
else if (movePower == 1)
|
||||
movePower = 60;
|
||||
else if (GetMoveEffect(moves[i * MAX_MON_MOVES + j]) == EFFECT_EXPLOSION)
|
||||
else if (B_EXPLOSION_DEFENSE < GEN_5
|
||||
&& (effect == EFFECT_EXPLOSION || EFFECT_MISTY_EXPLOSION))
|
||||
movePower /= 2;
|
||||
|
||||
for (k = 0; k < FRONTIER_PARTY_SIZE; k++)
|
||||
|
||||
@ -407,6 +407,7 @@ static u32 GetMaxPowerTier(u32 move)
|
||||
case EFFECT_FINAL_GAMBIT:
|
||||
return MAX_POWER_TIER_2;
|
||||
case EFFECT_OHKO:
|
||||
case EFFECT_SHEER_COLD:
|
||||
case EFFECT_RETURN:
|
||||
case EFFECT_FRUSTRATION:
|
||||
case EFFECT_HEAT_CRASH:
|
||||
|
||||
@ -6058,6 +6058,7 @@ void SetTypeBeforeUsingMove(u32 move, u32 battler)
|
||||
&& GetBattleMoveType(move) == GetItemSecondaryId(heldItem)
|
||||
&& effect != EFFECT_PLEDGE
|
||||
&& effect != EFFECT_OHKO
|
||||
&& effect != EFFECT_SHEER_COLD
|
||||
&& effect != EFFECT_STRUGGLE)
|
||||
{
|
||||
gSpecialStatuses[battler].gemParam = GetBattlerHoldEffectParam(battler);
|
||||
|
||||
@ -1248,7 +1248,10 @@ static void Cmd_attackcanceler(void)
|
||||
|
||||
enum BattleMoveEffects effect = GetMoveEffect(gCurrentMove);
|
||||
|
||||
if (!IsBattlerAlive(gBattlerAttacker) && effect != EFFECT_EXPLOSION && !(gHitMarker & HITMARKER_NO_ATTACKSTRING))
|
||||
if (!IsBattlerAlive(gBattlerAttacker)
|
||||
&& effect != EFFECT_EXPLOSION
|
||||
&& effect != EFFECT_MISTY_EXPLOSION
|
||||
&& !(gHitMarker & HITMARKER_NO_ATTACKSTRING))
|
||||
{
|
||||
gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE;
|
||||
gBattlescriptCurrInstr = BattleScript_MoveEnd;
|
||||
@ -1515,7 +1518,8 @@ static bool32 AccuracyCalcHelper(u32 move, u32 battler)
|
||||
// If the target is under the effects of Telekinesis, and the move isn't a OH-KO move, move hits.
|
||||
else if (gStatuses3[battler] & STATUS3_TELEKINESIS
|
||||
&& !(gStatuses3[battler] & STATUS3_SEMI_INVULNERABLE)
|
||||
&& moveEffect != EFFECT_OHKO)
|
||||
&& moveEffect != EFFECT_OHKO
|
||||
&& moveEffect != EFFECT_SHEER_COLD)
|
||||
{
|
||||
effect = TRUE;
|
||||
}
|
||||
@ -2459,14 +2463,15 @@ static void Cmd_attackanimation(void)
|
||||
|
||||
u32 moveTarget = GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove);
|
||||
u32 moveResultFlags = gBattleStruct->moveResultFlags[gBattlerTarget];
|
||||
enum BattleMoveEffects effect = GetMoveEffect(gCurrentMove);
|
||||
|
||||
if (IsDoubleSpreadMove())
|
||||
moveResultFlags = UpdateEffectivenessResultFlagsForDoubleSpreadMoves(gBattleStruct->moveResultFlags[gBattlerTarget]);
|
||||
|
||||
if ((gHitMarker & (HITMARKER_NO_ANIMATIONS | HITMARKER_DISABLE_ANIMATION))
|
||||
&& gCurrentMove != MOVE_TRANSFORM
|
||||
&& gCurrentMove != MOVE_SUBSTITUTE
|
||||
&& gCurrentMove != MOVE_ALLY_SWITCH
|
||||
&& effect != EFFECT_TRANSFORM
|
||||
&& effect != EFFECT_SUBSTITUTE
|
||||
&& effect != EFFECT_ALLY_SWITCH
|
||||
// In a wild double battle gotta use the teleport animation if two wild pokemon are alive.
|
||||
&& !(GetMoveEffect(gCurrentMove) == EFFECT_TELEPORT && WILD_DOUBLE_BATTLE && !IsOnPlayerSide(gBattlerAttacker) && IsBattlerAlive(BATTLE_PARTNER(gBattlerAttacker))))
|
||||
{
|
||||
@ -6820,7 +6825,7 @@ static void Cmd_moveend(void)
|
||||
MoveValuesCleanUp();
|
||||
gBattleScripting.moveEffect = gBattleScripting.savedMoveEffect;
|
||||
|
||||
if (moveEffect == EFFECT_EXPLOSION)
|
||||
if (moveEffect == EFFECT_EXPLOSION || moveEffect == EFFECT_MISTY_EXPLOSION)
|
||||
BattleScriptPush(gBattleMoveEffects[EFFECT_HIT].battleScript); // Edge case for Explosion not changing targets
|
||||
else
|
||||
BattleScriptPush(GetMoveBattleScript(gCurrentMove));
|
||||
@ -6984,6 +6989,7 @@ static void Cmd_moveend(void)
|
||||
}
|
||||
break;
|
||||
case EFFECT_EXPLOSION:
|
||||
case EFFECT_MISTY_EXPLOSION:
|
||||
if (!IsAbilityOnField(ABILITY_DAMP))
|
||||
{
|
||||
gBattleStruct->moveDamage[gBattlerAttacker] = 0;
|
||||
@ -9111,7 +9117,7 @@ static bool32 TrySymbiosis(u32 battler, u32 itemId)
|
||||
&& GetBattlerHoldEffect(battler, TRUE) != HOLD_EFFECT_EJECT_BUTTON
|
||||
&& GetBattlerHoldEffect(battler, TRUE) != HOLD_EFFECT_EJECT_PACK
|
||||
&& (B_SYMBIOSIS_GEMS < GEN_7 || !(gSpecialStatuses[battler].gemBoost))
|
||||
&& gCurrentMove != MOVE_FLING //Fling and damage-reducing berries are handled separately.
|
||||
&& GetMoveEffect(gCurrentMove) != EFFECT_FLING //Fling and damage-reducing berries are handled separately.
|
||||
&& !gSpecialStatuses[battler].berryReduced
|
||||
&& TryTriggerSymbiosis(battler, BATTLE_PARTNER(battler)))
|
||||
{
|
||||
@ -12860,6 +12866,7 @@ static void Cmd_tryKO(void)
|
||||
CMD_ARGS(const u8 *failInstr);
|
||||
|
||||
bool32 lands = FALSE;
|
||||
enum BattleMoveEffects effect = GetMoveEffect(gCurrentMove);
|
||||
enum ItemHoldEffect holdEffect = GetBattlerHoldEffect(gBattlerTarget, TRUE);
|
||||
u16 targetAbility = GetBattlerAbility(gBattlerTarget);
|
||||
u32 rand = Random() % 100;
|
||||
@ -12915,7 +12922,7 @@ static void Cmd_tryKO(void)
|
||||
else
|
||||
{
|
||||
u16 odds = GetMoveAccuracy(gCurrentMove) + (gBattleMons[gBattlerAttacker].level - gBattleMons[gBattlerTarget].level);
|
||||
if (B_SHEER_COLD_ACC >= GEN_7 && gCurrentMove == MOVE_SHEER_COLD && !IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_ICE))
|
||||
if (B_SHEER_COLD_ACC >= GEN_7 && effect == EFFECT_SHEER_COLD && !IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_ICE))
|
||||
odds -= 10;
|
||||
if (RandomPercentage(RNG_ACCURACY, odds) && gBattleMons[gBattlerAttacker].level >= gBattleMons[gBattlerTarget].level)
|
||||
lands = TRUE;
|
||||
|
||||
@ -599,7 +599,8 @@ void BattleTv_SetDataBasedOnMove(u16 move, u16 weatherFlags, struct DisableStruc
|
||||
tvPtr->side[atkSide].wishMonId = gBattlerPartyIndexes[gBattlerAttacker] + 1;
|
||||
tvPtr->side[atkSide].wishMoveSlot = moveSlot;
|
||||
}
|
||||
if (GetMoveEffect(move) == EFFECT_EXPLOSION)
|
||||
enum BattleMoveEffects effect = GetMoveEffect(move);
|
||||
if (effect == EFFECT_EXPLOSION || effect == EFFECT_MISTY_EXPLOSION)
|
||||
{
|
||||
tvPtr->side[atkSide ^ BIT_SIDE].explosionMonId = gBattlerPartyIndexes[gBattlerAttacker] + 1;
|
||||
tvPtr->side[atkSide ^ BIT_SIDE].explosionMoveSlot = moveSlot;
|
||||
|
||||
@ -2333,9 +2333,11 @@ static void CancellerPsychicTerrain(u32 *effect)
|
||||
|
||||
static void CancellerExplodingDamp(u32 *effect)
|
||||
{
|
||||
enum BattleMoveEffects moveEffect = GetMoveEffect(gCurrentMove);
|
||||
u32 dampBattler = IsAbilityOnField(ABILITY_DAMP);
|
||||
if (dampBattler && (GetMoveEffect(gCurrentMove) == EFFECT_EXPLOSION
|
||||
|| GetMoveEffect(gCurrentMove) == EFFECT_MIND_BLOWN))
|
||||
if (dampBattler && (moveEffect == EFFECT_EXPLOSION
|
||||
|| moveEffect == EFFECT_MISTY_EXPLOSION
|
||||
|| moveEffect == EFFECT_MIND_BLOWN))
|
||||
{
|
||||
gBattleScripting.battler = dampBattler - 1;
|
||||
gBattlescriptCurrInstr = BattleScript_DampStopsExplosion;
|
||||
@ -2776,6 +2778,7 @@ static void ForewarnChooseMove(u32 battler)
|
||||
switch (GetMoveEffect(data[count].moveId))
|
||||
{
|
||||
case EFFECT_OHKO:
|
||||
case EFFECT_SHEER_COLD:
|
||||
data[count].power = 150;
|
||||
break;
|
||||
case EFFECT_COUNTER:
|
||||
@ -3594,7 +3597,9 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
|
||||
{
|
||||
move = gBattleMons[i].moves[j];
|
||||
moveType = GetBattleMoveType(move);
|
||||
if (CalcTypeEffectivenessMultiplier(move, moveType, i, battler, ABILITY_ANTICIPATION, FALSE) >= UQ_4_12(2.0) || GetMoveEffect(move) == EFFECT_OHKO)
|
||||
enum BattleMoveEffects moveEffect = GetMoveEffect(move);
|
||||
if (CalcTypeEffectivenessMultiplier(move, moveType, i, battler, ABILITY_ANTICIPATION, FALSE) >= UQ_4_12(2.0)
|
||||
|| moveEffect == EFFECT_OHKO || moveEffect == EFFECT_SHEER_COLD)
|
||||
{
|
||||
effect++;
|
||||
break;
|
||||
@ -8087,8 +8092,8 @@ static inline u32 CalcMoveBasePower(struct DamageCalculationData *damageCalcData
|
||||
if (gProtectStructs[battlerAtk].lashOutAffected)
|
||||
basePower *= 2;
|
||||
break;
|
||||
case EFFECT_EXPLOSION:
|
||||
if (move == MOVE_MISTY_EXPLOSION && IsBattlerTerrainAffected(battlerAtk, STATUS_FIELD_MISTY_TERRAIN))
|
||||
case EFFECT_MISTY_EXPLOSION:
|
||||
if (IsBattlerTerrainAffected(battlerAtk, STATUS_FIELD_MISTY_TERRAIN))
|
||||
basePower = uq4_12_multiply(basePower, UQ_4_12(1.5));
|
||||
break;
|
||||
case EFFECT_DYNAMAX_DOUBLE_DMG:
|
||||
@ -8800,7 +8805,8 @@ static inline u32 CalcDefenseStat(struct DamageCalculationData *damageCalcData,
|
||||
}
|
||||
|
||||
// Self-destruct / Explosion cut defense in half
|
||||
if (B_EXPLOSION_DEFENSE < GEN_5 && moveEffect == EFFECT_EXPLOSION)
|
||||
if (B_EXPLOSION_DEFENSE < GEN_5 && (moveEffect == EFFECT_EXPLOSION
|
||||
|| moveEffect == EFFECT_MISTY_EXPLOSION))
|
||||
defStat /= 2;
|
||||
|
||||
// critical hits ignore positive stat changes
|
||||
@ -9595,7 +9601,7 @@ static inline uq4_12_t CalcTypeEffectivenessMultiplierInternal(u32 move, u32 mov
|
||||
RecordAbilityBattle(battlerDef, ABILITY_LEVITATE);
|
||||
}
|
||||
}
|
||||
else if (B_SHEER_COLD_IMMUNITY >= GEN_7 && move == MOVE_SHEER_COLD && IS_BATTLER_OF_TYPE(battlerDef, TYPE_ICE))
|
||||
else if (B_SHEER_COLD_IMMUNITY >= GEN_7 && GetMoveEffect(move) == EFFECT_SHEER_COLD && IS_BATTLER_OF_TYPE(battlerDef, TYPE_ICE))
|
||||
{
|
||||
modifier = UQ_4_12(0.0);
|
||||
}
|
||||
|
||||
@ -549,7 +549,8 @@ u32 GetZMovePower(u32 move)
|
||||
{
|
||||
if (GetMoveCategory(move) == DAMAGE_CATEGORY_STATUS)
|
||||
return 0;
|
||||
if (GetMoveEffect(move) == EFFECT_OHKO)
|
||||
enum BattleMoveEffects moveEffect = GetMoveEffect(move);
|
||||
if (moveEffect == EFFECT_OHKO || moveEffect == EFFECT_SHEER_COLD)
|
||||
return 180;
|
||||
|
||||
u32 power = GetMoveZPowerOverride(move);
|
||||
@ -568,4 +569,3 @@ u32 GetZMovePower(u32 move)
|
||||
else if (power >= 60) return 120;
|
||||
else return 100;
|
||||
}
|
||||
|
||||
|
||||
@ -34,6 +34,12 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] =
|
||||
.battleTvScore = 0, // TODO: Assign points
|
||||
},
|
||||
|
||||
[EFFECT_MISTY_EXPLOSION] =
|
||||
{
|
||||
.battleScript = BattleScript_EffectExplosion,
|
||||
.battleTvScore = 0, // TODO: Assign points
|
||||
},
|
||||
|
||||
[EFFECT_DREAM_EATER] =
|
||||
{
|
||||
.battleScript = BattleScript_EffectDreamEater,
|
||||
@ -203,6 +209,12 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] =
|
||||
.battleTvScore = 7,
|
||||
},
|
||||
|
||||
[EFFECT_SHEER_COLD] =
|
||||
{
|
||||
.battleScript = BattleScript_EffectOHKO,
|
||||
.battleTvScore = 7,
|
||||
},
|
||||
|
||||
[EFFECT_FUSION_COMBO] =
|
||||
{
|
||||
.battleScript = BattleScript_EffectHit,
|
||||
|
||||
@ -8686,7 +8686,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] =
|
||||
.description = COMPOUND_STRING(
|
||||
"A chilling attack that\n"
|
||||
"causes fainting if it hits."),
|
||||
.effect = EFFECT_OHKO,
|
||||
.effect = EFFECT_SHEER_COLD,
|
||||
.power = 1,
|
||||
.type = TYPE_ICE,
|
||||
.accuracy = 30,
|
||||
@ -18544,7 +18544,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] =
|
||||
.description = COMPOUND_STRING(
|
||||
"Hit everything and faint.\n"
|
||||
"Powers up on Misty Terrain."),
|
||||
.effect = EFFECT_EXPLOSION,
|
||||
.effect = EFFECT_MISTY_EXPLOSION,
|
||||
.power = 100,
|
||||
.type = TYPE_FAIRY,
|
||||
.accuracy = 100,
|
||||
|
||||
@ -76,6 +76,7 @@ static bool32 AttackerHasToSwitch(u32 move) // User needs to send out a differen
|
||||
{
|
||||
if (gMovesInfo[move].effect == EFFECT_TELEPORT
|
||||
|| gMovesInfo[move].effect == EFFECT_EXPLOSION
|
||||
|| gMovesInfo[move].effect == EFFECT_MISTY_EXPLOSION
|
||||
|| gMovesInfo[move].effect == EFFECT_BATON_PASS
|
||||
|| gMovesInfo[move].effect == EFFECT_MEMENTO
|
||||
|| gMovesInfo[move].effect == EFFECT_HEALING_WISH
|
||||
|
||||
@ -3,22 +3,7 @@
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
||||
ASSUME(GetMoveEffect(MOVE_SHEER_COLD) == EFFECT_OHKO);
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Sheer Cold doesn't affect Ice-type Pokémon")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(B_SHEER_COLD_IMMUNITY >= GEN_7);
|
||||
ASSUME(GetSpeciesType(SPECIES_GLALIE, 0) == TYPE_ICE);
|
||||
PLAYER(SPECIES_WYNAUT);
|
||||
OPPONENT(SPECIES_GLALIE);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_SHEER_COLD); }
|
||||
} SCENE {
|
||||
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_SHEER_COLD, player);
|
||||
MESSAGE("It doesn't affect the opposing Glalie…");
|
||||
}
|
||||
ASSUME(GetMoveEffect(MOVE_FISSURE) == EFFECT_OHKO);
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("OHKO moves can hit semi-invulnerable mons when the user has No-Guard")
|
||||
@ -29,9 +14,9 @@ SINGLE_BATTLE_TEST("OHKO moves can hit semi-invulnerable mons when the user has
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_FLY); }
|
||||
TURN { MOVE(player, MOVE_SHEER_COLD); }
|
||||
TURN { MOVE(player, MOVE_FISSURE); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SHEER_COLD, player);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_FISSURE, player);
|
||||
HP_BAR(opponent, hp: 0);
|
||||
}
|
||||
}
|
||||
@ -42,9 +27,9 @@ SINGLE_BATTLE_TEST("OHKO moves can can be endured by Focus Sash")
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_FOCUS_SASH); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_SHEER_COLD); }
|
||||
TURN { MOVE(player, MOVE_FISSURE); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SHEER_COLD, player);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_FISSURE, player);
|
||||
HP_BAR(opponent, hp: 1);
|
||||
MESSAGE("The opposing Wobbuffet hung on using its Focus Sash!");
|
||||
}
|
||||
@ -56,15 +41,14 @@ SINGLE_BATTLE_TEST("OHKO moves can can be endured by Sturdy")
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_GEODUDE) { Ability(ABILITY_STURDY); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_SHEER_COLD); }
|
||||
TURN { MOVE(player, MOVE_FISSURE); }
|
||||
} SCENE {
|
||||
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_SHEER_COLD, player);
|
||||
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_FISSURE, player);
|
||||
ABILITY_POPUP(opponent, ABILITY_STURDY);
|
||||
MESSAGE("The opposing Geodude was protected by Sturdy!");
|
||||
}
|
||||
}
|
||||
|
||||
TO_DO_BATTLE_TEST("Fissure faints the target, skipping regular damage calculations")
|
||||
TO_DO_BATTLE_TEST("Fissure always fails if the target has a higher level than the user")
|
||||
TO_DO_BATTLE_TEST("Fissure's accuracy increases by 1% for every level the user has over the target")
|
||||
TO_DO_BATTLE_TEST("Fissure's ignores non-stage accuracy modifiers") // Gravity, Wide Lens, Compound Eyes
|
||||
TO_DO_BATTLE_TEST("OHKO moves faints the target, skipping regular damage calculations")
|
||||
TO_DO_BATTLE_TEST("OHKO moves always fails if the target has a higher level than the user")
|
||||
TO_DO_BATTLE_TEST("OHKO moves's accuracy increases by 1% for every level the user has over the target")
|
||||
TO_DO_BATTLE_TEST("OHKO moves's ignores non-stage accuracy modifiers") // Gravity, Wide Lens, Compound Eyes
|
||||
|
||||
70
test/battle/move_effect/sheer_cold.c
Normal file
70
test/battle/move_effect/sheer_cold.c
Normal file
@ -0,0 +1,70 @@
|
||||
#include "global.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
||||
ASSUME(GetMoveEffect(MOVE_SHEER_COLD) == EFFECT_SHEER_COLD);
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Sheer Cold doesn't affect Ice-type Pokémon")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(B_SHEER_COLD_IMMUNITY >= GEN_7);
|
||||
ASSUME(GetSpeciesType(SPECIES_GLALIE, 0) == TYPE_ICE);
|
||||
PLAYER(SPECIES_WYNAUT);
|
||||
OPPONENT(SPECIES_GLALIE);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_SHEER_COLD); }
|
||||
} SCENE {
|
||||
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_SHEER_COLD, player);
|
||||
MESSAGE("It doesn't affect the opposing Glalie…");
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Sheer Cold can hit semi-invulnerable mons when the user has No-Guard")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(GetItemHoldEffect(ITEM_FOCUS_SASH) == HOLD_EFFECT_FOCUS_SASH);
|
||||
PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_NO_GUARD); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_FLY); }
|
||||
TURN { MOVE(player, MOVE_SHEER_COLD); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SHEER_COLD, player);
|
||||
HP_BAR(opponent, hp: 0);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Sheer Cold can be endured by Focus Sash")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_FOCUS_SASH); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_SHEER_COLD); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SHEER_COLD, player);
|
||||
HP_BAR(opponent, hp: 1);
|
||||
MESSAGE("The opposing Wobbuffet hung on using its Focus Sash!");
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Sheer Cold can be endured by Sturdy")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_GEODUDE) { Ability(ABILITY_STURDY); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_SHEER_COLD); }
|
||||
} SCENE {
|
||||
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_SHEER_COLD, player);
|
||||
ABILITY_POPUP(opponent, ABILITY_STURDY);
|
||||
}
|
||||
}
|
||||
|
||||
TO_DO_BATTLE_TEST("Sheer Cold faints the target, skipping regular damage calculations")
|
||||
TO_DO_BATTLE_TEST("Sheer Cold always fails if the target has a higher level than the user")
|
||||
TO_DO_BATTLE_TEST("Sheer Cold's accuracy increases by 1% for every level the user has over the target")
|
||||
TO_DO_BATTLE_TEST("Sheer Cold's accuracy decreasaes by 10% if the user is not Ice type")
|
||||
TO_DO_BATTLE_TEST("Sheer Cold's ignores non-stage accuracy modifiers") // Gravity, Wide Lens, Compound Eyes
|
||||
Loading…
x
Reference in New Issue
Block a user