Implement SoulTrap magic effect

This commit is contained in:
scrawl 2014-01-02 21:21:28 +01:00
parent 596e0c8a49
commit 299690631f
10 changed files with 94 additions and 23 deletions

View file

@ -22,7 +22,8 @@ namespace MWGui
{ {
void EffectSourceVisitor::visit (MWMechanics::EffectKey key, void EffectSourceVisitor::visit (MWMechanics::EffectKey key,
const std::string& sourceName, float magnitude, float remainingTime) const std::string& sourceName, const std::string& casterHandle,
float magnitude, float remainingTime)
{ {
MagicEffectInfo newEffectSource; MagicEffectInfo newEffectSource;
newEffectSource.mKey = key; newEffectSource.mKey = key;

View file

@ -43,7 +43,8 @@ namespace MWGui
std::map <int, std::vector<MagicEffectInfo> > mEffectSources; std::map <int, std::vector<MagicEffectInfo> > mEffectSources;
virtual void visit (MWMechanics::EffectKey key, virtual void visit (MWMechanics::EffectKey key,
const std::string& sourceName, float magnitude, float remainingTime = -1); const std::string& sourceName, const std::string& casterHandle,
float magnitude, float remainingTime = -1);
}; };
class SpellIcons class SpellIcons

View file

@ -126,7 +126,8 @@ namespace MWMechanics
return mSpells; return mSpells;
} }
void ActiveSpells::addSpell(const std::string &id, bool stack, std::vector<Effect> effects, const std::string &displayName) void ActiveSpells::addSpell(const std::string &id, bool stack, std::vector<Effect> effects,
const std::string &displayName, const std::string& casterHandle)
{ {
bool exists = false; bool exists = false;
for (TContainer::const_iterator it = begin(); it != end(); ++it) for (TContainer::const_iterator it = begin(); it != end(); ++it)
@ -139,6 +140,7 @@ namespace MWMechanics
params.mTimeStamp = MWBase::Environment::get().getWorld()->getTimeStamp(); params.mTimeStamp = MWBase::Environment::get().getWorld()->getTimeStamp();
params.mEffects = effects; params.mEffects = effects;
params.mDisplayName = displayName; params.mDisplayName = displayName;
params.mCasterHandle = casterHandle;
if (!exists || stack) if (!exists || stack)
mSpells.insert (std::make_pair(id, params)); mSpells.insert (std::make_pair(id, params));
@ -164,7 +166,7 @@ namespace MWMechanics
float magnitude = effectIt->mMagnitude; float magnitude = effectIt->mMagnitude;
if (magnitude) if (magnitude)
visitor.visit(effectIt->mKey, name, magnitude, remainingTime); visitor.visit(effectIt->mKey, name, it->second.mCasterHandle, magnitude, remainingTime);
} }
} }
} }

View file

@ -37,8 +37,8 @@ namespace MWMechanics
MWWorld::TimeStamp mTimeStamp; MWWorld::TimeStamp mTimeStamp;
std::string mDisplayName; std::string mDisplayName;
// TODO: To handle CASTER_LINKED flag (spell is purged when caster dies), // Handle to the caster that that inflicted this spell on us
// we should probably store a handle to the caster here. std::string mCasterHandle;
}; };
typedef std::multimap<std::string, ActiveSpellParams > TContainer; typedef std::multimap<std::string, ActiveSpellParams > TContainer;
@ -76,8 +76,10 @@ namespace MWMechanics
/// \param stack If false, the spell is not added if one with the same ID exists already. /// \param stack If false, the spell is not added if one with the same ID exists already.
/// \param effects /// \param effects
/// \param displayName Name for display in magic menu. /// \param displayName Name for display in magic menu.
/// \param casterHandle
/// ///
void addSpell (const std::string& id, bool stack, std::vector<Effect> effects, const std::string& displayName); void addSpell (const std::string& id, bool stack, std::vector<Effect> effects,
const std::string& displayName, const std::string& casterHandle);
/// Remove all active effects with this id /// Remove all active effects with this id
void purgeEffect (short effectId); void purgeEffect (short effectId);

View file

@ -88,6 +88,68 @@ bool disintegrateSlot (MWWorld::Ptr ptr, int slot, float disintegrate)
namespace MWMechanics namespace MWMechanics
{ {
class SoulTrap : public MWMechanics::EffectSourceVisitor
{
MWWorld::Ptr mCreature;
MWWorld::Ptr mActor;
public:
SoulTrap(MWWorld::Ptr trappedCreature)
: mCreature(trappedCreature) {}
virtual void visit (MWMechanics::EffectKey key,
const std::string& sourceName, const std::string& casterHandle,
float magnitude, float remainingTime = -1)
{
if (key.mId != ESM::MagicEffect::Soultrap)
return;
if (magnitude <= 0)
return;
MWBase::World* world = MWBase::Environment::get().getWorld();
MWWorld::Ptr caster = world->searchPtrViaHandle(casterHandle);
if (caster.isEmpty() || !caster.getClass().isActor())
return;
static const float fSoulgemMult = world->getStore().get<ESM::GameSetting>().find("fSoulgemMult")->getFloat();
float creatureSoulValue = mCreature.get<ESM::Creature>()->mBase->mData.mSoul;
// Use the smallest soulgem that is large enough to hold the soul
MWWorld::ContainerStore& container = caster.getClass().getContainerStore(caster);
MWWorld::ContainerStoreIterator gem = container.end();
float gemCapacity = std::numeric_limits<float>().max();
std::string soulgemFilter = "misc_soulgem"; // no other way to check for soulgems? :/
for (MWWorld::ContainerStoreIterator it = container.begin(MWWorld::ContainerStore::Type_Miscellaneous);
it != container.end(); ++it)
{
const std::string& id = it->getCellRef().mRefID;
if (id.size() >= soulgemFilter.size()
&& id.substr(0,soulgemFilter.size()) == soulgemFilter)
{
float thisGemCapacity = it->get<ESM::Miscellaneous>()->mBase->mData.mValue * fSoulgemMult;
if (thisGemCapacity >= creatureSoulValue && thisGemCapacity < gemCapacity
&& it->getCellRef().mSoul.empty())
{
gem = it;
gemCapacity = thisGemCapacity;
}
}
}
if (gem == container.end())
return;
// Set the soul on just one of the gems, not the whole stack
gem->getContainerStore()->unstack(*gem, caster);
gem->getCellRef().mSoul = mCreature.getCellRef().mRefID;
if (caster.getRefData().getHandle() == "player")
MWBase::Environment::get().getWindowManager()->messageBox("#{sSoultrapSuccess}");
}
};
void Actors::updateActor (const MWWorld::Ptr& ptr, float duration) void Actors::updateActor (const MWWorld::Ptr& ptr, float duration)
{ {
// magic effects // magic effects
@ -705,6 +767,13 @@ namespace MWMechanics
iter->second->kill(); iter->second->kill();
// Apply soultrap
if (iter->first.getTypeName() == typeid(ESM::Creature).name())
{
SoulTrap soulTrap (iter->first);
stats.getActiveSpells().visitEffectSources(soulTrap);
}
// Reset magic effects and recalculate derived effects // Reset magic effects and recalculate derived effects
// One case where we need this is to make sure bound items are removed upon death // One case where we need this is to make sure bound items are removed upon death
stats.setMagicEffects(MWMechanics::MagicEffects()); stats.setMagicEffects(MWMechanics::MagicEffects());
@ -714,6 +783,7 @@ namespace MWMechanics
if(cls.isEssential(iter->first)) if(cls.isEssential(iter->first))
MWBase::Environment::get().getWindowManager()->messageBox("#{sKilledEssential}"); MWBase::Environment::get().getWindowManager()->messageBox("#{sKilledEssential}");
} }
} }

View file

@ -56,7 +56,8 @@ namespace MWMechanics
struct EffectSourceVisitor struct EffectSourceVisitor
{ {
virtual void visit (MWMechanics::EffectKey key, virtual void visit (MWMechanics::EffectKey key,
const std::string& sourceName, float magnitude, float remainingTime = -1) = 0; const std::string& sourceName, const std::string& casterHandle,
float magnitude, float remainingTime = -1) = 0;
}; };
/// \brief Effects currently affecting a NPC or creature /// \brief Effects currently affecting a NPC or creature

View file

@ -24,21 +24,13 @@ void Repair::repair(const MWWorld::Ptr &itemToRepair)
MWWorld::LiveCellRef<ESM::Repair> *ref = MWWorld::LiveCellRef<ESM::Repair> *ref =
mTool.get<ESM::Repair>(); mTool.get<ESM::Repair>();
// unstack tool if required
player.getClass().getContainerStore(player).unstack(mTool, player);
// reduce number of uses left // reduce number of uses left
int uses = (mTool.getCellRef().mCharge != -1) ? mTool.getCellRef().mCharge : ref->mBase->mData.mUses; int uses = (mTool.getCellRef().mCharge != -1) ? mTool.getCellRef().mCharge : ref->mBase->mData.mUses;
mTool.getCellRef().mCharge = uses-1; mTool.getCellRef().mCharge = uses-1;
// unstack tool if required
if (mTool.getRefData().getCount() > 1 && uses == ref->mBase->mData.mUses)
{
MWWorld::ContainerStore& store = MWWorld::Class::get(player).getContainerStore(player);
MWWorld::ContainerStoreIterator it = store.add(mTool, player);
it->getRefData().setCount(mTool.getRefData().getCount()-1);
it->getCellRef().mCharge = -1;
mTool.getRefData().setCount(1);
}
MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player); MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player);
MWMechanics::NpcStats& npcStats = MWWorld::Class::get(player).getNpcStats(player); MWMechanics::NpcStats& npcStats = MWWorld::Class::get(player).getNpcStats(player);

View file

@ -154,7 +154,8 @@ namespace MWMechanics
ActiveSpells::Effect effect_ = effect; ActiveSpells::Effect effect_ = effect;
effect_.mMagnitude *= -1; effect_.mMagnitude *= -1;
effects.push_back(effect_); effects.push_back(effect_);
caster.getClass().getCreatureStats(caster).getActiveSpells().addSpell("", true, effects, mSourceName); caster.getClass().getCreatureStats(caster).getActiveSpells().addSpell("", true,
effects, mSourceName, caster.getRefData().getHandle());
} }
} }
} }
@ -198,7 +199,8 @@ namespace MWMechanics
inflict(caster, target, reflectedEffects, range, true); inflict(caster, target, reflectedEffects, range, true);
if (appliedLastingEffects.size()) if (appliedLastingEffects.size())
target.getClass().getCreatureStats(target).getActiveSpells().addSpell(mId, mStack, appliedLastingEffects, mSourceName); target.getClass().getCreatureStats(target).getActiveSpells().addSpell(mId, mStack, appliedLastingEffects,
mSourceName, caster.getRefData().getHandle());
} }
void CastSpell::applyInstantEffect(const MWWorld::Ptr &target, short effectId, float magnitude) void CastSpell::applyInstantEffect(const MWWorld::Ptr &target, short effectId, float magnitude)

View file

@ -192,7 +192,7 @@ namespace MWMechanics
effectIt != list.mList.end(); ++effectIt, ++i) effectIt != list.mList.end(); ++effectIt, ++i)
{ {
float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * it->second[i]; float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * it->second[i];
visitor.visit(MWMechanics::EffectKey(*effectIt), spell->mName, magnitude); visitor.visit(MWMechanics::EffectKey(*effectIt), spell->mName, "", magnitude);
} }
} }
} }

View file

@ -607,7 +607,7 @@ void MWWorld::InventoryStore::visitEffectSources(MWMechanics::EffectSourceVisito
const EffectParams& params = mPermanentMagicEffectMagnitudes[(**iter).getCellRef().mRefID][i]; const EffectParams& params = mPermanentMagicEffectMagnitudes[(**iter).getCellRef().mRefID][i];
float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * params.mRandom; float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * params.mRandom;
magnitude *= params.mMultiplier; magnitude *= params.mMultiplier;
visitor.visit(MWMechanics::EffectKey(*effectIt), (**iter).getClass().getName(**iter), magnitude); visitor.visit(MWMechanics::EffectKey(*effectIt), (**iter).getClass().getName(**iter), "", magnitude);
++i; ++i;
} }