Add damage context to effectiveness multiplier (#7111)
This commit is contained in:
parent
e1b542944f
commit
d4146afcae
@ -282,7 +282,7 @@ u32 CalcFuryCutterBasePower(u32 basePower, u32 furyCutterCounter);
|
||||
s32 CalculateMoveDamage(struct DamageContext *ctx);
|
||||
s32 CalculateMoveDamageVars(struct DamageContext *ctx);
|
||||
s32 ApplyModifiersAfterDmgRoll(struct DamageContext *ctx, s32 dmg);
|
||||
uq4_12_t CalcTypeEffectivenessMultiplier(u32 move, u32 moveType, u32 battlerAtk, u32 battlerDef, u32 defAbility, bool32 recordAbilities);
|
||||
uq4_12_t CalcTypeEffectivenessMultiplier(struct DamageContext *ctx);
|
||||
uq4_12_t CalcPartyMonTypeEffectivenessMultiplier(u16 move, u16 speciesDef, u16 abilityDef);
|
||||
uq4_12_t GetTypeModifier(u32 atkType, u32 defType);
|
||||
uq4_12_t GetOverworldTypeEffectiveness(struct Pokemon *mon, u8 moveType);
|
||||
|
||||
@ -423,12 +423,20 @@ bool32 IsAffectedByPowder(u32 battler, u32 ability, enum ItemHoldEffect holdEffe
|
||||
// Consider a pokemon boosting their attack against a ghost pokemon having only normal-type physical attacks.
|
||||
bool32 MovesWithCategoryUnusable(u32 attacker, u32 target, u32 category)
|
||||
{
|
||||
s32 i, moveType;
|
||||
u32 usable = 0;
|
||||
u16 *moves = GetMovesArray(attacker);
|
||||
u32 moveLimitations = gAiLogicData->moveLimitations[attacker];
|
||||
|
||||
for (i = 0; i < MAX_MON_MOVES; i++)
|
||||
struct DamageContext ctx = {0};
|
||||
ctx.battlerAtk = attacker;
|
||||
ctx.battlerDef = target;
|
||||
ctx.updateFlags = FALSE;
|
||||
ctx.abilityAtk = gAiLogicData->abilities[attacker];
|
||||
ctx.abilityDef = gAiLogicData->abilities[target];
|
||||
ctx.holdEffectAtk = gAiLogicData->items[attacker];
|
||||
ctx.holdEffectDef = gAiLogicData->items[target];
|
||||
|
||||
for (u32 i = 0; i < MAX_MON_MOVES; i++)
|
||||
{
|
||||
if (IsMoveUnusable(i, moves[i], moveLimitations))
|
||||
continue;
|
||||
@ -436,8 +444,10 @@ bool32 MovesWithCategoryUnusable(u32 attacker, u32 target, u32 category)
|
||||
if (GetBattleMoveCategory(moves[i]) == category)
|
||||
{
|
||||
SetTypeBeforeUsingMove(moves[i], attacker);
|
||||
moveType = GetBattleMoveType(moves[i]);
|
||||
if (CalcTypeEffectivenessMultiplier(moves[i], moveType, attacker, target, gAiLogicData->abilities[target], FALSE) != 0)
|
||||
ctx.move = moves[i];
|
||||
ctx.moveType = GetBattleMoveType(moves[i]);
|
||||
|
||||
if (CalcTypeEffectivenessMultiplier(&ctx))
|
||||
usable |= 1u << i;
|
||||
}
|
||||
}
|
||||
@ -481,83 +491,83 @@ static inline s32 DmgRoll(s32 dmg)
|
||||
return dmg;
|
||||
}
|
||||
|
||||
bool32 IsDamageMoveUnusable(u32 battlerAtk, u32 battlerDef, u32 move, u32 moveType, uq4_12_t effectiveness, u32 weather)
|
||||
bool32 IsDamageMoveUnusable(struct DamageContext *ctx)
|
||||
{
|
||||
u32 battlerDefAbility;
|
||||
u32 partnerDefAbility;
|
||||
struct AiLogicData *aiData = gAiLogicData;
|
||||
|
||||
if (effectiveness == UQ_4_12(0.0))
|
||||
if (ctx->typeEffectivenessModifier == UQ_4_12(0.0))
|
||||
return TRUE;
|
||||
if (gBattleStruct->battlerState[battlerDef].commandingDondozo)
|
||||
if (gBattleStruct->battlerState[ctx->battlerDef].commandingDondozo)
|
||||
return TRUE;
|
||||
|
||||
// aiData->abilities does not check for Mold Breaker since it happens during combat so it needs to be done manually
|
||||
if (IsMoldBreakerTypeAbility(battlerAtk, aiData->abilities[battlerAtk]) || MoveIgnoresTargetAbility(move))
|
||||
if (IsMoldBreakerTypeAbility(ctx->battlerAtk, ctx->abilityAtk) || MoveIgnoresTargetAbility(ctx->move))
|
||||
{
|
||||
battlerDefAbility = ABILITY_NONE;
|
||||
partnerDefAbility = ABILITY_NONE;
|
||||
}
|
||||
else
|
||||
{
|
||||
battlerDefAbility = aiData->abilities[battlerDef];
|
||||
partnerDefAbility = aiData->abilities[BATTLE_PARTNER(battlerDef)];
|
||||
battlerDefAbility = ctx->abilityDef;
|
||||
partnerDefAbility = aiData->abilities[BATTLE_PARTNER(ctx->battlerDef)];
|
||||
}
|
||||
|
||||
if (CanAbilityBlockMove(battlerAtk, battlerDef, aiData->abilities[battlerAtk], battlerDefAbility, move, ABILITY_CHECK_TRIGGER))
|
||||
if (CanAbilityBlockMove(ctx->battlerAtk, ctx->battlerDef, ctx->abilityAtk, battlerDefAbility, ctx->move, ABILITY_CHECK_TRIGGER))
|
||||
return TRUE;
|
||||
|
||||
if (CanAbilityAbsorbMove(battlerAtk, battlerDef, battlerDefAbility, move, moveType, ABILITY_CHECK_TRIGGER))
|
||||
if (CanAbilityAbsorbMove(ctx->battlerAtk, ctx->battlerDef, battlerDefAbility, ctx->move, ctx->moveType, ABILITY_CHECK_TRIGGER))
|
||||
return TRUE;
|
||||
|
||||
// Limited to Lighning Rod and Storm Drain because otherwise the AI would consider Water Absorb, etc...
|
||||
if (partnerDefAbility == ABILITY_LIGHTNING_ROD || partnerDefAbility == ABILITY_STORM_DRAIN)
|
||||
{
|
||||
if (CanAbilityAbsorbMove(battlerAtk, BATTLE_PARTNER(battlerDef), partnerDefAbility, move, moveType, ABILITY_CHECK_TRIGGER))
|
||||
if (CanAbilityAbsorbMove(ctx->battlerAtk, BATTLE_PARTNER(ctx->battlerDef), partnerDefAbility, ctx->move, ctx->moveType, ABILITY_CHECK_TRIGGER))
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (HasWeatherEffect())
|
||||
{
|
||||
if (weather & B_WEATHER_SUN_PRIMAL && moveType == TYPE_WATER)
|
||||
if (ctx->weather & B_WEATHER_SUN_PRIMAL && ctx->moveType == TYPE_WATER)
|
||||
return TRUE;
|
||||
if (weather & B_WEATHER_RAIN_PRIMAL && moveType == TYPE_FIRE)
|
||||
if (ctx->weather & B_WEATHER_RAIN_PRIMAL && ctx->moveType == TYPE_FIRE)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
switch (GetMoveEffect(move))
|
||||
switch (GetMoveEffect(ctx->move))
|
||||
{
|
||||
case EFFECT_DREAM_EATER:
|
||||
if (!AI_IsBattlerAsleepOrComatose(battlerDef))
|
||||
if (!AI_IsBattlerAsleepOrComatose(ctx->battlerDef))
|
||||
return TRUE;
|
||||
break;
|
||||
case EFFECT_BELCH:
|
||||
if (IsBelchPreventingMove(battlerAtk, move))
|
||||
if (IsBelchPreventingMove(ctx->battlerAtk, ctx->move))
|
||||
return TRUE;
|
||||
break;
|
||||
case EFFECT_LAST_RESORT:
|
||||
if (!CanUseLastResort(battlerAtk))
|
||||
if (!CanUseLastResort(ctx->battlerAtk))
|
||||
return TRUE;
|
||||
break;
|
||||
case EFFECT_LOW_KICK:
|
||||
case EFFECT_HEAT_CRASH:
|
||||
if (GetActiveGimmick(battlerDef) == GIMMICK_DYNAMAX)
|
||||
if (GetActiveGimmick(ctx->battlerDef) == GIMMICK_DYNAMAX)
|
||||
return TRUE;
|
||||
break;
|
||||
case EFFECT_FAIL_IF_NOT_ARG_TYPE:
|
||||
if (!IS_BATTLER_OF_TYPE(battlerAtk, GetMoveArgType(move)))
|
||||
if (!IS_BATTLER_OF_TYPE(ctx->battlerAtk, GetMoveArgType(ctx->move)))
|
||||
return TRUE;
|
||||
break;
|
||||
case EFFECT_HIT_SET_REMOVE_TERRAIN:
|
||||
if (!(gFieldStatuses & STATUS_FIELD_TERRAIN_ANY) && GetMoveEffectArg_MoveProperty(move) == ARG_TRY_REMOVE_TERRAIN_FAIL)
|
||||
if (!(gFieldStatuses & STATUS_FIELD_TERRAIN_ANY) && GetMoveEffectArg_MoveProperty(ctx->move) == ARG_TRY_REMOVE_TERRAIN_FAIL)
|
||||
return TRUE;
|
||||
break;
|
||||
case EFFECT_POLTERGEIST:
|
||||
if (gAiLogicData->items[battlerDef] == ITEM_NONE || !IsBattlerItemEnabled(battlerDef))
|
||||
if (gAiLogicData->items[ctx->battlerDef] == ITEM_NONE || !IsBattlerItemEnabled(ctx->battlerDef))
|
||||
return TRUE;
|
||||
break;
|
||||
case EFFECT_FIRST_TURN_ONLY:
|
||||
if (!gDisableStructs[battlerAtk].isFirstTurn)
|
||||
if (!gDisableStructs[ctx->battlerAtk].isFirstTurn)
|
||||
return TRUE;
|
||||
break;
|
||||
case EFFECT_EXPLOSION:
|
||||
@ -733,9 +743,7 @@ static inline bool32 ShouldCalcCritDamage(u32 battlerAtk, u32 battlerDef, u32 mo
|
||||
struct SimulatedDamage AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, uq4_12_t *typeEffectiveness, enum AIConsiderGimmick considerGimmickAtk, enum AIConsiderGimmick considerGimmickDef, u32 weather)
|
||||
{
|
||||
struct SimulatedDamage simDamage;
|
||||
s32 moveType;
|
||||
enum BattleMoveEffects moveEffect = GetMoveEffect(move);
|
||||
uq4_12_t effectivenessMultiplier;
|
||||
bool32 isDamageMoveUnusable = FALSE;
|
||||
bool32 toggledGimmickAtk = FALSE;
|
||||
bool32 toggledGimmickDef = FALSE;
|
||||
@ -766,36 +774,32 @@ struct SimulatedDamage AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u
|
||||
|
||||
SetDynamicMoveCategory(battlerAtk, battlerDef, move);
|
||||
SetTypeBeforeUsingMove(move, battlerAtk);
|
||||
moveType = GetBattleMoveType(move);
|
||||
effectivenessMultiplier = CalcTypeEffectivenessMultiplier(move, moveType, battlerAtk, battlerDef, aiData->abilities[battlerDef], FALSE);
|
||||
|
||||
struct DamageContext ctx;
|
||||
ctx.battlerAtk = battlerAtk;
|
||||
ctx.battlerDef = battlerDef;
|
||||
ctx.move = move;
|
||||
ctx.moveType = GetBattleMoveType(move);
|
||||
ctx.isCrit = ShouldCalcCritDamage(battlerAtk, battlerDef, move, aiData);
|
||||
ctx.randomFactor = FALSE;
|
||||
ctx.updateFlags = FALSE;
|
||||
ctx.weather = weather;
|
||||
ctx.fixedBasePower = SetFixedMoveBasePower(battlerAtk, move);
|
||||
ctx.abilityAtk = aiData->abilities[battlerAtk];
|
||||
ctx.abilityDef = aiData->abilities[battlerDef];
|
||||
ctx.holdEffectAtk = aiData->holdEffects[battlerAtk];
|
||||
ctx.holdEffectDef = aiData->holdEffects[battlerDef];
|
||||
ctx.typeEffectivenessModifier = CalcTypeEffectivenessMultiplier(&ctx);
|
||||
|
||||
u32 movePower = GetMovePower(move);
|
||||
if (movePower)
|
||||
isDamageMoveUnusable = IsDamageMoveUnusable(battlerAtk, battlerDef, move, moveType, effectivenessMultiplier, weather);
|
||||
isDamageMoveUnusable = IsDamageMoveUnusable(&ctx);
|
||||
|
||||
if (movePower && !isDamageMoveUnusable)
|
||||
{
|
||||
u32 types[3];
|
||||
AI_StoreBattlerTypes(battlerAtk, types);
|
||||
|
||||
ProteanTryChangeType(battlerAtk, aiData->abilities[battlerAtk], move, moveType);
|
||||
s32 fixedBasePower = SetFixedMoveBasePower(battlerAtk, move);
|
||||
|
||||
struct DamageContext ctx;
|
||||
ctx.battlerAtk = battlerAtk;
|
||||
ctx.battlerDef = battlerDef;
|
||||
ctx.move = move;
|
||||
ctx.moveType = moveType;
|
||||
ctx.isCrit = ShouldCalcCritDamage(battlerAtk, battlerDef, move, aiData);
|
||||
ctx.randomFactor = FALSE;
|
||||
ctx.updateFlags = FALSE;
|
||||
ctx.weather = weather;
|
||||
ctx.fixedBasePower = fixedBasePower;
|
||||
ctx.typeEffectivenessModifier = effectivenessMultiplier;
|
||||
ctx.abilityAtk = aiData->abilities[battlerAtk];
|
||||
ctx.abilityDef = aiData->abilities[battlerDef];
|
||||
ctx.holdEffectAtk = aiData->holdEffects[battlerAtk];
|
||||
ctx.holdEffectDef = aiData->holdEffects[battlerDef];
|
||||
ProteanTryChangeType(battlerAtk, aiData->abilities[battlerAtk], move, ctx.moveType);
|
||||
|
||||
if (moveEffect == EFFECT_TRIPLE_KICK)
|
||||
{
|
||||
@ -842,7 +846,7 @@ struct SimulatedDamage AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u
|
||||
}
|
||||
|
||||
// convert multiper to AI_EFFECTIVENESS_xX
|
||||
*typeEffectiveness = effectivenessMultiplier;
|
||||
*typeEffectiveness = ctx.typeEffectivenessModifier;
|
||||
|
||||
// Undo temporary settings
|
||||
gBattleStruct->dynamicMoveType = 0;
|
||||
@ -1137,7 +1141,6 @@ u32 GetCurrDamageHpPercent(u32 battlerAtk, u32 battlerDef, enum DamageCalcContex
|
||||
uq4_12_t AI_GetMoveEffectiveness(u32 move, u32 battlerAtk, u32 battlerDef)
|
||||
{
|
||||
uq4_12_t typeEffectiveness;
|
||||
u32 moveType;
|
||||
|
||||
SaveBattlerData(battlerAtk);
|
||||
SaveBattlerData(battlerDef);
|
||||
@ -1147,8 +1150,17 @@ uq4_12_t AI_GetMoveEffectiveness(u32 move, u32 battlerAtk, u32 battlerDef)
|
||||
|
||||
gBattleStruct->dynamicMoveType = 0;
|
||||
SetTypeBeforeUsingMove(move, battlerAtk);
|
||||
moveType = GetBattleMoveType(move);
|
||||
typeEffectiveness = CalcTypeEffectivenessMultiplier(move, moveType, battlerAtk, battlerDef, gAiLogicData->abilities[battlerDef], FALSE);
|
||||
struct DamageContext ctx = {0};
|
||||
ctx.battlerAtk = battlerAtk;
|
||||
ctx.battlerDef = battlerDef;
|
||||
ctx.move = move;
|
||||
ctx.moveType = GetBattleMoveType(move);
|
||||
ctx.updateFlags = FALSE;
|
||||
ctx.abilityAtk = gAiLogicData->abilities[battlerAtk];
|
||||
ctx.abilityDef = gAiLogicData->abilities[battlerDef];
|
||||
ctx.holdEffectAtk = gAiLogicData->items[battlerAtk];
|
||||
ctx.holdEffectDef = gAiLogicData->items[battlerDef];
|
||||
typeEffectiveness = CalcTypeEffectivenessMultiplier(&ctx);
|
||||
|
||||
RestoreBattlerData(battlerAtk);
|
||||
RestoreBattlerData(battlerDef);
|
||||
|
||||
@ -100,7 +100,7 @@ static void Task_UpdateLvlInHealthbox(u8);
|
||||
static void PrintLinkStandbyMsg(void);
|
||||
|
||||
static void ReloadMoveNames(u32 battler);
|
||||
static u32 CheckTypeEffectiveness(u32 targetId, u32 battler);
|
||||
static u32 CheckTypeEffectiveness(u32 battlerAtk, u32 battlerDef);
|
||||
static u32 CheckTargetTypeEffectiveness(u32 battler);
|
||||
static void MoveSelectionDisplayMoveEffectiveness(u32 foeEffectiveness, u32 battler);
|
||||
|
||||
@ -507,7 +507,7 @@ void HandleInputChooseTarget(u32 battler)
|
||||
break;
|
||||
}
|
||||
if (B_SHOW_EFFECTIVENESS)
|
||||
MoveSelectionDisplayMoveEffectiveness(CheckTypeEffectiveness(GetBattlerPosition(gMultiUsePlayerCursor), battler), battler);
|
||||
MoveSelectionDisplayMoveEffectiveness(CheckTypeEffectiveness(battler, GetBattlerPosition(gMultiUsePlayerCursor)), battler);
|
||||
|
||||
if (gAbsentBattlerFlags & (1u << gMultiUsePlayerCursor)
|
||||
|| !CanTargetBattler(battler, gMultiUsePlayerCursor, move)
|
||||
@ -558,7 +558,7 @@ void HandleInputChooseTarget(u32 battler)
|
||||
break;
|
||||
}
|
||||
if (B_SHOW_EFFECTIVENESS)
|
||||
MoveSelectionDisplayMoveEffectiveness(CheckTypeEffectiveness(GetBattlerPosition(gMultiUsePlayerCursor), battler), battler);
|
||||
MoveSelectionDisplayMoveEffectiveness(CheckTypeEffectiveness(battler, GetBattlerPosition(gMultiUsePlayerCursor)), battler);
|
||||
|
||||
if (gAbsentBattlerFlags & (1u << gMultiUsePlayerCursor)
|
||||
|| !CanTargetBattler(battler, gMultiUsePlayerCursor, move)
|
||||
@ -758,7 +758,7 @@ void HandleInputChooseMove(u32 battler)
|
||||
else
|
||||
gMultiUsePlayerCursor = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT);
|
||||
if (B_SHOW_EFFECTIVENESS)
|
||||
MoveSelectionDisplayMoveEffectiveness(CheckTypeEffectiveness(GetBattlerPosition(gMultiUsePlayerCursor), battler), battler);
|
||||
MoveSelectionDisplayMoveEffectiveness(CheckTypeEffectiveness(battler, GetBattlerPosition(gMultiUsePlayerCursor)), battler);
|
||||
|
||||
gSprites[gBattlerSpriteIds[gMultiUsePlayerCursor]].callback = SpriteCB_ShowAsMoveTarget;
|
||||
break;
|
||||
@ -2435,15 +2435,23 @@ static bool32 ShouldShowTypeEffectiveness(u32 targetId)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static u32 CheckTypeEffectiveness(u32 targetId, u32 battler)
|
||||
static u32 CheckTypeEffectiveness(u32 battlerAtk, u32 battlerDef)
|
||||
{
|
||||
struct ChooseMoveStruct *moveInfo = (struct ChooseMoveStruct *)(&gBattleResources->bufferA[battler][4]);
|
||||
struct Pokemon *mon = GetBattlerMon(battler);
|
||||
u32 move = moveInfo->moves[gMoveSelectionCursor[battler]];
|
||||
u32 moveType = CheckDynamicMoveType(mon, move, battler, MON_IN_BATTLE);
|
||||
uq4_12_t modifier = CalcTypeEffectivenessMultiplier(move, moveType, battler, targetId, GetBattlerAbility(targetId), FALSE);
|
||||
struct ChooseMoveStruct *moveInfo = (struct ChooseMoveStruct *)(&gBattleResources->bufferA[battlerAtk][4]);
|
||||
struct DamageContext ctx = {0};
|
||||
ctx.battlerAtk = battlerAtk;
|
||||
ctx.battlerDef = battlerDef;
|
||||
ctx.move = moveInfo->moves[gMoveSelectionCursor[battlerAtk]];
|
||||
ctx.moveType = CheckDynamicMoveType(GetBattlerMon(battlerAtk), ctx.move, battlerAtk, MON_IN_BATTLE);
|
||||
ctx.updateFlags = FALSE;
|
||||
ctx.abilityAtk = GetBattlerAbility(battlerAtk);
|
||||
ctx.abilityDef = GetBattlerAbility(battlerDef);
|
||||
ctx.holdEffectAtk = GetBattlerHoldEffect(battlerAtk, TRUE);
|
||||
ctx.holdEffectDef = GetBattlerHoldEffect(battlerDef, TRUE);
|
||||
|
||||
if (!ShouldShowTypeEffectiveness(targetId))
|
||||
uq4_12_t modifier = CalcTypeEffectivenessMultiplier(&ctx);
|
||||
|
||||
if (!ShouldShowTypeEffectiveness(battlerDef))
|
||||
return EFFECTIVENESS_CANNOT_VIEW;
|
||||
|
||||
if (modifier == UQ_4_12(0.0))
|
||||
@ -2458,12 +2466,12 @@ static u32 CheckTypeEffectiveness(u32 targetId, u32 battler)
|
||||
static u32 CheckTargetTypeEffectiveness(u32 battler)
|
||||
{
|
||||
u32 battlerFoe = BATTLE_OPPOSITE(GetBattlerPosition(battler));
|
||||
u32 foeEffectiveness = CheckTypeEffectiveness(battlerFoe, battler);
|
||||
u32 foeEffectiveness = CheckTypeEffectiveness(battler, battlerFoe);
|
||||
|
||||
if (IsDoubleBattle())
|
||||
{
|
||||
u32 partnerFoe = BATTLE_PARTNER(battlerFoe);
|
||||
u32 partnerFoeEffectiveness = CheckTypeEffectiveness(partnerFoe, battler);
|
||||
u32 partnerFoeEffectiveness = CheckTypeEffectiveness(battler, partnerFoe);
|
||||
if (!IsBattlerAlive(battlerFoe))
|
||||
return partnerFoeEffectiveness;
|
||||
if (IsBattlerAlive(battlerFoe) && IsBattlerAlive(partnerFoe)
|
||||
|
||||
@ -1715,13 +1715,15 @@ static void AccuracyCheck(bool32 recalcDragonDarts, const u8 *nextInstr, const u
|
||||
if (JumpIfMoveAffectedByProtect(move, battlerDef, FALSE, failInstr) || AccuracyCalcHelper(move, battlerDef))
|
||||
continue;
|
||||
|
||||
u32 abilityDef = GetBattlerAbility(battlerDef);
|
||||
u32 holdEffectDef = GetBattlerHoldEffect(battlerDef, TRUE);
|
||||
u32 accuracy = GetTotalAccuracy(gBattlerAttacker,
|
||||
battlerDef,
|
||||
move,
|
||||
abilityAtk,
|
||||
GetBattlerAbility(battlerDef),
|
||||
abilityDef,
|
||||
holdEffectAtk,
|
||||
GetBattlerHoldEffect(battlerDef, TRUE));
|
||||
holdEffectDef);
|
||||
|
||||
if (!RandomPercentage(RNG_ACCURACY, accuracy))
|
||||
{
|
||||
@ -1746,7 +1748,20 @@ static void AccuracyCheck(bool32 recalcDragonDarts, const u8 *nextInstr, const u
|
||||
}
|
||||
|
||||
if (GetMovePower(move) != 0)
|
||||
CalcTypeEffectivenessMultiplier(move, moveType, gBattlerAttacker, battlerDef, GetBattlerAbility(battlerDef), TRUE);
|
||||
{
|
||||
struct DamageContext ctx = {0};
|
||||
ctx.battlerAtk = gBattlerAttacker;
|
||||
ctx.battlerDef = battlerDef;
|
||||
ctx.move = move;
|
||||
ctx.moveType = moveType;
|
||||
ctx.updateFlags = TRUE;
|
||||
ctx.abilityAtk = abilityAtk;
|
||||
ctx.abilityDef = abilityDef;
|
||||
ctx.holdEffectAtk = holdEffectAtk;
|
||||
ctx.holdEffectDef = holdEffectDef;
|
||||
|
||||
CalcTypeEffectivenessMultiplier(&ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2126,8 +2141,18 @@ static void Cmd_typecalc(void)
|
||||
|
||||
if (!IsSpreadMove(GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove))) // Handled in CANCELLER_MULTI_TARGET_MOVES for Spread Moves
|
||||
{
|
||||
u32 moveType = GetBattleMoveType(gCurrentMove);
|
||||
CalcTypeEffectivenessMultiplier(gCurrentMove, moveType, gBattlerAttacker, gBattlerTarget, GetBattlerAbility(gBattlerTarget), TRUE);
|
||||
struct DamageContext ctx = {0};
|
||||
ctx.battlerAtk = gBattlerAttacker;
|
||||
ctx.battlerDef = gBattlerTarget;
|
||||
ctx.move = gCurrentMove;
|
||||
ctx.moveType = GetBattleMoveType(gCurrentMove);
|
||||
ctx.updateFlags = TRUE;
|
||||
ctx.abilityAtk = GetBattlerAbility(gBattlerAttacker);
|
||||
ctx.abilityDef = GetBattlerAbility(gBattlerTarget);
|
||||
ctx.holdEffectAtk = GetBattlerHoldEffect(gBattlerAttacker, TRUE);
|
||||
ctx.holdEffectDef = GetBattlerHoldEffect(gBattlerTarget, TRUE);
|
||||
|
||||
CalcTypeEffectivenessMultiplier(&ctx);
|
||||
}
|
||||
|
||||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||||
|
||||
@ -204,6 +204,24 @@ static const struct BattleWeatherInfo sBattleWeatherInfo[BATTLE_WEATHER_COUNT] =
|
||||
},
|
||||
};
|
||||
|
||||
// Helper function for actual dmg calcs during battle. For simulated AI dmg, CalcTypeEffectivenessMultiplier should be used directly
|
||||
// This should stay a static function. Ideally everything else is handled through CalcTypeEffectivenessMultiplier just like AI
|
||||
static uq4_12_t CalcTypeEffectivenessMultiplierHelper(u32 move, u32 moveType, u32 battlerAtk, u32 battlerDef, u32 defAbility, bool32 recordAbilities)
|
||||
{
|
||||
struct DamageContext ctx = {0};
|
||||
ctx.battlerAtk = battlerAtk;
|
||||
ctx.battlerDef = battlerDef;
|
||||
ctx.move = move;
|
||||
ctx.moveType = GetBattleMoveType(gCurrentMove);
|
||||
ctx.updateFlags = recordAbilities;
|
||||
ctx.abilityAtk = GetBattlerAbility(battlerAtk);
|
||||
ctx.abilityDef = defAbility;
|
||||
ctx.holdEffectAtk = GetBattlerHoldEffect(battlerAtk, TRUE);
|
||||
ctx.holdEffectDef = GetBattlerHoldEffect(battlerDef, TRUE);
|
||||
|
||||
return CalcTypeEffectivenessMultiplier(&ctx);
|
||||
}
|
||||
|
||||
u32 GetCurrentBattleWeather(void)
|
||||
{
|
||||
u32 currBattleWeather = 0xFF;
|
||||
@ -2479,7 +2497,7 @@ static void CancellerMultiTargetMoves(u32 *effect)
|
||||
}
|
||||
else
|
||||
{
|
||||
CalcTypeEffectivenessMultiplier(gCurrentMove, GetBattleMoveType(gCurrentMove), gBattlerAttacker, battlerDef, GetBattlerAbility(battlerDef), TRUE);
|
||||
CalcTypeEffectivenessMultiplierHelper(gCurrentMove, GetBattleMoveType(gCurrentMove), gBattlerAttacker, battlerDef, GetBattlerAbility(battlerDef), TRUE);
|
||||
}
|
||||
}
|
||||
if (moveTarget == MOVE_TARGET_BOTH)
|
||||
@ -3591,6 +3609,8 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
|
||||
case ABILITY_ANTICIPATION:
|
||||
if (!gSpecialStatuses[battler].switchInAbilityDone)
|
||||
{
|
||||
u32 types[3];
|
||||
GetBattlerTypes(battler, FALSE, types);
|
||||
for (i = 0; i < MAX_BATTLERS_COUNT; i++)
|
||||
{
|
||||
if (IsBattlerAlive(i) && !IsBattlerAlly(i, battler))
|
||||
@ -3598,10 +3618,14 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
|
||||
for (j = 0; j < MAX_MON_MOVES; j++)
|
||||
{
|
||||
move = gBattleMons[i].moves[j];
|
||||
moveType = GetBattleMoveType(move);
|
||||
enum BattleMoveEffects moveEffect = GetMoveEffect(move);
|
||||
if (CalcTypeEffectivenessMultiplier(move, moveType, i, battler, ABILITY_ANTICIPATION, FALSE) >= UQ_4_12(2.0)
|
||||
|| moveEffect == EFFECT_OHKO || moveEffect == EFFECT_SHEER_COLD)
|
||||
moveType = GetBattleMoveType(move);
|
||||
|
||||
if (GetTypeModifier(moveType, types[0]) >= UQ_4_12(2.0)
|
||||
|| (types[0] != types[1] && GetTypeModifier(moveType, types[1]) >= UQ_4_12(2.0))
|
||||
|| (types[2] != TYPE_MYSTERY && GetTypeModifier(moveType, types[2]) >= UQ_4_12(2.0))
|
||||
|| moveEffect == EFFECT_OHKO
|
||||
|| moveEffect == EFFECT_SHEER_COLD)
|
||||
{
|
||||
effect++;
|
||||
break;
|
||||
@ -5620,7 +5644,7 @@ bool32 CanSetNonVolatileStatus(u32 battlerAtk, u32 battlerDef, u32 abilityAtk, u
|
||||
battleScript = BattleScript_NotAffected;
|
||||
}
|
||||
else if (option == STATUS_RUN_SCRIPT // Check only important during battle execution for moves
|
||||
&& CalcTypeEffectivenessMultiplier(gCurrentMove, GetBattleMoveType(gCurrentMove), battlerAtk, battlerDef, abilityDef, TRUE)
|
||||
&& CalcTypeEffectivenessMultiplierHelper(gCurrentMove, GetBattleMoveType(gCurrentMove), battlerAtk, battlerDef, abilityDef, TRUE)
|
||||
&& gBattleStruct->moveResultFlags[battlerDef] & MOVE_RESULT_NO_EFFECT)
|
||||
{
|
||||
battleScript = BattleScript_ButItFailed;
|
||||
@ -9346,10 +9370,10 @@ static inline s32 DoMoveDamageCalc(struct DamageContext *ctx)
|
||||
if (dmg != INT32_MAX)
|
||||
return dmg;
|
||||
|
||||
ctx->holdEffectAtk = GetBattlerHoldEffect(ctx->battlerAtk, TRUE);
|
||||
ctx->holdEffectDef = GetBattlerHoldEffect(ctx->battlerDef, TRUE);
|
||||
ctx->abilityAtk = GetBattlerAbility(ctx->battlerAtk);
|
||||
ctx->abilityDef = GetBattlerAbility(ctx->battlerDef);
|
||||
ctx->holdEffectDef = GetBattlerHoldEffect(ctx->battlerDef, TRUE);
|
||||
ctx->holdEffectAtk = GetBattlerHoldEffect(ctx->battlerAtk, TRUE);
|
||||
|
||||
return DoMoveDamageCalcVars(ctx);
|
||||
}
|
||||
@ -9435,13 +9459,7 @@ s32 CalculateMoveDamage(struct DamageContext *ctx)
|
||||
ctx->holdEffectAtk = GetItemHoldEffect(ctx->battlerAtk);
|
||||
ctx->holdEffectDef = GetItemHoldEffect(ctx->battlerDef);
|
||||
|
||||
ctx->typeEffectivenessModifier = CalcTypeEffectivenessMultiplier(
|
||||
ctx->move,
|
||||
ctx->moveType,
|
||||
ctx->battlerAtk,
|
||||
ctx->battlerDef,
|
||||
ctx->abilityDef,
|
||||
ctx->updateFlags);
|
||||
ctx->typeEffectivenessModifier = CalcTypeEffectivenessMultiplier(ctx);
|
||||
|
||||
if (IsFutureSightAttackerInParty(ctx->battlerAtk, ctx->battlerDef, ctx->move))
|
||||
return DoFutureSightAttackDamageCalc(ctx);
|
||||
@ -9459,37 +9477,36 @@ s32 CalculateMoveDamageVars(struct DamageContext *ctx)
|
||||
return DoMoveDamageCalcVars(ctx);
|
||||
}
|
||||
|
||||
static inline void MulByTypeEffectiveness(uq4_12_t *modifier, u32 move, u32 moveType, u32 battlerDef, u32 defAbility, u32 defType, u32 battlerAtk, bool32 recordAbilities)
|
||||
static inline void MulByTypeEffectiveness(struct DamageContext *ctx, uq4_12_t *modifier, u32 defType)
|
||||
{
|
||||
uq4_12_t mod = GetTypeModifier(moveType, defType);
|
||||
u32 abilityAtk = GetBattlerAbility(battlerAtk);
|
||||
uq4_12_t mod = GetTypeModifier(ctx->moveType, defType);
|
||||
|
||||
if (mod == UQ_4_12(0.0) && GetBattlerHoldEffect(battlerDef, TRUE) == HOLD_EFFECT_RING_TARGET)
|
||||
if (mod == UQ_4_12(0.0) && ctx->holdEffectDef == HOLD_EFFECT_RING_TARGET)
|
||||
{
|
||||
mod = UQ_4_12(1.0);
|
||||
if (recordAbilities)
|
||||
RecordItemEffectBattle(battlerDef, HOLD_EFFECT_RING_TARGET);
|
||||
if (ctx->updateFlags)
|
||||
RecordItemEffectBattle(ctx->battlerDef, HOLD_EFFECT_RING_TARGET);
|
||||
}
|
||||
else if ((moveType == TYPE_FIGHTING || moveType == TYPE_NORMAL) && defType == TYPE_GHOST && gBattleMons[battlerDef].status2 & STATUS2_FORESIGHT && mod == UQ_4_12(0.0))
|
||||
else if ((ctx->moveType == TYPE_FIGHTING || ctx->moveType == TYPE_NORMAL) && defType == TYPE_GHOST && gBattleMons[ctx->battlerDef].status2 & STATUS2_FORESIGHT && mod == UQ_4_12(0.0))
|
||||
{
|
||||
mod = UQ_4_12(1.0);
|
||||
}
|
||||
else if ((moveType == TYPE_FIGHTING || moveType == TYPE_NORMAL) && defType == TYPE_GHOST
|
||||
&& (abilityAtk == ABILITY_SCRAPPY || abilityAtk == ABILITY_MINDS_EYE)
|
||||
else if ((ctx->moveType == TYPE_FIGHTING || ctx->moveType == TYPE_NORMAL) && defType == TYPE_GHOST
|
||||
&& (ctx->abilityAtk == ABILITY_SCRAPPY || ctx->abilityAtk == ABILITY_MINDS_EYE)
|
||||
&& mod == UQ_4_12(0.0))
|
||||
{
|
||||
mod = UQ_4_12(1.0);
|
||||
if (recordAbilities)
|
||||
RecordAbilityBattle(battlerAtk, abilityAtk);
|
||||
if (ctx->updateFlags)
|
||||
RecordAbilityBattle(ctx->battlerAtk, ctx->abilityAtk);
|
||||
}
|
||||
|
||||
if (moveType == TYPE_PSYCHIC && defType == TYPE_DARK && gStatuses3[battlerDef] & STATUS3_MIRACLE_EYED && mod == UQ_4_12(0.0))
|
||||
if (ctx->moveType == TYPE_PSYCHIC && defType == TYPE_DARK && gStatuses3[ctx->battlerDef] & STATUS3_MIRACLE_EYED && mod == UQ_4_12(0.0))
|
||||
mod = UQ_4_12(1.0);
|
||||
if (GetMoveEffect(move) == EFFECT_SUPER_EFFECTIVE_ON_ARG && defType == GetMoveArgType(move))
|
||||
if (GetMoveEffect(ctx->move) == EFFECT_SUPER_EFFECTIVE_ON_ARG && defType == GetMoveArgType(ctx->move))
|
||||
mod = UQ_4_12(2.0);
|
||||
if (moveType == TYPE_GROUND && defType == TYPE_FLYING && IsBattlerGrounded(battlerDef) && mod == UQ_4_12(0.0))
|
||||
if (ctx->moveType == TYPE_GROUND && defType == TYPE_FLYING && IsBattlerGrounded(ctx->battlerDef) && mod == UQ_4_12(0.0))
|
||||
mod = UQ_4_12(1.0);
|
||||
if (moveType == TYPE_STELLAR && GetActiveGimmick(battlerDef) == GIMMICK_TERA)
|
||||
if (ctx->moveType == TYPE_STELLAR && GetActiveGimmick(ctx->battlerDef) == GIMMICK_TERA)
|
||||
mod = UQ_4_12(2.0);
|
||||
|
||||
// B_WEATHER_STRONG_WINDS weakens Super Effective moves against Flying-type Pokémon
|
||||
@ -9499,14 +9516,14 @@ static inline void MulByTypeEffectiveness(uq4_12_t *modifier, u32 move, u32 move
|
||||
mod = UQ_4_12(1.0);
|
||||
}
|
||||
|
||||
if (gSpecialStatuses[battlerDef].distortedTypeMatchups || (mod > UQ_4_12(0.0) && ShouldTeraShellDistortTypeMatchups(move, battlerDef, defAbility)))
|
||||
if (gSpecialStatuses[ctx->battlerDef].distortedTypeMatchups || (mod > UQ_4_12(0.0) && ShouldTeraShellDistortTypeMatchups(ctx->move, ctx->battlerDef, ctx->abilityDef)))
|
||||
{
|
||||
mod = UQ_4_12(0.5);
|
||||
if (recordAbilities)
|
||||
if (ctx->updateFlags)
|
||||
{
|
||||
RecordAbilityBattle(battlerDef, defAbility);
|
||||
gSpecialStatuses[battlerDef].distortedTypeMatchups = TRUE;
|
||||
gSpecialStatuses[battlerDef].teraShellAbilityDone = TRUE;
|
||||
RecordAbilityBattle(ctx->battlerDef, ctx->abilityDef);
|
||||
gSpecialStatuses[ctx->battlerDef].distortedTypeMatchups = TRUE;
|
||||
gSpecialStatuses[ctx->battlerDef].teraShellAbilityDone = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
@ -9517,12 +9534,24 @@ static inline void TryNoticeIllusionInTypeEffectiveness(u32 move, u32 moveType,
|
||||
{
|
||||
// Check if the type effectiveness would've been different if the pokemon really had the types as the disguise.
|
||||
uq4_12_t presumedModifier = UQ_4_12(1.0);
|
||||
MulByTypeEffectiveness(&presumedModifier, move, moveType, battlerDef, ABILITY_ILLUSION, GetSpeciesType(illusionSpecies, 0), battlerAtk, FALSE);
|
||||
|
||||
struct DamageContext ctx = {0};
|
||||
ctx.battlerAtk = battlerAtk;
|
||||
ctx.battlerDef = battlerDef;
|
||||
ctx.move = move;
|
||||
ctx.moveType = moveType;
|
||||
ctx.updateFlags = FALSE;
|
||||
ctx.abilityAtk = GetBattlerAbility(battlerAtk);
|
||||
ctx.abilityDef = ABILITY_ILLUSION;
|
||||
ctx.holdEffectAtk = GetBattlerHoldEffect(battlerAtk, TRUE);
|
||||
ctx.holdEffectDef = GetBattlerHoldEffect(battlerDef, TRUE);
|
||||
|
||||
MulByTypeEffectiveness(&ctx, &presumedModifier, GetSpeciesType(illusionSpecies, 0));
|
||||
if (GetSpeciesType(illusionSpecies, 1) != GetSpeciesType(illusionSpecies, 0))
|
||||
MulByTypeEffectiveness(&presumedModifier, move, moveType, battlerDef, ABILITY_ILLUSION, GetSpeciesType(illusionSpecies, 1), battlerAtk, FALSE);
|
||||
MulByTypeEffectiveness(&ctx, &presumedModifier, GetSpeciesType(illusionSpecies, 1));
|
||||
|
||||
if (presumedModifier != resultingModifier)
|
||||
RecordAbilityBattle(battlerDef, ABILITY_ILLUSION);
|
||||
RecordAbilityBattle(ctx.battlerDef, ABILITY_ILLUSION);
|
||||
}
|
||||
|
||||
void UpdateMoveResultFlags(uq4_12_t modifier, u16 *resultFlags)
|
||||
@ -9549,99 +9578,102 @@ void UpdateMoveResultFlags(uq4_12_t modifier, u16 *resultFlags)
|
||||
}
|
||||
}
|
||||
|
||||
static inline uq4_12_t CalcTypeEffectivenessMultiplierInternal(u32 move, u32 moveType, u32 battlerAtk, u32 battlerDef, bool32 recordAbilities, uq4_12_t modifier, u32 defAbility)
|
||||
static inline uq4_12_t CalcTypeEffectivenessMultiplierInternal(struct DamageContext *ctx, uq4_12_t modifier)
|
||||
{
|
||||
u32 illusionSpecies;
|
||||
u32 types[3];
|
||||
GetBattlerTypes(battlerDef, FALSE, types);
|
||||
GetBattlerTypes(ctx->battlerDef, FALSE, types);
|
||||
|
||||
MulByTypeEffectiveness(&modifier, move, moveType, battlerDef, defAbility, types[0], battlerAtk, recordAbilities);
|
||||
MulByTypeEffectiveness(ctx, &modifier, types[0]);
|
||||
if (types[1] != types[0])
|
||||
MulByTypeEffectiveness(&modifier, move, moveType, battlerDef, defAbility, types[1], battlerAtk, recordAbilities);
|
||||
MulByTypeEffectiveness(ctx, &modifier, types[1]);
|
||||
if (types[2] != TYPE_MYSTERY && types[2] != types[1] && types[2] != types[0])
|
||||
MulByTypeEffectiveness(&modifier, move, moveType, battlerDef, defAbility, types[2], battlerAtk, recordAbilities);
|
||||
if (moveType == TYPE_FIRE && gDisableStructs[battlerDef].tarShot)
|
||||
MulByTypeEffectiveness(ctx, &modifier, types[2]);
|
||||
if (ctx->moveType == TYPE_FIRE && gDisableStructs[ctx->battlerDef].tarShot)
|
||||
modifier = uq4_12_multiply(modifier, UQ_4_12(2.0));
|
||||
|
||||
if (recordAbilities && (illusionSpecies = GetIllusionMonSpecies(battlerDef)))
|
||||
TryNoticeIllusionInTypeEffectiveness(move, moveType, battlerAtk, battlerDef, modifier, illusionSpecies);
|
||||
if (ctx->updateFlags && (illusionSpecies = GetIllusionMonSpecies(ctx->battlerDef)))
|
||||
TryNoticeIllusionInTypeEffectiveness(ctx->move, ctx->moveType, ctx->battlerAtk, ctx->battlerDef, modifier, illusionSpecies);
|
||||
|
||||
if (GetMoveCategory(move) == DAMAGE_CATEGORY_STATUS && move != MOVE_THUNDER_WAVE)
|
||||
if (GetMoveCategory(ctx->move) == DAMAGE_CATEGORY_STATUS && ctx->move != MOVE_THUNDER_WAVE)
|
||||
{
|
||||
modifier = UQ_4_12(1.0);
|
||||
if (B_GLARE_GHOST < GEN_4 && move == MOVE_GLARE && IS_BATTLER_OF_TYPE(battlerDef, TYPE_GHOST))
|
||||
if (B_GLARE_GHOST < GEN_4 && ctx->move == MOVE_GLARE && IS_BATTLER_OF_TYPE(ctx->battlerDef, TYPE_GHOST))
|
||||
modifier = UQ_4_12(0.0);
|
||||
}
|
||||
else if (moveType == TYPE_GROUND && !IsBattlerGroundedInverseCheck(battlerDef, INVERSE_BATTLE, CHECK_IRON_BALL) && !(MoveIgnoresTypeIfFlyingAndUngrounded(move)))
|
||||
else if (ctx->moveType == TYPE_GROUND && !IsBattlerGroundedInverseCheck(ctx->battlerDef, INVERSE_BATTLE, CHECK_IRON_BALL) && !(MoveIgnoresTypeIfFlyingAndUngrounded(ctx->move)))
|
||||
{
|
||||
modifier = UQ_4_12(0.0);
|
||||
if (recordAbilities && defAbility == ABILITY_LEVITATE)
|
||||
if (ctx->updateFlags && ctx->abilityDef == ABILITY_LEVITATE)
|
||||
{
|
||||
gBattleStruct->moveResultFlags[battlerDef] |= (MOVE_RESULT_MISSED | MOVE_RESULT_DOESNT_AFFECT_FOE);
|
||||
gBattleStruct->moveResultFlags[ctx->battlerDef] |= (MOVE_RESULT_MISSED | MOVE_RESULT_DOESNT_AFFECT_FOE);
|
||||
gLastUsedAbility = ABILITY_LEVITATE;
|
||||
gLastLandedMoves[battlerDef] = 0;
|
||||
gBattleStruct->missStringId[battlerDef] = B_MSG_GROUND_MISS;
|
||||
RecordAbilityBattle(battlerDef, ABILITY_LEVITATE);
|
||||
gLastLandedMoves[ctx->battlerDef] = 0;
|
||||
gBattleStruct->missStringId[ctx->battlerDef] = B_MSG_GROUND_MISS;
|
||||
RecordAbilityBattle(ctx->battlerDef, ABILITY_LEVITATE);
|
||||
}
|
||||
}
|
||||
else if (B_SHEER_COLD_IMMUNITY >= GEN_7 && GetMoveEffect(move) == EFFECT_SHEER_COLD && IS_BATTLER_OF_TYPE(battlerDef, TYPE_ICE))
|
||||
else if (B_SHEER_COLD_IMMUNITY >= GEN_7 && GetMoveEffect(ctx->move) == EFFECT_SHEER_COLD && IS_BATTLER_OF_TYPE(ctx->battlerDef, TYPE_ICE))
|
||||
{
|
||||
modifier = UQ_4_12(0.0);
|
||||
}
|
||||
|
||||
// Thousand Arrows ignores type modifiers for flying mons
|
||||
if (!IsBattlerGrounded(battlerDef)
|
||||
&& MoveIgnoresTypeIfFlyingAndUngrounded(move)
|
||||
&& IS_BATTLER_OF_TYPE(battlerDef, TYPE_FLYING))
|
||||
if (!IsBattlerGrounded(ctx->battlerDef)
|
||||
&& MoveIgnoresTypeIfFlyingAndUngrounded(ctx->move)
|
||||
&& IS_BATTLER_OF_TYPE(ctx->battlerDef, TYPE_FLYING))
|
||||
{
|
||||
modifier = UQ_4_12(1.0);
|
||||
}
|
||||
|
||||
// Iron Ball ignores type modifiers for flying-type mons if it is the only source of grounding
|
||||
if (B_IRON_BALL >= GEN_5
|
||||
&& moveType == TYPE_GROUND
|
||||
&& IS_BATTLER_OF_TYPE(battlerDef, TYPE_FLYING)
|
||||
&& GetBattlerHoldEffect(battlerDef, TRUE) == HOLD_EFFECT_IRON_BALL
|
||||
&& !IsBattlerGroundedInverseCheck(battlerDef, NOT_INVERSE_BATTLE, IGNORE_IRON_BALL)
|
||||
&& ctx->moveType == TYPE_GROUND
|
||||
&& IS_BATTLER_OF_TYPE(ctx->battlerDef, TYPE_FLYING)
|
||||
&& GetBattlerHoldEffect(ctx->battlerDef, TRUE) == HOLD_EFFECT_IRON_BALL
|
||||
&& !IsBattlerGroundedInverseCheck(ctx->battlerDef, NOT_INVERSE_BATTLE, IGNORE_IRON_BALL)
|
||||
&& !FlagGet(B_FLAG_INVERSE_BATTLE))
|
||||
{
|
||||
modifier = UQ_4_12(1.0);
|
||||
}
|
||||
|
||||
if (((defAbility == ABILITY_WONDER_GUARD && modifier <= UQ_4_12(1.0))
|
||||
|| (defAbility == ABILITY_TELEPATHY && battlerDef == BATTLE_PARTNER(battlerAtk)))
|
||||
&& GetMovePower(move) != 0)
|
||||
if (((ctx->abilityDef == ABILITY_WONDER_GUARD && modifier <= UQ_4_12(1.0))
|
||||
|| (ctx->abilityDef == ABILITY_TELEPATHY && ctx->battlerDef == BATTLE_PARTNER(ctx->battlerAtk)))
|
||||
&& GetMovePower(ctx->move) != 0)
|
||||
{
|
||||
modifier = UQ_4_12(0.0);
|
||||
if (recordAbilities)
|
||||
if (ctx->updateFlags)
|
||||
{
|
||||
gLastUsedAbility = gBattleMons[battlerDef].ability;
|
||||
gBattleStruct->moveResultFlags[battlerDef] |= MOVE_RESULT_MISSED;
|
||||
gLastLandedMoves[battlerDef] = 0;
|
||||
gBattleStruct->missStringId[battlerDef] = B_MSG_AVOIDED_DMG;
|
||||
RecordAbilityBattle(battlerDef, gBattleMons[battlerDef].ability);
|
||||
gLastUsedAbility = gBattleMons[ctx->battlerDef].ability;
|
||||
gBattleStruct->moveResultFlags[ctx->battlerDef] |= MOVE_RESULT_MISSED;
|
||||
gLastLandedMoves[ctx->battlerDef] = 0;
|
||||
gBattleStruct->missStringId[ctx->battlerDef] = B_MSG_AVOIDED_DMG;
|
||||
RecordAbilityBattle(ctx->battlerDef, gBattleMons[ctx->battlerDef].ability);
|
||||
}
|
||||
}
|
||||
|
||||
if (recordAbilities)
|
||||
TryInitializeFirstSTABMoveTrainerSlide(battlerDef, battlerAtk, moveType);
|
||||
if (ctx->updateFlags)
|
||||
TryInitializeFirstSTABMoveTrainerSlide(ctx->battlerDef, ctx->battlerAtk, ctx->moveType);
|
||||
|
||||
return modifier;
|
||||
}
|
||||
|
||||
uq4_12_t CalcTypeEffectivenessMultiplier(u32 move, u32 moveType, u32 battlerAtk, u32 battlerDef, u32 defAbility, bool32 recordAbilities)
|
||||
uq4_12_t CalcTypeEffectivenessMultiplier(struct DamageContext *ctx)
|
||||
{
|
||||
uq4_12_t modifier = UQ_4_12(1.0);
|
||||
|
||||
if (move != MOVE_STRUGGLE && moveType != TYPE_MYSTERY)
|
||||
if (ctx->move != MOVE_STRUGGLE && ctx->moveType != TYPE_MYSTERY)
|
||||
{
|
||||
modifier = CalcTypeEffectivenessMultiplierInternal(move, moveType, battlerAtk, battlerDef, recordAbilities, modifier, defAbility);
|
||||
if (GetMoveEffect(move) == EFFECT_TWO_TYPED_MOVE)
|
||||
modifier = CalcTypeEffectivenessMultiplierInternal(move, GetMoveArgType(move), battlerAtk, battlerDef, recordAbilities, modifier, defAbility);
|
||||
modifier = CalcTypeEffectivenessMultiplierInternal(ctx, modifier);
|
||||
if (GetMoveEffect(ctx->move) == EFFECT_TWO_TYPED_MOVE)
|
||||
{
|
||||
ctx->moveType = GetMoveArgType(ctx->move);
|
||||
modifier = CalcTypeEffectivenessMultiplierInternal(ctx, modifier);
|
||||
}
|
||||
}
|
||||
|
||||
if (recordAbilities)
|
||||
UpdateMoveResultFlags(modifier, &gBattleStruct->moveResultFlags[battlerDef]);
|
||||
if (ctx->updateFlags)
|
||||
UpdateMoveResultFlags(modifier, &gBattleStruct->moveResultFlags[ctx->battlerDef]);
|
||||
return modifier;
|
||||
}
|
||||
|
||||
@ -9652,11 +9684,17 @@ uq4_12_t CalcPartyMonTypeEffectivenessMultiplier(u16 move, u16 speciesDef, u16 a
|
||||
|
||||
if (move != MOVE_STRUGGLE && moveType != TYPE_MYSTERY)
|
||||
{
|
||||
MulByTypeEffectiveness(&modifier, move, moveType, 0, 0, GetSpeciesType(speciesDef, 0), 0, FALSE);
|
||||
if (GetSpeciesType(speciesDef, 1) != GetSpeciesType(speciesDef, 0))
|
||||
MulByTypeEffectiveness(&modifier, move, moveType, 0, 0, GetSpeciesType(speciesDef, 1), 0, FALSE);
|
||||
struct DamageContext ctx = {0};
|
||||
ctx.move = move;
|
||||
ctx.moveType = moveType;
|
||||
ctx.updateFlags = FALSE;
|
||||
ctx.abilityDef = abilityDef;
|
||||
|
||||
if (moveType == TYPE_GROUND && abilityDef == ABILITY_LEVITATE && !(gFieldStatuses & STATUS_FIELD_GRAVITY))
|
||||
MulByTypeEffectiveness(&ctx, &modifier, GetSpeciesType(speciesDef, 0));
|
||||
if (GetSpeciesType(speciesDef, 1) != GetSpeciesType(speciesDef, 0))
|
||||
MulByTypeEffectiveness(&ctx, &modifier, GetSpeciesType(speciesDef, 1));
|
||||
|
||||
if (ctx.moveType == TYPE_GROUND && abilityDef == ABILITY_LEVITATE && !(gFieldStatuses & STATUS_FIELD_GRAVITY))
|
||||
modifier = UQ_4_12(0.0);
|
||||
if (abilityDef == ABILITY_WONDER_GUARD && modifier <= UQ_4_12(1.0) && GetMovePower(move) != 0)
|
||||
modifier = UQ_4_12(0.0);
|
||||
@ -9690,9 +9728,14 @@ uq4_12_t GetOverworldTypeEffectiveness(struct Pokemon *mon, u8 moveType)
|
||||
|
||||
if (moveType != TYPE_MYSTERY)
|
||||
{
|
||||
MulByTypeEffectiveness(&modifier, MOVE_POUND, moveType, 0, 0, type1, 0, FALSE);
|
||||
struct DamageContext ctx = {0};
|
||||
ctx.move = MOVE_POUND;
|
||||
ctx.moveType = moveType;
|
||||
ctx.updateFlags = FALSE;
|
||||
|
||||
MulByTypeEffectiveness(&ctx, &modifier, type1);
|
||||
if (type2 != type1)
|
||||
MulByTypeEffectiveness(&modifier, MOVE_POUND, moveType, 0, 0, type2, 0, FALSE);
|
||||
MulByTypeEffectiveness(&ctx, &modifier, type2);
|
||||
|
||||
if ((modifier <= UQ_4_12(1.0) && abilityDef == ABILITY_WONDER_GUARD)
|
||||
|| (moveType == TYPE_FIRE && abilityDef == ABILITY_FLASH_FIRE)
|
||||
@ -11103,7 +11146,7 @@ static inline bool32 DoesBattlerHaveAbilityImmunity(u32 battlerAtk, u32 battlerD
|
||||
bool32 TargetFullyImmuneToCurrMove(u32 battlerAtk, u32 battlerDef)
|
||||
{
|
||||
u32 moveType = GetBattleMoveType(gCurrentMove);
|
||||
return ((CalcTypeEffectivenessMultiplier(gCurrentMove, moveType, battlerAtk, battlerDef, GetBattlerAbility(battlerDef), FALSE) == UQ_4_12(0.0))
|
||||
return ((CalcTypeEffectivenessMultiplierHelper(gCurrentMove, moveType, battlerAtk, battlerDef, GetBattlerAbility(battlerDef), FALSE) == UQ_4_12(0.0))
|
||||
|| IsBattlerProtected(battlerAtk, battlerDef, gCurrentMove)
|
||||
|| IsSemiInvulnerable(battlerDef, gCurrentMove)
|
||||
|| DoesBattlerHaveAbilityImmunity(battlerAtk, battlerDef, moveType));
|
||||
|
||||
@ -133,7 +133,6 @@ SINGLE_BATTLE_TEST("Anticipation considers Synchronoise as an ordinary Psychic-t
|
||||
|
||||
SINGLE_BATTLE_TEST("Anticipation considers Freeze-Dry as an ordinary Ice-type move")
|
||||
{
|
||||
KNOWN_FAILING;
|
||||
GIVEN {
|
||||
ASSUME(GetMoveType(MOVE_FREEZE_DRY) == TYPE_ICE);
|
||||
ASSUME(GetSpeciesType(SPECIES_SQUIRTLE, 0) == TYPE_WATER);
|
||||
@ -150,7 +149,6 @@ SINGLE_BATTLE_TEST("Anticipation considers Freeze-Dry as an ordinary Ice-type mo
|
||||
|
||||
SINGLE_BATTLE_TEST("Anticipation considers Flying Press as an ordinary Fighting-type move")
|
||||
{
|
||||
KNOWN_FAILING;
|
||||
GIVEN {
|
||||
ASSUME(GetMoveType(MOVE_FLYING_PRESS) == TYPE_FIGHTING);
|
||||
ASSUME(GetSpeciesType(SPECIES_TANGELA, 0) == TYPE_GRASS);
|
||||
@ -280,7 +278,6 @@ SINGLE_BATTLE_TEST("Anticipation treats dynamic move types as their base type (N
|
||||
|
||||
SINGLE_BATTLE_TEST("Anticipation does not consider Strong Winds on type matchups")
|
||||
{
|
||||
KNOWN_FAILING;
|
||||
GIVEN {
|
||||
ASSUME(GetSpeciesType(SPECIES_RAYQUAZA_MEGA, 0) == TYPE_DRAGON);
|
||||
ASSUME(GetSpeciesType(SPECIES_RAYQUAZA_MEGA, 1) == TYPE_FLYING);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user