#include "DamageLibrary.hpp" #include #include "../PkmnScriptHook.hpp" #include "../Pokemon/Pokemon.hpp" using HitData = const CreatureLib::Battling::ExecutingAttack::HitData; uint32_t PkmnLib::Battling::DamageLibrary::GetDamage(CreatureLib::Battling::ExecutingAttack* attack, CreatureLib::Battling::Creature* target, uint8_t hitIndex, const HitData& hitData) const { auto levelMod = static_cast(2 * attack->GetUser()->GetLevel()) / 5; auto bp = hitData.GetBasePower(); auto statMod = GetStatModifier(attack, target, hitIndex, hitData); auto damageMod = GetDamageModifier(attack, target, hitIndex, hitData); // HOOK: Modify stat modifier auto floatDamage = (((levelMod * static_cast(bp) * statMod) / 50) + 2) * damageMod; uint32_t damage; if (floatDamage < 0) { damage = 0; } else if (floatDamage >= (float)UINT32_MAX) { damage = UINT32_MAX; } else { // TODO: C++ 20 - add [[likely]] attribute when supported by both gcc and Clang damage = static_cast(floatDamage); } // HOOK: Override damage return damage; } uint8_t PkmnLib::Battling::DamageLibrary::GetBasePower(CreatureLib::Battling::ExecutingAttack* attack, [[maybe_unused]] CreatureLib::Battling::Creature* target, [[maybe_unused]] uint8_t hitIndex, [[maybe_unused]] const HitData& hitData) const { auto bp = attack->GetAttack()->GetAttack()->GetBasePower(); // HOOK: modify base power. return bp; } float PkmnLib::Battling::DamageLibrary::GetStatModifier(CreatureLib::Battling::ExecutingAttack* attack, CreatureLib::Battling::Creature* target, uint8_t, const HitData& hitData) const { auto user = attack->GetUser(); // HOOK: allow overriding for which users stat we use. CreatureLib::Library::Statistic offensiveStat; CreatureLib::Library::Statistic defensiveStat; auto learnedMove = attack->GetAttack().ForceAs(); auto moveData = learnedMove->GetMoveData(); if (moveData->GetCategory() == Library::MoveCategory::Physical) { offensiveStat = Library::Statistic::PhysicalAttack; defensiveStat = Library::Statistic::PhysicalDefense; } else { offensiveStat = Library::Statistic::SpecialAttack; defensiveStat = Library::Statistic::SpecialDefense; } auto bypassDefensive = hitData.IsCritical() && target->GetStatBoost(defensiveStat) > 0; // HOOK: allow bypassing defensive stat modifiers. auto bypassOffensive = hitData.IsCritical() && user->GetStatBoost(offensiveStat) < 0; // HOOK: Allow bypassing offensive stat modifiers. float offensiveValue; float defensiveValue; if (bypassOffensive) { offensiveValue = user->GetFlatStat(offensiveStat); } else { offensiveValue = user->GetBoostedStat(offensiveStat); } if (bypassDefensive) { defensiveValue = target->GetFlatStat(defensiveStat); } else { defensiveValue = target->GetBoostedStat(defensiveStat); } return offensiveValue / defensiveValue; } float PkmnLib::Battling::DamageLibrary::GetDamageModifier(CreatureLib::Battling::ExecutingAttack* attack, CreatureLib::Battling::Creature* target, uint8_t hitIndex, const HitData& hitData) const { float mod = 1; if (attack->GetTargetCount() > 1) mod *= 0.75; if (hitData.IsCritical()) { float critModifier = 1.5; PKMN_HOOK(OverrideCriticalModifier, attack, attack, target, hitIndex, &critModifier); mod *= critModifier; } float randPercentage = 85 + attack->GetUser()->GetBattle()->GetRandom()->Get(0, 16); mod *= randPercentage / 100.0; if (attack->GetUser()->HasType(hitData.GetType())) { float stabModifier = 1.5; PKMN_HOOK(OverrideSTABModifier, attack, attack, target, hitIndex, &stabModifier); mod *= stabModifier; } mod *= hitData.GetEffectiveness(); // HOOK: Modify damage modifier. return mod; }