diff --git a/src/Battling/Library/DamageLibrary.cpp b/src/Battling/Library/DamageLibrary.cpp index 2362639..8e194dc 100644 --- a/src/Battling/Library/DamageLibrary.cpp +++ b/src/Battling/Library/DamageLibrary.cpp @@ -5,22 +5,28 @@ using HitData = const CreatureLib::Battling::ExecutingAttack::HitData; +inline constexpr float to_f(i32 a) { return static_cast(a); } +inline constexpr float fl(float a) { return std::floor(a); } + 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 levelMod = fl((2 * attack->GetUser()->GetLevel()) / 5.0f) + 2; 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; + auto floatDamage = fl(levelMod * bp); + floatDamage = fl(floatDamage * statMod); + floatDamage = fl(floatDamage / 50) + 2; + floatDamage = fl(floatDamage * damageMod); + uint32_t damage = 0; 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 + } else { damage = static_cast(floatDamage); } // HOOK: Override damage @@ -82,8 +88,8 @@ float PkmnLib::Battling::DamageLibrary::GetDamageModifier(CreatureLib::Battling: PKMN_HOOK(OverrideCriticalModifier, attack, attack, target, hitIndex, &critModifier); mod *= critModifier; } - Ensure(attack->GetUser()->GetBattle().GetValue()); if (_hasRandomness) { + Ensure(attack->GetUser()->GetBattle().GetValue()); float randPercentage = 85 + attack->GetUser()->GetBattle().GetValue()->GetRandom()->Get(0, 16); mod *= randPercentage / 100.0; } diff --git a/tests/BattleTests/DamageTests.cpp b/tests/BattleTests/DamageTests.cpp new file mode 100644 index 0000000..9202b5e --- /dev/null +++ b/tests/BattleTests/DamageTests.cpp @@ -0,0 +1,42 @@ +#ifdef TESTS_BUILD +#include "../../extern/doctest.hpp" +#include "../../src/Battling/Library/DamageLibrary.hpp" +#include "../../src/Battling/Pokemon/CreatePokemon.hpp" +#include "../TestLibrary/TestLibrary.hpp" + +using namespace PkmnLib::Battling; + +TEST_CASE("Correct rounding for damage.") { + auto lib = TestLibrary::GetLibrary(); + auto mon1 = CreatePokemon(lib, "testCharizard"_cnc, 100) + .WithIndividualValues(31, 31, 31, 31, 31, 31) + .WithEffortValues(0, 0, 0, 0, 0, 0) + .WithNature("neutralNature"_cnc) + .WithGender(CreatureLib::Library::Gender::Male) + .IsAllowedExperienceGain(false) + .Build(); + auto mon2 = CreatePokemon(lib, "testVenusaur"_cnc, 100) + .WithIndividualValues(31, 31, 31, 31, 31, 31) + .WithEffortValues(0, 0, 0, 0, 0, 0) + .WithNature("neutralNature"_cnc) + .WithGender(CreatureLib::Library::Gender::Male) + .IsAllowedExperienceGain(false) + .Build(); + + auto move = PkmnLib::Library::MoveData("test", 0, PkmnLib::Library::MoveCategory::Physical, 55, 100, 10, + CreatureLib::Library::AttackTarget::All, 0, nullptr, {}); + + auto attack = PkmnLib::Battling::LearnedMove(&move, CreatureLib::Battling::AttackLearnMethod::Unknown); + + auto executingAttack = CreatureLib::Battling::ExecutingAttack({mon2}, 1, mon1, &attack, &move, nullptr); + + auto hit = executingAttack.GetHitData(mon2, 0); + hit.SetBasePower(55); + hit.SetEffectiveness(2.0f); + hit.SetCritical(0); + + auto damage = lib->GetDamageLibrary()->GetDamage(&executingAttack, mon2, 0, hit); + REQUIRE_EQ(144, damage); +} + +#endif \ No newline at end of file diff --git a/tests/TestLibrary/TestLibrary.cpp b/tests/TestLibrary/TestLibrary.cpp index abf814a..ddf1452 100644 --- a/tests/TestLibrary/TestLibrary.cpp +++ b/tests/TestLibrary/TestLibrary.cpp @@ -37,6 +37,23 @@ PkmnLib::Library::SpeciesLibrary* TestLibrary::BuildSpeciesLibrary() { {"testHiddenAbility"_cnc}, new PkmnLib::Library::LearnableMoves(100)), 0.5f, "testGrowthRate"_cnc, 100, 100, {"testEggGroup"_cnc})); + lib->Insert("testCharizard"_cnc.GetHash(), + new PkmnLib::Library::PokemonSpecies( + 5, "testCharizard"_cnc, + new PkmnLib::Library::PokemonForme( + "default"_cnc, 1.7f, 90.5f, 240, {0, 4}, + CreatureLib::Library::StatisticSet(78, 84, 78, 109, 85, 100), {"testAbility"_cnc}, + {"testHiddenAbility"_cnc}, new PkmnLib::Library::LearnableMoves(100)), + 0.5f, "testGrowthRate"_cnc, 100, 100, {"testEggGroup"_cnc})); + lib->Insert("testVenusaur"_cnc.GetHash(), + new PkmnLib::Library::PokemonSpecies( + 6, "testVenusaur"_cnc, + new PkmnLib::Library::PokemonForme( + "default"_cnc, 2.0f, 100.0f, 236, {0, 4}, + CreatureLib::Library::StatisticSet(80, 82, 83, 100, 100, 80), {"testAbility"_cnc}, + {"testHiddenAbility"_cnc}, new PkmnLib::Library::LearnableMoves(100)), + 0.5f, "testGrowthRate"_cnc, 100, 100, {"testEggGroup"_cnc})); + return lib; } PkmnLib::Library::MoveLibrary* TestLibrary::BuildMoveLibrary() { diff --git a/tests/TestLibrary/TestLibrary.hpp b/tests/TestLibrary/TestLibrary.hpp index 28ac718..d6db382 100644 --- a/tests/TestLibrary/TestLibrary.hpp +++ b/tests/TestLibrary/TestLibrary.hpp @@ -26,7 +26,7 @@ public: auto statCalc = new PkmnLib::Battling::StatCalculator(); auto scriptResolver = PkmnLib::Battling::BattleLibrary::CreateScriptResolver(); auto lib = new PkmnLib::Battling::BattleLibrary( - BuildStaticLibrary(), statCalc, new PkmnLib::Battling::DamageLibrary(), + BuildStaticLibrary(), statCalc, new PkmnLib::Battling::DamageLibrary(false), new PkmnLib::Battling::ExperienceLibrary(), scriptResolver, new PkmnLib::Battling::MiscLibrary(GetTime)); scriptResolver->Initialize(lib); return lib;