Fixes Shell Side Arm (#4753)
* Fixes Shell Side Arm * Fixes to random call * hardcoded the effect to the move * minor change * minor change 2 * applied reviews
This commit is contained in:
parent
5ebdcdc9b0
commit
9c72392891
15 changed files with 221 additions and 120 deletions
|
@ -1637,11 +1637,11 @@
|
|||
.macro trygulpmissile
|
||||
callnative BS_TryGulpMissile
|
||||
.endm
|
||||
|
||||
|
||||
.macro tryactivategulpmissile
|
||||
callnative BS_TryActivateGulpMissile
|
||||
.endm
|
||||
|
||||
|
||||
.macro tryquash failInstr:req
|
||||
callnative BS_TryQuash
|
||||
.4byte \failInstr
|
||||
|
@ -2211,10 +2211,6 @@
|
|||
.4byte \failInstr
|
||||
.endm
|
||||
|
||||
.macro shellsidearmcheck
|
||||
various BS_ATTACKER, VARIOUS_SHELL_SIDE_ARM_CHECK
|
||||
.endm
|
||||
|
||||
.macro jumpifteanoberry jumpInstr:req
|
||||
various BS_ATTACKER, VARIOUS_TEATIME_TARGETS
|
||||
.4byte \jumpInstr
|
||||
|
|
|
@ -719,10 +719,6 @@ BattleScript_FlingMissed:
|
|||
ppreduce
|
||||
goto BattleScript_MoveMissedPause
|
||||
|
||||
BattleScript_EffectShellSideArm::
|
||||
shellsidearmcheck
|
||||
goto BattleScript_EffectHit
|
||||
|
||||
BattleScript_EffectPhotonGeyser::
|
||||
setphotongeysercategory
|
||||
goto BattleScript_EffectHit
|
||||
|
|
|
@ -779,6 +779,7 @@ struct BattleStruct
|
|||
u8 supremeOverlordCounter[MAX_BATTLERS_COUNT];
|
||||
u8 quickClawRandom[MAX_BATTLERS_COUNT];
|
||||
u8 quickDrawRandom[MAX_BATTLERS_COUNT];
|
||||
u8 shellSideArmCategory[MAX_BATTLERS_COUNT][MAX_BATTLERS_COUNT];
|
||||
};
|
||||
|
||||
// The palaceFlags member of struct BattleStruct contains 1 flag per move to indicate which moves the AI should consider,
|
||||
|
|
|
@ -796,7 +796,6 @@ extern const u8 BattleScript_EffectPlasmaFists[];
|
|||
extern const u8 BattleScript_EffectHyperspaceFury[];
|
||||
extern const u8 BattleScript_EffectAuraWheel[];
|
||||
extern const u8 BattleScript_EffectPhotonGeyser[];
|
||||
extern const u8 BattleScript_EffectShellSideArm[];
|
||||
extern const u8 BattleScript_EffectNoRetreat[];
|
||||
extern const u8 BattleScript_EffectTarShot[];
|
||||
extern const u8 BattleScript_EffectPoltergeist[];
|
||||
|
|
|
@ -155,7 +155,7 @@ u32 IsAbilityOnOpposingSide(u32 battler, u32 ability);
|
|||
u32 IsAbilityOnField(u32 ability);
|
||||
u32 IsAbilityOnFieldExcept(u32 battler, u32 ability);
|
||||
u32 IsAbilityPreventingEscape(u32 battler);
|
||||
bool32 IsBattlerProtected(u32 battler, u32 move);
|
||||
bool32 IsBattlerProtected(u32 battlerAtk, u32 battlerDef, u32 move);
|
||||
bool32 CanBattlerEscape(u32 battler); // no ability check
|
||||
void BattleScriptExecute(const u8 *BS_ptr);
|
||||
void BattleScriptPushCursorAndCallback(const u8 *BS_ptr);
|
||||
|
@ -206,6 +206,7 @@ bool32 IsBelchPreventingMove(u32 battler, u32 move);
|
|||
bool32 HasEnoughHpToEatBerry(u32 battler, u32 hpFraction, u32 itemId);
|
||||
bool32 IsPartnerMonFromSameTrainer(u32 battler);
|
||||
u8 GetCategoryBasedOnStats(u32 battler);
|
||||
void SetShellSideArmCategory(void);
|
||||
bool32 MoveIsAffectedBySheerForce(u32 move);
|
||||
bool32 TestIfSheerForceAffected(u32 battler, u16 move);
|
||||
void TryRestoreHeldItems(void);
|
||||
|
|
|
@ -302,7 +302,6 @@ enum {
|
|||
EFFECT_HYPERSPACE_FURY,
|
||||
EFFECT_AURA_WHEEL,
|
||||
EFFECT_PHOTON_GEYSER,
|
||||
EFFECT_SHELL_SIDE_ARM,
|
||||
EFFECT_TERRAIN_PULSE,
|
||||
EFFECT_NO_RETREAT,
|
||||
EFFECT_TAR_SHOT,
|
||||
|
|
|
@ -208,35 +208,34 @@
|
|||
#define VARIOUS_JUMP_IF_WEATHER_AFFECTED 116
|
||||
#define VARIOUS_JUMP_IF_LEAF_GUARD_PROTECTED 117
|
||||
#define VARIOUS_SET_ATTACKER_STICKY_WEB_USER 118
|
||||
#define VARIOUS_SHELL_SIDE_ARM_CHECK 119
|
||||
#define VARIOUS_TRY_NO_RETREAT 120
|
||||
#define VARIOUS_TRY_TAR_SHOT 121
|
||||
#define VARIOUS_CAN_TAR_SHOT_WORK 122
|
||||
#define VARIOUS_CHECK_POLTERGEIST 123
|
||||
#define VARIOUS_CUT_1_3_HP_RAISE_STATS 124
|
||||
#define VARIOUS_TRY_END_NEUTRALIZING_GAS 125
|
||||
#define VARIOUS_JUMP_IF_UNDER_200 126
|
||||
#define VARIOUS_SET_SKY_DROP 127
|
||||
#define VARIOUS_CLEAR_SKY_DROP 128
|
||||
#define VARIOUS_SKY_DROP_YAWN 129
|
||||
#define VARIOUS_CURE_CERTAIN_STATUSES 130
|
||||
#define VARIOUS_TRY_RESET_NEGATIVE_STAT_STAGES 131
|
||||
#define VARIOUS_JUMP_IF_LAST_USED_ITEM_BERRY 132
|
||||
#define VARIOUS_JUMP_IF_LAST_USED_ITEM_HOLD_EFFECT 133
|
||||
#define VARIOUS_SAVE_BATTLER_ITEM 134
|
||||
#define VARIOUS_RESTORE_BATTLER_ITEM 135
|
||||
#define VARIOUS_BATTLER_ITEM_TO_LAST_USED_ITEM 136
|
||||
#define VARIOUS_SET_BEAK_BLAST 137
|
||||
#define VARIOUS_SWAP_SIDE_STATUSES 138
|
||||
#define VARIOUS_SWAP_STATS 139
|
||||
#define VARIOUS_TEATIME_INVUL 140
|
||||
#define VARIOUS_TEATIME_TARGETS 141
|
||||
#define VARIOUS_TRY_WIND_RIDER_POWER 142
|
||||
#define VARIOUS_ACTIVATE_WEATHER_CHANGE_ABILITIES 143
|
||||
#define VARIOUS_ACTIVATE_TERRAIN_CHANGE_ABILITIES 144
|
||||
#define VARIOUS_STORE_HEALING_WISH 145
|
||||
#define VARIOUS_HIT_SWITCH_TARGET_FAILED 146
|
||||
#define VARIOUS_TRY_REVIVAL_BLESSING 147
|
||||
#define VARIOUS_TRY_NO_RETREAT 119
|
||||
#define VARIOUS_TRY_TAR_SHOT 120
|
||||
#define VARIOUS_CAN_TAR_SHOT_WORK 121
|
||||
#define VARIOUS_CHECK_POLTERGEIST 122
|
||||
#define VARIOUS_CUT_1_3_HP_RAISE_STATS 123
|
||||
#define VARIOUS_TRY_END_NEUTRALIZING_GAS 124
|
||||
#define VARIOUS_JUMP_IF_UNDER_200 125
|
||||
#define VARIOUS_SET_SKY_DROP 126
|
||||
#define VARIOUS_CLEAR_SKY_DROP 127
|
||||
#define VARIOUS_SKY_DROP_YAWN 128
|
||||
#define VARIOUS_CURE_CERTAIN_STATUSES 129
|
||||
#define VARIOUS_TRY_RESET_NEGATIVE_STAT_STAGES 130
|
||||
#define VARIOUS_JUMP_IF_LAST_USED_ITEM_BERRY 131
|
||||
#define VARIOUS_JUMP_IF_LAST_USED_ITEM_HOLD_EFFECT 132
|
||||
#define VARIOUS_SAVE_BATTLER_ITEM 133
|
||||
#define VARIOUS_RESTORE_BATTLER_ITEM 134
|
||||
#define VARIOUS_BATTLER_ITEM_TO_LAST_USED_ITEM 135
|
||||
#define VARIOUS_SET_BEAK_BLAST 136
|
||||
#define VARIOUS_SWAP_SIDE_STATUSES 137
|
||||
#define VARIOUS_SWAP_STATS 138
|
||||
#define VARIOUS_TEATIME_INVUL 139
|
||||
#define VARIOUS_TEATIME_TARGETS 140
|
||||
#define VARIOUS_TRY_WIND_RIDER_POWER 141
|
||||
#define VARIOUS_ACTIVATE_WEATHER_CHANGE_ABILITIES 142
|
||||
#define VARIOUS_ACTIVATE_TERRAIN_CHANGE_ABILITIES 143
|
||||
#define VARIOUS_STORE_HEALING_WISH 144
|
||||
#define VARIOUS_HIT_SWITCH_TARGET_FAILED 145
|
||||
#define VARIOUS_TRY_REVIVAL_BLESSING 146
|
||||
|
||||
// Cmd_manipulatedamage
|
||||
#define DMG_CHANGE_SIGN 0
|
||||
|
|
|
@ -189,6 +189,7 @@ enum RandomTag
|
|||
RNG_TRACE,
|
||||
RNG_FICKLE_BEAM,
|
||||
RNG_AI_ABILITY,
|
||||
RNG_SHELL_SIDE_ARM,
|
||||
};
|
||||
|
||||
#define RandomWeighted(tag, ...) \
|
||||
|
|
|
@ -477,8 +477,9 @@ s32 AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u8 *typeEffectivenes
|
|||
}
|
||||
else if (gMovesInfo[move].effect == EFFECT_PHOTON_GEYSER)
|
||||
gBattleStruct->swapDamageCategory = (GetCategoryBasedOnStats(gBattlerAttacker) == DAMAGE_CATEGORY_PHYSICAL);
|
||||
|
||||
if (gMovesInfo[move].effect == EFFECT_NATURE_POWER)
|
||||
else if (move == MOVE_SHELL_SIDE_ARM && gBattleStruct->shellSideArmCategory[battlerAtk][battlerDef] == DAMAGE_CATEGORY_SPECIAL)
|
||||
gBattleStruct->swapDamageCategory = TRUE;
|
||||
else if (gMovesInfo[move].effect == EFFECT_NATURE_POWER)
|
||||
move = GetNaturePowerMove();
|
||||
|
||||
gBattleStruct->dynamicMoveType = 0;
|
||||
|
|
|
@ -3677,8 +3677,8 @@ const u8* FaintClearSetData(u32 battler)
|
|||
gBattleStruct->zmove.toBeUsed[battler] = MOVE_NONE;
|
||||
gBattleStruct->zmove.effect = EFFECT_HIT;
|
||||
// Clear Dynamax data
|
||||
UndoDynamax(battler);
|
||||
|
||||
UndoDynamax(battler);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -4162,6 +4162,7 @@ static void TryDoEventsBeforeFirstTurn(void)
|
|||
|
||||
memset(gQueuedStatBoosts, 0, sizeof(gQueuedStatBoosts)); // erase all totem boosts just to be safe
|
||||
|
||||
SetShellSideArmCategory();
|
||||
SetAiLogicDataForTurn(AI_DATA); // get assumed abilities, hold effects, etc of all battlers
|
||||
|
||||
if (gBattleTypeFlags & BATTLE_TYPE_ARENA)
|
||||
|
@ -4254,6 +4255,7 @@ void BattleTurnPassed(void)
|
|||
|
||||
*(&gBattleStruct->absentBattlerFlags) = gAbsentBattlerFlags;
|
||||
BattlePutTextOnWindow(gText_EmptyString3, B_WIN_MSG);
|
||||
SetShellSideArmCategory();
|
||||
SetAiLogicDataForTurn(AI_DATA); // get assumed abilities, hold effects, etc of all battlers
|
||||
gBattleMainFunc = HandleTurnActionSelectionState;
|
||||
|
||||
|
@ -5835,7 +5837,7 @@ static void TryEvolvePokemon(void)
|
|||
sTriedEvolving |= gBitTable[i];
|
||||
|
||||
if (species == SPECIES_NONE && (gLeveledUpInBattle & gBitTable[i]))
|
||||
{
|
||||
{
|
||||
gLeveledUpInBattle &= ~(gBitTable[i]);
|
||||
species = GetEvolutionTargetSpecies(&gPlayerParty[i], EVO_MODE_BATTLE_ONLY, gLeveledUpInBattle, NULL);
|
||||
}
|
||||
|
|
|
@ -1479,7 +1479,7 @@ static void Cmd_attackcanceler(void)
|
|||
gBattlescriptCurrInstr = BattleScript_TookAttack;
|
||||
RecordAbilityBattle(gBattlerTarget, gLastUsedAbility);
|
||||
}
|
||||
else if (IsBattlerProtected(gBattlerTarget, gCurrentMove)
|
||||
else if (IsBattlerProtected(gBattlerAttacker, gBattlerTarget, gCurrentMove)
|
||||
&& (gCurrentMove != MOVE_CURSE || IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_GHOST))
|
||||
&& (!gBattleMoveEffects[gMovesInfo[gCurrentMove].effect].twoTurnEffect || (gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS))
|
||||
&& gMovesInfo[gCurrentMove].effect != EFFECT_SUCKER_PUNCH
|
||||
|
@ -1535,7 +1535,7 @@ static void Cmd_unused5(void)
|
|||
{
|
||||
CMD_ARGS(const u8 *failInstr);
|
||||
|
||||
if (IsBattlerProtected(gBattlerTarget, gCurrentMove))
|
||||
if (IsBattlerProtected(gBattlerAttacker, gBattlerTarget, gCurrentMove))
|
||||
{
|
||||
gMoveResultFlags |= MOVE_RESULT_MISSED;
|
||||
JumpIfMoveFailed(sizeof(*cmd), MOVE_NONE);
|
||||
|
@ -1550,7 +1550,7 @@ static void Cmd_unused5(void)
|
|||
static bool8 JumpIfMoveAffectedByProtect(u16 move)
|
||||
{
|
||||
bool8 affected = FALSE;
|
||||
if (IsBattlerProtected(gBattlerTarget, move))
|
||||
if (IsBattlerProtected(gBattlerAttacker, gBattlerTarget, move))
|
||||
{
|
||||
gMoveResultFlags |= MOVE_RESULT_MISSED;
|
||||
JumpIfMoveFailed(7, move);
|
||||
|
@ -2009,6 +2009,8 @@ static void Cmd_damagecalc(void)
|
|||
u8 moveType;
|
||||
|
||||
GET_MOVE_TYPE(gCurrentMove, moveType);
|
||||
if (gBattleStruct->shellSideArmCategory[gBattlerAttacker][gBattlerTarget] == DAMAGE_CATEGORY_SPECIAL && gCurrentMove == MOVE_SHELL_SIDE_ARM)
|
||||
gBattleStruct->swapDamageCategory = TRUE;
|
||||
gBattleMoveDamage = CalculateMoveDamage(gCurrentMove, gBattlerAttacker, gBattlerTarget, moveType, 0, gIsCriticalHit, TRUE, TRUE);
|
||||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||||
}
|
||||
|
@ -2106,7 +2108,7 @@ static void Cmd_adjustdamage(void)
|
|||
gLastUsedItem = gBattleMons[gBattlerTarget].item;
|
||||
gSpecialStatuses[gBattlerTarget].focusBanded = FALSE;
|
||||
gSpecialStatuses[gBattlerTarget].focusSashed = FALSE;
|
||||
|
||||
|
||||
}
|
||||
else if (gSpecialStatuses[gBattlerTarget].sturdied)
|
||||
{
|
||||
|
@ -3225,8 +3227,8 @@ void SetMoveEffect(bool32 primary, bool32 certain)
|
|||
{
|
||||
gBattleMons[gEffectBattler].status2 |= sStatusFlagsForMoveEffects[gBattleScripting.moveEffect];
|
||||
gBattlescriptCurrInstr++;
|
||||
}
|
||||
else
|
||||
}
|
||||
else
|
||||
{
|
||||
gBattlescriptCurrInstr++;
|
||||
}
|
||||
|
@ -6285,7 +6287,7 @@ static void Cmd_moveend(void)
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!(gBattleStruct->lastMoveFailed & gBitTable[gBattlerAttacker]
|
||||
|| (!gSpecialStatuses[gBattlerAttacker].dancerUsedMove
|
||||
&& gBattleStruct->bouncedMoveIsUsed)))
|
||||
|
@ -10487,44 +10489,6 @@ static void Cmd_various(void)
|
|||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||||
return;
|
||||
}
|
||||
case VARIOUS_SHELL_SIDE_ARM_CHECK: // 0% chance GameFreak actually checks this way according to DaWobblefet, but this is the only functional explanation at the moment
|
||||
{
|
||||
VARIOUS_ARGS();
|
||||
|
||||
u32 attackerAtkStat = gBattleMons[gBattlerAttacker].attack;
|
||||
u32 targetDefStat = gBattleMons[gBattlerTarget].defense;
|
||||
u32 attackerSpAtkStat = gBattleMons[gBattlerAttacker].spAttack;
|
||||
u32 targetSpDefStat = gBattleMons[gBattlerTarget].spDefense;
|
||||
u8 statStage;
|
||||
u32 physical;
|
||||
u32 special;
|
||||
|
||||
gBattleStruct->swapDamageCategory = FALSE;
|
||||
|
||||
statStage = gBattleMons[gBattlerAttacker].statStages[STAT_ATK];
|
||||
attackerAtkStat *= gStatStageRatios[statStage][0];
|
||||
attackerAtkStat /= gStatStageRatios[statStage][1];
|
||||
|
||||
statStage = gBattleMons[gBattlerTarget].statStages[STAT_DEF];
|
||||
targetDefStat *= gStatStageRatios[statStage][0];
|
||||
targetDefStat /= gStatStageRatios[statStage][1];
|
||||
|
||||
physical = ((((2 * gBattleMons[gBattlerAttacker].level / 5 + 2) * gMovesInfo[gCurrentMove].power * attackerAtkStat) / targetDefStat) / 50);
|
||||
|
||||
statStage = gBattleMons[gBattlerAttacker].statStages[STAT_SPATK];
|
||||
attackerSpAtkStat *= gStatStageRatios[statStage][0];
|
||||
attackerSpAtkStat /= gStatStageRatios[statStage][1];
|
||||
|
||||
statStage = gBattleMons[gBattlerTarget].statStages[STAT_SPDEF];
|
||||
targetSpDefStat *= gStatStageRatios[statStage][0];
|
||||
targetSpDefStat /= gStatStageRatios[statStage][1];
|
||||
|
||||
special = ((((2 * gBattleMons[gBattlerAttacker].level / 5 + 2) * gMovesInfo[gCurrentMove].power * attackerSpAtkStat) / targetSpDefStat) / 50);
|
||||
|
||||
if (((physical > special) || (physical == special && (Random() % 2) == 0)))
|
||||
gBattleStruct->swapDamageCategory = TRUE;
|
||||
break;
|
||||
}
|
||||
case VARIOUS_JUMP_IF_LEAF_GUARD_PROTECTED:
|
||||
{
|
||||
VARIOUS_ARGS(const u8 *jumpInstr);
|
||||
|
@ -13903,7 +13867,7 @@ static void Cmd_trymemento(void)
|
|||
if (B_MEMENTO_FAIL >= GEN_4
|
||||
&& (gBattleCommunication[MISS_TYPE] == B_MSG_PROTECTED
|
||||
|| gStatuses3[gBattlerTarget] & STATUS3_SEMI_INVULNERABLE
|
||||
|| IsBattlerProtected(gBattlerTarget, gCurrentMove)
|
||||
|| IsBattlerProtected(gBattlerAttacker, gBattlerTarget, gCurrentMove)
|
||||
|| DoesSubstituteBlockMove(gBattlerAttacker, gBattlerTarget, gCurrentMove)))
|
||||
{
|
||||
// Failed, target was protected.
|
||||
|
|
|
@ -8264,7 +8264,7 @@ bool32 IsMoveMakingContact(u32 move, u32 battlerAtk)
|
|||
|
||||
if (!gMovesInfo[move].makesContact)
|
||||
{
|
||||
if (gMovesInfo[move].effect == EFFECT_SHELL_SIDE_ARM && gBattleStruct->swapDamageCategory)
|
||||
if (move == MOVE_SHELL_SIDE_ARM && gBattleStruct->shellSideArmCategory[battlerAtk][gBattlerTarget] == DAMAGE_CATEGORY_SPECIAL)
|
||||
return TRUE;
|
||||
else
|
||||
return FALSE;
|
||||
|
@ -8280,59 +8280,59 @@ bool32 IsMoveMakingContact(u32 move, u32 battlerAtk)
|
|||
}
|
||||
}
|
||||
|
||||
bool32 IsBattlerProtected(u32 battler, u32 move)
|
||||
bool32 IsBattlerProtected(u32 battlerAtk, u32 battlerDef, u32 move)
|
||||
{
|
||||
// Decorate bypasses protect and detect, but not crafty shield
|
||||
if (move == MOVE_DECORATE)
|
||||
{
|
||||
if (gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_CRAFTY_SHIELD)
|
||||
if (gSideStatuses[GetBattlerSide(battlerDef)] & SIDE_STATUS_CRAFTY_SHIELD)
|
||||
return TRUE;
|
||||
else if (gProtectStructs[battler].protected)
|
||||
else if (gProtectStructs[battlerDef].protected)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Z-Moves and Max Moves bypass protection (except Max Guard).
|
||||
if ((IsMaxMove(move) || gBattleStruct->zmove.active)
|
||||
&& (!gProtectStructs[battler].maxGuarded
|
||||
&& (!gProtectStructs[battlerDef].maxGuarded
|
||||
|| gMovesInfo[move].argument == MAX_EFFECT_BYPASS_PROTECT))
|
||||
return FALSE;
|
||||
|
||||
// Max Guard is silly about the moves it blocks, including Teatime.
|
||||
if (gProtectStructs[battler].maxGuarded && IsMoveBlockedByMaxGuard(move))
|
||||
if (gProtectStructs[battlerDef].maxGuarded && IsMoveBlockedByMaxGuard(move))
|
||||
return TRUE;
|
||||
|
||||
// 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
|
||||
&& (gMovesInfo[move].makesContact || (gMovesInfo[move].effect == EFFECT_SHELL_SIDE_ARM && gBattleStruct->swapDamageCategory))
|
||||
&& !gProtectStructs[battler].maxGuarded) // Max Guard cannot be bypassed by Unseen Fist
|
||||
&& (gMovesInfo[move].makesContact || (move == MOVE_SHELL_SIDE_ARM && gBattleStruct->shellSideArmCategory[battlerAtk][battlerDef] == DAMAGE_CATEGORY_SPECIAL))
|
||||
&& !gProtectStructs[battlerDef].maxGuarded) // Max Guard cannot be bypassed by Unseen Fist
|
||||
return FALSE;
|
||||
else if (gMovesInfo[move].ignoresProtect)
|
||||
return FALSE;
|
||||
else if (gProtectStructs[battler].protected)
|
||||
else if (gProtectStructs[battlerDef].protected)
|
||||
return TRUE;
|
||||
else if (gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_WIDE_GUARD
|
||||
&& GetBattlerMoveTargetType(gBattlerAttacker, move) & (MOVE_TARGET_BOTH | MOVE_TARGET_FOES_AND_ALLY))
|
||||
else if (gSideStatuses[GetBattlerSide(battlerDef)] & SIDE_STATUS_WIDE_GUARD
|
||||
&& GetBattlerMoveTargetType(gBattlerAttacker, move) & (MOVE_TARGET_BOTH | MOVE_TARGET_FOES_AND_ALLY))
|
||||
return TRUE;
|
||||
else if (gProtectStructs[battler].banefulBunkered)
|
||||
else if (gProtectStructs[battlerDef].banefulBunkered)
|
||||
return TRUE;
|
||||
else if (gProtectStructs[battler].burningBulwarked)
|
||||
else if (gProtectStructs[battlerDef].burningBulwarked)
|
||||
return TRUE;
|
||||
else if ((gProtectStructs[battler].obstructed || gProtectStructs[battler].silkTrapped) && !IS_MOVE_STATUS(move))
|
||||
else if ((gProtectStructs[battlerDef].obstructed || gProtectStructs[battlerDef].silkTrapped) && !IS_MOVE_STATUS(move))
|
||||
return TRUE;
|
||||
else if (gProtectStructs[battler].spikyShielded)
|
||||
else if (gProtectStructs[battlerDef].spikyShielded)
|
||||
return TRUE;
|
||||
else if (gProtectStructs[battler].kingsShielded && gMovesInfo[move].power != 0)
|
||||
else if (gProtectStructs[battlerDef].kingsShielded && gMovesInfo[move].power != 0)
|
||||
return TRUE;
|
||||
else if (gProtectStructs[battler].maxGuarded)
|
||||
else if (gProtectStructs[battlerDef].maxGuarded)
|
||||
return TRUE;
|
||||
else if (gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_QUICK_GUARD
|
||||
else if (gSideStatuses[GetBattlerSide(battlerDef)] & SIDE_STATUS_QUICK_GUARD
|
||||
&& GetChosenMovePriority(gBattlerAttacker) > 0)
|
||||
return TRUE;
|
||||
else if (gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_CRAFTY_SHIELD
|
||||
else if (gSideStatuses[GetBattlerSide(battlerDef)] & SIDE_STATUS_CRAFTY_SHIELD
|
||||
&& IS_MOVE_STATUS(move))
|
||||
return TRUE;
|
||||
else if (gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_MAT_BLOCK
|
||||
else if (gSideStatuses[GetBattlerSide(battlerDef)] & SIDE_STATUS_MAT_BLOCK
|
||||
&& !IS_MOVE_STATUS(move))
|
||||
return TRUE;
|
||||
else
|
||||
|
@ -11382,3 +11382,51 @@ void RemoveBattlerType(u32 battler, u8 type)
|
|||
*(u8 *)(&gBattleMons[battler].type1 + i) = TYPE_MYSTERY;
|
||||
}
|
||||
}
|
||||
|
||||
void SetShellSideArmCategory(void)
|
||||
{
|
||||
u32 battlerAtk, battlerDef;
|
||||
u32 attackerAtkStat;
|
||||
u32 targetDefStat;
|
||||
u32 attackerSpAtkStat;
|
||||
u32 targetSpDefStat;
|
||||
u8 statStage;
|
||||
u32 physical;
|
||||
u32 special;
|
||||
|
||||
for (battlerAtk = 0; battlerAtk < gBattlersCount; battlerAtk++)
|
||||
{
|
||||
attackerAtkStat = gBattleMons[battlerAtk].attack;
|
||||
statStage = gBattleMons[battlerAtk].statStages[STAT_ATK];
|
||||
attackerAtkStat *= gStatStageRatios[statStage][0];
|
||||
attackerAtkStat /= gStatStageRatios[statStage][1];
|
||||
|
||||
attackerSpAtkStat = gBattleMons[battlerAtk].spAttack;
|
||||
statStage = gBattleMons[battlerAtk].statStages[STAT_SPATK];
|
||||
attackerSpAtkStat *= gStatStageRatios[statStage][0];
|
||||
attackerSpAtkStat /= gStatStageRatios[statStage][1];
|
||||
|
||||
for (battlerDef = 0; battlerDef < gBattlersCount; battlerDef++)
|
||||
{
|
||||
if (battlerAtk == battlerDef)
|
||||
continue;
|
||||
|
||||
targetDefStat = gBattleMons[battlerDef].defense;
|
||||
statStage = gBattleMons[battlerDef].statStages[STAT_DEF];
|
||||
targetDefStat *= gStatStageRatios[statStage][0];
|
||||
targetDefStat /= gStatStageRatios[statStage][1];
|
||||
|
||||
physical = ((((2 * gBattleMons[battlerAtk].level / 5 + 2) * gMovesInfo[MOVE_SHELL_SIDE_ARM].power * attackerAtkStat) / targetDefStat) / 50);
|
||||
|
||||
targetSpDefStat = gBattleMons[battlerDef].spDefense;
|
||||
statStage = gBattleMons[battlerDef].statStages[STAT_SPDEF];
|
||||
targetSpDefStat *= gStatStageRatios[statStage][0];
|
||||
targetSpDefStat /= gStatStageRatios[statStage][1];
|
||||
|
||||
special = ((((2 * gBattleMons[battlerAtk].level / 5 + 2) * gMovesInfo[MOVE_SHELL_SIDE_ARM].power * attackerSpAtkStat) / targetSpDefStat) / 50);
|
||||
|
||||
if (((physical > special) || (physical == special && RandomPercentage(RNG_SHELL_SIDE_ARM, 50))))
|
||||
gBattleStruct->shellSideArmCategory[battlerAtk][battlerDef] = DAMAGE_CATEGORY_SPECIAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1934,12 +1934,6 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] =
|
|||
.battleTvScore = 0, // TODO: Assign points
|
||||
},
|
||||
|
||||
[EFFECT_SHELL_SIDE_ARM] =
|
||||
{
|
||||
.battleScript = BattleScript_EffectShellSideArm,
|
||||
.battleTvScore = 0, // TODO: Assign points
|
||||
},
|
||||
|
||||
[EFFECT_TERRAIN_PULSE] =
|
||||
{
|
||||
.battleScript = BattleScript_EffectHit,
|
||||
|
|
|
@ -17350,7 +17350,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] =
|
|||
.description = COMPOUND_STRING(
|
||||
"Deals better of physical and\n"
|
||||
"special damage. May poison."),
|
||||
.effect = EFFECT_SHELL_SIDE_ARM,
|
||||
.effect = EFFECT_HIT, // The effect is hardcoded to the move since SetShellSideArmCategory() can't be used with anything but Shell Side Arm because of the BP requirement
|
||||
.power = 90,
|
||||
.type = TYPE_POISON,
|
||||
.accuracy = 100,
|
||||
|
|
100
test/battle/move_effect/shell_side_arm.c
Normal file
100
test/battle/move_effect/shell_side_arm.c
Normal file
|
@ -0,0 +1,100 @@
|
|||
#include "global.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
SINGLE_BATTLE_TEST("Shell Side Arm can be countered if it is physical")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_SHELL_SIDE_ARM); }
|
||||
OPPONENT(SPECIES_REGICE) { Defense(100); SpDefense(200); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_SHELL_SIDE_ARM); MOVE(opponent, MOVE_COUNTER); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SHELL_SIDE_ARM, player);
|
||||
HP_BAR(opponent);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_COUNTER, opponent);
|
||||
HP_BAR(player);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Shell Side Arm can be mirror coated if it is special")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_SHELL_SIDE_ARM); }
|
||||
OPPONENT(SPECIES_REGIROCK) { Defense(200); SpDefense(100); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_SHELL_SIDE_ARM); MOVE(opponent, MOVE_MIRROR_COAT); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SHELL_SIDE_ARM, player);
|
||||
HP_BAR(opponent);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_MIRROR_COAT, opponent);
|
||||
HP_BAR(player);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Shell Side Arm does not change catogory mid-turn")
|
||||
{
|
||||
s16 damage[3];
|
||||
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_SHELL_SIDE_ARM); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Defense(100); SpDefense(120); }
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_CELEBRATE); MOVE(player, MOVE_SHELL_SIDE_ARM); }
|
||||
TURN { MOVE(opponent, MOVE_LIGHT_SCREEN); MOVE(player, MOVE_SHELL_SIDE_ARM); }
|
||||
TURN { MOVE(opponent, MOVE_CELEBRATE); MOVE(player, MOVE_SHELL_SIDE_ARM); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SHELL_SIDE_ARM, player);
|
||||
HP_BAR(opponent, captureDamage: &damage[0]);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_LIGHT_SCREEN, opponent);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SHELL_SIDE_ARM, player);
|
||||
HP_BAR(opponent, captureDamage: &damage[1]);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SHELL_SIDE_ARM, player);
|
||||
HP_BAR(opponent, captureDamage: &damage[2]);
|
||||
} THEN {
|
||||
EXPECT_EQ(damage[0], damage[1]);
|
||||
EXPECT_EQ(damage[1], damage[2]);
|
||||
}
|
||||
}
|
||||
|
||||
DOUBLE_BATTLE_TEST("Shell Side Arm is choosing it's type for each battler on the field")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { Speed(10); Moves(MOVE_SHELL_SIDE_ARM); }
|
||||
PLAYER(SPECIES_WOBBUFFET) { Speed(20); }
|
||||
OPPONENT(SPECIES_REGIROCK) { Speed(30); Defense(200); SpDefense(100); }
|
||||
OPPONENT(SPECIES_REGICE) { Speed(30); Defense(100); SpDefense(200); }
|
||||
} WHEN {
|
||||
TURN { MOVE(playerLeft, MOVE_SHELL_SIDE_ARM, target: opponentRight); MOVE(opponentRight, MOVE_COUNTER); }
|
||||
TURN { MOVE(playerLeft, MOVE_SHELL_SIDE_ARM, target: opponentLeft); MOVE(opponentLeft, MOVE_MIRROR_COAT); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SHELL_SIDE_ARM, playerLeft);
|
||||
HP_BAR(opponentRight);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_COUNTER, opponentRight);
|
||||
HP_BAR(playerLeft);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SHELL_SIDE_ARM, playerLeft);
|
||||
HP_BAR(opponentLeft);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_MIRROR_COAT, opponentLeft);
|
||||
HP_BAR(playerLeft);
|
||||
}
|
||||
}
|
||||
|
||||
DOUBLE_BATTLE_TEST("Shell Side Arm does not change category mid-turn")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { Speed(10); Moves(MOVE_SHELL_SIDE_ARM); }
|
||||
PLAYER(SPECIES_WOBBUFFET) { Speed(20); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Speed(30); Defense(200); SpDefense(190); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Speed(40); }
|
||||
} WHEN {
|
||||
TURN { MOVE(playerLeft, MOVE_SHELL_SIDE_ARM, target: opponentLeft);
|
||||
MOVE(opponentRight, MOVE_LIGHT_SCREEN);
|
||||
MOVE(opponentLeft, MOVE_MIRROR_COAT);
|
||||
}
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_LIGHT_SCREEN, opponentRight);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SHELL_SIDE_ARM, playerLeft);
|
||||
HP_BAR(opponentLeft);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_MIRROR_COAT, opponentLeft);
|
||||
HP_BAR(playerLeft);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue