diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index c645e1a0a..97ab13012 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -76,7 +76,7 @@ add_openmw_dir (mwmechanics mechanicsmanagerimp stat character creaturestats magiceffects movement actors objects drawstate spells activespells npcstats aipackage aisequence aipursue alchemy aiwander aitravel aifollow aiavoiddoor aiescort aiactivate aicombat repair enchanting pathfinding pathgrid security spellsuccess spellcasting - disease pickpocket levelledlist combat steering obstacle autocalcspell difficultyscaling aicombataction actor + disease pickpocket levelledlist combat steering obstacle autocalcspell difficultyscaling aicombataction actor summoning ) add_openmw_dir (mwstate diff --git a/apps/openmw/mwgui/spellicons.cpp b/apps/openmw/mwgui/spellicons.cpp index 8ea9cfd7f..d23f1a235 100644 --- a/apps/openmw/mwgui/spellicons.cpp +++ b/apps/openmw/mwgui/spellicons.cpp @@ -25,7 +25,7 @@ namespace MWGui { void EffectSourceVisitor::visit (MWMechanics::EffectKey key, - const std::string& sourceName, int casterActorId, + const std::string& sourceName, const std::string& sourceId, int casterActorId, float magnitude, float remainingTime, float totalTime) { MagicEffectInfo newEffectSource; diff --git a/apps/openmw/mwgui/spellicons.hpp b/apps/openmw/mwgui/spellicons.hpp index e9d9967ea..5099fc4d6 100644 --- a/apps/openmw/mwgui/spellicons.hpp +++ b/apps/openmw/mwgui/spellicons.hpp @@ -47,7 +47,7 @@ namespace MWGui virtual ~EffectSourceVisitor() {} virtual void visit (MWMechanics::EffectKey key, - const std::string& sourceName, int casterActorId, + const std::string& sourceName, const std::string& sourceId, int casterActorId, float magnitude, float remainingTime = -1, float totalTime = -1); }; diff --git a/apps/openmw/mwmechanics/activespells.cpp b/apps/openmw/mwmechanics/activespells.cpp index 717a63be8..6e15449e1 100644 --- a/apps/openmw/mwmechanics/activespells.cpp +++ b/apps/openmw/mwmechanics/activespells.cpp @@ -195,7 +195,7 @@ namespace MWMechanics float magnitude = effectIt->mMagnitude; if (magnitude) - visitor.visit(MWMechanics::EffectKey(effectIt->mEffectId, effectIt->mArg), name, it->second.mCasterActorId, magnitude, remainingTime, effectIt->mDuration); + visitor.visit(MWMechanics::EffectKey(effectIt->mEffectId, effectIt->mArg), name, it->first, it->second.mCasterActorId, magnitude, remainingTime, effectIt->mDuration); } } } @@ -229,6 +229,22 @@ namespace MWMechanics 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) diff --git a/apps/openmw/mwmechanics/activespells.hpp b/apps/openmw/mwmechanics/activespells.hpp index 9c1a5e613..4f9d15d8c 100644 --- a/apps/openmw/mwmechanics/activespells.hpp +++ b/apps/openmw/mwmechanics/activespells.hpp @@ -82,6 +82,9 @@ namespace MWMechanics /// Remove all active effects with this effect id void purgeEffect (short effectId); + /// Remove all active effects with this effect id and source id + void purgeEffect (short effectId, const std::string& sourceId); + /// Remove all active effects, if roll succeeds (for each effect) void purgeAll (float chance); diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 1055e0f4a..c0fc80692 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -36,6 +36,7 @@ #include "aipursue.hpp" #include "actor.hpp" +#include "summoning.hpp" namespace { @@ -105,7 +106,7 @@ public: , mCommanded(false){} virtual void visit (MWMechanics::EffectKey key, - const std::string& sourceName, int casterActorId, + const std::string& sourceName, const std::string& sourceId, int casterActorId, float magnitude, float remainingTime = -1, float totalTime = -1) { MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); @@ -165,30 +166,6 @@ void getRestorationPerHourOfSleep (const MWWorld::Ptr& ptr, float& health, float } } -void cleanupSummonedCreature (MWMechanics::CreatureStats& casterStats, int creatureActorId) -{ - MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->searchPtrViaActorId(creatureActorId); - if (!ptr.isEmpty()) - { - // TODO: Show death animation before deleting? We shouldn't allow looting the corpse while the animation - // plays though, which is a rather lame exploit in vanilla. - MWBase::Environment::get().getWorld()->deleteObject(ptr); - - const ESM::Static* fx = MWBase::Environment::get().getWorld()->getStore().get() - .search("VFX_Summon_End"); - if (fx) - MWBase::Environment::get().getWorld()->spawnEffect("meshes\\" + fx->mModel, - "", Ogre::Vector3(ptr.getRefData().getPosition().pos)); - } - else - { - // We didn't find the creature. It's probably in an inactive cell. - // Add to graveyard so we can delete it when the cell becomes active. - std::vector& graveyard = casterStats.getSummonedCreatureGraveyard(); - graveyard.push_back(creatureActorId); - } -} - } namespace MWMechanics @@ -203,7 +180,7 @@ namespace MWMechanics : mCreature(trappedCreature) {} virtual void visit (MWMechanics::EffectKey key, - const std::string& sourceName, int casterActorId, + const std::string& sourceName, const std::string& sourceId, int casterActorId, float magnitude, float remainingTime = -1, float totalTime = -1) { if (key.mId != ESM::MagicEffect::Soultrap) @@ -782,131 +759,11 @@ namespace MWMechanics } } - // Update summon effects - static std::map summonMap; - if (summonMap.empty()) - { - summonMap[ESM::MagicEffect::SummonAncestralGhost] = "sMagicAncestralGhostID"; - summonMap[ESM::MagicEffect::SummonBonelord] = "sMagicBonelordID"; - summonMap[ESM::MagicEffect::SummonBonewalker] = "sMagicLeastBonewalkerID"; - summonMap[ESM::MagicEffect::SummonCenturionSphere] = "sMagicCenturionSphereID"; - summonMap[ESM::MagicEffect::SummonClannfear] = "sMagicClannfearID"; - summonMap[ESM::MagicEffect::SummonDaedroth] = "sMagicDaedrothID"; - summonMap[ESM::MagicEffect::SummonDremora] = "sMagicDremoraID"; - summonMap[ESM::MagicEffect::SummonFabricant] = "sMagicFabricantID"; - summonMap[ESM::MagicEffect::SummonFlameAtronach] = "sMagicFlameAtronachID"; - summonMap[ESM::MagicEffect::SummonFrostAtronach] = "sMagicFrostAtronachID"; - summonMap[ESM::MagicEffect::SummonGoldenSaint] = "sMagicGoldenSaintID"; - summonMap[ESM::MagicEffect::SummonGreaterBonewalker] = "sMagicGreaterBonewalkerID"; - summonMap[ESM::MagicEffect::SummonHunger] = "sMagicHungerID"; - summonMap[ESM::MagicEffect::SummonScamp] = "sMagicScampID"; - summonMap[ESM::MagicEffect::SummonSkeletalMinion] = "sMagicSkeletalMinionID"; - summonMap[ESM::MagicEffect::SummonStormAtronach] = "sMagicStormAtronachID"; - summonMap[ESM::MagicEffect::SummonWingedTwilight] = "sMagicWingedTwilightID"; - summonMap[ESM::MagicEffect::SummonWolf] = "sMagicCreature01ID"; - summonMap[ESM::MagicEffect::SummonBear] = "sMagicCreature02ID"; - summonMap[ESM::MagicEffect::SummonBonewolf] = "sMagicCreature03ID"; - summonMap[ESM::MagicEffect::SummonCreature04] = "sMagicCreature04ID"; - summonMap[ESM::MagicEffect::SummonCreature05] = "sMagicCreature05ID"; - } - - std::map& creatureMap = creatureStats.getSummonedCreatureMap(); - for (std::map::iterator it = summonMap.begin(); it != summonMap.end(); ++it) - { - bool found = creatureMap.find(it->first) != creatureMap.end(); - int magnitude = creatureStats.getMagicEffects().get(it->first).getMagnitude(); - if (found != (magnitude > 0)) - { - if (magnitude > 0) - { - ESM::Position ipos = ptr.getRefData().getPosition(); - Ogre::Vector3 pos(ipos.pos); - Ogre::Quaternion rot(Ogre::Radian(-ipos.rot[2]), Ogre::Vector3::UNIT_Z); - const float distance = 50; - pos = pos + distance*rot.yAxis(); - ipos.pos[0] = pos.x; - ipos.pos[1] = pos.y; - ipos.pos[2] = pos.z; - ipos.rot[0] = 0; - ipos.rot[1] = 0; - ipos.rot[2] = 0; - - std::string creatureID = - MWBase::Environment::get().getWorld()->getStore().get().find(it->second)->getString(); - - if (!creatureID.empty()) - { - MWWorld::CellStore* store = ptr.getCell(); - MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), creatureID, 1); - ref.getPtr().getCellRef().setPosition(ipos); - - MWMechanics::CreatureStats& summonedCreatureStats = ref.getPtr().getClass().getCreatureStats(ref.getPtr()); - - // Make the summoned creature follow its master and help in fights - AiFollow package(ptr.getCellRef().getRefId()); - summonedCreatureStats.getAiSequence().stack(package, ref.getPtr()); - int creatureActorId = summonedCreatureStats.getActorId(); - - MWWorld::Ptr placed = MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),store,ipos); - - MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(placed); - if (anim) - { - const ESM::Static* fx = MWBase::Environment::get().getWorld()->getStore().get() - .search("VFX_Summon_Start"); - if (fx) - anim->addEffect("meshes\\" + fx->mModel, -1, false); - } - - creatureMap.insert(std::make_pair(it->first, creatureActorId)); - } - } - else - { - // Effect has ended - std::map::iterator foundCreature = creatureMap.find(it->first); - cleanupSummonedCreature(creatureStats, foundCreature->second); - creatureMap.erase(foundCreature); - } - } - } - - for (std::map::iterator it = creatureMap.begin(); it != creatureMap.end(); ) - { - MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->searchPtrViaActorId(it->second); - if (!ptr.isEmpty() && ptr.getClass().getCreatureStats(ptr).isDead()) - { - // Purge the magic effect so a new creature can be summoned if desired - creatureStats.getActiveSpells().purgeEffect(it->first); - if (ptr.getClass().hasInventoryStore(ptr)) - ptr.getClass().getInventoryStore(ptr).purgeEffect(it->first); - - cleanupSummonedCreature(creatureStats, it->second); - creatureMap.erase(it++); - } - else - ++it; - } - - std::vector& graveyard = creatureStats.getSummonedCreatureGraveyard(); - for (std::vector::iterator it = graveyard.begin(); it != graveyard.end(); ) - { - MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->searchPtrViaActorId(*it); - if (!ptr.isEmpty()) - { - it = graveyard.erase(it); - - const ESM::Static* fx = MWBase::Environment::get().getWorld()->getStore().get() - .search("VFX_Summon_End"); - if (fx) - MWBase::Environment::get().getWorld()->spawnEffect("meshes\\" + fx->mModel, - "", Ogre::Vector3(ptr.getRefData().getPosition().pos)); - - MWBase::Environment::get().getWorld()->deleteObject(ptr); - } - else - ++it; - } + UpdateSummonedCreatures updateSummonedCreatures(ptr); + creatureStats.getActiveSpells().visitEffectSources(updateSummonedCreatures); + if (ptr.getClass().hasInventoryStore(ptr)) + ptr.getClass().getInventoryStore(ptr).visitEffectSources(updateSummonedCreatures); + updateSummonedCreatures.finish(); } void Actors::calculateNpcStatModifiers (const MWWorld::Ptr& ptr, float duration) diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index ac6f88d44..c61cc9697 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -638,7 +638,7 @@ namespace MWMechanics mDeathAnimation = index; } - std::map& CreatureStats::getSummonedCreatureMap() + std::map& CreatureStats::getSummonedCreatureMap() { return mSummonedCreatures; } diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index 9a08b58c9..145eb8a5b 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -67,8 +67,11 @@ namespace MWMechanics // The index of the death animation that was played unsigned char mDeathAnimation; - // - std::map mSummonedCreatures; + public: + typedef std::pair SummonKey; // + private: + std::map mSummonedCreatures; // + // Contains ActorIds of summoned creatures with an expired lifetime that have not been deleted yet. // This may be necessary when the creature is in an inactive cell. std::vector mSummonGraveyard; @@ -216,8 +219,8 @@ namespace MWMechanics void setBlock(bool value); bool getBlock() const; - std::map& getSummonedCreatureMap(); - std::vector& getSummonedCreatureGraveyard(); + std::map& getSummonedCreatureMap(); // + std::vector& getSummonedCreatureGraveyard(); // ActorIds enum Flag { diff --git a/apps/openmw/mwmechanics/magiceffects.hpp b/apps/openmw/mwmechanics/magiceffects.hpp index 88d8d988f..c384d0857 100644 --- a/apps/openmw/mwmechanics/magiceffects.hpp +++ b/apps/openmw/mwmechanics/magiceffects.hpp @@ -73,7 +73,7 @@ namespace MWMechanics struct EffectSourceVisitor { virtual void visit (MWMechanics::EffectKey key, - const std::string& sourceName, int casterActorId, + const std::string& sourceName, const std::string& sourceId, int casterActorId, float magnitude, float remainingTime = -1, float totalTime = -1) = 0; }; diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 58ccd389a..9cfcdda18 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -21,6 +21,7 @@ #include "magiceffects.hpp" #include "npcstats.hpp" +#include "summoning.hpp" namespace { diff --git a/apps/openmw/mwmechanics/spells.cpp b/apps/openmw/mwmechanics/spells.cpp index 5953be523..1f8ada06d 100644 --- a/apps/openmw/mwmechanics/spells.cpp +++ b/apps/openmw/mwmechanics/spells.cpp @@ -249,7 +249,7 @@ namespace MWMechanics random = it->second.at(i); float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * random; - visitor.visit(MWMechanics::EffectKey(*effectIt), spell->mName, -1, magnitude); + visitor.visit(MWMechanics::EffectKey(*effectIt), spell->mName, spell->mId, -1, magnitude); } } } diff --git a/apps/openmw/mwmechanics/summoning.cpp b/apps/openmw/mwmechanics/summoning.cpp new file mode 100644 index 000000000..356cb422f --- /dev/null +++ b/apps/openmw/mwmechanics/summoning.cpp @@ -0,0 +1,195 @@ +#include "summoning.hpp" + +#include + +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + +#include "../mwworld/esmstore.hpp" +#include "../mwworld/class.hpp" +#include "../mwworld/manualref.hpp" +#include "../mwworld/inventorystore.hpp" + +#include "../mwrender/animation.hpp" + +#include "creaturestats.hpp" +#include "aifollow.hpp" + + +namespace MWMechanics +{ + + void cleanupSummonedCreature (MWMechanics::CreatureStats& casterStats, int creatureActorId) + { + MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->searchPtrViaActorId(creatureActorId); + if (!ptr.isEmpty()) + { + // TODO: Show death animation before deleting? We shouldn't allow looting the corpse while the animation + // plays though, which is a rather lame exploit in vanilla. + MWBase::Environment::get().getWorld()->deleteObject(ptr); + + const ESM::Static* fx = MWBase::Environment::get().getWorld()->getStore().get() + .search("VFX_Summon_End"); + if (fx) + MWBase::Environment::get().getWorld()->spawnEffect("meshes\\" + fx->mModel, + "", Ogre::Vector3(ptr.getRefData().getPosition().pos)); + } + else + { + // We didn't find the creature. It's probably in an inactive cell. + // Add to graveyard so we can delete it when the cell becomes active. + std::vector& graveyard = casterStats.getSummonedCreatureGraveyard(); + graveyard.push_back(creatureActorId); + } + } + + UpdateSummonedCreatures::UpdateSummonedCreatures(const MWWorld::Ptr &actor) + : mActor(actor) + { + + } + + void UpdateSummonedCreatures::visit(EffectKey key, const std::string &sourceName, const std::string &sourceId, int casterActorId, float magnitude, float remainingTime, float totalTime) + { + if (key.mId >= ESM::MagicEffect::SummonScamp + && key.mId <= ESM::MagicEffect::SummonStormAtronach && magnitude > 0) + { + mActiveEffects.insert(std::make_pair(key.mId, sourceId)); + } + } + + void UpdateSummonedCreatures::finish() + { + static std::map summonMap; + if (summonMap.empty()) + { + summonMap[ESM::MagicEffect::SummonAncestralGhost] = "sMagicAncestralGhostID"; + summonMap[ESM::MagicEffect::SummonBonelord] = "sMagicBonelordID"; + summonMap[ESM::MagicEffect::SummonBonewalker] = "sMagicLeastBonewalkerID"; + summonMap[ESM::MagicEffect::SummonCenturionSphere] = "sMagicCenturionSphereID"; + summonMap[ESM::MagicEffect::SummonClannfear] = "sMagicClannfearID"; + summonMap[ESM::MagicEffect::SummonDaedroth] = "sMagicDaedrothID"; + summonMap[ESM::MagicEffect::SummonDremora] = "sMagicDremoraID"; + summonMap[ESM::MagicEffect::SummonFabricant] = "sMagicFabricantID"; + summonMap[ESM::MagicEffect::SummonFlameAtronach] = "sMagicFlameAtronachID"; + summonMap[ESM::MagicEffect::SummonFrostAtronach] = "sMagicFrostAtronachID"; + summonMap[ESM::MagicEffect::SummonGoldenSaint] = "sMagicGoldenSaintID"; + summonMap[ESM::MagicEffect::SummonGreaterBonewalker] = "sMagicGreaterBonewalkerID"; + summonMap[ESM::MagicEffect::SummonHunger] = "sMagicHungerID"; + summonMap[ESM::MagicEffect::SummonScamp] = "sMagicScampID"; + summonMap[ESM::MagicEffect::SummonSkeletalMinion] = "sMagicSkeletalMinionID"; + summonMap[ESM::MagicEffect::SummonStormAtronach] = "sMagicStormAtronachID"; + summonMap[ESM::MagicEffect::SummonWingedTwilight] = "sMagicWingedTwilightID"; + summonMap[ESM::MagicEffect::SummonWolf] = "sMagicCreature01ID"; + summonMap[ESM::MagicEffect::SummonBear] = "sMagicCreature02ID"; + summonMap[ESM::MagicEffect::SummonBonewolf] = "sMagicCreature03ID"; + summonMap[ESM::MagicEffect::SummonCreature04] = "sMagicCreature04ID"; + summonMap[ESM::MagicEffect::SummonCreature05] = "sMagicCreature05ID"; + } + + MWMechanics::CreatureStats& creatureStats = mActor.getClass().getCreatureStats(mActor); + + // Update summon effects + std::map& creatureMap = creatureStats.getSummonedCreatureMap(); + for (std::map::iterator it = creatureMap.begin(); it != creatureMap.end(); ) + { + bool found = mActiveEffects.find(it->first) != mActiveEffects.end(); + if (!found) + { + // Effect has ended + cleanupSummonedCreature(creatureStats, it->second); + creatureMap.erase(it++); + continue; + } + ++it; + } + + for (std::set >::iterator it = mActiveEffects.begin(); it != mActiveEffects.end(); ++it) + { + bool found = creatureMap.find(std::make_pair(it->first, it->second)) != creatureMap.end(); + if (!found) + { + ESM::Position ipos = mActor.getRefData().getPosition(); + Ogre::Vector3 pos(ipos.pos); + Ogre::Quaternion rot(Ogre::Radian(-ipos.rot[2]), Ogre::Vector3::UNIT_Z); + const float distance = 50; + pos = pos + distance*rot.yAxis(); + ipos.pos[0] = pos.x; + ipos.pos[1] = pos.y; + ipos.pos[2] = pos.z; + ipos.rot[0] = 0; + ipos.rot[1] = 0; + ipos.rot[2] = 0; + + const std::string& creatureGmst = summonMap[it->first]; + std::string creatureID = + MWBase::Environment::get().getWorld()->getStore().get().find(creatureGmst)->getString(); + + if (!creatureID.empty()) + { + MWWorld::CellStore* store = mActor.getCell(); + MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), creatureID, 1); + ref.getPtr().getCellRef().setPosition(ipos); + + MWMechanics::CreatureStats& summonedCreatureStats = ref.getPtr().getClass().getCreatureStats(ref.getPtr()); + + // Make the summoned creature follow its master and help in fights + AiFollow package(mActor.getCellRef().getRefId()); + summonedCreatureStats.getAiSequence().stack(package, ref.getPtr()); + int creatureActorId = summonedCreatureStats.getActorId(); + + MWWorld::Ptr placed = MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),store,ipos); + + MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(placed); + if (anim) + { + const ESM::Static* fx = MWBase::Environment::get().getWorld()->getStore().get() + .search("VFX_Summon_Start"); + if (fx) + anim->addEffect("meshes\\" + fx->mModel, -1, false); + } + + creatureMap.insert(std::make_pair(*it, creatureActorId)); + } + } + } + + for (std::map::iterator it = creatureMap.begin(); it != creatureMap.end(); ) + { + MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->searchPtrViaActorId(it->second); + if (!ptr.isEmpty() && ptr.getClass().getCreatureStats(ptr).isDead()) + { + // Purge the magic effect so a new creature can be summoned if desired + creatureStats.getActiveSpells().purgeEffect(it->first.first, it->first.second); + if (mActor.getClass().hasInventoryStore(ptr)) + mActor.getClass().getInventoryStore(mActor).purgeEffect(it->first.first, it->first.second); + + cleanupSummonedCreature(creatureStats, it->second); + creatureMap.erase(it++); + } + else + ++it; + } + + std::vector& graveyard = creatureStats.getSummonedCreatureGraveyard(); + for (std::vector::iterator it = graveyard.begin(); it != graveyard.end(); ) + { + MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->searchPtrViaActorId(*it); + if (!ptr.isEmpty()) + { + it = graveyard.erase(it); + + const ESM::Static* fx = MWBase::Environment::get().getWorld()->getStore().get() + .search("VFX_Summon_End"); + if (fx) + MWBase::Environment::get().getWorld()->spawnEffect("meshes\\" + fx->mModel, + "", Ogre::Vector3(ptr.getRefData().getPosition().pos)); + + MWBase::Environment::get().getWorld()->deleteObject(ptr); + } + else + ++it; + } + } + +} diff --git a/apps/openmw/mwmechanics/summoning.hpp b/apps/openmw/mwmechanics/summoning.hpp new file mode 100644 index 000000000..b8fe37783 --- /dev/null +++ b/apps/openmw/mwmechanics/summoning.hpp @@ -0,0 +1,35 @@ +#ifndef OPENMW_MECHANICS_SUMMONING_H +#define OPENMW_MECHANICS_SUMMONING_H + +#include + +#include "magiceffects.hpp" +#include "../mwworld/ptr.hpp" + +namespace MWMechanics +{ + + class CreatureStats; + + struct UpdateSummonedCreatures : public EffectSourceVisitor + { + UpdateSummonedCreatures(const MWWorld::Ptr& actor); + + virtual void visit (MWMechanics::EffectKey key, + const std::string& sourceName, const std::string& sourceId, int casterActorId, + float magnitude, float remainingTime = -1, float totalTime = -1); + + /// To call after all effect sources have been visited + void finish(); + + private: + MWWorld::Ptr mActor; + + std::set > mActiveEffects; + }; + + void cleanupSummonedCreature (MWMechanics::CreatureStats& casterStats, int creatureActorId); + +} + +#endif diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index 9c329ce72..445b42d8d 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -583,7 +583,8 @@ void MWWorld::InventoryStore::visitEffectSources(MWMechanics::EffectSourceVisito const EffectParams& params = mPermanentMagicEffectMagnitudes[(**iter).getCellRef().getRefId()][i]; float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * params.mRandom; magnitude *= params.mMultiplier; - visitor.visit(MWMechanics::EffectKey(*effectIt), (**iter).getClass().getName(**iter), -1, magnitude); + if (magnitude > 0) + visitor.visit(MWMechanics::EffectKey(*effectIt), (**iter).getClass().getName(**iter), (**iter).getCellRef().getRefId(), -1, magnitude); ++i; } @@ -639,6 +640,52 @@ void MWWorld::InventoryStore::purgeEffect(short effectId) mMagicEffects.remove(MWMechanics::EffectKey(effectId)); } +void MWWorld::InventoryStore::purgeEffect(short effectId, const std::string &sourceId) +{ + TEffectMagnitudes::iterator effectMagnitudeIt = mPermanentMagicEffectMagnitudes.find(sourceId); + if (effectMagnitudeIt == mPermanentMagicEffectMagnitudes.end()) + return; + + for (TSlots::const_iterator iter (mSlots.begin()); iter!=mSlots.end(); ++iter) + { + if (*iter==end()) + continue; + + if ((*iter)->getClass().getId(**iter) != sourceId) + continue; + + std::string enchantmentId = (*iter)->getClass().getEnchantment (**iter); + + if (!enchantmentId.empty()) + { + const ESM::Enchantment& enchantment = + *MWBase::Environment::get().getWorld()->getStore().get().find (enchantmentId); + + if (enchantment.mData.mType != ESM::Enchantment::ConstantEffect) + continue; + + std::vector& params = effectMagnitudeIt->second; + + int i=0; + for (std::vector::const_iterator effectIt (enchantment.mEffects.mList.begin()); + effectIt!=enchantment.mEffects.mList.end(); ++effectIt, ++i) + { + if (effectIt->mEffectID != effectId) + continue; + + float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * params[i].mRandom; + magnitude *= params[i].mMultiplier; + + if (magnitude) + mMagicEffects.add (*effectIt, -magnitude); + + params[i].mMultiplier = 0; + break; + } + } + } +} + void MWWorld::InventoryStore::clear() { mSlots.clear(); diff --git a/apps/openmw/mwworld/inventorystore.hpp b/apps/openmw/mwworld/inventorystore.hpp index 48742b557..9fd18c54b 100644 --- a/apps/openmw/mwworld/inventorystore.hpp +++ b/apps/openmw/mwworld/inventorystore.hpp @@ -203,6 +203,9 @@ namespace MWWorld void purgeEffect (short effectId); ///< Remove a magic effect + void purgeEffect (short effectId, const std::string& sourceId); + ///< Remove a magic effect + virtual void clear(); ///< Empty container. diff --git a/components/esm/creaturestats.cpp b/components/esm/creaturestats.cpp index cc76ef0df..1fdce703c 100644 --- a/components/esm/creaturestats.cpp +++ b/components/esm/creaturestats.cpp @@ -94,9 +94,10 @@ void ESM::CreatureStats::load (ESMReader &esm) { int magicEffect; esm.getHT(magicEffect); + std::string source = esm.getHNOString("SOUR"); int actorId; esm.getHNT (actorId, "ACID"); - mSummonedCreatureMap[magicEffect] = actorId; + mSummonedCreatureMap[std::make_pair(magicEffect, source)] = actorId; } while (esm.isNextSub("GRAV")) @@ -204,9 +205,10 @@ void ESM::CreatureStats::save (ESMWriter &esm) const mAiSequence.save(esm); mMagicEffects.save(esm); - for (std::map::const_iterator it = mSummonedCreatureMap.begin(); it != mSummonedCreatureMap.end(); ++it) + for (std::map, int>::const_iterator it = mSummonedCreatureMap.begin(); it != mSummonedCreatureMap.end(); ++it) { - esm.writeHNT ("SUMM", it->first); + esm.writeHNT ("SUMM", it->first.first); + esm.writeHNString ("SOUR", it->first.second); esm.writeHNT ("ACID", it->second); } diff --git a/components/esm/creaturestats.hpp b/components/esm/creaturestats.hpp index 7946d0e45..680577f93 100644 --- a/components/esm/creaturestats.hpp +++ b/components/esm/creaturestats.hpp @@ -32,7 +32,7 @@ namespace ESM bool mHasAiSettings; StatState mAiSettings[4]; - std::map mSummonedCreatureMap; + std::map, int> mSummonedCreatureMap; std::vector mSummonGraveyard; ESM::TimeStamp mTradeTime;