mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-16 18:49:58 +00:00
Move NPC autocal code out to a separate class.
This commit is contained in:
parent
c7c0023ed2
commit
04c5c0d82a
6 changed files with 466 additions and 385 deletions
|
@ -19,7 +19,7 @@ opencs_hdrs_noqt (model/doc
|
|||
|
||||
opencs_units (model/world
|
||||
idtable idtableproxymodel regionmap data commanddispatcher idtablebase resourcetable nestedtableproxymodel idtree infotableproxymodel
|
||||
pathgridcommands
|
||||
pathgridcommands npcautocalc
|
||||
)
|
||||
|
||||
|
||||
|
@ -36,7 +36,7 @@ opencs_hdrs_noqt (model/world
|
|||
|
||||
|
||||
opencs_units (model/tools
|
||||
tools reportmodel mergeoperation
|
||||
tools reportmodel mergeoperation
|
||||
)
|
||||
|
||||
opencs_units_noqt (model/tools
|
||||
|
|
|
@ -10,10 +10,6 @@
|
|||
#include <components/esm/loadglob.hpp>
|
||||
#include <components/esm/cellref.hpp>
|
||||
|
||||
#include <components/autocalc/autocalc.hpp>
|
||||
#include <components/autocalc/autocalcspell.hpp>
|
||||
#include <components/autocalc/store.hpp>
|
||||
|
||||
#include "idtable.hpp"
|
||||
#include "idtree.hpp"
|
||||
#include "columnimp.hpp"
|
||||
|
@ -23,70 +19,7 @@
|
|||
#include "resourcetable.hpp"
|
||||
#include "nestedcoladapterimp.hpp"
|
||||
#include "npcstats.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
class CSStore : public AutoCalc::StoreCommon
|
||||
{
|
||||
const CSMWorld::IdCollection<ESM::GameSetting>& mGmstTable;
|
||||
const CSMWorld::IdCollection<ESM::Skill>& mSkillTable;
|
||||
const CSMWorld::IdCollection<ESM::MagicEffect>& mMagicEffectTable;
|
||||
const CSMWorld::NestedIdCollection<ESM::Spell>& mSpells;
|
||||
|
||||
public:
|
||||
|
||||
CSStore(const CSMWorld::IdCollection<ESM::GameSetting>& gmst,
|
||||
const CSMWorld::IdCollection<ESM::Skill>& skills,
|
||||
const CSMWorld::IdCollection<ESM::MagicEffect>& magicEffects,
|
||||
const CSMWorld::NestedIdCollection<ESM::Spell>& spells)
|
||||
: mGmstTable(gmst), mSkillTable(skills), mMagicEffectTable(magicEffects), mSpells(spells)
|
||||
{ }
|
||||
|
||||
~CSStore() {}
|
||||
|
||||
virtual int findGmstInt(const std::string& name) const
|
||||
{
|
||||
return mGmstTable.getRecord(name).get().getInt();
|
||||
}
|
||||
|
||||
virtual float findGmstFloat(const std::string& name) const
|
||||
{
|
||||
return mGmstTable.getRecord(name).get().getFloat();
|
||||
}
|
||||
|
||||
virtual const ESM::Skill *findSkill(int index) const
|
||||
{
|
||||
// if the skill does not exist, throws std::runtime_error ("invalid ID: " + id)
|
||||
return &mSkillTable.getRecord(ESM::Skill::indexToId(index)).get();
|
||||
}
|
||||
|
||||
virtual const ESM::MagicEffect* findMagicEffect(int id) const
|
||||
{
|
||||
// if the magic effect does not exist, throws std::runtime_error ("invalid ID: " + id)
|
||||
return &mMagicEffectTable.getRecord(ESM::MagicEffect::indexToId((short)id)).get();
|
||||
}
|
||||
|
||||
virtual void getSpells(std::vector<ESM::Spell*>& spells)
|
||||
{
|
||||
// prepare data in a format used by OpenMW store
|
||||
for (int index = 0; index < mSpells.getSize(); ++index)
|
||||
spells.push_back(const_cast<ESM::Spell *>(&mSpells.getRecord(index).get()));
|
||||
}
|
||||
};
|
||||
|
||||
unsigned short autoCalculateMana(AutoCalc::StatsBase& stats)
|
||||
{
|
||||
return stats.getBaseAttribute(ESM::Attribute::Intelligence) * 2;
|
||||
}
|
||||
|
||||
unsigned short autoCalculateFatigue(AutoCalc::StatsBase& stats)
|
||||
{
|
||||
return stats.getBaseAttribute(ESM::Attribute::Strength)
|
||||
+ stats.getBaseAttribute(ESM::Attribute::Willpower)
|
||||
+ stats.getBaseAttribute(ESM::Attribute::Agility)
|
||||
+ stats.getBaseAttribute(ESM::Attribute::Endurance);
|
||||
}
|
||||
}
|
||||
#include "npcautocalc.hpp"
|
||||
|
||||
void CSMWorld::Data::addModel (QAbstractItemModel *model, UniversalId::Type type, bool update)
|
||||
{
|
||||
|
@ -129,8 +62,9 @@ int CSMWorld::Data::count (RecordBase::State state, const CollectionBase& collec
|
|||
}
|
||||
|
||||
CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourcesManager)
|
||||
: mEncoder (encoding), mPathgrids (mCells), mReferenceables(self()), mRefs (mCells),
|
||||
mResourcesManager (resourcesManager), mReader (0), mDialogue (0), mReaderIndex(0)
|
||||
: mEncoder (encoding), mPathgrids (mCells), mReferenceables (self()), mRefs (mCells),
|
||||
mResourcesManager (resourcesManager), mReader (0), mDialogue (0), mReaderIndex(0),
|
||||
mNpcAutoCalc (0)
|
||||
{
|
||||
int index = 0;
|
||||
|
||||
|
@ -621,32 +555,18 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc
|
|||
CSMWorld::IdTree *objects =
|
||||
static_cast<CSMWorld::IdTree*>(getTableModel(UniversalId::Type_Referenceable));
|
||||
|
||||
connect (gmsts, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),
|
||||
this, SLOT (gmstDataChanged (const QModelIndex&, const QModelIndex&)));
|
||||
connect (skills, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),
|
||||
this, SLOT (skillDataChanged (const QModelIndex&, const QModelIndex&)));
|
||||
connect (classes, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),
|
||||
this, SLOT (classDataChanged (const QModelIndex&, const QModelIndex&)));
|
||||
connect (races, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),
|
||||
this, SLOT (raceDataChanged (const QModelIndex&, const QModelIndex&)));
|
||||
connect (objects, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),
|
||||
this, SLOT (npcDataChanged (const QModelIndex&, const QModelIndex&)));
|
||||
connect (this, SIGNAL (updateNpcAutocalc (int, const std::string&)),
|
||||
objects, SLOT (updateNpcAutocalc (int, const std::string&)));
|
||||
connect (this, SIGNAL (cacheNpcStats (const std::string&, NpcStats*)),
|
||||
this, SLOT (cacheNpcStatsEvent (const std::string&, NpcStats*)));
|
||||
mNpcAutoCalc = new NpcAutoCalc (self(), gmsts, skills, classes, races, objects);
|
||||
|
||||
mRefLoadCache.clear(); // clear here rather than startLoading() and continueLoading() for multiple content files
|
||||
}
|
||||
|
||||
CSMWorld::Data::~Data()
|
||||
{
|
||||
clearNpcStatsCache();
|
||||
|
||||
for (std::vector<QAbstractItemModel *>::iterator iter (mModels.begin()); iter!=mModels.end(); ++iter)
|
||||
delete *iter;
|
||||
|
||||
delete mReader;
|
||||
delete mNpcAutoCalc;
|
||||
}
|
||||
|
||||
const CSMWorld::IdCollection<ESM::Global>& CSMWorld::Data::getGlobals() const
|
||||
|
@ -946,6 +866,11 @@ void CSMWorld::Data::setMetaData (const MetaData& metaData)
|
|||
mMetaData.setRecord (0, record);
|
||||
}
|
||||
|
||||
const CSMWorld::NpcAutoCalc& CSMWorld::Data::getNpcAutoCalc() const
|
||||
{
|
||||
return *mNpcAutoCalc;
|
||||
}
|
||||
|
||||
QAbstractItemModel *CSMWorld::Data::getTableModel (const CSMWorld::UniversalId& id)
|
||||
{
|
||||
std::map<UniversalId::Type, QAbstractItemModel *>::iterator iter = mModelIndex.find (id.getType());
|
||||
|
@ -1297,271 +1222,3 @@ const CSMWorld::Data& CSMWorld::Data::self ()
|
|||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
void CSMWorld::Data::skillDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight)
|
||||
{
|
||||
// mData.mAttribute (affects attributes skill bonus autocalc)
|
||||
// mData.mSpecialization (affects skills autocalc)
|
||||
CSMWorld::IdTable *skillModel =
|
||||
static_cast<CSMWorld::IdTable*>(getTableModel(CSMWorld::UniversalId::Type_Skill));
|
||||
|
||||
int attributeColumn = skillModel->findColumnIndex(CSMWorld::Columns::ColumnId_Attribute);
|
||||
int specialisationColumn = skillModel->findColumnIndex(CSMWorld::Columns::ColumnId_Specialisation);
|
||||
|
||||
if ((topLeft.column() <= attributeColumn && attributeColumn <= bottomRight.column())
|
||||
|| (topLeft.column() <= specialisationColumn && specialisationColumn <= bottomRight.column()))
|
||||
{
|
||||
clearNpcStatsCache();
|
||||
|
||||
std::string empty;
|
||||
emit updateNpcAutocalc(0/*all*/, empty);
|
||||
}
|
||||
}
|
||||
|
||||
void CSMWorld::Data::classDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight)
|
||||
{
|
||||
// update autocalculated attributes/skills of every NPC with matching class
|
||||
// - mData.mAttribute[2]
|
||||
// - mData.mSkills[5][2]
|
||||
// - mData.mSpecialization
|
||||
CSMWorld::IdTable *classModel =
|
||||
static_cast<CSMWorld::IdTable*>(getTableModel(CSMWorld::UniversalId::Type_Class));
|
||||
|
||||
int attribute1Column = classModel->findColumnIndex(CSMWorld::Columns::ColumnId_Attribute1); // +1
|
||||
int majorSkill1Column = classModel->findColumnIndex(CSMWorld::Columns::ColumnId_MajorSkill1); // +4
|
||||
int minorSkill1Column = classModel->findColumnIndex(CSMWorld::Columns::ColumnId_MinorSkill1); // +4
|
||||
int specialisationColumn = classModel->findColumnIndex(CSMWorld::Columns::ColumnId_Specialisation);
|
||||
|
||||
if ((topLeft.column() > attribute1Column+1 || attribute1Column > bottomRight.column())
|
||||
&& (topLeft.column() > majorSkill1Column+4 || majorSkill1Column > bottomRight.column())
|
||||
&& (topLeft.column() > minorSkill1Column+4 || minorSkill1Column > bottomRight.column())
|
||||
&& (topLeft.column() > specialisationColumn || specialisationColumn > bottomRight.column()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// get the affected class
|
||||
int idColumn = classModel->findColumnIndex(CSMWorld::Columns::ColumnId_Id);
|
||||
for (int classRow = topLeft.row(); classRow <= bottomRight.row(); ++classRow)
|
||||
{
|
||||
clearNpcStatsCache();
|
||||
|
||||
std::string classId =
|
||||
classModel->data(classModel->index(classRow, idColumn)).toString().toUtf8().constData();
|
||||
emit updateNpcAutocalc(1/*class*/, classId);
|
||||
}
|
||||
}
|
||||
|
||||
void CSMWorld::Data::raceDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight)
|
||||
{
|
||||
// affects racial bonus attributes & skills
|
||||
// - mData.mAttributeValues[]
|
||||
// - mData.mBonus[].mBonus
|
||||
// - mPowers.mList[]
|
||||
CSMWorld::IdTree *raceModel =
|
||||
static_cast<CSMWorld::IdTree*>(getTableModel(CSMWorld::UniversalId::Type_Race));
|
||||
|
||||
int attrColumn = raceModel->findColumnIndex(CSMWorld::Columns::ColumnId_RaceAttributes);
|
||||
int bonusColumn = raceModel->findColumnIndex(CSMWorld::Columns::ColumnId_RaceSkillBonus);
|
||||
int powersColumn = raceModel->findColumnIndex(CSMWorld::Columns::ColumnId_PowerList);
|
||||
|
||||
bool match = false;
|
||||
int raceRow = topLeft.row();
|
||||
int raceEnd = bottomRight.row();
|
||||
if (topLeft.parent().isValid() && bottomRight.parent().isValid())
|
||||
{
|
||||
if ((topLeft.parent().column() <= attrColumn && attrColumn <= bottomRight.parent().column())
|
||||
|| (topLeft.parent().column() <= bonusColumn && bonusColumn <= bottomRight.parent().column())
|
||||
|| (topLeft.parent().column() <= powersColumn && powersColumn <= bottomRight.parent().column()))
|
||||
{
|
||||
match = true; // TODO: check for specific nested column?
|
||||
raceRow = topLeft.parent().row();
|
||||
raceEnd = bottomRight.parent().row();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((topLeft.column() <= attrColumn && attrColumn <= bottomRight.column())
|
||||
|| (topLeft.column() <= bonusColumn && bonusColumn <= bottomRight.column())
|
||||
|| (topLeft.column() <= powersColumn && powersColumn <= bottomRight.column()))
|
||||
{
|
||||
match = true; // maybe the whole table changed
|
||||
}
|
||||
}
|
||||
|
||||
if (!match)
|
||||
return;
|
||||
|
||||
// update autocalculated attributes/skills of every NPC with matching race
|
||||
int idColumn = raceModel->findColumnIndex(CSMWorld::Columns::ColumnId_Id);
|
||||
for (; raceRow <= raceEnd; ++raceRow)
|
||||
{
|
||||
clearNpcStatsCache();
|
||||
|
||||
std::string raceId =
|
||||
raceModel->data(raceModel->index(raceRow, idColumn)).toString().toUtf8().constData();
|
||||
emit updateNpcAutocalc(2/*race*/, raceId);
|
||||
}
|
||||
}
|
||||
|
||||
void CSMWorld::Data::npcDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight)
|
||||
{
|
||||
// TODO: for now always recalculate
|
||||
clearNpcStatsCache();
|
||||
}
|
||||
|
||||
void CSMWorld::Data::gmstDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight)
|
||||
{
|
||||
static const QStringList gmsts(QStringList()<< "fNPCbaseMagickaMult" << "fAutoSpellChance"
|
||||
<< "fEffectCostMult" << "iAutoSpellAlterationMax" << "iAutoSpellConjurationMax"
|
||||
<< "iAutoSpellDestructionMax" << "iAutoSpellIllusionMax" << "iAutoSpellMysticismMax"
|
||||
<< "iAutoSpellRestorationMax" << "iAutoSpellTimesCanCast" << "iAutoSpellAttSkillMin");
|
||||
|
||||
bool match = false;
|
||||
for (int row = topLeft.row(); row <= bottomRight.row(); ++row)
|
||||
{
|
||||
if (gmsts.contains(mGmsts.getRecord(row).get().mId.c_str()))
|
||||
{
|
||||
match = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!match)
|
||||
return;
|
||||
|
||||
clearNpcStatsCache();
|
||||
|
||||
std::string empty;
|
||||
emit updateNpcAutocalc(0/*all*/, empty);
|
||||
}
|
||||
|
||||
void CSMWorld::Data::clearNpcStatsCache ()
|
||||
{
|
||||
for (std::map<std::string, CSMWorld::NpcStats*>::iterator it (mNpcStatCache.begin());
|
||||
it != mNpcStatCache.end(); ++it)
|
||||
delete it->second;
|
||||
|
||||
mNpcStatCache.clear();
|
||||
}
|
||||
|
||||
CSMWorld::NpcStats* CSMWorld::Data::npcAutoCalculate(const ESM::NPC& npc) const
|
||||
{
|
||||
CSMWorld::NpcStats * cachedStats = getCachedNpcData (npc.mId);
|
||||
if (cachedStats)
|
||||
return cachedStats;
|
||||
|
||||
int raceIndex = mRaces.searchId(npc.mRace);
|
||||
int classIndex = mClasses.searchId(npc.mClass);
|
||||
// this can happen when creating a new game from scratch
|
||||
if (raceIndex == -1 || classIndex == -1)
|
||||
return 0;
|
||||
|
||||
const ESM::Race *race = &mRaces.getRecord(raceIndex).get();
|
||||
const ESM::Class *class_ = &mClasses.getRecord(classIndex).get();
|
||||
|
||||
bool autoCalc = npc.mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS;
|
||||
short level = npc.mNpdt52.mLevel;
|
||||
if (autoCalc)
|
||||
level = npc.mNpdt12.mLevel;
|
||||
|
||||
std::auto_ptr<CSMWorld::NpcStats> stats (new CSMWorld::NpcStats());
|
||||
|
||||
CSStore store(mGmsts, mSkills, mMagicEffects, mSpells);
|
||||
|
||||
if (autoCalc)
|
||||
{
|
||||
AutoCalc::autoCalcAttributesImpl (&npc, race, class_, level, *stats, &store);
|
||||
|
||||
stats->setHealth(autoCalculateHealth(level, class_, *stats));
|
||||
stats->setMana(autoCalculateMana(*stats));
|
||||
stats->setFatigue(autoCalculateFatigue(*stats));
|
||||
|
||||
AutoCalc::autoCalcSkillsImpl(&npc, race, class_, level, *stats, &store);
|
||||
|
||||
AutoCalc::autoCalculateSpells(race, *stats, &store);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (std::vector<std::string>::const_iterator it = npc.mSpells.mList.begin();
|
||||
it != npc.mSpells.mList.end(); ++it)
|
||||
{
|
||||
stats->addSpell(*it);
|
||||
}
|
||||
}
|
||||
|
||||
// update spell info
|
||||
const std::vector<std::string> &racePowers = race->mPowers.mList;
|
||||
for (unsigned int i = 0; i < racePowers.size(); ++i)
|
||||
{
|
||||
int type = -1;
|
||||
int spellIndex = mSpells.searchId(racePowers[i]);
|
||||
if (spellIndex != -1)
|
||||
type = mSpells.getRecord(spellIndex).get().mData.mType;
|
||||
stats->addPowers(racePowers[i], type);
|
||||
}
|
||||
// cost/chance
|
||||
int skills[ESM::Skill::Length];
|
||||
if (autoCalc)
|
||||
for (int i = 0; i< ESM::Skill::Length; ++i)
|
||||
skills[i] = stats->getBaseSkill(i);
|
||||
else
|
||||
for (int i = 0; i< ESM::Skill::Length; ++i)
|
||||
skills[i] = npc.mNpdt52.mSkills[i];
|
||||
|
||||
int attributes[ESM::Attribute::Length];
|
||||
if (autoCalc)
|
||||
for (int i = 0; i< ESM::Attribute::Length; ++i)
|
||||
attributes[i] = stats->getBaseAttribute(i);
|
||||
else
|
||||
{
|
||||
attributes[ESM::Attribute::Strength] = npc.mNpdt52.mStrength;
|
||||
attributes[ESM::Attribute::Willpower] = npc.mNpdt52.mWillpower;
|
||||
attributes[ESM::Attribute::Agility] = npc.mNpdt52.mAgility;
|
||||
attributes[ESM::Attribute::Speed] = npc.mNpdt52.mSpeed;
|
||||
attributes[ESM::Attribute::Endurance] = npc.mNpdt52.mEndurance;
|
||||
attributes[ESM::Attribute::Personality] = npc.mNpdt52.mPersonality;
|
||||
attributes[ESM::Attribute::Luck] = npc.mNpdt52.mLuck;
|
||||
}
|
||||
|
||||
const std::vector<CSMWorld::SpellInfo>& spells = stats->spells();
|
||||
for (std::vector<SpellInfo>::const_iterator it = spells.begin(); it != spells.end(); ++it)
|
||||
{
|
||||
int cost = -1;
|
||||
int spellIndex = mSpells.searchId((*it).mName);
|
||||
const ESM::Spell* spell = 0;
|
||||
if (spellIndex != -1)
|
||||
{
|
||||
spell = &mSpells.getRecord(spellIndex).get();
|
||||
cost = spell->mData.mCost;
|
||||
|
||||
int school;
|
||||
float skillTerm;
|
||||
AutoCalc::calcWeakestSchool(spell, skills, school, skillTerm, &store);
|
||||
float chance = calcAutoCastChance(spell, skills, attributes, school, &store);
|
||||
|
||||
stats->addCostAndChance((*it).mName, cost, (int)ceil(chance)); // percent
|
||||
}
|
||||
}
|
||||
|
||||
if (stats.get() == 0)
|
||||
return 0;
|
||||
|
||||
CSMWorld::NpcStats *result = stats.release();
|
||||
emit cacheNpcStats (npc.mId, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
void CSMWorld::Data::cacheNpcStatsEvent (const std::string& id, CSMWorld::NpcStats *stats)
|
||||
{
|
||||
mNpcStatCache[id] = stats;
|
||||
}
|
||||
|
||||
CSMWorld::NpcStats* CSMWorld::Data::getCachedNpcData (const std::string& id) const
|
||||
{
|
||||
std::map<std::string, CSMWorld::NpcStats*>::const_iterator it = mNpcStatCache.find(id);
|
||||
if (it != mNpcStatCache.end())
|
||||
return it->second;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -62,6 +62,7 @@ namespace CSMWorld
|
|||
class ResourcesManager;
|
||||
class Resources;
|
||||
class NpcStats;
|
||||
class NpcAutoCalc;
|
||||
|
||||
class Data : public QObject
|
||||
{
|
||||
|
@ -109,7 +110,7 @@ namespace CSMWorld
|
|||
|
||||
std::vector<boost::shared_ptr<ESM::ESMReader> > mReaders;
|
||||
|
||||
std::map<std::string, NpcStats*> mNpcStatCache;
|
||||
NpcAutoCalc *mNpcAutoCalc;
|
||||
|
||||
// not implemented
|
||||
Data (const Data&);
|
||||
|
@ -282,37 +283,17 @@ namespace CSMWorld
|
|||
int count (RecordBase::State state) const;
|
||||
///< Return number of top-level records with the given \a state.
|
||||
|
||||
NpcStats* npcAutoCalculate (const ESM::NPC& npc) const;
|
||||
|
||||
NpcStats* getCachedNpcData (const std::string& id) const;
|
||||
const NpcAutoCalc& getNpcAutoCalc() const;
|
||||
|
||||
signals:
|
||||
|
||||
void idListChanged();
|
||||
|
||||
// refresh NPC dialogue subviews via object table model
|
||||
void updateNpcAutocalc (int type, const std::string& id);
|
||||
|
||||
void cacheNpcStats (const std::string& id, NpcStats *stats) const;
|
||||
|
||||
private slots:
|
||||
|
||||
void dataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);
|
||||
|
||||
void rowsChanged (const QModelIndex& parent, int start, int end);
|
||||
|
||||
// for autocalc updates when gmst/race/class/skils tables change
|
||||
void gmstDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);
|
||||
|
||||
void raceDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);
|
||||
|
||||
void classDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);
|
||||
|
||||
void skillDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);
|
||||
|
||||
void npcDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);
|
||||
|
||||
void cacheNpcStatsEvent (const std::string& id, NpcStats *stats);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
367
apps/opencs/model/world/npcautocalc.cpp
Normal file
367
apps/opencs/model/world/npcautocalc.cpp
Normal file
|
@ -0,0 +1,367 @@
|
|||
#include "npcautocalc.hpp"
|
||||
|
||||
#include <QStringList>
|
||||
|
||||
#include <components/autocalc/autocalc.hpp>
|
||||
#include <components/autocalc/autocalcspell.hpp>
|
||||
#include <components/autocalc/store.hpp>
|
||||
|
||||
#include "npcstats.hpp"
|
||||
#include "data.hpp"
|
||||
#include "idtable.hpp"
|
||||
#include "idtree.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
class CSStore : public AutoCalc::StoreCommon
|
||||
{
|
||||
const CSMWorld::IdCollection<ESM::GameSetting>& mGmstTable;
|
||||
const CSMWorld::IdCollection<ESM::Skill>& mSkillTable;
|
||||
const CSMWorld::IdCollection<ESM::MagicEffect>& mMagicEffectTable;
|
||||
const CSMWorld::NestedIdCollection<ESM::Spell>& mSpells;
|
||||
|
||||
public:
|
||||
|
||||
CSStore(const CSMWorld::IdCollection<ESM::GameSetting>& gmst,
|
||||
const CSMWorld::IdCollection<ESM::Skill>& skills,
|
||||
const CSMWorld::IdCollection<ESM::MagicEffect>& magicEffects,
|
||||
const CSMWorld::NestedIdCollection<ESM::Spell>& spells)
|
||||
: mGmstTable(gmst), mSkillTable(skills), mMagicEffectTable(magicEffects), mSpells(spells)
|
||||
{}
|
||||
|
||||
~CSStore() {}
|
||||
|
||||
virtual int findGmstInt(const std::string& name) const
|
||||
{
|
||||
return mGmstTable.getRecord(name).get().getInt();
|
||||
}
|
||||
|
||||
virtual float findGmstFloat(const std::string& name) const
|
||||
{
|
||||
return mGmstTable.getRecord(name).get().getFloat();
|
||||
}
|
||||
|
||||
virtual const ESM::Skill *findSkill(int index) const
|
||||
{
|
||||
// if the skill does not exist, throws std::runtime_error ("invalid ID: " + id)
|
||||
return &mSkillTable.getRecord(ESM::Skill::indexToId(index)).get();
|
||||
}
|
||||
|
||||
virtual const ESM::MagicEffect* findMagicEffect(int id) const
|
||||
{
|
||||
// if the magic effect does not exist, throws std::runtime_error ("invalid ID: " + id)
|
||||
return &mMagicEffectTable.getRecord(ESM::MagicEffect::indexToId((short)id)).get();
|
||||
}
|
||||
|
||||
virtual void getSpells(std::vector<ESM::Spell*>& spells)
|
||||
{
|
||||
// prepare data in a format used by OpenMW store
|
||||
for (int index = 0; index < mSpells.getSize(); ++index)
|
||||
spells.push_back(const_cast<ESM::Spell *>(&mSpells.getRecord(index).get()));
|
||||
}
|
||||
};
|
||||
|
||||
unsigned short autoCalculateMana(const AutoCalc::StatsBase& stats)
|
||||
{
|
||||
return stats.getBaseAttribute(ESM::Attribute::Intelligence) * 2;
|
||||
}
|
||||
|
||||
unsigned short autoCalculateFatigue(const AutoCalc::StatsBase& stats)
|
||||
{
|
||||
return stats.getBaseAttribute(ESM::Attribute::Strength)
|
||||
+ stats.getBaseAttribute(ESM::Attribute::Willpower)
|
||||
+ stats.getBaseAttribute(ESM::Attribute::Agility)
|
||||
+ stats.getBaseAttribute(ESM::Attribute::Endurance);
|
||||
}
|
||||
}
|
||||
|
||||
CSMWorld::NpcAutoCalc::NpcAutoCalc (const Data& data,
|
||||
const IdTable *gmsts, const IdTable *skills, const IdTable *classes, const IdTree *races, const IdTree *objects)
|
||||
: mData(data), mSkillModel(skills), mClassModel(classes), mRaceModel(races)
|
||||
{
|
||||
// for autocalc updates when gmst/race/class/skils tables change
|
||||
connect (gmsts, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),
|
||||
this, SLOT (gmstDataChanged (const QModelIndex&, const QModelIndex&)));
|
||||
connect (mSkillModel, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),
|
||||
this, SLOT (skillDataChanged (const QModelIndex&, const QModelIndex&)));
|
||||
connect (mClassModel, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),
|
||||
this, SLOT (classDataChanged (const QModelIndex&, const QModelIndex&)));
|
||||
connect (mRaceModel, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),
|
||||
this, SLOT (raceDataChanged (const QModelIndex&, const QModelIndex&)));
|
||||
connect (objects, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),
|
||||
this, SLOT (npcDataChanged (const QModelIndex&, const QModelIndex&)));
|
||||
connect (this, SIGNAL (updateNpcAutocalc (int, const std::string&)),
|
||||
objects, SLOT (updateNpcAutocalc (int, const std::string&)));
|
||||
|
||||
//connect (this, SIGNAL (cacheNpcStats (const std::string&, NpcStats*)),
|
||||
//this, SLOT (cacheNpcStatsEvent (const std::string&, NpcStats*)));
|
||||
}
|
||||
|
||||
CSMWorld::NpcAutoCalc::~NpcAutoCalc()
|
||||
{
|
||||
clearNpcStatsCache();
|
||||
}
|
||||
|
||||
void CSMWorld::NpcAutoCalc::skillDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight)
|
||||
{
|
||||
// mData.mAttribute (affects attributes skill bonus autocalc)
|
||||
// mData.mSpecialization (affects skills autocalc)
|
||||
int attributeColumn = mSkillModel->findColumnIndex(CSMWorld::Columns::ColumnId_Attribute);
|
||||
int specialisationColumn = mSkillModel->findColumnIndex(CSMWorld::Columns::ColumnId_Specialisation);
|
||||
|
||||
if ((topLeft.column() <= attributeColumn && attributeColumn <= bottomRight.column())
|
||||
|| (topLeft.column() <= specialisationColumn && specialisationColumn <= bottomRight.column()))
|
||||
{
|
||||
clearNpcStatsCache();
|
||||
|
||||
std::string empty;
|
||||
emit updateNpcAutocalc(0/*all*/, empty);
|
||||
}
|
||||
}
|
||||
|
||||
void CSMWorld::NpcAutoCalc::classDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight)
|
||||
{
|
||||
// update autocalculated attributes/skills of every NPC with matching class
|
||||
// - mData.mAttribute[2]
|
||||
// - mData.mSkills[5][2]
|
||||
// - mData.mSpecialization
|
||||
int attribute1Column = mClassModel->findColumnIndex(CSMWorld::Columns::ColumnId_Attribute1); // +1
|
||||
int majorSkill1Column = mClassModel->findColumnIndex(CSMWorld::Columns::ColumnId_MajorSkill1); // +4
|
||||
int minorSkill1Column = mClassModel->findColumnIndex(CSMWorld::Columns::ColumnId_MinorSkill1); // +4
|
||||
int specialisationColumn = mClassModel->findColumnIndex(CSMWorld::Columns::ColumnId_Specialisation);
|
||||
|
||||
if ((topLeft.column() > attribute1Column+1 || attribute1Column > bottomRight.column())
|
||||
&& (topLeft.column() > majorSkill1Column+4 || majorSkill1Column > bottomRight.column())
|
||||
&& (topLeft.column() > minorSkill1Column+4 || minorSkill1Column > bottomRight.column())
|
||||
&& (topLeft.column() > specialisationColumn || specialisationColumn > bottomRight.column()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// get the affected class
|
||||
int idColumn = mClassModel->findColumnIndex(CSMWorld::Columns::ColumnId_Id);
|
||||
for (int classRow = topLeft.row(); classRow <= bottomRight.row(); ++classRow)
|
||||
{
|
||||
clearNpcStatsCache();
|
||||
|
||||
std::string classId =
|
||||
mClassModel->data(mClassModel->index(classRow, idColumn)).toString().toUtf8().constData();
|
||||
emit updateNpcAutocalc(1/*class*/, classId);
|
||||
}
|
||||
}
|
||||
|
||||
void CSMWorld::NpcAutoCalc::raceDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight)
|
||||
{
|
||||
// affects racial bonus attributes & skills
|
||||
// - mData.mAttributeValues[]
|
||||
// - mData.mBonus[].mBonus
|
||||
// - mPowers.mList[]
|
||||
int attrColumn = mRaceModel->findColumnIndex(CSMWorld::Columns::ColumnId_RaceAttributes);
|
||||
int bonusColumn = mRaceModel->findColumnIndex(CSMWorld::Columns::ColumnId_RaceSkillBonus);
|
||||
int powersColumn = mRaceModel->findColumnIndex(CSMWorld::Columns::ColumnId_PowerList);
|
||||
|
||||
bool match = false;
|
||||
int raceRow = topLeft.row();
|
||||
int raceEnd = bottomRight.row();
|
||||
if (topLeft.parent().isValid() && bottomRight.parent().isValid())
|
||||
{
|
||||
if ((topLeft.parent().column() <= attrColumn && attrColumn <= bottomRight.parent().column())
|
||||
|| (topLeft.parent().column() <= bonusColumn && bonusColumn <= bottomRight.parent().column())
|
||||
|| (topLeft.parent().column() <= powersColumn && powersColumn <= bottomRight.parent().column()))
|
||||
{
|
||||
match = true; // TODO: check for specific nested column?
|
||||
raceRow = topLeft.parent().row();
|
||||
raceEnd = bottomRight.parent().row();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((topLeft.column() <= attrColumn && attrColumn <= bottomRight.column())
|
||||
|| (topLeft.column() <= bonusColumn && bonusColumn <= bottomRight.column())
|
||||
|| (topLeft.column() <= powersColumn && powersColumn <= bottomRight.column()))
|
||||
{
|
||||
match = true; // maybe the whole table changed
|
||||
}
|
||||
}
|
||||
|
||||
if (!match)
|
||||
return;
|
||||
|
||||
// update autocalculated attributes/skills of every NPC with matching race
|
||||
int idColumn = mRaceModel->findColumnIndex(CSMWorld::Columns::ColumnId_Id);
|
||||
for (; raceRow <= raceEnd; ++raceRow)
|
||||
{
|
||||
clearNpcStatsCache();
|
||||
|
||||
std::string raceId =
|
||||
mRaceModel->data(mRaceModel->index(raceRow, idColumn)).toString().toUtf8().constData();
|
||||
emit updateNpcAutocalc(2/*race*/, raceId);
|
||||
}
|
||||
}
|
||||
|
||||
void CSMWorld::NpcAutoCalc::npcDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight)
|
||||
{
|
||||
// TODO: for now always recalculate
|
||||
clearNpcStatsCache();
|
||||
|
||||
// TODO: check if below signal slows things down
|
||||
std::string empty;
|
||||
emit updateNpcAutocalc(0/*all*/, empty);
|
||||
}
|
||||
|
||||
void CSMWorld::NpcAutoCalc::gmstDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight)
|
||||
{
|
||||
static const QStringList gmsts(QStringList()<< "fNPCbaseMagickaMult" << "fAutoSpellChance"
|
||||
<< "fEffectCostMult" << "iAutoSpellAlterationMax" << "iAutoSpellConjurationMax"
|
||||
<< "iAutoSpellDestructionMax" << "iAutoSpellIllusionMax" << "iAutoSpellMysticismMax"
|
||||
<< "iAutoSpellRestorationMax" << "iAutoSpellTimesCanCast" << "iAutoSpellAttSkillMin");
|
||||
|
||||
bool match = false;
|
||||
for (int row = topLeft.row(); row <= bottomRight.row(); ++row)
|
||||
{
|
||||
if (gmsts.contains(mData.getGmsts().getRecord(row).get().mId.c_str()))
|
||||
{
|
||||
match = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!match)
|
||||
return;
|
||||
|
||||
clearNpcStatsCache();
|
||||
|
||||
std::string empty;
|
||||
emit updateNpcAutocalc(0/*all*/, empty);
|
||||
}
|
||||
|
||||
void CSMWorld::NpcAutoCalc::clearNpcStatsCache ()
|
||||
{
|
||||
for (std::map<const std::string, CSMWorld::NpcStats*>::iterator it (mNpcStatCache.begin());
|
||||
it != mNpcStatCache.end(); ++it)
|
||||
delete it->second;
|
||||
|
||||
mNpcStatCache.clear();
|
||||
}
|
||||
|
||||
CSMWorld::NpcStats* CSMWorld::NpcAutoCalc::npcAutoCalculate(const ESM::NPC& npc) const
|
||||
{
|
||||
CSMWorld::NpcStats *cachedStats = getCachedNpcData (npc.mId);
|
||||
if (cachedStats)
|
||||
return cachedStats;
|
||||
|
||||
int raceIndex = mData.getRaces().searchId(npc.mRace);
|
||||
int classIndex = mData.getClasses().searchId(npc.mClass);
|
||||
// this can happen when creating a new game from scratch
|
||||
if (raceIndex == -1 || classIndex == -1)
|
||||
return 0;
|
||||
|
||||
const ESM::Race *race = &mData.getRaces().getRecord(raceIndex).get();
|
||||
const ESM::Class *class_ = &mData.getClasses().getRecord(classIndex).get();
|
||||
|
||||
bool autoCalc = npc.mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS;
|
||||
short level = npc.mNpdt52.mLevel;
|
||||
if (autoCalc)
|
||||
level = npc.mNpdt12.mLevel;
|
||||
|
||||
std::auto_ptr<CSMWorld::NpcStats> stats (new CSMWorld::NpcStats());
|
||||
|
||||
CSStore store(mData.getGmsts(), mData.getSkills(), mData.getMagicEffects(), static_cast<const CSMWorld::NestedIdCollection<ESM::Spell>&>(mData.getSpells()));
|
||||
|
||||
if (autoCalc)
|
||||
{
|
||||
AutoCalc::autoCalcAttributesImpl (&npc, race, class_, level, *stats, &store);
|
||||
|
||||
stats->setHealth(autoCalculateHealth(level, class_, *stats));
|
||||
stats->setMana(autoCalculateMana(*stats));
|
||||
stats->setFatigue(autoCalculateFatigue(*stats));
|
||||
|
||||
AutoCalc::autoCalcSkillsImpl(&npc, race, class_, level, *stats, &store);
|
||||
|
||||
AutoCalc::autoCalculateSpells(race, *stats, &store);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (std::vector<std::string>::const_iterator it = npc.mSpells.mList.begin();
|
||||
it != npc.mSpells.mList.end(); ++it)
|
||||
{
|
||||
stats->addSpell(*it);
|
||||
}
|
||||
}
|
||||
|
||||
// update spell info
|
||||
const std::vector<std::string> &racePowers = race->mPowers.mList;
|
||||
for (unsigned int i = 0; i < racePowers.size(); ++i)
|
||||
{
|
||||
int type = -1;
|
||||
int spellIndex = mData.getSpells().searchId(racePowers[i]);
|
||||
if (spellIndex != -1)
|
||||
type = mData.getSpells().getRecord(spellIndex).get().mData.mType;
|
||||
stats->addPowers(racePowers[i], type);
|
||||
}
|
||||
// cost/chance
|
||||
int skills[ESM::Skill::Length];
|
||||
if (autoCalc)
|
||||
for (int i = 0; i< ESM::Skill::Length; ++i)
|
||||
skills[i] = stats->getBaseSkill(i);
|
||||
else
|
||||
for (int i = 0; i< ESM::Skill::Length; ++i)
|
||||
skills[i] = npc.mNpdt52.mSkills[i];
|
||||
|
||||
int attributes[ESM::Attribute::Length];
|
||||
if (autoCalc)
|
||||
for (int i = 0; i< ESM::Attribute::Length; ++i)
|
||||
attributes[i] = stats->getBaseAttribute(i);
|
||||
else
|
||||
{
|
||||
attributes[ESM::Attribute::Strength] = npc.mNpdt52.mStrength;
|
||||
attributes[ESM::Attribute::Willpower] = npc.mNpdt52.mWillpower;
|
||||
attributes[ESM::Attribute::Agility] = npc.mNpdt52.mAgility;
|
||||
attributes[ESM::Attribute::Speed] = npc.mNpdt52.mSpeed;
|
||||
attributes[ESM::Attribute::Endurance] = npc.mNpdt52.mEndurance;
|
||||
attributes[ESM::Attribute::Personality] = npc.mNpdt52.mPersonality;
|
||||
attributes[ESM::Attribute::Luck] = npc.mNpdt52.mLuck;
|
||||
}
|
||||
|
||||
const std::vector<CSMWorld::SpellInfo>& spells = stats->spells();
|
||||
for (std::vector<SpellInfo>::const_iterator it = spells.begin(); it != spells.end(); ++it)
|
||||
{
|
||||
int cost = -1;
|
||||
int spellIndex = mData.getSpells().searchId((*it).mName);
|
||||
const ESM::Spell* spell = 0;
|
||||
if (spellIndex != -1)
|
||||
{
|
||||
spell = &mData.getSpells().getRecord(spellIndex).get();
|
||||
cost = spell->mData.mCost;
|
||||
|
||||
int school;
|
||||
float skillTerm;
|
||||
AutoCalc::calcWeakestSchool(spell, skills, school, skillTerm, &store);
|
||||
float chance = calcAutoCastChance(spell, skills, attributes, school, &store);
|
||||
|
||||
stats->addCostAndChance((*it).mName, cost, (int)ceil(chance)); // percent
|
||||
}
|
||||
}
|
||||
|
||||
if (stats.get() == 0)
|
||||
return 0;
|
||||
|
||||
CSMWorld::NpcStats *result = stats.release();
|
||||
//emit cacheNpcStats (npc.mId, result);
|
||||
mNpcStatCache[npc.mId] = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
//void CSMWorld::NpcAutoCalc::cacheNpcStatsEvent (const std::string& id, CSMWorld::NpcStats *stats)
|
||||
//{
|
||||
//mNpcStatCache[id] = stats;
|
||||
//}
|
||||
|
||||
CSMWorld::NpcStats* CSMWorld::NpcAutoCalc::getCachedNpcData (const std::string& id) const
|
||||
{
|
||||
std::map<const std::string, CSMWorld::NpcStats*>::const_iterator it = mNpcStatCache.find(id);
|
||||
if (it != mNpcStatCache.end())
|
||||
return it->second;
|
||||
else
|
||||
return 0;
|
||||
}
|
75
apps/opencs/model/world/npcautocalc.hpp
Normal file
75
apps/opencs/model/world/npcautocalc.hpp
Normal file
|
@ -0,0 +1,75 @@
|
|||
#ifndef CSM_WORLD_NPCAUTOCALC_H
|
||||
#define CSM_WORLD_NPCAUTOCALC_H
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
#include <QObject>
|
||||
#include <QModelIndex>
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
struct NPC;
|
||||
}
|
||||
|
||||
namespace CSMWorld
|
||||
{
|
||||
class Data;
|
||||
class NpcStats;
|
||||
class IdTable;
|
||||
class IdTree;
|
||||
|
||||
class NpcAutoCalc : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
const Data& mData;
|
||||
const IdTable *mSkillModel;
|
||||
const IdTable *mClassModel;
|
||||
const IdTree *mRaceModel;
|
||||
mutable std::map<const std::string, NpcStats*> mNpcStatCache;
|
||||
|
||||
public:
|
||||
|
||||
NpcAutoCalc (const Data& data, const IdTable *gmsts, const IdTable *skills, const IdTable *classes,
|
||||
const IdTree *races, const IdTree *objects);
|
||||
|
||||
~NpcAutoCalc ();
|
||||
|
||||
NpcStats* npcAutoCalculate (const ESM::NPC& npc) const;
|
||||
|
||||
private:
|
||||
|
||||
// not implemented
|
||||
NpcAutoCalc (const NpcAutoCalc&);
|
||||
NpcAutoCalc& operator= (const NpcAutoCalc&);
|
||||
|
||||
NpcStats* getCachedNpcData (const std::string& id) const;
|
||||
|
||||
void clearNpcStatsCache ();
|
||||
|
||||
signals:
|
||||
|
||||
// refresh NPC dialogue subviews via object table model
|
||||
void updateNpcAutocalc (int type, const std::string& id);
|
||||
|
||||
//void cacheNpcStats (const std::string& id, NpcStats *stats) const;
|
||||
|
||||
private slots:
|
||||
|
||||
// for autocalc updates when gmst/race/class/skils tables change
|
||||
void gmstDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);
|
||||
|
||||
void raceDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);
|
||||
|
||||
void classDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);
|
||||
|
||||
void skillDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);
|
||||
|
||||
void npcDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);
|
||||
|
||||
//void cacheNpcStatsEvent (const std::string& id, NpcStats *stats);
|
||||
};
|
||||
}
|
||||
|
||||
#endif // CSM_WORLD_NPCAUTOCALC_H
|
|
@ -12,6 +12,7 @@
|
|||
#include "usertype.hpp"
|
||||
#include "idtree.hpp"
|
||||
#include "npcstats.hpp"
|
||||
#include "npcautocalc.hpp"
|
||||
|
||||
CSMWorld::PotionColumns::PotionColumns (const InventoryColumns& columns)
|
||||
: InventoryColumns (columns) {}
|
||||
|
@ -777,7 +778,7 @@ void CSMWorld::NpcRefIdAdapter::setData (const RefIdColumn *column, RefIdData& d
|
|||
{
|
||||
if (npc.mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS)
|
||||
{
|
||||
CSMWorld::NpcStats *stats = mData.npcAutoCalculate(npc);
|
||||
CSMWorld::NpcStats *stats = mData.getNpcAutoCalc().npcAutoCalculate(npc);
|
||||
if (!stats)
|
||||
{
|
||||
record.setModified (npc);
|
||||
|
@ -817,7 +818,7 @@ void CSMWorld::NpcRefIdAdapter::setData (const RefIdColumn *column, RefIdData& d
|
|||
{
|
||||
npc.mNpdtType = ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS;
|
||||
npc.mNpdt12.mLevel = npc.mNpdt52.mLevel; // for NPC's loaded as non-autocalc
|
||||
mData.npcAutoCalculate(npc);
|
||||
mData.getNpcAutoCalc().npcAutoCalculate(npc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -888,7 +889,7 @@ QVariant CSMWorld::NpcAttributesRefIdAdapter::getNestedData (const RefIdColumn *
|
|||
else if (subColIndex == 1)
|
||||
if (npc.mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS)
|
||||
{
|
||||
CSMWorld::NpcStats *stats = mData.npcAutoCalculate(npc);
|
||||
CSMWorld::NpcStats *stats = mData.getNpcAutoCalc().npcAutoCalculate(npc);
|
||||
if (!stats)
|
||||
return QVariant();
|
||||
|
||||
|
@ -1020,7 +1021,7 @@ QVariant CSMWorld::NpcSkillsRefIdAdapter::getNestedData (const RefIdColumn *colu
|
|||
{
|
||||
if (npc.mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS)
|
||||
{
|
||||
CSMWorld::NpcStats *stats = mData.npcAutoCalculate(npc);
|
||||
CSMWorld::NpcStats *stats = mData.getNpcAutoCalc().npcAutoCalculate(npc);
|
||||
if (!stats)
|
||||
return QVariant();
|
||||
|
||||
|
@ -1108,7 +1109,7 @@ QVariant CSMWorld::NpcMiscRefIdAdapter::getNestedData (const RefIdColumn *column
|
|||
|
||||
if (autoCalc)
|
||||
{
|
||||
CSMWorld::NpcStats *stats = mData.npcAutoCalculate(npc);
|
||||
CSMWorld::NpcStats *stats = mData.getNpcAutoCalc().npcAutoCalculate(npc);
|
||||
|
||||
switch (subColIndex)
|
||||
{
|
||||
|
@ -1732,7 +1733,7 @@ QVariant NestedSpellRefIdAdapter<ESM::NPC>::getNestedData (const RefIdColumn *co
|
|||
const Record<ESM::NPC>& record =
|
||||
static_cast<const Record<ESM::NPC>&> (data.getRecord (RefIdData::LocalIndex (index, mType)));
|
||||
|
||||
CSMWorld::NpcStats *stats = mData.npcAutoCalculate(record.get());
|
||||
CSMWorld::NpcStats *stats = mData.getNpcAutoCalc().npcAutoCalculate(record.get());
|
||||
if (!stats)
|
||||
return QVariant();
|
||||
|
||||
|
@ -1766,7 +1767,7 @@ int NestedSpellRefIdAdapter<ESM::NPC>::getNestedRowsCount(const RefIdColumn *col
|
|||
const Record<ESM::NPC>& record =
|
||||
static_cast<const Record<ESM::NPC>&> (data.getRecord (RefIdData::LocalIndex (index, mType)));
|
||||
|
||||
CSMWorld::NpcStats *stats = mData.npcAutoCalculate(record.get());
|
||||
CSMWorld::NpcStats *stats = mData.getNpcAutoCalc().npcAutoCalculate(record.get());
|
||||
if (!stats)
|
||||
return 0;
|
||||
|
||||
|
|
Loading…
Reference in a new issue