#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, int8_t talent, std::vector 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)) { _activeTalent = _library->LoadScript(ScriptCategory::Talent, GetActiveTalent()); } void Battling::Creature::ChangeLevel(int8_t amount) { this->_level += amount; RecalculateFlatStats(); } const std::string& Battling::Creature::GetNickname() const { if (_nickname.empty()) return _species->GetName(); return _nickname; } const std::string& 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(Core::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(Core::Statistic stat) const { return _flatStats.GetStat(stat); } uint32_t Battling::Creature::GetBoostedStat(Core::Statistic stat) const { return _boostedStats.GetStat(stat); } uint32_t Battling::Creature::GetBaseStat(Core::Statistic stat) const { return _variant->GetStatistic(stat); } int8_t Battling::Creature::GetStatBoost(Core::Statistic stat) const { 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(Core::Statistic stat) { auto s = this->_library->GetStatCalculator()->CalculateFlatStat(this, stat); this->_flatStats.SetStat(stat, s); RecalculateBoostedStat(stat); } void Battling::Creature::RecalculateBoostedStat(Core::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 { return this->_currentHealth <= 0; } void Battling::Creature::OnFaint() { // HOOK: On Faint _library->GetExperienceLibrary()->HandleExperienceGain(this, _seenOpponents); if (!_battle->CanSlotBeFilled(_side->GetSideIndex(), _side->GetCreatureIndex(this))) { _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) { 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 std::string& talent) { _hasOverridenTalent = true; _activeTalent->OnRemove(); delete _activeTalent; _overridenTalentName = talent; _activeTalent = this->_library->LoadScript(ScriptCategory::Talent, talent); } const std::vector& Battling::Creature::GetTypes() const { // HOOK: override types. return this->_variant->GetTypes(); } bool Battling::Creature::HasType(uint8_t type) const { auto t = GetTypes(); return std::find(t.begin(), t.end(), type) != t.end(); } void Battling::Creature::GetActiveScripts(std::vector& scripts) { scripts.emplace_back(&_activeTalent); scripts.emplace_back(&_status); scripts.emplace_back(&_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 { auto species = _displaySpecies; if (species == nullptr) species = _species; return species; } const Library::SpeciesVariant* Battling::Creature::GetDisplayVariant() const { auto variant = _displayVariant; if (variant == nullptr) variant = _variant; return variant; } void Battling::Creature::SetHeldItem(const std::string& itemName) { const Library::Item* item; if (!_library->GetItemLibrary()->TryGet(itemName, item)) { throw CreatureException("Item not found."); } _heldItem = item; } void Battling::Creature::AddVolatileScript(const std::string& 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); }