#ifndef CSM_WOLRD_REFIDADAPTERIMP_H #define CSM_WOLRD_REFIDADAPTERIMP_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "columnbase.hpp" #include "nestedtablewrapper.hpp" #include "record.hpp" #include "refidadapter.hpp" #include "refiddata.hpp" #include "universalid.hpp" namespace CSMWorld { class RefIdColumn; struct BaseColumns { const RefIdColumn* mId; const RefIdColumn* mModified; const RefIdColumn* mType; const RefIdColumn* mBlocked; BaseColumns() : mId(nullptr) , mModified(nullptr) , mType(nullptr) , mBlocked(nullptr) { } }; /// \brief Base adapter for all refereceable record types /// Adapters that can handle nested tables, needs to return valid qvariant for parent columns template class BaseRefIdAdapter : public RefIdAdapter { UniversalId::Type mType; BaseColumns mBase; public: BaseRefIdAdapter(UniversalId::Type type, const BaseColumns& base); ESM::RefId getId(const RecordBase& record) const override; void setId(RecordBase& record, const std::string& id) override; QVariant getData(const RefIdColumn* column, const RefIdData& data, int index) const override; void setData(const RefIdColumn* column, RefIdData& data, int index, const QVariant& value) const override; ///< If the data type does not match an exception is thrown. UniversalId::Type getType() const; }; template BaseRefIdAdapter::BaseRefIdAdapter(UniversalId::Type type, const BaseColumns& base) : mType(type) , mBase(base) { } template void BaseRefIdAdapter::setId(RecordBase& record, const std::string& id) { (dynamic_cast&>(record).get().mId) = ESM::RefId::stringRefId(id); } template ESM::RefId BaseRefIdAdapter::getId(const RecordBase& record) const { return dynamic_cast&>(record).get().mId; } template QVariant BaseRefIdAdapter::getData(const RefIdColumn* column, const RefIdData& data, int index) const { const Record& record = static_cast&>(data.getRecord(RefIdData::LocalIndex(index, mType))); if (column == mBase.mId) return QString::fromStdString(record.get().mId.toString()); if (column == mBase.mModified) { if (record.mState == Record::State_Erased) return static_cast(Record::State_Deleted); return static_cast(record.mState); } if (column == mBase.mType) return static_cast(mType); if (column == mBase.mBlocked) return (record.get().mRecordFlags & ESM::FLAG_Blocked) != 0; return QVariant(); } template void BaseRefIdAdapter::setData( const RefIdColumn* column, RefIdData& data, int index, const QVariant& value) const { Record& record = static_cast&>(data.getRecord(RefIdData::LocalIndex(index, mType))); if (column == mBase.mModified) record.mState = static_cast(value.toInt()); else if (column == mBase.mBlocked) { RecordT record2 = record.get(); if (value.toInt() != 0) record2.mRecordFlags |= ESM::FLAG_Blocked; else record2.mRecordFlags &= ~ESM::FLAG_Blocked; record.setModified(record2); } } template UniversalId::Type BaseRefIdAdapter::getType() const { return mType; } // NOTE: Body Part should not have persistence (but BodyPart is not listed in the Objects // table at the moment). // // Spellmaking - not persistent - currently not part of objects table // Enchanting - not persistent - currently not part of objects table // // Leveled Creature - no model, so not persistent // Leveled Item - no model, so not persistent struct ModelColumns : public BaseColumns { const RefIdColumn* mModel; const RefIdColumn* mPersistence; ModelColumns(const BaseColumns& base) : BaseColumns(base) , mModel(nullptr) , mPersistence(nullptr) { } }; /// \brief Adapter for IDs with models (all but levelled lists) template class ModelRefIdAdapter : public BaseRefIdAdapter { ModelColumns mModel; public: ModelRefIdAdapter(UniversalId::Type type, const ModelColumns& columns); QVariant getData(const RefIdColumn* column, const RefIdData& data, int index) const override; void setData(const RefIdColumn* column, RefIdData& data, int index, const QVariant& value) const override; ///< If the data type does not match an exception is thrown. }; template ModelRefIdAdapter::ModelRefIdAdapter(UniversalId::Type type, const ModelColumns& columns) : BaseRefIdAdapter(type, columns) , mModel(columns) { } template QVariant ModelRefIdAdapter::getData(const RefIdColumn* column, const RefIdData& data, int index) const { const Record& record = static_cast&>( data.getRecord(RefIdData::LocalIndex(index, BaseRefIdAdapter::getType()))); if (column == mModel.mModel) return QString::fromUtf8(record.get().mModel.c_str()); if (column == mModel.mPersistence) return (record.get().mRecordFlags & ESM::FLAG_Persistent) != 0; return BaseRefIdAdapter::getData(column, data, index); } template void ModelRefIdAdapter::setData( const RefIdColumn* column, RefIdData& data, int index, const QVariant& value) const { Record& record = static_cast&>( data.getRecord(RefIdData::LocalIndex(index, BaseRefIdAdapter::getType()))); RecordT record2 = record.get(); if (column == mModel.mModel) record2.mModel = value.toString().toUtf8().constData(); else if (column == mModel.mPersistence) { if (value.toInt() != 0) record2.mRecordFlags |= ESM::FLAG_Persistent; else record2.mRecordFlags &= ~ESM::FLAG_Persistent; } else { BaseRefIdAdapter::setData(column, data, index, value); return; } record.setModified(record2); } struct NameColumns : public ModelColumns { const RefIdColumn* mName; const RefIdColumn* mScript; NameColumns(const ModelColumns& base) : ModelColumns(base) , mName(nullptr) , mScript(nullptr) { } }; /// \brief Adapter for IDs with names (all but levelled lists and statics) template class NameRefIdAdapter : public ModelRefIdAdapter { NameColumns mName; public: NameRefIdAdapter(UniversalId::Type type, const NameColumns& columns); QVariant getData(const RefIdColumn* column, const RefIdData& data, int index) const override; void setData(const RefIdColumn* column, RefIdData& data, int index, const QVariant& value) const override; ///< If the data type does not match an exception is thrown. }; template NameRefIdAdapter::NameRefIdAdapter(UniversalId::Type type, const NameColumns& columns) : ModelRefIdAdapter(type, columns) , mName(columns) { } template QVariant NameRefIdAdapter::getData(const RefIdColumn* column, const RefIdData& data, int index) const { const Record& record = static_cast&>( data.getRecord(RefIdData::LocalIndex(index, BaseRefIdAdapter::getType()))); if (column == mName.mName) return QString::fromUtf8(record.get().mName.c_str()); if (column == mName.mScript) return QString::fromUtf8(record.get().mScript.getRefIdString().c_str()); return ModelRefIdAdapter::getData(column, data, index); } template void NameRefIdAdapter::setData( const RefIdColumn* column, RefIdData& data, int index, const QVariant& value) const { Record& record = static_cast&>( data.getRecord(RefIdData::LocalIndex(index, BaseRefIdAdapter::getType()))); RecordT record2 = record.get(); if (column == mName.mName) record2.mName = value.toString().toUtf8().constData(); else if (column == mName.mScript) record2.mScript = ESM::RefId::stringRefId(value.toString().toUtf8().constData()); else { ModelRefIdAdapter::setData(column, data, index, value); return; } record.setModified(record2); } struct InventoryColumns : public NameColumns { const RefIdColumn* mIcon; const RefIdColumn* mWeight; const RefIdColumn* mValue; InventoryColumns(const NameColumns& base) : NameColumns(base) , mIcon(nullptr) , mWeight(nullptr) , mValue(nullptr) { } }; /// \brief Adapter for IDs that can go into an inventory template class InventoryRefIdAdapter : public NameRefIdAdapter { InventoryColumns mInventory; public: InventoryRefIdAdapter(UniversalId::Type type, const InventoryColumns& columns); QVariant getData(const RefIdColumn* column, const RefIdData& data, int index) const override; void setData(const RefIdColumn* column, RefIdData& data, int index, const QVariant& value) const override; ///< If the data type does not match an exception is thrown. }; template InventoryRefIdAdapter::InventoryRefIdAdapter(UniversalId::Type type, const InventoryColumns& columns) : NameRefIdAdapter(type, columns) , mInventory(columns) { } template QVariant InventoryRefIdAdapter::getData(const RefIdColumn* column, const RefIdData& data, int index) const { const Record& record = static_cast&>( data.getRecord(RefIdData::LocalIndex(index, BaseRefIdAdapter::getType()))); if (column == mInventory.mIcon) return QString::fromUtf8(record.get().mIcon.c_str()); if (column == mInventory.mWeight) return record.get().mData.mWeight; if (column == mInventory.mValue) return record.get().mData.mValue; return NameRefIdAdapter::getData(column, data, index); } template void InventoryRefIdAdapter::setData( const RefIdColumn* column, RefIdData& data, int index, const QVariant& value) const { Record& record = static_cast&>( data.getRecord(RefIdData::LocalIndex(index, BaseRefIdAdapter::getType()))); RecordT record2 = record.get(); if (column == mInventory.mIcon) record2.mIcon = value.toString().toUtf8().constData(); else if (column == mInventory.mWeight) record2.mData.mWeight = value.toFloat(); else if (column == mInventory.mValue) record2.mData.mValue = value.toInt(); else { NameRefIdAdapter::setData(column, data, index, value); return; } record.setModified(record2); } struct PotionColumns : public InventoryColumns { const RefIdColumn* mEffects; PotionColumns(const InventoryColumns& columns); }; class PotionRefIdAdapter : public InventoryRefIdAdapter { PotionColumns mColumns; const RefIdColumn* mAutoCalc; public: PotionRefIdAdapter(const PotionColumns& columns, const RefIdColumn* autoCalc); QVariant getData(const RefIdColumn* column, const RefIdData& data, int index) const override; void setData(const RefIdColumn* column, RefIdData& data, int index, const QVariant& value) const override; ///< If the data type does not match an exception is thrown. }; struct IngredientColumns : public InventoryColumns { const RefIdColumn* mEffects; IngredientColumns(const InventoryColumns& columns); }; class IngredientRefIdAdapter : public InventoryRefIdAdapter { IngredientColumns mColumns; public: IngredientRefIdAdapter(const IngredientColumns& columns); QVariant getData(const RefIdColumn* column, const RefIdData& data, int index) const override; void setData(const RefIdColumn* column, RefIdData& data, int index, const QVariant& value) const override; ///< If the data type does not match an exception is thrown. }; class IngredEffectRefIdAdapter : public NestedRefIdAdapterBase { UniversalId::Type mType; public: IngredEffectRefIdAdapter(); IngredEffectRefIdAdapter(const IngredEffectRefIdAdapter&) = delete; IngredEffectRefIdAdapter& operator=(const IngredEffectRefIdAdapter&) = delete; ~IngredEffectRefIdAdapter() override = default; void addNestedRow(const RefIdColumn* column, RefIdData& data, int index, int position) const override; void removeNestedRow(const RefIdColumn* column, RefIdData& data, int index, int rowToRemove) const override; void setNestedTable(const RefIdColumn* column, RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const override; NestedTableWrapperBase* nestedTable(const RefIdColumn* column, const RefIdData& data, int index) const override; QVariant getNestedData(const RefIdColumn* column, const RefIdData& data, int index, int subRowIndex, int subColIndex) const override; void setNestedData(const RefIdColumn* column, RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const override; int getNestedColumnsCount(const RefIdColumn* column, const RefIdData& data) const override; int getNestedRowsCount(const RefIdColumn* column, const RefIdData& data, int index) const override; }; struct EnchantableColumns : public InventoryColumns { const RefIdColumn* mEnchantment; const RefIdColumn* mEnchantmentPoints; EnchantableColumns(const InventoryColumns& base) : InventoryColumns(base) , mEnchantment(nullptr) , mEnchantmentPoints(nullptr) { } }; /// \brief Adapter for enchantable IDs template class EnchantableRefIdAdapter : public InventoryRefIdAdapter { EnchantableColumns mEnchantable; public: EnchantableRefIdAdapter(UniversalId::Type type, const EnchantableColumns& columns); QVariant getData(const RefIdColumn* column, const RefIdData& data, int index) const override; void setData(const RefIdColumn* column, RefIdData& data, int index, const QVariant& value) const override; ///< If the data type does not match an exception is thrown. }; template EnchantableRefIdAdapter::EnchantableRefIdAdapter(UniversalId::Type type, const EnchantableColumns& columns) : InventoryRefIdAdapter(type, columns) , mEnchantable(columns) { } template QVariant EnchantableRefIdAdapter::getData( const RefIdColumn* column, const RefIdData& data, int index) const { const Record& record = static_cast&>( data.getRecord(RefIdData::LocalIndex(index, BaseRefIdAdapter::getType()))); if (column == mEnchantable.mEnchantment) return QString::fromUtf8(record.get().mEnchant.getRefIdString().c_str()); if (column == mEnchantable.mEnchantmentPoints) return static_cast(record.get().mData.mEnchant); return InventoryRefIdAdapter::getData(column, data, index); } template void EnchantableRefIdAdapter::setData( const RefIdColumn* column, RefIdData& data, int index, const QVariant& value) const { Record& record = static_cast&>( data.getRecord(RefIdData::LocalIndex(index, BaseRefIdAdapter::getType()))); RecordT record2 = record.get(); if (column == mEnchantable.mEnchantment) record2.mEnchant = ESM::RefId::stringRefId(value.toString().toUtf8().constData()); else if (column == mEnchantable.mEnchantmentPoints) record2.mData.mEnchant = value.toInt(); else { InventoryRefIdAdapter::setData(column, data, index, value); return; } record.setModified(record2); } struct ToolColumns : public InventoryColumns { const RefIdColumn* mQuality; const RefIdColumn* mUses; ToolColumns(const InventoryColumns& base) : InventoryColumns(base) , mQuality(nullptr) , mUses(nullptr) { } }; /// \brief Adapter for tools with limited uses IDs (lockpick, repair, probes) template class ToolRefIdAdapter : public InventoryRefIdAdapter { ToolColumns mTools; public: ToolRefIdAdapter(UniversalId::Type type, const ToolColumns& columns); QVariant getData(const RefIdColumn* column, const RefIdData& data, int index) const override; void setData(const RefIdColumn* column, RefIdData& data, int index, const QVariant& value) const override; ///< If the data type does not match an exception is thrown. }; template ToolRefIdAdapter::ToolRefIdAdapter(UniversalId::Type type, const ToolColumns& columns) : InventoryRefIdAdapter(type, columns) , mTools(columns) { } template QVariant ToolRefIdAdapter::getData(const RefIdColumn* column, const RefIdData& data, int index) const { const Record& record = static_cast&>( data.getRecord(RefIdData::LocalIndex(index, BaseRefIdAdapter::getType()))); if (column == mTools.mQuality) return record.get().mData.mQuality; if (column == mTools.mUses) return record.get().mData.mUses; return InventoryRefIdAdapter::getData(column, data, index); } template void ToolRefIdAdapter::setData( const RefIdColumn* column, RefIdData& data, int index, const QVariant& value) const { Record& record = static_cast&>( data.getRecord(RefIdData::LocalIndex(index, BaseRefIdAdapter::getType()))); RecordT record2 = record.get(); if (column == mTools.mQuality) record2.mData.mQuality = value.toFloat(); else if (column == mTools.mUses) record2.mData.mUses = value.toInt(); else { InventoryRefIdAdapter::setData(column, data, index, value); return; } record.setModified(record2); } struct ActorColumns : public NameColumns { const RefIdColumn* mHello; const RefIdColumn* mFlee; const RefIdColumn* mFight; const RefIdColumn* mAlarm; const RefIdColumn* mInventory; const RefIdColumn* mSpells; const RefIdColumn* mDestinations; const RefIdColumn* mAiPackages; std::map mServices; ActorColumns(const NameColumns& base) : NameColumns(base) , mHello(nullptr) , mFlee(nullptr) , mFight(nullptr) , mAlarm(nullptr) , mInventory(nullptr) , mSpells(nullptr) , mDestinations(nullptr) , mAiPackages(nullptr) { } }; /// \brief Adapter for actor IDs (handles common AI functionality) template class ActorRefIdAdapter : public NameRefIdAdapter { ActorColumns mActors; public: ActorRefIdAdapter(UniversalId::Type type, const ActorColumns& columns); QVariant getData(const RefIdColumn* column, const RefIdData& data, int index) const override; void setData(const RefIdColumn* column, RefIdData& data, int index, const QVariant& value) const override; ///< If the data type does not match an exception is thrown. }; template ActorRefIdAdapter::ActorRefIdAdapter(UniversalId::Type type, const ActorColumns& columns) : NameRefIdAdapter(type, columns) , mActors(columns) { } template QVariant ActorRefIdAdapter::getData(const RefIdColumn* column, const RefIdData& data, int index) const { const Record& record = static_cast&>( data.getRecord(RefIdData::LocalIndex(index, BaseRefIdAdapter::getType()))); 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 QVariant::fromValue(ColumnBase::TableEdit_Full); if (column == mActors.mSpells) return QVariant::fromValue(ColumnBase::TableEdit_Full); if (column == mActors.mDestinations) return QVariant::fromValue(ColumnBase::TableEdit_Full); if (column == mActors.mAiPackages) return QVariant::fromValue(ColumnBase::TableEdit_Full); std::map::const_iterator iter = mActors.mServices.find(column); if (iter != mActors.mServices.end()) return (record.get().mAiData.mServices & iter->second) != 0; return NameRefIdAdapter::getData(column, data, index); } template void ActorRefIdAdapter::setData( const RefIdColumn* column, RefIdData& data, int index, const QVariant& value) const { Record& record = static_cast&>( data.getRecord(RefIdData::LocalIndex(index, BaseRefIdAdapter::getType()))); RecordT record2 = record.get(); if (column == mActors.mHello) record2.mAiData.mHello = value.toInt(); else if (column == mActors.mFlee) // Flee, Fight and Alarm ratings are probabilities. record2.mAiData.mFlee = std::min(100, value.toInt()); else if (column == mActors.mFight) record2.mAiData.mFight = std::min(100, value.toInt()); else if (column == mActors.mAlarm) record2.mAiData.mAlarm = std::min(100, value.toInt()); else { typename std::map::const_iterator iter = mActors.mServices.find(column); if (iter != mActors.mServices.end()) { if (value.toInt() != 0) record2.mAiData.mServices |= iter->second; else record2.mAiData.mServices &= ~iter->second; } else { NameRefIdAdapter::setData(column, data, index, value); return; } } record.setModified(record2); } class ApparatusRefIdAdapter : public InventoryRefIdAdapter { const RefIdColumn* mType; const RefIdColumn* mQuality; public: ApparatusRefIdAdapter(const InventoryColumns& columns, const RefIdColumn* type, const RefIdColumn* quality); QVariant getData(const RefIdColumn* column, const RefIdData& data, int index) const override; void setData(const RefIdColumn* column, RefIdData& data, int index, const QVariant& value) const override; ///< If the data type does not match an exception is thrown. }; class ArmorRefIdAdapter : public EnchantableRefIdAdapter { const RefIdColumn* mType; const RefIdColumn* mHealth; const RefIdColumn* mArmor; const RefIdColumn* mPartRef; public: ArmorRefIdAdapter(const EnchantableColumns& columns, const RefIdColumn* type, const RefIdColumn* health, const RefIdColumn* armor, const RefIdColumn* partRef); QVariant getData(const RefIdColumn* column, const RefIdData& data, int index) const override; void setData(const RefIdColumn* column, RefIdData& data, int index, const QVariant& value) const override; ///< If the data type does not match an exception is thrown. }; class BookRefIdAdapter : public EnchantableRefIdAdapter { const RefIdColumn* mBookType; const RefIdColumn* mSkill; const RefIdColumn* mText; public: BookRefIdAdapter(const EnchantableColumns& columns, const RefIdColumn* bookType, const RefIdColumn* skill, const RefIdColumn* text); QVariant getData(const RefIdColumn* column, const RefIdData& data, int index) const override; void setData(const RefIdColumn* column, RefIdData& data, int index, const QVariant& value) const override; ///< If the data type does not match an exception is thrown. }; class ClothingRefIdAdapter : public EnchantableRefIdAdapter { const RefIdColumn* mType; const RefIdColumn* mPartRef; public: ClothingRefIdAdapter(const EnchantableColumns& columns, const RefIdColumn* type, const RefIdColumn* partRef); QVariant getData(const RefIdColumn* column, const RefIdData& data, int index) const override; void setData(const RefIdColumn* column, RefIdData& data, int index, const QVariant& value) const override; ///< If the data type does not match an exception is thrown. }; class ContainerRefIdAdapter : public NameRefIdAdapter { const RefIdColumn* mWeight; const RefIdColumn* mOrganic; const RefIdColumn* mRespawn; const RefIdColumn* mContent; public: ContainerRefIdAdapter(const NameColumns& columns, const RefIdColumn* weight, const RefIdColumn* organic, const RefIdColumn* respawn, const RefIdColumn* content); QVariant getData(const RefIdColumn* column, const RefIdData& data, int index) const override; void setData(const RefIdColumn* column, RefIdData& data, int index, const QVariant& value) const override; ///< If the data type does not match an exception is thrown. }; struct CreatureColumns : public ActorColumns { std::map mFlags; const RefIdColumn* mType; const RefIdColumn* mScale; const RefIdColumn* mOriginal; const RefIdColumn* mAttributes; const RefIdColumn* mAttacks; const RefIdColumn* mMisc; const RefIdColumn* mBloodType; CreatureColumns(const ActorColumns& actorColumns); }; class CreatureRefIdAdapter : public ActorRefIdAdapter { CreatureColumns mColumns; public: CreatureRefIdAdapter(const CreatureColumns& columns); QVariant getData(const RefIdColumn* column, const RefIdData& data, int index) const override; void setData(const RefIdColumn* column, RefIdData& data, int index, const QVariant& value) const override; ///< If the data type does not match an exception is thrown. }; class DoorRefIdAdapter : public NameRefIdAdapter { const RefIdColumn* mOpenSound; const RefIdColumn* mCloseSound; public: DoorRefIdAdapter(const NameColumns& columns, const RefIdColumn* openSound, const RefIdColumn* closeSound); QVariant getData(const RefIdColumn* column, const RefIdData& data, int index) const override; void setData(const RefIdColumn* column, RefIdData& data, int index, const QVariant& value) const override; ///< If the data type does not match an exception is thrown. }; struct LightColumns : public InventoryColumns { const RefIdColumn* mTime; const RefIdColumn* mRadius; const RefIdColumn* mColor; const RefIdColumn* mSound; const RefIdColumn* mEmitterType; std::map mFlags; LightColumns(const InventoryColumns& columns); }; class LightRefIdAdapter : public InventoryRefIdAdapter { LightColumns mColumns; public: LightRefIdAdapter(const LightColumns& columns); QVariant getData(const RefIdColumn* column, const RefIdData& data, int index) const override; void setData(const RefIdColumn* column, RefIdData& data, int index, const QVariant& value) const override; ///< If the data type does not match an exception is thrown. }; class MiscRefIdAdapter : public InventoryRefIdAdapter { const RefIdColumn* mKey; public: MiscRefIdAdapter(const InventoryColumns& columns, const RefIdColumn* key); QVariant getData(const RefIdColumn* column, const RefIdData& data, int index) const override; void setData(const RefIdColumn* column, RefIdData& data, int index, const QVariant& value) const override; ///< If the data type does not match an exception is thrown. }; struct NpcColumns : public ActorColumns { std::map mFlags; const RefIdColumn* mRace; const RefIdColumn* mClass; const RefIdColumn* mFaction; const RefIdColumn* mHair; const RefIdColumn* mHead; const RefIdColumn* mAttributes; // depends on npc type const RefIdColumn* mSkills; // depends on npc type const RefIdColumn* mMisc; // may depend on npc type, e.g. FactionID const RefIdColumn* mBloodType; const RefIdColumn* mGender; NpcColumns(const ActorColumns& actorColumns); }; class NpcRefIdAdapter : public ActorRefIdAdapter { NpcColumns mColumns; public: NpcRefIdAdapter(const NpcColumns& columns); QVariant getData(const RefIdColumn* column, const RefIdData& data, int index) const override; void setData(const RefIdColumn* column, RefIdData& data, int index, const QVariant& value) const override; ///< If the data type does not match an exception is thrown. }; struct WeaponColumns : public EnchantableColumns { const RefIdColumn* mType; const RefIdColumn* mHealth; const RefIdColumn* mSpeed; const RefIdColumn* mReach; const RefIdColumn* mChop[2]; const RefIdColumn* mSlash[2]; const RefIdColumn* mThrust[2]; std::map mFlags; WeaponColumns(const EnchantableColumns& columns); }; class WeaponRefIdAdapter : public EnchantableRefIdAdapter { WeaponColumns mColumns; public: WeaponRefIdAdapter(const WeaponColumns& columns); QVariant getData(const RefIdColumn* column, const RefIdData& data, int index) const override; void setData(const RefIdColumn* column, RefIdData& data, int index, const QVariant& value) const override; ///< If the data type does not match an exception is thrown. }; class NpcAttributesRefIdAdapter : public NestedRefIdAdapterBase { public: NpcAttributesRefIdAdapter() = default; void addNestedRow(const RefIdColumn* column, RefIdData& data, int index, int position) const override; void removeNestedRow(const RefIdColumn* column, RefIdData& data, int index, int rowToRemove) const override; void setNestedTable(const RefIdColumn* column, RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const override; NestedTableWrapperBase* nestedTable(const RefIdColumn* column, const RefIdData& data, int index) const override; QVariant getNestedData(const RefIdColumn* column, const RefIdData& data, int index, int subRowIndex, int subColIndex) const override; void setNestedData(const RefIdColumn* column, RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const override; int getNestedColumnsCount(const RefIdColumn* column, const RefIdData& data) const override; int getNestedRowsCount(const RefIdColumn* column, const RefIdData& data, int index) const override; }; class NpcSkillsRefIdAdapter : public NestedRefIdAdapterBase { public: NpcSkillsRefIdAdapter() = default; void addNestedRow(const RefIdColumn* column, RefIdData& data, int index, int position) const override; void removeNestedRow(const RefIdColumn* column, RefIdData& data, int index, int rowToRemove) const override; void setNestedTable(const RefIdColumn* column, RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const override; NestedTableWrapperBase* nestedTable(const RefIdColumn* column, const RefIdData& data, int index) const override; QVariant getNestedData(const RefIdColumn* column, const RefIdData& data, int index, int subRowIndex, int subColIndex) const override; void setNestedData(const RefIdColumn* column, RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const override; int getNestedColumnsCount(const RefIdColumn* column, const RefIdData& data) const override; int getNestedRowsCount(const RefIdColumn* column, const RefIdData& data, int index) const override; }; class NpcMiscRefIdAdapter : public NestedRefIdAdapterBase { public: NpcMiscRefIdAdapter() = default; NpcMiscRefIdAdapter(const NpcMiscRefIdAdapter&) = delete; NpcMiscRefIdAdapter& operator=(const NpcMiscRefIdAdapter&) = delete; ~NpcMiscRefIdAdapter() override = default; void addNestedRow(const RefIdColumn* column, RefIdData& data, int index, int position) const override; void removeNestedRow(const RefIdColumn* column, RefIdData& data, int index, int rowToRemove) const override; void setNestedTable(const RefIdColumn* column, RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const override; NestedTableWrapperBase* nestedTable(const RefIdColumn* column, const RefIdData& data, int index) const override; QVariant getNestedData(const RefIdColumn* column, const RefIdData& data, int index, int subRowIndex, int subColIndex) const override; void setNestedData(const RefIdColumn* column, RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const override; int getNestedColumnsCount(const RefIdColumn* column, const RefIdData& data) const override; int getNestedRowsCount(const RefIdColumn* column, const RefIdData& data, int index) const override; }; class CreatureAttributesRefIdAdapter : public NestedRefIdAdapterBase { public: CreatureAttributesRefIdAdapter() = default; void addNestedRow(const RefIdColumn* column, RefIdData& data, int index, int position) const override; void removeNestedRow(const RefIdColumn* column, RefIdData& data, int index, int rowToRemove) const override; void setNestedTable(const RefIdColumn* column, RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const override; NestedTableWrapperBase* nestedTable(const RefIdColumn* column, const RefIdData& data, int index) const override; QVariant getNestedData(const RefIdColumn* column, const RefIdData& data, int index, int subRowIndex, int subColIndex) const override; void setNestedData(const RefIdColumn* column, RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const override; int getNestedColumnsCount(const RefIdColumn* column, const RefIdData& data) const override; int getNestedRowsCount(const RefIdColumn* column, const RefIdData& data, int index) const override; }; class CreatureAttackRefIdAdapter : public NestedRefIdAdapterBase { public: CreatureAttackRefIdAdapter() = default; void addNestedRow(const RefIdColumn* column, RefIdData& data, int index, int position) const override; void removeNestedRow(const RefIdColumn* column, RefIdData& data, int index, int rowToRemove) const override; void setNestedTable(const RefIdColumn* column, RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const override; NestedTableWrapperBase* nestedTable(const RefIdColumn* column, const RefIdData& data, int index) const override; QVariant getNestedData(const RefIdColumn* column, const RefIdData& data, int index, int subRowIndex, int subColIndex) const override; void setNestedData(const RefIdColumn* column, RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const override; int getNestedColumnsCount(const RefIdColumn* column, const RefIdData& data) const override; int getNestedRowsCount(const RefIdColumn* column, const RefIdData& data, int index) const override; }; class CreatureMiscRefIdAdapter : public NestedRefIdAdapterBase { public: CreatureMiscRefIdAdapter() = default; CreatureMiscRefIdAdapter(const CreatureMiscRefIdAdapter&) = delete; CreatureMiscRefIdAdapter& operator=(const CreatureMiscRefIdAdapter&) = delete; ~CreatureMiscRefIdAdapter() override = default; void addNestedRow(const RefIdColumn* column, RefIdData& data, int index, int position) const override; void removeNestedRow(const RefIdColumn* column, RefIdData& data, int index, int rowToRemove) const override; void setNestedTable(const RefIdColumn* column, RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const override; NestedTableWrapperBase* nestedTable(const RefIdColumn* column, const RefIdData& data, int index) const override; QVariant getNestedData(const RefIdColumn* column, const RefIdData& data, int index, int subRowIndex, int subColIndex) const override; void setNestedData(const RefIdColumn* column, RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const override; int getNestedColumnsCount(const RefIdColumn* column, const RefIdData& data) const override; int getNestedRowsCount(const RefIdColumn* column, const RefIdData& data, int index) const override; }; template class EffectsListAdapter; template class EffectsRefIdAdapter : public EffectsListAdapter, public NestedRefIdAdapterBase { UniversalId::Type mType; // not implemented EffectsRefIdAdapter(const EffectsRefIdAdapter&); EffectsRefIdAdapter& operator=(const EffectsRefIdAdapter&); public: EffectsRefIdAdapter(UniversalId::Type type) : mType(type) { } virtual ~EffectsRefIdAdapter() = default; void addNestedRow(const RefIdColumn* column, RefIdData& data, int index, int position) const override { Record& record = static_cast&>(data.getRecord(RefIdData::LocalIndex(index, mType))); EffectsListAdapter::addRow(record, position); } void removeNestedRow(const RefIdColumn* column, RefIdData& data, int index, int rowToRemove) const override { Record& record = static_cast&>(data.getRecord(RefIdData::LocalIndex(index, mType))); EffectsListAdapter::removeRow(record, rowToRemove); } void setNestedTable(const RefIdColumn* column, RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const override { Record& record = static_cast&>(data.getRecord(RefIdData::LocalIndex(index, mType))); EffectsListAdapter::setTable(record, nestedTable); } NestedTableWrapperBase* nestedTable(const RefIdColumn* column, const RefIdData& data, int index) const override { const Record& record = static_cast&>(data.getRecord(RefIdData::LocalIndex(index, mType))); return EffectsListAdapter::table(record); } QVariant getNestedData(const RefIdColumn* column, const RefIdData& data, int index, int subRowIndex, int subColIndex) const override { const Record& record = static_cast&>(data.getRecord(RefIdData::LocalIndex(index, mType))); return EffectsListAdapter::getData(record, subRowIndex, subColIndex); } void setNestedData(const RefIdColumn* column, RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const override { Record& record = static_cast&>(data.getRecord(RefIdData::LocalIndex(row, mType))); EffectsListAdapter::setData(record, value, subRowIndex, subColIndex); } int getNestedColumnsCount(const RefIdColumn* column, const RefIdData& data) const override { const Record record; // not used, just a dummy return EffectsListAdapter::getColumnsCount(record); } int getNestedRowsCount(const RefIdColumn* column, const RefIdData& data, int index) const override { const Record& record = static_cast&>(data.getRecord(RefIdData::LocalIndex(index, mType))); return EffectsListAdapter::getRowsCount(record); } }; template class NestedInventoryRefIdAdapter : public NestedRefIdAdapterBase { UniversalId::Type mType; // not implemented NestedInventoryRefIdAdapter(const NestedInventoryRefIdAdapter&); NestedInventoryRefIdAdapter& operator=(const NestedInventoryRefIdAdapter&); public: NestedInventoryRefIdAdapter(UniversalId::Type type) : mType(type) { } virtual ~NestedInventoryRefIdAdapter() = default; void addNestedRow(const RefIdColumn* column, RefIdData& data, int index, int position) const override { Record& record = static_cast&>(data.getRecord(RefIdData::LocalIndex(index, mType))); ESXRecordT container = record.get(); std::vector& list = container.mInventory.mList; ESM::ContItem newRow = ESM::ContItem(); if (position >= (int)list.size()) list.push_back(newRow); else list.insert(list.begin() + position, newRow); record.setModified(container); } void removeNestedRow(const RefIdColumn* column, RefIdData& data, int index, int rowToRemove) const override { Record& record = static_cast&>(data.getRecord(RefIdData::LocalIndex(index, mType))); ESXRecordT container = record.get(); std::vector& list = container.mInventory.mList; if (rowToRemove < 0 || rowToRemove >= static_cast(list.size())) throw std::runtime_error("index out of range"); list.erase(list.begin() + rowToRemove); record.setModified(container); } void setNestedTable(const RefIdColumn* column, RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const override { Record& record = static_cast&>(data.getRecord(RefIdData::LocalIndex(index, mType))); ESXRecordT container = record.get(); container.mInventory.mList = static_cast>&>(nestedTable).mNestedTable; record.setModified(container); } NestedTableWrapperBase* nestedTable(const RefIdColumn* column, const RefIdData& data, int index) const override { const Record& record = static_cast&>(data.getRecord(RefIdData::LocalIndex(index, mType))); // deleted by dtor of NestedTableStoring return new NestedTableWrapper>(record.get().mInventory.mList); } QVariant getNestedData(const RefIdColumn* column, const RefIdData& data, int index, int subRowIndex, int subColIndex) const override { const Record& record = static_cast&>(data.getRecord(RefIdData::LocalIndex(index, mType))); const std::vector& list = record.get().mInventory.mList; if (subRowIndex < 0 || subRowIndex >= static_cast(list.size())) throw std::runtime_error("index out of range"); const ESM::ContItem& content = list.at(subRowIndex); switch (subColIndex) { case 0: return QString::fromUtf8(content.mItem.getRefIdString().c_str()); case 1: return content.mCount; default: throw std::runtime_error("Trying to access non-existing column in the nested table!"); } } void setNestedData(const RefIdColumn* column, RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const override { Record& record = static_cast&>(data.getRecord(RefIdData::LocalIndex(row, mType))); ESXRecordT container = record.get(); std::vector& list = container.mInventory.mList; if (subRowIndex < 0 || subRowIndex >= static_cast(list.size())) throw std::runtime_error("index out of range"); switch (subColIndex) { case 0: list.at(subRowIndex).mItem = ESM::RefId::stringRefId(value.toString().toUtf8().constData()); break; case 1: list.at(subRowIndex).mCount = value.toInt(); break; default: throw std::runtime_error("Trying to access non-existing column in the nested table!"); } record.setModified(container); } int getNestedColumnsCount(const RefIdColumn* column, const RefIdData& data) const override { return 2; } int getNestedRowsCount(const RefIdColumn* column, const RefIdData& data, int index) const override { const Record& record = static_cast&>(data.getRecord(RefIdData::LocalIndex(index, mType))); return static_cast(record.get().mInventory.mList.size()); } }; template class NestedSpellRefIdAdapter : public NestedRefIdAdapterBase { UniversalId::Type mType; // not implemented NestedSpellRefIdAdapter(const NestedSpellRefIdAdapter&); NestedSpellRefIdAdapter& operator=(const NestedSpellRefIdAdapter&); public: NestedSpellRefIdAdapter(UniversalId::Type type) : mType(type) { } virtual ~NestedSpellRefIdAdapter() = default; void addNestedRow(const RefIdColumn* column, RefIdData& data, int index, int position) const override { Record& record = static_cast&>(data.getRecord(RefIdData::LocalIndex(index, mType))); ESXRecordT caster = record.get(); std::vector& list = caster.mSpells.mList; ESM::RefId newString; if (position >= (int)list.size()) list.push_back(newString); else list.insert(list.begin() + position, newString); record.setModified(caster); } void removeNestedRow(const RefIdColumn* column, RefIdData& data, int index, int rowToRemove) const override { Record& record = static_cast&>(data.getRecord(RefIdData::LocalIndex(index, mType))); ESXRecordT caster = record.get(); std::vector& list = caster.mSpells.mList; if (rowToRemove < 0 || rowToRemove >= static_cast(list.size())) throw std::runtime_error("index out of range"); list.erase(list.begin() + rowToRemove); record.setModified(caster); } void setNestedTable(const RefIdColumn* column, RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const override { Record& record = static_cast&>(data.getRecord(RefIdData::LocalIndex(index, mType))); ESXRecordT caster = record.get(); caster.mSpells.mList = static_cast>&>(nestedTable).mNestedTable; record.setModified(caster); } NestedTableWrapperBase* nestedTable(const RefIdColumn* column, const RefIdData& data, int index) const override { const Record& record = static_cast&>(data.getRecord(RefIdData::LocalIndex(index, mType))); // deleted by dtor of NestedTableStoring return new NestedTableWrapper>(record.get().mSpells.mList); } QVariant getNestedData(const RefIdColumn* column, const RefIdData& data, int index, int subRowIndex, int subColIndex) const override { const Record& record = static_cast&>(data.getRecord(RefIdData::LocalIndex(index, mType))); const std::vector& list = record.get().mSpells.mList; if (subRowIndex < 0 || subRowIndex >= static_cast(list.size())) throw std::runtime_error("index out of range"); const ESM::RefId& content = list.at(subRowIndex); if (subColIndex == 0) return QString::fromUtf8(content.getRefIdString().c_str()); else throw std::runtime_error("Trying to access non-existing column in the nested table!"); } void setNestedData(const RefIdColumn* column, RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const override { Record& record = static_cast&>(data.getRecord(RefIdData::LocalIndex(row, mType))); ESXRecordT caster = record.get(); std::vector& list = caster.mSpells.mList; if (subRowIndex < 0 || subRowIndex >= static_cast(list.size())) throw std::runtime_error("index out of range"); if (subColIndex == 0) list.at(subRowIndex) = ESM::RefId::stringRefId(value.toString().toUtf8().constData()); else throw std::runtime_error("Trying to access non-existing column in the nested table!"); record.setModified(caster); } int getNestedColumnsCount(const RefIdColumn* column, const RefIdData& data) const override { return 1; } int getNestedRowsCount(const RefIdColumn* column, const RefIdData& data, int index) const override { const Record& record = static_cast&>(data.getRecord(RefIdData::LocalIndex(index, mType))); return static_cast(record.get().mSpells.mList.size()); } }; template class NestedTravelRefIdAdapter : public NestedRefIdAdapterBase { UniversalId::Type mType; // not implemented NestedTravelRefIdAdapter(const NestedTravelRefIdAdapter&); NestedTravelRefIdAdapter& operator=(const NestedTravelRefIdAdapter&); public: NestedTravelRefIdAdapter(UniversalId::Type type) : mType(type) { } virtual ~NestedTravelRefIdAdapter() = default; void addNestedRow(const RefIdColumn* column, RefIdData& data, int index, int position) const override { Record& record = static_cast&>(data.getRecord(RefIdData::LocalIndex(index, mType))); ESXRecordT traveller = record.get(); std::vector& list = traveller.mTransport.mList; ESM::Position newPos; for (unsigned i = 0; i < 3; ++i) { newPos.pos[i] = 0; newPos.rot[i] = 0; } ESM::Transport::Dest newRow; newRow.mPos = newPos; newRow.mCellName.clear(); if (position >= (int)list.size()) list.push_back(newRow); else list.insert(list.begin() + position, newRow); record.setModified(traveller); } void removeNestedRow(const RefIdColumn* column, RefIdData& data, int index, int rowToRemove) const override { Record& record = static_cast&>(data.getRecord(RefIdData::LocalIndex(index, mType))); ESXRecordT traveller = record.get(); std::vector& list = traveller.mTransport.mList; if (rowToRemove < 0 || rowToRemove >= static_cast(list.size())) throw std::runtime_error("index out of range"); list.erase(list.begin() + rowToRemove); record.setModified(traveller); } void setNestedTable(const RefIdColumn* column, RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const override { Record& record = static_cast&>(data.getRecord(RefIdData::LocalIndex(index, mType))); ESXRecordT traveller = record.get(); traveller.mTransport.mList = static_cast>&>(nestedTable) .mNestedTable; record.setModified(traveller); } NestedTableWrapperBase* nestedTable(const RefIdColumn* column, const RefIdData& data, int index) const override { const Record& record = static_cast&>(data.getRecord(RefIdData::LocalIndex(index, mType))); // deleted by dtor of NestedTableStoring return new NestedTableWrapper>(record.get().mTransport.mList); } QVariant getNestedData(const RefIdColumn* column, const RefIdData& data, int index, int subRowIndex, int subColIndex) const override { const Record& record = static_cast&>(data.getRecord(RefIdData::LocalIndex(index, mType))); const std::vector& list = record.get().mTransport.mList; if (subRowIndex < 0 || subRowIndex >= static_cast(list.size())) throw std::runtime_error("index out of range"); const ESM::Transport::Dest& content = list.at(subRowIndex); switch (subColIndex) { case 0: return QString::fromUtf8(content.mCellName.c_str()); case 1: return content.mPos.pos[0]; case 2: return content.mPos.pos[1]; case 3: return content.mPos.pos[2]; case 4: return content.mPos.rot[0]; case 5: return content.mPos.rot[1]; case 6: return content.mPos.rot[2]; default: throw std::runtime_error("Trying to access non-existing column in the nested table!"); } } void setNestedData(const RefIdColumn* column, RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const override { Record& record = static_cast&>(data.getRecord(RefIdData::LocalIndex(row, mType))); ESXRecordT traveller = record.get(); std::vector& list = traveller.mTransport.mList; if (subRowIndex < 0 || subRowIndex >= static_cast(list.size())) throw std::runtime_error("index out of range"); switch (subColIndex) { case 0: list.at(subRowIndex).mCellName = value.toString().toUtf8().constData(); break; case 1: list.at(subRowIndex).mPos.pos[0] = value.toFloat(); break; case 2: list.at(subRowIndex).mPos.pos[1] = value.toFloat(); break; case 3: list.at(subRowIndex).mPos.pos[2] = value.toFloat(); break; case 4: list.at(subRowIndex).mPos.rot[0] = value.toFloat(); break; case 5: list.at(subRowIndex).mPos.rot[1] = value.toFloat(); break; case 6: list.at(subRowIndex).mPos.rot[2] = value.toFloat(); break; default: throw std::runtime_error("Trying to access non-existing column in the nested table!"); } record.setModified(traveller); } int getNestedColumnsCount(const RefIdColumn* column, const RefIdData& data) const override { return 7; } int getNestedRowsCount(const RefIdColumn* column, const RefIdData& data, int index) const override { const Record& record = static_cast&>(data.getRecord(RefIdData::LocalIndex(index, mType))); return static_cast(record.get().mTransport.mList.size()); } }; template class ActorAiRefIdAdapter : public NestedRefIdAdapterBase { UniversalId::Type mType; // not implemented ActorAiRefIdAdapter(const ActorAiRefIdAdapter&); ActorAiRefIdAdapter& operator=(const ActorAiRefIdAdapter&); public: ActorAiRefIdAdapter(UniversalId::Type type) : mType(type) { } virtual ~ActorAiRefIdAdapter() = default; // FIXME: should check if the AI package type is already in the list and use a default // that wasn't used already (in extreme case do not add anything at all? void addNestedRow(const RefIdColumn* column, RefIdData& data, int index, int position) const override { Record& record = static_cast&>(data.getRecord(RefIdData::LocalIndex(index, mType))); ESXRecordT actor = record.get(); std::vector& list = actor.mAiPackage.mList; ESM::AIPackage newRow; newRow.mType = ESM::AI_Wander; newRow.mWander.mDistance = 0; newRow.mWander.mDuration = 0; newRow.mWander.mTimeOfDay = 0; for (int i = 0; i < 8; ++i) newRow.mWander.mIdle[i] = 0; newRow.mWander.mShouldRepeat = 1; newRow.mCellName.clear(); if (position >= (int)list.size()) list.push_back(newRow); else list.insert(list.begin() + position, newRow); record.setModified(actor); } void removeNestedRow(const RefIdColumn* column, RefIdData& data, int index, int rowToRemove) const override { Record& record = static_cast&>(data.getRecord(RefIdData::LocalIndex(index, mType))); ESXRecordT actor = record.get(); std::vector& list = actor.mAiPackage.mList; if (rowToRemove < 0 || rowToRemove >= static_cast(list.size())) throw std::runtime_error("index out of range"); list.erase(list.begin() + rowToRemove); record.setModified(actor); } void setNestedTable(const RefIdColumn* column, RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const override { Record& record = static_cast&>(data.getRecord(RefIdData::LocalIndex(index, mType))); ESXRecordT actor = record.get(); actor.mAiPackage.mList = static_cast>&>(nestedTable) .mNestedTable; record.setModified(actor); } NestedTableWrapperBase* nestedTable(const RefIdColumn* column, const RefIdData& data, int index) const override { const Record& record = static_cast&>(data.getRecord(RefIdData::LocalIndex(index, mType))); // deleted by dtor of NestedTableStoring return new NestedTableWrapper>(record.get().mAiPackage.mList); } QVariant getNestedData(const RefIdColumn* column, const RefIdData& data, int index, int subRowIndex, int subColIndex) const override { const Record& record = static_cast&>(data.getRecord(RefIdData::LocalIndex(index, mType))); const std::vector& list = record.get().mAiPackage.mList; if (subRowIndex < 0 || subRowIndex >= static_cast(list.size())) throw std::runtime_error("index out of range"); const ESM::AIPackage& content = list.at(subRowIndex); switch (subColIndex) { case 0: // FIXME: should more than one AI package type be allowed? Check vanilla switch (content.mType) { case ESM::AI_Wander: return 0; case ESM::AI_Travel: return 1; case ESM::AI_Follow: return 2; case ESM::AI_Escort: return 3; case ESM::AI_Activate: return 4; default: return QVariant(); } case 1: // wander dist if (content.mType == ESM::AI_Wander) return content.mWander.mDistance; else return QVariant(); case 2: // wander/follow dur if (content.mType == ESM::AI_Wander) return content.mWander.mDuration; else if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort) return content.mTarget.mDuration; else return QVariant(); case 3: // wander ToD if (content.mType == ESM::AI_Wander) return content.mWander.mTimeOfDay; // FIXME: not sure of the format else return QVariant(); case 4: // wander idle case 5: case 6: case 7: case 8: case 9: case 10: case 11: if (content.mType == ESM::AI_Wander) return static_cast(content.mWander.mIdle[subColIndex - 4]); else return QVariant(); case 12: // repeat if (content.mType == ESM::AI_Wander) return content.mWander.mShouldRepeat != 0; else if (content.mType == ESM::AI_Travel) return content.mTravel.mShouldRepeat != 0; else if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort) return content.mTarget.mShouldRepeat != 0; else if (content.mType == ESM::AI_Activate) return content.mActivate.mShouldRepeat != 0; else return QVariant(); case 13: // activate name if (content.mType == ESM::AI_Activate) return QString(content.mActivate.mName.toString().c_str()); else return QVariant(); case 14: // target id if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort) return QString(content.mTarget.mId.toString().c_str()); else return QVariant(); case 15: // target cell if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort) return QString::fromUtf8(content.mCellName.c_str()); else return QVariant(); case 16: if (content.mType == ESM::AI_Travel) return content.mTravel.mX; else if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort) return content.mTarget.mX; else return QVariant(); case 17: if (content.mType == ESM::AI_Travel) return content.mTravel.mY; else if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort) return content.mTarget.mY; else return QVariant(); case 18: if (content.mType == ESM::AI_Travel) return content.mTravel.mZ; else if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort) return content.mTarget.mZ; else return QVariant(); default: throw std::runtime_error("Trying to access non-existing column in the nested table!"); } } void setNestedData(const RefIdColumn* column, RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const override { Record& record = static_cast&>(data.getRecord(RefIdData::LocalIndex(row, mType))); ESXRecordT actor = record.get(); std::vector& list = actor.mAiPackage.mList; if (subRowIndex < 0 || subRowIndex >= static_cast(list.size())) throw std::runtime_error("index out of range"); ESM::AIPackage& content = list.at(subRowIndex); switch (subColIndex) { case 0: // ai package type switch (value.toInt()) { case 0: content.mType = ESM::AI_Wander; break; case 1: content.mType = ESM::AI_Travel; break; case 2: content.mType = ESM::AI_Follow; break; case 3: content.mType = ESM::AI_Escort; break; case 4: content.mType = ESM::AI_Activate; break; default: return; // return without saving } break; // always save case 1: if (content.mType == ESM::AI_Wander) content.mWander.mDistance = static_cast(value.toInt()); else return; // return without saving break; // always save case 2: if (content.mType == ESM::AI_Wander) content.mWander.mDuration = static_cast(value.toInt()); else if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort) content.mTarget.mDuration = static_cast(value.toInt()); else return; // return without saving break; case 3: if (content.mType == ESM::AI_Wander) content.mWander.mTimeOfDay = static_cast(value.toInt()); else return; // return without saving break; // always save case 4: case 5: case 6: case 7: case 8: case 9: case 10: case 11: if (content.mType == ESM::AI_Wander) content.mWander.mIdle[subColIndex - 4] = static_cast(value.toInt()); else return; // return without saving break; // always save case 12: if (content.mType == ESM::AI_Wander) content.mWander.mShouldRepeat = static_cast(value.toInt()); else if (content.mType == ESM::AI_Travel) content.mTravel.mShouldRepeat = static_cast(value.toInt()); else if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort) content.mTarget.mShouldRepeat = static_cast(value.toInt()); else if (content.mType == ESM::AI_Activate) content.mActivate.mShouldRepeat = static_cast(value.toInt()); else return; // return without saving break; // always save case 13: // NAME32 if (content.mType == ESM::AI_Activate) { const QByteArray name = value.toString().toUtf8(); content.mActivate.mName.assign(std::string_view(name.constData(), name.size())); } else return; // return without saving break; // always save case 14: // NAME32 if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort) { const QByteArray id = value.toString().toUtf8(); content.mTarget.mId.assign(std::string_view(id.constData(), id.size())); } else return; // return without saving break; // always save case 15: if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort) content.mCellName = std::string(value.toString().toUtf8().constData()); else return; // return without saving break; // always save case 16: if (content.mType == ESM::AI_Travel) content.mTravel.mX = value.toFloat(); else if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort) content.mTarget.mX = value.toFloat(); else return; // return without saving break; // always save case 17: if (content.mType == ESM::AI_Travel) content.mTravel.mY = value.toFloat(); else if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort) content.mTarget.mY = value.toFloat(); else return; // return without saving break; // always save case 18: if (content.mType == ESM::AI_Travel) content.mTravel.mZ = value.toFloat(); else if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort) content.mTarget.mZ = value.toFloat(); else return; // return without saving break; // always save default: throw std::runtime_error("Trying to access non-existing column in the nested table!"); } record.setModified(actor); } int getNestedColumnsCount(const RefIdColumn* column, const RefIdData& data) const override { return 19; } int getNestedRowsCount(const RefIdColumn* column, const RefIdData& data, int index) const override { const Record& record = static_cast&>(data.getRecord(RefIdData::LocalIndex(index, mType))); return static_cast(record.get().mAiPackage.mList.size()); } }; template class BodyPartRefIdAdapter : public NestedRefIdAdapterBase { UniversalId::Type mType; // not implemented BodyPartRefIdAdapter(const BodyPartRefIdAdapter&); BodyPartRefIdAdapter& operator=(const BodyPartRefIdAdapter&); public: BodyPartRefIdAdapter(UniversalId::Type type) : mType(type) { } virtual ~BodyPartRefIdAdapter() = default; void addNestedRow(const RefIdColumn* column, RefIdData& data, int index, int position) const override { Record& record = static_cast&>(data.getRecord(RefIdData::LocalIndex(index, mType))); ESXRecordT apparel = record.get(); std::vector& list = apparel.mParts.mParts; ESM::PartReference newPart; newPart.mPart = 0; // 0 == head newPart.mMale = ESM::RefId(); newPart.mFemale = ESM::RefId(); if (position >= (int)list.size()) list.push_back(newPart); else list.insert(list.begin() + position, newPart); record.setModified(apparel); } void removeNestedRow(const RefIdColumn* column, RefIdData& data, int index, int rowToRemove) const override { Record& record = static_cast&>(data.getRecord(RefIdData::LocalIndex(index, mType))); ESXRecordT apparel = record.get(); std::vector& list = apparel.mParts.mParts; if (rowToRemove < 0 || rowToRemove >= static_cast(list.size())) throw std::runtime_error("index out of range"); list.erase(list.begin() + rowToRemove); record.setModified(apparel); } void setNestedTable(const RefIdColumn* column, RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const override { Record& record = static_cast&>(data.getRecord(RefIdData::LocalIndex(index, mType))); ESXRecordT apparel = record.get(); apparel.mParts.mParts = static_cast>&>(nestedTable) .mNestedTable; record.setModified(apparel); } NestedTableWrapperBase* nestedTable(const RefIdColumn* column, const RefIdData& data, int index) const override { const Record& record = static_cast&>(data.getRecord(RefIdData::LocalIndex(index, mType))); // deleted by dtor of NestedTableStoring return new NestedTableWrapper>(record.get().mParts.mParts); } QVariant getNestedData(const RefIdColumn* column, const RefIdData& data, int index, int subRowIndex, int subColIndex) const override { const Record& record = static_cast&>(data.getRecord(RefIdData::LocalIndex(index, mType))); const std::vector& list = record.get().mParts.mParts; if (subRowIndex < 0 || subRowIndex >= static_cast(list.size())) throw std::runtime_error("index out of range"); const ESM::PartReference& content = list.at(subRowIndex); switch (subColIndex) { case 0: { if (content.mPart < ESM::PRT_Count) return content.mPart; else throw std::runtime_error("Part Reference Type unexpected value"); } case 1: return QString(content.mMale.getRefIdString().c_str()); case 2: return QString(content.mFemale.getRefIdString().c_str()); default: throw std::runtime_error("Trying to access non-existing column in the nested table!"); } } void setNestedData(const RefIdColumn* column, RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const override { Record& record = static_cast&>(data.getRecord(RefIdData::LocalIndex(row, mType))); ESXRecordT apparel = record.get(); std::vector& list = apparel.mParts.mParts; if (subRowIndex < 0 || subRowIndex >= static_cast(list.size())) throw std::runtime_error("index out of range"); switch (subColIndex) { case 0: list.at(subRowIndex).mPart = static_cast(value.toInt()); break; case 1: list.at(subRowIndex).mMale = ESM::RefId::stringRefId(value.toString().toStdString()); break; case 2: list.at(subRowIndex).mFemale = ESM::RefId::stringRefId(value.toString().toStdString()); break; default: throw std::runtime_error("Trying to access non-existing column in the nested table!"); } record.setModified(apparel); } int getNestedColumnsCount(const RefIdColumn* column, const RefIdData& data) const override { return 3; } int getNestedRowsCount(const RefIdColumn* column, const RefIdData& data, int index) const override { const Record& record = static_cast&>(data.getRecord(RefIdData::LocalIndex(index, mType))); return static_cast(record.get().mParts.mParts.size()); } }; struct LevListColumns : public BaseColumns { const RefIdColumn* mLevList; const RefIdColumn* mNestedListLevList; LevListColumns(const BaseColumns& base) : BaseColumns(base) , mLevList(nullptr) , mNestedListLevList(nullptr) { } }; template class LevelledListRefIdAdapter : public BaseRefIdAdapter { LevListColumns mLevList; public: LevelledListRefIdAdapter(UniversalId::Type type, const LevListColumns& columns); QVariant getData(const RefIdColumn* column, const RefIdData& data, int index) const override; void setData(const RefIdColumn* column, RefIdData& data, int index, const QVariant& value) const override; ///< If the data type does not match an exception is thrown. }; template LevelledListRefIdAdapter::LevelledListRefIdAdapter(UniversalId::Type type, const LevListColumns& columns) : BaseRefIdAdapter(type, columns) , mLevList(columns) { } template QVariant LevelledListRefIdAdapter::getData( const RefIdColumn* column, const RefIdData& data, int index) const { if (column == mLevList.mLevList || column == mLevList.mNestedListLevList) return QVariant::fromValue(ColumnBase::TableEdit_Full); return BaseRefIdAdapter::getData(column, data, index); } template void LevelledListRefIdAdapter::setData( const RefIdColumn* column, RefIdData& data, int index, const QVariant& value) const { BaseRefIdAdapter::setData(column, data, index, value); return; } // for non-tables template class NestedListLevListRefIdAdapter : public NestedRefIdAdapterBase { UniversalId::Type mType; // not implemented NestedListLevListRefIdAdapter(const NestedListLevListRefIdAdapter&); NestedListLevListRefIdAdapter& operator=(const NestedListLevListRefIdAdapter&); public: NestedListLevListRefIdAdapter(UniversalId::Type type) : mType(type) { } virtual ~NestedListLevListRefIdAdapter() = default; void addNestedRow(const RefIdColumn* column, RefIdData& data, int index, int position) const override { throw std::logic_error("cannot add a row to a fixed table"); } void removeNestedRow(const RefIdColumn* column, RefIdData& data, int index, int rowToRemove) const override { throw std::logic_error("cannot remove a row to a fixed table"); } void setNestedTable(const RefIdColumn* column, RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const override { throw std::logic_error("table operation not supported"); } NestedTableWrapperBase* nestedTable(const RefIdColumn* column, const RefIdData& data, int index) const override { throw std::logic_error("table operation not supported"); } QVariant getNestedData(const RefIdColumn* column, const RefIdData& data, int index, int subRowIndex, int subColIndex) const override { const Record& record = static_cast&>(data.getRecord(RefIdData::LocalIndex(index, mType))); if (mType == UniversalId::Type_CreatureLevelledList) { switch (subColIndex) { case 0: return QVariant(); // disable the checkbox editor case 1: return record.get().mFlags & ESM::CreatureLevList::AllLevels; case 2: return static_cast(record.get().mChanceNone); default: throw std::runtime_error("Trying to access non-existing column in levelled creatues!"); } } else { switch (subColIndex) { case 0: return record.get().mFlags & ESM::ItemLevList::Each; case 1: return record.get().mFlags & ESM::ItemLevList::AllLevels; case 2: return static_cast(record.get().mChanceNone); default: throw std::runtime_error("Trying to access non-existing column in levelled items!"); } } } void setNestedData(const RefIdColumn* column, RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const override { Record& record = static_cast&>(data.getRecord(RefIdData::LocalIndex(row, mType))); ESXRecordT leveled = record.get(); if (mType == UniversalId::Type_CreatureLevelledList) { switch (subColIndex) { case 0: return; // return without saving case 1: { if (value.toBool()) { leveled.mFlags |= ESM::CreatureLevList::AllLevels; break; } else { leveled.mFlags &= ~ESM::CreatureLevList::AllLevels; break; } } case 2: leveled.mChanceNone = static_cast(value.toInt()); break; default: throw std::runtime_error("Trying to set non-existing column in levelled creatures!"); } } else { switch (subColIndex) { case 0: { if (value.toBool()) { leveled.mFlags |= ESM::ItemLevList::Each; break; } else { leveled.mFlags &= ~ESM::ItemLevList::Each; break; } } case 1: { if (value.toBool()) { leveled.mFlags |= ESM::ItemLevList::AllLevels; break; } else { leveled.mFlags &= ~ESM::ItemLevList::AllLevels; break; } } case 2: leveled.mChanceNone = static_cast(value.toInt()); break; default: throw std::runtime_error("Trying to set non-existing column in levelled items!"); } } record.setModified(leveled); } int getNestedColumnsCount(const RefIdColumn* column, const RefIdData& data) const override { return 3; } int getNestedRowsCount(const RefIdColumn* column, const RefIdData& data, int index) const override { return 1; // fixed at size 1 } }; // for tables template class NestedLevListRefIdAdapter : public NestedRefIdAdapterBase { UniversalId::Type mType; // not implemented NestedLevListRefIdAdapter(const NestedLevListRefIdAdapter&); NestedLevListRefIdAdapter& operator=(const NestedLevListRefIdAdapter&); public: NestedLevListRefIdAdapter(UniversalId::Type type) : mType(type) { } virtual ~NestedLevListRefIdAdapter() = default; void addNestedRow(const RefIdColumn* column, RefIdData& data, int index, int position) const override { Record& record = static_cast&>(data.getRecord(RefIdData::LocalIndex(index, mType))); ESXRecordT leveled = record.get(); std::vector& list = leveled.mList; ESM::LevelledListBase::LevelItem newItem; newItem.mId = ESM::RefId(); newItem.mLevel = 0; if (position >= (int)list.size()) list.push_back(newItem); else list.insert(list.begin() + position, newItem); record.setModified(leveled); } void removeNestedRow(const RefIdColumn* column, RefIdData& data, int index, int rowToRemove) const override { Record& record = static_cast&>(data.getRecord(RefIdData::LocalIndex(index, mType))); ESXRecordT leveled = record.get(); std::vector& list = leveled.mList; if (rowToRemove < 0 || rowToRemove >= static_cast(list.size())) throw std::runtime_error("index out of range"); list.erase(list.begin() + rowToRemove); record.setModified(leveled); } void setNestedTable(const RefIdColumn* column, RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const override { Record& record = static_cast&>(data.getRecord(RefIdData::LocalIndex(index, mType))); ESXRecordT leveled = record.get(); leveled.mList = static_cast>&>( nestedTable) .mNestedTable; record.setModified(leveled); } NestedTableWrapperBase* nestedTable(const RefIdColumn* column, const RefIdData& data, int index) const override { const Record& record = static_cast&>(data.getRecord(RefIdData::LocalIndex(index, mType))); // deleted by dtor of NestedTableStoring return new NestedTableWrapper>(record.get().mList); } QVariant getNestedData(const RefIdColumn* column, const RefIdData& data, int index, int subRowIndex, int subColIndex) const override { const Record& record = static_cast&>(data.getRecord(RefIdData::LocalIndex(index, mType))); const std::vector& list = record.get().mList; if (subRowIndex < 0 || subRowIndex >= static_cast(list.size())) throw std::runtime_error("index out of range"); const ESM::LevelledListBase::LevelItem& content = list.at(subRowIndex); switch (subColIndex) { case 0: return QString(content.mId.getRefIdString().c_str()); case 1: return content.mLevel; default: throw std::runtime_error("Trying to access non-existing column in the nested table!"); } } void setNestedData(const RefIdColumn* column, RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const override { Record& record = static_cast&>(data.getRecord(RefIdData::LocalIndex(row, mType))); ESXRecordT leveled = record.get(); std::vector& list = leveled.mList; if (subRowIndex < 0 || subRowIndex >= static_cast(list.size())) throw std::runtime_error("index out of range"); switch (subColIndex) { case 0: list.at(subRowIndex).mId = ESM::RefId::stringRefId(value.toString().toStdString()); break; case 1: list.at(subRowIndex).mLevel = static_cast(value.toInt()); break; default: throw std::runtime_error("Trying to access non-existing column in the nested table!"); } record.setModified(leveled); } int getNestedColumnsCount(const RefIdColumn* column, const RefIdData& data) const override { return 2; } int getNestedRowsCount(const RefIdColumn* column, const RefIdData& data, int index) const override { const Record& record = static_cast&>(data.getRecord(RefIdData::LocalIndex(index, mType))); return static_cast(record.get().mList.size()); } }; } #endif