diff --git a/CInterface/Battling/BattleLibrary.cpp b/CInterface/Battling/BattleLibrary.cpp index e7fd285..f3744fd 100644 --- a/CInterface/Battling/BattleLibrary.cpp +++ b/CInterface/Battling/BattleLibrary.cpp @@ -6,9 +6,12 @@ export uint8_t PkmnLib_BattleLibrary_Construct(BattleLibrary*& out, PkmnLib::Lib StatCalculator* statCalculator, DamageLibrary* damageLibrary, ExperienceLibrary* experienceLibrary, PkmnLib::Battling::ScriptResolver* scriptResolver, - MiscLibrary* miscLibrary) { + MiscLibrary* miscLibrary, CaptureLibrary* captureLibrary) { Try(out = new BattleLibrary(staticLib, statCalculator, damageLibrary, experienceLibrary, scriptResolver, - miscLibrary)); + miscLibrary, captureLibrary)); } export void PkmnLib_BattleLibrary_Destruct(BattleLibrary* p) { delete p; } + +export uint8_t PkmnLib_CaptureLibrary_Construct(CaptureLibrary*& out) { Try(out = new CaptureLibrary()); } +export void PkmnLib_CaptureLibrary_Destruct(CaptureLibrary* p) { delete p; } diff --git a/CInterface/Battling/EventHooks.cpp b/CInterface/Battling/EventHooks.cpp index c74e106..c3239ca 100644 --- a/CInterface/Battling/EventHooks.cpp +++ b/CInterface/Battling/EventHooks.cpp @@ -1,3 +1,4 @@ +#include "../../src/Battling/EventHooks/CaptureAttemptEvent.hpp" #include "../../src/Battling/EventHooks/WeatherChangeEvent.hpp" #include "../Core.hpp" using namespace PkmnLib::Battling; @@ -5,4 +6,13 @@ using namespace PkmnLib::Battling; export void PkmnLib_WeatherChangeEvent_Destruct(WeatherChangeEvent* p) { delete p; } export const char* PkmnLib_WeatherChangeEvent_GetWeatherName(WeatherChangeEvent* p) { return p->GetWeatherName().c_str(); +} + +export void PkmnLib_CaptureAttemptEvent_Destruct(CaptureAttemptEvent* p) { delete p; } +export const PkmnLib::Battling::Pokemon* PkmnLib_CaptureAttemptEvent_GetPokemon(CaptureAttemptEvent* p) { + return p->GetPokemon(); +} +export const PkmnLib::Battling::CaptureLibrary::CaptureResult +PkmnLib_CaptureAttemptEvent_GetResult(CaptureAttemptEvent* p) { + return p->GetResult(); } \ No newline at end of file diff --git a/CInterface/Battling/Pokemon.cpp b/CInterface/Battling/Pokemon.cpp index 519e741..2f91f79 100644 --- a/CInterface/Battling/Pokemon.cpp +++ b/CInterface/Battling/Pokemon.cpp @@ -30,6 +30,7 @@ PkmnLib_Pokemon_Construct(Pokemon*& out, const BattleLibrary* library, const Pkm export void PkmnLib_Pokemon_Destruct(const Pokemon* p) { delete p; } SIMPLE_GET_FUNC(Pokemon, IsShiny, bool) +SIMPLE_GET_FUNC(Pokemon, WasCaught, bool) SIMPLE_GET_FUNC_SMART_PTR(Pokemon, GetNature, const PkmnLib::Library::Nature*) export u8 PkmnLib_Pokemon_GetIndividualValue(const Pokemon* p, CreatureLib::Library::Statistic stat) { @@ -55,4 +56,8 @@ export void PkmnLib_Pokemon_SetIsEgg(Pokemon* p, bool value) { p->SetIsEgg(value export u8 PkmnLib_Pokemon_Evolve(Pokemon* p, const PkmnLib::Library::PokemonSpecies* species, const PkmnLib::Library::PokemonForme* forme) { Try(p->Evolve(species, forme);) +} + +export u8 PkmnLib_Pokemon_AttemptCapture(Pokemon* p, PkmnLib::Library::Item* non_null catchItem) { + Try(p->AttemptCapture(catchItem);) } \ No newline at end of file diff --git a/src/Battling/EventHooks/CaptureAttemptEvent.hpp b/src/Battling/EventHooks/CaptureAttemptEvent.hpp new file mode 100644 index 0000000..f309bf5 --- /dev/null +++ b/src/Battling/EventHooks/CaptureAttemptEvent.hpp @@ -0,0 +1,26 @@ +#ifndef PKMNLIB_CAPTUREATTEMPTEVENT_HPP +#define PKMNLIB_CAPTUREATTEMPTEVENT_HPP +#include +#include "../Pokemon/Pokemon.hpp" +#include "Arbutils/Memory/Memory.hpp" +#include "PkmnEventKind.hpp" + +namespace PkmnLib::Battling { + class CaptureAttemptEvent final : public CreatureLib::Battling::EventData { + ArbUt::BorrowedPtr _pokemon; + CaptureLibrary::CaptureResult _result; + + public: + explicit CaptureAttemptEvent(PkmnLib::Battling::Pokemon* pokemon, const CaptureLibrary::CaptureResult& result) + : _pokemon(pokemon), _result(result) {} + + [[nodiscard]] CreatureLib::Battling::EventDataKind GetKind() const noexcept override { + return static_cast(PkmnEventDataKind::CaptureAttempt); + } + + const ArbUt::BorrowedPtr& GetPokemon() const noexcept { return _pokemon; } + const CaptureLibrary::CaptureResult& GetResult() const noexcept { return _result; } + }; +} + +#endif // PKMNLIB_CAPTUREATTEMPTEVENT_HPP diff --git a/src/Battling/EventHooks/PkmnEventKind.hpp b/src/Battling/EventHooks/PkmnEventKind.hpp index 80b4bc0..a85e95a 100644 --- a/src/Battling/EventHooks/PkmnEventKind.hpp +++ b/src/Battling/EventHooks/PkmnEventKind.hpp @@ -2,6 +2,6 @@ #define PKMNLIB_PKMNEVENTKIND_HPP #include -ENUM_WITH_START_VALUE(PkmnEventDataKind, u8, 128, WeatherChange) +ENUM_WITH_START_VALUE(PkmnEventDataKind, u8, 128, WeatherChange, CaptureAttempt) #endif // PKMNLIB_PKMNEVENTKIND_HPP diff --git a/src/Battling/Library/BattleLibrary.hpp b/src/Battling/Library/BattleLibrary.hpp index bfe1820..d4eb207 100644 --- a/src/Battling/Library/BattleLibrary.hpp +++ b/src/Battling/Library/BattleLibrary.hpp @@ -3,6 +3,7 @@ #include #include "../../Library/PokemonLibrary.hpp" +#include "CaptureLibrary.hpp" #include "DamageLibrary.hpp" #include "ExperienceLibrary.hpp" #include "MiscLibrary.hpp" @@ -11,13 +12,16 @@ namespace PkmnLib::Battling { class BattleLibrary final : public CreatureLib::Battling::BattleLibrary { + std::unique_ptr _captureLibrary; + public: BattleLibrary(Library::PokemonLibrary* non_null staticLib, StatCalculator* non_null statCalculator, - DamageLibrary* non_null damageLibrary, - PkmnLib::Battling::ExperienceLibrary* non_null experienceLibrary, - ScriptResolver* non_null scriptResolver, PkmnLib::Battling::MiscLibrary* non_null miscLibrary) + DamageLibrary* non_null damageLibrary, ExperienceLibrary* non_null experienceLibrary, + ScriptResolver* non_null scriptResolver, MiscLibrary* non_null miscLibrary, + CaptureLibrary* non_null captureLibrary) : CreatureLib::Battling::BattleLibrary(staticLib, statCalculator, damageLibrary, experienceLibrary, - scriptResolver, miscLibrary) {} + scriptResolver, miscLibrary), + _captureLibrary(captureLibrary) {} const std::unique_ptr& GetSettings() const { return reinterpret_cast&>(_staticLib->GetSettings()); @@ -43,6 +47,9 @@ namespace PkmnLib::Battling { const ArbUt::BorrowedPtr GetNatureLibrary() const { return GetStaticLib()->GetNatureLibrary(); } + [[nodiscard]] inline const ArbUt::BorrowedPtr GetCaptureLibrary() const noexcept { + return _captureLibrary; + } static PkmnLib::Battling::ScriptResolver* non_null CreateScriptResolver(); }; diff --git a/src/Battling/Library/CaptureLibrary.cpp b/src/Battling/Library/CaptureLibrary.cpp index 5cd13f5..275246f 100644 --- a/src/Battling/Library/CaptureLibrary.cpp +++ b/src/Battling/Library/CaptureLibrary.cpp @@ -1,10 +1,11 @@ #include "CaptureLibrary.hpp" #include "../PkmnItemUseScript.hpp" #include "../PkmnScriptHook.hpp" +#include "../Pokemon/Pokemon.hpp" namespace PkmnLib::Battling { CaptureLibrary::CaptureResult CaptureLibrary::TryCatch(Pokemon* non_null pokemon, Library::Item* non_null catchItem, - ArbUt::Random* non_null random) const { + CreatureLib::Battling::BattleRandom* non_null random) const { auto hpMax = pokemon->GetMaxHealth(); auto hpCurrent = pokemon->GetCurrentHealth(); auto rate = pokemon->GetSpecies()->GetCaptureRate(); diff --git a/src/Battling/Library/CaptureLibrary.hpp b/src/Battling/Library/CaptureLibrary.hpp index b0a08ed..d915575 100644 --- a/src/Battling/Library/CaptureLibrary.hpp +++ b/src/Battling/Library/CaptureLibrary.hpp @@ -1,9 +1,13 @@ #ifndef PKMNLIB_CAPTURELIBRARY_HPP #define PKMNLIB_CAPTURELIBRARY_HPP +#include #include -#include "../Pokemon/Pokemon.hpp" +#include "../../Library/Items/Item.hpp" + namespace PkmnLib::Battling { + class Pokemon; + class CaptureLibrary { public: struct CaptureResult { @@ -13,7 +17,7 @@ namespace PkmnLib::Battling { }; CaptureResult TryCatch(Pokemon* non_null pokemon, Library::Item* non_null catchItem, - ArbUt::Random* non_null random) const; + CreatureLib::Battling::BattleRandom* non_null random) const; }; } diff --git a/src/Battling/Pokemon/Pokemon.cpp b/src/Battling/Pokemon/Pokemon.cpp index 2ebb609..1ceee50 100644 --- a/src/Battling/Pokemon/Pokemon.cpp +++ b/src/Battling/Pokemon/Pokemon.cpp @@ -1,5 +1,6 @@ #include "Pokemon.hpp" #include +#include "../EventHooks/CaptureAttemptEvent.hpp" #include "../PkmnScriptCategory.hpp" void PkmnLib::Battling::Pokemon::Evolve(ArbUt::BorrowedPtr mon, @@ -71,5 +72,30 @@ bool PkmnLib::Battling::Pokemon::IsUsable() const noexcept { if (IsEgg()) { return false; } + if (_wasCaught) { + return false; + } return Creature::IsUsable(); } +void PkmnLib::Battling::Pokemon::AttemptCapture(PkmnLib::Library::Item* catchItem) { + Ensure(_battleData.OnBattleField); + Ensure(_battleData.Battle.HasValue()); + Ensure(_battleData.Side.HasValue()); + Ensure(!IsFainted()); + Ensure(IsUsable()); + Ensure(!GetBattleSide().GetValue()->IsSlotUnfillabe(this)) auto captureLibrary = + GetLibrary().ForceAs()->GetCaptureLibrary(); + auto result = captureLibrary->TryCatch(this, catchItem, _battleData.Battle.GetValue()->GetRandom()); + _battleData.Battle.GetValue()->TriggerEventListener(this, result); + + if (result.WasCaught) { + // By marking the pokemon as caught, it becomes no longer usable for switch in. + _wasCaught = true; + // As the pokemon is caught now, we replace it with an empty space. + _battleData.Side.GetValue()->SetCreature(nullptr, _battleData.Index.GetCreatureIndex()); + } +} +void PkmnLib::Battling::Pokemon::ClearBattleData() noexcept { + Creature::ClearBattleData(); + _wasCaught = false; +} diff --git a/src/Battling/Pokemon/Pokemon.hpp b/src/Battling/Pokemon/Pokemon.hpp index a8a1a66..e48436f 100644 --- a/src/Battling/Pokemon/Pokemon.hpp +++ b/src/Battling/Pokemon/Pokemon.hpp @@ -4,6 +4,7 @@ #include #include "../../Library/Statistic.hpp" #include "../Library/BattleLibrary.hpp" +#include "../Library/CaptureLibrary.hpp" #include "../PkmnScript.hpp" #include "LearnedMove.hpp" @@ -16,6 +17,7 @@ namespace PkmnLib::Battling { ArbUt::BorrowedPtr _nature; u8 _friendship = 0; bool _isEgg; + bool _wasCaught = {}; public: Pokemon(ArbUt::BorrowedPtr library, @@ -81,6 +83,15 @@ namespace PkmnLib::Battling { _friendship = newValue; } + /// @brief Attempt to capture the Pokemon. + /// @param catchItem The item used to try and catch the Pokemon with (generally a pokeball). + /// @warning This requires the Pokemon to be on the battle field, not fainted, and usable (so not already caught + /// and not an egg). + void AttemptCapture(Library::Item* non_null catchItem); + inline bool WasCaught() const noexcept { return _wasCaught; } + + void ClearBattleData() noexcept override; + bool IsUsable() const noexcept override; Creature* non_null Clone() const override; diff --git a/tests/ScriptTests/BaseScriptClassTests.cpp b/tests/ScriptTests/BaseScriptClassTests.cpp index 9e890d6..7436f1d 100644 --- a/tests/ScriptTests/BaseScriptClassTests.cpp +++ b/tests/ScriptTests/BaseScriptClassTests.cpp @@ -6,7 +6,9 @@ #include "../TestLibrary/TestLibrary.hpp" #define AS_CLASS(name, contents) \ - { #name, "namespace Pokemon{ [Pokemon effect=" #name "] shared class " #name " : PkmnScript { " contents "}}" } + { \ +#name, "namespace Pokemon{ [Pokemon effect=" #name "] shared class " #name " : PkmnScript { " contents "}}" \ + } static std::unordered_map _scripts = std::unordered_map{ AS_CLASS(blankScript, ), @@ -387,7 +389,8 @@ TEST_CASE("Add Volatile with return script function") { auto resolver = dynamic_cast(PkmnLib::Battling::BattleLibrary::CreateScriptResolver()); auto mainLib = new PkmnLib::Battling::BattleLibrary( TestLibrary::BuildStaticLibrary(), statCalc, new PkmnLib::Battling::DamageLibrary(), - new PkmnLib::Battling::ExperienceLibrary(), resolver, new PkmnLib::Battling::MiscLibrary(GetTime)); + new PkmnLib::Battling::ExperienceLibrary(), resolver, new PkmnLib::Battling::MiscLibrary(GetTime), + new PkmnLib::Battling::CaptureLibrary()); resolver->Initialize(mainLib); for (auto kv : _scripts) { resolver->CreateScript(kv.first, kv.second); diff --git a/tests/TestLibrary/TestLibrary.hpp b/tests/TestLibrary/TestLibrary.hpp index c031012..214802a 100644 --- a/tests/TestLibrary/TestLibrary.hpp +++ b/tests/TestLibrary/TestLibrary.hpp @@ -27,7 +27,8 @@ public: auto scriptResolver = PkmnLib::Battling::BattleLibrary::CreateScriptResolver(); auto lib = new PkmnLib::Battling::BattleLibrary( BuildStaticLibrary(), statCalc, new PkmnLib::Battling::DamageLibrary(false), - new PkmnLib::Battling::ExperienceLibrary(), scriptResolver, new PkmnLib::Battling::MiscLibrary(GetTime)); + new PkmnLib::Battling::ExperienceLibrary(), scriptResolver, new PkmnLib::Battling::MiscLibrary(GetTime), + new PkmnLib::Battling::CaptureLibrary()); scriptResolver->Initialize(lib); return lib; }