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

loadfix
gus 11 years ago
commit 875daed4ec

@ -12,7 +12,7 @@ set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/)
message(STATUS "Configuring OpenMW...") message(STATUS "Configuring OpenMW...")
set(OPENMW_VERSION_MAJOR 0) set(OPENMW_VERSION_MAJOR 0)
set(OPENMW_VERSION_MINOR 30) set(OPENMW_VERSION_MINOR 31)
set(OPENMW_VERSION_RELEASE 0) set(OPENMW_VERSION_RELEASE 0)
set(OPENMW_VERSION_COMMITHASH "") set(OPENMW_VERSION_COMMITHASH "")

@ -18,13 +18,13 @@ opencs_hdrs_noqt (model/doc
opencs_units (model/world opencs_units (model/world
idtable idtableproxymodel regionmap data commanddispatcher idtable idtableproxymodel regionmap data commanddispatcher idtablebase resourcetable
) )
opencs_units_noqt (model/world opencs_units_noqt (model/world
universalid record commands columnbase scriptcontext cell refidcollection universalid record commands columnbase scriptcontext cell refidcollection
refidadapter refiddata refidadapterimp ref collectionbase refcollection columns infocollection tablemimedata cellcoordinates cellselection refidadapter refiddata refidadapterimp ref collectionbase refcollection columns infocollection tablemimedata cellcoordinates cellselection resources resourcesmanager
) )
opencs_hdrs_noqt (model/world opencs_hdrs_noqt (model/world
@ -59,8 +59,17 @@ opencs_hdrs_noqt (view/doc
opencs_units (view/world opencs_units (view/world
table tablesubview scriptsubview util regionmapsubview tablebottombox creator genericcreator table tablesubview scriptsubview util regionmapsubview tablebottombox creator genericcreator
cellcreator referenceablecreator referencecreator scenesubview scenetoolbar scenetool cellcreator referenceablecreator referencecreator scenesubview
scenetoolmode infocreator scriptedit dialoguesubview previewsubview regionmap dragrecordtable infocreator scriptedit dialoguesubview previewsubview regionmap dragrecordtable
)
opencs_units_noqt (view/world
subviews enumdelegate vartypedelegate recordstatusdelegate idtypedelegate datadisplaydelegate
scripthighlighter idvalidator dialoguecreator
)
opencs_units (view/widget
scenetoolbar scenetool scenetoolmode pushbutton
) )
opencs_units (view/render opencs_units (view/render
@ -73,11 +82,6 @@ opencs_units_noqt (view/render
lightingbright object cell lightingbright object cell
) )
opencs_units_noqt (view/world
subviews enumdelegate vartypedelegate recordstatusdelegate idtypedelegate datadisplaydelegate
scripthighlighter idvalidator dialoguecreator
)
opencs_units (view/tools opencs_units (view/tools
reportsubview reportsubview

@ -35,6 +35,8 @@ CS::Editor::Editor (OgreInit::OgreInit& ogreInit)
Bsa::registerResources (Files::Collections (config.first, !mFsStrict), config.second, true, Bsa::registerResources (Files::Collections (config.first, !mFsStrict), config.second, true,
mFsStrict); mFsStrict);
mDocumentManager.listResources();
mNewGame.setLocalData (mLocal); mNewGame.setLocalData (mLocal);
mFileDialog.setLocalData (mLocal); mFileDialog.setLocalData (mLocal);

@ -2205,9 +2205,9 @@ void CSMDoc::Document::createBase()
CSMDoc::Document::Document (const Files::ConfigurationManager& configuration, CSMDoc::Document::Document (const Files::ConfigurationManager& configuration,
const std::vector< boost::filesystem::path >& files, bool new_, const std::vector< boost::filesystem::path >& files, bool new_,
const boost::filesystem::path& savePath, const boost::filesystem::path& resDir, const boost::filesystem::path& savePath, const boost::filesystem::path& resDir,
ToUTF8::FromType encoding) ToUTF8::FromType encoding, const CSMWorld::ResourcesManager& resourcesManager)
: mSavePath (savePath), mContentFiles (files), mNew (new_), mData (encoding), mTools (mData), : mSavePath (savePath), mContentFiles (files), mNew (new_), mData (encoding, resourcesManager),
mResDir(resDir), mTools (mData), mResDir(resDir),
mProjectPath ((configuration.getUserDataPath() / "projects") / mProjectPath ((configuration.getUserDataPath() / "projects") /
(savePath.filename().string() + ".project")), (savePath.filename().string() + ".project")),
mSaving (*this, mProjectPath, encoding) mSaving (*this, mProjectPath, encoding)

@ -31,6 +31,11 @@ namespace Files
class ConfigurationManager; class ConfigurationManager;
} }
namespace CSMWorld
{
class ResourcesManager;
}
namespace CSMDoc namespace CSMDoc
{ {
class Document : public QObject class Document : public QObject
@ -73,7 +78,7 @@ namespace CSMDoc
Document (const Files::ConfigurationManager& configuration, Document (const Files::ConfigurationManager& configuration,
const std::vector< boost::filesystem::path >& files, bool new_, const std::vector< boost::filesystem::path >& files, bool new_,
const boost::filesystem::path& savePath, const boost::filesystem::path& resDir, const boost::filesystem::path& savePath, const boost::filesystem::path& resDir,
ToUTF8::FromType encoding); ToUTF8::FromType encoding, const CSMWorld::ResourcesManager& resourcesManager);
~Document(); ~Document();

@ -52,7 +52,7 @@ CSMDoc::DocumentManager::~DocumentManager()
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_)
{ {
Document *document = new Document (mConfiguration, files, new_, savePath, mResDir, mEncoding); Document *document = new Document (mConfiguration, files, new_, savePath, mResDir, mEncoding, mResourcesManager);
mDocuments.push_back (document); mDocuments.push_back (document);
@ -85,6 +85,11 @@ void CSMDoc::DocumentManager::setEncoding (ToUTF8::FromType encoding)
mEncoding = encoding; mEncoding = encoding;
} }
void CSMDoc::DocumentManager::listResources()
{
mResourcesManager.listResources();
}
void CSMDoc::DocumentManager::documentLoaded (Document *document) void CSMDoc::DocumentManager::documentLoaded (Document *document)
{ {
emit documentAdded (document); emit documentAdded (document);

@ -11,6 +11,8 @@
#include <components/to_utf8/to_utf8.hpp> #include <components/to_utf8/to_utf8.hpp>
#include "../world/resourcesmanager.hpp"
#include "loader.hpp" #include "loader.hpp"
namespace Files namespace Files
@ -31,6 +33,7 @@ namespace CSMDoc
QThread mLoaderThread; QThread mLoaderThread;
Loader mLoader; Loader mLoader;
ToUTF8::FromType mEncoding; ToUTF8::FromType mEncoding;
CSMWorld::ResourcesManager mResourcesManager;
DocumentManager (const DocumentManager&); DocumentManager (const DocumentManager&);
DocumentManager& operator= (const DocumentManager&); DocumentManager& operator= (const DocumentManager&);
@ -50,6 +53,9 @@ namespace CSMDoc
void setEncoding (ToUTF8::FromType encoding); void setEncoding (ToUTF8::FromType encoding);
/// Ask OGRE for a list of available resources.
void listResources();
private: private:
boost::filesystem::path mResDir; boost::filesystem::path mResDir;

@ -7,7 +7,7 @@ CSMFilter::AndNode::AndNode (const std::vector<boost::shared_ptr<Node> >& nodes)
: NAryNode (nodes, "and") : NAryNode (nodes, "and")
{} {}
bool CSMFilter::AndNode::test (const CSMWorld::IdTable& table, int row, bool CSMFilter::AndNode::test (const CSMWorld::IdTableBase& table, int row,
const std::map<int, int>& columns) const const std::map<int, int>& columns) const
{ {
int size = getSize(); int size = getSize();

@ -11,7 +11,7 @@ namespace CSMFilter
AndNode (const std::vector<boost::shared_ptr<Node> >& nodes); AndNode (const std::vector<boost::shared_ptr<Node> >& nodes);
virtual bool test (const CSMWorld::IdTable& table, int row, virtual bool test (const CSMWorld::IdTableBase& table, int row,
const std::map<int, int>& columns) const; const std::map<int, int>& columns) const;
///< \return Can the specified table row pass through to filter? ///< \return Can the specified table row pass through to filter?
/// \param columns column ID to column index mapping /// \param columns column ID to column index mapping

@ -3,7 +3,7 @@
CSMFilter::BooleanNode::BooleanNode (bool true_) : mTrue (true_) {} CSMFilter::BooleanNode::BooleanNode (bool true_) : mTrue (true_) {}
bool CSMFilter::BooleanNode::test (const CSMWorld::IdTable& table, int row, bool CSMFilter::BooleanNode::test (const CSMWorld::IdTableBase& table, int row,
const std::map<int, int>& columns) const const std::map<int, int>& columns) const
{ {
return mTrue; return mTrue;

@ -13,7 +13,7 @@ namespace CSMFilter
BooleanNode (bool true_); BooleanNode (bool true_);
virtual bool test (const CSMWorld::IdTable& table, int row, virtual bool test (const CSMWorld::IdTableBase& table, int row,
const std::map<int, int>& columns) const; const std::map<int, int>& columns) const;
///< \return Can the specified table row pass through to filter? ///< \return Can the specified table row pass through to filter?
/// \param columns column ID to column index mapping /// \param columns column ID to column index mapping

@ -11,7 +11,7 @@
namespace CSMWorld namespace CSMWorld
{ {
class IdTable; class IdTableBase;
} }
namespace CSMFilter namespace CSMFilter
@ -32,7 +32,7 @@ namespace CSMFilter
virtual ~Node(); virtual ~Node();
virtual bool test (const CSMWorld::IdTable& table, int row, virtual bool test (const CSMWorld::IdTableBase& table, int row,
const std::map<int, int>& columns) const = 0; const std::map<int, int>& columns) const = 0;
///< \return Can the specified table row pass through to filter? ///< \return Can the specified table row pass through to filter?
/// \param columns column ID to column index mapping /// \param columns column ID to column index mapping

@ -3,7 +3,7 @@
CSMFilter::NotNode::NotNode (boost::shared_ptr<Node> child) : UnaryNode (child, "not") {} CSMFilter::NotNode::NotNode (boost::shared_ptr<Node> child) : UnaryNode (child, "not") {}
bool CSMFilter::NotNode::test (const CSMWorld::IdTable& table, int row, bool CSMFilter::NotNode::test (const CSMWorld::IdTableBase& table, int row,
const std::map<int, int>& columns) const const std::map<int, int>& columns) const
{ {
return !getChild().test (table, row, columns); return !getChild().test (table, row, columns);

@ -11,7 +11,7 @@ namespace CSMFilter
NotNode (boost::shared_ptr<Node> child); NotNode (boost::shared_ptr<Node> child);
virtual bool test (const CSMWorld::IdTable& table, int row, virtual bool test (const CSMWorld::IdTableBase& table, int row,
const std::map<int, int>& columns) const; const std::map<int, int>& columns) const;
///< \return Can the specified table row pass through to filter? ///< \return Can the specified table row pass through to filter?
/// \param columns column ID to column index mapping /// \param columns column ID to column index mapping

@ -7,7 +7,7 @@ CSMFilter::OrNode::OrNode (const std::vector<boost::shared_ptr<Node> >& nodes)
: NAryNode (nodes, "or") : NAryNode (nodes, "or")
{} {}
bool CSMFilter::OrNode::test (const CSMWorld::IdTable& table, int row, bool CSMFilter::OrNode::test (const CSMWorld::IdTableBase& table, int row,
const std::map<int, int>& columns) const const std::map<int, int>& columns) const
{ {
int size = getSize(); int size = getSize();

@ -11,7 +11,7 @@ namespace CSMFilter
OrNode (const std::vector<boost::shared_ptr<Node> >& nodes); OrNode (const std::vector<boost::shared_ptr<Node> >& nodes);
virtual bool test (const CSMWorld::IdTable& table, int row, virtual bool test (const CSMWorld::IdTableBase& table, int row,
const std::map<int, int>& columns) const; const std::map<int, int>& columns) const;
///< \return Can the specified table row pass through to filter? ///< \return Can the specified table row pass through to filter?
/// \param columns column ID to column index mapping /// \param columns column ID to column index mapping

@ -7,13 +7,13 @@
#include <QRegExp> #include <QRegExp>
#include "../world/columns.hpp" #include "../world/columns.hpp"
#include "../world/idtable.hpp" #include "../world/idtablebase.hpp"
CSMFilter::TextNode::TextNode (int columnId, const std::string& text) CSMFilter::TextNode::TextNode (int columnId, const std::string& text)
: mColumnId (columnId), mText (text) : mColumnId (columnId), mText (text)
{} {}
bool CSMFilter::TextNode::test (const CSMWorld::IdTable& table, int row, bool CSMFilter::TextNode::test (const CSMWorld::IdTableBase& table, int row,
const std::map<int, int>& columns) const const std::map<int, int>& columns) const
{ {
const std::map<int, int>::const_iterator iter = columns.find (mColumnId); const std::map<int, int>::const_iterator iter = columns.find (mColumnId);

@ -14,7 +14,7 @@ namespace CSMFilter
TextNode (int columnId, const std::string& text); TextNode (int columnId, const std::string& text);
virtual bool test (const CSMWorld::IdTable& table, int row, virtual bool test (const CSMWorld::IdTableBase& table, int row,
const std::map<int, int>& columns) const; const std::map<int, int>& columns) const;
///< \return Can the specified table row pass through to filter? ///< \return Can the specified table row pass through to filter?
/// \param columns column ID to column index mapping /// \param columns column ID to column index mapping

@ -5,13 +5,13 @@
#include <stdexcept> #include <stdexcept>
#include "../world/columns.hpp" #include "../world/columns.hpp"
#include "../world/idtable.hpp" #include "../world/idtablebase.hpp"
CSMFilter::ValueNode::ValueNode (int columnId, Type lowerType, Type upperType, CSMFilter::ValueNode::ValueNode (int columnId, Type lowerType, Type upperType,
double lower, double upper) double lower, double upper)
: mColumnId (columnId), mLowerType (lowerType), mUpperType (upperType), mLower (lower), mUpper (upper){} : mColumnId (columnId), mLowerType (lowerType), mUpperType (upperType), mLower (lower), mUpper (upper){}
bool CSMFilter::ValueNode::test (const CSMWorld::IdTable& table, int row, bool CSMFilter::ValueNode::test (const CSMWorld::IdTableBase& table, int row,
const std::map<int, int>& columns) const const std::map<int, int>& columns) const
{ {
const std::map<int, int>::const_iterator iter = columns.find (mColumnId); const std::map<int, int>::const_iterator iter = columns.find (mColumnId);

@ -27,7 +27,7 @@ namespace CSMFilter
ValueNode (int columnId, Type lowerType, Type upperType, double lower, double upper); ValueNode (int columnId, Type lowerType, Type upperType, double lower, double upper);
virtual bool test (const CSMWorld::IdTable& table, int row, virtual bool test (const CSMWorld::IdTableBase& table, int row,
const std::map<int, int>& columns) const; const std::map<int, int>& columns) const;
///< \return Can the specified table row pass through to filter? ///< \return Can the specified table row pass through to filter?
/// \param columns column ID to column index mapping /// \param columns column ID to column index mapping

@ -92,7 +92,14 @@ namespace CSMWorld
Display_EnchantmentType, Display_EnchantmentType,
Display_BodyPartType, Display_BodyPartType,
Display_MeshType, Display_MeshType,
Display_Gender Display_Gender,
Display_Mesh,
Display_Icon,
Display_Music,
Display_SoundRes,
Display_Texture,
Display_Video,
Display_Colour
}; };
int mColumnId; int mColumnId;

@ -604,7 +604,7 @@ namespace CSMWorld
struct SoundFileColumn : public Column<ESXRecordT> struct SoundFileColumn : public Column<ESXRecordT>
{ {
SoundFileColumn() SoundFileColumn()
: Column<ESXRecordT> (Columns::ColumnId_SoundFile, ColumnBase::Display_Sound) : Column<ESXRecordT> (Columns::ColumnId_SoundFile, ColumnBase::Display_SoundRes)
{} {}
virtual QVariant get (const Record<ESXRecordT>& record) const virtual QVariant get (const Record<ESXRecordT>& record) const
@ -634,7 +634,7 @@ namespace CSMWorld
{ {
/// \todo Replace Display_Integer with something that displays the colour value more directly. /// \todo Replace Display_Integer with something that displays the colour value more directly.
MapColourColumn() MapColourColumn()
: Column<ESXRecordT> (Columns::ColumnId_MapColour, ColumnBase::Display_Integer) : Column<ESXRecordT> (Columns::ColumnId_MapColour, ColumnBase::Display_Colour)
{} {}
virtual QVariant get (const Record<ESXRecordT>& record) const virtual QVariant get (const Record<ESXRecordT>& record) const

@ -49,7 +49,7 @@ namespace CSMWorld
public: public:
CloneCommand (IdTable& model, const std::string& idOrigin, CloneCommand (IdTable& model, const std::string& idOrigin,
const std::string& IdDestination, const std::string& IdDestination,
const UniversalId::Type type, const UniversalId::Type type,
QUndoCommand* parent = 0); QUndoCommand* parent = 0);

@ -15,6 +15,8 @@
#include "columnimp.hpp" #include "columnimp.hpp"
#include "regionmap.hpp" #include "regionmap.hpp"
#include "columns.hpp" #include "columns.hpp"
#include "resourcesmanager.hpp"
#include "resourcetable.hpp"
void CSMWorld::Data::addModel (QAbstractItemModel *model, UniversalId::Type type, bool update) void CSMWorld::Data::addModel (QAbstractItemModel *model, UniversalId::Type type, bool update)
{ {
@ -56,8 +58,9 @@ int CSMWorld::Data::count (RecordBase::State state, const CollectionBase& collec
return number; return number;
} }
CSMWorld::Data::Data (ToUTF8::FromType encoding) CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourcesManager)
: mEncoder (encoding), mRefs (mCells), mReader (0), mDialogue (0) : mEncoder (encoding), mRefs (mCells), mResourcesManager (resourcesManager), mReader (0),
mDialogue (0)
{ {
mGlobals.addColumn (new StringIdColumn<ESM::Global>); mGlobals.addColumn (new StringIdColumn<ESM::Global>);
mGlobals.addColumn (new RecordStateColumn<ESM::Global>); mGlobals.addColumn (new RecordStateColumn<ESM::Global>);
@ -277,6 +280,18 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding)
UniversalId::Type_Referenceable); UniversalId::Type_Referenceable);
addModel (new IdTable (&mRefs, IdTable::Feature_ViewCell | IdTable::Feature_Preview), UniversalId::Type_Reference); addModel (new IdTable (&mRefs, IdTable::Feature_ViewCell | IdTable::Feature_Preview), UniversalId::Type_Reference);
addModel (new IdTable (&mFilters), UniversalId::Type_Filter); addModel (new IdTable (&mFilters), UniversalId::Type_Filter);
addModel (new ResourceTable (&mResourcesManager.get (UniversalId::Type_Mesh)),
UniversalId::Type_Mesh);
addModel (new ResourceTable (&mResourcesManager.get (UniversalId::Type_Icon)),
UniversalId::Type_Icon);
addModel (new ResourceTable (&mResourcesManager.get (UniversalId::Type_Music)),
UniversalId::Type_Music);
addModel (new ResourceTable (&mResourcesManager.get (UniversalId::Type_SoundRes)),
UniversalId::Type_SoundRes);
addModel (new ResourceTable (&mResourcesManager.get (UniversalId::Type_Texture)),
UniversalId::Type_Texture);
addModel (new ResourceTable (&mResourcesManager.get (UniversalId::Type_Video)),
UniversalId::Type_Video);
} }
CSMWorld::Data::~Data() CSMWorld::Data::~Data()
@ -498,6 +513,11 @@ CSMWorld::IdCollection<ESM::BodyPart>& CSMWorld::Data::getBodyParts()
return mBodyParts; return mBodyParts;
} }
const CSMWorld::Resources& CSMWorld::Data::getResources (const UniversalId& id) const
{
return mResourcesManager.get (UniversalId::getParentType (id.getType()));
}
QAbstractItemModel *CSMWorld::Data::getTableModel (const CSMWorld::UniversalId& id) QAbstractItemModel *CSMWorld::Data::getTableModel (const CSMWorld::UniversalId& id)
{ {
std::map<UniversalId::Type, QAbstractItemModel *>::iterator iter = mModelIndex.find (id.getType()); std::map<UniversalId::Type, QAbstractItemModel *>::iterator iter = mModelIndex.find (id.getType());

@ -47,6 +47,9 @@ namespace ESM
namespace CSMWorld namespace CSMWorld
{ {
class ResourcesManager;
class Resources;
class Data : public QObject class Data : public QObject
{ {
Q_OBJECT Q_OBJECT
@ -73,6 +76,7 @@ namespace CSMWorld
RefIdCollection mReferenceables; RefIdCollection mReferenceables;
RefCollection mRefs; RefCollection mRefs;
IdCollection<CSMFilter::Filter> mFilters; IdCollection<CSMFilter::Filter> mFilters;
const ResourcesManager& mResourcesManager;
std::vector<QAbstractItemModel *> mModels; std::vector<QAbstractItemModel *> mModels;
std::map<UniversalId::Type, QAbstractItemModel *> mModelIndex; std::map<UniversalId::Type, QAbstractItemModel *> mModelIndex;
std::string mAuthor; std::string mAuthor;
@ -98,7 +102,7 @@ namespace CSMWorld
public: public:
Data (ToUTF8::FromType encoding); Data (ToUTF8::FromType encoding, const ResourcesManager& resourcesManager);
virtual ~Data(); virtual ~Data();
@ -186,6 +190,9 @@ namespace CSMWorld
IdCollection<ESM::BodyPart>& getBodyParts(); IdCollection<ESM::BodyPart>& getBodyParts();
/// Throws an exception, if \a id does not match a resources list.
const Resources& getResources (const UniversalId& id) const;
QAbstractItemModel *getTableModel (const UniversalId& id); QAbstractItemModel *getTableModel (const UniversalId& id);
///< If no table model is available for \a id, an exception is thrown. ///< If no table model is available for \a id, an exception is thrown.
/// ///

@ -5,7 +5,7 @@
#include "columnbase.hpp" #include "columnbase.hpp"
CSMWorld::IdTable::IdTable (CollectionBase *idCollection, unsigned int features) CSMWorld::IdTable::IdTable (CollectionBase *idCollection, unsigned int features)
: mIdCollection (idCollection), mFeatures (features) : IdTableBase (features), mIdCollection (idCollection)
{} {}
CSMWorld::IdTable::~IdTable() CSMWorld::IdTable::~IdTable()
@ -185,17 +185,12 @@ void CSMWorld::IdTable::reorderRows (int baseIndex, const std::vector<int>& newO
index (baseIndex+newOrder.size()-1, mIdCollection->getColumns()-1)); index (baseIndex+newOrder.size()-1, mIdCollection->getColumns()-1));
} }
unsigned int CSMWorld::IdTable::getFeatures() const
{
return mFeatures;
}
std::pair<CSMWorld::UniversalId, std::string> CSMWorld::IdTable::view (int row) const std::pair<CSMWorld::UniversalId, std::string> CSMWorld::IdTable::view (int row) const
{ {
std::string id; std::string id;
std::string hint; std::string hint;
if (mFeatures & Feature_ViewCell) if (getFeatures() & Feature_ViewCell)
{ {
int cellColumn = mIdCollection->searchColumnIndex (Columns::ColumnId_Cell); int cellColumn = mIdCollection->searchColumnIndex (Columns::ColumnId_Cell);
int idColumn = mIdCollection->searchColumnIndex (Columns::ColumnId_Id); int idColumn = mIdCollection->searchColumnIndex (Columns::ColumnId_Id);
@ -206,7 +201,7 @@ std::pair<CSMWorld::UniversalId, std::string> CSMWorld::IdTable::view (int row)
hint = "r:" + std::string (mIdCollection->getData (row, idColumn).toString().toUtf8().constData()); hint = "r:" + std::string (mIdCollection->getData (row, idColumn).toString().toUtf8().constData());
} }
} }
else if (mFeatures & Feature_ViewId) else if (getFeatures() & Feature_ViewId)
{ {
int column = mIdCollection->searchColumnIndex (Columns::ColumnId_Id); int column = mIdCollection->searchColumnIndex (Columns::ColumnId_Id);
@ -226,6 +221,11 @@ std::pair<CSMWorld::UniversalId, std::string> CSMWorld::IdTable::view (int row)
return std::make_pair (UniversalId (UniversalId::Type_Scene, id), hint); return std::make_pair (UniversalId (UniversalId::Type_Scene, id), hint);
} }
bool CSMWorld::IdTable::isDeleted (const std::string& id) const
{
return getRecord (id).isDeleted();
}
int CSMWorld::IdTable::getColumnId(int column) const int CSMWorld::IdTable::getColumnId(int column) const
{ {
return mIdCollection->getColumn(column).getId(); return mIdCollection->getColumn(column).getId();

@ -3,8 +3,7 @@
#include <vector> #include <vector>
#include <QAbstractItemModel> #include "idtablebase.hpp"
#include "universalid.hpp" #include "universalid.hpp"
#include "columns.hpp" #include "columns.hpp"
@ -13,34 +12,13 @@ namespace CSMWorld
class CollectionBase; class CollectionBase;
class RecordBase; class RecordBase;
class IdTable : public QAbstractItemModel class IdTable : public IdTableBase
{ {
Q_OBJECT Q_OBJECT
public:
enum Features
{
Feature_ReorderWithinTopic = 1,
/// Use ID column to generate view request (ID is transformed into
/// worldspace and original ID is passed as hint with c: prefix).
Feature_ViewId = 2,
/// Use cell column to generate view request (cell ID is transformed
/// into worldspace and record ID is passed as hint with r: prefix).
Feature_ViewCell = 4,
Feature_View = Feature_ViewId | Feature_ViewCell,
Feature_Preview = 8
};
private: private:
CollectionBase *mIdCollection; CollectionBase *mIdCollection;
unsigned int mFeatures;
bool mPreview;
// not implemented // not implemented
IdTable (const IdTable&); IdTable (const IdTable&);
@ -79,17 +57,17 @@ namespace CSMWorld
const std::string& destination, const std::string& destination,
UniversalId::Type type = UniversalId::Type_None); UniversalId::Type type = UniversalId::Type_None);
QModelIndex getModelIndex (const std::string& id, int column) const; virtual QModelIndex getModelIndex (const std::string& id, int column) const;
void setRecord (const std::string& id, const RecordBase& record); void setRecord (const std::string& id, const RecordBase& record);
///< Add record or overwrite existing recrod. ///< Add record or overwrite existing recrod.
const RecordBase& getRecord (const std::string& id) const; const RecordBase& getRecord (const std::string& id) const;
int searchColumnIndex (Columns::ColumnId id) const; virtual int searchColumnIndex (Columns::ColumnId id) const;
///< Return index of column with the given \a id. If no such column exists, -1 is returned. ///< Return index of column with the given \a id. If no such column exists, -1 is returned.
int findColumnIndex (Columns::ColumnId id) const; virtual int findColumnIndex (Columns::ColumnId id) const;
///< Return index of column with the given \a id. If no such column exists, an exception is ///< Return index of column with the given \a id. If no such column exists, an exception is
/// thrown. /// thrown.
@ -97,12 +75,13 @@ namespace CSMWorld
///< Reorder the rows [baseIndex, baseIndex+newOrder.size()) according to the indices ///< Reorder the rows [baseIndex, baseIndex+newOrder.size()) according to the indices
/// given in \a newOrder (baseIndex+newOrder[0] specifies the new index of row baseIndex). /// given in \a newOrder (baseIndex+newOrder[0] specifies the new index of row baseIndex).
unsigned int getFeatures() const; virtual std::pair<UniversalId, std::string> view (int row) const;
std::pair<UniversalId, std::string> view (int row) const;
///< Return the UniversalId and the hint for viewing \a row. If viewing is not ///< Return the UniversalId and the hint for viewing \a row. If viewing is not
/// supported by this table, return (UniversalId::Type_None, ""). /// supported by this table, return (UniversalId::Type_None, "").
/// Is \a id flagged as deleted?
virtual bool isDeleted (const std::string& id) const;
int getColumnId(int column) const; int getColumnId(int column) const;
}; };
} }

@ -0,0 +1,9 @@
#include "idtablebase.hpp"
CSMWorld::IdTableBase::IdTableBase (unsigned int features) : mFeatures (features) {}
unsigned int CSMWorld::IdTableBase::getFeatures() const
{
return mFeatures;
}

@ -0,0 +1,67 @@
#ifndef CSM_WOLRD_IDTABLEBASE_H
#define CSM_WOLRD_IDTABLEBASE_H
#include <QAbstractItemModel>
#include "columns.hpp"
namespace CSMWorld
{
class UniversalId;
class IdTableBase : public QAbstractItemModel
{
Q_OBJECT
public:
enum Features
{
Feature_ReorderWithinTopic = 1,
/// Use ID column to generate view request (ID is transformed into
/// worldspace and original ID is passed as hint with c: prefix).
Feature_ViewId = 2,
/// Use cell column to generate view request (cell ID is transformed
/// into worldspace and record ID is passed as hint with r: prefix).
Feature_ViewCell = 4,
Feature_View = Feature_ViewId | Feature_ViewCell,
Feature_Preview = 8,
/// Table can not be modified through ordinary means.
Feature_Constant = 16
};
private:
unsigned int mFeatures;
public:
IdTableBase (unsigned int features);
virtual QModelIndex getModelIndex (const std::string& id, int column) const = 0;
/// Return index of column with the given \a id. If no such column exists, -1 is
/// returned.
virtual int searchColumnIndex (Columns::ColumnId id) const = 0;
/// Return index of column with the given \a id. If no such column exists, an
/// exception is thrown.
virtual int findColumnIndex (Columns::ColumnId id) const = 0;
/// Return the UniversalId and the hint for viewing \a row. If viewing is not
/// supported by this table, return (UniversalId::Type_None, "").
virtual std::pair<UniversalId, std::string> view (int row) const = 0;
/// Is \a id flagged as deleted?
virtual bool isDeleted (const std::string& id) const = 0;
unsigned int getFeatures() const;
};
}
#endif

@ -3,7 +3,7 @@
#include <vector> #include <vector>
#include "idtable.hpp" #include "idtablebase.hpp"
void CSMWorld::IdTableProxyModel::updateColumnMap() void CSMWorld::IdTableProxyModel::updateColumnMap()
{ {
@ -13,7 +13,7 @@ void CSMWorld::IdTableProxyModel::updateColumnMap()
{ {
std::vector<int> columns = mFilter->getReferencedColumns(); std::vector<int> columns = mFilter->getReferencedColumns();
const IdTable& table = dynamic_cast<const IdTable&> (*sourceModel()); const IdTableBase& table = dynamic_cast<const IdTableBase&> (*sourceModel());
for (std::vector<int>::const_iterator iter (columns.begin()); iter!=columns.end(); ++iter) for (std::vector<int>::const_iterator iter (columns.begin()); iter!=columns.end(); ++iter)
mColumnMap.insert (std::make_pair (*iter, mColumnMap.insert (std::make_pair (*iter,
@ -28,7 +28,7 @@ bool CSMWorld::IdTableProxyModel::filterAcceptsRow (int sourceRow, const QModelI
return true; return true;
return mFilter->test ( return mFilter->test (
dynamic_cast<IdTable&> (*sourceModel()), sourceRow, mColumnMap); dynamic_cast<IdTableBase&> (*sourceModel()), sourceRow, mColumnMap);
} }
CSMWorld::IdTableProxyModel::IdTableProxyModel (QObject *parent) CSMWorld::IdTableProxyModel::IdTableProxyModel (QObject *parent)
@ -39,7 +39,7 @@ CSMWorld::IdTableProxyModel::IdTableProxyModel (QObject *parent)
QModelIndex CSMWorld::IdTableProxyModel::getModelIndex (const std::string& id, int column) const QModelIndex CSMWorld::IdTableProxyModel::getModelIndex (const std::string& id, int column) const
{ {
return mapFromSource (dynamic_cast<IdTable&> (*sourceModel()).getModelIndex (id, column)); return mapFromSource (dynamic_cast<IdTableBase&> (*sourceModel()).getModelIndex (id, column));
} }
void CSMWorld::IdTableProxyModel::setFilter (const boost::shared_ptr<CSMFilter::Node>& filter) void CSMWorld::IdTableProxyModel::setFilter (const boost::shared_ptr<CSMFilter::Node>& filter)

@ -52,7 +52,7 @@ CSMWorld::RefIdCollection::RefIdCollection()
ModelColumns modelColumns (baseColumns); ModelColumns modelColumns (baseColumns);
mColumns.push_back (RefIdColumn (Columns::ColumnId_Model, ColumnBase::Display_String)); mColumns.push_back (RefIdColumn (Columns::ColumnId_Model, ColumnBase::Display_Mesh));
modelColumns.mModel = &mColumns.back(); modelColumns.mModel = &mColumns.back();
NameColumns nameColumns (modelColumns); NameColumns nameColumns (modelColumns);
@ -64,7 +64,7 @@ CSMWorld::RefIdCollection::RefIdCollection()
InventoryColumns inventoryColumns (nameColumns); InventoryColumns inventoryColumns (nameColumns);
mColumns.push_back (RefIdColumn (Columns::ColumnId_Icon, ColumnBase::Display_String)); mColumns.push_back (RefIdColumn (Columns::ColumnId_Icon, ColumnBase::Display_Icon));
inventoryColumns.mIcon = &mColumns.back(); inventoryColumns.mIcon = &mColumns.back();
mColumns.push_back (RefIdColumn (Columns::ColumnId_Weight, ColumnBase::Display_Float)); mColumns.push_back (RefIdColumn (Columns::ColumnId_Weight, ColumnBase::Display_Float));
inventoryColumns.mWeight = &mColumns.back(); inventoryColumns.mWeight = &mColumns.back();

@ -0,0 +1,103 @@
#include "resources.hpp"
#include <sstream>
#include <stdexcept>
#include <OgreResourceGroupManager.h>
#include <components/misc/stringops.hpp>
CSMWorld::Resources::Resources (const std::string& baseDirectory, UniversalId::Type type,
const char * const *extensions)
: mBaseDirectory (baseDirectory), mType (type)
{
int baseSize = mBaseDirectory.size();
Ogre::StringVector resourcesGroups =
Ogre::ResourceGroupManager::getSingleton().getResourceGroups();
for (Ogre::StringVector::iterator iter (resourcesGroups.begin());
iter!=resourcesGroups.end(); ++iter)
{
if (*iter=="General" || *iter=="Internal" || *iter=="Autodetect")
continue;
Ogre::StringVectorPtr resources =
Ogre::ResourceGroupManager::getSingleton().listResourceNames (*iter);
for (Ogre::StringVector::const_iterator iter (resources->begin());
iter!=resources->end(); ++iter)
{
if (static_cast<int> (iter->size())<baseSize+1 ||
iter->substr (0, baseSize)!=mBaseDirectory ||
((*iter)[baseSize]!='/' && (*iter)[baseSize]!='\\'))
continue;
if (extensions)
{
std::string::size_type index = iter->find_last_of ('.');
if (index==std::string::npos)
continue;
std::string extension = iter->substr (index+1);
int i = 0;
for (; extensions[i]; ++i)
if (extensions[i]==extension)
break;
if (!extensions[i])
continue;
}
std::string file = iter->substr (baseSize+1);
mFiles.push_back (file);
mIndex.insert (std::make_pair (file, static_cast<int> (mFiles.size())-1));
}
}
}
int CSMWorld::Resources::getSize() const
{
return mFiles.size();
}
std::string CSMWorld::Resources::getId (int index) const
{
return mFiles.at (index);
}
int CSMWorld::Resources::getIndex (const std::string& id) const
{
int index = searchId (id);
if (index==-1)
{
std::ostringstream stream;
stream << "Invalid resource: " << mBaseDirectory << '/' << id;
throw std::runtime_error (stream.str().c_str());
}
return index;
}
int CSMWorld::Resources::searchId (const std::string& id) const
{
std::string id2 = Misc::StringUtils::lowerCase (id);
std::map<std::string, int>::const_iterator iter = mIndex.find (id2);
if (iter==mIndex.end())
return -1;
return iter->second;
}
CSMWorld::UniversalId::Type CSMWorld::Resources::getType() const
{
return mType;
}

@ -0,0 +1,37 @@
#ifndef CSM_WOLRD_RESOURCES_H
#define CSM_WOLRD_RESOURCES_H
#include <string>
#include <map>
#include <vector>
#include "universalid.hpp"
namespace CSMWorld
{
class Resources
{
std::map<std::string, int> mIndex;
std::vector<std::string> mFiles;
std::string mBaseDirectory;
UniversalId::Type mType;
public:
/// \param type Type of resources in this table.
Resources (const std::string& baseDirectory, UniversalId::Type type,
const char * const *extensions = 0);
int getSize() const;
std::string getId (int index) const;
int getIndex (const std::string& id) const;
int searchId (const std::string& id) const;
UniversalId::Type getType() const;
};
}
#endif

@ -0,0 +1,31 @@
#include "resourcesmanager.hpp"
#include <stdexcept>
void CSMWorld::ResourcesManager::addResources (const Resources& resources)
{
mResources.insert (std::make_pair (resources.getType(), resources));
}
void CSMWorld::ResourcesManager::listResources()
{
static const char * const sMeshTypes[] = { "nif", 0 };
addResources (Resources ("meshes", UniversalId::Type_Mesh, sMeshTypes));
addResources (Resources ("icons", UniversalId::Type_Icon));
addResources (Resources ("music", UniversalId::Type_Music));
addResources (Resources ("sound", UniversalId::Type_SoundRes));
addResources (Resources ("textures", UniversalId::Type_Texture));
addResources (Resources ("videos", UniversalId::Type_Video));
}
const CSMWorld::Resources& CSMWorld::ResourcesManager::get (UniversalId::Type type) const
{
std::map<UniversalId::Type, Resources>::const_iterator iter = mResources.find (type);
if (iter==mResources.end())
throw std::logic_error ("Unknown resource type");
return iter->second;
}

@ -0,0 +1,28 @@
#ifndef CSM_WOLRD_RESOURCESMANAGER_H
#define CSM_WOLRD_RESOURCESMANAGER_H
#include <map>
#include "universalid.hpp"
#include "resources.hpp"
namespace CSMWorld
{
class ResourcesManager
{
std::map<UniversalId::Type, Resources> mResources;
private:
void addResources (const Resources& resources);
public:
/// Ask OGRE for a list of available resources.
void listResources();
const Resources& get (UniversalId::Type type) const;
};
}
#endif

@ -0,0 +1,146 @@
#include "resourcetable.hpp"
#include <stdexcept>
#include "resources.hpp"
#include "columnbase.hpp"
#include "universalid.hpp"
CSMWorld::ResourceTable::ResourceTable (const Resources *resources, unsigned int features)
: IdTableBase (features | Feature_Constant), mResources (resources)
{}
CSMWorld::ResourceTable::~ResourceTable() {}
int CSMWorld::ResourceTable::rowCount (const QModelIndex & parent) const
{
if (parent.isValid())
return 0;
return mResources->getSize();
}
int CSMWorld::ResourceTable::columnCount (const QModelIndex & parent) const
{
if (parent.isValid())
return 0;
return 2; // ID, type
}
QVariant CSMWorld::ResourceTable::data (const QModelIndex & index, int role) const
{
if (role!=Qt::DisplayRole)
return QVariant();
if (index.column()==0)
return QString::fromUtf8 (mResources->getId (index.row()).c_str());
if (index.column()==1)
return static_cast<int> (mResources->getType());
throw std::logic_error ("Invalid column in resource table");
}
QVariant CSMWorld::ResourceTable::headerData (int section, Qt::Orientation orientation,
int role ) const
{
if (orientation==Qt::Vertical)
return QVariant();
if (role==ColumnBase::Role_Flags)
return ColumnBase::Flag_Table;
switch (section)
{
case 0:
if (role==Qt::DisplayRole)
return Columns::getName (Columns::ColumnId_Id).c_str();
if (role==ColumnBase::Role_Display)
return ColumnBase::Display_String;
break;
case 1:
if (role==Qt::DisplayRole)
return Columns::getName (Columns::ColumnId_RecordType).c_str();
if (role==ColumnBase::Role_Display)
return ColumnBase::Display_Integer;
break;
}
return QVariant();
}
bool CSMWorld::ResourceTable::setData ( const QModelIndex &index, const QVariant &value,
int role)
{
return false;
}
Qt::ItemFlags CSMWorld::ResourceTable::flags (const QModelIndex & index) const
{
return Qt::ItemIsSelectable | Qt::ItemIsEnabled;;
}
QModelIndex CSMWorld::ResourceTable::index (int row, int column, const QModelIndex& parent)
const
{
if (parent.isValid())
return QModelIndex();
if (row<0 || row>=mResources->getSize())
return QModelIndex();
if (column<0 || column>1)
return QModelIndex();
return createIndex (row, column);
}
QModelIndex CSMWorld::ResourceTable::parent (const QModelIndex& index) const
{
return QModelIndex();
}
QModelIndex CSMWorld::ResourceTable::getModelIndex (const std::string& id, int column) const
{
return index (mResources->getIndex (id), column);
}
int CSMWorld::ResourceTable::searchColumnIndex (Columns::ColumnId id) const
{
if (id==Columns::ColumnId_Id)
return 0;
if (id==Columns::ColumnId_RecordType)
return 1;
return -1;
}
int CSMWorld::ResourceTable::findColumnIndex (Columns::ColumnId id) const
{
int index = searchColumnIndex (id);
if (index==-1)
throw std::logic_error ("invalid column index");
return index;
}
std::pair<CSMWorld::UniversalId, std::string> CSMWorld::ResourceTable::view (int row) const
{
return std::make_pair (UniversalId::Type_None, "");
}
bool CSMWorld::ResourceTable::isDeleted (const std::string& id) const
{
return false;
}

@ -0,0 +1,57 @@
#ifndef CSM_WOLRD_RESOURCETABLE_H
#define CSM_WOLRD_RESOURCETABLE_H
#include "idtablebase.hpp"
namespace CSMWorld
{
class Resources;
class ResourceTable : public IdTableBase
{
const Resources *mResources;
public:
/// \note The feature Feature_Constant will be added implicitly.
ResourceTable (const Resources *resources, unsigned int features = 0);
virtual ~ResourceTable();
virtual int rowCount (const QModelIndex & parent = QModelIndex()) const;
virtual int columnCount (const QModelIndex & parent = QModelIndex()) const;
virtual QVariant data (const QModelIndex & index, int role = Qt::DisplayRole) const;
virtual QVariant headerData (int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
virtual bool setData ( const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
virtual Qt::ItemFlags flags (const QModelIndex & index) const;
virtual QModelIndex index (int row, int column, const QModelIndex& parent = QModelIndex())
const;
virtual QModelIndex parent (const QModelIndex& index) const;
virtual QModelIndex getModelIndex (const std::string& id, int column) const;
/// Return index of column with the given \a id. If no such column exists, -1 is
/// returned.
virtual int searchColumnIndex (Columns::ColumnId id) const;
/// Return index of column with the given \a id. If no such column exists, an
/// exception is thrown.
virtual int findColumnIndex (Columns::ColumnId id) const;
/// Return the UniversalId and the hint for viewing \a row. If viewing is not
/// supported by this table, return (UniversalId::Type_None, "").
virtual std::pair<UniversalId, std::string> view (int row) const;
/// Is \a id flagged as deleted?
virtual bool isDeleted (const std::string& id) const;
};
}
#endif

@ -37,7 +37,7 @@ std::string CSMWorld::TableMimeData::getIcon() const
if (mUniversalId.empty()) if (mUniversalId.empty())
{ {
qDebug()<<"TableMimeData object does not hold any records!"; //because throwing in the event loop tends to be problematic qDebug()<<"TableMimeData object does not hold any records!"; //because throwing in the event loop tends to be problematic
throw("TableMimeData object does not hold any records!"); throw std::runtime_error ("TableMimeData object does not hold any records!");
} }
std::string tmpIcon; std::string tmpIcon;
@ -179,7 +179,7 @@ CSMWorld::UniversalId CSMWorld::TableMimeData::returnMatching (CSMWorld::Univers
} }
} }
throw ("TableMimeData object does not hold object of the seeked type"); throw std::runtime_error ("TableMimeData object does not hold object of the seeked type");
} }
CSMWorld::UniversalId CSMWorld::TableMimeData::returnMatching (CSMWorld::ColumnBase::Display type) const CSMWorld::UniversalId CSMWorld::TableMimeData::returnMatching (CSMWorld::ColumnBase::Display type) const
@ -201,7 +201,7 @@ CSMWorld::UniversalId CSMWorld::TableMimeData::returnMatching (CSMWorld::ColumnB
} }
} }
throw ("TableMimeData object does not hold object of the seeked type"); throw std::runtime_error ("TableMimeData object does not hold object of the seeked type");
} }
bool CSMWorld::TableMimeData::fromDocument (const CSMDoc::Document& document) const bool CSMWorld::TableMimeData::fromDocument (const CSMDoc::Document& document) const
@ -209,327 +209,82 @@ bool CSMWorld::TableMimeData::fromDocument (const CSMDoc::Document& document) co
return &document == &mDocument; return &document == &mDocument;
} }
CSMWorld::UniversalId::Type CSMWorld::TableMimeData::convertEnums (CSMWorld::ColumnBase::Display type) namespace
{ {
switch (type) struct Mapping
{ {
case CSMWorld::ColumnBase::Display_Race: CSMWorld::UniversalId::Type mUniversalIdType;
return CSMWorld::UniversalId::Type_Race; CSMWorld::ColumnBase::Display mDisplayType;
};
const Mapping mapping[] =
case CSMWorld::ColumnBase::Display_Skill: {
return CSMWorld::UniversalId::Type_Skill; { CSMWorld::UniversalId::Type_Race, CSMWorld::ColumnBase::Display_Race },
{ CSMWorld::UniversalId::Type_Skill, CSMWorld::ColumnBase::Display_Skill },
{ CSMWorld::UniversalId::Type_Class, CSMWorld::ColumnBase::Display_Class },
case CSMWorld::ColumnBase::Display_Class: { CSMWorld::UniversalId::Type_Class, CSMWorld::ColumnBase::Display_Class },
return CSMWorld::UniversalId::Type_Class; { CSMWorld::UniversalId::Type_Faction, CSMWorld::ColumnBase::Display_Faction },
{ CSMWorld::UniversalId::Type_Sound, CSMWorld::ColumnBase::Display_Sound },
{ CSMWorld::UniversalId::Type_Region, CSMWorld::ColumnBase::Display_Region },
case CSMWorld::ColumnBase::Display_Faction: { CSMWorld::UniversalId::Type_Birthsign, CSMWorld::ColumnBase::Display_Birthsign },
return CSMWorld::UniversalId::Type_Faction; { CSMWorld::UniversalId::Type_Spell, CSMWorld::ColumnBase::Display_Spell },
{ CSMWorld::UniversalId::Type_Cell, CSMWorld::ColumnBase::Display_Cell },
{ CSMWorld::UniversalId::Type_Referenceable, CSMWorld::ColumnBase::Display_Referenceable },
case CSMWorld::ColumnBase::Display_Sound: { CSMWorld::UniversalId::Type_Activator, CSMWorld::ColumnBase::Display_Activator },
return CSMWorld::UniversalId::Type_Sound; { CSMWorld::UniversalId::Type_Potion, CSMWorld::ColumnBase::Display_Potion },
{ CSMWorld::UniversalId::Type_Apparatus, CSMWorld::ColumnBase::Display_Apparatus },
{ CSMWorld::UniversalId::Type_Armor, CSMWorld::ColumnBase::Display_Armor },
case CSMWorld::ColumnBase::Display_Region: { CSMWorld::UniversalId::Type_Book, CSMWorld::ColumnBase::Display_Book },
return CSMWorld::UniversalId::Type_Region; { CSMWorld::UniversalId::Type_Clothing, CSMWorld::ColumnBase::Display_Clothing },
{ CSMWorld::UniversalId::Type_Container, CSMWorld::ColumnBase::Display_Container },
{ CSMWorld::UniversalId::Type_Creature, CSMWorld::ColumnBase::Display_Creature },
case CSMWorld::ColumnBase::Display_Birthsign: { CSMWorld::UniversalId::Type_Door, CSMWorld::ColumnBase::Display_Door },
return CSMWorld::UniversalId::Type_Birthsign; { CSMWorld::UniversalId::Type_Ingredient, CSMWorld::ColumnBase::Display_Ingredient },
{ CSMWorld::UniversalId::Type_CreatureLevelledList, CSMWorld::ColumnBase::Display_CreatureLevelledList },
{ CSMWorld::UniversalId::Type_ItemLevelledList, CSMWorld::ColumnBase::Display_ItemLevelledList },
case CSMWorld::ColumnBase::Display_Spell: { CSMWorld::UniversalId::Type_Light, CSMWorld::ColumnBase::Display_Light },
return CSMWorld::UniversalId::Type_Spell; { CSMWorld::UniversalId::Type_Lockpick, CSMWorld::ColumnBase::Display_Lockpick },
{ CSMWorld::UniversalId::Type_Miscellaneous, CSMWorld::ColumnBase::Display_Miscellaneous },
{ CSMWorld::UniversalId::Type_Npc, CSMWorld::ColumnBase::Display_Npc },
case CSMWorld::ColumnBase::Display_Cell: { CSMWorld::UniversalId::Type_Probe, CSMWorld::ColumnBase::Display_Probe },
return CSMWorld::UniversalId::Type_Cell; { CSMWorld::UniversalId::Type_Repair, CSMWorld::ColumnBase::Display_Repair },
{ CSMWorld::UniversalId::Type_Static, CSMWorld::ColumnBase::Display_Static },
{ CSMWorld::UniversalId::Type_Weapon, CSMWorld::ColumnBase::Display_Weapon },
case CSMWorld::ColumnBase::Display_Referenceable: { CSMWorld::UniversalId::Type_Reference, CSMWorld::ColumnBase::Display_Reference },
return CSMWorld::UniversalId::Type_Referenceable; { CSMWorld::UniversalId::Type_Filter, CSMWorld::ColumnBase::Display_Filter },
{ CSMWorld::UniversalId::Type_Topic, CSMWorld::ColumnBase::Display_Topic },
{ CSMWorld::UniversalId::Type_Journal, CSMWorld::ColumnBase::Display_Journal },
case CSMWorld::ColumnBase::Display_Activator: { CSMWorld::UniversalId::Type_TopicInfo, CSMWorld::ColumnBase::Display_TopicInfo },
return CSMWorld::UniversalId::Type_Activator; { CSMWorld::UniversalId::Type_JournalInfo, CSMWorld::ColumnBase::Display_JournalInfo },
{ CSMWorld::UniversalId::Type_Scene, CSMWorld::ColumnBase::Display_Scene },
{ CSMWorld::UniversalId::Type_Script, CSMWorld::ColumnBase::Display_Script },
case CSMWorld::ColumnBase::Display_Potion: { CSMWorld::UniversalId::Type_Mesh, CSMWorld::ColumnBase::Display_Mesh },
return CSMWorld::UniversalId::Type_Potion; { CSMWorld::UniversalId::Type_Icon, CSMWorld::ColumnBase::Display_Icon },
{ CSMWorld::UniversalId::Type_Music, CSMWorld::ColumnBase::Display_Music },
{ CSMWorld::UniversalId::Type_SoundRes, CSMWorld::ColumnBase::Display_SoundRes },
case CSMWorld::ColumnBase::Display_Apparatus: { CSMWorld::UniversalId::Type_Texture, CSMWorld::ColumnBase::Display_Texture },
return CSMWorld::UniversalId::Type_Apparatus; { CSMWorld::UniversalId::Type_Video, CSMWorld::ColumnBase::Display_Video },
{ CSMWorld::UniversalId::Type_None, CSMWorld::ColumnBase::Display_None } // end marker
case CSMWorld::ColumnBase::Display_Armor: };
return CSMWorld::UniversalId::Type_Armor;
case CSMWorld::ColumnBase::Display_Book:
return CSMWorld::UniversalId::Type_Book;
case CSMWorld::ColumnBase::Display_Clothing:
return CSMWorld::UniversalId::Type_Clothing;
case CSMWorld::ColumnBase::Display_Container:
return CSMWorld::UniversalId::Type_Container;
case CSMWorld::ColumnBase::Display_Creature:
return CSMWorld::UniversalId::Type_Creature;
case CSMWorld::ColumnBase::Display_Door:
return CSMWorld::UniversalId::Type_Door;
case CSMWorld::ColumnBase::Display_Ingredient:
return CSMWorld::UniversalId::Type_Ingredient;
case CSMWorld::ColumnBase::Display_CreatureLevelledList:
return CSMWorld::UniversalId::Type_CreatureLevelledList;
case CSMWorld::ColumnBase::Display_ItemLevelledList:
return CSMWorld::UniversalId::Type_ItemLevelledList;
case CSMWorld::ColumnBase::Display_Light:
return CSMWorld::UniversalId::Type_Light;
case CSMWorld::ColumnBase::Display_Lockpick:
return CSMWorld::UniversalId::Type_Lockpick;
case CSMWorld::ColumnBase::Display_Miscellaneous:
return CSMWorld::UniversalId::Type_Miscellaneous;
case CSMWorld::ColumnBase::Display_Npc:
return CSMWorld::UniversalId::Type_Npc;
case CSMWorld::ColumnBase::Display_Probe:
return CSMWorld::UniversalId::Type_Probe;
case CSMWorld::ColumnBase::Display_Repair:
return CSMWorld::UniversalId::Type_Repair;
case CSMWorld::ColumnBase::Display_Static:
return CSMWorld::UniversalId::Type_Static;
case CSMWorld::ColumnBase::Display_Weapon:
return CSMWorld::UniversalId::Type_Weapon;
case CSMWorld::ColumnBase::Display_Reference:
return CSMWorld::UniversalId::Type_Reference;
case CSMWorld::ColumnBase::Display_Filter:
return CSMWorld::UniversalId::Type_Filter;
case CSMWorld::ColumnBase::Display_Topic:
return CSMWorld::UniversalId::Type_Topic;
case CSMWorld::ColumnBase::Display_Journal:
return CSMWorld::UniversalId::Type_Journal;
case CSMWorld::ColumnBase::Display_TopicInfo:
return CSMWorld::UniversalId::Type_TopicInfo;
case CSMWorld::ColumnBase::Display_JournalInfo:
return CSMWorld::UniversalId::Type_JournalInfo;
case CSMWorld::ColumnBase::Display_Scene:
return CSMWorld::UniversalId::Type_Scene;
case CSMWorld::ColumnBase::Display_Script:
return CSMWorld::UniversalId::Type_Script;
default:
return CSMWorld::UniversalId::Type_None;
}
} }
CSMWorld::ColumnBase::Display CSMWorld::TableMimeData::convertEnums (CSMWorld::UniversalId::Type type) CSMWorld::UniversalId::Type CSMWorld::TableMimeData::convertEnums (ColumnBase::Display type)
{ {
switch (type) for (int i=0; mapping[i].mUniversalIdType!=CSMWorld::UniversalId::Type_None; ++i)
{ if (mapping[i].mDisplayType==type)
case CSMWorld::UniversalId::Type_Race: return mapping[i].mUniversalIdType;
return CSMWorld::ColumnBase::Display_Race;
case CSMWorld::UniversalId::Type_Skill:
return CSMWorld::ColumnBase::Display_Skill;
case CSMWorld::UniversalId::Type_Class:
return CSMWorld::ColumnBase::Display_Class;
case CSMWorld::UniversalId::Type_Faction:
return CSMWorld::ColumnBase::Display_Faction;
case CSMWorld::UniversalId::Type_Sound:
return CSMWorld::ColumnBase::Display_Sound;
case CSMWorld::UniversalId::Type_Region:
return CSMWorld::ColumnBase::Display_Region;
case CSMWorld::UniversalId::Type_Birthsign:
return CSMWorld::ColumnBase::Display_Birthsign;
case CSMWorld::UniversalId::Type_Spell:
return CSMWorld::ColumnBase::Display_Spell;
case CSMWorld::UniversalId::Type_Cell:
return CSMWorld::ColumnBase::Display_Cell;
case CSMWorld::UniversalId::Type_Referenceable:
return CSMWorld::ColumnBase::Display_Referenceable;
case CSMWorld::UniversalId::Type_Activator:
return CSMWorld::ColumnBase::Display_Activator;
case CSMWorld::UniversalId::Type_Potion:
return CSMWorld::ColumnBase::Display_Potion;
case CSMWorld::UniversalId::Type_Apparatus:
return CSMWorld::ColumnBase::Display_Apparatus;
case CSMWorld::UniversalId::Type_Armor:
return CSMWorld::ColumnBase::Display_Armor;
case CSMWorld::UniversalId::Type_Book:
return CSMWorld::ColumnBase::Display_Book;
case CSMWorld::UniversalId::Type_Clothing:
return CSMWorld::ColumnBase::Display_Clothing;
case CSMWorld::UniversalId::Type_Container:
return CSMWorld::ColumnBase::Display_Container;
case CSMWorld::UniversalId::Type_Creature:
return CSMWorld::ColumnBase::Display_Creature;
case CSMWorld::UniversalId::Type_Door:
return CSMWorld::ColumnBase::Display_Door;
case CSMWorld::UniversalId::Type_Ingredient:
return CSMWorld::ColumnBase::Display_Ingredient;
return CSMWorld::UniversalId::Type_None;
}
case CSMWorld::UniversalId::Type_CreatureLevelledList: CSMWorld::ColumnBase::Display CSMWorld::TableMimeData::convertEnums (UniversalId::Type type)
return CSMWorld::ColumnBase::Display_CreatureLevelledList; {
for (int i=0; mapping[i].mUniversalIdType!=CSMWorld::UniversalId::Type_None; ++i)
if (mapping[i].mUniversalIdType==type)
case CSMWorld::UniversalId::Type_ItemLevelledList: return mapping[i].mDisplayType;
return CSMWorld::ColumnBase::Display_ItemLevelledList;
case CSMWorld::UniversalId::Type_Light:
return CSMWorld::ColumnBase::Display_Light;
case CSMWorld::UniversalId::Type_Lockpick:
return CSMWorld::ColumnBase::Display_Lockpick;
case CSMWorld::UniversalId::Type_Miscellaneous:
return CSMWorld::ColumnBase::Display_Miscellaneous;
case CSMWorld::UniversalId::Type_Npc:
return CSMWorld::ColumnBase::Display_Npc;
case CSMWorld::UniversalId::Type_Probe:
return CSMWorld::ColumnBase::Display_Probe;
case CSMWorld::UniversalId::Type_Repair:
return CSMWorld::ColumnBase::Display_Repair;
case CSMWorld::UniversalId::Type_Static:
return CSMWorld::ColumnBase::Display_Static;
case CSMWorld::UniversalId::Type_Weapon:
return CSMWorld::ColumnBase::Display_Weapon;
case CSMWorld::UniversalId::Type_Reference:
return CSMWorld::ColumnBase::Display_Reference;
case CSMWorld::UniversalId::Type_Filter:
return CSMWorld::ColumnBase::Display_Filter;
case CSMWorld::UniversalId::Type_Topic:
return CSMWorld::ColumnBase::Display_Topic;
case CSMWorld::UniversalId::Type_Journal:
return CSMWorld::ColumnBase::Display_Journal;
case CSMWorld::UniversalId::Type_TopicInfo:
return CSMWorld::ColumnBase::Display_TopicInfo;
case CSMWorld::UniversalId::Type_JournalInfo:
return CSMWorld::ColumnBase::Display_JournalInfo;
case CSMWorld::UniversalId::Type_Scene:
return CSMWorld::ColumnBase::Display_Scene;
case CSMWorld::UniversalId::Type_Script:
return CSMWorld::ColumnBase::Display_Script;
default: return CSMWorld::ColumnBase::Display_None;
return CSMWorld::ColumnBase::Display_None;
}
} }
const CSMDoc::Document* CSMWorld::TableMimeData::getDocumentPtr() const const CSMDoc::Document* CSMWorld::TableMimeData::getDocumentPtr() const

@ -44,6 +44,12 @@ namespace
{ CSMWorld::UniversalId::Class_NonRecord, CSMWorld::UniversalId::Type_RegionMap, { CSMWorld::UniversalId::Class_NonRecord, CSMWorld::UniversalId::Type_RegionMap,
"Region Map", 0 }, "Region Map", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Filters, "Filters", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Filters, "Filters", 0 },
{ CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Meshes, "Meshes", 0 },
{ CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Icons, "Icons", 0 },
{ CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Musics, "Musics", 0 },
{ CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_SoundsRes, "Sound Files", 0 },
{ CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Textures, "Textures", 0 },
{ CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Videos, "Videos", 0 },
{ CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0, 0 } // end marker { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0, 0 } // end marker
}; };
@ -97,6 +103,12 @@ namespace
{ CSMWorld::UniversalId::Class_Collection, CSMWorld::UniversalId::Type_Preview, "Preview", 0 }, { CSMWorld::UniversalId::Class_Collection, CSMWorld::UniversalId::Type_Preview, "Preview", 0 },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Enchantment, "Enchantment", 0 }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Enchantment, "Enchantment", 0 },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_BodyPart, "Body Part", 0 }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_BodyPart, "Body Part", 0 },
{ CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Mesh, "Mesh", 0 },
{ CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Icon, "Icon", 0 },
{ CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Music, "Music", 0 },
{ CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_SoundRes, "Sound File", 0 },
{ CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Texture, "Texture", 0 },
{ CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Video, "Video", 0 },
{ CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0, 0 } // end marker { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0, 0 } // end marker
}; };
@ -331,7 +343,8 @@ CSMWorld::UniversalId::Type CSMWorld::UniversalId::getParentType (Type type)
if (sIdArg[i].mClass==Class_RefRecord) if (sIdArg[i].mClass==Class_RefRecord)
return Type_Referenceables; return Type_Referenceables;
if (sIdArg[i].mClass==Class_SubRecord || sIdArg[i].mClass==Class_Record) if (sIdArg[i].mClass==Class_SubRecord || sIdArg[i].mClass==Class_Record ||
sIdArg[i].mClass==Class_Resource)
{ {
if (type==Type_Cell_Missing) if (type==Type_Cell_Missing)
return Type_Cells; return Type_Cells;

@ -22,7 +22,10 @@ namespace CSMWorld
Class_RecordList, Class_RecordList,
Class_Collection, // multiple types of records combined Class_Collection, // multiple types of records combined
Class_Transient, // not part of the world data or the project data Class_Transient, // not part of the world data or the project data
Class_NonRecord // record like data that is not part of the world Class_NonRecord, // record like data that is not part of the world
Class_Resource, ///< \attention Resource IDs are unique only within the
/// respective collection
Class_ResourceList
}; };
enum ArgumentType enum ArgumentType
@ -104,7 +107,19 @@ namespace CSMWorld
Type_Enchantments, Type_Enchantments,
Type_Enchantment, Type_Enchantment,
Type_BodyParts, Type_BodyParts,
Type_BodyPart Type_BodyPart,
Type_Meshes,
Type_Mesh,
Type_Icons,
Type_Icon,
Type_Musics,
Type_Music,
Type_SoundsRes,
Type_SoundRes,
Type_Textures,
Type_Texture,
Type_Videos,
Type_Video
}; };
enum { NumberOfTypes = Type_BodyPart+1 }; enum { NumberOfTypes = Type_BodyPart+1 };

@ -81,6 +81,7 @@ QWidget *CSVDoc::StartupDialogue::createTools()
config->setSizePolicy (QSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed)); config->setSizePolicy (QSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed));
config->setIcon (QIcon (":startup/configure")); config->setIcon (QIcon (":startup/configure"));
config->setToolTip ("Open user settings");
layout->addWidget (config); layout->addWidget (config);

@ -204,6 +204,32 @@ void CSVDoc::View::setupAssetsMenu()
QAction *sounds = new QAction (tr ("Sounds"), this); QAction *sounds = new QAction (tr ("Sounds"), this);
connect (sounds, SIGNAL (triggered()), this, SLOT (addSoundsSubView())); connect (sounds, SIGNAL (triggered()), this, SLOT (addSoundsSubView()));
assets->addAction (sounds); assets->addAction (sounds);
assets->addSeparator(); // resources follow here
QAction *meshes = new QAction (tr ("Meshes"), this);
connect (meshes, SIGNAL (triggered()), this, SLOT (addMeshesSubView()));
assets->addAction (meshes);
QAction *icons = new QAction (tr ("Icons"), this);
connect (icons, SIGNAL (triggered()), this, SLOT (addIconsSubView()));
assets->addAction (icons);
QAction *musics = new QAction (tr ("Music"), this);
connect (musics, SIGNAL (triggered()), this, SLOT (addMusicsSubView()));
assets->addAction (musics);
QAction *soundsRes = new QAction (tr ("Sound Files"), this);
connect (soundsRes, SIGNAL (triggered()), this, SLOT (addSoundsResSubView()));
assets->addAction (soundsRes);
QAction *textures = new QAction (tr ("Textures"), this);
connect (textures, SIGNAL (triggered()), this, SLOT (addTexturesSubView()));
assets->addAction (textures);
QAction *videos = new QAction (tr ("Videos"), this);
connect (videos, SIGNAL (triggered()), this, SLOT (addVideosSubView()));
assets->addAction (videos);
} }
void CSVDoc::View::setupUi() void CSVDoc::View::setupUi()
@ -487,6 +513,36 @@ void CSVDoc::View::addBodyPartsSubView()
addSubView (CSMWorld::UniversalId::Type_BodyParts); addSubView (CSMWorld::UniversalId::Type_BodyParts);
} }
void CSVDoc::View::addMeshesSubView()
{
addSubView (CSMWorld::UniversalId::Type_Meshes);
}
void CSVDoc::View::addIconsSubView()
{
addSubView (CSMWorld::UniversalId::Type_Icons);
}
void CSVDoc::View::addMusicsSubView()
{
addSubView (CSMWorld::UniversalId::Type_Musics);
}
void CSVDoc::View::addSoundsResSubView()
{
addSubView (CSMWorld::UniversalId::Type_SoundsRes);
}
void CSVDoc::View::addTexturesSubView()
{
addSubView (CSMWorld::UniversalId::Type_Textures);
}
void CSVDoc::View::addVideosSubView()
{
addSubView (CSMWorld::UniversalId::Type_Videos);
}
void CSVDoc::View::abortOperation (int type) void CSVDoc::View::abortOperation (int type)
{ {
mDocument->abortOperation (type); mDocument->abortOperation (type);

@ -182,6 +182,18 @@ namespace CSVDoc
void addBodyPartsSubView(); void addBodyPartsSubView();
void addMeshesSubView();
void addIconsSubView();
void addMusicsSubView();
void addSoundsResSubView();
void addTexturesSubView();
void addVideosSubView();
void toggleShowStatusBar (bool show); void toggleShowStatusBar (bool show);
void loadErrorLog(); void loadErrorLog();

@ -3,6 +3,7 @@
#include <QEvent> #include <QEvent>
#include <QResizeEvent> #include <QResizeEvent>
#include <QTimer> #include <QTimer>
#include <QShortcut>
#include <OgreRoot.h> #include <OgreRoot.h>
#include <OgreRenderWindow.h> #include <OgreRenderWindow.h>
@ -11,7 +12,7 @@
#include <OgreSceneNode.h> #include <OgreSceneNode.h>
#include <OgreViewport.h> #include <OgreViewport.h>
#include "../world/scenetoolmode.hpp" #include "../widget/scenetoolmode.hpp"
#include "navigation.hpp" #include "navigation.hpp"
#include "lighting.hpp" #include "lighting.hpp"
@ -51,16 +52,34 @@ namespace CSVRender
QTimer *timer = new QTimer (this); QTimer *timer = new QTimer (this);
connect (timer, SIGNAL (timeout()), this, SLOT (update())); connect (timer, SIGNAL (timeout()), this, SLOT (update()));
timer->start (20); /// \todo make this configurable timer->start (20); ///< \todo make this configurable
/// \todo make shortcut configurable
QShortcut *focusToolbar = new QShortcut (Qt::Key_T, this, 0, 0, Qt::WidgetWithChildrenShortcut);
connect (focusToolbar, SIGNAL (activated()), this, SIGNAL (focusToolbarRequest()));
} }
CSVWorld::SceneToolMode *SceneWidget::makeLightingSelector (CSVWorld::SceneToolbar *parent) CSVWidget::SceneToolMode *SceneWidget::makeLightingSelector (CSVWidget::SceneToolbar *parent)
{ {
CSVWorld::SceneToolMode *tool = new CSVWorld::SceneToolMode (parent); CSVWidget::SceneToolMode *tool = new CSVWidget::SceneToolMode (parent, "Lighting Mode");
tool->addButton (":door.png", "day"); /// \todo replace icons /// \todo replace icons
tool->addButton (":GMST.png", "night"); tool->addButton (":door.png", "day",
tool->addButton (":Info.png", "bright"); "Day"
"<ul><li>Cell specific ambient in interiors</li>"
"<li>Low ambient in exteriors</li>"
"<li>Strong directional light source/lir>"
"<li>This mode closely resembles day time in-game</li></ul>");
tool->addButton (":GMST.png", "night",
"Night"
"<ul><li>Cell specific ambient in interiors</li>"
"<li>Low ambient in exteriors</li>"
"<li>Weak directional light source</li>"
"<li>This mode closely resembles night time in-game</li></ul>");
tool->addButton (":Info.png", "bright",
"Bright"
"<ul><li>Maximum ambient</li>"
"<li>Strong directional light source</li></ul>");
connect (tool, SIGNAL (modeChanged (const std::string&)), connect (tool, SIGNAL (modeChanged (const std::string&)),
this, SLOT (selectLightingMode (const std::string&))); this, SLOT (selectLightingMode (const std::string&)));
@ -347,6 +366,9 @@ namespace CSVRender
mLighting = lighting; mLighting = lighting;
mLighting->activate (mSceneMgr, mHasDefaultAmbient ? &mDefaultAmbient : 0); mLighting->activate (mSceneMgr, mHasDefaultAmbient ? &mDefaultAmbient : 0);
if (mWindow)
mWindow->update();
} }
void SceneWidget::selectLightingMode (const std::string& mode) void SceneWidget::selectLightingMode (const std::string& mode)

@ -16,7 +16,7 @@ namespace Ogre
class RenderWindow; class RenderWindow;
} }
namespace CSVWorld namespace CSVWidget
{ {
class SceneToolMode; class SceneToolMode;
class SceneToolbar; class SceneToolbar;
@ -38,7 +38,7 @@ namespace CSVRender
QPaintEngine* paintEngine() const; QPaintEngine* paintEngine() const;
CSVWorld::SceneToolMode *makeLightingSelector (CSVWorld::SceneToolbar *parent); CSVWidget::SceneToolMode *makeLightingSelector (CSVWidget::SceneToolbar *parent);
///< \attention The created tool is not added to the toolbar (via addTool). Doing that ///< \attention The created tool is not added to the toolbar (via addTool). Doing that
/// is the responsibility of the calling function. /// is the responsibility of the calling function.
@ -111,6 +111,10 @@ namespace CSVRender
void update(); void update();
void selectLightingMode (const std::string& mode); void selectLightingMode (const std::string& mode);
signals:
void focusToolbarRequest();
}; };
} }

@ -7,8 +7,9 @@
#include <QtGui/qevent.h> #include <QtGui/qevent.h>
#include "../world/scenetoolmode.hpp" #include "../../model/world/universalid.hpp"
#include <apps/opencs/model/world/universalid.hpp>
#include "../widget/scenetoolmode.hpp"
CSVRender::WorldspaceWidget::WorldspaceWidget (CSMDoc::Document& document, QWidget* parent) CSVRender::WorldspaceWidget::WorldspaceWidget (CSMDoc::Document& document, QWidget* parent)
: SceneWidget (parent), mDocument(document) : SceneWidget (parent), mDocument(document)
@ -53,14 +54,39 @@ void CSVRender::WorldspaceWidget::selectDefaultNavigationMode()
setNavigation (&m1st); setNavigation (&m1st);
} }
CSVWorld::SceneToolMode *CSVRender::WorldspaceWidget::makeNavigationSelector ( CSVWidget::SceneToolMode *CSVRender::WorldspaceWidget::makeNavigationSelector (
CSVWorld::SceneToolbar *parent) CSVWidget::SceneToolbar *parent)
{ {
CSVWorld::SceneToolMode *tool = new CSVWorld::SceneToolMode (parent); CSVWidget::SceneToolMode *tool = new CSVWidget::SceneToolMode (parent, "Camera Mode");
tool->addButton (":door.png", "1st"); /// \todo replace icons /// \todo replace icons
tool->addButton (":GMST.png", "free"); /// \todo consider user-defined button-mapping
tool->addButton (":Info.png", "orbit"); tool->addButton (":door.png", "1st",
"First Person"
"<ul><li>Mouse-Look while holding the left button</li>"
"<li>WASD movement keys</li>"
"<li>Mouse wheel moves the camera forawrd/backward</li>"
"<li>Stafing (also vertically) by holding the left mouse button and control</li>"
"<li>Camera is held upright</li>"
"<li>Hold shift to speed up movement</li>"
"</ul>");
tool->addButton (":GMST.png", "free",
"Free Camera"
"<ul><li>Mouse-Look while holding the left button</li>"
"<li>Stafing (also vertically) via WASD or by holding the left mouse button and control</li>"
"<li>Mouse wheel moves the camera forawrd/backward</li>"
"<li>Roll camera with Q and E keys</li>"
"<li>Hold shift to speed up movement</li>"
"</ul>");
tool->addButton (":Info.png", "orbit",
"Orbiting Camera"
"<ul><li>Always facing the centre point</li>"
"<li>Rotate around the centre point via WASD or by moving the mouse while holding the left button</li>"
"<li>Mouse wheel moves camera away or towards centre point but can not pass through it</li>"
"<li>Roll camera with Q and E keys</li>"
"<li>Stafing (also vertically) by holding the left mouse button and control (includes relocation of the centre point)</li>"
"<li>Hold shift to speed up movement</li>"
"</ul>");
connect (tool, SIGNAL (modeChanged (const std::string&)), connect (tool, SIGNAL (modeChanged (const std::string&)),
this, SLOT (selectNavigationMode (const std::string&))); this, SLOT (selectNavigationMode (const std::string&)));

@ -13,7 +13,7 @@ namespace CSMWorld
{ {
class UniversalId; class UniversalId;
} }
namespace CSVWorld namespace CSVWidget
{ {
class SceneToolMode; class SceneToolMode;
class SceneToolbar; class SceneToolbar;
@ -49,7 +49,7 @@ namespace CSVRender
WorldspaceWidget (CSMDoc::Document& document, QWidget *parent = 0); WorldspaceWidget (CSMDoc::Document& document, QWidget *parent = 0);
CSVWorld::SceneToolMode *makeNavigationSelector (CSVWorld::SceneToolbar *parent); CSVWidget::SceneToolMode *makeNavigationSelector (CSVWidget::SceneToolbar *parent);
///< \attention The created tool is not added to the toolbar (via addTool). Doing that ///< \attention The created tool is not added to the toolbar (via addTool). Doing that
/// is the responsibility of the calling function. /// is the responsibility of the calling function.

@ -0,0 +1,83 @@
#include "pushbutton.hpp"
#include <QMouseEvent>
#include <QKeyEvent>
void CSVWidget::PushButton::setExtendedToolTip (const QString& text)
{
QString tooltip = text;
if (tooltip.isEmpty())
tooltip = "(Tool tip not implemented yet)";
switch (mType)
{
case Type_TopMode:
tooltip +=
"<p>(left click to change mode)";
break;
case Type_Mode:
tooltip +=
"<p>(left click to activate,"
"<br>shift-left click to activate and keep panel open)";
break;
}
setToolTip (tooltip);
}
void CSVWidget::PushButton::keyPressEvent (QKeyEvent *event)
{
if (event->key()!=Qt::Key_Shift)
mKeepOpen = false;
QPushButton::keyPressEvent (event);
}
void CSVWidget::PushButton::keyReleaseEvent (QKeyEvent *event)
{
if (event->key()==Qt::Key_Return || event->key()==Qt::Key_Enter)
{
mKeepOpen = event->modifiers() & Qt::ShiftModifier;
emit clicked();
}
QPushButton::keyReleaseEvent (event);
}
void CSVWidget::PushButton::mouseReleaseEvent (QMouseEvent *event)
{
mKeepOpen = event->button()==Qt::LeftButton && (event->modifiers() & Qt::ShiftModifier);
QPushButton::mouseReleaseEvent (event);
}
CSVWidget::PushButton::PushButton (const QIcon& icon, Type type, const QString& tooltip,
QWidget *parent)
: QPushButton (icon, "", parent), mKeepOpen (false), mType (type), mToolTip (tooltip)
{
setCheckable (type==Type_Mode);
setExtendedToolTip (tooltip);
}
CSVWidget::PushButton::PushButton (Type type, const QString& tooltip, QWidget *parent)
: QPushButton (parent), mKeepOpen (false), mType (type), mToolTip (tooltip)
{
setCheckable (type==Type_Mode);
setExtendedToolTip (tooltip);
}
bool CSVWidget::PushButton::hasKeepOpen() const
{
return mKeepOpen;
}
QString CSVWidget::PushButton::getBaseToolTip() const
{
return mToolTip;
}

@ -0,0 +1,55 @@
#ifndef CSV_WIDGET_PUSHBUTTON_H
#define CSV_WIDGET_PUSHBUTTON_H
#include <QPushButton>
namespace CSVWidget
{
class PushButton : public QPushButton
{
Q_OBJECT
public:
enum Type
{
Type_TopMode, // top level button for mode selector panel
Type_Mode // mode button
};
private:
bool mKeepOpen;
Type mType;
QString mToolTip;
private:
void setExtendedToolTip (const QString& text);
protected:
virtual void keyPressEvent (QKeyEvent *event);
virtual void keyReleaseEvent (QKeyEvent *event);
virtual void mouseReleaseEvent (QMouseEvent *event);
public:
/// \param push Do not maintain a toggle state
PushButton (const QIcon& icon, Type type, const QString& tooltip = "",
QWidget *parent = 0);
/// \param push Do not maintain a toggle state
PushButton (Type type, const QString& tooltip = "",
QWidget *parent = 0);
bool hasKeepOpen() const;
/// Return tooltip used at construction (without any button-specific modifications)
QString getBaseToolTip() const;
};
}
#endif

@ -3,7 +3,8 @@
#include "scenetoolbar.hpp" #include "scenetoolbar.hpp"
CSVWorld::SceneTool::SceneTool (SceneToolbar *parent) : QPushButton (parent) CSVWidget::SceneTool::SceneTool (SceneToolbar *parent)
: PushButton (PushButton::Type_TopMode, "", parent)
{ {
setSizePolicy (QSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed)); setSizePolicy (QSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed));
setIconSize (QSize (parent->getIconSize(), parent->getIconSize())); setIconSize (QSize (parent->getIconSize(), parent->getIconSize()));
@ -12,7 +13,7 @@ CSVWorld::SceneTool::SceneTool (SceneToolbar *parent) : QPushButton (parent)
connect (this, SIGNAL (clicked()), this, SLOT (openRequest())); connect (this, SIGNAL (clicked()), this, SLOT (openRequest()));
} }
void CSVWorld::SceneTool::openRequest() void CSVWidget::SceneTool::openRequest()
{ {
showPanel (parentWidget()->mapToGlobal (pos())); showPanel (parentWidget()->mapToGlobal (pos()));
} }

@ -1,14 +1,14 @@
#ifndef CSV_WORLD_SCENETOOL_H #ifndef CSV_WIDGET_SCENETOOL_H
#define CSV_WORLD_SCENETOOL_H #define CSV_WIDGET_SCENETOOL_H
#include <QPushButton> #include "pushbutton.hpp"
namespace CSVWorld namespace CSVWidget
{ {
class SceneToolbar; class SceneToolbar;
///< \brief Tool base class ///< \brief Tool base class
class SceneTool : public QPushButton class SceneTool : public PushButton
{ {
Q_OBJECT Q_OBJECT

@ -0,0 +1,47 @@
#include "scenetoolbar.hpp"
#include <QVBoxLayout>
#include <QShortcut>
#include "scenetool.hpp"
void CSVWidget::SceneToolbar::focusInEvent (QFocusEvent *event)
{
QWidget::focusInEvent (event);
if (mLayout->count())
dynamic_cast<QWidgetItem&> (*mLayout->itemAt (0)).widget()->setFocus();
}
CSVWidget::SceneToolbar::SceneToolbar (int buttonSize, QWidget *parent)
: QWidget (parent), mButtonSize (buttonSize), mIconSize (buttonSize-6)
{
setFixedWidth (mButtonSize);
mLayout = new QVBoxLayout (this);
mLayout->setAlignment (Qt::AlignTop);
mLayout->setContentsMargins (QMargins (0, 0, 0, 0));
setLayout (mLayout);
/// \todo make shortcut configurable
QShortcut *focusScene = new QShortcut (Qt::Key_T, this, 0, 0, Qt::WidgetWithChildrenShortcut);
connect (focusScene, SIGNAL (activated()), this, SIGNAL (focusSceneRequest()));
}
void CSVWidget::SceneToolbar::addTool (SceneTool *tool)
{
mLayout->addWidget (tool, 0, Qt::AlignTop);
}
int CSVWidget::SceneToolbar::getButtonSize() const
{
return mButtonSize;
}
int CSVWidget::SceneToolbar::getIconSize() const
{
return mIconSize;
}

@ -1,11 +1,11 @@
#ifndef CSV_WORLD_SCENETOOLBAR_H #ifndef CSV_WIDGET_SCENETOOLBAR_H
#define CSV_WORLD_SCENETOOLBAR_H #define CSV_WIDGET_SCENETOOLBAR_H
#include <QWidget> #include <QWidget>
class QVBoxLayout; class QVBoxLayout;
namespace CSVWorld namespace CSVWidget
{ {
class SceneTool; class SceneTool;
@ -17,6 +17,10 @@ namespace CSVWorld
int mButtonSize; int mButtonSize;
int mIconSize; int mIconSize;
protected:
virtual void focusInEvent (QFocusEvent *event);
public: public:
SceneToolbar (int buttonSize, QWidget *parent = 0); SceneToolbar (int buttonSize, QWidget *parent = 0);
@ -26,6 +30,10 @@ namespace CSVWorld
int getButtonSize() const; int getButtonSize() const;
int getIconSize() const; int getIconSize() const;
signals:
void focusSceneRequest();
}; };
} }

@ -0,0 +1,86 @@
#include "scenetoolmode.hpp"
#include <QHBoxLayout>
#include <QFrame>
#include <QSignalMapper>
#include "scenetoolbar.hpp"
#include "pushbutton.hpp"
void CSVWidget::SceneToolMode::adjustToolTip (const PushButton *activeMode)
{
QString toolTip = mToolTip;
toolTip += "<p>Currently selected: " + activeMode->getBaseToolTip();
toolTip += "<p>(left click to change mode)";
setToolTip (toolTip);
}
CSVWidget::SceneToolMode::SceneToolMode (SceneToolbar *parent, const QString& toolTip)
: SceneTool (parent), mButtonSize (parent->getButtonSize()), mIconSize (parent->getIconSize()),
mToolTip (toolTip), mFirst (0)
{
mPanel = new QFrame (this, Qt::Popup);
mLayout = new QHBoxLayout (mPanel);
mLayout->setContentsMargins (QMargins (0, 0, 0, 0));
mPanel->setLayout (mLayout);
}
void CSVWidget::SceneToolMode::showPanel (const QPoint& position)
{
mPanel->move (position);
mPanel->show();
if (mFirst)
mFirst->setFocus (Qt::OtherFocusReason);
}
void CSVWidget::SceneToolMode::addButton (const std::string& icon, const std::string& id,
const QString& tooltip)
{
PushButton *button = new PushButton (QIcon (QPixmap (icon.c_str())), PushButton::Type_Mode,
tooltip, mPanel);
button->setSizePolicy (QSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed));
button->setIconSize (QSize (mIconSize, mIconSize));
button->setFixedSize (mButtonSize, mButtonSize);
mLayout->addWidget (button);
mButtons.insert (std::make_pair (button, id));
connect (button, SIGNAL (clicked()), this, SLOT (selected()));
if (mButtons.size()==1)
{
mFirst = button;
setIcon (button->icon());
button->setChecked (true);
adjustToolTip (button);
}
}
void CSVWidget::SceneToolMode::selected()
{
std::map<PushButton *, std::string>::const_iterator iter =
mButtons.find (dynamic_cast<PushButton *> (sender()));
if (iter!=mButtons.end())
{
if (!iter->first->hasKeepOpen())
mPanel->hide();
for (std::map<PushButton *, std::string>::const_iterator iter2 = mButtons.begin();
iter2!=mButtons.end(); ++iter2)
iter2->first->setChecked (iter2==iter);
setIcon (iter->first->icon());
adjustToolTip (iter->first);
emit modeChanged (iter->second);
}
}

@ -1,5 +1,5 @@
#ifndef CSV_WORLD_SCENETOOL_MODE_H #ifndef CSV_WIDGET_SCENETOOL_MODE_H
#define CSV_WORLD_SCENETOOL_MODE_H #define CSV_WIDGET_SCENETOOL_MODE_H
#include "scenetool.hpp" #include "scenetool.hpp"
@ -7,9 +7,10 @@
class QHBoxLayout; class QHBoxLayout;
namespace CSVWorld namespace CSVWidget
{ {
class SceneToolbar; class SceneToolbar;
class PushButton;
///< \brief Mode selector tool ///< \brief Mode selector tool
class SceneToolMode : public SceneTool class SceneToolMode : public SceneTool
@ -18,17 +19,22 @@ namespace CSVWorld
QWidget *mPanel; QWidget *mPanel;
QHBoxLayout *mLayout; QHBoxLayout *mLayout;
std::map<QPushButton *, std::string> mButtons; // widget, id std::map<PushButton *, std::string> mButtons; // widget, id
int mButtonSize; int mButtonSize;
int mIconSize; int mIconSize;
QString mToolTip;
PushButton *mFirst;
void adjustToolTip (const PushButton *activeMode);
public: public:
SceneToolMode (SceneToolbar *parent); SceneToolMode (SceneToolbar *parent, const QString& toolTip);
virtual void showPanel (const QPoint& position); virtual void showPanel (const QPoint& position);
void addButton (const std::string& icon, const std::string& id); void addButton (const std::string& icon, const std::string& id,
const QString& tooltip = "");
signals: signals:

@ -426,25 +426,32 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM
QHBoxLayout *buttonsLayout = new QHBoxLayout; QHBoxLayout *buttonsLayout = new QHBoxLayout;
QToolButton* prevButton = new QToolButton(mainWidget); QToolButton* prevButton = new QToolButton(mainWidget);
prevButton->setIcon(QIcon(":/go-previous.png")); prevButton->setIcon(QIcon(":/go-previous.png"));
prevButton->setToolTip ("Switch to previous record");
QToolButton* nextButton = new QToolButton(mainWidget); QToolButton* nextButton = new QToolButton(mainWidget);
nextButton->setIcon(QIcon(":/go-next.png")); nextButton->setIcon(QIcon(":/go-next.png"));
nextButton->setToolTip ("Switch to next record");
buttonsLayout->addWidget(prevButton, 0); buttonsLayout->addWidget(prevButton, 0);
buttonsLayout->addWidget(nextButton, 1); buttonsLayout->addWidget(nextButton, 1);
buttonsLayout->addStretch(2); buttonsLayout->addStretch(2);
QToolButton* cloneButton = new QToolButton(mainWidget); QToolButton* cloneButton = new QToolButton(mainWidget);
cloneButton->setIcon(QIcon(":/edit-clone.png")); cloneButton->setIcon(QIcon(":/edit-clone.png"));
cloneButton->setToolTip ("Clone record");
QToolButton* addButton = new QToolButton(mainWidget); QToolButton* addButton = new QToolButton(mainWidget);
addButton->setIcon(QIcon(":/add.png")); addButton->setIcon(QIcon(":/add.png"));
addButton->setToolTip ("Add new record");
QToolButton* deleteButton = new QToolButton(mainWidget); QToolButton* deleteButton = new QToolButton(mainWidget);
deleteButton->setIcon(QIcon(":/edit-delete.png")); deleteButton->setIcon(QIcon(":/edit-delete.png"));
deleteButton->setToolTip ("Delete record");
QToolButton* revertButton = new QToolButton(mainWidget); QToolButton* revertButton = new QToolButton(mainWidget);
revertButton->setIcon(QIcon(":/edit-undo.png")); revertButton->setIcon(QIcon(":/edit-undo.png"));
revertButton->setToolTip ("Revert record");
if (mTable->getFeatures() & CSMWorld::IdTable::Feature_Preview) if (mTable->getFeatures() & CSMWorld::IdTable::Feature_Preview)
{ {
QToolButton* previewButton = new QToolButton(mainWidget); QToolButton* previewButton = new QToolButton(mainWidget);
previewButton->setIcon(QIcon(":/edit-preview.png")); previewButton->setIcon(QIcon(":/edit-preview.png"));
previewButton->setToolTip ("Open a preview of this record");
buttonsLayout->addWidget(previewButton); buttonsLayout->addWidget(previewButton);
connect(previewButton, SIGNAL(clicked()), this, SLOT(showPreview())); connect(previewButton, SIGNAL(clicked()), this, SLOT(showPreview()));
} }
@ -453,6 +460,7 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM
{ {
QToolButton* viewButton = new QToolButton(mainWidget); QToolButton* viewButton = new QToolButton(mainWidget);
viewButton->setIcon(QIcon(":/cell.png")); viewButton->setIcon(QIcon(":/cell.png"));
viewButton->setToolTip ("Open a scene view of the cell this record is located in");
buttonsLayout->addWidget(viewButton); buttonsLayout->addWidget(viewButton);
connect(viewButton, SIGNAL(clicked()), this, SLOT(viewRecord())); connect(viewButton, SIGNAL(clicked()), this, SLOT(viewRecord()));
} }

@ -5,8 +5,8 @@
#include "../render/previewwidget.hpp" #include "../render/previewwidget.hpp"
#include "scenetoolbar.hpp" #include "../widget/scenetoolbar.hpp"
#include "scenetoolmode.hpp" #include "../widget/scenetoolmode.hpp"
CSVWorld::PreviewSubView::PreviewSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document) CSVWorld::PreviewSubView::PreviewSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document)
: SubView (id) : SubView (id)
@ -28,9 +28,9 @@ CSVWorld::PreviewSubView::PreviewSubView (const CSMWorld::UniversalId& id, CSMDo
else else
mScene = new CSVRender::PreviewWidget (document.getData(), id.getId(), true, this); mScene = new CSVRender::PreviewWidget (document.getData(), id.getId(), true, this);
SceneToolbar *toolbar = new SceneToolbar (48+6, this); CSVWidget::SceneToolbar *toolbar = new CSVWidget::SceneToolbar (48+6, this);
SceneToolMode *lightingTool = mScene->makeLightingSelector (toolbar); CSVWidget::SceneToolMode *lightingTool = mScene->makeLightingSelector (toolbar);
toolbar->addTool (lightingTool); toolbar->addTool (lightingTool);
layout->addWidget (toolbar, 0); layout->addWidget (toolbar, 0);
@ -46,6 +46,8 @@ CSVWorld::PreviewSubView::PreviewSubView (const CSMWorld::UniversalId& id, CSMDo
connect (mScene, SIGNAL (closeRequest()), this, SLOT (closeRequest())); connect (mScene, SIGNAL (closeRequest()), this, SLOT (closeRequest()));
connect (mScene, SIGNAL (referenceableIdChanged (const std::string&)), connect (mScene, SIGNAL (referenceableIdChanged (const std::string&)),
this, SLOT (referenceableIdChanged (const std::string&))); this, SLOT (referenceableIdChanged (const std::string&)));
connect (mScene, SIGNAL (focusToolbarRequest()), toolbar, SLOT (setFocus()));
connect (toolbar, SIGNAL (focusSceneRequest()), mScene, SLOT (setFocus()));
} }
void CSVWorld::PreviewSubView::setEditLock (bool locked) {} void CSVWorld::PreviewSubView::setEditLock (bool locked) {}

@ -17,9 +17,11 @@
#include "../render/pagedworldspacewidget.hpp" #include "../render/pagedworldspacewidget.hpp"
#include "../render/unpagedworldspacewidget.hpp" #include "../render/unpagedworldspacewidget.hpp"
#include "../widget/scenetoolbar.hpp"
#include "../widget/scenetoolmode.hpp"
#include "tablebottombox.hpp" #include "tablebottombox.hpp"
#include "creator.hpp" #include "creator.hpp"
#include "scenetoolmode.hpp"
CSVWorld::SceneSubView::SceneSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document) CSVWorld::SceneSubView::SceneSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document)
: SubView (id), mLayout(new QHBoxLayout), mDocument(document), mScene(NULL), mToolbar(NULL) : SubView (id), mLayout(new QHBoxLayout), mDocument(document), mScene(NULL), mToolbar(NULL)
@ -95,18 +97,18 @@ void CSVWorld::SceneSubView::makeConnections (CSVRender::PagedWorldspaceWidget*
this, SLOT (cellSelectionChanged (const CSMWorld::CellSelection&))); this, SLOT (cellSelectionChanged (const CSMWorld::CellSelection&)));
} }
CSVWorld::SceneToolbar* CSVWorld::SceneSubView::makeToolbar (CSVRender::WorldspaceWidget* widget, widgetType type) CSVWidget::SceneToolbar* CSVWorld::SceneSubView::makeToolbar (CSVRender::WorldspaceWidget* widget, widgetType type)
{ {
CSVWorld::SceneToolbar* toolbar = new SceneToolbar (48+6, this); CSVWidget::SceneToolbar* toolbar = new CSVWidget::SceneToolbar (48+6, this);
SceneToolMode *navigationTool = widget->makeNavigationSelector (toolbar); CSVWidget::SceneToolMode *navigationTool = widget->makeNavigationSelector (toolbar);
toolbar->addTool (navigationTool); toolbar->addTool (navigationTool);
SceneToolMode *lightingTool = widget->makeLightingSelector (toolbar); CSVWidget::SceneToolMode *lightingTool = widget->makeLightingSelector (toolbar);
toolbar->addTool (lightingTool); toolbar->addTool (lightingTool);
/* Add buttons specific to the type. For now no need for it. /* Add buttons specific to the type. For now no need for it.
* *
switch (type) switch (type)
{ {
case widget_Paged: case widget_Paged:
@ -188,7 +190,7 @@ void CSVWorld::SceneSubView::handleDrop (const std::vector< CSMWorld::UniversalI
{ {
CSVRender::PagedWorldspaceWidget* pagedNewWidget = NULL; CSVRender::PagedWorldspaceWidget* pagedNewWidget = NULL;
CSVRender::UnpagedWorldspaceWidget* unPagedNewWidget = NULL; CSVRender::UnpagedWorldspaceWidget* unPagedNewWidget = NULL;
SceneToolbar* toolbar = NULL; CSVWidget::SceneToolbar* toolbar = NULL;
switch (mScene->getDropRequirements(CSVRender::WorldspaceWidget::getDropType(data))) switch (mScene->getDropRequirements(CSVRender::WorldspaceWidget::getDropType(data)))
{ {
@ -217,7 +219,7 @@ void CSVWorld::SceneSubView::handleDrop (const std::vector< CSMWorld::UniversalI
} }
} }
void CSVWorld::SceneSubView::replaceToolbarAndWorldspace (CSVRender::WorldspaceWidget* widget, CSVWorld::SceneToolbar* toolbar) void CSVWorld::SceneSubView::replaceToolbarAndWorldspace (CSVRender::WorldspaceWidget* widget, CSVWidget::SceneToolbar* toolbar)
{ {
assert(mLayout); assert(mLayout);
@ -236,8 +238,12 @@ void CSVWorld::SceneSubView::replaceToolbarAndWorldspace (CSVRender::WorldspaceW
mScene = widget; mScene = widget;
mToolbar = toolbar; mToolbar = toolbar;
connect (mScene, SIGNAL (focusToolbarRequest()), mToolbar, SLOT (setFocus()));
connect (mToolbar, SIGNAL (focusSceneRequest()), mScene, SLOT (setFocus()));
mLayout->addWidget (mToolbar, 0); mLayout->addWidget (mToolbar, 0);
mLayout->addWidget (mScene, 1); mLayout->addWidget (mScene, 1);
mScene->selectDefaultNavigationMode(); mScene->selectDefaultNavigationMode();
setFocusProxy (mScene);
} }

@ -4,7 +4,6 @@
#include <QHBoxLayout> #include <QHBoxLayout>
#include "../doc/subview.hpp" #include "../doc/subview.hpp"
#include "scenetoolbar.hpp"
class QModelIndex; class QModelIndex;
@ -25,6 +24,11 @@ namespace CSVRender
class UnpagedWorldspaceWidget; class UnpagedWorldspaceWidget;
} }
namespace CSVWidget
{
class SceneToolbar;
}
namespace CSVWorld namespace CSVWorld
{ {
class Table; class Table;
@ -39,7 +43,7 @@ namespace CSVWorld
CSVRender::WorldspaceWidget *mScene; CSVRender::WorldspaceWidget *mScene;
QHBoxLayout* mLayout; QHBoxLayout* mLayout;
CSMDoc::Document& mDocument; CSMDoc::Document& mDocument;
SceneToolbar* mToolbar; CSVWidget::SceneToolbar* mToolbar;
public: public:
@ -59,14 +63,15 @@ namespace CSVWorld
void makeConnections(CSVRender::UnpagedWorldspaceWidget* widget); void makeConnections(CSVRender::UnpagedWorldspaceWidget* widget);
void replaceToolbarAndWorldspace(CSVRender::WorldspaceWidget* widget, SceneToolbar* toolbar); void replaceToolbarAndWorldspace(CSVRender::WorldspaceWidget* widget, CSVWidget::SceneToolbar* toolbar);
enum widgetType enum widgetType
{ {
widget_Paged, widget_Paged,
widget_Unpaged widget_Unpaged
}; };
SceneToolbar* makeToolbar(CSVRender::WorldspaceWidget* widget, widgetType type);
CSVWidget::SceneToolbar* makeToolbar(CSVRender::WorldspaceWidget* widget, widgetType type);
private slots: private slots:

@ -1,34 +0,0 @@
#include "scenetoolbar.hpp"
#include <QVBoxLayout>
#include "scenetool.hpp"
CSVWorld::SceneToolbar::SceneToolbar (int buttonSize, QWidget *parent)
: QWidget (parent), mButtonSize (buttonSize), mIconSize (buttonSize-6)
{
setFixedWidth (mButtonSize);
mLayout = new QVBoxLayout (this);
mLayout->setAlignment (Qt::AlignTop);
mLayout->setContentsMargins (QMargins (0, 0, 0, 0));
setLayout (mLayout);
}
void CSVWorld::SceneToolbar::addTool (SceneTool *tool)
{
mLayout->addWidget (tool, 0, Qt::AlignTop);
}
int CSVWorld::SceneToolbar::getButtonSize() const
{
return mButtonSize;
}
int CSVWorld::SceneToolbar::getIconSize() const
{
return mIconSize;
}

@ -1,57 +0,0 @@
#include "scenetoolmode.hpp"
#include <QHBoxLayout>
#include <QFrame>
#include <QSignalMapper>
#include "scenetoolbar.hpp"
CSVWorld::SceneToolMode::SceneToolMode (SceneToolbar *parent)
: SceneTool (parent), mButtonSize (parent->getButtonSize()), mIconSize (parent->getIconSize())
{
mPanel = new QFrame (this, Qt::Popup);
mLayout = new QHBoxLayout (mPanel);
mLayout->setContentsMargins (QMargins (0, 0, 0, 0));
mPanel->setLayout (mLayout);
}
void CSVWorld::SceneToolMode::showPanel (const QPoint& position)
{
mPanel->move (position);
mPanel->show();
}
void CSVWorld::SceneToolMode::addButton (const std::string& icon, const std::string& id)
{
QPushButton *button = new QPushButton (QIcon (QPixmap (icon.c_str())), "", mPanel);
button->setSizePolicy (QSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed));
button->setIconSize (QSize (mIconSize, mIconSize));
button->setFixedSize (mButtonSize, mButtonSize);
mLayout->addWidget (button);
mButtons.insert (std::make_pair (button, id));
connect (button, SIGNAL (clicked()), this, SLOT (selected()));
if (mButtons.size()==1)
setIcon (button->icon());
}
void CSVWorld::SceneToolMode::selected()
{
std::map<QPushButton *, std::string>::const_iterator iter =
mButtons.find (dynamic_cast<QPushButton *> (sender()));
if (iter!=mButtons.end())
{
mPanel->hide();
setIcon (iter->first->icon());
emit modeChanged (iter->second);
}
}

@ -70,6 +70,21 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager)
manager.add (CSMWorld::UniversalId::Type_JournalInfos, manager.add (CSMWorld::UniversalId::Type_JournalInfos,
new CSVDoc::SubViewFactoryWithCreator<TableSubView, CreatorFactory<InfoCreator> > (false)); new CSVDoc::SubViewFactoryWithCreator<TableSubView, CreatorFactory<InfoCreator> > (false));
// Subviews for resources tables
manager.add (CSMWorld::UniversalId::Type_Meshes,
new CSVDoc::SubViewFactoryWithCreator<TableSubView, NullCreatorFactory>);
manager.add (CSMWorld::UniversalId::Type_Icons,
new CSVDoc::SubViewFactoryWithCreator<TableSubView, NullCreatorFactory>);
manager.add (CSMWorld::UniversalId::Type_Musics,
new CSVDoc::SubViewFactoryWithCreator<TableSubView, NullCreatorFactory>);
manager.add (CSMWorld::UniversalId::Type_SoundsRes,
new CSVDoc::SubViewFactoryWithCreator<TableSubView, NullCreatorFactory>);
manager.add (CSMWorld::UniversalId::Type_Textures,
new CSVDoc::SubViewFactoryWithCreator<TableSubView, NullCreatorFactory>);
manager.add (CSMWorld::UniversalId::Type_Videos,
new CSVDoc::SubViewFactoryWithCreator<TableSubView, NullCreatorFactory>);
// Subviews for editing/viewing individual records // Subviews for editing/viewing individual records
manager.add (CSMWorld::UniversalId::Type_Script, new CSVDoc::SubViewFactory<ScriptSubView>); manager.add (CSMWorld::UniversalId::Type_Script, new CSVDoc::SubViewFactory<ScriptSubView>);

@ -14,6 +14,7 @@
#include "../../model/world/data.hpp" #include "../../model/world/data.hpp"
#include "../../model/world/commands.hpp" #include "../../model/world/commands.hpp"
#include "../../model/world/idtableproxymodel.hpp" #include "../../model/world/idtableproxymodel.hpp"
#include "../../model/world/idtablebase.hpp"
#include "../../model/world/idtable.hpp" #include "../../model/world/idtable.hpp"
#include "../../model/world/record.hpp" #include "../../model/world/record.hpp"
#include "../../model/world/columns.hpp" #include "../../model/world/columns.hpp"
@ -53,7 +54,7 @@ void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event)
/// \todo add menu items for select all and clear selection /// \todo add menu items for select all and clear selection
if (!mEditLock) if (!mEditLock && !(mModel->getFeatures() & CSMWorld::IdTableBase::Feature_Constant))
{ {
if (selectedRows.size()==1) if (selectedRows.size()==1)
{ {
@ -82,7 +83,7 @@ void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event)
menu.addAction (mExtendedDeleteAction); menu.addAction (mExtendedDeleteAction);
} }
if (mModel->getFeatures() & CSMWorld::IdTable::Feature_ReorderWithinTopic) if (mModel->getFeatures() & CSMWorld::IdTableBase::Feature_ReorderWithinTopic)
{ {
/// \todo allow reordering of multiple rows /// \todo allow reordering of multiple rows
if (selectedRows.size()==1) if (selectedRows.size()==1)
@ -119,7 +120,7 @@ void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event)
row = mProxyModel->mapToSource (mProxyModel->index (row, 0)).row(); row = mProxyModel->mapToSource (mProxyModel->index (row, 0)).row();
if (mModel->getFeatures() & CSMWorld::IdTable::Feature_View) if (mModel->getFeatures() & CSMWorld::IdTableBase::Feature_View)
{ {
CSMWorld::UniversalId id = mModel->view (row).first; CSMWorld::UniversalId id = mModel->view (row).first;
@ -131,7 +132,7 @@ void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event)
menu.addAction (mViewAction); menu.addAction (mViewAction);
} }
if (mModel->getFeatures() & CSMWorld::IdTable::Feature_Preview) if (mModel->getFeatures() & CSMWorld::IdTableBase::Feature_Preview)
{ {
QModelIndex index = mModel->index (row, QModelIndex index = mModel->index (row,
mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Modification)); mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Modification));
@ -152,7 +153,7 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id,
: mCreateAction (0), mCloneAction(0), mRecordStatusDisplay (0), : mCreateAction (0), mCloneAction(0), mRecordStatusDisplay (0),
DragRecordTable(document) DragRecordTable(document)
{ {
mModel = &dynamic_cast<CSMWorld::IdTable&> (*mDocument.getData().getTableModel (id)); mModel = &dynamic_cast<CSMWorld::IdTableBase&> (*mDocument.getData().getTableModel (id));
mProxyModel = new CSMWorld::IdTableProxyModel (this); mProxyModel = new CSMWorld::IdTableProxyModel (this);
mProxyModel->setSourceModel (mModel); mProxyModel->setSourceModel (mModel);
@ -275,7 +276,7 @@ CSMWorld::UniversalId CSVWorld::Table::getUniversalId (int row) const
void CSVWorld::Table::editRecord() void CSVWorld::Table::editRecord()
{ {
if (!mEditLock) if (!mEditLock || (mModel->getFeatures() & CSMWorld::IdTableBase::Feature_Constant))
{ {
QModelIndexList selectedRows = selectionModel()->selectedRows(); QModelIndexList selectedRows = selectionModel()->selectedRows();
@ -286,11 +287,11 @@ void CSVWorld::Table::editRecord()
void CSVWorld::Table::cloneRecord() void CSVWorld::Table::cloneRecord()
{ {
if (!mEditLock) if (!mEditLock || (mModel->getFeatures() & CSMWorld::IdTableBase::Feature_Constant))
{ {
QModelIndexList selectedRows = selectionModel()->selectedRows(); QModelIndexList selectedRows = selectionModel()->selectedRows();
const CSMWorld::UniversalId& toClone = getUniversalId(selectedRows.begin()->row()); const CSMWorld::UniversalId& toClone = getUniversalId(selectedRows.begin()->row());
if (selectedRows.size()==1 && !mModel->getRecord(toClone.getId()).isDeleted()) if (selectedRows.size()==1 && !mModel->isDeleted (toClone.getId()))
{ {
emit cloneRequest (toClone); emit cloneRequest (toClone);
} }
@ -299,7 +300,7 @@ void CSVWorld::Table::cloneRecord()
void CSVWorld::Table::moveUpRecord() void CSVWorld::Table::moveUpRecord()
{ {
if (mEditLock) if (mEditLock || (mModel->getFeatures() & CSMWorld::IdTableBase::Feature_Constant))
return; return;
QModelIndexList selectedRows = selectionModel()->selectedRows(); QModelIndexList selectedRows = selectionModel()->selectedRows();
@ -324,14 +325,15 @@ void CSVWorld::Table::moveUpRecord()
for (int i=1; i<row2-row; ++i) for (int i=1; i<row2-row; ++i)
newOrder[i] = i; newOrder[i] = i;
mDocument.getUndoStack().push (new CSMWorld::ReorderRowsCommand (*mModel, row, newOrder)); mDocument.getUndoStack().push (new CSMWorld::ReorderRowsCommand (
dynamic_cast<CSMWorld::IdTable&> (*mModel), row, newOrder));
} }
} }
} }
void CSVWorld::Table::moveDownRecord() void CSVWorld::Table::moveDownRecord()
{ {
if (mEditLock) if (mEditLock || (mModel->getFeatures() & CSMWorld::IdTableBase::Feature_Constant))
return; return;
QModelIndexList selectedRows = selectionModel()->selectedRows(); QModelIndexList selectedRows = selectionModel()->selectedRows();
@ -356,7 +358,8 @@ void CSVWorld::Table::moveDownRecord()
for (int i=1; i<row2-row; ++i) for (int i=1; i<row2-row; ++i)
newOrder[i] = i; newOrder[i] = i;
mDocument.getUndoStack().push (new CSMWorld::ReorderRowsCommand (*mModel, row, newOrder)); mDocument.getUndoStack().push (new CSMWorld::ReorderRowsCommand (
dynamic_cast<CSMWorld::IdTable&> (*mModel), row, newOrder));
} }
} }
} }
@ -422,21 +425,27 @@ void CSVWorld::Table::tableSizeUpdate()
{ {
int rows = mProxyModel->rowCount(); int rows = mProxyModel->rowCount();
for (int i=0; i<rows; ++i) int columnIndex = mModel->searchColumnIndex (CSMWorld::Columns::ColumnId_Modification);
if (columnIndex!=-1)
{ {
QModelIndex index = mProxyModel->mapToSource (mProxyModel->index (i, 0)); for (int i=0; i<rows; ++i)
{
QModelIndex index = mProxyModel->mapToSource (mProxyModel->index (i, 0));
int columnIndex = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Modification); int state = mModel->data (mModel->index (index.row(), columnIndex)).toInt();
int state = mModel->data (mModel->index (index.row(), columnIndex)).toInt();
switch (state) switch (state)
{ {
case CSMWorld::RecordBase::State_BaseOnly: ++size; break; case CSMWorld::RecordBase::State_BaseOnly: ++size; break;
case CSMWorld::RecordBase::State_Modified: ++size; ++modified; break; case CSMWorld::RecordBase::State_Modified: ++size; ++modified; break;
case CSMWorld::RecordBase::State_ModifiedOnly: ++size; ++modified; break; case CSMWorld::RecordBase::State_ModifiedOnly: ++size; ++modified; break;
case CSMWorld::RecordBase:: State_Deleted: ++deleted; ++modified; break; case CSMWorld::RecordBase:: State_Deleted: ++deleted; ++modified; break;
}
} }
} }
else
size = rows;
} }
tableSizeChanged (size, deleted, modified); tableSizeChanged (size, deleted, modified);

@ -23,7 +23,7 @@ namespace CSMWorld
class Data; class Data;
class UniversalId; class UniversalId;
class IdTableProxyModel; class IdTableProxyModel;
class IdTable; class IdTableBase;
class CommandDispatcher; class CommandDispatcher;
} }
@ -49,7 +49,7 @@ namespace CSVWorld
QAction *mExtendedDeleteAction; QAction *mExtendedDeleteAction;
QAction *mExtendedRevertAction; QAction *mExtendedRevertAction;
CSMWorld::IdTableProxyModel *mProxyModel; CSMWorld::IdTableProxyModel *mProxyModel;
CSMWorld::IdTable *mModel; CSMWorld::IdTableBase *mModel;
int mRecordStatusDisplay; int mRecordStatusDisplay;
CSMWorld::CommandDispatcher *mDispatcher; CSMWorld::CommandDispatcher *mDispatcher;

@ -143,48 +143,54 @@ QWidget *CSVWorld::CommandDelegate::createEditor (QWidget *parent, const QStyleO
} }
} }
if (display != CSMWorld::ColumnBase::Display_None) switch (display)
{ {
if (variant.type() == QVariant::Color) case CSMWorld::ColumnBase::Display_Colour:
{
return new QLineEdit(parent); return new QLineEdit(parent);
}
if (display == CSMWorld::ColumnBase::Display_Integer) case CSMWorld::ColumnBase::Display_Integer:
{
return new QSpinBox(parent); return new QSpinBox(parent);
}
if (display == CSMWorld::ColumnBase::Display_Var) case CSMWorld::ColumnBase::Display_Var:
{
return new QLineEdit(parent); return new QLineEdit(parent);
}
if (display == CSMWorld::ColumnBase::Display_Float) case CSMWorld::ColumnBase::Display_Float:
{
return new QDoubleSpinBox(parent); return new QDoubleSpinBox(parent);
}
if (display == CSMWorld::ColumnBase::Display_LongString) case CSMWorld::ColumnBase::Display_LongString:
{
return new QTextEdit(parent); return new QTextEdit(parent);
}
if (display == CSMWorld::ColumnBase::Display_String || case CSMWorld::ColumnBase::Display_Boolean:
display == CSMWorld::ColumnBase::Display_Skill ||
display == CSMWorld::ColumnBase::Display_Script ||
display == CSMWorld::ColumnBase::Display_Race ||
display == CSMWorld::ColumnBase::Display_Class ||
display == CSMWorld::ColumnBase::Display_Faction ||
display == CSMWorld::ColumnBase::Display_Miscellaneous ||
display == CSMWorld::ColumnBase::Display_Sound)
{
return new DropLineEdit(parent);
}
if (display == CSMWorld::ColumnBase::Display_Boolean)
{
return new QCheckBox(parent); return new QCheckBox(parent);
}
}
return QStyledItemDelegate::createEditor (parent, option, index); case CSMWorld::ColumnBase::Display_String:
} case CSMWorld::ColumnBase::Display_Skill:
case CSMWorld::ColumnBase::Display_Script:
case CSMWorld::ColumnBase::Display_Race:
case CSMWorld::ColumnBase::Display_Class:
case CSMWorld::ColumnBase::Display_Faction:
case CSMWorld::ColumnBase::Display_Miscellaneous:
case CSMWorld::ColumnBase::Display_Sound:
case CSMWorld::ColumnBase::Display_Mesh:
case CSMWorld::ColumnBase::Display_Icon:
case CSMWorld::ColumnBase::Display_Music:
case CSMWorld::ColumnBase::Display_SoundRes:
case CSMWorld::ColumnBase::Display_Texture:
case CSMWorld::ColumnBase::Display_Video:
return new DropLineEdit(parent);
default:
return QStyledItemDelegate::createEditor (parent, option, index);
}
}
void CSVWorld::CommandDelegate::setEditLock (bool locked) void CSVWorld::CommandDelegate::setEditLock (bool locked)
{ {

@ -69,7 +69,7 @@ add_openmw_dir (mwmechanics
mechanicsmanagerimp stat character creaturestats magiceffects movement actors objects mechanicsmanagerimp stat character creaturestats magiceffects movement actors objects
drawstate spells activespells npcstats aipackage aisequence aipursue alchemy aiwander aitravel aifollow aiavoiddoor drawstate spells activespells npcstats aipackage aisequence aipursue alchemy aiwander aitravel aifollow aiavoiddoor
aiescort aiactivate aicombat repair enchanting pathfinding pathgrid security spellsuccess spellcasting aiescort aiactivate aicombat repair enchanting pathfinding pathgrid security spellsuccess spellcasting
disease pickpocket levelledlist combat steering obstacle disease pickpocket levelledlist combat steering obstacle autocalcspell
) )
add_openmw_dir (mwstate add_openmw_dir (mwstate

@ -250,8 +250,11 @@ namespace MWClass
text += "\n#{sTrapped}"; text += "\n#{sTrapped}";
if (MWBase::Environment::get().getWindowManager()->getFullHelp()) if (MWBase::Environment::get().getWindowManager()->getFullHelp())
{
text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script");
text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner");
text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction");
}
info.text = text; info.text = text;
return info; return info;

@ -22,6 +22,7 @@
#include "../mwmechanics/spellcasting.hpp" #include "../mwmechanics/spellcasting.hpp"
#include "../mwmechanics/disease.hpp" #include "../mwmechanics/disease.hpp"
#include "../mwmechanics/combat.hpp" #include "../mwmechanics/combat.hpp"
#include "../mwmechanics/autocalcspell.hpp"
#include "../mwworld/ptr.hpp" #include "../mwworld/ptr.hpp"
#include "../mwworld/actiontalk.hpp" #include "../mwworld/actiontalk.hpp"
@ -53,6 +54,24 @@ namespace
return new NpcCustomData (*this); return new NpcCustomData (*this);
} }
int is_even(double d) {
double int_part;
modf(d / 2.0, &int_part);
return 2.0 * int_part == d;
}
int round_ieee_754(double d) {
double i = floor(d);
d -= i;
if(d < 0.5)
return i;
if(d > 0.5)
return i + 1.0;
if(is_even(i))
return i;
return i + 1.0;
}
void autoCalculateAttributes (const ESM::NPC* npc, MWMechanics::CreatureStats& creatureStats) void autoCalculateAttributes (const ESM::NPC* npc, MWMechanics::CreatureStats& creatureStats)
{ {
// race bonus // race bonus
@ -108,8 +127,9 @@ namespace
} }
modifierSum += add; modifierSum += add;
} }
creatureStats.setAttribute(attribute, std::min(creatureStats.getAttribute(attribute).getBase() creatureStats.setAttribute(attribute, std::min(
+ static_cast<int>((level-1) * modifierSum+0.5), 100) ); round_ieee_754(creatureStats.getAttribute(attribute).getBase()
+ (level-1) * modifierSum), 100) );
} }
// initial health // initial health
@ -193,18 +213,6 @@ namespace
majorMultiplier = 1.0f; majorMultiplier = 1.0f;
break; break;
} }
if (class_->mData.mSkills[k][1] == skillIndex)
{
// Major skill -> add starting spells for this skill if existing
const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();
MWWorld::Store<ESM::Spell>::iterator it = store.get<ESM::Spell>().begin();
for (; it != store.get<ESM::Spell>().end(); ++it)
{
if (it->mData.mFlags & ESM::Spell::F_Autocalc
&& MWMechanics::spellSchoolToSkill(MWMechanics::getSpellSchool(&*it, ptr)) == skillIndex)
npcStats.getSpells().add(it->mId);
}
}
} }
// is this skill in the same Specialization as the class? // is this skill in the same Specialization as the class?
@ -217,12 +225,25 @@ namespace
npcStats.getSkill(skillIndex).setBase( npcStats.getSkill(skillIndex).setBase(
std::min( std::min(
npcStats.getSkill(skillIndex).getBase() round_ieee_754(
npcStats.getSkill(skillIndex).getBase()
+ 5 + 5
+ raceBonus + raceBonus
+ specBonus + specBonus
+ static_cast<int>((level-1) * (majorMultiplier + specMultiplier)), 100)); +(int(level)-1) * (majorMultiplier + specMultiplier)), 100)); // Must gracefully handle level 0
} }
int skills[ESM::Skill::Length];
for (int i=0; i<ESM::Skill::Length; ++i)
skills[i] = npcStats.getSkill(i).getBase();
int attributes[ESM::Attribute::Length];
for (int i=0; i<ESM::Attribute::Length; ++i)
attributes[i] = npcStats.getAttribute(i).getBase();
std::vector<std::string> spells = MWMechanics::autoCalcNpcSpells(skills, attributes, race);
for (std::vector<std::string>::iterator it = spells.begin(); it != spells.end(); ++it)
npcStats.getSpells().add(*it);
} }
} }

@ -153,6 +153,11 @@ void CompanionWindow::onReferenceUnavailable()
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Companion); MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Companion);
} }
void CompanionWindow::resetReference()
{
ReferenceInterface::resetReference();
mItemView->setModel(NULL);
}
} }

@ -20,6 +20,8 @@ namespace MWGui
virtual void exit(); virtual void exit();
virtual void resetReference();
void open(const MWWorld::Ptr& npc); void open(const MWWorld::Ptr& npc);
void onFrame (); void onFrame ();

@ -258,6 +258,12 @@ namespace MWGui
onTakeAllButtonClicked(mTakeButton); onTakeAllButtonClicked(mTakeButton);
} }
void ContainerWindow::resetReference()
{
ReferenceInterface::resetReference();
mItemView->setModel(NULL);
}
void ContainerWindow::close() void ContainerWindow::close()
{ {
WindowBase::close(); WindowBase::close();

@ -54,6 +54,8 @@ namespace MWGui
void open(const MWWorld::Ptr& container, bool loot=false); void open(const MWWorld::Ptr& container, bool loot=false);
virtual void close(); virtual void close();
virtual void resetReference();
virtual void exit(); virtual void exit();
private: private:

@ -260,21 +260,28 @@ namespace MWGui
// More hacks! The french game uses several win1252 characters that are not included // More hacks! The french game uses several win1252 characters that are not included
// in the cp437 encoding of the font. Fall back to similar available characters. // in the cp437 encoding of the font. Fall back to similar available characters.
// Same for U+2013 if (mEncoding == ToUTF8::CP437)
std::map<int, int> additional;
additional[39] = 0x2019; // apostrophe
additional[45] = 0x2013; // dash
if (additional.find(i) != additional.end() && mEncoding == ToUTF8::CP437)
{ {
MyGUI::xml::ElementPtr code = codes->createChild("Code"); std::multimap<int, int> additional;
code->addAttribute("index", additional[i]); additional.insert(std::make_pair(39, 0x2019)); // apostrophe
code->addAttribute("coord", MyGUI::utility::toString(x1) + " " additional.insert(std::make_pair(45, 0x2013)); // dash
+ MyGUI::utility::toString(y1) + " " additional.insert(std::make_pair(34, 0x201D)); // right double quotation mark
+ MyGUI::utility::toString(w) + " " additional.insert(std::make_pair(34, 0x201C)); // left double quotation mark
+ MyGUI::utility::toString(h)); for (std::multimap<int, int>::iterator it = additional.begin(); it != additional.end(); ++it)
code->addAttribute("advance", data[i].width); {
code->addAttribute("bearing", MyGUI::utility::toString(data[i].kerning) + " " if (it->first != i)
+ MyGUI::utility::toString((fontSize-data[i].ascent))); continue;
MyGUI::xml::ElementPtr code = codes->createChild("Code");
code->addAttribute("index", it->second);
code->addAttribute("coord", MyGUI::utility::toString(x1) + " "
+ MyGUI::utility::toString(y1) + " "
+ MyGUI::utility::toString(w) + " "
+ MyGUI::utility::toString(h));
code->addAttribute("advance", data[i].width);
code->addAttribute("bearing", MyGUI::utility::toString(data[i].kerning) + " "
+ MyGUI::utility::toString((fontSize-data[i].ascent)));
}
} }
// ASCII vertical bar, use this as text input cursor // ASCII vertical bar, use this as text input cursor

@ -140,26 +140,28 @@ void ItemView::onMouseWheel(MyGUI::Widget *_sender, int _rel)
void ItemView::setSize(const MyGUI::IntSize &_value) void ItemView::setSize(const MyGUI::IntSize &_value)
{ {
bool changed = (_value.width != getWidth() || _value.height != getHeight());
Base::setSize(_value); Base::setSize(_value);
update(); if (changed)
update();
} }
void ItemView::setSize(int _width, int _height) void ItemView::setSize(int _width, int _height)
{ {
Base::setSize(_width, _height); setSize(MyGUI::IntSize(_width, _height));
update();
} }
void ItemView::setCoord(const MyGUI::IntCoord &_value) void ItemView::setCoord(const MyGUI::IntCoord &_value)
{ {
bool changed = (_value.width != getWidth() || _value.height != getHeight());
Base::setCoord(_value); Base::setCoord(_value);
update(); if (changed)
update();
} }
void ItemView::setCoord(int _left, int _top, int _width, int _height) void ItemView::setCoord(int _left, int _top, int _width, int _height)
{ {
Base::setCoord(_left, _top, _width, _height); setCoord(MyGUI::IntCoord(_left, _top, _width, _height));
update();
} }
void ItemView::registerComponents() void ItemView::registerComponents()

@ -16,9 +16,10 @@
namespace MWGui namespace MWGui
{ {
const unsigned int LevelupDialog::sMaxCoins = 3;
LevelupDialog::LevelupDialog() LevelupDialog::LevelupDialog()
: WindowBase("openmw_levelup_dialog.layout") : WindowBase("openmw_levelup_dialog.layout"),
mCoinCount(sMaxCoins)
{ {
getWidget(mOkButton, "OkButton"); getWidget(mOkButton, "OkButton");
getWidget(mClassImage, "ClassImage"); getWidget(mClassImage, "ClassImage");
@ -46,12 +47,10 @@ namespace MWGui
mAttributeMultipliers.push_back(t); mAttributeMultipliers.push_back(t);
} }
int curX = mMainWidget->getWidth()/2 - (16 + 2) * 1.5; for (unsigned int i = 0; i < mCoinCount; ++i)
for (int i=0; i<3; ++i)
{ {
MyGUI::ImageBox* image = mMainWidget->createWidget<MyGUI::ImageBox>("ImageBox", MyGUI::IntCoord(curX,250,16,16), MyGUI::Align::Default); MyGUI::ImageBox* image = mCoinBox->createWidget<MyGUI::ImageBox>("ImageBox", MyGUI::IntCoord(0,0,16,16), MyGUI::Align::Default);
image->setImageTexture ("icons\\tx_goldicon.dds"); image->setImageTexture ("icons\\tx_goldicon.dds");
curX += 24+2;
mCoins.push_back(image); mCoins.push_back(image);
} }
@ -61,15 +60,15 @@ namespace MWGui
void LevelupDialog::setAttributeValues() void LevelupDialog::setAttributeValues()
{ {
MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr();
MWMechanics::CreatureStats& creatureStats = player.getClass().getCreatureStats (player); MWMechanics::CreatureStats& creatureStats = player.getClass().getCreatureStats(player);
MWMechanics::NpcStats& pcStats = player.getClass().getNpcStats (player); MWMechanics::NpcStats& pcStats = player.getClass().getNpcStats (player);
for (int i=0; i<8; ++i) for (int i = 0; i < 8; ++i)
{ {
int val = creatureStats.getAttribute (i).getBase (); int val = creatureStats.getAttribute(i).getBase();
if (std::find(mSpentAttributes.begin(), mSpentAttributes.end(), i) != mSpentAttributes.end()) if (std::find(mSpentAttributes.begin(), mSpentAttributes.end(), i) != mSpentAttributes.end())
{ {
val += pcStats.getLevelupAttributeMultiplier (i); val += pcStats.getLevelupAttributeMultiplier(i);
} }
if (val >= 100) if (val >= 100)
@ -80,20 +79,21 @@ namespace MWGui
} }
void LevelupDialog::resetCoins () void LevelupDialog::resetCoins()
{ {
int curX = 0; const int coinSpacing = 10;
for (int i=0; i<3; ++i) int curX = mCoinBox->getWidth()/2 - (coinSpacing*(mCoinCount - 1) + 16*mCoinCount)/2;
for (unsigned int i=0; i<mCoinCount; ++i)
{ {
MyGUI::ImageBox* image = mCoins[i]; MyGUI::ImageBox* image = mCoins[i];
image->detachFromWidget(); image->detachFromWidget();
image->attachToWidget(mCoinBox); image->attachToWidget(mCoinBox);
image->setCoord(MyGUI::IntCoord(curX,0,16,16)); image->setCoord(MyGUI::IntCoord(curX,0,16,16));
curX += 24+2; curX += 16+coinSpacing;
} }
} }
void LevelupDialog::assignCoins () void LevelupDialog::assignCoins()
{ {
resetCoins(); resetCoins();
for (unsigned int i=0; i<mSpentAttributes.size(); ++i) for (unsigned int i=0; i<mSpentAttributes.size(); ++i)
@ -118,13 +118,8 @@ namespace MWGui
{ {
MWBase::World *world = MWBase::Environment::get().getWorld(); MWBase::World *world = MWBase::Environment::get().getWorld();
MWWorld::Ptr player = world->getPlayerPtr(); MWWorld::Ptr player = world->getPlayerPtr();
MWMechanics::CreatureStats& creatureStats = player.getClass().getCreatureStats (player); MWMechanics::CreatureStats& creatureStats = player.getClass().getCreatureStats(player);
MWMechanics::NpcStats& pcStats = player.getClass().getNpcStats (player); MWMechanics::NpcStats& pcStats = player.getClass().getNpcStats(player);
mSpentAttributes.clear();
resetCoins();
setAttributeValues();
const ESM::NPC *playerData = player.get<ESM::NPC>()->mBase; const ESM::NPC *playerData = player.get<ESM::NPC>()->mBase;
@ -144,70 +139,98 @@ namespace MWGui
if(it->mData.mIsPlayable && it->mData.mSpecialization == 2 && it->mData.mAttribute[0] == 4 && it->mData.mAttribute[1] == 3) if(it->mData.mIsPlayable && it->mData.mSpecialization == 2 && it->mData.mAttribute[0] == 4 && it->mData.mAttribute[1] == 3)
break; break;
} }
mClassImage->setImageTexture ("textures\\levelup\\" + it->mId + ".dds"); mClassImage->setImageTexture("textures\\levelup\\" + it->mId + ".dds");
} }
else else
mClassImage->setImageTexture ("textures\\levelup\\" + cls->mId + ".dds"); mClassImage->setImageTexture("textures\\levelup\\" + cls->mId + ".dds");
int level = creatureStats.getLevel ()+1; int level = creatureStats.getLevel ()+1;
mLevelText->setCaptionWithReplacing("#{sLevelUpMenu1} " + boost::lexical_cast<std::string>(level)); mLevelText->setCaptionWithReplacing("#{sLevelUpMenu1} " + boost::lexical_cast<std::string>(level));
std::string levelupdescription; std::string levelupdescription;
if(level>20) if(level > 20)
levelupdescription=world->getFallback()->getFallbackString("Level_Up_Default"); levelupdescription=world->getFallback()->getFallbackString("Level_Up_Default");
else else
levelupdescription=world->getFallback()->getFallbackString("Level_Up_Level"+boost::lexical_cast<std::string>(level)); levelupdescription=world->getFallback()->getFallbackString("Level_Up_Level"+boost::lexical_cast<std::string>(level));
mLevelDescription->setCaption (levelupdescription); mLevelDescription->setCaption (levelupdescription);
for (int i=0; i<8; ++i) unsigned int availableAttributes = 0;
for (int i = 0; i < 8; ++i)
{ {
MyGUI::TextBox* text = mAttributeMultipliers[i]; MyGUI::TextBox* text = mAttributeMultipliers[i];
int mult = pcStats.getLevelupAttributeMultiplier (i); if (pcStats.getAttribute(i).getBase() < 100)
text->setCaption(mult <= 1 ? "" : "x" + boost::lexical_cast<std::string>(mult)); {
mAttributes[i]->setEnabled(true);
availableAttributes++;
int mult = pcStats.getLevelupAttributeMultiplier (i);
text->setCaption(mult <= 1 ? "" : "x" + boost::lexical_cast<std::string>(mult));
}
else
{
mAttributes[i]->setEnabled(false);
text->setCaption("");
}
}
mCoinCount = std::min(sMaxCoins, availableAttributes);
for (unsigned int i = 0; i < sMaxCoins; i++)
{
if (i < mCoinCount)
mCoins[i]->attachToWidget(mCoinBox);
else
mCoins[i]->detachFromWidget();
} }
mSpentAttributes.clear();
resetCoins();
setAttributeValues();
center(); center();
} }
void LevelupDialog::onOkButtonClicked (MyGUI::Widget* sender) void LevelupDialog::onOkButtonClicked(MyGUI::Widget* sender)
{ {
MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
MWMechanics::NpcStats& pcStats = player.getClass().getNpcStats (player); MWMechanics::NpcStats& pcStats = player.getClass().getNpcStats (player);
if (mSpentAttributes.size() < 3) if (mSpentAttributes.size() < mCoinCount)
MWBase::Environment::get().getWindowManager ()->messageBox("#{sNotifyMessage36}"); MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage36}");
else else
{ {
// increase attributes // increase attributes
for (int i=0; i<3; ++i) for (unsigned int i = 0; i < mCoinCount; ++i)
{ {
MWMechanics::AttributeValue attribute = pcStats.getAttribute(mSpentAttributes[i]); MWMechanics::AttributeValue attribute = pcStats.getAttribute(mSpentAttributes[i]);
attribute.setBase (attribute.getBase () + pcStats.getLevelupAttributeMultiplier (mSpentAttributes[i])); attribute.setBase(attribute.getBase() + pcStats.getLevelupAttributeMultiplier(mSpentAttributes[i]));
if (attribute.getBase() >= 100) if (attribute.getBase() >= 100)
attribute.setBase(100); attribute.setBase(100);
pcStats.setAttribute(mSpentAttributes[i], attribute); pcStats.setAttribute(mSpentAttributes[i], attribute);
} }
pcStats.levelUp (); pcStats.levelUp();
MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Levelup); MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Levelup);
} }
} }
void LevelupDialog::onAttributeClicked (MyGUI::Widget *sender) void LevelupDialog::onAttributeClicked(MyGUI::Widget *sender)
{ {
int attribute = *sender->getUserData<int>(); int attribute = *sender->getUserData<int>();
std::vector<int>::iterator found = std::find(mSpentAttributes.begin(), mSpentAttributes.end(), attribute); std::vector<int>::iterator found = std::find(mSpentAttributes.begin(), mSpentAttributes.end(), attribute);
if (found != mSpentAttributes.end()) if (found != mSpentAttributes.end())
mSpentAttributes.erase (found); mSpentAttributes.erase(found);
else else
{ {
if (mSpentAttributes.size() == 3) if (mSpentAttributes.size() == mCoinCount)
mSpentAttributes[2] = attribute; mSpentAttributes[mCoinCount - 1] = attribute;
else else
mSpentAttributes.push_back(attribute); mSpentAttributes.push_back(attribute);
} }

@ -28,8 +28,11 @@ namespace MWGui
std::vector<int> mSpentAttributes; std::vector<int> mSpentAttributes;
void onOkButtonClicked (MyGUI::Widget* sender); unsigned int mCoinCount;
void onAttributeClicked (MyGUI::Widget* sender); static const unsigned int sMaxCoins;
void onOkButtonClicked(MyGUI::Widget* sender);
void onAttributeClicked(MyGUI::Widget* sender);
void assignCoins(); void assignCoins();
void resetCoins(); void resetCoins();

@ -321,6 +321,11 @@ namespace MWGui
skillValueWidget->_setWidgetState(state); skillValueWidget->_setWidgetState(state);
skillValueWidget->eventMouseWheel += MyGUI::newDelegate(this, &StatsWindow::onMouseWheel); skillValueWidget->eventMouseWheel += MyGUI::newDelegate(this, &StatsWindow::onMouseWheel);
// resize dynamically according to text size
int textWidthPlusMargin = skillValueWidget->getTextSize().width + 12;
skillValueWidget->setCoord(coord2.left + coord2.width - textWidthPlusMargin, coord2.top, textWidthPlusMargin, coord2.height);
skillNameWidget->setSize(skillNameWidget->getSize() + MyGUI::IntSize(coord2.width - textWidthPlusMargin, 0));
mSkillWidgets.push_back(skillNameWidget); mSkillWidgets.push_back(skillNameWidget);
mSkillWidgets.push_back(skillValueWidget); mSkillWidgets.push_back(skillValueWidget);

@ -531,4 +531,10 @@ namespace MWGui
sellerStats.setLastRestockTime(MWBase::Environment::get().getWorld()->getTimeStamp()); sellerStats.setLastRestockTime(MWBase::Environment::get().getWorld()->getTimeStamp());
} }
} }
void TradeWindow::resetReference()
{
ReferenceInterface::resetReference();
mItemView->setModel(NULL);
}
} }

@ -37,6 +37,7 @@ namespace MWGui
virtual void exit(); virtual void exit();
virtual void resetReference();
private: private:
ItemView* mItemView; ItemView* mItemView;

@ -1529,6 +1529,8 @@ namespace MWGui
mCompanionWindow->resetReference(); mCompanionWindow->resetReference();
mConsole->resetReference(); mConsole->resetReference();
mSelectedSpell.clear();
mGuiModes.clear(); mGuiModes.clear();
MWBase::Environment::get().getInputManager()->changeInputMode(false); MWBase::Environment::get().getInputManager()->changeInputMode(false);
updateVisible(); updateVisible();

@ -707,11 +707,13 @@ namespace MWInput
} }
void InputManager::quickLoad() { void InputManager::quickLoad() {
MWBase::Environment::get().getStateManager()->quickLoad(); if (!MyGUI::InputManager::getInstance().isModalAny())
MWBase::Environment::get().getStateManager()->quickLoad();
} }
void InputManager::quickSave() { void InputManager::quickSave() {
MWBase::Environment::get().getStateManager()->quickSave(); if (!MyGUI::InputManager::getInstance().isModalAny())
MWBase::Environment::get().getStateManager()->quickSave();
} }
void InputManager::toggleSpell() void InputManager::toggleSpell()
{ {

@ -265,7 +265,7 @@ namespace MWMechanics
const ESM::Weapon *weapon = NULL; const ESM::Weapon *weapon = NULL;
MWMechanics::WeaponType weaptype; MWMechanics::WeaponType weaptype;
float weapRange; float weapRange = 1.0f;
actorClass.getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, true); actorClass.getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, true);
@ -300,7 +300,7 @@ namespace MWMechanics
else //is creature else //is creature
{ {
weaptype = WeapType_HandToHand; //doesn't matter, should only reflect if it is melee or distant weapon weaptype = WeapType_HandToHand; //doesn't matter, should only reflect if it is melee or distant weapon
weapRange = 150; //TODO: use true attack range (the same problem in Creature::hit) weapRange = 150.0f; //TODO: use true attack range (the same problem in Creature::hit)
} }
float rangeAttack; float rangeAttack;

@ -0,0 +1,232 @@
#include "autocalcspell.hpp"
#include <climits>
#include "../mwworld/esmstore.hpp"
#include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp"
namespace MWMechanics
{
struct SchoolCaps
{
int mCount;
int mLimit;
bool mReachedLimit;
int mMinCost;
std::string mWeakestSpell;
};
std::vector<std::string> autoCalcNpcSpells(const int *actorSkills, const int *actorAttributes, const ESM::Race* race)
{
const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
static const float fNPCbaseMagickaMult = gmst.find("fNPCbaseMagickaMult")->getFloat();
float baseMagicka = fNPCbaseMagickaMult * actorAttributes[ESM::Attribute::Intelligence];
static const std::string schools[] = {
"alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration"
};
static int iAutoSpellSchoolMax[6];
static bool init = false;
if (!init)
{
for (int i=0; i<6; ++i)
{
const std::string& gmstName = "iAutoSpell" + schools[i] + "Max";
iAutoSpellSchoolMax[i] = gmst.find(gmstName)->getInt();
}
init = true;
}
std::map<int, SchoolCaps> schoolCaps;
for (int i=0; i<6; ++i)
{
SchoolCaps caps;
caps.mCount = 0;
caps.mLimit = iAutoSpellSchoolMax[i];
caps.mReachedLimit = iAutoSpellSchoolMax[i] <= 0;
caps.mMinCost = INT_MAX;
caps.mWeakestSpell.clear();
schoolCaps[i] = caps;
}
std::vector<std::string> selectedSpells;
const MWWorld::Store<ESM::Spell> &spells =
MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>();
// Note: the algorithm heavily depends on the traversal order of the spells. For vanilla-compatible results the
// Store must preserve the record ordering as it was in the content files.
for (MWWorld::Store<ESM::Spell>::iterator iter = spells.begin(); iter != spells.end(); ++iter)
{
const ESM::Spell* spell = &*iter;
if (spell->mData.mType != ESM::Spell::ST_Spell)
continue;
if (!(spell->mData.mFlags & ESM::Spell::F_Autocalc))
continue;
static const int iAutoSpellTimesCanCast = gmst.find("iAutoSpellTimesCanCast")->getInt();
if (baseMagicka < iAutoSpellTimesCanCast * spell->mData.mCost)
continue;
if (race && std::find(race->mPowers.mList.begin(), race->mPowers.mList.end(), spell->mId) != race->mPowers.mList.end())
continue;
if (!attrSkillCheck(spell, actorSkills, actorAttributes))
continue;
int school;
float skillTerm;
calcWeakestSchool(spell, actorSkills, school, skillTerm);
assert(school >= 0 && school < 6);
SchoolCaps& cap = schoolCaps[school];
if (cap.mReachedLimit && spell->mData.mCost <= cap.mMinCost)
continue;
static const float fAutoSpellChance = gmst.find("fAutoSpellChance")->getFloat();
if (calcAutoCastChance(spell, actorSkills, actorAttributes, school) < fAutoSpellChance)
continue;
selectedSpells.push_back(spell->mId);
if (cap.mReachedLimit)
{
std::vector<std::string>::iterator found = std::find(selectedSpells.begin(), selectedSpells.end(), cap.mWeakestSpell);
if (found != selectedSpells.end())
selectedSpells.erase(found);
cap.mMinCost = INT_MAX;
for (std::vector<std::string>::iterator weakIt = selectedSpells.begin(); weakIt != selectedSpells.end(); ++weakIt)
{
const ESM::Spell* testSpell = spells.find(*weakIt);
//int testSchool;
//float dummySkillTerm;
//calcWeakestSchool(testSpell, actorSkills, testSchool, dummySkillTerm);
// Note: if there are multiple spells with the same cost, we pick the first one we found.
// So the algorithm depends on the iteration order of the outer loop.
if (
// There is a huge bug here. It is not checked that weakestSpell is of the correct school.
// As result multiple SchoolCaps could have the same mWeakestSpell. Erasing the weakest spell would then fail if another school
// already erased it, and so the number of spells would often exceed the sum of limits.
// This bug cannot be fixed without significantly changing the results of the spell autocalc, which will not have been playtested.
//testSchool == school &&
testSpell->mData.mCost < cap.mMinCost)
{
cap.mMinCost = testSpell->mData.mCost;
cap.mWeakestSpell = testSpell->mId;
}
}
}
else
{
cap.mCount += 1;
if (cap.mCount == cap.mLimit)
cap.mReachedLimit = true;
if (spell->mData.mCost < cap.mMinCost)
{
cap.mWeakestSpell = spell->mId;
cap.mMinCost = spell->mData.mCost;
}
}
}
return selectedSpells;
}
bool attrSkillCheck (const ESM::Spell* spell, const int* actorSkills, const int* actorAttributes)
{
const std::vector<ESM::ENAMstruct>& effects = spell->mEffects.mList;
for (std::vector<ESM::ENAMstruct>::const_iterator effectIt = effects.begin(); effectIt != effects.end(); ++effectIt)
{
const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(effectIt->mEffectID);
static const int iAutoSpellAttSkillMin = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("iAutoSpellAttSkillMin")->getInt();
if ((magicEffect->mData.mFlags & ESM::MagicEffect::TargetSkill))
{
assert (effectIt->mSkill >= 0 && effectIt->mSkill < ESM::Skill::Length);
if (actorSkills[effectIt->mSkill] < iAutoSpellAttSkillMin)
return false;
}
if ((magicEffect->mData.mFlags & ESM::MagicEffect::TargetAttribute))
{
assert (effectIt->mAttribute >= 0 && effectIt->mAttribute < ESM::Attribute::Length);
if (actorAttributes[effectIt->mAttribute] < iAutoSpellAttSkillMin)
return false;
}
}
return true;
}
ESM::Skill::SkillEnum mapSchoolToSkill(int school)
{
std::map<int, ESM::Skill::SkillEnum> schoolSkillMap; // maps spell school to skill id
schoolSkillMap[0] = ESM::Skill::Alteration;
schoolSkillMap[1] = ESM::Skill::Conjuration;
schoolSkillMap[3] = ESM::Skill::Illusion;
schoolSkillMap[2] = ESM::Skill::Destruction;
schoolSkillMap[4] = ESM::Skill::Mysticism;
schoolSkillMap[5] = ESM::Skill::Restoration;
assert(schoolSkillMap.find(school) != schoolSkillMap.end());
return schoolSkillMap[school];
}
void calcWeakestSchool (const ESM::Spell* spell, const int* actorSkills, int& effectiveSchool, float& skillTerm)
{
float minChance = FLT_MAX;
const ESM::EffectList& effects = spell->mEffects;
for (std::vector<ESM::ENAMstruct>::const_iterator it = effects.mList.begin(); it != effects.mList.end(); ++it)
{
const ESM::ENAMstruct& effect = *it;
float x = effect.mDuration;
const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(effect.mEffectID);
if (!(magicEffect->mData.mFlags & ESM::MagicEffect::UncappedDamage))
x = std::max(1.f, x);
x *= 0.1f * magicEffect->mData.mBaseCost;
x *= 0.5f * (effect.mMagnMin + effect.mMagnMax);
x += effect.mArea * 0.05f * magicEffect->mData.mBaseCost;
if (effect.mRange == ESM::RT_Target)
x *= 1.5f;
static const float fEffectCostMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fEffectCostMult")->getFloat();
x *= fEffectCostMult;
float s = 2.f * actorSkills[mapSchoolToSkill(magicEffect->mData.mSchool)];
if (s - x < minChance)
{
minChance = s - x;
effectiveSchool = magicEffect->mData.mSchool;
skillTerm = s;
}
}
}
float calcAutoCastChance(const ESM::Spell *spell, const int *actorSkills, const int *actorAttributes, int effectiveSchool)
{
if (spell->mData.mType != ESM::Spell::ST_Spell)
return 100.f;
if (spell->mData.mFlags & ESM::Spell::F_Always)
return 100.f;
float skillTerm;
if (effectiveSchool != -1)
skillTerm = 2.f * actorSkills[mapSchoolToSkill(effectiveSchool)];
else
calcWeakestSchool(spell, actorSkills, effectiveSchool, skillTerm); // Note effectiveSchool is unused after this
float castChance = skillTerm - spell->mData.mCost + 0.2f * actorAttributes[ESM::Attribute::Willpower] + 0.1f * actorAttributes[ESM::Attribute::Luck];
return castChance;
}
}

@ -0,0 +1,31 @@
#ifndef OPENMW_AUTOCALCSPELL_H
#define OPENMW_AUTOCALCSPELL_H
#include <cfloat>
#include <set>
#include <components/esm/loadspel.hpp>
#include <components/esm/loadskil.hpp>
#include <components/esm/loadrace.hpp>
namespace MWMechanics
{
/// Contains algorithm for calculating an NPC's spells based on stats
/// @note We might want to move this code to a component later, so the editor can use it for preview purposes
std::vector<std::string> autoCalcNpcSpells(const int* actorSkills, const int* actorAttributes, const ESM::Race* race);
// Helpers
bool attrSkillCheck (const ESM::Spell* spell, const int* actorSkills, const int* actorAttributes);
ESM::Skill::SkillEnum mapSchoolToSkill(int school);
void calcWeakestSchool(const ESM::Spell* spell, const int* actorSkills, int& effectiveSchool, float& skillTerm);
float calcAutoCastChance(const ESM::Spell* spell, const int* actorSkills, const int* actorAttributes, int effectiveSchool);
}
#endif

@ -361,9 +361,10 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat
* beginning. */ * beginning. */
int mode = ((movement == mCurrentMovement) ? 2 : 1); int mode = ((movement == mCurrentMovement) ? 2 : 1);
mMovementAnimationControlled = true;
mAnimation->disable(mCurrentMovement); mAnimation->disable(mCurrentMovement);
mCurrentMovement = movement; mCurrentMovement = movement;
mMovementAnimVelocity = 0.0f;
if(!mCurrentMovement.empty()) if(!mCurrentMovement.empty())
{ {
float vel, speedmult = 1.0f; float vel, speedmult = 1.0f;
@ -383,16 +384,18 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat
if(mMovementSpeed > 0.0f && (vel=mAnimation->getVelocity(anim)) > 1.0f) if(mMovementSpeed > 0.0f && (vel=mAnimation->getVelocity(anim)) > 1.0f)
{ {
mMovementAnimVelocity = vel;
speedmult = mMovementSpeed / vel; speedmult = mMovementSpeed / vel;
} }
else if (mMovementState == CharState_TurnLeft || mMovementState == CharState_TurnRight) else if (mMovementState == CharState_TurnLeft || mMovementState == CharState_TurnRight)
speedmult = 1.f; // TODO: should get a speed mult depending on the current turning speed speedmult = 1.f; // TODO: should get a speed mult depending on the current turning speed
else if (mMovementSpeed > 0.0f) else if (mMovementSpeed > 0.0f)
{
// The first person anims don't have any velocity to calculate a speed multiplier from. // The first person anims don't have any velocity to calculate a speed multiplier from.
// We use the third person velocities instead. // We use the third person velocities instead.
// FIXME: should be pulled from the actual animation, but it is not presently loaded. // FIXME: should be pulled from the actual animation, but it is not presently loaded.
speedmult = mMovementSpeed / (isrunning ? 222.857f : 154.064f); speedmult = mMovementSpeed / (isrunning ? 222.857f : 154.064f);
mMovementAnimationControlled = false;
}
mAnimation->play(mCurrentMovement, Priority_Movement, movegroup, false, mAnimation->play(mCurrentMovement, Priority_Movement, movegroup, false,
speedmult, ((mode!=2)?"start":"loop start"), "stop", 0.0f, ~0ul); speedmult, ((mode!=2)?"start":"loop start"), "stop", 0.0f, ~0ul);
} }
@ -506,6 +509,7 @@ void CharacterController::playDeath(float startpoint, CharacterState death)
mJumpState = JumpState_None; mJumpState = JumpState_None;
mAnimation->disable(mCurrentJump); mAnimation->disable(mCurrentJump);
mCurrentJump = ""; mCurrentJump = "";
mMovementAnimationControlled = true;
mAnimation->play(mCurrentDeath, Priority_Death, MWRender::Animation::Group_All, mAnimation->play(mCurrentDeath, Priority_Death, MWRender::Animation::Group_All,
false, 1.0f, "start", "stop", startpoint, 0); false, 1.0f, "start", "stop", startpoint, 0);
@ -547,7 +551,7 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim
, mIdleState(CharState_None) , mIdleState(CharState_None)
, mMovementState(CharState_None) , mMovementState(CharState_None)
, mMovementSpeed(0.0f) , mMovementSpeed(0.0f)
, mMovementAnimVelocity(0.0f) , mMovementAnimationControlled(true)
, mDeathState(CharState_None) , mDeathState(CharState_None)
, mHitState(CharState_None) , mHitState(CharState_None)
, mUpperBodyState(UpperCharState_Nothing) , mUpperBodyState(UpperCharState_Nothing)
@ -570,10 +574,14 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim
if (cls.hasInventoryStore(mPtr)) if (cls.hasInventoryStore(mPtr))
{ {
getActiveWeapon(cls.getCreatureStats(mPtr), cls.getInventoryStore(mPtr), &mWeaponType); getActiveWeapon(cls.getCreatureStats(mPtr), cls.getInventoryStore(mPtr), &mWeaponType);
if(mWeaponType != WeapType_None && mWeaponType != WeapType_Spell && mWeaponType != WeapType_HandToHand) if (mWeaponType != WeapType_None)
{ {
getWeaponGroup(mWeaponType, mCurrentWeapon);
mUpperBodyState = UpperCharState_WeapEquiped; mUpperBodyState = UpperCharState_WeapEquiped;
getWeaponGroup(mWeaponType, mCurrentWeapon);
}
if(mWeaponType != WeapType_None && mWeaponType != WeapType_Spell && mWeaponType != WeapType_HandToHand)
{
mAnimation->showWeapons(true); mAnimation->showWeapons(true);
mAnimation->setWeaponGroup(mCurrentWeapon); mAnimation->setWeaponGroup(mCurrentWeapon);
} }
@ -1241,6 +1249,7 @@ void CharacterController::update(float duration)
if (inwater || flying) if (inwater || flying)
cls.getCreatureStats(mPtr).land(); cls.getCreatureStats(mPtr).land();
bool inJump = true;
if(!onground && !flying && !inwater) if(!onground && !flying && !inwater)
{ {
// In the air (either getting up —ascending part of jump— or falling). // In the air (either getting up —ascending part of jump— or falling).
@ -1330,6 +1339,8 @@ void CharacterController::update(float duration)
mJumpState = JumpState_None; mJumpState = JumpState_None;
vec.z = 0.0f; vec.z = 0.0f;
inJump = false;
if(std::abs(vec.x/2.0f) > std::abs(vec.y)) if(std::abs(vec.x/2.0f) > std::abs(vec.y))
{ {
if(vec.x > 0.0f) if(vec.x > 0.0f)
@ -1391,6 +1402,8 @@ void CharacterController::update(float duration)
forcestateupdate = updateCreatureState() || forcestateupdate; forcestateupdate = updateCreatureState() || forcestateupdate;
refreshCurrentAnims(idlestate, movestate, forcestateupdate); refreshCurrentAnims(idlestate, movestate, forcestateupdate);
if (inJump)
mMovementAnimationControlled = false;
if (!mSkipAnim) if (!mSkipAnim)
{ {
@ -1402,7 +1415,7 @@ void CharacterController::update(float duration)
else //avoid z-rotating for knockdown else //avoid z-rotating for knockdown
world->rotateObject(mPtr, rot.x, rot.y, 0.0f, true); world->rotateObject(mPtr, rot.x, rot.y, 0.0f, true);
if (mMovementAnimVelocity == 0) if (!mMovementAnimationControlled)
world->queueMovement(mPtr, vec); world->queueMovement(mPtr, vec);
} }
else else
@ -1446,7 +1459,7 @@ void CharacterController::update(float duration)
} }
// Update movement // Update movement
if(mMovementAnimVelocity > 0) if(mMovementAnimationControlled && mPtr.getClass().isActor())
world->queueMovement(mPtr, moved); world->queueMovement(mPtr, moved);
} }
else if (mAnimation) else if (mAnimation)

@ -147,7 +147,7 @@ class CharacterController
CharacterState mMovementState; CharacterState mMovementState;
std::string mCurrentMovement; std::string mCurrentMovement;
float mMovementSpeed; float mMovementSpeed;
float mMovementAnimVelocity; bool mMovementAnimationControlled;
CharacterState mDeathState; CharacterState mDeathState;
std::string mCurrentDeath; std::string mCurrentDeath;

@ -19,6 +19,7 @@
#include <OgreSceneNode.h> #include <OgreSceneNode.h>
#include "spellcasting.hpp" #include "spellcasting.hpp"
#include "autocalcspell.hpp"
namespace namespace
{ {
@ -155,19 +156,6 @@ namespace MWMechanics
npcStats.getSkill (index).setBase ( npcStats.getSkill (index).setBase (
npcStats.getSkill (index).getBase() + bonus); npcStats.getSkill (index).getBase() + bonus);
} }
if (i==1)
{
// Major skill - add starting spells for this skill if existing
const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();
MWWorld::Store<ESM::Spell>::iterator it = store.get<ESM::Spell>().begin();
for (; it != store.get<ESM::Spell>().end(); ++it)
{
if (it->mData.mFlags & ESM::Spell::F_PCStart
&& spellSchoolToSkill(getSpellSchool(&*it, ptr)) == index)
creatureStats.getSpells().add(it->mId);
}
}
} }
} }
@ -190,6 +178,87 @@ namespace MWMechanics
} }
} }
// F_PCStart spells
static const float fPCbaseMagickaMult = esmStore.get<ESM::GameSetting>().find("fPCbaseMagickaMult")->getFloat();
float baseMagicka = fPCbaseMagickaMult * creatureStats.getAttribute(ESM::Attribute::Intelligence).getBase();
bool reachedLimit = false;
const ESM::Spell* weakestSpell = NULL;
int minCost = INT_MAX;
std::vector<std::string> selectedSpells;
const ESM::Race* race = NULL;
if (mRaceSelected)
race = esmStore.get<ESM::Race>().find(player->mRace);
int skills[ESM::Skill::Length];
for (int i=0; i<ESM::Skill::Length; ++i)
skills[i] = npcStats.getSkill(i).getBase();
int attributes[ESM::Attribute::Length];
for (int i=0; i<ESM::Attribute::Length; ++i)
attributes[i] = npcStats.getAttribute(i).getBase();
const MWWorld::Store<ESM::Spell> &spells =
esmStore.get<ESM::Spell>();
for (MWWorld::Store<ESM::Spell>::iterator iter = spells.begin(); iter != spells.end(); ++iter)
{
const ESM::Spell* spell = &*iter;
if (spell->mData.mType != ESM::Spell::ST_Spell)
continue;
if (!(spell->mData.mFlags & ESM::Spell::F_PCStart))
continue;
if (reachedLimit && spell->mData.mCost <= minCost)
continue;
if (race && std::find(race->mPowers.mList.begin(), race->mPowers.mList.end(), spell->mId) != race->mPowers.mList.end())
continue;
if (baseMagicka < spell->mData.mCost)
continue;
static const float fAutoPCSpellChance = esmStore.get<ESM::GameSetting>().find("fAutoPCSpellChance")->getFloat();
if (calcAutoCastChance(spell, skills, attributes, -1) < fAutoPCSpellChance)
continue;
if (!attrSkillCheck(spell, skills, attributes))
continue;
selectedSpells.push_back(spell->mId);
if (reachedLimit)
{
std::vector<std::string>::iterator it = std::find(selectedSpells.begin(), selectedSpells.end(), weakestSpell->mId);
if (it != selectedSpells.end())
selectedSpells.erase(it);
minCost = INT_MAX;
for (std::vector<std::string>::iterator weakIt = selectedSpells.begin(); weakIt != selectedSpells.end(); ++weakIt)
{
const ESM::Spell* testSpell = esmStore.get<ESM::Spell>().find(*weakIt);
if (testSpell->mData.mCost < minCost)
{
minCost = testSpell->mData.mCost;
weakestSpell = testSpell;
}
}
}
else
{
if (spell->mData.mCost < minCost)
{
weakestSpell = spell;
minCost = weakestSpell->mData.mCost;
}
static const unsigned int iAutoPCSpellMax = esmStore.get<ESM::GameSetting>().find("iAutoPCSpellMax")->getInt();
if (selectedSpells.size() == iAutoPCSpellMax)
reachedLimit = true;
}
}
for (std::vector<std::string>::iterator it = selectedSpells.begin(); it != selectedSpells.end(); ++it)
creatureStats.getSpells().add(*it);
// forced update and current value adjustments // forced update and current value adjustments
mActors.updateActor (ptr, 0); mActors.updateActor (ptr, 0);

@ -92,7 +92,7 @@ namespace MWMechanics
x *= 0.1 * magicEffect->mData.mBaseCost; x *= 0.1 * magicEffect->mData.mBaseCost;
x *= 0.5 * (it->mMagnMin + it->mMagnMax); x *= 0.5 * (it->mMagnMin + it->mMagnMax);
x *= it->mArea * 0.05 * magicEffect->mData.mBaseCost; x *= it->mArea * 0.05 * magicEffect->mData.mBaseCost;
if (magicEffect->mData.mFlags & ESM::MagicEffect::CastTarget) if (it->mRange == ESM::RT_Target)
x *= 1.5; x *= 1.5;
static const float fEffectCostMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find( static const float fEffectCostMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(
"fEffectCostMult")->getFloat(); "fEffectCostMult")->getFloat();

@ -149,6 +149,8 @@ namespace MWRender
mViewModeToggleQueued = true; mViewModeToggleQueued = true;
return; return;
} }
else
mViewModeToggleQueued = false;
mFirstPersonView = !mFirstPersonView; mFirstPersonView = !mFirstPersonView;
processViewChange(); processViewChange();

@ -1088,7 +1088,7 @@ public:
void close() { } void close() { }
bool update(Ogre::MaterialPtr &mat, Ogre::Rectangle2D *rect, int screen_width, int screen_height) bool update()
{ return false; } { return false; }
}; };

@ -86,16 +86,24 @@ namespace MWScript
float ay = Ogre::Radian(ptr.getRefData().getPosition().rot[1]).valueDegrees(); float ay = Ogre::Radian(ptr.getRefData().getPosition().rot[1]).valueDegrees();
float az = Ogre::Radian(ptr.getRefData().getPosition().rot[2]).valueDegrees(); float az = Ogre::Radian(ptr.getRefData().getPosition().rot[2]).valueDegrees();
MWWorld::LocalRotation localRot = ptr.getRefData().getLocalRotation();
if (axis == "x") if (axis == "x")
{ {
localRot.rot[0] = 0;
ptr.getRefData().setLocalRotation(localRot);
MWBase::Environment::get().getWorld()->rotateObject(ptr,angle,ay,az); MWBase::Environment::get().getWorld()->rotateObject(ptr,angle,ay,az);
} }
else if (axis == "y") else if (axis == "y")
{ {
localRot.rot[1] = 0;
ptr.getRefData().setLocalRotation(localRot);
MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,angle,az); MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,angle,az);
} }
else if (axis == "z") else if (axis == "z")
{ {
localRot.rot[2] = 0;
ptr.getRefData().setLocalRotation(localRot);
MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay,angle); MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay,angle);
} }
else else

@ -364,7 +364,7 @@ namespace MWWorld
continue; // velocity updated, calculate nextpos again continue; // velocity updated, calculate nextpos again
} }
if(newPosition.squaredDistance(nextpos) > 0.00000001*0.00000001) if(newPosition.squaredDistance(nextpos) > 0.0001)
{ {
// trace to where character would go if there were no obstructions // trace to where character would go if there were no obstructions
tracer.doTrace(colobj, newPosition, nextpos, engine); tracer.doTrace(colobj, newPosition, nextpos, engine);

@ -209,8 +209,6 @@ namespace MWWorld
} }
void setUp() { void setUp() {
//std::sort(mStatic.begin(), mStatic.end(), RecordCmp());
mShared.clear(); mShared.clear();
mShared.reserve(mStatic.size()); mShared.reserve(mStatic.size());
typename std::map<std::string, T>::iterator it = mStatic.begin(); typename std::map<std::string, T>::iterator it = mStatic.begin();
@ -675,18 +673,15 @@ namespace MWWorld
} }
void setUp() { void setUp() {
//typedef std::vector<ESM::Cell>::iterator Iterator;
typedef DynamicExt::iterator ExtIterator; typedef DynamicExt::iterator ExtIterator;
typedef std::map<std::string, ESM::Cell>::iterator IntIterator; typedef std::map<std::string, ESM::Cell>::iterator IntIterator;
//std::sort(mInt.begin(), mInt.end(), RecordCmp());
mSharedInt.clear(); mSharedInt.clear();
mSharedInt.reserve(mInt.size()); mSharedInt.reserve(mInt.size());
for (IntIterator it = mInt.begin(); it != mInt.end(); ++it) { for (IntIterator it = mInt.begin(); it != mInt.end(); ++it) {
mSharedInt.push_back(&(it->second)); mSharedInt.push_back(&(it->second));
} }
//std::sort(mExt.begin(), mExt.end(), ExtCmp());
mSharedExt.clear(); mSharedExt.clear();
mSharedExt.reserve(mExt.size()); mSharedExt.reserve(mExt.size());
for (ExtIterator it = mExt.begin(); it != mExt.end(); ++it) { for (ExtIterator it = mExt.begin(); it != mExt.end(); ++it) {
@ -1147,6 +1142,37 @@ namespace MWWorld
} }
}; };
// Specialisation for ESM::Spell to preserve record order as it was in the content files.
// The NPC spell autocalc code heavily depends on this order.
// We could also do this in the base class, but it's usually not a good idea to depend on record order.
template<>
inline void Store<ESM::Spell>::clearDynamic()
{
// remove the dynamic part of mShared
mShared.erase(mShared.begin() + mStatic.size(), mShared.end());
mDynamic.clear();
}
template<>
inline void Store<ESM::Spell>::load(ESM::ESMReader &esm, const std::string &id) {
std::string idLower = Misc::StringUtils::lowerCase(id);
std::pair<Static::iterator, bool> inserted = mStatic.insert(std::make_pair(idLower, ESM::Spell()));
if (inserted.second)
mShared.push_back(&mStatic[idLower]);
inserted.first->second.mId = idLower;
inserted.first->second.load(esm);
}
template<>
inline void Store<ESM::Spell>::setUp()
{
// remove the dynamic part of mShared
mShared.erase(mShared.begin() + mStatic.size(), mShared.end());
}
} //end namespace } //end namespace
#endif #endif

@ -1123,7 +1123,10 @@ namespace MWWorld
ptr.getRefData().setPosition(pos); ptr.getRefData().setPosition(pos);
mWorldScene->updateObjectRotation(ptr); if (ptr.getClass().isActor())
mWorldScene->updateObjectRotation(ptr);
else
mWorldScene->updateObjectLocalRotation(ptr);
} }
void World::localRotateObject (const Ptr& ptr, float x, float y, float z) void World::localRotateObject (const Ptr& ptr, float x, float y, float z)
@ -2313,15 +2316,10 @@ namespace MWWorld
} }
// If this is a power, check if it was already used in the last 24h // If this is a power, check if it was already used in the last 24h
if (!fail && spell->mData.mType == ESM::Spell::ST_Power) if (!fail && spell->mData.mType == ESM::Spell::ST_Power && !stats.getSpells().canUsePower(spell->mId))
{ {
if (stats.getSpells().canUsePower(spell->mId)) message = "#{sPowerAlreadyUsed}";
stats.getSpells().usePower(spell->mId); fail = true;
else
{
message = "#{sPowerAlreadyUsed}";
fail = true;
}
} }
// Reduce mana // Reduce mana
@ -2354,6 +2352,10 @@ namespace MWWorld
if (!selectedSpell.empty()) if (!selectedSpell.empty())
{ {
const ESM::Spell* spell = getStore().get<ESM::Spell>().search(selectedSpell); const ESM::Spell* spell = getStore().get<ESM::Spell>().search(selectedSpell);
// A power can be used once per 24h
if (spell->mData.mType == ESM::Spell::ST_Power)
stats.getSpells().usePower(spell->mId);
cast.cast(spell); cast.cast(spell);
} }
@ -2649,6 +2651,8 @@ namespace MWWorld
{ {
mGoToJail = false; mGoToJail = false;
MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Dialogue);
MWWorld::Ptr player = getPlayerPtr(); MWWorld::Ptr player = getPlayerPtr();
teleportToClosestMarker(player, "prisonmarker"); teleportToClosestMarker(player, "prisonmarker");
int bounty = player.getClass().getNpcStats(player).getBounty(); int bounty = player.getClass().getNpcStats(player).getBounty();

@ -12,68 +12,3 @@ struct StringOpsTest : public ::testing::Test
{ {
} }
}; };
TEST_F(StringOpsTest, begins_matching)
{
ASSERT_TRUE(Misc::begins("abc", "a"));
ASSERT_TRUE(Misc::begins("abc", "ab"));
ASSERT_TRUE(Misc::begins("abc", "abc"));
ASSERT_TRUE(Misc::begins("abcd", "abc"));
}
TEST_F(StringOpsTest, begins_not_matching)
{
ASSERT_FALSE(Misc::begins("abc", "b"));
ASSERT_FALSE(Misc::begins("abc", "bc"));
ASSERT_FALSE(Misc::begins("abc", "bcd"));
ASSERT_FALSE(Misc::begins("abc", "abcd"));
}
TEST_F(StringOpsTest, ibegins_matching)
{
ASSERT_TRUE(Misc::ibegins("Abc", "a"));
ASSERT_TRUE(Misc::ibegins("aBc", "ab"));
ASSERT_TRUE(Misc::ibegins("abC", "abc"));
ASSERT_TRUE(Misc::ibegins("abcD", "abc"));
}
TEST_F(StringOpsTest, ibegins_not_matching)
{
ASSERT_FALSE(Misc::ibegins("abc", "b"));
ASSERT_FALSE(Misc::ibegins("abc", "bc"));
ASSERT_FALSE(Misc::ibegins("abc", "bcd"));
ASSERT_FALSE(Misc::ibegins("abc", "abcd"));
}
TEST_F(StringOpsTest, ends_matching)
{
ASSERT_TRUE(Misc::ends("abc", "c"));
ASSERT_TRUE(Misc::ends("abc", "bc"));
ASSERT_TRUE(Misc::ends("abc", "abc"));
ASSERT_TRUE(Misc::ends("abcd", "abcd"));
}
TEST_F(StringOpsTest, ends_not_matching)
{
ASSERT_FALSE(Misc::ends("abc", "b"));
ASSERT_FALSE(Misc::ends("abc", "ab"));
ASSERT_FALSE(Misc::ends("abc", "bcd"));
ASSERT_FALSE(Misc::ends("abc", "abcd"));
}
TEST_F(StringOpsTest, iends_matching)
{
ASSERT_TRUE(Misc::iends("Abc", "c"));
ASSERT_TRUE(Misc::iends("aBc", "bc"));
ASSERT_TRUE(Misc::iends("abC", "abc"));
ASSERT_TRUE(Misc::iends("abcD", "abcd"));
}
TEST_F(StringOpsTest, iends_not_matching)
{
ASSERT_FALSE(Misc::iends("abc", "b"));
ASSERT_FALSE(Misc::iends("abc", "ab"));
ASSERT_FALSE(Misc::iends("abc", "bcd"));
ASSERT_FALSE(Misc::iends("abc", "abcd"));
}

@ -10,8 +10,6 @@ namespace ESM
void NPC::load(ESMReader &esm) void NPC::load(ESMReader &esm)
{ {
//mNpdt52.mGold = -10;
mPersistent = esm.getRecordFlags() & 0x0400; mPersistent = esm.getRecordFlags() & 0x0400;
mModel = esm.getHNOString("MODL"); mModel = esm.getHNOString("MODL");
@ -63,7 +61,6 @@ void NPC::load(ESMReader &esm)
} }
} }
mAiPackage.load(esm); mAiPackage.load(esm);
esm.skipRecord();
} }
void NPC::save(ESMWriter &esm) const void NPC::save(ESMWriter &esm) const
{ {

@ -27,8 +27,8 @@ struct Spell
enum Flags enum Flags
{ {
F_Autocalc = 1, F_Autocalc = 1, // Can be selected by NPC spells auto-calc
F_PCStart = 2, F_PCStart = 2, // Can be selected by player spells auto-calc
F_Always = 4 // Casting always succeeds F_Always = 4 // Casting always succeeds
}; };

@ -12,59 +12,6 @@
namespace Misc namespace Misc
{ {
bool begins(const char* str1, const char* str2) std::locale StringUtils::mLocale = std::locale::classic();
{
while(*str2)
{
if(*str1 == 0 || *str1 != *str2) return false;
str1++;
str2++;
}
return true;
}
bool ends(const char* str1, const char* str2)
{
int len1 = strlen(str1);
int len2 = strlen(str2);
if(len1 < len2) return false;
return strcmp(str2, str1+len1-len2) == 0;
}
// True if the given chars match, case insensitive
static bool icmp(char a, char b)
{
if(a >= 'A' && a <= 'Z')
a += 'a' - 'A';
if(b >= 'A' && b <= 'Z')
b += 'a' - 'A';
return a == b;
}
bool ibegins(const char* str1, const char* str2)
{
while(*str2)
{
if(*str1 == 0 || !icmp(*str1,*str2)) return false;
str1++;
str2++;
}
return true;
}
bool iends(const char* str1, const char* str2)
{
int len1 = strlen(str1);
int len2 = strlen(str2);
if(len1 < len2) return false;
return strcasecmp(str2, str1+len1-len2) == 0;
}
} }

@ -4,15 +4,18 @@
#include <cctype> #include <cctype>
#include <string> #include <string>
#include <algorithm> #include <algorithm>
#include <locale>
namespace Misc namespace Misc
{ {
class StringUtils class StringUtils
{ {
static std::locale mLocale;
struct ci struct ci
{ {
bool operator()(int x, int y) const { bool operator()(char x, char y) const {
return std::tolower(x) < std::tolower(y); return std::tolower(x, StringUtils::mLocale) < std::tolower(y, StringUtils::mLocale);
} }
}; };
@ -28,7 +31,7 @@ public:
std::string::const_iterator xit = x.begin(); std::string::const_iterator xit = x.begin();
std::string::const_iterator yit = y.begin(); std::string::const_iterator yit = y.begin();
for (; xit != x.end(); ++xit, ++yit) { for (; xit != x.end(); ++xit, ++yit) {
if (std::tolower(*xit) != std::tolower(*yit)) { if (std::tolower(*xit, mLocale) != std::tolower(*yit, mLocale)) {
return false; return false;
} }
} }
@ -42,7 +45,7 @@ public:
for(;xit != x.end() && yit != y.end() && len > 0;++xit,++yit,--len) for(;xit != x.end() && yit != y.end() && len > 0;++xit,++yit,--len)
{ {
int res = *xit - *yit; int res = *xit - *yit;
if(res != 0 && std::tolower(*xit) != std::tolower(*yit)) if(res != 0 && std::tolower(*xit, mLocale) != std::tolower(*yit, mLocale))
return (res > 0) ? 1 : -1; return (res > 0) ? 1 : -1;
} }
if(len > 0) if(len > 0)
@ -57,12 +60,8 @@ public:
/// Transforms input string to lower case w/o copy /// Transforms input string to lower case w/o copy
static std::string &toLower(std::string &inout) { static std::string &toLower(std::string &inout) {
std::transform( for (unsigned int i=0; i<inout.size(); ++i)
inout.begin(), inout[i] = std::tolower(inout[i], mLocale);
inout.end(),
inout.begin(),
(int (*)(int)) std::tolower
);
return inout; return inout;
} }
@ -74,19 +73,6 @@ public:
} }
}; };
/// Returns true if str1 begins with substring str2
bool begins(const char* str1, const char* str2);
/// Returns true if str1 ends with substring str2
bool ends(const char* str1, const char* str2);
/// Case insensitive, returns true if str1 begins with substring str2
bool ibegins(const char* str1, const char* str2);
/// Case insensitive, returns true if str1 ends with substring str2
bool iends(const char* str1, const char* str2);
} }
#endif #endif

@ -267,10 +267,11 @@ namespace sh
MaterialInstance* Factory::searchInstance (const std::string& name) MaterialInstance* Factory::searchInstance (const std::string& name)
{ {
if (mMaterials.find(name) != mMaterials.end()) MaterialMap::iterator it = mMaterials.find(name);
return &mMaterials.find(name)->second; if (it != mMaterials.end())
return &(it->second);
return NULL; else
return NULL;
} }
MaterialInstance* Factory::findInstance (const std::string& name) MaterialInstance* Factory::findInstance (const std::string& name)
@ -434,8 +435,9 @@ namespace sh
std::string Factory::retrieveTextureAlias (const std::string& name) std::string Factory::retrieveTextureAlias (const std::string& name)
{ {
if (mTextureAliases.find(name) != mTextureAliases.end()) TextureAliasMap::iterator it = mTextureAliases.find(name);
return mTextureAliases[name]; if (it != mTextureAliases.end())
return it->second;
else else
return ""; return "";
} }

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save