Merge pull request #305 from TES3MP/master

Add master commits up to 6 Oct 2017
new-script-api
David Cernat 7 years ago committed by GitHub
commit 7a38a0b223

@ -6,5 +6,5 @@ brew outdated cmake || brew upgrade cmake
brew outdated pkgconfig || brew upgrade pkgconfig brew outdated pkgconfig || brew upgrade pkgconfig
brew install $macos_qt_formula 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 unzip ~/openmw-deps.zip -d /private/tmp/openmw-deps > /dev/null

@ -3,7 +3,7 @@ TES3MP
[![Build Status](https://travis-ci.org/TES3MP/openmw-tes3mp.svg?branch=master)](https://travis-ci.org/TES3MP/openmw-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 * Version: 0.7-alpha
* License: GPLv3 (see docs/license/GPL3.txt for more information) * 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. 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. 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 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) * [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) * [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) * [Known issues and bug reports](https://github.com/TES3MP/openmw-tes3mp/issues)
Donations Donations

@ -18,7 +18,7 @@ opencs_hdrs_noqt (model/doc
opencs_units (model/world 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 cellcreator pathgridcreator referenceablecreator startscriptcreator referencecreator scenesubview
infocreator scriptedit dialoguesubview previewsubview regionmap dragrecordtable nestedtable infocreator scriptedit dialoguesubview previewsubview regionmap dragrecordtable nestedtable
dialoguespinbox recordbuttonbar tableeditidaction scripterrortable extendedcommandconfigurator dialoguespinbox recordbuttonbar tableeditidaction scripterrortable extendedcommandconfigurator
bodypartcreator bodypartcreator landtexturecreator landcreator
) )
opencs_units_noqt (view/world opencs_units_noqt (view/world

@ -240,6 +240,8 @@ void CSMPrefs::State::declare()
declareShortcut ("document-world-cells", "Open Cell List", QKeySequence()); declareShortcut ("document-world-cells", "Open Cell List", QKeySequence());
declareShortcut ("document-world-referencables", "Open Object List", QKeySequence()); declareShortcut ("document-world-referencables", "Open Object List", QKeySequence());
declareShortcut ("document-world-references", "Open Instance 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-pathgrid", "Open Pathgrid List", QKeySequence());
declareShortcut ("document-world-regionmap", "Open Region Map", QKeySequence()); declareShortcut ("document-world-regionmap", "Open Region Map", QKeySequence());
declareShortcut ("document-mechanics-globals", "Open Global List", 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-edit", "Edit Record", QKeySequence());
declareShortcut ("table-add", "Add Row/Record", QKeySequence(Qt::ShiftModifier | Qt::Key_A)); declareShortcut ("table-add", "Add Row/Record", QKeySequence(Qt::ShiftModifier | Qt::Key_A));
declareShortcut ("table-clone", "Clone Record", QKeySequence(Qt::ShiftModifier | Qt::Key_D)); 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-revert", "Revert Record", QKeySequence());
declareShortcut ("table-remove", "Remove Row/Record", QKeySequence(Qt::Key_Delete)); declareShortcut ("table-remove", "Remove Row/Record", QKeySequence(Qt::Key_Delete));
declareShortcut ("table-moveup", "Move Record Up", QKeySequence()); declareShortcut ("table-moveup", "Move Record Up", QKeySequence());

@ -6,6 +6,7 @@
#include <algorithm> #include <algorithm>
#include <cctype> #include <cctype>
#include <stdexcept> #include <stdexcept>
#include <string>
#include <functional> #include <functional>
#include <QVariant> #include <QVariant>
@ -13,8 +14,9 @@
#include <components/misc/stringops.hpp> #include <components/misc/stringops.hpp>
#include "columnbase.hpp" #include "columnbase.hpp"
#include "collectionbase.hpp" #include "collectionbase.hpp"
#include "land.hpp"
#include "landtexture.hpp"
namespace CSMWorld namespace CSMWorld
{ {
@ -22,15 +24,14 @@ namespace CSMWorld
template<typename ESXRecordT> template<typename ESXRecordT>
struct IdAccessor struct IdAccessor
{ {
std::string& getId (ESXRecordT& record); void setId(ESXRecordT& record, const std::string& id) const;
const std::string getId (const ESXRecordT& record) const; const std::string getId (const ESXRecordT& record) const;
}; };
template<typename ESXRecordT> template<typename ESXRecordT>
std::string& IdAccessor<ESXRecordT>::getId (ESXRecordT& record) void IdAccessor<ESXRecordT>::setId(ESXRecordT& record, const std::string& id) const
{ {
return record.mId; record.mId = id;
} }
template<typename ESXRecordT> template<typename ESXRecordT>
@ -39,6 +40,39 @@ namespace CSMWorld
return record.mId; return record.mId;
} }
template<>
inline void IdAccessor<Land>::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<LandTexture>::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<Land>::getId (const Land& record) const
{
return Land::createUniqueRecordId(record.mX, record.mY);
}
template<>
inline const std::string IdAccessor<LandTexture>::getId (const LandTexture& record) const
{
return LandTexture::createUniqueRecordId(record.mPluginIndex, record.mIndex);
}
/// \brief Single-type record collection /// \brief Single-type record collection
template<typename ESXRecordT, typename IdAccessorT = IdAccessor<ESXRecordT> > template<typename ESXRecordT, typename IdAccessorT = IdAccessor<ESXRecordT> >
class Collection : public CollectionBase class Collection : public CollectionBase
@ -69,6 +103,13 @@ namespace CSMWorld
/// ///
/// \return Success? /// \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: public:
Collection(); Collection();
@ -108,6 +149,10 @@ namespace CSMWorld
const std::string& destination, const std::string& destination,
const UniversalId::Type type); 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; virtual int searchId (const std::string& id) const;
////< Search record with \a id. ////< Search record with \a id.
/// \return index of record (if found) or -1 (not found) /// \return index of record (if found) or -1 (not found)
@ -205,17 +250,72 @@ namespace CSMWorld
return true; return true;
} }
template<typename ESXRecordT, typename IdAccessorT>
int Collection<ESXRecordT, IdAccessorT>::cloneRecordImp(const std::string& origin,
const std::string& destination, UniversalId::Type type)
{
Record<ESXRecordT> 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<typename ESXRecordT, typename IdAccessorT>
int Collection<ESXRecordT, IdAccessorT>::touchRecordImp(const std::string& id)
{
int index = getIndex(id);
Record<ESXRecordT>& 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<typename ESXRecordT, typename IdAccessorT> template<typename ESXRecordT, typename IdAccessorT>
void Collection<ESXRecordT, IdAccessorT>::cloneRecord(const std::string& origin, void Collection<ESXRecordT, IdAccessorT>::cloneRecord(const std::string& origin,
const std::string& destination, const std::string& destination, const UniversalId::Type type)
const UniversalId::Type type)
{ {
Record<ESXRecordT> copy; cloneRecordImp(origin, destination, type);
copy.mModified = getRecord(origin).get(); }
copy.mState = RecordBase::State_ModifiedOnly;
copy.get().mId = destination;
insertRecord(copy, getAppendIndex(destination, type)); template<>
inline void Collection<Land, IdAccessor<Land> >::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<typename ESXRecordT, typename IdAccessorT>
bool Collection<ESXRecordT, IdAccessorT>::touchRecord(const std::string& id)
{
return touchRecordImp(id) != -1;
}
template<>
inline bool Collection<Land, IdAccessor<Land> >::touchRecord(const std::string& id)
{
int index = touchRecordImp(id);
if (index >= 0)
{
mRecords.at(index).get().mPlugin = 0;
return true;
}
return false;
} }
template<typename ESXRecordT, typename IdAccessorT> template<typename ESXRecordT, typename IdAccessorT>
@ -366,7 +466,7 @@ namespace CSMWorld
UniversalId::Type type) UniversalId::Type type)
{ {
ESXRecordT record; ESXRecordT record;
IdAccessorT().getId (record) = id; IdAccessorT().setId(record, id);
record.blank(); record.blank();
Record<ESXRecordT> record2; Record<ESXRecordT> record2;

@ -78,6 +78,8 @@ namespace CSMWorld
const std::string& destination, const std::string& destination,
const UniversalId::Type type) = 0; 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 (const std::string& id) const = 0;
virtual const RecordBase& getRecord (int index) const = 0; virtual const RecordBase& getRecord (int index) const = 0;

@ -1,28 +1,341 @@
#include "columnimp.hpp" #include "columnimp.hpp"
CSMWorld::BodyPartRaceColumn::BodyPartRaceColumn(const MeshTypeColumn<ESM::BodyPart> *meshType) #include <stdexcept>
: mMeshType(meshType) #include <QVector>
{}
QVariant CSMWorld::BodyPartRaceColumn::get(const Record<ESM::BodyPart> &record) const namespace CSMWorld
{ {
if (mMeshType != NULL && mMeshType->get(record) == ESM::BodyPart::MT_Skin) /* LandTextureNicknameColumn */
LandTextureNicknameColumn::LandTextureNicknameColumn()
: Column<LandTexture>(Columns::ColumnId_TextureNickname, ColumnBase::Display_String)
{ {
return QString::fromUtf8(record.get().mRace.c_str());
} }
return QVariant(QVariant::UserType);
}
void CSMWorld::BodyPartRaceColumn::set(Record<ESM::BodyPart> &record, const QVariant &data) QVariant LandTextureNicknameColumn::get(const Record<LandTexture>& record) const
{ {
ESM::BodyPart record2 = record.get(); return QString::fromUtf8(record.get().mId.c_str());
}
record2.mRace = data.toString().toUtf8().constData(); void LandTextureNicknameColumn::set(Record<LandTexture>& 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 /* LandTextureIndexColumn */
{ LandTextureIndexColumn::LandTextureIndexColumn()
return true; : Column<LandTexture>(Columns::ColumnId_TextureIndex, ColumnBase::Display_Integer)
{
}
QVariant LandTextureIndexColumn::get(const Record<LandTexture>& record) const
{
return record.get().mIndex;
}
bool LandTextureIndexColumn::isEditable() const
{
return false;
}
/* LandPluginIndexColumn */
LandPluginIndexColumn::LandPluginIndexColumn()
: Column<Land>(Columns::ColumnId_PluginIndex, ColumnBase::Display_Integer, 0)
{
}
QVariant LandPluginIndexColumn::get(const Record<Land>& record) const
{
return record.get().mPlugin;
}
bool LandPluginIndexColumn::isEditable() const
{
return false;
}
/* LandTexturePluginIndexColumn */
LandTexturePluginIndexColumn::LandTexturePluginIndexColumn()
: Column<LandTexture>(Columns::ColumnId_PluginIndex, ColumnBase::Display_Integer, 0)
{
}
QVariant LandTexturePluginIndexColumn::get(const Record<LandTexture>& record) const
{
return record.get().mPluginIndex;
}
bool LandTexturePluginIndexColumn::isEditable() const
{
return false;
}
/* LandMapLodColumn */
LandMapLodColumn::LandMapLodColumn()
: Column<Land>(Columns::ColumnId_LandMapLodIndex, ColumnBase::Display_String, 0)
{
}
QVariant LandMapLodColumn::get(const Record<Land>& 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<Land>& record, const QVariant& data)
{
DataType values = data.value<DataType>();
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<Land>(Columns::ColumnId_LandNormalsIndex, ColumnBase::Display_String, 0)
{
}
QVariant LandNormalsColumn::get(const Record<Land>& 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<Land>& record, const QVariant& data)
{
DataType values = data.value<DataType>();
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<Land>(Columns::ColumnId_LandHeightsIndex, ColumnBase::Display_String, 0)
{
}
QVariant LandHeightsColumn::get(const Record<Land>& 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<Land>& record, const QVariant& data)
{
DataType values = data.value<DataType>();
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<Land>(Columns::ColumnId_LandColoursIndex, ColumnBase::Display_String, 0)
{
}
QVariant LandColoursColumn::get(const Record<Land>& 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<Land>& record, const QVariant& data)
{
DataType values = data.value<DataType>();
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<Land>(Columns::ColumnId_LandTexturesIndex, ColumnBase::Display_String, 0)
{
}
QVariant LandTexturesColumn::get(const Record<Land>& 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<Land>& record, const QVariant& data)
{
DataType values = data.value<DataType>();
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<ESM::BodyPart> *meshType)
: mMeshType(meshType)
{}
QVariant BodyPartRaceColumn::get(const Record<ESM::BodyPart> &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<ESM::BodyPart> &record, const QVariant &data)
{
ESM::BodyPart record2 = record.get();
record2.mRace = data.toString().toUtf8().constData();
record.setModified(record2);
}
bool BodyPartRaceColumn::isEditable() const
{
return true;
}
} }

@ -2,10 +2,12 @@
#define CSM_WOLRD_COLUMNIMP_H #define CSM_WOLRD_COLUMNIMP_H
#include <cassert> #include <cassert>
#include <cstdint>
#include <sstream> #include <sstream>
#include <stdexcept> #include <stdexcept>
#include <QColor> #include <QColor>
#include <QVector>
#include <components/esm/loadbody.hpp> #include <components/esm/loadbody.hpp>
#include <components/esm/loadskil.hpp> #include <components/esm/loadskil.hpp>
@ -15,6 +17,9 @@
#include "columns.hpp" #include "columns.hpp"
#include "info.hpp" #include "info.hpp"
#include "land.hpp"
#include "landtexture.hpp"
namespace CSMWorld namespace CSMWorld
{ {
/// \note Shares ID with VarValueColumn. A table can not have both. /// \note Shares ID with VarValueColumn. A table can not have both.
@ -60,6 +65,20 @@ namespace CSMWorld
} }
}; };
template<>
inline QVariant StringIdColumn<Land>::get(const Record<Land>& record) const
{
const Land& land = record.get();
return QString::fromUtf8(Land::createUniqueRecordId(land.mX, land.mY).c_str());
}
template<>
inline QVariant StringIdColumn<LandTexture>::get(const Record<LandTexture>& record) const
{
const LandTexture& ltex = record.get();
return QString::fromUtf8(LandTexture::createUniqueRecordId(ltex.mPluginIndex, ltex.mIndex).c_str());
}
template<typename ESXRecordT> template<typename ESXRecordT>
struct RecordStateColumn : public Column<ESXRecordT> struct RecordStateColumn : public Column<ESXRecordT>
{ {
@ -2418,6 +2437,94 @@ namespace CSMWorld
} }
}; };
struct LandTextureNicknameColumn : public Column<LandTexture>
{
LandTextureNicknameColumn();
QVariant get(const Record<LandTexture>& record) const override;
void set(Record<LandTexture>& record, const QVariant& data) override;
bool isEditable() const override;
};
struct LandTextureIndexColumn : public Column<LandTexture>
{
LandTextureIndexColumn();
QVariant get(const Record<LandTexture>& record) const override;
bool isEditable() const override;
};
struct LandPluginIndexColumn : public Column<Land>
{
LandPluginIndexColumn();
QVariant get(const Record<Land>& record) const override;
bool isEditable() const override;
};
struct LandTexturePluginIndexColumn : public Column<LandTexture>
{
LandTexturePluginIndexColumn();
QVariant get(const Record<LandTexture>& record) const override;
bool isEditable() const override;
};
struct LandMapLodColumn : public Column<Land>
{
using DataType = QVector<signed char>;
LandMapLodColumn();
QVariant get(const Record<Land>& record) const override;
void set(Record<Land>& record, const QVariant& data) override;
bool isEditable() const override;
};
struct LandNormalsColumn : public Column<Land>
{
using DataType = QVector<signed char>;
LandNormalsColumn();
QVariant get(const Record<Land>& record) const override;
void set(Record<Land>& record, const QVariant& data) override;
bool isEditable() const override;
};
struct LandHeightsColumn : public Column<Land>
{
using DataType = QVector<float>;
LandHeightsColumn();
QVariant get(const Record<Land>& record) const override;
void set(Record<Land>& record, const QVariant& data) override;
bool isEditable() const override;
};
struct LandColoursColumn : public Column<Land>
{
using DataType = QVector<unsigned char>;
LandColoursColumn();
QVariant get(const Record<Land>& record) const override;
void set(Record<Land>& record, const QVariant& data) override;
bool isEditable() const override;
};
struct LandTexturesColumn : public Column<Land>
{
using DataType = QVector<uint16_t>;
LandTexturesColumn();
QVariant get(const Record<Land>& record) const override;
void set(Record<Land>& record, const QVariant& data) override;
bool isEditable() const override;
};
struct BodyPartRaceColumn : public RaceColumn<ESM::BodyPart> struct BodyPartRaceColumn : public RaceColumn<ESM::BodyPart>
{ {
const MeshTypeColumn<ESM::BodyPart> *mMeshType; const MeshTypeColumn<ESM::BodyPart> *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 #endif

@ -330,6 +330,14 @@ namespace CSMWorld
{ ColumnId_WeatherChance, "Percent Chance" }, { ColumnId_WeatherChance, "Percent Chance" },
{ ColumnId_Text, "Text" }, { 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_UseValue1, "Use value 1" },
{ ColumnId_UseValue2, "Use value 2" }, { ColumnId_UseValue2, "Use value 2" },

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

@ -2,6 +2,7 @@
#include <cmath> #include <cmath>
#include <sstream> #include <sstream>
#include <unordered_set>
#include <components/misc/stringops.hpp> #include <components/misc/stringops.hpp>
@ -15,6 +16,175 @@
#include "nestedtablewrapper.hpp" #include "nestedtablewrapper.hpp"
#include "pathgrid.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<DataType>();
// 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<int> 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<std::string> oldTextures;
for (int index : texIndices)
{
oldTextures.push_back(LandTexture::createUniqueRecordId(oldPlugin, index));
}
// Import the textures, replace old values
LandTextureIdTable::ImportResults results = dynamic_cast<LandTextureIdTable&>(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, CSMWorld::ModifyCommand::ModifyCommand (QAbstractItemModel& model, const QModelIndex& index,
const QVariant& new_, QUndoCommand* parent) const QVariant& new_, QUndoCommand* parent)
: QUndoCommand (parent), mModel (&model), mIndex (index), mNew (new_), mHasRecordState(false), mOldRecordState(CSMWorld::RecordBase::State_BaseOnly) : QUndoCommand (parent), mModel (&model), mIndex (index), mNew (new_), mHasRecordState(false), mOldRecordState(CSMWorld::RecordBase::State_BaseOnly)

@ -5,12 +5,14 @@
#include <string> #include <string>
#include <map> #include <map>
#include <memory>
#include <vector> #include <vector>
#include <QVariant> #include <QVariant>
#include <QUndoCommand> #include <QUndoCommand>
#include <QModelIndex> #include <QModelIndex>
#include "columnimp.hpp"
#include "universalid.hpp" #include "universalid.hpp"
#include "nestedtablewrapper.hpp" #include "nestedtablewrapper.hpp"
@ -24,6 +26,91 @@ namespace CSMWorld
struct RecordBase; struct RecordBase;
struct NestedTableWrapperBase; 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<RecordBase> 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<std::string> 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<RecordBase> mOld;
bool mChanged;
};
class ModifyCommand : public QUndoCommand class ModifyCommand : public QUndoCommand
{ {
QAbstractItemModel *mModel; QAbstractItemModel *mModel;

@ -412,6 +412,24 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, bool fsStrict, const Files::Pat
Columns::ColumnId_NegativeLight, ESM::MagicEffect::NegativeLight)); Columns::ColumnId_NegativeLight, ESM::MagicEffect::NegativeLight));
mMagicEffects.addColumn (new DescriptionColumn<ESM::MagicEffect>); mMagicEffects.addColumn (new DescriptionColumn<ESM::MagicEffect>);
mLand.addColumn (new StringIdColumn<Land>);
mLand.addColumn (new RecordStateColumn<Land>);
mLand.addColumn (new FixedRecordTypeColumn<Land>(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<LandTexture>(true));
mLandTextures.addColumn (new RecordStateColumn<LandTexture>);
mLandTextures.addColumn (new FixedRecordTypeColumn<LandTexture>(UniversalId::Type_LandTexture));
mLandTextures.addColumn (new LandTextureNicknameColumn);
mLandTextures.addColumn (new LandTexturePluginIndexColumn);
mLandTextures.addColumn (new LandTextureIndexColumn);
mLandTextures.addColumn (new TextureColumn<LandTexture>);
mPathgrids.addColumn (new StringIdColumn<Pathgrid>); mPathgrids.addColumn (new StringIdColumn<Pathgrid>);
mPathgrids.addColumn (new RecordStateColumn<Pathgrid>); mPathgrids.addColumn (new RecordStateColumn<Pathgrid>);
mPathgrids.addColumn (new FixedRecordTypeColumn<Pathgrid> (UniversalId::Type_Pathgrid)); mPathgrids.addColumn (new FixedRecordTypeColumn<Pathgrid> (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 (&mBodyParts), UniversalId::Type_BodyPart);
addModel (new IdTable (&mSoundGens), UniversalId::Type_SoundGen); addModel (new IdTable (&mSoundGens), UniversalId::Type_SoundGen);
addModel (new IdTable (&mMagicEffects), UniversalId::Type_MagicEffect); 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 IdTree (&mPathgrids, &mPathgrids), UniversalId::Type_Pathgrid);
addModel (new IdTable (&mStartScripts), UniversalId::Type_StartScript); addModel (new IdTable (&mStartScripts), UniversalId::Type_StartScript);
addModel (new IdTree (&mReferenceables, &mReferenceables, IdTable::Feature_Preview), 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_LTEX: mLandTextures.load (*mReader, mBase); break;
case ESM::REC_LAND: case ESM::REC_LAND: mLand.load(*mReader, mBase); break;
{
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_CELL: case ESM::REC_CELL:
{ {

@ -4,6 +4,7 @@
#include <components/esm/esmreader.hpp> #include <components/esm/esmreader.hpp>
#include "collection.hpp" #include "collection.hpp"
#include "land.hpp"
namespace CSMWorld namespace CSMWorld
{ {
@ -39,6 +40,22 @@ namespace CSMWorld
record.load (reader, isDeleted); record.load (reader, isDeleted);
} }
template<>
inline void IdCollection<Land, IdAccessor<Land> >::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<typename ESXRecordT, typename IdAccessorT> template<typename ESXRecordT, typename IdAccessorT>
int IdCollection<ESXRecordT, IdAccessorT>::load (ESM::ESMReader& reader, bool base) int IdCollection<ESXRecordT, IdAccessorT>::load (ESM::ESMReader& reader, bool base)
{ {

@ -1,11 +1,17 @@
#include "idtable.hpp" #include "idtable.hpp"
#include <algorithm>
#include <cctype>
#include <cstdint>
#include <limits>
#include <map>
#include <stdexcept> #include <stdexcept>
#include <components/esm/cellid.hpp> #include <components/esm/cellid.hpp>
#include "collectionbase.hpp" #include "collectionbase.hpp"
#include "columnbase.hpp" #include "columnbase.hpp"
#include "landtexture.hpp"
CSMWorld::IdTable::IdTable (CollectionBase *idCollection, unsigned int features) CSMWorld::IdTable::IdTable (CollectionBase *idCollection, unsigned int features)
: IdTableBase (features), mIdCollection (idCollection) : IdTableBase (features), mIdCollection (idCollection)
@ -179,6 +185,26 @@ void CSMWorld::IdTable::cloneRecord(const std::string& origin,
endInsertRows(); 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 ///This method can return only indexes to the top level table cells
QModelIndex CSMWorld::IdTable::getModelIndex (const std::string& id, int column) const QModelIndex CSMWorld::IdTable::getModelIndex (const std::string& id, int column) const
{ {
@ -281,3 +307,72 @@ CSMWorld::CollectionBase *CSMWorld::IdTable::idCollection() const
{ {
return mIdCollection; return mIdCollection;
} }
CSMWorld::LandTextureIdTable::LandTextureIdTable(CollectionBase* idCollection, unsigned int features)
: IdTable(idCollection, features)
{
}
CSMWorld::LandTextureIdTable::ImportResults CSMWorld::LandTextureIdTable::importTextures(const std::vector<std::string>& ids)
{
ImportResults results;
// Map existing textures to ids
std::map<std::string, std::string> reverseLookupMap;
for (int i = 0; i < idCollection()->getSize(); ++i)
{
auto& record = static_cast<const Record<LandTexture>&>(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<const Record<LandTexture>&>(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<uint16_t>::max() - 1;
index = (index + 1) % MaxIndex;
} while (index != startIndex);
}
return results;
}

@ -61,6 +61,11 @@ namespace CSMWorld
const std::string& destination, const std::string& destination,
UniversalId::Type type = UniversalId::Type_None); 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; virtual QModelIndex getModelIndex (const std::string& id, int column) const;
void setRecord (const std::string& id, const RecordBase& record, void setRecord (const std::string& id, const RecordBase& record,
@ -93,6 +98,29 @@ namespace CSMWorld
virtual CollectionBase *idCollection() const; 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<std::string,std::string>;
/// The newly added records
std::vector<std::string> createdRecords;
/// The 1st string is the original id, the 2nd is the mapped id
std::vector<StringPair> recordMapping;
};
LandTextureIdTable(CollectionBase* idCollection, unsigned int features=0);
/// Finds and maps/recreates the specified ids.
ImportResults importTextures(const std::vector<std::string>& ids);
};
} }
#endif #endif

@ -32,7 +32,9 @@ namespace CSMWorld
Feature_Preview = 8, Feature_Preview = 8,
/// Table can not be modified through ordinary means. /// Table can not be modified through ordinary means.
Feature_Constant = 16 Feature_Constant = 16,
Feature_AllowTouch = 32
}; };
private: private:

@ -1,15 +1,30 @@
#include "land.hpp" #include "land.hpp"
#include <sstream> #include <sstream>
#include <stdexcept>
namespace CSMWorld namespace CSMWorld
{ {
void Land::load(ESM::ESMReader &esm, bool &isDeleted) void Land::load(ESM::ESMReader &esm, bool &isDeleted)
{ {
ESM::Land::load(esm, isDeleted); ESM::Land::load(esm, isDeleted);
}
std::string Land::createUniqueRecordId(int x, int y)
{
std::ostringstream stream; std::ostringstream stream;
stream << "#" << mX << " " << mY; stream << "#" << x << " " << y;
mId = stream.str(); 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));
} }
} }

@ -12,10 +12,11 @@ namespace CSMWorld
/// \todo Add worldspace support to the Land record. /// \todo Add worldspace support to the Land record.
struct Land : public ESM::Land struct Land : public ESM::Land
{ {
std::string mId;
/// Loads the metadata and ID /// Loads the metadata and ID
void load (ESM::ESMReader &esm, bool &isDeleted); 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);
}; };
} }

@ -1,5 +1,8 @@
#include "landtexture.hpp" #include "landtexture.hpp"
#include <sstream>
#include <stdexcept>
#include <components/esm/esmreader.hpp> #include <components/esm/esmreader.hpp>
namespace CSMWorld namespace CSMWorld
@ -11,4 +14,21 @@ namespace CSMWorld
mPluginIndex = esm.getIndex(); 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));
}
} }

@ -13,6 +13,11 @@ namespace CSMWorld
int mPluginIndex; int mPluginIndex;
void load (ESM::ESMReader &esm, bool &isDeleted); 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);
}; };
} }

@ -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);
}
}

@ -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

@ -813,6 +813,12 @@ void CSMWorld::RefIdCollection::cloneRecord(const std::string& origin,
mData.insertRecord(*newRecord, type, destination); 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, void CSMWorld::RefIdCollection::appendRecord (const RecordBase& record,
UniversalId::Type type) UniversalId::Type type)
{ {

@ -80,6 +80,8 @@ namespace CSMWorld
const std::string& destination, const std::string& destination,
const UniversalId::Type type); const UniversalId::Type type);
virtual bool touchRecord(const std::string& id);
virtual void appendBlankRecord (const std::string& id, UniversalId::Type type); virtual void appendBlankRecord (const std::string& id, UniversalId::Type type);
///< \param type Will be ignored, unless the collection supports multiple record types ///< \param type Will be ignored, unless the collection supports multiple record types

@ -53,6 +53,8 @@ namespace
{ CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_RunLog, "Run Log", 0 }, { 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_SoundGens, "Sound Generators", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_MagicEffects, "Magic Effects", 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_Pathgrids, "Pathgrids", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_StartScripts, "Start Scripts", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_StartScripts, "Start Scripts", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_MetaDatas, "Meta Data Table", 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_DebugProfile, "Debug Profile", 0 },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_SoundGen, "Sound Generator", 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_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_Pathgrid, "Pathgrid", 0 },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_StartScript, "Start Script", 0 }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_StartScript, "Start Script", 0 },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_MetaData, "Meta Data", 0 }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_MetaData, "Meta Data", 0 },

@ -126,6 +126,10 @@ namespace CSMWorld
Type_SoundGen, Type_SoundGen,
Type_MagicEffects, Type_MagicEffects,
Type_MagicEffect, Type_MagicEffect,
Type_Lands,
Type_Land,
Type_LandTextures,
Type_LandTexture,
Type_Pathgrids, Type_Pathgrids,
Type_Pathgrid, Type_Pathgrid,
Type_StartScripts, Type_StartScripts,

@ -172,6 +172,16 @@ void CSVDoc::View::setupWorldMenu()
setupShortcut("document-world-references", references); setupShortcut("document-world-references", references);
world->addAction (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); QAction *grid = new QAction (tr ("Pathgrid"), this);
connect (grid, SIGNAL (triggered()), this, SLOT (addPathgridSubView())); connect (grid, SIGNAL (triggered()), this, SLOT (addPathgridSubView()));
setupShortcut("document-world-pathgrid", grid); setupShortcut("document-world-pathgrid", grid);
@ -876,6 +886,16 @@ void CSVDoc::View::addRunLogSubView()
addSubView (CSMWorld::UniversalId::Type_RunLog); 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() void CSVDoc::View::addPathgridSubView()
{ {
addSubView (CSMWorld::UniversalId::Type_Pathgrids); addSubView (CSMWorld::UniversalId::Type_Pathgrids);

@ -224,6 +224,10 @@ namespace CSVDoc
void addRunLogSubView(); void addRunLogSubView();
void addLandsSubView();
void addLandTexturesSubView();
void addPathgridSubView(); void addPathgridSubView();
void addStartScriptsSubView(); void addStartScriptsSubView();

@ -26,6 +26,33 @@
#include "terrainstorage.hpp" #include "terrainstorage.hpp"
#include "object.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<CellNodeContainer*>(node->getUserData());
container->getCell()->updateLand();
}
};
}
bool CSVRender::Cell::removeObject (const std::string& id) bool CSVRender::Cell::removeObject (const std::string& id)
{ {
std::map<std::string, Object *>::iterator iter = std::map<std::string, Object *>::iterator iter =
@ -75,10 +102,69 @@ bool CSVRender::Cell::addObjects (int start, int end)
return modified; 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<CSMWorld::Land>& 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, CSVRender::Cell::Cell (CSMWorld::Data& data, osg::Group* rootNode, const std::string& id,
bool deleted) bool deleted)
: mData (data), mId (Misc::StringUtils::lowerCase (id)), mDeleted (deleted), mSubMode (0), : mData (data), mId (Misc::StringUtils::lowerCase (id)), mDeleted (deleted), mSubMode (0),
mSubModeElementMask (0) mSubModeElementMask (0), mUpdateLand(true), mLandDeleted(false)
{ {
std::pair<CSMWorld::CellCoordinates, bool> result = CSMWorld::CellCoordinates::fromId (id); std::pair<CSMWorld::CellCoordinates, bool> result = CSMWorld::CellCoordinates::fromId (id);
@ -86,6 +172,8 @@ CSVRender::Cell::Cell (CSMWorld::Data& data, osg::Group* rootNode, const std::st
mCoordinates = result.first; mCoordinates = result.first;
mCellNode = new osg::Group; mCellNode = new osg::Group;
mCellNode->setUserData(new CellNodeContainer(this));
mCellNode->setUpdateCallback(new CellNodeCallback);
rootNode->addChild(mCellNode); rootNode->addChild(mCellNode);
setCellMarker(); setCellMarker();
@ -99,22 +187,7 @@ CSVRender::Cell::Cell (CSMWorld::Data& data, osg::Group* rootNode, const std::st
addObjects (0, rows-1); addObjects (0, rows-1);
const CSMWorld::IdCollection<CSMWorld::Land>& land = mData.getLand(); updateLand();
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);
}
}
mPathgrid.reset(new Pathgrid(mData, mCellNode, mId, mCoordinates)); mPathgrid.reset(new Pathgrid(mData, mCellNode, mId, mCoordinates));
mCellWater.reset(new CellWater(mData, mCellNode, mId, mCoordinates)); mCellWater.reset(new CellWater(mData, mCellNode, mId, mCoordinates));
@ -285,6 +358,38 @@ void CSVRender::Cell::pathgridRemoved()
mPathgrid->removeGeometry(); 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() void CSVRender::Cell::reloadAssets()
{ {
for (std::map<std::string, Object *>::const_iterator iter (mObjects.begin()); for (std::map<std::string, Object *>::const_iterator iter (mObjects.begin());

@ -57,6 +57,7 @@ namespace CSVRender
bool mDeleted; bool mDeleted;
int mSubMode; int mSubMode;
unsigned int mSubModeElementMask; unsigned int mSubModeElementMask;
bool mUpdateLand, mLandDeleted;
/// Ignored if cell does not have an object with the given ID. /// Ignored if cell does not have an object with the given ID.
/// ///
@ -72,6 +73,9 @@ namespace CSVRender
/// \return Have any objects been added? /// \return Have any objects been added?
bool addObjects (int start, int end); bool addObjects (int start, int end);
void updateLand();
void unloadLand();
public: public:
enum Selection enum Selection
@ -118,6 +122,18 @@ namespace CSVRender
void pathgridRemoved(); 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 reloadAssets();
void setSelection (int elementMask, Selection mode); 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 /// Erase all overrides and restore the visual representation of the cell to its
/// true state. /// true state.
void reset (unsigned int elementMask); void reset (unsigned int elementMask);
friend class CellNodeCallback;
}; };
} }

@ -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() std::string CSVRender::PagedWorldspaceWidget::getStartupInstruction()
{ {
@ -475,6 +541,24 @@ CSVRender::PagedWorldspaceWidget::PagedWorldspaceWidget (QWidget* parent, CSMDoc
connect (&document.getData(), SIGNAL (assetTablesChanged ()), connect (&document.getData(), SIGNAL (assetTablesChanged ()),
this, SLOT (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 // Shortcuts
CSMPrefs::Shortcut* loadCameraCellShortcut = new CSMPrefs::Shortcut("scene-load-cam-cell", this); CSMPrefs::Shortcut* loadCameraCellShortcut = new CSMPrefs::Shortcut("scene-load-cam-cell", this);
connect(loadCameraCellShortcut, SIGNAL(activated()), this, SLOT(loadCameraCell())); connect(loadCameraCellShortcut, SIGNAL(activated()), this, SLOT(loadCameraCell()));

@ -155,6 +155,14 @@ namespace CSVRender
virtual void cellAdded (const QModelIndex& index, int start, int end); 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 assetTablesChanged ();
void loadCameraCell(); void loadCameraCell();

@ -1,5 +1,8 @@
#include "terrainstorage.hpp" #include "terrainstorage.hpp"
#include "../../model/world/land.hpp"
#include "../../model/world/landtexture.hpp"
namespace CSVRender namespace CSVRender
{ {
@ -11,12 +14,9 @@ namespace CSVRender
osg::ref_ptr<const ESMTerrain::LandObject> TerrainStorage::getLand(int cellX, int cellY) osg::ref_ptr<const ESMTerrain::LandObject> 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 // 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 // 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) if (index == -1)
return NULL; return NULL;
@ -26,16 +26,11 @@ namespace CSVRender
const ESM::LandTexture* TerrainStorage::getLandTexture(int index, short plugin) const ESM::LandTexture* TerrainStorage::getLandTexture(int index, short plugin)
{ {
int numRecords = mData.getLandTextures().getSize(); int row = mData.getLandTextures().searchId(CSMWorld::LandTexture::createUniqueRecordId(plugin, index));
if (row == -1)
for (int i=0; i<numRecords; ++i) return nullptr;
{
const CSMWorld::LandTexture* ltex = &mData.getLandTextures().getRecord(i).get();
if (ltex->mIndex == index && ltex->mPluginIndex == plugin)
return ltex;
}
return NULL; return &mData.getLandTextures().getRecord(row).get();
} }
void TerrainStorage::getBounds(float &minX, float &maxX, float &minY, float &maxY) void TerrainStorage::getBounds(float &minX, float &maxX, float &minY, float &maxY)

@ -31,6 +31,9 @@ namespace CSVWorld
virtual void cloneMode(const std::string& originId, virtual void cloneMode(const std::string& originId,
const CSMWorld::UniversalId::Type type) = 0; const CSMWorld::UniversalId::Type type) = 0;
/// Touches a record, if the creator supports it.
virtual void touch(const std::vector<CSMWorld::UniversalId>& ids) = 0;
virtual void setEditLock (bool locked) = 0; virtual void setEditLock (bool locked) = 0;
virtual void toggleWidgets(bool active = true) = 0; virtual void toggleWidgets(bool active = true) = 0;

@ -51,6 +51,11 @@ std::string CSVWorld::GenericCreator::getId() const
return mId->text().toUtf8().constData(); return mId->text().toUtf8().constData();
} }
std::string CSVWorld::GenericCreator::getClonedId() const
{
return mClonedId;
}
std::string CSVWorld::GenericCreator::getIdValidatorResult() const std::string CSVWorld::GenericCreator::getIdValidatorResult() const
{ {
std::string errors; std::string errors;
@ -254,6 +259,22 @@ void CSVWorld::GenericCreator::cloneMode(const std::string& originId,
mClonedType = type; mClonedType = type;
} }
void CSVWorld::GenericCreator::touch(const std::vector<CSMWorld::UniversalId>& ids)
{
// Combine multiple touch commands into one "macro" command
mUndoStack.beginMacro("Touch Records");
CSMWorld::IdTable& table = dynamic_cast<CSMWorld::IdTable&>(*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) void CSVWorld::GenericCreator::toggleWidgets(bool active)
{ {
} }

@ -64,6 +64,8 @@ namespace CSVWorld
virtual std::string getId() const; virtual std::string getId() const;
std::string getClonedId() const;
virtual std::string getIdValidatorResult() const; virtual std::string getIdValidatorResult() const;
/// Allow subclasses to add additional data to \a command. /// Allow subclasses to add additional data to \a command.
@ -103,6 +105,8 @@ namespace CSVWorld
virtual void cloneMode(const std::string& originId, virtual void cloneMode(const std::string& originId,
const CSMWorld::UniversalId::Type type); const CSMWorld::UniversalId::Type type);
virtual void touch(const std::vector<CSMWorld::UniversalId>& ids);
virtual std::string getErrors() const; virtual std::string getErrors() const;
///< Return formatted error descriptions for the current state of the creator. if an empty ///< Return formatted error descriptions for the current state of the creator. if an empty
/// string is returned, there is no error. /// string is returned, there is no error.

@ -0,0 +1,120 @@
#include "landcreator.hpp"
#include <limits>
#include <QLabel>
#include <QSpinBox>
#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<int>::max();
const int MinInt = std::numeric_limits<int>::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<CSMWorld::UniversalId>& ids)
{
// Combine multiple touch commands into one "macro" command
getUndoStack().beginMacro("Touch records");
CSMWorld::IdTable& lands = dynamic_cast<CSMWorld::IdTable&>(*getData().getTableModel(CSMWorld::UniversalId::Type_Lands));
CSMWorld::IdTable& ltexs = dynamic_cast<CSMWorld::IdTable&>(*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<CSMWorld::CreateCommand> command, const std::string& id)
{
if (mCloneMode)
{
CSMWorld::IdTable& lands = dynamic_cast<CSMWorld::IdTable&>(*getData().getTableModel(CSMWorld::UniversalId::Type_Lands));
CSMWorld::IdTable& ltexs = dynamic_cast<CSMWorld::IdTable&>(*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();
}
}

@ -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<CSMWorld::UniversalId>& ids) override;
void focus() override;
void reset() override;
std::string getErrors() const override;
protected:
std::string getId() const override;
void pushCommand(std::unique_ptr<CSMWorld::CreateCommand> command,
const std::string& id) override;
private slots:
void coordChanged(int value);
};
}
#endif

@ -0,0 +1,101 @@
#include "landtexturecreator.hpp"
#include <cstdint>
#include <limits>
#include <QLabel>
#include <QLineEdit>
#include <QSpinBox>
#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<uint16_t>::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<CSMWorld::IdTable&>(*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<CSMWorld::IdTable&>(*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();
}
}

@ -0,0 +1,49 @@
#ifndef CSV_WORLD_LANDTEXTURECREATOR_H
#define CSV_WORLD_LANDTEXTURECREATOR_H
#include <string>
#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

@ -18,6 +18,8 @@
#include "pathgridcreator.hpp" #include "pathgridcreator.hpp"
#include "previewsubview.hpp" #include "previewsubview.hpp"
#include "bodypartcreator.hpp" #include "bodypartcreator.hpp"
#include "landcreator.hpp"
#include "landtexturecreator.hpp"
void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager)
{ {
@ -81,6 +83,12 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager)
manager.add (CSMWorld::UniversalId::Type_Pathgrids, manager.add (CSMWorld::UniversalId::Type_Pathgrids,
new CSVDoc::SubViewFactoryWithCreator<TableSubView, PathgridCreatorFactory>); new CSVDoc::SubViewFactoryWithCreator<TableSubView, PathgridCreatorFactory>);
manager.add (CSMWorld::UniversalId::Type_Lands,
new CSVDoc::SubViewFactoryWithCreator<TableSubView, CreatorFactory<LandCreator> >);
manager.add (CSMWorld::UniversalId::Type_LandTextures,
new CSVDoc::SubViewFactoryWithCreator<TableSubView, CreatorFactory<LandTextureCreator> >);
manager.add (CSMWorld::UniversalId::Type_Globals, manager.add (CSMWorld::UniversalId::Type_Globals,
new CSVDoc::SubViewFactoryWithCreator<TableSubView, CreatorFactory<GlobalCreator> >); new CSVDoc::SubViewFactoryWithCreator<TableSubView, CreatorFactory<GlobalCreator> >);
@ -181,6 +189,12 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager)
manager.add (CSMWorld::UniversalId::Type_Pathgrid, manager.add (CSMWorld::UniversalId::Type_Pathgrid,
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, PathgridCreatorFactory> (false)); new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, PathgridCreatorFactory> (false));
manager.add (CSMWorld::UniversalId::Type_Land,
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, CreatorFactory<LandCreator> >(false));
manager.add (CSMWorld::UniversalId::Type_LandTexture,
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, CreatorFactory<LandTextureCreator> >(false));
manager.add (CSMWorld::UniversalId::Type_DebugProfile, manager.add (CSMWorld::UniversalId::Type_DebugProfile,
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, CreatorFactory<GenericCreator, CSMWorld::Scope_Project | CSMWorld::Scope_Session> > (false)); new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, CreatorFactory<GenericCreator, CSMWorld::Scope_Project | CSMWorld::Scope_Session> > (false));

@ -16,6 +16,7 @@
#include "../../model/world/idtableproxymodel.hpp" #include "../../model/world/idtableproxymodel.hpp"
#include "../../model/world/idtablebase.hpp" #include "../../model/world/idtablebase.hpp"
#include "../../model/world/idtable.hpp" #include "../../model/world/idtable.hpp"
#include "../../model/world/landtexturetableproxymodel.hpp"
#include "../../model/world/record.hpp" #include "../../model/world/record.hpp"
#include "../../model/world/columns.hpp" #include "../../model/world/columns.hpp"
#include "../../model/world/commanddispatcher.hpp" #include "../../model/world/commanddispatcher.hpp"
@ -60,6 +61,9 @@ void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event)
menu.addAction(mCloneAction); menu.addAction(mCloneAction);
} }
if (mTouchAction)
menu.addAction (mTouchAction);
if (mCreateAction) if (mCreateAction)
menu.addAction (mCreateAction); menu.addAction (mCreateAction);
@ -226,17 +230,22 @@ void CSVWorld::Table::mouseDoubleClickEvent (QMouseEvent *event)
CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSVWorld::Table::Table (const CSMWorld::UniversalId& id,
bool createAndDelete, bool sorting, CSMDoc::Document& document) bool createAndDelete, bool sorting, CSMDoc::Document& document)
: DragRecordTable(document), mCreateAction (0), : DragRecordTable(document), mCreateAction (nullptr), mCloneAction(nullptr), mTouchAction(nullptr),
mCloneAction(0), mRecordStatusDisplay (0), mJumpToAddedRecord(false), mUnselectAfterJump(false) mRecordStatusDisplay (0), mJumpToAddedRecord(false), mUnselectAfterJump(false)
{ {
mModel = &dynamic_cast<CSMWorld::IdTableBase&> (*mDocument.getData().getTableModel (id)); mModel = &dynamic_cast<CSMWorld::IdTableBase&> (*mDocument.getData().getTableModel (id));
bool isInfoTable = id.getType() == CSMWorld::UniversalId::Type_TopicInfos || bool isInfoTable = id.getType() == CSMWorld::UniversalId::Type_TopicInfos ||
id.getType() == CSMWorld::UniversalId::Type_JournalInfos; id.getType() == CSMWorld::UniversalId::Type_JournalInfos;
bool isLtexTable = (id.getType() == CSMWorld::UniversalId::Type_LandTextures);
if (isInfoTable) if (isInfoTable)
{ {
mProxyModel = new CSMWorld::InfoTableProxyModel(id.getType(), this); mProxyModel = new CSMWorld::InfoTableProxyModel(id.getType(), this);
} }
else if (isLtexTable)
{
mProxyModel = new CSMWorld::LandTextureTableProxyModel (this);
}
else else
{ {
mProxyModel = new CSMWorld::IdTableProxyModel (this); mProxyModel = new CSMWorld::IdTableProxyModel (this);
@ -302,6 +311,15 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id,
cloneShortcut->associateAction(mCloneAction); 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); mRevertAction = new QAction (tr ("Revert Record"), this);
connect (mRevertAction, SIGNAL (triggered()), mDispatcher, SLOT (executeRevert())); connect (mRevertAction, SIGNAL (triggered()), mDispatcher, SLOT (executeRevert()));
addAction (mRevertAction); addAction (mRevertAction);
@ -442,6 +460,22 @@ void CSVWorld::Table::cloneRecord()
} }
} }
void CSVWorld::Table::touchRecord()
{
if (!mEditLock && mModel->getFeatures() & CSMWorld::IdTableBase::Feature_AllowTouch)
{
std::vector<CSMWorld::UniversalId> 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() void CSVWorld::Table::moveUpRecord()
{ {
if (mEditLock || (mModel->getFeatures() & CSMWorld::IdTableBase::Feature_Constant)) if (mEditLock || (mModel->getFeatures() & CSMWorld::IdTableBase::Feature_Constant))

@ -56,6 +56,7 @@ namespace CSVWorld
QAction *mEditAction; QAction *mEditAction;
QAction *mCreateAction; QAction *mCreateAction;
QAction *mCloneAction; QAction *mCloneAction;
QAction *mTouchAction;
QAction *mRevertAction; QAction *mRevertAction;
QAction *mDeleteAction; QAction *mDeleteAction;
QAction *mMoveUpAction; QAction *mMoveUpAction;
@ -115,6 +116,8 @@ namespace CSVWorld
void cloneRequest(const CSMWorld::UniversalId&); void cloneRequest(const CSMWorld::UniversalId&);
void touchRequest(const std::vector<CSMWorld::UniversalId>& ids);
void closeRequest(); void closeRequest();
void extendedDeleteConfigRequest(const std::vector<std::string> &selectedIds); void extendedDeleteConfigRequest(const std::vector<std::string> &selectedIds);
@ -129,6 +132,8 @@ namespace CSVWorld
void cloneRecord(); void cloneRecord();
void touchRecord();
void moveUpRecord(); void moveUpRecord();
void moveDownRecord(); void moveDownRecord();

@ -249,6 +249,11 @@ void CSVWorld::TableBottomBox::cloneRequest(const std::string& id,
mCreator->focus(); mCreator->focus();
} }
void CSVWorld::TableBottomBox::touchRequest(const std::vector<CSMWorld::UniversalId>& ids)
{
mCreator->touch(ids);
}
void CSVWorld::TableBottomBox::extendedDeleteConfigRequest(const std::vector<std::string> &selectedIds) void CSVWorld::TableBottomBox::extendedDeleteConfigRequest(const std::vector<std::string> &selectedIds)
{ {
extendedConfigRequest(ExtendedCommandConfigurator::Mode_Delete, selectedIds); extendedConfigRequest(ExtendedCommandConfigurator::Mode_Delete, selectedIds);

@ -102,6 +102,7 @@ namespace CSVWorld
void createRequest(); void createRequest();
void cloneRequest(const std::string& id, void cloneRequest(const std::string& id,
const CSMWorld::UniversalId::Type type); const CSMWorld::UniversalId::Type type);
void touchRequest(const std::vector<CSMWorld::UniversalId>&);
void extendedDeleteConfigRequest(const std::vector<std::string> &selectedIds); void extendedDeleteConfigRequest(const std::vector<std::string> &selectedIds);
void extendedRevertConfigRequest(const std::vector<std::string> &selectedIds); void extendedRevertConfigRequest(const std::vector<std::string> &selectedIds);

@ -69,6 +69,9 @@ CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::D
connect (this, SIGNAL(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type)), connect (this, SIGNAL(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type)),
mBottom, SLOT(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<CSMWorld::UniversalId>&)),
mBottom, SLOT(touchRequest(const std::vector<CSMWorld::UniversalId>&)));
connect (mTable, SIGNAL(extendedDeleteConfigRequest(const std::vector<std::string> &)), connect (mTable, SIGNAL(extendedDeleteConfigRequest(const std::vector<std::string> &)),
mBottom, SLOT(extendedDeleteConfigRequest(const std::vector<std::string> &))); mBottom, SLOT(extendedDeleteConfigRequest(const std::vector<std::string> &)));
connect (mTable, SIGNAL(extendedRevertConfigRequest(const std::vector<std::string> &)), connect (mTable, SIGNAL(extendedRevertConfigRequest(const std::vector<std::string> &)),

@ -42,7 +42,7 @@ add_openmw_dir (mwgui
itemmodel containeritemmodel inventoryitemmodel sortfilteritemmodel itemview itemmodel containeritemmodel inventoryitemmodel sortfilteritemmodel itemview
tradeitemmodel companionitemmodel pickpocketitemmodel controllers savegamedialog tradeitemmodel companionitemmodel pickpocketitemmodel controllers savegamedialog
recharge mode videowidget backgroundimage itemwidget screenfader debugwindow spellmodel spellview recharge mode videowidget backgroundimage itemwidget screenfader debugwindow spellmodel spellview
draganddrop timeadvancer jailscreen itemchargeview draganddrop timeadvancer jailscreen itemchargeview keyboardnavigation
) )
add_openmw_dir (mwdialogue add_openmw_dir (mwdialogue

@ -231,11 +231,6 @@ void OMW::Engine::frame(float frametime)
// update GUI // update GUI
mEnvironment.getWindowManager()->onFrame(frametime); mEnvironment.getWindowManager()->onFrame(frametime);
if (mEnvironment.getStateManager()->getState()!=
MWBase::StateManager::State_NoGame)
{
mEnvironment.getWindowManager()->update();
}
unsigned int frameNumber = mViewer->getFrameStamp()->getFrameNumber(); unsigned int frameNumber = mViewer->getFrameStamp()->getFrameNumber();
osg::Stats* stats = mViewer->getViewerStats(); osg::Stats* stats = mViewer->getViewerStats();

@ -2,6 +2,8 @@
#define GAME_MWBASE_DIALOGUEMANAGER_H #define GAME_MWBASE_DIALOGUEMANAGER_H
#include <string> #include <string>
#include <vector>
#include <list>
#include <stdint.h> #include <stdint.h>
@ -42,7 +44,8 @@ namespace MWBase
virtual bool isInChoice() const = 0; virtual bool isInChoice() const = 0;
virtual void startDialogue (const MWWorld::Ptr& actor) = 0; typedef std::pair<std::string, std::string> Response; // title, text
virtual bool startDialogue (const MWWorld::Ptr& actor, Response& response) = 0;
virtual void addTopic (const std::string& topic) = 0; virtual void addTopic (const std::string& topic) = 0;
@ -57,20 +60,24 @@ namespace MWBase
End of tes3mp addition 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<std::pair<std::string, int> >& getChoices() = 0;
virtual bool isGoodbye() = 0;
virtual void goodbye() = 0; virtual void goodbye() = 0;
virtual void say(const MWWorld::Ptr &actor, const std::string &topic) = 0; virtual void say(const MWWorld::Ptr &actor, const std::string &topic) = 0;
//calbacks for the GUI virtual Response keywordSelected (const std::string& keyword) = 0;
virtual void keywordSelected (const std::string& keyword) = 0;
virtual void goodbyeSelected() = 0; virtual void goodbyeSelected() = 0;
virtual void questionAnswered (int answer) = 0; virtual Response questionAnswered (int answer) = 0;
virtual std::list<std::string> 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; virtual int getTemporaryDispositionChange () const = 0;
/// @note This change is temporary and gets discarded when dialogue ends. /// @note This change is temporary and gets discarded when dialogue ends.

@ -248,7 +248,7 @@ namespace MWBase
/// Has the player stolen this item from the given owner? /// Has the player stolen this item from the given owner?
virtual bool isItemStolenFrom(const std::string& itemid, const std::string& ownerid) = 0; virtual bool isItemStolenFrom(const std::string& itemid, const std::string& ownerid) = 0;
virtual bool isAllowedToUse (const MWWorld::Ptr& ptr, const MWWorld::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. /// Turn actor into werewolf or normal form.
virtual void setWerewolf(const MWWorld::Ptr& actor, bool werewolf) = 0; virtual void setWerewolf(const MWWorld::Ptr& actor, bool werewolf) = 0;

@ -7,6 +7,8 @@
#include <map> #include <map>
#include <set> #include <set>
#include <MyGUI_KeyCode.h>
#include "../mwgui/mode.hpp" #include "../mwgui/mode.hpp"
namespace Loading namespace Loading
@ -100,23 +102,17 @@ namespace MWBase
virtual ~WindowManager() {} 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 /// @note This method will block until the video finishes playing
/// (and will continually update the window while doing so) /// (and will continually update the window while doing so)
virtual void playVideo(const std::string& name, bool allowSkipping) = 0; virtual void playVideo(const std::string& name, bool allowSkipping) = 0;
virtual void setNewGame(bool newgame) = 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 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 ///< can be anywhere in the stack
virtual void goToJail(int days) = 0; virtual void goToJail(int days) = 0;
@ -144,7 +140,6 @@ namespace MWBase
virtual bool isAllowed (MWGui::GuiWindow wnd) const = 0; 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 /// \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::InventoryWindow* getInventoryWindow() = 0;
virtual MWGui::CountDialog* getCountDialog() = 0; virtual MWGui::CountDialog* getCountDialog() = 0;
virtual MWGui::ConfirmationDialog* getConfirmationDialog() = 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 setFocusObjectScreenCoords(float min_x, float min_y, float max_x, float max_y) = 0;
virtual void setCursorVisible(bool visible) = 0; virtual void setCursorVisible(bool visible) = 0;
virtual void setCursorActive(bool active) = 0;
virtual void getMousePosition(int &x, int &y) = 0; virtual void getMousePosition(int &x, int &y) = 0;
virtual void getMousePosition(float &x, float &y) = 0; virtual void getMousePosition(float &x, float &y) = 0;
virtual void setDragDrop(bool dragDrop) = 0; virtual void setDragDrop(bool dragDrop) = 0;
@ -238,7 +234,7 @@ namespace MWBase
virtual void showCrosshair(bool show) = 0; virtual void showCrosshair(bool show) = 0;
virtual bool getSubtitlesEnabled() = 0; virtual bool getSubtitlesEnabled() = 0;
virtual bool toggleGui() = 0; virtual bool toggleHud() = 0;
virtual void disallowMouse() = 0; virtual void disallowMouse() = 0;
virtual void allowMouse() = 0; virtual void allowMouse() = 0;
@ -302,21 +298,6 @@ namespace MWBase
virtual bool getPlayerSleeping() = 0; virtual bool getPlayerSleeping() = 0;
virtual void wakeUpPlayer() = 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 showSoulgemDialog (MWWorld::Ptr item) = 0;
virtual void changePointer (const std::string& name) = 0; virtual void changePointer (const std::string& name) = 0;
@ -358,11 +339,11 @@ namespace MWBase
virtual void pinWindow (MWGui::GuiWindow window) = 0; virtual void pinWindow (MWGui::GuiWindow window) = 0;
/// Fade the screen in, over \a time seconds /// 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 /// 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 /// 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 /// Darken the screen to a specified percentage
virtual void setBlindness(const int percent) = 0; virtual void setBlindness(const int percent) = 0;
@ -388,6 +369,8 @@ namespace MWBase
virtual void writeFog(MWWorld::CellStore* cell) = 0; virtual void writeFog(MWWorld::CellStore* cell) = 0;
virtual const MWGui::TextColours& getTextColours() = 0; virtual const MWGui::TextColours& getTextColours() = 0;
virtual bool injectKeyPress(MyGUI::KeyCode key, unsigned int text) = 0;
}; };
} }

@ -567,11 +567,11 @@ namespace MWClass
// by default user can loot friendly actors during death animation // by default user can loot friendly actors during death animation
if (canLoot && !stats.getAiSequence().isInCombat()) if (canLoot && !stats.getAiSequence().isInCombat())
return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr, true)); return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr));
// otherwise wait until death animation // otherwise wait until death animation
if(stats.isDeathAnimationFinished()) if(stats.isDeathAnimationFinished())
return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr, true)); return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr));
// death animation is not finished, do nothing // death animation is not finished, do nothing
return std::shared_ptr<MWWorld::Action> (new MWWorld::FailedAction("")); return std::shared_ptr<MWWorld::Action> (new MWWorld::FailedAction(""));
@ -580,6 +580,9 @@ namespace MWClass
if(stats.getAiSequence().isInCombat()) if(stats.getAiSequence().isInCombat())
return std::shared_ptr<MWWorld::Action>(new MWWorld::FailedAction("")); return std::shared_ptr<MWWorld::Action>(new MWWorld::FailedAction(""));
if(stats.getKnockedDown())
return std::shared_ptr<MWWorld::Action>(new MWWorld::FailedAction(""));
return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionTalk(ptr)); return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionTalk(ptr));
} }

@ -1008,11 +1008,11 @@ namespace MWClass
// by default user can loot friendly actors during death animation // by default user can loot friendly actors during death animation
if (canLoot && !stats.getAiSequence().isInCombat()) if (canLoot && !stats.getAiSequence().isInCombat())
return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr, true)); return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr));
// otherwise wait until death animation // otherwise wait until death animation
if(stats.isDeathAnimationFinished()) if(stats.isDeathAnimationFinished())
return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr, true)); return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr));
// death animation is not finished, do nothing // death animation is not finished, do nothing
return std::shared_ptr<MWWorld::Action> (new MWWorld::FailedAction("")); return std::shared_ptr<MWWorld::Action> (new MWWorld::FailedAction(""));

@ -5,6 +5,7 @@
#include <algorithm> #include <algorithm>
#include <iterator> #include <iterator>
#include <list> #include <list>
#include <iostream>
#include <components/esm/loaddial.hpp> #include <components/esm/loaddial.hpp>
#include <components/esm/loadinfo.hpp> #include <components/esm/loadinfo.hpp>
@ -46,8 +47,6 @@
#include "../mwworld/containerstore.hpp" #include "../mwworld/containerstore.hpp"
#include "../mwworld/esmstore.hpp" #include "../mwworld/esmstore.hpp"
#include "../mwgui/dialogue.hpp"
#include "../mwscript/compilercontext.hpp" #include "../mwscript/compilercontext.hpp"
#include "../mwscript/interpretercontext.hpp" #include "../mwscript/interpretercontext.hpp"
#include "../mwscript/extensions.hpp" #include "../mwscript/extensions.hpp"
@ -72,16 +71,8 @@ namespace MWDialogue
{ {
mChoice = -1; mChoice = -1;
mIsInChoice = false; mIsInChoice = false;
mGoodbye = false;
mCompilerContext.setExtensions (&extensions); mCompilerContext.setExtensions (&extensions);
const MWWorld::Store<ESM::Dialogue> &dialogs =
MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>();
MWWorld::Store<ESM::Dialogue>::iterator it = dialogs.begin();
for (; it != dialogs.end(); ++it)
{
mDialogueMap[Misc::StringUtils::lowerCase(it->mId)] = *it;
}
} }
void DialogueManager::clear() void DialogueManager::clear()
@ -146,17 +137,15 @@ namespace MWDialogue
if (mActorKnownTopics.count( topicId )) if (mActorKnownTopics.count( topicId ))
mKnownTopics.insert( topicId ); mKnownTopics.insert( topicId );
} }
updateTopics();
} }
void DialogueManager::startDialogue (const MWWorld::Ptr& actor) bool DialogueManager::startDialogue (const MWWorld::Ptr& actor, Response& response)
{ {
updateGlobals(); updateGlobals();
// Dialogue with dead actor (e.g. through script) should not be allowed. // Dialogue with dead actor (e.g. through script) should not be allowed.
if (actor.getClass().getCreatureStats(actor).isDead()) if (actor.getClass().getCreatureStats(actor).isDead())
return; return false;
mLastTopic = ""; mLastTopic = "";
mPermanentDispositionChange = 0; mPermanentDispositionChange = 0;
@ -164,6 +153,8 @@ namespace MWDialogue
mChoice = -1; mChoice = -1;
mIsInChoice = false; mIsInChoice = false;
mGoodbye = false;
mChoices.clear();
mActor = actor; mActor = actor;
@ -172,13 +163,6 @@ namespace MWDialogue
mActorKnownTopics.clear(); 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 //greeting
const MWWorld::Store<ESM::Dialogue> &dialogs = const MWWorld::Store<ESM::Dialogue> &dialogs =
MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>(); MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>();
@ -192,9 +176,6 @@ namespace MWDialogue
// Search a response (we do not accept a fallback to "Info refusal" here) // Search a response (we do not accept a fallback to "Info refusal" here)
if (const ESM::DialInfo *info = filter.search (*it, false)) if (const ESM::DialInfo *info = filter.search (*it, false))
{ {
//initialise the GUI
MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Dialogue);
creatureStats.talkedToPlayer(); creatureStats.talkedToPlayer();
if (!info->mSound.empty()) if (!info->mSound.empty())
@ -203,29 +184,23 @@ namespace MWDialogue
} }
// first topics update so that parseText knows the keywords to highlight // first topics update so that parseText knows the keywords to highlight
updateTopics(); updateActorKnownTopics();
parseText (info->mResponse); parseText (info->mResponse);
MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor); 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); executeScript (info->mResultScript, mActor);
mLastTopic = Misc::StringUtils::lowerCase(it->mId); mLastTopic = it->mId;
// update topics again to accommodate changes resulting from executeScript // update topics again to accommodate changes resulting from executeScript
updateTopics(); updateActorKnownTopics();
return; return true;
} }
} }
} }
return false;
// 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);
} }
bool DialogueManager::compile (const std::string& cmd, std::vector<Interpreter::Type_Code>& code, const MWWorld::Ptr& actor) bool DialogueManager::compile (const std::string& cmd, std::vector<Interpreter::Type_Code>& 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); Filter filter (mActor, mChoice, mTalkedTo);
const MWWorld::Store<ESM::Dialogue> &dialogues = const MWWorld::Store<ESM::Dialogue> &dialogues =
@ -312,8 +288,6 @@ namespace MWDialogue
const ESM::Dialogue& dialogue = *dialogues.find (topic); const ESM::Dialogue& dialogue = *dialogues.find (topic);
MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow();
const ESM::DialInfo* info = filter.search(dialogue, true); const ESM::DialInfo* info = filter.search(dialogue, true);
if (info) if (info)
{ {
@ -338,7 +312,7 @@ namespace MWDialogue
title = topic; title = topic;
MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor); 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) if (dialogue.mType == ESM::Dialogue::Topic)
{ {
@ -349,7 +323,7 @@ namespace MWDialogue
{ {
if (iter->mId == info->mId) 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; break;
} }
} }
@ -359,11 +333,12 @@ namespace MWDialogue
mLastTopic = topic; mLastTopic = topic;
} }
else return response;
{ }
// no response found, print a fallback text
win->addResponse ("", topic); const ESM::Dialogue *DialogueManager::searchDialogue(const std::string& id)
} {
return MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>().search(id);
} }
void DialogueManager::updateGlobals() void DialogueManager::updateGlobals()
@ -371,19 +346,16 @@ namespace MWDialogue
MWBase::Environment::get().getWorld()->updateDialogueGlobals(); MWBase::Environment::get().getWorld()->updateDialogueGlobals();
} }
void DialogueManager::updateTopics() void DialogueManager::updateActorKnownTopics()
{ {
updateGlobals(); updateGlobals();
std::list<std::string> keywordList;
int choice = mChoice;
mChoice = -1;
mActorKnownTopics.clear(); mActorKnownTopics.clear();
const MWWorld::Store<ESM::Dialogue> &dialogs = const MWWorld::Store<ESM::Dialogue> &dialogs =
MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>(); MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>();
Filter filter (mActor, mChoice, mTalkedTo); Filter filter (mActor, -1, mTalkedTo);
for (MWWorld::Store<ESM::Dialogue>::iterator iter = dialogs.begin(); iter != dialogs.end(); ++iter) for (MWWorld::Store<ESM::Dialogue>::iterator iter = dialogs.begin(); iter != dialogs.end(); ++iter)
{ {
@ -393,90 +365,42 @@ namespace MWDialogue
{ {
std::string lower = Misc::StringUtils::lowerCase(iter->mId); std::string lower = Misc::StringUtils::lowerCase(iter->mId);
mActorKnownTopics.insert (lower); 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<ESM::NPC>* ref = mActor.get<ESM::NPC>();
if (ref->mBase->mHasAI)
services = ref->mBase->mAiData.mServices;
}
else if (mActor.getTypeName() == typeid(ESM::Creature).name())
{
MWWorld::LiveCellRef<ESM::Creature>* ref = mActor.get<ESM::Creature>();
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<ESM::NPC>()->mBase->getTransport().empty())
|| (mActor.getTypeName() == typeid(ESM::Creature).name() && !mActor.get<ESM::Creature>()->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) std::list<std::string> DialogueManager::getAvailableTopics()
windowServices |= MWGui::DialogueWindow::Service_Repair; {
updateActorKnownTopics();
MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow(); std::list<std::string> 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 // sort again, because the previous sort was case-sensitive
keywordList.sort(Misc::StringUtils::ciLess); keywordList.sort(Misc::StringUtils::ciLess);
win->setKeywords(keywordList); return keywordList;
mChoice = choice;
} }
void DialogueManager::keywordSelected (const std::string& keyword) DialogueManager::Response DialogueManager::keywordSelected (const std::string& keyword)
{ {
Response response;
if(!mIsInChoice) 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) response = executeTopic (keyword);
{
executeTopic (keyword);
}
} }
} }
return response;
updateTopics();
} }
bool DialogueManager::isInChoice() const bool DialogueManager::isInChoice() const
@ -486,8 +410,6 @@ namespace MWDialogue
void DialogueManager::goodbyeSelected() void DialogueManager::goodbyeSelected()
{ {
MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Dialogue);
// Apply disposition change to NPC's base disposition // Apply disposition change to NPC's base disposition
if (mActor.getClass().isNpc()) if (mActor.getClass().isNpc())
{ {
@ -503,37 +425,38 @@ namespace MWDialogue
mTemporaryDispositionChange = 0; mTemporaryDispositionChange = 0;
} }
void DialogueManager::questionAnswered (int answer) DialogueManager::Response DialogueManager::questionAnswered (int answer)
{ {
mChoice = 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); Filter filter (mActor, mChoice, mTalkedTo);
if (mDialogueMap[mLastTopic].mType == ESM::Dialogue::Topic if (dialogue->mType == ESM::Dialogue::Topic || dialogue->mType == ESM::Dialogue::Greeting)
|| mDialogueMap[mLastTopic].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; std::string text = info->mResponse;
parseText (text); parseText (text);
mChoice = -1; mChoice = -1;
mIsInChoice = false; mIsInChoice = false;
MWBase::Environment::get().getWindowManager()->getDialogueWindow()->clearChoices(); mChoices.clear();
MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor); 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, // 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. // in which case it should not be added to the journal.
for (ESM::Dialogue::InfoContainer::const_iterator iter = mDialogueMap[mLastTopic].mInfo.begin(); for (ESM::Dialogue::InfoContainer::const_iterator iter = dialogue->mInfo.begin();
iter!=mDialogueMap[mLastTopic].mInfo.end(); ++iter) iter!=dialogue->mInfo.end(); ++iter)
{ {
if (iter->mId == info->mId) 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; break;
} }
} }
@ -544,32 +467,39 @@ namespace MWDialogue
{ {
mChoice = -1; mChoice = -1;
mIsInChoice = false; 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; mIsInChoice = true;
MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow(); mChoices.push_back(std::make_pair(text, choice));
win->addChoice(question, choice);
} }
void DialogueManager::goodbye() const std::vector<std::pair<std::string, int> >& 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; bool success;
float temp, perm; float temp, perm;
@ -618,7 +548,7 @@ namespace MWDialogue
text = "Bribe"; text = "Bribe";
} }
executeTopic (text + (success ? " Success" : " Fail")); return executeTopic (text + (success ? " Success" : " Fail"));
} }
int DialogueManager::getTemporaryDispositionChange() const int DialogueManager::getTemporaryDispositionChange() const
@ -631,7 +561,7 @@ namespace MWDialogue
mTemporaryDispositionChange += delta; mTemporaryDispositionChange += delta;
} }
bool DialogueManager::checkServiceRefused() bool DialogueManager::checkServiceRefused(Response& response)
{ {
Filter filter (mActor, mChoice, mTalkedTo); Filter filter (mActor, mChoice, mTalkedTo);
@ -639,7 +569,6 @@ namespace MWDialogue
MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>(); MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>();
const ESM::Dialogue& dialogue = *dialogues.find ("Service Refusal"); const ESM::Dialogue& dialogue = *dialogues.find ("Service Refusal");
MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow();
std::vector<const ESM::DialInfo *> infos = filter.list (dialogue, false, false, true); std::vector<const ESM::DialInfo *> infos = filter.list (dialogue, false, false, true);
if (!infos.empty()) if (!infos.empty())
@ -653,8 +582,7 @@ namespace MWDialogue
MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor); MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor);
win->addResponse (Interpreter::fixDefinesDialog(info->mResponse, interpreterContext), response = Response(gmsts.find ("sServiceRefusal")->getString(), Interpreter::fixDefinesDialog(info->mResponse, interpreterContext));
gmsts.find ("sServiceRefusal")->getString());
executeScript (info->mResultScript, mActor); executeScript (info->mResultScript, mActor);
return true; return true;
@ -677,6 +605,12 @@ namespace MWDialogue
return; return;
} }
if (actor.getClass().getCreatureStats(actor).getKnockedDown())
{
// Unconscious actors can not speak
return;
}
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
const ESM::Dialogue *dial = store.get<ESM::Dialogue>().find(topic); const ESM::Dialogue *dial = store.get<ESM::Dialogue>().find(topic);
@ -813,7 +747,7 @@ namespace MWDialogue
if (actor == mActor && !mLastTopic.empty()) if (actor == mActor && !mLastTopic.empty())
{ {
MWBase::Environment::get().getJournal()->removeLastAddedTopicResponse( MWBase::Environment::get().getJournal()->removeLastAddedTopicResponse(
mLastTopic, actor.getClass().getName(actor)); Misc::StringUtils::lowerCase(mLastTopic), actor.getClass().getName(actor));
} }
} }
} }

@ -22,7 +22,6 @@ namespace MWDialogue
{ {
class DialogueManager : public MWBase::DialogueManager class DialogueManager : public MWBase::DialogueManager
{ {
std::map<std::string, ESM::Dialogue> mDialogueMap;
std::set<std::string> mKnownTopics;// Those are the topics the player knows. std::set<std::string> mKnownTopics;// Those are the topics the player knows.
// Modified faction reactions. <Faction1, <Faction2, Difference> > // Modified faction reactions. <Faction1, <Faction2, Difference> >
@ -42,19 +41,24 @@ namespace MWDialogue
int mChoice; int mChoice;
std::string mLastTopic; // last topic ID, lowercase std::string mLastTopic; // last topic ID, lowercase
bool mIsInChoice; bool mIsInChoice;
bool mGoodbye;
std::vector<std::pair<std::string, int> > mChoices;
float mTemporaryDispositionChange; float mTemporaryDispositionChange;
float mPermanentDispositionChange; float mPermanentDispositionChange;
void parseText (const std::string& text); void parseText (const std::string& text);
void updateTopics(); void updateActorKnownTopics();
void updateGlobals(); void updateGlobals();
bool compile (const std::string& cmd, std::vector<Interpreter::Type_Code>& code, const MWWorld::Ptr& actor); bool compile (const std::string& cmd, std::vector<Interpreter::Type_Code>& code, const MWWorld::Ptr& actor);
void executeScript (const std::string& script, 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: public:
@ -64,7 +68,9 @@ namespace MWDialogue
virtual bool isInChoice() const; virtual bool isInChoice() const;
virtual void startDialogue (const MWWorld::Ptr& actor); virtual bool startDialogue (const MWWorld::Ptr& actor, Response& response);
std::list<std::string> getAvailableTopics();
virtual void addTopic (const std::string& topic); virtual void addTopic (const std::string& topic);
@ -79,20 +85,23 @@ namespace MWDialogue
End of tes3mp addition 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<std::pair<std::string, int> >& getChoices();
virtual bool isGoodbye();
virtual void goodbye(); virtual void goodbye();
virtual bool checkServiceRefused (); virtual bool checkServiceRefused (Response& response);
virtual void say(const MWWorld::Ptr &actor, const std::string &topic); virtual void say(const MWWorld::Ptr &actor, const std::string &topic);
//calbacks for the GUI //calbacks for the GUI
virtual void keywordSelected (const std::string& keyword); virtual Response keywordSelected (const std::string& keyword);
virtual void goodbyeSelected(); 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; virtual int getTemporaryDispositionChange () const;
/// @note This change is temporary and gets discarded when dialogue ends. /// @note This change is temporary and gets discarded when dialogue ends.

@ -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 return false; // shouldn't happen, we checked that variable has a type above, so must exist
const MWScript::Locals& locals = mActor.getRefData().getLocals(); const MWScript::Locals& locals = mActor.getRefData().getLocals();
if (locals.isEmpty())
return select.selectCompare(0);
switch (type) switch (type)
{ {
case 's': return select.selectCompare (static_cast<int> (locals.mShorts[index])); case 's': return select.selectCompare (static_cast<int> (locals.mShorts[index]));

@ -1,6 +1,8 @@
#include "alchemywindow.hpp" #include "alchemywindow.hpp"
#include <MyGUI_Gui.h> #include <MyGUI_Gui.h>
#include <MyGUI_Button.h>
#include <MyGUI_EditBox.h>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
@ -19,6 +21,7 @@
#include "sortfilteritemmodel.hpp" #include "sortfilteritemmodel.hpp"
#include "itemview.hpp" #include "itemview.hpp"
#include "itemwidget.hpp" #include "itemwidget.hpp"
#include "widgets.hpp"
namespace MWGui namespace MWGui
{ {
@ -54,12 +57,19 @@ namespace MWGui
mCreateButton->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::onCreateButtonClicked); mCreateButton->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::onCreateButtonClicked);
mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::onCancelButtonClicked); mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::onCancelButtonClicked);
mNameEdit->eventEditSelectAccept += MyGUI::newDelegate(this, &AlchemyWindow::onAccept);
center(); center();
} }
void AlchemyWindow::onAccept(MyGUI::EditBox* sender)
{
onCreateButtonClicked(sender);
}
void AlchemyWindow::onCancelButtonClicked(MyGUI::Widget* _sender) void AlchemyWindow::onCancelButtonClicked(MyGUI::Widget* _sender)
{ {
exit(); MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Alchemy);
} }
void AlchemyWindow::onCreateButtonClicked(MyGUI::Widget* _sender) void AlchemyWindow::onCreateButtonClicked(MyGUI::Widget* _sender)
@ -112,8 +122,9 @@ namespace MWGui
update(); update();
} }
void AlchemyWindow::open() void AlchemyWindow::onOpen()
{ {
mAlchemy->clear();
mAlchemy->setAlchemist (MWMechanics::getPlayer()); mAlchemy->setAlchemist (MWMechanics::getPlayer());
InventoryItemModel* model = new InventoryItemModel(MWMechanics::getPlayer()); InventoryItemModel* model = new InventoryItemModel(MWMechanics::getPlayer());
@ -138,12 +149,8 @@ namespace MWGui
} }
update(); update();
}
void AlchemyWindow::exit() { MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mNameEdit);
mAlchemy->clear();
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Alchemy);
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Inventory);
} }
void AlchemyWindow::onIngredientSelected(MyGUI::Widget* _sender) void AlchemyWindow::onIngredientSelected(MyGUI::Widget* _sender)

@ -5,7 +5,6 @@
#include "../mwmechanics/alchemy.hpp" #include "../mwmechanics/alchemy.hpp"
#include "widgets.hpp"
#include "windowbase.hpp" #include "windowbase.hpp"
namespace MWMechanics namespace MWMechanics
@ -24,8 +23,9 @@ namespace MWGui
public: public:
AlchemyWindow(); AlchemyWindow();
virtual void open(); virtual void onOpen();
virtual void exit();
void onResChange(int, int) { center(); }
private: private:
std::string mSuggestedPotionName; std::string mSuggestedPotionName;
@ -43,6 +43,7 @@ namespace MWGui
void onCancelButtonClicked(MyGUI::Widget* _sender); void onCancelButtonClicked(MyGUI::Widget* _sender);
void onCreateButtonClicked(MyGUI::Widget* _sender); void onCreateButtonClicked(MyGUI::Widget* _sender);
void onIngredientSelected(MyGUI::Widget* _sender); void onIngredientSelected(MyGUI::Widget* _sender);
void onAccept(MyGUI::EditBox*);
void onSelectedItem(int index); void onSelectedItem(int index);

@ -64,11 +64,12 @@ namespace MWGui
okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", "")); okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", ""));
} }
void BirthDialog::open() void BirthDialog::onOpen()
{ {
WindowModal::open(); WindowModal::onOpen();
updateBirths(); updateBirths();
updateSpells(); updateSpells();
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mBirthList);
} }

@ -20,7 +20,9 @@ namespace MWGui
void setBirthId(const std::string &raceId); void setBirthId(const std::string &raceId);
void setNextButtonShow(bool shown); void setNextButtonShow(bool shown);
virtual void open(); virtual void onOpen();
bool exit() { return false; }
// Events // Events
typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void; typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void;

@ -1,6 +1,7 @@
#include "bookwindow.hpp" #include "bookwindow.hpp"
#include <MyGUI_TextBox.h> #include <MyGUI_TextBox.h>
#include <MyGUI_InputManager.h>
#include <components/esm/loadbook.hpp> #include <components/esm/loadbook.hpp>
@ -11,6 +12,7 @@
#include "../mwmechanics/actorutil.hpp" #include "../mwmechanics/actorutil.hpp"
#include "../mwworld/actiontake.hpp" #include "../mwworld/actiontake.hpp"
#include "../mwworld/class.hpp"
#include "formatting.hpp" #include "formatting.hpp"
@ -51,6 +53,11 @@ namespace MWGui
mRightPage->setNeedMouseFocus(true); mRightPage->setNeedMouseFocus(true);
mRightPage->eventMouseWheel += MyGUI::newDelegate(this, &BookWindow::onMouseWheel); 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) if (mNextPageButton->getSize().width == 64)
{ {
// english button has a 7 pixel wide strip of garbage on its right edge // english button has a 7 pixel wide strip of garbage on its right edge
@ -74,15 +81,16 @@ namespace MWGui
mPages.clear(); mPages.clear();
} }
void BookWindow::openBook (MWWorld::Ptr book, bool showTakeButton) void BookWindow::setPtr (const MWWorld::Ptr& book)
{ {
mBook = book; mBook = book;
MWWorld::Ptr player = MWMechanics::getPlayer();
bool showTakeButton = book.getContainerStore() != &player.getClass().getContainerStore(player);
clearPages(); clearPages();
mCurrentPage = 0; mCurrentPage = 0;
MWBase::Environment::get().getWindowManager()->playSound("book open");
MWWorld::LiveCellRef<ESM::Book> *ref = mBook.get<ESM::Book>(); MWWorld::LiveCellRef<ESM::Book> *ref = mBook.get<ESM::Book>();
Formatting::BookFormatter formatter; Formatting::BookFormatter formatter;
@ -92,14 +100,8 @@ namespace MWGui
updatePages(); updatePages();
setTakeButtonShow(showTakeButton); setTakeButtonShow(showTakeButton);
}
void BookWindow::exit() MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCloseButton);
{
// 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);
} }
void BookWindow::setTakeButtonShow(bool show) void BookWindow::setTakeButtonShow(bool show)
@ -108,6 +110,14 @@ namespace MWGui
mTakeButton->setVisible(mTakeButtonShow && mTakeButtonAllowed); 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) void BookWindow::setInventoryAllowed(bool allowed)
{ {
mTakeButtonAllowed = allowed; mTakeButtonAllowed = allowed;
@ -116,7 +126,7 @@ namespace MWGui
void BookWindow::onCloseButtonClicked (MyGUI::Widget* sender) void BookWindow::onCloseButtonClicked (MyGUI::Widget* sender)
{ {
exit(); MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Book);
} }
void BookWindow::onTakeButtonClicked (MyGUI::Widget* sender) void BookWindow::onTakeButtonClicked (MyGUI::Widget* sender)
@ -144,20 +154,16 @@ namespace MWGui
mLeftPageNumber->setCaption( MyGUI::utility::toString(mCurrentPage*2 + 1) ); mLeftPageNumber->setCaption( MyGUI::utility::toString(mCurrentPage*2 + 1) );
mRightPageNumber->setCaption( MyGUI::utility::toString(mCurrentPage*2 + 2) ); mRightPageNumber->setCaption( MyGUI::utility::toString(mCurrentPage*2 + 2) );
//If it is the last page, hide the button "Next Page" MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getKeyFocusWidget();
if ( (mCurrentPage+1)*2 == mPages.size() bool nextPageVisible = (mCurrentPage+1)*2 < mPages.size();
|| (mCurrentPage+1)*2 == mPages.size() + 1) mNextPageButton->setVisible(nextPageVisible);
{ bool prevPageVisible = mCurrentPage != 0;
mNextPageButton->setVisible(false); mPrevPageButton->setVisible(prevPageVisible);
} else {
mNextPageButton->setVisible(true); if (focus == mNextPageButton && !nextPageVisible && prevPageVisible)
} MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mPrevPageButton);
//If it is the fist page, hide the button "Prev Page" else if (focus == mPrevPageButton && !prevPageVisible && nextPageVisible)
if (mCurrentPage == 0) { MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mNextPageButton);
mPrevPageButton->setVisible(false);
} else {
mPrevPageButton->setVisible(true);
}
if (mPages.empty()) if (mPages.empty())
return; return;

@ -14,11 +14,11 @@ namespace MWGui
public: public:
BookWindow(); BookWindow();
virtual void exit(); void setPtr(const MWWorld::Ptr& book);
void openBook(MWWorld::Ptr book, bool showTakeButton);
void setInventoryAllowed(bool allowed); void setInventoryAllowed(bool allowed);
void onResChange(int, int) { center(); }
protected: protected:
void onNextPageButtonClicked (MyGUI::Widget* sender); void onNextPageButtonClicked (MyGUI::Widget* sender);
void onPrevPageButtonClicked (MyGUI::Widget* sender); void onPrevPageButtonClicked (MyGUI::Widget* sender);
@ -27,6 +27,8 @@ namespace MWGui
void onMouseWheel(MyGUI::Widget* _sender, int _rel); void onMouseWheel(MyGUI::Widget* _sender, int _rel);
void setTakeButtonShow(bool show); void setTakeButtonShow(bool show);
void onKeyButtonPressed(MyGUI::Widget* sender, MyGUI::KeyCode key, MyGUI::Char character);
void nextPage(); void nextPage();
void prevPage(); void prevPage();

@ -125,11 +125,12 @@ namespace MWGui
okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", "")); okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", ""));
} }
void PickClassDialog::open() void PickClassDialog::onOpen()
{ {
WindowModal::open (); WindowModal::onOpen ();
updateClasses(); updateClasses();
updateStats(); 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 // Fix layout
layoutVertically(mTextBox, 4); layoutVertically(mTextBox, 4);
layoutVertically(mButtonBar, 6); layoutVertically(mButtonBar, 6);
@ -730,9 +731,10 @@ namespace MWGui
exit(); exit();
} }
void SelectSpecializationDialog::exit() bool SelectSpecializationDialog::exit()
{ {
eventCancel(); eventCancel();
return true;
} }
/* SelectAttributeDialog */ /* SelectAttributeDialog */
@ -778,9 +780,10 @@ namespace MWGui
exit(); exit();
} }
void SelectAttributeDialog::exit() bool SelectAttributeDialog::exit()
{ {
eventCancel(); eventCancel();
return true;
} }
@ -869,9 +872,10 @@ namespace MWGui
exit(); exit();
} }
void SelectSkillDialog::exit() bool SelectSkillDialog::exit()
{ {
eventCancel(); eventCancel();
return true;
} }
/* DescriptionDialog */ /* DescriptionDialog */

@ -21,7 +21,9 @@ namespace MWGui
std::string getText() const; std::string getText() const;
void setButtons(ButtonList &buttons); void setButtons(ButtonList &buttons);
virtual void open(); virtual void onOpen();
bool exit() { return false; }
// Events // Events
typedef MyGUI::delegates::CMultiDelegate1<int> EventHandle_Int; typedef MyGUI::delegates::CMultiDelegate1<int> EventHandle_Int;
@ -67,6 +69,8 @@ namespace MWGui
std::string getClassId() const; std::string getClassId() const;
void setClassId(const std::string &classId); void setClassId(const std::string &classId);
bool exit() { return false; }
// Events // Events
typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void; typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void;
@ -100,7 +104,9 @@ namespace MWGui
void setClassId(const std::string &classId); void setClassId(const std::string &classId);
void setNextButtonShow(bool shown); void setNextButtonShow(bool shown);
virtual void open(); virtual void onOpen();
bool exit() { return false; }
// Events // Events
typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void; typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void;
@ -142,7 +148,7 @@ namespace MWGui
SelectSpecializationDialog(); SelectSpecializationDialog();
~SelectSpecializationDialog(); ~SelectSpecializationDialog();
virtual void exit(); virtual bool exit();
ESM::Class::Specialization getSpecializationId() const { return mSpecializationId; } ESM::Class::Specialization getSpecializationId() const { return mSpecializationId; }
@ -175,7 +181,7 @@ namespace MWGui
SelectAttributeDialog(); SelectAttributeDialog();
~SelectAttributeDialog(); ~SelectAttributeDialog();
virtual void exit(); virtual bool exit();
ESM::Attribute::AttributeID getAttributeId() const { return mAttributeId; } ESM::Attribute::AttributeID getAttributeId() const { return mAttributeId; }
@ -206,7 +212,7 @@ namespace MWGui
SelectSkillDialog(); SelectSkillDialog();
~SelectSkillDialog(); ~SelectSkillDialog();
virtual void exit(); virtual bool exit();
ESM::Skill::SkillEnum getSkillId() const { return mSkillId; } ESM::Skill::SkillEnum getSkillId() const { return mSkillId; }
@ -262,6 +268,8 @@ namespace MWGui
CreateClassDialog(); CreateClassDialog();
virtual ~CreateClassDialog(); virtual ~CreateClassDialog();
bool exit() { return false; }
std::string getName() const; std::string getName() const;
std::string getDescription() const; std::string getDescription() const;
ESM::Class::Specialization getSpecializationId() const; ESM::Class::Specialization getSpecializationId() const;

@ -13,6 +13,7 @@
#include "companionitemmodel.hpp" #include "companionitemmodel.hpp"
#include "draganddrop.hpp" #include "draganddrop.hpp"
#include "countdialog.hpp" #include "countdialog.hpp"
#include "widgets.hpp"
namespace namespace
{ {
@ -103,7 +104,7 @@ void CompanionWindow::onBackgroundSelected()
} }
} }
void CompanionWindow::openCompanion(const MWWorld::Ptr& npc) void CompanionWindow::setPtr(const MWWorld::Ptr& npc)
{ {
mPtr = npc; mPtr = npc;
updateEncumbranceBar(); updateEncumbranceBar();
@ -116,8 +117,9 @@ void CompanionWindow::openCompanion(const MWWorld::Ptr& npc)
setTitle(npc.getClass().getName(npc)); setTitle(npc.getClass().getName(npc));
} }
void CompanionWindow::onFrame() void CompanionWindow::onFrame(float dt)
{ {
checkReferenceAvailable();
updateEncumbranceBar(); updateEncumbranceBar();
} }
@ -139,10 +141,11 @@ void CompanionWindow::updateEncumbranceBar()
void CompanionWindow::onCloseButtonClicked(MyGUI::Widget* _sender) 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) if (mModel && mModel->hasProfit(mPtr) && getProfit(mPtr) < 0)
{ {
@ -151,9 +154,9 @@ void CompanionWindow::exit()
buttons.push_back("#{sCompanionWarningButtonTwo}"); buttons.push_back("#{sCompanionWarningButtonTwo}");
mMessageBoxManager->createInteractiveMessageBox("#{sCompanionWarningMessage}", buttons); mMessageBoxManager->createInteractiveMessageBox("#{sCompanionWarningMessage}", buttons);
mMessageBoxManager->eventButtonPressed += MyGUI::newDelegate(this, &CompanionWindow::onMessageBoxButtonClicked); mMessageBoxManager->eventButtonPressed += MyGUI::newDelegate(this, &CompanionWindow::onMessageBoxButtonClicked);
return false;
} }
else return true;
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Companion);
} }
void CompanionWindow::onMessageBoxButtonClicked(int button) void CompanionWindow::onMessageBoxButtonClicked(int button)
@ -162,7 +165,7 @@ void CompanionWindow::onMessageBoxButtonClicked(int button)
{ {
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Companion); MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Companion);
// Important for Calvus' contract script to work properly // Important for Calvus' contract script to work properly
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Dialogue); MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode();
} }
} }

@ -1,12 +1,16 @@
#ifndef OPENMW_MWGUI_COMPANIONWINDOW_H #ifndef OPENMW_MWGUI_COMPANIONWINDOW_H
#define OPENMW_MWGUI_COMPANIONWINDOW_H #define OPENMW_MWGUI_COMPANIONWINDOW_H
#include "widgets.hpp"
#include "windowbase.hpp" #include "windowbase.hpp"
#include "referenceinterface.hpp" #include "referenceinterface.hpp"
namespace MWGui namespace MWGui
{ {
namespace Widgets
{
class MWDynamicStat;
}
class MessageBoxManager; class MessageBoxManager;
class ItemView; class ItemView;
class DragAndDrop; class DragAndDrop;
@ -18,12 +22,13 @@ namespace MWGui
public: public:
CompanionWindow(DragAndDrop* dragAndDrop, MessageBoxManager* manager); CompanionWindow(DragAndDrop* dragAndDrop, MessageBoxManager* manager);
virtual void exit(); virtual bool exit();
virtual void resetReference(); virtual void resetReference();
void openCompanion(const MWWorld::Ptr& npc); void setPtr(const MWWorld::Ptr& npc);
void onFrame (); void onFrame (float dt);
void clear() { resetReference(); }
private: private:
ItemView* mItemView; ItemView* mItemView;

@ -3,6 +3,9 @@
#include <MyGUI_Button.h> #include <MyGUI_Button.h>
#include <MyGUI_EditBox.h> #include <MyGUI_EditBox.h>
#include "../mwbase/environment.hpp"
#include "../mwbase/windowmanager.hpp"
namespace MWGui namespace MWGui
{ {
ConfirmationDialog::ConfirmationDialog() : ConfirmationDialog::ConfirmationDialog() :
@ -16,14 +19,6 @@ namespace MWGui
mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ConfirmationDialog::onOkButtonClicked); 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) void ConfirmationDialog::askForConfirmation(const std::string& message)
{ {
setVisible(true); setVisible(true);
@ -38,18 +33,21 @@ namespace MWGui
mMessage->setSize(mMessage->getWidth(), mMessage->getTextSize().height + 24); mMessage->setSize(mMessage->getWidth(), mMessage->getTextSize().height + 24);
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mOkButton);
center(); center();
} }
void ConfirmationDialog::exit() bool ConfirmationDialog::exit()
{ {
setVisible(false);
eventCancelClicked(); eventCancelClicked();
return true;
} }
void ConfirmationDialog::onCancelButtonClicked(MyGUI::Widget* _sender) void ConfirmationDialog::onCancelButtonClicked(MyGUI::Widget* _sender)
{ {
setVisible(false);
exit(); exit();
} }

@ -10,8 +10,7 @@ namespace MWGui
public: public:
ConfirmationDialog(); ConfirmationDialog();
void askForConfirmation(const std::string& message); void askForConfirmation(const std::string& message);
void askForConfirmation(const std::string& message, const std::string& confirmMessage, const std::string& cancelMessage); virtual bool exit();
virtual void exit();
typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void; typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void;

@ -145,25 +145,13 @@ namespace MWGui
mCompilerContext.setExtensions (&mExtensions); mCompilerContext.setExtensions (&mExtensions);
} }
void Console::open() void Console::onOpen()
{ {
// Give keyboard focus to the combo box whenever the console is // Give keyboard focus to the combo box whenever the console is
// turned on // turned on
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCommandLine); 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) void Console::setFont(const std::string &fntName)
{ {
mHistory->setFontName(fntName); mHistory->setFontName(fntName);
@ -227,6 +215,11 @@ namespace MWGui
} }
} }
void Console::clear()
{
resetReference();
}
void Console::keyPress(MyGUI::Widget* _sender, void Console::keyPress(MyGUI::Widget* _sender,
MyGUI::KeyCode key, MyGUI::KeyCode key,
MyGUI::Char _char) MyGUI::Char _char)

@ -39,10 +39,7 @@ namespace MWGui
Console(int w, int h, bool consoleOnlyScripts); Console(int w, int h, bool consoleOnlyScripts);
virtual void open(); virtual void onOpen();
virtual void close();
virtual void exit();
void setFont(const std::string &fntName); void setFont(const std::string &fntName);
@ -63,6 +60,8 @@ namespace MWGui
void executeFile (const std::string& path); void executeFile (const std::string& path);
void clear();
virtual void resetReference (); virtual void resetReference ();
protected: protected:

@ -36,6 +36,7 @@
#include "itemview.hpp" #include "itemview.hpp"
#include "itemwidget.hpp" #include "itemwidget.hpp"
#include "inventoryitemmodel.hpp" #include "inventoryitemmodel.hpp"
#include "containeritemmodel.hpp"
#include "sortfilteritemmodel.hpp" #include "sortfilteritemmodel.hpp"
#include "pickpocketitemmodel.hpp" #include "pickpocketitemmodel.hpp"
#include "draganddrop.hpp" #include "draganddrop.hpp"
@ -61,7 +62,6 @@ namespace MWGui
mDisposeCorpseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ContainerWindow::onDisposeCorpseButtonClicked); mDisposeCorpseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ContainerWindow::onDisposeCorpseButtonClicked);
mCloseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ContainerWindow::onCloseButtonClicked); mCloseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ContainerWindow::onCloseButtonClicked);
mCloseButton->eventKeyButtonPressed += MyGUI::newDelegate(this, &ContainerWindow::onKeyPressed);
mTakeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ContainerWindow::onTakeAllButtonClicked); mTakeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ContainerWindow::onTakeAllButtonClicked);
setCoord(200,0,600,300); setCoord(200,0,600,300);
@ -219,22 +219,39 @@ namespace MWGui
dropItem(); 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); mwmp::Main::get().getCellController()->openContainer(container, loot);
/*
End of tes3mp addition
*/
mPickpocketDetected = false; mPickpocketDetected = false;
mPtr = container; 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 if (mPtr.getClass().isNpc() && !loot)
MWWorld::Ptr player = MWMechanics::getPlayer(); {
mModel = new PickpocketItemModel(player, new InventoryItemModel(container), // we are stealing stuff
!mPtr.getClass().getCreatureStats(mPtr).getKnockedDown()); MWWorld::Ptr player = MWMechanics::getPlayer();
mModel = new PickpocketItemModel(player, new InventoryItemModel(container),
!mPtr.getClass().getCreatureStats(mPtr).getKnockedDown());
}
else
mModel = new InventoryItemModel(container);
} }
else else
mModel = new InventoryItemModel(container); {
mModel = new ContainerItemModel(container);
}
mDisposeCorpseButton->setVisible(loot); mDisposeCorpseButton->setVisible(loot);
@ -248,14 +265,6 @@ namespace MWGui
setTitle(container.getClass().getName(container)); 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() void ContainerWindow::resetReference()
{ {
ReferenceInterface::resetReference(); ReferenceInterface::resetReference();
@ -264,10 +273,19 @@ namespace MWGui
mSortModel = NULL; 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); mwmp::Main::get().getCellController()->closeContainer(mPtr);
WindowBase::close(); /*
End of tes3mp addition
*/
WindowBase::onClose();
if (dynamic_cast<PickpocketItemModel*>(mModel) if (dynamic_cast<PickpocketItemModel*>(mModel)
// Make sure we were actually closed, rather than just temporarily hidden (e.g. console or main menu opened) // 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) void ContainerWindow::onCloseButtonClicked(MyGUI::Widget* _sender)
{ {
exit(); MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container);
} }
void ContainerWindow::onTakeAllButtonClicked(MyGUI::Widget* _sender) void ContainerWindow::onTakeAllButtonClicked(MyGUI::Widget* _sender)
@ -420,9 +430,8 @@ namespace MWGui
MWMechanics::Pickpocket pickpocket(player, mPtr); MWMechanics::Pickpocket pickpocket(player, mPtr);
if (pickpocket.pick(item.mBase, count)) if (pickpocket.pick(item.mBase, count))
{ {
int value = item.mBase.getClass().getValue(item.mBase) * count;
MWBase::Environment::get().getMechanicsManager()->commitCrime( 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); MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Container);
mPickpocketDetected = true; mPickpocketDetected = true;
return false; return false;

@ -33,12 +33,13 @@ namespace MWGui
public: public:
ContainerWindow(DragAndDrop* dragAndDrop); ContainerWindow(DragAndDrop* dragAndDrop);
void openContainer(const MWWorld::Ptr& container, bool loot=false); void setPtr(const MWWorld::Ptr& container);
virtual void close(); virtual void onClose();
void clear() { resetReference(); }
virtual void resetReference(); void onFrame(float dt) { checkReferenceAvailable(); }
virtual void exit(); virtual void resetReference();
private: private:
DragAndDrop* mDragAndDrop; DragAndDrop* mDragAndDrop;
@ -61,7 +62,6 @@ namespace MWGui
void onCloseButtonClicked(MyGUI::Widget* _sender); void onCloseButtonClicked(MyGUI::Widget* _sender);
void onTakeAllButtonClicked(MyGUI::Widget* _sender); void onTakeAllButtonClicked(MyGUI::Widget* _sender);
void onDisposeCorpseButtonClicked(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? /// @return is taking the item allowed?
bool onTakeItem(const ItemStack& item, int count); bool onTakeItem(const ItemStack& item, int count);

@ -6,8 +6,11 @@
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwmechanics/actorutil.hpp"
namespace namespace
{ {
@ -47,6 +50,19 @@ ContainerItemModel::ContainerItemModel (const MWWorld::Ptr& source)
mItemSources.push_back(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) ItemStack ContainerItemModel::getItem (ModelIndex index)
{ {
if (index < 0) if (index < 0)

@ -17,6 +17,7 @@ namespace MWGui
ContainerItemModel (const MWWorld::Ptr& source); ContainerItemModel (const MWWorld::Ptr& source);
virtual bool allowedToUseItems() const;
virtual ItemStack getItem (ModelIndex index); virtual ItemStack getItem (ModelIndex index);
virtual ModelIndex getIndex (ItemStack item); virtual ModelIndex getIndex (ItemStack item);
virtual size_t getItemCount(); virtual size_t getItemCount();

@ -56,19 +56,9 @@ namespace MWGui
mItemEdit->setValue(maxCount); 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) void CountDialog::onCancelButtonClicked(MyGUI::Widget* _sender)
{ {
cancel(); setVisible(false);
} }
void CountDialog::onOkButtonClicked(MyGUI::Widget* _sender) void CountDialog::onOkButtonClicked(MyGUI::Widget* _sender)

@ -15,8 +15,6 @@ namespace MWGui
public: public:
CountDialog(); CountDialog();
void openCountDialog(const std::string& item, const std::string& message, const int maxCount); void openCountDialog(const std::string& item, const std::string& message, const int maxCount);
void cancel();
virtual void exit();
typedef MyGUI::delegates::CMultiDelegate2<MyGUI::Widget*, int> EventHandle_WidgetInt; typedef MyGUI::delegates::CMultiDelegate2<MyGUI::Widget*, int> EventHandle_WidgetInt;

@ -3,6 +3,8 @@
#include <MyGUI_LanguageManager.h> #include <MyGUI_LanguageManager.h>
#include <MyGUI_Window.h> #include <MyGUI_Window.h>
#include <MyGUI_ProgressBar.h> #include <MyGUI_ProgressBar.h>
#include <MyGUI_ScrollBar.h>
#include <MyGUI_Button.h>
#include <components/widgets/list.hpp> #include <components/widgets/list.hpp>
#include <components/translation/translation.hpp> #include <components/translation/translation.hpp>
@ -20,7 +22,6 @@
#include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/creaturestats.hpp"
#include "../mwmechanics/actorutil.hpp" #include "../mwmechanics/actorutil.hpp"
#include "widgets.hpp"
#include "bookpage.hpp" #include "bookpage.hpp"
#include "textcolours.hpp" #include "textcolours.hpp"
@ -52,7 +53,7 @@ namespace MWGui
void PersuasionDialog::onCancel(MyGUI::Widget *sender) void PersuasionDialog::onCancel(MyGUI::Widget *sender)
{ {
exit(); setVisible(false);
} }
void PersuasionDialog::onPersuade(MyGUI::Widget *sender) void PersuasionDialog::onPersuade(MyGUI::Widget *sender)
@ -68,14 +69,15 @@ namespace MWGui
else /*if (sender == mBribe1000Button)*/ else /*if (sender == mBribe1000Button)*/
type = MWBase::MechanicsManager::PT_Bribe1000; 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); setVisible(false);
} }
void PersuasionDialog::open() void PersuasionDialog::onOpen()
{ {
WindowModal::open();
center(); center();
MWWorld::Ptr player = MWMechanics::getPlayer(); MWWorld::Ptr player = MWMechanics::getPlayer();
@ -86,11 +88,12 @@ namespace MWGui
mBribe1000Button->setEnabled (playerGold >= 1000); mBribe1000Button->setEnabled (playerGold >= 1000);
mGoldLabel->setCaptionWithReplacing("#{sGold}: " + MyGUI::utility::toString(playerGold)); 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() void Choice::activated()
{ {
MWBase::Environment::get().getWindowManager()->playSound("Menu Click"); MWBase::Environment::get().getWindowManager()->playSound("Menu Click");
MWBase::Environment::get().getDialogueManager()->questionAnswered(mChoiceId); eventChoiceActivated(mChoiceId);
} }
void Topic::activated() void Topic::activated()
{ {
MWBase::Environment::get().getWindowManager()->playSound("Menu Click"); MWBase::Environment::get().getWindowManager()->playSound("Menu Click");
MWBase::Environment::get().getDialogueManager()->keywordSelected(Misc::StringUtils::lowerCase(mTopicId)); eventTopicActivated(mTopicId);
} }
void Goodbye::activated() void Goodbye::activated()
{ {
MWBase::Environment::get().getWindowManager()->playSound("Menu Click"); MWBase::Environment::get().getWindowManager()->playSound("Menu Click");
MWBase::Environment::get().getDialogueManager()->goodbyeSelected(); eventActivated();
} }
// -------------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------------
DialogueWindow::DialogueWindow() DialogueWindow::DialogueWindow()
: WindowBase("openmw_dialogue_window.layout") : WindowBase("openmw_dialogue_window.layout")
, mServices(0) , mIsCompanion(false)
, mEnabled(false)
, mGoodbye(false) , mGoodbye(false)
, mPersuasionDialog() , mPersuasionDialog()
{ {
@ -251,17 +250,17 @@ namespace MWGui
center(); center();
mPersuasionDialog.setVisible(false); mPersuasionDialog.setVisible(false);
mPersuasionDialog.eventPersuadeMsg += MyGUI::newDelegate(this, &DialogueWindow::onPersuadeResult);
//History view //History view
getWidget(mHistory, "History"); getWidget(mHistory, "History");
//Topics list //Topics list
getWidget(mTopicsList, "TopicsList"); getWidget(mTopicsList, "TopicsList");
mTopicsList->eventItemSelected += MyGUI::newDelegate(this, &DialogueWindow::onSelectTopic); mTopicsList->eventItemSelected += MyGUI::newDelegate(this, &DialogueWindow::onSelectListItem);
MyGUI::Button* byeButton; getWidget(mGoodbyeButton, "ByeButton");
getWidget(byeButton, "ByeButton"); mGoodbyeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &DialogueWindow::onByeClicked);
byeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &DialogueWindow::onByeClicked);
getWidget(mDispositionBar, "Disposition"); getWidget(mDispositionBar, "Disposition");
getWidget(mDispositionText,"DispositionText"); getWidget(mDispositionText,"DispositionText");
@ -276,18 +275,36 @@ namespace MWGui
mMainWidget->castType<MyGUI::Window>()->eventWindowChangeCoord += MyGUI::newDelegate(this, &DialogueWindow::onWindowResize); mMainWidget->castType<MyGUI::Window>()->eventWindowChangeCoord += MyGUI::newDelegate(this, &DialogueWindow::onWindowResize);
} }
void DialogueWindow::exit() DialogueWindow::~DialogueWindow()
{ {
if ((!mEnabled || MWBase::Environment::get().getDialogueManager()->isInChoice()) mPersuasionDialog.eventPersuadeMsg.clear();
&& !mGoodbye)
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 return false;
MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu);
} }
else else
{ {
resetReference();
MWBase::Environment::get().getDialogueManager()->goodbyeSelected(); MWBase::Environment::get().getDialogueManager()->goodbyeSelected();
mTopicsList->scrollToTop(); mTopicsList->scrollToTop();
return true;
} }
} }
@ -312,12 +329,13 @@ namespace MWGui
void DialogueWindow::onByeClicked(MyGUI::Widget* _sender) 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; return;
int separatorPos = 0; int separatorPos = 0;
@ -328,63 +346,84 @@ namespace MWGui
} }
if (id >= separatorPos) if (id >= separatorPos)
MWBase::Environment::get().getDialogueManager()->keywordSelected(Misc::StringUtils::lowerCase(topic)); {
onTopicActivated(topic);
if (mGoodbyeButton->getEnabled())
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mGoodbyeButton);
}
else else
{ {
const MWWorld::Store<ESM::GameSetting> &gmst = const MWWorld::Store<ESM::GameSetting> &gmst =
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>(); MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
MWBase::DialogueManager::Response response;
if (topic == gmst.find("sPersuasion")->getString()) if (topic == gmst.find("sPersuasion")->getString())
mPersuasionDialog.setVisible(true); mPersuasionDialog.setVisible(true);
else if (topic == gmst.find("sCompanionShare")->getString()) else if (topic == gmst.find("sCompanionShare")->getString())
MWBase::Environment::get().getWindowManager()->showCompanionWindow(mPtr); MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Companion, mPtr);
else if (!MWBase::Environment::get().getDialogueManager()->checkServiceRefused()) else if (!MWBase::Environment::get().getDialogueManager()->checkServiceRefused(response))
{ {
if (topic == gmst.find("sBarter")->getString()) 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()) 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()) 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()) 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()) 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()) 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()) 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; mGoodbye = false;
mEnabled = true;
bool sameActor = (mPtr == actor); bool sameActor = (mPtr == actor);
mPtr = actor; mPtr = actor;
mTopicsList->setEnabled(true); mTopicsList->setEnabled(true);
setTitle(npcName); setTitle(mPtr.getClass().getName(mPtr));
clearChoices();
mTopicsList->clear(); mTopicsList->clear();
if (resetHistory || !sameActor) if (!sameActor)
{ {
for (std::vector<DialogueText*>::iterator it = mHistoryContents.begin(); it != mHistoryContents.end(); ++it) for (std::vector<DialogueText*>::iterator it = mHistoryContents.begin(); it != mHistoryContents.end(); ++it)
delete (*it); delete (*it);
mHistoryContents.clear(); mHistoryContents.clear();
mKeywords.clear();
updateTopicsPane();
} }
for (std::vector<Link*>::iterator it = mLinks.begin(); it != mLinks.end(); ++it) for (std::vector<Link*>::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(); mLinks.clear();
updateOptions(); updateDisposition();
restock(); restock();
addResponse(response.first, response.second, false);
} }
void DialogueWindow::restock() 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<std::string> keyWords) void DialogueWindow::setKeywords(std::list<std::string> keyWords)
{
if (mKeywords == keyWords && isCompanion() == mIsCompanion)
return;
mIsCompanion = isCompanion();
mKeywords = keyWords;
updateTopicsPane();
}
void DialogueWindow::updateTopicsPane()
{ {
mTopicsList->clear(); mTopicsList->clear();
for (std::map<std::string, Link*>::iterator it = mTopicLinks.begin(); it != mTopicLinks.end(); ++it) for (std::map<std::string, Link*>::iterator it = mTopicLinks.begin(); it != mTopicLinks.end(); ++it)
delete it->second; mDeleteLater.push_back(it->second);
mTopicLinks.clear(); mTopicLinks.clear();
mKeywordSearch.clear(); mKeywordSearch.clear();
bool isCompanion = !mPtr.getClass().getScript(mPtr).empty() int services = mPtr.getClass().getServices(mPtr);
&& mPtr.getRefData().getLocals().getIntVar(mPtr.getClass().getScript(mPtr), "companion");
bool travel = (mPtr.getTypeName() == typeid(ESM::NPC).name() && !mPtr.get<ESM::NPC>()->mBase->getTransport().empty())
|| (mPtr.getTypeName() == typeid(ESM::Creature).name() && !mPtr.get<ESM::Creature>()->mBase->getTransport().empty());
const MWWorld::Store<ESM::GameSetting> &gmst = const MWWorld::Store<ESM::GameSetting> &gmst =
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>(); MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
@ -418,39 +476,40 @@ namespace MWGui
if (mPtr.getTypeName() == typeid(ESM::NPC).name()) if (mPtr.getTypeName() == typeid(ESM::NPC).name())
mTopicsList->addItem(gmst.find("sPersuasion")->getString()); mTopicsList->addItem(gmst.find("sPersuasion")->getString());
if (mServices & Service_Trade) if (services & ESM::NPC::AllItems)
mTopicsList->addItem(gmst.find("sBarter")->getString()); mTopicsList->addItem(gmst.find("sBarter")->getString());
if (mServices & Service_BuySpells) if (services & ESM::NPC::Spells)
mTopicsList->addItem(gmst.find("sSpells")->getString()); mTopicsList->addItem(gmst.find("sSpells")->getString());
if (mServices & Service_Travel) if (travel)
mTopicsList->addItem(gmst.find("sTravel")->getString()); mTopicsList->addItem(gmst.find("sTravel")->getString());
if (mServices & Service_CreateSpells) if (services & ESM::NPC::Spellmaking)
mTopicsList->addItem(gmst.find("sSpellmakingMenuTitle")->getString()); mTopicsList->addItem(gmst.find("sSpellmakingMenuTitle")->getString());
if (mServices & Service_Enchant) if (services & ESM::NPC::Enchanting)
mTopicsList->addItem(gmst.find("sEnchanting")->getString()); mTopicsList->addItem(gmst.find("sEnchanting")->getString());
if (mServices & Service_Training) if (services & ESM::NPC::Training)
mTopicsList->addItem(gmst.find("sServiceTrainingTitle")->getString()); mTopicsList->addItem(gmst.find("sServiceTrainingTitle")->getString());
if (mServices & Service_Repair) if (services & ESM::NPC::Repair)
mTopicsList->addItem(gmst.find("sRepair")->getString()); mTopicsList->addItem(gmst.find("sRepair")->getString());
if (isCompanion) if (isCompanion())
mTopicsList->addItem(gmst.find("sCompanionShare")->getString()); mTopicsList->addItem(gmst.find("sCompanionShare")->getString());
if (mTopicsList->getItemCount() > 0) if (mTopicsList->getItemCount() > 0)
mTopicsList->addSeparator(); mTopicsList->addSeparator();
for(std::list<std::string>::iterator it = keyWords.begin(); it != keyWords.end(); ++it) for(std::list<std::string>::iterator it = mKeywords.begin(); it != mKeywords.end(); ++it)
{ {
mTopicsList->addItem(*it); mTopicsList->addItem(*it);
Topic* t = new Topic(*it); Topic* t = new Topic(*it);
t->eventTopicActivated += MyGUI::newDelegate(this, &DialogueWindow::onTopicActivated);
mTopicLinks[Misc::StringUtils::lowerCase(*it)] = t; mTopicLinks[Misc::StringUtils::lowerCase(*it)] = t;
mKeywordSearch.seed(Misc::StringUtils::lowerCase(*it), intptr_t(t)); mKeywordSearch.seed(Misc::StringUtils::lowerCase(*it), intptr_t(t));
@ -484,9 +543,11 @@ namespace MWGui
typesetter->sectionBreak(9); typesetter->sectionBreak(9);
// choices // choices
const TextColours& textColours = MWBase::Environment::get().getWindowManager()->getTextColours(); const TextColours& textColours = MWBase::Environment::get().getWindowManager()->getTextColours();
for (std::vector<std::pair<std::string, int> >::iterator it = mChoices.begin(); it != mChoices.end(); ++it) mChoices = MWBase::Environment::get().getDialogueManager()->getChoices();
for (std::vector<std::pair<std::string, int> >::const_iterator it = mChoices.begin(); it != mChoices.end(); ++it)
{ {
Choice* link = new Choice(it->second); Choice* link = new Choice(it->second);
link->eventChoiceActivated += MyGUI::newDelegate(this, &DialogueWindow::onChoiceActivated);
mLinks.push_back(link); mLinks.push_back(link);
typesetter->lineBreak(); typesetter->lineBreak();
@ -496,9 +557,11 @@ namespace MWGui
typesetter->write(questionStyle, to_utf8_span(it->first.c_str())); typesetter->write(questionStyle, to_utf8_span(it->first.c_str()));
} }
mGoodbye = MWBase::Environment::get().getDialogueManager()->isGoodbye();
if (mGoodbye) if (mGoodbye)
{ {
Goodbye* link = new Goodbye(); Goodbye* link = new Goodbye();
link->eventActivated += MyGUI::newDelegate(this, &DialogueWindow::onGoodbyeActivated);
mLinks.push_back(link); mLinks.push_back(link);
std::string goodbye = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("sGoodbye")->getString(); std::string goodbye = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("sGoodbye")->getString();
BookTypesetter::Style* questionStyle = typesetter->createHotStyle(body, textColours.answer, textColours.answerOver, BookTypesetter::Style* questionStyle = typesetter->createHotStyle(body, textColours.answer, textColours.answerOver,
@ -528,10 +591,11 @@ namespace MWGui
onScrollbarMoved(mScrollBar, 0); onScrollbarMoved(mScrollBar, 0);
} }
MyGUI::Button* byeButton;
getWidget(byeButton, "ByeButton");
bool goodbyeEnabled = !MWBase::Environment::get().getDialogueManager()->isInChoice() || mGoodbye; 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; bool topicsEnabled = !MWBase::Environment::get().getDialogueManager()->isInChoice() && !mGoodbye;
mTopicsList->setEnabled(topicsEnabled); mTopicsList->setEnabled(topicsEnabled);
@ -542,59 +606,47 @@ namespace MWGui
reinterpret_cast<Link*>(link)->activated(); reinterpret_cast<Link*>(link)->activated();
} }
void DialogueWindow::onScrollbarMoved(MyGUI::ScrollBar *sender, size_t pos) void DialogueWindow::onTopicActivated(const std::string &topicId)
{ {
mHistory->setPosition(0, static_cast<int>(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 MWBase::DialogueManager::Response response = MWBase::Environment::get().getDialogueManager()->questionAnswered(id);
// case-smashed - thus we have to retrieve the correct case addResponse(response.first, response.second);
// of the title through the topic list. }
std::string realTitle = title;
if (realTitle != "")
{
for (size_t i=0; i<mTopicsList->getItemCount(); ++i)
{
std::string item = mTopicsList->getItemNameAt(i);
if (Misc::StringUtils::ciEqual(item, title))
{
realTitle = item;
break;
}
}
}
mHistoryContents.push_back(new Response(text, realTitle, needMargin)); void DialogueWindow::onGoodbyeActivated()
updateHistory(); {
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)); mHistory->setPosition(0, static_cast<int>(pos) * -1);
updateHistory();
} }
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(); updateHistory();
updateTopics();
} }
void DialogueWindow::clearChoices() void DialogueWindow::addMessageBox(const std::string& text)
{ {
mChoices.clear(); mHistoryContents.push_back(new Message(text));
updateHistory(); updateHistory();
} }
void DialogueWindow::updateOptions() void DialogueWindow::updateDisposition()
{ {
//Clear the list of topics
mTopicsList->clear();
bool dispositionVisible = false; bool dispositionVisible = false;
if (mPtr.getClass().isNpc()) if (!mPtr.isEmpty() && mPtr.getClass().isNpc())
{ {
dispositionVisible = true; dispositionVisible = true;
mDispositionBar->setProgressRange(100); mDispositionBar->setProgressRange(100);
@ -620,26 +672,38 @@ namespace MWGui
} }
} }
void DialogueWindow::goodbye() void DialogueWindow::onReferenceUnavailable()
{ {
mGoodbye = true; MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Dialogue);
mEnabled = false;
updateHistory();
} }
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()) setKeywords(MWBase::Environment::get().getDialogueManager()->getAvailableTopics());
{ }
int disp = MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mPtr);
mDispositionBar->setProgressRange(100); bool DialogueWindow::isCompanion()
mDispositionBar->setProgressPosition(disp); {
mDispositionText->setCaption(MyGUI::utility::toString(disp)+std::string("/100")); 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);
} }
} }

@ -8,6 +8,8 @@
#include "../mwdialogue/keywordsearch.hpp" #include "../mwdialogue/keywordsearch.hpp"
#include <MyGUI_Delegate.h>
namespace Gui namespace Gui
{ {
class MWList; class MWList;
@ -20,16 +22,17 @@ namespace MWGui
namespace MWGui namespace MWGui
{ {
class DialogueHistoryViewModel;
class BookPage;
class PersuasionDialog : public WindowModal class PersuasionDialog : public WindowModal
{ {
public: public:
PersuasionDialog(); PersuasionDialog();
virtual void open(); typedef MyGUI::delegates::CMultiDelegate2<const std::string&, const std::string&> EventHandle_Result;
virtual void exit(); EventHandle_Result eventPersuadeMsg;
virtual void onOpen();
virtual MyGUI::Widget* getDefaultKeyFocus();
private: private:
MyGUI::Button* mCancelButton; MyGUI::Button* mCancelButton;
@ -54,6 +57,8 @@ namespace MWGui
struct Topic : Link struct Topic : Link
{ {
typedef MyGUI::delegates::CMultiDelegate1<const std::string&> EventHandle_TopicId;
EventHandle_TopicId eventTopicActivated;
Topic(const std::string& id) : mTopicId(id) {} Topic(const std::string& id) : mTopicId(id) {}
std::string mTopicId; std::string mTopicId;
virtual void activated (); virtual void activated ();
@ -61,6 +66,8 @@ namespace MWGui
struct Choice : Link struct Choice : Link
{ {
typedef MyGUI::delegates::CMultiDelegate1<int> EventHandle_ChoiceId;
EventHandle_ChoiceId eventChoiceActivated;
Choice(int id) : mChoiceId(id) {} Choice(int id) : mChoiceId(id) {}
int mChoiceId; int mChoiceId;
virtual void activated (); virtual void activated ();
@ -68,6 +75,8 @@ namespace MWGui
struct Goodbye : Link struct Goodbye : Link
{ {
typedef MyGUI::delegates::CMultiDelegate0 Event_Activated;
Event_Activated eventActivated;
virtual void activated (); virtual void activated ();
}; };
@ -99,46 +108,41 @@ namespace MWGui
{ {
public: public:
DialogueWindow(); DialogueWindow();
~DialogueWindow();
virtual void exit(); void onTradeComplete();
virtual bool exit();
// Events // Events
typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void; typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void;
void notifyLinkClicked (TypesetBook::InteractiveId link); 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<std::string> keyWord); void setKeywords(std::list<std::string> 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 addMessageBox(const std::string& text);
void addChoice(const std::string& choice, int id); void onFrame(float dt);
void clearChoices(); void clear() { resetReference(); }
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
};
protected: 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 onByeClicked(MyGUI::Widget* _sender);
void onMouseWheel(MyGUI::Widget* _sender, int _rel); void onMouseWheel(MyGUI::Widget* _sender, int _rel);
void onWindowResize(MyGUI::Window* _sender); 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); void onScrollbarMoved (MyGUI::ScrollBar* sender, size_t pos);
@ -147,21 +151,24 @@ namespace MWGui
virtual void onReferenceUnavailable(); virtual void onReferenceUnavailable();
private: private:
void updateOptions(); void updateDisposition();
void restock(); void restock();
void deleteLater();
int mServices;
bool mEnabled; bool mEnabled;
bool mGoodbye; bool mIsCompanion;
std::list<std::string> mKeywords;
std::vector<DialogueText*> mHistoryContents; std::vector<DialogueText*> mHistoryContents;
std::vector<std::pair<std::string, int> > mChoices; std::vector<std::pair<std::string, int> > mChoices;
bool mGoodbye;
std::vector<Link*> mLinks; std::vector<Link*> mLinks;
std::map<std::string, Link*> mTopicLinks; std::map<std::string, Link*> mTopicLinks;
std::vector<Link*> mDeleteLater;
KeywordSearchT mKeywordSearch; KeywordSearchT mKeywordSearch;
BookPage* mHistory; BookPage* mHistory;
@ -169,6 +176,7 @@ namespace MWGui
MyGUI::ScrollBar* mScrollBar; MyGUI::ScrollBar* mScrollBar;
MyGUI::ProgressBar* mDispositionBar; MyGUI::ProgressBar* mDispositionBar;
MyGUI::TextBox* mDispositionText; MyGUI::TextBox* mDispositionText;
MyGUI::Button* mGoodbyeButton;
PersuasionDialog mPersuasionDialog; PersuasionDialog mPersuasionDialog;

@ -121,10 +121,18 @@ void DragAndDrop::drop(ItemModel *targetModel, ItemView *targetView)
mSourceView->update(); mSourceView->update();
} }
void DragAndDrop::onFrame()
{
if (mIsOnDragAndDrop && mItem.mBase.getRefData().getCount() == 0)
finish();
}
void DragAndDrop::finish() void DragAndDrop::finish()
{ {
mIsOnDragAndDrop = false; mIsOnDragAndDrop = false;
mSourceSortModel->clearDragItems(); mSourceSortModel->clearDragItems();
// since mSourceView doesn't get updated in drag()
MWBase::Environment::get().getWindowManager()->getInventoryWindow()->updateItemView();
MyGUI::Gui::getInstance().destroyWidget(mDraggedWidget); MyGUI::Gui::getInstance().destroyWidget(mDraggedWidget);
mDraggedWidget = 0; mDraggedWidget = 0;

@ -29,6 +29,7 @@ namespace MWGui
void startDrag (int index, SortFilterItemModel* sortModel, ItemModel* sourceModel, ItemView* sourceView, int count); void startDrag (int index, SortFilterItemModel* sortModel, ItemModel* sourceModel, ItemView* sourceView, int count);
void drop (ItemModel* targetModel, ItemView* targetView); void drop (ItemModel* targetModel, ItemView* targetView);
void onFrame();
void finish(); void finish();
}; };

@ -4,11 +4,11 @@
#include <MyGUI_Button.h> #include <MyGUI_Button.h>
#include <MyGUI_ScrollView.h> #include <MyGUI_ScrollView.h>
#include <MyGUI_EditBox.h>
#include <components/widgets/list.hpp> #include <components/widgets/list.hpp>
#include <components/settings/settings.hpp> #include <components/settings/settings.hpp>
#include "../mwbase/dialoguemanager.hpp"
#include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/mechanicsmanager.hpp"
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
#include "../mwworld/containerstore.hpp" #include "../mwworld/containerstore.hpp"
@ -53,6 +53,7 @@ namespace MWGui
mSoulBox->eventMouseButtonClick += MyGUI::newDelegate(this, &EnchantingDialog::onSelectSoul); mSoulBox->eventMouseButtonClick += MyGUI::newDelegate(this, &EnchantingDialog::onSelectSoul);
mBuyButton->eventMouseButtonClick += MyGUI::newDelegate(this, &EnchantingDialog::onBuyButtonClicked); mBuyButton->eventMouseButtonClick += MyGUI::newDelegate(this, &EnchantingDialog::onBuyButtonClicked);
mTypeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &EnchantingDialog::onTypeButtonClicked); mTypeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &EnchantingDialog::onTypeButtonClicked);
mName->eventEditSelectAccept += MyGUI::newDelegate(this, &EnchantingDialog::onAccept);
} }
EnchantingDialog::~EnchantingDialog() EnchantingDialog::~EnchantingDialog()
@ -60,9 +61,10 @@ namespace MWGui
delete mItemSelectionDialog; delete mItemSelectionDialog;
} }
void EnchantingDialog::open() void EnchantingDialog::onOpen()
{ {
center(); center();
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mName);
} }
void EnchantingDialog::setSoulGem(const MWWorld::Ptr &gem) 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() void EnchantingDialog::updateLabels()
{ {
std::stringstream enchantCost; std::stringstream enchantCost;
@ -143,57 +140,41 @@ namespace MWGui
} }
} }
void EnchantingDialog::startEnchanting (MWWorld::Ptr actor) void EnchantingDialog::setPtr (const MWWorld::Ptr& ptr)
{ {
mName->setCaption(""); mName->setCaption("");
mEnchanting.setSelfEnchanting(false); if (ptr.getClass().isActor())
mEnchanting.setEnchanter(actor); {
mEnchanting.setSelfEnchanting(false);
mBuyButton->setCaptionWithReplacing("#{sBuy}"); mEnchanting.setEnchanter(ptr);
mBuyButton->setCaptionWithReplacing("#{sBuy}");
mChanceLayout->setVisible(false); mChanceLayout->setVisible(false);
mPtr = ptr;
mPtr = actor; 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()); setItem(MWWorld::Ptr());
startEditing (); 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(); updateLabels();
} }
void EnchantingDialog::onReferenceUnavailable () void EnchantingDialog::onReferenceUnavailable ()
{ {
MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Dialogue);
MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Enchanting); MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Enchanting);
resetReference(); resetReference();
} }
@ -209,7 +190,7 @@ namespace MWGui
void EnchantingDialog::onCancelButtonClicked(MyGUI::Widget* sender) void EnchantingDialog::onCancelButtonClicked(MyGUI::Widget* sender)
{ {
exit(); MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Enchanting);
} }
void EnchantingDialog::onSelectItem(MyGUI::Widget *sender) void EnchantingDialog::onSelectItem(MyGUI::Widget *sender)
@ -304,6 +285,11 @@ namespace MWGui
updateEffectsView(); updateEffectsView();
} }
void EnchantingDialog::onAccept(MyGUI::EditBox *sender)
{
onBuyButtonClicked(sender);
}
void EnchantingDialog::onBuyButtonClicked(MyGUI::Widget* sender) void EnchantingDialog::onBuyButtonClicked(MyGUI::Widget* sender)
{ {
if (mEffects.size() <= 0) if (mEffects.size() <= 0)
@ -364,7 +350,7 @@ namespace MWGui
MWBase::Environment::get().getMechanicsManager()->confiscateStolenItemToOwner(player, item, mPtr, 1); MWBase::Environment::get().getMechanicsManager()->confiscateStolenItemToOwner(player, item, mPtr, 1);
MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Enchanting); MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Enchanting);
MWBase::Environment::get().getDialogueManager()->goodbyeSelected(); MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode();
return; return;
} }
} }

@ -19,15 +19,17 @@ namespace MWGui
EnchantingDialog(); EnchantingDialog();
virtual ~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 setSoulGem (const MWWorld::Ptr& gem);
void setItem (const MWWorld::Ptr& item); void setItem (const MWWorld::Ptr& item);
void startEnchanting(MWWorld::Ptr actor); /// Actor Ptr: buy enchantment from this actor
void startSelfEnchanting(MWWorld::Ptr soulgem); /// Soulgem Ptr: player self-enchant
void setPtr(const MWWorld::Ptr& ptr);
virtual void resetReference(); virtual void resetReference();
@ -46,6 +48,7 @@ namespace MWGui
void onBuyButtonClicked(MyGUI::Widget* sender); void onBuyButtonClicked(MyGUI::Widget* sender);
void updateLabels(); void updateLabels();
void onTypeButtonClicked(MyGUI::Widget* sender); void onTypeButtonClicked(MyGUI::Widget* sender);
void onAccept(MyGUI::EditBox* sender);
ItemSelectionDialog* mItemSelectionDialog; ItemSelectionDialog* mItemSelectionDialog;
@ -58,7 +61,7 @@ namespace MWGui
MyGUI::Button* mTypeButton; MyGUI::Button* mTypeButton;
MyGUI::Button* mBuyButton; MyGUI::Button* mBuyButton;
MyGUI::TextBox* mName; MyGUI::EditBox* mName;
MyGUI::TextBox* mEnchantmentPoints; MyGUI::TextBox* mEnchantmentPoints;
MyGUI::TextBox* mCastCost; MyGUI::TextBox* mCastCost;
MyGUI::TextBox* mCharge; MyGUI::TextBox* mCharge;

@ -406,10 +406,11 @@ namespace MWGui
MyGUI::EditBox* box = parent->createWidget<MyGUI::EditBox>("NormalText", MyGUI::EditBox* box = parent->createWidget<MyGUI::EditBox>("NormalText",
MyGUI::IntCoord(0, pag.getCurrentTop(), pag.getPageWidth(), 0), MyGUI::Align::Left | MyGUI::Align::Top, MyGUI::IntCoord(0, pag.getCurrentTop(), pag.getPageWidth(), 0), MyGUI::Align::Left | MyGUI::Align::Top,
parent->getName() + MyGUI::utility::toString(parent->getChildCount())); parent->getName() + MyGUI::utility::toString(parent->getChildCount()));
box->setProperty("Static", "true"); box->setEditStatic(true);
box->setProperty("MultiLine", "true"); box->setEditMultiLine(true);
box->setProperty("WordWrap", "true"); box->setEditWordWrap(true);
box->setProperty("NeedMouse", "false"); box->setNeedMouseFocus(false);
box->setNeedKeyFocus(false);
box->setMaxTextLength(text.size()); box->setMaxTextLength(text.size());
box->setTextAlign(mBlockStyle.mAlign); box->setTextAlign(mBlockStyle.mAlign);
box->setTextColour(mTextStyle.mColour); box->setTextColour(mTextStyle.mColour);

@ -108,7 +108,7 @@ namespace MWGui
HUD::HUD(CustomMarkerCollection &customMarkers, DragAndDrop* dragAndDrop, MWRender::LocalMap* localMapRender) 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")) , LocalMapBase(customMarkers, localMapRender, Settings::Manager::getBool("local map hud fog of war", "Map"))
, mHealth(NULL) , mHealth(NULL)
, mMagicka(NULL) , mMagicka(NULL)
@ -422,6 +422,20 @@ namespace MWGui
if (mIsDrowning) if (mIsDrowning)
mDrowningFlashTheta += dt * osg::PI*2; 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) 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) void HUD::setEnemy(const MWWorld::Ptr &enemy)
{ {
mEnemyActorId = enemy.getClass().getCreatureStats(enemy).getActorId(); mEnemyActorId = enemy.getClass().getCreatureStats(enemy).getActorId();
@ -686,6 +683,13 @@ namespace MWGui
mEnemyHealthTimer = -1; mEnemyHealthTimer = -1;
} }
void HUD::clear()
{
unsetSelectedSpell();
unsetSelectedWeapon();
resetEnemy();
}
void HUD::customMarkerCreated(MyGUI::Widget *marker) void HUD::customMarkerCreated(MyGUI::Widget *marker)
{ {
marker->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onMapClicked); marker->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onMapClicked);

@ -16,7 +16,7 @@ namespace MWGui
class SpellIcons; class SpellIcons;
class ItemWidget; class ItemWidget;
class HUD : public Layout, public LocalMapBase class HUD : public WindowBase, public LocalMapBase
{ {
public: public:
HUD(CustomMarkerCollection& customMarkers, DragAndDrop* dragAndDrop, MWRender::LocalMap* localMapRender); HUD(CustomMarkerCollection& customMarkers, DragAndDrop* dragAndDrop, MWRender::LocalMap* localMapRender);
@ -55,11 +55,11 @@ namespace MWGui
MyGUI::Widget* getEffectBox() { return mEffectBox; } MyGUI::Widget* getEffectBox() { return mEffectBox; }
void update();
void setEnemy(const MWWorld::Ptr& enemy); void setEnemy(const MWWorld::Ptr& enemy);
void resetEnemy(); void resetEnemy();
void clear();
private: private:
MyGUI::ProgressBar *mHealth, *mMagicka, *mStamina, *mEnemyHealth, *mDrowning; MyGUI::ProgressBar *mHealth, *mMagicka, *mStamina, *mEnemyHealth, *mDrowning;
MyGUI::Widget* mHealthFrame; MyGUI::Widget* mHealthFrame;

@ -377,7 +377,7 @@ namespace MWGui
dirtyPreview(); dirtyPreview();
} }
void InventoryWindow::open() void InventoryWindow::onOpen()
{ {
if (!mPtr.isEmpty()) if (!mPtr.isEmpty())
{ {
@ -603,11 +603,8 @@ namespace MWGui
mEncumbranceBar->setValue(static_cast<int>(encumbrance), static_cast<int>(capacity)); mEncumbranceBar->setValue(static_cast<int>(encumbrance), static_cast<int>(capacity));
} }
void InventoryWindow::onFrame() void InventoryWindow::onFrame(float dt)
{ {
if (!mMainWidget->getVisible())
return;
updateEncumbranceBar(); updateEncumbranceBar();
} }

@ -35,12 +35,12 @@ namespace MWGui
public: public:
InventoryWindow(DragAndDrop* dragAndDrop, osg::Group* parent, Resource::ResourceSystem* resourceSystem); InventoryWindow(DragAndDrop* dragAndDrop, osg::Group* parent, Resource::ResourceSystem* resourceSystem);
virtual void open(); virtual void onOpen();
/// start trading, disables item drag&drop /// start trading, disables item drag&drop
void setTrading(bool trading); void setTrading(bool trading);
void onFrame(); void onFrame(float dt);
void pickUpObject (MWWorld::Ptr object); void pickUpObject (MWWorld::Ptr object);

@ -119,6 +119,11 @@ namespace MWGui
return ret; return ret;
} }
bool ItemModel::allowedToUseItems() const
{
return true;
}
bool ItemModel::allowedToInsertItems() const bool ItemModel::allowedToInsertItems() const
{ {
return true; return true;
@ -135,6 +140,11 @@ namespace MWGui
delete mSourceModel; delete mSourceModel;
} }
bool ProxyItemModel::allowedToUseItems() const
{
return mSourceModel->allowedToUseItems();
}
MWWorld::Ptr ProxyItemModel::copyItem (const ItemStack& item, size_t count, bool setNewOwner) MWWorld::Ptr ProxyItemModel::copyItem (const ItemStack& item, size_t count, bool setNewOwner)
{ {
return mSourceModel->copyItem (item, count, setNewOwner); return mSourceModel->copyItem (item, count, setNewOwner);

@ -70,6 +70,9 @@ namespace MWGui
virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count, bool setNewOwner=false) = 0; virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count, bool setNewOwner=false) = 0;
virtual void removeItem (const ItemStack& item, size_t count) = 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) /// Is the player allowed to insert items into this model? (default true)
virtual bool allowedToInsertItems() const; virtual bool allowedToInsertItems() const;
@ -85,6 +88,9 @@ namespace MWGui
public: public:
ProxyItemModel(); ProxyItemModel();
virtual ~ProxyItemModel(); virtual ~ProxyItemModel();
bool allowedToUseItems() const;
virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count, bool setNewOwner=false); virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count, bool setNewOwner=false);
virtual void removeItem (const ItemStack& item, size_t count); virtual void removeItem (const ItemStack& item, size_t count);
virtual ModelIndex getIndex (ItemStack item); virtual ModelIndex getIndex (ItemStack item);

@ -29,9 +29,10 @@ namespace MWGui
center(); center();
} }
void ItemSelectionDialog::exit() bool ItemSelectionDialog::exit()
{ {
eventDialogCanceled(); eventDialogCanceled();
return true;
} }
void ItemSelectionDialog::openContainer(const MWWorld::Ptr& container) void ItemSelectionDialog::openContainer(const MWWorld::Ptr& container)

@ -21,7 +21,7 @@ namespace MWGui
public: public:
ItemSelectionDialog(const std::string& label); ItemSelectionDialog(const std::string& label);
virtual void exit(); virtual bool exit();
typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void; typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void;
typedef MyGUI::delegates::CMultiDelegate1<MWWorld::Ptr> EventHandle_Item; typedef MyGUI::delegates::CMultiDelegate1<MWWorld::Ptr> EventHandle_Item;

@ -37,8 +37,6 @@ namespace MWGui
{ {
getWidget(mProgressBar, "ProgressBar"); getWidget(mProgressBar, "ProgressBar");
setVisible(false);
mTimeAdvancer.eventProgressChanged += MyGUI::newDelegate(this, &JailScreen::onJailProgressChanged); mTimeAdvancer.eventProgressChanged += MyGUI::newDelegate(this, &JailScreen::onJailProgressChanged);
mTimeAdvancer.eventFinished += MyGUI::newDelegate(this, &JailScreen::onJailFinished); mTimeAdvancer.eventFinished += MyGUI::newDelegate(this, &JailScreen::onJailFinished);

@ -14,6 +14,8 @@ namespace MWGui
void onFrame(float dt); void onFrame(float dt);
bool exit() { return false; }
private: private:
int mDays; int mDays;

@ -8,6 +8,7 @@
#include <MyGUI_TextBox.h> #include <MyGUI_TextBox.h>
#include <MyGUI_Button.h> #include <MyGUI_Button.h>
#include <MyGUI_InputManager.h>
#include <components/misc/stringops.hpp> #include <components/misc/stringops.hpp>
#include <components/widgets/imagebutton.hpp> #include <components/widgets/imagebutton.hpp>
@ -44,7 +45,7 @@ namespace
static char const LeftTopicIndex [] = "LeftTopicIndex"; static char const LeftTopicIndex [] = "LeftTopicIndex";
static char const RightTopicIndex [] = "RightTopicIndex"; static char const RightTopicIndex [] = "RightTopicIndex";
struct JournalWindowImpl : MWGui::WindowBase, MWGui::JournalBooks, MWGui::JournalWindow struct JournalWindowImpl : MWGui::JournalBooks, MWGui::JournalWindow
{ {
struct DisplayState struct DisplayState
{ {
@ -84,19 +85,24 @@ namespace
void adviseButtonClick (char const * name, void (JournalWindowImpl::*Handler) (MyGUI::Widget* _sender)) void adviseButtonClick (char const * name, void (JournalWindowImpl::*Handler) (MyGUI::Widget* _sender))
{ {
getWidget <Gui::ImageButton> (name) -> getWidget <MyGUI::Widget> (name) ->
eventMouseButtonClick += newDelegate(this, Handler); eventMouseButtonClick += newDelegate(this, Handler);
} }
void adviseKeyPress (char const * name, void (JournalWindowImpl::*Handler) (MyGUI::Widget* _sender, MyGUI::KeyCode key, MyGUI::Char character))
{
getWidget <MyGUI::Widget> (name) ->
eventKeyButtonPressed += newDelegate(this, Handler);
}
MWGui::BookPage* getPage (char const * name) MWGui::BookPage* getPage (char const * name)
{ {
return getWidget <MWGui::BookPage> (name); return getWidget <MWGui::BookPage> (name);
} }
JournalWindowImpl (MWGui::JournalViewModel::Ptr Model, bool questList) JournalWindowImpl (MWGui::JournalViewModel::Ptr Model, bool questList)
: WindowBase("openmw_journal.layout"), JournalBooks (Model) : JournalBooks (Model), JournalWindow()
{ {
mMainWidget->setVisible(false);
center(); center();
adviseButtonClick (OptionsBTN, &JournalWindowImpl::notifyOptions ); adviseButtonClick (OptionsBTN, &JournalWindowImpl::notifyOptions );
@ -112,6 +118,12 @@ namespace
adviseButtonClick (ShowAllBTN, &JournalWindowImpl::notifyShowAll ); adviseButtonClick (ShowAllBTN, &JournalWindowImpl::notifyShowAll );
adviseButtonClick (ShowActiveBTN, &JournalWindowImpl::notifyShowActive); 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<Gui::MWList>(QuestsList); Gui::MWList* list = getWidget<Gui::MWList>(QuestsList);
list->eventItemSelected += MyGUI::newDelegate(this, &JournalWindowImpl::notifyQuestClicked); list->eventItemSelected += MyGUI::newDelegate(this, &JournalWindowImpl::notifyQuestClicked);
@ -211,7 +223,7 @@ namespace
button->setPosition(button->getPosition() + MyGUI::IntPoint(diff.width,0)); button->setPosition(button->getPosition() + MyGUI::IntPoint(diff.width,0));
} }
void open() void onOpen()
{ {
if (!MWBase::Environment::get().getWindowManager ()->getJournalAllowed ()) if (!MWBase::Environment::get().getWindowManager ()->getJournalAllowed ())
{ {
@ -238,9 +250,11 @@ namespace
--page; --page;
} }
updateShowingPages(); updateShowingPages();
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(getWidget<MyGUI::Widget>(CloseBTN));
} }
void close() void onClose()
{ {
mModel->unload (); mModel->unload ();
@ -349,8 +363,19 @@ namespace
relPages = 0; relPages = 0;
} }
setVisible (PrevPageBTN, page > 0); MyGUI::Widget* nextPageBtn = getWidget<MyGUI::Widget>(NextPageBTN);
setVisible (NextPageBTN, relPages > 2); MyGUI::Widget* prevPageBtn = getWidget<MyGUI::Widget>(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 (PageOneNum, relPages > 0);
setVisible (PageTwoNum, relPages > 1); setVisible (PageTwoNum, relPages > 1);
@ -362,6 +387,14 @@ namespace
setText (PageTwoNum, page + 2); 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) void notifyTopicClicked (intptr_t linkId)
{ {
Book topicBook = createTopicBook (linkId); Book topicBook = createTopicBook (linkId);
@ -603,3 +636,9 @@ MWGui::JournalWindow * MWGui::JournalWindow::create (JournalViewModel::Ptr Model
{ {
return new JournalWindowImpl (Model, questList); return new JournalWindowImpl (Model, questList);
} }
MWGui::JournalWindow::JournalWindow()
:WindowBase("openmw_journal.layout")
{
}

@ -1,6 +1,8 @@
#ifndef MWGUI_JOURNAL_H #ifndef MWGUI_JOURNAL_H
#define MWGUI_JOURNAL_H #define MWGUI_JOURNAL_H
#include "windowbase.hpp"
#include <memory> #include <memory>
namespace MWBase { class WindowManager; } namespace MWBase { class WindowManager; }
@ -9,8 +11,10 @@ namespace MWGui
{ {
struct JournalViewModel; struct JournalViewModel;
struct JournalWindow struct JournalWindow : public WindowBase
{ {
JournalWindow();
/// construct a new instance of the one JournalWindow implementation /// construct a new instance of the one JournalWindow implementation
static JournalWindow * create (std::shared_ptr <JournalViewModel> Model, bool questList); static JournalWindow * create (std::shared_ptr <JournalViewModel> Model, bool questList);

@ -0,0 +1,283 @@
#include "keyboardnavigation.hpp"
#include <MyGUI_InputManager.h>
#include <MyGUI_WidgetManager.h>
#include <MyGUI_Button.h>
#include <MyGUI_Gui.h>
#include <MyGUI_Window.h>
#include "../mwbase/windowmanager.hpp"
#include "../mwbase/environment.hpp"
namespace MWGui
{
bool shouldAcceptKeyFocus(MyGUI::Widget* w)
{
return w && !w->castType<MyGUI::Window>(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<MyGUI::Widget*>& 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<int, MyGUI::Widget*>::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<const int, MyGUI::Widget*>& 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<MyGUI::Button>(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<MyGUI::Button>(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<int>(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<MyGUI::Button>(false);
//if (button && button->getEnabled())
if (focus->getTypeName().find("Button") != std::string::npos && focus->getEnabled())
{
focus->eventMouseButtonClick(focus);
return true;
}
return false;
}
}

@ -0,0 +1,47 @@
#ifndef OPENMW_MWGUI_KEYBOARDNAVIGATION_H
#define OPENMW_MWGUI_KEYBOARDNAVIGATION_H
#include <MyGUI_KeyCode.h>
#include <MyGUI_IUnlinkWidget.h>
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<int, MyGUI::Widget*> mKeyFocus;
MyGUI::Widget* mCurrentFocus;
MyGUI::Widget* mModalWindow;
};
}
#endif

@ -41,6 +41,7 @@ namespace MWGui
{ {
MyGUI::TextBox* t; MyGUI::TextBox* t;
getWidget(t, "AttribVal" + MyGUI::utility::toString(i)); getWidget(t, "AttribVal" + MyGUI::utility::toString(i));
mAttributeValues.push_back(t);
MyGUI::Button* b; MyGUI::Button* b;
getWidget(b, "Attrib" + MyGUI::utility::toString(i)); getWidget(b, "Attrib" + MyGUI::utility::toString(i));
@ -48,10 +49,7 @@ namespace MWGui
b->eventMouseButtonClick += MyGUI::newDelegate(this, &LevelupDialog::onAttributeClicked); b->eventMouseButtonClick += MyGUI::newDelegate(this, &LevelupDialog::onAttributeClicked);
mAttributes.push_back(b); mAttributes.push_back(b);
mAttributeValues.push_back(t);
getWidget(t, "AttribMultiplier" + MyGUI::utility::toString(i)); getWidget(t, "AttribMultiplier" + MyGUI::utility::toString(i));
mAttributeMultipliers.push_back(t); mAttributeMultipliers.push_back(t);
} }
@ -128,7 +126,7 @@ namespace MWGui
setAttributeValues(); setAttributeValues();
} }
void LevelupDialog::open() void LevelupDialog::onOpen()
{ {
MWBase::World *world = MWBase::Environment::get().getWorld(); MWBase::World *world = MWBase::Environment::get().getWorld();
MWWorld::Ptr player = world->getPlayerPtr(); MWWorld::Ptr player = world->getPlayerPtr();

@ -11,7 +11,7 @@ namespace MWGui
public: public:
LevelupDialog(); LevelupDialog();
virtual void open(); virtual void onOpen();
private: private:
MyGUI::Button* mOkButton; MyGUI::Button* mOkButton;

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

Loading…
Cancel
Save