mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-03-30 06:36: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 #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 #3812: Wrong multiline tooltips width when word-wrapping is enabled
|
||||||
Bug #3894: Hostile spell effects not detected/present on first frame of OnPCHitMe
|
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 #4202: Open .omwaddon files without needing toopen openmw-cs first
|
||||||
Bug #4240: Ash storm origin coordinates and hand shielding animation behavior are incorrect
|
Bug #4240: Ash storm origin coordinates and hand shielding animation behavior are incorrect
|
||||||
Bug #4270: Closing doors while they are obstructed desyncs closing sfx
|
Bug #4270: Closing doors while they are obstructed desyncs closing sfx
|
||||||
|
|
|
@ -81,7 +81,7 @@ add_openmw_dir (mwclass
|
||||||
add_openmw_dir (mwmechanics
|
add_openmw_dir (mwmechanics
|
||||||
mechanicsmanagerimp stat creaturestats magiceffects movement actorutil
|
mechanicsmanagerimp stat creaturestats magiceffects movement actorutil
|
||||||
drawstate spells activespells npcstats aipackage aisequence aipursue alchemy aiwander aitravel aifollow aiavoiddoor aibreathe
|
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
|
disease pickpocket levelledlist combat steering obstacle autocalcspell difficultyscaling aicombataction actor summoning
|
||||||
character actors objects aistate coordinateconverter trading weaponpriority spellpriority weapontype
|
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;
|
double hours = (frametime * mEnvironment.getWorld()->getTimeScaleFactor()) / 3600.0;
|
||||||
mEnvironment.getWorld()->advanceTime(hours, true);
|
mEnvironment.getWorld()->advanceTime(hours, true);
|
||||||
|
mEnvironment.getWorld()->rechargeItems(frametime, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
osg::Timer_t afterScriptTick = osg::Timer::instance()->tick();
|
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
|
/// \param paused In game type does not currently advance (this usually means some GUI
|
||||||
/// component is up).
|
/// component is up).
|
||||||
|
|
||||||
virtual void advanceTime (float duration) = 0;
|
|
||||||
|
|
||||||
virtual void setPlayerName (const std::string& name) = 0;
|
virtual void setPlayerName (const std::string& name) = 0;
|
||||||
///< Set player name.
|
///< Set player name.
|
||||||
|
|
||||||
|
|
|
@ -584,6 +584,7 @@ namespace MWBase
|
||||||
virtual bool isPlayerInJail() const = 0;
|
virtual bool isPlayerInJail() const = 0;
|
||||||
|
|
||||||
virtual void rest(double hours) = 0;
|
virtual void rest(double hours) = 0;
|
||||||
|
virtual void rechargeItems(double duration, bool activeOnly) = 0;
|
||||||
|
|
||||||
virtual void setPlayerTraveling(bool traveling) = 0;
|
virtual void setPlayerTraveling(bool traveling) = 0;
|
||||||
virtual bool isPlayerTraveling() const = 0;
|
virtual bool isPlayerTraveling() const = 0;
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
|
|
||||||
#include "../mwmechanics/creaturestats.hpp"
|
#include "../mwmechanics/creaturestats.hpp"
|
||||||
#include "../mwmechanics/actorutil.hpp"
|
#include "../mwmechanics/actorutil.hpp"
|
||||||
|
#include "../mwmechanics/recharge.hpp"
|
||||||
|
|
||||||
#include "itemwidget.hpp"
|
#include "itemwidget.hpp"
|
||||||
#include "itemchargeview.hpp"
|
#include "itemchargeview.hpp"
|
||||||
|
@ -130,62 +131,9 @@ void Recharge::onItemCancel()
|
||||||
void Recharge::onItemClicked(MyGUI::Widget *sender, const MWWorld::Ptr& item)
|
void Recharge::onItemClicked(MyGUI::Widget *sender, const MWWorld::Ptr& item)
|
||||||
{
|
{
|
||||||
MWWorld::Ptr gem = *mGemIcon->getUserData<MWWorld::Ptr>();
|
MWWorld::Ptr gem = *mGemIcon->getUserData<MWWorld::Ptr>();
|
||||||
|
if (!MWMechanics::rechargeItem(item, gem))
|
||||||
if (!gem.getRefData().getCount())
|
|
||||||
return;
|
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();
|
updateView();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -287,16 +287,6 @@ namespace MWMechanics
|
||||||
mWatched = ptr;
|
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)
|
void MechanicsManager::update(float duration, bool paused)
|
||||||
{
|
{
|
||||||
if(!mWatched.isEmpty())
|
if(!mWatched.isEmpty())
|
||||||
|
|
|
@ -78,8 +78,6 @@ namespace MWMechanics
|
||||||
/// \param paused In game type does not currently advance (this usually means some GUI
|
/// \param paused In game type does not currently advance (this usually means some GUI
|
||||||
/// component is up).
|
/// component is up).
|
||||||
|
|
||||||
virtual void advanceTime (float duration) override;
|
|
||||||
|
|
||||||
virtual void setPlayerName (const std::string& name) override;
|
virtual void setPlayerName (const std::string& name) override;
|
||||||
///< Set player name.
|
///< 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)
|
MWWorld::CellStore *MWWorld::Cells::getCell (const ESM::CellId& id)
|
||||||
{
|
{
|
||||||
if (id.mPaged)
|
if (id.mPaged)
|
||||||
|
|
|
@ -61,7 +61,9 @@ namespace MWWorld
|
||||||
|
|
||||||
/// @note name must be lower case
|
/// @note name must be lower case
|
||||||
Ptr getPtr (const std::string& name);
|
Ptr getPtr (const std::string& name);
|
||||||
|
|
||||||
void rest (double hours);
|
void rest (double hours);
|
||||||
|
void recharge (float duration);
|
||||||
|
|
||||||
/// Get all Ptrs referencing \a name in exterior cells
|
/// Get all Ptrs referencing \a name in exterior cells
|
||||||
/// @note Due to the current implementation of getPtr this only supports one Ptr per cell.
|
/// @note Due to the current implementation of getPtr this only supports one Ptr per cell.
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include "../mwbase/world.hpp"
|
#include "../mwbase/world.hpp"
|
||||||
|
|
||||||
#include "../mwmechanics/creaturestats.hpp"
|
#include "../mwmechanics/creaturestats.hpp"
|
||||||
|
#include "../mwmechanics/recharge.hpp"
|
||||||
|
|
||||||
#include "ptr.hpp"
|
#include "ptr.hpp"
|
||||||
#include "esmstore.hpp"
|
#include "esmstore.hpp"
|
||||||
|
@ -328,6 +329,7 @@ namespace MWWorld
|
||||||
void CellStore::updateMergedRefs()
|
void CellStore::updateMergedRefs()
|
||||||
{
|
{
|
||||||
mMergedRefs.clear();
|
mMergedRefs.clear();
|
||||||
|
mRechargingItemsUpToDate = false;
|
||||||
MergeVisitor visitor(mMergedRefs, mMovedHere, mMovedToAnotherCell);
|
MergeVisitor visitor(mMergedRefs, mMovedHere, mMovedToAnotherCell);
|
||||||
forEachInternal(visitor);
|
forEachInternal(visitor);
|
||||||
visitor.merge();
|
visitor.merge();
|
||||||
|
@ -345,7 +347,7 @@ namespace MWWorld
|
||||||
}
|
}
|
||||||
|
|
||||||
CellStore::CellStore (const ESM::Cell *cell, const MWWorld::ESMStore& esmStore, std::vector<ESM::ESMReader>& readerList)
|
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;
|
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()
|
void CellStore::respawn()
|
||||||
{
|
{
|
||||||
if (mState == State_Loaded)
|
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.
|
/// Repopulate mMergedRefs.
|
||||||
void updateMergedRefs();
|
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
|
// helper function for forEachInternal
|
||||||
template<class Visitor, class List>
|
template<class Visitor, class List>
|
||||||
bool forEachImp (Visitor& visitor, List& list)
|
bool forEachImp (Visitor& visitor, List& list)
|
||||||
|
@ -184,6 +194,7 @@ namespace MWWorld
|
||||||
MWWorld::Ptr moveTo(const MWWorld::Ptr& object, MWWorld::CellStore* cellToMoveTo);
|
MWWorld::Ptr moveTo(const MWWorld::Ptr& object, MWWorld::CellStore* cellToMoveTo);
|
||||||
|
|
||||||
void rest(double hours);
|
void rest(double hours);
|
||||||
|
void recharge(float duration);
|
||||||
|
|
||||||
/// Make a copy of the given object and insert it into this cell.
|
/// 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.
|
/// @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/creaturestats.hpp"
|
||||||
#include "../mwmechanics/levelledlist.hpp"
|
#include "../mwmechanics/levelledlist.hpp"
|
||||||
#include "../mwmechanics/actorutil.hpp"
|
#include "../mwmechanics/actorutil.hpp"
|
||||||
|
#include "../mwmechanics/recharge.hpp"
|
||||||
|
|
||||||
#include "manualref.hpp"
|
#include "manualref.hpp"
|
||||||
#include "refdata.hpp"
|
#include "refdata.hpp"
|
||||||
|
@ -114,7 +115,11 @@ void MWWorld::ContainerStore::storeStates (const CellRefList<T>& collection,
|
||||||
|
|
||||||
const std::string MWWorld::ContainerStore::sGoldId = "gold_001";
|
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() {}
|
MWWorld::ContainerStore::~ContainerStore() {}
|
||||||
|
|
||||||
|
@ -392,6 +397,46 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::addNewStack (const Cons
|
||||||
return it;
|
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 MWWorld::ContainerStore::remove(const std::string& itemId, int count, const Ptr& actor)
|
||||||
{
|
{
|
||||||
int toRemove = count;
|
int toRemove = count;
|
||||||
|
@ -617,6 +662,7 @@ void MWWorld::ContainerStore::clear()
|
||||||
void MWWorld::ContainerStore::flagAsModified()
|
void MWWorld::ContainerStore::flagAsModified()
|
||||||
{
|
{
|
||||||
mWeightUpToDate = false;
|
mWeightUpToDate = false;
|
||||||
|
mRechargingItemsUpToDate = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
float MWWorld::ContainerStore::getWeight() const
|
float MWWorld::ContainerStore::getWeight() const
|
||||||
|
|
|
@ -71,6 +71,12 @@ namespace MWWorld
|
||||||
protected:
|
protected:
|
||||||
ContainerStoreListener* mListener;
|
ContainerStoreListener* mListener;
|
||||||
|
|
||||||
|
// (item, max charge)
|
||||||
|
typedef std::vector<std::pair<ContainerStoreIterator, float> > TRechargingItems;
|
||||||
|
TRechargingItems mRechargingItems;
|
||||||
|
|
||||||
|
bool mRechargingItemsUpToDate;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
MWWorld::CellRefList<ESM::Potion> potions;
|
MWWorld::CellRefList<ESM::Potion> potions;
|
||||||
|
@ -108,6 +114,7 @@ namespace MWWorld
|
||||||
ESM::InventoryState& inventory, int& index,
|
ESM::InventoryState& inventory, int& index,
|
||||||
bool equipable = false) const;
|
bool equipable = false) const;
|
||||||
|
|
||||||
|
void updateRechargingItems();
|
||||||
|
|
||||||
virtual void storeEquipmentState (const MWWorld::LiveCellRefBase& ref, int index, ESM::InventoryState& inventory) const;
|
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
|
/// @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);
|
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).
|
///< 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)
|
, mUpdatesEnabled (true)
|
||||||
, mFirstAutoEquip(true)
|
, mFirstAutoEquip(true)
|
||||||
, mSelectedEnchantItem(end())
|
, mSelectedEnchantItem(end())
|
||||||
, mRechargingItemsUpToDate(false)
|
|
||||||
{
|
{
|
||||||
initSlots (mSlots);
|
initSlots (mSlots);
|
||||||
}
|
}
|
||||||
|
@ -114,7 +113,6 @@ MWWorld::InventoryStore::InventoryStore (const InventoryStore& store)
|
||||||
, mFirstAutoEquip(store.mFirstAutoEquip)
|
, mFirstAutoEquip(store.mFirstAutoEquip)
|
||||||
, mPermanentMagicEffectMagnitudes(store.mPermanentMagicEffectMagnitudes)
|
, mPermanentMagicEffectMagnitudes(store.mPermanentMagicEffectMagnitudes)
|
||||||
, mSelectedEnchantItem(end())
|
, mSelectedEnchantItem(end())
|
||||||
, mRechargingItemsUpToDate(false)
|
|
||||||
{
|
{
|
||||||
copySlots (store);
|
copySlots (store);
|
||||||
}
|
}
|
||||||
|
@ -709,12 +707,6 @@ void MWWorld::InventoryStore::updateMagicEffects(const Ptr& actor)
|
||||||
mFirstAutoEquip = false;
|
mFirstAutoEquip = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MWWorld::InventoryStore::flagAsModified()
|
|
||||||
{
|
|
||||||
ContainerStore::flagAsModified();
|
|
||||||
mRechargingItemsUpToDate = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MWWorld::InventoryStore::stacks(const ConstPtr& ptr1, const ConstPtr& ptr2) const
|
bool MWWorld::InventoryStore::stacks(const ConstPtr& ptr1, const ConstPtr& ptr2) const
|
||||||
{
|
{
|
||||||
bool canStack = MWWorld::ContainerStore::stacks(ptr1, ptr2);
|
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)
|
void MWWorld::InventoryStore::purgeEffect(short effectId)
|
||||||
{
|
{
|
||||||
for (TSlots::const_iterator it = mSlots.begin(); it != mSlots.end(); ++it)
|
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")
|
// selected magic item (for using enchantments of type "Cast once" or "Cast when used")
|
||||||
ContainerStoreIterator mSelectedEnchantItem;
|
ContainerStoreIterator mSelectedEnchantItem;
|
||||||
|
|
||||||
// (item, max charge)
|
|
||||||
typedef std::vector<std::pair<ContainerStoreIterator, float> > TRechargingItems;
|
|
||||||
TRechargingItems mRechargingItems;
|
|
||||||
|
|
||||||
bool mRechargingItemsUpToDate;
|
|
||||||
|
|
||||||
void copySlots (const InventoryStore& store);
|
void copySlots (const InventoryStore& store);
|
||||||
|
|
||||||
void initSlots (TSlots& slots_);
|
void initSlots (TSlots& slots_);
|
||||||
|
|
||||||
void updateMagicEffects(const Ptr& actor);
|
void updateMagicEffects(const Ptr& actor);
|
||||||
void updateRechargingItems();
|
|
||||||
|
|
||||||
void fireEquipmentChangedEvent(const Ptr& actor);
|
void fireEquipmentChangedEvent(const Ptr& actor);
|
||||||
|
|
||||||
|
@ -171,10 +164,6 @@ namespace MWWorld
|
||||||
const MWMechanics::MagicEffects& getMagicEffects() const;
|
const MWMechanics::MagicEffects& getMagicEffects() const;
|
||||||
///< Return magic effects from worn items.
|
///< 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;
|
virtual bool stacks (const ConstPtr& ptr1, const ConstPtr& ptr2) const;
|
||||||
///< @return true if the two specified objects can stack with each other
|
///< @return true if the two specified objects can stack with each other
|
||||||
|
|
||||||
|
@ -216,9 +205,6 @@ namespace MWWorld
|
||||||
|
|
||||||
void visitEffectSources (MWMechanics::EffectSourceVisitor& visitor);
|
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);
|
void purgeEffect (short effectId);
|
||||||
///< Remove a magic effect
|
///< Remove a magic effect
|
||||||
|
|
||||||
|
|
|
@ -857,7 +857,17 @@ namespace MWWorld
|
||||||
|
|
||||||
void World::advanceTime (double hours, bool incremental)
|
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);
|
mWeatherManager->advanceTime (hours, incremental);
|
||||||
|
|
||||||
|
@ -3305,7 +3315,6 @@ namespace MWWorld
|
||||||
closestDistance = distance;
|
closestDistance = distance;
|
||||||
closestMarker = marker;
|
closestMarker = marker;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return closestMarker;
|
return closestMarker;
|
||||||
|
@ -3316,6 +3325,22 @@ namespace MWWorld
|
||||||
mCells.rest(hours);
|
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,
|
void World::teleportToClosestMarker (const MWWorld::Ptr& ptr,
|
||||||
const std::string& id)
|
const std::string& id)
|
||||||
{
|
{
|
||||||
|
|
|
@ -574,6 +574,7 @@ namespace MWWorld
|
||||||
///< check if the player is allowed to rest
|
///< check if the player is allowed to rest
|
||||||
|
|
||||||
void rest(double hours) override;
|
void rest(double hours) override;
|
||||||
|
void rechargeItems(double duration, bool activeOnly) override;
|
||||||
|
|
||||||
/// \todo Probably shouldn't be here
|
/// \todo Probably shouldn't be here
|
||||||
MWRender::Animation* getAnimation(const MWWorld::Ptr &ptr) override;
|
MWRender::Animation* getAnimation(const MWWorld::Ptr &ptr) override;
|
||||||
|
|
Loading…
Reference in a new issue