Merge branch 'master' into new-script-api

pull/276/head
Koncord 7 years ago
commit 510e657c93

@ -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
.directory
.idea
files/windows/*.aps
cmake-build-*
## qt-creator
CMakeLists.txt.user*

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

@ -5,7 +5,7 @@ TES3MP
TES3MP is a project aiming to add multiplayer functionality to [OpenMW](https://github.com/OpenMW/openmw), a free and open source recreation of the popular Bethesda Softworks game "The Elder Scrolls III: Morrowind".
* Version: 0.6.0
* Version: 0.6.1
* License: GPLv3 (see docs/license/GPL3.txt for more information)
Font Licenses:

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

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

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

@ -1,8 +1,6 @@
#ifndef CS_EDITOR_H
#define CS_EDITOR_H
#include <memory>
#include <boost/interprocess/sync/file_lock.hpp>
#include <boost/filesystem/fstream.hpp>
@ -30,11 +28,6 @@
#include "view/tools/merge.hpp"
namespace VFS
{
class Manager;
}
namespace CSMDoc
{
class Document;
@ -46,9 +39,6 @@ namespace CS
{
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;
CSMPrefs::State mSettingsState;
CSMDoc::DocumentManager mDocumentManager;

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

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

@ -9,7 +9,7 @@
#include "document.hpp"
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";
@ -62,7 +62,7 @@ CSMDoc::Document *CSMDoc::DocumentManager::makeDocument (
const std::vector< boost::filesystem::path >& files,
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)
@ -127,8 +127,9 @@ void CSMDoc::DocumentManager::documentNotLoaded (Document *document, const std::
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);
mVFS = vfs;
mFsStrict = strict;
mDataPaths = dataPaths;
mArchives = archives;
}

@ -11,8 +11,7 @@
#include <components/to_utf8/to_utf8.hpp>
#include <components/fallback/fallback.hpp>
#include "../world/resourcesmanager.hpp"
#include <components/files/multidircollection.hpp>
#include "loader.hpp"
@ -39,9 +38,14 @@ namespace CSMDoc
QThread mLoaderThread;
Loader mLoader;
ToUTF8::FromType mEncoding;
CSMWorld::ResourcesManager mResourcesManager;
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& operator= (const DocumentManager&);
@ -74,15 +78,11 @@ namespace CSMDoc
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();
private:
boost::filesystem::path mResDir;
Fallback::Map mFallbackMap;
private slots:
void documentLoaded (Document *document);

@ -259,6 +259,7 @@ void CSMPrefs::State::declare()
declareShortcut ("document-character-topicinfos", "Open Topic Info List", QKeySequence());
declareShortcut ("document-character-journalinfos", "Open Journal Info 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-soundgens", "Open Sound Generator List", QKeySequence());
declareShortcut ("document-assets-meshes", "Open Mesh Asset List", QKeySequence());

@ -11,6 +11,8 @@
#include <components/esm/cellref.hpp>
#include <components/resource/scenemanager.hpp>
#include <components/vfs/manager.hpp>
#include <components/vfs/registerarchives.hpp>
#include "idtable.hpp"
#include "idtree.hpp"
@ -61,11 +63,18 @@ int CSMWorld::Data::count (RecordBase::State state, const CollectionBase& collec
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),
mResourcesManager (resourcesManager), mFallbackMap(fallback),
mReader (0), mDialogue (0), mReaderIndex(1), mResourceSystem(new Resource::ResourceSystem(resourcesManager.getVFS()))
mFallbackMap(fallback), mReader (0), mDialogue (0), mReaderIndex(1),
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());
int index = 0;
@ -1215,6 +1224,43 @@ std::vector<std::string> CSMWorld::Data::getIds (bool listDeleted) const
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)
{
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
{
return mResourcesManager.getVFS();
return mVFS.get();
}
const Fallback::Map* CSMWorld::Data::getFallbackMap() const

@ -31,6 +31,7 @@
#include <components/resource/resourcesystem.hpp>
#include <components/files/multidircollection.hpp>
#include <components/to_utf8/to_utf8.hpp>
#include "../doc/stage.hpp"
@ -46,6 +47,7 @@
#include "infocollection.hpp"
#include "nestedinfocollection.hpp"
#include "pathgrid.hpp"
#include "resourcesmanager.hpp"
#include "metadata.hpp"
#ifndef Q_MOC_RUN
#include "subcellcollection.hpp"
@ -108,7 +110,6 @@ namespace CSMWorld
RefCollection mRefs;
IdCollection<ESM::Filter> mFilters;
Collection<MetaData> mMetaData;
const ResourcesManager& mResourcesManager;
const Fallback::Map* mFallbackMap;
std::vector<QAbstractItemModel *> mModels;
std::map<UniversalId::Type, QAbstractItemModel *> mModelIndex;
@ -119,6 +120,11 @@ namespace CSMWorld
std::map<std::string, std::map<ESM::RefNum, std::string> > mRefLoadCache;
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::vector<std::shared_ptr<ESM::ESMReader> > mReaders;
@ -140,7 +146,9 @@ namespace CSMWorld
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();
@ -304,8 +312,12 @@ namespace CSMWorld
void idListChanged();
void assetTablesChanged();
private slots:
void assetsChanged();
void dataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);
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)
: 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();
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,
const char * const *extensions = 0);
void recreate(const VFS::Manager* vfs, const char * const *extensions = 0);
int getSize() const;
std::string getId (int index) const;

@ -14,21 +14,24 @@ void CSMWorld::ResourcesManager::addResources (const 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)
{
mVFS = vfs;
mResources.clear();
// 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 };
addResources (Resources (vfs, "meshes", UniversalId::Type_Mesh, sMeshTypes));
addResources (Resources (vfs, "meshes", UniversalId::Type_Mesh, getMeshExtensions()));
addResources (Resources (vfs, "icons", UniversalId::Type_Icon));
addResources (Resources (vfs, "music", UniversalId::Type_Music));
addResources (Resources (vfs, "sound", UniversalId::Type_SoundRes));
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
@ -36,6 +39,18 @@ const VFS::Manager* CSMWorld::ResourcesManager::getVFS() const
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
{
std::map<UniversalId::Type, Resources>::const_iterator iter = mResources.find (type);

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

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

@ -52,7 +52,12 @@ namespace CSMWorld
/// Is \a id flagged as deleted?
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"));
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);
connect (sounds, SIGNAL (triggered()), this, SLOT (addSoundsSubView()));
setupShortcut("document-assets-sounds", sounds);

@ -275,12 +275,32 @@ bool CSVRender::Cell::referenceAdded (const QModelIndex& parent, int start, int
void CSVRender::Cell::pathgridModified()
{
mPathgrid->recreateGeometry();
if (mPathgrid)
mPathgrid->recreateGeometry();
}
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)
@ -302,7 +322,7 @@ void CSVRender::Cell::setSelection (int elementMask, Selection mode)
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
// 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)
if (iter->second->getSelected())
result.push_back (iter->second->getTag());
if (elementMask & Mask_Pathgrid)
if (mPathgrid && elementMask & Mask_Pathgrid)
if (mPathgrid->isSelected())
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());
iter!=mObjects.end(); ++iter)
iter->second->reset();
if (elementMask & Mask_Pathgrid)
if (mPathgrid && elementMask & Mask_Pathgrid)
mPathgrid->resetIndicators();
}

@ -118,6 +118,8 @@ namespace CSVRender
void pathgridRemoved();
void reloadAssets();
void setSelection (int elementMask, Selection mode);
// 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)
{
const CSMWorld::Collection<CSMWorld::Cell>& cells = mData.getCells();

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

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

@ -151,6 +151,9 @@ namespace CSVRender
/// this object?
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.
std::string getReferenceId() const;

@ -472,6 +472,9 @@ CSVRender::PagedWorldspaceWidget::PagedWorldspaceWidget (QWidget* parent, CSMDoc
connect (cells, SIGNAL (rowsInserted (const QModelIndex&, int, int)),
this, SLOT (cellAdded (const QModelIndex&, int, int)));
connect (&document.getData(), SIGNAL (assetTablesChanged ()),
this, SLOT (assetTablesChanged ()));
// Shortcuts
CSMPrefs::Shortcut* loadCameraCellShortcut = new CSMPrefs::Shortcut("scene-load-cam-cell", this);
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
while (stream >> ignore1 >> ignore2 >> x >> y)
selection.add (CSMWorld::CellCoordinates (x, y));
// Mark that camera needs setup
mCamPositionSet=false;
}
@ -763,6 +766,15 @@ void CSVRender::PagedWorldspaceWidget::cellAdded (const QModelIndex& index, int
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()
{
addCellToSceneFromCamera(0, 0);

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

@ -72,12 +72,15 @@ namespace CSVRender
}
else if (Cell* cell = getWorldspaceWidget().getCell (hitResult.worldPos))
{
// Add node
QUndoStack& undoStack = getWorldspaceWidget().getDocument().getUndoStack();
QString description = "Add node";
if (cell->getPathgrid())
{
// Add node
QUndoStack& undoStack = getWorldspaceWidget().getDocument().getUndoStack();
QString description = "Add node";
CSMWorld::CommandMacro macro(undoStack, description);
cell->getPathgrid()->applyPoint(macro, hitResult.worldPos);
CSMWorld::CommandMacro macro(undoStack, description);
cell->getPathgrid()->applyPoint(macro, hitResult.worldPos);
}
}
}
@ -205,7 +208,7 @@ namespace CSVRender
WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask());
Cell* cell = getWorldspaceWidget().getCell(hit.worldPos);
if (cell)
if (cell && cell->getPathgrid())
{
PathgridTag* tag = 0;
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)),
this, SLOT (referenceableAboutToBeRemoved (const QModelIndex&, int, int)));
connect (&mData, SIGNAL (assetTablesChanged ()),
this, SLOT (assetTablesChanged ()));
if (!referenceable)
{
QAbstractItemModel *references =
@ -119,3 +122,8 @@ void CSVRender::PreviewWidget::referenceAboutToBeRemoved (const QModelIndex& par
if (index.row()>=start && index.row()<=end)
emit closeRequest();
}
void CSVRender::PreviewWidget::assetTablesChanged ()
{
mObject.reloadAssets();
}

@ -47,6 +47,8 @@ namespace CSVRender
void referenceDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);
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()
{
// 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
mResourceSystem->getSceneManager()->releaseGLObjects(mView->getCamera()->getGraphicsContext()->getState());
// 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->releaseGLObjects(mView->getCamera()->getGraphicsContext()->getState());
}
void SceneWidget::setLighting(Lighting *lighting)
@ -393,6 +393,7 @@ void SceneWidget::selectNavigationMode (const std::string& mode)
mCurrentCamControl->setCamera(NULL);
mCurrentCamControl = mOrbitCamControl;
mOrbitCamControl->setCamera(getCamera());
mOrbitCamControl->reset();
}
}

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

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

@ -32,13 +32,19 @@ std::string CSVWorld::InfoCreator::getId() const
void CSVWorld::InfoCreator::configureCreateCommand (CSMWorld::CreateCommand& command) const
{
int index =
dynamic_cast<CSMWorld::IdTable&> (*getData().getTableModel (getCollectionId())).
findColumnIndex (
getCollectionId().getType()==CSMWorld::UniversalId::Type_TopicInfos ?
CSMWorld::Columns::ColumnId_Topic : CSMWorld::Columns::ColumnId_Journal);
CSMWorld::IdTable& table = dynamic_cast<CSMWorld::IdTable&> (*getData().getTableModel (getCollectionId()));
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,

@ -87,8 +87,15 @@ Cell *CellController::addCell(ESM::Cell cellData)
auto it = find_if (cells.begin(), cells.end(), [cellData](const Cell *c) {
// Currently we cannot compare because plugin lists can be loaded in different order
//return c->cell.sRecordId == cellData.sRecordId;
return c->cell.isExterior() ? (c->cell.mData.mX == cellData.mData.mX && c->cell.mData.mY == cellData.mData.mY) :
(c->cell.mName == cellData.mName);
if (c->cell.isExterior() && cellData.isExterior())
{
if (c->cell.mData.mX == cellData.mData.mX && c->cell.mData.mY == cellData.mData.mY)
return true;
}
else if (c->cell.mName == cellData.mName)
return true;
return false;
});
Cell *cell;

@ -124,6 +124,7 @@ bool MasterClient::Process(RakNet::Packet *packet)
rs.Read(pid);
switch (pid)
{
case ID_SND_RECEIPT_ACKED:
case ID_CONNECTION_ATTEMPT_FAILED:
case ID_CONNECTION_REQUEST_ACCEPTED:
case ID_DISCONNECTION_NOTIFICATION:

@ -71,7 +71,6 @@ Networking::~Networking()
delete playerPacketController;
delete actorPacketController;
delete worldPacketController;
LOG_QUIT();
}
void Networking::setServerPassword(std::string passw) noexcept
@ -467,6 +466,7 @@ int Networking::mainLoop()
LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Client at %s has lost connection", packet->systemAddress.ToString());
disconnectPlayer(packet->guid);
break;
case ID_SND_RECEIPT_ACKED:
case ID_CONNECTED_PING:
case ID_UNCONNECTED_PING:
break;

@ -315,6 +315,8 @@ int main(int argc, char *argv[])
if (code == 0)
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Quitting peacefully.");
LOG_QUIT();
if (!variables["no-logs"].as<bool>())
{
// Restore cout and cerr

@ -33,35 +33,35 @@ namespace mwmp
LOG_APPEND(Log::LOG_INFO, "- Started information exchange with %s", other->npc.mName.c_str());
playerController->GetPacket(ID_PLAYER_SHAPESHIFT)->setPlayer(other);
playerController->GetPacket(ID_PLAYER_STATS_DYNAMIC)->setPlayer(other);
playerController->GetPacket(ID_PLAYER_ATTRIBUTE)->setPlayer(other);
playerController->GetPacket(ID_PLAYER_POSITION)->setPlayer(other);
playerController->GetPacket(ID_PLAYER_SKILL)->setPlayer(other);
playerController->GetPacket(ID_PLAYER_EQUIPMENT)->setPlayer(other);
playerController->GetPacket(ID_PLAYER_ANIM_FLAGS)->setPlayer(other);
playerController->GetPacket(ID_PLAYER_SHAPESHIFT)->setPlayer(other);
playerController->GetPacket(ID_PLAYER_SHAPESHIFT)->Send(pl->guid);
playerController->GetPacket(ID_PLAYER_STATS_DYNAMIC)->Send(pl->guid);
playerController->GetPacket(ID_PLAYER_ATTRIBUTE)->Send(pl->guid);
playerController->GetPacket(ID_PLAYER_POSITION)->Send(pl->guid);
playerController->GetPacket(ID_PLAYER_SKILL)->Send(pl->guid);
playerController->GetPacket(ID_PLAYER_EQUIPMENT)->Send(pl->guid);
playerController->GetPacket(ID_PLAYER_ANIM_FLAGS)->Send(pl->guid);
playerController->GetPacket(ID_PLAYER_SHAPESHIFT)->Send(pl->guid);
playerController->GetPacket(ID_PLAYER_SHAPESHIFT)->setPlayer(pl);
playerController->GetPacket(ID_PLAYER_STATS_DYNAMIC)->setPlayer(pl);
playerController->GetPacket(ID_PLAYER_ATTRIBUTE)->setPlayer(pl);
playerController->GetPacket(ID_PLAYER_SKILL)->setPlayer(pl);
playerController->GetPacket(ID_PLAYER_EQUIPMENT)->setPlayer(pl);
playerController->GetPacket(ID_PLAYER_ANIM_FLAGS)->setPlayer(pl);
playerController->GetPacket(ID_PLAYER_SHAPESHIFT)->setPlayer(pl);
playerController->GetPacket(ID_PLAYER_SHAPESHIFT)->Send(other->guid);
playerController->GetPacket(ID_PLAYER_STATS_DYNAMIC)->Send(other->guid);
playerController->GetPacket(ID_PLAYER_ATTRIBUTE)->Send(other->guid);
playerController->GetPacket(ID_PLAYER_SKILL)->Send(other->guid);
playerController->GetPacket(ID_PLAYER_EQUIPMENT)->Send(other->guid);
playerController->GetPacket(ID_PLAYER_ANIM_FLAGS)->Send(other->guid);
playerController->GetPacket(ID_PLAYER_SHAPESHIFT)->Send(other->guid);
LOG_APPEND(Log::LOG_INFO, "- Finished information exchange with %s", other->npc.mName.c_str());
});

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

@ -148,7 +148,7 @@ namespace OMW
/// Set resource dir
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);
/**

@ -30,6 +30,9 @@
#include <cstdlib>
#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))
#define USE_CRASH_CATCHER 1

@ -46,6 +46,17 @@ namespace MWBase
virtual void addTopic (const std::string& topic) = 0;
/*
Start of tes3mp addition
Make it possible to check whether a topic is known by the player from elsewhere
in the code
*/
virtual bool isNewTopic(const std::string& topic) = 0;
/*
End of tes3mp addition
*/
virtual void askQuestion (const std::string& question,int choice) = 0;
virtual void goodbye() = 0;

@ -2,6 +2,8 @@
#include <cassert>
#include <OpenThreads/Thread>
#include "world.hpp"
#include "scriptmanager.hpp"
#include "dialoguemanager.hpp"
@ -17,7 +19,7 @@ MWBase::Environment *MWBase::Environment::sThis = 0;
MWBase::Environment::Environment()
: mWorld (0), mSoundManager (0), mScriptManager (0), mWindowManager (0),
mMechanicsManager (0), mDialogueManager (0), mJournal (0), mInputManager (0), mStateManager (0),
mFrameDuration (0)
mFrameDuration (0), mFrameRateLimit(0.f)
{
assert (!sThis);
sThis = this;
@ -79,6 +81,29 @@ void MWBase::Environment::setFrameDuration (float 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
{
assert (mWorld);

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

@ -7,6 +7,8 @@
#include <set>
#include <stdint.h>
#include "../mwworld/ptr.hpp"
namespace osg
{
class Vec3f;
@ -231,6 +233,7 @@ namespace MWBase
virtual void keepPlayerAlive() = 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;
@ -240,8 +243,8 @@ namespace MWBase
/// Has the player stolen this item from the given owner?
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.
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 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 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 void setSelectedSpell(const std::string& spellId, int successChancePercent) = 0;

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

@ -42,6 +42,8 @@ namespace MWClass
virtual bool useAnim() const;
///< 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/creaturestate.hpp>
#include <components/settings/settings.hpp>
/*
Start of tes3mp addition
@ -558,10 +559,27 @@ namespace MWClass
return action;
}
if(getCreatureStats(ptr).isDead())
return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr, true));
if(ptr.getClass().getCreatureStats(ptr).getAiSequence().isInCombat())
const MWMechanics::CreatureStats& stats = getCreatureStats(ptr);
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::ActionTalk(ptr));
}
@ -668,7 +686,11 @@ namespace MWClass
return true;
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

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

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

@ -7,6 +7,7 @@
#include <components/esm/loadmgef.hpp>
#include <components/esm/loadnpc.hpp>
#include <components/esm/npcstate.hpp>
#include <components/settings/settings.hpp>
/*
Start of tes3mp addition
@ -999,16 +1000,35 @@ namespace MWClass
return action;
}
if(getCreatureStats(ptr).isDead())
return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr, true));
if(ptr.getClass().getCreatureStats(ptr).getAiSequence().isInCombat())
return std::shared_ptr<MWWorld::Action>(new MWWorld::FailedAction("#{sActorInCombat}"));
const MWMechanics::CreatureStats& stats = getCreatureStats(ptr);
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(""));
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
// Can't talk to werewolfs
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::ActionTalk(ptr));
}
@ -1155,7 +1175,11 @@ namespace MWClass
return true;
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

@ -4,6 +4,7 @@
#include <components/settings/settings.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwbase/world.hpp"
#include "../mwbase/windowmanager.hpp"
@ -373,6 +374,9 @@ namespace MWClass
if (hasItemHealth(ptr) && ptr.getCellRef().getCharge() == 0)
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);
if (slots_.first.empty())

@ -97,6 +97,20 @@ namespace MWDialogue
mKnownTopics.insert( Misc::StringUtils::lowerCase(topic) );
}
/*
Start of tes3mp addition
Make it possible to check whether a topic is known by the player from elsewhere
in the code
*/
bool DialogueManager::isNewTopic(const std::string& topic)
{
return (!mKnownTopics.count(topic));
}
/*
End of tes3mp addition
*/
void DialogueManager::parseText (const std::string& text)
{
std::vector<HyperTextParser::Token> hypertext = HyperTextParser::parseHyperText(text);
@ -120,7 +134,7 @@ namespace MWDialogue
Send an ID_PLAYER_TOPIC packet every time a new topic becomes known
*/
if (mActorKnownTopics.count(topicId) && !mKnownTopics.count(topicId))
if (mActorKnownTopics.count(topicId) && isNewTopic(topicId))
mwmp::Main::get().getLocalPlayer()->sendTopic(topicId);
/*
End of tes3mp addition

@ -68,6 +68,17 @@ namespace MWDialogue
virtual void addTopic (const std::string& topic);
/*
Start of tes3mp addition
Make it possible to check whether a topic is known by the player from elsewhere
in the code
*/
virtual bool isNewTopic(const std::string& topic);
/*
End of tes3mp addition
*/
virtual void askQuestion (const std::string& question,int choice);
virtual void goodbye();

@ -435,9 +435,15 @@ struct TypesetBookImpl::Typesetter : BookTypesetter
}
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
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
int splitPos = curPageStop;

@ -397,6 +397,18 @@ namespace MWGui
if (mNameDialog)
{
mPlayerName = mNameDialog->getTextInput();
/*
Start of tes3mp change (major)
Ensure names are not longer than the original game's 31 character maximum
*/
if (mPlayerName.length() > 31)
mPlayerName = mPlayerName.substr(0, 31);
/*
End of tes3mp change (major)
*/
MWBase::Environment::get().getWindowManager()->setValue("name", mPlayerName);
MWBase::Environment::get().getMechanicsManager()->setPlayerName(mPlayerName);
MWBase::Environment::get().getWindowManager()->removeDialog(mNameDialog);

@ -360,8 +360,9 @@ namespace MWGui
if (msg.find("%s") != std::string::npos)
msg.replace(msg.find("%s"), 2, item.getClass().getName(item));
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().getDialogueManager()->goodbyeSelected();
return;

@ -219,29 +219,33 @@ namespace MWGui
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());
// Fatigue can be negative
if (id != "FBar")
current = std::max(0, current);
MyGUI::Widget* w;
std::string valStr = MyGUI::utility::toString(current) + " / " + MyGUI::utility::toString(modified);
if (id == "HBar")
{
mHealth->setProgressRange(modified);
mHealth->setProgressPosition(current);
mHealth->setProgressRange(std::max(0, modified));
mHealth->setProgressPosition(std::max(0, current));
getWidget(w, "HealthFrame");
w->setUserString("Caption_HealthDescription", "#{sHealthDesc}\n" + valStr);
}
else if (id == "MBar")
{
mMagicka->setProgressRange (modified);
mMagicka->setProgressPosition (current);
mMagicka->setProgressRange(std::max(0, modified));
mMagicka->setProgressPosition(std::max(0, current));
getWidget(w, "MagickaFrame");
w->setUserString("Caption_HealthDescription", "#{sMagDesc}\n" + valStr);
}
else if (id == "FBar")
{
mStamina->setProgressRange (modified);
mStamina->setProgressPosition (current);
mStamina->setProgressRange(std::max(0, modified));
mStamina->setProgressPosition(std::max(0, current));
getWidget(w, "FatigueFrame");
w->setUserString("Caption_HealthDescription", "#{sFatDesc}\n" + valStr);
}

@ -43,6 +43,7 @@
#include "../mwrender/characterpreview.hpp"
#include "../mwmechanics/actorutil.hpp"
#include "../mwmechanics/creaturestats.hpp"
#include "itemview.hpp"
#include "inventoryitemmodel.hpp"
@ -693,9 +694,18 @@ namespace MWGui
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;
// 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.update();
if (model.getItemCount() == 0)

@ -136,7 +136,7 @@ namespace MWGui
*/
value.setBase(std::min(100, value.getBase()+1));
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>();

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

@ -187,12 +187,12 @@ namespace
}
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 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>(QuestsBTN)->setPosition((pageWidth - width)/2 + topicsWidth, getWidget<MyGUI::Widget>(QuestsBTN)->getPosition().top);
getWidget<MyGUI::Widget>(TopicsBTN)->setPosition(cancelLeft - topicsWidth, getWidget<MyGUI::Widget>(TopicsBTN)->getPosition().top);
getWidget<MyGUI::Widget>(QuestsBTN)->setPosition(cancelRight, getWidget<MyGUI::Widget>(QuestsBTN)->getPosition().top);
mQuestMode = false;
mAllQuests = false;

@ -102,6 +102,15 @@ namespace MWGui
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
{
public:
@ -141,7 +150,7 @@ namespace MWGui
if (mViewer->getIncrementalCompileOperation())
{
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
@ -210,7 +219,7 @@ namespace MWGui
void LoadingScreen::setProgress (size_t value)
{
// 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;
value = std::min(value, mProgressBar->getScrollRange()-1);
mProgress = value;
@ -231,7 +240,7 @@ namespace MWGui
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;
// the minimal delay before a loading screen shows

@ -43,6 +43,8 @@ namespace MWGui
virtual void setVisible(bool visible);
double getTargetFrameRate() const;
private:
void findSplashScreens();
bool needToDrawLoadingScreen();
@ -73,8 +75,6 @@ namespace MWGui
std::vector<std::string> mSplashScreens;
// TODO: add releaseGLObjects() for mTexture
osg::ref_ptr<osg::Texture2D> mTexture;
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)
: WindowModal("openmw_interactive_messagebox.layout")
: WindowModal(MWBase::Environment::get().getWindowManager()->isGuiMode() ? "openmw_interactive_messagebox_notransp.layout" : "openmw_interactive_messagebox.layout")
, mMessageBoxManager(parMessageBoxManager)
, mButtonPressed(-1)
{

@ -14,6 +14,7 @@
#include "../mwworld/esmstore.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwbase/world.hpp"
#include "../mwbase/windowmanager.hpp"
@ -36,6 +37,7 @@ namespace MWGui
, mItemSelectionDialog(0)
, mMagicSelectionDialog(0)
, mSelectedIndex(-1)
, mActivatedIndex(-1)
{
getWidget(mOkButton, "OKButton");
getWidget(mInstructionLabel, "InstructionLabel");
@ -69,6 +71,8 @@ namespace MWGui
void QuickKeysMenu::clear()
{
mActivatedIndex = -1;
for (int i=0; i<10; ++i)
{
unassign(mQuickKeyButtons[i], i);
@ -254,6 +258,15 @@ namespace MWGui
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)
{
assert (index-1 >= 0);
@ -263,6 +276,27 @@ namespace MWGui
MWWorld::Ptr player = MWMechanics::getPlayer();
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)
{
@ -309,6 +343,21 @@ namespace MWGui
else if (type == Type_Item)
{
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);
MWWorld::ConstContainerStoreIterator rightHand = store.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
// change draw state only if the item is in player's right hand

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

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

@ -180,7 +180,7 @@ namespace MWGui
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());
mFatigue->setValue(current, modified);

@ -20,6 +20,7 @@
#include "../mwbase/windowmanager.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwworld/inventorystore.hpp"
#include "../mwworld/class.hpp"
@ -216,6 +217,15 @@ namespace MWGui
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()));
SpellModel::ModelIndex selected = 0;

@ -102,12 +102,13 @@ namespace MWGui
{
MyGUI::ProgressBar* pt;
getWidget(pt, name);
pt->setProgressRange(max);
pt->setProgressPosition(val);
std::stringstream out;
out << val << "/" << max;
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)
@ -147,9 +148,13 @@ namespace MWGui
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());
// Fatigue can be negative
if (id != "FBar")
current = std::max(0, current);
setBar (id, id + "T", current, modified);
// health, magicka, fatigue tooltip

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

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

@ -134,7 +134,7 @@ namespace MWGui
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::World* world = MWBase::Environment::get().getWorld();

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

@ -529,6 +529,8 @@ namespace MWGui
cleanupGarbage();
mHud->update();
updateActivatedQuickKey ();
}
void WindowManager::updateVisible()
@ -916,19 +918,30 @@ namespace MWGui
if (block)
{
osg::Timer frameTimer;
while (mMessageBoxManager->readPressedButton(false) == -1
&& !MWBase::Environment::get().getStateManager()->hasQuitRequest())
{
mMessageBoxManager->onFrame(0.f);
MWBase::Environment::get().getInputManager()->update(0, true, false);
double dt = frameTimer.time_s();
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,
// 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()
mViewer->eventTraversal();
mViewer->updateTraversal();
mViewer->renderingTraversals();
mViewer->advance(mViewer->getFrameStamp()->getSimulationTime());
MWBase::Environment::get().limitFrameRate(frameTimer.time_s());
}
}
}
@ -1566,6 +1579,11 @@ namespace MWGui
mHud->setCrosshairVisible (show && mCrosshairEnabled);
}
void WindowManager::updateActivatedQuickKey ()
{
mQuickKeysMenu->updateActivatedQuickKey();
}
void WindowManager::activateQuickKey (int index)
{
mQuickKeysMenu->activateQuickKey(index);
@ -1869,18 +1887,28 @@ namespace MWGui
if (mVideoWidget->hasAudioStream())
MWBase::Environment::get().getSoundManager()->pauseSounds(
MWBase::SoundManager::Play_TypeMask&(~MWBase::SoundManager::Play_TypeMovie));
osg::Timer frameTimer;
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,
// 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()
mViewer->eventTraversal();
mViewer->updateTraversal();
mViewer->renderingTraversals();
mViewer->advance(mViewer->getFrameStamp()->getSimulationTime());
MWBase::Environment::get().limitFrameRate(frameTimer.time_s());
}
mVideoWidget->stop();
@ -2021,12 +2049,8 @@ namespace MWGui
char* text=0;
text = SDL_GetClipboardText();
if (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;
}
_data = MyGUI::TextIterator::toTagsString(text);
SDL_free(text);
}

@ -251,7 +251,10 @@ namespace MWGui
virtual void setSpellVisibility(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 void setSelectedSpell(const std::string& spellId, int successChancePercent);

@ -13,7 +13,16 @@
#include <components/sdlutil/sdlinputwrapper.hpp>
#include <components/sdlutil/sdlvideowrapper.hpp>
/*
Start of tes3mp addition
Include additional headers for multiplayer purposes
*/
#include "../mwmp/Main.hpp"
#include "../mwmp/LocalPlayer.hpp"
/*
End of tes3mp addition
*/
#include <components/esm/esmwriter.hpp>
#include <components/esm/esmreader.hpp>
@ -23,6 +32,7 @@
#include "../mwbase/windowmanager.hpp"
#include "../mwbase/statemanager.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwworld/player.hpp"
#include "../mwworld/class.hpp"
@ -957,6 +967,9 @@ namespace MWInput
inventory.getSelectedEnchantItem() == inventory.end())
return;
if (MWBase::Environment::get().getMechanicsManager()->isAttackingOrSpell(mPlayer->getPlayer()))
return;
MWMechanics::DrawState_ state = mPlayer->getDrawState();
if (state == MWMechanics::DrawState_Weapon || state == MWMechanics::DrawState_Nothing)
mPlayer->setDrawState(MWMechanics::DrawState_Spell);
@ -972,6 +985,9 @@ namespace MWInput
if (!mControlSwitch["playerfighting"] || !mControlSwitch["playercontrols"])
return;
if (MWBase::Environment::get().getMechanicsManager()->isAttackingOrSpell(mPlayer->getPlayer()))
return;
MWMechanics::DrawState_ state = mPlayer->getDrawState();
if (state == MWMechanics::DrawState_Spell || state == MWMechanics::DrawState_Nothing)
mPlayer->setDrawState(MWMechanics::DrawState_Weapon);
@ -1026,6 +1042,17 @@ namespace MWInput
void InputManager::toggleConsole()
{
/*
Start of tes3mp addition
If a player's console is disabled by the server, go no further
*/
if (!mwmp::Main::get().getLocalPlayer()->consoleAllowed)
return;
/*
End of tes3mp addition
*/
if (MyGUI::InputManager::getInstance ().isModalAny())
return;
@ -1050,6 +1077,7 @@ namespace MWInput
return;
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()->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(); )
{
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)
mSpells.erase(it++);
else

@ -89,7 +89,7 @@ namespace MWMechanics
void purgeEffect (short effectId, const std::string& sourceId);
/// 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
void purge (int casterActorId);

@ -832,7 +832,7 @@ namespace MWMechanics
creatureStats.getActiveSpells().visitEffectSources(updateSummonedCreatures);
if (ptr.getClass().hasInventoryStore(ptr))
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)
{
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()
{
@ -1149,6 +1171,7 @@ namespace MWMechanics
// target lists get updated once every 1.0 sec
if (timerUpdateAITargets >= 1.0f) timerUpdateAITargets = 0;
if (timerUpdateHeadTrack >= 0.3f) timerUpdateHeadTrack = 0;
if (mTimerDisposeSummonsCorpses >= 0.2f) mTimerDisposeSummonsCorpses = 0;
if (timerUpdateEquippedLight >= updateEquippedLightInterval) timerUpdateEquippedLight = 0;
MWWorld::Ptr player = getPlayer();
@ -1318,6 +1341,7 @@ namespace MWMechanics
timerUpdateAITargets += duration;
timerUpdateHeadTrack += duration;
timerUpdateEquippedLight += duration;
mTimerDisposeSummonsCorpses += duration;
// Looping magic VFX update
// Note: we need to do this before any of the animations are updated.
@ -1955,6 +1979,16 @@ namespace MWMechanics
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()
{
if (!MWBase::Environment::get().getMechanicsManager()->isAIActive())

@ -117,6 +117,9 @@ namespace MWMechanics
End of tes3mp addition
*/
bool isRunning(const MWWorld::Ptr& ptr);
bool isSneaking(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);
@ -157,9 +160,11 @@ namespace MWMechanics
void clear(); // Clear death counter
bool isReadyToBlock(const MWWorld::Ptr& ptr) const;
bool isAttackingOrSpell(const MWWorld::Ptr& ptr) const;
private:
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));
int types = getRangeTypes(enchantment->mEffects);
isRanged = (types & RangeTypes::Target) | (types & RangeTypes::Self);
return suggestCombatRange(types);
}

@ -54,7 +54,7 @@ namespace MWMechanics
virtual float getCombatRange (bool& isRanged) const;
/// 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
@ -68,7 +68,7 @@ namespace MWMechanics
virtual bool isAttackingOrSpell() const { return false; }
/// 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

@ -22,8 +22,15 @@ struct AiFollowStorage : AiTemporaryBase
{
float mTimer;
bool mMoving;
AiFollowStorage() : mTimer(0.f), mMoving(false) {}
float mTargetAngleRadians;
bool mTurnActorToTarget;
AiFollowStorage() :
mTimer(0.f),
mMoving(false),
mTargetAngleRadians(0.f),
mTurnActorToTarget(false)
{}
};
int AiFollow::mFollowIndexCounter = 0;
@ -73,6 +80,15 @@ bool AiFollow::execute (const MWWorld::Ptr& actor, CharacterController& characte
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
if (!mActive)
{
@ -144,13 +160,33 @@ bool AiFollow::execute (const MWWorld::Ptr& actor, CharacterController& characte
//Set the target destination from the actor
ESM::Pathgrid::Point dest = target.getRefData().getPosition().pos;
if (!storage.mMoving)
{
const short threshold = 10; // to avoid constant switching between moving/stopping
short baseFollowDistance = followDistance;
short threshold = 30; // to avoid constant switching between moving/stopping
if (storage.mMoving)
followDistance -= threshold;
else
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)
{

@ -457,7 +457,9 @@ bool MWMechanics::Alchemy::knownEffect(unsigned int potionEffectIndex, const MWW
static const float fWortChanceValue =
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fWortChanceValue")->getFloat();
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)

@ -1768,7 +1768,7 @@ void CharacterController::update(float duration)
mSecondsOfSwimming -= 1;
}
}
else if(isrunning)
else if(isrunning && !sneak)
{
mSecondsOfRunning += duration;
while(mSecondsOfRunning > 1)
@ -1806,7 +1806,7 @@ void CharacterController::update(float duration)
else
fatigueLoss = fFatigueSwimRunBase + encumbrance * fFatigueSwimRunMult;
}
if (isrunning)
else if (isrunning)
fatigueLoss = fFatigueRunBase + encumbrance * fFatigueRunMult;
}
}
@ -2367,6 +2367,12 @@ bool CharacterController::isKnockedOut() const
return mHitState == CharState_KnockOut;
}
bool CharacterController::isAttackingOrSpell() const
{
return mUpperBodyState != UpperCharState_Nothing &&
mUpperBodyState != UpperCharState_WeapEquiped;
}
bool CharacterController::isSneaking() const
{
return mIdleState == CharState_IdleSneak ||
@ -2376,6 +2382,18 @@ bool CharacterController::isSneaking() const
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)
{
mAttackingOrSpell = attackingOrSpell;

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

@ -26,6 +26,7 @@
#include "../mwworld/inventorystore.hpp"
#include "../mwworld/class.hpp"
#include "../mwworld/player.hpp"
#include "../mwworld/ptr.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
@ -435,6 +436,16 @@ namespace MWMechanics
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)
{
mActors.rest(sleep);
@ -845,8 +856,17 @@ namespace MWMechanics
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();
bool isOwned = !owner.empty() && owner != "player";
@ -888,7 +908,7 @@ namespace MWMechanics
}
MWWorld::Ptr victim;
if (isAllowedToUse(ptr, bed.getCellRef(), victim))
if (isAllowedToUse(ptr, bed, victim))
return false;
if(commitCrime(ptr, victim, OT_SleepingInOwnedBed))
@ -903,7 +923,7 @@ namespace MWMechanics
void MechanicsManager::objectOpened(const MWWorld::Ptr &ptr, const MWWorld::Ptr &item)
{
MWWorld::Ptr victim;
if (isAllowedToUse(ptr, item.getCellRef(), victim))
if (isAllowedToUse(ptr, item, victim))
return;
commitCrime(ptr, victim, OT_Trespassing);
}
@ -933,6 +953,43 @@ namespace MWMechanics
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)
{
MWWorld::ContainerStore& store = player.getClass().getContainerStore(player);
@ -986,7 +1043,7 @@ namespace MWMechanics
}
}
if (isAllowedToUse(ptr, *ownerCellRef, victim))
if (isAllowedToUse(ptr, item, victim))
return;
Owner owner;
@ -1067,11 +1124,6 @@ namespace MWMechanics
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;
}
}
@ -1173,10 +1225,25 @@ namespace MWMechanics
if (it->getClass().getCreatureStats(*it).getAiSequence().isInCombat(victim))
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?
if (it->getClass().getCreatureStats(*it).getAiSetting(CreatureStats::AI_Alarm).getBase() >= 100)
{
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"))
@ -1189,7 +1256,9 @@ namespace MWMechanics
it->getClass().getNpcStats(*it).setCrimeId(id);
if (!it->getClass().getCreatureStats(*it).getAiSequence().hasPackage(AiPackage::TypeIdPursue))
{
it->getClass().getCreatureStats(*it).getAiSequence().stack(AiPursue(player), *it);
}
}
else
{
@ -1579,6 +1648,11 @@ namespace MWMechanics
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)
{
MWMechanics::NpcStats& npcStats = actor.getClass().getNpcStats(actor);

@ -198,6 +198,8 @@ namespace MWMechanics
virtual void keepPlayerAlive();
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);
@ -207,15 +209,19 @@ namespace MWMechanics
/// Has the player stolen this item from the given owner?
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?
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 applyWerewolfAcrobatics(const MWWorld::Ptr& actor);
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:
void reportCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim,
OffenseType type, int arg=0);

@ -137,6 +137,11 @@ namespace MWMechanics
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)
return 0;
@ -154,11 +159,6 @@ namespace MWMechanics
return 100;
}
float castBonus = -stats.getMagicEffects().get(ESM::MagicEffect::Sound).getMagnitude();
float castChance = calcSpellBaseSuccessChance(spell, actor, effectiveSchool) + castBonus;
castChance *= stats.getFatigueTerm();
if (!cap)
return std::max(0.f, castChance);
else
@ -715,7 +715,7 @@ namespace MWMechanics
}
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;
}
else if (target.getClass().isActor() && target == getPlayer())

@ -25,6 +25,14 @@ namespace
const MWMechanics::ActiveSpells& activeSpells = actor.getClass().getCreatureStats(actor).getActiveSpells();
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;
for (std::vector<MWMechanics::ActiveSpells::ActiveEffect>::const_iterator effectIt = params.mEffects.begin();
effectIt != params.mEffects.end(); ++effectIt)
@ -46,6 +54,26 @@ namespace
}
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
@ -114,15 +142,39 @@ namespace MWMechanics
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)
{
return rateEffects(enchantment->mEffects, actor, enemy);
}
else
else if (enchantment->mData.mType == ESM::Enchantment::WhenUsed)
{
//if (!ptr.getClass().canBeEquipped(ptr, actor))
return 0.f;
MWWorld::InventoryStore& store = actor.getClass().getInventoryStore(actor);
// 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)
@ -444,6 +496,15 @@ namespace MWMechanics
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);
// 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);
// Update summon effects
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)
{
@ -143,21 +127,18 @@ namespace MWMechanics
}
}
// Update summon effects
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())
bool found = mActiveEffects.find(it->first) != mActiveEffects.end();
if (!found)
{
// 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(ptr))
mActor.getClass().getInventoryStore(mActor).purgeEffect(it->first.first, it->first.second);
// Effect has ended
MWBase::Environment::get().getMechanicsManager()->cleanupSummonedCreature(mActor, it->second);
creatureMap.erase(it++);
continue;
}
else
++it;
++it;
}
std::vector<int>& graveyard = creatureStats.getSummonedCreatureGraveyard();
@ -192,6 +173,26 @@ namespace MWMechanics
else
++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);
/// To call after all effect sources have been visited
void process();
void process(bool cleanup);
private:
MWWorld::Ptr mActor;

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

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

Loading…
Cancel
Save