2019-11-28 11:55:22 +00:00
|
|
|
#include "Creature.hpp"
|
2021-04-11 13:20:50 +00:00
|
|
|
#include <utility>
|
2020-08-14 11:52:22 +00:00
|
|
|
#include "../EventHooks/EventDataClasses.hpp"
|
2019-10-24 11:37:55 +00:00
|
|
|
#include "../Models/Battle.hpp"
|
2020-02-19 14:32:43 +00:00
|
|
|
#include "../ScriptHandling/ScriptMacros.hpp"
|
2019-10-24 11:37:55 +00:00
|
|
|
|
|
|
|
using namespace CreatureLib;
|
|
|
|
|
2020-08-13 08:38:56 +00:00
|
|
|
namespace CreatureLib::Battling {
|
2021-04-11 13:20:50 +00:00
|
|
|
Creature::Creature(const ArbUt::BorrowedPtr<const BattleLibrary>& library,
|
2020-08-13 08:38:56 +00:00
|
|
|
const ArbUt::BorrowedPtr<const Library::CreatureSpecies>& species,
|
2020-08-19 18:11:00 +00:00
|
|
|
const ArbUt::BorrowedPtr<const Library::SpeciesVariant>& variant, level_int_t level,
|
2020-08-13 08:38:56 +00:00
|
|
|
uint32_t experience, uint32_t uid, Library::Gender gender, uint8_t coloring,
|
2021-04-11 13:20:50 +00:00
|
|
|
ArbUt::OptionalBorrowedPtr<const Library::Item> heldItem, std::string nickname,
|
2020-08-13 08:38:56 +00:00
|
|
|
const Library::TalentIndex& talent, const std::vector<LearnedAttack*>& attacks,
|
|
|
|
bool allowedExperienceGain)
|
|
|
|
: _library(library), _species(species), _variant(variant), _level(level), _experience(experience),
|
|
|
|
_uniqueIdentifier(uid), _gender(gender), _coloring(coloring), _heldItem(heldItem),
|
|
|
|
_nickname(std::move(nickname)), _talentIndex(talent), _hasOverridenTalent(false), _attacks(attacks),
|
|
|
|
_allowedExperienceGain(allowedExperienceGain) {
|
2021-03-07 09:26:41 +00:00
|
|
|
_activeTalent = std::unique_ptr<BattleScript>(_library->LoadScript(ScriptCategory::Talent, GetActiveTalent()));
|
2020-08-13 08:38:56 +00:00
|
|
|
for (auto t : _variant->GetTypes()) {
|
2020-10-23 14:51:15 +00:00
|
|
|
_types.push_back(t);
|
2020-08-13 08:38:56 +00:00
|
|
|
}
|
2020-02-27 17:23:23 +00:00
|
|
|
}
|
2020-08-13 08:38:56 +00:00
|
|
|
|
|
|
|
void Creature::ChangeSpecies(const ArbUt::BorrowedPtr<const Library::CreatureSpecies>& species,
|
|
|
|
const ArbUt::BorrowedPtr<const Library::SpeciesVariant>& variant) {
|
|
|
|
_species = species;
|
2020-08-13 10:23:49 +00:00
|
|
|
|
|
|
|
// If the creature is genderless, but it's new species is not, we want to set its gender
|
|
|
|
if (_gender != CreatureLib::Library::Gender::Genderless && _species->GetGenderRate() != -1) {
|
|
|
|
// If we are currently in battle, use the battle random so we can get predictable events.
|
2020-12-12 11:22:48 +00:00
|
|
|
if (_battle.HasValue()) {
|
|
|
|
_gender = _species->GetRandomGender(_battle.GetValue()->GetRandom()->GetRNG());
|
2020-08-13 10:23:49 +00:00
|
|
|
}
|
|
|
|
// Else create a new random.
|
|
|
|
else {
|
|
|
|
ArbUt::Random rand;
|
|
|
|
_gender = _species->GetRandomGender(rand);
|
|
|
|
}
|
|
|
|
} // Else if the new species is genderless, but the creature has a gender, make the creature genderless.
|
|
|
|
else if (_species->GetGenderRate() == -1 && _gender != CreatureLib::Library::Gender::Genderless) {
|
|
|
|
_gender = CreatureLib::Library::Gender::Genderless;
|
|
|
|
}
|
2020-12-12 11:22:48 +00:00
|
|
|
if (_battle.HasValue()) {
|
|
|
|
_battle.GetValue()->TriggerEventListener<ChangeSpeciesEvent>(this, _species);
|
2020-08-14 11:52:22 +00:00
|
|
|
}
|
|
|
|
ChangeVariant(variant);
|
2020-06-05 14:38:27 +00:00
|
|
|
}
|
2019-10-24 11:37:55 +00:00
|
|
|
|
2020-08-13 08:38:56 +00:00
|
|
|
void Creature::ChangeVariant(const ArbUt::BorrowedPtr<const Library::SpeciesVariant>& variant) {
|
|
|
|
_variant = variant;
|
|
|
|
|
|
|
|
// Set the types to the new variant.
|
|
|
|
_types.clear();
|
|
|
|
for (auto t : variant->GetTypes()) {
|
2020-10-23 14:51:15 +00:00
|
|
|
_types.push_back(t);
|
2020-08-13 08:38:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Grab the new active talent.
|
2021-03-07 09:26:41 +00:00
|
|
|
_activeTalent = std::unique_ptr<BattleScript>(_library->LoadScript(ScriptCategory::Talent, GetActiveTalent()));
|
2020-08-13 08:38:56 +00:00
|
|
|
|
|
|
|
// We modify the health of the creature by the change in its max health.
|
|
|
|
auto prevHealth = GetBoostedStat(CreatureLib::Library::Statistic::Health);
|
|
|
|
RecalculateFlatStats();
|
|
|
|
int32_t diffHealth = GetBoostedStat(CreatureLib::Library::Statistic::Health) - prevHealth;
|
|
|
|
if (_currentHealth < static_cast<uint32_t>(INT32_MAX) && static_cast<int32_t>(_currentHealth) < -diffHealth) {
|
|
|
|
_currentHealth = 0;
|
|
|
|
} else {
|
|
|
|
_currentHealth += diffHealth;
|
|
|
|
}
|
2020-06-10 12:39:20 +00:00
|
|
|
|
2020-08-13 08:38:56 +00:00
|
|
|
// TODO: consider variant specific attacks?
|
2020-08-14 11:52:22 +00:00
|
|
|
|
2020-12-12 11:22:48 +00:00
|
|
|
if (_battle.HasValue()) {
|
|
|
|
_battle.GetValue()->TriggerEventListener<ChangeVariantEvent>(this, _variant);
|
2020-08-14 11:52:22 +00:00
|
|
|
}
|
2020-06-10 12:39:20 +00:00
|
|
|
}
|
|
|
|
|
2020-08-13 08:38:56 +00:00
|
|
|
void Creature::ChangeLevelBy(int8_t amount) {
|
|
|
|
auto level = _level + amount;
|
|
|
|
if (level > _library->GetSettings()->GetMaximalLevel())
|
|
|
|
level = _library->GetSettings()->GetMaximalLevel();
|
|
|
|
if (level < 1)
|
|
|
|
level = 1;
|
|
|
|
_level = level;
|
|
|
|
_experience = _library->GetGrowthRateLibrary()->CalculateExperience(_species->GetGrowthRate(), _level);
|
|
|
|
RecalculateFlatStats();
|
2020-06-10 12:39:20 +00:00
|
|
|
}
|
|
|
|
|
2020-08-13 08:38:56 +00:00
|
|
|
const ArbUt::StringView& Creature::GetActiveTalent() const {
|
|
|
|
if (_hasOverridenTalent) {
|
|
|
|
return _overridenTalentName;
|
|
|
|
}
|
|
|
|
return _variant->GetTalent(_talentIndex);
|
2019-11-18 17:41:55 +00:00
|
|
|
}
|
2020-08-13 08:38:56 +00:00
|
|
|
|
2020-12-12 11:22:48 +00:00
|
|
|
void Creature::SetBattleData(const ArbUt::BorrowedPtr<Battle>& battle, const ArbUt::BorrowedPtr<BattleSide>& side) {
|
|
|
|
_battle = battle.GetRaw();
|
|
|
|
_side = side.GetRaw();
|
2020-08-13 08:38:56 +00:00
|
|
|
this->ResetActiveScripts();
|
2020-03-09 15:29:41 +00:00
|
|
|
}
|
2020-12-28 12:40:39 +00:00
|
|
|
void Creature::ClearBattleData() noexcept {
|
|
|
|
_battle = nullptr;
|
|
|
|
_side = nullptr;
|
2021-01-15 14:46:48 +00:00
|
|
|
_seenOpponents = {};
|
2021-01-15 15:31:47 +00:00
|
|
|
ResetActiveScripts();
|
2020-12-28 12:40:39 +00:00
|
|
|
}
|
2020-08-13 08:38:56 +00:00
|
|
|
|
|
|
|
bool Creature::ChangeStatBoost(Library::Statistic stat, int8_t diffAmount) {
|
|
|
|
bool changed = false;
|
2021-01-16 20:16:47 +00:00
|
|
|
auto oldValue = this->_statBoost.GetStat(stat);
|
2021-04-11 13:20:50 +00:00
|
|
|
if (diffAmount > 0) {
|
2020-08-13 08:38:56 +00:00
|
|
|
changed = this->_statBoost.IncreaseStatBy(stat, diffAmount);
|
2021-04-11 13:20:50 +00:00
|
|
|
} else if (diffAmount < 0) {
|
2020-08-13 08:38:56 +00:00
|
|
|
changed = this->_statBoost.DecreaseStatBy(stat, -diffAmount);
|
2021-04-11 13:20:50 +00:00
|
|
|
}
|
2021-01-16 20:16:47 +00:00
|
|
|
if (this->GetBattle().HasValue()) {
|
|
|
|
auto newValue = this->_statBoost.GetStat(stat);
|
|
|
|
this->GetBattle().GetValue()->TriggerEventListener<ChangeStatBoostEvent>(this, stat, oldValue, newValue);
|
|
|
|
}
|
2020-08-13 08:38:56 +00:00
|
|
|
this->RecalculateBoostedStat(stat);
|
|
|
|
return changed;
|
2019-12-14 12:28:23 +00:00
|
|
|
}
|
|
|
|
|
2020-08-13 08:38:56 +00:00
|
|
|
void Creature::RecalculateFlatStats() {
|
2021-04-11 13:20:50 +00:00
|
|
|
const auto& statCalc = this->_library->GetStatCalculator();
|
2020-08-13 08:38:56 +00:00
|
|
|
this->_flatStats = statCalc->CalculateFlatStats(this);
|
|
|
|
RecalculateBoostedStats();
|
2019-11-03 12:47:50 +00:00
|
|
|
}
|
2020-08-13 08:38:56 +00:00
|
|
|
void Creature::RecalculateBoostedStats() {
|
|
|
|
this->_boostedStats = this->_library->GetStatCalculator()->CalculateFlatStats(this);
|
2020-02-02 11:34:02 +00:00
|
|
|
}
|
2019-12-07 20:56:29 +00:00
|
|
|
|
2020-08-13 08:38:56 +00:00
|
|
|
void Creature::RecalculateFlatStat(Library::Statistic stat) {
|
|
|
|
auto s = this->_library->GetStatCalculator()->CalculateFlatStat(this, stat);
|
|
|
|
this->_flatStats.SetStat(stat, s);
|
|
|
|
RecalculateBoostedStat(stat);
|
2019-12-07 20:56:29 +00:00
|
|
|
}
|
2019-11-05 07:06:12 +00:00
|
|
|
|
2020-08-13 08:38:56 +00:00
|
|
|
void Creature::RecalculateBoostedStat(Library::Statistic stat) {
|
|
|
|
auto s = this->_library->GetStatCalculator()->CalculateBoostedStat(this, stat);
|
|
|
|
this->_boostedStats.SetStat(stat, s);
|
2020-03-09 15:29:41 +00:00
|
|
|
}
|
2020-08-13 08:38:56 +00:00
|
|
|
|
|
|
|
bool Creature::IsFainted() const noexcept { return this->_currentHealth == 0; }
|
|
|
|
|
|
|
|
void Creature::OnFaint() {
|
2020-12-13 11:15:40 +00:00
|
|
|
EnsureNotNull(_battle)
|
|
|
|
EnsureNotNull(_side)
|
2020-08-13 08:38:56 +00:00
|
|
|
// HOOK: On Faint
|
2020-12-12 11:22:48 +00:00
|
|
|
if (_battle.HasValue()) {
|
|
|
|
_battle.GetValue()->TriggerEventListener<FaintEvent>(this);
|
2020-08-13 08:38:56 +00:00
|
|
|
}
|
|
|
|
_library->GetExperienceLibrary()->HandleExperienceGain(this, _seenOpponents);
|
2020-12-12 11:22:48 +00:00
|
|
|
if (_battle.HasValue() && _side.HasValue()) {
|
|
|
|
auto sideIndex = _side.GetValue()->GetCreatureIndex(this);
|
|
|
|
if (!_battle.GetValue()->CanSlotBeFilled(_side.GetValue()->GetSideIndex(), sideIndex)) {
|
|
|
|
_side.GetValue()->MarkSlotAsUnfillable(this);
|
|
|
|
}
|
|
|
|
_battle.GetValue()->ValidateBattleState();
|
2020-08-13 08:38:56 +00:00
|
|
|
}
|
2020-02-13 13:48:09 +00:00
|
|
|
}
|
2020-08-13 08:38:56 +00:00
|
|
|
|
|
|
|
void Creature::Damage(uint32_t damage, DamageSource source) {
|
|
|
|
if (damage > _currentHealth) {
|
|
|
|
damage = _currentHealth;
|
|
|
|
}
|
|
|
|
if (damage == 0)
|
|
|
|
return;
|
|
|
|
// HOOK: On Damage
|
|
|
|
auto newHealth = _currentHealth - damage;
|
|
|
|
auto battle = this->GetBattle();
|
2020-12-12 11:22:48 +00:00
|
|
|
if (battle.HasValue()) {
|
|
|
|
battle.GetValue()->TriggerEventListener<DamageEvent>(this, source, _currentHealth, newHealth);
|
2020-08-13 08:38:56 +00:00
|
|
|
}
|
|
|
|
_currentHealth = newHealth;
|
|
|
|
|
|
|
|
if (IsFainted() && damage > 0 && battle != nullptr) {
|
|
|
|
OnFaint();
|
|
|
|
}
|
2020-02-13 13:48:09 +00:00
|
|
|
}
|
|
|
|
|
2020-08-13 08:38:56 +00:00
|
|
|
void Creature::Heal(uint32_t amount, bool canRevive) {
|
|
|
|
if (_currentHealth == 0 && !canRevive) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (amount > GetMaxHealth() - _currentHealth) {
|
|
|
|
amount = GetMaxHealth() - _currentHealth;
|
|
|
|
}
|
|
|
|
// HOOK: On Heal
|
|
|
|
auto newHealth = _currentHealth + amount;
|
|
|
|
auto battle = this->GetBattle();
|
2020-12-12 11:22:48 +00:00
|
|
|
if (battle.HasValue()) {
|
|
|
|
battle.GetValue()->TriggerEventListener<HealEvent>(this, _currentHealth, newHealth);
|
2020-08-08 09:32:25 +00:00
|
|
|
}
|
2020-08-13 08:38:56 +00:00
|
|
|
_currentHealth = newHealth;
|
2020-08-08 09:32:25 +00:00
|
|
|
}
|
|
|
|
|
2020-08-13 08:38:56 +00:00
|
|
|
void Creature::RestoreAllAttackUses() noexcept {
|
|
|
|
for (auto& a : _attacks) {
|
|
|
|
if (a != nullptr) {
|
|
|
|
a->RestoreAllUses();
|
|
|
|
}
|
|
|
|
}
|
2020-08-01 08:27:46 +00:00
|
|
|
}
|
2019-11-18 17:41:55 +00:00
|
|
|
|
2020-08-13 08:38:56 +00:00
|
|
|
void Creature::OverrideActiveTalent(const ArbUt::StringView& talent) {
|
|
|
|
_hasOverridenTalent = true;
|
|
|
|
if (_activeTalent != nullptr) {
|
|
|
|
_activeTalent->OnRemove();
|
|
|
|
_activeTalent.reset(this->_library->LoadScript(ScriptCategory::Talent, talent));
|
|
|
|
}
|
|
|
|
_overridenTalentName = talent;
|
|
|
|
}
|
2019-11-05 13:31:54 +00:00
|
|
|
|
2020-10-23 14:51:15 +00:00
|
|
|
const std::vector<uint8_t>& Creature::GetTypes() const noexcept { return _types; }
|
2019-11-10 13:32:05 +00:00
|
|
|
|
2020-08-13 08:38:56 +00:00
|
|
|
bool Creature::HasType(uint8_t type) const noexcept {
|
|
|
|
return std::find(_types.begin(), _types.end(), type) != _types.end();
|
2020-02-17 08:57:15 +00:00
|
|
|
}
|
2020-08-13 08:38:56 +00:00
|
|
|
|
|
|
|
size_t Creature::ScriptCount() const {
|
|
|
|
auto c = 3;
|
2020-12-12 11:22:48 +00:00
|
|
|
if (_side.HasValue()) {
|
|
|
|
c += _side.GetValue()->ScriptCount();
|
2020-08-13 08:38:56 +00:00
|
|
|
}
|
|
|
|
return c;
|
2019-12-14 12:28:23 +00:00
|
|
|
}
|
2020-08-13 08:38:56 +00:00
|
|
|
|
|
|
|
void Creature::GetActiveScripts(ArbUt::List<ScriptWrapper>& scripts) {
|
|
|
|
scripts.Append(ScriptWrapper::FromScript(&_activeTalent));
|
|
|
|
scripts.Append(ScriptWrapper::FromScript(&_status));
|
|
|
|
scripts.Append(ScriptWrapper::FromSet(&_volatile));
|
2020-12-12 11:22:48 +00:00
|
|
|
if (_side.HasValue()) {
|
|
|
|
_side.GetValue()->GetActiveScripts(scripts);
|
2020-08-13 08:38:56 +00:00
|
|
|
}
|
2019-12-14 12:28:23 +00:00
|
|
|
}
|
2020-08-13 08:38:56 +00:00
|
|
|
void Creature::ClearVolatileScripts() { _volatile.Clear(); }
|
|
|
|
void Creature::AddExperience(uint32_t amount) {
|
|
|
|
auto maxLevel = _library->GetSettings()->GetMaximalLevel();
|
|
|
|
if (_level >= maxLevel) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
auto exp = _experience + amount;
|
|
|
|
auto level = _library->GetGrowthRateLibrary()->CalculateLevel(this->GetSpecies()->GetGrowthRate(), exp);
|
|
|
|
if (level >= maxLevel) {
|
|
|
|
exp = _library->GetGrowthRateLibrary()->CalculateExperience(this->GetSpecies()->GetGrowthRate(), maxLevel);
|
|
|
|
}
|
2020-12-12 11:22:48 +00:00
|
|
|
if (_battle.HasValue()) {
|
|
|
|
_battle.GetValue()->TriggerEventListener<ExperienceGainEvent>(this, _experience, exp);
|
2020-08-13 08:38:56 +00:00
|
|
|
}
|
|
|
|
_experience = exp;
|
|
|
|
_level = level;
|
2020-08-07 09:02:37 +00:00
|
|
|
}
|
2020-12-12 11:22:48 +00:00
|
|
|
ArbUt::OptionalBorrowedPtr<const Library::CreatureSpecies> Creature::GetDisplaySpecies() const noexcept {
|
2020-08-13 08:38:56 +00:00
|
|
|
auto species = _displaySpecies;
|
2020-12-12 11:22:48 +00:00
|
|
|
if (!species.HasValue())
|
|
|
|
species = _species.GetRaw();
|
2020-08-13 08:38:56 +00:00
|
|
|
return species;
|
2020-02-17 16:16:28 +00:00
|
|
|
}
|
2020-12-12 11:22:48 +00:00
|
|
|
ArbUt::OptionalBorrowedPtr<const Library::SpeciesVariant> Creature::GetDisplayVariant() const noexcept {
|
2020-08-13 08:38:56 +00:00
|
|
|
auto variant = _displayVariant;
|
2020-12-12 11:22:48 +00:00
|
|
|
if (!variant.HasValue())
|
|
|
|
variant = _variant.GetRaw();
|
2020-08-13 08:38:56 +00:00
|
|
|
return variant;
|
2020-03-05 10:25:41 +00:00
|
|
|
}
|
2020-08-13 08:38:56 +00:00
|
|
|
void Creature::SetHeldItem(const ArbUt::BasicStringView& itemName) {
|
2020-12-12 11:42:28 +00:00
|
|
|
auto v = _library->GetItemLibrary()->TryGet(itemName.GetHash());
|
|
|
|
if (!v.has_value()) {
|
2020-08-17 10:18:01 +00:00
|
|
|
THROW("Item not found '" << itemName.c_str() << "'.");
|
2020-08-13 08:38:56 +00:00
|
|
|
}
|
2020-12-12 11:42:28 +00:00
|
|
|
_heldItem = v.value();
|
2020-02-22 15:01:01 +00:00
|
|
|
}
|
2020-08-13 08:38:56 +00:00
|
|
|
void Creature::SetHeldItem(uint32_t itemNameHash) {
|
2020-12-12 11:42:28 +00:00
|
|
|
auto v = _library->GetItemLibrary()->TryGet(itemNameHash);
|
|
|
|
if (!v.has_value()) {
|
2020-08-17 10:18:01 +00:00
|
|
|
THROW("Item not found.");
|
2020-08-13 08:38:56 +00:00
|
|
|
}
|
2020-12-12 11:42:28 +00:00
|
|
|
_heldItem = v.value();
|
2020-04-10 22:22:08 +00:00
|
|
|
}
|
2020-08-13 08:38:56 +00:00
|
|
|
|
2021-03-27 15:36:20 +00:00
|
|
|
BattleScript* Creature::AddVolatileScript(const ArbUt::StringView& name) {
|
2020-08-13 08:38:56 +00:00
|
|
|
auto script = _volatile.Get(name);
|
2021-03-27 19:20:49 +00:00
|
|
|
if (script.HasValue()) {
|
|
|
|
script.GetValue()->Stack();
|
|
|
|
return script.GetValue();
|
2020-08-12 15:51:06 +00:00
|
|
|
}
|
2020-08-13 08:38:56 +00:00
|
|
|
script = this->_library->LoadScript(ScriptCategory::Creature, name);
|
2021-03-27 19:20:49 +00:00
|
|
|
if (!script.HasValue()) {
|
2020-08-17 10:18:01 +00:00
|
|
|
THROW("Invalid volatile script requested for creature: '" << name.c_str() << "'.");
|
2020-08-12 15:51:06 +00:00
|
|
|
}
|
2021-03-27 19:20:49 +00:00
|
|
|
return _volatile.Add(script.GetValue());
|
2020-08-12 15:51:06 +00:00
|
|
|
}
|
2020-08-13 08:38:56 +00:00
|
|
|
|
2021-03-27 15:36:20 +00:00
|
|
|
BattleScript* Creature::AddVolatileScript(BattleScript* script) { return _volatile.Add(script); }
|
2020-08-13 08:38:56 +00:00
|
|
|
void Creature::RemoveVolatileScript(const ArbUt::BasicStringView& name) { _volatile.Remove(name); }
|
2021-03-07 09:26:41 +00:00
|
|
|
void Creature::RemoveVolatileScript(BattleScript* script) { _volatile.Remove(script->GetName()); }
|
2020-08-13 08:38:56 +00:00
|
|
|
bool Creature::HasVolatileScript(const ArbUt::BasicStringView& name) const { return _volatile.Has(name); }
|
|
|
|
void Creature::AddAttack(LearnedAttack* attack) {
|
|
|
|
for (size_t i = 0; i < _attacks.Count(); i++) {
|
|
|
|
if (_attacks[i] == nullptr) {
|
|
|
|
_attacks.Set(i, attack);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2020-08-12 15:51:06 +00:00
|
|
|
if (_attacks.Count() < _library->GetStaticLib()->GetSettings()->GetMaximalAttacks()) {
|
|
|
|
_attacks.Append(attack);
|
|
|
|
}
|
2020-08-17 10:18:01 +00:00
|
|
|
THROW("Can't add attack. The creature already has the maximum amount of attacks.");
|
2020-08-12 15:51:06 +00:00
|
|
|
}
|
2020-08-13 08:38:56 +00:00
|
|
|
uint8_t Creature::GetAvailableAttackSlot() const noexcept {
|
|
|
|
for (uint8_t i = 0; i < (uint8_t)_attacks.Count(); i++) {
|
|
|
|
if (_attacks[i] == nullptr) {
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (_attacks.Count() < _library->GetStaticLib()->GetSettings()->GetMaximalAttacks()) {
|
|
|
|
return _attacks.Count();
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
void Creature::ReplaceAttack(size_t index, LearnedAttack* attack) {
|
|
|
|
if (_attacks.Count() <= index) {
|
|
|
|
if (_attacks.Count() < _library->GetStaticLib()->GetSettings()->GetMaximalAttacks()) {
|
|
|
|
_attacks.Append(attack);
|
|
|
|
}
|
2020-08-17 10:18:01 +00:00
|
|
|
THROW("Can't replace attack at index " << index << ". Number of attacks is " << _attacks.Count() << ".");
|
2020-08-13 08:38:56 +00:00
|
|
|
}
|
|
|
|
_attacks.Set(index, attack);
|
|
|
|
}
|
|
|
|
|
2021-04-11 14:01:18 +00:00
|
|
|
Creature* Creature::Clone() const {
|
2021-04-11 13:20:50 +00:00
|
|
|
auto attacks = std::vector<LearnedAttack*>(_attacks.Count());
|
|
|
|
auto i = 0;
|
|
|
|
for (auto* attack : _attacks) {
|
|
|
|
if (attack == nullptr) {
|
|
|
|
attacks[i++] = nullptr;
|
|
|
|
} else {
|
|
|
|
attacks[i++] = attack->Clone();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
auto* c = new Creature(_library, _species, _variant, _level, _experience, _uniqueIdentifier, _gender, _coloring,
|
|
|
|
_heldItem, _nickname, _talentIndex, attacks, _allowedExperienceGain);
|
|
|
|
c->_displaySpecies = _displaySpecies;
|
|
|
|
c->_displayVariant = _displayVariant;
|
|
|
|
c->_currentHealth = _currentHealth;
|
|
|
|
c->_statBoost = _statBoost;
|
|
|
|
c->_flatStats = _flatStats;
|
|
|
|
c->_boostedStats = _boostedStats;
|
|
|
|
c->_battle = _battle;
|
|
|
|
c->_side = _side;
|
|
|
|
c->_onBattleField = _onBattleField;
|
|
|
|
if (_activeTalent != nullptr) {
|
|
|
|
c->_activeTalent = std::unique_ptr<BattleScript>(_activeTalent->Clone());
|
|
|
|
}
|
|
|
|
c->_hasOverridenTalent = _hasOverridenTalent;
|
|
|
|
c->_overridenTalentName = _overridenTalentName;
|
|
|
|
if (_status != nullptr) {
|
|
|
|
c->_status = std::unique_ptr<BattleScript>(_status->Clone());
|
|
|
|
}
|
|
|
|
_volatile.Clone(c->_volatile);
|
|
|
|
c->_types = std::vector<u8>(_types);
|
|
|
|
c->RecalculateFlatStats();
|
|
|
|
|
|
|
|
return c;
|
|
|
|
}
|
|
|
|
|
2020-08-13 08:38:56 +00:00
|
|
|
}
|