Refactor protect to allow at least 126 different types of protect eff… (#6506)

This commit is contained in:
Alex 2025-04-02 17:26:05 +02:00 committed by GitHub
parent ae62c80d0f
commit c1f18d0765
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 252 additions and 256 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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