CreatureLib/src/Battling/Models/Creature.cpp

235 lines
8.5 KiB
C++
Raw Normal View History

#include "Creature.hpp"
2019-11-05 13:31:54 +00:00
#include <algorithm>
#include <utility>
#include "../Models/Battle.hpp"
#include "../ScriptHandling/ScriptMacros.hpp"
using namespace CreatureLib;
2020-06-02 14:20:47 +00:00
Battling::Creature::Creature(ArbUt::BorrowedPtr<const BattleLibrary> library,
2020-05-26 16:31:06 +00:00
const ArbUt::BorrowedPtr<const Library::CreatureSpecies>& species,
const ArbUt::BorrowedPtr<const Library::SpeciesVariant>& variant, uint8_t level,
2020-05-26 12:35:18 +00:00
uint32_t experience, uint32_t uid, Library::Gender gender, uint8_t coloring,
2020-05-26 16:31:06 +00:00
const ArbUt::BorrowedPtr<const Library::Item> heldItem, std::string nickname,
const Library::TalentIndex& talent, const std::vector<LearnedAttack*>& attacks,
2020-05-26 12:35:18 +00:00
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) {
2020-03-22 12:42:26 +00:00
AssertNotNull(library)
AssertNotNull(species)
AssertNotNull(variant)
_activeTalent = std::unique_ptr<Script>(_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(ArbUt::BorrowedPtr<Battle> battle, ArbUt::BorrowedPtr<BattleSide> side) {
_battle = battle;
_side = side;
this->ResetActiveScripts();
}
// region Stat APIs
bool Battling::Creature::ChangeStatBoost(Library::Statistic stat, int8_t diffAmount) {
bool changed = false;
if (diffAmount > 0)
changed = this->_statBoost.IncreaseStatBy(stat, diffAmount);
else
changed = this->_statBoost.DecreaseStatBy(stat, -diffAmount);
this->RecalculateBoostedStat(stat);
return changed;
}
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
2019-11-02 12:57:43 +00:00
2020-06-02 11:06:24 +00:00
const ArbUt::BorrowedPtr<CreatureLib::Battling::Battle>& Battling::Creature::GetBattle() const { return _battle; }
2020-06-02 14:20:47 +00:00
const ArbUt::BorrowedPtr<CreatureLib::Battling::BattleSide>& Battling::Creature::GetBattleSide() const { return _side; }
2020-04-22 19:20:07 +00:00
bool Battling::Creature::IsFainted() const noexcept { return this->_currentHealth == 0; }
2019-11-03 12:47:50 +00:00
void Battling::Creature::OnFaint() {
// HOOK: On Faint
if (_battle != nullptr) {
_battle->TriggerEventListener(new FaintEvent(this));
}
_library->GetExperienceLibrary()->HandleExperienceGain(this, _seenOpponents);
2020-04-04 15:03:06 +00:00
auto sideIndex = _side->GetCreatureIndex(this);
if (!_battle->CanSlotBeFilled(_side->GetSideIndex(), sideIndex)) {
_side->MarkSlotAsUnfillable(this);
}
_battle->ValidateBattleState();
}
2019-11-03 12:47:50 +00:00
void Battling::Creature::Damage(uint32_t damage, Battling::DamageSource source) {
if (damage > _currentHealth) {
damage = _currentHealth;
2019-11-03 12:47:50 +00:00
}
2020-04-22 19:20:07 +00:00
if (damage == 0)
return;
2019-11-03 12:47:50 +00:00
// HOOK: On Damage
auto newHealth = _currentHealth - damage;
auto battle = this->GetBattle();
if (battle != nullptr) {
battle->TriggerEventListener(new DamageEvent(this, source, _currentHealth, newHealth));
}
_currentHealth = newHealth;
2019-12-07 21:52:43 +00:00
if (IsFainted() && damage > 0) {
OnFaint();
}
2019-11-03 12:47:50 +00:00
}
2019-11-05 07:06:12 +00:00
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();
_overridenTalentName = talent;
_activeTalent.reset(this->_library->LoadScript(ScriptCategory::Talent, talent));
}
2020-05-26 16:31:06 +00:00
const ArbUt::List<uint8_t>& Battling::Creature::GetTypes() const noexcept {
// HOOK: override types.
return this->_variant->GetTypes();
2019-11-05 07:06:12 +00:00
}
2019-11-05 13:31:54 +00:00
2020-03-22 12:42:26 +00:00
bool Battling::Creature::HasType(uint8_t type) const noexcept {
2019-11-05 13:31:54 +00:00
auto t = GetTypes();
return std::find(t.begin(), t.end(), type) != t.end();
}
size_t Battling::Creature::ScriptCount() const {
auto c = 3;
if (_side != nullptr) {
c += _side->ScriptCount();
}
return c;
}
2020-05-26 16:31:06 +00:00
void Battling::Creature::GetActiveScripts(ArbUt::List<ScriptWrapper>& scripts) {
2020-04-23 21:23:58 +00:00
scripts.Append(ScriptWrapper::FromScript(&_activeTalent));
scripts.Append(ScriptWrapper::FromScript(&_status));
scripts.Append(ScriptWrapper::FromSet(&_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;
}
2020-05-26 16:31:06 +00:00
ArbUt::BorrowedPtr<const Library::CreatureSpecies> Battling::Creature::GetDisplaySpecies() const noexcept {
auto species = _displaySpecies;
if (species == nullptr)
species = _species;
return species;
}
2020-05-26 16:31:06 +00:00
ArbUt::BorrowedPtr<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) {
2020-05-26 16:31:06 +00:00
ArbUt::BorrowedPtr<const Library::Item> item;
2020-05-24 18:57:22 +00:00
if (!_library->GetItemLibrary()->TryGet(itemName.GetHash(), item)) {
throw CreatureException("Item not found.");
}
_heldItem = item;
}
void Battling::Creature::SetHeldItem(uint32_t itemNameHash) {
2020-05-26 16:31:06 +00:00
ArbUt::BorrowedPtr<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);
if (script == nullptr) {
std::stringstream ss;
ss << "Invalid volatile script requested for creature: '" << name.c_str() << "'.";
throw CreatureException(ss.str());
}
2020-06-02 13:03:31 +00:00
_volatile.Add(script.GetRaw());
}
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); }