diff --git a/apps/esmtool/esmtool.cpp b/apps/esmtool/esmtool.cpp index 6ccf9c3f3..27980096e 100644 --- a/apps/esmtool/esmtool.cpp +++ b/apps/esmtool/esmtool.cpp @@ -339,6 +339,8 @@ int load(Arguments& info) } std::string id = esm.getHNOString("NAME"); + if (id.empty()) + id = esm.getHNOString("INAM"); if(!quiet && interested) std::cout << "\nRecord: " << n.toString() diff --git a/apps/esmtool/record.cpp b/apps/esmtool/record.cpp index 68e0dcc09..e9fb1c537 100644 --- a/apps/esmtool/record.cpp +++ b/apps/esmtool/record.cpp @@ -740,8 +740,8 @@ void Record::print() if (mData.mClass != "") std::cout << " Class: " << mData.mClass << std::endl; std::cout << " Factionless: " << mData.mFactionLess << std::endl; - if (mData.mNpcFaction != "") - std::cout << " NPC Faction: " << mData.mNpcFaction << std::endl; + if (mData.mFaction != "") + std::cout << " NPC Faction: " << mData.mFaction << std::endl; if (mData.mData.mRank != -1) std::cout << " NPC Rank: " << (int)mData.mData.mRank << std::endl; if (mData.mPcFaction != "") diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 895970696..ec1ecb65e 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -24,11 +24,11 @@ opencs_units (model/world opencs_units_noqt (model/world universalid record commands columnbase scriptcontext cell refidcollection - refidadapter refiddata refidadapterimp ref collectionbase refcollection columns + refidadapter refiddata refidadapterimp ref collectionbase refcollection columns infocollection ) opencs_hdrs_noqt (model/world - columnimp idcollection collection + columnimp idcollection collection info ) @@ -60,7 +60,7 @@ opencs_hdrs_noqt (view/doc opencs_units (view/world table tablesubview scriptsubview util regionmapsubview tablebottombox creator genericcreator cellcreator referenceablecreator referencecreator scenesubview scenetoolbar scenetool - scenetoolmode + scenetoolmode infocreator ) opencs_units (view/render diff --git a/apps/opencs/model/doc/saving.cpp b/apps/opencs/model/doc/saving.cpp index b756a9dc1..73278ba97 100644 --- a/apps/opencs/model/doc/saving.cpp +++ b/apps/opencs/model/doc/saving.cpp @@ -58,12 +58,9 @@ CSMDoc::Saving::Saving (Document& document, const boost::filesystem::path& proje appendStage (new WriteCollectionStage > (mDocument.getData().getSpells(), mState)); - /// \todo deal with info records for topcis and journals - appendStage (new WriteCollectionStage > - (mDocument.getData().getTopics(), mState)); + appendStage (new WriteDialogueCollectionStage (mDocument, mState, false)); - appendStage (new WriteCollectionStage > - (mDocument.getData().getJournals(), mState)); + appendStage (new WriteDialogueCollectionStage (mDocument, mState, true)); appendStage (new WriteRefIdCollectionStage (mDocument, mState)); diff --git a/apps/opencs/model/doc/savingstages.cpp b/apps/opencs/model/doc/savingstages.cpp index d68c72317..8e9bcfc0d 100644 --- a/apps/opencs/model/doc/savingstages.cpp +++ b/apps/opencs/model/doc/savingstages.cpp @@ -7,6 +7,10 @@ #include +#include + +#include "../world/infocollection.hpp" + #include "document.hpp" #include "savingstate.hpp" @@ -80,6 +84,115 @@ void CSMDoc::WriteHeaderStage::perform (int stage, std::vector& mes } +CSMDoc::WriteDialogueCollectionStage::WriteDialogueCollectionStage (Document& document, + SavingState& state, bool journal) +: mDocument (document), mState (state), + mTopics (journal ? document.getData().getJournals() : document.getData().getTopics()), + mInfos (journal ? document.getData().getJournalInfos() : document.getData().getTopicInfos()) +{} + +int CSMDoc::WriteDialogueCollectionStage::setup() +{ + return mTopics.getSize(); +} + +void CSMDoc::WriteDialogueCollectionStage::perform (int stage, std::vector& messages) +{ + const CSMWorld::Record& topic = mTopics.getRecord (stage); + + CSMWorld::RecordBase::State state = topic.mState; + + if (state==CSMWorld::RecordBase::State_Deleted) + { + // if the topic is deleted, we do not need to bother with INFO records. + + /// \todo wrote record with delete flag + + return; + } + + // Test, if we need to save anything associated info records. + bool infoModified = false; + + CSMWorld::InfoCollection::Range range = mInfos.getTopicRange (topic.get().mId); + + for (CSMWorld::InfoCollection::RecordConstIterator iter (range.first); iter!=range.second; ++iter) + { + CSMWorld::RecordBase::State state = iter->mState; + + if (state==CSMWorld::RecordBase::State_Modified || + state==CSMWorld::RecordBase::State_ModifiedOnly || + state==CSMWorld::RecordBase::State_Deleted) + { + infoModified = true; + break; + } + } + + if (state==CSMWorld::RecordBase::State_Modified || + state==CSMWorld::RecordBase::State_ModifiedOnly || + infoModified) + { + // always write the topic record + std::string type; + for (int i=0; i<4; ++i) + /// \todo make endianess agnostic (change ESMWriter interface?) + type += reinterpret_cast (&topic.mModified.sRecordId)[i]; + + mState.getWriter().startRecord (type); + mState.getWriter().writeHNCString ("NAME", topic.mModified.mId); + topic.mModified.save (mState.getWriter()); + mState.getWriter().endRecord (type); + + // write modified selected info records + for (CSMWorld::InfoCollection::RecordConstIterator iter (range.first); iter!=range.second; + ++iter) + { + CSMWorld::RecordBase::State state = iter->mState; + + if (state==CSMWorld::RecordBase::State_Deleted) + { + /// \todo wrote record with delete flag + } + else if (state==CSMWorld::RecordBase::State_Modified || + state==CSMWorld::RecordBase::State_ModifiedOnly) + { + ESM::DialInfo info = iter->get(); + info.mId = info.mId.substr (info.mId.find_last_of ('#')+1); + + if (iter!=range.first) + { + CSMWorld::InfoCollection::RecordConstIterator prev = iter; + --prev; + + info.mPrev = + prev->mModified.mId.substr (prev->mModified.mId.find_last_of ('#')+1); + } + + CSMWorld::InfoCollection::RecordConstIterator next = iter; + ++next; + + if (next!=range.second) + { + info.mNext = + next->mModified.mId.substr (next->mModified.mId.find_last_of ('#')+1); + } + + std::string type; + for (int i=0; i<4; ++i) + /// \todo make endianess agnostic (change ESMWriter interface?) + type += reinterpret_cast (&info.sRecordId)[i]; + + mState.getWriter().startRecord (type); + mState.getWriter().writeHNCString ("INAM", info.mId); + info.save (mState.getWriter()); + mState.getWriter().endRecord (type); + } + } + } +} + + CSMDoc::WriteRefIdCollectionStage::WriteRefIdCollectionStage (Document& document, SavingState& state) : mDocument (document), mState (state) {} diff --git a/apps/opencs/model/doc/savingstages.hpp b/apps/opencs/model/doc/savingstages.hpp index ff94116fd..ca5586511 100644 --- a/apps/opencs/model/doc/savingstages.hpp +++ b/apps/opencs/model/doc/savingstages.hpp @@ -3,13 +3,23 @@ #include "stage.hpp" -#include "savingstate.hpp" - #include "../world/record.hpp" #include "../world/idcollection.hpp" #include "../filter/filter.hpp" +#include "savingstate.hpp" + +namespace ESM +{ + struct Dialogue; +} + +namespace CSMWorld +{ + class InfoCollection; +} + namespace CSMDoc { class Document; @@ -106,6 +116,25 @@ namespace CSMDoc } + class WriteDialogueCollectionStage : public Stage + { + Document& mDocument; + SavingState& mState; + const CSMWorld::IdCollection& mTopics; + CSMWorld::InfoCollection& mInfos; + + public: + + WriteDialogueCollectionStage (Document& document, SavingState& state, bool journal); + + virtual int setup(); + ///< \return number of steps + + virtual void perform (int stage, std::vector& messages); + ///< Messages resulting from this stage will be appended to \a messages. + }; + + class WriteRefIdCollectionStage : public Stage { Document& mDocument; diff --git a/apps/opencs/model/world/collection.hpp b/apps/opencs/model/world/collection.hpp index 84a00cef8..df144ba7b 100644 --- a/apps/opencs/model/world/collection.hpp +++ b/apps/opencs/model/world/collection.hpp @@ -51,6 +51,18 @@ namespace CSMWorld Collection (const Collection&); Collection& operator= (const Collection&); + protected: + + const std::map& getIdMap() const; + + const std::vector >& getRecords() const; + + bool reorderRowsImp (int baseIndex, const std::vector& newOrder); + ///< Reorder the rows [baseIndex, baseIndex+newOrder.size()) according to the indices + /// given in \a newOrder (baseIndex+newOrder[0] specifies the new index of row baseIndex). + /// + /// \return Success? + public: Collection(); @@ -104,7 +116,8 @@ namespace CSMWorld virtual const Record& getRecord (int index) const; - virtual int getAppendIndex (UniversalId::Type type = UniversalId::Type_None) const; + virtual int getAppendIndex (const std::string& id, + UniversalId::Type type = UniversalId::Type_None) const; ///< \param type Will be ignored, unless the collection supports multiple record types virtual std::vector getIds (bool listDeleted = true) const; @@ -112,12 +125,74 @@ namespace CSMWorld /// /// \param listDeleted include deleted record in the list + virtual void insertRecord (const RecordBase& record, int index, + UniversalId::Type type = UniversalId::Type_None); + ///< Insert record before index. + /// + /// If the record type does not match, an exception is thrown. + /// + /// If the index is invalid either generally (by being out of range) or for the particular + /// record, an exception is thrown. + + virtual bool reorderRows (int baseIndex, const std::vector& newOrder); + ///< Reorder the rows [baseIndex, baseIndex+newOrder.size()) according to the indices + /// given in \a newOrder (baseIndex+newOrder[0] specifies the new index of row baseIndex). + /// + /// \return Success? + void addColumn (Column *column); void setRecord (int index, const Record& record); ///< \attention This function must not change the ID. }; + template + const std::map& Collection::getIdMap() const + { + return mIndex; + } + + template + const std::vector >& Collection::getRecords() const + { + return mRecords; + } + + template + bool Collection::reorderRowsImp (int baseIndex, + const std::vector& newOrder) + { + if (!newOrder.empty()) + { + int size = static_cast (newOrder.size()); + + // check that all indices are present + std::vector test (newOrder); + std::sort (test.begin(), test.end()); + if (*test.begin()!=0 || *--test.end()!=size-1) + return false; + + // reorder records + std::vector > buffer (size); + + for (int i=0; i::iterator iter (mIndex.begin()); iter!=mIndex.end(); + ++iter) + if (iter->second>=baseIndex && iter->secondsecond = newOrder.at (iter->second-baseIndex)+baseIndex; + } + + return true; + } + template Collection::Collection() {} @@ -142,8 +217,7 @@ namespace CSMWorld record2.mState = Record::State_ModifiedOnly; record2.mModified = record; - mRecords.push_back (record2); - mIndex.insert (std::make_pair (Misc::StringUtils::lowerCase (id), mRecords.size()-1)); + insertRecord (record2, getAppendIndex (id)); } else { @@ -260,7 +334,12 @@ namespace CSMWorld ESXRecordT record; IdAccessorT().getId (record) = id; record.blank(); - add (record); + + Record record2; + record2.mState = Record::State_ModifiedOnly; + record2.mModified = record; + + insertRecord (record2, getAppendIndex (id, type), type); } template @@ -286,14 +365,14 @@ namespace CSMWorld void Collection::appendRecord (const RecordBase& record, UniversalId::Type type) { - mRecords.push_back (dynamic_cast&> (record)); - mIndex.insert (std::make_pair (Misc::StringUtils::lowerCase (IdAccessorT().getId ( - dynamic_cast&> (record).get())), - mRecords.size()-1)); + insertRecord (record, + getAppendIndex (IdAccessorT().getId ( + dynamic_cast&> (record).get()), type), type); } template - int Collection::getAppendIndex (UniversalId::Type type) const + int Collection::getAppendIndex (const std::string& id, + UniversalId::Type type) const { return static_cast (mRecords.size()); } @@ -326,6 +405,29 @@ namespace CSMWorld return mRecords.at (index); } + template + void Collection::insertRecord (const RecordBase& record, int index, + UniversalId::Type type) + { + if (index<0 || index>static_cast (mRecords.size())) + throw std::runtime_error ("index out of range"); + + const Record& record2 = dynamic_cast&> (record); + + mRecords.insert (mRecords.begin()+index, record2); + + if (index (mRecords.size())-1) + { + for (std::map::iterator iter (mIndex.begin()); iter!=mIndex.end(); + ++iter) + if (iter->second>=index) + ++(iter->second); + } + + mIndex.insert (std::make_pair (Misc::StringUtils::lowerCase (IdAccessorT().getId ( + record2.get())), index)); + } + template void Collection::setRecord (int index, const Record& record) { @@ -334,6 +436,12 @@ namespace CSMWorld mRecords.at (index) = record; } + + template + bool Collection::reorderRows (int baseIndex, const std::vector& newOrder) + { + return false; + } } #endif diff --git a/apps/opencs/model/world/collectionbase.hpp b/apps/opencs/model/world/collectionbase.hpp index 1056a961d..ab7a9ebec 100644 --- a/apps/opencs/model/world/collectionbase.hpp +++ b/apps/opencs/model/world/collectionbase.hpp @@ -2,6 +2,7 @@ #define CSM_WOLRD_COLLECTIONBASE_H #include +#include #include "universalid.hpp" #include "columns.hpp" @@ -77,7 +78,8 @@ namespace CSMWorld virtual const RecordBase& getRecord (int index) const = 0; - virtual int getAppendIndex (UniversalId::Type type = UniversalId::Type_None) const = 0; + virtual int getAppendIndex (const std::string& id, + UniversalId::Type type = UniversalId::Type_None) const = 0; ///< \param type Will be ignored, unless the collection supports multiple record types virtual std::vector getIds (bool listDeleted = true) const = 0; @@ -85,6 +87,12 @@ namespace CSMWorld /// /// \param listDeleted include deleted record in the list + virtual bool reorderRows (int baseIndex, const std::vector& newOrder) = 0; + ///< Reorder the rows [baseIndex, baseIndex+newOrder.size()) according to the indices + /// given in \a newOrder (baseIndex+newOrder[0] specifies the new index of row baseIndex). + /// + /// \return Success? + int searchColumnIndex (Columns::ColumnId id) const; ///< Return index of column with the given \a id. If no such column exists, -1 is returned. diff --git a/apps/opencs/model/world/columnbase.hpp b/apps/opencs/model/world/columnbase.hpp index 9b8d7dafb..70f38c534 100644 --- a/apps/opencs/model/world/columnbase.hpp +++ b/apps/opencs/model/world/columnbase.hpp @@ -44,7 +44,9 @@ namespace CSMWorld Display_WeaponType, Display_RecordState, Display_RefRecordType, - Display_DialogueType + Display_DialogueType, + Display_QuestStatusType, + Display_Gender }; int mColumnId; diff --git a/apps/opencs/model/world/columnimp.hpp b/apps/opencs/model/world/columnimp.hpp index 8575480fd..18aac9e0b 100644 --- a/apps/opencs/model/world/columnimp.hpp +++ b/apps/opencs/model/world/columnimp.hpp @@ -9,6 +9,7 @@ #include "columnbase.hpp" #include "columns.hpp" +#include "info.hpp" namespace CSMWorld { @@ -814,14 +815,14 @@ namespace CSMWorld virtual QVariant get (const Record& record) const { - return QString::fromUtf8 (record.get().mCellId.c_str()); + return QString::fromUtf8 (record.get().mCell.c_str()); } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); - record2.mCellId = data.toString().toUtf8().constData(); + record2.mCell = data.toString().toUtf8().constData(); record.setModified (record2); } @@ -1348,6 +1349,340 @@ namespace CSMWorld return false; } }; + + template + struct QuestStatusTypeColumn : public Column + { + QuestStatusTypeColumn() + : Column (Columns::ColumnId_QuestStatusType, ColumnBase::Display_QuestStatusType) + {} + + virtual QVariant get (const Record& record) const + { + return static_cast (record.get().mQuestStatus); + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + record2.mQuestStatus = static_cast (data.toInt()); + + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct QuestDescriptionColumn : public Column + { + QuestDescriptionColumn() : Column (Columns::ColumnId_QuestDescription, ColumnBase::Display_String) {} + + virtual QVariant get (const Record& record) const + { + return QString::fromUtf8 (record.get().mResponse.c_str()); + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + record2.mResponse = data.toString().toUtf8().constData(); + + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct QuestIndexColumn : public Column + { + QuestIndexColumn() + : Column (Columns::ColumnId_QuestIndex, ColumnBase::Display_Integer) + {} + + virtual QVariant get (const Record& record) const + { + return record.get().mData.mDisposition; + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + record2.mData.mDisposition = data.toInt(); + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct TopicColumn : public Column + { + TopicColumn (bool journal) : Column (journal ? Columns::ColumnId_Journal : Columns::ColumnId_Topic, ColumnBase::Display_String) {} + + virtual QVariant get (const Record& record) const + { + return QString::fromUtf8 (record.get().mTopicId.c_str()); + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + record2.mTopicId = data.toString().toUtf8().constData(); + + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + + virtual bool isUserEditable() const + { + return false; + } + }; + + template + struct ActorColumn : public Column + { + ActorColumn() : Column (Columns::ColumnId_Actor, ColumnBase::Display_String) {} + + virtual QVariant get (const Record& record) const + { + return QString::fromUtf8 (record.get().mActor.c_str()); + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + record2.mActor = data.toString().toUtf8().constData(); + + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct RaceColumn : public Column + { + RaceColumn() : Column (Columns::ColumnId_Race, ColumnBase::Display_String) {} + + virtual QVariant get (const Record& record) const + { + return QString::fromUtf8 (record.get().mRace.c_str()); + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + record2.mRace = data.toString().toUtf8().constData(); + + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct ClassColumn : public Column + { + ClassColumn() : Column (Columns::ColumnId_Class, ColumnBase::Display_String) {} + + virtual QVariant get (const Record& record) const + { + return QString::fromUtf8 (record.get().mClass.c_str()); + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + record2.mClass = data.toString().toUtf8().constData(); + + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct PcFactionColumn : public Column + { + PcFactionColumn() : Column (Columns::ColumnId_PcFaction, ColumnBase::Display_String) {} + + virtual QVariant get (const Record& record) const + { + return QString::fromUtf8 (record.get().mPcFaction.c_str()); + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + record2.mPcFaction = data.toString().toUtf8().constData(); + + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct ResponseColumn : public Column + { + ResponseColumn() : Column (Columns::ColumnId_Response, ColumnBase::Display_String) {} + + virtual QVariant get (const Record& record) const + { + return QString::fromUtf8 (record.get().mResponse.c_str()); + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + record2.mResponse = data.toString().toUtf8().constData(); + + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct DispositionColumn : public Column + { + DispositionColumn() + : Column (Columns::ColumnId_Disposition, ColumnBase::Display_Integer) + {} + + virtual QVariant get (const Record& record) const + { + return record.get().mData.mDisposition; + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + record2.mData.mDisposition = data.toInt(); + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct RankColumn : public Column + { + RankColumn() + : Column (Columns::ColumnId_Rank, ColumnBase::Display_Integer) + {} + + virtual QVariant get (const Record& record) const + { + return static_cast (record.get().mData.mRank); + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + record2.mData.mRank = static_cast (data.toInt()); + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct PcRankColumn : public Column + { + PcRankColumn() + : Column (Columns::ColumnId_PcRank, ColumnBase::Display_Integer) + {} + + virtual QVariant get (const Record& record) const + { + return static_cast (record.get().mData.mPCrank); + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + record2.mData.mPCrank = static_cast (data.toInt()); + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct GenderColumn : public Column + { + GenderColumn() + : Column (Columns::ColumnId_Gender, ColumnBase::Display_Gender) + {} + + virtual QVariant get (const Record& record) const + { + return static_cast (record.get().mData.mGender); + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + record2.mData.mGender = data.toInt(); + + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; } #endif diff --git a/apps/opencs/model/world/columns.cpp b/apps/opencs/model/world/columns.cpp index a842e7b8d..7a1313720 100644 --- a/apps/opencs/model/world/columns.cpp +++ b/apps/opencs/model/world/columns.cpp @@ -160,6 +160,18 @@ namespace CSMWorld { ColumnId_DoorPositionYRot, "Teleport Rot Y" }, { ColumnId_DoorPositionZRot, "Teleport Rot Z" }, { ColumnId_DialogueType, "Dialogue Type" }, + { ColumnId_QuestIndex, "Quest Index" }, + { ColumnId_QuestStatusType, "Quest Status" }, + { ColumnId_QuestDescription, "Quest Description" }, + { ColumnId_Topic, "Topic" }, + { ColumnId_Journal, "Journal" }, + { ColumnId_Actor, "Actor" }, + { ColumnId_PcFaction, "PC Faction" }, + { ColumnId_Response, "Response" }, + { ColumnId_Disposition, "Disposition" }, + { ColumnId_Rank, "Rank" }, + { ColumnId_Gender, "Gender" }, + { ColumnId_PcRank, "PC Rank" }, { ColumnId_Scope, "Scope", }, { ColumnId_UseValue1, "Use value 1" }, @@ -276,6 +288,16 @@ namespace "Topic", "Voice", "Greeting", "Persuasion", 0 }; + static const char *sQuestStatusTypes[] = + { + "None", "Name", "Finished", "Restart", 0 + }; + + static const char *sGenderEnums[] = + { + "Male", "Female", 0 + }; + const char **getEnumNames (CSMWorld::Columns::ColumnId column) { switch (column) @@ -291,6 +313,8 @@ namespace case CSMWorld::Columns::ColumnId_Modification: return sModificationEnums; case CSMWorld::Columns::ColumnId_ValueType: return sVarTypeEnums; case CSMWorld::Columns::ColumnId_DialogueType: return sDialogueTypeEnums; + case CSMWorld::Columns::ColumnId_QuestStatusType: return sQuestStatusTypes; + case CSMWorld::Columns::ColumnId_Gender: return sGenderEnums; default: return 0; } diff --git a/apps/opencs/model/world/columns.hpp b/apps/opencs/model/world/columns.hpp index 55d085a96..52e022e78 100644 --- a/apps/opencs/model/world/columns.hpp +++ b/apps/opencs/model/world/columns.hpp @@ -153,7 +153,19 @@ namespace CSMWorld ColumnId_DoorPositionYRot = 140, ColumnId_DoorPositionZRot = 141, ColumnId_DialogueType = 142, - ColumnId_Scope = 143, + ColumnId_QuestIndex = 143, + ColumnId_QuestStatusType = 144, + ColumnId_QuestDescription = 145, + ColumnId_Topic = 146, + ColumnId_Journal = 147, + ColumnId_Actor = 148, + ColumnId_PcFaction = 149, + ColumnId_Response = 150, + ColumnId_Disposition = 151, + ColumnId_Rank = 152, + ColumnId_Gender = 153, + ColumnId_PcRank = 154, + ColumnId_Scope = 155, // Allocated to a separate value range, so we don't get a collision should we ever need // to extend the number of use values. diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index f6f421c6a..21feb14be 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -122,4 +122,26 @@ void CSMWorld::DeleteCommand::redo() void CSMWorld::DeleteCommand::undo() { mModel.setRecord (mId, *mOld); +} + + +CSMWorld::ReorderRowsCommand::ReorderRowsCommand (IdTable& model, int baseIndex, + const std::vector& newOrder) +: mModel (model), mBaseIndex (baseIndex), mNewOrder (newOrder) +{} + +void CSMWorld::ReorderRowsCommand::redo() +{ + mModel.reorderRows (mBaseIndex, mNewOrder); +} + +void CSMWorld::ReorderRowsCommand::undo() +{ + int size = static_cast (mNewOrder.size()); + std::vector reverse (size); + + for (int i=0; i #include +#include #include #include @@ -99,6 +100,21 @@ namespace CSMWorld virtual void undo(); }; + + class ReorderRowsCommand : public QUndoCommand + { + IdTable& mModel; + int mBaseIndex; + std::vector mNewOrder; + + public: + + ReorderRowsCommand (IdTable& model, int baseIndex, const std::vector& newOrder); + + virtual void redo(); + + virtual void undo(); + }; } #endif \ No newline at end of file diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index 10e56765f..04e35cdaa 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -160,6 +160,29 @@ CSMWorld::Data::Data() : mRefs (mCells) mJournals.addColumn (new RecordStateColumn); mJournals.addColumn (new DialogueTypeColumn (true)); + mTopicInfos.addColumn (new StringIdColumn (true)); + mTopicInfos.addColumn (new RecordStateColumn); + mTopicInfos.addColumn (new TopicColumn (false)); + mTopicInfos.addColumn (new ActorColumn); + mTopicInfos.addColumn (new RaceColumn); + mTopicInfos.addColumn (new ClassColumn); + mTopicInfos.addColumn (new FactionColumn); + mTopicInfos.addColumn (new CellColumn); + mTopicInfos.addColumn (new DispositionColumn); + mTopicInfos.addColumn (new RankColumn); + mTopicInfos.addColumn (new GenderColumn); + mTopicInfos.addColumn (new PcFactionColumn); + mTopicInfos.addColumn (new PcRankColumn); + mTopicInfos.addColumn (new SoundFileColumn); + mTopicInfos.addColumn (new ResponseColumn); + + mJournalInfos.addColumn (new StringIdColumn (true)); + mJournalInfos.addColumn (new RecordStateColumn); + mJournalInfos.addColumn (new TopicColumn (true)); + mJournalInfos.addColumn (new QuestStatusTypeColumn); + mJournalInfos.addColumn (new QuestIndexColumn); + mJournalInfos.addColumn (new QuestDescriptionColumn); + mCells.addColumn (new StringIdColumn); mCells.addColumn (new RecordStateColumn); mCells.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Cell)); @@ -218,6 +241,8 @@ CSMWorld::Data::Data() : mRefs (mCells) addModel (new IdTable (&mSpells), UniversalId::Type_Spells, UniversalId::Type_Spell); addModel (new IdTable (&mTopics), UniversalId::Type_Topics, UniversalId::Type_Topic); addModel (new IdTable (&mJournals), UniversalId::Type_Journals, UniversalId::Type_Journal); + addModel (new IdTable (&mTopicInfos), UniversalId::Type_TopicInfos, UniversalId::Type_TopicInfo); + addModel (new IdTable (&mJournalInfos), UniversalId::Type_JournalInfos, UniversalId::Type_JournalInfo); addModel (new IdTable (&mCells), UniversalId::Type_Cells, UniversalId::Type_Cell); addModel (new IdTable (&mReferenceables), UniversalId::Type_Referenceables, UniversalId::Type_Referenceable); @@ -362,6 +387,25 @@ CSMWorld::IdCollection& CSMWorld::Data::getJournals() return mJournals; } +const CSMWorld::InfoCollection& CSMWorld::Data::getTopicInfos() const +{ + return mTopicInfos; +} + +CSMWorld::InfoCollection& CSMWorld::Data::getTopicInfos() +{ + return mTopicInfos; +} + +const CSMWorld::InfoCollection& CSMWorld::Data::getJournalInfos() const +{ + return mJournalInfos; +} + +CSMWorld::InfoCollection& CSMWorld::Data::getJournalInfos() +{ + return mJournalInfos; +} const CSMWorld::IdCollection& CSMWorld::Data::getCells() const { @@ -441,6 +485,8 @@ void CSMWorld::Data::loadFile (const boost::filesystem::path& path, bool base, b reader.open (path.string()); + const ESM::Dialogue *dialogue = 0; + mAuthor = reader.getAuthor(); mDescription = reader.getDesc(); @@ -505,9 +551,13 @@ void CSMWorld::Data::loadFile (const boost::filesystem::path& path, bool base, b if (record.mType==ESM::Dialogue::Journal) { mJournals.load (record, base); + dialogue = &mJournals.getRecord (id).get(); } else if (record.mType==ESM::Dialogue::Deleted) { + dialogue = 0; // record vector can be shuffled around which would make pointer + // to record invalid + if (mJournals.tryDelete (id)) { /// \todo handle info records @@ -524,11 +574,29 @@ void CSMWorld::Data::loadFile (const boost::filesystem::path& path, bool base, b else { mTopics.load (record, base); + dialogue = &mTopics.getRecord (id).get(); } break; } + case ESM::REC_INFO: + { + if (!dialogue) + { + /// \todo INFO record without matching DIAL record -> report to user + reader.skipRecord(); + break; + } + + if (dialogue->mType==ESM::Dialogue::Journal) + mJournalInfos.load (reader, base, *dialogue); + else + mTopicInfos.load (reader, base, *dialogue); + + break; + } + case ESM::REC_FILT: if (project) @@ -545,6 +613,7 @@ void CSMWorld::Data::loadFile (const boost::filesystem::path& path, bool base, b default: /// \todo throw an exception instead, once all records are implemented + /// or maybe report error and continue? reader.skipRecord(); } } diff --git a/apps/opencs/model/world/data.hpp b/apps/opencs/model/world/data.hpp index 1a9eae2d2..152c3ac41 100644 --- a/apps/opencs/model/world/data.hpp +++ b/apps/opencs/model/world/data.hpp @@ -29,6 +29,7 @@ #include "cell.hpp" #include "refidcollection.hpp" #include "refcollection.hpp" +#include "infocollection.hpp" class QAbstractItemModel; @@ -51,6 +52,8 @@ namespace CSMWorld IdCollection mSpells; IdCollection mTopics; IdCollection mJournals; + InfoCollection mTopicInfos; + InfoCollection mJournalInfos; IdCollection mCells; RefIdCollection mReferenceables; RefCollection mRefs; @@ -131,6 +134,14 @@ namespace CSMWorld IdCollection& getJournals(); + const InfoCollection& getTopicInfos() const; + + InfoCollection& getTopicInfos(); + + const InfoCollection& getJournalInfos() const; + + InfoCollection& getJournalInfos(); + const IdCollection& getCells() const; IdCollection& getCells(); diff --git a/apps/opencs/model/world/idtable.cpp b/apps/opencs/model/world/idtable.cpp index b7b1a9db0..809d64339 100644 --- a/apps/opencs/model/world/idtable.cpp +++ b/apps/opencs/model/world/idtable.cpp @@ -4,15 +4,12 @@ #include "collectionbase.hpp" #include "columnbase.hpp" -CSMWorld::IdTable::IdTable (CollectionBase *idCollection) : mIdCollection (idCollection) -{ - -} +CSMWorld::IdTable::IdTable (CollectionBase *idCollection, Reordering reordering) +: mIdCollection (idCollection), mReordering (reordering) +{} CSMWorld::IdTable::~IdTable() -{ - -} +{} int CSMWorld::IdTable::rowCount (const QModelIndex & parent) const { @@ -118,7 +115,7 @@ QModelIndex CSMWorld::IdTable::parent (const QModelIndex& index) const void CSMWorld::IdTable::addRecord (const std::string& id, UniversalId::Type type) { - int index = mIdCollection->getAppendIndex(); + int index = mIdCollection->getAppendIndex (id, type); beginInsertRows (QModelIndex(), index, index); @@ -138,7 +135,7 @@ void CSMWorld::IdTable::setRecord (const std::string& id, const RecordBase& reco if (index==-1) { - int index = mIdCollection->getAppendIndex(); + int index = mIdCollection->getAppendIndex (id); beginInsertRows (QModelIndex(), index, index); @@ -167,4 +164,17 @@ int CSMWorld::IdTable::searchColumnIndex (Columns::ColumnId id) const int CSMWorld::IdTable::findColumnIndex (Columns::ColumnId id) const { return mIdCollection->findColumnIndex (id); +} + +void CSMWorld::IdTable::reorderRows (int baseIndex, const std::vector& newOrder) +{ + if (!newOrder.empty()) + if (mIdCollection->reorderRows (baseIndex, newOrder)) + emit dataChanged (index (baseIndex, 0), + index (baseIndex+newOrder.size()-1, mIdCollection->getColumns()-1)); +} + +CSMWorld::IdTable::Reordering CSMWorld::IdTable::getReordering() const +{ + return mReordering; } \ No newline at end of file diff --git a/apps/opencs/model/world/idtable.hpp b/apps/opencs/model/world/idtable.hpp index 00c577236..e4ae58fd0 100644 --- a/apps/opencs/model/world/idtable.hpp +++ b/apps/opencs/model/world/idtable.hpp @@ -1,6 +1,8 @@ #ifndef CSM_WOLRD_IDTABLE_H #define CSM_WOLRD_IDTABLE_H +#include + #include #include "universalid.hpp" @@ -15,7 +17,18 @@ namespace CSMWorld { Q_OBJECT + public: + + enum Reordering + { + Reordering_None, + Reordering_WithinTopic + }; + + private: + CollectionBase *mIdCollection; + Reordering mReordering; // not implemented IdTable (const IdTable&); @@ -23,7 +36,7 @@ namespace CSMWorld public: - IdTable (CollectionBase *idCollection); + IdTable (CollectionBase *idCollection, Reordering reordering = Reordering_WithinTopic); ///< The ownership of \a idCollection is not transferred. virtual ~IdTable(); @@ -63,6 +76,12 @@ namespace CSMWorld int findColumnIndex (Columns::ColumnId id) const; ///< Return index of column with the given \a id. If no such column exists, an exception is /// thrown. + + void reorderRows (int baseIndex, const std::vector& newOrder); + ///< Reorder the rows [baseIndex, baseIndex+newOrder.size()) according to the indices + /// given in \a newOrder (baseIndex+newOrder[0] specifies the new index of row baseIndex). + + Reordering getReordering() const; }; } diff --git a/apps/opencs/model/world/info.hpp b/apps/opencs/model/world/info.hpp new file mode 100644 index 000000000..1bcb2dc2d --- /dev/null +++ b/apps/opencs/model/world/info.hpp @@ -0,0 +1,14 @@ +#ifndef CSM_WOLRD_INFO_H +#define CSM_WOLRD_INFO_H + +#include + +namespace CSMWorld +{ + struct Info : public ESM::DialInfo + { + std::string mTopicId; + }; +} + +#endif diff --git a/apps/opencs/model/world/infocollection.cpp b/apps/opencs/model/world/infocollection.cpp new file mode 100644 index 000000000..87bb925c2 --- /dev/null +++ b/apps/opencs/model/world/infocollection.cpp @@ -0,0 +1,184 @@ + +#include "infocollection.hpp" + +#include +#include + +#include +#include + +#include + +void CSMWorld::InfoCollection::load (const Info& record, bool base) +{ + int index = searchId (record.mId); + + if (index==-1) + { + // new record + Record record2; + record2.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly; + (base ? record2.mBase : record2.mModified) = record; + + int index = -1; + + std::string topic = Misc::StringUtils::lowerCase (record2.get().mTopicId); + + if (!record2.get().mPrev.empty()) + { + index = getIndex (record2.get().mPrev, topic); + + if (index!=-1) + ++index; + } + + if (index==-1 && !record2.get().mNext.empty()) + { + index = getIndex (record2.get().mNext, topic); + } + + if (index==-1) + { + Range range = getTopicRange (topic); + + index = std::distance (getRecords().begin(), range.second); + } + + insertRecord (record2, index); + } + else + { + // old record + Record record2 = getRecord (index); + + if (base) + record2.mBase = record; + else + record2.setModified (record); + + setRecord (index, record2); + } +} + +int CSMWorld::InfoCollection::getIndex (const std::string& id, const std::string& topic) const +{ + std::string fullId = Misc::StringUtils::lowerCase (topic) + "#" + id; + + std::pair range = getTopicRange (topic); + + for (; range.first!=range.second; ++range.first) + if (Misc::StringUtils::lowerCase (range.first->get().mId)==fullId) + return std::distance (getRecords().begin(), range.first); + + return -1; +} + +int CSMWorld::InfoCollection::getAppendIndex (const std::string& id, UniversalId::Type type) const +{ + std::string::size_type separator = id.find_last_of ('#'); + + if (separator==std::string::npos) + throw std::runtime_error ("invalid info ID: " + id); + + std::pair range = getTopicRange (id.substr (0, separator)); + + if (range.first==range.second) + return Collection >::getAppendIndex (id, type); + + return std::distance (getRecords().begin(), range.second); +} + +bool CSMWorld::InfoCollection::reorderRows (int baseIndex, const std::vector& newOrder) +{ + // check if the range is valid + int lastIndex = baseIndex + newOrder.size() -1; + + if (lastIndex>=getSize()) + return false; + + // Check that topics match + if (getRecord (baseIndex).get().mTopicId!=getRecord (lastIndex).get().mTopicId) + return false; + + // reorder + return reorderRowsImp (baseIndex, newOrder); +} + +void CSMWorld::InfoCollection::load (ESM::ESMReader& reader, bool base, const ESM::Dialogue& dialogue) +{ + std::string id = Misc::StringUtils::lowerCase (dialogue.mId) + "#" + + reader.getHNOString ("INAM"); + + if (reader.isNextSub ("DELE")) + { + int index = searchId (id); + + reader.skipRecord(); + + if (index==-1) + { + // deleting a record that does not exist + + // ignore it for now + + /// \todo report the problem to the user + } + else if (base) + { + removeRows (index, 1); + } + else + { + Record record = getRecord (index); + record.mState = RecordBase::State_Deleted; + setRecord (index, record); + } + } + else + { + Info record; + record.mTopicId = dialogue.mId; + record.mId = id; + record.load (reader); + + load (record, base); + } +} + +CSMWorld::InfoCollection::Range CSMWorld::InfoCollection::getTopicRange (const std::string& topic) + const +{ + std::string topic2 = Misc::StringUtils::lowerCase (topic); + + std::map::const_iterator iter = getIdMap().lower_bound (topic2); + + // Skip invalid records: The beginning of a topic string could be identical to another topic + // string. + for (; iter!=getIdMap().end(); ++iter) + { + std::string testTopicId = + Misc::StringUtils::lowerCase (getRecord (iter->second).get().mTopicId); + + if (testTopicId==topic2) + break; + + std::size_t size = topic2.size(); + + if (testTopicId.size()second; + + // Find end + RecordConstIterator end = begin; + + for (; end!=getRecords().end(); ++end) + if (Misc::StringUtils::lowerCase (end->get().mTopicId)!=topic2) + break; + + return Range (begin, end); +} \ No newline at end of file diff --git a/apps/opencs/model/world/infocollection.hpp b/apps/opencs/model/world/infocollection.hpp new file mode 100644 index 000000000..ae61f5d39 --- /dev/null +++ b/apps/opencs/model/world/infocollection.hpp @@ -0,0 +1,50 @@ +#ifndef CSM_WOLRD_INFOCOLLECTION_H +#define CSM_WOLRD_INFOCOLLECTION_H + +#include "collection.hpp" +#include "info.hpp" + +namespace ESM +{ + class Dialogue; +} + +namespace CSMWorld +{ + class InfoCollection : public Collection > + { + public: + + typedef std::vector >::const_iterator RecordConstIterator; + typedef std::pair Range; + + private: + + void load (const Info& record, bool base); + + int getIndex (const std::string& id, const std::string& topic) const; + ///< Return index for record \a id or -1 (if not present; deleted records are considered) + /// + /// \param id info ID without topic prefix + + public: + + virtual int getAppendIndex (const std::string& id, + UniversalId::Type type = UniversalId::Type_None) const; + ///< \param type Will be ignored, unless the collection supports multiple record types + + virtual bool reorderRows (int baseIndex, const std::vector& newOrder); + ///< Reorder the rows [baseIndex, baseIndex+newOrder.size()) according to the indices + /// given in \a newOrder (baseIndex+newOrder[0] specifies the new index of row baseIndex). + /// + /// \return Success? + + void load (ESM::ESMReader& reader, bool base, const ESM::Dialogue& dialogue); + + Range getTopicRange (const std::string& topic) const; + ///< Return iterators that point to the beginning and past the end of the range for + /// the given topic. + }; +} + +#endif \ No newline at end of file diff --git a/apps/opencs/model/world/ref.cpp b/apps/opencs/model/world/ref.cpp index af363bafb..74f60419b 100644 --- a/apps/opencs/model/world/ref.cpp +++ b/apps/opencs/model/world/ref.cpp @@ -6,7 +6,7 @@ void CSMWorld::CellRef::load (ESM::ESMReader &esm, Cell& cell, const std::string& id) { mId = id; - mCellId = cell.mId; + mCell = cell.mId; if (!mDeleted) cell.addRef (mId); diff --git a/apps/opencs/model/world/ref.hpp b/apps/opencs/model/world/ref.hpp index 3d107d675..fcf016ee2 100644 --- a/apps/opencs/model/world/ref.hpp +++ b/apps/opencs/model/world/ref.hpp @@ -16,7 +16,7 @@ namespace CSMWorld struct CellRef : public ESM::CellRef { std::string mId; - std::string mCellId; + std::string mCell; void load (ESM::ESMReader &esm, Cell& cell, const std::string& id); ///< Load cell ref and register it with \a cell. diff --git a/apps/opencs/model/world/refidcollection.cpp b/apps/opencs/model/world/refidcollection.cpp index 3a6f70d31..86a542c5c 100644 --- a/apps/opencs/model/world/refidcollection.cpp +++ b/apps/opencs/model/world/refidcollection.cpp @@ -530,7 +530,7 @@ void CSMWorld::RefIdCollection::load (ESM::ESMReader& reader, bool base, Univers } } -int CSMWorld::RefIdCollection::getAppendIndex (UniversalId::Type type) const +int CSMWorld::RefIdCollection::getAppendIndex (const std::string& id, UniversalId::Type type) const { return mData.getAppendIndex (type); } @@ -540,7 +540,12 @@ std::vector CSMWorld::RefIdCollection::getIds (bool listDeleted) co return mData.getIds (listDeleted); } +bool CSMWorld::RefIdCollection::reorderRows (int baseIndex, const std::vector& newOrder) +{ + return false; +} + void CSMWorld::RefIdCollection::save (int index, ESM::ESMWriter& writer) const { mData.save (index, writer); -} \ No newline at end of file +} diff --git a/apps/opencs/model/world/refidcollection.hpp b/apps/opencs/model/world/refidcollection.hpp index a479735db..5ff4a70bf 100644 --- a/apps/opencs/model/world/refidcollection.hpp +++ b/apps/opencs/model/world/refidcollection.hpp @@ -92,7 +92,7 @@ namespace CSMWorld void load (ESM::ESMReader& reader, bool base, UniversalId::Type type); - virtual int getAppendIndex (UniversalId::Type type) const; + virtual int getAppendIndex (const std::string& id, UniversalId::Type type) const; ///< \param type Will be ignored, unless the collection supports multiple record types virtual std::vector getIds (bool listDeleted) const; @@ -100,6 +100,12 @@ namespace CSMWorld /// /// \param listDeleted include deleted record in the list + virtual bool reorderRows (int baseIndex, const std::vector& newOrder); + ///< Reorder the rows [baseIndex, baseIndex+newOrder.size()) according to the indices + /// given in \a newOrder (baseIndex+newOrder[0] specifies the new index of row baseIndex). + /// + /// \return Success? + void save (int index, ESM::ESMWriter& writer) const; }; } diff --git a/apps/opencs/model/world/universalid.cpp b/apps/opencs/model/world/universalid.cpp index 6201a3cda..e633f4f69 100644 --- a/apps/opencs/model/world/universalid.cpp +++ b/apps/opencs/model/world/universalid.cpp @@ -31,6 +31,8 @@ namespace { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Spells, "Spells", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Topics, "Topics", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Journals, "Journals", 0 }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_TopicInfos, "Topic Infos", 0 }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_JournalInfos, "Journal Infos", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Cells, "Cells", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Referenceables, "Referenceables", 0 }, @@ -58,6 +60,8 @@ namespace { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Spell, "Spell", ":./spell.png" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Topic, "Topic", 0 }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Journal, "Journal", 0 }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_TopicInfo, "TopicInfo", 0 }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_JournalInfo, "JournalInfo", 0 }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Cell, "Cell", ":./cell.png" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Referenceable, "Referenceables", 0 }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Activator, "Activator", ":./activator.png" }, diff --git a/apps/opencs/model/world/universalid.hpp b/apps/opencs/model/world/universalid.hpp index ffd99e572..0c17da03b 100644 --- a/apps/opencs/model/world/universalid.hpp +++ b/apps/opencs/model/world/universalid.hpp @@ -91,6 +91,10 @@ namespace CSMWorld Type_Topic, Type_Journals, Type_Journal, + Type_TopicInfos, + Type_TopicInfo, + Type_JournalInfos, + Type_JournalInfo, Type_Scene }; diff --git a/apps/opencs/view/doc/subviewfactoryimp.hpp b/apps/opencs/view/doc/subviewfactoryimp.hpp index 2c4158f85..059b24fd0 100644 --- a/apps/opencs/view/doc/subviewfactoryimp.hpp +++ b/apps/opencs/view/doc/subviewfactoryimp.hpp @@ -26,16 +26,25 @@ namespace CSVDoc template class SubViewFactoryWithCreator : public SubViewFactoryBase { + bool mSorting; + public: + SubViewFactoryWithCreator (bool sorting = true); + virtual CSVDoc::SubView *makeSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document); }; + template + SubViewFactoryWithCreator::SubViewFactoryWithCreator (bool sorting) + : mSorting (sorting) + {} + template CSVDoc::SubView *SubViewFactoryWithCreator::makeSubView ( const CSMWorld::UniversalId& id, CSMDoc::Document& document) { - return new SubViewT (id, document, CreatorFactoryT()); + return new SubViewT (id, document, CreatorFactoryT(), mSorting); } } diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index 5713449f2..533ca7f57 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -136,41 +136,54 @@ void CSVDoc::View::setupMechanicsMenu() connect (gmsts, SIGNAL (triggered()), this, SLOT (addGmstsSubView())); mechanics->addAction (gmsts); - QAction *skills = new QAction (tr ("Skills"), this); - connect (skills, SIGNAL (triggered()), this, SLOT (addSkillsSubView())); - mechanics->addAction (skills); - - QAction *classes = new QAction (tr ("Classes"), this); - connect (classes, SIGNAL (triggered()), this, SLOT (addClassesSubView())); - mechanics->addAction (classes); - - QAction *factions = new QAction (tr ("Factions"), this); - connect (factions, SIGNAL (triggered()), this, SLOT (addFactionsSubView())); - mechanics->addAction (factions); - - QAction *races = new QAction (tr ("Races"), this); - connect (races, SIGNAL (triggered()), this, SLOT (addRacesSubView())); - mechanics->addAction (races); - QAction *scripts = new QAction (tr ("Scripts"), this); connect (scripts, SIGNAL (triggered()), this, SLOT (addScriptsSubView())); mechanics->addAction (scripts); - QAction *birthsigns = new QAction (tr ("Birthsigns"), this); - connect (birthsigns, SIGNAL (triggered()), this, SLOT (addBirthsignsSubView())); - mechanics->addAction (birthsigns); - QAction *spells = new QAction (tr ("Spells"), this); connect (spells, SIGNAL (triggered()), this, SLOT (addSpellsSubView())); mechanics->addAction (spells); +} + +void CSVDoc::View::setupCharacterMenu() +{ + QMenu *characters = menuBar()->addMenu (tr ("Characters")); + + QAction *skills = new QAction (tr ("Skills"), this); + connect (skills, SIGNAL (triggered()), this, SLOT (addSkillsSubView())); + characters->addAction (skills); + + QAction *classes = new QAction (tr ("Classes"), this); + connect (classes, SIGNAL (triggered()), this, SLOT (addClassesSubView())); + characters->addAction (classes); + + QAction *factions = new QAction (tr ("Factions"), this); + connect (factions, SIGNAL (triggered()), this, SLOT (addFactionsSubView())); + characters->addAction (factions); + + QAction *races = new QAction (tr ("Races"), this); + connect (races, SIGNAL (triggered()), this, SLOT (addRacesSubView())); + characters->addAction (races); + + QAction *birthsigns = new QAction (tr ("Birthsigns"), this); + connect (birthsigns, SIGNAL (triggered()), this, SLOT (addBirthsignsSubView())); + characters->addAction (birthsigns); QAction *topics = new QAction (tr ("Topics"), this); connect (topics, SIGNAL (triggered()), this, SLOT (addTopicsSubView())); - mechanics->addAction (topics); + characters->addAction (topics); QAction *journals = new QAction (tr ("Journals"), this); connect (journals, SIGNAL (triggered()), this, SLOT (addJournalsSubView())); - mechanics->addAction (journals); + characters->addAction (journals); + + QAction *topicInfos = new QAction (tr ("Topic Infos"), this); + connect (topicInfos, SIGNAL (triggered()), this, SLOT (addTopicInfosSubView())); + characters->addAction (topicInfos); + + QAction *journalInfos = new QAction (tr ("Journal Infos"), this); + connect (journalInfos, SIGNAL (triggered()), this, SLOT (addJournalInfosSubView())); + characters->addAction (journalInfos); } void CSVDoc::View::setupAssetsMenu() @@ -189,6 +202,7 @@ void CSVDoc::View::setupUi() setupViewMenu(); setupWorldMenu(); setupMechanicsMenu(); + setupCharacterMenu(); setupAssetsMenu(); } @@ -430,6 +444,16 @@ void CSVDoc::View::addJournalsSubView() addSubView (CSMWorld::UniversalId::Type_Journals); } +void CSVDoc::View::addTopicInfosSubView() +{ + addSubView (CSMWorld::UniversalId::Type_TopicInfos); +} + +void CSVDoc::View::addJournalInfosSubView() +{ + addSubView (CSMWorld::UniversalId::Type_JournalInfos); +} + void CSVDoc::View::abortOperation (int type) { mDocument->abortOperation (type); diff --git a/apps/opencs/view/doc/view.hpp b/apps/opencs/view/doc/view.hpp index 2a31d9d80..13c15ec9b 100644 --- a/apps/opencs/view/doc/view.hpp +++ b/apps/opencs/view/doc/view.hpp @@ -63,6 +63,8 @@ namespace CSVDoc void setupMechanicsMenu(); + void setupCharacterMenu(); + void setupAssetsMenu(); void setupUi(); @@ -170,6 +172,10 @@ namespace CSVDoc void addJournalsSubView(); + void addTopicInfosSubView(); + + void addJournalInfosSubView(); + void toggleShowStatusBar (bool show); }; } diff --git a/apps/opencs/view/doc/viewmanager.cpp b/apps/opencs/view/doc/viewmanager.cpp index a4849795b..4a4dbc124 100644 --- a/apps/opencs/view/doc/viewmanager.cpp +++ b/apps/opencs/view/doc/viewmanager.cpp @@ -75,7 +75,9 @@ CSVDoc::ViewManager::ViewManager (CSMDoc::DocumentManager& documentManager) { CSMWorld::ColumnBase::Display_ClothingType, CSMWorld::Columns::ColumnId_ClothingType, false }, { CSMWorld::ColumnBase::Display_CreatureType, CSMWorld::Columns::ColumnId_CreatureType, false }, { CSMWorld::ColumnBase::Display_WeaponType, CSMWorld::Columns::ColumnId_WeaponType, false }, - { CSMWorld::ColumnBase::Display_DialogueType, CSMWorld::Columns::ColumnId_DialogueType, false } + { CSMWorld::ColumnBase::Display_DialogueType, CSMWorld::Columns::ColumnId_DialogueType, false }, + { CSMWorld::ColumnBase::Display_QuestStatusType, CSMWorld::Columns::ColumnId_QuestStatusType, false }, + { CSMWorld::ColumnBase::Display_Gender, CSMWorld::Columns::ColumnId_Gender, true } }; for (std::size_t i=0; isetContentsMargins (0, 0, 0, 0); mId = new QLineEdit; - mId->setValidator (new IdValidator (this)); + mId->setValidator (new IdValidator (relaxedIdRules, this)); mLayout->addWidget (mId, 1); mCreate = new QPushButton ("Create"); diff --git a/apps/opencs/view/world/genericcreator.hpp b/apps/opencs/view/world/genericcreator.hpp index 6752d8591..8dd2ca911 100644 --- a/apps/opencs/view/world/genericcreator.hpp +++ b/apps/opencs/view/world/genericcreator.hpp @@ -51,7 +51,7 @@ namespace CSVWorld public: GenericCreator (CSMWorld::Data& data, QUndoStack& undoStack, - const CSMWorld::UniversalId& id); + const CSMWorld::UniversalId& id, bool relaxedIdRules = false); virtual void setEditLock (bool locked); diff --git a/apps/opencs/view/world/idvalidator.cpp b/apps/opencs/view/world/idvalidator.cpp index cf6e5d77b..7c210daae 100644 --- a/apps/opencs/view/world/idvalidator.cpp +++ b/apps/opencs/view/world/idvalidator.cpp @@ -12,15 +12,25 @@ bool CSVWorld::IdValidator::isValid (const QChar& c, bool first) const return false; } -CSVWorld::IdValidator::IdValidator (QObject *parent) : QValidator (parent) {} +CSVWorld::IdValidator::IdValidator (bool relaxed, QObject *parent) +: QValidator (parent), mRelaxed (relaxed) +{} QValidator::State CSVWorld::IdValidator::validate (QString& input, int& pos) const { - bool first = true; - - for (QString::const_iterator iter (input.begin()); iter!=input.end(); ++iter, first = false) - if (!isValid (*iter, first)) + if (mRelaxed) + { + if (input.indexOf ('"')!=-1 || input.indexOf ("::")!=-1 || input.indexOf ("#")!=-1) return QValidator::Invalid; + } + else + { + bool first = true; + + for (QString::const_iterator iter (input.begin()); iter!=input.end(); ++iter, first = false) + if (!isValid (*iter, first)) + return QValidator::Invalid; + } return QValidator::Acceptable; } \ No newline at end of file diff --git a/apps/opencs/view/world/idvalidator.hpp b/apps/opencs/view/world/idvalidator.hpp index db0ecb27a..8ca162440 100644 --- a/apps/opencs/view/world/idvalidator.hpp +++ b/apps/opencs/view/world/idvalidator.hpp @@ -7,13 +7,16 @@ namespace CSVWorld { class IdValidator : public QValidator { + bool mRelaxed; + private: bool isValid (const QChar& c, bool first) const; public: - IdValidator (QObject *parent = 0); + IdValidator (bool relaxed = false, QObject *parent = 0); + ///< \param relaxed Relaxed rules for IDs that also functino as user visible text virtual State validate (QString& input, int& pos) const; diff --git a/apps/opencs/view/world/infocreator.cpp b/apps/opencs/view/world/infocreator.cpp new file mode 100644 index 000000000..f09222930 --- /dev/null +++ b/apps/opencs/view/world/infocreator.cpp @@ -0,0 +1,81 @@ + +#include "infocreator.hpp" + +#include + +#include +#include +#include + +#include + +#include "../../model/world/data.hpp" +#include "../../model/world/commands.hpp" +#include "../../model/world/columns.hpp" +#include "../../model/world/idtable.hpp" + +std::string CSVWorld::InfoCreator::getId() const +{ + std::string id = Misc::StringUtils::lowerCase (mTopic->text().toUtf8().constData()); + + std::string unique = QUuid::createUuid().toByteArray().data(); + + unique.erase (std::remove (unique.begin(), unique.end(), '-'), unique.end()); + + unique = unique.substr (1, unique.size()-2); + + return id + '#' + unique; +} + +void CSVWorld::InfoCreator::configureCreateCommand (CSMWorld::CreateCommand& command) const +{ + int index = + dynamic_cast (*getData().getTableModel (getCollectionId())). + findColumnIndex ( + getCollectionId().getType()==CSMWorld::UniversalId::Type_TopicInfos ? + CSMWorld::Columns::ColumnId_Topic : CSMWorld::Columns::ColumnId_Journal); + + command.addValue (index, mTopic->text()); +} + +CSVWorld::InfoCreator::InfoCreator (CSMWorld::Data& data, QUndoStack& undoStack, + const CSMWorld::UniversalId& id) +: GenericCreator (data, undoStack, id) +{ + QLabel *label = new QLabel ("Topic", this); + insertBeforeButtons (label, false); + + mTopic = new QLineEdit (this); + insertBeforeButtons (mTopic, true); + + setManualEditing (false); + + connect (mTopic, SIGNAL (textChanged (const QString&)), this, SLOT (topicChanged())); +} + +void CSVWorld::InfoCreator::reset() +{ + mTopic->setText (""); + GenericCreator::reset(); +} + +std::string CSVWorld::InfoCreator::getErrors() const +{ + // We ignore errors from GenericCreator here, because they can never happen in an InfoCreator. + std::string errors; + + std::string topic = mTopic->text().toUtf8().constData(); + + if ((getCollectionId().getType()==CSMWorld::UniversalId::Type_TopicInfos ? + getData().getTopics() : getData().getJournals()).searchId (topic)==-1) + { + errors += "Invalid Topic ID"; + } + + return errors; +} + +void CSVWorld::InfoCreator::topicChanged() +{ + update(); +} \ No newline at end of file diff --git a/apps/opencs/view/world/infocreator.hpp b/apps/opencs/view/world/infocreator.hpp new file mode 100644 index 000000000..e9cb7e596 --- /dev/null +++ b/apps/opencs/view/world/infocreator.hpp @@ -0,0 +1,42 @@ +#ifndef CSV_WORLD_INFOCREATOR_H +#define CSV_WORLD_INFOCREATOR_H + +#include "genericcreator.hpp" + +class QLineEdit; + +namespace CSMWorld +{ + class InfoCollection; +} + +namespace CSVWorld +{ + class InfoCreator : public GenericCreator + { + Q_OBJECT + + QLineEdit *mTopic; + + virtual std::string getId() const; + + virtual void configureCreateCommand (CSMWorld::CreateCommand& command) const; + + public: + + InfoCreator (CSMWorld::Data& data, QUndoStack& undoStack, + const CSMWorld::UniversalId& id); + + virtual void reset(); + + virtual std::string getErrors() const; + ///< Return formatted error descriptions for the current state of the creator. if an empty + /// string is returned, there is no error. + + private slots: + + void topicChanged(); + }; +} + +#endif diff --git a/apps/opencs/view/world/subviews.cpp b/apps/opencs/view/world/subviews.cpp index 3d98cf73c..48c32e171 100644 --- a/apps/opencs/view/world/subviews.cpp +++ b/apps/opencs/view/world/subviews.cpp @@ -15,6 +15,7 @@ #include "referencecreator.hpp" #include "scenesubview.hpp" #include "dialoguecreator.hpp" +#include "infocreator.hpp" void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) { @@ -60,6 +61,12 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) manager.add (CSMWorld::UniversalId::Type_Journal, new CSVDoc::SubViewFactoryWithCreator); + manager.add (CSMWorld::UniversalId::Type_TopicInfos, + new CSVDoc::SubViewFactoryWithCreator > (false)); + + manager.add (CSMWorld::UniversalId::Type_JournalInfos, + new CSVDoc::SubViewFactoryWithCreator > (false)); + // Subviews for editing/viewing individual records manager.add (CSMWorld::UniversalId::Type_Script, new CSVDoc::SubViewFactory); diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index a58eb873f..d4ccba4a7 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -12,6 +12,7 @@ #include "../../model/world/idtableproxymodel.hpp" #include "../../model/world/idtable.hpp" #include "../../model/world/record.hpp" +#include "../../model/world/columns.hpp" #include "recordstatusdelegate.hpp" #include "util.hpp" @@ -32,11 +33,43 @@ void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event) if (mCreateAction) menu.addAction (mCreateAction); - if (listRevertableSelectedIds().size()>0) - menu.addAction (mRevertAction); + /// \todo Reverting temporarily disabled on tables that support reordering, because + /// revert logic currently can not handle reordering. + if (mModel->getReordering()==CSMWorld::IdTable::Reordering_None) + if (listRevertableSelectedIds().size()>0) + menu.addAction (mRevertAction); if (listDeletableSelectedIds().size()>0) menu.addAction (mDeleteAction); + + if (mModel->getReordering()==CSMWorld::IdTable::Reordering_WithinTopic) + { + /// \todo allow reordering of multiple rows + if (selectedRows.size()==1) + { + int row =selectedRows.begin()->row(); + + int column = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Topic); + + if (column==-1) + column = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Journal); + + if (column!=-1) + { + if (row>0 && mProxyModel->data (mProxyModel->index (row, column))== + mProxyModel->data (mProxyModel->index (row-1, column))) + { + menu.addAction (mMoveUpAction); + } + + if (rowrowCount()-1 && mProxyModel->data (mProxyModel->index (row, column))== + mProxyModel->data (mProxyModel->index (row+1, column))) + { + menu.addAction (mMoveDownAction); + } + } + } + } } menu.exec (event->globalPos()); @@ -121,7 +154,7 @@ std::vector CSVWorld::Table::listDeletableSelectedIds() const } CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack, - bool createAndDelete) + bool createAndDelete, bool sorting) : mUndoStack (undoStack), mCreateAction (0), mEditLock (false), mRecordStatusDisplay (0) { mModel = &dynamic_cast (*data.getTableModel (id)); @@ -132,7 +165,7 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, Q setModel (mProxyModel); horizontalHeader()->setResizeMode (QHeaderView::Interactive); verticalHeader()->hide(); - setSortingEnabled (true); + setSortingEnabled (sorting); setSelectionBehavior (QAbstractItemView::SelectRows); setSelectionMode (QAbstractItemView::ExtendedSelection); @@ -176,6 +209,14 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, Q connect (mDeleteAction, SIGNAL (triggered()), this, SLOT (deleteRecord())); addAction (mDeleteAction); + mMoveUpAction = new QAction (tr ("Move Up"), this); + connect (mMoveUpAction, SIGNAL (triggered()), this, SLOT (moveUpRecord())); + addAction (mMoveUpAction); + + mMoveDownAction = new QAction (tr ("Move Down"), this); + connect (mMoveDownAction, SIGNAL (triggered()), this, SLOT (moveDownRecord())); + addAction (mMoveDownAction); + connect (mProxyModel, SIGNAL (rowsInserted (const QModelIndex&, int, int)), this, SLOT (tableSizeUpdate())); @@ -254,6 +295,64 @@ void CSVWorld::Table::editRecord() } } +void CSVWorld::Table::moveUpRecord() +{ + QModelIndexList selectedRows = selectionModel()->selectedRows(); + + if (selectedRows.size()==1) + { + int row2 =selectedRows.begin()->row(); + + if (row2>0) + { + int row = row2-1; + + row = mProxyModel->mapToSource (mProxyModel->index (row, 0)).row(); + row2 = mProxyModel->mapToSource (mProxyModel->index (row2, 0)).row(); + + if (row2<=row) + throw std::runtime_error ("Inconsistent row order"); + + std::vector newOrder (row2-row+1); + newOrder[0] = row2-row; + newOrder[row2-row] = 0; + for (int i=1; iselectedRows(); + + if (selectedRows.size()==1) + { + int row =selectedRows.begin()->row(); + + if (rowrowCount()-1) + { + int row2 = row+1; + + row = mProxyModel->mapToSource (mProxyModel->index (row, 0)).row(); + row2 = mProxyModel->mapToSource (mProxyModel->index (row2, 0)).row(); + + if (row2<=row) + throw std::runtime_error ("Inconsistent row order"); + + std::vector newOrder (row2-row+1); + newOrder[0] = row2-row; + newOrder[row2-row] = 0; + for (int i=1; icolumnCount(); diff --git a/apps/opencs/view/world/table.hpp b/apps/opencs/view/world/table.hpp index d93109056..889e2847a 100644 --- a/apps/opencs/view/world/table.hpp +++ b/apps/opencs/view/world/table.hpp @@ -34,6 +34,8 @@ namespace CSVWorld QAction *mCreateAction; QAction *mRevertAction; QAction *mDeleteAction; + QAction *mMoveUpAction; + QAction *mMoveDownAction; CSMWorld::IdTableProxyModel *mProxyModel; CSMWorld::IdTable *mModel; bool mEditLock; @@ -49,8 +51,9 @@ namespace CSVWorld public: - Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack, bool createAndDelete); + Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack, bool createAndDelete, bool sorting); ///< \param createAndDelete Allow creation and deletion of records. + /// \param sorting Allow changing order of rows in the view via column headers. void setEditLock (bool locked); @@ -79,6 +82,10 @@ namespace CSVWorld void editRecord(); + void moveUpRecord(); + + void moveDownRecord(); + public slots: void tableSizeUpdate(); diff --git a/apps/opencs/view/world/tablesubview.cpp b/apps/opencs/view/world/tablesubview.cpp index 1e05fbf51..55ded09de 100644 --- a/apps/opencs/view/world/tablesubview.cpp +++ b/apps/opencs/view/world/tablesubview.cpp @@ -12,7 +12,7 @@ #include "creator.hpp" CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document, - const CreatorFactoryBase& creatorFactory) + const CreatorFactoryBase& creatorFactory, bool sorting) : SubView (id) { QVBoxLayout *layout = new QVBoxLayout; @@ -23,7 +23,7 @@ CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::D new TableBottomBox (creatorFactory, document.getData(), document.getUndoStack(), id, this), 0); layout->insertWidget (0, mTable = - new Table (id, document.getData(), document.getUndoStack(), mBottom->canCreateAndDelete()), 2); + new Table (id, document.getData(), document.getUndoStack(), mBottom->canCreateAndDelete(), sorting), 2); CSVFilter::FilterBox *filterBox = new CSVFilter::FilterBox (document.getData(), this); diff --git a/apps/opencs/view/world/tablesubview.hpp b/apps/opencs/view/world/tablesubview.hpp index d61c78935..56a441a4d 100644 --- a/apps/opencs/view/world/tablesubview.hpp +++ b/apps/opencs/view/world/tablesubview.hpp @@ -26,7 +26,7 @@ namespace CSVWorld public: TableSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document, - const CreatorFactoryBase& creatorFactory); + const CreatorFactoryBase& creatorFactory, bool sorting); virtual void setEditLock (bool locked); diff --git a/apps/openmw/mwdialogue/filter.cpp b/apps/openmw/mwdialogue/filter.cpp index a7b0f1924..11dccde42 100644 --- a/apps/openmw/mwdialogue/filter.cpp +++ b/apps/openmw/mwdialogue/filter.cpp @@ -58,13 +58,13 @@ bool MWDialogue::Filter::testActor (const ESM::DialInfo& info) const } // NPC faction - if (!info.mNpcFaction.empty()) + if (!info.mFaction.empty()) { if (isCreature) return false; MWMechanics::NpcStats& stats = MWWorld::Class::get (mActor).getNpcStats (mActor); - std::map::iterator iter = stats.getFactionRanks().find ( Misc::StringUtils::lowerCase (info.mNpcFaction)); + std::map::iterator iter = stats.getFactionRanks().find ( Misc::StringUtils::lowerCase (info.mFaction)); if (iter==stats.getFactionRanks().end()) return false; diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 0ae633aa0..da7ce6557 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -253,6 +253,8 @@ namespace MWGui void InventoryWindow::open() { + mPtr = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + updateEncumbranceBar(); mItemView->update(); diff --git a/apps/openmw/mwworld/esmstore.cpp b/apps/openmw/mwworld/esmstore.cpp index ab8878717..f1bff11a2 100644 --- a/apps/openmw/mwworld/esmstore.cpp +++ b/apps/openmw/mwworld/esmstore.cpp @@ -70,8 +70,10 @@ void ESMStore::load(ESM::ESMReader &esm, Loading::Listener* listener) if (it == mStores.end()) { if (n.val == ESM::REC_INFO) { + std::string id = esm.getHNOString("INAM"); if (dialogue) { dialogue->mInfo.push_back(ESM::DialInfo()); + dialogue->mInfo.back().mId = id; dialogue->mInfo.back().load(esm); } else { std::cerr << "error: info record without dialog" << std::endl; diff --git a/apps/openmw/mwworld/store.hpp b/apps/openmw/mwworld/store.hpp index 90452f661..c25197319 100644 --- a/apps/openmw/mwworld/store.hpp +++ b/apps/openmw/mwworld/store.hpp @@ -258,7 +258,7 @@ namespace MWWorld typename std::vector::iterator sharedIter = mShared.begin(); typename std::vector::iterator end = sharedIter + mStatic.size(); - while (sharedIter != mShared.end() && sharedIter != end) { + while (sharedIter != mShared.end() && sharedIter != end) { if((*sharedIter)->mId == item.mId) { mShared.erase(sharedIter); break; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index dd29c33a9..bdd5d58c8 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2239,6 +2239,7 @@ namespace MWWorld void World::updateAnimParts(const Ptr& actor) { - mRendering->updateAnimParts(actor); + if (actor.mCell && actor.mCell == mWorldScene->getCurrentCell()) + mRendering->updateAnimParts(actor); } } diff --git a/components/esm/loadinfo.cpp b/components/esm/loadinfo.cpp index 4f248cc65..f86ad3b51 100644 --- a/components/esm/loadinfo.cpp +++ b/components/esm/loadinfo.cpp @@ -10,7 +10,6 @@ namespace ESM void DialInfo::load(ESMReader &esm) { - mId = esm.getHNString("INAM"); mPrev = esm.getHNString("PNAM"); mNext = esm.getHNString("NNAM"); @@ -50,8 +49,8 @@ void DialInfo::load(ESMReader &esm) mFactionLess = false; if (subName.val == REC_FNAM) { - mNpcFaction = esm.getHString(); - if (mNpcFaction == "FFFF") + mFaction = esm.getHString(); + if (mFaction == "FFFF") mFactionLess = true; if (esm.isEmptyOrGetName()) return; @@ -124,14 +123,13 @@ void DialInfo::load(ESMReader &esm) void DialInfo::save(ESMWriter &esm) const { - esm.writeHNCString("INAM", mId); esm.writeHNCString("PNAM", mPrev); esm.writeHNCString("NNAM", mNext); esm.writeHNT("DATA", mData, 12); esm.writeHNOCString("ONAM", mActor); esm.writeHNOCString("RNAM", mRace); esm.writeHNOCString("CNAM", mClass); - esm.writeHNOCString("FNAM", mNpcFaction); + esm.writeHNOCString("FNAM", mFaction); esm.writeHNOCString("ANAM", mCell); esm.writeHNOCString("DNAM", mPcFaction); esm.writeHNOCString("SNAM", mSound); @@ -155,4 +153,28 @@ void DialInfo::save(ESMWriter &esm) const } } + void DialInfo::blank() + { + mData.mUnknown1 = 0; + mData.mDisposition = 0; + mData.mRank = 0; + mData.mGender = 0; + mData.mPCrank = 0; + mData.mUnknown2 = 0; + + mSelects.clear(); + mPrev.clear(); + mNext.clear(); + mActor.clear(); + mRace.clear(); + mClass.clear(); + mFaction.clear(); + mPcFaction.clear(); + mCell.clear(); + mSound.clear(); + mResponse.clear(); + mResultScript.clear(); + mFactionLess = false; + mQuestStatus = QS_None; + } } diff --git a/components/esm/loadinfo.hpp b/components/esm/loadinfo.hpp index 2589ea7b8..737494f6c 100644 --- a/components/esm/loadinfo.hpp +++ b/components/esm/loadinfo.hpp @@ -50,10 +50,10 @@ struct DialInfo // Journal quest indices (introduced with the quest system in Tribunal) enum QuestStatus { - QS_None, - QS_Name, - QS_Finished, - QS_Restart, + QS_None = 0, + QS_Name = 1, + QS_Finished = 2, + QS_Restart = 3, QS_Deleted }; @@ -65,7 +65,7 @@ struct DialInfo std::string mId, mPrev, mNext; // Various references used in determining when to select this item. - std::string mActor, mRace, mClass, mNpcFaction, mPcFaction, mCell; + std::string mActor, mRace, mClass, mFaction, mPcFaction, mCell; // Sound and text associated with this item std::string mSound, mResponse; @@ -102,6 +102,9 @@ struct DialInfo void load(ESMReader &esm); void save(ESMWriter &esm) const; + + void blank(); + ///< Set record to default state (does not touch the ID). }; }