diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index 0bcaff6a5..c70b3dd19 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -159,15 +159,23 @@ std::pair > CS::Editor::readConfi } dataDirs.insert (dataDirs.end(), dataLocal.begin(), dataLocal.end()); + Files::PathContainer canonicalPaths; //iterate the data directories and add them to the file dialog for loading for (Files::PathContainer::const_iterator iter = dataDirs.begin(); iter != dataDirs.end(); ++iter) { + boost::filesystem::path p = boost::filesystem::canonical(*iter); + Files::PathContainer::iterator it = std::find(canonicalPaths.begin(), canonicalPaths.end(), p); + if (it == canonicalPaths.end()) + canonicalPaths.push_back(p); + else + continue; + QString path = QString::fromUtf8 (iter->string().c_str()); mFileDialog.addFiles(path); } - return std::make_pair (dataDirs, variables["fallback-archive"].as >()); + return std::make_pair (canonicalPaths, variables["fallback-archive"].as >()); } void CS::Editor::createGame() diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index c7e676361..7b3e1fe4f 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -552,7 +552,15 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc mMetaData.addColumn (new FormatColumn); mMetaData.addColumn (new AuthorColumn); mMetaData.addColumn (new FileDescriptionColumn); - + + mLandTextures.addColumn (new StringIdColumn); + mLandTextures.addColumn (new RecordStateColumn); + mLandTextures.addColumn (new FixedRecordTypeColumn (UniversalId::Type_LandTexture)); + + mLand.addColumn (new StringIdColumn); + mLand.addColumn (new RecordStateColumn); + mLand.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Land)); + addModel (new IdTable (&mGlobals), UniversalId::Type_Global); addModel (new IdTable (&mGmsts), UniversalId::Type_Gmst); addModel (new IdTable (&mSkills), UniversalId::Type_Skill); @@ -594,6 +602,8 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc addModel (new ResourceTable (&mResourcesManager.get (UniversalId::Type_Videos)), UniversalId::Type_Video); addModel (new IdTable (&mMetaData), UniversalId::Type_MetaData); + addModel (new IdTable (&mLand), UniversalId::Type_Land); + addModel (new IdTable (&mLandTextures), UniversalId::Type_LandTexture); // for autocalc updates when gmst/race/class/skils tables change CSMWorld::IdTable *gmsts = @@ -968,7 +978,7 @@ int CSMWorld::Data::startLoading (const boost::filesystem::path& path, bool base mMetaData.setRecord (0, Record (RecordBase::State_ModifiedOnly, 0, &metaData)); } - + return mReader->getRecordCount(); } @@ -1419,8 +1429,14 @@ CSMWorld::NpcStats* CSMWorld::Data::npcAutoCalculate(const ESM::NPC& npc) const if (cachedStats) return cachedStats; - const ESM::Race *race = &mRaces.getRecord(npc.mRace).get(); - const ESM::Class *class_ = &mClasses.getRecord(npc.mClass).get(); + int raceIndex = mRaces.searchId(npc.mRace); + int classIndex = mClasses.searchId(npc.mClass); + // this can happen when creating a new game from scratch + if (raceIndex == -1 || classIndex == -1) + return 0; + + const ESM::Race *race = &mRaces.getRecord(raceIndex).get(); + const ESM::Class *class_ = &mClasses.getRecord(classIndex).get(); bool autoCalc = npc.mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS; short level = npc.mNpdt52.mLevel; diff --git a/apps/opencs/model/world/refidadapterimp.cpp b/apps/opencs/model/world/refidadapterimp.cpp index 2bb980bfe..16579c2c7 100644 --- a/apps/opencs/model/world/refidadapterimp.cpp +++ b/apps/opencs/model/world/refidadapterimp.cpp @@ -638,6 +638,11 @@ void CSMWorld::NpcRefIdAdapter::setData (const RefIdColumn *column, RefIdData& d if (npc.mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS) { CSMWorld::NpcStats *stats = mData.npcAutoCalculate(npc); + if (!stats) + { + record.setModified (npc); + return; + } // update npc npc.mNpdtType = ESM::NPC::NPC_DEFAULT; @@ -755,6 +760,8 @@ QVariant CSMWorld::NpcAttributesRefIdAdapter::getNestedData (const RefIdColumn * if (npc.mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS) { CSMWorld::NpcStats *stats = mData.npcAutoCalculate(npc); + if (!stats) + return QVariant(); switch (subRowIndex) { @@ -885,6 +892,9 @@ QVariant CSMWorld::NpcSkillsRefIdAdapter::getNestedData (const RefIdColumn *colu if (npc.mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS) { CSMWorld::NpcStats *stats = mData.npcAutoCalculate(npc); + if (!stats) + return QVariant(); + return static_cast(stats->getBaseSkill(subRowIndex)); } else @@ -981,17 +991,23 @@ QVariant CSMWorld::NpcMiscRefIdAdapter::getNestedData (const RefIdColumn *column } case 2: { - UserInt i(stats->getHealth()); + UserInt i(0); + if (stats) + i = UserInt(stats->getHealth()); return QVariant(QVariant::fromValue(i)); } case 3: { - UserInt i(stats->getMana()); + UserInt i(0); + if (stats) + i = UserInt(stats->getMana()); return QVariant(QVariant::fromValue(i)); } case 4: { - UserInt i(stats->getFatigue()); + UserInt i(0); + if (stats) + i = UserInt(stats->getFatigue()); return QVariant(QVariant::fromValue(i)); } case 5: return static_cast(record.get().mNpdt12.mDisposition); @@ -1164,6 +1180,54 @@ void CSMWorld::WeaponRefIdAdapter::setData (const RefIdColumn *column, RefIdData namespace CSMWorld { +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.mHasAi) + return record.get().mHasAI!=0; + + if (column==mActors.mHello) + return record.get().mAiData.mHello; + + if (column==mActors.mFlee) + return record.get().mAiData.mFlee; + + if (column==mActors.mFight) + return record.get().mAiData.mFight; + + if (column==mActors.mAlarm) + return record.get().mAiData.mAlarm; + + if (column==mActors.mInventory) + return true; // to show nested tables in dialogue subview, see IdTree::hasChildren() + + if (column==mActors.mSpells) + { + if ((record.get().mFlags & ESM::NPC::Autocalc) != 0) + return QVariant(QVariant::UserType); + else + return true; + } + + if (column==mActors.mDestinations) + return true; // to show nested tables in dialogue subview, see IdTree::hasChildren() + + if (column==mActors.mAiPackages) + return true; // to show nested tables in dialogue subview, see IdTree::hasChildren() + + std::map::const_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 NestedSpellRefIdAdapter::addNestedRow (const RefIdColumn *column, RefIdData& data, int index, int position) const @@ -1259,7 +1323,11 @@ QVariant NestedSpellRefIdAdapter::getNestedData (const RefIdColumn *co const Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); - const std::vector& spells = mData.npcAutoCalculate(record.get())->spells(); + CSMWorld::NpcStats *stats = mData.npcAutoCalculate(record.get()); + if (!stats) + return QVariant(); + + const std::vector& spells = stats->spells(); if (subRowIndex < 0 || subRowIndex >= static_cast (spells.size())) throw std::runtime_error ("index out of range"); @@ -1289,8 +1357,54 @@ int NestedSpellRefIdAdapter::getNestedRowsCount(const RefIdColumn *col const Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); - const std::vector spells = mData.npcAutoCalculate(record.get())->spells(); - return static_cast(spells.size()); + CSMWorld::NpcStats *stats = mData.npcAutoCalculate(record.get()); + if (!stats) + return 0; + + return static_cast(stats->spells().size()); +} + +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.mHasAi) + return record.get().mHasAI!=0; + + if (column==mActors.mHello) + return record.get().mAiData.mHello; + + if (column==mActors.mFlee) + return record.get().mAiData.mFlee; + + if (column==mActors.mFight) + return record.get().mAiData.mFight; + + if (column==mActors.mAlarm) + return record.get().mAiData.mAlarm; + + if (column==mActors.mInventory) + return true; // to show nested tables in dialogue subview, see IdTree::hasChildren() + + if (column==mActors.mSpells) + return true; // to show nested tables in dialogue subview, see IdTree::hasChildren() + + if (column==mActors.mDestinations) + return true; // to show nested tables in dialogue subview, see IdTree::hasChildren() + + if (column==mActors.mAiPackages) + return true; // to show nested tables in dialogue subview, see IdTree::hasChildren() + + std::map::const_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 <> diff --git a/apps/opencs/model/world/refidadapterimp.hpp b/apps/opencs/model/world/refidadapterimp.hpp index 4a8288fa4..9fc296231 100644 --- a/apps/opencs/model/world/refidadapterimp.hpp +++ b/apps/opencs/model/world/refidadapterimp.hpp @@ -523,49 +523,6 @@ namespace CSMWorld : 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.mHasAi) - return record.get().mHasAI!=0; - - if (column==mActors.mHello) - return record.get().mAiData.mHello; - - if (column==mActors.mFlee) - return record.get().mAiData.mFlee; - - if (column==mActors.mFight) - return record.get().mAiData.mFight; - - if (column==mActors.mAlarm) - return record.get().mAiData.mAlarm; - - if (column==mActors.mInventory) - return true; // to show nested tables in dialogue subview, see IdTree::hasChildren() - - if (column==mActors.mSpells) - return true; // to show nested tables in dialogue subview, see IdTree::hasChildren() - - if (column==mActors.mDestinations) - return true; // to show nested tables in dialogue subview, see IdTree::hasChildren() - - if (column==mActors.mAiPackages) - return true; // to show nested tables in dialogue subview, see IdTree::hasChildren() - - std::map::const_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 diff --git a/apps/opencs/model/world/universalid.cpp b/apps/opencs/model/world/universalid.cpp index 73d893a26..8ad9873fc 100644 --- a/apps/opencs/model/world/universalid.cpp +++ b/apps/opencs/model/world/universalid.cpp @@ -57,6 +57,8 @@ namespace { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Pathgrids, "Pathgrids", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_StartScripts, "Start Scripts", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_MetaDatas, "Meta Data Table", 0 }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_LandTextures, "Land Texture Table", 0 }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Lands, "Land Table", 0 }, { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0, 0 } // end marker }; @@ -122,6 +124,8 @@ namespace { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Pathgrid, "Pathgrid", 0 }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_StartScript, "Start Script", 0 }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_MetaData, "Meta Data", 0 }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_LandTexture, "Land Texture", 0 }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Land, "Land", 0 }, { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0, 0 } // end marker }; @@ -365,8 +369,8 @@ std::vector CSMWorld::UniversalId::listTypes (int c for (int i=0; sIndexArg[i].mName; ++i) if (sIndexArg[i].mClass & classes) list.push_back (sIndexArg[i].mType); - - return list; + + return list; } CSMWorld::UniversalId::Type CSMWorld::UniversalId::getParentType (Type type) diff --git a/apps/opencs/model/world/universalid.hpp b/apps/opencs/model/world/universalid.hpp index e9104fc22..a05843597 100644 --- a/apps/opencs/model/world/universalid.hpp +++ b/apps/opencs/model/world/universalid.hpp @@ -133,6 +133,10 @@ namespace CSMWorld Type_Search, Type_MetaDatas, Type_MetaData, + Type_LandTextures, + Type_LandTexture, + Type_Lands, + Type_Land, Type_RunLog }; diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index 78203666d..173a78e9d 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -169,6 +169,10 @@ void CSVDoc::View::setupWorldMenu() connect (grid, SIGNAL (triggered()), this, SLOT (addPathgridSubView())); world->addAction (grid); + QAction *land = new QAction (tr ("Lands"), this); + connect (land, SIGNAL (triggered()), this, SLOT (addLandSubView())); + world->addAction (land); + world->addSeparator(); // items that don't represent single record lists follow here QAction *regionMap = new QAction (tr ("Region Map"), this); @@ -288,6 +292,10 @@ void CSVDoc::View::setupAssetsMenu() connect (textures, SIGNAL (triggered()), this, SLOT (addTexturesSubView())); assets->addAction (textures); + QAction *land = new QAction (tr ("Land Textures"), this); + connect (land, SIGNAL (triggered()), this, SLOT (addLandTextureSubView())); + assets->addAction (land); + QAction *videos = new QAction (tr ("Videos"), this); connect (videos, SIGNAL (triggered()), this, SLOT (addVideosSubView())); assets->addAction (videos); @@ -838,6 +846,16 @@ void CSVDoc::View::addPathgridSubView() addSubView (CSMWorld::UniversalId::Type_Pathgrids); } +void CSVDoc::View::addLandTextureSubView() +{ + addSubView (CSMWorld::UniversalId::Type_LandTextures); +} + +void CSVDoc::View::addLandSubView() +{ + addSubView (CSMWorld::UniversalId::Type_Lands); +} + void CSVDoc::View::addStartScriptsSubView() { addSubView (CSMWorld::UniversalId::Type_StartScripts); diff --git a/apps/opencs/view/doc/view.hpp b/apps/opencs/view/doc/view.hpp index dc8e14fb5..7f465c8f3 100644 --- a/apps/opencs/view/doc/view.hpp +++ b/apps/opencs/view/doc/view.hpp @@ -228,6 +228,10 @@ namespace CSVDoc void addPathgridSubView(); + void addLandTextureSubView(); + + void addLandSubView(); + void addStartScriptsSubView(); void addSearchSubView(); diff --git a/apps/opencs/view/world/nestedtable.cpp b/apps/opencs/view/world/nestedtable.cpp index de3b3aa16..89b171891 100644 --- a/apps/opencs/view/world/nestedtable.cpp +++ b/apps/opencs/view/world/nestedtable.cpp @@ -22,6 +22,8 @@ CSVWorld::NestedTable::NestedTable(CSMDoc::Document& document, mEditIdAction(0), mModel(model) { + mDispatcher = new CSMWorld::CommandDispatcher (document, id, this); + setSelectionBehavior (QAbstractItemView::SelectRows); setSelectionMode (QAbstractItemView::ExtendedSelection); @@ -34,26 +36,23 @@ CSVWorld::NestedTable::NestedTable(CSMDoc::Document& document, int columns = model->columnCount(QModelIndex()); - setModel(model); + for(int i = 0 ; i < columns; ++i) + { + CSMWorld::ColumnBase::Display display = static_cast ( + model->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); - setAcceptDrops(true); + CommandDelegate *delegate = CommandDelegateFactoryCollection::get().makeDelegate(display, + mDispatcher, + document, + this); + + setItemDelegateForColumn(i, delegate); + } + + setModel(model); if (editable) { - mDispatcher = new CSMWorld::CommandDispatcher (document, id, this); - for(int i = 0 ; i < columns; ++i) - { - CSMWorld::ColumnBase::Display display = static_cast ( - model->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); - - CommandDelegate *delegate = CommandDelegateFactoryCollection::get().makeDelegate(display, - mDispatcher, - document, - this); - - setItemDelegateForColumn(i, delegate); - } - mAddNewRowAction = new QAction (tr ("Add new row"), this); connect(mAddNewRowAction, SIGNAL(triggered()), diff --git a/apps/opencs/view/world/subviews.cpp b/apps/opencs/view/world/subviews.cpp index b8a6ba429..73000af13 100644 --- a/apps/opencs/view/world/subviews.cpp +++ b/apps/opencs/view/world/subviews.cpp @@ -43,6 +43,8 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) CSMWorld::UniversalId::Type_BodyParts, CSMWorld::UniversalId::Type_SoundGens, CSMWorld::UniversalId::Type_Pathgrids, + CSMWorld::UniversalId::Type_LandTextures, + CSMWorld::UniversalId::Type_Lands, CSMWorld::UniversalId::Type_StartScripts, CSMWorld::UniversalId::Type_None // end marker @@ -172,7 +174,7 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) manager.add (CSMWorld::UniversalId::Type_MetaData, new CSVDoc::SubViewFactory); - + //preview manager.add (CSMWorld::UniversalId::Type_Preview, new CSVDoc::SubViewFactory); } diff --git a/components/config/gamesettings.cpp b/components/config/gamesettings.cpp index ca6bfd80d..10b0234d1 100644 --- a/components/config/gamesettings.cpp +++ b/components/config/gamesettings.cpp @@ -291,7 +291,8 @@ bool Config::GameSettings::writeFileWithComments(QFile &file) if (!comments.empty() && index != -1 && settingRegex.captureCount() >= 2 && mUserSettings.find(settingRegex.cap(1)) != mUserSettings.end()) { - for (std::vector::const_iterator it = comments.begin(); it != comments.end(); ++it) + for (std::vector::const_iterator it = comments.begin(); + it != comments.end() && commentStart != fileCopy.end(); ++it) { *commentStart = *it; ++commentStart; diff --git a/components/esm/loadscpt.cpp b/components/esm/loadscpt.cpp index 0c2bdd42f..60b4a3304 100644 --- a/components/esm/loadscpt.cpp +++ b/components/esm/loadscpt.cpp @@ -77,8 +77,13 @@ namespace ESM break; case ESM::FourCC<'S','C','D','T'>::value: // compiled script - mScriptData.resize(mData.mScriptDataSize); - esm.getHExact(&mScriptData[0], mScriptData.size()); + if (mData.mScriptDataSize) + { + mScriptData.resize(mData.mScriptDataSize); + esm.getHExact(&mScriptData[0], mScriptData.size()); + } + else + esm.skipHSub(); break; case ESM::FourCC<'S','C','T','X'>::value: mScriptText = esm.getHString(); diff --git a/components/files/configurationmanager.cpp b/components/files/configurationmanager.cpp index dc6f02b60..ac461697a 100644 --- a/components/files/configurationmanager.cpp +++ b/components/files/configurationmanager.cpp @@ -57,14 +57,24 @@ void ConfigurationManager::readConfiguration(boost::program_options::variables_m bool silent = mSilent; mSilent = quiet; + boost::filesystem::path pUser = boost::filesystem::canonical(mFixedPath.getUserConfigPath()); + boost::filesystem::path pLocal = boost::filesystem::canonical(mFixedPath.getLocalPath()); + boost::filesystem::path pGlobal = boost::filesystem::canonical(mFixedPath.getGlobalConfigPath()); + loadConfig(mFixedPath.getUserConfigPath(), variables, description); boost::program_options::notify(variables); - loadConfig(mFixedPath.getLocalPath(), variables, description); - boost::program_options::notify(variables); - loadConfig(mFixedPath.getGlobalConfigPath(), variables, description); - boost::program_options::notify(variables); + if (pLocal != pUser) + { + loadConfig(mFixedPath.getLocalPath(), variables, description); + boost::program_options::notify(variables); + } + if (pGlobal != pUser && pGlobal != pLocal) + { + loadConfig(mFixedPath.getGlobalConfigPath(), variables, description); + boost::program_options::notify(variables); + } mSilent = silent; }