From c1f18d07656640d7964934f3065b39ff3aca69f7 Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Wed, 2 Apr 2025 17:26:05 +0200 Subject: [PATCH] =?UTF-8?q?Refactor=20protect=20to=20allow=20at=20least=20?= =?UTF-8?q?126=20different=20types=20of=20protect=20eff=E2=80=A6=20(#6506)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/battle_scripts_1.s | 2 +- include/battle.h | 30 +--- include/battle_util.h | 1 + include/constants/battle.h | 14 +- include/move.h | 33 +++- src/battle_main.c | 17 +- src/battle_script_commands.c | 309 +++++++++++++++-------------------- src/battle_util.c | 85 +++++++--- src/data/moves_info.h | 17 +- 9 files changed, 252 insertions(+), 256 deletions(-) diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index d359067792..f3fdab5ae1 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -3836,7 +3836,7 @@ BattleScript_EffectHoldHands:: attackcanceler attackstring ppreduce - jumpifsideaffecting BS_TARGET, SIDE_STATUS_CRAFTY_SHIELD, BattleScript_ButItFailed + accuracycheck BattleScript_ButItFailed, NO_ACC_CALC_CHECK_LOCK_ON jumpifbyteequal gBattlerTarget, gBattlerAttacker, BattleScript_ButItFailed attackanimation waitanimation diff --git a/include/battle.h b/include/battle.h index d8f8f1a409..594176ceb9 100644 --- a/include/battle.h +++ b/include/battle.h @@ -138,11 +138,7 @@ struct DisableStruct // Fully Cleared each turn after end turn effects are done. A few things are cleared before end turn effects struct ProtectStruct { - u32 protected:1; - u32 spikyShielded:1; - u32 kingsShielded:1; - u32 banefulBunkered:1; - u32 obstructed:1; + u32 protected:7; // 126 protect options u32 endured:1; u32 noValidMoves:1; u32 helpingHand:1; @@ -167,9 +163,8 @@ struct ProtectStruct u32 usedThroatChopPreventedMove:1; u32 statRaised:1; u32 usedCustapBerry:1; // also quick claw - u32 touchedProtectLike:1; - u32 unused:1; // End of 32-bit bitfield + u16 touchedProtectLike:1; u16 disableEjectPack:1; u16 statFell:1; u16 pranksterElevated:1; @@ -177,16 +172,13 @@ struct ProtectStruct u16 beakBlastCharge:1; u16 quash:1; u16 shellTrap:1; - u16 maxGuarded:1; - u16 silkTrapped:1; - u16 burningBulwarked:1; u16 eatMirrorHerb:1; u16 activateOpportunist:2; // 2 - to copy stats. 1 - stats copied (do not repeat). 0 - no stats to copy u16 usedAllySwitch:1; - u16 padding:2; + u16 padding:4; // End of 16-bit bitfield - u32 physicalDmg; - u32 specialDmg; + u16 physicalDmg; + u16 specialDmg; u8 physicalBattlerId; u8 specialBattlerId; }; @@ -910,18 +902,6 @@ static inline bool32 IsBattleMoveRecoil(u32 move) gBattleMons[battlerId].types[2] = TYPE_MYSTERY; \ } -#define IS_BATTLER_PROTECTED(battlerId)(gProtectStructs[battlerId].protected \ - || gSideStatuses[GetBattlerSide(battlerId)] & SIDE_STATUS_WIDE_GUARD \ - || gSideStatuses[GetBattlerSide(battlerId)] & SIDE_STATUS_QUICK_GUARD \ - || gSideStatuses[GetBattlerSide(battlerId)] & SIDE_STATUS_CRAFTY_SHIELD \ - || gSideStatuses[GetBattlerSide(battlerId)] & SIDE_STATUS_MAT_BLOCK \ - || gProtectStructs[battlerId].spikyShielded \ - || gProtectStructs[battlerId].kingsShielded \ - || gProtectStructs[battlerId].banefulBunkered \ - || gProtectStructs[battlerId].burningBulwarked \ - || gProtectStructs[battlerId].obstructed \ - || gProtectStructs[battlerId].silkTrapped) - #define GET_STAT_BUFF_ID(n) ((n & 7)) // first three bits 0x1, 0x2, 0x4 #define GET_STAT_BUFF_VALUE_WITH_SIGN(n) ((n & 0xF8)) #define GET_STAT_BUFF_VALUE(n) (((n >> 3) & 0xF)) // 0x8, 0x10, 0x20, 0x40 diff --git a/include/battle_util.h b/include/battle_util.h index ed8bda6491..c7f2bd44b8 100644 --- a/include/battle_util.h +++ b/include/battle_util.h @@ -231,6 +231,7 @@ u32 IsAbilityOnField(u32 ability); u32 IsAbilityOnFieldExcept(u32 battler, u32 ability); u32 IsAbilityPreventingEscape(u32 battler); bool32 IsBattlerProtected(u32 battlerAtk, u32 battlerDef, u32 move); +u32 GetProtectType(enum ProtectMethod method); bool32 CanBattlerEscape(u32 battler); // no ability check void BattleScriptExecute(const u8 *BS_ptr); void BattleScriptPushCursorAndCallback(const u8 *BS_ptr); diff --git a/include/constants/battle.h b/include/constants/battle.h index ce1cf0e948..fc1127b8c8 100644 --- a/include/constants/battle.h +++ b/include/constants/battle.h @@ -239,15 +239,11 @@ #define SIDE_STATUS_TOXIC_SPIKES (1 << 13) #define SIDE_STATUS_STEALTH_ROCK (1 << 14) // Missing flags previously were SIDE_STATUS_TOXIC_SPIKES_DAMAGED, SIDE_STATUS_STEALTH_ROCK_DAMAGED, SIDE_STATUS_STICKY_WEB_DAMAGED -#define SIDE_STATUS_QUICK_GUARD (1 << 18) -#define SIDE_STATUS_WIDE_GUARD (1 << 19) -#define SIDE_STATUS_CRAFTY_SHIELD (1 << 20) -#define SIDE_STATUS_MAT_BLOCK (1 << 21) -#define SIDE_STATUS_STEELSURGE (1 << 22) -#define SIDE_STATUS_DAMAGE_NON_TYPES (1 << 23) -#define SIDE_STATUS_RAINBOW (1 << 24) -#define SIDE_STATUS_SEA_OF_FIRE (1 << 25) -#define SIDE_STATUS_SWAMP (1 << 26) +#define SIDE_STATUS_STEELSURGE (1 << 18) +#define SIDE_STATUS_DAMAGE_NON_TYPES (1 << 19) +#define SIDE_STATUS_RAINBOW (1 << 20) +#define SIDE_STATUS_SEA_OF_FIRE (1 << 21) +#define SIDE_STATUS_SWAMP (1 << 22) #define SIDE_STATUS_HAZARDS_ANY (SIDE_STATUS_SPIKES | SIDE_STATUS_STICKY_WEB | SIDE_STATUS_TOXIC_SPIKES | SIDE_STATUS_STEALTH_ROCK | SIDE_STATUS_STEELSURGE) #define SIDE_STATUS_SCREEN_ANY (SIDE_STATUS_REFLECT | SIDE_STATUS_LIGHTSCREEN | SIDE_STATUS_AURORA_VEIL) diff --git a/include/move.h b/include/move.h index d00751642a..ce1c40f5cc 100644 --- a/include/move.h +++ b/include/move.h @@ -38,6 +38,30 @@ struct AdditionalEffect u8 chance; // 0% = effect certain, primary effect }; +enum ProtectType +{ + PROTECT_TYPE_NONE, + PROTECT_TYPE_SIDE, + PROTECT_TYPE_SINGLE, +}; + +enum ProtectMethod +{ + PROTECT_NONE, + PROTECT_NORMAL, + PROTECT_SPIKY_SHIELD, + PROTECT_KINGS_SHIELD, + PROTECT_BANEFUL_BUNKER, + PROTECT_BURNING_BULWARK, + PROTECT_OBSTRUCT, + PROTECT_SILK_TRAP, + PROTECT_MAX_GUARD, + PROTECT_WIDE_GUARD, + PROTECT_QUICK_GUARD, + PROTECT_CRAFTY_SHIELD, + PROTECT_MAT_BLOCK, +}; + struct MoveInfo { const u8 *name; @@ -113,10 +137,7 @@ struct MoveInfo u16 stringId; u16 status; } twoTurnAttack; - struct { - u16 side; - u16 property; // can be used to remove the hardcoded values - } protect; + u32 protectMethod; u32 status; u32 moveProperty; u32 holdEffect; @@ -447,9 +468,9 @@ static inline u32 GetMoveTwoTurnAttackWeather(u32 moveId) return gMovesInfo[SanitizeMoveId(moveId)].argument.twoTurnAttack.status; } -static inline u32 GetMoveProtectSide(u32 moveId) +static inline u32 GetMoveProtectMethod(u32 moveId) { - return gMovesInfo[SanitizeMoveId(moveId)].argument.protect.side; + return gMovesInfo[SanitizeMoveId(moveId)].argument.protectMethod; } static inline u32 GetMoveEffectArg_Status(u32 moveId) diff --git a/src/battle_main.c b/src/battle_main.c index 03f8f7d815..6845190d10 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -3275,14 +3275,10 @@ const u8* FaintClearSetData(u32 battler) memset(&gDisableStructs[battler], 0, sizeof(struct DisableStruct)); - gProtectStructs[battler].protected = FALSE; - gProtectStructs[battler].spikyShielded = FALSE; - gProtectStructs[battler].kingsShielded = FALSE; - gProtectStructs[battler].banefulBunkered = FALSE; + if (GetProtectType(gProtectStructs[battler].protected) == PROTECT_TYPE_SINGLE) // Side type protects expire at the end of the turn + gProtectStructs[battler].protected = PROTECT_NONE; + gProtectStructs[battler].quash = FALSE; - gProtectStructs[battler].obstructed = FALSE; - gProtectStructs[battler].silkTrapped = FALSE; - gProtectStructs[battler].burningBulwarked = FALSE; gProtectStructs[battler].endured = FALSE; gProtectStructs[battler].noValidMoves = FALSE; gProtectStructs[battler].helpingHand = FALSE; @@ -5107,10 +5103,7 @@ static void TurnValuesCleanUp(bool8 var0) { if (var0) { - gProtectStructs[i].protected = FALSE; - gProtectStructs[i].spikyShielded = FALSE; - gProtectStructs[i].kingsShielded = FALSE; - gProtectStructs[i].banefulBunkered = FALSE; + gProtectStructs[i].protected = PROTECT_NONE; gProtectStructs[i].quash = FALSE; gProtectStructs[i].usedCustapBerry = FALSE; gProtectStructs[i].quickDraw = FALSE; @@ -5142,8 +5135,6 @@ static void TurnValuesCleanUp(bool8 var0) gBattleStruct->battlerState[i].usedEjectItem = FALSE; } - gSideStatuses[B_SIDE_PLAYER] &= ~(SIDE_STATUS_QUICK_GUARD | SIDE_STATUS_WIDE_GUARD | SIDE_STATUS_CRAFTY_SHIELD | SIDE_STATUS_MAT_BLOCK); - gSideStatuses[B_SIDE_OPPONENT] &= ~(SIDE_STATUS_QUICK_GUARD | SIDE_STATUS_WIDE_GUARD | SIDE_STATUS_CRAFTY_SHIELD | SIDE_STATUS_MAT_BLOCK); gSideTimers[B_SIDE_PLAYER].followmeTimer = 0; gSideTimers[B_SIDE_OPPONENT].followmeTimer = 0; diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 4e9a588d47..8ba9d70211 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -1362,7 +1362,9 @@ static void Cmd_attackcanceler(void) } // Z-moves and Max Moves bypass protection, but deal reduced damage (factored in AccumulateOtherModifiers) - if ((IsZMove(gCurrentMove) || IsMaxMove(gCurrentMove)) && IS_BATTLER_PROTECTED(gBattlerTarget)) + if ((IsZMove(gCurrentMove) || IsMaxMove(gCurrentMove)) + && gProtectStructs[gBattlerTarget].protected != PROTECT_NONE + && gProtectStructs[gBattlerTarget].protected != PROTECT_MAX_GUARD) { BattleScriptPush(cmd->nextInstr); gBattlescriptCurrInstr = BattleScript_CouldntFullyProtect; @@ -1705,7 +1707,7 @@ static void AccuracyCheck(bool32 recalcDragonDarts, const u8 *nextInstr, const u gBattlescriptCurrInstr = nextInstr; if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_DYNAMAX) { - if (gProtectStructs[gBattlerTarget].maxGuarded) + if (gProtectStructs[gBattlerTarget].protected == PROTECT_MAX_GUARD) gBattlescriptCurrInstr = nextInstr; else AbilityBattleEffects(ABILITYEFFECT_ABSORBING, gBattlerTarget, 0, 0, gCurrentMove); @@ -3950,19 +3952,20 @@ void SetMoveEffect(bool32 primary, bool32 certain) } break; case MOVE_EFFECT_FEINT: - if (IS_BATTLER_PROTECTED(gBattlerTarget)) + i = FALSE; // Remove Protect if any + if (gProtectStructs[gBattlerTarget].protected != PROTECT_NONE + && gProtectStructs[gBattlerTarget].protected != PROTECT_MAX_GUARD) + { + gProtectStructs[gBattlerTarget].protected = PROTECT_NONE; + i = TRUE; + } + if (GetProtectType(gProtectStructs[BATTLE_PARTNER(gBattlerTarget)].protected) == PROTECT_TYPE_SIDE) + { + gProtectStructs[BATTLE_PARTNER(gBattlerTarget)].protected = PROTECT_NONE; + i = TRUE; + } + if (i) { - gProtectStructs[gBattlerTarget].protected = FALSE; - gSideStatuses[GetBattlerSide(gBattlerTarget)] &= ~SIDE_STATUS_WIDE_GUARD; - gSideStatuses[GetBattlerSide(gBattlerTarget)] &= ~SIDE_STATUS_QUICK_GUARD; - gSideStatuses[GetBattlerSide(gBattlerTarget)] &= ~SIDE_STATUS_CRAFTY_SHIELD; - gSideStatuses[GetBattlerSide(gBattlerTarget)] &= ~SIDE_STATUS_MAT_BLOCK; - gProtectStructs[gBattlerTarget].spikyShielded = FALSE; - gProtectStructs[gBattlerTarget].kingsShielded = FALSE; - gProtectStructs[gBattlerTarget].banefulBunkered = FALSE; - gProtectStructs[gBattlerTarget].obstructed = FALSE; - gProtectStructs[gBattlerTarget].silkTrapped = FALSE; - gProtectStructs[gBattlerTarget].burningBulwarked = FALSE; BattleScriptPush(gBattlescriptCurrInstr + 1); if (gCurrentMove == MOVE_HYPERSPACE_FURY) gBattlescriptCurrInstr = BattleScript_HyperspaceFuryRemoveProtect; @@ -6462,84 +6465,104 @@ static void Cmd_moveend(void) case MOVEEND_PROTECT_LIKE_EFFECT: if (gProtectStructs[gBattlerAttacker].touchedProtectLike) { - if (gProtectStructs[gBattlerTarget].spikyShielded - && !IsProtectivePadsProtected(gBattlerAttacker, GetBattlerHoldEffect(gBattlerAttacker, TRUE)) - && !IsMagicGuardProtected(gBattlerAttacker, GetBattlerAbility(gBattlerAttacker))) + enum ProtectMethod method = gProtectStructs[gBattlerTarget].protected; + switch (method) { - gProtectStructs[gBattlerAttacker].touchedProtectLike = FALSE; - gBattleStruct->moveDamage[gBattlerAttacker] = GetNonDynamaxMaxHP(gBattlerAttacker) / 8; - if (gBattleStruct->moveDamage[gBattlerAttacker] == 0) - gBattleStruct->moveDamage[gBattlerAttacker] = 1; - PREPARE_MOVE_BUFFER(gBattleTextBuff1, MOVE_SPIKY_SHIELD); - BattleScriptPushCursor(); - gBattlescriptCurrInstr = BattleScript_SpikyShieldEffect; - effect = 1; - } - else if (gProtectStructs[gBattlerTarget].kingsShielded - && !IsProtectEffectAffected(gBattlerAttacker, gCurrentMove)) - { - gProtectStructs[gBattlerAttacker].touchedProtectLike = FALSE; - i = gBattlerAttacker; - gBattlerAttacker = gBattlerTarget; - gBattlerTarget = i; // gBattlerTarget and gBattlerAttacker are swapped in order to activate Defiant, if applicable - if (B_KINGS_SHIELD_LOWER_ATK >= GEN_8) - gBattleScripting.moveEffect = MOVE_EFFECT_ATK_MINUS_1; - else - gBattleScripting.moveEffect = MOVE_EFFECT_ATK_MINUS_2; - BattleScriptPushCursor(); - gBattlescriptCurrInstr = BattleScript_KingsShieldEffect; - effect = 1; - } - else if (gProtectStructs[gBattlerTarget].banefulBunkered - && !IsProtectivePadsProtected(gBattlerAttacker, GetBattlerHoldEffect(gBattlerAttacker, TRUE))) - { - gProtectStructs[gBattlerAttacker].touchedProtectLike = FALSE; - gBattleScripting.moveEffect = MOVE_EFFECT_POISON | MOVE_EFFECT_AFFECTS_USER; - PREPARE_MOVE_BUFFER(gBattleTextBuff1, MOVE_BANEFUL_BUNKER); - BattleScriptPushCursor(); - gBattlescriptCurrInstr = BattleScript_BanefulBunkerEffect; - effect = 1; - } - else if (gProtectStructs[gBattlerTarget].obstructed - && moveEffect != EFFECT_SUCKER_PUNCH - && moveEffect != EFFECT_UPPER_HAND - && !IsProtectEffectAffected(gBattlerAttacker, gCurrentMove)) - { - gProtectStructs[gBattlerAttacker].touchedProtectLike = FALSE; - i = gBattlerAttacker; - gBattlerAttacker = gBattlerTarget; - gBattlerTarget = i; // gBattlerTarget and gBattlerAttacker are swapped in order to activate Defiant, if applicable - gBattleScripting.moveEffect = MOVE_EFFECT_DEF_MINUS_2; - BattleScriptPushCursor(); - gBattlescriptCurrInstr = BattleScript_KingsShieldEffect; - effect = 1; - } - else if (gProtectStructs[gBattlerTarget].silkTrapped - && !IsProtectEffectAffected(gBattlerAttacker, gCurrentMove)) - { - gProtectStructs[gBattlerAttacker].touchedProtectLike = FALSE; - i = gBattlerAttacker; - gBattlerAttacker = gBattlerTarget; - gBattlerTarget = i; // gBattlerTarget and gBattlerAttacker are swapped in order to activate Defiant, if applicable - gBattleScripting.moveEffect = MOVE_EFFECT_SPD_MINUS_1; - BattleScriptPushCursor(); - gBattlescriptCurrInstr = BattleScript_KingsShieldEffect; - effect = 1; - } - else if (gProtectStructs[gBattlerTarget].burningBulwarked - && !IsProtectivePadsProtected(gBattlerAttacker, GetBattlerHoldEffect(gBattlerAttacker, TRUE))) - { - gProtectStructs[gBattlerAttacker].touchedProtectLike = FALSE; - gBattleScripting.moveEffect = MOVE_EFFECT_BURN | MOVE_EFFECT_AFFECTS_USER; - PREPARE_MOVE_BUFFER(gBattleTextBuff1, MOVE_BURNING_BULWARK); - BattleScriptPushCursor(); - gBattlescriptCurrInstr = BattleScript_BanefulBunkerEffect; - effect = 1; + case PROTECT_SPIKY_SHIELD: + if (moveEffect != EFFECT_COUNTER + && !IsProtectivePadsProtected(gBattlerAttacker, GetBattlerHoldEffect(gBattlerAttacker, TRUE)) + && !IsMagicGuardProtected(gBattlerAttacker, GetBattlerAbility(gBattlerAttacker))) + { + gProtectStructs[gBattlerAttacker].touchedProtectLike = FALSE; + gBattleStruct->moveDamage[gBattlerAttacker] = GetNonDynamaxMaxHP(gBattlerAttacker) / 8; + if (gBattleStruct->moveDamage[gBattlerAttacker] == 0) + gBattleStruct->moveDamage[gBattlerAttacker] = 1; + PREPARE_MOVE_BUFFER(gBattleTextBuff1, MOVE_SPIKY_SHIELD); + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_SpikyShieldEffect; + effect = 1; + } + break; + case PROTECT_KINGS_SHIELD: + if (!IsProtectivePadsProtected(gBattlerAttacker, GetBattlerHoldEffect(gBattlerAttacker, TRUE))) + { + gProtectStructs[gBattlerAttacker].touchedProtectLike = FALSE; + i = gBattlerAttacker; + gBattlerAttacker = gBattlerTarget; + gBattlerTarget = i; // gBattlerTarget and gBattlerAttacker are swapped in order to activate Defiant, if applicable + if (B_KINGS_SHIELD_LOWER_ATK >= GEN_8) + gBattleScripting.moveEffect = MOVE_EFFECT_ATK_MINUS_1; + else + gBattleScripting.moveEffect = MOVE_EFFECT_ATK_MINUS_2; + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_KingsShieldEffect; + effect = 1; + } + break; + case PROTECT_BANEFUL_BUNKER: + if (!IsProtectivePadsProtected(gBattlerAttacker, GetBattlerHoldEffect(gBattlerAttacker, TRUE))) + { + gProtectStructs[gBattlerAttacker].touchedProtectLike = FALSE; + gBattleScripting.moveEffect = MOVE_EFFECT_POISON | MOVE_EFFECT_AFFECTS_USER; + PREPARE_MOVE_BUFFER(gBattleTextBuff1, MOVE_BANEFUL_BUNKER); + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_BanefulBunkerEffect; + effect = 1; + } + break; + case PROTECT_BURNING_BULWARK: + if (!IsProtectivePadsProtected(gBattlerAttacker, GetBattlerHoldEffect(gBattlerAttacker, TRUE))) + { + gProtectStructs[gBattlerAttacker].touchedProtectLike = FALSE; + gBattleScripting.moveEffect = MOVE_EFFECT_BURN | MOVE_EFFECT_AFFECTS_USER; + PREPARE_MOVE_BUFFER(gBattleTextBuff1, MOVE_BURNING_BULWARK); + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_BanefulBunkerEffect; + effect = 1; + } + break; + case PROTECT_OBSTRUCT: + if (moveEffect != EFFECT_SUCKER_PUNCH // Why??? + && moveEffect != EFFECT_UPPER_HAND // Why??? + && !IsProtectivePadsProtected(gBattlerAttacker, GetBattlerHoldEffect(gBattlerAttacker, TRUE))) + { + gProtectStructs[gBattlerAttacker].touchedProtectLike = FALSE; + i = gBattlerAttacker; + gBattlerAttacker = gBattlerTarget; + gBattlerTarget = i; // gBattlerTarget and gBattlerAttacker are swapped in order to activate Defiant, if applicable + gBattleScripting.moveEffect = MOVE_EFFECT_DEF_MINUS_2; + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_KingsShieldEffect; + effect = 1; + } + break; + case PROTECT_SILK_TRAP: + if (!IsProtectivePadsProtected(gBattlerAttacker, GetBattlerHoldEffect(gBattlerAttacker, TRUE))) + { + gProtectStructs[gBattlerAttacker].touchedProtectLike = FALSE; + i = gBattlerAttacker; + gBattlerAttacker = gBattlerTarget; + gBattlerTarget = i; // gBattlerTarget and gBattlerAttacker are swapped in order to activate Defiant, if applicable + gBattleScripting.moveEffect = MOVE_EFFECT_SPD_MINUS_1; + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_KingsShieldEffect; + effect = 1; + } + break; + case PROTECT_NONE: + case PROTECT_NORMAL: + case PROTECT_MAX_GUARD: + case PROTECT_WIDE_GUARD: + case PROTECT_QUICK_GUARD: + case PROTECT_CRAFTY_SHIELD: + case PROTECT_MAT_BLOCK: + break; } + // Not strictly a protect effect, but works the same way - else if (gProtectStructs[gBattlerTarget].beakBlastCharge - && CanBeBurned(gBattlerAttacker, GetBattlerAbility(gBattlerAttacker)) - && !(gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_NO_EFFECT)) + if (gProtectStructs[gBattlerTarget].beakBlastCharge + && CanBeBurned(gBattlerAttacker, GetBattlerAbility(gBattlerAttacker)) + && !(gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_NO_EFFECT)) { gProtectStructs[gBattlerAttacker].touchedProtectLike = FALSE; gBattleMons[gBattlerAttacker].status1 = STATUS1_BURN; @@ -10817,7 +10840,7 @@ static void Cmd_various(void) case VARIOUS_SUCKER_PUNCH_CHECK: { VARIOUS_ARGS(const u8 *failInstr); - if (gProtectStructs[gBattlerTarget].obstructed) + if (gProtectStructs[gBattlerTarget].protected == PROTECT_OBSTRUCT) gBattlescriptCurrInstr = cmd->failInstr; else if (GetBattlerTurnOrderNum(gBattlerAttacker) > GetBattlerTurnOrderNum(gBattlerTarget)) gBattlescriptCurrInstr = cmd->failInstr; @@ -11840,102 +11863,40 @@ static void Cmd_setprotectlike(void) { CMD_ARGS(); - bool32 fail = TRUE; + bool32 protectFails = TRUE; bool32 notLastTurn = TRUE; + u32 protectMethod = GetMoveProtectMethod(gCurrentMove); TryResetProtectUseCounter(gBattlerAttacker); + if (gCurrentTurnActionNumber == (gBattlersCount - 1)) notLastTurn = FALSE; if ((sProtectSuccessRates[gDisableStructs[gBattlerAttacker].protectUses] >= Random() && notLastTurn) - || (gCurrentMove == MOVE_WIDE_GUARD && B_WIDE_GUARD != GEN_5) - || (gCurrentMove == MOVE_QUICK_GUARD && B_QUICK_GUARD != GEN_5)) + || (protectMethod == PROTECT_WIDE_GUARD && B_WIDE_GUARD != GEN_5) + || (protectMethod == PROTECT_QUICK_GUARD && B_QUICK_GUARD != GEN_5)) { - if (!GetMoveProtectSide(gCurrentMove)) // Protects one mon only. + if (GetMoveEffect(gCurrentMove) == EFFECT_ENDURE) { - if (GetMoveEffect(gCurrentMove) == EFFECT_ENDURE) - { - gProtectStructs[gBattlerAttacker].endured = TRUE; - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_BRACED_ITSELF; - } - else if (gCurrentMove == MOVE_DETECT || gCurrentMove == MOVE_PROTECT) - { - gProtectStructs[gBattlerAttacker].protected = TRUE; - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_PROTECTED_ITSELF; - } - else if (gCurrentMove == MOVE_SPIKY_SHIELD) - { - gProtectStructs[gBattlerAttacker].spikyShielded = TRUE; - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_PROTECTED_ITSELF; - } - else if (gCurrentMove == MOVE_KINGS_SHIELD) - { - gProtectStructs[gBattlerAttacker].kingsShielded = TRUE; - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_PROTECTED_ITSELF; - } - else if (gCurrentMove == MOVE_BANEFUL_BUNKER) - { - gProtectStructs[gBattlerAttacker].banefulBunkered = TRUE; - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_PROTECTED_ITSELF; - } - else if (gCurrentMove == MOVE_OBSTRUCT) - { - gProtectStructs[gBattlerAttacker].obstructed = TRUE; - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_PROTECTED_ITSELF; - } - else if (gCurrentMove == MOVE_MAX_GUARD) - { - gProtectStructs[gBattlerAttacker].maxGuarded = TRUE; - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_PROTECTED_ITSELF; - } - else if (gCurrentMove == MOVE_SILK_TRAP) - { - gProtectStructs[gBattlerAttacker].silkTrapped = TRUE; - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_PROTECTED_ITSELF; - } - else if (gCurrentMove == MOVE_BURNING_BULWARK) - { - gProtectStructs[gBattlerAttacker].burningBulwarked = TRUE; - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_PROTECTED_ITSELF; - } + gProtectStructs[gBattlerAttacker].endured = TRUE; + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_BRACED_ITSELF; + } + else if (GetProtectType(protectMethod) == PROTECT_TYPE_SIDE) + { + gProtectStructs[gBattlerAttacker].protected = protectMethod; + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_PROTECTED_TEAM; + } + else + { + gProtectStructs[gBattlerAttacker].protected = protectMethod; + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_PROTECTED_ITSELF; + } - gDisableStructs[gBattlerAttacker].protectUses++; - fail = FALSE; - } - else // Protects the whole side. - { - u8 side = GetBattlerSide(gBattlerAttacker); - if (gCurrentMove == MOVE_WIDE_GUARD && !(gSideStatuses[side] & SIDE_STATUS_WIDE_GUARD)) - { - gSideStatuses[side] |= SIDE_STATUS_WIDE_GUARD; - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_PROTECTED_TEAM; - gDisableStructs[gBattlerAttacker].protectUses++; - fail = FALSE; - } - else if (gCurrentMove == MOVE_QUICK_GUARD && !(gSideStatuses[side] & SIDE_STATUS_QUICK_GUARD)) - { - gSideStatuses[side] |= SIDE_STATUS_QUICK_GUARD; - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_PROTECTED_TEAM; - gDisableStructs[gBattlerAttacker].protectUses++; - fail = FALSE; - } - else if (gCurrentMove == MOVE_CRAFTY_SHIELD && !(gSideStatuses[side] & SIDE_STATUS_CRAFTY_SHIELD)) - { - gSideStatuses[side] |= SIDE_STATUS_CRAFTY_SHIELD; - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_PROTECTED_TEAM; - gDisableStructs[gBattlerAttacker].protectUses++; - fail = FALSE; - } - else if (gCurrentMove == MOVE_MAT_BLOCK && !(gSideStatuses[side] & SIDE_STATUS_MAT_BLOCK)) - { - gSideStatuses[side] |= SIDE_STATUS_MAT_BLOCK; - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_PROTECTED_TEAM; - fail = FALSE; - } - } + gDisableStructs[gBattlerAttacker].protectUses++; + protectFails = FALSE; } - if (fail) + if (protectFails) { gDisableStructs[gBattlerAttacker].protectUses = 0; gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_PROTECT_FAILED; diff --git a/src/battle_util.c b/src/battle_util.c index 2fbe8375df..69cd97d315 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -8752,45 +8752,55 @@ bool32 IsMoveMakingContact(u32 move, u32 battlerAtk) } } +static inline bool32 IsSideProtected(u32 battler, enum ProtectMethod method) +{ + return gProtectStructs[battler].protected == method + || gProtectStructs[BATTLE_PARTNER(battler)].protected == method; +} + bool32 IsBattlerProtected(u32 battlerAtk, u32 battlerDef, u32 move) { + if (gProtectStructs[battlerDef].protected == PROTECT_NONE + && gProtectStructs[BATTLE_PARTNER(battlerDef)].protected == PROTECT_NONE) + return FALSE; + + if (gProtectStructs[battlerDef].protected != PROTECT_MAX_GUARD && !MoveIgnoresProtect(move)) + { + if (IsZMove(move) || IsMaxMove(move)) + return FALSE; // Z-Moves and Max Moves bypass protection (except Max Guard). + if (IsMoveMakingContact(move, battlerAtk) && GetBattlerAbility(battlerAtk) == ABILITY_UNSEEN_FIST) + return FALSE; + } + bool32 isProtected = FALSE; - if ((IsZMove(move) || IsMaxMove(move)) - && (!gProtectStructs[battlerDef].maxGuarded || MoveIgnoresProtect(move))) - isProtected = FALSE; // Z-Moves and Max Moves bypass protection (except Max Guard). - else if (gProtectStructs[battlerDef].maxGuarded && IsMoveBlockedByMaxGuard(move)) - isProtected = TRUE; - else if (!gProtectStructs[battlerDef].maxGuarded // Max Guard cannot be bypassed by Unseen Fist - && IsMoveMakingContact(move, battlerAtk) - && GetBattlerAbility(battlerAtk) == ABILITY_UNSEEN_FIST) - isProtected = FALSE; - else if (gSideStatuses[GetBattlerSide(battlerDef)] & SIDE_STATUS_CRAFTY_SHIELD && IsBattleMoveStatus(move) && GetMoveEffect(move) != EFFECT_COACHING) + if (IsSideProtected(battlerDef, PROTECT_CRAFTY_SHIELD) + && IsBattleMoveStatus(move) + && GetMoveEffect(move) != EFFECT_COACHING) isProtected = TRUE; else if (MoveIgnoresProtect(move)) isProtected = FALSE; - else if (gProtectStructs[battlerDef].protected) + else if (IsSideProtected(battlerDef, PROTECT_WIDE_GUARD) && IsSpreadMove(GetBattlerMoveTargetType(battlerAtk, move))) isProtected = TRUE; - else if (gSideStatuses[GetBattlerSide(battlerDef)] & SIDE_STATUS_WIDE_GUARD - && GetBattlerMoveTargetType(battlerAtk, move) & (MOVE_TARGET_BOTH | MOVE_TARGET_FOES_AND_ALLY)) + else if (gProtectStructs[battlerDef].protected == PROTECT_NORMAL) isProtected = TRUE; - else if (gProtectStructs[battlerDef].banefulBunkered) + else if (gProtectStructs[battlerDef].protected == PROTECT_SPIKY_SHIELD) isProtected = TRUE; - else if (gProtectStructs[battlerDef].burningBulwarked) + else if (gProtectStructs[battlerDef].protected == PROTECT_MAX_GUARD) isProtected = TRUE; - else if ((gProtectStructs[battlerDef].obstructed || gProtectStructs[battlerDef].silkTrapped) && !IsBattleMoveStatus(move)) + else if (gProtectStructs[battlerDef].protected == PROTECT_BANEFUL_BUNKER) isProtected = TRUE; - else if (gProtectStructs[battlerDef].spikyShielded) + else if (gProtectStructs[battlerDef].protected == PROTECT_BURNING_BULWARK) isProtected = TRUE; - else if (gProtectStructs[battlerDef].kingsShielded && !IsBattleMoveStatus(move)) + else if (gProtectStructs[battlerDef].protected == PROTECT_OBSTRUCT && !IsBattleMoveStatus(move)) isProtected = TRUE; - else if (gProtectStructs[battlerDef].maxGuarded) + else if (gProtectStructs[battlerDef].protected == PROTECT_SILK_TRAP && !IsBattleMoveStatus(move)) isProtected = TRUE; - else if (gSideStatuses[GetBattlerSide(battlerDef)] & SIDE_STATUS_QUICK_GUARD - && GetChosenMovePriority(battlerAtk) > 0) + else if (gProtectStructs[battlerDef].protected == PROTECT_KINGS_SHIELD && !IsBattleMoveStatus(move)) isProtected = TRUE; - else if (gSideStatuses[GetBattlerSide(battlerDef)] & SIDE_STATUS_MAT_BLOCK - && !IsBattleMoveStatus(move)) + else if (IsSideProtected(battlerDef, PROTECT_QUICK_GUARD) && GetChosenMovePriority(battlerAtk) > 0) + isProtected = TRUE; + else if (IsSideProtected(battlerDef, PROTECT_MAT_BLOCK) && !IsBattleMoveStatus(move)) isProtected = TRUE; else isProtected = FALSE; @@ -8801,6 +8811,31 @@ bool32 IsBattlerProtected(u32 battlerAtk, u32 battlerDef, u32 move) return isProtected; } +u32 GetProtectType(enum ProtectMethod method) +{ + switch (method) + { + case PROTECT_NONE: + return PROTECT_TYPE_NONE; + case PROTECT_NORMAL: + case PROTECT_SPIKY_SHIELD: + case PROTECT_KINGS_SHIELD: + case PROTECT_BANEFUL_BUNKER: + case PROTECT_BURNING_BULWARK: + case PROTECT_OBSTRUCT: + case PROTECT_SILK_TRAP: + case PROTECT_MAX_GUARD: + return PROTECT_TYPE_SINGLE; + case PROTECT_WIDE_GUARD: + case PROTECT_QUICK_GUARD: + case PROTECT_CRAFTY_SHIELD: + case PROTECT_MAT_BLOCK: + return PROTECT_TYPE_SIDE; + } + + return FALSE; +} + // Only called directly when calculating damage type effectiveness static bool32 IsBattlerGroundedInverseCheck(u32 battler, bool32 considerInverse) { @@ -10173,7 +10208,9 @@ static inline uq4_12_t GetGlaiveRushModifier(u32 battlerDef) static inline uq4_12_t GetZMaxMoveAgainstProtectionModifier(struct DamageCalculationData *damageCalcData) { - if ((IsZMove(damageCalcData->move) || IsMaxMove(damageCalcData->move)) && IS_BATTLER_PROTECTED(damageCalcData->battlerDef)) + if ((IsZMove(damageCalcData->move) || IsMaxMove(damageCalcData->move)) + && gProtectStructs[damageCalcData->battlerDef].protected != PROTECT_NONE + && gProtectStructs[damageCalcData->battlerDef].protected != PROTECT_MAX_GUARD) return UQ_4_12(0.25); return UQ_4_12(1.0); } diff --git a/src/data/moves_info.h b/src/data/moves_info.h index b34aad70d4..fee2dcccc4 100644 --- a/src/data/moves_info.h +++ b/src/data/moves_info.h @@ -4789,6 +4789,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] = .target = MOVE_TARGET_USER, .priority = B_UPDATED_MOVE_DATA >= GEN_5 ? 4 : 3, .category = DAMAGE_CATEGORY_STATUS, + .argument = { .protectMethod = PROTECT_NORMAL }, .zMove = { .effect = Z_EFFECT_RESET_STATS }, .metronomeBanned = TRUE, .copycatBanned = TRUE, @@ -5179,6 +5180,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] = .target = MOVE_TARGET_USER, .priority = B_UPDATED_MOVE_DATA >= GEN_5 ? 4 : 3, .category = DAMAGE_CATEGORY_STATUS, + .argument = { .protectMethod = PROTECT_NORMAL }, .zMove = { .effect = Z_EFFECT_EVSN_UP_1 }, .ignoresProtect = TRUE, .mirrorMoveBanned = TRUE, @@ -12049,7 +12051,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] = .target = MOVE_TARGET_USER, .priority = 3, .category = DAMAGE_CATEGORY_STATUS, - .argument = { .protect.side = TRUE, }, + .argument = { .protectMethod = PROTECT_WIDE_GUARD }, .zMove = { .effect = Z_EFFECT_DEF_UP_1 }, .snatchAffected = TRUE, .ignoresProtect = TRUE, @@ -12810,7 +12812,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] = .target = MOVE_TARGET_USER, .priority = 3, .category = DAMAGE_CATEGORY_STATUS, - .argument = { .protect.side = TRUE, }, + .argument = { .protectMethod = PROTECT_QUICK_GUARD, }, .zMove = { .effect = Z_EFFECT_DEF_UP_1 }, .snatchAffected = TRUE, .ignoresProtect = TRUE, @@ -14268,7 +14270,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] = .target = MOVE_TARGET_USER, .priority = 0, .category = DAMAGE_CATEGORY_STATUS, - .argument = { .protect.side = TRUE }, + .argument = { .protectMethod = PROTECT_MAT_BLOCK, }, .zMove = { .effect = Z_EFFECT_DEF_UP_1 }, .snatchAffected = TRUE, .ignoresProtect = TRUE, @@ -14694,7 +14696,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] = .target = MOVE_TARGET_USER, .priority = 3, .category = DAMAGE_CATEGORY_STATUS, - .argument = { .protect.side = TRUE, }, + .argument = { .protectMethod = PROTECT_CRAFTY_SHIELD }, .zMove = { .effect = Z_EFFECT_SPDEF_UP_1 }, .ignoresProtect = TRUE, .mirrorMoveBanned = TRUE, @@ -14936,6 +14938,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] = .target = MOVE_TARGET_USER, .priority = 4, .category = DAMAGE_CATEGORY_STATUS, + .argument = { .protectMethod = PROTECT_KINGS_SHIELD }, .zMove = { .effect = Z_EFFECT_RESET_STATS }, .ignoresProtect = TRUE, .mirrorMoveBanned = TRUE, @@ -15140,6 +15143,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] = .target = MOVE_TARGET_USER, .priority = 4, .category = DAMAGE_CATEGORY_STATUS, + .argument = { .protectMethod = PROTECT_SPIKY_SHIELD }, .zMove = { .effect = Z_EFFECT_DEF_UP_1 }, .ignoresProtect = TRUE, .mirrorMoveBanned = TRUE, @@ -15829,6 +15833,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] = .target = MOVE_TARGET_USER, .priority = 4, .category = DAMAGE_CATEGORY_STATUS, + .argument = { .protectMethod = PROTECT_BANEFUL_BUNKER }, .zMove = { .effect = Z_EFFECT_DEF_UP_1 }, .ignoresProtect = TRUE, .mirrorMoveBanned = TRUE, @@ -18211,6 +18216,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] = .target = MOVE_TARGET_USER, .priority = 4, .category = DAMAGE_CATEGORY_STATUS, + .argument = { .protectMethod = PROTECT_OBSTRUCT }, .ignoresProtect = TRUE, .mirrorMoveBanned = TRUE, .metronomeBanned = TRUE, @@ -19598,6 +19604,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] = .target = MOVE_TARGET_USER, .priority = 4, .category = DAMAGE_CATEGORY_STATUS, + .argument = { .protectMethod = PROTECT_SILK_TRAP }, .ignoresProtect = TRUE, .mirrorMoveBanned = TRUE, .metronomeBanned = TRUE, @@ -20814,6 +20821,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] = .target = MOVE_TARGET_USER, .priority = 4, .category = DAMAGE_CATEGORY_STATUS, + .argument = { .protectMethod = PROTECT_BURNING_BULWARK }, .zMove = { .effect = Z_EFFECT_RESET_STATS }, .ignoresProtect = TRUE, .mirrorMoveBanned = TRUE, @@ -21559,6 +21567,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] = .target = MOVE_TARGET_USER, .priority = 4, .category = DAMAGE_CATEGORY_STATUS, + .argument = { .protectMethod = PROTECT_MAX_GUARD }, .battleAnimScript = gBattleAnimMove_MaxGuard, },