mirror of
				https://github.com/TES3MP/openmw-tes3mp.git
				synced 2025-10-31 14:56:44 +00:00 
			
		
		
		
	Merge pull request #2080 from akortunov/recharge
Recharge items outside of player inventory
This commit is contained in:
		
						commit
						b7a1e6561b
					
				
					 20 changed files with 332 additions and 146 deletions
				
			
		|  | @ -19,6 +19,7 @@ | |||
|     Bug #3778: [Mod] Improved Thrown Weapon Projectiles - weapons have wrong transformation during throw animation | ||||
|     Bug #3812: Wrong multiline tooltips width when word-wrapping is enabled | ||||
|     Bug #3894: Hostile spell effects not detected/present on first frame of OnPCHitMe | ||||
|     Bug #4077: Enchanted items are not recharged if they are not in the player's inventory | ||||
|     Bug #4202: Open .omwaddon files without needing toopen openmw-cs first | ||||
|     Bug #4240: Ash storm origin coordinates and hand shielding animation behavior are incorrect | ||||
|     Bug #4270: Closing doors while they are obstructed desyncs closing sfx | ||||
|  |  | |||
|  | @ -81,7 +81,7 @@ add_openmw_dir (mwclass | |||
| add_openmw_dir (mwmechanics | ||||
|     mechanicsmanagerimp stat creaturestats magiceffects movement actorutil | ||||
|     drawstate spells activespells npcstats aipackage aisequence aipursue alchemy aiwander aitravel aifollow aiavoiddoor aibreathe | ||||
|     aicast aiescort aiface aiactivate aicombat repair enchanting pathfinding pathgrid security spellsuccess spellcasting | ||||
|     aicast aiescort aiface aiactivate aicombat recharge repair enchanting pathfinding pathgrid security spellsuccess spellcasting | ||||
|     disease pickpocket levelledlist combat steering obstacle autocalcspell difficultyscaling aicombataction actor summoning | ||||
|     character actors objects aistate coordinateconverter trading weaponpriority spellpriority weapontype | ||||
|     ) | ||||
|  |  | |||
|  | @ -133,6 +133,7 @@ bool OMW::Engine::frame(float frametime) | |||
|             { | ||||
|                 double hours = (frametime * mEnvironment.getWorld()->getTimeScaleFactor()) / 3600.0; | ||||
|                 mEnvironment.getWorld()->advanceTime(hours, true); | ||||
|                 mEnvironment.getWorld()->rechargeItems(frametime, true); | ||||
|             } | ||||
|         } | ||||
|         osg::Timer_t afterScriptTick = osg::Timer::instance()->tick(); | ||||
|  |  | |||
|  | @ -73,8 +73,6 @@ namespace MWBase | |||
|             /// \param paused In game type does not currently advance (this usually means some GUI
 | ||||
|             /// component is up).
 | ||||
| 
 | ||||
|             virtual void advanceTime (float duration) = 0; | ||||
| 
 | ||||
|             virtual void setPlayerName (const std::string& name) = 0; | ||||
|             ///< Set player name.
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -584,6 +584,7 @@ namespace MWBase | |||
|             virtual bool isPlayerInJail() const = 0; | ||||
| 
 | ||||
|             virtual void rest(double hours) = 0; | ||||
|             virtual void rechargeItems(double duration, bool activeOnly) = 0; | ||||
| 
 | ||||
|             virtual void setPlayerTraveling(bool traveling) = 0; | ||||
|             virtual bool isPlayerTraveling() const = 0; | ||||
|  |  | |||
|  | @ -17,6 +17,7 @@ | |||
| 
 | ||||
| #include "../mwmechanics/creaturestats.hpp" | ||||
| #include "../mwmechanics/actorutil.hpp" | ||||
| #include "../mwmechanics/recharge.hpp" | ||||
| 
 | ||||
| #include "itemwidget.hpp" | ||||
| #include "itemchargeview.hpp" | ||||
|  | @ -130,62 +131,9 @@ void Recharge::onItemCancel() | |||
| void Recharge::onItemClicked(MyGUI::Widget *sender, const MWWorld::Ptr& item) | ||||
| { | ||||
|     MWWorld::Ptr gem = *mGemIcon->getUserData<MWWorld::Ptr>(); | ||||
| 
 | ||||
|     if (!gem.getRefData().getCount()) | ||||
|     if (!MWMechanics::rechargeItem(item, gem)) | ||||
|         return; | ||||
| 
 | ||||
|     MWWorld::Ptr player = MWMechanics::getPlayer(); | ||||
|     MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player); | ||||
| 
 | ||||
|     float luckTerm = 0.1f * stats.getAttribute(ESM::Attribute::Luck).getModified(); | ||||
|     if (luckTerm < 1|| luckTerm > 10) | ||||
|         luckTerm = 1; | ||||
| 
 | ||||
|     float intelligenceTerm = 0.2f * stats.getAttribute(ESM::Attribute::Intelligence).getModified(); | ||||
| 
 | ||||
|     if (intelligenceTerm > 20) | ||||
|         intelligenceTerm = 20; | ||||
|     if (intelligenceTerm < 1) | ||||
|         intelligenceTerm = 1; | ||||
| 
 | ||||
|     float x = (player.getClass().getSkill(player, ESM::Skill::Enchant) + intelligenceTerm + luckTerm) * stats.getFatigueTerm(); | ||||
|     int roll = Misc::Rng::roll0to99(); | ||||
|     if (roll < x) | ||||
|     { | ||||
|         std::string soul = gem.getCellRef().getSoul(); | ||||
|         const ESM::Creature *creature = MWBase::Environment::get().getWorld()->getStore().get<ESM::Creature>().find(soul); | ||||
| 
 | ||||
|         float restored = creature->mData.mSoul * (roll / x); | ||||
| 
 | ||||
|         const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().find( | ||||
|                     item.getClass().getEnchantment(item)); | ||||
|         item.getCellRef().setEnchantmentCharge( | ||||
|             std::min(item.getCellRef().getEnchantmentCharge() + restored, static_cast<float>(enchantment->mData.mCharge))); | ||||
| 
 | ||||
|         MWBase::Environment::get().getWindowManager()->playSound("Enchant Success"); | ||||
| 
 | ||||
|         player.getClass().getContainerStore(player).restack(item); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         MWBase::Environment::get().getWindowManager()->playSound("Enchant Fail"); | ||||
|     } | ||||
| 
 | ||||
|     player.getClass().skillUsageSucceeded (player, ESM::Skill::Enchant, 0); | ||||
|     gem.getContainerStore()->remove(gem, 1, player); | ||||
| 
 | ||||
|     if (gem.getRefData().getCount() == 0) | ||||
|     { | ||||
|         std::string message = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("sNotifyMessage51")->mValue.getString(); | ||||
|         message = Misc::StringUtils::format(message, gem.getClass().getName(gem)); | ||||
| 
 | ||||
|         MWBase::Environment::get().getWindowManager()->messageBox(message); | ||||
| 
 | ||||
|         // special case: readd Azura's Star
 | ||||
|         if (Misc::StringUtils::ciEqual(gem.get<ESM::Miscellaneous>()->mBase->mId, "Misc_SoulGem_Azura")) | ||||
|             player.getClass().getContainerStore(player).add("Misc_SoulGem_Azura", 1, player); | ||||
|     } | ||||
| 
 | ||||
|     updateView(); | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -287,16 +287,6 @@ namespace MWMechanics | |||
|         mWatched = ptr; | ||||
|     } | ||||
| 
 | ||||
|     void MechanicsManager::advanceTime (float duration) | ||||
|     { | ||||
|         // Uses ingame time, but scaled to real time
 | ||||
|         const float timeScaleFactor = MWBase::Environment::get().getWorld()->getTimeScaleFactor(); | ||||
|         if (timeScaleFactor != 0.0f) | ||||
|             duration /= timeScaleFactor; | ||||
|         MWWorld::Ptr player = getPlayer(); | ||||
|         player.getClass().getInventoryStore(player).rechargeItems(duration); | ||||
|     } | ||||
| 
 | ||||
|     void MechanicsManager::update(float duration, bool paused) | ||||
|     { | ||||
|         if(!mWatched.isEmpty()) | ||||
|  |  | |||
|  | @ -78,8 +78,6 @@ namespace MWMechanics | |||
|             /// \param paused In game type does not currently advance (this usually means some GUI
 | ||||
|             /// component is up).
 | ||||
| 
 | ||||
|             virtual void advanceTime (float duration) override; | ||||
| 
 | ||||
|             virtual void setPlayerName (const std::string& name) override; | ||||
|             ///< Set player name.
 | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										92
									
								
								apps/openmw/mwmechanics/recharge.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								apps/openmw/mwmechanics/recharge.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,92 @@ | |||
| #include "recharge.hpp" | ||||
| 
 | ||||
| #include <components/misc/rng.hpp> | ||||
| 
 | ||||
| #include "../mwbase/world.hpp" | ||||
| #include "../mwbase/environment.hpp" | ||||
| #include "../mwbase/windowmanager.hpp" | ||||
| 
 | ||||
| #include "../mwworld/containerstore.hpp" | ||||
| #include "../mwworld/class.hpp" | ||||
| #include "../mwworld/esmstore.hpp" | ||||
| 
 | ||||
| #include "creaturestats.hpp" | ||||
| #include "actorutil.hpp" | ||||
| 
 | ||||
| namespace MWMechanics | ||||
| { | ||||
| 
 | ||||
| bool rechargeItem(const MWWorld::Ptr &item, const float maxCharge, const float duration) | ||||
| { | ||||
|     float charge = item.getCellRef().getEnchantmentCharge(); | ||||
|     if (charge == -1 || charge == maxCharge) | ||||
|         return false; | ||||
| 
 | ||||
|     static const float fMagicItemRechargePerSecond = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find( | ||||
|                 "fMagicItemRechargePerSecond")->mValue.getFloat(); | ||||
| 
 | ||||
|     item.getCellRef().setEnchantmentCharge(std::min(charge + fMagicItemRechargePerSecond * duration, maxCharge)); | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| bool rechargeItem(const MWWorld::Ptr &item, const MWWorld::Ptr &gem) | ||||
| { | ||||
|     if (!gem.getRefData().getCount()) | ||||
|         return false; | ||||
| 
 | ||||
|     MWWorld::Ptr player = MWMechanics::getPlayer(); | ||||
|     MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player); | ||||
| 
 | ||||
|     float luckTerm = 0.1f * stats.getAttribute(ESM::Attribute::Luck).getModified(); | ||||
|     if (luckTerm < 1 || luckTerm > 10) | ||||
|         luckTerm = 1; | ||||
| 
 | ||||
|     float intelligenceTerm = 0.2f * stats.getAttribute(ESM::Attribute::Intelligence).getModified(); | ||||
| 
 | ||||
|     if (intelligenceTerm > 20) | ||||
|         intelligenceTerm = 20; | ||||
|     if (intelligenceTerm < 1) | ||||
|         intelligenceTerm = 1; | ||||
| 
 | ||||
|     float x = (player.getClass().getSkill(player, ESM::Skill::Enchant) + intelligenceTerm + luckTerm) * stats.getFatigueTerm(); | ||||
|     int roll = Misc::Rng::roll0to99(); | ||||
|     if (roll < x) | ||||
|     { | ||||
|         std::string soul = gem.getCellRef().getSoul(); | ||||
|         const ESM::Creature *creature = MWBase::Environment::get().getWorld()->getStore().get<ESM::Creature>().find(soul); | ||||
| 
 | ||||
|         float restored = creature->mData.mSoul * (roll / x); | ||||
| 
 | ||||
|         const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().find( | ||||
|                     item.getClass().getEnchantment(item)); | ||||
|         item.getCellRef().setEnchantmentCharge( | ||||
|             std::min(item.getCellRef().getEnchantmentCharge() + restored, static_cast<float>(enchantment->mData.mCharge))); | ||||
| 
 | ||||
|         MWBase::Environment::get().getWindowManager()->playSound("Enchant Success"); | ||||
| 
 | ||||
|         player.getClass().getContainerStore(player).restack(item); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         MWBase::Environment::get().getWindowManager()->playSound("Enchant Fail"); | ||||
|     } | ||||
| 
 | ||||
|     player.getClass().skillUsageSucceeded (player, ESM::Skill::Enchant, 0); | ||||
|     gem.getContainerStore()->remove(gem, 1, player); | ||||
| 
 | ||||
|     if (gem.getRefData().getCount() == 0) | ||||
|     { | ||||
|         std::string message = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("sNotifyMessage51")->mValue.getString(); | ||||
|         message = Misc::StringUtils::format(message, gem.getClass().getName(gem)); | ||||
| 
 | ||||
|         MWBase::Environment::get().getWindowManager()->messageBox(message); | ||||
| 
 | ||||
|         // special case: readd Azura's Star
 | ||||
|         if (Misc::StringUtils::ciEqual(gem.get<ESM::Miscellaneous>()->mBase->mId, "Misc_SoulGem_Azura")) | ||||
|             player.getClass().getContainerStore(player).add("Misc_SoulGem_Azura", 1, player); | ||||
|     } | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										15
									
								
								apps/openmw/mwmechanics/recharge.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								apps/openmw/mwmechanics/recharge.hpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,15 @@ | |||
| #ifndef MWMECHANICS_RECHARGE_H | ||||
| #define MWMECHANICS_RECHARGE_H | ||||
| 
 | ||||
| #include "../mwworld/ptr.hpp" | ||||
| 
 | ||||
| namespace MWMechanics | ||||
| { | ||||
| 
 | ||||
|     bool rechargeItem(const MWWorld::Ptr &item, const float maxCharge, const float duration); | ||||
| 
 | ||||
|     bool rechargeItem(const MWWorld::Ptr &item, const MWWorld::Ptr &gem); | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
|  | @ -163,6 +163,19 @@ void MWWorld::Cells::rest (double hours) | |||
|     } | ||||
| } | ||||
| 
 | ||||
| void MWWorld::Cells::recharge (float duration) | ||||
| { | ||||
|     for (auto &interior : mInteriors) | ||||
|     { | ||||
|         interior.second.recharge(duration); | ||||
|     } | ||||
| 
 | ||||
|     for (auto &exterior : mExteriors) | ||||
|     { | ||||
|         exterior.second.recharge(duration); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| MWWorld::CellStore *MWWorld::Cells::getCell (const ESM::CellId& id) | ||||
| { | ||||
|     if (id.mPaged) | ||||
|  |  | |||
|  | @ -61,7 +61,9 @@ namespace MWWorld | |||
| 
 | ||||
|             /// @note name must be lower case
 | ||||
|             Ptr getPtr (const std::string& name); | ||||
| 
 | ||||
|             void rest (double hours); | ||||
|             void recharge (float duration); | ||||
| 
 | ||||
|             /// Get all Ptrs referencing \a name in exterior cells
 | ||||
|             /// @note Due to the current implementation of getPtr this only supports one Ptr per cell.
 | ||||
|  |  | |||
|  | @ -21,6 +21,7 @@ | |||
| #include "../mwbase/world.hpp" | ||||
| 
 | ||||
| #include "../mwmechanics/creaturestats.hpp" | ||||
| #include "../mwmechanics/recharge.hpp" | ||||
| 
 | ||||
| #include "ptr.hpp" | ||||
| #include "esmstore.hpp" | ||||
|  | @ -328,6 +329,7 @@ namespace MWWorld | |||
|     void CellStore::updateMergedRefs() | ||||
|     { | ||||
|         mMergedRefs.clear(); | ||||
|         mRechargingItemsUpToDate = false; | ||||
|         MergeVisitor visitor(mMergedRefs, mMovedHere, mMovedToAnotherCell); | ||||
|         forEachInternal(visitor); | ||||
|         visitor.merge(); | ||||
|  | @ -345,7 +347,7 @@ namespace MWWorld | |||
|     } | ||||
| 
 | ||||
|     CellStore::CellStore (const ESM::Cell *cell, const MWWorld::ESMStore& esmStore, std::vector<ESM::ESMReader>& readerList) | ||||
|         : mStore(esmStore), mReader(readerList), mCell (cell), mState (State_Unloaded), mHasState (false), mLastRespawn(0,0) | ||||
|         : mStore(esmStore), mReader(readerList), mCell (cell), mState (State_Unloaded), mHasState (false), mLastRespawn(0,0), mRechargingItemsUpToDate(false) | ||||
|     { | ||||
|         mWaterLevel = cell->mWater; | ||||
|     } | ||||
|  | @ -992,6 +994,42 @@ namespace MWWorld | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     void CellStore::recharge(float duration) | ||||
|     { | ||||
|         if (duration <= 0) | ||||
|             return; | ||||
| 
 | ||||
|         if (mState == State_Loaded) | ||||
|         { | ||||
|             for (CellRefList<ESM::Creature>::List::iterator it (mCreatures.mList.begin()); it!=mCreatures.mList.end(); ++it) | ||||
|             { | ||||
|                 Ptr ptr = getCurrentPtr(&*it); | ||||
|                 if (!ptr.isEmpty() && ptr.getRefData().getCount() > 0) | ||||
|                 { | ||||
|                     ptr.getClass().getContainerStore(ptr).rechargeItems(duration); | ||||
|                 } | ||||
|             } | ||||
|             for (CellRefList<ESM::NPC>::List::iterator it (mNpcs.mList.begin()); it!=mNpcs.mList.end(); ++it) | ||||
|             { | ||||
|                 Ptr ptr = getCurrentPtr(&*it); | ||||
|                 if (!ptr.isEmpty() && ptr.getRefData().getCount() > 0) | ||||
|                 { | ||||
|                     ptr.getClass().getContainerStore(ptr).rechargeItems(duration); | ||||
|                 } | ||||
|             } | ||||
|             for (CellRefList<ESM::Container>::List::iterator it (mContainers.mList.begin()); it!=mContainers.mList.end(); ++it) | ||||
|             { | ||||
|                 Ptr ptr = getCurrentPtr(&*it); | ||||
|                 if (!ptr.isEmpty() && ptr.getRefData().getCustomData() != nullptr && ptr.getRefData().getCount() > 0) | ||||
|                 { | ||||
|                     ptr.getClass().getContainerStore(ptr).rechargeItems(duration); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             rechargeItems(duration); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     void CellStore::respawn() | ||||
|     { | ||||
|         if (mState == State_Loaded) | ||||
|  | @ -1027,4 +1065,73 @@ namespace MWWorld | |||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     void MWWorld::CellStore::rechargeItems(float duration) | ||||
|     { | ||||
|         if (!mRechargingItemsUpToDate) | ||||
|         { | ||||
|             updateRechargingItems(); | ||||
|             mRechargingItemsUpToDate = true; | ||||
|         } | ||||
|         for (TRechargingItems::iterator it = mRechargingItems.begin(); it != mRechargingItems.end(); ++it) | ||||
|         { | ||||
|             MWMechanics::rechargeItem(it->first, it->second, duration); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     void MWWorld::CellStore::updateRechargingItems() | ||||
|     { | ||||
|         mRechargingItems.clear(); | ||||
| 
 | ||||
|         for (CellRefList<ESM::Weapon>::List::iterator it (mWeapons.mList.begin()); it!=mWeapons.mList.end(); ++it) | ||||
|         { | ||||
|             Ptr ptr = getCurrentPtr(&*it); | ||||
|             if (!ptr.isEmpty() && ptr.getRefData().getCount() > 0) | ||||
|             { | ||||
|                 checkItem(ptr); | ||||
|             } | ||||
|         } | ||||
|         for (CellRefList<ESM::Armor>::List::iterator it (mArmors.mList.begin()); it!=mArmors.mList.end(); ++it) | ||||
|         { | ||||
|             Ptr ptr = getCurrentPtr(&*it); | ||||
|             if (!ptr.isEmpty() && ptr.getRefData().getCount() > 0) | ||||
|             { | ||||
|                 checkItem(ptr); | ||||
|             } | ||||
|         } | ||||
|         for (CellRefList<ESM::Clothing>::List::iterator it (mClothes.mList.begin()); it!=mClothes.mList.end(); ++it) | ||||
|         { | ||||
|             Ptr ptr = getCurrentPtr(&*it); | ||||
|             if (!ptr.isEmpty() && ptr.getRefData().getCount() > 0) | ||||
|             { | ||||
|                 checkItem(ptr); | ||||
|             } | ||||
|         } | ||||
|         for (CellRefList<ESM::Book>::List::iterator it (mBooks.mList.begin()); it!=mBooks.mList.end(); ++it) | ||||
|         { | ||||
|             Ptr ptr = getCurrentPtr(&*it); | ||||
|             if (!ptr.isEmpty() && ptr.getRefData().getCount() > 0) | ||||
|             { | ||||
|                 checkItem(ptr); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     void MWWorld::CellStore::checkItem(Ptr ptr) | ||||
|     { | ||||
|         if (ptr.getClass().getEnchantment(ptr).empty()) | ||||
|             return; | ||||
| 
 | ||||
|         std::string enchantmentId = ptr.getClass().getEnchantment(ptr); | ||||
|         const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().search(enchantmentId); | ||||
|         if (!enchantment) | ||||
|         { | ||||
|             Log(Debug::Warning) << "Warning: Can't find enchantment '" << enchantmentId << "' on item " << ptr.getCellRef().getRefId(); | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         if (enchantment->mData.mType == ESM::Enchantment::WhenUsed | ||||
|                 || enchantment->mData.mType == ESM::Enchantment::WhenStrikes) | ||||
|             mRechargingItems.emplace_back(ptr.getBase(), static_cast<float>(enchantment->mData.mCharge)); | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -118,6 +118,16 @@ namespace MWWorld | |||
|             /// Repopulate mMergedRefs.
 | ||||
|             void updateMergedRefs(); | ||||
| 
 | ||||
|             // (item, max charge)
 | ||||
|             typedef std::vector<std::pair<LiveCellRefBase*, float> > TRechargingItems; | ||||
|             TRechargingItems mRechargingItems; | ||||
| 
 | ||||
|             bool mRechargingItemsUpToDate; | ||||
| 
 | ||||
|             void updateRechargingItems(); | ||||
|             void rechargeItems(float duration); | ||||
|             void checkItem(Ptr ptr); | ||||
| 
 | ||||
|             // helper function for forEachInternal
 | ||||
|             template<class Visitor, class List> | ||||
|             bool forEachImp (Visitor& visitor, List& list) | ||||
|  | @ -184,6 +194,7 @@ namespace MWWorld | |||
|             MWWorld::Ptr moveTo(const MWWorld::Ptr& object, MWWorld::CellStore* cellToMoveTo); | ||||
| 
 | ||||
|             void rest(double hours); | ||||
|             void recharge(float duration); | ||||
| 
 | ||||
|             /// Make a copy of the given object and insert it into this cell.
 | ||||
|             /// @note If you get a linker error here, this means the given type can not be inserted into a cell.
 | ||||
|  |  | |||
|  | @ -13,6 +13,7 @@ | |||
| #include "../mwmechanics/creaturestats.hpp" | ||||
| #include "../mwmechanics/levelledlist.hpp" | ||||
| #include "../mwmechanics/actorutil.hpp" | ||||
| #include "../mwmechanics/recharge.hpp" | ||||
| 
 | ||||
| #include "manualref.hpp" | ||||
| #include "refdata.hpp" | ||||
|  | @ -114,7 +115,11 @@ void MWWorld::ContainerStore::storeStates (const CellRefList<T>& collection, | |||
| 
 | ||||
| const std::string MWWorld::ContainerStore::sGoldId = "gold_001"; | ||||
| 
 | ||||
| MWWorld::ContainerStore::ContainerStore() : mListener(nullptr), mCachedWeight (0), mWeightUpToDate (false) {} | ||||
| MWWorld::ContainerStore::ContainerStore() | ||||
|     : mListener(nullptr) | ||||
|     , mRechargingItemsUpToDate(false) | ||||
|     , mCachedWeight (0) | ||||
|     , mWeightUpToDate (false) {} | ||||
| 
 | ||||
| MWWorld::ContainerStore::~ContainerStore() {} | ||||
| 
 | ||||
|  | @ -392,6 +397,46 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::addNewStack (const Cons | |||
|     return it; | ||||
| } | ||||
| 
 | ||||
| void MWWorld::ContainerStore::rechargeItems(float duration) | ||||
| { | ||||
|     if (!mRechargingItemsUpToDate) | ||||
|     { | ||||
|         updateRechargingItems(); | ||||
|         mRechargingItemsUpToDate = true; | ||||
|     } | ||||
|     for (TRechargingItems::iterator it = mRechargingItems.begin(); it != mRechargingItems.end(); ++it) | ||||
|     { | ||||
|         if (!MWMechanics::rechargeItem(*it->first, it->second, duration)) | ||||
|             continue; | ||||
| 
 | ||||
|         // attempt to restack when fully recharged
 | ||||
|         if (it->first->getCellRef().getEnchantmentCharge() == it->second) | ||||
|             it->first = restack(*it->first); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void MWWorld::ContainerStore::updateRechargingItems() | ||||
| { | ||||
|     mRechargingItems.clear(); | ||||
|     for (ContainerStoreIterator it = begin(); it != end(); ++it) | ||||
|     { | ||||
|         const std::string& enchantmentId = it->getClass().getEnchantment(*it); | ||||
|         if (!enchantmentId.empty()) | ||||
|         { | ||||
|             const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().search(enchantmentId); | ||||
|             if (!enchantment) | ||||
|             { | ||||
|                 Log(Debug::Warning) << "Warning: Can't find enchantment '" << enchantmentId << "' on item " << it->getCellRef().getRefId(); | ||||
|                 continue; | ||||
|             } | ||||
| 
 | ||||
|             if (enchantment->mData.mType == ESM::Enchantment::WhenUsed | ||||
|                     || enchantment->mData.mType == ESM::Enchantment::WhenStrikes) | ||||
|                 mRechargingItems.emplace_back(it, static_cast<float>(enchantment->mData.mCharge)); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| int MWWorld::ContainerStore::remove(const std::string& itemId, int count, const Ptr& actor) | ||||
| { | ||||
|     int toRemove = count; | ||||
|  | @ -617,6 +662,7 @@ void MWWorld::ContainerStore::clear() | |||
| void MWWorld::ContainerStore::flagAsModified() | ||||
| { | ||||
|     mWeightUpToDate = false; | ||||
|     mRechargingItemsUpToDate = false; | ||||
| } | ||||
| 
 | ||||
| float MWWorld::ContainerStore::getWeight() const | ||||
|  |  | |||
|  | @ -71,6 +71,12 @@ namespace MWWorld | |||
|         protected: | ||||
|             ContainerStoreListener* mListener; | ||||
| 
 | ||||
|             // (item, max charge)
 | ||||
|             typedef std::vector<std::pair<ContainerStoreIterator, float> > TRechargingItems; | ||||
|             TRechargingItems mRechargingItems; | ||||
| 
 | ||||
|             bool mRechargingItemsUpToDate; | ||||
| 
 | ||||
|         private: | ||||
| 
 | ||||
|             MWWorld::CellRefList<ESM::Potion>            potions; | ||||
|  | @ -108,6 +114,7 @@ namespace MWWorld | |||
|                 ESM::InventoryState& inventory, int& index, | ||||
|                 bool equipable = false) const; | ||||
| 
 | ||||
|             void updateRechargingItems(); | ||||
| 
 | ||||
|             virtual void storeEquipmentState (const MWWorld::LiveCellRefBase& ref, int index, ESM::InventoryState& inventory) const; | ||||
| 
 | ||||
|  | @ -156,6 +163,9 @@ namespace MWWorld | |||
|             ///
 | ||||
|             /// @return the number of items actually removed
 | ||||
| 
 | ||||
|             void rechargeItems (float duration); | ||||
|             ///< Restore charge on enchanted items. Note this should only be done for the player.
 | ||||
| 
 | ||||
|             ContainerStoreIterator unstack (const Ptr& ptr, const Ptr& container, int count = 1); | ||||
|             ///< Unstack an item in this container. The item's count will be set to count, then a new stack will be added with (origCount-count).
 | ||||
|             ///
 | ||||
|  |  | |||
|  | @ -101,7 +101,6 @@ MWWorld::InventoryStore::InventoryStore() | |||
|  , mUpdatesEnabled (true) | ||||
|  , mFirstAutoEquip(true) | ||||
|  , mSelectedEnchantItem(end()) | ||||
|  , mRechargingItemsUpToDate(false) | ||||
| { | ||||
|     initSlots (mSlots); | ||||
| } | ||||
|  | @ -114,7 +113,6 @@ MWWorld::InventoryStore::InventoryStore (const InventoryStore& store) | |||
|  , mFirstAutoEquip(store.mFirstAutoEquip) | ||||
|  , mPermanentMagicEffectMagnitudes(store.mPermanentMagicEffectMagnitudes) | ||||
|  , mSelectedEnchantItem(end()) | ||||
|  , mRechargingItemsUpToDate(false) | ||||
| { | ||||
|     copySlots (store); | ||||
| } | ||||
|  | @ -709,12 +707,6 @@ void MWWorld::InventoryStore::updateMagicEffects(const Ptr& actor) | |||
|     mFirstAutoEquip = false; | ||||
| } | ||||
| 
 | ||||
| void MWWorld::InventoryStore::flagAsModified() | ||||
| { | ||||
|     ContainerStore::flagAsModified(); | ||||
|     mRechargingItemsUpToDate = false; | ||||
| } | ||||
| 
 | ||||
| bool MWWorld::InventoryStore::stacks(const ConstPtr& ptr1, const ConstPtr& ptr2) const | ||||
| { | ||||
|     bool canStack = MWWorld::ContainerStore::stacks(ptr1, ptr2); | ||||
|  | @ -957,57 +949,6 @@ void MWWorld::InventoryStore::visitEffectSources(MWMechanics::EffectSourceVisito | |||
|     } | ||||
| } | ||||
| 
 | ||||
| void MWWorld::InventoryStore::updateRechargingItems() | ||||
| { | ||||
|     mRechargingItems.clear(); | ||||
|     for (ContainerStoreIterator it = begin(); it != end(); ++it) | ||||
|     { | ||||
|         if (it->getClass().getEnchantment(*it) != "") | ||||
|         { | ||||
|             std::string enchantmentId = it->getClass().getEnchantment(*it); | ||||
|             const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().search( | ||||
|                         enchantmentId); | ||||
|             if (!enchantment) | ||||
|             { | ||||
|                 Log(Debug::Warning) << "Warning: Can't find enchantment '" << enchantmentId << "' on item " << it->getCellRef().getRefId(); | ||||
|                 continue; | ||||
|             } | ||||
| 
 | ||||
|             if (enchantment->mData.mType == ESM::Enchantment::WhenUsed | ||||
|                     || enchantment->mData.mType == ESM::Enchantment::WhenStrikes) | ||||
|                 mRechargingItems.push_back(std::make_pair(it, static_cast<float>(enchantment->mData.mCharge))); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void MWWorld::InventoryStore::rechargeItems(float duration) | ||||
| { | ||||
|     if (!mRechargingItemsUpToDate) | ||||
|     { | ||||
|         updateRechargingItems(); | ||||
|         mRechargingItemsUpToDate = true; | ||||
|     } | ||||
|     for (TRechargingItems::iterator it = mRechargingItems.begin(); it != mRechargingItems.end(); ++it) | ||||
|     { | ||||
|         if (it->first->getCellRef().getEnchantmentCharge() == -1 | ||||
|                 || it->first->getCellRef().getEnchantmentCharge() == it->second) | ||||
|             continue; | ||||
| 
 | ||||
|         static float fMagicItemRechargePerSecond = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find( | ||||
|                     "fMagicItemRechargePerSecond")->mValue.getFloat(); | ||||
| 
 | ||||
|         if (it->first->getCellRef().getEnchantmentCharge() <= it->second) | ||||
|         { | ||||
|             it->first->getCellRef().setEnchantmentCharge(std::min (it->first->getCellRef().getEnchantmentCharge() + fMagicItemRechargePerSecond * duration, | ||||
|                                                                   it->second)); | ||||
| 
 | ||||
|             // attempt to restack when fully recharged
 | ||||
|             if (it->first->getCellRef().getEnchantmentCharge() == it->second) | ||||
|                 it->first = restack(*it->first); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void MWWorld::InventoryStore::purgeEffect(short effectId) | ||||
| { | ||||
|     for (TSlots::const_iterator it = mSlots.begin(); it != mSlots.end(); ++it) | ||||
|  |  | |||
|  | @ -101,18 +101,11 @@ namespace MWWorld | |||
|             // selected magic item (for using enchantments of type "Cast once" or "Cast when used")
 | ||||
|             ContainerStoreIterator mSelectedEnchantItem; | ||||
| 
 | ||||
|             // (item, max charge)
 | ||||
|             typedef std::vector<std::pair<ContainerStoreIterator, float> > TRechargingItems; | ||||
|             TRechargingItems mRechargingItems; | ||||
| 
 | ||||
|             bool mRechargingItemsUpToDate; | ||||
| 
 | ||||
|             void copySlots (const InventoryStore& store); | ||||
| 
 | ||||
|             void initSlots (TSlots& slots_); | ||||
| 
 | ||||
|             void updateMagicEffects(const Ptr& actor); | ||||
|             void updateRechargingItems(); | ||||
| 
 | ||||
|             void fireEquipmentChangedEvent(const Ptr& actor); | ||||
| 
 | ||||
|  | @ -171,10 +164,6 @@ namespace MWWorld | |||
|             const MWMechanics::MagicEffects& getMagicEffects() const; | ||||
|             ///< Return magic effects from worn items.
 | ||||
| 
 | ||||
|             virtual void flagAsModified(); | ||||
|             ///< \attention This function is internal to the world model and should not be called from
 | ||||
|             /// outside.
 | ||||
| 
 | ||||
|             virtual bool stacks (const ConstPtr& ptr1, const ConstPtr& ptr2) const; | ||||
|             ///< @return true if the two specified objects can stack with each other
 | ||||
| 
 | ||||
|  | @ -216,9 +205,6 @@ namespace MWWorld | |||
| 
 | ||||
|             void visitEffectSources (MWMechanics::EffectSourceVisitor& visitor); | ||||
| 
 | ||||
|             void rechargeItems (float duration); | ||||
|             ///< Restore charge on enchanted items. Note this should only be done for the player.
 | ||||
| 
 | ||||
|             void purgeEffect (short effectId); | ||||
|             ///< Remove a magic effect
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -857,7 +857,17 @@ namespace MWWorld | |||
| 
 | ||||
|     void World::advanceTime (double hours, bool incremental) | ||||
|     { | ||||
|         MWBase::Environment::get().getMechanicsManager()->advanceTime(static_cast<float>(hours * 3600)); | ||||
|         if (!incremental) | ||||
|         { | ||||
|             // When we fast-forward time, we should recharge magic items
 | ||||
|             // in all loaded cells, using game world time
 | ||||
|             float duration = hours * 3600; | ||||
|             const float timeScaleFactor = getTimeScaleFactor(); | ||||
|             if (timeScaleFactor != 0.0f) | ||||
|                 duration /= timeScaleFactor; | ||||
| 
 | ||||
|             rechargeItems(duration, false); | ||||
|         } | ||||
| 
 | ||||
|         mWeatherManager->advanceTime (hours, incremental); | ||||
| 
 | ||||
|  | @ -3305,7 +3315,6 @@ namespace MWWorld | |||
|                 closestDistance = distance; | ||||
|                 closestMarker = marker; | ||||
|             } | ||||
| 
 | ||||
|         } | ||||
| 
 | ||||
|         return closestMarker; | ||||
|  | @ -3316,6 +3325,22 @@ namespace MWWorld | |||
|         mCells.rest(hours); | ||||
|     } | ||||
| 
 | ||||
|     void World::rechargeItems(double duration, bool activeOnly) | ||||
|     { | ||||
|         MWWorld::Ptr player = getPlayerPtr(); | ||||
|         player.getClass().getInventoryStore(player).rechargeItems(duration); | ||||
| 
 | ||||
|         if (activeOnly) | ||||
|         { | ||||
|             for (auto &cell : mWorldScene->getActiveCells()) | ||||
|             { | ||||
|                 cell->recharge(duration); | ||||
|             } | ||||
|         } | ||||
|         else | ||||
|             mCells.recharge(duration); | ||||
|     } | ||||
| 
 | ||||
|     void World::teleportToClosestMarker (const MWWorld::Ptr& ptr, | ||||
|                                           const std::string& id) | ||||
|     { | ||||
|  |  | |||
|  | @ -574,6 +574,7 @@ namespace MWWorld | |||
|             ///< check if the player is allowed to rest
 | ||||
| 
 | ||||
|             void rest(double hours) override; | ||||
|             void rechargeItems(double duration, bool activeOnly) override; | ||||
| 
 | ||||
|             /// \todo Probably shouldn't be here
 | ||||
|             MWRender::Animation* getAnimation(const MWWorld::Ptr &ptr) override; | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue