Remove more instances of hard-coded Move IDs (#7056)

This commit is contained in:
Eduardo Quezada 2025-06-03 17:26:48 -04:00 committed by GitHub
parent 21f0eb1583
commit d795de9e9d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 251 additions and 138 deletions

View File

@ -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,

View File

@ -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;
}

View File

@ -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:

View File

@ -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

View File

@ -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;
}

View File

@ -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++)

View File

@ -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:

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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,

View File

@ -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,

View File

@ -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

View File

@ -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

View 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