Support changing species for a creature.
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
Signed-off-by: Deukhoofd <Deukhoofd@gmail.com>
This commit is contained in:
parent
51325943ab
commit
bb35248174
|
@ -22,6 +22,10 @@ BORROWED_GET_FUNC(Creature, GetLibrary, const CreatureLib::Battling::BattleLibra
|
||||||
BORROWED_GET_FUNC(Creature, GetSpecies, const CreatureLib::Library::CreatureSpecies*);
|
BORROWED_GET_FUNC(Creature, GetSpecies, const CreatureLib::Library::CreatureSpecies*);
|
||||||
BORROWED_GET_FUNC(Creature, GetVariant, const CreatureLib::Library::SpeciesVariant*);
|
BORROWED_GET_FUNC(Creature, GetVariant, const CreatureLib::Library::SpeciesVariant*);
|
||||||
|
|
||||||
|
export uint8_t CreatureLib_Creature_ChangeSpecies(Creature* p, const CreatureLib::Library::CreatureSpecies* species,
|
||||||
|
const CreatureLib::Library::SpeciesVariant* variant) {
|
||||||
|
Try(p->ChangeSpecies(species, variant);)
|
||||||
|
}
|
||||||
export uint8_t CreatureLib_Creature_ChangeVariant(Creature* p, const CreatureLib::Library::SpeciesVariant* variant) {
|
export uint8_t CreatureLib_Creature_ChangeVariant(Creature* p, const CreatureLib::Library::SpeciesVariant* variant) {
|
||||||
Try(p->ChangeVariant(variant);)
|
Try(p->ChangeVariant(variant);)
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,301 +6,311 @@
|
||||||
|
|
||||||
using namespace CreatureLib;
|
using namespace CreatureLib;
|
||||||
|
|
||||||
Battling::Creature::Creature(ArbUt::BorrowedPtr<const BattleLibrary> library,
|
namespace CreatureLib::Battling {
|
||||||
const ArbUt::BorrowedPtr<const Library::CreatureSpecies>& species,
|
Creature::Creature(ArbUt::BorrowedPtr<const BattleLibrary> library,
|
||||||
const ArbUt::BorrowedPtr<const Library::SpeciesVariant>& variant, uint8_t level,
|
const ArbUt::BorrowedPtr<const Library::CreatureSpecies>& species,
|
||||||
uint32_t experience, uint32_t uid, Library::Gender gender, uint8_t coloring,
|
const ArbUt::BorrowedPtr<const Library::SpeciesVariant>& variant, uint8_t level,
|
||||||
const ArbUt::BorrowedPtr<const Library::Item> heldItem, const std::string& nickname,
|
uint32_t experience, uint32_t uid, Library::Gender gender, uint8_t coloring,
|
||||||
const Library::TalentIndex& talent, const std::vector<LearnedAttack*>& attacks,
|
const ArbUt::BorrowedPtr<const Library::Item> heldItem, const std::string& nickname,
|
||||||
bool allowedExperienceGain)
|
const Library::TalentIndex& talent, const std::vector<LearnedAttack*>& attacks,
|
||||||
: _library(library), _species(species), _variant(variant), _level(level), _experience(experience),
|
bool allowedExperienceGain)
|
||||||
_uniqueIdentifier(uid), _gender(gender), _coloring(coloring), _heldItem(heldItem), _nickname(std::move(nickname)),
|
: _library(library), _species(species), _variant(variant), _level(level), _experience(experience),
|
||||||
_talentIndex(talent), _hasOverridenTalent(false), _attacks(attacks),
|
_uniqueIdentifier(uid), _gender(gender), _coloring(coloring), _heldItem(heldItem),
|
||||||
_allowedExperienceGain(allowedExperienceGain) {
|
_nickname(std::move(nickname)), _talentIndex(talent), _hasOverridenTalent(false), _attacks(attacks),
|
||||||
AssertNotNull(library)
|
_allowedExperienceGain(allowedExperienceGain) {
|
||||||
AssertNotNull(species)
|
AssertNotNull(library)
|
||||||
AssertNotNull(variant)
|
AssertNotNull(species)
|
||||||
|
AssertNotNull(variant)
|
||||||
|
|
||||||
_activeTalent = std::unique_ptr<Script>(_library->LoadScript(ScriptCategory::Talent, GetActiveTalent()));
|
_activeTalent = std::unique_ptr<Script>(_library->LoadScript(ScriptCategory::Talent, GetActiveTalent()));
|
||||||
if (_nickname.empty()) {
|
if (_nickname.empty()) {
|
||||||
_nickname = species->GetName().std_str();
|
_nickname = species->GetName().std_str();
|
||||||
}
|
}
|
||||||
for (auto t : _variant->GetTypes()) {
|
for (auto t : _variant->GetTypes()) {
|
||||||
_types.insert(t);
|
_types.insert(t);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Battling::Creature::ChangeVariant(ArbUt::BorrowedPtr<const Library::SpeciesVariant> variant) {
|
|
||||||
AssertNotNull(variant)
|
|
||||||
_variant = variant;
|
|
||||||
|
|
||||||
// Set the types to the new variant.
|
|
||||||
_types.clear();
|
|
||||||
for (auto t : variant->GetTypes()) {
|
|
||||||
_types.insert(t);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Grab the new active talent.
|
|
||||||
_activeTalent =
|
|
||||||
std::unique_ptr<CreatureLib::Battling::Script>(_library->LoadScript(ScriptCategory::Talent, GetActiveTalent()));
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: consider variant specific attacks?
|
|
||||||
}
|
|
||||||
|
|
||||||
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 ArbUt::StringView& 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
|
|
||||||
|
|
||||||
const ArbUt::BorrowedPtr<CreatureLib::Battling::Battle>& Battling::Creature::GetBattle() const { return _battle; }
|
|
||||||
|
|
||||||
const ArbUt::BorrowedPtr<CreatureLib::Battling::BattleSide>& Battling::Creature::GetBattleSide() const { return _side; }
|
|
||||||
|
|
||||||
bool Battling::Creature::IsFainted() const noexcept { return this->_currentHealth == 0; }
|
|
||||||
|
|
||||||
void Battling::Creature::OnFaint() {
|
|
||||||
AssertNotNull(_battle)
|
|
||||||
AssertNotNull(_side)
|
|
||||||
// HOOK: On Faint
|
|
||||||
if (_battle != nullptr) {
|
|
||||||
_battle->TriggerEventListener<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;
|
|
||||||
}
|
|
||||||
if (damage == 0)
|
|
||||||
return;
|
|
||||||
// HOOK: On Damage
|
|
||||||
auto newHealth = _currentHealth - damage;
|
|
||||||
auto battle = this->GetBattle();
|
|
||||||
if (battle != nullptr) {
|
|
||||||
battle->TriggerEventListener<DamageEvent>(this, source, _currentHealth, newHealth);
|
|
||||||
}
|
|
||||||
_currentHealth = newHealth;
|
|
||||||
|
|
||||||
if (IsFainted() && damage > 0 && battle != nullptr) {
|
|
||||||
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<HealEvent>(this, _currentHealth, newHealth);
|
|
||||||
}
|
|
||||||
_currentHealth = newHealth;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Battling::Creature::RestoreAllAttackUses() noexcept {
|
|
||||||
for (auto& a : _attacks) {
|
|
||||||
if (a != nullptr) {
|
|
||||||
a->RestoreAllUses();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void Battling::Creature::OverrideActiveTalent(const ArbUt::StringView& talent) {
|
void Creature::ChangeSpecies(const ArbUt::BorrowedPtr<const Library::CreatureSpecies>& species,
|
||||||
_hasOverridenTalent = true;
|
const ArbUt::BorrowedPtr<const Library::SpeciesVariant>& variant) {
|
||||||
if (_activeTalent != nullptr) {
|
AssertNotNull(species);
|
||||||
_activeTalent->OnRemove();
|
AssertNotNull(variant);
|
||||||
_activeTalent.reset(this->_library->LoadScript(ScriptCategory::Talent, talent));
|
_species = species;
|
||||||
|
ChangeVariant(variant);
|
||||||
}
|
}
|
||||||
_overridenTalentName = talent;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::unordered_set<uint8_t>& Battling::Creature::GetTypes() const noexcept { return _types; }
|
void Creature::ChangeVariant(const ArbUt::BorrowedPtr<const Library::SpeciesVariant>& variant) {
|
||||||
|
AssertNotNull(variant)
|
||||||
|
_variant = variant;
|
||||||
|
|
||||||
bool Battling::Creature::HasType(uint8_t type) const noexcept {
|
// Set the types to the new variant.
|
||||||
return std::find(_types.begin(), _types.end(), type) != _types.end();
|
_types.clear();
|
||||||
}
|
for (auto t : variant->GetTypes()) {
|
||||||
|
_types.insert(t);
|
||||||
|
}
|
||||||
|
|
||||||
size_t Battling::Creature::ScriptCount() const {
|
// Grab the new active talent.
|
||||||
auto c = 3;
|
_activeTalent = std::unique_ptr<Script>(_library->LoadScript(ScriptCategory::Talent, GetActiveTalent()));
|
||||||
if (_side != nullptr) {
|
|
||||||
c += _side->ScriptCount();
|
|
||||||
}
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Battling::Creature::GetActiveScripts(ArbUt::List<ScriptWrapper>& scripts) {
|
// We modify the health of the creature by the change in its max health.
|
||||||
scripts.Append(ScriptWrapper::FromScript(&_activeTalent));
|
auto prevHealth = GetBoostedStat(CreatureLib::Library::Statistic::Health);
|
||||||
scripts.Append(ScriptWrapper::FromScript(&_status));
|
RecalculateFlatStats();
|
||||||
scripts.Append(ScriptWrapper::FromSet(&_volatile));
|
int32_t diffHealth = GetBoostedStat(CreatureLib::Library::Statistic::Health) - prevHealth;
|
||||||
if (_side != nullptr) {
|
if (_currentHealth < static_cast<uint32_t>(INT32_MAX) && static_cast<int32_t>(_currentHealth) < -diffHealth) {
|
||||||
_side->GetActiveScripts(scripts);
|
_currentHealth = 0;
|
||||||
}
|
} else {
|
||||||
}
|
_currentHealth += diffHealth;
|
||||||
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);
|
|
||||||
}
|
|
||||||
if (_battle != nullptr) {
|
|
||||||
_battle->TriggerEventListener<ExperienceGainEvent>(this, _experience, exp);
|
|
||||||
}
|
|
||||||
_experience = exp;
|
|
||||||
_level = level;
|
|
||||||
}
|
|
||||||
ArbUt::BorrowedPtr<const Library::CreatureSpecies> Battling::Creature::GetDisplaySpecies() const noexcept {
|
|
||||||
auto species = _displaySpecies;
|
|
||||||
if (species == nullptr)
|
|
||||||
species = _species;
|
|
||||||
return species;
|
|
||||||
}
|
|
||||||
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 ArbUt::BasicStringView& itemName) {
|
|
||||||
ArbUt::BorrowedPtr<const Library::Item> item;
|
|
||||||
if (!_library->GetItemLibrary()->TryGet(itemName.GetHash(), item)) {
|
|
||||||
THROW_CREATURE("Item not found '" << itemName.c_str() << "'.");
|
|
||||||
}
|
|
||||||
_heldItem = item;
|
|
||||||
}
|
|
||||||
void Battling::Creature::SetHeldItem(uint32_t itemNameHash) {
|
|
||||||
ArbUt::BorrowedPtr<const Library::Item> item;
|
|
||||||
if (!_library->GetItemLibrary()->TryGet(itemNameHash, item)) {
|
|
||||||
THROW_CREATURE("Item not found.");
|
|
||||||
}
|
|
||||||
_heldItem = item;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Battling::Creature::AddVolatileScript(const ArbUt::StringView& name) {
|
// TODO: consider variant specific attacks?
|
||||||
auto script = _volatile.Get(name);
|
|
||||||
if (script != nullptr) {
|
|
||||||
script->Stack();
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
script = this->_library->LoadScript(ScriptCategory::Creature, name);
|
|
||||||
if (script == nullptr) {
|
|
||||||
THROW_CREATURE("Invalid volatile script requested for creature: '" << name.c_str() << "'.");
|
|
||||||
}
|
|
||||||
_volatile.Add(script.GetRaw());
|
|
||||||
}
|
|
||||||
|
|
||||||
void Battling::Creature::AddVolatileScript(Script* script) { _volatile.Add(script); }
|
void Creature::ChangeLevelBy(int8_t amount) {
|
||||||
void Battling::Creature::RemoveVolatileScript(const ArbUt::BasicStringView& name) { _volatile.Remove(name); }
|
auto level = _level + amount;
|
||||||
void Battling::Creature::RemoveVolatileScript(Battling::Script* script) { _volatile.Remove(script->GetName()); }
|
if (level > _library->GetSettings()->GetMaximalLevel())
|
||||||
bool Battling::Creature::HasVolatileScript(const ArbUt::BasicStringView& name) const { return _volatile.Has(name); }
|
level = _library->GetSettings()->GetMaximalLevel();
|
||||||
void Battling::Creature::AddAttack(Battling::LearnedAttack* attack) {
|
if (level < 1)
|
||||||
for (size_t i = 0; i < _attacks.Count(); i++) {
|
level = 1;
|
||||||
if (_attacks[i] == nullptr) {
|
_level = level;
|
||||||
_attacks.Set(i, attack);
|
_experience = _library->GetGrowthRateLibrary()->CalculateExperience(_species->GetGrowthRate(), _level);
|
||||||
|
RecalculateFlatStats();
|
||||||
|
}
|
||||||
|
|
||||||
|
const ArbUt::StringView& Creature::GetActiveTalent() const {
|
||||||
|
if (_hasOverridenTalent) {
|
||||||
|
return _overridenTalentName;
|
||||||
|
}
|
||||||
|
return _variant->GetTalent(_talentIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Creature::SetBattleData(ArbUt::BorrowedPtr<Battle> battle, ArbUt::BorrowedPtr<BattleSide> side) {
|
||||||
|
_battle = battle;
|
||||||
|
_side = side;
|
||||||
|
this->ResetActiveScripts();
|
||||||
|
}
|
||||||
|
|
||||||
|
// region Stat APIs
|
||||||
|
|
||||||
|
bool 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 Creature::RecalculateFlatStats() {
|
||||||
|
auto& statCalc = this->_library->GetStatCalculator();
|
||||||
|
this->_flatStats = statCalc->CalculateFlatStats(this);
|
||||||
|
RecalculateBoostedStats();
|
||||||
|
}
|
||||||
|
void Creature::RecalculateBoostedStats() {
|
||||||
|
this->_boostedStats = this->_library->GetStatCalculator()->CalculateFlatStats(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Creature::RecalculateFlatStat(Library::Statistic stat) {
|
||||||
|
auto s = this->_library->GetStatCalculator()->CalculateFlatStat(this, stat);
|
||||||
|
this->_flatStats.SetStat(stat, s);
|
||||||
|
RecalculateBoostedStat(stat);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Creature::RecalculateBoostedStat(Library::Statistic stat) {
|
||||||
|
auto s = this->_library->GetStatCalculator()->CalculateBoostedStat(this, stat);
|
||||||
|
this->_boostedStats.SetStat(stat, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
const ArbUt::BorrowedPtr<Battle>& Creature::GetBattle() const { return _battle; }
|
||||||
|
|
||||||
|
const ArbUt::BorrowedPtr<BattleSide>& Creature::GetBattleSide() const { return _side; }
|
||||||
|
|
||||||
|
bool Creature::IsFainted() const noexcept { return this->_currentHealth == 0; }
|
||||||
|
|
||||||
|
void Creature::OnFaint() {
|
||||||
|
AssertNotNull(_battle)
|
||||||
|
AssertNotNull(_side)
|
||||||
|
// HOOK: On Faint
|
||||||
|
if (_battle != nullptr) {
|
||||||
|
_battle->TriggerEventListener<FaintEvent>(this);
|
||||||
|
}
|
||||||
|
_library->GetExperienceLibrary()->HandleExperienceGain(this, _seenOpponents);
|
||||||
|
auto sideIndex = _side->GetCreatureIndex(this);
|
||||||
|
if (!_battle->CanSlotBeFilled(_side->GetSideIndex(), sideIndex)) {
|
||||||
|
_side->MarkSlotAsUnfillable(this);
|
||||||
|
}
|
||||||
|
_battle->ValidateBattleState();
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
if (battle != nullptr) {
|
||||||
|
battle->TriggerEventListener<DamageEvent>(this, source, _currentHealth, newHealth);
|
||||||
|
}
|
||||||
|
_currentHealth = newHealth;
|
||||||
|
|
||||||
|
if (IsFainted() && damage > 0 && battle != nullptr) {
|
||||||
|
OnFaint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Creature::Heal(uint32_t amount, bool canRevive) {
|
||||||
|
if (_currentHealth == 0 && !canRevive) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (amount > GetMaxHealth() - _currentHealth) {
|
||||||
|
amount = GetMaxHealth() - _currentHealth;
|
||||||
|
}
|
||||||
|
// HOOK: On Heal
|
||||||
|
auto newHealth = _currentHealth + amount;
|
||||||
|
auto battle = this->GetBattle();
|
||||||
|
if (battle != nullptr) {
|
||||||
|
battle->TriggerEventListener<HealEvent>(this, _currentHealth, newHealth);
|
||||||
|
}
|
||||||
|
_currentHealth = newHealth;
|
||||||
}
|
}
|
||||||
if (_attacks.Count() < _library->GetStaticLib()->GetSettings()->GetMaximalAttacks()) {
|
|
||||||
_attacks.Append(attack);
|
void Creature::RestoreAllAttackUses() noexcept {
|
||||||
}
|
for (auto& a : _attacks) {
|
||||||
THROW_CREATURE("Can't add attack. The creature already has the maximum amount of attacks.");
|
if (a != nullptr) {
|
||||||
}
|
a->RestoreAllUses();
|
||||||
uint8_t Battling::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();
|
void Creature::OverrideActiveTalent(const ArbUt::StringView& talent) {
|
||||||
|
_hasOverridenTalent = true;
|
||||||
|
if (_activeTalent != nullptr) {
|
||||||
|
_activeTalent->OnRemove();
|
||||||
|
_activeTalent.reset(this->_library->LoadScript(ScriptCategory::Talent, talent));
|
||||||
|
}
|
||||||
|
_overridenTalentName = talent;
|
||||||
}
|
}
|
||||||
return -1;
|
|
||||||
}
|
const std::unordered_set<uint8_t>& Creature::GetTypes() const noexcept { return _types; }
|
||||||
void Battling::Creature::ReplaceAttack(size_t index, Battling::LearnedAttack* attack) {
|
|
||||||
if (_attacks.Count() <= index) {
|
bool Creature::HasType(uint8_t type) const noexcept {
|
||||||
|
return std::find(_types.begin(), _types.end(), type) != _types.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Creature::ScriptCount() const {
|
||||||
|
auto c = 3;
|
||||||
|
if (_side != nullptr) {
|
||||||
|
c += _side->ScriptCount();
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Creature::GetActiveScripts(ArbUt::List<ScriptWrapper>& scripts) {
|
||||||
|
scripts.Append(ScriptWrapper::FromScript(&_activeTalent));
|
||||||
|
scripts.Append(ScriptWrapper::FromScript(&_status));
|
||||||
|
scripts.Append(ScriptWrapper::FromSet(&_volatile));
|
||||||
|
if (_side != nullptr) {
|
||||||
|
_side->GetActiveScripts(scripts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
if (_battle != nullptr) {
|
||||||
|
_battle->TriggerEventListener<ExperienceGainEvent>(this, _experience, exp);
|
||||||
|
}
|
||||||
|
_experience = exp;
|
||||||
|
_level = level;
|
||||||
|
}
|
||||||
|
ArbUt::BorrowedPtr<const Library::CreatureSpecies> Creature::GetDisplaySpecies() const noexcept {
|
||||||
|
auto species = _displaySpecies;
|
||||||
|
if (species == nullptr)
|
||||||
|
species = _species;
|
||||||
|
return species;
|
||||||
|
}
|
||||||
|
ArbUt::BorrowedPtr<const Library::SpeciesVariant> Creature::GetDisplayVariant() const noexcept {
|
||||||
|
auto variant = _displayVariant;
|
||||||
|
if (variant == nullptr)
|
||||||
|
variant = _variant;
|
||||||
|
return variant;
|
||||||
|
}
|
||||||
|
void Creature::SetHeldItem(const ArbUt::BasicStringView& itemName) {
|
||||||
|
ArbUt::BorrowedPtr<const Library::Item> item;
|
||||||
|
if (!_library->GetItemLibrary()->TryGet(itemName.GetHash(), item)) {
|
||||||
|
THROW_CREATURE("Item not found '" << itemName.c_str() << "'.");
|
||||||
|
}
|
||||||
|
_heldItem = item;
|
||||||
|
}
|
||||||
|
void Creature::SetHeldItem(uint32_t itemNameHash) {
|
||||||
|
ArbUt::BorrowedPtr<const Library::Item> item;
|
||||||
|
if (!_library->GetItemLibrary()->TryGet(itemNameHash, item)) {
|
||||||
|
THROW_CREATURE("Item not found.");
|
||||||
|
}
|
||||||
|
_heldItem = item;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Creature::AddVolatileScript(const ArbUt::StringView& name) {
|
||||||
|
auto script = _volatile.Get(name);
|
||||||
|
if (script != nullptr) {
|
||||||
|
script->Stack();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
script = this->_library->LoadScript(ScriptCategory::Creature, name);
|
||||||
|
if (script == nullptr) {
|
||||||
|
THROW_CREATURE("Invalid volatile script requested for creature: '" << name.c_str() << "'.");
|
||||||
|
}
|
||||||
|
_volatile.Add(script.GetRaw());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Creature::AddVolatileScript(Script* script) { _volatile.Add(script); }
|
||||||
|
void Creature::RemoveVolatileScript(const ArbUt::BasicStringView& name) { _volatile.Remove(name); }
|
||||||
|
void Creature::RemoveVolatileScript(Script* script) { _volatile.Remove(script->GetName()); }
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (_attacks.Count() < _library->GetStaticLib()->GetSettings()->GetMaximalAttacks()) {
|
if (_attacks.Count() < _library->GetStaticLib()->GetSettings()->GetMaximalAttacks()) {
|
||||||
_attacks.Append(attack);
|
_attacks.Append(attack);
|
||||||
}
|
}
|
||||||
THROW_CREATURE("Can't replace attack at index " << index << ". Number of attacks is " << _attacks.Count()
|
THROW_CREATURE("Can't add attack. The creature already has the maximum amount of attacks.");
|
||||||
<< ".");
|
|
||||||
}
|
}
|
||||||
_attacks.Set(index, attack);
|
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);
|
||||||
|
}
|
||||||
|
THROW_CREATURE("Can't replace attack at index " << index << ". Number of attacks is " << _attacks.Count()
|
||||||
|
<< ".");
|
||||||
|
}
|
||||||
|
_attacks.Set(index, attack);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -85,7 +85,9 @@ namespace CreatureLib::Battling {
|
||||||
return _species;
|
return _species;
|
||||||
}
|
}
|
||||||
inline const ArbUt::BorrowedPtr<const Library::SpeciesVariant>& GetVariant() const noexcept { return _variant; }
|
inline const ArbUt::BorrowedPtr<const Library::SpeciesVariant>& GetVariant() const noexcept { return _variant; }
|
||||||
virtual void ChangeVariant(ArbUt::BorrowedPtr<const Library::SpeciesVariant> variant);
|
virtual void ChangeSpecies(const ArbUt::BorrowedPtr<const Library::CreatureSpecies>& species,
|
||||||
|
const ArbUt::BorrowedPtr<const Library::SpeciesVariant>& variant);
|
||||||
|
virtual void ChangeVariant(const ArbUt::BorrowedPtr<const Library::SpeciesVariant>& variant);
|
||||||
inline uint8_t GetLevel() const noexcept { return _level; }
|
inline uint8_t GetLevel() const noexcept { return _level; }
|
||||||
inline uint32_t GetExperience() const noexcept { return _experience; }
|
inline uint32_t GetExperience() const noexcept { return _experience; }
|
||||||
inline uint32_t GetUniqueIdentifier() const noexcept { return _uniqueIdentifier; }
|
inline uint32_t GetUniqueIdentifier() const noexcept { return _uniqueIdentifier; }
|
||||||
|
|
Loading…
Reference in New Issue