|
|
|
@ -61,38 +61,36 @@ namespace MWMechanics
|
|
|
|
|
|
|
|
|
|
// Note: the algorithm heavily depends on the traversal order of the spells. For vanilla-compatible results the
|
|
|
|
|
// Store must preserve the record ordering as it was in the content files.
|
|
|
|
|
for (MWWorld::Store<ESM::Spell>::iterator iter = spells.begin(); iter != spells.end(); ++iter)
|
|
|
|
|
for (const ESM::Spell& spell : spells)
|
|
|
|
|
{
|
|
|
|
|
const ESM::Spell* spell = &*iter;
|
|
|
|
|
|
|
|
|
|
if (spell->mData.mType != ESM::Spell::ST_Spell)
|
|
|
|
|
if (spell.mData.mType != ESM::Spell::ST_Spell)
|
|
|
|
|
continue;
|
|
|
|
|
if (!(spell->mData.mFlags & ESM::Spell::F_Autocalc))
|
|
|
|
|
if (!(spell.mData.mFlags & ESM::Spell::F_Autocalc))
|
|
|
|
|
continue;
|
|
|
|
|
static const int iAutoSpellTimesCanCast = gmst.find("iAutoSpellTimesCanCast")->mValue.getInteger();
|
|
|
|
|
if (baseMagicka < iAutoSpellTimesCanCast * spell->mData.mCost)
|
|
|
|
|
if (baseMagicka < iAutoSpellTimesCanCast * spell.mData.mCost)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (race && race->mPowers.exists(spell->mId))
|
|
|
|
|
if (race && race->mPowers.exists(spell.mId))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (!attrSkillCheck(spell, actorSkills, actorAttributes))
|
|
|
|
|
if (!attrSkillCheck(&spell, actorSkills, actorAttributes))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
int school;
|
|
|
|
|
float skillTerm;
|
|
|
|
|
calcWeakestSchool(spell, actorSkills, school, skillTerm);
|
|
|
|
|
calcWeakestSchool(&spell, actorSkills, school, skillTerm);
|
|
|
|
|
assert(school >= 0 && school < 6);
|
|
|
|
|
SchoolCaps& cap = schoolCaps[school];
|
|
|
|
|
|
|
|
|
|
if (cap.mReachedLimit && spell->mData.mCost <= cap.mMinCost)
|
|
|
|
|
if (cap.mReachedLimit && spell.mData.mCost <= cap.mMinCost)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
static const float fAutoSpellChance = gmst.find("fAutoSpellChance")->mValue.getFloat();
|
|
|
|
|
if (calcAutoCastChance(spell, actorSkills, actorAttributes, school) < fAutoSpellChance)
|
|
|
|
|
if (calcAutoCastChance(&spell, actorSkills, actorAttributes, school) < fAutoSpellChance)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
selectedSpells.push_back(spell->mId);
|
|
|
|
|
selectedSpells.push_back(spell.mId);
|
|
|
|
|
|
|
|
|
|
if (cap.mReachedLimit)
|
|
|
|
|
{
|
|
|
|
@ -101,9 +99,9 @@ namespace MWMechanics
|
|
|
|
|
selectedSpells.erase(found);
|
|
|
|
|
|
|
|
|
|
cap.mMinCost = std::numeric_limits<int>::max();
|
|
|
|
|
for (std::vector<std::string>::iterator weakIt = selectedSpells.begin(); weakIt != selectedSpells.end(); ++weakIt)
|
|
|
|
|
for (const std::string& testSpellName : selectedSpells)
|
|
|
|
|
{
|
|
|
|
|
const ESM::Spell* testSpell = spells.find(*weakIt);
|
|
|
|
|
const ESM::Spell* testSpell = spells.find(testSpellName);
|
|
|
|
|
|
|
|
|
|
//int testSchool;
|
|
|
|
|
//float dummySkillTerm;
|
|
|
|
@ -130,10 +128,10 @@ namespace MWMechanics
|
|
|
|
|
if (cap.mCount == cap.mLimit)
|
|
|
|
|
cap.mReachedLimit = true;
|
|
|
|
|
|
|
|
|
|
if (spell->mData.mCost < cap.mMinCost)
|
|
|
|
|
if (spell.mData.mCost < cap.mMinCost)
|
|
|
|
|
{
|
|
|
|
|
cap.mWeakestSpell = spell->mId;
|
|
|
|
|
cap.mMinCost = spell->mData.mCost;
|
|
|
|
|
cap.mWeakestSpell = spell.mId;
|
|
|
|
|
cap.mMinCost = spell.mData.mCost;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -154,32 +152,28 @@ namespace MWMechanics
|
|
|
|
|
|
|
|
|
|
std::vector<std::string> selectedSpells;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const MWWorld::Store<ESM::Spell> &spells =
|
|
|
|
|
esmStore.get<ESM::Spell>();
|
|
|
|
|
for (MWWorld::Store<ESM::Spell>::iterator iter = spells.begin(); iter != spells.end(); ++iter)
|
|
|
|
|
const MWWorld::Store<ESM::Spell> &spells = esmStore.get<ESM::Spell>();
|
|
|
|
|
for (const ESM::Spell& spell : spells)
|
|
|
|
|
{
|
|
|
|
|
const ESM::Spell* spell = &*iter;
|
|
|
|
|
|
|
|
|
|
if (spell->mData.mType != ESM::Spell::ST_Spell)
|
|
|
|
|
if (spell.mData.mType != ESM::Spell::ST_Spell)
|
|
|
|
|
continue;
|
|
|
|
|
if (!(spell->mData.mFlags & ESM::Spell::F_PCStart))
|
|
|
|
|
if (!(spell.mData.mFlags & ESM::Spell::F_PCStart))
|
|
|
|
|
continue;
|
|
|
|
|
if (reachedLimit && spell->mData.mCost <= minCost)
|
|
|
|
|
if (reachedLimit && spell.mData.mCost <= minCost)
|
|
|
|
|
continue;
|
|
|
|
|
if (race && std::find(race->mPowers.mList.begin(), race->mPowers.mList.end(), spell->mId) != race->mPowers.mList.end())
|
|
|
|
|
if (race && std::find(race->mPowers.mList.begin(), race->mPowers.mList.end(), spell.mId) != race->mPowers.mList.end())
|
|
|
|
|
continue;
|
|
|
|
|
if (baseMagicka < spell->mData.mCost)
|
|
|
|
|
if (baseMagicka < spell.mData.mCost)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
static const float fAutoPCSpellChance = esmStore.get<ESM::GameSetting>().find("fAutoPCSpellChance")->mValue.getFloat();
|
|
|
|
|
if (calcAutoCastChance(spell, actorSkills, actorAttributes, -1) < fAutoPCSpellChance)
|
|
|
|
|
if (calcAutoCastChance(&spell, actorSkills, actorAttributes, -1) < fAutoPCSpellChance)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (!attrSkillCheck(spell, actorSkills, actorAttributes))
|
|
|
|
|
if (!attrSkillCheck(&spell, actorSkills, actorAttributes))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
selectedSpells.push_back(spell->mId);
|
|
|
|
|
selectedSpells.push_back(spell.mId);
|
|
|
|
|
|
|
|
|
|
if (reachedLimit)
|
|
|
|
|
{
|
|
|
|
@ -188,9 +182,9 @@ namespace MWMechanics
|
|
|
|
|
selectedSpells.erase(it);
|
|
|
|
|
|
|
|
|
|
minCost = std::numeric_limits<int>::max();
|
|
|
|
|
for (std::vector<std::string>::iterator weakIt = selectedSpells.begin(); weakIt != selectedSpells.end(); ++weakIt)
|
|
|
|
|
for (const std::string& testSpellName : selectedSpells)
|
|
|
|
|
{
|
|
|
|
|
const ESM::Spell* testSpell = esmStore.get<ESM::Spell>().find(*weakIt);
|
|
|
|
|
const ESM::Spell* testSpell = esmStore.get<ESM::Spell>().find(testSpellName);
|
|
|
|
|
if (testSpell->mData.mCost < minCost)
|
|
|
|
|
{
|
|
|
|
|
minCost = testSpell->mData.mCost;
|
|
|
|
@ -200,9 +194,9 @@ namespace MWMechanics
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (spell->mData.mCost < minCost)
|
|
|
|
|
if (spell.mData.mCost < minCost)
|
|
|
|
|
{
|
|
|
|
|
weakestSpell = spell;
|
|
|
|
|
weakestSpell = &spell;
|
|
|
|
|
minCost = weakestSpell->mData.mCost;
|
|
|
|
|
}
|
|
|
|
|
static const unsigned int iAutoPCSpellMax = esmStore.get<ESM::GameSetting>().find("iAutoPCSpellMax")->mValue.getInteger();
|
|
|
|
@ -216,23 +210,22 @@ namespace MWMechanics
|
|
|
|
|
|
|
|
|
|
bool attrSkillCheck (const ESM::Spell* spell, const int* actorSkills, const int* actorAttributes)
|
|
|
|
|
{
|
|
|
|
|
const std::vector<ESM::ENAMstruct>& effects = spell->mEffects.mList;
|
|
|
|
|
for (std::vector<ESM::ENAMstruct>::const_iterator effectIt = effects.begin(); effectIt != effects.end(); ++effectIt)
|
|
|
|
|
for (const auto& spellEffect : spell->mEffects.mList)
|
|
|
|
|
{
|
|
|
|
|
const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(effectIt->mEffectID);
|
|
|
|
|
const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(spellEffect.mEffectID);
|
|
|
|
|
static const int iAutoSpellAttSkillMin = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("iAutoSpellAttSkillMin")->mValue.getInteger();
|
|
|
|
|
|
|
|
|
|
if ((magicEffect->mData.mFlags & ESM::MagicEffect::TargetSkill))
|
|
|
|
|
{
|
|
|
|
|
assert (effectIt->mSkill >= 0 && effectIt->mSkill < ESM::Skill::Length);
|
|
|
|
|
if (actorSkills[effectIt->mSkill] < iAutoSpellAttSkillMin)
|
|
|
|
|
assert (spellEffect.mSkill >= 0 && spellEffect.mSkill < ESM::Skill::Length);
|
|
|
|
|
if (actorSkills[spellEffect.mSkill] < iAutoSpellAttSkillMin)
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((magicEffect->mData.mFlags & ESM::MagicEffect::TargetAttribute))
|
|
|
|
|
{
|
|
|
|
|
assert (effectIt->mAttribute >= 0 && effectIt->mAttribute < ESM::Attribute::Length);
|
|
|
|
|
if (actorAttributes[effectIt->mAttribute] < iAutoSpellAttSkillMin)
|
|
|
|
|
assert (spellEffect.mAttribute >= 0 && spellEffect.mAttribute < ESM::Attribute::Length);
|
|
|
|
|
if (actorAttributes[spellEffect.mAttribute] < iAutoSpellAttSkillMin)
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -244,11 +237,8 @@ namespace MWMechanics
|
|
|
|
|
{
|
|
|
|
|
// Morrowind for some reason uses a formula slightly different from magicka cost calculation
|
|
|
|
|
float minChance = std::numeric_limits<float>::max();
|
|
|
|
|
|
|
|
|
|
const ESM::EffectList& effects = spell->mEffects;
|
|
|
|
|
for (std::vector<ESM::ENAMstruct>::const_iterator it = effects.mList.begin(); it != effects.mList.end(); ++it)
|
|
|
|
|
for (const ESM::ENAMstruct& effect : spell->mEffects.mList)
|
|
|
|
|
{
|
|
|
|
|
const ESM::ENAMstruct& effect = *it;
|
|
|
|
|
const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(effect.mEffectID);
|
|
|
|
|
|
|
|
|
|
int minMagn = 1;
|
|
|
|
|