mirror of
https://github.com/rh-hideout/pokeemerald-expansion.git
synced 2026-04-26 02:14:22 -05:00
Move crit calculation into the damage calc (#8365)
Co-authored-by: Pawkkie <61265402+Pawkkie@users.noreply.github.com>
This commit is contained in:
parent
1fa97941c8
commit
55fbbfa586
|
|
@ -18,6 +18,7 @@
|
|||
.endm
|
||||
|
||||
.macro critcalc
|
||||
.warning "critcalc macro has been deprecated, please remove from scripts"
|
||||
.byte 0x4
|
||||
.endm
|
||||
|
||||
|
|
|
|||
|
|
@ -25,12 +25,12 @@ BattleScript_EffectFickleBeam::
|
|||
attackcanceler
|
||||
accuracycheck BattleScript_MoveMissedPause, ACC_CURR_MOVE
|
||||
ficklebeamdamagecalculation
|
||||
goto BattleScript_HitFromCritCalc
|
||||
goto BattleScript_HitFromDamageCalc
|
||||
BattleScript_FickleBeamDoubled::
|
||||
pause B_WAIT_TIME_SHORTEST
|
||||
printstring STRINGID_FICKLEBEAMDOUBLED
|
||||
waitmessage B_WAIT_TIME_LONG
|
||||
goto BattleScript_HitFromCritCalc
|
||||
goto BattleScript_HitFromDamageCalc
|
||||
|
||||
BattleScript_Terastallization::
|
||||
@ TODO: no string prints in S/V, but right now this helps with clarity
|
||||
|
|
@ -655,7 +655,7 @@ BattleScript_SkyDropTurn2:
|
|||
call BattleScript_TwoTurnMovesSecondTurnRet
|
||||
clearskydrop BattleScript_SkyDropChangedTarget
|
||||
jumpiftype BS_TARGET, TYPE_FLYING, BattleScript_SkyDropFlyingType
|
||||
goto BattleScript_HitFromCritCalc
|
||||
goto BattleScript_HitFromDamageCalc
|
||||
BattleScript_SkyDropFlyingType:
|
||||
makevisible BS_TARGET
|
||||
printstring STRINGID_ITDOESNTAFFECT
|
||||
|
|
@ -687,7 +687,6 @@ BattleScript_EffectFling::
|
|||
pause B_WAIT_TIME_SHORT
|
||||
printstring STRINGID_PKMNFLUNG
|
||||
waitmessage B_WAIT_TIME_SHORT
|
||||
critcalc
|
||||
damagecalc
|
||||
adjustdamage
|
||||
removeitem BS_ATTACKER
|
||||
|
|
@ -773,7 +772,7 @@ BattleScript_EffectPoltergeist::
|
|||
setpoltergeistmessage BattleScript_ButItFailed
|
||||
printstring STRINGID_ABOUTTOUSEPOLTERGEIST
|
||||
waitmessage B_WAIT_TIME_LONG
|
||||
goto BattleScript_HitFromCritCalc
|
||||
goto BattleScript_HitFromDamageCalc
|
||||
|
||||
BattleScript_EffectTarShot::
|
||||
attackcanceler
|
||||
|
|
@ -1162,7 +1161,6 @@ BattleScript_EffectSpectralThief::
|
|||
typecalc
|
||||
tryspectralthiefsteal BattleScript_SpectralThiefSteal
|
||||
BattleScript_EffectSpectralThiefFromDamage:
|
||||
critcalc
|
||||
damagecalc
|
||||
adjustdamage
|
||||
call BattleScript_Hit_RetFromAtkAnimation
|
||||
|
|
@ -1471,7 +1469,7 @@ BattleScript_EffectSynchronoise::
|
|||
pause B_WAIT_TIME_MED
|
||||
trysynchronoise BattleScript_MoveEnd
|
||||
accuracycheck BattleScript_ButItFailed, ACC_CURR_MOVE
|
||||
goto BattleScript_HitFromCritCalc
|
||||
goto BattleScript_HitFromDamageCalc
|
||||
|
||||
BattleScript_ItDoesntAffectFoe::
|
||||
savetarget
|
||||
|
|
@ -2386,8 +2384,7 @@ BattleScript_EffectHit::
|
|||
attackcanceler
|
||||
BattleScript_HitFromAccCheck::
|
||||
accuracycheck BattleScript_MoveMissedPause, ACC_CURR_MOVE
|
||||
BattleScript_HitFromCritCalc::
|
||||
critcalc
|
||||
BattleScript_HitFromDamageCalc::
|
||||
damagecalc
|
||||
adjustdamage
|
||||
BattleScript_HitFromAtkAnimation::
|
||||
|
|
@ -2402,8 +2399,6 @@ BattleScript_EffectHit_Ret::
|
|||
attackcanceler
|
||||
BattleScript_EffectHit_RetFromAccCheck::
|
||||
accuracycheck BattleScript_MoveMissedPause, ACC_CURR_MOVE
|
||||
BattleScript_EffectHit_RetFromCritCalc::
|
||||
critcalc
|
||||
damagecalc
|
||||
adjustdamage
|
||||
BattleScript_Hit_RetFromAtkAnimation::
|
||||
|
|
@ -2428,7 +2423,7 @@ BattleScript_EffectNaturalGift::
|
|||
jumpifability BS_ATTACKER, ABILITY_KLUTZ, BattleScript_ButItFailed
|
||||
jumpifvolatile BS_ATTACKER, VOLATILE_EMBARGO, BattleScript_ButItFailed
|
||||
accuracycheck BattleScript_MoveMissedPause, ACC_CURR_MOVE
|
||||
call BattleScript_HitFromCritCalc
|
||||
call BattleScript_HitFromDamageCalc
|
||||
|
||||
BattleScript_MakeMoveMissed::
|
||||
setmoveresultflags MOVE_RESULT_MISSED
|
||||
|
|
@ -2559,7 +2554,7 @@ BattleScript_EffectExplosion::
|
|||
waitstate
|
||||
jumpiffainted BS_TARGET, TRUE, BattleScript_MoveEnd
|
||||
accuracycheck BattleScript_MoveMissedPause, ACC_CURR_MOVE
|
||||
goto BattleScript_HitFromCritCalc
|
||||
goto BattleScript_HitFromDamageCalc
|
||||
|
||||
BattleScript_FaintAttackerForExplosion::
|
||||
tryfaintmon BS_ATTACKER
|
||||
|
|
@ -3072,7 +3067,7 @@ BattleScript_TwoTurnMovesSecondPowerHerbActivates:
|
|||
BattleScript_FromTwoTurnMovesSecondTurnRet:
|
||||
call BattleScript_TwoTurnMovesSecondTurnRet
|
||||
accuracycheck BattleScript_MoveMissedPause, ACC_CURR_MOVE
|
||||
goto BattleScript_HitFromCritCalc
|
||||
goto BattleScript_HitFromDamageCalc
|
||||
|
||||
BattleScript_TwoTurnMovesSecondTurn::
|
||||
attackcanceler
|
||||
|
|
@ -3117,7 +3112,7 @@ BattleScript_EffectRage::
|
|||
attackcanceler
|
||||
accuracycheck BattleScript_RageMiss, ACC_CURR_MOVE
|
||||
seteffectprimary BS_ATTACKER, BS_TARGET, MOVE_EFFECT_RAGE
|
||||
goto BattleScript_HitFromCritCalc
|
||||
goto BattleScript_HitFromDamageCalc
|
||||
BattleScript_RageMiss::
|
||||
clearvolatile BS_ATTACKER, VOLATILE_RAGE
|
||||
goto BattleScript_MoveMissedPause
|
||||
|
|
@ -3231,7 +3226,7 @@ BattleScript_EffectSnore::
|
|||
statusanimation BS_ATTACKER
|
||||
BattleScript_DoSnore::
|
||||
accuracycheck BattleScript_MoveMissedPause, ACC_CURR_MOVE
|
||||
goto BattleScript_HitFromCritCalc
|
||||
goto BattleScript_HitFromDamageCalc
|
||||
|
||||
BattleScript_EffectConversion2::
|
||||
attackcanceler
|
||||
|
|
@ -3478,7 +3473,7 @@ BattleScript_RolloutCheckAccuracy::
|
|||
BattleScript_RolloutHit::
|
||||
typecalc
|
||||
handlerollout
|
||||
goto BattleScript_HitFromCritCalc
|
||||
goto BattleScript_HitFromDamageCalc
|
||||
|
||||
BattleScript_EffectSwagger::
|
||||
attackcanceler
|
||||
|
|
@ -3503,7 +3498,6 @@ BattleScript_EffectFuryCutter::
|
|||
accuracycheck BattleScript_FuryCutterHit, ACC_CURR_MOVE
|
||||
BattleScript_FuryCutterHit:
|
||||
handlefurycutter
|
||||
critcalc
|
||||
damagecalc
|
||||
jumpifmovehadnoeffect BattleScript_FuryCutterHit
|
||||
adjustdamage
|
||||
|
|
@ -3564,7 +3558,7 @@ BattleScript_EffectMagnitude::
|
|||
printstring STRINGID_MAGNITUDESTRENGTH
|
||||
waitmessage B_WAIT_TIME_LONG
|
||||
accuracycheck BattleScript_MoveMissedPause, ACC_CURR_MOVE
|
||||
goto BattleScript_HitFromCritCalc
|
||||
goto BattleScript_HitFromDamageCalc
|
||||
|
||||
BattleScript_EffectBatonPass::
|
||||
attackcanceler
|
||||
|
|
@ -3714,7 +3708,7 @@ BattleScript_EffectBeatUpGen3:
|
|||
pause B_WAIT_TIME_SHORT
|
||||
trydobeatup BattleScript_MoveEnd, BattleScript_ButItFailed
|
||||
printstring STRINGID_PKMNATTACK
|
||||
goto BattleScript_HitFromCritCalc
|
||||
goto BattleScript_HitFromDamageCalc
|
||||
|
||||
BattleScript_EffectDefenseCurl::
|
||||
attackcanceler
|
||||
|
|
@ -4069,7 +4063,6 @@ BattleScript_EffectBrickBreak::
|
|||
accuracycheck BattleScript_MoveMissedPause, ACC_CURR_MOVE
|
||||
typecalc
|
||||
removescreens
|
||||
critcalc
|
||||
damagecalc
|
||||
adjustdamage
|
||||
jumpifbyte CMP_EQUAL, sB_ANIM_TURN, 0, BattleScript_BrickBreakAnim
|
||||
|
|
@ -5353,7 +5346,6 @@ BattleScript_MonTookFutureAttack::
|
|||
BattleScript_CheckDoomDesireMiss::
|
||||
accuracycheck BattleScript_FutureAttackMiss, MOVE_DOOM_DESIRE
|
||||
BattleScript_FutureAttackAnimate::
|
||||
critcalc
|
||||
damagecalc
|
||||
adjustdamage
|
||||
jumpifmovehadnoeffect BattleScript_DoFutureAttackResult
|
||||
|
|
@ -8215,7 +8207,7 @@ BattleScript_TargetAbilityStatRaiseRet_End:
|
|||
BattleScript_EffectMaxMove::
|
||||
attackcanceler
|
||||
accuracycheck BattleScript_ButItFailed, NO_ACC_CALC_CHECK_LOCK_ON
|
||||
goto BattleScript_HitFromCritCalc
|
||||
goto BattleScript_HitFromDamageCalc
|
||||
|
||||
BattleScript_EffectRaiseStatAllies::
|
||||
savetarget
|
||||
|
|
|
|||
|
|
@ -764,7 +764,6 @@ struct BattleStruct
|
|||
u16 prevTurnSpecies[MAX_BATTLERS_COUNT]; // Stores species the AI has in play at start of turn
|
||||
s16 passiveHpUpdate[MAX_BATTLERS_COUNT]; // non-move damage and healing
|
||||
s16 moveDamage[MAX_BATTLERS_COUNT];
|
||||
s16 critChance[MAX_BATTLERS_COUNT];
|
||||
u16 moveResultFlags[MAX_BATTLERS_COUNT];
|
||||
u8 missStringId[MAX_BATTLERS_COUNT];
|
||||
enum CalcDamageState noResultString[MAX_BATTLERS_COUNT];
|
||||
|
|
|
|||
|
|
@ -40,9 +40,6 @@ union TRANSPARENT StatChangeFlags
|
|||
};
|
||||
};
|
||||
|
||||
s32 CalcCritChanceStage(u32 battlerAtk, u32 battlerDef, u32 move, bool32 recordAbility, enum Ability abilityAtk, enum Ability abilityDef, enum HoldEffect holdEffectAtk);
|
||||
s32 CalcCritChanceStageGen1(u32 battlerAtk, u32 battlerDef, u32 move, bool32 recordAbility, enum Ability abilityAtk, enum Ability abilityDef, enum HoldEffect holdEffectAtk);
|
||||
s32 GetCritHitOdds(s32 critChanceIndex);
|
||||
bool32 HasBattlerActedThisTurn(u32 battler);
|
||||
u32 GetBattlerTurnOrderNum(u32 battler);
|
||||
bool32 NoAliveMonsForBattlerSide(u32 battler);
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ extern const u8 BattleScript_OpportunistCopyStatChange[];
|
|||
extern const u8 BattleScript_MirrorHerbCopyStatChange[];
|
||||
extern const u8 BattleScript_MirrorHerbCopyStatChangeEnd2[];
|
||||
extern const u8 BattleScript_NotAffected[];
|
||||
extern const u8 BattleScript_HitFromCritCalc[];
|
||||
extern const u8 BattleScript_HitFromDamageCalc[];
|
||||
extern const u8 BattleScript_MoveEnd[];
|
||||
extern const u8 BattleScript_MakeMoveMissed[];
|
||||
extern const u8 BattleScript_MoveMissedPause[];
|
||||
|
|
|
|||
|
|
@ -301,6 +301,8 @@ u32 GetMoveSlot(u16 *moves, u32 move);
|
|||
u32 GetBattlerWeight(u32 battler);
|
||||
u32 CalcRolloutBasePower(u32 battlerAtk, u32 basePower, u32 rolloutTimer);
|
||||
u32 CalcFuryCutterBasePower(u32 basePower, u32 furyCutterCounter);
|
||||
s32 CalcCritChanceStage(struct DamageContext *ctx);
|
||||
s32 CalcCritChanceStageGen1(struct DamageContext *ctx);
|
||||
s32 CalculateMoveDamage(struct DamageContext *ctx);
|
||||
s32 CalculateMoveDamageVars(struct DamageContext *ctx);
|
||||
s32 DoFixedDamageMoveCalc(struct DamageContext *ctx);
|
||||
|
|
|
|||
|
|
@ -868,26 +868,27 @@ static inline void CalcDynamicMoveDamage(struct DamageContext *ctx, u16 *medianD
|
|||
*maximumDamage = maximum;
|
||||
}
|
||||
|
||||
static inline bool32 ShouldCalcCritDamage(u32 battlerAtk, u32 battlerDef, u32 move, struct AiLogicData *aiData)
|
||||
static inline bool32 ShouldCalcCritDamage(struct DamageContext *ctx)
|
||||
{
|
||||
s32 critChanceIndex = 0;
|
||||
|
||||
// Get crit chance
|
||||
if (GetGenConfig(GEN_CONFIG_CRIT_CHANCE) == GEN_1)
|
||||
critChanceIndex = CalcCritChanceStageGen1(battlerAtk, battlerDef, move, FALSE, aiData->abilities[battlerAtk], aiData->abilities[battlerDef], aiData->holdEffects[battlerAtk]);
|
||||
critChanceIndex = CalcCritChanceStageGen1(ctx);
|
||||
else
|
||||
critChanceIndex = CalcCritChanceStage(battlerAtk, battlerDef, move, FALSE, aiData->abilities[battlerAtk], aiData->abilities[battlerDef], aiData->holdEffects[battlerAtk]);
|
||||
critChanceIndex = CalcCritChanceStage(ctx);
|
||||
|
||||
if (critChanceIndex == CRITICAL_HIT_ALWAYS)
|
||||
return TRUE;
|
||||
if (critChanceIndex >= RISKY_AI_CRIT_STAGE_THRESHOLD // Not guaranteed but above Risky threshold
|
||||
&& (gAiThinkingStruct->aiFlags[battlerAtk] & AI_FLAG_RISKY)
|
||||
&& (gAiThinkingStruct->aiFlags[ctx->battlerAtk] & AI_FLAG_RISKY)
|
||||
&& GetGenConfig(GEN_CONFIG_CRIT_CHANCE) != GEN_1)
|
||||
return TRUE;
|
||||
if (critChanceIndex >= RISKY_AI_CRIT_THRESHOLD_GEN_1 // Not guaranteed but above Risky threshold
|
||||
&& (gAiThinkingStruct->aiFlags[battlerAtk] & AI_FLAG_RISKY)
|
||||
&& (gAiThinkingStruct->aiFlags[ctx->battlerAtk] & AI_FLAG_RISKY)
|
||||
&& GetGenConfig(GEN_CONFIG_CRIT_CHANCE) == GEN_1)
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
|
@ -931,7 +932,6 @@ struct SimulatedDamage AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u
|
|||
ctx.battlerDef = battlerDef;
|
||||
ctx.move = ctx.chosenMove = move;
|
||||
ctx.moveType = GetBattleMoveType(move);
|
||||
ctx.isCrit = ShouldCalcCritDamage(battlerAtk, battlerDef, move, aiData);
|
||||
ctx.randomFactor = FALSE;
|
||||
ctx.updateFlags = FALSE;
|
||||
ctx.weather = weather;
|
||||
|
|
@ -940,6 +940,7 @@ struct SimulatedDamage AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u
|
|||
ctx.holdEffectDef = aiData->holdEffects[battlerDef];
|
||||
ctx.abilityAtk = aiData->abilities[battlerAtk];
|
||||
ctx.abilityDef = AI_GetMoldBreakerSanitizedAbility(battlerAtk, ctx.abilityAtk, aiData->abilities[battlerDef], ctx.holdEffectDef, move);
|
||||
ctx.isCrit = ShouldCalcCritDamage(&ctx);
|
||||
ctx.typeEffectivenessModifier = CalcTypeEffectivenessMultiplier(&ctx);
|
||||
|
||||
u32 movePower = GetMovePower(move);
|
||||
|
|
|
|||
|
|
@ -1544,222 +1544,11 @@ static void Cmd_unused_0x3(void)
|
|||
{
|
||||
}
|
||||
|
||||
// The chance is 1/N for each stage.
|
||||
static const u32 sGen7CriticalHitOdds[] = {24, 8, 2, 1, 1}; // 1/X
|
||||
static const u32 sGen6CriticalHitOdds[] = {16, 8, 2, 1, 1}; // 1/X
|
||||
static const u32 sCriticalHitOdds[] = {16, 8, 4, 3, 2}; // 1/X, Gens 3,4,5
|
||||
static const u32 sGen2CriticalHitOdds[] = {17, 32, 64, 85, 128}; // X/256
|
||||
|
||||
static inline u32 GetCriticalHitOdds(u32 critChance)
|
||||
{
|
||||
if (GetGenConfig(GEN_CONFIG_CRIT_CHANCE) >= GEN_7)
|
||||
return sGen7CriticalHitOdds[critChance];
|
||||
if (GetGenConfig(GEN_CONFIG_CRIT_CHANCE) == GEN_6)
|
||||
return sGen6CriticalHitOdds[critChance];
|
||||
if (GetGenConfig(GEN_CONFIG_CRIT_CHANCE) == GEN_2)
|
||||
return sGen2CriticalHitOdds[critChance];
|
||||
|
||||
return sCriticalHitOdds[critChance];
|
||||
}
|
||||
|
||||
static inline u32 IsBattlerLeekAffected(u32 battler, enum HoldEffect holdEffect)
|
||||
{
|
||||
if (holdEffect == HOLD_EFFECT_LEEK)
|
||||
{
|
||||
return GET_BASE_SPECIES_ID(gBattleMons[battler].species) == SPECIES_FARFETCHD
|
||||
|| gBattleMons[battler].species == SPECIES_SIRFETCHD;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static inline u32 GetHoldEffectCritChanceIncrease(u32 battler, enum HoldEffect holdEffect)
|
||||
{
|
||||
u32 critStageIncrease = 0;
|
||||
|
||||
switch (holdEffect)
|
||||
{
|
||||
case HOLD_EFFECT_SCOPE_LENS:
|
||||
critStageIncrease = 1;
|
||||
break;
|
||||
case HOLD_EFFECT_LUCKY_PUNCH:
|
||||
if (gBattleMons[battler].species == SPECIES_CHANSEY)
|
||||
critStageIncrease = 2;
|
||||
break;
|
||||
case HOLD_EFFECT_LEEK:
|
||||
if (IsBattlerLeekAffected(battler, holdEffect))
|
||||
critStageIncrease = 2;
|
||||
break;
|
||||
default:
|
||||
critStageIncrease = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return critStageIncrease;
|
||||
}
|
||||
|
||||
s32 CalcCritChanceStage(u32 battlerAtk, u32 battlerDef, u32 move, bool32 recordAbility, enum Ability abilityAtk, enum Ability abilityDef, enum HoldEffect holdEffectAtk)
|
||||
{
|
||||
s32 critChance = 0;
|
||||
|
||||
if (gSideStatuses[battlerDef] & SIDE_STATUS_LUCKY_CHANT)
|
||||
{
|
||||
critChance = CRITICAL_HIT_BLOCKED;
|
||||
}
|
||||
else if (gBattleMons[battlerAtk].volatiles.laserFocus
|
||||
|| MoveAlwaysCrits(move)
|
||||
|| (abilityAtk == ABILITY_MERCILESS && gBattleMons[battlerDef].status1 & STATUS1_PSN_ANY))
|
||||
{
|
||||
critChance = CRITICAL_HIT_ALWAYS;
|
||||
}
|
||||
else
|
||||
{
|
||||
critChance = (gBattleMons[battlerAtk].volatiles.focusEnergy != 0 ? 2 : 0)
|
||||
+ (gBattleMons[battlerAtk].volatiles.dragonCheer != 0 ? 1 : 0)
|
||||
+ GetMoveCriticalHitStage(move)
|
||||
+ GetHoldEffectCritChanceIncrease(battlerAtk, holdEffectAtk)
|
||||
+ ((B_AFFECTION_MECHANICS == TRUE && GetBattlerAffectionHearts(battlerAtk) == AFFECTION_FIVE_HEARTS) ? 2 : 0)
|
||||
+ (abilityAtk == ABILITY_SUPER_LUCK ? 1 : 0)
|
||||
+ gBattleStruct->bonusCritStages[gBattlerAttacker];
|
||||
|
||||
if (critChance >= ARRAY_COUNT(sCriticalHitOdds))
|
||||
critChance = ARRAY_COUNT(sCriticalHitOdds) - 1;
|
||||
}
|
||||
|
||||
if (critChance != CRITICAL_HIT_BLOCKED && (abilityDef == ABILITY_BATTLE_ARMOR || abilityDef == ABILITY_SHELL_ARMOR))
|
||||
{
|
||||
// Record ability only if move had 100% chance to get a crit
|
||||
if (recordAbility)
|
||||
{
|
||||
if (critChance == CRITICAL_HIT_ALWAYS)
|
||||
RecordAbilityBattle(battlerDef, abilityDef);
|
||||
else if (GetCriticalHitOdds(critChance) == 1)
|
||||
RecordAbilityBattle(battlerDef, abilityDef);
|
||||
}
|
||||
critChance = CRITICAL_HIT_BLOCKED;
|
||||
}
|
||||
|
||||
return critChance;
|
||||
}
|
||||
|
||||
// Bulbapedia: https://bulbapedia.bulbagarden.net/wiki/Critical_hit#Generation_I
|
||||
// Crit chance = Threshold / 256, Threshold maximum of 255
|
||||
// Threshold = Base Speed / 2
|
||||
// High crit move = 8 * (Base Speed / 2)
|
||||
// Focus Energy = 4 * (Base Speed / 2)
|
||||
s32 CalcCritChanceStageGen1(u32 battlerAtk, u32 battlerDef, u32 move, bool32 recordAbility, enum Ability abilityAtk, enum Ability abilityDef, enum HoldEffect holdEffectAtk)
|
||||
{
|
||||
s32 critChance = 0;
|
||||
s32 moveCritStage = GetMoveCriticalHitStage(gCurrentMove);
|
||||
s32 bonusCritStage = gBattleStruct->bonusCritStages[battlerAtk]; // G-Max Chi Strike
|
||||
u32 holdEffectCritStage = GetHoldEffectCritChanceIncrease(battlerAtk, holdEffectAtk);
|
||||
u16 baseSpeed = GetSpeciesBaseSpeed(gBattleMons[battlerAtk].species);
|
||||
|
||||
critChance = baseSpeed / 2;
|
||||
|
||||
// Crit scaling
|
||||
if (moveCritStage > 0)
|
||||
critChance *= 8 * moveCritStage;
|
||||
|
||||
if (bonusCritStage > 0)
|
||||
critChance *= bonusCritStage;
|
||||
|
||||
if (gBattleMons[battlerAtk].volatiles.focusEnergy)
|
||||
critChance *= 4;
|
||||
else if (gBattleMons[battlerAtk].volatiles.dragonCheer)
|
||||
critChance *= 2;
|
||||
|
||||
if (holdEffectCritStage > 0)
|
||||
critChance *= 4 * holdEffectCritStage;
|
||||
|
||||
if (abilityAtk == ABILITY_SUPER_LUCK)
|
||||
critChance *= 4;
|
||||
|
||||
if (critChance > 255)
|
||||
critChance = 255;
|
||||
|
||||
// Prevented crits
|
||||
if (gSideStatuses[battlerDef] & SIDE_STATUS_LUCKY_CHANT)
|
||||
critChance = CRITICAL_HIT_BLOCKED;
|
||||
else if (abilityDef == ABILITY_BATTLE_ARMOR || abilityDef == ABILITY_SHELL_ARMOR)
|
||||
{
|
||||
if (recordAbility)
|
||||
RecordAbilityBattle(battlerDef, abilityDef);
|
||||
critChance = CRITICAL_HIT_BLOCKED;
|
||||
}
|
||||
|
||||
// Guaranteed crits
|
||||
else if (gBattleMons[battlerAtk].volatiles.laserFocus
|
||||
|| MoveAlwaysCrits(move)
|
||||
|| (abilityAtk == ABILITY_MERCILESS && gBattleMons[battlerDef].status1 & STATUS1_PSN_ANY))
|
||||
{
|
||||
critChance = CRITICAL_HIT_ALWAYS;
|
||||
}
|
||||
|
||||
return critChance;
|
||||
}
|
||||
|
||||
s32 GetCritHitOdds(s32 critChanceIndex)
|
||||
{
|
||||
if (critChanceIndex < 0)
|
||||
return -1;
|
||||
else
|
||||
return GetCriticalHitOdds(critChanceIndex);
|
||||
}
|
||||
|
||||
// Calculations have been moved to cmd_damagecalc. Please remove the macro from scripts
|
||||
// Will be set to unused next cycle
|
||||
static void Cmd_critcalc(void)
|
||||
{
|
||||
CMD_ARGS();
|
||||
|
||||
u32 partySlot = gBattlerPartyIndexes[gBattlerAttacker],
|
||||
moveTarget = GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove),
|
||||
battlerDef;
|
||||
bool32 calcSpreadMoveDamage = IsSpreadMove(moveTarget) && !IsBattleMoveStatus(gCurrentMove);
|
||||
enum HoldEffect holdEffectAtk = GetBattlerHoldEffect(gBattlerAttacker);
|
||||
gPotentialItemEffectBattler = gBattlerAttacker;
|
||||
|
||||
for (battlerDef = 0; battlerDef < gBattlersCount; battlerDef++)
|
||||
{
|
||||
if (gBattleStruct->calculatedDamageDone)
|
||||
break;
|
||||
|
||||
if (!calcSpreadMoveDamage && battlerDef != gBattlerTarget)
|
||||
continue;
|
||||
|
||||
if (IsBattlerInvalidForSpreadMove(gBattlerAttacker, battlerDef, moveTarget)
|
||||
|| gBattleStruct->noResultString[battlerDef] != CAN_DAMAGE
|
||||
|| gBattleStruct->moveResultFlags[battlerDef] & MOVE_RESULT_NO_EFFECT)
|
||||
continue;
|
||||
|
||||
enum Ability abilityDef = GetBattlerAbility(battlerDef);
|
||||
enum Ability abilityAtk = GetBattlerAbility(gBattlerAttacker);
|
||||
|
||||
if (GetGenConfig(GEN_CONFIG_CRIT_CHANCE) == GEN_1)
|
||||
gBattleStruct->critChance[battlerDef] = CalcCritChanceStageGen1(gBattlerAttacker, battlerDef, gCurrentMove, TRUE, abilityAtk, abilityDef, holdEffectAtk);
|
||||
else
|
||||
gBattleStruct->critChance[battlerDef] = CalcCritChanceStage(gBattlerAttacker, battlerDef, gCurrentMove, TRUE, abilityAtk, abilityDef, holdEffectAtk);
|
||||
|
||||
if (gBattleTypeFlags & (BATTLE_TYPE_WALLY_TUTORIAL | BATTLE_TYPE_FIRST_BATTLE))
|
||||
gSpecialStatuses[battlerDef].criticalHit = FALSE;
|
||||
else if (gBattleStruct->critChance[battlerDef] == -1)
|
||||
gSpecialStatuses[battlerDef].criticalHit = FALSE;
|
||||
else if (gBattleStruct->critChance[battlerDef] == -2)
|
||||
gSpecialStatuses[battlerDef].criticalHit = TRUE;
|
||||
else
|
||||
{
|
||||
if (GetGenConfig(GEN_CONFIG_CRIT_CHANCE) == GEN_1)
|
||||
gSpecialStatuses[battlerDef].criticalHit = RandomChance(RNG_CRITICAL_HIT, gBattleStruct->critChance[battlerDef], 256);
|
||||
else if (GetGenConfig(GEN_CONFIG_CRIT_CHANCE) == GEN_2)
|
||||
gSpecialStatuses[battlerDef].criticalHit = RandomChance(RNG_CRITICAL_HIT, GetCriticalHitOdds(gBattleStruct->critChance[battlerDef]), 256);
|
||||
else
|
||||
gSpecialStatuses[battlerDef].criticalHit = RandomChance(RNG_CRITICAL_HIT, 1, GetCriticalHitOdds(gBattleStruct->critChance[battlerDef]));
|
||||
}
|
||||
|
||||
// Counter for IF_CRITICAL_HITS_GE evolution condition.
|
||||
if (gSpecialStatuses[battlerDef].criticalHit && IsOnPlayerSide(gBattlerAttacker)
|
||||
&& !(gBattleTypeFlags & BATTLE_TYPE_MULTI && GetBattlerPosition(gBattlerAttacker) == B_POSITION_PLAYER_LEFT))
|
||||
gPartyCriticalHits[partySlot]++;
|
||||
}
|
||||
|
||||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||||
}
|
||||
|
||||
|
|
@ -11804,7 +11593,7 @@ static void Cmd_presentdamagecalculation(void)
|
|||
|
||||
if (gBattleStruct->presentBasePower)
|
||||
{
|
||||
gBattlescriptCurrInstr = BattleScript_HitFromCritCalc;
|
||||
gBattlescriptCurrInstr = BattleScript_HitFromDamageCalc;
|
||||
}
|
||||
else if (gBattleMons[gBattlerTarget].maxHP == gBattleMons[gBattlerTarget].hp)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -7429,7 +7429,7 @@ static inline u32 CalcMoveBasePowerAfterModifiers(struct DamageContext *ctx)
|
|||
case ABILITY_PROTOSYNTHESIS:
|
||||
{
|
||||
enum Stat defHighestStat = GetParadoxBoostedStatId(battlerDef);
|
||||
if (((ctx->weather & B_WEATHER_SUN && HasWeatherEffect()) || gDisableStructs[battlerDef].boosterEnergyActivated)
|
||||
if ((ctx->weather & B_WEATHER_SUN || gDisableStructs[battlerDef].boosterEnergyActivated)
|
||||
&& ((IsBattleMovePhysical(move) && defHighestStat == STAT_DEF) || (IsBattleMoveSpecial(move) && defHighestStat == STAT_SPDEF))
|
||||
&& !(gBattleMons[battlerDef].volatiles.transformed))
|
||||
modifier = uq4_12_multiply(modifier, UQ_4_12(0.7));
|
||||
|
|
@ -7729,7 +7729,7 @@ static inline u32 CalcAttackStat(struct DamageContext *ctx)
|
|||
if (!(gBattleMons[battlerAtk].volatiles.transformed))
|
||||
{
|
||||
enum Stat atkHighestStat = GetParadoxBoostedStatId(battlerAtk);
|
||||
if (((ctx->weather & B_WEATHER_SUN) && HasWeatherEffect()) || gDisableStructs[battlerAtk].boosterEnergyActivated)
|
||||
if (ctx->weather & B_WEATHER_SUN || gDisableStructs[battlerAtk].boosterEnergyActivated)
|
||||
{
|
||||
if ((IsBattleMovePhysical(move) && atkHighestStat == STAT_ATK) || (IsBattleMoveSpecial(move) && atkHighestStat == STAT_SPATK))
|
||||
modifier = uq4_12_multiply(modifier, UQ_4_12(1.3));
|
||||
|
|
@ -7748,7 +7748,7 @@ static inline u32 CalcAttackStat(struct DamageContext *ctx)
|
|||
}
|
||||
break;
|
||||
case ABILITY_ORICHALCUM_PULSE:
|
||||
if ((ctx->weather & B_WEATHER_SUN) && HasWeatherEffect() && IsBattleMovePhysical(move))
|
||||
if (ctx->weather & B_WEATHER_SUN && IsBattleMovePhysical(move))
|
||||
modifier = uq4_12_multiply(modifier, UQ_4_12(1.3333));
|
||||
break;
|
||||
case ABILITY_HADRON_ENGINE:
|
||||
|
|
@ -8531,16 +8531,6 @@ static inline s32 DoFutureSightAttackDamageCalc(struct DamageContext *ctx)
|
|||
return DoFutureSightAttackDamageCalcVars(ctx);
|
||||
}
|
||||
|
||||
#undef DAMAGE_APPLY_MODIFIER
|
||||
|
||||
static u32 GetWeather(void)
|
||||
{
|
||||
if (gBattleWeather == B_WEATHER_NONE || !HasWeatherEffect())
|
||||
return B_WEATHER_NONE;
|
||||
else
|
||||
return gBattleWeather;
|
||||
}
|
||||
|
||||
bool32 IsFutureSightAttackerInParty(u32 battlerAtk, u32 battlerDef, u32 move)
|
||||
{
|
||||
if (GetMoveEffect(move) != EFFECT_FUTURE_SIGHT)
|
||||
|
|
@ -8556,6 +8546,206 @@ bool32 IsFutureSightAttackerInParty(u32 battlerAtk, u32 battlerDef, u32 move)
|
|||
return &party[gWishFutureKnock.futureSightPartyIndex[battlerDef]] != &party[gBattlerPartyIndexes[battlerAtk]];
|
||||
}
|
||||
|
||||
|
||||
#undef DAMAGE_APPLY_MODIFIER
|
||||
|
||||
static u32 GetWeather(void)
|
||||
{
|
||||
if (gBattleWeather == B_WEATHER_NONE || !HasWeatherEffect())
|
||||
return B_WEATHER_NONE;
|
||||
else
|
||||
return gBattleWeather;
|
||||
}
|
||||
|
||||
// The chance is 1/N for each stage.
|
||||
static const u32 sGen7CriticalHitOdds[] = {24, 8, 2, 1, 1}; // 1/X
|
||||
static const u32 sGen6CriticalHitOdds[] = {16, 8, 2, 1, 1}; // 1/X
|
||||
static const u32 sCriticalHitOdds[] = {16, 8, 4, 3, 2}; // 1/X, Gens 3,4,5
|
||||
static const u32 sGen2CriticalHitOdds[] = {17, 32, 64, 85, 128}; // X/256
|
||||
|
||||
static inline u32 GetCriticalHitOdds(u32 critChance)
|
||||
{
|
||||
if (GetGenConfig(GEN_CONFIG_CRIT_CHANCE) >= GEN_7)
|
||||
return sGen7CriticalHitOdds[critChance];
|
||||
if (GetGenConfig(GEN_CONFIG_CRIT_CHANCE) == GEN_6)
|
||||
return sGen6CriticalHitOdds[critChance];
|
||||
if (GetGenConfig(GEN_CONFIG_CRIT_CHANCE) == GEN_2)
|
||||
return sGen2CriticalHitOdds[critChance];
|
||||
|
||||
return sCriticalHitOdds[critChance];
|
||||
}
|
||||
|
||||
static inline u32 IsBattlerLeekAffected(u32 battler, enum HoldEffect holdEffect)
|
||||
{
|
||||
if (holdEffect == HOLD_EFFECT_LEEK)
|
||||
{
|
||||
return GET_BASE_SPECIES_ID(gBattleMons[battler].species) == SPECIES_FARFETCHD
|
||||
|| gBattleMons[battler].species == SPECIES_SIRFETCHD;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static inline u32 GetHoldEffectCritChanceIncrease(u32 battler, enum HoldEffect holdEffect)
|
||||
{
|
||||
u32 critStageIncrease = 0;
|
||||
|
||||
switch (holdEffect)
|
||||
{
|
||||
case HOLD_EFFECT_SCOPE_LENS:
|
||||
critStageIncrease = 1;
|
||||
break;
|
||||
case HOLD_EFFECT_LUCKY_PUNCH:
|
||||
if (gBattleMons[battler].species == SPECIES_CHANSEY)
|
||||
critStageIncrease = 2;
|
||||
break;
|
||||
case HOLD_EFFECT_LEEK:
|
||||
if (IsBattlerLeekAffected(battler, holdEffect))
|
||||
critStageIncrease = 2;
|
||||
break;
|
||||
default:
|
||||
critStageIncrease = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return critStageIncrease;
|
||||
}
|
||||
|
||||
s32 CalcCritChanceStage(struct DamageContext *ctx)
|
||||
{
|
||||
s32 critChance = 0;
|
||||
|
||||
if (gSideStatuses[ctx->battlerDef] & SIDE_STATUS_LUCKY_CHANT)
|
||||
{
|
||||
critChance = CRITICAL_HIT_BLOCKED;
|
||||
}
|
||||
else if (gBattleMons[ctx->battlerAtk].volatiles.laserFocus
|
||||
|| MoveAlwaysCrits(ctx->move)
|
||||
|| (ctx->abilityAtk == ABILITY_MERCILESS && gBattleMons[ctx->battlerDef].status1 & STATUS1_PSN_ANY))
|
||||
{
|
||||
critChance = CRITICAL_HIT_ALWAYS;
|
||||
}
|
||||
else
|
||||
{
|
||||
critChance = (gBattleMons[ctx->battlerAtk].volatiles.focusEnergy != 0 ? 2 : 0)
|
||||
+ (gBattleMons[ctx->battlerAtk].volatiles.dragonCheer != 0 ? 1 : 0)
|
||||
+ GetMoveCriticalHitStage(ctx->move)
|
||||
+ GetHoldEffectCritChanceIncrease(ctx->battlerAtk, ctx->holdEffectAtk)
|
||||
+ ((B_AFFECTION_MECHANICS == TRUE && GetBattlerAffectionHearts(ctx->battlerAtk) == AFFECTION_FIVE_HEARTS) ? 2 : 0)
|
||||
+ (ctx->abilityAtk == ABILITY_SUPER_LUCK ? 1 : 0)
|
||||
+ gBattleStruct->bonusCritStages[ctx->battlerAtk];
|
||||
|
||||
if (critChance >= ARRAY_COUNT(sCriticalHitOdds))
|
||||
critChance = ARRAY_COUNT(sCriticalHitOdds) - 1;
|
||||
}
|
||||
|
||||
if (critChance != CRITICAL_HIT_BLOCKED && (ctx->abilityDef == ABILITY_BATTLE_ARMOR || ctx->abilityDef == ABILITY_SHELL_ARMOR))
|
||||
{
|
||||
// Record ability only if move had 100% chance to get a crit
|
||||
if (ctx->updateFlags)
|
||||
{
|
||||
if (critChance == CRITICAL_HIT_ALWAYS)
|
||||
RecordAbilityBattle(ctx->battlerDef, ctx->abilityDef);
|
||||
else if (GetCriticalHitOdds(critChance) == 1)
|
||||
RecordAbilityBattle(ctx->battlerDef, ctx->abilityDef);
|
||||
}
|
||||
critChance = CRITICAL_HIT_BLOCKED;
|
||||
}
|
||||
|
||||
return critChance;
|
||||
}
|
||||
|
||||
// Bulbapedia: https://bulbapedia.bulbagarden.net/wiki/Critical_hit#Generation_I
|
||||
// Crit chance = Threshold / 256, Threshold maximum of 255
|
||||
// Threshold = Base Speed / 2
|
||||
// High crit move = 8 * (Base Speed / 2)
|
||||
// Focus Energy = 4 * (Base Speed / 2)
|
||||
s32 CalcCritChanceStageGen1(struct DamageContext *ctx)
|
||||
{
|
||||
s32 critChance = 0;
|
||||
s32 moveCritStage = GetMoveCriticalHitStage(ctx->move);
|
||||
s32 bonusCritStage = gBattleStruct->bonusCritStages[ctx->battlerAtk]; // G-Max Chi Strike
|
||||
u32 holdEffectCritStage = GetHoldEffectCritChanceIncrease(ctx->battlerAtk, ctx->holdEffectAtk);
|
||||
u16 baseSpeed = GetSpeciesBaseSpeed(gBattleMons[ctx->battlerAtk].species);
|
||||
|
||||
critChance = baseSpeed / 2;
|
||||
|
||||
// Crit scaling
|
||||
if (moveCritStage > 0)
|
||||
critChance *= 8 * moveCritStage;
|
||||
|
||||
if (bonusCritStage > 0)
|
||||
critChance *= bonusCritStage;
|
||||
|
||||
if (gBattleMons[ctx->battlerAtk].volatiles.focusEnergy)
|
||||
critChance *= 4;
|
||||
else if (gBattleMons[ctx->battlerAtk].volatiles.dragonCheer)
|
||||
critChance *= 2;
|
||||
|
||||
if (holdEffectCritStage > 0)
|
||||
critChance *= 4 * holdEffectCritStage;
|
||||
|
||||
if (ctx->abilityAtk == ABILITY_SUPER_LUCK)
|
||||
critChance *= 4;
|
||||
|
||||
if (critChance > 255)
|
||||
critChance = 255;
|
||||
|
||||
// Prevented crits
|
||||
if (gSideStatuses[ctx->battlerDef] & SIDE_STATUS_LUCKY_CHANT)
|
||||
critChance = CRITICAL_HIT_BLOCKED;
|
||||
else if (ctx->abilityDef == ABILITY_BATTLE_ARMOR || ctx->abilityDef == ABILITY_SHELL_ARMOR)
|
||||
{
|
||||
if (ctx->updateFlags)
|
||||
RecordAbilityBattle(ctx->battlerDef, ctx->abilityDef);
|
||||
critChance = CRITICAL_HIT_BLOCKED;
|
||||
}
|
||||
|
||||
// Guaranteed crits
|
||||
else if (gBattleMons[ctx->battlerAtk].volatiles.laserFocus
|
||||
|| MoveAlwaysCrits(ctx->move)
|
||||
|| (ctx->abilityAtk == ABILITY_MERCILESS && gBattleMons[ctx->battlerDef].status1 & STATUS1_PSN_ANY))
|
||||
{
|
||||
critChance = CRITICAL_HIT_ALWAYS;
|
||||
}
|
||||
|
||||
return critChance;
|
||||
}
|
||||
|
||||
static bool32 IsCriticalHit(struct DamageContext *ctx)
|
||||
{
|
||||
bool32 isCrit = FALSE;
|
||||
s32 critChance = 0;
|
||||
|
||||
if (GetGenConfig(GEN_CONFIG_CRIT_CHANCE) == GEN_1)
|
||||
critChance = CalcCritChanceStageGen1(ctx);
|
||||
else
|
||||
critChance = CalcCritChanceStage(ctx);
|
||||
|
||||
if (gBattleTypeFlags & (BATTLE_TYPE_WALLY_TUTORIAL | BATTLE_TYPE_FIRST_BATTLE))
|
||||
isCrit = FALSE;
|
||||
else if (critChance == -1)
|
||||
isCrit = FALSE;
|
||||
else if (critChance == -2)
|
||||
isCrit = TRUE;
|
||||
else
|
||||
{
|
||||
if (GetGenConfig(GEN_CONFIG_CRIT_CHANCE) == GEN_1)
|
||||
isCrit = RandomChance(RNG_CRITICAL_HIT, critChance, 256);
|
||||
else if (GetGenConfig(GEN_CONFIG_CRIT_CHANCE) == GEN_2)
|
||||
isCrit = RandomChance(RNG_CRITICAL_HIT, GetCriticalHitOdds(critChance), 256);
|
||||
else
|
||||
isCrit = RandomChance(RNG_CRITICAL_HIT, 1, GetCriticalHitOdds(critChance));
|
||||
}
|
||||
|
||||
// Counter for IF_CRITICAL_HITS_GE evolution condition.
|
||||
if (isCrit && IsOnPlayerSide(ctx->battlerAtk)
|
||||
&& !(gBattleTypeFlags & BATTLE_TYPE_MULTI && GetBattlerPosition(ctx->battlerAtk) == B_POSITION_PLAYER_LEFT))
|
||||
gPartyCriticalHits[gBattlerPartyIndexes[ctx->battlerAtk]]++;
|
||||
|
||||
gSpecialStatuses[ctx->battlerDef].criticalHit = isCrit;
|
||||
return isCrit;
|
||||
}
|
||||
|
||||
s32 CalculateMoveDamage(struct DamageContext *ctx)
|
||||
{
|
||||
ctx->weather = GetWeather();
|
||||
|
|
@ -8565,6 +8755,8 @@ s32 CalculateMoveDamage(struct DamageContext *ctx)
|
|||
ctx->holdEffectDef = GetBattlerHoldEffect(ctx->battlerDef);
|
||||
|
||||
ctx->typeEffectivenessModifier = CalcTypeEffectivenessMultiplier(ctx);
|
||||
if (!ctx->isSelfInflicted)
|
||||
ctx->isCrit = IsCriticalHit(ctx);
|
||||
|
||||
if (IsFutureSightAttackerInParty(ctx->battlerAtk, ctx->battlerDef, ctx->move))
|
||||
return DoFutureSightAttackDamageCalc(ctx);
|
||||
|
|
@ -8615,7 +8807,7 @@ static inline void MulByTypeEffectiveness(struct DamageContext *ctx, uq4_12_t *m
|
|||
mod = UQ_4_12(2.0);
|
||||
|
||||
// B_WEATHER_STRONG_WINDS weakens Super Effective moves against Flying-type Pokémon
|
||||
if (gBattleWeather & B_WEATHER_STRONG_WINDS && HasWeatherEffect() && !ctx->isAnticipation)
|
||||
if (ctx->weather & B_WEATHER_STRONG_WINDS && !ctx->isAnticipation)
|
||||
{
|
||||
if (defType == TYPE_FLYING && mod >= UQ_4_12(2.0))
|
||||
mod = UQ_4_12(1.0);
|
||||
|
|
@ -10371,7 +10563,6 @@ void ClearDamageCalcResults(void)
|
|||
for (u32 battler = 0; battler < MAX_BATTLERS_COUNT; battler++)
|
||||
{
|
||||
gBattleStruct->moveDamage[battler] = 0;
|
||||
gBattleStruct->critChance[battler] = 0;
|
||||
gBattleStruct->moveResultFlags[battler] = CAN_DAMAGE;
|
||||
gBattleStruct->noResultString[battler] = 0;
|
||||
gBattleStruct->missStringId[battler] = 0;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user