mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-16 16:49:55 +00:00
Fix for issue #6 (https://github.com/cc9cii/openmw/issues/6) where dialogue subview for editing an NPC fails with an "invalid ID" exception.
* NPC autocalc code was looking for non-existent values of race and class, this is now validated first. * Also took the opportunity to grey out the spells table when auto-calculated. The template specialisation is a bit ugly, though.
This commit is contained in:
parent
5bef43ac14
commit
870bb491af
4 changed files with 145 additions and 69 deletions
|
@ -552,7 +552,7 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc
|
|||
mMetaData.addColumn (new FormatColumn<MetaData>);
|
||||
mMetaData.addColumn (new AuthorColumn<MetaData>);
|
||||
mMetaData.addColumn (new FileDescriptionColumn<MetaData>);
|
||||
|
||||
|
||||
addModel (new IdTable (&mGlobals), UniversalId::Type_Global);
|
||||
addModel (new IdTable (&mGmsts), UniversalId::Type_Gmst);
|
||||
addModel (new IdTable (&mSkills), UniversalId::Type_Skill);
|
||||
|
@ -968,7 +968,7 @@ int CSMWorld::Data::startLoading (const boost::filesystem::path& path, bool base
|
|||
|
||||
mMetaData.setRecord (0, Record<MetaData> (RecordBase::State_ModifiedOnly, 0, &metaData));
|
||||
}
|
||||
|
||||
|
||||
return mReader->getRecordCount();
|
||||
}
|
||||
|
||||
|
@ -1419,8 +1419,14 @@ CSMWorld::NpcStats* CSMWorld::Data::npcAutoCalculate(const ESM::NPC& npc) const
|
|||
if (cachedStats)
|
||||
return cachedStats;
|
||||
|
||||
const ESM::Race *race = &mRaces.getRecord(npc.mRace).get();
|
||||
const ESM::Class *class_ = &mClasses.getRecord(npc.mClass).get();
|
||||
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;
|
||||
|
|
|
@ -638,6 +638,11 @@ void CSMWorld::NpcRefIdAdapter::setData (const RefIdColumn *column, RefIdData& d
|
|||
if (npc.mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS)
|
||||
{
|
||||
CSMWorld::NpcStats *stats = mData.npcAutoCalculate(npc);
|
||||
if (!stats)
|
||||
{
|
||||
record.setModified (npc);
|
||||
return;
|
||||
}
|
||||
|
||||
// update npc
|
||||
npc.mNpdtType = ESM::NPC::NPC_DEFAULT;
|
||||
|
@ -755,6 +760,8 @@ QVariant CSMWorld::NpcAttributesRefIdAdapter::getNestedData (const RefIdColumn *
|
|||
if (npc.mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS)
|
||||
{
|
||||
CSMWorld::NpcStats *stats = mData.npcAutoCalculate(npc);
|
||||
if (!stats)
|
||||
return QVariant();
|
||||
|
||||
switch (subRowIndex)
|
||||
{
|
||||
|
@ -885,6 +892,9 @@ QVariant CSMWorld::NpcSkillsRefIdAdapter::getNestedData (const RefIdColumn *colu
|
|||
if (npc.mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS)
|
||||
{
|
||||
CSMWorld::NpcStats *stats = mData.npcAutoCalculate(npc);
|
||||
if (!stats)
|
||||
return QVariant();
|
||||
|
||||
return static_cast<int>(stats->getBaseSkill(subRowIndex));
|
||||
}
|
||||
else
|
||||
|
@ -981,17 +991,23 @@ QVariant CSMWorld::NpcMiscRefIdAdapter::getNestedData (const RefIdColumn *column
|
|||
}
|
||||
case 2:
|
||||
{
|
||||
UserInt i(stats->getHealth());
|
||||
UserInt i(0);
|
||||
if (stats)
|
||||
i = UserInt(stats->getHealth());
|
||||
return QVariant(QVariant::fromValue(i));
|
||||
}
|
||||
case 3:
|
||||
{
|
||||
UserInt i(stats->getMana());
|
||||
UserInt i(0);
|
||||
if (stats)
|
||||
i = UserInt(stats->getMana());
|
||||
return QVariant(QVariant::fromValue(i));
|
||||
}
|
||||
case 4:
|
||||
{
|
||||
UserInt i(stats->getFatigue());
|
||||
UserInt i(0);
|
||||
if (stats)
|
||||
i = UserInt(stats->getFatigue());
|
||||
return QVariant(QVariant::fromValue(i));
|
||||
}
|
||||
case 5: return static_cast<int>(record.get().mNpdt12.mDisposition);
|
||||
|
@ -1164,6 +1180,54 @@ void CSMWorld::WeaponRefIdAdapter::setData (const RefIdColumn *column, RefIdData
|
|||
namespace CSMWorld
|
||||
{
|
||||
|
||||
template<>
|
||||
QVariant ActorRefIdAdapter<ESM::NPC>::getData (const RefIdColumn *column, const RefIdData& data,
|
||||
int index) const
|
||||
{
|
||||
const Record<ESM::NPC>& record = static_cast<const Record<ESM::NPC>&> (
|
||||
data.getRecord (RefIdData::LocalIndex (index, BaseRefIdAdapter<ESM::NPC>::getType())));
|
||||
|
||||
if (column==mActors.mHasAi)
|
||||
return record.get().mHasAI!=0;
|
||||
|
||||
if (column==mActors.mHello)
|
||||
return record.get().mAiData.mHello;
|
||||
|
||||
if (column==mActors.mFlee)
|
||||
return record.get().mAiData.mFlee;
|
||||
|
||||
if (column==mActors.mFight)
|
||||
return record.get().mAiData.mFight;
|
||||
|
||||
if (column==mActors.mAlarm)
|
||||
return record.get().mAiData.mAlarm;
|
||||
|
||||
if (column==mActors.mInventory)
|
||||
return true; // to show nested tables in dialogue subview, see IdTree::hasChildren()
|
||||
|
||||
if (column==mActors.mSpells)
|
||||
{
|
||||
if ((record.get().mFlags & ESM::NPC::Autocalc) != 0)
|
||||
return QVariant(QVariant::UserType);
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
if (column==mActors.mDestinations)
|
||||
return true; // to show nested tables in dialogue subview, see IdTree::hasChildren()
|
||||
|
||||
if (column==mActors.mAiPackages)
|
||||
return true; // to show nested tables in dialogue subview, see IdTree::hasChildren()
|
||||
|
||||
std::map<const RefIdColumn *, unsigned int>::const_iterator iter =
|
||||
mActors.mServices.find (column);
|
||||
|
||||
if (iter!=mActors.mServices.end())
|
||||
return (record.get().mAiData.mServices & iter->second)!=0;
|
||||
|
||||
return NameRefIdAdapter<ESM::NPC>::getData (column, data, index);
|
||||
}
|
||||
|
||||
template <>
|
||||
void NestedSpellRefIdAdapter<ESM::NPC>::addNestedRow (const RefIdColumn *column,
|
||||
RefIdData& data, int index, int position) const
|
||||
|
@ -1259,7 +1323,11 @@ 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)));
|
||||
|
||||
const std::vector<SpellInfo>& spells = mData.npcAutoCalculate(record.get())->spells();
|
||||
CSMWorld::NpcStats *stats = mData.npcAutoCalculate(record.get());
|
||||
if (!stats)
|
||||
return QVariant();
|
||||
|
||||
const std::vector<SpellInfo>& spells = stats->spells();
|
||||
|
||||
if (subRowIndex < 0 || subRowIndex >= static_cast<int> (spells.size()))
|
||||
throw std::runtime_error ("index out of range");
|
||||
|
@ -1289,8 +1357,54 @@ 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)));
|
||||
|
||||
const std::vector<SpellInfo> spells = mData.npcAutoCalculate(record.get())->spells();
|
||||
return static_cast<int>(spells.size());
|
||||
CSMWorld::NpcStats *stats = mData.npcAutoCalculate(record.get());
|
||||
if (!stats)
|
||||
return 0;
|
||||
|
||||
return static_cast<int>(stats->spells().size());
|
||||
}
|
||||
|
||||
template<>
|
||||
QVariant ActorRefIdAdapter<ESM::Creature>::getData (const RefIdColumn *column, const RefIdData& data,
|
||||
int index) const
|
||||
{
|
||||
const Record<ESM::Creature>& record = static_cast<const Record<ESM::Creature>&> (
|
||||
data.getRecord (RefIdData::LocalIndex (index, BaseRefIdAdapter<ESM::Creature>::getType())));
|
||||
|
||||
if (column==mActors.mHasAi)
|
||||
return record.get().mHasAI!=0;
|
||||
|
||||
if (column==mActors.mHello)
|
||||
return record.get().mAiData.mHello;
|
||||
|
||||
if (column==mActors.mFlee)
|
||||
return record.get().mAiData.mFlee;
|
||||
|
||||
if (column==mActors.mFight)
|
||||
return record.get().mAiData.mFight;
|
||||
|
||||
if (column==mActors.mAlarm)
|
||||
return record.get().mAiData.mAlarm;
|
||||
|
||||
if (column==mActors.mInventory)
|
||||
return true; // to show nested tables in dialogue subview, see IdTree::hasChildren()
|
||||
|
||||
if (column==mActors.mSpells)
|
||||
return true; // to show nested tables in dialogue subview, see IdTree::hasChildren()
|
||||
|
||||
if (column==mActors.mDestinations)
|
||||
return true; // to show nested tables in dialogue subview, see IdTree::hasChildren()
|
||||
|
||||
if (column==mActors.mAiPackages)
|
||||
return true; // to show nested tables in dialogue subview, see IdTree::hasChildren()
|
||||
|
||||
std::map<const RefIdColumn *, unsigned int>::const_iterator iter =
|
||||
mActors.mServices.find (column);
|
||||
|
||||
if (iter!=mActors.mServices.end())
|
||||
return (record.get().mAiData.mServices & iter->second)!=0;
|
||||
|
||||
return NameRefIdAdapter<ESM::Creature>::getData (column, data, index);
|
||||
}
|
||||
|
||||
template <>
|
||||
|
|
|
@ -523,49 +523,6 @@ namespace CSMWorld
|
|||
: NameRefIdAdapter<RecordT> (type, columns), mActors (columns)
|
||||
{}
|
||||
|
||||
template<typename RecordT>
|
||||
QVariant ActorRefIdAdapter<RecordT>::getData (const RefIdColumn *column, const RefIdData& data,
|
||||
int index) const
|
||||
{
|
||||
const Record<RecordT>& record = static_cast<const Record<RecordT>&> (
|
||||
data.getRecord (RefIdData::LocalIndex (index, BaseRefIdAdapter<RecordT>::getType())));
|
||||
|
||||
if (column==mActors.mHasAi)
|
||||
return record.get().mHasAI!=0;
|
||||
|
||||
if (column==mActors.mHello)
|
||||
return record.get().mAiData.mHello;
|
||||
|
||||
if (column==mActors.mFlee)
|
||||
return record.get().mAiData.mFlee;
|
||||
|
||||
if (column==mActors.mFight)
|
||||
return record.get().mAiData.mFight;
|
||||
|
||||
if (column==mActors.mAlarm)
|
||||
return record.get().mAiData.mAlarm;
|
||||
|
||||
if (column==mActors.mInventory)
|
||||
return true; // to show nested tables in dialogue subview, see IdTree::hasChildren()
|
||||
|
||||
if (column==mActors.mSpells)
|
||||
return true; // to show nested tables in dialogue subview, see IdTree::hasChildren()
|
||||
|
||||
if (column==mActors.mDestinations)
|
||||
return true; // to show nested tables in dialogue subview, see IdTree::hasChildren()
|
||||
|
||||
if (column==mActors.mAiPackages)
|
||||
return true; // to show nested tables in dialogue subview, see IdTree::hasChildren()
|
||||
|
||||
std::map<const RefIdColumn *, unsigned int>::const_iterator iter =
|
||||
mActors.mServices.find (column);
|
||||
|
||||
if (iter!=mActors.mServices.end())
|
||||
return (record.get().mAiData.mServices & iter->second)!=0;
|
||||
|
||||
return NameRefIdAdapter<RecordT>::getData (column, data, index);
|
||||
}
|
||||
|
||||
template<typename RecordT>
|
||||
void ActorRefIdAdapter<RecordT>::setData (const RefIdColumn *column, RefIdData& data, int index,
|
||||
const QVariant& value) const
|
||||
|
|
|
@ -22,6 +22,8 @@ CSVWorld::NestedTable::NestedTable(CSMDoc::Document& document,
|
|||
mEditIdAction(0),
|
||||
mModel(model)
|
||||
{
|
||||
mDispatcher = new CSMWorld::CommandDispatcher (document, id, this);
|
||||
|
||||
setSelectionBehavior (QAbstractItemView::SelectRows);
|
||||
setSelectionMode (QAbstractItemView::ExtendedSelection);
|
||||
|
||||
|
@ -34,26 +36,23 @@ CSVWorld::NestedTable::NestedTable(CSMDoc::Document& document,
|
|||
|
||||
int columns = model->columnCount(QModelIndex());
|
||||
|
||||
setModel(model);
|
||||
for(int i = 0 ; i < columns; ++i)
|
||||
{
|
||||
CSMWorld::ColumnBase::Display display = static_cast<CSMWorld::ColumnBase::Display> (
|
||||
model->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt());
|
||||
|
||||
setAcceptDrops(true);
|
||||
CommandDelegate *delegate = CommandDelegateFactoryCollection::get().makeDelegate(display,
|
||||
mDispatcher,
|
||||
document,
|
||||
this);
|
||||
|
||||
setItemDelegateForColumn(i, delegate);
|
||||
}
|
||||
|
||||
setModel(model);
|
||||
|
||||
if (editable)
|
||||
{
|
||||
mDispatcher = new CSMWorld::CommandDispatcher (document, id, this);
|
||||
for(int i = 0 ; i < columns; ++i)
|
||||
{
|
||||
CSMWorld::ColumnBase::Display display = static_cast<CSMWorld::ColumnBase::Display> (
|
||||
model->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt());
|
||||
|
||||
CommandDelegate *delegate = CommandDelegateFactoryCollection::get().makeDelegate(display,
|
||||
mDispatcher,
|
||||
document,
|
||||
this);
|
||||
|
||||
setItemDelegateForColumn(i, delegate);
|
||||
}
|
||||
|
||||
mAddNewRowAction = new QAction (tr ("Add new row"), this);
|
||||
|
||||
connect(mAddNewRowAction, SIGNAL(triggered()),
|
||||
|
|
Loading…
Reference in a new issue