#include "Creature.hpp" #include #include #include "../Models/Battle.hpp" #include "../ScriptHandling/ScriptMacros.hpp" using namespace CreatureLib; Battling::Creature::Creature(const BattleLibrary* library, const Library::CreatureSpecies* species, const Library::SpeciesVariant* variant, uint8_t level, uint32_t experience, uint32_t uid, Library::Gender gender, uint8_t coloring, const Library::Item* heldItem, std::string nickname, const Library::TalentIndex& talent, List attacks) : _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(std::move(attacks)) { AssertNotNull(library) AssertNotNull(species) AssertNotNull(variant) _activeTalent = _library->LoadScript(ScriptCategory::Talent, GetActiveTalent()); if (_nickname.empty()) { _nickname = species->GetName().std_str(); } } void Battling::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(); } const ConstString& Battling::Creature::GetActiveTalent() const { if (_hasOverridenTalent) { return _overridenTalentName; } return _variant->GetTalent(_talentIndex); } void Battling::Creature::SetBattleData(Battling::Battle* battle, Battling::BattleSide* side) { _battle = battle; _side = side; this->ResetActiveScripts(); } // region Stat APIs void Battling::Creature::ChangeStatBoost(Library::Statistic stat, int8_t diffAmount) { if (diffAmount > 0) this->_statBoost.IncreaseStatBy(stat, diffAmount); else this->_statBoost.DecreaseStatBy(stat, -diffAmount); this->RecalculateBoostedStat(stat); } uint32_t Battling::Creature::GetFlatStat(Library::Statistic stat) const noexcept { return _flatStats.GetStat(stat); } uint32_t Battling::Creature::GetBoostedStat(Library::Statistic stat) const noexcept { return _boostedStats.GetStat(stat); } uint32_t Battling::Creature::GetBaseStat(Library::Statistic stat) const noexcept { return _variant->GetStatistic(stat); } int8_t Battling::Creature::GetStatBoost(Library::Statistic stat) const noexcept { return _statBoost.GetStat(stat); } void Battling::Creature::RecalculateFlatStats() { auto statCalc = this->_library->GetStatCalculator(); this->_flatStats = statCalc->CalculateFlatStats(this); RecalculateBoostedStats(); } void Battling::Creature::RecalculateBoostedStats() { this->_boostedStats = this->_library->GetStatCalculator()->CalculateFlatStats(this); } void Battling::Creature::RecalculateFlatStat(Library::Statistic stat) { auto s = this->_library->GetStatCalculator()->CalculateFlatStat(this, stat); this->_flatStats.SetStat(stat, s); RecalculateBoostedStat(stat); } void Battling::Creature::RecalculateBoostedStat(Library::Statistic stat) { auto s = this->_library->GetStatCalculator()->CalculateBoostedStat(this, stat); this->_boostedStats.SetStat(stat, s); } // endregion Battling::Battle* Battling::Creature::GetBattle() const { return _battle; } Battling::BattleSide* Battling::Creature::GetBattleSide() const { return _side; } bool Battling::Creature::IsFainted() const noexcept { return this->_currentHealth <= 0; } void Battling::Creature::OnFaint() { // HOOK: On Faint if (_battle != nullptr) { _battle->TriggerEventListener(new FaintEvent(this)); } _library->GetExperienceLibrary()->HandleExperienceGain(this, _seenOpponents); auto sideIndex = _side->GetCreatureIndex(this); if (!_battle->CanSlotBeFilled(_side->GetSideIndex(), sideIndex)) { _side->MarkSlotAsUnfillable(this); } _battle->ValidateBattleState(); } void Battling::Creature::Damage(uint32_t damage, Battling::DamageSource source) { if (damage > _currentHealth) { damage = _currentHealth; } // HOOK: On Damage auto newHealth = _currentHealth - damage; auto battle = this->GetBattle(); if (battle != nullptr) { battle->TriggerEventListener(new DamageEvent(this, source, _currentHealth, newHealth)); } _currentHealth = newHealth; if (IsFainted() && damage > 0) { OnFaint(); } } void Battling::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(); if (battle != nullptr) { battle->TriggerEventListener(new HealEvent(this, _currentHealth, newHealth)); } _currentHealth = newHealth; } void Battling::Creature::OverrideActiveTalent(const ConstString& talent) { _hasOverridenTalent = true; _activeTalent->OnRemove(); delete _activeTalent; _overridenTalentName = talent; _activeTalent = this->_library->LoadScript(ScriptCategory::Talent, talent); } const List& Battling::Creature::GetTypes() const noexcept { // HOOK: override types. return this->_variant->GetTypes(); } bool Battling::Creature::HasType(uint8_t type) const noexcept { auto t = GetTypes(); return std::find(t.begin(), t.end(), type) != t.end(); } void Battling::Creature::GetActiveScripts(Arbutils::Collections::List& scripts) { scripts.Append(&_activeTalent); scripts.Append(&_status); scripts.Append(&_volatile); if (_side != nullptr) { _side->GetActiveScripts(scripts); } } void Battling::Creature::ClearVolatileScripts() { _volatile.Clear(); } void Battling::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); } _experience = exp; _level = level; } const Library::CreatureSpecies* Battling::Creature::GetDisplaySpecies() const noexcept { auto species = _displaySpecies; if (species == nullptr) species = _species; return species; } const Library::SpeciesVariant* Battling::Creature::GetDisplayVariant() const noexcept { auto variant = _displayVariant; if (variant == nullptr) variant = _variant; return variant; } void Battling::Creature::SetHeldItem(const ConstString& itemName) { const Library::Item* item; if (!_library->GetItemLibrary()->TryGet(itemName, item)) { throw CreatureException("Item not found."); } _heldItem = item; } void Battling::Creature::SetHeldItem(uint32_t itemNameHash) { const Library::Item* item; if (!_library->GetItemLibrary()->TryGet(itemNameHash, item)) { throw CreatureException("Item not found."); } _heldItem = item; } void Battling::Creature::AddVolatileScript(const ConstString& name) { auto script = _volatile.Get(name); if (script != nullptr) { script->Stack(); return; } script = this->_library->LoadScript(ScriptCategory::Creature, name); _volatile.Add(script); } void Battling::Creature::AddVolatileScript(Script* script) { _volatile.Add(script); } void Battling::Creature::RemoveVolatileScript(const ConstString& name) { _volatile.Remove(name); } void Battling::Creature::RemoveVolatileScript(Battling::Script* script) { _volatile.Remove(script->GetName()); } bool Battling::Creature::HasVolatileScript(const ConstString& name) const { return _volatile.Has(name); }