Add new move flags and update all flags for every move (#3425)

Co-authored-by: Eduardo Quezada D'Ottone <eduardo602002@gmail.com>
This commit is contained in:
kittenchilly 2023-10-19 18:16:37 -05:00 committed by GitHub
parent 14b7a70254
commit 33a0fdbbc6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 800 additions and 327 deletions

View File

@ -435,7 +435,6 @@ gBattleScriptsForMoveEffects::
.4byte BattleScript_EffectCorrosiveGas @ EFFECT_CORROSIVE_GAS
.4byte BattleScript_EffectHit @ EFFECT_POPULATION_BOMB
.4byte BattleScript_EffectMortalSpin @ EFFECT_MORTAL_SPIN
.4byte BattleScript_EffectHit @ EFFECT_GIGATON_HAMMER
.4byte BattleScript_EffectSaltCure @ EFFECT_SALT_CURE
.4byte BattleScript_EffectMatchaGotcha @ EFFECT_MATCHA_GOTCHA
.4byte BattleScript_EffectSyrupBomb @ EFFECT_SYRUP_BOMB

View File

@ -173,7 +173,6 @@ struct SpecialStatus
u8 lightningRodRedirected:1;
u8 restoredBattlerSprite: 1;
u8 traced:1;
u8 ppNotAffectedByPressure:1;
u8 faintedHasReplacement:1;
u8 focusBanded:1;
u8 focusSashed:1;

View File

@ -132,6 +132,7 @@ bool32 IsStatRaisingEffect(u32 effect);
bool32 IsAttackBoostMoveEffect(u32 effect);
bool32 IsUngroundingEffect(u32 effect);
bool32 IsSemiInvulnerable(u32 battlerDef, u32 move);
bool32 HasSubstituteIgnoringMove(u32 battler);
bool32 HasSoundMove(u32 battler);
bool32 HasHighCritRatioMove(u32 battler);
bool32 HasMagicCoatAffectedMove(u32 battler);

View File

@ -15,7 +15,7 @@
#define MOVE_LIMITATION_BELCH (1 << 11)
#define MOVE_LIMITATION_THROAT_CHOP (1 << 12)
#define MOVE_LIMITATION_STUFF_CHEEKS (1 << 13)
#define MOVE_LIMITATION_GIGATON_HAMMER (1 << 14)
#define MOVE_LIMITATION_CANT_USE_TWICE (1 << 14)
#define MOVE_LIMITATION_PLACEHOLDER (1 << 15)
#define MOVE_LIMITATIONS_ALL 0xFFFF
@ -115,9 +115,6 @@ void HandleAction_TryFinish(void);
void HandleAction_NothingIsFainted(void);
void HandleAction_ActionFinished(void);
u8 GetBattlerForBattleScript(u8 caseId);
void PressurePPLose(u8 target, u8 attacker, u16 move);
void PressurePPLoseOnUsingPerishSong(u8 attacker);
void PressurePPLoseOnUsingImprison(u8 attacker);
bool32 IsBattlerMarkedForControllerExec(u32 battler);
void MarkBattlerForControllerExec(u32 battler);
void MarkBattlerReceivedLinkData(u32 battler);

View File

@ -68,6 +68,7 @@
#define B_KLUTZ_FLING_INTERACTION GEN_LATEST // In Gen5+, Pokémon with the Klutz ability can't use Fling.
#define B_UPDATED_CONVERSION GEN_LATEST // In Gen6+, Conversion changes the user's type to match their first move's. Before, it would choose a move at random.
#define B_PP_REDUCED_BY_SPITE GEN_LATEST // In Gen4+, Spite reduces the foe's last move's PP by 4, instead of 2 to 5.
#define B_EXTRAPOLATED_MOVE_FLAGS TRUE // Adds move flags to moves that they don't officially have but would likely have if they were in the latest core series game.
// Move accuracy settings
#define B_TOXIC_NEVER_MISS GEN_LATEST // In Gen6+, if Toxic is used by a Poison-type Pokémon, it will never miss.
@ -84,7 +85,6 @@
#define B_GROWTH_STAT_RAISE GEN_LATEST // In Gen5+, Growth raises Attack in addition to Special Attack by 1 stage each. Under the effects of the sun, it raises them by 2 stages each instead.
// Other move settings
#define B_SOUND_SUBSTITUTE GEN_LATEST // In Gen6+, sound moves bypass Substitute.
#define B_INCINERATE_GEMS GEN_LATEST // In Gen6+, Incinerate can destroy Gems.
#define B_CAN_SPITE_FAIL GEN_LATEST // In Gen4+, Spite can no longer fail if the foe's last move only has 1 remaining PP.
#define B_CRASH_IF_TARGET_IMMUNE GEN_LATEST // In Gen4+, The user of Jump Kick or High Jump Kick will "keep going and crash" if it attacks a target that is immune to the move.

View File

@ -412,13 +412,12 @@
#define EFFECT_CORROSIVE_GAS 406
#define EFFECT_POPULATION_BOMB 407
#define EFFECT_MORTAL_SPIN 408
#define EFFECT_GIGATON_HAMMER 409
#define EFFECT_SALT_CURE 410
#define EFFECT_MATCHA_GOTCHA 411
#define EFFECT_SYRUP_BOMB 412
#define EFFECT_IVY_CUDGEL 413
#define EFFECT_MAX_MOVE 414
#define EFFECT_SALT_CURE 409
#define EFFECT_MATCHA_GOTCHA 410
#define EFFECT_SYRUP_BOMB 411
#define EFFECT_IVY_CUDGEL 412
#define EFFECT_MAX_MOVE 413
#define NUM_BATTLE_MOVE_EFFECTS 415
#define NUM_BATTLE_MOVE_EFFECTS 414
#endif // GUARD_CONSTANTS_BATTLE_MOVE_EFFECTS_H

View File

@ -357,6 +357,7 @@ struct BattleMove
u32 mirrorMoveBanned:1;
u32 ignoresKingsRock:1;
u32 highCritRatio:1;
u32 twoTurnMove:1;
u32 punchingMove:1;
u32 sheerForceBoost:1;
u32 bitingMove:1;
@ -379,13 +380,19 @@ struct BattleMove
u32 thawsUser:1;
u32 ignoresSubstitute:1;
u32 strikeCount:4; // Max 15 hits. Defaults to 1 if not set. May apply its effect on each hit.
u32 meFirstBanned:1;
u32 forcePressure:1;
u32 cantUseTwice:1;
u32 gravityBanned:1;
u32 healBlockBanned:1;
u32 meFirstBanned:1;
u32 mimicBanned:1;
u32 metronomeBanned:1;
u32 copycatBanned:1;
u32 assistBanned:1; // Matches same moves as copycatBanned + semi-invulnerable moves and Mirror Coat.
u32 sleepTalkBanned:1;
u32 instructBanned:1;
u32 encoreBanned:1;
u32 parentalBondBanned:1;
};
#define SPINDA_SPOT_WIDTH 16

View File

@ -1442,7 +1442,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
ADJUST_SCORE(-8);
else if (aiData->hpPercents[battlerAtk] <= 25)
ADJUST_SCORE(-10);
else if (B_SOUND_SUBSTITUTE >= GEN_6 && HasSoundMove(battlerDef))
else if (HasSubstituteIgnoringMove(battlerDef))
ADJUST_SCORE(-8);
break;
case EFFECT_LEECH_SEED:
@ -2521,6 +2521,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
if (instructedMove == MOVE_NONE
|| gBattleMoves[instructedMove].instructBanned
|| gBattleMoves[instructedMove].twoTurnMove
|| gBattleMoves[instructedMove].effect == EFFECT_RECHARGE
|| IsZMove(instructedMove)
|| (gLockedMoves[battlerDef] != 0 && gLockedMoves[battlerDef] != 0xFFFF)

View File

@ -1020,7 +1020,6 @@ u32 AI_WhichMoveBetter(u32 move1, u32 move2, u32 battlerAtk, u32 battlerDef, s32
return 0;
}
// Check additional effects.
effect1 = AI_IsMoveEffectInMinus(battlerAtk, battlerDef, move1, noOfHitsToKo);
effect2 = AI_IsMoveEffectInMinus(battlerAtk, battlerDef, move2, noOfHitsToKo);
@ -2359,6 +2358,11 @@ bool32 HasDamagingMoveOfType(u32 battlerId, u32 type)
return FALSE;
}
bool32 HasSubstituteIgnoringMove(u32 battler)
{
CHECK_MOVE_FLAG(ignoresSubstitute);
}
bool32 HasSoundMove(u32 battler)
{
CHECK_MOVE_FLAG(soundMove);

View File

@ -336,7 +336,6 @@ static const u16 sWhiteOutBadgeMoney[9] = { 8, 16, 24, 36, 48, 64, 80, 100, 120
#define TAG_LVLUP_BANNER_MON_ICON 55130
static bool8 IsTwoTurnsMove(u16 move);
static void TrySetDestinyBondToHappen(void);
static u32 ChangeStatBuffs(s8 statValue, u32 statId, u32 flags, const u8 *BS_ptr);
static bool32 IsMonGettingExpSentOut(void);
@ -1365,14 +1364,14 @@ static void Cmd_attackcanceler(void)
gHitMarker |= HITMARKER_OBEYS;
// Check if no available target present on the field.
if (NoTargetPresent(gBattlerAttacker, gCurrentMove)
&& (!IsTwoTurnsMove(gCurrentMove) || (gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS)))
&& (!gBattleMoves[gCurrentMove].twoTurnMove || (gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS)))
{
if (gBattleMoves[gCurrentMove].effect == EFFECT_FLING) // Edge case for removing a mon's item when there is no target available after using Fling.
gBattlescriptCurrInstr = BattleScript_FlingFailConsumeItem;
else
gBattlescriptCurrInstr = BattleScript_FailedFromAtkString;
if (!IsTwoTurnsMove(gCurrentMove) || (gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS))
if (!gBattleMoves[gCurrentMove].twoTurnMove || (gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS))
CancelMultiTurnMoves(gBattlerAttacker);
return;
}
@ -1381,7 +1380,6 @@ static void Cmd_attackcanceler(void)
&& gBattleMoves[gCurrentMove].magicCoatAffected
&& !gProtectStructs[gBattlerAttacker].usesBouncedMove)
{
PressurePPLose(gBattlerAttacker, gBattlerTarget, MOVE_MAGIC_COAT);
gProtectStructs[gBattlerTarget].usesBouncedMove = TRUE;
gBattleCommunication[MULTISTRING_CHOOSER] = 0;
// Edge case for bouncing a powder move against a grass type pokemon.
@ -1426,7 +1424,6 @@ static void Cmd_attackcanceler(void)
{
if ((gProtectStructs[gBattlerByTurnOrder[i]].stealMove) && gBattleMoves[gCurrentMove].snatchAffected)
{
PressurePPLose(gBattlerAttacker, gBattlerByTurnOrder[i], MOVE_SNATCH);
gProtectStructs[gBattlerByTurnOrder[i]].stealMove = FALSE;
gBattleScripting.battler = gBattlerByTurnOrder[i];
BattleScriptPushCursor();
@ -1453,7 +1450,7 @@ static void Cmd_attackcanceler(void)
}
else if (IsBattlerProtected(gBattlerTarget, gCurrentMove)
&& (gCurrentMove != MOVE_CURSE || IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_GHOST))
&& ((!IsTwoTurnsMove(gCurrentMove) || (gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS)))
&& ((!gBattleMoves[gCurrentMove].twoTurnMove || (gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS)))
&& gBattleMoves[gCurrentMove].effect != EFFECT_SUCKER_PUNCH)
{
if (IsMoveMakingContact(gCurrentMove, gBattlerAttacker))
@ -1825,35 +1822,27 @@ static void Cmd_ppreduce(void)
CMD_ARGS();
s32 i, ppToDeduct = 1;
u32 moveTarget = GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove);
if (gBattleControllerExecFlags)
return;
if (!gSpecialStatuses[gBattlerAttacker].ppNotAffectedByPressure)
if (moveTarget == MOVE_TARGET_BOTH
|| moveTarget == MOVE_TARGET_FOES_AND_ALLY
|| moveTarget == MOVE_TARGET_ALL_BATTLERS
|| gBattleMoves[gCurrentMove].forcePressure)
{
switch (GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove))
for (i = 0; i < gBattlersCount; i++)
{
case MOVE_TARGET_FOES_AND_ALLY:
for (i = 0; i < gBattlersCount; i++)
{
if (i != gBattlerAttacker && IsBattlerAlive(i))
ppToDeduct += (GetBattlerAbility(i) == ABILITY_PRESSURE);
}
break;
case MOVE_TARGET_BOTH:
case MOVE_TARGET_OPPONENTS_FIELD:
for (i = 0; i < gBattlersCount; i++)
{
if (GetBattlerSide(i) != GetBattlerSide(gBattlerAttacker) && IsBattlerAlive(i))
ppToDeduct += (GetBattlerAbility(i) == ABILITY_PRESSURE);
}
break;
default:
if (gBattlerAttacker != gBattlerTarget && GetBattlerAbility(gBattlerTarget) == ABILITY_PRESSURE)
ppToDeduct++;
break;
if (GetBattlerSide(i) != GetBattlerSide(gBattlerAttacker) && IsBattlerAlive(i))
ppToDeduct += (GetBattlerAbility(i) == ABILITY_PRESSURE);
}
}
else if (moveTarget != MOVE_TARGET_OPPONENTS_FIELD)
{
if (gBattlerAttacker != gBattlerTarget && GetBattlerAbility(gBattlerTarget) == ABILITY_PRESSURE)
ppToDeduct++;
}
if (!(gHitMarker & (HITMARKER_NO_PPDEDUCT | HITMARKER_NO_ATTACKSTRING)) && gBattleMons[gBattlerAttacker].pp[gCurrMovePos])
{
@ -9569,7 +9558,7 @@ static void Cmd_various(void)
case VARIOUS_TRY_COPYCAT:
{
VARIOUS_ARGS(const u8 *failInstr);
if (gLastUsedMove == MOVE_UNAVAILABLE || gBattleMoves[gLastUsedMove].copycatBanned)
if (gLastUsedMove == MOVE_NONE || gLastUsedMove == MOVE_UNAVAILABLE || gBattleMoves[gLastUsedMove].copycatBanned)
{
gBattlescriptCurrInstr = cmd->failInstr;
}
@ -9589,7 +9578,8 @@ static void Cmd_various(void)
{
VARIOUS_ARGS(const u8 *failInstr);
u16 move = gLastMoves[gBattlerTarget];
if (move == MOVE_UNAVAILABLE || gBattleMoves[move].instructBanned || IsDynamaxed(gBattlerTarget))
if (move == MOVE_NONE || move == MOVE_UNAVAILABLE || gBattleMoves[move].effect == EFFECT_RECHARGE
|| gBattleMoves[move].instructBanned || gBattleMoves[move].twoTurnMove || IsDynamaxed(gBattlerTarget))
{
gBattlescriptCurrInstr = cmd->failInstr;
}
@ -10970,7 +10960,6 @@ static void Cmd_trymirrormove(void)
}
else // no valid moves found
{
gSpecialStatuses[gBattlerAttacker].ppNotAffectedByPressure = TRUE;
gBattlescriptCurrInstr = cmd->nextInstr;
}
}
@ -12458,6 +12447,7 @@ static void Cmd_mimicattackcopy(void)
if ((gBattleMoves[gLastMoves[gBattlerTarget]].mimicBanned)
|| (gBattleMons[gBattlerAttacker].status2 & STATUS2_TRANSFORMED)
|| gLastMoves[gBattlerTarget] == MOVE_NONE
|| gLastMoves[gBattlerTarget] == MOVE_UNAVAILABLE)
{
gBattlescriptCurrInstr = cmd->failInstr;
@ -12565,7 +12555,6 @@ static void Cmd_counterdamagecalculator(void)
}
else
{
gSpecialStatuses[gBattlerAttacker].ppNotAffectedByPressure = TRUE;
gBattlescriptCurrInstr = cmd->failInstr;
}
}
@ -12593,7 +12582,6 @@ static void Cmd_mirrorcoatdamagecalculator(void)
}
else
{
gSpecialStatuses[gBattlerAttacker].ppNotAffectedByPressure = TRUE;
gBattlescriptCurrInstr = cmd->failInstr;
}
}
@ -12652,12 +12640,9 @@ static void Cmd_trysetencore(void)
}
}
if (gLastMoves[gBattlerTarget] == MOVE_NONE
|| gLastMoves[gBattlerTarget] == MOVE_UNAVAILABLE
|| gLastMoves[gBattlerTarget] == MOVE_STRUGGLE
|| gLastMoves[gBattlerTarget] == MOVE_ENCORE
|| gLastMoves[gBattlerTarget] == MOVE_MIRROR_MOVE
|| gLastMoves[gBattlerTarget] == MOVE_SHELL_TRAP)
if ((gBattleMoves[gLastMoves[gBattlerTarget]].encoreBanned)
|| gLastMoves[gBattlerTarget] == MOVE_NONE
|| gLastMoves[gBattlerTarget] == MOVE_UNAVAILABLE)
{
i = MAX_MON_MOVES;
}
@ -12712,7 +12697,7 @@ static void Cmd_settypetorandomresistance(void)
{
gBattlescriptCurrInstr = cmd->failInstr;
}
else if (IsTwoTurnsMove(gLastLandedMoves[gBattlerAttacker])
else if (gBattleMoves[gLastLandedMoves[gBattlerAttacker]].twoTurnMove
&& gBattleMons[gLastHitBy[gBattlerAttacker]].status2 & STATUS2_MULTIPLETURNS)
{
gBattlescriptCurrInstr = cmd->failInstr;
@ -12821,20 +12806,6 @@ static void Cmd_copymovepermanently(void)
}
}
static bool8 IsTwoTurnsMove(u16 move)
{
if (gBattleMoves[move].effect == EFFECT_SKULL_BASH
|| gBattleMoves[move].effect == EFFECT_TWO_TURNS_ATTACK
|| gBattleMoves[move].effect == EFFECT_SOLAR_BEAM
|| gBattleMoves[move].effect == EFFECT_SEMI_INVULNERABLE
|| gBattleMoves[move].effect == EFFECT_BIDE
|| gBattleMoves[move].effect == EFFECT_METEOR_BEAM
|| gBattleMoves[move].effect == EFFECT_GEOMANCY)
return TRUE;
else
return FALSE;
}
static void Cmd_trychoosesleeptalkmove(void)
{
CMD_ARGS(const u8 *failInstr);
@ -12843,8 +12814,8 @@ static void Cmd_trychoosesleeptalkmove(void)
for (i = 0; i < MAX_MON_MOVES; i++)
{
if ((gBattleMoves[gBattleMons[gBattlerAttacker].moves[i]].sleepTalkBanned)
|| IsTwoTurnsMove(gBattleMons[gBattlerAttacker].moves[i]))
if (gBattleMoves[gBattleMons[gBattlerAttacker].moves[i]].sleepTalkBanned
|| gBattleMoves[gBattleMons[gBattlerAttacker].moves[i]].twoTurnMove)
{
unusableMovesBits |= gBitTable[i];
}
@ -13109,7 +13080,6 @@ static void Cmd_trysetspikes(void)
if (gSideTimers[targetSide].spikesAmount == 3)
{
gSpecialStatuses[gBattlerAttacker].ppNotAffectedByPressure = TRUE;
gBattlescriptCurrInstr = cmd->failInstr;
}
else
@ -13150,8 +13120,6 @@ static void Cmd_trysetperishsong(void)
}
}
PressurePPLoseOnUsingPerishSong(gBattlerAttacker);
if (notAffectedCount == gBattlersCount)
gBattlescriptCurrInstr = cmd->failInstr;
else
@ -14256,7 +14224,6 @@ static void Cmd_tryimprison(void)
u8 battler, sideAttacker;
sideAttacker = GetBattlerSide(gBattlerAttacker);
PressurePPLoseOnUsingImprison(gBattlerAttacker);
for (battler = 0; battler < gBattlersCount; battler++)
{
if (sideAttacker != GetBattlerSide(battler))
@ -14351,9 +14318,7 @@ static void Cmd_assistattackselect(void)
{
u16 move = GetMonData(&party[monId], MON_DATA_MOVE1 + moveId);
if (gBattleMoves[move].copycatBanned
|| gBattleMoves[move].effect == EFFECT_SEMI_INVULNERABLE
|| gBattleMoves[move].effect == EFFECT_SKY_DROP)
if (gBattleMoves[move].assistBanned)
continue;
validMoves[chooseableMovesNo++] = move;
@ -14380,8 +14345,6 @@ static void Cmd_trysetmagiccoat(void)
{
CMD_ARGS(const u8 *failInstr);
gBattlerTarget = gBattlerAttacker;
gSpecialStatuses[gBattlerAttacker].ppNotAffectedByPressure = TRUE;
if (gCurrentTurnActionNumber == gBattlersCount - 1) // moves last turn
{
gBattlescriptCurrInstr = cmd->failInstr;
@ -14398,7 +14361,6 @@ static void Cmd_trysetsnatch(void)
{
CMD_ARGS(const u8 *failInstr);
gSpecialStatuses[gBattlerAttacker].ppNotAffectedByPressure = TRUE;
if (gCurrentTurnActionNumber == gBattlersCount - 1) // moves last turn
{
gBattlescriptCurrInstr = cmd->failInstr;
@ -14705,8 +14667,6 @@ bool32 DoesSubstituteBlockMove(u32 battlerAtk, u32 battlerDef, u32 move)
{
if (!(gBattleMons[battlerDef].status2 & STATUS2_SUBSTITUTE))
return FALSE;
else if (B_SOUND_SUBSTITUTE >= GEN_6 && gBattleMoves[move].soundMove)
return FALSE;
else if (gBattleMoves[move].ignoresSubstitute)
return FALSE;
else if (GetBattlerAbility(battlerAtk) == ABILITY_INFILTRATOR)
@ -15625,7 +15585,6 @@ void BS_CalcMetalBurstDmg(void)
}
else
{
gSpecialStatuses[gBattlerAttacker].ppNotAffectedByPressure = TRUE;
gBattlescriptCurrInstr = cmd->failInstr;
}
}
@ -15726,41 +15685,13 @@ static bool32 CriticalCapture(u32 odds)
return FALSE;
}
static const u16 sParentalBondBannedEffects[] =
{
EFFECT_BEAT_UP,
EFFECT_BIDE, // Note: Bide should work with Parental Bond. This will be addressed in future.
EFFECT_ENDEAVOR,
EFFECT_EXPLOSION,
EFFECT_FINAL_GAMBIT,
EFFECT_FLING,
EFFECT_GEOMANCY,
EFFECT_METEOR_BEAM,
EFFECT_MULTI_HIT,
EFFECT_OHKO,
EFFECT_ROLLOUT,
EFFECT_SEMI_INVULNERABLE,
EFFECT_SKULL_BASH,
EFFECT_SKY_DROP,
EFFECT_SOLAR_BEAM,
EFFECT_TRIPLE_KICK,
EFFECT_TWO_TURNS_ATTACK,
EFFECT_UPROAR,
};
bool32 IsMoveAffectedByParentalBond(u32 move, u32 battler)
{
if (move != MOVE_NONE && move != MOVE_STRUGGLE
if (move != MOVE_NONE && move != MOVE_UNAVAILABLE && move != MOVE_STRUGGLE
&& !gBattleMoves[move].parentalBondBanned
&& gBattleMoves[move].split != SPLIT_STATUS
&& gBattleMoves[move].strikeCount < 2)
{
u32 i;
for (i = 0; i < ARRAY_COUNT(sParentalBondBannedEffects); i++)
{
if (gBattleMoves[move].effect == sParentalBondBannedEffects[i])
return FALSE;
}
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
{
switch (GetBattlerMoveTargetType(battler, move))

View File

@ -1212,93 +1212,6 @@ u8 GetBattlerForBattleScript(u8 caseId)
return ret;
}
void PressurePPLose(u8 target, u8 attacker, u16 move)
{
int moveIndex;
if (GetBattlerAbility(target) != ABILITY_PRESSURE)
return;
for (moveIndex = 0; moveIndex < MAX_MON_MOVES; moveIndex++)
{
if (gBattleMons[attacker].moves[moveIndex] == move)
break;
}
if (moveIndex == MAX_MON_MOVES)
return;
if (gBattleMons[attacker].pp[moveIndex] != 0)
gBattleMons[attacker].pp[moveIndex]--;
if (MOVE_IS_PERMANENT(attacker, moveIndex))
{
BtlController_EmitSetMonData(attacker, BUFFER_A, REQUEST_PPMOVE1_BATTLE + moveIndex, 0, 1, &gBattleMons[attacker].pp[moveIndex]);
MarkBattlerForControllerExec(attacker);
}
}
void PressurePPLoseOnUsingImprison(u8 attacker)
{
int i, j;
int imprisonPos = MAX_MON_MOVES;
u8 atkSide = GetBattlerSide(attacker);
for (i = 0; i < gBattlersCount; i++)
{
if (atkSide != GetBattlerSide(i) && GetBattlerAbility(i) == ABILITY_PRESSURE)
{
for (j = 0; j < MAX_MON_MOVES; j++)
{
if (gBattleMons[attacker].moves[j] == MOVE_IMPRISON)
break;
}
if (j != MAX_MON_MOVES)
{
imprisonPos = j;
if (gBattleMons[attacker].pp[j] != 0)
gBattleMons[attacker].pp[j]--;
}
}
}
if (imprisonPos != MAX_MON_MOVES && MOVE_IS_PERMANENT(attacker, imprisonPos))
{
BtlController_EmitSetMonData(attacker, BUFFER_A, REQUEST_PPMOVE1_BATTLE + imprisonPos, 0, 1, &gBattleMons[attacker].pp[imprisonPos]);
MarkBattlerForControllerExec(attacker);
}
}
void PressurePPLoseOnUsingPerishSong(u8 attacker)
{
int i, j;
int perishSongPos = MAX_MON_MOVES;
for (i = 0; i < gBattlersCount; i++)
{
if (GetBattlerAbility(i) == ABILITY_PRESSURE && i != attacker)
{
for (j = 0; j < MAX_MON_MOVES; j++)
{
if (gBattleMons[attacker].moves[j] == MOVE_PERISH_SONG)
break;
}
if (j != MAX_MON_MOVES)
{
perishSongPos = j;
if (gBattleMons[attacker].pp[j] != 0)
gBattleMons[attacker].pp[j]--;
}
}
}
if (perishSongPos != MAX_MON_MOVES && MOVE_IS_PERMANENT(attacker, perishSongPos))
{
BtlController_EmitSetMonData(attacker, BUFFER_A, REQUEST_PPMOVE1_BATTLE + perishSongPos, 0, 1, &gBattleMons[attacker].pp[perishSongPos]);
MarkBattlerForControllerExec(attacker);
}
}
static void UNUSED MarkAllBattlersForControllerExec(void)
{
int i;
@ -1575,26 +1488,7 @@ bool32 IsHealBlockPreventingMove(u32 battler, u32 move)
if (!(gStatuses3[battler] & STATUS3_HEAL_BLOCK))
return FALSE;
switch (gBattleMoves[move].effect)
{
case EFFECT_MORNING_SUN:
case EFFECT_SYNTHESIS:
case EFFECT_MOONLIGHT:
case EFFECT_RESTORE_HP:
case EFFECT_REST:
case EFFECT_ROOST:
case EFFECT_HEALING_WISH:
case EFFECT_WISH:
case EFFECT_HEAL_PULSE:
case EFFECT_JUNGLE_HEALING:
return TRUE;
case EFFECT_ABSORB:
case EFFECT_STRENGTH_SAP:
case EFFECT_DREAM_EATER:
return B_HEAL_BLOCKING >= GEN_6;
default:
return FALSE;
}
return gBattleMoves[move].healBlockBanned;
}
static bool32 IsBelchPreventingMove(u32 battler, u32 move)
@ -1755,7 +1649,7 @@ u32 TrySetCantSelectMoveBattleScript(u32 battler)
}
}
if (gBattleMoves[move].effect == EFFECT_GIGATON_HAMMER && move == gLastResultingMoves[battler])
if (gBattleMoves[move].cantUseTwice && move == gLastResultingMoves[battler])
{
gCurrentMove = move;
PREPARE_MOVE_BUFFER(gBattleTextBuff1, gCurrentMove);
@ -1910,7 +1804,8 @@ u8 CheckMoveLimitations(u32 battler, u8 unusableMoves, u16 check)
// Gorilla Tactics
else if (check & MOVE_LIMITATION_CHOICE_ITEM && GetBattlerAbility(battler) == ABILITY_GORILLA_TACTICS && *choicedMove != MOVE_NONE && *choicedMove != MOVE_UNAVAILABLE && *choicedMove != gBattleMons[battler].moves[i])
unusableMoves |= gBitTable[i];
else if (check & MOVE_LIMITATION_GIGATON_HAMMER && gBattleMoves[gBattleMons[battler].moves[i]].effect == EFFECT_GIGATON_HAMMER && gBattleMons[battler].moves[i] == gLastResultingMoves[battler])
// Can't Use Twice flag
else if (check & MOVE_LIMITATION_CANT_USE_TWICE && gBattleMoves[gBattleMons[battler].moves[i]].cantUseTwice && gBattleMons[battler].moves[i] == gLastResultingMoves[battler])
unusableMoves |= gBitTable[i];
}
return unusableMoves;
@ -8305,9 +8200,6 @@ bool32 IsBattlerProtected(u32 battler, u32 move)
if (gProtectStructs[battler].maxGuarded && IsMoveBlockedByMaxGuard(move))
return TRUE;
if (move == MOVE_TEATIME)
return FALSE;
// Protective Pads doesn't stop Unseen Fist from bypassing Protect effects, so IsMoveMakingContact() isn't used here.
// This means extra logic is needed to handle Shell Side Arm.
if (GetBattlerAbility(gBattlerAttacker) == ABILITY_UNSEEN_FIST
@ -8316,8 +8208,6 @@ bool32 IsBattlerProtected(u32 battler, u32 move)
return FALSE;
else if (gBattleMoves[move].ignoresProtect)
return FALSE;
else if (gBattleMoves[move].effect == EFFECT_FEINT)
return FALSE;
else if (gProtectStructs[battler].protected)
return TRUE;
else if (gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_WIDE_GUARD

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,70 @@
#include "global.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Pressure causes opponent's moves to use up 1 additional PP")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { MovesWithPP({MOVE_POUND, 35}); }
OPPONENT(SPECIES_WOBBUFFET) { Ability(ABILITY_PRESSURE); }
} WHEN {
TURN { MOVE(player, MOVE_POUND); }
} THEN {
EXPECT_EQ(player->pp[0], 33);
}
}
DOUBLE_BATTLE_TEST("Pressure's effect stacks with multiple Pokémon")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { MovesWithPP({MOVE_SWIFT, 20}); }
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_WOBBUFFET) { Ability(ABILITY_PRESSURE); }
OPPONENT(SPECIES_WYNAUT) { Ability(ABILITY_PRESSURE); }
} WHEN {
TURN { MOVE(playerLeft, MOVE_SWIFT); }
} THEN {
EXPECT_EQ(playerLeft->pp[0], 17);
}
}
SINGLE_BATTLE_TEST("Pressure's effect applies to Imprison and Snatch")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { MovesWithPP({MOVE_IMPRISON, 10}, {MOVE_SNATCH, 10}); }
OPPONENT(SPECIES_WOBBUFFET) { Ability(ABILITY_PRESSURE); }
} WHEN {
TURN { MOVE(player, MOVE_IMPRISON); }
TURN { MOVE(player, MOVE_SNATCH); }
} THEN {
EXPECT_EQ(player->pp[0], 8);
EXPECT_EQ(player->pp[1], 8);
}
}
SINGLE_BATTLE_TEST("Pressure's effect applies to Spikes, Stealth Rock and Toxic Spikes")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { MovesWithPP({MOVE_SPIKES, 20}, {MOVE_STEALTH_ROCK, 20}, {MOVE_TOXIC_SPIKES, 20}); }
OPPONENT(SPECIES_WOBBUFFET) { Ability(ABILITY_PRESSURE); }
} WHEN {
TURN { MOVE(player, MOVE_SPIKES); }
TURN { MOVE(player, MOVE_STEALTH_ROCK); }
TURN { MOVE(player, MOVE_TOXIC_SPIKES); }
} THEN {
EXPECT_EQ(player->pp[0], 18);
EXPECT_EQ(player->pp[1], 18);
EXPECT_EQ(player->pp[2], 18);
}
}
SINGLE_BATTLE_TEST("Pressure's effect doesn't apply to Sticky Web")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { MovesWithPP({MOVE_STICKY_WEB, 20}); }
OPPONENT(SPECIES_WOBBUFFET) { Ability(ABILITY_PRESSURE); }
} WHEN {
TURN { MOVE(player, MOVE_STICKY_WEB); }
} THEN {
EXPECT_EQ(player->pp[0], 19);
}
}

View File

@ -1,63 +0,0 @@
#include "global.h"
#include "test/battle.h"
ASSUMPTIONS
{
ASSUME(gBattleMoves[MOVE_GIGATON_HAMMER].effect == EFFECT_GIGATON_HAMMER);
}
SINGLE_BATTLE_TEST("Struggle will be used if slow Encore is used on Gigaton Hammer")
{
GIVEN {
ASSUME(gBattleMoves[MOVE_ENCORE].effect == EFFECT_ENCORE);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_GIGATON_HAMMER); MOVE(opponent, MOVE_ENCORE); }
TURN { FORCED_MOVE(player); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_GIGATON_HAMMER, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_ENCORE, opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_STRUGGLE, player);
}
}
SINGLE_BATTLE_TEST("Gigaton Hammer strikes again if fast encore is used")
{
GIVEN {
ASSUME(gBattleMoves[MOVE_ENCORE].effect == EFFECT_ENCORE);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_GIGATON_HAMMER); }
TURN { MOVE(opponent, MOVE_ENCORE); FORCED_MOVE(player); }
TURN { FORCED_MOVE(player); }
TURN { FORCED_MOVE(player); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_GIGATON_HAMMER, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_ENCORE, opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_GIGATON_HAMMER, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_STRUGGLE, player);
}
}
SINGLE_BATTLE_TEST("Gigaton Hammer alternates with Struggle if it is the only usable move left")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_GIGATON_HAMMER, MOVE_NONE, MOVE_NONE, MOVE_NONE); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_GIGATON_HAMMER); }
TURN { FORCED_MOVE(player); }
TURN { MOVE(player, MOVE_GIGATON_HAMMER); }
TURN { FORCED_MOVE(player); }
TURN { MOVE(player, MOVE_GIGATON_HAMMER); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_GIGATON_HAMMER, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_STRUGGLE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_GIGATON_HAMMER, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_STRUGGLE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_GIGATON_HAMMER, player);
}
}

View File

@ -0,0 +1,93 @@
#include "global.h"
#include "test/battle.h"
ASSUMPTIONS
{
ASSUME(gBattleMoves[MOVE_GIGATON_HAMMER].cantUseTwice == TRUE);
ASSUME(gBattleMoves[MOVE_BLOOD_MOON].cantUseTwice == TRUE);
}
SINGLE_BATTLE_TEST("Struggle will be used if slow Encore is used on moves with the cantUseTwice flag")
{
u32 move;
PARAMETRIZE { move = MOVE_GIGATON_HAMMER; }
PARAMETRIZE { move = MOVE_BLOOD_MOON; }
GIVEN {
ASSUME(gBattleMoves[MOVE_ENCORE].effect == EFFECT_ENCORE);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, move); MOVE(opponent, MOVE_ENCORE); }
TURN { FORCED_MOVE(player); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, move, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_ENCORE, opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_STRUGGLE, player);
}
}
SINGLE_BATTLE_TEST("Moves with the cantUseTwice flag strike again if fast encore is used")
{
u32 move;
PARAMETRIZE { move = MOVE_GIGATON_HAMMER; }
PARAMETRIZE { move = MOVE_BLOOD_MOON; }
GIVEN {
ASSUME(gBattleMoves[MOVE_ENCORE].effect == EFFECT_ENCORE);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, move); }
TURN { MOVE(opponent, MOVE_ENCORE); FORCED_MOVE(player); }
TURN { FORCED_MOVE(player); }
TURN { FORCED_MOVE(player); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponent);
ANIMATION(ANIM_TYPE_MOVE, move, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_ENCORE, opponent);
ANIMATION(ANIM_TYPE_MOVE, move, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_STRUGGLE, player);
}
}
SINGLE_BATTLE_TEST("Moves with the cantUseTwice flag alternate with Struggle if it is the only usable move left")
{
u32 move;
PARAMETRIZE { move = MOVE_GIGATON_HAMMER; }
PARAMETRIZE { move = MOVE_BLOOD_MOON; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Moves(move, MOVE_NONE, MOVE_NONE, MOVE_NONE); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, move); }
TURN { FORCED_MOVE(player); }
TURN { MOVE(player, move); }
TURN { FORCED_MOVE(player); }
TURN { MOVE(player, move); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, move, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_STRUGGLE, player);
ANIMATION(ANIM_TYPE_MOVE, move, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_STRUGGLE, player);
ANIMATION(ANIM_TYPE_MOVE, move, player);
}
}
SINGLE_BATTLE_TEST("Moves with the cantUseTwice flag can alternate between each other")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_GIGATON_HAMMER, MOVE_BLOOD_MOON, MOVE_NONE, MOVE_NONE); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_GIGATON_HAMMER); }
TURN { MOVE(player, MOVE_BLOOD_MOON); }
TURN { MOVE(player, MOVE_GIGATON_HAMMER); }
TURN { MOVE(player, MOVE_BLOOD_MOON); }
TURN { MOVE(player, MOVE_GIGATON_HAMMER); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_GIGATON_HAMMER, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_BLOOD_MOON, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_GIGATON_HAMMER, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_BLOOD_MOON, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_GIGATON_HAMMER, player);
}
}