#include "activespells.hpp" #include #include #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwworld/esmstore.hpp" namespace MWMechanics { void ActiveSpells::update() const { bool rebuild = false; MWWorld::TimeStamp now = MWBase::Environment::get().getWorld()->getTimeStamp(); // Erase no longer active spells and effects if (mLastUpdate!=now) { TContainer::iterator iter (mSpells.begin()); while (iter!=mSpells.end()) { if (!timeToExpire (iter)) { mSpells.erase (iter++); rebuild = true; } else { std::vector& effects = iter->second.mEffects; for (std::vector::iterator effectIt = effects.begin(); effectIt != effects.end();) { MWWorld::TimeStamp start = iter->second.mTimeStamp; MWWorld::TimeStamp end = start + static_cast(effectIt->mDuration)*MWBase::Environment::get().getWorld()->getTimeScaleFactor()/(60*60); if (end <= now) { effectIt = effects.erase(effectIt); rebuild = true; } else ++effectIt; } ++iter; } } mLastUpdate = now; } if (mSpellsChanged) { mSpellsChanged = false; rebuild = true; } if (rebuild) rebuildEffects(); } void ActiveSpells::rebuildEffects() const { MWWorld::TimeStamp now = MWBase::Environment::get().getWorld()->getTimeStamp(); mEffects = MagicEffects(); for (TIterator iter (begin()); iter!=end(); ++iter) { const MWWorld::TimeStamp& start = iter->second.mTimeStamp; const std::vector& effects = iter->second.mEffects; for (std::vector::const_iterator effectIt = effects.begin(); effectIt != effects.end(); ++effectIt) { double duration = effectIt->mDuration; MWWorld::TimeStamp end = start; end += duration * MWBase::Environment::get().getWorld()->getTimeScaleFactor()/(60*60); if (end>now) mEffects.add(MWMechanics::EffectKey(effectIt->mEffectId, effectIt->mArg), MWMechanics::EffectParam(effectIt->mMagnitude)); } } } ActiveSpells::ActiveSpells() : mSpellsChanged (false) , mLastUpdate (MWBase::Environment::get().getWorld()->getTimeStamp()) {} const MagicEffects& ActiveSpells::getMagicEffects() const { update(); return mEffects; } ActiveSpells::TIterator ActiveSpells::begin() const { return mSpells.begin(); } ActiveSpells::TIterator ActiveSpells::end() const { return mSpells.end(); } double ActiveSpells::timeToExpire (const TIterator& iterator) const { const std::vector& effects = iterator->second.mEffects; float duration = 0; for (std::vector::const_iterator iter (effects.begin()); iter!=effects.end(); ++iter) { if (iter->mDuration > duration) duration = iter->mDuration; } double scaledDuration = duration * MWBase::Environment::get().getWorld()->getTimeScaleFactor()/(60*60); double usedUp = MWBase::Environment::get().getWorld()->getTimeStamp() - iterator->second.mTimeStamp; if (usedUp>=scaledDuration) return 0; return scaledDuration-usedUp; } bool ActiveSpells::isSpellActive(const std::string& id) const { for (TContainer::iterator iter = mSpells.begin(); iter != mSpells.end(); ++iter) { if (Misc::StringUtils::ciEqual(iter->first, id)) return true; } return false; } const ActiveSpells::TContainer& ActiveSpells::getActiveSpells() const { return mSpells; } void ActiveSpells::addSpell(const std::string &id, bool stack, std::vector effects, const std::string &displayName, int casterActorId) { TContainer::iterator it(mSpells.find(id)); ActiveSpellParams params; params.mTimeStamp = MWBase::Environment::get().getWorld()->getTimeStamp(); params.mEffects = effects; params.mDisplayName = displayName; params.mCasterActorId = casterActorId; if (it == end() || stack) { mSpells.insert(std::make_pair(id, params)); } else { // addSpell() is called with effects for a range. // but a spell may have effects with different ranges (e.g. Touch & Target) // so, if we see new effects for same spell assume additional // spell effects and add to existing effects of spell mergeEffects(params.mEffects, it->second.mEffects); it->second = params; } mSpellsChanged = true; } void ActiveSpells::mergeEffects(std::vector& addTo, const std::vector& from) { for (std::vector::const_iterator effect(from.begin()); effect != from.end(); ++effect) { // if effect is not in addTo, add it bool missing = true; for (std::vector::const_iterator iter(addTo.begin()); iter != addTo.end(); ++iter) { if ((effect->mEffectId == iter->mEffectId) && (effect->mArg == iter->mArg)) { missing = false; break; } } if (missing) { addTo.push_back(*effect); } } } void ActiveSpells::removeEffects(const std::string &id) { mSpells.erase(Misc::StringUtils::lowerCase(id)); mSpellsChanged = true; } void ActiveSpells::visitEffectSources(EffectSourceVisitor &visitor) const { for (TContainer::const_iterator it = begin(); it != end(); ++it) { float timeScale = MWBase::Environment::get().getWorld()->getTimeScaleFactor(); for (std::vector::const_iterator effectIt = it->second.mEffects.begin(); effectIt != it->second.mEffects.end(); ++effectIt) { std::string name = it->second.mDisplayName; float remainingTime = effectIt->mDuration + static_cast(it->second.mTimeStamp - MWBase::Environment::get().getWorld()->getTimeStamp())*3600/timeScale; float magnitude = effectIt->mMagnitude; if (magnitude) visitor.visit(MWMechanics::EffectKey(effectIt->mEffectId, effectIt->mArg), name, it->first, it->second.mCasterActorId, magnitude, remainingTime, effectIt->mDuration); } } } void ActiveSpells::purgeAll(float chance, bool spellOnly) { for (TContainer::iterator it = mSpells.begin(); it != mSpells.end(); ) { const std::string spellId = it->first; // if spellOnly is true, dispell only spells. Leave potions, enchanted items etc. if (spellOnly) { const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get().search(spellId); if (!spell || spell->mData.mType != ESM::Spell::ST_Spell) { ++it; continue; } } if (Misc::Rng::roll0to99() < chance) mSpells.erase(it++); else ++it; } mSpellsChanged = true; } void ActiveSpells::purgeEffect(short effectId) { for (TContainer::iterator it = mSpells.begin(); it != mSpells.end(); ++it) { for (std::vector::iterator effectIt = it->second.mEffects.begin(); effectIt != it->second.mEffects.end();) { if (effectIt->mEffectId == effectId) effectIt = it->second.mEffects.erase(effectIt); else ++effectIt; } } mSpellsChanged = true; } void ActiveSpells::purgeEffect(short effectId, const std::string& sourceId) { for (TContainer::iterator it = mSpells.begin(); it != mSpells.end(); ++it) { for (std::vector::iterator effectIt = it->second.mEffects.begin(); effectIt != it->second.mEffects.end();) { if (effectIt->mEffectId == effectId && it->first == sourceId) effectIt = it->second.mEffects.erase(effectIt); else ++effectIt; } } mSpellsChanged = true; } void ActiveSpells::purge(int casterActorId) { for (TContainer::iterator it = mSpells.begin(); it != mSpells.end(); ++it) { for (std::vector::iterator effectIt = it->second.mEffects.begin(); effectIt != it->second.mEffects.end();) { if (it->second.mCasterActorId == casterActorId) effectIt = it->second.mEffects.erase(effectIt); else ++effectIt; } } mSpellsChanged = true; } /* Start of tes3mp addition Allow the purging of an effect for a specific arg (attribute or skill) */ void ActiveSpells::purgeEffectByArg(short effectId, int effectArg) { for (TContainer::iterator it = mSpells.begin(); it != mSpells.end(); ++it) { for (std::vector::iterator effectIt = it->second.mEffects.begin(); effectIt != it->second.mEffects.end();) { if (effectIt->mEffectId == effectId && effectIt->mArg == effectArg) effectIt = it->second.mEffects.erase(effectIt); else ++effectIt; } } mSpellsChanged = true; } /* End of tes3mp addition */ /* Start of tes3mp addition Make it easy to get an effect's duration */ float ActiveSpells::getEffectDuration(short effectId, std::string sourceId) { for (TContainer::iterator it = mSpells.begin(); it != mSpells.end(); ++it) { if (sourceId.compare(it->first) == 0) { for (std::vector::iterator effectIt = it->second.mEffects.begin(); effectIt != it->second.mEffects.end(); it++) { if (effectIt->mEffectId == effectId) return effectIt->mDuration; } } } return 0.f; } /* End of tes3mp addition */ void ActiveSpells::clear() { mSpells.clear(); mSpellsChanged = true; } void ActiveSpells::writeState(ESM::ActiveSpells &state) const { for (TContainer::const_iterator it = mSpells.begin(); it != mSpells.end(); ++it) { // Stupid copying of almost identical structures. ESM::TimeStamp <-> MWWorld::TimeStamp ESM::ActiveSpells::ActiveSpellParams params; params.mEffects = it->second.mEffects; params.mCasterActorId = it->second.mCasterActorId; params.mDisplayName = it->second.mDisplayName; params.mTimeStamp = it->second.mTimeStamp.toEsm(); state.mSpells.insert (std::make_pair(it->first, params)); } } void ActiveSpells::readState(const ESM::ActiveSpells &state) { for (ESM::ActiveSpells::TContainer::const_iterator it = state.mSpells.begin(); it != state.mSpells.end(); ++it) { // Stupid copying of almost identical structures. ESM::TimeStamp <-> MWWorld::TimeStamp ActiveSpellParams params; params.mEffects = it->second.mEffects; params.mCasterActorId = it->second.mCasterActorId; params.mDisplayName = it->second.mDisplayName; params.mTimeStamp = MWWorld::TimeStamp(it->second.mTimeStamp); mSpells.insert (std::make_pair(it->first, params)); mSpellsChanged = true; } } }