diff --git a/CMakeLists.txt b/CMakeLists.txt index d13437214..487d07aa5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -131,8 +131,10 @@ endif() # Platform specific if (WIN32) + if(NOT MINGW) set(Boost_USE_STATIC_LIBS ON) add_definitions(-DBOOST_ALL_NO_LIB) + endif(NOT MINGW) # Suppress WinMain(), provided by SDL add_definitions(-DSDL_MAIN_HANDLED) diff --git a/apps/mwiniimporter/CMakeLists.txt b/apps/mwiniimporter/CMakeLists.txt index 790d47dc4..753c86fef 100644 --- a/apps/mwiniimporter/CMakeLists.txt +++ b/apps/mwiniimporter/CMakeLists.txt @@ -18,6 +18,10 @@ target_link_libraries(openmw-iniimporter components ) +if (MINGW) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -municode") +endif() + if (BUILD_WITH_CODE_COVERAGE) add_definitions (--coverage) target_link_libraries(openmw-iniimporter gcov) diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index ddcfba254..6fec6f194 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -40,7 +40,7 @@ opencs_units (model/tools opencs_units_noqt (model/tools mandatoryid skillcheck classcheck factioncheck racecheck soundcheck regioncheck birthsigncheck spellcheck referencecheck referenceablecheck scriptcheck bodypartcheck - startscriptcheck search searchoperation searchstage + startscriptcheck search searchoperation searchstage pathgridcheck ) @@ -63,6 +63,7 @@ opencs_units (view/world table tablesubview scriptsubview util regionmapsubview tablebottombox creator genericcreator cellcreator referenceablecreator referencecreator scenesubview infocreator scriptedit dialoguesubview previewsubview regionmap dragrecordtable nestedtable + dialoguespinbox ) opencs_units_noqt (view/world diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index 0fcbcf678..d6aecd7fb 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -58,9 +58,11 @@ CS::Editor::Editor () connect (&mFileDialog, SIGNAL(signalCreateNewFile (const boost::filesystem::path&)), this, SLOT(createNewFile (const boost::filesystem::path&))); + connect (&mFileDialog, SIGNAL (rejected()), this, SLOT (cancelFileDialog ())); connect (&mNewGame, SIGNAL (createRequest (const boost::filesystem::path&)), this, SLOT (createNewGame (const boost::filesystem::path&))); + connect (&mNewGame, SIGNAL (cancelCreateGame()), this, SLOT (cancelCreateGame ())); } CS::Editor::~Editor () @@ -164,12 +166,40 @@ void CS::Editor::createGame() mNewGame.activateWindow(); } +void CS::Editor::cancelCreateGame() +{ + if (!mDocumentManager.isEmpty()) + return; + + mNewGame.hide(); + + if (mStartup.isHidden()) + mStartup.show(); + + mStartup.raise(); + mStartup.activateWindow(); +} + void CS::Editor::createAddon() { mStartup.hide(); mFileDialog.showDialog (CSVDoc::ContentAction_New); } +void CS::Editor::cancelFileDialog() +{ + if (!mDocumentManager.isEmpty()) + return; + + mFileDialog.hide(); + + if (mStartup.isHidden()) + mStartup.show(); + + mStartup.raise(); + mStartup.activateWindow(); +} + void CS::Editor::loadDocument() { mStartup.hide(); diff --git a/apps/opencs/editor.hpp b/apps/opencs/editor.hpp index e973b9c55..59998e891 100644 --- a/apps/opencs/editor.hpp +++ b/apps/opencs/editor.hpp @@ -80,6 +80,8 @@ namespace CS void createGame(); void createAddon(); + void cancelCreateGame(); + void cancelFileDialog(); void loadDocument(); void openFiles (const boost::filesystem::path &path); diff --git a/apps/opencs/model/doc/documentmanager.cpp b/apps/opencs/model/doc/documentmanager.cpp index f617cbe22..5a5f50159 100644 --- a/apps/opencs/model/doc/documentmanager.cpp +++ b/apps/opencs/model/doc/documentmanager.cpp @@ -49,6 +49,11 @@ CSMDoc::DocumentManager::~DocumentManager() delete *iter; } +bool CSMDoc::DocumentManager::isEmpty() +{ + return mDocuments.empty(); +} + void CSMDoc::DocumentManager::addDocument (const std::vector& files, const boost::filesystem::path& savePath, bool new_) { diff --git a/apps/opencs/model/doc/documentmanager.hpp b/apps/opencs/model/doc/documentmanager.hpp index 0c6e1541d..b61656d4b 100644 --- a/apps/opencs/model/doc/documentmanager.hpp +++ b/apps/opencs/model/doc/documentmanager.hpp @@ -64,6 +64,8 @@ namespace CSMDoc void setVFS(const VFS::Manager* vfs); + bool isEmpty(); + private: boost::filesystem::path mResDir; diff --git a/apps/opencs/model/settings/usersettings.cpp b/apps/opencs/model/settings/usersettings.cpp index 311c1cdc1..e8d90853c 100644 --- a/apps/opencs/model/settings/usersettings.cpp +++ b/apps/opencs/model/settings/usersettings.cpp @@ -245,6 +245,42 @@ void CSMSettings::UserSettings::buildSettingModelDefaults() Setting *monoFont = createSetting (Type_CheckBox, "mono-font", "Use monospace font"); monoFont->setDefaultValue ("true"); monoFont->setToolTip ("Whether to use monospaced fonts on script edit subview."); + + QString tooltip = + "\n#RGB (each of R, G, and B is a single hex digit)" + "\n#RRGGBB" + "\n#RRRGGGBBB" + "\n#RRRRGGGGBBBB" + "\nA name from the list of colors defined in the list of SVG color keyword names." + "\nX11 color names may also work."; + + Setting *formatInt = createSetting (Type_LineEdit, "colour-int", "Highlight Colour: Int"); + formatInt->setDefaultValues (QStringList() << "Dark magenta"); + formatInt->setToolTip ("(Default: Green) Use one of the following formats:" + tooltip); + + Setting *formatFloat = createSetting (Type_LineEdit, "colour-float", "Highlight Colour: Float"); + formatFloat->setDefaultValues (QStringList() << "Magenta"); + formatFloat->setToolTip ("(Default: Magenta) Use one of the following formats:" + tooltip); + + Setting *formatName = createSetting (Type_LineEdit, "colour-name", "Highlight Colour: Name"); + formatName->setDefaultValues (QStringList() << "Gray"); + formatName->setToolTip ("(Default: Gray) Use one of the following formats:" + tooltip); + + Setting *formatKeyword = createSetting (Type_LineEdit, "colour-keyword", "Highlight Colour: Keyword"); + formatKeyword->setDefaultValues (QStringList() << "Red"); + formatKeyword->setToolTip ("(Default: Red) Use one of the following formats:" + tooltip); + + Setting *formatSpecial = createSetting (Type_LineEdit, "colour-special", "Highlight Colour: Special"); + formatSpecial->setDefaultValues (QStringList() << "Dark yellow"); + formatSpecial->setToolTip ("(Default: Dark yellow) Use one of the following formats:" + tooltip); + + Setting *formatComment = createSetting (Type_LineEdit, "colour-comment", "Highlight Colour: Comment"); + formatComment->setDefaultValues (QStringList() << "Green"); + formatComment->setToolTip ("(Default: Green) Use one of the following formats:" + tooltip); + + Setting *formatId = createSetting (Type_LineEdit, "colour-id", "Highlight Colour: Id"); + formatId->setDefaultValues (QStringList() << "Blue"); + formatId->setToolTip ("(Default: Blue) Use one of the following formats:" + tooltip); } { diff --git a/apps/opencs/model/tools/pathgridcheck.cpp b/apps/opencs/model/tools/pathgridcheck.cpp new file mode 100644 index 000000000..76edeb573 --- /dev/null +++ b/apps/opencs/model/tools/pathgridcheck.cpp @@ -0,0 +1,151 @@ +#include "pathgridcheck.hpp" + +#include +#include + +#include "../world/universalid.hpp" +#include "../world/idcollection.hpp" +#include "../world/subcellcollection.hpp" +#include "../world/pathgrid.hpp" + +CSMTools::PathgridCheckStage::PathgridCheckStage (const CSMWorld::SubCellCollection& pathgrids) +: mPathgrids (pathgrids) +{} + +int CSMTools::PathgridCheckStage::setup() +{ + return mPathgrids.getSize(); +} + +void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& messages) +{ + const CSMWorld::Record& record = mPathgrids.getRecord (stage); + + if (record.isDeleted()) + return; + + const CSMWorld::Pathgrid& pathgrid = record.get(); + + CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Pathgrid, pathgrid.mId); + + // check the number of pathgrid points + if (pathgrid.mData.mS2 > static_cast(pathgrid.mPoints.size())) + messages.push_back (std::make_pair (id, pathgrid.mId + " has less points than expected")); + else if (pathgrid.mData.mS2 > static_cast(pathgrid.mPoints.size())) + messages.push_back (std::make_pair (id, pathgrid.mId + " has more points than expected")); + + std::vector pointList(pathgrid.mPoints.size()); + std::vector duplList; + + for (unsigned int i = 0; i < pathgrid.mEdges.size(); ++i) + { + if (pathgrid.mEdges[i].mV0 < static_cast(pathgrid.mPoints.size()) && pathgrid.mEdges[i].mV0 >= 0) + { + pointList[pathgrid.mEdges[i].mV0].mConnectionNum++; + // first check for duplicate edges + unsigned int j = 0; + for (; j < pointList[pathgrid.mEdges[i].mV0].mOtherIndex.size(); ++j) + { + if (pointList[pathgrid.mEdges[i].mV0].mOtherIndex[j] == pathgrid.mEdges[i].mV1) + { + std::ostringstream ss; + ss << "has a duplicate edge between points" << pathgrid.mEdges[i].mV0 + << " and " << pathgrid.mEdges[i].mV1; + messages.push_back (std::make_pair (id, pathgrid.mId + ss.str())); + break; + } + } + + // only add if not a duplicate + if (j == pointList[pathgrid.mEdges[i].mV0].mOtherIndex.size()) + pointList[pathgrid.mEdges[i].mV0].mOtherIndex.push_back(pathgrid.mEdges[i].mV1); + } + else + { + std::ostringstream ss; + ss << " has an edge connecting a non-existent point " << pathgrid.mEdges[i].mV0; + messages.push_back (std::make_pair (id, pathgrid.mId + ss.str())); + } + } + + for (unsigned int i = 0; i < pathgrid.mPoints.size(); ++i) + { + // check the connection number for each point matches the edge connections + if (pathgrid.mPoints[i].mConnectionNum > pointList[i].mConnectionNum) + { + std::ostringstream ss; + ss << " has has less edges than expected for point " << i; + messages.push_back (std::make_pair (id, pathgrid.mId + ss.str())); + } + else if (pathgrid.mPoints[i].mConnectionNum < pointList[i].mConnectionNum) + { + std::ostringstream ss; + ss << " has has more edges than expected for point " << i; + messages.push_back (std::make_pair (id, pathgrid.mId + ss.str())); + } + + // check that edges are bidirectional + bool foundReverse = false; + for (unsigned int j = 0; j < pointList[i].mOtherIndex.size(); ++j) + { + for (unsigned int k = 0; k < pointList[pointList[i].mOtherIndex[j]].mOtherIndex.size(); ++k) + { + if (pointList[pointList[i].mOtherIndex[j]].mOtherIndex[k] == static_cast(i)) + { + foundReverse = true; + break; + } + } + + if (!foundReverse) + { + std::ostringstream ss; + ss << " has a missing edge between points " << i << " and " << pointList[i].mOtherIndex[j]; + messages.push_back (std::make_pair (id, pathgrid.mId + ss.str())); + } + } + + // check duplicate points + // FIXME: how to do this efficiently? + for (unsigned int j = 0; j < pathgrid.mPoints.size(); ++j) + { + if (j == i) + continue; + + if (pathgrid.mPoints[i].mX == pathgrid.mPoints[j].mX && + pathgrid.mPoints[i].mY == pathgrid.mPoints[j].mY && + pathgrid.mPoints[i].mZ == pathgrid.mPoints[j].mZ) + { + std::vector::const_iterator it = find(duplList.begin(), duplList.end(), i); + if (it == duplList.end()) + { + std::ostringstream ss; + ss << " has a duplicated point (" << i + << ") x=" << pathgrid.mPoints[i].mX + << ", y=" << pathgrid.mPoints[i].mY + << ", z=" << pathgrid.mPoints[i].mZ; + messages.push_back (std::make_pair (id, pathgrid.mId + ss.str())); + + duplList.push_back(i); + break; + } + } + } + } + + // check pathgrid points that are not connected to anything + for (unsigned int i = 0; i < pointList.size(); ++i) + { + if (pointList[i].mConnectionNum == 0) + { + std::ostringstream ss; + ss << " has an orphaned point (" << i + << ") x=" << pathgrid.mPoints[i].mX + << ", y=" << pathgrid.mPoints[i].mY + << ", z=" << pathgrid.mPoints[i].mZ; + messages.push_back (std::make_pair (id, pathgrid.mId + ss.str())); + } + } + + // TODO: check whether there are disconnected graphs +} diff --git a/apps/opencs/model/tools/pathgridcheck.hpp b/apps/opencs/model/tools/pathgridcheck.hpp new file mode 100644 index 000000000..f45b5bc93 --- /dev/null +++ b/apps/opencs/model/tools/pathgridcheck.hpp @@ -0,0 +1,40 @@ +#ifndef CSM_TOOLS_PATHGRIDCHECK_H +#define CSM_TOOLS_PATHGRIDCHECK_H + +#include "../world/collection.hpp" + +#include "../doc/stage.hpp" + +namespace CSMWorld +{ + struct Pathgrid; + template + class SubCellCollection; +} + +namespace CSMTools +{ + struct Point + { + unsigned char mConnectionNum; + std::vector mOtherIndex; + Point() : mConnectionNum(0), mOtherIndex(0) {} + }; + + class PathgridCheckStage : public CSMDoc::Stage + { + const CSMWorld::SubCellCollection >& mPathgrids; + + public: + + PathgridCheckStage (const CSMWorld::SubCellCollection >& pathgrids); + + virtual int setup(); + + virtual void perform (int stage, CSMDoc::Messages& messages); + }; +} + +#endif // CSM_TOOLS_PATHGRIDCHECK_H diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index c0991a330..548fcd36f 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -8,12 +8,14 @@ CSMTools::ReferenceableCheckStage::ReferenceableCheckStage( const CSMWorld::RefIdData& referenceable, const CSMWorld::IdCollection& races, const CSMWorld::IdCollection& classes, - const CSMWorld::IdCollection& faction) + const CSMWorld::IdCollection& faction, + const CSMWorld::IdCollection& scripts) : mReferencables(referenceable), mRaces(races), mClasses(classes), mFactions(faction), + mScripts(scripts), mPlayerPresent(false) { } @@ -245,6 +247,9 @@ void CSMTools::ReferenceableCheckStage::bookCheck( CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Book, book.mId); inventoryItemCheck(book, messages, id.toString(), true); + + // Check that mentioned scripts exist + scriptCheck(book, messages, id.toString()); } void CSMTools::ReferenceableCheckStage::activatorCheck( @@ -265,6 +270,9 @@ void CSMTools::ReferenceableCheckStage::activatorCheck( //Checking for model, IIRC all activators should have a model if (activator.mModel.empty()) messages.push_back (std::make_pair (id, activator.mId + " has no model")); + + // Check that mentioned scripts exist + scriptCheck(activator, messages, id.toString()); } void CSMTools::ReferenceableCheckStage::potionCheck( @@ -284,6 +292,9 @@ void CSMTools::ReferenceableCheckStage::potionCheck( inventoryItemCheck(potion, messages, id.toString()); //IIRC potion can have empty effects list just fine. + + // Check that mentioned scripts exist + scriptCheck(potion, messages, id.toString()); } @@ -305,6 +316,9 @@ void CSMTools::ReferenceableCheckStage::apparatusCheck( inventoryItemCheck(apparatus, messages, id.toString()); toolCheck(apparatus, messages, id.toString()); + + // Check that mentioned scripts exist + scriptCheck(apparatus, messages, id.toString()); } void CSMTools::ReferenceableCheckStage::armorCheck( @@ -331,6 +345,9 @@ void CSMTools::ReferenceableCheckStage::armorCheck( //checking for health. Only positive numbers are allowed, or 0 is illegal if (armor.mData.mHealth <= 0) messages.push_back (std::make_pair (id, armor.mId + " has non positive health")); + + // Check that mentioned scripts exist + scriptCheck(armor, messages, id.toString()); } void CSMTools::ReferenceableCheckStage::clothingCheck( @@ -348,6 +365,9 @@ void CSMTools::ReferenceableCheckStage::clothingCheck( const ESM::Clothing& clothing = (dynamic_cast& >(baseRecord)).get(); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Clothing, clothing.mId); inventoryItemCheck(clothing, messages, id.toString(), true); + + // Check that mentioned scripts exist + scriptCheck(clothing, messages, id.toString()); } void CSMTools::ReferenceableCheckStage::containerCheck( @@ -377,6 +397,9 @@ void CSMTools::ReferenceableCheckStage::containerCheck( //checking for name if (container.mName.empty()) messages.push_back (std::make_pair (id, container.mId + " has an empty name")); + + // Check that mentioned scripts exist + scriptCheck(container, messages, id.toString()); } void CSMTools::ReferenceableCheckStage::creatureCheck ( @@ -444,6 +467,9 @@ void CSMTools::ReferenceableCheckStage::creatureCheck ( //TODO, find meaning of other values if (creature.mData.mGold < 0) //It seems that this is for gold in merchant creatures messages.push_back (std::make_pair (id, creature.mId + " has negative gold ")); + + // Check that mentioned scripts exist + scriptCheck(creature, messages, id.toString()); } void CSMTools::ReferenceableCheckStage::doorCheck( @@ -455,15 +481,18 @@ void CSMTools::ReferenceableCheckStage::doorCheck( if (baseRecord.isDeleted()) return; - const ESM::Door& Door = (dynamic_cast&>(baseRecord)).get(); - CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Door, Door.mId); + const ESM::Door& door = (dynamic_cast&>(baseRecord)).get(); + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Door, door.mId); //usual, name or model - if (Door.mName.empty()) - messages.push_back (std::make_pair (id, Door.mId + " has an empty name")); + if (door.mName.empty()) + messages.push_back (std::make_pair (id, door.mId + " has an empty name")); + + if (door.mModel.empty()) + messages.push_back (std::make_pair (id, door.mId + " has no model")); - if (Door.mModel.empty()) - messages.push_back (std::make_pair (id, Door.mId + " has no model")); + // Check that mentioned scripts exist + scriptCheck(door, messages, id.toString()); } void CSMTools::ReferenceableCheckStage::ingredientCheck( @@ -478,10 +507,13 @@ void CSMTools::ReferenceableCheckStage::ingredientCheck( return; } - const ESM::Ingredient& Ingredient = (dynamic_cast& >(baseRecord)).get(); - CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Ingredient, Ingredient.mId); + const ESM::Ingredient& ingredient = (dynamic_cast& >(baseRecord)).get(); + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Ingredient, ingredient.mId); - inventoryItemCheck(Ingredient, messages, id.toString()); + inventoryItemCheck(ingredient, messages, id.toString()); + + // Check that mentioned scripts exist + scriptCheck(ingredient, messages, id.toString()); } void CSMTools::ReferenceableCheckStage::creaturesLevListCheck( @@ -542,6 +574,9 @@ void CSMTools::ReferenceableCheckStage::lightCheck( if (light.mData.mTime == 0) messages.push_back (std::make_pair (id, light.mId + " has zero duration")); } + + // Check that mentioned scripts exist + scriptCheck(light, messages, id.toString()); } void CSMTools::ReferenceableCheckStage::lockpickCheck( @@ -562,6 +597,9 @@ void CSMTools::ReferenceableCheckStage::lockpickCheck( inventoryItemCheck(lockpick, messages, id.toString()); toolCheck(lockpick, messages, id.toString(), true); + + // Check that mentioned scripts exist + scriptCheck(lockpick, messages, id.toString()); } void CSMTools::ReferenceableCheckStage::miscCheck( @@ -580,6 +618,9 @@ void CSMTools::ReferenceableCheckStage::miscCheck( CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Miscellaneous, miscellaneous.mId); inventoryItemCheck(miscellaneous, messages, id.toString()); + + // Check that mentioned scripts exist + scriptCheck(miscellaneous, messages, id.toString()); } void CSMTools::ReferenceableCheckStage::npcCheck ( @@ -697,6 +738,9 @@ void CSMTools::ReferenceableCheckStage::npcCheck ( messages.push_back (std::make_pair (id, npc.mId + " has no hair")); //TODO: reputation, Disposition, rank, everything else + + // Check that mentioned scripts exist + scriptCheck(npc, messages, id.toString()); } void CSMTools::ReferenceableCheckStage::weaponCheck( @@ -773,6 +817,9 @@ void CSMTools::ReferenceableCheckStage::weaponCheck( messages.push_back (std::make_pair (id, weapon.mId + " has negative reach")); } } + + // Check that mentioned scripts exist + scriptCheck(weapon, messages, id.toString()); } void CSMTools::ReferenceableCheckStage::probeCheck( @@ -792,6 +839,9 @@ void CSMTools::ReferenceableCheckStage::probeCheck( inventoryItemCheck(probe, messages, id.toString()); toolCheck(probe, messages, id.toString(), true); + + // Check that mentioned scripts exist + scriptCheck(probe, messages, id.toString()); } void CSMTools::ReferenceableCheckStage::repairCheck ( @@ -808,6 +858,9 @@ void CSMTools::ReferenceableCheckStage::repairCheck ( inventoryItemCheck (repair, messages, id.toString()); toolCheck (repair, messages, id.toString(), true); + + // Check that mentioned scripts exist + scriptCheck(repair, messages, id.toString()); } void CSMTools::ReferenceableCheckStage::staticCheck ( @@ -919,3 +972,13 @@ template void CSMTools::ReferenceableCheckStage::listCheck ( someList.mId + " contains item with non-positive level")); } } + +template void CSMTools::ReferenceableCheckStage::scriptCheck ( + const Tool& someTool, CSMDoc::Messages& messages, const std::string& someID) +{ + if (!someTool.mScript.empty()) + { + if (mScripts.searchId(someTool.mScript) == -1) + messages.push_back (std::make_pair (someID, someTool.mId + " refers to an unknown script \""+someTool.mScript+"\"")); + } +} diff --git a/apps/opencs/model/tools/referenceablecheck.hpp b/apps/opencs/model/tools/referenceablecheck.hpp index ac7ed7082..a34f3a789 100644 --- a/apps/opencs/model/tools/referenceablecheck.hpp +++ b/apps/opencs/model/tools/referenceablecheck.hpp @@ -15,7 +15,8 @@ namespace CSMTools ReferenceableCheckStage (const CSMWorld::RefIdData& referenceable, const CSMWorld::IdCollection& races, const CSMWorld::IdCollection& classes, - const CSMWorld::IdCollection& factions); + const CSMWorld::IdCollection& factions, + const CSMWorld::IdCollection& scripts); virtual void perform(int stage, CSMDoc::Messages& messages); virtual int setup(); @@ -46,26 +47,30 @@ namespace CSMTools //FINAL CHECK void finalCheck (CSMDoc::Messages& messages); - //TEMPLATE CHECKS - template void inventoryItemCheck(const ITEM& someItem, + //TEMPLATE CHECKS + template void inventoryItemCheck(const ITEM& someItem, CSMDoc::Messages& messages, const std::string& someID, bool enchantable); //for all enchantable items. - template void inventoryItemCheck(const ITEM& someItem, + template void inventoryItemCheck(const ITEM& someItem, CSMDoc::Messages& messages, const std::string& someID); //for non-enchantable items. - template void toolCheck(const TOOL& someTool, + template void toolCheck(const TOOL& someTool, CSMDoc::Messages& messages, const std::string& someID, bool canbebroken); //for tools with uses. - template void toolCheck(const TOOL& someTool, + template void toolCheck(const TOOL& someTool, CSMDoc::Messages& messages, const std::string& someID); //for tools without uses. - template void listCheck(const LIST& someList, + template void listCheck(const LIST& someList, + CSMDoc::Messages& messages, + const std::string& someID); + + template void scriptCheck(const TOOL& someTool, CSMDoc::Messages& messages, const std::string& someID); @@ -73,6 +78,7 @@ namespace CSMTools const CSMWorld::IdCollection& mRaces; const CSMWorld::IdCollection& mClasses; const CSMWorld::IdCollection& mFactions; + const CSMWorld::IdCollection& mScripts; bool mPlayerPresent; }; } diff --git a/apps/opencs/model/tools/tools.cpp b/apps/opencs/model/tools/tools.cpp index 99e462b1d..8d93a9433 100644 --- a/apps/opencs/model/tools/tools.cpp +++ b/apps/opencs/model/tools/tools.cpp @@ -26,6 +26,7 @@ #include "referencecheck.hpp" #include "startscriptcheck.hpp" #include "searchoperation.hpp" +#include "pathgridcheck.hpp" CSMDoc::OperationHolder *CSMTools::Tools::get (int type) { @@ -81,7 +82,7 @@ CSMDoc::OperationHolder *CSMTools::Tools::getVerifier() mVerifierOperation->appendStage (new SpellCheckStage (mData.getSpells())); - mVerifierOperation->appendStage (new ReferenceableCheckStage (mData.getReferenceables().getDataSet(), mData.getRaces(), mData.getClasses(), mData.getFactions())); + mVerifierOperation->appendStage (new ReferenceableCheckStage (mData.getReferenceables().getDataSet(), mData.getRaces(), mData.getClasses(), mData.getFactions(), mData.getScripts())); mVerifierOperation->appendStage (new ReferenceCheckStage(mData.getReferences(), mData.getReferenceables(), mData.getCells(), mData.getFactions())); @@ -96,6 +97,8 @@ CSMDoc::OperationHolder *CSMTools::Tools::getVerifier() CSMWorld::UniversalId( CSMWorld::UniversalId::Type_Meshes )), mData.getRaces() )); + mVerifierOperation->appendStage (new PathgridCheckStage (mData.getPathgrids())); + mVerifier.setOperation (mVerifierOperation); } @@ -114,7 +117,7 @@ CSMTools::Tools::Tools (CSMDoc::Document& document) connect (&mSearch, SIGNAL (done (int, bool)), this, SIGNAL (done (int, bool))); connect (&mSearch, SIGNAL (reportMessage (const CSMWorld::UniversalId&, const std::string&, const std::string&, int)), - this, SLOT (verifierMessage (const CSMWorld::UniversalId&, const std::string&, const std::string&, int))); + this, SLOT (verifierMessage (const CSMWorld::UniversalId&, const std::string&, const std::string&, int))); } CSMTools::Tools::~Tools() @@ -130,7 +133,7 @@ CSMTools::Tools::~Tools() mSearch.abortAndWait(); delete mSearchOperation; } - + for (std::map::iterator iter (mReports.begin()); iter!=mReports.end(); ++iter) delete iter->second; } @@ -149,7 +152,7 @@ CSMWorld::UniversalId CSMTools::Tools::newSearch() { mReports.insert (std::make_pair (mNextReportNumber++, new ReportModel (true))); - return CSMWorld::UniversalId (CSMWorld::UniversalId::Type_Search, mNextReportNumber-1); + return CSMWorld::UniversalId (CSMWorld::UniversalId::Type_Search, mNextReportNumber-1); } void CSMTools::Tools::runSearch (const CSMWorld::UniversalId& searchId, const Search& search) @@ -159,11 +162,11 @@ void CSMTools::Tools::runSearch (const CSMWorld::UniversalId& searchId, const Se if (!mSearchOperation) { mSearchOperation = new SearchOperation (mDocument); - mSearch.setOperation (mSearchOperation); + mSearch.setOperation (mSearchOperation); } mSearchOperation->configure (search); - + mSearch.start(); } @@ -198,7 +201,7 @@ CSMTools::ReportModel *CSMTools::Tools::getReport (const CSMWorld::UniversalId& id.getType()!=CSMWorld::UniversalId::Type_LoadErrorLog && id.getType()!=CSMWorld::UniversalId::Type_Search) throw std::logic_error ("invalid request for report model: " + id.toString()); - + return mReports.at (id.getIndex()); } diff --git a/apps/opencs/model/world/columnbase.cpp b/apps/opencs/model/world/columnbase.cpp index 53987b23c..3d13538c0 100644 --- a/apps/opencs/model/world/columnbase.cpp +++ b/apps/opencs/model/world/columnbase.cpp @@ -84,6 +84,7 @@ bool CSMWorld::ColumnBase::isId (Display display) Display_InfoCondFunc, Display_InfoCondVar, Display_InfoCondComp, + Display_RaceSkill, Display_None }; @@ -137,8 +138,8 @@ bool CSMWorld::NestableColumn::hasChildren() const } CSMWorld::NestedChildColumn::NestedChildColumn (int id, - CSMWorld::ColumnBase::Display display, bool isEditable) - : NestableColumn (id, display, CSMWorld::ColumnBase::Flag_Dialogue) , mIsEditable(isEditable) + CSMWorld::ColumnBase::Display display, int flags, bool isEditable) + : NestableColumn (id, display, flags) , mIsEditable(isEditable) {} bool CSMWorld::NestedChildColumn::isEditable () const diff --git a/apps/opencs/model/world/columnbase.hpp b/apps/opencs/model/world/columnbase.hpp index 2d2513774..bf8378e37 100644 --- a/apps/opencs/model/world/columnbase.hpp +++ b/apps/opencs/model/world/columnbase.hpp @@ -25,7 +25,8 @@ namespace CSMWorld { Flag_Table = 1, // column should be displayed in table view Flag_Dialogue = 2, // column should be displayed in dialogue view - Flag_Dialogue_List = 4 // column should be diaplyed in dialogue view + Flag_Dialogue_List = 4, // column should be diaplyed in dialogue view + Flag_Dialogue_Refresh = 8 // refresh dialogue view if this column is modified }; enum Display @@ -119,6 +120,7 @@ namespace CSMWorld Display_InfoCondFunc, Display_InfoCondVar, Display_InfoCondComp, + Display_RaceSkill, //top level columns that nest other columns Display_NestedHeader @@ -199,7 +201,8 @@ namespace CSMWorld struct NestedChildColumn : public NestableColumn { - NestedChildColumn (int id, Display display, bool isEditable = true); + NestedChildColumn (int id, + Display display, int flags = ColumnBase::Flag_Dialogue, bool isEditable = true); virtual bool isEditable() const; diff --git a/apps/opencs/model/world/columnimp.hpp b/apps/opencs/model/world/columnimp.hpp index cbe0d74c4..6b496e0ca 100644 --- a/apps/opencs/model/world/columnimp.hpp +++ b/apps/opencs/model/world/columnimp.hpp @@ -467,8 +467,9 @@ namespace CSMWorld int mMask; bool mInverted; - FlagColumn (int columnId, int mask, bool inverted = false) - : Column (columnId, ColumnBase::Display_Boolean), mMask (mask), + FlagColumn (int columnId, int mask, + int flags = ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue, bool inverted = false) + : Column (columnId, ColumnBase::Display_Boolean, flags), mMask (mask), mInverted (inverted) {} diff --git a/apps/opencs/model/world/columns.cpp b/apps/opencs/model/world/columns.cpp index 89ee6258b..9491c3246 100644 --- a/apps/opencs/model/world/columns.cpp +++ b/apps/opencs/model/world/columns.cpp @@ -176,7 +176,7 @@ namespace CSMWorld { ColumnId_ContainerContent, "Content" }, { ColumnId_ItemCount, "Count" }, - { ColumnId_InventoryItemId, "ID"}, + { ColumnId_InventoryItemId, "Item ID"}, { ColumnId_CombatState, "Combat" }, { ColumnId_MagicState, "Magic" }, @@ -188,10 +188,10 @@ namespace CSMWorld { ColumnId_ActorInventory, "Inventory" }, { ColumnId_SpellList, "Spells" }, - { ColumnId_SpellId, "ID"}, + { ColumnId_SpellId, "Spell ID"}, { ColumnId_NpcDestinations, "Destinations" }, - { ColumnId_DestinationCell, "Cell"}, + { ColumnId_DestinationCell, "Dest Cell"}, { ColumnId_PosX, "Dest X"}, { ColumnId_PosY, "Dest Y"}, { ColumnId_PosZ, "Dest Z"}, @@ -224,17 +224,17 @@ namespace CSMWorld { ColumnId_BoltSound, "Bolt Sound" }, { ColumnId_PathgridPoints, "Points" }, - { ColumnId_PathgridIndex, "Index" }, + { ColumnId_PathgridIndex, "pIndex" }, { ColumnId_PathgridPosX, "X" }, { ColumnId_PathgridPosY, "Y" }, { ColumnId_PathgridPosZ, "Z" }, { ColumnId_PathgridEdges, "Edges" }, - { ColumnId_PathgridEdgeIndex, "Index" }, + { ColumnId_PathgridEdgeIndex, "eIndex" }, { ColumnId_PathgridEdge0, "Point 0" }, { ColumnId_PathgridEdge1, "Point 1" }, { ColumnId_RegionSounds, "Sounds" }, - { ColumnId_SoundName, "Name" }, + { ColumnId_SoundName, "Sound Name" }, { ColumnId_SoundChance, "Chance" }, { ColumnId_FactionReactions, "Reactions" }, @@ -250,7 +250,7 @@ namespace CSMWorld { ColumnId_AiPackageList, "Ai Packages" }, { ColumnId_AiPackageType, "Package" }, { ColumnId_AiWanderDist, "Wander Dist" }, - { ColumnId_AiDuration, "Duration" }, + { ColumnId_AiDuration, "Ai Duration" }, { ColumnId_AiWanderToD, "Wander ToD" }, { ColumnId_AiWanderIdle, "Wander Idle" }, { ColumnId_AiWanderRepeat, "Wander Repeat" }, @@ -260,11 +260,11 @@ namespace CSMWorld { ColumnId_PartRefList, "Part Reference" }, { ColumnId_PartRefType, "Type" }, - { ColumnId_PartRefMale, "Male" }, - { ColumnId_PartRefFemale, "Female" }, + { ColumnId_PartRefMale, "Male Part" }, + { ColumnId_PartRefFemale, "Female Part" }, { ColumnId_LevelledList,"Levelled List" }, - { ColumnId_LevelledItemId,"Item ID" }, + { ColumnId_LevelledItemId,"Levelled Item" }, { ColumnId_LevelledItemLevel,"Level" }, { ColumnId_LevelledItemType, "Calculate all levels <= player" }, { ColumnId_LevelledItemTypeEach, "Select a new item each instance" }, @@ -278,9 +278,39 @@ namespace CSMWorld { ColumnId_InfoCondFunc, "Function" }, { ColumnId_InfoCondVar, "Func/Variable" }, { ColumnId_InfoCondComp, "Comp" }, - { ColumnId_InfoCondValue, "Value" }, + { ColumnId_InfoCondValue, "Values" }, { ColumnId_OriginalCell, "Original Cell" }, + { ColumnId_NpcAttributes, "Attributes" }, + { ColumnId_NpcSkills, "Skills" }, + { ColumnId_UChar, "Value [0..255]" }, + { ColumnId_NpcMisc, "Misc" }, + { ColumnId_NpcLevel, "Level" }, + { ColumnId_NpcFactionID, "Faction ID" }, + { ColumnId_NpcHealth, "Health" }, + { ColumnId_NpcMana, "Mana" }, + { ColumnId_NpcFatigue, "Fatigue" }, + { ColumnId_NpcDisposition, "Disposition" }, + { ColumnId_NpcReputation, "Reputation" }, + { ColumnId_NpcRank, "Rank" }, + { ColumnId_NpcGold, "Gold" }, + { ColumnId_NpcPersistence, "Persistent" }, + + { ColumnId_RaceAttributes, "Attributes" }, + { ColumnId_RaceMaleValue, "Male" }, + { ColumnId_RaceFemaleValue, "Female" }, + { ColumnId_RaceSkillBonus, "Skill Bonus" }, + { ColumnId_RaceSkill, "Skills" }, + { ColumnId_RaceBonus, "Bonus" }, + + { ColumnId_Interior, "Interior" }, + { ColumnId_Ambient, "Ambient" }, + { ColumnId_Sunlight, "Sunlight" }, + { ColumnId_Fog, "Fog" }, + { ColumnId_FogDensity, "Fog Density" }, + { ColumnId_WaterLevel, "Water Level" }, + { ColumnId_MapColor, "Map Color" }, + { ColumnId_UseValue1, "Use value 1" }, { ColumnId_UseValue2, "Use value 2" }, { ColumnId_UseValue3, "Use value 3" }, @@ -551,6 +581,7 @@ namespace // FIXME: don't have dynamic value enum delegate, use Display_String for now //case CSMWorld::Columns::ColumnId_InfoCond: return sInfoCond; case CSMWorld::Columns::ColumnId_InfoCondComp: return sInfoCondComp; + case CSMWorld::Columns::ColumnId_RaceSkill: return sSkills; default: return 0; } diff --git a/apps/opencs/model/world/columns.hpp b/apps/opencs/model/world/columns.hpp index f971f3fd8..191bbdea8 100644 --- a/apps/opencs/model/world/columns.hpp +++ b/apps/opencs/model/world/columns.hpp @@ -272,6 +272,36 @@ namespace CSMWorld ColumnId_OriginalCell = 247, + ColumnId_NpcAttributes = 248, + ColumnId_NpcSkills = 249, + ColumnId_UChar = 250, + ColumnId_NpcMisc = 251, + ColumnId_NpcLevel = 252, + ColumnId_NpcFactionID = 253, + ColumnId_NpcHealth = 254, + ColumnId_NpcMana = 255, + ColumnId_NpcFatigue = 256, + ColumnId_NpcDisposition = 257, + ColumnId_NpcReputation = 258, + ColumnId_NpcRank = 259, + ColumnId_NpcGold = 260, + ColumnId_NpcPersistence = 261, + + ColumnId_RaceAttributes = 262, + ColumnId_RaceMaleValue = 263, + ColumnId_RaceFemaleValue = 264, + ColumnId_RaceSkillBonus = 265, + ColumnId_RaceSkill = 266, + ColumnId_RaceBonus = 267, + + ColumnId_Interior = 268, + ColumnId_Ambient = 269, + ColumnId_Sunlight = 270, + ColumnId_Fog = 271, + ColumnId_FogDensity = 272, + ColumnId_WaterLevel = 273, + ColumnId_MapColor = 274, + // Allocated to a separate value range, so we don't get a collision should we ever need // to extend the number of use values. ColumnId_UseValue1 = 0x10000, diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index 9772a68fa..6a9ecf311 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -136,6 +136,25 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc mRaces.addAdapter (std::make_pair(&mRaces.getColumn(index), new SpellListAdapter ())); mRaces.getNestableColumn(index)->addColumn( new NestedChildColumn (Columns::ColumnId_SpellId, ColumnBase::Display_String)); + // Race attributes + mRaces.addColumn (new NestedParentColumn (Columns::ColumnId_RaceAttributes)); + index = mRaces.getColumns()-1; + mRaces.addAdapter (std::make_pair(&mRaces.getColumn(index), new RaceAttributeAdapter())); + mRaces.getNestableColumn(index)->addColumn( + new NestedChildColumn (Columns::ColumnId_RaceAttributes, ColumnBase::Display_String, + ColumnBase::Flag_Dialogue, false)); + mRaces.getNestableColumn(index)->addColumn( + new NestedChildColumn (Columns::ColumnId_RaceMaleValue, ColumnBase::Display_Integer)); + mRaces.getNestableColumn(index)->addColumn( + new NestedChildColumn (Columns::ColumnId_RaceFemaleValue, ColumnBase::Display_Integer)); + // Race skill bonus + mRaces.addColumn (new NestedParentColumn (Columns::ColumnId_RaceSkillBonus)); + index = mRaces.getColumns()-1; + mRaces.addAdapter (std::make_pair(&mRaces.getColumn(index), new RaceSkillsBonusAdapter())); + mRaces.getNestableColumn(index)->addColumn( + new NestedChildColumn (Columns::ColumnId_RaceSkill, ColumnBase::Display_RaceSkill)); + mRaces.getNestableColumn(index)->addColumn( + new NestedChildColumn (Columns::ColumnId_RaceBonus, ColumnBase::Display_Integer)); mSounds.addColumn (new StringIdColumn); mSounds.addColumn (new RecordStateColumn); @@ -269,10 +288,32 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc mCells.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Cell)); mCells.addColumn (new NameColumn); mCells.addColumn (new FlagColumn (Columns::ColumnId_SleepForbidden, ESM::Cell::NoSleep)); - mCells.addColumn (new FlagColumn (Columns::ColumnId_InteriorWater, ESM::Cell::HasWater)); - mCells.addColumn (new FlagColumn (Columns::ColumnId_InteriorSky, ESM::Cell::QuasiEx)); + mCells.addColumn (new FlagColumn (Columns::ColumnId_InteriorWater, ESM::Cell::HasWater, + ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue | ColumnBase::Flag_Dialogue_Refresh)); + mCells.addColumn (new FlagColumn (Columns::ColumnId_InteriorSky, ESM::Cell::QuasiEx, + ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue | ColumnBase::Flag_Dialogue_Refresh)); mCells.addColumn (new RegionColumn); mCells.addColumn (new RefNumCounterColumn); + // Misc Cell data + mCells.addColumn (new NestedParentColumn (Columns::ColumnId_Cell, + ColumnBase::Flag_Dialogue | ColumnBase::Flag_Dialogue_List)); + index = mCells.getColumns()-1; + mCells.addAdapter (std::make_pair(&mCells.getColumn(index), new CellListAdapter ())); + mCells.getNestableColumn(index)->addColumn( + new NestedChildColumn (Columns::ColumnId_Interior, ColumnBase::Display_Boolean, + ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue | ColumnBase::Flag_Dialogue_Refresh)); + mCells.getNestableColumn(index)->addColumn( + new NestedChildColumn (Columns::ColumnId_Ambient, ColumnBase::Display_Integer)); + mCells.getNestableColumn(index)->addColumn( + new NestedChildColumn (Columns::ColumnId_Sunlight, ColumnBase::Display_Integer)); + mCells.getNestableColumn(index)->addColumn( + new NestedChildColumn (Columns::ColumnId_Fog, ColumnBase::Display_Integer)); + mCells.getNestableColumn(index)->addColumn( + new NestedChildColumn (Columns::ColumnId_FogDensity, ColumnBase::Display_Float)); + mCells.getNestableColumn(index)->addColumn( + new NestedChildColumn (Columns::ColumnId_WaterLevel, ColumnBase::Display_Float)); + mCells.getNestableColumn(index)->addColumn( + new NestedChildColumn (Columns::ColumnId_MapColor, ColumnBase::Display_Integer)); mEnchantments.addColumn (new StringIdColumn); mEnchantments.addColumn (new RecordStateColumn); @@ -309,7 +350,8 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc mBodyParts.addColumn (new BodyPartTypeColumn); mBodyParts.addColumn (new VampireColumn); mBodyParts.addColumn (new FlagColumn (Columns::ColumnId_Female, ESM::BodyPart::BPF_Female)); - mBodyParts.addColumn (new FlagColumn (Columns::ColumnId_Playable, ESM::BodyPart::BPF_NotPlayable, true)); + mBodyParts.addColumn (new FlagColumn (Columns::ColumnId_Playable, + ESM::BodyPart::BPF_NotPlayable, ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue, true)); mBodyParts.addColumn (new MeshTypeColumn); mBodyParts.addColumn (new ModelColumn); mBodyParts.addColumn (new RaceColumn); @@ -356,7 +398,8 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc // new objects deleted in dtor of NestableColumn // WARNING: The order of the columns below are assumed in PathgridPointListAdapter mPathgrids.getNestableColumn(index)->addColumn( - new NestedChildColumn (Columns::ColumnId_PathgridIndex, ColumnBase::Display_Integer, false)); + new NestedChildColumn (Columns::ColumnId_PathgridIndex, ColumnBase::Display_Integer, + ColumnBase::Flag_Dialogue, false)); mPathgrids.getNestableColumn(index)->addColumn( new NestedChildColumn (Columns::ColumnId_PathgridPosX, ColumnBase::Display_Integer)); mPathgrids.getNestableColumn(index)->addColumn( @@ -368,7 +411,8 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc index = mPathgrids.getColumns()-1; mPathgrids.addAdapter (std::make_pair(&mPathgrids.getColumn(index), new PathgridEdgeListAdapter ())); mPathgrids.getNestableColumn(index)->addColumn( - new NestedChildColumn (Columns::ColumnId_PathgridEdgeIndex, ColumnBase::Display_Integer, false)); + new NestedChildColumn (Columns::ColumnId_PathgridEdgeIndex, ColumnBase::Display_Integer, + ColumnBase::Flag_Dialogue, false)); mPathgrids.getNestableColumn(index)->addColumn( new NestedChildColumn (Columns::ColumnId_PathgridEdge0, ColumnBase::Display_Integer)); mPathgrids.getNestableColumn(index)->addColumn( @@ -447,7 +491,7 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc addModel (new IdTree (&mTopicInfos, &mTopicInfos, IdTable::Feature_ReorderWithinTopic), UniversalId::Type_TopicInfo); addModel (new IdTable (&mJournalInfos, IdTable::Feature_ReorderWithinTopic), UniversalId::Type_JournalInfo); - addModel (new IdTable (&mCells, IdTable::Feature_ViewId), UniversalId::Type_Cell); + addModel (new IdTree (&mCells, &mCells, IdTable::Feature_ViewId), UniversalId::Type_Cell); addModel (new IdTree (&mEnchantments, &mEnchantments), UniversalId::Type_Enchantment); addModel (new IdTable (&mBodyParts), UniversalId::Type_BodyPart); addModel (new IdTable (&mSoundGens), UniversalId::Type_SoundGen); diff --git a/apps/opencs/model/world/data.hpp b/apps/opencs/model/world/data.hpp index a41946687..b837c2316 100644 --- a/apps/opencs/model/world/data.hpp +++ b/apps/opencs/model/world/data.hpp @@ -95,7 +95,7 @@ namespace CSMWorld IdCollection mStartScripts; NestedInfoCollection mTopicInfos; InfoCollection mJournalInfos; - IdCollection mCells; + NestedIdCollection mCells; IdCollection mLandTextures; IdCollection mLand; RefIdCollection mReferenceables; diff --git a/apps/opencs/model/world/idtable.cpp b/apps/opencs/model/world/idtable.cpp index a0dd0b47b..04aa271cc 100644 --- a/apps/opencs/model/world/idtable.cpp +++ b/apps/opencs/model/world/idtable.cpp @@ -74,8 +74,7 @@ bool CSMWorld::IdTable::setData (const QModelIndex &index, const QVariant &value { mIdCollection->setData (index.row(), index.column(), value); - emit dataChanged (CSMWorld::IdTable::index (index.row(), 0), - CSMWorld::IdTable::index (index.row(), mIdCollection->getColumns()-1)); + emit dataChanged (index, index); return true; } @@ -160,7 +159,7 @@ void CSMWorld::IdTable::setRecord (const std::string& id, const RecordBase& reco if (index==-1) { - int index = mIdCollection->getAppendIndex (id); + int index = mIdCollection->getAppendIndex (id, type); beginInsertRows (QModelIndex(), index, index); diff --git a/apps/opencs/model/world/idtableproxymodel.cpp b/apps/opencs/model/world/idtableproxymodel.cpp index 93c1749c6..987d27462 100644 --- a/apps/opencs/model/world/idtableproxymodel.cpp +++ b/apps/opencs/model/world/idtableproxymodel.cpp @@ -46,10 +46,16 @@ void CSMWorld::IdTableProxyModel::setFilter (const boost::shared_ptr& filter); + void refreshFilter(); + protected: bool lessThan(const QModelIndex &left, const QModelIndex &right) const; diff --git a/apps/opencs/model/world/idtree.cpp b/apps/opencs/model/world/idtree.cpp index 06db09a0f..7351c03a7 100644 --- a/apps/opencs/model/world/idtree.cpp +++ b/apps/opencs/model/world/idtree.cpp @@ -74,7 +74,7 @@ QVariant CSMWorld::IdTree::nestedHeaderData(int section, int subSection, Qt::Ori return tr(parentColumn->nestedColumn(subSection).getTitle().c_str()); if (role==ColumnBase::Role_Flags) - return idCollection()->getColumn (section).mFlags; + return parentColumn->nestedColumn(subSection).mFlags; if (role==ColumnBase::Role_Display) return parentColumn->nestedColumn(subSection).mDisplayType; @@ -92,8 +92,8 @@ bool CSMWorld::IdTree::setData (const QModelIndex &index, const QVariant &value, mNestedCollection->setNestedData(parentAddress.first, parentAddress.second, value, index.row(), index.column()); - emit dataChanged (CSMWorld::IdTree::index (parentAddress.first, 0), - CSMWorld::IdTree::index (parentAddress.first, idCollection()->getColumns()-1)); + emit dataChanged (index, index); + return true; } else diff --git a/apps/opencs/model/world/infocollection.cpp b/apps/opencs/model/world/infocollection.cpp index f2d81823c..a508d28f3 100644 --- a/apps/opencs/model/world/infocollection.cpp +++ b/apps/opencs/model/world/infocollection.cpp @@ -173,6 +173,17 @@ CSMWorld::InfoCollection::Range CSMWorld::InfoCollection::getTopicRange (const s RecordConstIterator begin = getRecords().begin()+iter->second; + while (begin != getRecords().begin()) + { + if (!Misc::StringUtils::ciEqual(begin->get().mTopicId, topic2)) + { + // we've gone one too far, go back + ++begin; + break; + } + --begin; + } + // Find end RecordConstIterator end = begin; diff --git a/apps/opencs/model/world/nestedcoladapterimp.cpp b/apps/opencs/model/world/nestedcoladapterimp.cpp index a0d764576..b7d09777d 100644 --- a/apps/opencs/model/world/nestedcoladapterimp.cpp +++ b/apps/opencs/model/world/nestedcoladapterimp.cpp @@ -481,7 +481,7 @@ namespace CSMWorld void InfoListAdapter::removeRow(Record& record, int rowToRemove) const { - throw std::logic_error ("cannot add a row to a fixed table"); + throw std::logic_error ("cannot remove a row to a fixed table"); } void InfoListAdapter::setTable(Record& record, @@ -880,4 +880,315 @@ namespace CSMWorld { return static_cast(record.get().mSelects.size()); } + + RaceAttributeAdapter::RaceAttributeAdapter () {} + + void RaceAttributeAdapter::addRow(Record& record, int position) const + { + // Do nothing, this table cannot be changed by the user + } + + void RaceAttributeAdapter::removeRow(Record& record, int rowToRemove) const + { + // Do nothing, this table cannot be changed by the user + } + + void RaceAttributeAdapter::setTable(Record& record, + const NestedTableWrapperBase& nestedTable) const + { + ESM::Race race = record.get(); + + race.mData = + static_cast >&>(nestedTable).mNestedTable.at(0); + + record.setModified (race); + } + + NestedTableWrapperBase* RaceAttributeAdapter::table(const Record& record) const + { + std::vector wrap; + wrap.push_back(record.get().mData); + // deleted by dtor of NestedTableStoring + return new NestedTableWrapper >(wrap); + } + + QVariant RaceAttributeAdapter::getData(const Record& record, + int subRowIndex, int subColIndex) const + { + ESM::Race race = record.get(); + + if (subRowIndex < 0 || subRowIndex >= ESM::Attribute::Length) + throw std::runtime_error ("index out of range"); + + switch (subColIndex) + { + case 0: return QString(ESM::Attribute::sAttributeNames[subRowIndex].c_str()); + case 1: return race.mData.mAttributeValues[subRowIndex].mMale; + case 2: return race.mData.mAttributeValues[subRowIndex].mFemale; + default: throw std::runtime_error("Race Attribute subcolumn index out of range"); + } + } + + void RaceAttributeAdapter::setData(Record& record, + const QVariant& value, int subRowIndex, int subColIndex) const + { + ESM::Race race = record.get(); + + if (subRowIndex < 0 || subRowIndex >= ESM::Attribute::Length) + throw std::runtime_error ("index out of range"); + + switch (subColIndex) + { + case 0: return; // throw an exception here? + case 1: race.mData.mAttributeValues[subRowIndex].mMale = value.toInt(); break; + case 2: race.mData.mAttributeValues[subRowIndex].mFemale = value.toInt(); break; + default: throw std::runtime_error("Race Attribute subcolumn index out of range"); + } + + record.setModified (race); + } + + int RaceAttributeAdapter::getColumnsCount(const Record& record) const + { + return 3; // attrib, male, female + } + + int RaceAttributeAdapter::getRowsCount(const Record& record) const + { + return ESM::Attribute::Length; // there are 8 attributes + } + + RaceSkillsBonusAdapter::RaceSkillsBonusAdapter () {} + + void RaceSkillsBonusAdapter::addRow(Record& record, int position) const + { + // Do nothing, this table cannot be changed by the user + } + + void RaceSkillsBonusAdapter::removeRow(Record& record, int rowToRemove) const + { + // Do nothing, this table cannot be changed by the user + } + + void RaceSkillsBonusAdapter::setTable(Record& record, + const NestedTableWrapperBase& nestedTable) const + { + ESM::Race race = record.get(); + + race.mData = + static_cast >&>(nestedTable).mNestedTable.at(0); + + record.setModified (race); + } + + NestedTableWrapperBase* RaceSkillsBonusAdapter::table(const Record& record) const + { + std::vector wrap; + wrap.push_back(record.get().mData); + // deleted by dtor of NestedTableStoring + return new NestedTableWrapper >(wrap); + } + + QVariant RaceSkillsBonusAdapter::getData(const Record& record, + int subRowIndex, int subColIndex) const + { + ESM::Race race = record.get(); + + if (subRowIndex < 0 || subRowIndex >= static_cast(sizeof(race.mData.mBonus)/sizeof(race.mData.mBonus[0]))) + throw std::runtime_error ("index out of range"); + + switch (subColIndex) + { + case 0: return race.mData.mBonus[subRowIndex].mSkill; // can be -1 + case 1: return race.mData.mBonus[subRowIndex].mBonus; + default: throw std::runtime_error("Race skill bonus subcolumn index out of range"); + } + } + + void RaceSkillsBonusAdapter::setData(Record& record, + const QVariant& value, int subRowIndex, int subColIndex) const + { + ESM::Race race = record.get(); + + if (subRowIndex < 0 || subRowIndex >= static_cast(sizeof(race.mData.mBonus)/sizeof(race.mData.mBonus[0]))) + throw std::runtime_error ("index out of range"); + + switch (subColIndex) + { + case 0: race.mData.mBonus[subRowIndex].mSkill = value.toInt(); break; // can be -1 + case 1: race.mData.mBonus[subRowIndex].mBonus = value.toInt(); break; + default: throw std::runtime_error("Race skill bonus subcolumn index out of range"); + } + + record.setModified (race); + } + + int RaceSkillsBonusAdapter::getColumnsCount(const Record& record) const + { + return 2; // skill, bonus + } + + int RaceSkillsBonusAdapter::getRowsCount(const Record& record) const + { + // there are 7 skill bonuses + return static_cast(sizeof(record.get().mData.mBonus)/sizeof(record.get().mData.mBonus[0])); + } + + CellListAdapter::CellListAdapter () {} + + void CellListAdapter::addRow(Record& record, int position) const + { + throw std::logic_error ("cannot add a row to a fixed table"); + } + + void CellListAdapter::removeRow(Record& record, int rowToRemove) const + { + throw std::logic_error ("cannot remove a row to a fixed table"); + } + + void CellListAdapter::setTable(Record& record, + const NestedTableWrapperBase& nestedTable) const + { + throw std::logic_error ("table operation not supported"); + } + + NestedTableWrapperBase* CellListAdapter::table(const Record& record) const + { + throw std::logic_error ("table operation not supported"); + } + + QVariant CellListAdapter::getData(const Record& record, + int subRowIndex, int subColIndex) const + { + CSMWorld::Cell cell = record.get(); + + bool isInterior = (cell.mData.mFlags & ESM::Cell::Interior) != 0; + bool behaveLikeExterior = (cell.mData.mFlags & ESM::Cell::QuasiEx) != 0; + bool interiorWater = (cell.mData.mFlags & ESM::Cell::HasWater) != 0; + + switch (subColIndex) + { + case 0: return isInterior; + case 1: return (isInterior && !behaveLikeExterior) ? + cell.mAmbi.mAmbient : QVariant(QVariant::UserType); + case 2: return (isInterior && !behaveLikeExterior) ? + cell.mAmbi.mSunlight : QVariant(QVariant::UserType); + case 3: return (isInterior && !behaveLikeExterior) ? + cell.mAmbi.mFog : QVariant(QVariant::UserType); + case 4: return (isInterior && !behaveLikeExterior) ? + cell.mAmbi.mFogDensity : QVariant(QVariant::UserType); + case 5: + { + if (isInterior && !behaveLikeExterior && interiorWater) + return cell.mWater; + else + return QVariant(QVariant::UserType); + } + case 6: return isInterior ? + QVariant(QVariant::UserType) : cell.mMapColor; // TODO: how to select? + //case 7: return isInterior ? + //behaveLikeExterior : QVariant(QVariant::UserType); + default: throw std::runtime_error("Cell subcolumn index out of range"); + } + } + + void CellListAdapter::setData(Record& record, + const QVariant& value, int subRowIndex, int subColIndex) const + { + CSMWorld::Cell cell = record.get(); + + bool isInterior = (cell.mData.mFlags & ESM::Cell::Interior) != 0; + bool behaveLikeExterior = (cell.mData.mFlags & ESM::Cell::QuasiEx) != 0; + bool interiorWater = (cell.mData.mFlags & ESM::Cell::HasWater) != 0; + + switch (subColIndex) + { + case 0: + { + if (value.toBool()) + cell.mData.mFlags |= ESM::Cell::Interior; + else + cell.mData.mFlags &= ~ESM::Cell::Interior; + break; + } + case 1: + { + if (isInterior && !behaveLikeExterior) + cell.mAmbi.mAmbient = static_cast(value.toInt()); + else + return; // return without saving + break; + } + case 2: + { + if (isInterior && !behaveLikeExterior) + cell.mAmbi.mSunlight = static_cast(value.toInt()); + else + return; // return without saving + break; + } + case 3: + { + if (isInterior && !behaveLikeExterior) + cell.mAmbi.mFog = static_cast(value.toInt()); + else + return; // return without saving + break; + } + case 4: + { + if (isInterior && !behaveLikeExterior) + cell.mAmbi.mFogDensity = value.toFloat(); + else + return; // return without saving + break; + } + case 5: + { + if (isInterior && !behaveLikeExterior && interiorWater) + cell.mWater = value.toFloat(); + else + return; // return without saving + break; + } + case 6: + { + if (!isInterior) + cell.mMapColor = value.toInt(); + else + return; // return without saving + break; + } +#if 0 + // redundant since this flag is shown in the main table as "Interior Sky" + // keep here for documenting the logic based on vanilla + case 7: + { + if (isInterior) + { + if (value.toBool()) + cell.mData.mFlags |= ESM::Cell::QuasiEx; + else + cell.mData.mFlags &= ~ESM::Cell::QuasiEx; + } + else + return; // return without saving + break; + } +#endif + default: throw std::runtime_error("Cell subcolumn index out of range"); + } + + record.setModified (cell); + } + + int CellListAdapter::getColumnsCount(const Record& record) const + { + return 7; + } + + int CellListAdapter::getRowsCount(const Record& record) const + { + return 1; // fixed at size 1 + } } diff --git a/apps/opencs/model/world/nestedcoladapterimp.hpp b/apps/opencs/model/world/nestedcoladapterimp.hpp index 776a908ba..81c52588b 100644 --- a/apps/opencs/model/world/nestedcoladapterimp.hpp +++ b/apps/opencs/model/world/nestedcoladapterimp.hpp @@ -8,9 +8,11 @@ #include // for converting magic effect id to string & back #include // for converting skill names #include // for converting attributes +#include #include "nestedcolumnadapter.hpp" #include "nestedtablewrapper.hpp" +#include "cell.hpp" namespace ESM { @@ -437,6 +439,81 @@ namespace CSMWorld virtual int getRowsCount(const Record& record) const; }; + + class RaceAttributeAdapter : public NestedColumnAdapter + { + public: + RaceAttributeAdapter (); + + virtual void addRow(Record& record, int position) const; + + virtual void removeRow(Record& record, int rowToRemove) const; + + virtual void setTable(Record& record, + const NestedTableWrapperBase& nestedTable) const; + + virtual NestedTableWrapperBase* table(const Record& record) const; + + virtual QVariant getData(const Record& record, + int subRowIndex, int subColIndex) const; + + virtual void setData(Record& record, + const QVariant& value, int subRowIndex, int subColIndex) const; + + virtual int getColumnsCount(const Record& record) const; + + virtual int getRowsCount(const Record& record) const; + }; + + class RaceSkillsBonusAdapter : public NestedColumnAdapter + { + public: + RaceSkillsBonusAdapter (); + + virtual void addRow(Record& record, int position) const; + + virtual void removeRow(Record& record, int rowToRemove) const; + + virtual void setTable(Record& record, + const NestedTableWrapperBase& nestedTable) const; + + virtual NestedTableWrapperBase* table(const Record& record) const; + + virtual QVariant getData(const Record& record, + int subRowIndex, int subColIndex) const; + + virtual void setData(Record& record, + const QVariant& value, int subRowIndex, int subColIndex) const; + + virtual int getColumnsCount(const Record& record) const; + + virtual int getRowsCount(const Record& record) const; + }; + + class CellListAdapter : public NestedColumnAdapter + { + public: + CellListAdapter (); + + virtual void addRow(Record& record, int position) const; + + virtual void removeRow(Record& record, int rowToRemove) const; + + virtual void setTable(Record& record, + const NestedTableWrapperBase& nestedTable) const; + + virtual NestedTableWrapperBase* table(const Record& record) const; + + virtual QVariant getData(const Record& record, + int subRowIndex, int subColIndex) const; + + virtual void setData(Record& record, + const QVariant& value, int subRowIndex, int subColIndex) const; + + virtual int getColumnsCount(const Record& record) const; + + virtual int getRowsCount(const Record& record) const; + }; } #endif // CSM_WOLRD_NESTEDCOLADAPTERIMP_H diff --git a/apps/opencs/model/world/refidadapterimp.cpp b/apps/opencs/model/world/refidadapterimp.cpp index c784e14ce..d31a9ceaa 100644 --- a/apps/opencs/model/world/refidadapterimp.cpp +++ b/apps/opencs/model/world/refidadapterimp.cpp @@ -468,7 +468,10 @@ CSMWorld::NpcColumns::NpcColumns (const ActorColumns& actorColumns) mClass(NULL), mFaction(NULL), mHair(NULL), - mHead(NULL) + mHead(NULL), + mAttributes(NULL), + mSkills(NULL), + mMisc(NULL) {} CSMWorld::NpcRefIdAdapter::NpcRefIdAdapter (const NpcColumns& columns) @@ -496,6 +499,17 @@ QVariant CSMWorld::NpcRefIdAdapter::getData (const RefIdColumn *column, const Re if (column==mColumns.mHead) return QString::fromUtf8 (record.get().mHead.c_str()); + if (column==mColumns.mAttributes || column==mColumns.mSkills) + { + if ((record.get().mFlags & ESM::NPC::Autocalc) != 0) + return QVariant(QVariant::UserType); + else + return true; + } + + if (column==mColumns.mMisc) + return true; + std::map::const_iterator iter = mColumns.mFlags.find (column); @@ -538,6 +552,338 @@ void CSMWorld::NpcRefIdAdapter::setData (const RefIdColumn *column, RefIdData& d } } +CSMWorld::NpcAttributesRefIdAdapter::NpcAttributesRefIdAdapter () +{} + +void CSMWorld::NpcAttributesRefIdAdapter::addNestedRow (const RefIdColumn *column, + RefIdData& data, int index, int position) const +{ + // Do nothing, this table cannot be changed by the user +} + +void CSMWorld::NpcAttributesRefIdAdapter::removeNestedRow (const RefIdColumn *column, + RefIdData& data, int index, int rowToRemove) const +{ + // Do nothing, this table cannot be changed by the user +} + +void CSMWorld::NpcAttributesRefIdAdapter::setNestedTable (const RefIdColumn* column, + RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const +{ + Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Npc))); + ESM::NPC npc = record.get(); + + // store the whole struct + npc.mNpdt52 = + static_cast > &>(nestedTable).mNestedTable.at(0); + + record.setModified (npc); +} + +CSMWorld::NestedTableWrapperBase* CSMWorld::NpcAttributesRefIdAdapter::nestedTable (const RefIdColumn* column, + const RefIdData& data, int index) const +{ + const Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Npc))); + + // return the whole struct + std::vector wrap; + wrap.push_back(record.get().mNpdt52); + // deleted by dtor of NestedTableStoring + return new NestedTableWrapper >(wrap); +} + +QVariant CSMWorld::NpcAttributesRefIdAdapter::getNestedData (const RefIdColumn *column, + const RefIdData& data, int index, int subRowIndex, int subColIndex) const +{ + const Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Npc))); + + const ESM::NPC::NPDTstruct52& npcStruct = record.get().mNpdt52; + + if (subColIndex == 0) + switch (subRowIndex) + { + case 0: return QString("Strength"); + case 1: return QString("Intelligence"); + case 2: return QString("Willpower"); + case 3: return QString("Agility"); + case 4: return QString("Speed"); + case 5: return QString("Endurance"); + case 6: return QString("Personality"); + case 7: return QString("Luck"); + default: return QVariant(); // throw an exception here? + } + else if (subColIndex == 1) + switch (subRowIndex) + { + case 0: return static_cast(npcStruct.mStrength); + case 1: return static_cast(npcStruct.mIntelligence); + case 2: return static_cast(npcStruct.mWillpower); + case 3: return static_cast(npcStruct.mAgility); + case 4: return static_cast(npcStruct.mSpeed); + case 5: return static_cast(npcStruct.mEndurance); + case 6: return static_cast(npcStruct.mPersonality); + case 7: return static_cast(npcStruct.mLuck); + default: return QVariant(); // throw an exception here? + } + else + return QVariant(); // throw an exception here? +} + +void CSMWorld::NpcAttributesRefIdAdapter::setNestedData (const RefIdColumn *column, + RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const +{ + Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (row, UniversalId::Type_Npc))); + ESM::NPC npc = record.get(); + ESM::NPC::NPDTstruct52& npcStruct = npc.mNpdt52; + + if (subColIndex == 1) + switch(subRowIndex) + { + case 0: npcStruct.mStrength = static_cast(value.toInt()); break; + case 1: npcStruct.mIntelligence = static_cast(value.toInt()); break; + case 2: npcStruct.mWillpower = static_cast(value.toInt()); break; + case 3: npcStruct.mAgility = static_cast(value.toInt()); break; + case 4: npcStruct.mSpeed = static_cast(value.toInt()); break; + case 5: npcStruct.mEndurance = static_cast(value.toInt()); break; + case 6: npcStruct.mPersonality = static_cast(value.toInt()); break; + case 7: npcStruct.mLuck = static_cast(value.toInt()); break; + default: return; // throw an exception here? + } + else + return; // throw an exception here? + + record.setModified (npc); +} + +int CSMWorld::NpcAttributesRefIdAdapter::getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const +{ + return 2; +} + +int CSMWorld::NpcAttributesRefIdAdapter::getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const +{ + // There are 8 attributes + return 8; +} + +CSMWorld::NpcSkillsRefIdAdapter::NpcSkillsRefIdAdapter () +{} + +void CSMWorld::NpcSkillsRefIdAdapter::addNestedRow (const RefIdColumn *column, + RefIdData& data, int index, int position) const +{ + // Do nothing, this table cannot be changed by the user +} + +void CSMWorld::NpcSkillsRefIdAdapter::removeNestedRow (const RefIdColumn *column, + RefIdData& data, int index, int rowToRemove) const +{ + // Do nothing, this table cannot be changed by the user +} + +void CSMWorld::NpcSkillsRefIdAdapter::setNestedTable (const RefIdColumn* column, + RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const +{ + Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Npc))); + ESM::NPC npc = record.get(); + + // store the whole struct + npc.mNpdt52 = + static_cast > &>(nestedTable).mNestedTable.at(0); + + record.setModified (npc); +} + +CSMWorld::NestedTableWrapperBase* CSMWorld::NpcSkillsRefIdAdapter::nestedTable (const RefIdColumn* column, + const RefIdData& data, int index) const +{ + const Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Npc))); + + // return the whole struct + std::vector wrap; + wrap.push_back(record.get().mNpdt52); + // deleted by dtor of NestedTableStoring + return new NestedTableWrapper >(wrap); +} + +QVariant CSMWorld::NpcSkillsRefIdAdapter::getNestedData (const RefIdColumn *column, + const RefIdData& data, int index, int subRowIndex, int subColIndex) const +{ + const Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Npc))); + + const ESM::NPC::NPDTstruct52& npcStruct = record.get().mNpdt52; + + if (subRowIndex < 0 || subRowIndex >= ESM::Skill::Length) + throw std::runtime_error ("index out of range"); + + if (subColIndex == 0) + return QString(ESM::Skill::sSkillNames[subRowIndex].c_str()); + else if (subColIndex == 1) + return static_cast(npcStruct.mSkills[subRowIndex]); + else + return QVariant(); // throw an exception here? +} + +void CSMWorld::NpcSkillsRefIdAdapter::setNestedData (const RefIdColumn *column, + RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const +{ + Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (row, UniversalId::Type_Npc))); + ESM::NPC npc = record.get(); + ESM::NPC::NPDTstruct52& npcStruct = npc.mNpdt52; + + if (subRowIndex < 0 || subRowIndex >= ESM::Skill::Length) + throw std::runtime_error ("index out of range"); + + if (subColIndex == 1) + npcStruct.mSkills[subRowIndex] = static_cast(value.toInt()); + else + return; // throw an exception here? + + record.setModified (npc); +} + +int CSMWorld::NpcSkillsRefIdAdapter::getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const +{ + return 2; +} + +int CSMWorld::NpcSkillsRefIdAdapter::getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const +{ + // There are 27 skills + return ESM::Skill::Length; +} + +CSMWorld::NpcMiscRefIdAdapter::NpcMiscRefIdAdapter () +{} + +CSMWorld::NpcMiscRefIdAdapter::~NpcMiscRefIdAdapter() +{} + +void CSMWorld::NpcMiscRefIdAdapter::addNestedRow (const RefIdColumn *column, + RefIdData& data, int index, int position) const +{ + throw std::logic_error ("cannot add a row to a fixed table"); +} + +void CSMWorld::NpcMiscRefIdAdapter::removeNestedRow (const RefIdColumn *column, + RefIdData& data, int index, int rowToRemove) const +{ + throw std::logic_error ("cannot remove a row to a fixed table"); +} + +void CSMWorld::NpcMiscRefIdAdapter::setNestedTable (const RefIdColumn* column, + RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const +{ + throw std::logic_error ("table operation not supported"); +} + +CSMWorld::NestedTableWrapperBase* CSMWorld::NpcMiscRefIdAdapter::nestedTable (const RefIdColumn* column, + const RefIdData& data, int index) const +{ + throw std::logic_error ("table operation not supported"); +} + +QVariant CSMWorld::NpcMiscRefIdAdapter::getNestedData (const RefIdColumn *column, + const RefIdData& data, int index, int subRowIndex, int subColIndex) const +{ + const Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Npc))); + + bool autoCalc = (record.get().mFlags & ESM::NPC::Autocalc) != 0; + + if (autoCalc) + switch (subColIndex) + { + case 0: return static_cast(record.get().mNpdt12.mLevel); + case 1: return QVariant(QVariant::UserType); + case 2: return QVariant(QVariant::UserType); + case 3: return QVariant(QVariant::UserType); + case 4: return QVariant(QVariant::UserType); + case 5: return static_cast(record.get().mNpdt12.mDisposition); + case 6: return static_cast(record.get().mNpdt12.mReputation); + case 7: return static_cast(record.get().mNpdt12.mRank); + case 8: return record.get().mNpdt12.mGold; + case 9: return record.get().mPersistent == true; + default: return QVariant(); // throw an exception here? + } + else + switch (subColIndex) + { + case 0: return static_cast(record.get().mNpdt52.mLevel); + case 1: return static_cast(record.get().mNpdt52.mFactionID); + case 2: return static_cast(record.get().mNpdt52.mHealth); + case 3: return static_cast(record.get().mNpdt52.mMana); + case 4: return static_cast(record.get().mNpdt52.mFatigue); + case 5: return static_cast(record.get().mNpdt52.mDisposition); + case 6: return static_cast(record.get().mNpdt52.mReputation); + case 7: return static_cast(record.get().mNpdt52.mRank); + case 8: return record.get().mNpdt52.mGold; + case 9: return record.get().mPersistent == true; + default: return QVariant(); // throw an exception here? + } +} + +void CSMWorld::NpcMiscRefIdAdapter::setNestedData (const RefIdColumn *column, + RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const +{ + Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (row, UniversalId::Type_Npc))); + ESM::NPC npc = record.get(); + + bool autoCalc = (record.get().mFlags & ESM::NPC::Autocalc) != 0; + + if (autoCalc) + switch(subColIndex) + { + case 0: npc.mNpdt12.mLevel = static_cast(value.toInt()); break; + case 1: return; + case 2: return; + case 3: return; + case 4: return; + case 5: npc.mNpdt12.mDisposition = static_cast(value.toInt()); break; + case 6: npc.mNpdt12.mReputation = static_cast(value.toInt()); break; + case 7: npc.mNpdt12.mRank = static_cast(value.toInt()); break; + case 8: npc.mNpdt12.mGold = value.toInt(); break; + case 9: npc.mPersistent = value.toBool(); break; + default: return; // throw an exception here? + } + else + switch(subColIndex) + { + case 0: npc.mNpdt52.mLevel = static_cast(value.toInt()); break; + case 1: npc.mNpdt52.mFactionID = static_cast(value.toInt()); break; + case 2: npc.mNpdt52.mHealth = static_cast(value.toInt()); break; + case 3: npc.mNpdt52.mMana = static_cast(value.toInt()); break; + case 4: npc.mNpdt52.mFatigue = static_cast(value.toInt()); break; + case 5: npc.mNpdt52.mDisposition = static_cast(value.toInt()); break; + case 6: npc.mNpdt52.mReputation = static_cast(value.toInt()); break; + case 7: npc.mNpdt52.mRank = static_cast(value.toInt()); break; + case 8: npc.mNpdt52.mGold = value.toInt(); break; + case 9: npc.mPersistent = value.toBool(); break; + default: return; // throw an exception here? + } + + record.setModified (npc); +} + +int CSMWorld::NpcMiscRefIdAdapter::getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const +{ + return 10; // Level, FactionID, Health, Mana, Fatigue, Disposition, Reputation, Rank, Gold, Persist +} + +int CSMWorld::NpcMiscRefIdAdapter::getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const +{ + return 1; // fixed at size 1 +} + CSMWorld::WeaponColumns::WeaponColumns (const EnchantableColumns& columns) : EnchantableColumns (columns) {} diff --git a/apps/opencs/model/world/refidadapterimp.hpp b/apps/opencs/model/world/refidadapterimp.hpp index 61e8115c0..869996da5 100644 --- a/apps/opencs/model/world/refidadapterimp.hpp +++ b/apps/opencs/model/world/refidadapterimp.hpp @@ -792,6 +792,9 @@ namespace CSMWorld 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 NpcColumns (const ActorColumns& actorColumns); }; @@ -842,8 +845,100 @@ namespace CSMWorld ///< If the data type does not match an exception is thrown. }; + class NestedRefIdAdapterBase; + class NpcAttributesRefIdAdapter : public NestedRefIdAdapterBase + { + public: + + NpcAttributesRefIdAdapter (); + + virtual void addNestedRow (const RefIdColumn *column, + RefIdData& data, int index, int position) const; + + virtual void removeNestedRow (const RefIdColumn *column, + RefIdData& data, int index, int rowToRemove) const; + + virtual void setNestedTable (const RefIdColumn* column, + RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const; + + virtual NestedTableWrapperBase* nestedTable (const RefIdColumn* column, + const RefIdData& data, int index) const; + + virtual QVariant getNestedData (const RefIdColumn *column, + const RefIdData& data, int index, int subRowIndex, int subColIndex) const; + + virtual void setNestedData (const RefIdColumn *column, + RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const; + + virtual int getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const; + + virtual int getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const; + }; + + class NpcSkillsRefIdAdapter : public NestedRefIdAdapterBase + { + public: + + NpcSkillsRefIdAdapter (); + + virtual void addNestedRow (const RefIdColumn *column, + RefIdData& data, int index, int position) const; + + virtual void removeNestedRow (const RefIdColumn *column, + RefIdData& data, int index, int rowToRemove) const; + + virtual void setNestedTable (const RefIdColumn* column, + RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const; + + virtual NestedTableWrapperBase* nestedTable (const RefIdColumn* column, + const RefIdData& data, int index) const; + + virtual QVariant getNestedData (const RefIdColumn *column, + const RefIdData& data, int index, int subRowIndex, int subColIndex) const; + + virtual void setNestedData (const RefIdColumn *column, + RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const; + + virtual int getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const; + + virtual int getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const; + }; + + class NpcMiscRefIdAdapter : public NestedRefIdAdapterBase + { + NpcMiscRefIdAdapter (const NpcMiscRefIdAdapter&); + NpcMiscRefIdAdapter& operator= (const NpcMiscRefIdAdapter&); + + public: + + NpcMiscRefIdAdapter (); + virtual ~NpcMiscRefIdAdapter(); + + virtual void addNestedRow (const RefIdColumn *column, + RefIdData& data, int index, int position) const; + + virtual void removeNestedRow (const RefIdColumn *column, + RefIdData& data, int index, int rowToRemove) const; + + virtual void setNestedTable (const RefIdColumn* column, + RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const; + + virtual NestedTableWrapperBase* nestedTable (const RefIdColumn* column, + const RefIdData& data, int index) const; + + virtual QVariant getNestedData (const RefIdColumn *column, + const RefIdData& data, int index, int subRowIndex, int subColIndex) const; + + virtual void setNestedData (const RefIdColumn *column, + RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const; + + virtual int getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const; + + virtual int getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const; + }; + template class EffectsListAdapter; @@ -1881,7 +1976,7 @@ namespace CSMWorld { switch (subColIndex) { - case 0: return QVariant(); // don't allow checkbox editor to be created + 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: diff --git a/apps/opencs/model/world/refidcollection.cpp b/apps/opencs/model/world/refidcollection.cpp index 1941c505a..cda19c87b 100644 --- a/apps/opencs/model/world/refidcollection.cpp +++ b/apps/opencs/model/world/refidcollection.cpp @@ -245,7 +245,8 @@ CSMWorld::RefIdCollection::RefIdCollection() actorsColumns.mServices.insert (std::make_pair (&mColumns.back(), sServiceTable[i].mFlag)); } - mColumns.push_back (RefIdColumn (Columns::ColumnId_AutoCalc, ColumnBase::Display_Boolean)); + mColumns.push_back (RefIdColumn (Columns::ColumnId_AutoCalc, ColumnBase::Display_Boolean, + ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue | ColumnBase::Flag_Dialogue_Refresh)); const RefIdColumn *autoCalc = &mColumns.back(); mColumns.push_back (RefIdColumn (Columns::ColumnId_ApparatusType, @@ -427,6 +428,62 @@ CSMWorld::RefIdCollection::RefIdCollection() npcColumns.mFlags.insert (std::make_pair (metalBlood, ESM::NPC::Metal)); + // Need a way to add a table of stats and values (rather than adding a long list of + // entries in the dialogue subview) E.g. attributes+stats(health, mana, fatigue), skills + // These needs to be driven from the autocalculated setting. + + // Nested table + mColumns.push_back(RefIdColumn (Columns::ColumnId_NpcAttributes, + ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue)); + npcColumns.mAttributes = &mColumns.back(); + std::map attrMap; + attrMap.insert(std::make_pair(UniversalId::Type_Npc, new NpcAttributesRefIdAdapter())); + mNestedAdapters.push_back (std::make_pair(&mColumns.back(), attrMap)); + mColumns.back().addColumn( + new RefIdColumn (Columns::ColumnId_NpcAttributes, CSMWorld::ColumnBase::Display_String, false, false)); + mColumns.back().addColumn( + new RefIdColumn (Columns::ColumnId_UChar, CSMWorld::ColumnBase::Display_Integer)); + + // Nested table + mColumns.push_back(RefIdColumn (Columns::ColumnId_NpcSkills, + ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue)); + npcColumns.mSkills = &mColumns.back(); + std::map skillsMap; + skillsMap.insert(std::make_pair(UniversalId::Type_Npc, new NpcSkillsRefIdAdapter())); + mNestedAdapters.push_back (std::make_pair(&mColumns.back(), skillsMap)); + mColumns.back().addColumn( + new RefIdColumn (Columns::ColumnId_NpcSkills, CSMWorld::ColumnBase::Display_String, false, false)); + mColumns.back().addColumn( + new RefIdColumn (Columns::ColumnId_UChar, CSMWorld::ColumnBase::Display_Integer)); + + // Nested list + mColumns.push_back(RefIdColumn (Columns::ColumnId_NpcMisc, + ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue | ColumnBase::Flag_Dialogue_List)); + npcColumns.mMisc = &mColumns.back(); + std::map miscMap; + miscMap.insert(std::make_pair(UniversalId::Type_Npc, new NpcMiscRefIdAdapter())); + mNestedAdapters.push_back (std::make_pair(&mColumns.back(), miscMap)); + mColumns.back().addColumn( + new RefIdColumn (Columns::ColumnId_NpcLevel, CSMWorld::ColumnBase::Display_Integer)); + mColumns.back().addColumn( + new RefIdColumn (Columns::ColumnId_NpcFactionID, CSMWorld::ColumnBase::Display_Integer)); + mColumns.back().addColumn( + new RefIdColumn (Columns::ColumnId_NpcHealth, CSMWorld::ColumnBase::Display_Integer)); + mColumns.back().addColumn( + new RefIdColumn (Columns::ColumnId_NpcMana, CSMWorld::ColumnBase::Display_Integer)); + mColumns.back().addColumn( + new RefIdColumn (Columns::ColumnId_NpcFatigue, CSMWorld::ColumnBase::Display_Integer)); + mColumns.back().addColumn( + new RefIdColumn (Columns::ColumnId_NpcDisposition, CSMWorld::ColumnBase::Display_Integer)); + mColumns.back().addColumn( + new RefIdColumn (Columns::ColumnId_NpcReputation, CSMWorld::ColumnBase::Display_Integer)); + mColumns.back().addColumn( + new RefIdColumn (Columns::ColumnId_NpcRank, CSMWorld::ColumnBase::Display_Integer)); + mColumns.back().addColumn( + new RefIdColumn (Columns::ColumnId_NpcGold, CSMWorld::ColumnBase::Display_Integer)); + mColumns.back().addColumn( + new RefIdColumn (Columns::ColumnId_NpcPersistence, CSMWorld::ColumnBase::Display_Boolean)); + WeaponColumns weaponColumns (enchantableColumns); mColumns.push_back (RefIdColumn (Columns::ColumnId_WeaponType, ColumnBase::Display_WeaponType)); diff --git a/apps/opencs/view/doc/newgame.cpp b/apps/opencs/view/doc/newgame.cpp index 98681c499..32b483728 100644 --- a/apps/opencs/view/doc/newgame.cpp +++ b/apps/opencs/view/doc/newgame.cpp @@ -66,3 +66,9 @@ void CSVDoc::NewGameDialogue::create() { emit createRequest (mAdjusterWidget->getPath()); } + +void CSVDoc::NewGameDialogue::reject() +{ + emit cancelCreateGame (); + QDialog::reject(); +} diff --git a/apps/opencs/view/doc/newgame.hpp b/apps/opencs/view/doc/newgame.hpp index 9ad7ea169..70e9d684b 100644 --- a/apps/opencs/view/doc/newgame.hpp +++ b/apps/opencs/view/doc/newgame.hpp @@ -36,11 +36,15 @@ namespace CSVDoc void createRequest (const boost::filesystem::path& file); + void cancelCreateGame (); + private slots: void stateChanged (bool valid); void create(); + + void reject(); }; } diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index 5e3df2739..fca9b2715 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -404,11 +404,7 @@ CSVDoc::View::View (ViewManager& viewManager, CSMDoc::Document *document, int to width = std::max(width, 300); height = std::max(height, 300); - // trick to get the window decorations and their sizes - show(); - hide(); - resize (width - (frameGeometry().width() - geometry().width()), - height - (frameGeometry().height() - geometry().height())); + resize (width, height); mSubViewWindow.setDockOptions (QMainWindow::AllowNestedDocks); diff --git a/apps/opencs/view/doc/viewmanager.cpp b/apps/opencs/view/doc/viewmanager.cpp index 6362f9659..97b7aac19 100644 --- a/apps/opencs/view/doc/viewmanager.cpp +++ b/apps/opencs/view/doc/viewmanager.cpp @@ -92,7 +92,8 @@ CSVDoc::ViewManager::ViewManager (CSMDoc::DocumentManager& documentManager) { CSMWorld::ColumnBase::Display_AiPackageType, CSMWorld::Columns::ColumnId_AiPackageType, false }, { CSMWorld::ColumnBase::Display_YesNo, CSMWorld::Columns::ColumnId_AiWanderRepeat, false }, { CSMWorld::ColumnBase::Display_InfoCondFunc, CSMWorld::Columns::ColumnId_InfoCondFunc, false }, - { CSMWorld::ColumnBase::Display_InfoCondComp, CSMWorld::Columns::ColumnId_InfoCondComp, false } + { CSMWorld::ColumnBase::Display_InfoCondComp, CSMWorld::Columns::ColumnId_InfoCondComp, false }, + { CSMWorld::ColumnBase::Display_RaceSkill, CSMWorld::Columns::ColumnId_RaceSkill, true }, }; for (std::size_t i=0; i + +CSVWorld::DialogueSpinBox::DialogueSpinBox(QWidget *parent) : QSpinBox(parent) +{ + setFocusPolicy(Qt::StrongFocus); +} + +void CSVWorld::DialogueSpinBox::focusInEvent(QFocusEvent *event) +{ + setFocusPolicy(Qt::WheelFocus); + QSpinBox::focusInEvent(event); +} + +void CSVWorld::DialogueSpinBox::focusOutEvent(QFocusEvent *event) +{ + setFocusPolicy(Qt::StrongFocus); + QSpinBox::focusOutEvent(event); +} + +void CSVWorld::DialogueSpinBox::wheelEvent(QWheelEvent *event) +{ + if (!hasFocus()) + event->ignore(); + else + QSpinBox::wheelEvent(event); +} + +CSVWorld::DialogueDoubleSpinBox::DialogueDoubleSpinBox(QWidget *parent) : QDoubleSpinBox(parent) +{ + setFocusPolicy(Qt::StrongFocus); +} + +void CSVWorld::DialogueDoubleSpinBox::focusInEvent(QFocusEvent *event) +{ + setFocusPolicy(Qt::WheelFocus); + QDoubleSpinBox::focusInEvent(event); +} + +void CSVWorld::DialogueDoubleSpinBox::focusOutEvent(QFocusEvent *event) +{ + setFocusPolicy(Qt::StrongFocus); + QDoubleSpinBox::focusOutEvent(event); +} + +void CSVWorld::DialogueDoubleSpinBox::wheelEvent(QWheelEvent *event) +{ + if (!hasFocus()) + event->ignore(); + else + QDoubleSpinBox::wheelEvent(event); +} diff --git a/apps/opencs/view/world/dialoguespinbox.hpp b/apps/opencs/view/world/dialoguespinbox.hpp new file mode 100644 index 000000000..a68e0c314 --- /dev/null +++ b/apps/opencs/view/world/dialoguespinbox.hpp @@ -0,0 +1,40 @@ +#ifndef CSV_WORLD_DIALOGUESPINBOX_H +#define CSV_WORLD_DIALOGUESPINBOX_H + +#include +#include + +namespace CSVWorld +{ + class DialogueSpinBox : public QSpinBox + { + Q_OBJECT + + public: + + DialogueSpinBox (QWidget *parent = 0); + + protected: + + virtual void focusInEvent(QFocusEvent *event); + virtual void focusOutEvent(QFocusEvent *event); + virtual void wheelEvent(QWheelEvent *event); + }; + + class DialogueDoubleSpinBox : public QDoubleSpinBox + { + Q_OBJECT + + public: + + DialogueDoubleSpinBox (QWidget *parent = 0); + + protected: + + virtual void focusInEvent(QFocusEvent *event); + virtual void focusOutEvent(QFocusEvent *event); + virtual void wheelEvent(QWheelEvent *event); + }; +} + +#endif // CSV_WORLD_DIALOGUESPINBOX_H diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index 647accd4c..66e8fcb7a 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include "../../model/world/nestedtableproxymodel.hpp" #include "../../model/world/columnbase.hpp" @@ -210,8 +211,17 @@ void CSVWorld::DialogueDelegateDispatcher::editorDataCommited(QWidget* editor, void CSVWorld::DialogueDelegateDispatcher::setEditorData (QWidget* editor, const QModelIndex& index) const { - CSMWorld::ColumnBase::Display display = static_cast - (mTable->headerData (index.column(), Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); + CSMWorld::ColumnBase::Display display = CSMWorld::ColumnBase::Display_None; + if (index.parent().isValid()) + { + display = static_cast + (static_cast(mTable)->nestedHeaderData (index.parent().column(), index.column(), Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); + } + else + { + display = static_cast + (mTable->headerData (index.column(), Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); + } QLabel* label = qobject_cast(editor); if(label) @@ -347,10 +357,13 @@ CSVWorld::DialogueDelegateDispatcher::~DialogueDelegateDispatcher() CSVWorld::EditWidget::~EditWidget() { for (unsigned i = 0; i < mNestedModels.size(); ++i) - { delete mNestedModels[i]; - } - delete mNestedTableDispatcher; + + if (mDispatcher) + delete mDispatcher; + + if (mNestedTableDispatcher) + delete mNestedTableDispatcher; } CSVWorld::EditWidget::EditWidget(QWidget *parent, @@ -359,7 +372,7 @@ CSVWorld::EditWidget::EditWidget(QWidget *parent, QScrollArea(parent), mWidgetMapper(NULL), mNestedTableMapper(NULL), -mDispatcher(this, table, commandDispatcher, document), +mDispatcher(NULL), mNestedTableDispatcher(NULL), mMainWidget(NULL), mTable(table), @@ -368,41 +381,42 @@ mDocument (document) { remake (row); - connect(&mDispatcher, SIGNAL(tableMimeDataDropped(QWidget*, const QModelIndex&, const CSMWorld::UniversalId&, const CSMDoc::Document*)), + connect(mDispatcher, SIGNAL(tableMimeDataDropped(QWidget*, const QModelIndex&, const CSMWorld::UniversalId&, const CSMDoc::Document*)), this, SIGNAL(tableMimeDataDropped(QWidget*, const QModelIndex&, const CSMWorld::UniversalId&, const CSMDoc::Document*))); } void CSVWorld::EditWidget::remake(int row) { - for (unsigned i = 0; i < mNestedModels.size(); ++i) - { - delete mNestedModels[i]; - } - mNestedModels.clear(); - delete mNestedTableDispatcher; - if (mMainWidget) { - delete mMainWidget; - mMainWidget = 0; + QWidget *del = this->takeWidget(); + del->deleteLater(); } mMainWidget = new QWidget (this); + for (unsigned i = 0; i < mNestedModels.size(); ++i) + delete mNestedModels[i]; + + mNestedModels.clear(); + + if (mDispatcher) + delete mDispatcher; + mDispatcher = new DialogueDelegateDispatcher(0/*this*/, mTable, mCommandDispatcher, mDocument); + + if (mNestedTableDispatcher) + delete mNestedTableDispatcher; + //not sure if widget mapper can handle deleting the widgets that were mapped if (mWidgetMapper) - { delete mWidgetMapper; - mWidgetMapper = 0; - } + + mWidgetMapper = new QDataWidgetMapper (this); + mWidgetMapper->setModel(mTable); + mWidgetMapper->setItemDelegate(mDispatcher); + if (mNestedTableMapper) - { delete mNestedTableMapper; - mNestedTableMapper = 0; - } - mWidgetMapper = new QDataWidgetMapper (this); - mWidgetMapper->setModel(mTable); - mWidgetMapper->setItemDelegate(&mDispatcher); QFrame* line = new QFrame(mMainWidget); line->setObjectName(QString::fromUtf8("line")); @@ -457,7 +471,14 @@ void CSVWorld::EditWidget::remake(int row) NestedTable* table = new NestedTable(mDocument, id, mNestedModels.back(), this); // FIXME: does not work well when enum delegates are used //table->resizeColumnsToContents(); - table->setEditTriggers(QAbstractItemView::SelectedClicked | QAbstractItemView::CurrentChanged); + + if(mTable->index(row, i).data().type() == QVariant::UserType) + { + table->setEditTriggers(QAbstractItemView::NoEditTriggers); + table->setEnabled(false); + } + else + table->setEditTriggers(QAbstractItemView::SelectedClicked | QAbstractItemView::CurrentChanged); int rows = mTable->rowCount(mTable->index(row, i)); int rowHeight = (rows == 0) ? table->horizontalHeader()->height() : table->rowHeight(0); @@ -469,14 +490,16 @@ void CSVWorld::EditWidget::remake(int row) new QLabel (mTable->headerData (i, Qt::Horizontal, Qt::DisplayRole).toString(), mMainWidget); label->setSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed); + if(mTable->index(row, i).data().type() == QVariant::UserType) + label->setEnabled(false); tablesLayout->addWidget(label); tablesLayout->addWidget(table); } else if (!(flags & CSMWorld::ColumnBase::Flag_Dialogue_List)) { - mDispatcher.makeDelegate (display); - QWidget* editor = mDispatcher.makeEditor (display, (mTable->index (row, i))); + mDispatcher->makeDelegate (display); + QWidget* editor = mDispatcher->makeEditor (display, (mTable->index (row, i))); if (editor) { @@ -499,26 +522,30 @@ void CSVWorld::EditWidget::remake(int row) unlockedLayout->addWidget (editor, unlocked, 1); ++unlocked; } + + if(mTable->index(row, i).data().type() == QVariant::UserType) + { + editor->setEnabled(false); + label->setEnabled(false); + } } } else { - mNestedModels.push_back(new CSMWorld::NestedTableProxyModel ( - static_cast(mTable)->index(row, i), - display, static_cast(mTable))); + CSMWorld::IdTree *tree = static_cast(mTable); mNestedTableMapper = new QDataWidgetMapper (this); - mNestedTableMapper->setModel(mNestedModels.back()); + mNestedTableMapper->setModel(tree); // FIXME: lack MIME support? mNestedTableDispatcher = - new DialogueDelegateDispatcher (this, mTable, mCommandDispatcher, mDocument, mNestedModels.back()); + new DialogueDelegateDispatcher (0/*this*/, mTable, mCommandDispatcher, mDocument, tree); + mNestedTableMapper->setRootIndex (tree->index(row, i)); mNestedTableMapper->setItemDelegate(mNestedTableDispatcher); - int columnCount = - mTable->columnCount(mTable->getModelIndex (mNestedModels.back()->getParentId(), i)); + int columnCount = tree->columnCount(tree->index(row, i)); for (int col = 0; col < columnCount; ++col) { - int displayRole = mNestedModels.back()->headerData (col, + int displayRole = tree->nestedHeaderData (i, col, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt(); CSMWorld::ColumnBase::Display display = @@ -528,16 +555,16 @@ void CSVWorld::EditWidget::remake(int row) // FIXME: assumed all columns are editable QWidget* editor = - mNestedTableDispatcher->makeEditor (display, mNestedModels.back()->index (0, col)); + mNestedTableDispatcher->makeEditor (display, tree->index (0, col, tree->index(row, i))); if (editor) { mNestedTableMapper->addMapping (editor, col); - std::string disString = mNestedModels.back()->headerData (col, + std::string disString = tree->nestedHeaderData (i, col, Qt::Horizontal, Qt::DisplayRole).toString().toStdString(); - // Need ot use Qt::DisplayRole in order to get the correct string + // Need to use Qt::DisplayRole in order to get the correct string // from CSMWorld::Columns - QLabel* label = new QLabel (mNestedModels.back()->headerData (col, + QLabel* label = new QLabel (tree->nestedHeaderData (i, col, Qt::Horizontal, Qt::DisplayRole).toString(), mMainWidget); label->setSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed); @@ -546,9 +573,15 @@ void CSVWorld::EditWidget::remake(int row) unlockedLayout->addWidget (label, unlocked, 0); unlockedLayout->addWidget (editor, unlocked, 1); ++unlocked; + + if(tree->index(0, col, tree->index(row, i)).data().type() == QVariant::UserType) + { + editor->setEnabled(false); + label->setEnabled(false); + } } } - mNestedTableMapper->setCurrentModelIndex(mNestedModels.back()->index(0, 0)); + mNestedTableMapper->setCurrentModelIndex(tree->index(0, 0, tree->index(row, i))); } } } @@ -578,6 +611,7 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM mCommandDispatcher (document, CSMWorld::UniversalId::getParentType (id.getType())) { connect(mTable, SIGNAL(dataChanged (const QModelIndex&, const QModelIndex&)), this, SLOT(dataChanged(const QModelIndex&))); + connect(mTable, SIGNAL(rowsAboutToBeRemoved(const QModelIndex&, int, int)), this, SLOT(rowsAboutToBeRemoved(const QModelIndex&, int, int))); changeCurrentId(id.getId()); @@ -740,6 +774,9 @@ void CSVWorld::DialogueSubView::nextId () void CSVWorld::DialogueSubView::setEditLock (bool locked) { + if (!mEditWidget) // hack to indicate that mCurrentId is no longer valid + return; + mLocked = locked; QModelIndex currentIndex(mTable->getModelIndex(mCurrentId, 0)); @@ -758,11 +795,47 @@ void CSVWorld::DialogueSubView::dataChanged (const QModelIndex & index) { QModelIndex currentIndex(mTable->getModelIndex(mCurrentId, 0)); - if (currentIndex.isValid() && index.row() == currentIndex.row()) + if (currentIndex.isValid() && + (index.parent().isValid() ? index.parent().row() : index.row()) == currentIndex.row()) { CSMWorld::RecordBase::State state = static_cast(mTable->data (mTable->index (currentIndex.row(), 1)).toInt()); mEditWidget->setDisabled (state==CSMWorld::RecordBase::State_Deleted || mLocked); + + // Check if the changed data should force refresh (rebuild) the dialogue subview + int flags = 0; + if (index.parent().isValid()) // TODO: check that index is topLeft + { + flags = static_cast(mTable)->nestedHeaderData (index.parent().column(), + index.column(), Qt::Horizontal, CSMWorld::ColumnBase::Role_Flags).toInt(); + } + else + { + flags = mTable->headerData (index.column(), + Qt::Horizontal, CSMWorld::ColumnBase::Role_Flags).toInt(); + } + + if (flags & CSMWorld::ColumnBase::Flag_Dialogue_Refresh) + { + int y = mEditWidget->verticalScrollBar()->value(); + mEditWidget->remake (index.parent().isValid() ? index.parent().row() : index.row()); + mEditWidget->verticalScrollBar()->setValue(y); + } + } +} + +void CSVWorld::DialogueSubView::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) +{ + QModelIndex currentIndex(mTable->getModelIndex(mCurrentId, 0)); + + if (currentIndex.isValid() && currentIndex.row() >= start && currentIndex.row() <= end) + { + if(mEditWidget) + { + delete mEditWidget; + mEditWidget = 0; + } + emit closeRequest(this); } } diff --git a/apps/opencs/view/world/dialoguesubview.hpp b/apps/opencs/view/world/dialoguesubview.hpp index b5a44d266..6cbd8ad77 100644 --- a/apps/opencs/view/world/dialoguesubview.hpp +++ b/apps/opencs/view/world/dialoguesubview.hpp @@ -165,7 +165,7 @@ namespace CSVWorld Q_OBJECT QDataWidgetMapper *mWidgetMapper; QDataWidgetMapper *mNestedTableMapper; - DialogueDelegateDispatcher mDispatcher; + DialogueDelegateDispatcher *mDispatcher; DialogueDelegateDispatcher *mNestedTableDispatcher; QWidget* mMainWidget; CSMWorld::IdTable* mTable; @@ -235,6 +235,8 @@ namespace CSVWorld const CSMDoc::Document* document); void requestFocus (const std::string& id); + + void rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end); }; } diff --git a/apps/opencs/view/world/scriptedit.cpp b/apps/opencs/view/world/scriptedit.cpp index 2f0d82ae1..ad2cddbf8 100644 --- a/apps/opencs/view/world/scriptedit.cpp +++ b/apps/opencs/view/world/scriptedit.cpp @@ -31,11 +31,11 @@ bool CSVWorld::ScriptEdit::event (QEvent *event) if (event->type()==QEvent::ShortcutOverride) { QKeyEvent *keyEvent = static_cast (event); - + if (keyEvent->matches (QKeySequence::Undo) || keyEvent->matches (QKeySequence::Redo)) return true; } - + return QPlainTextEdit::event (event); } @@ -92,13 +92,16 @@ CSVWorld::ScriptEdit::ScriptEdit (const CSMDoc::Document& document, ScriptHighli connect (&mUpdateTimer, SIGNAL (timeout()), this, SLOT (updateHighlighting())); + CSMSettings::UserSettings &userSettings = CSMSettings::UserSettings::instance(); + connect (&userSettings, SIGNAL (userSettingUpdated(const QString &, const QStringList &)), + this, SLOT (updateUserSetting (const QString &, const QStringList &))); + mUpdateTimer.setSingleShot (true); // TODO: provide a font selector dialogue mMonoFont.setStyleHint(QFont::TypeWriter); - std::string useMonoFont = - CSMSettings::UserSettings::instance().setting("script-editor/mono-font", "true").toStdString(); - if (useMonoFont == "true") + + if (userSettings.setting("script-editor/mono-font", "true") == "true") setFont(mMonoFont); mLineNumberArea = new LineNumberArea(this); @@ -107,10 +110,13 @@ CSVWorld::ScriptEdit::ScriptEdit (const CSMDoc::Document& document, ScriptHighli connect(this, SIGNAL(blockCountChanged(int)), this, SLOT(updateLineNumberAreaWidth(int))); connect(this, SIGNAL(updateRequest(QRect,int)), this, SLOT(updateLineNumberArea(QRect,int))); - std::string showStatusBar = - CSMSettings::UserSettings::instance().settingValue("script-editor/show-linenum").toStdString(); + showLineNum(userSettings.settingValue("script-editor/show-linenum") == "true"); +} - showLineNum(showStatusBar == "true"); +void CSVWorld::ScriptEdit::updateUserSetting (const QString &name, const QStringList &list) +{ + if (mHighlighter->updateUserSetting (name, list)) + updateHighlighting(); } void CSVWorld::ScriptEdit::showLineNum(bool show) diff --git a/apps/opencs/view/world/scriptedit.hpp b/apps/opencs/view/world/scriptedit.hpp index fb577e60e..d17abf24e 100644 --- a/apps/opencs/view/world/scriptedit.hpp +++ b/apps/opencs/view/world/scriptedit.hpp @@ -56,7 +56,7 @@ namespace CSVWorld protected: bool event (QEvent *event); - + public: ScriptEdit (const CSMDoc::Document& document, ScriptHighlighter::Mode mode, @@ -96,7 +96,12 @@ namespace CSVWorld void updateHighlighting(); void updateLineNumberAreaWidth(int newBlockCount); + void updateLineNumberArea(const QRect &, int); + + public slots: + + void updateUserSetting (const QString &name, const QStringList &list); }; class LineNumberArea : public QWidget diff --git a/apps/opencs/view/world/scripthighlighter.cpp b/apps/opencs/view/world/scripthighlighter.cpp index 6dda8d4fa..4923a44d8 100644 --- a/apps/opencs/view/world/scripthighlighter.cpp +++ b/apps/opencs/view/world/scripthighlighter.cpp @@ -6,6 +6,8 @@ #include #include +#include "../../model/settings/usersettings.hpp" + bool CSVWorld::ScriptHighlighter::parseInt (int value, const Compiler::TokenLoc& loc, Compiler::Scanner& scanner) { @@ -78,46 +80,77 @@ CSVWorld::ScriptHighlighter::ScriptHighlighter (const CSMWorld::Data& data, Mode : QSyntaxHighlighter (parent), Compiler::Parser (mErrorHandler, mContext), mContext (data), mMode (mode) { - /// \todo replace this with user settings + CSMSettings::UserSettings &userSettings = CSMSettings::UserSettings::instance(); + + QColor color = QColor(); + { + color.setNamedColor(userSettings.setting("script-editor/colour-int", "Dark magenta")); + if (!color.isValid()) + color = QColor(Qt::darkMagenta); + QTextCharFormat format; - format.setForeground (Qt::darkMagenta); + format.setForeground (color); mScheme.insert (std::make_pair (Type_Int, format)); } { + color.setNamedColor(userSettings.setting ("script-editor/colour-float", "Magenta")); + if (!color.isValid()) + color = QColor(Qt::magenta); + QTextCharFormat format; - format.setForeground (Qt::magenta); + format.setForeground (color); mScheme.insert (std::make_pair (Type_Float, format)); } { + color.setNamedColor(userSettings.setting ("script-editor/colour-name", "Gray")); + if (!color.isValid()) + color = QColor(Qt::gray); + QTextCharFormat format; - format.setForeground (Qt::gray); + format.setForeground (color); mScheme.insert (std::make_pair (Type_Name, format)); } { + color.setNamedColor(userSettings.setting ("script-editor/colour-keyword", "Red")); + if (!color.isValid()) + color = QColor(Qt::red); + QTextCharFormat format; - format.setForeground (Qt::red); + format.setForeground (color); mScheme.insert (std::make_pair (Type_Keyword, format)); } { + color.setNamedColor(userSettings.setting ("script-editor/colour-special", "Dark yellow")); + if (!color.isValid()) + color = QColor(Qt::darkYellow); + QTextCharFormat format; - format.setForeground (Qt::darkYellow); + format.setForeground (color); mScheme.insert (std::make_pair (Type_Special, format)); } { + color.setNamedColor(userSettings.setting ("script-editor/colour-comment", "Green")); + if (!color.isValid()) + color = QColor(Qt::green); + QTextCharFormat format; - format.setForeground (Qt::green); + format.setForeground (color); mScheme.insert (std::make_pair (Type_Comment, format)); } { + color.setNamedColor(userSettings.setting ("script-editor/colour-id", "Blue")); + if (!color.isValid()) + color = QColor(Qt::blue); + QTextCharFormat format; - format.setForeground (Qt::blue); + format.setForeground (color); mScheme.insert (std::make_pair (Type_Id, format)); } @@ -143,3 +176,86 @@ void CSVWorld::ScriptHighlighter::invalidateIds() { mContext.invalidateIds(); } + +bool CSVWorld::ScriptHighlighter::updateUserSetting (const QString &name, const QStringList &list) +{ + if (list.empty()) + return false; + + QColor color = QColor(); + + if (name == "script-editor/colour-int") + { + color.setNamedColor(list.at(0)); + if (!color.isValid()) + return false; + + QTextCharFormat format; + format.setForeground (color); + mScheme[Type_Int] = format; + } + else if (name == "script-editor/colour-float") + { + color.setNamedColor(list.at(0)); + if (!color.isValid()) + return false; + + QTextCharFormat format; + format.setForeground (color); + mScheme[Type_Float] = format; + } + else if (name == "script-editor/colour-name") + { + color.setNamedColor(list.at(0)); + if (!color.isValid()) + return false; + + QTextCharFormat format; + format.setForeground (color); + mScheme[Type_Name] = format; + } + else if (name == "script-editor/colour-keyword") + { + color.setNamedColor(list.at(0)); + if (!color.isValid()) + return false; + + QTextCharFormat format; + format.setForeground (color); + mScheme[Type_Keyword] = format; + } + else if (name == "script-editor/colour-special") + { + color.setNamedColor(list.at(0)); + if (!color.isValid()) + return false; + + QTextCharFormat format; + format.setForeground (color); + mScheme[Type_Special] = format; + } + else if (name == "script-editor/colour-comment") + { + color.setNamedColor(list.at(0)); + if (!color.isValid()) + return false; + + QTextCharFormat format; + format.setForeground (color); + mScheme[Type_Comment] = format; + } + else if (name == "script-editor/colour-id") + { + color.setNamedColor(list.at(0)); + if (!color.isValid()) + return false; + + QTextCharFormat format; + format.setForeground (color); + mScheme[Type_Id] = format; + } + else + return false; + + return true; +} diff --git a/apps/opencs/view/world/scripthighlighter.hpp b/apps/opencs/view/world/scripthighlighter.hpp index 953f2f953..6f1f58e82 100644 --- a/apps/opencs/view/world/scripthighlighter.hpp +++ b/apps/opencs/view/world/scripthighlighter.hpp @@ -87,6 +87,8 @@ namespace CSVWorld virtual void highlightBlock (const QString& text); void invalidateIds(); + + bool updateUserSetting (const QString &name, const QStringList &list); }; } diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index b8972edbe..bb3dfa4d3 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -649,6 +649,10 @@ void CSVWorld::Table::tableSizeUpdate() } emit tableSizeChanged (size, deleted, modified); + + // not really related to tableSizeUpdate() but placed here for convenience rather than + // creating a bunch of extra connections & slot + mProxyModel->refreshFilter(); } void CSVWorld::Table::selectionSizeUpdate() diff --git a/apps/opencs/view/world/util.cpp b/apps/opencs/view/world/util.cpp index a11b5bdde..5452214ef 100644 --- a/apps/opencs/view/world/util.cpp +++ b/apps/opencs/view/world/util.cpp @@ -9,8 +9,6 @@ #include #include #include -#include -#include #include #include #include @@ -19,7 +17,7 @@ #include "../../model/world/commands.hpp" #include "../../model/world/tablemimedata.hpp" #include "../../model/world/commanddispatcher.hpp" - +#include "dialoguespinbox.hpp" #include "scriptedit.hpp" CSVWorld::NastyTableModelHack::NastyTableModelHack (QAbstractItemModel& model) @@ -174,7 +172,7 @@ QWidget *CSVWorld::CommandDelegate::createEditor (QWidget *parent, const QStyleO case CSMWorld::ColumnBase::Display_Integer: { - QSpinBox *sb = new QSpinBox(parent); + DialogueSpinBox *sb = new DialogueSpinBox(parent); sb->setRange(INT_MIN, INT_MAX); return sb; } @@ -185,7 +183,7 @@ QWidget *CSVWorld::CommandDelegate::createEditor (QWidget *parent, const QStyleO case CSMWorld::ColumnBase::Display_Float: { - QDoubleSpinBox *dsb = new QDoubleSpinBox(parent); + DialogueDoubleSpinBox *dsb = new DialogueDoubleSpinBox(parent); dsb->setRange(-FLT_MAX, FLT_MAX); dsb->setSingleStep(0.01f); dsb->setDecimals(3); diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index 792a2674e..901d9ad42 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -17,7 +17,7 @@ #if defined(_WIN32) // For OutputDebugString #define WIN32_LEAN_AND_MEAN -#include +#include // makes __argc and __argv available on windows #include #endif diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index 36f7b021c..5a8c736d9 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -146,7 +146,7 @@ namespace MWClass if (ptr.getCellRef().getTeleport()) { - boost::shared_ptr action(new MWWorld::ActionTeleport (ptr.getCellRef().getDestCell(), ptr.getCellRef().getDoorDest())); + boost::shared_ptr action(new MWWorld::ActionTeleport (ptr.getCellRef().getDestCell(), ptr.getCellRef().getDoorDest(), true)); action->setSound(openSound); diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index 692cea952..0cb0475c9 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -291,7 +291,10 @@ namespace MWGui MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); } else + { MWBase::Environment::get().getDialogueManager()->goodbyeSelected(); + mTopicsList->scrollToTop(); + } } void DialogueWindow::onWindowResize(MyGUI::Window* _sender) diff --git a/apps/openmw/mwgui/travelwindow.cpp b/apps/openmw/mwgui/travelwindow.cpp index 4da1ab33a..ba6fc2a78 100644 --- a/apps/openmw/mwgui/travelwindow.cpp +++ b/apps/openmw/mwgui/travelwindow.cpp @@ -183,7 +183,7 @@ namespace MWGui MWBase::Environment::get().getDialogueManager()->goodbyeSelected(); // Teleports any followers, too. - MWWorld::ActionTeleport action(interior ? cellname : "", pos); + MWWorld::ActionTeleport action(interior ? cellname : "", pos, true); action.execute(player); MWBase::Environment::get().getWindowManager()->fadeScreenOut(0); diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index cdca6eeaa..a47da7bf4 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -679,7 +679,7 @@ namespace MWMechanics if (markedCell) { MWWorld::ActionTeleport action(markedCell->isExterior() ? "" : markedCell->getCell()->mName, - markedPosition); + markedPosition, false); action.execute(target); } } diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index da44d7f20..1f05d93b6 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -227,7 +227,11 @@ const NpcAnimation::PartBoneMap NpcAnimation::sPartList = createPartListMap(); NpcAnimation::~NpcAnimation() { - if (!mListenerDisabled) + if (!mListenerDisabled + // No need to getInventoryStore() to reset, if none exists + // This is to avoid triggering the listener via ensureCustomData()->autoEquip()->fireEquipmentChanged() + // all from within this destructor. ouch! + && mPtr.getRefData().getCustomData()) mPtr.getClass().getInventoryStore(mPtr).setListener(NULL, mPtr); } diff --git a/apps/openmw/mwworld/actionteleport.cpp b/apps/openmw/mwworld/actionteleport.cpp index 8bbb08008..fccd176a8 100644 --- a/apps/openmw/mwworld/actionteleport.cpp +++ b/apps/openmw/mwworld/actionteleport.cpp @@ -25,23 +25,26 @@ namespace namespace MWWorld { ActionTeleport::ActionTeleport (const std::string& cellName, - const ESM::Position& position) - : Action (true), mCellName (cellName), mPosition (position) + const ESM::Position& position, bool teleportFollowers) + : Action (true), mCellName (cellName), mPosition (position), mTeleportFollowers(teleportFollowers) { } void ActionTeleport::executeImp (const Ptr& actor) { - //find any NPC that is following the actor and teleport him too - std::set followers; - getFollowers(actor, followers); - for(std::set::iterator it = followers.begin();it != followers.end();++it) + if (mTeleportFollowers) { - MWWorld::Ptr follower = *it; - if (Ogre::Vector3(follower.getRefData().getPosition().pos).squaredDistance( - Ogre::Vector3( actor.getRefData().getPosition().pos)) - <= 800*800) - teleport(*it); + //find any NPC that is following the actor and teleport him too + std::set followers; + getFollowers(actor, followers); + for(std::set::iterator it = followers.begin();it != followers.end();++it) + { + MWWorld::Ptr follower = *it; + if (Ogre::Vector3(follower.getRefData().getPosition().pos).squaredDistance( + Ogre::Vector3( actor.getRefData().getPosition().pos)) + <= 800*800) + teleport(*it); + } } teleport(actor); diff --git a/apps/openmw/mwworld/actionteleport.hpp b/apps/openmw/mwworld/actionteleport.hpp index 9ca664de8..6191ee9f6 100644 --- a/apps/openmw/mwworld/actionteleport.hpp +++ b/apps/openmw/mwworld/actionteleport.hpp @@ -13,6 +13,7 @@ namespace MWWorld { std::string mCellName; ESM::Position mPosition; + bool mTeleportFollowers; /// Teleports this actor and also teleports anyone following that actor. virtual void executeImp (const Ptr& actor); @@ -22,8 +23,9 @@ namespace MWWorld public: - ActionTeleport (const std::string& cellName, const ESM::Position& position); + ActionTeleport (const std::string& cellName, const ESM::Position& position, bool teleportFollowers); ///< If cellName is empty, an exterior cell is assumed. + /// @param teleportFollowers Whether to teleport any following actors of the target actor as well. }; } diff --git a/apps/openmw/mwworld/localscripts.cpp b/apps/openmw/mwworld/localscripts.cpp index 5be66ccea..e30246f7c 100644 --- a/apps/openmw/mwworld/localscripts.cpp +++ b/apps/openmw/mwworld/localscripts.cpp @@ -93,7 +93,7 @@ std::pair MWWorld::LocalScripts::getNext() void MWWorld::LocalScripts::add (const std::string& scriptName, const Ptr& ptr) { - if (const ESM::Script *script = mStore.get().find (scriptName)) + if (const ESM::Script *script = mStore.get().search (scriptName)) { try { @@ -108,6 +108,10 @@ void MWWorld::LocalScripts::add (const std::string& scriptName, const Ptr& ptr) << " because an exception has been thrown: " << exception.what() << std::endl; } } + else + std::cerr + << "failed to add local script " << scriptName + << " because the script does not exist." << std::endl; } void MWWorld::LocalScripts::addCell (CellStore *cell) diff --git a/apps/openmw/mwworld/store.hpp b/apps/openmw/mwworld/store.hpp index 0e2a93d2c..5d5028559 100644 --- a/apps/openmw/mwworld/store.hpp +++ b/apps/openmw/mwworld/store.hpp @@ -366,6 +366,19 @@ namespace MWWorld inserted.first->second = scpt; } + template <> + inline void Store::load(ESM::ESMReader &esm, const std::string &id) + { + ESM::StartScript s; + s.load(esm); + s.mId = Misc::StringUtils::toLower(s.mId); + std::pair inserted = mStatic.insert(std::make_pair(s.mId, s)); + if (inserted.second) + mShared.push_back(&inserted.first->second); + else + inserted.first->second = s; + } + template <> class Store : public StoreBase { diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 2f9a13f78..7528e9286 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1,6 +1,6 @@ #include "worldimp.hpp" -#ifdef _WIN32 +#if defined(_WIN32) && !defined(__MINGW32__) #include #elif defined HAVE_UNORDERED_MAP #include @@ -2876,7 +2876,7 @@ namespace MWWorld if ( !closestMarker.mCell->isExterior() ) cellName = closestMarker.mCell->getCell()->mName; - MWWorld::ActionTeleport action(cellName, closestMarker.getRefData().getPosition()); + MWWorld::ActionTeleport action(cellName, closestMarker.getRefData().getPosition(), false); action.execute(ptr); } diff --git a/cmake/FindMyGUI.cmake b/cmake/FindMyGUI.cmake index b52d94f98..e883a29c1 100644 --- a/cmake/FindMyGUI.cmake +++ b/cmake/FindMyGUI.cmake @@ -19,7 +19,46 @@ include(PreprocessorUtils) # ENDIF (MYGUI_LIBRARIES AND MYGUI_INCLUDE_DIRS) IF (WIN32) #Windows + MESSAGE(STATUS "Looking for MyGUI") + + IF(MINGW) + + FIND_PATH ( MYGUI_INCLUDE_DIRS MyGUI.h PATH_SUFFIXES MYGUI) + FIND_PATH ( MYGUI_PLATFORM_INCLUDE_DIRS MyGUI_OgrePlatform.h PATH_SUFFIXES MYGUI) + FIND_LIBRARY ( MYGUI_LIBRARIES_REL NAMES + libMyGUIEngine${CMAKE_SHARED_LIBRARY_SUFFIX} + libMyGUI.OgrePlatform${CMAKE_STATIC_LIBRARY_SUFFIX} + HINTS + ${MYGUI_LIB_DIR} + PATH_SUFFIXES "" release relwithdebinfo minsizerel ) + + FIND_LIBRARY ( MYGUI_LIBRARIES_DBG NAMES + libMyGUIEngine_d${CMAKE_SHARED_LIBRARY_SUFFIX} + libMyGUI.OgrePlatform_d${CMAKE_STATIC_LIBRARY_SUFFIX} + HINTS + ${MYGUI_LIB_DIR} + PATH_SUFFIXES "" debug ) + + FIND_LIBRARY ( MYGUI_PLATFORM_LIBRARIES_REL NAMES + libMyGUI.OgrePlatform${CMAKE_STATIC_LIBRARY_SUFFIX} + HINTS + ${MYGUI_LIB_DIR} + PATH_SUFFIXES "" release relwithdebinfo minsizerel ) + + FIND_LIBRARY ( MYGUI_PLATFORM_LIBRARIES_DBG NAMES + MyGUI.OgrePlatform_d${CMAKE_STATIC_LIBRARY_SUFFIX} + HINTS + ${MYGUI_LIB_DIR} + PATH_SUFFIXES "" debug ) + + make_library_set ( MYGUI_LIBRARIES ) + make_library_set ( MYGUI_PLATFORM_LIBRARIES ) + + MESSAGE ("${MYGUI_LIBRARIES}") + MESSAGE ("${MYGUI_PLATFORM_LIBRARIES}") + ENDIF(MINGW) + SET(MYGUISDK $ENV{MYGUI_HOME}) IF (MYGUISDK) findpkg_begin ( "MYGUI" ) diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 51cb55aee..7b0094b3f 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -136,6 +136,9 @@ set (ESM_UI ${CMAKE_SOURCE_DIR}/files/ui/contentselector.ui ) find_package(Qt4 COMPONENTS QtCore QtGui) +if(MINGW) +find_package(Bullet REQUIRED COMPONENTS Collision) +endif() if(QT_QTGUI_LIBRARY AND QT_QTCORE_LIBRARY) add_component_qt_dir (contentselector @@ -183,6 +186,14 @@ if (GIT_CHECKOUT) add_dependencies (components git-version) endif (GIT_CHECKOUT) +if(MINGW) +target_link_libraries(components ${QT_LIBRARIES} ${BULLET_LIBRARIES}) +endif() + +if (WIN32) +target_link_libraries(components shlwapi) +endif() + # Fix for not visible pthreads functions for linker with glibc 2.15 if (UNIX AND NOT APPLE) target_link_libraries(components ${CMAKE_THREAD_LIBS_INIT}) diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index c56ee2ffb..a16e653c3 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -531,7 +531,7 @@ namespace Compiler extensions.registerInstruction("placeitemcell","ccffff",opcodePlaceItemCell); extensions.registerInstruction("placeitem","cffff",opcodePlaceItem); extensions.registerInstruction("placeatpc","clfl",opcodePlaceAtPc); - extensions.registerInstruction("placeatme","clfl",opcodePlaceAtMe,opcodePlaceAtMeExplicit); + extensions.registerInstruction("placeatme","clflX",opcodePlaceAtMe,opcodePlaceAtMeExplicit); extensions.registerInstruction("modscale","f",opcodeModScale,opcodeModScaleExplicit); extensions.registerInstruction("rotate","cf",opcodeRotate,opcodeRotateExplicit); extensions.registerInstruction("rotateworld","cf",opcodeRotateWorld,opcodeRotateWorldExplicit); diff --git a/components/esm/loadcell.cpp b/components/esm/loadcell.cpp index 0f8897c48..94f4b0b6e 100644 --- a/components/esm/loadcell.cpp +++ b/components/esm/loadcell.cpp @@ -18,9 +18,11 @@ namespace ///< Translate 8bit/24bit code (stored in refNum.mIndex) into a proper refNum void adjustRefNum (ESM::RefNum& refNum, ESM::ESMReader& reader) { - int local = (refNum.mIndex & 0xff000000) >> 24; + unsigned int local = (refNum.mIndex & 0xff000000) >> 24; - if (local) + // If we have an index value that does not make sense, assume that it was an addition + // by the present plugin (but a faulty one) + if (local && local <= reader.getGameFiles().size()) { // If the most significant 8 bits are used, then this reference already exists. // In this case, do not spawn a new reference, but overwrite the old one. diff --git a/components/files/configurationmanager.hpp b/components/files/configurationmanager.hpp index b0b7fea9a..24b08c523 100644 --- a/components/files/configurationmanager.hpp +++ b/components/files/configurationmanager.hpp @@ -1,7 +1,7 @@ #ifndef COMPONENTS_FILES_CONFIGURATIONMANAGER_HPP #define COMPONENTS_FILES_CONFIGURATIONMANAGER_HPP -#ifdef _WIN32 +#if defined(_WIN32) && !defined(__MINGW32__) #include #elif defined HAVE_UNORDERED_MAP #include diff --git a/components/files/windowspath.cpp b/components/files/windowspath.cpp index 0df782702..ece4049a8 100644 --- a/components/files/windowspath.cpp +++ b/components/files/windowspath.cpp @@ -6,9 +6,7 @@ #include #include -#include - -#pragma comment(lib, "Shlwapi.lib") +#include #include namespace bconv = boost::locale::conv; diff --git a/components/widgets/list.cpp b/components/widgets/list.cpp index 5a79de3d1..df7e7d61d 100644 --- a/components/widgets/list.cpp +++ b/components/widgets/list.cpp @@ -48,7 +48,7 @@ namespace Gui const int _scrollBarWidth = 20; // fetch this from skin? const int scrollBarWidth = scrollbarShown ? _scrollBarWidth : 0; const int spacing = 3; - size_t viewPosition = -mScrollView->getViewOffset().top; + int viewPosition = -mScrollView->getViewOffset().top; while (mScrollView->getChildCount()) { @@ -99,10 +99,10 @@ namespace Gui if (!scrollbarShown && mItemHeight > mClient->getSize().height) redraw(true); - size_t viewRange = mScrollView->getCanvasSize().height; + int viewRange = mScrollView->getCanvasSize().height; if(viewPosition > viewRange) viewPosition = viewRange; - mScrollView->setViewOffset(MyGUI::IntPoint(0, viewPosition * -1)); + mScrollView->setViewOffset(MyGUI::IntPoint(0, -viewPosition)); } void MWList::setPropertyOverride(const std::string &_key, const std::string &_value) @@ -157,4 +157,8 @@ namespace Gui return mScrollView->findWidget (getName() + "_item_" + name)->castType(); } + void MWList::scrollToTop() + { + mScrollView->setViewOffset(MyGUI::IntPoint(0, 0)); + } } diff --git a/components/widgets/list.hpp b/components/widgets/list.hpp index 6ee2ef3f2..aed69da79 100644 --- a/components/widgets/list.hpp +++ b/components/widgets/list.hpp @@ -46,6 +46,8 @@ namespace Gui MyGUI::Button* getItemWidget(const std::string& name); ///< get widget for an item name, useful to set up tooltip + void scrollToTop(); + virtual void setPropertyOverride(const std::string& _key, const std::string& _value); protected: diff --git a/extern/oics/tinyxml.cpp b/extern/oics/tinyxml.cpp index 21b2d9c9a..5d8eb475a 100644 --- a/extern/oics/tinyxml.cpp +++ b/extern/oics/tinyxml.cpp @@ -32,7 +32,7 @@ distribution. #include "tinyxml.h" #ifdef _WIN32 -#include // import MultiByteToWideChar +#include // import MultiByteToWideChar #endif diff --git a/extern/osg-ffmpeg-videoplayer/audiodecoder.hpp b/extern/osg-ffmpeg-videoplayer/audiodecoder.hpp index b05b16d42..fe36ec39f 100644 --- a/extern/osg-ffmpeg-videoplayer/audiodecoder.hpp +++ b/extern/osg-ffmpeg-videoplayer/audiodecoder.hpp @@ -21,8 +21,8 @@ extern "C" #endif } -#ifdef _WIN32 -#include +#if defined(_WIN32) && !defined(__MINGW32__) +#include typedef SSIZE_T ssize_t; #endif diff --git a/manual/opencs/creating_file.tex b/manual/opencs/creating_file.tex index 317e453b7..8cab33fb9 100644 --- a/manual/opencs/creating_file.tex +++ b/manual/opencs/creating_file.tex @@ -17,7 +17,7 @@ It is simple. When you click either \textbf{Create new Addon} you will be asked The last thing to do is to name your your addon and click create. \paragraph{Selecting File for Editing} -Clicking \textbf{Edit A Content File} will show somewhat similar window. Here you should select your Game file with drop down menu. If you want to edit this game file, simply click \textbf{OK} button. If you want to alter addon depending on that file, mark it with checkbox and than click \textbf{Ok} button. +Clicking \textbf{Edit A Content File} will show somewhat similar window. Here you should select your Game file with drop down menu. If you want to edit this game file, simply click \textbf{OK} button. If you want to alter addon depending on that file, mark it with check-box and than click \textbf{Ok} button. \subsection{Advanced} If you are paying attention, you noticed any extra icon with wrench. This one will open small settings window. Those are general OpenCS settings. We will cover this is separate section.\\ diff --git a/manual/opencs/main.tex b/manual/opencs/main.tex index f3a46c1b7..fd85baee4 100644 --- a/manual/opencs/main.tex +++ b/manual/opencs/main.tex @@ -25,7 +25,9 @@ \title{OpenCS User Manual} \maketitle +\newpage \tableofcontents{} +\newpage \input{files_and_directories} \input{creating_file} \input{windows} diff --git a/manual/opencs/recordmodification.tex b/manual/opencs/recordmodification.tex index b1cf866d6..570bd4a95 100644 --- a/manual/opencs/recordmodification.tex +++ b/manual/opencs/recordmodification.tex @@ -1,17 +1,17 @@ \section{Records Modification} \subsection{Introduction} -So far you learned how to browse trough records stored inside the content files, but not how to modify them using the \OCS{} editor. Although browsing is certainly a usefull ability on it's own, You probabbly counted on doing actual editing with this editor. There are few ways user can alter records stored in the content files, each suited for certain class of a problem. In this section We will describe how to do change records using tables interface and edit panel. +So far you learned how to browse trough records stored inside the content files, but not how to modify them using the \OCS{} editor. Although browsing is certainly a useful ability on it's own, You probably counted on doing actual editing with this editor. There are few ways user can alter records stored in the content files, each suited for certain class of a problem. In this section We will describe how to do change records using tables interface and edit panel. \subsubsection{Glossary} \begin{description} - \item[Edit Panel] Interface element used inside the \OCS{} to present records data for editing. Unlike table it showes only one record at the time. However it also presents fields that are not visible inside the table. It is also safe to say that Edit Panel presents data in way that is easier to read thanks to it's horizontal layout. + \item[Edit Panel] Interface element used inside the \OCS{} to present records data for editing. Unlike table it shows only one record at the time. However it also presents fields that are not visible inside the table. It is also safe to say that Edit Panel presents data in way that is easier to read thanks to it's horizontal layout. \end{description} \subsection{Edit Panel Interface} -Edit Panel is designed to aid you with record modification tasks. As It has been said, it uses vertical layout and presents some additional fields when compared with the table -- and some fields, even if they are actually displayed in the table, clearly ill-suited for modification isnide of them (this applies to fields that holds long text strings -- like descriptions). It also displays visual difference beetween non-editable field and editable.\\ +Edit Panel is designed to aid you with record modification tasks. As It has been said, it uses vertical layout and presents some additional fields when compared with the table -- and some fields, even if they are actually displayed in the table, clearly ill-suited for modification inside of them (this applies to fields that holds long text strings -- like descriptions). It also displays visual difference between non-editable field and editable.\\ To open edit panel, please open context menu on any record and choose edit action. This will open edit panel in the same window as your table and will present you the record fields. First data fields are actually not user editable and presented in the form of the text labels at the top of the edit panel. Lower data fields are presented in the form of actually user-editable widgets. Those includes spinboxes, text edits and text fields\footnote{Those are actually a valid terms used to describe classes of the user interface elements. If you don't understand those, don't worry -- those are very standard {GUI} elements present in almost every application since the rise of the desktop metaphor.}. Once you will finish editing one of those fields, data will be updated. There is no apply button of any sort -- simply use one of those widgets and be merry.\\ -In addition to that you probabbly noticed some icons in the bar located at the very bottom of the edit panel. Those can be used to perform the following actions: +In addition to that you probably noticed some icons in the bar located at the very bottom of the edit panel. Those can be used to perform the following actions: \begin{description} \item[Preview] This will launch simple preview panel -- which will be described later. @@ -20,7 +20,7 @@ In addition to that you probabbly noticed some icons in the bar located at the v \end{description} \subsection{Verification tool} -As you could notice there is nothing that can stop you from breaking the game by violating record fields logic, and yet -- it is something that you are always trying to avoid. To adress this problem \OCS{} utilizes so called verification tool (or verifer as many prefer to call it) that basicly goes trough all records and checks if it contains any illogical fields. This includes for instance torch duration equal 0\footnote{Interestingly negative values are perfectly fine: they indicate that light source has no duration limit at all. There are records like this in the original game.} or characters without name, race or any other record with a mandatory field missing.\\ -This tool is even more usefull than it seems. If you somehow delete race that is used by some of the characters, all those characters will be suddenly broken. As a rule of thumb it is a good idea to use verifer before saving your content file.\\ -To launch this usefull tool %don't remember, todo... -Resoults are presented as a yet another table with short (and hopefully descriptive enough) description of the identified problem. It is worth noticing that some records located in the \MW{} esm files are listed by the verification tool -- it is not fault of our tool: those records are just broken. For instance, you actually may find the 0 duration torch. However, those records are usually not placed in game world itself -- and that's good since \MW{} game engine will crash if player equip light source like this!\footnote{We would like to thanks \BS{} for such a usefull testing material. It makes us feel special.} +As you could notice there is nothing that can stop you from breaking the game by violating record fields logic, and yet -- it is something that you are always trying to avoid. To address this problem \OCS{} utilizes so called verification tool (or verifer as many prefer to call it) that basically goes trough all records and checks if it contains any illogical fields. This includes for instance torch duration equal 0\footnote{Interestingly negative values are perfectly fine: they indicate that light source has no duration limit at all. There are records like this in the original game.} or characters without name, race or any other record with a mandatory field missing.\\ +This tool is even more useful than it seems. If you somehow delete race that is used by some of the characters, all those characters will be suddenly broken. As a rule of thumb it is a good idea to use verifer before saving your content file.\\ +To launch this useful tool %don't remember, todo... +Results are presented as a yet another table with short (and hopefully descriptive enough) description of the identified problem. It is worth noticing that some records located in the \MW{} esm files are listed by the verification tool -- it is not fault of our tool: those records are just broken. For instance, you actually may find the 0 duration torch. However, those records are usually not placed in game world itself -- and that's good since \MW{} game engine will crash if player equip light source like this!\footnote{We would like to thanks \BS{} for such a useful testing material. It makes us feel special.} diff --git a/manual/opencs/recordtypes.tex b/manual/opencs/recordtypes.tex index 4dbfbe286..661f5ac7d 100644 --- a/manual/opencs/recordtypes.tex +++ b/manual/opencs/recordtypes.tex @@ -1,15 +1,15 @@ \section{Record Types} \subsection{Introduction} -A gameworld contains many objects, such as chests, weapons and monsters. All these objects are merely instances of templates that we call Referenceables. The OpenCS Referenceables table contains information about each of these template objects, eg. its value and weight in the case of items and an aggression level in the case of NPCs. +A gameworld contains many items, such as chests, weapons and monsters. All these items are merely instances of templates that we call \textbf{Objects}. The OpenCS \textbf{Objects} table contains information about each of these template objects, eg. its value and weight in the case of items and an aggression level in the case of NPCs. Let's go through all Record Types and discuss what you can tell OpenCS about them. \begin{description} \item[Activator:] When the player enters the same cell as this object, a script is started. Often it also has a \textbf{Script} attached to it, though it not mandatory. These scripts are small bits of code written in a special scripting language that OpenCS can read and interpret. - \item[Potion:] This is a potion that is not self-made. It has an \textbf{Icon} for your inventory, Aside from the self-explanatory \textbf{Weight} and \textbf{Coin Value}, it has an attribute called \textbf{Auto Calc} set to ``False''. This means that the effects of this potion are preconfigured. This does not happen when the player makes their own potion. - \item[Apparatus:] This is a tool to make potions. Again there's an icon for your inventory as well as a weight and a coin value. It also has a \textbf{Quality} value attached to it: higher the number, the better the effect on your potions will be. The \textbf{Apparatus Type} describes if the item is a Calcinator, Retort, Alembir or Mortar & Pestal. Each has a different effect on the potion the player makes. For more information on this subject, please refer to the \href{http://www.uesp.net/wiki/Morrowind:Alchemy#Tools}{UESP page on Alchemy Tools}. - \item[Armor:] This type of item adds \textbf{Enchantment Points} to the mix. Every piece of clothing or armor has a ''pool'' of potential magicka that gets unlocked when you enchant it. Strong enchantments consume more magicka from this pool: the stronger the enchantment, the more Enchantment Points each cast will take up. For more information on this subject, please refer to the \href{http://www.uesp.net/wiki/Morrowind:Enchant}{Enchant page on UESP}. \textbf{Health} means the amount of hit points this piece of armor has. If it sustains enough damage, the armor will be destroyed. Finally, \textbf{Armor Value} tells the game how much points to add to the player character's Armor Rating. + \item[Potion:] This is a potion that is not self-made. It has an \textbf{Icon} for your inventory, Aside from the self-explanatory \textbf{Weight} and \textbf{Coin Value}, it has an attribute called \textbf{Auto Calc} set to ``False''. This means that the effects of this potion are pre-configured. This does not happen when the player makes their own potion. + \item[Apparatus:] This is a tool to make potions. Again there's an icon for your inventory as well as a weight and a coin value. It also has a \textbf{Quality} value attached to it: higher the number, the better the effect on your potions will be. The \textbf{Apparatus Type} describes if the item is a Calcinator, Retort, Alembic or Mortar \& Pestle. Each has a different effect on the potion the player makes. For more information on this subject, please refer to the \href{http://www.uesp.net/wiki/Morrowind:Alchemy#Tools}{UESP page on Alchemy Tools}. + \item[Armor:] This type of item adds \textbf{Enchantment Points} to the mix. Every piece of clothing or armor has a ''pool'' of potential Magicka that gets unlocked when you enchant it. Strong enchantments consume more Magicka from this pool: the stronger the enchantment, the more Enchantment Points each cast will take up. For more information on this subject, please refer to the \href{http://www.uesp.net/wiki/Morrowind:Enchant}{Enchant page on UESP}. \textbf{Health} means the amount of hit points this piece of armor has. If it sustains enough damage, the armor will be destroyed. Finally, \textbf{Armor Value} tells the game how much points to add to the player character's Armor Rating. \item[Book:] This includes scrolls and notes. For the game to make the distinction between books and scrolls, an extra property, \textbf{Scroll}, has been added. Under the \textbf{Skill} column a scroll or book can have an in-game skill listed. Reading this item will raise the player's level in that specific skill. For more information on this, please refer to the \href{http://www.uesp.net/wiki/Morrowind:Skill_Books}{Skill Books page on UESP}. \item[Clothing:] These items work just like Armors, but confer no protective properties. Rather than ``Armor Type'', these items have a ``Clothing Type''. \item[Container:] This is all the stuff that stores items, from chests to sacks to plants. Its \textbf{Capacity} shows how much stuff you can put in the container. You can compare it to the maximum allowed load a player character can carry (who will get over-encumbered and unable to move if he crosses this threshold). A container, however, will just refuse to take the item in question when it gets ''over-encumbered''. \textbf{Organic Container}s are containers such as plants. Containers that \textbf{Respawn} are not safe to store stuff in. After a certain amount of time they will reset to their default contents, meaning that everything in it is gone forever. diff --git a/manual/opencs/tables.tex b/manual/opencs/tables.tex index 503489872..984157f88 100644 --- a/manual/opencs/tables.tex +++ b/manual/opencs/tables.tex @@ -14,7 +14,7 @@ Let's browse through the various screens and see what all these tables show. \begin{description} \item[Record:] An entry in \OCS{} representing an item, location, sound, NPC or anything else. - \item[Reference, Referenceable:] When an item is placed in the world, it isn't an isolated and unique object. For example, the game world might contain a lot of exquisite belts on different NPCs and in many crates, but they all refer to one specific record in the game's library: the Exquisite Belt record. In this case, all those belts in crates and on NPCs are references. The central Exquisite Belt record is called a referenceable. This allows modders to make changes to all items of the same type. For example, if you want all exquisite belts to have 4000 enchantment points rather than 400, you will only need to change the referenceable Exquisite Belt rather than all exquisite belts references individually. + \item[Instance, Object:] When an item is placed in the world, it isn't an isolated and unique object. For example, the game world might contain a lot of exquisite belts on different NPCs and in many crates, but they all refer to one specific record in the game's library: the Exquisite Belt record. In this case, all those belts in crates and on NPCs are \textbf{instances}. The central Exquisite Belt record is called a \textbf{object}. This allows modders to make changes to all items of the same type. For example, if you want all exquisite belts to have 4000 enchantment points rather than 400, you will only need to change the \textbf{object} Exquisite Belt rather than all exquisite belts \textbf{instances} individually. \end{description} \subsubsection{Recurring Terms} @@ -29,7 +29,7 @@ optionally the Bloodmoon and Tribunal expansions. \item[Modified] means that the record is part of the base game, but has been changed in some way. \item[Deleted] means that this record used to be part of the base game, but has been removed as an entry. This does not mean, however, that the occurrences in the game itself have been removed! For example, if you remove the CharGen\_Bed entry from morrowind.esm, it does not mean the bedroll in the basement -of the Census and Excise Office in Seyda Neen is gone. You're going to have to delete that reference yourself or make sure that that object is replaced +of the Census and Excise Office in Seyda Neen is gone. You're going to have to delete that instance yourself or make sure that that object is replaced by something that still exists otherwise you will get crashes in the worst case scenario. \end{description} @@ -37,12 +37,12 @@ by something that still exists otherwise you will get crashes in the worst case The contents of the game world can be changed by choosing one of the options in the appropriate menu at the top of the screen. \subsubsection{Regions} -This describes the general areas of Vvardenfell. Each of these areas has different rules about things such as encounters and weather. +This describes the general areas of the gameworld. Each of these areas has different rules about things such as encounters and weather. \begin{description} \item[Name:] This is how the game will show your location in-game. - \item[Map Colour:] This is a six-digit hexidecimal representation of the colour used to identify the region on the map available in - World > Region Map. If you do not have an application with a colour picker, you can use your favourite search engine to find a colour picker online. + \item[Map Colour:] This is a six-digit hexadecimal representation of the color used to identify the region on the map available in + World > Region Map. If you do not have an application with a color picker, you can use your favorite search engine to find a color picker on-line. \item[Sleep Encounter:] These are the rules for what kind of enemies you might encounter when you sleep outside in the wild. \end{description} @@ -52,7 +52,7 @@ why would the computer need to keep track the exact locations of NPCs walking th be quite useless and bring your system to its knees! So the world has been divided up into squares we call "cells". Once your character enters a cell, the game will load everything that is going on in that cell so you can interact with it. -In the original \MW{} this could be seen when you were travelling and you would see a small loading bar at the bottom of the screen; +In the original \MW{} this could be seen when you were traveling and you would see a small loading bar at the bottom of the screen; you had just entered a new cell and the game would have to load all the items and NPCs. The Cells screen in \OCS{} provides you with a list of cells in the game, both the interior cells (houses, dungeons, mines, etc.) and the exterior cells (the outside world). @@ -71,7 +71,7 @@ in the game, both the interior cells (houses, dungeons, mines, etc.) and the ext \item[Interior Sky:] Should this interior cell have a sky? This is a rather unique case. The \TB{} expansion took place in a city on the mainland. Normally this would require the city to be composed of exterior cells so it has a sky, weather and the like. But if the player is - in an exterior cell and looks at his in-game map, he sees Vvardenfell with an overview of all exterior cells. The player would have to see + in an exterior cell and looks at his in-game map, he sees the map of the gameworld with an overview of all exterior cells. The player would have to see the city's very own map, as if he was walking around in an interior cell. So the developers decided to create a workaround and take a bit of both: The whole city would technically work exactly like an interior cell, @@ -83,7 +83,7 @@ in the game, both the interior cells (houses, dungeons, mines, etc.) and the ext \end{description} -\subsubsection{Referenceables} +\subsubsection{Objects} This is a library of all the items, triggers, containers, NPCs, etc. in the game. There are several kinds of Record Types. Depending on which type a record is, it will need specific information to function. For example, an NPC needs a value attached to its aggression level. A chest, of course, does not. All Record Types contain at least a~model. How else would the player see them? Usually they also have a Name, which is what you see @@ -91,4 +91,4 @@ when you hover your reticle over the object. This is a library of all the items, triggers, containers, NPCs, etc. in the game. There are several kinds of Record Types. Depending on which type a record is, it will need specific information to function. For example, an NPC needs a value attached to its aggression level. A chest, of course, does not. All Record Types contain at least a model. How else would the player see them? Usually they also have a Name, which is what you see when you hover your reticle over the object. -Please refer to the Record Types section for an overview of what each type of Referenceable does and what you can tell OpenCS about these objects. +Please refer to the Record Types section for an overview of what each type of object does and what you can tell OpenCS about these objects.