diff --git a/CI/before_install.osx.sh b/CI/before_install.osx.sh index 25e1c9517..86e8fb81b 100755 --- a/CI/before_install.osx.sh +++ b/CI/before_install.osx.sh @@ -6,5 +6,5 @@ brew outdated cmake || brew upgrade cmake brew outdated pkgconfig || brew upgrade pkgconfig brew install $macos_qt_formula -curl https://downloads.openmw.org/osx/dependencies/openmw-deps-5e144e2.zip -o ~/openmw-deps.zip +curl https://downloads.openmw.org/osx/dependencies/openmw-deps-c40905f.zip -o ~/openmw-deps.zip unzip ~/openmw-deps.zip -d /private/tmp/openmw-deps > /dev/null diff --git a/README.md b/README.md index fa526f85d..d26b97a3b 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ TES3MP [![Build Status](https://travis-ci.org/TES3MP/openmw-tes3mp.svg?branch=master)](https://travis-ci.org/TES3MP/openmw-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". +TES3MP is a project aiming to add multiplayer functionality to [OpenMW](https://github.com/OpenMW/openmw), a free and open source engine recreation of the popular Bethesda Softworks game "The Elder Scrolls III: Morrowind". * Version: 0.7-alpha * License: GPLv3 (see docs/license/GPL3.txt for more information) @@ -25,17 +25,17 @@ Contributing Development has been relatively fast, but any contribution regarding [code](https://github.com/TES3MP/openmw-tes3mp/blob/master/CONTRIBUTING.md), documentation, bug hunting or video showcases is greatly appreciated. -Test sessions are often advertised in [our Steam group](https://steamcommunity.com/groups/mwmulti) or [our Discord server](https://discord.gg/H8zhhuk). +Test sessions are often advertised in [our Steam group](https://steamcommunity.com/groups/mwmulti) or [our Discord server](https://discord.gg/ECJk293). Feel free to contact the [team members](https://github.com/TES3MP/openmw-tes3mp/blob/master/tes3mp-credits.md) for any questions you might have. Getting Started --------------- +* [Quickstart guide](https://github.com/TES3MP/openmw-tes3mp/wiki/Quickstart-guide) +* [Steam group](https://steamcommunity.com/groups/mwmulti) and its [detailed FAQ](http://steamcommunity.com/groups/mwmulti/discussions/1/353916184342480541/) * [TES3MP section on OpenMW forums](https://forum.openmw.org/viewforum.php?f=44) -* [Steam group](https://steamcommunity.com/groups/mwmulti) * [Subreddit](https://www.reddit.com/r/tes3mp) -* [Installation and build instructions](https://github.com/TES3MP/openmw-tes3mp/wiki/Installation-and-build-instructions) * [Known issues and bug reports](https://github.com/TES3MP/openmw-tes3mp/issues) Donations diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 281921c81..777bb2d52 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -18,7 +18,7 @@ opencs_hdrs_noqt (model/doc opencs_units (model/world - idtable idtableproxymodel regionmap data commanddispatcher idtablebase resourcetable nestedtableproxymodel idtree infotableproxymodel + idtable idtableproxymodel regionmap data commanddispatcher idtablebase resourcetable nestedtableproxymodel idtree infotableproxymodel landtexturetableproxymodel ) @@ -70,7 +70,7 @@ opencs_units (view/world cellcreator pathgridcreator referenceablecreator startscriptcreator referencecreator scenesubview infocreator scriptedit dialoguesubview previewsubview regionmap dragrecordtable nestedtable dialoguespinbox recordbuttonbar tableeditidaction scripterrortable extendedcommandconfigurator - bodypartcreator + bodypartcreator landtexturecreator landcreator ) opencs_units_noqt (view/world diff --git a/apps/opencs/model/prefs/state.cpp b/apps/opencs/model/prefs/state.cpp index 5c0b2e282..1f84c5a87 100644 --- a/apps/opencs/model/prefs/state.cpp +++ b/apps/opencs/model/prefs/state.cpp @@ -240,6 +240,8 @@ void CSMPrefs::State::declare() declareShortcut ("document-world-cells", "Open Cell List", QKeySequence()); declareShortcut ("document-world-referencables", "Open Object List", QKeySequence()); declareShortcut ("document-world-references", "Open Instance List", QKeySequence()); + declareShortcut ("document-world-lands", "Open Lands List", QKeySequence()); + declareShortcut ("document-world-landtextures", "Open Land Textures List", QKeySequence()); declareShortcut ("document-world-pathgrid", "Open Pathgrid List", QKeySequence()); declareShortcut ("document-world-regionmap", "Open Region Map", QKeySequence()); declareShortcut ("document-mechanics-globals", "Open Global List", QKeySequence()); @@ -276,6 +278,7 @@ void CSMPrefs::State::declare() declareShortcut ("table-edit", "Edit Record", QKeySequence()); declareShortcut ("table-add", "Add Row/Record", QKeySequence(Qt::ShiftModifier | Qt::Key_A)); declareShortcut ("table-clone", "Clone Record", QKeySequence(Qt::ShiftModifier | Qt::Key_D)); + declareShortcut ("touch-record", "Touch Record", QKeySequence()); declareShortcut ("table-revert", "Revert Record", QKeySequence()); declareShortcut ("table-remove", "Remove Row/Record", QKeySequence(Qt::Key_Delete)); declareShortcut ("table-moveup", "Move Record Up", QKeySequence()); diff --git a/apps/opencs/model/world/collection.hpp b/apps/opencs/model/world/collection.hpp index 16f5ce51f..80117d0c6 100644 --- a/apps/opencs/model/world/collection.hpp +++ b/apps/opencs/model/world/collection.hpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -13,8 +14,9 @@ #include #include "columnbase.hpp" - #include "collectionbase.hpp" +#include "land.hpp" +#include "landtexture.hpp" namespace CSMWorld { @@ -22,15 +24,14 @@ namespace CSMWorld template struct IdAccessor { - std::string& getId (ESXRecordT& record); - + void setId(ESXRecordT& record, const std::string& id) const; const std::string getId (const ESXRecordT& record) const; }; template - std::string& IdAccessor::getId (ESXRecordT& record) + void IdAccessor::setId(ESXRecordT& record, const std::string& id) const { - return record.mId; + record.mId = id; } template @@ -39,6 +40,39 @@ namespace CSMWorld return record.mId; } + template<> + inline void IdAccessor::setId (Land& record, const std::string& id) const + { + int x=0, y=0; + + Land::parseUniqueRecordId(id, x, y); + record.mX = x; + record.mY = y; + } + + template<> + inline void IdAccessor::setId (LandTexture& record, const std::string& id) const + { + int plugin = 0; + int index = 0; + + LandTexture::parseUniqueRecordId(id, plugin, index); + record.mPluginIndex = plugin; + record.mIndex = index; + } + + template<> + inline const std::string IdAccessor::getId (const Land& record) const + { + return Land::createUniqueRecordId(record.mX, record.mY); + } + + template<> + inline const std::string IdAccessor::getId (const LandTexture& record) const + { + return LandTexture::createUniqueRecordId(record.mPluginIndex, record.mIndex); + } + /// \brief Single-type record collection template > class Collection : public CollectionBase @@ -69,6 +103,13 @@ namespace CSMWorld /// /// \return Success? + int cloneRecordImp (const std::string& origin, const std::string& dest, + UniversalId::Type type); + ///< Returns the index of the clone. + + int touchRecordImp (const std::string& id); + ///< Returns the index of the record on success, -1 on failure. + public: Collection(); @@ -108,6 +149,10 @@ namespace CSMWorld const std::string& destination, const UniversalId::Type type); + virtual bool touchRecord(const std::string& id); + ///< Change the state of a record from base to modified, if it is not already. + /// \return True if the record was changed. + virtual int searchId (const std::string& id) const; ////< Search record with \a id. /// \return index of record (if found) or -1 (not found) @@ -205,17 +250,72 @@ namespace CSMWorld return true; } + template + int Collection::cloneRecordImp(const std::string& origin, + const std::string& destination, UniversalId::Type type) + { + Record copy; + copy.mModified = getRecord(origin).get(); + copy.mState = RecordBase::State_ModifiedOnly; + IdAccessorT().setId(copy.get(), destination); + + int index = getAppendIndex(destination, type); + insertRecord(copy, getAppendIndex(destination, type)); + + return index; + } + + template + int Collection::touchRecordImp(const std::string& id) + { + int index = getIndex(id); + Record& record = mRecords.at(index); + if (record.isDeleted()) + { + throw std::runtime_error("attempt to touch deleted record"); + } + + if (!record.isModified()) + { + record.setModified(record.get()); + return index; + } + + return -1; + } + template void Collection::cloneRecord(const std::string& origin, - const std::string& destination, - const UniversalId::Type type) + const std::string& destination, const UniversalId::Type type) { - Record copy; - copy.mModified = getRecord(origin).get(); - copy.mState = RecordBase::State_ModifiedOnly; - copy.get().mId = destination; + cloneRecordImp(origin, destination, type); + } - insertRecord(copy, getAppendIndex(destination, type)); + template<> + inline void Collection >::cloneRecord(const std::string& origin, + const std::string& destination, const UniversalId::Type type) + { + int index = cloneRecordImp(origin, destination, type); + mRecords.at(index).get().mPlugin = 0; + } + + template + bool Collection::touchRecord(const std::string& id) + { + return touchRecordImp(id) != -1; + } + + template<> + inline bool Collection >::touchRecord(const std::string& id) + { + int index = touchRecordImp(id); + if (index >= 0) + { + mRecords.at(index).get().mPlugin = 0; + return true; + } + + return false; } template @@ -366,7 +466,7 @@ namespace CSMWorld UniversalId::Type type) { ESXRecordT record; - IdAccessorT().getId (record) = id; + IdAccessorT().setId(record, id); record.blank(); Record record2; diff --git a/apps/opencs/model/world/collectionbase.hpp b/apps/opencs/model/world/collectionbase.hpp index ef826e31c..bac790c5d 100644 --- a/apps/opencs/model/world/collectionbase.hpp +++ b/apps/opencs/model/world/collectionbase.hpp @@ -78,6 +78,8 @@ namespace CSMWorld const std::string& destination, const UniversalId::Type type) = 0; + virtual bool touchRecord(const std::string& id) = 0; + virtual const RecordBase& getRecord (const std::string& id) const = 0; virtual const RecordBase& getRecord (int index) const = 0; diff --git a/apps/opencs/model/world/columnimp.cpp b/apps/opencs/model/world/columnimp.cpp index dc3d39edb..18da81b53 100644 --- a/apps/opencs/model/world/columnimp.cpp +++ b/apps/opencs/model/world/columnimp.cpp @@ -1,28 +1,341 @@ #include "columnimp.hpp" -CSMWorld::BodyPartRaceColumn::BodyPartRaceColumn(const MeshTypeColumn *meshType) - : mMeshType(meshType) -{} +#include +#include -QVariant CSMWorld::BodyPartRaceColumn::get(const Record &record) const +namespace CSMWorld { - if (mMeshType != NULL && mMeshType->get(record) == ESM::BodyPart::MT_Skin) + /* LandTextureNicknameColumn */ + LandTextureNicknameColumn::LandTextureNicknameColumn() + : Column(Columns::ColumnId_TextureNickname, ColumnBase::Display_String) { - return QString::fromUtf8(record.get().mRace.c_str()); } - return QVariant(QVariant::UserType); -} -void CSMWorld::BodyPartRaceColumn::set(Record &record, const QVariant &data) -{ - ESM::BodyPart record2 = record.get(); + QVariant LandTextureNicknameColumn::get(const Record& record) const + { + return QString::fromUtf8(record.get().mId.c_str()); + } - record2.mRace = data.toString().toUtf8().constData(); + void LandTextureNicknameColumn::set(Record& record, const QVariant& data) + { + LandTexture copy = record.get(); + copy.mId = data.toString().toUtf8().constData(); + record.setModified(copy); + } - record.setModified(record2); -} + bool LandTextureNicknameColumn::isEditable() const + { + return true; + } -bool CSMWorld::BodyPartRaceColumn::isEditable() const -{ - return true; + /* LandTextureIndexColumn */ + LandTextureIndexColumn::LandTextureIndexColumn() + : Column(Columns::ColumnId_TextureIndex, ColumnBase::Display_Integer) + { + } + + QVariant LandTextureIndexColumn::get(const Record& record) const + { + return record.get().mIndex; + } + + bool LandTextureIndexColumn::isEditable() const + { + return false; + } + + /* LandPluginIndexColumn */ + LandPluginIndexColumn::LandPluginIndexColumn() + : Column(Columns::ColumnId_PluginIndex, ColumnBase::Display_Integer, 0) + { + } + + QVariant LandPluginIndexColumn::get(const Record& record) const + { + return record.get().mPlugin; + } + + bool LandPluginIndexColumn::isEditable() const + { + return false; + } + + /* LandTexturePluginIndexColumn */ + LandTexturePluginIndexColumn::LandTexturePluginIndexColumn() + : Column(Columns::ColumnId_PluginIndex, ColumnBase::Display_Integer, 0) + { + } + + QVariant LandTexturePluginIndexColumn::get(const Record& record) const + { + return record.get().mPluginIndex; + } + + bool LandTexturePluginIndexColumn::isEditable() const + { + return false; + } + + /* LandMapLodColumn */ + LandMapLodColumn::LandMapLodColumn() + : Column(Columns::ColumnId_LandMapLodIndex, ColumnBase::Display_String, 0) + { + } + + QVariant LandMapLodColumn::get(const Record& record) const + { + const int Size = Land::LAND_GLOBAL_MAP_LOD_SIZE; + const Land& land = record.get(); + + DataType values(Size, 0); + + if (land.isDataLoaded(Land::DATA_WNAM)) + { + for (int i = 0; i < Size; ++i) + values[i] = land.mWnam[i]; + } + + QVariant variant; + variant.setValue(values); + return variant; + } + + void LandMapLodColumn::set(Record& record, const QVariant& data) + { + DataType values = data.value(); + + if (values.size() != Land::LAND_GLOBAL_MAP_LOD_SIZE) + throw std::runtime_error("invalid land map LOD data"); + + Land copy = record.get(); + copy.setDataLoaded(Land::DATA_WNAM); + + for (int i = 0; i < values.size(); ++i) + { + copy.mWnam[i] = values[i]; + } + + record.setModified(copy); + } + + bool LandMapLodColumn::isEditable() const + { + return true; + } + + /* LandNormalsColumn */ + LandNormalsColumn::LandNormalsColumn() + : Column(Columns::ColumnId_LandNormalsIndex, ColumnBase::Display_String, 0) + { + } + + QVariant LandNormalsColumn::get(const Record& record) const + { + const int Size = Land::LAND_NUM_VERTS * 3; + const Land& land = record.get(); + + DataType values(Size, 0); + + if (land.isDataLoaded(Land::DATA_VNML)) + { + for (int i = 0; i < Size; ++i) + values[i] = land.getLandData()->mNormals[i]; + } + + QVariant variant; + variant.setValue(values); + return variant; + } + + void LandNormalsColumn::set(Record& record, const QVariant& data) + { + DataType values = data.value(); + + if (values.size() != Land::LAND_NUM_VERTS * 3) + throw std::runtime_error("invalid land normals data"); + + Land copy = record.get(); + copy.setDataLoaded(Land::DATA_VNML); + + for (int i = 0; i < values.size(); ++i) + { + copy.getLandData()->mNormals[i] = values[i]; + } + + record.setModified(copy); + } + + bool LandNormalsColumn::isEditable() const + { + return true; + } + + /* LandHeightsColumn */ + LandHeightsColumn::LandHeightsColumn() + : Column(Columns::ColumnId_LandHeightsIndex, ColumnBase::Display_String, 0) + { + } + + QVariant LandHeightsColumn::get(const Record& record) const + { + const int Size = Land::LAND_NUM_VERTS; + const Land& land = record.get(); + + DataType values(Size, 0); + + if (land.isDataLoaded(Land::DATA_VHGT)) + { + for (int i = 0; i < Size; ++i) + values[i] = land.getLandData()->mHeights[i]; + } + + QVariant variant; + variant.setValue(values); + return variant; + } + + void LandHeightsColumn::set(Record& record, const QVariant& data) + { + DataType values = data.value(); + + if (values.size() != Land::LAND_NUM_VERTS) + throw std::runtime_error("invalid land heights data"); + + Land copy = record.get(); + copy.setDataLoaded(Land::DATA_VHGT); + + for (int i = 0; i < values.size(); ++i) + { + copy.getLandData()->mHeights[i] = values[i]; + } + + record.setModified(copy); + } + + bool LandHeightsColumn::isEditable() const + { + return true; + } + + /* LandColoursColumn */ + LandColoursColumn::LandColoursColumn() + : Column(Columns::ColumnId_LandColoursIndex, ColumnBase::Display_String, 0) + { + } + + QVariant LandColoursColumn::get(const Record& record) const + { + const int Size = Land::LAND_NUM_VERTS * 3; + const Land& land = record.get(); + + DataType values(Size, 0); + + if (land.isDataLoaded(Land::DATA_VCLR)) + { + for (int i = 0; i < Size; ++i) + values[i] = land.getLandData()->mColours[i]; + } + + QVariant variant; + variant.setValue(values); + return variant; + } + + void LandColoursColumn::set(Record& record, const QVariant& data) + { + DataType values = data.value(); + + if (values.size() != Land::LAND_NUM_VERTS * 3) + throw std::runtime_error("invalid land colours data"); + + Land copy = record.get(); + copy.setDataLoaded(Land::DATA_VCLR); + + for (int i = 0; i < values.size(); ++i) + { + copy.getLandData()->mColours[i] = values[i]; + } + + record.setModified(copy); + } + + bool LandColoursColumn::isEditable() const + { + return true; + } + + /* LandTexturesColumn */ + LandTexturesColumn::LandTexturesColumn() + : Column(Columns::ColumnId_LandTexturesIndex, ColumnBase::Display_String, 0) + { + } + + QVariant LandTexturesColumn::get(const Record& record) const + { + const int Size = Land::LAND_NUM_TEXTURES; + const Land& land = record.get(); + + DataType values(Size, 0); + + if (land.isDataLoaded(Land::DATA_VTEX)) + { + for (int i = 0; i < Size; ++i) + values[i] = land.getLandData()->mTextures[i]; + } + + QVariant variant; + variant.setValue(values); + return variant; + } + + void LandTexturesColumn::set(Record& record, const QVariant& data) + { + DataType values = data.value(); + + if (values.size() != Land::LAND_NUM_TEXTURES) + throw std::runtime_error("invalid land textures data"); + + Land copy = record.get(); + copy.setDataLoaded(Land::DATA_VTEX); + + for (int i = 0; i < values.size(); ++i) + { + copy.getLandData()->mTextures[i] = values[i]; + } + + record.setModified(copy); + } + + bool LandTexturesColumn::isEditable() const + { + return true; + } + + /* BodyPartRaceColumn */ + BodyPartRaceColumn::BodyPartRaceColumn(const MeshTypeColumn *meshType) + : mMeshType(meshType) + {} + + QVariant BodyPartRaceColumn::get(const Record &record) const + { + if (mMeshType != NULL && mMeshType->get(record) == ESM::BodyPart::MT_Skin) + { + return QString::fromUtf8(record.get().mRace.c_str()); + } + return QVariant(QVariant::UserType); + } + + void BodyPartRaceColumn::set(Record &record, const QVariant &data) + { + ESM::BodyPart record2 = record.get(); + + record2.mRace = data.toString().toUtf8().constData(); + + record.setModified(record2); + } + + bool BodyPartRaceColumn::isEditable() const + { + return true; + } } diff --git a/apps/opencs/model/world/columnimp.hpp b/apps/opencs/model/world/columnimp.hpp index 154bdab82..e36e386c9 100644 --- a/apps/opencs/model/world/columnimp.hpp +++ b/apps/opencs/model/world/columnimp.hpp @@ -2,10 +2,12 @@ #define CSM_WOLRD_COLUMNIMP_H #include +#include #include #include #include +#include #include #include @@ -15,6 +17,9 @@ #include "columns.hpp" #include "info.hpp" +#include "land.hpp" +#include "landtexture.hpp" + namespace CSMWorld { /// \note Shares ID with VarValueColumn. A table can not have both. @@ -60,6 +65,20 @@ namespace CSMWorld } }; + template<> + inline QVariant StringIdColumn::get(const Record& record) const + { + const Land& land = record.get(); + return QString::fromUtf8(Land::createUniqueRecordId(land.mX, land.mY).c_str()); + } + + template<> + inline QVariant StringIdColumn::get(const Record& record) const + { + const LandTexture& ltex = record.get(); + return QString::fromUtf8(LandTexture::createUniqueRecordId(ltex.mPluginIndex, ltex.mIndex).c_str()); + } + template struct RecordStateColumn : public Column { @@ -1499,9 +1518,9 @@ namespace CSMWorld template struct TopicColumn : public Column { - TopicColumn (bool journal) + TopicColumn (bool journal) : Column (journal ? Columns::ColumnId_Journal : Columns::ColumnId_Topic, - journal ? ColumnBase::Display_Journal : ColumnBase::Display_Topic) + journal ? ColumnBase::Display_Journal : ColumnBase::Display_Topic) {} virtual QVariant get (const Record& record) const @@ -1755,7 +1774,7 @@ namespace CSMWorld return true; } }; - + template struct GenderNpcColumn : public Column { @@ -2198,8 +2217,8 @@ namespace CSMWorld struct EffectTextureColumn : public Column { EffectTextureColumn (Columns::ColumnId columnId) - : Column (columnId, - columnId == Columns::ColumnId_Particle ? ColumnBase::Display_Texture + : Column (columnId, + columnId == Columns::ColumnId_Particle ? ColumnBase::Display_Texture : ColumnBase::Display_Icon) { assert (this->mColumnId==Columns::ColumnId_Icon || @@ -2417,7 +2436,95 @@ namespace CSMWorld return true; } }; - + + struct LandTextureNicknameColumn : public Column + { + LandTextureNicknameColumn(); + + QVariant get(const Record& record) const override; + void set(Record& record, const QVariant& data) override; + bool isEditable() const override; + }; + + struct LandTextureIndexColumn : public Column + { + LandTextureIndexColumn(); + + QVariant get(const Record& record) const override; + bool isEditable() const override; + }; + + struct LandPluginIndexColumn : public Column + { + LandPluginIndexColumn(); + + QVariant get(const Record& record) const override; + bool isEditable() const override; + }; + + struct LandTexturePluginIndexColumn : public Column + { + LandTexturePluginIndexColumn(); + + QVariant get(const Record& record) const override; + bool isEditable() const override; + }; + + struct LandMapLodColumn : public Column + { + using DataType = QVector; + + LandMapLodColumn(); + + QVariant get(const Record& record) const override; + void set(Record& record, const QVariant& data) override; + bool isEditable() const override; + }; + + struct LandNormalsColumn : public Column + { + using DataType = QVector; + + LandNormalsColumn(); + + QVariant get(const Record& record) const override; + void set(Record& record, const QVariant& data) override; + bool isEditable() const override; + }; + + struct LandHeightsColumn : public Column + { + using DataType = QVector; + + LandHeightsColumn(); + + QVariant get(const Record& record) const override; + void set(Record& record, const QVariant& data) override; + bool isEditable() const override; + }; + + struct LandColoursColumn : public Column + { + using DataType = QVector; + + LandColoursColumn(); + + QVariant get(const Record& record) const override; + void set(Record& record, const QVariant& data) override; + bool isEditable() const override; + }; + + struct LandTexturesColumn : public Column + { + using DataType = QVector; + + LandTexturesColumn(); + + QVariant get(const Record& record) const override; + void set(Record& record, const QVariant& data) override; + bool isEditable() const override; + }; + struct BodyPartRaceColumn : public RaceColumn { const MeshTypeColumn *mMeshType; @@ -2430,4 +2537,11 @@ namespace CSMWorld }; } +// This is required to access the type as a QVariant. +Q_DECLARE_METATYPE(CSMWorld::LandMapLodColumn::DataType) +//Q_DECLARE_METATYPE(CSMWorld::LandNormalsColumn::DataType) // Same as LandMapLodColumn::DataType +Q_DECLARE_METATYPE(CSMWorld::LandHeightsColumn::DataType) +Q_DECLARE_METATYPE(CSMWorld::LandColoursColumn::DataType) +Q_DECLARE_METATYPE(CSMWorld::LandTexturesColumn::DataType) + #endif diff --git a/apps/opencs/model/world/columns.cpp b/apps/opencs/model/world/columns.cpp index aeee0d208..ec010ba36 100644 --- a/apps/opencs/model/world/columns.cpp +++ b/apps/opencs/model/world/columns.cpp @@ -124,7 +124,7 @@ namespace CSMWorld { ColumnId_Portable, "Portable" }, { ColumnId_NegativeLight, "Negative Light" }, { ColumnId_EmitterType, "Emitter Type" }, - + { ColumnId_Fire, "Fire" }, { ColumnId_OffByDefault, "Off by default" }, { ColumnId_IsKey, "Is Key" }, @@ -330,6 +330,14 @@ namespace CSMWorld { ColumnId_WeatherChance, "Percent Chance" }, { ColumnId_Text, "Text" }, + { ColumnId_TextureNickname, "Texture Nickname" }, + { ColumnId_PluginIndex, "Plugin Index" }, + { ColumnId_TextureIndex, "Texture Index" }, + { ColumnId_LandMapLodIndex, "Land map height LOD" }, + { ColumnId_LandNormalsIndex, "Land normals" }, + { ColumnId_LandHeightsIndex, "Land heights" }, + { ColumnId_LandColoursIndex, "Land colors" }, + { ColumnId_LandTexturesIndex, "Land textures" }, { ColumnId_UseValue1, "Use value 1" }, { ColumnId_UseValue2, "Use value 2" }, diff --git a/apps/opencs/model/world/columns.hpp b/apps/opencs/model/world/columns.hpp index b23d86367..15018795c 100644 --- a/apps/opencs/model/world/columns.hpp +++ b/apps/opencs/model/world/columns.hpp @@ -329,6 +329,15 @@ namespace CSMWorld ColumnId_Text = 297, + ColumnId_TextureNickname = 298, + ColumnId_PluginIndex = 299, + ColumnId_TextureIndex = 300, + ColumnId_LandMapLodIndex = 301, + ColumnId_LandNormalsIndex = 302, + ColumnId_LandHeightsIndex = 303, + ColumnId_LandColoursIndex = 304, + ColumnId_LandTexturesIndex = 305, + // Allocated to a separate value range, so we don't get a collision should we ever need // to extend the number of use values. ColumnId_UseValue1 = 0x10000, diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index 5f9422376..79900c6c4 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -2,6 +2,7 @@ #include #include +#include #include @@ -15,6 +16,175 @@ #include "nestedtablewrapper.hpp" #include "pathgrid.hpp" +CSMWorld::TouchCommand::TouchCommand(IdTable& table, const std::string& id, QUndoCommand* parent) + : QUndoCommand(parent) + , mTable(table) + , mId(id) + , mOld(nullptr) + , mChanged(false) +{ + setText(("Touch " + mId).c_str()); + mOld.reset(mTable.getRecord(mId).clone()); +} + +void CSMWorld::TouchCommand::redo() +{ + mChanged = mTable.touchRecord(mId); +} + +void CSMWorld::TouchCommand::undo() +{ + if (mChanged) + { + mTable.setRecord(mId, *mOld); + mChanged = false; + } +} + +CSMWorld::ImportLandTexturesCommand::ImportLandTexturesCommand(IdTable& landTable, + IdTable& ltexTable, QUndoCommand* parent) + : QUndoCommand(parent) + , mLands(landTable) + , mLtexs(ltexTable) + , mOldState(0) +{ + setText("Copy land textures to current plugin"); +} + +void CSMWorld::ImportLandTexturesCommand::redo() +{ + int pluginColumn = mLands.findColumnIndex(Columns::ColumnId_PluginIndex); + int oldPlugin = mLands.data(mLands.getModelIndex(getOriginId(), pluginColumn)).toInt(); + + // Original data + int textureColumn = mLands.findColumnIndex(Columns::ColumnId_LandTexturesIndex); + mOld = mLands.data(mLands.getModelIndex(getOriginId(), textureColumn)).value(); + + // Need to make a copy so the old values can be looked up + DataType copy(mOld); + + // Perform touch/copy/etc... + onRedo(); + + // Find all indices used + std::unordered_set texIndices; + for (int i = 0; i < mOld.size(); ++i) + { + // All indices are offset by 1 for a default texture + if (mOld[i] > 0) + texIndices.insert(mOld[i] - 1); + } + + std::vector oldTextures; + for (int index : texIndices) + { + oldTextures.push_back(LandTexture::createUniqueRecordId(oldPlugin, index)); + } + + // Import the textures, replace old values + LandTextureIdTable::ImportResults results = dynamic_cast(mLtexs).importTextures(oldTextures); + mCreatedTextures = std::move(results.createdRecords); + for (const auto& it : results.recordMapping) + { + int plugin = 0, newIndex = 0, oldIndex = 0; + LandTexture::parseUniqueRecordId(it.first, plugin, oldIndex); + LandTexture::parseUniqueRecordId(it.second, plugin, newIndex); + + if (newIndex != oldIndex) + { + for (int i = 0; i < Land::LAND_NUM_TEXTURES; ++i) + { + // All indices are offset by 1 for a default texture + if (mOld[i] == oldIndex + 1) + copy[i] = newIndex + 1; + } + } + } + + // Apply modification + int stateColumn = mLands.findColumnIndex(Columns::ColumnId_Modification); + mOldState = mLands.data(mLands.getModelIndex(getDestinationId(), stateColumn)).toInt(); + + QVariant variant; + variant.setValue(copy); + mLands.setData(mLands.getModelIndex(getDestinationId(), textureColumn), variant); +} + +void CSMWorld::ImportLandTexturesCommand::undo() +{ + // Restore to previous + int textureColumn = mLands.findColumnIndex(Columns::ColumnId_LandTexturesIndex); + QVariant variant; + variant.setValue(mOld); + mLands.setData(mLands.getModelIndex(getDestinationId(), textureColumn), variant); + + int stateColumn = mLands.findColumnIndex(Columns::ColumnId_Modification); + mLands.setData(mLands.getModelIndex(getDestinationId(), stateColumn), mOldState); + + // Undo copy/touch/etc... + onUndo(); + + for (const std::string& id : mCreatedTextures) + { + int row = mLtexs.getModelIndex(id, 0).row(); + mLtexs.removeRows(row, 1); + } + mCreatedTextures.clear(); +} + +CSMWorld::CopyLandTexturesCommand::CopyLandTexturesCommand(IdTable& landTable, IdTable& ltexTable, + const std::string& origin, const std::string& dest, QUndoCommand* parent) + : ImportLandTexturesCommand(landTable, ltexTable, parent) + , mOriginId(origin) + , mDestId(dest) +{ +} + +const std::string& CSMWorld::CopyLandTexturesCommand::getOriginId() const +{ + return mOriginId; +} + +const std::string& CSMWorld::CopyLandTexturesCommand::getDestinationId() const +{ + return mDestId; +} + +CSMWorld::TouchLandCommand::TouchLandCommand(IdTable& landTable, IdTable& ltexTable, + const std::string& id, QUndoCommand* parent) + : ImportLandTexturesCommand(landTable, ltexTable, parent) + , mId(id) + , mOld(nullptr) + , mChanged(false) +{ + setText(("Touch " + mId).c_str()); + mOld.reset(mLands.getRecord(mId).clone()); +} + +const std::string& CSMWorld::TouchLandCommand::getOriginId() const +{ + return mId; +} + +const std::string& CSMWorld::TouchLandCommand::getDestinationId() const +{ + return mId; +} + +void CSMWorld::TouchLandCommand::onRedo() +{ + mChanged = mLands.touchRecord(mId); +} + +void CSMWorld::TouchLandCommand::onUndo() +{ + if (mChanged) + { + mLands.setRecord(mId, *mOld); + mChanged = false; + } +} + CSMWorld::ModifyCommand::ModifyCommand (QAbstractItemModel& model, const QModelIndex& index, const QVariant& new_, QUndoCommand* parent) : QUndoCommand (parent), mModel (&model), mIndex (index), mNew (new_), mHasRecordState(false), mOldRecordState(CSMWorld::RecordBase::State_BaseOnly) diff --git a/apps/opencs/model/world/commands.hpp b/apps/opencs/model/world/commands.hpp index b54a1d5ac..be86dd508 100644 --- a/apps/opencs/model/world/commands.hpp +++ b/apps/opencs/model/world/commands.hpp @@ -5,12 +5,14 @@ #include #include +#include #include #include #include #include +#include "columnimp.hpp" #include "universalid.hpp" #include "nestedtablewrapper.hpp" @@ -24,6 +26,91 @@ namespace CSMWorld struct RecordBase; struct NestedTableWrapperBase; + class TouchCommand : public QUndoCommand + { + public: + + TouchCommand(IdTable& model, const std::string& id, QUndoCommand* parent=nullptr); + + void redo() override; + void undo() override; + + private: + + IdTable& mTable; + std::string mId; + std::unique_ptr mOld; + + bool mChanged; + }; + + class ImportLandTexturesCommand : public QUndoCommand + { + public: + + ImportLandTexturesCommand(IdTable& landTable, IdTable& ltexTable, + QUndoCommand* parent); + + void redo() override; + void undo() override; + + protected: + + using DataType = LandTexturesColumn::DataType; + + virtual const std::string& getOriginId() const = 0; + virtual const std::string& getDestinationId() const = 0; + + virtual void onRedo() = 0; + virtual void onUndo() = 0; + + IdTable& mLands; + IdTable& mLtexs; + DataType mOld; + int mOldState; + std::vector mCreatedTextures; + }; + + class CopyLandTexturesCommand : public ImportLandTexturesCommand + { + public: + + CopyLandTexturesCommand(IdTable& landTable, IdTable& ltexTable, const std::string& origin, + const std::string& dest, QUndoCommand* parent = nullptr); + + private: + + const std::string& getOriginId() const override; + const std::string& getDestinationId() const override; + + void onRedo() override {} + void onUndo() override {} + + std::string mOriginId; + std::string mDestId; + }; + + class TouchLandCommand : public ImportLandTexturesCommand + { + public: + + TouchLandCommand(IdTable& landTable, IdTable& ltexTable, + const std::string& id, QUndoCommand* parent = nullptr); + + private: + + const std::string& getOriginId() const override; + const std::string& getDestinationId() const override; + + void onRedo() override; + void onUndo() override; + + std::string mId; + std::unique_ptr mOld; + + bool mChanged; + }; + class ModifyCommand : public QUndoCommand { QAbstractItemModel *mModel; diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index 007190e4a..2216d5ca6 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -412,6 +412,24 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, bool fsStrict, const Files::Pat Columns::ColumnId_NegativeLight, ESM::MagicEffect::NegativeLight)); mMagicEffects.addColumn (new DescriptionColumn); + mLand.addColumn (new StringIdColumn); + mLand.addColumn (new RecordStateColumn); + mLand.addColumn (new FixedRecordTypeColumn(UniversalId::Type_Land)); + mLand.addColumn (new LandPluginIndexColumn); + mLand.addColumn (new LandMapLodColumn); + mLand.addColumn (new LandNormalsColumn); + mLand.addColumn (new LandHeightsColumn); + mLand.addColumn (new LandColoursColumn); + mLand.addColumn (new LandTexturesColumn); + + mLandTextures.addColumn (new StringIdColumn(true)); + mLandTextures.addColumn (new RecordStateColumn); + mLandTextures.addColumn (new FixedRecordTypeColumn(UniversalId::Type_LandTexture)); + mLandTextures.addColumn (new LandTextureNicknameColumn); + mLandTextures.addColumn (new LandTexturePluginIndexColumn); + mLandTextures.addColumn (new LandTextureIndexColumn); + mLandTextures.addColumn (new TextureColumn); + mPathgrids.addColumn (new StringIdColumn); mPathgrids.addColumn (new RecordStateColumn); mPathgrids.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Pathgrid)); @@ -531,6 +549,8 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, bool fsStrict, const Files::Pat addModel (new IdTable (&mBodyParts), UniversalId::Type_BodyPart); addModel (new IdTable (&mSoundGens), UniversalId::Type_SoundGen); addModel (new IdTable (&mMagicEffects), UniversalId::Type_MagicEffect); + addModel (new IdTable (&mLand, IdTable::Feature_AllowTouch), UniversalId::Type_Land); + addModel (new LandTextureIdTable (&mLandTextures), UniversalId::Type_LandTexture); addModel (new IdTree (&mPathgrids, &mPathgrids), UniversalId::Type_Pathgrid); addModel (new IdTable (&mStartScripts), UniversalId::Type_StartScript); addModel (new IdTree (&mReferenceables, &mReferenceables, IdTable::Feature_Preview), @@ -993,19 +1013,7 @@ bool CSMWorld::Data::continueLoading (CSMDoc::Messages& messages) case ESM::REC_LTEX: mLandTextures.load (*mReader, mBase); break; - case ESM::REC_LAND: - { - int index = mLand.load(*mReader, mBase); - - // Load all land data for now. A future optimisation may only load non-base data - // if a suitable mechanism for avoiding race conditions can be established. - if (index!=-1/* && !mBase*/) - mLand.getRecord (index).get().loadData ( - ESM::Land::DATA_VHGT | ESM::Land::DATA_VNML | ESM::Land::DATA_VCLR | - ESM::Land::DATA_VTEX); - - break; - } + case ESM::REC_LAND: mLand.load(*mReader, mBase); break; case ESM::REC_CELL: { diff --git a/apps/opencs/model/world/idcollection.hpp b/apps/opencs/model/world/idcollection.hpp index ea6eefb88..7849aab92 100644 --- a/apps/opencs/model/world/idcollection.hpp +++ b/apps/opencs/model/world/idcollection.hpp @@ -4,6 +4,7 @@ #include #include "collection.hpp" +#include "land.hpp" namespace CSMWorld { @@ -39,6 +40,22 @@ namespace CSMWorld record.load (reader, isDeleted); } + template<> + inline void IdCollection >::loadRecord (Land& record, + ESM::ESMReader& reader, bool& isDeleted) + { + record.load (reader, isDeleted); + + // Load all land data for now. A future optimisation may only load non-base data + // if a suitable mechanism for avoiding race conditions can be established. + int flags = ESM::Land::DATA_VHGT | ESM::Land::DATA_VNML | + ESM::Land::DATA_VCLR | ESM::Land::DATA_VTEX; + record.loadData (flags); + + // Prevent data from being reloaded. + record.mContext.filename.clear(); + } + template int IdCollection::load (ESM::ESMReader& reader, bool base) { diff --git a/apps/opencs/model/world/idtable.cpp b/apps/opencs/model/world/idtable.cpp index 7975e05ea..fcfc8577e 100644 --- a/apps/opencs/model/world/idtable.cpp +++ b/apps/opencs/model/world/idtable.cpp @@ -1,11 +1,17 @@ #include "idtable.hpp" +#include +#include +#include +#include +#include #include #include #include "collectionbase.hpp" #include "columnbase.hpp" +#include "landtexture.hpp" CSMWorld::IdTable::IdTable (CollectionBase *idCollection, unsigned int features) : IdTableBase (features), mIdCollection (idCollection) @@ -179,6 +185,26 @@ void CSMWorld::IdTable::cloneRecord(const std::string& origin, endInsertRows(); } +bool CSMWorld::IdTable::touchRecord(const std::string& id) +{ + bool changed = mIdCollection->touchRecord(id); + + int row = mIdCollection->getIndex(id); + int column = mIdCollection->searchColumnIndex(Columns::ColumnId_RecordType); + if (changed && column != -1) + { + QModelIndex modelIndex = index(row, column); + emit dataChanged(modelIndex, modelIndex); + } + + return changed; +} + +std::string CSMWorld::IdTable::getId(int row) const +{ + return mIdCollection->getId(row); +} + ///This method can return only indexes to the top level table cells QModelIndex CSMWorld::IdTable::getModelIndex (const std::string& id, int column) const { @@ -281,3 +307,72 @@ CSMWorld::CollectionBase *CSMWorld::IdTable::idCollection() const { return mIdCollection; } + +CSMWorld::LandTextureIdTable::LandTextureIdTable(CollectionBase* idCollection, unsigned int features) + : IdTable(idCollection, features) +{ +} + +CSMWorld::LandTextureIdTable::ImportResults CSMWorld::LandTextureIdTable::importTextures(const std::vector& ids) +{ + ImportResults results; + + // Map existing textures to ids + std::map reverseLookupMap; + for (int i = 0; i < idCollection()->getSize(); ++i) + { + auto& record = static_cast&>(idCollection()->getRecord(i)); + std::string texture = record.get().mTexture; + std::transform(texture.begin(), texture.end(), texture.begin(), tolower); + if (record.isModified()) + reverseLookupMap.emplace(texture, idCollection()->getId(i)); + } + + for (const std::string& id : ids) + { + int plugin, index; + + LandTexture::parseUniqueRecordId(id, plugin, index); + int oldRow = idCollection()->searchId(id); + + // If it does not exist or it is in the current plugin, it can be skipped. + if (oldRow <= 0 || plugin == 0) + { + results.recordMapping.push_back(std::make_pair(id, id)); + continue; + } + + // Look for a pre-existing record + auto& record = static_cast&>(idCollection()->getRecord(oldRow)); + std::string texture = record.get().mTexture; + std::transform(texture.begin(), texture.end(), texture.begin(), tolower); + auto searchIt = reverseLookupMap.find(texture); + if (searchIt != reverseLookupMap.end()) + { + results.recordMapping.push_back(std::make_pair(id, searchIt->second)); + continue; + } + + // Iterate until an unused index or found, or the index has completely wrapped around. + int startIndex = index; + do { + std::string newId = LandTexture::createUniqueRecordId(0, index); + int newRow = idCollection()->searchId(newId); + + if (newRow < 0) + { + // Id not taken, clone it + cloneRecord(id, newId, UniversalId::Type_LandTexture); + results.createdRecords.push_back(newId); + results.recordMapping.push_back(std::make_pair(id, newId)); + reverseLookupMap.emplace(texture, newId); + break; + } + + const size_t MaxIndex = std::numeric_limits::max() - 1; + index = (index + 1) % MaxIndex; + } while (index != startIndex); + } + + return results; +} diff --git a/apps/opencs/model/world/idtable.hpp b/apps/opencs/model/world/idtable.hpp index 9faf64d71..4136061e4 100644 --- a/apps/opencs/model/world/idtable.hpp +++ b/apps/opencs/model/world/idtable.hpp @@ -61,6 +61,11 @@ namespace CSMWorld const std::string& destination, UniversalId::Type type = UniversalId::Type_None); + bool touchRecord(const std::string& id); + ///< Will change the record state to modified, if it is not already. + + std::string getId(int row) const; + virtual QModelIndex getModelIndex (const std::string& id, int column) const; void setRecord (const std::string& id, const RecordBase& record, @@ -93,6 +98,29 @@ namespace CSMWorld virtual CollectionBase *idCollection() const; }; + + /// An IdTable customized to handle the more unique needs of LandTextureId's which behave + /// differently from other records. The major difference is that base records cannot be + /// modified. + class LandTextureIdTable : public IdTable + { + public: + + struct ImportResults + { + using StringPair = std::pair; + + /// The newly added records + std::vector createdRecords; + /// The 1st string is the original id, the 2nd is the mapped id + std::vector recordMapping; + }; + + LandTextureIdTable(CollectionBase* idCollection, unsigned int features=0); + + /// Finds and maps/recreates the specified ids. + ImportResults importTextures(const std::vector& ids); + }; } #endif diff --git a/apps/opencs/model/world/idtablebase.hpp b/apps/opencs/model/world/idtablebase.hpp index 0d77d48ef..8b82f984b 100644 --- a/apps/opencs/model/world/idtablebase.hpp +++ b/apps/opencs/model/world/idtablebase.hpp @@ -32,7 +32,9 @@ namespace CSMWorld Feature_Preview = 8, /// Table can not be modified through ordinary means. - Feature_Constant = 16 + Feature_Constant = 16, + + Feature_AllowTouch = 32 }; private: @@ -61,7 +63,7 @@ namespace CSMWorld virtual bool isDeleted (const std::string& id) const = 0; virtual int getColumnId (int column) const = 0; - + unsigned int getFeatures() const; }; } diff --git a/apps/opencs/model/world/land.cpp b/apps/opencs/model/world/land.cpp index 80f86c746..bfa927444 100644 --- a/apps/opencs/model/world/land.cpp +++ b/apps/opencs/model/world/land.cpp @@ -1,15 +1,30 @@ #include "land.hpp" #include +#include namespace CSMWorld { void Land::load(ESM::ESMReader &esm, bool &isDeleted) { ESM::Land::load(esm, isDeleted); + } + std::string Land::createUniqueRecordId(int x, int y) + { std::ostringstream stream; - stream << "#" << mX << " " << mY; - mId = stream.str(); + stream << "#" << x << " " << y; + return stream.str(); + } + + void Land::parseUniqueRecordId(const std::string& id, int& x, int& y) + { + size_t mid = id.find(' '); + + if (mid == std::string::npos || id[0] != '#') + throw std::runtime_error("Invalid Land ID"); + + x = std::stoi(id.substr(1, mid - 1)); + y = std::stoi(id.substr(mid + 1)); } } diff --git a/apps/opencs/model/world/land.hpp b/apps/opencs/model/world/land.hpp index e5f25c1d3..e604f1311 100644 --- a/apps/opencs/model/world/land.hpp +++ b/apps/opencs/model/world/land.hpp @@ -12,10 +12,11 @@ namespace CSMWorld /// \todo Add worldspace support to the Land record. struct Land : public ESM::Land { - std::string mId; - /// Loads the metadata and ID void load (ESM::ESMReader &esm, bool &isDeleted); + + static std::string createUniqueRecordId(int x, int y); + static void parseUniqueRecordId(const std::string& id, int& x, int& y); }; } diff --git a/apps/opencs/model/world/landtexture.cpp b/apps/opencs/model/world/landtexture.cpp index 266377d0f..43deb64a4 100644 --- a/apps/opencs/model/world/landtexture.cpp +++ b/apps/opencs/model/world/landtexture.cpp @@ -1,5 +1,8 @@ #include "landtexture.hpp" +#include +#include + #include namespace CSMWorld @@ -11,4 +14,21 @@ namespace CSMWorld mPluginIndex = esm.getIndex(); } + std::string LandTexture::createUniqueRecordId(int plugin, int index) + { + std::stringstream ss; + ss << 'L' << plugin << '#' << index; + return ss.str(); + } + + void LandTexture::parseUniqueRecordId(const std::string& id, int& plugin, int& index) + { + size_t middle = id.find('#'); + + if (middle == std::string::npos || id[0] != 'L') + throw std::runtime_error("Invalid LandTexture ID"); + + plugin = std::stoi(id.substr(1,middle-1)); + index = std::stoi(id.substr(middle+1)); + } } diff --git a/apps/opencs/model/world/landtexture.hpp b/apps/opencs/model/world/landtexture.hpp index 91459eee2..a7376438c 100644 --- a/apps/opencs/model/world/landtexture.hpp +++ b/apps/opencs/model/world/landtexture.hpp @@ -13,6 +13,11 @@ namespace CSMWorld int mPluginIndex; void load (ESM::ESMReader &esm, bool &isDeleted); + + /// Returns a string identifier that will be unique to any LandTexture. + static std::string createUniqueRecordId(int plugin, int index); + /// Deconstructs a unique string identifier into plugin and index. + static void parseUniqueRecordId(const std::string& id, int& plugin, int& index); }; } diff --git a/apps/opencs/model/world/landtexturetableproxymodel.cpp b/apps/opencs/model/world/landtexturetableproxymodel.cpp new file mode 100644 index 000000000..cf33fab9e --- /dev/null +++ b/apps/opencs/model/world/landtexturetableproxymodel.cpp @@ -0,0 +1,21 @@ +#include "landtexturetableproxymodel.hpp" + +#include "idtable.hpp" + +namespace CSMWorld +{ + LandTextureTableProxyModel::LandTextureTableProxyModel(QObject* parent) + : IdTableProxyModel(parent) + { + } + + bool LandTextureTableProxyModel::filterAcceptsRow (int sourceRow, const QModelIndex& sourceParent) const + { + int columnIndex = mSourceModel->findColumnIndex(Columns::ColumnId_Modification); + QModelIndex index = mSourceModel->index(sourceRow, columnIndex); + if (mSourceModel->data(index).toInt() != RecordBase::State_ModifiedOnly) + return false; + + return IdTableProxyModel::filterAcceptsRow(sourceRow, sourceParent); + } +} diff --git a/apps/opencs/model/world/landtexturetableproxymodel.hpp b/apps/opencs/model/world/landtexturetableproxymodel.hpp new file mode 100644 index 000000000..bbedecb53 --- /dev/null +++ b/apps/opencs/model/world/landtexturetableproxymodel.hpp @@ -0,0 +1,22 @@ +#ifndef CSM_WORLD_LANDTEXTURETABLEPROXYMODEL_H +#define CSM_WORLD_LANDTEXTURETABLEPROXYMODEL_H + +#include "idtableproxymodel.hpp" + +namespace CSMWorld +{ + /// \brief Removes base records from filtered results. + class LandTextureTableProxyModel : public IdTableProxyModel + { + Q_OBJECT + public: + + LandTextureTableProxyModel(QObject* parent = nullptr); + + protected: + + bool filterAcceptsRow (int sourceRow, const QModelIndex& sourceParent) const override; + }; +} + +#endif diff --git a/apps/opencs/model/world/refidcollection.cpp b/apps/opencs/model/world/refidcollection.cpp index 31dae256e..44a6ce07d 100644 --- a/apps/opencs/model/world/refidcollection.cpp +++ b/apps/opencs/model/world/refidcollection.cpp @@ -813,6 +813,12 @@ void CSMWorld::RefIdCollection::cloneRecord(const std::string& origin, mData.insertRecord(*newRecord, type, destination); } +bool CSMWorld::RefIdCollection::touchRecord(const std::string& id) +{ + throw std::runtime_error("RefIdCollection::touchRecord is unimplemented"); + return false; +} + void CSMWorld::RefIdCollection::appendRecord (const RecordBase& record, UniversalId::Type type) { diff --git a/apps/opencs/model/world/refidcollection.hpp b/apps/opencs/model/world/refidcollection.hpp index de5a709c6..48558d1c2 100644 --- a/apps/opencs/model/world/refidcollection.hpp +++ b/apps/opencs/model/world/refidcollection.hpp @@ -80,6 +80,8 @@ namespace CSMWorld const std::string& destination, const UniversalId::Type type); + virtual bool touchRecord(const std::string& id); + virtual void appendBlankRecord (const std::string& id, UniversalId::Type type); ///< \param type Will be ignored, unless the collection supports multiple record types diff --git a/apps/opencs/model/world/universalid.cpp b/apps/opencs/model/world/universalid.cpp index 77db2be10..38386f6da 100644 --- a/apps/opencs/model/world/universalid.cpp +++ b/apps/opencs/model/world/universalid.cpp @@ -53,6 +53,8 @@ namespace { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_RunLog, "Run Log", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_SoundGens, "Sound Generators", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_MagicEffects, "Magic Effects", 0 }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Lands, "Lands", 0 }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_LandTextures, "LandTextures", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Pathgrids, "Pathgrids", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_StartScripts, "Start Scripts", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_MetaDatas, "Meta Data Table", 0 }, @@ -118,6 +120,8 @@ namespace { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_DebugProfile, "Debug Profile", 0 }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_SoundGen, "Sound Generator", 0 }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_MagicEffect, "Magic Effect", 0 }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Land, "Land", 0 }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_LandTexture, "LandTexture", 0 }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Pathgrid, "Pathgrid", 0 }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_StartScript, "Start Script", 0 }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_MetaData, "Meta Data", 0 }, @@ -364,8 +368,8 @@ std::vector CSMWorld::UniversalId::listTypes (int c for (int i=0; sIndexArg[i].mName; ++i) if (sIndexArg[i].mClass & classes) list.push_back (sIndexArg[i].mType); - - return list; + + return list; } CSMWorld::UniversalId::Type CSMWorld::UniversalId::getParentType (Type type) diff --git a/apps/opencs/model/world/universalid.hpp b/apps/opencs/model/world/universalid.hpp index e9104fc22..accd1b78d 100644 --- a/apps/opencs/model/world/universalid.hpp +++ b/apps/opencs/model/world/universalid.hpp @@ -126,6 +126,10 @@ namespace CSMWorld Type_SoundGen, Type_MagicEffects, Type_MagicEffect, + Type_Lands, + Type_Land, + Type_LandTextures, + Type_LandTexture, Type_Pathgrids, Type_Pathgrid, Type_StartScripts, diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index dfbeea031..10de46e06 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -172,6 +172,16 @@ void CSVDoc::View::setupWorldMenu() setupShortcut("document-world-references", references); world->addAction (references); + QAction *lands = new QAction (tr ("Lands"), this); + connect (lands, SIGNAL (triggered()), this, SLOT (addLandsSubView())); + setupShortcut("document-world-lands", lands); + world->addAction (lands); + + QAction *landTextures = new QAction (tr ("Land Textures"), this); + connect (landTextures, SIGNAL (triggered()), this, SLOT (addLandTexturesSubView())); + setupShortcut("document-world-landtextures", landTextures); + world->addAction (landTextures); + QAction *grid = new QAction (tr ("Pathgrid"), this); connect (grid, SIGNAL (triggered()), this, SLOT (addPathgridSubView())); setupShortcut("document-world-pathgrid", grid); @@ -876,6 +886,16 @@ void CSVDoc::View::addRunLogSubView() addSubView (CSMWorld::UniversalId::Type_RunLog); } +void CSVDoc::View::addLandsSubView() +{ + addSubView (CSMWorld::UniversalId::Type_Lands); +} + +void CSVDoc::View::addLandTexturesSubView() +{ + addSubView (CSMWorld::UniversalId::Type_LandTextures); +} + void CSVDoc::View::addPathgridSubView() { addSubView (CSMWorld::UniversalId::Type_Pathgrids); diff --git a/apps/opencs/view/doc/view.hpp b/apps/opencs/view/doc/view.hpp index 834407be0..46aa87891 100644 --- a/apps/opencs/view/doc/view.hpp +++ b/apps/opencs/view/doc/view.hpp @@ -224,6 +224,10 @@ namespace CSVDoc void addRunLogSubView(); + void addLandsSubView(); + + void addLandTexturesSubView(); + void addPathgridSubView(); void addStartScriptsSubView(); diff --git a/apps/opencs/view/render/cell.cpp b/apps/opencs/view/render/cell.cpp index 765c5b316..552a54ac2 100644 --- a/apps/opencs/view/render/cell.cpp +++ b/apps/opencs/view/render/cell.cpp @@ -26,6 +26,33 @@ #include "terrainstorage.hpp" #include "object.hpp" +namespace CSVRender +{ + class CellNodeContainer : public osg::Referenced + { + public: + + CellNodeContainer(Cell* cell) : mCell(cell) {} + + Cell* getCell(){ return mCell; } + + private: + + Cell* mCell; + }; + + class CellNodeCallback : public osg::NodeCallback + { + public: + + virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) + { + CellNodeContainer* container = static_cast(node->getUserData()); + container->getCell()->updateLand(); + } + }; +} + bool CSVRender::Cell::removeObject (const std::string& id) { std::map::iterator iter = @@ -75,10 +102,69 @@ bool CSVRender::Cell::addObjects (int start, int end) return modified; } +void CSVRender::Cell::updateLand() +{ + if (!mUpdateLand || mLandDeleted) + return; + + mUpdateLand = false; + + // Cell is deleted + if (mDeleted) + { + unloadLand(); + return; + } + + // Setup land if available + const CSMWorld::IdCollection& land = mData.getLand(); + int landIndex = land.searchId(mId); + if (landIndex != -1 && !land.getRecord(mId).isDeleted()) + { + const ESM::Land& esmLand = land.getRecord(mId).get(); + + if (esmLand.getLandData (ESM::Land::DATA_VHGT)) + { + if (mTerrain) + { + mTerrain->unloadCell(mCoordinates.getX(), mCoordinates.getY()); + mTerrain->clearAssociatedCaches(); + } + else + { + mTerrain.reset(new Terrain::TerrainGrid(mCellNode, mCellNode, + mData.getResourceSystem().get(), new TerrainStorage(mData), Mask_Terrain)); + } + + mTerrain->loadCell(esmLand.mX, esmLand.mY); + + if (!mCellBorder) + mCellBorder.reset(new CellBorder(mCellNode, mCoordinates)); + + mCellBorder->buildShape(esmLand); + + return; + } + } + + // No land data + mLandDeleted = true; + unloadLand(); +} + +void CSVRender::Cell::unloadLand() +{ + if (mTerrain) + mTerrain->unloadCell(mCoordinates.getX(), mCoordinates.getY()); + + if (mCellBorder) + mCellBorder.reset(); +} + CSVRender::Cell::Cell (CSMWorld::Data& data, osg::Group* rootNode, const std::string& id, bool deleted) : mData (data), mId (Misc::StringUtils::lowerCase (id)), mDeleted (deleted), mSubMode (0), - mSubModeElementMask (0) + mSubModeElementMask (0), mUpdateLand(true), mLandDeleted(false) { std::pair result = CSMWorld::CellCoordinates::fromId (id); @@ -86,6 +172,8 @@ CSVRender::Cell::Cell (CSMWorld::Data& data, osg::Group* rootNode, const std::st mCoordinates = result.first; mCellNode = new osg::Group; + mCellNode->setUserData(new CellNodeContainer(this)); + mCellNode->setUpdateCallback(new CellNodeCallback); rootNode->addChild(mCellNode); setCellMarker(); @@ -99,22 +187,7 @@ CSVRender::Cell::Cell (CSMWorld::Data& data, osg::Group* rootNode, const std::st addObjects (0, rows-1); - const CSMWorld::IdCollection& land = mData.getLand(); - int landIndex = land.searchId(mId); - if (landIndex != -1) - { - const ESM::Land& esmLand = land.getRecord(mId).get(); - - if (esmLand.getLandData (ESM::Land::DATA_VHGT)) - { - mTerrain.reset(new Terrain::TerrainGrid(mCellNode, mCellNode, data.getResourceSystem().get(), new TerrainStorage(mData), Mask_Terrain)); - mTerrain->loadCell(esmLand.mX, - esmLand.mY); - - mCellBorder.reset(new CellBorder(mCellNode, mCoordinates)); - mCellBorder->buildShape(esmLand); - } - } + updateLand(); mPathgrid.reset(new Pathgrid(mData, mCellNode, mId, mCoordinates)); mCellWater.reset(new CellWater(mData, mCellNode, mId, mCoordinates)); @@ -285,6 +358,38 @@ void CSVRender::Cell::pathgridRemoved() mPathgrid->removeGeometry(); } +void CSVRender::Cell::landDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) +{ + mUpdateLand = true; +} + +void CSVRender::Cell::landAboutToBeRemoved (const QModelIndex& parent, int start, int end) +{ + mLandDeleted = true; + unloadLand(); +} + +void CSVRender::Cell::landAdded (const QModelIndex& parent, int start, int end) +{ + mUpdateLand = true; + mLandDeleted = false; +} + +void CSVRender::Cell::landTextureChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) +{ + mUpdateLand = true; +} + +void CSVRender::Cell::landTextureAboutToBeRemoved (const QModelIndex& parent, int start, int end) +{ + mUpdateLand = true; +} + +void CSVRender::Cell::landTextureAdded (const QModelIndex& parent, int start, int end) +{ + mUpdateLand = true; +} + void CSVRender::Cell::reloadAssets() { for (std::map::const_iterator iter (mObjects.begin()); diff --git a/apps/opencs/view/render/cell.hpp b/apps/opencs/view/render/cell.hpp index f53f61973..444608688 100644 --- a/apps/opencs/view/render/cell.hpp +++ b/apps/opencs/view/render/cell.hpp @@ -57,6 +57,7 @@ namespace CSVRender bool mDeleted; int mSubMode; unsigned int mSubModeElementMask; + bool mUpdateLand, mLandDeleted; /// Ignored if cell does not have an object with the given ID. /// @@ -72,6 +73,9 @@ namespace CSVRender /// \return Have any objects been added? bool addObjects (int start, int end); + void updateLand(); + void unloadLand(); + public: enum Selection @@ -118,6 +122,18 @@ namespace CSVRender void pathgridRemoved(); + void landDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); + + void landAboutToBeRemoved (const QModelIndex& parent, int start, int end); + + void landAdded (const QModelIndex& parent, int start, int end); + + void landTextureChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); + + void landTextureAboutToBeRemoved (const QModelIndex& parent, int start, int end); + + void landTextureAdded (const QModelIndex& parent, int start, int end); + void reloadAssets(); void setSelection (int elementMask, Selection mode); @@ -145,6 +161,8 @@ namespace CSVRender /// Erase all overrides and restore the visual representation of the cell to its /// true state. void reset (unsigned int elementMask); + + friend class CellNodeCallback; }; } diff --git a/apps/opencs/view/render/pagedworldspacewidget.cpp b/apps/opencs/view/render/pagedworldspacewidget.cpp index b1077139c..4a745195b 100644 --- a/apps/opencs/view/render/pagedworldspacewidget.cpp +++ b/apps/opencs/view/render/pagedworldspacewidget.cpp @@ -351,6 +351,72 @@ void CSVRender::PagedWorldspaceWidget::pathgridAdded(const QModelIndex& parent, } } +void CSVRender::PagedWorldspaceWidget::landDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) +{ + for (int r = topLeft.row(); r <= bottomRight.row(); ++r) + { + std::string id = mDocument.getData().getLand().getId(r); + + auto cellIt = mCells.find(CSMWorld::CellCoordinates::fromId(id).first); + if (cellIt != mCells.end()) + { + cellIt->second->landDataChanged(topLeft, bottomRight); + flagAsModified(); + } + } +} + +void CSVRender::PagedWorldspaceWidget::landAboutToBeRemoved (const QModelIndex& parent, int start, int end) +{ + for (int r = start; r <= end; ++r) + { + std::string id = mDocument.getData().getLand().getId(r); + + auto cellIt = mCells.find(CSMWorld::CellCoordinates::fromId(id).first); + if (cellIt != mCells.end()) + { + cellIt->second->landAboutToBeRemoved(parent, start, end); + flagAsModified(); + } + } +} + +void CSVRender::PagedWorldspaceWidget::landAdded (const QModelIndex& parent, int start, int end) +{ + for (int r = start; r <= end; ++r) + { + std::string id = mDocument.getData().getLand().getId(r); + + auto cellIt = mCells.find(CSMWorld::CellCoordinates::fromId(id).first); + if (cellIt != mCells.end()) + { + cellIt->second->landAdded(parent, start, end); + flagAsModified(); + } + } +} + +void CSVRender::PagedWorldspaceWidget::landTextureDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) +{ + for (auto cellIt : mCells) + cellIt.second->landTextureChanged(topLeft, bottomRight); + flagAsModified(); +} + +void CSVRender::PagedWorldspaceWidget::landTextureAboutToBeRemoved (const QModelIndex& parent, int start, int end) +{ + for (auto cellIt : mCells) + cellIt.second->landTextureAboutToBeRemoved(parent, start, end); + flagAsModified(); +} + +void CSVRender::PagedWorldspaceWidget::landTextureAdded (const QModelIndex& parent, int start, int end) +{ + for (auto cellIt : mCells) + cellIt.second->landTextureAdded(parent, start, end); + flagAsModified(); +} + std::string CSVRender::PagedWorldspaceWidget::getStartupInstruction() { @@ -475,6 +541,24 @@ CSVRender::PagedWorldspaceWidget::PagedWorldspaceWidget (QWidget* parent, CSMDoc connect (&document.getData(), SIGNAL (assetTablesChanged ()), this, SLOT (assetTablesChanged ())); + QAbstractItemModel *lands = document.getData().getTableModel (CSMWorld::UniversalId::Type_Lands); + + connect (lands, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)), + this, SLOT (landDataChanged (const QModelIndex&, const QModelIndex&))); + connect (lands, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)), + this, SLOT (landAboutToBeRemoved (const QModelIndex&, int, int))); + connect (lands, SIGNAL (rowsInserted (const QModelIndex&, int, int)), + this, SLOT (landAdded (const QModelIndex&, int, int))); + + QAbstractItemModel *ltexs = document.getData().getTableModel (CSMWorld::UniversalId::Type_LandTextures); + + connect (ltexs, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)), + this, SLOT (landTextureDataChanged (const QModelIndex&, const QModelIndex&))); + connect (ltexs, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)), + this, SLOT (landTextureAboutToBeRemoved (const QModelIndex&, int, int))); + connect (ltexs, SIGNAL (rowsInserted (const QModelIndex&, int, int)), + this, SLOT (landTextureAdded (const QModelIndex&, int, int))); + // Shortcuts CSMPrefs::Shortcut* loadCameraCellShortcut = new CSMPrefs::Shortcut("scene-load-cam-cell", this); connect(loadCameraCellShortcut, SIGNAL(activated()), this, SLOT(loadCameraCell())); diff --git a/apps/opencs/view/render/pagedworldspacewidget.hpp b/apps/opencs/view/render/pagedworldspacewidget.hpp index 8c41df51e..6672c2268 100644 --- a/apps/opencs/view/render/pagedworldspacewidget.hpp +++ b/apps/opencs/view/render/pagedworldspacewidget.hpp @@ -155,6 +155,14 @@ namespace CSVRender virtual void cellAdded (const QModelIndex& index, int start, int end); + virtual void landDataChanged (const QModelIndex& topLeft, const QModelIndex& botomRight); + virtual void landAboutToBeRemoved (const QModelIndex& parent, int start, int end); + virtual void landAdded (const QModelIndex& parent, int start, int end); + + virtual void landTextureDataChanged (const QModelIndex& topLeft, const QModelIndex& botomRight); + virtual void landTextureAboutToBeRemoved (const QModelIndex& parent, int start, int end); + virtual void landTextureAdded (const QModelIndex& parent, int start, int end); + void assetTablesChanged (); void loadCameraCell(); diff --git a/apps/opencs/view/render/terrainstorage.cpp b/apps/opencs/view/render/terrainstorage.cpp index c63d41be3..51c9dd009 100644 --- a/apps/opencs/view/render/terrainstorage.cpp +++ b/apps/opencs/view/render/terrainstorage.cpp @@ -1,5 +1,8 @@ #include "terrainstorage.hpp" +#include "../../model/world/land.hpp" +#include "../../model/world/landtexture.hpp" + namespace CSVRender { @@ -11,12 +14,9 @@ namespace CSVRender osg::ref_ptr TerrainStorage::getLand(int cellX, int cellY) { - std::ostringstream stream; - stream << "#" << cellX << " " << cellY; - // The cell isn't guaranteed to have Land. This is because the terrain implementation // has to wrap the vertices of the last row and column to the next cell, which may be a nonexisting cell - int index = mData.getLand().searchId(stream.str()); + int index = mData.getLand().searchId(CSMWorld::Land::createUniqueRecordId(cellX, cellY)); if (index == -1) return NULL; @@ -26,16 +26,11 @@ namespace CSVRender const ESM::LandTexture* TerrainStorage::getLandTexture(int index, short plugin) { - int numRecords = mData.getLandTextures().getSize(); - - for (int i=0; imIndex == index && ltex->mPluginIndex == plugin) - return ltex; - } + int row = mData.getLandTextures().searchId(CSMWorld::LandTexture::createUniqueRecordId(plugin, index)); + if (row == -1) + return nullptr; - return NULL; + return &mData.getLandTextures().getRecord(row).get(); } void TerrainStorage::getBounds(float &minX, float &maxX, float &minY, float &maxY) diff --git a/apps/opencs/view/world/creator.hpp b/apps/opencs/view/world/creator.hpp index 320bbf6ae..0020fd3c8 100644 --- a/apps/opencs/view/world/creator.hpp +++ b/apps/opencs/view/world/creator.hpp @@ -31,6 +31,9 @@ namespace CSVWorld virtual void cloneMode(const std::string& originId, const CSMWorld::UniversalId::Type type) = 0; + /// Touches a record, if the creator supports it. + virtual void touch(const std::vector& ids) = 0; + virtual void setEditLock (bool locked) = 0; virtual void toggleWidgets(bool active = true) = 0; diff --git a/apps/opencs/view/world/genericcreator.cpp b/apps/opencs/view/world/genericcreator.cpp index bf4c4967f..5e2118e9b 100644 --- a/apps/opencs/view/world/genericcreator.cpp +++ b/apps/opencs/view/world/genericcreator.cpp @@ -51,6 +51,11 @@ std::string CSVWorld::GenericCreator::getId() const return mId->text().toUtf8().constData(); } +std::string CSVWorld::GenericCreator::getClonedId() const +{ + return mClonedId; +} + std::string CSVWorld::GenericCreator::getIdValidatorResult() const { std::string errors; @@ -254,6 +259,22 @@ void CSVWorld::GenericCreator::cloneMode(const std::string& originId, mClonedType = type; } +void CSVWorld::GenericCreator::touch(const std::vector& ids) +{ + // Combine multiple touch commands into one "macro" command + mUndoStack.beginMacro("Touch Records"); + + CSMWorld::IdTable& table = dynamic_cast(*mData.getTableModel(mListId)); + for (const CSMWorld::UniversalId& uid : ids) + { + CSMWorld::TouchCommand* touchCmd = new CSMWorld::TouchCommand(table, uid.getId()); + mUndoStack.push(touchCmd); + } + + // Execute + mUndoStack.endMacro(); +} + void CSVWorld::GenericCreator::toggleWidgets(bool active) { } diff --git a/apps/opencs/view/world/genericcreator.hpp b/apps/opencs/view/world/genericcreator.hpp index 60d487bc1..3baacfc06 100644 --- a/apps/opencs/view/world/genericcreator.hpp +++ b/apps/opencs/view/world/genericcreator.hpp @@ -64,6 +64,8 @@ namespace CSVWorld virtual std::string getId() const; + std::string getClonedId() const; + virtual std::string getIdValidatorResult() const; /// Allow subclasses to add additional data to \a command. @@ -103,6 +105,8 @@ namespace CSVWorld virtual void cloneMode(const std::string& originId, const CSMWorld::UniversalId::Type type); + virtual void touch(const std::vector& ids); + virtual std::string getErrors() const; ///< Return formatted error descriptions for the current state of the creator. if an empty /// string is returned, there is no error. diff --git a/apps/opencs/view/world/landcreator.cpp b/apps/opencs/view/world/landcreator.cpp new file mode 100644 index 000000000..2ebfe1869 --- /dev/null +++ b/apps/opencs/view/world/landcreator.cpp @@ -0,0 +1,120 @@ +#include "landcreator.hpp" + +#include + +#include +#include + +#include "../../model/world/commands.hpp" +#include "../../model/world/idtable.hpp" +#include "../../model/world/land.hpp" + +namespace CSVWorld +{ + LandCreator::LandCreator(CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id) + : GenericCreator(data, undoStack, id) + , mXLabel(nullptr) + , mYLabel(nullptr) + , mX(nullptr) + , mY(nullptr) + { + const int MaxInt = std::numeric_limits::max(); + const int MinInt = std::numeric_limits::min(); + + setManualEditing(false); + + mXLabel = new QLabel("X: "); + mX = new QSpinBox(); + mX->setMinimum(MinInt); + mX->setMaximum(MaxInt); + insertBeforeButtons(mXLabel, false); + insertBeforeButtons(mX, true); + + mYLabel = new QLabel("Y: "); + mY = new QSpinBox(); + mY->setMinimum(MinInt); + mY->setMaximum(MaxInt); + insertBeforeButtons(mYLabel, false); + insertBeforeButtons(mY, true); + + connect (mX, SIGNAL(valueChanged(int)), this, SLOT(coordChanged(int))); + connect (mY, SIGNAL(valueChanged(int)), this, SLOT(coordChanged(int))); + } + + void LandCreator::cloneMode(const std::string& originId, const CSMWorld::UniversalId::Type type) + { + GenericCreator::cloneMode(originId, type); + + int x = 0, y = 0; + CSMWorld::Land::parseUniqueRecordId(originId, x, y); + + mX->setValue(x); + mY->setValue(y); + } + + void LandCreator::touch(const std::vector& ids) + { + // Combine multiple touch commands into one "macro" command + getUndoStack().beginMacro("Touch records"); + + CSMWorld::IdTable& lands = dynamic_cast(*getData().getTableModel(CSMWorld::UniversalId::Type_Lands)); + CSMWorld::IdTable& ltexs = dynamic_cast(*getData().getTableModel(CSMWorld::UniversalId::Type_LandTextures)); + for (const CSMWorld::UniversalId& uid : ids) + { + CSMWorld::TouchLandCommand* touchCmd = new CSMWorld::TouchLandCommand(lands, ltexs, uid.getId()); + getUndoStack().push(touchCmd); + } + + // Execute + getUndoStack().endMacro(); + } + + void LandCreator::focus() + { + mX->setFocus(); + } + + void LandCreator::reset() + { + GenericCreator::reset(); + mX->setValue(0); + mY->setValue(0); + } + + std::string LandCreator::getErrors() const + { + if (getData().getLand().searchId(getId()) >= 0) + return "A land with that name already exists."; + + return ""; + } + + std::string LandCreator::getId() const + { + return CSMWorld::Land::createUniqueRecordId(mX->value(), mY->value()); + } + + void LandCreator::pushCommand(std::unique_ptr command, const std::string& id) + { + if (mCloneMode) + { + CSMWorld::IdTable& lands = dynamic_cast(*getData().getTableModel(CSMWorld::UniversalId::Type_Lands)); + CSMWorld::IdTable& ltexs = dynamic_cast(*getData().getTableModel(CSMWorld::UniversalId::Type_LandTextures)); + + getUndoStack().beginMacro(("Clone " + id).c_str()); + getUndoStack().push(command.release()); + + CSMWorld::CopyLandTexturesCommand* ltexCopy = new CSMWorld::CopyLandTexturesCommand(lands, ltexs, getClonedId(), getId()); + getUndoStack().push(ltexCopy); + + getUndoStack().endMacro(); + } + else + getUndoStack().push (command.release()); + } + + void LandCreator::coordChanged(int value) + { + update(); + } +} diff --git a/apps/opencs/view/world/landcreator.hpp b/apps/opencs/view/world/landcreator.hpp new file mode 100644 index 000000000..e0c5577e4 --- /dev/null +++ b/apps/opencs/view/world/landcreator.hpp @@ -0,0 +1,47 @@ +#ifndef CSV_WORLD_LANDCREATOR_H +#define CSV_WORLD_LANDCREATOR_H + +#include "genericcreator.hpp" + +class QLabel; +class QSpinBox; + +namespace CSVWorld +{ + class LandCreator : public GenericCreator + { + Q_OBJECT + + QLabel* mXLabel; + QLabel* mYLabel; + QSpinBox* mX; + QSpinBox* mY; + + public: + + LandCreator(CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id); + + void cloneMode(const std::string& originId, const CSMWorld::UniversalId::Type type) override; + + void touch(const std::vector& ids) override; + + void focus() override; + + void reset() override; + + std::string getErrors() const override; + + protected: + + std::string getId() const override; + + void pushCommand(std::unique_ptr command, + const std::string& id) override; + + private slots: + + void coordChanged(int value); + }; +} + +#endif diff --git a/apps/opencs/view/world/landtexturecreator.cpp b/apps/opencs/view/world/landtexturecreator.cpp new file mode 100644 index 000000000..43d911e50 --- /dev/null +++ b/apps/opencs/view/world/landtexturecreator.cpp @@ -0,0 +1,101 @@ +#include "landtexturecreator.hpp" + +#include +#include + +#include +#include +#include + +#include "../../model/world/commands.hpp" +#include "../../model/world/idtable.hpp" +#include "../../model/world/landtexture.hpp" + +namespace CSVWorld +{ + LandTextureCreator::LandTextureCreator(CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id) + : GenericCreator(data, undoStack, id) + { + // One index is reserved for a default texture + const size_t MaxIndex = std::numeric_limits::max() - 1; + + setManualEditing(false); + + QLabel* nameLabel = new QLabel("Name"); + insertBeforeButtons(nameLabel, false); + + mNameEdit = new QLineEdit(this); + insertBeforeButtons(mNameEdit, true); + + QLabel* indexLabel = new QLabel("Index"); + insertBeforeButtons(indexLabel, false); + + mIndexBox = new QSpinBox(this); + mIndexBox->setMinimum(0); + mIndexBox->setMaximum(MaxIndex); + insertBeforeButtons(mIndexBox, true); + + connect(mNameEdit, SIGNAL(textChanged(const QString&)), this, SLOT(nameChanged(const QString&))); + connect(mIndexBox, SIGNAL(valueChanged(int)), this, SLOT(indexChanged(int))); + } + + void LandTextureCreator::cloneMode(const std::string& originId, const CSMWorld::UniversalId::Type type) + { + GenericCreator::cloneMode(originId, type); + + CSMWorld::IdTable& table = dynamic_cast(*getData().getTableModel(getCollectionId())); + + int column = table.findColumnIndex(CSMWorld::Columns::ColumnId_TextureNickname); + mNameEdit->setText((table.data(table.getModelIndex(originId, column)).toString())); + + column = table.findColumnIndex(CSMWorld::Columns::ColumnId_TextureIndex); + mIndexBox->setValue((table.data(table.getModelIndex(originId, column)).toInt())); + } + + void LandTextureCreator::focus() + { + mIndexBox->setFocus(); + } + + void LandTextureCreator::reset() + { + GenericCreator::reset(); + mNameEdit->setText(""); + mIndexBox->setValue(0); + } + + std::string LandTextureCreator::getErrors() const + { + if (getData().getLandTextures().searchId(getId()) >= 0) + { + return "Index is already in use"; + } + + return ""; + } + + void LandTextureCreator::configureCreateCommand(CSMWorld::CreateCommand& command) const + { + GenericCreator::configureCreateCommand(command); + + CSMWorld::IdTable& table = dynamic_cast(*getData().getTableModel(getCollectionId())); + int column = table.findColumnIndex(CSMWorld::Columns::ColumnId_TextureNickname); + command.addValue(column, mName.c_str()); + } + + std::string LandTextureCreator::getId() const + { + return CSMWorld::LandTexture::createUniqueRecordId(0, mIndexBox->value()); + } + + void LandTextureCreator::nameChanged(const QString& value) + { + mName = value.toUtf8().constData(); + update(); + } + + void LandTextureCreator::indexChanged(int value) + { + update(); + } +} diff --git a/apps/opencs/view/world/landtexturecreator.hpp b/apps/opencs/view/world/landtexturecreator.hpp new file mode 100644 index 000000000..b11c47758 --- /dev/null +++ b/apps/opencs/view/world/landtexturecreator.hpp @@ -0,0 +1,49 @@ +#ifndef CSV_WORLD_LANDTEXTURECREATOR_H +#define CSV_WORLD_LANDTEXTURECREATOR_H + +#include + +#include "genericcreator.hpp" + +class QLineEdit; +class QSpinBox; + +namespace CSVWorld +{ + class LandTextureCreator : public GenericCreator + { + Q_OBJECT + + public: + + LandTextureCreator(CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id); + + void cloneMode(const std::string& originId, const CSMWorld::UniversalId::Type type) override; + + void focus() override; + + void reset() override; + + std::string getErrors() const override; + + protected: + + void configureCreateCommand(CSMWorld::CreateCommand& command) const override; + + std::string getId() const override; + + private slots: + + void nameChanged(const QString& val); + void indexChanged(int val); + + private: + + QLineEdit* mNameEdit; + QSpinBox* mIndexBox; + + std::string mName; + }; +} + +#endif diff --git a/apps/opencs/view/world/subviews.cpp b/apps/opencs/view/world/subviews.cpp index 7c27bdf7a..3e72f9a9e 100644 --- a/apps/opencs/view/world/subviews.cpp +++ b/apps/opencs/view/world/subviews.cpp @@ -18,6 +18,8 @@ #include "pathgridcreator.hpp" #include "previewsubview.hpp" #include "bodypartcreator.hpp" +#include "landcreator.hpp" +#include "landtexturecreator.hpp" void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) { @@ -81,6 +83,12 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) manager.add (CSMWorld::UniversalId::Type_Pathgrids, new CSVDoc::SubViewFactoryWithCreator); + manager.add (CSMWorld::UniversalId::Type_Lands, + new CSVDoc::SubViewFactoryWithCreator >); + + manager.add (CSMWorld::UniversalId::Type_LandTextures, + new CSVDoc::SubViewFactoryWithCreator >); + manager.add (CSMWorld::UniversalId::Type_Globals, new CSVDoc::SubViewFactoryWithCreator >); @@ -181,6 +189,12 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) manager.add (CSMWorld::UniversalId::Type_Pathgrid, new CSVDoc::SubViewFactoryWithCreator (false)); + manager.add (CSMWorld::UniversalId::Type_Land, + new CSVDoc::SubViewFactoryWithCreator >(false)); + + manager.add (CSMWorld::UniversalId::Type_LandTexture, + new CSVDoc::SubViewFactoryWithCreator >(false)); + manager.add (CSMWorld::UniversalId::Type_DebugProfile, new CSVDoc::SubViewFactoryWithCreator > (false)); diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index 30dba4241..34ecd57d0 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -16,6 +16,7 @@ #include "../../model/world/idtableproxymodel.hpp" #include "../../model/world/idtablebase.hpp" #include "../../model/world/idtable.hpp" +#include "../../model/world/landtexturetableproxymodel.hpp" #include "../../model/world/record.hpp" #include "../../model/world/columns.hpp" #include "../../model/world/commanddispatcher.hpp" @@ -60,6 +61,9 @@ void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event) menu.addAction(mCloneAction); } + if (mTouchAction) + menu.addAction (mTouchAction); + if (mCreateAction) menu.addAction (mCreateAction); @@ -226,17 +230,22 @@ void CSVWorld::Table::mouseDoubleClickEvent (QMouseEvent *event) CSVWorld::Table::Table (const CSMWorld::UniversalId& id, bool createAndDelete, bool sorting, CSMDoc::Document& document) -: DragRecordTable(document), mCreateAction (0), - mCloneAction(0), mRecordStatusDisplay (0), mJumpToAddedRecord(false), mUnselectAfterJump(false) + : DragRecordTable(document), mCreateAction (nullptr), mCloneAction(nullptr), mTouchAction(nullptr), + mRecordStatusDisplay (0), mJumpToAddedRecord(false), mUnselectAfterJump(false) { mModel = &dynamic_cast (*mDocument.getData().getTableModel (id)); bool isInfoTable = id.getType() == CSMWorld::UniversalId::Type_TopicInfos || id.getType() == CSMWorld::UniversalId::Type_JournalInfos; + bool isLtexTable = (id.getType() == CSMWorld::UniversalId::Type_LandTextures); if (isInfoTable) { mProxyModel = new CSMWorld::InfoTableProxyModel(id.getType(), this); } + else if (isLtexTable) + { + mProxyModel = new CSMWorld::LandTextureTableProxyModel (this); + } else { mProxyModel = new CSMWorld::IdTableProxyModel (this); @@ -302,6 +311,15 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id, cloneShortcut->associateAction(mCloneAction); } + if (mModel->getFeatures() & CSMWorld::IdTableBase::Feature_AllowTouch) + { + mTouchAction = new QAction(tr("Touch Record"), this); + connect(mTouchAction, SIGNAL(triggered()), this, SLOT(touchRecord())); + addAction(mTouchAction); + CSMPrefs::Shortcut* touchShortcut = new CSMPrefs::Shortcut("table-touch", this); + touchShortcut->associateAction(mTouchAction); + } + mRevertAction = new QAction (tr ("Revert Record"), this); connect (mRevertAction, SIGNAL (triggered()), mDispatcher, SLOT (executeRevert())); addAction (mRevertAction); @@ -442,6 +460,22 @@ void CSVWorld::Table::cloneRecord() } } +void CSVWorld::Table::touchRecord() +{ + if (!mEditLock && mModel->getFeatures() & CSMWorld::IdTableBase::Feature_AllowTouch) + { + std::vector touchIds; + + QModelIndexList selectedRows = selectionModel()->selectedRows(); + for (auto it = selectedRows.begin(); it != selectedRows.end(); ++it) + { + touchIds.push_back(getUniversalId(it->row())); + } + + emit touchRequest(touchIds); + } +} + void CSVWorld::Table::moveUpRecord() { if (mEditLock || (mModel->getFeatures() & CSMWorld::IdTableBase::Feature_Constant)) diff --git a/apps/opencs/view/world/table.hpp b/apps/opencs/view/world/table.hpp index 26f6e7899..02f9023e7 100644 --- a/apps/opencs/view/world/table.hpp +++ b/apps/opencs/view/world/table.hpp @@ -56,6 +56,7 @@ namespace CSVWorld QAction *mEditAction; QAction *mCreateAction; QAction *mCloneAction; + QAction *mTouchAction; QAction *mRevertAction; QAction *mDeleteAction; QAction *mMoveUpAction; @@ -115,6 +116,8 @@ namespace CSVWorld void cloneRequest(const CSMWorld::UniversalId&); + void touchRequest(const std::vector& ids); + void closeRequest(); void extendedDeleteConfigRequest(const std::vector &selectedIds); @@ -129,6 +132,8 @@ namespace CSVWorld void cloneRecord(); + void touchRecord(); + void moveUpRecord(); void moveDownRecord(); diff --git a/apps/opencs/view/world/tablebottombox.cpp b/apps/opencs/view/world/tablebottombox.cpp index 5a25bbc53..cfde5c694 100644 --- a/apps/opencs/view/world/tablebottombox.cpp +++ b/apps/opencs/view/world/tablebottombox.cpp @@ -72,9 +72,9 @@ void CSVWorld::TableBottomBox::extendedConfigRequest(CSVWorld::ExtendedCommandCo mExtendedConfigurator->setFocus(); } -CSVWorld::TableBottomBox::TableBottomBox (const CreatorFactoryBase& creatorFactory, - CSMDoc::Document& document, - const CSMWorld::UniversalId& id, +CSVWorld::TableBottomBox::TableBottomBox (const CreatorFactoryBase& creatorFactory, + CSMDoc::Document& document, + const CSMWorld::UniversalId& id, QWidget *parent) : QWidget (parent), mShowStatusBar (false), mEditMode(EditMode_None), mHasPosition(false), mRow(0), mColumn(0) { @@ -249,6 +249,11 @@ void CSVWorld::TableBottomBox::cloneRequest(const std::string& id, mCreator->focus(); } +void CSVWorld::TableBottomBox::touchRequest(const std::vector& ids) +{ + mCreator->touch(ids); +} + void CSVWorld::TableBottomBox::extendedDeleteConfigRequest(const std::vector &selectedIds) { extendedConfigRequest(ExtendedCommandConfigurator::Mode_Delete, selectedIds); diff --git a/apps/opencs/view/world/tablebottombox.hpp b/apps/opencs/view/world/tablebottombox.hpp index 781cccc9e..5402c466e 100644 --- a/apps/opencs/view/world/tablebottombox.hpp +++ b/apps/opencs/view/world/tablebottombox.hpp @@ -102,6 +102,7 @@ namespace CSVWorld void createRequest(); void cloneRequest(const std::string& id, const CSMWorld::UniversalId::Type type); + void touchRequest(const std::vector&); void extendedDeleteConfigRequest(const std::vector &selectedIds); void extendedRevertConfigRequest(const std::vector &selectedIds); diff --git a/apps/opencs/view/world/tablesubview.cpp b/apps/opencs/view/world/tablesubview.cpp index 6664a3771..12e29995d 100644 --- a/apps/opencs/view/world/tablesubview.cpp +++ b/apps/opencs/view/world/tablesubview.cpp @@ -69,6 +69,9 @@ CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::D connect (this, SIGNAL(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type)), mBottom, SLOT(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type))); + connect (mTable, SIGNAL(touchRequest(const std::vector&)), + mBottom, SLOT(touchRequest(const std::vector&))); + connect (mTable, SIGNAL(extendedDeleteConfigRequest(const std::vector &)), mBottom, SLOT(extendedDeleteConfigRequest(const std::vector &))); connect (mTable, SIGNAL(extendedRevertConfigRequest(const std::vector &)), diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index e920e614a..0efb269b9 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -42,7 +42,7 @@ add_openmw_dir (mwgui itemmodel containeritemmodel inventoryitemmodel sortfilteritemmodel itemview tradeitemmodel companionitemmodel pickpocketitemmodel controllers savegamedialog recharge mode videowidget backgroundimage itemwidget screenfader debugwindow spellmodel spellview - draganddrop timeadvancer jailscreen itemchargeview + draganddrop timeadvancer jailscreen itemchargeview keyboardnavigation ) add_openmw_dir (mwdialogue diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 5072cacda..799b531b1 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -231,11 +231,6 @@ void OMW::Engine::frame(float frametime) // update GUI mEnvironment.getWindowManager()->onFrame(frametime); - if (mEnvironment.getStateManager()->getState()!= - MWBase::StateManager::State_NoGame) - { - mEnvironment.getWindowManager()->update(); - } unsigned int frameNumber = mViewer->getFrameStamp()->getFrameNumber(); osg::Stats* stats = mViewer->getViewerStats(); diff --git a/apps/openmw/mwbase/dialoguemanager.hpp b/apps/openmw/mwbase/dialoguemanager.hpp index 2aedb48fc..386f73382 100644 --- a/apps/openmw/mwbase/dialoguemanager.hpp +++ b/apps/openmw/mwbase/dialoguemanager.hpp @@ -2,6 +2,8 @@ #define GAME_MWBASE_DIALOGUEMANAGER_H #include +#include +#include #include @@ -42,7 +44,8 @@ namespace MWBase virtual bool isInChoice() const = 0; - virtual void startDialogue (const MWWorld::Ptr& actor) = 0; + typedef std::pair Response; // title, text + virtual bool startDialogue (const MWWorld::Ptr& actor, Response& response) = 0; virtual void addTopic (const std::string& topic) = 0; @@ -57,20 +60,24 @@ namespace MWBase End of tes3mp addition */ - virtual void askQuestion (const std::string& question,int choice) = 0; + virtual void addChoice (const std::string& text,int choice) = 0; + virtual const std::vector >& getChoices() = 0; + + virtual bool isGoodbye() = 0; virtual void goodbye() = 0; virtual void say(const MWWorld::Ptr &actor, const std::string &topic) = 0; - //calbacks for the GUI - virtual void keywordSelected (const std::string& keyword) = 0; + virtual Response keywordSelected (const std::string& keyword) = 0; virtual void goodbyeSelected() = 0; - virtual void questionAnswered (int answer) = 0; + virtual Response questionAnswered (int answer) = 0; + + virtual std::list getAvailableTopics() = 0; - virtual bool checkServiceRefused () = 0; + virtual bool checkServiceRefused (Response& response) = 0; - virtual void persuade (int type) = 0; + virtual Response persuade (int type) = 0; virtual int getTemporaryDispositionChange () const = 0; /// @note This change is temporary and gets discarded when dialogue ends. diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index 3a0bf3d03..1de6fdaaf 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -248,7 +248,7 @@ 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::ConstPtr& item, MWWorld::Ptr& victim) = 0; + virtual bool isAllowedToUse (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target, MWWorld::Ptr& victim) = 0; /// Turn actor into werewolf or normal form. virtual void setWerewolf(const MWWorld::Ptr& actor, bool werewolf) = 0; diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index 310084c74..24bf910d3 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -7,6 +7,8 @@ #include #include +#include + #include "../mwgui/mode.hpp" namespace Loading @@ -100,23 +102,17 @@ namespace MWBase virtual ~WindowManager() {} - /** - * Should be called each frame to update windows/gui elements. - * This could mean updating sizes of gui elements or opening - * new dialogs. - */ - virtual void update() = 0; - /// @note This method will block until the video finishes playing /// (and will continually update the window while doing so) virtual void playVideo(const std::string& name, bool allowSkipping) = 0; virtual void setNewGame(bool newgame) = 0; + virtual void pushGuiMode (MWGui::GuiMode mode, const MWWorld::Ptr& arg) = 0; virtual void pushGuiMode (MWGui::GuiMode mode) = 0; - virtual void popGuiMode() = 0; + virtual void popGuiMode(bool noSound=false) = 0; - virtual void removeGuiMode (MWGui::GuiMode mode) = 0; + virtual void removeGuiMode (MWGui::GuiMode mode, bool noSound=false) = 0; ///< can be anywhere in the stack virtual void goToJail(int days) = 0; @@ -144,7 +140,6 @@ namespace MWBase virtual bool isAllowed (MWGui::GuiWindow wnd) const = 0; /// \todo investigate, if we really need to expose every single lousy UI element to the outside world - virtual MWGui::DialogueWindow* getDialogueWindow() = 0; virtual MWGui::InventoryWindow* getInventoryWindow() = 0; virtual MWGui::CountDialog* getCountDialog() = 0; virtual MWGui::ConfirmationDialog* getConfirmationDialog() = 0; @@ -195,6 +190,7 @@ namespace MWBase virtual void setFocusObjectScreenCoords(float min_x, float min_y, float max_x, float max_y) = 0; virtual void setCursorVisible(bool visible) = 0; + virtual void setCursorActive(bool active) = 0; virtual void getMousePosition(int &x, int &y) = 0; virtual void getMousePosition(float &x, float &y) = 0; virtual void setDragDrop(bool dragDrop) = 0; @@ -238,7 +234,7 @@ namespace MWBase virtual void showCrosshair(bool show) = 0; virtual bool getSubtitlesEnabled() = 0; - virtual bool toggleGui() = 0; + virtual bool toggleHud() = 0; virtual void disallowMouse() = 0; virtual void allowMouse() = 0; @@ -302,21 +298,6 @@ namespace MWBase virtual bool getPlayerSleeping() = 0; virtual void wakeUpPlayer() = 0; - virtual void showCompanionWindow(MWWorld::Ptr actor) = 0; - virtual void startSpellMaking(MWWorld::Ptr actor) = 0; - virtual void startEnchanting(MWWorld::Ptr actor) = 0; - virtual void startRecharge(MWWorld::Ptr soulgem) = 0; - virtual void startSelfEnchanting(MWWorld::Ptr soulgem) = 0; - virtual void startTraining(MWWorld::Ptr actor) = 0; - virtual void startRepair(MWWorld::Ptr actor) = 0; - virtual void startRepairItem(MWWorld::Ptr item) = 0; - virtual void startTravel(const MWWorld::Ptr& actor) = 0; - virtual void startSpellBuying(const MWWorld::Ptr& actor) = 0; - virtual void startTrade(const MWWorld::Ptr& actor) = 0; - virtual void openContainer(const MWWorld::Ptr& container, bool loot) = 0; - virtual void showBook(const MWWorld::Ptr& item, bool showTakeButton) = 0; - virtual void showScroll(const MWWorld::Ptr& item, bool showTakeButton) = 0; - virtual void showSoulgemDialog (MWWorld::Ptr item) = 0; virtual void changePointer (const std::string& name) = 0; @@ -358,11 +339,11 @@ namespace MWBase virtual void pinWindow (MWGui::GuiWindow window) = 0; /// Fade the screen in, over \a time seconds - virtual void fadeScreenIn(const float time, bool clearQueue=true) = 0; + virtual void fadeScreenIn(const float time, bool clearQueue=true, float delay=0.f) = 0; /// Fade the screen out to black, over \a time seconds - virtual void fadeScreenOut(const float time, bool clearQueue=true) = 0; + virtual void fadeScreenOut(const float time, bool clearQueue=true, float delay=0.f) = 0; /// Fade the screen to a specified percentage of black, over \a time seconds - virtual void fadeScreenTo(const int percent, const float time, bool clearQueue=true) = 0; + virtual void fadeScreenTo(const int percent, const float time, bool clearQueue=true, float delay=0.f) = 0; /// Darken the screen to a specified percentage virtual void setBlindness(const int percent) = 0; @@ -388,6 +369,8 @@ namespace MWBase virtual void writeFog(MWWorld::CellStore* cell) = 0; virtual const MWGui::TextColours& getTextColours() = 0; + + virtual bool injectKeyPress(MyGUI::KeyCode key, unsigned int text) = 0; }; } diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 5edf9ff0f..6d198b261 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -567,11 +567,11 @@ namespace MWClass // by default user can loot friendly actors during death animation if (canLoot && !stats.getAiSequence().isInCombat()) - return std::shared_ptr(new MWWorld::ActionOpen(ptr, true)); + return std::shared_ptr(new MWWorld::ActionOpen(ptr)); // otherwise wait until death animation if(stats.isDeathAnimationFinished()) - return std::shared_ptr(new MWWorld::ActionOpen(ptr, true)); + return std::shared_ptr(new MWWorld::ActionOpen(ptr)); // death animation is not finished, do nothing return std::shared_ptr (new MWWorld::FailedAction("")); @@ -580,6 +580,9 @@ namespace MWClass if(stats.getAiSequence().isInCombat()) return std::shared_ptr(new MWWorld::FailedAction("")); + if(stats.getKnockedDown()) + return std::shared_ptr(new MWWorld::FailedAction("")); + return std::shared_ptr(new MWWorld::ActionTalk(ptr)); } diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 38da4887c..aad8a7ba1 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -1008,11 +1008,11 @@ namespace MWClass // by default user can loot friendly actors during death animation if (canLoot && !stats.getAiSequence().isInCombat()) - return std::shared_ptr(new MWWorld::ActionOpen(ptr, true)); + return std::shared_ptr(new MWWorld::ActionOpen(ptr)); // otherwise wait until death animation if(stats.isDeathAnimationFinished()) - return std::shared_ptr(new MWWorld::ActionOpen(ptr, true)); + return std::shared_ptr(new MWWorld::ActionOpen(ptr)); // death animation is not finished, do nothing return std::shared_ptr (new MWWorld::FailedAction("")); diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index abb6ebd89..7c9fe3e2d 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -46,8 +47,6 @@ #include "../mwworld/containerstore.hpp" #include "../mwworld/esmstore.hpp" -#include "../mwgui/dialogue.hpp" - #include "../mwscript/compilercontext.hpp" #include "../mwscript/interpretercontext.hpp" #include "../mwscript/extensions.hpp" @@ -72,16 +71,8 @@ namespace MWDialogue { mChoice = -1; mIsInChoice = false; + mGoodbye = false; mCompilerContext.setExtensions (&extensions); - - const MWWorld::Store &dialogs = - MWBase::Environment::get().getWorld()->getStore().get(); - - MWWorld::Store::iterator it = dialogs.begin(); - for (; it != dialogs.end(); ++it) - { - mDialogueMap[Misc::StringUtils::lowerCase(it->mId)] = *it; - } } void DialogueManager::clear() @@ -146,17 +137,15 @@ namespace MWDialogue if (mActorKnownTopics.count( topicId )) mKnownTopics.insert( topicId ); } - - updateTopics(); } - void DialogueManager::startDialogue (const MWWorld::Ptr& actor) + bool DialogueManager::startDialogue (const MWWorld::Ptr& actor, Response& response) { updateGlobals(); // Dialogue with dead actor (e.g. through script) should not be allowed. if (actor.getClass().getCreatureStats(actor).isDead()) - return; + return false; mLastTopic = ""; mPermanentDispositionChange = 0; @@ -164,6 +153,8 @@ namespace MWDialogue mChoice = -1; mIsInChoice = false; + mGoodbye = false; + mChoices.clear(); mActor = actor; @@ -172,13 +163,6 @@ namespace MWDialogue mActorKnownTopics.clear(); - MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow(); - - // If the dialogue window was already open, keep the existing history - bool resetHistory = (!MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_Dialogue)); - - win->startDialogue(actor, actor.getClass().getName (actor), resetHistory); - //greeting const MWWorld::Store &dialogs = MWBase::Environment::get().getWorld()->getStore().get(); @@ -192,9 +176,6 @@ namespace MWDialogue // Search a response (we do not accept a fallback to "Info refusal" here) if (const ESM::DialInfo *info = filter.search (*it, false)) { - //initialise the GUI - MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Dialogue); - creatureStats.talkedToPlayer(); if (!info->mSound.empty()) @@ -203,29 +184,23 @@ namespace MWDialogue } // first topics update so that parseText knows the keywords to highlight - updateTopics(); + updateActorKnownTopics(); parseText (info->mResponse); MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor); - win->addResponse (Interpreter::fixDefinesDialog(info->mResponse, interpreterContext), "", false); + response = Response ("", Interpreter::fixDefinesDialog(info->mResponse, interpreterContext)); executeScript (info->mResultScript, mActor); - mLastTopic = Misc::StringUtils::lowerCase(it->mId); + mLastTopic = it->mId; // update topics again to accommodate changes resulting from executeScript - updateTopics(); + updateActorKnownTopics(); - return; + return true; } } } - - // No greetings found. The dialogue window should not be shown. - // If this is a companion, we must show the companion window directly (used by BM_bear_be_unique). - bool isCompanion = !mActor.getClass().getScript(mActor).empty() - && mActor.getRefData().getLocals().getIntVar(mActor.getClass().getScript(mActor), "companion"); - if (isCompanion) - MWBase::Environment::get().getWindowManager()->showCompanionWindow(mActor); + return false; } bool DialogueManager::compile (const std::string& cmd, std::vector& code, const MWWorld::Ptr& actor) @@ -303,8 +278,9 @@ namespace MWDialogue } } - void DialogueManager::executeTopic (const std::string& topic) + DialogueManager::Response DialogueManager::executeTopic (const std::string& topic) { + DialogueManager::Response response; Filter filter (mActor, mChoice, mTalkedTo); const MWWorld::Store &dialogues = @@ -312,8 +288,6 @@ namespace MWDialogue const ESM::Dialogue& dialogue = *dialogues.find (topic); - MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow(); - const ESM::DialInfo* info = filter.search(dialogue, true); if (info) { @@ -338,7 +312,7 @@ namespace MWDialogue title = topic; MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor); - win->addResponse (Interpreter::fixDefinesDialog(info->mResponse, interpreterContext), title); + response = Response(title, Interpreter::fixDefinesDialog(info->mResponse, interpreterContext)); if (dialogue.mType == ESM::Dialogue::Topic) { @@ -349,7 +323,7 @@ namespace MWDialogue { if (iter->mId == info->mId) { - MWBase::Environment::get().getJournal()->addTopic (topic, info->mId, mActor); + MWBase::Environment::get().getJournal()->addTopic (Misc::StringUtils::lowerCase(topic), info->mId, mActor); break; } } @@ -359,11 +333,12 @@ namespace MWDialogue mLastTopic = topic; } - else - { - // no response found, print a fallback text - win->addResponse ("…", topic); - } + return response; + } + + const ESM::Dialogue *DialogueManager::searchDialogue(const std::string& id) + { + return MWBase::Environment::get().getWorld()->getStore().get().search(id); } void DialogueManager::updateGlobals() @@ -371,19 +346,16 @@ namespace MWDialogue MWBase::Environment::get().getWorld()->updateDialogueGlobals(); } - void DialogueManager::updateTopics() + void DialogueManager::updateActorKnownTopics() { updateGlobals(); - std::list keywordList; - int choice = mChoice; - mChoice = -1; mActorKnownTopics.clear(); const MWWorld::Store &dialogs = MWBase::Environment::get().getWorld()->getStore().get(); - Filter filter (mActor, mChoice, mTalkedTo); + Filter filter (mActor, -1, mTalkedTo); for (MWWorld::Store::iterator iter = dialogs.begin(); iter != dialogs.end(); ++iter) { @@ -393,90 +365,42 @@ namespace MWDialogue { std::string lower = Misc::StringUtils::lowerCase(iter->mId); mActorKnownTopics.insert (lower); - - //does the player know the topic? - if (mKnownTopics.count(lower)) - { - keywordList.push_back (iter->mId); - } } } } - // check the available services of this actor - int services = 0; - if (mActor.getTypeName() == typeid(ESM::NPC).name()) - { - MWWorld::LiveCellRef* ref = mActor.get(); - if (ref->mBase->mHasAI) - services = ref->mBase->mAiData.mServices; - } - else if (mActor.getTypeName() == typeid(ESM::Creature).name()) - { - MWWorld::LiveCellRef* ref = mActor.get(); - if (ref->mBase->mHasAI) - services = ref->mBase->mAiData.mServices; - } - - int windowServices = 0; - - if (services & ESM::NPC::Weapon - || services & ESM::NPC::Armor - || services & ESM::NPC::Clothing - || services & ESM::NPC::Books - || services & ESM::NPC::Ingredients - || services & ESM::NPC::Picks - || services & ESM::NPC::Probes - || services & ESM::NPC::Lights - || services & ESM::NPC::Apparatus - || services & ESM::NPC::RepairItem - || services & ESM::NPC::Misc) - windowServices |= MWGui::DialogueWindow::Service_Trade; - - if((mActor.getTypeName() == typeid(ESM::NPC).name() && !mActor.get()->mBase->getTransport().empty()) - || (mActor.getTypeName() == typeid(ESM::Creature).name() && !mActor.get()->mBase->getTransport().empty())) - windowServices |= MWGui::DialogueWindow::Service_Travel; - - if (services & ESM::NPC::Spells) - windowServices |= MWGui::DialogueWindow::Service_BuySpells; - - if (services & ESM::NPC::Spellmaking) - windowServices |= MWGui::DialogueWindow::Service_CreateSpells; - - if (services & ESM::NPC::Training) - windowServices |= MWGui::DialogueWindow::Service_Training; - - if (services & ESM::NPC::Enchanting) - windowServices |= MWGui::DialogueWindow::Service_Enchant; + } - if (services & ESM::NPC::Repair) - windowServices |= MWGui::DialogueWindow::Service_Repair; + std::list DialogueManager::getAvailableTopics() + { + updateActorKnownTopics(); - MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow(); + std::list keywordList; - win->setServices (windowServices); + for (const std::string& topic : mActorKnownTopics) + { + //does the player know the topic? + if (mKnownTopics.count(Misc::StringUtils::lowerCase(topic))) + keywordList.push_back(topic); + } // sort again, because the previous sort was case-sensitive keywordList.sort(Misc::StringUtils::ciLess); - win->setKeywords(keywordList); - - mChoice = choice; + return keywordList; } - void DialogueManager::keywordSelected (const std::string& keyword) + DialogueManager::Response DialogueManager::keywordSelected (const std::string& keyword) { + Response response; if(!mIsInChoice) { - if(mDialogueMap.find(keyword) != mDialogueMap.end()) + const ESM::Dialogue* dialogue = searchDialogue(keyword); + if (dialogue && dialogue->mType == ESM::Dialogue::Topic) { - if (mDialogueMap[keyword].mType == ESM::Dialogue::Topic) - { - executeTopic (keyword); - } + response = executeTopic (keyword); } } - - updateTopics(); + return response; } bool DialogueManager::isInChoice() const @@ -486,8 +410,6 @@ namespace MWDialogue void DialogueManager::goodbyeSelected() { - MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Dialogue); - // Apply disposition change to NPC's base disposition if (mActor.getClass().isNpc()) { @@ -503,37 +425,38 @@ namespace MWDialogue mTemporaryDispositionChange = 0; } - void DialogueManager::questionAnswered (int answer) + DialogueManager::Response DialogueManager::questionAnswered (int answer) { mChoice = answer; + DialogueManager::Response response; - if (mDialogueMap.find(mLastTopic) != mDialogueMap.end()) + const ESM::Dialogue* dialogue = searchDialogue(mLastTopic); + if (dialogue) { Filter filter (mActor, mChoice, mTalkedTo); - if (mDialogueMap[mLastTopic].mType == ESM::Dialogue::Topic - || mDialogueMap[mLastTopic].mType == ESM::Dialogue::Greeting) + if (dialogue->mType == ESM::Dialogue::Topic || dialogue->mType == ESM::Dialogue::Greeting) { - if (const ESM::DialInfo *info = filter.search (mDialogueMap[mLastTopic], true)) + if (const ESM::DialInfo *info = filter.search (*dialogue, true)) { std::string text = info->mResponse; parseText (text); mChoice = -1; mIsInChoice = false; - MWBase::Environment::get().getWindowManager()->getDialogueWindow()->clearChoices(); + mChoices.clear(); MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor); - MWBase::Environment::get().getWindowManager()->getDialogueWindow()->addResponse (Interpreter::fixDefinesDialog(text, interpreterContext)); + response = Response("", Interpreter::fixDefinesDialog(text, interpreterContext)); // Make sure the returned DialInfo is from the Dialogue we supplied. If could also be from the Info refusal group, // in which case it should not be added to the journal. - for (ESM::Dialogue::InfoContainer::const_iterator iter = mDialogueMap[mLastTopic].mInfo.begin(); - iter!=mDialogueMap[mLastTopic].mInfo.end(); ++iter) + for (ESM::Dialogue::InfoContainer::const_iterator iter = dialogue->mInfo.begin(); + iter!=dialogue->mInfo.end(); ++iter) { if (iter->mId == info->mId) { - MWBase::Environment::get().getJournal()->addTopic (mLastTopic, info->mId, mActor); + MWBase::Environment::get().getJournal()->addTopic (Misc::StringUtils::lowerCase(mLastTopic), info->mId, mActor); break; } } @@ -544,32 +467,39 @@ namespace MWDialogue { mChoice = -1; mIsInChoice = false; - MWBase::Environment::get().getWindowManager()->getDialogueWindow()->clearChoices(); + mChoices.clear(); } } } - updateTopics(); + updateActorKnownTopics(); + return response; } - void DialogueManager::askQuestion (const std::string& question, int choice) + void DialogueManager::addChoice (const std::string& text, int choice) { mIsInChoice = true; - MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow(); - win->addChoice(question, choice); + mChoices.push_back(std::make_pair(text, choice)); } - void DialogueManager::goodbye() + const std::vector >& DialogueManager::getChoices() { - mIsInChoice = true; + return mChoices; + } - MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow(); + bool DialogueManager::isGoodbye() + { + return mGoodbye; + } - win->goodbye(); + void DialogueManager::goodbye() + { + mIsInChoice = false; + mGoodbye = true; } - void DialogueManager::persuade(int type) + DialogueManager::Response DialogueManager::persuade(int type) { bool success; float temp, perm; @@ -618,7 +548,7 @@ namespace MWDialogue text = "Bribe"; } - executeTopic (text + (success ? " Success" : " Fail")); + return executeTopic (text + (success ? " Success" : " Fail")); } int DialogueManager::getTemporaryDispositionChange() const @@ -631,7 +561,7 @@ namespace MWDialogue mTemporaryDispositionChange += delta; } - bool DialogueManager::checkServiceRefused() + bool DialogueManager::checkServiceRefused(Response& response) { Filter filter (mActor, mChoice, mTalkedTo); @@ -639,7 +569,6 @@ namespace MWDialogue MWBase::Environment::get().getWorld()->getStore().get(); const ESM::Dialogue& dialogue = *dialogues.find ("Service Refusal"); - MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow(); std::vector infos = filter.list (dialogue, false, false, true); if (!infos.empty()) @@ -653,8 +582,7 @@ namespace MWDialogue MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor); - win->addResponse (Interpreter::fixDefinesDialog(info->mResponse, interpreterContext), - gmsts.find ("sServiceRefusal")->getString()); + response = Response(gmsts.find ("sServiceRefusal")->getString(), Interpreter::fixDefinesDialog(info->mResponse, interpreterContext)); executeScript (info->mResultScript, mActor); return true; @@ -677,6 +605,12 @@ namespace MWDialogue return; } + if (actor.getClass().getCreatureStats(actor).getKnockedDown()) + { + // Unconscious actors can not speak + return; + } + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const ESM::Dialogue *dial = store.get().find(topic); @@ -813,7 +747,7 @@ namespace MWDialogue if (actor == mActor && !mLastTopic.empty()) { MWBase::Environment::get().getJournal()->removeLastAddedTopicResponse( - mLastTopic, actor.getClass().getName(actor)); + Misc::StringUtils::lowerCase(mLastTopic), actor.getClass().getName(actor)); } } } diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp index 00dbf5c05..48758674e 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp @@ -22,7 +22,6 @@ namespace MWDialogue { class DialogueManager : public MWBase::DialogueManager { - std::map mDialogueMap; std::set mKnownTopics;// Those are the topics the player knows. // Modified faction reactions. > @@ -42,19 +41,24 @@ namespace MWDialogue int mChoice; std::string mLastTopic; // last topic ID, lowercase bool mIsInChoice; + bool mGoodbye; + + std::vector > mChoices; float mTemporaryDispositionChange; float mPermanentDispositionChange; void parseText (const std::string& text); - void updateTopics(); + void updateActorKnownTopics(); void updateGlobals(); bool compile (const std::string& cmd, std::vector& code, const MWWorld::Ptr& actor); void executeScript (const std::string& script, const MWWorld::Ptr& actor); - void executeTopic (const std::string& topic); + Response executeTopic (const std::string& topic); + + const ESM::Dialogue* searchDialogue(const std::string& id); public: @@ -64,7 +68,9 @@ namespace MWDialogue virtual bool isInChoice() const; - virtual void startDialogue (const MWWorld::Ptr& actor); + virtual bool startDialogue (const MWWorld::Ptr& actor, Response& response); + + std::list getAvailableTopics(); virtual void addTopic (const std::string& topic); @@ -79,20 +85,23 @@ namespace MWDialogue End of tes3mp addition */ - virtual void askQuestion (const std::string& question,int choice); + virtual void addChoice (const std::string& text,int choice); + const std::vector >& getChoices(); + + virtual bool isGoodbye(); virtual void goodbye(); - virtual bool checkServiceRefused (); + virtual bool checkServiceRefused (Response& response); virtual void say(const MWWorld::Ptr &actor, const std::string &topic); //calbacks for the GUI - virtual void keywordSelected (const std::string& keyword); + virtual Response keywordSelected (const std::string& keyword); virtual void goodbyeSelected(); - virtual void questionAnswered (int answer); + virtual Response questionAnswered (int answer); - virtual void persuade (int type); + virtual Response persuade (int type); virtual int getTemporaryDispositionChange () const; /// @note This change is temporary and gets discarded when dialogue ends. diff --git a/apps/openmw/mwdialogue/filter.cpp b/apps/openmw/mwdialogue/filter.cpp index 67bb53e71..e8757e4a3 100644 --- a/apps/openmw/mwdialogue/filter.cpp +++ b/apps/openmw/mwdialogue/filter.cpp @@ -193,7 +193,8 @@ bool MWDialogue::Filter::testFunctionLocal(const MWDialogue::SelectWrapper& sele return false; // shouldn't happen, we checked that variable has a type above, so must exist const MWScript::Locals& locals = mActor.getRefData().getLocals(); - + if (locals.isEmpty()) + return select.selectCompare(0); switch (type) { case 's': return select.selectCompare (static_cast (locals.mShorts[index])); diff --git a/apps/openmw/mwgui/alchemywindow.cpp b/apps/openmw/mwgui/alchemywindow.cpp index 642dfe613..500115f83 100644 --- a/apps/openmw/mwgui/alchemywindow.cpp +++ b/apps/openmw/mwgui/alchemywindow.cpp @@ -1,6 +1,8 @@ #include "alchemywindow.hpp" #include +#include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -19,6 +21,7 @@ #include "sortfilteritemmodel.hpp" #include "itemview.hpp" #include "itemwidget.hpp" +#include "widgets.hpp" namespace MWGui { @@ -54,12 +57,19 @@ namespace MWGui mCreateButton->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::onCreateButtonClicked); mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::onCancelButtonClicked); + mNameEdit->eventEditSelectAccept += MyGUI::newDelegate(this, &AlchemyWindow::onAccept); + center(); } + void AlchemyWindow::onAccept(MyGUI::EditBox* sender) + { + onCreateButtonClicked(sender); + } + void AlchemyWindow::onCancelButtonClicked(MyGUI::Widget* _sender) { - exit(); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Alchemy); } void AlchemyWindow::onCreateButtonClicked(MyGUI::Widget* _sender) @@ -112,8 +122,9 @@ namespace MWGui update(); } - void AlchemyWindow::open() + void AlchemyWindow::onOpen() { + mAlchemy->clear(); mAlchemy->setAlchemist (MWMechanics::getPlayer()); InventoryItemModel* model = new InventoryItemModel(MWMechanics::getPlayer()); @@ -138,12 +149,8 @@ namespace MWGui } update(); - } - void AlchemyWindow::exit() { - mAlchemy->clear(); - MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Alchemy); - MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Inventory); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mNameEdit); } void AlchemyWindow::onIngredientSelected(MyGUI::Widget* _sender) diff --git a/apps/openmw/mwgui/alchemywindow.hpp b/apps/openmw/mwgui/alchemywindow.hpp index e1f48d4a3..d1e54241a 100644 --- a/apps/openmw/mwgui/alchemywindow.hpp +++ b/apps/openmw/mwgui/alchemywindow.hpp @@ -5,7 +5,6 @@ #include "../mwmechanics/alchemy.hpp" -#include "widgets.hpp" #include "windowbase.hpp" namespace MWMechanics @@ -24,8 +23,9 @@ namespace MWGui public: AlchemyWindow(); - virtual void open(); - virtual void exit(); + virtual void onOpen(); + + void onResChange(int, int) { center(); } private: std::string mSuggestedPotionName; @@ -43,6 +43,7 @@ namespace MWGui void onCancelButtonClicked(MyGUI::Widget* _sender); void onCreateButtonClicked(MyGUI::Widget* _sender); void onIngredientSelected(MyGUI::Widget* _sender); + void onAccept(MyGUI::EditBox*); void onSelectedItem(int index); diff --git a/apps/openmw/mwgui/birth.cpp b/apps/openmw/mwgui/birth.cpp index ecc011fc1..92f29e3ef 100644 --- a/apps/openmw/mwgui/birth.cpp +++ b/apps/openmw/mwgui/birth.cpp @@ -64,11 +64,12 @@ namespace MWGui okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", "")); } - void BirthDialog::open() + void BirthDialog::onOpen() { - WindowModal::open(); + WindowModal::onOpen(); updateBirths(); updateSpells(); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mBirthList); } diff --git a/apps/openmw/mwgui/birth.hpp b/apps/openmw/mwgui/birth.hpp index c8ec9da09..86af14286 100644 --- a/apps/openmw/mwgui/birth.hpp +++ b/apps/openmw/mwgui/birth.hpp @@ -20,7 +20,9 @@ namespace MWGui void setBirthId(const std::string &raceId); void setNextButtonShow(bool shown); - virtual void open(); + virtual void onOpen(); + + bool exit() { return false; } // Events typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void; diff --git a/apps/openmw/mwgui/bookwindow.cpp b/apps/openmw/mwgui/bookwindow.cpp index b3296e7a7..c18548dad 100644 --- a/apps/openmw/mwgui/bookwindow.cpp +++ b/apps/openmw/mwgui/bookwindow.cpp @@ -1,6 +1,7 @@ #include "bookwindow.hpp" #include +#include #include @@ -11,6 +12,7 @@ #include "../mwmechanics/actorutil.hpp" #include "../mwworld/actiontake.hpp" +#include "../mwworld/class.hpp" #include "formatting.hpp" @@ -51,6 +53,11 @@ namespace MWGui mRightPage->setNeedMouseFocus(true); mRightPage->eventMouseWheel += MyGUI::newDelegate(this, &BookWindow::onMouseWheel); + mNextPageButton->eventKeyButtonPressed += MyGUI::newDelegate(this, &BookWindow::onKeyButtonPressed); + mPrevPageButton->eventKeyButtonPressed += MyGUI::newDelegate(this, &BookWindow::onKeyButtonPressed); + mTakeButton->eventKeyButtonPressed += MyGUI::newDelegate(this, &BookWindow::onKeyButtonPressed); + mCloseButton->eventKeyButtonPressed += MyGUI::newDelegate(this, &BookWindow::onKeyButtonPressed); + if (mNextPageButton->getSize().width == 64) { // english button has a 7 pixel wide strip of garbage on its right edge @@ -74,15 +81,16 @@ namespace MWGui mPages.clear(); } - void BookWindow::openBook (MWWorld::Ptr book, bool showTakeButton) + void BookWindow::setPtr (const MWWorld::Ptr& book) { mBook = book; + MWWorld::Ptr player = MWMechanics::getPlayer(); + bool showTakeButton = book.getContainerStore() != &player.getClass().getContainerStore(player); + clearPages(); mCurrentPage = 0; - MWBase::Environment::get().getWindowManager()->playSound("book open"); - MWWorld::LiveCellRef *ref = mBook.get(); Formatting::BookFormatter formatter; @@ -92,14 +100,8 @@ namespace MWGui updatePages(); setTakeButtonShow(showTakeButton); - } - void BookWindow::exit() - { - // no 3d sounds because the object could be in a container. - MWBase::Environment::get().getWindowManager()->playSound("book close"); - - MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Book); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCloseButton); } void BookWindow::setTakeButtonShow(bool show) @@ -108,6 +110,14 @@ namespace MWGui mTakeButton->setVisible(mTakeButtonShow && mTakeButtonAllowed); } + void BookWindow::onKeyButtonPressed(MyGUI::Widget *sender, MyGUI::KeyCode key, MyGUI::Char character) + { + if (key == MyGUI::KeyCode::ArrowUp) + prevPage(); + else if (key == MyGUI::KeyCode::ArrowDown) + nextPage(); + } + void BookWindow::setInventoryAllowed(bool allowed) { mTakeButtonAllowed = allowed; @@ -116,7 +126,7 @@ namespace MWGui void BookWindow::onCloseButtonClicked (MyGUI::Widget* sender) { - exit(); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Book); } void BookWindow::onTakeButtonClicked (MyGUI::Widget* sender) @@ -144,20 +154,16 @@ namespace MWGui mLeftPageNumber->setCaption( MyGUI::utility::toString(mCurrentPage*2 + 1) ); mRightPageNumber->setCaption( MyGUI::utility::toString(mCurrentPage*2 + 2) ); - //If it is the last page, hide the button "Next Page" - if ( (mCurrentPage+1)*2 == mPages.size() - || (mCurrentPage+1)*2 == mPages.size() + 1) - { - mNextPageButton->setVisible(false); - } else { - mNextPageButton->setVisible(true); - } - //If it is the fist page, hide the button "Prev Page" - if (mCurrentPage == 0) { - mPrevPageButton->setVisible(false); - } else { - mPrevPageButton->setVisible(true); - } + MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getKeyFocusWidget(); + bool nextPageVisible = (mCurrentPage+1)*2 < mPages.size(); + mNextPageButton->setVisible(nextPageVisible); + bool prevPageVisible = mCurrentPage != 0; + mPrevPageButton->setVisible(prevPageVisible); + + if (focus == mNextPageButton && !nextPageVisible && prevPageVisible) + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mPrevPageButton); + else if (focus == mPrevPageButton && !prevPageVisible && nextPageVisible) + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mNextPageButton); if (mPages.empty()) return; diff --git a/apps/openmw/mwgui/bookwindow.hpp b/apps/openmw/mwgui/bookwindow.hpp index 881b9997c..2459c3464 100644 --- a/apps/openmw/mwgui/bookwindow.hpp +++ b/apps/openmw/mwgui/bookwindow.hpp @@ -14,11 +14,11 @@ namespace MWGui public: BookWindow(); - virtual void exit(); - - void openBook(MWWorld::Ptr book, bool showTakeButton); + void setPtr(const MWWorld::Ptr& book); void setInventoryAllowed(bool allowed); + void onResChange(int, int) { center(); } + protected: void onNextPageButtonClicked (MyGUI::Widget* sender); void onPrevPageButtonClicked (MyGUI::Widget* sender); @@ -27,6 +27,8 @@ namespace MWGui void onMouseWheel(MyGUI::Widget* _sender, int _rel); void setTakeButtonShow(bool show); + void onKeyButtonPressed(MyGUI::Widget* sender, MyGUI::KeyCode key, MyGUI::Char character); + void nextPage(); void prevPage(); diff --git a/apps/openmw/mwgui/class.cpp b/apps/openmw/mwgui/class.cpp index e1f5a165e..33daa0ad1 100644 --- a/apps/openmw/mwgui/class.cpp +++ b/apps/openmw/mwgui/class.cpp @@ -125,11 +125,12 @@ namespace MWGui okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", "")); } - void PickClassDialog::open() + void PickClassDialog::onOpen() { - WindowModal::open (); + WindowModal::onOpen (); updateClasses(); updateStats(); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mClassList); } @@ -341,9 +342,9 @@ namespace MWGui } } - void InfoBoxDialog::open() + void InfoBoxDialog::onOpen() { - WindowModal::open(); + WindowModal::onOpen(); // Fix layout layoutVertically(mTextBox, 4); layoutVertically(mButtonBar, 6); @@ -730,9 +731,10 @@ namespace MWGui exit(); } - void SelectSpecializationDialog::exit() + bool SelectSpecializationDialog::exit() { eventCancel(); + return true; } /* SelectAttributeDialog */ @@ -778,9 +780,10 @@ namespace MWGui exit(); } - void SelectAttributeDialog::exit() + bool SelectAttributeDialog::exit() { eventCancel(); + return true; } @@ -869,9 +872,10 @@ namespace MWGui exit(); } - void SelectSkillDialog::exit() + bool SelectSkillDialog::exit() { eventCancel(); + return true; } /* DescriptionDialog */ diff --git a/apps/openmw/mwgui/class.hpp b/apps/openmw/mwgui/class.hpp index 8206d6b03..3a2573fd4 100644 --- a/apps/openmw/mwgui/class.hpp +++ b/apps/openmw/mwgui/class.hpp @@ -21,7 +21,9 @@ namespace MWGui std::string getText() const; void setButtons(ButtonList &buttons); - virtual void open(); + virtual void onOpen(); + + bool exit() { return false; } // Events typedef MyGUI::delegates::CMultiDelegate1 EventHandle_Int; @@ -67,6 +69,8 @@ namespace MWGui std::string getClassId() const; void setClassId(const std::string &classId); + bool exit() { return false; } + // Events typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void; @@ -100,7 +104,9 @@ namespace MWGui void setClassId(const std::string &classId); void setNextButtonShow(bool shown); - virtual void open(); + virtual void onOpen(); + + bool exit() { return false; } // Events typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void; @@ -142,7 +148,7 @@ namespace MWGui SelectSpecializationDialog(); ~SelectSpecializationDialog(); - virtual void exit(); + virtual bool exit(); ESM::Class::Specialization getSpecializationId() const { return mSpecializationId; } @@ -175,7 +181,7 @@ namespace MWGui SelectAttributeDialog(); ~SelectAttributeDialog(); - virtual void exit(); + virtual bool exit(); ESM::Attribute::AttributeID getAttributeId() const { return mAttributeId; } @@ -206,7 +212,7 @@ namespace MWGui SelectSkillDialog(); ~SelectSkillDialog(); - virtual void exit(); + virtual bool exit(); ESM::Skill::SkillEnum getSkillId() const { return mSkillId; } @@ -262,6 +268,8 @@ namespace MWGui CreateClassDialog(); virtual ~CreateClassDialog(); + bool exit() { return false; } + std::string getName() const; std::string getDescription() const; ESM::Class::Specialization getSpecializationId() const; diff --git a/apps/openmw/mwgui/companionwindow.cpp b/apps/openmw/mwgui/companionwindow.cpp index fc4a98489..ad71be7df 100644 --- a/apps/openmw/mwgui/companionwindow.cpp +++ b/apps/openmw/mwgui/companionwindow.cpp @@ -13,6 +13,7 @@ #include "companionitemmodel.hpp" #include "draganddrop.hpp" #include "countdialog.hpp" +#include "widgets.hpp" namespace { @@ -103,7 +104,7 @@ void CompanionWindow::onBackgroundSelected() } } -void CompanionWindow::openCompanion(const MWWorld::Ptr& npc) +void CompanionWindow::setPtr(const MWWorld::Ptr& npc) { mPtr = npc; updateEncumbranceBar(); @@ -116,8 +117,9 @@ void CompanionWindow::openCompanion(const MWWorld::Ptr& npc) setTitle(npc.getClass().getName(npc)); } -void CompanionWindow::onFrame() +void CompanionWindow::onFrame(float dt) { + checkReferenceAvailable(); updateEncumbranceBar(); } @@ -139,10 +141,11 @@ void CompanionWindow::updateEncumbranceBar() void CompanionWindow::onCloseButtonClicked(MyGUI::Widget* _sender) { - exit(); + if (exit()) + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Companion); } -void CompanionWindow::exit() +bool CompanionWindow::exit() { if (mModel && mModel->hasProfit(mPtr) && getProfit(mPtr) < 0) { @@ -151,9 +154,9 @@ void CompanionWindow::exit() buttons.push_back("#{sCompanionWarningButtonTwo}"); mMessageBoxManager->createInteractiveMessageBox("#{sCompanionWarningMessage}", buttons); mMessageBoxManager->eventButtonPressed += MyGUI::newDelegate(this, &CompanionWindow::onMessageBoxButtonClicked); + return false; } - else - MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Companion); + return true; } void CompanionWindow::onMessageBoxButtonClicked(int button) @@ -162,7 +165,7 @@ void CompanionWindow::onMessageBoxButtonClicked(int button) { MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Companion); // Important for Calvus' contract script to work properly - MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Dialogue); + MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode(); } } diff --git a/apps/openmw/mwgui/companionwindow.hpp b/apps/openmw/mwgui/companionwindow.hpp index d37f0c4e4..8ca350617 100644 --- a/apps/openmw/mwgui/companionwindow.hpp +++ b/apps/openmw/mwgui/companionwindow.hpp @@ -1,12 +1,16 @@ #ifndef OPENMW_MWGUI_COMPANIONWINDOW_H #define OPENMW_MWGUI_COMPANIONWINDOW_H -#include "widgets.hpp" #include "windowbase.hpp" #include "referenceinterface.hpp" namespace MWGui { + namespace Widgets + { + class MWDynamicStat; + } + class MessageBoxManager; class ItemView; class DragAndDrop; @@ -18,12 +22,13 @@ namespace MWGui public: CompanionWindow(DragAndDrop* dragAndDrop, MessageBoxManager* manager); - virtual void exit(); + virtual bool exit(); virtual void resetReference(); - void openCompanion(const MWWorld::Ptr& npc); - void onFrame (); + void setPtr(const MWWorld::Ptr& npc); + void onFrame (float dt); + void clear() { resetReference(); } private: ItemView* mItemView; diff --git a/apps/openmw/mwgui/confirmationdialog.cpp b/apps/openmw/mwgui/confirmationdialog.cpp index 33f8dbe3e..65b079d85 100644 --- a/apps/openmw/mwgui/confirmationdialog.cpp +++ b/apps/openmw/mwgui/confirmationdialog.cpp @@ -3,6 +3,9 @@ #include #include +#include "../mwbase/environment.hpp" +#include "../mwbase/windowmanager.hpp" + namespace MWGui { ConfirmationDialog::ConfirmationDialog() : @@ -16,14 +19,6 @@ namespace MWGui mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ConfirmationDialog::onOkButtonClicked); } - void ConfirmationDialog::askForConfirmation(const std::string& message, const std::string& confirmMessage, const std::string& cancelMessage) - { - mCancelButton->setCaptionWithReplacing(cancelMessage); - mOkButton->setCaptionWithReplacing(confirmMessage); - - askForConfirmation(message); - } - void ConfirmationDialog::askForConfirmation(const std::string& message) { setVisible(true); @@ -38,18 +33,21 @@ namespace MWGui mMessage->setSize(mMessage->getWidth(), mMessage->getTextSize().height + 24); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mOkButton); + center(); } - void ConfirmationDialog::exit() + bool ConfirmationDialog::exit() { - setVisible(false); - eventCancelClicked(); + return true; } void ConfirmationDialog::onCancelButtonClicked(MyGUI::Widget* _sender) { + setVisible(false); + exit(); } diff --git a/apps/openmw/mwgui/confirmationdialog.hpp b/apps/openmw/mwgui/confirmationdialog.hpp index 745c7a1a5..ab52549ec 100644 --- a/apps/openmw/mwgui/confirmationdialog.hpp +++ b/apps/openmw/mwgui/confirmationdialog.hpp @@ -10,8 +10,7 @@ namespace MWGui public: ConfirmationDialog(); void askForConfirmation(const std::string& message); - void askForConfirmation(const std::string& message, const std::string& confirmMessage, const std::string& cancelMessage); - virtual void exit(); + virtual bool exit(); typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void; diff --git a/apps/openmw/mwgui/console.cpp b/apps/openmw/mwgui/console.cpp index aeff7dc39..e8ac33f6d 100644 --- a/apps/openmw/mwgui/console.cpp +++ b/apps/openmw/mwgui/console.cpp @@ -145,25 +145,13 @@ namespace MWGui mCompilerContext.setExtensions (&mExtensions); } - void Console::open() + void Console::onOpen() { // Give keyboard focus to the combo box whenever the console is // turned on MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCommandLine); } - void Console::close() - { - // Apparently, hidden widgets can retain key focus - // Remove for MyGUI 3.2.2 - MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(NULL); - } - - void Console::exit() - { - MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Console); - } - void Console::setFont(const std::string &fntName) { mHistory->setFontName(fntName); @@ -227,6 +215,11 @@ namespace MWGui } } + void Console::clear() + { + resetReference(); + } + void Console::keyPress(MyGUI::Widget* _sender, MyGUI::KeyCode key, MyGUI::Char _char) diff --git a/apps/openmw/mwgui/console.hpp b/apps/openmw/mwgui/console.hpp index 98e46a559..bbff34c8d 100644 --- a/apps/openmw/mwgui/console.hpp +++ b/apps/openmw/mwgui/console.hpp @@ -39,10 +39,7 @@ namespace MWGui Console(int w, int h, bool consoleOnlyScripts); - virtual void open(); - virtual void close(); - - virtual void exit(); + virtual void onOpen(); void setFont(const std::string &fntName); @@ -63,6 +60,8 @@ namespace MWGui void executeFile (const std::string& path); + void clear(); + virtual void resetReference (); protected: diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index a1244b116..5663f5c5d 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -36,6 +36,7 @@ #include "itemview.hpp" #include "itemwidget.hpp" #include "inventoryitemmodel.hpp" +#include "containeritemmodel.hpp" #include "sortfilteritemmodel.hpp" #include "pickpocketitemmodel.hpp" #include "draganddrop.hpp" @@ -61,7 +62,6 @@ namespace MWGui mDisposeCorpseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ContainerWindow::onDisposeCorpseButtonClicked); mCloseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ContainerWindow::onCloseButtonClicked); - mCloseButton->eventKeyButtonPressed += MyGUI::newDelegate(this, &ContainerWindow::onKeyPressed); mTakeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ContainerWindow::onTakeAllButtonClicked); setCoord(200,0,600,300); @@ -219,22 +219,39 @@ namespace MWGui dropItem(); } - void ContainerWindow::openContainer(const MWWorld::Ptr& container, bool loot) + void ContainerWindow::setPtr(const MWWorld::Ptr& container) { + /* + Start of tes3mp addition + + Mark this container as open for multiplayer logic purposes + */ mwmp::Main::get().getCellController()->openContainer(container, loot); + /* + End of tes3mp addition + */ mPickpocketDetected = false; mPtr = container; - if (mPtr.getTypeName() == typeid(ESM::NPC).name() && !loot) + bool loot = mPtr.getClass().isActor() && mPtr.getClass().getCreatureStats(mPtr).isDead(); + + if (mPtr.getClass().hasInventoryStore(mPtr)) { - // we are stealing stuff - MWWorld::Ptr player = MWMechanics::getPlayer(); - mModel = new PickpocketItemModel(player, new InventoryItemModel(container), - !mPtr.getClass().getCreatureStats(mPtr).getKnockedDown()); + if (mPtr.getClass().isNpc() && !loot) + { + // we are stealing stuff + MWWorld::Ptr player = MWMechanics::getPlayer(); + mModel = new PickpocketItemModel(player, new InventoryItemModel(container), + !mPtr.getClass().getCreatureStats(mPtr).getKnockedDown()); + } + else + mModel = new InventoryItemModel(container); } else - mModel = new InventoryItemModel(container); + { + mModel = new ContainerItemModel(container); + } mDisposeCorpseButton->setVisible(loot); @@ -248,14 +265,6 @@ namespace MWGui setTitle(container.getClass().getName(container)); } - void ContainerWindow::onKeyPressed(MyGUI::Widget *_sender, MyGUI::KeyCode _key, MyGUI::Char _char) - { - if (_key == MyGUI::KeyCode::Space) - onCloseButtonClicked(mCloseButton); - if (_key == MyGUI::KeyCode::Return || _key == MyGUI::KeyCode::NumpadEnter) - onTakeAllButtonClicked(mTakeButton); - } - void ContainerWindow::resetReference() { ReferenceInterface::resetReference(); @@ -264,10 +273,19 @@ namespace MWGui mSortModel = NULL; } - void ContainerWindow::close() + void ContainerWindow::onClose() { + /* + Start of tes3mp addition + + Mark this container as closed for multiplayer logic purposes + */ mwmp::Main::get().getCellController()->closeContainer(mPtr); - WindowBase::close(); + /* + End of tes3mp addition + */ + + WindowBase::onClose(); if (dynamic_cast(mModel) // Make sure we were actually closed, rather than just temporarily hidden (e.g. console or main menu opened) @@ -289,17 +307,9 @@ namespace MWGui } } - void ContainerWindow::exit() - { - if(mDragAndDrop == NULL || !mDragAndDrop->mIsOnDragAndDrop) - { - MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container); - } - } - void ContainerWindow::onCloseButtonClicked(MyGUI::Widget* _sender) { - exit(); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container); } void ContainerWindow::onTakeAllButtonClicked(MyGUI::Widget* _sender) @@ -420,9 +430,8 @@ namespace MWGui MWMechanics::Pickpocket pickpocket(player, mPtr); if (pickpocket.pick(item.mBase, count)) { - int value = item.mBase.getClass().getValue(item.mBase) * count; MWBase::Environment::get().getMechanicsManager()->commitCrime( - player, mPtr, MWBase::MechanicsManager::OT_Theft, value, true); + player, mPtr, MWBase::MechanicsManager::OT_Pickpocket, 0, true); MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Container); mPickpocketDetected = true; return false; diff --git a/apps/openmw/mwgui/container.hpp b/apps/openmw/mwgui/container.hpp index 520bce9d9..50c69da3b 100644 --- a/apps/openmw/mwgui/container.hpp +++ b/apps/openmw/mwgui/container.hpp @@ -33,12 +33,13 @@ namespace MWGui public: ContainerWindow(DragAndDrop* dragAndDrop); - void openContainer(const MWWorld::Ptr& container, bool loot=false); - virtual void close(); + void setPtr(const MWWorld::Ptr& container); + virtual void onClose(); + void clear() { resetReference(); } - virtual void resetReference(); + void onFrame(float dt) { checkReferenceAvailable(); } - virtual void exit(); + virtual void resetReference(); private: DragAndDrop* mDragAndDrop; @@ -61,7 +62,6 @@ namespace MWGui void onCloseButtonClicked(MyGUI::Widget* _sender); void onTakeAllButtonClicked(MyGUI::Widget* _sender); void onDisposeCorpseButtonClicked(MyGUI::Widget* sender); - void onKeyPressed(MyGUI::Widget* _sender, MyGUI::KeyCode _key, MyGUI::Char _char); /// @return is taking the item allowed? bool onTakeItem(const ItemStack& item, int count); diff --git a/apps/openmw/mwgui/containeritemmodel.cpp b/apps/openmw/mwgui/containeritemmodel.cpp index d62cd16b0..479638672 100644 --- a/apps/openmw/mwgui/containeritemmodel.cpp +++ b/apps/openmw/mwgui/containeritemmodel.cpp @@ -6,8 +6,11 @@ #include "../mwworld/class.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/environment.hpp" +#include "../mwmechanics/actorutil.hpp" + namespace { @@ -47,6 +50,19 @@ ContainerItemModel::ContainerItemModel (const MWWorld::Ptr& source) mItemSources.push_back(source); } +bool ContainerItemModel::allowedToUseItems() const +{ + if (mItemSources.size() == 0) + return true; + + MWWorld::Ptr ptr = MWMechanics::getPlayer(); + MWWorld::Ptr victim; + + // Check if the player is allowed to use items from opened container + MWBase::MechanicsManager* mm = MWBase::Environment::get().getMechanicsManager(); + return mm->isAllowedToUse(ptr, mItemSources[0], victim); +} + ItemStack ContainerItemModel::getItem (ModelIndex index) { if (index < 0) diff --git a/apps/openmw/mwgui/containeritemmodel.hpp b/apps/openmw/mwgui/containeritemmodel.hpp index 7ced6ae34..c6ecafd46 100644 --- a/apps/openmw/mwgui/containeritemmodel.hpp +++ b/apps/openmw/mwgui/containeritemmodel.hpp @@ -17,6 +17,7 @@ namespace MWGui ContainerItemModel (const MWWorld::Ptr& source); + virtual bool allowedToUseItems() const; virtual ItemStack getItem (ModelIndex index); virtual ModelIndex getIndex (ItemStack item); virtual size_t getItemCount(); diff --git a/apps/openmw/mwgui/countdialog.cpp b/apps/openmw/mwgui/countdialog.cpp index 03cf1cab6..cf058caac 100644 --- a/apps/openmw/mwgui/countdialog.cpp +++ b/apps/openmw/mwgui/countdialog.cpp @@ -56,19 +56,9 @@ namespace MWGui mItemEdit->setValue(maxCount); } - void CountDialog::cancel() //Keeping this here as I don't know if anything else relies on it. - { - exit(); - } - - void CountDialog::exit() - { - setVisible(false); - } - void CountDialog::onCancelButtonClicked(MyGUI::Widget* _sender) { - cancel(); + setVisible(false); } void CountDialog::onOkButtonClicked(MyGUI::Widget* _sender) diff --git a/apps/openmw/mwgui/countdialog.hpp b/apps/openmw/mwgui/countdialog.hpp index 7014b5fad..766612f68 100644 --- a/apps/openmw/mwgui/countdialog.hpp +++ b/apps/openmw/mwgui/countdialog.hpp @@ -15,8 +15,6 @@ namespace MWGui public: CountDialog(); void openCountDialog(const std::string& item, const std::string& message, const int maxCount); - void cancel(); - virtual void exit(); typedef MyGUI::delegates::CMultiDelegate2 EventHandle_WidgetInt; diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index 2e80301d2..e13e626ae 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -3,6 +3,8 @@ #include #include #include +#include +#include #include #include @@ -20,7 +22,6 @@ #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/actorutil.hpp" -#include "widgets.hpp" #include "bookpage.hpp" #include "textcolours.hpp" @@ -52,7 +53,7 @@ namespace MWGui void PersuasionDialog::onCancel(MyGUI::Widget *sender) { - exit(); + setVisible(false); } void PersuasionDialog::onPersuade(MyGUI::Widget *sender) @@ -68,14 +69,15 @@ namespace MWGui else /*if (sender == mBribe1000Button)*/ type = MWBase::MechanicsManager::PT_Bribe1000; - MWBase::Environment::get().getDialogueManager()->persuade(type); + MWBase::DialogueManager::Response response = MWBase::Environment::get().getDialogueManager()->persuade(type); + + eventPersuadeMsg(response.first, response.second); setVisible(false); } - void PersuasionDialog::open() + void PersuasionDialog::onOpen() { - WindowModal::open(); center(); MWWorld::Ptr player = MWMechanics::getPlayer(); @@ -86,11 +88,12 @@ namespace MWGui mBribe1000Button->setEnabled (playerGold >= 1000); mGoldLabel->setCaptionWithReplacing("#{sGold}: " + MyGUI::utility::toString(playerGold)); + WindowModal::onOpen(); } - void PersuasionDialog::exit() + MyGUI::Widget* PersuasionDialog::getDefaultKeyFocus() { - setVisible(false); + return mAdmireButton; } // -------------------------------------------------------------------------------------------------- @@ -219,31 +222,27 @@ namespace MWGui void Choice::activated() { - MWBase::Environment::get().getWindowManager()->playSound("Menu Click"); - MWBase::Environment::get().getDialogueManager()->questionAnswered(mChoiceId); + eventChoiceActivated(mChoiceId); } void Topic::activated() { - MWBase::Environment::get().getWindowManager()->playSound("Menu Click"); - MWBase::Environment::get().getDialogueManager()->keywordSelected(Misc::StringUtils::lowerCase(mTopicId)); + eventTopicActivated(mTopicId); } void Goodbye::activated() { - MWBase::Environment::get().getWindowManager()->playSound("Menu Click"); - MWBase::Environment::get().getDialogueManager()->goodbyeSelected(); + eventActivated(); } // -------------------------------------------------------------------------------------------------- DialogueWindow::DialogueWindow() : WindowBase("openmw_dialogue_window.layout") - , mServices(0) - , mEnabled(false) + , mIsCompanion(false) , mGoodbye(false) , mPersuasionDialog() { @@ -251,17 +250,17 @@ namespace MWGui center(); mPersuasionDialog.setVisible(false); + mPersuasionDialog.eventPersuadeMsg += MyGUI::newDelegate(this, &DialogueWindow::onPersuadeResult); //History view getWidget(mHistory, "History"); //Topics list getWidget(mTopicsList, "TopicsList"); - mTopicsList->eventItemSelected += MyGUI::newDelegate(this, &DialogueWindow::onSelectTopic); + mTopicsList->eventItemSelected += MyGUI::newDelegate(this, &DialogueWindow::onSelectListItem); - MyGUI::Button* byeButton; - getWidget(byeButton, "ByeButton"); - byeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &DialogueWindow::onByeClicked); + getWidget(mGoodbyeButton, "ByeButton"); + mGoodbyeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &DialogueWindow::onByeClicked); getWidget(mDispositionBar, "Disposition"); getWidget(mDispositionText,"DispositionText"); @@ -276,18 +275,36 @@ namespace MWGui mMainWidget->castType()->eventWindowChangeCoord += MyGUI::newDelegate(this, &DialogueWindow::onWindowResize); } - void DialogueWindow::exit() + DialogueWindow::~DialogueWindow() { - if ((!mEnabled || MWBase::Environment::get().getDialogueManager()->isInChoice()) - && !mGoodbye) + mPersuasionDialog.eventPersuadeMsg.clear(); + + deleteLater(); + for (Link* link : mLinks) + delete link; + for (auto link : mTopicLinks) + delete link.second; + for (auto history : mHistoryContents) + delete history; + } + + void DialogueWindow::onTradeComplete() + { + addResponse("", MyGUI::LanguageManager::getInstance().replaceTags("#{sBarterDialog5}")); + } + + bool DialogueWindow::exit() + { + if ((MWBase::Environment::get().getDialogueManager()->isInChoice())) { - // in choice, not allowed to escape, but give access to main menu to allow loading other saves - MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); + return false; } else { + resetReference(); MWBase::Environment::get().getDialogueManager()->goodbyeSelected(); mTopicsList->scrollToTop(); + return true; } } @@ -312,12 +329,13 @@ namespace MWGui void DialogueWindow::onByeClicked(MyGUI::Widget* _sender) { - exit(); + if (exit()) + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Dialogue); } - void DialogueWindow::onSelectTopic(const std::string& topic, int id) + void DialogueWindow::onSelectListItem(const std::string& topic, int id) { - if (!mEnabled || MWBase::Environment::get().getDialogueManager()->isInChoice()) + if (mGoodbye || MWBase::Environment::get().getDialogueManager()->isInChoice()) return; int separatorPos = 0; @@ -328,63 +346,84 @@ namespace MWGui } if (id >= separatorPos) - MWBase::Environment::get().getDialogueManager()->keywordSelected(Misc::StringUtils::lowerCase(topic)); + { + onTopicActivated(topic); + if (mGoodbyeButton->getEnabled()) + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mGoodbyeButton); + } else { const MWWorld::Store &gmst = MWBase::Environment::get().getWorld()->getStore().get(); + MWBase::DialogueManager::Response response; if (topic == gmst.find("sPersuasion")->getString()) mPersuasionDialog.setVisible(true); else if (topic == gmst.find("sCompanionShare")->getString()) - MWBase::Environment::get().getWindowManager()->showCompanionWindow(mPtr); - else if (!MWBase::Environment::get().getDialogueManager()->checkServiceRefused()) + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Companion, mPtr); + else if (!MWBase::Environment::get().getDialogueManager()->checkServiceRefused(response)) { if (topic == gmst.find("sBarter")->getString()) - MWBase::Environment::get().getWindowManager()->startTrade(mPtr); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Barter, mPtr); else if (topic == gmst.find("sSpells")->getString()) - MWBase::Environment::get().getWindowManager()->startSpellBuying(mPtr); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_SpellBuying, mPtr); else if (topic == gmst.find("sTravel")->getString()) - MWBase::Environment::get().getWindowManager()->startTravel(mPtr); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Travel, mPtr); else if (topic == gmst.find("sSpellMakingMenuTitle")->getString()) - MWBase::Environment::get().getWindowManager()->startSpellMaking (mPtr); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_SpellCreation, mPtr); else if (topic == gmst.find("sEnchanting")->getString()) - MWBase::Environment::get().getWindowManager()->startEnchanting (mPtr); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Enchanting, mPtr); else if (topic == gmst.find("sServiceTrainingTitle")->getString()) - MWBase::Environment::get().getWindowManager()->startTraining (mPtr); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Training, mPtr); else if (topic == gmst.find("sRepair")->getString()) - MWBase::Environment::get().getWindowManager()->startRepair (mPtr); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_MerchantRepair, mPtr); } + else + addResponse(response.first, response.second); } } - void DialogueWindow::startDialogue(MWWorld::Ptr actor, std::string npcName, bool resetHistory) + void DialogueWindow::setPtr(const MWWorld::Ptr& actor) { + MWBase::DialogueManager::Response response; + if (!MWBase::Environment::get().getDialogueManager()->startDialogue(actor, response)) + { + // No greetings found. The dialogue window should not be shown. + // If this is a companion, we must show the companion window directly (used by BM_bear_be_unique). + MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Dialogue); + if (isCompanion()) + MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Companion, mPtr); + return; + } + + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mGoodbyeButton); + mGoodbye = false; - mEnabled = true; bool sameActor = (mPtr == actor); mPtr = actor; mTopicsList->setEnabled(true); - setTitle(npcName); - - clearChoices(); + setTitle(mPtr.getClass().getName(mPtr)); mTopicsList->clear(); - if (resetHistory || !sameActor) + if (!sameActor) { for (std::vector::iterator it = mHistoryContents.begin(); it != mHistoryContents.end(); ++it) delete (*it); mHistoryContents.clear(); + + mKeywords.clear(); + updateTopicsPane(); } for (std::vector::iterator it = mLinks.begin(); it != mLinks.end(); ++it) - delete (*it); + mDeleteLater.push_back(*it); // Links are not deleted right away to prevent issues with event handlers mLinks.clear(); - updateOptions(); - + updateDisposition(); restock(); + + addResponse(response.first, response.second, false); } void DialogueWindow::restock() @@ -401,16 +440,35 @@ namespace MWGui } } + void DialogueWindow::deleteLater() + { + for (Link* link : mDeleteLater) + delete link; + mDeleteLater.clear(); + } + void DialogueWindow::setKeywords(std::list keyWords) + { + if (mKeywords == keyWords && isCompanion() == mIsCompanion) + return; + mIsCompanion = isCompanion(); + mKeywords = keyWords; + + updateTopicsPane(); + } + + void DialogueWindow::updateTopicsPane() { mTopicsList->clear(); for (std::map::iterator it = mTopicLinks.begin(); it != mTopicLinks.end(); ++it) - delete it->second; + mDeleteLater.push_back(it->second); mTopicLinks.clear(); mKeywordSearch.clear(); - bool isCompanion = !mPtr.getClass().getScript(mPtr).empty() - && mPtr.getRefData().getLocals().getIntVar(mPtr.getClass().getScript(mPtr), "companion"); + int services = mPtr.getClass().getServices(mPtr); + + bool travel = (mPtr.getTypeName() == typeid(ESM::NPC).name() && !mPtr.get()->mBase->getTransport().empty()) + || (mPtr.getTypeName() == typeid(ESM::Creature).name() && !mPtr.get()->mBase->getTransport().empty()); const MWWorld::Store &gmst = MWBase::Environment::get().getWorld()->getStore().get(); @@ -418,39 +476,40 @@ namespace MWGui if (mPtr.getTypeName() == typeid(ESM::NPC).name()) mTopicsList->addItem(gmst.find("sPersuasion")->getString()); - if (mServices & Service_Trade) + if (services & ESM::NPC::AllItems) mTopicsList->addItem(gmst.find("sBarter")->getString()); - if (mServices & Service_BuySpells) + if (services & ESM::NPC::Spells) mTopicsList->addItem(gmst.find("sSpells")->getString()); - if (mServices & Service_Travel) + if (travel) mTopicsList->addItem(gmst.find("sTravel")->getString()); - if (mServices & Service_CreateSpells) + if (services & ESM::NPC::Spellmaking) mTopicsList->addItem(gmst.find("sSpellmakingMenuTitle")->getString()); - if (mServices & Service_Enchant) + if (services & ESM::NPC::Enchanting) mTopicsList->addItem(gmst.find("sEnchanting")->getString()); - if (mServices & Service_Training) + if (services & ESM::NPC::Training) mTopicsList->addItem(gmst.find("sServiceTrainingTitle")->getString()); - if (mServices & Service_Repair) + if (services & ESM::NPC::Repair) mTopicsList->addItem(gmst.find("sRepair")->getString()); - if (isCompanion) + if (isCompanion()) mTopicsList->addItem(gmst.find("sCompanionShare")->getString()); if (mTopicsList->getItemCount() > 0) mTopicsList->addSeparator(); - for(std::list::iterator it = keyWords.begin(); it != keyWords.end(); ++it) + for(std::list::iterator it = mKeywords.begin(); it != mKeywords.end(); ++it) { mTopicsList->addItem(*it); Topic* t = new Topic(*it); + t->eventTopicActivated += MyGUI::newDelegate(this, &DialogueWindow::onTopicActivated); mTopicLinks[Misc::StringUtils::lowerCase(*it)] = t; mKeywordSearch.seed(Misc::StringUtils::lowerCase(*it), intptr_t(t)); @@ -484,9 +543,11 @@ namespace MWGui typesetter->sectionBreak(9); // choices const TextColours& textColours = MWBase::Environment::get().getWindowManager()->getTextColours(); - for (std::vector >::iterator it = mChoices.begin(); it != mChoices.end(); ++it) + mChoices = MWBase::Environment::get().getDialogueManager()->getChoices(); + for (std::vector >::const_iterator it = mChoices.begin(); it != mChoices.end(); ++it) { Choice* link = new Choice(it->second); + link->eventChoiceActivated += MyGUI::newDelegate(this, &DialogueWindow::onChoiceActivated); mLinks.push_back(link); typesetter->lineBreak(); @@ -496,9 +557,11 @@ namespace MWGui typesetter->write(questionStyle, to_utf8_span(it->first.c_str())); } + mGoodbye = MWBase::Environment::get().getDialogueManager()->isGoodbye(); if (mGoodbye) { Goodbye* link = new Goodbye(); + link->eventActivated += MyGUI::newDelegate(this, &DialogueWindow::onGoodbyeActivated); mLinks.push_back(link); std::string goodbye = MWBase::Environment::get().getWorld()->getStore().get().find("sGoodbye")->getString(); BookTypesetter::Style* questionStyle = typesetter->createHotStyle(body, textColours.answer, textColours.answerOver, @@ -528,10 +591,11 @@ namespace MWGui onScrollbarMoved(mScrollBar, 0); } - MyGUI::Button* byeButton; - getWidget(byeButton, "ByeButton"); bool goodbyeEnabled = !MWBase::Environment::get().getDialogueManager()->isInChoice() || mGoodbye; - byeButton->setEnabled(goodbyeEnabled); + bool goodbyeWasEnabled = mGoodbyeButton->getEnabled(); + mGoodbyeButton->setEnabled(goodbyeEnabled); + if (goodbyeEnabled && !goodbyeWasEnabled) + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mGoodbyeButton); bool topicsEnabled = !MWBase::Environment::get().getDialogueManager()->isInChoice() && !mGoodbye; mTopicsList->setEnabled(topicsEnabled); @@ -542,59 +606,47 @@ namespace MWGui reinterpret_cast(link)->activated(); } - void DialogueWindow::onScrollbarMoved(MyGUI::ScrollBar *sender, size_t pos) + void DialogueWindow::onTopicActivated(const std::string &topicId) { - mHistory->setPosition(0, static_cast(pos) * -1); + MWBase::DialogueManager::Response response = MWBase::Environment::get().getDialogueManager()->keywordSelected(topicId); + addResponse(response.first, response.second); } - void DialogueWindow::addResponse(const std::string &text, const std::string &title, bool needMargin) + void DialogueWindow::onChoiceActivated(int id) { - // This is called from the dialogue manager, so text is - // case-smashed - thus we have to retrieve the correct case - // of the title through the topic list. - std::string realTitle = title; - if (realTitle != "") - { - for (size_t i=0; igetItemCount(); ++i) - { - std::string item = mTopicsList->getItemNameAt(i); - if (Misc::StringUtils::ciEqual(item, title)) - { - realTitle = item; - break; - } - } - } + MWBase::DialogueManager::Response response = MWBase::Environment::get().getDialogueManager()->questionAnswered(id); + addResponse(response.first, response.second); + } - mHistoryContents.push_back(new Response(text, realTitle, needMargin)); - updateHistory(); + void DialogueWindow::onGoodbyeActivated() + { + MWBase::Environment::get().getDialogueManager()->goodbyeSelected(); + MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Dialogue); + resetReference(); } - void DialogueWindow::addMessageBox(const std::string& text) + void DialogueWindow::onScrollbarMoved(MyGUI::ScrollBar *sender, size_t pos) { - mHistoryContents.push_back(new Message(text)); - updateHistory(); + mHistory->setPosition(0, static_cast(pos) * -1); } - void DialogueWindow::addChoice(const std::string& choice, int id) + void DialogueWindow::addResponse(const std::string &title, const std::string &text, bool needMargin) { - mChoices.push_back(std::make_pair(choice, id)); + mHistoryContents.push_back(new Response(text, title, needMargin)); updateHistory(); + updateTopics(); } - void DialogueWindow::clearChoices() + void DialogueWindow::addMessageBox(const std::string& text) { - mChoices.clear(); + mHistoryContents.push_back(new Message(text)); updateHistory(); } - void DialogueWindow::updateOptions() + void DialogueWindow::updateDisposition() { - //Clear the list of topics - mTopicsList->clear(); - bool dispositionVisible = false; - if (mPtr.getClass().isNpc()) + if (!mPtr.isEmpty() && mPtr.getClass().isNpc()) { dispositionVisible = true; mDispositionBar->setProgressRange(100); @@ -620,26 +672,38 @@ namespace MWGui } } - void DialogueWindow::goodbye() + void DialogueWindow::onReferenceUnavailable() { - mGoodbye = true; - mEnabled = false; - updateHistory(); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Dialogue); } - void DialogueWindow::onReferenceUnavailable() + void DialogueWindow::onFrame(float dt) { - MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Dialogue); + checkReferenceAvailable(); + if (mPtr.isEmpty()) + return; + + updateDisposition(); + deleteLater(); + + if (mChoices != MWBase::Environment::get().getDialogueManager()->getChoices() + || mGoodbye != MWBase::Environment::get().getDialogueManager()->isGoodbye()) + updateHistory(); } - void DialogueWindow::onFrame() + void DialogueWindow::updateTopics() { - if(mMainWidget->getVisible() && mPtr.getTypeName() == typeid(ESM::NPC).name()) - { - int disp = MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mPtr); - mDispositionBar->setProgressRange(100); - mDispositionBar->setProgressPosition(disp); - mDispositionText->setCaption(MyGUI::utility::toString(disp)+std::string("/100")); - } + setKeywords(MWBase::Environment::get().getDialogueManager()->getAvailableTopics()); + } + + bool DialogueWindow::isCompanion() + { + return !mPtr.getClass().getScript(mPtr).empty() + && mPtr.getRefData().getLocals().getIntVar(mPtr.getClass().getScript(mPtr), "companion"); + } + + void DialogueWindow::onPersuadeResult(const std::string &title, const std::string &text) + { + addResponse(title, text); } } diff --git a/apps/openmw/mwgui/dialogue.hpp b/apps/openmw/mwgui/dialogue.hpp index 5b5ae5b68..97a0e8b37 100644 --- a/apps/openmw/mwgui/dialogue.hpp +++ b/apps/openmw/mwgui/dialogue.hpp @@ -8,6 +8,8 @@ #include "../mwdialogue/keywordsearch.hpp" +#include + namespace Gui { class MWList; @@ -20,16 +22,17 @@ namespace MWGui namespace MWGui { - class DialogueHistoryViewModel; - class BookPage; - class PersuasionDialog : public WindowModal { public: PersuasionDialog(); - virtual void open(); - virtual void exit(); + typedef MyGUI::delegates::CMultiDelegate2 EventHandle_Result; + EventHandle_Result eventPersuadeMsg; + + virtual void onOpen(); + + virtual MyGUI::Widget* getDefaultKeyFocus(); private: MyGUI::Button* mCancelButton; @@ -54,6 +57,8 @@ namespace MWGui struct Topic : Link { + typedef MyGUI::delegates::CMultiDelegate1 EventHandle_TopicId; + EventHandle_TopicId eventTopicActivated; Topic(const std::string& id) : mTopicId(id) {} std::string mTopicId; virtual void activated (); @@ -61,6 +66,8 @@ namespace MWGui struct Choice : Link { + typedef MyGUI::delegates::CMultiDelegate1 EventHandle_ChoiceId; + EventHandle_ChoiceId eventChoiceActivated; Choice(int id) : mChoiceId(id) {} int mChoiceId; virtual void activated (); @@ -68,6 +75,8 @@ namespace MWGui struct Goodbye : Link { + typedef MyGUI::delegates::CMultiDelegate0 Event_Activated; + Event_Activated eventActivated; virtual void activated (); }; @@ -99,46 +108,41 @@ namespace MWGui { public: DialogueWindow(); + ~DialogueWindow(); - virtual void exit(); + void onTradeComplete(); + + virtual bool exit(); // Events typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void; void notifyLinkClicked (TypesetBook::InteractiveId link); - void startDialogue(MWWorld::Ptr actor, std::string npcName, bool resetHistory); + void setPtr(const MWWorld::Ptr& actor); + void setKeywords(std::list keyWord); - void addResponse (const std::string& text, const std::string& title="", bool needMargin = true); + void addResponse (const std::string& title, const std::string& text, bool needMargin = true); void addMessageBox(const std::string& text); - void addChoice(const std::string& choice, int id); - void clearChoices(); - - void goodbye(); - void onFrame(); - - // make sure to call these before setKeywords() - void setServices(int services) { mServices = services; } - - enum Services - { - Service_Trade = 0x01, - Service_BuySpells = 0x02, - Service_CreateSpells = 0x04, - Service_Enchant = 0x08, - Service_Training = 0x10, - Service_Travel = 0x20, - Service_Repair = 0x40 - }; + void onFrame(float dt); + void clear() { resetReference(); } protected: - void onSelectTopic(const std::string& topic, int id); + void updateTopics(); + void updateTopicsPane(); + bool isCompanion(); + + void onPersuadeResult(const std::string& title, const std::string& text); + void onSelectListItem(const std::string& topic, int id); void onByeClicked(MyGUI::Widget* _sender); void onMouseWheel(MyGUI::Widget* _sender, int _rel); void onWindowResize(MyGUI::Window* _sender); + void onTopicActivated(const std::string& topicId); + void onChoiceActivated(int id); + void onGoodbyeActivated(); void onScrollbarMoved (MyGUI::ScrollBar* sender, size_t pos); @@ -147,21 +151,24 @@ namespace MWGui virtual void onReferenceUnavailable(); private: - void updateOptions(); + void updateDisposition(); void restock(); - - int mServices; + void deleteLater(); bool mEnabled; - bool mGoodbye; + bool mIsCompanion; + std::list mKeywords; std::vector mHistoryContents; std::vector > mChoices; + bool mGoodbye; std::vector mLinks; std::map mTopicLinks; + std::vector mDeleteLater; + KeywordSearchT mKeywordSearch; BookPage* mHistory; @@ -169,6 +176,7 @@ namespace MWGui MyGUI::ScrollBar* mScrollBar; MyGUI::ProgressBar* mDispositionBar; MyGUI::TextBox* mDispositionText; + MyGUI::Button* mGoodbyeButton; PersuasionDialog mPersuasionDialog; diff --git a/apps/openmw/mwgui/draganddrop.cpp b/apps/openmw/mwgui/draganddrop.cpp index fa17d974d..d81b2ed00 100644 --- a/apps/openmw/mwgui/draganddrop.cpp +++ b/apps/openmw/mwgui/draganddrop.cpp @@ -121,10 +121,18 @@ void DragAndDrop::drop(ItemModel *targetModel, ItemView *targetView) mSourceView->update(); } +void DragAndDrop::onFrame() +{ + if (mIsOnDragAndDrop && mItem.mBase.getRefData().getCount() == 0) + finish(); +} + void DragAndDrop::finish() { mIsOnDragAndDrop = false; mSourceSortModel->clearDragItems(); + // since mSourceView doesn't get updated in drag() + MWBase::Environment::get().getWindowManager()->getInventoryWindow()->updateItemView(); MyGUI::Gui::getInstance().destroyWidget(mDraggedWidget); mDraggedWidget = 0; diff --git a/apps/openmw/mwgui/draganddrop.hpp b/apps/openmw/mwgui/draganddrop.hpp index a356fe4e2..dff8cd73c 100644 --- a/apps/openmw/mwgui/draganddrop.hpp +++ b/apps/openmw/mwgui/draganddrop.hpp @@ -29,6 +29,7 @@ namespace MWGui void startDrag (int index, SortFilterItemModel* sortModel, ItemModel* sourceModel, ItemView* sourceView, int count); void drop (ItemModel* targetModel, ItemView* targetView); + void onFrame(); void finish(); }; diff --git a/apps/openmw/mwgui/enchantingdialog.cpp b/apps/openmw/mwgui/enchantingdialog.cpp index 4e4f6ab3b..6e0be4089 100644 --- a/apps/openmw/mwgui/enchantingdialog.cpp +++ b/apps/openmw/mwgui/enchantingdialog.cpp @@ -4,11 +4,11 @@ #include #include +#include #include #include -#include "../mwbase/dialoguemanager.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/containerstore.hpp" @@ -53,6 +53,7 @@ namespace MWGui mSoulBox->eventMouseButtonClick += MyGUI::newDelegate(this, &EnchantingDialog::onSelectSoul); mBuyButton->eventMouseButtonClick += MyGUI::newDelegate(this, &EnchantingDialog::onBuyButtonClicked); mTypeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &EnchantingDialog::onTypeButtonClicked); + mName->eventEditSelectAccept += MyGUI::newDelegate(this, &EnchantingDialog::onAccept); } EnchantingDialog::~EnchantingDialog() @@ -60,9 +61,10 @@ namespace MWGui delete mItemSelectionDialog; } - void EnchantingDialog::open() + void EnchantingDialog::onOpen() { center(); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mName); } void EnchantingDialog::setSoulGem(const MWWorld::Ptr &gem) @@ -100,11 +102,6 @@ namespace MWGui } } - void EnchantingDialog::exit() - { - MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Enchanting); - } - void EnchantingDialog::updateLabels() { std::stringstream enchantCost; @@ -143,57 +140,41 @@ namespace MWGui } } - void EnchantingDialog::startEnchanting (MWWorld::Ptr actor) + void EnchantingDialog::setPtr (const MWWorld::Ptr& ptr) { mName->setCaption(""); - mEnchanting.setSelfEnchanting(false); - mEnchanting.setEnchanter(actor); - - mBuyButton->setCaptionWithReplacing("#{sBuy}"); - - mChanceLayout->setVisible(false); - - mPtr = actor; + if (ptr.getClass().isActor()) + { + mEnchanting.setSelfEnchanting(false); + mEnchanting.setEnchanter(ptr); + mBuyButton->setCaptionWithReplacing("#{sBuy}"); + mChanceLayout->setVisible(false); + mPtr = ptr; + setSoulGem(MWWorld::Ptr()); + mPrice->setVisible(true); + mPriceText->setVisible(true); + } + else + { + mEnchanting.setSelfEnchanting(true); + mEnchanting.setEnchanter(MWMechanics::getPlayer()); + mBuyButton->setCaptionWithReplacing("#{sCreate}"); + bool enabled = Settings::Manager::getBool("show enchant chance","Game"); + mChanceLayout->setVisible(enabled); + mPtr = MWMechanics::getPlayer(); + setSoulGem(ptr); + mPrice->setVisible(false); + mPriceText->setVisible(false); + } - setSoulGem(MWWorld::Ptr()); setItem(MWWorld::Ptr()); - startEditing (); - mPrice->setVisible(true); - mPriceText->setVisible(true); - updateLabels(); - } - - void EnchantingDialog::startSelfEnchanting(MWWorld::Ptr soulgem) - { - mName->setCaption(""); - - MWWorld::Ptr player = MWMechanics::getPlayer(); - - mEnchanting.setSelfEnchanting(true); - mEnchanting.setEnchanter(player); - - mBuyButton->setCaptionWithReplacing("#{sCreate}"); - - bool enabled = Settings::Manager::getBool("show enchant chance","Game"); - - mChanceLayout->setVisible(enabled); - - mPtr = player; - startEditing(); - - setSoulGem(soulgem); - setItem(MWWorld::Ptr()); - - mPrice->setVisible(false); - mPriceText->setVisible(false); updateLabels(); } void EnchantingDialog::onReferenceUnavailable () { - MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Dialogue); MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Enchanting); resetReference(); } @@ -209,7 +190,7 @@ namespace MWGui void EnchantingDialog::onCancelButtonClicked(MyGUI::Widget* sender) { - exit(); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Enchanting); } void EnchantingDialog::onSelectItem(MyGUI::Widget *sender) @@ -304,6 +285,11 @@ namespace MWGui updateEffectsView(); } + void EnchantingDialog::onAccept(MyGUI::EditBox *sender) + { + onBuyButtonClicked(sender); + } + void EnchantingDialog::onBuyButtonClicked(MyGUI::Widget* sender) { if (mEffects.size() <= 0) @@ -364,7 +350,7 @@ namespace MWGui MWBase::Environment::get().getMechanicsManager()->confiscateStolenItemToOwner(player, item, mPtr, 1); MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Enchanting); - MWBase::Environment::get().getDialogueManager()->goodbyeSelected(); + MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode(); return; } } diff --git a/apps/openmw/mwgui/enchantingdialog.hpp b/apps/openmw/mwgui/enchantingdialog.hpp index cb7c6c0d0..4906de919 100644 --- a/apps/openmw/mwgui/enchantingdialog.hpp +++ b/apps/openmw/mwgui/enchantingdialog.hpp @@ -19,15 +19,17 @@ namespace MWGui EnchantingDialog(); virtual ~EnchantingDialog(); - virtual void open(); + virtual void onOpen(); - virtual void exit(); + void onFrame(float dt) { checkReferenceAvailable(); } + void clear() { resetReference(); } void setSoulGem (const MWWorld::Ptr& gem); void setItem (const MWWorld::Ptr& item); - void startEnchanting(MWWorld::Ptr actor); - void startSelfEnchanting(MWWorld::Ptr soulgem); + /// Actor Ptr: buy enchantment from this actor + /// Soulgem Ptr: player self-enchant + void setPtr(const MWWorld::Ptr& ptr); virtual void resetReference(); @@ -46,6 +48,7 @@ namespace MWGui void onBuyButtonClicked(MyGUI::Widget* sender); void updateLabels(); void onTypeButtonClicked(MyGUI::Widget* sender); + void onAccept(MyGUI::EditBox* sender); ItemSelectionDialog* mItemSelectionDialog; @@ -58,7 +61,7 @@ namespace MWGui MyGUI::Button* mTypeButton; MyGUI::Button* mBuyButton; - MyGUI::TextBox* mName; + MyGUI::EditBox* mName; MyGUI::TextBox* mEnchantmentPoints; MyGUI::TextBox* mCastCost; MyGUI::TextBox* mCharge; diff --git a/apps/openmw/mwgui/formatting.cpp b/apps/openmw/mwgui/formatting.cpp index cf4a5b589..a9319048e 100644 --- a/apps/openmw/mwgui/formatting.cpp +++ b/apps/openmw/mwgui/formatting.cpp @@ -406,10 +406,11 @@ namespace MWGui MyGUI::EditBox* box = parent->createWidget("NormalText", MyGUI::IntCoord(0, pag.getCurrentTop(), pag.getPageWidth(), 0), MyGUI::Align::Left | MyGUI::Align::Top, parent->getName() + MyGUI::utility::toString(parent->getChildCount())); - box->setProperty("Static", "true"); - box->setProperty("MultiLine", "true"); - box->setProperty("WordWrap", "true"); - box->setProperty("NeedMouse", "false"); + box->setEditStatic(true); + box->setEditMultiLine(true); + box->setEditWordWrap(true); + box->setNeedMouseFocus(false); + box->setNeedKeyFocus(false); box->setMaxTextLength(text.size()); box->setTextAlign(mBlockStyle.mAlign); box->setTextColour(mTextStyle.mColour); diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index 9fd6d3ed2..3cd800989 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -108,7 +108,7 @@ namespace MWGui HUD::HUD(CustomMarkerCollection &customMarkers, DragAndDrop* dragAndDrop, MWRender::LocalMap* localMapRender) - : Layout("openmw_hud.layout") + : WindowBase("openmw_hud.layout") , LocalMapBase(customMarkers, localMapRender, Settings::Manager::getBool("local map hud fog of war", "Map")) , mHealth(NULL) , mMagicka(NULL) @@ -422,6 +422,20 @@ namespace MWGui if (mIsDrowning) mDrowningFlashTheta += dt * osg::PI*2; + + mSpellIcons->updateWidgets(mEffectBox, true); + + if (mEnemyActorId != -1 && mEnemyHealth->getVisible()) + { + updateEnemyHealthBar(); + } + + if (mIsDrowning) + { + float intensity = (cos(mDrowningFlashTheta) + 2.0f) / 3.0f; + + mDrowningFlash->setAlpha(intensity); + } } void HUD::setSelectedSpell(const std::string& spellId, int successChancePercent) @@ -653,23 +667,6 @@ namespace MWGui } - void HUD::update() - { - mSpellIcons->updateWidgets(mEffectBox, true); - - if (mEnemyActorId != -1 && mEnemyHealth->getVisible()) - { - updateEnemyHealthBar(); - } - - if (mIsDrowning) - { - float intensity = (cos(mDrowningFlashTheta) + 2.0f) / 3.0f; - - mDrowningFlash->setAlpha(intensity); - } - } - void HUD::setEnemy(const MWWorld::Ptr &enemy) { mEnemyActorId = enemy.getClass().getCreatureStats(enemy).getActorId(); @@ -686,6 +683,13 @@ namespace MWGui mEnemyHealthTimer = -1; } + void HUD::clear() + { + unsetSelectedSpell(); + unsetSelectedWeapon(); + resetEnemy(); + } + void HUD::customMarkerCreated(MyGUI::Widget *marker) { marker->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onMapClicked); diff --git a/apps/openmw/mwgui/hud.hpp b/apps/openmw/mwgui/hud.hpp index e2ef52be0..3542f3ebf 100644 --- a/apps/openmw/mwgui/hud.hpp +++ b/apps/openmw/mwgui/hud.hpp @@ -16,7 +16,7 @@ namespace MWGui class SpellIcons; class ItemWidget; - class HUD : public Layout, public LocalMapBase + class HUD : public WindowBase, public LocalMapBase { public: HUD(CustomMarkerCollection& customMarkers, DragAndDrop* dragAndDrop, MWRender::LocalMap* localMapRender); @@ -55,11 +55,11 @@ namespace MWGui MyGUI::Widget* getEffectBox() { return mEffectBox; } - void update(); - void setEnemy(const MWWorld::Ptr& enemy); void resetEnemy(); + void clear(); + private: MyGUI::ProgressBar *mHealth, *mMagicka, *mStamina, *mEnemyHealth, *mDrowning; MyGUI::Widget* mHealthFrame; diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 0fdd2bcf6..97d0a5247 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -377,7 +377,7 @@ namespace MWGui dirtyPreview(); } - void InventoryWindow::open() + void InventoryWindow::onOpen() { if (!mPtr.isEmpty()) { @@ -603,11 +603,8 @@ namespace MWGui mEncumbranceBar->setValue(static_cast(encumbrance), static_cast(capacity)); } - void InventoryWindow::onFrame() + void InventoryWindow::onFrame(float dt) { - if (!mMainWidget->getVisible()) - return; - updateEncumbranceBar(); } diff --git a/apps/openmw/mwgui/inventorywindow.hpp b/apps/openmw/mwgui/inventorywindow.hpp index 761464540..5576b52ed 100644 --- a/apps/openmw/mwgui/inventorywindow.hpp +++ b/apps/openmw/mwgui/inventorywindow.hpp @@ -35,12 +35,12 @@ namespace MWGui public: InventoryWindow(DragAndDrop* dragAndDrop, osg::Group* parent, Resource::ResourceSystem* resourceSystem); - virtual void open(); + virtual void onOpen(); /// start trading, disables item drag&drop void setTrading(bool trading); - void onFrame(); + void onFrame(float dt); void pickUpObject (MWWorld::Ptr object); diff --git a/apps/openmw/mwgui/itemmodel.cpp b/apps/openmw/mwgui/itemmodel.cpp index 390bb0586..ffcf9075e 100644 --- a/apps/openmw/mwgui/itemmodel.cpp +++ b/apps/openmw/mwgui/itemmodel.cpp @@ -119,6 +119,11 @@ namespace MWGui return ret; } + bool ItemModel::allowedToUseItems() const + { + return true; + } + bool ItemModel::allowedToInsertItems() const { return true; @@ -135,6 +140,11 @@ namespace MWGui delete mSourceModel; } + bool ProxyItemModel::allowedToUseItems() const + { + return mSourceModel->allowedToUseItems(); + } + MWWorld::Ptr ProxyItemModel::copyItem (const ItemStack& item, size_t count, bool setNewOwner) { return mSourceModel->copyItem (item, count, setNewOwner); diff --git a/apps/openmw/mwgui/itemmodel.hpp b/apps/openmw/mwgui/itemmodel.hpp index 20955b206..bc6be8023 100644 --- a/apps/openmw/mwgui/itemmodel.hpp +++ b/apps/openmw/mwgui/itemmodel.hpp @@ -70,6 +70,9 @@ namespace MWGui virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count, bool setNewOwner=false) = 0; virtual void removeItem (const ItemStack& item, size_t count) = 0; + /// Is the player allowed to use items from this item model? (default true) + virtual bool allowedToUseItems() const; + /// Is the player allowed to insert items into this model? (default true) virtual bool allowedToInsertItems() const; @@ -85,6 +88,9 @@ namespace MWGui public: ProxyItemModel(); virtual ~ProxyItemModel(); + + bool allowedToUseItems() const; + virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count, bool setNewOwner=false); virtual void removeItem (const ItemStack& item, size_t count); virtual ModelIndex getIndex (ItemStack item); diff --git a/apps/openmw/mwgui/itemselection.cpp b/apps/openmw/mwgui/itemselection.cpp index 095f392b7..effd11d55 100644 --- a/apps/openmw/mwgui/itemselection.cpp +++ b/apps/openmw/mwgui/itemselection.cpp @@ -29,9 +29,10 @@ namespace MWGui center(); } - void ItemSelectionDialog::exit() + bool ItemSelectionDialog::exit() { eventDialogCanceled(); + return true; } void ItemSelectionDialog::openContainer(const MWWorld::Ptr& container) diff --git a/apps/openmw/mwgui/itemselection.hpp b/apps/openmw/mwgui/itemselection.hpp index 50e15a3fe..07b6452a5 100644 --- a/apps/openmw/mwgui/itemselection.hpp +++ b/apps/openmw/mwgui/itemselection.hpp @@ -21,7 +21,7 @@ namespace MWGui public: ItemSelectionDialog(const std::string& label); - virtual void exit(); + virtual bool exit(); typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void; typedef MyGUI::delegates::CMultiDelegate1 EventHandle_Item; diff --git a/apps/openmw/mwgui/jailscreen.cpp b/apps/openmw/mwgui/jailscreen.cpp index ecd6d18f0..a994c8840 100644 --- a/apps/openmw/mwgui/jailscreen.cpp +++ b/apps/openmw/mwgui/jailscreen.cpp @@ -37,8 +37,6 @@ namespace MWGui { getWidget(mProgressBar, "ProgressBar"); - setVisible(false); - mTimeAdvancer.eventProgressChanged += MyGUI::newDelegate(this, &JailScreen::onJailProgressChanged); mTimeAdvancer.eventFinished += MyGUI::newDelegate(this, &JailScreen::onJailFinished); diff --git a/apps/openmw/mwgui/jailscreen.hpp b/apps/openmw/mwgui/jailscreen.hpp index 7a544126d..36d19b5a9 100644 --- a/apps/openmw/mwgui/jailscreen.hpp +++ b/apps/openmw/mwgui/jailscreen.hpp @@ -14,6 +14,8 @@ namespace MWGui void onFrame(float dt); + bool exit() { return false; } + private: int mDays; diff --git a/apps/openmw/mwgui/journalwindow.cpp b/apps/openmw/mwgui/journalwindow.cpp index e96cc8b70..30a440b58 100644 --- a/apps/openmw/mwgui/journalwindow.cpp +++ b/apps/openmw/mwgui/journalwindow.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -44,7 +45,7 @@ namespace static char const LeftTopicIndex [] = "LeftTopicIndex"; static char const RightTopicIndex [] = "RightTopicIndex"; - struct JournalWindowImpl : MWGui::WindowBase, MWGui::JournalBooks, MWGui::JournalWindow + struct JournalWindowImpl : MWGui::JournalBooks, MWGui::JournalWindow { struct DisplayState { @@ -84,19 +85,24 @@ namespace void adviseButtonClick (char const * name, void (JournalWindowImpl::*Handler) (MyGUI::Widget* _sender)) { - getWidget (name) -> + getWidget (name) -> eventMouseButtonClick += newDelegate(this, Handler); } + void adviseKeyPress (char const * name, void (JournalWindowImpl::*Handler) (MyGUI::Widget* _sender, MyGUI::KeyCode key, MyGUI::Char character)) + { + getWidget (name) -> + eventKeyButtonPressed += newDelegate(this, Handler); + } + MWGui::BookPage* getPage (char const * name) { return getWidget (name); } JournalWindowImpl (MWGui::JournalViewModel::Ptr Model, bool questList) - : WindowBase("openmw_journal.layout"), JournalBooks (Model) + : JournalBooks (Model), JournalWindow() { - mMainWidget->setVisible(false); center(); adviseButtonClick (OptionsBTN, &JournalWindowImpl::notifyOptions ); @@ -112,6 +118,12 @@ namespace adviseButtonClick (ShowAllBTN, &JournalWindowImpl::notifyShowAll ); adviseButtonClick (ShowActiveBTN, &JournalWindowImpl::notifyShowActive); + adviseKeyPress (OptionsBTN, &JournalWindowImpl::notifyKeyPress); + adviseKeyPress (PrevPageBTN, &JournalWindowImpl::notifyKeyPress); + adviseKeyPress (NextPageBTN, &JournalWindowImpl::notifyKeyPress); + adviseKeyPress (CloseBTN, &JournalWindowImpl::notifyKeyPress); + adviseKeyPress (JournalBTN, &JournalWindowImpl::notifyKeyPress); + Gui::MWList* list = getWidget(QuestsList); list->eventItemSelected += MyGUI::newDelegate(this, &JournalWindowImpl::notifyQuestClicked); @@ -211,7 +223,7 @@ namespace button->setPosition(button->getPosition() + MyGUI::IntPoint(diff.width,0)); } - void open() + void onOpen() { if (!MWBase::Environment::get().getWindowManager ()->getJournalAllowed ()) { @@ -238,9 +250,11 @@ namespace --page; } updateShowingPages(); + + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(getWidget(CloseBTN)); } - void close() + void onClose() { mModel->unload (); @@ -349,8 +363,19 @@ namespace relPages = 0; } - setVisible (PrevPageBTN, page > 0); - setVisible (NextPageBTN, relPages > 2); + MyGUI::Widget* nextPageBtn = getWidget(NextPageBTN); + MyGUI::Widget* prevPageBtn = getWidget(PrevPageBTN); + + MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getKeyFocusWidget(); + bool nextPageVisible = relPages > 2; + nextPageBtn->setVisible(nextPageVisible); + bool prevPageVisible = page > 0; + prevPageBtn->setVisible(prevPageVisible); + + if (focus == nextPageBtn && !nextPageVisible && prevPageVisible) + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(prevPageBtn); + else if (focus == prevPageBtn && !prevPageVisible && nextPageVisible) + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(nextPageBtn); setVisible (PageOneNum, relPages > 0); setVisible (PageTwoNum, relPages > 1); @@ -362,6 +387,14 @@ namespace setText (PageTwoNum, page + 2); } + void notifyKeyPress(MyGUI::Widget* sender, MyGUI::KeyCode key, MyGUI::Char character) + { + if (key == MyGUI::KeyCode::ArrowUp) + notifyPrevPage(sender); + else if (key == MyGUI::KeyCode::ArrowDown) + notifyNextPage(sender); + } + void notifyTopicClicked (intptr_t linkId) { Book topicBook = createTopicBook (linkId); @@ -603,3 +636,9 @@ MWGui::JournalWindow * MWGui::JournalWindow::create (JournalViewModel::Ptr Model { return new JournalWindowImpl (Model, questList); } + +MWGui::JournalWindow::JournalWindow() + :WindowBase("openmw_journal.layout") +{ + +} diff --git a/apps/openmw/mwgui/journalwindow.hpp b/apps/openmw/mwgui/journalwindow.hpp index cc645ca44..c9bf0ef0a 100644 --- a/apps/openmw/mwgui/journalwindow.hpp +++ b/apps/openmw/mwgui/journalwindow.hpp @@ -1,6 +1,8 @@ #ifndef MWGUI_JOURNAL_H #define MWGUI_JOURNAL_H +#include "windowbase.hpp" + #include namespace MWBase { class WindowManager; } @@ -9,8 +11,10 @@ namespace MWGui { struct JournalViewModel; - struct JournalWindow + struct JournalWindow : public WindowBase { + JournalWindow(); + /// construct a new instance of the one JournalWindow implementation static JournalWindow * create (std::shared_ptr Model, bool questList); diff --git a/apps/openmw/mwgui/keyboardnavigation.cpp b/apps/openmw/mwgui/keyboardnavigation.cpp new file mode 100644 index 000000000..d27185204 --- /dev/null +++ b/apps/openmw/mwgui/keyboardnavigation.cpp @@ -0,0 +1,283 @@ +#include "keyboardnavigation.hpp" + +#include +#include +#include +#include +#include + +#include "../mwbase/windowmanager.hpp" +#include "../mwbase/environment.hpp" + +namespace MWGui +{ + +bool shouldAcceptKeyFocus(MyGUI::Widget* w) +{ + return w && !w->castType(false) && w->getInheritedEnabled() && w->getInheritedVisible() && w->getVisible() && w->getEnabled(); +} + +/// Recursively get all child widgets that accept keyboard input +void getKeyFocusWidgets(MyGUI::Widget* parent, std::vector& results) +{ + if (!parent->getVisible() || !parent->getEnabled()) + return; + + MyGUI::EnumeratorWidgetPtr enumerator = parent->getEnumerator(); + while (enumerator.next()) + { + MyGUI::Widget* w = enumerator.current(); + if (!w->getVisible() || !w->getEnabled()) + continue; + if (w->getNeedKeyFocus() && shouldAcceptKeyFocus(w)) + results.push_back(w); + else + getKeyFocusWidgets(w, results); + } +} + +KeyboardNavigation::KeyboardNavigation() + : mCurrentFocus(nullptr) + , mModalWindow(nullptr) +{ + MyGUI::WidgetManager::getInstance().registerUnlinker(this); +} + +KeyboardNavigation::~KeyboardNavigation() +{ + MyGUI::WidgetManager::getInstance().unregisterUnlinker(this); +} + +void KeyboardNavigation::saveFocus(int mode) +{ + MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getKeyFocusWidget(); + if (shouldAcceptKeyFocus(focus)) + { + mKeyFocus[mode] = focus; + } + else + { + mKeyFocus[mode] = mCurrentFocus; + } +} + +void KeyboardNavigation::restoreFocus(int mode) +{ + std::map::const_iterator found = mKeyFocus.find(mode); + if (found != mKeyFocus.end()) + { + MyGUI::Widget* w = found->second; + if (w && w->getVisible() && w->getEnabled()) + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(found->second); + } +} + +void KeyboardNavigation::_unlinkWidget(MyGUI::Widget *widget) +{ + for (std::pair& w : mKeyFocus) + if (w.second == widget) + w.second = nullptr; + if (widget == mCurrentFocus) + mCurrentFocus = nullptr; +} + +void styleFocusedButton(MyGUI::Widget* w) +{ + if (w) + { + if (MyGUI::Button* b = w->castType(false)) + { + b->_setWidgetState("highlighted"); + } + } +} + +bool isRootParent(MyGUI::Widget* widget, MyGUI::Widget* root) +{ + while (widget && widget->getParent()) + widget = widget->getParent(); + return widget == root; +} + +void KeyboardNavigation::onFrame() +{ + MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getKeyFocusWidget(); + + if (focus == mCurrentFocus) + { + styleFocusedButton(mCurrentFocus); + return; + } + + // workaround incorrect key focus resets (fix in MyGUI TBD) + if (!shouldAcceptKeyFocus(focus) && shouldAcceptKeyFocus(mCurrentFocus) && (!mModalWindow || isRootParent(mCurrentFocus, mModalWindow))) + { + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCurrentFocus); + focus = mCurrentFocus; + } + + // style highlighted button (won't be needed for MyGUI 3.2.3) + if (focus != mCurrentFocus) + { + if (mCurrentFocus) + { + if (MyGUI::Button* b = mCurrentFocus->castType(false)) + b->_setWidgetState("normal"); + } + + mCurrentFocus = focus; + } + + styleFocusedButton(mCurrentFocus); +} + +void KeyboardNavigation::setDefaultFocus(MyGUI::Widget *window, MyGUI::Widget *defaultFocus) +{ + MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getKeyFocusWidget(); + if (!focus || !shouldAcceptKeyFocus(focus)) + { + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(defaultFocus); + } + else + { + if (!isRootParent(focus, window)) + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(defaultFocus); + } +} + +void KeyboardNavigation::setModalWindow(MyGUI::Widget *window) +{ + mModalWindow = window; +} + +enum Direction +{ + D_Left, + D_Up, + D_Right, + D_Down, + D_Next, + D_Prev +}; + +bool KeyboardNavigation::injectKeyPress(MyGUI::KeyCode key, unsigned int text) +{ + switch (key.getValue()) + { + case MyGUI::KeyCode::ArrowLeft: + return switchFocus(D_Left, false); + case MyGUI::KeyCode::ArrowRight: + return switchFocus(D_Right, false); + case MyGUI::KeyCode::ArrowUp: + return switchFocus(D_Up, false); + case MyGUI::KeyCode::ArrowDown: + return switchFocus(D_Down, false); + case MyGUI::KeyCode::Tab: + return switchFocus(MyGUI::InputManager::getInstance().isShiftPressed() ? D_Prev : D_Next, true); + case MyGUI::KeyCode::Return: + case MyGUI::KeyCode::NumpadEnter: + case MyGUI::KeyCode::Space: + return accept(); + default: + return false; + } +} + +bool KeyboardNavigation::switchFocus(int direction, bool wrap) +{ + MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getKeyFocusWidget(); + + bool isCycle = (direction == D_Prev || direction == D_Next); + + if ((focus && focus->getTypeName().find("Button") == std::string::npos) && !isCycle) + return false; + + if (focus && isCycle && focus->getUserString("AcceptTab") == "true") + return false; + + if ((!focus || !focus->getNeedKeyFocus()) && isCycle) + { + // if nothing is selected, select the first widget + return selectFirstWidget(); + } + if (!focus) + return false; + + MyGUI::Widget* window = focus; + while (window && window->getParent()) + window = window->getParent(); + MyGUI::VectorWidgetPtr keyFocusList; + getKeyFocusWidgets(window, keyFocusList); + + if (keyFocusList.empty()) + return false; + + MyGUI::VectorWidgetPtr::iterator found = std::find(keyFocusList.begin(), keyFocusList.end(), focus); + if (found == keyFocusList.end()) + { + if (isCycle) + return selectFirstWidget(); + else + return false; + } + + bool forward = (direction == D_Next || direction == D_Right || direction == D_Down); + + int index = found - keyFocusList.begin(); + index = forward ? (index+1) : (index-1); + if (wrap) + index = (index + keyFocusList.size())%keyFocusList.size(); + else + index = std::min(std::max(0, index), static_cast(keyFocusList.size())-1); + + MyGUI::Widget* next = keyFocusList[index]; + int vertdiff = next->getTop() - focus->getTop(); + int horizdiff = next->getLeft() - focus->getLeft(); + bool isVertical = std::abs(vertdiff) > std::abs(horizdiff); + if (direction == D_Right && (horizdiff <= 0 || isVertical)) + return false; + else if (direction == D_Left && (horizdiff >= 0 || isVertical)) + return false; + else if (direction == D_Down && (vertdiff <= 0 || !isVertical)) + return false; + else if (direction == D_Up && (vertdiff >= 0 || !isVertical)) + return false; + + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(keyFocusList[index]); + return true; +} + +bool KeyboardNavigation::selectFirstWidget() +{ + MyGUI::VectorWidgetPtr keyFocusList; + MyGUI::EnumeratorWidgetPtr enumerator = MyGUI::Gui::getInstance().getEnumerator(); + if (mModalWindow) + enumerator = mModalWindow->getEnumerator(); + while (enumerator.next()) + getKeyFocusWidgets(enumerator.current(), keyFocusList); + + if (!keyFocusList.empty()) + { + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(keyFocusList[0]); + return true; + } + return false; +} + +bool KeyboardNavigation::accept() +{ + MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getKeyFocusWidget(); + if (!focus) + return false; + //MyGUI::Button* button = focus->castType(false); + //if (button && button->getEnabled()) + if (focus->getTypeName().find("Button") != std::string::npos && focus->getEnabled()) + { + focus->eventMouseButtonClick(focus); + return true; + } + return false; +} + + +} diff --git a/apps/openmw/mwgui/keyboardnavigation.hpp b/apps/openmw/mwgui/keyboardnavigation.hpp new file mode 100644 index 000000000..728f16a3d --- /dev/null +++ b/apps/openmw/mwgui/keyboardnavigation.hpp @@ -0,0 +1,47 @@ +#ifndef OPENMW_MWGUI_KEYBOARDNAVIGATION_H +#define OPENMW_MWGUI_KEYBOARDNAVIGATION_H + +#include +#include + +namespace MWGui +{ + + class KeyboardNavigation : public MyGUI::IUnlinkWidget + { + public: + KeyboardNavigation(); + ~KeyboardNavigation(); + + /// @return Was the key handled by this class? + bool injectKeyPress(MyGUI::KeyCode key, unsigned int text); + + void saveFocus(int mode); + void restoreFocus(int mode); + + void _unlinkWidget(MyGUI::Widget* widget); + + void onFrame(); + + /// Set a key focus widget for this window, if one isn't already set. + void setDefaultFocus(MyGUI::Widget* window, MyGUI::Widget* defaultFocus); + + void setModalWindow(MyGUI::Widget* window); + + private: + bool switchFocus(int direction, bool wrap); + + bool selectFirstWidget(); + + /// Send button press event to focused button + bool accept(); + + std::map mKeyFocus; + + MyGUI::Widget* mCurrentFocus; + MyGUI::Widget* mModalWindow; + }; + +} + +#endif diff --git a/apps/openmw/mwgui/levelupdialog.cpp b/apps/openmw/mwgui/levelupdialog.cpp index da8e93279..286405f79 100644 --- a/apps/openmw/mwgui/levelupdialog.cpp +++ b/apps/openmw/mwgui/levelupdialog.cpp @@ -41,6 +41,7 @@ namespace MWGui { MyGUI::TextBox* t; getWidget(t, "AttribVal" + MyGUI::utility::toString(i)); + mAttributeValues.push_back(t); MyGUI::Button* b; getWidget(b, "Attrib" + MyGUI::utility::toString(i)); @@ -48,10 +49,7 @@ namespace MWGui b->eventMouseButtonClick += MyGUI::newDelegate(this, &LevelupDialog::onAttributeClicked); mAttributes.push_back(b); - mAttributeValues.push_back(t); - getWidget(t, "AttribMultiplier" + MyGUI::utility::toString(i)); - mAttributeMultipliers.push_back(t); } @@ -128,7 +126,7 @@ namespace MWGui setAttributeValues(); } - void LevelupDialog::open() + void LevelupDialog::onOpen() { MWBase::World *world = MWBase::Environment::get().getWorld(); MWWorld::Ptr player = world->getPlayerPtr(); diff --git a/apps/openmw/mwgui/levelupdialog.hpp b/apps/openmw/mwgui/levelupdialog.hpp index d49f7536d..36810665e 100644 --- a/apps/openmw/mwgui/levelupdialog.hpp +++ b/apps/openmw/mwgui/levelupdialog.hpp @@ -11,7 +11,7 @@ namespace MWGui public: LevelupDialog(); - virtual void open(); + virtual void onOpen(); private: MyGUI::Button* mOkButton; diff --git a/apps/openmw/mwgui/loadingscreen.cpp b/apps/openmw/mwgui/loadingscreen.cpp index 15d9d555f..4753c39f2 100644 --- a/apps/openmw/mwgui/loadingscreen.cpp +++ b/apps/openmw/mwgui/loadingscreen.cpp @@ -50,8 +50,8 @@ namespace MWGui mBackgroundImage = MyGUI::Gui::getInstance().createWidgetReal("ImageBox", 0,0,1,1, MyGUI::Align::Stretch, "Menu"); - - setVisible(false); + mSceneImage = MyGUI::Gui::getInstance().createWidgetReal("ImageBox", 0,0,1,1, + MyGUI::Align::Stretch, "Scene"); findSplashScreens(); } @@ -112,6 +112,7 @@ namespace MWGui { WindowBase::setVisible(visible); mBackgroundImage->setVisible(visible); + mSceneImage->setVisible(visible); } double LoadingScreen::getTargetFrameRate() const @@ -216,8 +217,11 @@ namespace MWGui // TODO: add option (filename pattern?) to use image aspect ratio instead of 4:3 // we can't do this by default, because the Morrowind splash screens are 1024x1024, but should be displayed as 4:3 bool stretch = Settings::Manager::getBool("stretch menu background", "GUI"); + mBackgroundImage->setVisible(true); mBackgroundImage->setBackgroundImage(randomSplash, true, stretch); } + mSceneImage->setBackgroundImage(""); + mSceneImage->setVisible(false); } void LoadingScreen::setProgressRange (size_t range) @@ -294,9 +298,11 @@ namespace MWGui mViewer->getCamera()->setInitialDrawCallback(new CopyFramebufferToTextureCallback(mTexture)); mBackgroundImage->setBackgroundImage(""); + mBackgroundImage->setVisible(false); - mBackgroundImage->setRenderItemTexture(mGuiTexture.get()); - mBackgroundImage->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 0.f, 1.f, 1.f)); + mSceneImage->setRenderItemTexture(mGuiTexture.get()); + mSceneImage->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 0.f, 1.f, 1.f)); + mSceneImage->setVisible(true); } void LoadingScreen::draw() diff --git a/apps/openmw/mwgui/loadingscreen.hpp b/apps/openmw/mwgui/loadingscreen.hpp index 2f8831fdc..8536972a3 100644 --- a/apps/openmw/mwgui/loadingscreen.hpp +++ b/apps/openmw/mwgui/loadingscreen.hpp @@ -72,6 +72,7 @@ namespace MWGui MyGUI::TextBox* mLoadingText; MyGUI::ScrollBar* mProgressBar; BackgroundImage* mBackgroundImage; + BackgroundImage* mSceneImage; std::vector mSplashScreens; diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index 07f585df2..b79646d71 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -22,7 +22,7 @@ namespace MWGui { MainMenu::MainMenu(int w, int h, const VFS::Manager* vfs, const std::string& versionDescription) - : Layout("openmw_mainmenu.layout") + : WindowBase("openmw_mainmenu.layout") , mWidth (w), mHeight (h) , mVFS(vfs), mButtonBox(0) , mBackground(NULL) @@ -56,15 +56,31 @@ namespace MWGui if (visible) updateMenu(); - showBackground( - MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_MainMenu) && - MWBase::Environment::get().getStateManager()->getState() == MWBase::StateManager::State_NoGame); + bool isMainMenu = + MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_MainMenu) && + MWBase::Environment::get().getStateManager()->getState() == MWBase::StateManager::State_NoGame; + + showBackground(isMainMenu); + + if (visible) + { + if (isMainMenu) + { + if (mButtons["loadgame"]->getVisible()) + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mButtons["loadgame"]); + else + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mButtons["newgame"]); + } + else + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mButtons["return"]); + } Layout::setVisible (visible); } void MainMenu::onNewGameConfirmed() { + MWBase::Environment::get().getWindowManager()->removeGuiMode (MWGui::GM_MainMenu); MWBase::Environment::get().getStateManager()->newGame(); } @@ -182,7 +198,7 @@ namespace MWGui } } - void MainMenu::update(float dt) + void MainMenu::onFrame(float dt) { if (mVideo) { @@ -194,6 +210,11 @@ namespace MWGui } } + bool MainMenu::exit() + { + return MWBase::Environment::get().getStateManager()->getState() == MWBase::StateManager::State_Running; + } + void MainMenu::updateMenu() { setCoord(0,0, mWidth, mHeight); @@ -244,7 +265,8 @@ namespace MWGui buttons.push_back("exitgame"); // Create new buttons if needed - for (std::vector::iterator it = buttons.begin(); it != buttons.end(); ++it) + std::vector allButtons { "return", "newgame", "savegame", "loadgame", "options", "credits", "exitgame"}; + for (std::vector::iterator it = allButtons.begin(); it != allButtons.end(); ++it) { if (mButtons.find(*it) == mButtons.end()) { diff --git a/apps/openmw/mwgui/mainmenu.hpp b/apps/openmw/mwgui/mainmenu.hpp index fe256dc8e..82553d22e 100644 --- a/apps/openmw/mwgui/mainmenu.hpp +++ b/apps/openmw/mwgui/mainmenu.hpp @@ -1,7 +1,7 @@ #ifndef OPENMW_GAME_MWGUI_MAINMENU_H #define OPENMW_GAME_MWGUI_MAINMENU_H -#include "layout.hpp" +#include "windowbase.hpp" namespace Gui { @@ -20,7 +20,7 @@ namespace MWGui class SaveGameDialog; class VideoWidget; - class MainMenu : public Layout + class MainMenu : public WindowBase { int mWidth; int mHeight; @@ -36,7 +36,9 @@ namespace MWGui virtual void setVisible (bool visible); - void update(float dt); + void onFrame(float dt); + + bool exit(); private: const VFS::Manager* mVFS; diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index 3db7d080b..ff389c797 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -760,7 +760,7 @@ namespace MWGui void MapWindow::onNoteEditDelete() { ConfirmationDialog* confirmation = MWBase::Environment::get().getWindowManager()->getConfirmationDialog(); - confirmation->askForConfirmation("#{sDeleteNote}", "#{sYes}", "#{sNo}"); + confirmation->askForConfirmation("#{sDeleteNote}"); confirmation->eventCancelClicked.clear(); confirmation->eventOkClicked.clear(); confirmation->eventOkClicked += MyGUI::newDelegate(this, &MapWindow::onNoteEditDeleteConfirm); @@ -1002,7 +1002,7 @@ namespace MWGui MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Map); } - void MapWindow::open() + void MapWindow::onOpen() { ensureGlobalMapLoaded(); @@ -1166,18 +1166,13 @@ namespace MWGui return MyGUI::TextIterator::getOnlyText(mTextEdit->getCaption()); } - void EditNoteDialog::open() + void EditNoteDialog::onOpen() { - WindowModal::open(); + WindowModal::onOpen(); center(); MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mTextEdit); } - void EditNoteDialog::exit() - { - setVisible(false); - } - void EditNoteDialog::onCancelButtonClicked(MyGUI::Widget *sender) { setVisible(false); diff --git a/apps/openmw/mwgui/mapwindow.hpp b/apps/openmw/mwgui/mapwindow.hpp index c5877470a..bb5546174 100644 --- a/apps/openmw/mwgui/mapwindow.hpp +++ b/apps/openmw/mwgui/mapwindow.hpp @@ -211,8 +211,7 @@ namespace MWGui public: EditNoteDialog(); - virtual void open(); - virtual void exit(); + virtual void onOpen(); void showDeleteButton(bool show); bool getDeleteButtonShown(); @@ -269,7 +268,7 @@ namespace MWGui void ensureGlobalMapLoaded(); - virtual void open(); + virtual void onOpen(); void onFrame(float dt); diff --git a/apps/openmw/mwgui/merchantrepair.cpp b/apps/openmw/mwgui/merchantrepair.cpp index 9e27c0154..d3504ebc3 100644 --- a/apps/openmw/mwgui/merchantrepair.cpp +++ b/apps/openmw/mwgui/merchantrepair.cpp @@ -32,7 +32,7 @@ MerchantRepair::MerchantRepair() mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &MerchantRepair::onOkButtonClick); } -void MerchantRepair::startRepair(const MWWorld::Ptr &actor) +void MerchantRepair::setPtr(const MWWorld::Ptr &actor) { mActor = actor; @@ -111,18 +111,13 @@ void MerchantRepair::onMouseWheel(MyGUI::Widget* _sender, int _rel) mList->setViewOffset(MyGUI::IntPoint(0, static_cast(mList->getViewOffset().top + _rel*0.3f))); } -void MerchantRepair::open() +void MerchantRepair::onOpen() { center(); // Reset scrollbars mList->setViewOffset(MyGUI::IntPoint(0, 0)); } -void MerchantRepair::exit() -{ - MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_MerchantRepair); -} - void MerchantRepair::onRepairButtonClick(MyGUI::Widget *sender) { MWWorld::Ptr player = MWMechanics::getPlayer(); @@ -145,12 +140,12 @@ void MerchantRepair::onRepairButtonClick(MyGUI::Widget *sender) MWMechanics::CreatureStats& actorStats = mActor.getClass().getCreatureStats(mActor); actorStats.setGoldPool(actorStats.getGoldPool() + price); - startRepair(mActor); + setPtr(mActor); } void MerchantRepair::onOkButtonClick(MyGUI::Widget *sender) { - exit(); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_MerchantRepair); } } diff --git a/apps/openmw/mwgui/merchantrepair.hpp b/apps/openmw/mwgui/merchantrepair.hpp index 8dcdfee12..4e4e7164f 100644 --- a/apps/openmw/mwgui/merchantrepair.hpp +++ b/apps/openmw/mwgui/merchantrepair.hpp @@ -12,11 +12,9 @@ class MerchantRepair : public WindowBase public: MerchantRepair(); - virtual void open(); + virtual void onOpen(); - virtual void exit(); - - void startRepair(const MWWorld::Ptr& actor); + void setPtr(const MWWorld::Ptr& actor); private: MyGUI::ScrollView* mList; diff --git a/apps/openmw/mwgui/messagebox.cpp b/apps/openmw/mwgui/messagebox.cpp index bfe173b47..376a28aa7 100644 --- a/apps/openmw/mwgui/messagebox.cpp +++ b/apps/openmw/mwgui/messagebox.cpp @@ -36,8 +36,13 @@ namespace MWGui void MessageBoxManager::clear() { - delete mInterMessageBoxe; - mInterMessageBoxe = NULL; + if (mInterMessageBoxe) + { + mInterMessageBoxe->setVisible(false); + + delete mInterMessageBoxe; + mInterMessageBoxe = NULL; + } std::vector::iterator it(mMessageBoxes.begin()); for (; it != mMessageBoxes.end(); ++it) @@ -77,6 +82,7 @@ namespace MWGui if(mInterMessageBoxe != NULL && mInterMessageBoxe->mMarkedToDelete) { mLastButtonPressed = mInterMessageBoxe->readPressedButton(); + mInterMessageBoxe->setVisible(false); delete mInterMessageBoxe; mInterMessageBoxe = NULL; MWBase::Environment::get().getInputManager()->changeInputMode( @@ -200,7 +206,7 @@ namespace MWGui , mMessageBoxManager(parMessageBoxManager) , mButtonPressed(-1) { - WindowModal::open(); + setVisible(true); int textPadding = 10; // padding between text-widget and main-widget int textButtonPadding = 10; // padding between the text-widget und the button-widget @@ -229,8 +235,7 @@ namespace MWGui int buttonHeight = 0; MyGUI::IntCoord dummyCoord(0, 0, 0, 0); - std::vector::const_iterator it; - for(it = buttons.begin(); it != buttons.end(); ++it) + for(std::vector::const_iterator it = buttons.begin(); it != buttons.end(); ++it) { MyGUI::Button* button = mButtonsWidget->createWidget( MyGUI::WidgetStyle::Child, @@ -290,8 +295,7 @@ namespace MWGui MyGUI::IntSize buttonSize(0, buttonHeight); int left = (mainWidgetSize.width - buttonsWidth)/2; - std::vector::const_iterator button; - for(button = mButtons.begin(); button != mButtons.end(); ++button) + for(std::vector::const_iterator button = mButtons.begin(); button != mButtons.end(); ++button) { buttonCord.left = left; buttonCord.top = messageWidgetCoord.top + textSize.height + textButtonPadding; @@ -320,8 +324,7 @@ namespace MWGui int top = textPadding + textSize.height + textButtonPadding; - std::vector::const_iterator button; - for(button = mButtons.begin(); button != mButtons.end(); ++button) + for(std::vector::const_iterator button = mButtons.begin(); button != mButtons.end(); ++button) { buttonSize.width = (*button)->getTextSize().width + buttonLabelLeftPadding*2; buttonSize.height = (*button)->getTextSize().height + buttonLabelTopPadding*2; @@ -355,25 +358,20 @@ namespace MWGui } // Set key focus to "Ok" button - std::string ok = Misc::StringUtils::lowerCase(MyGUI::LanguageManager::getInstance().replaceTags("#{sOK}")); - std::vector::const_iterator button; - for(button = mButtons.begin(); button != mButtons.end(); ++button) + std::vector keywords { "sOk", "sYes" }; + for(std::vector::const_iterator button = mButtons.begin(); button != mButtons.end(); ++button) { - if(Misc::StringUtils::ciEqual((*button)->getCaption(), ok)) + for (const std::string& keyword : keywords) { - MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(*button); - (*button)->eventKeyButtonPressed += MyGUI::newDelegate(this, &InteractiveMessageBox::onKeyPressed); - break; + if(Misc::StringUtils::ciEqual(MyGUI::LanguageManager::getInstance().replaceTags("#{" + keyword + "}"), (*button)->getCaption())) + { + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(*button); + return; + } } } } - void InteractiveMessageBox::onKeyPressed(MyGUI::Widget *_sender, MyGUI::KeyCode _key, MyGUI::Char _char) - { - if (_key == MyGUI::KeyCode::Return || _key == MyGUI::KeyCode::NumpadEnter || _key == MyGUI::KeyCode::Space) - buttonActivated(_sender); - } - void InteractiveMessageBox::mousePressed (MyGUI::Widget* pressed) { buttonActivated (pressed); diff --git a/apps/openmw/mwgui/messagebox.hpp b/apps/openmw/mwgui/messagebox.hpp index b4121fed3..53b277416 100644 --- a/apps/openmw/mwgui/messagebox.hpp +++ b/apps/openmw/mwgui/messagebox.hpp @@ -77,11 +77,12 @@ namespace MWGui void mousePressed (MyGUI::Widget* _widget); int readPressedButton (); + virtual bool exit() { return false; } + bool mMarkedToDelete; private: void buttonActivated (MyGUI::Widget* _widget); - void onKeyPressed(MyGUI::Widget* _sender, MyGUI::KeyCode _key, MyGUI::Char _char); MessageBoxManager& mMessageBoxManager; MyGUI::EditBox* mMessageWidget; diff --git a/apps/openmw/mwgui/mode.hpp b/apps/openmw/mwgui/mode.hpp index db851e067..c452a1f5f 100644 --- a/apps/openmw/mwgui/mode.hpp +++ b/apps/openmw/mwgui/mode.hpp @@ -23,7 +23,6 @@ namespace MWGui GM_Dialogue, // NPC interaction GM_Barter, GM_Rest, - GM_RestBed, GM_SpellBuying, GM_Travel, GM_SpellCreation, diff --git a/apps/openmw/mwgui/pickpocketitemmodel.cpp b/apps/openmw/mwgui/pickpocketitemmodel.cpp index 238fb5913..ec4b378a0 100644 --- a/apps/openmw/mwgui/pickpocketitemmodel.cpp +++ b/apps/openmw/mwgui/pickpocketitemmodel.cpp @@ -26,6 +26,11 @@ namespace MWGui } } + bool PickpocketItemModel::allowedToUseItems() const + { + return false; + } + ItemStack PickpocketItemModel::getItem (ModelIndex index) { if (index < 0) diff --git a/apps/openmw/mwgui/pickpocketitemmodel.hpp b/apps/openmw/mwgui/pickpocketitemmodel.hpp index 61f0569b5..1b08ec485 100644 --- a/apps/openmw/mwgui/pickpocketitemmodel.hpp +++ b/apps/openmw/mwgui/pickpocketitemmodel.hpp @@ -11,6 +11,8 @@ namespace MWGui { public: PickpocketItemModel (const MWWorld::Ptr& thief, ItemModel* sourceModel, bool hideItems=true); + + virtual bool allowedToUseItems() const; virtual ItemStack getItem (ModelIndex index); virtual size_t getItemCount(); virtual void update(); diff --git a/apps/openmw/mwgui/quickkeysmenu.cpp b/apps/openmw/mwgui/quickkeysmenu.cpp index e912193bf..6da7b0905 100644 --- a/apps/openmw/mwgui/quickkeysmenu.cpp +++ b/apps/openmw/mwgui/quickkeysmenu.cpp @@ -64,11 +64,6 @@ namespace MWGui } } - void QuickKeysMenu::exit() - { - MWBase::Environment::get().getWindowManager()->removeGuiMode (MWGui::GM_QuickKeysMenu); - } - void QuickKeysMenu::clear() { mActivatedIndex = -1; @@ -449,11 +444,6 @@ namespace MWGui center(); } - void QuickKeysMenuAssign::exit() - { - setVisible(false); - } - void QuickKeysMenu::write(ESM::ESMWriter &writer) { writer.startRecord(ESM::REC_KEYS); @@ -585,14 +575,15 @@ namespace MWGui exit(); } - void MagicSelectionDialog::exit() + bool MagicSelectionDialog::exit() { mParent->onAssignMagicCancel(); + return true; } - void MagicSelectionDialog::open () + void MagicSelectionDialog::onOpen () { - WindowModal::open(); + WindowModal::onOpen(); mMagicList->setModel(new SpellModel(MWMechanics::getPlayer())); mMagicList->resetScrollbars(); diff --git a/apps/openmw/mwgui/quickkeysmenu.hpp b/apps/openmw/mwgui/quickkeysmenu.hpp index 64db9043e..0070aa55b 100644 --- a/apps/openmw/mwgui/quickkeysmenu.hpp +++ b/apps/openmw/mwgui/quickkeysmenu.hpp @@ -22,7 +22,7 @@ namespace MWGui QuickKeysMenu(); ~QuickKeysMenu(); - virtual void exit(); + void onResChange(int, int) { center(); } void onItemButtonClicked(MyGUI::Widget* sender); void onMagicButtonClicked(MyGUI::Widget* sender); @@ -77,7 +77,6 @@ namespace MWGui { public: QuickKeysMenuAssign(QuickKeysMenu* parent); - virtual void exit(); private: MyGUI::TextBox* mLabel; @@ -94,8 +93,8 @@ namespace MWGui public: MagicSelectionDialog(QuickKeysMenu* parent); - virtual void open(); - virtual void exit(); + virtual void onOpen(); + virtual bool exit(); private: MyGUI::Button* mCancelButton; diff --git a/apps/openmw/mwgui/race.cpp b/apps/openmw/mwgui/race.cpp index 187aeb408..d9878f495 100644 --- a/apps/openmw/mwgui/race.cpp +++ b/apps/openmw/mwgui/race.cpp @@ -131,9 +131,9 @@ namespace MWGui okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", "")); } - void RaceDialog::open() + void RaceDialog::onOpen() { - WindowModal::open(); + WindowModal::onOpen(); updateRaces(); updateSkills(); @@ -174,6 +174,8 @@ namespace MWGui size_t initialPos = mHeadRotate->getScrollRange()/2+mHeadRotate->getScrollRange()/10; mHeadRotate->setScrollPosition(initialPos); onHeadRotate(mHeadRotate, initialPos); + + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mRaceList); } void RaceDialog::setRaceId(const std::string &raceId) @@ -194,8 +196,10 @@ namespace MWGui updateSpellPowers(); } - void RaceDialog::close() + void RaceDialog::onClose() { + WindowModal::onClose(); + mPreviewImage->setRenderItemTexture(NULL); mPreviewTexture.reset(NULL); diff --git a/apps/openmw/mwgui/race.hpp b/apps/openmw/mwgui/race.hpp index b762745cd..c9e31d42d 100644 --- a/apps/openmw/mwgui/race.hpp +++ b/apps/openmw/mwgui/race.hpp @@ -51,8 +51,10 @@ namespace MWGui void setGender(Gender gender) { mGenderIndex = gender == GM_Male ? 0 : 1; } void setNextButtonShow(bool shown); - virtual void open(); - virtual void close(); + virtual void onOpen(); + virtual void onClose(); + + bool exit() { return false; } // Events typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void; diff --git a/apps/openmw/mwgui/recharge.cpp b/apps/openmw/mwgui/recharge.cpp index 390a1f83c..26a364f72 100644 --- a/apps/openmw/mwgui/recharge.cpp +++ b/apps/openmw/mwgui/recharge.cpp @@ -21,7 +21,6 @@ #include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/actorutil.hpp" -#include "widgets.hpp" #include "itemwidget.hpp" #include "itemchargeview.hpp" #include "sortfilteritemmodel.hpp" @@ -46,11 +45,9 @@ Recharge::Recharge() mBox->setDisplayMode(ItemChargeView::DisplayMode_EnchantmentCharge); mGemIcon->eventMouseButtonClick += MyGUI::newDelegate(this, &Recharge::onSelectItem); - - setVisible(false); } -void Recharge::open() +void Recharge::onOpen() { center(); @@ -62,12 +59,7 @@ void Recharge::open() mBox->resetScrollbars(); } -void Recharge::exit() -{ - MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Recharge); -} - -void Recharge::start (const MWWorld::Ptr &item) +void Recharge::setPtr (const MWWorld::Ptr &item) { mGemIcon->setItem(item); mGemIcon->setUserString("ToolTipType", "ItemPtr"); @@ -107,7 +99,7 @@ void Recharge::updateView() void Recharge::onCancel(MyGUI::Widget *sender) { - exit(); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Recharge); } void Recharge::onSelectItem(MyGUI::Widget *sender) diff --git a/apps/openmw/mwgui/recharge.hpp b/apps/openmw/mwgui/recharge.hpp index cc3db14df..f4602ce30 100644 --- a/apps/openmw/mwgui/recharge.hpp +++ b/apps/openmw/mwgui/recharge.hpp @@ -22,11 +22,9 @@ class Recharge : public WindowBase public: Recharge(); - virtual void open(); + virtual void onOpen(); - virtual void exit(); - - void start (const MWWorld::Ptr& gem); + void setPtr (const MWWorld::Ptr& gem); protected: ItemChargeView* mBox; diff --git a/apps/openmw/mwgui/referenceinterface.cpp b/apps/openmw/mwgui/referenceinterface.cpp index 9aaa98f19..6213d9e25 100644 --- a/apps/openmw/mwgui/referenceinterface.cpp +++ b/apps/openmw/mwgui/referenceinterface.cpp @@ -1,14 +1,8 @@ #include "referenceinterface.hpp" -#include "../mwbase/world.hpp" -#include "../mwbase/environment.hpp" - -#include "../mwmechanics/actorutil.hpp" - namespace MWGui { ReferenceInterface::ReferenceInterface() - : mCurrentPlayerCell(NULL) { } @@ -18,8 +12,6 @@ namespace MWGui void ReferenceInterface::checkReferenceAvailable() { - MWWorld::CellStore* playerCell = MWMechanics::getPlayer().getCell(); - // check if count of the reference has become 0 if (!mPtr.isEmpty() && mPtr.getRefData().getCount() == 0) { @@ -29,7 +21,5 @@ namespace MWGui onReferenceUnavailable(); } } - - mCurrentPlayerCell = playerCell; } } diff --git a/apps/openmw/mwgui/referenceinterface.hpp b/apps/openmw/mwgui/referenceinterface.hpp index 0ba180de8..6428a5b54 100644 --- a/apps/openmw/mwgui/referenceinterface.hpp +++ b/apps/openmw/mwgui/referenceinterface.hpp @@ -17,15 +17,12 @@ namespace MWGui void checkReferenceAvailable(); ///< closes the window, if the MW-reference has become unavailable - virtual void resetReference() { mPtr = MWWorld::Ptr(); mCurrentPlayerCell = NULL; } + virtual void resetReference() { mPtr = MWWorld::Ptr(); } protected: virtual void onReferenceUnavailable() = 0; ///< called when reference has become unavailable MWWorld::Ptr mPtr; - - private: - MWWorld::CellStore* mCurrentPlayerCell; }; } diff --git a/apps/openmw/mwgui/repair.cpp b/apps/openmw/mwgui/repair.cpp index 4d6441450..11a8aece2 100644 --- a/apps/openmw/mwgui/repair.cpp +++ b/apps/openmw/mwgui/repair.cpp @@ -17,8 +17,6 @@ #include "../mwworld/containerstore.hpp" #include "../mwworld/class.hpp" -#include "widgets.hpp" - #include "itemwidget.hpp" #include "itemchargeview.hpp" #include "sortfilteritemmodel.hpp" @@ -46,7 +44,7 @@ Repair::Repair() mToolIcon->eventMouseButtonClick += MyGUI::newDelegate(this, &Repair::onSelectItem); } -void Repair::open() +void Repair::onOpen() { center(); @@ -58,12 +56,7 @@ void Repair::open() mRepairBox->resetScrollbars(); } -void Repair::exit() -{ - MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Repair); -} - -void Repair::startRepairItem(const MWWorld::Ptr &item) +void Repair::setPtr(const MWWorld::Ptr &item) { MWBase::Environment::get().getWindowManager()->playSound("Item Repair Up"); @@ -145,7 +138,7 @@ void Repair::onItemCancel() void Repair::onCancel(MyGUI::Widget* /*sender*/) { - exit(); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Repair); } void Repair::onRepairItem(MyGUI::Widget* /*sender*/, const MWWorld::Ptr& ptr) diff --git a/apps/openmw/mwgui/repair.hpp b/apps/openmw/mwgui/repair.hpp index c50ba861a..f31625095 100644 --- a/apps/openmw/mwgui/repair.hpp +++ b/apps/openmw/mwgui/repair.hpp @@ -19,11 +19,9 @@ class Repair : public WindowBase public: Repair(); - virtual void open(); + virtual void onOpen(); - virtual void exit(); - - void startRepairItem (const MWWorld::Ptr& item); + void setPtr (const MWWorld::Ptr& item); protected: ItemChargeView* mRepairBox; diff --git a/apps/openmw/mwgui/review.cpp b/apps/openmw/mwgui/review.cpp index bf18e7355..61a7a2ac0 100644 --- a/apps/openmw/mwgui/review.cpp +++ b/apps/openmw/mwgui/review.cpp @@ -101,9 +101,9 @@ namespace MWGui okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onOkClicked); } - void ReviewDialog::open() + void ReviewDialog::onOpen() { - WindowModal::open(); + WindowModal::onOpen(); mUpdateSkillArea = true; } diff --git a/apps/openmw/mwgui/review.hpp b/apps/openmw/mwgui/review.hpp index f21f2fa9d..8e9ec0ec7 100644 --- a/apps/openmw/mwgui/review.hpp +++ b/apps/openmw/mwgui/review.hpp @@ -31,6 +31,8 @@ namespace MWGui ReviewDialog(); + bool exit() { return false; } + void setPlayerName(const std::string &name); void setRace(const std::string &raceId); void setClass(const ESM::Class& class_); @@ -45,7 +47,7 @@ namespace MWGui void configureSkills(const SkillList& major, const SkillList& minor); void setSkillValue(ESM::Skill::SkillEnum skillId, const MWMechanics::SkillValue& value); - virtual void open(); + virtual void onOpen(); void onFrame(float duration); diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index 84d4ca959..6a290bef1 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -29,7 +29,6 @@ #include "../mwstate/character.hpp" #include "confirmationdialog.hpp" -#include "widgets.hpp" namespace MWGui { @@ -51,13 +50,17 @@ namespace MWGui mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SaveGameDialog::onOkButtonClicked); mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SaveGameDialog::onCancelButtonClicked); mDeleteButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SaveGameDialog::onDeleteButtonClicked); - mCharacterSelection->eventComboAccept += MyGUI::newDelegate(this, &SaveGameDialog::onCharacterSelected); + mCharacterSelection->eventComboChangePosition += MyGUI::newDelegate(this, &SaveGameDialog::onCharacterSelected); + mCharacterSelection->eventComboAccept += MyGUI::newDelegate(this, &SaveGameDialog::onCharacterAccept); mSaveList->eventListChangePosition += MyGUI::newDelegate(this, &SaveGameDialog::onSlotSelected); mSaveList->eventListMouseItemActivate += MyGUI::newDelegate(this, &SaveGameDialog::onSlotMouseClick); mSaveList->eventListSelectAccept += MyGUI::newDelegate(this, &SaveGameDialog::onSlotActivated); mSaveList->eventKeyButtonPressed += MyGUI::newDelegate(this, &SaveGameDialog::onKeyButtonPressed); mSaveNameEdit->eventEditSelectAccept += MyGUI::newDelegate(this, &SaveGameDialog::onEditSelectAccept); mSaveNameEdit->eventEditTextChange += MyGUI::newDelegate(this, &SaveGameDialog::onSaveNameChanged); + + // To avoid accidental deletions + mDeleteButton->setNeedKeyFocus(false); } void SaveGameDialog::onSlotActivated(MyGUI::ListBox *sender, size_t pos) @@ -106,6 +109,11 @@ namespace MWGui } } + void SaveGameDialog::onDeleteSlotCancel() + { + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mSaveList); + } + void SaveGameDialog::onSaveNameChanged(MyGUI::EditBox *sender) { // This might have previously been a save slot from the list. If so, that is no longer the case @@ -118,13 +126,15 @@ namespace MWGui accept(); } - void SaveGameDialog::open() + void SaveGameDialog::onOpen() { - WindowModal::open(); + WindowModal::onOpen(); mSaveNameEdit->setCaption (""); if (mSaving) MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mSaveNameEdit); + else + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mSaveList); center(); @@ -191,11 +201,6 @@ namespace MWGui } - void SaveGameDialog::exit() - { - setVisible(false); - } - void SaveGameDialog::setLoadOrSave(bool load) { mSaving = !load; @@ -217,7 +222,7 @@ namespace MWGui void SaveGameDialog::onCancelButtonClicked(MyGUI::Widget *sender) { - exit(); + setVisible(false); } void SaveGameDialog::onDeleteButtonClicked(MyGUI::Widget *sender) @@ -231,6 +236,11 @@ namespace MWGui accept(true); } + void SaveGameDialog::onConfirmationCancel() + { + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mSaveList); + } + void SaveGameDialog::accept(bool reallySure) { // Remove for MyGUI 3.2.2 @@ -246,6 +256,7 @@ namespace MWGui dialog->eventOkClicked.clear(); dialog->eventOkClicked += MyGUI::newDelegate(this, &SaveGameDialog::onConfirmationGiven); dialog->eventCancelClicked.clear(); + dialog->eventCancelClicked += MyGUI::newDelegate(this, &SaveGameDialog::onConfirmationCancel); return; } if (mSaveNameEdit->getCaption().empty()) @@ -266,6 +277,7 @@ namespace MWGui dialog->eventOkClicked.clear(); dialog->eventOkClicked += MyGUI::newDelegate(this, &SaveGameDialog::onConfirmationGiven); dialog->eventCancelClicked.clear(); + dialog->eventCancelClicked += MyGUI::newDelegate(this, &SaveGameDialog::onConfirmationCancel); return; } } @@ -313,6 +325,12 @@ namespace MWGui fillSaveList(); } + void SaveGameDialog::onCharacterAccept(MyGUI::ComboBox* sender, size_t pos) + { + // Give key focus to save list so we can confirm the selection with Enter + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mSaveList); + } + void SaveGameDialog::fillSaveList() { mSaveList->removeAllItems(); @@ -327,8 +345,6 @@ namespace MWGui { mSaveList->setIndexSelected(0); onSlotSelected(mSaveList, 0); - // Give key focus to save list so we can confirm the selection with Enter - MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mSaveList); } else onSlotSelected(mSaveList, MyGUI::ITEM_NONE); diff --git a/apps/openmw/mwgui/savegamedialog.hpp b/apps/openmw/mwgui/savegamedialog.hpp index be0e47cab..0a87b6600 100644 --- a/apps/openmw/mwgui/savegamedialog.hpp +++ b/apps/openmw/mwgui/savegamedialog.hpp @@ -17,9 +17,7 @@ namespace MWGui public: SaveGameDialog(); - virtual void open(); - - virtual void exit(); + virtual void onOpen(); void setLoadOrSave(bool load); @@ -31,6 +29,7 @@ namespace MWGui void onOkButtonClicked (MyGUI::Widget* sender); void onDeleteButtonClicked (MyGUI::Widget* sender); void onCharacterSelected (MyGUI::ComboBox* sender, size_t pos); + void onCharacterAccept(MyGUI::ComboBox* sender, size_t pos); // Slot selected (mouse click or arrow keys) void onSlotSelected (MyGUI::ListBox* sender, size_t pos); // Slot activated (double click or enter key) @@ -39,10 +38,12 @@ namespace MWGui void onSlotMouseClick(MyGUI::ListBox* sender, size_t pos); void onDeleteSlotConfirmed(); + void onDeleteSlotCancel(); void onEditSelectAccept (MyGUI::EditBox* sender); void onSaveNameChanged (MyGUI::EditBox* sender); void onConfirmationGiven(); + void onConfirmationCancel(); void accept(bool reallySure=false); diff --git a/apps/openmw/mwgui/screenfader.cpp b/apps/openmw/mwgui/screenfader.cpp index be0346e88..9a9a1ae01 100644 --- a/apps/openmw/mwgui/screenfader.cpp +++ b/apps/openmw/mwgui/screenfader.cpp @@ -2,16 +2,18 @@ #include #include +#include namespace MWGui { - FadeOp::FadeOp(ScreenFader * fader, float time, float targetAlpha) + FadeOp::FadeOp(ScreenFader * fader, float time, float targetAlpha, float delay) : mFader(fader), - mRemainingTime(time), + mRemainingTime(time+delay), mTargetTime(time), mTargetAlpha(targetAlpha), mStartAlpha(0.f), + mDelay(delay), mRunning(false) { } @@ -26,7 +28,7 @@ namespace MWGui if (mRunning) return; - mRemainingTime = mTargetTime; + mRemainingTime = mTargetTime + mDelay; mStartAlpha = mFader->getCurrentAlpha(); mRunning = true; } @@ -42,6 +44,12 @@ namespace MWGui return; } + if (mRemainingTime > mTargetTime) + { + mRemainingTime -= dt; + return; + } + float currentAlpha = mFader->getCurrentAlpha(); if (mStartAlpha > mTargetAlpha) { @@ -73,8 +81,9 @@ namespace MWGui , mFactor(1.f) , mRepeat(false) { + MyGUI::Gui::getInstance().eventFrameStart += MyGUI::newDelegate(this, &ScreenFader::onFrameStart); + mMainWidget->setSize(MyGUI::RenderManager::getInstance().getViewSize()); - setVisible(false); MyGUI::ImageBox* imageBox = mMainWidget->castType(false); if (imageBox) @@ -88,7 +97,12 @@ namespace MWGui } } - void ScreenFader::update(float dt) + ScreenFader::~ScreenFader() + { + MyGUI::Gui::getInstance().eventFrameStart -= MyGUI::newDelegate(this, &ScreenFader::onFrameStart); + } + + void ScreenFader::onFrameStart(float dt) { if (!mQueue.empty()) { @@ -104,19 +118,25 @@ namespace MWGui mMainWidget->setAlpha(1.f-((1.f-mCurrentAlpha) * mFactor)); } - void ScreenFader::fadeIn(float time) + void ScreenFader::fadeIn(float time, float delay) + { + queue(time, 1.f, delay); + } + + void ScreenFader::fadeOut(const float time, float delay) { - queue(time, 1.f); + queue(time, 0.f, delay); } - void ScreenFader::fadeOut(const float time) + void ScreenFader::fadeTo(const int percent, const float time, float delay) { - queue(time, 0.f); + queue(time, percent/100.f, delay); } - void ScreenFader::fadeTo(const int percent, const float time) + void ScreenFader::clear() { - queue(time, percent/100.f); + clearQueue(); + notifyAlphaChanged(0.f); } void ScreenFader::setFactor(float factor) @@ -130,7 +150,7 @@ namespace MWGui mRepeat = repeat; } - void ScreenFader::queue(float time, float targetAlpha) + void ScreenFader::queue(float time, float targetAlpha, float delay) { if (time < 0.f) return; @@ -142,7 +162,7 @@ namespace MWGui return; } - mQueue.push_back(FadeOp::Ptr(new FadeOp(this, time, targetAlpha))); + mQueue.push_back(FadeOp::Ptr(new FadeOp(this, time, targetAlpha, delay))); } bool ScreenFader::isEmpty() diff --git a/apps/openmw/mwgui/screenfader.hpp b/apps/openmw/mwgui/screenfader.hpp index b510e133a..79bea30e5 100644 --- a/apps/openmw/mwgui/screenfader.hpp +++ b/apps/openmw/mwgui/screenfader.hpp @@ -14,7 +14,7 @@ namespace MWGui public: typedef std::shared_ptr Ptr; - FadeOp(ScreenFader * fader, float time, float targetAlpha); + FadeOp(ScreenFader * fader, float time, float targetAlpha, float delay); bool isRunning(); @@ -28,6 +28,7 @@ namespace MWGui float mTargetTime; float mTargetAlpha; float mStartAlpha; + float mDelay; bool mRunning; }; @@ -35,17 +36,20 @@ namespace MWGui { public: ScreenFader(const std::string & texturePath, const std::string& layout = "openmw_screen_fader.layout", const MyGUI::FloatCoord& texCoordOverride = MyGUI::FloatCoord(0,0,1,1)); + ~ScreenFader(); - void update(float dt); + void onFrameStart(float dt); + + void fadeIn(const float time, float delay=0); + void fadeOut(const float time, float delay=0); + void fadeTo(const int percent, const float time, float delay=0); - void fadeIn(const float time); - void fadeOut(const float time); - void fadeTo(const int percent, const float time); + void clear(); void setFactor (float factor); void setRepeat(bool repeat); - void queue(float time, float targetAlpha); + void queue(float time, float targetAlpha, float delay); bool isEmpty(); void clearQueue(); diff --git a/apps/openmw/mwgui/scrollwindow.cpp b/apps/openmw/mwgui/scrollwindow.cpp index a5d4c3324..ea811f5b1 100644 --- a/apps/openmw/mwgui/scrollwindow.cpp +++ b/apps/openmw/mwgui/scrollwindow.cpp @@ -12,6 +12,7 @@ #include "../mwmechanics/actorutil.hpp" #include "../mwworld/actiontake.hpp" +#include "../mwworld/class.hpp" #include "formatting.hpp" @@ -46,16 +47,19 @@ namespace MWGui adjustButton(mCloseButton); adjustButton(mTakeButton); + mCloseButton->eventKeyButtonPressed += MyGUI::newDelegate(this, &ScrollWindow::onKeyButtonPressed); + mTakeButton->eventKeyButtonPressed += MyGUI::newDelegate(this, &ScrollWindow::onKeyButtonPressed); + center(); } - void ScrollWindow::openScroll (MWWorld::Ptr scroll, bool showTakeButton) + void ScrollWindow::setPtr (const MWWorld::Ptr& scroll) { - // no 3d sounds because the object could be in a container. - MWBase::Environment::get().getWindowManager()->playSound("scroll"); - mScroll = scroll; + MWWorld::Ptr player = MWMechanics::getPlayer(); + bool showTakeButton = scroll.getContainerStore() != &player.getClass().getContainerStore(player); + MWWorld::LiveCellRef *ref = mScroll.get(); Formatting::BookFormatter formatter; @@ -65,21 +69,28 @@ namespace MWGui // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden mTextView->setVisibleVScroll(false); if (size.height > mTextView->getSize().height) - mTextView->setCanvasSize(MyGUI::IntSize(410, size.height)); + mTextView->setCanvasSize(mTextView->getWidth(), size.height); else - mTextView->setCanvasSize(410, mTextView->getSize().height); + mTextView->setCanvasSize(mTextView->getWidth(), mTextView->getSize().height); mTextView->setVisibleVScroll(true); mTextView->setViewOffset(MyGUI::IntPoint(0,0)); setTakeButtonShow(showTakeButton); + + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCloseButton); } - void ScrollWindow::exit() + void ScrollWindow::onKeyButtonPressed(MyGUI::Widget *sender, MyGUI::KeyCode key, MyGUI::Char character) { - MWBase::Environment::get().getWindowManager()->playSound("scroll"); - - MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Scroll); + int scroll = 0; + if (key == MyGUI::KeyCode::ArrowUp) + scroll = 40; + else if (key == MyGUI::KeyCode::ArrowDown) + scroll = -40; + + if (scroll != 0) + mTextView->setViewOffset(mTextView->getViewOffset() + MyGUI::IntPoint(0, scroll)); } void ScrollWindow::setTakeButtonShow(bool show) @@ -96,7 +107,7 @@ namespace MWGui void ScrollWindow::onCloseButtonClicked (MyGUI::Widget* _sender) { - exit(); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Scroll); } void ScrollWindow::onTakeButtonClicked (MyGUI::Widget* _sender) @@ -106,6 +117,6 @@ namespace MWGui MWWorld::ActionTake take(mScroll); take.execute (MWMechanics::getPlayer()); - MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Scroll); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Scroll, true); } } diff --git a/apps/openmw/mwgui/scrollwindow.hpp b/apps/openmw/mwgui/scrollwindow.hpp index 961f1b675..adc0bd93e 100644 --- a/apps/openmw/mwgui/scrollwindow.hpp +++ b/apps/openmw/mwgui/scrollwindow.hpp @@ -17,14 +17,16 @@ namespace MWGui public: ScrollWindow (); - void openScroll (MWWorld::Ptr scroll, bool showTakeButton); - virtual void exit(); + void setPtr (const MWWorld::Ptr& scroll); void setInventoryAllowed(bool allowed); + void onResChange(int, int) { center(); } + protected: void onCloseButtonClicked (MyGUI::Widget* _sender); void onTakeButtonClicked (MyGUI::Widget* _sender); void setTakeButtonShow(bool show); + void onKeyButtonPressed(MyGUI::Widget* sender, MyGUI::KeyCode key, MyGUI::Char character); private: Gui::ImageButton* mCloseButton; diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index f46199c80..9bf6e4385 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -258,7 +258,7 @@ namespace MWGui void SettingsWindow::onOkButtonClicked(MyGUI::Widget* _sender) { - exit(); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Settings); } void SettingsWindow::onResolutionSelected(MyGUI::ListBox* _sender, size_t index) @@ -353,24 +353,31 @@ namespace MWGui } bool supported = false; + int fallbackX = 0, fallbackY = 0; for (unsigned int i=0; igetItemCount(); ++i) { std::string resStr = mResolutionList->getItemNameAt(i); int resX, resY; parseResolution (resX, resY, resStr); + if (i == 0) + { + fallbackX = resX; + fallbackY = resY; + } + if (resX == Settings::Manager::getInt("resolution x", "Video") && resY == Settings::Manager::getInt("resolution y", "Video")) supported = true; } - if (!supported) + if (!supported && mResolutionList->getItemCount()) { - std::string msg = "This resolution is not supported in Fullscreen mode. Please select a resolution from the list."; - MWBase::Environment::get().getWindowManager()-> - messageBox(msg); - _sender->castType()->setCaption(off); - return; + if (fallbackX != 0 && fallbackY != 0) + { + Settings::Manager::setInt("resolution x", "Video", fallbackX); + Settings::Manager::setInt("resolution y", "Video", fallbackY); + } } mWindowBorderButton->setEnabled(!newState); @@ -553,17 +560,12 @@ namespace MWGui updateControlsBox (); } - void SettingsWindow::open() + void SettingsWindow::onOpen() { updateControlsBox (); resetScrollbars(); } - void SettingsWindow::exit() - { - MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Settings); - } - void SettingsWindow::onWindowResize(MyGUI::Window *_sender) { layoutControlsBox(); diff --git a/apps/openmw/mwgui/settingswindow.hpp b/apps/openmw/mwgui/settingswindow.hpp index 2ac06dcaa..c6e48819c 100644 --- a/apps/openmw/mwgui/settingswindow.hpp +++ b/apps/openmw/mwgui/settingswindow.hpp @@ -15,12 +15,12 @@ namespace MWGui public: SettingsWindow(); - virtual void open(); - - virtual void exit(); + virtual void onOpen(); void updateControlsBox(); + void onResChange(int, int) { center(); } + protected: MyGUI::TabControl* mSettingsTab; MyGUI::Button* mOkButton; diff --git a/apps/openmw/mwgui/sortfilteritemmodel.cpp b/apps/openmw/mwgui/sortfilteritemmodel.cpp index 45aa261df..e294ebe07 100644 --- a/apps/openmw/mwgui/sortfilteritemmodel.cpp +++ b/apps/openmw/mwgui/sortfilteritemmodel.cpp @@ -159,6 +159,11 @@ namespace MWGui mSourceModel = sourceModel; } + bool SortFilterItemModel::allowedToUseItems() const + { + return mSourceModel->allowedToUseItems(); + } + void SortFilterItemModel::addDragItem (const MWWorld::Ptr& dragItem, size_t count) { mDragItems.push_back(std::make_pair(dragItem, count)); diff --git a/apps/openmw/mwgui/sortfilteritemmodel.hpp b/apps/openmw/mwgui/sortfilteritemmodel.hpp index 4731cbb8a..6ddb019b0 100644 --- a/apps/openmw/mwgui/sortfilteritemmodel.hpp +++ b/apps/openmw/mwgui/sortfilteritemmodel.hpp @@ -15,6 +15,7 @@ namespace MWGui bool filterAccepts (const ItemStack& item); + bool allowedToUseItems() const; virtual ItemStack getItem (ModelIndex index); virtual size_t getItemCount(); diff --git a/apps/openmw/mwgui/soulgemdialog.cpp b/apps/openmw/mwgui/soulgemdialog.cpp index 0232eb7b3..104c81eab 100644 --- a/apps/openmw/mwgui/soulgemdialog.cpp +++ b/apps/openmw/mwgui/soulgemdialog.cpp @@ -22,13 +22,11 @@ namespace MWGui { if (button == 0) { - MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Recharge); - MWBase::Environment::get().getWindowManager()->startRecharge(mSoulgem); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Recharge, mSoulgem); } else { - MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Enchanting); - MWBase::Environment::get().getWindowManager()->startSelfEnchanting(mSoulgem); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Enchanting, mSoulgem); } } diff --git a/apps/openmw/mwgui/spellbuyingwindow.cpp b/apps/openmw/mwgui/spellbuyingwindow.cpp index cf991c85f..b238b9081 100644 --- a/apps/openmw/mwgui/spellbuyingwindow.cpp +++ b/apps/openmw/mwgui/spellbuyingwindow.cpp @@ -42,11 +42,6 @@ namespace MWGui mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellBuyingWindow::onCancelButtonClicked); } - void SpellBuyingWindow::exit() - { - MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_SpellBuying); - } - bool SpellBuyingWindow::sortSpells (const ESM::Spell* left, const ESM::Spell* right) { std::string leftName = Misc::StringUtils::lowerCase(left->mName); @@ -99,7 +94,12 @@ namespace MWGui mSpellsWidgetMap.clear(); } - void SpellBuyingWindow::startSpellBuying(const MWWorld::Ptr& actor, int startOffset) + void SpellBuyingWindow::setPtr(const MWWorld::Ptr &actor) + { + setPtr(actor, 0); + } + + void SpellBuyingWindow::setPtr(const MWWorld::Ptr& actor, int startOffset) { center(); mPtr = actor; @@ -183,14 +183,14 @@ namespace MWGui MWMechanics::CreatureStats& npcStats = mPtr.getClass().getCreatureStats(mPtr); npcStats.setGoldPool(npcStats.getGoldPool() + price); - startSpellBuying(mPtr, mSpellsView->getViewOffset().top); + setPtr(mPtr, mSpellsView->getViewOffset().top); MWBase::Environment::get().getWindowManager()->playSound("Item Gold Up"); } void SpellBuyingWindow::onCancelButtonClicked(MyGUI::Widget* _sender) { - exit(); + MWBase::Environment::get().getWindowManager()->removeGuiMode (MWGui::GM_SpellBuying); } void SpellBuyingWindow::updateLabels() @@ -209,7 +209,7 @@ namespace MWGui { // remove both Spells and Dialogue (since you always trade with the NPC/creature that you have previously talked to) MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_SpellBuying); - MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Dialogue); + MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode(); } void SpellBuyingWindow::onMouseWheel(MyGUI::Widget* _sender, int _rel) diff --git a/apps/openmw/mwgui/spellbuyingwindow.hpp b/apps/openmw/mwgui/spellbuyingwindow.hpp index 37210819f..649aab3f2 100644 --- a/apps/openmw/mwgui/spellbuyingwindow.hpp +++ b/apps/openmw/mwgui/spellbuyingwindow.hpp @@ -25,9 +25,13 @@ namespace MWGui public: SpellBuyingWindow(); - void startSpellBuying(const MWWorld::Ptr& actor, int startOffset); + void setPtr(const MWWorld::Ptr& actor); + void setPtr(const MWWorld::Ptr& actor, int startOffset); - virtual void exit(); + void onFrame(float dt) { checkReferenceAvailable(); } + void clear() { resetReference(); } + + void onResChange(int, int) { center(); } protected: MyGUI::Button* mCancelButton; diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index 8e3d55cb6..14fc6f09e 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -107,19 +107,19 @@ namespace MWGui mConstantEffect = constant; } - void EditEffectDialog::open() + void EditEffectDialog::onOpen() { - WindowModal::open(); + WindowModal::onOpen(); center(); } - void EditEffectDialog::exit() + bool EditEffectDialog::exit() { - setVisible(false); if(mEditing) eventEffectModified(mOldEffect); else eventEffectRemoved(mEffect); + return true; } void EditEffectDialog::newEffect (const ESM::MagicEffect *effect) @@ -286,6 +286,7 @@ namespace MWGui void EditEffectDialog::onCancelButtonClicked (MyGUI::Widget* sender) { + setVisible(false); exit(); } @@ -359,11 +360,12 @@ namespace MWGui mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellCreationDialog::onCancelButtonClicked); mBuyButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellCreationDialog::onBuyButtonClicked); + mNameEdit->eventEditSelectAccept += MyGUI::newDelegate(this, &SpellCreationDialog::onAccept); setWidgets(mAvailableEffectsList, mUsedEffectsView); } - void SpellCreationDialog::startSpellMaking (MWWorld::Ptr actor) + void SpellCreationDialog::setPtr (const MWWorld::Ptr& actor) { mPtr = actor; mNameEdit->setCaption(""); @@ -373,7 +375,7 @@ namespace MWGui void SpellCreationDialog::onCancelButtonClicked (MyGUI::Widget* sender) { - exit(); + MWBase::Environment::get().getWindowManager()->removeGuiMode (MWGui::GM_SpellCreation); } void SpellCreationDialog::onBuyButtonClicked (MyGUI::Widget* sender) @@ -440,14 +442,15 @@ namespace MWGui MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_SpellCreation); } - void SpellCreationDialog::open() + void SpellCreationDialog::onAccept(MyGUI::EditBox *sender) { - center(); + onBuyButtonClicked(sender); } - void SpellCreationDialog::exit() + void SpellCreationDialog::onOpen() { - MWBase::Environment::get().getWindowManager()->removeGuiMode (MWGui::GM_SpellCreation); + center(); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mNameEdit); } void SpellCreationDialog::onReferenceUnavailable () diff --git a/apps/openmw/mwgui/spellcreationdialog.hpp b/apps/openmw/mwgui/spellcreationdialog.hpp index 6cdf74d10..ec90fa3ce 100644 --- a/apps/openmw/mwgui/spellcreationdialog.hpp +++ b/apps/openmw/mwgui/spellcreationdialog.hpp @@ -23,8 +23,8 @@ namespace MWGui public: EditEffectDialog(); - virtual void open(); - virtual void exit(); + virtual void onOpen(); + virtual bool exit(); void setConstantEffect(bool constant); @@ -150,16 +150,19 @@ namespace MWGui public: SpellCreationDialog(); - virtual void open(); - virtual void exit(); + virtual void onOpen(); + void clear() { resetReference(); } - void startSpellMaking(MWWorld::Ptr actor); + void onFrame(float dt) { checkReferenceAvailable(); } + + void setPtr(const MWWorld::Ptr& actor); protected: virtual void onReferenceUnavailable (); void onCancelButtonClicked (MyGUI::Widget* sender); void onBuyButtonClicked (MyGUI::Widget* sender); + void onAccept(MyGUI::EditBox* sender); virtual void notifyEffectsChanged (); diff --git a/apps/openmw/mwgui/spellview.cpp b/apps/openmw/mwgui/spellview.cpp index ebda8873c..4268b33a0 100644 --- a/apps/openmw/mwgui/spellview.cpp +++ b/apps/openmw/mwgui/spellview.cpp @@ -106,6 +106,7 @@ namespace MWGui Gui::SharedStateButton* t = mScrollView->createWidget(skin, MyGUI::IntCoord(0, 0, 0, spellHeight), MyGUI::Align::Left | MyGUI::Align::Top); + t->setNeedKeyFocus(true); t->setCaption(spell.mName); t->setTextAlign(MyGUI::Align::Left); adjustSpellWidget(spell, i, t); diff --git a/apps/openmw/mwgui/spellwindow.cpp b/apps/openmw/mwgui/spellwindow.cpp index 0ae16fffa..1f9e65624 100644 --- a/apps/openmw/mwgui/spellwindow.cpp +++ b/apps/openmw/mwgui/spellwindow.cpp @@ -73,22 +73,19 @@ namespace MWGui MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Magic); } - void SpellWindow::open() + void SpellWindow::onOpen() { updateSpells(); } void SpellWindow::onFrame(float dt) - { - if (mMainWidget->getVisible()) + { + NoDrop::onFrame(dt); + mUpdateTimer += dt; + if (0.5f < mUpdateTimer) { - NoDrop::onFrame(dt); - mUpdateTimer += dt; - if (0.5f < mUpdateTimer) - { - mUpdateTimer = 0; - mSpellView->incrementalUpdate(); - } + mUpdateTimer = 0; + mSpellView->incrementalUpdate(); } } diff --git a/apps/openmw/mwgui/spellwindow.hpp b/apps/openmw/mwgui/spellwindow.hpp index dcce10f9d..f8fead9ea 100644 --- a/apps/openmw/mwgui/spellwindow.hpp +++ b/apps/openmw/mwgui/spellwindow.hpp @@ -37,7 +37,7 @@ namespace MWGui virtual void onPinToggled(); virtual void onTitleDoubleClicked(); - virtual void open(); + virtual void onOpen(); SpellView* mSpellView; SpellIcons* mSpellIcons; diff --git a/apps/openmw/mwgui/statswindow.cpp b/apps/openmw/mwgui/statswindow.cpp index 17e51e338..bf505b00f 100644 --- a/apps/openmw/mwgui/statswindow.cpp +++ b/apps/openmw/mwgui/statswindow.cpp @@ -297,9 +297,6 @@ namespace MWGui void StatsWindow::onFrame (float dt) { - if (!mMainWidget->getVisible()) - return; - NoDrop::onFrame(dt); MWWorld::Ptr player = MWMechanics::getPlayer(); diff --git a/apps/openmw/mwgui/statswindow.hpp b/apps/openmw/mwgui/statswindow.hpp index 1fe3cb68d..487953e00 100644 --- a/apps/openmw/mwgui/statswindow.hpp +++ b/apps/openmw/mwgui/statswindow.hpp @@ -37,7 +37,7 @@ namespace MWGui void setBounty (int bounty) { if (bounty != mBounty) mChanged = true; this->mBounty = bounty; } void updateSkillArea(); - virtual void open() { onWindowResize(mMainWidget->castType()); } + virtual void onOpen() { onWindowResize(mMainWidget->castType()); } private: void addSkills(const SkillList &skills, const std::string &titleId, const std::string &titleDefault, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2); diff --git a/apps/openmw/mwgui/textinput.cpp b/apps/openmw/mwgui/textinput.cpp index 958b52dc0..169c9e742 100644 --- a/apps/openmw/mwgui/textinput.cpp +++ b/apps/openmw/mwgui/textinput.cpp @@ -42,9 +42,9 @@ namespace MWGui setText("LabelT", label); } - void TextInputDialog::open() + void TextInputDialog::onOpen() { - WindowModal::open(); + WindowModal::onOpen(); // Make sure the edit box has focus MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mTextEdit); } diff --git a/apps/openmw/mwgui/textinput.hpp b/apps/openmw/mwgui/textinput.hpp index 57dd34dd6..56c6632e1 100644 --- a/apps/openmw/mwgui/textinput.hpp +++ b/apps/openmw/mwgui/textinput.hpp @@ -20,7 +20,9 @@ namespace MWGui void setNextButtonShow(bool shown); void setTextLabel(const std::string &label); - virtual void open(); + virtual void onOpen(); + + bool exit() { return false; } /** Event : Dialog finished, OK button clicked.\n signature : void method()\n diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index 9b89c3957..af6bf4726 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -97,6 +97,8 @@ namespace MWGui if (guiMode) { + if (!MWBase::Environment::get().getWindowManager()->getCursorVisible()) + return; const MyGUI::IntPoint& mousePos = MyGUI::InputManager::getInstance().getMousePosition(); if (MWBase::Environment::get().getWindowManager()->getWorldMouseOver() && ((MWBase::Environment::get().getWindowManager()->getMode() == GM_Console) @@ -118,7 +120,7 @@ namespace MWGui if (info.caption.empty()) info.caption=mFocusObject.getCellRef().getRefId(); info.icon=""; - tooltipSize = createToolTip(info, true); + tooltipSize = createToolTip(info, checkOwned()); } else tooltipSize = getToolTipViaPtr(mFocusObject.getRefData().getCount(), true); @@ -184,22 +186,24 @@ namespace MWGui ToolTipInfo info; info.text = data.caption; info.notes = data.notes; - tooltipSize = createToolTip(info, false); + tooltipSize = createToolTip(info); } else if (type == "ItemPtr") { mFocusObject = *focus->getUserData(); - tooltipSize = getToolTipViaPtr(mFocusObject.getRefData().getCount(), false); + bool isAllowedToUse = checkOwned(); + tooltipSize = getToolTipViaPtr(mFocusObject.getRefData().getCount(), false, !isAllowedToUse); } else if (type == "ItemModelIndex") { std::pair pair = *focus->getUserData >(); mFocusObject = pair.second->getItem(pair.first).mBase; - tooltipSize = getToolTipViaPtr(pair.second->getItem(pair.first).mCount, false); + bool isAllowedToUse = pair.second->allowedToUseItems(); + tooltipSize = getToolTipViaPtr(pair.second->getItem(pair.first).mCount, false, !isAllowedToUse); } else if (type == "ToolTipInfo") { - tooltipSize = createToolTip(*focus->getUserData(), false); + tooltipSize = createToolTip(*focus->getUserData()); } else if (type == "AvatarItemSelection") { @@ -242,7 +246,7 @@ namespace MWGui info.text = "#{sSchool}: " + sSchoolNames[school]; } info.effects = effects; - tooltipSize = createToolTip(info, false); + tooltipSize = createToolTip(info); } else if (type == "Layout") { @@ -296,7 +300,7 @@ namespace MWGui { if (!mFocusObject.isEmpty()) { - MyGUI::IntSize tooltipSize = getToolTipViaPtr(mFocusObject.getRefData().getCount()); + MyGUI::IntSize tooltipSize = getToolTipViaPtr(mFocusObject.getRefData().getCount(), true, checkOwned()); setCoord(viewSize.width/2 - tooltipSize.width/2, std::max(0, int(mFocusToolTipY*viewSize.height - tooltipSize.height)), @@ -323,14 +327,14 @@ namespace MWGui } } - void ToolTips::setFocusObject(const MWWorld::ConstPtr& focus) + void ToolTips::setFocusObject(const MWWorld::Ptr& focus) { mFocusObject = focus; update(mFrameDuration); } - MyGUI::IntSize ToolTips::getToolTipViaPtr (int count, bool image) + MyGUI::IntSize ToolTips::getToolTipViaPtr (int count, bool image, bool isOwned) { // this the maximum width of the tooltip before it starts word-wrapping setCoord(0, 0, 300, 300); @@ -349,7 +353,7 @@ namespace MWGui ToolTipInfo info = object.getToolTipInfo(mFocusObject, count); if (!image) info.icon = ""; - tooltipSize = createToolTip(info, true); + tooltipSize = createToolTip(info, isOwned); } return tooltipSize; @@ -357,27 +361,21 @@ namespace MWGui bool ToolTips::checkOwned() { - if(!mFocusObject.isEmpty()) - { - MWWorld::Ptr ptr = MWMechanics::getPlayer(); - MWWorld::Ptr victim; - - MWBase::MechanicsManager* mm = MWBase::Environment::get().getMechanicsManager(); - bool allowed = mm->isAllowedToUse(ptr, mFocusObject, victim); - - return !allowed; - } - else - { + if(mFocusObject.isEmpty()) return false; - } + + MWWorld::Ptr ptr = MWMechanics::getPlayer(); + MWWorld::Ptr victim; + + MWBase::MechanicsManager* mm = MWBase::Environment::get().getMechanicsManager(); + return !mm->isAllowedToUse(ptr, mFocusObject, victim); } - MyGUI::IntSize ToolTips::createToolTip(const MWGui::ToolTipInfo& info, bool isFocusObject) + MyGUI::IntSize ToolTips::createToolTip(const MWGui::ToolTipInfo& info, bool isOwned) { mDynamicToolTipBox->setVisible(true); - if((mShowOwned == 1 || mShowOwned == 3) && isFocusObject && checkOwned()) + if((mShowOwned == 1 || mShowOwned == 3) && isOwned) 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"); @@ -422,18 +420,20 @@ namespace MWGui std::string realImage = MWBase::Environment::get().getWindowManager()->correctIconPath(image); MyGUI::EditBox* captionWidget = mDynamicToolTipBox->createWidget("NormalText", MyGUI::IntCoord(0, 0, 300, 300), MyGUI::Align::Left | MyGUI::Align::Top, "ToolTipCaption"); - captionWidget->setProperty("Static", "true"); + captionWidget->setEditStatic(true); + captionWidget->setNeedKeyFocus(false); captionWidget->setCaptionWithReplacing(caption); MyGUI::IntSize captionSize = captionWidget->getTextSize(); int captionHeight = std::max(caption != "" ? captionSize.height : 0, imageSize); MyGUI::EditBox* textWidget = mDynamicToolTipBox->createWidget("SandText", MyGUI::IntCoord(0, captionHeight+imageCaptionVPadding, 300, 300-captionHeight-imageCaptionVPadding), MyGUI::Align::Stretch, "ToolTipText"); - textWidget->setProperty("Static", "true"); - textWidget->setProperty("MultiLine", "true"); - textWidget->setProperty("WordWrap", info.wordWrap ? "true" : "false"); + textWidget->setEditStatic(true); + textWidget->setEditMultiLine(true); + textWidget->setEditWordWrap(info.wordWrap); textWidget->setCaptionWithReplacing(text); textWidget->setTextAlign(MyGUI::Align::HCenter | MyGUI::Align::Top); + textWidget->setNeedKeyFocus(false); MyGUI::IntSize textSize = textWidget->getTextSize(); captionSize += MyGUI::IntSize(imageSize, 0); // adjust for image diff --git a/apps/openmw/mwgui/tooltips.hpp b/apps/openmw/mwgui/tooltips.hpp index bb2affc63..1ef473b5a 100644 --- a/apps/openmw/mwgui/tooltips.hpp +++ b/apps/openmw/mwgui/tooltips.hpp @@ -59,7 +59,7 @@ namespace MWGui void setDelay(float delay); - void setFocusObject(const MWWorld::ConstPtr& focus); + void setFocusObject(const MWWorld::Ptr& focus); void setFocusObjectScreenCoords(float min_x, float min_y, float max_x, float max_y); ///< set the screen-space position of the tooltip for focused object @@ -96,12 +96,12 @@ namespace MWGui private: MyGUI::Widget* mDynamicToolTipBox; - MWWorld::ConstPtr mFocusObject; + MWWorld::Ptr mFocusObject; - MyGUI::IntSize getToolTipViaPtr (int count, bool image=true); + MyGUI::IntSize getToolTipViaPtr (int count, bool image = true, bool isOwned = false); ///< @return requested tooltip size - MyGUI::IntSize createToolTip(const ToolTipInfo& info, bool isFocusObject); + MyGUI::IntSize createToolTip(const ToolTipInfo& info, bool isOwned = false); ///< @return requested tooltip size /// @param isFocusObject Is the object this tooltips originates from mFocusObject? diff --git a/apps/openmw/mwgui/tradeitemmodel.cpp b/apps/openmw/mwgui/tradeitemmodel.cpp index ae3feb61c..a26294a0e 100644 --- a/apps/openmw/mwgui/tradeitemmodel.cpp +++ b/apps/openmw/mwgui/tradeitemmodel.cpp @@ -15,6 +15,11 @@ namespace MWGui mSourceModel = sourceModel; } + bool TradeItemModel::allowedToUseItems() const + { + return true; + } + ItemStack TradeItemModel::getItem (ModelIndex index) { if (index < 0) diff --git a/apps/openmw/mwgui/tradeitemmodel.hpp b/apps/openmw/mwgui/tradeitemmodel.hpp index 1bfee9b2a..cdb949c49 100644 --- a/apps/openmw/mwgui/tradeitemmodel.hpp +++ b/apps/openmw/mwgui/tradeitemmodel.hpp @@ -15,6 +15,8 @@ namespace MWGui public: TradeItemModel (ItemModel* sourceModel, const MWWorld::Ptr& merchant); + bool allowedToUseItems() const; + virtual ItemStack getItem (ModelIndex index); virtual size_t getItemCount(); diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index ca0bb48e8..2eeeafe0d 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -27,7 +27,6 @@ #include "containeritemmodel.hpp" #include "tradeitemmodel.hpp" #include "countdialog.hpp" -#include "dialogue.hpp" #include "controllers.hpp" namespace @@ -96,6 +95,7 @@ namespace MWGui mDecreaseButton->eventMouseButtonReleased += MyGUI::newDelegate(this, &TradeWindow::onBalanceButtonReleased); mTotalBalance->eventValueChanged += MyGUI::newDelegate(this, &TradeWindow::onBalanceValueChanged); + mTotalBalance->eventEditSelectAccept += MyGUI::newDelegate(this, &TradeWindow::onAccept); mTotalBalance->setMinValue(INT_MIN+1); // disallow INT_MIN since abs(INT_MIN) is undefined setCoord(400, 0, 400, 300); @@ -115,7 +115,7 @@ namespace MWGui } } - void TradeWindow::startTrade(const MWWorld::Ptr& actor) + void TradeWindow::setPtr(const MWWorld::Ptr& actor) { mPtr = actor; @@ -140,6 +140,13 @@ namespace MWGui setTitle(actor.getClass().getName(actor)); onFilterChanged(mFilterAll); + + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mTotalBalance); + } + + void TradeWindow::onFrame(float dt) + { + checkReferenceAvailable(); } void TradeWindow::onFilterChanged(MyGUI::Widget* _sender) @@ -171,11 +178,11 @@ namespace MWGui return mPtr.getClass().getServices(mPtr); } - void TradeWindow::exit() + bool TradeWindow::exit() { mTradeModel->abort(); MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getTradeModel()->abort(); - MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Barter); + return true; } void TradeWindow::onItemSelected (int index) @@ -315,7 +322,7 @@ namespace MWGui MWBase::Environment::get().getMechanicsManager()->confiscateStolenItemToOwner(player, it->mBase, mPtr, it->mCount); onCancelButtonClicked(mCancelButton); - MWBase::Environment::get().getDialogueManager()->goodbyeSelected(); + MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode(); return; } } @@ -350,8 +357,7 @@ namespace MWGui mPtr.getClass().getCreatureStats(mPtr).getGoldPool() - mCurrentBalance ); } - MWBase::Environment::get().getWindowManager()->getDialogueWindow()->addResponse( - MWBase::Environment::get().getWorld()->getStore().get().find("sBarterDialog5")->getString()); + eventTradeDone(); MWBase::Environment::get().getWindowManager()->playSound("Item Gold Up"); MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Barter); @@ -359,9 +365,15 @@ namespace MWGui restock(); } + void TradeWindow::onAccept(MyGUI::EditBox *sender) + { + onOfferButtonClicked(sender); + } + void TradeWindow::onCancelButtonClicked(MyGUI::Widget* _sender) { exit(); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Barter); } void TradeWindow::onMaxSaleButtonClicked(MyGUI::Widget* _sender) @@ -490,7 +502,7 @@ namespace MWGui { // remove both Trade and Dialogue (since you always trade with the NPC/creature that you have previously talked to) MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Barter); - MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Dialogue); + MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode(); } int TradeWindow::getMerchantGold() diff --git a/apps/openmw/mwgui/tradewindow.hpp b/apps/openmw/mwgui/tradewindow.hpp index 4b03c8d90..514d24022 100644 --- a/apps/openmw/mwgui/tradewindow.hpp +++ b/apps/openmw/mwgui/tradewindow.hpp @@ -27,17 +27,23 @@ namespace MWGui public: TradeWindow(); - void startTrade(const MWWorld::Ptr& actor); + void setPtr(const MWWorld::Ptr& actor); + + void onFrame(float dt); + void clear() { resetReference(); } void borrowItem (int index, size_t count); void returnItem (int index, size_t count); int getMerchantServices(); - virtual void exit(); + virtual bool exit(); virtual void resetReference(); + typedef MyGUI::delegates::CMultiDelegate0 EventHandle_TradeDone; + EventHandle_TradeDone eventTradeDone; + private: ItemView* mItemView; SortFilterItemModel* mSortModel; @@ -81,6 +87,7 @@ namespace MWGui void onFilterChanged(MyGUI::Widget* _sender); void onOfferButtonClicked(MyGUI::Widget* _sender); + void onAccept(MyGUI::EditBox* sender); void onCancelButtonClicked(MyGUI::Widget* _sender); void onMaxSaleButtonClicked(MyGUI::Widget* _sender); void onIncreaseButtonPressed(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id); diff --git a/apps/openmw/mwgui/trainingwindow.cpp b/apps/openmw/mwgui/trainingwindow.cpp index 82218fef1..b6504d223 100644 --- a/apps/openmw/mwgui/trainingwindow.cpp +++ b/apps/openmw/mwgui/trainingwindow.cpp @@ -6,7 +6,6 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/mechanicsmanager.hpp" -#include "../mwbase/dialoguemanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/containerstore.hpp" @@ -40,7 +39,6 @@ namespace MWGui TrainingWindow::TrainingWindow() : WindowBase("openmw_trainingwindow.layout") - , mFadeTimeRemaining(0) , mTimeAdvancer(0.05f) { getWidget(mTrainingOptions, "TrainingOptions"); @@ -51,21 +49,22 @@ namespace MWGui mTimeAdvancer.eventProgressChanged += MyGUI::newDelegate(this, &TrainingWindow::onTrainingProgressChanged); mTimeAdvancer.eventFinished += MyGUI::newDelegate(this, &TrainingWindow::onTrainingFinished); - - mProgressBar.setVisible(false); } - void TrainingWindow::open() + void TrainingWindow::onOpen() { - center(); - } + if (mTimeAdvancer.isRunning()) + { + mProgressBar.setVisible(true); + setVisible(false); + } + else + mProgressBar.setVisible(false); - void TrainingWindow::exit() - { - MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Training); + center(); } - void TrainingWindow::startTraining (MWWorld::Ptr actor) + void TrainingWindow::setPtr (const MWWorld::Ptr& actor) { mPtr = actor; @@ -124,7 +123,7 @@ namespace MWGui void TrainingWindow::onCancelButtonClicked (MyGUI::Widget *sender) { - exit(); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Training); } void TrainingWindow::onTrainingSelected (MyGUI::Widget *sender) @@ -171,21 +170,18 @@ namespace MWGui // add gold to NPC trading gold pool npcStats.setGoldPool(npcStats.getGoldPool() + price); - // go back to game mode - MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Training); - MWBase::Environment::get().getDialogueManager()->goodbyeSelected(); - // advance time MWBase::Environment::get().getMechanicsManager()->rest(false); MWBase::Environment::get().getMechanicsManager()->rest(false); MWBase::Environment::get().getWorld ()->advanceTime (2); + setVisible(false); mProgressBar.setVisible(true); mProgressBar.setProgress(0, 2); mTimeAdvancer.run(2); MWBase::Environment::get().getWindowManager()->fadeScreenOut(0.25); - mFadeTimeRemaining = 0.5; + MWBase::Environment::get().getWindowManager()->fadeScreenIn(0.25, false, 0.25); } void TrainingWindow::onTrainingProgressChanged(int cur, int total) @@ -196,18 +192,15 @@ namespace MWGui void TrainingWindow::onTrainingFinished() { mProgressBar.setVisible(false); + + // go back to game mode + MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Training); + MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode(); } void TrainingWindow::onFrame(float dt) { + checkReferenceAvailable(); mTimeAdvancer.onFrame(dt); - - if (mFadeTimeRemaining <= 0) - return; - - mFadeTimeRemaining -= dt; - - if (mFadeTimeRemaining <= 0) - MWBase::Environment::get().getWindowManager()->fadeScreenIn(0.25); } } diff --git a/apps/openmw/mwgui/trainingwindow.hpp b/apps/openmw/mwgui/trainingwindow.hpp index 7c4582062..69a013059 100644 --- a/apps/openmw/mwgui/trainingwindow.hpp +++ b/apps/openmw/mwgui/trainingwindow.hpp @@ -14,14 +14,18 @@ namespace MWGui public: TrainingWindow(); - virtual void open(); + virtual void onOpen(); - virtual void exit(); + bool exit() { return false; } - void startTraining(MWWorld::Ptr actor); + void setPtr(const MWWorld::Ptr& actor); void onFrame(float dt); + WindowBase* getProgressBar() { return &mProgressBar; } + + void clear() { resetReference(); } + protected: virtual void onReferenceUnavailable (); @@ -35,8 +39,6 @@ namespace MWGui MyGUI::Button* mCancelButton; MyGUI::TextBox* mPlayerGold; - float mFadeTimeRemaining; - WaitDialogProgressBar mProgressBar; TimeAdvancer mTimeAdvancer; }; diff --git a/apps/openmw/mwgui/travelwindow.cpp b/apps/openmw/mwgui/travelwindow.cpp index 3a7afe497..7586bb66a 100644 --- a/apps/openmw/mwgui/travelwindow.cpp +++ b/apps/openmw/mwgui/travelwindow.cpp @@ -8,7 +8,6 @@ #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" -#include "../mwbase/dialoguemanager.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/actorutil.hpp" @@ -45,11 +44,6 @@ namespace MWGui mSelect->getHeight()); } - void TravelWindow::exit() - { - MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Travel); - } - void TravelWindow::addDestination(const std::string& name,ESM::Position pos,bool interior) { int price; @@ -108,7 +102,7 @@ namespace MWGui MyGUI::Gui::getInstance().destroyWidget(mDestinationsView->getChildAt(0)); } - void TravelWindow::startTravel(const MWWorld::Ptr& actor) + void TravelWindow::setPtr(const MWWorld::Ptr& actor) { center(); mPtr = actor; @@ -182,7 +176,9 @@ namespace MWGui } MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Travel); - MWBase::Environment::get().getDialogueManager()->goodbyeSelected(); + MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode(); + + MWBase::Environment::get().getWindowManager()->fadeScreenOut(1); // Teleports any followers, too. MWWorld::ActionTeleport action(interior ? cellname : "", pos, true); @@ -194,7 +190,7 @@ namespace MWGui void TravelWindow::onCancelButtonClicked(MyGUI::Widget* _sender) { - exit(); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Travel); } void TravelWindow::updateLabels() @@ -212,7 +208,7 @@ namespace MWGui void TravelWindow::onReferenceUnavailable() { MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Travel); - MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Dialogue); + MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode(); } void TravelWindow::onMouseWheel(MyGUI::Widget* _sender, int _rel) diff --git a/apps/openmw/mwgui/travelwindow.hpp b/apps/openmw/mwgui/travelwindow.hpp index 3230f897f..5ae466047 100644 --- a/apps/openmw/mwgui/travelwindow.hpp +++ b/apps/openmw/mwgui/travelwindow.hpp @@ -24,9 +24,7 @@ namespace MWGui public: TravelWindow(); - virtual void exit(); - - void startTravel(const MWWorld::Ptr& actor); + void setPtr (const MWWorld::Ptr& actor); protected: MyGUI::Button* mCancelButton; diff --git a/apps/openmw/mwgui/waitdialog.cpp b/apps/openmw/mwgui/waitdialog.cpp index ba58a9c69..a7ad687cb 100644 --- a/apps/openmw/mwgui/waitdialog.cpp +++ b/apps/openmw/mwgui/waitdialog.cpp @@ -1,6 +1,8 @@ #include "waitdialog.hpp" #include +#include +#include #include @@ -23,8 +25,6 @@ #include "../mwstate/charactermanager.hpp" -#include "widgets.hpp" - namespace MWGui { @@ -35,7 +35,7 @@ namespace MWGui getWidget(mProgressText, "ProgressText"); } - void WaitDialogProgressBar::open() + void WaitDialogProgressBar::onOpen() { center(); } @@ -72,21 +72,43 @@ namespace MWGui mWaitButton->eventMouseButtonClick += MyGUI::newDelegate(this, &WaitDialog::onWaitButtonClicked); mHourSlider->eventScrollChangePosition += MyGUI::newDelegate(this, &WaitDialog::onHourSliderChangedPosition); + mCancelButton->eventKeyButtonPressed += MyGUI::newDelegate(this, &WaitDialog::onKeyButtonPressed); + mWaitButton->eventKeyButtonPressed += MyGUI::newDelegate(this, &WaitDialog::onKeyButtonPressed); + mUntilHealedButton->eventKeyButtonPressed += MyGUI::newDelegate(this, &WaitDialog::onKeyButtonPressed); + mTimeAdvancer.eventProgressChanged += MyGUI::newDelegate(this, &WaitDialog::onWaitingProgressChanged); mTimeAdvancer.eventInterrupted += MyGUI::newDelegate(this, &WaitDialog::onWaitingInterrupted); mTimeAdvancer.eventFinished += MyGUI::newDelegate(this, &WaitDialog::onWaitingFinished); + } - mProgressBar.setVisible (false); + void WaitDialog::setPtr(const MWWorld::Ptr &ptr) + { + setCanRest(!ptr.isEmpty() || MWBase::Environment::get().getWorld ()->canRest () == 0); + + if (mUntilHealedButton->getVisible()) + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mUntilHealedButton); + else + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mWaitButton); } - void WaitDialog::exit() + bool WaitDialog::exit() { - if(!mProgressBar.isVisible()) //Only exit if not currently waiting - MWBase::Environment::get().getWindowManager()->popGuiMode(); + return (!mTimeAdvancer.isRunning()); //Only exit if not currently waiting } - void WaitDialog::open() + void WaitDialog::onOpen() { + if (mTimeAdvancer.isRunning()) + { + mProgressBar.setVisible(true); + setVisible(false); + return; + } + else + { + mProgressBar.setVisible(false); + } + if (!MWBase::Environment::get().getWindowManager ()->getRestEnabled ()) { MWBase::Environment::get().getWindowManager()->popGuiMode (); @@ -101,8 +123,6 @@ namespace MWGui MWBase::Environment::get().getWindowManager()->popGuiMode (); } - setCanRest(canRest == 0); - onHourSliderChangedPosition(mHourSlider, 0); mHourSlider->setScrollPosition (0); @@ -177,13 +197,25 @@ namespace MWGui void WaitDialog::onCancelButtonClicked(MyGUI::Widget* sender) { - exit(); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Rest); } void WaitDialog::onHourSliderChangedPosition(MyGUI::ScrollBar* sender, size_t position) { mHourText->setCaptionWithReplacing (MyGUI::utility::toString(position+1) + " #{sRestMenu2}"); mManualHours = position+1; + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mWaitButton); + } + + void WaitDialog::onKeyButtonPressed(MyGUI::Widget *sender, MyGUI::KeyCode key, MyGUI::Char character) + { + if (key == MyGUI::KeyCode::ArrowDown) + mHourSlider->setScrollPosition(std::min(mHourSlider->getScrollPosition()+1, mHourSlider->getScrollRange()-1)); + else if (key == MyGUI::KeyCode::ArrowUp) + mHourSlider->setScrollPosition(std::max(static_cast(mHourSlider->getScrollPosition())-1, 0)); + else + return; + onHourSliderChangedPosition(mHourSlider, mHourSlider->getScrollPosition()); } void WaitDialog::onWaitingProgressChanged(int cur, int total) @@ -265,7 +297,6 @@ namespace MWGui MWBase::Environment::get().getWindowManager()->fadeScreenIn(0.2f); mProgressBar.setVisible (false); MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Rest); - MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_RestBed); mTimeAdvancer.stop(); } diff --git a/apps/openmw/mwgui/waitdialog.hpp b/apps/openmw/mwgui/waitdialog.hpp index 13df1f8ae..eb6a55640 100644 --- a/apps/openmw/mwgui/waitdialog.hpp +++ b/apps/openmw/mwgui/waitdialog.hpp @@ -13,7 +13,7 @@ namespace MWGui public: WaitDialogProgressBar(); - virtual void open(); + virtual void onOpen(); void setProgress(int cur, int total); @@ -27,18 +27,20 @@ namespace MWGui public: WaitDialog(); - virtual void open(); + void setPtr(const MWWorld::Ptr &ptr); - virtual void exit(); + virtual void onOpen(); - void onFrame(float dt); + virtual bool exit(); - void bedActivated() { setCanRest(true); } + void onFrame(float dt); bool getSleeping() { return mTimeAdvancer.isRunning() && mSleeping; } void wakeUp(); void autosave(); + WindowBase* getProgressBar() { return &mProgressBar; } + protected: MyGUI::TextBox* mDateTimeText; MyGUI::TextBox* mRestText; @@ -63,6 +65,7 @@ namespace MWGui void onWaitButtonClicked(MyGUI::Widget* sender); void onCancelButtonClicked(MyGUI::Widget* sender); void onHourSliderChangedPosition(MyGUI::ScrollBar* sender, size_t position); + void onKeyButtonPressed(MyGUI::Widget* sender, MyGUI::KeyCode key, MyGUI::Char character); void onWaitingProgressChanged(int cur, int total); void onWaitingInterrupted(); diff --git a/apps/openmw/mwgui/widgets.cpp b/apps/openmw/mwgui/widgets.cpp index 744ef236f..45767bf01 100644 --- a/apps/openmw/mwgui/widgets.cpp +++ b/apps/openmw/mwgui/widgets.cpp @@ -105,7 +105,7 @@ namespace MWGui assignWidget(button, "StatValueButton"); if (button) { - mSkillNameWidget = button; + mSkillValueWidget = button; button->eventMouseButtonClick += MyGUI::newDelegate(this, &MWSkill::onClicked); } } diff --git a/apps/openmw/mwgui/windowbase.cpp b/apps/openmw/mwgui/windowbase.cpp index 99b74529e..a0e7eedde 100644 --- a/apps/openmw/mwgui/windowbase.cpp +++ b/apps/openmw/mwgui/windowbase.cpp @@ -15,6 +15,7 @@ using namespace MWGui; WindowBase::WindowBase(const std::string& parLayout) : Layout(parLayout) { + mMainWidget->setVisible(false); } void WindowBase::setVisible(bool visible) @@ -23,9 +24,9 @@ void WindowBase::setVisible(bool visible) mMainWidget->setVisible(visible); if (visible) - open(); + onOpen(); else if (wasVisible && !visible) - close(); + onClose(); // This is needed as invisible widgets can retain key focus. // Remove for MyGUI 3.2.2 @@ -64,16 +65,20 @@ WindowModal::WindowModal(const std::string& parLayout) { } -void WindowModal::open() +void WindowModal::onOpen() { - MyGUI::InputManager::getInstance ().addWidgetModal (mMainWidget); MWBase::Environment::get().getWindowManager()->addCurrentModal(this); //Set so we can escape it if needed + + MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getKeyFocusWidget(); + MyGUI::InputManager::getInstance ().addWidgetModal (mMainWidget); + MyGUI::InputManager::getInstance().setKeyFocusWidget(focus); } -void WindowModal::close() +void WindowModal::onClose() { - MyGUI::InputManager::getInstance ().removeWidgetModal (mMainWidget); MWBase::Environment::get().getWindowManager()->removeCurrentModal(this); + + MyGUI::InputManager::getInstance ().removeWidgetModal (mMainWidget); } NoDrop::NoDrop(DragAndDrop *drag, MyGUI::Widget *widget) diff --git a/apps/openmw/mwgui/windowbase.hpp b/apps/openmw/mwgui/windowbase.hpp index 43fb8901e..56901c95a 100644 --- a/apps/openmw/mwgui/windowbase.hpp +++ b/apps/openmw/mwgui/windowbase.hpp @@ -8,6 +8,11 @@ namespace MWBase class WindowManager; } +namespace MWWorld +{ + class Ptr; +} + namespace MWGui { class WindowManager; @@ -18,20 +23,35 @@ namespace MWGui public: WindowBase(const std::string& parLayout); + virtual MyGUI::Widget* getDefaultKeyFocus() { return NULL; } + // Events typedef MyGUI::delegates::CMultiDelegate1 EventHandle_WindowBase; + /// Open this object in the GUI, for windows that support it + virtual void setPtr(const MWWorld::Ptr& ptr) {} + + /// Called every frame if the window is in an active GUI mode + virtual void onFrame(float duration) {} + /// Notify that window has been made visible - virtual void open() {} + virtual void onOpen() {} /// Notify that window has been hidden - virtual void close () {} + virtual void onClose () {} /// Gracefully exits the window - virtual void exit() {} + virtual bool exit() {return true;} /// Sets the visibility of the window virtual void setVisible(bool visible); /// Returns the visibility state of the window bool isVisible(); + void center(); + + /// Clear any state specific to the running game + virtual void clear() {} + + /// Called when GUI viewport changes size + virtual void onResChange(int width, int height) {} }; @@ -42,9 +62,9 @@ namespace MWGui { public: WindowModal(const std::string& parLayout); - virtual void open(); - virtual void close(); - virtual void exit() {} + virtual void onOpen(); + virtual void onClose(); + virtual bool exit() {return true;} }; /// A window that cannot be the target of a drag&drop action. diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index aa4d0578c..cbe7d877c 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -123,6 +124,7 @@ #include "controllers.hpp" #include "jailscreen.hpp" #include "itemchargeview.hpp" +#include "keyboardnavigation.hpp" namespace { @@ -150,38 +152,24 @@ namespace MWGui , mHud(NULL) , mMap(NULL) , mLocalMapRender(NULL) - , mMenu(NULL) , mToolTips(NULL) , mStatsWindow(NULL) , mMessageBoxManager(NULL) , mConsole(NULL) - , mJournal(NULL) , mDialogueWindow(NULL) - , mContainerWindow(NULL) , mDragAndDrop(NULL) , mInventoryWindow(NULL) , mScrollWindow(NULL) , mBookWindow(NULL) , mCountDialog(NULL) , mTradeWindow(NULL) - , mSpellBuyingWindow(NULL) - , mTravelWindow(NULL) , mSettingsWindow(NULL) , mConfirmationDialog(NULL) - , mAlchemyWindow(NULL) , mSpellWindow(NULL) , mQuickKeysMenu(NULL) , mLoadingScreen(NULL) - , mLevelupDialog(NULL) , mWaitDialog(NULL) - , mSpellCreationDialog(NULL) - , mEnchantingDialog(NULL) - , mTrainingWindow(NULL) - , mMerchantRepair(NULL) , mSoulgemDialog(NULL) - , mRepair(NULL) - , mRecharge(NULL) - , mCompanionWindow(NULL) , mVideoBackground(NULL) , mVideoWidget(NULL) , mWerewolfFader(NULL) @@ -198,8 +186,8 @@ namespace MWGui , mHitFaderEnabled(Settings::Manager::getBool ("hit fader", "GUI")) , mWerewolfOverlayEnabled(Settings::Manager::getBool ("werewolf overlay", "GUI")) , mHudEnabled(true) - , mGuiEnabled(true) , mCursorVisible(true) + , mCursorActive(false) , mPlayerName() , mPlayerRaceId() , mPlayerAttributes() @@ -259,7 +247,10 @@ namespace MWGui MyGUI::FactoryManager::getInstance().registerFactory("Resource", "ResourceImageSetPointer"); MyGUI::ResourceManager::getInstance().load("core.xml"); + mKeyboardNavigation.reset(new KeyboardNavigation()); + mLoadingScreen = new LoadingScreen(mResourceSystem->getVFS(), mViewer); + mWindows.push_back(mLoadingScreen); //set up the hardware cursor manager mCursorManager = new SDLUtil::SDLCursorManager(); @@ -327,57 +318,155 @@ namespace MWGui mDragAndDrop = new DragAndDrop(); - mRecharge = new Recharge(); - mMenu = new MainMenu(w, h, mResourceSystem->getVFS(), mVersionDescription); + Recharge* recharge = new Recharge(); + mGuiModeStates[GM_Recharge] = GuiModeState(recharge); + mWindows.push_back(recharge); + + MainMenu* menu = new MainMenu(w, h, mResourceSystem->getVFS(), mVersionDescription); + mGuiModeStates[GM_MainMenu] = GuiModeState(menu); + mWindows.push_back(menu); + mLocalMapRender = new MWRender::LocalMap(mViewer->getSceneData()->asGroup()); mMap = new MapWindow(mCustomMarkers, mDragAndDrop, mLocalMapRender, mWorkQueue); + mWindows.push_back(mMap); mMap->renderGlobalMap(); trackWindow(mMap, "map"); + mStatsWindow = new StatsWindow(mDragAndDrop); + mWindows.push_back(mStatsWindow); trackWindow(mStatsWindow, "stats"); + + mInventoryWindow = new InventoryWindow(mDragAndDrop, mViewer->getSceneData()->asGroup(), mResourceSystem); + mWindows.push_back(mInventoryWindow); + + mSpellWindow = new SpellWindow(mDragAndDrop); + mWindows.push_back(mSpellWindow); + trackWindow(mSpellWindow, "spells"); + + mGuiModeStates[GM_Inventory] = GuiModeState({mMap, mInventoryWindow, mSpellWindow, mStatsWindow}); + mGuiModeStates[GM_None] = GuiModeState({mMap, mInventoryWindow, mSpellWindow, mStatsWindow}); + + mTradeWindow = new TradeWindow(); + mWindows.push_back(mTradeWindow); + trackWindow(mTradeWindow, "barter"); + mGuiModeStates[GM_Barter] = GuiModeState({mInventoryWindow, mTradeWindow}); + mConsole = new Console(w,h, mConsoleOnlyScripts); + mWindows.push_back(mConsole); trackWindow(mConsole, "console"); + mGuiModeStates[GM_Console] = GuiModeState(mConsole); bool questList = mResourceSystem->getVFS()->exists("textures/tx_menubook_options_over.dds"); - mJournal = JournalWindow::create(JournalViewModel::create (), questList); + JournalWindow* journal = JournalWindow::create(JournalViewModel::create (), questList); + mWindows.push_back(journal); + mGuiModeStates[GM_Journal] = GuiModeState(journal); + mGuiModeStates[GM_Journal].mCloseSound = "book close"; + mGuiModeStates[GM_Journal].mOpenSound = "book open"; + mMessageBoxManager = new MessageBoxManager(mStore->get().find("fMessageTimePerChar")->getFloat()); - mInventoryWindow = new InventoryWindow(mDragAndDrop, mViewer->getSceneData()->asGroup(), mResourceSystem); - mTradeWindow = new TradeWindow(); - trackWindow(mTradeWindow, "barter"); - mSpellBuyingWindow = new SpellBuyingWindow(); - mTravelWindow = new TravelWindow(); + + SpellBuyingWindow* spellBuyingWindow = new SpellBuyingWindow(); + mWindows.push_back(spellBuyingWindow); + mGuiModeStates[GM_SpellBuying] = GuiModeState(spellBuyingWindow); + + TravelWindow* travelWindow = new TravelWindow(); + mWindows.push_back(travelWindow); + mGuiModeStates[GM_Travel] = GuiModeState(travelWindow); + mDialogueWindow = new DialogueWindow(); + mWindows.push_back(mDialogueWindow); trackWindow(mDialogueWindow, "dialogue"); - mContainerWindow = new ContainerWindow(mDragAndDrop); - trackWindow(mContainerWindow, "container"); + mGuiModeStates[GM_Dialogue] = GuiModeState(mDialogueWindow); + mTradeWindow->eventTradeDone += MyGUI::newDelegate(mDialogueWindow, &DialogueWindow::onTradeComplete); + + ContainerWindow* containerWindow = new ContainerWindow(mDragAndDrop); + mWindows.push_back(containerWindow); + trackWindow(containerWindow, "container"); + mGuiModeStates[GM_Container] = GuiModeState({containerWindow, mInventoryWindow}); + mHud = new HUD(mCustomMarkers, mDragAndDrop, mLocalMapRender); + mWindows.push_back(mHud); + mToolTips = new ToolTips(); + mScrollWindow = new ScrollWindow(); + mWindows.push_back(mScrollWindow); + mGuiModeStates[GM_Scroll] = GuiModeState(mScrollWindow); + mGuiModeStates[GM_Scroll].mOpenSound = "scroll"; + mGuiModeStates[GM_Scroll].mCloseSound = "scroll"; + mBookWindow = new BookWindow(); + mWindows.push_back(mBookWindow); + mGuiModeStates[GM_Book] = GuiModeState(mBookWindow); + mGuiModeStates[GM_Book].mOpenSound = "book open"; + mGuiModeStates[GM_Book].mCloseSound = "book close"; + mCountDialog = new CountDialog(); + mWindows.push_back(mCountDialog); + mSettingsWindow = new SettingsWindow(); + mWindows.push_back(mSettingsWindow); + mGuiModeStates[GM_Settings] = GuiModeState(mSettingsWindow); + mConfirmationDialog = new ConfirmationDialog(); - mAlchemyWindow = new AlchemyWindow(); - trackWindow(mAlchemyWindow, "alchemy"); - mSpellWindow = new SpellWindow(mDragAndDrop); - trackWindow(mSpellWindow, "spells"); + mWindows.push_back(mConfirmationDialog); + + AlchemyWindow* alchemyWindow = new AlchemyWindow(); + mWindows.push_back(alchemyWindow); + trackWindow(alchemyWindow, "alchemy"); + mGuiModeStates[GM_Alchemy] = GuiModeState(alchemyWindow); + mQuickKeysMenu = new QuickKeysMenu(); - mLevelupDialog = new LevelupDialog(); + mWindows.push_back(mQuickKeysMenu); + mGuiModeStates[GM_QuickKeysMenu] = GuiModeState(mQuickKeysMenu); + + LevelupDialog* levelupDialog = new LevelupDialog(); + mWindows.push_back(levelupDialog); + mGuiModeStates[GM_Levelup] = GuiModeState(levelupDialog); + mWaitDialog = new WaitDialog(); - mSpellCreationDialog = new SpellCreationDialog(); - mEnchantingDialog = new EnchantingDialog(); - mTrainingWindow = new TrainingWindow(); - mMerchantRepair = new MerchantRepair(); - mRepair = new Repair(); + mWindows.push_back(mWaitDialog); + mGuiModeStates[GM_Rest] = GuiModeState({mWaitDialog->getProgressBar(), mWaitDialog}); + + SpellCreationDialog* spellCreationDialog = new SpellCreationDialog(); + mWindows.push_back(spellCreationDialog); + mGuiModeStates[GM_SpellCreation] = GuiModeState(spellCreationDialog); + + EnchantingDialog* enchantingDialog = new EnchantingDialog(); + mWindows.push_back(enchantingDialog); + mGuiModeStates[GM_Enchanting] = GuiModeState(enchantingDialog); + + TrainingWindow* trainingWindow = new TrainingWindow(); + mWindows.push_back(trainingWindow); + mGuiModeStates[GM_Training] = GuiModeState({trainingWindow->getProgressBar(), trainingWindow}); + + MerchantRepair* merchantRepair = new MerchantRepair(); + mWindows.push_back(merchantRepair); + mGuiModeStates[GM_MerchantRepair] = GuiModeState(merchantRepair); + + Repair* repair = new Repair(); + mWindows.push_back(repair); + mGuiModeStates[GM_Repair] = GuiModeState(repair); + mSoulgemDialog = new SoulgemDialog(mMessageBoxManager); - mCompanionWindow = new CompanionWindow(mDragAndDrop, mMessageBoxManager); - trackWindow(mCompanionWindow, "companion"); + + CompanionWindow* companionWindow = new CompanionWindow(mDragAndDrop, mMessageBoxManager); + mWindows.push_back(companionWindow); + trackWindow(companionWindow, "companion"); + mGuiModeStates[GM_Companion] = GuiModeState({mInventoryWindow, companionWindow}); + mJailScreen = new JailScreen(); + mWindows.push_back(mJailScreen); + mGuiModeStates[GM_Jail] = GuiModeState(mJailScreen); std::string werewolfFaderTex = "textures\\werewolfoverlay.dds"; if (mResourceSystem->getVFS()->exists(werewolfFaderTex)) + { mWerewolfFader = new ScreenFader(werewolfFaderTex); + mWindows.push_back(mWerewolfFader); + } mBlindnessFader = new ScreenFader("black"); + mWindows.push_back(mBlindnessFader); // fall back to player_hit_01.dds if bm_player_hit_01.dds is not available std::string hitFaderTexture = "textures\\bm_player_hit_01.dds"; @@ -389,14 +478,17 @@ namespace MWGui hitFaderCoord = MyGUI::FloatCoord(0.2, 0.25, 0.6, 0.5); } mHitFader = new ScreenFader(hitFaderTexture, hitFaderLayout, hitFaderCoord); + mWindows.push_back(mHitFader); mScreenFader = new ScreenFader("black"); + mWindows.push_back(mScreenFader); mDebugWindow = new DebugWindow(); + mWindows.push_back(mDebugWindow); mInputBlocker = MyGUI::Gui::getInstance().createWidget("",0,0,w,h,MyGUI::Align::Stretch,"InputBlocker"); - mHud->setVisible(mHudEnabled); + mHud->setVisible(true); mCharGen = new CharacterCreation(mViewer->getSceneData()->asGroup(), mResourceSystem); @@ -415,84 +507,39 @@ namespace MWGui // Set up visibility updateVisible(); - - MWBase::Environment::get().getInputManager()->changeInputMode(false); } void WindowManager::setNewGame(bool newgame) { - // This method will always be called after loading a savegame or starting a new game - // Reset enemy, it could be a dangling pointer from a previous game - mHud->resetEnemy(); - if (newgame) { disallowAll(); delete mCharGen; mCharGen = new CharacterCreation(mViewer->getSceneData()->asGroup(), mResourceSystem); - mGuiModes.clear(); - MWBase::Environment::get().getInputManager()->changeInputMode(false); - mHud->unsetSelectedWeapon(); - mHud->unsetSelectedSpell(); - unsetForceHide(GW_ALL); } else allow(GW_ALL); - - mRestAllowed = !newgame; } WindowManager::~WindowManager() { + mKeyboardNavigation.reset(); + MyGUI::LanguageManager::getInstance().eventRequestTag.clear(); MyGUI::PointerManager::getInstance().eventChangeMousePointer.clear(); MyGUI::InputManager::getInstance().eventChangeKeyFocus.clear(); MyGUI::ClipboardManager::getInstance().eventClipboardChanged.clear(); MyGUI::ClipboardManager::getInstance().eventClipboardRequested.clear(); - delete mConsole; + for (WindowBase* window : mWindows) + delete window; + mWindows.clear(); + delete mMessageBoxManager; - delete mHud; - delete mMap; delete mLocalMapRender; - delete mMenu; - delete mStatsWindow; - delete mJournal; - delete mDialogueWindow; - delete mContainerWindow; - delete mInventoryWindow; - delete mToolTips; delete mCharGen; delete mDragAndDrop; - delete mBookWindow; - delete mScrollWindow; - delete mTradeWindow; - delete mSpellBuyingWindow; - delete mTravelWindow; - delete mSettingsWindow; - delete mConfirmationDialog; - delete mAlchemyWindow; - delete mSpellWindow; - delete mLoadingScreen; - delete mLevelupDialog; - delete mWaitDialog; - delete mSpellCreationDialog; - delete mEnchantingDialog; - delete mTrainingWindow; - delete mCountDialog; - delete mQuickKeysMenu; - delete mMerchantRepair; - delete mRepair; delete mSoulgemDialog; - delete mRecharge; - delete mCompanionWindow; - delete mHitFader; - delete mWerewolfFader; - delete mScreenFader; - delete mBlindnessFader; - delete mDebugWindow; - delete mJailScreen; - delete mCursorManager; cleanupGarbage(); @@ -524,210 +571,96 @@ namespace MWGui } } - void WindowManager::update() - { - cleanupGarbage(); - - mHud->update(); - - updateActivatedQuickKey (); - } - void WindowManager::updateVisible() { if (!mMap) return; // UI not created yet - // Start out by hiding everything except the HUD - mMap->setVisible(false); - mMenu->setVisible(false); - mStatsWindow->setVisible(false); - mConsole->setVisible(false); - mJournal->setVisible(false); - mDialogueWindow->setVisible(false); - mContainerWindow->setVisible(false); - mInventoryWindow->setVisible(false); - mScrollWindow->setVisible(false); - mBookWindow->setVisible(false); - mTradeWindow->setVisible(false); - mSpellBuyingWindow->setVisible(false); - mTravelWindow->setVisible(false); - mSettingsWindow->setVisible(false); - mAlchemyWindow->setVisible(false); - mSpellWindow->setVisible(false); - mQuickKeysMenu->setVisible(false); - mLevelupDialog->setVisible(false); - mWaitDialog->setVisible(false); - mSpellCreationDialog->setVisible(false); - mEnchantingDialog->setVisible(false); - mTrainingWindow->setVisible(false); - mMerchantRepair->setVisible(false); - mRepair->setVisible(false); - mCompanionWindow->setVisible(false); - mInventoryWindow->setTrading(false); - mRecharge->setVisible(false); - mVideoBackground->setVisible(false); - mJailScreen->setVisible(false); - mHud->setVisible(mHudEnabled && mGuiEnabled); - mToolTips->setVisible(mGuiEnabled); + bool loading = (getMode() == GM_Loading || getMode() == GM_LoadingWallpaper); + + mHud->setVisible(mHudEnabled && !loading); + mToolTips->setVisible(mHudEnabled && !loading); bool gameMode = !isGuiMode(); + MWBase::Environment::get().getInputManager()->changeInputMode(!gameMode); + mInputBlocker->setVisible (gameMode); - setCursorVisible(!gameMode); + + if (loading) + setCursorVisible(mMessageBoxManager && mMessageBoxManager->isInteractiveMessageBox()); + else + setCursorVisible(!gameMode); if (gameMode) setKeyFocusWidget (NULL); - if (!mGuiEnabled) - { - if (containsMode(GM_Console)) - mConsole->setVisible(true); - return; - } - // Icons of forced hidden windows are displayed setMinimapVisibility((mAllowed & GW_Map) && (!mMap->pinned() || (mForceHidden & GW_Map))); setWeaponVisibility((mAllowed & GW_Inventory) && (!mInventoryWindow->pinned() || (mForceHidden & GW_Inventory))); setSpellVisibility((mAllowed & GW_Magic) && (!mSpellWindow->pinned() || (mForceHidden & GW_Magic))); setHMSVisibility((mAllowed & GW_Stats) && (!mStatsWindow->pinned() || (mForceHidden & GW_Stats))); - // If in game mode (or interactive messagebox), show only the pinned windows + mInventoryWindow->setGuiMode(getMode()); + + // If in game mode (or interactive messagebox), show the pinned windows if (mGuiModes.empty()) { - mInventoryWindow->setGuiMode(GM_None); mMap->setVisible(mMap->pinned() && !(mForceHidden & GW_Map) && (mAllowed & GW_Map)); mStatsWindow->setVisible(mStatsWindow->pinned() && !(mForceHidden & GW_Stats) && (mAllowed & GW_Stats)); mInventoryWindow->setVisible(mInventoryWindow->pinned() && !(mForceHidden & GW_Inventory) && (mAllowed & GW_Inventory)); mSpellWindow->setVisible(mSpellWindow->pinned() && !(mForceHidden & GW_Magic) && (mAllowed & GW_Magic)); - return; } + else if (getMode() != GM_Inventory) + { + mMap->setVisible(false); + mStatsWindow->setVisible(false); + mSpellWindow->setVisible(false); + mInventoryWindow->setVisible(getMode() == GM_Container || getMode() == GM_Barter || getMode() == GM_Companion); + } + + GuiMode mode = mGuiModes.back(); + + mInventoryWindow->setTrading(mode == GM_Barter); - if(mGuiModes.size() != 0) + if (getMode() == GM_Inventory) { - GuiMode mode = mGuiModes.back(); - - switch(mode) { - case GM_QuickKeysMenu: - mQuickKeysMenu->setVisible (true); - break; - case GM_MainMenu: - mMenu->setVisible(true); - break; - case GM_Settings: - mSettingsWindow->setVisible(true); - break; - case GM_Console: - mConsole->setVisible(true); - break; - case GM_Scroll: - mScrollWindow->setVisible(true); - break; - case GM_Book: - mBookWindow->setVisible(true); - break; - case GM_Alchemy: - mAlchemyWindow->setVisible(true); - break; - case GM_Rest: - mWaitDialog->setVisible(true); - break; - case GM_RestBed: - mWaitDialog->setVisible(true); - mWaitDialog->bedActivated(); - break; - case GM_Levelup: - mLevelupDialog->setVisible(true); - break; - case GM_Name: - case GM_Race: - case GM_Class: - case GM_ClassPick: - case GM_ClassCreate: - case GM_Birth: - case GM_ClassGenerate: - case GM_Review: - mCharGen->spawnDialog(mode); - break; - case GM_Inventory: - { - // First, compute the effective set of windows to show. - // This is controlled both by what windows the - // user has opened/closed (the 'shown' variable) and by what - // windows we are allowed to show (the 'allowed' var.) - int eff = mShown & mAllowed & ~mForceHidden; - - // Show the windows we want - mMap ->setVisible((eff & GW_Map) != 0); - mStatsWindow ->setVisible((eff & GW_Stats) != 0); - mInventoryWindow->setVisible((eff & GW_Inventory) != 0); - mInventoryWindow->setGuiMode(mode); - mSpellWindow ->setVisible((eff & GW_Magic) != 0); - break; - } - case GM_Container: - mContainerWindow->setVisible(true); - mInventoryWindow->setVisible(true); - mInventoryWindow->setGuiMode(mode); - break; - case GM_Companion: - mCompanionWindow->setVisible(true); - mInventoryWindow->setVisible(true); - mInventoryWindow->setGuiMode(mode); - break; - case GM_Dialogue: - mDialogueWindow->setVisible(true); - break; - case GM_Barter: - mInventoryWindow->setVisible(true); - mInventoryWindow->setTrading(true); - mInventoryWindow->setGuiMode(mode); - mTradeWindow->setVisible(true); - break; - case GM_SpellBuying: - mSpellBuyingWindow->setVisible(true); - break; - case GM_Travel: - mTravelWindow->setVisible(true); - break; - case GM_SpellCreation: - mSpellCreationDialog->setVisible(true); - break; - case GM_Recharge: - mRecharge->setVisible(true); - break; - case GM_Enchanting: - mEnchantingDialog->setVisible(true); - break; - case GM_Training: - mTrainingWindow->setVisible(true); - break; - case GM_MerchantRepair: - mMerchantRepair->setVisible(true); - break; - case GM_Repair: - mRepair->setVisible(true); - break; - case GM_Journal: - mJournal->setVisible(true); - break; - case GM_Jail: - mJailScreen->setVisible(true); - break; - case GM_LoadingWallpaper: - case GM_Loading: - // Don't need to show anything here - GM_LoadingWallpaper covers everything else anyway, - // GM_Loading uses a texture of the last rendered frame so everything previously visible will be rendered. - mHud->setVisible(false); - mToolTips->setVisible(false); - setCursorVisible(mMessageBoxManager && mMessageBoxManager->isInteractiveMessageBox()); - break; - default: - mwmp::Main::get().getGUIController()->WM_UpdateVisible(mode); - // Unsupported mode, switch back to game - break; - } + // For the inventory mode, compute the effective set of windows to show. + // This is controlled both by what windows the + // user has opened/closed (the 'shown' variable) and by what + // windows we are allowed to show (the 'allowed' var.) + int eff = mShown & mAllowed & ~mForceHidden; + mMap->setVisible(eff & GW_Map); + mInventoryWindow->setVisible(eff & GW_Inventory); + mSpellWindow->setVisible(eff & GW_Magic); + mStatsWindow->setVisible(eff & GW_Stats); + } + + switch (mode) + { + // FIXME: refactor chargen windows to use modes properly (or not use them at all) + case GM_Name: + case GM_Race: + case GM_Class: + case GM_ClassPick: + case GM_ClassCreate: + case GM_Birth: + case GM_ClassGenerate: + case GM_Review: + mCharGen->spawnDialog(mode); + break; + default: + /* + Start of tes3mp addition + + Pass the GuiMode further on to the multiplayer-specific GUI controller + */ + mwmp::Main::get().getGUIController()->WM_UpdateVisible(mode); + /* + End of tes3mp addition + */ + break; } } @@ -823,97 +756,32 @@ namespace MWGui mGarbageDialogs.push_back(dialog); } - void WindowManager::exitCurrentGuiMode() { - switch(mGuiModes.back()) { - case GM_QuickKeysMenu: - mQuickKeysMenu->exit(); - break; - case GM_MainMenu: - removeGuiMode(GM_MainMenu); //Simple way to remove it - break; - case GM_Settings: - mSettingsWindow->exit(); - break; - case GM_Console: - mConsole->exit(); - break; - case GM_Scroll: - mScrollWindow->exit(); - break; - case GM_Book: - mBookWindow->exit(); - break; - case GM_Alchemy: - mAlchemyWindow->exit(); - break; - case GM_Rest: - mWaitDialog->exit(); - break; - case GM_RestBed: - mWaitDialog->exit(); - break; - case GM_Name: - case GM_Race: - case GM_Class: - case GM_ClassPick: - case GM_ClassCreate: - case GM_Birth: - case GM_ClassGenerate: - case GM_Review: - break; - case GM_Inventory: - removeGuiMode(GM_Inventory); //Simple way to remove it - break; - case GM_Container: - mContainerWindow->exit(); - break; - case GM_Companion: - mCompanionWindow->exit(); - break; - case GM_Dialogue: - mDialogueWindow->exit(); - break; - case GM_Barter: - mTradeWindow->exit(); - break; - case GM_SpellBuying: - mSpellBuyingWindow->exit(); - break; - case GM_Travel: - mTravelWindow->exit(); - break; - case GM_SpellCreation: - mSpellCreationDialog->exit(); - break; - case GM_Recharge: - mRecharge->exit(); - break; - case GM_Enchanting: - mEnchantingDialog->exit(); - break; - case GM_Training: - mTrainingWindow->exit(); - break; - case GM_MerchantRepair: - mMerchantRepair->exit(); - break; - case GM_Repair: - mRepair->exit(); - break; - case GM_Journal: - playSound("book close"); - removeGuiMode(GM_Journal); //Simple way to remove it - break; - default: - // Unsupported mode, switch back to game - break; + void WindowManager::exitCurrentGuiMode() + { + if (mDragAndDrop && mDragAndDrop->mIsOnDragAndDrop) + { + mDragAndDrop->finish(); + return; + } + + GuiModeState& state = mGuiModeStates[mGuiModes.back()]; + for (WindowBase* window : state.mWindows) + { + if (!window->exit()) + { + // unable to exit window, but give access to main menu + if (!MyGUI::InputManager::getInstance().isModalAny() && getMode() != GM_MainMenu) + pushGuiMode (GM_MainMenu); + return; + } } + + popGuiMode(); } void WindowManager::interactiveMessageBox(const std::string &message, const std::vector &buttons, bool block) { mMessageBoxManager->createInteractiveMessageBox(message, buttons); - MWBase::Environment::get().getInputManager()->changeInputMode(isGuiMode()); updateVisible(); if (block) @@ -925,6 +793,7 @@ namespace MWGui double dt = frameTimer.time_s(); frameTimer.setStartTick(); + mKeyboardNavigation->onFrame(); mMessageBoxManager->onFrame(dt); MWBase::Environment::get().getInputManager()->update(dt, true, false); @@ -1022,12 +891,29 @@ namespace MWGui void WindowManager::onFrame (float frameDuration) { + if (!mGuiModes.empty()) + { + GuiModeState& state = mGuiModeStates[mGuiModes.back()]; + for (WindowBase* window : state.mWindows) + window->onFrame(frameDuration); + } + else + { + // update pinned windows if visible + for (WindowBase* window : mGuiModeStates[GM_Inventory].mWindows) + if (window->isVisible()) + window->onFrame(frameDuration); + } + + if (!mCurrentModals.empty()) + mCurrentModals.back()->onFrame(frameDuration); + + mKeyboardNavigation->onFrame(); + mMessageBoxManager->onFrame(frameDuration); mToolTips->onFrame(frameDuration); - mMenu->update(frameDuration); - if (mLocalMapRender) mLocalMapRender->cleanupCameras(); @@ -1035,50 +921,20 @@ namespace MWGui MWBase::StateManager::State_NoGame) return; - if (mDragAndDrop->mIsOnDragAndDrop) - { - assert(mDragAndDrop->mDraggedWidget); - mDragAndDrop->mDraggedWidget->setPosition(MyGUI::InputManager::getInstance().getMousePosition()); - } - - mDialogueWindow->onFrame(); - - mInventoryWindow->onFrame(); + mDragAndDrop->onFrame(); updateMap(); - mStatsWindow->onFrame(frameDuration); - mMap->onFrame(frameDuration); - mSpellWindow->onFrame(frameDuration); - - mWaitDialog->onFrame(frameDuration); - mHud->onFrame(frameDuration); - mTrainingWindow->onFrame (frameDuration); - - mTrainingWindow->checkReferenceAvailable(); - mDialogueWindow->checkReferenceAvailable(); - mTradeWindow->checkReferenceAvailable(); - mSpellBuyingWindow->checkReferenceAvailable(); - mSpellCreationDialog->checkReferenceAvailable(); - mEnchantingDialog->checkReferenceAvailable(); - mContainerWindow->checkReferenceAvailable(); - mCompanionWindow->checkReferenceAvailable(); - mConsole->checkReferenceAvailable(); - mCompanionWindow->onFrame(); - mJailScreen->onFrame(frameDuration); - - if (mWerewolfFader) - mWerewolfFader->update(frameDuration); - mBlindnessFader->update(frameDuration); - mHitFader->update(frameDuration); - mScreenFader->update(frameDuration); - mDebugWindow->onFrame(frameDuration); if (mCharGen) mCharGen->onFrame(frameDuration); + + updateActivatedQuickKey (); + + cleanupGarbage(); } void WindowManager::changeCell(const MWWorld::CellStore* cell) @@ -1205,7 +1061,16 @@ namespace MWGui void WindowManager::setCursorVisible(bool visible) { + if (visible == mCursorVisible) + return; mCursorVisible = visible; + if (!visible) + mCursorActive = false; + } + + void WindowManager::setCursorActive(bool active) + { + mCursorActive = active; } void WindowManager::onRetrieveTag(const MyGUI::UString& _tag, MyGUI::UString& _result) @@ -1290,54 +1155,88 @@ namespace MWGui it->first->setSize(size); } - mConsole->onResChange(x, y); - mMenu->onResChange(x, y); - mSettingsWindow->center(); - mAlchemyWindow->center(); - mScrollWindow->center(); - mBookWindow->center(); - mQuickKeysMenu->center(); - mSpellBuyingWindow->center(); + for (WindowBase* window : mWindows) + window->onResChange(x, y); + + // TODO: check if any windows are now off-screen and move them back if so + } + + void WindowManager::onCursorChange(const std::string &name) + { + mCursorManager->cursorChanged(name); } void WindowManager::pushGuiMode(GuiMode mode) + { + pushGuiMode(mode, MWWorld::Ptr()); + } + + void WindowManager::pushGuiMode(GuiMode mode, const MWWorld::Ptr& arg) { if (mode==GM_Inventory && mAllowed==GW_None) return; - - // If this mode already exists somewhere in the stack, just bring it to the front. - if (std::find(mGuiModes.begin(), mGuiModes.end(), mode) != mGuiModes.end()) + if (mGuiModes.empty() || mGuiModes.back() != mode) { - mGuiModes.erase(std::find(mGuiModes.begin(), mGuiModes.end(), mode)); - } + // If this mode already exists somewhere in the stack, just bring it to the front. + if (std::find(mGuiModes.begin(), mGuiModes.end(), mode) != mGuiModes.end()) + { + mGuiModes.erase(std::find(mGuiModes.begin(), mGuiModes.end(), mode)); + } - mGuiModes.push_back(mode); + if (!mGuiModes.empty()) + { + mKeyboardNavigation->saveFocus(mGuiModes.back()); + mGuiModeStates[mGuiModes.back()].update(false); + } + mGuiModes.push_back(mode); - bool gameMode = !isGuiMode(); - MWBase::Environment::get().getInputManager()->changeInputMode(!gameMode); + mGuiModeStates[mode].update(true); + playSound(mGuiModeStates[mode].mOpenSound); + } + for (WindowBase* window : mGuiModeStates[mode].mWindows) + window->setPtr(arg); + + mKeyboardNavigation->restoreFocus(mode); updateVisible(); } - void WindowManager::onCursorChange(const std::string &name) + void WindowManager::popGuiMode(bool noSound) { - mCursorManager->cursorChanged(name); - } + if (mDragAndDrop && mDragAndDrop->mIsOnDragAndDrop) + { + mDragAndDrop->finish(); + } - void WindowManager::popGuiMode() - { if (!mGuiModes.empty()) + { + const GuiMode mode = mGuiModes.back(); + mKeyboardNavigation->saveFocus(mode); mGuiModes.pop_back(); + mGuiModeStates[mode].update(false); + if (!noSound) + playSound(mGuiModeStates[mode].mCloseSound); + } - bool gameMode = !isGuiMode(); - MWBase::Environment::get().getInputManager()->changeInputMode(!gameMode); + if (!mGuiModes.empty()) + { + const GuiMode mode = mGuiModes.back(); + mGuiModeStates[mode].update(true); + mKeyboardNavigation->restoreFocus(mode); + } updateVisible(); } - void WindowManager::removeGuiMode(GuiMode mode) + void WindowManager::removeGuiMode(GuiMode mode, bool noSound) { + if (!mGuiModes.empty() && mGuiModes.back() == mode) + { + popGuiMode(noSound); + return; + } + std::vector::iterator it = mGuiModes.begin(); while (it != mGuiModes.end()) { @@ -1347,9 +1246,6 @@ namespace MWGui ++it; } - bool gameMode = !isGuiMode(); - MWBase::Environment::get().getInputManager()->changeInputMode(!gameMode); - updateVisible(); } @@ -1449,7 +1345,6 @@ namespace MWGui mConsole->executeFile (path); } - MWGui::DialogueWindow* WindowManager::getDialogueWindow() { return mDialogueWindow; } MWGui::InventoryWindow* WindowManager::getInventoryWindow() { return mInventoryWindow; } MWGui::CountDialog* WindowManager::getCountDialog() { return mCountDialog; } MWGui::ConfirmationDialog* WindowManager::getConfirmationDialog() { return mConfirmationDialog; } @@ -1482,6 +1377,7 @@ namespace MWGui void WindowManager::disallowAll() { mAllowed = GW_None; + mRestAllowed = false; mBookWindow->setInventoryAllowed (false); mScrollWindow->setInventoryAllowed (false); @@ -1594,11 +1490,11 @@ namespace MWGui return mSubtitlesEnabled; } - bool WindowManager::toggleGui() + bool WindowManager::toggleHud() { - mGuiEnabled = !mGuiEnabled; + mHudEnabled = !mHudEnabled; updateVisible(); - return mGuiEnabled; + return mHudEnabled; } bool WindowManager::getRestEnabled() @@ -1624,52 +1520,11 @@ namespace MWGui mMap->addVisitedLocation (name, x, y); } - void WindowManager::startSpellMaking(MWWorld::Ptr actor) - { - pushGuiMode(GM_SpellCreation); - mSpellCreationDialog->startSpellMaking (actor); - } - - void WindowManager::startEnchanting (MWWorld::Ptr actor) - { - pushGuiMode(GM_Enchanting); - mEnchantingDialog->startEnchanting (actor); - } - - void WindowManager::startSelfEnchanting(MWWorld::Ptr soulgem) - { - mEnchantingDialog->startSelfEnchanting(soulgem); - } - - void WindowManager::startTraining(MWWorld::Ptr actor) - { - pushGuiMode(GM_Training); - mTrainingWindow->startTraining(actor); - } - - void WindowManager::startRepair(MWWorld::Ptr actor) - { - pushGuiMode(GM_MerchantRepair); - mMerchantRepair->startRepair(actor); - } - - void WindowManager::startRepairItem(MWWorld::Ptr item) - { - pushGuiMode(MWGui::GM_Repair); - mRepair->startRepairItem(item); - } - const Translation::Storage& WindowManager::getTranslationDataStorage() const { return mTranslationDataStorage; } - void WindowManager::showCompanionWindow(MWWorld::Ptr actor) - { - pushGuiMode(MWGui::GM_Companion); - mCompanionWindow->openCompanion(actor); - } - void WindowManager::changePointer(const std::string &name) { MyGUI::PointerManager::getInstance().setPointer(name); @@ -1679,7 +1534,6 @@ namespace MWGui void WindowManager::showSoulgemDialog(MWWorld::Ptr item) { mSoulgemDialog->show(item); - MWBase::Environment::get().getInputManager()->changeInputMode(isGuiMode()); updateVisible(); } @@ -1723,14 +1577,9 @@ namespace MWGui return mLoadingScreen; } - void WindowManager::startRecharge(MWWorld::Ptr soulgem) - { - mRecharge->start(soulgem); - } - bool WindowManager::getCursorVisible() { - return mCursorVisible; + return mCursorVisible && mCursorActive; } void WindowManager::trackWindow(Layout *layout, const std::string &name) @@ -1764,37 +1613,25 @@ namespace MWGui void WindowManager::clear() { + for (WindowBase* window : mWindows) + window->clear(); + if (mLocalMapRender) mLocalMapRender->clear(); - mMap->clear(); - mQuickKeysMenu->clear(); mMessageBoxManager->clear(); - mTrainingWindow->resetReference(); - mDialogueWindow->resetReference(); - mTradeWindow->resetReference(); - mSpellBuyingWindow->resetReference(); - mSpellCreationDialog->resetReference(); - mEnchantingDialog->resetReference(); - mContainerWindow->resetReference(); - mCompanionWindow->resetReference(); - mConsole->resetReference(); - - mToolTips->setFocusObject(MWWorld::ConstPtr()); - - mInventoryWindow->clear(); + mToolTips->setFocusObject(MWWorld::Ptr()); mSelectedSpell.clear(); - mCustomMarkers.clear(); mForceHidden = GW_None; + mRestAllowed = true; - setWerewolfOverlay(false); + while (!mGuiModes.empty()) + popGuiMode(); - mGuiModes.clear(); - MWBase::Environment::get().getInputManager()->changeInputMode(false); updateVisible(); } @@ -1852,7 +1689,7 @@ namespace MWGui { return !MyGUI::InputManager::getInstance().isModalAny() // TODO: remove this, once we have properly serialized the state of open windows - && (!isGuiMode() || (mGuiModes.size() == 1 && (getMode() == GM_MainMenu || getMode() == GM_Rest || getMode() == GM_RestBed))); + && (!isGuiMode() || (mGuiModes.size() == 1 && (getMode() == GM_MainMenu || getMode() == GM_Rest))); } void WindowManager::playVideo(const std::string &name, bool allowSkipping) @@ -1937,16 +1774,50 @@ namespace MWGui void WindowManager::exitCurrentModal() { if (!mCurrentModals.empty()) - mCurrentModals.top()->exit(); + { + if (!mCurrentModals.back()->exit()) + return; + mCurrentModals.back()->setVisible(false); + } + } + + void WindowManager::addCurrentModal(WindowModal *input) + { + if (mCurrentModals.empty()) + mKeyboardNavigation->saveFocus(getMode()); + + mCurrentModals.push_back(input); + mKeyboardNavigation->restoreFocus(-1); + + mKeyboardNavigation->setModalWindow(input->mMainWidget); + mKeyboardNavigation->setDefaultFocus(input->mMainWidget, input->getDefaultKeyFocus()); } void WindowManager::removeCurrentModal(WindowModal* input) { - // Only remove the top if it matches the current pointer. A lot of things hide their visibility before showing it, - //so just popping the top would cause massive issues. if(!mCurrentModals.empty()) - if(input == mCurrentModals.top()) - mCurrentModals.pop(); + { + if(input == mCurrentModals.back()) + { + mCurrentModals.pop_back(); + mKeyboardNavigation->saveFocus(-1); + } + else + { + auto found = std::find(mCurrentModals.begin(), mCurrentModals.end(), input); + if (found != mCurrentModals.end()) + mCurrentModals.erase(found); + else + std::cerr << " warning: can't find modal window " << input << std::endl; + } + } + if (mCurrentModals.empty()) + { + mKeyboardNavigation->setModalWindow(NULL); + mKeyboardNavigation->restoreFocus(getMode()); + } + else + mKeyboardNavigation->setModalWindow(mCurrentModals.back()->mMainWidget); } void WindowManager::onVideoKeyPressed(MyGUI::Widget *_sender, MyGUI::KeyCode _key, MyGUI::Char _char) @@ -1989,25 +1860,25 @@ namespace MWGui updateVisible(); } - void WindowManager::fadeScreenIn(const float time, bool clearQueue) + void WindowManager::fadeScreenIn(const float time, bool clearQueue, float delay) { if (clearQueue) mScreenFader->clearQueue(); - mScreenFader->fadeOut(time); + mScreenFader->fadeOut(time, delay); } - void WindowManager::fadeScreenOut(const float time, bool clearQueue) + void WindowManager::fadeScreenOut(const float time, bool clearQueue, float delay) { if (clearQueue) mScreenFader->clearQueue(); - mScreenFader->fadeIn(time); + mScreenFader->fadeIn(time, delay); } - void WindowManager::fadeScreenTo(const int percent, const float time, bool clearQueue) + void WindowManager::fadeScreenTo(const int percent, const float time, bool clearQueue, float delay) { if (clearQueue) mScreenFader->clearQueue(); - mScreenFader->fadeTo(percent, time); + mScreenFader->fadeTo(percent, time, delay); } void WindowManager::setBlindness(const int percent) @@ -2074,54 +1945,20 @@ namespace MWGui void WindowManager::playSound(const std::string& soundId, float volume, float pitch) { + if (soundId.empty()) + return; MWBase::Environment::get().getSoundManager()->playSound(soundId, volume, pitch, MWSound::Type::Sfx, MWSound::PlayMode::NoEnv); } - void WindowManager::setConsoleSelectedObject(const MWWorld::Ptr &object) - { - mConsole->setSelectedObject(object); - } - void WindowManager::updateSpellWindow() { if (mSpellWindow) mSpellWindow->updateSpells(); } - void WindowManager::startTravel(const MWWorld::Ptr &actor) - { - pushGuiMode(GM_Travel); - mTravelWindow->startTravel(actor); - } - - void WindowManager::startSpellBuying(const MWWorld::Ptr &actor) - { - pushGuiMode(GM_SpellBuying); - mSpellBuyingWindow->startSpellBuying(actor, 0); - } - - void WindowManager::startTrade(const MWWorld::Ptr &actor) - { - pushGuiMode(GM_Barter); - mTradeWindow->startTrade(actor); - } - - void WindowManager::openContainer(const MWWorld::Ptr &container, bool loot) - { - pushGuiMode(GM_Container); - mContainerWindow->openContainer(container, loot); - } - - void WindowManager::showBook(const MWWorld::Ptr &item, bool showTakeButton) - { - pushGuiMode(GM_Book); - mBookWindow->openBook(item, showTakeButton); - } - - void WindowManager::showScroll(const MWWorld::Ptr &item, bool showTakeButton) + void WindowManager::setConsoleSelectedObject(const MWWorld::Ptr &object) { - pushGuiMode(GM_Scroll); - mScrollWindow->openScroll(item, showTakeButton); + mConsole->setSelectedObject(object); } std::string WindowManager::correctIconPath(const std::string& path) @@ -2240,4 +2077,41 @@ namespace MWGui return mTextColours; } + bool WindowManager::injectKeyPress(MyGUI::KeyCode key, unsigned int text) + { + if (!mKeyboardNavigation->injectKeyPress(key, text)) + { + MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getKeyFocusWidget(); + bool widgetActive = MyGUI::InputManager::getInstance().injectKeyPress(key, text); + if (!widgetActive || !focus) + return false; + // FIXME: MyGUI doesn't allow widgets to state if a given key was actually used, so make a guess + if (focus->getTypeName().find("Button") != std::string::npos) + { + switch (key.getValue()) + { + case MyGUI::KeyCode::ArrowDown: + case MyGUI::KeyCode::ArrowUp: + case MyGUI::KeyCode::ArrowLeft: + case MyGUI::KeyCode::ArrowRight: + case MyGUI::KeyCode::Return: + case MyGUI::KeyCode::NumpadEnter: + case MyGUI::KeyCode::Space: + return true; + default: + return false; + } + } + return false; + } + else + return true; + } + + void WindowManager::GuiModeState::update(bool visible) + { + for (unsigned int i=0; isetVisible(visible); + } + } diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index e3cbd51f6..453d55fc3 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -122,6 +122,7 @@ namespace MWGui class ScreenFader; class DebugWindow; class JailScreen; + class KeyboardNavigation; class WindowManager : public MWBase::WindowManager { @@ -145,21 +146,15 @@ namespace MWGui /// (and will continually update the window while doing so) virtual void playVideo(const std::string& name, bool allowSkipping); - /** - * Should be called each frame to update windows/gui elements. - * This could mean updating sizes of gui elements or opening - * new dialogs. - */ - virtual void update(); - /// Warning: do not use MyGUI::InputManager::setKeyFocusWidget directly. Instead use this. virtual void setKeyFocusWidget (MyGUI::Widget* widget); virtual void setNewGame(bool newgame); - virtual void pushGuiMode(GuiMode mode); - virtual void popGuiMode(); - virtual void removeGuiMode(GuiMode mode); ///< can be anywhere in the stack + virtual void pushGuiMode(GuiMode mode, const MWWorld::Ptr& arg); + virtual void pushGuiMode (GuiMode mode); + virtual void popGuiMode(bool noSound=false); + virtual void removeGuiMode(GuiMode mode, bool noSound=false); ///< can be anywhere in the stack virtual void goToJail(int days); @@ -184,7 +179,6 @@ namespace MWGui virtual bool isAllowed(GuiWindow wnd) const; /// \todo investigate, if we really need to expose every single lousy UI element to the outside world - virtual MWGui::DialogueWindow* getDialogueWindow(); virtual MWGui::InventoryWindow* getInventoryWindow(); virtual MWGui::CountDialog* getCountDialog(); virtual MWGui::ConfirmationDialog* getConfirmationDialog(); @@ -268,8 +262,8 @@ namespace MWGui virtual void showCrosshair(bool show); virtual bool getSubtitlesEnabled(); - /// Turn visibility of *all* GUI elements on or off (HUD and all windows, except the console) - virtual bool toggleGui(); + /// Turn visibility of HUD on or off + virtual bool toggleHud(); virtual void disallowMouse(); virtual void allowMouse(); @@ -334,21 +328,6 @@ namespace MWGui virtual void updatePlayer(); - virtual void showCompanionWindow(MWWorld::Ptr actor); - virtual void startSpellMaking(MWWorld::Ptr actor); - virtual void startEnchanting(MWWorld::Ptr actor); - virtual void startSelfEnchanting(MWWorld::Ptr soulgem); - virtual void startTraining(MWWorld::Ptr actor); - virtual void startRepair(MWWorld::Ptr actor); - virtual void startRepairItem(MWWorld::Ptr item); - virtual void startRecharge(MWWorld::Ptr soulgem); - virtual void startTravel(const MWWorld::Ptr& actor); - virtual void startSpellBuying(const MWWorld::Ptr &actor); - virtual void startTrade(const MWWorld::Ptr &actor); - virtual void openContainer(const MWWorld::Ptr &container, bool loot); - virtual void showBook(const MWWorld::Ptr& item, bool showTakeButton); - virtual void showScroll(const MWWorld::Ptr& item, bool showTakeButton); - virtual void showSoulgemDialog (MWWorld::Ptr item); virtual void changePointer (const std::string& name); @@ -361,6 +340,9 @@ namespace MWGui virtual bool getCursorVisible(); + /// Call when mouse cursor or buttons are used. + virtual void setCursorActive(bool active); + /// Clear all savegame-specific data virtual void clear(); @@ -376,7 +358,7 @@ namespace MWGui /// Sets the current Modal /** Used to send exit command to active Modal when Esc is pressed **/ - virtual void addCurrentModal(WindowModal* input) {mCurrentModals.push(input);} + virtual void addCurrentModal(WindowModal* input); /// Removes the top Modal /** Used when one Modal adds another Modal @@ -386,11 +368,11 @@ namespace MWGui virtual void pinWindow (MWGui::GuiWindow window); /// Fade the screen in, over \a time seconds - virtual void fadeScreenIn(const float time, bool clearQueue); + virtual void fadeScreenIn(const float time, bool clearQueue, float delay); /// Fade the screen out to black, over \a time seconds - virtual void fadeScreenOut(const float time, bool clearQueue); + virtual void fadeScreenOut(const float time, bool clearQueue, float delay); /// Fade the screen to a specified percentage of black, over \a time seconds - virtual void fadeScreenTo(const int percent, const float time, bool clearQueue); + virtual void fadeScreenTo(const int percent, const float time, bool clearQueue, float delay); /// Darken the screen to a specified percentage virtual void setBlindness(const int percent); @@ -417,6 +399,8 @@ namespace MWGui virtual const MWGui::TextColours& getTextColours(); + virtual bool injectKeyPress(MyGUI::KeyCode key, unsigned int text); + private: const MWWorld::ESMStore* mStore; Resource::ResourceSystem* mResourceSystem; @@ -437,7 +421,7 @@ namespace MWGui MWWorld::Ptr mSelectedEnchantItem; MWWorld::Ptr mSelectedWeapon; - std::stack mCurrentModals; + std::vector mCurrentModals; // Markers placed manually by the player. Must be shared between both map views (the HUD map and the map window). CustomMarkerCollection mCustomMarkers; @@ -445,38 +429,24 @@ namespace MWGui HUD *mHud; MapWindow *mMap; MWRender::LocalMap* mLocalMapRender; - MainMenu *mMenu; ToolTips *mToolTips; StatsWindow *mStatsWindow; MessageBoxManager *mMessageBoxManager; Console *mConsole; - JournalWindow* mJournal; DialogueWindow *mDialogueWindow; - ContainerWindow *mContainerWindow; DragAndDrop* mDragAndDrop; InventoryWindow *mInventoryWindow; ScrollWindow* mScrollWindow; BookWindow* mBookWindow; CountDialog* mCountDialog; TradeWindow* mTradeWindow; - SpellBuyingWindow* mSpellBuyingWindow; - TravelWindow* mTravelWindow; SettingsWindow* mSettingsWindow; ConfirmationDialog* mConfirmationDialog; - AlchemyWindow* mAlchemyWindow; SpellWindow* mSpellWindow; QuickKeysMenu* mQuickKeysMenu; LoadingScreen* mLoadingScreen; - LevelupDialog* mLevelupDialog; WaitDialog* mWaitDialog; - SpellCreationDialog* mSpellCreationDialog; - EnchantingDialog* mEnchantingDialog; - TrainingWindow* mTrainingWindow; - MerchantRepair* mMerchantRepair; SoulgemDialog* mSoulgemDialog; - Repair* mRepair; - Recharge* mRecharge; - CompanionWindow* mCompanionWindow; MyGUI::ImageBox* mVideoBackground; VideoWidget* mVideoWidget; ScreenFader* mWerewolfFader; @@ -486,6 +456,8 @@ namespace MWGui DebugWindow* mDebugWindow; JailScreen* mJailScreen; + std::vector mWindows; + Translation::Storage& mTranslationDataStorage; CharacterCreation* mCharGen; @@ -497,8 +469,8 @@ namespace MWGui bool mHitFaderEnabled; bool mWerewolfOverlayEnabled; bool mHudEnabled; - bool mGuiEnabled; bool mCursorVisible; + bool mCursorActive; void setCursorVisible(bool visible); @@ -511,6 +483,27 @@ namespace MWGui std::map mPlayerSkillValues; MyGUI::Gui *mGui; // Gui + + struct GuiModeState + { + GuiModeState(WindowBase* window) + { + mWindows.push_back(window); + } + GuiModeState(const std::vector& windows) + : mWindows(windows) {} + GuiModeState() {} + + void update(bool visible); + + std::vector mWindows; + + std::string mCloseSound; + std::string mOpenSound; + }; + // Defines the windows that should be shown in a particular GUI mode. + std::map mGuiModeStates; + // The currently active stack of GUI modes (top mode is the one we are in). std::vector mGuiModes; SDLUtil::SDLCursorManager* mCursorManager; @@ -542,6 +535,8 @@ namespace MWGui MWGui::TextColours mTextColours; + std::unique_ptr mKeyboardNavigation; + /** * Called when MyGUI tries to retrieve a tag's value. Tags must be denoted in #{tag} notation and will be replaced upon setting a user visible text/property. * Supported syntax: diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 301a73d6f..b80571ad8 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -174,28 +174,50 @@ namespace MWInput void InputManager::setPlayerControlsEnabled(bool enabled) { - int nPlayerChannels = 18; - int playerChannels[] = {A_Activate, A_AutoMove, A_AlwaysRun, A_ToggleWeapon, + int playerChannels[] = {A_AutoMove, A_AlwaysRun, A_ToggleWeapon, A_ToggleSpell, A_Rest, A_QuickKey1, A_QuickKey2, A_QuickKey3, A_QuickKey4, A_QuickKey5, A_QuickKey6, A_QuickKey7, A_QuickKey8, A_QuickKey9, A_QuickKey10, A_Use, A_Journal}; - for(int i = 0; i < nPlayerChannels; i++) { + for(size_t i = 0; i < sizeof(playerChannels)/sizeof(playerChannels[0]); i++) { int pc = playerChannels[i]; mInputBinder->getChannel(pc)->setEnabled(enabled); } } - void InputManager::channelChanged(ICS::Channel* channel, float currentValue, float previousValue) + void InputManager::handleGuiArrowKey(int action) { - if (mDragDrop) - return; + MyGUI::KeyCode key; + switch (action) + { + case A_MoveLeft: + key = MyGUI::KeyCode::ArrowLeft; + break; + case A_MoveRight: + key = MyGUI::KeyCode::ArrowRight; + break; + case A_MoveForward: + key = MyGUI::KeyCode::ArrowUp; + break; + case A_MoveBackward: + default: + key = MyGUI::KeyCode::ArrowDown; + break; + } + + MWBase::Environment::get().getWindowManager()->injectKeyPress(key, 0); + } + void InputManager::channelChanged(ICS::Channel* channel, float currentValue, float previousValue) + { resetIdleTime (); int action = channel->getNumber(); + if (mDragDrop && action != A_GameMenu && action != A_Inventory) + return; + if((previousValue == 1 || previousValue == 0) && (currentValue==1 || currentValue==0)) { //Is a normal button press, so don't change it at all @@ -231,9 +253,7 @@ namespace MWInput switch (action) { case A_GameMenu: - if(!(MWBase::Environment::get().getStateManager()->getState() != MWBase::StateManager::State_Running - && MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_MainMenu)) - toggleMainMenu (); + toggleMainMenu (); break; case A_Screenshot: screenshot(); @@ -246,8 +266,13 @@ namespace MWInput break; case A_Activate: resetIdleTime(); - if (!MWBase::Environment::get().getWindowManager()->isGuiMode()) - activate(); + activate(); + break; + case A_MoveLeft: + case A_MoveRight: + case A_MoveForward: + case A_MoveBackward: + handleGuiArrowKey(action); break; case A_Journal: toggleJournal (); @@ -301,7 +326,7 @@ namespace MWInput showQuickKeysMenu(); break; case A_ToggleHUD: - MWBase::Environment::get().getWindowManager()->toggleGui(); + MWBase::Environment::get().getWindowManager()->toggleHud(); break; case A_ToggleDebug: MWBase::Environment::get().getWindowManager()->toggleDebugWindow(); @@ -380,8 +405,6 @@ namespace MWInput mInputManager->setMouseVisible(MWBase::Environment::get().getWindowManager()->getCursorVisible()); mInputManager->capture(disableEvents); - // inject some fake mouse movement to force updating MyGUI's widget states - MyGUI::InputManager::getInstance().injectMouseMove( int(mGuiCursorX), int(mGuiCursorY), mMouseWheel); if (mControlsDisabled) { @@ -685,13 +708,17 @@ namespace MWInput SDL_StopTextInput(); bool consumed = false; - if (kc != OIS::KC_UNASSIGNED) + if (kc != OIS::KC_UNASSIGNED && !mInputBinder->detectingBindingState()) { - consumed = SDL_IsTextInputActive() && - ( !(SDLK_SCANCODE_MASK & arg.keysym.sym) && std::isprint(arg.keysym.sym)); // Little trick to check if key is printable - bool guiFocus = MyGUI::InputManager::getInstance().injectKeyPress(MyGUI::KeyCode::Enum(kc), 0); - setPlayerControlsEnabled(!guiFocus); + consumed = MWBase::Environment::get().getWindowManager()->injectKeyPress(MyGUI::KeyCode::Enum(kc), 0); + if (SDL_IsTextInputActive() && // Little trick to check if key is printable + ( !(SDLK_SCANCODE_MASK & arg.keysym.sym) && std::isprint(arg.keysym.sym))) + consumed = true; + setPlayerControlsEnabled(!consumed); } + if (arg.repeat) + return; + if (!mControlsDisabled && !consumed) mInputBinder->keyPressed (arg); mJoystickLastUsed = false; @@ -710,7 +737,8 @@ namespace MWInput mJoystickLastUsed = false; OIS::KeyCode kc = mInputManager->sdl2OISKeyCode(arg.keysym.sym); - setPlayerControlsEnabled(!MyGUI::InputManager::getInstance().injectKeyRelease(MyGUI::KeyCode::Enum(kc))); + if (!mInputBinder->detectingBindingState()) + setPlayerControlsEnabled(!MyGUI::InputManager::getInstance().injectKeyRelease(MyGUI::KeyCode::Enum(kc))); mInputBinder->keyReleased (arg); } @@ -731,6 +759,7 @@ namespace MWInput MWBase::Environment::get().getWindowManager()->playSound("Menu Click"); } } + MWBase::Environment::get().getWindowManager()->setCursorActive(true); } setPlayerControlsEnabled(!guiMode); @@ -775,6 +804,10 @@ namespace MWInput mMouseWheel = int(arg.z); MyGUI::InputManager::getInstance().injectMouseMove( int(mGuiCursorX), int(mGuiCursorY), mMouseWheel); + // FIXME: inject twice to force updating focused widget states (tooltips) resulting from changing the viewport by scroll wheel + MyGUI::InputManager::getInstance().injectMouseMove( int(mGuiCursorX), int(mGuiCursorY), mMouseWheel); + + MWBase::Environment::get().getWindowManager()->setCursorActive(true); } if (mMouseLookEnabled && !mControlsDisabled) @@ -1084,12 +1117,10 @@ namespace MWInput && MWBase::Environment::get().getWindowManager()->getMode() != MWGui::GM_MainMenu && MWBase::Environment::get().getWindowManager ()->getJournalAllowed()) { - MWBase::Environment::get().getWindowManager()->playSound ("book open"); MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Journal); } else if(MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_Journal)) { - MWBase::Environment::get().getWindowManager()->playSound ("book close"); MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Journal); } } @@ -1126,7 +1157,9 @@ namespace MWInput void InputManager::activate() { - if (mControlSwitch["playercontrols"]) + if (MWBase::Environment::get().getWindowManager()->isGuiMode()) + MWBase::Environment::get().getWindowManager()->injectKeyPress(MyGUI::KeyCode::Return, 0); + else if (mControlSwitch["playercontrols"]) mPlayer->activate(); } @@ -1261,6 +1294,17 @@ namespace MWInput control->setInitialValue(0.0f); mInputBinder->addMouseButtonBinding (control, defaultMouseButtonBindings[i], ICS::Control::INCREASE); } + + if (i == A_LookLeftRight && !mInputBinder->isKeyBound(SDL_SCANCODE_KP_4) && !mInputBinder->isKeyBound(SDL_SCANCODE_KP_6)) + { + mInputBinder->addKeyBinding(control, SDL_SCANCODE_KP_6, ICS::Control::INCREASE); + mInputBinder->addKeyBinding(control, SDL_SCANCODE_KP_4, ICS::Control::DECREASE); + } + if (i == A_LookUpDown && !mInputBinder->isKeyBound(SDL_SCANCODE_KP_8) && !mInputBinder->isKeyBound(SDL_SCANCODE_KP_2)) + { + mInputBinder->addKeyBinding(control, SDL_SCANCODE_KP_2, ICS::Control::INCREASE); + mInputBinder->addKeyBinding(control, SDL_SCANCODE_KP_8, ICS::Control::DECREASE); + } } } } diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index 8809f44cd..cba7fc743 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -214,6 +214,7 @@ namespace MWInput void updateIdleTime(float dt); void setPlayerControlsEnabled(bool enabled); + void handleGuiArrowKey(int action); void updateCursorMode(); diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index f745fbf67..f964a81e9 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -88,7 +88,9 @@ void adjustBoundItem (const std::string& item, bool bound, const MWWorld::Ptr& a } else { - actor.getClass().getContainerStore(actor).remove(item, 1, actor); + MWWorld::Ptr itemPtr = actor.getClass().getInventoryStore(actor).search(item); + if (!itemPtr.isEmpty()) + actor.getClass().getInventoryStore(actor).remove(itemPtr, 1, actor, true); } } @@ -1383,13 +1385,17 @@ namespace MWMechanics float sqrHeadTrackDistance = std::numeric_limits::max(); MWWorld::Ptr headTrackTarget; - for(PtrActorMap::iterator it(mActors.begin()); it != mActors.end(); ++it) + // Unconsious actor can not track target + if (!iter->first.getClass().getCreatureStats(iter->first).getKnockedDown()) { - if (it->first == iter->first) - continue; - updateHeadTracking(iter->first, it->first, headTrackTarget, sqrHeadTrackDistance); + for(PtrActorMap::iterator it(mActors.begin()); it != mActors.end(); ++it) + { + if (it->first == iter->first) + continue; + updateHeadTracking(iter->first, it->first, headTrackTarget, sqrHeadTrackDistance); + } + iter->second->getCharacterController()->setHeadTrackTarget(headTrackTarget); } - iter->second->getCharacterController()->setHeadTrackTarget(headTrackTarget); } if (iter->first.getClass().isNpc() && iter->first != player && (isLocalActor || isAIActive)) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 6f7e25a68..a1c9723ef 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -866,16 +866,35 @@ namespace MWMechanics mAI = true; } - bool MechanicsManager::isAllowedToUse (const MWWorld::Ptr& ptr, const MWWorld::ConstPtr& item, MWWorld::Ptr& victim) + bool MechanicsManager::isAllowedToUse (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target, MWWorld::Ptr& victim) { - const MWWorld::CellRef& cellref = item.getCellRef(); + if (target.isEmpty()) + return true; + + const MWWorld::CellRef& cellref = target.getCellRef(); // there is no harm to use unlocked doors - if (item.getClass().isDoor() && cellref.getLockLevel() <= 0 && ptr.getCellRef().getTrap().empty()) + if (target.getClass().isDoor() && cellref.getLockLevel() <= 0 && ptr.getCellRef().getTrap().empty()) + return true; + + // TODO: implement a better check to check if target is owned bed + if (target.getClass().isActivator() && target.getClass().getScript(target).compare(0, 3, "Bed") != 0) 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) + if (target.getClass().isNpc()) + { + if (target.getClass().getCreatureStats(target).isDead()) + return true; + + if (target.getClass().getCreatureStats(target).getAiSequence().isInCombat()) + return true; + + // check if a player tries to pickpocket a target NPC + if(ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Sneak) + || target.getClass().getCreatureStats(target).getKnockedDown()) + return false; + return true; + } const std::string& owner = cellref.getOwner(); bool isOwned = !owner.empty() && owner != "player"; @@ -1110,6 +1129,10 @@ namespace MWMechanics if (it->getClass().getCreatureStats(*it).isDead()) continue; + // Unconsious actor can not report about crime + if (it->getClass().getCreatureStats(*it).getKnockedDown()) + continue; + if ((*it == victim && victimAware) || (MWBase::Environment::get().getWorld()->getLOS(player, *it) && awarenessCheck(player, *it) ) // Murder crime can be reported even if no one saw it (hearing is enough, I guess). @@ -1232,12 +1255,16 @@ namespace MWMechanics // Tell everyone (including the original reporter) in alarm range for (std::vector::iterator it = neighbors.begin(); it != neighbors.end(); ++it) { - if ( *it == player + if (*it == player || !it->getClass().isNpc() || it->getClass().getCreatureStats(*it).isDead()) continue; if (it->getClass().getCreatureStats(*it).getAiSequence().isInCombat(victim)) continue; + // Unconsious actor can not report about crime and should not become hostile + if (it->getClass().getCreatureStats(*it).getKnockedDown()) + continue; + // Player's followers should not attack player, or try to arrest him if (it->getClass().getCreatureStats(*it).getAiSequence().hasPackage(AiPackage::TypeIdFollow)) { diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index af0863381..25c2dae3d 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -214,8 +214,8 @@ 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::ConstPtr& item, MWWorld::Ptr& victim); + /// @return is \a ptr allowed to take/use \a target or is it a crime? + virtual bool isAllowedToUse (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target, MWWorld::Ptr& victim); virtual void setWerewolf(const MWWorld::Ptr& actor, bool werewolf); virtual void applyWerewolfAcrobatics(const MWWorld::Ptr& actor); diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 3a66c264b..3826fd5f6 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -251,6 +251,10 @@ namespace MWRender sceneRoot->setNodeMask(Mask_Scene); sceneRoot->setName("Scene Root"); + mUniformRainIntensity = new osg::Uniform("rainIntensity",(float) 0.0); + + mRootNode->getOrCreateStateSet()->addUniform(mUniformRainIntensity); + mSky.reset(new SkyManager(sceneRoot, resourceSystem->getSceneManager())); source->setStateSetModes(*mRootNode->getOrCreateStateSet(), osg::StateAttribute::ON); @@ -500,6 +504,9 @@ namespace MWRender mWater->update(dt); } + if (!mSky->isEnabled() || !mSky->hasRain()) + clearRainRipples(); + mCamera->update(dt, paused); osg::Vec3f focal, cameraPos; @@ -801,6 +808,11 @@ namespace MWRender mWater->clearRipples(); } + void RenderingManager::clearRainRipples() + { + mUniformRainIntensity->set((float) 0.0); + } + void RenderingManager::clear() { mSky->setMoonColour(false); diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index f0087e43d..43059ab6d 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -85,6 +85,7 @@ namespace MWRender osg::Uniform* mUniformNear; osg::Uniform* mUniformFar; + osg::Uniform* mUniformRainIntensity; void preloadCommonAssets(); @@ -159,6 +160,8 @@ namespace MWRender /// Clear all worldspace-specific data void notifyWorldSpaceChanged(); + void clearRainRipples(); + void update(float dt, bool paused); Animation* getAnimation(const MWWorld::Ptr& ptr); diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index e6aa013ec..178c753c6 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -1305,13 +1305,19 @@ public: std::vector > mAlphaFaders; }; -private: +protected: float mAlpha; }; class RainFader : public AlphaFader { public: + + RainFader(osg::Uniform *rainIntensityUniform): AlphaFader() + { + mRainIntensityUniform = rainIntensityUniform; + } + virtual void setDefaults(osg::StateSet* stateset) { osg::ref_ptr mat (new osg::Material); @@ -1320,6 +1326,15 @@ public: mat->setColorMode(osg::Material::OFF); stateset->setAttributeAndModes(mat, osg::StateAttribute::ON); } + + virtual void apply(osg::StateSet *stateset, osg::NodeVisitor *nv) + { + AlphaFader::apply(stateset,nv); + mRainIntensityUniform->set((float) (mAlpha * 2.0)); // mAlpha is limited to 0.6 so multiply by 2 to reach full intensity + } + +protected: + osg::Uniform* mRainIntensityUniform; }; void SkyManager::createRain() @@ -1375,7 +1390,7 @@ void SkyManager::createRain() mRainNode->addChild(mRainParticleSystem); mRainNode->addChild(updater); - mRainFader = new RainFader; + mRainFader = new RainFader(mRootNode->getParent(0)->getParent(0)->getStateSet()->getUniform("rainIntensity")); mRainNode->addUpdateCallback(mRainFader); mRainNode->addCullCallback(mUnderwaterSwitch); mRainNode->setNodeMask(Mask_WeatherParticles); @@ -1416,6 +1431,16 @@ int SkyManager::getSecundaPhase() const return mSecunda->getPhaseInt(); } +bool SkyManager::isEnabled() +{ + return mEnabled; +} + +bool SkyManager::hasRain() +{ + return mRainNode != NULL; +} + void SkyManager::update(float duration) { if (!mEnabled) return; diff --git a/apps/openmw/mwrender/sky.hpp b/apps/openmw/mwrender/sky.hpp index 863ecc77d..59a8ddc4e 100644 --- a/apps/openmw/mwrender/sky.hpp +++ b/apps/openmw/mwrender/sky.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -137,6 +138,10 @@ namespace MWRender void sunDisable(); + bool isEnabled(); + + bool hasRain(); + void setRainSpeed(float speed); void setStormDirection(const osg::Vec3f& direction); diff --git a/apps/openmw/mwscript/dialogueextensions.cpp b/apps/openmw/mwscript/dialogueextensions.cpp index 3295134e6..1e488467b 100644 --- a/apps/openmw/mwscript/dialogueextensions.cpp +++ b/apps/openmw/mwscript/dialogueextensions.cpp @@ -23,6 +23,7 @@ #include "../mwbase/dialoguemanager.hpp" #include "../mwbase/journal.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/class.hpp" #include "../mwmechanics/npcstats.hpp" @@ -163,7 +164,7 @@ namespace MWScript runtime.pop(); arg0 = arg0 -1; } - dialogue->askQuestion(question,choice); + dialogue->addChoice(question,choice); } } }; @@ -187,7 +188,7 @@ namespace MWScript preventing infinite greeting loops */ if (!MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_Dialogue)) - MWBase::Environment::get().getDialogueManager()->startDialogue(ptr); + MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Dialogue, ptr); /* End of tes3mp change (major) */ diff --git a/apps/openmw/mwscript/guiextensions.cpp b/apps/openmw/mwscript/guiextensions.cpp index 254da56d6..cea176ff6 100644 --- a/apps/openmw/mwscript/guiextensions.cpp +++ b/apps/openmw/mwscript/guiextensions.cpp @@ -57,7 +57,7 @@ namespace MWScript if (bed.isEmpty() || !MWBase::Environment::get().getMechanicsManager()->sleepInBed(MWMechanics::getPlayer(), bed)) - MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_RestBed); + MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Rest, bed); } }; @@ -209,7 +209,7 @@ namespace MWScript public: virtual void execute(Interpreter::Runtime &runtime) { - bool state = MWBase::Environment::get().getWindowManager()->toggleGui(); + bool state = MWBase::Environment::get().getWindowManager()->toggleHud(); runtime.getContext().report(state ? "GUI -> On" : "GUI -> Off"); if (!state) diff --git a/apps/openmw/mwworld/actionopen.cpp b/apps/openmw/mwworld/actionopen.cpp index 0df451b18..7ff6dd068 100644 --- a/apps/openmw/mwworld/actionopen.cpp +++ b/apps/openmw/mwworld/actionopen.cpp @@ -10,9 +10,8 @@ namespace MWWorld { - ActionOpen::ActionOpen (const MWWorld::Ptr& container, bool loot) + ActionOpen::ActionOpen (const MWWorld::Ptr& container) : Action (false, container) - , mLoot(loot) { } @@ -23,6 +22,6 @@ namespace MWWorld MWMechanics::diseaseContact(actor, getTarget()); - MWBase::Environment::get().getWindowManager()->openContainer(getTarget(), mLoot); + MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Container, getTarget()); } } diff --git a/apps/openmw/mwworld/actionopen.hpp b/apps/openmw/mwworld/actionopen.hpp index 454cc09f1..3bce25c6b 100644 --- a/apps/openmw/mwworld/actionopen.hpp +++ b/apps/openmw/mwworld/actionopen.hpp @@ -12,12 +12,9 @@ namespace MWWorld virtual void executeImp (const MWWorld::Ptr& actor); public: - ActionOpen (const Ptr& container, bool loot=false); + ActionOpen (const Ptr& container); ///< \param container The Container the Player has activated. - /// \param loot If true, display the "dispose of corpse" button - private: - bool mLoot; }; } diff --git a/apps/openmw/mwworld/actionread.cpp b/apps/openmw/mwworld/actionread.cpp index afdcfdda2..8e45f14e8 100644 --- a/apps/openmw/mwworld/actionread.cpp +++ b/apps/openmw/mwworld/actionread.cpp @@ -43,14 +43,12 @@ namespace MWWorld return; } - bool showTakeButton = (getTarget().getContainerStore() != &actor.getClass().getContainerStore(actor)); - LiveCellRef *ref = getTarget().get(); if (ref->mBase->mData.mIsScroll) - MWBase::Environment::get().getWindowManager()->showScroll(getTarget(), showTakeButton); + MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Scroll, getTarget()); else - MWBase::Environment::get().getWindowManager()->showBook(getTarget(), showTakeButton); + MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Book, getTarget()); MWMechanics::NpcStats& npcStats = actor.getClass().getNpcStats (actor); diff --git a/apps/openmw/mwworld/actionread.hpp b/apps/openmw/mwworld/actionread.hpp index 00a4756dd..c23bf2900 100644 --- a/apps/openmw/mwworld/actionread.hpp +++ b/apps/openmw/mwworld/actionread.hpp @@ -16,4 +16,4 @@ namespace MWWorld }; } -#endif // ACTIONOPEN_H +#endif // ACTIONREAD_H diff --git a/apps/openmw/mwworld/actionrepair.cpp b/apps/openmw/mwworld/actionrepair.cpp index 8e19927b8..191cf2063 100644 --- a/apps/openmw/mwworld/actionrepair.cpp +++ b/apps/openmw/mwworld/actionrepair.cpp @@ -23,6 +23,6 @@ namespace MWWorld return; } - MWBase::Environment::get().getWindowManager()->startRepairItem(getTarget()); + MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Repair, getTarget()); } } diff --git a/apps/openmw/mwworld/actiontalk.cpp b/apps/openmw/mwworld/actiontalk.cpp index 051380ff5..c7bb6a26e 100644 --- a/apps/openmw/mwworld/actiontalk.cpp +++ b/apps/openmw/mwworld/actiontalk.cpp @@ -1,7 +1,7 @@ #include "actiontalk.hpp" #include "../mwbase/environment.hpp" -#include "../mwbase/dialoguemanager.hpp" +#include "../mwbase/windowmanager.hpp" namespace MWWorld { @@ -9,6 +9,6 @@ namespace MWWorld void ActionTalk::executeImp (const Ptr& actor) { - MWBase::Environment::get().getDialogueManager()->startDialogue (getTarget()); + MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Dialogue, getTarget()); } } diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 5d04a7af0..ae152646f 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -645,10 +645,16 @@ namespace MWWorld MWBase::Environment::get().getWorld()->positionToIndex (position.pos[0], position.pos[1], x, y); + if (changeEvent) + MWBase::Environment::get().getWindowManager()->fadeScreenOut(0.5); + changeCellGrid(x, y, changeEvent); CellStore* current = MWBase::Environment::get().getWorld()->getExterior(x, y); changePlayerCell(current, position, adjustPlayerPos); + + if (changeEvent) + MWBase::Environment::get().getWindowManager()->fadeScreenIn(0.5); } CellStore* Scene::getCurrentCell () diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index ca83a05cd..4ebefff32 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2218,7 +2218,9 @@ namespace MWWorld pos.z() += heightRatio*2*mPhysics->getRenderingHalfExtents(object).z(); - return isUnderwater(object.getCell(), pos); + const CellStore *currCell = object.isInCell() ? object.getCell() : NULL; // currCell == NULL should only happen for player, during initial startup + + return isUnderwater(currCell, pos); } bool World::isUnderwater(const MWWorld::CellStore* cell, const osg::Vec3f &pos) const diff --git a/components/esm/loadland.cpp b/components/esm/loadland.cpp index 72c3eb98d..f3f72e88a 100644 --- a/components/esm/loadland.cpp +++ b/components/esm/loadland.cpp @@ -175,6 +175,45 @@ namespace ESM } + void Land::blank() + { + mPlugin = 0; + + for (int i = 0; i < LAND_GLOBAL_MAP_LOD_SIZE; ++i) + mWnam[0] = 0; + + if (!mLandData) + mLandData = new LandData; + + mLandData->mHeightOffset = 0; + for (int i = 0; i < LAND_NUM_VERTS; ++i) + mLandData->mHeights[i] = 0; + mLandData->mMinHeight = 0; + mLandData->mMaxHeight = 0; + for (int i = 0; i < LAND_NUM_VERTS; ++i) + { + mLandData->mNormals[i*3+0] = 0; + mLandData->mNormals[i*3+1] = 0; + mLandData->mNormals[i*3+2] = 127; + } + for (int i = 0; i < LAND_NUM_TEXTURES; ++i) + mLandData->mTextures[i] = 0; + for (int i = 0; i < LAND_NUM_VERTS; ++i) + { + mLandData->mColours[i*3+0] = -1; + mLandData->mColours[i*3+1] = -1; + mLandData->mColours[i*3+2] = -1; + } + mLandData->mUnk1 = 0; + mLandData->mUnk2 = 0; + mLandData->mDataLoaded = Land::DATA_VNML | Land::DATA_VHGT | Land::DATA_WNAM | + Land::DATA_VCLR | Land::DATA_VTEX; + mDataTypes = mLandData->mDataLoaded; + + // No file associated with the land now + mContext.filename.clear(); + } + void Land::loadData(int flags, LandData* target) const { // Create storage if nothing is loaded @@ -193,6 +232,16 @@ namespace ESM return; } + // Copy data to target if no file + if (mContext.filename.empty()) + { + // Make sure there is data, and that it doesn't point to the same object. + if (mLandData && mLandData != target) + *target = *mLandData; + + return; + } + ESM::ESMReader reader; reader.restoreContext(mContext); @@ -269,6 +318,15 @@ namespace ESM return mLandData && (mLandData->mDataLoaded & flags) == (flags & mDataTypes); } + void Land::setDataLoaded(int flags) + { + if (!mLandData) + mLandData = new LandData; + + mDataTypes |= flags; + mLandData->mDataLoaded |= flags; + } + Land::Land (const Land& land) : mFlags (land.mFlags), mX (land.mX), mY (land.mY), mPlugin (land.mPlugin), mContext (land.mContext), mDataTypes (land.mDataTypes), diff --git a/components/esm/loadland.hpp b/components/esm/loadland.hpp index 9f33c37da..7be954b3e 100644 --- a/components/esm/loadland.hpp +++ b/components/esm/loadland.hpp @@ -31,6 +31,8 @@ struct Land // File context. This allows the ESM reader to be 'reset' to this // location later when we are ready to load the full data set. + // In the editor, there may not be a file associated with the Land, + // in which case the filename will be empty. ESM_Context mContext; int mDataTypes; @@ -64,6 +66,8 @@ struct Land //total number of textures per land static const int LAND_NUM_TEXTURES = LAND_TEXTURE_SIZE * LAND_TEXTURE_SIZE; + static const int LAND_GLOBAL_MAP_LOD_SIZE = 81; + #pragma pack(push,1) struct VHGT { @@ -109,12 +113,12 @@ struct Land }; // low-LOD heightmap (used for rendering the global map) - signed char mWnam[81]; + signed char mWnam[LAND_GLOBAL_MAP_LOD_SIZE]; void load(ESMReader &esm, bool &isDeleted); void save(ESMWriter &esm, bool isDeleted = false) const; - void blank() {} + void blank(); /** * Actually loads data into target @@ -131,6 +135,9 @@ struct Land /// @note We only check data types that *can* be loaded (present in mDataTypes) bool isDataLoaded(int flags) const; + /// Sets the flags and creates a LandData if needed + void setDataLoaded(int flags); + Land (const Land& land); Land& operator= (Land land); diff --git a/components/esm/loadltex.cpp b/components/esm/loadltex.cpp index 613b706e3..3e150f9c0 100644 --- a/components/esm/loadltex.cpp +++ b/components/esm/loadltex.cpp @@ -59,7 +59,7 @@ namespace ESM void LandTexture::blank() { + mId.clear(); mTexture.clear(); - mIndex = -1; } } diff --git a/components/esm/loadltex.hpp b/components/esm/loadltex.hpp index 2cb5abf0c..1604e9281 100644 --- a/components/esm/loadltex.hpp +++ b/components/esm/loadltex.hpp @@ -31,14 +31,15 @@ struct LandTexture /// Return a string descriptor for this record type. Currently used for debugging / error logs only. static std::string getRecordType() { return "LandTexture"; } + // mId is merely a user friendly name for the texture in the editor. std::string mId, mTexture; int mIndex; void load(ESMReader &esm, bool &isDeleted); void save(ESMWriter &esm, bool isDeleted = false) const; + /// Sets the record to the default state. Does not touch the index. Does touch mID. void blank(); - ///< Set record to default state (does not touch the ID). }; } #endif diff --git a/components/esm/loadnpc.hpp b/components/esm/loadnpc.hpp index 5b89f4055..d7f30e079 100644 --- a/components/esm/loadnpc.hpp +++ b/components/esm/loadnpc.hpp @@ -43,10 +43,12 @@ struct NPC Misc = 0x00400, Potions = 0x02000, + AllItems = Weapon|Armor|Clothing|Books|Ingredients|Picks|Probes|Lights|Apparatus|RepairItem|Misc|Potions, + // Other services Spells = 0x00800, MagicItems = 0x01000, - Training = 0x04000, // What skills? + Training = 0x04000, Spellmaking = 0x08000, Enchanting = 0x10000, Repair = 0x20000 diff --git a/components/sdlutil/sdlinputwrapper.cpp b/components/sdlutil/sdlinputwrapper.cpp index a76de00d1..634c4884f 100644 --- a/components/sdlutil/sdlinputwrapper.cpp +++ b/components/sdlutil/sdlinputwrapper.cpp @@ -85,14 +85,11 @@ InputWrapper::InputWrapper(SDL_Window* window, osg::ref_ptr v mMouseListener->mouseReleased(evt.button, evt.button.button); break; case SDL_KEYDOWN: - if (!evt.key.repeat) - { - mKeyboardListener->keyPressed(evt.key); + mKeyboardListener->keyPressed(evt.key); - if (!isModifierHeld(KMOD_ALT) && evt.key.keysym.sym >= SDLK_F1 && evt.key.keysym.sym <= SDLK_F12) - { - mViewer->getEventQueue()->keyPress(osgGA::GUIEventAdapter::KEY_F1 + (evt.key.keysym.sym - SDLK_F1)); - } + if (!isModifierHeld(KMOD_ALT) && evt.key.keysym.sym >= SDLK_F1 && evt.key.keysym.sym <= SDLK_F12) + { + mViewer->getEventQueue()->keyPress(osgGA::GUIEventAdapter::KEY_F1 + (evt.key.keysym.sym - SDLK_F1)); } break; diff --git a/components/terrain/world.cpp b/components/terrain/world.cpp index b7cc0ae01..335bb496b 100644 --- a/components/terrain/world.cpp +++ b/components/terrain/world.cpp @@ -75,4 +75,9 @@ void World::updateTextureFiltering() mTextureManager->updateTextureFiltering(); } +void World::clearAssociatedCaches() +{ + mChunkManager->clearCache(); +} + } diff --git a/components/terrain/world.hpp b/components/terrain/world.hpp index d0576fbd3..e1c3828fc 100644 --- a/components/terrain/world.hpp +++ b/components/terrain/world.hpp @@ -63,6 +63,10 @@ namespace Terrain float getHeightAt (const osg::Vec3f& worldPos); + /// Clears the cached land and landtexture data. + /// @note Thread safe. + virtual void clearAssociatedCaches(); + /// Load a terrain cell at maximum LOD and store it in the View for later use. /// @note Thread safe. virtual void cacheCell(View* view, int x, int y) {} diff --git a/components/widgets/box.cpp b/components/widgets/box.cpp index eeddc22dd..bf18cef5b 100644 --- a/components/widgets/box.cpp +++ b/components/widgets/box.cpp @@ -66,6 +66,13 @@ namespace Gui notifySizeChange (this); } + void AutoSizedEditBox::initialiseOverride() + { + Base::initialiseOverride(); + setNeedKeyFocus(false); + setEditStatic(true); + } + void AutoSizedEditBox::setPropertyOverride(const std::string& _key, const std::string& _value) { if (_key == "ExpandDirection") @@ -238,6 +245,14 @@ namespace Gui align(); } + void HBox::initialiseOverride() + { + Base::initialiseOverride(); + MyGUI::Widget* client = 0; + assignWidget(client, "Client"); + setWidgetClient(client); + } + void HBox::onWidgetCreated(MyGUI::Widget* _widget) { align(); @@ -385,6 +400,14 @@ namespace Gui align(); } + void VBox::initialiseOverride() + { + Base::initialiseOverride(); + MyGUI::Widget* client = 0; + assignWidget(client, "Client"); + setWidgetClient(client); + } + MyGUI::IntSize VBox::getRequestedSize () { MyGUI::IntSize size(0,0); diff --git a/components/widgets/box.hpp b/components/widgets/box.hpp index 70f73ce42..66be00719 100644 --- a/components/widgets/box.hpp +++ b/components/widgets/box.hpp @@ -39,9 +39,12 @@ namespace Gui MYGUI_RTTI_DERIVED( AutoSizedEditBox ) public: + virtual MyGUI::IntSize getRequestedSize(); virtual void setCaption(const MyGUI::UString& _value); + virtual void initialiseOverride(); + protected: virtual void setPropertyOverride(const std::string& _key, const std::string& _value); }; @@ -99,6 +102,8 @@ namespace Gui virtual void setCoord (const MyGUI::IntCoord &_value); protected: + virtual void initialiseOverride(); + virtual void align(); virtual MyGUI::IntSize getRequestedSize(); @@ -116,6 +121,8 @@ namespace Gui virtual void setCoord (const MyGUI::IntCoord &_value); protected: + virtual void initialiseOverride(); + virtual void align(); virtual MyGUI::IntSize getRequestedSize(); diff --git a/components/widgets/imagebutton.cpp b/components/widgets/imagebutton.cpp index 8e3f8ed69..ab0739c2c 100644 --- a/components/widgets/imagebutton.cpp +++ b/components/widgets/imagebutton.cpp @@ -5,6 +5,15 @@ namespace Gui { + ImageButton::ImageButton() + : Base() + , mMouseFocus(false) + , mMousePress(false) + , mKeyFocus(false) + { + setNeedKeyFocus(true); + } + void ImageButton::setPropertyOverride(const std::string &_key, const std::string &_value) { if (_key == "ImageHighlighted") @@ -24,22 +33,36 @@ namespace Gui } void ImageButton::onMouseSetFocus(Widget* _old) { - setImageTexture(mImageHighlighted); - ImageBox::onMouseSetFocus(_old); + mMouseFocus = true; + updateImage(); + Base::onMouseSetFocus(_old); } void ImageButton::onMouseLostFocus(Widget* _new) { - setImageTexture(mImageNormal); - ImageBox::onMouseLostFocus(_new); + mMouseFocus = false; + updateImage(); + Base::onMouseLostFocus(_new); } void ImageButton::onMouseButtonPressed(int _left, int _top, MyGUI::MouseButton _id) { if (_id == MyGUI::MouseButton::Left) - setImageTexture(mImagePushed); + { + mMousePress = true; + updateImage(); + } + Base::onMouseButtonPressed(_left, _top, _id); + } - ImageBox::onMouseButtonPressed(_left, _top, _id); + void ImageButton::updateImage() + { + if (mMousePress) + setImageTexture(mImagePushed); + else if (mMouseFocus || mKeyFocus) + setImageTexture(mImageHighlighted); + else + setImageTexture(mImageNormal); } MyGUI::IntSize ImageButton::getRequestedSize() @@ -70,8 +93,23 @@ namespace Gui void ImageButton::onMouseButtonReleased(int _left, int _top, MyGUI::MouseButton _id) { if (_id == MyGUI::MouseButton::Left) - setImageTexture(mImageHighlighted); + { + mMousePress = false; + updateImage(); + } + + Base::onMouseButtonReleased(_left, _top, _id); + } - ImageBox::onMouseButtonReleased(_left, _top, _id); + void ImageButton::onKeySetFocus(MyGUI::Widget *_old) + { + mKeyFocus = true; + updateImage(); + } + + void ImageButton::onKeyLostFocus(MyGUI::Widget *_new) + { + mKeyFocus = false; + updateImage(); } } diff --git a/components/widgets/imagebutton.hpp b/components/widgets/imagebutton.hpp index a539f15c9..509b1c8c2 100644 --- a/components/widgets/imagebutton.hpp +++ b/components/widgets/imagebutton.hpp @@ -16,19 +16,30 @@ namespace Gui public: MyGUI::IntSize getRequestedSize(); + ImageButton(); + /// Set mImageNormal, mImageHighlighted and mImagePushed based on file convention (image_idle.ext, image_over.ext and image_pressed.ext) void setImage(const std::string& image); + private: + void updateImage(); + protected: virtual void setPropertyOverride(const std::string& _key, const std::string& _value); virtual void onMouseLostFocus(MyGUI::Widget* _new); virtual void onMouseSetFocus(MyGUI::Widget* _old); virtual void onMouseButtonPressed(int _left, int _top, MyGUI::MouseButton _id); virtual void onMouseButtonReleased(int _left, int _top, MyGUI::MouseButton _id); + virtual void onKeySetFocus(MyGUI::Widget* _old); + virtual void onKeyLostFocus(MyGUI::Widget* _new); std::string mImageHighlighted; std::string mImageNormal; std::string mImagePushed; + + bool mMouseFocus; + bool mMousePress; + bool mKeyFocus; }; } diff --git a/components/widgets/list.cpp b/components/widgets/list.cpp index c5a459f22..9318e32ed 100644 --- a/components/widgets/list.cpp +++ b/components/widgets/list.cpp @@ -72,6 +72,7 @@ namespace Gui button->getSubWidgetText()->setTextAlign(MyGUI::Align::Left); button->eventMouseWheel += MyGUI::newDelegate(this, &MWList::onMouseWheelMoved); button->eventMouseButtonClick += MyGUI::newDelegate(this, &MWList::onItemSelected); + button->setNeedKeyFocus(true); int height = button->getTextSize().height; button->setSize(MyGUI::IntSize(button->getSize().width, height)); diff --git a/components/widgets/numericeditbox.cpp b/components/widgets/numericeditbox.cpp index 828a84589..6e884a091 100644 --- a/components/widgets/numericeditbox.cpp +++ b/components/widgets/numericeditbox.cpp @@ -75,4 +75,20 @@ namespace Gui setCaption(MyGUI::utility::toString(mValue)); } + void NumericEditBox::onKeyButtonPressed(MyGUI::KeyCode key, MyGUI::Char character) + { + if (key == MyGUI::KeyCode::ArrowUp) + { + setValue(std::min(mValue+1, mMaxValue)); + eventValueChanged(mValue); + } + else if (key == MyGUI::KeyCode::ArrowDown) + { + setValue(std::max(mValue-1, mMinValue)); + eventValueChanged(mValue); + } + else + Base::onKeyButtonPressed(key, character); + } + } diff --git a/components/widgets/numericeditbox.hpp b/components/widgets/numericeditbox.hpp index 20000b3d3..ca7674f22 100644 --- a/components/widgets/numericeditbox.hpp +++ b/components/widgets/numericeditbox.hpp @@ -33,6 +33,7 @@ namespace Gui private: void onEditTextChange(MyGUI::EditBox* sender); void onKeyLostFocus(MyGUI::Widget* _new); + void onKeyButtonPressed(MyGUI::KeyCode key, MyGUI::Char character); int mValue; diff --git a/files/mygui/openmw_book.layout b/files/mygui/openmw_book.layout index 7c158af8d..c83c4982b 100644 --- a/files/mygui/openmw_book.layout +++ b/files/mygui/openmw_book.layout @@ -5,49 +5,46 @@ - - + + + + + + + + - - - - + - - - - - - - - - - - + + + + + - - - - - - - - - - + + + + + + + + + + - - - + + + diff --git a/files/mygui/openmw_button.skin.xml b/files/mygui/openmw_button.skin.xml index 5aee8de7a..ac9bf042d 100644 --- a/files/mygui/openmw_button.skin.xml +++ b/files/mygui/openmw_button.skin.xml @@ -71,6 +71,7 @@ + diff --git a/files/mygui/openmw_chargen_generate_class_result.layout b/files/mygui/openmw_chargen_generate_class_result.layout index 83595fafd..d49f57dde 100644 --- a/files/mygui/openmw_chargen_generate_class_result.layout +++ b/files/mygui/openmw_chargen_generate_class_result.layout @@ -3,7 +3,7 @@ - + diff --git a/files/mygui/openmw_confirmation_dialog.layout b/files/mygui/openmw_confirmation_dialog.layout index 39e77cb93..c5eb573a7 100644 --- a/files/mygui/openmw_confirmation_dialog.layout +++ b/files/mygui/openmw_confirmation_dialog.layout @@ -10,6 +10,7 @@ + diff --git a/files/mygui/openmw_console.layout b/files/mygui/openmw_console.layout index 854568f9b..103cdcf14 100644 --- a/files/mygui/openmw_console.layout +++ b/files/mygui/openmw_console.layout @@ -18,6 +18,7 @@ + diff --git a/files/mygui/openmw_hud.layout b/files/mygui/openmw_hud.layout index 366b0c4c6..cee3f4682 100644 --- a/files/mygui/openmw_hud.layout +++ b/files/mygui/openmw_hud.layout @@ -82,6 +82,7 @@ + diff --git a/files/mygui/openmw_interactive_messagebox.layout b/files/mygui/openmw_interactive_messagebox.layout index 50bc178ed..410426656 100644 --- a/files/mygui/openmw_interactive_messagebox.layout +++ b/files/mygui/openmw_interactive_messagebox.layout @@ -9,6 +9,7 @@ + diff --git a/files/mygui/openmw_interactive_messagebox_notransp.layout b/files/mygui/openmw_interactive_messagebox_notransp.layout index 6b79b9417..f5a462977 100644 --- a/files/mygui/openmw_interactive_messagebox_notransp.layout +++ b/files/mygui/openmw_interactive_messagebox_notransp.layout @@ -9,6 +9,7 @@ + diff --git a/files/mygui/openmw_inventory_window.layout b/files/mygui/openmw_inventory_window.layout index bb707fa0d..6221799b5 100644 --- a/files/mygui/openmw_inventory_window.layout +++ b/files/mygui/openmw_inventory_window.layout @@ -32,18 +32,23 @@ + + + + + diff --git a/files/mygui/openmw_journal.layout b/files/mygui/openmw_journal.layout index 1131b1bbc..964b5ea95 100644 --- a/files/mygui/openmw_journal.layout +++ b/files/mygui/openmw_journal.layout @@ -3,32 +3,27 @@ - - - - - - - - - - - - - - - - - + + + + + + + + + + + + @@ -36,11 +31,14 @@ + + + diff --git a/files/mygui/openmw_layers.xml b/files/mygui/openmw_layers.xml index cd8a9f760..5e4a74978 100644 --- a/files/mygui/openmw_layers.xml +++ b/files/mygui/openmw_layers.xml @@ -1,6 +1,7 @@ + diff --git a/files/mygui/openmw_levelup_dialog.layout b/files/mygui/openmw_levelup_dialog.layout index fc11ddfd7..66855373a 100644 --- a/files/mygui/openmw_levelup_dialog.layout +++ b/files/mygui/openmw_levelup_dialog.layout @@ -50,7 +50,7 @@ - + @@ -64,7 +64,7 @@ - + @@ -78,7 +78,7 @@ - + @@ -92,7 +92,7 @@ - + @@ -107,7 +107,7 @@ - + @@ -121,7 +121,7 @@ - + @@ -135,7 +135,7 @@ - + @@ -149,7 +149,7 @@ - + diff --git a/files/mygui/openmw_map_window.layout b/files/mygui/openmw_map_window.layout index b38097dce..8d45022f8 100644 --- a/files/mygui/openmw_map_window.layout +++ b/files/mygui/openmw_map_window.layout @@ -31,6 +31,7 @@ + diff --git a/files/mygui/openmw_map_window.skin.xml b/files/mygui/openmw_map_window.skin.xml index 31a70191f..f6c099630 100644 --- a/files/mygui/openmw_map_window.skin.xml +++ b/files/mygui/openmw_map_window.skin.xml @@ -2,6 +2,7 @@ + diff --git a/files/mygui/openmw_recharge_dialog.layout b/files/mygui/openmw_recharge_dialog.layout index d620aadab..200d345e3 100644 --- a/files/mygui/openmw_recharge_dialog.layout +++ b/files/mygui/openmw_recharge_dialog.layout @@ -2,7 +2,7 @@ - + diff --git a/files/mygui/openmw_repair.layout b/files/mygui/openmw_repair.layout index 340711bb1..abc429594 100644 --- a/files/mygui/openmw_repair.layout +++ b/files/mygui/openmw_repair.layout @@ -2,7 +2,7 @@ - + diff --git a/files/mygui/openmw_savegame_dialog.layout b/files/mygui/openmw_savegame_dialog.layout index f18218430..236eaaa61 100644 --- a/files/mygui/openmw_savegame_dialog.layout +++ b/files/mygui/openmw_savegame_dialog.layout @@ -2,7 +2,7 @@ - + @@ -46,6 +46,7 @@ + diff --git a/files/mygui/openmw_scroll.skin.xml b/files/mygui/openmw_scroll.skin.xml index 415c2479a..cff0ad7eb 100644 --- a/files/mygui/openmw_scroll.skin.xml +++ b/files/mygui/openmw_scroll.skin.xml @@ -3,11 +3,13 @@ + + diff --git a/files/mygui/openmw_text.skin.xml b/files/mygui/openmw_text.skin.xml index 163b9d134..5f96d0a57 100644 --- a/files/mygui/openmw_text.skin.xml +++ b/files/mygui/openmw_text.skin.xml @@ -66,6 +66,7 @@ color_misc=0,205,205 # ???? + @@ -81,6 +82,7 @@ color_misc=0,205,205 # ???? + @@ -96,6 +98,7 @@ color_misc=0,205,205 # ???? + @@ -103,6 +106,7 @@ color_misc=0,205,205 # ???? + diff --git a/files/mygui/openmw_tooltips.layout b/files/mygui/openmw_tooltips.layout index a7eeabfa5..c038066f1 100644 --- a/files/mygui/openmw_tooltips.layout +++ b/files/mygui/openmw_tooltips.layout @@ -32,7 +32,7 @@ - + @@ -44,7 +44,7 @@ - + @@ -61,7 +61,7 @@ - + @@ -77,7 +77,7 @@ - + @@ -137,7 +137,7 @@ - + @@ -159,7 +159,7 @@ - + @@ -211,7 +211,7 @@ - + @@ -245,7 +245,7 @@ - + @@ -277,7 +277,7 @@ - + diff --git a/files/mygui/openmw_trade_window.layout b/files/mygui/openmw_trade_window.layout index 6d5dfb89b..d1f31c475 100644 --- a/files/mygui/openmw_trade_window.layout +++ b/files/mygui/openmw_trade_window.layout @@ -9,18 +9,23 @@ + + + + + @@ -48,6 +53,10 @@ + + + + @@ -60,9 +69,6 @@ - - - diff --git a/files/mygui/openmw_wait_dialog.layout b/files/mygui/openmw_wait_dialog.layout index 112fa6001..4bc60ceb4 100644 --- a/files/mygui/openmw_wait_dialog.layout +++ b/files/mygui/openmw_wait_dialog.layout @@ -2,7 +2,7 @@ - + diff --git a/files/shaders/water_fragment.glsl b/files/shaders/water_fragment.glsl index 9c8e56191..931422d5e 100644 --- a/files/shaders/water_fragment.glsl +++ b/files/shaders/water_fragment.glsl @@ -38,10 +38,69 @@ const float WIND_SPEED = 0.2f; const vec3 WATER_COLOR = vec3(0.090195, 0.115685, 0.12745); +// ---------------- rain ripples related stuff --------------------- + +const float RAIN_RIPPLE_GAPS = 5.0; +const float RAIN_RIPPLE_RADIUS = 0.1; + +int modulo(int v1, int v2) +{ + return v1 - v2 * int(floor(float(v1) / float(v2))); +} + +vec2 randOffset(vec2 c) +{ + return fract(vec2( + c.x * c.y / 8.0 + c.y * 0.3 + c.x * 0.2, + c.x * c.y / 14.0 + c.y * 0.5 + c.x * 0.7)); +} + +float randPhase(vec2 c) +{ + return fract((c.x * c.y) / (c.x + c.y + 0.1)); +} + +vec4 circle(vec2 coords, vec2 i_part, float phase) +{ + vec2 center = vec2(0.5,0.5) + (0.5 - RAIN_RIPPLE_RADIUS) * (2.0 * randOffset(i_part) - 1.0); + vec2 toCenter = coords - center; + float d = length(toCenter); + + float r = RAIN_RIPPLE_RADIUS * phase; + + if (d > r) + return vec4(0.0,0.0,1.0,0.0); + + float sinValue = (sin(d / r * 1.2) + 0.7) / 2.0; + + float height = (1.0 - abs(phase)) * pow(sinValue,3.0); + + vec3 normal = normalize(mix(vec3(0.0,0.0,1.0),vec3(normalize(toCenter),0.0),height)); + + return vec4(normal,height); +} + +vec4 rain(vec2 uv, float time) +{ + vec2 i_part = floor(uv * RAIN_RIPPLE_GAPS); + vec2 f_part = fract(uv * RAIN_RIPPLE_GAPS); + return circle(f_part,i_part,fract(time * 1.2 + randPhase(i_part))); +} + +vec4 rainCombined(vec2 uv, float time) // returns ripple normal in xyz and ripple height in w +{ + return + rain(uv,time) + + rain(uv + vec2(10.5,5.7),time) + + rain(uv * 0.75 + vec2(3.7,18.9),time) + + rain(uv * 0.9 + vec2(5.7,30.1),time) + + rain(uv * 0.8 + vec2(1.2,3.0),time); +} + // -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - float fresnel_dielectric(vec3 Incoming, vec3 Normal, float eta) -{ + { float c = abs(dot(Incoming, Normal)); float g = eta * eta - 1.0 + c * c; float result; @@ -56,7 +115,12 @@ float fresnel_dielectric(vec3 Incoming, vec3 Normal, float eta) result = 1.0; /* TIR (no refracted component) */ return result; -} + } + +vec2 normalCoords(vec2 uv, float scale, float speed, float time, float timer1, float timer2, vec3 previousNormal) + { + return uv * (WAVE_SCALE * scale) + WIND_DIR * time * (WIND_SPEED * speed) -(previousNormal.xy/previousNormal.zz) * WAVE_CHOPPYNESS + vec2(time * timer1,time * timer2); + } varying vec3 screenCoordsPassthrough; varying vec4 position; @@ -76,6 +140,8 @@ uniform float near; uniform float far; uniform vec3 nodePosition; +uniform float rainIntensity; + float frustumDepth; float linearizeDepth(float depth) // takes <0,1> non-linear depth value and returns <0,1> linearized value @@ -101,26 +167,26 @@ void main(void) #define waterTimer osg_SimulationTime - nCoord = UV * (WAVE_SCALE * 0.05) + WIND_DIR * waterTimer * (WIND_SPEED*0.04); - vec3 normal0 = 2.0 * texture2D(normalMap, nCoord + vec2(-waterTimer*0.015,-waterTimer*0.005)).rgb - 1.0; - nCoord = UV * (WAVE_SCALE * 0.1) + WIND_DIR * waterTimer * (WIND_SPEED*0.08)-(normal0.xy/normal0.zz)*WAVE_CHOPPYNESS; - vec3 normal1 = 2.0 * texture2D(normalMap, nCoord + vec2(+waterTimer*0.020,+waterTimer*0.015)).rgb - 1.0; - - nCoord = UV * (WAVE_SCALE * 0.25) + WIND_DIR * waterTimer * (WIND_SPEED*0.07)-(normal1.xy/normal1.zz)*WAVE_CHOPPYNESS; - vec3 normal2 = 2.0 * texture2D(normalMap, nCoord + vec2(-waterTimer*0.04,-waterTimer*0.03)).rgb - 1.0; - nCoord = UV * (WAVE_SCALE * 0.5) + WIND_DIR * waterTimer * (WIND_SPEED*0.09)-(normal2.xy/normal2.z)*WAVE_CHOPPYNESS; - vec3 normal3 = 2.0 * texture2D(normalMap, nCoord + vec2(+waterTimer*0.03,+waterTimer*0.04)).rgb - 1.0; - - nCoord = UV * (WAVE_SCALE* 1.0) + WIND_DIR * waterTimer * (WIND_SPEED*0.4)-(normal3.xy/normal3.zz)*WAVE_CHOPPYNESS; - vec3 normal4 = 2.0 * texture2D(normalMap, nCoord + vec2(-waterTimer*0.02,+waterTimer*0.1)).rgb - 1.0; - nCoord = UV * (WAVE_SCALE * 2.0) + WIND_DIR * waterTimer * (WIND_SPEED*0.7)-(normal4.xy/normal4.zz)*WAVE_CHOPPYNESS; - vec3 normal5 = 2.0 * texture2D(normalMap, nCoord + vec2(+waterTimer*0.1,-waterTimer*0.06)).rgb - 1.0; + vec3 normal0 = 2.0 * texture2D(normalMap,normalCoords(UV, 0.05, 0.04, waterTimer, -0.015, -0.005, vec3(0.0,0.0,0.0))).rgb - 1.0; + vec3 normal1 = 2.0 * texture2D(normalMap,normalCoords(UV, 0.1, 0.08, waterTimer, 0.02, 0.015, normal0)).rgb - 1.0; + vec3 normal2 = 2.0 * texture2D(normalMap,normalCoords(UV, 0.25, 0.07, waterTimer, -0.04, -0.03, normal1)).rgb - 1.0; + vec3 normal3 = 2.0 * texture2D(normalMap,normalCoords(UV, 0.5, 0.09, waterTimer, 0.03, 0.04, normal2)).rgb - 1.0; + vec3 normal4 = 2.0 * texture2D(normalMap,normalCoords(UV, 1.0, 0.4, waterTimer, -0.02, 0.1, normal3)).rgb - 1.0; + vec3 normal5 = 2.0 * texture2D(normalMap,normalCoords(UV, 2.0, 0.7, waterTimer, 0.1, -0.06, normal4)).rgb - 1.0; + vec4 rainRipple; + if (rainIntensity > 0.01) + rainRipple = rainCombined(position.xy / 1000.0,waterTimer) * clamp(rainIntensity,0.0,1.0); + else + rainRipple = vec4(0.0,0.0,0.0,0.0); + + vec3 rippleAdd = rainRipple.xyz * rainRipple.w * 10.0; vec3 normal = (normal0 * BIG_WAVES_X + normal1 * BIG_WAVES_Y + normal2 * MID_WAVES_X + normal3 * MID_WAVES_Y + - normal4 * SMALL_WAVES_X + normal5 * SMALL_WAVES_Y); + normal4 * SMALL_WAVES_X + normal5 * SMALL_WAVES_Y + + rippleAdd); normal = normalize(vec3(normal.x * BUMP, normal.y * BUMP, normal.z)); @@ -129,11 +195,12 @@ void main(void) // normal for sunlight scattering vec3 lNormal = (normal0 * BIG_WAVES_X*0.5 + normal1 * BIG_WAVES_Y*0.5 + normal2 * MID_WAVES_X*0.2 + normal3 * MID_WAVES_Y*0.2 + - normal4 * SMALL_WAVES_X*0.1 + normal5 * SMALL_WAVES_Y*0.1).xyz; + normal4 * SMALL_WAVES_X*0.1 + normal5 * SMALL_WAVES_Y*0.1 + + rippleAdd).xyz; + lNormal = normalize(vec3(lNormal.x * BUMP, lNormal.y * BUMP, lNormal.z)); lNormal = vec3(-lNormal.x, -lNormal.y, lNormal.z); - vec3 lVec = normalize((gl_ModelViewMatrixInverse * vec4(gl_LightSource[0].position.xyz, 0.0)).xyz); vec3 cameraPos = (gl_ModelViewMatrixInverse * vec4(0,0,0,1)).xyz; @@ -151,10 +218,10 @@ void main(void) float s = clamp(dot(lR, vVec)*2.0-1.2, 0.0, 1.0); float lightScatter = shadow * clamp(dot(lVec,lNormal)*0.7+0.3, 0.0, 1.0) * s * SCATTER_AMOUNT * sunFade * clamp(1.0-exp(-sunHeight), 0.0, 1.0); - vec3 scatterColour = mix(vec3(SCATTER_COLOUR)*vec3(1.0,0.4,0.0), SCATTER_COLOUR, clamp(1.0-exp(-sunHeight*SUN_EXT), 0.0, 1.0)); + vec3 scatterColour = mix(vec3(SCATTER_COLOUR)*vec3(1.0,0.4,0.0), SCATTER_COLOUR, clamp(1.0-exp(-sunHeight*SUN_EXT), 0.0, 1.0)); // fresnel - float ior = (cameraPos.z>0.0)?(1.333/1.0):(1.0/1.333); //air to water; water to air + float ior = (cameraPos.z>0.0)?(1.333/1.0):(1.0/1.333); // air to water; water to air float fresnel = fresnel_dielectric(vVec, normal, ior); fresnel = clamp(fresnel, 0.0, 1.0); @@ -164,18 +231,19 @@ void main(void) float depthSample = linearizeDepth(texture2D(refractionDepthMap,screenCoords).x) * normalization; float depthSampleDistorted = linearizeDepth(texture2D(refractionDepthMap,screenCoords-(normal.xy*REFR_BUMP)).x) * normalization; float surfaceDepth = linearizeDepth(gl_FragCoord.z) * normalization; - float realWaterDepth = depthSample - surfaceDepth; // undistorted water depth in view direction, independent of frustum - + float realWaterDepth = depthSample - surfaceDepth; // undistorted water depth in view direction, independent of frustum float shore = clamp(realWaterDepth / BUMP_SUPPRESS_DEPTH,0,1); #else float shore = 1.0; #endif + vec2 screenCoordsOffset = normal.xy * REFL_BUMP * shore; + // reflection - vec3 reflection = texture2D(reflectionMap, screenCoords+(normal.xy*REFL_BUMP*shore)).rgb; + vec3 reflection = texture2D(reflectionMap, screenCoords + screenCoordsOffset).rgb; // refraction #if REFRACTION - vec3 refraction = texture2D(refractionMap, screenCoords-(normal.xy*REFR_BUMP*shore)).rgb; + vec3 refraction = texture2D(refractionMap, screenCoords - screenCoordsOffset).rgb; // brighten up the refraction underwater refraction = (cameraPos.z < 0.0) ? clamp(refraction * 1.5, 0.0, 1.0) : refraction; @@ -195,14 +263,19 @@ void main(void) #else gl_FragData[0].xyz = mix(reflection, waterColor, (1.0-fresnel)*0.5) + specular * gl_LightSource[0].specular.xyz; #endif - // fog float fogValue = clamp((depthPassthrough - gl_Fog.start) * gl_Fog.scale, 0.0, 1.0); gl_FragData[0].xyz = mix(gl_FragData[0].xyz, gl_Fog.color.xyz, fogValue); +#if REFRACTION + gl_FragData[0].xyz += vec3(rainRipple.w) * 0.2; +#else + gl_FragData[0].xyz += vec3(rainRipple.w) * 0.7; +#endif + #if REFRACTION gl_FragData[0].w = 1.0; #else - gl_FragData[0].w = clamp(fresnel*2.0 + specular, 0.0, 1.0); + gl_FragData[0].w = clamp(fresnel*6.0 + specular, 0.0, 1.0); //clamp(fresnel*2.0 + specular, 0.0, 1.0); #endif } diff --git a/tes3mp-credits.md b/tes3mp-credits.md index f474702a4..ad603c16f 100644 --- a/tes3mp-credits.md +++ b/tes3mp-credits.md @@ -26,7 +26,7 @@ Community administrators Art --- - Texafornian - tes3mp logo + Texafornian - TES3MP logo Special thanks (in alphabetical order) @@ -51,6 +51,7 @@ Special thanks (in alphabetical order) Rhiyo Scorcio Shnatsel + Simon Nemes Zaphida Zaric Zhakaron All the developers of OpenMW for creating an amazing open source project