diff --git a/include/battle/btlcmd.h b/include/battle/btlcmd.h index f3bb701065..9fccfafd3e 100644 --- a/include/battle/btlcmd.h +++ b/include/battle/btlcmd.h @@ -133,6 +133,14 @@ enum CheckHaveOp { }; enum StatusEffect { + STATUS_EFFECT_NONE = 0, + + STATUS_EFFECT_SLEEP, + STATUS_EFFECT_POISON, + STATUS_EFFECT_BURN, + STATUS_EFFECT_FREEZE, + STATUS_EFFECT_PARALYSIS, + STATUS_EFFECT_LEVEL_UP = 8, STATUS_EFFECT_STAGE_UP = 12, diff --git a/include/constants/battle/moves.h b/include/constants/battle/moves.h index 9a471dc913..beaf63e369 100644 --- a/include/constants/battle/moves.h +++ b/include/constants/battle/moves.h @@ -99,6 +99,7 @@ #define MOVE_STATUS_DID_NOT_AFFECT (MOVE_STATUS_INEFFECTIVE | MOVE_STATUS_WONDER_GUARD) #define MOVE_STATUS_GENERAL_MISS (MOVE_STATUS_MISSED | MOVE_STATUS_SEMI_INVULNERABLE) +#define MOVE_STATUS_BASIC_EFFECTIVENESS (MOVE_STATUS_SUPER_EFFECTIVE | MOVE_STATUS_NOT_VERY_EFFECTIVE) #define RANGE_SINGLE_TARGET 0 #define RANGE_SINGLE_TARGET_SPECIAL (1 << 0) diff --git a/include/constants/battle/system_control.h b/include/constants/battle/system_control.h index 6776c5d91c..eb32ddce13 100644 --- a/include/constants/battle/system_control.h +++ b/include/constants/battle/system_control.h @@ -12,9 +12,10 @@ #define SYSCTL_BATON_PASS (1 << 8) #define SYSCTL_LAST_OF_MULTI_TURN (1 << 9) #define SYSCTL_NONSTANDARD_ACC_CHECK (1 << 10) // used by Future Sight and OHKO moves +#define SYSCTL_IGNORE_TYPE_CHECKS (1 << 11) #define SYSCTL_MOVE_HIT (1 << 13) // this indicates that the move hit its target(s) #define SYSCTL_PLAYED_MOVE_ANIMATION (1 << 14) -#define SYSCTL_SKIP_TYPE_CHECK (1 << 15) +#define SYSCTL_IGNORE_IMMUNITIES (1 << 15) #define SYSCTL_FAIL_STAT_STAGE_CHANGE (1 << 17) #define SYSCTL_HIT_DURING_SHADOW_FORCE (1 << 19) #define SYSCTL_REUSE_LAST_MOVE (1 << 20) diff --git a/include/constants/items.h b/include/constants/items.h index 567f1868d4..16e42c7975 100644 --- a/include/constants/items.h +++ b/include/constants/items.h @@ -173,7 +173,7 @@ enum { #define HOLD_EFFECT_ARCEUS_ELECTRIC 128 #define HOLD_EFFECT_ARCEUS_GRASS 129 #define HOLD_EFFECT_ARCEUS_ICE 130 -#define HOLD_EFFECT_ARCEUS_FIGHT 131 +#define HOLD_EFFECT_ARCEUS_FIGHTING 131 #define HOLD_EFFECT_ARCEUS_POISON 132 #define HOLD_EFFECT_ARCEUS_GROUND 133 #define HOLD_EFFECT_ARCEUS_FLYING 134 diff --git a/include/overlay016/ov16_0225177C.h b/include/overlay016/ov16_0225177C.h index ff0ea96a36..5a60dde8a4 100644 --- a/include/overlay016/ov16_0225177C.h +++ b/include/overlay016/ov16_0225177C.h @@ -6,6 +6,7 @@ #include "battle/battle_context.h" #include "battle/battle_mon.h" #include "battle/battle_message.h" +#include "battle/btlcmd.h" enum BattleContextParam { BATTLECTX_SIDE_CONDITIONS_MASK = 0, @@ -356,8 +357,31 @@ BOOL BattleSystem_TriggerSecondaryEffect(BattleSystem *battleSys, BattleContext * @return The chosen defender for the move */ int BattleSystem_Defender(BattleSystem *battleSys, BattleContext *battleCtx, int attacker, u16 move, BOOL randomize, int inRange); -void BattleSystem_RedirectTarget(BattleSystem * param0, BattleContext * param1, int param2, u16 param3); -BOOL BattleMove_TriggerRedirectionAbilities(BattleSystem * param0, BattleContext * param1); + +/** + * @brief Check for redirection abilities given an attacker using a certain move. + * + * This routine will update the appropriate flags and the current target if the + * used move is Electric- or Water-type, has single-target range, and at least + * one other battler on the field has Lightning Rod or Storm Drain, respectively. + * + * @param battleSys + * @param battleCtx + * @param attacker + * @param move + */ +void BattleSystem_CheckRedirectionAbilities(BattleSystem *battleSys, BattleContext *battleCtx, int attacker, u16 move); + +/** + * @brief Trigger a redirection ability's effect and load the appropriate + * subscript for execution. + * + * @param battleSys + * @param battleCtx + * @return TRUE if a subscript was loaded and should be executed for the effect, + * FALSE otherwise. + */ +BOOL BattleSystem_TriggerRedirectionAbilities(BattleSystem *battleSys, BattleContext *battleCtx); void BattleMon_CopyToParty(BattleSystem * param0, BattleContext * param1, int param2); /** @@ -380,7 +404,16 @@ void Battler_LockMoveChoice(BattleSystem *battleSys, BattleContext *battleCtx, i * @param battler */ void Battler_UnlockMoveChoice(BattleSystem *battleSys, BattleContext *battleCtx, int battler); -int ov16_02253F7C(BattleContext * param0, int param1); + +/** + * @brief Calculate the status effect value for a battler's non-volatile + * condition, if any. + * + * @param battleCtx + * @param battler + * @return A value representing the battler's status non-volatile status. + */ +enum StatusEffect Battler_StatusCondition(BattleContext *battleCtx, int battler); BOOL BattleSystem_CheckTrainerMessage(BattleSystem * param0, BattleContext * param1); void BattleContext_Init(BattleContext * param0); void BattleContext_InitCounters(BattleSystem * param0, BattleContext * param1); @@ -419,8 +452,47 @@ void BattleSystem_SetupNextTurn(BattleSystem * param0, BattleContext * param1); int BattleSystem_CheckStruggling(BattleSystem * param0, BattleContext * param1, int param2, int param3, int param4); BOOL BattleSystem_CanUseMove(BattleSystem *battleSys, BattleContext *battleCtx, int battler, int moveSlot, BattleMessage *msgOut); int Battler_SlotForMove(BattleMon * param0, u16 param1); -int BattleSystem_CheckTypeChart(BattleSystem * param0, BattleContext * param1, int param2, int param3, int param4, int param5, int param6, u32 * param7); -void ov16_022552D4(BattleContext * param0, int param1, int param2, int param3, int param4, int param5, int param6, int param7, u32 * param8); + +/** + * @brief Apply type-chart effectiveness for a given move against its target. + * + * This encapsulates the following multipliers: + * - Same-Type Attack Bonus (and its modification by the ability Adaptability) + * - Super/Not Very Effective multipliers for each of the defender's types + * - Type-effectiveness overrides for effects like Iron Ball, Roost, and Miracle Eye + * - Wonder Guard's override for moves that are not super-effective + * - Expert Belt's multiplier for super-effective moves + * - Filter and Solid Rock's multipliers for defenders taking super-effective damage + * - Tinted Lens's multiplier for defenders taking not-very-effective damage + * + * The move status mask will also be updated with the correct flags. + * + * @param battleSys + * @param battleCtx + * @param move + * @param inType If this is non-zero, then it will be regarded as the move's type + * @param attacker + * @param defender + * @param damage Pre-calculated starting damage value + * @param[out] moveStatusMask + * @return Damage value after applying the type-chart and related multipliers + */ +int BattleSystem_ApplyTypeChart(BattleSystem *battleSys, BattleContext *battleCtx, int move, int inType, int attacker, int defender, int damage, u32 *moveStatusMask); + +/** + * @brief Calculate the effectiveness mask of the given move. + * + * @param battleCtx + * @param move + * @param inType If this is non-zero, then it will be regarded as the move's type + * @param attackerAbility + * @param defenderAbility + * @param defenderItemEffect + * @param defenderType1 + * @param defenderType2 + * @param[out] moveStatusMask + */ +void BattleSystem_CalcEffectiveness(BattleContext *battleCtx, int move, int inType, int attackerAbility, int defenderAbility, int defenderItemEffect, int defenderType1, int defenderType2, u32 *moveStatusMask); BOOL BattleContext_MoveFailed(BattleContext * param0, int param1); /** diff --git a/res/items/pl_item_data.csv b/res/items/pl_item_data.csv index f21e8bf798..b9742d9262 100644 --- a/res/items/pl_item_data.csv +++ b/res/items/pl_item_data.csv @@ -280,7 +280,7 @@ ITEM_SPLASH_PLATE,1000,HOLD_EFFECT_ARCEUS_WATER,20,0,0,90,0,31,false,false,POCKE ITEM_ZAP_PLATE,1000,HOLD_EFFECT_ARCEUS_ELECTRIC,20,0,0,90,0,31,false,false,POCKET_ITEMS,0,0,0,0,false,false,false,false,false,false,false,false,false,false,false,false,0,0,0,0,0,0,0,false,false,false,false,false,false,false,false,false,false,false,false,false,false,0,0,0,0,0,0,0,0,0,0,0 ITEM_MEADOW_PLATE,1000,HOLD_EFFECT_ARCEUS_GRASS,20,0,0,90,0,31,false,false,POCKET_ITEMS,0,0,0,0,false,false,false,false,false,false,false,false,false,false,false,false,0,0,0,0,0,0,0,false,false,false,false,false,false,false,false,false,false,false,false,false,false,0,0,0,0,0,0,0,0,0,0,0 ITEM_ICICLE_PLATE,1000,HOLD_EFFECT_ARCEUS_ICE,20,0,0,90,0,31,false,false,POCKET_ITEMS,0,0,0,0,false,false,false,false,false,false,false,false,false,false,false,false,0,0,0,0,0,0,0,false,false,false,false,false,false,false,false,false,false,false,false,false,false,0,0,0,0,0,0,0,0,0,0,0 -ITEM_FIST_PLATE,1000,HOLD_EFFECT_ARCEUS_FIGHT,20,0,0,90,0,31,false,false,POCKET_ITEMS,0,0,0,0,false,false,false,false,false,false,false,false,false,false,false,false,0,0,0,0,0,0,0,false,false,false,false,false,false,false,false,false,false,false,false,false,false,0,0,0,0,0,0,0,0,0,0,0 +ITEM_FIST_PLATE,1000,HOLD_EFFECT_ARCEUS_FIGHTING,20,0,0,90,0,31,false,false,POCKET_ITEMS,0,0,0,0,false,false,false,false,false,false,false,false,false,false,false,false,0,0,0,0,0,0,0,false,false,false,false,false,false,false,false,false,false,false,false,false,false,0,0,0,0,0,0,0,0,0,0,0 ITEM_TOXIC_PLATE,1000,HOLD_EFFECT_ARCEUS_POISON,20,0,0,90,0,31,false,false,POCKET_ITEMS,0,0,0,0,false,false,false,false,false,false,false,false,false,false,false,false,0,0,0,0,0,0,0,false,false,false,false,false,false,false,false,false,false,false,false,false,false,0,0,0,0,0,0,0,0,0,0,0 ITEM_EARTH_PLATE,1000,HOLD_EFFECT_ARCEUS_GROUND,20,0,0,90,0,31,false,false,POCKET_ITEMS,0,0,0,0,false,false,false,false,false,false,false,false,false,false,false,false,0,0,0,0,0,0,0,false,false,false,false,false,false,false,false,false,false,false,false,false,false,0,0,0,0,0,0,0,0,0,0,0 ITEM_SKY_PLATE,1000,HOLD_EFFECT_ARCEUS_FLYING,20,0,0,90,0,31,false,false,POCKET_ITEMS,0,0,0,0,false,false,false,false,false,false,false,false,false,false,false,false,0,0,0,0,0,0,0,false,false,false,false,false,false,false,false,false,false,false,false,false,false,0,0,0,0,0,0,0,0,0,0,0 diff --git a/src/overlay014/ov14_0221FC20.c b/src/overlay014/ov14_0221FC20.c index 7c07a43839..f2b341da03 100644 --- a/src/overlay014/ov14_0221FC20.c +++ b/src/overlay014/ov14_0221FC20.c @@ -1469,7 +1469,7 @@ static void ov14_02221060 (BattleSystem * param0, BattleContext * param1) v4 = ov14_0222327C(param0, param1, param1->aiContext.attacker, v3); if (v3) { - v1 = BattleSystem_CheckTypeChart(param0, param1, v3, v4, param1->aiContext.attacker, param1->aiContext.defender, v1, &v2); + v1 = BattleSystem_ApplyTypeChart(param0, param1, v3, v4, param1->aiContext.attacker, param1->aiContext.defender, v1, &v2); if (v1 == 60 * 2) { v1 = 80; @@ -1505,7 +1505,7 @@ static void ov14_02221114 (BattleSystem * param0, BattleContext * param1) v1 = ov14_02222CF0(param1); v2 = 40; v3 = 0; - v2 = BattleSystem_CheckTypeChart(param0, param1, param1->aiContext.move, ov14_0222327C(param0, param1, param1->aiContext.attacker, param1->aiContext.move), param1->aiContext.attacker, param1->aiContext.defender, v2, &v3); + v2 = BattleSystem_ApplyTypeChart(param0, param1, param1->aiContext.move, ov14_0222327C(param0, param1, param1->aiContext.attacker, param1->aiContext.move), param1->aiContext.attacker, param1->aiContext.defender, v2, &v3); if (v2 == 60 * 2) { v2 = 80; @@ -3272,7 +3272,7 @@ static s32 ov14_02222E84 (BattleSystem * param0, BattleContext * param1, u16 par param1->battleStatusMask |= 0x800; } - v5 = BattleSystem_CheckTypeChart(param0, param1, param2, v2, param5, param1->aiContext.defender, v5, &v4); + v5 = BattleSystem_ApplyTypeChart(param0, param1, param2, v2, param5, param1->aiContext.defender, v5, &v4); param1->battleStatusMask &= (0x800 ^ 0xffffffff); if (v4 & (0x8 | 0x800 | 0x100000 | 0x40000)) { @@ -3413,7 +3413,7 @@ static BOOL ov14_0222342C (BattleSystem * param0, BattleContext * param1, int pa if (v2) { v4 = 0; - BattleSystem_CheckTypeChart(param0, param1, v2, v3, param2, param2 ^ 1, 0, &v4); + BattleSystem_ApplyTypeChart(param0, param1, v2, v3, param2, param2 ^ 1, 0, &v4); if (v4 & 0x2) { return 0; @@ -3431,7 +3431,7 @@ static BOOL ov14_0222342C (BattleSystem * param0, BattleContext * param1, int pa if (v2) { v4 = 0; - ov16_022552D4(param1, v2, v3, Pokemon_GetValue(v5, MON_DATA_ABILITY, NULL), Battler_Ability(param1, param2 ^ 1), Battler_HeldItemEffect(param1, param2 ^ 1), BattleMon_Get(param1, param2 ^ 1, 27, NULL), BattleMon_Get(param1, param2 ^ 1, 28, NULL), &v4); + BattleSystem_CalcEffectiveness(param1, v2, v3, Pokemon_GetValue(v5, MON_DATA_ABILITY, NULL), Battler_Ability(param1, param2 ^ 1), Battler_HeldItemEffect(param1, param2 ^ 1), BattleMon_Get(param1, param2 ^ 1, 27, NULL), BattleMon_Get(param1, param2 ^ 1, 28, NULL), &v4); if (v4 & 0x2) { if ((BattleSystem_RandNext(param0) % 3) < 2) { @@ -3479,7 +3479,7 @@ static BOOL ov14_022235F0 (BattleSystem * param0, BattleContext * param1, int pa v8 = 0; if (param1->battleMons[v2].curHP) { - BattleSystem_CheckTypeChart(param0, param1, v6, v7, param2, v2, 0, &v8); + BattleSystem_ApplyTypeChart(param0, param1, v6, v7, param2, v2, 0, &v8); } if ((v8 & 0x8) == 0) { @@ -3489,7 +3489,7 @@ static BOOL ov14_022235F0 (BattleSystem * param0, BattleContext * param1, int pa v8 = 0; if (param1->battleMons[v3].curHP) { - BattleSystem_CheckTypeChart(param0, param1, v6, v7, param2, v3, 0, &v8); + BattleSystem_ApplyTypeChart(param0, param1, v6, v7, param2, v3, 0, &v8); } if ((v8 & 0x8) == 0) { @@ -3525,7 +3525,7 @@ static BOOL ov14_022235F0 (BattleSystem * param0, BattleContext * param1, int pa v8 = 0; if (param1->battleMons[v2].curHP) { - ov16_022552D4(param1, v6, v7, Pokemon_GetValue(v12, MON_DATA_ABILITY, NULL), Battler_Ability(param1, v2), Battler_HeldItemEffect(param1, v2), BattleMon_Get(param1, v2, 27, NULL), BattleMon_Get(param1, v2, 28, NULL), &v8); + BattleSystem_CalcEffectiveness(param1, v6, v7, Pokemon_GetValue(v12, MON_DATA_ABILITY, NULL), Battler_Ability(param1, v2), Battler_HeldItemEffect(param1, v2), BattleMon_Get(param1, v2, 27, NULL), BattleMon_Get(param1, v2, 28, NULL), &v8); } if (v8 & 0x2) { @@ -3538,7 +3538,7 @@ static BOOL ov14_022235F0 (BattleSystem * param0, BattleContext * param1, int pa v8 = 0; if (param1->battleMons[v3].curHP) { - ov16_022552D4(param1, v6, v7, Pokemon_GetValue(v12, MON_DATA_ABILITY, NULL), Battler_Ability(param1, v3), Battler_HeldItemEffect(param1, v3), BattleMon_Get(param1, v3, 27, NULL), BattleMon_Get(param1, v3, 28, NULL), &v8); + BattleSystem_CalcEffectiveness(param1, v6, v7, Pokemon_GetValue(v12, MON_DATA_ABILITY, NULL), Battler_Ability(param1, v3), Battler_HeldItemEffect(param1, v3), BattleMon_Get(param1, v3, 27, NULL), BattleMon_Get(param1, v3, 28, NULL), &v8); } if (v8 & 0x2) { @@ -3564,7 +3564,7 @@ static BOOL ov14_022235F0 (BattleSystem * param0, BattleContext * param1, int pa v8 = 0; if (param1->battleMons[v2].curHP) { - ov16_022552D4(param1, v6, v7, Pokemon_GetValue(v12, MON_DATA_ABILITY, NULL), Battler_Ability(param1, v2), Battler_HeldItemEffect(param1, v2), BattleMon_Get(param1, v2, 27, NULL), BattleMon_Get(param1, v2, 28, NULL), &v8); + BattleSystem_CalcEffectiveness(param1, v6, v7, Pokemon_GetValue(v12, MON_DATA_ABILITY, NULL), Battler_Ability(param1, v2), Battler_HeldItemEffect(param1, v2), BattleMon_Get(param1, v2, 27, NULL), BattleMon_Get(param1, v2, 28, NULL), &v8); } if (v8 == 0) { @@ -3577,7 +3577,7 @@ static BOOL ov14_022235F0 (BattleSystem * param0, BattleContext * param1, int pa v8 = 0; if (param1->battleMons[v3].curHP) { - ov16_022552D4(param1, v6, v7, Pokemon_GetValue(v12, MON_DATA_ABILITY, NULL), Battler_Ability(param1, v3), Battler_HeldItemEffect(param1, v3), BattleMon_Get(param1, v3, 27, NULL), BattleMon_Get(param1, v3, 28, NULL), &v8); + BattleSystem_CalcEffectiveness(param1, v6, v7, Pokemon_GetValue(v12, MON_DATA_ABILITY, NULL), Battler_Ability(param1, v3), Battler_HeldItemEffect(param1, v3), BattleMon_Get(param1, v3, 27, NULL), BattleMon_Get(param1, v3, 28, NULL), &v8); } if (v8 == 0) { @@ -3613,7 +3613,7 @@ static BOOL ov14_02223B34 (BattleSystem * param0, BattleContext * param1, int pa if (v4) { v1 = 0; - BattleSystem_CheckTypeChart(param0, param1, v4, v5, param2, v2, 0, &v1); + BattleSystem_ApplyTypeChart(param0, param1, v4, v5, param2, v2, 0, &v1); if (v1 & 0x2) { if (param3) { @@ -3641,7 +3641,7 @@ static BOOL ov14_02223B34 (BattleSystem * param0, BattleContext * param1, int pa if (v4) { v1 = 0; - BattleSystem_CheckTypeChart(param0, param1, v4, v5, param2, v2, 0, &v1); + BattleSystem_ApplyTypeChart(param0, param1, v4, v5, param2, v2, 0, &v1); if (v1 & 0x2) { if (param3) { @@ -3760,7 +3760,7 @@ static BOOL ov14_02223E10 (BattleSystem * param0, BattleContext * param1, int pa v6 = 0; v5 = ov14_0222327C(param0, param1, param1->moveHitBattler[param2], param1->moveHit[param2]); - ov16_022552D4(param1, param1->moveHit[param2], v5, Battler_Ability(param1, param1->moveHitBattler[param2]), Pokemon_GetValue(v9, MON_DATA_ABILITY, NULL), BattleSystem_GetItemData(param1, Pokemon_GetValue(v9, MON_DATA_HELD_ITEM, NULL), 1), Pokemon_GetValue(v9, MON_DATA_TYPE_1, NULL), Pokemon_GetValue(v9, MON_DATA_TYPE_2, NULL), &v6); + BattleSystem_CalcEffectiveness(param1, param1->moveHit[param2], v5, Battler_Ability(param1, param1->moveHitBattler[param2]), Pokemon_GetValue(v9, MON_DATA_ABILITY, NULL), BattleSystem_GetItemData(param1, Pokemon_GetValue(v9, MON_DATA_HELD_ITEM, NULL), 1), Pokemon_GetValue(v9, MON_DATA_TYPE_1, NULL), Pokemon_GetValue(v9, MON_DATA_TYPE_2, NULL), &v6); if (v6 & param3) { for (v1 = 0; v1 < 4; v1++) { @@ -3769,7 +3769,7 @@ static BOOL ov14_02223E10 (BattleSystem * param0, BattleContext * param1, int pa if (v4) { v6 = 0; - ov16_022552D4(param1, v4, v5, Pokemon_GetValue(v9, MON_DATA_ABILITY, NULL), Battler_Ability(param1, param1->moveHitBattler[param2]), Battler_HeldItemEffect(param1, param1->moveHitBattler[param2]), BattleMon_Get(param1, param1->moveHitBattler[param2], 27, NULL), BattleMon_Get(param1, param1->moveHitBattler[param2], 28, NULL), &v6); + BattleSystem_CalcEffectiveness(param1, v4, v5, Pokemon_GetValue(v9, MON_DATA_ABILITY, NULL), Battler_Ability(param1, param1->moveHitBattler[param2]), Battler_HeldItemEffect(param1, param1->moveHitBattler[param2]), BattleMon_Get(param1, param1->moveHitBattler[param2], 27, NULL), BattleMon_Get(param1, param1->moveHitBattler[param2], 28, NULL), &v6); if (v6 & 0x2) { if ((BattleSystem_RandNext(param0) % param4) == 0) { diff --git a/src/overlay016/battle_controller.c b/src/overlay016/battle_controller.c index 7b457691c4..eb052c32ba 100644 --- a/src/overlay016/battle_controller.c +++ b/src/overlay016/battle_controller.c @@ -2352,10 +2352,10 @@ static int BattleController_CheckTypeChart(BattleSystem *battleSys, BattleContex if ((CURRENT_MOVE_DATA.range != RANGE_USER && CURRENT_MOVE_DATA.range != RANGE_USER_SIDE && CURRENT_MOVE_DATA.power - && (battleCtx->battleStatusMask & SYSCTL_SKIP_TYPE_CHECK) == FALSE + && (battleCtx->battleStatusMask & SYSCTL_IGNORE_IMMUNITIES) == FALSE && (battleCtx->battleStatusMask & SYSCTL_FIRST_OF_MULTI_TURN) == FALSE) || battleCtx->moveCur == MOVE_THUNDER_WAVE) { - battleCtx->damage = BattleSystem_CheckTypeChart(battleSys, + battleCtx->damage = BattleSystem_ApplyTypeChart(battleSys, battleCtx, battleCtx->moveCur, battleCtx->moveType, @@ -3203,7 +3203,7 @@ static void BattleController_BeforeMove(BattleSystem *battleSys, BattleContext * battleCtx->beforeMoveCheckState++; case BEFORE_MOVE_STATE_REDIRECT_TARGET: - BattleSystem_RedirectTarget(battleSys, battleCtx, battleCtx->attacker, battleCtx->moveCur); + BattleSystem_CheckRedirectionAbilities(battleSys, battleCtx, battleCtx->attacker, battleCtx->moveCur); battleCtx->beforeMoveCheckState = BEFORE_MOVE_START; } @@ -3248,7 +3248,7 @@ static void BattleController_TryMove(BattleSystem *battleSys, BattleContext *bat case TRY_MOVE_STATE_TRIGGER_REDIRECTION_ABILITIES: battleCtx->tryMoveCheckState++; - if (BattleMove_TriggerRedirectionAbilities(battleSys, battleCtx) == TRUE) { + if (BattleSystem_TriggerRedirectionAbilities(battleSys, battleCtx) == TRUE) { return; } diff --git a/src/overlay016/battle_script.c b/src/overlay016/battle_script.c index 3b18f62ec0..a6239d36e5 100644 --- a/src/overlay016/battle_script.c +++ b/src/overlay016/battle_script.c @@ -2357,7 +2357,7 @@ static BOOL BtlCmd_JumpToMove(BattleSystem *battleSys, BattleContext *battleCtx) if (targetIsSet == FALSE) { battleCtx->defender = BattleSystem_Defender(battleSys, battleCtx, battleCtx->attacker, battleCtx->msgMoveTemp, TRUE, 0); - BattleSystem_RedirectTarget(battleSys, battleCtx, battleCtx->attacker, battleCtx->msgMoveTemp); + BattleSystem_CheckRedirectionAbilities(battleSys, battleCtx, battleCtx->attacker, battleCtx->msgMoveTemp); ATTACKER_ACTION[BATTLE_ACTION_CHOOSE_TARGET] = battleCtx->defender; } @@ -3843,7 +3843,6 @@ static BOOL BtlCmd_UnlockMoveChoice(BattleSystem *battleSys, BattleContext *batt */ static BOOL BtlCmd_SetStatusIcon(BattleSystem *battleSys, BattleContext *battleCtx) { - BattleScript_Iter(battleCtx, 1); int inBattler = BattleScript_Read(battleCtx); int status = BattleScript_Read(battleCtx); @@ -5260,9 +5259,6 @@ static BOOL BtlCmd_TrySpite(BattleSystem *battleSys, BattleContext *battleCtx) * this effect is Heal Bell, then it will fail for any party Pokemon with the * ability Soundproof. * - * Inputs: - * 1. The distance to jump if the effect fails to execute. - * * @param battleSys * @param battleCtx * @return FALSE @@ -7210,7 +7206,7 @@ static BOOL BtlCmd_ApplyTypeMatchup(BattleSystem *battleSys, BattleContext *batt { BattleScript_Iter(battleCtx, 1); - battleCtx->damage = BattleSystem_CheckTypeChart(battleSys, + battleCtx->damage = BattleSystem_ApplyTypeChart(battleSys, battleCtx, battleCtx->moveCur, battleCtx->moveType, @@ -8232,6 +8228,10 @@ static BOOL BtlCmd_GetItemEffectPower(BattleSystem *battleSys, BattleContext *ba * @brief Try to change the battler's type to one according to the battle * terrain. * + * Inputs: + * 1. The distance to jump ahead if the user is already of the terrain's + * corresponding type, or if the user is Arceus. + * * @param battleSys * @param battleCtx * @return BOOL diff --git a/src/overlay016/ov16_0225177C.c b/src/overlay016/ov16_0225177C.c index 28cdd58bfe..d962cd495d 100644 --- a/src/overlay016/ov16_0225177C.c +++ b/src/overlay016/ov16_0225177C.c @@ -44,19 +44,19 @@ #include "overlay016/ov16_0225CBB8.h" #include "overlay016/ov16_0226485C.h" -static BOOL ov16_02254EF4(BattleContext * param0, int param1, int param2, int param3); +static BOOL BasicTypeMulApplies(BattleContext *battleCtx, int attacker, int defender, int chartEntry); static int MapSideEffectToSubscript(BattleContext *battleCtx, enum SideEffectType type, u32 effect); -static int ov16_0225B63C(BattleContext * param0, int param1, int param2, int param3, int param4, u32 * param5); -static BOOL ov16_022553F8(BattleContext * param0, int param1, int param2); -static void ov16_02255448(int param0, u32 * param1); -static BOOL ov16_0225B6C8(BattleContext * param0, int param1); +static int ApplyTypeMultiplier(BattleContext *battleCtx, int attacker, int mul, int damage, BOOL update, u32 *moveStatus); +static BOOL NoImmunityOverrides(BattleContext *battleCtx, int itemEffect, int chartEntry); +static void UpateMoveStatusForTypeMul(int mul, u32 *moveStatusMask); +static BOOL MoveIsOnDamagingTurn(BattleContext *battleCtx, int move); static u8 Battler_MonType(BattleContext *battleCtx, int battler, enum BattleMonParam paramID); static void BattleAI_ClearKnownMoves(BattleContext *battleCtx, u8 battler); static void BattleAI_ClearKnownAbility(BattleContext *battleCtx, u8 battler); static void BattleAI_ClearKnownItem(BattleContext *battleCtx, u8 battler); static int ChooseTraceTarget(BattleSystem *battleSys, BattleContext *battleCtx, int defender1, int defender2); -static BOOL MoveCannotTriggerAnticipation(BattleContext * param0, int param1); -static int BattleMove_Type(BattleSystem * param0, BattleContext * param1, int param2, int param3); +static BOOL MoveCannotTriggerAnticipation(BattleContext *battleCtx, int move); +static int CalcMoveType(BattleSystem *battleSys, BattleContext *battleCtx, int item, int move); static const Fraction sStatStageBoosts[]; @@ -1732,71 +1732,72 @@ int BattleSystem_Defender(BattleSystem *battleSys, BattleContext *battleCtx, int return defender; } -void BattleSystem_RedirectTarget (BattleSystem * param0, BattleContext * param1, int param2, u16 param3) +void BattleSystem_CheckRedirectionAbilities(BattleSystem *battleSys, BattleContext *battleCtx, int attacker, u16 move) { - int v0; - int v1; - int v2; - int v3; - int v4; - - if (param1->defender == 0xff) { - return; - } + int battler, moveType; // must declare these first to match - if ((Battler_Ability(param1, param2) == 96) || (Battler_Ability(param1, param2) == 104)) { + if (battleCtx->defender == BATTLER_NONE + || Battler_Ability(battleCtx, attacker) == ABILITY_NORMALIZE + || Battler_Ability(battleCtx, attacker) == ABILITY_MOLD_BREAKER) { return; } - v0 = Battler_Side(param0, param2) ^ 1; - - if ((param1->sideConditions[v0].followMe) && (param1->battleMons[param1->sideConditions[v0].followMeUser].curHP)) { + int defSide = Battler_Side(battleSys, attacker) ^ 1; + if (battleCtx->sideConditions[defSide].followMe && FOLLOW_ME_MON(defSide).curHP) { return; } - v3 = BattleMove_Type(param0, param1, param2, param3); - - if (v3 == 0) { - v3 = param1->aiContext.moveTable[param3].type; + moveType = CalcMoveType(battleSys, battleCtx, attacker, move); + if (moveType == TYPE_NORMAL) { + moveType = MOVE_DATA(move).type; } - v4 = BattleSystem_MaxBattlers(param0); - - if ((v3 == 13) && ((param1->aiContext.moveTable[param3].range == 0x0) || (param1->aiContext.moveTable[param3].range == 0x2)) && ((param1->battleStatusMask & 0x20) == 0) && (BattleSystem_CountAbility(param0, param1, 9, param2, 31))) { - for (v1 = 0; v1 < v4; v1++) { - v2 = param1->monSpeedOrder[v1]; + int maxBattlers = BattleSystem_MaxBattlers(battleSys); + if (moveType == TYPE_ELECTRIC + && (MOVE_DATA(move).range == RANGE_SINGLE_TARGET || MOVE_DATA(move).range == RANGE_RANDOM_OPPONENT) + && (battleCtx->battleStatusMask & SYSCTL_FIRST_OF_MULTI_TURN) == FALSE + && BattleSystem_CountAbility(battleSys, battleCtx, COUNT_ALIVE_BATTLERS_EXCEPT_ME, attacker, ABILITY_LIGHTNING_ROD)) { + for (int i = 0; i < maxBattlers; i++) { + battler = battleCtx->monSpeedOrder[i]; - if ((Battler_Ability(param1, v2) == 31) && (param1->battleMons[v2].curHP) && (param2 != v2)) { + if (Battler_Ability(battleCtx, battler) == ABILITY_LIGHTNING_ROD + && battleCtx->battleMons[battler].curHP + && attacker != battler) { break; } } - if (v2 != param1->defender) { - param1->selfTurnFlags[v2].lightningRodActivated = 1; - param1->defender = v2; + if (battler != battleCtx->defender) { + battleCtx->selfTurnFlags[battler].lightningRodActivated = TRUE; + battleCtx->defender = battler; } - } else if ((v3 == 11) && ((param1->aiContext.moveTable[param3].range == 0x0) || (param1->aiContext.moveTable[param3].range == 0x2)) && ((param1->battleStatusMask & 0x20) == 0) && (BattleSystem_CountAbility(param0, param1, 9, param2, 114))) { - for (v1 = 0; v1 < v4; v1++) { - v2 = param1->monSpeedOrder[v1]; + } else if (moveType == TYPE_WATER + && (MOVE_DATA(move).range == RANGE_SINGLE_TARGET || MOVE_DATA(move).range == RANGE_RANDOM_OPPONENT) + && (battleCtx->battleStatusMask & SYSCTL_FIRST_OF_MULTI_TURN) == FALSE + && BattleSystem_CountAbility(battleSys, battleCtx, COUNT_ALIVE_BATTLERS_EXCEPT_ME, attacker, ABILITY_STORM_DRAIN)) { + for (int i = 0; i < maxBattlers; i++) { + battler = battleCtx->monSpeedOrder[i]; - if ((Battler_Ability(param1, v2) == 114) && (param1->battleMons[v2].curHP) && (param2 != v2)) { + if (Battler_Ability(battleCtx, battler) == ABILITY_STORM_DRAIN + && battleCtx->battleMons[battler].curHP + && attacker != battler) { break; } } - if (v2 != param1->defender) { - param1->selfTurnFlags[v2].stormDrainActivated = 1; - param1->defender = v2; + if (battler != battleCtx->defender) { + battleCtx->selfTurnFlags[battler].stormDrainActivated = TRUE; + battleCtx->defender = battler; } } } -BOOL BattleMove_TriggerRedirectionAbilities(BattleSystem *battleSys, BattleContext *battleCtx) +BOOL BattleSystem_TriggerRedirectionAbilities(BattleSystem *battleSys, BattleContext *battleCtx) { BOOL result = FALSE; if ((battleCtx->moveStatusFlags & MOVE_STATUS_NO_EFFECTS) == FALSE && DEFENDER_SELF_TURN_FLAGS.lightningRodActivated) { - battleCtx->selfTurnFlags[battleCtx->defender].lightningRodActivated = 0; + battleCtx->selfTurnFlags[battleCtx->defender].lightningRodActivated = FALSE; LOAD_SUBSEQ(BATTLE_SUBSEQ_LIGHTNING_ROD_REDIRECTED); battleCtx->commandNext = battleCtx->command; @@ -1806,7 +1807,7 @@ BOOL BattleMove_TriggerRedirectionAbilities(BattleSystem *battleSys, BattleConte } if ((battleCtx->moveStatusFlags & MOVE_STATUS_NO_EFFECTS) == FALSE && DEFENDER_SELF_TURN_FLAGS.stormDrainActivated) { - battleCtx->selfTurnFlags[battleCtx->defender].stormDrainActivated = 0; + battleCtx->selfTurnFlags[battleCtx->defender].stormDrainActivated = FALSE; LOAD_SUBSEQ(BATTLE_SUBSEQ_LIGHTNING_ROD_REDIRECTED); battleCtx->commandNext = battleCtx->command; @@ -1842,23 +1843,23 @@ void Battler_UnlockMoveChoice(BattleSystem *battleSys, BattleContext *battleCtx, battleCtx->battleMons[battler].moveEffectsData.furyCutterCount = 0; } -int ov16_02253F7C (BattleContext * param0, int param1) +enum StatusEffect Battler_StatusCondition(BattleContext *battleCtx, int battler) { - if (param0->battleMons[param1].status & 0x7) { - return 1; - } else if (param0->battleMons[param1].status & 0x8) { - return 2; - } else if (param0->battleMons[param1].status & 0x10) { - return 3; - } else if (param0->battleMons[param1].status & 0x20) { - return 4; - } else if (param0->battleMons[param1].status & 0x40) { - return 5; - } else if (param0->battleMons[param1].status & 0x80) { - return 2; + if (battleCtx->battleMons[battler].status & MON_CONDITION_SLEEP) { + return STATUS_EFFECT_SLEEP; + } else if (battleCtx->battleMons[battler].status & MON_CONDITION_POISON) { + return STATUS_EFFECT_POISON; + } else if (battleCtx->battleMons[battler].status & MON_CONDITION_BURN) { + return STATUS_EFFECT_BURN; + } else if (battleCtx->battleMons[battler].status & MON_CONDITION_FREEZE) { + return STATUS_EFFECT_FREEZE; + } else if (battleCtx->battleMons[battler].status & MON_CONDITION_PARALYSIS) { + return STATUS_EFFECT_PARALYSIS; + } else if (battleCtx->battleMons[battler].status & MON_CONDITION_TOXIC) { + return STATUS_EFFECT_POISON; } - return 0; + return STATUS_EFFECT_NONE; } BOOL BattleSystem_CheckTrainerMessage (BattleSystem * param0, BattleContext * param1) @@ -2523,255 +2524,293 @@ static const u8 sTypeMatchupMultipliers[][3] = { { 0xFF, 0xFF, TYPE_MULTI_IMMUNE }, }; -static BOOL ov16_02254EF4 (BattleContext * param0, int param1, int param2, int param3) +/** + * @brief Check if the basic type multiplier applies. + * + * @param battleCtx + * @param attacker + * @param defender + * @param chartEntry Index of the entry into the type-chart + * @return TRUE if there are no active effects to override the given chart-entry, + * FALSE if the chart-entry should be overriden + */ +static BOOL BasicTypeMulApplies(BattleContext *battleCtx, int attacker, int defender, int chartEntry) { - BOOL v0; - int v1; - - v1 = Battler_HeldItemEffect(param0, param2); - v0 = 1; + int itemEffect = Battler_HeldItemEffect(battleCtx, defender); + BOOL result = TRUE; - if ((v1 == 106) || (param0->battleMons[param2].moveEffectsMask & 0x400)) { - if ((sTypeMatchupMultipliers[param3][1] == 2) && (sTypeMatchupMultipliers[param3][2] == 0)) { - v0 = 0; - } + if ((itemEffect == HOLD_EFFECT_SPEED_DOWN_GROUNDED || (battleCtx->battleMons[defender].moveEffectsMask & MOVE_EFFECT_INGRAIN)) + && sTypeMatchupMultipliers[chartEntry][1] == TYPE_FLYING + && sTypeMatchupMultipliers[chartEntry][2] == TYPE_MULTI_IMMUNE) { + result = FALSE; } - if (param0->turnFlags[param2].roosting) { - if (sTypeMatchupMultipliers[param3][1] == 2) { - v0 = 0; - } + if (battleCtx->turnFlags[defender].roosting + && sTypeMatchupMultipliers[chartEntry][1] == TYPE_FLYING) { + result = FALSE; } - if (param0->fieldConditionsMask & 0x7000) { - if ((sTypeMatchupMultipliers[param3][1] == 2) && (sTypeMatchupMultipliers[param3][2] == 0)) { - v0 = 0; - } + if ((battleCtx->fieldConditionsMask & FIELD_CONDITION_GRAVITY) + && sTypeMatchupMultipliers[chartEntry][1] == TYPE_FLYING + && sTypeMatchupMultipliers[chartEntry][2] == TYPE_MULTI_IMMUNE) { + result = FALSE; } - if (param0->battleMons[param2].moveEffectsMask & 0x400000) { - if ((sTypeMatchupMultipliers[param3][1] == 17) && (sTypeMatchupMultipliers[param3][2] == 0)) { - v0 = 0; - } + if ((battleCtx->battleMons[defender].moveEffectsMask & MOVE_EFFECT_MIRACLE_EYE) + && sTypeMatchupMultipliers[chartEntry][1] == TYPE_DARK + && sTypeMatchupMultipliers[chartEntry][2] == TYPE_MULTI_IMMUNE) { + result = FALSE; } - return v0; + return result; } -int BattleSystem_CheckTypeChart (BattleSystem * param0, BattleContext * param1, int param2, int param3, int param4, int param5, int param6, u32 * param7) +int BattleSystem_ApplyTypeChart(BattleSystem *battleSys, BattleContext *battleCtx, int move, int inType, int attacker, int defender, int damage, u32 *moveStatusMask) { - int v0; - int v1; - u8 v2; - u32 v3; - u8 v4; - u8 v5; - u8 v6; - u8 v7; + int chartEntry; + int totalMul; + u8 moveType; + u32 movePower; + u8 attackerItemEffect; + u8 defenderItemEffect; + u8 attackerItemPower; + u8 defenderItemPower; - v1 = 1; + totalMul = 1; - if (param2 == 165) { - return param6; + if (move == MOVE_STRUGGLE) { + return damage; } - v4 = Battler_HeldItemEffect(param1, param4); - v6 = Battler_HeldItemPower(param1, param4, 0); - v5 = Battler_HeldItemEffect(param1, param5); - v7 = Battler_HeldItemPower(param1, param5, 0); + attackerItemEffect = Battler_HeldItemEffect(battleCtx, attacker); + attackerItemPower = Battler_HeldItemPower(battleCtx, attacker, ITEM_POWER_CHECK_ALL); + defenderItemEffect = Battler_HeldItemEffect(battleCtx, defender); + defenderItemPower = Battler_HeldItemPower(battleCtx, defender, ITEM_POWER_CHECK_ALL); - if (Battler_Ability(param1, param4) == 96) { - v2 = 0; - } else if (param3) { - v2 = param3; + if (Battler_Ability(battleCtx, attacker) == ABILITY_NORMALIZE) { + moveType = TYPE_NORMAL; + } else if (inType) { + moveType = inType; } else { - v2 = param1->aiContext.moveTable[param2].type; + moveType = MOVE_DATA(move).type; } - v3 = param1->aiContext.moveTable[param2].power; + movePower = MOVE_DATA(move).power; - if (((param1->battleStatusMask & 0x800) == 0) && ((BattleMon_Get(param1, param4, 27, NULL) == v2) || (BattleMon_Get(param1, param4, 28, NULL) == v2))) { - if (Battler_Ability(param1, param4) == 91) { - param6 *= 2; + if ((battleCtx->battleStatusMask & SYSCTL_IGNORE_TYPE_CHECKS) == FALSE && MON_HAS_TYPE(attacker, moveType)) { + if (Battler_Ability(battleCtx, attacker) == ABILITY_ADAPTABILITY) { + damage *= 2; } else { - param6 = param6 * 15 / 10; + damage = damage * 15 / 10; } } - if ((Battler_IgnorableAbility(param1, param4, param5, 26) == 1) && (v2 == 4) && (v5 != 106)) { - param7[0] |= 0x800; - } else if ((param1->battleMons[param5].moveEffectsData.magnetRiseTurns) && ((param1->battleMons[param5].moveEffectsMask & 0x400) == 0) && (v2 == 4) && (v5 != 106)) { - param7[0] |= 0x100000; + if (Battler_IgnorableAbility(battleCtx, attacker, defender, ABILITY_LEVITATE) == TRUE + && moveType == TYPE_GROUND + && defenderItemEffect != HOLD_EFFECT_SPEED_DOWN_GROUNDED) { + *moveStatusMask |= MOVE_STATUS_LEVITATED; + } else if (battleCtx->battleMons[defender].moveEffectsData.magnetRiseTurns + && (battleCtx->battleMons[defender].moveEffectsMask & MOVE_EFFECT_INGRAIN) == FALSE + && moveType == TYPE_GROUND + && defenderItemEffect != HOLD_EFFECT_SPEED_DOWN_GROUNDED) { + *moveStatusMask |= MOVE_STATUS_MAGNET_RISE; } else { - v0 = 0; + chartEntry = 0; - while (sTypeMatchupMultipliers[v0][0] != 0xff) { - if (sTypeMatchupMultipliers[v0][0] == 0xfe) { - if ((param1->battleMons[param5].statusVolatile & 0x20000000) || (Battler_Ability(param1, param4) == 113)) { + while (sTypeMatchupMultipliers[chartEntry][0] != 0xFF) { + if (sTypeMatchupMultipliers[chartEntry][0] == 0xFE) { + // The Ghost-type immunities are listed separately and ignored as a batch + if ((battleCtx->battleMons[defender].statusVolatile & VOLATILE_CONDITION_FORESIGHT) + || Battler_Ability(battleCtx, attacker) == ABILITY_SCRAPPY) { break; } else { - v0++; + chartEntry++; continue; } } - if (sTypeMatchupMultipliers[v0][0] == v2) { - if (sTypeMatchupMultipliers[v0][1] == BattleMon_Get(param1, param5, 27, NULL)) { - if (ov16_02254EF4(param1, param4, param5, v0) == 1) { - param6 = ov16_0225B63C(param1, param4, sTypeMatchupMultipliers[v0][2], param6, v3, param7); + if (sTypeMatchupMultipliers[chartEntry][0] == moveType) { + if (sTypeMatchupMultipliers[chartEntry][1] == BattleMon_Get(battleCtx, defender, BATTLEMON_TYPE_1, NULL) + && BasicTypeMulApplies(battleCtx, attacker, defender, chartEntry) == TRUE) { + damage = ApplyTypeMultiplier(battleCtx, attacker, sTypeMatchupMultipliers[chartEntry][2], damage, movePower, moveStatusMask); - if (sTypeMatchupMultipliers[v0][2] == 20) { - v1 *= 2; - } + if (sTypeMatchupMultipliers[chartEntry][2] == TYPE_MULTI_SUPER_EFF) { + totalMul *= 2; } } - if ((sTypeMatchupMultipliers[v0][1] == BattleMon_Get(param1, param5, 28, NULL)) && (BattleMon_Get(param1, param5, 27, NULL) != BattleMon_Get(param1, param5, 28, NULL))) { - if (ov16_02254EF4(param1, param4, param5, v0) == 1) { - param6 = ov16_0225B63C(param1, param4, sTypeMatchupMultipliers[v0][2], param6, v3, param7); + if (sTypeMatchupMultipliers[chartEntry][1] == BattleMon_Get(battleCtx, defender, BATTLEMON_TYPE_2, NULL) + && BattleMon_Get(battleCtx, defender, BATTLEMON_TYPE_1, NULL) != BattleMon_Get(battleCtx, defender, BATTLEMON_TYPE_2, NULL) + && BasicTypeMulApplies(battleCtx, attacker, defender, chartEntry) == TRUE) { + damage = ApplyTypeMultiplier(battleCtx, attacker, sTypeMatchupMultipliers[chartEntry][2], damage, movePower, moveStatusMask); - if (sTypeMatchupMultipliers[v0][2] == 20) { - v1 *= 2; - } + if (sTypeMatchupMultipliers[chartEntry][2] == TYPE_MULTI_SUPER_EFF) { + totalMul *= 2; } } } - v0++; + chartEntry++; } } - if ((Battler_IgnorableAbility(param1, param4, param5, 25) == 1) && (ov16_0225B6C8(param1, param2)) && (((param7[0] & 0x2) == 0) || ((param7[0] & (2 | 4)) == (2 | 4))) && (v3)) { - param7[0] |= 0x40000; - } else { - if (((param1->battleStatusMask & 0x800) == 0) && ((param1->battleStatusMask & 0x8000) == 0)) { - if ((param7[0] & 0x2) && (v3)) { - if ((Battler_IgnorableAbility(param1, param4, param5, 111) == 1) || (Battler_IgnorableAbility(param1, param4, param5, 116) == 1)) { - param6 = BattleSystem_Divide(param6 * 3, 4); - } + if (Battler_IgnorableAbility(battleCtx, attacker, defender, ABILITY_WONDER_GUARD) == TRUE + && MoveIsOnDamagingTurn(battleCtx, move) + && ((*moveStatusMask & MOVE_STATUS_SUPER_EFFECTIVE) == FALSE + || (*moveStatusMask & MOVE_STATUS_BASIC_EFFECTIVENESS) == MOVE_STATUS_BASIC_EFFECTIVENESS) + && movePower) { + *moveStatusMask |= MOVE_STATUS_WONDER_GUARD; + } else if ((battleCtx->battleStatusMask & SYSCTL_IGNORE_TYPE_CHECKS) == FALSE + && (battleCtx->battleStatusMask & SYSCTL_IGNORE_IMMUNITIES) == FALSE) { + if ((*moveStatusMask & MOVE_STATUS_SUPER_EFFECTIVE) && movePower) { + if (Battler_IgnorableAbility(battleCtx, attacker, defender, ABILITY_FILTER) == TRUE + || Battler_IgnorableAbility(battleCtx, attacker, defender, ABILITY_SOLID_ROCK) == TRUE) { + damage = BattleSystem_Divide(damage * 3, 4); + } - if (v4 == 96) { - param6 = param6 * (100 + v6) / 100; - } + if (attackerItemEffect == HOLD_EFFECT_POWER_UP_SE) { + damage = damage * (100 + attackerItemPower) / 100; } + } - if ((param7[0] & 0x4) && (v3)) { - if (Battler_Ability(param1, param4) == 110) { - param6 *= 2; - } + if ((*moveStatusMask & MOVE_STATUS_NOT_VERY_EFFECTIVE) && movePower) { + if (Battler_Ability(battleCtx, attacker) == ABILITY_TINTED_LENS) { + damage *= 2; } - } else { - param7[0] &= (0x2 ^ 0xffffffff); - param7[0] &= (0x4 ^ 0xffffffff); } + } else { + *moveStatusMask &= ~MOVE_STATUS_SUPER_EFFECTIVE; + *moveStatusMask &= ~MOVE_STATUS_NOT_VERY_EFFECTIVE; } - return param6; + return damage; } -void ov16_022552D4 (BattleContext * param0, int param1, int param2, int param3, int param4, int param5, int param6, int param7, u32 * param8) +void BattleSystem_CalcEffectiveness(BattleContext *battleCtx, int move, int inType, int attackerAbility, int defenderAbility, int defenderItemEffect, int defenderType1, int defenderType2, u32 *moveStatusMask) { - int v0; - u8 v1; + int chartEntry; + u8 moveType; - if (param1 == 165) { + if (move == MOVE_STRUGGLE) { return; } - if (param3 == 96) { - v1 = 0; - } else if (param2) { - v1 = param2; + if (attackerAbility == ABILITY_NORMALIZE) { + moveType = TYPE_NORMAL; + } else if (inType) { + moveType = inType; } else { - v1 = param0->aiContext.moveTable[param1].type; + moveType = MOVE_DATA(move).type; } - if ((param3 != 104) && (param4 == 26) && (v1 == 4) && ((param0->fieldConditionsMask & 0x7000) == 0) && (param5 != 106)) { - param8[0] |= 0x8; + if (attackerAbility != ABILITY_MOLD_BREAKER + && defenderAbility == ABILITY_LEVITATE + && moveType == TYPE_GROUND + && (battleCtx->fieldConditionsMask & FIELD_CONDITION_GRAVITY) == FALSE + && defenderItemEffect != HOLD_EFFECT_SPEED_DOWN_GROUNDED) { + *moveStatusMask |= MOVE_STATUS_INEFFECTIVE; } else { - v0 = 0; + chartEntry = 0; - while (sTypeMatchupMultipliers[v0][0] != 0xff) { - if (sTypeMatchupMultipliers[v0][0] == 0xfe) { - if (param3 == 113) { + while (sTypeMatchupMultipliers[chartEntry][0] != 0xFF) { + if (sTypeMatchupMultipliers[chartEntry][0] == 0xFE) { + if (attackerAbility == ABILITY_SCRAPPY) { break; } else { - v0++; + chartEntry++; continue; } } - if (sTypeMatchupMultipliers[v0][0] == v1) { - if (sTypeMatchupMultipliers[v0][1] == param6) { - if (ov16_022553F8(param0, param5, v0) == 1) { - ov16_02255448(sTypeMatchupMultipliers[v0][2], param8); - } + if (sTypeMatchupMultipliers[chartEntry][0] == moveType) { + if (sTypeMatchupMultipliers[chartEntry][1] == defenderType1 + && NoImmunityOverrides(battleCtx, defenderItemEffect, chartEntry) == TRUE) { + UpateMoveStatusForTypeMul(sTypeMatchupMultipliers[chartEntry][2], moveStatusMask); } - if ((sTypeMatchupMultipliers[v0][1] == param7) && (param6 != param7)) { - if (ov16_022553F8(param0, param5, v0) == 1) { - ov16_02255448(sTypeMatchupMultipliers[v0][2], param8); - } + if (sTypeMatchupMultipliers[chartEntry][1] == defenderType2 + && defenderType1 != defenderType2 + && NoImmunityOverrides(battleCtx, defenderItemEffect, chartEntry) == TRUE) { + UpateMoveStatusForTypeMul(sTypeMatchupMultipliers[chartEntry][2], moveStatusMask); } } - v0++; + chartEntry++; } } - if ((param3 != 104) && (param4 == 25) && (ov16_0225B6C8(param0, param1)) && (((param8[0] & 0x2) == 0) || ((param8[0] & (2 | 4)) == (2 | 4)))) { - param8[0] |= 0x8; + if (attackerAbility != ABILITY_MOLD_BREAKER + && defenderAbility == ABILITY_WONDER_GUARD + && MoveIsOnDamagingTurn(battleCtx, move) + && ((*moveStatusMask & MOVE_STATUS_SUPER_EFFECTIVE) == FALSE + || (*moveStatusMask & MOVE_STATUS_BASIC_EFFECTIVENESS) == MOVE_STATUS_BASIC_EFFECTIVENESS)) { + *moveStatusMask |= MOVE_STATUS_INEFFECTIVE; } return; } -static BOOL ov16_022553F8 (BattleContext * param0, int param1, int param2) +/** + * @brief Determines if there are no immunity-overriding effects in play. + * + * This only checks for the basic immunity-overriding effects: Iron Ball + * and Gravity for Ground-type attacks against Flying-type Pokemon. + * + * @param battleCtx + * @param itemEffect + * @param chartEntry + * @return TRUE if immunities should be respected, FALSE if not. + */ +static BOOL NoImmunityOverrides(BattleContext *battleCtx, int itemEffect, int chartEntry) { - BOOL v0; - - v0 = 1; + BOOL result = TRUE; - if (param1 == 106) { - if ((sTypeMatchupMultipliers[param2][1] == 2) && (sTypeMatchupMultipliers[param2][2] == 0)) { - v0 = 0; - } + if (itemEffect == HOLD_EFFECT_SPEED_DOWN_GROUNDED + && sTypeMatchupMultipliers[chartEntry][1] == TYPE_FLYING + && sTypeMatchupMultipliers[chartEntry][2] == TYPE_MULTI_IMMUNE) { + result = FALSE; } - if (param0->fieldConditionsMask & 0x7000) { - if ((sTypeMatchupMultipliers[param2][1] == 2) && (sTypeMatchupMultipliers[param2][2] == 0)) { - v0 = 0; - } + if ((battleCtx->fieldConditionsMask & FIELD_CONDITION_GRAVITY) + && sTypeMatchupMultipliers[chartEntry][1] == TYPE_FLYING + && sTypeMatchupMultipliers[chartEntry][2] == TYPE_MULTI_IMMUNE) { + result = FALSE; } - return v0; + return result; } -static void ov16_02255448 (int param0, u32 * param1) +/** + * @brief Update the given move status mask according to a type multiplier. + * + * @param mul + * @param moveStatusMask + */ +static void UpateMoveStatusForTypeMul(int mul, u32 *moveStatusMask) { - switch (param0) { - case 0: - param1[0] |= 0x8; - param1[0] &= (0x4 ^ 0xffffffff); - param1[0] &= (0x2 ^ 0xffffffff); + switch (mul) { + case TYPE_MULTI_IMMUNE: + *moveStatusMask |= MOVE_STATUS_INEFFECTIVE; + *moveStatusMask &= ~MOVE_STATUS_NOT_VERY_EFFECTIVE; + *moveStatusMask &= ~MOVE_STATUS_SUPER_EFFECTIVE; break; - case 5: - if (param1[0] & 0x2) { - param1[0] &= (0x2 ^ 0xffffffff); + + case TYPE_MULTI_NOT_VERY_EFF: + if (*moveStatusMask & MOVE_STATUS_SUPER_EFFECTIVE) { + *moveStatusMask &= ~MOVE_STATUS_SUPER_EFFECTIVE; } else { - param1[0] |= 0x4; + *moveStatusMask |= MOVE_STATUS_NOT_VERY_EFFECTIVE; } break; - case 20: - if (param1[0] & 0x4) { - param1[0] &= (0x4 ^ 0xffffffff); + + case TYPE_MULTI_SUPER_EFF: + if (*moveStatusMask & MOVE_STATUS_NOT_VERY_EFFECTIVE) { + *moveStatusMask &= ~MOVE_STATUS_NOT_VERY_EFFECTIVE; } else { - param1[0] |= 0x2; + *moveStatusMask |= MOVE_STATUS_SUPER_EFFECTIVE; } break; } - - return; } BOOL BattleContext_MoveFailed (BattleContext * param0, int param1) @@ -3854,7 +3893,7 @@ int BattleSystem_TriggerEffectOnSwitch(BattleSystem *battleSys, BattleContext *b if (move) { effectiveness = 0; - battleCtx->damage = BattleSystem_CheckTypeChart(battleSys, battleCtx, move, NULL, j, battler, battleCtx->damage, &effectiveness); + battleCtx->damage = BattleSystem_ApplyTypeChart(battleSys, battleCtx, move, NULL, j, battler, battleCtx->damage, &effectiveness); if ((effectiveness & MOVE_STATUS_INEFFECTIVE) == FALSE && MoveCannotTriggerAnticipation(battleCtx, move) == FALSE @@ -6499,7 +6538,7 @@ static const ItemEffectTypePair sTypeBoostingItems[] = { { HOLD_EFFECT_ARCEUS_ELECTRIC, TYPE_ELECTRIC }, { HOLD_EFFECT_ARCEUS_GRASS, TYPE_GRASS }, { HOLD_EFFECT_ARCEUS_ICE, TYPE_ICE }, - { HOLD_EFFECT_ARCEUS_FIGHT, TYPE_FIGHTING }, + { HOLD_EFFECT_ARCEUS_FIGHTING, TYPE_FIGHTING }, { HOLD_EFFECT_ARCEUS_POISON, TYPE_POISON }, { HOLD_EFFECT_ARCEUS_GROUND, TYPE_GROUND }, { HOLD_EFFECT_ARCEUS_FLYING, TYPE_FLYING }, @@ -7486,59 +7525,82 @@ static int MapSideEffectToSubscript(BattleContext *battleCtx, enum SideEffectTyp return sSideEffectSubscripts[effect & MOVE_SIDE_EFFECT_SUBSCRIPT_POINTER]; } -static int ov16_0225B63C (BattleContext * param0, int param1, int param2, int param3, int param4, u32 * param5) +/** + * @brief Apply the type multiplier to the given damage value and update the + * move status mask. + * + * @param battleCtx + * @param attacker + * @param mul + * @param damage + * @param update If TRUE, the move status mask will be updated + * @param[out] moveStatus + * @return The resulting damage value after applying type effectiveness + */ +static int ApplyTypeMultiplier(BattleContext *battleCtx, int attacker, int mul, int damage, BOOL update, u32 *moveStatus) { - if (((param0->battleStatusMask & 0x800) == 0) && ((param0->battleStatusMask & 0x8000) == 0) && (param3)) { - param3 = BattleSystem_Divide(param3 * param2, 10); + if ((battleCtx->battleStatusMask & SYSCTL_IGNORE_TYPE_CHECKS) == FALSE + && (battleCtx->battleStatusMask & SYSCTL_IGNORE_IMMUNITIES) == FALSE + && damage) { + damage = BattleSystem_Divide(damage * mul, 10); } - switch (param2) { - case 0: - param5[0] |= 0x8; - param5[0] &= (0x4 ^ 0xffffffff); - param5[0] &= (0x2 ^ 0xffffffff); + switch (mul) { + case TYPE_MULTI_IMMUNE: + *moveStatus |= MOVE_STATUS_INEFFECTIVE; + *moveStatus &= ~MOVE_STATUS_NOT_VERY_EFFECTIVE; + *moveStatus &= ~MOVE_STATUS_SUPER_EFFECTIVE; break; - case 5: - if (param4) { - if (param5[0] & 0x2) { - param5[0] &= (0x2 ^ 0xffffffff); + + case TYPE_MULTI_NOT_VERY_EFF: + if (update) { + if (*moveStatus & MOVE_STATUS_SUPER_EFFECTIVE) { + *moveStatus &= ~MOVE_STATUS_SUPER_EFFECTIVE; } else { - param5[0] |= 0x4; + *moveStatus |= MOVE_STATUS_NOT_VERY_EFFECTIVE; } } break; - case 20: - if (param4) { - if (param5[0] & 0x4) { - param5[0] &= (0x4 ^ 0xffffffff); + + case TYPE_MULTI_SUPER_EFF: + if (update) { + if (*moveStatus & MOVE_STATUS_NOT_VERY_EFFECTIVE) { + *moveStatus &= ~MOVE_STATUS_NOT_VERY_EFFECTIVE; } else { - param5[0] |= 0x2; + *moveStatus |= MOVE_STATUS_SUPER_EFFECTIVE; } } break; } - return param3; + return damage; } -static BOOL ov16_0225B6C8 (BattleContext * param0, int param1) +/** + * @brief Checks if a move is on its damaging turn. + * + * @param battleCtx + * @param move + * @return TRUE if the move is on its damaging turn, FALSE otherwise + */ +static BOOL MoveIsOnDamagingTurn(BattleContext *battleCtx, int move) { - switch (param0->aiContext.moveTable[param1].effect) { - case 26: - case 39: - case 75: - case 145: - case 151: - case 155: - case 255: - case 256: - case 263: - case 273: - return param0->battleStatusMask & 0x200; + switch (MOVE_DATA(move).effect) { + case BATTLE_EFFECT_BIDE: + case BATTLE_EFFECT_CHARGE_TURN_HIGH_CRIT: + case BATTLE_EFFECT_CHARGE_TURN_HIGH_CRIT_FLINCH: + case BATTLE_EFFECT_CHARGE_TURN_DEF_UP: + case BATTLE_EFFECT_SKIP_CHARGE_TURN_IN_SUN: + case BATTLE_EFFECT_FLY: + case BATTLE_EFFECT_DIVE: + case BATTLE_EFFECT_DIG: + case BATTLE_EFFECT_BOUNCE: + case BATTLE_EFFECT_FLINCH_BURN_HIT: // BUG: This entry should be Shadow Force, but is instead Fire Fang + return battleCtx->battleStatusMask & SYSCTL_LAST_OF_MULTI_TURN; break; } - return 1; + return TRUE; } /** @@ -7585,7 +7647,7 @@ static u8 Battler_MonType(BattleContext *battleCtx, int battler, enum BattleMonP type = TYPE_ICE; break; - case HOLD_EFFECT_ARCEUS_FIGHT: + case HOLD_EFFECT_ARCEUS_FIGHTING: type = TYPE_FIGHTING; break; @@ -7736,104 +7798,122 @@ static BOOL MoveCannotTriggerAnticipation(BattleContext *battleCtx, int move) return FALSE; } -static int BattleMove_Type (BattleSystem * param0, BattleContext * param1, int param2, int param3) +/** + * @brief Compute the type of a move which has variable typing. + * + * @param battleSys + * @param battleCtx + * @param item The attacker's held item. Affects the typing of Natural Gift + * and Judgment. + * @param move The move being used. + * @return The variable-type of the given move. + */ +static int CalcMoveType(BattleSystem *battleSys, BattleContext *battleCtx, int item, int move) { - int v0; + int type; - switch (param3) { - case 363: - v0 = Battler_NaturalGiftType(param1, param2); + switch (move) { + case MOVE_NATURAL_GIFT: + type = Battler_NaturalGiftType(battleCtx, item); break; - case 449: - switch (Battler_HeldItemEffect(param1, param2)) { - case 131: - v0 = 1; + + case MOVE_JUDGMENT: + switch (Battler_HeldItemEffect(battleCtx, item)) { + case HOLD_EFFECT_ARCEUS_FIGHTING: + type = TYPE_FIGHTING; break; - case 134: - v0 = 2; + case HOLD_EFFECT_ARCEUS_FLYING: + type = TYPE_FLYING; break; - case 132: - v0 = 3; + case HOLD_EFFECT_ARCEUS_POISON: + type = TYPE_POISON; break; - case 133: - v0 = 4; + case HOLD_EFFECT_ARCEUS_GROUND: + type = TYPE_GROUND; break; - case 137: - v0 = 5; + case HOLD_EFFECT_ARCEUS_ROCK: + type = TYPE_ROCK; break; - case 136: - v0 = 6; + case HOLD_EFFECT_ARCEUS_BUG: + type = TYPE_BUG; break; - case 138: - v0 = 7; + case HOLD_EFFECT_ARCEUS_GHOST: + type = TYPE_GHOST; break; - case 141: - v0 = 8; + case HOLD_EFFECT_ARCEUS_STEEL: + type = TYPE_STEEL; break; - case 126: - v0 = 10; + case HOLD_EFFECT_ARCEUS_FIRE: + type = TYPE_FIRE; break; - case 127: - v0 = 11; + case HOLD_EFFECT_ARCEUS_WATER: + type = TYPE_WATER; break; - case 129: - v0 = 12; + case HOLD_EFFECT_ARCEUS_GRASS: + type = TYPE_GRASS; break; - case 128: - v0 = 13; + case HOLD_EFFECT_ARCEUS_ELECTRIC: + type = TYPE_ELECTRIC; break; - case 135: - v0 = 14; + case HOLD_EFFECT_ARCEUS_PSYCHIC: + type = TYPE_PSYCHIC; break; - case 130: - v0 = 15; + case HOLD_EFFECT_ARCEUS_ICE: + type = TYPE_ICE; break; - case 139: - v0 = 16; + case HOLD_EFFECT_ARCEUS_DRAGON: + type = TYPE_DRAGON; break; - case 140: - v0 = 17; + case HOLD_EFFECT_ARCEUS_DARK: + type = TYPE_DARK; break; default: - v0 = 0; + type = TYPE_NORMAL; break; } break; - case 237: - v0 = ((param1->battleMons[param2].hpIV & 1) >> 0) | ((param1->battleMons[param2].attackIV & 1) << 1) | ((param1->battleMons[param2].defenseIV & 1) << 2) | ((param1->battleMons[param2].speedIV & 1) << 3) | ((param1->battleMons[param2].spAttackIV & 1) << 4) | ((param1->battleMons[param2].spDefenseIV & 1) << 5); - v0 = (v0 * 15 / 63) + 1; - if (v0 >= 9) { - v0++; + case MOVE_HIDDEN_POWER: + type = ((battleCtx->battleMons[item].hpIV & 1) >> 0) + | ((battleCtx->battleMons[item].attackIV & 1) << 1) + | ((battleCtx->battleMons[item].defenseIV & 1) << 2) + | ((battleCtx->battleMons[item].speedIV & 1) << 3) + | ((battleCtx->battleMons[item].spAttackIV & 1) << 4) + | ((battleCtx->battleMons[item].spDefenseIV & 1) << 5); + type = (type * 15 / 63) + 1; + + if (type >= TYPE_MYSTERY) { + type++; } break; - case 311: - if ((BattleSystem_CountAbility(param0, param1, 8, 0, 13) == 0) && (BattleSystem_CountAbility(param0, param1, 8, 0, 76) == 0)) { - if (param1->fieldConditionsMask & (0x3 | 0xc | 0x30 | 0xc0 | 0x8000)) { - if (param1->fieldConditionsMask & 0x3) { - v0 = 11; - } - if (param1->fieldConditionsMask & 0xc) { - v0 = 5; - } + case MOVE_WEATHER_BALL: + if (NO_CLOUD_NINE + && battleCtx->fieldConditionsMask & FIELD_CONDITION_WEATHER) { + if (WEATHER_IS_RAIN) { + type = TYPE_WATER; + } - if (param1->fieldConditionsMask & 0x30) { - v0 = 10; - } + if (WEATHER_IS_SAND) { + type = TYPE_ROCK; + } - if (param1->fieldConditionsMask & 0xc0) { - v0 = 15; - } + if (WEATHER_IS_SUN) { + type = TYPE_FIRE; + } + + if (WEATHER_IS_HAIL) { + type = TYPE_ICE; } } break; + default: - v0 = 0; + type = TYPE_NORMAL; break; } - return v0; + return type; } int ov16_0225BA88 (BattleSystem * param0, int param1) @@ -7905,7 +7985,7 @@ int ov16_0225BA88 (BattleSystem * param0, int param1) if (v8) { v16 = 0; - ov16_022552D4(v20, v8, v9, Pokemon_GetValue(v19, MON_DATA_ABILITY, NULL), Battler_Ability(v20, v2), Battler_HeldItemEffect(v20, v2), BattleMon_Get(v20, v2, 27, NULL), BattleMon_Get(v20, v2, 28, NULL), &v16); + BattleSystem_CalcEffectiveness(v20, v8, v9, Pokemon_GetValue(v19, MON_DATA_ABILITY, NULL), Battler_Ability(v20, v2), Battler_HeldItemEffect(v20, v2), BattleMon_Get(v20, v2, 27, NULL), BattleMon_Get(v20, v2, 28, NULL), &v16); if (v16 & 0x2) { break; @@ -7938,7 +8018,7 @@ int ov16_0225BA88 (BattleSystem * param0, int param1) if ((v8) && (v20->aiContext.moveTable[v8].power != 1)) { v11 = BattleSystem_CalcMoveDamage(param0, v20, v8, v20->sideConditionsMask[Battler_Side(param0, v2)], v20->fieldConditionsMask, 0, 0, param1, v2, 1); v16 = 0; - v11 = BattleSystem_CheckTypeChart(param0, v20, v8, v9, param1, v2, v11, &v16); + v11 = BattleSystem_ApplyTypeChart(param0, v20, v8, v9, param1, v2, v11, &v16); if (v16 & (0x8 | 0x800 | 0x100000 | 0x40000)) { v11 = 0; diff --git a/src/overlay016/ov16_0226485C.c b/src/overlay016/ov16_0226485C.c index fcba899710..85572dd699 100644 --- a/src/overlay016/ov16_0226485C.c +++ b/src/overlay016/ov16_0226485C.c @@ -554,7 +554,7 @@ void BattleIO_SlideHPGaugeIn (BattleSystem * param0, BattleContext * param1, int v0.unk_02 = param1->battleMons[param2].curHP; v0.unk_04 = param1->battleMons[param2].maxHP; v0.unk_06 = param1->selectedPartySlot[param2]; - v0.unk_07_0 = ov16_02253F7C(param1, param2); + v0.unk_07_0 = Battler_StatusCondition(param1, param2); if (((param1->battleMons[param2].species == 29) || (param1->battleMons[param2].species == 32)) && (param1->battleMons[param2].hasNickname == 0)) { v0.unk_07_5 = 2; @@ -1241,7 +1241,7 @@ void BattleIO_RefreshHPGauge (BattleSystem * param0, BattleContext * param1, int v0.unk_02 = param1->battleMons[param2].curHP; v0.unk_04 = param1->battleMons[param2].maxHP; v0.unk_06 = param1->selectedPartySlot[param2]; - v0.unk_07_0 = ov16_02253F7C(param1, param2); + v0.unk_07_0 = Battler_StatusCondition(param1, param2); if (((param1->battleMons[param2].species == 29) || (param1->battleMons[param2].species == 32)) && (param1->battleMons[param2].hasNickname == 0)) { v0.unk_07_5 = 2; diff --git a/src/pokemon.c b/src/pokemon.c index 9d459272e0..c54f8f8621 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -3881,7 +3881,7 @@ u8 Pokemon_GetArceusTypeOf(u16 itemHoldEffect) case HOLD_EFFECT_ARCEUS_ICE: type = TYPE_ICE; break; - case HOLD_EFFECT_ARCEUS_FIGHT: + case HOLD_EFFECT_ARCEUS_FIGHTING: type = TYPE_FIGHTING; break; case HOLD_EFFECT_ARCEUS_POISON: