2020-01-02 17:02:40 +00:00
|
|
|
#include "Pokemon.hpp"
|
2020-06-10 10:33:51 +00:00
|
|
|
#include <CreatureLib/Battling/Models/Battle.hpp>
|
2022-03-26 15:23:24 +00:00
|
|
|
#include "../EventHooks/CaptureAttemptEvent.hpp"
|
2020-08-08 10:01:05 +00:00
|
|
|
#include "../PkmnScriptCategory.hpp"
|
2020-06-10 10:33:51 +00:00
|
|
|
|
|
|
|
void PkmnLib::Battling::Pokemon::Evolve(ArbUt::BorrowedPtr<const Library::PokemonSpecies> mon,
|
|
|
|
ArbUt::BorrowedPtr<const Library::PokemonForme> forme) {
|
2020-06-10 12:53:03 +00:00
|
|
|
// Set species and variant
|
2020-06-10 10:33:51 +00:00
|
|
|
_species = mon.ForceAs<const CreatureLib::Library::CreatureSpecies>();
|
2020-06-10 12:53:03 +00:00
|
|
|
ChangeVariant(forme.ForceAs<const CreatureLib::Library::SpeciesVariant>());
|
2020-06-10 10:33:51 +00:00
|
|
|
|
2020-06-10 12:53:03 +00:00
|
|
|
// If the pokemon is genderless, but it's new evolution is not, we want to set its gender
|
2020-06-10 10:33:51 +00:00
|
|
|
if (_gender != CreatureLib::Library::Gender::Genderless && _species->GetGenderRate() != -1) {
|
2020-06-10 12:53:03 +00:00
|
|
|
// If we are currently in battle, use the battle random so we can get predictable events.
|
2021-05-08 10:12:36 +00:00
|
|
|
if (_battleData.Battle.HasValue()) {
|
|
|
|
_gender = _species->GetRandomGender(_battleData.Battle.GetValue()->GetRandom()->GetRNG());
|
2020-06-10 12:53:03 +00:00
|
|
|
}
|
|
|
|
// Else create a new random.
|
|
|
|
else {
|
|
|
|
ArbUt::Random rand;
|
|
|
|
_gender = _species->GetRandomGender(rand);
|
|
|
|
}
|
|
|
|
// Else if the new pokemon species is genderless, but the pokemon has a gender, make the Pokemon genderless.
|
2020-06-10 10:33:51 +00:00
|
|
|
} else if (_species->GetGenderRate() == -1 && _gender != CreatureLib::Library::Gender::Genderless) {
|
|
|
|
_gender = CreatureLib::Library::Gender::Genderless;
|
|
|
|
}
|
|
|
|
// TODO: Learn moves?
|
2020-06-10 12:53:03 +00:00
|
|
|
// TODO: Event hook
|
2020-06-10 10:33:51 +00:00
|
|
|
}
|
2020-08-08 10:01:05 +00:00
|
|
|
|
2021-04-11 14:27:21 +00:00
|
|
|
CreatureLib::Battling::Creature* PkmnLib::Battling::Pokemon::Clone() const {
|
|
|
|
auto moves = std::vector<CreatureLib::Battling::LearnedAttack*>(_attacks.Count());
|
|
|
|
auto i = 0;
|
|
|
|
for (auto* attack : _attacks) {
|
|
|
|
if (attack == nullptr) {
|
|
|
|
moves[i++] = nullptr;
|
|
|
|
} else {
|
|
|
|
moves[i++] = dynamic_cast<LearnedMove*>(attack->Clone());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
auto* c = new Pokemon(_library.ForceAs<const BattleLibrary>(), _species.ForceAs<const Library::PokemonSpecies>(),
|
|
|
|
_variant.ForceAs<const Library::PokemonForme>(), _level, _experience, _uniqueIdentifier,
|
|
|
|
_gender, _coloring, _heldItem.ForceAs<const Library::Item>(), _nickname, _talentIndex, moves,
|
|
|
|
_individualValues, _effortValues, _nature, _allowedExperienceGain);
|
|
|
|
c->_displaySpecies = _displaySpecies;
|
|
|
|
c->_displayVariant = _displayVariant;
|
|
|
|
c->_currentHealth = _currentHealth;
|
|
|
|
c->_statBoost = _statBoost;
|
|
|
|
c->_flatStats = _flatStats;
|
|
|
|
c->_boostedStats = _boostedStats;
|
2021-05-08 10:12:36 +00:00
|
|
|
c->_battleData.Battle = _battleData.Battle;
|
|
|
|
c->_battleData.Side = _battleData.Side;
|
|
|
|
c->_battleData.OnBattleField = _battleData.OnBattleField;
|
|
|
|
c->_battleData.Index = _battleData.Index;
|
2022-05-14 14:50:20 +00:00
|
|
|
if (_activeTalent.HasValue()) {
|
|
|
|
c->_activeTalent = _activeTalent.GetValue()->Clone(c);
|
2021-04-11 14:27:21 +00:00
|
|
|
}
|
|
|
|
c->_hasOverridenTalent = _hasOverridenTalent;
|
2021-11-15 11:47:02 +00:00
|
|
|
c->_overridenTalent = _overridenTalent;
|
2022-05-14 14:50:20 +00:00
|
|
|
if (_status.HasValue()) {
|
|
|
|
c->_status = _status.GetValue()->Clone(c);
|
2021-04-11 14:27:21 +00:00
|
|
|
}
|
2021-10-29 21:54:44 +00:00
|
|
|
_volatile.Clone(c, c->_volatile);
|
2021-04-11 14:27:21 +00:00
|
|
|
c->_types = std::vector<u8>(_types);
|
|
|
|
c->_friendship = _friendship;
|
|
|
|
c->RecalculateFlatStats();
|
|
|
|
|
|
|
|
return c;
|
|
|
|
}
|
2021-06-19 10:44:05 +00:00
|
|
|
bool PkmnLib::Battling::Pokemon::IsUsable() const noexcept {
|
|
|
|
if (IsEgg()) {
|
|
|
|
return false;
|
|
|
|
}
|
2022-03-26 15:23:24 +00:00
|
|
|
if (_wasCaught) {
|
|
|
|
return false;
|
|
|
|
}
|
2021-06-19 10:44:05 +00:00
|
|
|
return Creature::IsUsable();
|
|
|
|
}
|
2022-03-26 15:23:24 +00:00
|
|
|
void PkmnLib::Battling::Pokemon::AttemptCapture(PkmnLib::Library::Item* catchItem) {
|
|
|
|
Ensure(_battleData.OnBattleField);
|
|
|
|
Ensure(_battleData.Battle.HasValue());
|
|
|
|
Ensure(_battleData.Side.HasValue());
|
|
|
|
Ensure(!IsFainted());
|
|
|
|
Ensure(IsUsable());
|
2022-05-15 08:08:15 +00:00
|
|
|
auto captureLibrary =
|
2022-03-26 15:23:24 +00:00
|
|
|
GetLibrary().ForceAs<const BattleLibrary>()->GetCaptureLibrary();
|
|
|
|
auto result = captureLibrary->TryCatch(this, catchItem, _battleData.Battle.GetValue()->GetRandom());
|
|
|
|
_battleData.Battle.GetValue()->TriggerEventListener<CaptureAttemptEvent>(this, result);
|
|
|
|
|
|
|
|
if (result.WasCaught) {
|
|
|
|
// By marking the pokemon as caught, it becomes no longer usable for switch in.
|
|
|
|
_wasCaught = true;
|
2022-05-15 09:31:39 +00:00
|
|
|
|
|
|
|
if (_battleData.Battle.HasValue() && _battleData.Side.HasValue()) {
|
|
|
|
auto sideIndex = _battleData.Side.GetValue()->GetCreatureIndex(this);
|
|
|
|
if (!_battleData.Battle.GetValue()->CanSlotBeFilled(_battleData.Side.GetValue()->GetSideIndex(),
|
|
|
|
sideIndex)) {
|
|
|
|
_battleData.Side.GetValue()->MarkSlotAsUnfillable(this);
|
|
|
|
}
|
|
|
|
_battleData.Battle.GetValue()->ValidateBattleState();
|
|
|
|
}
|
2022-03-26 15:23:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
void PkmnLib::Battling::Pokemon::ClearBattleData() noexcept {
|
|
|
|
Creature::ClearBattleData();
|
|
|
|
_wasCaught = false;
|
|
|
|
}
|