mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-19 21:53:51 +00:00
Implement Hrnchamd's player and NPC autocalc spells (Some unclarities remaining, XXX)
This commit is contained in:
parent
d91d599269
commit
1c41ce9b9d
6 changed files with 368 additions and 28 deletions
|
@ -69,7 +69,7 @@ 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 aipursue alchemy aiwander aitravel aifollow aiavoiddoor
|
drawstate spells activespells npcstats aipackage aisequence aipursue alchemy aiwander aitravel aifollow aiavoiddoor
|
||||||
aiescort aiactivate aicombat repair enchanting pathfinding pathgrid security spellsuccess spellcasting
|
aiescort aiactivate aicombat repair enchanting pathfinding pathgrid security spellsuccess spellcasting
|
||||||
disease pickpocket levelledlist combat steering obstacle
|
disease pickpocket levelledlist combat steering obstacle autocalcspell
|
||||||
)
|
)
|
||||||
|
|
||||||
add_openmw_dir (mwstate
|
add_openmw_dir (mwstate
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#include "../mwmechanics/spellcasting.hpp"
|
#include "../mwmechanics/spellcasting.hpp"
|
||||||
#include "../mwmechanics/disease.hpp"
|
#include "../mwmechanics/disease.hpp"
|
||||||
#include "../mwmechanics/combat.hpp"
|
#include "../mwmechanics/combat.hpp"
|
||||||
|
#include "../mwmechanics/autocalcspell.hpp"
|
||||||
|
|
||||||
#include "../mwworld/ptr.hpp"
|
#include "../mwworld/ptr.hpp"
|
||||||
#include "../mwworld/actiontalk.hpp"
|
#include "../mwworld/actiontalk.hpp"
|
||||||
|
@ -193,18 +194,6 @@ namespace
|
||||||
majorMultiplier = 1.0f;
|
majorMultiplier = 1.0f;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (class_->mData.mSkills[k][1] == skillIndex)
|
|
||||||
{
|
|
||||||
// Major skill -> add starting spells for this skill if existing
|
|
||||||
const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();
|
|
||||||
MWWorld::Store<ESM::Spell>::iterator it = store.get<ESM::Spell>().begin();
|
|
||||||
for (; it != store.get<ESM::Spell>().end(); ++it)
|
|
||||||
{
|
|
||||||
if (it->mData.mFlags & ESM::Spell::F_Autocalc
|
|
||||||
&& MWMechanics::spellSchoolToSkill(MWMechanics::getSpellSchool(&*it, ptr)) == skillIndex)
|
|
||||||
npcStats.getSpells().add(it->mId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// is this skill in the same Specialization as the class?
|
// is this skill in the same Specialization as the class?
|
||||||
|
@ -223,6 +212,42 @@ namespace
|
||||||
+ specBonus
|
+ specBonus
|
||||||
+ static_cast<int>((level-1) * (majorMultiplier + specMultiplier)), 100));
|
+ static_cast<int>((level-1) * (majorMultiplier + specMultiplier)), 100));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int skills[ESM::Skill::Length];
|
||||||
|
for (int i=0; i<ESM::Skill::Length; ++i)
|
||||||
|
skills[i] = npcStats.getSkill(i).getBase();
|
||||||
|
|
||||||
|
int attributes[ESM::Attribute::Length];
|
||||||
|
for (int i=0; i<ESM::Attribute::Length; ++i)
|
||||||
|
attributes[i] = npcStats.getAttribute(i).getBase();
|
||||||
|
|
||||||
|
std::cout << npc->mId << std::endl;
|
||||||
|
std::cout << "Skills: " << std::endl;
|
||||||
|
for (int i=0; i<ESM::Skill::Length; ++i)
|
||||||
|
std::cout << skills[i] << std::endl;
|
||||||
|
std::cout << "Attributes: " << std::endl;
|
||||||
|
for (int i=0; i<ESM::Attribute::Length; ++i)
|
||||||
|
std::cout << attributes[i] << std::endl;
|
||||||
|
std::set<std::string> spells = MWMechanics::autoCalcNpcSpells(skills, attributes, race);
|
||||||
|
std::cout << "Spells: " << spells.size() << std::endl;
|
||||||
|
for (std::set<std::string>::iterator it = spells.begin(); it != spells.end(); ++it)
|
||||||
|
{
|
||||||
|
std::cout << *it << ", ";
|
||||||
|
npcStats.getSpells().add(*it);
|
||||||
|
}
|
||||||
|
std::cout << std::endl;
|
||||||
|
|
||||||
|
const char* compare[] = { "weary","dire noise","reflect","weak spelldrinker","absorb endurance","absorb personality","absorb speed","absorb strength","absorb willpower","fortify alteration skill","fortify illusion skill","fortify unarmored skill","fortify mysticism skill","fortify restoration skill","assured sublime wisdom","surpassing sublime wisdom","surpassing golden wisdom","blood gift","surpassing silver wisdom","surpassing unseen wisdom","surpassing green wisdom","powerwell","orc's strength","surpassing fluid evasion","poet's whim","rapid regenerate","dispel","shadow weave" };
|
||||||
|
int n = sizeof(compare) / sizeof(compare[0]);
|
||||||
|
std::set<std::string> compareSet;
|
||||||
|
for (int i=0; i<n; ++i)
|
||||||
|
compareSet.insert(compare[i]);
|
||||||
|
std::cout << "Compare: " << n << std::endl;
|
||||||
|
for (std::set<std::string>::iterator it = compareSet.begin(); it != compareSet.end(); ++it)
|
||||||
|
{
|
||||||
|
std::cout << *it << ", ";
|
||||||
|
}
|
||||||
|
std::cout << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
214
apps/openmw/mwmechanics/autocalcspell.cpp
Normal file
214
apps/openmw/mwmechanics/autocalcspell.cpp
Normal file
|
@ -0,0 +1,214 @@
|
||||||
|
#include "autocalcspell.hpp"
|
||||||
|
|
||||||
|
#include <climits>
|
||||||
|
|
||||||
|
#include "../mwworld/esmstore.hpp"
|
||||||
|
|
||||||
|
#include "../mwbase/world.hpp"
|
||||||
|
#include "../mwbase/environment.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
namespace MWMechanics
|
||||||
|
{
|
||||||
|
|
||||||
|
struct SchoolCaps
|
||||||
|
{
|
||||||
|
int mCount;
|
||||||
|
int mLimit;
|
||||||
|
bool mReachedLimit;
|
||||||
|
int mMinCost;
|
||||||
|
std::string mWeakestSpell;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::set<std::string> autoCalcNpcSpells(const int *actorSkills, const int *actorAttributes, const ESM::Race* race)
|
||||||
|
{
|
||||||
|
const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
|
||||||
|
static const float fNPCbaseMagickaMult = gmst.find("fNPCbaseMagickaMult")->getFloat();
|
||||||
|
float baseMagicka = fNPCbaseMagickaMult * actorAttributes[ESM::Attribute::Intelligence];
|
||||||
|
|
||||||
|
static const std::string schools[] = {
|
||||||
|
"alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration"
|
||||||
|
};
|
||||||
|
static int iAutoSpellSchoolMax[6];
|
||||||
|
static bool init = false;
|
||||||
|
if (!init)
|
||||||
|
{
|
||||||
|
for (int i=0; i<6; ++i)
|
||||||
|
{
|
||||||
|
const std::string& gmstName = "iAutoSpell" + schools[i] + "Max";
|
||||||
|
iAutoSpellSchoolMax[i] = gmst.find(gmstName)->getInt();
|
||||||
|
}
|
||||||
|
init = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::map<int, SchoolCaps> schoolCaps;
|
||||||
|
for (int i=0; i<6; ++i)
|
||||||
|
{
|
||||||
|
SchoolCaps caps;
|
||||||
|
caps.mCount = 0;
|
||||||
|
caps.mLimit = iAutoSpellSchoolMax[i];
|
||||||
|
caps.mReachedLimit = iAutoSpellSchoolMax[i] <= 0;
|
||||||
|
caps.mMinCost = INT_MAX;
|
||||||
|
caps.mWeakestSpell.clear();
|
||||||
|
schoolCaps[i] = caps;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::set<std::string> selectedSpells;
|
||||||
|
|
||||||
|
const MWWorld::Store<ESM::Spell> &spells =
|
||||||
|
MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>();
|
||||||
|
for (MWWorld::Store<ESM::Spell>::iterator iter = spells.begin(); iter != spells.end(); ++iter)
|
||||||
|
{
|
||||||
|
const ESM::Spell* spell = &*iter;
|
||||||
|
|
||||||
|
if (spell->mData.mType != ESM::Spell::ST_Spell)
|
||||||
|
continue;
|
||||||
|
if (!(spell->mData.mFlags & ESM::Spell::F_Autocalc))
|
||||||
|
continue;
|
||||||
|
static const int iAutoSpellTimesCanCast = gmst.find("iAutoSpellTimesCanCast")->getInt();
|
||||||
|
if (baseMagicka < iAutoSpellTimesCanCast * spell->mData.mCost)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (race && std::find(race->mPowers.mList.begin(), race->mPowers.mList.end(), spell->mId) != race->mPowers.mList.end())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!attrSkillCheck(spell, actorSkills, actorAttributes))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
int school;
|
||||||
|
float skillTerm;
|
||||||
|
calcWeakestSchool(spell, actorSkills, school, skillTerm);
|
||||||
|
assert(school >= 0 && school < 6);
|
||||||
|
SchoolCaps& cap = schoolCaps[school];
|
||||||
|
|
||||||
|
if (cap.mReachedLimit && spell->mData.mCost <= cap.mMinCost)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
static const float fAutoSpellChance = gmst.find("fAutoSpellChance")->getFloat();
|
||||||
|
if (calcAutoCastChance(spell, actorSkills, actorAttributes, school) < fAutoSpellChance)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
selectedSpells.insert(spell->mId);
|
||||||
|
|
||||||
|
if (cap.mReachedLimit)
|
||||||
|
{
|
||||||
|
selectedSpells.erase(cap.mWeakestSpell);
|
||||||
|
|
||||||
|
// Note: not school specific
|
||||||
|
cap.mMinCost = INT_MAX;
|
||||||
|
for (std::set<std::string>::iterator weakIt = selectedSpells.begin(); weakIt != selectedSpells.end(); ++weakIt)
|
||||||
|
{
|
||||||
|
const ESM::Spell* testSpell = spells.find(*weakIt);
|
||||||
|
if (testSpell->mData.mCost < cap.mMinCost) // XXX what if 2 candidates have the same cost?
|
||||||
|
{
|
||||||
|
cap.mMinCost = testSpell->mData.mCost;
|
||||||
|
cap.mWeakestSpell = testSpell->mId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cap.mCount += 1;
|
||||||
|
if (cap.mCount == cap.mLimit)
|
||||||
|
cap.mReachedLimit = true;
|
||||||
|
|
||||||
|
if (spell->mData.mCost < cap.mMinCost)
|
||||||
|
{
|
||||||
|
cap.mWeakestSpell = spell->mId;
|
||||||
|
cap.mMinCost = spell->mData.mCost;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return selectedSpells;
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(effectIt->mEffectID);
|
||||||
|
static const int iAutoSpellAttSkillMin = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("iAutoSpellAttSkillMin")->getInt();
|
||||||
|
|
||||||
|
if ((magicEffect->mData.mFlags & ESM::MagicEffect::TargetSkill))
|
||||||
|
{
|
||||||
|
assert (effectIt->mSkill >= 0 && effectIt->mSkill < ESM::Skill::Length);
|
||||||
|
if (actorSkills[effectIt->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)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ESM::Skill::SkillEnum mapSchoolToSkill(int school)
|
||||||
|
{
|
||||||
|
std::map<int, ESM::Skill::SkillEnum> schoolSkillMap; // maps spell school to skill id
|
||||||
|
schoolSkillMap[0] = ESM::Skill::Alteration;
|
||||||
|
schoolSkillMap[1] = ESM::Skill::Conjuration;
|
||||||
|
schoolSkillMap[3] = ESM::Skill::Illusion;
|
||||||
|
schoolSkillMap[2] = ESM::Skill::Destruction;
|
||||||
|
schoolSkillMap[4] = ESM::Skill::Mysticism;
|
||||||
|
schoolSkillMap[5] = ESM::Skill::Restoration;
|
||||||
|
assert(schoolSkillMap.find(school) != schoolSkillMap.end());
|
||||||
|
return schoolSkillMap[school];
|
||||||
|
}
|
||||||
|
|
||||||
|
void calcWeakestSchool (const ESM::Spell* spell, const int* actorSkills, int& effectiveSchool, float& skillTerm)
|
||||||
|
{
|
||||||
|
float minChance = FLT_MAX;
|
||||||
|
|
||||||
|
const ESM::EffectList& effects = spell->mEffects;
|
||||||
|
for (std::vector<ESM::ENAMstruct>::const_iterator it = effects.mList.begin(); it != effects.mList.end(); ++it)
|
||||||
|
{
|
||||||
|
const ESM::ENAMstruct& effect = *it;
|
||||||
|
float x = effect.mDuration;
|
||||||
|
|
||||||
|
const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(effect.mEffectID);
|
||||||
|
if (!(magicEffect->mData.mFlags & ESM::MagicEffect::UncappedDamage))
|
||||||
|
x = std::max(1.f, x);
|
||||||
|
|
||||||
|
x *= 0.1f * magicEffect->mData.mBaseCost;
|
||||||
|
x *= 0.5f * (effect.mMagnMin + effect.mMagnMax);
|
||||||
|
x += effect.mArea * 0.05f * magicEffect->mData.mBaseCost; // XXX spell.radius
|
||||||
|
if (magicEffect->mData.mFlags & ESM::MagicEffect::CastTarget) // XXX effect.flags & CAST_TARGET
|
||||||
|
x *= 1.5f;
|
||||||
|
|
||||||
|
static const float fEffectCostMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fEffectCostMult")->getFloat();
|
||||||
|
x *= fEffectCostMult;
|
||||||
|
|
||||||
|
float s = 2.f * actorSkills[mapSchoolToSkill(magicEffect->mData.mSchool)];
|
||||||
|
if (s - x < minChance)
|
||||||
|
{
|
||||||
|
minChance = s - x;
|
||||||
|
effectiveSchool = magicEffect->mData.mSchool;
|
||||||
|
skillTerm = s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float calcAutoCastChance(const ESM::Spell *spell, const int *actorSkills, const int *actorAttributes, int effectiveSchool)
|
||||||
|
{
|
||||||
|
if (spell->mData.mType != ESM::Spell::ST_Spell)
|
||||||
|
return 100.f;
|
||||||
|
|
||||||
|
if (spell->mData.mFlags & ESM::Spell::F_Always)
|
||||||
|
return 100.f;
|
||||||
|
|
||||||
|
float skillTerm;
|
||||||
|
if (effectiveSchool != -1)
|
||||||
|
skillTerm = 2.f * actorSkills[mapSchoolToSkill(effectiveSchool)];
|
||||||
|
else
|
||||||
|
calcWeakestSchool(spell, actorSkills, effectiveSchool, skillTerm); // Note effectiveSchool is unused after this
|
||||||
|
|
||||||
|
float castChance = skillTerm - spell->mData.mCost + 0.2f * actorAttributes[ESM::Attribute::Willpower] + 0.1f * actorAttributes[ESM::Attribute::Luck];
|
||||||
|
return castChance;
|
||||||
|
}
|
||||||
|
}
|
31
apps/openmw/mwmechanics/autocalcspell.hpp
Normal file
31
apps/openmw/mwmechanics/autocalcspell.hpp
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
#ifndef OPENMW_AUTOCALCSPELL_H
|
||||||
|
#define OPENMW_AUTOCALCSPELL_H
|
||||||
|
|
||||||
|
#include <cfloat>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
|
#include <components/esm/loadspel.hpp>
|
||||||
|
#include <components/esm/loadskil.hpp>
|
||||||
|
#include <components/esm/loadrace.hpp>
|
||||||
|
|
||||||
|
namespace MWMechanics
|
||||||
|
{
|
||||||
|
|
||||||
|
/// Contains algorithm for calculating an NPC's spells based on stats
|
||||||
|
/// @note We might want to move this code to a component later, so the editor can use it for preview purposes
|
||||||
|
|
||||||
|
std::set<std::string> autoCalcNpcSpells(const int* actorSkills, const int* actorAttributes, const ESM::Race* race);
|
||||||
|
|
||||||
|
// Helpers
|
||||||
|
|
||||||
|
bool attrSkillCheck (const ESM::Spell* spell, const int* actorSkills, const int* actorAttributes);
|
||||||
|
|
||||||
|
ESM::Skill::SkillEnum mapSchoolToSkill(int school);
|
||||||
|
|
||||||
|
void calcWeakestSchool(const ESM::Spell* spell, const int* actorSkills, int& effectiveSchool, float& skillTerm);
|
||||||
|
|
||||||
|
float calcAutoCastChance(const ESM::Spell* spell, const int* actorSkills, const int* actorAttributes, int effectiveSchool);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -19,6 +19,7 @@
|
||||||
#include <OgreSceneNode.h>
|
#include <OgreSceneNode.h>
|
||||||
|
|
||||||
#include "spellcasting.hpp"
|
#include "spellcasting.hpp"
|
||||||
|
#include "autocalcspell.hpp"
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
@ -155,19 +156,6 @@ namespace MWMechanics
|
||||||
npcStats.getSkill (index).setBase (
|
npcStats.getSkill (index).setBase (
|
||||||
npcStats.getSkill (index).getBase() + bonus);
|
npcStats.getSkill (index).getBase() + bonus);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i==1)
|
|
||||||
{
|
|
||||||
// Major skill - add starting spells for this skill if existing
|
|
||||||
const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();
|
|
||||||
MWWorld::Store<ESM::Spell>::iterator it = store.get<ESM::Spell>().begin();
|
|
||||||
for (; it != store.get<ESM::Spell>().end(); ++it)
|
|
||||||
{
|
|
||||||
if (it->mData.mFlags & ESM::Spell::F_PCStart
|
|
||||||
&& spellSchoolToSkill(getSpellSchool(&*it, ptr)) == index)
|
|
||||||
creatureStats.getSpells().add(it->mId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -190,6 +178,88 @@ namespace MWMechanics
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// F_PCStart spells
|
||||||
|
static const float fPCbaseMagickaMult = esmStore.get<ESM::GameSetting>().find("fPCbaseMagickaMult")->getFloat();
|
||||||
|
|
||||||
|
float baseMagicka = fPCbaseMagickaMult * creatureStats.getAttribute(ESM::Attribute::Intelligence).getBase();
|
||||||
|
bool reachedLimit = false;
|
||||||
|
const ESM::Spell* weakestSpell = NULL;
|
||||||
|
int minCost = INT_MAX;
|
||||||
|
|
||||||
|
std::set<std::string> selectedSpells;
|
||||||
|
|
||||||
|
const ESM::Race* race = NULL;
|
||||||
|
if (mRaceSelected)
|
||||||
|
race = esmStore.get<ESM::Race>().find(player->mRace);
|
||||||
|
|
||||||
|
int skills[ESM::Skill::Length];
|
||||||
|
for (int i=0; i<ESM::Skill::Length; ++i)
|
||||||
|
skills[i] = npcStats.getSkill(i).getBase();
|
||||||
|
|
||||||
|
int attributes[ESM::Attribute::Length];
|
||||||
|
for (int i=0; i<ESM::Attribute::Length; ++i)
|
||||||
|
attributes[i] = npcStats.getAttribute(i).getBase();
|
||||||
|
|
||||||
|
const MWWorld::Store<ESM::Spell> &spells =
|
||||||
|
esmStore.get<ESM::Spell>();
|
||||||
|
for (MWWorld::Store<ESM::Spell>::iterator iter = spells.begin(); iter != spells.end(); ++iter)
|
||||||
|
{
|
||||||
|
const ESM::Spell* spell = &*iter;
|
||||||
|
|
||||||
|
if (spell->mData.mType != ESM::Spell::ST_Spell)
|
||||||
|
continue;
|
||||||
|
if (!(spell->mData.mFlags & ESM::Spell::F_PCStart))
|
||||||
|
continue;
|
||||||
|
if (reachedLimit && spell->mData.mCost <= minCost)
|
||||||
|
continue;
|
||||||
|
if (selectedSpells.find(spell->mId) != selectedSpells.end())
|
||||||
|
continue;
|
||||||
|
if (race && std::find(race->mPowers.mList.begin(), race->mPowers.mList.end(), spell->mId) != race->mPowers.mList.end())
|
||||||
|
continue;
|
||||||
|
if (baseMagicka < spell->mData.mCost)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
static const float fAutoPCSpellChance = esmStore.get<ESM::GameSetting>().find("fAutoPCSpellChance")->getFloat();
|
||||||
|
if (calcAutoCastChance(spell, skills, attributes, -1) < fAutoPCSpellChance)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!attrSkillCheck(spell, skills, attributes))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
selectedSpells.insert(spell->mId);
|
||||||
|
|
||||||
|
if (reachedLimit)
|
||||||
|
{
|
||||||
|
selectedSpells.erase(weakestSpell->mId);
|
||||||
|
|
||||||
|
minCost = INT_MAX;
|
||||||
|
for (std::set<std::string>::iterator weakIt = selectedSpells.begin(); weakIt != selectedSpells.end(); ++weakIt)
|
||||||
|
{
|
||||||
|
const ESM::Spell* testSpell = esmStore.get<ESM::Spell>().find(*weakIt);
|
||||||
|
if (testSpell->mData.mCost < minCost) // XXX what if 2 candidates have the same cost?
|
||||||
|
// Note iAutoPCSpellMax is 100 by default, so reachedLimit is very unlikely to happen
|
||||||
|
{
|
||||||
|
minCost = testSpell->mData.mCost;
|
||||||
|
weakestSpell = testSpell;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (spell->mData.mCost < minCost)
|
||||||
|
{
|
||||||
|
weakestSpell = spell;
|
||||||
|
minCost = weakestSpell->mData.mCost;
|
||||||
|
}
|
||||||
|
static const unsigned int iAutoPCSpellMax = esmStore.get<ESM::GameSetting>().find("iAutoPCSpellMax")->getInt();
|
||||||
|
if (selectedSpells.size() == iAutoPCSpellMax)
|
||||||
|
reachedLimit = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (std::set<std::string>::iterator it = selectedSpells.begin(); it != selectedSpells.end(); ++it)
|
||||||
|
creatureStats.getSpells().add(*it);
|
||||||
|
|
||||||
// forced update and current value adjustments
|
// forced update and current value adjustments
|
||||||
mActors.updateActor (ptr, 0);
|
mActors.updateActor (ptr, 0);
|
||||||
|
|
||||||
|
|
|
@ -27,8 +27,8 @@ struct Spell
|
||||||
|
|
||||||
enum Flags
|
enum Flags
|
||||||
{
|
{
|
||||||
F_Autocalc = 1,
|
F_Autocalc = 1, // Can be selected by NPC spells auto-calc
|
||||||
F_PCStart = 2,
|
F_PCStart = 2, // Can be selected by player spells auto-calc
|
||||||
F_Always = 4 // Casting always succeeds
|
F_Always = 4 // Casting always succeeds
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue