1 #ifndef GAME_MWWORLD_INVENTORYSTORE_H
2 #define GAME_MWWORLD_INVENTORYSTORE_H
3 
4 #include "containerstore.hpp"
5 
6 #include "../mwmechanics/magiceffects.hpp"
7 
8 namespace ESM
9 {
10     struct MagicEffect;
11 }
12 
13 namespace MWMechanics
14 {
15     class NpcStats;
16 }
17 
18 namespace MWWorld
19 {
20     class InventoryStoreListener
21     {
22     public:
23         /**
24          * Fired when items are equipped or unequipped
25          */
equipmentChanged()26         virtual void equipmentChanged () {}
27 
28         /**
29          * @param effect
30          * @param isNew Is this effect new (e.g. the item for it was just now manually equipped)
31          *              or was it loaded from a savegame / initial game state? \n
32          *              If it isn't new, non-looping VFX should not be played.
33          * @param playSound Play effect sound?
34          */
permanentEffectAdded(const ESM::MagicEffect * magicEffect,bool isNew)35         virtual void permanentEffectAdded (const ESM::MagicEffect *magicEffect, bool isNew) {}
36 
37         virtual ~InventoryStoreListener() = default;
38     };
39 
40     ///< \brief Variant of the ContainerStore for NPCs
41     class InventoryStore : public ContainerStore
42     {
43         public:
44 
45             static constexpr int Slot_Helmet = 0;
46             static constexpr int Slot_Cuirass = 1;
47             static constexpr int Slot_Greaves = 2;
48             static constexpr int Slot_LeftPauldron = 3;
49             static constexpr int Slot_RightPauldron = 4;
50             static constexpr int Slot_LeftGauntlet = 5;
51             static constexpr int Slot_RightGauntlet = 6;
52             static constexpr int Slot_Boots = 7;
53             static constexpr int Slot_Shirt = 8;
54             static constexpr int Slot_Pants = 9;
55             static constexpr int Slot_Skirt = 10;
56             static constexpr int Slot_Robe = 11;
57             static constexpr int Slot_LeftRing = 12;
58             static constexpr int Slot_RightRing = 13;
59             static constexpr int Slot_Amulet = 14;
60             static constexpr int Slot_Belt = 15;
61             static constexpr int Slot_CarriedRight = 16;
62             static constexpr int Slot_CarriedLeft = 17;
63             static constexpr int Slot_Ammunition = 18;
64 
65             static constexpr int Slots = 19;
66 
67             static constexpr int Slot_NoSlot = -1;
68 
69         private:
70 
71             MWMechanics::MagicEffects mMagicEffects;
72 
73             InventoryStoreListener* mInventoryListener;
74 
75             // Enables updates of magic effects and actor model whenever items are equipped or unequipped.
76             // This is disabled during autoequip to avoid excessive updates
77             bool mUpdatesEnabled;
78 
79             bool mFirstAutoEquip;
80 
81             // Vanilla allows permanent effects with a random magnitude, so it needs to be stored here.
82             // We also need this to only play sounds and particle effects when the item is equipped, rather than on every update.
83             struct EffectParams
84             {
85                 // Modifier to scale between min and max magnitude
86                 float mRandom;
87                 // Multiplier for when an effect was fully or partially resisted
88                 float mMultiplier;
89             };
90 
91             typedef std::map<std::string, std::vector<EffectParams> > TEffectMagnitudes;
92             TEffectMagnitudes mPermanentMagicEffectMagnitudes;
93 
94             typedef std::vector<ContainerStoreIterator> TSlots;
95 
96             TSlots mSlots;
97 
98             void autoEquipWeapon(const MWWorld::Ptr& actor, TSlots& slots_);
99             void autoEquipArmor(const MWWorld::Ptr& actor, TSlots& slots_);
100             void autoEquipShield(const MWWorld::Ptr& actor, TSlots& slots_);
101 
102             // selected magic item (for using enchantments of type "Cast once" or "Cast when used")
103             ContainerStoreIterator mSelectedEnchantItem;
104 
105             void copySlots (const InventoryStore& store);
106 
107             void initSlots (TSlots& slots_);
108 
109             void updateMagicEffects(const Ptr& actor);
110 
111             void fireEquipmentChangedEvent(const Ptr& actor);
112 
113             void storeEquipmentState (const MWWorld::LiveCellRefBase& ref, int index, ESM::InventoryState& inventory) const override;
114             void readEquipmentState (const MWWorld::ContainerStoreIterator& iter, int index, const ESM::InventoryState& inventory) override;
115 
116             ContainerStoreIterator findSlot (int slot) const;
117 
118         public:
119 
120             InventoryStore();
121 
122             InventoryStore (const InventoryStore& store);
123 
124             InventoryStore& operator= (const InventoryStore& store);
125 
clone()126             std::unique_ptr<ContainerStore> clone() override { return std::make_unique<InventoryStore>(*this); }
127 
128             ContainerStoreIterator add (const Ptr& itemPtr, int count, const Ptr& actorPtr, bool allowAutoEquip = true, bool resolve = true) override;
129             ///< Add the item pointed to by \a ptr to this container. (Stacks automatically if needed)
130             /// Auto-equip items if specific conditions are fulfilled and allowAutoEquip is true (see the implementation).
131             ///
132             /// \note The item pointed to is not required to exist beyond this function call.
133             ///
134             /// \attention Do not add items to an existing stack by increasing the count instead of
135             /// calling this function!
136             ///
137             /// @return if stacking happened, return iterator to the item that was stacked against, otherwise iterator to the newly inserted item.
138 
139             void equip (int slot, const ContainerStoreIterator& iterator, const Ptr& actor);
140             ///< \warning \a iterator can not be an end()-iterator, use unequip function instead
141 
142             bool isEquipped(const MWWorld::ConstPtr& item);
143             ///< Utility function, returns true if the given item is equipped in any slot
144 
145             void setSelectedEnchantItem(const ContainerStoreIterator& iterator);
146             ///< set the selected magic item (for using enchantments of type "Cast once" or "Cast when used")
147             /// \note to unset the selected item, call this method with end() iterator
148 
149             ContainerStoreIterator getSelectedEnchantItem();
150             ///< @return selected magic item (for using enchantments of type "Cast once" or "Cast when used")
151             /// \note if no item selected, return end() iterator
152 
153             ContainerStoreIterator getSlot (int slot);
154             ConstContainerStoreIterator getSlot(int slot) const;
155 
156             ContainerStoreIterator getPreferredShield(const MWWorld::Ptr& actor);
157 
158             void unequipAll(const MWWorld::Ptr& actor);
159             ///< Unequip all currently equipped items.
160 
161             void autoEquip (const MWWorld::Ptr& actor);
162             ///< Auto equip items according to stats and item value.
163 
164             const MWMechanics::MagicEffects& getMagicEffects() const;
165             ///< Return magic effects from worn items.
166 
167             bool stacks (const ConstPtr& ptr1, const ConstPtr& ptr2) const override;
168             ///< @return true if the two specified objects can stack with each other
169 
170             using ContainerStore::remove;
171             int remove(const Ptr& item, int count, const Ptr& actor, bool equipReplacement = 0, bool resolve = true) override;
172             ///< Remove \a count item(s) designated by \a item from this inventory.
173             ///
174             /// @return the number of items actually removed
175 
176             ContainerStoreIterator unequipSlot(int slot, const Ptr& actor, bool applyUpdates = true);
177             ///< Unequip \a slot.
178             ///
179             /// @return an iterator to the item that was previously in the slot
180 
181             ContainerStoreIterator unequipItem(const Ptr& item, const Ptr& actor);
182             ///< Unequip an item identified by its Ptr. An exception is thrown
183             /// if the item is not currently equipped.
184             ///
185             /// @return an iterator to the item that was previously in the slot
186             /// (it can be re-stacked so its count may be different than when it
187             /// was equipped).
188 
189             ContainerStoreIterator unequipItemQuantity(const Ptr& item, const Ptr& actor, int count);
190             ///< Unequip a specific quantity of an item identified by its Ptr.
191             /// An exception is thrown if the item is not currently equipped,
192             /// if count <= 0, or if count > the item stack size.
193             ///
194             /// @return an iterator to the unequipped items that were previously
195             /// in the slot (they can be re-stacked so its count may be different
196             /// than the requested count).
197 
198             void setInvListener (InventoryStoreListener* listener, const Ptr& actor);
199             ///< Set a listener for various events, see \a InventoryStoreListener
200 
201             InventoryStoreListener* getInvListener();
202 
203             void visitEffectSources (MWMechanics::EffectSourceVisitor& visitor);
204 
205             void purgeEffect (short effectId, bool wholeSpell = false);
206             ///< Remove a magic effect
207 
208             void purgeEffect (short effectId, const std::string& sourceId, bool wholeSpell = false, int effectIndex=-1);
209             ///< Remove a magic effect
210 
211             void clear() override;
212             ///< Empty container.
213 
214             void writeState (ESM::InventoryState& state) const override;
215 
216             void readState (const ESM::InventoryState& state) override;
217     };
218 }
219 
220 #endif
221