Merge remote-tracking branch 'scrawl/master'

pull/136/head
Marc Zinnschlag 11 years ago
commit 3f25036630

@ -69,7 +69,7 @@ add_openmw_dir (mwclass
add_openmw_dir (mwmechanics
mechanicsmanagerimp stat character creaturestats magiceffects movement actors objects
drawstate spells activespells npcstats aipackage aisequence alchemy aiwander aitravel aifollow
aiescort aiactivate repair enchanting pathfinding security spellsuccess
aiescort aiactivate repair enchanting pathfinding security spellsuccess spellcasting
)
add_openmw_dir (mwbase

@ -468,9 +468,9 @@ namespace MWClass
{
weapon.getCellRef().mEnchantmentCharge -= castCost;
// Touch
othercls.getCreatureStats(victim).getActiveSpells().addSpell(enchantmentName, victim, ESM::RT_Touch, weapon.getClass().getName(weapon));
othercls.getCreatureStats(victim).getActiveSpells().addSpell(enchantmentName, victim, ptr, ESM::RT_Touch, weapon.getClass().getName(weapon));
// Self
getCreatureStats(ptr).getActiveSpells().addSpell(enchantmentName, ptr, ESM::RT_Self, weapon.getClass().getName(weapon));
getCreatureStats(ptr).getActiveSpells().addSpell(enchantmentName, ptr, ptr, ESM::RT_Self, weapon.getClass().getName(weapon));
// Target
MWBase::Environment::get().getWorld()->launchProjectile(enchantmentName, enchantment->mEffects, ptr, weapon.getClass().getName(weapon));
}
@ -936,7 +936,7 @@ namespace MWClass
/// \todo consider instant effects
return stats.getActiveSpells().addSpell (id, actor);
return stats.getActiveSpells().addSpell (id, actor, actor);
}
void Npc::skillUsageSucceeded (const MWWorld::Ptr& ptr, int skill, int usageType) const

@ -5,7 +5,7 @@
#include "../mwworld/player.hpp"
#include "../mwworld/inventorystore.hpp"
#include "../mwworld/actionequip.hpp"
#include "../mwmechanics/spellsuccess.hpp"
#include "../mwmechanics/spellcasting.hpp"
#include "../mwgui/inventorywindow.hpp"
#include "../mwgui/bookwindow.hpp"
#include "../mwgui/scrollwindow.hpp"

@ -9,7 +9,7 @@
#include "../mwworld/player.hpp"
#include "../mwmechanics/spellsuccess.hpp"
#include "../mwmechanics/spellcasting.hpp"
#include "tooltips.hpp"
#include "class.hpp"
@ -89,6 +89,8 @@ namespace MWGui
mEffect.mMagnMax = 1;
mEffect.mDuration = 1;
mEffect.mArea = 0;
mEffect.mSkill = -1;
mEffect.mAttribute = -1;
eventEffectAdded(mEffect);
onRangeButtonClicked(mRangeButton);

@ -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
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;
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);
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);
EffectSourceVisitor visitor;
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;
// permanent item enchantments & permanent spells
visitor.mIsPermanent = true;
MWWorld::InventoryStore& store = MWWorld::Class::get(player).getInventoryStore(player);
store.visitEffectSources(visitor);
stats.getSpells().visitEffectSources(visitor);
effectInfo.mMagnitude = static_cast<int> (0.05*randomFactor / (0.1 * magicEffect->mData.mBaseCost));
}
// now add lasting effects
visitor.mIsPermanent = false;
stats.getActiveSpells().visitEffectSources(visitor);
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;
};

@ -9,7 +9,7 @@
#include "../mwworld/inventorystore.hpp"
#include "../mwworld/actionequip.hpp"
#include "../mwmechanics/spellsuccess.hpp"
#include "../mwmechanics/spellcasting.hpp"
#include "spellicons.hpp"
#include "inventorywindow.hpp"

@ -16,11 +16,14 @@
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwbase/soundmanager.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwrender/animation.hpp"
#include "../mwworld/class.hpp"
#include "../mwmechanics/spellcasting.hpp"
#include "creaturestats.hpp"
#include "npcstats.hpp"
@ -74,7 +77,7 @@ namespace MWMechanics
for (std::vector<ESM::ENAMstruct>::const_iterator effectIter (effects.first.mList.begin());
effectIter!=effects.first.mList.end(); ++effectIter, ++i)
{
float magnitude = iter->second.mRandom[i];
float random = iter->second.mRandom[i];
if (effectIter->mRange != iter->second.mRange)
continue;
@ -83,7 +86,7 @@ namespace MWMechanics
int duration = effectIter->mDuration;
if (effects.second.first)
duration *= magnitude;
duration *= random;
MWWorld::TimeStamp end = start;
end += static_cast<double> (duration)*
@ -102,19 +105,21 @@ namespace MWMechanics
if (effectIter->mDuration==0)
{
param.mMagnitude =
static_cast<int> (magnitude / (0.1 * magicEffect->mData.mBaseCost));
static_cast<int> (random / (0.1 * magicEffect->mData.mBaseCost));
}
else
{
param.mMagnitude =
static_cast<int> (0.05*magnitude / (0.1 * magicEffect->mData.mBaseCost));
static_cast<int> (0.05*random / (0.1 * magicEffect->mData.mBaseCost));
}
}
else
param.mMagnitude = static_cast<int> (
(effectIter->mMagnMax-effectIter->mMagnMin)*magnitude + effectIter->mMagnMin);
mEffects.add (*effectIter, param);
(effectIter->mMagnMax-effectIter->mMagnMin)*random + effectIter->mMagnMin);
param.mMagnitude *= iter->second.mMultiplier[i];
if (param.mMagnitude)
mEffects.add (*effectIter, param);
}
}
}
@ -164,12 +169,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, const MWWorld::Ptr& caster, 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;
@ -178,6 +202,8 @@ namespace MWMechanics
for (std::vector<ESM::ENAMstruct>::const_iterator iter (effects.first.mList.begin());
iter!=effects.first.mList.end(); ++iter)
{
if (iter->mRange != range)
continue;
if (iter->mDuration)
{
found = true;
@ -185,54 +211,89 @@ namespace MWMechanics
}
}
// If none of the effects need to apply, no need to add the spell
if (!found)
return false;
TContainer::iterator iter = mSpells.find (id);
float random = static_cast<float> (std::rand()) / RAND_MAX;
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 =
(npcStats.getSkill (ESM::Skill::Alchemy).getModified() +
0.2 * creatureStats.getAttribute (1).getModified()
+ 0.1 * creatureStats.getAttribute (7).getModified())
* creatureStats.getFatigueTerm();
random *= 100;
random = random / std::min (x, 100.0f);
random *= 0.25 * x;
}
ActiveSpellParams params;
for (unsigned int i=0; i<effects.first.mList.size(); ++i)
params.mRandom.push_back(static_cast<float> (std::rand()) / RAND_MAX);
{
float random = static_cast<float> (std::rand()) / RAND_MAX;
if (effects.second.first)
{
// ingredient -> special treatment required.
const NpcStats& npcStats = MWWorld::Class::get (actor).getNpcStats (actor);
float x =
(npcStats.getSkill (ESM::Skill::Alchemy).getModified() +
0.2 * creatureStats.getAttribute (1).getModified()
+ 0.1 * creatureStats.getAttribute (7).getModified())
* creatureStats.getFatigueTerm();
random *= 100;
random = random / std::min (x, 100.0f);
random *= 0.25 * x;
}
params.mRandom.push_back(random);
}
params.mRange = range;
params.mTimeStamp = MWBase::Environment::get().getWorld()->getTimeStamp();
params.mName = name;
params.mMultiplier.resize(effects.first.mList.size(), 1);
if (iter==mSpells.end() || stacks)
mSpells.insert (std::make_pair (id, params));
else
iter->second = params;
// Play sounds & particles
bool first=true;
for (std::vector<ESM::ENAMstruct>::const_iterator iter (effects.first.mList.begin());
iter!=effects.first.mList.end(); ++iter)
/*
for (int i=0; i<effects.first.mList.size(); ++i)
{
if (iter->mRange != range)
{
params.mDisabled.push_back(true);
continue;
}
// TODO: For Area effects, launch a growing particle effect that applies the effect to more actors as it hits them. Best managed in World.
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;
}
*/
bool first=true;
int i = 0;
for (std::vector<ESM::ENAMstruct>::const_iterator effectIt (effects.first.mList.begin());
effectIt!=effects.first.mList.end(); ++effectIt, ++i)
{
if (effectIt->mRange != range)
continue;
const ESM::MagicEffect *magicEffect =
MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find (
iter->mEffectID);
effectIt->mEffectID);
if (caster.getRefData().getHandle() == "player" && actor != caster
&& magicEffect->mData.mFlags & ESM::MagicEffect::Harmful)
MWBase::Environment::get().getWindowManager()->setEnemy(actor);
// Try resisting effect in case its harmful
const ESM::Spell *spell =
MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().search (id);
params.mMultiplier[i] = MWMechanics::getEffectMultiplier(effectIt->mEffectID, actor, caster, spell);
if (params.mMultiplier[i] == 0)
{
if (actor.getRefData().getHandle() == "player")
MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicPCResisted}");
else
MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicTargetResisted}");
}
// If fully resisted, don't play sounds or particles
if (params.mMultiplier[i] == 0)
continue;
// TODO: For Area effects, launch a growing particle effect that applies the effect to more actors as it hits them. Best managed in World.
// Only the sound of the first effect plays
if (first)
@ -258,6 +319,11 @@ namespace MWMechanics
first = false;
}
if (iter==mSpells.end() || stacks)
mSpells.insert (std::make_pair (id, params));
else
iter->second = params;
mSpellsChanged = true;
return true;
@ -338,4 +404,49 @@ 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));
}
magnitude *= it->second.mMultiplier[i];
if (magnitude)
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<float> 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;
@ -53,7 +57,7 @@ namespace MWMechanics
private:
mutable TContainer mSpells; // spellId, (time of casting, relative magnitude)
mutable TContainer mSpells;
mutable MagicEffects mEffects;
mutable bool mSpellsChanged;
mutable MWWorld::TimeStamp mLastUpdate;
@ -65,17 +69,31 @@ 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, const MWWorld::Ptr& caster, 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 actor actor to add the spell to
/// @param caster actor who casted the spell
/// @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 +104,8 @@ namespace MWMechanics
const MagicEffects& getMagicEffects() const;
const TContainer& getActiveSpells() const;
TIterator begin() const;
TIterator end() const;
void visitEffectSources (MWMechanics::EffectSourceVisitor& visitor) const;
double timeToExpire (const TIterator& iterator) const;
///< Returns time (in in-game hours) until the spell pointed to by \a iterator
/// expires.
};
}

@ -29,7 +29,7 @@ namespace MWMechanics
// magic effects
adjustMagicEffects (ptr);
calculateDynamicStats (ptr);
calculateCreatureStatModifiers (ptr);
calculateCreatureStatModifiers (ptr, duration);
if(!MWBase::Environment::get().getWindowManager()->isGuiMode())
{
@ -145,7 +145,7 @@ namespace MWMechanics
stats.setFatigue (fatigue);
}
void Actors::calculateCreatureStatModifiers (const MWWorld::Ptr& ptr)
void Actors::calculateCreatureStatModifiers (const MWWorld::Ptr& ptr, float duration)
{
CreatureStats &creatureStats = MWWorld::Class::get(ptr).getCreatureStats(ptr);
const MagicEffects &effects = creatureStats.getMagicEffects();
@ -167,10 +167,25 @@ namespace MWMechanics
stat.setModifier(effects.get(EffectKey(ESM::MagicEffect::FortifyHealth+i)).mMagnitude -
effects.get(EffectKey(ESM::MagicEffect::DrainHealth+i)).mMagnitude);
float currentDiff = creatureStats.getMagicEffects().get(EffectKey(ESM::MagicEffect::RestoreHealth+i)).mMagnitude
- creatureStats.getMagicEffects().get(EffectKey(ESM::MagicEffect::DamageHealth+i)).mMagnitude;
stat.setCurrent(stat.getCurrent() + currentDiff * duration);
creatureStats.setDynamic(i, stat);
}
// Apply damage ticks
int damageEffects[] = {
ESM::MagicEffect::FireDamage, ESM::MagicEffect::ShockDamage, ESM::MagicEffect::FrostDamage, ESM::MagicEffect::Poison
};
for (unsigned int i=0; i<sizeof(damageEffects)/sizeof(int); ++i)
{
float magnitude = creatureStats.getMagicEffects().get(EffectKey(damageEffects[i])).mMagnitude;
DynamicStat<float> health = creatureStats.getHealth();
health.setCurrent(health.getCurrent() - magnitude * duration);
creatureStats.setHealth(health);
}
}
void Actors::calculateNpcStatModifiers (const MWWorld::Ptr& ptr)

@ -40,7 +40,7 @@ namespace MWMechanics
void calculateDynamicStats (const MWWorld::Ptr& ptr);
void calculateCreatureStatModifiers (const MWWorld::Ptr& ptr);
void calculateCreatureStatModifiers (const MWWorld::Ptr& ptr, float duration);
void calculateNpcStatModifiers (const MWWorld::Ptr& ptr);
void calculateRestoration (const MWWorld::Ptr& ptr, float duration);

@ -782,7 +782,7 @@ void CharacterController::update(float duration)
if(!onground && !flying && !inwater)
{
// The player is in the air (either getting up —ascending part of jump— or falling).
// In the air (either getting up —ascending part of jump— or falling).
if (world->isSlowFalling(mPtr))
{
@ -817,8 +817,7 @@ void CharacterController::update(float duration)
}
else if(vec.z > 0.0f && mJumpState == JumpState_None)
{
// The player has started a jump.
// Started a jump.
float z = cls.getJump(mPtr);
if(vec.x == 0 && vec.y == 0)
vec = Ogre::Vector3(0.0f, 0.0f, z);
@ -829,7 +828,8 @@ void CharacterController::update(float duration)
}
// advance acrobatics
cls.skillUsageSucceeded(mPtr, ESM::Skill::Acrobatics, 0);
if (mPtr.getRefData().getHandle() == "player")
cls.skillUsageSucceeded(mPtr, ESM::Skill::Acrobatics, 0);
// decrease fatigue
const MWWorld::Store<ESM::GameSetting> &gmst = world->getStore().get<ESM::GameSetting>();
@ -843,8 +843,6 @@ void CharacterController::update(float duration)
}
else if(mJumpState == JumpState_Falling)
{
// The player is landing.
forcestateupdate = true;
mJumpState = JumpState_Landing;
vec.z = 0.0f;
@ -861,7 +859,8 @@ void CharacterController::update(float duration)
cls.getCreatureStats(mPtr).setHealth(health);
// report acrobatics progression
cls.skillUsageSucceeded(mPtr, ESM::Skill::Acrobatics, 1);
if (mPtr.getRefData().getHandle() == "player")
cls.skillUsageSucceeded(mPtr, ESM::Skill::Acrobatics, 1);
const float acrobaticsSkill = cls.getNpcStats(mPtr).getSkill(ESM::Skill::Acrobatics).getModified();
if (healthLost > (acrobaticsSkill * fatigueTerm))

@ -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);

@ -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();
@ -114,6 +114,72 @@ namespace MWMechanics
return school;
}
/// @return >=100 for fully resisted. can also return negative value for damage amplification.
inline float getEffectResistance (short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, const ESM::Spell* spell = NULL)
{
const ESM::MagicEffect *magicEffect =
MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find (
effectId);
const MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor);
float resisted = 0;
if (magicEffect->mData.mFlags & ESM::MagicEffect::Harmful)
{
short resistanceEffect = ESM::MagicEffect::getResistanceEffect(effectId);
short weaknessEffect = ESM::MagicEffect::getWeaknessEffect(effectId);
float resistance = 0;
if (resistanceEffect != -1)
resistance += stats.getMagicEffects().get(resistanceEffect).mMagnitude;
if (weaknessEffect != -1)
resistance -= stats.getMagicEffects().get(weaknessEffect).mMagnitude;
float willpower = stats.getAttribute(ESM::Attribute::Willpower).getModified();
float luck = stats.getAttribute(ESM::Attribute::Luck).getModified();
float x = (willpower + 0.1 * luck) * stats.getFatigueTerm();
// This makes spells that are easy to cast harder to resist and vice versa
if (spell != NULL)
{
float castChance = getSpellSuccessChance(spell, caster);
if (castChance > 0)
x *= 50 / castChance;
}
float roll = static_cast<float>(std::rand()) / RAND_MAX * 100;
if (magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude)
roll -= resistance;
if (x <= roll)
x = 0;
else
{
if (magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude)
x = 100;
else
x = roll / std::min(x, 100.f);
}
x = std::min(x + resistance, 100.f);
resisted = x;
}
return resisted;
}
inline float getEffectMultiplier(short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, const ESM::Spell* spell = NULL)
{
float resistance = getEffectResistance(effectId, actor, caster, spell);
if (resistance >= 0)
return 1 - resistance / 100.f;
else
return -(resistance-100) / 100.f;
}
}
#endif

@ -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;
};
}

@ -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();
}

@ -16,7 +16,7 @@ namespace MWWorld
// TODO: Apply RT_Self effects on the door / container that triggered the trap. Not terribly useful, but you could
// make it lock itself when activated for example.
actor.getClass().getCreatureStats(actor).getActiveSpells().addSpell(mSpellId, actor, ESM::RT_Touch);
actor.getClass().getCreatureStats(actor).getActiveSpells().addSpell(mSpellId, actor, actor, ESM::RT_Touch);
const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find(mSpellId);

@ -11,6 +11,8 @@
#include "../mwbase/windowmanager.hpp"
#include "../mwmechanics/npcstats.hpp"
#include "../mwmechanics/spellcasting.hpp"
#include "esmstore.hpp"
#include "class.hpp"
@ -123,7 +125,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 +152,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 +213,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 +257,7 @@ void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& npc)
{
mSlots.swap (slots_);
fireEquipmentChangedEvent();
updateMagicEffects();
updateMagicEffects(actor);
flagAsModified();
}
}
@ -265,7 +267,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)
@ -292,30 +294,46 @@ void MWWorld::InventoryStore::updateMagicEffects()
if (enchantment.mData.mType != ESM::Enchantment::ConstantEffect)
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;
bool existed = (mPermanentMagicEffectMagnitudes.find((**iter).getCellRef().mRefID) != mPermanentMagicEffectMagnitudes.end());
if (!existed)
{
// Roll some dice, one for each effect
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;
// 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)
{
params[i].mMultiplier = MWMechanics::getEffectMultiplier(effectIt->mEffectID, actor, actor);
++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;
}
else
params = mPermanentMagicEffectMagnitudes[(**iter).getCellRef().mRefID];
int i=0;
for (std::vector<ESM::ENAMstruct>::const_iterator effectIt (enchantment.mEffects.mList.begin());
effectIt!=enchantment.mEffects.mList.end(); ++effectIt)
effectIt!=enchantment.mEffects.mList.end(); ++effectIt, ++i)
{
const ESM::MagicEffect *magicEffect =
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,8 +343,10 @@ void MWWorld::InventoryStore::updateMagicEffects()
!mFirstAutoEquip && effectIt == enchantment.mEffects.mList.begin());
}
mMagicEffects.add (*effectIt, random[i]);
++i;
float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * params[i].mRandom;
magnitude *= params[i].mMultiplier;
if (magnitude)
mMagicEffects.add (*effectIt, magnitude);
}
}
}
@ -467,7 +487,7 @@ MWWorld::ContainerStoreIterator MWWorld::InventoryStore::unequipSlot(int slot, c
}
fireEquipmentChangedEvent();
updateMagicEffects();
updateMagicEffects(actor);
return retval;
}
@ -487,10 +507,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 +520,37 @@ 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)
{
const EffectParams& params = mPermanentMagicEffectMagnitudes[(**iter).getCellRef().mRefID][i];
float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * params.mRandom;
magnitude *= params.mMultiplier;
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);
};
}

@ -26,7 +26,7 @@
#include "../mwmechanics/creaturestats.hpp"
#include "../mwmechanics/movement.hpp"
#include "../mwmechanics/npcstats.hpp"
#include "../mwmechanics/spellsuccess.hpp"
#include "../mwmechanics/spellcasting.hpp"
#include "../mwrender/sky.hpp"
@ -2158,7 +2158,7 @@ namespace MWWorld
// Now apply the spell!
// Apply Self portion
actor.getClass().getCreatureStats(actor).getActiveSpells().addSpell(selectedSpell, actor, ESM::RT_Self, sourceName);
actor.getClass().getCreatureStats(actor).getActiveSpells().addSpell(selectedSpell, actor, actor, ESM::RT_Self, sourceName);
// Apply Touch portion
// TODO: Distance is probably incorrect, and should it be hardcoded?
@ -2166,7 +2166,7 @@ namespace MWWorld
if (!contact.first.isEmpty())
{
if (contact.first.getClass().isActor())
contact.first.getClass().getCreatureStats(contact.first).getActiveSpells().addSpell(selectedSpell, contact.first, ESM::RT_Touch, sourceName);
contact.first.getClass().getCreatureStats(contact.first).getActiveSpells().addSpell(selectedSpell, contact.first, actor, ESM::RT_Touch, sourceName);
else
{
// We hit a non-actor, e.g. a door. Only instant effects are relevant.

@ -81,6 +81,100 @@ void MagicEffect::save(ESMWriter &esm) const
esm.writeHNOString("DESC", mDescription);
}
short MagicEffect::getResistanceEffect(short effect)
{
// Source https://wiki.openmw.org/index.php?title=Research:Magic#Effect_attribute
// <Effect, Effect providing resistance against first effect>
std::map<short, short> effects;
effects[DisintegrateArmor] = Sanctuary;
effects[DisintegrateWeapon] = Sanctuary;
for (int i=0; i<5; ++i)
effects[DrainAttribute+i] = ResistMagicka;
for (int i=0; i<5; ++i)
effects[DamageAttribute+i] = ResistMagicka;
for (int i=0; i<5; ++i)
effects[AbsorbAttribute+i] = ResistMagicka;
for (int i=0; i<10; ++i)
effects[WeaknessToFire+i] = ResistMagicka;
effects[Burden] = ResistMagicka;
effects[Charm] = ResistMagicka;
effects[Silence] = ResistMagicka;
effects[Blind] = ResistMagicka;
effects[Sound] = ResistMagicka;
for (int i=0; i<2; ++i)
{
effects[CalmHumanoid] = ResistMagicka;
effects[FrenzyHumanoid] = ResistMagicka;
effects[DemoralizeHumanoid] = ResistMagicka;
effects[RallyHumanoid] = ResistMagicka;
}
effects[TurnUndead] = ResistMagicka;
effects[FireDamage] = ResistFire;
effects[FrostDamage] = ResistFrost;
effects[ShockDamage] = ResistShock;
effects[Vampirism] = ResistCommonDisease;
effects[Corprus] = ResistCorprusDisease;
effects[Poison] = ResistPoison;
effects[Paralyze] = ResistParalysis;
if (effects.find(effect) != effects.end())
return effects[effect];
else
return -1;
}
short MagicEffect::getWeaknessEffect(short effect)
{
std::map<short, short> effects;
effects[DisintegrateArmor] = Sanctuary;
effects[DisintegrateWeapon] = Sanctuary;
for (int i=0; i<5; ++i)
effects[DrainAttribute+i] = WeaknessToMagicka;
for (int i=0; i<5; ++i)
effects[DamageAttribute+i] = WeaknessToMagicka;
for (int i=0; i<5; ++i)
effects[AbsorbAttribute+i] = WeaknessToMagicka;
for (int i=0; i<10; ++i)
effects[WeaknessToFire+i] = WeaknessToMagicka;
effects[Burden] = WeaknessToMagicka;
effects[Charm] = WeaknessToMagicka;
effects[Silence] = WeaknessToMagicka;
effects[Blind] = WeaknessToMagicka;
effects[Sound] = WeaknessToMagicka;
for (int i=0; i<2; ++i)
{
effects[CalmHumanoid] = WeaknessToMagicka;
effects[FrenzyHumanoid] = WeaknessToMagicka;
effects[DemoralizeHumanoid] = WeaknessToMagicka;
effects[RallyHumanoid] = WeaknessToMagicka;
}
effects[TurnUndead] = WeaknessToMagicka;
effects[FireDamage] = WeaknessToFire;
effects[FrostDamage] = WeaknessToFrost;
effects[ShockDamage] = WeaknessToShock;
effects[Vampirism] = WeaknessToCommonDisease;
effects[Corprus] = WeaknessToCorprusDisease;
effects[Poison] = WeaknessToPoison;
// Weakness to magicka or -1 ?
effects[Paralyze] = WeaknessToMagicka;
if (effects.find(effect) != effects.end())
return effects[effect];
else
return -1;
}
static std::map<short,std::string> genNameMap()
{

@ -58,6 +58,12 @@ struct MagicEffect
static const std::string &effectIdToString(short effectID);
static short effectStringToId(const std::string &effect);
/// Returns the effect that provides resistance against \a effect (or -1 if there's none)
static short getResistanceEffect(short effect);
/// Returns the effect that induces weakness against \a effect (or -1 if there's none)
static short getWeaknessEffect(short effect);
MagnitudeDisplayType getMagnitudeDisplayType() const;

Loading…
Cancel
Save