From c51aba0b13e2ddd44ad9d9c0ec3d34c54ea2d1b3 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 15 Dec 2018 10:23:50 +0400 Subject: [PATCH] Recharge items outside of player's inventory (bug #4077) --- CHANGELOG.md | 1 + apps/openmw/CMakeLists.txt | 2 +- apps/openmw/engine.cpp | 1 + apps/openmw/mwbase/mechanicsmanager.hpp | 2 - apps/openmw/mwbase/world.hpp | 1 + apps/openmw/mwgui/recharge.cpp | 56 +-------- .../mwmechanics/mechanicsmanagerimp.cpp | 10 -- .../mwmechanics/mechanicsmanagerimp.hpp | 2 - apps/openmw/mwmechanics/recharge.cpp | 92 +++++++++++++++ apps/openmw/mwmechanics/recharge.hpp | 15 +++ apps/openmw/mwworld/cells.cpp | 13 +++ apps/openmw/mwworld/cells.hpp | 2 + apps/openmw/mwworld/cellstore.cpp | 109 +++++++++++++++++- apps/openmw/mwworld/cellstore.hpp | 11 ++ apps/openmw/mwworld/containerstore.cpp | 48 +++++++- apps/openmw/mwworld/containerstore.hpp | 10 ++ apps/openmw/mwworld/inventorystore.cpp | 59 ---------- apps/openmw/mwworld/inventorystore.hpp | 14 --- apps/openmw/mwworld/worldimp.cpp | 29 ++++- apps/openmw/mwworld/worldimp.hpp | 1 + 20 files changed, 332 insertions(+), 146 deletions(-) create mode 100644 apps/openmw/mwmechanics/recharge.cpp create mode 100644 apps/openmw/mwmechanics/recharge.hpp diff --git a/CHANGELOG.md b/CHANGELOG.md index 47e5bb485..ef908284b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,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 diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 5c893845d..55d6b1af9 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -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 ) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 7c92236ed..4d8d47d51 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -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(); diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index ae898b24e..411f5fab1 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -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. diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 261f29608..4df175b89 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -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; diff --git a/apps/openmw/mwgui/recharge.cpp b/apps/openmw/mwgui/recharge.cpp index 63da6092e..f8d89c0cb 100644 --- a/apps/openmw/mwgui/recharge.cpp +++ b/apps/openmw/mwgui/recharge.cpp @@ -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(); - - 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().find(soul); - - float restored = creature->mData.mSoul * (roll / x); - - const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get().find( - item.getClass().getEnchantment(item)); - item.getCellRef().setEnchantmentCharge( - std::min(item.getCellRef().getEnchantmentCharge() + restored, static_cast(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().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()->mBase->mId, "Misc_SoulGem_Azura")) - player.getClass().getContainerStore(player).add("Misc_SoulGem_Azura", 1, player); - } - updateView(); } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index a1ba0a5e7..695abe105 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -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()) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index 0ac9092e8..640bd3bdd 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -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. diff --git a/apps/openmw/mwmechanics/recharge.cpp b/apps/openmw/mwmechanics/recharge.cpp new file mode 100644 index 000000000..51c78e1e3 --- /dev/null +++ b/apps/openmw/mwmechanics/recharge.cpp @@ -0,0 +1,92 @@ +#include "recharge.hpp" + +#include + +#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().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().find(soul); + + float restored = creature->mData.mSoul * (roll / x); + + const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get().find( + item.getClass().getEnchantment(item)); + item.getCellRef().setEnchantmentCharge( + std::min(item.getCellRef().getEnchantmentCharge() + restored, static_cast(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().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()->mBase->mId, "Misc_SoulGem_Azura")) + player.getClass().getContainerStore(player).add("Misc_SoulGem_Azura", 1, player); + } + + return true; +} + +} diff --git a/apps/openmw/mwmechanics/recharge.hpp b/apps/openmw/mwmechanics/recharge.hpp new file mode 100644 index 000000000..913f2109b --- /dev/null +++ b/apps/openmw/mwmechanics/recharge.hpp @@ -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 diff --git a/apps/openmw/mwworld/cells.cpp b/apps/openmw/mwworld/cells.cpp index bea5825da..a724c9bdb 100644 --- a/apps/openmw/mwworld/cells.cpp +++ b/apps/openmw/mwworld/cells.cpp @@ -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) diff --git a/apps/openmw/mwworld/cells.hpp b/apps/openmw/mwworld/cells.hpp index dce42d996..4a6d7abf6 100644 --- a/apps/openmw/mwworld/cells.hpp +++ b/apps/openmw/mwworld/cells.hpp @@ -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. diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index b3264fb7a..3d9c24009 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -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& 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::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::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::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::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::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::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::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().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(enchantment->mData.mCharge)); + } } diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index baecfb5ea..4872625c1 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -118,6 +118,16 @@ namespace MWWorld /// Repopulate mMergedRefs. void updateMergedRefs(); + // (item, max charge) + typedef std::vector > TRechargingItems; + TRechargingItems mRechargingItems; + + bool mRechargingItemsUpToDate; + + void updateRechargingItems(); + void rechargeItems(float duration); + void checkItem(Ptr ptr); + // helper function for forEachInternal template 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. diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 76555de02..60aa97da2 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -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& 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() {} @@ -408,6 +413,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().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(enchantment->mData.mCharge)); + } + } +} + int MWWorld::ContainerStore::remove(const std::string& itemId, int count, const Ptr& actor) { int toRemove = count; @@ -633,6 +678,7 @@ void MWWorld::ContainerStore::clear() void MWWorld::ContainerStore::flagAsModified() { mWeightUpToDate = false; + mRechargingItemsUpToDate = false; } float MWWorld::ContainerStore::getWeight() const diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index 1e1e4a844..9ee4eef7f 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -71,6 +71,12 @@ namespace MWWorld protected: ContainerStoreListener* mListener; + // (item, max charge) + typedef std::vector > TRechargingItems; + TRechargingItems mRechargingItems; + + bool mRechargingItemsUpToDate; + private: MWWorld::CellRefList 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). /// diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index 65b1186aa..79b41a795 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -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().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(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().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) diff --git a/apps/openmw/mwworld/inventorystore.hpp b/apps/openmw/mwworld/inventorystore.hpp index e99a99bfa..11524b1a8 100644 --- a/apps/openmw/mwworld/inventorystore.hpp +++ b/apps/openmw/mwworld/inventorystore.hpp @@ -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 > 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 diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index c914acfba..42cd1c6dc 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -857,7 +857,17 @@ namespace MWWorld void World::advanceTime (double hours, bool incremental) { - MWBase::Environment::get().getMechanicsManager()->advanceTime(static_cast(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) { diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index f803079fb..e67ddbb72 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -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;