forked from teamnwah/openmw-tes3coop
Move code for listing effect sources to the spell management classes
This commit is contained in:
parent
c5f1bbcc5f
commit
c73217627e
11 changed files with 273 additions and 210 deletions
|
@ -21,6 +21,20 @@
|
|||
namespace MWGui
|
||||
{
|
||||
|
||||
void EffectSourceVisitor::visit (const ESM::ENAMstruct& enam,
|
||||
const std::string& sourceName, float magnitude, float remainingTime)
|
||||
{
|
||||
MagicEffectInfo newEffectSource;
|
||||
newEffectSource.mKey = MWMechanics::EffectKey(enam);
|
||||
newEffectSource.mMagnitude = magnitude;
|
||||
newEffectSource.mPermanent = mIsPermanent;
|
||||
newEffectSource.mRemainingTime = remainingTime;
|
||||
newEffectSource.mSource = sourceName;
|
||||
|
||||
mEffectSources[enam.mEffectID].push_back(newEffectSource);
|
||||
}
|
||||
|
||||
|
||||
void SpellIcons::updateWidgets(MyGUI::Widget *parent, bool adjustSize)
|
||||
{
|
||||
// TODO: Tracking add/remove/expire would be better than force updating every frame
|
||||
|
@ -28,125 +42,20 @@ namespace MWGui
|
|||
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer();
|
||||
const MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player);
|
||||
|
||||
std::map <int, std::vector<MagicEffectInfo> > effects;
|
||||
|
||||
// add permanent item enchantments
|
||||
EffectSourceVisitor visitor;
|
||||
|
||||
// permanent item enchantments & permanent spells
|
||||
visitor.mIsPermanent = true;
|
||||
MWWorld::InventoryStore& store = MWWorld::Class::get(player).getInventoryStore(player);
|
||||
for (int slot = 0; slot < MWWorld::InventoryStore::Slots; ++slot)
|
||||
{
|
||||
MWWorld::ContainerStoreIterator it = store.getSlot(slot);
|
||||
if (it == store.end())
|
||||
continue;
|
||||
std::string enchantment = MWWorld::Class::get(*it).getEnchantment(*it);
|
||||
if (enchantment.empty())
|
||||
continue;
|
||||
const ESM::Enchantment* enchant = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().find(enchantment);
|
||||
if (enchant->mData.mType != ESM::Enchantment::ConstantEffect)
|
||||
continue;
|
||||
store.visitEffectSources(visitor);
|
||||
stats.getSpells().visitEffectSources(visitor);
|
||||
|
||||
const ESM::EffectList& list = enchant->mEffects;
|
||||
for (std::vector<ESM::ENAMstruct>::const_iterator effectIt = list.mList.begin();
|
||||
effectIt != list.mList.end(); ++effectIt)
|
||||
{
|
||||
const ESM::MagicEffect* magicEffect =
|
||||
MWBase::Environment::get().getWorld ()->getStore ().get<ESM::MagicEffect>().find(effectIt->mEffectID);
|
||||
// now add lasting effects
|
||||
visitor.mIsPermanent = false;
|
||||
stats.getActiveSpells().visitEffectSources(visitor);
|
||||
|
||||
MagicEffectInfo effectInfo;
|
||||
effectInfo.mSource = MWWorld::Class::get(*it).getName(*it);
|
||||
effectInfo.mKey = MWMechanics::EffectKey (effectIt->mEffectID);
|
||||
if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetSkill)
|
||||
effectInfo.mKey.mArg = effectIt->mSkill;
|
||||
else if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetAttribute)
|
||||
effectInfo.mKey.mArg = effectIt->mAttribute;
|
||||
// just using the min magnitude here, permanent enchantments with a random magnitude just wouldn't make any sense
|
||||
effectInfo.mMagnitude = effectIt->mMagnMin;
|
||||
effectInfo.mPermanent = true;
|
||||
effects[effectIt->mEffectID].push_back (effectInfo);
|
||||
}
|
||||
}
|
||||
|
||||
// add permanent spells
|
||||
const MWMechanics::Spells& spells = stats.getSpells();
|
||||
for (MWMechanics::Spells::TIterator it = spells.begin(); it != spells.end(); ++it)
|
||||
{
|
||||
const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find(it->first);
|
||||
|
||||
// these are the spell types that are permanently in effect
|
||||
if (!(spell->mData.mType == ESM::Spell::ST_Ability)
|
||||
&& !(spell->mData.mType == ESM::Spell::ST_Disease)
|
||||
&& !(spell->mData.mType == ESM::Spell::ST_Curse)
|
||||
&& !(spell->mData.mType == ESM::Spell::ST_Blight))
|
||||
continue;
|
||||
const ESM::EffectList& list = spell->mEffects;
|
||||
for (std::vector<ESM::ENAMstruct>::const_iterator effectIt = list.mList.begin();
|
||||
effectIt != list.mList.end(); ++effectIt)
|
||||
{
|
||||
const ESM::MagicEffect* magicEffect =
|
||||
MWBase::Environment::get().getWorld ()->getStore ().get<ESM::MagicEffect>().find(effectIt->mEffectID);
|
||||
MagicEffectInfo effectInfo;
|
||||
effectInfo.mSource = getSpellDisplayName (it->first);
|
||||
effectInfo.mKey = MWMechanics::EffectKey (effectIt->mEffectID);
|
||||
if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetSkill)
|
||||
effectInfo.mKey.mArg = effectIt->mSkill;
|
||||
else if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetAttribute)
|
||||
effectInfo.mKey.mArg = effectIt->mAttribute;
|
||||
// just using the min magnitude here, permanent spells with a random magnitude just wouldn't make any sense
|
||||
effectInfo.mMagnitude = effectIt->mMagnMin;
|
||||
effectInfo.mPermanent = true;
|
||||
|
||||
effects[effectIt->mEffectID].push_back (effectInfo);
|
||||
}
|
||||
}
|
||||
|
||||
// add lasting effect spells/potions etc
|
||||
|
||||
// TODO: Move this to ActiveSpells
|
||||
const MWMechanics::ActiveSpells::TContainer& activeSpells = stats.getActiveSpells().getActiveSpells();
|
||||
for (MWMechanics::ActiveSpells::TContainer::const_iterator it = activeSpells.begin();
|
||||
it != activeSpells.end(); ++it)
|
||||
{
|
||||
const ESM::EffectList& list = getSpellEffectList(it->first);
|
||||
|
||||
float timeScale = MWBase::Environment::get().getWorld()->getTimeScaleFactor();
|
||||
|
||||
int i=0;
|
||||
for (std::vector<ESM::ENAMstruct>::const_iterator effectIt = list.mList.begin();
|
||||
effectIt != list.mList.end(); ++effectIt, ++i)
|
||||
{
|
||||
if (effectIt->mRange != it->second.mRange)
|
||||
continue;
|
||||
|
||||
float randomFactor = it->second.mRandom[i];
|
||||
|
||||
const ESM::MagicEffect* magicEffect =
|
||||
MWBase::Environment::get().getWorld ()->getStore ().get<ESM::MagicEffect>().find(effectIt->mEffectID);
|
||||
|
||||
MagicEffectInfo effectInfo;
|
||||
if (!it->second.mName.empty())
|
||||
effectInfo.mSource = it->second.mName;
|
||||
else
|
||||
effectInfo.mSource = getSpellDisplayName(it->first);
|
||||
effectInfo.mKey = MWMechanics::EffectKey (effectIt->mEffectID);
|
||||
if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetSkill)
|
||||
effectInfo.mKey.mArg = effectIt->mSkill;
|
||||
else if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetAttribute)
|
||||
effectInfo.mKey.mArg = effectIt->mAttribute;
|
||||
effectInfo.mMagnitude = effectIt->mMagnMin + (effectIt->mMagnMax-effectIt->mMagnMin) * randomFactor;
|
||||
effectInfo.mRemainingTime = effectIt->mDuration +
|
||||
(it->second.mTimeStamp - MWBase::Environment::get().getWorld()->getTimeStamp())*3600/timeScale;
|
||||
|
||||
// ingredients need special casing for their magnitude / duration
|
||||
if (MWBase::Environment::get().getWorld()->getStore().get<ESM::Ingredient>().search (it->first))
|
||||
{
|
||||
effectInfo.mRemainingTime = effectIt->mDuration * randomFactor +
|
||||
(it->second.mTimeStamp - MWBase::Environment::get().getWorld()->getTimeStamp())*3600/timeScale;
|
||||
|
||||
effectInfo.mMagnitude = static_cast<int> (0.05*randomFactor / (0.1 * magicEffect->mData.mBaseCost));
|
||||
}
|
||||
|
||||
effects[effectIt->mEffectID].push_back (effectInfo);
|
||||
}
|
||||
}
|
||||
std::map <int, std::vector<MagicEffectInfo> >& effects = visitor.mEffectSources;
|
||||
|
||||
int w=2;
|
||||
|
||||
|
@ -280,59 +189,4 @@ namespace MWGui
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
std::string SpellIcons::getSpellDisplayName (const std::string& id)
|
||||
{
|
||||
if (const ESM::Spell *spell =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().search (id))
|
||||
return spell->mName;
|
||||
|
||||
if (const ESM::Potion *potion =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::Potion>().search (id))
|
||||
return potion->mName;
|
||||
|
||||
if (const ESM::Ingredient *ingredient =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::Ingredient>().search (id))
|
||||
return ingredient->mName;
|
||||
|
||||
throw std::runtime_error ("ID " + id + " has no display name");
|
||||
}
|
||||
|
||||
ESM::EffectList SpellIcons::getSpellEffectList (const std::string& id)
|
||||
{
|
||||
if (const ESM::Enchantment* enchantment =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().search (id))
|
||||
return enchantment->mEffects;
|
||||
|
||||
if (const ESM::Spell *spell =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().search (id))
|
||||
return spell->mEffects;
|
||||
|
||||
if (const ESM::Potion *potion =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::Potion>().search (id))
|
||||
return potion->mEffects;
|
||||
|
||||
if (const ESM::Ingredient *ingredient =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::Ingredient>().search (id))
|
||||
{
|
||||
const ESM::MagicEffect *magicEffect =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find (
|
||||
ingredient->mData.mEffectID[0]);
|
||||
|
||||
ESM::ENAMstruct effect;
|
||||
effect.mEffectID = ingredient->mData.mEffectID[0];
|
||||
effect.mSkill = ingredient->mData.mSkills[0];
|
||||
effect.mAttribute = ingredient->mData.mAttributes[0];
|
||||
effect.mRange = 0;
|
||||
effect.mArea = 0;
|
||||
effect.mDuration = magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration ? 0 : 1;
|
||||
effect.mMagnMin = 1;
|
||||
effect.mMagnMax = 1;
|
||||
ESM::EffectList result;
|
||||
result.mList.push_back (effect);
|
||||
return result;
|
||||
}
|
||||
throw std::runtime_error("ID " + id + " does not have effects");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#define MWGUI_SPELLICONS_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "../mwmechanics/magiceffects.hpp"
|
||||
|
||||
|
@ -34,14 +35,23 @@ namespace MWGui
|
|||
bool mPermanent; // the effect is permanent
|
||||
};
|
||||
|
||||
class EffectSourceVisitor : public MWMechanics::EffectSourceVisitor
|
||||
{
|
||||
public:
|
||||
bool mIsPermanent;
|
||||
|
||||
std::map <int, std::vector<MagicEffectInfo> > mEffectSources;
|
||||
|
||||
virtual void visit (const ESM::ENAMstruct& enam,
|
||||
const std::string& sourceName, float magnitude, float remainingTime = -1);
|
||||
};
|
||||
|
||||
class SpellIcons
|
||||
{
|
||||
public:
|
||||
void updateWidgets(MyGUI::Widget* parent, bool adjustSize);
|
||||
|
||||
private:
|
||||
std::string getSpellDisplayName (const std::string& id);
|
||||
ESM::EffectList getSpellEffectList (const std::string& id);
|
||||
|
||||
std::map<int, MyGUI::ImageBox*> mWidgetMap;
|
||||
};
|
||||
|
|
|
@ -164,12 +164,31 @@ namespace MWMechanics
|
|||
throw std::runtime_error ("ID " + id + " can not produce lasting effects");
|
||||
}
|
||||
|
||||
std::string ActiveSpells::getSpellDisplayName (const std::string& id) const
|
||||
{
|
||||
if (const ESM::Spell *spell =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().search (id))
|
||||
return spell->mName;
|
||||
|
||||
if (const ESM::Potion *potion =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::Potion>().search (id))
|
||||
return potion->mName;
|
||||
|
||||
if (const ESM::Ingredient *ingredient =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::Ingredient>().search (id))
|
||||
return ingredient->mName;
|
||||
|
||||
throw std::runtime_error ("ID " + id + " has no display name");
|
||||
}
|
||||
|
||||
ActiveSpells::ActiveSpells()
|
||||
: mSpellsChanged (false), mLastUpdate (MWBase::Environment::get().getWorld()->getTimeStamp())
|
||||
{}
|
||||
|
||||
bool ActiveSpells::addSpell (const std::string& id, const MWWorld::Ptr& actor, ESM::RangeType range, const std::string& name)
|
||||
bool ActiveSpells::addSpell (const std::string& id, const MWWorld::Ptr& actor, ESM::RangeType range, const std::string& name, int effectIndex)
|
||||
{
|
||||
const CreatureStats& creatureStats = MWWorld::Class::get (actor).getCreatureStats (actor);
|
||||
|
||||
std::pair<ESM::EffectList, std::pair<bool, bool> > effects = getEffectList (id);
|
||||
bool stacks = effects.second.second;
|
||||
|
||||
|
@ -195,7 +214,6 @@ namespace MWMechanics
|
|||
if (effects.second.first)
|
||||
{
|
||||
// ingredient -> special treatment required.
|
||||
const CreatureStats& creatureStats = MWWorld::Class::get (actor).getCreatureStats (actor);
|
||||
const NpcStats& npcStats = MWWorld::Class::get (actor).getNpcStats (actor);
|
||||
|
||||
float x =
|
||||
|
@ -220,6 +238,26 @@ namespace MWMechanics
|
|||
else
|
||||
iter->second = params;
|
||||
|
||||
|
||||
|
||||
/*
|
||||
for (int i=0; i<effects.first.mList.size(); ++i)
|
||||
{
|
||||
if (iter->mRange != range)
|
||||
{
|
||||
params.mDisabled.push_back(true);
|
||||
continue;
|
||||
}
|
||||
|
||||
bool disabled = false;
|
||||
|
||||
int reflect = creatureStats.getMagicEffects().get(ESM::MagicEffect::Reflect).mMagnitude;
|
||||
int roll = std::rand()/ (static_cast<double> (RAND_MAX) + 1) * 100; // [0, 99]
|
||||
if (roll < reflect)
|
||||
disabled = true;
|
||||
}
|
||||
*/
|
||||
|
||||
// Play sounds & particles
|
||||
bool first=true;
|
||||
for (std::vector<ESM::ENAMstruct>::const_iterator iter (effects.first.mList.begin());
|
||||
|
@ -338,4 +376,47 @@ namespace MWMechanics
|
|||
{
|
||||
return mSpells;
|
||||
}
|
||||
|
||||
void ActiveSpells::visitEffectSources(EffectSourceVisitor &visitor) const
|
||||
{
|
||||
for (TContainer::const_iterator it = begin(); it != end(); ++it)
|
||||
{
|
||||
const ESM::EffectList& list = getEffectList(it->first).first;
|
||||
|
||||
float timeScale = MWBase::Environment::get().getWorld()->getTimeScaleFactor();
|
||||
|
||||
int i=0;
|
||||
for (std::vector<ESM::ENAMstruct>::const_iterator effectIt = list.mList.begin();
|
||||
effectIt != list.mList.end(); ++effectIt, ++i)
|
||||
{
|
||||
if (effectIt->mRange != it->second.mRange)
|
||||
continue;
|
||||
|
||||
std::string name;
|
||||
if (it->second.mName.empty())
|
||||
name = getSpellDisplayName(it->first);
|
||||
else
|
||||
name = it->second.mName;
|
||||
|
||||
float remainingTime = effectIt->mDuration +
|
||||
(it->second.mTimeStamp - MWBase::Environment::get().getWorld()->getTimeStamp())*3600/timeScale;
|
||||
float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * it->second.mRandom[i];
|
||||
|
||||
// hack for ingredients
|
||||
if (MWBase::Environment::get().getWorld()->getStore().get<ESM::Ingredient>().search (it->first))
|
||||
{
|
||||
const ESM::MagicEffect *magicEffect =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find (
|
||||
effectIt->mEffectID);
|
||||
|
||||
remainingTime = effectIt->mDuration * it->second.mRandom[i] +
|
||||
(it->second.mTimeStamp - MWBase::Environment::get().getWorld()->getTimeStamp())*3600/timeScale;
|
||||
|
||||
magnitude = static_cast<int> (0.05*it->second.mRandom[i] / (0.1 * magicEffect->mData.mBaseCost));
|
||||
}
|
||||
|
||||
visitor.visit(*effectIt, name, magnitude, remainingTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,6 +35,10 @@ namespace MWMechanics
|
|||
// Random factor for each effect
|
||||
std::vector<float> mRandom;
|
||||
|
||||
// Effect magnitude multiplier. Use 0 to completely disable the effect
|
||||
// (if it was resisted, reflected or absorbed). Use (0,1) for partially resisted.
|
||||
std::vector<bool> mMultiplier;
|
||||
|
||||
// Display name, we need this for enchantments, which don't have a name - so you need to supply the
|
||||
// name of the item with the enchantment to addSpell
|
||||
std::string mName;
|
||||
|
@ -65,17 +69,30 @@ namespace MWMechanics
|
|||
std::pair<ESM::EffectList, std::pair<bool, bool> > getEffectList (const std::string& id) const;
|
||||
///< @return (EffectList, (isIngredient, stacks))
|
||||
|
||||
double timeToExpire (const TIterator& iterator) const;
|
||||
///< Returns time (in in-game hours) until the spell pointed to by \a iterator
|
||||
/// expires.
|
||||
|
||||
const TContainer& getActiveSpells() const;
|
||||
|
||||
TIterator begin() const;
|
||||
|
||||
TIterator end() const;
|
||||
|
||||
std::string getSpellDisplayName (const std::string& id) const;
|
||||
|
||||
public:
|
||||
|
||||
ActiveSpells();
|
||||
|
||||
bool addSpell (const std::string& id, const MWWorld::Ptr& actor, ESM::RangeType range = ESM::RT_Self, const std::string& name = "");
|
||||
bool addSpell (const std::string& id, const MWWorld::Ptr& actor, ESM::RangeType range = ESM::RT_Self, const std::string& name = "", int effectIndex = -1);
|
||||
///< Overwrites an existing spell with the same ID. If the spell does not have any
|
||||
/// non-instant effects, it is ignored.
|
||||
/// @param id
|
||||
/// @param actor
|
||||
/// @param range Only effects with range type \a range will be applied
|
||||
/// @param name Display name for enchantments, since they don't have a name in their record
|
||||
/// @param effectIndex Only apply one specific effect - useful for reflecting spells, since each effect is reflected individually
|
||||
///
|
||||
/// \return Has the spell been added?
|
||||
|
||||
|
@ -86,15 +103,8 @@ namespace MWMechanics
|
|||
|
||||
const MagicEffects& getMagicEffects() const;
|
||||
|
||||
const TContainer& getActiveSpells() const;
|
||||
void visitEffectSources (MWMechanics::EffectSourceVisitor& visitor) const;
|
||||
|
||||
TIterator begin() const;
|
||||
|
||||
TIterator end() const;
|
||||
|
||||
double timeToExpire (const TIterator& iterator) const;
|
||||
///< Returns time (in in-game hours) until the spell pointed to by \a iterator
|
||||
/// expires.
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#define GAME_MWMECHANICS_MAGICEFFECTS_H
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
|
@ -11,6 +12,13 @@ namespace ESM
|
|||
|
||||
namespace MWMechanics
|
||||
{
|
||||
// Used by effect management classes (ActiveSpells, InventoryStore, Spells) to list active effect sources for GUI display
|
||||
struct EffectSourceVisitor
|
||||
{
|
||||
virtual void visit (const ESM::ENAMstruct& enam,
|
||||
const std::string& sourceName, float magnitude, float remainingTime = -1) = 0;
|
||||
};
|
||||
|
||||
struct EffectKey
|
||||
{
|
||||
int mId;
|
||||
|
@ -29,11 +37,12 @@ namespace MWMechanics
|
|||
|
||||
struct EffectParam
|
||||
{
|
||||
int mMagnitude;
|
||||
// Note usually this would be int, but applying partial resistance might introduce decimal point.
|
||||
float mMagnitude;
|
||||
|
||||
EffectParam();
|
||||
|
||||
EffectParam(int magnitude) : mMagnitude(magnitude) {}
|
||||
EffectParam(float magnitude) : mMagnitude(magnitude) {}
|
||||
|
||||
EffectParam& operator+= (const EffectParam& param);
|
||||
|
||||
|
|
|
@ -117,4 +117,27 @@ namespace MWMechanics
|
|||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Spells::visitEffectSources(EffectSourceVisitor &visitor) const
|
||||
{
|
||||
for (TIterator it = begin(); it != end(); ++it)
|
||||
{
|
||||
const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find(it->first);
|
||||
|
||||
// these are the spell types that are permanently in effect
|
||||
if (!(spell->mData.mType == ESM::Spell::ST_Ability)
|
||||
&& !(spell->mData.mType == ESM::Spell::ST_Disease)
|
||||
&& !(spell->mData.mType == ESM::Spell::ST_Curse)
|
||||
&& !(spell->mData.mType == ESM::Spell::ST_Blight))
|
||||
continue;
|
||||
const ESM::EffectList& list = spell->mEffects;
|
||||
int i=0;
|
||||
for (std::vector<ESM::ENAMstruct>::const_iterator effectIt = list.mList.begin();
|
||||
effectIt != list.mList.end(); ++effectIt, ++i)
|
||||
{
|
||||
float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * it->second[i];
|
||||
visitor.visit(*effectIt, spell->mName, magnitude);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
|
||||
#include "../mwworld/ptr.hpp"
|
||||
|
||||
#include "magiceffects.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
struct Spell;
|
||||
|
@ -59,6 +61,8 @@ namespace MWMechanics
|
|||
bool hasCommonDisease() const;
|
||||
|
||||
bool hasBlightDisease() const;
|
||||
|
||||
void visitEffectSources (MWMechanics::EffectSourceVisitor& visitor) const;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -63,7 +63,7 @@ namespace MWMechanics
|
|||
x *= 0.1 * magicEffect->mData.mBaseCost;
|
||||
x *= 0.5 * (it->mMagnMin + it->mMagnMax);
|
||||
x *= it->mArea * 0.05 * magicEffect->mData.mBaseCost;
|
||||
if (it->mRange == ESM::RT_Target)
|
||||
if (magicEffect->mData.mFlags & ESM::MagicEffect::CastTarget)
|
||||
x *= 1.5;
|
||||
static const float fEffectCostMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(
|
||||
"fEffectCostMult")->getFloat();
|
||||
|
|
|
@ -60,7 +60,7 @@ const NpcAnimation::PartBoneMap NpcAnimation::sPartList = createPartListMap();
|
|||
NpcAnimation::~NpcAnimation()
|
||||
{
|
||||
if (!mListenerDisabled)
|
||||
mPtr.getClass().getInventoryStore(mPtr).setListener(NULL);
|
||||
mPtr.getClass().getInventoryStore(mPtr).setListener(NULL, mPtr);
|
||||
|
||||
Ogre::SceneManager *sceneMgr = mInsert->getCreator();
|
||||
for(size_t i = 0;i < ESM::PRT_Count;i++)
|
||||
|
@ -85,7 +85,7 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, int v
|
|||
}
|
||||
|
||||
if (!disableListener)
|
||||
mPtr.getClass().getInventoryStore(mPtr).setListener(this);
|
||||
mPtr.getClass().getInventoryStore(mPtr).setListener(this, mPtr);
|
||||
|
||||
updateNpcBase();
|
||||
}
|
||||
|
|
|
@ -123,7 +123,7 @@ void MWWorld::InventoryStore::equip (int slot, const ContainerStoreIterator& ite
|
|||
flagAsModified();
|
||||
|
||||
fireEquipmentChangedEvent();
|
||||
updateMagicEffects();
|
||||
updateMagicEffects(actor);
|
||||
}
|
||||
|
||||
void MWWorld::InventoryStore::unequipAll(const MWWorld::Ptr& actor)
|
||||
|
@ -150,10 +150,10 @@ MWWorld::ContainerStoreIterator MWWorld::InventoryStore::getSlot (int slot)
|
|||
return mSlots[slot];
|
||||
}
|
||||
|
||||
void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& npc)
|
||||
void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& actor)
|
||||
{
|
||||
const MWMechanics::NpcStats& stats = MWWorld::Class::get(npc).getNpcStats(npc);
|
||||
MWWorld::InventoryStore& invStore = MWWorld::Class::get(npc).getInventoryStore(npc);
|
||||
const MWMechanics::NpcStats& stats = MWWorld::Class::get(actor).getNpcStats(actor);
|
||||
MWWorld::InventoryStore& invStore = MWWorld::Class::get(actor).getInventoryStore(actor);
|
||||
|
||||
TSlots slots_;
|
||||
initSlots (slots_);
|
||||
|
@ -211,15 +211,15 @@ void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& npc)
|
|||
}
|
||||
}
|
||||
|
||||
switch(MWWorld::Class::get (test).canBeEquipped (test, npc).first)
|
||||
switch(MWWorld::Class::get (test).canBeEquipped (test, actor).first)
|
||||
{
|
||||
case 0:
|
||||
continue;
|
||||
case 2:
|
||||
invStore.unequipSlot(MWWorld::InventoryStore::Slot_CarriedLeft, npc);
|
||||
invStore.unequipSlot(MWWorld::InventoryStore::Slot_CarriedLeft, actor);
|
||||
break;
|
||||
case 3:
|
||||
invStore.unequipSlot(MWWorld::InventoryStore::Slot_CarriedRight, npc);
|
||||
invStore.unequipSlot(MWWorld::InventoryStore::Slot_CarriedRight, actor);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -255,7 +255,7 @@ void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& npc)
|
|||
{
|
||||
mSlots.swap (slots_);
|
||||
fireEquipmentChangedEvent();
|
||||
updateMagicEffects();
|
||||
updateMagicEffects(actor);
|
||||
flagAsModified();
|
||||
}
|
||||
}
|
||||
|
@ -265,7 +265,7 @@ const MWMechanics::MagicEffects& MWWorld::InventoryStore::getMagicEffects() cons
|
|||
return mMagicEffects;
|
||||
}
|
||||
|
||||
void MWWorld::InventoryStore::updateMagicEffects()
|
||||
void MWWorld::InventoryStore::updateMagicEffects(const Ptr& actor)
|
||||
{
|
||||
// To avoid excessive updates during auto-equip
|
||||
if (!mUpdatesEnabled)
|
||||
|
@ -293,19 +293,41 @@ void MWWorld::InventoryStore::updateMagicEffects()
|
|||
continue;
|
||||
|
||||
// Roll some dice, one for each effect
|
||||
std::vector<float> random;
|
||||
random.resize(enchantment.mEffects.mList.size());
|
||||
for (unsigned int i=0; i<random.size();++i)
|
||||
random[i] = static_cast<float> (std::rand()) / RAND_MAX;
|
||||
std::vector<EffectParams> params;
|
||||
params.resize(enchantment.mEffects.mList.size());
|
||||
for (unsigned int i=0; i<params.size();++i)
|
||||
params[i].mRandom = static_cast<float> (std::rand()) / RAND_MAX;
|
||||
|
||||
bool existed = (mPermanentMagicEffectMagnitudes.find((**iter).getCellRef().mRefID) != mPermanentMagicEffectMagnitudes.end());
|
||||
if (!existed)
|
||||
{
|
||||
// Try resisting each effect
|
||||
int i=0;
|
||||
for (std::vector<ESM::ENAMstruct>::const_iterator effectIt (enchantment.mEffects.mList.begin());
|
||||
effectIt!=enchantment.mEffects.mList.end(); ++effectIt)
|
||||
{
|
||||
const ESM::MagicEffect *magicEffect =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find (
|
||||
effectIt->mEffectID);
|
||||
|
||||
//const MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor);
|
||||
|
||||
float resisted = 0;
|
||||
if (magicEffect->mData.mFlags & ESM::MagicEffect::Harmful)
|
||||
{
|
||||
|
||||
}
|
||||
params[i].mMultiplier = (100.f - resisted) / 100.f;
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
|
||||
// Note that using the RefID as a key here is not entirely correct.
|
||||
// Consider equipping the same item twice (e.g. a ring)
|
||||
// However, permanent enchantments with a random magnitude are kind of an exploit anyway,
|
||||
// so it doesn't really matter if both items will get the same magnitude. *Extreme* edge case.
|
||||
mPermanentMagicEffectMagnitudes[(**iter).getCellRef().mRefID] = random;
|
||||
mPermanentMagicEffectMagnitudes[(**iter).getCellRef().mRefID] = params;
|
||||
}
|
||||
|
||||
int i=0;
|
||||
|
@ -316,6 +338,10 @@ void MWWorld::InventoryStore::updateMagicEffects()
|
|||
MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find (
|
||||
effectIt->mEffectID);
|
||||
|
||||
// Fully resisted?
|
||||
if (params[i].mMultiplier == 0)
|
||||
continue;
|
||||
|
||||
if (!existed)
|
||||
{
|
||||
// During first auto equip, we don't play any sounds.
|
||||
|
@ -325,7 +351,10 @@ void MWWorld::InventoryStore::updateMagicEffects()
|
|||
!mFirstAutoEquip && effectIt == enchantment.mEffects.mList.begin());
|
||||
}
|
||||
|
||||
mMagicEffects.add (*effectIt, random[i]);
|
||||
float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * params[i].mRandom;
|
||||
magnitude *= params[i].mMultiplier;
|
||||
if (magnitude)
|
||||
mMagicEffects.add (*effectIt, magnitude);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
@ -467,7 +496,7 @@ MWWorld::ContainerStoreIterator MWWorld::InventoryStore::unequipSlot(int slot, c
|
|||
}
|
||||
|
||||
fireEquipmentChangedEvent();
|
||||
updateMagicEffects();
|
||||
updateMagicEffects(actor);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
@ -487,10 +516,10 @@ MWWorld::ContainerStoreIterator MWWorld::InventoryStore::unequipItem(const MWWor
|
|||
throw std::runtime_error ("attempt to unequip an item that is not currently equipped");
|
||||
}
|
||||
|
||||
void MWWorld::InventoryStore::setListener(InventoryStoreListener *listener)
|
||||
void MWWorld::InventoryStore::setListener(InventoryStoreListener *listener, const Ptr& actor)
|
||||
{
|
||||
mListener = listener;
|
||||
updateMagicEffects();
|
||||
updateMagicEffects(actor);
|
||||
}
|
||||
|
||||
void MWWorld::InventoryStore::fireEquipmentChangedEvent()
|
||||
|
@ -500,3 +529,36 @@ void MWWorld::InventoryStore::fireEquipmentChangedEvent()
|
|||
if (mListener)
|
||||
mListener->equipmentChanged();
|
||||
}
|
||||
|
||||
void MWWorld::InventoryStore::visitEffectSources(MWMechanics::EffectSourceVisitor &visitor)
|
||||
{
|
||||
for (TSlots::const_iterator iter (mSlots.begin()); iter!=mSlots.end(); ++iter)
|
||||
{
|
||||
if (*iter==end())
|
||||
continue;
|
||||
|
||||
std::string enchantmentId = MWWorld::Class::get (**iter).getEnchantment (**iter);
|
||||
if (enchantmentId.empty())
|
||||
continue;
|
||||
|
||||
const ESM::Enchantment& enchantment =
|
||||
*MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().find (enchantmentId);
|
||||
|
||||
if (enchantment.mData.mType != ESM::Enchantment::ConstantEffect)
|
||||
continue;
|
||||
|
||||
if (mPermanentMagicEffectMagnitudes.find((**iter).getCellRef().mRefID) == mPermanentMagicEffectMagnitudes.end())
|
||||
continue;
|
||||
|
||||
int i=0;
|
||||
for (std::vector<ESM::ENAMstruct>::const_iterator effectIt (enchantment.mEffects.mList.begin());
|
||||
effectIt!=enchantment.mEffects.mList.end(); ++effectIt)
|
||||
{
|
||||
float random = mPermanentMagicEffectMagnitudes[(**iter).getCellRef().mRefID][i].mRandom;
|
||||
float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * random;
|
||||
visitor.visit(*effectIt, (**iter).getClass().getName(**iter), magnitude);
|
||||
|
||||
++i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -74,7 +74,15 @@ namespace MWWorld
|
|||
|
||||
// Vanilla allows permanent effects with a random magnitude, so it needs to be stored here.
|
||||
// We also need this to only play sounds and particle effects when the item is equipped, rather than on every update.
|
||||
typedef std::map<std::string, std::vector<float> > TEffectMagnitudes;
|
||||
struct EffectParams
|
||||
{
|
||||
// Modifier to scale between min and max magnitude
|
||||
float mRandom;
|
||||
// Multiplier for when an effect was fully or partially resisted
|
||||
float mMultiplier;
|
||||
};
|
||||
|
||||
typedef std::map<std::string, std::vector<EffectParams> > TEffectMagnitudes;
|
||||
TEffectMagnitudes mPermanentMagicEffectMagnitudes;
|
||||
|
||||
typedef std::vector<ContainerStoreIterator> TSlots;
|
||||
|
@ -88,7 +96,7 @@ namespace MWWorld
|
|||
|
||||
void initSlots (TSlots& slots_);
|
||||
|
||||
void updateMagicEffects();
|
||||
void updateMagicEffects(const Ptr& actor);
|
||||
|
||||
void fireEquipmentChangedEvent();
|
||||
|
||||
|
@ -127,7 +135,7 @@ namespace MWWorld
|
|||
void unequipAll(const MWWorld::Ptr& actor);
|
||||
///< Unequip all currently equipped items.
|
||||
|
||||
void autoEquip (const MWWorld::Ptr& npc);
|
||||
void autoEquip (const MWWorld::Ptr& actor);
|
||||
///< Auto equip items according to stats and item value.
|
||||
|
||||
const MWMechanics::MagicEffects& getMagicEffects() const;
|
||||
|
@ -160,8 +168,10 @@ namespace MWWorld
|
|||
/// (it can be re-stacked so its count may be different than when it
|
||||
/// was equipped).
|
||||
|
||||
void setListener (InventoryStoreListener* listener);
|
||||
void setListener (InventoryStoreListener* listener, const Ptr& actor);
|
||||
///< Set a listener for various events, see \a InventoryStoreListener
|
||||
|
||||
void visitEffectSources (MWMechanics::EffectSourceVisitor& visitor);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue