#ifndef CREATURELIB_BATTLECREATURE_HPP #define CREATURELIB_BATTLECREATURE_HPP #include "../../Defines.hpp" #include "../../Library/ClampedStatisticSet.hpp" #include "../../Library/CreatureData/CreatureSpecies.hpp" #include "../../Library/Items/Item.hpp" #include "../ScriptHandling/ScriptAggregator.hpp" #include "../ScriptHandling/ScriptSet.hpp" #include "../ScriptHandling/ScriptSource.hpp" #include "CreatureIndex.hpp" #include "DamageSource.hpp" #include "LearnedAttack.hpp" namespace CreatureLib::Battling { // Forward declare battle class class Battle; class BattleSide; class BattleLibrary; class Creature : public ScriptSource { protected: ArbUt::BorrowedPtr _library; ArbUt::BorrowedPtr _species; ArbUt::BorrowedPtr _variant; ArbUt::OptionalBorrowedPtr _displaySpecies = nullptr; ArbUt::OptionalBorrowedPtr _displayVariant = nullptr; level_int_t _level; u32 _experience; u32 _uniqueIdentifier; Library::Gender _gender; u8 _coloring; u32 _currentHealth = 0; f32 _weight; f32 _height; Library::ClampedStatisticSet _statBoost; Library::StatisticSet _flatStats; Library::StatisticSet _boostedStats; struct BattleData { bool OnBattleField = false; ArbUt::OptionalBorrowedPtr Battle = nullptr; ArbUt::OptionalBorrowedPtr Side = nullptr; CreatureIndex Index = {}; std::unordered_set> SeenOpponents; } _battleData = {}; std::optional _nickname = {}; CreatureLib::Library::TalentIndex _talentIndex = {}; std::unique_ptr _activeTalent = {}; bool _hasOverridenTalent = false; ArbUt::OptionalBorrowedPtr _overridenTalent = {}; ArbUt::OptionalUniquePtrList _attacks = {}; bool _allowedExperienceGain = {}; std::unique_ptr _status = nullptr; ScriptSet _volatile = {}; std::vector _types = {}; ArbUt::OptionalBorrowedPtr _heldItem; std::unique_ptr _heldItemTriggerScript = nullptr; private: void OnFaint(DamageSource damageSource); public: Creature(const ArbUt::BorrowedPtr& library, const ArbUt::BorrowedPtr& species, const ArbUt::BorrowedPtr& variant, level_int_t level, u32 experience, u32 uid, Library::Gender gender, u8 coloring, ArbUt::OptionalBorrowedPtr heldItem, std::optional nickname, const Library::TalentIndex& talent, const std::vector& attacks, bool allowedExperienceGain = true); virtual ~Creature(); virtual void Initialize() { RecalculateFlatStats(); _currentHealth = GetBoostedStat(Library::Statistic::Health); } inline const ArbUt::BorrowedPtr& GetLibrary() const noexcept { return _library; } inline const ArbUt::BorrowedPtr& GetSpecies() const noexcept { return _species; } inline const ArbUt::BorrowedPtr& GetVariant() const noexcept { return _variant; } virtual void ChangeSpecies(const ArbUt::BorrowedPtr& species, const ArbUt::BorrowedPtr& variant); virtual void ChangeVariant(const ArbUt::StringView& variantName); virtual void ChangeVariant(const ArbUt::BorrowedPtr& variant); inline level_int_t GetLevel() const noexcept { return _level; } inline u32 GetExperience() const noexcept { return _experience; } inline u32 GetUniqueIdentifier() const noexcept { return _uniqueIdentifier; } inline Library::Gender GetGender() const noexcept { return _gender; } inline u8 GetColoring() const noexcept { return _coloring; } inline bool HasHeldItem(const ArbUt::BasicStringView& name) const noexcept { return _heldItem.HasValue() && _heldItem.GetValue()->GetName() == name; } inline bool HasHeldItem(u32 nameHash) const noexcept { return _heldItem.HasValue() && _heldItem.GetValue()->GetName() == nameHash; } inline const ArbUt::OptionalBorrowedPtr& GetHeldItem() const noexcept { return _heldItem; } void SetHeldItem(const ArbUt::BasicStringView& itemName); void SetHeldItem(u32 itemNameHash); inline void SetHeldItem(const ArbUt::BorrowedPtr& item) noexcept { _heldItem = item; }; bool ConsumeHeldItem(); inline u32 GetCurrentHealth() const noexcept { return _currentHealth; } inline f32 GetWeight() const noexcept { return _weight; } inline void SetWeight(f32 weight) noexcept { if (weight < 0.1f) { weight = 0.1f; } _weight = weight; } inline f32 GetHeight() const noexcept { return _height; } inline void SetHeight(f32 height) noexcept { if (height < 0.1f) { height = 0.1f; } _height = height; } void SetBattleData(const ArbUt::BorrowedPtr& battle, const ArbUt::BorrowedPtr& side); void ClearBattleData() noexcept; inline const ArbUt::OptionalBorrowedPtr& GetBattle() const { return _battleData.Battle; } inline const ArbUt::OptionalBorrowedPtr& GetBattleSide() const { return _battleData.Side; } void SetOnBattleField(bool value); inline void SetBattleIndex(const CreatureIndex& index) { _battleData.Index = index; } inline const CreatureIndex& GetBattleIndex() const noexcept { return _battleData.Index; } inline bool IsOnBattleField() const { return _battleData.OnBattleField; } inline std::optional GetNickname() const noexcept { if (_nickname.has_value()) { return _nickname.value(); } return {}; } inline void SetNickname(std::string nickname) noexcept { _nickname = nickname; } const CreatureLib::Library::TalentIndex& GetRealTalent() const noexcept { return _talentIndex; } ArbUt::BorrowedPtr GetActiveTalent() const; /// Are we allowed to use this creature in a battle? [[nodiscard]] virtual bool IsUsable() const noexcept; [[nodiscard]] bool IsFainted() const noexcept; [[nodiscard]] const std::vector& GetTypes() const noexcept; [[nodiscard]] bool HasType(u8 type) const noexcept; void SetType(u8 index, u8 type) noexcept; u32 GetMaxHealth() const noexcept { return _boostedStats.GetHealth(); } void ChangeLevelBy(i8 amount); void Damage(u32 damage, DamageSource source); void Heal(u32 amount, bool canRevive = false); void RestoreAllAttackUses() noexcept; void OverrideActiveTalent(const ArbUt::StringView& talent); void AddExperience(u32 amount); void MarkOpponentAsSeen(ArbUt::BorrowedPtr creature) { _battleData.SeenOpponents.insert(creature); } const std::unordered_set>& GetSeenOpponents() const { return _battleData.SeenOpponents; } size_t ScriptCount() const override; void GetActiveScripts(ArbUt::List& scripts) override; void GetOwnScripts(ArbUt::List& scripts) override; void ClearVolatileScripts(); BattleScript* non_null AddVolatileScript(const ArbUt::StringView& name); BattleScript* non_null AddVolatileScript(BattleScript* non_null script); void RemoveVolatileScript(const ArbUt::BasicStringView& name); void RemoveVolatileScript(BattleScript* non_null script); bool HasVolatileScript(const ArbUt::BasicStringView& name) const; const ArbUt::OptionalUniquePtrList& GetAttacks() noexcept { return _attacks; } bool HasAttack(const ArbUt::StringView& name) { for (auto& a : _attacks) { if (a == nullptr) continue; if (a->GetAttack()->GetName() == name) return true; } return false; } ArbUt::OptionalBorrowedPtr GetDisplaySpecies() const noexcept; ArbUt::OptionalBorrowedPtr GetDisplayVariant() const noexcept; void SetDisplaySpecies(const ArbUt::BorrowedPtr& species) noexcept { _displaySpecies = species.GetRaw(); } void SetDisplayVariant(ArbUt::BorrowedPtr variant) noexcept { _displayVariant = variant.GetRaw(); }; inline bool AllowedExperienceGain() const noexcept { return _allowedExperienceGain; } inline void SetAllowedExperienceGain(bool allowed) noexcept { _allowedExperienceGain = allowed; } u8 GetAvailableAttackSlot() const noexcept; void AddAttack(LearnedAttack* non_null attack); void ReplaceAttack(size_t index, LearnedAttack* non_null attack); void SwapAttacks(size_t a, size_t b) { _attacks.Swap(a, b); } void SetStatus(const ArbUt::StringView& name); void ClearStatus(); const ArbUt::StringView& GetStatusName() const noexcept { if (_status == nullptr) return ArbUt::StringView::EmptyString(); return _status->GetName(); } // region Stat APIs bool ChangeStatBoost(Library::Statistic stat, i8 diffAmount, bool selfInflicted = false); [[nodiscard]] inline u32 GetFlatStat(Library::Statistic stat) const { return _flatStats.GetStat(stat); } [[nodiscard]] inline u32 GetBoostedStat(Library::Statistic stat) const { return _boostedStats.GetStat(stat); } [[nodiscard]] inline u32 GetBaseStat(Library::Statistic stat) const { return _variant->GetStatistic(stat); } [[nodiscard]] inline i8 GetStatBoost(Library::Statistic stat) const { return _statBoost.GetStat(stat); } void RecalculateFlatStats(); void RecalculateBoostedStats(); void RecalculateFlatStat(Library::Statistic); void RecalculateBoostedStat(Library::Statistic); // endregion virtual Creature* non_null Clone() const; }; } #endif // CREATURELIB_CREATURE_HPP