|
|
|
@ -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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -185,7 +190,7 @@ namespace MWMechanics
|
|
|
|
|
: 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, int effectIndex)
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
@ -197,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;
|
|
|
|
@ -204,41 +211,37 @@ 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 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;
|
|
|
|
|
|
|
|
|
|
if (iter==mSpells.end() || stacks)
|
|
|
|
|
mSpells.insert (std::make_pair (id, params));
|
|
|
|
|
else
|
|
|
|
|
iter->second = params;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
params.mMultiplier.resize(effects.first.mList.size(), 1);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
for (int i=0; i<effects.first.mList.size(); ++i)
|
|
|
|
@ -258,19 +261,35 @@ namespace MWMechanics
|
|
|
|
|
}
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
// Play sounds & particles
|
|
|
|
|
bool first=true;
|
|
|
|
|
for (std::vector<ESM::ENAMstruct>::const_iterator iter (effects.first.mList.begin());
|
|
|
|
|
iter!=effects.first.mList.end(); ++iter)
|
|
|
|
|
int i = 0;
|
|
|
|
|
for (std::vector<ESM::ENAMstruct>::const_iterator effectIt (effects.first.mList.begin());
|
|
|
|
|
effectIt!=effects.first.mList.end(); ++effectIt, ++i)
|
|
|
|
|
{
|
|
|
|
|
if (iter->mRange != range)
|
|
|
|
|
if (effectIt->mRange != range)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
// 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.
|
|
|
|
|
|
|
|
|
|
const ESM::MagicEffect *magicEffect =
|
|
|
|
|
MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find (
|
|
|
|
|
iter->mEffectID);
|
|
|
|
|
effectIt->mEffectID);
|
|
|
|
|
|
|
|
|
|
// Only the sound of the first effect plays
|
|
|
|
|
if (first)
|
|
|
|
@ -296,6 +315,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;
|
|
|
|
@ -415,7 +439,9 @@ namespace MWMechanics
|
|
|
|
|
magnitude = static_cast<int> (0.05*it->second.mRandom[i] / (0.1 * magicEffect->mData.mBaseCost));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
visitor.visit(*effectIt, name, magnitude, remainingTime);
|
|
|
|
|
magnitude *= it->second.mMultiplier[i];
|
|
|
|
|
if (magnitude)
|
|
|
|
|
visitor.visit(*effectIt, name, magnitude, remainingTime);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|