mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-03-03 14:19:41 +00:00
Merge remote-tracking branch 'scrawl/master'
This commit is contained in:
commit
3f25036630
23 changed files with 540 additions and 272 deletions
|
@ -69,7 +69,7 @@ add_openmw_dir (mwclass
|
||||||
add_openmw_dir (mwmechanics
|
add_openmw_dir (mwmechanics
|
||||||
mechanicsmanagerimp stat character creaturestats magiceffects movement actors objects
|
mechanicsmanagerimp stat character creaturestats magiceffects movement actors objects
|
||||||
drawstate spells activespells npcstats aipackage aisequence alchemy aiwander aitravel aifollow
|
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
|
add_openmw_dir (mwbase
|
||||||
|
|
|
@ -468,9 +468,9 @@ namespace MWClass
|
||||||
{
|
{
|
||||||
weapon.getCellRef().mEnchantmentCharge -= castCost;
|
weapon.getCellRef().mEnchantmentCharge -= castCost;
|
||||||
// Touch
|
// 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
|
// 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
|
// Target
|
||||||
MWBase::Environment::get().getWorld()->launchProjectile(enchantmentName, enchantment->mEffects, ptr, weapon.getClass().getName(weapon));
|
MWBase::Environment::get().getWorld()->launchProjectile(enchantmentName, enchantment->mEffects, ptr, weapon.getClass().getName(weapon));
|
||||||
}
|
}
|
||||||
|
@ -936,7 +936,7 @@ namespace MWClass
|
||||||
|
|
||||||
/// \todo consider instant effects
|
/// \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
|
void Npc::skillUsageSucceeded (const MWWorld::Ptr& ptr, int skill, int usageType) const
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
#include "../mwworld/player.hpp"
|
#include "../mwworld/player.hpp"
|
||||||
#include "../mwworld/inventorystore.hpp"
|
#include "../mwworld/inventorystore.hpp"
|
||||||
#include "../mwworld/actionequip.hpp"
|
#include "../mwworld/actionequip.hpp"
|
||||||
#include "../mwmechanics/spellsuccess.hpp"
|
#include "../mwmechanics/spellcasting.hpp"
|
||||||
#include "../mwgui/inventorywindow.hpp"
|
#include "../mwgui/inventorywindow.hpp"
|
||||||
#include "../mwgui/bookwindow.hpp"
|
#include "../mwgui/bookwindow.hpp"
|
||||||
#include "../mwgui/scrollwindow.hpp"
|
#include "../mwgui/scrollwindow.hpp"
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
#include "../mwworld/player.hpp"
|
#include "../mwworld/player.hpp"
|
||||||
|
|
||||||
#include "../mwmechanics/spellsuccess.hpp"
|
#include "../mwmechanics/spellcasting.hpp"
|
||||||
|
|
||||||
#include "tooltips.hpp"
|
#include "tooltips.hpp"
|
||||||
#include "class.hpp"
|
#include "class.hpp"
|
||||||
|
@ -89,6 +89,8 @@ namespace MWGui
|
||||||
mEffect.mMagnMax = 1;
|
mEffect.mMagnMax = 1;
|
||||||
mEffect.mDuration = 1;
|
mEffect.mDuration = 1;
|
||||||
mEffect.mArea = 0;
|
mEffect.mArea = 0;
|
||||||
|
mEffect.mSkill = -1;
|
||||||
|
mEffect.mAttribute = -1;
|
||||||
eventEffectAdded(mEffect);
|
eventEffectAdded(mEffect);
|
||||||
|
|
||||||
onRangeButtonClicked(mRangeButton);
|
onRangeButtonClicked(mRangeButton);
|
||||||
|
|
|
@ -21,6 +21,20 @@
|
||||||
namespace MWGui
|
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)
|
void SpellIcons::updateWidgets(MyGUI::Widget *parent, bool adjustSize)
|
||||||
{
|
{
|
||||||
// TODO: Tracking add/remove/expire would be better than force updating every frame
|
// 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();
|
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer();
|
||||||
const MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player);
|
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);
|
MWWorld::InventoryStore& store = MWWorld::Class::get(player).getInventoryStore(player);
|
||||||
for (int slot = 0; slot < MWWorld::InventoryStore::Slots; ++slot)
|
store.visitEffectSources(visitor);
|
||||||
{
|
stats.getSpells().visitEffectSources(visitor);
|
||||||
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;
|
// now add lasting effects
|
||||||
for (std::vector<ESM::ENAMstruct>::const_iterator effectIt = list.mList.begin();
|
visitor.mIsPermanent = false;
|
||||||
effectIt != list.mList.end(); ++effectIt)
|
stats.getActiveSpells().visitEffectSources(visitor);
|
||||||
{
|
|
||||||
const ESM::MagicEffect* magicEffect =
|
|
||||||
MWBase::Environment::get().getWorld ()->getStore ().get<ESM::MagicEffect>().find(effectIt->mEffectID);
|
|
||||||
|
|
||||||
MagicEffectInfo effectInfo;
|
std::map <int, std::vector<MagicEffectInfo> >& effects = visitor.mEffectSources;
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int w=2;
|
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
|
#define MWGUI_SPELLICONS_H
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "../mwmechanics/magiceffects.hpp"
|
#include "../mwmechanics/magiceffects.hpp"
|
||||||
|
|
||||||
|
@ -34,14 +35,23 @@ namespace MWGui
|
||||||
bool mPermanent; // the effect is permanent
|
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
|
class SpellIcons
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
void updateWidgets(MyGUI::Widget* parent, bool adjustSize);
|
void updateWidgets(MyGUI::Widget* parent, bool adjustSize);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string getSpellDisplayName (const std::string& id);
|
|
||||||
ESM::EffectList getSpellEffectList (const std::string& id);
|
|
||||||
|
|
||||||
std::map<int, MyGUI::ImageBox*> mWidgetMap;
|
std::map<int, MyGUI::ImageBox*> mWidgetMap;
|
||||||
};
|
};
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
#include "../mwworld/inventorystore.hpp"
|
#include "../mwworld/inventorystore.hpp"
|
||||||
#include "../mwworld/actionequip.hpp"
|
#include "../mwworld/actionequip.hpp"
|
||||||
|
|
||||||
#include "../mwmechanics/spellsuccess.hpp"
|
#include "../mwmechanics/spellcasting.hpp"
|
||||||
|
|
||||||
#include "spellicons.hpp"
|
#include "spellicons.hpp"
|
||||||
#include "inventorywindow.hpp"
|
#include "inventorywindow.hpp"
|
||||||
|
|
|
@ -16,11 +16,14 @@
|
||||||
#include "../mwbase/environment.hpp"
|
#include "../mwbase/environment.hpp"
|
||||||
#include "../mwbase/world.hpp"
|
#include "../mwbase/world.hpp"
|
||||||
#include "../mwbase/soundmanager.hpp"
|
#include "../mwbase/soundmanager.hpp"
|
||||||
|
#include "../mwbase/windowmanager.hpp"
|
||||||
|
|
||||||
#include "../mwrender/animation.hpp"
|
#include "../mwrender/animation.hpp"
|
||||||
|
|
||||||
#include "../mwworld/class.hpp"
|
#include "../mwworld/class.hpp"
|
||||||
|
|
||||||
|
#include "../mwmechanics/spellcasting.hpp"
|
||||||
|
|
||||||
#include "creaturestats.hpp"
|
#include "creaturestats.hpp"
|
||||||
#include "npcstats.hpp"
|
#include "npcstats.hpp"
|
||||||
|
|
||||||
|
@ -74,7 +77,7 @@ namespace MWMechanics
|
||||||
for (std::vector<ESM::ENAMstruct>::const_iterator effectIter (effects.first.mList.begin());
|
for (std::vector<ESM::ENAMstruct>::const_iterator effectIter (effects.first.mList.begin());
|
||||||
effectIter!=effects.first.mList.end(); ++effectIter, ++i)
|
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)
|
if (effectIter->mRange != iter->second.mRange)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
@ -83,7 +86,7 @@ namespace MWMechanics
|
||||||
int duration = effectIter->mDuration;
|
int duration = effectIter->mDuration;
|
||||||
|
|
||||||
if (effects.second.first)
|
if (effects.second.first)
|
||||||
duration *= magnitude;
|
duration *= random;
|
||||||
|
|
||||||
MWWorld::TimeStamp end = start;
|
MWWorld::TimeStamp end = start;
|
||||||
end += static_cast<double> (duration)*
|
end += static_cast<double> (duration)*
|
||||||
|
@ -102,18 +105,20 @@ namespace MWMechanics
|
||||||
if (effectIter->mDuration==0)
|
if (effectIter->mDuration==0)
|
||||||
{
|
{
|
||||||
param.mMagnitude =
|
param.mMagnitude =
|
||||||
static_cast<int> (magnitude / (0.1 * magicEffect->mData.mBaseCost));
|
static_cast<int> (random / (0.1 * magicEffect->mData.mBaseCost));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
param.mMagnitude =
|
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
|
else
|
||||||
param.mMagnitude = static_cast<int> (
|
param.mMagnitude = static_cast<int> (
|
||||||
(effectIter->mMagnMax-effectIter->mMagnMin)*magnitude + effectIter->mMagnMin);
|
(effectIter->mMagnMax-effectIter->mMagnMin)*random + effectIter->mMagnMin);
|
||||||
|
param.mMagnitude *= iter->second.mMultiplier[i];
|
||||||
|
|
||||||
|
if (param.mMagnitude)
|
||||||
mEffects.add (*effectIter, param);
|
mEffects.add (*effectIter, param);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -164,12 +169,31 @@ namespace MWMechanics
|
||||||
throw std::runtime_error ("ID " + id + " can not produce lasting effects");
|
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()
|
ActiveSpells::ActiveSpells()
|
||||||
: mSpellsChanged (false), mLastUpdate (MWBase::Environment::get().getWorld()->getTimeStamp())
|
: 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);
|
std::pair<ESM::EffectList, std::pair<bool, bool> > effects = getEffectList (id);
|
||||||
bool stacks = effects.second.second;
|
bool stacks = effects.second.second;
|
||||||
|
|
||||||
|
@ -178,6 +202,8 @@ namespace MWMechanics
|
||||||
for (std::vector<ESM::ENAMstruct>::const_iterator iter (effects.first.mList.begin());
|
for (std::vector<ESM::ENAMstruct>::const_iterator iter (effects.first.mList.begin());
|
||||||
iter!=effects.first.mList.end(); ++iter)
|
iter!=effects.first.mList.end(); ++iter)
|
||||||
{
|
{
|
||||||
|
if (iter->mRange != range)
|
||||||
|
continue;
|
||||||
if (iter->mDuration)
|
if (iter->mDuration)
|
||||||
{
|
{
|
||||||
found = true;
|
found = true;
|
||||||
|
@ -185,17 +211,19 @@ namespace MWMechanics
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If none of the effects need to apply, no need to add the spell
|
||||||
if (!found)
|
if (!found)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
TContainer::iterator iter = mSpells.find (id);
|
TContainer::iterator iter = mSpells.find (id);
|
||||||
|
|
||||||
|
ActiveSpellParams params;
|
||||||
|
for (unsigned int i=0; i<effects.first.mList.size(); ++i)
|
||||||
|
{
|
||||||
float random = static_cast<float> (std::rand()) / RAND_MAX;
|
float random = static_cast<float> (std::rand()) / RAND_MAX;
|
||||||
|
|
||||||
if (effects.second.first)
|
if (effects.second.first)
|
||||||
{
|
{
|
||||||
// ingredient -> special treatment required.
|
// ingredient -> special treatment required.
|
||||||
const CreatureStats& creatureStats = MWWorld::Class::get (actor).getCreatureStats (actor);
|
|
||||||
const NpcStats& npcStats = MWWorld::Class::get (actor).getNpcStats (actor);
|
const NpcStats& npcStats = MWWorld::Class::get (actor).getNpcStats (actor);
|
||||||
|
|
||||||
float x =
|
float x =
|
||||||
|
@ -208,31 +236,64 @@ namespace MWMechanics
|
||||||
random *= 0.25 * x;
|
random *= 0.25 * x;
|
||||||
}
|
}
|
||||||
|
|
||||||
ActiveSpellParams params;
|
params.mRandom.push_back(random);
|
||||||
for (unsigned int i=0; i<effects.first.mList.size(); ++i)
|
}
|
||||||
params.mRandom.push_back(static_cast<float> (std::rand()) / RAND_MAX);
|
|
||||||
params.mRange = range;
|
params.mRange = range;
|
||||||
params.mTimeStamp = MWBase::Environment::get().getWorld()->getTimeStamp();
|
params.mTimeStamp = MWBase::Environment::get().getWorld()->getTimeStamp();
|
||||||
params.mName = name;
|
params.mName = name;
|
||||||
|
params.mMultiplier.resize(effects.first.mList.size(), 1);
|
||||||
|
|
||||||
if (iter==mSpells.end() || stacks)
|
/*
|
||||||
mSpells.insert (std::make_pair (id, params));
|
for (int i=0; i<effects.first.mList.size(); ++i)
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
if (iter->mRange != range)
|
if (iter->mRange != range)
|
||||||
|
{
|
||||||
|
params.mDisabled.push_back(true);
|
||||||
continue;
|
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 =
|
const ESM::MagicEffect *magicEffect =
|
||||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find (
|
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
|
// Only the sound of the first effect plays
|
||||||
if (first)
|
if (first)
|
||||||
|
@ -258,6 +319,11 @@ namespace MWMechanics
|
||||||
first = false;
|
first = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (iter==mSpells.end() || stacks)
|
||||||
|
mSpells.insert (std::make_pair (id, params));
|
||||||
|
else
|
||||||
|
iter->second = params;
|
||||||
|
|
||||||
mSpellsChanged = true;
|
mSpellsChanged = true;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -338,4 +404,49 @@ namespace MWMechanics
|
||||||
{
|
{
|
||||||
return mSpells;
|
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
|
// Random factor for each effect
|
||||||
std::vector<float> mRandom;
|
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
|
// 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
|
// name of the item with the enchantment to addSpell
|
||||||
std::string mName;
|
std::string mName;
|
||||||
|
@ -53,7 +57,7 @@ namespace MWMechanics
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
mutable TContainer mSpells; // spellId, (time of casting, relative magnitude)
|
mutable TContainer mSpells;
|
||||||
mutable MagicEffects mEffects;
|
mutable MagicEffects mEffects;
|
||||||
mutable bool mSpellsChanged;
|
mutable bool mSpellsChanged;
|
||||||
mutable MWWorld::TimeStamp mLastUpdate;
|
mutable MWWorld::TimeStamp mLastUpdate;
|
||||||
|
@ -65,17 +69,31 @@ namespace MWMechanics
|
||||||
std::pair<ESM::EffectList, std::pair<bool, bool> > getEffectList (const std::string& id) const;
|
std::pair<ESM::EffectList, std::pair<bool, bool> > getEffectList (const std::string& id) const;
|
||||||
///< @return (EffectList, (isIngredient, stacks))
|
///< @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:
|
public:
|
||||||
|
|
||||||
ActiveSpells();
|
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
|
///< Overwrites an existing spell with the same ID. If the spell does not have any
|
||||||
/// non-instant effects, it is ignored.
|
/// non-instant effects, it is ignored.
|
||||||
/// @param id
|
/// @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 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 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?
|
/// \return Has the spell been added?
|
||||||
|
|
||||||
|
@ -86,15 +104,8 @@ namespace MWMechanics
|
||||||
|
|
||||||
const MagicEffects& getMagicEffects() const;
|
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.
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@ namespace MWMechanics
|
||||||
// magic effects
|
// magic effects
|
||||||
adjustMagicEffects (ptr);
|
adjustMagicEffects (ptr);
|
||||||
calculateDynamicStats (ptr);
|
calculateDynamicStats (ptr);
|
||||||
calculateCreatureStatModifiers (ptr);
|
calculateCreatureStatModifiers (ptr, duration);
|
||||||
|
|
||||||
if(!MWBase::Environment::get().getWindowManager()->isGuiMode())
|
if(!MWBase::Environment::get().getWindowManager()->isGuiMode())
|
||||||
{
|
{
|
||||||
|
@ -145,7 +145,7 @@ namespace MWMechanics
|
||||||
stats.setFatigue (fatigue);
|
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);
|
CreatureStats &creatureStats = MWWorld::Class::get(ptr).getCreatureStats(ptr);
|
||||||
const MagicEffects &effects = creatureStats.getMagicEffects();
|
const MagicEffects &effects = creatureStats.getMagicEffects();
|
||||||
|
@ -167,10 +167,25 @@ namespace MWMechanics
|
||||||
stat.setModifier(effects.get(EffectKey(ESM::MagicEffect::FortifyHealth+i)).mMagnitude -
|
stat.setModifier(effects.get(EffectKey(ESM::MagicEffect::FortifyHealth+i)).mMagnitude -
|
||||||
effects.get(EffectKey(ESM::MagicEffect::DrainHealth+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);
|
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)
|
void Actors::calculateNpcStatModifiers (const MWWorld::Ptr& ptr)
|
||||||
|
|
|
@ -40,7 +40,7 @@ namespace MWMechanics
|
||||||
|
|
||||||
void calculateDynamicStats (const MWWorld::Ptr& ptr);
|
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 calculateNpcStatModifiers (const MWWorld::Ptr& ptr);
|
||||||
|
|
||||||
void calculateRestoration (const MWWorld::Ptr& ptr, float duration);
|
void calculateRestoration (const MWWorld::Ptr& ptr, float duration);
|
||||||
|
|
|
@ -782,7 +782,7 @@ void CharacterController::update(float duration)
|
||||||
|
|
||||||
if(!onground && !flying && !inwater)
|
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))
|
if (world->isSlowFalling(mPtr))
|
||||||
{
|
{
|
||||||
|
@ -817,8 +817,7 @@ void CharacterController::update(float duration)
|
||||||
}
|
}
|
||||||
else if(vec.z > 0.0f && mJumpState == JumpState_None)
|
else if(vec.z > 0.0f && mJumpState == JumpState_None)
|
||||||
{
|
{
|
||||||
// The player has started a jump.
|
// Started a jump.
|
||||||
|
|
||||||
float z = cls.getJump(mPtr);
|
float z = cls.getJump(mPtr);
|
||||||
if(vec.x == 0 && vec.y == 0)
|
if(vec.x == 0 && vec.y == 0)
|
||||||
vec = Ogre::Vector3(0.0f, 0.0f, z);
|
vec = Ogre::Vector3(0.0f, 0.0f, z);
|
||||||
|
@ -829,6 +828,7 @@ void CharacterController::update(float duration)
|
||||||
}
|
}
|
||||||
|
|
||||||
// advance acrobatics
|
// advance acrobatics
|
||||||
|
if (mPtr.getRefData().getHandle() == "player")
|
||||||
cls.skillUsageSucceeded(mPtr, ESM::Skill::Acrobatics, 0);
|
cls.skillUsageSucceeded(mPtr, ESM::Skill::Acrobatics, 0);
|
||||||
|
|
||||||
// decrease fatigue
|
// decrease fatigue
|
||||||
|
@ -843,8 +843,6 @@ void CharacterController::update(float duration)
|
||||||
}
|
}
|
||||||
else if(mJumpState == JumpState_Falling)
|
else if(mJumpState == JumpState_Falling)
|
||||||
{
|
{
|
||||||
// The player is landing.
|
|
||||||
|
|
||||||
forcestateupdate = true;
|
forcestateupdate = true;
|
||||||
mJumpState = JumpState_Landing;
|
mJumpState = JumpState_Landing;
|
||||||
vec.z = 0.0f;
|
vec.z = 0.0f;
|
||||||
|
@ -861,6 +859,7 @@ void CharacterController::update(float duration)
|
||||||
cls.getCreatureStats(mPtr).setHealth(health);
|
cls.getCreatureStats(mPtr).setHealth(health);
|
||||||
|
|
||||||
// report acrobatics progression
|
// report acrobatics progression
|
||||||
|
if (mPtr.getRefData().getHandle() == "player")
|
||||||
cls.skillUsageSucceeded(mPtr, ESM::Skill::Acrobatics, 1);
|
cls.skillUsageSucceeded(mPtr, ESM::Skill::Acrobatics, 1);
|
||||||
|
|
||||||
const float acrobaticsSkill = cls.getNpcStats(mPtr).getSkill(ESM::Skill::Acrobatics).getModified();
|
const float acrobaticsSkill = cls.getNpcStats(mPtr).getSkill(ESM::Skill::Acrobatics).getModified();
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#define GAME_MWMECHANICS_MAGICEFFECTS_H
|
#define GAME_MWMECHANICS_MAGICEFFECTS_H
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
namespace ESM
|
namespace ESM
|
||||||
{
|
{
|
||||||
|
@ -11,6 +12,13 @@ namespace ESM
|
||||||
|
|
||||||
namespace MWMechanics
|
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
|
struct EffectKey
|
||||||
{
|
{
|
||||||
int mId;
|
int mId;
|
||||||
|
@ -29,11 +37,12 @@ namespace MWMechanics
|
||||||
|
|
||||||
struct EffectParam
|
struct EffectParam
|
||||||
{
|
{
|
||||||
int mMagnitude;
|
// Note usually this would be int, but applying partial resistance might introduce decimal point.
|
||||||
|
float mMagnitude;
|
||||||
|
|
||||||
EffectParam();
|
EffectParam();
|
||||||
|
|
||||||
EffectParam(int magnitude) : mMagnitude(magnitude) {}
|
EffectParam(float magnitude) : mMagnitude(magnitude) {}
|
||||||
|
|
||||||
EffectParam& operator+= (const EffectParam& param);
|
EffectParam& operator+= (const EffectParam& param);
|
||||||
|
|
||||||
|
|
|
@ -63,7 +63,7 @@ namespace MWMechanics
|
||||||
x *= 0.1 * magicEffect->mData.mBaseCost;
|
x *= 0.1 * magicEffect->mData.mBaseCost;
|
||||||
x *= 0.5 * (it->mMagnMin + it->mMagnMax);
|
x *= 0.5 * (it->mMagnMin + it->mMagnMax);
|
||||||
x *= it->mArea * 0.05 * magicEffect->mData.mBaseCost;
|
x *= it->mArea * 0.05 * magicEffect->mData.mBaseCost;
|
||||||
if (it->mRange == ESM::RT_Target)
|
if (magicEffect->mData.mFlags & ESM::MagicEffect::CastTarget)
|
||||||
x *= 1.5;
|
x *= 1.5;
|
||||||
static const float fEffectCostMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(
|
static const float fEffectCostMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(
|
||||||
"fEffectCostMult")->getFloat();
|
"fEffectCostMult")->getFloat();
|
||||||
|
@ -114,6 +114,72 @@ namespace MWMechanics
|
||||||
return school;
|
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
|
#endif
|
|
@ -117,4 +117,27 @@ namespace MWMechanics
|
||||||
|
|
||||||
return false;
|
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 "../mwworld/ptr.hpp"
|
||||||
|
|
||||||
|
#include "magiceffects.hpp"
|
||||||
|
|
||||||
namespace ESM
|
namespace ESM
|
||||||
{
|
{
|
||||||
struct Spell;
|
struct Spell;
|
||||||
|
@ -59,6 +61,8 @@ namespace MWMechanics
|
||||||
bool hasCommonDisease() const;
|
bool hasCommonDisease() const;
|
||||||
|
|
||||||
bool hasBlightDisease() const;
|
bool hasBlightDisease() const;
|
||||||
|
|
||||||
|
void visitEffectSources (MWMechanics::EffectSourceVisitor& visitor) const;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -60,7 +60,7 @@ const NpcAnimation::PartBoneMap NpcAnimation::sPartList = createPartListMap();
|
||||||
NpcAnimation::~NpcAnimation()
|
NpcAnimation::~NpcAnimation()
|
||||||
{
|
{
|
||||||
if (!mListenerDisabled)
|
if (!mListenerDisabled)
|
||||||
mPtr.getClass().getInventoryStore(mPtr).setListener(NULL);
|
mPtr.getClass().getInventoryStore(mPtr).setListener(NULL, mPtr);
|
||||||
|
|
||||||
Ogre::SceneManager *sceneMgr = mInsert->getCreator();
|
Ogre::SceneManager *sceneMgr = mInsert->getCreator();
|
||||||
for(size_t i = 0;i < ESM::PRT_Count;i++)
|
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)
|
if (!disableListener)
|
||||||
mPtr.getClass().getInventoryStore(mPtr).setListener(this);
|
mPtr.getClass().getInventoryStore(mPtr).setListener(this, mPtr);
|
||||||
|
|
||||||
updateNpcBase();
|
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
|
// 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.
|
// 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);
|
const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find(mSpellId);
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,8 @@
|
||||||
#include "../mwbase/windowmanager.hpp"
|
#include "../mwbase/windowmanager.hpp"
|
||||||
|
|
||||||
#include "../mwmechanics/npcstats.hpp"
|
#include "../mwmechanics/npcstats.hpp"
|
||||||
|
#include "../mwmechanics/spellcasting.hpp"
|
||||||
|
|
||||||
|
|
||||||
#include "esmstore.hpp"
|
#include "esmstore.hpp"
|
||||||
#include "class.hpp"
|
#include "class.hpp"
|
||||||
|
@ -123,7 +125,7 @@ void MWWorld::InventoryStore::equip (int slot, const ContainerStoreIterator& ite
|
||||||
flagAsModified();
|
flagAsModified();
|
||||||
|
|
||||||
fireEquipmentChangedEvent();
|
fireEquipmentChangedEvent();
|
||||||
updateMagicEffects();
|
updateMagicEffects(actor);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MWWorld::InventoryStore::unequipAll(const MWWorld::Ptr& actor)
|
void MWWorld::InventoryStore::unequipAll(const MWWorld::Ptr& actor)
|
||||||
|
@ -150,10 +152,10 @@ MWWorld::ContainerStoreIterator MWWorld::InventoryStore::getSlot (int slot)
|
||||||
return mSlots[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);
|
const MWMechanics::NpcStats& stats = MWWorld::Class::get(actor).getNpcStats(actor);
|
||||||
MWWorld::InventoryStore& invStore = MWWorld::Class::get(npc).getInventoryStore(npc);
|
MWWorld::InventoryStore& invStore = MWWorld::Class::get(actor).getInventoryStore(actor);
|
||||||
|
|
||||||
TSlots slots_;
|
TSlots slots_;
|
||||||
initSlots (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:
|
case 0:
|
||||||
continue;
|
continue;
|
||||||
case 2:
|
case 2:
|
||||||
invStore.unequipSlot(MWWorld::InventoryStore::Slot_CarriedLeft, npc);
|
invStore.unequipSlot(MWWorld::InventoryStore::Slot_CarriedLeft, actor);
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
invStore.unequipSlot(MWWorld::InventoryStore::Slot_CarriedRight, npc);
|
invStore.unequipSlot(MWWorld::InventoryStore::Slot_CarriedRight, actor);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -255,7 +257,7 @@ void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& npc)
|
||||||
{
|
{
|
||||||
mSlots.swap (slots_);
|
mSlots.swap (slots_);
|
||||||
fireEquipmentChangedEvent();
|
fireEquipmentChangedEvent();
|
||||||
updateMagicEffects();
|
updateMagicEffects(actor);
|
||||||
flagAsModified();
|
flagAsModified();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -265,7 +267,7 @@ const MWMechanics::MagicEffects& MWWorld::InventoryStore::getMagicEffects() cons
|
||||||
return mMagicEffects;
|
return mMagicEffects;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MWWorld::InventoryStore::updateMagicEffects()
|
void MWWorld::InventoryStore::updateMagicEffects(const Ptr& actor)
|
||||||
{
|
{
|
||||||
// To avoid excessive updates during auto-equip
|
// To avoid excessive updates during auto-equip
|
||||||
if (!mUpdatesEnabled)
|
if (!mUpdatesEnabled)
|
||||||
|
@ -292,30 +294,46 @@ void MWWorld::InventoryStore::updateMagicEffects()
|
||||||
if (enchantment.mData.mType != ESM::Enchantment::ConstantEffect)
|
if (enchantment.mData.mType != ESM::Enchantment::ConstantEffect)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Roll some dice, one for each effect
|
std::vector<EffectParams> params;
|
||||||
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;
|
|
||||||
|
|
||||||
bool existed = (mPermanentMagicEffectMagnitudes.find((**iter).getCellRef().mRefID) != mPermanentMagicEffectMagnitudes.end());
|
bool existed = (mPermanentMagicEffectMagnitudes.find((**iter).getCellRef().mRefID) != mPermanentMagicEffectMagnitudes.end());
|
||||||
if (!existed)
|
if (!existed)
|
||||||
{
|
{
|
||||||
// Note that using the RefID as a key here is not entirely correct.
|
// Roll some dice, one for each effect
|
||||||
// Consider equipping the same item twice (e.g. a ring)
|
params.resize(enchantment.mEffects.mList.size());
|
||||||
// However, permanent enchantments with a random magnitude are kind of an exploit anyway,
|
for (unsigned int i=0; i<params.size();++i)
|
||||||
// so it doesn't really matter if both items will get the same magnitude. *Extreme* edge case.
|
params[i].mRandom = static_cast<float> (std::rand()) / RAND_MAX;
|
||||||
mPermanentMagicEffectMagnitudes[(**iter).getCellRef().mRefID] = random;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Try resisting each effect
|
||||||
int i=0;
|
int i=0;
|
||||||
for (std::vector<ESM::ENAMstruct>::const_iterator effectIt (enchantment.mEffects.mList.begin());
|
for (std::vector<ESM::ENAMstruct>::const_iterator effectIt (enchantment.mEffects.mList.begin());
|
||||||
effectIt!=enchantment.mEffects.mList.end(); ++effectIt)
|
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] = 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, ++i)
|
||||||
{
|
{
|
||||||
const ESM::MagicEffect *magicEffect =
|
const ESM::MagicEffect *magicEffect =
|
||||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find (
|
MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find (
|
||||||
effectIt->mEffectID);
|
effectIt->mEffectID);
|
||||||
|
|
||||||
|
// Fully resisted?
|
||||||
|
if (params[i].mMultiplier == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
if (!existed)
|
if (!existed)
|
||||||
{
|
{
|
||||||
// During first auto equip, we don't play any sounds.
|
// During first auto equip, we don't play any sounds.
|
||||||
|
@ -325,8 +343,10 @@ void MWWorld::InventoryStore::updateMagicEffects()
|
||||||
!mFirstAutoEquip && effectIt == enchantment.mEffects.mList.begin());
|
!mFirstAutoEquip && effectIt == enchantment.mEffects.mList.begin());
|
||||||
}
|
}
|
||||||
|
|
||||||
mMagicEffects.add (*effectIt, random[i]);
|
float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * params[i].mRandom;
|
||||||
++i;
|
magnitude *= params[i].mMultiplier;
|
||||||
|
if (magnitude)
|
||||||
|
mMagicEffects.add (*effectIt, magnitude);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -467,7 +487,7 @@ MWWorld::ContainerStoreIterator MWWorld::InventoryStore::unequipSlot(int slot, c
|
||||||
}
|
}
|
||||||
|
|
||||||
fireEquipmentChangedEvent();
|
fireEquipmentChangedEvent();
|
||||||
updateMagicEffects();
|
updateMagicEffects(actor);
|
||||||
|
|
||||||
return retval;
|
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");
|
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;
|
mListener = listener;
|
||||||
updateMagicEffects();
|
updateMagicEffects(actor);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MWWorld::InventoryStore::fireEquipmentChangedEvent()
|
void MWWorld::InventoryStore::fireEquipmentChangedEvent()
|
||||||
|
@ -500,3 +520,37 @@ void MWWorld::InventoryStore::fireEquipmentChangedEvent()
|
||||||
if (mListener)
|
if (mListener)
|
||||||
mListener->equipmentChanged();
|
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.
|
// 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.
|
// 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;
|
TEffectMagnitudes mPermanentMagicEffectMagnitudes;
|
||||||
|
|
||||||
typedef std::vector<ContainerStoreIterator> TSlots;
|
typedef std::vector<ContainerStoreIterator> TSlots;
|
||||||
|
@ -88,7 +96,7 @@ namespace MWWorld
|
||||||
|
|
||||||
void initSlots (TSlots& slots_);
|
void initSlots (TSlots& slots_);
|
||||||
|
|
||||||
void updateMagicEffects();
|
void updateMagicEffects(const Ptr& actor);
|
||||||
|
|
||||||
void fireEquipmentChangedEvent();
|
void fireEquipmentChangedEvent();
|
||||||
|
|
||||||
|
@ -127,7 +135,7 @@ namespace MWWorld
|
||||||
void unequipAll(const MWWorld::Ptr& actor);
|
void unequipAll(const MWWorld::Ptr& actor);
|
||||||
///< Unequip all currently equipped items.
|
///< 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.
|
///< Auto equip items according to stats and item value.
|
||||||
|
|
||||||
const MWMechanics::MagicEffects& getMagicEffects() const;
|
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
|
/// (it can be re-stacked so its count may be different than when it
|
||||||
/// was equipped).
|
/// was equipped).
|
||||||
|
|
||||||
void setListener (InventoryStoreListener* listener);
|
void setListener (InventoryStoreListener* listener, const Ptr& actor);
|
||||||
///< Set a listener for various events, see \a InventoryStoreListener
|
///< Set a listener for various events, see \a InventoryStoreListener
|
||||||
|
|
||||||
|
void visitEffectSources (MWMechanics::EffectSourceVisitor& visitor);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
#include "../mwmechanics/creaturestats.hpp"
|
#include "../mwmechanics/creaturestats.hpp"
|
||||||
#include "../mwmechanics/movement.hpp"
|
#include "../mwmechanics/movement.hpp"
|
||||||
#include "../mwmechanics/npcstats.hpp"
|
#include "../mwmechanics/npcstats.hpp"
|
||||||
#include "../mwmechanics/spellsuccess.hpp"
|
#include "../mwmechanics/spellcasting.hpp"
|
||||||
|
|
||||||
|
|
||||||
#include "../mwrender/sky.hpp"
|
#include "../mwrender/sky.hpp"
|
||||||
|
@ -2158,7 +2158,7 @@ namespace MWWorld
|
||||||
// Now apply the spell!
|
// Now apply the spell!
|
||||||
|
|
||||||
// Apply Self portion
|
// 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
|
// Apply Touch portion
|
||||||
// TODO: Distance is probably incorrect, and should it be hardcoded?
|
// TODO: Distance is probably incorrect, and should it be hardcoded?
|
||||||
|
@ -2166,7 +2166,7 @@ namespace MWWorld
|
||||||
if (!contact.first.isEmpty())
|
if (!contact.first.isEmpty())
|
||||||
{
|
{
|
||||||
if (contact.first.getClass().isActor())
|
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
|
else
|
||||||
{
|
{
|
||||||
// We hit a non-actor, e.g. a door. Only instant effects are relevant.
|
// 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);
|
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()
|
static std::map<short,std::string> genNameMap()
|
||||||
{
|
{
|
||||||
|
|
|
@ -58,6 +58,12 @@ struct MagicEffect
|
||||||
|
|
||||||
static const std::string &effectIdToString(short effectID);
|
static const std::string &effectIdToString(short effectID);
|
||||||
static short effectStringToId(const std::string &effect);
|
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;
|
MagnitudeDisplayType getMagnitudeDisplayType() const;
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue