Merge branch 'master' of https://github.com/OpenMW/openmw into osg

Conflicts:
	extern/sdl4ogre/sdlwindowhelper.cpp
pull/638/head
scrawl 10 years ago
commit 974fda5bde

@ -131,8 +131,10 @@ endif()
# Platform specific # Platform specific
if (WIN32) if (WIN32)
if(NOT MINGW)
set(Boost_USE_STATIC_LIBS ON) set(Boost_USE_STATIC_LIBS ON)
add_definitions(-DBOOST_ALL_NO_LIB) add_definitions(-DBOOST_ALL_NO_LIB)
endif(NOT MINGW)
# Suppress WinMain(), provided by SDL # Suppress WinMain(), provided by SDL
add_definitions(-DSDL_MAIN_HANDLED) add_definitions(-DSDL_MAIN_HANDLED)

@ -18,6 +18,10 @@ target_link_libraries(openmw-iniimporter
components components
) )
if (MINGW)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -municode")
endif()
if (BUILD_WITH_CODE_COVERAGE) if (BUILD_WITH_CODE_COVERAGE)
add_definitions (--coverage) add_definitions (--coverage)
target_link_libraries(openmw-iniimporter gcov) target_link_libraries(openmw-iniimporter gcov)

@ -40,7 +40,7 @@ opencs_units (model/tools
opencs_units_noqt (model/tools opencs_units_noqt (model/tools
mandatoryid skillcheck classcheck factioncheck racecheck soundcheck regioncheck mandatoryid skillcheck classcheck factioncheck racecheck soundcheck regioncheck
birthsigncheck spellcheck referencecheck referenceablecheck scriptcheck bodypartcheck 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 table tablesubview scriptsubview util regionmapsubview tablebottombox creator genericcreator
cellcreator referenceablecreator referencecreator scenesubview cellcreator referenceablecreator referencecreator scenesubview
infocreator scriptedit dialoguesubview previewsubview regionmap dragrecordtable nestedtable infocreator scriptedit dialoguesubview previewsubview regionmap dragrecordtable nestedtable
dialoguespinbox
) )
opencs_units_noqt (view/world opencs_units_noqt (view/world

@ -58,9 +58,11 @@ CS::Editor::Editor ()
connect (&mFileDialog, SIGNAL(signalCreateNewFile (const boost::filesystem::path&)), connect (&mFileDialog, SIGNAL(signalCreateNewFile (const boost::filesystem::path&)),
this, SLOT(createNewFile (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&)), connect (&mNewGame, SIGNAL (createRequest (const boost::filesystem::path&)),
this, SLOT (createNewGame (const boost::filesystem::path&))); this, SLOT (createNewGame (const boost::filesystem::path&)));
connect (&mNewGame, SIGNAL (cancelCreateGame()), this, SLOT (cancelCreateGame ()));
} }
CS::Editor::~Editor () CS::Editor::~Editor ()
@ -164,12 +166,40 @@ void CS::Editor::createGame()
mNewGame.activateWindow(); 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() void CS::Editor::createAddon()
{ {
mStartup.hide(); mStartup.hide();
mFileDialog.showDialog (CSVDoc::ContentAction_New); 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() void CS::Editor::loadDocument()
{ {
mStartup.hide(); mStartup.hide();

@ -80,6 +80,8 @@ namespace CS
void createGame(); void createGame();
void createAddon(); void createAddon();
void cancelCreateGame();
void cancelFileDialog();
void loadDocument(); void loadDocument();
void openFiles (const boost::filesystem::path &path); void openFiles (const boost::filesystem::path &path);

@ -49,6 +49,11 @@ CSMDoc::DocumentManager::~DocumentManager()
delete *iter; delete *iter;
} }
bool CSMDoc::DocumentManager::isEmpty()
{
return mDocuments.empty();
}
void CSMDoc::DocumentManager::addDocument (const std::vector<boost::filesystem::path>& files, const boost::filesystem::path& savePath, void CSMDoc::DocumentManager::addDocument (const std::vector<boost::filesystem::path>& files, const boost::filesystem::path& savePath,
bool new_) bool new_)
{ {

@ -64,6 +64,8 @@ namespace CSMDoc
void setVFS(const VFS::Manager* vfs); void setVFS(const VFS::Manager* vfs);
bool isEmpty();
private: private:
boost::filesystem::path mResDir; boost::filesystem::path mResDir;

@ -245,6 +245,42 @@ void CSMSettings::UserSettings::buildSettingModelDefaults()
Setting *monoFont = createSetting (Type_CheckBox, "mono-font", "Use monospace font"); Setting *monoFont = createSetting (Type_CheckBox, "mono-font", "Use monospace font");
monoFont->setDefaultValue ("true"); monoFont->setDefaultValue ("true");
monoFont->setToolTip ("Whether to use monospaced fonts on script edit subview."); 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);
} }
{ {

@ -0,0 +1,151 @@
#include "pathgridcheck.hpp"
#include <sstream>
#include <algorithm>
#include "../world/universalid.hpp"
#include "../world/idcollection.hpp"
#include "../world/subcellcollection.hpp"
#include "../world/pathgrid.hpp"
CSMTools::PathgridCheckStage::PathgridCheckStage (const CSMWorld::SubCellCollection<CSMWorld::Pathgrid>& pathgrids)
: mPathgrids (pathgrids)
{}
int CSMTools::PathgridCheckStage::setup()
{
return mPathgrids.getSize();
}
void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& messages)
{
const CSMWorld::Record<CSMWorld::Pathgrid>& 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<int>(pathgrid.mPoints.size()))
messages.push_back (std::make_pair (id, pathgrid.mId + " has less points than expected"));
else if (pathgrid.mData.mS2 > static_cast<int>(pathgrid.mPoints.size()))
messages.push_back (std::make_pair (id, pathgrid.mId + " has more points than expected"));
std::vector<CSMTools::Point> pointList(pathgrid.mPoints.size());
std::vector<int> duplList;
for (unsigned int i = 0; i < pathgrid.mEdges.size(); ++i)
{
if (pathgrid.mEdges[i].mV0 < static_cast<int>(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<int>(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<int>::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
}

@ -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<typename T, typename AT>
class SubCellCollection;
}
namespace CSMTools
{
struct Point
{
unsigned char mConnectionNum;
std::vector<int> mOtherIndex;
Point() : mConnectionNum(0), mOtherIndex(0) {}
};
class PathgridCheckStage : public CSMDoc::Stage
{
const CSMWorld::SubCellCollection<CSMWorld::Pathgrid,
CSMWorld::IdAccessor<CSMWorld::Pathgrid> >& mPathgrids;
public:
PathgridCheckStage (const CSMWorld::SubCellCollection<CSMWorld::Pathgrid,
CSMWorld::IdAccessor<CSMWorld::Pathgrid> >& pathgrids);
virtual int setup();
virtual void perform (int stage, CSMDoc::Messages& messages);
};
}
#endif // CSM_TOOLS_PATHGRIDCHECK_H

@ -8,12 +8,14 @@
CSMTools::ReferenceableCheckStage::ReferenceableCheckStage( CSMTools::ReferenceableCheckStage::ReferenceableCheckStage(
const CSMWorld::RefIdData& referenceable, const CSMWorld::IdCollection<ESM::Race >& races, const CSMWorld::RefIdData& referenceable, const CSMWorld::IdCollection<ESM::Race >& races,
const CSMWorld::IdCollection<ESM::Class>& classes, const CSMWorld::IdCollection<ESM::Class>& classes,
const CSMWorld::IdCollection<ESM::Faction>& faction) const CSMWorld::IdCollection<ESM::Faction>& faction,
const CSMWorld::IdCollection<ESM::Script>& scripts)
: :
mReferencables(referenceable), mReferencables(referenceable),
mRaces(races), mRaces(races),
mClasses(classes), mClasses(classes),
mFactions(faction), mFactions(faction),
mScripts(scripts),
mPlayerPresent(false) mPlayerPresent(false)
{ {
} }
@ -245,6 +247,9 @@ void CSMTools::ReferenceableCheckStage::bookCheck(
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Book, book.mId); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Book, book.mId);
inventoryItemCheck<ESM::Book>(book, messages, id.toString(), true); inventoryItemCheck<ESM::Book>(book, messages, id.toString(), true);
// Check that mentioned scripts exist
scriptCheck<ESM::Book>(book, messages, id.toString());
} }
void CSMTools::ReferenceableCheckStage::activatorCheck( void CSMTools::ReferenceableCheckStage::activatorCheck(
@ -265,6 +270,9 @@ void CSMTools::ReferenceableCheckStage::activatorCheck(
//Checking for model, IIRC all activators should have a model //Checking for model, IIRC all activators should have a model
if (activator.mModel.empty()) if (activator.mModel.empty())
messages.push_back (std::make_pair (id, activator.mId + " has no model")); messages.push_back (std::make_pair (id, activator.mId + " has no model"));
// Check that mentioned scripts exist
scriptCheck<ESM::Activator>(activator, messages, id.toString());
} }
void CSMTools::ReferenceableCheckStage::potionCheck( void CSMTools::ReferenceableCheckStage::potionCheck(
@ -284,6 +292,9 @@ void CSMTools::ReferenceableCheckStage::potionCheck(
inventoryItemCheck<ESM::Potion>(potion, messages, id.toString()); inventoryItemCheck<ESM::Potion>(potion, messages, id.toString());
//IIRC potion can have empty effects list just fine. //IIRC potion can have empty effects list just fine.
// Check that mentioned scripts exist
scriptCheck<ESM::Potion>(potion, messages, id.toString());
} }
@ -305,6 +316,9 @@ void CSMTools::ReferenceableCheckStage::apparatusCheck(
inventoryItemCheck<ESM::Apparatus>(apparatus, messages, id.toString()); inventoryItemCheck<ESM::Apparatus>(apparatus, messages, id.toString());
toolCheck<ESM::Apparatus>(apparatus, messages, id.toString()); toolCheck<ESM::Apparatus>(apparatus, messages, id.toString());
// Check that mentioned scripts exist
scriptCheck<ESM::Apparatus>(apparatus, messages, id.toString());
} }
void CSMTools::ReferenceableCheckStage::armorCheck( void CSMTools::ReferenceableCheckStage::armorCheck(
@ -331,6 +345,9 @@ void CSMTools::ReferenceableCheckStage::armorCheck(
//checking for health. Only positive numbers are allowed, or 0 is illegal //checking for health. Only positive numbers are allowed, or 0 is illegal
if (armor.mData.mHealth <= 0) if (armor.mData.mHealth <= 0)
messages.push_back (std::make_pair (id, armor.mId + " has non positive health")); messages.push_back (std::make_pair (id, armor.mId + " has non positive health"));
// Check that mentioned scripts exist
scriptCheck<ESM::Armor>(armor, messages, id.toString());
} }
void CSMTools::ReferenceableCheckStage::clothingCheck( void CSMTools::ReferenceableCheckStage::clothingCheck(
@ -348,6 +365,9 @@ void CSMTools::ReferenceableCheckStage::clothingCheck(
const ESM::Clothing& clothing = (dynamic_cast<const CSMWorld::Record<ESM::Clothing>& >(baseRecord)).get(); const ESM::Clothing& clothing = (dynamic_cast<const CSMWorld::Record<ESM::Clothing>& >(baseRecord)).get();
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Clothing, clothing.mId); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Clothing, clothing.mId);
inventoryItemCheck<ESM::Clothing>(clothing, messages, id.toString(), true); inventoryItemCheck<ESM::Clothing>(clothing, messages, id.toString(), true);
// Check that mentioned scripts exist
scriptCheck<ESM::Clothing>(clothing, messages, id.toString());
} }
void CSMTools::ReferenceableCheckStage::containerCheck( void CSMTools::ReferenceableCheckStage::containerCheck(
@ -377,6 +397,9 @@ void CSMTools::ReferenceableCheckStage::containerCheck(
//checking for name //checking for name
if (container.mName.empty()) if (container.mName.empty())
messages.push_back (std::make_pair (id, container.mId + " has an empty name")); messages.push_back (std::make_pair (id, container.mId + " has an empty name"));
// Check that mentioned scripts exist
scriptCheck<ESM::Container>(container, messages, id.toString());
} }
void CSMTools::ReferenceableCheckStage::creatureCheck ( void CSMTools::ReferenceableCheckStage::creatureCheck (
@ -444,6 +467,9 @@ void CSMTools::ReferenceableCheckStage::creatureCheck (
//TODO, find meaning of other values //TODO, find meaning of other values
if (creature.mData.mGold < 0) //It seems that this is for gold in merchant creatures 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 ")); messages.push_back (std::make_pair (id, creature.mId + " has negative gold "));
// Check that mentioned scripts exist
scriptCheck<ESM::Creature>(creature, messages, id.toString());
} }
void CSMTools::ReferenceableCheckStage::doorCheck( void CSMTools::ReferenceableCheckStage::doorCheck(
@ -455,15 +481,18 @@ void CSMTools::ReferenceableCheckStage::doorCheck(
if (baseRecord.isDeleted()) if (baseRecord.isDeleted())
return; return;
const ESM::Door& Door = (dynamic_cast<const CSMWorld::Record<ESM::Door>&>(baseRecord)).get(); const ESM::Door& door = (dynamic_cast<const CSMWorld::Record<ESM::Door>&>(baseRecord)).get();
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Door, Door.mId); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Door, door.mId);
//usual, name or model //usual, name or model
if (Door.mName.empty()) if (door.mName.empty())
messages.push_back (std::make_pair (id, Door.mId + " has an empty name")); 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()) // Check that mentioned scripts exist
messages.push_back (std::make_pair (id, Door.mId + " has no model")); scriptCheck<ESM::Door>(door, messages, id.toString());
} }
void CSMTools::ReferenceableCheckStage::ingredientCheck( void CSMTools::ReferenceableCheckStage::ingredientCheck(
@ -478,10 +507,13 @@ void CSMTools::ReferenceableCheckStage::ingredientCheck(
return; return;
} }
const ESM::Ingredient& Ingredient = (dynamic_cast<const CSMWorld::Record<ESM::Ingredient>& >(baseRecord)).get(); const ESM::Ingredient& ingredient = (dynamic_cast<const CSMWorld::Record<ESM::Ingredient>& >(baseRecord)).get();
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Ingredient, Ingredient.mId); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Ingredient, ingredient.mId);
inventoryItemCheck<ESM::Ingredient>(Ingredient, messages, id.toString()); inventoryItemCheck<ESM::Ingredient>(ingredient, messages, id.toString());
// Check that mentioned scripts exist
scriptCheck<ESM::Ingredient>(ingredient, messages, id.toString());
} }
void CSMTools::ReferenceableCheckStage::creaturesLevListCheck( void CSMTools::ReferenceableCheckStage::creaturesLevListCheck(
@ -542,6 +574,9 @@ void CSMTools::ReferenceableCheckStage::lightCheck(
if (light.mData.mTime == 0) if (light.mData.mTime == 0)
messages.push_back (std::make_pair (id, light.mId + " has zero duration")); messages.push_back (std::make_pair (id, light.mId + " has zero duration"));
} }
// Check that mentioned scripts exist
scriptCheck<ESM::Light>(light, messages, id.toString());
} }
void CSMTools::ReferenceableCheckStage::lockpickCheck( void CSMTools::ReferenceableCheckStage::lockpickCheck(
@ -562,6 +597,9 @@ void CSMTools::ReferenceableCheckStage::lockpickCheck(
inventoryItemCheck<ESM::Lockpick>(lockpick, messages, id.toString()); inventoryItemCheck<ESM::Lockpick>(lockpick, messages, id.toString());
toolCheck<ESM::Lockpick>(lockpick, messages, id.toString(), true); toolCheck<ESM::Lockpick>(lockpick, messages, id.toString(), true);
// Check that mentioned scripts exist
scriptCheck<ESM::Lockpick>(lockpick, messages, id.toString());
} }
void CSMTools::ReferenceableCheckStage::miscCheck( void CSMTools::ReferenceableCheckStage::miscCheck(
@ -580,6 +618,9 @@ void CSMTools::ReferenceableCheckStage::miscCheck(
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Miscellaneous, miscellaneous.mId); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Miscellaneous, miscellaneous.mId);
inventoryItemCheck<ESM::Miscellaneous>(miscellaneous, messages, id.toString()); inventoryItemCheck<ESM::Miscellaneous>(miscellaneous, messages, id.toString());
// Check that mentioned scripts exist
scriptCheck<ESM::Miscellaneous>(miscellaneous, messages, id.toString());
} }
void CSMTools::ReferenceableCheckStage::npcCheck ( void CSMTools::ReferenceableCheckStage::npcCheck (
@ -697,6 +738,9 @@ void CSMTools::ReferenceableCheckStage::npcCheck (
messages.push_back (std::make_pair (id, npc.mId + " has no hair")); messages.push_back (std::make_pair (id, npc.mId + " has no hair"));
//TODO: reputation, Disposition, rank, everything else //TODO: reputation, Disposition, rank, everything else
// Check that mentioned scripts exist
scriptCheck<ESM::NPC>(npc, messages, id.toString());
} }
void CSMTools::ReferenceableCheckStage::weaponCheck( void CSMTools::ReferenceableCheckStage::weaponCheck(
@ -773,6 +817,9 @@ void CSMTools::ReferenceableCheckStage::weaponCheck(
messages.push_back (std::make_pair (id, weapon.mId + " has negative reach")); messages.push_back (std::make_pair (id, weapon.mId + " has negative reach"));
} }
} }
// Check that mentioned scripts exist
scriptCheck<ESM::Weapon>(weapon, messages, id.toString());
} }
void CSMTools::ReferenceableCheckStage::probeCheck( void CSMTools::ReferenceableCheckStage::probeCheck(
@ -792,6 +839,9 @@ void CSMTools::ReferenceableCheckStage::probeCheck(
inventoryItemCheck<ESM::Probe>(probe, messages, id.toString()); inventoryItemCheck<ESM::Probe>(probe, messages, id.toString());
toolCheck<ESM::Probe>(probe, messages, id.toString(), true); toolCheck<ESM::Probe>(probe, messages, id.toString(), true);
// Check that mentioned scripts exist
scriptCheck<ESM::Probe>(probe, messages, id.toString());
} }
void CSMTools::ReferenceableCheckStage::repairCheck ( void CSMTools::ReferenceableCheckStage::repairCheck (
@ -808,6 +858,9 @@ void CSMTools::ReferenceableCheckStage::repairCheck (
inventoryItemCheck<ESM::Repair> (repair, messages, id.toString()); inventoryItemCheck<ESM::Repair> (repair, messages, id.toString());
toolCheck<ESM::Repair> (repair, messages, id.toString(), true); toolCheck<ESM::Repair> (repair, messages, id.toString(), true);
// Check that mentioned scripts exist
scriptCheck<ESM::Repair>(repair, messages, id.toString());
} }
void CSMTools::ReferenceableCheckStage::staticCheck ( void CSMTools::ReferenceableCheckStage::staticCheck (
@ -919,3 +972,13 @@ template<typename List> void CSMTools::ReferenceableCheckStage::listCheck (
someList.mId + " contains item with non-positive level")); someList.mId + " contains item with non-positive level"));
} }
} }
template<typename Tool> 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+"\""));
}
}

@ -15,7 +15,8 @@ namespace CSMTools
ReferenceableCheckStage (const CSMWorld::RefIdData& referenceable, ReferenceableCheckStage (const CSMWorld::RefIdData& referenceable,
const CSMWorld::IdCollection<ESM::Race>& races, const CSMWorld::IdCollection<ESM::Race>& races,
const CSMWorld::IdCollection<ESM::Class>& classes, const CSMWorld::IdCollection<ESM::Class>& classes,
const CSMWorld::IdCollection<ESM::Faction>& factions); const CSMWorld::IdCollection<ESM::Faction>& factions,
const CSMWorld::IdCollection<ESM::Script>& scripts);
virtual void perform(int stage, CSMDoc::Messages& messages); virtual void perform(int stage, CSMDoc::Messages& messages);
virtual int setup(); virtual int setup();
@ -46,26 +47,30 @@ namespace CSMTools
//FINAL CHECK //FINAL CHECK
void finalCheck (CSMDoc::Messages& messages); void finalCheck (CSMDoc::Messages& messages);
//TEMPLATE CHECKS //TEMPLATE CHECKS
template<typename ITEM> void inventoryItemCheck(const ITEM& someItem, template<typename ITEM> void inventoryItemCheck(const ITEM& someItem,
CSMDoc::Messages& messages, CSMDoc::Messages& messages,
const std::string& someID, const std::string& someID,
bool enchantable); //for all enchantable items. bool enchantable); //for all enchantable items.
template<typename ITEM> void inventoryItemCheck(const ITEM& someItem, template<typename ITEM> void inventoryItemCheck(const ITEM& someItem,
CSMDoc::Messages& messages, CSMDoc::Messages& messages,
const std::string& someID); //for non-enchantable items. const std::string& someID); //for non-enchantable items.
template<typename TOOL> void toolCheck(const TOOL& someTool, template<typename TOOL> void toolCheck(const TOOL& someTool,
CSMDoc::Messages& messages, CSMDoc::Messages& messages,
const std::string& someID, const std::string& someID,
bool canbebroken); //for tools with uses. bool canbebroken); //for tools with uses.
template<typename TOOL> void toolCheck(const TOOL& someTool, template<typename TOOL> void toolCheck(const TOOL& someTool,
CSMDoc::Messages& messages, CSMDoc::Messages& messages,
const std::string& someID); //for tools without uses. const std::string& someID); //for tools without uses.
template<typename LIST> void listCheck(const LIST& someList, template<typename LIST> void listCheck(const LIST& someList,
CSMDoc::Messages& messages,
const std::string& someID);
template<typename TOOL> void scriptCheck(const TOOL& someTool,
CSMDoc::Messages& messages, CSMDoc::Messages& messages,
const std::string& someID); const std::string& someID);
@ -73,6 +78,7 @@ namespace CSMTools
const CSMWorld::IdCollection<ESM::Race>& mRaces; const CSMWorld::IdCollection<ESM::Race>& mRaces;
const CSMWorld::IdCollection<ESM::Class>& mClasses; const CSMWorld::IdCollection<ESM::Class>& mClasses;
const CSMWorld::IdCollection<ESM::Faction>& mFactions; const CSMWorld::IdCollection<ESM::Faction>& mFactions;
const CSMWorld::IdCollection<ESM::Script>& mScripts;
bool mPlayerPresent; bool mPlayerPresent;
}; };
} }

@ -26,6 +26,7 @@
#include "referencecheck.hpp" #include "referencecheck.hpp"
#include "startscriptcheck.hpp" #include "startscriptcheck.hpp"
#include "searchoperation.hpp" #include "searchoperation.hpp"
#include "pathgridcheck.hpp"
CSMDoc::OperationHolder *CSMTools::Tools::get (int type) 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 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())); 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 )), CSMWorld::UniversalId( CSMWorld::UniversalId::Type_Meshes )),
mData.getRaces() )); mData.getRaces() ));
mVerifierOperation->appendStage (new PathgridCheckStage (mData.getPathgrids()));
mVerifier.setOperation (mVerifierOperation); 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 (done (int, bool)), this, SIGNAL (done (int, bool)));
connect (&mSearch, connect (&mSearch,
SIGNAL (reportMessage (const CSMWorld::UniversalId&, const std::string&, const std::string&, int)), 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() CSMTools::Tools::~Tools()
@ -130,7 +133,7 @@ CSMTools::Tools::~Tools()
mSearch.abortAndWait(); mSearch.abortAndWait();
delete mSearchOperation; delete mSearchOperation;
} }
for (std::map<int, ReportModel *>::iterator iter (mReports.begin()); iter!=mReports.end(); ++iter) for (std::map<int, ReportModel *>::iterator iter (mReports.begin()); iter!=mReports.end(); ++iter)
delete iter->second; delete iter->second;
} }
@ -149,7 +152,7 @@ CSMWorld::UniversalId CSMTools::Tools::newSearch()
{ {
mReports.insert (std::make_pair (mNextReportNumber++, new ReportModel (true))); 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) 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) if (!mSearchOperation)
{ {
mSearchOperation = new SearchOperation (mDocument); mSearchOperation = new SearchOperation (mDocument);
mSearch.setOperation (mSearchOperation); mSearch.setOperation (mSearchOperation);
} }
mSearchOperation->configure (search); mSearchOperation->configure (search);
mSearch.start(); 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_LoadErrorLog &&
id.getType()!=CSMWorld::UniversalId::Type_Search) id.getType()!=CSMWorld::UniversalId::Type_Search)
throw std::logic_error ("invalid request for report model: " + id.toString()); throw std::logic_error ("invalid request for report model: " + id.toString());
return mReports.at (id.getIndex()); return mReports.at (id.getIndex());
} }

@ -84,6 +84,7 @@ bool CSMWorld::ColumnBase::isId (Display display)
Display_InfoCondFunc, Display_InfoCondFunc,
Display_InfoCondVar, Display_InfoCondVar,
Display_InfoCondComp, Display_InfoCondComp,
Display_RaceSkill,
Display_None Display_None
}; };
@ -137,8 +138,8 @@ bool CSMWorld::NestableColumn::hasChildren() const
} }
CSMWorld::NestedChildColumn::NestedChildColumn (int id, CSMWorld::NestedChildColumn::NestedChildColumn (int id,
CSMWorld::ColumnBase::Display display, bool isEditable) CSMWorld::ColumnBase::Display display, int flags, bool isEditable)
: NestableColumn (id, display, CSMWorld::ColumnBase::Flag_Dialogue) , mIsEditable(isEditable) : NestableColumn (id, display, flags) , mIsEditable(isEditable)
{} {}
bool CSMWorld::NestedChildColumn::isEditable () const bool CSMWorld::NestedChildColumn::isEditable () const

@ -25,7 +25,8 @@ namespace CSMWorld
{ {
Flag_Table = 1, // column should be displayed in table view Flag_Table = 1, // column should be displayed in table view
Flag_Dialogue = 2, // column should be displayed in dialogue 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 enum Display
@ -119,6 +120,7 @@ namespace CSMWorld
Display_InfoCondFunc, Display_InfoCondFunc,
Display_InfoCondVar, Display_InfoCondVar,
Display_InfoCondComp, Display_InfoCondComp,
Display_RaceSkill,
//top level columns that nest other columns //top level columns that nest other columns
Display_NestedHeader Display_NestedHeader
@ -199,7 +201,8 @@ namespace CSMWorld
struct NestedChildColumn : public NestableColumn 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; virtual bool isEditable() const;

@ -467,8 +467,9 @@ namespace CSMWorld
int mMask; int mMask;
bool mInverted; bool mInverted;
FlagColumn (int columnId, int mask, bool inverted = false) FlagColumn (int columnId, int mask,
: Column<ESXRecordT> (columnId, ColumnBase::Display_Boolean), mMask (mask), int flags = ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue, bool inverted = false)
: Column<ESXRecordT> (columnId, ColumnBase::Display_Boolean, flags), mMask (mask),
mInverted (inverted) mInverted (inverted)
{} {}

@ -176,7 +176,7 @@ namespace CSMWorld
{ ColumnId_ContainerContent, "Content" }, { ColumnId_ContainerContent, "Content" },
{ ColumnId_ItemCount, "Count" }, { ColumnId_ItemCount, "Count" },
{ ColumnId_InventoryItemId, "ID"}, { ColumnId_InventoryItemId, "Item ID"},
{ ColumnId_CombatState, "Combat" }, { ColumnId_CombatState, "Combat" },
{ ColumnId_MagicState, "Magic" }, { ColumnId_MagicState, "Magic" },
@ -188,10 +188,10 @@ namespace CSMWorld
{ ColumnId_ActorInventory, "Inventory" }, { ColumnId_ActorInventory, "Inventory" },
{ ColumnId_SpellList, "Spells" }, { ColumnId_SpellList, "Spells" },
{ ColumnId_SpellId, "ID"}, { ColumnId_SpellId, "Spell ID"},
{ ColumnId_NpcDestinations, "Destinations" }, { ColumnId_NpcDestinations, "Destinations" },
{ ColumnId_DestinationCell, "Cell"}, { ColumnId_DestinationCell, "Dest Cell"},
{ ColumnId_PosX, "Dest X"}, { ColumnId_PosX, "Dest X"},
{ ColumnId_PosY, "Dest Y"}, { ColumnId_PosY, "Dest Y"},
{ ColumnId_PosZ, "Dest Z"}, { ColumnId_PosZ, "Dest Z"},
@ -224,17 +224,17 @@ namespace CSMWorld
{ ColumnId_BoltSound, "Bolt Sound" }, { ColumnId_BoltSound, "Bolt Sound" },
{ ColumnId_PathgridPoints, "Points" }, { ColumnId_PathgridPoints, "Points" },
{ ColumnId_PathgridIndex, "Index" }, { ColumnId_PathgridIndex, "pIndex" },
{ ColumnId_PathgridPosX, "X" }, { ColumnId_PathgridPosX, "X" },
{ ColumnId_PathgridPosY, "Y" }, { ColumnId_PathgridPosY, "Y" },
{ ColumnId_PathgridPosZ, "Z" }, { ColumnId_PathgridPosZ, "Z" },
{ ColumnId_PathgridEdges, "Edges" }, { ColumnId_PathgridEdges, "Edges" },
{ ColumnId_PathgridEdgeIndex, "Index" }, { ColumnId_PathgridEdgeIndex, "eIndex" },
{ ColumnId_PathgridEdge0, "Point 0" }, { ColumnId_PathgridEdge0, "Point 0" },
{ ColumnId_PathgridEdge1, "Point 1" }, { ColumnId_PathgridEdge1, "Point 1" },
{ ColumnId_RegionSounds, "Sounds" }, { ColumnId_RegionSounds, "Sounds" },
{ ColumnId_SoundName, "Name" }, { ColumnId_SoundName, "Sound Name" },
{ ColumnId_SoundChance, "Chance" }, { ColumnId_SoundChance, "Chance" },
{ ColumnId_FactionReactions, "Reactions" }, { ColumnId_FactionReactions, "Reactions" },
@ -250,7 +250,7 @@ namespace CSMWorld
{ ColumnId_AiPackageList, "Ai Packages" }, { ColumnId_AiPackageList, "Ai Packages" },
{ ColumnId_AiPackageType, "Package" }, { ColumnId_AiPackageType, "Package" },
{ ColumnId_AiWanderDist, "Wander Dist" }, { ColumnId_AiWanderDist, "Wander Dist" },
{ ColumnId_AiDuration, "Duration" }, { ColumnId_AiDuration, "Ai Duration" },
{ ColumnId_AiWanderToD, "Wander ToD" }, { ColumnId_AiWanderToD, "Wander ToD" },
{ ColumnId_AiWanderIdle, "Wander Idle" }, { ColumnId_AiWanderIdle, "Wander Idle" },
{ ColumnId_AiWanderRepeat, "Wander Repeat" }, { ColumnId_AiWanderRepeat, "Wander Repeat" },
@ -260,11 +260,11 @@ namespace CSMWorld
{ ColumnId_PartRefList, "Part Reference" }, { ColumnId_PartRefList, "Part Reference" },
{ ColumnId_PartRefType, "Type" }, { ColumnId_PartRefType, "Type" },
{ ColumnId_PartRefMale, "Male" }, { ColumnId_PartRefMale, "Male Part" },
{ ColumnId_PartRefFemale, "Female" }, { ColumnId_PartRefFemale, "Female Part" },
{ ColumnId_LevelledList,"Levelled List" }, { ColumnId_LevelledList,"Levelled List" },
{ ColumnId_LevelledItemId,"Item ID" }, { ColumnId_LevelledItemId,"Levelled Item" },
{ ColumnId_LevelledItemLevel,"Level" }, { ColumnId_LevelledItemLevel,"Level" },
{ ColumnId_LevelledItemType, "Calculate all levels <= player" }, { ColumnId_LevelledItemType, "Calculate all levels <= player" },
{ ColumnId_LevelledItemTypeEach, "Select a new item each instance" }, { ColumnId_LevelledItemTypeEach, "Select a new item each instance" },
@ -278,9 +278,39 @@ namespace CSMWorld
{ ColumnId_InfoCondFunc, "Function" }, { ColumnId_InfoCondFunc, "Function" },
{ ColumnId_InfoCondVar, "Func/Variable" }, { ColumnId_InfoCondVar, "Func/Variable" },
{ ColumnId_InfoCondComp, "Comp" }, { ColumnId_InfoCondComp, "Comp" },
{ ColumnId_InfoCondValue, "Value" }, { ColumnId_InfoCondValue, "Values" },
{ ColumnId_OriginalCell, "Original Cell" }, { 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_UseValue1, "Use value 1" },
{ ColumnId_UseValue2, "Use value 2" }, { ColumnId_UseValue2, "Use value 2" },
{ ColumnId_UseValue3, "Use value 3" }, { ColumnId_UseValue3, "Use value 3" },
@ -551,6 +581,7 @@ namespace
// FIXME: don't have dynamic value enum delegate, use Display_String for now // FIXME: don't have dynamic value enum delegate, use Display_String for now
//case CSMWorld::Columns::ColumnId_InfoCond: return sInfoCond; //case CSMWorld::Columns::ColumnId_InfoCond: return sInfoCond;
case CSMWorld::Columns::ColumnId_InfoCondComp: return sInfoCondComp; case CSMWorld::Columns::ColumnId_InfoCondComp: return sInfoCondComp;
case CSMWorld::Columns::ColumnId_RaceSkill: return sSkills;
default: return 0; default: return 0;
} }

@ -272,6 +272,36 @@ namespace CSMWorld
ColumnId_OriginalCell = 247, 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 // Allocated to a separate value range, so we don't get a collision should we ever need
// to extend the number of use values. // to extend the number of use values.
ColumnId_UseValue1 = 0x10000, ColumnId_UseValue1 = 0x10000,

@ -136,6 +136,25 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc
mRaces.addAdapter (std::make_pair(&mRaces.getColumn(index), new SpellListAdapter<ESM::Race> ())); mRaces.addAdapter (std::make_pair(&mRaces.getColumn(index), new SpellListAdapter<ESM::Race> ()));
mRaces.getNestableColumn(index)->addColumn( mRaces.getNestableColumn(index)->addColumn(
new NestedChildColumn (Columns::ColumnId_SpellId, ColumnBase::Display_String)); new NestedChildColumn (Columns::ColumnId_SpellId, ColumnBase::Display_String));
// Race attributes
mRaces.addColumn (new NestedParentColumn<ESM::Race> (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<ESM::Race> (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<ESM::Sound>); mSounds.addColumn (new StringIdColumn<ESM::Sound>);
mSounds.addColumn (new RecordStateColumn<ESM::Sound>); mSounds.addColumn (new RecordStateColumn<ESM::Sound>);
@ -269,10 +288,32 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc
mCells.addColumn (new FixedRecordTypeColumn<Cell> (UniversalId::Type_Cell)); mCells.addColumn (new FixedRecordTypeColumn<Cell> (UniversalId::Type_Cell));
mCells.addColumn (new NameColumn<Cell>); mCells.addColumn (new NameColumn<Cell>);
mCells.addColumn (new FlagColumn<Cell> (Columns::ColumnId_SleepForbidden, ESM::Cell::NoSleep)); mCells.addColumn (new FlagColumn<Cell> (Columns::ColumnId_SleepForbidden, ESM::Cell::NoSleep));
mCells.addColumn (new FlagColumn<Cell> (Columns::ColumnId_InteriorWater, ESM::Cell::HasWater)); mCells.addColumn (new FlagColumn<Cell> (Columns::ColumnId_InteriorWater, ESM::Cell::HasWater,
mCells.addColumn (new FlagColumn<Cell> (Columns::ColumnId_InteriorSky, ESM::Cell::QuasiEx)); ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue | ColumnBase::Flag_Dialogue_Refresh));
mCells.addColumn (new FlagColumn<Cell> (Columns::ColumnId_InteriorSky, ESM::Cell::QuasiEx,
ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue | ColumnBase::Flag_Dialogue_Refresh));
mCells.addColumn (new RegionColumn<Cell>); mCells.addColumn (new RegionColumn<Cell>);
mCells.addColumn (new RefNumCounterColumn<Cell>); mCells.addColumn (new RefNumCounterColumn<Cell>);
// Misc Cell data
mCells.addColumn (new NestedParentColumn<Cell> (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<ESM::Enchantment>); mEnchantments.addColumn (new StringIdColumn<ESM::Enchantment>);
mEnchantments.addColumn (new RecordStateColumn<ESM::Enchantment>); mEnchantments.addColumn (new RecordStateColumn<ESM::Enchantment>);
@ -309,7 +350,8 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc
mBodyParts.addColumn (new BodyPartTypeColumn<ESM::BodyPart>); mBodyParts.addColumn (new BodyPartTypeColumn<ESM::BodyPart>);
mBodyParts.addColumn (new VampireColumn<ESM::BodyPart>); mBodyParts.addColumn (new VampireColumn<ESM::BodyPart>);
mBodyParts.addColumn (new FlagColumn<ESM::BodyPart> (Columns::ColumnId_Female, ESM::BodyPart::BPF_Female)); mBodyParts.addColumn (new FlagColumn<ESM::BodyPart> (Columns::ColumnId_Female, ESM::BodyPart::BPF_Female));
mBodyParts.addColumn (new FlagColumn<ESM::BodyPart> (Columns::ColumnId_Playable, ESM::BodyPart::BPF_NotPlayable, true)); mBodyParts.addColumn (new FlagColumn<ESM::BodyPart> (Columns::ColumnId_Playable,
ESM::BodyPart::BPF_NotPlayable, ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue, true));
mBodyParts.addColumn (new MeshTypeColumn<ESM::BodyPart>); mBodyParts.addColumn (new MeshTypeColumn<ESM::BodyPart>);
mBodyParts.addColumn (new ModelColumn<ESM::BodyPart>); mBodyParts.addColumn (new ModelColumn<ESM::BodyPart>);
mBodyParts.addColumn (new RaceColumn<ESM::BodyPart>); mBodyParts.addColumn (new RaceColumn<ESM::BodyPart>);
@ -356,7 +398,8 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc
// new objects deleted in dtor of NestableColumn // new objects deleted in dtor of NestableColumn
// WARNING: The order of the columns below are assumed in PathgridPointListAdapter // WARNING: The order of the columns below are assumed in PathgridPointListAdapter
mPathgrids.getNestableColumn(index)->addColumn( 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( mPathgrids.getNestableColumn(index)->addColumn(
new NestedChildColumn (Columns::ColumnId_PathgridPosX, ColumnBase::Display_Integer)); new NestedChildColumn (Columns::ColumnId_PathgridPosX, ColumnBase::Display_Integer));
mPathgrids.getNestableColumn(index)->addColumn( mPathgrids.getNestableColumn(index)->addColumn(
@ -368,7 +411,8 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc
index = mPathgrids.getColumns()-1; index = mPathgrids.getColumns()-1;
mPathgrids.addAdapter (std::make_pair(&mPathgrids.getColumn(index), new PathgridEdgeListAdapter ())); mPathgrids.addAdapter (std::make_pair(&mPathgrids.getColumn(index), new PathgridEdgeListAdapter ()));
mPathgrids.getNestableColumn(index)->addColumn( 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( mPathgrids.getNestableColumn(index)->addColumn(
new NestedChildColumn (Columns::ColumnId_PathgridEdge0, ColumnBase::Display_Integer)); new NestedChildColumn (Columns::ColumnId_PathgridEdge0, ColumnBase::Display_Integer));
mPathgrids.getNestableColumn(index)->addColumn( 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), addModel (new IdTree (&mTopicInfos, &mTopicInfos, IdTable::Feature_ReorderWithinTopic),
UniversalId::Type_TopicInfo); UniversalId::Type_TopicInfo);
addModel (new IdTable (&mJournalInfos, IdTable::Feature_ReorderWithinTopic), UniversalId::Type_JournalInfo); 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 IdTree (&mEnchantments, &mEnchantments), UniversalId::Type_Enchantment);
addModel (new IdTable (&mBodyParts), UniversalId::Type_BodyPart); addModel (new IdTable (&mBodyParts), UniversalId::Type_BodyPart);
addModel (new IdTable (&mSoundGens), UniversalId::Type_SoundGen); addModel (new IdTable (&mSoundGens), UniversalId::Type_SoundGen);

@ -95,7 +95,7 @@ namespace CSMWorld
IdCollection<ESM::StartScript> mStartScripts; IdCollection<ESM::StartScript> mStartScripts;
NestedInfoCollection mTopicInfos; NestedInfoCollection mTopicInfos;
InfoCollection mJournalInfos; InfoCollection mJournalInfos;
IdCollection<Cell> mCells; NestedIdCollection<Cell> mCells;
IdCollection<LandTexture> mLandTextures; IdCollection<LandTexture> mLandTextures;
IdCollection<Land> mLand; IdCollection<Land> mLand;
RefIdCollection mReferenceables; RefIdCollection mReferenceables;

@ -74,8 +74,7 @@ bool CSMWorld::IdTable::setData (const QModelIndex &index, const QVariant &value
{ {
mIdCollection->setData (index.row(), index.column(), value); mIdCollection->setData (index.row(), index.column(), value);
emit dataChanged (CSMWorld::IdTable::index (index.row(), 0), emit dataChanged (index, index);
CSMWorld::IdTable::index (index.row(), mIdCollection->getColumns()-1));
return true; return true;
} }
@ -160,7 +159,7 @@ void CSMWorld::IdTable::setRecord (const std::string& id, const RecordBase& reco
if (index==-1) if (index==-1)
{ {
int index = mIdCollection->getAppendIndex (id); int index = mIdCollection->getAppendIndex (id, type);
beginInsertRows (QModelIndex(), index, index); beginInsertRows (QModelIndex(), index, index);

@ -46,10 +46,16 @@ void CSMWorld::IdTableProxyModel::setFilter (const boost::shared_ptr<CSMFilter::
{ {
mFilter = filter; mFilter = filter;
updateColumnMap(); updateColumnMap();
invalidateFilter(); reset();
} }
bool CSMWorld::IdTableProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const bool CSMWorld::IdTableProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
{ {
return QSortFilterProxyModel::lessThan(left, right); return QSortFilterProxyModel::lessThan(left, right);
} }
void CSMWorld::IdTableProxyModel::refreshFilter()
{
updateColumnMap();
invalidateFilter();
}

@ -34,6 +34,8 @@ namespace CSMWorld
void setFilter (const boost::shared_ptr<CSMFilter::Node>& filter); void setFilter (const boost::shared_ptr<CSMFilter::Node>& filter);
void refreshFilter();
protected: protected:
bool lessThan(const QModelIndex &left, const QModelIndex &right) const; bool lessThan(const QModelIndex &left, const QModelIndex &right) const;

@ -74,7 +74,7 @@ QVariant CSMWorld::IdTree::nestedHeaderData(int section, int subSection, Qt::Ori
return tr(parentColumn->nestedColumn(subSection).getTitle().c_str()); return tr(parentColumn->nestedColumn(subSection).getTitle().c_str());
if (role==ColumnBase::Role_Flags) if (role==ColumnBase::Role_Flags)
return idCollection()->getColumn (section).mFlags; return parentColumn->nestedColumn(subSection).mFlags;
if (role==ColumnBase::Role_Display) if (role==ColumnBase::Role_Display)
return parentColumn->nestedColumn(subSection).mDisplayType; 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()); mNestedCollection->setNestedData(parentAddress.first, parentAddress.second, value, index.row(), index.column());
emit dataChanged (CSMWorld::IdTree::index (parentAddress.first, 0), emit dataChanged (index, index);
CSMWorld::IdTree::index (parentAddress.first, idCollection()->getColumns()-1));
return true; return true;
} }
else else

@ -173,6 +173,17 @@ CSMWorld::InfoCollection::Range CSMWorld::InfoCollection::getTopicRange (const s
RecordConstIterator begin = getRecords().begin()+iter->second; 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 // Find end
RecordConstIterator end = begin; RecordConstIterator end = begin;

@ -481,7 +481,7 @@ namespace CSMWorld
void InfoListAdapter::removeRow(Record<Info>& record, int rowToRemove) const void InfoListAdapter::removeRow(Record<Info>& 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<Info>& record, void InfoListAdapter::setTable(Record<Info>& record,
@ -880,4 +880,315 @@ namespace CSMWorld
{ {
return static_cast<int>(record.get().mSelects.size()); return static_cast<int>(record.get().mSelects.size());
} }
RaceAttributeAdapter::RaceAttributeAdapter () {}
void RaceAttributeAdapter::addRow(Record<ESM::Race>& record, int position) const
{
// Do nothing, this table cannot be changed by the user
}
void RaceAttributeAdapter::removeRow(Record<ESM::Race>& record, int rowToRemove) const
{
// Do nothing, this table cannot be changed by the user
}
void RaceAttributeAdapter::setTable(Record<ESM::Race>& record,
const NestedTableWrapperBase& nestedTable) const
{
ESM::Race race = record.get();
race.mData =
static_cast<const NestedTableWrapper<std::vector<ESM::Race::RADTstruct> >&>(nestedTable).mNestedTable.at(0);
record.setModified (race);
}
NestedTableWrapperBase* RaceAttributeAdapter::table(const Record<ESM::Race>& record) const
{
std::vector<typename ESM::Race::RADTstruct> wrap;
wrap.push_back(record.get().mData);
// deleted by dtor of NestedTableStoring
return new NestedTableWrapper<std::vector<ESM::Race::RADTstruct> >(wrap);
}
QVariant RaceAttributeAdapter::getData(const Record<ESM::Race>& 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<ESM::Race>& 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<ESM::Race>& record) const
{
return 3; // attrib, male, female
}
int RaceAttributeAdapter::getRowsCount(const Record<ESM::Race>& record) const
{
return ESM::Attribute::Length; // there are 8 attributes
}
RaceSkillsBonusAdapter::RaceSkillsBonusAdapter () {}
void RaceSkillsBonusAdapter::addRow(Record<ESM::Race>& record, int position) const
{
// Do nothing, this table cannot be changed by the user
}
void RaceSkillsBonusAdapter::removeRow(Record<ESM::Race>& record, int rowToRemove) const
{
// Do nothing, this table cannot be changed by the user
}
void RaceSkillsBonusAdapter::setTable(Record<ESM::Race>& record,
const NestedTableWrapperBase& nestedTable) const
{
ESM::Race race = record.get();
race.mData =
static_cast<const NestedTableWrapper<std::vector<ESM::Race::RADTstruct> >&>(nestedTable).mNestedTable.at(0);
record.setModified (race);
}
NestedTableWrapperBase* RaceSkillsBonusAdapter::table(const Record<ESM::Race>& record) const
{
std::vector<typename ESM::Race::RADTstruct> wrap;
wrap.push_back(record.get().mData);
// deleted by dtor of NestedTableStoring
return new NestedTableWrapper<std::vector<ESM::Race::RADTstruct> >(wrap);
}
QVariant RaceSkillsBonusAdapter::getData(const Record<ESM::Race>& record,
int subRowIndex, int subColIndex) const
{
ESM::Race race = record.get();
if (subRowIndex < 0 || subRowIndex >= static_cast<int>(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<ESM::Race>& record,
const QVariant& value, int subRowIndex, int subColIndex) const
{
ESM::Race race = record.get();
if (subRowIndex < 0 || subRowIndex >= static_cast<int>(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<ESM::Race>& record) const
{
return 2; // skill, bonus
}
int RaceSkillsBonusAdapter::getRowsCount(const Record<ESM::Race>& record) const
{
// there are 7 skill bonuses
return static_cast<int>(sizeof(record.get().mData.mBonus)/sizeof(record.get().mData.mBonus[0]));
}
CellListAdapter::CellListAdapter () {}
void CellListAdapter::addRow(Record<CSMWorld::Cell>& record, int position) const
{
throw std::logic_error ("cannot add a row to a fixed table");
}
void CellListAdapter::removeRow(Record<CSMWorld::Cell>& record, int rowToRemove) const
{
throw std::logic_error ("cannot remove a row to a fixed table");
}
void CellListAdapter::setTable(Record<CSMWorld::Cell>& record,
const NestedTableWrapperBase& nestedTable) const
{
throw std::logic_error ("table operation not supported");
}
NestedTableWrapperBase* CellListAdapter::table(const Record<CSMWorld::Cell>& record) const
{
throw std::logic_error ("table operation not supported");
}
QVariant CellListAdapter::getData(const Record<CSMWorld::Cell>& 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<CSMWorld::Cell>& 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<int32_t>(value.toInt());
else
return; // return without saving
break;
}
case 2:
{
if (isInterior && !behaveLikeExterior)
cell.mAmbi.mSunlight = static_cast<int32_t>(value.toInt());
else
return; // return without saving
break;
}
case 3:
{
if (isInterior && !behaveLikeExterior)
cell.mAmbi.mFog = static_cast<int32_t>(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<CSMWorld::Cell>& record) const
{
return 7;
}
int CellListAdapter::getRowsCount(const Record<CSMWorld::Cell>& record) const
{
return 1; // fixed at size 1
}
} }

@ -8,9 +8,11 @@
#include <components/esm/loadmgef.hpp> // for converting magic effect id to string & back #include <components/esm/loadmgef.hpp> // for converting magic effect id to string & back
#include <components/esm/loadskil.hpp> // for converting skill names #include <components/esm/loadskil.hpp> // for converting skill names
#include <components/esm/attr.hpp> // for converting attributes #include <components/esm/attr.hpp> // for converting attributes
#include <components/esm/loadrace.hpp>
#include "nestedcolumnadapter.hpp" #include "nestedcolumnadapter.hpp"
#include "nestedtablewrapper.hpp" #include "nestedtablewrapper.hpp"
#include "cell.hpp"
namespace ESM namespace ESM
{ {
@ -437,6 +439,81 @@ namespace CSMWorld
virtual int getRowsCount(const Record<Info>& record) const; virtual int getRowsCount(const Record<Info>& record) const;
}; };
class RaceAttributeAdapter : public NestedColumnAdapter<ESM::Race>
{
public:
RaceAttributeAdapter ();
virtual void addRow(Record<ESM::Race>& record, int position) const;
virtual void removeRow(Record<ESM::Race>& record, int rowToRemove) const;
virtual void setTable(Record<ESM::Race>& record,
const NestedTableWrapperBase& nestedTable) const;
virtual NestedTableWrapperBase* table(const Record<ESM::Race>& record) const;
virtual QVariant getData(const Record<ESM::Race>& record,
int subRowIndex, int subColIndex) const;
virtual void setData(Record<ESM::Race>& record,
const QVariant& value, int subRowIndex, int subColIndex) const;
virtual int getColumnsCount(const Record<ESM::Race>& record) const;
virtual int getRowsCount(const Record<ESM::Race>& record) const;
};
class RaceSkillsBonusAdapter : public NestedColumnAdapter<ESM::Race>
{
public:
RaceSkillsBonusAdapter ();
virtual void addRow(Record<ESM::Race>& record, int position) const;
virtual void removeRow(Record<ESM::Race>& record, int rowToRemove) const;
virtual void setTable(Record<ESM::Race>& record,
const NestedTableWrapperBase& nestedTable) const;
virtual NestedTableWrapperBase* table(const Record<ESM::Race>& record) const;
virtual QVariant getData(const Record<ESM::Race>& record,
int subRowIndex, int subColIndex) const;
virtual void setData(Record<ESM::Race>& record,
const QVariant& value, int subRowIndex, int subColIndex) const;
virtual int getColumnsCount(const Record<ESM::Race>& record) const;
virtual int getRowsCount(const Record<ESM::Race>& record) const;
};
class CellListAdapter : public NestedColumnAdapter<CSMWorld::Cell>
{
public:
CellListAdapter ();
virtual void addRow(Record<CSMWorld::Cell>& record, int position) const;
virtual void removeRow(Record<CSMWorld::Cell>& record, int rowToRemove) const;
virtual void setTable(Record<CSMWorld::Cell>& record,
const NestedTableWrapperBase& nestedTable) const;
virtual NestedTableWrapperBase* table(const Record<CSMWorld::Cell>& record) const;
virtual QVariant getData(const Record<CSMWorld::Cell>& record,
int subRowIndex, int subColIndex) const;
virtual void setData(Record<CSMWorld::Cell>& record,
const QVariant& value, int subRowIndex, int subColIndex) const;
virtual int getColumnsCount(const Record<CSMWorld::Cell>& record) const;
virtual int getRowsCount(const Record<CSMWorld::Cell>& record) const;
};
} }
#endif // CSM_WOLRD_NESTEDCOLADAPTERIMP_H #endif // CSM_WOLRD_NESTEDCOLADAPTERIMP_H

@ -468,7 +468,10 @@ CSMWorld::NpcColumns::NpcColumns (const ActorColumns& actorColumns)
mClass(NULL), mClass(NULL),
mFaction(NULL), mFaction(NULL),
mHair(NULL), mHair(NULL),
mHead(NULL) mHead(NULL),
mAttributes(NULL),
mSkills(NULL),
mMisc(NULL)
{} {}
CSMWorld::NpcRefIdAdapter::NpcRefIdAdapter (const NpcColumns& columns) CSMWorld::NpcRefIdAdapter::NpcRefIdAdapter (const NpcColumns& columns)
@ -496,6 +499,17 @@ QVariant CSMWorld::NpcRefIdAdapter::getData (const RefIdColumn *column, const Re
if (column==mColumns.mHead) if (column==mColumns.mHead)
return QString::fromUtf8 (record.get().mHead.c_str()); 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 RefIdColumn *, unsigned int>::const_iterator iter = std::map<const RefIdColumn *, unsigned int>::const_iterator iter =
mColumns.mFlags.find (column); 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<ESM::NPC>& record =
static_cast<Record<ESM::NPC>&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Npc)));
ESM::NPC npc = record.get();
// store the whole struct
npc.mNpdt52 =
static_cast<const NestedTableWrapper<std::vector<typename ESM::NPC::NPDTstruct52> > &>(nestedTable).mNestedTable.at(0);
record.setModified (npc);
}
CSMWorld::NestedTableWrapperBase* CSMWorld::NpcAttributesRefIdAdapter::nestedTable (const RefIdColumn* column,
const RefIdData& data, int index) const
{
const Record<ESM::NPC>& record =
static_cast<const Record<ESM::NPC>&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Npc)));
// return the whole struct
std::vector<typename ESM::NPC::NPDTstruct52> wrap;
wrap.push_back(record.get().mNpdt52);
// deleted by dtor of NestedTableStoring
return new NestedTableWrapper<std::vector<typename ESM::NPC::NPDTstruct52> >(wrap);
}
QVariant CSMWorld::NpcAttributesRefIdAdapter::getNestedData (const RefIdColumn *column,
const RefIdData& data, int index, int subRowIndex, int subColIndex) const
{
const Record<ESM::NPC>& record =
static_cast<const Record<ESM::NPC>&> (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<int>(npcStruct.mStrength);
case 1: return static_cast<int>(npcStruct.mIntelligence);
case 2: return static_cast<int>(npcStruct.mWillpower);
case 3: return static_cast<int>(npcStruct.mAgility);
case 4: return static_cast<int>(npcStruct.mSpeed);
case 5: return static_cast<int>(npcStruct.mEndurance);
case 6: return static_cast<int>(npcStruct.mPersonality);
case 7: return static_cast<int>(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<ESM::NPC>& record =
static_cast<Record<ESM::NPC>&> (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<unsigned char>(value.toInt()); break;
case 1: npcStruct.mIntelligence = static_cast<unsigned char>(value.toInt()); break;
case 2: npcStruct.mWillpower = static_cast<unsigned char>(value.toInt()); break;
case 3: npcStruct.mAgility = static_cast<unsigned char>(value.toInt()); break;
case 4: npcStruct.mSpeed = static_cast<unsigned char>(value.toInt()); break;
case 5: npcStruct.mEndurance = static_cast<unsigned char>(value.toInt()); break;
case 6: npcStruct.mPersonality = static_cast<unsigned char>(value.toInt()); break;
case 7: npcStruct.mLuck = static_cast<unsigned char>(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<ESM::NPC>& record =
static_cast<Record<ESM::NPC>&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Npc)));
ESM::NPC npc = record.get();
// store the whole struct
npc.mNpdt52 =
static_cast<const NestedTableWrapper<std::vector<typename ESM::NPC::NPDTstruct52> > &>(nestedTable).mNestedTable.at(0);
record.setModified (npc);
}
CSMWorld::NestedTableWrapperBase* CSMWorld::NpcSkillsRefIdAdapter::nestedTable (const RefIdColumn* column,
const RefIdData& data, int index) const
{
const Record<ESM::NPC>& record =
static_cast<const Record<ESM::NPC>&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Npc)));
// return the whole struct
std::vector<typename ESM::NPC::NPDTstruct52> wrap;
wrap.push_back(record.get().mNpdt52);
// deleted by dtor of NestedTableStoring
return new NestedTableWrapper<std::vector<typename ESM::NPC::NPDTstruct52> >(wrap);
}
QVariant CSMWorld::NpcSkillsRefIdAdapter::getNestedData (const RefIdColumn *column,
const RefIdData& data, int index, int subRowIndex, int subColIndex) const
{
const Record<ESM::NPC>& record =
static_cast<const Record<ESM::NPC>&> (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<int>(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<ESM::NPC>& record =
static_cast<Record<ESM::NPC>&> (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<unsigned char>(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<ESM::NPC>& record =
static_cast<const Record<ESM::NPC>&> (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<int>(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<int>(record.get().mNpdt12.mDisposition);
case 6: return static_cast<int>(record.get().mNpdt12.mReputation);
case 7: return static_cast<int>(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<int>(record.get().mNpdt52.mLevel);
case 1: return static_cast<int>(record.get().mNpdt52.mFactionID);
case 2: return static_cast<int>(record.get().mNpdt52.mHealth);
case 3: return static_cast<int>(record.get().mNpdt52.mMana);
case 4: return static_cast<int>(record.get().mNpdt52.mFatigue);
case 5: return static_cast<int>(record.get().mNpdt52.mDisposition);
case 6: return static_cast<int>(record.get().mNpdt52.mReputation);
case 7: return static_cast<int>(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<ESM::NPC>& record =
static_cast<Record<ESM::NPC>&> (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<short>(value.toInt()); break;
case 1: return;
case 2: return;
case 3: return;
case 4: return;
case 5: npc.mNpdt12.mDisposition = static_cast<signed char>(value.toInt()); break;
case 6: npc.mNpdt12.mReputation = static_cast<signed char>(value.toInt()); break;
case 7: npc.mNpdt12.mRank = static_cast<signed char>(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<short>(value.toInt()); break;
case 1: npc.mNpdt52.mFactionID = static_cast<char>(value.toInt()); break;
case 2: npc.mNpdt52.mHealth = static_cast<unsigned short>(value.toInt()); break;
case 3: npc.mNpdt52.mMana = static_cast<unsigned short>(value.toInt()); break;
case 4: npc.mNpdt52.mFatigue = static_cast<unsigned short>(value.toInt()); break;
case 5: npc.mNpdt52.mDisposition = static_cast<signed char>(value.toInt()); break;
case 6: npc.mNpdt52.mReputation = static_cast<signed char>(value.toInt()); break;
case 7: npc.mNpdt52.mRank = static_cast<signed char>(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) CSMWorld::WeaponColumns::WeaponColumns (const EnchantableColumns& columns)
: EnchantableColumns (columns) {} : EnchantableColumns (columns) {}

@ -792,6 +792,9 @@ namespace CSMWorld
const RefIdColumn *mFaction; const RefIdColumn *mFaction;
const RefIdColumn *mHair; const RefIdColumn *mHair;
const RefIdColumn *mHead; 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); NpcColumns (const ActorColumns& actorColumns);
}; };
@ -842,8 +845,100 @@ namespace CSMWorld
///< If the data type does not match an exception is thrown. ///< If the data type does not match an exception is thrown.
}; };
class NestedRefIdAdapterBase; 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<typename ESXRecordT> template<typename ESXRecordT>
class EffectsListAdapter; class EffectsListAdapter;
@ -1881,7 +1976,7 @@ namespace CSMWorld
{ {
switch (subColIndex) 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 1: return record.get().mFlags & ESM::CreatureLevList::AllLevels;
case 2: return static_cast<int> (record.get().mChanceNone); case 2: return static_cast<int> (record.get().mChanceNone);
default: default:

@ -245,7 +245,8 @@ CSMWorld::RefIdCollection::RefIdCollection()
actorsColumns.mServices.insert (std::make_pair (&mColumns.back(), sServiceTable[i].mFlag)); 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(); const RefIdColumn *autoCalc = &mColumns.back();
mColumns.push_back (RefIdColumn (Columns::ColumnId_ApparatusType, mColumns.push_back (RefIdColumn (Columns::ColumnId_ApparatusType,
@ -427,6 +428,62 @@ CSMWorld::RefIdCollection::RefIdCollection()
npcColumns.mFlags.insert (std::make_pair (metalBlood, ESM::NPC::Metal)); 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<UniversalId::Type, NestedRefIdAdapterBase*> 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<UniversalId::Type, NestedRefIdAdapterBase*> 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<UniversalId::Type, NestedRefIdAdapterBase*> 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); WeaponColumns weaponColumns (enchantableColumns);
mColumns.push_back (RefIdColumn (Columns::ColumnId_WeaponType, ColumnBase::Display_WeaponType)); mColumns.push_back (RefIdColumn (Columns::ColumnId_WeaponType, ColumnBase::Display_WeaponType));

@ -66,3 +66,9 @@ void CSVDoc::NewGameDialogue::create()
{ {
emit createRequest (mAdjusterWidget->getPath()); emit createRequest (mAdjusterWidget->getPath());
} }
void CSVDoc::NewGameDialogue::reject()
{
emit cancelCreateGame ();
QDialog::reject();
}

@ -36,11 +36,15 @@ namespace CSVDoc
void createRequest (const boost::filesystem::path& file); void createRequest (const boost::filesystem::path& file);
void cancelCreateGame ();
private slots: private slots:
void stateChanged (bool valid); void stateChanged (bool valid);
void create(); void create();
void reject();
}; };
} }

@ -404,11 +404,7 @@ CSVDoc::View::View (ViewManager& viewManager, CSMDoc::Document *document, int to
width = std::max(width, 300); width = std::max(width, 300);
height = std::max(height, 300); height = std::max(height, 300);
// trick to get the window decorations and their sizes resize (width, height);
show();
hide();
resize (width - (frameGeometry().width() - geometry().width()),
height - (frameGeometry().height() - geometry().height()));
mSubViewWindow.setDockOptions (QMainWindow::AllowNestedDocks); mSubViewWindow.setDockOptions (QMainWindow::AllowNestedDocks);

@ -92,7 +92,8 @@ CSVDoc::ViewManager::ViewManager (CSMDoc::DocumentManager& documentManager)
{ CSMWorld::ColumnBase::Display_AiPackageType, CSMWorld::Columns::ColumnId_AiPackageType, false }, { CSMWorld::ColumnBase::Display_AiPackageType, CSMWorld::Columns::ColumnId_AiPackageType, false },
{ CSMWorld::ColumnBase::Display_YesNo, CSMWorld::Columns::ColumnId_AiWanderRepeat, false }, { CSMWorld::ColumnBase::Display_YesNo, CSMWorld::Columns::ColumnId_AiWanderRepeat, false },
{ CSMWorld::ColumnBase::Display_InfoCondFunc, CSMWorld::Columns::ColumnId_InfoCondFunc, 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<sizeof (sMapping)/sizeof (Mapping); ++i) for (std::size_t i=0; i<sizeof (sMapping)/sizeof (Mapping); ++i)

@ -0,0 +1,53 @@
#include "dialoguespinbox.hpp"
#include <QWheelEvent>
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);
}

@ -0,0 +1,40 @@
#ifndef CSV_WORLD_DIALOGUESPINBOX_H
#define CSV_WORLD_DIALOGUESPINBOX_H
#include <QSpinBox>
#include <QDoubleSpinBox>
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

@ -20,6 +20,7 @@
#include <QPushButton> #include <QPushButton>
#include <QToolButton> #include <QToolButton>
#include <QHeaderView> #include <QHeaderView>
#include <QScrollBar>
#include "../../model/world/nestedtableproxymodel.hpp" #include "../../model/world/nestedtableproxymodel.hpp"
#include "../../model/world/columnbase.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 void CSVWorld::DialogueDelegateDispatcher::setEditorData (QWidget* editor, const QModelIndex& index) const
{ {
CSMWorld::ColumnBase::Display display = static_cast<CSMWorld::ColumnBase::Display> CSMWorld::ColumnBase::Display display = CSMWorld::ColumnBase::Display_None;
(mTable->headerData (index.column(), Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); if (index.parent().isValid())
{
display = static_cast<CSMWorld::ColumnBase::Display>
(static_cast<CSMWorld::IdTree *>(mTable)->nestedHeaderData (index.parent().column(), index.column(), Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt());
}
else
{
display = static_cast<CSMWorld::ColumnBase::Display>
(mTable->headerData (index.column(), Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt());
}
QLabel* label = qobject_cast<QLabel*>(editor); QLabel* label = qobject_cast<QLabel*>(editor);
if(label) if(label)
@ -347,10 +357,13 @@ CSVWorld::DialogueDelegateDispatcher::~DialogueDelegateDispatcher()
CSVWorld::EditWidget::~EditWidget() CSVWorld::EditWidget::~EditWidget()
{ {
for (unsigned i = 0; i < mNestedModels.size(); ++i) for (unsigned i = 0; i < mNestedModels.size(); ++i)
{
delete mNestedModels[i]; delete mNestedModels[i];
}
delete mNestedTableDispatcher; if (mDispatcher)
delete mDispatcher;
if (mNestedTableDispatcher)
delete mNestedTableDispatcher;
} }
CSVWorld::EditWidget::EditWidget(QWidget *parent, CSVWorld::EditWidget::EditWidget(QWidget *parent,
@ -359,7 +372,7 @@ CSVWorld::EditWidget::EditWidget(QWidget *parent,
QScrollArea(parent), QScrollArea(parent),
mWidgetMapper(NULL), mWidgetMapper(NULL),
mNestedTableMapper(NULL), mNestedTableMapper(NULL),
mDispatcher(this, table, commandDispatcher, document), mDispatcher(NULL),
mNestedTableDispatcher(NULL), mNestedTableDispatcher(NULL),
mMainWidget(NULL), mMainWidget(NULL),
mTable(table), mTable(table),
@ -368,41 +381,42 @@ mDocument (document)
{ {
remake (row); 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*))); this, SIGNAL(tableMimeDataDropped(QWidget*, const QModelIndex&, const CSMWorld::UniversalId&, const CSMDoc::Document*)));
} }
void CSVWorld::EditWidget::remake(int row) void CSVWorld::EditWidget::remake(int row)
{ {
for (unsigned i = 0; i < mNestedModels.size(); ++i)
{
delete mNestedModels[i];
}
mNestedModels.clear();
delete mNestedTableDispatcher;
if (mMainWidget) if (mMainWidget)
{ {
delete mMainWidget; QWidget *del = this->takeWidget();
mMainWidget = 0; del->deleteLater();
} }
mMainWidget = new QWidget (this); 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 //not sure if widget mapper can handle deleting the widgets that were mapped
if (mWidgetMapper) if (mWidgetMapper)
{
delete mWidgetMapper; delete mWidgetMapper;
mWidgetMapper = 0;
} mWidgetMapper = new QDataWidgetMapper (this);
mWidgetMapper->setModel(mTable);
mWidgetMapper->setItemDelegate(mDispatcher);
if (mNestedTableMapper) if (mNestedTableMapper)
{
delete mNestedTableMapper; delete mNestedTableMapper;
mNestedTableMapper = 0;
}
mWidgetMapper = new QDataWidgetMapper (this);
mWidgetMapper->setModel(mTable);
mWidgetMapper->setItemDelegate(&mDispatcher);
QFrame* line = new QFrame(mMainWidget); QFrame* line = new QFrame(mMainWidget);
line->setObjectName(QString::fromUtf8("line")); line->setObjectName(QString::fromUtf8("line"));
@ -457,7 +471,14 @@ void CSVWorld::EditWidget::remake(int row)
NestedTable* table = new NestedTable(mDocument, id, mNestedModels.back(), this); NestedTable* table = new NestedTable(mDocument, id, mNestedModels.back(), this);
// FIXME: does not work well when enum delegates are used // FIXME: does not work well when enum delegates are used
//table->resizeColumnsToContents(); //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 rows = mTable->rowCount(mTable->index(row, i));
int rowHeight = (rows == 0) ? table->horizontalHeader()->height() : table->rowHeight(0); 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); new QLabel (mTable->headerData (i, Qt::Horizontal, Qt::DisplayRole).toString(), mMainWidget);
label->setSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed); label->setSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed);
if(mTable->index(row, i).data().type() == QVariant::UserType)
label->setEnabled(false);
tablesLayout->addWidget(label); tablesLayout->addWidget(label);
tablesLayout->addWidget(table); tablesLayout->addWidget(table);
} }
else if (!(flags & CSMWorld::ColumnBase::Flag_Dialogue_List)) else if (!(flags & CSMWorld::ColumnBase::Flag_Dialogue_List))
{ {
mDispatcher.makeDelegate (display); mDispatcher->makeDelegate (display);
QWidget* editor = mDispatcher.makeEditor (display, (mTable->index (row, i))); QWidget* editor = mDispatcher->makeEditor (display, (mTable->index (row, i)));
if (editor) if (editor)
{ {
@ -499,26 +522,30 @@ void CSVWorld::EditWidget::remake(int row)
unlockedLayout->addWidget (editor, unlocked, 1); unlockedLayout->addWidget (editor, unlocked, 1);
++unlocked; ++unlocked;
} }
if(mTable->index(row, i).data().type() == QVariant::UserType)
{
editor->setEnabled(false);
label->setEnabled(false);
}
} }
} }
else else
{ {
mNestedModels.push_back(new CSMWorld::NestedTableProxyModel ( CSMWorld::IdTree *tree = static_cast<CSMWorld::IdTree *>(mTable);
static_cast<CSMWorld::IdTree *>(mTable)->index(row, i),
display, static_cast<CSMWorld::IdTree *>(mTable)));
mNestedTableMapper = new QDataWidgetMapper (this); mNestedTableMapper = new QDataWidgetMapper (this);
mNestedTableMapper->setModel(mNestedModels.back()); mNestedTableMapper->setModel(tree);
// FIXME: lack MIME support? // FIXME: lack MIME support?
mNestedTableDispatcher = 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); mNestedTableMapper->setItemDelegate(mNestedTableDispatcher);
int columnCount = int columnCount = tree->columnCount(tree->index(row, i));
mTable->columnCount(mTable->getModelIndex (mNestedModels.back()->getParentId(), i));
for (int col = 0; col < columnCount; ++col) 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(); Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt();
CSMWorld::ColumnBase::Display display = CSMWorld::ColumnBase::Display display =
@ -528,16 +555,16 @@ void CSVWorld::EditWidget::remake(int row)
// FIXME: assumed all columns are editable // FIXME: assumed all columns are editable
QWidget* editor = QWidget* editor =
mNestedTableDispatcher->makeEditor (display, mNestedModels.back()->index (0, col)); mNestedTableDispatcher->makeEditor (display, tree->index (0, col, tree->index(row, i)));
if (editor) if (editor)
{ {
mNestedTableMapper->addMapping (editor, col); mNestedTableMapper->addMapping (editor, col);
std::string disString = mNestedModels.back()->headerData (col, std::string disString = tree->nestedHeaderData (i, col,
Qt::Horizontal, Qt::DisplayRole).toString().toStdString(); 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 // 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); Qt::Horizontal, Qt::DisplayRole).toString(), mMainWidget);
label->setSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed); label->setSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed);
@ -546,9 +573,15 @@ void CSVWorld::EditWidget::remake(int row)
unlockedLayout->addWidget (label, unlocked, 0); unlockedLayout->addWidget (label, unlocked, 0);
unlockedLayout->addWidget (editor, unlocked, 1); unlockedLayout->addWidget (editor, unlocked, 1);
++unlocked; ++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())) mCommandDispatcher (document, CSMWorld::UniversalId::getParentType (id.getType()))
{ {
connect(mTable, SIGNAL(dataChanged (const QModelIndex&, const QModelIndex&)), this, SLOT(dataChanged(const QModelIndex&))); 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()); changeCurrentId(id.getId());
@ -740,6 +774,9 @@ void CSVWorld::DialogueSubView::nextId ()
void CSVWorld::DialogueSubView::setEditLock (bool locked) void CSVWorld::DialogueSubView::setEditLock (bool locked)
{ {
if (!mEditWidget) // hack to indicate that mCurrentId is no longer valid
return;
mLocked = locked; mLocked = locked;
QModelIndex currentIndex(mTable->getModelIndex(mCurrentId, 0)); QModelIndex currentIndex(mTable->getModelIndex(mCurrentId, 0));
@ -758,11 +795,47 @@ void CSVWorld::DialogueSubView::dataChanged (const QModelIndex & index)
{ {
QModelIndex currentIndex(mTable->getModelIndex(mCurrentId, 0)); 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<CSMWorld::RecordBase::State>(mTable->data (mTable->index (currentIndex.row(), 1)).toInt()); CSMWorld::RecordBase::State state = static_cast<CSMWorld::RecordBase::State>(mTable->data (mTable->index (currentIndex.row(), 1)).toInt());
mEditWidget->setDisabled (state==CSMWorld::RecordBase::State_Deleted || mLocked); 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<CSMWorld::IdTree *>(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);
} }
} }

@ -165,7 +165,7 @@ namespace CSVWorld
Q_OBJECT Q_OBJECT
QDataWidgetMapper *mWidgetMapper; QDataWidgetMapper *mWidgetMapper;
QDataWidgetMapper *mNestedTableMapper; QDataWidgetMapper *mNestedTableMapper;
DialogueDelegateDispatcher mDispatcher; DialogueDelegateDispatcher *mDispatcher;
DialogueDelegateDispatcher *mNestedTableDispatcher; DialogueDelegateDispatcher *mNestedTableDispatcher;
QWidget* mMainWidget; QWidget* mMainWidget;
CSMWorld::IdTable* mTable; CSMWorld::IdTable* mTable;
@ -235,6 +235,8 @@ namespace CSVWorld
const CSMDoc::Document* document); const CSMDoc::Document* document);
void requestFocus (const std::string& id); void requestFocus (const std::string& id);
void rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end);
}; };
} }

@ -31,11 +31,11 @@ bool CSVWorld::ScriptEdit::event (QEvent *event)
if (event->type()==QEvent::ShortcutOverride) if (event->type()==QEvent::ShortcutOverride)
{ {
QKeyEvent *keyEvent = static_cast<QKeyEvent *> (event); QKeyEvent *keyEvent = static_cast<QKeyEvent *> (event);
if (keyEvent->matches (QKeySequence::Undo) || keyEvent->matches (QKeySequence::Redo)) if (keyEvent->matches (QKeySequence::Undo) || keyEvent->matches (QKeySequence::Redo))
return true; return true;
} }
return QPlainTextEdit::event (event); return QPlainTextEdit::event (event);
} }
@ -92,13 +92,16 @@ CSVWorld::ScriptEdit::ScriptEdit (const CSMDoc::Document& document, ScriptHighli
connect (&mUpdateTimer, SIGNAL (timeout()), this, SLOT (updateHighlighting())); 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); mUpdateTimer.setSingleShot (true);
// TODO: provide a font selector dialogue // TODO: provide a font selector dialogue
mMonoFont.setStyleHint(QFont::TypeWriter); mMonoFont.setStyleHint(QFont::TypeWriter);
std::string useMonoFont =
CSMSettings::UserSettings::instance().setting("script-editor/mono-font", "true").toStdString(); if (userSettings.setting("script-editor/mono-font", "true") == "true")
if (useMonoFont == "true")
setFont(mMonoFont); setFont(mMonoFont);
mLineNumberArea = new LineNumberArea(this); 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(blockCountChanged(int)), this, SLOT(updateLineNumberAreaWidth(int)));
connect(this, SIGNAL(updateRequest(QRect,int)), this, SLOT(updateLineNumberArea(QRect,int))); connect(this, SIGNAL(updateRequest(QRect,int)), this, SLOT(updateLineNumberArea(QRect,int)));
std::string showStatusBar = showLineNum(userSettings.settingValue("script-editor/show-linenum") == "true");
CSMSettings::UserSettings::instance().settingValue("script-editor/show-linenum").toStdString(); }
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) void CSVWorld::ScriptEdit::showLineNum(bool show)

@ -56,7 +56,7 @@ namespace CSVWorld
protected: protected:
bool event (QEvent *event); bool event (QEvent *event);
public: public:
ScriptEdit (const CSMDoc::Document& document, ScriptHighlighter::Mode mode, ScriptEdit (const CSMDoc::Document& document, ScriptHighlighter::Mode mode,
@ -96,7 +96,12 @@ namespace CSVWorld
void updateHighlighting(); void updateHighlighting();
void updateLineNumberAreaWidth(int newBlockCount); void updateLineNumberAreaWidth(int newBlockCount);
void updateLineNumberArea(const QRect &, int); void updateLineNumberArea(const QRect &, int);
public slots:
void updateUserSetting (const QString &name, const QStringList &list);
}; };
class LineNumberArea : public QWidget class LineNumberArea : public QWidget

@ -6,6 +6,8 @@
#include <components/compiler/scanner.hpp> #include <components/compiler/scanner.hpp>
#include <components/compiler/extensions0.hpp> #include <components/compiler/extensions0.hpp>
#include "../../model/settings/usersettings.hpp"
bool CSVWorld::ScriptHighlighter::parseInt (int value, const Compiler::TokenLoc& loc, bool CSVWorld::ScriptHighlighter::parseInt (int value, const Compiler::TokenLoc& loc,
Compiler::Scanner& scanner) Compiler::Scanner& scanner)
{ {
@ -78,46 +80,77 @@ CSVWorld::ScriptHighlighter::ScriptHighlighter (const CSMWorld::Data& data, Mode
: QSyntaxHighlighter (parent), Compiler::Parser (mErrorHandler, mContext), mContext (data), : QSyntaxHighlighter (parent), Compiler::Parser (mErrorHandler, mContext), mContext (data),
mMode (mode) 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; QTextCharFormat format;
format.setForeground (Qt::darkMagenta); format.setForeground (color);
mScheme.insert (std::make_pair (Type_Int, format)); 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; QTextCharFormat format;
format.setForeground (Qt::magenta); format.setForeground (color);
mScheme.insert (std::make_pair (Type_Float, format)); 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; QTextCharFormat format;
format.setForeground (Qt::gray); format.setForeground (color);
mScheme.insert (std::make_pair (Type_Name, format)); 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; QTextCharFormat format;
format.setForeground (Qt::red); format.setForeground (color);
mScheme.insert (std::make_pair (Type_Keyword, format)); 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; QTextCharFormat format;
format.setForeground (Qt::darkYellow); format.setForeground (color);
mScheme.insert (std::make_pair (Type_Special, format)); 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; QTextCharFormat format;
format.setForeground (Qt::green); format.setForeground (color);
mScheme.insert (std::make_pair (Type_Comment, format)); 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; QTextCharFormat format;
format.setForeground (Qt::blue); format.setForeground (color);
mScheme.insert (std::make_pair (Type_Id, format)); mScheme.insert (std::make_pair (Type_Id, format));
} }
@ -143,3 +176,86 @@ void CSVWorld::ScriptHighlighter::invalidateIds()
{ {
mContext.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;
}

@ -87,6 +87,8 @@ namespace CSVWorld
virtual void highlightBlock (const QString& text); virtual void highlightBlock (const QString& text);
void invalidateIds(); void invalidateIds();
bool updateUserSetting (const QString &name, const QStringList &list);
}; };
} }

@ -649,6 +649,10 @@ void CSVWorld::Table::tableSizeUpdate()
} }
emit tableSizeChanged (size, deleted, modified); 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() void CSVWorld::Table::selectionSizeUpdate()

@ -9,8 +9,6 @@
#include <QMetaProperty> #include <QMetaProperty>
#include <QStyledItemDelegate> #include <QStyledItemDelegate>
#include <QLineEdit> #include <QLineEdit>
#include <QSpinBox>
#include <QDoubleSpinBox>
#include <QComboBox> #include <QComboBox>
#include <QCheckBox> #include <QCheckBox>
#include <QPlainTextEdit> #include <QPlainTextEdit>
@ -19,7 +17,7 @@
#include "../../model/world/commands.hpp" #include "../../model/world/commands.hpp"
#include "../../model/world/tablemimedata.hpp" #include "../../model/world/tablemimedata.hpp"
#include "../../model/world/commanddispatcher.hpp" #include "../../model/world/commanddispatcher.hpp"
#include "dialoguespinbox.hpp"
#include "scriptedit.hpp" #include "scriptedit.hpp"
CSVWorld::NastyTableModelHack::NastyTableModelHack (QAbstractItemModel& model) CSVWorld::NastyTableModelHack::NastyTableModelHack (QAbstractItemModel& model)
@ -174,7 +172,7 @@ QWidget *CSVWorld::CommandDelegate::createEditor (QWidget *parent, const QStyleO
case CSMWorld::ColumnBase::Display_Integer: case CSMWorld::ColumnBase::Display_Integer:
{ {
QSpinBox *sb = new QSpinBox(parent); DialogueSpinBox *sb = new DialogueSpinBox(parent);
sb->setRange(INT_MIN, INT_MAX); sb->setRange(INT_MIN, INT_MAX);
return sb; return sb;
} }
@ -185,7 +183,7 @@ QWidget *CSVWorld::CommandDelegate::createEditor (QWidget *parent, const QStyleO
case CSMWorld::ColumnBase::Display_Float: case CSMWorld::ColumnBase::Display_Float:
{ {
QDoubleSpinBox *dsb = new QDoubleSpinBox(parent); DialogueDoubleSpinBox *dsb = new DialogueDoubleSpinBox(parent);
dsb->setRange(-FLT_MAX, FLT_MAX); dsb->setRange(-FLT_MAX, FLT_MAX);
dsb->setSingleStep(0.01f); dsb->setSingleStep(0.01f);
dsb->setDecimals(3); dsb->setDecimals(3);

@ -17,7 +17,7 @@
#if defined(_WIN32) #if defined(_WIN32)
// For OutputDebugString // For OutputDebugString
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
#include <Windows.h> #include <windows.h>
// makes __argc and __argv available on windows // makes __argc and __argv available on windows
#include <cstdlib> #include <cstdlib>
#endif #endif

@ -146,7 +146,7 @@ namespace MWClass
if (ptr.getCellRef().getTeleport()) if (ptr.getCellRef().getTeleport())
{ {
boost::shared_ptr<MWWorld::Action> action(new MWWorld::ActionTeleport (ptr.getCellRef().getDestCell(), ptr.getCellRef().getDoorDest())); boost::shared_ptr<MWWorld::Action> action(new MWWorld::ActionTeleport (ptr.getCellRef().getDestCell(), ptr.getCellRef().getDoorDest(), true));
action->setSound(openSound); action->setSound(openSound);

@ -291,7 +291,10 @@ namespace MWGui
MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu);
} }
else else
{
MWBase::Environment::get().getDialogueManager()->goodbyeSelected(); MWBase::Environment::get().getDialogueManager()->goodbyeSelected();
mTopicsList->scrollToTop();
}
} }
void DialogueWindow::onWindowResize(MyGUI::Window* _sender) void DialogueWindow::onWindowResize(MyGUI::Window* _sender)

@ -183,7 +183,7 @@ namespace MWGui
MWBase::Environment::get().getDialogueManager()->goodbyeSelected(); MWBase::Environment::get().getDialogueManager()->goodbyeSelected();
// Teleports any followers, too. // Teleports any followers, too.
MWWorld::ActionTeleport action(interior ? cellname : "", pos); MWWorld::ActionTeleport action(interior ? cellname : "", pos, true);
action.execute(player); action.execute(player);
MWBase::Environment::get().getWindowManager()->fadeScreenOut(0); MWBase::Environment::get().getWindowManager()->fadeScreenOut(0);

@ -679,7 +679,7 @@ namespace MWMechanics
if (markedCell) if (markedCell)
{ {
MWWorld::ActionTeleport action(markedCell->isExterior() ? "" : markedCell->getCell()->mName, MWWorld::ActionTeleport action(markedCell->isExterior() ? "" : markedCell->getCell()->mName,
markedPosition); markedPosition, false);
action.execute(target); action.execute(target);
} }
} }

@ -227,7 +227,11 @@ const NpcAnimation::PartBoneMap NpcAnimation::sPartList = createPartListMap();
NpcAnimation::~NpcAnimation() 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); mPtr.getClass().getInventoryStore(mPtr).setListener(NULL, mPtr);
} }

@ -25,23 +25,26 @@ namespace
namespace MWWorld namespace MWWorld
{ {
ActionTeleport::ActionTeleport (const std::string& cellName, ActionTeleport::ActionTeleport (const std::string& cellName,
const ESM::Position& position) const ESM::Position& position, bool teleportFollowers)
: Action (true), mCellName (cellName), mPosition (position) : Action (true), mCellName (cellName), mPosition (position), mTeleportFollowers(teleportFollowers)
{ {
} }
void ActionTeleport::executeImp (const Ptr& actor) void ActionTeleport::executeImp (const Ptr& actor)
{ {
//find any NPC that is following the actor and teleport him too if (mTeleportFollowers)
std::set<MWWorld::Ptr> followers;
getFollowers(actor, followers);
for(std::set<MWWorld::Ptr>::iterator it = followers.begin();it != followers.end();++it)
{ {
MWWorld::Ptr follower = *it; //find any NPC that is following the actor and teleport him too
if (Ogre::Vector3(follower.getRefData().getPosition().pos).squaredDistance( std::set<MWWorld::Ptr> followers;
Ogre::Vector3( actor.getRefData().getPosition().pos)) getFollowers(actor, followers);
<= 800*800) for(std::set<MWWorld::Ptr>::iterator it = followers.begin();it != followers.end();++it)
teleport(*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); teleport(actor);

@ -13,6 +13,7 @@ namespace MWWorld
{ {
std::string mCellName; std::string mCellName;
ESM::Position mPosition; ESM::Position mPosition;
bool mTeleportFollowers;
/// Teleports this actor and also teleports anyone following that actor. /// Teleports this actor and also teleports anyone following that actor.
virtual void executeImp (const Ptr& actor); virtual void executeImp (const Ptr& actor);
@ -22,8 +23,9 @@ namespace MWWorld
public: 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. ///< If cellName is empty, an exterior cell is assumed.
/// @param teleportFollowers Whether to teleport any following actors of the target actor as well.
}; };
} }

@ -93,7 +93,7 @@ std::pair<std::string, MWWorld::Ptr> MWWorld::LocalScripts::getNext()
void MWWorld::LocalScripts::add (const std::string& scriptName, const Ptr& ptr) void MWWorld::LocalScripts::add (const std::string& scriptName, const Ptr& ptr)
{ {
if (const ESM::Script *script = mStore.get<ESM::Script>().find (scriptName)) if (const ESM::Script *script = mStore.get<ESM::Script>().search (scriptName))
{ {
try 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; << " 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) void MWWorld::LocalScripts::addCell (CellStore *cell)

@ -366,6 +366,19 @@ namespace MWWorld
inserted.first->second = scpt; inserted.first->second = scpt;
} }
template <>
inline void Store<ESM::StartScript>::load(ESM::ESMReader &esm, const std::string &id)
{
ESM::StartScript s;
s.load(esm);
s.mId = Misc::StringUtils::toLower(s.mId);
std::pair<typename Static::iterator, bool> inserted = mStatic.insert(std::make_pair(s.mId, s));
if (inserted.second)
mShared.push_back(&inserted.first->second);
else
inserted.first->second = s;
}
template <> template <>
class Store<ESM::LandTexture> : public StoreBase class Store<ESM::LandTexture> : public StoreBase
{ {

@ -1,6 +1,6 @@
#include "worldimp.hpp" #include "worldimp.hpp"
#ifdef _WIN32 #if defined(_WIN32) && !defined(__MINGW32__)
#include <boost/tr1/tr1/unordered_map> #include <boost/tr1/tr1/unordered_map>
#elif defined HAVE_UNORDERED_MAP #elif defined HAVE_UNORDERED_MAP
#include <unordered_map> #include <unordered_map>
@ -2876,7 +2876,7 @@ namespace MWWorld
if ( !closestMarker.mCell->isExterior() ) if ( !closestMarker.mCell->isExterior() )
cellName = closestMarker.mCell->getCell()->mName; cellName = closestMarker.mCell->getCell()->mName;
MWWorld::ActionTeleport action(cellName, closestMarker.getRefData().getPosition()); MWWorld::ActionTeleport action(cellName, closestMarker.getRefData().getPosition(), false);
action.execute(ptr); action.execute(ptr);
} }

@ -19,7 +19,46 @@ include(PreprocessorUtils)
# ENDIF (MYGUI_LIBRARIES AND MYGUI_INCLUDE_DIRS) # ENDIF (MYGUI_LIBRARIES AND MYGUI_INCLUDE_DIRS)
IF (WIN32) #Windows IF (WIN32) #Windows
MESSAGE(STATUS "Looking for MyGUI") 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}) SET(MYGUISDK $ENV{MYGUI_HOME})
IF (MYGUISDK) IF (MYGUISDK)
findpkg_begin ( "MYGUI" ) findpkg_begin ( "MYGUI" )

@ -136,6 +136,9 @@ set (ESM_UI ${CMAKE_SOURCE_DIR}/files/ui/contentselector.ui
) )
find_package(Qt4 COMPONENTS QtCore QtGui) find_package(Qt4 COMPONENTS QtCore QtGui)
if(MINGW)
find_package(Bullet REQUIRED COMPONENTS Collision)
endif()
if(QT_QTGUI_LIBRARY AND QT_QTCORE_LIBRARY) if(QT_QTGUI_LIBRARY AND QT_QTCORE_LIBRARY)
add_component_qt_dir (contentselector add_component_qt_dir (contentselector
@ -183,6 +186,14 @@ if (GIT_CHECKOUT)
add_dependencies (components git-version) add_dependencies (components git-version)
endif (GIT_CHECKOUT) 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 # Fix for not visible pthreads functions for linker with glibc 2.15
if (UNIX AND NOT APPLE) if (UNIX AND NOT APPLE)
target_link_libraries(components ${CMAKE_THREAD_LIBS_INIT}) target_link_libraries(components ${CMAKE_THREAD_LIBS_INIT})

@ -531,7 +531,7 @@ namespace Compiler
extensions.registerInstruction("placeitemcell","ccffff",opcodePlaceItemCell); extensions.registerInstruction("placeitemcell","ccffff",opcodePlaceItemCell);
extensions.registerInstruction("placeitem","cffff",opcodePlaceItem); extensions.registerInstruction("placeitem","cffff",opcodePlaceItem);
extensions.registerInstruction("placeatpc","clfl",opcodePlaceAtPc); 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("modscale","f",opcodeModScale,opcodeModScaleExplicit);
extensions.registerInstruction("rotate","cf",opcodeRotate,opcodeRotateExplicit); extensions.registerInstruction("rotate","cf",opcodeRotate,opcodeRotateExplicit);
extensions.registerInstruction("rotateworld","cf",opcodeRotateWorld,opcodeRotateWorldExplicit); extensions.registerInstruction("rotateworld","cf",opcodeRotateWorld,opcodeRotateWorldExplicit);

@ -18,9 +18,11 @@ namespace
///< Translate 8bit/24bit code (stored in refNum.mIndex) into a proper refNum ///< Translate 8bit/24bit code (stored in refNum.mIndex) into a proper refNum
void adjustRefNum (ESM::RefNum& refNum, ESM::ESMReader& reader) 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. // 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. // In this case, do not spawn a new reference, but overwrite the old one.

@ -1,7 +1,7 @@
#ifndef COMPONENTS_FILES_CONFIGURATIONMANAGER_HPP #ifndef COMPONENTS_FILES_CONFIGURATIONMANAGER_HPP
#define COMPONENTS_FILES_CONFIGURATIONMANAGER_HPP #define COMPONENTS_FILES_CONFIGURATIONMANAGER_HPP
#ifdef _WIN32 #if defined(_WIN32) && !defined(__MINGW32__)
#include <boost/tr1/tr1/unordered_map> #include <boost/tr1/tr1/unordered_map>
#elif defined HAVE_UNORDERED_MAP #elif defined HAVE_UNORDERED_MAP
#include <unordered_map> #include <unordered_map>

@ -6,9 +6,7 @@
#include <windows.h> #include <windows.h>
#include <shlobj.h> #include <shlobj.h>
#include <Shlwapi.h> #include <shlwapi.h>
#pragma comment(lib, "Shlwapi.lib")
#include <boost/locale.hpp> #include <boost/locale.hpp>
namespace bconv = boost::locale::conv; namespace bconv = boost::locale::conv;

@ -48,7 +48,7 @@ namespace Gui
const int _scrollBarWidth = 20; // fetch this from skin? const int _scrollBarWidth = 20; // fetch this from skin?
const int scrollBarWidth = scrollbarShown ? _scrollBarWidth : 0; const int scrollBarWidth = scrollbarShown ? _scrollBarWidth : 0;
const int spacing = 3; const int spacing = 3;
size_t viewPosition = -mScrollView->getViewOffset().top; int viewPosition = -mScrollView->getViewOffset().top;
while (mScrollView->getChildCount()) while (mScrollView->getChildCount())
{ {
@ -99,10 +99,10 @@ namespace Gui
if (!scrollbarShown && mItemHeight > mClient->getSize().height) if (!scrollbarShown && mItemHeight > mClient->getSize().height)
redraw(true); redraw(true);
size_t viewRange = mScrollView->getCanvasSize().height; int viewRange = mScrollView->getCanvasSize().height;
if(viewPosition > viewRange) if(viewPosition > viewRange)
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) void MWList::setPropertyOverride(const std::string &_key, const std::string &_value)
@ -157,4 +157,8 @@ namespace Gui
return mScrollView->findWidget (getName() + "_item_" + name)->castType<MyGUI::Button>(); return mScrollView->findWidget (getName() + "_item_" + name)->castType<MyGUI::Button>();
} }
void MWList::scrollToTop()
{
mScrollView->setViewOffset(MyGUI::IntPoint(0, 0));
}
} }

@ -46,6 +46,8 @@ namespace Gui
MyGUI::Button* getItemWidget(const std::string& name); MyGUI::Button* getItemWidget(const std::string& name);
///< get widget for an item name, useful to set up tooltip ///< get widget for an item name, useful to set up tooltip
void scrollToTop();
virtual void setPropertyOverride(const std::string& _key, const std::string& _value); virtual void setPropertyOverride(const std::string& _key, const std::string& _value);
protected: protected:

@ -32,7 +32,7 @@ distribution.
#include "tinyxml.h" #include "tinyxml.h"
#ifdef _WIN32 #ifdef _WIN32
#include <Windows.h> // import MultiByteToWideChar #include <windows.h> // import MultiByteToWideChar
#endif #endif

@ -21,8 +21,8 @@ extern "C"
#endif #endif
} }
#ifdef _WIN32 #if defined(_WIN32) && !defined(__MINGW32__)
#include <BaseTsd.h> #include <basetsd.h>
typedef SSIZE_T ssize_t; typedef SSIZE_T ssize_t;
#endif #endif

@ -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. The last thing to do is to name your your addon and click create.
\paragraph{Selecting File for Editing} \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} \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.\\ 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.\\

@ -25,7 +25,9 @@
\title{OpenCS User Manual} \title{OpenCS User Manual}
\maketitle \maketitle
\newpage
\tableofcontents{} \tableofcontents{}
\newpage
\input{files_and_directories} \input{files_and_directories}
\input{creating_file} \input{creating_file}
\input{windows} \input{windows}

@ -1,17 +1,17 @@
\section{Records Modification} \section{Records Modification}
\subsection{Introduction} \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} \subsubsection{Glossary}
\begin{description} \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} \end{description}
\subsection{Edit Panel Interface} \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.\\ 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} \begin{description}
\item[Preview] This will launch simple preview panel -- which will be described later. \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} \end{description}
\subsection{Verification tool} \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.\\ 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 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.\\ 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 usefull tool %don't remember, todo... To launch this useful 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.} 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.}

@ -1,15 +1,15 @@
\section{Record Types} \section{Record Types}
\subsection{Introduction} \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. Let's go through all Record Types and discuss what you can tell OpenCS about them.
\begin{description} \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[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[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, 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[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[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[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[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. \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.

@ -14,7 +14,7 @@ Let's browse through the various screens and see what all these tables show.
\begin{description} \begin{description}
\item[Record:] An entry in \OCS{} representing an item, location, sound, NPC or anything else. \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} \end{description}
\subsubsection{Recurring Terms} \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[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 \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 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. by something that still exists otherwise you will get crashes in the worst case scenario.
\end{description} \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. 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} \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} \begin{description}
\item[Name:] This is how the game will show your location in-game. \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 \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 colour picker, you can use your favourite search engine to find a colour picker online. 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. \item[Sleep Encounter:] These are the rules for what kind of enemies you might encounter when you sleep outside in the wild.
\end{description} \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, 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. 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 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). 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 \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 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. 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, 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} \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 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, 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 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. 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.

Loading…
Cancel
Save