diff --git a/src/Battling/Library/ExperienceLibrary.cpp b/src/Battling/Library/ExperienceLibrary.cpp index 3a7b3e9..6fb43e9 100644 --- a/src/Battling/Library/ExperienceLibrary.cpp +++ b/src/Battling/Library/ExperienceLibrary.cpp @@ -1,7 +1,19 @@ #include "ExperienceLibrary.hpp" +#include #include "../PkmnScriptHook.hpp" #include "../Pokemon/Pokemon.hpp" +static inline uint32_t CalculateDynamicExperience(uint8_t level, float v1, CreatureLib::Battling::Creature* op, + CreatureLib::Battling::Creature* faintedMon) { + float a = 2 * level + 10; + float b = level + op->GetLevel() + 10; + float v2 = (a * a * sqrt(a)) / (b * b * sqrt(b)); + uint32_t experienceGain = v1 * v2 + 1; + // TODO: Check owner and international + PKMN_HOOK(ModifyExperienceGain, op, faintedMon, op, &experienceGain); + return experienceGain; +} + void PkmnLib::Battling::ExperienceLibrary::HandleExperienceGain( CreatureLib::Battling::Creature* faintedMon, const std::unordered_set& opponents) const { @@ -9,17 +21,45 @@ void PkmnLib::Battling::ExperienceLibrary::HandleExperienceGain( auto fainted = dynamic_cast(faintedMon); auto expGain = fainted->GetForme()->GetBaseExperience(); auto level = fainted->GetLevel(); - // TODO exp share - auto v1 = (expGain * level) / 5; + float v1 = (expGain * level) / 5; for (auto op : opponents) { if (!op->AllowedExperienceGain()) continue; - auto v2 = pow(2 * level + 10, 2.5) / pow(level + op->GetLevel() + 10, 2.5); - uint32_t experienceGain = v1 * v2 + 1; - // TODO: Check owner and international - PKMN_HOOK(ModifyExperienceGain, op, faintedMon, op, experienceGain); + auto experienceGain = CalculateDynamicExperience(level, v1, op, fainted); + op->AddExperience(experienceGain); + } + + auto battle = fainted->GetBattle(); + if (battle == nullptr) { + return; + } + + std::unordered_set shareExperience; + for (const auto& party : battle->GetParties()) { + for (const auto& mon : party->GetParty()->GetParty()) { + if (mon == nullptr) + continue; + // If the mon is not allowed to gain experience, or is fainted, don't check for experience sharing. + if (!mon->AllowedExperienceGain() || mon->IsFainted()) + continue; + // If the mon is already in the opponents set, don't check for experience sharing. + if (opponents.find(mon) != opponents.end()) + continue; + bool sharedExp = false; + PKMN_HOOK(DoesShareExperience, mon, faintedMon, mon, &sharedExp); + if (sharedExp) { + shareExperience.insert(mon); + } + } + } + if (shareExperience.size() == 0) + return; + + v1 /= 2; + for (auto op : shareExperience) { + auto experienceGain = CalculateDynamicExperience(level, v1, op, faintedMon); op->AddExperience(experienceGain); } } diff --git a/src/Battling/PkmnScript.hpp b/src/Battling/PkmnScript.hpp index 0dc95dd..1de4c68 100644 --- a/src/Battling/PkmnScript.hpp +++ b/src/Battling/PkmnScript.hpp @@ -8,7 +8,9 @@ namespace PkmnLib::Battling { virtual void ModifyCriticalStage(CreatureLib::Battling::ExecutingAttack* attack, CreatureLib::Battling::Creature* target, uint8_t hit, uint8_t* critStage){}; virtual void ModifyExperienceGain(CreatureLib::Battling::Creature* faintedMon, - CreatureLib::Battling::Creature* winningMon, uint32_t experienceGain){}; + CreatureLib::Battling::Creature* winningMon, uint32_t* experienceGain){}; + virtual void DoesShareExperience(CreatureLib::Battling::Creature* faintedMon, + CreatureLib::Battling::Creature* winningMon, bool* shareExperience){}; }; } diff --git a/src/ScriptResolving/AngelScript/AngelScriptScript.cpp b/src/ScriptResolving/AngelScript/AngelScriptScript.cpp index f35476a..c6879c1 100644 --- a/src/ScriptResolving/AngelScript/AngelScriptScript.cpp +++ b/src/ScriptResolving/AngelScript/AngelScriptScript.cpp @@ -258,3 +258,19 @@ void AngelScriptScript::ModifyCriticalStage(CreatureLib::Battling::ExecutingAtta ctx->SetArgAddress(3, critStage); }) } +void AngelScriptScript::ModifyExperienceGain(CreatureLib::Battling::Creature* faintedMon, + CreatureLib::Battling::Creature* winningMon, uint32_t* experienceGain) { + CALL_HOOK(ModifyExperienceGain, { + ctx->SetArgObject(0, (void*)faintedMon); + ctx->SetArgObject(1, (void*)winningMon); + ctx->SetArgAddress(2, experienceGain); + }) +} +void AngelScriptScript::DoesShareExperience(CreatureLib::Battling::Creature* faintedMon, + CreatureLib::Battling::Creature* winningMon, bool* shareExperience) { + CALL_HOOK(DoesShareExperience, { + ctx->SetArgObject(0, (void*)faintedMon); + ctx->SetArgObject(1, (void*)winningMon); + ctx->SetArgAddress(2, shareExperience); + }) +} diff --git a/src/ScriptResolving/AngelScript/AngelScriptScript.hpp b/src/ScriptResolving/AngelScript/AngelScriptScript.hpp index 4bfaaa0..27bdc13 100644 --- a/src/ScriptResolving/AngelScript/AngelScriptScript.hpp +++ b/src/ScriptResolving/AngelScript/AngelScriptScript.hpp @@ -104,6 +104,11 @@ public: void ModifyCriticalStage(CreatureLib::Battling::ExecutingAttack* attack, CreatureLib::Battling::Creature* target, uint8_t hit, uint8_t* critStage) override; + + void ModifyExperienceGain(CreatureLib::Battling::Creature* faintedMon, CreatureLib::Battling::Creature* winningMon, + uint32_t* experienceGain) override; + void DoesShareExperience(CreatureLib::Battling::Creature* faintedMon, CreatureLib::Battling::Creature* winningMon, + bool* shareExperience) override; }; #undef CALL_HOOK diff --git a/src/ScriptResolving/AngelScript/AngelScriptTypeInfo.hpp b/src/ScriptResolving/AngelScript/AngelScriptTypeInfo.hpp index 09781a6..2ebb203 100644 --- a/src/ScriptResolving/AngelScript/AngelScriptTypeInfo.hpp +++ b/src/ScriptResolving/AngelScript/AngelScriptTypeInfo.hpp @@ -131,6 +131,10 @@ public: SCRIPT_HOOK_FUNCTION( ModifyCriticalStage, "void ModifyCriticalStage(ExecutingMove@ attack, Pokemon@ target, uint8 hit, uint8& critStage)"); + SCRIPT_HOOK_FUNCTION(ModifyExperienceGain, + "void ModifyExperienceGain(Pokemon@ faintedMon, Pokemon@ winningMon, uint32& critStage)"); + SCRIPT_HOOK_FUNCTION(DoesShareExperience, + "void DoesShareExperience(Pokemon@ faintedMon, Pokemon@ winningMon, bool& shareExperience)"); }; #undef SCRIPT_HOOK_FUNCTION diff --git a/src/ScriptResolving/AngelScript/TypeRegistry/BasicScriptClass.cpp b/src/ScriptResolving/AngelScript/TypeRegistry/BasicScriptClass.cpp index 050e877..fe0a53f 100644 --- a/src/ScriptResolving/AngelScript/TypeRegistry/BasicScriptClass.cpp +++ b/src/ScriptResolving/AngelScript/TypeRegistry/BasicScriptClass.cpp @@ -39,6 +39,8 @@ shared abstract class PkmnScript { // PkmnLib methods void ModifyCriticalStage(ExecutingMove@ attack, Pokemon@ target, uint8 hit, uint8& critStage){}; + void ModifyExperienceGain(Pokemon@ faintedMon, Pokemon@ winningMon, uint32& critStage){}; + void DoesShareExperience(Pokemon@ faintedMon, Pokemon@ winningMon, bool& shareExperience){}; } )"); assert(r >= 0);