Merge pull request #280 from OpenMW/master while resolving conflicts

# Conflicts:
#	.gitignore
#	apps/openmw/mwmechanics/actors.hpp
#	apps/openmw/mwmechanics/mechanicsmanagerimp.cpp
pull/281/head^2
David Cernat 7 years ago
commit 4468e6ec4a

@ -0,0 +1,11 @@
root = true
[*.cpp]
indent_style = space
indent_size = 4
insert_final_newline = true
[*.hpp]
indent_style = space
indent_size = 4
insert_final_newline = true

1
.gitignore vendored

@ -26,6 +26,7 @@ Doxygen
.settings .settings
.directory .directory
.idea .idea
files/windows/*.aps
cmake-build-* cmake-build-*
## qt-creator ## qt-creator
CMakeLists.txt.user* CMakeLists.txt.user*

@ -60,10 +60,11 @@ Programmers
Gašper Sedej Gašper Sedej
gugus/gus gugus/gus
Hallfaer Tuilinn Hallfaer Tuilinn
Haoda Wang (h313)
hristoast hristoast
Internecine Internecine
Jacob Essex (Yacoby) Jacob Essex (Yacoby)
Jannik Heller (scrawl) Jake Westrip (16bitint)
Jason Hooks (jhooks) Jason Hooks (jhooks)
jeaye jeaye
Jeffrey Haines (Jyby) Jeffrey Haines (Jyby)
@ -132,12 +133,14 @@ Programmers
Roman Proskuryakov (kpp) Roman Proskuryakov (kpp)
Sandy Carter (bwrsandman) Sandy Carter (bwrsandman)
Scott Howard Scott Howard
scrawl
Sebastian Wick (swick) Sebastian Wick (swick)
Sergey Shambir Sergey Shambir
ShadowRadiance ShadowRadiance
Siimacore Siimacore
sir_herrbatka sir_herrbatka
smbas smbas
spycrab
Stefan Galowicz (bogglez) Stefan Galowicz (bogglez)
Stanislav Bobrov (Jiub) Stanislav Bobrov (Jiub)
stil-t stil-t

@ -172,7 +172,10 @@ Launcher::FirstRunDialogResult Launcher::MainDialog::showFirstRunDialog()
} }
} }
return setup() ? FirstRunDialogResultContinue : FirstRunDialogResultFailure; if (!setup() || !setupGameData()) {
return FirstRunDialogResultFailure;
}
return FirstRunDialogResultContinue;
} }
void Launcher::MainDialog::setVersionLabel() void Launcher::MainDialog::setVersionLabel()
@ -344,6 +347,11 @@ bool Launcher::MainDialog::setupGameSettings()
file.close(); file.close();
} }
return true;
}
bool Launcher::MainDialog::setupGameData()
{
QStringList dataDirs; QStringList dataDirs;
// Check if the paths actually contain data files // Check if the paths actually contain data files

@ -72,6 +72,7 @@ namespace Launcher
bool setupLauncherSettings(); bool setupLauncherSettings();
bool setupGameSettings(); bool setupGameSettings();
bool setupGraphicsSettings(); bool setupGraphicsSettings();
bool setupGameData();
void setVersionLabel(); void setVersionLabel();

@ -5,9 +5,6 @@
#include <QLocalSocket> #include <QLocalSocket>
#include <QMessageBox> #include <QMessageBox>
#include <components/vfs/manager.hpp>
#include <components/vfs/registerarchives.hpp>
#include <components/fallback/validate.hpp> #include <components/fallback/validate.hpp>
#include <components/nifosg/nifloader.hpp> #include <components/nifosg/nifloader.hpp>
@ -33,11 +30,7 @@ CS::Editor::Editor ()
NifOsg::Loader::setShowMarkers(true); NifOsg::Loader::setShowMarkers(true);
mVFS.reset(new VFS::Manager(mFsStrict)); mDocumentManager.setFileData(mFsStrict, config.first, config.second);
VFS::registerArchives(mVFS.get(), Files::Collections(config.first, !mFsStrict), config.second, true);
mDocumentManager.setVFS(mVFS.get());
mNewGame.setLocalData (mLocal); mNewGame.setLocalData (mLocal);
mFileDialog.setLocalData (mLocal); mFileDialog.setLocalData (mLocal);

@ -1,8 +1,6 @@
#ifndef CS_EDITOR_H #ifndef CS_EDITOR_H
#define CS_EDITOR_H #define CS_EDITOR_H
#include <memory>
#include <boost/interprocess/sync/file_lock.hpp> #include <boost/interprocess/sync/file_lock.hpp>
#include <boost/filesystem/fstream.hpp> #include <boost/filesystem/fstream.hpp>
@ -30,11 +28,6 @@
#include "view/tools/merge.hpp" #include "view/tools/merge.hpp"
namespace VFS
{
class Manager;
}
namespace CSMDoc namespace CSMDoc
{ {
class Document; class Document;
@ -46,9 +39,6 @@ namespace CS
{ {
Q_OBJECT Q_OBJECT
// FIXME: should be moved to document, so we can have different resources for each opened project
std::unique_ptr<VFS::Manager> mVFS;
Files::ConfigurationManager mCfgMgr; Files::ConfigurationManager mCfgMgr;
CSMPrefs::State mSettingsState; CSMPrefs::State mSettingsState;
CSMDoc::DocumentManager mDocumentManager; CSMDoc::DocumentManager mDocumentManager;

@ -269,13 +269,14 @@ void CSMDoc::Document::createBase()
} }
} }
CSMDoc::Document::Document (const VFS::Manager* vfs, 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,
const Fallback::Map* fallback, const Fallback::Map* fallback,
ToUTF8::FromType encoding, const CSMWorld::ResourcesManager& resourcesManager, ToUTF8::FromType encoding,
const std::vector<std::string>& blacklistedScripts) const std::vector<std::string>& blacklistedScripts,
: mVFS(vfs), mSavePath (savePath), mContentFiles (files), mNew (new_), mData (encoding, resourcesManager, fallback, resDir), bool fsStrict, const Files::PathContainer& dataPaths, const std::vector<std::string>& archives)
: mSavePath (savePath), mContentFiles (files), mNew (new_), mData (encoding, fsStrict, dataPaths, archives, fallback, resDir),
mTools (*this, encoding), mTools (*this, encoding),
mProjectPath ((configuration.getUserDataPath() / "projects") / mProjectPath ((configuration.getUserDataPath() / "projects") /
(savePath.filename().string() + ".project")), (savePath.filename().string() + ".project")),
@ -337,11 +338,6 @@ CSMDoc::Document::~Document()
{ {
} }
const VFS::Manager *CSMDoc::Document::getVFS() const
{
return mVFS;
}
QUndoStack& CSMDoc::Document::getUndoStack() QUndoStack& CSMDoc::Document::getUndoStack()
{ {
return mUndoStack; return mUndoStack;

@ -9,6 +9,7 @@
#include <QObject> #include <QObject>
#include <QTimer> #include <QTimer>
#include <components/files/multidircollection.hpp>
#include <components/to_utf8/to_utf8.hpp> #include <components/to_utf8/to_utf8.hpp>
#include "../world/data.hpp" #include "../world/data.hpp"
@ -59,7 +60,6 @@ namespace CSMDoc
private: private:
const VFS::Manager* mVFS;
boost::filesystem::path mSavePath; boost::filesystem::path mSavePath;
std::vector<boost::filesystem::path> mContentFiles; std::vector<boost::filesystem::path> mContentFiles;
bool mNew; bool mNew;
@ -102,17 +102,15 @@ namespace CSMDoc
public: public:
Document (const VFS::Manager* vfs, 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,
const Fallback::Map* fallback, const Fallback::Map* fallback, ToUTF8::FromType encoding,
ToUTF8::FromType encoding, const CSMWorld::ResourcesManager& resourcesManager, const std::vector<std::string>& blacklistedScripts,
const std::vector<std::string>& blacklistedScripts); bool fsStrict, const Files::PathContainer& dataPaths, const std::vector<std::string>& archives);
~Document(); ~Document();
const VFS::Manager* getVFS() const;
QUndoStack& getUndoStack(); QUndoStack& getUndoStack();
int getState() const; int getState() const;

@ -9,7 +9,7 @@
#include "document.hpp" #include "document.hpp"
CSMDoc::DocumentManager::DocumentManager (const Files::ConfigurationManager& configuration) CSMDoc::DocumentManager::DocumentManager (const Files::ConfigurationManager& configuration)
: mConfiguration (configuration), mEncoding (ToUTF8::WINDOWS_1252), mVFS(NULL) : mConfiguration (configuration), mEncoding (ToUTF8::WINDOWS_1252)
{ {
boost::filesystem::path projectPath = configuration.getUserDataPath() / "projects"; boost::filesystem::path projectPath = configuration.getUserDataPath() / "projects";
@ -62,7 +62,7 @@ CSMDoc::Document *CSMDoc::DocumentManager::makeDocument (
const std::vector< boost::filesystem::path >& files, const std::vector< boost::filesystem::path >& files,
const boost::filesystem::path& savePath, bool new_) const boost::filesystem::path& savePath, bool new_)
{ {
return new Document (mVFS, mConfiguration, files, new_, savePath, mResDir, &mFallbackMap, mEncoding, mResourcesManager, mBlacklistedScripts); return new Document (mConfiguration, files, new_, savePath, mResDir, &mFallbackMap, mEncoding, mBlacklistedScripts, mFsStrict, mDataPaths, mArchives);
} }
void CSMDoc::DocumentManager::insertDocument (CSMDoc::Document *document) void CSMDoc::DocumentManager::insertDocument (CSMDoc::Document *document)
@ -127,8 +127,9 @@ void CSMDoc::DocumentManager::documentNotLoaded (Document *document, const std::
removeDocument (document); removeDocument (document);
} }
void CSMDoc::DocumentManager::setVFS(const VFS::Manager *vfs) void CSMDoc::DocumentManager::setFileData(bool strict, const Files::PathContainer& dataPaths, const std::vector<std::string>& archives)
{ {
mResourcesManager.setVFS(vfs); mFsStrict = strict;
mVFS = vfs; mDataPaths = dataPaths;
mArchives = archives;
} }

@ -11,8 +11,7 @@
#include <components/to_utf8/to_utf8.hpp> #include <components/to_utf8/to_utf8.hpp>
#include <components/fallback/fallback.hpp> #include <components/fallback/fallback.hpp>
#include <components/files/multidircollection.hpp>
#include "../world/resourcesmanager.hpp"
#include "loader.hpp" #include "loader.hpp"
@ -39,9 +38,14 @@ namespace CSMDoc
QThread mLoaderThread; QThread mLoaderThread;
Loader mLoader; Loader mLoader;
ToUTF8::FromType mEncoding; ToUTF8::FromType mEncoding;
CSMWorld::ResourcesManager mResourcesManager;
std::vector<std::string> mBlacklistedScripts; std::vector<std::string> mBlacklistedScripts;
const VFS::Manager* mVFS;
boost::filesystem::path mResDir;
Fallback::Map mFallbackMap;
bool mFsStrict;
Files::PathContainer mDataPaths;
std::vector<std::string> mArchives;
DocumentManager (const DocumentManager&); DocumentManager (const DocumentManager&);
DocumentManager& operator= (const DocumentManager&); DocumentManager& operator= (const DocumentManager&);
@ -74,15 +78,11 @@ namespace CSMDoc
void setBlacklistedScripts (const std::vector<std::string>& scriptIds); void setBlacklistedScripts (const std::vector<std::string>& scriptIds);
void setVFS(const VFS::Manager* vfs); /// Sets the file data that gets passed to newly created documents.
void setFileData(bool strict, const Files::PathContainer& dataPaths, const std::vector<std::string>& archives);
bool isEmpty(); bool isEmpty();
private:
boost::filesystem::path mResDir;
Fallback::Map mFallbackMap;
private slots: private slots:
void documentLoaded (Document *document); void documentLoaded (Document *document);

@ -259,6 +259,7 @@ void CSMPrefs::State::declare()
declareShortcut ("document-character-topicinfos", "Open Topic Info List", QKeySequence()); declareShortcut ("document-character-topicinfos", "Open Topic Info List", QKeySequence());
declareShortcut ("document-character-journalinfos", "Open Journal Info List", QKeySequence()); declareShortcut ("document-character-journalinfos", "Open Journal Info List", QKeySequence());
declareShortcut ("document-character-bodyparts", "Open Body Part List", QKeySequence()); declareShortcut ("document-character-bodyparts", "Open Body Part List", QKeySequence());
declareShortcut ("document-assets-reload", "Reload Assets", QKeySequence(Qt::Key_F5));
declareShortcut ("document-assets-sounds", "Open Sound Asset List", QKeySequence()); declareShortcut ("document-assets-sounds", "Open Sound Asset List", QKeySequence());
declareShortcut ("document-assets-soundgens", "Open Sound Generator List", QKeySequence()); declareShortcut ("document-assets-soundgens", "Open Sound Generator List", QKeySequence());
declareShortcut ("document-assets-meshes", "Open Mesh Asset List", QKeySequence()); declareShortcut ("document-assets-meshes", "Open Mesh Asset List", QKeySequence());

@ -11,6 +11,8 @@
#include <components/esm/cellref.hpp> #include <components/esm/cellref.hpp>
#include <components/resource/scenemanager.hpp> #include <components/resource/scenemanager.hpp>
#include <components/vfs/manager.hpp>
#include <components/vfs/registerarchives.hpp>
#include "idtable.hpp" #include "idtable.hpp"
#include "idtree.hpp" #include "idtree.hpp"
@ -61,11 +63,18 @@ int CSMWorld::Data::count (RecordBase::State state, const CollectionBase& collec
return number; return number;
} }
CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourcesManager, const Fallback::Map* fallback, const boost::filesystem::path& resDir) CSMWorld::Data::Data (ToUTF8::FromType encoding, bool fsStrict, const Files::PathContainer& dataPaths,
const std::vector<std::string>& archives, const Fallback::Map* fallback, const boost::filesystem::path& resDir)
: mEncoder (encoding), mPathgrids (mCells), mRefs (mCells), : mEncoder (encoding), mPathgrids (mCells), mRefs (mCells),
mResourcesManager (resourcesManager), mFallbackMap(fallback), mFallbackMap(fallback), mReader (0), mDialogue (0), mReaderIndex(1),
mReader (0), mDialogue (0), mReaderIndex(1), mResourceSystem(new Resource::ResourceSystem(resourcesManager.getVFS())) mFsStrict(fsStrict), mDataPaths(dataPaths), mArchives(archives)
{ {
mVFS.reset(new VFS::Manager(mFsStrict));
VFS::registerArchives(mVFS.get(), Files::Collections(mDataPaths, !mFsStrict), mArchives, true);
mResourcesManager.setVFS(mVFS.get());
mResourceSystem.reset(new Resource::ResourceSystem(mVFS.get()));
mResourceSystem->getSceneManager()->setShaderPath((resDir / "shaders").string()); mResourceSystem->getSceneManager()->setShaderPath((resDir / "shaders").string());
int index = 0; int index = 0;
@ -1215,6 +1224,43 @@ std::vector<std::string> CSMWorld::Data::getIds (bool listDeleted) const
return ids; return ids;
} }
void CSMWorld::Data::assetsChanged()
{
mVFS.get()->reset();
VFS::registerArchives(mVFS.get(), Files::Collections(mDataPaths, !mFsStrict), mArchives, true);
const UniversalId assetTableIds[] = {
UniversalId::Type_Meshes,
UniversalId::Type_Icons,
UniversalId::Type_Musics,
UniversalId::Type_SoundsRes,
UniversalId::Type_Textures,
UniversalId::Type_Videos
};
size_t numAssetTables = sizeof(assetTableIds) / sizeof(UniversalId);
for (size_t i = 0; i < numAssetTables; ++i)
{
ResourceTable* table = static_cast<ResourceTable*>(getTableModel(assetTableIds[i]));
table->beginReset();
}
// Trigger recreation
mResourcesManager.recreateResources();
for (size_t i = 0; i < numAssetTables; ++i)
{
ResourceTable* table = static_cast<ResourceTable*>(getTableModel(assetTableIds[i]));
table->endReset();
}
// Get rid of potentially old cached assets
mResourceSystem->clearCache();
emit assetTablesChanged();
}
void CSMWorld::Data::dataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) void CSMWorld::Data::dataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight)
{ {
if (topLeft.column()<=0) if (topLeft.column()<=0)
@ -1228,7 +1274,7 @@ void CSMWorld::Data::rowsChanged (const QModelIndex& parent, int start, int end)
const VFS::Manager* CSMWorld::Data::getVFS() const const VFS::Manager* CSMWorld::Data::getVFS() const
{ {
return mResourcesManager.getVFS(); return mVFS.get();
} }
const Fallback::Map* CSMWorld::Data::getFallbackMap() const const Fallback::Map* CSMWorld::Data::getFallbackMap() const

@ -31,6 +31,7 @@
#include <components/resource/resourcesystem.hpp> #include <components/resource/resourcesystem.hpp>
#include <components/files/multidircollection.hpp>
#include <components/to_utf8/to_utf8.hpp> #include <components/to_utf8/to_utf8.hpp>
#include "../doc/stage.hpp" #include "../doc/stage.hpp"
@ -46,6 +47,7 @@
#include "infocollection.hpp" #include "infocollection.hpp"
#include "nestedinfocollection.hpp" #include "nestedinfocollection.hpp"
#include "pathgrid.hpp" #include "pathgrid.hpp"
#include "resourcesmanager.hpp"
#include "metadata.hpp" #include "metadata.hpp"
#ifndef Q_MOC_RUN #ifndef Q_MOC_RUN
#include "subcellcollection.hpp" #include "subcellcollection.hpp"
@ -108,7 +110,6 @@ namespace CSMWorld
RefCollection mRefs; RefCollection mRefs;
IdCollection<ESM::Filter> mFilters; IdCollection<ESM::Filter> mFilters;
Collection<MetaData> mMetaData; Collection<MetaData> mMetaData;
const ResourcesManager& mResourcesManager;
const Fallback::Map* mFallbackMap; const Fallback::Map* mFallbackMap;
std::vector<QAbstractItemModel *> mModels; std::vector<QAbstractItemModel *> mModels;
std::map<UniversalId::Type, QAbstractItemModel *> mModelIndex; std::map<UniversalId::Type, QAbstractItemModel *> mModelIndex;
@ -119,6 +120,11 @@ namespace CSMWorld
std::map<std::string, std::map<ESM::RefNum, std::string> > mRefLoadCache; std::map<std::string, std::map<ESM::RefNum, std::string> > mRefLoadCache;
int mReaderIndex; int mReaderIndex;
bool mFsStrict;
Files::PathContainer mDataPaths;
std::vector<std::string> mArchives;
std::unique_ptr<VFS::Manager> mVFS;
ResourcesManager mResourcesManager;
std::shared_ptr<Resource::ResourceSystem> mResourceSystem; std::shared_ptr<Resource::ResourceSystem> mResourceSystem;
std::vector<std::shared_ptr<ESM::ESMReader> > mReaders; std::vector<std::shared_ptr<ESM::ESMReader> > mReaders;
@ -140,7 +146,9 @@ namespace CSMWorld
public: public:
Data (ToUTF8::FromType encoding, const ResourcesManager& resourcesManager, const Fallback::Map* fallback, const boost::filesystem::path& resDir); Data (ToUTF8::FromType encoding, bool fsStrict, const Files::PathContainer& dataPaths,
const std::vector<std::string>& archives, const Fallback::Map* fallback,
const boost::filesystem::path& resDir);
virtual ~Data(); virtual ~Data();
@ -304,8 +312,12 @@ namespace CSMWorld
void idListChanged(); void idListChanged();
void assetTablesChanged();
private slots: private slots:
void assetsChanged();
void dataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); void dataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);
void rowsChanged (const QModelIndex& parent, int start, int end); void rowsChanged (const QModelIndex& parent, int start, int end);

@ -12,6 +12,14 @@ CSMWorld::Resources::Resources (const VFS::Manager* vfs, const std::string& base
const char * const *extensions) const char * const *extensions)
: mBaseDirectory (baseDirectory), mType (type) : mBaseDirectory (baseDirectory), mType (type)
{ {
recreate(vfs, extensions);
}
void CSMWorld::Resources::recreate(const VFS::Manager* vfs, const char * const *extensions)
{
mFiles.clear();
mIndex.clear();
int baseSize = mBaseDirectory.size(); int baseSize = mBaseDirectory.size();
const std::map<std::string, VFS::File*>& index = vfs->getIndex(); const std::map<std::string, VFS::File*>& index = vfs->getIndex();

@ -27,6 +27,8 @@ namespace CSMWorld
Resources (const VFS::Manager* vfs, const std::string& baseDirectory, UniversalId::Type type, Resources (const VFS::Manager* vfs, const std::string& baseDirectory, UniversalId::Type type,
const char * const *extensions = 0); const char * const *extensions = 0);
void recreate(const VFS::Manager* vfs, const char * const *extensions = 0);
int getSize() const; int getSize() const;
std::string getId (int index) const; std::string getId (int index) const;

@ -14,21 +14,24 @@ void CSMWorld::ResourcesManager::addResources (const Resources& resources)
resources)); resources));
} }
const char * const * CSMWorld::ResourcesManager::getMeshExtensions()
{
// maybe we could go over the osgDB::Registry to list all supported node formats
static const char * const sMeshTypes[] = { "nif", "osg", "osgt", "osgb", "osgx", "osg2", 0 };
return sMeshTypes;
}
void CSMWorld::ResourcesManager::setVFS(const VFS::Manager *vfs) void CSMWorld::ResourcesManager::setVFS(const VFS::Manager *vfs)
{ {
mVFS = vfs; mVFS = vfs;
mResources.clear(); mResources.clear();
// maybe we could go over the osgDB::Registry to list all supported node formats addResources (Resources (vfs, "meshes", UniversalId::Type_Mesh, getMeshExtensions()));
static const char * const sMeshTypes[] = { "nif", "osg", "osgt", "osgb", "osgx", "osg2", 0 };
addResources (Resources (vfs, "meshes", UniversalId::Type_Mesh, sMeshTypes));
addResources (Resources (vfs, "icons", UniversalId::Type_Icon)); addResources (Resources (vfs, "icons", UniversalId::Type_Icon));
addResources (Resources (vfs, "music", UniversalId::Type_Music)); addResources (Resources (vfs, "music", UniversalId::Type_Music));
addResources (Resources (vfs, "sound", UniversalId::Type_SoundRes)); addResources (Resources (vfs, "sound", UniversalId::Type_SoundRes));
addResources (Resources (vfs, "textures", UniversalId::Type_Texture)); addResources (Resources (vfs, "textures", UniversalId::Type_Texture));
addResources (Resources (vfs, "videos", UniversalId::Type_Video)); addResources (Resources (vfs, "video", UniversalId::Type_Video));
} }
const VFS::Manager* CSMWorld::ResourcesManager::getVFS() const const VFS::Manager* CSMWorld::ResourcesManager::getVFS() const
@ -36,6 +39,18 @@ const VFS::Manager* CSMWorld::ResourcesManager::getVFS() const
return mVFS; return mVFS;
} }
void CSMWorld::ResourcesManager::recreateResources()
{
std::map<UniversalId::Type, Resources>::iterator it = mResources.begin();
for ( ; it != mResources.end(); ++it)
{
if (it->first == UniversalId::Type_Mesh)
it->second.recreate(mVFS, getMeshExtensions());
else
it->second.recreate(mVFS);
}
}
const CSMWorld::Resources& CSMWorld::ResourcesManager::get (UniversalId::Type type) const const CSMWorld::Resources& CSMWorld::ResourcesManager::get (UniversalId::Type type) const
{ {
std::map<UniversalId::Type, Resources>::const_iterator iter = mResources.find (type); std::map<UniversalId::Type, Resources>::const_iterator iter = mResources.find (type);

@ -22,6 +22,8 @@ namespace CSMWorld
void addResources (const Resources& resources); void addResources (const Resources& resources);
const char * const * getMeshExtensions();
public: public:
ResourcesManager(); ResourcesManager();
@ -30,6 +32,8 @@ namespace CSMWorld
void setVFS(const VFS::Manager* vfs); void setVFS(const VFS::Manager* vfs);
void recreateResources();
const Resources& get (UniversalId::Type type) const; const Resources& get (UniversalId::Type type) const;
}; };
} }

@ -154,3 +154,13 @@ int CSMWorld::ResourceTable::getColumnId (int column) const
return -1; return -1;
} }
void CSMWorld::ResourceTable::beginReset()
{
beginResetModel();
}
void CSMWorld::ResourceTable::endReset()
{
endResetModel();
}

@ -52,7 +52,12 @@ namespace CSMWorld
/// Is \a id flagged as deleted? /// Is \a id flagged as deleted?
virtual bool isDeleted (const std::string& id) const; virtual bool isDeleted (const std::string& id) const;
virtual int getColumnId (int column) const; virtual int getColumnId (int column) const;
/// Signal Qt that the data is about to change.
void beginReset();
/// Signal Qt that the data has been changed.
void endReset();
}; };
} }

@ -284,6 +284,13 @@ void CSVDoc::View::setupAssetsMenu()
{ {
QMenu *assets = menuBar()->addMenu (tr ("Assets")); QMenu *assets = menuBar()->addMenu (tr ("Assets"));
QAction *reload = new QAction (tr ("Reload"), this);
connect (reload, SIGNAL (triggered()), &mDocument->getData(), SLOT (assetsChanged()));
setupShortcut("document-assets-reload", reload);
assets->addAction (reload);
assets->addSeparator();
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()));
setupShortcut("document-assets-sounds", sounds); setupShortcut("document-assets-sounds", sounds);

@ -275,12 +275,32 @@ bool CSVRender::Cell::referenceAdded (const QModelIndex& parent, int start, int
void CSVRender::Cell::pathgridModified() void CSVRender::Cell::pathgridModified()
{ {
mPathgrid->recreateGeometry(); if (mPathgrid)
mPathgrid->recreateGeometry();
} }
void CSVRender::Cell::pathgridRemoved() void CSVRender::Cell::pathgridRemoved()
{ {
mPathgrid->removeGeometry(); if (mPathgrid)
mPathgrid->removeGeometry();
}
void CSVRender::Cell::reloadAssets()
{
for (std::map<std::string, Object *>::const_iterator iter (mObjects.begin());
iter != mObjects.end(); ++iter)
{
iter->second->reloadAssets();
}
if (mTerrain)
{
mTerrain->unloadCell(mCoordinates.getX(), mCoordinates.getY());
mTerrain->loadCell(mCoordinates.getX(), mCoordinates.getY());
}
if (mCellWater)
mCellWater->reloadAssets();
} }
void CSVRender::Cell::setSelection (int elementMask, Selection mode) void CSVRender::Cell::setSelection (int elementMask, Selection mode)
@ -302,7 +322,7 @@ void CSVRender::Cell::setSelection (int elementMask, Selection mode)
iter->second->setSelected (selected); iter->second->setSelected (selected);
} }
} }
if (elementMask & Mask_Pathgrid) if (mPathgrid && elementMask & Mask_Pathgrid)
{ {
// Only one pathgrid may be selected, so some operations will only have an effect // Only one pathgrid may be selected, so some operations will only have an effect
// if the pathgrid is already focused // if the pathgrid is already focused
@ -402,7 +422,7 @@ std::vector<osg::ref_ptr<CSVRender::TagBase> > CSVRender::Cell::getSelection (un
iter!=mObjects.end(); ++iter) iter!=mObjects.end(); ++iter)
if (iter->second->getSelected()) if (iter->second->getSelected())
result.push_back (iter->second->getTag()); result.push_back (iter->second->getTag());
if (elementMask & Mask_Pathgrid) if (mPathgrid && elementMask & Mask_Pathgrid)
if (mPathgrid->isSelected()) if (mPathgrid->isSelected())
result.push_back(mPathgrid->getTag()); result.push_back(mPathgrid->getTag());
@ -439,6 +459,6 @@ void CSVRender::Cell::reset (unsigned int elementMask)
for (std::map<std::string, Object *>::const_iterator iter (mObjects.begin()); for (std::map<std::string, Object *>::const_iterator iter (mObjects.begin());
iter!=mObjects.end(); ++iter) iter!=mObjects.end(); ++iter)
iter->second->reset(); iter->second->reset();
if (elementMask & Mask_Pathgrid) if (mPathgrid && elementMask & Mask_Pathgrid)
mPathgrid->resetIndicators(); mPathgrid->resetIndicators();
} }

@ -118,6 +118,8 @@ namespace CSVRender
void pathgridRemoved(); void pathgridRemoved();
void reloadAssets();
void setSelection (int elementMask, Selection mode); void setSelection (int elementMask, Selection mode);
// Select everything that references the same ID as at least one of the elements // Select everything that references the same ID as at least one of the elements

@ -92,6 +92,11 @@ namespace CSVRender
} }
} }
void CellWater::reloadAssets()
{
recreate();
}
void CellWater::cellDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight) void CellWater::cellDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight)
{ {
const CSMWorld::Collection<CSMWorld::Cell>& cells = mData.getCells(); const CSMWorld::Collection<CSMWorld::Cell>& cells = mData.getCells();

@ -42,6 +42,8 @@ namespace CSVRender
void updateCellData(const CSMWorld::Record<CSMWorld::Cell>& cellRecord); void updateCellData(const CSMWorld::Record<CSMWorld::Cell>& cellRecord);
void reloadAssets();
private slots: private slots:
void cellDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight); void cellDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight);

@ -532,6 +532,12 @@ bool CSVRender::Object::referenceDataChanged (const QModelIndex& topLeft,
return false; return false;
} }
void CSVRender::Object::reloadAssets()
{
update();
updateMarker();
}
std::string CSVRender::Object::getReferenceId() const std::string CSVRender::Object::getReferenceId() const
{ {
return mReferenceId; return mReferenceId;

@ -151,6 +151,9 @@ namespace CSVRender
/// this object? /// this object?
bool referenceDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); bool referenceDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);
/// Reloads the underlying asset
void reloadAssets();
/// Returns an empty string if this is a refereceable-type object. /// Returns an empty string if this is a refereceable-type object.
std::string getReferenceId() const; std::string getReferenceId() const;

@ -472,6 +472,9 @@ CSVRender::PagedWorldspaceWidget::PagedWorldspaceWidget (QWidget* parent, CSMDoc
connect (cells, SIGNAL (rowsInserted (const QModelIndex&, int, int)), connect (cells, SIGNAL (rowsInserted (const QModelIndex&, int, int)),
this, SLOT (cellAdded (const QModelIndex&, int, int))); this, SLOT (cellAdded (const QModelIndex&, int, int)));
connect (&document.getData(), SIGNAL (assetTablesChanged ()),
this, SLOT (assetTablesChanged ()));
// Shortcuts // Shortcuts
CSMPrefs::Shortcut* loadCameraCellShortcut = new CSMPrefs::Shortcut("scene-load-cam-cell", this); CSMPrefs::Shortcut* loadCameraCellShortcut = new CSMPrefs::Shortcut("scene-load-cam-cell", this);
connect(loadCameraCellShortcut, SIGNAL(activated()), this, SLOT(loadCameraCell())); connect(loadCameraCellShortcut, SIGNAL(activated()), this, SLOT(loadCameraCell()));
@ -520,7 +523,7 @@ void CSVRender::PagedWorldspaceWidget::useViewHint (const std::string& hint)
// Loop through all the coordinates to add them to selection // Loop through all the coordinates to add them to selection
while (stream >> ignore1 >> ignore2 >> x >> y) while (stream >> ignore1 >> ignore2 >> x >> y)
selection.add (CSMWorld::CellCoordinates (x, y)); selection.add (CSMWorld::CellCoordinates (x, y));
// Mark that camera needs setup // Mark that camera needs setup
mCamPositionSet=false; mCamPositionSet=false;
} }
@ -763,6 +766,15 @@ void CSVRender::PagedWorldspaceWidget::cellAdded (const QModelIndex& index, int
flagAsModified(); flagAsModified();
} }
void CSVRender::PagedWorldspaceWidget::assetTablesChanged()
{
std::map<CSMWorld::CellCoordinates, Cell *>::iterator iter = mCells.begin();
for ( ; iter != mCells.end(); ++iter)
{
iter->second->reloadAssets();
}
}
void CSVRender::PagedWorldspaceWidget::loadCameraCell() void CSVRender::PagedWorldspaceWidget::loadCameraCell()
{ {
addCellToSceneFromCamera(0, 0); addCellToSceneFromCamera(0, 0);

@ -84,7 +84,7 @@ namespace CSVRender
/// hint system. /// hint system.
virtual ~PagedWorldspaceWidget(); virtual ~PagedWorldspaceWidget();
/// Decodes the the hint string to set of cell that are rendered. /// Decodes the the hint string to set of cell that are rendered.
void useViewHint (const std::string& hint); void useViewHint (const std::string& hint);
@ -155,6 +155,8 @@ namespace CSVRender
virtual void cellAdded (const QModelIndex& index, int start, int end); virtual void cellAdded (const QModelIndex& index, int start, int end);
void assetTablesChanged ();
void loadCameraCell(); void loadCameraCell();
void loadEastCell(); void loadEastCell();

@ -72,12 +72,15 @@ namespace CSVRender
} }
else if (Cell* cell = getWorldspaceWidget().getCell (hitResult.worldPos)) else if (Cell* cell = getWorldspaceWidget().getCell (hitResult.worldPos))
{ {
// Add node if (cell->getPathgrid())
QUndoStack& undoStack = getWorldspaceWidget().getDocument().getUndoStack(); {
QString description = "Add node"; // Add node
QUndoStack& undoStack = getWorldspaceWidget().getDocument().getUndoStack();
QString description = "Add node";
CSMWorld::CommandMacro macro(undoStack, description); CSMWorld::CommandMacro macro(undoStack, description);
cell->getPathgrid()->applyPoint(macro, hitResult.worldPos); cell->getPathgrid()->applyPoint(macro, hitResult.worldPos);
}
} }
} }
@ -205,7 +208,7 @@ namespace CSVRender
WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask()); WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask());
Cell* cell = getWorldspaceWidget().getCell(hit.worldPos); Cell* cell = getWorldspaceWidget().getCell(hit.worldPos);
if (cell) if (cell && cell->getPathgrid())
{ {
PathgridTag* tag = 0; PathgridTag* tag = 0;
if (hit.tag && (tag = dynamic_cast<PathgridTag*>(hit.tag.get())) && tag->getPathgrid()->getId() == mEdgeId) if (hit.tag && (tag = dynamic_cast<PathgridTag*>(hit.tag.get())) && tag->getPathgrid()->getId() == mEdgeId)

@ -17,6 +17,9 @@ CSVRender::PreviewWidget::PreviewWidget (CSMWorld::Data& data,
connect (referenceables, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)), connect (referenceables, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)),
this, SLOT (referenceableAboutToBeRemoved (const QModelIndex&, int, int))); this, SLOT (referenceableAboutToBeRemoved (const QModelIndex&, int, int)));
connect (&mData, SIGNAL (assetTablesChanged ()),
this, SLOT (assetTablesChanged ()));
if (!referenceable) if (!referenceable)
{ {
QAbstractItemModel *references = QAbstractItemModel *references =
@ -119,3 +122,8 @@ void CSVRender::PreviewWidget::referenceAboutToBeRemoved (const QModelIndex& par
if (index.row()>=start && index.row()<=end) if (index.row()>=start && index.row()<=end)
emit closeRequest(); emit closeRequest();
} }
void CSVRender::PreviewWidget::assetTablesChanged ()
{
mObject.reloadAssets();
}

@ -47,6 +47,8 @@ namespace CSVRender
void referenceDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); void referenceDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);
void referenceAboutToBeRemoved (const QModelIndex& parent, int start, int end); void referenceAboutToBeRemoved (const QModelIndex& parent, int start, int end);
void assetTablesChanged ();
}; };
} }

@ -221,8 +221,8 @@ SceneWidget::SceneWidget(std::shared_ptr<Resource::ResourceSystem> resourceSyste
SceneWidget::~SceneWidget() SceneWidget::~SceneWidget()
{ {
// Since we're holding on to the scene templates past the existence of this graphics context, we'll need to manually release the created objects // Since we're holding on to the resources past the existence of this graphics context, we'll need to manually release the created objects
mResourceSystem->getSceneManager()->releaseGLObjects(mView->getCamera()->getGraphicsContext()->getState()); mResourceSystem->releaseGLObjects(mView->getCamera()->getGraphicsContext()->getState());
} }
void SceneWidget::setLighting(Lighting *lighting) void SceneWidget::setLighting(Lighting *lighting)
@ -393,6 +393,7 @@ void SceneWidget::selectNavigationMode (const std::string& mode)
mCurrentCamControl->setCamera(NULL); mCurrentCamControl->setCamera(NULL);
mCurrentCamControl = mOrbitCamControl; mCurrentCamControl = mOrbitCamControl;
mOrbitCamControl->setCamera(getCamera()); mOrbitCamControl->setCamera(getCamera());
mOrbitCamControl->reset();
} }
} }

@ -47,6 +47,9 @@ CSVRender::UnpagedWorldspaceWidget::UnpagedWorldspaceWidget (const std::string&
connect (mCellsModel, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)), connect (mCellsModel, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)),
this, SLOT (cellRowsAboutToBeRemoved (const QModelIndex&, int, int))); this, SLOT (cellRowsAboutToBeRemoved (const QModelIndex&, int, int)));
connect (&document.getData(), SIGNAL (assetTablesChanged ()),
this, SLOT (assetTablesChanged ()));
update(); update();
mCell.reset (new Cell (document.getData(), mRootNode, mCellId)); mCell.reset (new Cell (document.getData(), mRootNode, mCellId));
@ -82,6 +85,12 @@ void CSVRender::UnpagedWorldspaceWidget::cellRowsAboutToBeRemoved (const QModelI
emit closeRequest(); emit closeRequest();
} }
void CSVRender::UnpagedWorldspaceWidget::assetTablesChanged()
{
if (mCell)
mCell->reloadAssets();
}
bool CSVRender::UnpagedWorldspaceWidget::handleDrop (const std::vector<CSMWorld::UniversalId>& universalIdData, DropType type) bool CSVRender::UnpagedWorldspaceWidget::handleDrop (const std::vector<CSMWorld::UniversalId>& universalIdData, DropType type)
{ {
if (WorldspaceWidget::handleDrop (universalIdData, type)) if (WorldspaceWidget::handleDrop (universalIdData, type))

@ -108,6 +108,8 @@ namespace CSVRender
void cellRowsAboutToBeRemoved (const QModelIndex& parent, int start, int end); void cellRowsAboutToBeRemoved (const QModelIndex& parent, int start, int end);
void assetTablesChanged ();
signals: signals:
void cellChanged(const CSMWorld::UniversalId& id); void cellChanged(const CSMWorld::UniversalId& id);

@ -32,13 +32,19 @@ std::string CSVWorld::InfoCreator::getId() const
void CSVWorld::InfoCreator::configureCreateCommand (CSMWorld::CreateCommand& command) const void CSVWorld::InfoCreator::configureCreateCommand (CSMWorld::CreateCommand& command) const
{ {
int index = CSMWorld::IdTable& table = dynamic_cast<CSMWorld::IdTable&> (*getData().getTableModel (getCollectionId()));
dynamic_cast<CSMWorld::IdTable&> (*getData().getTableModel (getCollectionId())).
findColumnIndex (
getCollectionId().getType()==CSMWorld::UniversalId::Type_TopicInfos ?
CSMWorld::Columns::ColumnId_Topic : CSMWorld::Columns::ColumnId_Journal);
command.addValue (index, mTopic->text()); if (getCollectionId() == CSMWorld::UniversalId::Type_TopicInfos)
{
command.addValue (table.findColumnIndex(CSMWorld::Columns::ColumnId_Topic), mTopic->text());
command.addValue (table.findColumnIndex(CSMWorld::Columns::ColumnId_Rank), -1);
command.addValue (table.findColumnIndex(CSMWorld::Columns::ColumnId_Gender), -1);
command.addValue (table.findColumnIndex(CSMWorld::Columns::ColumnId_PcRank), -1);
}
else
{
command.addValue (table.findColumnIndex(CSMWorld::Columns::ColumnId_Journal), mTopic->text());
}
} }
CSVWorld::InfoCreator::InfoCreator (CSMWorld::Data& data, QUndoStack& undoStack, CSVWorld::InfoCreator::InfoCreator (CSMWorld::Data& data, QUndoStack& undoStack,

@ -113,7 +113,6 @@ void OMW::Engine::frame(float frametime)
try try
{ {
mStartTick = mViewer->getStartTick(); mStartTick = mViewer->getStartTick();
mEnvironment.setFrameDuration (frametime);
// update input // update input
mEnvironment.getInputManager()->update(frametime, false); mEnvironment.getInputManager()->update(frametime, false);
@ -378,8 +377,7 @@ void OMW::Engine::setResourceDir (const boost::filesystem::path& parResDir)
mResDir = parResDir; mResDir = parResDir;
} }
// Set start cell name (only interiors for now) // Set start cell name
void OMW::Engine::setCell (const std::string& cellName) void OMW::Engine::setCell (const std::string& cellName)
{ {
mCellName = cellName; mCellName = cellName;
@ -763,6 +761,8 @@ void OMW::Engine::go()
Settings::Manager::getString("screenshot format", "General"))); Settings::Manager::getString("screenshot format", "General")));
mViewer->addEventHandler(mScreenCaptureHandler); mViewer->addEventHandler(mScreenCaptureHandler);
mEnvironment.setFrameRateLimit(Settings::Manager::getFloat("framerate limit", "Video"));
// Create encoder // Create encoder
ToUTF8::Utf8Encoder encoder (mEncoding); ToUTF8::Utf8Encoder encoder (mEncoding);
mEncoder = &encoder; mEncoder = &encoder;
@ -816,7 +816,6 @@ void OMW::Engine::go()
// Start the main rendering loop // Start the main rendering loop
osg::Timer frameTimer; osg::Timer frameTimer;
double simulationTime = 0.0; double simulationTime = 0.0;
float framerateLimit = Settings::Manager::getFloat("framerate limit", "Video");
while (!mViewer->done() && !mEnvironment.getStateManager()->hasQuitRequest()) while (!mViewer->done() && !mEnvironment.getStateManager()->hasQuitRequest())
{ {
double dt = frameTimer.time_s(); double dt = frameTimer.time_s();
@ -841,6 +840,8 @@ void OMW::Engine::go()
mViewer->advance(simulationTime); mViewer->advance(simulationTime);
mEnvironment.setFrameDuration(dt);
frame(dt); frame(dt);
if (!mEnvironment.getInputManager()->isWindowVisible()) if (!mEnvironment.getInputManager()->isWindowVisible())
@ -858,15 +859,7 @@ void OMW::Engine::go()
mViewer->renderingTraversals(); mViewer->renderingTraversals();
} }
if (framerateLimit > 0.f) mEnvironment.limitFrameRate(frameTimer.time_s());
{
double thisFrameTime = frameTimer.time_s();
double minFrameTime = 1.0 / framerateLimit;
if (thisFrameTime < minFrameTime)
{
OpenThreads::Thread::microSleep(1000*1000*(minFrameTime-thisFrameTime));
}
}
} }
// Save user settings // Save user settings

@ -148,7 +148,7 @@ namespace OMW
/// Set resource dir /// Set resource dir
void setResourceDir(const boost::filesystem::path& parResDir); void setResourceDir(const boost::filesystem::path& parResDir);
/// Set start cell name (only interiors for now) /// Set start cell name
void setCell(const std::string& cellName); void setCell(const std::string& cellName);
/** /**

@ -30,6 +30,9 @@
#include <cstdlib> #include <cstdlib>
#endif #endif
#if (defined(__APPLE__) || defined(__linux) || defined(__unix) || defined(__posix))
#include <unistd.h>
#endif
#if (defined(__APPLE__) || (defined(__linux) && !defined(ANDROID)) || (defined(__unix) && !defined(ANDROID)) || defined(__posix)) #if (defined(__APPLE__) || (defined(__linux) && !defined(ANDROID)) || (defined(__unix) && !defined(ANDROID)) || defined(__posix))
#define USE_CRASH_CATCHER 1 #define USE_CRASH_CATCHER 1

@ -2,6 +2,8 @@
#include <cassert> #include <cassert>
#include <OpenThreads/Thread>
#include "world.hpp" #include "world.hpp"
#include "scriptmanager.hpp" #include "scriptmanager.hpp"
#include "dialoguemanager.hpp" #include "dialoguemanager.hpp"
@ -17,7 +19,7 @@ MWBase::Environment *MWBase::Environment::sThis = 0;
MWBase::Environment::Environment() MWBase::Environment::Environment()
: mWorld (0), mSoundManager (0), mScriptManager (0), mWindowManager (0), : mWorld (0), mSoundManager (0), mScriptManager (0), mWindowManager (0),
mMechanicsManager (0), mDialogueManager (0), mJournal (0), mInputManager (0), mStateManager (0), mMechanicsManager (0), mDialogueManager (0), mJournal (0), mInputManager (0), mStateManager (0),
mFrameDuration (0) mFrameDuration (0), mFrameRateLimit(0.f)
{ {
assert (!sThis); assert (!sThis);
sThis = this; sThis = this;
@ -79,6 +81,29 @@ void MWBase::Environment::setFrameDuration (float duration)
mFrameDuration = duration; mFrameDuration = duration;
} }
void MWBase::Environment::setFrameRateLimit(float limit)
{
mFrameRateLimit = limit;
}
float MWBase::Environment::getFrameRateLimit() const
{
return mFrameRateLimit;
}
void MWBase::Environment::limitFrameRate(double dt) const
{
if (mFrameRateLimit > 0.f)
{
double thisFrameTime = dt;
double minFrameTime = 1.0 / static_cast<double>(mFrameRateLimit);
if (thisFrameTime < minFrameTime)
{
OpenThreads::Thread::microSleep(1000*1000*(minFrameTime-thisFrameTime));
}
}
}
MWBase::World *MWBase::Environment::getWorld() const MWBase::World *MWBase::Environment::getWorld() const
{ {
assert (mWorld); assert (mWorld);

@ -33,6 +33,7 @@ namespace MWBase
InputManager *mInputManager; InputManager *mInputManager;
StateManager *mStateManager; StateManager *mStateManager;
float mFrameDuration; float mFrameDuration;
float mFrameRateLimit;
Environment (const Environment&); Environment (const Environment&);
///< not implemented ///< not implemented
@ -67,6 +68,10 @@ namespace MWBase
void setFrameDuration (float duration); void setFrameDuration (float duration);
///< Set length of current frame in seconds. ///< Set length of current frame in seconds.
void setFrameRateLimit(float frameRateLimit);
float getFrameRateLimit() const;
void limitFrameRate(double dt) const;
World *getWorld() const; World *getWorld() const;
SoundManager *getSoundManager() const; SoundManager *getSoundManager() const;

@ -7,6 +7,8 @@
#include <set> #include <set>
#include <stdint.h> #include <stdint.h>
#include "../mwworld/ptr.hpp"
namespace osg namespace osg
{ {
class Vec3f; class Vec3f;
@ -231,6 +233,7 @@ namespace MWBase
virtual void keepPlayerAlive() = 0; virtual void keepPlayerAlive() = 0;
virtual bool isReadyToBlock (const MWWorld::Ptr& ptr) const = 0; virtual bool isReadyToBlock (const MWWorld::Ptr& ptr) const = 0;
virtual bool isAttackingOrSpell(const MWWorld::Ptr &ptr) const = 0;
virtual void confiscateStolenItems (const MWWorld::Ptr& player, const MWWorld::Ptr& targetContainer) = 0; virtual void confiscateStolenItems (const MWWorld::Ptr& player, const MWWorld::Ptr& targetContainer) = 0;
@ -240,8 +243,8 @@ namespace MWBase
/// Has the player stolen this item from the given owner? /// Has the player stolen this item from the given owner?
virtual bool isItemStolenFrom(const std::string& itemid, const std::string& ownerid) = 0; virtual bool isItemStolenFrom(const std::string& itemid, const std::string& ownerid) = 0;
virtual bool isAllowedToUse (const MWWorld::Ptr& ptr, const MWWorld::CellRef& cellref, MWWorld::Ptr& victim) = 0; virtual bool isAllowedToUse (const MWWorld::Ptr& ptr, const MWWorld::ConstPtr& item, MWWorld::Ptr& victim) = 0;
/// Turn actor into werewolf or normal form. /// Turn actor into werewolf or normal form.
virtual void setWerewolf(const MWWorld::Ptr& actor, bool werewolf) = 0; virtual void setWerewolf(const MWWorld::Ptr& actor, bool werewolf) = 0;
@ -251,6 +254,11 @@ namespace MWBase
virtual void applyWerewolfAcrobatics(const MWWorld::Ptr& actor) = 0; virtual void applyWerewolfAcrobatics(const MWWorld::Ptr& actor) = 0;
virtual void cleanupSummonedCreature(const MWWorld::Ptr& caster, int creatureActorId) = 0; virtual void cleanupSummonedCreature(const MWWorld::Ptr& caster, int creatureActorId) = 0;
virtual void confiscateStolenItemToOwner(const MWWorld::Ptr &player, const MWWorld::Ptr &item, const MWWorld::Ptr& victim, int count) = 0;
virtual bool isRunning(const MWWorld::Ptr& ptr) = 0;
virtual bool isSneaking(const MWWorld::Ptr& ptr) = 0;
}; };
} }

@ -222,7 +222,10 @@ namespace MWBase
virtual void setSpellVisibility(bool visible) = 0; virtual void setSpellVisibility(bool visible) = 0;
virtual void setSneakVisibility(bool visible) = 0; virtual void setSneakVisibility(bool visible) = 0;
virtual void activateQuickKey (int index) = 0; /// activate selected quick key
virtual void activateQuickKey (int index) = 0;
/// update activated quick key state (if action executing was delayed for some reason)
virtual void updateActivatedQuickKey () = 0;
virtual std::string getSelectedSpell() = 0; virtual std::string getSelectedSpell() = 0;
virtual void setSelectedSpell(const std::string& spellId, int successChancePercent) = 0; virtual void setSelectedSpell(const std::string& spellId, int successChancePercent) = 0;

@ -51,6 +51,11 @@ namespace MWClass
return ""; return "";
} }
bool Activator::isActivator() const
{
return true;
}
bool Activator::useAnim() const bool Activator::useAnim() const
{ {
return true; return true;

@ -42,6 +42,8 @@ namespace MWClass
virtual bool useAnim() const; virtual bool useAnim() const;
///< Whether or not to use animated variant of model (default false) ///< Whether or not to use animated variant of model (default false)
virtual bool isActivator() const;
}; };
} }

@ -4,6 +4,7 @@
#include <components/esm/loadcrea.hpp> #include <components/esm/loadcrea.hpp>
#include <components/esm/creaturestate.hpp> #include <components/esm/creaturestate.hpp>
#include <components/settings/settings.hpp>
/* /*
Start of tes3mp addition Start of tes3mp addition
@ -558,10 +559,27 @@ namespace MWClass
return action; return action;
} }
if(getCreatureStats(ptr).isDead()) const MWMechanics::CreatureStats& stats = getCreatureStats(ptr);
return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr, true));
if(ptr.getClass().getCreatureStats(ptr).getAiSequence().isInCombat()) if(stats.isDead())
{
bool canLoot = Settings::Manager::getBool ("can loot during death animation", "Game");
// by default user can loot friendly actors during death animation
if (canLoot && !stats.getAiSequence().isInCombat())
return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr, true));
// otherwise wait until death animation
if(stats.isDeathAnimationFinished())
return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr, true));
// death animation is not finished, do nothing
return std::shared_ptr<MWWorld::Action> (new MWWorld::FailedAction(""));
}
if(stats.getAiSequence().isInCombat())
return std::shared_ptr<MWWorld::Action>(new MWWorld::FailedAction("")); return std::shared_ptr<MWWorld::Action>(new MWWorld::FailedAction(""));
return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionTalk(ptr)); return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionTalk(ptr));
} }
@ -668,7 +686,11 @@ namespace MWClass
return true; return true;
const CreatureCustomData& customData = ptr.getRefData().getCustomData()->asCreatureCustomData(); const CreatureCustomData& customData = ptr.getRefData().getCustomData()->asCreatureCustomData();
return !customData.mCreatureStats.getAiSequence().isInCombat() || customData.mCreatureStats.isDead();
if (customData.mCreatureStats.isDead() && customData.mCreatureStats.isDeathAnimationFinished())
return true;
return !customData.mCreatureStats.getAiSequence().isInCombat();
} }
MWGui::ToolTipInfo Creature::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const MWGui::ToolTipInfo Creature::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const

@ -88,6 +88,11 @@ namespace MWClass
} }
} }
bool Door::isDoor() const
{
return true;
}
bool Door::useAnim() const bool Door::useAnim() const
{ {
return true; return true;

@ -20,6 +20,8 @@ namespace MWClass
virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const; virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const;
virtual bool isDoor() const;
virtual bool useAnim() const; virtual bool useAnim() const;
virtual std::string getName (const MWWorld::ConstPtr& ptr) const; virtual std::string getName (const MWWorld::ConstPtr& ptr) const;

@ -7,6 +7,7 @@
#include <components/esm/loadmgef.hpp> #include <components/esm/loadmgef.hpp>
#include <components/esm/loadnpc.hpp> #include <components/esm/loadnpc.hpp>
#include <components/esm/npcstate.hpp> #include <components/esm/npcstate.hpp>
#include <components/settings/settings.hpp>
/* /*
Start of tes3mp addition Start of tes3mp addition
@ -999,16 +1000,35 @@ namespace MWClass
return action; return action;
} }
if(getCreatureStats(ptr).isDead()) const MWMechanics::CreatureStats& stats = getCreatureStats(ptr);
return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr, true));
if(ptr.getClass().getCreatureStats(ptr).getAiSequence().isInCombat()) if(stats.isDead())
return std::shared_ptr<MWWorld::Action>(new MWWorld::FailedAction("#{sActorInCombat}")); {
bool canLoot = Settings::Manager::getBool ("can loot during death animation", "Game");
// by default user can loot friendly actors during death animation
if (canLoot && !stats.getAiSequence().isInCombat())
return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr, true));
// otherwise wait until death animation
if(stats.isDeathAnimationFinished())
return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr, true));
// death animation is not finished, do nothing
return std::shared_ptr<MWWorld::Action> (new MWWorld::FailedAction(""));
}
if(stats.getAiSequence().isInCombat())
return std::shared_ptr<MWWorld::Action>(new MWWorld::FailedAction(""));
if(getCreatureStats(actor).getStance(MWMechanics::CreatureStats::Stance_Sneak) if(getCreatureStats(actor).getStance(MWMechanics::CreatureStats::Stance_Sneak)
|| ptr.getClass().getCreatureStats(ptr).getKnockedDown()) || ptr.getClass().getCreatureStats(ptr).getKnockedDown())
return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr)); // stealing return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr)); // stealing
// Can't talk to werewolfs // Can't talk to werewolfs
if(ptr.getClass().isNpc() && ptr.getClass().getNpcStats(ptr).isWerewolf()) if(ptr.getClass().isNpc() && ptr.getClass().getNpcStats(ptr).isWerewolf())
return std::shared_ptr<MWWorld::Action> (new MWWorld::FailedAction("")); return std::shared_ptr<MWWorld::Action> (new MWWorld::FailedAction(""));
return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionTalk(ptr)); return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionTalk(ptr));
} }
@ -1155,7 +1175,11 @@ namespace MWClass
return true; return true;
const NpcCustomData& customData = ptr.getRefData().getCustomData()->asNpcCustomData(); const NpcCustomData& customData = ptr.getRefData().getCustomData()->asNpcCustomData();
return !customData.mNpcStats.getAiSequence().isInCombat() || customData.mNpcStats.isDead();
if (customData.mNpcStats.isDead() && customData.mNpcStats.isDeathAnimationFinished())
return true;
return !customData.mNpcStats.getAiSequence().isInCombat();
} }
MWGui::ToolTipInfo Npc::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const MWGui::ToolTipInfo Npc::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const

@ -4,6 +4,7 @@
#include <components/settings/settings.hpp> #include <components/settings/settings.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwbase/windowmanager.hpp" #include "../mwbase/windowmanager.hpp"
@ -373,6 +374,9 @@ namespace MWClass
if (hasItemHealth(ptr) && ptr.getCellRef().getCharge() == 0) if (hasItemHealth(ptr) && ptr.getCellRef().getCharge() == 0)
return std::make_pair(0, "#{sInventoryMessage1}"); return std::make_pair(0, "#{sInventoryMessage1}");
if (MWBase::Environment::get().getMechanicsManager()->isAttackingOrSpell(npc))
return std::make_pair(0, "#{sCantEquipWeapWarning}");
std::pair<std::vector<int>, bool> slots_ = ptr.getClass().getEquipmentSlots(ptr); std::pair<std::vector<int>, bool> slots_ = ptr.getClass().getEquipmentSlots(ptr);
if (slots_.first.empty()) if (slots_.first.empty())

@ -435,9 +435,15 @@ struct TypesetBookImpl::Typesetter : BookTypesetter
} }
else else
{ {
// The section won't completely fit on the current page. Finish the current page and start a new one.
mBook->mPages.push_back (Page (curPageStart, curPageStop));
curPageStart = i->mRect.top;
curPageStop = i->mRect.bottom;
//split section //split section
int sectionHeightLeft = sectionHeight; int sectionHeightLeft = sectionHeight;
while (sectionHeightLeft > mPageHeight) while (sectionHeightLeft >= mPageHeight)
{ {
// Adjust to the top of the first line that does not fit on the current page anymore // Adjust to the top of the first line that does not fit on the current page anymore
int splitPos = curPageStop; int splitPos = curPageStop;

@ -360,8 +360,9 @@ namespace MWGui
if (msg.find("%s") != std::string::npos) if (msg.find("%s") != std::string::npos)
msg.replace(msg.find("%s"), 2, item.getClass().getName(item)); msg.replace(msg.find("%s"), 2, item.getClass().getName(item));
MWBase::Environment::get().getWindowManager()->messageBox(msg); MWBase::Environment::get().getWindowManager()->messageBox(msg);
MWBase::Environment::get().getMechanicsManager()->commitCrime(player, mPtr, MWBase::MechanicsManager::OT_Theft,
item.getClass().getValue(item), true); MWBase::Environment::get().getMechanicsManager()->confiscateStolenItemToOwner(player, item, mPtr, 1);
MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Enchanting); MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Enchanting);
MWBase::Environment::get().getDialogueManager()->goodbyeSelected(); MWBase::Environment::get().getDialogueManager()->goodbyeSelected();
return; return;

@ -219,29 +219,33 @@ namespace MWGui
void HUD::setValue(const std::string& id, const MWMechanics::DynamicStat<float>& value) void HUD::setValue(const std::string& id, const MWMechanics::DynamicStat<float>& value)
{ {
int current = std::max(0, static_cast<int>(value.getCurrent())); int current = static_cast<int>(value.getCurrent());
int modified = static_cast<int>(value.getModified()); int modified = static_cast<int>(value.getModified());
// Fatigue can be negative
if (id != "FBar")
current = std::max(0, current);
MyGUI::Widget* w; MyGUI::Widget* w;
std::string valStr = MyGUI::utility::toString(current) + " / " + MyGUI::utility::toString(modified); std::string valStr = MyGUI::utility::toString(current) + " / " + MyGUI::utility::toString(modified);
if (id == "HBar") if (id == "HBar")
{ {
mHealth->setProgressRange(modified); mHealth->setProgressRange(std::max(0, modified));
mHealth->setProgressPosition(current); mHealth->setProgressPosition(std::max(0, current));
getWidget(w, "HealthFrame"); getWidget(w, "HealthFrame");
w->setUserString("Caption_HealthDescription", "#{sHealthDesc}\n" + valStr); w->setUserString("Caption_HealthDescription", "#{sHealthDesc}\n" + valStr);
} }
else if (id == "MBar") else if (id == "MBar")
{ {
mMagicka->setProgressRange (modified); mMagicka->setProgressRange(std::max(0, modified));
mMagicka->setProgressPosition (current); mMagicka->setProgressPosition(std::max(0, current));
getWidget(w, "MagickaFrame"); getWidget(w, "MagickaFrame");
w->setUserString("Caption_HealthDescription", "#{sMagDesc}\n" + valStr); w->setUserString("Caption_HealthDescription", "#{sMagDesc}\n" + valStr);
} }
else if (id == "FBar") else if (id == "FBar")
{ {
mStamina->setProgressRange (modified); mStamina->setProgressRange(std::max(0, modified));
mStamina->setProgressPosition (current); mStamina->setProgressPosition(std::max(0, current));
getWidget(w, "FatigueFrame"); getWidget(w, "FatigueFrame");
w->setUserString("Caption_HealthDescription", "#{sFatDesc}\n" + valStr); w->setUserString("Caption_HealthDescription", "#{sFatDesc}\n" + valStr);
} }

@ -43,6 +43,7 @@
#include "../mwrender/characterpreview.hpp" #include "../mwrender/characterpreview.hpp"
#include "../mwmechanics/actorutil.hpp" #include "../mwmechanics/actorutil.hpp"
#include "../mwmechanics/creaturestats.hpp"
#include "itemview.hpp" #include "itemview.hpp"
#include "inventoryitemmodel.hpp" #include "inventoryitemmodel.hpp"
@ -693,9 +694,18 @@ namespace MWGui
void InventoryWindow::cycle(bool next) void InventoryWindow::cycle(bool next)
{ {
MWWorld::Ptr player = MWMechanics::getPlayer();
if (MWBase::Environment::get().getMechanicsManager()->isAttackingOrSpell(player))
return;
const MWMechanics::CreatureStats &stats = player.getClass().getCreatureStats(player);
if (stats.isParalyzed() || stats.getKnockedDown() || stats.isDead() || stats.getHitRecovery())
return;
ItemModel::ModelIndex selected = -1; ItemModel::ModelIndex selected = -1;
// not using mSortFilterModel as we only need sorting, not filtering // not using mSortFilterModel as we only need sorting, not filtering
SortFilterItemModel model(new InventoryItemModel(MWMechanics::getPlayer())); SortFilterItemModel model(new InventoryItemModel(player));
model.setSortByType(false); model.setSortByType(false);
model.update(); model.update();
if (model.getItemCount() == 0) if (model.getItemCount() == 0)

@ -136,7 +136,7 @@ namespace MWGui
*/ */
value.setBase(std::min(100, value.getBase()+1)); value.setBase(std::min(100, value.getBase()+1));
else else
value.setBase(value.getBase()-1); value.setBase(std::max(0, value.getBase()-1));
} }
const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>(); const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();

@ -82,7 +82,7 @@ namespace
AddEntry::operator () (entry); AddEntry::operator () (entry);
mTypesetter->sectionBreak (10); mTypesetter->sectionBreak (30);
} }
}; };
@ -107,7 +107,7 @@ namespace
mTypesetter->selectContent (mContentId); mTypesetter->selectContent (mContentId);
mTypesetter->write (mBodyStyle, 2, 3);// end quote mTypesetter->write (mBodyStyle, 2, 3);// end quote
mTypesetter->sectionBreak (10); mTypesetter->sectionBreak (30);
} }
}; };
@ -121,7 +121,7 @@ namespace
void operator () (MWGui::JournalViewModel::Utf8Span topicName) void operator () (MWGui::JournalViewModel::Utf8Span topicName)
{ {
mTypesetter->write (mBodyStyle, topicName); mTypesetter->write (mBodyStyle, topicName);
mTypesetter->sectionBreak (10); mTypesetter->sectionBreak ();
} }
}; };
@ -135,7 +135,7 @@ namespace
void operator () (MWGui::JournalViewModel::Utf8Span topicName) void operator () (MWGui::JournalViewModel::Utf8Span topicName)
{ {
mTypesetter->write (mBodyStyle, topicName); mTypesetter->write (mBodyStyle, topicName);
mTypesetter->sectionBreak (10); mTypesetter->sectionBreak ();
} }
}; };
} }
@ -250,7 +250,7 @@ book JournalBooks::createTopicIndexBook ()
BookTypesetter::Ptr JournalBooks::createTypesetter () BookTypesetter::Ptr JournalBooks::createTypesetter ()
{ {
//TODO: determine page size from layout... //TODO: determine page size from layout...
return BookTypesetter::create (240, 300); return BookTypesetter::create (240, 320);
} }
} }

@ -187,12 +187,12 @@ namespace
} }
adjustButton(TopicsBTN); adjustButton(TopicsBTN);
int width = getWidget<MyGUI::Widget>(TopicsBTN)->getSize().width + getWidget<MyGUI::Widget>(QuestsBTN)->getSize().width;
int topicsWidth = getWidget<MyGUI::Widget>(TopicsBTN)->getSize().width; int topicsWidth = getWidget<MyGUI::Widget>(TopicsBTN)->getSize().width;
int pageWidth = getWidget<MyGUI::Widget>(RightBookPage)->getSize().width; int cancelLeft = getWidget<MyGUI::Widget>(CancelBTN)->getPosition().left;
int cancelRight = getWidget<MyGUI::Widget>(CancelBTN)->getPosition().left + getWidget<MyGUI::Widget>(CancelBTN)->getSize().width;
getWidget<MyGUI::Widget>(TopicsBTN)->setPosition((pageWidth - width)/2, getWidget<MyGUI::Widget>(TopicsBTN)->getPosition().top); getWidget<MyGUI::Widget>(TopicsBTN)->setPosition(cancelLeft - topicsWidth, getWidget<MyGUI::Widget>(TopicsBTN)->getPosition().top);
getWidget<MyGUI::Widget>(QuestsBTN)->setPosition((pageWidth - width)/2 + topicsWidth, getWidget<MyGUI::Widget>(QuestsBTN)->getPosition().top); getWidget<MyGUI::Widget>(QuestsBTN)->setPosition(cancelRight, getWidget<MyGUI::Widget>(QuestsBTN)->getPosition().top);
mQuestMode = false; mQuestMode = false;
mAllQuests = false; mAllQuests = false;

@ -102,6 +102,15 @@ namespace MWGui
mBackgroundImage->setVisible(visible); mBackgroundImage->setVisible(visible);
} }
double LoadingScreen::getTargetFrameRate() const
{
double frameRateLimit = MWBase::Environment::get().getFrameRateLimit();
if (frameRateLimit > 0)
return std::min(frameRateLimit, mTargetFrameRate);
else
return mTargetFrameRate;
}
class CopyFramebufferToTextureCallback : public osg::Camera::DrawCallback class CopyFramebufferToTextureCallback : public osg::Camera::DrawCallback
{ {
public: public:
@ -141,7 +150,7 @@ namespace MWGui
if (mViewer->getIncrementalCompileOperation()) if (mViewer->getIncrementalCompileOperation())
{ {
mViewer->getIncrementalCompileOperation()->setMaximumNumOfObjectsToCompilePerFrame(100); mViewer->getIncrementalCompileOperation()->setMaximumNumOfObjectsToCompilePerFrame(100);
mViewer->getIncrementalCompileOperation()->setTargetFrameRate(mTargetFrameRate); mViewer->getIncrementalCompileOperation()->setTargetFrameRate(getTargetFrameRate());
} }
// Assign dummy bounding sphere callback to avoid the bounding sphere of the entire scene being recomputed after each frame of loading // Assign dummy bounding sphere callback to avoid the bounding sphere of the entire scene being recomputed after each frame of loading
@ -210,7 +219,7 @@ namespace MWGui
void LoadingScreen::setProgress (size_t value) void LoadingScreen::setProgress (size_t value)
{ {
// skip expensive update if there isn't enough visible progress // skip expensive update if there isn't enough visible progress
if (value - mProgress < mProgressBar->getScrollRange()/200.f) if (mProgressBar->getWidth() <= 0 || value - mProgress < mProgressBar->getScrollRange()/mProgressBar->getWidth())
return; return;
value = std::min(value, mProgressBar->getScrollRange()-1); value = std::min(value, mProgressBar->getScrollRange()-1);
mProgress = value; mProgress = value;
@ -231,7 +240,7 @@ namespace MWGui
bool LoadingScreen::needToDrawLoadingScreen() bool LoadingScreen::needToDrawLoadingScreen()
{ {
if ( mTimer.time_m() <= mLastRenderTime + (1.0/mTargetFrameRate) * 1000.0) if ( mTimer.time_m() <= mLastRenderTime + (1.0/getTargetFrameRate()) * 1000.0)
return false; return false;
// the minimal delay before a loading screen shows // the minimal delay before a loading screen shows

@ -43,6 +43,8 @@ namespace MWGui
virtual void setVisible(bool visible); virtual void setVisible(bool visible);
double getTargetFrameRate() const;
private: private:
void findSplashScreens(); void findSplashScreens();
bool needToDrawLoadingScreen(); bool needToDrawLoadingScreen();
@ -73,8 +75,6 @@ namespace MWGui
std::vector<std::string> mSplashScreens; std::vector<std::string> mSplashScreens;
// TODO: add releaseGLObjects() for mTexture
osg::ref_ptr<osg::Texture2D> mTexture; osg::ref_ptr<osg::Texture2D> mTexture;
std::unique_ptr<MyGUI::ITexture> mGuiTexture; std::unique_ptr<MyGUI::ITexture> mGuiTexture;

@ -196,7 +196,7 @@ namespace MWGui
InteractiveMessageBox::InteractiveMessageBox(MessageBoxManager& parMessageBoxManager, const std::string& message, const std::vector<std::string>& buttons) InteractiveMessageBox::InteractiveMessageBox(MessageBoxManager& parMessageBoxManager, const std::string& message, const std::vector<std::string>& buttons)
: WindowModal("openmw_interactive_messagebox.layout") : WindowModal(MWBase::Environment::get().getWindowManager()->isGuiMode() ? "openmw_interactive_messagebox_notransp.layout" : "openmw_interactive_messagebox.layout")
, mMessageBoxManager(parMessageBoxManager) , mMessageBoxManager(parMessageBoxManager)
, mButtonPressed(-1) , mButtonPressed(-1)
{ {

@ -14,6 +14,7 @@
#include "../mwworld/esmstore.hpp" #include "../mwworld/esmstore.hpp"
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwbase/windowmanager.hpp" #include "../mwbase/windowmanager.hpp"
@ -36,6 +37,7 @@ namespace MWGui
, mItemSelectionDialog(0) , mItemSelectionDialog(0)
, mMagicSelectionDialog(0) , mMagicSelectionDialog(0)
, mSelectedIndex(-1) , mSelectedIndex(-1)
, mActivatedIndex(-1)
{ {
getWidget(mOkButton, "OKButton"); getWidget(mOkButton, "OKButton");
getWidget(mInstructionLabel, "InstructionLabel"); getWidget(mInstructionLabel, "InstructionLabel");
@ -69,6 +71,8 @@ namespace MWGui
void QuickKeysMenu::clear() void QuickKeysMenu::clear()
{ {
mActivatedIndex = -1;
for (int i=0; i<10; ++i) for (int i=0; i<10; ++i)
{ {
unassign(mQuickKeyButtons[i], i); unassign(mQuickKeyButtons[i], i);
@ -254,6 +258,15 @@ namespace MWGui
mMagicSelectionDialog->setVisible(false); mMagicSelectionDialog->setVisible(false);
} }
void QuickKeysMenu::updateActivatedQuickKey()
{
// there is no delayed action, nothing to do.
if (mActivatedIndex < 0)
return;
activateQuickKey(mActivatedIndex);
}
void QuickKeysMenu::activateQuickKey(int index) void QuickKeysMenu::activateQuickKey(int index)
{ {
assert (index-1 >= 0); assert (index-1 >= 0);
@ -263,6 +276,27 @@ namespace MWGui
MWWorld::Ptr player = MWMechanics::getPlayer(); MWWorld::Ptr player = MWMechanics::getPlayer();
MWWorld::InventoryStore& store = player.getClass().getInventoryStore(player); MWWorld::InventoryStore& store = player.getClass().getInventoryStore(player);
const MWMechanics::CreatureStats &playerStats = player.getClass().getCreatureStats(player);
// Delay action executing,
// if player is busy for now (casting a spell, attacking someone, etc.)
bool isDelayNeeded = MWBase::Environment::get().getMechanicsManager()->isAttackingOrSpell(player)
|| playerStats.getKnockedDown()
|| playerStats.getHitRecovery();
bool isReturnNeeded = playerStats.isParalyzed() || playerStats.isDead();
if (isReturnNeeded && type != Type_Item)
{
return;
}
if (isDelayNeeded && type != Type_Item)
{
mActivatedIndex = index;
return;
}
else
mActivatedIndex = -1;
if (type == Type_Item || type == Type_MagicItem) if (type == Type_Item || type == Type_MagicItem)
{ {
@ -309,6 +343,21 @@ namespace MWGui
else if (type == Type_Item) else if (type == Type_Item)
{ {
MWWorld::Ptr item = *button->getUserData<MWWorld::Ptr>(); MWWorld::Ptr item = *button->getUserData<MWWorld::Ptr>();
bool isWeapon = item.getTypeName() == typeid(ESM::Weapon).name();
// delay weapon switching if player is busy
if (isDelayNeeded && isWeapon)
{
mActivatedIndex = index;
return;
}
// disable weapon switching if player is dead or paralyzed
if (isReturnNeeded && isWeapon)
{
return;
}
MWBase::Environment::get().getWindowManager()->useItem(item); MWBase::Environment::get().getWindowManager()->useItem(item);
MWWorld::ConstContainerStoreIterator rightHand = store.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); MWWorld::ConstContainerStoreIterator rightHand = store.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
// change draw state only if the item is in player's right hand // change draw state only if the item is in player's right hand

@ -36,6 +36,7 @@ namespace MWGui
void onAssignMagicCancel (); void onAssignMagicCancel ();
void activateQuickKey(int index); void activateQuickKey(int index);
void updateActivatedQuickKey();
/// @note This enum is serialized, so don't move the items around! /// @note This enum is serialized, so don't move the items around!
enum QuickKeyType enum QuickKeyType
@ -64,7 +65,7 @@ namespace MWGui
MagicSelectionDialog* mMagicSelectionDialog; MagicSelectionDialog* mMagicSelectionDialog;
int mSelectedIndex; int mSelectedIndex;
int mActivatedIndex;
void onQuickKeyButtonClicked(MyGUI::Widget* sender); void onQuickKeyButtonClicked(MyGUI::Widget* sender);
void onOkButtonClicked(MyGUI::Widget* sender); void onOkButtonClicked(MyGUI::Widget* sender);

@ -20,9 +20,8 @@ namespace MWGui
{ {
MWWorld::CellStore* playerCell = MWMechanics::getPlayer().getCell(); MWWorld::CellStore* playerCell = MWMechanics::getPlayer().getCell();
// check if player has changed cell, or count of the reference has become 0 // check if count of the reference has become 0
if ((playerCell != mCurrentPlayerCell && mCurrentPlayerCell != NULL) if (!mPtr.isEmpty() && mPtr.getRefData().getCount() == 0)
|| (!mPtr.isEmpty() && mPtr.getRefData().getCount() == 0))
{ {
if (!mPtr.isEmpty()) if (!mPtr.isEmpty())
{ {

@ -180,7 +180,7 @@ namespace MWGui
void ReviewDialog::setFatigue(const MWMechanics::DynamicStat<float>& value) void ReviewDialog::setFatigue(const MWMechanics::DynamicStat<float>& value)
{ {
int current = std::max(0, static_cast<int>(value.getCurrent())); int current = static_cast<int>(value.getCurrent());
int modified = static_cast<int>(value.getModified()); int modified = static_cast<int>(value.getModified());
mFatigue->setValue(current, modified); mFatigue->setValue(current, modified);

@ -20,6 +20,7 @@
#include "../mwbase/windowmanager.hpp" #include "../mwbase/windowmanager.hpp"
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwworld/inventorystore.hpp" #include "../mwworld/inventorystore.hpp"
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
@ -216,6 +217,15 @@ namespace MWGui
void SpellWindow::cycle(bool next) void SpellWindow::cycle(bool next)
{ {
MWWorld::Ptr player = MWMechanics::getPlayer();
if (MWBase::Environment::get().getMechanicsManager()->isAttackingOrSpell(player))
return;
const MWMechanics::CreatureStats &stats = player.getClass().getCreatureStats(player);
if (stats.isParalyzed() || stats.getKnockedDown() || stats.isDead() || stats.getHitRecovery())
return;
mSpellView->setModel(new SpellModel(MWMechanics::getPlayer())); mSpellView->setModel(new SpellModel(MWMechanics::getPlayer()));
SpellModel::ModelIndex selected = 0; SpellModel::ModelIndex selected = 0;

@ -102,12 +102,13 @@ namespace MWGui
{ {
MyGUI::ProgressBar* pt; MyGUI::ProgressBar* pt;
getWidget(pt, name); getWidget(pt, name);
pt->setProgressRange(max);
pt->setProgressPosition(val);
std::stringstream out; std::stringstream out;
out << val << "/" << max; out << val << "/" << max;
setText(tname, out.str().c_str()); setText(tname, out.str().c_str());
pt->setProgressRange(std::max(0, max));
pt->setProgressPosition(std::max(0, val));
} }
void StatsWindow::setPlayerName(const std::string& playerName) void StatsWindow::setPlayerName(const std::string& playerName)
@ -147,9 +148,13 @@ namespace MWGui
void StatsWindow::setValue (const std::string& id, const MWMechanics::DynamicStat<float>& value) void StatsWindow::setValue (const std::string& id, const MWMechanics::DynamicStat<float>& value)
{ {
int current = std::max(0, static_cast<int>(value.getCurrent())); int current = static_cast<int>(value.getCurrent());
int modified = static_cast<int>(value.getModified()); int modified = static_cast<int>(value.getModified());
// Fatigue can be negative
if (id != "FBar")
current = std::max(0, current);
setBar (id, id + "T", current, modified); setBar (id, id + "T", current, modified);
// health, magicka, fatigue tooltip // health, magicka, fatigue tooltip

@ -359,12 +359,11 @@ namespace MWGui
{ {
if(!mFocusObject.isEmpty()) if(!mFocusObject.isEmpty())
{ {
const MWWorld::CellRef& cellref = mFocusObject.getCellRef();
MWWorld::Ptr ptr = MWMechanics::getPlayer(); MWWorld::Ptr ptr = MWMechanics::getPlayer();
MWWorld::Ptr victim; MWWorld::Ptr victim;
MWBase::MechanicsManager* mm = MWBase::Environment::get().getMechanicsManager(); MWBase::MechanicsManager* mm = MWBase::Environment::get().getMechanicsManager();
bool allowed = mm->isAllowedToUse(ptr, cellref, victim); bool allowed = mm->isAllowedToUse(ptr, mFocusObject, victim);
return !allowed; return !allowed;
} }
@ -378,17 +377,10 @@ namespace MWGui
{ {
mDynamicToolTipBox->setVisible(true); mDynamicToolTipBox->setVisible(true);
if(mShowOwned == 1 || mShowOwned == 3) if((mShowOwned == 1 || mShowOwned == 3) && isFocusObject && checkOwned())
{ mDynamicToolTipBox->changeWidgetSkin(MWBase::Environment::get().getWindowManager()->isGuiMode() ? "HUD_Box_NoTransp_Owned" : "HUD_Box_Owned");
if(isFocusObject && checkOwned()) else
{ mDynamicToolTipBox->changeWidgetSkin(MWBase::Environment::get().getWindowManager()->isGuiMode() ? "HUD_Box_NoTransp" : "HUD_Box");
mDynamicToolTipBox->changeWidgetSkin("HUD_Box_NoTransp_Owned");
}
else
{
mDynamicToolTipBox->changeWidgetSkin("HUD_Box_NoTransp");
}
}
std::string caption = info.caption; std::string caption = info.caption;
std::string image = info.icon; std::string image = info.icon;

@ -311,9 +311,9 @@ namespace MWGui
if (msg.find("%s") != std::string::npos) if (msg.find("%s") != std::string::npos)
msg.replace(msg.find("%s"), 2, it->mBase.getClass().getName(it->mBase)); msg.replace(msg.find("%s"), 2, it->mBase.getClass().getName(it->mBase));
MWBase::Environment::get().getWindowManager()->messageBox(msg); MWBase::Environment::get().getWindowManager()->messageBox(msg);
MWBase::Environment::get().getMechanicsManager()->commitCrime(player, mPtr, MWBase::MechanicsManager::OT_Theft,
it->mBase.getClass().getValue(it->mBase) MWBase::Environment::get().getMechanicsManager()->confiscateStolenItemToOwner(player, it->mBase, mPtr, it->mCount);
* it->mCount, true);
onCancelButtonClicked(mCancelButton); onCancelButtonClicked(mCancelButton);
MWBase::Environment::get().getDialogueManager()->goodbyeSelected(); MWBase::Environment::get().getDialogueManager()->goodbyeSelected();
return; return;

@ -134,7 +134,7 @@ namespace MWGui
void WaitDialog::startWaiting(int hoursToWait) void WaitDialog::startWaiting(int hoursToWait)
{ {
if(Settings::Manager::getBool("autosave","Saves") && mSleeping) //autosaves when enabled and sleeping if(Settings::Manager::getBool("autosave","Saves")) //autosaves when enabled
MWBase::Environment::get().getStateManager()->quickSave("Autosave"); MWBase::Environment::get().getStateManager()->quickSave("Autosave");
MWBase::World* world = MWBase::Environment::get().getWorld(); MWBase::World* world = MWBase::Environment::get().getWorld();

@ -502,11 +502,10 @@ namespace MWGui
if (mBarWidget) if (mBarWidget)
{ {
mBarWidget->setProgressRange(mMax); mBarWidget->setProgressRange(std::max(0, mMax));
mBarWidget->setProgressPosition(mValue); mBarWidget->setProgressPosition(std::max(0, mValue));
} }
if (mBarTextWidget) if (mBarTextWidget)
{ {
std::stringstream out; std::stringstream out;

@ -529,6 +529,8 @@ namespace MWGui
cleanupGarbage(); cleanupGarbage();
mHud->update(); mHud->update();
updateActivatedQuickKey ();
} }
void WindowManager::updateVisible() void WindowManager::updateVisible()
@ -916,19 +918,30 @@ namespace MWGui
if (block) if (block)
{ {
osg::Timer frameTimer;
while (mMessageBoxManager->readPressedButton(false) == -1 while (mMessageBoxManager->readPressedButton(false) == -1
&& !MWBase::Environment::get().getStateManager()->hasQuitRequest()) && !MWBase::Environment::get().getStateManager()->hasQuitRequest())
{ {
mMessageBoxManager->onFrame(0.f); double dt = frameTimer.time_s();
MWBase::Environment::get().getInputManager()->update(0, true, false); frameTimer.setStartTick();
mMessageBoxManager->onFrame(dt);
MWBase::Environment::get().getInputManager()->update(dt, true, false);
if (!MWBase::Environment::get().getInputManager()->isWindowVisible())
OpenThreads::Thread::microSleep(5000);
else
{
mViewer->eventTraversal();
mViewer->updateTraversal();
mViewer->renderingTraversals();
}
// at the time this function is called we are in the middle of a frame, // at the time this function is called we are in the middle of a frame,
// so out of order calls are necessary to get a correct frameNumber for the next frame. // so out of order calls are necessary to get a correct frameNumber for the next frame.
// refer to the advance() and frame() order in Engine::go() // refer to the advance() and frame() order in Engine::go()
mViewer->eventTraversal();
mViewer->updateTraversal();
mViewer->renderingTraversals();
mViewer->advance(mViewer->getFrameStamp()->getSimulationTime()); mViewer->advance(mViewer->getFrameStamp()->getSimulationTime());
MWBase::Environment::get().limitFrameRate(frameTimer.time_s());
} }
} }
} }
@ -1566,6 +1579,11 @@ namespace MWGui
mHud->setCrosshairVisible (show && mCrosshairEnabled); mHud->setCrosshairVisible (show && mCrosshairEnabled);
} }
void WindowManager::updateActivatedQuickKey ()
{
mQuickKeysMenu->updateActivatedQuickKey();
}
void WindowManager::activateQuickKey (int index) void WindowManager::activateQuickKey (int index)
{ {
mQuickKeysMenu->activateQuickKey(index); mQuickKeysMenu->activateQuickKey(index);
@ -1869,18 +1887,28 @@ namespace MWGui
if (mVideoWidget->hasAudioStream()) if (mVideoWidget->hasAudioStream())
MWBase::Environment::get().getSoundManager()->pauseSounds( MWBase::Environment::get().getSoundManager()->pauseSounds(
MWBase::SoundManager::Play_TypeMask&(~MWBase::SoundManager::Play_TypeMovie)); MWBase::SoundManager::Play_TypeMask&(~MWBase::SoundManager::Play_TypeMovie));
osg::Timer frameTimer;
while (mVideoWidget->update() && !MWBase::Environment::get().getStateManager()->hasQuitRequest()) while (mVideoWidget->update() && !MWBase::Environment::get().getStateManager()->hasQuitRequest())
{ {
MWBase::Environment::get().getInputManager()->update(0, true, false); double dt = frameTimer.time_s();
frameTimer.setStartTick();
MWBase::Environment::get().getInputManager()->update(dt, true, false);
if (!MWBase::Environment::get().getInputManager()->isWindowVisible())
OpenThreads::Thread::microSleep(5000);
else
{
mViewer->eventTraversal();
mViewer->updateTraversal();
mViewer->renderingTraversals();
}
// at the time this function is called we are in the middle of a frame, // at the time this function is called we are in the middle of a frame,
// so out of order calls are necessary to get a correct frameNumber for the next frame. // so out of order calls are necessary to get a correct frameNumber for the next frame.
// refer to the advance() and frame() order in Engine::go() // refer to the advance() and frame() order in Engine::go()
mViewer->eventTraversal();
mViewer->updateTraversal();
mViewer->renderingTraversals();
mViewer->advance(mViewer->getFrameStamp()->getSimulationTime()); mViewer->advance(mViewer->getFrameStamp()->getSimulationTime());
MWBase::Environment::get().limitFrameRate(frameTimer.time_s());
} }
mVideoWidget->stop(); mVideoWidget->stop();
@ -2021,12 +2049,8 @@ namespace MWGui
char* text=0; char* text=0;
text = SDL_GetClipboardText(); text = SDL_GetClipboardText();
if (text) if (text)
{ _data = MyGUI::TextIterator::toTagsString(text);
// MyGUI's clipboard might still have color information, to retain that information, only set the new text
// if it actually changed (clipboard inserted by an external application)
if (MyGUI::TextIterator::getOnlyText(_data) != text)
_data = text;
}
SDL_free(text); SDL_free(text);
} }

@ -251,7 +251,10 @@ namespace MWGui
virtual void setSpellVisibility(bool visible); virtual void setSpellVisibility(bool visible);
virtual void setSneakVisibility(bool visible); virtual void setSneakVisibility(bool visible);
virtual void activateQuickKey (int index); /// activate selected quick key
virtual void activateQuickKey (int index);
/// update activated quick key state (if action executing was delayed for some reason)
virtual void updateActivatedQuickKey ();
virtual std::string getSelectedSpell() { return mSelectedSpell; } virtual std::string getSelectedSpell() { return mSelectedSpell; }
virtual void setSelectedSpell(const std::string& spellId, int successChancePercent); virtual void setSelectedSpell(const std::string& spellId, int successChancePercent);

@ -32,6 +32,7 @@
#include "../mwbase/windowmanager.hpp" #include "../mwbase/windowmanager.hpp"
#include "../mwbase/statemanager.hpp" #include "../mwbase/statemanager.hpp"
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwworld/player.hpp" #include "../mwworld/player.hpp"
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
@ -966,6 +967,9 @@ namespace MWInput
inventory.getSelectedEnchantItem() == inventory.end()) inventory.getSelectedEnchantItem() == inventory.end())
return; return;
if (MWBase::Environment::get().getMechanicsManager()->isAttackingOrSpell(mPlayer->getPlayer()))
return;
MWMechanics::DrawState_ state = mPlayer->getDrawState(); MWMechanics::DrawState_ state = mPlayer->getDrawState();
if (state == MWMechanics::DrawState_Weapon || state == MWMechanics::DrawState_Nothing) if (state == MWMechanics::DrawState_Weapon || state == MWMechanics::DrawState_Nothing)
mPlayer->setDrawState(MWMechanics::DrawState_Spell); mPlayer->setDrawState(MWMechanics::DrawState_Spell);
@ -981,6 +985,9 @@ namespace MWInput
if (!mControlSwitch["playerfighting"] || !mControlSwitch["playercontrols"]) if (!mControlSwitch["playerfighting"] || !mControlSwitch["playercontrols"])
return; return;
if (MWBase::Environment::get().getMechanicsManager()->isAttackingOrSpell(mPlayer->getPlayer()))
return;
MWMechanics::DrawState_ state = mPlayer->getDrawState(); MWMechanics::DrawState_ state = mPlayer->getDrawState();
if (state == MWMechanics::DrawState_Spell || state == MWMechanics::DrawState_Nothing) if (state == MWMechanics::DrawState_Spell || state == MWMechanics::DrawState_Nothing)
mPlayer->setDrawState(MWMechanics::DrawState_Weapon); mPlayer->setDrawState(MWMechanics::DrawState_Weapon);
@ -1070,6 +1077,7 @@ namespace MWInput
return; return;
if(MWBase::Environment::get().getWindowManager()->getMode() != MWGui::GM_Journal if(MWBase::Environment::get().getWindowManager()->getMode() != MWGui::GM_Journal
&& MWBase::Environment::get().getWindowManager()->getMode() != MWGui::GM_MainMenu
&& MWBase::Environment::get().getWindowManager ()->getJournalAllowed()) && MWBase::Environment::get().getWindowManager ()->getJournalAllowed())
{ {
MWBase::Environment::get().getWindowManager()->playSound ("book open"); MWBase::Environment::get().getWindowManager()->playSound ("book open");

@ -222,10 +222,23 @@ namespace MWMechanics
} }
} }
void ActiveSpells::purgeAll(float chance) void ActiveSpells::purgeAll(float chance, bool spellOnly)
{ {
for (TContainer::iterator it = mSpells.begin(); it != mSpells.end(); ) for (TContainer::iterator it = mSpells.begin(); it != mSpells.end(); )
{ {
const std::string spellId = it->first;
// if spellOnly is true, dispell only spells. Leave potions, enchanted items etc.
if (spellOnly)
{
const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().search(spellId);
if (!spell || spell->mData.mType != ESM::Spell::ST_Spell)
{
++it;
continue;
}
}
if (Misc::Rng::roll0to99() < chance) if (Misc::Rng::roll0to99() < chance)
mSpells.erase(it++); mSpells.erase(it++);
else else

@ -89,7 +89,7 @@ namespace MWMechanics
void purgeEffect (short effectId, const std::string& sourceId); void purgeEffect (short effectId, const std::string& sourceId);
/// Remove all active effects, if roll succeeds (for each effect) /// Remove all active effects, if roll succeeds (for each effect)
void purgeAll (float chance); void purgeAll(float chance, bool spellOnly = false);
/// Remove all effects with CASTER_LINKED flag that were cast by \a casterActorId /// Remove all effects with CASTER_LINKED flag that were cast by \a casterActorId
void purge (int casterActorId); void purge (int casterActorId);

@ -832,7 +832,7 @@ namespace MWMechanics
creatureStats.getActiveSpells().visitEffectSources(updateSummonedCreatures); creatureStats.getActiveSpells().visitEffectSources(updateSummonedCreatures);
if (ptr.getClass().hasInventoryStore(ptr)) if (ptr.getClass().hasInventoryStore(ptr))
ptr.getClass().getInventoryStore(ptr).visitEffectSources(updateSummonedCreatures); ptr.getClass().getInventoryStore(ptr).visitEffectSources(updateSummonedCreatures);
updateSummonedCreatures.process(); updateSummonedCreatures.process(mTimerDisposeSummonsCorpses == 0.f);
} }
} }
@ -851,6 +851,26 @@ namespace MWMechanics
} }
} }
bool Actors::isRunning(const MWWorld::Ptr& ptr)
{
PtrActorMap::iterator it = mActors.find(ptr);
if (it == mActors.end())
return false;
CharacterController* ctrl = it->second->getCharacterController();
return ctrl->isRunning();
}
bool Actors::isSneaking(const MWWorld::Ptr& ptr)
{
PtrActorMap::iterator it = mActors.find(ptr);
if (it == mActors.end())
return false;
CharacterController* ctrl = it->second->getCharacterController();
return ctrl->isSneaking();
}
void Actors::updateDrowning(const MWWorld::Ptr& ptr, float duration) void Actors::updateDrowning(const MWWorld::Ptr& ptr, float duration)
{ {
PtrActorMap::iterator it = mActors.find(ptr); PtrActorMap::iterator it = mActors.find(ptr);
@ -1080,7 +1100,9 @@ namespace MWMechanics
} }
} }
Actors::Actors() {} Actors::Actors() {
mTimerDisposeSummonsCorpses = 0.2f; // We should add a delay between summoned creature death and its corpse despawning
}
Actors::~Actors() Actors::~Actors()
{ {
@ -1149,6 +1171,7 @@ namespace MWMechanics
// target lists get updated once every 1.0 sec // target lists get updated once every 1.0 sec
if (timerUpdateAITargets >= 1.0f) timerUpdateAITargets = 0; if (timerUpdateAITargets >= 1.0f) timerUpdateAITargets = 0;
if (timerUpdateHeadTrack >= 0.3f) timerUpdateHeadTrack = 0; if (timerUpdateHeadTrack >= 0.3f) timerUpdateHeadTrack = 0;
if (mTimerDisposeSummonsCorpses >= 0.2f) mTimerDisposeSummonsCorpses = 0;
if (timerUpdateEquippedLight >= updateEquippedLightInterval) timerUpdateEquippedLight = 0; if (timerUpdateEquippedLight >= updateEquippedLightInterval) timerUpdateEquippedLight = 0;
MWWorld::Ptr player = getPlayer(); MWWorld::Ptr player = getPlayer();
@ -1318,6 +1341,7 @@ namespace MWMechanics
timerUpdateAITargets += duration; timerUpdateAITargets += duration;
timerUpdateHeadTrack += duration; timerUpdateHeadTrack += duration;
timerUpdateEquippedLight += duration; timerUpdateEquippedLight += duration;
mTimerDisposeSummonsCorpses += duration;
// Looping magic VFX update // Looping magic VFX update
// Note: we need to do this before any of the animations are updated. // Note: we need to do this before any of the animations are updated.
@ -1955,6 +1979,16 @@ namespace MWMechanics
return it->second->getCharacterController()->isReadyToBlock(); return it->second->getCharacterController()->isReadyToBlock();
} }
bool Actors::isAttackingOrSpell(const MWWorld::Ptr& ptr) const
{
PtrActorMap::const_iterator it = mActors.find(ptr);
if (it == mActors.end())
return false;
CharacterController* ctrl = it->second->getCharacterController();
return ctrl->isAttackingOrSpell();
}
void Actors::fastForwardAi() void Actors::fastForwardAi()
{ {
if (!MWBase::Environment::get().getMechanicsManager()->isAIActive()) if (!MWBase::Environment::get().getMechanicsManager()->isAIActive())

@ -117,6 +117,9 @@ namespace MWMechanics
End of tes3mp addition End of tes3mp addition
*/ */
bool isRunning(const MWWorld::Ptr& ptr);
bool isSneaking(const MWWorld::Ptr& ptr);
void forceStateUpdate(const MWWorld::Ptr &ptr); void forceStateUpdate(const MWWorld::Ptr &ptr);
bool playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number, bool persist=false); bool playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number, bool persist=false);
@ -157,9 +160,11 @@ namespace MWMechanics
void clear(); // Clear death counter void clear(); // Clear death counter
bool isReadyToBlock(const MWWorld::Ptr& ptr) const; bool isReadyToBlock(const MWWorld::Ptr& ptr) const;
bool isAttackingOrSpell(const MWWorld::Ptr& ptr) const;
private: private:
PtrActorMap mActors; PtrActorMap mActors;
float mTimerDisposeSummonsCorpses;
}; };
} }

@ -71,6 +71,8 @@ namespace MWMechanics
{ {
const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().find(mItem->getClass().getEnchantment(*mItem)); const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().find(mItem->getClass().getEnchantment(*mItem));
int types = getRangeTypes(enchantment->mEffects); int types = getRangeTypes(enchantment->mEffects);
isRanged = (types & RangeTypes::Target) | (types & RangeTypes::Self);
return suggestCombatRange(types); return suggestCombatRange(types);
} }

@ -54,7 +54,7 @@ namespace MWMechanics
virtual float getCombatRange (bool& isRanged) const; virtual float getCombatRange (bool& isRanged) const;
/// Since this action has no animation, apply a small cool down for using it /// Since this action has no animation, apply a small cool down for using it
virtual float getActionCooldown() { return 1.f; } virtual float getActionCooldown() { return 0.75f; }
}; };
class ActionPotion : public Action class ActionPotion : public Action
@ -68,7 +68,7 @@ namespace MWMechanics
virtual bool isAttackingOrSpell() const { return false; } virtual bool isAttackingOrSpell() const { return false; }
/// Since this action has no animation, apply a small cool down for using it /// Since this action has no animation, apply a small cool down for using it
virtual float getActionCooldown() { return 1.f; } virtual float getActionCooldown() { return 0.75f; }
}; };
class ActionWeapon : public Action class ActionWeapon : public Action

@ -22,8 +22,15 @@ struct AiFollowStorage : AiTemporaryBase
{ {
float mTimer; float mTimer;
bool mMoving; bool mMoving;
float mTargetAngleRadians;
AiFollowStorage() : mTimer(0.f), mMoving(false) {} bool mTurnActorToTarget;
AiFollowStorage() :
mTimer(0.f),
mMoving(false),
mTargetAngleRadians(0.f),
mTurnActorToTarget(false)
{}
}; };
int AiFollow::mFollowIndexCounter = 0; int AiFollow::mFollowIndexCounter = 0;
@ -73,6 +80,15 @@ bool AiFollow::execute (const MWWorld::Ptr& actor, CharacterController& characte
AiFollowStorage& storage = state.get<AiFollowStorage>(); AiFollowStorage& storage = state.get<AiFollowStorage>();
bool& rotate = storage.mTurnActorToTarget;
if (rotate)
{
if (zTurn(actor, storage.mTargetAngleRadians))
rotate = false;
return false;
}
// AiFollow requires the target to be in range and within sight for the initial activation // AiFollow requires the target to be in range and within sight for the initial activation
if (!mActive) if (!mActive)
{ {
@ -144,13 +160,33 @@ bool AiFollow::execute (const MWWorld::Ptr& actor, CharacterController& characte
//Set the target destination from the actor //Set the target destination from the actor
ESM::Pathgrid::Point dest = target.getRefData().getPosition().pos; ESM::Pathgrid::Point dest = target.getRefData().getPosition().pos;
if (!storage.mMoving) short baseFollowDistance = followDistance;
{ short threshold = 30; // to avoid constant switching between moving/stopping
const short threshold = 10; // to avoid constant switching between moving/stopping if (storage.mMoving)
followDistance -= threshold;
else
followDistance += threshold; followDistance += threshold;
osg::Vec3f targetPos(target.getRefData().getPosition().asVec3());
osg::Vec3f actorPos(actor.getRefData().getPosition().asVec3());
osg::Vec3f dir = targetPos - actorPos;
float targetDistSqr = dir.length2();
if (targetDistSqr <= followDistance * followDistance)
{
float faceAngleRadians = std::atan2(dir.x(), dir.y());
if (!zTurn(actor, faceAngleRadians, osg::DegreesToRadians(45.f)))
{
storage.mTargetAngleRadians = faceAngleRadians;
storage.mTurnActorToTarget = true;
}
return false;
} }
storage.mMoving = !pathTo(actor, dest, duration, followDistance); // Go to the destination storage.mMoving = !pathTo(actor, dest, duration, baseFollowDistance); // Go to the destination
if (storage.mMoving) if (storage.mMoving)
{ {

@ -457,7 +457,9 @@ bool MWMechanics::Alchemy::knownEffect(unsigned int potionEffectIndex, const MWW
static const float fWortChanceValue = static const float fWortChanceValue =
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fWortChanceValue")->getFloat(); MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fWortChanceValue")->getFloat();
return (potionEffectIndex <= 1 && alchemySkill >= fWortChanceValue) return (potionEffectIndex <= 1 && alchemySkill >= fWortChanceValue)
|| (potionEffectIndex <= 3 && alchemySkill >= fWortChanceValue*2); || (potionEffectIndex <= 3 && alchemySkill >= fWortChanceValue*2)
|| (potionEffectIndex <= 5 && alchemySkill >= fWortChanceValue*3)
|| (potionEffectIndex <= 7 && alchemySkill >= fWortChanceValue*4);
} }
MWMechanics::Alchemy::Result MWMechanics::Alchemy::create (const std::string& name) MWMechanics::Alchemy::Result MWMechanics::Alchemy::create (const std::string& name)

@ -1768,7 +1768,7 @@ void CharacterController::update(float duration)
mSecondsOfSwimming -= 1; mSecondsOfSwimming -= 1;
} }
} }
else if(isrunning) else if(isrunning && !sneak)
{ {
mSecondsOfRunning += duration; mSecondsOfRunning += duration;
while(mSecondsOfRunning > 1) while(mSecondsOfRunning > 1)
@ -1806,7 +1806,7 @@ void CharacterController::update(float duration)
else else
fatigueLoss = fFatigueSwimRunBase + encumbrance * fFatigueSwimRunMult; fatigueLoss = fFatigueSwimRunBase + encumbrance * fFatigueSwimRunMult;
} }
if (isrunning) else if (isrunning)
fatigueLoss = fFatigueRunBase + encumbrance * fFatigueRunMult; fatigueLoss = fFatigueRunBase + encumbrance * fFatigueRunMult;
} }
} }
@ -2367,6 +2367,12 @@ bool CharacterController::isKnockedOut() const
return mHitState == CharState_KnockOut; return mHitState == CharState_KnockOut;
} }
bool CharacterController::isAttackingOrSpell() const
{
return mUpperBodyState != UpperCharState_Nothing &&
mUpperBodyState != UpperCharState_WeapEquiped;
}
bool CharacterController::isSneaking() const bool CharacterController::isSneaking() const
{ {
return mIdleState == CharState_IdleSneak || return mIdleState == CharState_IdleSneak ||
@ -2376,6 +2382,18 @@ bool CharacterController::isSneaking() const
mMovementState == CharState_SneakRight; mMovementState == CharState_SneakRight;
} }
bool CharacterController::isRunning() const
{
return mMovementState == CharState_RunForward ||
mMovementState == CharState_RunBack ||
mMovementState == CharState_RunLeft ||
mMovementState == CharState_RunRight ||
mMovementState == CharState_SwimRunForward ||
mMovementState == CharState_SwimRunBack ||
mMovementState == CharState_SwimRunLeft ||
mMovementState == CharState_SwimRunRight;
}
void CharacterController::setAttackingOrSpell(bool attackingOrSpell) void CharacterController::setAttackingOrSpell(bool attackingOrSpell)
{ {
mAttackingOrSpell = attackingOrSpell; mAttackingOrSpell = attackingOrSpell;

@ -266,6 +266,8 @@ public:
bool isReadyToBlock() const; bool isReadyToBlock() const;
bool isKnockedOut() const; bool isKnockedOut() const;
bool isSneaking() const; bool isSneaking() const;
bool isRunning() const;
bool isAttackingOrSpell() const;
void setAttackingOrSpell(bool attackingOrSpell); void setAttackingOrSpell(bool attackingOrSpell);
void setAIAttackType(const std::string& attackType); void setAIAttackType(const std::string& attackType);

@ -26,6 +26,7 @@
#include "../mwworld/inventorystore.hpp" #include "../mwworld/inventorystore.hpp"
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
#include "../mwworld/player.hpp" #include "../mwworld/player.hpp"
#include "../mwworld/ptr.hpp"
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
@ -435,6 +436,16 @@ namespace MWMechanics
mObjects.update(duration, paused); mObjects.update(duration, paused);
} }
bool MechanicsManager::isRunning(const MWWorld::Ptr& ptr)
{
return mActors.isRunning(ptr);
}
bool MechanicsManager::isSneaking(const MWWorld::Ptr& ptr)
{
return mActors.isSneaking(ptr);
}
void MechanicsManager::rest(bool sleep) void MechanicsManager::rest(bool sleep)
{ {
mActors.rest(sleep); mActors.rest(sleep);
@ -845,8 +856,17 @@ namespace MWMechanics
mAI = true; mAI = true;
} }
bool MechanicsManager::isAllowedToUse (const MWWorld::Ptr& ptr, const MWWorld::CellRef& cellref, MWWorld::Ptr& victim) bool MechanicsManager::isAllowedToUse (const MWWorld::Ptr& ptr, const MWWorld::ConstPtr& item, MWWorld::Ptr& victim)
{ {
const MWWorld::CellRef& cellref = item.getCellRef();
// there is no harm to use unlocked doors
if (item.getClass().isDoor() && cellref.getLockLevel() <= 0 && ptr.getCellRef().getTrap().empty())
return true;
// TODO: implement a better check to check if item is owned bed
if (item.getClass().isActivator() && item.getClass().getScript(item).compare(0, 3, "Bed") != 0)
return true;
const std::string& owner = cellref.getOwner(); const std::string& owner = cellref.getOwner();
bool isOwned = !owner.empty() && owner != "player"; bool isOwned = !owner.empty() && owner != "player";
@ -888,7 +908,7 @@ namespace MWMechanics
} }
MWWorld::Ptr victim; MWWorld::Ptr victim;
if (isAllowedToUse(ptr, bed.getCellRef(), victim)) if (isAllowedToUse(ptr, bed, victim))
return false; return false;
if(commitCrime(ptr, victim, OT_SleepingInOwnedBed)) if(commitCrime(ptr, victim, OT_SleepingInOwnedBed))
@ -903,7 +923,7 @@ namespace MWMechanics
void MechanicsManager::objectOpened(const MWWorld::Ptr &ptr, const MWWorld::Ptr &item) void MechanicsManager::objectOpened(const MWWorld::Ptr &ptr, const MWWorld::Ptr &item)
{ {
MWWorld::Ptr victim; MWWorld::Ptr victim;
if (isAllowedToUse(ptr, item.getCellRef(), victim)) if (isAllowedToUse(ptr, item, victim))
return; return;
commitCrime(ptr, victim, OT_Trespassing); commitCrime(ptr, victim, OT_Trespassing);
} }
@ -933,6 +953,43 @@ namespace MWMechanics
return ownerFound != owners.end(); return ownerFound != owners.end();
} }
void MechanicsManager::confiscateStolenItemToOwner(const MWWorld::Ptr &player, const MWWorld::Ptr &item, const MWWorld::Ptr& victim, int count)
{
if (player != getPlayer())
return;
const std::string itemId = Misc::StringUtils::lowerCase(item.getCellRef().getRefId());
StolenItemsMap::iterator stolenIt = mStolenItems.find(itemId);
if (stolenIt == mStolenItems.end())
return;
Owner owner;
owner.first = victim.getCellRef().getRefId();
owner.second = false;
Misc::StringUtils::lowerCaseInPlace(owner.first);
// decrease count of stolen items
int toRemove = std::min(count, mStolenItems[itemId][owner]);
mStolenItems[itemId][owner] -= toRemove;
if (mStolenItems[itemId][owner] == 0)
{
// erase owner from stolen items owners
OwnerMap& owners = stolenIt->second;
OwnerMap::iterator ownersIt = owners.find(owner);
if (ownersIt != owners.end())
owners.erase(ownersIt);
}
MWWorld::ContainerStore& store = player.getClass().getContainerStore(player);
// move items from player to owner and report about theft
victim.getClass().getContainerStore(victim).add(item, toRemove, victim);
store.remove(item, toRemove, player);
commitCrime(player, victim, OT_Theft, item.getClass().getValue(item) * toRemove);
}
void MechanicsManager::confiscateStolenItems(const MWWorld::Ptr &player, const MWWorld::Ptr &targetContainer) void MechanicsManager::confiscateStolenItems(const MWWorld::Ptr &player, const MWWorld::Ptr &targetContainer)
{ {
MWWorld::ContainerStore& store = player.getClass().getContainerStore(player); MWWorld::ContainerStore& store = player.getClass().getContainerStore(player);
@ -986,7 +1043,7 @@ namespace MWMechanics
} }
} }
if (isAllowedToUse(ptr, *ownerCellRef, victim)) if (isAllowedToUse(ptr, item, victim))
return; return;
Owner owner; Owner owner;
@ -1067,11 +1124,6 @@ namespace MWMechanics
End of tes3mp addition End of tes3mp addition
*/ */
if (type == OT_Theft || type == OT_Pickpocket)
MWBase::Environment::get().getDialogueManager()->say(*it, "thief");
else if (type == OT_Trespassing)
MWBase::Environment::get().getDialogueManager()->say(*it, "intruder");
crimeSeen = true; crimeSeen = true;
} }
} }
@ -1173,10 +1225,25 @@ namespace MWMechanics
if (it->getClass().getCreatureStats(*it).getAiSequence().isInCombat(victim)) if (it->getClass().getCreatureStats(*it).getAiSequence().isInCombat(victim))
continue; continue;
// Player's followers should not attack player, or try to arrest him
if (it->getClass().getCreatureStats(*it).getAiSequence().hasPackage(AiPackage::TypeIdFollow))
{
std::set<MWWorld::Ptr> playerFollowers;
getActorsSidingWith(player, playerFollowers);
if (playerFollowers.find(*it) != playerFollowers.end())
continue;
}
// Will the witness report the crime? // Will the witness report the crime?
if (it->getClass().getCreatureStats(*it).getAiSetting(CreatureStats::AI_Alarm).getBase() >= 100) if (it->getClass().getCreatureStats(*it).getAiSetting(CreatureStats::AI_Alarm).getBase() >= 100)
{ {
reported = true; reported = true;
if (type == OT_Theft || type == OT_Pickpocket)
MWBase::Environment::get().getDialogueManager()->say(*it, "thief");
else if (type == OT_Trespassing)
MWBase::Environment::get().getDialogueManager()->say(*it, "intruder");
} }
if (it->getClass().isClass(*it, "guard")) if (it->getClass().isClass(*it, "guard"))
@ -1189,7 +1256,9 @@ namespace MWMechanics
it->getClass().getNpcStats(*it).setCrimeId(id); it->getClass().getNpcStats(*it).setCrimeId(id);
if (!it->getClass().getCreatureStats(*it).getAiSequence().hasPackage(AiPackage::TypeIdPursue)) if (!it->getClass().getCreatureStats(*it).getAiSequence().hasPackage(AiPackage::TypeIdPursue))
{
it->getClass().getCreatureStats(*it).getAiSequence().stack(AiPursue(player), *it); it->getClass().getCreatureStats(*it).getAiSequence().stack(AiPursue(player), *it);
}
} }
else else
{ {
@ -1579,6 +1648,11 @@ namespace MWMechanics
return mActors.isReadyToBlock(ptr); return mActors.isReadyToBlock(ptr);
} }
bool MechanicsManager::isAttackingOrSpell(const MWWorld::Ptr &ptr) const
{
return mActors.isAttackingOrSpell(ptr);
}
void MechanicsManager::setWerewolf(const MWWorld::Ptr& actor, bool werewolf) void MechanicsManager::setWerewolf(const MWWorld::Ptr& actor, bool werewolf)
{ {
MWMechanics::NpcStats& npcStats = actor.getClass().getNpcStats(actor); MWMechanics::NpcStats& npcStats = actor.getClass().getNpcStats(actor);

@ -198,6 +198,8 @@ namespace MWMechanics
virtual void keepPlayerAlive(); virtual void keepPlayerAlive();
virtual bool isReadyToBlock (const MWWorld::Ptr& ptr) const; virtual bool isReadyToBlock (const MWWorld::Ptr& ptr) const;
/// Is \a ptr casting spell or using weapon now?
virtual bool isAttackingOrSpell(const MWWorld::Ptr &ptr) const;
virtual void confiscateStolenItems (const MWWorld::Ptr& player, const MWWorld::Ptr& targetContainer); virtual void confiscateStolenItems (const MWWorld::Ptr& player, const MWWorld::Ptr& targetContainer);
@ -207,15 +209,19 @@ namespace MWMechanics
/// Has the player stolen this item from the given owner? /// Has the player stolen this item from the given owner?
virtual bool isItemStolenFrom(const std::string& itemid, const std::string& ownerid); virtual bool isItemStolenFrom(const std::string& itemid, const std::string& ownerid);
/// @return is \a ptr allowed to take/use \a cellref or is it a crime? /// @return is \a ptr allowed to take/use \a cellref or is it a crime?
virtual bool isAllowedToUse (const MWWorld::Ptr& ptr, const MWWorld::CellRef& cellref, MWWorld::Ptr& victim); virtual bool isAllowedToUse (const MWWorld::Ptr& ptr, const MWWorld::ConstPtr& item, MWWorld::Ptr& victim);
virtual void setWerewolf(const MWWorld::Ptr& actor, bool werewolf); virtual void setWerewolf(const MWWorld::Ptr& actor, bool werewolf);
virtual void applyWerewolfAcrobatics(const MWWorld::Ptr& actor); virtual void applyWerewolfAcrobatics(const MWWorld::Ptr& actor);
virtual void cleanupSummonedCreature(const MWWorld::Ptr& caster, int creatureActorId); virtual void cleanupSummonedCreature(const MWWorld::Ptr& caster, int creatureActorId);
virtual void confiscateStolenItemToOwner(const MWWorld::Ptr &player, const MWWorld::Ptr &item, const MWWorld::Ptr& victim, int count);
virtual bool isRunning(const MWWorld::Ptr& ptr);
virtual bool isSneaking(const MWWorld::Ptr& ptr);
private: private:
void reportCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim, void reportCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim,
OffenseType type, int arg=0); OffenseType type, int arg=0);

@ -137,6 +137,11 @@ namespace MWMechanics
CreatureStats& stats = actor.getClass().getCreatureStats(actor); CreatureStats& stats = actor.getClass().getCreatureStats(actor);
float castBonus = -stats.getMagicEffects().get(ESM::MagicEffect::Sound).getMagnitude();
float castChance = calcSpellBaseSuccessChance(spell, actor, effectiveSchool) + castBonus;
castChance *= stats.getFatigueTerm();
if (stats.getMagicEffects().get(ESM::MagicEffect::Silence).getMagnitude()&& !godmode) if (stats.getMagicEffects().get(ESM::MagicEffect::Silence).getMagnitude()&& !godmode)
return 0; return 0;
@ -154,11 +159,6 @@ namespace MWMechanics
return 100; return 100;
} }
float castBonus = -stats.getMagicEffects().get(ESM::MagicEffect::Sound).getMagnitude();
float castChance = calcSpellBaseSuccessChance(spell, actor, effectiveSchool) + castBonus;
castChance *= stats.getFatigueTerm();
if (!cap) if (!cap)
return std::max(0.f, castChance); return std::max(0.f, castChance);
else else
@ -715,7 +715,7 @@ namespace MWMechanics
} }
else if (target.getClass().isActor() && effectId == ESM::MagicEffect::Dispel) else if (target.getClass().isActor() && effectId == ESM::MagicEffect::Dispel)
{ {
target.getClass().getCreatureStats(target).getActiveSpells().purgeAll(magnitude); target.getClass().getCreatureStats(target).getActiveSpells().purgeAll(magnitude, true);
return true; return true;
} }
else if (target.getClass().isActor() && target == getPlayer()) else if (target.getClass().isActor() && target == getPlayer())

@ -25,6 +25,14 @@ namespace
const MWMechanics::ActiveSpells& activeSpells = actor.getClass().getCreatureStats(actor).getActiveSpells(); const MWMechanics::ActiveSpells& activeSpells = actor.getClass().getCreatureStats(actor).getActiveSpells();
for (MWMechanics::ActiveSpells::TIterator it = activeSpells.begin(); it != activeSpells.end(); ++it) for (MWMechanics::ActiveSpells::TIterator it = activeSpells.begin(); it != activeSpells.end(); ++it)
{ {
// if the effect filter is not specified, take in account only spells effects. Leave potions, enchanted items etc.
if (effectFilter == -1)
{
const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().search(it->first);
if (!spell || spell->mData.mType != ESM::Spell::ST_Spell)
continue;
}
const MWMechanics::ActiveSpells::ActiveSpellParams& params = it->second; const MWMechanics::ActiveSpells::ActiveSpellParams& params = it->second;
for (std::vector<MWMechanics::ActiveSpells::ActiveEffect>::const_iterator effectIt = params.mEffects.begin(); for (std::vector<MWMechanics::ActiveSpells::ActiveEffect>::const_iterator effectIt = params.mEffects.begin();
effectIt != params.mEffects.end(); ++effectIt) effectIt != params.mEffects.end(); ++effectIt)
@ -46,6 +54,26 @@ namespace
} }
return toCure; return toCure;
} }
float getSpellDuration (const MWWorld::Ptr& actor, const std::string& spellId)
{
float duration = 0;
const MWMechanics::ActiveSpells& activeSpells = actor.getClass().getCreatureStats(actor).getActiveSpells();
for (MWMechanics::ActiveSpells::TIterator it = activeSpells.begin(); it != activeSpells.end(); ++it)
{
if (it->first != spellId)
continue;
const MWMechanics::ActiveSpells::ActiveSpellParams& params = it->second;
for (std::vector<MWMechanics::ActiveSpells::ActiveEffect>::const_iterator effectIt = params.mEffects.begin();
effectIt != params.mEffects.end(); ++effectIt)
{
if (effectIt->mDuration > duration)
duration = effectIt->mDuration;
}
}
return duration;
}
} }
namespace MWMechanics namespace MWMechanics
@ -114,15 +142,39 @@ namespace MWMechanics
const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().find(ptr.getClass().getEnchantment(ptr)); const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().find(ptr.getClass().getEnchantment(ptr));
// Spells don't stack, so early out if the spell is still active on the target
int types = getRangeTypes(enchantment->mEffects);
if ((types & Self) && actor.getClass().getCreatureStats(actor).getActiveSpells().isSpellActive(ptr.getCellRef().getRefId()))
return 0.f;
if (types & (Touch|Target) && getSpellDuration(enemy, ptr.getCellRef().getRefId()) > 3)
return 0.f;
if (enchantment->mData.mType == ESM::Enchantment::CastOnce) if (enchantment->mData.mType == ESM::Enchantment::CastOnce)
{ {
return rateEffects(enchantment->mEffects, actor, enemy); return rateEffects(enchantment->mEffects, actor, enemy);
} }
else else if (enchantment->mData.mType == ESM::Enchantment::WhenUsed)
{ {
//if (!ptr.getClass().canBeEquipped(ptr, actor)) MWWorld::InventoryStore& store = actor.getClass().getInventoryStore(actor);
return 0.f;
// Creatures can not wear armor/clothing, so allow creatures to use non-equipped items,
if (actor.getClass().isNpc() && !store.isEquipped(ptr))
return 0.f;
int castCost = getEffectiveEnchantmentCastCost(static_cast<float>(enchantment->mData.mCost), actor);
if (ptr.getCellRef().getEnchantmentCharge() != -1
&& ptr.getCellRef().getEnchantmentCharge() < castCost)
return 0.f;
float rating = rateEffects(enchantment->mEffects, actor, enemy);
rating *= 2; // prefer rechargable magic items over spells
return rating;
} }
return 0.f;
} }
float rateEffect(const ESM::ENAMstruct &effect, const MWWorld::Ptr &actor, const MWWorld::Ptr &enemy) float rateEffect(const ESM::ENAMstruct &effect, const MWWorld::Ptr &actor, const MWWorld::Ptr &enemy)
@ -444,6 +496,15 @@ namespace MWMechanics
break; break;
} }
// Allow only one summoned creature at time
if (isSummoningEffect(effect.mEffectID))
{
MWMechanics::CreatureStats& creatureStats = actor.getClass().getCreatureStats(actor);
if (!creatureStats.getSummonedCreatureMap().empty())
return 0.f;
}
const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(effect.mEffectID); const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(effect.mEffectID);
// Underwater casting not possible // Underwater casting not possible

@ -52,26 +52,10 @@ namespace MWMechanics
} }
} }
void UpdateSummonedCreatures::process() void UpdateSummonedCreatures::process(bool cleanup)
{ {
MWMechanics::CreatureStats& creatureStats = mActor.getClass().getCreatureStats(mActor); MWMechanics::CreatureStats& creatureStats = mActor.getClass().getCreatureStats(mActor);
// Update summon effects
std::map<CreatureStats::SummonKey, int>& creatureMap = creatureStats.getSummonedCreatureMap(); std::map<CreatureStats::SummonKey, int>& creatureMap = creatureStats.getSummonedCreatureMap();
for (std::map<CreatureStats::SummonKey, int>::iterator it = creatureMap.begin(); it != creatureMap.end(); )
{
bool found = mActiveEffects.find(it->first) != mActiveEffects.end();
if (!found)
{
// Effect has ended
MWBase::Environment::get().getMechanicsManager()->cleanupSummonedCreature(mActor, it->second);
creatureMap.erase(it++);
continue;
}
++it;
}
for (std::set<std::pair<int, std::string> >::iterator it = mActiveEffects.begin(); it != mActiveEffects.end(); ++it) for (std::set<std::pair<int, std::string> >::iterator it = mActiveEffects.begin(); it != mActiveEffects.end(); ++it)
{ {
@ -143,21 +127,18 @@ namespace MWMechanics
} }
} }
// Update summon effects
for (std::map<CreatureStats::SummonKey, int>::iterator it = creatureMap.begin(); it != creatureMap.end(); ) for (std::map<CreatureStats::SummonKey, int>::iterator it = creatureMap.begin(); it != creatureMap.end(); )
{ {
MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->searchPtrViaActorId(it->second); bool found = mActiveEffects.find(it->first) != mActiveEffects.end();
if (!ptr.isEmpty() && ptr.getClass().getCreatureStats(ptr).isDead() && ptr.getClass().getCreatureStats(ptr).isDeathAnimationFinished()) if (!found)
{ {
// Purge the magic effect so a new creature can be summoned if desired // Effect has ended
creatureStats.getActiveSpells().purgeEffect(it->first.first, it->first.second);
if (mActor.getClass().hasInventoryStore(ptr))
mActor.getClass().getInventoryStore(mActor).purgeEffect(it->first.first, it->first.second);
MWBase::Environment::get().getMechanicsManager()->cleanupSummonedCreature(mActor, it->second); MWBase::Environment::get().getMechanicsManager()->cleanupSummonedCreature(mActor, it->second);
creatureMap.erase(it++); creatureMap.erase(it++);
continue;
} }
else ++it;
++it;
} }
std::vector<int>& graveyard = creatureStats.getSummonedCreatureGraveyard(); std::vector<int>& graveyard = creatureStats.getSummonedCreatureGraveyard();
@ -192,6 +173,26 @@ namespace MWMechanics
else else
++it; ++it;
} }
if (!cleanup)
return;
for (std::map<CreatureStats::SummonKey, int>::iterator it = creatureMap.begin(); it != creatureMap.end(); )
{
MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->searchPtrViaActorId(it->second);
if (ptr.isEmpty() || (ptr.getClass().getCreatureStats(ptr).isDead() && ptr.getClass().getCreatureStats(ptr).isDeathAnimationFinished()))
{
// Purge the magic effect so a new creature can be summoned if desired
creatureStats.getActiveSpells().purgeEffect(it->first.first, it->first.second);
if (mActor.getClass().hasInventoryStore(mActor))
mActor.getClass().getInventoryStore(mActor).purgeEffect(it->first.first, it->first.second);
MWBase::Environment::get().getMechanicsManager()->cleanupSummonedCreature(mActor, it->second);
creatureMap.erase(it++);
}
else
++it;
}
} }
} }

@ -22,7 +22,7 @@ namespace MWMechanics
float magnitude, float remainingTime = -1, float totalTime = -1); float magnitude, float remainingTime = -1, float totalTime = -1);
/// To call after all effect sources have been visited /// To call after all effect sources have been visited
void process(); void process(bool cleanup);
private: private:
MWWorld::Ptr mActor; MWWorld::Ptr mActor;

@ -13,6 +13,7 @@
#include "combat.hpp" #include "combat.hpp"
#include "aicombataction.hpp" #include "aicombataction.hpp"
#include "spellpriority.hpp" #include "spellpriority.hpp"
#include "spellcasting.hpp"
namespace MWMechanics namespace MWMechanics
{ {
@ -90,10 +91,13 @@ namespace MWMechanics
if (!weapon->mEnchant.empty()) if (!weapon->mEnchant.empty())
{ {
const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().find(weapon->mEnchant); const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().find(weapon->mEnchant);
if (enchantment->mData.mType == ESM::Enchantment::WhenStrikes if (enchantment->mData.mType == ESM::Enchantment::WhenStrikes)
&& (item.getCellRef().getEnchantmentCharge() == -1 {
|| item.getCellRef().getEnchantmentCharge() >= enchantment->mData.mCost)) int castCost = getEffectiveEnchantmentCastCost(static_cast<float>(enchantment->mData.mCost), actor);
rating += rateEffects(enchantment->mEffects, actor, enemy);
if (item.getCellRef().getEnchantmentCharge() == -1 || item.getCellRef().getEnchantmentCharge() >= castCost)
rating += rateEffects(enchantment->mEffects, actor, enemy);
}
} }
int skill = item.getClass().getEquipmentSkill(item); int skill = item.getClass().getEquipmentSkill(item);

@ -9,12 +9,14 @@
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/inputmanager.hpp" #include "../mwbase/inputmanager.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
#include "../mwworld/ptr.hpp" #include "../mwworld/ptr.hpp"
#include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/npcstats.hpp"
#include "../mwmechanics/movement.hpp"
#include "interpretercontext.hpp" #include "interpretercontext.hpp"
#include "ref.hpp" #include "ref.hpp"
@ -167,7 +169,11 @@ namespace MWScript
virtual void execute (Interpreter::Runtime& runtime) virtual void execute (Interpreter::Runtime& runtime)
{ {
MWWorld::Ptr ptr = MWBase::Environment::get().getWorld ()->getPlayerPtr(); MWWorld::Ptr ptr = MWBase::Environment::get().getWorld ()->getPlayerPtr();
runtime.push (ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Run)); const MWWorld::Class &cls = ptr.getClass();
bool isRunning = MWBase::Environment::get().getMechanicsManager()->isRunning(ptr);
runtime.push (isRunning && cls.getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Run));
} }
}; };
@ -177,8 +183,12 @@ namespace MWScript
virtual void execute (Interpreter::Runtime& runtime) virtual void execute (Interpreter::Runtime& runtime)
{ {
MWWorld::Ptr ptr = MWBase::Environment::get().getWorld ()->getPlayerPtr(); MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getPlayerPtr();
runtime.push (ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Sneak)); const MWWorld::Class &cls = ptr.getClass();
bool isSneaking = MWBase::Environment::get().getMechanicsManager()->isSneaking(ptr);
runtime.push (isSneaking && cls.getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Sneak));
} }
}; };

@ -3,6 +3,7 @@
#include <iostream> #include <iostream>
#include <algorithm> #include <algorithm>
#include <map> #include <map>
#include <numeric>
#include <osg/Matrixf> #include <osg/Matrixf>
@ -34,7 +35,7 @@
namespace MWSound namespace MWSound
{ {
SoundManager::SoundManager(const VFS::Manager* vfs, const std::map<std::string,std::string>& fallbackMap, bool useSound) SoundManager::SoundManager(const VFS::Manager* vfs, const std::map<std::string, std::string>& fallbackMap, bool useSound)
: mVFS(vfs) : mVFS(vfs)
, mFallback(fallbackMap) , mFallback(fallbackMap)
, mOutput(new DEFAULT_OUTPUT(*this)) , mOutput(new DEFAULT_OUTPUT(*this))
@ -271,7 +272,6 @@ namespace MWSound
return sound; return sound;
} }
// Gets the combined volume settings for the given sound type // Gets the combined volume settings for the given sound type
float SoundManager::volumeFromType(PlayType type) const float SoundManager::volumeFromType(PlayType type) const
{ {
@ -298,7 +298,6 @@ namespace MWSound
return volume; return volume;
} }
void SoundManager::stopMusic() void SoundManager::stopMusic()
{ {
if(mMusic) if(mMusic)
@ -328,14 +327,28 @@ namespace MWSound
} }
} }
void SoundManager::advanceMusic(const std::string& filename)
{
if (!isMusicPlaying())
{
streamMusicFull(filename);
return;
}
mNextMusic = filename;
mMusic->setFadeout(0.5f);
}
void SoundManager::streamMusic(const std::string& filename) void SoundManager::streamMusic(const std::string& filename)
{ {
streamMusicFull("Music/"+filename); advanceMusic("Music/"+filename);
} }
void SoundManager::startRandomTitle() void SoundManager::startRandomTitle()
{ {
std::vector<std::string> filelist; std::vector<std::string> filelist;
auto &tracklist = mMusicToPlay[mCurrentPlaylist];
if (mMusicFiles.find(mCurrentPlaylist) == mMusicFiles.end()) if (mMusicFiles.find(mCurrentPlaylist) == mMusicFiles.end())
{ {
const std::map<std::string, VFS::File*>& index = mVFS->getIndex(); const std::map<std::string, VFS::File*>& index = mVFS->getIndex();
@ -354,7 +367,6 @@ namespace MWSound
} }
mMusicFiles[mCurrentPlaylist] = filelist; mMusicFiles[mCurrentPlaylist] = filelist;
} }
else else
filelist = mMusicFiles[mCurrentPlaylist]; filelist = mMusicFiles[mCurrentPlaylist];
@ -362,15 +374,25 @@ namespace MWSound
if(filelist.empty()) if(filelist.empty())
return; return;
int i = Misc::Rng::rollDice(filelist.size()); // Do a Fisher-Yates shuffle
// Don't play the same music track twice in a row // Repopulate if playlist is empty
if (filelist[i] == mLastPlayedMusic) if(tracklist.empty())
{ {
i = (i+1) % filelist.size(); tracklist.resize(filelist.size());
std::iota(tracklist.begin(), tracklist.end(), 0);
} }
streamMusicFull(filelist[i]); int i = Misc::Rng::rollDice(tracklist.size());
// Reshuffle if last played music is the same after a repopulation
if(filelist[tracklist[i]] == mLastPlayedMusic)
i = (i+1) % tracklist.size();
// Remove music from list after advancing music
advanceMusic(filelist[tracklist[i]]);
tracklist[i] = tracklist.back();
tracklist.pop_back();
} }
bool SoundManager::isMusicPlaying() bool SoundManager::isMusicPlaying()
@ -925,6 +947,8 @@ namespace MWSound
env env
); );
updateMusic(duration);
// Check if any sounds are finished playing, and trash them // Check if any sounds are finished playing, and trash them
SoundMap::iterator snditer = mActiveSounds.begin(); SoundMap::iterator snditer = mActiveSounds.begin();
while(snditer != mActiveSounds.end()) while(snditer != mActiveSounds.end())
@ -1029,6 +1053,23 @@ namespace MWSound
} }
void SoundManager::updateMusic(float duration)
{
if (!mNextMusic.empty())
{
mMusic->updateFade(duration);
mOutput->updateStream(mMusic);
if (mMusic->getRealVolume() <= 0.f)
{
streamMusicFull(mNextMusic);
mNextMusic.clear();
}
}
}
void SoundManager::update(float duration) void SoundManager::update(float duration)
{ {
if(!mOutput->isInitialized()) if(!mOutput->isInitialized())

@ -6,6 +6,7 @@
#include <utility> #include <utility>
#include <deque> #include <deque>
#include <map> #include <map>
#include <unordered_map>
#include <components/settings/settings.hpp> #include <components/settings/settings.hpp>
@ -49,6 +50,7 @@ namespace MWSound
// Caches available music tracks by <playlist name, (sound files) > // Caches available music tracks by <playlist name, (sound files) >
std::map<std::string, std::vector<std::string> > mMusicFiles; std::map<std::string, std::vector<std::string> > mMusicFiles;
std::unordered_map<std::string, std::vector<int>> mMusicToPlay; // A list with music files not yet played
std::string mLastPlayedMusic; // The music file that was last played std::string mLastPlayedMusic; // The music file that was last played
float mMasterVolume; float mMasterVolume;
@ -114,9 +116,14 @@ namespace MWSound
MWBase::SoundStreamPtr playVoice(DecoderPtr decoder, const osg::Vec3f &pos, bool playlocal); MWBase::SoundStreamPtr playVoice(DecoderPtr decoder, const osg::Vec3f &pos, bool playlocal);
void streamMusicFull(const std::string& filename); void streamMusicFull(const std::string& filename);
void advanceMusic(const std::string& filename);
void updateSounds(float duration); void updateSounds(float duration);
void updateRegionSound(float duration); void updateRegionSound(float duration);
void updateWaterSound(float duration); void updateWaterSound(float duration);
void updateMusic(float duration);
std::string mNextMusic;
float volumeFromType(PlayType type) const; float volumeFromType(PlayType type) const;

@ -301,6 +301,10 @@ namespace MWWorld
virtual Ptr copyToCell(const ConstPtr &ptr, CellStore &cell, const ESM::Position &pos, int count) const; virtual Ptr copyToCell(const ConstPtr &ptr, CellStore &cell, const ESM::Position &pos, int count) const;
virtual bool isActivator() const {
return false;
}
virtual bool isActor() const { virtual bool isActor() const {
return false; return false;
} }
@ -309,6 +313,10 @@ namespace MWWorld
return false; return false;
} }
virtual bool isDoor() const {
return false;
}
virtual bool isBipedal(const MWWorld::ConstPtr& ptr) const; virtual bool isBipedal(const MWWorld::ConstPtr& ptr) const;
virtual bool canFly(const MWWorld::ConstPtr& ptr) const; virtual bool canFly(const MWWorld::ConstPtr& ptr) const;
virtual bool canSwim(const MWWorld::ConstPtr& ptr) const; virtual bool canSwim(const MWWorld::ConstPtr& ptr) const;

@ -4,6 +4,7 @@
#include <iosfwd> #include <iosfwd>
#include <iostream> #include <iostream>
#include <boost/filesystem/path.hpp> #include <boost/filesystem/path.hpp>
#include <MyGUI_TextIterator.h>
#include "components/loadinglistener/loadinglistener.hpp" #include "components/loadinglistener/loadinglistener.hpp"
@ -24,7 +25,7 @@ struct ContentLoader
virtual void load(const boost::filesystem::path& filepath, int& index) virtual void load(const boost::filesystem::path& filepath, int& index)
{ {
std::cout << "Loading content file " << filepath.string() << std::endl; std::cout << "Loading content file " << filepath.string() << std::endl;
mListener.setLabel(filepath.string()); mListener.setLabel(MyGUI::TextIterator::toTagsString(filepath.string()));
} }
protected: protected:

@ -2743,7 +2743,25 @@ namespace MWWorld
{ {
pos.rot[0] = pos.rot[1] = pos.rot[2] = 0; pos.rot[0] = pos.rot[1] = pos.rot[2] = 0;
if (const ESM::Cell *ext = getExterior(name)) { const ESM::Cell *ext = getExterior(name);
if (!ext && name.find(',') != std::string::npos) {
try {
int x = std::stoi(name.substr(0, name.find(',')));
int y = std::stoi(name.substr(name.find(',')+1));
ext = getExterior(x, y)->getCell();
}
catch (std::invalid_argument)
{
// This exception can be ignored, as this means that name probably refers to a interior cell instead of comma separated coordinates
}
catch (std::out_of_range)
{
throw std::runtime_error("Cell coordinates out of range.");
}
}
if (ext) {
int x = ext->getGridX(); int x = ext->getGridX();
int y = ext->getGridY(); int y = ext->getGridY();
indexToPosition(x, y, pos.pos[0], pos.pos[1], true); indexToPosition(x, y, pos.pos[0], pos.pos[1], true);
@ -2753,6 +2771,7 @@ namespace MWWorld
return true; return true;
} }
return false; return false;
} }

@ -2,7 +2,6 @@
#include <QDebug> #include <QDebug>
#include <QPushButton> #include <QPushButton>
#include <QAbstractButton>
#include <QMessageBox> #include <QMessageBox>
#include "mainwizard.hpp" #include "mainwizard.hpp"
@ -26,7 +25,7 @@ Wizard::ComponentSelectionPage::ComponentSelectionPage(QWidget *parent) :
void Wizard::ComponentSelectionPage::updateButton(QListWidgetItem*) void Wizard::ComponentSelectionPage::updateButton(QListWidgetItem*)
{ {
if (field(QLatin1String("installation.new")).toBool() == true) if (field(QLatin1String("installation.retailDisc")).toBool() == true)
return; // Morrowind is always checked here return; // Morrowind is always checked here
bool unchecked = true; bool unchecked = true;
@ -60,7 +59,7 @@ void Wizard::ComponentSelectionPage::initializePage()
QListWidgetItem *tribunalItem = new QListWidgetItem(QLatin1String("Tribunal")); QListWidgetItem *tribunalItem = new QListWidgetItem(QLatin1String("Tribunal"));
QListWidgetItem *bloodmoonItem = new QListWidgetItem(QLatin1String("Bloodmoon")); QListWidgetItem *bloodmoonItem = new QListWidgetItem(QLatin1String("Bloodmoon"));
if (field(QLatin1String("installation.new")).toBool() == true) if (field(QLatin1String("installation.retailDisc")).toBool() == true)
{ {
morrowindItem->setFlags((morrowindItem->flags() & ~Qt::ItemIsEnabled) | Qt::ItemIsUserCheckable); morrowindItem->setFlags((morrowindItem->flags() & ~Qt::ItemIsEnabled) | Qt::ItemIsUserCheckable);
morrowindItem->setData(Qt::CheckStateRole, Qt::Checked); morrowindItem->setData(Qt::CheckStateRole, Qt::Checked);
@ -117,7 +116,7 @@ bool Wizard::ComponentSelectionPage::validatePage()
// qDebug() << components << path << mWizard->mInstallations[path]; // qDebug() << components << path << mWizard->mInstallations[path];
if (field(QLatin1String("installation.new")).toBool() == false) { if (field(QLatin1String("installation.retailDisc")).toBool() == false) {
if (components.contains(QLatin1String("Tribunal")) && !components.contains(QLatin1String("Bloodmoon"))) if (components.contains(QLatin1String("Tribunal")) && !components.contains(QLatin1String("Bloodmoon")))
{ {
if (mWizard->mInstallations[path].hasBloodmoon) if (mWizard->mInstallations[path].hasBloodmoon)

@ -16,14 +16,14 @@ Wizard::ConclusionPage::ConclusionPage(QWidget *parent) :
void Wizard::ConclusionPage::initializePage() void Wizard::ConclusionPage::initializePage()
{ {
// Write the path to openmw.cfg // Write the path to openmw.cfg
if (field(QLatin1String("installation.new")).toBool() == true) { if (field(QLatin1String("installation.retailDisc")).toBool() == true) {
QString path(field(QLatin1String("installation.path")).toString()); QString path(field(QLatin1String("installation.path")).toString());
mWizard->addInstallation(path); mWizard->addInstallation(path);
} }
if (!mWizard->mError) if (!mWizard->mError)
{ {
if ((field(QLatin1String("installation.new")).toBool() == true) if ((field(QLatin1String("installation.retailDisc")).toBool() == true)
|| (field(QLatin1String("installation.import-settings")).toBool() == true)) || (field(QLatin1String("installation.import-settings")).toBool() == true))
{ {
qDebug() << "IMPORT SETTINGS"; qDebug() << "IMPORT SETTINGS";
@ -33,7 +33,7 @@ void Wizard::ConclusionPage::initializePage()
if (!mWizard->mError) if (!mWizard->mError)
{ {
if (field(QLatin1String("installation.new")).toBool() == true) if (field(QLatin1String("installation.retailDisc")).toBool() == true)
{ {
textLabel->setText(tr("<html><head/><body><p>The OpenMW Wizard successfully installed Morrowind on your computer.</p> \ textLabel->setText(tr("<html><head/><body><p>The OpenMW Wizard successfully installed Morrowind on your computer.</p> \
<p>Click Finish to close the Wizard.</p></body></html>")); <p>Click Finish to close the Wizard.</p></body></html>"));

@ -7,7 +7,6 @@
#include <QMessageBox> #include <QMessageBox>
#include "mainwizard.hpp" #include "mainwizard.hpp"
#include "inisettings.hpp"
Wizard::InstallationPage::InstallationPage(QWidget *parent) : Wizard::InstallationPage::InstallationPage(QWidget *parent) :
QWizardPage(parent) QWizardPage(parent)
@ -76,7 +75,7 @@ void Wizard::InstallationPage::initializePage()
// That way installing all three components would yield 300% // That way installing all three components would yield 300%
// When one component is done the bar will be filled by 33% // When one component is done the bar will be filled by 33%
if (field(QLatin1String("installation.new")).toBool() == true) { if (field(QLatin1String("installation.retailDisc")).toBool() == true) {
installProgressBar->setMaximum((components.count() * 100)); installProgressBar->setMaximum((components.count() * 100));
} else { } else {
if (components.contains(QLatin1String("Tribunal")) if (components.contains(QLatin1String("Tribunal"))
@ -96,7 +95,7 @@ void Wizard::InstallationPage::startInstallation()
QStringList components(field(QLatin1String("installation.components")).toStringList()); QStringList components(field(QLatin1String("installation.components")).toStringList());
QString path(field(QLatin1String("installation.path")).toString()); QString path(field(QLatin1String("installation.path")).toString());
if (field(QLatin1String("installation.new")).toBool() == true) if (field(QLatin1String("installation.retailDisc")).toBool() == true)
{ {
// Always install Morrowind // Always install Morrowind
mUnshield->setInstallComponent(Wizard::Component_Morrowind, true); mUnshield->setInstallComponent(Wizard::Component_Morrowind, true);
@ -227,7 +226,7 @@ bool Wizard::InstallationPage::isComplete() const
int Wizard::InstallationPage::nextId() const int Wizard::InstallationPage::nextId() const
{ {
if (field(QLatin1String("installation.new")).toBool() == true) { if (field(QLatin1String("installation.retailDisc")).toBool() == true) {
return MainWizard::Page_Conclusion; return MainWizard::Page_Conclusion;
} else { } else {
if (!mWizard->mError) { if (!mWizard->mError) {

@ -30,7 +30,7 @@ void Wizard::LanguageSelectionPage::initializePage()
int Wizard::LanguageSelectionPage::nextId() const int Wizard::LanguageSelectionPage::nextId() const
{ {
if (field(QLatin1String("installation.new")).toBool() == true) { if (field(QLatin1String("installation.retailDisc")).toBool() == true) {
return MainWizard::Page_ComponentSelection; return MainWizard::Page_ComponentSelection;
} else { } else {
QString path(field(QLatin1String("installation.path")).toString()); QString path(field(QLatin1String("installation.path")).toString());

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

Loading…
Cancel
Save